/* ** sandbox-serial-util.h ** ** This file is part of mkxp. ** ** Copyright (C) 2013 - 2021 Amaryllis Kulla ** ** 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 . */ #ifndef MKXPZ_SANDBOX_SERIAL_UTIL_H #define MKXPZ_SANDBOX_SERIAL_UTIL_H #include #include #include #include #include #include #include #include "sandbox.h" #include "bitmap.h" #include "etc.h" #include "font.h" #include "plane.h" #include "sprite.h" #include "table.h" #include "tilemap.h" #include "tilemapvx.h" #include "viewport.h" #include "window.h" #include "windowvx.h" #define SANDBOX_TYPENUM_TYPES \ (Bitmap) \ (Color) \ (Font) \ (Plane) \ (Rect) \ (Sprite) \ (Table) \ (Tilemap) \ (Tilemap::Autotiles) \ (TilemapVX) \ (TilemapVX::BitmapArray) \ (Tone) \ (Viewport) \ (Window) \ (WindowVX) \ #define SANDBOX_NUM_TYPENUMS BOOST_PP_SEQ_SIZE(SANDBOX_TYPENUM_TYPES) #define _SANDBOX_DEF_GET_TYPENUM_DETAIL(T, num) template <> struct get_typenum { \ static_assert(num != 0, "typenum should not be 0"); \ static_assert(num <= SANDBOX_NUM_TYPENUMS, "typenum should not be greater than the number of typenums"); \ static constexpr wasm_size_t value = num; \ }; #define _SANDBOX_DEF_GET_TYPENUM(_r, _data, T) _SANDBOX_DEF_GET_TYPENUM_DETAIL(T, __COUNTER__ - _get_typenum_counter_start) namespace mkxp_sandbox { template struct get_typenum; static constexpr wasm_size_t _get_typenum_counter_start = __COUNTER__; BOOST_PP_SEQ_FOR_EACH(_SANDBOX_DEF_GET_TYPENUM, _, SANDBOX_TYPENUM_TYPES); struct sandbox_swizzle_info { template sandbox_swizzle_info(T *&ref) : ptr(new std::vector({(void **)&ref})), typenum(mkxp_sandbox::get_typenum::value), ref_count(1), exists(false) {} sandbox_swizzle_info(void *ptr, wasm_size_t typenum); sandbox_swizzle_info(const struct sandbox_swizzle_info &) = delete; sandbox_swizzle_info(struct sandbox_swizzle_info &&) noexcept; struct sandbox_swizzle_info &operator=(const struct sandbox_swizzle_info &) = delete; struct sandbox_swizzle_info &operator=(struct sandbox_swizzle_info &&) noexcept; ~sandbox_swizzle_info(); wasm_size_t get_ref_count() const noexcept; template bool add_ref(T *&ref) { if (typenum != mkxp_sandbox::get_typenum::value) { return false; } if (ref_count > 0 && (std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value)) { // Don't allow types that are copied by value (Color, Tone, Rect and Font) or autotiles/bitmap arrays to be referenced more than once return false; } if (exists) { ref = (T *)ptr; } else { ref = nullptr; ((std::vector *)ptr)->push_back((void **)&ref); } ++ref_count; return true; } bool set_ptr(void *ptr, wasm_size_t typenum); void *get_ptr() const; wasm_size_t get_typenum() const; bool get_exists() const; private: // If `exists` is true, this is a pointer to the object. Otherwise, this is a `std::vector` of pointers that are waiting to point to the object. void *ptr; // The type of the object. wasm_size_t typenum; // The number of times this object is referenced by other objects. wasm_size_t ref_count; // True if the object has been deserialized, otherwise false. bool exists; }; extern std::unordered_map swizzle_map; extern bool deser_swap_bytes; template using sandbox_serialize_member_declaration = decltype(std::declval()->sandbox_serialize(std::declval(), std::declval())); template using sandbox_deserialize_member_declaration = decltype(std::declval()->sandbox_deserialize(std::declval(), std::declval())); template typename std::enable_if::value, bool>::type sandbox_serialize(T value, void *&data, wasm_size_t &max_size); template typename std::enable_if::value, bool>::type sandbox_serialize(T value, void *&data, wasm_size_t &max_size); template typename std::enable_if::value && !std::is_enum::value && std::is_arithmetic::value, bool>::type sandbox_serialize(T value, void *&data, wasm_size_t &max_size); template bool sandbox_serialize(const std::vector &value, void *&data, wasm_size_t &max_size); template typename std::enable_if::value, bool>::type sandbox_serialize(const T *value, void *&data, wasm_size_t &max_size); template typename std::enable_if::value, bool>::type sandbox_serialize(const T *value, void *&data, wasm_size_t &max_size); template typename std::enable_if::value && boost::is_detected::value, bool>::type sandbox_serialize(const T &value, void *&data, wasm_size_t &max_size); template typename std::enable_if::value && !boost::is_detected::value, bool>::type sandbox_serialize(const T &value, void *&data, wasm_size_t &max_size); template typename std::enable_if::value && std::is_same::value, bool>::type sandbox_deserialize(T &value, const void *&data, wasm_size_t &max_size); template typename std::enable_if::value && std::is_enum::value, bool>::type sandbox_deserialize(T &value, const void *&data, wasm_size_t &max_size); template typename std::enable_if::value && !std::is_same::value && !std::is_enum::value && std::is_arithmetic::value, bool>::type sandbox_deserialize(T &value, const void *&data, wasm_size_t &max_size); template typename std::enable_if::value, bool>::type sandbox_deserialize(std::vector &value, const void *&data, wasm_size_t &max_size); template typename std::enable_if::value && std::is_class::value, bool>::type sandbox_deserialize(T *&value, const void *&data, wasm_size_t &max_size); template typename std::enable_if::value && std::is_class::value && boost::is_detected::value, bool>::type sandbox_deserialize(T &value, const void *&data, wasm_size_t &max_size); template typename std::enable_if::value && std::is_class::value && !boost::is_detected::value, bool>::type sandbox_deserialize(T &value, const void *&data, wasm_size_t &max_size); template struct sandbox_ptr_map { private: static std::unordered_map unswizzle_map; static bool is_serializing; static bool is_deserializing; public: static void sandbox_serialize_begin() { using namespace mkxp_sandbox; if (is_deserializing) { std::abort(); } if (is_serializing) { return; } is_serializing = true; unswizzle_map.clear(); wasm_objkey_t key = 0; for (const auto &object : sb()->objects) { ++key; if (object.typenum == get_typenum::value) { unswizzle_map.emplace((T *)object.inner.ptr, key); } } } static bool sandbox_serialize(const T *ptr, void *&data, wasm_size_t &max_size) { using namespace mkxp_sandbox; if (is_deserializing) { std::abort(); } if (!mkxp_sandbox::sandbox_serialize(ptr != nullptr, data, max_size)) return false; if (ptr != nullptr) { if (!mkxp_sandbox::sandbox_serialize(unswizzle_map.at(ptr), data, max_size)) return false; } return true; } static void sandbox_serialize_end() { if (is_deserializing) { std::abort(); } is_serializing = false; unswizzle_map.clear(); } static void sandbox_deserialize_begin() { using namespace mkxp_sandbox; if (is_serializing) { std::abort(); } if (is_deserializing) { return; } is_deserializing = true; swizzle_map.clear(); } static bool sandbox_deserialize(T *&ref, const void *&data, wasm_size_t &max_size) { using namespace mkxp_sandbox; if (is_serializing) { std::abort(); } bool is_not_null; if (!mkxp_sandbox::sandbox_deserialize(is_not_null, data, max_size)) return false; if (!is_not_null) { ref = nullptr; return true; } wasm_objkey_t key; if (!mkxp_sandbox::sandbox_deserialize(key, data, max_size)) return false; const auto it = swizzle_map.find(key); if (it == swizzle_map.end()) { swizzle_map.emplace(key, sandbox_swizzle_info(ref)); return true; } else { return it->second.add_ref(ref); } } static void sandbox_deserialize_end() { if (is_serializing) { std::abort(); } is_deserializing = false; swizzle_map.clear(); } }; template std::unordered_map sandbox_ptr_map::unswizzle_map; template bool sandbox_ptr_map::is_serializing = false; template bool sandbox_ptr_map::is_deserializing = false; template typename std::enable_if::value, bool>::type sandbox_serialize(const T *value, void *&data, wasm_size_t &max_size) { return sandbox_ptr_map::sandbox_serialize(value, data, max_size); } template typename std::enable_if::value && std::is_class::value, bool>::type sandbox_deserialize(T *&value, const void *&data, wasm_size_t &max_size) { return sandbox_ptr_map::sandbox_deserialize(value, data, max_size); } template bool sandbox_serialize(const std::vector &value, void *&data, wasm_size_t &max_size) { if (!sandbox_serialize((wasm_size_t)value.size(), data, max_size)) return false; for (const T &item : value) { if (!sandbox_serialize(item, data, max_size)) return false; } return true; } template typename std::enable_if::value, bool>::type sandbox_deserialize(std::vector &value, const void *&data, wasm_size_t &max_size) { wasm_size_t size; if (!sandbox_deserialize(size, data, max_size)) return false; value.clear(); value.reserve(size); while (size > 0) { value.emplace_back(); if (!sandbox_deserialize(value.back(), data, max_size)) return false; --size; } return true; } template typename std::enable_if::value, bool>::type sandbox_serialize(T value, void *&data, wasm_size_t &max_size) { return sandbox_serialize((int32_t)value, data, max_size); } template typename std::enable_if::value && std::is_enum::value, bool>::type sandbox_deserialize(T &value, const void *&data, wasm_size_t &max_size) { return sandbox_deserialize((int32_t &)value, data, max_size); } template typename std::enable_if::value && boost::is_detected::value, bool>::type sandbox_serialize(const T &value, void *&data, wasm_size_t &max_size) { return value.sandbox_serialize(data, max_size); } template typename std::enable_if::value && std::is_class::value && boost::is_detected::value, bool>::type sandbox_deserialize(T &value, const void *&data, wasm_size_t &max_size) { return value.sandbox_deserialize(data, max_size); } } #endif // MKXPZ_SANDBOX_SERIAL_UTIL_H