#include "common.h" #include #include #include "UsrCheatRepository.h" #define CRCPOLY 0xEDB88320 static u32 crc32(const void* buffer, u32 length) { u32 crc = ~0u; const u8* p = (u8*)buffer; while (length--) { crc ^= *p++; for (int i = 0; i < 8; i++) { crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY : 0); } } return crc; } std::unique_ptr UsrCheatRepository::GetCheatsForGame(const FastFileRef& romFile) const { auto file = std::make_unique(); file->Open(romFile, FA_READ); auto headerBuffer = std::make_unique_for_overwrite(512); if (!file->ReadExact(headerBuffer.get(), 512)) { LOG_ERROR("Could not read rom header\n"); return nullptr; } file->Close(); u32 gameCode = *(u32*)(headerBuffer.get() + 0xC); u32 crc = crc32(headerBuffer.get(), 512); headerBuffer.reset(); return GetCheatsForGame(gameCode, crc); } void UsrCheatRepository::UpdateEnabledCheatsForGame(const std::unique_ptr& cheats) const { u32 cheatDataLength = 0; auto cheatData = cheats->GetCheatData(cheatDataLength); if (_usrCheatFile->Seek(cheats->GetFileOffset()) != FR_OK) { LOG_ERROR("Failed to seek to cheat data\n"); return; } u32 bytesWritten = 0; if (_usrCheatFile->Write(cheatData, cheatDataLength, bytesWritten) != FR_OK || bytesWritten != cheatDataLength) { LOG_ERROR("Failed to write cheat data\n"); return; } if (_usrCheatFile->Sync() != FR_OK) { LOG_ERROR("Failed to flush cheat data\n"); } } std::unique_ptr UsrCheatRepository::GetCheatsForGame(u32 gameCode, u32 headerCrc32) const { auto index = FindIndex(gameCode, headerCrc32); if (index == nullptr) { LOG_DEBUG("Cheats not found for %c%c%c%c - 0x%X\n", gameCode & 0xFF, (gameCode >> 8) & 0xFF, (gameCode >> 16) & 0xFF, gameCode >> 24, headerCrc32); return nullptr; } const u32 cheatDataLength = index->padding; // padding was set to the size in UsrCheatRepositoryFactory auto cheatData = std::make_unique_for_overwrite(cheatDataLength); if (_usrCheatFile->Seek(index->offset) != FR_OK) { LOG_ERROR("Failed to seek to cheat data\n"); return nullptr; } if (!_usrCheatFile->ReadExact(cheatData.get(), cheatDataLength)) { LOG_ERROR("Failed to read cheat data\n"); return nullptr; } u8* ptr = cheatData.get(); // game name const char* gameName = (const char*)ptr; ptr += strlen(gameName) + 1; // padding ptr = (u8*)(((u32)ptr + 3) & ~3); // 32-bit align // flags u32 flags = *(u32*)ptr; u32 totalNumberOfItems = flags & 0x0FFFFFFF; // u32 gameActive = flags >> 28; ptr += 4; // master codes ptr += 8 * 4; auto categories = (CheatCategory*)malloc(totalNumberOfItems * sizeof(CheatCategory)); u32 categoryCount = 0; auto cheats = (Cheat*)malloc(totalNumberOfItems * sizeof(Cheat)); u32 cheatCount = 0; while (ptr < cheatData.get() + cheatDataLength) { u32 itemFlags = *(u32*)ptr; bool isCategory = ((itemFlags >> 28) & 1) == 1; if (isCategory) { ParseCategory(categories[categoryCount], ptr); categoryCount++; } else { ParseCheat(cheats[cheatCount], ptr); cheatCount++; } } categories = (CheatCategory*)realloc(categories, categoryCount * sizeof(CheatCategory)); cheats = (Cheat*)realloc(cheats, cheatCount * sizeof(Cheat)); return std::make_unique( std::move(cheatData), cheatDataLength, index->offset, gameName, categories, categoryCount, cheats, cheatCount); } const usr_cheat_index_entry_t* UsrCheatRepository::FindIndex(u32 gameCode, u32 headerCrc32) const { if (_numberOfIndices != 0) { const auto index = std::lower_bound(_sortedIndices.get(), _sortedIndices.get() + _numberOfIndices, gameCode, [headerCrc32] (const usr_cheat_index_entry_t& entry, u32 value) { if (entry.gameCode != value) { return entry.gameCode < value; } return entry.headerCrc32 < headerCrc32; }); if (index != _sortedIndices.get() + _numberOfIndices && index->gameCode == gameCode && index->headerCrc32 == headerCrc32) { return index; } } return nullptr; } void UsrCheatRepository::ParseCategory(CheatCategory& category, u8*& ptr) const { // flags u32 itemFlags = *(u32*)ptr; ptr += 4; u32 numberOfItems = itemFlags & 0x00FFFFFF; bool isMaxOneCheatActive = ((itemFlags >> 24) & 1) == 1; // item name const char* itemName = (const char*)ptr; ptr += strlen(itemName) + 1; // item description const char* itemDescription = (const char*)ptr; ptr += strlen(itemDescription) + 1; // padding ptr = (u8*)(((u32)ptr + 3) & ~3); // 32-bit align auto categories = (CheatCategory*)malloc(numberOfItems * sizeof(CheatCategory)); u32 categoryCount = 0; auto cheats = (Cheat*)malloc(numberOfItems * sizeof(Cheat)); u32 cheatCount = 0; for (u32 i = 0; i < numberOfItems; i++) { u32 itemFlags = *(u32*)ptr; bool isCategory = ((itemFlags >> 28) & 1) == 1; if (isCategory) { ParseCategory(categories[categoryCount], ptr); categoryCount++; } else { ParseCheat(cheats[cheatCount], ptr); cheatCount++; } } categories = (CheatCategory*)realloc(categories, categoryCount * sizeof(CheatCategory)); cheats = (Cheat*)realloc(cheats, cheatCount * sizeof(Cheat)); new (&category) CheatCategory (itemName, itemDescription, isMaxOneCheatActive, categories, categoryCount, cheats, cheatCount); } void UsrCheatRepository::ParseCheat(Cheat& cheat, u8*& ptr) const { // flags u32* flagsPtr = (u32*)ptr; ptr += 4; // item name const char* itemName = (const char*)ptr; ptr += strlen(itemName) + 1; // item description const char* itemDescription = (const char*)ptr; ptr += strlen(itemDescription) + 1; // padding ptr = (u8*)(((u32)ptr + 3) & ~3); // 32-bit align // number of code words u32 numberOfCodeWords = *(u32*)ptr; ptr += 4; new (&cheat) Cheat(itemName, itemDescription, flagsPtr, ptr, numberOfCodeWords * 4); // code ptr += numberOfCodeWords * 4; }