1 // Copyright (C) 2003 Dolphin Project.
2 
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
6 
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 // GNU General Public License 2.0 for more details.
11 
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
14 
15 // Official SVN repository and contact information can be found at
16 // http://code.google.com/p/dolphin-emu/
17 
18 #pragma once
19 
20 #include "ppsspp_config.h"
21 
22 // Extremely simple serialization framework.
23 // Currently mis-named, a native ChunkFile is something different (a RIFF file)
24 
25 // (mis)-features:
26 // + Super fast
27 // + Very simple
28 // + Same code is used for serialization and deserializaition (in most cases)
29 // + Sections can be versioned for backwards/forwards compatibility
30 // - Serialization code for anything complex has to be manually written.
31 
32 #include <string>
33 #include <vector>
34 #include <cstdlib>
35 
36 #include "Common/CommonTypes.h"
37 #include "Common/Log.h"
38 #include "Common/File/Path.h"
39 
40 namespace File {
41 class IOFile;
42 };
43 
44 template <class T>
45 struct LinkedListItem : public T
46 {
47 	LinkedListItem<T> *next;
48 };
49 
50 class PointerWrap;
51 
52 class PointerWrapSection
53 {
54 public:
PointerWrapSection(PointerWrap & p,int ver,const char * title)55 	PointerWrapSection(PointerWrap &p, int ver, const char *title) : p_(p), ver_(ver), title_(title) {
56 	}
57 	~PointerWrapSection();
58 
59 	bool operator == (const int &v) const { return ver_ == v; }
60 	bool operator != (const int &v) const { return ver_ != v; }
61 	bool operator <= (const int &v) const { return ver_ <= v; }
62 	bool operator >= (const int &v) const { return ver_ >= v; }
63 	bool operator <  (const int &v) const { return ver_ < v; }
64 	bool operator >  (const int &v) const { return ver_ > v; }
65 
66 	operator bool() const  {
67 		return ver_ > 0;
68 	}
69 
70 private:
71 	PointerWrap &p_;
72 	int ver_;
73 	const char *title_;
74 };
75 
76 // Wrapper class
77 class PointerWrap
78 {
79 public:
80 	enum Mode {
81 		MODE_READ = 1, // load
82 		MODE_WRITE, // save
83 		MODE_MEASURE, // calculate size
84 		MODE_VERIFY, // compare
85 	};
86 
87 	enum Error {
88 		ERROR_NONE = 0,
89 		ERROR_WARNING = 1,
90 		ERROR_FAILURE = 2,
91 	};
92 
93 	u8 **ptr;
94 	Mode mode;
95 	Error error = ERROR_NONE;
96 
PointerWrap(u8 ** ptr_,Mode mode_)97 	PointerWrap(u8 **ptr_, Mode mode_) : ptr(ptr_), mode(mode_) {}
PointerWrap(unsigned char ** ptr_,int mode_)98 	PointerWrap(unsigned char **ptr_, int mode_) : ptr((u8**)ptr_), mode((Mode)mode_) {}
99 
100 	PointerWrapSection Section(const char *title, int ver);
101 
102 	// The returned object can be compared against the version that was loaded.
103 	// This can be used to support versions as old as minVer.
104 	// Version = 0 means the section was not found.
105 	PointerWrapSection Section(const char *title, int minVer, int ver);
106 
SetMode(Mode mode_)107 	void SetMode(Mode mode_) {mode = mode_;}
GetMode()108 	Mode GetMode() const {return mode;}
GetPPtr()109 	u8 **GetPPtr() {return ptr;}
110 	void SetError(Error error_);
111 
GetBadSectionTitle()112 	const char *GetBadSectionTitle() const {
113 		return firstBadSectionTitle_;
114 	}
115 
116 	// Same as DoVoid, except doesn't advance pointer if it doesn't match on read.
117 	bool ExpectVoid(void *data, int size);
118 	void DoVoid(void *data, int size);
119 
120 	void DoMarker(const char *prevName, u32 arbitraryNumber = 0x42);
121 
122 private:
123 	const char *firstBadSectionTitle_ = nullptr;
124 };
125 
126 class CChunkFileReader
127 {
128 public:
129 	enum Error {
130 		ERROR_NONE,
131 		ERROR_BAD_FILE,
132 		ERROR_BROKEN_STATE,
133 		ERROR_BAD_ALLOC,
134 	};
135 
136 	// May fail badly if ptr doesn't point to valid data.
137 	template<class T>
LoadPtr(u8 * ptr,T & _class,std::string * errorString)138 	static Error LoadPtr(u8 *ptr, T &_class, std::string *errorString)
139 	{
140 		PointerWrap p(&ptr, PointerWrap::MODE_READ);
141 		_class.DoState(p);
142 
143 		if (p.error != p.ERROR_FAILURE) {
144 			return ERROR_NONE;
145 		} else {
146 			std::string badSectionTitle = p.GetBadSectionTitle() ? p.GetBadSectionTitle() : "(unknown bad section)";
147 			*errorString = std::string("Failure at ") + badSectionTitle;
148 			return ERROR_BROKEN_STATE;
149 		}
150 	}
151 
152 	template<class T>
MeasurePtr(T & _class)153 	static size_t MeasurePtr(T &_class)
154 	{
155 		u8 *ptr = 0;
156 		PointerWrap p(&ptr, PointerWrap::MODE_MEASURE);
157 		_class.DoState(p);
158 		return (size_t)ptr;
159 	}
160 
161 	// Expects ptr to have at least MeasurePtr bytes at ptr.
162 	template<class T>
SavePtr(u8 * ptr,T & _class,size_t expected_size)163 	static Error SavePtr(u8 *ptr, T &_class, size_t expected_size)
164 	{
165 		const u8 *expected_end = ptr + expected_size;
166 		PointerWrap p(&ptr, PointerWrap::MODE_WRITE);
167 		_class.DoState(p);
168 
169 		if (p.error != p.ERROR_FAILURE && (expected_end == ptr || expected_size == 0)) {
170 			return ERROR_NONE;
171 		} else {
172 			return ERROR_BROKEN_STATE;
173 		}
174 	}
175 
176 	// Load file template
177 	template<class T>
Load(const Path & filename,std::string * gitVersion,T & _class,std::string * failureReason)178 	static Error Load(const Path &filename, std::string *gitVersion, T& _class, std::string *failureReason)
179 	{
180 		*failureReason = "LoadStateWrongVersion";
181 
182 		u8 *ptr = nullptr;
183 		size_t sz;
184 		Error error = LoadFile(filename, gitVersion, ptr, sz, failureReason);
185 		if (error == ERROR_NONE) {
186 			failureReason->clear();
187 			error = LoadPtr(ptr, _class, failureReason);
188 			delete [] ptr;
189 			INFO_LOG(SAVESTATE, "ChunkReader: Done loading '%s'", filename.c_str());
190 		} else {
191 			WARN_LOG(SAVESTATE, "ChunkReader: Error found during load of '%s'", filename.c_str());
192 		}
193 		return error;
194 	}
195 
196 	// Save file template
197 	template<class T>
Save(const Path & filename,const std::string & title,const char * gitVersion,T & _class)198 	static Error Save(const Path &filename, const std::string &title, const char *gitVersion, T& _class)
199 	{
200 		// Get data
201 		size_t const sz = MeasurePtr(_class);
202 		u8 *buffer = (u8 *)malloc(sz);
203 		if (!buffer)
204 			return ERROR_BAD_ALLOC;
205 		Error error = SavePtr(buffer, _class, sz);
206 
207 		// SaveFile takes ownership of buffer
208 		if (error == ERROR_NONE)
209 			error = SaveFile(filename, title, gitVersion, buffer, sz);
210 		return error;
211 	}
212 
213 	template <class T>
Verify(T & _class)214 	static Error Verify(T& _class)
215 	{
216 		u8 *ptr = 0;
217 
218 		// Step 1: Measure the space required.
219 		PointerWrap p(&ptr, PointerWrap::MODE_MEASURE);
220 		_class.DoState(p);
221 		size_t const sz = (size_t)ptr;
222 		std::vector<u8> buffer(sz);
223 
224 		// Step 2: Dump the state.
225 		ptr = &buffer[0];
226 		p.SetMode(PointerWrap::MODE_WRITE);
227 		_class.DoState(p);
228 
229 		// Step 3: Verify the state.
230 		ptr = &buffer[0];
231 		p.SetMode(PointerWrap::MODE_VERIFY);
232 		_class.DoState(p);
233 
234 		return ERROR_NONE;
235 	}
236 
237 	static Error GetFileTitle(const Path &filename, std::string *title);
238 
239 private:
240 	struct SChunkHeader
241 	{
242 		int Revision;
243 		int Compress;
244 		u32 ExpectedSize;
245 		u32 UncompressedSize;
246 		char GitVersion[32];
247 	};
248 
249 	enum {
250 		REVISION_MIN = 4,
251 		REVISION_TITLE = 5,
252 		REVISION_CURRENT = REVISION_TITLE,
253 	};
254 
255 	static Error LoadFile(const Path &filename, std::string *gitVersion, u8 *&buffer, size_t &sz, std::string *failureReason);
256 	static Error SaveFile(const Path &filename, const std::string &title, const char *gitVersion, u8 *buffer, size_t sz);
257 	static Error LoadFileHeader(File::IOFile &pFile, SChunkHeader &header, std::string *title);
258 };
259