diff --git a/src/CRC32.cpp b/src/CRC32.cpp index b51b1713..eabdf33f 100644 --- a/src/CRC32.cpp +++ b/src/CRC32.cpp @@ -52,7 +52,7 @@ void _inittable() } } -u32 CRC32(u8 *data, int len) +u32 CRC32(u8 *data, int len, u32 start) { if (!tableinited) { @@ -60,7 +60,7 @@ u32 CRC32(u8 *data, int len) tableinited = true; } - u32 crc = 0xFFFFFFFF; + u32 crc = start ^ 0xFFFFFFFF; while (len--) crc = (crc >> 8) ^ crctable[(crc & 0xFF) ^ *data++]; diff --git a/src/CRC32.h b/src/CRC32.h index 2107533a..5600a637 100644 --- a/src/CRC32.h +++ b/src/CRC32.h @@ -21,6 +21,6 @@ #include "types.h" -u32 CRC32(u8* data, int len); +u32 CRC32(u8* data, int len, u32 start=0); #endif // CRC32_H diff --git a/src/GBACart.cpp b/src/GBACart.cpp index f859a862..ca092f4f 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -43,7 +43,6 @@ const char SOLAR_SENSOR_GAMECODES[10][5] = bool CartInserted; u8* CartROM; u32 CartROMSize; -u32 CartCRC; u32 CartID; CartCommon* Cart; @@ -111,6 +110,15 @@ CartGame::~CartGame() if (SRAM) delete[] SRAM; } +u32 CartGame::Checksum() +{ + u32 crc = CRC32(ROM, 0xC0, 0); + + // TODO: hash more contents? + + return crc; +} + void CartGame::Reset() { memset(&GPIO, 0, sizeof(GPIO)); @@ -129,8 +137,6 @@ void CartGame::DoSavestate(Savestate* file) file->Var16(&GPIO.data); file->Var16(&GPIO.direction); - // logic mostly copied from NDSCart_SRAM - u32 oldlen = SRAMLength; file->Var32(&SRAMLength); @@ -151,9 +157,7 @@ void CartGame::DoSavestate(Savestate* file) { // no save data, clear the current state SRAMType = SaveType::S_NULL; - //if (SRAMFile) fclose(SRAMFile); SRAM = nullptr; - //SRAMFile = nullptr; return; } @@ -165,6 +169,9 @@ void CartGame::DoSavestate(Savestate* file) file->Var8(&SRAMFlashState.state); file->Var8((u8*)&SRAMType); + + if ((!file->Saving) && SRAM) + Platform::WriteGBASave(SRAM, SRAMLength, 0, SRAMLength); } void CartGame::SetupSave(u32 type) @@ -698,50 +705,32 @@ void DoSavestate(Savestate* file) { file->Section("GBAC"); // Game Boy Advance Cartridge - // logic mostly copied from NDSCart + // little state here + // no need to save OpenBusDecay, it will be set later - // first we need to reload the cart itself, - // since unlike with DS, it's not loaded in advance - - file->Var32(&CartROMSize); - if (!CartROMSize) // no GBA cartridge state? nothing to do here (no! FIXME) + u32 carttype = 0; + u32 cartchk = 0; + if (Cart) { - // do eject the cartridge if something is inserted - EjectCart(); - return; + carttype = Cart->Type(); + cartchk = Cart->Checksum(); } - u32 oldCRC = CartCRC; - file->Var32(&CartCRC); - - if (CartCRC != oldCRC) + if (file->Saving) { - // delete and reallocate ROM so that it is zero-padded to its full length - if (CartROM) delete[] CartROM; - CartROM = new u8[CartROMSize]; + file->Var32(&carttype); + file->Var32(&cartchk); } + else + { + u32 savetype; + file->Var32(&savetype); + if (savetype != carttype) return; - // only save/load the cartridge header - // - // GBA connectivity on DS mainly involves identifying the title currently - // inserted, reading save data, and issuing commands intercepted here - // (e.g. solar sensor signals). we don't know of any case where GBA ROM is - // read directly from DS software. therefore, it is more practical, both - // from the development and user experience perspectives, to avoid dealing - // with file dependencies, and store a small portion of ROM data that should - // satisfy the needs of all known software that reads from the GBA slot. - // - // note: in case of a state load, only the cartridge header is restored, but - // the rest of the ROM data is only cleared (zero-initialized) if the CRC - // differs. Therefore, loading the GBA cartridge associated with the save state - // in advance will maintain access to the full ROM contents. - file->VarArray(CartROM, 192); - - CartInserted = true; // known, because CartROMSize > 0 - file->Var32(&CartCRC); - file->Var32(&CartID); - - // now do the rest + u32 savechk; + file->Var32(&savechk); + if (savechk != cartchk) return; + } if (Cart) Cart->DoSavestate(file); } @@ -784,9 +773,6 @@ bool LoadROM(const u8* romdata, u32 romlen) printf("GBA solar sensor support detected!\n"); } - CartCRC = CRC32(CartROM, CartROMSize); - printf("GBA ROM CRC32: %08X\n", CartCRC); - CartInserted = true; if (solarsensor) @@ -823,7 +809,6 @@ void LoadAddon(int type) { CartROMSize = 0; CartROM = nullptr; - CartCRC = 0; switch (type) { @@ -849,7 +834,6 @@ void EjectCart() CartInserted = false; CartROM = nullptr; CartROMSize = 0; - CartCRC = 0; CartID = 0; } diff --git a/src/GBACart.h b/src/GBACart.h index eae3fd90..13c6100c 100644 --- a/src/GBACart.h +++ b/src/GBACart.h @@ -31,6 +31,10 @@ class CartCommon public: CartCommon(); virtual ~CartCommon(); + + virtual u32 Type() { return 0x001; } + virtual u32 Checksum() { return 0; } + virtual void Reset(); virtual void DoSavestate(Savestate* file); @@ -53,6 +57,10 @@ class CartGame : public CartCommon public: CartGame(u8* rom, u32 len); virtual ~CartGame() override; + + virtual u32 Type() override { return 0x101; } + virtual u32 Checksum() override; + virtual void Reset() override; virtual void DoSavestate(Savestate* file) override; @@ -119,6 +127,9 @@ class CartGameSolarSensor : public CartGame public: CartGameSolarSensor(u8* rom, u32 len); virtual ~CartGameSolarSensor() override; + + virtual u32 Type() override { return 0x102; } + virtual void Reset() override; virtual void DoSavestate(Savestate* file) override; @@ -142,6 +153,9 @@ class CartRAMExpansion : public CartCommon public: CartRAMExpansion(); ~CartRAMExpansion() override; + + virtual u32 Type() override { return 0x201; } + void Reset() override; void DoSavestate(Savestate* file) override; @@ -164,7 +178,6 @@ enum extern bool CartInserted; extern u8* CartROM; extern u32 CartROMSize; -extern u32 CartCRC; bool Init(); void DeInit(); diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp index 1429565f..940827f9 100644 --- a/src/NDSCart.cpp +++ b/src/NDSCart.cpp @@ -22,11 +22,11 @@ #include "DSi.h" #include "NDSCart.h" #include "ARM.h" +#include "CRC32.h" #include "DSi_AES.h" #include "Platform.h" #include "ROMList.h" #include "melonDLDI.h" -#include "NDSCart_SRAMManager.h" namespace NDSCart @@ -199,6 +199,22 @@ CartCommon::~CartCommon() { } +u32 CartCommon::Checksum() +{ + u32 crc = CRC32(ROM, 0x40); + + crc = CRC32(&ROM[Header.ARM9ROMOffset], Header.ARM9Size, crc); + crc = CRC32(&ROM[Header.ARM7ROMOffset], Header.ARM7Size, crc); + + if (IsDSi) + { + crc = CRC32(&ROM[Header.DSiARM9iROMOffset], Header.DSiARM9iSize, crc); + crc = CRC32(&ROM[Header.DSiARM7iROMOffset], Header.DSiARM7iSize, crc); + } + + return crc; +} + void CartCommon::Reset() { CmdEncMode = 0; @@ -391,11 +407,7 @@ void CartRetail::DoSavestate(Savestate* file) CartCommon::DoSavestate(file); // we reload the SRAM contents. - // it should be the same file (as it should be the same ROM, duh) - // but the contents may change - - //if (!file->Saving && SRAMLength) - // delete[] SRAM; + // it should be the same file, but the contents may change u32 oldlen = SRAMLength; @@ -411,9 +423,6 @@ void CartRetail::DoSavestate(Savestate* file) } if (SRAMLength) { - //if (!file->Saving) - // SRAM = new u8[SRAMLength]; - file->VarArray(SRAM, SRAMLength); } @@ -423,12 +432,8 @@ void CartRetail::DoSavestate(Savestate* file) file->Var32(&SRAMAddr); file->Var8(&SRAMStatus); - // SRAMManager might now have an old buffer (or one from the future or alternate timeline!) - /*if (!file->Saving) - { - SRAMFileDirty = false; - NDSCart_SRAMManager::RequestFlush(); - }*/ + if ((!file->Saving) && SRAM) + Platform::WriteNDSSave(SRAM, SRAMLength, 0, SRAMLength); } void CartRetail::SetupSave(u32 type) @@ -1467,6 +1472,30 @@ void DoSavestate(Savestate* file) // (TODO: system to verify that indeed the right ROM is loaded) // (what to CRC? whole ROM? code binaries? latter would be more convenient for ie. romhaxing) + u32 carttype = 0; + u32 cartchk = 0; + if (Cart) + { + carttype = Cart->Type(); + cartchk = Cart->Checksum(); + } + + if (file->Saving) + { + file->Var32(&carttype); + file->Var32(&cartchk); + } + else + { + u32 savetype; + file->Var32(&savetype); + if (savetype != carttype) return; + + u32 savechk; + file->Var32(&savechk); + if (savechk != cartchk) return; + } + if (Cart) Cart->DoSavestate(file); } diff --git a/src/NDSCart.h b/src/NDSCart.h index 0d83ce1a..b5b5c3f0 100644 --- a/src/NDSCart.h +++ b/src/NDSCart.h @@ -35,6 +35,9 @@ public: CartCommon(u8* rom, u32 len, u32 chipid); virtual ~CartCommon(); + virtual u32 Type() { return 0x001; } + virtual u32 Checksum(); + virtual void Reset(); virtual void SetupDirectBoot(std::string romname); @@ -71,6 +74,8 @@ public: CartRetail(u8* rom, u32 len, u32 chipid); virtual ~CartRetail() override; + virtual u32 Type() override { return 0x101; } + virtual void Reset() override; virtual void DoSavestate(Savestate* file) override; @@ -106,6 +111,8 @@ public: CartRetailNAND(u8* rom, u32 len, u32 chipid); ~CartRetailNAND() override; + virtual u32 Type() override { return 0x102; } + void Reset() override; void DoSavestate(Savestate* file) override; @@ -134,6 +141,8 @@ public: CartRetailIR(u8* rom, u32 len, u32 chipid, u32 irversion); ~CartRetailIR() override; + virtual u32 Type() override { return 0x103; } + void Reset() override; void DoSavestate(Savestate* file) override; @@ -152,6 +161,8 @@ public: CartRetailBT(u8* rom, u32 len, u32 chipid); ~CartRetailBT() override; + virtual u32 Type() override { return 0x104; } + void Reset() override; void DoSavestate(Savestate* file) override; @@ -166,6 +177,8 @@ public: CartHomebrew(u8* rom, u32 len, u32 chipid); ~CartHomebrew() override; + virtual u32 Type() override { return 0x201; } + void Reset() override; void SetupDirectBoot(std::string romname) override; diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index 1221d944..a0085a8a 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -27,74 +27,6 @@ namespace Frontend { -enum -{ - ROMSlot_NDS = 0, - ROMSlot_GBA, - - ROMSlot_MAX -}; - -enum -{ - Load_OK = 0, - - Load_BIOS9Missing, - Load_BIOS9Bad, - - Load_BIOS7Missing, - Load_BIOS7Bad, - - Load_FirmwareMissing, - Load_FirmwareBad, - Load_FirmwareNotBootable, - - Load_DSiBIOS9Missing, - Load_DSiBIOS9Bad, - - Load_DSiBIOS7Missing, - Load_DSiBIOS7Bad, - - Load_DSiNANDMissing, - Load_DSiNANDBad, - - // TODO: more precise errors for ROM loading - Load_ROMLoadError, -}; - -extern std::string ROMPath [ROMSlot_MAX]; -extern std::string SRAMPath[ROMSlot_MAX]; -extern bool SavestateLoaded; - -// Stores type of nds rom i.e. nds/srl/dsi. Should be updated everytime an NDS rom is loaded from an archive -extern std::string NDSROMExtension; - -// initialize the ROM handling utility -void Init_ROM(); - -// deinitialize the ROM handling utility -void DeInit_ROM(); - - - - -// get the filename associated with the given savestate slot (1-8) -std::string GetSavestateName(int slot); - -// determine whether the given savestate slot does contain a savestate -bool SavestateExists(int slot); - -// load the given savestate file -// if successful, emulation will continue from the savestate's point -bool LoadState(std::string filename); - -// save the current emulator state to the given file -bool SaveState(std::string filename); - -// undo the latest savestate load -void UndoStateLoad(); - - // setup the display layout based on the provided display size and parameters // * screenWidth/screenHeight: size of the host display // * screenLayout: how the DS screens are laid out diff --git a/src/frontend/Util_ROM.cpp b/src/frontend/Util_ROM.cpp index 9c428fcf..a36fbe4a 100644 --- a/src/frontend/Util_ROM.cpp +++ b/src/frontend/Util_ROM.cpp @@ -39,41 +39,10 @@ namespace Frontend { -std::string ROMPath [ROMSlot_MAX]; -std::string SRAMPath [ROMSlot_MAX]; -std::string PrevSRAMPath[ROMSlot_MAX]; // for savestate 'undo load' -std::string NDSROMExtension; bool SavestateLoaded; -ARCodeFile* CheatFile; -bool CheatsOn; - - -void Init_ROM() -{ - SavestateLoaded = false; - - ROMPath[ROMSlot_NDS] = ""; - ROMPath[ROMSlot_GBA] = ""; - SRAMPath[ROMSlot_NDS] = ""; - SRAMPath[ROMSlot_GBA] = ""; - PrevSRAMPath[ROMSlot_NDS] = ""; - PrevSRAMPath[ROMSlot_GBA] = ""; - - CheatFile = nullptr; - CheatsOn = false; -} - -void DeInit_ROM() -{ - if (CheatFile) - { - delete CheatFile; - CheatFile = nullptr; - } -} @@ -82,7 +51,7 @@ void DeInit_ROM() // SAVESTATE TODO // * configurable paths. not everyone wants their ROM directory to be polluted, I guess. - +/* std::string GetSavestateName(int slot) { std::string filename; @@ -175,7 +144,7 @@ bool LoadState(std::string filename) slot, loadedPartialGBAROM ? " (GBA ROM header only)" : ""); else sprintf(msg, "State loaded from file%s", loadedPartialGBAROM ? " (GBA ROM header only)" : ""); - OSD::AddMessage(0, msg);*/ + OSD::AddMessage(0, msg);*-/ SavestateLoaded = true; } @@ -224,22 +193,8 @@ void UndoStateLoad() SRAMPath[ROMSlot_NDS] = PrevSRAMPath[ROMSlot_NDS]; // NDS::RelocateSave(SRAMPath[ROMSlot_NDS].c_str(), false); } -} +}*/ -int ImportSRAM(const char* filename) -{ - /*FILE* file = fopen(filename, "rb"); - fseek(file, 0, SEEK_END); - u32 size = ftell(file); - u8* importData = new u8[size]; - rewind(file); - fread(importData, size, 1, file); - fclose(file); - int diff = NDS::ImportSRAM(importData, size); - delete[] importData; - return diff;*/ - return 0; -} } diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp index ba655051..194aef0e 100644 --- a/src/frontend/qt_sdl/ROMManager.cpp +++ b/src/frontend/qt_sdl/ROMManager.cpp @@ -49,6 +49,9 @@ std::string BaseGBAAssetName = ""; SaveManager* NDSSave = nullptr; SaveManager* GBASave = nullptr; +bool SavestateLoaded = false; +std::string PreviousSaveFile = ""; + ARCodeFile* CheatFile = nullptr; bool CheatsOn = false; @@ -67,11 +70,18 @@ int LastSep(std::string path) return -1; } -std::string GetAssetPath(bool gba, std::string configpath, std::string ext) +std::string GetAssetPath(bool gba, std::string configpath, std::string ext, std::string file="") { if (configpath.empty()) configpath = gba ? BaseGBAROMDir : BaseROMDir; + if (file.empty()) + { + file = gba ? BaseGBAAssetName : BaseAssetName; + if (file.empty()) + file = "firmware"; + } + for (;;) { int i = configpath.length() - 1; @@ -84,7 +94,7 @@ std::string GetAssetPath(bool gba, std::string configpath, std::string ext) if (!configpath.empty()) configpath += "/"; - return configpath + (gba ? BaseGBAAssetName : BaseAssetName) + ext; + return configpath + file + ext; } @@ -262,6 +272,97 @@ QString VerifySetup() } +std::string GetSavestateName(int slot) +{ + std::string ext = ".ml"; + ext += ('0'+slot); + return GetAssetPath(false, Config::SavestatePath, ext); +} + +bool SavestateExists(int slot) +{ + std::string ssfile = GetSavestateName(slot); + return Platform::FileExists(ssfile); +} + +bool LoadState(std::string filename) +{ + // backup + Savestate* backup = new Savestate("timewarp.mln", true); + NDS::DoSavestate(backup); + delete backup; + + bool failed = false; + + Savestate* state = new Savestate(filename, false); + if (state->Error) + { + delete state; + + // current state might be crapoed, so restore from sane backup + state = new Savestate("timewarp.mln", false); + failed = true; + } + + NDS::DoSavestate(state); + delete state; + + if (failed) return false; + + if (Config::SavestateRelocSRAM && NDSSave) + { + PreviousSaveFile = NDSSave->GetPath(); + + std::string savefile = filename.substr(LastSep(filename)+1); + savefile = GetAssetPath(false, Config::SaveFilePath, ".sav", savefile); + NDSSave->SetPath(savefile, true); + } + + SavestateLoaded = true; + + return true; +} + +bool SaveState(std::string filename) +{ + Savestate* state = new Savestate(filename, true); + if (state->Error) + { + delete state; + return false; + } + + NDS::DoSavestate(state); + delete state; + + if (Config::SavestateRelocSRAM && NDSSave) + { + std::string savefile = filename.substr(LastSep(filename)+1); + savefile = GetAssetPath(false, Config::SaveFilePath, ".sav", savefile); + NDSSave->SetPath(savefile, false); + } + + return true; +} + +void UndoStateLoad() +{ + if (!SavestateLoaded) return; + + // pray that this works + // what do we do if it doesn't??? + // but it should work. + Savestate* backup = new Savestate("timewarp.mln", false); + NDS::DoSavestate(backup); + delete backup; + + if (NDSSave && (!PreviousSaveFile.empty())) + { + NDSSave->SetPath(PreviousSaveFile, true); + } +} + + void UnloadCheats() { if (CheatFile) diff --git a/src/frontend/qt_sdl/ROMManager.h b/src/frontend/qt_sdl/ROMManager.h index bc100646..8c97965e 100644 --- a/src/frontend/qt_sdl/ROMManager.h +++ b/src/frontend/qt_sdl/ROMManager.h @@ -47,6 +47,12 @@ void EjectGBACart(); bool GBACartInserted(); QString GBACartLabel(); +std::string GetSavestateName(int slot); +bool SavestateExists(int slot); +bool LoadState(std::string filename); +bool SaveState(std::string filename); +void UndoStateLoad(); + void EnableCheats(bool enable); ARCodeFile* GetCheatFile(); diff --git a/src/frontend/qt_sdl/SaveManager.cpp b/src/frontend/qt_sdl/SaveManager.cpp index 05200f88..20ac5431 100644 --- a/src/frontend/qt_sdl/SaveManager.cpp +++ b/src/frontend/qt_sdl/SaveManager.cpp @@ -64,6 +64,28 @@ SaveManager::~SaveManager() if (Buffer) delete[] Buffer; } +std::string SaveManager::GetPath() +{ + return Path; +} + +void SaveManager::SetPath(std::string path, bool reload) +{ + Path = path; + + if (reload) + { + FILE* f = Platform::OpenFile(Path, "rb", true); + if (f) + { + fread(Buffer, 1, Length, f); + fclose(f); + } + } + else + FlushRequested = true; +} + void SaveManager::RequestFlush(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen) { if (Length != savelen) diff --git a/src/frontend/qt_sdl/SaveManager.h b/src/frontend/qt_sdl/SaveManager.h index 5f8d36c5..9ccb0e3e 100644 --- a/src/frontend/qt_sdl/SaveManager.h +++ b/src/frontend/qt_sdl/SaveManager.h @@ -37,6 +37,9 @@ public: SaveManager(std::string path); ~SaveManager(); + std::string GetPath(); + void SetPath(std::string path, bool reload); + void RequestFlush(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen); void CheckFlush(); diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 02cbc336..827a8b14 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -2294,7 +2294,7 @@ void MainWindow::onSaveState() std::string filename; if (slot > 0) { - filename = Frontend::GetSavestateName(slot); + filename = ROMManager::GetSavestateName(slot); } else { @@ -2312,7 +2312,7 @@ void MainWindow::onSaveState() filename = qfilename.toStdString(); } - if (Frontend::SaveState(filename)) + if (ROMManager::SaveState(filename)) { char msg[64]; if (slot > 0) sprintf(msg, "State saved to slot %d", slot); @@ -2338,7 +2338,7 @@ void MainWindow::onLoadState() std::string filename; if (slot > 0) { - filename = Frontend::GetSavestateName(slot); + filename = ROMManager::GetSavestateName(slot); } else { @@ -2367,7 +2367,7 @@ void MainWindow::onLoadState() return; } - if (Frontend::LoadState(filename)) + if (ROMManager::LoadState(filename)) { char msg[64]; if (slot > 0) sprintf(msg, "State loaded from slot %d", slot); @@ -2387,7 +2387,7 @@ void MainWindow::onLoadState() void MainWindow::onUndoStateLoad() { emuThread->emuPause(); - Frontend::UndoStateLoad(); + ROMManager::UndoStateLoad(); emuThread->emuUnpause(); OSD::AddMessage(0, "State load undone"); @@ -2819,7 +2819,7 @@ void MainWindow::onEmuStart() for (int i = 1; i < 9; i++) { actSaveState[i]->setEnabled(true); - actLoadState[i]->setEnabled(Frontend::SavestateExists(i)); + actLoadState[i]->setEnabled(ROMManager::SavestateExists(i)); } actSaveState[0]->setEnabled(true); actLoadState[0]->setEnabled(true); @@ -3002,7 +3002,6 @@ int main(int argc, char** argv) micExtBufferWritePos = 0; micWavBuffer = nullptr; - Frontend::Init_ROM(); ROMManager::EnableCheats(Config::EnableCheats != 0); Frontend::Init_Audio(audioFreq); @@ -3045,8 +3044,6 @@ int main(int argc, char** argv) Input::CloseJoystick(); - Frontend::DeInit_ROM(); - if (audioDevice) SDL_CloseAudioDevice(audioDevice); micClose();