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
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<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() {
size_t classCount = g_CKClassInfo.size();

View File

@ -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,
@ -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_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<CKDWORD>((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<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
void CKStateChunk::StartRead(void) {
@ -529,38 +525,6 @@ namespace LibCmo::CK2 {
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 ==========*/

View File

@ -6,7 +6,20 @@
namespace LibCmo::CK2 {
struct ChunkProfile {
class CKStateChunk {
public:
//CKStateChunk();
CKStateChunk(CKFileVisitor* visitor, CKContext* ctx);
CKStateChunk(const CKStateChunk&);
CKStateChunk(CKStateChunk&&);
CKStateChunk& operator=(const CKStateChunk&);
CKStateChunk& operator=(CKStateChunk&&);
~CKStateChunk();
#pragma region Self Used Data Struct
public:
struct ProfileStateChunk_t {
CK_CLASSID m_ClassId;
CKDWORD m_DataDwSize;
CKDWORD* m_pData;
@ -19,21 +32,74 @@ namespace LibCmo::CK2 {
CKFileVisitor* m_BindFile;
CKContext* m_BindContext;
};
struct IdentifierProfile {
struct ProfileIdentifier_t {
CKDWORD m_Identifier;
void* m_DataPtr;
CKDWORD m_AreaSize;
};
class CKStateChunk {
template<bool _TCanWrite>
class LockedBuffer_t {
public:
//CKStateChunk();
CKStateChunk(CKFileVisitor* visitor, CKContext* ctx);
CKStateChunk(const CKStateChunk&);
CKStateChunk(CKStateChunk&&);
CKStateChunk& operator=(const CKStateChunk&);
CKStateChunk& operator=(CKStateChunk&&);
~CKStateChunk();
/**
* @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:
enum class CKStateChunkStatus : CKDWORD {
@ -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<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);
/**
* @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<CKDWORD>(enum_v), out_size);
}
const XContainer::XArray<IdentifierProfile> 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.