mirror of
				https://git.tardis.systems/mirrors/yuzu
				synced 2025-11-04 12:45:03 +01:00 
			
		
		
		
	updated to chunk_file module from ppsspp
This commit is contained in:
		
							parent
							
								
									8990b51ac8
								
							
						
					
					
						commit
						a5b31dea56
					
				@ -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…
	
		Reference in New Issue
	
	Block a user