From 96dcc1ded15f690a32a02c6b73f2fe621caed5bb Mon Sep 17 00:00:00 2001 From: Philippe Symons Date: Thu, 10 Oct 2024 12:54:17 +0200 Subject: [PATCH] Create dedicated Gen2ReproSaveManager class. This one allows you to read save data from the rom. But it doesn't let you write to them. That will have to be implemented in PokeMe64, because it needs transfer pak writes to specific MBC registers --- examples/extractReproSave/main.cpp | 13 ++- include/gen2/Gen2ReproSaveManager.h | 89 ++++++++++++++++++++ src/gen2/Gen2ReproSaveManager.cpp | 124 ++++++++++++++++++++++++++++ 3 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 include/gen2/Gen2ReproSaveManager.h create mode 100644 src/gen2/Gen2ReproSaveManager.cpp diff --git a/examples/extractReproSave/main.cpp b/examples/extractReproSave/main.cpp index d9fb5de..b913b96 100644 --- a/examples/extractReproSave/main.cpp +++ b/examples/extractReproSave/main.cpp @@ -1,5 +1,6 @@ #include "gen1/Gen1GameReader.h" #include "gen2/Gen2GameReader.h" +#include "gen2/Gen2ReproSaveManager.h" #include "RomReader.h" #include "SaveManager.h" #include "utils.h" @@ -39,6 +40,7 @@ int main(int argc, char** argv) GameboyCartridgeHeader cartridgeHeader; BufferBasedRomReader romReader(romBuffer, romFileSize); + Gen2ReproSaveManager saveManager(romReader, Gen2ReproType::ALIEXPRESS_TYPE1, false); readGameboyCartridgeHeader(romReader, cartridgeHeader); @@ -63,11 +65,19 @@ int main(int argc, char** argv) return 1; } + uint8_t savBankBuffer[GB_SRAM_BANK_SIZE]; + + for(uint8_t i=0; i < 4; ++i) + { + saveManager.read(savBankBuffer, GB_SRAM_BANK_SIZE); + fwrite(savBankBuffer, sizeof(uint8_t), GB_SRAM_BANK_SIZE, outFile); + } +#if 0 + // So, turns out that these reproductions dedicate an entire rom bank // for every SRAM bank. // As a matter of fact, each SRAM bank is located at an offset of 0x2000 within // said rom bank. - uint8_t savBankBuffer[GB_SRAM_BANK_SIZE]; const uint8_t* cur = romBuffer + GEN2_REPRO_SAVE_OFFSET_IN_ROM; const uint8_t* const end = cur + (GB_ROM_BANK_SIZE * 4); while(cur < end) @@ -76,6 +86,7 @@ int main(int argc, char** argv) fwrite(savBankBuffer, sizeof(uint8_t), GB_SRAM_BANK_SIZE, outFile); cur += GB_ROM_BANK_SIZE; } +#endif fclose(outFile); diff --git a/include/gen2/Gen2ReproSaveManager.h b/include/gen2/Gen2ReproSaveManager.h new file mode 100644 index 0000000..0966f28 --- /dev/null +++ b/include/gen2/Gen2ReproSaveManager.h @@ -0,0 +1,89 @@ +#ifndef _GEN2REPROSAVEMANAGER_H +#define _GEN2REPROSAVEMANAGER_H + +#include "RomReader.h" +#include "SaveManager.h" + +enum class Gen2ReproType +{ + INVALID, + ALIEXPRESS_TYPE1 +}; + +typedef struct Gen2ReproSRAMRomOffsetMetadata +{ + // A list of rom offsets for each SRAM bank + struct { + uint8_t romBankIndex; + uint16_t romBankOffset; + } sramBankOffsets[4]; +} Gen2ReproSRAMRomOffsetMetadata; + +/** + * @brief This class provides a way to read save data from a reproduction cart + * Reproduction carts save their data inside the rom data. + */ +class Gen2ReproSaveManager : public BaseSaveManager +{ +public: + Gen2ReproSaveManager(IRomReader& romReader, Gen2ReproType reproType, bool writeEnabled); + virtual ~Gen2ReproSaveManager(); + + /** + * @brief This function reads a byte, returns it and advances the internal pointer by 1 byte + * + * @return uint8_t + */ + bool readByte(uint8_t& outByte) override; + void writeByte(uint8_t byte) override; + + /** + * @brief This function reads multiple bytes into the specified output buffer + * @note: bytesToRead should be <= 0x2000 (SRAM bank size) + * + * @return whether the operation was successful + */ + bool read(uint8_t* outBuffer, uint32_t bytesToRead) override; + void write(const uint8_t* buffer, uint32_t bytesToWrite) override; + + /** + * @brief This function reads the current byte without advancing the internal pointer by 1 byte + * + * @return uint8_t + */ + uint8_t peek() override; + + /** + * @brief This function advances the internal pointer by the specified numBytes. + * This can be considered a relative seek + * + */ + bool advance(uint32_t numBytes = 1) override; + + /** + * @brief This function rewinds the internal pointer with the specified numBytes + * This can be considered a backward relative seek + */ + bool rewind(uint32_t numBytes = 1) override; + + /** + * @brief This function seeks to the specified absolute SRAM offset + * + * @param absoluteOffset + */ + bool seek(uint32_t absoluteOffset) override; + + /** + * @brief Returns the index of the current bank + */ + uint8_t getCurrentBankIndex() const override; +protected: +private: + IRomReader& romReader_; + Gen2ReproType reproType_; + const Gen2ReproSRAMRomOffsetMetadata* reproRomOffsetMeta_; + uint32_t absoluteOffset_; + bool writeEnabled_; +}; + +#endif \ No newline at end of file diff --git a/src/gen2/Gen2ReproSaveManager.cpp b/src/gen2/Gen2ReproSaveManager.cpp new file mode 100644 index 0000000..3a18a4d --- /dev/null +++ b/src/gen2/Gen2ReproSaveManager.cpp @@ -0,0 +1,124 @@ +#include "gen2/Gen2ReproSaveManager.h" + +static const Gen2ReproSRAMRomOffsetMetadata aliexpressReproRomOffsets = { + .sramBankOffsets = { + { + .romBankIndex = 0x74, + .romBankOffset = 0x6000 + }, + { + .romBankIndex = 0x75, + .romBankOffset = 0x6000 + }, + { + .romBankIndex = 0x76, + .romBankOffset = 0x6000 + }, + { + .romBankIndex = 0x77, + .romBankOffset = 0x6000 + } + } +}; + +static uint8_t calculateBankIndex(uint32_t absoluteSRAMOffset) +{ + return (absoluteSRAMOffset / 0x2000); +} + +static const Gen2ReproSRAMRomOffsetMetadata* getReproSaveRomOffsets(Gen2ReproType reproType) +{ + switch(reproType) + { + case Gen2ReproType::ALIEXPRESS_TYPE1: + return &aliexpressReproRomOffsets; + default: + return nullptr; + } +} + +Gen2ReproSaveManager::Gen2ReproSaveManager(IRomReader& romReader, Gen2ReproType reproType, bool writeEnabled) + : romReader_(romReader) + , reproType_(reproType) + , reproRomOffsetMeta_(getReproSaveRomOffsets(reproType)) + , absoluteOffset_(0) + , writeEnabled_(writeEnabled) +{ + seek(0); +} + +Gen2ReproSaveManager::~Gen2ReproSaveManager() +{ +} + +bool Gen2ReproSaveManager::readByte(uint8_t& outByte) +{ + return read(&outByte, 1); +} + +void Gen2ReproSaveManager::writeByte(uint8_t byte) +{ + write(&byte, 1); +} + +bool Gen2ReproSaveManager::read(uint8_t* outBuffer, uint32_t bytesToRead) +{ + bool ret = romReader_.read(outBuffer, bytesToRead); + if(!ret) + { + return ret; + } + + ret &= advance(bytesToRead); + + return ret; +} + +void Gen2ReproSaveManager::write(const uint8_t* buffer, uint32_t bytesToWrite) +{ + (void)buffer; + (void)bytesToWrite; + if(!writeEnabled_) + { + return; + } + + //TODO: +} + +uint8_t Gen2ReproSaveManager::peek() +{ + return romReader_.peek(); +} + +bool Gen2ReproSaveManager::advance(uint32_t numBytes) +{ + absoluteOffset_ += numBytes; + return seek(absoluteOffset_); +} + +bool Gen2ReproSaveManager::rewind(uint32_t numBytes) +{ + absoluteOffset_ -= numBytes; + return seek(absoluteOffset_); +} + +bool Gen2ReproSaveManager::seek(uint32_t absoluteOffset) +{ + const uint8_t bankIndex = calculateBankIndex(absoluteOffset); + + if(!reproRomOffsetMeta_ || bankIndex > 3) + { + return false; + } + + absoluteOffset_ = absoluteOffset; + + const uint16_t localOffset = absoluteOffset % 0x2000; + return romReader_.seekToRomPointer(reproRomOffsetMeta_->sramBankOffsets[bankIndex].romBankOffset + localOffset, reproRomOffsetMeta_->sramBankOffsets[bankIndex].romBankIndex); +} + +uint8_t Gen2ReproSaveManager::getCurrentBankIndex() const +{ + return calculateBankIndex(absoluteOffset_); +} \ No newline at end of file