mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40: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
 | ||||
| // Licensed under GPLv2
 | ||||
| // Refer to the license.txt file included.
 | ||||
| // 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/
 | ||||
| 
 | ||||
| #ifndef _POINTERWRAP_H_ | ||||
| #define _POINTERWRAP_H_ | ||||
|  | @ -17,13 +29,39 @@ | |||
| 
 | ||||
| #include <map> | ||||
| #include <vector> | ||||
| #include <list> | ||||
| #include <deque> | ||||
| #include <string> | ||||
| #include <list> | ||||
| #include <set> | ||||
| #ifndef __SYMBIAN32__ | ||||
| #if defined(IOS) || defined(MACGNUSTD) | ||||
| #include <tr1/type_traits> | ||||
| #else | ||||
| #include <type_traits> | ||||
| #endif | ||||
| #endif | ||||
| 
 | ||||
| #include "common.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> | ||||
| struct LinkedListItem : public T | ||||
|  | @ -31,118 +69,505 @@ struct LinkedListItem : public T | |||
| 	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
 | ||||
| class PointerWrap | ||||
| { | ||||
| public: | ||||
| 	enum Mode | ||||
| 	// 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...
 | ||||
| #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_WRITE, // save
 | ||||
| 		MODE_MEASURE, // calculate size
 | ||||
| 		MODE_VERIFY, // compare
 | ||||
| 	}; | ||||
| 
 | ||||
| 	enum Error { | ||||
| 		ERROR_NONE = 0, | ||||
| 		ERROR_WARNING = 1, | ||||
| 		ERROR_FAILURE = 2, | ||||
| 	}; | ||||
| 
 | ||||
| 	u8 **ptr; | ||||
| 	Mode mode; | ||||
| 	Error error; | ||||
| 
 | ||||
| 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_; } | ||||
| 	Mode GetMode() const { return mode; } | ||||
| 	u8** GetPPtr() { return ptr; } | ||||
| 	PointerWrapSection Section(const char *title, int ver) { | ||||
| 		return Section(title, ver, ver); | ||||
| 	} | ||||
| 
 | ||||
| 	template <typename K, class V> | ||||
| 	void Do(std::map<K, V>& x) | ||||
| 	{ | ||||
| 		u32 count = (u32)x.size(); | ||||
| 		Do(count); | ||||
| 	// The returned object can be compared against the version that was loaded.
 | ||||
| 	// This can be used to support versions as old as minVer.
 | ||||
| 	// Version = 0 means the section was not found.
 | ||||
| 	PointerWrapSection Section(const char *title, int minVer, int ver) { | ||||
| 		char marker[16] = {0}; | ||||
| 		int foundVersion = ver; | ||||
| 
 | ||||
| 		switch (mode) | ||||
| 		strncpy(marker, title, sizeof(marker)); | ||||
| 		if (!ExpectVoid(marker, sizeof(marker))) | ||||
| 		{ | ||||
| 		case MODE_READ: | ||||
| 			for (x.clear(); count != 0; --count) | ||||
| 			// Might be before we added name markers for safety.
 | ||||
| 			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; | ||||
| 				Do(pair.first); | ||||
| 				Do(pair.second); | ||||
| 				x.insert(pair); | ||||
| 				if (it->second != NULL) | ||||
| 					delete it->second; | ||||
| 			} | ||||
| 		} | ||||
| 		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; | ||||
| 
 | ||||
| 		case MODE_WRITE: | ||||
| 		case MODE_MEASURE: | ||||
| 		case MODE_VERIFY: | ||||
| 			for (auto itr = x.begin(); itr != x.end(); ++itr) | ||||
| 			{ | ||||
| 				Do(itr->first); | ||||
| 				Do(itr->second); | ||||
| 				typename std::map<K, T>::iterator itr = x.begin(); | ||||
| 				while (number > 0) | ||||
| 				{ | ||||
| 					K first = itr->first; | ||||
| 					Do(first); | ||||
| 					Do(itr->second); | ||||
| 					--number; | ||||
| 					++itr; | ||||
| 				} | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	template <typename T> | ||||
| 	void DoContainer(T& x) | ||||
| 	template<class K, class T> | ||||
| 	void Do(std::multimap<K, T *> &x) | ||||
| 	{ | ||||
| 		u32 size = (u32)x.size(); | ||||
| 		Do(size); | ||||
| 		x.resize(size); | ||||
| 
 | ||||
| 		for (auto itr = x.begin(); itr != x.end(); ++itr) | ||||
| 			Do(*itr); | ||||
| 		if (mode == MODE_READ) | ||||
| 		{ | ||||
| 			for (auto it = x.begin(), end = x.end(); it != end; ++it) | ||||
| 			{ | ||||
| 				if (it->second != NULL) | ||||
| 					delete it->second; | ||||
| 			} | ||||
| 		} | ||||
| 		T *dv = NULL; | ||||
| 		DoMultimap(x, dv); | ||||
| 	} | ||||
| 
 | ||||
| 	template <typename T> | ||||
| 	void Do(std::vector<T>& x) | ||||
| 	template<class K, class T> | ||||
| 	void Do(std::multimap<K, T> &x) | ||||
| 	{ | ||||
| 		DoContainer(x); | ||||
| 		T dv = T(); | ||||
| 		DoMultimap(x, dv); | ||||
| 	} | ||||
| 
 | ||||
| 	template <typename T> | ||||
| 	void Do(std::list<T>& x) | ||||
| 	template<class K, class T> | ||||
| 	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> | ||||
| 	void Do(std::deque<T>& x) | ||||
| 	// Store vectors.
 | ||||
| 	template<class T> | ||||
| 	void Do(std::vector<T *> &x) | ||||
| 	{ | ||||
| 		DoContainer(x); | ||||
| 		T *dv = NULL; | ||||
| 		DoVector(x, dv); | ||||
| 	} | ||||
| 
 | ||||
| 	template <typename T> | ||||
| 	void Do(std::basic_string<T>& x) | ||||
| 	template<class T> | ||||
| 	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]); | ||||
| 	} | ||||
| 
 | ||||
| 	template <typename T> | ||||
| 	void Do(T& x) | ||||
| 	// Store STL lists.
 | ||||
| 	template<class T> | ||||
| 	void Do(std::list<T *> &x) | ||||
| 	{ | ||||
| 		// Ideally this would be std::is_trivially_copyable, but not enough support yet
 | ||||
| 		static_assert(std::is_pod<T>::value, "Only sane for POD types"); | ||||
| 		 | ||||
| 		DoVoid((void*)&x, sizeof(x)); | ||||
| 	} | ||||
| 	 | ||||
| 	template <typename T> | ||||
| 	void DoPOD(T& x) | ||||
| 	{ | ||||
| 		DoVoid((void*)&x, sizeof(x)); | ||||
| 		T *dv = NULL; | ||||
| 		Do(x, dv); | ||||
| 	} | ||||
| 
 | ||||
| 	template <typename T> | ||||
| 	void DoPointer(T*& x, T* const base) | ||||
| 	template<class T> | ||||
| 	void Do(std::list<T> &x) | ||||
| 	{ | ||||
| 		T dv = T(); | ||||
| 		DoList(x, dv); | ||||
| 	} | ||||
| 
 | ||||
| 	template<class T> | ||||
| 	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
 | ||||
| 		s32 offset = x - base; | ||||
| 		Do(offset); | ||||
|  | @ -150,8 +575,7 @@ public: | |||
| 			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) | ||||
| 	{ | ||||
| 		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; | ||||
| 		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...", | ||||
| 				prevName, cookie, cookie, arbitraryNumber, arbitraryNumber); | ||||
| 			mode = PointerWrap::MODE_MEASURE; | ||||
| 			PanicAlertT("Error: After \"%s\", found %d (0x%X) instead of save marker %d (0x%X). Aborting savestate load...", prevName, cookie, cookie, arbitraryNumber, arbitraryNumber); | ||||
| 			SetError(ERROR_FAILURE); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 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 | ||||
| { | ||||
| public: | ||||
| 	enum Error { | ||||
| 		ERROR_NONE, | ||||
| 		ERROR_BAD_FILE, | ||||
| 		ERROR_BROKEN_STATE, | ||||
| 	}; | ||||
| 
 | ||||
| 	// Load file template
 | ||||
| 	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()); | ||||
| 		_failureReason->clear(); | ||||
| 		_failureReason->append("LoadStateWrongVersion"); | ||||
| 
 | ||||
| 		if (!File::Exists(_rFilename)) | ||||
| 			return false; | ||||
| 		if (!File::Exists(_rFilename)) { | ||||
| 			_failureReason->clear(); | ||||
| 			_failureReason->append("LoadStateDoesntExist"); | ||||
| 			ERROR_LOG(COMMON, "ChunkReader: File doesn't exist"); | ||||
| 			return ERROR_BAD_FILE; | ||||
| 		} | ||||
| 				 | ||||
| 		// Check file size
 | ||||
| 		const u64 fileSize = File::GetSize(_rFilename); | ||||
|  | @ -278,14 +684,14 @@ public: | |||
| 		if (fileSize < headerSize) | ||||
| 		{ | ||||
| 			ERROR_LOG(COMMON,"ChunkReader: File too small"); | ||||
| 			return false; | ||||
| 			return ERROR_BAD_FILE; | ||||
| 		} | ||||
| 
 | ||||
| 		File::IOFile pFile(_rFilename, "rb"); | ||||
| 		if (!pFile) | ||||
| 		{ | ||||
| 			ERROR_LOG(COMMON,"ChunkReader: Can't open file for reading"); | ||||
| 			return false; | ||||
| 			return ERROR_BAD_FILE; | ||||
| 		} | ||||
| 
 | ||||
| 		// read the header
 | ||||
|  | @ -293,91 +699,175 @@ public: | |||
| 		if (!pFile.ReadArray(&header, 1)) | ||||
| 		{ | ||||
| 			ERROR_LOG(COMMON,"ChunkReader: Bad header size"); | ||||
| 			return false; | ||||
| 			return ERROR_BAD_FILE; | ||||
| 		} | ||||
| 
 | ||||
| 		 | ||||
| 		// Check revision
 | ||||
| 		if (header.Revision != _Revision) | ||||
| 		{ | ||||
| 			ERROR_LOG(COMMON,"ChunkReader: Wrong file revision, got %d expected %d", | ||||
| 				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
 | ||||
| 		const u32 sz = (u32)(fileSize - headerSize); | ||||
| 		const int sz = (int)(fileSize - headerSize); | ||||
| 		if (header.ExpectedSize != sz) | ||||
| 		{ | ||||
| 			ERROR_LOG(COMMON,"ChunkReader: Bad file size, got %d expected %d", | ||||
| 				sz, header.ExpectedSize); | ||||
| 			return false; | ||||
| 			return ERROR_BAD_FILE; | ||||
| 		} | ||||
| 
 | ||||
| 		 | ||||
| 		// read the state
 | ||||
| 		std::vector<u8> buffer(sz); | ||||
| 		if (!pFile.ReadArray(&buffer[0], sz)) | ||||
| 		u8* buffer = new u8[sz]; | ||||
| 		if (!pFile.ReadBytes(buffer, sz)) | ||||
| 		{ | ||||
| 			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); | ||||
| 		_class.DoState(p); | ||||
| 		delete[] buf; | ||||
| 		 | ||||
| 		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
 | ||||
| 	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()); | ||||
| 
 | ||||
| 		File::IOFile pFile(_rFilename, "wb"); | ||||
| 		if (!pFile) | ||||
| 		{ | ||||
| 			ERROR_LOG(COMMON,"ChunkReader: Error opening file for write"); | ||||
| 			return false; | ||||
| 			return ERROR_BAD_FILE; | ||||
| 		} | ||||
| 
 | ||||
| 		bool compress = true; | ||||
| 
 | ||||
| 		// Get data
 | ||||
| 		u8 *ptr = 0; | ||||
| 		PointerWrap p(&ptr, PointerWrap::MODE_MEASURE); | ||||
| 		_class.DoState(p); | ||||
| 		size_t const sz = (size_t)ptr; | ||||
| 		std::vector<u8> buffer(sz); | ||||
| 		 | ||||
| 		u8 * buffer = new u8[sz]; | ||||
| 		ptr = &buffer[0]; | ||||
| 		p.SetMode(PointerWrap::MODE_WRITE); | ||||
| 		_class.DoState(p); | ||||
| 
 | ||||
| 		// Create header
 | ||||
| 		SChunkHeader header; | ||||
| 		header.Compress = compress ? 1 : 0; | ||||
| 		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
 | ||||
| 		if (!pFile.WriteArray(&header, 1)) | ||||
| 		{ | ||||
| 			ERROR_LOG(COMMON,"ChunkReader: Failed writing header"); | ||||
| 			return false; | ||||
| 		if (compress) { | ||||
| 			size_t comp_len = snappy_max_compressed_length(sz); | ||||
| 			u8 *compressed_buffer = new u8[comp_len]; | ||||
| 			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)) | ||||
| 		{ | ||||
| 			ERROR_LOG(COMMON,"ChunkReader: Failed writing data"); | ||||
| 			return false; | ||||
| 		 | ||||
| 		INFO_LOG(COMMON,"ChunkReader: Done writing %s",  | ||||
| 				 _rFilename.c_str()); | ||||
| 		if (p.error != p.ERROR_FAILURE) { | ||||
| 			return ERROR_NONE; | ||||
| 		} else { | ||||
| 			return ERROR_BROKEN_STATE; | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	template <class T> | ||||
| 	static Error Verify(T& _class) | ||||
| 	{ | ||||
| 		u8 *ptr = 0; | ||||
| 
 | ||||
| 		INFO_LOG(COMMON,"ChunkReader: Done writing %s", _rFilename.c_str()); | ||||
| 		return true; | ||||
| 		// 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: | ||||
| 	struct SChunkHeader | ||||
| 	{ | ||||
| 		u32 Revision; | ||||
| 		u32 ExpectedSize; | ||||
| 		int Revision; | ||||
| 		int Compress; | ||||
| 		int ExpectedSize; | ||||
| 		int UncompressedSize; | ||||
| 		char GitVersion[32]; | ||||
| 	}; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue