mirror of
https://github.com/melonDS-emu/melonDS.git
synced 2025-07-21 05:09:46 -06:00
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
This commit is contained in:

committed by
GitHub

parent
ca7fb4f55e
commit
391ad8c95e
@ -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;
|
||||
|
||||
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;
|
||||
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 res = NDS::DoSavestate(state);
|
||||
delete state;
|
||||
|
||||
if (!res)
|
||||
{
|
||||
failed = true;
|
||||
state = new Savestate("timewarp.mln", false);
|
||||
NDS::DoSavestate(state);
|
||||
delete state;
|
||||
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 (failed) 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.
|
||||
|
||||
// Get the size of the file that we opened
|
||||
if (fseek(file, 0, SEEK_END) != 0)
|
||||
{
|
||||
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
|
||||
|
||||
// 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
|
||||
|
||||
// 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 (!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;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
delete state;
|
||||
FILE* file = fopen(filename.c_str(), "wb");
|
||||
|
||||
if (file == nullptr)
|
||||
{ // If the file couldn't be opened...
|
||||
return false;
|
||||
}
|
||||
|
||||
NDS::DoSavestate(state);
|
||||
delete state;
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
Reference in New Issue
Block a user