From 1d9bd09224d2fe33c5dfc5257c37f99f06381b04 Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Mon, 30 Oct 2023 10:25:31 +0800 Subject: [PATCH] change bmap interface. update bmfile safe guard --- BMap/BMExports.cpp | 9 +- BMap/BMExports.hpp | 2 +- BMap/BMap.cpp | 47 +++++------ BMap/BMap.hpp | 200 ++++++++++++++++++++++++++++----------------- README.md | 11 +-- 5 files changed, 155 insertions(+), 114 deletions(-) diff --git a/BMap/BMExports.cpp b/BMap/BMExports.cpp index 3d00410..6430a28 100644 --- a/BMap/BMExports.cpp +++ b/BMap/BMExports.cpp @@ -105,7 +105,7 @@ bool BMFile_Load( // create a now one and try to load data. std::unique_ptr file(new BMap::BMFile(temp_folder, texture_folder, encoding_count, encodings, false)); - if (file->IsFreezed()) return false; + if (file->IsInitError()) return false; if (!file->Load(file_name)) return false; // add into list and return @@ -124,7 +124,7 @@ bool BMFile_Create( // create a now one std::unique_ptr file(new BMap::BMFile(temp_folder, texture_folder, encoding_count, encodings, false)); - if (file->IsFreezed()) return false; + if (file->IsInitError()) return false; // add into list and return if success g_AllBMFiles.emplace(file.get()); @@ -307,9 +307,10 @@ bool BMMeshTrans_PrepareFaceMtlSlot(BMPARAM_MESHTRANS_DECL(trans), BMPARAM_OUT(L BMPARAM_OUT_ASSIGN(out_mem, trans->PrepareFaceMtlSlot()); return BMPARAM_OUT_VAL(out_mem) != nullptr; } -bool BMMeshTrans_Parse(BMPARAM_MESHTRANS_DECL(trans), BMPARAM_IN(LibCmo::CK2::ObjImpls::CKMesh*, write_into_mesh)) { +bool BMMeshTrans_Parse(BMPARAM_MESHTRANS_DECL(trans), BMPARAM_IN(BMap::BMFile*, bmfile), BMPARAM_IN(LibCmo::CK2::CK_ID, objid)) { if (!CheckBMMeshTrans(trans)) return false; - return trans->Parse(write_into_mesh); + if (!CheckBMFile(bmfile)) return false; + return trans->Parse(bmfile, objid); } #pragma endregion diff --git a/BMap/BMExports.hpp b/BMap/BMExports.hpp index 8cadc72..d86829f 100644 --- a/BMap/BMExports.hpp +++ b/BMap/BMExports.hpp @@ -130,7 +130,7 @@ LIBCMO_EXPORT bool BMMeshTrans_PrepareFaceVertexIndices(BMPARAM_MESHTRANS_DECL(t LIBCMO_EXPORT bool BMMeshTrans_PrepareFaceNormalIndices(BMPARAM_MESHTRANS_DECL(trans), BMPARAM_OUT(LibCmo::CKDWORD*, out_mem)); LIBCMO_EXPORT bool BMMeshTrans_PrepareFaceUVIndices(BMPARAM_MESHTRANS_DECL(trans), BMPARAM_OUT(LibCmo::CKDWORD*, out_mem)); LIBCMO_EXPORT bool BMMeshTrans_PrepareFaceMtlSlot(BMPARAM_MESHTRANS_DECL(trans), BMPARAM_OUT(LibCmo::CKDWORD*, out_mem)); -LIBCMO_EXPORT bool BMMeshTrans_Parse(BMPARAM_MESHTRANS_DECL(trans), BMPARAM_IN(LibCmo::CK2::ObjImpls::CKMesh*, write_into_mesh)); +LIBCMO_EXPORT bool BMMeshTrans_Parse(BMPARAM_MESHTRANS_DECL(trans), BMPARAM_IN(BMap::BMFile*, bmfile), BMPARAM_IN(LibCmo::CK2::CK_ID, objid)); #pragma endregion diff --git a/BMap/BMap.cpp b/BMap/BMap.cpp index fbd74ff..33c31f1 100644 --- a/BMap/BMap.cpp +++ b/BMap/BMap.cpp @@ -107,12 +107,16 @@ namespace BMap { return m_FaceVertexs.data(); } - bool BMMeshTransition::Parse(LibCmo::CK2::ObjImpls::CKMesh* write_into_mesh) { - if (m_IsParsed || write_into_mesh == nullptr) return false; + bool BMMeshTransition::Parse(BMFile* bmfile, LibCmo::CK2::CK_ID mesh_id) { + // check basic status + if (m_IsParsed || bmfile == nullptr) return false; if (!m_IsVertexOK || !m_IsNormalOK || !m_IsUVOK || !m_IsFaceOK || !m_IsMtlSlotOK) return false; - m_IsParsed = true; + // check pointer assign + LibCmo::CK2::ObjImpls::CKObject* writing_mesh = bmfile->GetObjectPtr(mesh_id); + if (writing_mesh == nullptr || writing_mesh->GetClassID() != LibCmo::CK2::CK_CLASSID::CKCID_MESH) return false; // do parse + m_IsParsed = true; DoRealParse(); // check vertex overflow @@ -125,7 +129,7 @@ namespace BMap { } // apply to mesh - ApplyToMesh(write_into_mesh); + ApplyToMesh(bmfile, static_cast(writing_mesh)); return true; } @@ -139,7 +143,7 @@ namespace BMap { // iterate face for (size_t faceid = 0; faceid < face_size; ++faceid) { - LibCmo::CKDWORD idx[3]; + LibCmo::CKDWORD idx[3] { 0, 0, 0 }; for (int j = 0; j < 3; ++j) { // create one first TransitionVertex tvec( @@ -163,7 +167,7 @@ namespace BMap { } } - void BMMeshTransition::ApplyToMesh(LibCmo::CK2::ObjImpls::CKMesh* write_into_mesh) { + void BMMeshTransition::ApplyToMesh(BMFile* bmfile, LibCmo::CK2::ObjImpls::CKMesh* write_into_mesh) { LibCmo::CKDWORD vec_count = static_cast(m_ProcVertexs.size()), face_count = static_cast(m_ProcFaces.size()), mtl_count = static_cast(m_MtlSlots.size()); @@ -209,13 +213,12 @@ namespace BMap { } // set mtl slot - LibCmo::CK2::CKContext* correspondingCtx = write_into_mesh->GetCKContext(); write_into_mesh->SetMaterialSlotCount(mtl_count); LibCmo::CK2::ObjImpls::CKMaterial** pMtlSlot = write_into_mesh->GetMaterialSlots(); for (LibCmo::CKDWORD i = 0; i < mtl_count; ++i) { // convert id to CKMaterial* and check its type - LibCmo::CK2::ObjImpls::CKObject* mtlptr = correspondingCtx->GetObject(m_MtlSlots[i]); - if (mtlptr != nullptr && LibCmo::CK2::CKIsChildClassOf(mtlptr->GetClassID(), LibCmo::CK2::CK_CLASSID::CKCID_MATERIAL)) { + LibCmo::CK2::ObjImpls::CKObject* mtlptr = bmfile->GetObjectPtr(m_MtlSlots[i]); + if (mtlptr != nullptr && mtlptr->GetClassID() == LibCmo::CK2::CK_CLASSID::CKCID_MATERIAL) { *(pMtlSlot++) = static_cast(mtlptr); } else { *(pMtlSlot++) = nullptr; @@ -228,13 +231,13 @@ namespace BMap { #pragma region BMfile - BMFile::BMFile(LibCmo::CKSTRING temp_folder, LibCmo::CKSTRING texture_folder, LibCmo::CKDWORD encoding_count, LibCmo::CKSTRING* encodings, bool is_reader) : - m_IsReader(is_reader), m_IsFreezed(false) { + BMFile::BMFile(LibCmo::CKSTRING temp_folder, LibCmo::CKSTRING texture_folder, LibCmo::CKDWORD encoding_count, LibCmo::CKSTRING* encodings, bool is_loader) : + m_IsInitError(false), m_IsLoader(is_loader), m_HasLoaded(false), m_HasSaved(false), m_Context(nullptr) { m_Context = new LibCmo::CK2::CKContext(); // set temp folder and texture folder auto pm = m_Context->GetPathManager(); - m_IsFreezed = m_IsFreezed || !pm->AddPath(texture_folder); - m_IsFreezed = m_IsFreezed || !pm->SetTempFolder(temp_folder); + m_IsInitError = m_IsInitError || !pm->AddPath(texture_folder); + m_IsInitError = m_IsInitError || !pm->SetTempFolder(temp_folder); // set encoding LibCmo::XContainer::XArray cache; for (LibCmo::CKDWORD i = 0; i < encoding_count; ++i) { @@ -253,12 +256,8 @@ namespace BMap { delete m_Context; } - bool BMFile::IsFreezed() { - return m_IsFreezed; - } - bool BMFile::Load(LibCmo::CKSTRING filename) { - if (m_IsFreezed || !m_IsReader) return false; + if (!CanExecLoad()) return false; // create temp ckfile and load LibCmo::CK2::CKFileReader reader(m_Context); @@ -299,11 +298,12 @@ namespace BMap { } } + m_HasLoaded = true; return true; } bool BMFile::Save(LibCmo::CKSTRING filename, LibCmo::CKINT compress_level) { - if (m_IsFreezed || m_IsReader) return false; + if (!CanExecSave()) return false; // create temp writer LibCmo::CK2::CKFileWriter writer(m_Context); @@ -331,18 +331,11 @@ namespace BMap { // save to file and detect error LibCmo::CK2::CKERROR err = writer.Save(filename); - // set freezed to stop any change again. - // aka, only allow save once. - m_IsFreezed = true; - // return with error detect. + m_HasSaved = true; return err == LibCmo::CK2::CKERROR::CKERR_OK; } - LibCmo::CK2::ObjImpls::CKObject* BMFile::GetObjectPtr(LibCmo::CK2::CK_ID objid) { - return m_Context->GetObject(objid);; - } - LibCmo::CKDWORD BMFile::GetGroupCount() { return CommonGetObjectCount(m_ObjGroups); } LibCmo::CK2::CK_ID BMFile::GetGroup(LibCmo::CKDWORD idx) { return CommonGetObject(m_ObjGroups, idx); } LibCmo::CK2::CK_ID BMFile::CreateGroup() { return CommonCreateObject(m_ObjGroups, LibCmo::CK2::CK_CLASSID::CKCID_GROUP); } diff --git a/BMap/BMap.hpp b/BMap/BMap.hpp index 1a6828b..a17b9e0 100644 --- a/BMap/BMap.hpp +++ b/BMap/BMap.hpp @@ -8,6 +8,127 @@ namespace BMap { + class BMFile { + public: + BMFile(LibCmo::CKSTRING temp_folder, LibCmo::CKSTRING texture_folder, LibCmo::CKDWORD encoding_count, LibCmo::CKSTRING* encodings, bool is_reader); + ~BMFile(); + LIBCMO_DISABLE_COPY_MOVE(BMFile); + + // ===== safe visit functions ===== + + /** + Safe Visit Function will make sure this class is visited with safe mode. + These function will block all other functions if this class init failed. + Or, block any more operations if this class has loaded or saved once. In this time you only can free this class + */ + + public: + bool IsInitError() { + return m_IsInitError; + } + + private: + bool CanExecLoad() { + // no error, is loader, no prev load + return (!m_IsInitError && m_IsLoader && !m_HasLoaded); + } + bool CanExecSave() { + // no error, is saver, no prev save + return (!m_IsInitError && !m_IsLoader && !m_HasSaved); + } + bool CanExecLoaderVisitor() { + // no error, is loader, has loaded + return (!m_IsInitError && m_IsLoader && m_HasLoaded); + } + bool CanExecSaverVisitor() { + // no error, is saver, not saveed yet + // same as CanExecSave + return (!m_IsInitError && !m_IsLoader && !m_HasSaved); + } + + private: + /** + * @brief True if an error occurs when initializing this class. + */ + bool m_IsInitError; + /** + * @brief True if this class is a reader. + */ + bool m_IsLoader; + /** + * @brief True if this class has read. Only valid when this class is reader. + */ + bool m_HasLoaded; + /** + * @brief True if this class has written. Only valid when this class is writer. + */ + bool m_HasSaved; + + // ===== help functions ===== + + public: + bool Load(LibCmo::CKSTRING filename); + bool Save(LibCmo::CKSTRING filename, LibCmo::CKINT compress_level); + + LibCmo::CK2::ObjImpls::CKObject* GetObjectPtr(LibCmo::CK2::CK_ID objid) { + return m_Context->GetObject(objid);; + } + + // ===== visitors ===== + + private: + LibCmo::CKDWORD CommonGetObjectCount(std::vector& container) { + // only available in loader + if (!CanExecLoaderVisitor()) return 0; + return static_cast(container.size()); + } + LibCmo::CK2::CK_ID CommonGetObject(std::vector& container, LibCmo::CKDWORD idx) { + // only available in loader + if (!CanExecLoaderVisitor()) return 0; + return container[idx]; + } + LibCmo::CK2::CK_ID CommonCreateObject(std::vector& container, LibCmo::CK2::CK_CLASSID cid) { + // only available in saver + if (!CanExecSaverVisitor()) return 0; + + // try create object and get its pointer + LibCmo::CK2::ObjImpls::CKObject* obj = m_Context->CreateObject(cid, nullptr); + // check creation validation + if (obj == nullptr) return 0; + + // if success, write its id and emplace its id into list + LibCmo::CK2::CK_ID objid = obj->GetID(); + container.emplace_back(objid); + return objid; + } + public: + LibCmo::CKDWORD GetGroupCount(); + LibCmo::CK2::CK_ID GetGroup(LibCmo::CKDWORD idx); + LibCmo::CK2::CK_ID CreateGroup(); + LibCmo::CKDWORD Get3dObjectCount(); + LibCmo::CK2::CK_ID Get3dObject(LibCmo::CKDWORD idx); + LibCmo::CK2::CK_ID Create3dObject(); + LibCmo::CKDWORD GetMeshCount(); + LibCmo::CK2::CK_ID GetMesh(LibCmo::CKDWORD idx); + LibCmo::CK2::CK_ID CreateMesh(); + LibCmo::CKDWORD GetMaterialCount(); + LibCmo::CK2::CK_ID GetMaterial(LibCmo::CKDWORD idx); + LibCmo::CK2::CK_ID CreateMaterial(); + LibCmo::CKDWORD GetTextureCount(); + LibCmo::CK2::CK_ID GetTexture(LibCmo::CKDWORD idx); + LibCmo::CK2::CK_ID CreateTexture(); + + private: + LibCmo::CK2::CKContext* m_Context; + + std::vector m_ObjGroups; + std::vector m_Obj3dObjects; + std::vector m_ObjMeshs; + std::vector m_ObjMaterials; + std::vector m_ObjTextures; + + }; + class BMMeshTransition { private: struct TransitionVertex { @@ -47,11 +168,11 @@ namespace BMap { LibCmo::CKDWORD* PrepareFaceUVIndices(); LibCmo::CKDWORD* PrepareFaceMtlSlot(); - bool Parse(LibCmo::CK2::ObjImpls::CKMesh* write_into_mesh); + bool Parse(BMFile* bmfile, LibCmo::CK2::CK_ID mesh_id); private: void DoRealParse(); - void ApplyToMesh(LibCmo::CK2::ObjImpls::CKMesh* write_into_mesh); + void ApplyToMesh(BMFile* bmfile, LibCmo::CK2::ObjImpls::CKMesh* write_into_mesh); bool m_IsVertexOK, m_IsNormalOK, m_IsUVOK, m_IsFaceOK, m_IsMtlSlotOK; bool m_IsParsed; @@ -73,79 +194,4 @@ namespace BMap { std::map m_ProcDupRemover; }; - class BMFile { - public: - BMFile(LibCmo::CKSTRING temp_folder, LibCmo::CKSTRING texture_folder, LibCmo::CKDWORD encoding_count, LibCmo::CKSTRING* encodings, bool is_reader); - ~BMFile(); - LIBCMO_DISABLE_COPY_MOVE(BMFile); - - // ===== help functions ===== - - /** - * @brief Check whether this instance is freezed. - * @return True if freezed. This instance should be free immediately. - */ - bool IsFreezed(); - bool Load(LibCmo::CKSTRING filename); - bool Save(LibCmo::CKSTRING filename, LibCmo::CKINT compress_level); - LibCmo::CK2::ObjImpls::CKObject* GetObjectPtr(LibCmo::CK2::CK_ID objid); - - // ===== visitors ===== - - private: - LibCmo::CKDWORD CommonGetObjectCount(std::vector& container) { - if (m_IsFreezed || !m_IsReader) return 0; - return static_cast(container.size()); - } - LibCmo::CK2::CK_ID CommonGetObject(std::vector& container, LibCmo::CKDWORD idx) { - if (m_IsFreezed || !m_IsReader || idx >= container.size()) return 0; - return container[idx]; - } - LibCmo::CK2::CK_ID CommonCreateObject(std::vector& container, LibCmo::CK2::CK_CLASSID cid) { - // only available in writer - if (m_IsFreezed || m_IsReader) return 0; - - // try create object and get its pointer - LibCmo::CK2::ObjImpls::CKObject* obj = m_Context->CreateObject(cid, nullptr); - // check creation validation - if (obj == nullptr) return 0; - - // if success, write its id and emplace its id into list - LibCmo::CK2::CK_ID objid = obj->GetID(); - container.emplace_back(objid); - return objid; - } - public: - LibCmo::CKDWORD GetGroupCount(); - LibCmo::CK2::CK_ID GetGroup(LibCmo::CKDWORD idx); - LibCmo::CK2::CK_ID CreateGroup(); - LibCmo::CKDWORD Get3dObjectCount(); - LibCmo::CK2::CK_ID Get3dObject(LibCmo::CKDWORD idx); - LibCmo::CK2::CK_ID Create3dObject(); - LibCmo::CKDWORD GetMeshCount(); - LibCmo::CK2::CK_ID GetMesh(LibCmo::CKDWORD idx); - LibCmo::CK2::CK_ID CreateMesh(); - LibCmo::CKDWORD GetMaterialCount(); - LibCmo::CK2::CK_ID GetMaterial(LibCmo::CKDWORD idx); - LibCmo::CK2::CK_ID CreateMaterial(); - LibCmo::CKDWORD GetTextureCount(); - LibCmo::CK2::CK_ID GetTexture(LibCmo::CKDWORD idx); - LibCmo::CK2::CK_ID CreateTexture(); - - private: - /** - * @brief True if all operation of this instance should be rejected. - */ - bool m_IsFreezed; - bool m_IsReader; - LibCmo::CK2::CKContext* m_Context; - - std::vector m_ObjGroups; - std::vector m_Obj3dObjects; - std::vector m_ObjMeshs; - std::vector m_ObjMaterials; - std::vector m_ObjTextures; - - }; - } diff --git a/README.md b/README.md index ae4a89e..544cc1a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # libcmo21 -The Library for CMO File Read/Write. Also the Minimalist Virtools Environment. +The Library for CMO (also accept NMO, VMO and NMS) File Read/Write. Also the Minimalist Virtools Environment. +Write with one Library and Load Virtools File Everywhere. ## Status @@ -43,9 +44,9 @@ There are 3 lists which indicate our accept guideline. These features will be accepted as soon as possible. -* The bug fix of Virtools file reader. +* The bug fix of any existing code. * Class layout, `Load()` functions of following `CKObject` based classes. - - `CK3dEntity` + - None * Class layout, and `LoadData()` functions of following `CKBaseManager` based classes. - `CKAttributeManager` @@ -53,8 +54,8 @@ These features will be accepted as soon as possible. These features are in plan, but not urge to merge. -* The `CK_ID` remap system of Reader & Writer. -* Any Save functions. +* The `CK_ID` remap system of Reader. +* CK3dEntity hierarchy system. * Other CK classes implementations. * Non-Virtools 2.1 implementations.