diff --git a/LibCmo/CK2/CKGlobals.cpp b/LibCmo/CK2/CKGlobals.cpp index 04af4f9..f374428 100644 --- a/LibCmo/CK2/CKGlobals.cpp +++ b/LibCmo/CK2/CKGlobals.cpp @@ -228,31 +228,35 @@ namespace LibCmo::CK2 { /* A small hint for ToBeNotify, CommonToBeNotify and ToNotify - Assume that A need notification from B, we can see it as A want buy something from B. + Assume that A need notification from B, we can see it as that A want buy something from B. This relation is represented in ToBeNotify, a pure relation without any inhertance hierarchy. Ok, now we assume A have children AA, B also have children BB. - It's okey to order AA to buy something from B, becase AA is the children of A. - This relation is represneted in CommonToBeNotify. + Because B is a businessman, so his children BB also is a bussinessman. + B and BB have the same goods so A can buy his stuff from both of B and BB. + This is the first step executed by ComputeParentsNotifyTable(). + In this step, the function expand existing business relations to all possible business relations (expand to businessman's children) + + Image there is a candy store, C. Because AA still is a kids. + So AA want to buy something from C. Now C is in his ToBeNotify. + Additionally, A, the parent of AA, force AA to buy something from B, just for A himself. For AA, he does not need want to buy something from B, but his parent A order he to do. - So AA's ToBeNotify do not have B, but his CommonToBeNotify have B. + This is the second step executed by ComputeParentsNotifyTable(). + For AA, his parent's relations also need to be merged after he processed his relations with C like I introduced previously. + + Now, AA have a full business list writing all trades he can do. + This is represented as CommonToBeNotify. + In this time, AA's ToBeNotify only have C, but his CommonToBeNotify have B, BB and C. So now, let we change the view from A to B. - B now can sell something to A or AA. This represent in B's ToNotify. - - Because B is a bussiness man, as the children of B, let we assume BB also have capability to sell something. - He initially do not have any bussiness relation, because no one need to buy something from him. - But he is smart, he want to use his parent bussiness relation. He copied his parent relation, B's ToNotify. - Now BB can trade with A and AA, this represent in his ToNotify. - - Now A and AA know that BB is children of B and B is their bussiness friend. - So they also can buy something from BB. This result in that BB is added into their CommonToBeNotify. - + B want to know whom I can sell something to. + Because a full trades list are created from A side. + The better solution is just ask the guest: do you want to buy something from me? + This operation will fill ToNotify and is implemented at ComputeHierarchyTable(). + At the end of this story, All bussiness man can use ToNofity to see whom they want to sell something to. - ToNofity list have A and all of his children. And all buyer can use CommonToBeNofity to check who they can buy something from. - CommonToBeNotify have B and all of this children. ToBeNotify is just a list to indicate the initial bussiness relation and will never used anymore after real relation established. */ @@ -308,33 +312,6 @@ namespace LibCmo::CK2 { // set done desc.Done = true; } - //static void ComputeParentsNotifyTable(CKClassDesc& desc) { - // // if it has done, do not process it again. - // if (desc.Done) return; - // - // // find direct parent - // CKClassDesc& parent = g_CKClassInfo[static_cast(desc.Parent)]; - // if (!parent.IsValid) LIBPANIC("No such CK_CLASSID."); - - // // if it is not self inheritance, call recursively - // if (desc.Self != desc.Parent) { - // ComputeParentsNotifyTable(parent); - // } - // - // // copy parent ToNotify list - // desc.ToNotify = parent.ToNotify; - // // iterate all desc to know which id need beNotify me - // // and add them - // for (const auto& checking : g_CKClassInfo) { - // if (!checking.IsValid) continue; - // if (XContainer::NSXBitArray::IsSet(checking.CommonToBeNotify, static_cast(desc.Self))) { - // XContainer::NSXBitArray::Set(desc.ToNotify, static_cast(checking.Self)); - // } - // } - - // // set done - // desc.Done = true; - //} static void CKBuildClassHierarchyTable() { size_t classCount = g_CKClassInfo.size(); diff --git a/LibCmo/CK2/CKStateChunk.cpp b/LibCmo/CK2/CKStateChunk.cpp index d159185..560aa2c 100644 --- a/LibCmo/CK2/CKStateChunk.cpp +++ b/LibCmo/CK2/CKStateChunk.cpp @@ -6,15 +6,6 @@ namespace LibCmo::CK2 { #pragma region Ctor Dtor - //CKStateChunk::CKStateChunk() : - // m_ClassId(CK_CLASSID::CKCID_OBJECT), m_DataDwSize(0u), m_pData(nullptr), - // m_DataVersion(CK_STATECHUNK_DATAVERSION::CHUNKDATA_CURRENTVERSION), m_ChunkVersion(CK_STATECHUNK_CHUNKVERSION::CHUNK_VERSION4), - // m_Parser{ CKStateChunkStatus::IDLE, 0u, 0u, 0u }, - // m_ObjectList(), m_ChunkList(), m_ManagerList(), - // m_BindDoc(nullptr), m_BindContext(nullptr) - //{ - // ; - //} CKStateChunk::CKStateChunk(CKFileVisitor* visitor, CKContext* ctx) : m_ClassId(CK_CLASSID::CKCID_OBJECT), m_DataDwSize(0u), m_pData(nullptr), m_DataVersion(CK_STATECHUNK_DATAVERSION::CHUNKDATA_CURRENTVERSION), m_ChunkVersion(CK_STATECHUNK_CHUNKVERSION::CHUNK_VERSION4), @@ -110,8 +101,10 @@ namespace LibCmo::CK2 { #pragma region Misc Funcs - const ChunkProfile CKStateChunk::GetStateChunkProfile() { - return ChunkProfile { + // ========== Public Funcs ========== + + const CKStateChunk::ProfileStateChunk_t CKStateChunk::GetStateChunkProfile() { + return CKStateChunk::ProfileStateChunk_t { .m_ClassId = this->m_ClassId, .m_DataDwSize = this->m_DataDwSize, .m_pData = this->m_pData, @@ -127,8 +120,41 @@ namespace LibCmo::CK2 { .m_BindContext = this->m_BindContext, }; } + + const XContainer::XArray CKStateChunk::GetIdentifiersProfile() { + XContainer::XArray collection; + if (this->m_Parser.m_Status != CKStateChunkStatus::READ) return collection; - void CKStateChunk::Clear(void) { + CKDWORD pos = 0u; + if (this->m_DataDwSize < 2u) return collection; // impossible to have a identifier + + // iterate identifier + while (true) { + // add current identifier + CKDWORD nextptr = this->m_pData[pos + 1]; + if (nextptr == 0u) { + nextptr = this->m_DataDwSize; // got tail. no more identifier + } + collection.emplace_back(CKStateChunk::ProfileIdentifier_t{ + this->m_pData[pos], + this->m_pData + pos + 2, + CKSizeof(CKDWORD) * (nextptr - pos - 2u) + }); + + // move to next identifier or exit + // got tail. no more identifier + if (this->m_pData[pos + 1] == 0u) break; + + pos = this->m_pData[pos + 1]; + + // out of buffer + if (pos + 1 >= this->m_DataDwSize) break; + }; + return collection; + + } + + void CKStateChunk::Clear() { this->m_ClassId = CK_CLASSID::CKCID_OBJECT; this->m_DataVersion = CK_STATECHUNK_DATAVERSION::CHUNK_DEV_2_1; this->m_ChunkVersion = CK_STATECHUNK_CHUNKVERSION::CHUNK_VERSION4; @@ -149,11 +175,11 @@ namespace LibCmo::CK2 { this->m_ChunkList.clear(); } - CKDWORD CKStateChunk::GetDataSize(void) { + CKDWORD CKStateChunk::GetDataSize() const { return CKSizeof(CKDWORD) * this->m_DataDwSize; } - CK_STATECHUNK_DATAVERSION CKStateChunk::GetDataVersion() { + CK_STATECHUNK_DATAVERSION CKStateChunk::GetDataVersion() const { return this->m_DataVersion; } @@ -161,6 +187,14 @@ namespace LibCmo::CK2 { this->m_DataVersion = version; } + CK_CLASSID CKStateChunk::GetClassId() const { + return this->m_ClassId; + } + + void CKStateChunk::SetClassId(CK_CLASSID cid) { + this->m_ClassId = cid; + } + bool CKStateChunk::Skip(CKDWORD DwordCount) { bool result; switch (this->m_Parser.m_Status) { @@ -183,18 +217,7 @@ namespace LibCmo::CK2 { return result; } - - - void* CKStateChunk::GetCurrentPointer() { - switch (this->m_Parser.m_Status) { - case CKStateChunkStatus::READ: - case CKStateChunkStatus::WRITE: - return this->m_pData + this->m_Parser.m_CurrentPos; - case CKStateChunkStatus::IDLE: - default: - return nullptr; - } - } + // ========== Private Funcs ========== CKDWORD CKStateChunk::GetCeilDwordSize(size_t char_size) { return static_cast((char_size + 3) >> 2); @@ -448,33 +471,6 @@ namespace LibCmo::CK2 { #pragma endregion - - //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 - // CKBYTE* buffer = new CKBYTE[DestSize]; - // 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) / CKSizeof(CKDWORD); - - // delete[] this->m_pData; - // this->m_pData = nullptr; - // this->m_pData = new CKDWORD[this->m_DataDwSize]; - // std::memcpy(this->m_pData, buffer, this->m_DataDwSize * sizeof(CKDWORD)); - // } - // delete[] buffer; - // return true; - //} - #pragma region Read Functions void CKStateChunk::StartRead(void) { @@ -529,38 +525,6 @@ namespace LibCmo::CK2 { return true; } - const XContainer::XArray CKStateChunk::GetIdentifierProfile() { - XContainer::XArray collection; - if (this->m_Parser.m_Status != CKStateChunkStatus::READ) return collection; - - CKDWORD pos = 0u; - if (this->m_DataDwSize < 2u) return collection; // impossible to have a identifier - - // iterate identifier - while (true) { - // add current identifier - CKDWORD nextptr = this->m_pData[pos + 1]; - if (nextptr == 0u) { - nextptr = this->m_DataDwSize; // got tail. no more identifier - } - collection.emplace_back(IdentifierProfile{ - this->m_pData[pos], - this->m_pData + pos + 2, - CKSizeof(CKDWORD) * (nextptr - pos - 2u) - }); - - // move to next identifier or exit - // got tail. no more identifier - if (this->m_pData[pos + 1] == 0u) break; - - pos = this->m_pData[pos + 1]; - - // out of buffer - if (pos + 1 >= this->m_DataDwSize) break; - }; - return collection; - - } /* ========== Basic Data Read Functions ==========*/ diff --git a/LibCmo/CK2/CKStateChunk.hpp b/LibCmo/CK2/CKStateChunk.hpp index d6bfe78..b6352a4 100644 --- a/LibCmo/CK2/CKStateChunk.hpp +++ b/LibCmo/CK2/CKStateChunk.hpp @@ -6,25 +6,6 @@ namespace LibCmo::CK2 { - struct ChunkProfile { - CK_CLASSID m_ClassId; - CKDWORD m_DataDwSize; - CKDWORD* m_pData; - - CK_STATECHUNK_DATAVERSION m_DataVersion; - CK_STATECHUNK_CHUNKVERSION m_ChunkVersion; - - size_t m_ObjectListSize, m_ChunkListSize, m_ManagerListSize; - - CKFileVisitor* m_BindFile; - CKContext* m_BindContext; - }; - struct IdentifierProfile { - CKDWORD m_Identifier; - void* m_DataPtr; - CKDWORD m_AreaSize; - }; - class CKStateChunk { public: //CKStateChunk(); @@ -35,6 +16,91 @@ namespace LibCmo::CK2 { CKStateChunk& operator=(CKStateChunk&&); ~CKStateChunk(); +#pragma region Self Used Data Struct + + public: + struct ProfileStateChunk_t { + CK_CLASSID m_ClassId; + CKDWORD m_DataDwSize; + CKDWORD* m_pData; + + CK_STATECHUNK_DATAVERSION m_DataVersion; + CK_STATECHUNK_CHUNKVERSION m_ChunkVersion; + + size_t m_ObjectListSize, m_ChunkListSize, m_ManagerListSize; + + CKFileVisitor* m_BindFile; + CKContext* m_BindContext; + }; + struct ProfileIdentifier_t { + CKDWORD m_Identifier; + void* m_DataPtr; + CKDWORD m_AreaSize; + }; + template + class LockedBuffer_t { + public: + /** + * @brief The type of free function called by this class. + * It must have 2 argument, first one is buffer need to be free, second is consumed size. + */ + using FreeFct = std::function; + + LockedBuffer_t() : m_Ptr(nullptr), m_ConsumedByteSize(0), m_FreeFct(nullptr) {} + LockedBuffer_t(const void* ptr, CKDWORD expectedByteSize, FreeFct fct) : + m_Ptr(const_cast(static_cast(ptr))), m_ConsumedByteSize(expectedByteSize), m_FreeFct(fct) {} + LockedBuffer_t(const LockedBuffer_t&) = delete; + LockedBuffer_t& operator=(const LockedBuffer_t&) = delete; + LockedBuffer_t(LockedBuffer_t&& rhs) : + m_Ptr(rhs.m_Ptr), m_ConsumedByteSize(rhs.m_ConsumedByteSize), m_FreeFct(rhs.m_FreeFct) { + rhs.m_Ptr = nullptr; + rhs.m_ConsumedByteSize = 0; + rhs.m_FreeFct = nullptr; + } + LockedBuffer_t& operator=(LockedBuffer_t&& rhs) { + // reset self + Reset(); + // copy data + m_Ptr = rhs.m_Ptr; + m_ConsumedByteSize = rhs.m_ConsumedByteSize; + m_FreeFct = rhs.m_FreeFct; + // remove rhs data. + rhs.m_Ptr = nullptr; + rhs.m_ConsumedByteSize = 0; + rhs.m_FreeFct = nullptr; + } + ~LockedBuffer_t() { + Reset(); + } + + const void* GetPtr() const { + return m_Ptr; + } + // References: + // https://stackoverflow.com/questions/43051882/how-to-disable-a-class-member-function-for-certain-template-types + // https://stackoverflow.com/questions/13964447/why-compile-error-with-enable-if + template = 0> + void* GetMutablePtr() { + return m_Ptr; + } + void SetRealConsumedSize(CKDWORD sizeInByte) { + m_ConsumedByteSize = sizeInByte; + } + void Reset() { + if (m_Ptr == nullptr) return; + if (m_FreeFct == nullptr) return; + m_FreeFct(m_Ptr, m_ConsumedByteSize); + + m_Ptr = nullptr; + m_ConsumedByteSize = 0; + m_FreeFct = nullptr; + } + private: + CKBYTE* m_Ptr; + CKDWORD m_ConsumedByteSize; + FreeFct m_FreeFct; + }; + private: enum class CKStateChunkStatus : CKDWORD { IDLE, @@ -48,6 +114,10 @@ namespace LibCmo::CK2 { CKDWORD m_PrevIdentifierPos; }; +#pragma endregion + +#pragma region Member Decl + CK_CLASSID m_ClassId; CKDWORD m_DataDwSize; CKDWORD* m_pData; @@ -64,6 +134,8 @@ namespace LibCmo::CK2 { CKFileVisitor* m_BindFile; CKContext* m_BindContext; +#pragma endregion + #pragma region Buffer Related public: @@ -75,25 +147,66 @@ namespace LibCmo::CK2 { #pragma region Misc Functions public: - const ChunkProfile GetStateChunkProfile(); + // ===== 2 Profiles Getter used by Unvirt ===== - //bool UnPack(CKDWORD DestSize); - void Clear(void); - CKDWORD GetDataSize(void); - CK_STATECHUNK_DATAVERSION GetDataVersion(); + /** + * @brief Get basic info of this CKStateChunk. + * @return The struct describing this CKStateChunk. + */ + const ProfileStateChunk_t GetStateChunkProfile(); + /** + * @brief Get all indentifier infos of this CKStateChunk, + * including identifier self, data area size and address. + * @return A arrary, each item describe a single identifier's info. + * @remark The detail of implement can be seen in SeekIdentifierAndReturnSize() + * @see SeekIdentifierAndReturnSize + */ + const XContainer::XArray GetIdentifiersProfile(); + + /** + * @brief Clear this CKStateChunk, restore it to original status for other using. + */ + void Clear(); + /** + * @brief Get the size of data buffer in this CKStateChunk. + * @return The data buffer's size in CKBYTE. + */ + CKDWORD GetDataSize() const; + /** + * @brief Get data version in this CKStateChunk. + * @remark Data version is frequently used by calling of CKStateChunk to distinguish different data layout due to compatibility reason. + * @return The data version. + */ + CK_STATECHUNK_DATAVERSION GetDataVersion() const; + /** + * @brief Set data version of this CKStateChunk. + * @param version The set data version + * @remark Frequently used when saving someing in CKStateChunk. + */ void SetDataVersion(CK_STATECHUNK_DATAVERSION version); + /** + * @brief Get associated CK_CLASSID of this CKStateChunk + * @return Associated CK_CLASSID + */ + CK_CLASSID GetClassId() const; + /** + * @brief Set associated CK_CLASSID of this CKStateChunk. + * @param cid The set CK_CLASSID + * @remark Frequently used at the end of CKObject::Save() override. + */ + void SetClassId(CK_CLASSID cid); + + bool Skip(CKDWORD DwordCount); private: CKDWORD GetCeilDwordSize(size_t char_size); - void* GetCurrentPointer(); bool ResizeBuffer(CKDWORD new_dwsize); bool EnsureWriteSpace(CKDWORD dwsize); bool EnsureReadSpace(CKDWORD dword_required); #pragma endregion - #pragma region Read Function public: @@ -112,11 +225,14 @@ namespace LibCmo::CK2 { inline bool SeekIdentifierAndReturnSize(TEnum enum_v, CKDWORD* out_size) { return SeekIdentifierDwordAndReturnSize(static_cast(enum_v), out_size); } - const XContainer::XArray GetIdentifierProfile(); /* ========== Basic Data Read Functions ==========*/ private: + + bool LockReadBuffer(void** ppData, CKDWORD expectedByteSize); + bool UnLockReadBuffer(CKDWORD realConsumedByteSize); + /** * @brief The base read function for all data. * This function will check all read requirements.