write some shit for CKStateChunk

This commit is contained in:
yyc12345 2023-09-17 23:48:18 +08:00
parent 17333527ee
commit 50784e719c
3 changed files with 212 additions and 155 deletions

View File

@ -228,31 +228,35 @@ namespace LibCmo::CK2 {
/* /*
A small hint for ToBeNotify, CommonToBeNotify and ToNotify 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. 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. 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. Because B is a businessman, so his children BB also is a bussinessman.
This relation is represneted in CommonToBeNotify. 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. 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. 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. B want to know whom I can sell something to.
Because a full trades list are created from A side.
Because B is a bussiness man, as the children of B, let we assume BB also have capability to sell something. The better solution is just ask the guest: do you want to buy something from me?
He initially do not have any bussiness relation, because no one need to buy something from him. This operation will fill ToNotify and is implemented at ComputeHierarchyTable().
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.
At the end of this story, At the end of this story,
All bussiness man can use ToNofity to see whom they want to sell something to. 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. 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. 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 // set done
desc.Done = true; 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<size_t>(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<CKDWORD>(desc.Self))) {
// XContainer::NSXBitArray::Set(desc.ToNotify, static_cast<CKDWORD>(checking.Self));
// }
// }
// // set done
// desc.Done = true;
//}
static void CKBuildClassHierarchyTable() { static void CKBuildClassHierarchyTable() {
size_t classCount = g_CKClassInfo.size(); size_t classCount = g_CKClassInfo.size();

View File

@ -6,15 +6,6 @@ namespace LibCmo::CK2 {
#pragma region Ctor Dtor #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) : CKStateChunk::CKStateChunk(CKFileVisitor* visitor, CKContext* ctx) :
m_ClassId(CK_CLASSID::CKCID_OBJECT), m_DataDwSize(0u), m_pData(nullptr), 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_DataVersion(CK_STATECHUNK_DATAVERSION::CHUNKDATA_CURRENTVERSION), m_ChunkVersion(CK_STATECHUNK_CHUNKVERSION::CHUNK_VERSION4),
@ -110,8 +101,10 @@ namespace LibCmo::CK2 {
#pragma region Misc Funcs #pragma region Misc Funcs
const ChunkProfile CKStateChunk::GetStateChunkProfile() { // ========== Public Funcs ==========
return ChunkProfile {
const CKStateChunk::ProfileStateChunk_t CKStateChunk::GetStateChunkProfile() {
return CKStateChunk::ProfileStateChunk_t {
.m_ClassId = this->m_ClassId, .m_ClassId = this->m_ClassId,
.m_DataDwSize = this->m_DataDwSize, .m_DataDwSize = this->m_DataDwSize,
.m_pData = this->m_pData, .m_pData = this->m_pData,
@ -128,7 +121,40 @@ namespace LibCmo::CK2 {
}; };
} }
void CKStateChunk::Clear(void) { const XContainer::XArray<CKStateChunk::ProfileIdentifier_t> CKStateChunk::GetIdentifiersProfile() {
XContainer::XArray<CKStateChunk::ProfileIdentifier_t> 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(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_ClassId = CK_CLASSID::CKCID_OBJECT;
this->m_DataVersion = CK_STATECHUNK_DATAVERSION::CHUNK_DEV_2_1; this->m_DataVersion = CK_STATECHUNK_DATAVERSION::CHUNK_DEV_2_1;
this->m_ChunkVersion = CK_STATECHUNK_CHUNKVERSION::CHUNK_VERSION4; this->m_ChunkVersion = CK_STATECHUNK_CHUNKVERSION::CHUNK_VERSION4;
@ -149,11 +175,11 @@ namespace LibCmo::CK2 {
this->m_ChunkList.clear(); this->m_ChunkList.clear();
} }
CKDWORD CKStateChunk::GetDataSize(void) { CKDWORD CKStateChunk::GetDataSize() const {
return CKSizeof(CKDWORD) * this->m_DataDwSize; return CKSizeof(CKDWORD) * this->m_DataDwSize;
} }
CK_STATECHUNK_DATAVERSION CKStateChunk::GetDataVersion() { CK_STATECHUNK_DATAVERSION CKStateChunk::GetDataVersion() const {
return this->m_DataVersion; return this->m_DataVersion;
} }
@ -161,6 +187,14 @@ namespace LibCmo::CK2 {
this->m_DataVersion = version; 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 CKStateChunk::Skip(CKDWORD DwordCount) {
bool result; bool result;
switch (this->m_Parser.m_Status) { switch (this->m_Parser.m_Status) {
@ -183,18 +217,7 @@ namespace LibCmo::CK2 {
return result; return result;
} }
// ========== Private Funcs ==========
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;
}
}
CKDWORD CKStateChunk::GetCeilDwordSize(size_t char_size) { CKDWORD CKStateChunk::GetCeilDwordSize(size_t char_size) {
return static_cast<CKDWORD>((char_size + 3) >> 2); return static_cast<CKDWORD>((char_size + 3) >> 2);
@ -448,33 +471,6 @@ namespace LibCmo::CK2 {
#pragma endregion #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<Bytef*>(buffer), &destSize,
// reinterpret_cast<const Bytef*>(this->m_pData), static_cast<uLong>(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<CKDWORD>(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 #pragma region Read Functions
void CKStateChunk::StartRead(void) { void CKStateChunk::StartRead(void) {
@ -529,38 +525,6 @@ namespace LibCmo::CK2 {
return true; return true;
} }
const XContainer::XArray<IdentifierProfile> CKStateChunk::GetIdentifierProfile() {
XContainer::XArray<IdentifierProfile> 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 ==========*/ /* ========== Basic Data Read Functions ==========*/

View File

@ -6,25 +6,6 @@
namespace LibCmo::CK2 { 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 { class CKStateChunk {
public: public:
//CKStateChunk(); //CKStateChunk();
@ -35,6 +16,91 @@ namespace LibCmo::CK2 {
CKStateChunk& operator=(CKStateChunk&&); CKStateChunk& operator=(CKStateChunk&&);
~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<bool _TCanWrite>
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<void(const void*,CKDWORD)>;
LockedBuffer_t() : m_Ptr(nullptr), m_ConsumedByteSize(0), m_FreeFct(nullptr) {}
LockedBuffer_t(const void* ptr, CKDWORD expectedByteSize, FreeFct fct) :
m_Ptr(const_cast<CKBYTE*>(static_cast<const CKBYTE*>(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<bool _UCanWrite = _TCanWrite, std::enable_if_t<sizeof(_UCanWrite) && _TCanWrite, int> = 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: private:
enum class CKStateChunkStatus : CKDWORD { enum class CKStateChunkStatus : CKDWORD {
IDLE, IDLE,
@ -48,6 +114,10 @@ namespace LibCmo::CK2 {
CKDWORD m_PrevIdentifierPos; CKDWORD m_PrevIdentifierPos;
}; };
#pragma endregion
#pragma region Member Decl
CK_CLASSID m_ClassId; CK_CLASSID m_ClassId;
CKDWORD m_DataDwSize; CKDWORD m_DataDwSize;
CKDWORD* m_pData; CKDWORD* m_pData;
@ -64,6 +134,8 @@ namespace LibCmo::CK2 {
CKFileVisitor* m_BindFile; CKFileVisitor* m_BindFile;
CKContext* m_BindContext; CKContext* m_BindContext;
#pragma endregion
#pragma region Buffer Related #pragma region Buffer Related
public: public:
@ -75,25 +147,66 @@ namespace LibCmo::CK2 {
#pragma region Misc Functions #pragma region Misc Functions
public: public:
const ChunkProfile GetStateChunkProfile(); // ===== 2 Profiles Getter used by Unvirt =====
//bool UnPack(CKDWORD DestSize); /**
void Clear(void); * @brief Get basic info of this CKStateChunk.
CKDWORD GetDataSize(void); * @return The struct describing this CKStateChunk.
CK_STATECHUNK_DATAVERSION GetDataVersion(); */
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<ProfileIdentifier_t> 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); 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); bool Skip(CKDWORD DwordCount);
private: private:
CKDWORD GetCeilDwordSize(size_t char_size); CKDWORD GetCeilDwordSize(size_t char_size);
void* GetCurrentPointer();
bool ResizeBuffer(CKDWORD new_dwsize); bool ResizeBuffer(CKDWORD new_dwsize);
bool EnsureWriteSpace(CKDWORD dwsize); bool EnsureWriteSpace(CKDWORD dwsize);
bool EnsureReadSpace(CKDWORD dword_required); bool EnsureReadSpace(CKDWORD dword_required);
#pragma endregion #pragma endregion
#pragma region Read Function #pragma region Read Function
public: public:
@ -112,11 +225,14 @@ namespace LibCmo::CK2 {
inline bool SeekIdentifierAndReturnSize(TEnum enum_v, CKDWORD* out_size) { inline bool SeekIdentifierAndReturnSize(TEnum enum_v, CKDWORD* out_size) {
return SeekIdentifierDwordAndReturnSize(static_cast<CKDWORD>(enum_v), out_size); return SeekIdentifierDwordAndReturnSize(static_cast<CKDWORD>(enum_v), out_size);
} }
const XContainer::XArray<IdentifierProfile> GetIdentifierProfile();
/* ========== Basic Data Read Functions ==========*/ /* ========== Basic Data Read Functions ==========*/
private: private:
bool LockReadBuffer(void** ppData, CKDWORD expectedByteSize);
bool UnLockReadBuffer(CKDWORD realConsumedByteSize);
/** /**
* @brief The base read function for all data. * @brief The base read function for all data.
* This function will check all read requirements. * This function will check all read requirements.