diff --git a/LibCmo/CK2/CKContext.cpp b/LibCmo/CK2/CKContext.cpp index 4088707..71120cc 100644 --- a/LibCmo/CK2/CKContext.cpp +++ b/LibCmo/CK2/CKContext.cpp @@ -14,7 +14,7 @@ namespace LibCmo::CK2 { #pragma region Ctor Dtor CKContext::CKContext() : - m_ObjectsList(), m_ReturnedObjectIds(), + m_ObjectsList(), m_ReturnedObjectOffsets(), m_GroupGlobalIndex(), m_SceneGlobalIndex(), m_CompressionLevel(5), m_FileWriteMode(CK_FILE_WRITEMODE::CKFILE_UNCOMPRESSED), m_NameEncoding(), m_TempFolder(), @@ -29,147 +29,28 @@ namespace LibCmo::CK2 { } CKContext::~CKContext() { - DestroyAllCKObjects(); + ClearAll(); } #pragma endregion #pragma region Objects Management - ObjImpls::CKObject* CKContext::CreateObject(CK_CLASSID cls, CKSTRING name, - CK_OBJECTCREATION_OPTIONS options, CK_CREATIONMODE* res) { - // todo: Process paramter options and res + void CKContext::ClearAll() { - // get description first - const CKClassDesc* desc = CKGetClassDesc(cls); - if (desc == nullptr) return nullptr; - - // allocate a CK_ID first - CK_ID decided_id; - if (this->m_ReturnedObjectIds.empty()) { - // create new CK_ID. - decided_id = static_cast(m_ObjectsList.size()); - m_ObjectsList.resize(decided_id + 1); - } else { - // use returned CK_ID. - decided_id = m_ReturnedObjectIds.back(); - m_ReturnedObjectIds.pop_back(); - } - - // create one - ObjImpls::CKObject* obj = desc->CreationFct(this, decided_id + c_ObjectIdOffset, name); - - // put into slot - m_ObjectsList[decided_id] = obj; - - // set out variable - return obj; - } - - ObjImpls::CKObject* CKContext::GetObject(CK_ID id) { - id -= c_ObjectIdOffset; - if (id >= m_ObjectsList.size()) return nullptr; - return m_ObjectsList[id]; - } - - /** - * @brief The real CKObject destroy worker shared by CKContext::DestroyObject and CKContext::~CKContext - * @param[in] ctx The CKContext - * @param[in] obj The CKObject need to be free. - */ - static void InternalDestroy(CKContext* ctx, ObjImpls::CKObject* obj) { - // find desc by classid - // if really we can not find it, we only can delete it directly. - const CKClassDesc* desc = CKGetClassDesc(obj->GetClassID()); - if (desc == nullptr) { - delete obj; - return; - } - - // free it and return its value - desc->ReleaseFct(ctx, obj); - } - void CKContext::DestroyObject(CK_ID id) { - id -= c_ObjectIdOffset; - if (id >= m_ObjectsList.size()) return; - - // get object and free it - ObjImpls::CKObject* obj = m_ObjectsList[id]; - if (obj == nullptr) return; - InternalDestroy(this, obj); - - // return its allocated id. - m_ObjectsList[id] = nullptr; - m_ReturnedObjectIds.emplace_back(id); - - } - - CKDWORD CKContext::AllocateGroupGlobalIndex() { - // try find first non-true position - CKDWORD index; - if (!XContainer::XBitArrayPatch::GetSetBitPosition(m_GroupGlobalIndex, 0, index)) { - // failed. distribute new one - index = static_cast(m_GroupGlobalIndex.size()); - m_GroupGlobalIndex.resize(m_GroupGlobalIndex.size() + 1); - } - - // set to occupy - m_GroupGlobalIndex[index] = true; - return index; - } - - CKDWORD CKContext::AllocateSceneGlobalIndex() { - // same as group - CKDWORD index; - if (!XContainer::XBitArrayPatch::GetSetBitPosition(m_SceneGlobalIndex, 0, index)) { - index = static_cast(m_SceneGlobalIndex.size()); - m_SceneGlobalIndex.resize(m_SceneGlobalIndex.size() + 1); - } - - m_SceneGlobalIndex[index] = true; - return index; - } - - void CKContext::FreeGroupGlobalIndex(CKDWORD id) { - // check position - if (id >= m_GroupGlobalIndex.size()) return; - // set value - m_GroupGlobalIndex[id] = false; - } - - void CKContext::FreeSceneGlobalIndex(CKDWORD id) { - // same as group - if (id >= m_SceneGlobalIndex.size()) return; - m_SceneGlobalIndex[id] = false; - } - - void CKContext::DestroyAllCKObjects() { - // free all created objects - for (auto& ptr : m_ObjectsList) { - if (ptr != nullptr) { - InternalDestroy(this, ptr); - } - } - // restore returned object list - m_ReturnedObjectIds.clear(); - // empty object list - m_ObjectsList.clear(); - - // clear group and scene global index at the same time - m_SceneGlobalIndex.clear(); - m_GroupGlobalIndex.clear(); } #pragma endregion #pragma region Common Manager Functions - CKINT CKContext::GetManagerCount() { - return 0; + CKDWORD CKContext::GetManagerCount() { + return m_ManagerList.size(); } - MgrImpls::CKBaseManager* CKContext::GetManager(CKINT index) { - return nullptr; + MgrImpls::CKBaseManager* CKContext::GetManager(CKDWORD index) { + if (index >= m_ManagerList.size()) return nullptr; + return m_ManagerList[index]; } #pragma endregion diff --git a/LibCmo/CK2/CKContext.hpp b/LibCmo/CK2/CKContext.hpp index f754d88..6111ab0 100644 --- a/LibCmo/CK2/CKContext.hpp +++ b/LibCmo/CK2/CKContext.hpp @@ -21,61 +21,44 @@ namespace LibCmo::CK2 { class CKContext { public: CKContext(); - CKContext(const CKContext&) = delete; - CKContext& operator=(const CKContext&) = delete; ~CKContext(); - - // ========== Objects Management ========== + LIBCMO_DISABLE_COPY_MOVE(CKContext); /** - * @brief Creates a CKObject or derived class instance. - * @param[in] cls Class Identifier (CK_CLASSID) of the object to create. - * @param[in] name The name of this object. - * @param[in] options Tell CKContext how to create this object when conflict happended. - * @param[out] res The value indicate the real method how this object created. - * @return A pointer to the newly created object. - * @remark CKObjects must be destroy with the DestroyObject method. - * @see CKObject, DestroyObject + * @brief Simply clear all CKContext to restore its status. */ - ObjImpls::CKObject* CreateObject(CK_CLASSID cls, CKSTRING name, - CK_OBJECTCREATION_OPTIONS options = CK_OBJECTCREATION_OPTIONS::CK_OBJECTCREATION_NONAMECHECK, - CK_CREATIONMODE* res = nullptr); - ObjImpls::CKObject* GetObject(CK_ID id); - void DestroyObject(CK_ID id); + void ClearAll(); - CKDWORD AllocateGroupGlobalIndex(); - CKDWORD AllocateSceneGlobalIndex(); - void FreeGroupGlobalIndex(CKDWORD id); - void FreeSceneGlobalIndex(CKDWORD id); + // ========== Common Managers ========== - void DestroyAllCKObjects(); + MgrImpls::CKObjectManager* GetObjectManager(); + MgrImpls::CKPathManager* GetPathManager(); - // ========== Object Access ========== + CKDWORD GetManagerCount(); + MgrImpls::CKBaseManager* GetManager(CKDWORD index); - //CKManagerImplements::CKBaseManager* CreateCKManager(CKGUID guid); - //CKManagerImplements::CKBaseManager* GetCKManager(CK_ID guid); - //void DestroyCKManager(CKManagerImplements::CKBaseManager* mgr); - - //CKObject* GetObjectByName(CKSTRING name, CKObject* previous = NULL); - //CKObject* GetObjectByNameAndClass(CKSTRING name, CK_CLASSID cid, CKObject* previous = NULL); - //CKObject* GetObjectByNameAndParentClass(CKSTRING name, CK_CLASSID pcid, CKObject* previous); - //const XContainer::XObjectPointerArray GetObjectListByType(CK_CLASSID cid, CKBOOL derived); - //CKINT GetObjectsCountByClassID(CK_CLASSID cid); - //CK_ID* GetObjectsListByClassID(CK_CLASSID cid); - - // ========== Common Managers Functions ========== - - CKINT GetManagerCount(); - MgrImpls::CKBaseManager* GetManager(CKINT index); + void ExecuteManagersOnPreClearAll(); + void ExecuteManagersOnPostClearAll(); + void ExecuteManagersSequenceToBeDeleted(); + void ExecuteManagersSequenceDeleted(); // ========== File Save/Load Options ========== - + void SetCompressionLevel(CKINT level); CKINT GetCompressionLevel(); void SetFileWriteMode(CK_FILE_WRITEMODE mode); CK_FILE_WRITEMODE GetFileWriteMode(); + CK_TEXTURE_SAVEOPTIONS GetGlobalImagesSaveOptions(); + void SetGlobalImagesSaveOptions(CK_TEXTURE_SAVEOPTIONS Options); + + CKBitmapProperties* GetGlobalImagesSaveFormat(); + void SetGlobalImagesSaveFormat(CKBitmapProperties* Format); + + CK_SOUND_SAVEOPTIONS GetGlobalSoundsSaveOptions(); + void SetGlobalSoundsSaveOptions(CK_SOUND_SAVEOPTIONS Options); + // ========== Encoding utilities ========== void GetUtf8String(const std::string& native_name, std::string& u8_name); @@ -95,24 +78,16 @@ namespace LibCmo::CK2 { void SetOutputCallback(OutputCallback cb); protected: - // ========== Objects Management ========== - - /** - * The global offset of created CK_ID. - * The value close to zero may cause some issue. - * So we add a static offset to every created CK_ID. - */ - const CK_ID c_ObjectIdOffset = 61u; - - XContainer::XArray m_ObjectsList; - std::deque m_ReturnedObjectIds; - - XContainer::XBitArray m_GroupGlobalIndex; - XContainer::XBitArray m_SceneGlobalIndex; + // ========== Common Managers ========== + void ExecuteManagersGeneral(std::function fct); + XContainer::XArray m_ManagerList; // ========== File Save/Load Options ========== CKINT m_CompressionLevel; CK_FILE_WRITEMODE m_FileWriteMode; + CK_TEXTURE_SAVEOPTIONS m_GlobalImagesSaveOptions; + CK_SOUND_SAVEOPTIONS m_GlobalSoundsSaveOptions; + CKBitmapProperties* m_GlobalImagesSaveFormat; // ========== Encoding utilities ========== diff --git a/LibCmo/CK2/CKFileReader.cpp b/LibCmo/CK2/CKFileReader.cpp index 71ac9b0..c1c6aef 100644 --- a/LibCmo/CK2/CKFileReader.cpp +++ b/LibCmo/CK2/CKFileReader.cpp @@ -348,7 +348,7 @@ namespace LibCmo::CK2 { if (obj.Data == nullptr) continue; // create object and assign created obj ckid - obj.ObjPtr = m_Ctx->CreateObject(obj.ObjectCid, obj.Name.c_str()); + obj.ObjPtr = m_Ctx->CreateObject(obj.ObjectCid, obj.Name.toCKSTRING()); if (obj.ObjPtr == nullptr) { obj.CreatedObjectId = 0u; } else { diff --git a/LibCmo/CK2/CKFileWriter.cpp b/LibCmo/CK2/CKFileWriter.cpp index 0057bb5..2c751ba 100644 --- a/LibCmo/CK2/CKFileWriter.cpp +++ b/LibCmo/CK2/CKFileWriter.cpp @@ -89,9 +89,9 @@ namespace LibCmo::CK2 { for (auto& obj : m_FileObjects) { // += 4DWORD(ObjId, ObjCid, FileIndex, NameLen) sumHdrObjSize += 4 * CKSizeof(CKDWORD); - if (obj.Name.c_str() != nullptr) { + if (obj.Name.toCKSTRING() != nullptr) { // += Name size - m_Ctx->GetNativeString(obj.Name.string(), name_conv); + m_Ctx->GetNativeString(obj.Name.toString(), name_conv); sumHdrObjSize += static_cast(name_conv.size()); } @@ -176,8 +176,8 @@ namespace LibCmo::CK2 { hdrparser->Write(&obj.ObjectCid, sizeof(CK_CLASSID)); hdrparser->Write(&obj.FileIndex, sizeof(CKDWORD)); - if (obj.Name.c_str() != nullptr) { - m_Ctx->GetNativeString(obj.Name.string(), name_conv); + if (obj.Name.toCKSTRING() != nullptr) { + m_Ctx->GetNativeString(obj.Name.toString(), name_conv); CKDWORD namelen = static_cast(name_conv.size()); hdrparser->Write(&namelen, sizeof(CKDWORD)); hdrparser->Write(name_conv.data(), namelen); diff --git a/LibCmo/CK2/CKGlobals.cpp b/LibCmo/CK2/CKGlobals.cpp index 82755bc..23eb8e0 100644 --- a/LibCmo/CK2/CKGlobals.cpp +++ b/LibCmo/CK2/CKGlobals.cpp @@ -64,6 +64,22 @@ namespace LibCmo::CK2 { #pragma endregion +#pragma region String Utilities + + bool CKStrEqual(CKSTRING str1, CKSTRING str2) { + if (str1 == nullptr) { + if (str2 == nullptr) return true; + else return false; + } else { + if (str2 == nullptr) return false; + else { + return std::strcmp(str1, str2) == 0; + } + } + } + +#pragma endregion + #pragma region CKClass Registration static XContainer::XArray g_CKClassInfo; diff --git a/LibCmo/CK2/CKGlobals.hpp b/LibCmo/CK2/CKGlobals.hpp index cac3f72..a03a22d 100644 --- a/LibCmo/CK2/CKGlobals.hpp +++ b/LibCmo/CK2/CKGlobals.hpp @@ -45,6 +45,10 @@ namespace LibCmo::CK2 { */ CKDWORD CKComputeDataCRC(const void* data, CKDWORD size, CKDWORD PreviousCRC = 0); + // ========== String Utilities ========== + + bool CKStrEqual(CKSTRING str1, CKSTRING str2); + // ========== Numberic Utilities ========== /* diff --git a/LibCmo/CK2/CKTypes.hpp b/LibCmo/CK2/CKTypes.hpp index 9c0af23..8cb99cc 100644 --- a/LibCmo/CK2/CKTypes.hpp +++ b/LibCmo/CK2/CKTypes.hpp @@ -272,6 +272,7 @@ namespace LibCmo::CK2 { //--- Managers namespace MgrImpls { class CKBaseManager; + class CKObjectManager; class CKSoundManager; class CKTimeManager; class CKRenderManager; diff --git a/LibCmo/CK2/MgrImpls/CKBaseManager.hpp b/LibCmo/CK2/MgrImpls/CKBaseManager.hpp index 46fe30a..f0f5cfe 100644 --- a/LibCmo/CK2/MgrImpls/CKBaseManager.hpp +++ b/LibCmo/CK2/MgrImpls/CKBaseManager.hpp @@ -2,6 +2,28 @@ #include "../../VTAll.hpp" +/** +CKBaseManager virtual functions implementations help +All virtual functions is not supported except we implemented. + +Read/Write functions: +- SaveData() +- LoadData() + +Clear functions: +- PreClearAll() +- PostClearAll() + +Delete functions: +- SequenceToBeDeleted() +- SequenceDeleted() + +Moved functions +- OnCKInit(): Implement in ctor +- OnCKEnd(): Implement in dtor + +*/ + namespace LibCmo::CK2::MgrImpls { /** @@ -16,8 +38,7 @@ namespace LibCmo::CK2::MgrImpls { CKBaseManager(CKContext* ctx, CKGUID guid, CKSTRING name) : m_ManagerGuid(guid), m_ManagerName(name), - m_Context(ctx) - {} + m_Context(ctx) {} virtual ~CKBaseManager() {} LIBCMO_DISABLE_COPY_MOVE(CKBaseManager); @@ -37,8 +58,8 @@ namespace LibCmo::CK2::MgrImpls { ``` @see CKContext::RegisterNewManager, GetName */ - CKGUID GetGuid() { - return m_ManagerGuid; + CKGUID GetGuid() { + return m_ManagerGuid; } /** @brief Acces to Manager name @@ -55,14 +76,14 @@ namespace LibCmo::CK2::MgrImpls { } ``` */ - CKSTRING GetName() { - return m_ManagerName.c_str(); + CKSTRING GetName() { + return m_ManagerName.toCKSTRING(); } /** @brief Called to save manager data. @param SavedFile A pointer to the CKFile being saved. - @return This function should return a valid CKStateChunk that contain data to save or NULL if there is nothing to save. + @return This function should return true or false if there is nothing to save. @remark + During a save operation, each manager is given the opportunity to save its data in the file. + The file being saved is given for information only and must not be modified. It can be used to decide whether it is worth saving @@ -70,21 +91,68 @@ namespace LibCmo::CK2::MgrImpls { @see CKStateChunk, LoadData */ virtual bool SaveData(CKStateChunk* chunk, CKFileVisitor* SavedFile) { - return true; + return false; // default value is false to indicate this manager do not need save any data. } /** @brief Called to load manager data. @param chunk A pointer to a CKStateChunk that was saved in the file. @param LoadedFile A pointer to the CKFile being loaded. - @return CK_OK if successful or an error code otherwise. + @return CKERR_OK if successful or an error code otherwise. @remark + During a load operation, each manager is automatically called if there was a chunk saved in the file with SaveData. @see CKStateChunk, SaveData */ virtual CKERROR LoadData(CKStateChunk* chunk, CKFileVisitor* LoadedFile) { - return CKERROR::CKERR_OK; + return CKERROR::CKERR_OK; } + /** + @brief Called at the beginning of a CKContext::ClearAll operation. + @return CK_OK if successful or an error code otherwise. + @remark + + You can override this function to add specific processing at the beginning of a CKContext::ClearAll operation. + + You must override GetValidFunctionsMask and return a value including + CKMANAGER_FUNC_PreClearAll for this function to get called. + @see Main Virtools Events, PostClearAll, CKContext::ClearAll + */ + virtual CKERROR PreClearAll() { return CKERROR::CKERR_OK; } + /** + @brief Called at the end of a CKContext::ClearAll operation. + @return CKERR_OK if successful or an error code otherwise. + @remark + + You can override this function to add specific processing at the end of a CKContext::ClearAll operation. + + You must override GetValidFunctionsMask and return a value including + CKMANAGER_FUNC_PostClearAll for this function to get called. + @see Main Virtools Events, PreClearAll, CKContext::ClearAll + */ + virtual CKERROR PostClearAll() { return CKERROR::CKERR_OK; } + + /** + @brief Called just before objects are deleted. + @return CK_OK if successful or an error code otherwise. + @param objids[in] A pointer to a list of CK_ID of the objects being deleted. + @param count[in] number of objects in objids list. + @remark + + You can override this function if you need to add specific processing before objects are deleted. + + You must override GetValidFunctionsMask and return a value including + CKMANAGER_FUNC_OnSequenceToBeDeleted for this function to get called. + @see Main Virtools Events, CKContext::DestroyObjects, SequenceDeleted + */ + virtual CKERROR SequenceToBeDeleted(const CK_ID* objids, CKDWORD count) { return CKERROR::CKERR_OK; } + /** + @brief Called after objects have been deleted. + @return CK_OK if successful or an error code otherwise. + @param objids[in] A pointer to a list of CK_ID of the objects being deleted. + @param count[in] number of objects in objids list. + @remark + + You can override this function if you need to add specific processing after objects have been deleted. + + You must override GetValidFunctionsMask and return a value including + CKMANAGER_FUNC_OnSequenceDeleted for this function to get called. + @see Main Virtools Events, CKContext::DestroyObjects, SequenceToBeDeleted + */ + virtual CKERROR SequenceDeleted(const CK_ID* objids, CKDWORD count) { return CKERROR::CKERR_OK; } + + protected: CKGUID m_ManagerGuid; ///> Manager GUID TypeHelper::MKString m_ManagerName; ///> Manager Name diff --git a/LibCmo/CK2/MgrImpls/CKObjectManager.cpp b/LibCmo/CK2/MgrImpls/CKObjectManager.cpp new file mode 100644 index 0000000..2336420 --- /dev/null +++ b/LibCmo/CK2/MgrImpls/CKObjectManager.cpp @@ -0,0 +1,196 @@ +#include "CKObjectManager.hpp" +#include "../ObjImpls/CKObject.hpp" + +namespace LibCmo::CK2::MgrImpls { + + CKObjectManager::CKObjectManager(CKContext* ctx) : + CKBaseManager(ctx, OBJECT_MANAGER_GUID, "Object Manager"), + m_ObjectsList(), m_ReturnedObjectOffsets(), m_ObjectCount(0), + m_GroupGlobalIndex(), m_SceneGlobalIndex(), + m_ObjectsListByClass(CKGetClassCount()) {} + + CKObjectManager::~CKObjectManager() { + DestroyAllObjects(); + } + + ObjImpls::CKObject* CKObjectManager::CreateObject(CK_CLASSID cls, CKSTRING name, + CK_OBJECTCREATION_OPTIONS options, CK_CREATIONMODE* res) { + // todo: Process paramter options and res + + // get description first + const CKClassDesc* desc = CKGetClassDesc(cls); + if (desc == nullptr) return nullptr; + + // allocate a CK_ID first + CKDWORD decided_off; + if (this->m_ReturnedObjectOffsets.empty()) { + // create new CK_ID. + decided_off = static_cast(m_ObjectsList.size()); + m_ObjectsList.resize(decided_off + 1); + } else { + // use returned CK_ID. + decided_off = m_ReturnedObjectOffsets.back(); + m_ReturnedObjectOffsets.pop_back(); + } + + // create one + ObjImpls::CKObject* obj = desc->CreationFct(m_Context, Offset2Id(decided_off), name); + + // put into slot and inc count + m_ObjectsList[decided_off] = obj; + ++m_ObjectCount; + + // set out variable + return obj; + } + + ObjImpls::CKObject* CKObjectManager::GetObject(CK_ID id) { + CKDWORD off = Id2Offset(id); + if (off >= m_ObjectsList.size()) return nullptr; + return m_ObjectsList[off]; + } + + CKDWORD CKObjectManager::GetObjectCount() { + return m_ObjectCount; + } + + void CKObjectManager::InternalDestroy(ObjImpls::CKObject* obj) { + // find desc by classid + // if really we can not find it, we only can delete it directly. + const CKClassDesc* desc = CKGetClassDesc(obj->GetClassID()); + if (desc == nullptr) { + delete obj; + return; + } + + // free it + desc->ReleaseFct(m_Context, obj); + } + + void CKObjectManager::DestroyObject(CK_ID id) { + CKDWORD off = Id2Offset(id); + if (off >= m_ObjectsList.size()) return; + + // get object and free it + ObjImpls::CKObject* obj = m_ObjectsList[off]; + if (obj == nullptr) return; + InternalDestroy(obj); + + // return its allocated id. + // and dec count + m_ObjectsList[off] = nullptr; + m_ReturnedObjectOffsets.emplace_back(off); + --m_ObjectCount; + + } + + void CKObjectManager::DestroyAllObjects() { + // free all created objects + for (auto& ptr : m_ObjectsList) { + if (ptr != nullptr) { + InternalDestroy(ptr); + } + } + // restore returned object list + m_ReturnedObjectOffsets.clear(); + // empty object list + m_ObjectsList.clear(); + // reset count + m_ObjectCount = 0; + + // clear obj by class list + for (auto& ls : m_ObjectsListByClass) { + ls.clear(); + } + + // clear group and scene global index at the same time + m_SceneGlobalIndex.clear(); + m_GroupGlobalIndex.clear(); + } + + XContainer::XObjectPointerArray CKObjectManager::GetObjectByNameAndClass(CKSTRING name, CK_CLASSID cid, bool derived) { + XContainer::XObjectPointerArray result; + + for (size_t i = 0; i < m_ObjectsListByClass.size(); ++i) { + // check class id first + if (derived) { + if (!CKIsChildClassOf(static_cast(i), cid)) continue; + } else { + if (static_cast(i) != cid) continue; + } + + // iterate all sub object and check name + for (const auto& objoff : m_ObjectsListByClass[i]) { + ObjImpls::CKObject* obj = m_ObjectsList[objoff]; + + if (name == nullptr) { + // directly add + result.emplace_back(obj); + } else { + // check name + if (CKStrEqual(name, obj->GetName())) { + result.emplace_back(obj); + } + } + } + } + } + + CKDWORD CKObjectManager::AllocateGroupGlobalIndex(ObjImpls::CKObject* group) { + // try find first nullptr position + CKDWORD index = 0; + for (const auto& ptr : m_GroupGlobalIndex) { + if (ptr == nullptr) break; + ++index; + } + // resize array for new position + if (index == m_GroupGlobalIndex.size()) { + m_GroupGlobalIndex.resize(m_GroupGlobalIndex.size() + 1); + } + + // set to occupy + m_GroupGlobalIndex[index] = group; + return index; + } + + CKDWORD CKObjectManager::AllocateSceneGlobalIndex(ObjImpls::CKObject* scene) { + // same as group + CKDWORD index = 0; + for (const auto& ptr : m_SceneGlobalIndex) { + if (ptr == nullptr) break; + ++index; + } + // resize array for new position + if (index == m_SceneGlobalIndex.size()) { + m_SceneGlobalIndex.resize(m_SceneGlobalIndex.size() + 1); + } + + // set to occupy + m_SceneGlobalIndex[index] = scene; + return index; + } + + ObjImpls::CKObject* CKObjectManager::GetGroupByGlobalIndex(CKDWORD index) { + if (index >= m_GroupGlobalIndex.size()) return nullptr; + else return m_GroupGlobalIndex[index]; + } + + ObjImpls::CKObject* CKObjectManager::GetSceneByGlobalIndex(CKDWORD index) { + if (index >= m_SceneGlobalIndex.size()) return nullptr; + else return m_SceneGlobalIndex[index]; + } + + void CKObjectManager::FreeGroupGlobalIndex(CKDWORD id) { + // check position + if (id >= m_GroupGlobalIndex.size()) return; + // set value + m_GroupGlobalIndex[id] = nullptr; + } + + void CKObjectManager::FreeSceneGlobalIndex(CKDWORD id) { + // same as group + if (id >= m_SceneGlobalIndex.size()) return; + m_SceneGlobalIndex[id] = nullptr; + } + +} diff --git a/LibCmo/CK2/MgrImpls/CKObjectManager.hpp b/LibCmo/CK2/MgrImpls/CKObjectManager.hpp new file mode 100644 index 0000000..ce6d88c --- /dev/null +++ b/LibCmo/CK2/MgrImpls/CKObjectManager.hpp @@ -0,0 +1,81 @@ +#pragma once + +#include "../../VTAll.hpp" +#include "CKBaseManager.hpp" +#include + +namespace LibCmo::CK2::MgrImpls { + + class CKObjectManager : public CKBaseManager { + public: + CKObjectManager(CKContext* ctx); + virtual ~CKObjectManager(); + LIBCMO_DISABLE_COPY_MOVE(CKObjectManager); + + // ========== Objects Management ========== + + /** + * @brief Creates a CKObject or derived class instance. + * @param[in] cls Class Identifier (CK_CLASSID) of the object to create. + * @param[in] name The name of this object. + * @param[in] options Tell CKContext how to create this object when conflict happended. + * @param[out] res The value indicate the real method how this object created. + * @return A pointer to the newly created object. + * @remark CKObjects must be destroy with the DestroyObject method. + * @see CKObject, DestroyObject + */ + ObjImpls::CKObject* CreateObject(CK_CLASSID cls, CKSTRING name, + CK_OBJECTCREATION_OPTIONS options = CK_OBJECTCREATION_OPTIONS::CK_OBJECTCREATION_NONAMECHECK, + CK_CREATIONMODE* res = nullptr); + // todo: implement CopyObject by CKClassDesc + //ObjImpls::CKObject* CopyObject(ObjImpls::CKObject *src, + // CKDependencies* Dependencies = nullptr, + // CKSTRING AppendName = nullptr, + // CK_OBJECTCREATION_OPTIONS Options = CK_OBJECTCREATION_OPTIONS::CK_OBJECTCREATION_NONAMECHECK); + + ObjImpls::CKObject* GetObject(CK_ID id); + CKDWORD GetObjectCount(); + void DestroyObject(CK_ID id); + void DestroyAllObjects(); + + // ========== Objects Access ========== + + XContainer::XObjectPointerArray GetObjectByNameAndClass( + CKSTRING name, CK_CLASSID cid, bool derived); + + // ========== Special Functions ========== + + CKDWORD AllocateGroupGlobalIndex(ObjImpls::CKObject* group); + CKDWORD AllocateSceneGlobalIndex(ObjImpls::CKObject* scene); + ObjImpls::CKObject* GetGroupByGlobalIndex(CKDWORD index); + ObjImpls::CKObject* GetSceneByGlobalIndex(CKDWORD index); + void FreeGroupGlobalIndex(CKDWORD id); + void FreeSceneGlobalIndex(CKDWORD id); + + protected: + /** + * The global offset of created CK_ID. + * The value close to zero may cause some issue. + * So we add a static offset to every created CK_ID. + */ + const CK_ID c_ObjectIdOffset = 61u; + CKDWORD Id2Offset(CK_ID id) { return static_cast(id - c_ObjectIdOffset); } + CK_ID Offset2Id(CKDWORD off) { return static_cast(off + c_ObjectIdOffset); } + + /** + * @brief The real CKObject destroy worker shared by CKObjectManager::DestroyObject and CKObjectManager::~CKObjectManager + * @param[in] obj The CKObject need to be free. + */ + void InternalDestroy(ObjImpls::CKObject* obj); + + CKDWORD m_ObjectCount; + XContainer::XObjectPointerArray m_ObjectsList; + XContainer::XArray> m_ObjectsListByClass; + std::deque m_ReturnedObjectOffsets; + + XContainer::XObjectPointerArray m_GroupGlobalIndex; + XContainer::XObjectPointerArray m_SceneGlobalIndex; + + }; + +} diff --git a/LibCmo/CK2/MgrImpls/CKPathManager.cpp b/LibCmo/CK2/MgrImpls/CKPathManager.cpp new file mode 100644 index 0000000..9525ae7 --- /dev/null +++ b/LibCmo/CK2/MgrImpls/CKPathManager.cpp @@ -0,0 +1,6 @@ +#include "CKPathManager.hpp" + +namespace LibCmo::CK2::MgrImpls { + +} + diff --git a/LibCmo/CK2/MgrImpls/CKPathManager.hpp b/LibCmo/CK2/MgrImpls/CKPathManager.hpp new file mode 100644 index 0000000..675e7d8 --- /dev/null +++ b/LibCmo/CK2/MgrImpls/CKPathManager.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include "../../VTAll.hpp" +#include "CKBaseManager.hpp" + +namespace LibCmo::CK2::MgrImpls { + + class CKPathManager : public CKBaseManager { + public: + CKPathManager(CKContext* ctx) : + CKBaseManager(ctx, PATH_MANAGER_GUID, "Path Manager") {} + virtual ~CKPathManager() {} + LIBCMO_DISABLE_COPY_MOVE(CKPathManager); + + protected: + + }; + +} diff --git a/LibCmo/CK2/ObjImpls/CKObject.hpp b/LibCmo/CK2/ObjImpls/CKObject.hpp index ca486d6..fbeb230 100644 --- a/LibCmo/CK2/ObjImpls/CKObject.hpp +++ b/LibCmo/CK2/ObjImpls/CKObject.hpp @@ -2,6 +2,37 @@ #include "../../VTAll.hpp" +/** +CKObject virtual functions implementations help + +Implement as original meaning: +- PreSave() +- Save() +- Load() +- PostLoad() + +- GetClassID() + +- Show() +- IsHiddenByParent() +- CanBeHide() +- IsVisible() + +No implement because don't care: +- GetMemoryOccupation() +- IsObjectUsed() +- PrepareDependencies() +- RemapDependencies() + +- CheckPreDeletion() +- CheckPostDeletion() + +Implement moved into other location: +- Copy(): Use CKObject::CKObject(CK_ID newid, const CKObject* obj) ctor and CKClassDesc to implement. +- PreDelete(): Write in dtor. + +*/ + namespace LibCmo::CK2::ObjImpls { class CKObject { @@ -19,7 +50,7 @@ namespace LibCmo::CK2::ObjImpls { return m_ID; } CKSTRING GetName(void) { - return m_Name.c_str(); + return m_Name.toCKSTRING(); } void SetName(CKSTRING u8_name) { m_Name = u8_name; diff --git a/LibCmo/LibCmo.vcxproj b/LibCmo/LibCmo.vcxproj index 4668ae9..68ec90b 100644 --- a/LibCmo/LibCmo.vcxproj +++ b/LibCmo/LibCmo.vcxproj @@ -180,6 +180,8 @@ + + @@ -203,6 +205,8 @@ + + diff --git a/LibCmo/LibCmo.vcxproj.filters b/LibCmo/LibCmo.vcxproj.filters index 9cbc81c..983e87d 100644 --- a/LibCmo/LibCmo.vcxproj.filters +++ b/LibCmo/LibCmo.vcxproj.filters @@ -96,6 +96,12 @@ Sources\CK2\ObjImpls + + Sources\CK2\MgrImpls + + + Sources\CK2\MgrImpls + @@ -167,5 +173,11 @@ Headers\CK2\ObjImpls + + Headers\CK2\MgrImpls + + + Headers\CK2\MgrImpls + \ No newline at end of file diff --git a/LibCmo/VTUtils.hpp b/LibCmo/VTUtils.hpp index a26e2c2..def6e87 100644 --- a/LibCmo/VTUtils.hpp +++ b/LibCmo/VTUtils.hpp @@ -94,12 +94,26 @@ namespace LibCmo { m_Str = m_HasStr ? cstr : ""; return *this; } + bool operator==(const char* rhs) const { + if (m_HasStr) { + return m_Str == rhs; + } else { + return rhs == nullptr; + } + } MKString(const std::string& cstr) : m_HasStr(true), m_Str(cstr) {} MKString& operator=(const std::string& cstr) { m_HasStr = true; m_Str = cstr; return *this; } + bool operator==(const std::string& rhs) const { + if (m_HasStr) { + return m_Str == rhs; + } else { + return false; + } + } MKString(const MKString& rhs) : m_HasStr(rhs.m_HasStr), m_Str(rhs.m_Str) {} MKString(MKString&& rhs) noexcept : m_HasStr(rhs.m_HasStr), m_Str(std::move(rhs.m_Str)) { @@ -116,13 +130,26 @@ namespace LibCmo { rhs.m_HasStr = false; return *this; } + bool operator==(const MKString& rhs) const { + return (m_HasStr == rhs.m_HasStr && m_Str == rhs.m_Str); + } - const char* c_str() const { + const char* toCKSTRING() const { return m_HasStr ? m_Str.c_str() : nullptr; } - const std::string& string() const { + /** + * @brief Return the std::string format of this string. + * @remark nullptr string will return blank string. + * @return The std::string format of this string. + */ + const std::string& toString() const { return m_Str; } + /** + * @brief The size of this string. + * @remark Both empty string and nullptr will return 0 + * @return The size of this string + */ const size_t size() const { return m_HasStr ? m_Str.size() : 0u; }