Play-/Source/saves/McDumpReader.cpp

195 lines
5.6 KiB
C++
Raw Permalink Normal View History

2022-09-21 18:14:03 -04:00
#include "McDumpReader.h"
#include <cstring>
#include <cassert>
2023-01-19 20:27:11 -05:00
#include <stdexcept>
2023-05-02 08:56:25 -04:00
#include "maybe_unused.h"
2022-09-21 18:14:03 -04:00
//Source:
//http://www.csclub.uwaterloo.ca:11068/mymc/ps2mcfs.html
static const char g_magic[29] = "Sony PS2 Memory Card Format ";
CMcDumpReader::CMcDumpReader(Framework::CStream& stream)
2022-09-23 11:43:52 -04:00
: m_stream(stream)
2022-09-21 18:14:03 -04:00
{
m_stream.Read(&m_header, sizeof(HEADER));
if(memcmp(m_header.magic, g_magic, sizeof(m_header.magic)))
{
throw std::exception();
}
2022-09-23 11:43:52 -04:00
2022-09-21 18:14:03 -04:00
assert(m_header.pageSize == 0x200);
assert(m_header.pagesPerCluster == 2);
2022-09-23 11:43:52 -04:00
//Some cards have ECC which are not taken in consideration by pageSize.
//Compute raw page size which contains ECC codes
2022-09-23 11:43:52 -04:00
uint32 totalPageCount = m_header.pagesPerCluster * m_header.clustersPerCard;
size_t expectedSize = m_header.pageSize * totalPageCount;
size_t actualSize = m_stream.GetLength();
assert(actualSize >= expectedSize);
uint32 pageSpareSize = (actualSize - expectedSize) / totalPageCount;
//Some other card dumps have ECC but have a wrong size which makes the above check fail
//If we have 0 at 0x20C, we most likely have a dump that has ECC interleaved with pages
//Problematic dumps: Smash Court Pro Tournament
{
m_stream.Seek(0x20C, Framework::STREAM_SEEK_SET);
uint32 spareAreaCheck = m_stream.Read32();
if(spareAreaCheck == 0)
{
pageSpareSize = 0x10;
}
}
2022-12-20 15:39:16 -05:00
m_rawPageSize = m_header.pageSize + pageSpareSize;
2022-09-21 18:14:03 -04:00
}
CMcDumpReader::Directory CMcDumpReader::ReadDirectory(uint32 dirCluster, int32 entryCount)
2022-09-21 18:14:03 -04:00
{
std::vector<DIRENTRY> result;
CFatReader reader(*this, dirCluster);
2022-09-21 18:14:03 -04:00
DIRENTRY baseDirEntry = {};
2023-05-02 08:56:25 -04:00
FRAMEWORK_MAYBE_UNUSED uint32 readAmount = reader.Read(&baseDirEntry, sizeof(DIRENTRY));
2022-09-21 18:14:03 -04:00
assert(readAmount == sizeof(DIRENTRY));
result.push_back(baseDirEntry);
if(entryCount == -1)
{
//Use number of entries from entry itself, only seems to work with root dir.
entryCount = baseDirEntry.length;
}
assert(entryCount >= 2);
for(uint32 i = 0; i < (entryCount - 1); i++)
2022-09-21 18:14:03 -04:00
{
DIRENTRY dirEntry = {};
readAmount = reader.Read(&dirEntry, sizeof(DIRENTRY));
if(readAmount != sizeof(DIRENTRY))
{
throw std::runtime_error("Failed to read directory entry.");
}
2022-09-21 18:14:03 -04:00
result.push_back(dirEntry);
}
return result;
}
CMcDumpReader::Directory CMcDumpReader::ReadRootDirectory()
{
return ReadDirectory(m_header.rootDirCluster);
}
std::vector<uint8> CMcDumpReader::ReadFile(uint32 fileCluster, uint32 fileSize)
{
std::vector<uint8> result;
CFatReader reader(*this, fileCluster);
2022-09-21 18:14:03 -04:00
result.resize(fileSize);
2023-05-02 08:56:25 -04:00
FRAMEWORK_MAYBE_UNUSED uint32 readAmount = reader.Read(result.data(), fileSize);
2022-09-21 18:14:03 -04:00
assert(readAmount == fileSize);
return result;
}
void CMcDumpReader::ReadCluster(uint32 clusterIndex, void* buffer)
{
uint32 pageIndex = clusterIndex * m_header.pagesPerCluster;
assert(m_rawPageSize >= m_header.pageSize);
uint32 spareSize = m_rawPageSize - m_header.pageSize;
m_stream.Seek(pageIndex * m_rawPageSize, Framework::STREAM_SEEK_SET);
for(uint32 i = 0; i < m_header.pagesPerCluster; i++)
{
m_stream.Read(reinterpret_cast<uint8*>(buffer) + (i * m_header.pageSize), m_header.pageSize);
m_stream.Seek(spareSize, Framework::STREAM_SEEK_CUR);
}
}
void CMcDumpReader::ReadClusterCached(uint32 clusterIndex, void* buffer)
2022-09-21 18:14:03 -04:00
{
static const uint32 clusterSize = m_header.pagesPerCluster * m_header.pageSize;
auto clusterIterator = m_clusterCache.find(clusterIndex);
if(clusterIterator == std::end(m_clusterCache))
{
Cluster cluster;
cluster.resize(clusterSize);
ReadCluster(clusterIndex, cluster.data());
auto result = m_clusterCache.emplace(std::make_pair(clusterIndex, std::move(cluster)));
clusterIterator = result.first;
}
2022-09-23 11:43:52 -04:00
const auto& cluster = clusterIterator->second;
assert(cluster.size() == clusterSize);
memcpy(buffer, cluster.data(), cluster.size());
}
CMcDumpReader::CFatReader::CFatReader(CMcDumpReader& parent, uint32 cluster)
2022-09-23 11:43:52 -04:00
: m_parent(parent)
{
2022-09-23 11:48:40 -04:00
assert(m_parent.m_header.pageSize == MC_PAGE_SIZE);
assert(m_parent.m_header.pagesPerCluster == MC_PAGES_PER_CLUSTER);
2022-09-21 18:14:03 -04:00
ReadFatCluster(cluster);
}
uint32 CMcDumpReader::CFatReader::Read(void* buffer, uint32 size)
{
if(m_cluster == INVALID_CLUSTER_IDX)
{
return 0;
}
uint32 amountRead = 0;
uint8* byteBuffer = reinterpret_cast<uint8*>(buffer);
while(1)
{
2022-09-23 11:48:40 -04:00
if(m_bufferIndex == MC_CLUSTER_SIZE)
2022-09-21 18:14:03 -04:00
{
uint32 entry = GetNextFatClusterEntry(m_cluster);
static const uint32 nextValidBit = (1 << 31);
if(entry & nextValidBit)
{
ReadFatCluster(entry & ~nextValidBit);
}
else
{
m_cluster = INVALID_CLUSTER_IDX;
break;
}
}
2022-09-23 11:48:40 -04:00
assert(m_bufferIndex < MC_CLUSTER_SIZE);
uint32 amountAvail = MC_CLUSTER_SIZE - m_bufferIndex;
2022-09-21 18:14:03 -04:00
uint32 toRead = std::min(amountAvail, size);
memcpy(byteBuffer, m_buffer + m_bufferIndex, toRead);
m_bufferIndex += toRead;
byteBuffer += toRead;
amountRead += toRead;
size -= toRead;
if(size == 0)
{
break;
}
}
return amountRead;
}
void CMcDumpReader::CFatReader::ReadFatCluster(uint32 clusterIndex)
{
m_cluster = clusterIndex;
m_parent.ReadCluster(m_cluster + m_parent.m_header.allocOffset, m_buffer);
2022-09-21 18:14:03 -04:00
m_bufferIndex = 0;
}
uint32 CMcDumpReader::CFatReader::GetNextFatClusterEntry(uint32 clusterIndex)
{
2022-09-23 11:48:40 -04:00
uint32 clusterTemp[MC_CLUSTER_SIZE / sizeof(uint32)];
2022-09-23 11:43:52 -04:00
2022-09-21 18:14:03 -04:00
uint32 fatOffset = clusterIndex & 0xFF;
uint32 indirectIndex = clusterIndex / 256;
uint32 indirectOffset = indirectIndex & 0xFF;
uint32 dblIndirectIndex = indirectIndex / 256;
uint32 indirectClusterNum = m_parent.m_header.ifcList[dblIndirectIndex];
2022-09-23 11:43:52 -04:00
m_parent.ReadClusterCached(indirectClusterNum, clusterTemp);
uint32 fatClusterNum = clusterTemp[indirectOffset];
2022-09-23 11:43:52 -04:00
m_parent.ReadClusterCached(fatClusterNum, clusterTemp);
uint32 entry = clusterTemp[fatOffset];
2022-09-21 18:14:03 -04:00
return entry;
}