2013-09-19 23:28:05 -04:00
// Copyright (C) 2003 Dolphin Project.
2013-09-04 20:17:46 -04:00
2013-09-19 23:28:05 -04:00
// 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/
2013-09-04 20:17:46 -04:00
2014-08-17 13:45:50 -04:00
# pragma once
2013-09-04 20:17:46 -04:00
// 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.
2015-06-20 17:24:01 -04:00
# include <cstring>
2013-09-04 20:17:46 -04:00
# include <deque>
2013-09-19 23:28:05 -04:00
# include <list>
2015-06-20 17:24:01 -04:00
# include <map>
2013-09-19 23:28:05 -04:00
# include <set>
2015-06-20 17:24:01 -04:00
# include <string>
2013-09-04 20:17:46 -04:00
# include <type_traits>
2015-06-20 17:24:01 -04:00
# include <vector>
2013-09-04 20:17:46 -04:00
2015-05-06 03:06:12 -04:00
# include "common/common_types.h"
2014-04-08 20:15:08 -04:00
# include "common/file_util.h"
2015-05-06 03:06:12 -04:00
# include "common/logging/log.h"
2013-09-04 20:17:46 -04:00
template < class T >
struct LinkedListItem : public T
{
2014-04-01 18:20:08 -04:00
LinkedListItem < T > * next ;
2013-09-04 20:17:46 -04:00
} ;
2013-09-19 23:28:05 -04:00
class PointerWrap ;
class PointerWrapSection
{
public :
2014-04-01 18:20:08 -04:00
PointerWrapSection ( PointerWrap & p , int ver , const char * title ) : p_ ( p ) , ver_ ( ver ) , title_ ( title ) {
}
~ PointerWrapSection ( ) ;
2014-11-19 03:49:13 -05:00
2014-04-01 18:20:08 -04:00
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 ;
}
2013-09-19 23:28:05 -04:00
private :
2014-04-01 18:20:08 -04:00
PointerWrap & p_ ;
int ver_ ;
const char * title_ ;
2013-09-19 23:28:05 -04:00
} ;
2013-09-04 20:17:46 -04:00
// Wrapper class
class PointerWrap
{
2014-04-01 18:20:08 -04:00
// 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...
2013-09-19 23:28:05 -04:00
# ifdef _MSC_VER
2014-04-01 18:20:08 -04:00
template < typename T , bool isPOD = std : : is_pod < T > : : value , bool isPointer = std : : is_pointer < T > : : value >
2013-09-19 23:28:05 -04:00
# else
2014-04-01 18:20:08 -04:00
template < typename T , bool isPOD = __is_pod ( T ) , bool isPointer = std : : is_pointer < T > : : value >
2013-09-19 23:28:05 -04:00
# endif
2014-04-01 18:20:08 -04:00
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 ) ) ;
}
} ;
2013-09-19 23:28:05 -04:00
public :
2014-04-01 18:20:08 -04:00
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 ;
2013-09-04 20:17:46 -04:00
public :
2014-04-01 18:20:08 -04: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 ) { }
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.
// Version = 0 means the section was not found.
PointerWrapSection Section ( const char * title , int minVer , int ver ) {
char marker [ 16 ] = { 0 } ;
int foundVersion = ver ;
strncpy ( marker , title , sizeof ( marker ) ) ;
if ( ! ExpectVoid ( marker , sizeof ( marker ) ) )
{
// 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 ) {
2014-12-05 20:53:49 -05:00
LOG_ERROR ( Common , " Savestate failure: wrong version %d found for %s " , foundVersion , title ) ;
2014-04-01 18:20:08 -04:00
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
2014-12-05 20:53:49 -05:00
case MODE_VERIFY :
for ( int i = 0 ; i < size ; i + + ) {
2015-01-20 20:16:47 -05:00
DEBUG_ASSERT_MSG ( ( ( u8 * ) data ) [ i ] = = ( * ptr ) [ i ] ,
2014-12-05 20:53:49 -05:00
" 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 ;
2014-04-01 18:20:08 -04:00
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
2014-12-05 20:53:49 -05:00
case MODE_VERIFY :
for ( int i = 0 ; i < size ; i + + ) {
2015-01-20 20:16:47 -05:00
DEBUG_ASSERT_MSG ( ( ( u8 * ) data ) [ i ] = = ( * ptr ) [ i ] ,
2014-12-05 20:53:49 -05:00
" 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 ;
2014-04-01 18:20:08 -04:00
default : break ; // throw an error?
}
( * ptr ) + = size ;
}
2014-11-19 03:49:13 -05:00
2014-04-01 18:20:08 -04:00
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 )
{
2014-12-03 13:57:57 -05:00
if ( it - > second ! = nullptr )
2014-04-01 18:20:08 -04:00
delete it - > second ;
}
}
2014-12-03 13:57:57 -05:00
T * dv = nullptr ;
2014-04-01 18:20:08 -04:00
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 :
{
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 < 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 )
{
2014-12-03 13:57:57 -05:00
if ( it - > second ! = nullptr )
2014-04-01 18:20:08 -04:00
delete it - > second ;
}
}
2014-12-03 13:57:57 -05:00
T * dv = nullptr ;
2014-04-01 18:20:08 -04:00
DoMultimap ( x , dv ) ;
}
template < class K , class T >
void Do ( std : : multimap < K , T > & x )
{
T dv = T ( ) ;
DoMultimap ( x , dv ) ;
}
template < class K , class T >
void DoMultimap ( std : : multimap < 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 . 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 ;
}
}
// Store vectors.
template < class T >
void Do ( std : : vector < T * > & x )
{
2014-12-03 13:57:57 -05:00
T * dv = nullptr ;
2014-04-01 18:20:08 -04:00
DoVector ( x , dv ) ;
}
template < class T >
void Do ( std : : vector < T > & x )
{
T dv = T ( ) ;
DoVector ( x , dv ) ;
}
template < class T >
void DoPOD ( std : : vector < T > & x )
{
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 ) ;
}
2014-11-19 03:49:13 -05:00
2014-04-01 18:20:08 -04:00
// Store deques.
template < class T >
void Do ( std : : deque < T * > & x )
{
2014-12-03 13:57:57 -05:00
T * dv = nullptr ;
2014-04-01 18:20:08 -04:00
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 ] ) ;
}
// Store STL lists.
template < class T >
void Do ( std : : list < T * > & x )
{
2014-12-03 13:57:57 -05:00
T * dv = nullptr ;
2014-04-01 18:20:08 -04:00
Do ( x , dv ) ;
}
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 )
{
2014-12-03 13:57:57 -05:00
if ( * it ! = nullptr )
2014-04-01 18:20:08 -04:00
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 :
2014-12-05 20:53:49 -05:00
LOG_ERROR ( Common , " Savestate error: invalid mode %d. " , mode ) ;
2014-04-01 18:20:08 -04:00
}
}
// Store strings.
2014-11-19 03:49:13 -05:00
void Do ( std : : string & x )
2014-04-01 18:20:08 -04:00
{
int stringLen = ( int ) x . length ( ) + 1 ;
Do ( stringLen ) ;
2014-11-19 03:49:13 -05:00
2014-04-01 18:20:08 -04:00
switch ( mode ) {
case MODE_READ : x = ( char * ) * ptr ; break ;
case MODE_WRITE : memcpy ( * ptr , x . c_str ( ) , stringLen ) ; break ;
case MODE_MEASURE : break ;
2014-12-05 20:53:49 -05:00
case MODE_VERIFY :
2015-01-20 20:16:47 -05:00
DEBUG_ASSERT_MSG ( ( x = = ( char * ) * ptr ) ,
2014-12-05 20:53:49 -05:00
" Savestate verification failure: \" %s \" != \" %s \" (at %p). \n " ,
x . c_str ( ) , ( char * ) * ptr , ptr ) ;
break ;
2014-04-01 18:20:08 -04:00
}
( * ptr ) + = stringLen ;
}
2014-11-19 03:49:13 -05:00
void Do ( std : : wstring & x )
2014-04-01 18:20:08 -04:00
{
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 ;
2014-12-05 20:53:49 -05:00
case MODE_VERIFY :
2015-01-20 20:16:47 -05:00
DEBUG_ASSERT_MSG ( ( x = = ( wchar_t * ) * ptr ) ,
2014-12-05 20:53:49 -05:00
" Savestate verification failure: \" %ls \" != \" %ls \" (at %p). \n " ,
x . c_str ( ) , ( wchar_t * ) * ptr , ptr ) ;
break ;
2014-04-01 18:20:08 -04:00
}
( * ptr ) + = stringLen ;
}
template < class T >
void DoClass ( T & x ) {
x . DoState ( * this ) ;
}
template < class T >
void DoClass ( T * & x ) {
if ( mode = = MODE_READ )
{
2014-12-03 13:57:57 -05:00
if ( x ! = nullptr )
2014-04-01 18:20:08 -04:00
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 ) ;
}
2014-11-19 03:49:13 -05:00
2014-04-01 18:20:08 -04:00
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 ) ;
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 )
{
2014-12-03 13:57:57 -05:00
cur - > next = nullptr ;
2014-04-01 18:20:08 -04:00
list_cur = cur ;
if ( prev )
prev - > next = cur ;
else
list_start = cur ;
}
else
{
TFree ( cur ) ;
continue ;
}
}
}
else
{
if ( mode = = MODE_READ )
{
if ( prev )
2014-12-03 13:57:57 -05:00
prev - > next = nullptr ;
2014-04-01 18:20:08 -04:00
if ( list_end )
* list_end = prev ;
if ( list_cur )
{
if ( list_start = = list_cur )
2014-12-03 13:57:57 -05:00
list_start = nullptr ;
2014-04-01 18:20:08 -04:00
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 )
{
2015-02-19 01:18:47 -05:00
LOG_ERROR ( Common , " After \" %s \" , found %d (0x%X) instead of save marker %d (0x%X). Aborting savestate load... " , prevName , cookie , cookie , arbitraryNumber , arbitraryNumber ) ;
2014-04-01 18:20:08 -04:00
SetError ( ERROR_FAILURE ) ;
}
}
2013-09-19 23:28:05 -04:00
} ;
2013-09-04 20:17:46 -04:00
2013-09-19 23:28:05 -04:00
inline PointerWrapSection : : ~ PointerWrapSection ( ) {
2014-04-01 18:20:08 -04:00
if ( ver_ > 0 ) {
p_ . DoMarker ( title_ ) ;
}
2013-09-19 23:28:05 -04:00
}