// Copyright (C) 2003 Dolphin Project. // This program 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, version 2.0 or later versions. // This program 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 2.0 for more details. // A copy of the GPL 2.0 should have been included with the program. // If not, see http://www.gnu.org/licenses/ // Official SVN repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ #pragma once // Templates for save state serialization. See Serializer.h. #include #include #include "Common/Serialize/Serializer.h" #include "Common/Swap.h" void Do(PointerWrap &p, std::string &x); void Do(PointerWrap &p, std::wstring &x); // DEPRECATED, do not save wstrings void Do(PointerWrap &p, std::u16string &x); void Do(PointerWrap &p, tm &t); // Don't use DoHelper_ directly. Just use Do(). // This makes it a compile error if you forget to define DoState() on non-POD. // Which also can be a problem, for example struct tm is non-POD on linux, for whatever reason... template::value && std::is_trivial::value, bool isPointer = std::is_pointer::value> struct DoHelper_ { static void DoArray(PointerWrap &p, T *x, int count) { for (int i = 0; i < count; ++i) Do(p, x[i]); } static void DoThing(PointerWrap &p, T &x) { DoClass(p, x); } }; template struct DoHelper_ { static void DoArray(PointerWrap &p, T *x, int count) { p.DoVoid((void *)x, sizeof(T) * count); } static void DoThing(PointerWrap &p, T &x) { p.DoVoid((void *)&x, sizeof(x)); } }; template void DoClass(PointerWrap &p, T &x) { x.DoState(p); } template void DoClass(PointerWrap &p, T *&x) { if (p.mode == PointerWrap::MODE_READ) { delete x; x = new T(); } x->DoState(p); } template void DoSubClass(PointerWrap &p, T *&x, Args... args) { if (p.mode == PointerWrap::MODE_READ) { if (x != nullptr) delete x; x = new S(args...); } x->DoState(p); } template void DoArray(PointerWrap &p, T *x, int count) { DoHelper_::DoArray(p, x, count); } // Lower bound on the bytes one element of a serialized container takes up, used to sanity check // element counts read from a savestate against how much buffer is actually left. // sizeof(T) is only valid for the types DoHelper_ writes out raw - the same condition as its // specialization above. Anything with its own Do()/DoState() (a string, a pointer to a class, a // nested container) routinely serializes far fewer bytes than it occupies in memory, and using // sizeof(T) for those rejects perfectly good savestates. template constexpr size_t SerializeMinElemSize() { return std::is_standard_layout::value && std::is_trivial::value && !std::is_pointer::value ? sizeof(T) : 1; } template void Do(PointerWrap &p, T &x) { DoHelper_::DoThing(p, x); } template void DoVector(PointerWrap &p, std::vector &x, T &default_val) { u32 vec_size = (u32)x.size(); Do(p, vec_size); // Guard against an attacker-controlled size that would both resize the // vector hugely and read past the end of the buffer. if (p.mode == PointerWrap::MODE_READ || p.mode == PointerWrap::MODE_VERIFY) { if (vec_size > p.Remaining() / SerializeMinElemSize()) { p.SetError(PointerWrap::ERROR_FAILURE); return; } } if (vec_size != x.size()) x.resize(vec_size, default_val); if (vec_size > 0) DoArray(p, &x[0], vec_size); } template void Do(PointerWrap &p, std::vector &x) { T *dv = nullptr; DoVector(p, x, dv); } template void Do(PointerWrap &p, std::vector &x) { T dv = T(); DoVector(p, x, dv); } template void Do(PointerWrap &p, std::vector &x, T &default_val) { DoVector(p, x, default_val); } template void Do(PointerWrap &p, swap_struct_t &x) { T v = x.swap(); Do(p, v); x = v; } template void DoPointer(PointerWrap &p, 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 s32 offset = x - base; Do(p, offset); if (p.mode == PointerWrap::MODE_READ) x = base + offset; }