From e957af931cdd3a74ae3609a076ebddd6b40cf647 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E7=9A=93?= Date: Fri, 23 May 2025 23:59:29 -0400 Subject: [PATCH] Implement save state serialization for WASI file descriptors in libretro builds --- binding-sandbox/sandbox.cpp | 4 ++++ binding-sandbox/sandbox.h | 1 + binding-sandbox/wasi.cpp | 41 +++++++++++++++++++++++++++++++++++-- binding-sandbox/wasi.h | 6 ++++-- src/core.cpp | 12 ++++++++--- 5 files changed, 57 insertions(+), 7 deletions(-) diff --git a/binding-sandbox/sandbox.cpp b/binding-sandbox/sandbox.cpp index 4c7a9c6f..608884a5 100644 --- a/binding-sandbox/sandbox.cpp +++ b/binding-sandbox/sandbox.cpp @@ -125,6 +125,10 @@ sandbox::~sandbox() { wasm2c_ruby_free(RB); } +bool sandbox::sandbox_serialize_fdtable(void *&data, wasm_size_t &max_size) const { + return wasi->sandbox_serialize(data, max_size); +} + Movie *sandbox::get_movie_from_main_thread() { return movie.load(std::memory_order_relaxed); // No need for synchronization because we always set the movie from the main thread } diff --git a/binding-sandbox/sandbox.h b/binding-sandbox/sandbox.h index 68173171..9ddcd6c8 100644 --- a/binding-sandbox/sandbox.h +++ b/binding-sandbox/sandbox.h @@ -63,6 +63,7 @@ namespace mkxp_sandbox { inline struct mkxp_sandbox::bindings *operator->() noexcept { return &*bindings; } sandbox(); ~sandbox(); + bool sandbox_serialize_fdtable(void *&data, wasm_size_t &max_size) const; Movie *get_movie_from_main_thread(); Movie *get_movie_from_audio_thread(); void set_movie(Movie *new_movie); diff --git a/binding-sandbox/wasi.cpp b/binding-sandbox/wasi.cpp index 7e886907..1ee3015c 100644 --- a/binding-sandbox/wasi.cpp +++ b/binding-sandbox/wasi.cpp @@ -28,6 +28,7 @@ #include "core.h" #include "wasi.h" #include "binding-base.h" +#include "sandbox-serial-util.h" using namespace mkxp_sandbox; @@ -36,11 +37,11 @@ static inline size_t strlen_safe(const char *str, size_t max_length) { return ptr == nullptr ? max_length : ptr - str; } -struct fs_dir *wasi_file_entry::dir_handle() { +struct fs_dir *wasi_file_entry::dir_handle() const noexcept { return (struct fs_dir *)handle; } -struct FileSystem::File *wasi_file_entry::file_handle() { +struct FileSystem::File *wasi_file_entry::file_handle() const noexcept { return (struct FileSystem::File *)handle; } @@ -128,6 +129,42 @@ const char *wasi_t::str(wasm_ptr_t address) const noexcept { return sandbox_str(*ruby, address); } +bool wasi_t::sandbox_serialize(void *&data, mkxp_sandbox::wasm_size_t &max_size) const { + if (!::sandbox_serialize((wasm_size_t)fdtable.size(), data, max_size)) return false; + + wasm_size_t num_free_handles = 0; + + for (const struct wasi_file_entry &entry : fdtable) { + if (entry.type == wasi_fd_type::FSDIR) { + if (num_free_handles > 0) { + if (!::sandbox_serialize((uint8_t)0, data, max_size)) return false; + if (!::sandbox_serialize(num_free_handles, data, max_size)) return false; + num_free_handles = 0; + } + if (!::sandbox_serialize((uint8_t)1, data, max_size)) return false; + if (!::sandbox_serialize(entry.dir_handle()->path, data, max_size)) return false; + if (!::sandbox_serialize(entry.dir_handle()->root->path, data, max_size)) return false; + } else if (entry.type == wasi_fd_type::FSFILE) { + if (num_free_handles > 0) { + if (!::sandbox_serialize((uint8_t)0, data, max_size)) return false; + if (!::sandbox_serialize(num_free_handles, data, max_size)) return false; + num_free_handles = 0; + } + if (!::sandbox_serialize((uint8_t)2, data, max_size)) return false; + if (!::sandbox_serialize(entry.file_handle()->path(), data, max_size)) return false; + } else { + ++num_free_handles; + } + } + if (num_free_handles > 0) { + if (!::sandbox_serialize((uint8_t)0, data, max_size)) return false; + if (!::sandbox_serialize(num_free_handles, data, max_size)) return false; + num_free_handles = 0; + } + + return true; +} + extern "C" uint32_t w2c_wasi__snapshot__preview1_args_get(wasi_t *wasi, wasm_ptr_t argv, wasm_ptr_t argv_buf) { return WASI_ESUCCESS; } diff --git a/binding-sandbox/wasi.h b/binding-sandbox/wasi.h index 5eae3bff..89878a0c 100644 --- a/binding-sandbox/wasi.h +++ b/binding-sandbox/wasi.h @@ -181,8 +181,8 @@ struct wasi_file_entry { // The file/directory handle that the file descriptor corresponds to. The exact type of this handle depends on the type of file descriptor. void *handle; - struct fs_dir *dir_handle(); - struct FileSystem::File *file_handle(); + struct fs_dir *dir_handle() const noexcept; + struct FileSystem::File *file_handle() const noexcept; }; typedef struct w2c_wasi__snapshot__preview1 { @@ -230,6 +230,8 @@ typedef struct w2c_wasi__snapshot__preview1 { template void arycpy(mkxp_sandbox::wasm_ptr_t dst_address, const T *src, mkxp_sandbox::wasm_size_t num_elements) const noexcept { mkxp_sandbox::sandbox_arycpy(*ruby, dst_address, src, num_elements); } + + bool sandbox_serialize(void *&data, mkxp_sandbox::wasm_size_t &max_size) const; } wasi_t; #endif /* MKXPZ_SANDBOX_WASI_H */ diff --git a/src/core.cpp b/src/core.cpp index d0ba3034..1fc25857 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -1639,10 +1639,12 @@ extern "C" RETRO_API bool retro_serialize(void *data, size_t len) { if (!Graphics::sandbox_serialize_movie(sb().get_movie_from_main_thread(), data, max_size)) return false; } - // TODO: write the file descriptor table + // Write the open WASI file descriptors + if (!sb().sandbox_serialize_fdtable(data, max_size)) return false; - // Write each object + // Write the number of objects, then each object OBJECTS_BEGIN; + if (!sandbox_serialize((wasm_size_t)sb()->get_objects().size(), data, max_size)) OBJECTS_END_FAIL; wasm_size_t num_free_objects = 0; for (const auto &object : sb()->get_objects()) { if (object.typenum == 0) { @@ -1665,7 +1667,10 @@ extern "C" RETRO_API bool retro_serialize(void *data, size_t len) { num_free_objects = 0; } - // Write each extra object that was found during serialization of the normal objects + // Write the number of extra objects that were found during serialization of the normal objects, then each such object + if (max_size < sizeof(wasm_size_t)) OBJECTS_END_FAIL; + wasm_size_t *num_extra_objects_ptr = (wasm_size_t *)data; + ADVANCE(sizeof(wasm_size_t)); for (size_t i = 0; i < extra_objects.size(); ++i) { // More items can be added to this vector during iteration const void *ptr = std::get<0>(extra_objects[i]); wasm_size_t typenum = std::get<1>(extra_objects[i]); @@ -1683,6 +1688,7 @@ extern "C" RETRO_API bool retro_serialize(void *data, size_t len) { if (!sandbox_serialize(num_free_objects, data, max_size)) OBJECTS_END_FAIL; num_free_objects = 0; } + *num_extra_objects_ptr = (wasm_size_t)extra_objects.size(); OBJECTS_END; std::memset(data, 0, max_size);