diff --git a/LibCmo/VTReader.cpp b/LibCmo/VTReader.cpp index 9453664..f131558 100644 --- a/LibCmo/VTReader.cpp +++ b/LibCmo/VTReader.cpp @@ -169,7 +169,7 @@ namespace LibCmo { dep.ValidGuids.resize(guid_size); // read data if (guid_size != 0) { - parser->Read(dep.m_Guids.data(), sizeof(CKGUID)* guid_size); + parser->Read(dep.m_Guids.data(), sizeof(CKGUID) * guid_size); } // extra load flag @@ -277,7 +277,140 @@ namespace LibCmo { } // ========== manager read ========== + // only file ver >= 6 have this + if (this->m_FileInfo.FileVersion >= 6 && this->m_FileInfo.ManagerCount != 0) { + this->m_ManagersData.resize(this->m_FileInfo.ManagerCount); + CKDWORD stateChunkLen = 0u; + bool stateChkParseSuccess = false; + for (auto& mgr : this->m_ManagersData) { + // read guid + parser->Read(&(mgr.Manager), sizeof(CKGUID)); + + // read statechunk len + parser->Read(&stateChunkLen, sizeof(CKDWORD)); + // check len + if (stateChunkLen == 0) { + mgr.Data = nullptr; + continue; + } + // read statechunk + mgr.Data = new(std::nothrow) CKStateChunk(); + if (mgr.Data != nullptr) { + stateChkParseSuccess = mgr.Data->ConvertFromBuffer(parser->GetPtr()); + if (!stateChkParseSuccess) { + delete mgr.Data; + mgr.Data = nullptr; + } + } + parser->MoveCursor(stateChunkLen); + } + } + + // ========== object read ========== + if (this->m_FileInfo.ObjectCount != 0) { + if (this->m_FileInfo.FileVersion >= 4) { + // new file reader section + CKDWORD stateChunkLen = 0u; + bool stateChkParseSuccess = false; + for (auto& obj : this->m_FileObject) { + if (this->m_FileInfo.FileVersion < 7) { + // it seems that file ver < 7, ck id was stored first. + parser->Read(&(obj.Object), sizeof(CK_ID)); + } + // get statechunk len + parser->Read(&stateChunkLen, sizeof(CKDWORD)); + + // if only load behavior and current loaded obj is not behavior, give up. + // >= 7 is corresponding with obj header version requirement + // so don't worry about this + if (EnumHelper::FlagEnumHas(this->m_Flags, CK_LOAD_FLAGS::CK_LOAD_ONLYBEHAVIORS) && + this->m_FileInfo.FileVersion >= 7 && + obj.ObjectCid != CK_CLASSID::CKCID_BEHAVIOR) { + // move cursor for next one. + parser->MoveCursor(stateChunkLen); + continue; + } + + // check state chunk len + if (stateChunkLen == 0) { + obj.Data = nullptr; + continue; + } + // read state chunk + obj.Data = new(std::nothrow) CKStateChunk(); + if (obj.Data != nullptr) { + stateChkParseSuccess = obj.Data->ConvertFromBuffer(parser->GetPtr()); + if (!stateChkParseSuccess) { + delete obj.Data; + obj.Data = nullptr; + } else { + // get data size and fill some fields + obj.PostPackSize = obj.Data->GetDataSize(); + obj.PrePackSize = obj.PostPackSize; + } + } + parser->MoveCursor(stateChunkLen); + + } + } else { + // old file read section + for (auto& obj : this->m_FileObject) { + // read CK_ID + parser->Read(&(obj.Object), sizeof(CK_ID)); + // get data len + CKDWORD dataLen = 0u; + parser->Read(&dataLen, sizeof(CKDWORD)); + + // check state chunk len + if (dataLen == 0) { + obj.Data = nullptr; + continue; + } + + // MARK: set dword_2405F6C0 + + // read class id + CK_CLASSID clsid = CK_CLASSID::CKCID_OBJECT; + parser->Read(&clsid, sizeof(CK_CLASSID)); + if (static_cast(clsid) == UINT32_C(0)) { + // invalid cid; + return CKERROR::CKERR_INVALIDFILE; + } + + // read save flags + parser->Read(&obj.SaveFlags, sizeof(CK_FILE_WRITEMODE)); + + // read statechunk len + CKDWORD oldBufLen = 0u; + parser->Read(&oldBufLen, sizeof(CKDWORD)); + + // setup state chunk and order parse it + obj.Data = new(std::nothrow) CKStateChunk(clsid); + if (obj.Data != nullptr) { + if (!obj.Data->ConvertFromOldBuffer(parser->GetPtr(), oldBufLen, dataLen, obj.SaveFlags)) { + delete obj.Data; + obj.Data = nullptr; + } + } + // move to next + parser->MoveCursor(oldBufLen); + } + } + } + + // ========== object name get ========== + // only file ver < 7 need get it from statechunk + if (this->m_FileInfo.FileVersion < 7) { + for (auto& obj : this->m_FileObject) { + if (obj.Data != nullptr) { + // TODO: CK_STATESAVE_NAME + if (obj.Data->SeekIdentifier(1u)) { + obj.Data->ReadString(obj.Name); + } + } + } + } return CKERROR::CKERR_OK; } diff --git a/LibCmo/VTStateChunk.cpp b/LibCmo/VTStateChunk.cpp index 5fa2b92..ac5b094 100644 --- a/LibCmo/VTStateChunk.cpp +++ b/LibCmo/VTStateChunk.cpp @@ -1,3 +1,10 @@ +#include "VTUtils.hpp" +#if defined(LIBCMO_OS_WIN32) +#define ZLIB_WINAPI +#include +#endif + +#include ; #include "VTStruct.hpp" namespace LibCmo { @@ -10,12 +17,46 @@ namespace LibCmo { { ; } - + LibCmo::CKStateChunk::CKStateChunk(CK_CLASSID clsid) : + m_ClassId(clsid), m_DataDwSize(0u), m_pData(nullptr), + m_DataVersion(CK_STATECHUNK_DATAVERSION::CHUNKDATA_CURRENTVERSION), m_ChunkVersion(CK_STATECHUNK_CHUNKVERSION::CHUNK_VERSION4), + m_Parser{ 0u, 0u, 0u }, + m_ObjectList(), m_ChunkList(), m_ManagerList() + { + ; + } CKStateChunk::~CKStateChunk() { if (this->m_pData != nullptr) delete[] this->m_pData; } + bool CKStateChunk::ConvertFromOldBuffer(const void* buf, CKDWORD buf_size, CKDWORD blk_size, CK_FILE_WRITEMODE mode) { + // if not use compress or 2 size is equal, it mean that this buffer is raw. + // assign it directly + if (!EnumHelper::FlagEnumHas(mode, CK_FILE_WRITEMODE::CKFILE_CHUNKCOMPRESSED_OLD) || + buf_size == blk_size) { + + this->m_DataDwSize = buf_size / sizeof(CKDWORD); + this->m_pData = new(std::nothrow) CKDWORD[this->m_DataDwSize]; + if (this->m_pData != nullptr) { + memcpy(this->m_pData, buf, this->m_DataDwSize * sizeof(CKDWORD)); + } + + return true; + + } else { + // otherwise, we need UnPack + // prepare UnPack requirement + this->m_DataDwSize = buf_size; // NOTE: store as BYTE length!!! + this->m_pData = reinterpret_cast(new(std::nothrow) char[buf_size]); + if (this->m_pData == nullptr) return false; + memcpy(this->m_pData, buf, buf_size); + + // call UnPack + return UnPack(blk_size); + } + } + bool CKStateChunk::ConvertFromBuffer(const void* buf) { if (buf == nullptr) return false; @@ -101,7 +142,7 @@ namespace LibCmo { CK_STATECHUNK_CHUNKOPTIONS options = static_cast( reinterpret_cast(buf)[3] ); - + // read normal data this->m_DataDwSize = dwbuf[1]; bufpos = 2; @@ -149,6 +190,39 @@ namespace LibCmo { return 0u; } + bool CKStateChunk::SeekIdentifier(CKDWORD identifier) { + return false; + } + + bool CKStateChunk::UnPack(CKDWORD DestSize) { + // NOTE: in UnPack. pData store the compressed buffer, and + // dwSize store the length of compressed buffer as CHAR size, not DWORD size! + + // create a enough buffer + char* buffer = new(std::nothrow) char[DestSize]; + if (buffer == nullptr) return false; + uLongf destSize = DestSize; + // uncompress it + auto err = uncompress( + reinterpret_cast(buffer), &destSize, + reinterpret_cast(this->m_pData), static_cast(this->m_DataDwSize) + ); + // if no error, assign data + if (err == Z_OK) { + // get dw size and copy it to remove useless blank data + this->m_DataDwSize = static_cast(destSize) / sizeof(CKDWORD); + + delete[] this->m_pData; + this->m_pData = nullptr; + this->m_pData = new(std::nothrow) CKDWORD[this->m_DataDwSize]; + if (this->m_pData != nullptr) { + memcpy(this->m_pData, buffer, this->m_DataDwSize * sizeof(CKDWORD)); + } + } + delete[] buffer; + return true; + } + void LibCmo::CKStateChunk::_EnsureEnoughSpace(CKDWORD size) { ; } diff --git a/LibCmo/VTStruct.cpp b/LibCmo/VTStruct.cpp index 544da84..d0fd625 100644 --- a/LibCmo/VTStruct.cpp +++ b/LibCmo/VTStruct.cpp @@ -1,7 +1,7 @@ #include "VTUtils.hpp" #if defined(LIBCMO_OS_WIN32) #define ZLIB_WINAPI -#include "zconf.h" +#include #endif #include "VTStruct.hpp" diff --git a/LibCmo/VTStruct.hpp b/LibCmo/VTStruct.hpp index 31a62bd..c26453b 100644 --- a/LibCmo/VTStruct.hpp +++ b/LibCmo/VTStruct.hpp @@ -85,13 +85,22 @@ namespace LibCmo { class CKStateChunk { public: CKStateChunk(); + CKStateChunk(CK_CLASSID clsid); CKStateChunk(const CKStateChunk&) = delete; CKStateChunk& operator=(const CKStateChunk&) = delete; ~CKStateChunk(); + bool ConvertFromOldBuffer(const void* buf, CKDWORD buf_size, CKDWORD blk_size, CK_FILE_WRITEMODE mode); bool ConvertFromBuffer(const void* buf); CKDWORD ConvertToBuffer(void* buf); + bool UnPack(CKDWORD DestSize); + CKDWORD GetDataSize(void); + + bool SeekIdentifier(CKDWORD identifier); + + void ReadString(std::string& strl); + private: CK_CLASSID m_ClassId; CKDWORD m_DataDwSize; @@ -138,7 +147,7 @@ namespace LibCmo { CK_CLASSID ObjectCid; void* ObjPtr; std::string Name; - void* Data; + CKStateChunk* Data; CKDWORD PostPackSize; CKDWORD PrePackSize; CK_FO_OPTIONS Options; @@ -146,6 +155,11 @@ namespace LibCmo { CK_FILE_WRITEMODE SaveFlags; }; + struct CKFileManagerData { + CKStateChunk* Data; + CKGUID Manager; + }; + struct CKFilePluginDependencies { CK_PLUGIN_TYPE m_PluginCategory; XArray m_Guids; @@ -170,7 +184,7 @@ namespace LibCmo { int32_t m_SaveIDMax; XArray m_FileObject; - //XArray m_ManagersData; + XArray m_ManagersData; XClassArray m_PluginDep; XClassArray m_IndexByClassId; XClassArray m_IncludedFiles;