2009-07-28 15:32:10 -06:00
// Copyright (C) 2003 Dolphin Project.
2008-12-07 21:46:09 -07: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.
// 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/
2009-03-28 02:57:34 -06:00
# ifndef _POINTERWRAP_H_
# define _POINTERWRAP_H_
2008-12-07 21:46:09 -07: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.
# include <map>
# include <vector>
2011-05-27 13:55:07 -06:00
# include <deque>
2008-12-07 21:46:09 -07:00
# include <string>
# include "Common.h"
2009-02-27 18:26:56 -07:00
# include "FileUtil.h"
2008-12-07 21:46:09 -07:00
template < class T >
struct LinkedListItem : public T
{
LinkedListItem < T > * next ;
} ;
2009-02-27 18:26:56 -07:00
// Wrapper class
2008-12-07 21:46:09 -07:00
class PointerWrap
{
public :
2010-04-17 15:02:03 -06:00
enum Mode {
MODE_READ = 1 , // load
MODE_WRITE , // save
MODE_MEASURE , // calculate size
MODE_VERIFY , // compare
2008-12-07 21:46:09 -07:00
} ;
u8 * * ptr ;
Mode mode ;
public :
PointerWrap ( u8 * * ptr_ , Mode mode_ ) : ptr ( ptr_ ) , mode ( mode_ ) { }
PointerWrap ( unsigned char * * ptr_ , int mode_ ) : ptr ( ( u8 * * ) ptr_ ) , mode ( ( Mode ) mode_ ) { }
void SetMode ( Mode mode_ ) { mode = mode_ ; }
Mode GetMode ( ) const { return mode ; }
u8 * * GetPPtr ( ) { return ptr ; }
void DoVoid ( void * data , int size )
{
2009-02-27 18:26:56 -07:00
switch ( mode ) {
2008-12-07 21:46:09 -07:00
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
2010-12-05 08:59:11 -07:00
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 ;
2008-12-07 21:46:09 -07:00
default : break ; // throw an error?
}
( * ptr ) + = size ;
}
template < class T >
2009-11-24 10:10:38 -07:00
void Do ( std : : map < unsigned int , T > & x )
{
unsigned int number = ( unsigned int ) x . size ( ) ;
Do ( number ) ;
switch ( mode ) {
case MODE_READ :
{
x . clear ( ) ;
while ( number > 0 )
{
2010-06-02 14:35:12 -06:00
unsigned int first = 0 ;
2009-11-24 10:10:38 -07:00
Do ( first ) ;
2011-12-19 16:05:35 -07:00
T second ;
2009-11-24 10:10:38 -07:00
Do ( second ) ;
x [ first ] = second ;
- - number ;
}
}
break ;
case MODE_WRITE :
case MODE_MEASURE :
2010-04-17 15:02:03 -06:00
case MODE_VERIFY :
2009-11-24 10:10:38 -07:00
{
2012-05-26 00:09:50 -06:00
typename std : : map < unsigned int , T > : : iterator itr = x . begin ( ) ;
2009-11-24 10:10:38 -07:00
while ( number > 0 )
{
Do ( itr - > first ) ;
Do ( itr - > second ) ;
- - number ;
+ + itr ;
}
}
break ;
}
}
2008-12-07 21:46:09 -07:00
// Store vectors.
template < class T >
2011-03-22 01:27:23 -06:00
void Do ( std : : vector < T > & x )
{
u32 vec_size = ( u32 ) x . size ( ) ;
Do ( vec_size ) ;
x . resize ( vec_size ) ;
DoArray ( & x [ 0 ] , vec_size ) ;
2008-12-07 21:46:09 -07:00
}
2009-02-27 18:26:56 -07:00
2011-05-27 13:55:07 -06:00
// Store deques.
template < class T >
void Do ( std : : deque < T > & x )
{
u32 deq_size = ( u32 ) x . size ( ) ;
Do ( deq_size ) ;
x . resize ( deq_size ) ;
u32 i ;
for ( i = 0 ; i < deq_size ; i + + )
DoVoid ( & x [ i ] , sizeof ( T ) ) ;
}
2008-12-07 21:46:09 -07:00
// Store strings.
void Do ( std : : string & x )
{
int stringLen = ( int ) x . length ( ) + 1 ;
Do ( stringLen ) ;
2009-02-27 18:26:56 -07:00
switch ( mode ) {
2008-12-07 21:46:09 -07:00
case MODE_READ : x = ( char * ) * ptr ; break ;
case MODE_WRITE : memcpy ( * ptr , x . c_str ( ) , stringLen ) ; break ;
case MODE_MEASURE : break ;
2010-12-05 08:59:11 -07:00
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 ;
2008-12-07 21:46:09 -07:00
}
( * ptr ) + = stringLen ;
}
2011-12-18 23:01:46 -07:00
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: \" %s \" != \" %s \" (at %p). \n " , x . c_str ( ) , ( wchar_t * ) * ptr , ptr ) ; break ;
}
( * ptr ) + = stringLen ;
}
2008-12-07 21:46:09 -07:00
template < class T >
2009-02-27 18:26:56 -07:00
void DoArray ( T * x , int count ) {
2008-12-07 21:46:09 -07:00
DoVoid ( ( void * ) x , sizeof ( T ) * count ) ;
}
2009-02-27 18:26:56 -07:00
2008-12-07 21:46:09 -07:00
template < class T >
void Do ( T & x ) {
DoVoid ( ( void * ) & x , sizeof ( x ) ) ;
}
2012-01-04 01:42:22 -07: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 ;
}
2011-12-19 00:36:54 -07:00
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 ;
}
2008-12-07 21:46:09 -07:00
}
2011-12-17 17:49:24 -07:00
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 ) ;
mode = PointerWrap : : MODE_MEASURE ;
}
}
2008-12-07 21:46:09 -07:00
} ;
class CChunkFileReader
{
public :
2009-02-27 18:26:56 -07:00
// Load file template
2008-12-07 21:46:09 -07:00
template < class T >
2009-02-27 18:26:56 -07:00
static bool Load ( const std : : string & _rFilename , int _Revision , T & _class )
2008-12-07 21:46:09 -07:00
{
2009-02-27 18:26:56 -07:00
INFO_LOG ( COMMON , " ChunkReader: Loading %s " , _rFilename . c_str ( ) ) ;
2011-02-28 20:06:14 -07:00
if ( ! File : : Exists ( _rFilename ) )
2009-02-27 18:26:56 -07:00
return false ;
// Check file size
2011-03-11 03:21:46 -07:00
const u64 fileSize = File : : GetSize ( _rFilename ) ;
static const u64 headerSize = sizeof ( SChunkHeader ) ;
if ( fileSize < headerSize )
{
2009-02-27 18:26:56 -07:00
ERROR_LOG ( COMMON , " ChunkReader: File too small " ) ;
return false ;
}
2008-12-07 21:46:09 -07:00
2011-03-11 03:21:46 -07:00
File : : IOFile pFile ( _rFilename , " rb " ) ;
if ( ! pFile )
{
2009-02-27 18:26:56 -07:00
ERROR_LOG ( COMMON , " ChunkReader: Can't open file for reading " ) ;
return false ;
}
2008-12-07 21:46:09 -07:00
2009-02-27 18:26:56 -07:00
// read the header
SChunkHeader header ;
2011-03-11 03:21:46 -07:00
if ( ! pFile . ReadArray ( & header , 1 ) )
{
2009-02-27 18:26:56 -07:00
ERROR_LOG ( COMMON , " ChunkReader: Bad header size " ) ;
return false ;
}
// Check revision
2011-03-11 03:21:46 -07:00
if ( header . Revision ! = _Revision )
{
2009-02-27 18:26:56 -07:00
ERROR_LOG ( COMMON , " ChunkReader: Wrong file revision, got %d expected %d " ,
2011-03-11 03:21:46 -07:00
header . Revision , _Revision ) ;
2009-02-27 18:26:56 -07:00
return false ;
}
// get size
2011-03-11 03:21:46 -07:00
const int sz = ( int ) ( fileSize - headerSize ) ;
if ( header . ExpectedSize ! = sz )
{
2009-02-27 18:26:56 -07:00
ERROR_LOG ( COMMON , " ChunkReader: Bad file size, got %d expected %d " ,
2011-03-11 03:21:46 -07:00
sz , header . ExpectedSize ) ;
2009-02-27 18:26:56 -07:00
return false ;
}
// read the state
u8 * buffer = new u8 [ sz ] ;
2011-03-11 03:21:46 -07:00
if ( ! pFile . ReadBytes ( buffer , sz ) )
{
2009-02-27 18:26:56 -07:00
ERROR_LOG ( COMMON , " ChunkReader: Error reading file " ) ;
return false ;
}
2008-12-07 21:46:09 -07:00
2009-02-27 18:26:56 -07:00
u8 * ptr = buffer ;
PointerWrap p ( & ptr , PointerWrap : : MODE_READ ) ;
_class . DoState ( p ) ;
2011-03-11 03:21:46 -07:00
delete [ ] buffer ;
2009-02-27 18:26:56 -07:00
INFO_LOG ( COMMON , " ChunkReader: Done loading %s " , _rFilename . c_str ( ) ) ;
return true ;
2008-12-07 21:46:09 -07:00
}
2009-02-27 18:26:56 -07:00
// Save file template
2008-12-07 21:46:09 -07:00
template < class T >
static bool Save ( const std : : string & _rFilename , int _Revision , T & _class )
{
2009-02-27 18:26:56 -07:00
INFO_LOG ( COMMON , " ChunkReader: Writing %s " , _rFilename . c_str ( ) ) ;
2011-03-11 03:21:46 -07:00
File : : IOFile pFile ( _rFilename , " wb " ) ;
if ( ! pFile )
{
2009-02-27 18:26:56 -07:00
ERROR_LOG ( COMMON , " ChunkReader: Error opening file for write " ) ;
return false ;
}
2008-12-07 21:46:09 -07:00
2009-02-27 18:26:56 -07:00
// Get data
u8 * ptr = 0 ;
PointerWrap p ( & ptr , PointerWrap : : MODE_MEASURE ) ;
_class . DoState ( p ) ;
2011-03-22 01:27:23 -06:00
size_t const sz = ( size_t ) ptr ;
std : : vector < u8 > buffer ( sz ) ;
ptr = & buffer [ 0 ] ;
2009-02-27 18:26:56 -07:00
p . SetMode ( PointerWrap : : MODE_WRITE ) ;
_class . DoState ( p ) ;
// Create header
SChunkHeader header ;
header . Compress = 0 ;
header . Revision = _Revision ;
header . ExpectedSize = ( int ) sz ;
// Write to file
2011-03-11 03:21:46 -07:00
if ( ! pFile . WriteArray ( & header , 1 ) )
{
2009-02-27 18:26:56 -07:00
ERROR_LOG ( COMMON , " ChunkReader: Failed writing header " ) ;
return false ;
2008-12-07 21:46:09 -07:00
}
2011-03-22 01:27:23 -06:00
if ( ! pFile . WriteBytes ( & buffer [ 0 ] , sz ) )
2011-03-11 03:21:46 -07:00
{
2009-02-27 18:26:56 -07:00
ERROR_LOG ( COMMON , " ChunkReader: Failed writing data " ) ;
return false ;
}
INFO_LOG ( COMMON , " ChunkReader: Done writing %s " ,
_rFilename . c_str ( ) ) ;
return true ;
2008-12-07 21:46:09 -07:00
}
2009-02-27 18:26:56 -07:00
2008-12-07 21:46:09 -07:00
private :
struct SChunkHeader
{
int Revision ;
int Compress ;
int ExpectedSize ;
} ;
} ;
2009-03-28 02:57:34 -06:00
# endif // _POINTERWRAP_H_