libcmo21/LibCmo/CK2/CKGlobals.cpp

460 lines
15 KiB
C++
Raw Normal View History

2023-08-23 16:04:58 +08:00
#include "../VTUtils.hpp"
2023-02-25 17:39:39 +08:00
#if defined(LIBCMO_OS_WIN32)
#define ZLIB_WINAPI
#endif
#include <zconf.h>
2023-09-16 18:31:25 +08:00
#include <zlib.h>
2023-02-25 17:39:39 +08:00
#include "CKGlobals.hpp"
2023-09-16 18:31:25 +08:00
2023-08-23 16:04:58 +08:00
#include <algorithm>
#include <initializer_list>
2023-02-25 17:39:39 +08:00
#include "ObjImpls/CKObject.hpp"
2023-09-01 12:19:06 +08:00
#include "ObjImpls/CKSceneObject.hpp"
#include "ObjImpls/CKBeObject.hpp"
#include "ObjImpls/CKGroup.hpp"
2023-09-01 14:55:31 +08:00
#include "ObjImpls/CKRenderObject.hpp"
#include "ObjImpls/CK3dEntity.hpp"
#include "ObjImpls/CK3dObject.hpp"
2023-09-12 17:03:06 +08:00
#include "ObjImpls/CKTexture.hpp"
2023-09-14 22:08:40 +08:00
#include "ObjImpls/CKMaterial.hpp"
2023-08-25 21:57:22 +08:00
2023-02-26 21:48:03 +08:00
namespace LibCmo::CK2 {
2023-08-23 16:04:58 +08:00
#pragma region Compression utilities
2023-08-28 21:21:40 +08:00
void* CKPackData(const void* Data, CKDWORD size, CKDWORD& NewSize, CKINT compressionlevel) {
2023-08-22 15:30:26 +08:00
uLong boundary = compressBound(static_cast<uLong>(size));
2023-09-17 10:38:46 +08:00
CKBYTE* DestBuffer = new CKBYTE[boundary];
2023-08-22 15:30:26 +08:00
uLongf _destLen = static_cast<uLongf>(boundary);
if (compress2(
reinterpret_cast<Bytef*>(DestBuffer), &_destLen,
reinterpret_cast<const Bytef*>(Data), static_cast<uLong>(size),
static_cast<int>(compressionlevel)) != Z_OK) {
NewSize = 0;
delete[] DestBuffer;
return nullptr;
}
2023-08-28 21:21:40 +08:00
NewSize = static_cast<CKDWORD>(_destLen);
2023-08-22 15:30:26 +08:00
return DestBuffer;
}
2023-02-25 17:39:39 +08:00
2023-08-28 21:21:40 +08:00
void* CKUnPackData(CKDWORD DestSize, const void* SrcBuffer, CKDWORD SrcSize) {
2023-09-17 10:38:46 +08:00
CKBYTE* DestBuffer = new CKBYTE[DestSize];
2023-02-25 17:39:39 +08:00
uLongf cache = DestSize;
if (uncompress(
reinterpret_cast<Bytef*>(DestBuffer), &cache,
2023-08-28 21:21:40 +08:00
reinterpret_cast<const Bytef*>(SrcBuffer), static_cast<uLong>(SrcSize)) != Z_OK) {
2023-02-25 17:39:39 +08:00
delete[] DestBuffer;
return nullptr;
}
return DestBuffer;
}
CKDWORD CKComputeDataCRC(const void* data, CKDWORD size, CKDWORD PreviousCRC) {
2023-02-25 17:39:39 +08:00
return static_cast<CKDWORD>(adler32(
static_cast<uLong>(PreviousCRC),
reinterpret_cast<const Bytef*>(data),
static_cast<uInt>(size)
));
}
2023-08-23 16:04:58 +08:00
#pragma endregion
2023-09-04 22:58:53 +08:00
#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;
}
}
}
bool CKStrEqualI(CKSTRING str1, CKSTRING str2) {
if (str1 == nullptr) {
if (str2 == nullptr) return true;
else return false;
} else {
if (str2 == nullptr) return false;
else {
// do real cmp
size_t i = 0;
while (str1[i] != '\0' && str2[i] != '\0') {
if (std::tolower(str1[i]) != std::tolower(str2[i])) return false;
++str1;
++str2;
}
// !XOR the result, if both of them is zero, return true(1)
return !((str1[i] != '\0') ^ (str2[i] != '\0'));
}
}
}
bool CKStrEmpty(CKSTRING strl) {
if (strl == nullptr) return true;
return strl[0] == '\0';
}
2023-09-04 22:58:53 +08:00
#pragma endregion
2023-08-23 16:04:58 +08:00
#pragma region CKClass Registration
static XContainer::XArray<CKClassDesc> g_CKClassInfo;
static bool GetClassIdIndex(CK_CLASSID cid, size_t& intcid) {
intcid = static_cast<size_t>(cid);
if (intcid >= g_CKClassInfo.size()) return false;
if (!g_CKClassInfo[intcid].IsValid) return false;
return true;
}
void CKClassNeedNotificationFrom(CK_CLASSID listener, CK_CLASSID listenTo) {
size_t idxListener, idxListenTo;
if (!GetClassIdIndex(listener, idxListener) || !GetClassIdIndex(listenTo, idxListenTo)) return;
XContainer::NSXBitArray::Set(g_CKClassInfo[idxListener].ToBeNotify, static_cast<CKDWORD>(idxListenTo));
}
2023-09-01 12:19:06 +08:00
CK_CLASSID CKClassGetNewIdentifier() {
size_t classsize = g_CKClassInfo.size();
if (classsize < static_cast<size_t>(CK_CLASSID::CKCID_MAXCLASSID)) {
return CK_CLASSID::CKCID_MAXCLASSID;
} else {
return static_cast<CK_CLASSID>(classsize);
}
}
2023-08-23 16:04:58 +08:00
void CKClassRegister(CK_CLASSID cid, CK_CLASSID parentCid,
CKClassRegisterFct regFct, CKClassCreationFct createFct, CKClassReleaseFct relFct, CKClassNameFct nameFct) {
2023-08-23 16:04:58 +08:00
2023-09-01 12:19:06 +08:00
// resize class desc array
size_t intcid = static_cast<size_t>(cid);
if (intcid >= g_CKClassInfo.size()) {
g_CKClassInfo.resize(intcid + 1);
}
// emplace desc
2023-08-23 16:04:58 +08:00
CKClassDesc desc;
2023-09-01 12:19:06 +08:00
desc.IsValid = true;
2023-08-23 16:04:58 +08:00
desc.Self = cid;
desc.Parent = parentCid;
desc.RegisterFct = regFct;
2023-08-23 16:04:58 +08:00
desc.CreationFct = createFct;
desc.ReleaseFct = relFct;
desc.NameFct = nameFct;
2023-09-01 12:19:06 +08:00
g_CKClassInfo[intcid] = std::move(desc);
2023-08-23 16:04:58 +08:00
}
#pragma endregion
#pragma region Class Hierarchy Management
2023-09-01 12:19:06 +08:00
CKDWORD CKGetClassCount() {
return static_cast<CKDWORD>(g_CKClassInfo.size());
2023-08-23 16:04:58 +08:00
}
const CKClassDesc* CKGetClassDesc(CK_CLASSID cid) {
2023-09-01 12:19:06 +08:00
size_t intcid;
if (!GetClassIdIndex(cid, intcid)) return nullptr;
return &g_CKClassInfo[intcid];
2023-08-23 16:04:58 +08:00
}
CKSTRING CKClassIDToString(CK_CLASSID cid) {
2023-09-01 12:19:06 +08:00
const CKClassDesc* desc = CKGetClassDesc(cid);
if (desc == nullptr) return "Undefined Type";
else return desc->NameFct();
2023-08-23 16:04:58 +08:00
}
bool CKIsChildClassOf(CK_CLASSID child, CK_CLASSID parent) {
2023-09-01 12:19:06 +08:00
size_t intchild, intparent;
if (!GetClassIdIndex(child, intchild) || !GetClassIdIndex(parent, intparent)) return false;
return g_CKClassInfo[intchild].Parents[intparent];
2023-08-23 16:04:58 +08:00
}
CK_CLASSID CKGetParentClassID(CK_CLASSID child) {
2023-09-01 12:19:06 +08:00
const CKClassDesc* desc = CKGetClassDesc(child);
if (desc == nullptr) return CK_CLASSID::CKCID_OBJECT;
return desc->Parent;
2023-08-23 16:04:58 +08:00
}
CK_CLASSID CKGetCommonParent(CK_CLASSID cid1, CK_CLASSID cid2) {
// I don't know algorithm, I just copy the decompiled code.
while (true) {
if (CKIsChildClassOf(cid1, cid2)) return cid2;
if (CKIsChildClassOf(cid2, cid1)) break;
cid2 = CKGetParentClassID(cid1);
cid1 = cid2;
}
return cid1;
}
bool CKIsNeedNotify(CK_CLASSID listener, CK_CLASSID deletedObjCid) {
const CKClassDesc* desc = CKGetClassDesc(listener);
if (desc == nullptr) return false;
return XContainer::NSXBitArray::IsSet(desc->CommonToBeNotify, static_cast<CKDWORD>(deletedObjCid));
}
XContainer::XBitArray CKGetAllNotifyClassID(const XContainer::XBitArray& delObjCids) {
XContainer::XBitArray result;
for (size_t i = 0; i < delObjCids.size(); ++i) {
if (!XContainer::NSXBitArray::IsSet(delObjCids, static_cast<CKDWORD>(i))) continue;
const CKClassDesc* desc = CKGetClassDesc(static_cast<CK_CLASSID>(i));
if (desc == nullptr) continue;
XContainer::NSXBitArray::Or(result, desc->ToNotify);
}
return result;
}
2023-08-23 16:04:58 +08:00
#pragma endregion
#pragma region Initializations functions
/*
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.
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.
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.
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.
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.
*/
static void ComputeParentsTable(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.");
2023-09-01 12:19:06 +08:00
// if it is not self inheritance, call recursively
if (desc.Self != desc.Parent) {
ComputeParentsTable(parent);
}
// copy parent's parents
desc.Parents = parent.Parents;
// and set self as its parent
XContainer::NSXBitArray::Set(desc.Parents, static_cast<CKDWORD>(desc.Self));
// set derivation level
desc.DerivationLevel = parent.DerivationLevel + 1;
// 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);
}
// add all children of ToBeNofity list
for (CKDWORD idx = 0; idx < desc.ToBeNotify.size(); ++idx) {
if (!XContainer::NSXBitArray::IsSet(desc.ToBeNotify, idx))
continue;
CKClassDesc& target = g_CKClassInfo[idx];
if (!target.IsValid) continue;
XContainer::NSXBitArray::Or(desc.CommonToBeNotify, target.Children);
}
// and merge parent notify list
XContainer::NSXBitArray::Or(desc.CommonToBeNotify, parent.CommonToBeNotify);
// 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();
// ===== Build Inhertance Hierarchy =====
// set Done to false and resize inhertance XBitArray
for (auto& item : g_CKClassInfo) {
if (!item.IsValid) continue;
item.Done = false;
XContainer::NSXBitArray::Resize(item.Parents, static_cast<CKDWORD>(classCount));
XContainer::NSXBitArray::Resize(item.Children, static_cast<CKDWORD>(classCount));
}
// compute parents
for (auto& item : g_CKClassInfo) {
if (!item.IsValid) continue;
ComputeParentsTable(item);
}
// compute children by parents table
// iterate CKClassDesc::Parents and register it self to gotten parents
for (auto& item : g_CKClassInfo) {
if (!item.IsValid) continue;
for (size_t idx = 0; idx < classCount; ++idx) {
CKClassDesc& checking = g_CKClassInfo[idx];
if (!checking.IsValid) continue;
// if this idx is its parent,
// add self to parent.
if (XContainer::NSXBitArray::IsSet(item.Parents, static_cast<CKDWORD>(idx))) {
XContainer::NSXBitArray::Set(checking.Children, static_cast<CKDWORD>(item.Self));
}
}
}
// ===== Register =====
// run register
for (auto& item : g_CKClassInfo) {
if (!item.IsValid || item.RegisterFct == nullptr) continue;
item.RegisterFct();
}
// ===== Build Notify Hierarchy =====
// set array first
for (auto& item : g_CKClassInfo) {
if (!item.IsValid) continue;
item.Done = false;
XContainer::NSXBitArray::Resize(item.ToBeNotify, static_cast<CKDWORD>(classCount));
XContainer::NSXBitArray::Resize(item.CommonToBeNotify, static_cast<CKDWORD>(classCount));
XContainer::NSXBitArray::Resize(item.ToNotify, static_cast<CKDWORD>(classCount));
}
// compute CommonToBeNotify
for (auto& item : g_CKClassInfo) {
if (!item.IsValid) continue;
ComputeParentsNotifyTable(item);
}
// compute ToNotify
for (CKDWORD idx = 0; idx < classCount; ++idx) {
CKClassDesc& thisDesc = g_CKClassInfo[idx];
if (!thisDesc.IsValid) continue;
for (auto& checking : g_CKClassInfo) {
if (!checking.IsValid) continue;
// if checkingDesc order me, add it.
if (XContainer::NSXBitArray::IsSet(checking.CommonToBeNotify, idx)) {
XContainer::NSXBitArray::Set(thisDesc.ToNotify, static_cast<CKDWORD>(checking.Self));
}
}
}
}
static void NeedNotificationWrapper(CK_CLASSID thiscid, std::initializer_list<CK_CLASSID> il) {
for (auto it = il.begin(); it != il.end(); ++it) {
CKClassNeedNotificationFrom(thiscid, *it);
}
}
CKERROR CKStartUp() {
2023-09-01 12:19:06 +08:00
// reserve class info array.
g_CKClassInfo.reserve(static_cast<size_t>(CK_CLASSID::CKCID_MAXCLASSID));
2023-08-23 16:04:58 +08:00
// todo: add class type registrations
2023-09-01 12:19:06 +08:00
#define EasyClassReg(clsname, cid, parentCid, strName) \
CKClassRegister(cid, parentCid, \
nullptr, \
[](CKContext* ctx, CK_ID id, CKSTRING name) -> ObjImpls::CKObject* { return new clsname(ctx, id, name); }, \
[](CKContext* ctx, ObjImpls::CKObject* obj) -> void { delete obj; }, \
[]() -> CKSTRING { return strName; });
#define EasyClassRegWithNotify(clsname, cid, parentCid, strName, notifyCids) \
CKClassRegister(cid, parentCid, \
[]() -> void { NeedNotificationWrapper(cid, notifyCids); }, \
2023-09-01 12:19:06 +08:00
[](CKContext* ctx, CK_ID id, CKSTRING name) -> ObjImpls::CKObject* { return new clsname(ctx, id, name); }, \
[](CKContext* ctx, ObjImpls::CKObject* obj) -> void { delete obj; }, \
[]() -> CKSTRING { return strName; });
EasyClassReg(ObjImpls::CKObject, CK_CLASSID::CKCID_OBJECT, CK_CLASSID::CKCID_OBJECT, "Basic Object");
EasyClassReg(ObjImpls::CKSceneObject, CK_CLASSID::CKCID_SCENEOBJECT, CK_CLASSID::CKCID_OBJECT, "Scene Object");
EasyClassReg(ObjImpls::CKBeObject, CK_CLASSID::CKCID_BEOBJECT, CK_CLASSID::CKCID_SCENEOBJECT, "Behavioral Object");
EasyClassRegWithNotify(ObjImpls::CKGroup, CK_CLASSID::CKCID_GROUP, CK_CLASSID::CKCID_BEOBJECT, "Group", { CK_CLASSID::CKCID_BEOBJECT });
2023-09-01 14:55:31 +08:00
EasyClassReg(ObjImpls::CKRenderObject, CK_CLASSID::CKCID_RENDEROBJECT, CK_CLASSID::CKCID_BEOBJECT, "Render Object");
EasyClassReg(ObjImpls::CK3dEntity, CK_CLASSID::CKCID_3DENTITY, CK_CLASSID::CKCID_RENDEROBJECT, "3D Entity");
EasyClassReg(ObjImpls::CK3dObject, CK_CLASSID::CKCID_3DOBJECT, CK_CLASSID::CKCID_3DENTITY, "3D Object");
2023-09-12 17:03:06 +08:00
EasyClassReg(ObjImpls::CKTexture, CK_CLASSID::CKCID_TEXTURE, CK_CLASSID::CKCID_BEOBJECT, "Texture");
2023-09-14 22:08:40 +08:00
EasyClassReg(ObjImpls::CKMaterial, CK_CLASSID::CKCID_MATERIAL, CK_CLASSID::CKCID_BEOBJECT, "Material");
2023-09-01 12:19:06 +08:00
#undef EasyClassReg
#undef EasyClassRegWithNotify
2023-08-23 16:04:58 +08:00
2023-08-25 21:57:22 +08:00
CKBuildClassHierarchyTable();
2023-08-23 16:04:58 +08:00
return CKERROR::CKERR_OK;
}
CKERROR CKShutdown() {
// free class indo
g_CKClassInfo.clear();
return CKERROR::CKERR_OK;
}
#pragma endregion
2023-02-25 17:39:39 +08:00
}