diff --git a/CHANGES b/CHANGES index 9d9c5d807..0162d2de5 100644 --- a/CHANGES +++ b/CHANGES @@ -7,7 +7,7 @@ Features: - Cheat code support in homebrew ports - Acclerometer and gyro support for controllers on PC - Support for combo "Super Game Boy Color" SGB + GBC ROM hacks - - Improved support for HuC-3 mapper + - Improved support for HuC-3 mapper, including RTC - Support for 64 kiB SRAM saves used in some bootlegs - Discord Rich Presence now supports time elapsed - Additional scaling shaders diff --git a/include/mgba/internal/gb/mbc.h b/include/mgba/internal/gb/mbc.h index 2a6c5a3bd..a63de3b84 100644 --- a/include/mgba/internal/gb/mbc.h +++ b/include/mgba/internal/gb/mbc.h @@ -42,9 +42,18 @@ struct GBMBCRTCSaveBuffer { uint32_t latchedDaysHi; uint64_t unixTime; }; + +struct GBMBCHuC3SaveBuffer { + uint8_t regs[0x80]; + uint64_t latchedUnix; +}; + void GBMBCRTCRead(struct GB* gb); void GBMBCRTCWrite(struct GB* gb); +void GBMBCHuC3Read(struct GB* gb); +void GBMBCHuC3Write(struct GB* gb); + CXX_GUARD_END #endif diff --git a/src/gb/gb.c b/src/gb/gb.c index 43f5d93a0..186231bd6 100644 --- a/src/gb/gb.c +++ b/src/gb/gb.c @@ -143,8 +143,12 @@ void GBYankROM(struct GB* gb) { static void GBSramDeinit(struct GB* gb) { if (gb->sramVf) { gb->sramVf->unmap(gb->sramVf, gb->memory.sram, gb->sramSize); - if (gb->memory.mbcType == GB_MBC3_RTC && gb->sramVf == gb->sramRealVf) { - GBMBCRTCWrite(gb); + if (gb->sramVf == gb->sramRealVf) { + if (gb->memory.mbcType == GB_MBC3_RTC) { + GBMBCRTCWrite(gb); + } else if (gb->memory.mbcType == GB_HuC3) { + GBMBCHuC3Write(gb); + } } gb->sramVf = NULL; } else if (gb->memory.sram) { @@ -163,6 +167,8 @@ bool GBLoadSave(struct GB* gb, struct VFile* vf) { if (gb->memory.mbcType == GB_MBC3_RTC) { GBMBCRTCRead(gb); + } else if (gb->memory.mbcType == GB_HuC3) { + GBMBCHuC3Read(gb); } } return vf; @@ -246,6 +252,8 @@ void GBSramClean(struct GB* gb, uint32_t frameCount) { } if (gb->memory.mbcType == GB_MBC3_RTC) { GBMBCRTCWrite(gb); + } else if (gb->memory.mbcType == GB_HuC3) { + GBMBCHuC3Write(gb); } if (gb->sramVf == gb->sramRealVf) { if (gb->memory.sram && gb->sramVf->sync(gb->sramVf, gb->memory.sram, gb->sramSize)) { diff --git a/src/gb/mbc.c b/src/gb/mbc.c index e2837e2f1..99190627c 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -440,6 +440,8 @@ void GBMBCInit(struct GB* gb) { if (gb->memory.mbcType == GB_MBC3_RTC) { GBMBCRTCRead(gb); + } else if (gb->memory.mbcType == GB_HuC3) { + GBMBCHuC3Read(gb); } } @@ -1692,6 +1694,21 @@ uint8_t _GBHitekRead(struct GBMemory* memory, uint16_t address) { } } +static void _appendSaveSuffix(struct GB* gb, const void* buffer, size_t size) { + struct VFile* vf = gb->sramVf; + if ((size_t) vf->size(vf) < gb->sramSize + size) { + // Writing past the end of the file can invalidate the file mapping + vf->unmap(vf, gb->memory.sram, gb->sramSize); + gb->memory.sram = NULL; + } + vf->seek(vf, gb->sramSize, SEEK_SET); + vf->write(vf, buffer, size); + if (!gb->memory.sram) { + gb->memory.sram = vf->map(vf, gb->sramSize, MAP_WRITE); + GBMBCSwitchSramBank(gb, gb->memory.sramCurrentBank); + } +} + void GBMBCRTCRead(struct GB* gb) { struct GBMBCRTCSaveBuffer rtcBuffer; struct VFile* vf = gb->sramVf; @@ -1735,15 +1752,41 @@ void GBMBCRTCWrite(struct GB* gb) { STORE_32LE(gb->memory.rtcRegs[4], 0, &rtcBuffer.latchedDaysHi); STORE_64LE(gb->memory.rtcLastLatch, 0, &rtcBuffer.unixTime); - if ((size_t) vf->size(vf) < gb->sramSize + sizeof(rtcBuffer)) { - // Writing past the end of the file can invalidate the file mapping - vf->unmap(vf, gb->memory.sram, gb->sramSize); - gb->memory.sram = NULL; + _appendSaveSuffix(gb, &rtcBuffer, sizeof(rtcBuffer)); +} + +void GBMBCHuC3Read(struct GB* gb) { + struct GBMBCHuC3SaveBuffer buffer; + struct VFile* vf = gb->sramVf; + if (!vf) { + return; } vf->seek(vf, gb->sramSize, SEEK_SET); - vf->write(vf, &rtcBuffer, sizeof(rtcBuffer)); - if (!gb->memory.sram) { - gb->memory.sram = vf->map(vf, gb->sramSize, MAP_WRITE); - GBMBCSwitchSramBank(gb, gb->memory.sramCurrentBank); + if (vf->read(vf, &buffer, sizeof(buffer)) < (ssize_t) sizeof(buffer)) { + return; } + + size_t i; + for (i = 0; i < 0x80; ++i) { + gb->memory.mbcState.huc3.registers[i * 2] = buffer.regs[i] & 0xF; + gb->memory.mbcState.huc3.registers[i * 2 + 1] = buffer.regs[i] >> 4; + } + LOAD_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix); +} + +void GBMBCHuC3Write(struct GB* gb) { + struct VFile* vf = gb->sramVf; + if (!vf) { + return; + } + + struct GBMBCHuC3SaveBuffer buffer; + size_t i; + for (i = 0; i < 0x80; ++i) { + buffer.regs[i] = gb->memory.mbcState.huc3.registers[i * 2] & 0xF; + buffer.regs[i] |= gb->memory.mbcState.huc3.registers[i * 2 + 1] << 4; + } + STORE_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix); + + _appendSaveSuffix(gb, &buffer, sizeof(buffer)); }