2016-09-10 02:28:11 +02:00
/*
* BinaryDeserializer . h , part of VCMI engine
*
* Authors : listed in file AUTHORS in main folder
*
* License : GNU General Public License v2 .0 or later
* Full text of license available in license . txt file , in main folder
*
*/
# pragma once
2023-11-08 22:05:36 +02:00
# include "CSerializer.h"
2016-09-10 02:28:11 +02:00
# include "CTypeList.h"
# include "../mapObjects/CGHeroInstance.h"
2018-01-29 17:54:41 +02:00
# include "../../Global.h"
2016-09-10 02:28:11 +02:00
2022-07-26 15:07:42 +02:00
VCMI_LIB_NAMESPACE_BEGIN
2016-09-10 02:28:11 +02:00
class CStackInstance ;
class DLL_LINKAGE CLoaderBase
{
protected :
IBinaryReader * reader ;
public :
CLoaderBase ( IBinaryReader * r ) : reader ( r ) { } ;
inline int read ( void * data , unsigned size )
{
return reader - > read ( data , size ) ;
} ;
} ;
/// Main class for deserialization of classes from binary form
/// Effectively revesed version of BinarySerializer
class DLL_LINKAGE BinaryDeserializer : public CLoaderBase
{
template < typename Ser , typename T >
struct LoadIfStackInstance
{
static bool invoke ( Ser & s , T & data )
{
return false ;
}
} ;
template < typename Ser >
struct LoadIfStackInstance < Ser , CStackInstance * >
{
static bool invoke ( Ser & s , CStackInstance * & data )
{
CArmedInstance * armedObj ;
SlotID slot ;
s . load ( armedObj ) ;
s . load ( slot ) ;
if ( slot ! = SlotID : : COMMANDER_SLOT_PLACEHOLDER )
{
assert ( armedObj - > hasStackAtSlot ( slot ) ) ;
data = armedObj - > stacks [ slot ] ;
}
else
{
2023-04-17 23:11:16 +02:00
auto * hero = dynamic_cast < CGHeroInstance * > ( armedObj ) ;
2016-09-10 02:28:11 +02:00
assert ( hero ) ;
assert ( hero - > commander ) ;
data = hero - > commander ;
}
return true ;
}
} ;
template < typename T , typename Enable = void >
struct ClassObjectCreator
{
static T * invoke ( )
{
static_assert ( ! std : : is_abstract < T > : : value , " Cannot call new upon abstract classes! " ) ;
return new T ( ) ;
}
} ;
template < typename T >
struct ClassObjectCreator < T , typename std : : enable_if < std : : is_abstract < T > : : value > : : type >
{
static T * invoke ( )
{
throw std : : runtime_error ( " Something went really wrong during deserialization. Attempted creating an object of an abstract class " + std : : string ( typeid ( T ) . name ( ) ) ) ;
}
} ;
2018-01-29 17:54:41 +02:00
STRONG_INLINE ui32 readAndCheckLength ( )
{
ui32 length ;
load ( length ) ;
2023-04-11 16:46:08 +02:00
//NOTE: also used for h3m's embedded in campaigns, so it may be quite large in some cases (e.g. XXL maps with multiple objects)
if ( length > 1000000 )
2018-01-29 17:54:41 +02:00
{
logGlobal - > warn ( " Warning: very big length: %d " , length ) ;
reader - > reportState ( logGlobal ) ;
} ;
return length ;
}
2016-09-10 02:28:11 +02:00
2023-11-08 22:05:36 +02:00
template < typename Type > class CPointerLoader ;
2016-09-10 02:28:11 +02:00
2023-11-08 22:05:36 +02:00
class IPointerLoader
2016-09-10 02:28:11 +02:00
{
public :
2023-11-08 22:05:36 +02:00
virtual void * loadPtr ( CLoaderBase & ar , ui32 pid ) const = 0 ; //data is pointer to the ACTUAL POINTER
virtual ~ IPointerLoader ( ) = default ;
2016-09-10 02:28:11 +02:00
2023-11-08 22:05:36 +02:00
template < typename Type > static IPointerLoader * getApplier ( const Type * t = nullptr )
2016-09-10 02:28:11 +02:00
{
2023-11-08 22:05:36 +02:00
return new CPointerLoader < Type > ( ) ;
2016-09-10 02:28:11 +02:00
}
} ;
2023-11-08 22:05:36 +02:00
template < typename Type > class CPointerLoader : public IPointerLoader
2016-09-10 02:28:11 +02:00
{
public :
2023-11-08 22:05:36 +02:00
void * loadPtr ( CLoaderBase & ar , ui32 pid ) const override //data is pointer to the ACTUAL POINTER
2016-09-10 02:28:11 +02:00
{
2023-04-17 23:11:16 +02:00
auto & s = static_cast < BinaryDeserializer & > ( ar ) ;
2016-09-10 02:28:11 +02:00
//create new object under pointer
2023-11-08 22:05:36 +02:00
Type * ptr = ClassObjectCreator < Type > : : invoke ( ) ; //does new npT or throws for abstract classes
2016-09-10 02:28:11 +02:00
s . ptrAllocated ( ptr , pid ) ;
2023-11-08 22:05:36 +02:00
2016-10-29 18:52:19 +02:00
assert ( s . fileVersion ! = 0 ) ;
2016-09-10 02:28:11 +02:00
ptr - > serialize ( s , s . fileVersion ) ;
2023-11-08 22:05:36 +02:00
return static_cast < void * > ( ptr ) ;
2016-09-10 02:28:11 +02:00
}
} ;
2023-11-08 22:05:36 +02:00
CApplier < IPointerLoader > applier ;
2016-09-10 02:28:11 +02:00
int write ( const void * data , unsigned size ) ;
public :
bool reverseEndianess ; //if source has different endianness than us, we reverse bytes
si32 fileVersion ;
std : : map < ui32 , void * > loadedPointers ;
std : : map < ui32 , const std : : type_info * > loadedPointersTypes ;
2023-11-08 22:05:36 +02:00
std : : map < const void * , std : : shared_ptr < void > > loadedSharedPointers ;
2016-09-10 02:28:11 +02:00
bool smartPointerSerialization ;
bool saving ;
BinaryDeserializer ( IBinaryReader * r ) : CLoaderBase ( r )
{
saving = false ;
fileVersion = 0 ;
smartPointerSerialization = true ;
reverseEndianess = false ;
}
template < class T >
BinaryDeserializer & operator & ( T & t )
{
this - > load ( t ) ;
return * this ;
}
template < class T , typename std : : enable_if < std : : is_fundamental < T > : : value & & ! std : : is_same < T , bool > : : value , int > : : type = 0 >
void load ( T & data )
{
2016-10-29 18:52:19 +02:00
unsigned length = sizeof ( data ) ;
2023-02-11 17:13:21 +02:00
char * dataPtr = reinterpret_cast < char * > ( & data ) ;
2016-10-29 18:52:19 +02:00
this - > read ( dataPtr , length ) ;
if ( reverseEndianess )
std : : reverse ( dataPtr , dataPtr + length ) ;
2016-09-10 02:28:11 +02:00
}
template < typename T , typename std : : enable_if < is_serializeable < BinaryDeserializer , T > : : value , int > : : type = 0 >
void load ( T & data )
{
2016-10-29 18:52:19 +02:00
assert ( fileVersion ! = 0 ) ;
2016-09-10 02:28:11 +02:00
////that const cast is evil because it allows to implicitly overwrite const objects when deserializing
typedef typename std : : remove_const < T > : : type nonConstT ;
2023-04-17 23:11:16 +02:00
auto & hlp = const_cast < nonConstT & > ( data ) ;
2016-09-10 02:28:11 +02:00
hlp . serialize ( * this , fileVersion ) ;
}
template < typename T , typename std : : enable_if < std : : is_array < T > : : value , int > : : type = 0 >
void load ( T & data )
{
2023-04-11 00:29:36 +02:00
ui32 size = std : : size ( data ) ;
2016-09-10 02:28:11 +02:00
for ( ui32 i = 0 ; i < size ; i + + )
load ( data [ i ] ) ;
}
template < typename T , typename std : : enable_if < std : : is_enum < T > : : value , int > : : type = 0 >
void load ( T & data )
{
si32 read ;
load ( read ) ;
data = static_cast < T > ( read ) ;
}
template < typename T , typename std : : enable_if < std : : is_same < T , bool > : : value , int > : : type = 0 >
void load ( T & data )
{
ui8 read ;
load ( read ) ;
data = static_cast < bool > ( read ) ;
}
template < typename T , typename std : : enable_if < std : : is_same < T , std : : vector < bool > > : : value , int > : : type = 0 >
void load ( T & data )
{
std : : vector < ui8 > convData ;
load ( convData ) ;
convData . resize ( data . size ( ) ) ;
range : : copy ( convData , data . begin ( ) ) ;
}
2016-10-30 11:42:53 +02:00
template < typename T , typename std : : enable_if < ! std : : is_same < T , bool > : : value , int > : : type = 0 >
void load ( std : : vector < T > & data )
2016-09-10 02:28:11 +02:00
{
2018-01-29 17:54:41 +02:00
ui32 length = readAndCheckLength ( ) ;
2016-09-10 02:28:11 +02:00
data . resize ( length ) ;
for ( ui32 i = 0 ; i < length ; i + + )
load ( data [ i ] ) ;
}
template < typename T , typename std : : enable_if < std : : is_pointer < T > : : value , int > : : type = 0 >
void load ( T & data )
{
2023-11-07 17:29:57 +02:00
bool isNull ;
load ( isNull ) ;
if ( isNull )
2016-09-10 02:28:11 +02:00
{
data = nullptr ;
return ;
}
2023-11-04 19:25:50 +02:00
loadPointerImpl ( data ) ;
}
template < typename T , typename std : : enable_if < std : : is_base_of_v < Entity , std : : remove_pointer_t < T > > , int > : : type = 0 >
void loadPointerImpl ( T & data )
{
using DataType = std : : remove_pointer_t < T > ;
typename DataType : : IdentifierType index ;
load ( index ) ;
auto * constEntity = index . toEntity ( VLC ) ;
auto * constData = dynamic_cast < const DataType * > ( constEntity ) ;
data = const_cast < DataType * > ( constData ) ;
}
template < typename T , typename std : : enable_if < ! std : : is_base_of_v < Entity , std : : remove_pointer_t < T > > , int > : : type = 0 >
void loadPointerImpl ( T & data )
{
2016-09-10 02:28:11 +02:00
if ( reader - > smartVectorMembersSerialization )
{
typedef typename std : : remove_const < typename std : : remove_pointer < T > : : type > : : type TObjectType ; //eg: const CGHeroInstance * => CGHeroInstance
typedef typename VectorizedTypeFor < TObjectType > : : type VType ; //eg: CGHeroInstance -> CGobjectInstance
typedef typename VectorizedIDType < TObjectType > : : type IDType ;
if ( const auto * info = reader - > getVectorizedTypeInfo < VType , IDType > ( ) )
{
IDType id ;
load ( id ) ;
if ( id ! = IDType ( - 1 ) )
{
data = static_cast < T > ( reader - > getVectorItemFromId < VType , IDType > ( * info , id ) ) ;
return ;
}
}
}
if ( reader - > sendStackInstanceByIds )
{
bool gotLoaded = LoadIfStackInstance < BinaryDeserializer , T > : : invoke ( * this , data ) ;
if ( gotLoaded )
return ;
}
ui32 pid = 0xffffffff ; //pointer id (or maybe rather pointee id)
if ( smartPointerSerialization )
{
load ( pid ) ; //get the id
2023-04-17 23:11:16 +02:00
auto i = loadedPointers . find ( pid ) ; //lookup
2016-09-10 02:28:11 +02:00
if ( i ! = loadedPointers . end ( ) )
{
// We already got this pointer
// Cast it in case we are loading it to a non-first base pointer
assert ( loadedPointersTypes . count ( pid ) ) ;
2023-11-08 22:05:36 +02:00
data = static_cast < T > ( i - > second ) ;
2016-09-10 02:28:11 +02:00
return ;
}
}
//get type id
ui16 tid ;
load ( tid ) ;
if ( ! tid )
{
typedef typename std : : remove_pointer < T > : : type npT ;
typedef typename std : : remove_const < npT > : : type ncpT ;
data = ClassObjectCreator < ncpT > : : invoke ( ) ;
ptrAllocated ( data , pid ) ;
load ( * data ) ;
}
else
{
2023-04-17 23:11:16 +02:00
auto * app = applier . getApplier ( tid ) ;
2016-10-27 17:39:17 +02:00
if ( app = = nullptr )
{
logGlobal - > error ( " load %d %d - no loader exists " , tid , pid ) ;
data = nullptr ;
return ;
}
2023-11-08 22:05:36 +02:00
data = static_cast < T > ( app - > loadPtr ( * this , pid ) ) ;
2016-09-10 02:28:11 +02:00
}
}
template < typename T >
void ptrAllocated ( const T * ptr , ui32 pid )
{
if ( smartPointerSerialization & & pid ! = 0xffffffff )
{
loadedPointersTypes [ pid ] = & typeid ( T ) ;
loadedPointers [ pid ] = ( void * ) ptr ; //add loaded pointer to our lookup map; cast is to avoid errors with const T* pt
}
}
template < typename Base , typename Derived > void registerType ( const Base * b = nullptr , const Derived * d = nullptr )
{
applier . registerType ( b , d ) ;
}
template < typename T >
void load ( std : : shared_ptr < T > & data )
{
typedef typename std : : remove_const < T > : : type NonConstT ;
NonConstT * internalPtr ;
load ( internalPtr ) ;
2023-11-08 22:05:36 +02:00
void * internalPtrDerived = static_cast < void * > ( internalPtr ) ;
2016-09-10 02:28:11 +02:00
if ( internalPtr )
{
auto itr = loadedSharedPointers . find ( internalPtrDerived ) ;
if ( itr ! = loadedSharedPointers . end ( ) )
{
// This pointers is already loaded. The "data" needs to be pointed to it,
// so their shared state is actually shared.
2023-11-08 22:05:36 +02:00
data = std : : static_pointer_cast < T > ( itr - > second ) ;
2016-09-10 02:28:11 +02:00
}
else
{
auto hlp = std : : shared_ptr < NonConstT > ( internalPtr ) ;
2022-09-11 15:12:35 +02:00
data = hlp ;
2023-11-08 22:05:36 +02:00
loadedSharedPointers [ internalPtrDerived ] = std : : static_pointer_cast < void > ( hlp ) ;
2022-09-11 15:12:35 +02:00
}
}
else
data . reset ( ) ;
}
2022-09-29 19:07:56 +02:00
2023-10-21 18:48:07 +02:00
void load ( std : : monostate & data )
{
// no-op
}
2022-09-11 15:12:35 +02:00
template < typename T >
2022-09-29 19:07:56 +02:00
void load ( std : : shared_ptr < const T > & data )
2022-09-11 15:12:35 +02:00
{
2022-09-29 19:07:56 +02:00
std : : shared_ptr < T > nonConstData ;
2022-09-11 15:12:35 +02:00
2022-09-29 19:07:56 +02:00
load ( nonConstData ) ;
2022-09-11 15:12:35 +02:00
2022-09-29 19:07:56 +02:00
data = nonConstData ;
2016-09-10 02:28:11 +02:00
}
2022-09-29 19:07:56 +02:00
2016-09-10 02:28:11 +02:00
template < typename T >
void load ( std : : unique_ptr < T > & data )
{
T * internalPtr ;
load ( internalPtr ) ;
data . reset ( internalPtr ) ;
}
template < typename T , size_t N >
void load ( std : : array < T , N > & data )
{
for ( ui32 i = 0 ; i < N ; i + + )
load ( data [ i ] ) ;
}
template < typename T >
void load ( std : : set < T > & data )
{
2018-01-29 17:54:41 +02:00
ui32 length = readAndCheckLength ( ) ;
2016-09-10 02:28:11 +02:00
data . clear ( ) ;
T ins ;
for ( ui32 i = 0 ; i < length ; i + + )
{
load ( ins ) ;
data . insert ( ins ) ;
}
}
template < typename T , typename U >
void load ( std : : unordered_set < T , U > & data )
{
2018-01-29 17:54:41 +02:00
ui32 length = readAndCheckLength ( ) ;
2016-09-10 02:28:11 +02:00
data . clear ( ) ;
T ins ;
for ( ui32 i = 0 ; i < length ; i + + )
{
load ( ins ) ;
data . insert ( ins ) ;
}
}
template < typename T >
void load ( std : : list < T > & data )
{
2018-01-29 17:54:41 +02:00
ui32 length = readAndCheckLength ( ) ;
2016-09-10 02:28:11 +02:00
data . clear ( ) ;
T ins ;
for ( ui32 i = 0 ; i < length ; i + + )
{
load ( ins ) ;
data . push_back ( ins ) ;
}
}
template < typename T1 , typename T2 >
void load ( std : : pair < T1 , T2 > & data )
{
load ( data . first ) ;
load ( data . second ) ;
}
template < typename T1 , typename T2 >
void load ( std : : map < T1 , T2 > & data )
{
2018-01-29 17:54:41 +02:00
ui32 length = readAndCheckLength ( ) ;
2016-09-10 02:28:11 +02:00
data . clear ( ) ;
T1 key ;
T2 value ;
for ( ui32 i = 0 ; i < length ; i + + )
{
load ( key ) ;
load ( value ) ;
data . insert ( std : : pair < T1 , T2 > ( std : : move ( key ) , std : : move ( value ) ) ) ;
}
}
template < typename T1 , typename T2 >
void load ( std : : multimap < T1 , T2 > & data )
{
2018-01-29 17:54:41 +02:00
ui32 length = readAndCheckLength ( ) ;
2016-09-10 02:28:11 +02:00
data . clear ( ) ;
T1 key ;
T2 value ;
for ( ui32 i = 0 ; i < length ; i + + )
{
load ( key ) ;
load ( value ) ;
data . insert ( std : : pair < T1 , T2 > ( std : : move ( key ) , std : : move ( value ) ) ) ;
}
}
void load ( std : : string & data )
{
2018-01-29 17:54:41 +02:00
ui32 length = readAndCheckLength ( ) ;
2016-09-10 02:28:11 +02:00
data . resize ( length ) ;
this - > read ( ( void * ) data . c_str ( ) , length ) ;
}
2023-11-04 16:21:54 +02:00
template < typename . . . TN >
void load ( std : : variant < TN . . . > & data )
2016-09-10 02:28:11 +02:00
{
si32 which ;
load ( which ) ;
2023-11-04 16:21:54 +02:00
assert ( which < sizeof . . . ( TN ) ) ;
// Create array of variants that contains all default-constructed alternatives
const std : : variant < TN . . . > table [ ] = { TN { } . . . } ;
// use appropriate alternative for result
data = table [ which ] ;
// perform actual load via std::visit dispatch
2023-11-04 19:25:50 +02:00
std : : visit ( [ & ] ( auto & o ) { load ( o ) ; } , data ) ;
2016-09-10 02:28:11 +02:00
}
2023-04-16 19:42:56 +02:00
template < typename T >
void load ( std : : optional < T > & data )
2016-09-10 02:28:11 +02:00
{
ui8 present ;
load ( present ) ;
if ( present )
{
2017-08-05 15:09:29 +02:00
//TODO: replace with emplace once we start request Boost 1.56+, see PR360
2016-09-10 02:28:11 +02:00
T t ;
load ( t ) ;
2023-04-16 19:42:56 +02:00
data = std : : make_optional ( std : : move ( t ) ) ;
2016-09-10 02:28:11 +02:00
}
else
{
2023-04-16 19:42:56 +02:00
data = std : : optional < T > ( ) ;
2016-09-10 02:28:11 +02:00
}
}
2022-09-18 16:39:10 +02:00
template < typename T >
void load ( boost : : multi_array < T , 3 > & data )
{
ui32 length = readAndCheckLength ( ) ;
ui32 x , y , z ;
load ( x ) ;
load ( y ) ;
load ( z ) ;
data . resize ( boost : : extents [ x ] [ y ] [ z ] ) ;
assert ( length = = data . num_elements ( ) ) ; //x*y*z should be equal to number of elements
for ( ui32 i = 0 ; i < length ; i + + )
load ( data . data ( ) [ i ] ) ;
}
2023-05-05 11:56:11 +02:00
template < std : : size_t T >
void load ( std : : bitset < T > & data )
{
static_assert ( T < = 64 ) ;
if constexpr ( T < = 16 )
{
uint16_t read ;
load ( read ) ;
data = read ;
}
else if constexpr ( T < = 32 )
{
uint32_t read ;
load ( read ) ;
data = read ;
}
else if constexpr ( T < = 64 )
{
uint64_t read ;
load ( read ) ;
data = read ;
}
}
2016-09-10 02:28:11 +02:00
} ;
class DLL_LINKAGE CLoadFile : public IBinaryReader
{
public :
BinaryDeserializer serializer ;
std : : string fName ;
2023-07-28 14:51:14 +02:00
std : : unique_ptr < std : : fstream > sfile ;
2016-09-10 02:28:11 +02:00
2016-10-30 14:14:59 +02:00
CLoadFile ( const boost : : filesystem : : path & fname , int minimalVersion = SERIALIZATION_VERSION ) ; //throws!
2022-05-19 14:14:50 +02:00
virtual ~ CLoadFile ( ) ;
2016-09-10 02:28:11 +02:00
int read ( void * data , unsigned size ) override ; //throws!
void openNextFile ( const boost : : filesystem : : path & fname , int minimalVersion ) ; //throws!
void clear ( ) ;
2017-08-11 19:03:05 +02:00
void reportState ( vstd : : CLoggerBase * out ) override ;
2016-09-10 02:28:11 +02:00
void checkMagicBytes ( const std : : string & text ) ;
template < class T >
CLoadFile & operator > > ( T & t )
{
serializer & t ;
return * this ;
}
} ;
2022-07-26 15:07:42 +02:00
VCMI_LIB_NAMESPACE_END