mkxp-z/binding-sandbox/sandbox-serial-util.cpp
刘皓 ef64eaa4e5
Implement reinitializing OpenGL objects in libretro builds
Libretro provides a way to detect when the OpenGL context is destroyed,
like on Android and possibly also iOS when switching between apps. This
commit implements reinitializing all OpenGL objects when this happens so
that the graphics continue to function in this case.
2025-06-10 16:30:44 -04:00

564 lines
20 KiB
C++

/*
** sandbox-serial-util.cpp
**
** This file is part of mkxp.
**
** Copyright (C) 2013 - 2021 Amaryllis Kulla <ancurio@mapleshrine.eu>
**
** mkxp is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 2 of the License, or
** (at your option) any later version.
**
** mkxp is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
*/
#include "sandbox-serial-util.h"
#include "etc-internal.h"
#include "scene.h"
#include "transform.h"
#ifdef _MSC_VER
# include <stdlib.h>
#endif // _MSC_VER
using namespace mkxp_sandbox;
#define RESERVE(bytes) do { \
if (max_size < (bytes)) { \
return false; \
} \
} while (0)
#define ADVANCE(bytes) do { \
data = (uint8_t *)data + (bytes); \
max_size -= (bytes); \
} while (0)
template <typename T> static typename std::enable_if<std::is_constructible<T>::value, void *>::type construct() {
return new T;
}
template <typename T> static typename std::enable_if<!std::is_constructible<T>::value && std::is_constructible<T, Exception &>::value, void *>::type construct() {
Exception e;
T *obj = new T(e);
if (e.is_ok()) {
return obj;
} else {
delete obj;
return nullptr;
}
}
template <typename T> static void destroy(void *self) {
if (self != nullptr) {
delete (T *)self;
}
}
template <typename T> static typename std::enable_if<std::is_base_of<Disposable, T>::value>::type dispose(void *self) {
if (self != nullptr) {
((T *)self)->dispose();
}
}
template <typename T> static typename std::enable_if<!std::is_base_of<Disposable, T>::value>::type dispose(void *self) {}
template <typename T> static typename std::enable_if<std::is_base_of<Disposable, T>::value, bool>::type disposed(void *self) {
return self == nullptr || ((T *)self)->isDisposed();
}
template <typename T> static typename std::enable_if<!std::is_base_of<Disposable, T>::value, bool>::type disposed(void *self) {
return self == nullptr;
}
template <typename T> static bool serialize(const void *self, void *&data, wasm_size_t &max_size) {
return ((const T *)self)->sandbox_serialize(data, max_size);
}
template <typename T> static bool deserialize(void *self, const void *&data, wasm_size_t &max_size) {
return ((T *)self)->sandbox_deserialize(data, max_size);
}
template <typename T> using deserialize_begin_declaration_with_is_new = decltype(std::declval<T *>()->sandbox_deserialize_begin(std::declval<bool>()));
template <typename T> using deserialize_begin_declaration_without_is_new = decltype(std::declval<T *>()->sandbox_deserialize_begin());
template <typename T> static typename std::enable_if<boost::is_detected<deserialize_begin_declaration_with_is_new, T>::value>::type deserialize_begin(void *self, bool is_new) {
((T *)self)->sandbox_deserialize_begin(is_new);
}
template <typename T> static typename std::enable_if<!boost::is_detected<deserialize_begin_declaration_with_is_new, T>::value && boost::is_detected<deserialize_begin_declaration_without_is_new, T>::value>::type deserialize_begin(void *self, bool is_new) {
((T *)self)->sandbox_deserialize_begin();
}
template <typename T> static typename std::enable_if<!boost::is_detected<deserialize_begin_declaration_with_is_new, T>::value && !boost::is_detected<deserialize_begin_declaration_without_is_new, T>::value>::type deserialize_begin(void *self, bool is_new) {}
template <typename T> using deserialize_end_declaration = decltype(std::declval<T *>()->sandbox_deserialize_end());
template <typename T> static typename std::enable_if<boost::is_detected<deserialize_end_declaration, T>::value>::type deserialize_end(void *self) {
((T *)self)->sandbox_deserialize_end();
}
template <typename T> static typename std::enable_if<!boost::is_detected<deserialize_end_declaration, T>::value>::type deserialize_end(void *self) {}
template <typename T> using reinit_declaration = decltype(std::declval<T *>()->sandbox_reinit());
template <typename T> static typename std::enable_if<boost::is_detected<reinit_declaration, T>::value>::type reinit(void *self) {
((T *)self)->sandbox_reinit();
}
template <typename T> static typename std::enable_if<!boost::is_detected<reinit_declaration, T>::value>::type reinit(void *self) {}
#define _SANDBOX_DEF_TYPENUM_TABLE_ENTRY(_r, _data, T) {construct<T>, destroy<T>, dispose<T>, disposed<T>, serialize<T>, deserialize<T>, deserialize_begin<T>, deserialize_end<T>, reinit<T>},
extern const struct typenum_table_entry mkxp_sandbox::typenum_table[SANDBOX_NUM_TYPENUMS] = {BOOST_PP_SEQ_FOR_EACH(_SANDBOX_DEF_TYPENUM_TABLE_ENTRY, _, SANDBOX_TYPENUM_TYPES)};
extern const wasm_size_t mkxp_sandbox::typenum_table_size = SANDBOX_NUM_TYPENUMS;
std::unordered_map<wasm_size_t, struct sandbox_swizzle_info> mkxp_sandbox::swizzle_map;
bool mkxp_sandbox::deser_swap_bytes = false;
sandbox_swizzle_info::sandbox_swizzle_info(void *ptr, wasm_size_t typenum) : ptr(ptr), typenum(typenum), ref_count(0), exists(true) {}
sandbox_swizzle_info::sandbox_swizzle_info(struct sandbox_swizzle_info &&info) noexcept : ptr(std::exchange(info.ptr, nullptr)), typenum(info.typenum), ref_count(std::exchange(info.ref_count, 1)), exists(std::exchange(info.exists, true)) {}
struct sandbox_swizzle_info &sandbox_swizzle_info::operator=(struct sandbox_swizzle_info &&info) noexcept {
ptr = std::exchange(info.ptr, nullptr);
typenum = info.typenum;
ref_count = std::exchange(info.ref_count, 1);
exists = std::exchange(info.exists, true);
return *this;
}
sandbox_swizzle_info::~sandbox_swizzle_info() {
if (!exists) {
delete (std::vector<void **> *)ptr;
}
}
wasm_size_t sandbox_swizzle_info::get_ref_count() const noexcept {
return ref_count;
}
bool sandbox_swizzle_info::set_ptr(void *ptr, wasm_size_t typenum) {
if (this->typenum != typenum) {
// Don't allow pointers of mismatching type
return false;
}
if (exists && ptr != this->ptr) {
// Don't allow setting the pointer more than once
return false;
}
if (!exists) {
for (void **ref : *(std::vector<void **> *)this->ptr) {
*ref = ptr;
}
delete (std::vector<void **> *)this->ptr;
exists = true;
this->ptr = ptr;
}
return true;
}
void *sandbox_swizzle_info::get_ptr() const {
return exists ? ptr : nullptr;
}
wasm_size_t sandbox_swizzle_info::get_typenum() const {
return typenum;
}
bool sandbox_swizzle_info::get_exists() const {
return exists;
}
template <> bool mkxp_sandbox::sandbox_serialize(bool value, void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(uint8_t));
*(uint8_t *)data = value;
ADVANCE(sizeof(uint8_t));
return true;
}
template <> bool mkxp_sandbox::sandbox_deserialize(bool &value, const void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(uint8_t));
value = *(uint8_t *)data;
ADVANCE(sizeof(uint8_t));
return true;
}
template <> bool mkxp_sandbox::sandbox_serialize(int8_t value, void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(int8_t));
*(int8_t *)data = value;
ADVANCE(sizeof(int8_t));
return true;
}
template <> bool mkxp_sandbox::sandbox_deserialize(int8_t &value, const void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(int8_t));
value = *(int8_t *)data;
ADVANCE(sizeof(int8_t));
return true;
}
template <> bool mkxp_sandbox::sandbox_serialize(uint8_t value, void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(uint8_t));
*(uint8_t *)data = value;
ADVANCE(sizeof(uint8_t));
return true;
}
template <> bool mkxp_sandbox::sandbox_deserialize(uint8_t &value, const void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(uint8_t));
value = *(uint8_t *)data;
ADVANCE(sizeof(uint8_t));
return true;
}
template <> bool mkxp_sandbox::sandbox_serialize(int16_t value, void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(int16_t));
*(int16_t *)data = value;
ADVANCE(sizeof(int16_t));
return true;
}
template <> bool mkxp_sandbox::sandbox_deserialize(int16_t &value, const void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(int16_t));
if (deser_swap_bytes) {
#ifdef _MSC_VER
static_assert(sizeof(unsigned short) == sizeof(int16_t), "unsigned short should be 16 bits");
value = (int16_t)_byteswap_ushort(*(unsigned short *)data);
#else
value = (int16_t)__builtin_bswap16(*(uint16_t *)data);
#endif // _MSC_VER
} else {
value = *(int16_t *)data;
}
ADVANCE(sizeof(int16_t));
return true;
}
template <> bool mkxp_sandbox::sandbox_serialize(uint16_t value, void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(uint16_t));
*(uint16_t *)data = value;
ADVANCE(sizeof(uint16_t));
return true;
}
template <> bool mkxp_sandbox::sandbox_deserialize(uint16_t &value, const void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(uint16_t));
if (deser_swap_bytes) {
#ifdef _MSC_VER
static_assert(sizeof(unsigned short) == sizeof(uint16_t), "unsigned short should be 16 bits");
value = (uint16_t)_byteswap_ushort(*(unsigned short *)data);
#else
value = __builtin_bswap16(*(uint16_t *)data);
#endif // _MSC_VER
} else {
value = *(uint16_t *)data;
}
ADVANCE(sizeof(uint16_t));
return true;
}
template <> bool mkxp_sandbox::sandbox_serialize(int32_t value, void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(int32_t));
*(int32_t *)data = value;
ADVANCE(sizeof(int32_t));
return true;
}
template <> bool mkxp_sandbox::sandbox_deserialize(int32_t &value, const void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(int32_t));
if (deser_swap_bytes) {
#ifdef _MSC_VER
static_assert(sizeof(unsigned long) == sizeof(int32_t), "unsigned long should be 32 bits");
value = (int32_t)_byteswap_ulong(*(unsigned long *)data);
#else
value = (int32_t)__builtin_bswap32(*(uint32_t *)data);
#endif // _MSC_VER
} else {
value = *(int32_t *)data;
}
ADVANCE(sizeof(int32_t));
return true;
}
template <> bool mkxp_sandbox::sandbox_serialize(uint32_t value, void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(uint32_t));
*(uint32_t *)data = value;
ADVANCE(sizeof(uint32_t));
return true;
}
template <> bool mkxp_sandbox::sandbox_deserialize(uint32_t &value, const void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(uint32_t));
if (deser_swap_bytes) {
#ifdef _MSC_VER
static_assert(sizeof(unsigned long) == sizeof(uint32_t), "unsigned long should be 32 bits");
value = (uint32_t)_byteswap_ulong(*(unsigned long *)data);
#else
value = __builtin_bswap32(*(uint32_t *)data);
#endif // _MSC_VER
} else {
value = *(uint32_t *)data;
}
ADVANCE(sizeof(uint32_t));
return true;
}
template <> bool mkxp_sandbox::sandbox_serialize(int64_t value, void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(int64_t));
*(int64_t *)data = value;
ADVANCE(sizeof(int64_t));
return true;
}
template <> bool mkxp_sandbox::sandbox_deserialize(int64_t &value, const void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(int64_t));
if (deser_swap_bytes) {
#ifdef _MSC_VER
value = (int64_t)_byteswap_uint64(*(unsigned __int64 *)data);
#else
value = (int64_t)__builtin_bswap64(*(uint64_t *)data);
#endif // _MSC_VER
} else {
value = *(int64_t *)data;
}
ADVANCE(sizeof(int64_t));
return true;
}
template <> bool mkxp_sandbox::sandbox_serialize(uint64_t value, void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(uint64_t));
*(uint64_t *)data = value;
ADVANCE(sizeof(uint64_t));
return true;
}
template <> bool mkxp_sandbox::sandbox_deserialize(uint64_t &value, const void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(uint64_t));
if (deser_swap_bytes) {
#ifdef _MSC_VER
value = (uint64_t)_byteswap_uint64(*(unsigned __int64 *)data);
#else
value = __builtin_bswap64(*(uint64_t *)data);
#endif // _MSC_VER
} else {
value = *(uint64_t *)data;
}
ADVANCE(sizeof(uint64_t));
return true;
}
template <> bool mkxp_sandbox::sandbox_serialize(float value, void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(float));
*(float *)data = value;
ADVANCE(sizeof(float));
return true;
}
template <> bool mkxp_sandbox::sandbox_deserialize(float &value, const void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(float));
if (deser_swap_bytes) {
#ifdef _MSC_VER
static_assert(sizeof(unsigned long) == sizeof(float), "unsigned long should be 32 bits");
uint32_t tmp = (uint32_t)_byteswap_ulong(*(unsigned long *)data);
#else
uint32_t tmp = __builtin_bswap32(*(uint32_t *)data);
#endif // _MSC_VER
std::memcpy(&value, &tmp, 4);
} else {
value = *(float *)data;
}
ADVANCE(sizeof(float));
return true;
}
template <> bool mkxp_sandbox::sandbox_serialize(double value, void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(double));
*(double *)data = value;
ADVANCE(sizeof(double));
return true;
}
template <> bool mkxp_sandbox::sandbox_deserialize(double &value, const void *&data, wasm_size_t &max_size) {
RESERVE(sizeof(double));
if (deser_swap_bytes) {
#ifdef _MSC_VER
uint64_t tmp = (uint64_t)_byteswap_uint64(*(unsigned __int64 *)data);
#else
uint64_t tmp = __builtin_bswap64(*(uint64_t *)data);
#endif // _MSC_VER
std::memcpy(&value, &tmp, 8);
} else {
value = *(double *)data;
}
ADVANCE(sizeof(double));
return true;
}
template <> bool mkxp_sandbox::sandbox_serialize(const char *value, void *&data, wasm_size_t &max_size) {
wasm_size_t size = std::strlen(value);
if (!sandbox_serialize(size, data, max_size)) return false;
RESERVE(size);
std::memcpy(data, value, size);
ADVANCE(size);
return true;
}
template <> bool mkxp_sandbox::sandbox_serialize(const std::string &value, void *&data, wasm_size_t &max_size) {
wasm_size_t size = value.length();
if (!sandbox_serialize(size, data, max_size)) return false;
RESERVE(size);
std::memcpy(data, value.c_str(), size);
ADVANCE(size);
return true;
}
template <> bool mkxp_sandbox::sandbox_deserialize(std::string &value, const void *&data, wasm_size_t &max_size) {
wasm_size_t size;
if (!sandbox_deserialize(size, data, max_size)) return false;
RESERVE(size);
value.clear();
value.resize(size);
char *str = &value[0];
std::memcpy(str, data, size);
if (std::strlen(str) != size) {
value.clear();
return false;
}
ADVANCE(size);
return true;
}
template <> bool mkxp_sandbox::sandbox_serialize(const NormValue &value, void *&data, wasm_size_t &max_size) {
if (!sandbox_serialize((int32_t)value.unNorm, data, max_size)) return false;
return true;
}
template <> bool mkxp_sandbox::sandbox_deserialize(NormValue &value, const void *&data, wasm_size_t &max_size) {
if (!sandbox_deserialize((int32_t &)value.unNorm, data, max_size)) return false;
value.unNorm = clamp(value.unNorm, 0, 255);
value.norm = value.unNorm / 255.0f;
return true;
}
template <> bool mkxp_sandbox::sandbox_serialize(const Vec2 &value, void *&data, wasm_size_t &max_size) {
if (!sandbox_serialize(value.x, data, max_size)) return false;
if (!sandbox_serialize(value.y, data, max_size)) return false;
return true;
}
template <> bool mkxp_sandbox::sandbox_deserialize(Vec2 &value, const void *&data, wasm_size_t &max_size) {
if (!sandbox_deserialize(value.x, data, max_size)) return false;
if (!sandbox_deserialize(value.y, data, max_size)) return false;
return true;
}
template <> bool mkxp_sandbox::sandbox_serialize(const Vec4 &value, void *&data, wasm_size_t &max_size) {
if (!sandbox_serialize(value.x, data, max_size)) return false;
if (!sandbox_serialize(value.y, data, max_size)) return false;
if (!sandbox_serialize(value.z, data, max_size)) return false;
if (!sandbox_serialize(value.w, data, max_size)) return false;
return true;
}
template <> bool mkxp_sandbox::sandbox_deserialize(Vec4 &value, const void *&data, wasm_size_t &max_size) {
if (!sandbox_deserialize(value.x, data, max_size)) return false;
if (!sandbox_deserialize(value.y, data, max_size)) return false;
if (!sandbox_deserialize(value.z, data, max_size)) return false;
if (!sandbox_deserialize(value.w, data, max_size)) return false;
return true;
}
template <> bool mkxp_sandbox::sandbox_serialize(const Vec2i &value, void *&data, wasm_size_t &max_size) {
if (!sandbox_serialize((int32_t)value.x, data, max_size)) return false;
if (!sandbox_serialize((int32_t)value.y, data, max_size)) return false;
return true;
}
template <> bool mkxp_sandbox::sandbox_deserialize(Vec2i &value, const void *&data, wasm_size_t &max_size) {
if (!sandbox_deserialize((int32_t &)value.x, data, max_size)) return false;
if (!sandbox_deserialize((int32_t &)value.y, data, max_size)) return false;
return true;
}
template <> bool mkxp_sandbox::sandbox_serialize(const IntRect &value, void *&data, wasm_size_t &max_size) {
if (!sandbox_serialize((int32_t)value.x, data, max_size)) return false;
if (!sandbox_serialize((int32_t)value.y, data, max_size)) return false;
if (!sandbox_serialize((int32_t)value.w, data, max_size)) return false;
if (!sandbox_serialize((int32_t)value.h, data, max_size)) return false;
return true;
}
template <> bool mkxp_sandbox::sandbox_deserialize(IntRect &value, const void *&data, wasm_size_t &max_size) {
if (!sandbox_deserialize((int32_t &)value.x, data, max_size)) return false;
if (!sandbox_deserialize((int32_t &)value.y, data, max_size)) return false;
if (!sandbox_deserialize((int32_t &)value.w, data, max_size)) return false;
if (!sandbox_deserialize((int32_t &)value.h, data, max_size)) return false;
return true;
}
template <> bool mkxp_sandbox::sandbox_serialize(const Scene::Geometry &value, void *&data, wasm_size_t &max_size) {
if (!sandbox_serialize(value.rect, data, max_size)) return false;
if (!sandbox_serialize(value.orig, data, max_size)) return false;
return true;
}
template <> bool mkxp_sandbox::sandbox_deserialize(Scene::Geometry &value, const void *&data, wasm_size_t &max_size) {
if (!sandbox_deserialize(value.rect, data, max_size)) return false;
if (!sandbox_deserialize(value.orig, data, max_size)) return false;
return true;
}
template <> bool mkxp_sandbox::sandbox_serialize(const Transform &value, void *&data, wasm_size_t &max_size) {
if (!sandbox_serialize(value.getPosition(), data, max_size)) return false;
if (!sandbox_serialize(value.getOrigin(), data, max_size)) return false;
if (!sandbox_serialize(value.getScale(), data, max_size)) return false;
if (!sandbox_serialize(value.getGlobalOffset(), data, max_size)) return false;
if (!sandbox_serialize(value.getRotation(), data, max_size)) return false;
return true;
}
template <> bool mkxp_sandbox::sandbox_deserialize(Transform &value, const void *&data, wasm_size_t &max_size) {
{
Vec2 position;
if (!sandbox_deserialize(position, data, max_size)) return false;
if (position != value.getPosition()) {
value.setPosition(position);
}
}
{
Vec2 origin;
if (!sandbox_deserialize(origin, data, max_size)) return false;
if (origin != value.getOrigin()) {
value.setOrigin(origin);
}
}
{
Vec2 scale;
if (!sandbox_deserialize(scale, data, max_size)) return false;
if (scale != value.getScale()) {
value.setScale(scale);
}
}
{
Vec2i offset;
if (!sandbox_deserialize(offset, data, max_size)) return false;
if (offset != value.getGlobalOffset()) {
value.setGlobalOffset(offset);
}
}
{
float rotation;
if (!sandbox_deserialize(rotation, data, max_size)) return false;
if (rotation != value.getRotation()) {
value.setRotation(rotation);
}
}
return true;
}