// Copyright 2008 Dolphin Emulator Project // Licensed under GPLv2+ // Refer to the license.txt file included. #pragma once // Extremely simple serialization framework. // (mis)-features: // + Super fast // + Very simple // + Same code is used for serialization and deserializaition (in most cases) // - Zero backwards/forwards compatibility // - Serialization code for anything complex has to be manually written. #include #include #include #include #include #include #include #include #include #include #include #include #include "Common/Assert.h" #include "Common/CommonTypes.h" #include "Common/Flag.h" #include "Common/Inline.h" #include "Common/Logging/Log.h" // XXX: Replace this with std::is_trivially_copyable once we stop using volatile // on things that are put in savestates, as volatile types are not trivially copyable. template constexpr bool IsTriviallyCopyable = std::is_trivially_copyable>::value; // Wrapper class class PointerWrap { public: enum Mode { MODE_READ = 1, // load MODE_WRITE, // save MODE_MEASURE, // calculate size MODE_VERIFY, // compare }; u8** ptr; Mode mode; public: PointerWrap(u8** ptr_, Mode mode_) : ptr(ptr_), mode(mode_) {} void SetMode(Mode mode_) { mode = mode_; } Mode GetMode() const { return mode; } template void Do(std::map& x) { u32 count = (u32)x.size(); Do(count); switch (mode) { case MODE_READ: for (x.clear(); count != 0; --count) { std::pair pair; Do(pair.first); Do(pair.second); x.insert(pair); } break; case MODE_WRITE: case MODE_MEASURE: case MODE_VERIFY: for (auto& elem : x) { Do(elem.first); Do(elem.second); } break; } } template void Do(std::set& x) { u32 count = (u32)x.size(); Do(count); switch (mode) { case MODE_READ: for (x.clear(); count != 0; --count) { V value; Do(value); x.insert(value); } break; case MODE_WRITE: case MODE_MEASURE: case MODE_VERIFY: for (const V& val : x) { Do(val); } break; } } template void Do(std::vector& x) { DoContiguousContainer(x); } template void Do(std::list& x) { DoContainer(x); } template void Do(std::deque& x) { DoContainer(x); } template void Do(std::basic_string& x) { DoContiguousContainer(x); } template void Do(std::pair& x) { Do(x.first); Do(x.second); } template void Do(std::optional& x) { bool present = x.has_value(); Do(present); switch (mode) { case MODE_READ: if (present) { x = std::make_optional(); Do(x.value()); } else { x = std::nullopt; } break; case MODE_WRITE: case MODE_MEASURE: case MODE_VERIFY: if (present) Do(x.value()); break; } } template void DoArray(std::array& x) { DoArray(x.data(), static_cast(x.size())); } template , int> = 0> void DoArray(T* x, u32 count) { DoVoid(x, count * sizeof(T)); } template , int> = 0> void DoArray(T* x, u32 count) { for (u32 i = 0; i < count; ++i) Do(x[i]); } template void DoArray(T (&arr)[N]) { DoArray(arr, static_cast(N)); } void Do(Common::Flag& flag) { bool s = flag.IsSet(); Do(s); if (mode == MODE_READ) flag.Set(s); } template void Do(std::atomic& atomic) { T temp = atomic.load(); Do(temp); if (mode == MODE_READ) atomic.store(temp); } template void Do(T& x) { static_assert(IsTriviallyCopyable, "Only sane for trivially copyable types"); // Note: // Usually we can just use x = **ptr, etc. However, this doesn't work // for unions containing BitFields (long story, stupid language rules) // or arrays. This will get optimized anyway. DoVoid((void*)&x, sizeof(x)); } template void DoPOD(T& x) { DoVoid((void*)&x, sizeof(x)); } void Do(bool& x) { // bool's size can vary depending on platform, which can // cause breakages. This treats all bools as if they were // 8 bits in size. u8 stable = static_cast(x); Do(stable); if (mode == MODE_READ) x = stable != 0; } template void DoPointer(T*& x, T* const base) { // pointers can be more than 2^31 apart, but you're using this function wrong if you need that // much range ptrdiff_t offset = x - base; Do(offset); if (mode == MODE_READ) { x = base + offset; } } void DoMarker(const std::string& prevName, u32 arbitraryNumber = 0x42) { u32 cookie = arbitraryNumber; Do(cookie); if (mode == PointerWrap::MODE_READ && cookie != arbitraryNumber) { PanicAlertT("Error: After \"%s\", found %d (0x%X) instead of save marker %d (0x%X). Aborting " "savestate load...", prevName.c_str(), cookie, cookie, arbitraryNumber, arbitraryNumber); mode = PointerWrap::MODE_MEASURE; } } template void DoEachElement(T& container, Functor member) { u32 size = static_cast(container.size()); Do(size); container.resize(size); for (auto& elem : container) member(*this, elem); } private: template void DoContiguousContainer(T& container) { u32 size = static_cast(container.size()); Do(size); container.resize(size); if (size > 0) DoArray(&container[0], size); } template void DoContainer(T& x) { DoEachElement(x, [](PointerWrap& p, typename T::value_type& elem) { p.Do(elem); }); } DOLPHIN_FORCE_INLINE void DoVoid(void* data, u32 size) { switch (mode) { case MODE_READ: memcpy(data, *ptr, size); break; case MODE_WRITE: memcpy(*ptr, data, size); break; case MODE_MEASURE: break; case MODE_VERIFY: DEBUG_ASSERT_MSG(COMMON, !memcmp(data, *ptr, size), "Savestate verification failure: buf %p != %p (size %u).\n", data, *ptr, size); break; } *ptr += size; } };