diff options
author | Jesse Talavera-Greenberg <jesse@jesse.tg> | 2023-06-12 17:56:09 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-06-12 23:56:09 +0200 |
commit | 391ad8c95e9b942ff39705f2c3cd5359aef633b3 (patch) | |
tree | 7718e0b201ebddf66ec1ca74f0866fe9adbeb32c /src/frontend/qt_sdl | |
parent | ca7fb4f55e8fdad53993ba279b073f97f453c13c (diff) |
Implement in-memory savestates (#1693)
* Refactor Savestate::Var{8,16,32,64}
- They now delegate to VarArray
- They're declared in the class header so they're likely to be inlined
* First crack at refactoring Savestate to work in-memory
- Well, third, but who's counting?
* Implement Savestate::Finish
* Remove the VersionMajor and VersionMinor fields
- Instead, pull their values directly from the savestate buffer
* Mark a new constructor as explicit
* Rename Reset to Rewind
* Fix a linebreak
* Implement Savestate::Rewind
* Add ROMManager::ClearBackupState
* Refactor ROMManager to use the refactored Savestate
* Capitalize "Least"
- It was driving me nuts
* Add a log call
* Increase default Savestate buffer length to 32MB
* Use C-style file I/O instead of C++-style
- Dumping bytes to a file with C++'s standard library is a MONSTROUS PAIN IN THE ASS
* Quote the savestate's file path for clarity
* Write the savestate's length into the header
* Add some extra logging calls
* Fix section-loading
* Remove the deprecated Savestate constructor
* Convert a char* to a u32 with memcpy, not a cast
* Fix section-handling in loads
* Include <cstring> in Savestate.h
- This was causing a build error on Linux
Diffstat (limited to 'src/frontend/qt_sdl')
-rw-r--r-- | src/frontend/qt_sdl/ROMManager.cpp | 121 | ||||
-rw-r--r-- | src/frontend/qt_sdl/ROMManager.h | 1 |
2 files changed, 93 insertions, 29 deletions
diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp index 95337e1..80f4652 100644 --- a/src/frontend/qt_sdl/ROMManager.cpp +++ b/src/frontend/qt_sdl/ROMManager.cpp @@ -21,6 +21,7 @@ #include <string> #include <utility> +#include <fstream> #include <zstd.h> #ifdef ARCHIVE_SUPPORT_ENABLED @@ -52,6 +53,7 @@ std::string BaseGBAAssetName = ""; SaveManager* NDSSave = nullptr; SaveManager* GBASave = nullptr; +std::unique_ptr<Savestate> BackupState = nullptr; bool SavestateLoaded = false; std::string PreviousSaveFile = ""; @@ -304,35 +306,62 @@ bool SavestateExists(int slot) bool LoadState(const std::string& filename) { - // backup - Savestate* backup = new Savestate("timewarp.mln", true); - NDS::DoSavestate(backup); - delete backup; + FILE* file = fopen(filename.c_str(), "rb"); + if (file == nullptr) + { // If we couldn't open the state file... + Platform::Log(Platform::LogLevel::Error, "Failed to open state file \"%s\"\n", filename.c_str()); + return false; + } - bool failed = false; + std::unique_ptr<Savestate> backup = std::make_unique<Savestate>(Savestate::DEFAULT_SIZE); + if (backup->Error) + { // If we couldn't allocate memory for the backup... + Platform::Log(Platform::LogLevel::Error, "Failed to allocate memory for state backup\n"); + fclose(file); + return false; + } + + if (!NDS::DoSavestate(backup.get()) || backup->Error) + { // Back up the emulator's state. If that failed... + Platform::Log(Platform::LogLevel::Error, "Failed to back up state, aborting load (from \"%s\")\n", filename.c_str()); + fclose(file); + return false; + } + // We'll store the backup once we're sure that the state was loaded. + // Now that we know the file and backup are both good, let's load the new state. - Savestate* state = new Savestate(filename, false); - if (state->Error) + // Get the size of the file that we opened + if (fseek(file, 0, SEEK_END) != 0) { - delete state; + Platform::Log(Platform::LogLevel::Error, "Failed to seek to end of state file \"%s\"\n", filename.c_str()); + fclose(file); + return false; + } + size_t size = ftell(file); + rewind(file); // reset the filebuf's position - // current state might be crapoed, so restore from sane backup - state = new Savestate("timewarp.mln", false); - failed = true; + // Allocate exactly as much memory as we need for the savestate + std::vector<u8> buffer(size); + if (fread(buffer.data(), size, 1, file) == 0) + { // Read the state file into the buffer. If that failed... + Platform::Log(Platform::LogLevel::Error, "Failed to read %u-byte state file \"%s\"\n", size, filename.c_str()); + fclose(file); + return false; } + fclose(file); // done with the file now - bool res = NDS::DoSavestate(state); - delete state; + // Get ready to load the state from the buffer into the emulator + std::unique_ptr<Savestate> state = std::make_unique<Savestate>(buffer.data(), size, false); - if (!res) - { - failed = true; - state = new Savestate("timewarp.mln", false); - NDS::DoSavestate(state); - delete state; + if (!NDS::DoSavestate(state.get()) || state->Error) + { // If we couldn't load the savestate from the buffer... + Platform::Log(Platform::LogLevel::Error, "Failed to load state file \"%s\" into emulator\n", filename.c_str()); + return false; } - if (failed) return false; + // The backup was made and the state was loaded, so we can store the backup now. + BackupState = std::move(backup); // This will clean up any existing backup + assert(backup == nullptr); if (Config::SavestateRelocSRAM && NDSSave) { @@ -351,15 +380,41 @@ bool LoadState(const std::string& filename) bool SaveState(const std::string& filename) { - Savestate* state = new Savestate(filename, true); - if (state->Error) + FILE* file = fopen(filename.c_str(), "wb"); + + if (file == nullptr) + { // If the file couldn't be opened... + return false; + } + + Savestate state; + if (state.Error) + { // If there was an error creating the state (and allocating its memory)... + fclose(file); + return false; + } + + // Write the savestate to the in-memory buffer + NDS::DoSavestate(&state); + + if (state.Error) { - delete state; + fclose(file); + return false; + } + + if (fwrite(state.Buffer(), state.Length(), 1, file) == 0) + { // Write the Savestate buffer to the file. If that fails... + Platform::Log(Platform::Error, + "Failed to write %d-byte savestate to %s\n", + state.Length(), + filename.c_str() + ); + fclose(file); return false; } - NDS::DoSavestate(state); - delete state; + fclose(file); if (Config::SavestateRelocSRAM && NDSSave) { @@ -374,14 +429,14 @@ bool SaveState(const std::string& filename) void UndoStateLoad() { - if (!SavestateLoaded) return; + if (!SavestateLoaded || !BackupState) return; + // Rewind the backup state and put it in load mode + BackupState->Rewind(false); // 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; + NDS::DoSavestate(BackupState.get()); if (NDSSave && (!PreviousSaveFile.empty())) { @@ -514,6 +569,14 @@ u32 DecompressROM(const u8* inContent, const u32 inSize, u8** outContent) return realSize; } +void ClearBackupState() +{ + if (BackupState != nullptr) + { + BackupState = nullptr; + } +} + bool LoadROM(QStringList filepath, bool reset) { if (filepath.empty()) return false; diff --git a/src/frontend/qt_sdl/ROMManager.h b/src/frontend/qt_sdl/ROMManager.h index efaed36..1ec0fe5 100644 --- a/src/frontend/qt_sdl/ROMManager.h +++ b/src/frontend/qt_sdl/ROMManager.h @@ -35,6 +35,7 @@ extern SaveManager* GBASave; QString VerifySetup(); void Reset(); bool LoadBIOS(); +void ClearBackupState(); bool LoadROM(QStringList filepath, bool reset); void EjectCart(); |