From 7037e20c0f4d44ca6278eadea2922ede8c1cf846 Mon Sep 17 00:00:00 2001 From: Andrea Odetti Date: Sat, 27 Nov 2021 21:27:46 +0000 Subject: [PATCH] libretro: implement save/load state. Signed-off-by: Andrea Odetti --- source/frontends/libretro/CMakeLists.txt | 4 +- source/frontends/libretro/libretro.cpp | 10 +- source/frontends/libretro/serialisation.cpp | 136 ++++++++++++++++++++ source/frontends/libretro/serialisation.h | 14 ++ 4 files changed, 160 insertions(+), 4 deletions(-) create mode 100644 source/frontends/libretro/serialisation.cpp create mode 100644 source/frontends/libretro/serialisation.h diff --git a/source/frontends/libretro/CMakeLists.txt b/source/frontends/libretro/CMakeLists.txt index 458687ca0..f67c63be3 100644 --- a/source/frontends/libretro/CMakeLists.txt +++ b/source/frontends/libretro/CMakeLists.txt @@ -11,9 +11,11 @@ set(SOURCE_FILES retroregistry.cpp retroframe.cpp diskcontrol.cpp + serialisation.cpp ) set(HEADER_FILES + libretro-common/include/libretro.h environment.h rdirectsound.h game.h @@ -25,7 +27,7 @@ set(HEADER_FILES retroregistry.h retroframe.h diskcontrol.h - libretro-common/include/libretro.h + serialisation.h ) add_library(applewin_libretro SHARED diff --git a/source/frontends/libretro/libretro.cpp b/source/frontends/libretro/libretro.cpp index 1707ac26f..55b7b3443 100644 --- a/source/frontends/libretro/libretro.cpp +++ b/source/frontends/libretro/libretro.cpp @@ -19,6 +19,7 @@ #include "frontends/libretro/joypad.h" #include "frontends/libretro/analog.h" #include "frontends/libretro/mouse.h" +#include "frontends/libretro/serialisation.h" namespace { @@ -362,19 +363,22 @@ bool retro_load_game_special(unsigned type, const struct retro_game_info *info, size_t retro_serialize_size(void) { ra2::log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__); - return 0; + const size_t size = ra2::RetroSerialisation::getSize(); + // we cannot guarantee exact file size, so we allow for a small grace buffer + const size_t buffer = 4096; + return size + buffer; } bool retro_serialize(void *data, size_t size) { ra2::log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__); - return false; + return ra2::RetroSerialisation::serialise(static_cast(data), size); } bool retro_unserialize(const void *data, size_t size) { ra2::log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__); - return false; + return ra2::RetroSerialisation::deserialise(static_cast(data), size); } void retro_cheat_reset(void) diff --git a/source/frontends/libretro/serialisation.cpp b/source/frontends/libretro/serialisation.cpp new file mode 100644 index 000000000..c94c4d00f --- /dev/null +++ b/source/frontends/libretro/serialisation.cpp @@ -0,0 +1,136 @@ +#include "StdAfx.h" +#include "SaveState.h" + +#include "frontends/libretro/serialisation.h" +#include "frontends/libretro/environment.h" + +#include +#include + +namespace +{ + class AutoFile + { + public: + AutoFile(); + ~AutoFile(); + + std::string getFilename() const; // only if true + + operator bool() const; + protected: + const char * myFilename; + }; + + AutoFile::AutoFile() + { + // massive race condition, but without changes to AW, little can we do here + myFilename = std::tmpnam(nullptr); + } + + AutoFile::~AutoFile() + { + std::remove(myFilename); + } + + AutoFile::operator bool() const + { + return myFilename; + } + + std::string AutoFile::getFilename() const + { + return myFilename; + } + + void saveToFile(const std::string & filename) // cannot be null! + { + Snapshot_SetFilename(filename); + Snapshot_SaveState(); + } + +} + +namespace ra2 +{ + + size_t RetroSerialisation::getSize() + { + AutoFile autoFile; + if (!autoFile) + { + return 0; + } + + const std::string filename = autoFile.getFilename(); + saveToFile(filename); + std::ifstream ifs(filename, std::ios::binary | std::ios::ate); + + const std::ifstream::pos_type fileSize = ifs.tellg(); + return sizeof(uint32_t) + fileSize; + } + + bool RetroSerialisation::serialise(char * data, size_t size) + { + AutoFile autoFile; + if (!autoFile) + { + return 0; + } + + const std::string filename = autoFile.getFilename(); + saveToFile(filename); + std::ifstream ifs(filename, std::ios::binary | std::ios::ate); + + const std::ifstream::pos_type fileSize = ifs.tellg(); + if (sizeof(uint32_t) + fileSize > size) + { + return false; + } + else + { + uint32_t * sizePtr = reinterpret_cast(data); + *sizePtr = fileSize; + + char * dataPtr = data + sizeof(uint32_t); + + ifs.seekg(0, std::ios::beg); + ifs.read(dataPtr, fileSize); + + return true; + } + } + + bool RetroSerialisation::deserialise(const char * data, size_t size) + { + AutoFile autoFile; + if (!autoFile) + { + return false; + } + + const uint32_t * sizePtr = reinterpret_cast(data); + const size_t fileSize = *sizePtr; + + if (sizeof(uint32_t) + fileSize > size) + { + return false; + } + else + { + const char * dataPtr = data + sizeof(uint32_t); + + const std::string filename = autoFile.getFilename(); + // do not remove the {} scope below! + { + std::ofstream ofs(filename, std::ios::binary); + ofs.write(dataPtr, fileSize); + } + + Snapshot_SetFilename(filename); + Snapshot_LoadState(); + return true; + } + } + +} diff --git a/source/frontends/libretro/serialisation.h b/source/frontends/libretro/serialisation.h new file mode 100644 index 000000000..a79db7ef7 --- /dev/null +++ b/source/frontends/libretro/serialisation.h @@ -0,0 +1,14 @@ +#include + +namespace ra2 +{ + + class RetroSerialisation + { + public: + static size_t getSize(); + static bool serialise(char * data, size_t size); + static bool deserialise(const char * data, size_t size); + }; + +}