Skip to content

Commit

Permalink
libretro: include DiscControl in save state.
Browse files Browse the repository at this point in the history
Signed-off-by: Andrea Odetti <mariofutire@gmail.com>
  • Loading branch information
audetto committed Nov 30, 2021
1 parent a6eea04 commit 0ae58c5
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 78 deletions.
45 changes: 45 additions & 0 deletions source/frontends/libretro/buffer.h
@@ -0,0 +1,45 @@
#include <cstddef>
#include <stdexcept>

namespace ra2
{

template<typename C> // 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 <typename T>
T & get()
{
C * c = myPtr;
advance(sizeof(T));
return *reinterpret_cast<T *>(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;
}
};

}
33 changes: 33 additions & 0 deletions source/frontends/libretro/diskcontrol.cpp
Expand Up @@ -242,6 +242,39 @@ namespace ra2
}
}

void DiskControl::serialise(Buffer<char> & buffer) const
{
buffer.get<bool>() = myEjected;
buffer.get<size_t>() = myIndex;
buffer.get<size_t>() = myImages.size();

for (std::string const & image : myImages)
{
size_t const size = image.size();
buffer.get<size_t>() = size;
char * begin, * end;
buffer.get(size, begin, end);
memcpy(begin, image.data(), end - begin);
}
}

void DiskControl::deserialise(Buffer<char const> & buffer)
{
myEjected = buffer.get<bool const>();
myIndex = buffer.get<size_t const>();
size_t const numberOfImages = buffer.get<size_t const>();
myImages.clear();
myImages.resize(numberOfImages);

for (size_t i = 0; i < numberOfImages; ++i)
{
size_t const size = buffer.get<size_t const>();
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)
Expand Down
4 changes: 4 additions & 0 deletions source/frontends/libretro/diskcontrol.h
@@ -1,3 +1,4 @@
#include "frontends/libretro/buffer.h"
#include <vector>
#include <string>
#include <filesystem>
Expand Down Expand Up @@ -31,6 +32,9 @@ namespace ra2

static void setInitialPath(unsigned index, const char *path);

void serialise(Buffer<char> & buffer) const;
void deserialise(Buffer<char const> & buffer);

private:
std::vector<std::filesystem::path> myImages;

Expand Down
39 changes: 30 additions & 9 deletions source/frontends/libretro/libretro.cpp
Expand Up @@ -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)
Expand Down
111 changes: 44 additions & 67 deletions source/frontends/libretro/serialisation.cpp
Expand Up @@ -3,6 +3,7 @@

#include "frontends/libretro/serialisation.h"
#include "frontends/libretro/environment.h"
#include "frontends/libretro/diskcontrol.h"

#include <cstdio>
#include <fstream>
Expand All @@ -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;
}
Expand All @@ -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
Expand All @@ -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<char *>(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<SerialisationFormat_t *>(data);
serialised->size = fileSize;
size_t const fileSize = ifs.tellg();
buffer.get<size_t>() = 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<const char *>(data), size);
diskControl.deserialise(buffer);

const SerialisationFormat_t * serialised = reinterpret_cast<const SerialisationFormat_t *>(data);
const size_t fileSize = buffer.get<size_t const>();

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();
}

}
6 changes: 4 additions & 2 deletions source/frontends/libretro/serialisation.h
Expand Up @@ -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);
};

}

0 comments on commit 0ae58c5

Please sign in to comment.