From a5b31dea56e9a966ec44345c08b1478a2867f4f8 Mon Sep 17 00:00:00 2001
From: ShizZy <shizzy@6bit.net>
Date: Thu, 19 Sep 2013 23:28:05 -0400
Subject: [PATCH] updated to chunk_file module from ppsspp

---
 src/common/src/chunk_file.h | 770 +++++++++++++++++++++++++++++-------
 1 file changed, 630 insertions(+), 140 deletions(-)

diff --git a/src/common/src/chunk_file.h b/src/common/src/chunk_file.h
index 0c333b094b..c480bc8ea7 100644
--- a/src/common/src/chunk_file.h
+++ b/src/common/src/chunk_file.h
@@ -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];
 	};
 };