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