almost finish CKStateChunk read function

This commit is contained in:
yyc12345 2023-03-08 15:45:06 +08:00
parent f3cb9b1a07
commit d3b0e92517
8 changed files with 290 additions and 20 deletions

View File

@ -288,7 +288,7 @@ struct CKStateChunk {
CKFile* m_BindFile; CKFile* m_BindFile;
DWORD m_unknow; DWORD m_unknow;
}; };
struct CKStateChunkRemap { // 0x40 count 16 struct ChunkIteratorData { // 0x40 count 16
DWORD m_ChunkVersion; DWORD m_ChunkVersion;
DWORD* m_pData; DWORD* m_pData;
DWORD m_DataDwordSize; DWORD m_DataDwordSize;

View File

@ -3,6 +3,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <cstring> #include <cstring>
#include <cinttypes>
namespace LibCmo { namespace LibCmo {
namespace CK2 { namespace CK2 {
@ -30,6 +31,11 @@ namespace LibCmo {
using XObjectArray = std::vector<CK_ID>; using XObjectArray = std::vector<CK_ID>;
//using CKObjectArray = std::vector<CKObject*>; //using CKObjectArray = std::vector<CKObject*>;
// some print macro define
#define PRICKdword PRIu32
#define PRICKword PRIu16
#define PRICKbyte PRIu8
#define PRICKint PRIi32
// forward decl for some CKObjects // forward decl for some CKObjects
namespace CKObjectImplements { namespace CKObjectImplements {

View File

@ -63,12 +63,12 @@ namespace LibCmo::CK2 {
#pragma region CKFileObject #pragma region CKFileObject
CKFileObject::CKFileObject() : CKFileObject::CKFileObject() :
ObjectId(0u), ObjectCid(CK_CLASSID::CKCID_OBJECT), Name(), ObjectId(0u), CreatedObject(0u), ObjectCid(CK_CLASSID::CKCID_OBJECT), Name(),
ObjPtr(nullptr), Data(nullptr), FileIndex(0u) { ObjPtr(nullptr), Data(nullptr), FileIndex(0u) {
} }
CKFileObject::CKFileObject(const CKFileObject& rhs) : CKFileObject::CKFileObject(const CKFileObject& rhs) :
ObjectId(rhs.ObjectId), ObjectCid(rhs.ObjectCid), Name(rhs.Name), ObjectId(rhs.ObjectId), CreatedObject(rhs.CreatedObject), ObjectCid(rhs.ObjectCid), Name(rhs.Name),
ObjPtr(rhs.ObjPtr), Data(rhs.Data), FileIndex(rhs.FileIndex) { ObjPtr(rhs.ObjPtr), Data(rhs.Data), FileIndex(rhs.FileIndex) {
// CKObject is managed by CKMinContext, so we just copy its pointer. // CKObject is managed by CKMinContext, so we just copy its pointer.
@ -81,6 +81,7 @@ namespace LibCmo::CK2 {
CKFileObject& CKFileObject::operator=(const CKFileObject& rhs) { CKFileObject& CKFileObject::operator=(const CKFileObject& rhs) {
this->ObjectId = rhs.ObjectId; this->ObjectId = rhs.ObjectId;
this->CreatedObject = rhs.CreatedObject;
this->ObjectCid = rhs.ObjectCid; this->ObjectCid = rhs.ObjectCid;
this->Name = rhs.Name; this->Name = rhs.Name;
this->FileIndex = rhs.FileIndex; this->FileIndex = rhs.FileIndex;
@ -173,7 +174,7 @@ namespace LibCmo::CK2 {
#pragma region CKFile Misc #pragma region CKFile Misc
CKFile::CKFile(CKMinContext* ctx) : CKFile::CKFile(CKMinContext* ctx) :
m_FileName(), m_MinCtx(ctx) { m_MinCtx(ctx) {
; ;
} }

View File

@ -89,6 +89,7 @@ namespace LibCmo::CK2 {
~CKFileObject(); ~CKFileObject();
CK_ID ObjectId; // ID of the object being load/saved (as it will be/was saved in the file) CK_ID ObjectId; // ID of the object being load/saved (as it will be/was saved in the file)
CK_ID CreatedObject; // ID of the object being created
CK_CLASSID ObjectCid; // Class Identifier of the object CK_CLASSID ObjectCid; // Class Identifier of the object
CKObjectImplements::CKObject* ObjPtr; // A pointer to the object itself (as CreatedObject when loading) CKObjectImplements::CKObject* ObjPtr; // A pointer to the object itself (as CreatedObject when loading)
std::string Name; // Name of the Object std::string Name; // Name of the Object
@ -186,7 +187,6 @@ namespace LibCmo::CK2 {
// writer function and varibales // writer function and varibales
// shared function and variables // shared function and variables
std::string m_FileName;
CKMinContext* m_MinCtx; CKMinContext* m_MinCtx;
}; };

View File

@ -27,7 +27,7 @@ namespace LibCmo::CK2 {
return CKERROR::CKERR_OUTOFMEMORY; return CKERROR::CKERR_OUTOFMEMORY;
} }
if (!mappedFile->IsValid()) { if (!mappedFile->IsValid()) {
this->m_MinCtx->Printf("Fail to create Memory File for \"%s\".", this->m_FileName.c_str()); this->m_MinCtx->Printf("Fail to create Memory File for \"%s\".", u8_filename);
return CKERROR::CKERR_INVALIDFILE; return CKERROR::CKERR_INVALIDFILE;
} }
@ -270,7 +270,7 @@ namespace LibCmo::CK2 {
} }
// read statechunk // read statechunk
mgr.Data = new(std::nothrow) CKStateChunk(); mgr.Data = new(std::nothrow) CKStateChunk(doc, this->m_MinCtx);
if (mgr.Data != nullptr) { if (mgr.Data != nullptr) {
stateChkParseSuccess = mgr.Data->ConvertFromBuffer(parser->GetPtr()); stateChkParseSuccess = mgr.Data->ConvertFromBuffer(parser->GetPtr());
if (!stateChkParseSuccess) { if (!stateChkParseSuccess) {
@ -298,7 +298,7 @@ namespace LibCmo::CK2 {
} }
// read state chunk // read state chunk
obj.Data = new(std::nothrow) CKStateChunk(); obj.Data = new(std::nothrow) CKStateChunk(doc, this->m_MinCtx);
if (obj.Data != nullptr) { if (obj.Data != nullptr) {
stateChkParseSuccess = obj.Data->ConvertFromBuffer(parser->GetPtr()); stateChkParseSuccess = obj.Data->ConvertFromBuffer(parser->GetPtr());
if (!stateChkParseSuccess) { if (!stateChkParseSuccess) {
@ -370,7 +370,13 @@ namespace LibCmo::CK2 {
// todo: resolve references // todo: resolve references
if (obj.Data == nullptr) continue; if (obj.Data == nullptr) continue;
// create object and assign created obj ckid
obj.ObjPtr = m_MinCtx->CreateCKObject(obj.ObjectId, obj.ObjectCid, obj.Name.c_str()); obj.ObjPtr = m_MinCtx->CreateCKObject(obj.ObjectId, obj.ObjectCid, obj.Name.c_str());
if (obj.ObjPtr == nullptr) {
obj.CreatedObject = 0u;
} else {
obj.CreatedObject = obj.ObjPtr->m_ID;
}
} }
// ========== CKStateChunk remap ========== // ========== CKStateChunk remap ==========
@ -397,6 +403,7 @@ namespace LibCmo::CK2 {
// if failed, delete it // if failed, delete it
m_MinCtx->DestroyCKObject(obj.ObjectId); m_MinCtx->DestroyCKObject(obj.ObjectId);
obj.ObjPtr = nullptr; obj.ObjPtr = nullptr;
obj.CreatedObject = 0u;
} }
} }

View File

@ -6,13 +6,12 @@
namespace LibCmo::CK2::CKObjectImplements { namespace LibCmo::CK2::CKObjectImplements {
class CKObject { class CKObject {
protected: public:
CK_ID m_ID; CK_ID m_ID;
std::string m_Name; std::string m_Name;
CK_OBJECT_FLAGS m_ObjectFlags; CK_OBJECT_FLAGS m_ObjectFlags;
CKMinContext* m_Context; CKMinContext* m_Context;
public:
CKObject(CKMinContext* ctx, CK_ID ckid, CKSTRING name); CKObject(CKMinContext* ctx, CK_ID ckid, CKSTRING name);
CKObject(const CKObject&) = delete; CKObject(const CKObject&) = delete;
CKObject& operator=(const CKObject&) = delete; CKObject& operator=(const CKObject&) = delete;

View File

@ -1,15 +1,27 @@
#include "VTUtils.hpp" #include "VTUtils.hpp"
#include "CKStateChunk.hpp" #include "CKStateChunk.hpp"
#include "CKMinContext.hpp"
#include "CKFile.hpp"
namespace LibCmo::CK2 { namespace LibCmo::CK2 {
#pragma region Ctor Dtor #pragma region Ctor Dtor
CKStateChunk::CKStateChunk() : //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(CKFileDocument* doc, CKMinContext* 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),
m_Parser{ CKStateChunkStatus::IDLE, 0u, 0u, 0u }, m_Parser{ CKStateChunkStatus::IDLE, 0u, 0u, 0u },
m_ObjectList(), m_ChunkList(), m_ManagerList() m_ObjectList(), m_ChunkList(), m_ManagerList(),
m_BindDoc(doc), m_BindContext(ctx)
{ {
; ;
} }
@ -17,7 +29,8 @@ namespace LibCmo::CK2 {
m_ClassId(rhs.m_ClassId), m_DataVersion(rhs.m_DataVersion), m_ChunkVersion(rhs.m_ChunkVersion), m_ClassId(rhs.m_ClassId), m_DataVersion(rhs.m_DataVersion), m_ChunkVersion(rhs.m_ChunkVersion),
m_Parser(rhs.m_Parser), m_Parser(rhs.m_Parser),
m_ObjectList(rhs.m_ObjectList), m_ManagerList(rhs.m_ManagerList), m_ChunkList(rhs.m_ChunkList), m_ObjectList(rhs.m_ObjectList), m_ManagerList(rhs.m_ManagerList), m_ChunkList(rhs.m_ChunkList),
m_pData(nullptr), m_DataDwSize(rhs.m_DataDwSize) { m_pData(nullptr), m_DataDwSize(rhs.m_DataDwSize),
m_BindDoc(rhs.m_BindDoc), m_BindContext(rhs.m_BindContext) {
// copy buffer // copy buffer
if (rhs.m_pData != nullptr) { if (rhs.m_pData != nullptr) {
this->m_pData = new(std::nothrow) CKDWORD[rhs.m_DataDwSize]; this->m_pData = new(std::nothrow) CKDWORD[rhs.m_DataDwSize];
@ -39,6 +52,9 @@ namespace LibCmo::CK2 {
this->m_ManagerList = rhs.m_ManagerList; this->m_ManagerList = rhs.m_ManagerList;
this->m_ChunkList = rhs.m_ChunkList; this->m_ChunkList = rhs.m_ChunkList;
this->m_BindDoc = rhs.m_BindDoc;
this->m_BindContext = rhs.m_BindContext;
// copy buffer // copy buffer
if (rhs.m_pData != nullptr) { if (rhs.m_pData != nullptr) {
this->m_pData = new(std::nothrow) CKDWORD[rhs.m_DataDwSize]; this->m_pData = new(std::nothrow) CKDWORD[rhs.m_DataDwSize];
@ -408,7 +424,11 @@ namespace LibCmo::CK2 {
if (this->EnsureReadSpace(size_in_dword)) { if (this->EnsureReadSpace(size_in_dword)) {
std::memcpy(data_ptr, this->m_pData + this->m_Parser.m_CurrentPos, size_in_byte); std::memcpy(data_ptr, this->m_pData + this->m_Parser.m_CurrentPos, size_in_byte);
return true; return true;
} else return false; } else {
// failed, report to context
m_BindContext->Printf("CKStateChunk read length error at %" PRICKdword ".", this->m_Parser.m_CurrentPos);
return false;
}
} }
bool CKStateChunk::ReadString(std::string* strl) { bool CKStateChunk::ReadString(std::string* strl) {
@ -433,14 +453,147 @@ namespace LibCmo::CK2 {
/* ========== Complex Data Read Functions ==========*/ /* ========== Complex Data Read Functions ==========*/
bool CKStateChunk::ReadObjectID(CK_ID* id) { bool CKStateChunk::ReadObjectID(CK_ID* id) {
if (id == nullptr) return false;
// get basic value
CKINT gotten_id = 0;
if (!this->ReadStruct(gotten_id)) return false;
// different strategy according to chunk ver
if (this->m_ChunkVersion >= CK_STATECHUNK_CHUNKVERSION::CHUNK_VERSION1) {
// new file
// if no doc associated, return directly
if (this->m_BindDoc == nullptr) {
*id = static_cast<CK_ID>(gotten_id);
return true;
}
// if it is positive, return corresponding value
if (gotten_id >= 0) {
*id = this->m_BindDoc->m_FileObjects[gotten_id].CreatedObject;
return true;
}
} else {
// old file
// i don't know why I need skip 2 DWORD
// just copy IDA code.
if (gotten_id) {
this->Skip(2);
return this->ReadStruct(id);
}
}
// all failed
*id = 0u;
return false; return false;
} }
bool CKStateChunk::ReadManagerInt(CKGUID* guid, CKINT* intval) { bool CKStateChunk::ReadManagerInt(CKGUID* guid, CKINT* intval) {
return false; if (guid == nullptr || intval == nullptr) return false;
// read guid first
if (!this->ReadStruct(guid)) return false;
// then read int value
if (!this->ReadStruct(intval)) return false;
return true;
} }
CKStateChunk* CKStateChunk::ReadSubChunk(void) { CKStateChunk* CKStateChunk::ReadSubChunk(void) {
CKStateChunk* subchunk = nullptr;
// get size and do a enough space check
CKDWORD subChunkSize;
if (!this->ReadStruct(subChunkSize)) goto subchunk_defer;
if (!this->EnsureReadSpace(subChunkSize)) goto subchunk_defer;
// create statechunk
subchunk = new(std::nothrow) CKStateChunk(this->m_BindDoc, this->m_BindContext);
if (subchunk == nullptr) goto subchunk_defer;
// start read data
// read class id
if (!this->ReadStruct(subchunk->m_ClassId)) goto subchunk_defer;
// different read strategy by chunk version
if (this->m_ChunkVersion >= CK_STATECHUNK_CHUNKVERSION::CHUNK_VERSION1) {
// new file
// read combined version
CKDWORD versionInfo;
if (!this->ReadStruct(versionInfo)) goto subchunk_defer;
subchunk->m_DataVersion = static_cast<CK_STATECHUNK_DATAVERSION>(versionInfo & 0xffff);
subchunk->m_ChunkVersion = static_cast<CK_STATECHUNK_CHUNKVERSION>((versionInfo >> 16) & 0xffff);
// read data size and create it
if (!this->ReadStruct(subchunk->m_DataDwSize)) goto subchunk_defer;
subchunk->m_pData = new(std::nothrow) CKDWORD[subchunk->m_DataDwSize];
if (subchunk->m_pData == nullptr) goto subchunk_defer;
// has bind file?
CKDWORD hasBindFile;
if (!this->ReadStruct(hasBindFile)) goto subchunk_defer;
if (hasBindFile == 1) subchunk->m_BindDoc = nullptr;
// 3 list size
// manager only existed when ver > 4
CKDWORD lssize;
if (!this->ReadStruct(lssize)) goto subchunk_defer;
subchunk->m_ObjectList.resize(lssize);
if (!this->ReadStruct(lssize)) goto subchunk_defer;
subchunk->m_ChunkList.resize(lssize);
if (this->m_ChunkVersion > CK_STATECHUNK_CHUNKVERSION::CHUNK_VERSION1) {
if (!this->ReadStruct(lssize)) goto subchunk_defer;
subchunk->m_ManagerList.resize(lssize);
}
// core data
if (subchunk->m_DataDwSize != 0) {
if (!this->ReadByteData(subchunk->m_pData, subchunk->m_DataDwSize * sizeof(CKDWORD))) goto subchunk_defer;
}
// 3 list data
if (!subchunk->m_ObjectList.empty()) {
if (!this->ReadByteData(
subchunk->m_ObjectList.data(),
subchunk->m_ObjectList.size() * sizeof(CKDWORD)
)) goto subchunk_defer;
}
if (!subchunk->m_ChunkList.empty()) {
if (!this->ReadByteData(
subchunk->m_ChunkList.data(),
subchunk->m_ChunkList.size() * sizeof(CKDWORD)
)) goto subchunk_defer;
}
if (!subchunk->m_ManagerList.empty()) {
if (!this->ReadByteData(
subchunk->m_ManagerList.data(),
subchunk->m_ManagerList.size() * sizeof(CKDWORD)
)) goto subchunk_defer;
}
} else {
// old file
// read data size and create it
if (!this->ReadStruct(subchunk->m_DataDwSize)) goto subchunk_defer;
subchunk->m_pData = new(std::nothrow) CKDWORD[subchunk->m_DataDwSize];
if (subchunk->m_pData == nullptr) goto subchunk_defer;
// skip one?
// I don't know why
this->Skip(1u);
// read core buf
if (!this->ReadByteData(subchunk->m_pData, subchunk->m_DataDwSize * sizeof(CKDWORD))) goto subchunk_defer;
}
return subchunk;
subchunk_defer:
if (subchunk != nullptr) delete subchunk;
return nullptr; return nullptr;
} }
@ -484,19 +637,119 @@ namespace LibCmo::CK2 {
/* ========== Sequence Functions ==========*/ /* ========== Sequence Functions ==========*/
bool CKStateChunk::ReadObjectIDSequence(std::vector<CK_ID>* ls) { bool CKStateChunk::ReadObjectIDSequence(std::vector<CK_ID>* ls) {
return false; if (ls == nullptr) return false;
ls->clear();
// read count
CKDWORD count;
if (!this->ReadStruct(count)) return false;
// resize list and read it
ls->resize(count);
for (size_t i = 0; i < count; ++i) {
if (!this->ReadObjectID(ls->data() + i)) {
ls->clear();
return false;
}
}
return true;
} }
bool CKStateChunk::ReadManagerIntSequence(CKGUID* guid, std::vector<CKINT>* ls) { bool CKStateChunk::ReadManagerIntSequence(CKGUID* guid, std::vector<CKINT>* ls) {
return false; if (guid == nullptr || ls == nullptr) return false;
// read count
CKDWORD count;
if (!this->ReadStruct(count)) return false;
// read guid
if (!this->ReadStruct(guid)) return false;
// resize list and read it
ls->resize(count);
for (size_t i = 0; i < count; ++i) {
if (!this->ReadStruct(ls->data() + i)) {
ls->clear();
return false;
}
}
return true;
} }
bool CKStateChunk::ReadSubChunkSequence(std::vector<CKStateChunk*>* ls) { bool CKStateChunk::ReadSubChunkSequence(std::vector<CKStateChunk*>* ls) {
return false; if (ls == nullptr) return false;
// clear first
for (auto& item : *ls) {
if (item != nullptr)
delete (item);
}
ls->clear();
// read count
CKDWORD count;
if (!this->ReadStruct(count)) return false;
// resize list and read it
ls->resize(count, nullptr);
for (size_t i = 0; i < count; ++i) {
(*ls)[i] = this->ReadSubChunk();
if ((*ls)[i] == nullptr) {
// fail. remove all created statechunk and clear it
for (auto& item : *ls) {
if (item != nullptr)
delete (item);
}
ls->clear();
// return
return false;
}
}
return true;
} }
bool CKStateChunk::ReadObjectArray(std::vector<CK_ID>* ls) { bool CKStateChunk::ReadObjectArray(std::vector<CK_ID>* ls) {
return false; if (ls == nullptr) return false;
ls->clear();
// read count
CKDWORD count;
if (!this->ReadStruct(count)) return false;
if (!count) return true; // 0 size array
// old file size correction
if (this->m_ChunkVersion < CK_STATECHUNK_CHUNKVERSION::CHUNK_VERSION1) {
// skip 4. but I don't know why!!!
this->Skip(4);
if (!this->ReadStruct(count)) return false;
}
// resize list and read
ls->resize(count);
for (auto& id : *ls) {
// read ID first
CKINT cache;
if (!this->ReadStruct(cache)) {
ls->clear();
return false;
}
// remap id
if (this->m_BindDoc != nullptr) {
if (cache < 0) {
id = 0u;
} else {
id = this->m_BindDoc->m_FileObjects[cache].CreatedObject;
}
} else {
id = static_cast<CK_ID>(cache);
}
}
return true;
} }
#pragma endregion #pragma endregion

View File

@ -9,7 +9,8 @@ namespace LibCmo::CK2 {
class CKStateChunk { class CKStateChunk {
public: public:
CKStateChunk(); //CKStateChunk();
CKStateChunk(CKFileDocument* doc, CKMinContext* ctx);
CKStateChunk(const CKStateChunk&); CKStateChunk(const CKStateChunk&);
CKStateChunk& operator=(const CKStateChunk&); CKStateChunk& operator=(const CKStateChunk&);
~CKStateChunk(); ~CKStateChunk();
@ -39,6 +40,9 @@ namespace LibCmo::CK2 {
std::vector<CKDWORD> m_ChunkList; std::vector<CKDWORD> m_ChunkList;
std::vector<CKDWORD> m_ManagerList; std::vector<CKDWORD> m_ManagerList;
CKFileDocument* m_BindDoc;
CKMinContext* m_BindContext;
#pragma region Buffer Related #pragma region Buffer Related
public: public: