diff --git a/.gitignore b/.gitignore index 7fb4fbd..c91861f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,9 @@ *.vmo CodeGen/dest/*.txt PyCmoOld/ + out/ +temp/ .vscode/ diff --git a/LibCmo/CKDefines.hpp b/LibCmo/CKDefines.hpp index e28b38c..0c7af81 100644 --- a/LibCmo/CKDefines.hpp +++ b/LibCmo/CKDefines.hpp @@ -68,20 +68,12 @@ namespace LibCmo { class CKMinContext; class CKStateChunk; class CKFile; - namespace CKFileData { - class ShallowDocument; - class DeepDocument; - class HybridDocument; - } + class CKFileDocument; // useful struct define struct CKGUID { - union { - struct { - CKDWORD d1, d2; - }; - CKDWORD d[2]; - }; + CKDWORD d1, d2; + constexpr CKGUID(CKDWORD gd1 = 0, CKDWORD gd2 = 0) : d1(gd1), d2(gd2) {} CKGUID(const CKGUID& rhs) : d1(rhs.d1), d2(rhs.d2) {} CKGUID& operator=(const CKGUID& rhs) { @@ -136,12 +128,7 @@ namespace LibCmo { class VxMemoryMappedFile; struct VxVector { - union { - struct { - float x, y, z; - }; - float v[3]; - }; + float x, y, z; VxVector() : x(0.0f), y(0.0f), z(0.0f) { ; } VxVector(float f) : x(f), y(f), z(f) { ; } @@ -150,12 +137,7 @@ namespace LibCmo { }; struct VxQuaternion { - union { - struct { - float x, y, z, w; - }; - float v[4]; - }; + float x, y, z, w; VxQuaternion() : x(0.0f), y(0.0f), z(0.0f), w(1.0f) { ; } VxQuaternion(float X, float Y, float Z, float W) : x(X), y(Y), z(Z), w(W) { ; } diff --git a/LibCmo/CKFile.cpp b/LibCmo/CKFile.cpp index 132527e..f868d57 100644 --- a/LibCmo/CKFile.cpp +++ b/LibCmo/CKFile.cpp @@ -64,13 +64,15 @@ namespace LibCmo::CK2 { CKFileObject::CKFileObject() : ObjectId(0u), ObjectCid(CK_CLASSID::CKCID_OBJECT), Name(), - Data(nullptr), FileIndex(0u) { + ObjPtr(nullptr), Data(nullptr), FileIndex(0u) { } CKFileObject::CKFileObject(const CKFileObject& rhs) : ObjectId(rhs.ObjectId), ObjectCid(rhs.ObjectCid), Name(rhs.Name), - 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. + // however, we need copy CKStateChunk. if (this->Data != nullptr) { this->Data = new(std::nothrow) CKStateChunk(*(rhs.Data)); } @@ -83,6 +85,10 @@ namespace LibCmo::CK2 { this->Name = rhs.Name; this->FileIndex = rhs.FileIndex; + // CKObject is managed by CKMinContext, so we just copy its pointer. + this->ObjPtr = rhs.ObjPtr; + + // however, we need copy CKStateChunk. this->Data = rhs.Data; if (this->Data != nullptr) { this->Data = new(std::nothrow) CKStateChunk(*(rhs.Data)); @@ -100,11 +106,11 @@ namespace LibCmo::CK2 { #pragma region CKFileManagerData CKFileManagerData::CKFileManagerData() : - Data(nullptr), Manager(0u, 0u) { + MgrPtr(nullptr), Data(nullptr), Manager(0u, 0u) { } CKFileManagerData::CKFileManagerData(const CKFileManagerData& rhs) : - Data(rhs.Data), Manager(rhs.Manager) { + MgrPtr(rhs.MgrPtr), Data(rhs.Data), Manager(rhs.Manager) { if (this->Data != nullptr) { this->Data = new(std::nothrow) CKStateChunk(*(rhs.Data)); @@ -114,6 +120,7 @@ namespace LibCmo::CK2 { CKFileManagerData& CKFileManagerData::operator=(const CKFileManagerData& rhs) { this->Manager = rhs.Manager; + this->MgrPtr = rhs.MgrPtr; this->Data = rhs.Data; if (this->Data != nullptr) { @@ -151,34 +158,18 @@ namespace LibCmo::CK2 { #pragma endregion - namespace CKFileData { - #pragma region ShallowDocument - ShallowDocument::ShallowDocument() { - /*this->m_IndexByClassId.resize(static_cast(CK_CLASSID::CKCID_MAXCLASSID));*/ - } + CKFileDocument::CKFileDocument() { + /*this->m_IndexByClassId.resize(static_cast(CK_CLASSID::CKCID_MAXCLASSID));*/ + } - ShallowDocument::~ShallowDocument() { - - } - -#pragma endregion - -#pragma region DeepDocument - - DeepDocument::DeepDocument() { - /*this->m_IndexByClassId.resize(static_cast(CK_CLASSID::CKCID_MAXCLASSID));*/ - } - - DeepDocument::~DeepDocument() { - - } - -#pragma endregion + CKFileDocument::~CKFileDocument() { } +#pragma endregion + #pragma region CKFile Misc CKFile::CKFile(CKMinContext* ctx) : diff --git a/LibCmo/CKFile.hpp b/LibCmo/CKFile.hpp index 129a196..3382fc0 100644 --- a/LibCmo/CKFile.hpp +++ b/LibCmo/CKFile.hpp @@ -88,11 +88,12 @@ namespace LibCmo::CK2 { CKFileObject& operator=(const CKFileObject&); ~CKFileObject(); - CK_ID ObjectId; - CK_CLASSID ObjectCid; - std::string Name; - CKStateChunk* Data; - CKDWORD FileIndex; + CK_ID ObjectId; // ID of the object being load/saved (as it will be/was saved in the file) + CK_CLASSID ObjectCid; // Class Identifier of the object + CKObjectImplements::CKObject* ObjPtr; // A pointer to the object itself (as CreatedObject when loading) + std::string Name; // Name of the Object + CKStateChunk* Data; // A CKStateChunk that contains object information + CKDWORD FileIndex; // Position of the object data inside uncompressed file buffer private: }; @@ -104,6 +105,7 @@ namespace LibCmo::CK2 { CKFileManagerData& operator=(const CKFileManagerData&); ~CKFileManagerData(); + CKManagerImplements::CKBaseManager* MgrPtr; CKStateChunk* Data; CKGUID Manager; private: @@ -123,56 +125,22 @@ namespace LibCmo::CK2 { }; - namespace CKFileData { - class ShallowDocument { - public: - ShallowDocument(); - ShallowDocument(const ShallowDocument&) = delete; - ShallowDocument& operator=(const ShallowDocument&) = delete; - ~ShallowDocument(); + class CKFileDocument { + public: + CKFileDocument(); + ~CKFileDocument(); - int32_t m_SaveIDMax; - CKFileInfo m_FileInfo; + int32_t m_SaveIDMax; + CKFileInfo m_FileInfo; - XArray m_FileObjects; - XArray m_FileManagersData; - XClassArray m_PluginDep; - /*XClassArray m_IndexByClassId;*/ - XClassArray m_IncludedFiles; + XArray m_FileObjects; + XArray m_FileManagersData; + XClassArray m_PluginDep; + /*XClassArray m_IndexByClassId;*/ + XClassArray m_IncludedFiles; + private: - private: - - }; - - class DeepDocument { - public: - DeepDocument(); - DeepDocument(const DeepDocument&) = delete; - DeepDocument& operator=(const DeepDocument&) = delete; - ~DeepDocument(); - - int32_t m_SaveIDMax; - CKFileInfo m_FileInfo; - - XArray m_Objects; - /*XClassArray m_IndexByClassId;*/ - XClassArray m_IncludedFiles; - private: - - }; - - class HybridDocument { - public: - HybridDocument(); - HybridDocument(const HybridDocument&) = delete; - HybridDocument& operator=(const HybridDocument&) = delete; - ~HybridDocument(); - - private: - - }; - - } + }; class CKFile { public: @@ -183,16 +151,10 @@ namespace LibCmo::CK2 { void ClearData(void); - CKERROR ShallowLoad(CKSTRING u8_filename, CKFileData::ShallowDocument** out_doc); - CKERROR DeepLoad(CKSTRING u8_filename, CKFileData::DeepDocument** out_doc); + CKERROR ShallowLoad(CKSTRING u8_filename, CKFileDocument** out_doc); + CKERROR DeepLoad(CKSTRING u8_filename, CKFileDocument** out_doc); - CKERROR ShallowSave(CKSTRING u8_filename, CKFileData::ShallowDocument* in_doc); - CKERROR DeepSave(CKSTRING u8_filename, CKFileData::DeepDocument* out_doc); - CKERROR HybridSave(CKSTRING u8_filename, CKFileData::HybridDocument* out_doc); - - CKERROR DestroyDocument(CKFileData::ShallowDocument* in_doc); - CKERROR DestroyDocument(CKFileData::DeepDocument* in_doc); - CKERROR DestroyDocument(CKFileData::HybridDocument* in_doc); + CKERROR Save(CKSTRING u8_filename, CKFileDocument* in_doc); //CKERROR Load(CKSTRING u8_filename, /*CKObjectArray list, */ CK_LOAD_FLAGS flags); //CKERROR OpenFile(CKSTRING u8_filename, CK_LOAD_FLAGS flags); @@ -218,8 +180,8 @@ namespace LibCmo::CK2 { private: // reader function and variables - CKERROR ReadFileHeader(CKBufferParser* ParserPtr, CKFileData::ShallowDocument* doc); - CKERROR ReadFileData(CKBufferParser* ParserPtr, CKFileData::ShallowDocument* doc); + CKERROR ReadFileHeader(CKBufferParser* ParserPtr, CKFileDocument* doc); + CKERROR ReadFileData(CKBufferParser* ParserPtr, CKFileDocument* doc); // writer function and varibales diff --git a/LibCmo/CKFileReader.cpp b/LibCmo/CKFileReader.cpp index 9cceb34..901c1fa 100644 --- a/LibCmo/CKFileReader.cpp +++ b/LibCmo/CKFileReader.cpp @@ -15,7 +15,7 @@ namespace LibCmo::CK2 { * No need to support them. */ - CKERROR CKFile::ShallowLoad(CKSTRING u8_filename, CKFileData::ShallowDocument** out_doc) { + CKERROR CKFile::ShallowLoad(CKSTRING u8_filename, CKFileDocument** out_doc) { // preset value *out_doc = nullptr; @@ -32,7 +32,7 @@ namespace LibCmo::CK2 { } // create document - std::unique_ptr doc(new(std::nothrow) CKFileData::ShallowDocument()); + std::unique_ptr doc(new(std::nothrow) CKFileDocument()); if (doc == nullptr) return CKERROR::CKERR_OUTOFMEMORY; // create buffer and start loading @@ -48,7 +48,7 @@ namespace LibCmo::CK2 { return CKERROR::CKERR_OK; } - CKERROR CKFile::ReadFileHeader(CKBufferParser* ParserPtr, CKFileData::ShallowDocument* doc) { + CKERROR CKFile::ReadFileHeader(CKBufferParser* ParserPtr, CKFileDocument* doc) { std::unique_ptr parser(new(std::nothrow) CKBufferParser(ParserPtr->GetBase(), ParserPtr->GetSize(), false)); if (parser == nullptr) return CKERROR::CKERR_OUTOFMEMORY; parser->SetCursor(ParserPtr->GetCursor()); @@ -149,7 +149,6 @@ namespace LibCmo::CK2 { // ========== dep list read ========== // file ver >= 8 have this feature - bool noDepLost = true; if (doc->m_FileInfo.FileVersion >= 8) { // get size and resize CKDWORD depSize; @@ -205,7 +204,7 @@ namespace LibCmo::CK2 { return CKERROR::CKERR_OK; } - CKERROR CKFile::ReadFileData(CKBufferParser* ParserPtr, CKFileData::ShallowDocument* doc) { + CKERROR CKFile::ReadFileData(CKBufferParser* ParserPtr, CKFileDocument* doc) { std::unique_ptr parser(new(std::nothrow) CKBufferParser(ParserPtr->GetBase(), ParserPtr->GetSize(), false)); if (parser == nullptr) return CKERROR::CKERR_OUTOFMEMORY; parser->SetCursor(ParserPtr->GetCursor()); @@ -351,32 +350,27 @@ namespace LibCmo::CK2 { return CKERROR::CKERR_OK; } - CKERROR CKFile::DeepLoad(CKSTRING u8_filename, CKFileData::DeepDocument** out_doc) { + CKERROR CKFile::DeepLoad(CKSTRING u8_filename, CKFileDocument** out_doc) { // ========== prepare work ========== // preset value *out_doc = nullptr; - + CKERROR err = CKERROR::CKERR_OK; + // get shallow document first - CKFileData::ShallowDocument* rawShallowDoc = nullptr; - CKERROR err = this->ShallowLoad(u8_filename, &rawShallowDoc); + CKFileDocument* rawShallowDoc = nullptr; + err = this->ShallowLoad(u8_filename, &rawShallowDoc); if (rawShallowDoc == nullptr) return err; - std::unique_ptr shallowDoc(rawShallowDoc); + // use unique ptr wrap it as a deep doc + std::unique_ptr deepDoc(rawShallowDoc); if (err != CKERROR::CKERR_OK) return err; - // create deep document - std::unique_ptr deepDoc(new(std::nothrow) CKFileData::DeepDocument()); - if (deepDoc == nullptr) return CKERROR::CKERR_OUTOFMEMORY; - // ========== create object first ========== - size_t index = 0u; - std::vector createdObjs(shallowDoc->m_FileObjects.size(), nullptr); - for (index = 0u; index < shallowDoc->m_FileObjects.size(); ++index) { + for (auto& obj : deepDoc->m_FileObjects) { // todo: skip CK_LEVEL // todo: resolve references - const auto& obj = shallowDoc->m_FileObjects[index]; if (obj.Data == nullptr) continue; - createdObjs[index] = m_MinCtx->CreateCKObject(obj.ObjectId, obj.ObjectCid, obj.Name.c_str()); + obj.ObjPtr = m_MinCtx->CreateCKObject(obj.ObjectId, obj.ObjectCid, obj.Name.c_str()); } // ========== CKStateChunk remap ========== @@ -387,523 +381,29 @@ namespace LibCmo::CK2 { // todo... // ========== analyze objects CKStateChunk ========== - for (index = 0u; index < shallowDoc->m_FileObjects.size(); ++index) { - const auto& obj = shallowDoc->m_FileObjects[index]; - if (obj.Data == nullptr || createdObjs[index] == nullptr) continue; + for (auto& obj : deepDoc->m_FileObjects) { + if (obj.Data == nullptr || obj.ObjPtr == nullptr) continue; // todo: special treat for CK_LEVEL // try parsing data obj.Data->StartRead(); - CKERROR err = createdObjs[index]->Load(obj.Data, shallowDoc.get()); + err = obj.ObjPtr->Load(obj.Data, deepDoc.get()); obj.Data->StopRead(); if (err != CKERROR::CKERR_OK) { - delete (createdObjs[index]); - createdObjs[index] = nullptr; + // if failed, delete it + m_MinCtx->DestroyCKObject(obj.ObjectId); + obj.ObjPtr = nullptr; } else { - // add into result - deepDoc->m_Objects.push_back(createdObjs[index]); + // if success, clear CKStateChunk* + delete obj.Data; + obj.Data = nullptr; } - } // ========== finalize work ========== - // copy misc structure - deepDoc->m_IncludedFiles = shallowDoc->m_IncludedFiles; - deepDoc->m_SaveIDMax = shallowDoc->m_SaveIDMax; - deepDoc->m_FileInfo = shallowDoc->m_FileInfo; // detach and return *out_doc = deepDoc.release(); return CKERROR::CKERR_OK; } - - //CKERROR CKFile::Load(CKSTRING u8_filename, /*CKObjectArray list, */ CK_LOAD_FLAGS flags) { - // CKERROR result = this->OpenFile(u8_filename, flags); - // if (result == CKERROR::CKERR_OK || result == CKERROR::CKERR_PLUGINSMISSING) { - // result = this->LoadFileData(); - // } - - // return result; - //} - - //CKERROR CKFile::OpenFile(CKSTRING u8_filename, CK_LOAD_FLAGS flags) { - // this->ClearData(); - // if (u8_filename == nullptr) return CKERROR::CKERR_INVALIDPARAMETER; - - // this->m_FileName = u8_filename; - // this->m_MappedFile = new(std::nothrow) VxMemoryMappedFile(this->m_FileName.c_str()); - // if (this->m_MappedFile == nullptr) return CKERROR::CKERR_OUTOFMEMORY; - // if (!this->m_MappedFile->IsValid()) return CKERROR::CKERR_INVALIDFILE; - - // // MARK: CKContext::SetLastCmoLoaded - // return this->OpenMemory(this->m_MappedFile->GetBase(), this->m_MappedFile->GetFileSize(), flags); - //} - - //CKERROR CKFile::OpenMemory(void* MemoryBuffer, size_t BufferSize, CK_LOAD_FLAGS Flags) { - // if (MemoryBuffer == nullptr) return CKERROR::CKERR_INVALIDPARAMETER; - // // compare magic words - // if (BufferSize < 0x20 || memcmp(MemoryBuffer, "Nemo", 4u)) return CKERROR::CKERR_INVALIDFILE; - - // this->m_Parser = new(std::nothrow) CKBufferParser(MemoryBuffer, BufferSize, false); - // if (this->m_Parser == nullptr) return CKERROR::CKERR_OUTOFMEMORY; - // // MARK: eliminate check of m_Parser->m_ReaderBegin - - // // MARK: dword_2405F6C0 = 0; - // this->m_Flags = Flags; - // this->m_IndexByClassId.resize(static_cast(CK_CLASSID::CKCID_MAXCLASSID)); - // return this->ReadFileHeaders(&(this->m_Parser)); - //} - - //CKERROR CKFile::ReadFileHeaders(CKBufferParser** ParserPtr) { - // CKBufferParser* parser = *ParserPtr; - // this->m_IncludedFiles.clear(); - - // // ========== read header1 ========== - // CKDWORD fhdr1[8]; - // CKDWORD fhdr2[8]; - // if (parser->GetSize() < sizeof(fhdr1)) return CKERROR::CKERR_INVALIDFILE; - // parser->Read(fhdr1, sizeof(fhdr1)); - - // if (fhdr1[5]) { // it seems that there is a ZERO checker? - // memset(fhdr1, 0, sizeof(fhdr1)); - // } - // // check outdated - // if (fhdr1[4] > 9) return CKERROR::CKERR_OBSOLETEVIRTOOLS; - - // // ========== read header2 ========== - // // file ver < 5 do not have second header - // if (fhdr1[4] < 5) { - // memset(fhdr2, 0, sizeof(fhdr2)); - // } else { - // if (parser->GetSize() < sizeof(fhdr1) + sizeof(fhdr2)) return CKERROR::CKERR_INVALIDFILE; - // parser->Read(fhdr2, sizeof(fhdr2)); - // } - - // // forcely reset too big product ver - // if (fhdr2[5] >= 12) { - // fhdr2[5] = 0; - // fhdr2[6] = 0x1010000; - // } - - // // ========== assign value ========== - // this->m_FileInfo.ProductVersion = fhdr2[5]; - // this->m_FileInfo.ProductBuild = fhdr2[6]; - // this->m_FileInfo.FileWriteMode = static_cast(fhdr1[6]); - // this->m_FileInfo.CKVersion = fhdr1[3]; - // this->m_FileInfo.FileVersion = fhdr1[4]; - // this->m_FileInfo.FileSize = parser->GetSize(); - // this->m_FileInfo.ManagerCount = fhdr2[2]; - // this->m_FileInfo.ObjectCount = fhdr2[3]; - // this->m_FileInfo.MaxIDSaved = fhdr2[4]; - // this->m_FileInfo.Hdr1PackSize = fhdr1[7]; - // this->m_FileInfo.Hdr1UnPackSize = fhdr2[7]; - // this->m_FileInfo.DataPackSize = fhdr2[0]; - // this->m_FileInfo.DataUnPackSize = fhdr2[1]; - // this->m_FileInfo.Crc = fhdr1[2]; - - // // ========== crc and body unpacker ========== - // if (this->m_FileInfo.FileVersion >= 8) { - // // crc checker for file ver >= 8 - // // reset crc field of header - // fhdr1[2] = 0; - - // // compute crc - // CKDWORD gotten_crc = CKComputeDataCRC(&fhdr1, sizeof(fhdr1), 0u); - // parser->SetCursor(sizeof(fhdr1)); - // gotten_crc = CKComputeDataCRC(parser->GetPtr(), sizeof(fhdr2), gotten_crc); - // parser->MoveCursor(sizeof(fhdr2)); - // gotten_crc = CKComputeDataCRC(parser->GetPtr(), this->m_FileInfo.Hdr1PackSize, gotten_crc); - // parser->MoveCursor(this->m_FileInfo.Hdr1PackSize); - // gotten_crc = CKComputeDataCRC(parser->GetPtr(), this->m_FileInfo.DataPackSize, gotten_crc); - // parser->SetCursor(sizeof(fhdr1) + sizeof(fhdr2)); - - // if (gotten_crc != this->m_FileInfo.Crc) return CKERROR::CKERR_FILECRCERROR; - - // // compare size to decide wheher use compress feature - // void* decomp_buffer = CKUnPackData(this->m_FileInfo.Hdr1UnPackSize, parser->GetPtr(), this->m_FileInfo.Hdr1PackSize); - // if (decomp_buffer != nullptr) { - // parser = new(std::nothrow) CKBufferParser(decomp_buffer, this->m_FileInfo.Hdr1UnPackSize, true); - // if (parser == nullptr) { - // delete[] decomp_buffer; - // return CKERROR::CKERR_OUTOFMEMORY; - // } - // } - // } - - // // ========== object list read ========== - // // file ver >= 7 have this features - // if (this->m_FileInfo.FileVersion >= 7) { - // // apply max id saved - // this->m_SaveIDMax = this->m_FileInfo.MaxIDSaved; - // // resize - // this->m_FileObject.resize(this->m_FileInfo.ObjectCount); - - // // read data - // for (auto& fileobj : this->m_FileObject) { - // // setup useless fields - // fileobj.ObjPtr = nullptr; - // fileobj.Data = nullptr; - - // // read basic fields - // parser->Read(&(fileobj.Object), sizeof(CK_ID)); - // parser->Read(&(fileobj.ObjectCid), sizeof(CK_CLASSID)); - // parser->Read(&(fileobj.FileIndex), sizeof(CKDWORD)); - - // CKDWORD namelen; - // parser->Read(&namelen, sizeof(CKDWORD)); - // if (namelen != 0) { - // fileobj.Name.resize(namelen); - // parser->Read(fileobj.Name.data(), namelen); - // } - // } - // } - - // // ========== dep list read ========== - // // file ver >= 8 have this feature - // bool noDepLost = true; - // if (this->m_FileInfo.FileVersion >= 8) { - // // get size and resize - // CKDWORD depSize; - // parser->Read(&depSize, sizeof(CKDWORD)); - // this->m_PluginDep.resize(depSize); - - // CKDWORD guid_size; - // for (auto& dep : this->m_PluginDep) { - // // read category - // parser->Read(&(dep.m_PluginCategory), sizeof(CK_PLUGIN_TYPE)); - // // get size and resize - // parser->Read(&guid_size, sizeof(CKDWORD)); - // dep.m_Guids.resize(guid_size); - // dep.ValidGuids.resize(guid_size); - // // read data - // if (guid_size != 0) { - // parser->Read(dep.m_Guids.data(), sizeof(CKGUID) * guid_size); - // } - - // // extra load flag - // if (EnumHelper::FlagEnumHas(this->m_Flags, CK_LOAD_FLAGS::CK_LOAD_CHECKDEPENDENCIES)) { - // for (size_t i = 0u; i < dep.m_Guids.size(); ++i) { - // // MARK: CKPluginManager::FindComponent - // bool plgEntryIsNull = true; - // if (plgEntryIsNull) { - // noDepLost = false; - // dep.ValidGuids[i] = false; - // } else { - // dep.ValidGuids[i] = true; - // } - // } - // } - - // } - // } - - // // ========== included file list read ========== - // // file ver >= 8 have this feature - // if (this->m_FileInfo.FileVersion >= 8) { - // // MARK: i don't knwo what is this! - // int32_t hasIncludedFile; - // parser->Read(&hasIncludedFile, sizeof(int32_t)); - - // if (hasIncludedFile > 0) { - // // read included file size and resize - // CKDWORD includedFileCount; - // parser->Read(&includedFileCount, sizeof(CKDWORD)); - // this->m_IncludedFiles.resize(includedFileCount); - - // hasIncludedFile -= 4; - // } - - // // backward pos - // parser->SetCursor(hasIncludedFile); - // } - - // // ========== read data ========== - // // restore old parser if needed - // if (parser != *ParserPtr) { - // delete parser; - // parser = *ParserPtr; - // parser->MoveCursor(this->m_FileInfo.Hdr1PackSize); - // } - - // // MARK: dword_2405F6BC, dword_2405F6B8 set - - // if (!(EnumHelper::FlagEnumHas(this->m_Flags, CK_LOAD_FLAGS::CK_LOAD_CHECKDEPENDENCIES) && - // this->m_FileInfo.FileVersion < 8)) { - // // we have read all header. return result - // return noDepLost ? CKERROR::CKERR_OK : CKERROR::CKERR_PLUGINSMISSING; - // } // however, if we need check dep, and, we have not read dep list. continue read them. - - // return CKERROR::CKERR_OK; - //} - - //CKERROR CKFile::ReadFileData(CKBufferParser** ParserPtr) { - // CKBufferParser* parser = *ParserPtr; - // CKBufferParser* parserSrc = *ParserPtr; - - // // ========== compress feature process ========== - // if (EnumHelper::FlagEnumHas(this->m_FileInfo.FileWriteMode, CK_FILE_WRITEMODE::CKFILE_CHUNKCOMPRESSED_OLD) || - // EnumHelper::FlagEnumHas(this->m_FileInfo.FileWriteMode, CK_FILE_WRITEMODE::CKFILE_WHOLECOMPRESSED)) { - // void* decomp_buffer = CKUnPackData(this->m_FileInfo.DataUnPackSize, parser->GetPtr(), this->m_FileInfo.DataPackSize); - // parser->MoveCursor(this->m_FileInfo.DataPackSize); - - // if (decomp_buffer != nullptr) { - // parser = new(std::nothrow) CKBufferParser(decomp_buffer, this->m_FileInfo.DataUnPackSize, true); - // if (parser == nullptr) { - // delete[] decomp_buffer; - // return CKERROR::CKERR_OUTOFMEMORY; - // } - - // // sync to args - // *ParserPtr = parser; - // } - // } - - // // ========== old file crc and obj list read ========== - // // only file ver < 8 run this - // if (this->m_FileInfo.FileVersion < 8) { - // // very very old flag - // if (this->m_FileInfo.FileVersion < 2) { - // ; - // // MARK: dword_2405F6C0 setter - // } - - // // check crc - // CKDWORD gotten_crc = CKComputeDataCRC( - // parser->GetPtr(), - // parser->GetSize() - parser->GetCursor(), - // 0u - // ); - // if (gotten_crc != this->m_FileInfo.Crc) { - // // MARK: report crc error - // return CKERROR::CKERR_FILECRCERROR; - // } - - // // get save id max - // parser->Read(&this->m_SaveIDMax, sizeof(int32_t)); - // // get object count and resize - // parser->Read(&this->m_FileInfo.ObjectCount, sizeof(CKDWORD)); - // if (this->m_FileObject.empty()) { - // this->m_FileObject.resize(this->m_FileInfo.ObjectCount); - // // MARK: ZeroMemory removed. i think it is not necessary. - // } - // } - - // // ========== manager read ========== - // // only file ver >= 6 have this - // if (this->m_FileInfo.FileVersion >= 6 && this->m_FileInfo.ManagerCount != 0) { - // this->m_ManagersData.resize(this->m_FileInfo.ManagerCount); - // CKDWORD stateChunkLen = 0u; - // bool stateChkParseSuccess = false; - - // for (auto& mgr : this->m_ManagersData) { - // // read guid - // parser->Read(&(mgr.Manager), sizeof(CKGUID)); - - // // read statechunk len - // parser->Read(&stateChunkLen, sizeof(CKDWORD)); - // // check len - // if (stateChunkLen == 0) { - // mgr.Data = nullptr; - // continue; - // } - // // read statechunk - // mgr.Data = new(std::nothrow) CKStateChunk(); - // if (mgr.Data != nullptr) { - // stateChkParseSuccess = mgr.Data->ConvertFromBuffer(parser->GetPtr()); - // if (!stateChkParseSuccess) { - // delete mgr.Data; - // mgr.Data = nullptr; - // } - // } - // parser->MoveCursor(stateChunkLen); - // } - // } - - // // ========== object read ========== - // if (this->m_FileInfo.ObjectCount != 0) { - // if (this->m_FileInfo.FileVersion >= 4) { - // // new file reader section - // CKDWORD stateChunkLen = 0u; - // bool stateChkParseSuccess = false; - // for (auto& obj : this->m_FileObject) { - // if (this->m_FileInfo.FileVersion < 7) { - // // it seems that file ver < 7, ck id was stored first. - // parser->Read(&(obj.Object), sizeof(CK_ID)); - // } - // // get statechunk len - // parser->Read(&stateChunkLen, sizeof(CKDWORD)); - - // // if only load behavior and current loaded obj is not behavior, give up. - // // >= 7 is corresponding with obj header version requirement - // // so don't worry about this - // if (EnumHelper::FlagEnumHas(this->m_Flags, CK_LOAD_FLAGS::CK_LOAD_ONLYBEHAVIORS) && - // this->m_FileInfo.FileVersion >= 7 && - // obj.ObjectCid != CK_CLASSID::CKCID_BEHAVIOR) { - // // move cursor for next one. - // parser->MoveCursor(stateChunkLen); - // continue; - // } - - // // check state chunk len - // if (stateChunkLen == 0) { - // obj.Data = nullptr; - // continue; - // } - // // read state chunk - // obj.Data = new(std::nothrow) CKStateChunk(); - // if (obj.Data != nullptr) { - // stateChkParseSuccess = obj.Data->ConvertFromBuffer(parser->GetPtr()); - // if (!stateChkParseSuccess) { - // delete obj.Data; - // obj.Data = nullptr; - // } else { - // // get data size and fill some fields - // obj.PostPackSize = obj.Data->GetDataSize(); - // obj.PrePackSize = obj.PostPackSize; - // } - // } - // parser->MoveCursor(stateChunkLen); - - // } - // } else { - // // old file read section - // for (auto& obj : this->m_FileObject) { - // // read CK_ID - // parser->Read(&(obj.Object), sizeof(CK_ID)); - // // get data len - // CKDWORD dataLen = 0u; - // parser->Read(&dataLen, sizeof(CKDWORD)); - - // // check state chunk len - // if (dataLen == 0) { - // obj.Data = nullptr; - // continue; - // } - - // // MARK: set dword_2405F6C0 - // - // // read class id - // CK_CLASSID clsid = CK_CLASSID::CKCID_OBJECT; - // parser->Read(&clsid, sizeof(CK_CLASSID)); - // if (static_cast(clsid) == UINT32_C(0)) { - // // invalid cid; - // return CKERROR::CKERR_INVALIDFILE; - // } - - // // read save flags - // parser->Read(&obj.SaveFlags, sizeof(CK_FILE_WRITEMODE)); - - // // read statechunk len - // CKDWORD oldBufLen = 0u; - // parser->Read(&oldBufLen, sizeof(CKDWORD)); - - // // setup state chunk and order parse it - // obj.Data = new(std::nothrow) CKStateChunk(clsid); - // if (obj.Data != nullptr) { - // if (!obj.Data->ConvertFromOldBuffer(parser->GetPtr(), oldBufLen, dataLen, obj.SaveFlags)) { - // delete obj.Data; - // obj.Data = nullptr; - // } - // } - // // move to next - // parser->MoveCursor(oldBufLen); - // } - - // // MARK: remove a weird assign: parserSrc = v46; it seems useless. - // } - // } - - // // ========== object name get ========== - // // only file ver < 7 need get it from statechunk - // if (this->m_FileInfo.FileVersion < 7) { - // for (auto& obj : this->m_FileObject) { - // if (obj.Data != nullptr) { - // // TODO: CK_STATESAVE_NAME - // if (obj.Data->SeekIdentifier(1u)) { - // obj.Data->ReadString(obj.Name); - // } - // } - // } - // } - - // // ========== included file get ========== - // // WARN: we use "parserSrc" to read, not "parser"!!! - // for (size_t i = 0; i < this->m_IncludedFiles.size(); ++i) { - // // get file name length and resize it - // CKDWORD filenamelen = 0u; - // parserSrc->Read(&filenamelen, sizeof(CKDWORD)); - // std::string filename; - // filename.resize(filenamelen); - - // // read filename - // if (filenamelen != 0) { - // parserSrc->Read(filename.data(), filenamelen); - // } - - // // todo: construct temp folder path - // // and regulate file name - - // // read file body length - // CKDWORD filebodylen = 0u; - // parserSrc->Read(&filebodylen, sizeof(CKDWORD)); - - // // todo: read file body - // parserSrc->MoveCursor(filebodylen); - // } - - // // ========== free parser ========== - // if (parserSrc != parser && parserSrc != nullptr) { - // // WARN: we should free parserSrc! not free parser - // // because "parser" has synced with ParserPtr and will be freed from outside. - // delete parserSrc; - - // } - - // return CKERROR::CKERR_OK; - //} - - //CKERROR CKFile::LoadFileData(void/*CKObjectArray list*/) { - // if (!this->m_Parser && !this->m_ReadFileDataDone) { - // return CKERROR::CKERR_INVALIDFILE; - // } - - // // MARK: sub_240372EA, ctx + 193 = 1 - - // // if file data has not been read. read it - // CKERROR err = CKERROR::CKERR_OK; - // CKBufferParser** ParserPtr = &this->m_Parser; - // if (!this->m_ReadFileDataDone) { - // err = this->ReadFileData(ParserPtr); - // } - - // // MARK: i assume FinishLoading do not calling mapped file anymore - // // free file data - // if (*ParserPtr != nullptr) { - // delete *ParserPtr; - // *ParserPtr = nullptr; - // } - // if (this->m_MappedFile != nullptr) { - // delete this->m_MappedFile; - // this->m_MappedFile = nullptr; - // } - - // // if no error, do finish loading - // if (err == CKERROR::CKERR_OK) { - // this->FinishLoading(this->m_Flags); - - // // MARK: dword_2405F6C0 old vt ver output - // } - // // MARK: CKContext::SetAutomaticLoadMode - // // MARK:CKContext::SetUserLoadCallback - - // // MARK: sub_24037360 - // // MARK: ctx + 193 = 0 - - // return err; - //} - - //CKERROR CKFile::FinishLoading(/*CKObjectArray list, */CK_LOAD_FLAGS flags) { - // return CKERROR::CKERR_OK; - //} - } diff --git a/LibCmo/CKManagerImplements/CKBaseManager.cpp b/LibCmo/CKManagerImplements/CKBaseManager.cpp index c0555e2..d2bcfa4 100644 --- a/LibCmo/CKManagerImplements/CKBaseManager.cpp +++ b/LibCmo/CKManagerImplements/CKBaseManager.cpp @@ -9,10 +9,10 @@ namespace LibCmo::CK2::CKManagerImplements { } - CKERROR CKBaseManager::LoadData(CKStateChunk* statechunk, CKFileData::ShallowDocument* doc) { + CKERROR CKBaseManager::LoadData(CKStateChunk* statechunk, CKFileDocument* doc) { return CKERROR::CKERR_OK; } - CKStateChunk* CKBaseManager::SaveData(CKFileData::ShallowDocument* doc) { + CKStateChunk* CKBaseManager::SaveData(CKFileDocument* doc) { return nullptr; } diff --git a/LibCmo/CKManagers.hpp b/LibCmo/CKManagers.hpp index 70c06e6..cb7e61b 100644 --- a/LibCmo/CKManagers.hpp +++ b/LibCmo/CKManagers.hpp @@ -12,8 +12,8 @@ namespace LibCmo::CK2::CKManagerImplements { CKBaseManager& operator=(const CKBaseManager&) = delete; virtual ~CKBaseManager(); - virtual CKERROR LoadData(CKStateChunk* statechunk, CKFileData::ShallowDocument* doc); - virtual CKStateChunk* SaveData(CKFileData::ShallowDocument* doc); + virtual CKERROR LoadData(CKStateChunk* statechunk, CKFileDocument* doc); + virtual CKStateChunk* SaveData(CKFileDocument* doc); private: diff --git a/LibCmo/CKMinContext.cpp b/LibCmo/CKMinContext.cpp index b5ac17a..fdf9f4b 100644 --- a/LibCmo/CKMinContext.cpp +++ b/LibCmo/CKMinContext.cpp @@ -42,6 +42,11 @@ namespace LibCmo::CK2 { std::filesystem::create_directory(m_TempFolder); } CKMinContext::~CKMinContext() { + // free all created objects + for (const auto& [key, value] : this->m_ObjectsList) { + delete value; + } + // todo: free all created managers } diff --git a/LibCmo/CKObjectImplements/CKObject.cpp b/LibCmo/CKObjectImplements/CKObject.cpp index b8938d5..69f9b05 100644 --- a/LibCmo/CKObjectImplements/CKObject.cpp +++ b/LibCmo/CKObjectImplements/CKObject.cpp @@ -16,7 +16,7 @@ namespace LibCmo::CK2::CKObjectImplements { } - CKERROR CKObject::Load(CKStateChunk* chunk, const CKFileData::ShallowDocument* doc) { + CKERROR CKObject::Load(CKStateChunk* chunk, const CKFileDocument* doc) { if (chunk->SeekIdentifier(Identifiers::CK_STATESAVEFLAGS_OBJECT::CK_STATESAVE_OBJECTHIDDEN)) { EnumsHelper::FlagEnumRm(this->m_ObjectFlags, { CK_OBJECT_FLAGS::CK_OBJECT_VISIBLE, @@ -45,7 +45,7 @@ namespace LibCmo::CK2::CKObjectImplements { return CKERROR::CKERR_OK; } - CKStateChunk* CKObject::Save(CKFileData::ShallowDocument* doc) { + CKStateChunk* CKObject::Save(const CKFileDocument* doc) { return nullptr; } diff --git a/LibCmo/CKObjects.hpp b/LibCmo/CKObjects.hpp index 734629d..0c31693 100644 --- a/LibCmo/CKObjects.hpp +++ b/LibCmo/CKObjects.hpp @@ -25,8 +25,8 @@ namespace LibCmo::CK2::CKObjectImplements { void SetObjectFlags(CK_OBJECT_FLAGS flags) { this->m_ObjectFlags = flags; } virtual CK_CLASSID GetClassID(void) { return CK_CLASSID::CKCID_OBJECT; } - virtual CKERROR Load(CKStateChunk* chunk, const CKFileData::ShallowDocument* doc); - virtual CKStateChunk* Save(CKFileData::ShallowDocument* doc); + virtual CKERROR Load(CKStateChunk* chunk, const CKFileDocument* doc); + virtual CKStateChunk* Save(const CKFileDocument* doc); }; class CKSceneObject : public CKObject { diff --git a/LibCmo/CKStateChunk.hpp b/LibCmo/CKStateChunk.hpp index b692458..9bfbbf5 100644 --- a/LibCmo/CKStateChunk.hpp +++ b/LibCmo/CKStateChunk.hpp @@ -85,7 +85,7 @@ namespace LibCmo::CK2 { if (EnsureReadSpace(static_cast(size))) { std::memcpy(data, this->m_pData + this->m_Parser.m_CurrentPos, size); - } return CKERROR::CKERR_OUTOFMEMORY; + } else return CKERROR::CKERR_OUTOFMEMORY; return CKERROR::CKERR_OK; } /* diff --git a/LibCmo/LibCmo.vcxproj b/LibCmo/LibCmo.vcxproj index 7fd53fe..18da604 100644 --- a/LibCmo/LibCmo.vcxproj +++ b/LibCmo/LibCmo.vcxproj @@ -96,7 +96,7 @@ - Level3 + Level4 true _CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true @@ -113,7 +113,7 @@ - Level3 + Level4 true true true @@ -134,7 +134,7 @@ - Level3 + Level4 true _CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true @@ -153,7 +153,7 @@ - Level3 + Level4 true true true diff --git a/LibCmo/VTEncoding.cpp b/LibCmo/VTEncoding.cpp index 50c6b77..877f87b 100644 --- a/LibCmo/VTEncoding.cpp +++ b/LibCmo/VTEncoding.cpp @@ -29,11 +29,11 @@ namespace LibCmo::EncodingHelper { int count, write_result; //converter to CHAR - count = WideCharToMultiByte(CP_UTF8, 0, src, -1, NULL, 0, NULL, NULL); + count = WideCharToMultiByte(codepage, 0, src, -1, NULL, 0, NULL, NULL); if (count <= 0) return false; dest.resize(count); - write_result = WideCharToMultiByte(CP_UTF8, 0, src, -1, dest.data(), count, NULL, NULL); + write_result = WideCharToMultiByte(codepage, 0, src, -1, dest.data(), count, NULL, NULL); if (write_result <= 0) return false; return true; @@ -50,7 +50,7 @@ namespace LibCmo::EncodingHelper { if (wcount <= 0) return false; dest.resize(wcount); - write_result = MultiByteToWideChar(CP_UTF8, 0, src, -1, dest.data(), wcount); + write_result = MultiByteToWideChar(codepage, 0, src, -1, dest.data(), wcount); if (write_result <= 0) return false; return true; @@ -71,9 +71,9 @@ namespace LibCmo::EncodingHelper { #else - static constexpr const IconvInc = 16; - bool DoIconv(const char* enc_from, const char* enc_to, - std::string& str_from, std::string& str_to) { + static constexpr const size_t IconvInc = 16; + + bool DoIconv(const char* enc_from, const char* enc_to, std::string& str_from, std::string& str_to) { iconv_t cd; char *inbuf = nullptr, *outbuf = nullptr; size_t inbytesleft, outbytesleft, nchars, result_len; @@ -199,7 +199,7 @@ namespace LibCmo::EncodingHelper { #else - static const char UTF8_SYMBOL[] = "UTF-8"; + static constexpr const char UTF8_SYMBOL[] = "UTF-8"; ENCODING_TOKEN CreateEncodingToken(std::string& token_string) { ENCODING_TOKEN token = new(std::nothrow) char[token_string.size() + 1]; diff --git a/Unvirt/CmdHelper.cpp b/Unvirt/CmdHelper.cpp index 15d7dc4..864a9c1 100644 --- a/Unvirt/CmdHelper.cpp +++ b/Unvirt/CmdHelper.cpp @@ -1,78 +1,243 @@ #include "CmdHelper.hpp" #include "TerminalHelper.hpp" -#include "VTUtils.hpp" -#include "VTEncoding.hpp" + +#include +#include + #include +#include +#include -/* - -namespace Unvirt { - namespace CmdHelper { +namespace Unvirt::CmdHelper { #pragma region CmdSplitter - CmdSplitter::CmdSplitter() : - mCmdChar(0), mBuffer(nullptr), mResult(nullptr), - mState(StateType::NORMAL), mPreState(StateType::NORMAL) { - ; - } - CmdSplitter::~CmdSplitter() { - ; - } + CmdSplitter::CmdSplitter() : + mCmdChar(0), mBuffer(nullptr), mResult(nullptr), + mState(StateType::NORMAL), mPreState(StateType::NORMAL) { + ; + } + CmdSplitter::~CmdSplitter() { + ; + } - const std::vector CmdSplitter::Convert(const std::string& u8cmd) { - // set up variables - std::vector result; - std::string buffer; - mBuffer = &buffer; - mResult = &result; - mState = mPreState = StateType::SPACE; + const std::deque CmdSplitter::Convert(const std::string& u8cmd) { + // set up variables + std::deque result; + std::string buffer; + mBuffer = &buffer; + mResult = &result; + mState = mPreState = StateType::SPACE; - // split - for (auto it = u8cmd.begin(); it != u8cmd.end(); ++it) { - mCmdChar = (*it); + // split + for (auto it = u8cmd.begin(); it != u8cmd.end(); ++it) { + mCmdChar = (*it); - switch (mState) { - case StateType::SPACE: - ProcSpace(); - break; - case StateType::SINGLE: - ProcSingle(); - break; - case StateType::DOUBLE: - ProcDouble(); - break; - case StateType::ESCAPE: - ProcEscape(); - break; - case StateType::NORMAL: - ProcNormal(); - break; - } - } - - // final proc switch (mState) { case StateType::SPACE: - break; - case StateType::NORMAL: - // push the last one - mResult->push_back(*mBuffer); + ProcSpace(); break; case StateType::SINGLE: + ProcSingle(); + break; case StateType::DOUBLE: + ProcDouble(); + break; case StateType::ESCAPE: - // error - result.clear(); + ProcEscape(); + break; + case StateType::NORMAL: + ProcNormal(); break; } - - // return value - return result; } + // final proc + switch (mState) { + case StateType::SPACE: + break; + case StateType::NORMAL: + // push the last one + mResult->push_back(*mBuffer); + break; + case StateType::SINGLE: + case StateType::DOUBLE: + case StateType::ESCAPE: + // error + result.clear(); + break; + } + + // return value + return result; + } + #pragma endregion +#pragma region ArgParser + + bool ArgParser::ParseInt(const std::vector& cmd, const size_t expected_index, int32_t& result) { + if (expected_index >= cmd.size()) { + result = 0; + return false; + } + + char* pend = nullptr; + errno = 0; + int64_t v = std::strtoll(cmd[expected_index].c_str(), &pend, 10); + + if (pend == cmd[expected_index].c_str() || errno == ERANGE) { + result = 0; + return false; + } + + result = static_cast(v); + return true; + } + + bool ArgParser::ParseString(const std::vector& cmd, const size_t expected_index, std::string& result) { + if (expected_index >= cmd.size()) { + result.clear(); + return false; + } else { + result = cmd[expected_index]; + return true; + } + } + + bool ArgParser::ParseSwitch(const std::vector& cmd, const size_t expected_index, const std::vector& switches, std::string& gotten) { + if (expected_index >= cmd.size()) { + gotten.clear(); + return false; + } + + for (const auto& sw : switches) { + if (cmd[expected_index] == sw) { + gotten = cmd[expected_index]; + return true; + } + } + + gotten.clear(); + return false; + } + +#pragma endregion + +#pragma region InteractiveCmd + + InteractiveCmd::InteractiveCmd() : + m_CmdSplitter(), m_ExitRunFlag(false), + m_Ctx(nullptr), m_Doc(nullptr) { + + } + + InteractiveCmd::~InteractiveCmd() { + if (m_Doc != nullptr) delete m_Doc; + if (m_Ctx != nullptr) delete m_Ctx; + } + + void InteractiveCmd::Run(void) { + std::string u8cmd; + + m_ExitRunFlag = false; + while (!m_ExitRunFlag) { + // get command + GetCmdLine(u8cmd); + + // split cmd and parse it + auto cmds = m_CmdSplitter.Convert(u8cmd); + + // get sub command + if (cmds.size() < 1u) { + this->PrintCommonError("No command specified!"); + this->PrintHelp(); + continue; + } + std::string subcmd(cmds.front()); + cmds.pop_front(); + + // dispatch command + bool success = true; + if (subcmd == "load") success = this->ProcLoad(cmds); + else if (subcmd == "unload") success = this->ProcUnLoad(cmds); + else if (subcmd == "info") success = this->ProcInfo(cmds); + else if (subcmd == "ls") success = this->ProcLs(cmds); + else { + this->PrintCommonError("No such command \"\".", subcmd.c_str()); + this->PrintHelp(); + } + + if (!success) this->PrintHelp(); + } + } + + void InteractiveCmd::GetCmdLine(std::string& u8cmd) { +#if defined(LIBCMO_OS_WIN32) + std::wstring wcmd; + std::getline(std::wcin, wcmd); + LibCmo::EncodingHelper::WcharToChar(wcmd, u8cmd, CP_UTF8); +#else + std::getline(std::cin, u8cmd); +#endif + } + + bool InteractiveCmd::HasOpenedFile(void) { + return (m_Ctx != nullptr || m_Doc != nullptr); + } + + void InteractiveCmd::PrintHelp(void) { + FILE* f = stdout; + fputs(UNVIRT_TERMCOL_LIGHT_YELLOW(("Allowed Subcommands: \n")), f); + } + + void InteractiveCmd::PrintArgParseError(const std::deque& cmd, size_t pos) { + if (pos >= cmd.size()) { + fprintf(stdout, UNVIRT_TERMCOL_LIGHT_RED(("Lost argument at position %zu.\n")), pos); + } else { + fprintf(stdout, UNVIRT_TERMCOL_LIGHT_RED(("Unexpected argument \"%s\".\n")), cmd[pos].c_str()); + } + + // arg error always print help + this->PrintHelp(); + } + + void InteractiveCmd::PrintCommonError(const char* u8_fmt, ...) { + va_list argptr; + va_start(argptr, u8_fmt); + std::fputs(UNVIRT_TERMCOLHDR_LIGHT_RED, stdout); + std::vfprintf(stdout, u8_fmt, argptr); + std::fputs(UNVIRT_TERMCOLTAIL, stdout); + va_end(argptr); + } + + +#pragma endregion + +#pragma region Command Processors + + bool InteractiveCmd::ProcLoad(const std::deque& cmd) { + } + + bool InteractiveCmd::ProcUnLoad(const std::deque& cmd) { + } + + bool InteractiveCmd::ProcInfo(const std::deque& cmd) { + } + + bool InteractiveCmd::ProcLs(const std::deque& cmd) { + static const std::vector c_AllowedSwitches { + "obj", "mgr" + }; + + + } + +#pragma endregion + + + /* + #pragma region OptionsDescription OptionsDescription::OptionsDescription() : @@ -444,7 +609,7 @@ namespace Unvirt { #pragma endregion - } - } -*/ \ No newline at end of file +*/ + +} diff --git a/Unvirt/CmdHelper.hpp b/Unvirt/CmdHelper.hpp index f6ecd59..17fb641 100644 --- a/Unvirt/CmdHelper.hpp +++ b/Unvirt/CmdHelper.hpp @@ -1,256 +1,290 @@ #pragma once -#include +#include +#include #include -#include #include -#include -#include -#include "VTUtils.hpp" +#include -/* +namespace Unvirt::CmdHelper { -namespace Unvirt { - namespace CmdHelper { + class CmdSplitter { + public: + CmdSplitter(); + CmdSplitter(const CmdSplitter&) = delete; + CmdSplitter& operator=(const CmdSplitter&) = delete; + ~CmdSplitter(); - class CmdSplitter { - public: - CmdSplitter(); - CmdSplitter(const CmdSplitter&) = delete; - CmdSplitter& operator=(const CmdSplitter&) = delete; - ~CmdSplitter(); + const std::deque Convert(const std::string& u8cmd); + private: + char mCmdChar; + std::string* mBuffer; + std::deque* mResult; - const std::vector Convert(const std::string& u8cmd); - private: - char mCmdChar; - std::string* mBuffer; - std::vector* mResult; + enum class StateType : int { + SPACE, + SINGLE, + DOUBLE, + ESCAPE, + NORMAL + }; + StateType mState, mPreState; - enum class StateType : int { - SPACE, - SINGLE, - DOUBLE, - ESCAPE, - NORMAL - }; - StateType mState, mPreState; - - inline void ProcSpace(void) { - switch (mCmdChar) { - case '\'': - mState = StateType::SINGLE; - break; - case '"': - mState = StateType::DOUBLE; - break; - case '\\': - mState = StateType::ESCAPE; - mPreState = StateType::NORMAL; - break; - case ' ': - break; // skip blank - default: - mBuffer->push_back(mCmdChar); - mState = StateType::NORMAL; - break; - } + inline void ProcSpace(void) { + switch (mCmdChar) { + case '\'': + mState = StateType::SINGLE; + break; + case '"': + mState = StateType::DOUBLE; + break; + case '\\': + mState = StateType::ESCAPE; + mPreState = StateType::NORMAL; + break; + case ' ': + break; // skip blank + default: + mBuffer->push_back(mCmdChar); + mState = StateType::NORMAL; + break; } - inline void ProcSingle(void) { - switch (mCmdChar) { - case '\'': - mState = StateType::NORMAL; - break; - case '"': - mBuffer->push_back('"'); - break; - case '\\': - mState = StateType::ESCAPE; - mPreState = StateType::SINGLE; - break; - case ' ': - mBuffer->push_back(' '); - break; - default: - mBuffer->push_back(mCmdChar); - break; - } + } + inline void ProcSingle(void) { + switch (mCmdChar) { + case '\'': + mState = StateType::NORMAL; + break; + case '"': + mBuffer->push_back('"'); + break; + case '\\': + mState = StateType::ESCAPE; + mPreState = StateType::SINGLE; + break; + case ' ': + mBuffer->push_back(' '); + break; + default: + mBuffer->push_back(mCmdChar); + break; } - inline void ProcDouble(void) { - switch (mCmdChar) { - case '\'': - mBuffer->push_back('\''); - break; - case '"': - mState = StateType::NORMAL; - break; - case '\\': - mState = StateType::ESCAPE; - mPreState = StateType::DOUBLE; - break; - case ' ': - mBuffer->push_back(' '); - break; - default: - mBuffer->push_back(mCmdChar); - break; - } + } + inline void ProcDouble(void) { + switch (mCmdChar) { + case '\'': + mBuffer->push_back('\''); + break; + case '"': + mState = StateType::NORMAL; + break; + case '\\': + mState = StateType::ESCAPE; + mPreState = StateType::DOUBLE; + break; + case ' ': + mBuffer->push_back(' '); + break; + default: + mBuffer->push_back(mCmdChar); + break; } - inline void ProcEscape(void) { - // add itself - mBuffer->push_back(mCmdChar); - // restore state - mState = mPreState; + } + inline void ProcEscape(void) { + // add itself + mBuffer->push_back(mCmdChar); + // restore state + mState = mPreState; + } + inline void ProcNormal(void) { + switch (mCmdChar) { + case '\'': + mBuffer->push_back('\''); + break; + case '"': + mBuffer->push_back('"'); + break; + case '\\': + mState = StateType::ESCAPE; + mPreState = StateType::NORMAL; + break; + case ' ': + mResult->push_back(*mBuffer); + mBuffer->clear(); + mState = StateType::SPACE; + break; + default: + mBuffer->push_back(mCmdChar); + break; } - inline void ProcNormal(void) { - switch (mCmdChar) { - case '\'': - mBuffer->push_back('\''); - break; - case '"': - mBuffer->push_back('"'); - break; - case '\\': - mState = StateType::ESCAPE; - mPreState = StateType::NORMAL; - break; - case ' ': - mResult->push_back(*mBuffer); - mBuffer->clear(); - mState = StateType::SPACE; - break; - default: - mBuffer->push_back(mCmdChar); - break; - } - } - }; + } + }; - enum class CmdArgType { - NONE, - STRING, - INT - }; + class ArgParser { + public: + ArgParser() {} + ArgParser(const ArgParser&) = delete; + ArgParser& operator=(const ArgParser&) = delete; + ~ArgParser() {} - struct OptionDescription { - std::string mLongName; - char mShortName; - CmdArgType mType; - std::string mDescription; - }; + static bool ParseInt(const std::vector& cmd, const size_t expected_index, int32_t& result); + static bool ParseString(const std::vector& cmd, const size_t expected_index, std::string& result); + static bool ParseSwitch(const std::vector& cmd, const size_t expected_index, const std::vector& switches, std::string& gotten); + }; - class OptionsDescription { - public: - OptionsDescription(); - OptionsDescription(const OptionsDescription&) = delete; - OptionsDescription& operator=(const OptionsDescription&) = delete; - ~OptionsDescription(); + class InteractiveCmd { + public: + InteractiveCmd(); + InteractiveCmd(const InteractiveCmd&) = delete; + InteractiveCmd& operator=(const InteractiveCmd&) = delete; + ~InteractiveCmd(); - /// - /// Add an option - /// - /// The long name of this option. Should NOT be blank or NULL. - /// A single char for the short name of this option. Leave ZERO to omit this. - /// The value type of this option. Set to CmdArgType::NONE to indicate this is a switch (no value). - /// The description of this option. This can be NULL. - void AddOption(const char* fullname, char shortname, CmdArgType type, const char* sescription); - void AddPositionalOption(const char* corresponding_longname); + void Run(void); - OptionDescription* GetDescByLongName(const std::string& longname); - OptionDescription* GetDescByShortName(char shortname); - OptionDescription* GetDescByPosition(size_t pos); + private: + void GetCmdLine(std::string& u8cmd); + bool HasOpenedFile(void); + void PrintHelp(void); + void PrintArgParseError(const std::deque& cmd, size_t pos); + void PrintCommonError(const char* u8_fmt, ...); - void PrintHelp(FILE* f); - private: - std::unordered_map mLongNameDict; - std::unordered_map mShortNameMapping; - std::vector mPositionalArgMapping; - }; + bool ProcLoad(const std::deque& cmd); + bool ProcUnLoad(const std::deque& cmd); + bool ProcInfo(const std::deque& cmd); + bool ProcLs(const std::deque& cmd); - struct AnyVariable { - size_t mDataBasicSize; - void* mData; - }; + bool m_ExitRunFlag; + CmdSplitter m_CmdSplitter; + LibCmo::CK2::CKMinContext* m_Ctx; + LibCmo::CK2::CKFileDocument* m_Doc; + }; - class VariablesMap { - private: - std::unordered_map mDataPair; - public: - VariablesMap(); - VariablesMap(const VariablesMap&) = delete; - VariablesMap& operator=(const VariablesMap&) = delete; - ~VariablesMap(); + /* - void Clear(void); - /// - /// Add option key value pair. - /// - /// - /// - /// - /// return false when this opt is existed. - bool AddPair(const std::string& name, CmdArgType t, const std::string& val); - bool Contain(const char* opt) { - if (opt == nullptr) throw std::invalid_argument("Invalid Option Name."); - return mDataPair.contains(opt); - } - template - T* GetData(const char* opt) { - if (opt == nullptr) throw std::invalid_argument("Invalid Option Name."); - const auto search = mDataPair.find(opt); - if (search == mDataPair.end()) return nullptr; + enum class CmdArgType { + NONE, + STRING, + INT + }; - if (sizeof(T) > search->second.mDataBasicSize) throw std::invalid_argument("Memory Violation."); - return reinterpret_cast(search->second.mData); - } - }; + struct OptionDescription { + std::string mLongName; + char mShortName; + CmdArgType mType; + std::string mDescription; + }; - struct CmdRegisteryEntry { - std::string mSubCmdDesc; - OptionsDescription mOptDesc; - std::function mBindProc; - }; + class OptionsDescription { + public: + OptionsDescription(); + OptionsDescription(const OptionsDescription&) = delete; + OptionsDescription& operator=(const OptionsDescription&) = delete; + ~OptionsDescription(); - class ExecEnvironment { - public: - ExecEnvironment(); - ExecEnvironment(const ExecEnvironment&) = delete; - ExecEnvironment& operator=(const ExecEnvironment&) = delete; - ~ExecEnvironment(); + /// + /// Add an option + /// + /// The long name of this option. Should NOT be blank or NULL. + /// A single char for the short name of this option. Leave ZERO to omit this. + /// The value type of this option. Set to CmdArgType::NONE to indicate this is a switch (no value). + /// The description of this option. This can be NULL. + void AddOption(const char* fullname, char shortname, CmdArgType type, const char* sescription); + void AddPositionalOption(const char* corresponding_longname); - void ProcLoad(OptionsDescription&, VariablesMap&); - void ProcInfo(OptionsDescription&, VariablesMap&); - void ProcClear(OptionsDescription&, VariablesMap&); - void ProcExportSql(OptionsDescription&, VariablesMap&); - private: - LibCmo::CKFile* mVtFile; - LibCmo::Utils::VirtoolsContext* mVtFileEnv; - }; + OptionDescription* GetDescByLongName(const std::string& longname); + OptionDescription* GetDescByShortName(char shortname); + OptionDescription* GetDescByPosition(size_t pos); - class InteractiveCmd { - public: - InteractiveCmd(); - InteractiveCmd(const InteractiveCmd&) = delete; - InteractiveCmd& operator=(const InteractiveCmd&) = delete; - ~InteractiveCmd(); + void PrintHelp(FILE* f); + private: + std::unordered_map mLongNameDict; + std::unordered_map mShortNameMapping; + std::vector mPositionalArgMapping; + }; - void Run(void); + struct AnyVariable { + size_t mDataBasicSize; + void* mData; + }; - private: - void GetCmdLine(std::string& u8cmd); - void CmdParser(const std::vector& args); - void PrintHelp(FILE* f); + class VariablesMap { + private: + std::unordered_map mDataPair; - void ProcExit(OptionsDescription&, VariablesMap&); + public: + VariablesMap(); + VariablesMap(const VariablesMap&) = delete; + VariablesMap& operator=(const VariablesMap&) = delete; + ~VariablesMap(); - std::unordered_map mCmdDispatcher; - CmdSplitter mCmdSplitter; - ExecEnvironment mExecEnv; - VariablesMap mVm; - std::string mBlank; - bool mExitRunFlag; - }; + void Clear(void); + /// + /// Add option key value pair. + /// + /// + /// + /// + /// return false when this opt is existed. + bool AddPair(const std::string& name, CmdArgType t, const std::string& val); + bool Contain(const char* opt) { + if (opt == nullptr) throw std::invalid_argument("Invalid Option Name."); + return mDataPair.contains(opt); + } + template + T* GetData(const char* opt) { + if (opt == nullptr) throw std::invalid_argument("Invalid Option Name."); + const auto search = mDataPair.find(opt); + if (search == mDataPair.end()) return nullptr; - } -} -*/ \ No newline at end of file + if (sizeof(T) > search->second.mDataBasicSize) throw std::invalid_argument("Memory Violation."); + return reinterpret_cast(search->second.mData); + } + }; + + struct CmdRegisteryEntry { + std::string mSubCmdDesc; + OptionsDescription mOptDesc; + std::function mBindProc; + }; + + class ExecEnvironment { + public: + ExecEnvironment(); + ExecEnvironment(const ExecEnvironment&) = delete; + ExecEnvironment& operator=(const ExecEnvironment&) = delete; + ~ExecEnvironment(); + + void ProcLoad(OptionsDescription&, VariablesMap&); + void ProcInfo(OptionsDescription&, VariablesMap&); + void ProcClear(OptionsDescription&, VariablesMap&); + void ProcExportSql(OptionsDescription&, VariablesMap&); + private: + LibCmo::CKFile* mVtFile; + LibCmo::Utils::VirtoolsContext* mVtFileEnv; + }; + + class InteractiveCmd { + public: + InteractiveCmd(); + InteractiveCmd(const InteractiveCmd&) = delete; + InteractiveCmd& operator=(const InteractiveCmd&) = delete; + ~InteractiveCmd(); + + private: + void GetCmdLine(std::string& u8cmd); + void CmdParser(const std::vector& args); + void PrintHelp(FILE* f); + + void ProcExit(OptionsDescription&, VariablesMap&); + + std::unordered_map mCmdDispatcher; + CmdSplitter mCmdSplitter; + ExecEnvironment mExecEnv; + VariablesMap mVm; + std::string mBlank; + bool mExitRunFlag; + }; + +*/ +} \ No newline at end of file diff --git a/Unvirt/TerminalHelper.hpp b/Unvirt/TerminalHelper.hpp index d2aad4b..604dc69 100644 --- a/Unvirt/TerminalHelper.hpp +++ b/Unvirt/TerminalHelper.hpp @@ -6,6 +6,28 @@ namespace Unvirt{ #define UNVIRT_REMOVE_PARENS_IMPL(...) __VA_ARGS__ #define UNVIRT_REMOVE_PARENS(T) UNVIRT_REMOVE_PARENS_IMPL T + +#define UNVIRT_TERMCOLHDR_BLACK "\033[30m" +#define UNVIRT_TERMCOLHDR_RED "\033[31m" +#define UNVIRT_TERMCOLHDR_GREEN "\033[32m" +#define UNVIRT_TERMCOLHDR_YELLOW "\033[33m" +#define UNVIRT_TERMCOLHDR_BLUE "\033[34m" +#define UNVIRT_TERMCOLHDR_MAGENTA "\033[35m" +#define UNVIRT_TERMCOLHDR_CYAN "\033[36m" +#define UNVIRT_TERMCOLHDR_WHITE "\033[37m" + +#define UNVIRT_TERMCOLHDR_LIGHT_BLACK "\033[90m" +#define UNVIRT_TERMCOLHDR_LIGHT_RED "\033[91m" +#define UNVIRT_TERMCOLHDR_LIGHT_GREEN "\033[92m" +#define UNVIRT_TERMCOLHDR_LIGHT_YELLOW "\033[93m" +#define UNVIRT_TERMCOLHDR_LIGHT_BLUE "\033[94m" +#define UNVIRT_TERMCOLHDR_LIGHT_MAGENTA "\033[95m" +#define UNVIRT_TERMCOLHDR_LIGHT_CYAN "\033[96m" +#define UNVIRT_TERMCOLHDR_LIGHT_WHITE "\033[97m" + +#define UNVIRT_TERMCOLTAIL "\033[97m" + + #define UNVIRT_TERMCOL_BLACK(T) "\033[30m" UNVIRT_REMOVE_PARENS(T) "\033[0m" #define UNVIRT_TERMCOL_RED(T) "\033[31m" UNVIRT_REMOVE_PARENS(T) "\033[0m" #define UNVIRT_TERMCOL_GREEN(T) "\033[32m" UNVIRT_REMOVE_PARENS(T) "\033[0m" diff --git a/Unvirt/Unvirt.cpp b/Unvirt/Unvirt.cpp index 75b0940..92d72c9 100644 --- a/Unvirt/Unvirt.cpp +++ b/Unvirt/Unvirt.cpp @@ -17,8 +17,8 @@ int main(int argc, char* argv[]) { vtctx.SetEncoding("850"); LibCmo::CK2::CKFile vtfile(&vtctx); - LibCmo::CK2::CKFileData::DeepDocument* doc; - LibCmo::CK2::CKERROR err = vtfile.DeepLoad("Level_01.NMO", &doc); + LibCmo::CK2::CKFileDocument* doc; + LibCmo::CK2::CKERROR err = vtfile.DeepLoad("Level_02.NMO", &doc); if (doc) Unvirt::StructFormatter::PrintCKFileInfo(doc->m_FileInfo); diff --git a/Unvirt/Unvirt.vcxproj b/Unvirt/Unvirt.vcxproj index 735142e..9831e9f 100644 --- a/Unvirt/Unvirt.vcxproj +++ b/Unvirt/Unvirt.vcxproj @@ -96,7 +96,7 @@ - Level3 + Level4 true _CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true @@ -114,7 +114,7 @@ - Level3 + Level4 true true true @@ -136,7 +136,7 @@ - Level3 + Level4 true _CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true @@ -154,7 +154,7 @@ - Level3 + Level4 true true true