HDD dump work.

This commit is contained in:
Jean-Philip Desjardins 2023-01-03 21:18:18 -05:00
parent b852400139
commit 2fff103dbb
17 changed files with 568 additions and 14 deletions

View file

@ -255,6 +255,13 @@ set(COMMON_SRC_FILES
gs/GsPixelFormats.h
gs/GsSpriteRegion.h
gs/GsTextureCache.h
hdd/ApaDefs.h
hdd/ApaReader.cpp
hdd/ApaReader.h
hdd/HddDefs.h
hdd/PfsDefs.h
hdd/PfsReader.cpp
hdd/PfsReader.h
input/InputBindingManager.cpp
input/InputBindingManager.h
input/InputProvider.h
@ -268,6 +275,8 @@ set(COMMON_SRC_FILES
iop/ioman/McDumpDevice.h
iop/ioman/HardDiskDevice.cpp
iop/ioman/HardDiskDevice.h
iop/ioman/HardDiskDumpDevice.cpp
iop/ioman/HardDiskDumpDevice.h
iop/ioman/OpticalMediaDevice.cpp
iop/ioman/OpticalMediaDevice.h
iop/ioman/OpticalMediaDirectoryIterator.cpp

View file

@ -427,8 +427,8 @@ void CPS2VM::ResetVM()
iopOs->GetIoman()->RegisterDevice("host0", std::make_shared<Iop::Ioman::CPreferenceDirectoryDevice>(PREF_PS2_HOST_DIRECTORY));
iopOs->GetIoman()->RegisterDevice("mc0", std::make_shared<Iop::Ioman::CPreferenceDirectoryDevice>(PREF_PS2_MC0_DIRECTORY));
iopOs->GetIoman()->RegisterDevice("mc1", std::make_shared<Iop::Ioman::CPreferenceDirectoryDevice>(PREF_PS2_MC1_DIRECTORY));
iopOs->GetIoman()->RegisterDevice("cdrom", Iop::CIoman::DevicePtr(new Iop::Ioman::COpticalMediaDevice(m_cdrom0)));
iopOs->GetIoman()->RegisterDevice("cdrom0", Iop::CIoman::DevicePtr(new Iop::Ioman::COpticalMediaDevice(m_cdrom0)));
iopOs->GetIoman()->RegisterDevice("cdrom", Iop::Ioman::DevicePtr(new Iop::Ioman::COpticalMediaDevice(m_cdrom0)));
iopOs->GetIoman()->RegisterDevice("cdrom0", Iop::Ioman::DevicePtr(new Iop::Ioman::COpticalMediaDevice(m_cdrom0)));
iopOs->GetIoman()->RegisterDevice("hdd0", std::make_shared<Iop::Ioman::CHardDiskDevice>());
iopOs->GetLoadcore()->SetLoadExecutableHandler(std::bind(&CPS2OS::LoadExecutable, m_ee->m_os, std::placeholders::_1, std::placeholders::_2));

79
Source/hdd/ApaDefs.h Normal file
View file

@ -0,0 +1,79 @@
#pragma once
namespace Hdd
{
enum
{
APA_HEADER_MAGIC = 0x415041,
};
struct APA_TIME
{
uint8_t unused;
uint8_t seconds;
uint8_t minutes;
uint8_t hours;
uint8_t day;
uint8_t month;
uint16_t year;
};
static_assert(sizeof(APA_TIME) == 8);
struct APA_MBR
{
char magic[0x20];
uint32_t version;
uint32_t sectorCount;
APA_TIME createdTime;
uint32_t osdStart;
uint32_t osdSize;
char padding[200];
};
static_assert(sizeof(APA_MBR) == 0x100);
struct APA_SUBPART
{
uint32_t start;
uint32_t count;
};
static_assert(sizeof(APA_SUBPART) == 8);
#pragma pack(push, 1)
struct APA_HEADER
{
enum
{
ID_SIZE = 32,
PASSWORD_SIZE = 8,
SUBPART_SIZE = 64,
};
enum TYPE
{
TYPE_PFS = 0x100,
};
uint32_t checksum;
uint32_t magic;
uint32_t next;
uint32_t prev;
char id[ID_SIZE];
char rPassword[PASSWORD_SIZE];
char fPassword[PASSWORD_SIZE];
uint32_t start;
uint32_t length;
uint16_t type;
uint16_t flags;
uint32_t subPartCount;
APA_TIME createdTime;
uint32_t main;
uint32_t number;
uint32_t modVer;
uint32_t padding1[7];
uint8_t padding2[0x80];
APA_MBR mbr;
APA_SUBPART subParts[SUBPART_SIZE];
};
static_assert(sizeof(APA_HEADER) == 0x400);
#pragma pack(pop)
}

30
Source/hdd/ApaReader.cpp Normal file
View file

@ -0,0 +1,30 @@
#include "ApaReader.h"
#include "ApaDefs.h"
#include "HddDefs.h"
using namespace Hdd;
CApaReader::CApaReader(Framework::CStream& stream)
: m_stream(stream)
{
}
uint32 CApaReader::FindPartition(const char* partitionId)
{
uint32_t lba = 0;
while(1)
{
APA_HEADER header;
m_stream.Seek(lba * g_sectorSize, Framework::STREAM_SEEK_SET);
m_stream.Read(&header, sizeof(APA_HEADER));
assert(header.magic == APA_HEADER_MAGIC);
if(!strcmp(header.id, partitionId))
{
return header.start;
}
lba = header.next;
if(lba == 0) break;
}
return -1;
}

17
Source/hdd/ApaReader.h Normal file
View file

@ -0,0 +1,17 @@
#pragma once
#include "Stream.h"
namespace Hdd
{
class CApaReader
{
public:
CApaReader(Framework::CStream&);
uint32 FindPartition(const char*);
private:
Framework::CStream& m_stream;
};
}

6
Source/hdd/HddDefs.h Normal file
View file

@ -0,0 +1,6 @@
#pragma once
namespace Hdd
{
static const uint64 g_sectorSize = 512;
}

87
Source/hdd/PfsDefs.h Normal file
View file

@ -0,0 +1,87 @@
#pragma once
namespace Hdd
{
enum
{
PFS_SUPERBLOCK_MAGIC = 0x50465300, //'/0sfp'
PFS_INODE_SEGDESC_DIRECT_MAGIC = 0x53454744, //'dges'
PFS_SUPERBLOCK_LBA = 8192,
PFS_INODE_SIZE = 1024,
PFS_BLOCK_SCALE = 1,
};
struct PFS_BLOCKINFO
{
uint32_t number;
uint16_t subPart;
uint16_t count;
};
static_assert(sizeof(PFS_BLOCKINFO) == 0x8);
struct PFS_TIME
{
uint8_t unused;
uint8_t seconds;
uint8_t minutes;
uint8_t hours;
uint8_t day;
uint8_t month;
uint16_t year;
};
static_assert(sizeof(PFS_TIME) == 8);
struct PFS_SUPERBLOCK
{
uint32_t magic;
uint32_t version;
uint32_t modVer;
uint32_t fsckStat;
uint32_t zoneSize;
uint32_t subPartCount;
PFS_BLOCKINFO logBlock;
PFS_BLOCKINFO rootBlock;
};
static_assert(sizeof(PFS_SUPERBLOCK) == 0x28);
struct PFS_INODE
{
enum
{
DATA_SIZE = 114,
};
uint32_t checksum;
uint32_t magic;
PFS_BLOCKINFO inodeBlock;
PFS_BLOCKINFO nextSegment;
PFS_BLOCKINFO lastSegment;
PFS_BLOCKINFO unused;
PFS_BLOCKINFO data[DATA_SIZE];
uint16_t mode;
uint16_t attr;
uint16_t uid;
uint16_t gid;
PFS_TIME atime;
PFS_TIME ctime;
PFS_TIME mtime;
uint64_t size;
uint32_t blockCount;
uint32_t dataCount;
uint32_t indirectCount;
uint32_t subPart;
uint32_t reserved[4];
};
static_assert(sizeof(PFS_INODE) == 1024);
struct PFS_DIRENTRY
{
uint32_t inode;
uint8_t subPart;
uint8_t pathLength;
uint16_t allocatedLength;
};
static_assert(sizeof(PFS_DIRENTRY) == 8);
}

154
Source/hdd/PfsReader.cpp Normal file
View file

@ -0,0 +1,154 @@
#include "PfsReader.h"
#include "HddDefs.h"
using namespace Hdd;
static uint32_t GetScale(uint32_t num, uint32_t size)
{
uint32 scale = 0;
while((size << scale) != num)
{
scale++;
}
return scale;
}
CPfsReader::CPfsReader(Framework::CStream& stream, uint32 baseLba)
: m_stream(stream)
, m_baseLba(baseLba)
{
uint32_t superBlockLba = m_baseLba + PFS_SUPERBLOCK_LBA;
m_stream.Seek(superBlockLba * g_sectorSize, Framework::STREAM_SEEK_SET);
m_stream.Read(&m_superBlock, sizeof(PFS_SUPERBLOCK));
assert(m_superBlock.magic == PFS_SUPERBLOCK_MAGIC);
m_inodeScale = GetScale(m_superBlock.zoneSize, PFS_INODE_SIZE);
}
CPfsFileReader* CPfsReader::GetFileStream(const char* path)
{
assert(path[0] == '/');
path++;
assert(strchr(path, '/') == nullptr);
auto rootInode = ReadInode(m_superBlock.rootBlock.number, m_superBlock.rootBlock.subPart);
assert(rootInode.dataCount == 2);
uint32 dirLba = GetBlockLba(rootInode.data[1].number, rootInode.data[1].subPart);
static const uint32_t dirBlockSize = g_sectorSize << PFS_BLOCK_SCALE;
uint8 dirBlock[dirBlockSize];
m_stream.Seek(dirLba * g_sectorSize, Framework::STREAM_SEEK_SET);
m_stream.Read(dirBlock, sizeof(dirBlock));
auto dirBlockCurr = dirBlock;
auto dirBlockEnd = dirBlock + dirBlockSize;
while(1)
{
assert((dirBlockEnd - dirBlockCurr) >= sizeof(PFS_DIRENTRY));
auto dirEntry = reinterpret_cast<const PFS_DIRENTRY*>(dirBlockCurr);
uint32_t entryLength = dirEntry->allocatedLength & 0xFFF;
if(entryLength == 0)
{
//We're done
dirBlockCurr = dirBlockEnd;
break;
}
assert((dirBlockEnd - dirBlockCurr) >= entryLength);
std::string entryName;
for(int i = 0; i < dirEntry->pathLength; i++)
{
entryName += dirBlockCurr[sizeof(PFS_DIRENTRY) + i];
}
if(!strcmp(entryName.c_str(), path))
{
break;
}
dirBlockCurr += entryLength;
}
if(dirBlockCurr == dirBlockEnd)
{
return nullptr;
}
auto dirEntry = reinterpret_cast<const PFS_DIRENTRY*>(dirBlockCurr);
auto fileInode = ReadInode(dirEntry->inode, dirEntry->subPart);
return new CPfsFileReader(*this, m_stream, fileInode);
}
uint32 CPfsReader::GetBlockLba(uint32 number, uint32 subPart)
{
assert(subPart == 0);
return m_baseLba + ((number << m_inodeScale) << PFS_BLOCK_SCALE);
}
PFS_INODE CPfsReader::ReadInode(uint32 number, uint32 subPart)
{
PFS_INODE result = {};
uint32 inodeLba = GetBlockLba(number, subPart);
m_stream.Seek(inodeLba * g_sectorSize, Framework::STREAM_SEEK_SET);
m_stream.Read(&result, sizeof(PFS_INODE));
assert(result.magic == PFS_INODE_SEGDESC_DIRECT_MAGIC);
return result;
}
CPfsFileReader::CPfsFileReader(CPfsReader& reader, Framework::CStream& stream, PFS_INODE inode)
: m_reader(reader)
, m_stream(stream)
, m_inode(inode)
{
}
void CPfsFileReader::Seek(int64 position, Framework::STREAM_SEEK_DIRECTION whence)
{
uint64 fileSize = m_inode.size;
switch(whence)
{
case Framework::STREAM_SEEK_SET:
m_position = std::min<uint64>(position, fileSize);
break;
case Framework::STREAM_SEEK_CUR:
m_position = std::min<uint64>(m_position + position, fileSize);
break;
case Framework::STREAM_SEEK_END:
m_position = fileSize;
break;
}
m_isEof = false;
}
uint64_t CPfsFileReader::Tell()
{
return m_position;
}
uint64 CPfsFileReader::Read(void* buffer, uint64 length)
{
uint64 fileSize = m_inode.size;
if(m_position >= fileSize)
{
m_isEof = true;
return 0;
}
uint64 remainFileSize = fileSize - m_position;
length = std::min<uint64>(length, remainFileSize);
assert(m_inode.dataCount == 2);
uint32 fileLba = m_reader.GetBlockLba(m_inode.data[1].number, m_inode.data[1].subPart);
m_stream.Seek(fileLba * g_sectorSize, Framework::STREAM_SEEK_SET);
m_stream.Read(buffer, length);
return length;
}
uint64 CPfsFileReader::Write(const void*, uint64)
{
assert(false);
return 0;
}
bool CPfsFileReader::IsEOF()
{
return m_isEof;
}

47
Source/hdd/PfsReader.h Normal file
View file

@ -0,0 +1,47 @@
#pragma once
#include "Stream.h"
#include "PfsDefs.h"
namespace Hdd
{
class CPfsReader
{
public:
CPfsReader(Framework::CStream&, uint32);
class CPfsFileReader* GetFileStream(const char*);
uint32 GetBlockLba(uint32, uint32);
private:
PFS_INODE ReadInode(uint32, uint32);
Framework::CStream& m_stream;
uint32 m_baseLba = 0;
PFS_SUPERBLOCK m_superBlock = {};
uint32 m_inodeScale = 0;
};
class CPfsFileReader : public Framework::CStream
{
public:
CPfsFileReader(CPfsReader&, Framework::CStream&, PFS_INODE);
~CPfsFileReader() = default;
void Seek(int64, Framework::STREAM_SEEK_DIRECTION) override;
uint64 Tell() override;
uint64 Read(void*, uint64) override;
uint64 Write(const void*, uint64) override;
bool IsEOF() override;
private:
CPfsReader& m_reader;
Framework::CStream& m_stream;
PFS_INODE m_inode;
uint64 m_position = 0;
bool m_isEof = false;
};
}

View file

@ -29,10 +29,12 @@ namespace Iop
{
throw std::runtime_error("Directory creation not supported.");
}
virtual fs::path GetMountPath(const char*)
virtual std::shared_ptr<CDevice> Mount(const char*)
{
throw std::runtime_error("Mounting not supported.");
}
};
typedef std::shared_ptr<CDevice> DevicePtr;
}
}

View file

@ -211,7 +211,7 @@ std::string CIoman::GetFunctionName(unsigned int functionId) const
}
}
void CIoman::RegisterDevice(const char* name, const DevicePtr& device)
void CIoman::RegisterDevice(const char* name, const Ioman::DevicePtr& device)
{
m_devices[name] = device;
}
@ -530,8 +530,7 @@ int32 CIoman::Mount(const char* fsName, const char* devicePath)
mountedDeviceName.erase(std::remove(mountedDeviceName.begin(), mountedDeviceName.end(), ':'), mountedDeviceName.end());
assert(m_devices.find(mountedDeviceName) == std::end(m_devices));
auto partitionPath = device->GetMountPath(pathInfo.devicePath.c_str());
auto mountedDevice = std::make_shared<Ioman::CPathDirectoryDevice>(partitionPath);
auto mountedDevice = device->Mount(pathInfo.devicePath.c_str());
m_devices[mountedDeviceName] = mountedDevice;
}
catch(const std::exception& except)

View file

@ -33,8 +33,6 @@ namespace Iop
{
};
typedef std::shared_ptr<Ioman::CDevice> DevicePtr;
CIoman(CIopBios&, uint8*);
virtual ~CIoman();
@ -45,7 +43,7 @@ namespace Iop
void SaveState(Framework::CZipArchiveWriter&) const override;
void LoadState(Framework::CZipArchiveReader&) override;
void RegisterDevice(const char*, const DevicePtr&);
void RegisterDevice(const char*, const Ioman::DevicePtr&);
uint32 Open(uint32, const char*);
uint32 Close(uint32);
@ -133,7 +131,7 @@ namespace Iop
typedef std::map<int32, FileInfo> FileMapType;
typedef std::map<uint32, Ioman::DirectoryIteratorPtr> DirectoryMapType;
typedef std::map<std::string, DevicePtr> DeviceMapType;
typedef std::map<std::string, Ioman::DevicePtr> DeviceMapType;
typedef std::map<std::string, uint32> UserDeviceMapType;
void PrepareOpenThunk();

View file

@ -4,6 +4,7 @@
#include "PathUtils.h"
#include "PS2VM_Preferences.h"
#include "StringUtils.h"
#include "PathDirectoryDevice.h"
#include "PathDirectoryIterator.h"
using namespace Iop::Ioman;
@ -53,7 +54,7 @@ DirectoryIteratorPtr CHardDiskDevice::GetDirectory(const char* devicePath)
return std::make_unique<CPathDirectoryIterator>(m_basePath);
}
fs::path CHardDiskDevice::GetMountPath(const char* devicePath)
DevicePtr CHardDiskDevice::Mount(const char* devicePath)
{
auto mountParams = StringUtils::Split(devicePath, ',', true);
assert(!mountParams.empty());
@ -62,7 +63,8 @@ fs::path CHardDiskDevice::GetMountPath(const char* devicePath)
{
throw std::runtime_error("Partition doesn't exist.");
}
return m_basePath / mountParams[0];
auto mountPath = m_basePath / mountParams[0];
return std::make_shared<CPathDirectoryDevice>(mountPath);
}
void CHardDiskDevice::CreatePartition(const std::vector<std::string>& createParams)

View file

@ -14,7 +14,7 @@ namespace Iop
Framework::CStream* GetFile(uint32, const char*) override;
DirectoryIteratorPtr GetDirectory(const char*) override;
fs::path GetMountPath(const char*) override;
DevicePtr Mount(const char*) override;
private:
void CreatePartition(const std::vector<std::string>&);

View file

@ -0,0 +1,62 @@
#include "HardDiskDumpDevice.h"
#include "HardDiskDevice.h"
#include "hdd/ApaReader.h"
#include "StringUtils.h"
#include "MemStream.h"
using namespace Iop;
using namespace Iop::Ioman;
CHardDiskDumpDevice::CHardDiskDumpDevice(std::unique_ptr<Framework::CStream> stream)
: m_stream(std::move(stream))
{
}
Framework::CStream* CHardDiskDumpDevice::GetFile(uint32 flags, const char* path)
{
assert(flags == OPEN_FLAG_RDONLY);
Hdd::CApaReader reader(*m_stream);
uint32 partitionLba = reader.FindPartition(path);
if(partitionLba == -1)
{
return nullptr;
}
return new CHardDiskPartition();
}
DirectoryIteratorPtr CHardDiskDumpDevice::GetDirectory(const char*)
{
return DirectoryIteratorPtr();
}
DevicePtr CHardDiskDumpDevice::Mount(const char* path)
{
auto mountParams = StringUtils::Split(path, ',', true);
Hdd::CApaReader reader(*m_stream);
uint32 partitionLba = reader.FindPartition(mountParams[0].c_str());
if(partitionLba == -1)
{
assert(false);
return DevicePtr();
}
return std::make_shared<CHardDiskDumpPartitionDevice>(*m_stream, partitionLba);
}
CHardDiskDumpPartitionDevice::CHardDiskDumpPartitionDevice(Framework::CStream& stream, uint32 partitionLba)
: m_pfsReader(stream, partitionLba)
{
}
Framework::CStream* CHardDiskDumpPartitionDevice::GetFile(uint32 flags, const char* path)
{
assert((flags & OPEN_FLAG_ACCMODE) == OPEN_FLAG_RDONLY);
return m_pfsReader.GetFileStream(path);
}
DirectoryIteratorPtr CHardDiskDumpPartitionDevice::GetDirectory(const char*)
{
assert(false);
return DirectoryIteratorPtr();
}

View file

@ -0,0 +1,37 @@
#pragma once
#include "../Ioman_Device.h"
#include "hdd/PfsReader.h"
namespace Iop
{
namespace Ioman
{
class CHardDiskDumpDevice : public CDevice
{
public:
CHardDiskDumpDevice(std::unique_ptr<Framework::CStream>);
virtual ~CHardDiskDumpDevice() = default;
Framework::CStream* GetFile(uint32, const char*) override;
DirectoryIteratorPtr GetDirectory(const char*) override;
DevicePtr Mount(const char*) override;
private:
std::unique_ptr<Framework::CStream> m_stream;
};
class CHardDiskDumpPartitionDevice : public CDevice
{
public:
CHardDiskDumpPartitionDevice(Framework::CStream&, uint32);
virtual ~CHardDiskDumpPartitionDevice() = default;
Framework::CStream* GetFile(uint32, const char*) override;
DirectoryIteratorPtr GetDirectory(const char*) override;
private:
Hdd::CPfsReader m_pfsReader;
};
}
}

View file

@ -9,7 +9,10 @@
#include "AppConfig.h"
#include "BootablesDbClient.h"
#include "BootablesProcesses.h"
#include "hdd/HddDefs.h"
#include "discimages/ChdImageStream.h"
#include "iop/ioman/McDumpDevice.h"
#include "iop/ioman/HardDiskDumpDevice.h"
#include "iop/Iop_NamcoArcade.h"
#include "iop/namco_arcade/Iop_NamcoAcCdvd.h"
#include "iop/namco_arcade/Iop_NamcoAcRam.h"
@ -26,6 +29,7 @@ struct ARCADE_MACHINE_DEF
std::string name;
std::string dongleFileName;
std::string cdvdFileName;
std::string hddFileName;
std::string boot;
std::vector<PATCH> patches;
};
@ -69,6 +73,10 @@ ARCADE_MACHINE_DEF ReadArcadeMachineDefinition(const fs::path& arcadeDefPath)
{
def.cdvdFileName = defJson["cdvd"]["name"];
}
if(defJson.contains("hdd"))
{
def.hddFileName = defJson["hdd"]["name"];
}
def.boot = defJson["boot"];
if(defJson.contains("patches"))
{
@ -89,7 +97,7 @@ void PrepareArcadeEnvironment(CPS2VM* virtualMachine, const ARCADE_MACHINE_DEF&
throw std::runtime_error(string_format("Failed to find '%s' in arcade ROMs directory.", romArchiveFileName.c_str()));
}
//Mount CDVD or HDD
//Mount CDVD
if(!def.cdvdFileName.empty())
{
fs::path cdvdPath = arcadeRomPath / def.id / def.cdvdFileName;
@ -106,6 +114,23 @@ void PrepareArcadeEnvironment(CPS2VM* virtualMachine, const ARCADE_MACHINE_DEF&
virtualMachine->CDROM0_SyncPath();
}
//Mount HDD
if(!def.hddFileName.empty())
{
fs::path hddPath = arcadeRomPath / def.id / def.hddFileName;
if(!fs::exists(hddPath))
{
throw std::runtime_error(string_format("Failed to find '%s' in game's directory.", def.hddFileName.c_str()));
}
auto imageStream = std::make_unique<CChdImageStream>(std::make_unique<Framework::CStdStream>(hddPath.string().c_str(), "rb"));
assert(imageStream->GetUnitSize() == Hdd::g_sectorSize);
auto device = std::make_shared<Iop::Ioman::CHardDiskDumpDevice>(std::move(imageStream));
auto iopBios = dynamic_cast<CIopBios*>(virtualMachine->m_iop->m_bios.get());
iopBios->GetIoman()->RegisterDevice("hdd0", device);
}
std::vector<uint8> mcDumpContents;
{