mirror of https://github.com/AxioDL/kabufuda.git
Various fixes and proper BAT handling
This commit is contained in:
parent
8b17918205
commit
e7b47d7a41
|
@ -13,6 +13,7 @@ uint32_t constexpr BlockSize = 0x2000;
|
|||
uint32_t constexpr MaxFiles = 127;
|
||||
uint32_t constexpr FSTBlocks = 5;
|
||||
uint32_t constexpr MbitToBlocks = 0x10;
|
||||
uint32_t constexpr BATSize = 0xFFB;
|
||||
|
||||
/**
|
||||
* @brief The EPermissions enum
|
||||
|
@ -61,6 +62,7 @@ enum class EEncoding : uint16_t
|
|||
|
||||
class File
|
||||
{
|
||||
friend class FileHandle;
|
||||
friend class Directory;
|
||||
friend class Card;
|
||||
#pragma pack(push, 4)
|
||||
|
@ -86,7 +88,9 @@ class File
|
|||
};
|
||||
uint8_t __raw[0x40];
|
||||
};
|
||||
|
||||
#pragma pop()
|
||||
void swapEndian();
|
||||
|
||||
public:
|
||||
File() {}
|
||||
|
@ -98,8 +102,15 @@ public:
|
|||
struct FileHandle
|
||||
{
|
||||
File* file = nullptr;
|
||||
uint16_t curBlock = 0;
|
||||
uint16_t blockOffset = 0;
|
||||
uint32_t offset =0;
|
||||
operator bool() const { return file != nullptr; }
|
||||
FileHandle(File* file = nullptr)
|
||||
: file(file),
|
||||
curBlock(file ? file->m_firstBlock : 0)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class BlockAllocationTable
|
||||
|
@ -121,15 +132,17 @@ class BlockAllocationTable
|
|||
};
|
||||
#pragma pop()
|
||||
|
||||
void swapEndian();
|
||||
|
||||
public:
|
||||
explicit BlockAllocationTable(uint32_t blockCount = (uint32_t(ECardSize::Card2043Mb) * MbitToBlocks));
|
||||
BlockAllocationTable(uint8_t data[BlockSize]);
|
||||
~BlockAllocationTable() {}
|
||||
|
||||
uint16_t getNextBlock(uint16_t block) const;
|
||||
uint16_t nextFreeBlock(uint16_t maxBlocks, uint16_t startingBlock = FSTBlocks) const;
|
||||
uint16_t nextFreeBlock(uint16_t maxBlock, uint16_t startingBlock) const;
|
||||
bool clear(uint16_t first, uint16_t count);
|
||||
uint16_t allocateBlocks(uint16_t count);
|
||||
uint16_t allocateBlocks(uint16_t count, uint16_t maxBlocks);
|
||||
};
|
||||
|
||||
class Directory
|
||||
|
@ -150,6 +163,8 @@ class Directory
|
|||
};
|
||||
#pragma pop()
|
||||
|
||||
void swapEndian();
|
||||
|
||||
public:
|
||||
Directory();
|
||||
Directory(uint8_t data[BlockSize]);
|
||||
|
@ -184,24 +199,23 @@ class Card
|
|||
};
|
||||
uint8_t __raw[BlockSize];
|
||||
};
|
||||
|
||||
void swapEndian();
|
||||
#pragma pop()
|
||||
|
||||
SystemString m_filename;
|
||||
Directory m_dir;
|
||||
Directory m_dirBackup;
|
||||
Directory* m_dirInUse = nullptr;
|
||||
Directory* m_currentDir;
|
||||
Directory* m_previousDir;
|
||||
BlockAllocationTable m_bat;
|
||||
BlockAllocationTable m_batBackup;
|
||||
BlockAllocationTable* m_batInUse = nullptr;
|
||||
BlockAllocationTable* m_currentBat;
|
||||
BlockAllocationTable* m_previousBat;
|
||||
|
||||
uint16_t m_maxBlock;
|
||||
char m_game[5] = {'\0'};
|
||||
char m_maker[3] = {'\0'};
|
||||
void setChecksum(uint16_t checksum)
|
||||
{
|
||||
m_checksum = SBig(checksum);
|
||||
m_checksumInv = SBig(checksum ^ 0xFFFF);
|
||||
}
|
||||
|
||||
public:
|
||||
Card();
|
||||
Card(const SystemString& filepath, const char* game = nullptr, const char* maker=nullptr);
|
||||
|
|
247
src/Card.cpp
247
src/Card.cpp
|
@ -7,6 +7,20 @@
|
|||
|
||||
namespace kabufuda
|
||||
{
|
||||
void Card::swapEndian()
|
||||
{
|
||||
m_formatTime = SBig(m_formatTime);
|
||||
m_sramBias = SBig(m_sramBias);
|
||||
m_sramLanguage = SBig(m_sramLanguage);
|
||||
m_unknown = SBig(m_unknown);
|
||||
m_deviceId = SBig(m_deviceId);
|
||||
m_sizeMb = SBig(m_sizeMb);
|
||||
m_encoding = SBig(m_encoding);
|
||||
m_updateCounter = SBig(m_updateCounter);
|
||||
m_checksum = SBig(m_checksum);
|
||||
m_checksumInv = SBig(m_checksumInv);
|
||||
}
|
||||
|
||||
Card::Card()
|
||||
{
|
||||
memset(__raw, 0xFF, BlockSize);
|
||||
|
@ -25,42 +39,58 @@ Card::Card(const SystemString& filename, const char* game, const char* maker)
|
|||
if (file)
|
||||
{
|
||||
fread(__raw, 1, BlockSize, file);
|
||||
m_maxBlock = m_sizeMb * MbitToBlocks;
|
||||
swapEndian();
|
||||
fread(m_dir.__raw, 1, BlockSize, file);
|
||||
fread(m_dirBackup.__raw, 1, BlockSize, file);
|
||||
fread(m_bat.__raw, 1, BlockSize, file);
|
||||
fread(m_batBackup.__raw, 1, BlockSize, file);
|
||||
if (m_dir.m_updateCounter < m_dirBackup.m_updateCounter)
|
||||
m_dirInUse = &m_dirBackup;
|
||||
if (m_dir.m_updateCounter > m_dirBackup.m_updateCounter)
|
||||
{
|
||||
m_currentDir = &m_dir;
|
||||
m_previousDir = &m_dirBackup;
|
||||
}
|
||||
else
|
||||
m_dirInUse = &m_dir;
|
||||
{
|
||||
m_currentDir = &m_dirBackup;
|
||||
m_previousDir = &m_dir;
|
||||
}
|
||||
|
||||
if (m_bat.m_updateCounter < m_batBackup.m_updateCounter)
|
||||
m_batInUse = &m_batBackup;
|
||||
if (m_bat.m_updateCounter > m_batBackup.m_updateCounter)
|
||||
{
|
||||
m_currentBat = &m_bat;
|
||||
m_previousBat = &m_batBackup;
|
||||
}
|
||||
else
|
||||
m_batInUse = &m_bat;
|
||||
{
|
||||
m_currentBat = &m_batBackup;
|
||||
m_previousBat = &m_bat;
|
||||
}
|
||||
m_currentDir->swapEndian();
|
||||
m_previousDir->swapEndian();
|
||||
m_currentBat->swapEndian();
|
||||
m_previousBat->swapEndian();
|
||||
}
|
||||
}
|
||||
|
||||
Card::~Card()
|
||||
{
|
||||
commit();
|
||||
}
|
||||
|
||||
FileHandle Card::openFile(const char* filename)
|
||||
{
|
||||
return {m_dirInUse->getFile(m_game, m_maker, filename)};
|
||||
return {m_currentDir->getFile(m_game, m_maker, filename)};
|
||||
}
|
||||
|
||||
FileHandle Card::createFile(const char* filename, size_t size)
|
||||
{
|
||||
File* f = m_dirInUse->getFirstFreeFile(m_game, m_maker, filename);
|
||||
uint16_t block = m_batInUse->allocateBlocks(size / BlockSize);
|
||||
File* f = m_currentDir->getFirstFreeFile(m_game, m_maker, filename);
|
||||
uint16_t block = m_currentBat->allocateBlocks(size / BlockSize, m_maxBlock);
|
||||
if (f && block != 0xFFFF)
|
||||
{
|
||||
//f->m_modifiedTime = SBig(uint32_t(getGCTime()));
|
||||
f->m_firstBlock = SBig(block);
|
||||
f->m_blockCount = SBig(uint16_t(size / BlockSize));
|
||||
commit();
|
||||
f->m_modifiedTime = getGCTime();
|
||||
f->m_firstBlock = block;
|
||||
f->m_blockCount = size / BlockSize;
|
||||
return {f};
|
||||
}
|
||||
return {};
|
||||
|
@ -72,13 +102,21 @@ void Card::write(FileHandle* f, const void* buf, size_t size)
|
|||
rewind(mc);
|
||||
if (mc)
|
||||
{
|
||||
f->file->m_modifiedTime = SBig(uint32_t(getGCTime()));
|
||||
size_t blockOffset = (SBig(f->file->m_firstBlock) * BlockSize) + f->offset;
|
||||
f->file->m_modifiedTime = getGCTime();
|
||||
if (f->blockOffset > BlockSize)
|
||||
{
|
||||
/* TODO: Add error */
|
||||
if (f->curBlock == 0xFFFF)
|
||||
return;
|
||||
|
||||
f->curBlock = m_currentBat->m_map[f->curBlock];
|
||||
}
|
||||
size_t blockOffset = (f->curBlock * BlockSize) + f->offset;
|
||||
fseek(mc, blockOffset, SEEK_SET);
|
||||
fwrite(buf, 1, size, mc);
|
||||
fclose(mc);
|
||||
f->offset += size;
|
||||
commit();
|
||||
f->blockOffset += size;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -144,7 +182,7 @@ void Card::format(EDeviceId id, ECardSize size, EEncoding encoding)
|
|||
{
|
||||
memset(__raw, 0xFF, BlockSize);
|
||||
uint64_t rand = uint64_t(getGCTime());
|
||||
m_formatTime = SBig(rand);
|
||||
m_formatTime = rand;
|
||||
for (int i = 0; i < 12; i++)
|
||||
{
|
||||
rand = (((rand * (uint64_t)0x41c64e6d) + (uint64_t)0x3039) >> 16);
|
||||
|
@ -153,30 +191,37 @@ void Card::format(EDeviceId id, ECardSize size, EEncoding encoding)
|
|||
rand &= (uint64_t)0x7fffULL;
|
||||
}
|
||||
|
||||
m_sramBias = g_SRAM.counter_bias;
|
||||
m_sramBias = SBig(g_SRAM.counter_bias);
|
||||
m_sramLanguage = SBig(g_SRAM.lang);
|
||||
m_unknown = 0; /* 1 works for slot A, 0 both */
|
||||
m_deviceId = 0;
|
||||
m_sizeMb = SBig(uint16_t(size));
|
||||
m_encoding = SBig(uint16_t(encoding));
|
||||
m_sizeMb = uint16_t(size);
|
||||
m_maxBlock = m_sizeMb * MbitToBlocks;
|
||||
m_encoding = uint16_t(encoding);
|
||||
calculateChecksum((uint16_t*)__raw, 0xFE, &m_checksum, &m_checksumInv);
|
||||
m_dir = Directory();
|
||||
m_dirBackup = m_dir;
|
||||
m_dirInUse = &m_dir;
|
||||
m_bat = BlockAllocationTable(uint32_t(size) * MbitToBlocks);
|
||||
m_batBackup = m_bat;
|
||||
m_batInUse = &m_bat;
|
||||
m_sizeMb = SBig(uint16_t(size));
|
||||
m_encoding = SBig(uint16_t(encoding));
|
||||
|
||||
FILE* f = Fopen(m_filename.c_str(), _S("wb"));
|
||||
if (f)
|
||||
{
|
||||
swapEndian();
|
||||
fwrite(__raw, 1, BlockSize, f);
|
||||
fwrite(m_dir.__raw, 1, BlockSize, f);
|
||||
fwrite(m_dirBackup.__raw, 1, BlockSize, f);
|
||||
fwrite(m_bat.__raw, 1, BlockSize, f);
|
||||
fwrite(m_batBackup.__raw, 1, BlockSize, f);
|
||||
swapEndian();
|
||||
Directory tmpDir = m_dir;
|
||||
tmpDir.swapEndian();
|
||||
fwrite(tmpDir.__raw, 1, BlockSize, f);
|
||||
tmpDir = m_dirBackup;
|
||||
tmpDir.swapEndian();
|
||||
fwrite(tmpDir.__raw, 1, BlockSize, f);
|
||||
BlockAllocationTable tmpBat = m_bat;
|
||||
tmpBat.swapEndian();
|
||||
fwrite(tmpBat.__raw, 1, BlockSize, f);
|
||||
tmpBat = m_batBackup;
|
||||
tmpBat.swapEndian();
|
||||
fwrite(tmpBat.__raw, 1, BlockSize, f);
|
||||
uint32_t dataLen = ((uint32_t(size) * MbitToBlocks) - 5) * BlockSize;
|
||||
std::unique_ptr<uint8_t[]> data(new uint8_t[dataLen]);
|
||||
memset(data.get(), 0xFF, dataLen);
|
||||
|
@ -197,11 +242,51 @@ void Card::commit()
|
|||
FILE* f = Fopen(m_filename.c_str(), _S("r+"));
|
||||
if (f)
|
||||
{
|
||||
Directory updateDir = *m_currentDir;
|
||||
updateDir.m_updateCounter++;
|
||||
|
||||
*m_previousDir = updateDir;
|
||||
if (m_previousDir == &m_dir)
|
||||
{
|
||||
m_currentDir = &m_dir;
|
||||
m_previousDir = &m_dirBackup;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_currentDir = &m_dirBackup;
|
||||
m_previousDir = &m_dir;
|
||||
}
|
||||
|
||||
BlockAllocationTable updateBat = *m_currentBat;
|
||||
updateBat.m_updateCounter++;
|
||||
|
||||
*m_previousBat = updateBat;
|
||||
if (m_previousBat == &m_bat)
|
||||
{
|
||||
m_currentBat = &m_bat;
|
||||
m_previousBat = &m_batBackup;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_currentBat = &m_batBackup;
|
||||
m_previousBat = &m_bat;
|
||||
}
|
||||
|
||||
swapEndian();
|
||||
fwrite(__raw, 1, BlockSize, f);
|
||||
fwrite(m_dir.__raw, 1, BlockSize, f);
|
||||
fwrite(m_dirBackup.__raw, 1, BlockSize, f);
|
||||
fwrite(m_bat.__raw, 1, BlockSize, f);
|
||||
fwrite(m_batBackup.__raw, 1, BlockSize, f);
|
||||
swapEndian();
|
||||
Directory tmpDir = *m_currentDir;
|
||||
tmpDir.swapEndian();
|
||||
fwrite(tmpDir.__raw, 1, BlockSize, f);
|
||||
tmpDir = *m_previousDir;
|
||||
tmpDir.swapEndian();
|
||||
fwrite(tmpDir.__raw, 1, BlockSize, f);
|
||||
BlockAllocationTable tmpBat = *m_currentBat;
|
||||
tmpBat.swapEndian();
|
||||
fwrite(tmpBat.__raw, 1, BlockSize, f);
|
||||
tmpBat = *m_previousBat;
|
||||
tmpBat.swapEndian();
|
||||
fwrite(tmpBat.__raw, 1, BlockSize, f);
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
@ -213,17 +298,42 @@ File::File(char data[])
|
|||
|
||||
File::File(const char* filename)
|
||||
{
|
||||
memset(m_filename, 0, 0x20);
|
||||
memset(__raw, 0xFF, 0x40);
|
||||
memset(m_filename, 0, 32);
|
||||
size_t len = strlen(filename);
|
||||
len = std::min<size_t>(len, 32);
|
||||
memcpy(m_filename, filename, len);
|
||||
}
|
||||
void File::swapEndian()
|
||||
{
|
||||
m_modifiedTime = SBig(m_modifiedTime);
|
||||
m_imageOffset = SBig(m_imageOffset);
|
||||
m_iconFmt = SBig(m_iconFmt);
|
||||
m_animSpeed = SBig(m_animSpeed);
|
||||
m_firstBlock = SBig(m_firstBlock);
|
||||
m_blockCount = SBig(m_blockCount);
|
||||
m_reserved2 = SBig(m_reserved2);
|
||||
m_commentAddr = SBig(m_commentAddr);
|
||||
}
|
||||
|
||||
|
||||
void BlockAllocationTable::swapEndian()
|
||||
{
|
||||
m_checksum = SBig(m_checksum);
|
||||
m_checksumInv = SBig(m_checksumInv);
|
||||
m_updateCounter = SBig(m_updateCounter);
|
||||
m_freeBlocks = SBig(m_freeBlocks);
|
||||
m_lastAllocated = SBig(m_lastAllocated);
|
||||
std::for_each(std::begin(m_map), std::end(m_map), [](uint16_t& val){
|
||||
val = SBig(val);
|
||||
});
|
||||
}
|
||||
|
||||
BlockAllocationTable::BlockAllocationTable(uint32_t blockCount)
|
||||
{
|
||||
memset(__raw, 0, BlockSize);
|
||||
m_freeBlocks = SBig(uint16_t(blockCount - 5));
|
||||
m_lastAllocated = SBig(uint16_t(4));
|
||||
m_freeBlocks = blockCount - FSTBlocks;
|
||||
m_lastAllocated = 4;
|
||||
calculateChecksum((uint16_t*)(__raw + 4), 0xFFE, &m_checksum, &m_checksumInv);
|
||||
}
|
||||
|
||||
|
@ -232,25 +342,21 @@ uint16_t BlockAllocationTable::getNextBlock(uint16_t block) const
|
|||
if ((block < FSTBlocks) || (block > 4091))
|
||||
return 0;
|
||||
|
||||
return SBig(m_map[block - FSTBlocks]);
|
||||
return m_map[block - FSTBlocks];
|
||||
}
|
||||
|
||||
uint16_t BlockAllocationTable::nextFreeBlock(uint16_t maxBlock, uint16_t startingBlock) const
|
||||
{
|
||||
if (m_freeBlocks > 0)
|
||||
{
|
||||
maxBlock = std::min(maxBlock, uint16_t(0xFFB));
|
||||
for (uint16_t i = startingBlock ; i < maxBlock ; ++i)
|
||||
{
|
||||
if (m_map[i - FSTBlocks] == 0)
|
||||
return i;
|
||||
}
|
||||
maxBlock = std::min(maxBlock, uint16_t(BATSize));
|
||||
for (uint16_t i = startingBlock; i < maxBlock; ++i)
|
||||
if (m_map[i - FSTBlocks] == 0)
|
||||
return i;
|
||||
|
||||
for (uint16_t i = FSTBlocks ; i < startingBlock ; ++i)
|
||||
{
|
||||
if (m_map[i - FSTBlocks] == 0)
|
||||
return i;
|
||||
}
|
||||
for (uint16_t i = FSTBlocks; i < startingBlock; ++i)
|
||||
if (m_map[i - FSTBlocks] == 0)
|
||||
return i;
|
||||
}
|
||||
|
||||
return 0xFFFF;
|
||||
|
@ -272,21 +378,45 @@ bool BlockAllocationTable::clear(uint16_t first, uint16_t count)
|
|||
|
||||
for (size_t i= 0 ; i < length ; ++i)
|
||||
m_map[blocks.at(i) - FSTBlocks] = 0;
|
||||
m_freeBlocks = SBig(uint16_t(m_freeBlocks) + count);
|
||||
m_freeBlocks += count;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t BlockAllocationTable::allocateBlocks(uint16_t count)
|
||||
uint16_t BlockAllocationTable::allocateBlocks(uint16_t count, uint16_t maxBlocks)
|
||||
{
|
||||
uint16_t freeBlock = nextFreeBlock(m_lastAllocated + count);
|
||||
uint16_t firstBlock = nextFreeBlock(maxBlocks - FSTBlocks, m_lastAllocated + 1);
|
||||
uint16_t freeBlock = firstBlock;
|
||||
if (freeBlock != 0xFFFF)
|
||||
{
|
||||
uint16_t tmpCount = count;
|
||||
while ((count--) > 0)
|
||||
m_map[freeBlock + count] = 0xFFFF;
|
||||
{
|
||||
if (count == 0)
|
||||
m_map[(freeBlock - FSTBlocks)] = 0xFFFF;
|
||||
else
|
||||
{
|
||||
m_map[(freeBlock - FSTBlocks)] = freeBlock + 1;
|
||||
freeBlock = nextFreeBlock(maxBlocks - FSTBlocks, m_lastAllocated + 1);
|
||||
}
|
||||
m_lastAllocated = freeBlock;
|
||||
}
|
||||
|
||||
m_freeBlocks -= tmpCount;
|
||||
}
|
||||
return freeBlock;
|
||||
return firstBlock;
|
||||
}
|
||||
|
||||
void Directory::swapEndian()
|
||||
{
|
||||
std::for_each(std::begin(m_files), std::end(m_files), [](File& f){
|
||||
f.swapEndian();
|
||||
});
|
||||
|
||||
m_updateCounter = SBig(m_updateCounter);
|
||||
m_checksum = SBig(m_checksum);
|
||||
m_checksumInv = SBig(m_checksumInv);
|
||||
}
|
||||
|
||||
Directory::Directory()
|
||||
|
@ -318,12 +448,13 @@ File* Directory::getFirstFreeFile(char* game, char* maker, const char* filename)
|
|||
if (m_files[i].m_id[0] == 0xFF)
|
||||
{
|
||||
File* ret = &m_files[i];
|
||||
memset(ret->__raw, 0xFF, sizeof(File));
|
||||
*ret = File(filename);
|
||||
if (game && strlen(game) == 4)
|
||||
memcpy(ret->m_id, game, 4);
|
||||
if (maker && strlen(maker) == 2)
|
||||
memcpy(ret->m_maker, maker, 2);
|
||||
return &m_files[i];
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -351,11 +482,11 @@ void calculateChecksum(uint16_t* data, size_t len, uint16_t* checksum, uint16_t*
|
|||
*checksumInv = 0;
|
||||
for (size_t i = 0; i < len; i++)
|
||||
{
|
||||
*checksum += SBig(data[i]);
|
||||
*checksumInv += SBig(uint16_t(data[i] ^ 0xFFFF));
|
||||
*checksum += data[i];
|
||||
*checksumInv += data[i] ^ 0xFFFF;
|
||||
}
|
||||
*checksum = SBig(uint16_t(*checksum));
|
||||
*checksumInv = SBig(uint16_t(*checksumInv));
|
||||
*checksum = *checksum;
|
||||
*checksumInv = *checksumInv;
|
||||
if (*checksum == 0xFFFF)
|
||||
*checksum = 0;
|
||||
if (*checksumInv == 0xFFFF)
|
||||
|
|
|
@ -4,16 +4,19 @@
|
|||
int main()
|
||||
{
|
||||
kabufuda::Card mc{_S("test.USA.raw"), "GM8E", "01"};
|
||||
mc.format(kabufuda::EDeviceId::SlotA, kabufuda::ECardSize::Card123Mb);
|
||||
mc.createFile("MetroidPrime A", kabufuda::BlockSize);
|
||||
kabufuda::FileHandle f = mc.openFile("MetroidPrime A");
|
||||
//mc.format(kabufuda::EDeviceId::SlotA, kabufuda::ECardSize::Card2043Mb);
|
||||
kabufuda::FileHandle f = mc.openFile("MetroidPrime B");
|
||||
if (!f)
|
||||
f = mc.createFile("MetroidPrime B", kabufuda::BlockSize);
|
||||
|
||||
if (f)
|
||||
{
|
||||
const char* test = "Metroid Prime is Cool";
|
||||
const char* test = "Metroid Prime B is Cool";
|
||||
size_t len = strlen(test);
|
||||
mc.write(&f, test, len + 1);
|
||||
uint16_t derp = 1234;
|
||||
mc.write(&f, &derp, 2);
|
||||
mc.commit();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue