mirror of
https://github.com/mkxp-z/mkxp-z.git
synced 2025-08-26 00:33:45 +02:00
Start implementing save state serialization in libretro builds
This commit is contained in:
parent
b8fb59e558
commit
80b3833fff
4 changed files with 274 additions and 11 deletions
|
@ -93,6 +93,18 @@ void binding_base::rtypeddata_dcompact(wasm_ptr_t data, wasm_ptr_t ptr) {
|
|||
w2c_ruby_mkxp_sandbox_rtypeddata_dcompact(&instance(), data, ptr);
|
||||
}
|
||||
|
||||
wasm_size_t binding_base::memory_capacity() const noexcept {
|
||||
return instance().w2c_memory.capacity;
|
||||
}
|
||||
|
||||
wasm_size_t binding_base::memory_size() const noexcept {
|
||||
return instance().w2c_memory.size;
|
||||
}
|
||||
|
||||
void binding_base::copy_memory_to(void *ptr) const noexcept {
|
||||
std::memcpy(ptr, instance().w2c_memory.data, memory_size());
|
||||
}
|
||||
|
||||
void *mkxp_sandbox::sandbox_ptr(struct w2c_ruby &instance, wasm_ptr_t address) noexcept {
|
||||
if (address >= instance.w2c_memory.size) {
|
||||
std::abort();
|
||||
|
|
|
@ -209,18 +209,32 @@ namespace mkxp_sandbox {
|
|||
typedef std::tuple<wasm_ptr_t, wasm_ptr_t, wasm_ptr_t> key_t;
|
||||
|
||||
struct stack_frame {
|
||||
void *coroutine;
|
||||
void (*destructor)(void *coroutine);
|
||||
wasm_ptr_t stack_ptr;
|
||||
friend struct binding_base;
|
||||
stack_frame(void *coroutine, void (*destructor)(void *coroutine), wasm_ptr_t stack_ptr);
|
||||
stack_frame(const struct stack_frame &frame) = delete;
|
||||
stack_frame(struct stack_frame &&frame) noexcept;
|
||||
struct stack_frame &operator=(const struct stack_frame &frame) = delete;
|
||||
struct stack_frame &operator=(struct stack_frame &&frame) noexcept;
|
||||
~stack_frame();
|
||||
inline operator int32_t() const noexcept {
|
||||
return (int32_t)(boost::asio::detail::coroutine_ref)(boost::asio::coroutine *)coroutine;
|
||||
}
|
||||
inline void operator=(int32_t value) noexcept {
|
||||
(boost::asio::detail::coroutine_ref)(boost::asio::coroutine *)coroutine = (int)value;
|
||||
}
|
||||
private:
|
||||
void *coroutine;
|
||||
void (*destructor)(void *coroutine);
|
||||
wasm_ptr_t stack_ptr;
|
||||
};
|
||||
|
||||
struct fiber {
|
||||
friend struct binding_base;
|
||||
fiber(key_t key) : key(key), stack_index(0) {};
|
||||
inline const std::vector<struct stack_frame> &get_stack() const noexcept {
|
||||
return stack;
|
||||
}
|
||||
private:
|
||||
key_t key;
|
||||
wasm_size_t stack_index;
|
||||
std::vector<struct stack_frame> stack;
|
||||
|
@ -263,6 +277,11 @@ namespace mkxp_sandbox {
|
|||
wasm_size_t rtypeddata_dsize(wasm_ptr_t data, wasm_ptr_t ptr);
|
||||
void rtypeddata_dcompact(wasm_ptr_t data, wasm_ptr_t ptr);
|
||||
|
||||
// Serialization functions
|
||||
wasm_size_t memory_capacity() const noexcept;
|
||||
wasm_size_t memory_size() const noexcept;
|
||||
void copy_memory_to(void *ptr) const noexcept;
|
||||
|
||||
// Gets a pointer to the given address in sandbox memory.
|
||||
void *ptr(wasm_ptr_t address) const noexcept;
|
||||
|
||||
|
@ -326,10 +345,12 @@ namespace mkxp_sandbox {
|
|||
bind.ref<wasm_ptr_t>(bind.instance().w2c_mkxp_sandbox_fiber_arg0),
|
||||
bind.ref<wasm_ptr_t>(bind.instance().w2c_mkxp_sandbox_fiber_arg1),
|
||||
};
|
||||
if (bind.fibers.count(key) == 0) {
|
||||
bind.fibers[key] = (struct fiber){.key = key, .stack_index = 0};
|
||||
const auto it = bind.fibers.find(key);
|
||||
if (it != bind.fibers.end()) {
|
||||
return it->second;
|
||||
} else {
|
||||
return bind.fibers.emplace(key, key).first->second;
|
||||
}
|
||||
return bind.fibers[key];
|
||||
}
|
||||
|
||||
template <typename U> static typename std::enable_if<std::is_constructible<U, struct binding_base &>::value, U *>::type construct_frame(struct binding_base &bind) {
|
||||
|
@ -441,9 +462,13 @@ namespace mkxp_sandbox {
|
|||
return *this;
|
||||
}
|
||||
|
||||
wasm_ptr_t stack_pointer() const noexcept {
|
||||
inline wasm_ptr_t stack_pointer() const noexcept {
|
||||
return stack_ptr;
|
||||
}
|
||||
|
||||
inline const std::unordered_map<key_t, struct fiber, boost::hash<key_t>> &get_fibers() const noexcept {
|
||||
return fibers;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ systemname = "RPG Maker XP/VX/VX Ace Game Engine"
|
|||
# Libretro Features
|
||||
database = "RPG Maker"
|
||||
supports_no_game = "false"
|
||||
savestate = "false"
|
||||
savestate_features = "null"
|
||||
savestate = "true"
|
||||
savestate_features = "deterministic"
|
||||
|
||||
description = ""
|
||||
|
|
230
src/core.cpp
230
src/core.cpp
|
@ -24,6 +24,7 @@
|
|||
#include <cstdarg>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
#include <alc.h>
|
||||
|
@ -79,6 +80,7 @@ static const struct retro_core_option_v2_definition core_option_definitions[] =
|
|||
" By default, mkxp will try to guess the required version"
|
||||
" based on the game files."
|
||||
" If this fails, the version defaults to 1."
|
||||
" Changes will take effect after the core is reset."
|
||||
),
|
||||
.info_categorized = nullptr,
|
||||
.category_key = "runtime",
|
||||
|
@ -92,6 +94,149 @@ static const struct retro_core_option_v2_definition core_option_definitions[] =
|
|||
},
|
||||
.default_value = "inherit",
|
||||
},
|
||||
{
|
||||
.key = "mkxp-z_saveStateSize",
|
||||
.desc = "Save State Size",
|
||||
.desc_categorized = nullptr,
|
||||
.info = (
|
||||
"Maximum size of each save state, in mebibytes."
|
||||
" If the game uses more than this much memory, save state creation will fail."
|
||||
" Changes to this setting will not take effect until the core is unloaded."
|
||||
),
|
||||
.info_categorized = nullptr,
|
||||
.category_key = "runtime",
|
||||
.values = {
|
||||
{"64", "64"},
|
||||
{"66", "66"},
|
||||
{"68", "68"},
|
||||
{"70", "70"},
|
||||
{"72", "72"},
|
||||
{"74", "74"},
|
||||
{"76", "76"},
|
||||
{"78", "78"},
|
||||
{"80", "80"},
|
||||
{"82", "82"},
|
||||
{"84", "84"},
|
||||
{"86", "86"},
|
||||
{"88", "88"},
|
||||
{"90", "90"},
|
||||
{"92", "92"},
|
||||
{"94", "94"},
|
||||
{"96", "96"},
|
||||
{"98", "98"},
|
||||
{"100", "100"},
|
||||
{"102", "102"},
|
||||
{"104", "104"},
|
||||
{"106", "106"},
|
||||
{"108", "108"},
|
||||
{"110", "110"},
|
||||
{"112", "112"},
|
||||
{"114", "114"},
|
||||
{"116", "116"},
|
||||
{"118", "118"},
|
||||
{"120", "120"},
|
||||
{"122", "122"},
|
||||
{"124", "124"},
|
||||
{"126", "126"},
|
||||
{"128", "128"},
|
||||
{"132", "132"},
|
||||
{"136", "136"},
|
||||
{"140", "140"},
|
||||
{"144", "144"},
|
||||
{"148", "148"},
|
||||
{"152", "152"},
|
||||
{"156", "156"},
|
||||
{"160", "160"},
|
||||
{"164", "164"},
|
||||
{"168", "168"},
|
||||
{"172", "172"},
|
||||
{"176", "176"},
|
||||
{"180", "180"},
|
||||
{"184", "184"},
|
||||
{"188", "188"},
|
||||
{"192", "192"},
|
||||
{"196", "196"},
|
||||
{"200", "200"},
|
||||
{"204", "204"},
|
||||
{"208", "208"},
|
||||
{"212", "212"},
|
||||
{"216", "216"},
|
||||
{"220", "220"},
|
||||
{"224", "224"},
|
||||
{"228", "228"},
|
||||
{"232", "232"},
|
||||
{"236", "236"},
|
||||
{"240", "240"},
|
||||
{"244", "244"},
|
||||
{"248", "248"},
|
||||
{"252", "252"},
|
||||
{"256", "256"},
|
||||
{"264", "264"},
|
||||
{"272", "272"},
|
||||
{"280", "280"},
|
||||
{"288", "288"},
|
||||
{"296", "296"},
|
||||
{"304", "304"},
|
||||
{"312", "312"},
|
||||
{"320", "320"},
|
||||
{"328", "328"},
|
||||
{"336", "336"},
|
||||
{"344", "344"},
|
||||
{"352", "352"},
|
||||
{"360", "360"},
|
||||
{"368", "368"},
|
||||
{"376", "376"},
|
||||
{"384", "384"},
|
||||
{"392", "392"},
|
||||
{"400", "400"},
|
||||
{"408", "408"},
|
||||
{"416", "416"},
|
||||
{"424", "424"},
|
||||
{"432", "432"},
|
||||
{"440", "440"},
|
||||
{"448", "448"},
|
||||
{"456", "456"},
|
||||
{"464", "464"},
|
||||
{"472", "472"},
|
||||
{"480", "480"},
|
||||
{"488", "488"},
|
||||
{"496", "496"},
|
||||
{"504", "504"},
|
||||
{"512", "512"},
|
||||
{"528", "528"},
|
||||
{"544", "544"},
|
||||
{"560", "560"},
|
||||
{"576", "576"},
|
||||
{"592", "592"},
|
||||
{"608", "608"},
|
||||
{"624", "624"},
|
||||
{"640", "640"},
|
||||
{"656", "656"},
|
||||
{"672", "672"},
|
||||
{"688", "688"},
|
||||
{"704", "704"},
|
||||
{"720", "720"},
|
||||
{"736", "736"},
|
||||
{"752", "752"},
|
||||
{"768", "768"},
|
||||
{"784", "784"},
|
||||
{"800", "800"},
|
||||
{"816", "816"},
|
||||
{"832", "832"},
|
||||
{"848", "848"},
|
||||
{"864", "864"},
|
||||
{"880", "880"},
|
||||
{"896", "896"},
|
||||
{"912", "912"},
|
||||
{"928", "928"},
|
||||
{"944", "944"},
|
||||
{"960", "960"},
|
||||
{"976", "976"},
|
||||
{"992", "992"},
|
||||
{nullptr, nullptr},
|
||||
},
|
||||
.default_value = "100",
|
||||
},
|
||||
{
|
||||
.key = "mkxp-z_frameSkip",
|
||||
.desc = "Frame Skip",
|
||||
|
@ -452,6 +597,7 @@ static LPALCLOOPBACKOPENDEVICESOFT alcLoopbackOpenDeviceSOFT = nullptr;
|
|||
static int16_t *sound_buf = nullptr;
|
||||
static bool retro_framebuffer_supported;
|
||||
static bool dupe_supported;
|
||||
static size_t save_state_size = 0;
|
||||
static retro_system_av_info av_info;
|
||||
static struct retro_audio_callback audio_callback;
|
||||
static struct retro_frame_time_callback frame_time_callback = {
|
||||
|
@ -1200,6 +1346,12 @@ extern "C" RETRO_API void retro_set_input_state(retro_input_state_t cb) {
|
|||
extern "C" RETRO_API void retro_init() {
|
||||
initialized = true;
|
||||
frame_buf = (uint32_t *)std::calloc(640 * 480, sizeof *frame_buf);
|
||||
|
||||
save_state_size = (size_t)std::strtoul(get_core_option("mkxp-z_saveStateSize"), nullptr, 10) * (size_t)0x100000;
|
||||
if (save_state_size == 0) {
|
||||
save_state_size = (size_t)(100 * 0x100000);
|
||||
}
|
||||
save_state_size = std::max(save_state_size, (size_t)(64 * 0x100000));
|
||||
}
|
||||
|
||||
extern "C" RETRO_API void retro_deinit() {
|
||||
|
@ -1399,15 +1551,89 @@ extern "C" RETRO_API void retro_run() {
|
|||
}
|
||||
|
||||
extern "C" RETRO_API size_t retro_serialize_size() {
|
||||
return 0;
|
||||
return save_state_size;
|
||||
}
|
||||
|
||||
#define RESERVE(bytes) do { \
|
||||
if (len < (bytes)) { \
|
||||
return false; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define ADVANCE(bytes) do { \
|
||||
data = (uint8_t *)data + (bytes); \
|
||||
len -= (bytes); \
|
||||
} while (0)
|
||||
|
||||
extern "C" RETRO_API bool retro_serialize(void *data, size_t len) {
|
||||
// Write 4-byte magic number: "MKXP" for big-endian platforms, "mkxp" for little-endian platforms
|
||||
RESERVE(4);
|
||||
#ifdef MKXPZ_BIG_ENDIAN
|
||||
std::memcpy(data, "MKXP", 4);
|
||||
#else
|
||||
std::memcpy(data, "mkxp", 4);
|
||||
#endif // MKXPZ_BIG_ENDIAN
|
||||
ADVANCE(4);
|
||||
|
||||
// Write 4-byte version: 1
|
||||
RESERVE(4);
|
||||
*(uint32_t *)data = 1;
|
||||
ADVANCE(4);
|
||||
|
||||
// Write the capacity of the VM memory
|
||||
RESERVE(sizeof(wasm_size_t));
|
||||
*(wasm_size_t *)data = sb()->memory_capacity();
|
||||
ADVANCE(sizeof(wasm_size_t));
|
||||
|
||||
{
|
||||
// Write the size of the VM memory
|
||||
RESERVE(sizeof(wasm_size_t));
|
||||
wasm_size_t memory_size = sb()->memory_size();
|
||||
*(wasm_size_t *)data = memory_size;
|
||||
ADVANCE(sizeof(wasm_size_t));
|
||||
|
||||
// Write the VM memory itself
|
||||
RESERVE(memory_size);
|
||||
sb()->copy_memory_to(data);
|
||||
ADVANCE(memory_size);
|
||||
}
|
||||
|
||||
// Write the number of sandbox fibers
|
||||
RESERVE(sizeof(wasm_size_t));
|
||||
*(wasm_size_t *)data = sb()->get_fibers().size();
|
||||
ADVANCE(sizeof(wasm_size_t));
|
||||
|
||||
for (const auto &fiber : sb()->get_fibers()) {
|
||||
// Write the key of the fiber
|
||||
RESERVE(sizeof(wasm_ptr_t));
|
||||
*(wasm_ptr_t *)data = std::get<0>(fiber.first);
|
||||
ADVANCE(sizeof(wasm_ptr_t));
|
||||
RESERVE(sizeof(wasm_ptr_t));
|
||||
*(wasm_ptr_t *)data = std::get<1>(fiber.first);
|
||||
ADVANCE(sizeof(wasm_ptr_t));
|
||||
RESERVE(sizeof(wasm_ptr_t));
|
||||
*(wasm_ptr_t *)data = std::get<2>(fiber.first);
|
||||
ADVANCE(sizeof(wasm_ptr_t));
|
||||
|
||||
// Write the number of frames in this fiber
|
||||
RESERVE(sizeof(wasm_size_t));
|
||||
*(wasm_size_t *)data = fiber.second.get_stack().size();
|
||||
ADVANCE(sizeof(wasm_size_t));
|
||||
|
||||
// Write the state of each frame
|
||||
for (const auto &frame : fiber.second.get_stack()) {
|
||||
RESERVE(sizeof(int32_t));
|
||||
*(int32_t *)data = (int32_t)frame;
|
||||
ADVANCE(sizeof(int32_t));
|
||||
}
|
||||
}
|
||||
|
||||
std::memset(data, 0, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
extern "C" RETRO_API bool retro_unserialize(const void *data, size_t len) {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
extern "C" RETRO_API void retro_cheat_reset() {
|
||||
|
|
Loading…
Add table
Reference in a new issue