mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-22 02:32:36 +01:00
Implement serialization.hpp, remove cereal submodule
Bump RSX capture version, use new serializer.
This commit is contained in:
parent
ddbe496097
commit
2169e8d935
4
.gitmodules
vendored
4
.gitmodules
vendored
@ -23,10 +23,6 @@
|
|||||||
path = 3rdparty/SPIRV/SPIRV-Headers
|
path = 3rdparty/SPIRV/SPIRV-Headers
|
||||||
url = ../../KhronosGroup/SPIRV-Headers.git
|
url = ../../KhronosGroup/SPIRV-Headers.git
|
||||||
ignore = dirty
|
ignore = dirty
|
||||||
[submodule "3rdparty/cereal"]
|
|
||||||
path = 3rdparty/cereal
|
|
||||||
url = ../../RPCS3/cereal.git
|
|
||||||
ignore = dirty
|
|
||||||
[submodule "3rdparty/zlib"]
|
[submodule "3rdparty/zlib"]
|
||||||
path = 3rdparty/zlib/zlib
|
path = 3rdparty/zlib/zlib
|
||||||
url = ../../madler/zlib
|
url = ../../madler/zlib
|
||||||
|
7
3rdparty/CMakeLists.txt
vendored
7
3rdparty/CMakeLists.txt
vendored
@ -103,12 +103,6 @@ else()
|
|||||||
target_include_directories(xxhash INTERFACE xxHash)
|
target_include_directories(xxhash INTERFACE xxHash)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
# cereal
|
|
||||||
add_library(3rdparty_cereal INTERFACE)
|
|
||||||
target_include_directories(3rdparty_cereal INTERFACE cereal/include)
|
|
||||||
|
|
||||||
|
|
||||||
# OpenGL
|
# OpenGL
|
||||||
|
|
||||||
# Prefer GLVND for OpenGL rather than legacy, unless it's been defined elsewhere, in the case of AppImage builds
|
# 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::xxhash ALIAS xxhash)
|
||||||
add_library(3rdparty::hidapi ALIAS 3rdparty_hidapi)
|
add_library(3rdparty::hidapi ALIAS 3rdparty_hidapi)
|
||||||
add_library(3rdparty::libpng ALIAS ${LIBPNG_TARGET})
|
add_library(3rdparty::libpng ALIAS ${LIBPNG_TARGET})
|
||||||
add_library(3rdparty::cereal ALIAS 3rdparty_cereal)
|
|
||||||
add_library(3rdparty::opengl ALIAS 3rdparty_opengl)
|
add_library(3rdparty::opengl ALIAS 3rdparty_opengl)
|
||||||
add_library(3rdparty::stblib ALIAS 3rdparty_stblib)
|
add_library(3rdparty::stblib ALIAS 3rdparty_stblib)
|
||||||
add_library(3rdparty::discordRPC ALIAS 3rdparty_discordRPC)
|
add_library(3rdparty::discordRPC ALIAS 3rdparty_discordRPC)
|
||||||
|
1
3rdparty/cereal
vendored
1
3rdparty/cereal
vendored
@ -1 +0,0 @@
|
|||||||
Subproject commit 60c69df968d1c72c998cd5f23ba34e2e3718a84b
|
|
@ -3,7 +3,7 @@
|
|||||||
<ImportGroup Label="PropertySheets" />
|
<ImportGroup Label="PropertySheets" />
|
||||||
<PropertyGroup Label="UserMacros" />
|
<PropertyGroup Label="UserMacros" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<IncludePath>.\;..\;..\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</IncludePath>
|
<IncludePath>.\;..\;..\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</IncludePath>
|
||||||
<OutDir>$(SolutionDir)lib\$(Configuration)-$(Platform)\</OutDir>
|
<OutDir>$(SolutionDir)lib\$(Configuration)-$(Platform)\</OutDir>
|
||||||
<LibraryPath>$(SolutionDir)lib\$(Configuration)-$(Platform)\;$(UniversalCRT_LibraryPath_x64);$(LibraryPath)</LibraryPath>
|
<LibraryPath>$(SolutionDir)lib\$(Configuration)-$(Platform)\;$(UniversalCRT_LibraryPath_x64);$(LibraryPath)</LibraryPath>
|
||||||
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)\</IntDir>
|
<IntDir>$(SolutionDir)tmp\$(ProjectName)-$(Configuration)-$(Platform)\</IntDir>
|
||||||
|
@ -38,7 +38,6 @@ target_sources(rpcs3_emu PRIVATE
|
|||||||
../util/atomic.cpp
|
../util/atomic.cpp
|
||||||
../util/logs.cpp
|
../util/logs.cpp
|
||||||
../util/yaml.cpp
|
../util/yaml.cpp
|
||||||
../util/cereal.cpp
|
|
||||||
../util/vm_native.cpp
|
../util/vm_native.cpp
|
||||||
../util/dyn_lib.cpp
|
../util/dyn_lib.cpp
|
||||||
../util/sysinfo.cpp
|
../util/sysinfo.cpp
|
||||||
@ -76,12 +75,6 @@ else()
|
|||||||
set_source_files_properties("../util/yaml.cpp" PROPERTIES COMPILE_FLAGS -fexceptions)
|
set_source_files_properties("../util/yaml.cpp" PROPERTIES COMPILE_FLAGS -fexceptions)
|
||||||
endif()
|
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
|
# Crypto
|
||||||
target_sources(rpcs3_emu PRIVATE
|
target_sources(rpcs3_emu PRIVATE
|
||||||
../Crypto/aes.cpp
|
../Crypto/aes.cpp
|
||||||
@ -499,7 +492,7 @@ endif()
|
|||||||
|
|
||||||
target_link_libraries(rpcs3_emu
|
target_link_libraries(rpcs3_emu
|
||||||
PUBLIC
|
PUBLIC
|
||||||
3rdparty::ffmpeg 3rdparty::cereal
|
3rdparty::ffmpeg
|
||||||
3rdparty::opengl 3rdparty::stblib
|
3rdparty::opengl 3rdparty::stblib
|
||||||
3rdparty::vulkan 3rdparty::glew
|
3rdparty::vulkan 3rdparty::glew
|
||||||
3rdparty::libusb 3rdparty::wolfssl
|
3rdparty::libusb 3rdparty::wolfssl
|
||||||
|
@ -8,35 +8,27 @@
|
|||||||
|
|
||||||
namespace rsx
|
namespace rsx
|
||||||
{
|
{
|
||||||
constexpr u32 FRAME_CAPTURE_MAGIC = 0x52524300; // ascii 'RRC/0'
|
enum : u32
|
||||||
constexpr u32 FRAME_CAPTURE_VERSION = 0x4;
|
{
|
||||||
|
c_fc_magic = "RRC"_u32,
|
||||||
|
c_fc_version = 0x5,
|
||||||
|
};
|
||||||
|
|
||||||
struct frame_capture_data
|
struct frame_capture_data
|
||||||
{
|
{
|
||||||
struct memory_block_data
|
struct memory_block_data
|
||||||
{
|
{
|
||||||
std::vector<u8> data{};
|
std::vector<u8> data{};
|
||||||
|
|
||||||
template<typename Archive>
|
|
||||||
void serialize(Archive& ar)
|
|
||||||
{
|
|
||||||
ar(data);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// simple block to hold ps3 address and data
|
// simple block to hold ps3 address and data
|
||||||
struct memory_block
|
struct memory_block
|
||||||
{
|
{
|
||||||
|
static constexpr bool enable_bitcopy = true;
|
||||||
|
|
||||||
u32 offset; // Offset in rsx address space
|
u32 offset; // Offset in rsx address space
|
||||||
u32 location; // rsx memory location of the block
|
u32 location; // rsx memory location of the block
|
||||||
u64 data_state;
|
u64 data_state;
|
||||||
|
|
||||||
template<typename Archive>
|
|
||||||
void serialize(Archive & ar)
|
|
||||||
{
|
|
||||||
ar(offset);
|
|
||||||
ar(location);
|
|
||||||
ar(data_state);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct replay_command
|
struct replay_command
|
||||||
@ -45,101 +37,61 @@ namespace rsx
|
|||||||
std::unordered_set<u64> memory_state{}; // index into memory_map for the various memory blocks that need applying before this command can run
|
std::unordered_set<u64> 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 tile_state{0}; // tile state for this command
|
||||||
u64 display_buffer_state{0};
|
u64 display_buffer_state{0};
|
||||||
|
|
||||||
template<typename Archive>
|
|
||||||
void serialize(Archive & ar)
|
|
||||||
{
|
|
||||||
ar(rsx_command);
|
|
||||||
ar(memory_state);
|
|
||||||
ar(tile_state);
|
|
||||||
ar(display_buffer_state);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct tile_info
|
struct tile_info
|
||||||
{
|
{
|
||||||
|
static constexpr bool enable_bitcopy = true;
|
||||||
|
|
||||||
u32 tile;
|
u32 tile;
|
||||||
u32 limit;
|
u32 limit;
|
||||||
u32 pitch;
|
u32 pitch;
|
||||||
u32 format;
|
u32 format;
|
||||||
|
|
||||||
template<typename Archive>
|
|
||||||
void serialize(Archive & ar)
|
|
||||||
{
|
|
||||||
ar(tile);
|
|
||||||
ar(limit);
|
|
||||||
ar(pitch);
|
|
||||||
ar(format);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct zcull_info
|
struct zcull_info
|
||||||
{
|
{
|
||||||
|
static constexpr bool enable_bitcopy = true;
|
||||||
|
|
||||||
u32 region;
|
u32 region;
|
||||||
u32 size;
|
u32 size;
|
||||||
u32 start;
|
u32 start;
|
||||||
u32 offset;
|
u32 offset;
|
||||||
u32 status0;
|
u32 status0;
|
||||||
u32 status1;
|
u32 status1;
|
||||||
|
|
||||||
template<typename Archive>
|
|
||||||
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
|
// bleh, may need to break these out, might be unnecessary to do both always
|
||||||
struct tile_state
|
struct tile_state
|
||||||
{
|
{
|
||||||
|
static constexpr bool enable_bitcopy = true;
|
||||||
|
|
||||||
tile_info tiles[15]{};
|
tile_info tiles[15]{};
|
||||||
zcull_info zculls[8]{};
|
zcull_info zculls[8]{};
|
||||||
|
|
||||||
template<typename Archive>
|
|
||||||
void serialize(Archive & ar)
|
|
||||||
{
|
|
||||||
ar(tiles);
|
|
||||||
ar(zculls);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct buffer_state
|
struct buffer_state
|
||||||
{
|
{
|
||||||
|
static constexpr bool enable_bitcopy = true;
|
||||||
|
|
||||||
u32 width{0};
|
u32 width{0};
|
||||||
u32 height{0};
|
u32 height{0};
|
||||||
u32 pitch{0};
|
u32 pitch{0};
|
||||||
u32 offset{0};
|
u32 offset{0};
|
||||||
|
|
||||||
template<typename Archive>
|
|
||||||
void serialize(Archive & ar)
|
|
||||||
{
|
|
||||||
ar(width);
|
|
||||||
ar(height);
|
|
||||||
ar(pitch);
|
|
||||||
ar(offset);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct display_buffers_state
|
struct display_buffers_state
|
||||||
{
|
{
|
||||||
|
static constexpr bool enable_bitcopy = true;
|
||||||
|
|
||||||
std::array<buffer_state, 8> buffers{};
|
std::array<buffer_state, 8> buffers{};
|
||||||
u32 count{0};
|
u32 count{0};
|
||||||
|
|
||||||
template<typename Archive>
|
|
||||||
void serialize(Archive & ar)
|
|
||||||
{
|
|
||||||
ar(buffers);
|
|
||||||
ar(count);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
u32 magic;
|
u32 magic = c_fc_magic;
|
||||||
u32 version;
|
u32 version = c_fc_version;
|
||||||
|
u32 LE_format = std::endian::little == std::endian::native;
|
||||||
|
|
||||||
// hashmap of holding various states for tile
|
// hashmap of holding various states for tile
|
||||||
std::unordered_map<u64, tile_state> tile_map;
|
std::unordered_map<u64, tile_state> tile_map;
|
||||||
// hashmap of various memory 'changes' that can be applied to ps3 memory
|
// 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
|
// Initial registers state at the beginning of the capture
|
||||||
rsx::rsx_state reg_state;
|
rsx::rsx_state reg_state;
|
||||||
|
|
||||||
template<typename Archive>
|
|
||||||
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()
|
void reset()
|
||||||
{
|
{
|
||||||
magic = FRAME_CAPTURE_MAGIC;
|
magic = c_fc_magic;
|
||||||
version = FRAME_CAPTURE_VERSION;
|
version = c_fc_version;
|
||||||
tile_map.clear();
|
tile_map.clear();
|
||||||
memory_map.clear();
|
memory_map.clear();
|
||||||
replay_commands.clear();
|
replay_commands.clear();
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
#include "Utilities/date_time.h"
|
#include "Utilities/date_time.h"
|
||||||
#include "Utilities/StrUtil.h"
|
#include "Utilities/StrUtil.h"
|
||||||
|
|
||||||
#include "util/cereal.hpp"
|
#include "util/serialization.hpp"
|
||||||
#include "util/asm.hpp"
|
#include "util/asm.hpp"
|
||||||
|
|
||||||
#include <span>
|
#include <span>
|
||||||
@ -39,6 +39,37 @@ rsx::frame_capture_data frame_capture;
|
|||||||
extern CellGcmOffsetTable offsetTable;
|
extern CellGcmOffsetTable offsetTable;
|
||||||
extern thread_local std::string(*g_tls_log_prefix)();
|
extern thread_local std::string(*g_tls_log_prefix)();
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool serialize<rsx::rsx_state>(utils::serial& ar, rsx::rsx_state& o)
|
||||||
|
{
|
||||||
|
return ar(o.transform_program, /*o.transform_constants,*/ o.registers);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool serialize<rsx::frame_capture_data>(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<rsx::frame_capture_data::memory_block_data>(utils::serial& ar, rsx::frame_capture_data::memory_block_data& o)
|
||||||
|
{
|
||||||
|
return ar(o.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool serialize<rsx::frame_capture_data::replay_command>(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
|
namespace rsx
|
||||||
{
|
{
|
||||||
std::function<bool(u32 addr, bool is_writing)> g_access_violation_handler;
|
std::function<bool(u32 addr, bool is_writing)> 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";
|
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?
|
// 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);
|
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);
|
rsx_log.success("Capture successful: %s", file_path);
|
||||||
}
|
}
|
||||||
|
@ -554,15 +554,6 @@ namespace rsx
|
|||||||
|
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
template<typename Archive>
|
|
||||||
void serialize(Archive & ar)
|
|
||||||
{
|
|
||||||
ar(transform_program,
|
|
||||||
// transform_constants,
|
|
||||||
registers
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
u16 viewport_width() const
|
u16 viewport_width() const
|
||||||
{
|
{
|
||||||
return decode<NV4097_SET_VIEWPORT_HORIZONTAL>().width();
|
return decode<NV4097_SET_VIEWPORT_HORIZONTAL>().width();
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
#include "../Crypto/unself.h"
|
#include "../Crypto/unself.h"
|
||||||
#include "util/yaml.hpp"
|
#include "util/yaml.hpp"
|
||||||
#include "util/logs.hpp"
|
#include "util/logs.hpp"
|
||||||
#include "util/cereal.hpp"
|
#include "util/serialization.hpp"
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -380,18 +380,31 @@ bool Emulator::BootRsxCapture(const std::string& path)
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<rsx::frame_capture_data> frame = std::make_unique<rsx::frame_capture_data>();
|
std::unique_ptr<rsx::frame_capture_data> frame = std::make_unique<rsx::frame_capture_data>();
|
||||||
cereal_deserialize(*frame, in_file.to_string());
|
utils::serial load_manager;
|
||||||
|
load_manager.set_reading_state(in_file.to_vector<u8>());
|
||||||
|
|
||||||
|
load_manager(*frame);
|
||||||
in_file.close();
|
in_file.close();
|
||||||
|
|
||||||
if (frame->magic != rsx::FRAME_CAPTURE_MAGIC)
|
if (frame->magic != rsx::c_fc_magic)
|
||||||
{
|
{
|
||||||
sys_log.error("Invalid rsx capture file!");
|
sys_log.error("Invalid rsx capture file!");
|
||||||
return false;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,10 +100,6 @@
|
|||||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||||
<ExceptionHandling>Sync</ExceptionHandling>
|
<ExceptionHandling>Sync</ExceptionHandling>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="util\cereal.cpp">
|
|
||||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
|
||||||
<RuntimeTypeInfo>true</RuntimeTypeInfo>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="..\Utilities\bin_patch.cpp">
|
<ClCompile Include="..\Utilities\bin_patch.cpp">
|
||||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
@ -490,6 +486,7 @@
|
|||||||
<ClInclude Include="Emu\system_config_types.h" />
|
<ClInclude Include="Emu\system_config_types.h" />
|
||||||
<ClInclude Include="Loader\mself.hpp" />
|
<ClInclude Include="Loader\mself.hpp" />
|
||||||
<ClInclude Include="util\atomic.hpp" />
|
<ClInclude Include="util\atomic.hpp" />
|
||||||
|
<ClInclude Include="util\serialization.hpp" />
|
||||||
<ClInclude Include="util\v128.hpp" />
|
<ClInclude Include="util\v128.hpp" />
|
||||||
<ClInclude Include="util\v128sse.hpp" />
|
<ClInclude Include="util\v128sse.hpp" />
|
||||||
<ClInclude Include="util\to_endian.hpp" />
|
<ClInclude Include="util\to_endian.hpp" />
|
||||||
|
@ -843,9 +843,6 @@
|
|||||||
<ClCompile Include="util\yaml.cpp">
|
<ClCompile Include="util\yaml.cpp">
|
||||||
<Filter>Utilities</Filter>
|
<Filter>Utilities</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="util\cereal.cpp">
|
|
||||||
<Filter>Utilities</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="util\cpu_stats.cpp">
|
<ClCompile Include="util\cpu_stats.cpp">
|
||||||
<Filter>Utilities</Filter>
|
<Filter>Utilities</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
@ -1962,6 +1959,9 @@
|
|||||||
<ClInclude Include="Emu\RSX\Overlays\overlay_progress_bar.hpp">
|
<ClInclude Include="Emu\RSX\Overlays\overlay_progress_bar.hpp">
|
||||||
<Filter>Emu\GPU\RSX\Overlays</Filter>
|
<Filter>Emu\GPU\RSX\Overlays</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="util\serialization.hpp">
|
||||||
|
<Filter>Utilities</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="Emu\RSX\Common\Interpreter\FragmentInterpreter.glsl">
|
<None Include="Emu\RSX\Common\Interpreter\FragmentInterpreter.glsl">
|
||||||
|
@ -62,12 +62,12 @@
|
|||||||
<IgnoreImportLibrary Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</IgnoreImportLibrary>
|
<IgnoreImportLibrary Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</IgnoreImportLibrary>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
<IncludePath>..\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</IncludePath>
|
<IncludePath>..\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</IncludePath>
|
||||||
<LibraryPath>$(SolutionDir)lib\$(Configuration)-$(Platform)\;$(UniversalCRT_LibraryPath_x64);$(LibraryPath)</LibraryPath>
|
<LibraryPath>$(SolutionDir)lib\$(Configuration)-$(Platform)\;$(UniversalCRT_LibraryPath_x64);$(LibraryPath)</LibraryPath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
<LibraryPath>$(SolutionDir)lib\$(Configuration)-$(Platform)\;$(UniversalCRT_LibraryPath_x64);$(LibraryPath)</LibraryPath>
|
<LibraryPath>$(SolutionDir)lib\$(Configuration)-$(Platform)\;$(UniversalCRT_LibraryPath_x64);$(LibraryPath)</LibraryPath>
|
||||||
<IncludePath>..\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</IncludePath>
|
<IncludePath>..\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</IncludePath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
|
@ -1,49 +0,0 @@
|
|||||||
#include "util/cereal.hpp"
|
|
||||||
#include <string>
|
|
||||||
#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 <cereal/types/vector.hpp>
|
|
||||||
#include <cereal/types/array.hpp>
|
|
||||||
#include <cereal/types/string.hpp>
|
|
||||||
#include <cereal/types/utility.hpp>
|
|
||||||
#include <cereal/types/unordered_set.hpp>
|
|
||||||
#include <cereal/types/unordered_map.hpp>
|
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
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<rsx::frame_capture_data>(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>(rsx::frame_capture_data& data, const std::string& src)
|
|
||||||
{
|
|
||||||
std::istringstream is(src);
|
|
||||||
cereal::BinaryInputArchive archive(is);
|
|
||||||
archive(data);
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
std::string cereal_serialize(const T&);
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void cereal_deserialize(T& data, const std::string& src);
|
|
346
rpcs3/util/serialization.hpp
Normal file
346
rpcs3/util/serialization.hpp
Normal file
@ -0,0 +1,346 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "util/types.hpp"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace stx
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
struct exact_t
|
||||||
|
{
|
||||||
|
T obj;
|
||||||
|
|
||||||
|
exact_t(T&& _obj) : obj(std::forward<T>(_obj)) {}
|
||||||
|
|
||||||
|
// TODO: More conversions
|
||||||
|
template <typename U> requires (std::is_same_v<U&, T>)
|
||||||
|
operator U&() const { return obj; };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace utils
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
concept FastRandomAccess = requires (T& obj)
|
||||||
|
{
|
||||||
|
std::data(obj)[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept Reservable = requires (T& obj)
|
||||||
|
{
|
||||||
|
obj.reserve(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept Bitcopy = (std::is_arithmetic_v<T>) || (std::is_enum_v<T>) || requires (T& obj)
|
||||||
|
{
|
||||||
|
std::enable_if_t<static_cast<bool>(std::remove_cv_t<T>::enable_bitcopy)>();
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept TupleAlike = requires ()
|
||||||
|
{
|
||||||
|
std::tuple_size<std::remove_cv_t<T>>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept ListAlike = requires (T& obj) { obj.insert(obj.end(), std::declval<typename T::value_type>()); };
|
||||||
|
|
||||||
|
struct serial
|
||||||
|
{
|
||||||
|
std::vector<u8> 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<const u8*>(ptr), static_cast<const u8*>(ptr) + size);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure(data.size() - pos >= size);
|
||||||
|
std::memcpy(const_cast<void*>(ptr), data.data() + pos, size);
|
||||||
|
pos += size;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// (De)serialization function
|
||||||
|
template <typename T>
|
||||||
|
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 <typename T> requires Bitcopy<T>
|
||||||
|
bool serialize(T& obj)
|
||||||
|
{
|
||||||
|
return raw_serialize(std::addressof(obj), sizeof(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
// std::vector, std::basic_string
|
||||||
|
template <typename T> requires FastRandomAccess<T> && ListAlike<T>
|
||||||
|
bool serialize(T& obj)
|
||||||
|
{
|
||||||
|
if (is_writing())
|
||||||
|
{
|
||||||
|
for (usz i = obj.size();;)
|
||||||
|
{
|
||||||
|
const usz i_old = std::exchange(i, i >> 7);
|
||||||
|
operator()<u8>(static_cast<u8>(i_old % 0x80) | (u8{!!i} << 7));
|
||||||
|
if (!i)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (std::is_trivially_copyable_v<typename std::remove_reference_t<T>::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<usz>(byte_data % 0x80) << i;
|
||||||
|
|
||||||
|
if (!(byte_data >> 7))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.resize(size);
|
||||||
|
|
||||||
|
if constexpr (std::is_trivially_copyable_v<typename T::value_type>)
|
||||||
|
{
|
||||||
|
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 <typename T> requires FastRandomAccess<T> && (!ListAlike<T>) && (!Bitcopy<T>)
|
||||||
|
bool serialize(T& obj)
|
||||||
|
{
|
||||||
|
if constexpr (std::is_trivially_copyable_v<std::remove_reference_t<decltype(std::declval<T>()[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 <typename T> requires (!FastRandomAccess<T>) && ListAlike<T>
|
||||||
|
bool serialize(T& obj)
|
||||||
|
{
|
||||||
|
if (is_writing())
|
||||||
|
{
|
||||||
|
for (usz i = obj.size();;)
|
||||||
|
{
|
||||||
|
const usz i_old = std::exchange(i, i >> 7);
|
||||||
|
operator()<u8>(static_cast<u8>(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<usz>(byte_data % 0x80) << i;
|
||||||
|
|
||||||
|
if (!(byte_data >> 7))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (Reservable<T>)
|
||||||
|
{
|
||||||
|
obj.reserve(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (usz i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
obj.insert(obj.end(), static_cast<typename T::value_type>(*this));
|
||||||
|
|
||||||
|
if (!is_valid())
|
||||||
|
{
|
||||||
|
obj.clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <usz i = 0, typename T>
|
||||||
|
bool serialize_tuple(T& obj)
|
||||||
|
{
|
||||||
|
const bool res = serialize(std::get<i>(obj));
|
||||||
|
constexpr usz next_i = std::min<usz>(i + 1, std::tuple_size_v<T> - 1);
|
||||||
|
|
||||||
|
if constexpr (next_i == i)
|
||||||
|
{
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return res && serialize_tuple<next_i>(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// std::pair, std::tuple
|
||||||
|
template <typename T> requires TupleAlike<T> && (!FastRandomAccess<T>)
|
||||||
|
bool serialize(T& obj)
|
||||||
|
{
|
||||||
|
return serialize_tuple(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrapper for serialize(T&), allows to pass multiple objects at once
|
||||||
|
template <typename... Args>
|
||||||
|
bool operator()(Args&&... args)
|
||||||
|
{
|
||||||
|
return ((AUDIT(!std::is_const_v<std::remove_reference_t<Args>> || is_writing())
|
||||||
|
, serialize(const_cast<Args&>(static_cast<const Args&>(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<u8>&& _data = std::vector<u8>{})
|
||||||
|
{
|
||||||
|
if (!_data.empty())
|
||||||
|
{
|
||||||
|
data = std::move(_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> requires (std::is_constructible_v<std::remove_const_t<T>> || Bitcopy<std::remove_const_t<T>> ||
|
||||||
|
std::is_constructible_v<std::remove_const_t<T>, stx::exact_t<serial&>> || TupleAlike<std::remove_const_t<T>>)
|
||||||
|
operator T()
|
||||||
|
{
|
||||||
|
AUDIT(!is_writing());
|
||||||
|
|
||||||
|
using type = std::remove_const_t<T>;
|
||||||
|
|
||||||
|
if constexpr (Bitcopy<T>)
|
||||||
|
{
|
||||||
|
u8 buf[sizeof(type)]{};
|
||||||
|
ensure(raw_serialize(buf, sizeof(buf)));
|
||||||
|
return std::bit_cast<type>(buf);
|
||||||
|
}
|
||||||
|
else if constexpr (std::is_constructible_v<type, stx::exact_t<serial&>>)
|
||||||
|
{
|
||||||
|
return type(stx::exact_t<serial&>(*this));
|
||||||
|
}
|
||||||
|
else if constexpr (std::is_constructible_v<type>)
|
||||||
|
{
|
||||||
|
type value{};
|
||||||
|
ensure(serialize(value));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
else if constexpr (TupleAlike<T>)
|
||||||
|
{
|
||||||
|
static_assert(std::tuple_size_v<type> == 2, "Unimplemented tuple serialization!");
|
||||||
|
return type{ operator std::remove_cvref_t<decltype(std::get<0>(std::declval<type&>()))>
|
||||||
|
, operator std::remove_cvref_t<decltype(std::get<1>(std::declval<type&>()))> };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if writable or readable and valid
|
||||||
|
bool is_valid() const
|
||||||
|
{
|
||||||
|
return is_writing() || pos < data.size();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -1070,3 +1070,11 @@ constexpr bool is_same_ptr(const volatile Y* ptr)
|
|||||||
|
|
||||||
template <typename X, typename Y>
|
template <typename X, typename Y>
|
||||||
concept PtrSame = (is_same_ptr<X, Y>());
|
concept PtrSame = (is_same_ptr<X, Y>());
|
||||||
|
|
||||||
|
namespace utils
|
||||||
|
{
|
||||||
|
struct serial;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
extern bool serialize(utils::serial& ar, T& obj);
|
||||||
|
Loading…
Reference in New Issue
Block a user