Implement save state serialization for WASI file descriptors in libretro builds

This commit is contained in:
刘皓 2025-05-23 23:59:29 -04:00
parent 057b6dc95d
commit e957af931c
No known key found for this signature in database
GPG key ID: 7901753DB465B711
5 changed files with 57 additions and 7 deletions

View file

@ -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
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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 <typename T> 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 */

View file

@ -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);