diff --git a/include/Card.hpp b/include/Card.hpp index 7865bb3..b326a5c 100644 --- a/include/Card.hpp +++ b/include/Card.hpp @@ -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); diff --git a/src/Card.cpp b/src/Card.cpp index 7131a1b..aed852e 100644 --- a/src/Card.cpp +++ b/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 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(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) diff --git a/test/main.cpp b/test/main.cpp index 35af412..ae6f683 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -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; }