write some funcs for CKStateChunk writer

This commit is contained in:
yyc12345 2023-09-24 12:21:33 +08:00
parent a17b9deb4c
commit 3e265cdf1d
5 changed files with 355 additions and 74 deletions

View File

@ -173,6 +173,7 @@ namespace LibCmo::CK2 {
CKFileVisitor& operator=(CKFileVisitor&&);
const CKFileObject* GetFileObjectByIndex(size_t index);
CKDWORD GetIndexByObjectID(CK_ID objid);
protected:
bool m_IsReader;
CKFileReader* m_Reader;
@ -226,7 +227,7 @@ namespace LibCmo::CK2 {
// ========== Saving Preparing ==========
bool AddSavedObject(ObjImpls::CKObject* obj, CKDWORD flags = CK_STATESAVE_ALL);
bool AddSavedObjects(CKObjectArray* objarray, CKDWORD flags = CK_STATESAVE_ALL);
bool AddSavedObjects(const XContainer::XObjectPointerArray& objarray, CKDWORD flags = CK_STATESAVE_ALL);
bool AddSavedFile(CKSTRING u8FileName);
// ========== Saving ==========
@ -248,6 +249,8 @@ namespace LibCmo::CK2 {
XContainer::XArray<CKFileManagerData> m_ManagersData; /**< Manager Data loaded */
XContainer::XArray<CKFilePluginDependencies> m_PluginsDep; /**< Plugins dependencies for this file */
XContainer::XArray<XContainer::XString> m_IncludedFiles; /**< List of files that should be inserted in the CMO file. */
XContainer::XHashTable<CK_ID, CKDWORD> m_ObjectsHashTable; /**< A Object ID to save index hash table. */
XContainer::XBitArray m_AlreadySavedMask; /**< Field recording saved object id. If this object is saved, set m_AlreadySavedMask[id] to true. Also used to check whether object already is in save list. */
CKFileInfo m_FileInfo; /**< Headers summary */
CKERROR PrepareFile(CKSTRING filename);

View File

@ -234,17 +234,47 @@ namespace LibCmo::CK2 {
bool CKFileWriter::AddSavedObject(ObjImpls::CKObject* obj, CKDWORD flags) {
if (m_Done || m_IsCopyFromReader) return false;
return false;
if (obj == nullptr) return false;
// check whether is saved.
CK_ID objid = obj->GetID();
if (XContainer::NSXBitArray::IsSet(m_AlreadySavedMask, static_cast<CKDWORD>(objid))) return false;
// ok, insert this value
m_ObjectsHashTable.try_emplace(objid, static_cast<CKDWORD>(m_FileObjects.size()));
XContainer::NSXBitArray::Set(m_AlreadySavedMask, static_cast<CKDWORD>(objid));
CKFileObject fobj;
fobj.ObjectId = objid;
fobj.ObjPtr = obj;
fobj.ObjectCid = obj->GetClassID();
fobj.SaveFlags = flags;
XContainer::NSXString::FromCKSTRING(fobj.Name, obj->GetName());
m_FileObjects.emplace_back(std::move(fobj));
return true;
}
bool CKFileWriter::AddSavedObjects(CKObjectArray* objarray, CKDWORD flags) {
bool CKFileWriter::AddSavedObjects(const XContainer::XObjectPointerArray& objarray, CKDWORD flags) {
if (m_Done || m_IsCopyFromReader) return false;
return false;
bool ret = true;
for (auto obj : objarray) {
if (!AddSavedObject(obj, flags)) {
ret = false;
}
}
return ret;
}
bool CKFileWriter::AddSavedFile(CKSTRING u8FileName) {
if (m_Done || m_IsCopyFromReader) return false;
return false;
if (u8FileName == nullptr) return false;
m_IncludedFiles.emplace_back(u8FileName);
return true;
}
#pragma endregion
@ -294,6 +324,16 @@ namespace LibCmo::CK2 {
}
}
CKDWORD CKFileVisitor::GetIndexByObjectID(CK_ID objid) {
// see CKFile::SaveFindObjectIndex in IDA
CKDWORD idx = -1;
if (m_IsReader) return idx;
auto finder = m_Writer->m_ObjectsHashTable.find(objid);
if (finder == m_Writer->m_ObjectsHashTable.end()) return idx;
return finder->second;
}
#pragma endregion
}

View File

@ -6,6 +6,17 @@
namespace LibCmo::CK2 {
/**
* @remark
* + We make sure m_BindContext and m_BindFile always are not nullptr. So some code of BindFile check and write different data struct has been removed.
* + Calling StartRead multiple times is illegal.
* - The solution is that use StartRead and StopRead to warp the real CKStateChunk consumer. And just calling read functions in real consumer directly.
* - See CKFileReader for more infomation.
* + Same as StartRead, calling StartWrite multiple times also is illegal. We also remove CKStateChunk merge function, AddChunkAndDelete and AddChunk.
* - The solution is same as StartRead solution. Use StartWrite and StopWrite warp the real CKStateChunk writer. Call write function in consumer directly.
* - Every inherited CKObject::Save must call SetClassId at the end of function if they have data to write.
* - See CKFileWrite for more infomation.
*/
class CKStateChunk {
public:
CKStateChunk(CKFileVisitor* visitor, CKContext* ctx);
@ -231,6 +242,7 @@ namespace LibCmo::CK2 {
/* ========== Identifier Functions ==========*/
public:
bool SeekIdentifierDword(CKDWORD identifier);
bool SeekIdentifierDwordAndReturnSize(CKDWORD identifier, CKDWORD* out_size);
template<typename TEnum>
@ -502,61 +514,122 @@ namespace LibCmo::CK2 {
return ReadXObjectPointerArray(&ls);
}
//int ReadInt();
//int StartReadSequence();
//CK_ID ReadObjectID();
//CKStateChunk* ReadSubChunk();
//int StartManagerReadSequence(CKGUID* guid);
//CKGUID ReadGuid();
//void ReadAndFillBuffer_LEndian(void* buffer);
//void ReadAndFillBuffer_LEndian16(void* buffer);
//float ReadFloat();
//CKWORD ReadWord();
//CKDWORD ReadDword();
//CKDWORD ReadDwordAsWords();
//void ReadVector(VxMath::VxVector* v);
//void ReadMatrix(VxMath::VxMatrix& mat);
//CKObjectImplements::CKObject* ReadObject(CKMinContext*);
//void ReadAndFillBuffer(void* buffer);
//CKBYTE* ReadRawBitmap(VxMath::VxImageDescEx& desc);
//XObjectArray ReadXObjectArray(void);
#pragma endregion
#pragma region Write Function
public:
void StartWrite();
//void WriteIdentifier(CKDWORD id);
//void AddChunkAndDelete(CKStateChunk*);
//void StartObjectIDSequence(int count);
//void WriteObjectSequence(CKObjectImplements::CKObject* obj);
//void WriteInt(int data);
//void WriteFloat(float data);
//void WriteDword(CKDWORD data);
//void WriteDwordAsWords(CKDWORD data);
//void WriteVector(const VxMath::VxVector* v);
//void WriteMatrix(const VxMath::VxMatrix& mat);
//void WriteObject(CKObjectImplements::CKObject* obj);
//void WriteBuffer_LEndian(int size, void* buf);
//void WriteBuffer_LEndian16(int size, void* buf);
//void WriteBufferNoSize_LEndian(int size, void* buf);
///*void UpdateDataSize();*/
//void* LockWriteBuffer(int DwordCount);
/*
* Old Name: CloseChunk();
* Actually this function mix various functions, including CloseChunk(), UpdateSize() and etc.
*/
void StopWrite(void);
bool LockWriteBuffer(const void** ppData, CKDWORD size_in_byte);
/* ========== Identifier Functions ==========*/
public:
bool WriteIdentifierDword(CKDWORD identifier);
template<typename TEnum>
inline bool WriteIdentifier(TEnum enum_v) {
return WriteIdentifierDword(static_cast<CKDWORD>(enum_v));
}
/* ========== Write Buffer Controller ==========*/
public:
bool LockWriteBuffer(void** ppData, CKDWORD size_in_byte);
bool UnLockWriteBuffer(CKDWORD size_in_byte);
LockedWriteBuffer_t LockWriteBufferWrapper(CKDWORD size_in_byte);
/* ========== Basic Data Write Functions ==========*/
private:
bool WriteByteData(const void* data_ptr, CKDWORD size_in_byte);
public:
template<typename T>
bool WriteStruct(const T* data) {
return WriteByteData(data, CKSizeof(T));
}
template<typename T>
inline bool WriteStruct(const T& data) {
return WriteByteData(&data, CKSizeof(T));
}
bool WriteString(const XContainer::XString* strl);
inline bool WriteString(const XContainer::XString& strl) {
return WriteString(&strl);
}
/* ========== Complex Data Read Functions ==========*/
public:
bool WriteObjectID(const CK_ID* id);
bool WriteObjectPointer(ObjImpls::CKObject* obj);
inline bool WriteObjectID(const CK_ID& id) {
return WriteObjectID(&id);
}
bool WriteManagerInt(const CKGUID* guid, CKINT intval);
inline bool WriteManagerInt(const CKGUID& guid, CKINT intval) {
return WriteManagerInt(&guid, intval);
}
// Sub Chunk not support now.
// Too complex and I even don't use it in my code.
//CKStateChunk* ReadSubChunk();
/* ========== Buffer Functions ==========*/
/*
Buffer related function implements:
WriteBuffer(int, void*) Write buffer with size. -> WriteBuffer(const void*, CKDWORD)
WriteBufferNoSize(int, void*) Write buffer without size. -> WriteBufferNoSize(const void*, CKDWORD)
WriteBuffer_LEndian(int, void*) Write buffer with size. -> WriteBuffer(const void*, CKDWORD)
WriteBuffer_LEndian16(int, void*) Write buffer with size. -> WriteBuffer(const void*, CKDWORD)
WriteBufferNoSize_LEndian(int, void*) Write buffer without size. -> WriteBufferNoSize(const void*, CKDWORD)
WriteBufferNoSize_LEndian16(int, void*) Write buffer without size. -> WriteBufferNoSize(const void*, CKDWORD)
*/
bool WriteBuffer(const void* buf, CKDWORD size_in_byte);
bool WriteBufferNoSize(const void* buf, CKDWORD size_in_byte);
/* ========== Sequence Functions ==========*/
public:
bool WriteObjectIDSequence(const XContainer::XObjectArray* ls);
inline bool ReadObjectIDSequence(const XContainer::XObjectArray& ls) {
return WriteObjectIDSequence(&ls);
}
bool WriteManagerIntSequence(const CKGUID* guid, const XContainer::XArray<CKINT>* ls);
inline bool WriteManagerIntSequence(const CKGUID& guid, const XContainer::XArray<CKINT>& ls) {
return WriteManagerIntSequence(&guid, &ls);
}
// Sub chunk is not available now
// Because my code never use it and it is too complex.
//bool ReadSubChunkSequence(XContainer::XArray<CKStateChunk*>* ls);
//inline bool ReadSubChunkSequence(XContainer::XArray<CKStateChunk*>& ls) {
// return ReadSubChunkSequence(&ls);
//}
bool WriteXObjectArray(const XContainer::XObjectArray* ls);
inline bool WriteXObjectArray(const XContainer::XObjectArray& ls) {
return WriteXObjectArray(&ls);
}
bool WriteXObjectPointerArray(const XContainer::XObjectPointerArray* ls);
inline bool WriteXObjectPointerArray(const XContainer::XObjectPointerArray& ls) {
return WriteXObjectPointerArray(&ls);
}
#pragma endregion
};
}

View File

@ -209,9 +209,9 @@ namespace LibCmo::CK2 {
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;
CKDWORD subDwordChunkSize;
if (!this->ReadStruct(subDwordChunkSize)) goto subchunk_defer;
if (!this->EnsureReadSpace(subDwordChunkSize)) goto subchunk_defer;
// create statechunk
subchunk = new CKStateChunk(this->m_BindFile, this->m_BindContext);
@ -502,6 +502,7 @@ namespace LibCmo::CK2 {
bool CKStateChunk::ReadXObjectPointerArray(XContainer::XObjectPointerArray* ls) {
if (ls == nullptr) return false;
ls->clear();
// very very similar to ReadXObjectArray
// we execute it first.

View File

@ -1,6 +1,7 @@
#include "CKStateChunk.hpp"
#include "CKFile.hpp"
#include "CKContext.hpp"
#include "ObjImpls/CKObject.hpp"
namespace LibCmo::CK2 {
@ -46,16 +47,179 @@ namespace LibCmo::CK2 {
this->m_Parser.m_Status = CKStateChunkStatus::IDLE;
}
bool CKStateChunk::LockWriteBuffer(const void** ppData, CKDWORD size_in_byte) {
/* ========== Identifier Functions ==========*/
bool CKStateChunk::WriteIdentifierDword(CKDWORD identifier) {
// check self status
if (this->m_Parser.m_Status != CKStateChunkStatus::WRITE) return false;
// make sure there are 2 DWORD space for writing identifier header
if (!EnsureWriteSpace(2)) return false;
// update the last identifier header to fill its length indicator
if (m_Parser.m_PrevIdentifierPos < m_Parser.m_CurrentPos) {
m_pData[m_Parser.m_PrevIdentifierPos + 1] = m_Parser.m_CurrentPos;
}
// set prev ident to this new created ident
m_Parser.m_PrevIdentifierPos = m_Parser.m_CurrentPos;
// write identifier and set default next ident data
m_pData[m_Parser.m_CurrentPos++] = identifier;
m_pData[m_Parser.m_CurrentPos++] = 0;
return true;
}
/* ========== Write Buffer Controller ==========*/
bool CKStateChunk::LockWriteBuffer(void** ppData, CKDWORD size_in_byte) {
// same as LockReadBuffer with slight difference.
if (this->m_Parser.m_Status != CKStateChunkStatus::WRITE) return false;
if (*ppData == nullptr) return false;
*ppData = nullptr;
CKDWORD size_in_dword = this->GetCeilDwordSize(size_in_byte);
if (this->EnsureWriteSpace(size_in_dword)) {
*ppData = this->m_pData + this->m_Parser.m_CurrentPos;
return true;
} else {
m_BindContext->OutputToConsoleEx("CKStateChunk::LockWriteBuffer at buffer pos %" PRIuCKDWORD ".", this->m_Parser.m_CurrentPos);
return false;
}
}
bool CKStateChunk::UnLockWriteBuffer(CKDWORD size_in_byte) {
// same as UnLockReadBuffer with slight difference.
if (this->m_Parser.m_Status != CKStateChunkStatus::WRITE) return false;
CKDWORD size_in_dword = this->GetCeilDwordSize(size_in_byte);
if (this->EnsureWriteSpace(size_in_dword)) {
this->m_Parser.m_CurrentPos += size_in_dword;
return true;
} else {
m_BindContext->OutputToConsoleEx("CKStateChunk::UnLockWriteBuffer at buffer pos %" PRIuCKDWORD ".", this->m_Parser.m_CurrentPos);
return false;
}
}
CKStateChunk::LockedWriteBuffer_t CKStateChunk::LockWriteBufferWrapper(CKDWORD size_in_byte) {
// same as LockReadBufferWrapper with slight difference.
void* pData;
bool ret = LockWriteBuffer(&pData, size_in_byte);
if (ret) {
return LockedWriteBuffer_t(pData, LockedWriteBufferDeleter(this, size_in_byte));
} else {
return LockedWriteBuffer_t();
}
}
/* ========== Basic Data Write Functions ==========*/
bool CKStateChunk::WriteByteData(const void* data_ptr, CKDWORD size_in_byte) {
// same as ReadByteData with slight difference.
if (data_ptr == nullptr) return false;
void* pData;
bool ret = LockWriteBuffer(&pData, size_in_byte);
if (ret) {
std::memcpy(pData, data_ptr, size_in_byte);
UnLockWriteBuffer(size_in_byte);
return true;
} else {
return false;
}
}
bool CKStateChunk::WriteString(const XContainer::XString* strl) {
if (strl == nullptr) return;
// convert encoding
XContainer::XString cache;
m_BindContext->GetNativeString(*strl, cache);
// get size
CKDWORD strByteSize = static_cast<CKDWORD>(cache.size());
if (!this->WriteStruct(strByteSize)) {
return false;
}
// write data
if (!this->WriteByteData(cache.c_str(), strByteSize)) {
return false;
}
return true;
}
bool CKStateChunk::WriteObjectID(const CK_ID* id) {
// MARK: if BindFile is not nullptr, no need to push this obj into obj list according to IDA code.
// but we assume BindFile always it not nullptr, so I remove that pushing code.
return this->WriteStruct(m_BindFile->GetIndexByObjectID(*id));
}
bool CKStateChunk::WriteObjectPointer(ObjImpls::CKObject* obj) {
CK_ID objid;
if (obj != nullptr) {
objid = obj->GetID();
}
return WriteObjectID(objid);
}
bool CKStateChunk::WriteManagerInt(const CKGUID* guid, CKINT intval) {
// push into manager list
m_ManagerList.emplace_back(m_Parser.m_CurrentPos);
// write data
if (!this->WriteStruct(guid)) return false;
if (!this->WriteStruct(intval)) return false;
return true;
}
/* ========== Buffer Functions ==========*/
bool CKStateChunk::WriteBuffer(const void* buf, CKDWORD size_in_byte) {
if (buf != nullptr) {
// write size
if (!this->WriteStruct(size_in_byte)) return false;
// write data
auto locker = LockWriteBufferWrapper(size_in_byte);
if (locker == nullptr) return false;
std::memcpy(locker.get(), buf, size_in_byte);
locker.reset();
} else {
// write blank data
if (!this->WriteStruct(0)) return false;
}
return true;
}
bool CKStateChunk::WriteBufferNoSize(const void* buf, CKDWORD size_in_byte) {
if (buf != nullptr) {
// write data
auto locker = LockWriteBufferWrapper(size_in_byte);
if (locker == nullptr) return false;
std::memcpy(locker.get(), buf, size_in_byte);
locker.reset();
}
// if nosize buffer is nullptr, nothing need to write.
return true;
}
/* ========== Sequence Functions ==========*/
bool CKStateChunk::WriteObjectIDSequence(const XContainer::XObjectArray* ls) {
return false;
}
bool CKStateChunk::WriteManagerIntSequence(const CKGUID* guid, const XContainer::XArray<CKINT>* ls) {
return false;
}
bool CKStateChunk::WriteXObjectArray(const XContainer::XObjectArray* ls) {
return false;
}
bool CKStateChunk::WriteXObjectPointerArray(const XContainer::XObjectPointerArray* ls) {
return false;
}
}