From 9e234b3bf5676ee280dca1c141cf7155a0e5b28d Mon Sep 17 00:00:00 2001 From: goyuken Date: Tue, 12 Aug 2014 00:34:19 +0000 Subject: [PATCH] vba next: some gamedb and saveram hookups, not all finished --- BizHawk.Client.Common/RomLoader.cs | 4 +- .../Consoles/Nintendo/GBA/LibVBANext.cs | 42 +- .../Consoles/Nintendo/GBA/VBANext.cs | 14 +- output/dll/libvbanext.dll | Bin 442368 -> 442368 bytes vbanext/instance.cpp | 476 +++++++----------- vbanext/instance.h | 9 + 6 files changed, 236 insertions(+), 309 deletions(-) diff --git a/BizHawk.Client.Common/RomLoader.cs b/BizHawk.Client.Common/RomLoader.cs index a678d5ae10..ad9c7bb599 100644 --- a/BizHawk.Client.Common/RomLoader.cs +++ b/BizHawk.Client.Common/RomLoader.cs @@ -424,13 +424,13 @@ namespace BizHawk.Client.Common case "GBA": if (false) { - var gba = new GBA(nextComm); + var gba = new GBA(nextComm); // meteor gba.Load(rom.RomData); nextEmulator = gba; } else { - var gba = new VBANext(rom.RomData, nextComm); + var gba = new VBANext(rom.RomData, nextComm, game); nextEmulator = gba; } break; diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/LibVBANext.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/LibVBANext.cs index 335e7f57cf..5e9f9d5d06 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/LibVBANext.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/LibVBANext.cs @@ -27,6 +27,38 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA L = 512 } + [StructLayout(LayoutKind.Sequential)] + public struct FrontEndSettings + { + public enum SaveType : int + { + auto = 0, + eeprom = 1, + sram = 2, + flash = 3, + eeprom_sensor = 4, + none = 5 + } + public enum FlashSize : int + { + small = 0x10000, + big = 0x20000 + } + public SaveType saveType; + public FlashSize flashSize; + public bool enableRtc; + public bool mirroringEnable; + public bool skipBios; + + public static FrontEndSettings GetDefaults() + { + return new FrontEndSettings + { + flashSize = FlashSize.big + }; + } + } + /// /// create a new context /// @@ -51,7 +83,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA /// /// success [DllImport(dllname, CallingConvention = cc)] - public static extern bool LoadRom(IntPtr g, byte[] romfile, uint romfilelen, byte[] biosfile, uint biosfilelen); + public static extern bool LoadRom(IntPtr g, byte[] romfile, uint romfilelen, byte[] biosfile, uint biosfilelen, [In]ref FrontEndSettings settings); /// /// hard reset @@ -82,5 +114,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA public static extern void TxtStateSave(IntPtr g, [In]ref TextStateFPtrs ff); [DllImport(dllname, CallingConvention = cc)] public static extern void TxtStateLoad(IntPtr g, [In]ref TextStateFPtrs ff); + + [DllImport(dllname, CallingConvention = cc)] + public static extern int SaveRamSize(IntPtr g); + [DllImport(dllname, CallingConvention = cc)] + public static extern bool SaveRamSave(IntPtr g, byte[] data, int length); + [DllImport(dllname, CallingConvention = cc)] + public static extern bool SaveRamLoad(IntPtr g, byte[] data, int length); + } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/VBANext.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/VBANext.cs index 637933fb0c..7898b2d6df 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/VBANext.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/VBANext.cs @@ -16,7 +16,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA { IntPtr Core; - public VBANext(byte[] romfile, CoreComm nextComm) + public VBANext(byte[] romfile, CoreComm nextComm, GameInfo gi) { CoreComm = nextComm; byte[] biosfile = CoreComm.CoreFileProvider.GetFirmware("GBA", "Bios", true, "GBA bios file is mandatory."); @@ -26,12 +26,19 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA if (biosfile.Length != 16 * 1024) throw new ArgumentException("BIOS file is not exactly 16K!"); + LibVBANext.FrontEndSettings FES; + FES.saveType = (LibVBANext.FrontEndSettings.SaveType)gi.GetInt("saveType", 0); + FES.flashSize = (LibVBANext.FrontEndSettings.FlashSize)gi.GetInt("flashSize", 0x10000); + FES.enableRtc = gi.GetInt("enableRtc", 0) != 0; + FES.mirroringEnable = gi.GetInt("mirroringEnable", 0) != 0; + FES.skipBios = false; // todo: hook me up as a syncsetting + Core = LibVBANext.Create(); if (Core == IntPtr.Zero) throw new InvalidOperationException("Create() returned nullptr!"); try { - if (!LibVBANext.LoadRom(Core, romfile, (uint)romfile.Length, biosfile, (uint)biosfile.Length)) + if (!LibVBANext.LoadRom(Core, romfile, (uint)romfile.Length, biosfile, (uint)biosfile.Length, ref FES)) throw new InvalidOperationException("LoadRom() returned false!"); CoreComm.VsyncNum = 262144; @@ -52,9 +59,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA } } - - - public void FrameAdvance(bool render, bool rendersound = true) { Frame++; diff --git a/output/dll/libvbanext.dll b/output/dll/libvbanext.dll index 24f2b01f65b75212fcc507f21fcb4183cfec32b3..f1b70ead996bb7789398cf9b77dc3ef294b4cb12 100644 GIT binary patch delta 67695 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⪻P`s`nG0O6Je;yTzZ__a*-~PHn|6K~If{5_{u_ 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 yRC))Fw<4JL(2DmFZ8f6b21AfnAIwta!dcm> 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 delta 67529 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 diff --git a/vbanext/instance.cpp b/vbanext/instance.cpp index 4cf2918bab..e92a883c0b 100644 --- a/vbanext/instance.cpp +++ b/vbanext/instance.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -48,12 +49,7 @@ class Gigazoid #define FLASH_PROGRAM 8 #define FLASH_SETBANK 9 -#ifdef __LIBRETRO__ -extern uint8_t libretro_save_buf[0x20000 + 0x2000]; -uint8_t *flashSaveMemory = libretro_save_buf; -#else uint8_t flashSaveMemory[FLASH_128K_SZ]; -#endif int flashState; // = FLASH_READ_ARRAY; int flashReadState; // = FLASH_READ_ARRAY; @@ -64,11 +60,7 @@ int flashBank; // = 0; void flashInit (void) { -#ifdef __LIBRETRO__ - memset(flashSaveMemory, 0xff, 0x20000); -#else memset(flashSaveMemory, 0xff, sizeof(flashSaveMemory)); -#endif } void flashReset() @@ -78,22 +70,6 @@ void flashReset() flashBank = 0; } -void flashSetSize(int size) -{ - if(size == 0x10000) { - flashDeviceID = 0x1b; - flashManufacturerID = 0x32; - } else { - flashDeviceID = 0x13; //0x09; - flashManufacturerID = 0x62; //0xc2; - } - // Added to make 64k saves compatible with 128k ones - // (allow wrongfuly set 64k saves to work for Pokemon games) - if ((size == 0x20000) && (flashSize == 0x10000)) - memcpy((uint8_t *)(flashSaveMemory+0x10000), (uint8_t *)(flashSaveMemory), 0x10000); - flashSize = size; -} - uint8_t flashRead(uint32_t address) { address &= 0xFFFF; @@ -122,24 +98,14 @@ uint8_t flashRead(uint32_t address) void flashSaveDecide(uint32_t address, uint8_t byte) { - if(address == 0x0e005555) { - saveType = 2; + if (address == 0x0e005555) cpuSaveGameFunc = &Gigazoid::flashWrite; - } else { - saveType = 1; + else cpuSaveGameFunc = &Gigazoid::sramWrite; - } (this->*cpuSaveGameFunc)(address, byte); } -void flashDelayedWrite(uint32_t address, uint8_t byte) -{ - saveType = 2; - cpuSaveGameFunc = &Gigazoid::flashWrite; - flashWrite(address, byte); -} - void flashWrite(uint32_t address, uint8_t byte) { address &= 0xFFFF; @@ -243,13 +209,7 @@ int eepromByte; // = 0; int eepromBits; // = 0; int eepromAddress; // = 0; -#ifdef __LIBRETRO__ -// Workaround for broken-by-design GBA save semantics. -extern u8 libretro_save_buf[0x20000 + 0x2000]; -u8 *eepromData = libretro_save_buf + 0x20000; -#else u8 eepromData[0x2000]; -#endif u8 eepromBuffer[16]; bool eepromInUse; // = false; @@ -257,11 +217,7 @@ int eepromSize; // = 512; void eepromInit (void) { -#ifdef __LIBRETRO__ - memset(eepromData, 255, 0x2000); -#else memset(eepromData, 255, sizeof(eepromData)); -#endif } void eepromReset (void) @@ -401,18 +357,15 @@ u8 sramRead(u32 address) return flashSaveMemory[address & 0xFFFF]; } -void sramDelayedWrite(u32 address, u8 byte) -{ - saveType = 1; - cpuSaveGameFunc = &Gigazoid::sramWrite; - sramWrite(address, byte); -} - void sramWrite(u32 address, u8 byte) { flashSaveMemory[address & 0xFFFF] = byte; } +void dummyWrite(u32 address, u8 byte) +{ +} + /*============================================================ RTC ============================================================ */ @@ -437,32 +390,19 @@ typedef struct RTCCLOCKDATA rtcClockData; bool rtcEnabled; // = false; -void rtcEnable(bool e) -{ - rtcEnabled = e; -} - -bool rtcIsEnabled (void) -{ - return rtcEnabled; -} - u16 rtcRead(u32 address) { - if(rtcEnabled) + switch(address) { - switch(address) - { - case 0x80000c8: - return rtcClockData.byte2; - case 0x80000c6: - return rtcClockData.byte1; - case 0x80000c4: - return rtcClockData.byte0; - } + case 0x80000c8: + return rtcClockData.byte2; + case 0x80000c6: + return rtcClockData.byte1; + case 0x80000c4: + return rtcClockData.byte0; + default: + return 0; } - - return READ16LE((&rom[address & 0x1FFFFFE])); } static u8 toBCD(u8 value) @@ -2764,7 +2704,6 @@ int gfxBG3X; int gfxBG3Y; bool ioReadable[0x400]; -int gbaSaveType; // used to remember the save type on reset //int gfxLastVCOUNT = 0; @@ -3054,7 +2993,7 @@ INLINE u32 CPUReadHalfWord(u32 address) case 10: case 11: case 12: - if(address == 0x80000c4 || address == 0x80000c6 || address == 0x80000c8) + if(rtcEnabled && (address == 0x80000c4 || address == 0x80000c6 || address == 0x80000c8)) value = rtcRead(address); else value = READ16LE(((u16 *)&rom[address & 0x1FFFFFE])); @@ -3194,14 +3133,11 @@ INLINE void CPUWriteMemory(u32 address, u32 value) WRITE32LE(((u32 *)&oam[address & 0x3fc]), value); break; case 0x0D: - if(cpuEEPROMEnabled) { + if (cpuEEPROMEnabled) eepromWrite(value); - break; - } break; case 0x0E: - if((!eepromInUse) | cpuSramEnabled | cpuFlashEnabled) - (this->*cpuSaveGameFunc)(address, (u8)value); + (this->*cpuSaveGameFunc)(address, (u8)value); break; default: break; @@ -3247,8 +3183,7 @@ INLINE void CPUWriteHalfWord(u32 address, u16 value) eepromWrite((u8)value); break; case 14: - if((!eepromInUse) | cpuSramEnabled | cpuFlashEnabled) - (this->*cpuSaveGameFunc)(address, (u8)value); + (this->*cpuSaveGameFunc)(address, (u8)value); break; default: break; @@ -3362,11 +3297,8 @@ INLINE void CPUWriteByte(u32 address, u8 b) eepromWrite(b); break; case 14: - if ((saveType != 5) && ((!eepromInUse) | cpuSramEnabled | cpuFlashEnabled)) - { - (this->*cpuSaveGameFunc)(address, b); - break; - } + (this->*cpuSaveGameFunc)(address, b); + break; default: break; } @@ -8860,14 +8792,14 @@ INLINE u32 gfxDecreaseBrightness(u32 color, int coeff) /*============================================================ GBA.CPP ============================================================ */ -int saveType; static const bool useBios = true; bool skipBios; -bool cpuIsMultiBoot; -int cpuSaveType; +// it's a few bytes in the linkscript to make a multiboot image work in normal boot as well, +// and most of the ones i've seen have done that, so this is not terribly useful +static const bool cpuIsMultiBoot = false; +int cpuSaveType; // used only in init() to set up function pointers bool enableRtc; bool mirroringEnable; -bool skipSaveGameBattery; int cpuDmaCount; @@ -8880,10 +8812,8 @@ u16 pix[2 * PIX_BUFFER_SCREEN_WIDTH * 160]; uint8_t oam[0x400]; uint8_t ioMem[0x400]; -bool cpuSramEnabled; // = true; -bool cpuFlashEnabled; // = true; -bool cpuEEPROMEnabled; // = true; -bool cpuEEPROMSensorEnabled; // = false; +bool cpuEEPROMEnabled; // true to process writes to EEPROM at 0dxxxxxx +bool cpuEEPROMSensorEnabled; // eeprom motion sensor? code is mostly disabled #ifndef LSB_FIRST bool cpuBiosSwapped = false; @@ -8955,97 +8885,6 @@ INLINE int CPUUpdateTicks (void) graphics.layerEnable = io_registers[REG_DISPCNT]; \ } -// TODO: Batteryram stuffs -/* -bool CPUWriteBatteryFile(const char *fileName) -{ - if(gbaSaveType == 0) - { - if(eepromInUse) - gbaSaveType = 3; - else - switch(saveType) - { - case 1: - gbaSaveType = 1; - break; - case 2: - gbaSaveType = 2; - break; - } - } - - if((gbaSaveType) && (gbaSaveType!=5)) - { - FILE *file = fopen(fileName, "wb"); - - if(!file) { - systemMessage("Error creating file %s", fileName); - return false; - } - - // only save if Flash/Sram in use or EEprom in use - if(gbaSaveType != 3) { - if(gbaSaveType == 2) { - if(fwrite(flashSaveMemory, 1, flashSize, file) != (size_t)flashSize) { - fclose(file); - return false; - } - } else { - if(fwrite(flashSaveMemory, 1, 0x10000, file) != 0x10000) { - fclose(file); - return false; - } - } - } else { - if(fwrite(eepromData, 1, eepromSize, file) != (size_t)eepromSize) { - fclose(file); - return false; - } - } - fclose(file); - } - return true; -} - -bool CPUReadBatteryFile(const char *fileName) -{ - FILE *file = fopen(fileName, "rb"); - - if(!file) - return false; - - // check file size to know what we should read - fseek(file, 0, SEEK_END); - - long size = ftell(file); - fseek(file, 0, SEEK_SET); - - if(size == 512 || size == 0x2000) { - if(fread(eepromData, 1, size, file) != (size_t)size) { - fclose(file); - return false; - } - } else { - if(size == 0x20000) { - if(fread(flashSaveMemory, 1, 0x20000, file) != 0x20000) { - fclose(file); - return false; - } - flashSetSize(0x20000); - } else { - if(fread(flashSaveMemory, 1, 0x10000, file) != 0x10000) { - fclose(file); - return false; - } - flashSetSize(0x10000); - } - } - fclose(file); - return true; -} -*/ - int CPULoadRom(const u8 *romfile, const u32 romfilelen) { if (cpuIsMultiBoot) @@ -12155,17 +11994,41 @@ void CPUUpdateRegister(uint32_t address, uint16_t value) void CPUInit(const u8 *biosfile, const u32 biosfilelen) { -#ifndef LSB_FIRST - if(!cpuBiosSwapped) { - for(unsigned int i = 0; i < sizeof(myROM)/4; i++) { - WRITE32LE(&myROM[i], myROM[i]); - } - cpuBiosSwapped = true; + eepromInUse = false; + switch(cpuSaveType) + { + case 0: // automatic + default: + cpuEEPROMEnabled = true; + cpuEEPROMSensorEnabled = false; + cpuSaveGameFunc = &Gigazoid::flashSaveDecide; // EEPROM usage is automatically detected + break; + case 1: // EEPROM + cpuEEPROMEnabled = true; + cpuEEPROMSensorEnabled = false; + cpuSaveGameFunc = &Gigazoid::dummyWrite; // EEPROM usage is automatically detected + break; + case 2: // SRAM + cpuEEPROMEnabled = false; + cpuEEPROMSensorEnabled = false; + cpuSaveGameFunc = &Gigazoid::sramWrite; + break; + case 3: // FLASH + cpuEEPROMEnabled = false; + cpuEEPROMSensorEnabled = false; + cpuSaveGameFunc = &Gigazoid::flashWrite; + break; + case 4: // EEPROM+Sensor + cpuEEPROMEnabled = true; + cpuEEPROMSensorEnabled = true; + cpuSaveGameFunc = &Gigazoid::dummyWrite; // EEPROM usage is automatically detected + break; + case 5: // NONE + cpuEEPROMEnabled = false; + cpuEEPROMSensorEnabled = false; + cpuSaveGameFunc = &Gigazoid::dummyWrite; + break; } -#endif - gbaSaveType = 0; - eepromInUse = 0; - saveType = 0; memcpy(bios, biosfile, 16384); @@ -12225,6 +12088,7 @@ void CPUInit(const u8 *biosfile, const u32 biosfilelen) for(i = 0x304; i < 0x400; i++) ioReadable[i] = false; + // what is this? if(romSize < 0x1fe2000) { *((uint16_t *)&rom[0x1fe209c]) = 0xdffa; // SWI 0xFA *((uint16_t *)&rom[0x1fe209e]) = 0x4770; // BX LR @@ -12259,29 +12123,14 @@ void CPUInit(const u8 *biosfile, const u32 biosfilelen) address_lut[0x32] = &io_registers[REG_BG3PB]; address_lut[0x34] = &io_registers[REG_BG3PC]; address_lut[0x36] = &io_registers[REG_BG3PD]; - address_lut[0x40] = &io_registers[REG_WIN0H]; - address_lut[0x42] = &io_registers[REG_WIN1H]; - address_lut[0x44] = &io_registers[REG_WIN0V]; - address_lut[0x46] = &io_registers[REG_WIN1V]; + address_lut[0x40] = &io_registers[REG_WIN0H]; + address_lut[0x42] = &io_registers[REG_WIN1H]; + address_lut[0x44] = &io_registers[REG_WIN0V]; + address_lut[0x46] = &io_registers[REG_WIN1V]; } void CPUReset (void) { - if(gbaSaveType == 0) - { - if(eepromInUse) - gbaSaveType = 3; - else - switch(saveType) - { - case 1: - gbaSaveType = 1; - break; - case 2: - gbaSaveType = 2; - break; - } - } rtcReset(); memset(&bus.reg[0], 0, sizeof(bus.reg)); // clean registers memset(oam, 0, 0x400); // clean OAM @@ -12450,11 +12299,9 @@ void CPUReset (void) dma2Dest = 0; dma3Source = 0; dma3Dest = 0; - cpuSaveGameFunc = &Gigazoid::flashSaveDecide; renderLine = &Gigazoid::mode0RenderLine; fxOn = false; windowOn = false; - saveType = 0; graphics.layerEnable = io_registers[REG_DISPCNT]; memset(line[0], -1, 240 * sizeof(u32)); @@ -12505,57 +12352,9 @@ void CPUReset (void) BIOS_RegisterRamReset(0xfe); else if(!useBios && !cpuIsMultiBoot) BIOS_RegisterRamReset(0xff); + else if (skipBios) + BIOS_RegisterRamReset(0xff); // ?? - switch(cpuSaveType) { - case 0: // automatic - cpuSramEnabled = true; - cpuFlashEnabled = true; - cpuEEPROMEnabled = true; - cpuEEPROMSensorEnabled = false; - saveType = gbaSaveType = 0; - break; - case 1: // EEPROM - cpuSramEnabled = false; - cpuFlashEnabled = false; - cpuEEPROMEnabled = true; - cpuEEPROMSensorEnabled = false; - saveType = gbaSaveType = 3; - // EEPROM usage is automatically detected - break; - case 2: // SRAM - cpuSramEnabled = true; - cpuFlashEnabled = false; - cpuEEPROMEnabled = false; - cpuEEPROMSensorEnabled = false; - cpuSaveGameFunc = &Gigazoid::sramDelayedWrite; // to insure we detect the write - saveType = gbaSaveType = 1; - break; - case 3: // FLASH - cpuSramEnabled = false; - cpuFlashEnabled = true; - cpuEEPROMEnabled = false; - cpuEEPROMSensorEnabled = false; - cpuSaveGameFunc = &Gigazoid::flashDelayedWrite; // to insure we detect the write - saveType = gbaSaveType = 2; - break; - case 4: // EEPROM+Sensor - cpuSramEnabled = false; - cpuFlashEnabled = false; - cpuEEPROMEnabled = true; - cpuEEPROMSensorEnabled = true; - // EEPROM usage is automatically detected - saveType = gbaSaveType = 3; - break; - case 5: // NONE - cpuSramEnabled = false; - cpuFlashEnabled = false; - cpuEEPROMEnabled = false; - cpuEEPROMSensorEnabled = false; - // no save at all - saveType = gbaSaveType = 5; - break; - } - ARM_PREFETCH; } @@ -13030,8 +12829,6 @@ void Gigazoid_Init() eepromInUse = false; eepromSize = 512; - rtcEnabled = false; - // this is constant now // soundSampleRate = 22050; @@ -13041,14 +12838,6 @@ void Gigazoid_Init() armIrqEnable = true; armMode = 0x1f; - romSize = 0x2000000; - - cpuSramEnabled = true; - cpuFlashEnabled = true; - cpuEEPROMEnabled = true; - cpuEEPROMSensorEnabled = false; - - cpuSaveGameFunc = &Gigazoid::flashSaveDecide; renderLine = &Gigazoid::mode0RenderLine; #define ARRAYINIT(n) memcpy((n), (n##_init), sizeof(n)) @@ -13205,8 +12994,7 @@ templatevoid SyncState(NewState *ns) EVS(cpuSaveGameFunc, &Gigazoid::flashWrite, 1); EVS(cpuSaveGameFunc, &Gigazoid::sramWrite, 2); EVS(cpuSaveGameFunc, &Gigazoid::flashSaveDecide, 3); - EVS(cpuSaveGameFunc, &Gigazoid::flashDelayedWrite, 4); - EVS(cpuSaveGameFunc, &Gigazoid::sramDelayedWrite, 5); + EVS(cpuSaveGameFunc, &Gigazoid::dummyWrite, 4); EES(cpuSaveGameFunc, nullptr); NSS(fxOn); @@ -13237,7 +13025,6 @@ templatevoid SyncState(NewState *ns) NSS(gfxBG3Y); NSS(ioReadable); - NSS(gbaSaveType); NSS(stopState); @@ -13261,13 +13048,10 @@ templatevoid SyncState(NewState *ns) NSS(timer3Reload); NSS(timer3ClockReload); - NSS(saveType); NSS(skipBios); - NSS(cpuIsMultiBoot); NSS(cpuSaveType); NSS(enableRtc); NSS(mirroringEnable); - NSS(skipSaveGameBattery); NSS(cpuDmaCount); @@ -13278,8 +13062,6 @@ templatevoid SyncState(NewState *ns) NSS(oam); NSS(ioMem); - NSS(cpuSramEnabled); - NSS(cpuFlashEnabled); NSS(cpuEEPROMEnabled); NSS(cpuEEPROMSensorEnabled); @@ -13311,6 +13093,63 @@ templatevoid SyncState(NewState *ns) NSS(lagged); } +// load a legacy battery ram file to a place where it might work, who knows +void LoadLegacyBatteryRam(const u8 *data, int len) +{ + std::memcpy(eepromData, data, std::min(len, sizeof(eepromData))); + std::memcpy(flashSaveMemory, data, std::min(len, sizeof(flashSaveMemory))); + if (len <= 0x10000) + { + // can salvage broken pokeymans saves in some cases + std::memcpy(flashSaveMemory + 0x10000, data, std::min(len, 0x10000)); + } +} + +templatebool SyncBatteryRam(NewState *ns) +{ + // if we were given a positive ID from the gamedb, we can choose to save/load only that type + // else, we save\load everything -- even if we used our knowledge of the current state to + // save only what was needed, we'd have to save that metadata as well for load + + // since we may get other people's crap, try to detect that here + char batteryramid[16]; + if (!isReader) + { + std::memcpy(batteryramid, "BIZVBANEXTBATTRY", 16); + } + NSS(batteryramid); + if (isReader) + { + if (std::memcmp(batteryramid, "BIZVBANEXTBATTRY", 16) != 0) + return false; + } + + switch (cpuSaveType) + { + default: + case 0: // auto + NSS(flashSaveMemory); + NSS(eepromData); + break; + case 1: + case 4: // eeprom + PSS(eepromData, eepromSize); + break; + case 2: // sram + // should only be 32K, but vba uses 64K as a stand-in for both SRAM (guess no game ever checks mirroring?), + // and for 64K flash where the program never issues any flash commands + PSS(flashSaveMemory, 0x10000); + break; + case 3: // flash + PSS(flashSaveMemory, flashSize); + break; + case 5: // none + break; + } + + return true; +} + Gigazoid() { Gigazoid_Init(); @@ -13320,25 +13159,31 @@ templatevoid SyncState(NewState *ns) { } - bool LoadRom(const u8 *romfile, const u32 romfilelen, const u8 *biosfile, const u32 biosfilelen) + bool LoadRom(const u8 *romfile, const u32 romfilelen, const u8 *biosfile, const u32 biosfilelen, const FrontEndSettings &settings) { if (biosfilelen != 16384) return false; - // todo: set cpuismultiboot if (!CPULoadRom(romfile, romfilelen)) return false; - // todo: populate these 4 variables from a syncsetting or a gamedb (vba_over.ini) - cpuSaveType = 0; - flashSize = 0x10000; - enableRtc = false; - mirroringEnable = false; + cpuSaveType = settings.cpuSaveType; + flashSize = settings.flashSize; + enableRtc = settings.enableRtc; + mirroringEnable = settings.mirroringEnable; + skipBios = settings.skipBios; + + if(flashSize == 0x10000) + { + flashDeviceID = 0x1b; + flashManufacturerID = 0x32; + } + else + { + flashDeviceID = 0x13; //0x09; + flashManufacturerID = 0x62; //0xc2; + } - if(flashSize == 0x10000 || flashSize == 0x20000) - flashSetSize(flashSize); - if(enableRtc) - rtcEnable(enableRtc); doMirroring(mirroringEnable); CPUInit(biosfile, biosfilelen); @@ -13396,9 +13241,9 @@ EXPORT void Destroy(Gigazoid *g) delete g; } -EXPORT int LoadRom(Gigazoid *g, const u8 *romfile, const u32 romfilelen, const u8 *biosfile, const u32 biosfilelen) +EXPORT int LoadRom(Gigazoid *g, const u8 *romfile, const u32 romfilelen, const u8 *biosfile, const u32 biosfilelen, const FrontEndSettings *settings) { - return g->LoadRom(romfile, romfilelen, biosfile, biosfilelen); + return g->LoadRom(romfile, romfilelen, biosfile, biosfilelen, *settings); } EXPORT void Reset(Gigazoid *g) @@ -13412,6 +13257,35 @@ EXPORT int FrameAdvance(Gigazoid *g, int input, u32 *videobuffer, s16 *audiobuff return g->FrameAdvance(input, videobuffer, audiobuffer, numsamp); } +EXPORT int SaveRamSize(Gigazoid *g) +{ + NewStateDummy dummy; + g->SyncBatteryRam(&dummy); + return dummy.GetLength(); +} + +EXPORT int SaveRamSave(Gigazoid *g, char *data, int length) +{ + NewStateExternalBuffer saver(data, length); + g->SyncBatteryRam(&saver); + return !saver.Overflow() && saver.GetLength() == length; +} + +EXPORT int SaveRamLoad(Gigazoid *g, const char *data, int length) +{ + NewStateExternalBuffer loader(const_cast(data), length); + if (g->SyncBatteryRam(&loader)) + { + return !loader.Overflow() && loader.GetLength() == length; + } + else + { + // couldn't find the magic signature at the top, so try a salvage load + g->LoadLegacyBatteryRam(reinterpret_cast(data), length); + return true; + } +} + EXPORT int BinStateSize(Gigazoid *g) { NewStateDummy dummy; diff --git a/vbanext/instance.h b/vbanext/instance.h index b0d58f4419..025f88336b 100644 --- a/vbanext/instance.h +++ b/vbanext/instance.h @@ -1,6 +1,15 @@ #ifndef INSTANCE_H #define INSTANCE_H +struct FrontEndSettings +{ + int cpuSaveType; // [0auto] 1eeprom 2sram 3flash 4eeprom+sensor 5none + int flashSize; // [0x10000] 0x20000 + int enableRtc; // [false] true + int mirroringEnable; // [false] true + int skipBios; // [false] true +}; + #define FLASH_128K_SZ 0x20000 #define EEPROM_IDLE 0