2028 lines
49 KiB
C++
2028 lines
49 KiB
C++
#include "../../include/kfile/trffiler.h"
|
|
#include <algorithm>
|
|
#include <vector>
|
|
#include <assert.h>
|
|
|
|
|
|
namespace trf {
|
|
|
|
template< typename T > static inline T min2( T a, T b ) { return a < b ? a : b; }
|
|
template< typename T > static inline T max2( T a, T b ) { return a > b ? a : b; }
|
|
|
|
template< typename T > static inline T toAlign4( T a ) { return (a%4)>0 ? 4-(a%4) : 0; }
|
|
|
|
class FilerFixedArray;
|
|
class FilerArray;
|
|
class FilerTemplate;
|
|
class FilerFixedTemplateArray;
|
|
class FilerTemplateArray;
|
|
class FilerDict;
|
|
|
|
|
|
struct MergeTable
|
|
{
|
|
std::vector< unsigned short > id;
|
|
std::vector< long > str;
|
|
std::vector< long > wstr;
|
|
};
|
|
|
|
struct Imp
|
|
{
|
|
MetaData* m_pMetaData;
|
|
FilerDict* m_pRoot;
|
|
Filer::TInitTemplateFn m_pTemplateInitCallback;
|
|
|
|
struct StrRec
|
|
{
|
|
std::string str;
|
|
int index;
|
|
};
|
|
|
|
struct WStrRec
|
|
{
|
|
std::wstring str;
|
|
int index;
|
|
};
|
|
|
|
std::vector< StrRec > m_stringTable;
|
|
std::vector< WStrRec > m_wstringTable;
|
|
std::vector< GUID > m_idTable;
|
|
|
|
bool m_bUnpackedDict;
|
|
|
|
bool saveIdTable( KStream& stream );
|
|
bool loadIdTable( KStream& stream, std::vector< unsigned short >& idMergeTable );
|
|
void applyStringIndex();
|
|
bool saveStringTable( KStream& stream );
|
|
bool loadStringTable( KStream& stream, std::vector< long >& strMergeTable, std::vector< long >& wstrMergeTable );
|
|
|
|
const char* getString( int i )
|
|
{
|
|
assert( i >= 0 && i <= int( m_stringTable.size() ) );
|
|
return i == 0 ? NULL : m_stringTable[ i-1 ].str.c_str();
|
|
}
|
|
|
|
int getStringIndex( const char* pString )
|
|
{
|
|
if ( pString == NULL || *pString == 0 )
|
|
return 0;
|
|
|
|
for ( int i = 0, n = static_cast< int >( m_stringTable.size() ); i < n; i++ )
|
|
if ( !strcmp( pString, m_stringTable[ i ].str.c_str() ) )
|
|
return i+1;
|
|
|
|
return -1;
|
|
}
|
|
|
|
int addString( const char* pString )
|
|
{
|
|
int index = getStringIndex( pString );
|
|
if ( index >= 0 )
|
|
return index;
|
|
|
|
StrRec rec;
|
|
rec.str = pString;
|
|
rec.index = -1;
|
|
m_stringTable.push_back( rec );
|
|
return int( m_stringTable.size() );
|
|
}
|
|
|
|
const wchar_t* getWString( int i )
|
|
{
|
|
assert( i >= 0 && i <= int( m_wstringTable.size() ) );
|
|
return i == 0 ? NULL : m_wstringTable[ i-1 ].str.c_str();
|
|
}
|
|
|
|
int getWStringIndex( const wchar_t* pString )
|
|
{
|
|
if ( pString == NULL || *pString == 0 )
|
|
return 0;
|
|
|
|
for ( int i = 0, n = static_cast< int >( m_wstringTable.size() ); i < n; i++ )
|
|
if ( !wcscmp( pString, m_wstringTable[ i ].str.c_str() ) )
|
|
return i+1;
|
|
|
|
return -1;
|
|
}
|
|
|
|
int addWString( const wchar_t* pString )
|
|
{
|
|
int index = getWStringIndex( pString );
|
|
if ( index >= 0 )
|
|
return index;
|
|
|
|
WStrRec rec;
|
|
rec.str = pString;
|
|
rec.index = -1;
|
|
m_wstringTable.push_back( rec );
|
|
return int( m_wstringTable.size() );
|
|
}
|
|
|
|
int setStringIndex( int i, int& newIndex )
|
|
{
|
|
if ( i == 0 )
|
|
return 0;
|
|
|
|
int& index = m_stringTable[ i-1 ].index;
|
|
if ( index == -1 )
|
|
index = newIndex++;
|
|
return index;
|
|
}
|
|
|
|
int setWStringIndex( int i, int& newIndex )
|
|
{
|
|
if ( i == 0 )
|
|
return 0;
|
|
|
|
int& index = m_wstringTable[ i-1 ].index;
|
|
if ( index == -1 )
|
|
index = newIndex++;
|
|
return index;
|
|
}
|
|
|
|
const GUID* getId( int i )
|
|
{
|
|
assert( i >= 0 && i <= int( m_idTable.size() ) );
|
|
return i == 0 ? NULL : &m_idTable[ i-1 ];
|
|
}
|
|
|
|
int getIdIndex( const GUID* id )
|
|
{
|
|
if ( id == NULL )
|
|
return 0;
|
|
|
|
for ( int i = 0, n = static_cast< int >( m_idTable.size() ); i < n; i++ )
|
|
if ( !memcmp( id, &m_idTable[ i ], sizeof(*id) ) )
|
|
return i+1;
|
|
|
|
return -1;
|
|
}
|
|
|
|
int addId( const GUID* id )
|
|
{
|
|
int index = getIdIndex( id );
|
|
if ( index >= 0 )
|
|
return index;
|
|
|
|
m_idTable.push_back( *id );
|
|
return int( m_idTable.size() );
|
|
}
|
|
|
|
std::vector< ATInfo > m_fieldLimits;
|
|
|
|
void initATIs()
|
|
{
|
|
int n = m_pMetaData->getTemplateInfoCount();
|
|
m_fieldLimits.resize( n );
|
|
for ( int i = 0; i < n; i++ )
|
|
{
|
|
ATInfo& fl = m_fieldLimits[ i ];
|
|
fl.pInfo = m_pMetaData->getTemplateInfoAt( i );
|
|
fl.fieldLimit = fl.pInfo->count();
|
|
fl.fixedSize = fl.pInfo->calcFixedSize( fl.fieldLimit );
|
|
}
|
|
|
|
}
|
|
|
|
const ATInfo* getATI( int n )
|
|
{
|
|
return &m_fieldLimits[ n ];
|
|
}
|
|
};
|
|
|
|
|
|
union Var
|
|
{
|
|
char asChar;
|
|
short asShort;
|
|
long asLong;
|
|
long long asLLong;
|
|
float asFloat;
|
|
double asDouble;
|
|
FilerArray* asArray;
|
|
FilerTemplate* asTemplate;
|
|
FilerTemplateArray* asTemplateArray;
|
|
FilerDict* asDict;
|
|
};
|
|
|
|
|
|
// =================================================================================================
|
|
|
|
|
|
class FilerTemplate: public ITemplate
|
|
{
|
|
public:
|
|
|
|
FilerTemplate( Imp* pParent, const ATInfo* pFL )
|
|
{
|
|
m_pParent = pParent;
|
|
init( pFL );
|
|
}
|
|
|
|
~FilerTemplate()
|
|
{
|
|
close();
|
|
}
|
|
|
|
void close();
|
|
|
|
int readFixedData( void* pOutBuf, int bufSize = 0 ) const;
|
|
|
|
const void* getSimpleDataAt( int i, int type ) const
|
|
{
|
|
assert( i >= 0 && i < count() && typeAt( i ) == type );
|
|
|
|
switch ( type )
|
|
{
|
|
case Type::CHAR: return &m_fields[ i ].asChar;
|
|
case Type::SHORT: return &m_fields[ i ].asShort;
|
|
case Type::STRING:
|
|
case Type::WSTRING:
|
|
case Type::LONG: return &m_fields[ i ].asLong;
|
|
case Type::LLONG: return &m_fields[ i ].asLLong;
|
|
case Type::FLOAT: return &m_fields[ i ].asFloat;
|
|
case Type::DOUBLE: return &m_fields[ i ].asDouble;
|
|
default:
|
|
assert( false );
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
const char* getStringValue( long strIndex ) const
|
|
{
|
|
return m_pParent->getString( strIndex );
|
|
}
|
|
|
|
const wchar_t* getWStringValue( long wstrIndex ) const
|
|
{
|
|
return m_pParent->getWString( wstrIndex );
|
|
}
|
|
|
|
const IArray* getArrayAt( int i ) const;
|
|
const ITemplate* getTemplateAt( int i ) const;
|
|
const ITemplateArray* getTemplateArrayAt( int i ) const;
|
|
const IDict* getDictAt( int i ) const;
|
|
|
|
//
|
|
|
|
void init( const ATInfo* pFL );
|
|
|
|
void clear()
|
|
{
|
|
close();
|
|
init( m_pATI );
|
|
}
|
|
|
|
bool setSimpleDataAt( int i, const void* pData, int type )
|
|
{
|
|
if ( i < 0 || i >= count() || typeAt( i ) != type )
|
|
return false;
|
|
|
|
switch ( type )
|
|
{
|
|
case Type::CHAR: m_fields[ i ].asChar = *reinterpret_cast< const char* >( pData ); break;
|
|
case Type::SHORT: m_fields[ i ].asShort = *reinterpret_cast< const short* >( pData ); break;
|
|
case Type::STRING:
|
|
case Type::WSTRING:
|
|
case Type::LONG: m_fields[ i ].asLong = *reinterpret_cast< const long* >( pData ); break;
|
|
case Type::LLONG: m_fields[ i ].asLLong = *reinterpret_cast< const long long* >( pData ); break;
|
|
case Type::FLOAT: m_fields[ i ].asFloat = *reinterpret_cast< const float* >( pData ); break;
|
|
case Type::DOUBLE: m_fields[ i ].asDouble = *reinterpret_cast< const double* >( pData ); break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
long addStringValue( const char* pStrValue )
|
|
{
|
|
return m_pParent->addString( pStrValue );
|
|
}
|
|
|
|
long addWStringValue( const wchar_t* pWStrValue )
|
|
{
|
|
return m_pParent->addWString( pWStrValue );
|
|
}
|
|
|
|
IArray* arrayAt( int i );
|
|
ITemplate* templateAt( int i );
|
|
ITemplateArray* templateArrayAt( int i );
|
|
IDict* dictAt( int i );
|
|
|
|
//
|
|
|
|
void makeTables( int& newStrIndex, int& newWStrIndex );
|
|
int save( KStream& stream, bool bID, bool bInfoUnknown );
|
|
bool load( KStream& stream, bool bID, bool bInfoUnknown, MergeTable& mergeTable );
|
|
|
|
protected:
|
|
|
|
Imp* m_pParent;
|
|
|
|
std::vector< Var > m_fields;
|
|
};
|
|
|
|
|
|
// -----------------------------------------------------------------------------------
|
|
|
|
|
|
class FilerTemplateArray: public ITemplateArray
|
|
{
|
|
public:
|
|
|
|
FilerTemplateArray( Imp* pParent, const ATInfo* pFL )
|
|
{
|
|
m_pParent = pParent;
|
|
m_pATI = pFL;
|
|
}
|
|
|
|
virtual ~FilerTemplateArray()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
int count() const
|
|
{
|
|
return int( m_fields.size() );
|
|
}
|
|
|
|
int readFixedData( int i, int n, void* pOutBuf, int bufSize ) const
|
|
{
|
|
int elementSize = fixedSize();
|
|
if ( elementSize <= 0 )
|
|
return -1;
|
|
|
|
if ( i < 0 || n < 0 || i >= count() || pOutBuf == NULL || bufSize <= 0 )
|
|
return 0;
|
|
|
|
if ( i+n > count() )
|
|
n = count()-i;
|
|
|
|
if ( bufSize > 0 && bufSize < n * elementSize )
|
|
n = bufSize / elementSize;
|
|
|
|
char* p = reinterpret_cast< char* >( pOutBuf );
|
|
for ( int j = i; n--; j++ )
|
|
m_fields[ i ]->readFixedData( p );
|
|
|
|
return n * elementSize;
|
|
}
|
|
|
|
const ITemplate* getTemplateAt( int i ) const
|
|
{
|
|
assert( i >= 0 && i < int( m_fields.size() ) );
|
|
return m_fields[ i ];
|
|
}
|
|
|
|
//
|
|
|
|
void clear()
|
|
{
|
|
for ( int i = 0, n = count(); i < n; i++ )
|
|
{
|
|
if ( m_fields[ i ] )
|
|
delete m_fields[ i ];
|
|
}
|
|
|
|
m_fields.clear();
|
|
}
|
|
|
|
ITemplate* addNewTemplate()
|
|
{
|
|
m_fields.push_back( new FilerTemplate( m_pParent, m_pATI ) );
|
|
return m_fields.back();
|
|
}
|
|
|
|
ITemplate* templateAt( int i )
|
|
{
|
|
if ( i < 0 || i >= count() )
|
|
return NULL;
|
|
|
|
return m_fields[ i ];
|
|
}
|
|
|
|
void makeTables( int& newStrIndex, int& newWStrIndex );
|
|
virtual int save( KStream& stream );
|
|
virtual bool load( KStream& stream, MergeTable& mergeTable );
|
|
|
|
protected:
|
|
|
|
Imp* m_pParent;
|
|
|
|
std::vector< FilerTemplate* > m_fields;
|
|
};
|
|
|
|
class FilerFixedTemplateArray: public FilerTemplateArray
|
|
{
|
|
public:
|
|
|
|
FilerFixedTemplateArray( Imp* pParent, const ATInfo* pFL, int count )
|
|
: FilerTemplateArray( pParent, pFL )
|
|
{
|
|
m_maxCount = count;
|
|
}
|
|
|
|
//
|
|
|
|
ITemplate* addNewTemplate()
|
|
{
|
|
if ( int( m_fields.size() ) >= m_maxCount )
|
|
return NULL;
|
|
|
|
m_fields.push_back( new FilerTemplate( m_pParent, m_pATI ) );
|
|
return m_fields.back();
|
|
}
|
|
|
|
int save( KStream& stream );
|
|
bool load( KStream& stream, MergeTable& mergeTable );
|
|
|
|
protected:
|
|
|
|
int m_maxCount;
|
|
};
|
|
|
|
|
|
// -----------------------------------------------------------------------------------
|
|
|
|
|
|
class FilerArray: public IArray
|
|
{
|
|
public:
|
|
|
|
FilerArray( Imp* pParent )
|
|
{
|
|
m_pParent = pParent;
|
|
m_elemType = Type::_NONE;
|
|
m_elemSize = 0;
|
|
}
|
|
|
|
FilerArray( Imp* pParent, int elemType )
|
|
{
|
|
m_pParent = pParent;
|
|
init( elemType );
|
|
}
|
|
|
|
virtual ~FilerArray()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
int count() const
|
|
{
|
|
return int( m_buf.size() ) / m_elemSize;
|
|
}
|
|
|
|
int type() const
|
|
{
|
|
return m_elemType;
|
|
}
|
|
|
|
int readFixedData( int i, int n, void* pOutBuf, int bufSize ) const
|
|
{
|
|
if ( i < 0 || n < 0 || i >= count() || pOutBuf == NULL || bufSize <= 0 )
|
|
return 0;
|
|
|
|
if ( i+n > count() )
|
|
n = count()-i;
|
|
|
|
if ( bufSize > 0 && bufSize < n * m_elemSize )
|
|
n = bufSize / m_elemSize;
|
|
|
|
if ( n > 0 )
|
|
s_memcpy( pOutBuf, bufSize, &m_buf.front() + i * m_elemSize, n * m_elemSize );
|
|
return n * m_elemSize;
|
|
}
|
|
|
|
const void* getSimpleDataArray( int start, int type ) const
|
|
{
|
|
check_error( start, type );
|
|
return &m_buf.front() + start * m_elemSize;
|
|
}
|
|
|
|
const char* getStringValue( long strIndex ) const
|
|
{
|
|
return m_pParent->getString( strIndex );
|
|
}
|
|
|
|
const wchar_t* getWStringValue( long wstrIndex ) const
|
|
{
|
|
return m_pParent->getWString( wstrIndex );
|
|
}
|
|
|
|
void init( int elemType )
|
|
{
|
|
m_elemType = static_cast< unsigned char >( elemType );
|
|
m_elemSize = Type::getTypeSize( elemType );
|
|
|
|
assert( Type::isSimpleType( elemType ) );
|
|
}
|
|
|
|
void clear()
|
|
{
|
|
m_buf.clear();
|
|
}
|
|
|
|
bool addSimpleDataArray( const void* pData, int type, int count )
|
|
{
|
|
if ( m_elemType != type || count < 0 )
|
|
return false;
|
|
|
|
int size = static_cast< int >( m_buf.size() );
|
|
m_buf.resize( size + count * m_elemSize );
|
|
if ( count > 0 )
|
|
s_memcpy( &m_buf.front() + size, m_buf.size() - size, pData, count * m_elemSize );
|
|
|
|
return true;
|
|
}
|
|
|
|
bool setSimpleDataAt( int i, const void* pData, int type )
|
|
{
|
|
if ( i < 0 || i >= count() || m_elemType != type )
|
|
return false;
|
|
|
|
s_memcpy( &m_buf.front() + (m_elemSize*i), m_buf.size() - (m_elemSize*i), pData, m_elemSize );
|
|
return true;
|
|
}
|
|
|
|
long addStringValue( const char* pStrValue )
|
|
{
|
|
return m_pParent->addString( pStrValue );
|
|
}
|
|
|
|
long addWStringValue( const wchar_t* pWStrValue )
|
|
{
|
|
return m_pParent->addWString( pWStrValue );
|
|
}
|
|
|
|
void makeTables( int& newStrIndex, int& newWStrIndex );
|
|
virtual int save( KStream& stream );
|
|
virtual bool load( KStream& stream, MergeTable& mergeTable );
|
|
|
|
protected:
|
|
|
|
void check_error( int i, int type ) const
|
|
{
|
|
assert( i >= 0 && i < count() );
|
|
assert( type == m_elemType );
|
|
}
|
|
|
|
Imp* m_pParent;
|
|
unsigned char m_elemType;
|
|
int m_elemSize;
|
|
|
|
std::vector< char > m_buf;
|
|
|
|
};
|
|
|
|
class FilerFixedArray: public FilerArray
|
|
{
|
|
public:
|
|
|
|
FilerFixedArray( Imp* pParent, int elemType, int count )
|
|
: FilerArray( pParent, elemType )
|
|
{
|
|
m_maxCount = count;
|
|
}
|
|
|
|
bool addSimpleDataArray( const void* pData, int type, int count )
|
|
{
|
|
if ( this->count() + count > m_maxCount )
|
|
return false;
|
|
|
|
return FilerArray::addSimpleDataArray( pData, type, count );
|
|
|
|
}
|
|
|
|
int save( KStream& stream );
|
|
bool load( KStream& stream, MergeTable& mergeTable );
|
|
|
|
protected:
|
|
|
|
int m_maxCount;
|
|
};
|
|
|
|
|
|
// -----------------------------------------------------------------------------------
|
|
|
|
|
|
class FilerDict: public IDict
|
|
{
|
|
public:
|
|
|
|
FilerDict( Imp* pParent )
|
|
{
|
|
m_pParent = pParent;
|
|
}
|
|
|
|
~FilerDict()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
int indexBy( const char* pName ) const
|
|
{
|
|
if ( pName == NULL )
|
|
return -1;
|
|
|
|
int name = m_pParent->getStringIndex( pName );
|
|
for ( int i = 0, n = static_cast< int >( m_fields.size() ); i < n; i++ )
|
|
{
|
|
if ( m_fields[ i ].name == name )
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int count() const
|
|
{
|
|
return int( m_fields.size() );
|
|
}
|
|
|
|
const char* nameAt( int i ) const
|
|
{
|
|
assert( i >= 0 && i < count() );
|
|
return m_pParent->getString( m_fields[ i ].name );
|
|
}
|
|
|
|
int typeAt( int i ) const
|
|
{
|
|
assert( i >= 0 && i < count() );
|
|
return m_fields[ i ].type;
|
|
}
|
|
|
|
int fixedSizeAt( int i ) const
|
|
{
|
|
int type = typeAt( i );
|
|
|
|
if ( Type::isSimpleType( type ) )
|
|
return Type::getTypeSize( type );
|
|
else if ( type == Type::TEMPLATE )
|
|
return m_fields[ i ].value.asTemplate->fixedSize();
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
const void* getSimpleDataAt( int i, int type ) const
|
|
{
|
|
assert( i >= 0 && i < count() && typeAt( i ) == type );
|
|
switch ( type )
|
|
{
|
|
case Type::CHAR: return &m_fields[ i ].value.asChar;
|
|
case Type::SHORT: return &m_fields[ i ].value.asShort;
|
|
case Type::STRING:
|
|
case Type::WSTRING:
|
|
case Type::LONG: return &m_fields[ i ].value.asLong;
|
|
case Type::LLONG: return &m_fields[ i ].value.asLLong;
|
|
case Type::FLOAT: return &m_fields[ i ].value.asFloat;
|
|
case Type::DOUBLE: return &m_fields[ i ].value.asDouble;
|
|
default:
|
|
assert( false );
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
const char* getStringValue( long strIndex ) const
|
|
{
|
|
return m_pParent->getString( strIndex );
|
|
}
|
|
|
|
const wchar_t* getWStringValue( long wstrIndex ) const
|
|
{
|
|
return m_pParent->getWString( wstrIndex );
|
|
}
|
|
|
|
const IArray* getArrayAt( int i ) const
|
|
{
|
|
assert( i >= 0 && i < count() && Type::isSimpleArrayType( typeAt( i ) ) );
|
|
return m_fields[ i ].value.asArray;
|
|
}
|
|
|
|
const ITemplate* getTemplateAt( int i ) const
|
|
{
|
|
assert( i >= 0 && i < count() && typeAt( i ) == Type::TEMPLATE );
|
|
return m_fields[ i ].value.asTemplate;
|
|
}
|
|
|
|
const ITemplateArray* getTemplateArrayAt( int i ) const
|
|
{
|
|
assert( i >= 0 && i < count() && typeAt( i ) == Type::TEMPLATE_ARRAY );
|
|
return m_fields[ i ].value.asTemplateArray;
|
|
}
|
|
|
|
const IDict* getDictAt( int i ) const
|
|
{
|
|
assert( i >= 0 && i < count() && typeAt( i ) == Type::DICT );
|
|
return m_fields[ i ].value.asDict;
|
|
}
|
|
|
|
void clear()
|
|
{
|
|
for ( int i = 0, n = static_cast< int >( m_fields.size() ); i < n; i++ )
|
|
{
|
|
int type = typeAt( i );
|
|
if ( Type::isSimpleArrayType( type ) )
|
|
delete m_fields[ i ].value.asArray;
|
|
else if ( type == Type::TEMPLATE_ARRAY )
|
|
delete m_fields[ i ].value.asTemplateArray;
|
|
else if ( type == Type::TEMPLATE )
|
|
delete m_fields[ i ].value.asTemplate;
|
|
else if ( type == Type::DICT )
|
|
delete m_fields[ i ].value.asDict;
|
|
}
|
|
|
|
m_fields.clear();
|
|
}
|
|
|
|
bool addSimpleData( const char* pName, const void* pData, int type )
|
|
{
|
|
if ( indexBy( pName ) != -1 )
|
|
return false;
|
|
|
|
_add_data( pName, type );
|
|
|
|
switch ( type )
|
|
{
|
|
case Type::CHAR: m_fields.back().value.asChar = *reinterpret_cast< const char* >( pData ); break;
|
|
case Type::SHORT: m_fields.back().value.asShort = *reinterpret_cast< const short* >( pData ); break;
|
|
case Type::STRING:
|
|
case Type::WSTRING:
|
|
case Type::LONG: m_fields.back().value.asLong = *reinterpret_cast< const long* >( pData ); break;
|
|
case Type::LLONG: m_fields.back().value.asLLong = *reinterpret_cast< const long long* >( pData ); break;
|
|
case Type::FLOAT: m_fields.back().value.asFloat = *reinterpret_cast< const float* >( pData ); break;
|
|
case Type::DOUBLE: m_fields.back().value.asDouble = *reinterpret_cast< const double* >( pData ); break;
|
|
default:
|
|
m_fields.pop_back();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool setSimpleDataAt( int i, const void* pData, int type )
|
|
{
|
|
if ( i >= count() || i < 0 || m_fields[ i ].type != type )
|
|
return false;
|
|
|
|
switch ( type )
|
|
{
|
|
case Type::CHAR: m_fields[ i ].value.asChar = *reinterpret_cast< const char* >( pData ); break;
|
|
case Type::SHORT: m_fields[ i ].value.asShort = *reinterpret_cast< const short* >( pData ); break;
|
|
case Type::STRING:
|
|
case Type::WSTRING:
|
|
case Type::LONG: m_fields[ i ].value.asLong = *reinterpret_cast< const long* >( pData ); break;
|
|
case Type::LLONG: m_fields[ i ].value.asLLong = *reinterpret_cast< const long long* >( pData ); break;
|
|
case Type::FLOAT: m_fields[ i ].value.asFloat = *reinterpret_cast< const float* >( pData ); break;
|
|
case Type::DOUBLE: m_fields[ i ].value.asDouble = *reinterpret_cast< const double* >( pData ); break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
IArray* arrayAt( int i )
|
|
{
|
|
if ( i >= count() || i < 0 || !Type::isSimpleArrayType( m_fields[ i ].type ) )
|
|
return NULL;
|
|
|
|
return m_fields[ i ].value.asArray;
|
|
}
|
|
|
|
ITemplate* templateAt( int i )
|
|
{
|
|
if ( i >= count() || i < 0 || m_fields[ i ].type != Type::TEMPLATE )
|
|
return NULL;
|
|
|
|
return m_fields[ i ].value.asTemplate;
|
|
}
|
|
|
|
ITemplateArray* templateArrayAt( int i )
|
|
{
|
|
if ( i >= count() || i < 0 || m_fields[ i ].type != Type::TEMPLATE_ARRAY )
|
|
return NULL;
|
|
|
|
return m_fields[ i ].value.asTemplateArray;
|
|
}
|
|
|
|
IDict* dictAt( int i )
|
|
{
|
|
if ( i >= count() || i < 0 || m_fields[ i ].type != Type::DICT )
|
|
return NULL;
|
|
|
|
return m_fields[ i ].value.asDict;
|
|
}
|
|
|
|
long addStringValue( const char* pStrValue )
|
|
{
|
|
return m_pParent->addString( pStrValue );
|
|
}
|
|
|
|
long addWStringValue( const wchar_t* pWStrValue )
|
|
{
|
|
return m_pParent->addWString( pWStrValue );
|
|
}
|
|
|
|
IArray* addNewArray( const char* pName, int type )
|
|
{
|
|
if ( indexBy( pName ) != -1 )
|
|
return NULL;
|
|
|
|
_add_data( pName, Type::getArrayType( type ) );
|
|
m_fields.back().value.asArray = new FilerArray( m_pParent, type );
|
|
return m_fields.back().value.asArray;
|
|
}
|
|
|
|
ITemplate* addNewTemplate( const char* pName, const TemplateInfo* pInfo )
|
|
{
|
|
if ( indexBy( pName ) != -1 )
|
|
return NULL;
|
|
|
|
_add_data( pName, Type::TEMPLATE );
|
|
m_fields.back().value.asTemplate = new FilerTemplate( m_pParent, m_pParent->getATI( pInfo->getMetaDataIndex() ) );
|
|
return m_fields.back().value.asTemplate;
|
|
}
|
|
|
|
ITemplateArray* addNewTemplateArray( const char* pName, const TemplateInfo* pInfo )
|
|
{
|
|
if ( indexBy( pName ) != -1 )
|
|
return NULL;
|
|
|
|
_add_data( pName, Type::TEMPLATE_ARRAY );
|
|
m_fields.back().value.asTemplateArray = new FilerTemplateArray( m_pParent, m_pParent->getATI( pInfo->getMetaDataIndex() ) );
|
|
return m_fields.back().value.asTemplateArray;
|
|
}
|
|
|
|
IDict* addNewDict( const char* pName )
|
|
{
|
|
if ( indexBy( pName ) != -1 )
|
|
return NULL;
|
|
|
|
_add_data( pName, Type::DICT );
|
|
m_fields.back().value.asDict = new FilerDict( m_pParent );
|
|
return m_fields.back().value.asDict;
|
|
}
|
|
|
|
void makeTables( int& newStrIndex, int& newWStrIndex );
|
|
int save( KStream& stream );
|
|
bool load( KStream& stream, MergeTable& mergeTable );
|
|
|
|
protected:
|
|
|
|
void _add_data( const char* pName, int type )
|
|
{
|
|
m_fields.resize( m_fields.size() + 1 );
|
|
m_fields.back().name = m_pParent->addString( pName );
|
|
m_fields.back().type = static_cast< unsigned char >( type );
|
|
}
|
|
|
|
Imp* m_pParent;
|
|
|
|
struct Field
|
|
{
|
|
unsigned char type;
|
|
long name;
|
|
Var value;
|
|
};
|
|
|
|
std::vector< Field > m_fields;
|
|
};
|
|
|
|
|
|
// =================================================================================================
|
|
|
|
|
|
const IArray* FilerTemplate::getArrayAt( int i ) const
|
|
{
|
|
assert( i >= 0 && i < count() && Type::isSimpleArrayType( typeAt( i ) ) );
|
|
return m_fields[ i ].asArray;
|
|
}
|
|
|
|
const ITemplate* FilerTemplate::getTemplateAt( int i ) const
|
|
{
|
|
assert( i >= 0 && i < count() && typeAt( i ) == Type::TEMPLATE );
|
|
return m_fields[ i ].asTemplate;
|
|
}
|
|
|
|
const ITemplateArray* FilerTemplate::getTemplateArrayAt( int i ) const
|
|
{
|
|
assert( i >= 0 && i < count() && typeAt( i ) == Type::TEMPLATE_ARRAY );
|
|
return m_fields[ i ].asTemplateArray;
|
|
}
|
|
|
|
int FilerTemplate::readFixedData( void* pOutBuf, int bufSize ) const
|
|
{
|
|
int size = fixedSize();
|
|
if ( size < 0 )
|
|
return -1;
|
|
if ( bufSize > 0 && bufSize < size )
|
|
return 0;
|
|
|
|
char* p = reinterpret_cast< char* >( pOutBuf );
|
|
for ( int i = 0, n = count(); i < n; i++ )
|
|
{
|
|
switch ( typeAt( i ) )
|
|
{
|
|
case Type::CHAR: *(p++) = m_fields[ i ].asChar; break;
|
|
case Type::SHORT: *(reinterpret_cast< short*& >( p )++) = m_fields[ i ].asShort; break;
|
|
case Type::STRING:
|
|
case Type::WSTRING:
|
|
case Type::LONG: *(reinterpret_cast< long*& >( p )++) = m_fields[ i ].asLong; break;
|
|
case Type::LLONG: *(reinterpret_cast< long long*& >( p )++) = m_fields[ i ].asLLong; break;
|
|
case Type::FLOAT: *(reinterpret_cast< float*& >( p )++) = m_fields[ i ].asFloat; break;
|
|
case Type::DOUBLE: *(reinterpret_cast< double*& >( p )++) = m_fields[ i ].asDouble; break;
|
|
|
|
case Type::CHAR_ARRAY:
|
|
case Type::SHORT_ARRAY:
|
|
case Type::STRING_ARRAY:
|
|
case Type::WSTRING_ARRAY:
|
|
case Type::LONG_ARRAY:
|
|
case Type::LLONG_ARRAY:
|
|
case Type::FLOAT_ARRAY:
|
|
case Type::DOUBLE_ARRAY: p += m_fields[ i ].asArray->IArray::readFixedData( p ); break;
|
|
|
|
case Type::TEMPLATE: p += m_fields[ i ].asTemplate->readFixedData( p ); break;
|
|
case Type::TEMPLATE_ARRAY: p += m_fields[ i ].asTemplateArray->ITemplateArray::readFixedData( p ); break;
|
|
|
|
default:
|
|
case Type::DICT:
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
const IDict* FilerTemplate::getDictAt( int i ) const
|
|
{
|
|
assert( i >= 0 && i < count() && typeAt( i ) == Type::DICT );
|
|
return m_fields[ i ].asDict;
|
|
}
|
|
|
|
IArray* FilerTemplate::arrayAt( int i )
|
|
{
|
|
if ( !Type::isSimpleArrayType( typeAt( i ) ) )
|
|
return NULL;
|
|
|
|
if ( m_fields[ i ].asArray == NULL )
|
|
{
|
|
if ( fixedCountAt( i ) >= 0 )
|
|
m_fields[ i ].asArray = new FilerFixedArray( m_pParent, Type::getElementType( typeAt( i ) ), fixedCountAt( i ) );
|
|
else
|
|
m_fields[ i ].asArray = new FilerArray( m_pParent, Type::getElementType( typeAt( i ) ) );
|
|
}
|
|
|
|
return m_fields[ i ].asArray;
|
|
}
|
|
|
|
ITemplate* FilerTemplate::templateAt( int i )
|
|
{
|
|
if ( typeAt( i ) != Type::TEMPLATE )
|
|
return NULL;
|
|
|
|
if ( m_fields[ i ].asTemplate == NULL )
|
|
m_fields[ i ].asTemplate = new FilerTemplate( m_pParent, m_pParent->getATI( info()->infoAt( i )->getMetaDataIndex() ) );
|
|
return m_fields[ i ].asTemplate;
|
|
}
|
|
|
|
ITemplateArray* FilerTemplate::templateArrayAt( int i )
|
|
{
|
|
if ( typeAt( i ) != Type::TEMPLATE_ARRAY )
|
|
return NULL;
|
|
|
|
if ( m_fields[ i ].asTemplateArray == NULL )
|
|
{
|
|
const ATInfo* pFL = m_pParent->getATI( m_pATI->pInfo->infoAt( i )->getMetaDataIndex() );
|
|
if ( fixedCountAt( i ) >= 0 )
|
|
m_fields[ i ].asTemplateArray = new FilerFixedTemplateArray( m_pParent, pFL, fixedCountAt( i ) );
|
|
else
|
|
m_fields[ i ].asTemplateArray = new FilerTemplateArray( m_pParent, pFL );
|
|
}
|
|
|
|
return m_fields[ i ].asTemplateArray;
|
|
}
|
|
|
|
IDict* FilerTemplate::dictAt( int i )
|
|
{
|
|
if ( typeAt( i ) != Type::DICT )
|
|
return NULL;
|
|
|
|
if ( m_fields[ i ].asDict == NULL )
|
|
m_fields[ i ].asDict = new FilerDict( m_pParent );
|
|
return m_fields[ i ].asDict;
|
|
}
|
|
|
|
void FilerTemplate::init( const ATInfo* pFL )
|
|
{
|
|
m_pATI = pFL;
|
|
if ( pFL == NULL )
|
|
return;
|
|
|
|
// filer 는 내부적으로 field limit 를 무시한다
|
|
|
|
m_fields.resize( m_pATI->pInfo->count() );
|
|
memset( &m_fields.front(), 0, m_pATI->pInfo->count() * sizeof( Var ) );
|
|
|
|
if ( m_pParent->m_pTemplateInitCallback )
|
|
m_pParent->m_pTemplateInitCallback( m_pATI->pInfo, this );
|
|
|
|
for ( int i = 0, n = m_pATI->pInfo->count(); i < n; i++ )
|
|
{
|
|
int type = typeAt( i );
|
|
if ( Type::isSimpleArrayType( type ) )
|
|
{
|
|
if ( fixedCountAt( i ) >= 0 )
|
|
m_fields[ i ].asArray = new FilerFixedArray( m_pParent, Type::getElementType( type ), fixedCountAt( i ) );
|
|
else
|
|
m_fields[ i ].asArray = new FilerArray( m_pParent, Type::getElementType( type ) );
|
|
}
|
|
else if ( type == Type::TEMPLATE_ARRAY )
|
|
{
|
|
const ATInfo* pFL = m_pParent->getATI( m_pATI->pInfo->infoAt( i )->getMetaDataIndex() );
|
|
if ( fixedCountAt( i ) >= 0 )
|
|
m_fields[ i ].asTemplateArray = new FilerFixedTemplateArray( m_pParent, pFL, fixedCountAt( i ) );
|
|
else
|
|
m_fields[ i ].asTemplateArray = new FilerTemplateArray( m_pParent, pFL );
|
|
}
|
|
else if ( type == Type::TEMPLATE )
|
|
{
|
|
const ATInfo* pFL = m_pParent->getATI( m_pATI->pInfo->infoAt( i )->getMetaDataIndex() );
|
|
m_fields[ i ].asTemplate = new FilerTemplate( m_pParent, pFL );
|
|
}
|
|
else if ( type == Type::DICT )
|
|
m_fields[ i ].asDict = new FilerDict( m_pParent );
|
|
}
|
|
}
|
|
|
|
void FilerTemplate::close()
|
|
{
|
|
for ( int i = 0, n = m_pATI->pInfo->count(); i < n; i++ )
|
|
{
|
|
int type = typeAt( i );
|
|
if ( Type::isSimpleArrayType( type ) )
|
|
delete m_fields[ i ].asArray;
|
|
else if ( type == Type::TEMPLATE )
|
|
delete m_fields[ i ].asTemplate;
|
|
else if ( type == Type::TEMPLATE_ARRAY )
|
|
delete m_fields[ i ].asTemplateArray;
|
|
else if ( type == Type::DICT )
|
|
delete m_fields[ i ].asDict;
|
|
}
|
|
|
|
m_fields.clear();
|
|
}
|
|
|
|
// =================================================================================================
|
|
|
|
// Binary save & load routines
|
|
|
|
void FilerTemplate::makeTables( int& newStrIndex, int& newWStrIndex )
|
|
{
|
|
for ( int i = 0, n = m_pATI->pInfo->count(); i < n; i++ )
|
|
{
|
|
int type = typeAt( i );
|
|
if ( type == Type::TEMPLATE )
|
|
{
|
|
FilerTemplate* pTpl = m_fields[ i ].asTemplate;
|
|
m_pParent->addId( &pTpl->classId() );
|
|
pTpl->makeTables( newStrIndex, newWStrIndex );
|
|
}
|
|
else if ( type == Type::TEMPLATE_ARRAY )
|
|
{
|
|
FilerTemplateArray* pTpls = m_fields[ i ].asTemplateArray;
|
|
m_pParent->addId( &pTpls->info()->id() );
|
|
pTpls->makeTables( newStrIndex, newWStrIndex );
|
|
}
|
|
else if ( Type::isSimpleArrayType( type ) )
|
|
{
|
|
FilerArray* pArray = m_fields[ i ].asArray;
|
|
pArray->makeTables( newStrIndex, newWStrIndex );
|
|
}
|
|
else if ( type == Type::DICT )
|
|
{
|
|
FilerDict* pDict = m_fields[ i ].asDict;
|
|
pDict->makeTables( newStrIndex, newWStrIndex );
|
|
}
|
|
else if ( type == Type::STRING )
|
|
{
|
|
m_fields[ i ].asLong = m_pParent->setStringIndex( m_fields[ i ].asLong, newStrIndex );
|
|
}
|
|
else if ( type == Type::WSTRING )
|
|
{
|
|
m_fields[ i ].asLong = m_pParent->setWStringIndex( m_fields[ i ].asLong, newWStrIndex );
|
|
}
|
|
}
|
|
}
|
|
|
|
void FilerTemplateArray::makeTables( int& newStrIndex, int& newWStrIndex )
|
|
{
|
|
for ( int i = 0, n = count(); i < n; i++ )
|
|
{
|
|
FilerTemplate* pTpl = m_fields[ i ];
|
|
pTpl->makeTables( newStrIndex, newWStrIndex );
|
|
}
|
|
}
|
|
|
|
void FilerArray::makeTables( int& newStrIndex, int& newWStrIndex )
|
|
{
|
|
if ( type() == Type::STRING )
|
|
{
|
|
long* p = count() ? reinterpret_cast< long* >( &m_buf.front() ) : NULL;
|
|
for ( int i = 0, n = count(); i < n; i++ )
|
|
{
|
|
p[ i ] = m_pParent->setStringIndex( p[ i ], newStrIndex );
|
|
}
|
|
}
|
|
else if ( type() == Type::WSTRING )
|
|
{
|
|
long* p = count() ? reinterpret_cast< long* >( &m_buf.front() ) : NULL;
|
|
for ( int i = 0, n = count(); i < n; i++ )
|
|
{
|
|
p[ i ] = m_pParent->setWStringIndex( p[ i ], newWStrIndex );
|
|
}
|
|
}
|
|
}
|
|
|
|
void FilerDict::makeTables( int& newStrIndex, int& newWStrIndex )
|
|
{
|
|
for ( int i = 0, n = count(); i < n; i++ )
|
|
{
|
|
m_fields[ i ].name = m_pParent->setStringIndex( m_fields[ i ].name, newStrIndex );
|
|
|
|
int type = typeAt( i );
|
|
if ( type == Type::TEMPLATE )
|
|
{
|
|
FilerTemplate* pTpl = m_fields[ i ].value.asTemplate;
|
|
m_pParent->addId( &pTpl->classId() );
|
|
pTpl->makeTables( newStrIndex, newWStrIndex );
|
|
}
|
|
else if ( type == Type::TEMPLATE_ARRAY )
|
|
{
|
|
FilerTemplateArray* pTpls = m_fields[ i ].value.asTemplateArray;
|
|
m_pParent->addId( &pTpls->info()->id() );
|
|
pTpls->makeTables( newStrIndex, newWStrIndex );
|
|
}
|
|
else if ( Type::isSimpleArrayType( type ) )
|
|
{
|
|
FilerArray* pArray = m_fields[ i ].value.asArray;
|
|
pArray->makeTables( newStrIndex, newWStrIndex );
|
|
}
|
|
else if ( type == Type::DICT )
|
|
{
|
|
FilerDict* pDict = m_fields[ i ].value.asDict;
|
|
pDict->makeTables( newStrIndex, newWStrIndex );
|
|
}
|
|
else if ( type == Type::STRING )
|
|
{
|
|
m_fields[ i ].value.asLong = m_pParent->setStringIndex( m_fields[ i ].value.asLong, newStrIndex );
|
|
}
|
|
else if ( type == Type::WSTRING )
|
|
{
|
|
m_fields[ i ].value.asLong = m_pParent->setWStringIndex( m_fields[ i ].value.asLong, newWStrIndex );
|
|
}
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------
|
|
|
|
static inline unsigned int SaveVar( KStream& stream, int type, const Var& value, bool bID = true, bool bInfoUnknown = false )
|
|
{
|
|
switch ( type )
|
|
{
|
|
case Type::CHAR:
|
|
return static_cast< unsigned int >( stream.Write( &value.asChar, sizeof(value.asChar) ) );
|
|
|
|
case Type::SHORT:
|
|
return static_cast< unsigned int >( stream.Write( &value.asShort, sizeof(value.asShort) ) );
|
|
|
|
case Type::STRING:
|
|
case Type::WSTRING:
|
|
case Type::LONG:
|
|
return static_cast< unsigned int >( stream.Write( &value.asLong, sizeof(value.asLong) ) );
|
|
|
|
case Type::LLONG:
|
|
return static_cast< unsigned int >( stream.Write( &value.asLLong, sizeof(value.asLLong) ) );
|
|
|
|
case Type::FLOAT:
|
|
return static_cast< unsigned int >( stream.Write( &value.asFloat, sizeof(value.asFloat) ) );
|
|
|
|
case Type::DOUBLE:
|
|
return static_cast< unsigned int >( stream.Write( &value.asDouble, sizeof(value.asDouble) ) );
|
|
|
|
case Type::CHAR_ARRAY:
|
|
case Type::SHORT_ARRAY:
|
|
case Type::LONG_ARRAY:
|
|
case Type::STRING_ARRAY:
|
|
case Type::WSTRING_ARRAY:
|
|
case Type::FLOAT_ARRAY:
|
|
case Type::DOUBLE_ARRAY:
|
|
return value.asArray->save( stream );
|
|
|
|
case Type::TEMPLATE:
|
|
return value.asTemplate->save( stream, bID, bInfoUnknown );
|
|
|
|
case Type::TEMPLATE_ARRAY:
|
|
return value.asTemplateArray->save( stream );
|
|
|
|
default:
|
|
case Type::DICT:
|
|
return value.asDict->save( stream );
|
|
}
|
|
}
|
|
|
|
static inline bool StreamRead( KStream& stream, void* pBuf, unsigned int readSize )
|
|
{
|
|
return stream.Read( pBuf, readSize ) == readSize;
|
|
}
|
|
|
|
static inline bool LoadVar( KStream& stream, int type, Var& outValue, MergeTable& mergeTable, bool bID = true, bool bInfoUnknown = false )
|
|
{
|
|
bool b;
|
|
switch ( type )
|
|
{
|
|
case Type::CHAR:
|
|
return StreamRead( stream, &outValue.asChar, sizeof(outValue.asChar) );
|
|
|
|
case Type::SHORT:
|
|
return StreamRead( stream, &outValue.asShort, sizeof(outValue.asShort) );
|
|
|
|
case Type::LONG:
|
|
return StreamRead( stream, &outValue.asLong, sizeof(outValue.asLong) );
|
|
|
|
case Type::LLONG:
|
|
return StreamRead( stream, &outValue.asLLong, sizeof(outValue.asLLong) );
|
|
|
|
case Type::FLOAT:
|
|
return StreamRead( stream, &outValue.asFloat, sizeof(outValue.asFloat) );
|
|
|
|
case Type::DOUBLE:
|
|
return StreamRead( stream, &outValue.asDouble, sizeof(outValue.asDouble) );
|
|
|
|
case Type::STRING:
|
|
b = StreamRead( stream, &outValue.asLong, sizeof(outValue.asLong) );
|
|
if ( outValue.asLong > 0 && outValue.asLong <= long(mergeTable.str.size()) )
|
|
outValue.asLong = mergeTable.str[ outValue.asLong-1 ];
|
|
return b;
|
|
|
|
case Type::WSTRING:
|
|
b = StreamRead( stream, &outValue.asLong, sizeof(outValue.asLong) );
|
|
if ( outValue.asLong > 0 && outValue.asLong <= long(mergeTable.wstr.size()) )
|
|
outValue.asLong = mergeTable.wstr[ outValue.asLong-1 ];
|
|
return b;
|
|
|
|
case Type::CHAR_ARRAY:
|
|
case Type::SHORT_ARRAY:
|
|
case Type::LONG_ARRAY:
|
|
case Type::STRING_ARRAY:
|
|
case Type::WSTRING_ARRAY:
|
|
case Type::FLOAT_ARRAY:
|
|
case Type::DOUBLE_ARRAY:
|
|
return outValue.asArray->load( stream, mergeTable );
|
|
|
|
case Type::TEMPLATE:
|
|
return outValue.asTemplate->load( stream, bID, bInfoUnknown, mergeTable );
|
|
|
|
case Type::TEMPLATE_ARRAY:
|
|
return outValue.asTemplateArray->load( stream, mergeTable );
|
|
|
|
default:
|
|
case Type::DICT:
|
|
return outValue.asDict->load( stream, mergeTable );
|
|
}
|
|
}
|
|
|
|
int FilerTemplate::save( KStream& stream, bool bID, bool bInfoUnknown )
|
|
{
|
|
unsigned int pos = static_cast< unsigned int >( stream.Tell() );
|
|
|
|
if ( bInfoUnknown || m_pATI->pInfo->fixedSize() < 0 )
|
|
stream.Seek( sizeof(unsigned int), KStream::seekCur ); // 이 템플릿의 길이가 가변일 때만 길이를 기록한다
|
|
|
|
unsigned int total = 0;
|
|
|
|
if ( bID )
|
|
{
|
|
short nId = static_cast< short >( m_pParent->getIdIndex( &classId() ) ); // 템플릿 배열의 일부일 때는 id 를 각각 기록하지 않는다.
|
|
total += static_cast< unsigned int >( stream.Write( &nId, sizeof(nId) ) );
|
|
}
|
|
|
|
for ( int i = 0, n = m_pATI->pInfo->count(); i < n; i++ ) // 세이브할 때는 field limit 를 무시하고 항상 최신 버전으로 저장한다.
|
|
total += SaveVar( stream, typeAt( i ), m_fields[ i ], false, false );
|
|
|
|
if ( bInfoUnknown || m_pATI->pInfo->fixedSize() < 0 )
|
|
{
|
|
stream.Seek( pos, KStream::seekSet );
|
|
stream.Write( &total, sizeof(total) );
|
|
stream.Seek( total, KStream::seekCur );
|
|
return int( total ) + sizeof( unsigned int );
|
|
}
|
|
else
|
|
return int( total );
|
|
}
|
|
|
|
bool FilerTemplate::load( KStream& stream, bool bID, bool bInfoUnknown, MergeTable& mergeTable )
|
|
{
|
|
unsigned int size = 0, posEnd = 0;
|
|
|
|
if ( bInfoUnknown || fixedSize() < 0 )
|
|
{
|
|
if ( !StreamRead( stream, &size, sizeof(size) ) )
|
|
return false;
|
|
|
|
posEnd = static_cast< unsigned int >( stream.Tell() ) + size;
|
|
}
|
|
|
|
if ( bID )
|
|
{
|
|
short nId;
|
|
if ( !StreamRead( stream, &nId, sizeof(nId) ) )
|
|
return false;
|
|
|
|
if ( nId > 0 && nId <= long(mergeTable.id.size()) )
|
|
nId = mergeTable.id[ nId-1 ];
|
|
|
|
const GUID* pId = m_pParent->getId( nId );
|
|
const TemplateInfo* pInfo = m_pParent->m_pMetaData->getTemplateInfo( *pId );
|
|
|
|
if ( pInfo == NULL || (m_pATI != NULL && m_pATI->pInfo != pInfo) )
|
|
return false;
|
|
|
|
if ( m_pATI == NULL )
|
|
init( m_pParent->getATI( pInfo->getMetaDataIndex() ) );
|
|
}
|
|
|
|
for ( int i = 0, n = count(); i < n; i++ )
|
|
if ( !LoadVar( stream, typeAt( i ), m_fields[ i ], mergeTable, false, false ) )
|
|
return false;
|
|
|
|
if ( bInfoUnknown || fixedSize() < 0 )
|
|
stream.Seek( posEnd, KStream::seekSet );
|
|
|
|
return true;
|
|
}
|
|
|
|
int FilerFixedArray::save( KStream& stream )
|
|
{
|
|
m_buf.resize( m_elemSize * m_maxCount, 0 );
|
|
return static_cast< int >( stream.Write( &m_buf.front(), m_maxCount * m_elemSize ) );
|
|
}
|
|
|
|
bool FilerFixedArray::load( KStream& stream, MergeTable& mergeTable )
|
|
{
|
|
assert( m_elemType != Type::_NONE );
|
|
|
|
bool b = true;
|
|
|
|
if ( m_maxCount > 0 )
|
|
{
|
|
m_buf.resize( m_elemSize * m_maxCount, 0 );
|
|
b = StreamRead( stream, &m_buf.front(), m_elemSize * m_maxCount );
|
|
|
|
long* p = reinterpret_cast< long* >( &m_buf.front() );
|
|
|
|
if ( m_elemType == Type::STRING )
|
|
{
|
|
for ( int i = 0; i < m_maxCount; i++ )
|
|
p[i] = mergeTable.str[ p[i]-1 ];
|
|
}
|
|
else if ( m_elemType == Type::WSTRING )
|
|
{
|
|
for ( int i = 0; i < m_maxCount; i++ )
|
|
p[i] = mergeTable.wstr[ p[i]-1 ];
|
|
}
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
int FilerFixedTemplateArray::save( KStream& stream )
|
|
{
|
|
unsigned int pos = static_cast< unsigned int >( stream.Tell() );
|
|
|
|
if ( m_pATI->pInfo->fixedSize() < 0 )
|
|
stream.Seek( sizeof(unsigned int), KStream::seekCur ); // 원소 템플릿의 길이가 가변일 때만 전체 길이를 기록한다
|
|
|
|
unsigned int total = 0;
|
|
int i,n;
|
|
|
|
for ( i = 0, n = count(); i < n; i++ )
|
|
total += m_fields[ i ]->save( stream, false, false );
|
|
|
|
for ( i = static_cast< int >( m_fields.size() ); i < m_maxCount; i++ )
|
|
{
|
|
m_fields.push_back( new FilerTemplate( m_pParent, m_pATI ) );
|
|
total += m_fields.back()->save( stream, false, false );
|
|
}
|
|
|
|
if ( m_pATI->pInfo->fixedSize() < 0 )
|
|
{
|
|
stream.Seek( pos, KStream::seekSet );
|
|
stream.Write( &total, sizeof(total) );
|
|
stream.Seek( total, KStream::seekCur );
|
|
return int( total ) + sizeof( unsigned int );
|
|
}
|
|
else
|
|
return int( total );
|
|
}
|
|
|
|
bool FilerFixedTemplateArray::load( KStream& stream, MergeTable& mergeTable )
|
|
{
|
|
assert( m_pATI != NULL );
|
|
|
|
unsigned int size = 0, posEnd = 0;
|
|
if ( m_pATI->pInfo->fixedSize() < 0 )
|
|
{
|
|
if ( !StreamRead( stream, &size, sizeof(size) ) )
|
|
return false;
|
|
|
|
posEnd = static_cast< unsigned int >( stream.Tell() ) + size;
|
|
}
|
|
|
|
m_fields.resize( m_maxCount );
|
|
for ( int i = 0; i < m_maxCount; i++ )
|
|
{
|
|
m_fields[ i ] = new FilerTemplate( m_pParent, m_pATI );
|
|
if ( !m_fields[ i ]->load( stream, false, false, mergeTable ) )
|
|
return false;
|
|
}
|
|
|
|
if ( m_pATI->pInfo->fixedSize() < 0 )
|
|
stream.Seek( posEnd, KStream::seekSet );
|
|
return true;
|
|
}
|
|
|
|
int FilerTemplateArray::save( KStream& stream )
|
|
{
|
|
unsigned int pos = static_cast< unsigned int >( stream.Tell() );
|
|
stream.Seek( sizeof(unsigned int), KStream::seekCur );
|
|
|
|
short nId = static_cast< short >( m_pParent->getIdIndex( &info()->id() ) );
|
|
unsigned int total = static_cast< unsigned int >( stream.Write( &nId, sizeof(nId) ) );
|
|
|
|
long n = count();
|
|
total += static_cast< unsigned int >( stream.Write( &n, sizeof(n) ) );
|
|
|
|
for ( int i = 0, n = count(); i < n; i++ )
|
|
total += m_fields[ i ]->save( stream, false, false );
|
|
|
|
stream.Seek( pos, KStream::seekSet );
|
|
stream.Write( &total, sizeof(total) );
|
|
stream.Seek( total, KStream::seekCur );
|
|
|
|
return int( total ) + sizeof( unsigned int );
|
|
}
|
|
|
|
bool FilerTemplateArray::load( KStream& stream, MergeTable& mergeTable )
|
|
{
|
|
unsigned int size;
|
|
if ( !StreamRead( stream, &size, sizeof(size) ) )
|
|
return false;
|
|
|
|
unsigned int posEnd = static_cast< unsigned int >( stream.Tell() ) + size;
|
|
|
|
short nId;
|
|
if ( !StreamRead( stream, &nId, sizeof(nId) ) )
|
|
return false;
|
|
|
|
if ( nId > 0 && nId <= long(mergeTable.id.size()) )
|
|
nId = mergeTable.id[ nId-1 ];
|
|
|
|
const GUID* pId = m_pParent->getId( nId );
|
|
m_pATI = m_pParent->getATI( m_pParent->m_pMetaData->getTemplateInfo( *pId )->getMetaDataIndex() );
|
|
|
|
if ( m_pATI == NULL )
|
|
return false;
|
|
|
|
long n;
|
|
if ( !StreamRead( stream, &n, sizeof(n) ) || n < 0 )
|
|
return false;
|
|
|
|
m_fields.resize( n );
|
|
for ( int i = 0; i < n; i++ )
|
|
{
|
|
m_fields[ i ] = new FilerTemplate( m_pParent, m_pATI );
|
|
if ( !m_fields[ i ]->load( stream, false, false, mergeTable ) )
|
|
return false;
|
|
}
|
|
|
|
stream.Seek( posEnd, KStream::seekSet );
|
|
return true;
|
|
}
|
|
|
|
int FilerArray::save( KStream& stream )
|
|
{
|
|
unsigned int pos = static_cast< unsigned int >( stream.Tell() );
|
|
stream.Seek( sizeof(unsigned int), KStream::seekCur );
|
|
|
|
unsigned int total = static_cast< unsigned int >( stream.Write( &m_elemType, sizeof(m_elemType) ) );
|
|
|
|
long n = count();
|
|
if ( n > 0 )
|
|
total += static_cast< unsigned int >( stream.Write( &m_buf.front(), n * m_elemSize ) );
|
|
|
|
stream.Seek( pos, KStream::seekSet );
|
|
stream.Write( &total, sizeof(total) );
|
|
stream.Seek( total, KStream::seekCur );
|
|
|
|
return int( total ) + sizeof( unsigned int );
|
|
}
|
|
|
|
bool FilerArray::load( KStream& stream, MergeTable& mergeTable )
|
|
{
|
|
unsigned int size;
|
|
if ( !StreamRead( stream, &size, sizeof(size) ) )
|
|
return false;
|
|
|
|
unsigned int posEnd = static_cast< unsigned int >( stream.Tell() ) + size;
|
|
|
|
unsigned char elemType;
|
|
if ( !StreamRead( stream, &elemType, sizeof(elemType) ) )
|
|
return false;
|
|
|
|
if ( elemType == Type::_NONE || (m_elemType != Type::_NONE && m_elemType != elemType ) )
|
|
return false;
|
|
|
|
if ( m_elemType == Type::_NONE )
|
|
init( elemType );
|
|
|
|
long n = (size - sizeof(elemType)) / m_elemSize;
|
|
m_buf.resize( n * m_elemSize );
|
|
|
|
if ( n > 0 )
|
|
{
|
|
if ( !StreamRead( stream, &m_buf.front(), static_cast< unsigned int >( m_buf.size() ) ) )
|
|
return false;
|
|
|
|
long* p = reinterpret_cast< long* >( &m_buf.front() );
|
|
|
|
if ( elemType == Type::STRING )
|
|
{
|
|
for ( int i = 0; i < n; i++ )
|
|
p[i] = mergeTable.str[ p[i]-1 ];
|
|
}
|
|
else if ( elemType == Type::WSTRING )
|
|
{
|
|
for ( int i = 0; i < n; i++ )
|
|
p[i] = mergeTable.wstr[ p[i]-1 ];
|
|
}
|
|
}
|
|
|
|
stream.Seek( posEnd, KStream::seekSet );
|
|
return true;
|
|
}
|
|
|
|
int FilerDict::save( KStream& stream )
|
|
{
|
|
unsigned int pos = static_cast< unsigned int >( stream.Tell() );
|
|
stream.Seek( sizeof(unsigned int), KStream::seekCur );
|
|
|
|
long n = count();
|
|
unsigned int total = static_cast< unsigned int >( stream.Write( &n, sizeof(n) ) );
|
|
|
|
for ( int i = 0; i < n; i++ )
|
|
{
|
|
Field& field = m_fields[ i ];
|
|
|
|
assert( (field.name & 0xff000000) == 0 );
|
|
|
|
unsigned long type_and_name = (field.type << 24) + (field.name & 0x00ffffff);
|
|
total += static_cast< unsigned int >( stream.Write( &type_and_name, sizeof(type_and_name) ) );
|
|
total += SaveVar( stream, field.type, field.value, true, true );
|
|
}
|
|
|
|
stream.Seek( pos, KStream::seekSet );
|
|
stream.Write( &total, sizeof(total) );
|
|
stream.Seek( total, KStream::seekCur );
|
|
|
|
return int( total ) + sizeof( unsigned int );
|
|
}
|
|
|
|
bool FilerDict::load( KStream& stream, MergeTable& mergeTable )
|
|
{
|
|
bool bUnpackedDict = m_pParent->m_bUnpackedDict;
|
|
|
|
unsigned int size;
|
|
if ( !StreamRead( stream, &size, sizeof(size) ) )
|
|
return false;
|
|
|
|
unsigned int posEnd = static_cast< unsigned int >( stream.Tell() ) + size;
|
|
|
|
long n;
|
|
if ( !StreamRead( stream, &n, sizeof(n) ) || n < 0 )
|
|
return false;
|
|
|
|
m_fields.reserve( m_fields.size() + n );
|
|
|
|
for ( int i = 0; i < n; i++ )
|
|
{
|
|
Field field;
|
|
if ( bUnpackedDict )
|
|
{
|
|
if ( !StreamRead( stream, &field.name, sizeof(field.name) ) )
|
|
return false;
|
|
if ( !StreamRead( stream, &field.type, sizeof(field.type) ) )
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
unsigned long type_and_name;
|
|
if ( !StreamRead( stream, &type_and_name, sizeof(type_and_name) ) )
|
|
return false;
|
|
field.name = long( type_and_name & 0x00ffffff );
|
|
field.type = unsigned char( type_and_name >> 24 );
|
|
}
|
|
|
|
if ( field.name > 0 )
|
|
field.name = mergeTable.str[ field.name-1 ];
|
|
|
|
int ndx = indexBy( m_pParent->getString( field.name ) );
|
|
if ( ndx < 0 )
|
|
{
|
|
// 이름이 NULL 이거나 같은 이름 없음. 뒤에 추가
|
|
|
|
if ( Type::isSimpleArrayType( field.type ) )
|
|
field.value.asArray = new FilerArray( m_pParent );
|
|
else if ( field.type == Type::TEMPLATE )
|
|
field.value.asTemplate = new FilerTemplate( m_pParent, NULL );
|
|
else if ( field.type == Type::TEMPLATE_ARRAY )
|
|
field.value.asTemplateArray = new FilerTemplateArray( m_pParent, NULL );
|
|
else if ( field.type == Type::DICT )
|
|
field.value.asDict = new FilerDict( m_pParent );
|
|
|
|
if ( !LoadVar( stream, field.type, field.value, mergeTable, true, true ) )
|
|
return false;
|
|
|
|
m_fields.push_back( field );
|
|
}
|
|
else
|
|
{
|
|
// 이름이 이미 존재. dict 라면 merge, 그 외는 skip.
|
|
|
|
if ( typeAt( ndx ) == Type::DICT && field.type == Type::DICT )
|
|
m_fields[ ndx ].value.asDict->load( stream, mergeTable );
|
|
else if ( Type::isSimpleType( field.type ) )
|
|
stream.Seek( Type::getTypeSize( field.type ), KStream::seekCur );
|
|
else
|
|
{
|
|
long size;
|
|
stream.Read( &size, sizeof(size) );
|
|
stream.Seek( size, KStream::seekCur );
|
|
}
|
|
}
|
|
}
|
|
|
|
stream.Seek( posEnd, KStream::seekSet );
|
|
return true;
|
|
}
|
|
|
|
// =================================================================================================
|
|
|
|
static char g_filler[8] = {'1','2','3','4','5','6','7','8'};
|
|
|
|
bool Imp::saveIdTable( KStream& stream )
|
|
{
|
|
long size = static_cast< unsigned int >( m_idTable.size() ) * (sizeof(GUID) + sizeof(unsigned short));
|
|
|
|
long skip = toAlign4( size );
|
|
size += skip;
|
|
|
|
stream.Write( &size, sizeof(size) );
|
|
|
|
if ( m_idTable.size() > 0 )
|
|
stream.Write( &m_idTable.front(), m_idTable.size() * sizeof(GUID) );
|
|
|
|
for ( int i = 0, n = static_cast< int >( m_idTable.size() ); i < n; i++ )
|
|
{
|
|
const TemplateInfo* pInfo = m_pMetaData->getTemplateInfo( m_idTable[ i ] );
|
|
unsigned short fieldCount = static_cast< unsigned short >( pInfo->count() );
|
|
stream.Write( &fieldCount, sizeof(fieldCount) );
|
|
}
|
|
|
|
if ( skip > 0 )
|
|
stream.Write( g_filler, skip );
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Imp::loadIdTable( KStream& stream, std::vector< unsigned short >& idMergeTable )
|
|
{
|
|
long size;
|
|
StreamRead( stream, &size, sizeof(size) );
|
|
|
|
unsigned int posEnd = static_cast< unsigned int >( stream.Tell() ) + size;
|
|
|
|
size /= sizeof(GUID) + sizeof(unsigned short);
|
|
idMergeTable.reserve( size );
|
|
|
|
int i;
|
|
for ( i = 0; i < size; i++ )
|
|
{
|
|
GUID id;
|
|
StreamRead( stream, &id, sizeof(id) );
|
|
|
|
idMergeTable.push_back( static_cast< unsigned short >( addId( &id ) ) );
|
|
}
|
|
|
|
for ( i = 0; i < size; i++ )
|
|
{
|
|
unsigned short fieldCount;
|
|
StreamRead( stream, &fieldCount, sizeof(fieldCount) );
|
|
|
|
const GUID* pId = getId( idMergeTable[ i ] );
|
|
const TemplateInfo* pInfo = m_pMetaData->getTemplateInfo( *pId );
|
|
ATInfo& fl = m_fieldLimits[ pInfo->getMetaDataIndex() ];
|
|
|
|
assert( fl.pInfo == pInfo );
|
|
|
|
fl.fieldLimit = fieldCount;
|
|
fl.fixedSize = pInfo->calcFixedSize( fieldCount );
|
|
}
|
|
|
|
stream.Seek( posEnd, KStream::seekSet );
|
|
return true;
|
|
}
|
|
|
|
template< typename T > struct FnNoIndex {
|
|
bool operator() ( const T& a ) {
|
|
return a.index == -1;
|
|
}
|
|
};
|
|
|
|
template< typename T > struct FnLessIndex {
|
|
bool operator() ( const T& a, const T& b ) {
|
|
return a.index < b.index;
|
|
}
|
|
};
|
|
|
|
template< typename T > struct FnResetIndex {
|
|
void operator() ( T& a ) {
|
|
a.index = -1;
|
|
}
|
|
};
|
|
|
|
void Imp::applyStringIndex()
|
|
{
|
|
m_stringTable.erase( std::remove_if( m_stringTable.begin(), m_stringTable.end(), FnNoIndex< StrRec >() ), m_stringTable.end() );
|
|
std::sort( m_stringTable.begin(), m_stringTable.end(), FnLessIndex< StrRec >() );
|
|
std::for_each( m_stringTable.begin(), m_stringTable.end(), FnResetIndex< StrRec >() );
|
|
|
|
m_wstringTable.erase( std::remove_if( m_wstringTable.begin(), m_wstringTable.end(), FnNoIndex< WStrRec >() ), m_wstringTable.end() );
|
|
std::sort( m_wstringTable.begin(), m_wstringTable.end(), FnLessIndex< WStrRec >() );
|
|
std::for_each( m_wstringTable.begin(), m_wstringTable.end(), FnResetIndex< WStrRec >() );
|
|
}
|
|
|
|
bool Imp::saveStringTable( KStream& stream )
|
|
{
|
|
unsigned int pos = static_cast< unsigned int >( stream.Tell() );
|
|
stream.Seek( sizeof(unsigned int), KStream::seekCur );
|
|
|
|
int i;
|
|
long n;
|
|
unsigned int total = 0;
|
|
|
|
// write string table
|
|
|
|
n = int( m_stringTable.size() );
|
|
total += static_cast< unsigned int >( stream.Write( &n, sizeof(n) ) );
|
|
|
|
for ( i = 0; i < n; i++ )
|
|
total += static_cast< unsigned int >( stream.Write( m_stringTable[ i ].str.c_str(), m_stringTable[ i ].str.size()+1 ) );
|
|
|
|
// write wstring table
|
|
|
|
n = int( m_wstringTable.size() );
|
|
total += static_cast< unsigned int >( stream.Write( &n, sizeof(n) ) );
|
|
|
|
for ( i = 0; i < n; i++ )
|
|
total += static_cast< unsigned int >( stream.Write( m_wstringTable[ i ].str.c_str(), (m_wstringTable[ i ].str.size()+1) * sizeof(wchar_t) ) );
|
|
|
|
// write block size
|
|
|
|
long skip = toAlign4( total );
|
|
total += skip;
|
|
|
|
if ( skip > 0 )
|
|
stream.Write( g_filler, skip );
|
|
|
|
stream.Seek( pos, KStream::seekSet );
|
|
stream.Write( &total, sizeof(total) );
|
|
stream.Seek( total, KStream::seekCur );
|
|
|
|
return true;
|
|
}
|
|
|
|
template< typename CHAR >
|
|
inline static int strnlen_( const CHAR* pStr, int limit )
|
|
{
|
|
if ( pStr == NULL )
|
|
return 0;
|
|
|
|
for ( int i = 0; i < limit; i++, pStr++ )
|
|
{
|
|
if ( *pStr == 0 )
|
|
return i;
|
|
}
|
|
return limit;
|
|
}
|
|
|
|
bool Imp::loadStringTable( KStream& stream, std::vector< long >& strMergeTable, std::vector< long >& wstrMergeTable )
|
|
{
|
|
unsigned int size;
|
|
if ( !StreamRead( stream, &size, sizeof(size) ) )
|
|
return false;
|
|
|
|
unsigned int posEnd = static_cast< unsigned int >( stream.Tell() ) + size;
|
|
|
|
// read all block to memory
|
|
|
|
std::vector< char > buf( size );
|
|
if ( !StreamRead( stream, &buf.front(), size ) )
|
|
return false;
|
|
|
|
int i;
|
|
long n;
|
|
char* p = &buf.front();
|
|
|
|
// decode string table
|
|
|
|
n = *reinterpret_cast< long* >( p );
|
|
p += sizeof( n );
|
|
size -= sizeof( n );
|
|
|
|
if ( n < 0 )
|
|
return false;
|
|
|
|
strMergeTable.reserve( n );
|
|
for ( i = 0; i < n; i++ )
|
|
{
|
|
int len = strnlen_( p, size );
|
|
strMergeTable.push_back( addString( p ) );
|
|
|
|
p += len+1;
|
|
size -= len+1;
|
|
}
|
|
|
|
// decode wstring table
|
|
|
|
n = *reinterpret_cast< long* >( p );
|
|
p += sizeof( n );
|
|
size -= sizeof( n );
|
|
|
|
if ( n < 0 )
|
|
return false;
|
|
|
|
wstrMergeTable.reserve( n );
|
|
for ( i = 0; i < n; i++ )
|
|
{
|
|
int len = strnlen_( reinterpret_cast< wchar_t* >( p ), size / sizeof(wchar_t) );
|
|
wstrMergeTable.push_back( addWString( reinterpret_cast< wchar_t* >( p ) ) );
|
|
|
|
p += (len+1) * sizeof(wchar_t);
|
|
size -= (len+1) * sizeof(wchar_t);
|
|
}
|
|
|
|
stream.Seek( posEnd, KStream::seekSet );
|
|
return size < 4;
|
|
}
|
|
|
|
// =================================================================================================
|
|
|
|
Filer::Filer( MetaData* pMetaData )
|
|
{
|
|
m_pImp = new Imp;
|
|
m_pImp->m_pMetaData = pMetaData;
|
|
m_pImp->m_pRoot = NULL;
|
|
m_pImp->m_bUnpackedDict = false;
|
|
m_pImp->m_pTemplateInitCallback = NULL;
|
|
|
|
if ( m_pImp->m_pMetaData )
|
|
{
|
|
m_pImp->m_pMetaData->addRef();
|
|
m_pImp->initATIs();
|
|
}
|
|
}
|
|
|
|
Filer::~Filer()
|
|
{
|
|
clear();
|
|
|
|
if ( m_pImp->m_pMetaData )
|
|
m_pImp->m_pMetaData->subRef();
|
|
|
|
delete m_pImp;
|
|
}
|
|
|
|
void Filer::clear()
|
|
{
|
|
delete m_pImp->m_pRoot;
|
|
m_pImp->m_pRoot = NULL;
|
|
}
|
|
|
|
void Filer::setTemplateInitCallback( TInitTemplateFn fn )
|
|
{
|
|
m_pImp->m_pTemplateInitCallback = fn;
|
|
}
|
|
|
|
const static FileHeader g_fileHeader =
|
|
{
|
|
{ 'B','T','R','F' },
|
|
sizeof( HeaderInfo )
|
|
};
|
|
|
|
const static HeaderInfo g_hdr =
|
|
{
|
|
1,
|
|
1,
|
|
};
|
|
|
|
bool Filer::save( KStream& stream )
|
|
{
|
|
// header
|
|
|
|
stream.Write( &g_fileHeader, sizeof(g_fileHeader) );
|
|
stream.Write( &g_hdr, sizeof(g_hdr) );
|
|
|
|
// id (GUID) & string table
|
|
|
|
root();
|
|
m_pImp->m_idTable.clear();
|
|
|
|
int newStrIndex = 1, newWStrIndex = 1;
|
|
m_pImp->m_pRoot->makeTables( newStrIndex, newWStrIndex );
|
|
m_pImp->applyStringIndex();
|
|
|
|
if ( !m_pImp->saveIdTable( stream ) )
|
|
return false;
|
|
|
|
if ( !m_pImp->saveStringTable( stream ) )
|
|
return false;
|
|
|
|
// data
|
|
|
|
if ( !m_pImp->m_pRoot->save( stream ) )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Filer::load( KStream& stream )
|
|
{
|
|
// header
|
|
{
|
|
FileHeader header;
|
|
if ( !StreamRead( stream, &header, sizeof(header) ) || strncmp( header.idstr, g_fileHeader.idstr, 4 ) || header.headerInfoSize < 0 )
|
|
return false;
|
|
|
|
HeaderInfo headerInfo;
|
|
if ( header.headerInfoSize >= sizeof(headerInfo) )
|
|
{
|
|
if ( !StreamRead( stream, &headerInfo, sizeof(headerInfo) ) )
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if ( !StreamRead( stream, &headerInfo, header.headerInfoSize ) )
|
|
return false;
|
|
stream.Seek( sizeof(headerInfo) - header.headerInfoSize, KStream::seekCur );
|
|
}
|
|
|
|
m_pImp->m_bUnpackedDict = headerInfo.majorVersion * 256 + headerInfo.minorVersion == 256 + 0; // ver 1.1 이상은 packed dict format
|
|
}
|
|
|
|
MergeTable mergeTable;
|
|
|
|
// id (GUID) & string table
|
|
|
|
if ( !m_pImp->loadIdTable( stream, mergeTable.id ) )
|
|
return false;
|
|
|
|
if ( !m_pImp->loadStringTable( stream, mergeTable.str, mergeTable.wstr ) )
|
|
return false;
|
|
|
|
// data
|
|
|
|
root();
|
|
|
|
if ( !m_pImp->m_pRoot->load( stream, mergeTable ) )
|
|
return false;
|
|
|
|
m_pImp->initATIs();
|
|
|
|
return true;
|
|
}
|
|
|
|
MetaData* Filer::metaData()
|
|
{
|
|
return m_pImp->m_pMetaData;
|
|
}
|
|
|
|
IDict* Filer::root()
|
|
{
|
|
if ( m_pImp->m_pRoot == NULL )
|
|
m_pImp->m_pRoot = new FilerDict( m_pImp );
|
|
|
|
return m_pImp->m_pRoot;
|
|
}
|
|
|
|
} // namespace trf
|