diff --git a/source/frontends/libretro/buffer.h b/source/frontends/libretro/buffer.h new file mode 100644 index 000000000..f464f8cc0 --- /dev/null +++ b/source/frontends/libretro/buffer.h @@ -0,0 +1,45 @@ +#include +#include + +namespace ra2 +{ + + template // so it works with both char * and const char * + class Buffer + { + public: + Buffer(C * const begin, size_t const size) : myPtr(begin), myEnd(begin + size) + { + + } + + template + T & get() + { + C * c = myPtr; + advance(sizeof(T)); + return *reinterpret_cast(c); + } + + void get(size_t const size, C * & begin, C * & end) + { + begin = myPtr; + advance(size); + end = myPtr; + } + + private: + C * myPtr; + C * const myEnd; + + void advance(size_t const size) + { + if (myPtr + size > myEnd) + { + throw std::runtime_error("Buffer: out of bounds"); + } + myPtr += size; + } + }; + +} diff --git a/source/frontends/libretro/diskcontrol.cpp b/source/frontends/libretro/diskcontrol.cpp index 58df48396..a987d2658 100644 --- a/source/frontends/libretro/diskcontrol.cpp +++ b/source/frontends/libretro/diskcontrol.cpp @@ -242,6 +242,39 @@ namespace ra2 } } + void DiskControl::serialise(Buffer & buffer) const + { + buffer.get() = myEjected; + buffer.get() = myIndex; + buffer.get() = myImages.size(); + + for (std::string const & image : myImages) + { + size_t const size = image.size(); + buffer.get() = size; + char * begin, * end; + buffer.get(size, begin, end); + memcpy(begin, image.data(), end - begin); + } + } + + void DiskControl::deserialise(Buffer & buffer) + { + myEjected = buffer.get(); + myIndex = buffer.get(); + size_t const numberOfImages = buffer.get(); + myImages.clear(); + myImages.resize(numberOfImages); + + for (size_t i = 0; i < numberOfImages; ++i) + { + size_t const size = buffer.get(); + char const * begin, * end; + buffer.get(size, begin, end); + myImages[i].assign(begin, end); + } + } + unsigned DiskControl::ourInitialIndex = 0; std::string DiskControl::ourInitialPath; void DiskControl::setInitialPath(unsigned index, const char *path) diff --git a/source/frontends/libretro/diskcontrol.h b/source/frontends/libretro/diskcontrol.h index 87b53605d..6e95459f1 100644 --- a/source/frontends/libretro/diskcontrol.h +++ b/source/frontends/libretro/diskcontrol.h @@ -1,3 +1,4 @@ +#include "frontends/libretro/buffer.h" #include #include #include @@ -31,6 +32,9 @@ namespace ra2 static void setInitialPath(unsigned index, const char *path); + void serialise(Buffer & buffer) const; + void deserialise(Buffer & buffer); + private: std::vector myImages; diff --git a/source/frontends/libretro/libretro.cpp b/source/frontends/libretro/libretro.cpp index 9f44a6498..365341c4d 100644 --- a/source/frontends/libretro/libretro.cpp +++ b/source/frontends/libretro/libretro.cpp @@ -367,23 +367,44 @@ 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__); - 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; + try + { + const size_t size = ra2::RetroSerialisation::getSize(); + return size; + } + catch(const std::exception& e) + { + ra2::log_cb(RETRO_LOG_INFO, "RA2: %s - %s\n", __FUNCTION__, e.what()); + return 0; + } } bool retro_serialize(void *data, size_t size) { - ra2::log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__); - return ra2::RetroSerialisation::serialise(data, size); + try + { + ra2::RetroSerialisation::serialise(data, size, ourGame->getDiskControl()); + return true; + } + catch(const std::exception& e) + { + ra2::log_cb(RETRO_LOG_INFO, "RA2: %s - %s\n", __FUNCTION__, e.what()); + return false; + } } bool retro_unserialize(const void *data, size_t size) { - ra2::log_cb(RETRO_LOG_INFO, "RA2: %s\n", __FUNCTION__); - return ra2::RetroSerialisation::deserialise(data, size); + try + { + ra2::RetroSerialisation::deserialise(data, size, ourGame->getDiskControl()); + return true; + } + catch(const std::exception& e) + { + ra2::log_cb(RETRO_LOG_INFO, "RA2: %s - %s\n", __FUNCTION__, e.what()); + return false; + } } void retro_cheat_reset(void) diff --git a/source/frontends/libretro/serialisation.cpp b/source/frontends/libretro/serialisation.cpp index eaeed53e0..0ceadb7da 100644 --- a/source/frontends/libretro/serialisation.cpp +++ b/source/frontends/libretro/serialisation.cpp @@ -3,6 +3,7 @@ #include "frontends/libretro/serialisation.h" #include "frontends/libretro/environment.h" +#include "frontends/libretro/diskcontrol.h" #include #include @@ -15,30 +16,29 @@ namespace AutoFile(); ~AutoFile(); - std::string getFilename() const; // only if true + const std::string & getFilename() const; // only if true - operator bool() const; protected: - const char * myFilename; + std::string myFilename; }; AutoFile::AutoFile() { // massive race condition, but without changes to AW, little can we do here - myFilename = std::tmpnam(nullptr); + const char * tmp = std::tmpnam(nullptr); + if (!tmp) + { + throw std::runtime_error("Cannot create temporary file"); + } + myFilename = tmp; } AutoFile::~AutoFile() { - std::remove(myFilename); + std::remove(myFilename.c_str()); } - AutoFile::operator bool() const - { - return myFilename; - } - - std::string AutoFile::getFilename() const + const std::string & AutoFile::getFilename() const { return myFilename; } @@ -49,12 +49,6 @@ namespace Snapshot_SaveState(); } - struct SerialisationFormat_t - { - uint32_t size; - char data[]; // zero-length array, containing the AW's yaml format - }; - } namespace ra2 @@ -63,75 +57,58 @@ namespace ra2 size_t RetroSerialisation::getSize() { AutoFile autoFile; - if (!autoFile) - { - return 0; - } - - const std::string filename = autoFile.getFilename(); + std::string const & 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; + const size_t fileSize = ifs.tellg(); + // we add a buffer to include a few things + // DiscControl images + // various sizes + // small variations in AW yaml format + const size_t buffer = 4096; + return fileSize + buffer; } - bool RetroSerialisation::serialise(void * data, size_t size) + void RetroSerialisation::serialise(void * data, size_t size, const DiskControl & diskControl) { - AutoFile autoFile; - if (!autoFile) - { - return 0; - } + Buffer buffer(reinterpret_cast(data), size); + diskControl.serialise(buffer); - const std::string filename = autoFile.getFilename(); + AutoFile autoFile; + std::string const & 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 - { - SerialisationFormat_t * serialised = reinterpret_cast(data); - serialised->size = fileSize; + size_t const fileSize = ifs.tellg(); + buffer.get() = fileSize; - ifs.seekg(0, std::ios::beg); - ifs.read(serialised->data, serialised->size); + char * begin, * end; + buffer.get(fileSize, begin, end); - return true; - } + ifs.seekg(0, std::ios::beg); + ifs.read(begin, end - begin); } - bool RetroSerialisation::deserialise(const void * data, size_t size) + void RetroSerialisation::deserialise(const void * data, size_t size, DiskControl & diskControl) { - AutoFile autoFile; - if (!autoFile) - { - return false; - } + Buffer buffer(reinterpret_cast(data), size); + diskControl.deserialise(buffer); - const SerialisationFormat_t * serialised = reinterpret_cast(data); + const size_t fileSize = buffer.get(); - if (sizeof(uint32_t) + serialised->size > size) - { - return false; - } - else + AutoFile autoFile; + std::string const & filename = autoFile.getFilename(); + // do not remove the {} scope below! it ensures the file is flushed { - const std::string filename = autoFile.getFilename(); - // do not remove the {} scope below! - { - std::ofstream ofs(filename, std::ios::binary); - ofs.write(serialised->data, serialised->size); - } - - Snapshot_SetFilename(filename); - Snapshot_LoadState(); - return true; + char const * begin, * end; + buffer.get(fileSize, begin, end); + std::ofstream ofs(filename, std::ios::binary); + ofs.write(begin, end - begin); } + + Snapshot_SetFilename(filename); + Snapshot_LoadState(); } } diff --git a/source/frontends/libretro/serialisation.h b/source/frontends/libretro/serialisation.h index 317644661..c222b8523 100644 --- a/source/frontends/libretro/serialisation.h +++ b/source/frontends/libretro/serialisation.h @@ -3,12 +3,14 @@ namespace ra2 { + class DiskControl; + class RetroSerialisation { public: static size_t getSize(); - static bool serialise(void * data, size_t size); - static bool deserialise(const void * data, size_t size); + static void serialise(void * data, size_t size, const DiskControl & diskControl); + static void deserialise(const void * data, size_t size, DiskControl & diskControl); }; }