#include "CKFile.hpp" #include "CKStateChunk.hpp" #include "CKContext.hpp" #include "MgrImpls/CKPathManager.hpp" #include "ObjImpls/CKObject.hpp" #include namespace LibCmo::CK2 { #pragma region CKFileObject // CKObject is managed by CKMinContext, // so we do not considering its memory leak // however, we need process CKStateChunk. CKFileObject::CKFileObject() : ObjectId(0u), CreatedObjectId(0u), ObjectCid(CK_CLASSID::CKCID_OBJECT), ObjPtr(nullptr), Name(), Data(nullptr), Options(CK_FO_OPTIONS::CK_FO_DEFAULT), FileIndex(0u), SaveFlags(CK_STATESAVE_ALL), PackSize(0u) {} CKFileObject::CKFileObject(const CKFileObject& rhs) : ObjectId(rhs.ObjectId), CreatedObjectId(rhs.CreatedObjectId), ObjectCid(rhs.ObjectCid), ObjPtr(rhs.ObjPtr), Name(rhs.Name), Data(rhs.Data), Options(rhs.Options), FileIndex(rhs.FileIndex), SaveFlags(rhs.SaveFlags), PackSize(rhs.PackSize) { if (this->Data != nullptr) { this->Data = new CKStateChunk(*(rhs.Data)); } } CKFileObject::CKFileObject(CKFileObject&& rhs) : ObjectId(rhs.ObjectId), CreatedObjectId(rhs.CreatedObjectId), ObjectCid(rhs.ObjectCid), ObjPtr(rhs.ObjPtr), Name(rhs.Name), Data(rhs.Data), Options(rhs.Options), FileIndex(rhs.FileIndex), SaveFlags(rhs.SaveFlags), PackSize(rhs.PackSize) { rhs.Data = nullptr; } CKFileObject& CKFileObject::operator=(const CKFileObject& rhs) { this->ObjectId = rhs.ObjectId; this->CreatedObjectId = rhs.CreatedObjectId; this->ObjectCid = rhs.ObjectCid; this->ObjPtr = rhs.ObjPtr; this->Name = rhs.Name; this->Data = rhs.Data; if (this->Data != nullptr) { this->Data = new CKStateChunk(*(rhs.Data)); } this->PackSize = rhs.PackSize; this->Options = rhs.Options; this->FileIndex = rhs.FileIndex; this->SaveFlags = rhs.SaveFlags; return *this; } CKFileObject& CKFileObject::operator=(CKFileObject&& rhs) { this->ObjectId = rhs.ObjectId; this->CreatedObjectId = rhs.CreatedObjectId; this->ObjectCid = rhs.ObjectCid; this->ObjPtr = rhs.ObjPtr; this->Name = rhs.Name; this->Data = rhs.Data; rhs.Data = nullptr; this->PackSize = rhs.PackSize; this->Options = rhs.Options; this->FileIndex = rhs.FileIndex; this->SaveFlags = rhs.SaveFlags; return *this; } CKFileObject::~CKFileObject() { if (Data != nullptr) delete Data; } #pragma endregion #pragma region CKFileManagerData CKFileManagerData::CKFileManagerData() : Data(nullptr), Manager(0u, 0u) {} CKFileManagerData::CKFileManagerData(const CKFileManagerData& rhs) : Data(rhs.Data), Manager(rhs.Manager) { if (this->Data != nullptr) { this->Data = new CKStateChunk(*(rhs.Data)); } } CKFileManagerData::CKFileManagerData(CKFileManagerData&& rhs) : Data(rhs.Data), Manager(rhs.Manager) { rhs.Data = nullptr; } CKFileManagerData& CKFileManagerData::operator=(const CKFileManagerData& rhs) { this->Manager = rhs.Manager; this->Data = rhs.Data; if (this->Data != nullptr) { this->Data = new CKStateChunk(*(rhs.Data)); } return *this; } CKFileManagerData& CKFileManagerData::operator=(CKFileManagerData&& rhs) { this->Manager = rhs.Manager; this->Data = rhs.Data; rhs.Data = nullptr; return *this; } CKFileManagerData::~CKFileManagerData() { if (Data != nullptr) delete Data; } #pragma endregion #pragma region CKFileReader CKFileReader::CKFileReader(CKContext* ctx) : m_Ctx(ctx), m_Visitor(this), m_Done(false), m_SaveIDMax(0), m_FileObjects(), m_ManagersData(), m_PluginsDep(), m_IncludedFiles(), m_FileInfo() {} CKFileReader::~CKFileReader() {} CK_ID CKFileReader::GetSaveIdMax() { return m_SaveIDMax; } const XContainer::XArray& CKFileReader::GetFileObjects() { return m_FileObjects; } const XContainer::XArray& CKFileReader::GetManagersData() { return m_ManagersData; } const XContainer::XArray& CKFileReader::GetPluginsDep() { return m_PluginsDep; } const XContainer::XArray& CKFileReader::GetIncludedFiles() { return m_IncludedFiles; } const CKFileInfo CKFileReader::GetFileInfo() { return m_FileInfo; } #pragma endregion #pragma region CKFileWriter CKFileWriter::CKFileWriter(CKContext* ctx) : m_Ctx(ctx), m_Visitor(this), m_Done(false), m_DisableAddingObject(false), m_DisableAddingFile(false), m_SaveIDMax(0), m_FileObjects(), m_ManagersData(), m_PluginsDep(), m_IncludedFiles(), m_FileInfo() {} CKFileWriter::CKFileWriter(CKContext* ctx, CKFileReader* reader, bool is_shallow) : m_Ctx(ctx), m_Visitor(this), m_Done(false), m_DisableAddingObject(true), m_DisableAddingFile(is_shallow), // only disable adding file in shallow mode. but disable adding object in all mode. m_SaveIDMax(0), m_FileObjects(), m_ManagersData(), m_PluginsDep(), m_IncludedFiles(), m_FileInfo() { if (is_shallow) { #pragma region Shallow Assign // sync save id max this->m_SaveIDMax = reader->GetSaveIdMax(); // copy statechunk for (const auto& item : reader->GetFileObjects()) { CKFileObject obj; // use ctor to copy CKStateChunk if (item.Data == nullptr) { obj.Data = nullptr; } else { obj.Data = new CKStateChunk(*item.Data); } // set other data obj.ObjectId = item.ObjectId; obj.CreatedObjectId = 0; obj.ObjectCid = item.ObjectCid; obj.ObjPtr = nullptr; // set zero for obj obj.Name = item.Name; obj.SaveFlags = item.SaveFlags; // insert m_FileObjects.emplace_back(std::move(obj)); } // copy managers for (const auto& item : reader->GetManagersData()) { CKFileManagerData mgr; // copy guid mgr.Manager = item.Manager; // copy chunk if (item.Data == nullptr) { mgr.Data = nullptr; } else { mgr.Data = new CKStateChunk(*item.Data); } // insert m_ManagersData.emplace_back(std::move(mgr)); } // copy plugin dep for (const auto& item : reader->GetPluginsDep()) { // direct copy m_PluginsDep.emplace_back(item); } // copy included file for (const auto& item : reader->GetIncludedFiles()) { // resolve it to temp folder // and add it m_IncludedFiles.emplace_back(m_Ctx->GetPathManager()->GetTempFilePath(item.c_str())); } #pragma endregion } else { #pragma region Deep Assign // call internal object adder one by one for (const auto& item : reader->GetFileObjects()) { CKFileObject obj; // skip if invalid if (item.ObjPtr == nullptr) continue; // try add InternalObjectAdder(item.ObjPtr); } #pragma endregion } } CKFileWriter::~CKFileWriter() {} bool LibCmo::CK2::CKFileWriter::InternalObjectAdder(ObjImpls::CKObject * obj, CKDWORD flags) { if (obj == nullptr) return false; // check whether is saved. CK_ID objid = obj->GetID(); if (XContainer::NSXBitArray::IsSet(m_AlreadySavedMask, static_cast(objid))) return false; // ok, insert this value m_ObjectsHashTable.try_emplace(objid, static_cast(m_FileObjects.size())); XContainer::NSXBitArray::Set(m_AlreadySavedMask, static_cast(objid)); // update max id m_SaveIDMax = std::max(m_SaveIDMax, objid); // add entry 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::AddSavedObject(ObjImpls::CKObject* obj, CKDWORD flags) { if (m_Done || m_DisableAddingObject) return false; // call internal adder return InternalObjectAdder(obj, flags); } bool CKFileWriter::AddSavedObjects(const XContainer::XObjectPointerArray& objarray, CKDWORD flags) { if (m_Done || m_DisableAddingObject) return false; bool ret = true; for (auto obj : objarray) { // call AddSavedObject one by one if (!AddSavedObject(obj, flags)) { ret = false; } } return ret; } bool CKFileWriter::AddSavedFile(CKSTRING u8FileName) { if (m_Done || m_DisableAddingFile) return false; if (u8FileName == nullptr) return false; m_IncludedFiles.emplace_back(u8FileName); return true; } #pragma endregion #pragma region CKFileVisitor CKFileVisitor::CKFileVisitor(CKFileReader* reader) : m_IsReader(true), m_Reader(reader), m_Writer(nullptr), m_Ctx(reader->m_Ctx) { if (reader == nullptr) LIBCMO_PANIC("Reader is nullptr."); } CKFileVisitor::CKFileVisitor(CKFileWriter* writer) : m_IsReader(false), m_Reader(nullptr), m_Writer(writer), m_Ctx(writer->m_Ctx) { if (writer == nullptr) LIBCMO_PANIC("Writer is nullptr."); } CKFileVisitor::CKFileVisitor(const CKFileVisitor& rhs) : m_IsReader(rhs.m_IsReader), m_Reader(rhs.m_Reader), m_Writer(rhs.m_Writer), m_Ctx(rhs.m_Ctx) {} CKFileVisitor::CKFileVisitor(CKFileVisitor&& rhs) : m_IsReader(rhs.m_IsReader), m_Reader(rhs.m_Reader), m_Writer(rhs.m_Writer), m_Ctx(rhs.m_Ctx) {} CKFileVisitor& CKFileVisitor::operator=(const CKFileVisitor& rhs) { this->m_IsReader = rhs.m_IsReader; this->m_Reader = rhs.m_Reader; this->m_Writer = rhs.m_Writer; this->m_Ctx = rhs.m_Ctx; return *this; } CKFileVisitor& CKFileVisitor::operator=(CKFileVisitor&& rhs) { this->m_IsReader = rhs.m_IsReader; this->m_Reader = rhs.m_Reader; this->m_Writer = rhs.m_Writer; this->m_Ctx = rhs.m_Ctx; return *this; } const CKFileObject* CKFileVisitor::GetFileObjectByIndex(CKDWORD index) { if (m_IsReader) { return &m_Reader->m_FileObjects[index]; } else { return nullptr; } } CKDWORD CKFileVisitor::GetIndexByObjectID(CK_ID objid) { // see CKFile::SaveFindObjectIndex in IDA CKDWORD idx = static_cast(-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; } bool CKFileVisitor::AddSavedFile(CKSTRING u8FileName) { if (m_IsReader) { return false; } else { return m_Writer->AddSavedFile(u8FileName); } } #pragma endregion }