diff --git a/.gitmodules b/.gitmodules
index 80fb5f6c91..7039c7b1f7 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -23,10 +23,6 @@
path = 3rdparty/SPIRV/SPIRV-Headers
url = ../../KhronosGroup/SPIRV-Headers.git
ignore = dirty
-[submodule "3rdparty/cereal"]
- path = 3rdparty/cereal
- url = ../../RPCS3/cereal.git
- ignore = dirty
[submodule "3rdparty/zlib"]
path = 3rdparty/zlib/zlib
url = ../../madler/zlib
diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt
index 338d756b0b..ac213bb7d3 100644
--- a/3rdparty/CMakeLists.txt
+++ b/3rdparty/CMakeLists.txt
@@ -103,12 +103,6 @@ else()
target_include_directories(xxhash INTERFACE xxHash)
endif()
-
-# cereal
-add_library(3rdparty_cereal INTERFACE)
-target_include_directories(3rdparty_cereal INTERFACE cereal/include)
-
-
# OpenGL
# Prefer GLVND for OpenGL rather than legacy, unless it's been defined elsewhere, in the case of AppImage builds
@@ -351,7 +345,6 @@ add_library(3rdparty::yaml-cpp ALIAS yaml-cpp)
add_library(3rdparty::xxhash ALIAS xxhash)
add_library(3rdparty::hidapi ALIAS 3rdparty_hidapi)
add_library(3rdparty::libpng ALIAS ${LIBPNG_TARGET})
-add_library(3rdparty::cereal ALIAS 3rdparty_cereal)
add_library(3rdparty::opengl ALIAS 3rdparty_opengl)
add_library(3rdparty::stblib ALIAS 3rdparty_stblib)
add_library(3rdparty::discordRPC ALIAS 3rdparty_discordRPC)
diff --git a/3rdparty/cereal b/3rdparty/cereal
deleted file mode 160000
index 60c69df968..0000000000
--- a/3rdparty/cereal
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 60c69df968d1c72c998cd5f23ba34e2e3718a84b
diff --git a/buildfiles/msvc/rpcs3_default.props b/buildfiles/msvc/rpcs3_default.props
index 90a88c1b55..2ad27957d5 100644
--- a/buildfiles/msvc/rpcs3_default.props
+++ b/buildfiles/msvc/rpcs3_default.props
@@ -3,7 +3,7 @@
- .\;..\;..\3rdparty\asmjit\asmjit\src;..\3rdparty\yaml-cpp\include;..\3rdparty\ffmpeg\include;..\3rdparty\cereal\include;$(VC_IncludePath);$(WindowsSDK_IncludePath);$(UniversalCRT_IncludePath);..\3rdparty\libpng\libpng;..\3rdparty\GL;..\3rdparty\stblib\include;..\3rdparty\OpenAL\include;..\3rdparty\pugixml\src;..\3rdparty\hidapi\hidapi;..\3rdparty\Optional;..\3rdparty\xxhash
+ .\;..\;..\3rdparty\asmjit\asmjit\src;..\3rdparty\yaml-cpp\include;..\3rdparty\ffmpeg\include;$(VC_IncludePath);$(WindowsSDK_IncludePath);$(UniversalCRT_IncludePath);..\3rdparty\libpng\libpng;..\3rdparty\GL;..\3rdparty\stblib\include;..\3rdparty\OpenAL\include;..\3rdparty\pugixml\src;..\3rdparty\hidapi\hidapi;..\3rdparty\Optional;..\3rdparty\xxhash
$(SolutionDir)lib\$(Configuration)-$(Platform)\
$(SolutionDir)lib\$(Configuration)-$(Platform)\;$(UniversalCRT_LibraryPath_x64);$(LibraryPath)
$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)\
diff --git a/rpcs3/Emu/CMakeLists.txt b/rpcs3/Emu/CMakeLists.txt
index 5ff512f455..139f403c48 100644
--- a/rpcs3/Emu/CMakeLists.txt
+++ b/rpcs3/Emu/CMakeLists.txt
@@ -38,7 +38,6 @@ target_sources(rpcs3_emu PRIVATE
../util/atomic.cpp
../util/logs.cpp
../util/yaml.cpp
- ../util/cereal.cpp
../util/vm_native.cpp
../util/dyn_lib.cpp
../util/sysinfo.cpp
@@ -76,12 +75,6 @@ else()
set_source_files_properties("../util/yaml.cpp" PROPERTIES COMPILE_FLAGS -fexceptions)
endif()
-if(MSVC)
- set_source_files_properties("../util/cereal.cpp" PROPERTIES COMPILE_FLAGS /GR)
-else()
- set_source_files_properties("../util/cereal.cpp" PROPERTIES COMPILE_FLAGS -frtti)
-endif()
-
# Crypto
target_sources(rpcs3_emu PRIVATE
../Crypto/aes.cpp
@@ -499,7 +492,7 @@ endif()
target_link_libraries(rpcs3_emu
PUBLIC
- 3rdparty::ffmpeg 3rdparty::cereal
+ 3rdparty::ffmpeg
3rdparty::opengl 3rdparty::stblib
3rdparty::vulkan 3rdparty::glew
3rdparty::libusb 3rdparty::wolfssl
diff --git a/rpcs3/Emu/RSX/Capture/rsx_replay.h b/rpcs3/Emu/RSX/Capture/rsx_replay.h
index 6e0a9d14aa..8049f5b6df 100644
--- a/rpcs3/Emu/RSX/Capture/rsx_replay.h
+++ b/rpcs3/Emu/RSX/Capture/rsx_replay.h
@@ -8,35 +8,27 @@
namespace rsx
{
- constexpr u32 FRAME_CAPTURE_MAGIC = 0x52524300; // ascii 'RRC/0'
- constexpr u32 FRAME_CAPTURE_VERSION = 0x4;
+ enum : u32
+ {
+ c_fc_magic = "RRC"_u32,
+ c_fc_version = 0x5,
+ };
+
struct frame_capture_data
{
struct memory_block_data
{
std::vector data{};
-
- template
- void serialize(Archive& ar)
- {
- ar(data);
- }
};
// simple block to hold ps3 address and data
struct memory_block
{
+ static constexpr bool enable_bitcopy = true;
+
u32 offset; // Offset in rsx address space
u32 location; // rsx memory location of the block
u64 data_state;
-
- template
- void serialize(Archive & ar)
- {
- ar(offset);
- ar(location);
- ar(data_state);
- }
};
struct replay_command
@@ -45,101 +37,61 @@ namespace rsx
std::unordered_set memory_state{}; // index into memory_map for the various memory blocks that need applying before this command can run
u64 tile_state{0}; // tile state for this command
u64 display_buffer_state{0};
-
- template
- void serialize(Archive & ar)
- {
- ar(rsx_command);
- ar(memory_state);
- ar(tile_state);
- ar(display_buffer_state);
- }
};
struct tile_info
{
+ static constexpr bool enable_bitcopy = true;
+
u32 tile;
u32 limit;
u32 pitch;
u32 format;
-
- template
- void serialize(Archive & ar)
- {
- ar(tile);
- ar(limit);
- ar(pitch);
- ar(format);
- }
};
struct zcull_info
{
+ static constexpr bool enable_bitcopy = true;
+
u32 region;
u32 size;
u32 start;
u32 offset;
u32 status0;
u32 status1;
-
- template
- void serialize(Archive & ar)
- {
- ar(region);
- ar(size);
- ar(start);
- ar(offset);
- ar(status0);
- ar(status1);
- }
};
// bleh, may need to break these out, might be unnecessary to do both always
struct tile_state
{
+ static constexpr bool enable_bitcopy = true;
+
tile_info tiles[15]{};
zcull_info zculls[8]{};
-
- template
- void serialize(Archive & ar)
- {
- ar(tiles);
- ar(zculls);
- }
};
struct buffer_state
{
+ static constexpr bool enable_bitcopy = true;
+
u32 width{0};
u32 height{0};
u32 pitch{0};
u32 offset{0};
-
- template
- void serialize(Archive & ar)
- {
- ar(width);
- ar(height);
- ar(pitch);
- ar(offset);
- }
};
struct display_buffers_state
{
+ static constexpr bool enable_bitcopy = true;
+
std::array buffers{};
u32 count{0};
-
- template
- void serialize(Archive & ar)
- {
- ar(buffers);
- ar(count);
- }
};
- u32 magic;
- u32 version;
+ u32 magic = c_fc_magic;
+ u32 version = c_fc_version;
+ u32 LE_format = std::endian::little == std::endian::native;
+
// hashmap of holding various states for tile
std::unordered_map tile_map;
// hashmap of various memory 'changes' that can be applied to ps3 memory
@@ -153,23 +105,10 @@ namespace rsx
// Initial registers state at the beginning of the capture
rsx::rsx_state reg_state;
- template
- void serialize(Archive & ar)
- {
- ar(magic);
- ar(version);
- ar(tile_map);
- ar(memory_map);
- ar(memory_data_map);
- ar(display_buffers_map);
- ar(replay_commands);
- ar(reg_state);
- }
-
void reset()
{
- magic = FRAME_CAPTURE_MAGIC;
- version = FRAME_CAPTURE_VERSION;
+ magic = c_fc_magic;
+ version = c_fc_version;
tile_map.clear();
memory_map.clear();
replay_commands.clear();
diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp
index c0ad5a04c5..3521944d29 100644
--- a/rpcs3/Emu/RSX/RSXThread.cpp
+++ b/rpcs3/Emu/RSX/RSXThread.cpp
@@ -19,7 +19,7 @@
#include "Utilities/date_time.h"
#include "Utilities/StrUtil.h"
-#include "util/cereal.hpp"
+#include "util/serialization.hpp"
#include "util/asm.hpp"
#include
@@ -39,6 +39,37 @@ rsx::frame_capture_data frame_capture;
extern CellGcmOffsetTable offsetTable;
extern thread_local std::string(*g_tls_log_prefix)();
+template <>
+bool serialize(utils::serial& ar, rsx::rsx_state& o)
+{
+ return ar(o.transform_program, /*o.transform_constants,*/ o.registers);
+}
+
+template <>
+bool serialize(utils::serial& ar, rsx::frame_capture_data& o)
+{
+ ar(o.magic, o.version, o.LE_format);
+
+ if (o.magic != rsx::c_fc_magic || o.version != rsx::c_fc_version || o.LE_format != (std::endian::little == std::endian::native))
+ {
+ return false;
+ }
+
+ return ar(o.tile_map, o.memory_map, o.memory_data_map, o.display_buffers_map, o.replay_commands, o.reg_state);
+}
+
+template <>
+bool serialize(utils::serial& ar, rsx::frame_capture_data::memory_block_data& o)
+{
+ return ar(o.data);
+}
+
+template <>
+bool serialize(utils::serial& ar, rsx::frame_capture_data::replay_command& o)
+{
+ return ar(o.rsx_command, o.memory_state, o.tile_state, o.display_buffer_state);
+}
+
namespace rsx
{
std::function g_access_violation_handler;
@@ -2861,11 +2892,14 @@ namespace rsx
const std::string file_path = fs::get_config_dir() + "captures/" + Emu.GetTitleID() + "_" + date_time::current_time_narrow() + "_capture.rrc";
// todo: may want to compress this data?
- const std::string file_data = cereal_serialize(frame_capture);
+ utils::serial save_manager;
+ save_manager.reserve(0x800'0000); // 128MB
+
+ save_manager(frame_capture);
fs::pending_file temp(file_path);
- if (temp.file && (temp.file.write(file_data), temp.commit(false)))
+ if (temp.file && (temp.file.write(save_manager.data), temp.commit(false)))
{
rsx_log.success("Capture successful: %s", file_path);
}
diff --git a/rpcs3/Emu/RSX/rsx_methods.h b/rpcs3/Emu/RSX/rsx_methods.h
index 2278d8ec60..a9845c79e5 100644
--- a/rpcs3/Emu/RSX/rsx_methods.h
+++ b/rpcs3/Emu/RSX/rsx_methods.h
@@ -554,15 +554,6 @@ namespace rsx
void init();
- template
- void serialize(Archive & ar)
- {
- ar(transform_program,
-// transform_constants,
- registers
- );
- }
-
u16 viewport_width() const
{
return decode().width();
diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp
index 963ff0508e..b9b74328aa 100644
--- a/rpcs3/Emu/System.cpp
+++ b/rpcs3/Emu/System.cpp
@@ -30,7 +30,7 @@
#include "../Crypto/unself.h"
#include "util/yaml.hpp"
#include "util/logs.hpp"
-#include "util/cereal.hpp"
+#include "util/serialization.hpp"
#include
#include
@@ -380,18 +380,31 @@ bool Emulator::BootRsxCapture(const std::string& path)
}
std::unique_ptr frame = std::make_unique();
- cereal_deserialize(*frame, in_file.to_string());
+ utils::serial load_manager;
+ load_manager.set_reading_state(in_file.to_vector());
+
+ load_manager(*frame);
in_file.close();
- if (frame->magic != rsx::FRAME_CAPTURE_MAGIC)
+ if (frame->magic != rsx::c_fc_magic)
{
sys_log.error("Invalid rsx capture file!");
return false;
}
- if (frame->version != rsx::FRAME_CAPTURE_VERSION)
+ if (frame->version != rsx::c_fc_version)
{
- sys_log.error("Rsx capture file version not supported! Expected %d, found %d", rsx::FRAME_CAPTURE_VERSION, frame->version);
+ sys_log.error("Rsx capture file version not supported! Expected %d, found %d", +rsx::c_fc_version, frame->version);
+ return false;
+ }
+
+ if (frame->LE_format != (std::endian::little == std::endian::native))
+ {
+ static constexpr std::string_view machines[2]{"Big-Endian", "Little-Endian"};
+
+ sys_log.error("Rsx capture byte endianness not supported! Expected %s format, found %s format"
+ , machines[frame->LE_format ^ 1], machines[frame->LE_format]);
+
return false;
}
diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj
index 51727b08ff..402bcdce38 100644
--- a/rpcs3/emucore.vcxproj
+++ b/rpcs3/emucore.vcxproj
@@ -100,10 +100,6 @@
NotUsing
Sync
-
- NotUsing
- true
-
NotUsing
@@ -490,6 +486,7 @@
+
diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters
index 8c76ee1cec..2c6076d531 100644
--- a/rpcs3/emucore.vcxproj.filters
+++ b/rpcs3/emucore.vcxproj.filters
@@ -843,9 +843,6 @@
Utilities
-
- Utilities
-
Utilities
@@ -1962,6 +1959,9 @@
Emu\GPU\RSX\Overlays
+
+ Utilities
+
diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj
index 994b61322f..b591b510d3 100644
--- a/rpcs3/rpcs3.vcxproj
+++ b/rpcs3/rpcs3.vcxproj
@@ -62,12 +62,12 @@
true
- ..\3rdparty\7z\src;..\3rdparty\hidapi\hidapi\hidapi;.\;..\;..\3rdparty\asmjit\asmjit\src;..\3rdparty\yaml-cpp\include;..\3rdparty\ffmpeg\include;..\3rdparty\cereal\include;$(VC_IncludePath);$(WindowsSDK_IncludePath);$(UniversalCRT_IncludePath);..\3rdparty\XAudio2Redist\include;..\3rdparty\libpng\libpng;..\3rdparty\GL;..\3rdparty\stblib\include;..\3rdparty\OpenAL\include;..\3rdparty\pugixml\src;..\3rdparty\Optional;..\3rdparty\discord-rpc\include;..\3rdparty\zlib\zlib
+ ..\3rdparty\7z\src;..\3rdparty\hidapi\hidapi\hidapi;.\;..\;..\3rdparty\asmjit\asmjit\src;..\3rdparty\yaml-cpp\include;..\3rdparty\ffmpeg\include;$(VC_IncludePath);$(WindowsSDK_IncludePath);$(UniversalCRT_IncludePath);..\3rdparty\XAudio2Redist\include;..\3rdparty\libpng\libpng;..\3rdparty\GL;..\3rdparty\stblib\include;..\3rdparty\OpenAL\include;..\3rdparty\pugixml\src;..\3rdparty\Optional;..\3rdparty\discord-rpc\include;..\3rdparty\zlib\zlib
$(SolutionDir)lib\$(Configuration)-$(Platform)\;$(UniversalCRT_LibraryPath_x64);$(LibraryPath)
$(SolutionDir)lib\$(Configuration)-$(Platform)\;$(UniversalCRT_LibraryPath_x64);$(LibraryPath)
- ..\3rdparty\7z\src;..\3rdparty\hidapi\hidapi\hidapi;.\;..\;..\3rdparty\asmjit\asmjit\src;..\3rdparty\yaml-cpp\include;..\3rdparty\ffmpeg\include;..\3rdparty\cereal\include;$(VC_IncludePath);$(WindowsSDK_IncludePath);$(UniversalCRT_IncludePath);..\3rdparty\XAudio2Redist\include;..\3rdparty\libpng\libpng;..\3rdparty\GL;..\3rdparty\stblib\include;..\3rdparty\OpenAL\include;..\3rdparty\pugixml\src;..\3rdparty\Optional;..\3rdparty\discord-rpc\include;..\3rdparty\zlib\zlib
+ ..\3rdparty\7z\src;..\3rdparty\hidapi\hidapi\hidapi;.\;..\;..\3rdparty\asmjit\asmjit\src;..\3rdparty\yaml-cpp\include;..\3rdparty\ffmpeg\include;$(VC_IncludePath);$(WindowsSDK_IncludePath);$(UniversalCRT_IncludePath);..\3rdparty\XAudio2Redist\include;..\3rdparty\libpng\libpng;..\3rdparty\GL;..\3rdparty\stblib\include;..\3rdparty\OpenAL\include;..\3rdparty\pugixml\src;..\3rdparty\Optional;..\3rdparty\discord-rpc\include;..\3rdparty\zlib\zlib
diff --git a/rpcs3/util/cereal.cpp b/rpcs3/util/cereal.cpp
deleted file mode 100644
index 221f8db4f9..0000000000
--- a/rpcs3/util/cereal.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-#include "util/cereal.hpp"
-#include
-#include "Utilities/StrFmt.h"
-#include "Emu/RSX/RSXThread.h"
-#include "Emu/RSX/Capture/rsx_capture.h"
-
-#ifndef _MSC_VER
-#pragma GCC diagnostic ignored "-Weffc++"
-#endif
-
-#include "cereal/archives/binary.hpp"
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-
-namespace cereal
-{
- [[noreturn]] void throw_exception(const std::string& err)
- {
- fmt::throw_exception("%s", err);
- }
-
- [[noreturn]] void throw_exception(const char* err)
- {
- fmt::throw_exception("%s", err);
- }
-}
-
-template <>
-std::string cereal_serialize(const rsx::frame_capture_data& data)
-{
- std::ostringstream os;
- cereal::BinaryOutputArchive archive(os);
- archive(data);
- return os.str();
-}
-
-template <>
-void cereal_deserialize(rsx::frame_capture_data& data, const std::string& src)
-{
- std::istringstream is(src);
- cereal::BinaryInputArchive archive(is);
- archive(data);
-}
diff --git a/rpcs3/util/cereal.hpp b/rpcs3/util/cereal.hpp
deleted file mode 100644
index 378a119150..0000000000
--- a/rpcs3/util/cereal.hpp
+++ /dev/null
@@ -1,9 +0,0 @@
-#pragma once
-
-#include
-
-template
-std::string cereal_serialize(const T&);
-
-template
-void cereal_deserialize(T& data, const std::string& src);
diff --git a/rpcs3/util/serialization.hpp b/rpcs3/util/serialization.hpp
new file mode 100644
index 0000000000..8446dc8d66
--- /dev/null
+++ b/rpcs3/util/serialization.hpp
@@ -0,0 +1,346 @@
+#pragma once
+
+#include "util/types.hpp"
+#include
+
+namespace stx
+{
+ template
+ struct exact_t
+ {
+ T obj;
+
+ exact_t(T&& _obj) : obj(std::forward(_obj)) {}
+
+ // TODO: More conversions
+ template requires (std::is_same_v)
+ operator U&() const { return obj; };
+ };
+}
+
+namespace utils
+{
+ template
+ concept FastRandomAccess = requires (T& obj)
+ {
+ std::data(obj)[0];
+ };
+
+ template
+ concept Reservable = requires (T& obj)
+ {
+ obj.reserve(0);
+ };
+
+ template
+ concept Bitcopy = (std::is_arithmetic_v) || (std::is_enum_v) || requires (T& obj)
+ {
+ std::enable_if_t(std::remove_cv_t::enable_bitcopy)>();
+ };
+
+ template
+ concept TupleAlike = requires ()
+ {
+ std::tuple_size>::value;
+ };
+
+ template
+ concept ListAlike = requires (T& obj) { obj.insert(obj.end(), std::declval()); };
+
+ struct serial
+ {
+ std::vector data;
+ usz pos = umax;
+
+ // Checks if this strcuture is currently used for serialization
+ bool is_writing() const
+ {
+ return pos == umax;
+ }
+
+ // Reserve memory for serialization
+ void reserve(usz size)
+ {
+ // Is a NO-OP for deserialization in order to allow usage from serialization specializations regardless
+ if (is_writing())
+ {
+ data.reserve(data.size() + size);
+ }
+ }
+
+ bool raw_serialize(const void* ptr, usz size)
+ {
+ if (is_writing())
+ {
+ data.insert(data.end(), static_cast(ptr), static_cast(ptr) + size);
+ return true;
+ }
+
+ ensure(data.size() - pos >= size);
+ std::memcpy(const_cast(ptr), data.data() + pos, size);
+ pos += size;
+ return true;
+ }
+
+ // (De)serialization function
+ template
+ bool serialize(T& obj)
+ {
+ // Fallback to global overload
+ return ::serialize(*this, obj);
+ }
+
+ // Enabled for fundamental types, enumerations and if specfied explicitly that type can be saved in pure bitwise manner
+ template requires Bitcopy
+ bool serialize(T& obj)
+ {
+ return raw_serialize(std::addressof(obj), sizeof(obj));
+ }
+
+ // std::vector, std::basic_string
+ template requires FastRandomAccess && ListAlike
+ bool serialize(T& obj)
+ {
+ if (is_writing())
+ {
+ for (usz i = obj.size();;)
+ {
+ const usz i_old = std::exchange(i, i >> 7);
+ operator()(static_cast(i_old % 0x80) | (u8{!!i} << 7));
+ if (!i)
+ break;
+ }
+
+ if constexpr (std::is_trivially_copyable_v::value_type>)
+ {
+ raw_serialize(obj.data(), sizeof(obj[0]) * obj.size());
+ }
+ else
+ {
+ for (auto&& value : obj)
+ {
+ if (!serialize(value))
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ obj.clear();
+
+ usz size = 0;
+
+ for (u32 i = 0;; i += 7)
+ {
+ u8 byte_data = 0;
+
+ if (!raw_serialize(&byte_data, 1))
+ {
+ return false;
+ }
+
+ size |= static_cast(byte_data % 0x80) << i;
+
+ if (!(byte_data >> 7))
+ {
+ break;
+ }
+ }
+
+ obj.resize(size);
+
+ if constexpr (std::is_trivially_copyable_v)
+ {
+ if (!raw_serialize(obj.data(), sizeof(obj[0]) * size))
+ {
+ obj.clear();
+ return false;
+ }
+ }
+ else
+ {
+ for (auto&& value : obj)
+ {
+ if (!serialize(value))
+ {
+ obj.clear();
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ // C-array, std::array, std::span
+ template requires FastRandomAccess && (!ListAlike) && (!Bitcopy)
+ bool serialize(T& obj)
+ {
+ if constexpr (std::is_trivially_copyable_v()[0])>>)
+ {
+ return raw_serialize(std::data(obj), sizeof(obj[0]) * std::size(obj));
+ }
+ else
+ {
+ for (auto&& value : obj)
+ {
+ if (!serialize(value))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
+
+ // std::deque, std::list, std::(unordered_)set, std::(unordered_)map, std::(unordered_)multiset, std::(unordered_)multimap
+ template requires (!FastRandomAccess) && ListAlike
+ bool serialize(T& obj)
+ {
+ if (is_writing())
+ {
+ for (usz i = obj.size();;)
+ {
+ const usz i_old = std::exchange(i, i >> 7);
+ operator()(static_cast(i_old % 0x80) | (u8{!!i} << 7));
+ if (!i)
+ break;
+ }
+
+ for (auto&& value : obj)
+ {
+ if (!serialize(value))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ obj.clear();
+
+ usz size = 0;
+
+ for (u32 i = 0;; i += 7)
+ {
+ u8 byte_data = 0;
+
+ if (!raw_serialize(&byte_data, 1))
+ {
+ return false;
+ }
+
+ size |= static_cast(byte_data % 0x80) << i;
+
+ if (!(byte_data >> 7))
+ {
+ break;
+ }
+ }
+
+ if constexpr (Reservable)
+ {
+ obj.reserve(size);
+ }
+
+ for (usz i = 0; i < size; i++)
+ {
+ obj.insert(obj.end(), static_cast(*this));
+
+ if (!is_valid())
+ {
+ obj.clear();
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ template
+ bool serialize_tuple(T& obj)
+ {
+ const bool res = serialize(std::get(obj));
+ constexpr usz next_i = std::min(i + 1, std::tuple_size_v - 1);
+
+ if constexpr (next_i == i)
+ {
+ return res;
+ }
+ else
+ {
+ return res && serialize_tuple(obj);
+ }
+ }
+
+ // std::pair, std::tuple
+ template requires TupleAlike && (!FastRandomAccess)
+ bool serialize(T& obj)
+ {
+ return serialize_tuple(obj);
+ }
+
+ // Wrapper for serialize(T&), allows to pass multiple objects at once
+ template
+ bool operator()(Args&&... args)
+ {
+ return ((AUDIT(!std::is_const_v> || is_writing())
+ , serialize(const_cast(static_cast(args)))), ...);
+ }
+
+ // Convert serialization manager to deserializion manager (can't go the other way)
+ // If no arg provided reuse saved buffer
+ void set_reading_state(std::vector&& _data = std::vector{})
+ {
+ if (!_data.empty())
+ {
+ data = std::move(_data);
+ }
+
+ pos = 0;
+ }
+
+ template requires (std::is_constructible_v> || Bitcopy> ||
+ std::is_constructible_v, stx::exact_t> || TupleAlike>)
+ operator T()
+ {
+ AUDIT(!is_writing());
+
+ using type = std::remove_const_t;
+
+ if constexpr (Bitcopy)
+ {
+ u8 buf[sizeof(type)]{};
+ ensure(raw_serialize(buf, sizeof(buf)));
+ return std::bit_cast(buf);
+ }
+ else if constexpr (std::is_constructible_v>)
+ {
+ return type(stx::exact_t(*this));
+ }
+ else if constexpr (std::is_constructible_v)
+ {
+ type value{};
+ ensure(serialize(value));
+ return value;
+ }
+ else if constexpr (TupleAlike)
+ {
+ static_assert(std::tuple_size_v == 2, "Unimplemented tuple serialization!");
+ return type{ operator std::remove_cvref_t(std::declval()))>
+ , operator std::remove_cvref_t(std::declval()))> };
+ }
+ }
+
+ // Returns true if writable or readable and valid
+ bool is_valid() const
+ {
+ return is_writing() || pos < data.size();
+ }
+ };
+}
diff --git a/rpcs3/util/types.hpp b/rpcs3/util/types.hpp
index 24639e0208..b7f698cac6 100644
--- a/rpcs3/util/types.hpp
+++ b/rpcs3/util/types.hpp
@@ -1070,3 +1070,11 @@ constexpr bool is_same_ptr(const volatile Y* ptr)
template
concept PtrSame = (is_same_ptr());
+
+namespace utils
+{
+ struct serial;
+}
+
+template
+extern bool serialize(utils::serial& ar, T& obj);