#pragma once #include "../VTInternal.hpp" namespace LibCmo::XContainer { using XIntArray = XArray; using XFileObjectsTable = XHashTable; } namespace LibCmo::CK2 { class CKBufferParser { private: CKBYTE* m_MemBegin; CKDWORD m_MemPos; bool m_NeedManualFree; CKDWORD m_MemSize; public: /** * @brief Create CKBufferParser from a existed buffer. * @param ptr The start pointer of buffer. This buffer should be allocated by 'new[]', not 'new' or 'malloc()'. * @param rsize The size of buffer. * @param need_manual_free True if provided buffer need freed by CKBufferParser automatically. */ CKBufferParser(const void* ptr, CKDWORD rsize, bool need_manual_free) : m_MemBegin(const_cast(static_cast(ptr))), m_MemPos(0u), m_MemSize(rsize), m_NeedManualFree(need_manual_free) {} /** * @brief Create CKBufferParser from a new created buffer. * @param newsize The size of new buffer. */ CKBufferParser(CKDWORD newsize) : m_MemBegin(new CKBYTE[newsize]), m_MemPos(0u), m_MemSize(newsize), m_NeedManualFree(true) {} ~CKBufferParser() { if (this->m_NeedManualFree) delete[](this->m_MemBegin); } YYCC_DEL_CLS_COPY_MOVE(CKBufferParser); const void* GetPtr(CKINT extraoff = 0) { return (this->m_MemBegin + m_MemPos + extraoff); } void* GetMutablePtr(CKINT extraoff = 0) { return (this->m_MemBegin + m_MemPos + extraoff); } void* GetBase(void) { return this->m_MemBegin; } CKDWORD GetSize(void) { return this->m_MemSize; } CKDWORD GetCursor(void) { return this->m_MemPos; } void MoveCursor(CKINT off) { this->m_MemPos += off; } void SetCursor(CKDWORD off) { this->m_MemPos = off; } void Read(void* data, CKDWORD data_size) { std::memcpy(data, (this->m_MemBegin + m_MemPos), data_size); this->m_MemPos += data_size; } template void Read(_Ty* data) { Read(data, CKSizeof(_Ty)); } void Write(const void* data, CKDWORD data_size) { std::memcpy((this->m_MemBegin + m_MemPos), data, data_size); this->m_MemPos += data_size; } template void Write(const _Ty* data) { Write(data, CKSizeof(_Ty)); } }; #pragma pack(1) struct CKRawFileInfo { CKBYTE NeMo[8]; CKDWORD Crc; CKDWORD CKVersion; CKDWORD FileVersion; CKDWORD Zero; CKDWORD FileWriteMode; CKDWORD Hdr1PackSize; CKDWORD DataPackSize; CKDWORD DataUnPackSize; CKDWORD ManagerCount; CKDWORD ObjectCount; CKDWORD MaxIDSaved; CKDWORD ProductVersion; CKDWORD ProductBuild; CKDWORD Hdr1UnPackSize; }; #pragma pack() class CKFileInfo { public: CKFileInfo() : ProductVersion(0u), ProductBuild(0x01010000u), FileWriteMode(CK_FILE_WRITEMODE::CKFILE_UNCOMPRESSED), FileVersion(8u), CKVersion(CKVERSION), FileSize(0u), ObjectCount(0u), ManagerCount(0u), MaxIDSaved(0u), Crc(0u), Hdr1PackSize(0u), Hdr1UnPackSize(0u), DataPackSize(0u), DataUnPackSize(0u) {} ~CKFileInfo() {} YYCC_DEF_CLS_COPY_MOVE(CKFileInfo); CKDWORD ProductVersion; /**< Virtools Version (Dev/Creation). (CK_VIRTOOLS_VERSION) */ CKDWORD ProductBuild; /**< Virtools Build Number. */ CK_FILE_WRITEMODE FileWriteMode; /**< Options used to save this file. (CK_FILE_WRITEMODE) */ CKDWORD FileVersion; /**< Version of file format when file was saved. */ CKDWORD CKVersion; /**< Version of CK when file was saved. */ CKDWORD FileSize; /**< Size of file in bytes. */ CKDWORD ObjectCount; /**< Number of objects stored in the file. */ CKDWORD ManagerCount; /**< Number of managers which saved data in the file. */ CK_ID MaxIDSaved; /**< Maximum Object identifier saved */ CKDWORD Crc; /**< Crc of data */ CKDWORD Hdr1PackSize; /**< The compressed size of Header section. */ CKDWORD Hdr1UnPackSize; /**< The uncompressed size of Header section. */ CKDWORD DataPackSize; /**< The compressed size of Data section. */ CKDWORD DataUnPackSize; /**< The uncompressed size of Data section. */ }; class CKFileObject { public: CKFileObject(); CKFileObject(const CKFileObject&); CKFileObject(CKFileObject&&); CKFileObject& operator=(const CKFileObject&); CKFileObject& operator=(CKFileObject&&); ~CKFileObject(); CK_ID ObjectId; /**< ID of the object being load/saved (as it will be/was saved in the file) */ CK_ID CreatedObjectId; /**< ID of the object being created */ CK_CLASSID ObjectCid; /**< Class Identifier of the object */ ObjImpls::CKObject* ObjPtr; /**< A pointer to the object itself (as CreatedObject when loading) */ XContainer::XString Name; /**< Name of the Object */ CKStateChunk* Data; /**< A CKStateChunk that contains object information */ CKDWORD PackSize; /**< The CKStateChunk data size */ //CKINT PostPackSize; /**< When compressed chunk by chunk : size of Data after compression */ //CKINT PrePackSize; /**< When compressed chunk by chunk : size of Data before compression */ CK_FO_OPTIONS Options; /**< When loading an object it may be renamed , use to replace another object */ CKDWORD FileIndex; /**< Position of the object data inside uncompressed file buffer */ CKDWORD SaveFlags; /**< Flags used when this object was saved. */ }; class CKFileManagerData { public: CKFileManagerData(); CKFileManagerData(const CKFileManagerData&); CKFileManagerData(CKFileManagerData&&); CKFileManagerData& operator=(const CKFileManagerData&); CKFileManagerData& operator=(CKFileManagerData&&); ~CKFileManagerData(); CKStateChunk* Data; CKGUID Manager; }; class CKFilePluginDependencies { public: CKFilePluginDependencies() : m_PluginCategory(CK_PLUGIN_TYPE::CKPLUGIN_MANAGER_DLL), m_Guids() {} ~CKFilePluginDependencies() {} YYCC_DEF_CLS_COPY_MOVE(CKFilePluginDependencies); CK_PLUGIN_TYPE m_PluginCategory; XContainer::XArray m_Guids; //XContainer::XBitArray ValidGuids; }; class CKFileVisitor { public: CKFileVisitor(CKFileReader* reader); CKFileVisitor(CKFileWriter* writer); CKFileVisitor(const CKFileVisitor&); CKFileVisitor(CKFileVisitor&&); CKFileVisitor& operator=(const CKFileVisitor&); CKFileVisitor& operator=(CKFileVisitor&&); const CKFileObject* GetFileObjectByIndex(CKDWORD index); CKDWORD GetIndexByObjectID(CK_ID objid); bool AddSavedFile(CKSTRING u8FileName); protected: bool m_IsReader; CKFileReader* m_Reader; CKFileWriter* m_Writer; CKContext* m_Ctx; }; class CKFileReader { friend class CKFileVisitor; public: CKFileReader(CKContext* ctx); ~CKFileReader(); YYCC_DEL_CLS_COPY_MOVE(CKFileReader); // ========== Loading ========== CKERROR ShallowLoad(CKSTRING u8_filename); CKERROR DeepLoad(CKSTRING u8_filename); // ========== Loading Result ========== CK_ID GetSaveIdMax(); const XContainer::XArray& GetFileObjects(); const XContainer::XArray& GetManagersData(); const XContainer::XArray& GetPluginsDep(); const XContainer::XArray& GetIncludedFiles(); const CKFileInfo GetFileInfo(); protected: bool m_Done; CK_ID m_SaveIDMax; /**< Maximum CK_ID found when saving or loading objects */ XContainer::XArray m_FileObjects; /**< List of objects being saved / loaded */ XContainer::XArray m_ManagersData; /**< Manager Data loaded */ XContainer::XArray m_PluginsDep; /**< Plugins dependencies for this file */ // XContainer::XClassArray m_IndexByClassId; /**< List of index in the m_FileObjects table sorted by ClassID */ /** * @brief List of files that should be inserted in the CMO file. * @remarks Each item is just file name, not the full path to file. */ XContainer::XArray m_IncludedFiles; CKFileInfo m_FileInfo; /**< Headers summary */ CKERROR ReadFileHeader(CKBufferParser* ParserPtr); CKERROR ReadFileData(CKBufferParser* ParserPtr); CKContext* m_Ctx; CKFileVisitor m_Visitor; }; class CKFileWriter { friend class CKFileVisitor; public: CKFileWriter(CKContext* ctx); CKFileWriter(CKContext* ctx, CKFileReader* reader, bool is_shallow); ~CKFileWriter(); YYCC_DEL_CLS_COPY_MOVE(CKFileWriter); // ========== Saving Preparing ========== bool AddSavedObject(ObjImpls::CKObject* obj, CKDWORD flags = CK_STATESAVE_ALL); bool AddSavedObjects(const XContainer::XObjectPointerArray& objarray, CKDWORD flags = CK_STATESAVE_ALL); bool AddSavedFile(CKSTRING u8FileName); // ========== Saving ========== CKERROR Save(CKSTRING u8_filename); protected: /** * @brief A helper function to check whether given file can be written. * @param[in] filename The name of file to be wriiten * @return CKERROR::CK_OK if can write. */ CKERROR PrepareFile(CKSTRING filename); /** * @brief Internal used Object Adder. * @details * This function is used by AddSavedObject() and Copy from reader constructor. * This function accept an object pointer, and try adding them. * And set m_IncludedFiles and m_ObjectsHashTable properly. * @param[in] obj The pointer to added object. * @param[in] flags The flag used when saving this object. * @return True if success. */ bool InternalObjectAdder(ObjImpls::CKObject* obj, CKDWORD flags = CK_STATESAVE_ALL); protected: bool m_Done; /**< True if this writer is already written into file. A written CKFileWriter can no be written again. */ /** * @brief True if this writer is not allowed to add objects. * @remarks * \li This field should be false in default. * \li This field usually be set when importing from reader. */ bool m_DisableAddingObject; /** * @brief True if this writer is not allowed to add files. * @remarks * \li This field should be false in default. * \li This field usually be set when importing from reader. */ bool m_DisableAddingFile; CK_ID m_SaveIDMax; /**< Maximum CK_ID found when saving or loading objects */ XContainer::XArray m_FileObjects; /**< List of objects being saved / loaded */ XContainer::XArray m_ManagersData; /**< Manager Data loaded */ XContainer::XArray m_PluginsDep; /**< Plugins dependencies for this file */ /** * @brief List of files that should be inserted in the CMO file. * @remarks Each item is the full path to file. */ XContainer::XArray m_IncludedFiles; XContainer::XHashTable m_ObjectsHashTable; /**< A Object ID to save index hash table. */ XContainer::XBitArray m_AlreadySavedMask; /**< Field recording saved object id. If this object is saved, set m_AlreadySavedMask[id] to true. Also used to check whether object already is in save list. */ CKFileInfo m_FileInfo; /**< Headers summary */ CKContext* m_Ctx; CKFileVisitor m_Visitor; }; }