2023-02-25 17:39:39 +08:00
#include "CKFile.hpp"
2023-02-26 21:48:03 +08:00
#include "CKStateChunk.hpp"
2023-08-26 16:37:26 +08:00
#include "ObjImpls/CKObject.hpp"
2023-09-06 10:42:23 +08:00
#include "MgrImpls/CKPathManager.hpp"
2023-08-25 17:35:45 +08:00
#include "../VxMath/VxMemoryMappedFile.hpp"
#include "CKContext.hpp"
2023-02-25 17:39:39 +08:00
#include <memory>
2023-02-26 21:48:03 +08:00
namespace LibCmo :: CK2 {
2023-02-25 17:39:39 +08:00
/*
* NOTE:
* We onlt support read Virtools file with FileVersion >= 7.
* The file with FileVersion < 7 is older than NeMo 1.0 (CK 1.1).
* No need to support them.
*/
2023-08-25 21:57:22 +08:00
CKERROR CKFileReader :: ShallowLoad ( CKSTRING u8_filename ) {
// check document status
2023-09-26 12:07:13 +08:00
if ( this -> m_Done ) return CKERROR :: CKERR_CANCELLED ;
2024-08-23 17:38:45 +08:00
// check CKContext encoding sequence
if ( ! this -> m_Ctx -> IsValidEncoding ()) return CKERROR :: CKERR_CANCELLED ;
2023-02-25 22:58:28 +08:00
// check file and open memory
if ( u8_filename == nullptr ) return CKERROR :: CKERR_INVALIDPARAMETER ;
2023-08-25 21:57:22 +08:00
std :: unique_ptr < VxMath :: VxMemoryMappedFile > mappedFile ( new VxMath :: VxMemoryMappedFile ( u8_filename ));
2023-02-25 22:58:28 +08:00
if ( ! mappedFile -> IsValid ()) {
2024-08-23 11:28:49 +08:00
this -> m_Ctx -> OutputToConsoleEx ( u8 "Fail to create Memory File for \" %s \" ." , u8_filename );
2023-02-25 17:39:39 +08:00
return CKERROR :: CKERR_INVALIDFILE ;
}
// create buffer and start loading
2023-08-25 21:57:22 +08:00
std :: unique_ptr < CKBufferParser > parser ( new CKBufferParser ( mappedFile -> GetBase (), mappedFile -> GetFileSize (), false ));
CKERROR err = this -> ReadFileHeader ( parser . get ());
2023-02-25 17:39:39 +08:00
if ( err != CKERROR :: CKERR_OK ) return err ;
2023-08-25 21:57:22 +08:00
err = this -> ReadFileData ( parser . get ());
2023-02-25 17:39:39 +08:00
if ( err != CKERROR :: CKERR_OK ) return err ;
2023-02-25 22:58:28 +08:00
// other data will be free automatically
2024-08-23 17:38:45 +08:00
// set done flag and return
this -> m_Done = true ;
2023-02-25 17:39:39 +08:00
return CKERROR :: CKERR_OK ;
}
2023-08-25 21:57:22 +08:00
CKERROR CKFileReader :: ReadFileHeader ( CKBufferParser * ParserPtr ) {
std :: unique_ptr < CKBufferParser > parser ( new CKBufferParser ( ParserPtr -> GetBase (), ParserPtr -> GetSize (), false ));
2023-02-25 22:58:28 +08:00
parser -> SetCursor ( ParserPtr -> GetCursor ());
2024-08-23 11:28:49 +08:00
std :: string name_conv ;
2023-02-25 17:39:39 +08:00
// ========== read header ==========
// check header size
2023-08-29 14:00:34 +08:00
if ( parser -> GetSize () < CKSizeof ( CKRawFileInfo )) return CKERROR :: CKERR_INVALIDFILE ;
2023-08-28 17:04:28 +08:00
if ( std :: memcmp ( parser -> GetPtr (), CKNEMOFI , sizeof ( CKRawFileInfo :: NeMo ))) return CKERROR :: CKERR_INVALIDFILE ;
2023-02-25 17:39:39 +08:00
// read header
CKRawFileInfo rawHeader ;
2023-09-17 10:38:46 +08:00
parser -> Read ( & rawHeader );
2023-02-25 17:39:39 +08:00
// ========== header checker ==========
// check zero flag?
if ( rawHeader . Zero ) return CKERROR :: CKERR_INVALIDFILE ;
// check file version
if ( rawHeader . FileVersion > 9 || rawHeader . FileVersion < 7 ) return CKERROR :: CKERR_OBSOLETEVIRTOOLS ;
// force reset too big product ver?
if ( rawHeader . ProductVersion >= 12u ) {
rawHeader . ProductVersion = 0u ;
2023-02-28 14:04:38 +08:00
rawHeader . ProductBuild = 0x01010000u ;
2023-02-25 17:39:39 +08:00
}
// ========== assign value ==========
2023-08-25 21:57:22 +08:00
this -> m_FileInfo . ProductVersion = rawHeader . ProductVersion ;
this -> m_FileInfo . ProductBuild = rawHeader . ProductBuild ;
this -> m_FileInfo . FileWriteMode = static_cast < CK_FILE_WRITEMODE > ( rawHeader . FileWriteMode );
this -> m_FileInfo . CKVersion = rawHeader . CKVersion ;
this -> m_FileInfo . FileVersion = rawHeader . FileVersion ;
2023-08-30 10:03:02 +08:00
this -> m_FileInfo . FileSize = parser -> GetSize ();
2023-08-25 21:57:22 +08:00
this -> m_FileInfo . ManagerCount = rawHeader . ManagerCount ;
this -> m_FileInfo . ObjectCount = rawHeader . ObjectCount ;
2023-09-30 14:24:37 +08:00
this -> m_FileInfo . MaxIDSaved = static_cast < CK_ID > ( rawHeader . MaxIDSaved );
2023-08-25 21:57:22 +08:00
this -> m_FileInfo . Hdr1PackSize = rawHeader . FileVersion >= 8 ? rawHeader . Hdr1PackSize : 0u ;
this -> m_FileInfo . Hdr1UnPackSize = rawHeader . FileVersion >= 8 ? rawHeader . Hdr1UnPackSize : 0u ;
this -> m_FileInfo . DataPackSize = rawHeader . DataPackSize ;
this -> m_FileInfo . DataUnPackSize = rawHeader . DataUnPackSize ;
this -> m_FileInfo . Crc = rawHeader . Crc ;
2023-02-25 17:39:39 +08:00
// ========== crc and body unpacker ==========
2023-08-25 21:57:22 +08:00
if ( this -> m_FileInfo . FileVersion >= 8 ) {
2023-02-25 17:39:39 +08:00
// crc checker for file ver >= 8
// reset crc field of header
rawHeader . Crc = 0u ;
2024-12-28 16:29:35 +08:00
// Compute and check CRC in theory (< Virtools 4.0)
2023-08-29 14:00:34 +08:00
CKDWORD gotten_crc = CKComputeDataCRC ( & rawHeader , CKSizeof ( CKRawFileInfo ), 0u );
2024-12-28 16:29:35 +08:00
parser -> SetCursor ( CKSizeof ( CKRawFileInfo ));
2023-08-25 21:57:22 +08:00
gotten_crc = CKComputeDataCRC ( parser -> GetPtr (), this -> m_FileInfo . Hdr1PackSize , gotten_crc );
parser -> MoveCursor ( this -> m_FileInfo . Hdr1PackSize );
gotten_crc = CKComputeDataCRC ( parser -> GetPtr (), this -> m_FileInfo . DataPackSize , gotten_crc );
2023-02-25 17:39:39 +08:00
2023-08-25 21:57:22 +08:00
if ( gotten_crc != this -> m_FileInfo . Crc ) {
2024-12-28 16:29:35 +08:00
// MARK:
// If the CRC check failed, there is another way to compute CRC. (>= Virtools 4.0)
// This is a patch for Dassault stupid programmer.
//
// After Virtools 4.0, Dassault use a new way to compute the CRC of file.
2024-12-31 17:43:39 +08:00
// Dassault introduces a new class called CKMemoryBufferWriter which use file and memory map to handle big file properly.
// This algorithm splits the whole data body into 8 MB chunks and calculate them one by one to avoid instantaneous memory occupation.
2024-12-28 16:29:35 +08:00
// However, there is a bug in virtual function CKMemoryBufferWriter::ComputeCRC.
2024-12-31 17:43:39 +08:00
// It takes `PreviousCRC` as argument but never use it in function.
// In this function, the start value of CRC compution is hardcoded 0.
2024-12-28 16:29:35 +08:00
// So, although Dassault programmer try to compute CRC for file header, header part and daat part in code, it actually only compute CRC for data part!
2024-12-31 17:43:39 +08:00
// I 100% sure this is the mistake of Dassault stupid programmer and this bug cause more horrible result.
2024-12-28 16:29:35 +08:00
//
// In Virtools 2.1, engine will check CRC of file first. If no matched CRC, engine will reject loading file.
// So the obvious result is that we can not load file saved by Virtools 4.0 in Virtools 2.1.
// But this is not the point which makes me indignant.
2024-12-31 17:43:39 +08:00
// The real weird point is that we can use Virtools 3.5 to open file saved by Virtools 4.0, but why?
// After some researches, I found that the programmer of Dassault totally removed CRC check when loading file, since some version which I don't know, to suppress this bug!
2024-12-28 16:29:35 +08:00
// This is totally cheat and commercial-oriented behavior!
2024-12-31 17:43:39 +08:00
// I guess Dassault programmer also found that they can not load new created file in old Virtools.
// But they didn't find out what cause this bug, and just directly remove the whole of CRC checker to resolve this bug!
// I can't believe that this thing happens on such official software.
// This is the point which makes me indignant.
2024-12-28 16:29:35 +08:00
gotten_crc = CKComputeDataCRC ( parser -> GetPtr (), this -> m_FileInfo . DataPackSize , 0u );
2024-12-31 17:43:39 +08:00
// Both CRC compute methods are failed. This file may be really broken.
2024-12-28 16:29:35 +08:00
// Report exception directly.
if ( gotten_crc != this -> m_FileInfo . Crc ) {
this -> m_Ctx -> OutputToConsole ( u8 "Virtools file CRC error." );
return CKERROR :: CKERR_FILECRCERROR ;
}
2023-02-25 22:58:28 +08:00
}
2023-02-25 17:39:39 +08:00
// reset cursor
2024-12-28 16:29:35 +08:00
parser -> SetCursor ( CKSizeof ( CKRawFileInfo ));
2023-02-25 17:39:39 +08:00
// compare size to decide wheher use compress feature
2023-08-28 21:21:40 +08:00
if ( this -> m_FileInfo . Hdr1PackSize != this -> m_FileInfo . Hdr1UnPackSize ) {
void * decomp_buffer = CKUnPackData ( this -> m_FileInfo . Hdr1UnPackSize , parser -> GetPtr (), this -> m_FileInfo . Hdr1PackSize );
if ( decomp_buffer != nullptr ) {
parser = std :: unique_ptr < CKBufferParser > ( new CKBufferParser ( decomp_buffer , this -> m_FileInfo . Hdr1UnPackSize , true ));
}
2023-02-25 17:39:39 +08:00
}
}
// ========== object list read ==========
// file ver >= 7 have this features
2023-02-25 22:58:28 +08:00
{
2023-02-25 17:39:39 +08:00
// apply max id saved
2023-08-25 21:57:22 +08:00
this -> m_SaveIDMax = this -> m_FileInfo . MaxIDSaved ;
2023-02-25 17:39:39 +08:00
// resize
2023-08-25 21:57:22 +08:00
this -> m_FileObjects . resize ( this -> m_FileInfo . ObjectCount );
2023-02-25 17:39:39 +08:00
// read data
2023-08-25 21:57:22 +08:00
for ( auto & fileobj : this -> m_FileObjects ) {
2023-02-25 17:39:39 +08:00
// read basic fields
2023-09-17 10:38:46 +08:00
parser -> Read ( & fileobj . ObjectId );
parser -> Read ( & fileobj . ObjectCid );
parser -> Read ( & fileobj . FileIndex );
2023-02-25 17:39:39 +08:00
CKDWORD namelen ;
2023-09-17 10:38:46 +08:00
parser -> Read ( & namelen );
2023-02-25 17:39:39 +08:00
if ( namelen != 0 ) {
2023-02-25 22:58:28 +08:00
name_conv . resize ( namelen );
parser -> Read ( name_conv . data (), namelen );
2024-08-27 11:25:53 +08:00
if ( ! m_Ctx -> GetUTF8String ( name_conv , fileobj . Name ))
m_Ctx -> OutputToConsole ( u8 "Fail to get UTF8 name for CKObject when reading file header. Some objects name will leave to blank." );
2023-09-16 18:31:25 +08:00
} else {
2023-11-30 22:48:40 +08:00
XContainer :: NSXString :: FromCKSTRING ( fileobj . Name , nullptr );
2023-02-25 17:39:39 +08:00
}
}
}
// ========== dep list read ==========
// file ver >= 8 have this feature
2023-08-25 21:57:22 +08:00
if ( this -> m_FileInfo . FileVersion >= 8 ) {
2023-02-25 17:39:39 +08:00
// get size and resize
CKDWORD depSize ;
2023-09-17 10:38:46 +08:00
parser -> Read ( & depSize );
2023-08-25 21:57:22 +08:00
this -> m_PluginsDep . resize ( depSize );
2023-02-25 17:39:39 +08:00
CKDWORD guid_size ;
2023-08-25 21:57:22 +08:00
for ( auto & dep : this -> m_PluginsDep ) {
2023-02-25 17:39:39 +08:00
// read category
2023-09-17 10:38:46 +08:00
parser -> Read ( & dep . m_PluginCategory );
2023-02-25 17:39:39 +08:00
// get size and resize
2023-09-17 10:38:46 +08:00
parser -> Read ( & guid_size );
2023-02-25 17:39:39 +08:00
dep . m_Guids . resize ( guid_size );
// read data
if ( guid_size != 0 ) {
parser -> Read ( dep . m_Guids . data (), sizeof ( CKGUID ) * guid_size );
}
}
}
// ========== included file list read ==========
// file ver >= 8 have this feature
2023-08-25 21:57:22 +08:00
if ( this -> m_FileInfo . FileVersion >= 8 ) {
2023-02-25 17:39:39 +08:00
// MARK: i don't knwo what is this!
2023-09-17 10:38:46 +08:00
CKINT hasIncludedFile ;
parser -> Read ( & hasIncludedFile );
2023-02-25 17:39:39 +08:00
if ( hasIncludedFile > 0 ) {
// read included file size and resize
CKDWORD includedFileCount ;
2023-09-17 10:38:46 +08:00
parser -> Read ( & includedFileCount );
2023-08-25 21:57:22 +08:00
this -> m_IncludedFiles . resize ( includedFileCount );
2023-02-25 17:39:39 +08:00
2023-09-17 10:38:46 +08:00
hasIncludedFile -= static_cast < CKINT > ( sizeof ( CKDWORD ));
2023-02-25 17:39:39 +08:00
}
2023-02-25 22:58:28 +08:00
// MARK: backward pos
// backward with 0?
parser -> MoveCursor ( hasIncludedFile );
}
// ========== sync main parser ==========
2023-08-25 21:57:22 +08:00
if ( this -> m_FileInfo . FileVersion >= 8 ) {
2023-02-25 22:58:28 +08:00
// file ver >= 8, use header offset
// because it have compress feature
2024-12-28 16:29:35 +08:00
ParserPtr -> SetCursor ( this -> m_FileInfo . Hdr1PackSize + CKSizeof ( CKRawFileInfo ));
2023-02-25 22:58:28 +08:00
} else {
// otherwise, sync with current parser.
ParserPtr -> SetCursor ( parser -> GetCursor ());
2023-02-25 17:39:39 +08:00
}
return CKERROR :: CKERR_OK ;
}
2023-08-25 21:57:22 +08:00
CKERROR CKFileReader :: ReadFileData ( CKBufferParser * ParserPtr ) {
2023-08-26 16:37:26 +08:00
std :: unique_ptr < CKBufferParser > parser ( new CKBufferParser ( ParserPtr -> GetBase (), ParserPtr -> GetSize (), false ));
2023-02-25 22:58:28 +08:00
parser -> SetCursor ( ParserPtr -> GetCursor ());
2024-08-23 11:28:49 +08:00
std :: string name_conv ;
2023-02-25 22:58:28 +08:00
// ========== compress feature process ==========
2024-11-03 19:05:27 +08:00
if ( YYCC :: EnumHelper :: Has ( this -> m_FileInfo . FileWriteMode , CK_FILE_WRITEMODE :: CKFILE_CHUNKCOMPRESSED_OLD ) ||
YYCC :: EnumHelper :: Has ( this -> m_FileInfo . FileWriteMode , CK_FILE_WRITEMODE :: CKFILE_WHOLECOMPRESSED )) {
2023-02-25 22:58:28 +08:00
2023-08-25 21:57:22 +08:00
void * decomp_buffer = CKUnPackData ( this -> m_FileInfo . DataUnPackSize , parser -> GetPtr (), this -> m_FileInfo . DataPackSize );
2023-02-25 22:58:28 +08:00
if ( decomp_buffer != nullptr ) {
2023-08-25 21:57:22 +08:00
parser = std :: unique_ptr < CKBufferParser > ( new CKBufferParser ( decomp_buffer , this -> m_FileInfo . DataUnPackSize , true ));
2023-02-25 22:58:28 +08:00
}
}
// ========== old file crc and obj list read ==========
// only file ver < 8 run this
2023-08-25 21:57:22 +08:00
if ( this -> m_FileInfo . FileVersion < 8 ) {
2023-02-25 22:58:28 +08:00
// check crc
CKDWORD gotten_crc = CKComputeDataCRC (
parser -> GetPtr (),
parser -> GetSize () - parser -> GetCursor (),
0u
);
2023-08-25 21:57:22 +08:00
if ( gotten_crc != this -> m_FileInfo . Crc ) {
2024-08-23 11:28:49 +08:00
this -> m_Ctx -> OutputToConsole ( u8 "Virtools file CRC error." );
2023-02-25 22:58:28 +08:00
return CKERROR :: CKERR_FILECRCERROR ;
}
// MARK: why read again? especially for file ver == 7.
// get save id max
2023-09-17 10:38:46 +08:00
parser -> Read ( & this -> m_SaveIDMax );
2023-02-25 22:58:28 +08:00
// get object count and resize
2023-09-17 10:38:46 +08:00
parser -> Read ( & this -> m_FileInfo . ObjectCount );
2023-08-25 21:57:22 +08:00
if ( this -> m_FileObjects . empty ()) {
this -> m_FileObjects . resize ( this -> m_FileInfo . ObjectCount );
2023-02-25 22:58:28 +08:00
}
}
// ========== manager read ==========
// only file ver >= 6 have this
2023-08-25 21:57:22 +08:00
if ( this -> m_FileInfo . ManagerCount != 0 ) {
this -> m_ManagersData . resize ( this -> m_FileInfo . ManagerCount );
2023-02-25 22:58:28 +08:00
CKDWORD stateChunkLen = 0u ;
bool stateChkParseSuccess = false ;
2023-08-25 21:57:22 +08:00
for ( auto & mgr : this -> m_ManagersData ) {
2023-02-25 22:58:28 +08:00
// read guid
2023-09-17 10:38:46 +08:00
parser -> Read ( & mgr . Manager );
2023-02-25 22:58:28 +08:00
// read statechunk len
2023-09-17 10:38:46 +08:00
parser -> Read ( & stateChunkLen );
2023-02-25 22:58:28 +08:00
// check len
if ( stateChunkLen == 0 ) {
mgr . Data = nullptr ;
continue ;
}
// read statechunk
2023-08-25 21:57:22 +08:00
mgr . Data = new CKStateChunk ( & this -> m_Visitor , this -> m_Ctx );
stateChkParseSuccess = mgr . Data -> ConvertFromBuffer ( parser -> GetPtr ());
if ( ! stateChkParseSuccess ) {
delete mgr . Data ;
mgr . Data = nullptr ;
2023-02-25 22:58:28 +08:00
}
parser -> MoveCursor ( stateChunkLen );
}
}
// ========== object read ==========
// only works file version >= 4. < 4 section has been removed.
2023-08-25 21:57:22 +08:00
if ( this -> m_FileInfo . ObjectCount != 0 ) {
2023-02-25 22:58:28 +08:00
// new file reader section
bool stateChkParseSuccess = false ;
2023-08-25 21:57:22 +08:00
for ( auto & obj : this -> m_FileObjects ) {
2023-02-25 22:58:28 +08:00
// get statechunk len
2023-09-17 10:38:46 +08:00
parser -> Read ( & obj . PackSize );
2023-02-25 22:58:28 +08:00
// check state chunk len
2023-08-28 17:04:28 +08:00
if ( obj . PackSize == 0 ) {
2023-02-25 22:58:28 +08:00
obj . Data = nullptr ;
continue ;
}
// read state chunk
2023-08-25 21:57:22 +08:00
obj . Data = new CKStateChunk ( & this -> m_Visitor , this -> m_Ctx );
stateChkParseSuccess = obj . Data -> ConvertFromBuffer ( parser -> GetPtr ());
if ( ! stateChkParseSuccess ) {
delete obj . Data ;
obj . Data = nullptr ;
2023-02-25 22:58:28 +08:00
}
2023-08-28 17:04:28 +08:00
parser -> MoveCursor ( obj . PackSize );
2023-02-25 22:58:28 +08:00
}
}
// ========== included file get ==========
// before reading, we need switch back to original parser.
// and skip data chunk size
2023-08-26 16:37:26 +08:00
parser = std :: unique_ptr < CKBufferParser > ( new CKBufferParser ( ParserPtr -> GetBase (), ParserPtr -> GetSize (), false ));
2023-08-30 10:03:02 +08:00
parser -> SetCursor ( ParserPtr -> GetCursor ());
2023-08-25 21:57:22 +08:00
parser -> MoveCursor ( this -> m_FileInfo . DataPackSize );
2023-02-25 22:58:28 +08:00
// then we can read it.
2023-08-25 21:57:22 +08:00
if ( this -> m_IncludedFiles . size () != 0 ) {
for ( auto & file : this -> m_IncludedFiles ) {
2023-02-25 22:58:28 +08:00
// get file name length and resize it
CKDWORD filenamelen = 0u ;
2023-09-17 10:38:46 +08:00
parser -> Read ( & filenamelen );
2023-02-25 22:58:28 +08:00
name_conv . resize ( filenamelen );
// read filename
if ( filenamelen != 0 ) {
parser -> Read ( name_conv . data (), filenamelen );
2024-08-27 11:25:53 +08:00
if ( ! m_Ctx -> GetUTF8String ( name_conv , file ))
m_Ctx -> OutputToConsole ( u8 "Fail to get UTF8 name for included file when reading file body. Some included files may be stripped." );
2023-02-25 22:58:28 +08:00
}
// read file body length
CKDWORD filebodylen = 0u ;
2023-09-17 10:38:46 +08:00
parser -> Read ( & filebodylen );
2023-02-25 22:58:28 +08:00
// read file body
2023-09-16 18:31:25 +08:00
XContainer :: XString tempfilename = m_Ctx -> GetPathManager () -> GetTempFilePath ( file . c_str ());
2024-08-23 11:28:49 +08:00
FILE * fp = YYCC :: IOHelper :: UTF8FOpen ( tempfilename . c_str (), u8 "wb" );
2023-02-25 22:58:28 +08:00
if ( fp != nullptr ) {
2023-09-17 10:38:46 +08:00
std :: fwrite ( parser -> GetPtr (), sizeof ( CKBYTE ), filebodylen , fp );
2023-08-29 14:00:34 +08:00
std :: fclose ( fp );
2023-08-30 10:03:02 +08:00
} else {
2024-08-23 11:28:49 +08:00
m_Ctx -> OutputToConsoleEx ( u8 "Fail to open temp file: %s" , tempfilename . c_str ());
2023-02-25 22:58:28 +08:00
}
// move to next
parser -> MoveCursor ( filebodylen );
}
}
return CKERROR :: CKERR_OK ;
2023-02-25 17:39:39 +08:00
}
2023-08-25 21:57:22 +08:00
CKERROR CKFileReader :: DeepLoad ( CKSTRING u8_filename ) {
2023-08-28 17:04:28 +08:00
// check document status
2023-09-26 12:07:13 +08:00
if ( this -> m_Done ) return CKERROR :: CKERR_CANCELLED ;
2024-08-23 17:38:45 +08:00
// check CKContext encoding sequence
if ( ! this -> m_Ctx -> IsValidEncoding ()) return CKERROR :: CKERR_CANCELLED ;
2023-08-28 17:04:28 +08:00
2023-02-28 11:35:54 +08:00
// ========== prepare work ==========
2023-03-03 11:06:26 +08:00
CKERROR err = CKERROR :: CKERR_OK ;
2023-02-28 11:35:54 +08:00
// get shallow document first
2023-08-25 21:57:22 +08:00
err = this -> ShallowLoad ( u8_filename );
2023-02-28 11:35:54 +08:00
if ( err != CKERROR :: CKERR_OK ) return err ;
2024-08-23 17:38:45 +08:00
// reset done flag because we need further processing
this -> m_Done = false ;
2023-02-28 11:35:54 +08:00
// ========== create object first ==========
2023-08-25 21:57:22 +08:00
for ( auto & obj : this -> m_FileObjects ) {
2023-02-28 11:35:54 +08:00
// todo: skip CK_LEVEL
// todo: resolve references
if ( obj . Data == nullptr ) continue ;
2023-03-08 15:45:06 +08:00
// create object and assign created obj ckid
2023-09-16 18:31:25 +08:00
obj . ObjPtr = m_Ctx -> CreateObject ( obj . ObjectCid , XContainer :: NSXString :: ToCKSTRING ( obj . Name ));
2023-03-08 15:45:06 +08:00
if ( obj . ObjPtr == nullptr ) {
2023-08-25 21:57:22 +08:00
obj . CreatedObjectId = 0u ;
2023-03-08 15:45:06 +08:00
} else {
2023-08-25 21:57:22 +08:00
obj . CreatedObjectId = obj . ObjPtr -> GetID ();
2023-03-08 15:45:06 +08:00
}
2023-02-28 11:35:54 +08:00
}
// ========== CKStateChunk remap ==========
// todo: remap
// todo: CK_LEVEL special proc
// ========== consume Managers ==========
// todo...
// ========== analyze objects CKStateChunk ==========
2023-08-25 21:57:22 +08:00
for ( auto & obj : this -> m_FileObjects ) {
2023-03-03 11:06:26 +08:00
if ( obj . Data == nullptr || obj . ObjPtr == nullptr ) continue ;
2023-02-28 11:35:54 +08:00
// todo: special treat for CK_LEVEL
// try parsing data
2023-02-28 14:04:38 +08:00
obj . Data -> StartRead ();
2023-08-25 21:57:22 +08:00
bool success = obj . ObjPtr -> Load ( obj . Data , & this -> m_Visitor );
2023-02-28 14:04:38 +08:00
obj . Data -> StopRead ();
2023-03-05 22:31:11 +08:00
if ( success ) {
2023-03-03 11:06:26 +08:00
// if success, clear CKStateChunk*
delete obj . Data ;
obj . Data = nullptr ;
2023-03-05 22:31:11 +08:00
} else {
// if failed, delete it
2023-09-01 13:27:46 +08:00
m_Ctx -> DestroyObject ( obj . ObjectId );
2023-03-05 22:31:11 +08:00
obj . ObjPtr = nullptr ;
2023-08-25 21:57:22 +08:00
obj . CreatedObjectId = 0u ;
2023-02-28 11:35:54 +08:00
}
}
// ========== finalize work ==========
2024-12-28 16:29:35 +08:00
2024-08-23 17:38:45 +08:00
// set done flag and return
this -> m_Done = true ;
2023-02-26 13:57:32 +08:00
return CKERROR :: CKERR_OK ;
}
2023-02-25 17:39:39 +08:00
}