From ebbea473a415c4a6b81b8752449cadae9e4c5362 Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Mon, 2 Feb 2026 14:17:31 +0800 Subject: [PATCH] feat: finish BMapInspector framework --- .clang-format | 2 +- Ballance/BMapInspector/BMapInspector.cpp | 58 +++++-- Ballance/BMapInspector/CMakeLists.txt | 6 +- Ballance/BMapInspector/Cli.hpp | 11 +- Ballance/BMapInspector/Map.cpp | 158 ++++++++++++++++++ Ballance/BMapInspector/Map.hpp | 68 ++++++++ Ballance/BMapInspector/Rule.cpp | 31 ++++ .../BMapInspector/{Ruleset.hpp => Rule.hpp} | 27 +-- Ballance/BMapInspector/Ruleset.cpp | 53 ------ 9 files changed, 319 insertions(+), 95 deletions(-) create mode 100644 Ballance/BMapInspector/Map.cpp create mode 100644 Ballance/BMapInspector/Map.hpp create mode 100644 Ballance/BMapInspector/Rule.cpp rename Ballance/BMapInspector/{Ruleset.hpp => Rule.hpp} (54%) delete mode 100644 Ballance/BMapInspector/Ruleset.cpp diff --git a/.clang-format b/.clang-format index 5fe266b..33c5e47 100644 --- a/.clang-format +++ b/.clang-format @@ -193,7 +193,7 @@ PenaltyBreakTemplateDeclaration: 10 PenaltyExcessCharacter: 50 PenaltyIndentedWhitespace: 0 PenaltyReturnTypeOnItsOwnLine: 300 -PointerAlignment: Right +PointerAlignment: Left PPIndentWidth: -1 QualifierAlignment: Leave ReferenceAlignment: Pointer diff --git a/Ballance/BMapInspector/BMapInspector.cpp b/Ballance/BMapInspector/BMapInspector.cpp index 1065aab..968500b 100644 --- a/Ballance/BMapInspector/BMapInspector.cpp +++ b/Ballance/BMapInspector/BMapInspector.cpp @@ -1,7 +1,8 @@ #include "Utils.hpp" -#include "Cli.hpp" #include "Reporter.hpp" -#include "Ruleset.hpp" +#include "Cli.hpp" +#include "Map.hpp" +#include "Rule.hpp" #include #include #include @@ -24,7 +25,7 @@ static void PrintSplash() { static std::optional AcceptArgs() { auto request = BMapInspector::Cli::parse(); if (request.has_value()) { - return request.value(); + return std::move(request.value()); } else { using BMapInspector::Cli::Error; @@ -62,23 +63,51 @@ static std::optional AcceptArgs() { } } -static std::optional LoadVirtools(BMapInspector::Cli::Args& args) { - return std::nullopt; +static std::optional LoadLevel(BMapInspector::Cli::Args& args) { + auto level = BMapInspector::Map::load(args); + if (level.has_value()) { + return std::move(level.value()); + } else { + using BMapInspector::Map::Error; + + std::u8string err_words; + switch (level.error()) { + case Error::BadTempDir: + err_words = u8"Can not set temporary directory for loading."; + break; + case Error::BadBallance: + err_words = u8"Can not find Ballance texture directory."; + break; + case Error::BadEncoding: + err_words = u8"Can not set encoding with your given name."; + break; + case Error::BadMap: + err_words = u8"Can not load your given map file."; + break; + default: + err_words = u8"Unknown error."; + break; + } + + termcolor::cprintln(err_words, Color::Red); + termcolor::cprintln(u8"Please carefully check your map file and parameters for loading this map file.", Color::Red); + return std::nullopt; + } } -static void CheckRules(BMapInspector::Ruleset::RuleContext& ctx) { +static void CheckRules(BMapInspector::Cli::Args& args, BMapInspector::Map::Level& level) { // Create reporter BMapInspector::Reporter::Reporter reporter; // Get rule collection - BMapInspector::Ruleset::RuleCollection rule_collection; + BMapInspector::Rule::Ruleset ruleset; // Show rule infos - std::cout << strop::printf(u8"Total %" PRIuSIZET " rule(s) are loaded.", rule_collection.GetRuleCount()) << std::endl + std::cout << strop::printf(u8"Total %" PRIuSIZET " rule(s) are loaded.", ruleset.GetRuleCount()) << std::endl << u8"Check may take few minutes. Please do not close this console..." << std::endl; // Check rules one by one - for (auto* rule : rule_collection.GetRules()) { - rule->Check(reporter, ctx); + for (auto* rule : ruleset.GetRules()) { + rule->Check(reporter, level); } // Show report conclusion @@ -91,6 +120,9 @@ static void CheckRules(BMapInspector::Ruleset::RuleContext& ctx) { // Print report in detail using BMapInspector::Utils::ReportLevel; for (const auto& report : reporter.GetReports()) { + // Filter report first + if (!BMapInspector::Utils::FilterReportLevel(report.level, args.level)) continue; + // Okey, output this report. switch (report.level) { case ReportLevel::Error: termcolor::cprintln(strop::printf(u8"[ERROR] [RULE: %s] %s", report.rule.c_str(), report.content.c_str()), Color::Red); @@ -111,9 +143,9 @@ int main(int argc, char* argv[]) { PrintSplash(); std::cout << std::endl; - auto ctx = LoadVirtools(args.value()); - if (ctx.has_value()) { - CheckRules(ctx.value()); + auto level = LoadLevel(args.value()); + if (level.has_value()) { + CheckRules(args.value(), level.value()); } } return 0; diff --git a/Ballance/BMapInspector/CMakeLists.txt b/Ballance/BMapInspector/CMakeLists.txt index cea0bc5..30cc125 100644 --- a/Ballance/BMapInspector/CMakeLists.txt +++ b/Ballance/BMapInspector/CMakeLists.txt @@ -7,7 +7,8 @@ PRIVATE Utils.cpp Reporter.cpp Cli.cpp - Ruleset.cpp + Map.cpp + Rule.cpp ) # Setup headers target_sources(BMapInspector @@ -17,7 +18,8 @@ FILES Utils.hpp Reporter.hpp Cli.hpp - Ruleset.hpp + Map.hpp + Rule.hpp ) # Setup header infomation target_include_directories(BMapInspector diff --git a/Ballance/BMapInspector/Cli.hpp b/Ballance/BMapInspector/Cli.hpp index 0a375c6..dad45ae 100644 --- a/Ballance/BMapInspector/Cli.hpp +++ b/Ballance/BMapInspector/Cli.hpp @@ -1,6 +1,5 @@ #pragma once #include "Utils.hpp" -#include "Reporter.hpp" #include #include #include @@ -17,13 +16,13 @@ namespace BMapInspector::Cli { }; 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. + 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. + BadLevel, ///< User given level name is bad. }; template diff --git a/Ballance/BMapInspector/Map.cpp b/Ballance/BMapInspector/Map.cpp new file mode 100644 index 0000000..17f54ab --- /dev/null +++ b/Ballance/BMapInspector/Map.cpp @@ -0,0 +1,158 @@ +#include "Map.hpp" +#include +#include +#include + +namespace C = LibCmo::CK2; +namespace O = LibCmo::CK2::ObjImpls; + +namespace BMapInspector::Map { + +#pragma region Level + + Level::Level(const Cli::Args& args) : m_Context(nullptr), m_LoadStatus() { + // Create contexy + this->m_Context = new C::CKContext(); + // Callback for eat all output. + this->m_Context->SetOutputCallback([](LibCmo::CKSTRING strl) -> void {}); + // Set temp folder + auto pm = m_Context->GetPathManager(); + auto temp_dir = std::filesystem::temp_directory_path(); + auto our_temp_dir = temp_dir / u8"88797AB5-915E-39AD-4800-BF1B0F59E207"; + std::filesystem::create_directories(our_temp_dir); + if (!pm->SetTempFolder(our_temp_dir.u8string().c_str())) { + this->m_LoadStatus = std::unexpected(Error::BadTempDir); + return; + } + // Set ballance textures directory + auto ballance_dir = std::filesystem::path(args.ballance_path); + auto textures_dir = ballance_dir / u8"Textures"; + if (!pm->AddPath(textures_dir.u8string().c_str())) { + this->m_LoadStatus = std::unexpected(Error::BadBallance); + return; + } + // Set encoding + LibCmo::XContainer::XArray encs; + encs.emplace_back(args.encoding); + this->m_Context->SetEncoding(encs); + if (!this->m_Context->IsValidEncoding()) { + this->m_LoadStatus = std::unexpected(Error::BadEncoding); + return; + } + // set default texture save mode is external + this->m_Context->SetGlobalImagesSaveOptions(C::CK_TEXTURE_SAVEOPTIONS::CKTEXTURE_EXTERNAL); + // set default file write mode is whole compressed + this->m_Context->SetFileWriteMode(C::CK_FILE_WRITEMODE::CKFILE_WHOLECOMPRESSED); + + // Create temp ckfile and load + C::CKFileReader reader(m_Context); + auto err = reader.DeepLoad(args.file_path.c_str()); + if (err != C::CKERROR::CKERR_OK) { + // failed. clear document and return + this->m_Context->ClearAll(); + this->m_LoadStatus = std::unexpected(Error::BadMap); + return; + } + + // Fill our list + for (const auto& fileobj : reader.GetFileObjects()) { + auto ptr = fileobj.ObjPtr; + if (ptr == nullptr) continue; + + switch (fileobj.ObjectCid) { + case LibCmo::CK2::CK_CLASSID::CKCID_GROUP: + m_ObjGroups.emplace_back(static_cast(ptr)); + break; + case LibCmo::CK2::CK_CLASSID::CKCID_3DOBJECT: + m_Obj3dObjects.emplace_back(static_cast(ptr)); + break; + case LibCmo::CK2::CK_CLASSID::CKCID_MESH: + m_ObjMeshes.emplace_back(static_cast(ptr)); + break; + case LibCmo::CK2::CK_CLASSID::CKCID_MATERIAL: + m_ObjMaterials.emplace_back(static_cast(ptr)); + break; + case LibCmo::CK2::CK_CLASSID::CKCID_TEXTURE: + m_ObjTextures.emplace_back(static_cast(ptr)); + break; + case LibCmo::CK2::CK_CLASSID::CKCID_TARGETLIGHT: + m_ObjTargetLights.emplace_back(static_cast(ptr)); + break; + default: + break; // skip unknow objects + } + } + // Okey + this->m_LoadStatus = {}; + } + + Level::~Level() { + if (this->m_Context != nullptr) { + delete this->m_Context; + } + } + + YYCC_IMPL_MOVE_CTOR(Level, rhs) : m_Context(rhs.m_Context) { + rhs.m_Context = nullptr; + } + + YYCC_IMPL_MOVE_OPER(Level, rhs) { + this->m_Context = rhs.m_Context; + rhs.m_Context = nullptr; + return *this; + } + +#define CHECK_STATUS(this) \ + if (!this->m_LoadStatus.has_value()) throw std::logic_error("can not fetch anything from not loaded level"); + + Result Level::GetLoadStatus() const { + return this->m_LoadStatus; + } + + C::CKContext* Level::GetCKContext() const { + CHECK_STATUS(this) + return this->m_Context; + } + + const std::vector& Level::GetGroups() const { + CHECK_STATUS(this) + return this->m_ObjGroups; + } + + const std::vector& Level::Get3dObjects() const { + CHECK_STATUS(this) + return this->m_Obj3dObjects; + } + + const std::vector& Level::GetMeshes() const { + CHECK_STATUS(this) + return this->m_ObjMeshes; + } + + const std::vector& Level::GetMaterials() const { + CHECK_STATUS(this) + return this->m_ObjMaterials; + } + + const std::vector& Level::GetTextures() const { + CHECK_STATUS(this) + return this->m_ObjTextures; + } + + const std::vector& Level::GetTargetLights() const { + CHECK_STATUS(this) + return this->m_ObjTargetLights; + } + +#undef CHECK_STATUS + +#pragma endregion + + Result load(const Cli::Args& args) { + Level rv(args); + auto status = rv.GetLoadStatus(); + if (status.has_value()) return rv; + else return std::unexpected(status.error()); + } + +} // namespace BMapInspector::Map diff --git a/Ballance/BMapInspector/Map.hpp b/Ballance/BMapInspector/Map.hpp new file mode 100644 index 0000000..d17987d --- /dev/null +++ b/Ballance/BMapInspector/Map.hpp @@ -0,0 +1,68 @@ +#pragma once +#include "Cli.hpp" +#include +#include +#include +#include +#include +#include + +namespace LibCmo::CK2 { + class CKContext; + namespace ObjImpls { + class CKGroup; + class CK3dObject; + class CKMesh; + class CKMaterial; + class CKTexture; + class CKTargetLight; + } // namespace ObjImpls +} // namespace LibCmo::CK2 + +namespace BMapInspector::Map { + + enum class Error { + BadTempDir, ///< Fail to set temporary directory. + BadBallance, ///< Fail to set Ballance texture directory. + BadEncoding, ///< Fail to set encoding for loading map. + BadMap, ///< Can not load given map. + }; + + template + using Result = std::expected; + + class Level { + public: + Level(const Cli::Args& args); + ~Level(); + YYCC_DELETE_COPY(Level) + YYCC_DECL_MOVE(Level) + + public: + Result GetLoadStatus() const; + LibCmo::CK2::CKContext* GetCKContext() const; + + private: + LibCmo::CK2::CKContext* m_Context; + Result m_LoadStatus; ///< Nothing or error occurs when loading user given map. + + public: + const std::vector& GetGroups() const; + const std::vector& Get3dObjects() const; + const std::vector& GetMeshes() const; + const std::vector& GetMaterials() const; + const std::vector& GetTextures() const; + const std::vector& GetTargetLights() const; + + private: + std::vector m_ObjGroups; + std::vector m_Obj3dObjects; + std::vector m_ObjMeshes; + std::vector m_ObjMaterials; + std::vector m_ObjTextures; + std::vector m_ObjTargetLights; + }; + + Result load(const Cli::Args& args); + +} // namespace BMapInspector::Map diff --git a/Ballance/BMapInspector/Rule.cpp b/Ballance/BMapInspector/Rule.cpp new file mode 100644 index 0000000..1957125 --- /dev/null +++ b/Ballance/BMapInspector/Rule.cpp @@ -0,0 +1,31 @@ +#include "Rule.hpp" + +namespace BMapInspector::Rule { + +#pragma region IRule + + IRule::IRule() {} + + IRule::~IRule() {} + +#pragma endregion + +#pragma region Ruleset + + Ruleset::Ruleset() : rules() { + // TODO: create instance for each rules. + } + + Ruleset::~Ruleset() {} + + size_t Ruleset::GetRuleCount() const { + return this->rules.size(); + } + + const std::vector& Ruleset::GetRules() const { + return this->rules; + } + +#pragma endregion + +} // namespace BMapInspector::Ruleset diff --git a/Ballance/BMapInspector/Ruleset.hpp b/Ballance/BMapInspector/Rule.hpp similarity index 54% rename from Ballance/BMapInspector/Ruleset.hpp rename to Ballance/BMapInspector/Rule.hpp index 7680d37..dc9ffe0 100644 --- a/Ballance/BMapInspector/Ruleset.hpp +++ b/Ballance/BMapInspector/Rule.hpp @@ -1,6 +1,7 @@ #pragma once #include "Utils.hpp" #include "Reporter.hpp" +#include "Map.hpp" #include #include #include @@ -8,21 +9,7 @@ #include #include -namespace BMapInspector::Ruleset { - - class RuleContext { - public: - RuleContext(); - ~RuleContext(); - YYCC_DELETE_COPY(RuleContext) - YYCC_DECL_MOVE(RuleContext) - - public: - LibCmo::CK2::CKContext *GetCKContext(); - - private: - LibCmo::CK2::CKContext *m_Ctx; - }; +namespace BMapInspector::Rule { class IRule { public: @@ -32,14 +19,14 @@ namespace BMapInspector::Ruleset { public: virtual std::u8string_view GetRuleName() const = 0; - virtual void Check(Reporter::Reporter& reporter, RuleContext& ctx) const = 0; + virtual void Check(Reporter::Reporter& reporter, Map::Level& ctx) const = 0; }; - class RuleCollection { + class Ruleset { public: - RuleCollection(); - ~RuleCollection(); - YYCC_DELETE_COPY_MOVE(RuleCollection) + Ruleset(); + ~Ruleset(); + YYCC_DELETE_COPY_MOVE(Ruleset) public: size_t GetRuleCount() const; diff --git a/Ballance/BMapInspector/Ruleset.cpp b/Ballance/BMapInspector/Ruleset.cpp deleted file mode 100644 index 36e0d37..0000000 --- a/Ballance/BMapInspector/Ruleset.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "Ruleset.hpp" - -namespace BMapInspector::Ruleset { - -#pragma region Rule Context - - RuleContext::RuleContext() : m_Ctx(nullptr) {} - - RuleContext::~RuleContext() {} - - LibCmo::CK2::CKContext* RuleContext::GetCKContext() { - return this->m_Ctx; - } - - YYCC_IMPL_MOVE_CTOR(RuleContext, rhs) : m_Ctx(rhs.m_Ctx) { - rhs.m_Ctx = nullptr; - } - - YYCC_IMPL_MOVE_OPER(RuleContext, rhs) { - this->m_Ctx = rhs.m_Ctx; - rhs.m_Ctx = nullptr; - return *this; - } - -#pragma endregion - -#pragma region IRule - - IRule::IRule() {} - - IRule::~IRule() {} - -#pragma endregion - -#pragma region Rule Collection - - RuleCollection::RuleCollection() : rules() { - // TODO: create instance for each rules. - } - - RuleCollection::~RuleCollection() {} - - size_t RuleCollection::GetRuleCount() const { - return this->rules.size(); - } - - const std::vector& RuleCollection::GetRules() const { - return this->rules; - } - -#pragma endregion - -} // namespace BMapInspector::Ruleset