diff --git a/LibCmo/CK2/CKContext.cpp b/LibCmo/CK2/CKContext.cpp index 03622e2..ab9244a 100644 --- a/LibCmo/CK2/CKContext.cpp +++ b/LibCmo/CK2/CKContext.cpp @@ -78,6 +78,19 @@ namespace LibCmo::CK2 { } + void CKContext::DestroyAllCKObjects() { + // free all created objects + for (auto& ptr : m_ObjectsList) { + if (ptr != nullptr) { + InternalDestroy(this, ptr); + } + } + // restore returned object list + m_ReturnedObjectIds.clear(); + // empty object list + m_ObjectsList.clear(); + } + #pragma endregion #pragma region Ctor Dtor @@ -96,13 +109,7 @@ namespace LibCmo::CK2 { } CKContext::~CKContext() { - // free all created objects - for (auto& ptr : m_ObjectsList) { - if (ptr != nullptr) { - InternalDestroy(this, ptr); - } - } - + DestroyAllCKObjects(); } #pragma endregion diff --git a/LibCmo/CK2/CKContext.hpp b/LibCmo/CK2/CKContext.hpp index d8a423d..ba3bc9c 100644 --- a/LibCmo/CK2/CKContext.hpp +++ b/LibCmo/CK2/CKContext.hpp @@ -42,6 +42,7 @@ namespace LibCmo::CK2 { CK_CREATIONMODE* res = nullptr); ObjImpls::CKObject* GetCKObject(CK_ID id); void DestroyCKObject(CK_ID id); + void DestroyAllCKObjects(); // ========== Object Access ========== diff --git a/LibCmo/CK2/CKStateChunk.cpp b/LibCmo/CK2/CKStateChunk.cpp index a156b55..1cddbf4 100644 --- a/LibCmo/CK2/CKStateChunk.cpp +++ b/LibCmo/CK2/CKStateChunk.cpp @@ -440,6 +440,39 @@ namespace LibCmo::CK2 { return true; } + XContainer::XArray CKStateChunk::GetIdentifierProfile() { + XContainer::XArray collection; + if (this->m_Parser.m_Status != CKStateChunkStatus::READ) return collection; + + CKDWORD pos = 0u; + if (this->m_DataDwSize < 2u) return collection; // impossible to have a identifier + + // iterate identifier + while (true) { + // add current identifier + CKDWORD nextptr = this->m_pData[pos + 1]; + if (nextptr == 0u) { + nextptr = this->m_DataDwSize; // got tail. no more identifier + } + collection.emplace_back(IdentifierProfile{ + this->m_pData[pos], + this->m_pData + pos + 2, + sizeof(CKDWORD) * (nextptr - pos - 2u) + }); + + // move to next identifier or exit + // got tail. no more identifier + if (this->m_pData[pos + 1] == 0u) break; + + pos = this->m_pData[pos + 1]; + + // out of buffer + if (pos + 1 >= this->m_DataDwSize) break; + }; + return collection; + + } + /* ========== Basic Data Read Functions ==========*/ bool CKStateChunk::ReadByteData(void* data_ptr, CKDWORD size_in_byte) { diff --git a/LibCmo/CK2/CKStateChunk.hpp b/LibCmo/CK2/CKStateChunk.hpp index 54ea423..6107f04 100644 --- a/LibCmo/CK2/CKStateChunk.hpp +++ b/LibCmo/CK2/CKStateChunk.hpp @@ -4,6 +4,12 @@ namespace LibCmo::CK2 { + struct IdentifierProfile { + CKDWORD m_Identifier; + void* m_DataPtr; + CKDWORD m_AreaSize; + }; + class CKStateChunk { public: //CKStateChunk(); @@ -89,6 +95,7 @@ namespace LibCmo::CK2 { inline bool SeekIdentifierAndReturnSize(TEnum enum_v, CKDWORD* out_size) { return SeekIdentifierDwordAndReturnSize(static_cast(enum_v), out_size); } + XContainer::XArray GetIdentifierProfile(); /* ========== Basic Data Read Functions ==========*/ diff --git a/Unvirt/CmdHelper.cpp b/Unvirt/CmdHelper.cpp index b67e844..ccb62e9 100644 --- a/Unvirt/CmdHelper.cpp +++ b/Unvirt/CmdHelper.cpp @@ -174,6 +174,7 @@ namespace Unvirt::CmdHelper { void HelpDocument::Print() { for (auto& item : m_Results) { + fputs("Syntax: ", stdout); for (auto& cmd : item.m_ArgDesc) { fputs(cmd.m_Name.c_str(), stdout); fputc(' ', stdout); @@ -181,8 +182,7 @@ namespace Unvirt::CmdHelper { fputc('\n', stdout); if (!item.m_CmdDesc.empty()) { - fputs(item.m_CmdDesc.c_str(), stdout); - fputc('\n', stdout); + fprintf(stdout, "Description: %s\n", item.m_CmdDesc.c_str()); } for (auto& cmd : item.m_ArgDesc) { diff --git a/Unvirt/CmdHelper.hpp b/Unvirt/CmdHelper.hpp index a45be12..284f96e 100644 --- a/Unvirt/CmdHelper.hpp +++ b/Unvirt/CmdHelper.hpp @@ -220,7 +220,8 @@ namespace Unvirt::CmdHelper { class StringArgument : public AbstractArgument { public: using vType = std::string; - StringArgument(const char* argname) : AbstractArgument(argname) {} + StringArgument(const char* argname) : + AbstractArgument(argname) {} virtual ~StringArgument() {} LIBCMO_DISABLE_COPY_MOVE(StringArgument); @@ -232,7 +233,8 @@ namespace Unvirt::CmdHelper { class EncodingArgument : public AbstractArgument { public: using vType = std::vector; - EncodingArgument(const char* argname) : AbstractArgument(argname) {} + EncodingArgument(const char* argname) : + AbstractArgument(argname) {} virtual ~EncodingArgument() {} LIBCMO_DISABLE_COPY_MOVE(EncodingArgument); diff --git a/Unvirt/StructFormatter.cpp b/Unvirt/StructFormatter.cpp index 8684b14..f7f941d 100644 --- a/Unvirt/StructFormatter.cpp +++ b/Unvirt/StructFormatter.cpp @@ -1,17 +1,68 @@ #include +#include #include "StructFormatter.hpp" #include "AccessibleValue.hpp" #include "TerminalHelper.hpp" namespace Unvirt::StructFormatter { - static FILE* fout = stdout; +#pragma region Assist Functions + + static void PrintCKSTRING(const LibCmo::TypeHelper::MKString& name) { + LibCmo::CK2::CKSTRING ckname = name.c_str(); + if (ckname == nullptr) { + fputs(UNVIRT_TERMCOL_LIGHT_MAGENTA(("")), stdout); + } else { + fputs(ckname, stdout); + } + } + static void PrintPointer(const void* ptr) { + if (ptr == nullptr) { + fputs(UNVIRT_TERMCOL_LIGHT_CYAN(("")), stdout); + } else { +#if UINTPTR_MAX == UINT32_MAX + // 32 bit. padding 8 zero + fprintf(stdout, "<0x%08" PRIXPTR ">", reinterpret_cast(ptr)); +#else + // 64 bit. padding 16 zero + fprintf(stdout, "<0x%016" PRIXPTR ">", reinterpret_cast(ptr)); +#endif + } + } + static void PrintCKGUID(const LibCmo::CK2::CKGUID& guid) { + fprintf(stdout, "<0x%08" PRIx32 ", 0x%08" PRIx32 ">", guid.d1, guid.d2); + } + static void PrintIdentifier(const LibCmo::CK2::CKDWORD val) { + fprintf(stdout, "0x%08" PRIx32, val); + } + + template + static void GeneralPrintList( + const std::vector<_Ty>& data, size_t page, size_t pageitems, + std::function printHdrFct, std::function printEntryFct) { + + size_t fulllen = data.size(), + startpos = page * pageitems, + fullpage = fulllen / pageitems; + + // print header + printHdrFct(); + + // print body + for (size_t counter = startpos; counter < fulllen && (counter - startpos) < pageitems; ++counter) { + printEntryFct(counter, data[counter]); + } + + fprintf(stdout, "Page %zu of %zu\n", page + 1, fullpage + 1); + } + +#pragma endregion void PrintCKFileInfo(const LibCmo::CK2::CKFileInfo& fileinfo) { - fputs(UNVIRT_TERMCOL_LIGHT_YELLOW(("CKFileInfo\n")), fout); - fprintf(fout, "FileVersion: %" PRIu32 "\n", fileinfo.FileVersion); - fprintf(fout, "CKVersion: %02" PRIX32 "/%02" PRIX32 "/%04" PRIX32 "\n", + fputs(UNVIRT_TERMCOL_LIGHT_YELLOW(("CKFileInfo\n")), stdout); + fprintf(stdout, "FileVersion: %" PRIu32 "\n", fileinfo.FileVersion); + fprintf(stdout, "CKVersion: %02" PRIX32 "/%02" PRIX32 "/%04" PRIX32 "\n", (fileinfo.CKVersion >> 24) & 0xFF, (fileinfo.CKVersion >> 16) & 0xFF, (fileinfo.CKVersion >> 0) & 0xFFFF @@ -23,118 +74,104 @@ namespace Unvirt::StructFormatter { (fileinfo.ProductBuild >> 8) & 0xFF, (fileinfo.ProductBuild >> 0) & 0xFF, }; - fprintf(fout, "Product (Version / Build): %" PRIu32 " / %" PRIu32 ".%" PRIu32 ".%" PRIu32 ".%" PRIu32 "\n", + fprintf(stdout, "Product (Version / Build): %" PRIu32 " / %" PRIu32 ".%" PRIu32 ".%" PRIu32 ".%" PRIu32 "\n", fileinfo.ProductVersion, product_series[0], product_series[1], product_series[2], product_series[3] ); - fprintf(fout, "Save Flags: %s\n", Unvirt::AccessibleValue::GetFlagEnumName( + fprintf(stdout, "Save Flags: %s\n", Unvirt::AccessibleValue::GetFlagEnumName( fileinfo.FileWriteMode, Unvirt::AccessibleValue::EnumDesc::CK_FILE_WRITEMODE ).c_str()); - fprintf(fout, "File Size: %s\n", Unvirt::AccessibleValue::GetReadableFileSize(fileinfo.FileSize).c_str()); + fprintf(stdout, "File Size: %s\n", Unvirt::AccessibleValue::GetReadableFileSize(fileinfo.FileSize).c_str()); - fprintf(fout, "Crc: 0x%" PRIX32 "\n", fileinfo.Crc); - fputc('\n', fout); + fprintf(stdout, "Crc: 0x%" PRIX32 "\n", fileinfo.Crc); + fputc('\n', stdout); - fputs("Hdr1 (Pack / UnPack): ", fout); - fprintf(fout, "%s / ", Unvirt::AccessibleValue::GetReadableFileSize(fileinfo.Hdr1PackSize).c_str()); - fprintf(fout, "%s\n", Unvirt::AccessibleValue::GetReadableFileSize(fileinfo.Hdr1UnPackSize).c_str()); + fputs("Hdr1 (Pack / UnPack): ", stdout); + fprintf(stdout, "%s / ", Unvirt::AccessibleValue::GetReadableFileSize(fileinfo.Hdr1PackSize).c_str()); + fprintf(stdout, "%s\n", Unvirt::AccessibleValue::GetReadableFileSize(fileinfo.Hdr1UnPackSize).c_str()); - fputs("Data (Pack / UnPack): ", fout); - fprintf(fout, "%s / ", Unvirt::AccessibleValue::GetReadableFileSize(fileinfo.DataPackSize).c_str()); - fprintf(fout, "%s\n", Unvirt::AccessibleValue::GetReadableFileSize(fileinfo.DataUnPackSize).c_str()); - fputc('\n', fout); + fputs("Data (Pack / UnPack): ", stdout); + fprintf(stdout, "%s / ", Unvirt::AccessibleValue::GetReadableFileSize(fileinfo.DataPackSize).c_str()); + fprintf(stdout, "%s\n", Unvirt::AccessibleValue::GetReadableFileSize(fileinfo.DataUnPackSize).c_str()); + fputc('\n', stdout); - fprintf(fout, "Manager Count: %" PRIu32 "\nObject Count: %" PRIu32 "\nMax ID Saved: %" PRIu32 "\n", + fprintf(stdout, "Manager Count: %" PRIu32 "\nObject Count: %" PRIu32 "\nMax ID Saved: %" PRIu32 "\n", fileinfo.ManagerCount, fileinfo.ObjectCount, fileinfo.MaxIDSaved ); } - static void PrintCKSTRING(const LibCmo::TypeHelper::MKString& name) { - LibCmo::CK2::CKSTRING ckname = name.c_str(); - if (ckname == nullptr) { - fputs(UNVIRT_TERMCOL_LIGHT_MAGENTA(("")), fout); - } else { - fputs(ckname, fout); - } - //if (name.empty()) { - // fputs(UNVIRT_TERMCOL_LIGHT_MAGENTA(("")), fout); - //} else { - // fputs(name.c_str(), fout); - //} - } - static void PrintPointer(const void* ptr) { - if (ptr == nullptr) { - fputs(UNVIRT_TERMCOL_LIGHT_CYAN(("")), fout); - } else { - fprintf(fout, "<0x%08zX>", reinterpret_cast(ptr)); - } - } - static void PrintCKGUID(const LibCmo::CK2::CKGUID& guid) { - fprintf(stdout, "<0x%08" PRIx32 ", 0x%08" PRIx32 ">", guid.d1, guid.d2); - } - void PrintObjectList(const LibCmo::XContainer::XArray& ls, size_t page, size_t pageitems) { - fputs(UNVIRT_TERMCOL_LIGHT_YELLOW(("CKFileObject\n")), fout); - size_t fulllen = ls.size(), - startpos = page * pageitems, - fullpage = fulllen / pageitems; + fputs(UNVIRT_TERMCOL_LIGHT_YELLOW(("CKFileObject\n")), stdout); + GeneralPrintList(ls, page, pageitems, + []() -> void { + fputs("Index\tThis CK ID\tFile CK ID\tType\tCKObject\tCKStateChunk\tName\n", stdout); + }, + [](size_t index, const LibCmo::CK2::CKFileObject& obj) -> void { + fprintf(stdout, "#%zu\t", index); + fprintf(stdout, "%" PRIu32 "\t%" PRIu32 "\t", obj.CreatedObjectId, obj.ObjectId); + fprintf(stdout, "%s\t", Unvirt::AccessibleValue::GetClassIdName(obj.ObjectCid).c_str()); + PrintPointer(obj.ObjPtr); + fputc('\t', stdout); + PrintPointer(obj.Data); + fputc('\t', stdout); + PrintCKSTRING(obj.Name); + fputc('\n', stdout); + } + ); - // print header - fputs("CK ID\tType\tCKObject\tCKStateChunk\tName\n", fout); - - // print body - for (size_t counter = startpos; counter < fulllen && (counter - startpos) < pageitems; ++counter) { - const auto& obj = ls[counter]; - - fprintf(fout, "%" PRIu32 "\t", obj.ObjectId); - fprintf(fout, "%s\t", Unvirt::AccessibleValue::GetClassIdName(obj.ObjectCid).c_str()); - PrintPointer(obj.ObjPtr); - fputc('\t', fout); - PrintPointer(obj.Data); - fputc('\t', fout); - PrintCKSTRING(obj.Name); - fputc('\n', fout); - } - - fprintf(fout, "Page %zu of %zu\n", page + 1, fullpage + 1); } void PrintManagerList(const LibCmo::XContainer::XArray& ls, size_t page, size_t pageitems) { - fputs(UNVIRT_TERMCOL_LIGHT_YELLOW(("CKFileManager\n")), fout); - size_t fulllen = ls.size(), - startpos = page * pageitems, - fullpage = fulllen / pageitems; + fputs(UNVIRT_TERMCOL_LIGHT_YELLOW(("CKFileManager\n")), stdout); + GeneralPrintList(ls, page, pageitems, + []() -> void { + // print header + fputs("Index\tCKGUID\tCKStateChunk\n", stdout); + }, + [](size_t index, const LibCmo::CK2::CKFileManagerData& mgr) -> void { + // print body + fprintf(stdout, "#%zu\t", index); + PrintCKGUID(mgr.Manager); + fputc('\t', stdout); + PrintPointer(mgr.Data); + fputc('\n', stdout); + } + ); + } + + void PrintCKObject(const LibCmo::CK2::ObjImpls::CKObject* obj) { + fputs(UNVIRT_TERMCOL_LIGHT_YELLOW(("CKObject\n")), stdout); + fputs(UNVIRT_TERMCOL_LIGHT_RED(("Not Implemented.\n")), stdout); + } + void PrintCKBaseManager(const LibCmo::CK2::MgrImpls::CKBaseManager* mgr) { + fputs(UNVIRT_TERMCOL_LIGHT_YELLOW(("CKBaseManager\n")), stdout); + fputs(UNVIRT_TERMCOL_LIGHT_RED(("Not Implemented.\n")), stdout); + } + void PrintCKStateChunk(const LibCmo::CK2::CKStateChunk* chunk) { + fputs(UNVIRT_TERMCOL_LIGHT_YELLOW(("CKStateChunk\n")), stdout); + // print header - fputs("CKGUID\tCKBaseManager\tCKStateChunk\n", fout); - + fputs("Identifier\tData Pointer\tData Size\n", stdout); + // print body - for (size_t counter = startpos; counter < fulllen && (counter - startpos) < pageitems; ++counter) { - const auto& mgr = ls[counter]; - - PrintCKGUID(mgr.Manager); - fputc('\t', fout); - PrintPointer(mgr.Data); - fputc('\n', fout); + LibCmo::CK2::CKStateChunk* operchunk = const_cast(chunk); + operchunk->StartRead(); + auto collection = operchunk->GetIdentifierProfile(); + operchunk->StopRead(); + for (const auto& profile : collection) { + PrintIdentifier(profile.m_Identifier); + fputc('\t', stdout); + PrintPointer(profile.m_DataPtr); + fputc('\t', stdout); + fprintf(stdout, "%" PRIckDWORD "\n", profile.m_AreaSize); } - - fprintf(fout, "Page %zu of %zu\n", page + 1, fullpage + 1); - } - - void PrintCKObject(const LibCmo::CK2::ObjImpls::CKObject*) { - fputs(UNVIRT_TERMCOL_MAGENTA(("NO IMPLEMENTS\n")), fout); - } - void PrintCKBaseManager(const LibCmo::CK2::MgrImpls::CKBaseManager*) { - fputs(UNVIRT_TERMCOL_MAGENTA(("NO IMPLEMENTS\n")), fout); - } - void PrintCKStateChunk(const LibCmo::CK2::CKStateChunk*) { - fputs(UNVIRT_TERMCOL_MAGENTA(("NO IMPLEMENTS\n")), fout); } } diff --git a/Unvirt/UnvirtContext.cpp b/Unvirt/UnvirtContext.cpp index 40c7774..44113ba 100644 --- a/Unvirt/UnvirtContext.cpp +++ b/Unvirt/UnvirtContext.cpp @@ -2,12 +2,6 @@ namespace Unvirt::Context { - static FILE* fout = stdout; - -#pragma region Encoding Arg - -#pragma endregion - #pragma region UnvirtContext Misc UnvirtContext::UnvirtContext() : @@ -106,6 +100,12 @@ namespace Unvirt::Context { ) ) ) + ->Then((new CmdHelper::Literal("help")) + ->Executes( + std::bind(&UnvirtContext::ProcHelp, this, std::placeholders::_1), + "Output this help page." + ) + ) ->Then((new CmdHelper::Literal("exit")) ->Executes( std::bind(&UnvirtContext::ProcExit, this, std::placeholders::_1), @@ -132,24 +132,27 @@ namespace Unvirt::Context { void UnvirtContext::ClearDocument() { if (m_FileReader == nullptr) return; + // delete reader delete m_FileReader; m_FileReader = nullptr; + // clear context + m_Ctx->DestroyAllCKObjects(); } void UnvirtContext::PrintContextMsg(LibCmo::CK2::CKSTRING msg) { if (msg != nullptr) { - fprintf(fout, UNVIRT_TERMCOL_LIGHT_YELLOW(("[CKContext] ")) "%s\n", msg); + fprintf(stdout, UNVIRT_TERMCOL_LIGHT_YELLOW(("[CKContext] ")) "%s\n", msg); } } void UnvirtContext::PrintCommonError(const char* u8_fmt, ...) { va_list argptr; va_start(argptr, u8_fmt); - std::fputs(UNVIRT_TERMCOLHDR_LIGHT_RED, fout); - std::vfprintf(fout, u8_fmt, argptr); - std::fputs(UNVIRT_TERMCOLTAIL, fout); + std::fputs(UNVIRT_TERMCOLHDR_LIGHT_RED, stdout); + std::vfprintf(stdout, u8_fmt, argptr); + std::fputs(UNVIRT_TERMCOLTAIL, stdout); va_end(argptr); - std::fputc('\n', fout); + std::fputc('\n', stdout); } #pragma endregion @@ -170,8 +173,7 @@ namespace Unvirt::Context { // get sub command if (!m_Root.RootConsume(cmds)) { - this->PrintCommonError("Command syntax error \"%s\".", u8cmd.c_str()); - m_Help->Print(); + this->PrintCommonError("Syntax error \"%s\".\nType 'help' for usage.", u8cmd.c_str()); } if (m_OrderExit) { @@ -187,7 +189,7 @@ namespace Unvirt::Context { void UnvirtContext::ProcLoad(const CmdHelper::ArgumentsMap* amap) { // check pre-requirement if (HasOpenedFile()) { - PrintCommonError("Already have a opened file. Close it before calling \"load\"."); + PrintCommonError("Already have a opened file. Close it before calling 'load'."); return; } @@ -238,7 +240,7 @@ namespace Unvirt::Context { void UnvirtContext::ProcLs(const CmdHelper::ArgumentsMap* amap) { // check pre-requirement if (!HasOpenedFile()) { - this->PrintCommonError("No loaded file."); + PrintCommonError("No loaded file."); return; } @@ -254,7 +256,6 @@ namespace Unvirt::Context { PrintCommonError("Page out of range."); return; } - Unvirt::StructFormatter::PrintObjectList(m_FileReader->GetFileObjects(), page, this->m_PageLen); break; } @@ -265,7 +266,6 @@ namespace Unvirt::Context { PrintCommonError("Page out of range."); return; } - Unvirt::StructFormatter::PrintManagerList(m_FileReader->GetManagersData(), page, this->m_PageLen); break; } @@ -275,7 +275,7 @@ namespace Unvirt::Context { void UnvirtContext::ProcData( const CmdHelper::ArgumentsMap* amap) { // check pre-requirement if (!HasOpenedFile()) { - this->PrintCommonError("No loaded file."); + PrintCommonError("No loaded file."); return; } @@ -299,6 +299,7 @@ namespace Unvirt::Context { PrintCommonError("Index out of range."); return; } + PrintCommonError("WIP function."); //Unvirt::StructFormatter::PrintCKBaseManager(m_FileReader->GetManagersData()[index].Data); break; } @@ -308,7 +309,7 @@ namespace Unvirt::Context { void UnvirtContext::ProcChunk(const CmdHelper::ArgumentsMap* amap) { // check pre-requirement if (!HasOpenedFile()) { - this->PrintCommonError("No loaded file."); + PrintCommonError("No loaded file."); return; } @@ -353,6 +354,10 @@ namespace Unvirt::Context { m_Ctx->SetTempPath(amap->Get("temppath")->c_str()); } + void Unvirt::Context::UnvirtContext::ProcHelp(const CmdHelper::ArgumentsMap* amap) { + m_Help->Print(); + } + void UnvirtContext::ProcExit(const CmdHelper::ArgumentsMap*) { m_OrderExit = true; } diff --git a/Unvirt/UnvirtContext.hpp b/Unvirt/UnvirtContext.hpp index d560b08..842add8 100644 --- a/Unvirt/UnvirtContext.hpp +++ b/Unvirt/UnvirtContext.hpp @@ -36,6 +36,7 @@ namespace Unvirt::Context { void ProcItems(const CmdHelper::ArgumentsMap* amap); void ProcEncoding(const CmdHelper::ArgumentsMap* amap); void ProcTemp(const CmdHelper::ArgumentsMap* amap); + void ProcHelp(const CmdHelper::ArgumentsMap* amap); void ProcExit(const CmdHelper::ArgumentsMap* amap); protected: