mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	updated to chunk_file module from ppsspp
This commit is contained in:
		
							parent
							
								
									8990b51ac8
								
							
						
					
					
						commit
						a5b31dea56
					
				
					 1 changed files with 630 additions and 140 deletions
				
			
		|  | @ -1,7 +1,19 @@ | ||||||
| // Copyright 2013 Dolphin Emulator Project
 | // Copyright (C) 2003 Dolphin Project.
 | ||||||
| // Licensed under GPLv2
 |  | ||||||
| // Refer to the license.txt file included.
 |  | ||||||
| 
 | 
 | ||||||
|  | // 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/
 | ||||||
| 
 | 
 | ||||||
| #ifndef _POINTERWRAP_H_ | #ifndef _POINTERWRAP_H_ | ||||||
| #define _POINTERWRAP_H_ | #define _POINTERWRAP_H_ | ||||||
|  | @ -17,13 +29,39 @@ | ||||||
| 
 | 
 | ||||||
| #include <map> | #include <map> | ||||||
| #include <vector> | #include <vector> | ||||||
| #include <list> |  | ||||||
| #include <deque> | #include <deque> | ||||||
| #include <string> | #include <string> | ||||||
|  | #include <list> | ||||||
|  | #include <set> | ||||||
|  | #ifndef __SYMBIAN32__ | ||||||
|  | #if defined(IOS) || defined(MACGNUSTD) | ||||||
|  | #include <tr1/type_traits> | ||||||
|  | #else | ||||||
| #include <type_traits> | #include <type_traits> | ||||||
|  | #endif | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
| #include "common.h" | #include "common.h" | ||||||
| #include "file_util.h" | #include "file_util.h" | ||||||
|  | //#include "../ext/snappy/snappy-c.h"
 | ||||||
|  | 
 | ||||||
|  | #if defined(IOS) || defined(MACGNUSTD) | ||||||
|  | namespace std { | ||||||
|  | 	using tr1::is_pointer; | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | #ifdef __SYMBIAN32__ | ||||||
|  | namespace std { | ||||||
|  | 	template <bool bool_value> | ||||||
|  | 	struct bool_constant { | ||||||
|  | 		typedef bool_constant<bool_value> type; | ||||||
|  | 		static const bool value = bool_value; | ||||||
|  | 	}; | ||||||
|  | 	template <bool bool_value> const bool bool_constant<bool_value>::value; | ||||||
|  | 	template <typename T> struct is_pointer : public bool_constant<false> {}; | ||||||
|  | 	template <typename T> struct is_pointer<T*> : public bool_constant<true> {}; | ||||||
|  | } | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
| template <class T> | template <class T> | ||||||
| struct LinkedListItem : public T | struct LinkedListItem : public T | ||||||
|  | @ -31,118 +69,505 @@ struct LinkedListItem : public T | ||||||
| 	LinkedListItem<T> *next; | 	LinkedListItem<T> *next; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | class PointerWrap; | ||||||
|  | 
 | ||||||
|  | class PointerWrapSection | ||||||
|  | { | ||||||
|  | public: | ||||||
|  | 	PointerWrapSection(PointerWrap &p, int ver, const char *title) : p_(p), ver_(ver), title_(title) { | ||||||
|  | 	} | ||||||
|  | 	~PointerWrapSection(); | ||||||
|  | 	 | ||||||
|  | 	bool operator == (const int &v) const { return ver_ == v; } | ||||||
|  | 	bool operator != (const int &v) const { return ver_ != v; } | ||||||
|  | 	bool operator <= (const int &v) const { return ver_ <= v; } | ||||||
|  | 	bool operator >= (const int &v) const { return ver_ >= v; } | ||||||
|  | 	bool operator <  (const int &v) const { return ver_ < v; } | ||||||
|  | 	bool operator >  (const int &v) const { return ver_ > v; } | ||||||
|  | 
 | ||||||
|  | 	operator bool() const  { | ||||||
|  | 		return ver_ > 0; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	PointerWrap &p_; | ||||||
|  | 	int ver_; | ||||||
|  | 	const char *title_; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| // Wrapper class
 | // Wrapper class
 | ||||||
| class PointerWrap | class PointerWrap | ||||||
| { | { | ||||||
| public: | 	// This makes it a compile error if you forget to define DoState() on non-POD.
 | ||||||
| 	enum Mode | 	// Which also can be a problem, for example struct tm is non-POD on linux, for whatever reason...
 | ||||||
|  | #ifdef _MSC_VER | ||||||
|  | 	template<typename T, bool isPOD = std::is_pod<T>::value, bool isPointer = std::is_pointer<T>::value> | ||||||
|  | #else | ||||||
|  | 	template<typename T, bool isPOD = __is_pod(T), bool isPointer = std::is_pointer<T>::value> | ||||||
|  | #endif | ||||||
|  | 	struct DoHelper | ||||||
| 	{ | 	{ | ||||||
|  | 		static void DoArray(PointerWrap *p, T *x, int count) | ||||||
|  | 		{ | ||||||
|  | 			for (int i = 0; i < count; ++i) | ||||||
|  | 				p->Do(x[i]); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		static void Do(PointerWrap *p, T &x) | ||||||
|  | 		{ | ||||||
|  | 			p->DoClass(x); | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	template<typename T> | ||||||
|  | 	struct DoHelper<T, true, false> | ||||||
|  | 	{ | ||||||
|  | 		static void DoArray(PointerWrap *p, T *x, int count) | ||||||
|  | 		{ | ||||||
|  | 			p->DoVoid((void *)x, sizeof(T) * count); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		static void Do(PointerWrap *p, T &x) | ||||||
|  | 		{ | ||||||
|  | 			p->DoVoid((void *)&x, sizeof(x)); | ||||||
|  | 		} | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 	enum Mode { | ||||||
| 		MODE_READ = 1, // load
 | 		MODE_READ = 1, // load
 | ||||||
| 		MODE_WRITE, // save
 | 		MODE_WRITE, // save
 | ||||||
| 		MODE_MEASURE, // calculate size
 | 		MODE_MEASURE, // calculate size
 | ||||||
| 		MODE_VERIFY, // compare
 | 		MODE_VERIFY, // compare
 | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
|  | 	enum Error { | ||||||
|  | 		ERROR_NONE = 0, | ||||||
|  | 		ERROR_WARNING = 1, | ||||||
|  | 		ERROR_FAILURE = 2, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
| 	u8 **ptr; | 	u8 **ptr; | ||||||
| 	Mode mode; | 	Mode mode; | ||||||
|  | 	Error error; | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
| 	PointerWrap(u8 **ptr_, Mode mode_) : ptr(ptr_), mode(mode_) {} | 	PointerWrap(u8 **ptr_, Mode mode_) : ptr(ptr_), mode(mode_), error(ERROR_NONE) {} | ||||||
|  | 	PointerWrap(unsigned char **ptr_, int mode_) : ptr((u8**)ptr_), mode((Mode)mode_), error(ERROR_NONE) {} | ||||||
| 
 | 
 | ||||||
| 	void SetMode(Mode mode_) { mode = mode_; } | 	PointerWrapSection Section(const char *title, int ver) { | ||||||
| 	Mode GetMode() const { return mode; } | 		return Section(title, ver, ver); | ||||||
| 	u8** GetPPtr() { return ptr; } | 	} | ||||||
| 
 | 
 | ||||||
| 	template <typename K, class V> | 	// The returned object can be compared against the version that was loaded.
 | ||||||
| 	void Do(std::map<K, V>& x) | 	// This can be used to support versions as old as minVer.
 | ||||||
| 	{ | 	// Version = 0 means the section was not found.
 | ||||||
| 		u32 count = (u32)x.size(); | 	PointerWrapSection Section(const char *title, int minVer, int ver) { | ||||||
| 		Do(count); | 		char marker[16] = {0}; | ||||||
|  | 		int foundVersion = ver; | ||||||
| 
 | 
 | ||||||
| 		switch (mode) | 		strncpy(marker, title, sizeof(marker)); | ||||||
|  | 		if (!ExpectVoid(marker, sizeof(marker))) | ||||||
| 		{ | 		{ | ||||||
| 		case MODE_READ: | 			// Might be before we added name markers for safety.
 | ||||||
| 			for (x.clear(); count != 0; --count) | 			if (foundVersion == 1 && ExpectVoid(&foundVersion, sizeof(foundVersion))) | ||||||
|  | 				DoMarker(title); | ||||||
|  | 			// Wasn't found, but maybe we can still load the state.
 | ||||||
|  | 			else | ||||||
|  | 				foundVersion = 0; | ||||||
|  | 		} | ||||||
|  | 		else | ||||||
|  | 			Do(foundVersion); | ||||||
|  | 
 | ||||||
|  | 		if (error == ERROR_FAILURE || foundVersion < minVer || foundVersion > ver) { | ||||||
|  | 			WARN_LOG(COMMON, "Savestate failure: wrong version %d found for %s", foundVersion, title); | ||||||
|  | 			SetError(ERROR_FAILURE); | ||||||
|  | 			return PointerWrapSection(*this, -1, title); | ||||||
|  | 		} | ||||||
|  | 		return PointerWrapSection(*this, foundVersion, title); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	void SetMode(Mode mode_) {mode = mode_;} | ||||||
|  | 	Mode GetMode() const {return mode;} | ||||||
|  | 	u8 **GetPPtr() {return ptr;} | ||||||
|  | 	void SetError(Error error_) | ||||||
|  | 	{ | ||||||
|  | 		if (error < error_) | ||||||
|  | 			error = error_; | ||||||
|  | 		if (error > ERROR_WARNING) | ||||||
|  | 			mode = PointerWrap::MODE_MEASURE; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	bool ExpectVoid(void *data, int size) | ||||||
|  | 	{ | ||||||
|  | 		switch (mode) { | ||||||
|  | 		case MODE_READ:	if (memcmp(data, *ptr, size) != 0) return false; break; | ||||||
|  | 		case MODE_WRITE: memcpy(*ptr, data, size); break; | ||||||
|  | 		case MODE_MEASURE: break;  // MODE_MEASURE - don't need to do anything
 | ||||||
|  | 		case MODE_VERIFY: for(int i = 0; i < size; i++) _dbg_assert_msg_(COMMON, ((u8*)data)[i] == (*ptr)[i], "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n", ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], (*ptr)[i], (*ptr)[i], &(*ptr)[i]); break; | ||||||
|  | 		default: break;  // throw an error?
 | ||||||
|  | 		} | ||||||
|  | 		(*ptr) += size; | ||||||
|  | 		return true; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	void DoVoid(void *data, int size) | ||||||
|  | 	{ | ||||||
|  | 		switch (mode) { | ||||||
|  | 		case MODE_READ:	memcpy(data, *ptr, size); break; | ||||||
|  | 		case MODE_WRITE: memcpy(*ptr, data, size); break; | ||||||
|  | 		case MODE_MEASURE: break;  // MODE_MEASURE - don't need to do anything
 | ||||||
|  | 		case MODE_VERIFY: for(int i = 0; i < size; i++) _dbg_assert_msg_(COMMON, ((u8*)data)[i] == (*ptr)[i], "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n", ((u8*)data)[i], ((u8*)data)[i], &((u8*)data)[i], (*ptr)[i], (*ptr)[i], &(*ptr)[i]); break; | ||||||
|  | 		default: break;  // throw an error?
 | ||||||
|  | 		} | ||||||
|  | 		(*ptr) += size; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	template<class K, class T> | ||||||
|  | 	void Do(std::map<K, T *> &x) | ||||||
|  | 	{ | ||||||
|  | 		if (mode == MODE_READ) | ||||||
|  | 		{ | ||||||
|  | 			for (auto it = x.begin(), end = x.end(); it != end; ++it) | ||||||
| 			{ | 			{ | ||||||
| 				std::pair<K, V> pair; | 				if (it->second != NULL) | ||||||
| 				Do(pair.first); | 					delete it->second; | ||||||
| 				Do(pair.second); | 			} | ||||||
| 				x.insert(pair); | 		} | ||||||
|  | 		T *dv = NULL; | ||||||
|  | 		DoMap(x, dv); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	template<class K, class T> | ||||||
|  | 	void Do(std::map<K, T> &x) | ||||||
|  | 	{ | ||||||
|  | 		T dv = T(); | ||||||
|  | 		DoMap(x, dv); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	template<class K, class T> | ||||||
|  | 	void DoMap(std::map<K, T> &x, T &default_val) | ||||||
|  | 	{ | ||||||
|  | 		unsigned int number = (unsigned int)x.size(); | ||||||
|  | 		Do(number); | ||||||
|  | 		switch (mode) { | ||||||
|  | 		case MODE_READ: | ||||||
|  | 			{ | ||||||
|  | 				x.clear(); | ||||||
|  | 				while (number > 0) | ||||||
|  | 				{ | ||||||
|  | 					K first = K(); | ||||||
|  | 					Do(first); | ||||||
|  | 					T second = default_val; | ||||||
|  | 					Do(second); | ||||||
|  | 					x[first] = second; | ||||||
|  | 					--number; | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
| 			break; | 			break; | ||||||
| 
 |  | ||||||
| 		case MODE_WRITE: | 		case MODE_WRITE: | ||||||
| 		case MODE_MEASURE: | 		case MODE_MEASURE: | ||||||
| 		case MODE_VERIFY: | 		case MODE_VERIFY: | ||||||
| 			for (auto itr = x.begin(); itr != x.end(); ++itr) |  | ||||||
| 			{ | 			{ | ||||||
| 				Do(itr->first); | 				typename std::map<K, T>::iterator itr = x.begin(); | ||||||
| 				Do(itr->second); | 				while (number > 0) | ||||||
|  | 				{ | ||||||
|  | 					K first = itr->first; | ||||||
|  | 					Do(first); | ||||||
|  | 					Do(itr->second); | ||||||
|  | 					--number; | ||||||
|  | 					++itr; | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	template <typename T> | 	template<class K, class T> | ||||||
| 	void DoContainer(T& x) | 	void Do(std::multimap<K, T *> &x) | ||||||
| 	{ | 	{ | ||||||
| 		u32 size = (u32)x.size(); | 		if (mode == MODE_READ) | ||||||
| 		Do(size); | 		{ | ||||||
| 		x.resize(size); | 			for (auto it = x.begin(), end = x.end(); it != end; ++it) | ||||||
| 
 | 			{ | ||||||
| 		for (auto itr = x.begin(); itr != x.end(); ++itr) | 				if (it->second != NULL) | ||||||
| 			Do(*itr); | 					delete it->second; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		T *dv = NULL; | ||||||
|  | 		DoMultimap(x, dv); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	template <typename T> | 	template<class K, class T> | ||||||
| 	void Do(std::vector<T>& x) | 	void Do(std::multimap<K, T> &x) | ||||||
| 	{ | 	{ | ||||||
| 		DoContainer(x); | 		T dv = T(); | ||||||
|  | 		DoMultimap(x, dv); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	template <typename T> | 	template<class K, class T> | ||||||
| 	void Do(std::list<T>& x) | 	void DoMultimap(std::multimap<K, T> &x, T &default_val) | ||||||
| 	{ | 	{ | ||||||
| 		DoContainer(x); | 		unsigned int number = (unsigned int)x.size(); | ||||||
|  | 		Do(number); | ||||||
|  | 		switch (mode) { | ||||||
|  | 		case MODE_READ: | ||||||
|  | 			{ | ||||||
|  | 				x.clear(); | ||||||
|  | 				while (number > 0) | ||||||
|  | 				{ | ||||||
|  | 					K first = K(); | ||||||
|  | 					Do(first); | ||||||
|  | 					T second = default_val; | ||||||
|  | 					Do(second); | ||||||
|  | 					x.insert(std::make_pair(first, second)); | ||||||
|  | 					--number; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			break; | ||||||
|  | 		case MODE_WRITE: | ||||||
|  | 		case MODE_MEASURE: | ||||||
|  | 		case MODE_VERIFY: | ||||||
|  | 			{ | ||||||
|  | 				typename std::multimap<K, T>::iterator itr = x.begin(); | ||||||
|  | 				while (number > 0) | ||||||
|  | 				{ | ||||||
|  | 					Do(itr->first); | ||||||
|  | 					Do(itr->second); | ||||||
|  | 					--number; | ||||||
|  | 					++itr; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	template <typename T> | 	// Store vectors.
 | ||||||
| 	void Do(std::deque<T>& x) | 	template<class T> | ||||||
|  | 	void Do(std::vector<T *> &x) | ||||||
| 	{ | 	{ | ||||||
| 		DoContainer(x); | 		T *dv = NULL; | ||||||
|  | 		DoVector(x, dv); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	template <typename T> | 	template<class T> | ||||||
| 	void Do(std::basic_string<T>& x) | 	void Do(std::vector<T> &x) | ||||||
| 	{ | 	{ | ||||||
| 		DoContainer(x); | 		T dv = T(); | ||||||
|  | 		DoVector(x, dv); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	template <typename T> | 
 | ||||||
| 	void DoArray(T* x, u32 count) | 	template<class T> | ||||||
|  | 	void DoPOD(std::vector<T> &x) | ||||||
| 	{ | 	{ | ||||||
| 		for (u32 i = 0; i != count; ++i) | 		T dv = T(); | ||||||
|  | 		DoVectorPOD(x, dv); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	template<class T> | ||||||
|  | 	void Do(std::vector<T> &x, T &default_val) | ||||||
|  | 	{ | ||||||
|  | 		DoVector(x, default_val); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	template<class T> | ||||||
|  | 	void DoVector(std::vector<T> &x, T &default_val) | ||||||
|  | 	{ | ||||||
|  | 		u32 vec_size = (u32)x.size(); | ||||||
|  | 		Do(vec_size); | ||||||
|  | 		x.resize(vec_size, default_val); | ||||||
|  | 		if (vec_size > 0) | ||||||
|  | 			DoArray(&x[0], vec_size); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	template<class T> | ||||||
|  | 	void DoVectorPOD(std::vector<T> &x, T &default_val) | ||||||
|  | 	{ | ||||||
|  | 		u32 vec_size = (u32)x.size(); | ||||||
|  | 		Do(vec_size); | ||||||
|  | 		x.resize(vec_size, default_val); | ||||||
|  | 		if (vec_size > 0) | ||||||
|  | 			DoArray(&x[0], vec_size); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	// Store deques.
 | ||||||
|  | 	template<class T> | ||||||
|  | 	void Do(std::deque<T *> &x) | ||||||
|  | 	{ | ||||||
|  | 		T *dv = NULL; | ||||||
|  | 		DoDeque(x, dv); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	template<class T> | ||||||
|  | 	void Do(std::deque<T> &x) | ||||||
|  | 	{ | ||||||
|  | 		T dv = T(); | ||||||
|  | 		DoDeque(x, dv); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	template<class T> | ||||||
|  | 	void DoDeque(std::deque<T> &x, T &default_val) | ||||||
|  | 	{ | ||||||
|  | 		u32 deq_size = (u32)x.size(); | ||||||
|  | 		Do(deq_size); | ||||||
|  | 		x.resize(deq_size, default_val); | ||||||
|  | 		u32 i; | ||||||
|  | 		for(i = 0; i < deq_size; i++) | ||||||
| 			Do(x[i]); | 			Do(x[i]); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	template <typename T> | 	// Store STL lists.
 | ||||||
| 	void Do(T& x) | 	template<class T> | ||||||
|  | 	void Do(std::list<T *> &x) | ||||||
| 	{ | 	{ | ||||||
| 		// Ideally this would be std::is_trivially_copyable, but not enough support yet
 | 		T *dv = NULL; | ||||||
| 		static_assert(std::is_pod<T>::value, "Only sane for POD types"); | 		Do(x, dv); | ||||||
| 		 |  | ||||||
| 		DoVoid((void*)&x, sizeof(x)); |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	template <typename T> | 	template<class T> | ||||||
| 	void DoPOD(T& x) | 	void Do(std::list<T> &x) | ||||||
| 	{ | 	{ | ||||||
| 		DoVoid((void*)&x, sizeof(x)); | 		T dv = T(); | ||||||
|  | 		DoList(x, dv); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	template <typename T> | 	template<class T> | ||||||
| 	void DoPointer(T*& x, T* const base) | 	void Do(std::list<T> &x, T &default_val) | ||||||
| 	{ | 	{ | ||||||
|  | 		DoList(x, default_val); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	template<class T> | ||||||
|  | 	void DoList(std::list<T> &x, T &default_val) | ||||||
|  | 	{ | ||||||
|  | 		u32 list_size = (u32)x.size(); | ||||||
|  | 		Do(list_size); | ||||||
|  | 		x.resize(list_size, default_val); | ||||||
|  | 
 | ||||||
|  | 		typename std::list<T>::iterator itr, end; | ||||||
|  | 		for (itr = x.begin(), end = x.end(); itr != end; ++itr) | ||||||
|  | 			Do(*itr); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	// Store STL sets.
 | ||||||
|  | 	template <class T> | ||||||
|  | 	void Do(std::set<T *> &x) | ||||||
|  | 	{ | ||||||
|  | 		if (mode == MODE_READ) | ||||||
|  | 		{ | ||||||
|  | 			for (auto it = x.begin(), end = x.end(); it != end; ++it) | ||||||
|  | 			{ | ||||||
|  | 				if (*it != NULL) | ||||||
|  | 					delete *it; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		DoSet(x); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	template <class T> | ||||||
|  | 	void Do(std::set<T> &x) | ||||||
|  | 	{ | ||||||
|  | 		DoSet(x); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	template <class T> | ||||||
|  | 	void DoSet(std::set<T> &x) | ||||||
|  | 	{ | ||||||
|  | 		unsigned int number = (unsigned int)x.size(); | ||||||
|  | 		Do(number); | ||||||
|  | 
 | ||||||
|  | 		switch (mode) | ||||||
|  | 		{ | ||||||
|  | 		case MODE_READ: | ||||||
|  | 			{ | ||||||
|  | 				x.clear(); | ||||||
|  | 				while (number-- > 0) | ||||||
|  | 				{ | ||||||
|  | 					T it = T(); | ||||||
|  | 					Do(it); | ||||||
|  | 					x.insert(it); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			break; | ||||||
|  | 		case MODE_WRITE: | ||||||
|  | 		case MODE_MEASURE: | ||||||
|  | 		case MODE_VERIFY: | ||||||
|  | 			{ | ||||||
|  | 				typename std::set<T>::iterator itr = x.begin(); | ||||||
|  | 				while (number-- > 0) | ||||||
|  | 					Do(*itr++); | ||||||
|  | 			} | ||||||
|  | 			break; | ||||||
|  | 
 | ||||||
|  | 		default: | ||||||
|  | 			ERROR_LOG(COMMON, "Savestate error: invalid mode %d.", mode); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Store strings.
 | ||||||
|  | 	void Do(std::string &x)  | ||||||
|  | 	{ | ||||||
|  | 		int stringLen = (int)x.length() + 1; | ||||||
|  | 		Do(stringLen); | ||||||
|  | 		 | ||||||
|  | 		switch (mode) { | ||||||
|  | 		case MODE_READ:		x = (char*)*ptr; break; | ||||||
|  | 		case MODE_WRITE:	memcpy(*ptr, x.c_str(), stringLen); break; | ||||||
|  | 		case MODE_MEASURE: break; | ||||||
|  | 		case MODE_VERIFY: _dbg_assert_msg_(COMMON, !strcmp(x.c_str(), (char*)*ptr), "Savestate verification failure: \"%s\" != \"%s\" (at %p).\n", x.c_str(), (char*)*ptr, ptr); break; | ||||||
|  | 		} | ||||||
|  | 		(*ptr) += stringLen; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	void Do(std::wstring &x)  | ||||||
|  | 	{ | ||||||
|  | 		int stringLen = sizeof(wchar_t)*((int)x.length() + 1); | ||||||
|  | 		Do(stringLen); | ||||||
|  | 
 | ||||||
|  | 		switch (mode) { | ||||||
|  | 		case MODE_READ:		x = (wchar_t*)*ptr; break; | ||||||
|  | 		case MODE_WRITE:	memcpy(*ptr, x.c_str(), stringLen); break; | ||||||
|  | 		case MODE_MEASURE: break; | ||||||
|  | 		case MODE_VERIFY: _dbg_assert_msg_(COMMON, x == (wchar_t*)*ptr, "Savestate verification failure: \"%ls\" != \"%ls\" (at %p).\n", x.c_str(), (wchar_t*)*ptr, ptr); break; | ||||||
|  | 		} | ||||||
|  | 		(*ptr) += stringLen; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	template<class T> | ||||||
|  | 	void DoClass(T &x) { | ||||||
|  | 		x.DoState(*this); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	template<class T> | ||||||
|  | 	void DoClass(T *&x) { | ||||||
|  | 		if (mode == MODE_READ) | ||||||
|  | 		{ | ||||||
|  | 			if (x != NULL) | ||||||
|  | 				delete x; | ||||||
|  | 			x = new T(); | ||||||
|  | 		} | ||||||
|  | 		x->DoState(*this); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	template<class T> | ||||||
|  | 	void DoArray(T *x, int count) { | ||||||
|  | 		DoHelper<T>::DoArray(this, x, count); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	template<class T> | ||||||
|  | 	void Do(T &x) { | ||||||
|  | 		DoHelper<T>::Do(this, x); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	template<class T> | ||||||
|  | 	void DoPOD(T &x) { | ||||||
|  | 		DoHelper<T>::Do(this, x); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	template<class T> | ||||||
|  | 	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
 | 		// 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; | 		s32 offset = x - base; | ||||||
| 		Do(offset); | 		Do(offset); | ||||||
|  | @ -150,8 +575,7 @@ public: | ||||||
| 			x = base + offset; | 			x = base + offset; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Let's pretend std::list doesn't exist!
 | 	template<class T, LinkedListItem<T>* (*TNew)(), void (*TFree)(LinkedListItem<T>*), void (*TDo)(PointerWrap&, T*)> | ||||||
| 	template <class T, LinkedListItem<T>* (*TNew)(), void (*TFree)(LinkedListItem<T>*), void (*TDo)(PointerWrap&, T*)> |  | ||||||
| 	void DoLinkedList(LinkedListItem<T>*& list_start, LinkedListItem<T>** list_end=0) | 	void DoLinkedList(LinkedListItem<T>*& list_start, LinkedListItem<T>** list_end=0) | ||||||
| 	{ | 	{ | ||||||
| 		LinkedListItem<T>* list_cur = list_start; | 		LinkedListItem<T>* list_cur = list_start; | ||||||
|  | @ -211,66 +635,48 @@ public: | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	void DoMarker(const char* prevName, u32 arbitraryNumber = 0x42) | 	void DoMarker(const char* prevName, u32 arbitraryNumber=0x42) | ||||||
| 	{ | 	{ | ||||||
| 		u32 cookie = arbitraryNumber; | 		u32 cookie = arbitraryNumber; | ||||||
| 		Do(cookie); | 		Do(cookie); | ||||||
| 
 | 		if(mode == PointerWrap::MODE_READ && cookie != arbitraryNumber) | ||||||
| 		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...", | 			PanicAlertT("Error: After \"%s\", found %d (0x%X) instead of save marker %d (0x%X). Aborting savestate load...", prevName, cookie, cookie, arbitraryNumber, arbitraryNumber); | ||||||
| 				prevName, cookie, cookie, arbitraryNumber, arbitraryNumber); | 			SetError(ERROR_FAILURE); | ||||||
| 			mode = PointerWrap::MODE_MEASURE; |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 |  | ||||||
| private: |  | ||||||
| 	__forceinline void DoByte(u8& x) |  | ||||||
| 	{ |  | ||||||
| 		switch (mode) |  | ||||||
| 		{ |  | ||||||
| 		case MODE_READ: |  | ||||||
| 			x = **ptr; |  | ||||||
| 			break; |  | ||||||
| 
 |  | ||||||
| 		case MODE_WRITE: |  | ||||||
| 			**ptr = x; |  | ||||||
| 			break; |  | ||||||
| 
 |  | ||||||
| 		case MODE_MEASURE: |  | ||||||
| 			break; |  | ||||||
| 
 |  | ||||||
| 		case MODE_VERIFY: |  | ||||||
| 			_dbg_assert_msg_(COMMON, (x == **ptr), |  | ||||||
| 				"Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n", |  | ||||||
| 					x, x, &x, **ptr, **ptr, *ptr); |  | ||||||
| 			break; |  | ||||||
| 
 |  | ||||||
| 		default: |  | ||||||
| 			break; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		++(*ptr); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	void DoVoid(void *data, u32 size) |  | ||||||
| 	{ |  | ||||||
| 		for(u32 i = 0; i != size; ++i) |  | ||||||
| 			DoByte(reinterpret_cast<u8*>(data)[i]); |  | ||||||
| 	} |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | inline PointerWrapSection::~PointerWrapSection() { | ||||||
|  | 	if (ver_ > 0) { | ||||||
|  | 		p_.DoMarker(title_); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class CChunkFileReader | class CChunkFileReader | ||||||
| { | { | ||||||
| public: | public: | ||||||
|  | 	enum Error { | ||||||
|  | 		ERROR_NONE, | ||||||
|  | 		ERROR_BAD_FILE, | ||||||
|  | 		ERROR_BROKEN_STATE, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
| 	// Load file template
 | 	// Load file template
 | ||||||
| 	template<class T> | 	template<class T> | ||||||
| 	static bool Load(const std::string& _rFilename, u32 _Revision, T& _class) | 	static Error Load(const std::string& _rFilename, int _Revision, const char *_VersionString, T& _class, std::string* _failureReason)  | ||||||
| 	{ | 	{ | ||||||
| 		INFO_LOG(COMMON, "ChunkReader: Loading %s" , _rFilename.c_str()); | 		INFO_LOG(COMMON, "ChunkReader: Loading %s" , _rFilename.c_str()); | ||||||
|  | 		_failureReason->clear(); | ||||||
|  | 		_failureReason->append("LoadStateWrongVersion"); | ||||||
| 
 | 
 | ||||||
| 		if (!File::Exists(_rFilename)) | 		if (!File::Exists(_rFilename)) { | ||||||
| 			return false; | 			_failureReason->clear(); | ||||||
|  | 			_failureReason->append("LoadStateDoesntExist"); | ||||||
|  | 			ERROR_LOG(COMMON, "ChunkReader: File doesn't exist"); | ||||||
|  | 			return ERROR_BAD_FILE; | ||||||
|  | 		} | ||||||
| 				 | 				 | ||||||
| 		// Check file size
 | 		// Check file size
 | ||||||
| 		const u64 fileSize = File::GetSize(_rFilename); | 		const u64 fileSize = File::GetSize(_rFilename); | ||||||
|  | @ -278,14 +684,14 @@ public: | ||||||
| 		if (fileSize < headerSize) | 		if (fileSize < headerSize) | ||||||
| 		{ | 		{ | ||||||
| 			ERROR_LOG(COMMON,"ChunkReader: File too small"); | 			ERROR_LOG(COMMON,"ChunkReader: File too small"); | ||||||
| 			return false; | 			return ERROR_BAD_FILE; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		File::IOFile pFile(_rFilename, "rb"); | 		File::IOFile pFile(_rFilename, "rb"); | ||||||
| 		if (!pFile) | 		if (!pFile) | ||||||
| 		{ | 		{ | ||||||
| 			ERROR_LOG(COMMON,"ChunkReader: Can't open file for reading"); | 			ERROR_LOG(COMMON,"ChunkReader: Can't open file for reading"); | ||||||
| 			return false; | 			return ERROR_BAD_FILE; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// read the header
 | 		// read the header
 | ||||||
|  | @ -293,7 +699,7 @@ public: | ||||||
| 		if (!pFile.ReadArray(&header, 1)) | 		if (!pFile.ReadArray(&header, 1)) | ||||||
| 		{ | 		{ | ||||||
| 			ERROR_LOG(COMMON,"ChunkReader: Bad header size"); | 			ERROR_LOG(COMMON,"ChunkReader: Bad header size"); | ||||||
| 			return false; | 			return ERROR_BAD_FILE; | ||||||
| 		} | 		} | ||||||
| 		 | 		 | ||||||
| 		// Check revision
 | 		// Check revision
 | ||||||
|  | @ -301,83 +707,167 @@ public: | ||||||
| 		{ | 		{ | ||||||
| 			ERROR_LOG(COMMON,"ChunkReader: Wrong file revision, got %d expected %d", | 			ERROR_LOG(COMMON,"ChunkReader: Wrong file revision, got %d expected %d", | ||||||
| 				header.Revision, _Revision); | 				header.Revision, _Revision); | ||||||
| 			return false; | 			return ERROR_BAD_FILE; | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		if (strcmp(header.GitVersion, _VersionString) != 0) | ||||||
|  | 		{ | ||||||
|  | 			WARN_LOG(COMMON, "This savestate was generated by a different version of PPSSPP, %s. It may not load properly.", | ||||||
|  | 				header.GitVersion); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// get size
 | 		// get size
 | ||||||
| 		const u32 sz = (u32)(fileSize - headerSize); | 		const int sz = (int)(fileSize - headerSize); | ||||||
| 		if (header.ExpectedSize != sz) | 		if (header.ExpectedSize != sz) | ||||||
| 		{ | 		{ | ||||||
| 			ERROR_LOG(COMMON,"ChunkReader: Bad file size, got %d expected %d", | 			ERROR_LOG(COMMON,"ChunkReader: Bad file size, got %d expected %d", | ||||||
| 				sz, header.ExpectedSize); | 				sz, header.ExpectedSize); | ||||||
| 			return false; | 			return ERROR_BAD_FILE; | ||||||
| 		} | 		} | ||||||
| 		 | 		 | ||||||
| 		// read the state
 | 		// read the state
 | ||||||
| 		std::vector<u8> buffer(sz); | 		u8* buffer = new u8[sz]; | ||||||
| 		if (!pFile.ReadArray(&buffer[0], sz)) | 		if (!pFile.ReadBytes(buffer, sz)) | ||||||
| 		{ | 		{ | ||||||
| 			ERROR_LOG(COMMON,"ChunkReader: Error reading file"); | 			ERROR_LOG(COMMON,"ChunkReader: Error reading file"); | ||||||
| 			return false; | 			return ERROR_BAD_FILE; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		u8 *ptr = buffer; | ||||||
|  | 		u8 *buf = buffer; | ||||||
|  | 		if (header.Compress) { | ||||||
|  | 			u8 *uncomp_buffer = new u8[header.UncompressedSize]; | ||||||
|  | 			size_t uncomp_size = header.UncompressedSize; | ||||||
|  | 			snappy_uncompress((const char *)buffer, sz, (char *)uncomp_buffer, &uncomp_size); | ||||||
|  | 			if ((int)uncomp_size != header.UncompressedSize) { | ||||||
|  | 				ERROR_LOG(COMMON,"Size mismatch: file: %i  calc: %i", (int)header.UncompressedSize, (int)uncomp_size); | ||||||
|  | 			} | ||||||
|  | 			ptr = uncomp_buffer; | ||||||
|  | 			buf = uncomp_buffer; | ||||||
|  | 			delete [] buffer; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		u8* ptr = &buffer[0]; |  | ||||||
| 		PointerWrap p(&ptr, PointerWrap::MODE_READ); | 		PointerWrap p(&ptr, PointerWrap::MODE_READ); | ||||||
| 		_class.DoState(p); | 		_class.DoState(p); | ||||||
|  | 		delete[] buf; | ||||||
| 		 | 		 | ||||||
| 		INFO_LOG(COMMON, "ChunkReader: Done loading %s" , _rFilename.c_str()); | 		INFO_LOG(COMMON, "ChunkReader: Done loading %s" , _rFilename.c_str()); | ||||||
| 		return true; | 		if (p.error != p.ERROR_FAILURE) { | ||||||
|  | 			return ERROR_NONE; | ||||||
|  | 		} else { | ||||||
|  | 			return ERROR_BROKEN_STATE; | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	 | 	 | ||||||
| 	// Save file template
 | 	// Save file template
 | ||||||
| 	template<class T> | 	template<class T> | ||||||
| 	static bool Save(const std::string& _rFilename, u32 _Revision, T& _class) | 	static Error Save(const std::string& _rFilename, int _Revision, const char *_VersionString, T& _class) | ||||||
| 	{ | 	{ | ||||||
| 		INFO_LOG(COMMON, "ChunkReader: Writing %s" , _rFilename.c_str()); | 		INFO_LOG(COMMON, "ChunkReader: Writing %s" , _rFilename.c_str()); | ||||||
|  | 
 | ||||||
| 		File::IOFile pFile(_rFilename, "wb"); | 		File::IOFile pFile(_rFilename, "wb"); | ||||||
| 		if (!pFile) | 		if (!pFile) | ||||||
| 		{ | 		{ | ||||||
| 			ERROR_LOG(COMMON,"ChunkReader: Error opening file for write"); | 			ERROR_LOG(COMMON,"ChunkReader: Error opening file for write"); | ||||||
| 			return false; | 			return ERROR_BAD_FILE; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		bool compress = true; | ||||||
|  | 
 | ||||||
| 		// Get data
 | 		// Get data
 | ||||||
| 		u8 *ptr = 0; | 		u8 *ptr = 0; | ||||||
| 		PointerWrap p(&ptr, PointerWrap::MODE_MEASURE); | 		PointerWrap p(&ptr, PointerWrap::MODE_MEASURE); | ||||||
| 		_class.DoState(p); | 		_class.DoState(p); | ||||||
| 		size_t const sz = (size_t)ptr; | 		size_t const sz = (size_t)ptr; | ||||||
| 		std::vector<u8> buffer(sz); | 		 | ||||||
|  | 		u8 * buffer = new u8[sz]; | ||||||
| 		ptr = &buffer[0]; | 		ptr = &buffer[0]; | ||||||
| 		p.SetMode(PointerWrap::MODE_WRITE); | 		p.SetMode(PointerWrap::MODE_WRITE); | ||||||
| 		_class.DoState(p); | 		_class.DoState(p); | ||||||
| 
 | 
 | ||||||
| 		// Create header
 | 		// Create header
 | ||||||
| 		SChunkHeader header; | 		SChunkHeader header; | ||||||
|  | 		header.Compress = compress ? 1 : 0; | ||||||
| 		header.Revision = _Revision; | 		header.Revision = _Revision; | ||||||
| 		header.ExpectedSize = (u32)sz; | 		header.ExpectedSize = (int)sz; | ||||||
|  | 		header.UncompressedSize = (int)sz; | ||||||
|  | 		strncpy(header.GitVersion, _VersionString, 32); | ||||||
|  | 		header.GitVersion[31] = '\0'; | ||||||
| 
 | 
 | ||||||
| 		// Write to file
 | 		// Write to file
 | ||||||
| 		if (!pFile.WriteArray(&header, 1)) | 		if (compress) { | ||||||
| 		{ | 			size_t comp_len = snappy_max_compressed_length(sz); | ||||||
| 			ERROR_LOG(COMMON,"ChunkReader: Failed writing header"); | 			u8 *compressed_buffer = new u8[comp_len]; | ||||||
| 			return false; | 			snappy_compress((const char *)buffer, sz, (char *)compressed_buffer, &comp_len); | ||||||
|  | 			delete [] buffer; | ||||||
|  | 			header.ExpectedSize = (int)comp_len; | ||||||
|  | 			if (!pFile.WriteArray(&header, 1)) | ||||||
|  | 			{ | ||||||
|  | 				ERROR_LOG(COMMON,"ChunkReader: Failed writing header"); | ||||||
|  | 				return ERROR_BAD_FILE; | ||||||
|  | 			} | ||||||
|  | 			if (!pFile.WriteBytes(&compressed_buffer[0], comp_len)) { | ||||||
|  | 				ERROR_LOG(COMMON,"ChunkReader: Failed writing compressed data"); | ||||||
|  | 				return ERROR_BAD_FILE; | ||||||
|  | 			}	else { | ||||||
|  | 				INFO_LOG(COMMON, "Savestate: Compressed %i bytes into %i", (int)sz, (int)comp_len); | ||||||
|  | 			} | ||||||
|  | 			delete [] compressed_buffer; | ||||||
|  | 		} else { | ||||||
|  | 			if (!pFile.WriteArray(&header, 1)) | ||||||
|  | 			{ | ||||||
|  | 				ERROR_LOG(COMMON,"ChunkReader: Failed writing header"); | ||||||
|  | 				return ERROR_BAD_FILE; | ||||||
|  | 			} | ||||||
|  | 			if (!pFile.WriteBytes(&buffer[0], sz)) | ||||||
|  | 			{ | ||||||
|  | 				ERROR_LOG(COMMON,"ChunkReader: Failed writing data"); | ||||||
|  | 				return ERROR_BAD_FILE; | ||||||
|  | 			} | ||||||
|  | 			delete [] buffer; | ||||||
| 		} | 		} | ||||||
| 		 | 		 | ||||||
| 		if (!pFile.WriteArray(&buffer[0], sz)) | 		INFO_LOG(COMMON,"ChunkReader: Done writing %s",  | ||||||
| 		{ | 				 _rFilename.c_str()); | ||||||
| 			ERROR_LOG(COMMON,"ChunkReader: Failed writing data"); | 		if (p.error != p.ERROR_FAILURE) { | ||||||
| 			return false; | 			return ERROR_NONE; | ||||||
|  | 		} else { | ||||||
|  | 			return ERROR_BROKEN_STATE; | ||||||
| 		} | 		} | ||||||
|  | 	} | ||||||
| 	 | 	 | ||||||
| 		INFO_LOG(COMMON,"ChunkReader: Done writing %s", _rFilename.c_str()); | 	template <class T> | ||||||
| 		return true; | 	static Error Verify(T& _class) | ||||||
|  | 	{ | ||||||
|  | 		u8 *ptr = 0; | ||||||
|  | 
 | ||||||
|  | 		// Step 1: Measure the space required.
 | ||||||
|  | 		PointerWrap p(&ptr, PointerWrap::MODE_MEASURE); | ||||||
|  | 		_class.DoState(p); | ||||||
|  | 		size_t const sz = (size_t)ptr; | ||||||
|  | 		std::vector<u8> buffer(sz); | ||||||
|  | 
 | ||||||
|  | 		// Step 2: Dump the state.
 | ||||||
|  | 		ptr = &buffer[0]; | ||||||
|  | 		p.SetMode(PointerWrap::MODE_WRITE); | ||||||
|  | 		_class.DoState(p); | ||||||
|  | 
 | ||||||
|  | 		// Step 3: Verify the state.
 | ||||||
|  | 		ptr = &buffer[0]; | ||||||
|  | 		p.SetMode(PointerWrap::MODE_VERIFY); | ||||||
|  | 		_class.DoState(p); | ||||||
|  | 
 | ||||||
|  | 		return ERROR_NONE; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
| 	struct SChunkHeader | 	struct SChunkHeader | ||||||
| 	{ | 	{ | ||||||
| 		u32 Revision; | 		int Revision; | ||||||
| 		u32 ExpectedSize; | 		int Compress; | ||||||
|  | 		int ExpectedSize; | ||||||
|  | 		int UncompressedSize; | ||||||
|  | 		char GitVersion[32]; | ||||||
| 	}; | 	}; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue