diff --git a/LibCmo/CK2/CKContext.hpp b/LibCmo/CK2/CKContext.hpp index eacb3bd..8d8ee19 100644 --- a/LibCmo/CK2/CKContext.hpp +++ b/LibCmo/CK2/CKContext.hpp @@ -153,7 +153,7 @@ namespace LibCmo::CK2 { * @details It accept a CKSTRING representing the string need to be printed. * The passed CKSTRING is guaranteen that it can not be nullptr. */ - using OutputCallback = void(*)(CKSTRING); + using OutputCallback = std::function; /** * @brief Output plain message. * @param[in] str Plain message. nullptr is allowed but not suggested. diff --git a/LibCmo/VTEncoding.cpp b/LibCmo/VTEncoding.cpp index e10878d..924b296 100644 --- a/LibCmo/VTEncoding.cpp +++ b/LibCmo/VTEncoding.cpp @@ -403,6 +403,9 @@ namespace LibCmo::EncodingHelper { #if YYCC_OS == YYCC_OS_WINDOWS struct WindowsEncodingToken { + WindowsEncodingToken(const std::u8string_view& universal_code, UINT cp) : + m_Name(universal_code), m_CodePage(cp) {} + std::u8string m_Name; UINT m_CodePage; }; @@ -412,7 +415,8 @@ namespace LibCmo::EncodingHelper { static const iconv_t c_InvalidIconvType = reinterpret_cast(-1); struct IconvEncodingToken { - IconvEncodingToken(const std::string_view& iconv_code) : + IconvEncodingToken(const std::u8string_view& universal_code, const std::string_view& iconv_code) : + m_Name(universal_code), m_FromUTF8(c_InvalidIconvType), m_ToUTF8(c_InvalidIconvType) { // if iconv code is empty, do nothing if (iconv_code.empty()) return; @@ -426,6 +430,8 @@ namespace LibCmo::EncodingHelper { if (this->m_ToUTF8 != c_InvalidIconvType) iconv_close(token_cast->m_ToUTF8); } + + std::u8string m_Name; iconv_t m_FromUTF8; iconv_t m_ToUTF8; }; @@ -508,7 +514,7 @@ namespace LibCmo::EncodingHelper { if (!YYCC::WinFctHelper::IsValidCodePage(cp)) return INVALID_ENCODING_TOKEN; // create token and return - WindowsEncodingToken* token = new WindowsEncodingToken { cp }; + WindowsEncodingToken* token = new WindowsEncodingToken(enc_name, cp); return token; #else // get iconv code first @@ -516,7 +522,7 @@ namespace LibCmo::EncodingHelper { if (!GetIconvCode(enc_name, code)) return INVALID_ENCODING_TOKEN; // create token and set default value - IconvEncodingToken* token = new IconvEncodingToken(code); + IconvEncodingToken* token = new IconvEncodingToken(enc_name, code); // check whether token has been initialized correctly if (token->m_FromUTF8 == c_InvalidIconvType || token->m_ToUTF8 == c_InvalidIconvType) { // failed. free resource and return @@ -541,6 +547,34 @@ namespace LibCmo::EncodingHelper { #endif } + std::u8string GetEncodingTokenAssociatedName(EncodingToken token) { + // prepare return value + std::u8string ret; + // if token is invalid, return directly + if (token == INVALID_ENCODING_TOKEN) return ret; + + // get associated name +#if YYCC_OS == YYCC_OS_WINDOWS + WindowsEncodingToken* token_cast = static_cast(token); + ret = token_cast->m_Name; +#else + IconvEncodingToken* token_cast = static_cast(token); + ret = token_cast->m_Name; +#endif + // return value + return ret; + } + + bool IsValidEncodingName(const std::u8string_view& enc_name) { +#if YYCC_OS == YYCC_OS_WINDOWS + UINT cp = CP_ACP; + return GetWindowsCodePage(enc_name, cp); +#else + std::string code; + return GetIconvCode(enc_name, code); +#endif + } + #pragma endregion #pragma region Exposed Convertion Functions diff --git a/LibCmo/VTEncoding.hpp b/LibCmo/VTEncoding.hpp index fbd47b2..841e64c 100644 --- a/LibCmo/VTEncoding.hpp +++ b/LibCmo/VTEncoding.hpp @@ -55,6 +55,23 @@ namespace LibCmo::EncodingHelper { * If token is #INVALID_ENCODING_TOKEN, this function does nothing. */ void DestroyEncodingToken(EncodingToken token); + /** + * @brief Get associated universal encoding name of given encoding token. + * @param[in] token The encoding token for getting name. + * @return Encoding token associated name (the universal encoding name used to create this token). + * Blank if given token is invalid or fail to get. + */ + std::u8string GetEncodingTokenAssociatedName(EncodingToken token); + /** + * @brief Check whether given universal encoding name can be used to produce token. + * @param[in] enc_name Universal encoding name. + * @return True if it is, otherwise false. + * @remarks + * Please note this function only check whether given encoding name is acceptable by token creator. + * Hoewver it doesn't mean that the token must be created if this function return true. + * Because there are some runtime issues which can cause that fail to create encoding token. + */ + bool IsValidEncodingName(const std::u8string_view& enc_name); /** * @brief Convert native string to UTF8 string by given encoding. diff --git a/Unvirt/CmdHelper.cpp b/Unvirt/CmdHelper.cpp index 36cb47b..8b6e54c 100644 --- a/Unvirt/CmdHelper.cpp +++ b/Unvirt/CmdHelper.cpp @@ -1,4 +1,5 @@ #include "CmdHelper.hpp" +#include #include namespace Unvirt::CmdHelper { @@ -11,7 +12,7 @@ namespace Unvirt::CmdHelper { return m_Result; } - bool CmdSplitter::Convert(const std::u8string& u8cmd) { + bool CmdSplitter::Lex(const std::u8string& u8cmd) { // Clear variables m_ValidResult = false; m_Result.clear(); @@ -537,7 +538,12 @@ if (!this->IsRootNode()) { \ bool EncodingListArgument::BeginConsume(const std::u8string& cur_cmd, ArgumentsMap& am) { // split given argument std::vector encs = YYCC::StringHelper::Split(cur_cmd, u8","); - // add into map + // check each parts is a valid encoding name + for (const auto& item : encs) { + if (!LibCmo::EncodingHelper::IsValidEncodingName(item)) + return false; + } + // okey, add into map am.Add(m_ArgumentName, encs); return true; } diff --git a/Unvirt/CmdHelper.hpp b/Unvirt/CmdHelper.hpp index 1bd6fe0..d9f211d 100644 --- a/Unvirt/CmdHelper.hpp +++ b/Unvirt/CmdHelper.hpp @@ -31,9 +31,9 @@ namespace Unvirt::CmdHelper { m_CurrentChar(u8'\0'), m_Buffer(), m_Result(), m_ValidResult(false), m_State(StateType::NORMAL), m_PrevState(StateType::NORMAL) {} ~CmdSplitter() {} - YYCC_DEL_CLS_COPY_MOVE(CmdSplitter); + YYCC_DEF_CLS_COPY_MOVE(CmdSplitter); - bool Convert(const std::u8string& u8cmd); + bool Lex(const std::u8string& u8cmd); const Result_t& GetResult() const; private: @@ -241,7 +241,7 @@ namespace Unvirt::CmdHelper { class AbstractNode { public: - using FctExecution_t = void(*)(const ArgumentsMap&); + using FctExecution_t = std::function; public: AbstractNode(); diff --git a/Unvirt/StructFormatter.cpp b/Unvirt/StructFormatter.cpp index 1db3186..ff96a1d 100644 --- a/Unvirt/StructFormatter.cpp +++ b/Unvirt/StructFormatter.cpp @@ -2,7 +2,6 @@ #include #include "AccessibleValue.hpp" -#include "TerminalHelper.hpp" namespace Console = YYCC::ConsoleHelper; diff --git a/Unvirt/UnvirtContext.cpp b/Unvirt/UnvirtContext.cpp index b46f6ea..2fbfbac 100644 --- a/Unvirt/UnvirtContext.cpp +++ b/Unvirt/UnvirtContext.cpp @@ -1,193 +1,208 @@ #include "UnvirtContext.hpp" + +#include #include #include #include namespace Unvirt::Context { +#pragma region Constraint Help Function + + static YYCC::Constraints::Constraint GetOneBasedIndexConstraint() { + return YYCC::Constraints::Constraint { + [](const size_t& val) -> bool { + return val > 0u; + } + }; + } + +#pragma endregion + + #pragma region UnvirtContext Misc UnvirtContext::UnvirtContext() : - m_Root(), m_Splitter(), m_Help(nullptr), + m_Splitter(), m_Parser(), m_Help(), m_PageLen(10u), m_ListStyleIsFull(true), m_OrderExit(false), m_SearchPart(SearchPart::None), m_SearchIdxResult(), m_Ctx(nullptr), m_FileReader(nullptr), m_IsShallowRead(true) { - // create command root - CmdHelper::CommandRoot* root = &m_Root; - + // Setup command parser + // get root node first + CmdHelper::Nodes::RootNode& root = m_Parser.GetRoot(); + // setup tree root - ->Then((new CmdHelper::Literal("load")) - ->Then((new CmdHelper::Choice("stage", { "deep", "shallow"})) - ->Comment("The stage of loading. 'deep' will load to CKObject stage. 'shallow' will load to CKStateChunk stage.") - ->Then((new CmdHelper::StringArgument("filepath")) - ->Comment("The path to loading file.") - ->Executes( + .Then(CmdHelper::Nodes::Literal(u8"load") + .Then(CmdHelper::Nodes::Choice(u8"stage", { u8"deep", u8"shallow"}) + .Comment(u8"The stage of loading. 'deep' will load to CKObject stage. 'shallow' will load to CKStateChunk stage.") + .Then(CmdHelper::Nodes::StringArgument(u8"filepath") + .Comment(u8"The path to loading file.") + .Executes( std::bind(&UnvirtContext::ProcLoad, this, std::placeholders::_1), - "Load a Virtools composition." + u8"Load a Virtools composition." ) ) ) ) - ->Then((new CmdHelper::Literal("unload")) - ->Executes( + .Then(CmdHelper::Nodes::Literal(u8"unload") + .Executes( std::bind(&UnvirtContext::ProcUnLoad, this, std::placeholders::_1), - "Release loaded Virtools composition." + u8"Release loaded Virtools composition." ) ) - ->Then((new CmdHelper::Literal("save")) - ->Then((new CmdHelper::StringArgument("filepath")) - ->Comment("The path to save file.") - ->Executes( + .Then(CmdHelper::Nodes::Literal(u8"save") + .Then(CmdHelper::Nodes::StringArgument(u8"filepath") + .Comment(u8"The path to save file.") + .Executes( std::bind(&UnvirtContext::ProcSave, this, std::placeholders::_1), - "Save the loaded file into a new file." + u8"Save the loaded file into a new file." ) ) ) - ->Then((new CmdHelper::Literal("info")) - ->Executes( + .Then(CmdHelper::Nodes::Literal(u8"info") + .Executes( std::bind(&UnvirtContext::ProcInfo, this, std::placeholders::_1), - "Show the header info of loaded Virtools composition." + u8"Show the header info of loaded Virtools composition." ) ) - ->Then((new CmdHelper::Literal("ls")) - ->Then((new CmdHelper::Choice("part", { "obj", "mgr", "search"})) - ->Comment("Which list you want to list.") - ->Then((new CmdHelper::IntArgument("page", [](int32_t v) -> bool { return v > 0; })) - ->Comment("The page index. Start with 1.") - ->Executes( + .Then(CmdHelper::Nodes::Literal(u8"ls") + .Then(CmdHelper::Nodes::Choice(u8"part", { u8"obj", u8"mgr", u8"search"}) + .Comment(u8"Which list you want to list.") + .Then>(CmdHelper::Nodes::ArithmeticArgument(u8"page", GetOneBasedIndexConstraint()) + .Comment(u8"The page index. Start with 1.") + .Executes( std::bind(&UnvirtContext::ProcLs, this, std::placeholders::_1), - "List something about loaded Virtools composition." + u8"List something about loaded Virtools composition." ) ) ) ) - ->Then((new CmdHelper::Literal("data")) - ->Then((new CmdHelper::Choice("part", { "obj", "mgr"})) - ->Comment("Which list you want to show data.") - ->Then((new CmdHelper::IntArgument("index", [](int32_t v) -> bool { return v >= 0; })) - ->Comment("The index. Start with 0.") - ->Executes( + .Then(CmdHelper::Nodes::Literal(u8"data") + .Then(CmdHelper::Nodes::Choice(u8"part", { u8"obj", u8"mgr"}) + .Comment(u8"Which list you want to show data.") + .Then>(CmdHelper::Nodes::ArithmeticArgument(u8"index") + .Comment(u8"The index. Start with 0.") + .Executes( std::bind(&UnvirtContext::ProcData, this, std::placeholders::_1), - "Show the specific CKObject / CKBaseManager data." + u8"Show the specific CKObject / CKBaseManager data." ) ) ) ) - ->Then((new CmdHelper::Literal("chunk")) - ->Then((new CmdHelper::Choice("part", { "obj", "mgr"})) - ->Comment("Which list you want to show chunk.") - ->Then((new CmdHelper::IntArgument("index", [](int32_t v) -> bool { return v >= 0; })) - ->Comment("The index. Start with 0.") - ->Executes( + .Then(CmdHelper::Nodes::Literal(u8"chunk") + .Then(CmdHelper::Nodes::Choice(u8"part", { u8"obj", u8"mgr"}) + .Comment(u8"Which list you want to show chunk.") + .Then>(CmdHelper::Nodes::ArithmeticArgument(u8"index") + .Comment(u8"The index. Start with 0.") + .Executes( std::bind(&UnvirtContext::ProcChunk, this, std::placeholders::_1), - "Show the specific CKStateChunk data." + u8"Show the specific CKStateChunk data." ) ) ) ) - ->Then((new CmdHelper::Literal("search")) - ->Then((new CmdHelper::Choice("part", { "obj", "mgr"})) - ->Comment("Which list you want to search.") - ->Then((new CmdHelper::Choice("mode", { "plain", "re"})) - ->Comment("The search mode. `plain` will search by substring and `re` will do regex search.") - ->Then((new CmdHelper::StringArgument("text")) - ->Comment("The text or regex to search.") - ->Executes( + .Then(CmdHelper::Nodes::Literal(u8"search") + .Then(CmdHelper::Nodes::Choice(u8"part", { u8"obj", u8"mgr"}) + .Comment(u8"Which list you want to search.") + .Then(CmdHelper::Nodes::Choice(u8"mode", { u8"plain", u8"re"}) + .Comment(u8"The search mode. `plain` will search by substring and `re` will do regex search.") + .Then(CmdHelper::Nodes::StringArgument(u8"text") + .Comment(u8"The text or regex to search.") + .Executes( std::bind(&UnvirtContext::ProcSearch, this, std::placeholders::_1), - "Search object or manager by text or regex. Please note that the regex have limited UTF8 support and may cause undefined behavior." + u8"Search object or manager by text or regex. Please note that the regex have limited UTF8 support and may cause undefined behavior." ) ) ) ) ) - ->Then((new CmdHelper::Literal("items")) - ->Then((new CmdHelper::IntArgument("count", [](int32_t v) -> bool { return v > 0; })) - ->Comment("The count of items you want to show in one page.") - ->Executes( + .Then(CmdHelper::Nodes::Literal(u8"items") + .Then>(CmdHelper::Nodes::ArithmeticArgument(u8"count", GetOneBasedIndexConstraint()) + .Comment(u8"The count of items you want to show in one page.") + .Executes( std::bind(&UnvirtContext::ProcItems, this, std::placeholders::_1), - "Set up how many items should be listed in one page when using 'ls' command." + u8"Set up how many items should be listed in one page when using 'ls' command." ) ) ) - ->Then((new CmdHelper::Literal("style")) - ->Then((new CmdHelper::Choice("level", { "full", "simple"})) - ->Comment("The amount of showen content.") - ->Executes( + .Then(CmdHelper::Nodes::Literal(u8"style") + .Then(CmdHelper::Nodes::Choice(u8"level", { u8"full", u8"simple"}) + .Comment(u8"The amount of showen content.") + .Executes( std::bind(&UnvirtContext::ProcStyle, this, std::placeholders::_1), - "Change the detail level of showen data in `ls` command." + u8"Change the detail level of showen data in `ls` command." ) ) ) - ->Then((new CmdHelper::Literal("encoding")) - ->Then((new CmdHelper::EncodingArgument("enc")) - ->Comment("CKContext used encoding splitted by ','. Support mutiple encoding.") - ->Executes( + .Then(CmdHelper::Nodes::Literal(u8"encoding") + .Then(CmdHelper::Nodes::EncodingListArgument(u8"enc") + .Comment(u8"CKContext used encoding splitted by ','. Support mutiple encoding.") + .Executes( std::bind(&UnvirtContext::ProcEncoding, this, std::placeholders::_1), - "Set the encoding series for CKContext." + u8"Set the encoding series for CKContext." ) ) ) - ->Then((new CmdHelper::Literal("temp")) - ->Then((new CmdHelper::StringArgument("temppath")) - ->Comment("The path to Temp folder.") - ->Executes( + .Then(CmdHelper::Nodes::Literal(u8"temp") + .Then(CmdHelper::Nodes::StringArgument(u8"temppath") + .Comment(u8"The path to Temp folder.") + .Executes( std::bind(&UnvirtContext::ProcTemp, this, std::placeholders::_1), - "Set the Temp path for CKContext." + u8"Set the Temp path for CKContext." ) ) ) - ->Then((new CmdHelper::Literal("rsc")) - ->Then((new CmdHelper::Literal("clear")) - ->Executes( + .Then(CmdHelper::Nodes::Literal(u8"rsc") + .Then(CmdHelper::Nodes::Literal(u8"clear") + .Executes( std::bind(&UnvirtContext::ProcRsc, this, std::placeholders::_1, true), - "Clear all data resources paths." + u8"Clear all data resources paths." ) ) - ->Then((new CmdHelper::Literal("add")) - ->Then((new CmdHelper::StringArgument("datares")) - ->Comment("The data resources path .") - ->Executes( + .Then(CmdHelper::Nodes::Literal(u8"add") + .Then(CmdHelper::Nodes::StringArgument(u8"datares") + .Comment(u8"The data resources path .") + .Executes( std::bind(&UnvirtContext::ProcRsc, this, std::placeholders::_1, false), - "Add a path to let Virtools find resources." + u8"Add a path to let Virtools find resources." ) ) ) ) - ->Then((new CmdHelper::Literal("test")) - ->Executes( + .Then(CmdHelper::Nodes::Literal(u8"test") + .Executes( std::bind(&UnvirtContext::ProcTest, this, std::placeholders::_1), - "Call custom debugging function (only available in Debug mode)." + u8"Call custom debugging function (only available in Debug mode)." ) ) - ->Then((new CmdHelper::Literal("help")) - ->Executes( + .Then(CmdHelper::Nodes::Literal(u8"help") + .Executes( std::bind(&UnvirtContext::ProcHelp, this, std::placeholders::_1), - "Output this help page." + u8"Output this help page." ) ) - ->Then((new CmdHelper::Literal("exit")) - ->Executes( + .Then(CmdHelper::Nodes::Literal(u8"exit") + .Executes( std::bind(&UnvirtContext::ProcExit, this, std::placeholders::_1), - "Exit program." + u8"Exit program." ) ); - // create help - m_Help = root->RootHelp(); + // create help document + m_Help = m_Parser.Help(); - // create context - LibCmo::CK2::CKStartUp(); + // initialize CK engine and create context + LibCmo::CK2::CKERROR err = LibCmo::CK2::CKStartUp(); + if (err != LibCmo::CK2::CKERROR::CKERR_OK) + throw std::runtime_error("fail to initialize CK2 engine."); m_Ctx = new LibCmo::CK2::CKContext(); m_Ctx->SetOutputCallback(std::bind(&UnvirtContext::PrintContextMsg, this, std::placeholders::_1)); m_Ctx->SetGlobalImagesSaveOptions(LibCmo::CK2::CK_TEXTURE_SAVEOPTIONS::CKTEXTURE_EXTERNAL); } UnvirtContext::~UnvirtContext() { - if (m_Help != nullptr) - delete m_Help; - - // free context + // free context and shutdown CK engine. ClearDocument(); delete m_Ctx; LibCmo::CK2::CKShutdown(); @@ -212,26 +227,26 @@ namespace Unvirt::Context { void UnvirtContext::PrintContextMsg(LibCmo::CKSTRING msg) { if (msg != nullptr) { - fprintf(stdout, UNVIRT_TERMCOL_LIGHT_YELLOW(("[CKContext] ")) "%s\n", msg); + YYCC::ConsoleHelper::FormatLine(YYCC_COLOR_LIGHT_YELLOW(u8"[CKContext] ") "%s", msg); } } - - void UnvirtContext::PrintCommonInfo(const char* u8_fmt, ...) { + + void UnvirtContext::PrintCommonInfo(const char8_t* u8_fmt, ...) { va_list argptr; va_start(argptr, u8_fmt); - std::vfprintf(stdout, u8_fmt, argptr); + YYCC::ConsoleHelper::WriteLine( + YYCC::StringHelper::VPrintf(u8_fmt, argptr).c_str() + ); va_end(argptr); - std::fputc('\n', stdout); } - void UnvirtContext::PrintCommonError(const char* u8_fmt, ...) { + void UnvirtContext::PrintCommonError(const char8_t* 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); + YYCC::ConsoleHelper::FormatLine(YYCC_COLOR_LIGHT_RED(u8"%s"), + YYCC::StringHelper::VPrintf(u8_fmt, argptr).c_str() + ); va_end(argptr); - std::fputc('\n', stdout); } #pragma endregion @@ -239,23 +254,34 @@ namespace Unvirt::Context { #pragma region Main Run void UnvirtContext::Run() { - Unvirt::TerminalHelper::EnsureTerminalColor(); - Unvirt::TerminalHelper::EnsureTerminalEncoding(); - std::string u8cmd; + // Enable terminal color feature + YYCC::ConsoleHelper::EnableColorfulConsole(); + // Show banner + YYCC::ConsoleHelper::WriteLine(YYCC_COLOR_LIGHT_YELLOW(u8"Unvirt")); + YYCC::ConsoleHelper::WriteLine(YYCC_COLOR_LIGHT_YELLOW(u8"Type 'help' for more infomation. Type 'exit' to quit.")); + + // start process loop while (true) { // get command - TerminalHelper::GetCmdLine(u8cmd); + YYCC::ConsoleHelper::Write(YYCC_COLOR_GREEN(u8"> ")); + std::u8string u8cmd = YYCC::ConsoleHelper::ReadLine(); - // split cmd and parse it - auto cmds = m_Splitter.Convert(u8cmd); + // lex command line first + if (!m_Splitter.Lex(u8cmd)) { + this->PrintCommonError(u8"Lexer error \"%s\".\nType 'help' for usage.", u8cmd.c_str()); + continue; + } + // if result is empty, skip to next one + auto cmds = m_Splitter.GetResult(); if (cmds.empty()) continue; - // get sub command - if (!m_Root.RootConsume(cmds)) { - this->PrintCommonError("Syntax error \"%s\".\nType 'help' for usage.", u8cmd.c_str()); + // run command parser + if (!m_Parser.Parse(cmds)) { + this->PrintCommonError(u8"Parser error \"%s\".\nType 'help' for usage.", u8cmd.c_str()); } + // check whether sub processor need exit. if (m_OrderExit) { break; } @@ -266,15 +292,20 @@ namespace Unvirt::Context { #pragma region Proc Detail - void UnvirtContext::ProcLoad(const CmdHelper::ArgumentsMap* amap) { + void UnvirtContext::ProcLoad(const CmdHelper::ArgumentsMap& amap) { // check pre-requirement if (HasOpenedFile()) { - PrintCommonError("Already have a opened file. Close it before calling 'load'."); + PrintCommonError(u8"Already have a opened file. Close it before calling 'load'."); + return; + } + if (!m_Ctx->IsValidEncoding()) { + PrintCommonError(u8"You have not set encoding properly. Set it before loading by calling 'encoding'."); return; } - std::string filepath = *amap->Get("filepath"); - bool is_deep = *amap->Get("stage") == 0; + // get argument + std::u8string filepath = amap.Get(u8"filepath").Get(); + bool is_deep = amap.Get(u8"stage").Get() == 0u; // proc m_FileReader = new LibCmo::CK2::CKFileReader(m_Ctx); @@ -288,7 +319,7 @@ namespace Unvirt::Context { } if (err != LibCmo::CK2::CKERROR::CKERR_OK) { // fail to load. release all. - PrintCommonError("Fail to load file. Function return: %s\n\t%s", + PrintCommonError(u8"Fail to load file. Function return: %s\n\t%s", Unvirt::AccessibleValue::GetCkErrorName(err).c_str(), Unvirt::AccessibleValue::GetCkErrorDescription(err).c_str() ); @@ -297,10 +328,10 @@ namespace Unvirt::Context { } } - void UnvirtContext::ProcUnLoad(const CmdHelper::ArgumentsMap*) { + void UnvirtContext::ProcUnLoad(const CmdHelper::ArgumentsMap&) { // check pre-requirement if (!HasOpenedFile()) { - this->PrintCommonError("No loaded file."); + this->PrintCommonError(u8"No loaded file."); return; } @@ -308,14 +339,18 @@ namespace Unvirt::Context { ClearDocument(); } - void Unvirt::Context::UnvirtContext::ProcSave(const CmdHelper::ArgumentsMap* amap) { + void Unvirt::Context::UnvirtContext::ProcSave(const CmdHelper::ArgumentsMap& amap) { // check pre-requirement if (!HasOpenedFile()) { - PrintCommonError("No loaded file."); + PrintCommonError(u8"No loaded file."); + return; + } + if (!m_Ctx->IsValidEncoding()) { + PrintCommonError(u8"You have not set encoding properly. Set it before saving by calling 'encoding'."); return; } - std::string filepath = *amap->Get("filepath"); + std::u8string filepath = amap.Get(u8"filepath").Get(); // construct writer from reader LibCmo::CK2::CKFileWriter writer(m_Ctx, m_FileReader, m_IsShallowRead); @@ -329,7 +364,7 @@ namespace Unvirt::Context { LibCmo::CK2::CKERROR err = writer.Save(filepath.c_str()); if (err != LibCmo::CK2::CKERROR::CKERR_OK) { // fail to load. release all. - PrintCommonError("Fail to save file. Function return: %s\n\t%s", + PrintCommonError(u8"Fail to save file. Function return: %s\n\t%s", Unvirt::AccessibleValue::GetCkErrorName(err).c_str(), Unvirt::AccessibleValue::GetCkErrorDescription(err).c_str() ); @@ -340,10 +375,10 @@ namespace Unvirt::Context { } - void UnvirtContext::ProcInfo(const CmdHelper::ArgumentsMap*) { + void UnvirtContext::ProcInfo(const CmdHelper::ArgumentsMap&) { // check pre-requirement if (!HasOpenedFile()) { - PrintCommonError("No loaded file."); + PrintCommonError(u8"No loaded file."); return; } @@ -351,46 +386,46 @@ namespace Unvirt::Context { Unvirt::StructFormatter::PrintCKFileInfo(m_FileReader->GetFileInfo()); } - void UnvirtContext::ProcLs(const CmdHelper::ArgumentsMap* amap) { + void UnvirtContext::ProcLs(const CmdHelper::ArgumentsMap& amap) { // check pre-requirement if (!HasOpenedFile()) { - PrintCommonError("No loaded file."); + PrintCommonError(u8"No loaded file."); return; } // get 0 based page (-1) - size_t page = *amap->Get("page") - 1; + size_t page = amap.Get::ArgValue_t>(u8"page").Get() - 1u; // show list - switch (*amap->Get("part")) { - case 0: + switch (amap.Get(u8"part").Get()) { + case 0u: { // obj list Unvirt::StructFormatter::PrintObjectList( - m_FileReader->GetFileObjects(), - m_FileReader->GetFileInfo(), + m_FileReader->GetFileObjects(), + m_FileReader->GetFileInfo(), page, this->m_PageLen, m_ListStyleIsFull ); break; } - case 1: + case 1u: { // mgr list Unvirt::StructFormatter::PrintManagerList( - m_FileReader->GetManagersData(), + m_FileReader->GetManagersData(), page, this->m_PageLen, m_ListStyleIsFull ); break; } - case 2: + case 2u: { // search list switch (m_SearchPart) { case SearchPart::None: { - PrintCommonError("No search result to list."); + PrintCommonError(u8"No search result to list."); break; } case SearchPart::Object: @@ -417,60 +452,64 @@ namespace Unvirt::Context { } break; } + default: + throw std::runtime_error("unreachable code"); } } - void UnvirtContext::ProcData( const CmdHelper::ArgumentsMap* amap) { + void UnvirtContext::ProcData(const CmdHelper::ArgumentsMap& amap) { // check pre-requirement if (!HasOpenedFile()) { - PrintCommonError("No loaded file."); + PrintCommonError(u8"No loaded file."); return; } // get index - size_t index = *amap->Get("index"); + size_t index = amap.Get::ArgValue_t>(u8"index").Get(); // show data - switch (*amap->Get("part")) { - case 0: + switch (amap.Get(u8"part").Get()) { + case 0u: { if (index >= m_FileReader->GetFileObjects().size()) { - PrintCommonError("Index out of range."); + PrintCommonError(u8"Index out of range."); return; } Unvirt::StructFormatter::PrintCKObject(m_FileReader->GetFileObjects()[index].ObjPtr); break; } - case 1: + case 1u: { if (index >= m_FileReader->GetManagersData().size()) { - PrintCommonError("Index out of range."); + PrintCommonError(u8"Index out of range."); return; } // todo: finish manager display - PrintCommonError("Not supported now."); + PrintCommonError(u8"Not supported now."); //Unvirt::StructFormatter::PrintCKBaseManager(m_FileReader->GetManagersData()[index].Data); break; } + default: + throw std::runtime_error("unreachable code"); } } - void UnvirtContext::ProcChunk(const CmdHelper::ArgumentsMap* amap) { + void UnvirtContext::ProcChunk(const CmdHelper::ArgumentsMap& amap) { // check pre-requirement if (!HasOpenedFile()) { - PrintCommonError("No loaded file."); + PrintCommonError(u8"No loaded file."); return; } // get index - size_t index = *amap->Get("index"); + size_t index = amap.Get::ArgValue_t>(u8"index").Get(); // show data - switch (*amap->Get("part")) { + switch (amap.Get(u8"part").Get()) { case 0: { if (index >= m_FileReader->GetFileObjects().size()) { - PrintCommonError("Index out of range."); + PrintCommonError(u8"Index out of range."); return; } Unvirt::StructFormatter::PrintCKStateChunk(m_FileReader->GetFileObjects()[index].Data); @@ -479,29 +518,31 @@ namespace Unvirt::Context { case 1: { if (index >= m_FileReader->GetManagersData().size()) { - PrintCommonError("Index out of range."); + PrintCommonError(u8"Index out of range."); return; } Unvirt::StructFormatter::PrintCKStateChunk(m_FileReader->GetManagersData()[index].Data); break; } + default: + throw std::runtime_error("unreachable code"); } } - void UnvirtContext::ProcSearch(const CmdHelper::ArgumentsMap* amap) { + void UnvirtContext::ProcSearch(const CmdHelper::ArgumentsMap& amap) { // check pre-requirement if (!HasOpenedFile()) { - PrintCommonError("No loaded file."); + PrintCommonError(u8"No loaded file."); return; } - // get search text - std::string search_text(*amap->Get("text")); - + // get search text *amap->Get("text") + std::u8string search_text = amap.Get(u8"text").Get(); + // analyse search mode - std::function search_fct = [](const LibCmo::XContainer::XString&) -> bool { return false; }; - switch (*amap->Get("mode")) { - case 0: + std::function search_fct = nullptr; + switch (amap.Get(u8"mode").Get()) { + case 0u: { // plain mode search_fct = [&search_text](const LibCmo::XContainer::XString& cmp) -> bool { @@ -509,31 +550,37 @@ namespace Unvirt::Context { }; break; } - case 1: + case 1u: { // regex mode - + + // get ordinary search text + std::string ordinary_search_text = YYCC::EncodingHelper::ToOrdinary(search_text); // try construct regex std::regex re; try { - re = std::regex(search_text, std::regex_constants::ECMAScript); + re = std::regex(ordinary_search_text, std::regex_constants::ECMAScript); } catch (const std::regex_error& e) { - PrintCommonError("Invalid regular expressions: %s", e.what()); + PrintCommonError(u8"Invalid regular expressions: %s", e.what()); return; } // use copy ctor capture to input regex // because re will be freed when exiting this switch. search_fct = [re](const LibCmo::XContainer::XString& cmp) -> bool { - return std::regex_search(cmp, re); + // get ordinary name and comapre + std::string ordinary_cmp = YYCC::EncodingHelper::ToOrdinary(cmp); + return std::regex_search(ordinary_cmp, re); }; break; } + default: + throw std::runtime_error("unreachable code"); } // start search - switch (*amap->Get("part")) { - case 0: + switch (amap.Get(u8"part").Get()) { + case 0u: { // object m_SearchPart = SearchPart::Object; @@ -549,85 +596,91 @@ namespace Unvirt::Context { break; } - case 1: + case 1u: { // manager m_SearchPart = SearchPart::Manager; m_SearchIdxResult.clear(); - PrintCommonError("Not supported now."); + PrintCommonError(u8"Not supported now."); // todo: remove this return when fixing manager searching. return; break; } + default: + throw std::runtime_error("unreachable code"); } // report search result if (m_SearchIdxResult.empty()) { - PrintCommonInfo("Search done, but no result."); + PrintCommonInfo(u8"Search done, but no result."); } else { - PrintCommonInfo("Search done with %" PRIuSIZET " results. Use `ls search` to check them.", m_SearchIdxResult.size()); + PrintCommonInfo(u8"Search done with %" PRIuSIZET " results. Use `ls search` to check them.", m_SearchIdxResult.size()); } } - void UnvirtContext::ProcItems(const CmdHelper::ArgumentsMap* amap) { + void UnvirtContext::ProcItems(const CmdHelper::ArgumentsMap& amap) { // assign - m_PageLen = *amap->Get("count"); + m_PageLen = amap.Get::ArgValue_t>(u8"count").Get(); } - void UnvirtContext::ProcStyle(const CmdHelper::ArgumentsMap* amap) { + void UnvirtContext::ProcStyle(const CmdHelper::ArgumentsMap& amap) { // set list style level - switch (*amap->Get("level")) { - case 0: + switch (amap.Get(u8"level").Get()) { + case 0u: { // full level m_ListStyleIsFull = true; break; } - case 1: + case 1u: { // simple level m_ListStyleIsFull = false; break; } + default: + throw std::runtime_error("unreachable code"); } } - void UnvirtContext::ProcEncoding(const CmdHelper::ArgumentsMap* amap) { - const auto& encodings = *amap->Get("enc"); + void UnvirtContext::ProcEncoding(const CmdHelper::ArgumentsMap& amap) { + const auto& encodings = amap.Get(u8"enc").Get(); m_Ctx->SetEncoding(encodings); } - void UnvirtContext::ProcTemp(const CmdHelper::ArgumentsMap* amap) { + void UnvirtContext::ProcTemp(const CmdHelper::ArgumentsMap& amap) { // assign - if (!m_Ctx->GetPathManager()->SetTempFolder(amap->Get("temppath")->c_str())) { - PrintCommonError("Set temp folder failed. Check your path first."); + std::u8string temp_path = amap.Get(u8"temppath").Get(); + if (!m_Ctx->GetPathManager()->SetTempFolder(temp_path.c_str())) { + PrintCommonError(u8"Set temp folder failed. Check your path first."); } } - void Unvirt::Context::UnvirtContext::ProcRsc(const CmdHelper::ArgumentsMap* amap, bool isClear) { - if (isClear) { + void Unvirt::Context::UnvirtContext::ProcRsc(const CmdHelper::ArgumentsMap& amap, bool is_clear) { + if (is_clear) { m_Ctx->GetPathManager()->ClearPath(); } else { - if (!m_Ctx->GetPathManager()->AddPath(amap->Get("datares")->c_str())) { - PrintCommonError("Set data resource folder failed. Check your path first."); + std::u8string data_res = amap.Get(u8"datares").Get(); + if (!m_Ctx->GetPathManager()->AddPath(data_res.c_str())) { + PrintCommonError(u8"Set data resource folder failed. Check your path first."); } } } - void Unvirt::Context::UnvirtContext::ProcTest(const CmdHelper::ArgumentsMap* amap) { + void Unvirt::Context::UnvirtContext::ProcTest(const CmdHelper::ArgumentsMap& amap) { #if defined(LIBCMO_BUILD_DEBUG) // MARK: Add the debug code here. - + // todo: temporaryly write Transparent Column Fixer code. // move to independent app in future. - + // check pre-requirement if (!HasOpenedFile()) { - PrintCommonError("No loaded file."); + PrintCommonError(u8"No loaded file."); return; } if (!m_IsShallowRead) { - PrintCommonError("Transparent Column Fixer only accept shallow loaded file."); + PrintCommonError(u8"Transparent Column Fixer only accept shallow loaded file."); return; } @@ -653,21 +706,21 @@ namespace Unvirt::Context { } } } - } + } #else - PrintCommonError("Test command only available in Debug mode."); + PrintCommonError(u8"Test command only available in Debug mode."); #endif + } + + void Unvirt::Context::UnvirtContext::ProcHelp(const CmdHelper::ArgumentsMap&) { + m_Help.Print(); } - void Unvirt::Context::UnvirtContext::ProcHelp(const CmdHelper::ArgumentsMap*) { - m_Help->Print(); - } - - void UnvirtContext::ProcExit(const CmdHelper::ArgumentsMap*) { + void UnvirtContext::ProcExit(const CmdHelper::ArgumentsMap&) { m_OrderExit = true; } #pragma endregion -} + } diff --git a/Unvirt/UnvirtContext.hpp b/Unvirt/UnvirtContext.hpp index 3755f54..f25821d 100644 --- a/Unvirt/UnvirtContext.hpp +++ b/Unvirt/UnvirtContext.hpp @@ -2,13 +2,9 @@ #include #include "AccessibleValue.hpp" -#include "TerminalHelper.hpp" #include "StructFormatter.hpp" #include "CmdHelper.hpp" -#include -#include - namespace Unvirt::Context { class UnvirtContext { @@ -23,39 +19,39 @@ namespace Unvirt::Context { enum class SearchPart { None, Object, Manager }; - void PrintCommonInfo(const char* u8_fmt, ...); - void PrintCommonError(const char* u8_fmt, ...); + void PrintCommonInfo(const char8_t* u8_fmt, ...); + void PrintCommonError(const char8_t* u8_fmt, ...); - void ProcLoad(const CmdHelper::ArgumentsMap* amap); - void ProcUnLoad(const CmdHelper::ArgumentsMap* amap); - void ProcSave(const CmdHelper::ArgumentsMap* amap); - void ProcInfo(const CmdHelper::ArgumentsMap* amap); - void ProcLs(const CmdHelper::ArgumentsMap* amap); - void ProcData(const CmdHelper::ArgumentsMap* amap); - void ProcChunk(const CmdHelper::ArgumentsMap* amap); - void ProcSearch(const CmdHelper::ArgumentsMap* amap); - void ProcItems(const CmdHelper::ArgumentsMap* amap); - void ProcStyle(const CmdHelper::ArgumentsMap* amap); - void ProcEncoding(const CmdHelper::ArgumentsMap* amap); - void ProcTemp(const CmdHelper::ArgumentsMap* amap); - void ProcRsc(const CmdHelper::ArgumentsMap* amap, bool isClear); - void ProcTest(const CmdHelper::ArgumentsMap* amap); - void ProcHelp(const CmdHelper::ArgumentsMap* amap); - void ProcExit(const CmdHelper::ArgumentsMap* amap); + void ProcLoad(const CmdHelper::ArgumentsMap& amap); + void ProcUnLoad(const CmdHelper::ArgumentsMap& amap); + void ProcSave(const CmdHelper::ArgumentsMap& amap); + void ProcInfo(const CmdHelper::ArgumentsMap& amap); + void ProcLs(const CmdHelper::ArgumentsMap& amap); + void ProcData(const CmdHelper::ArgumentsMap& amap); + void ProcChunk(const CmdHelper::ArgumentsMap& amap); + void ProcSearch(const CmdHelper::ArgumentsMap& amap); + void ProcItems(const CmdHelper::ArgumentsMap& amap); + void ProcStyle(const CmdHelper::ArgumentsMap& amap); + void ProcEncoding(const CmdHelper::ArgumentsMap& amap); + void ProcTemp(const CmdHelper::ArgumentsMap& amap); + void ProcRsc(const CmdHelper::ArgumentsMap& amap, bool is_clear); + void ProcTest(const CmdHelper::ArgumentsMap& amap); + void ProcHelp(const CmdHelper::ArgumentsMap& amap); + void ProcExit(const CmdHelper::ArgumentsMap& amap); protected: bool HasOpenedFile(); void ClearDocument(); void PrintContextMsg(LibCmo::CKSTRING msg); - CmdHelper::CommandRoot m_Root; - CmdHelper::HelpDocument* m_Help; CmdHelper::CmdSplitter m_Splitter; + CmdHelper::CommandParser m_Parser; + CmdHelper::HelpDocument m_Help; size_t m_PageLen; bool m_ListStyleIsFull; SearchPart m_SearchPart; - LibCmo::XContainer::XArray m_SearchIdxResult; + std::vector m_SearchIdxResult; bool m_OrderExit; LibCmo::CK2::CKContext* m_Ctx;