#include "../../include/kfile/trffiler.h" #include #include #include 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