diff --git a/Ballance/BMap/CMakeLists.txt b/Ballance/BMap/CMakeLists.txt index 1392960..8842119 100644 --- a/Ballance/BMap/CMakeLists.txt +++ b/Ballance/BMap/CMakeLists.txt @@ -35,7 +35,8 @@ PRIVATE # Install binary and headers install(TARGETS BMap EXPORT BMapTargets - LIBRARY DESTINATION ${NEMO_INSTALL_LIB_PATH} + RUNTIME DESTINATION ${NEMO_INSTALL_BIN_PATH} + LIBRARY DESTINATION ${NEMO_INSTALL_BIN_PATH} ARCHIVE DESTINATION ${NEMO_INSTALL_LIB_PATH} INCLUDES DESTINATION ${NEMO_INSTALL_INCLUDE_PATH} FILE_SET HEADERS DESTINATION ${NEMO_INSTALL_INCLUDE_PATH} diff --git a/Ballance/BMapInspector/BMapInspector.cpp b/Ballance/BMapInspector/BMapInspector.cpp index ec8d904..066259d 100644 --- a/Ballance/BMapInspector/BMapInspector.cpp +++ b/Ballance/BMapInspector/BMapInspector.cpp @@ -1,4 +1,6 @@ #include "Utils.hpp" +#include "Cli.hpp" +#include "Reporter.hpp" #include "Ruleset.hpp" #include #include @@ -11,16 +13,28 @@ using namespace yycc::patch::stream; namespace strop = yycc::string::op; namespace termcolor = yycc::carton::termcolor; +static bool ProcessCli() { + +} + +static void LoadVirtools() { + +} + +static void CheckRules() { + +} + int main(int argc, char *argv[]) { // Show splash - std::cout << termcolor::colored(u8"Ballance Map Inspector", termcolor::Color::LightYellow) + std::cout << termcolor::colored(u8"" BMAPINSP_NAME, termcolor::Color::LightYellow) << " (based on LibCmo " LIBCMO_VER_STR ") built at " __DATE__ " " __TIME__ << std::endl - << u8"The inspector for checking whether your Ballance custom map can be loaded without any issues." << std::endl + << u8"" BMAPINSP_DESC << std::endl << std::endl; // Create reporter - BMapInspector::Utils::Reporter reporter; + BMapInspector::Reporter reporter; // Get rule collection BMapInspector::Ruleset::RuleCollection rule_collection; diff --git a/Ballance/BMapInspector/CMakeLists.txt b/Ballance/BMapInspector/CMakeLists.txt index c71efee..cea0bc5 100644 --- a/Ballance/BMapInspector/CMakeLists.txt +++ b/Ballance/BMapInspector/CMakeLists.txt @@ -5,6 +5,8 @@ target_sources(BMapInspector PRIVATE BMapInspector.cpp Utils.cpp + Reporter.cpp + Cli.cpp Ruleset.cpp ) # Setup headers @@ -13,6 +15,8 @@ PRIVATE FILE_SET HEADERS FILES Utils.hpp + Reporter.hpp + Cli.hpp Ruleset.hpp ) # Setup header infomation diff --git a/Ballance/BMapInspector/Cli.cpp b/Ballance/BMapInspector/Cli.cpp new file mode 100644 index 0000000..2ab3f3f --- /dev/null +++ b/Ballance/BMapInspector/Cli.cpp @@ -0,0 +1,149 @@ +#include "Cli.hpp" +#include +#include + +namespace clap = yycc::carton::clap; + +namespace BMapInspector::Cli { + +#pragma region Request + + Request Request::FromHelpRequest() { + return Request(RequestKind::Help, std::nullopt, std::nullopt, std::nullopt, std::nullopt); + } + + Request Request::FromVersionRequest() { + return Request(RequestKind::Version, std::nullopt, std::nullopt, std::nullopt, std::nullopt); + } + + Request Request::FromWorkRequest(Utils::ReportLevel level, + const std::u8string_view& file_path, + const std::u8string_view& encoding, + const std::u8string_view& ballance_path) { + return Request(RequestKind::Work, level, file_path, encoding, ballance_path); + } + + Request::Request(RequestKind kind, + std::optional level, + std::optional file_path, + std::optional encoding, + std::optional ballance_path) : + kind(kind), level(level), file_path(file_path), encoding(encoding), ballance_path(ballance_path) {} + + Request::~Request() {} + + RequestKind Request::GetRequestKind() const { + return this->kind; + } + + Utils::ReportLevel Request::GetLevel() const { + if (this->level.has_value()) return this->level.value(); + else throw std::logic_error("can not visit this property in current kind"); + } + + std::u8string_view Request::GetFilePath() const { + if (this->file_path.has_value()) return this->file_path.value(); + else throw std::logic_error("can not visit this property in current kind"); + } + + std::u8string_view Request::GetEncoding() const { + if (this->encoding.has_value()) return this->encoding.value(); + else throw std::logic_error("can not visit this property in current kind"); + } + + std::u8string_view Request::GetBallancePath() const { + if (this->ballance_path.has_value()) return this->ballance_path.value(); + else throw std::logic_error("can not visit this property in current kind"); + } + +#pragma endregion + +#pragma region Custom Validators + + struct ReportLevelValidator { + using ReturnType = Utils::ReportLevel; + std::optional validate(const std::u8string_view& sv) const { return Utils::ParseReportLevel(sv); } + }; + +#pragma endregion + + + Result parse() { + // Create options + clap::option::OptionCollection opt_collection; + auto opt_file = opt_collection.add_option( + clap::option::Option(u8"i", u8"file", u8"FILE", u8R"(The path to map file loaded by this program. +This field is required.)")); + auto opt_ballance = opt_collection.add_option( + clap::option::Option(u8"b", u8"ballance", u8"DIR", u8R"(The path to your Ballance root directory for finding resources. +This field is required.)")); + auto opt_encoding = opt_collection.add_option( + clap::option::Option(u8"e", u8"encoding", u8"ENC", u8R"(The encoding used when loading this map file. +Frequently used encoding is "cp1252" and "gbk". +Default value is "cp1252".)")); + auto opt_level = opt_collection.add_option( + clap::option::Option(u8"l", u8"level", u8"LEVEL", u8R"(Set the filter level for checker output. +Available levels are "error", "warning" and "info". +Default value is "info".)")); + auto opt_version = opt_collection.add_option( + clap::option::Option(u8"v", u8"version", std::nullopt, u8"Print version of this program.")); + auto opt_help = opt_collection.add_option(clap::option::Option(u8"h", u8"help", std::nullopt, u8"Print this page.")); + // Create variables + clap::variable::VariableCollection var_collection; + // Create manifest + clap::summary::Summary summary(u8"" BMAPINSP_NAME, u8"yyc12345", u8"Universal", u8"" BMAPINSP_DESC); + // Create application + clap::application::Application app(std::move(summary), std::move(opt_collection), std::move(var_collection)); + + // Create parser and parse command line arguments + auto rv_parser = clap::parser::Parser::from_system(app); + if (!rv_parser.has_value()) return std::unexpected(Error::BadParse); + auto& parser = rv_parser.value(); + + // Check version and help first + if (auto help_flag = parser.get_flag_option(opt_help); help_flag.has_value()) { + return Request::FromHelpRequest(); + } + if (auto version_flag = parser.get_flag_option(opt_version); version_flag.has_value()) { + return Request::FromVersionRequest(); + } + + // Check other args + std::u8string file_rv; + if (parser.has_option(opt_file)) { + auto file_value = parser.get_value_option(opt_file); + if (!file_value.has_value()) return std::unexpected(Error::BadFile); + file_rv = std::move(file_value.value()); + } else { + return std::unexpected(Error::NoFile); + } + std::u8string ballance_rv; + if (parser.has_option(opt_ballance)) { + auto ballance_value = parser.get_value_option(opt_ballance); + if (!ballance_value.has_value()) return std::unexpected(Error::BadBallance); + ballance_rv = std::move(ballance_value.value()); + } else { + return std::unexpected(Error::NoBallance); + } + std::u8string encoding_rv; + if (parser.has_option(opt_encoding)) { + auto encoding_value = parser.get_value_option(opt_encoding); + if (!encoding_value.has_value()) return std::unexpected(Error::BadEncoding); + encoding_rv = std::move(encoding_value.value()); + } else { + encoding_rv = u8"cp1252"; + } + Utils::ReportLevel level_rv; + if (parser.has_option(opt_level)) { + auto level_value = parser.get_value_option(opt_level); + if (!level_value.has_value()) return std::unexpected(Error::BadLevel); + level_rv = std::move(level_value.value()); + } else { + level_rv = Utils::ReportLevel::Info; + } + + // Return result + return Request::FromWorkRequest(level_rv, file_rv, encoding_rv, ballance_rv); + } + +} // namespace BMapInspector::Cli diff --git a/Ballance/BMapInspector/Cli.hpp b/Ballance/BMapInspector/Cli.hpp new file mode 100644 index 0000000..4c9eff5 --- /dev/null +++ b/Ballance/BMapInspector/Cli.hpp @@ -0,0 +1,68 @@ +#pragma once +#include "Utils.hpp" +#include "Reporter.hpp" +#include +#include +#include +#include +#include + +namespace BMapInspector::Cli { + + enum class RequestKind { + Help, + Version, + Work, + }; + + class Request { + public: + static Request FromHelpRequest(); + static Request FromVersionRequest(); + static Request FromWorkRequest(Utils::ReportLevel level, + const std::u8string_view& file_path, + const std::u8string_view& encoding, + const std::u8string_view& ballance_path); + + private: + Request(RequestKind kind, + std::optional level, + std::optional file_path, + std::optional encoding, + std::optional ballance_path); + + public: + ~Request(); + YYCC_DEFAULT_COPY_MOVE(Request) + + public: + RequestKind GetRequestKind() const; + Utils::ReportLevel GetLevel() const; + std::u8string_view GetFilePath() const; + std::u8string_view GetEncoding() const; + std::u8string_view GetBallancePath() const; + + private: + RequestKind kind; ///< The kind of this request. + std::optional level; ///< The filter level. + std::optional file_path; ///< The path to loaded map file. + std::optional encoding; ///< The encoding used when loading map file. + std::optional ballance_path; ///< The path to Ballance root directory for loading resources. + }; + + enum class Error { + BadParse, ///< Error occurs when executing parser. + NoFile, ///< User do not specify file path for loading. + BadFile, ///< User specified file path is bad. + NoBallance, ///< User do not specify Ballance directory for loading. + BadBallance, ///< User specified Ballance directory is bad. + BadEncoding, ///< User given encoding value is bad. + BadLevel, ///< User given level name is bad. + }; + + template + using Result = std::expected; + + Result parse(); + +} // namespace BMapInspector::Cli diff --git a/Ballance/BMapInspector/Reporter.cpp b/Ballance/BMapInspector/Reporter.cpp new file mode 100644 index 0000000..fe23956 --- /dev/null +++ b/Ballance/BMapInspector/Reporter.cpp @@ -0,0 +1,107 @@ +#include "Reporter.hpp" +#include +#include +#include +#include +#include + +using namespace yycc::patch::stream; +namespace strop = yycc::string::op; +namespace termcolor = yycc::carton::termcolor; + +namespace BMapInspector { + +#pragma region Reporter + + Reporter::Reporter() {} + + Reporter::~Reporter() {} + + void Reporter::AddReport(Utils::ReportLevel level, const std::u8string_view &rule, const std::u8string_view &content) { + this->reports.emplace_back(Report{ + .level = level, + .rule = std::u8string(rule), + .content = std::u8string(content), + }); + } + + void Reporter::WriteInfo(const std::u8string_view &rule, const std::u8string_view &content) { + this->AddReport(Utils::ReportLevel::Info, rule, content); + } + + void Reporter::FormatInfo(const std::u8string_view &rule, const char8_t *fmt, ...) { + va_list argptr; + va_start(argptr, fmt); + this->WriteInfo(rule, strop::vprintf(fmt, argptr)); + va_end(argptr); + } + + void Reporter::WriteWarning(const std::u8string_view &rule, const std::u8string_view &content) { + this->AddReport(Utils::ReportLevel::Warning, rule, content); + } + void Reporter::FormatWarning(const std::u8string_view &rule, const char8_t *fmt, ...) { + va_list argptr; + va_start(argptr, fmt); + this->WriteWarning(rule, strop::vprintf(fmt, argptr)); + va_end(argptr); + } + + void Reporter::WriteError(const std::u8string_view &rule, const std::u8string_view &content) { + this->AddReport(Utils::ReportLevel::Error, rule, content); + } + + void Reporter::FormatError(const std::u8string_view &rule, const char8_t *fmt, ...) { + va_list argptr; + va_start(argptr, fmt); + this->WriteError(rule, strop::vprintf(fmt, argptr)); + va_end(argptr); + } + + void Reporter::PrintConclusion() const { + // Conclude count + size_t cnt_err = 0, cnt_warn = 0, cnt_info = 0; + for (const auto &report : this->reports) { + switch (report.level) { + case Utils::ReportLevel::Error: + ++cnt_err; + break; + case Utils::ReportLevel::Warning: + ++cnt_warn; + break; + case Utils::ReportLevel::Info: + ++cnt_info; + break; + } + } + + // Show in console + termcolor::cprintln(strop::printf(u8"Total %" PRIuSIZET " error(s), %" PRIuSIZET " warning(s) and %" PRIuSIZET " info(s).", + cnt_err, + cnt_warn, + cnt_info), + termcolor::Color::LightBlue); + } + + void Reporter::PrintReport() const { + // Print all entries by different color + for (const auto &report : this->reports) { + switch (report.level) { + case Utils::ReportLevel::Error: + termcolor::cprintln(strop::printf(u8"[ERROR] [RULE: %s] %s", report.rule.c_str(), report.content.c_str()), + termcolor::Color::LightRed); + break; + case Utils::ReportLevel::Warning: + termcolor::cprintln(strop::printf(u8"[WARNING] [RULE: %s] %s", report.rule.c_str(), report.content.c_str()), + termcolor::Color::LightYellow); + break; + case Utils::ReportLevel::Info: + termcolor::cprintln(strop::printf(u8"[INFO] [RULE: %s] %s", report.rule.c_str(), report.content.c_str()), + termcolor::Color::Default); + break; + } + } + } + +#pragma endregion + +} // namespace BMapInspector::Utils diff --git a/Ballance/BMapInspector/Reporter.hpp b/Ballance/BMapInspector/Reporter.hpp new file mode 100644 index 0000000..6ca987e --- /dev/null +++ b/Ballance/BMapInspector/Reporter.hpp @@ -0,0 +1,42 @@ +#pragma once +#include "Utils.hpp" +#include +#include +#include +#include +#include + +namespace BMapInspector { + + struct Report { + Utils::ReportLevel level; ///< The level of this report. + std::u8string rule; ///< The name of rule adding this report. + std::u8string content; ///< The content of this report. + }; + + class Reporter { + public: + Reporter(); + ~Reporter(); + YYCC_DEFAULT_COPY_MOVE(Reporter) + + private: + void AddReport(Utils::ReportLevel level, const std::u8string_view& rule, const std::u8string_view& content); + + public: + void WriteInfo(const std::u8string_view& rule, const std::u8string_view& content); + void FormatInfo(const std::u8string_view& rule, const char8_t* fmt, ...); + void WriteWarning(const std::u8string_view& rule, const std::u8string_view& content); + void FormatWarning(const std::u8string_view& rule, const char8_t* fmt, ...); + void WriteError(const std::u8string_view& rule, const std::u8string_view& content); + void FormatError(const std::u8string_view& rule, const char8_t* fmt, ...); + + public: + void PrintConclusion() const; + void PrintReport() const; + + private: + std::vector reports; + }; + +} // namespace BMapInspector::Utils diff --git a/Ballance/BMapInspector/Utils.cpp b/Ballance/BMapInspector/Utils.cpp index 832b959..b670e0e 100644 --- a/Ballance/BMapInspector/Utils.cpp +++ b/Ballance/BMapInspector/Utils.cpp @@ -1,107 +1,20 @@ #include "Utils.hpp" -#include -#include -#include -#include -#include - -using namespace yycc::patch::stream; -namespace strop = yycc::string::op; -namespace termcolor = yycc::carton::termcolor; +#include +#include namespace BMapInspector::Utils { -#pragma region Reporter - - Reporter::Reporter() {} - - Reporter::~Reporter() {} - - void Reporter::AddReport(ReportKind kind, const std::u8string_view &rule, const std::u8string_view &content) { - this->reports.emplace_back(Report{ - .kind = kind, - .rule = std::u8string(rule), - .content = std::u8string(content), - }); + std::optional ParseReportLevel(const std::u8string_view &value) { + if (value == u8"error") return ReportLevel::Error; + else if (value == u8"warning") return ReportLevel::Warning; + else if (value == u8"info") return ReportLevel::Info; + return std::nullopt; } - void Reporter::WriteInfo(const std::u8string_view &rule, const std::u8string_view &content) { - this->AddReport(ReportKind::Info, rule, content); + bool FilterReportLevel(ReportLevel check, ReportLevel filter) { + auto num_check = yycc::cenum::integer(check); + auto num_filter = yycc::cenum::integer(filter); + return num_check <= num_filter; } - void Reporter::FormatInfo(const std::u8string_view &rule, const char8_t *fmt, ...) { - va_list argptr; - va_start(argptr, fmt); - this->WriteInfo(rule, strop::vprintf(fmt, argptr)); - va_end(argptr); - } - - void Reporter::WriteWarning(const std::u8string_view &rule, const std::u8string_view &content) { - this->AddReport(ReportKind::Warning, rule, content); - } - void Reporter::FormatWarning(const std::u8string_view &rule, const char8_t *fmt, ...) { - va_list argptr; - va_start(argptr, fmt); - this->WriteWarning(rule, strop::vprintf(fmt, argptr)); - va_end(argptr); - } - - void Reporter::WriteError(const std::u8string_view &rule, const std::u8string_view &content) { - this->AddReport(ReportKind::Error, rule, content); - } - - void Reporter::FormatError(const std::u8string_view &rule, const char8_t *fmt, ...) { - va_list argptr; - va_start(argptr, fmt); - this->WriteError(rule, strop::vprintf(fmt, argptr)); - va_end(argptr); - } - - void Reporter::PrintConclusion() const { - // Conclude count - size_t cnt_err = 0, cnt_warn = 0, cnt_info = 0; - for (const auto &report : this->reports) { - switch (report.kind) { - case ReportKind::Error: - ++cnt_err; - break; - case ReportKind::Warning: - ++cnt_warn; - break; - case ReportKind::Info: - ++cnt_info; - break; - } - } - - // Show in console - termcolor::cprintln(strop::printf(u8"Total %" PRIuSIZET " error(s), %" PRIuSIZET " warning(s) and %" PRIuSIZET " info(s).", - cnt_err, - cnt_warn, - cnt_info), - termcolor::Color::LightBlue); - } - - void Reporter::PrintReport() const { - // Print all entries by different color - for (const auto &report : this->reports) { - switch (report.kind) { - case ReportKind::Error: - termcolor::cprintln(strop::printf(u8"[ERROR] [RULE: %s] %s", report.rule.c_str(), report.content.c_str()), - termcolor::Color::LightRed); - break; - case ReportKind::Warning: - termcolor::cprintln(strop::printf(u8"[WARNING] [RULE: %s] %s", report.rule.c_str(), report.content.c_str()), - termcolor::Color::LightYellow); - break; - case ReportKind::Info: - termcolor::cprintln(strop::printf(u8"[INFO] [RULE: %s] %s", report.rule.c_str(), report.content.c_str()), - termcolor::Color::Default); - break; - } - } - } - -#pragma endregion - } // namespace BMapInspector::Utils diff --git a/Ballance/BMapInspector/Utils.hpp b/Ballance/BMapInspector/Utils.hpp index f0cfe3e..0b1d9fa 100644 --- a/Ballance/BMapInspector/Utils.hpp +++ b/Ballance/BMapInspector/Utils.hpp @@ -1,45 +1,29 @@ #pragma once -#include -#include -#include +#include +#include #include -#include - -#define PRIuSIZET "zu" namespace BMapInspector::Utils { - enum class ReportKind { Error, Warning, Info }; +#define PRIuSIZET "zu" - struct Report { - ReportKind kind; ///< The kind of this report. - std::u8string rule; ///< The name of rule adding this report. - std::u8string content; ///< The content of this report. +#define BMAPINSP_NAME "Ballance Map Inspector" +#define BMAPINSP_DESC "The inspector for checking whether your Ballance custom map can be loaded without any issues." + + enum class ReportLevel : std::uint32_t { + Error = 0, + Warning = 1, + Info = 2, }; - class Reporter { - public: - Reporter(); - ~Reporter(); - YYCC_DEFAULT_COPY_MOVE(Reporter) + std::optional ParseReportLevel(const std::u8string_view& value); - private: - void AddReport(ReportKind kind, const std::u8string_view& rule, const std::u8string_view& content); - - public: - void WriteInfo(const std::u8string_view& rule, const std::u8string_view& content); - void FormatInfo(const std::u8string_view& rule, const char8_t* fmt, ...); - void WriteWarning(const std::u8string_view& rule, const std::u8string_view& content); - void FormatWarning(const std::u8string_view& rule, const char8_t* fmt, ...); - void WriteError(const std::u8string_view& rule, const std::u8string_view& content); - void FormatError(const std::u8string_view& rule, const char8_t* fmt, ...); - - public: - void PrintConclusion() const; - void PrintReport() const; - - private: - std::vector reports; - }; + /** + * @brief Check whether given report level can pass given filter. + * @param[in] check The level for checking whether it can pass filter. + * @param[in] filter The level of filter. + * @return True if is can pass, otherwise false. + */ + bool FilterReportLevel(ReportLevel check, ReportLevel filter); } // namespace BMapInspector::Utils