aboutsummaryrefslogtreecommitdiff
path: root/src/frontend/qt_sdl
diff options
context:
space:
mode:
authorJesse Talavera-Greenberg <jesse@jesse.tg>2023-06-12 17:56:09 -0400
committerGitHub <noreply@github.com>2023-06-12 23:56:09 +0200
commit391ad8c95e9b942ff39705f2c3cd5359aef633b3 (patch)
tree7718e0b201ebddf66ec1ca74f0866fe9adbeb32c /src/frontend/qt_sdl
parentca7fb4f55e8fdad53993ba279b073f97f453c13c (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.cpp121
-rw-r--r--src/frontend/qt_sdl/ROMManager.h1
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();