2012-11-01 15:19:01 +00:00
// 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
2012-11-04 22:01:49 +00:00
// the Free Software Foundation, version 2.0 or later versions.
2012-11-01 15:19:01 +00:00
// 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_
// Extremely simple serialization framework.
// (mis)-features:
// + Super fast
// + Very simple
// + Same code is used for serialization and deserializaition (in most cases)
// - Zero backwards/forwards compatibility
// - Serialization code for anything complex has to be manually written.
# include <map>
# include <vector>
# include <deque>
# include <string>
2012-12-27 05:07:29 +00:00
# include <list>
2012-12-27 06:45:19 +00:00
# include <set>
2013-02-03 23:55:56 +00:00
# ifndef __SYMBIAN32__
2013-05-06 22:08:57 +00:00
# if defined(IOS) || defined(MACGNUSTD)
2013-02-24 12:17:52 +00:00
# include <tr1/type_traits>
# else
2013-02-04 08:52:56 +00:00
# include <type_traits>
2013-02-03 23:55:56 +00:00
# endif
2013-02-24 12:17:52 +00:00
# endif
2012-11-01 15:19:01 +00:00
# include "Common.h"
# include "FileUtil.h"
2013-01-02 21:10:41 +00:00
# include "../ext/snappy/snappy-c.h"
2012-11-01 15:19:01 +00:00
2013-05-06 22:08:57 +00:00
# if defined(IOS) || defined(MACGNUSTD)
2013-02-04 19:19:00 +00:00
namespace std {
2013-04-13 19:32:01 +00:00
using tr1 : : is_pointer ;
2013-02-04 19:19:00 +00:00
}
# endif
2013-02-03 23:55:56 +00:00
# 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
2013-02-04 19:19:00 +00:00
2012-11-01 15:19:01 +00:00
template < class T >
struct LinkedListItem : public T
{
LinkedListItem < T > * next ;
} ;
2013-09-15 03:19:58 +00:00
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_ ;
} ;
2012-11-01 15:19:01 +00:00
// Wrapper class
class PointerWrap
{
2013-02-04 09:03:04 +00:00
// This makes it a compile error if you forget to define DoState() on non-POD.
2013-06-24 08:00:10 +00:00
// Which also can be a problem, for example struct tm is non-POD on linux, for whatever reason...
2013-02-04 19:19:00 +00:00
# ifdef _MSC_VER
2013-02-04 09:03:04 +00:00
template < typename T , bool isPOD = std : : is_pod < T > : : value , bool isPointer = std : : is_pointer < T > : : value >
2013-02-04 19:19:00 +00:00
# else
template < typename T , bool isPOD = __is_pod ( T ) , bool isPointer = std : : is_pointer < T > : : value >
# endif
2013-02-04 09:03:04 +00:00
struct DoHelper
{
static void DoArray ( PointerWrap * p , T * x , int count )
{
for ( int i = 0 ; i < count ; + + i )
2013-03-21 06:37:18 +00:00
p - > Do ( x [ i ] ) ;
2013-02-04 09:03:04 +00:00
}
static void Do ( PointerWrap * p , T & x )
{
p - > DoClass ( x ) ;
}
} ;
2013-02-04 08:52:56 +00:00
2013-02-04 09:03:04 +00:00
template < typename T >
struct DoHelper < T , true , false >
2013-02-04 08:52:56 +00:00
{
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 ) ) ;
}
} ;
2012-11-01 15:19:01 +00:00
public :
enum Mode {
MODE_READ = 1 , // load
MODE_WRITE , // save
MODE_MEASURE , // calculate size
MODE_VERIFY , // compare
} ;
2013-04-13 08:39:17 +00:00
enum Error {
ERROR_NONE = 0 ,
ERROR_WARNING = 1 ,
ERROR_FAILURE = 2 ,
} ;
2012-11-01 15:19:01 +00:00
u8 * * ptr ;
Mode mode ;
2013-04-13 08:39:17 +00:00
Error error ;
2012-11-01 15:19:01 +00:00
public :
2013-04-13 08:39:17 +00:00
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 ) { }
2012-11-01 15:19:01 +00:00
2013-09-15 03:19:58 +00:00
PointerWrapSection Section ( const char * title , int ver ) {
return Section ( title , ver , ver ) ;
}
// The returned object can be compared against the version that was loaded.
// This can be used to support versions as old as minVer.
PointerWrapSection Section ( const char * title , int minVer , int ver ) {
int foundVersion = ver ;
Do ( foundVersion ) ;
DoMarker ( title ) ;
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 ) ;
}
2012-11-01 15:19:01 +00:00
void SetMode ( Mode mode_ ) { mode = mode_ ; }
Mode GetMode ( ) const { return mode ; }
u8 * * GetPPtr ( ) { return ptr ; }
2013-04-13 08:39:17 +00:00
void SetError ( Error error_ )
{
if ( error < error_ )
error = error_ ;
2013-04-30 06:40:12 +00:00
if ( error > ERROR_WARNING )
2013-04-13 08:39:17 +00:00
mode = PointerWrap : : MODE_MEASURE ;
}
2012-11-01 15:19:01 +00:00
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 ;
}
2013-02-04 19:19:00 +00:00
2013-02-04 16:24:32 +00:00
template < class K , class T >
void Do ( std : : map < K , T * > & x )
2013-02-04 02:09:14 +00:00
{
2013-02-04 16:24:32 +00:00
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 ;
DoMap ( x , dv ) ;
2013-02-04 02:09:14 +00:00
}
template < class K , class T >
void Do ( std : : map < K , T > & x )
2013-02-04 16:24:32 +00:00
{
2013-07-08 01:24:53 +00:00
T dv = T ( ) ;
2013-02-04 16:24:32 +00:00
DoMap ( x , dv ) ;
}
template < class K , class T >
void DoMap ( std : : map < K , T > & x , T & default_val )
2012-11-01 15:19:01 +00:00
{
unsigned int number = ( unsigned int ) x . size ( ) ;
Do ( number ) ;
switch ( mode ) {
case MODE_READ :
{
x . clear ( ) ;
while ( number > 0 )
{
2013-07-08 00:57:52 +00:00
K first = K ( ) ;
2012-11-01 15:19:01 +00:00
Do ( first ) ;
2013-02-04 16:24:32 +00:00
T second = default_val ;
2012-11-01 15:19:01 +00:00
Do ( second ) ;
x [ first ] = second ;
- - number ;
}
}
break ;
case MODE_WRITE :
case MODE_MEASURE :
case MODE_VERIFY :
{
2013-02-04 02:09:14 +00:00
typename std : : map < K , T > : : iterator itr = x . begin ( ) ;
2012-11-01 15:19:01 +00:00
while ( number > 0 )
{
2013-07-05 07:00:41 +00:00
K first = itr - > first ;
Do ( first ) ;
2012-11-01 15:19:01 +00:00
Do ( itr - > second ) ;
- - number ;
+ + itr ;
}
}
break ;
}
}
2013-02-04 16:24:32 +00:00
template < class K , class T >
void Do ( std : : multimap < K , T * > & x )
{
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 ) ;
}
2012-12-27 22:21:39 +00:00
template < class K , class T >
void Do ( std : : multimap < K , T > & x )
2013-02-04 16:24:32 +00:00
{
2013-07-08 01:24:53 +00:00
T dv = T ( ) ;
2013-02-04 16:24:32 +00:00
DoMultimap ( x , dv ) ;
}
template < class K , class T >
void DoMultimap ( std : : multimap < K , T > & x , T & default_val )
2012-12-27 22:21:39 +00:00
{
unsigned int number = ( unsigned int ) x . size ( ) ;
Do ( number ) ;
switch ( mode ) {
case MODE_READ :
{
x . clear ( ) ;
while ( number > 0 )
{
2013-09-01 08:05:35 +00:00
K first = K ( ) ;
2012-12-27 22:21:39 +00:00
Do ( first ) ;
2013-02-04 16:24:32 +00:00
T second = default_val ;
2012-12-27 22:21:39 +00:00
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 ;
}
}
2012-11-01 15:19:01 +00:00
// Store vectors.
2013-02-04 16:24:32 +00:00
template < class T >
void Do ( std : : vector < T * > & x )
{
T * dv = NULL ;
DoVector ( x , dv ) ;
}
2012-11-01 15:19:01 +00:00
template < class T >
void Do ( std : : vector < T > & x )
2012-12-28 10:22:39 +00:00
{
2013-09-01 08:05:35 +00:00
T dv = T ( ) ;
2013-02-04 16:24:32 +00:00
DoVector ( x , dv ) ;
2012-12-28 10:22:39 +00:00
}
2013-02-04 19:19:00 +00:00
template < class T >
void DoPOD ( std : : vector < T > & x )
{
2013-09-01 08:05:35 +00:00
T dv = T ( ) ;
2013-02-04 19:19:00 +00:00
DoVectorPOD ( x , dv ) ;
}
2012-12-28 10:22:39 +00:00
template < class T >
void Do ( std : : vector < T > & x , T & default_val )
2013-02-04 16:24:32 +00:00
{
DoVector ( x , default_val ) ;
}
template < class T >
void DoVector ( std : : vector < T > & x , T & default_val )
2012-11-01 15:19:01 +00:00
{
u32 vec_size = ( u32 ) x . size ( ) ;
Do ( vec_size ) ;
2012-12-28 10:22:39 +00:00
x . resize ( vec_size , default_val ) ;
2012-12-28 06:14:31 +00:00
if ( vec_size > 0 )
DoArray ( & x [ 0 ] , vec_size ) ;
2012-11-01 15:19:01 +00:00
}
2013-02-04 19:19:00 +00:00
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 ) ;
}
2012-11-01 15:19:01 +00:00
// Store deques.
2013-02-04 16:24:32 +00:00
template < class T >
void Do ( std : : deque < T * > & x )
{
T * dv = NULL ;
DoDeque ( x , dv ) ;
}
2012-11-01 15:19:01 +00:00
template < class T >
void Do ( std : : deque < T > & x )
2013-02-04 16:24:32 +00:00
{
2013-09-01 08:05:35 +00:00
T dv = T ( ) ;
2013-02-04 16:24:32 +00:00
DoDeque ( x , dv ) ;
}
template < class T >
void DoDeque ( std : : deque < T > & x , T & default_val )
2012-11-01 15:19:01 +00:00
{
u32 deq_size = ( u32 ) x . size ( ) ;
Do ( deq_size ) ;
2013-02-04 16:24:32 +00:00
x . resize ( deq_size , default_val ) ;
2012-11-01 15:19:01 +00:00
u32 i ;
for ( i = 0 ; i < deq_size ; i + + )
2013-02-04 16:24:32 +00:00
Do ( x [ i ] ) ;
2012-11-01 15:19:01 +00:00
}
2012-12-27 05:07:29 +00:00
// Store STL lists.
2013-02-04 16:24:32 +00:00
template < class T >
void Do ( std : : list < T * > & x )
{
T * dv = NULL ;
Do ( x , dv ) ;
}
2012-12-27 05:07:29 +00:00
template < class T >
void Do ( std : : list < T > & x )
{
2013-09-01 08:05:35 +00:00
T dv = T ( ) ;
2013-02-04 16:24:32 +00:00
DoList ( x , dv ) ;
2012-12-27 05:07:29 +00:00
}
template < class T >
void Do ( std : : list < T > & x , T & default_val )
2013-02-04 16:24:32 +00:00
{
DoList ( x , default_val ) ;
}
template < class T >
void DoList ( std : : list < T > & x , T & default_val )
2012-12-27 05:07:29 +00:00
{
u32 list_size = ( u32 ) x . size ( ) ;
2012-12-27 06:45:19 +00:00
Do ( list_size ) ;
2012-12-27 05:07:29 +00:00
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 ) ;
}
2013-02-04 19:19:00 +00:00
2012-12-27 06:45:19 +00:00
// Store STL sets.
2013-02-04 16:24:32 +00:00
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 ) ;
}
2012-12-27 06:45:19 +00:00
template < class T >
void Do ( std : : set < T > & x )
2013-02-04 16:24:32 +00:00
{
DoSet ( x ) ;
}
template < class T >
void DoSet ( std : : set < T > & x )
2012-12-27 06:45:19 +00:00
{
unsigned int number = ( unsigned int ) x . size ( ) ;
Do ( number ) ;
switch ( mode )
{
case MODE_READ :
{
x . clear ( ) ;
while ( number - - > 0 )
{
2013-09-01 08:05:35 +00:00
T it = T ( ) ;
2012-12-27 06:45:19 +00:00
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 ) ;
}
}
2012-11-01 15:19:01 +00:00
// 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 ;
2013-01-07 14:57:19 +00:00
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 ;
2012-11-01 15:19:01 +00:00
}
( * ptr ) + = stringLen ;
}
2013-02-04 09:03:04 +00:00
template < class T >
void DoClass ( T & x ) {
x . DoState ( * this ) ;
}
template < class T >
void DoClass ( T * & x ) {
if ( mode = = MODE_READ )
2013-02-04 16:24:32 +00:00
{
if ( x ! = NULL )
delete x ;
2013-02-04 09:03:04 +00:00
x = new T ( ) ;
2013-02-04 16:24:32 +00:00
}
2013-02-04 09:03:04 +00:00
x - > DoState ( * this ) ;
}
2013-02-04 08:52:56 +00:00
template < class T >
2012-11-01 15:19:01 +00:00
void DoArray ( T * x , int count ) {
2013-02-04 09:39:52 +00:00
DoHelper < T > : : DoArray ( this , x , count ) ;
2013-02-04 08:52:56 +00:00
}
2013-02-04 19:19:00 +00:00
2012-11-01 15:19:01 +00:00
template < class T >
void Do ( T & x ) {
2013-02-04 08:52:56 +00:00
DoHelper < T > : : Do ( this , x ) ;
2012-11-01 15:19:01 +00:00
}
2013-02-04 19:19:00 +00:00
template < class T >
void DoPOD ( T & x ) {
DoHelper < T > : : Do ( this , x ) ;
}
2012-11-01 15:19:01 +00:00
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 ) ;
if ( mode = = MODE_READ )
x = base + offset ;
}
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 ;
LinkedListItem < T > * prev = 0 ;
while ( true )
{
u8 shouldExist = ( list_cur ? 1 : 0 ) ;
Do ( shouldExist ) ;
if ( shouldExist = = 1 )
{
LinkedListItem < T > * cur = list_cur ? list_cur : TNew ( ) ;
TDo ( * this , ( T * ) cur ) ;
if ( ! list_cur )
{
if ( mode = = MODE_READ )
{
cur - > next = 0 ;
list_cur = cur ;
if ( prev )
prev - > next = cur ;
else
list_start = cur ;
}
else
{
TFree ( cur ) ;
continue ;
}
}
}
else
{
if ( mode = = MODE_READ )
{
if ( prev )
prev - > next = 0 ;
if ( list_end )
* list_end = prev ;
if ( list_cur )
{
if ( list_start = = list_cur )
list_start = 0 ;
do
{
LinkedListItem < T > * next = list_cur - > next ;
TFree ( list_cur ) ;
list_cur = next ;
}
while ( list_cur ) ;
}
}
break ;
}
prev = list_cur ;
list_cur = list_cur - > next ;
}
}
void DoMarker ( const char * prevName , u32 arbitraryNumber = 0x42 )
{
u32 cookie = arbitraryNumber ;
Do ( cookie ) ;
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 ) ;
2013-04-13 08:39:17 +00:00
SetError ( ERROR_FAILURE ) ;
2012-11-01 15:19:01 +00:00
}
}
} ;
2013-09-15 03:19:58 +00:00
inline PointerWrapSection : : ~ PointerWrapSection ( ) {
p_ . DoMarker ( title_ ) ;
}
2012-11-01 15:19:01 +00:00
class CChunkFileReader
{
public :
2013-09-15 04:19:10 +00:00
enum Error {
ERROR_NONE ,
ERROR_BAD_FILE ,
ERROR_BROKEN_STATE ,
} ;
2012-11-01 15:19:01 +00:00
// Load file template
template < class T >
2013-09-15 04:19:10 +00:00
static Error Load ( const std : : string & _rFilename , int _Revision , const char * _VersionString , T & _class , std : : string * _failureReason )
2012-11-01 15:19:01 +00:00
{
INFO_LOG ( COMMON , " ChunkReader: Loading %s " , _rFilename . c_str ( ) ) ;
2013-06-24 08:47:12 +00:00
_failureReason - > clear ( ) ;
_failureReason - > append ( " LoadStateWrongVersion " ) ;
2012-11-01 15:19:01 +00:00
2013-06-24 06:58:21 +00:00
if ( ! File : : Exists ( _rFilename ) ) {
2013-06-24 08:47:12 +00:00
_failureReason - > clear ( ) ;
_failureReason - > append ( " LoadStateDoesntExist " ) ;
2013-06-24 06:58:21 +00:00
ERROR_LOG ( COMMON , " ChunkReader: File doesn't exist " ) ;
2013-09-15 04:19:10 +00:00
return ERROR_BAD_FILE ;
2013-06-24 06:58:21 +00:00
}
2012-11-01 15:19:01 +00:00
// Check file size
const u64 fileSize = File : : GetSize ( _rFilename ) ;
static const u64 headerSize = sizeof ( SChunkHeader ) ;
if ( fileSize < headerSize )
{
ERROR_LOG ( COMMON , " ChunkReader: File too small " ) ;
2013-09-15 04:19:10 +00:00
return ERROR_BAD_FILE ;
2012-11-01 15:19:01 +00:00
}
File : : IOFile pFile ( _rFilename , " rb " ) ;
if ( ! pFile )
{
ERROR_LOG ( COMMON , " ChunkReader: Can't open file for reading " ) ;
2013-09-15 04:19:10 +00:00
return ERROR_BAD_FILE ;
2012-11-01 15:19:01 +00:00
}
// read the header
SChunkHeader header ;
if ( ! pFile . ReadArray ( & header , 1 ) )
{
ERROR_LOG ( COMMON , " ChunkReader: Bad header size " ) ;
2013-09-15 04:19:10 +00:00
return ERROR_BAD_FILE ;
2012-11-01 15:19:01 +00:00
}
// Check revision
if ( header . Revision ! = _Revision )
{
ERROR_LOG ( COMMON , " ChunkReader: Wrong file revision, got %d expected %d " ,
header . Revision , _Revision ) ;
2013-09-15 04:19:10 +00:00
return ERROR_BAD_FILE ;
2012-11-01 15:19:01 +00:00
}
2013-09-15 03:28:41 +00:00
if ( strcmp ( header . GitVersion , _VersionString ) ! = 0 )
2013-07-17 05:33:26 +00:00
{
2013-07-18 18:24:07 +00:00
WARN_LOG ( COMMON , " This savestate was generated by a different version of PPSSPP, %s. It may not load properly. " ,
header . GitVersion ) ;
2013-07-17 05:33:26 +00:00
}
2012-11-01 15:19:01 +00:00
// get size
const int sz = ( int ) ( fileSize - headerSize ) ;
if ( header . ExpectedSize ! = sz )
{
ERROR_LOG ( COMMON , " ChunkReader: Bad file size, got %d expected %d " ,
sz , header . ExpectedSize ) ;
2013-09-15 04:19:10 +00:00
return ERROR_BAD_FILE ;
2012-11-01 15:19:01 +00:00
}
// read the state
u8 * buffer = new u8 [ sz ] ;
if ( ! pFile . ReadBytes ( buffer , sz ) )
{
ERROR_LOG ( COMMON , " ChunkReader: Error reading file " ) ;
2013-09-15 04:19:10 +00:00
return ERROR_BAD_FILE ;
2012-11-01 15:19:01 +00:00
}
u8 * ptr = buffer ;
2013-01-02 21:10:41 +00:00
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 ) ;
2013-02-18 16:44:59 +00:00
if ( ( int ) uncomp_size ! = header . UncompressedSize ) {
2013-01-02 21:10:41 +00:00
ERROR_LOG ( COMMON , " Size mismatch: file: %i calc: %i " , ( int ) header . UncompressedSize , ( int ) uncomp_size ) ;
}
ptr = uncomp_buffer ;
buf = uncomp_buffer ;
delete [ ] buffer ;
}
2012-11-01 15:19:01 +00:00
PointerWrap p ( & ptr , PointerWrap : : MODE_READ ) ;
_class . DoState ( p ) ;
2013-01-02 21:10:41 +00:00
delete [ ] buf ;
2012-11-01 15:19:01 +00:00
INFO_LOG ( COMMON , " ChunkReader: Done loading %s " , _rFilename . c_str ( ) ) ;
2013-09-15 04:19:10 +00:00
if ( p . error ! = p . ERROR_FAILURE ) {
return ERROR_NONE ;
} else {
return ERROR_BROKEN_STATE ;
}
2012-11-01 15:19:01 +00:00
}
// Save file template
template < class T >
2013-09-15 04:19:10 +00:00
static Error Save ( const std : : string & _rFilename , int _Revision , const char * _VersionString , T & _class )
2012-11-01 15:19:01 +00:00
{
INFO_LOG ( COMMON , " ChunkReader: Writing %s " , _rFilename . c_str ( ) ) ;
2013-06-24 07:08:24 +00:00
2012-11-01 15:19:01 +00:00
File : : IOFile pFile ( _rFilename , " wb " ) ;
if ( ! pFile )
{
ERROR_LOG ( COMMON , " ChunkReader: Error opening file for write " ) ;
2013-09-15 04:19:10 +00:00
return ERROR_BAD_FILE ;
2012-11-01 15:19:01 +00:00
}
2013-01-02 21:10:41 +00:00
bool compress = true ;
2012-11-01 15:19:01 +00:00
// Get data
u8 * ptr = 0 ;
PointerWrap p ( & ptr , PointerWrap : : MODE_MEASURE ) ;
_class . DoState ( p ) ;
size_t const sz = ( size_t ) ptr ;
2013-01-02 21:10:41 +00:00
u8 * buffer = new u8 [ sz ] ;
2012-11-01 15:19:01 +00:00
ptr = & buffer [ 0 ] ;
p . SetMode ( PointerWrap : : MODE_WRITE ) ;
_class . DoState ( p ) ;
2013-01-02 21:10:41 +00:00
2012-11-01 15:19:01 +00:00
// Create header
SChunkHeader header ;
2013-01-02 21:10:41 +00:00
header . Compress = compress ? 1 : 0 ;
2012-11-01 15:19:01 +00:00
header . Revision = _Revision ;
header . ExpectedSize = ( int ) sz ;
2013-01-02 21:10:41 +00:00
header . UncompressedSize = ( int ) sz ;
2013-09-15 03:28:41 +00:00
strncpy ( header . GitVersion , _VersionString , 32 ) ;
header . GitVersion [ 31 ] = ' \0 ' ;
2013-07-17 05:33:26 +00:00
2012-11-01 15:19:01 +00:00
// Write to file
2013-01-02 21:10:41 +00:00
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 ;
2013-01-19 21:48:20 +00:00
header . ExpectedSize = ( int ) comp_len ;
2013-01-02 21:10:41 +00:00
if ( ! pFile . WriteArray ( & header , 1 ) )
{
ERROR_LOG ( COMMON , " ChunkReader: Failed writing header " ) ;
2013-09-15 04:19:10 +00:00
return ERROR_BAD_FILE ;
2013-01-02 21:10:41 +00:00
}
if ( ! pFile . WriteBytes ( & compressed_buffer [ 0 ] , comp_len ) ) {
ERROR_LOG ( COMMON , " ChunkReader: Failed writing compressed data " ) ;
2013-09-15 04:19:10 +00:00
return ERROR_BAD_FILE ;
2013-01-02 21:10:41 +00:00
} 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 " ) ;
2013-09-15 04:19:10 +00:00
return ERROR_BAD_FILE ;
2013-01-02 21:10:41 +00:00
}
if ( ! pFile . WriteBytes ( & buffer [ 0 ] , sz ) )
{
ERROR_LOG ( COMMON , " ChunkReader: Failed writing data " ) ;
2013-09-15 04:19:10 +00:00
return ERROR_BAD_FILE ;
2013-01-02 21:10:41 +00:00
}
delete [ ] buffer ;
2012-11-01 15:19:01 +00:00
}
INFO_LOG ( COMMON , " ChunkReader: Done writing %s " ,
_rFilename . c_str ( ) ) ;
2013-09-15 04:19:10 +00:00
if ( p . error ! = p . ERROR_FAILURE ) {
return ERROR_NONE ;
} else {
return ERROR_BROKEN_STATE ;
}
2012-11-01 15:19:01 +00:00
}
2012-12-27 18:34:22 +00:00
template < class T >
2013-09-15 04:19:10 +00:00
static Error Verify ( T & _class )
2012-12-27 18:34:22 +00:00
{
u8 * ptr = 0 ;
// 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 ) ;
2013-09-15 04:19:10 +00:00
return ERROR_NONE ;
2012-12-27 18:34:22 +00:00
}
2012-11-01 15:19:01 +00:00
private :
struct SChunkHeader
{
int Revision ;
int Compress ;
int ExpectedSize ;
2013-01-02 21:10:41 +00:00
int UncompressedSize ;
2013-07-17 05:33:26 +00:00
char GitVersion [ 32 ] ;
2012-11-01 15:19:01 +00:00
} ;
} ;
# endif // _POINTERWRAP_H_