diff --git a/Ballance/BMapInspector/BMapInspector.cpp b/Ballance/BMapInspector/BMapInspector.cpp index 968500b..962e2fb 100644 --- a/Ballance/BMapInspector/BMapInspector.cpp +++ b/Ballance/BMapInspector/BMapInspector.cpp @@ -138,6 +138,11 @@ static void CheckRules(BMapInspector::Cli::Args& args, BMapInspector::Map::Level } int main(int argc, char* argv[]) { + + // Startup CK2 engine. + LibCmo::CK2::CKERROR err = LibCmo::CK2::CKStartUp(); + if (err != LibCmo::CK2::CKERROR::CKERR_OK) throw std::runtime_error("fail to initialize CK2 engine."); + auto args = AcceptArgs(); if (args.has_value()) { PrintSplash(); @@ -148,5 +153,9 @@ int main(int argc, char* argv[]) { CheckRules(args.value(), level.value()); } } + + // Shutdown CK2 engine. + LibCmo::CK2::CKShutdown(); + return 0; } diff --git a/Ballance/BMapInspector/CMakeLists.txt b/Ballance/BMapInspector/CMakeLists.txt index 9e723e6..f88eb6f 100644 --- a/Ballance/BMapInspector/CMakeLists.txt +++ b/Ballance/BMapInspector/CMakeLists.txt @@ -11,6 +11,7 @@ PRIVATE Map.cpp Rule.cpp # Rules + Rule/Shared.cpp Rule/GpRules.cpp Rule/ChirsRules.cpp Rule/YYCRules.cpp @@ -29,6 +30,7 @@ FILES Map.hpp Rule.hpp # Rules + Rule/Shared.hpp Rule/GpRules.hpp Rule/ChirsRules.hpp Rule/YYCRules.hpp diff --git a/Ballance/BMapInspector/Map.cpp b/Ballance/BMapInspector/Map.cpp index d1f2a2c..aa2f22f 100644 --- a/Ballance/BMapInspector/Map.cpp +++ b/Ballance/BMapInspector/Map.cpp @@ -92,13 +92,26 @@ namespace BMapInspector::Map { } } - YYCC_IMPL_MOVE_CTOR(Level, rhs) : m_Context(rhs.m_Context) { + YYCC_IMPL_MOVE_CTOR(Level, rhs) : + m_Context(rhs.m_Context), m_ObjGroups(std::move(rhs.m_ObjGroups)), m_Obj3dObjects(std::move(rhs.m_Obj3dObjects)), + m_ObjMeshes(std::move(rhs.m_ObjMeshes)), m_ObjMaterials(std::move(rhs.m_ObjMaterials)), m_ObjTextures(std::move(rhs.m_ObjTextures)), + m_ObjTargetLights(std::move(rhs.m_ObjTargetLights)) + + { rhs.m_Context = nullptr; } YYCC_IMPL_MOVE_OPER(Level, rhs) { this->m_Context = rhs.m_Context; + this->m_ObjGroups = std::move(rhs.m_ObjGroups); + this->m_Obj3dObjects = std::move(rhs.m_Obj3dObjects); + this->m_ObjMeshes = std::move(rhs.m_ObjMeshes); + this->m_ObjMaterials = std::move(rhs.m_ObjMaterials); + this->m_ObjTextures = std::move(rhs.m_ObjTextures); + this->m_ObjTargetLights = std::move(rhs.m_ObjTargetLights); + rhs.m_Context = nullptr; + return *this; } diff --git a/Ballance/BMapInspector/Rule.cpp b/Ballance/BMapInspector/Rule.cpp index b21b4b2..2eb7935 100644 --- a/Ballance/BMapInspector/Rule.cpp +++ b/Ballance/BMapInspector/Rule.cpp @@ -20,10 +20,11 @@ namespace BMapInspector::Rule { Ruleset::Ruleset() : rules() { // Add rule into list. - rules.emplace_back(new Gp1Rule()); - rules.emplace_back(new Gp2Rule()); - rules.emplace_back(new Gp3Rule()); - rules.emplace_back(new Chirs1Rule()); + //rules.emplace_back(new Gp1Rule()); + //rules.emplace_back(new Gp2Rule()); + //rules.emplace_back(new Gp3Rule()); + //rules.emplace_back(new Chirs1Rule()); + rules.emplace_back(new YYCRule1()); // Add more rules... } diff --git a/Ballance/BMapInspector/Rule.hpp b/Ballance/BMapInspector/Rule.hpp index dc9ffe0..d2db082 100644 --- a/Ballance/BMapInspector/Rule.hpp +++ b/Ballance/BMapInspector/Rule.hpp @@ -19,7 +19,7 @@ namespace BMapInspector::Rule { public: virtual std::u8string_view GetRuleName() const = 0; - virtual void Check(Reporter::Reporter& reporter, Map::Level& ctx) const = 0; + virtual void Check(Reporter::Reporter& reporter, Map::Level& level) const = 0; }; class Ruleset { diff --git a/Ballance/BMapInspector/Rule/ChirsRules.cpp b/Ballance/BMapInspector/Rule/ChirsRules.cpp index dc618c5..d3ca089 100644 --- a/Ballance/BMapInspector/Rule/ChirsRules.cpp +++ b/Ballance/BMapInspector/Rule/ChirsRules.cpp @@ -6,7 +6,7 @@ namespace BMapInspector::Rule { std::u8string_view Chirs1Rule::GetRuleName() const { return u8"CHIRS1"; } - void Chirs1Rule::Check(Reporter::Reporter& reporter, Map::Level& ctx) const { + void Chirs1Rule::Check(Reporter::Reporter& reporter, Map::Level& level) const { // Report error if there is some material named Laterne_Verlauf // but its texture is not pointed to Laterne_Verlauf texture. @@ -14,6 +14,5 @@ namespace BMapInspector::Rule { // but its name is not Laterne_Verlauf. // Report error if there is multiple Laterne_Verlauf material. - reporter.WriteError(this->GetRuleName(), u8"Fork you!"); } } // namespace BMapInspector::Rule diff --git a/Ballance/BMapInspector/Rule/ChirsRules.hpp b/Ballance/BMapInspector/Rule/ChirsRules.hpp index 91cfc96..009e536 100644 --- a/Ballance/BMapInspector/Rule/ChirsRules.hpp +++ b/Ballance/BMapInspector/Rule/ChirsRules.hpp @@ -19,6 +19,6 @@ namespace BMapInspector::Rule { public: std::u8string_view GetRuleName() const override; - void Check(Reporter::Reporter& reporter, Map::Level& ctx) const override; + void Check(Reporter::Reporter& reporter, Map::Level& level) const override; }; } diff --git a/Ballance/BMapInspector/Rule/GpRules.cpp b/Ballance/BMapInspector/Rule/GpRules.cpp index 9a60787..c104fd7 100644 --- a/Ballance/BMapInspector/Rule/GpRules.cpp +++ b/Ballance/BMapInspector/Rule/GpRules.cpp @@ -12,7 +12,7 @@ namespace BMapInspector::Rule { return u8"GP1"; } - void Gp1Rule::Check(Reporter::Reporter& reporter, Map::Level& ctx) const {} + void Gp1Rule::Check(Reporter::Reporter& reporter, Map::Level& level) const {} #pragma endregion @@ -26,7 +26,7 @@ namespace BMapInspector::Rule { return u8"GP2"; } - void Gp2Rule::Check(Reporter::Reporter& reporter, Map::Level& ctx) const {} + void Gp2Rule::Check(Reporter::Reporter& reporter, Map::Level& level) const {} #pragma endregion @@ -40,7 +40,7 @@ namespace BMapInspector::Rule { return u8"GP3"; } - void Gp3Rule::Check(Reporter::Reporter& reporter, Map::Level& ctx) const { + void Gp3Rule::Check(Reporter::Reporter& reporter, Map::Level& level) const { // TODO: Mesh hash is not implemented. } diff --git a/Ballance/BMapInspector/Rule/GpRules.hpp b/Ballance/BMapInspector/Rule/GpRules.hpp index ac8925f..aa47d21 100644 --- a/Ballance/BMapInspector/Rule/GpRules.hpp +++ b/Ballance/BMapInspector/Rule/GpRules.hpp @@ -18,7 +18,7 @@ namespace BMapInspector::Rule { public: std::u8string_view GetRuleName() const override; - void Check(Reporter::Reporter& reporter, Map::Level& ctx) const override; + void Check(Reporter::Reporter& reporter, Map::Level& level) const override; }; /** @@ -35,7 +35,7 @@ namespace BMapInspector::Rule { public: std::u8string_view GetRuleName() const override; - void Check(Reporter::Reporter& reporter, Map::Level& ctx) const override; + void Check(Reporter::Reporter& reporter, Map::Level& level) const override; }; /** @@ -52,7 +52,7 @@ namespace BMapInspector::Rule { public: std::u8string_view GetRuleName() const override; - void Check(Reporter::Reporter& reporter, Map::Level& ctx) const override; + void Check(Reporter::Reporter& reporter, Map::Level& level) const override; }; } diff --git a/Ballance/BMapInspector/Rule/Shared.cpp b/Ballance/BMapInspector/Rule/Shared.cpp index e69de29..b0d8cb6 100644 --- a/Ballance/BMapInspector/Rule/Shared.cpp +++ b/Ballance/BMapInspector/Rule/Shared.cpp @@ -0,0 +1,67 @@ +#include "Shared.hpp" +#include +#include +#include + +namespace termcolor = yycc::carton::termcolor; + +namespace BMapInspector::Rule::Shared { + +#pragma region Check Functions + + /** + * @brief + * @param[in] ctx Can not be nullptr. + * @param[in] name Can not be nullptr. + * @return Found pointer to CKGroup, otherwise nullptr. + */ + O::CKGroup* FetchGroup(C::CKContext* ctx, L::CKSTRING name) { + return static_cast(ctx->GetObjectByNameAndClass(name, C::CK_CLASSID::CKCID_GROUP, nullptr)); + } + + bool CheckTextureFileName(O::CKTexture* tex, L::CKSTRING name) { + // Get file name + auto filename = tex->GetUnderlyingData().GetSlotFileName(0); + if (filename == nullptr) return false; + // Extract file name part + std::filesystem::path filepath(filename); + auto filename_part = filepath.filename().u8string(); + // Return result. + return C::CKStrEqualI(filename_part.c_str(), name); + } + + std::vector Iter3dEntities(O::CKGroup* group) { + std::vector rv; + for (L::CKDWORD obj_idx = 0; obj_idx < group->GetObjectCount(); ++obj_idx) { + auto* group_object = group->GetObject(obj_idx); + if (!C::CKIsChildClassOf(group_object->GetClassID(), C::CK_CLASSID::CKCID_3DENTITY)) continue; + auto* group_3dentity = static_cast(group_object); + rv.emplace_back(group_3dentity); + } + return rv; + } + + std::vector IterMaterial(O::CKMesh* mesh) { + std::vector rv; + auto* mtls = mesh->GetMaterialSlots(); + for (L::CKDWORD mtl_idx = 0; mtl_idx < mesh->GetMaterialSlotCount(); ++mtl_idx) { + auto* mtl = mtls[mtl_idx]; + if (mtl == nullptr) continue; + rv.emplace_back(mtl); + } + return rv; + } + + const char8_t* RenderObjectName(O::CKObject* obj) { + static std::u8string ANONYMOUS = termcolor::colored(u8"", termcolor::Color::LightMagenta); + auto name = obj->GetName(); + if (name == nullptr) { + return ANONYMOUS.c_str(); + } else { + return name; + } + } + +#pragma endregion + +} // namespace BMapInspector::Rule::Shared diff --git a/Ballance/BMapInspector/Rule/Shared.hpp b/Ballance/BMapInspector/Rule/Shared.hpp index 1d5554f..a99bc76 100644 --- a/Ballance/BMapInspector/Rule/Shared.hpp +++ b/Ballance/BMapInspector/Rule/Shared.hpp @@ -1,5 +1,62 @@ #pragma once +#include +#include namespace BMapInspector::Rule::Shared { -} + namespace L = LibCmo; + namespace C = LibCmo::CK2; + namespace O = LibCmo::CK2::ObjImpls; + +#pragma region Constant Values + + namespace GroupNames { + constexpr char8_t PHYS_FLOORRAILS[] = u8"Phys_FloorRails"; + } + + namespace TextureNames { + constexpr char8_t RAIL_ENVIRONMENT[] = u8"Rail_Environment.bmp"; + } + +#pragma endregion + +#pragma region Check Functions + + /** + * @brief + * @param[in] ctx Can not be nullptr. + * @param[in] name Can not be nullptr. + * @return + */ + O::CKGroup* FetchGroup(C::CKContext* ctx, L::CKSTRING name); + /** + * @brief Check whether given CKTexture has the given file name (case-insensitive). + * @param[in] tex Can not be nullptr. + * @param[in] name Can not be nullptr. + * @return True if it is, otherwise false. + */ + bool CheckTextureFileName(O::CKTexture* tex, L::CKSTRING name); + + /** + * @brief + * @param[in] group Can not be nullptr. + * @return All objects is the child class of CK3dEntity. + */ + std::vector Iter3dEntities(O::CKGroup* group); + /** + * @brief + * @param[in] mesh Can not be nullptr. + * @return All nullptr reference are removed. + */ + std::vector IterMaterial(O::CKMesh* mesh); + + /** + * @brief + * @param[in] obj Can not be nullptr. + * @return + */ + const char8_t* RenderObjectName(O::CKObject* obj); + +#pragma endregion + +} // namespace BMapInspector::Rule::Shared diff --git a/Ballance/BMapInspector/Rule/YYCRules.cpp b/Ballance/BMapInspector/Rule/YYCRules.cpp index e69de29..6e4ef54 100644 --- a/Ballance/BMapInspector/Rule/YYCRules.cpp +++ b/Ballance/BMapInspector/Rule/YYCRules.cpp @@ -0,0 +1,84 @@ +#include "YYCRules.hpp" +#include "Shared.hpp" +#include + +namespace L = LibCmo; +namespace C = LibCmo::CK2; +namespace O = LibCmo::CK2::ObjImpls; + +namespace BMapInspector::Rule { + +#pragma region YYC Rule 1 + + constexpr char8_t YYC1[] = u8"YYC1"; + + YYCRule1::YYCRule1() {} + + YYCRule1::~YYCRule1() {} + + std::u8string_view YYCRule1::GetRuleName() const { + return YYC1; + } + + void YYCRule1::Check(Reporter::Reporter& reporter, Map::Level& level) const { + auto* ctx = level.GetCKContext(); + + // We get "Phys_FloorRails" group first. + auto* phys_floorrails = Shared::FetchGroup(ctx, Shared::GroupNames::PHYS_FLOORRAILS); + if (phys_floorrails == nullptr) return; + + // Create container holding smooth meshes + std::set smooth_meshes; + // We iterate all object grouped into it. + auto group_3dentities = Shared::Iter3dEntities(phys_floorrails); + for (auto* group_3dentity : group_3dentities) { + // Then we iterate their current meshes + auto* mesh = group_3dentity->GetCurrentMesh(); + if (mesh == nullptr) continue; + + // Iterate all meshes + auto mtls = Shared::IterMaterial(mesh); + for (auto* mtl : mtls) { + // Check whether all texture referred by this mesh are "Rail_Environment". + auto texture = mtl->GetTexture(); + if (texture == nullptr) continue; + if (!Shared::CheckTextureFileName(texture, Shared::TextureNames::RAIL_ENVIRONMENT)) { + // No, this is not rail texture, throw error. + reporter.FormatError( + YYC1, + u8R"(Object "%s" is grouped into Phys_FloorRails, but its texture "%s" (referred by mesh %s and material %s) seems not the rail texture. This will cause this object be smooth unexpectly.)", + Shared::RenderObjectName(group_3dentity), + Shared::RenderObjectName(texture), + Shared::RenderObjectName(mesh), + Shared::RenderObjectName(mtl)); + } + } + + // Record this mesh into set. + smooth_meshes.emplace(mesh); + } + + // Now we make sure that these smooth mesh is not referred by any other object. + // We iterate all 3d object first + auto all_3dobject = level.Get3dObjects(); + for (auto* obj : all_3dobject) { + // Then we get its current mesh + auto* mesh = obj->GetCurrentMesh(); + if (mesh == nullptr) continue; + + // Check whether its mesh is in smooth mesh, + // and itself is not in "Phys_FloorRails" group + if (!obj->IsInGroup(phys_floorrails) && smooth_meshes.contains(mesh)) { + // Report error. + reporter.FormatError( + YYC1, + u8R"(Object "%s" is not grouped into Phys_FloorRails, but some objects grouped into Phys_FloorRails refer its mesh "%s". This will cause this object be smooth unexpectly.)", + Shared::RenderObjectName(obj), + Shared::RenderObjectName(mesh)); + } + } + } + +#pragma endregion + +} // namespace BMapInspector::Rule diff --git a/Ballance/BMapInspector/Rule/YYCRules.hpp b/Ballance/BMapInspector/Rule/YYCRules.hpp index 1f4df78..5bb1fc7 100644 --- a/Ballance/BMapInspector/Rule/YYCRules.hpp +++ b/Ballance/BMapInspector/Rule/YYCRules.hpp @@ -2,5 +2,22 @@ #include "../Rule.hpp" namespace BMapInspector::Rule { - + + /** + * @brief YYC12345 Rule 1 + * @details + * The object grouped into "Phys_FloorRails" should only be rails, otherwise their meshes' UV will be smooth. + * Additionally, these smooth UV meshes will also affect those objects refering them. + */ + class YYCRule1 : public IRule { + public: + YYCRule1(); + ~YYCRule1(); + YYCC_DELETE_COPY_MOVE(YYCRule1) + + public: + std::u8string_view GetRuleName() const override; + void Check(Reporter::Reporter& reporter, Map::Level& level) const override; + }; + } diff --git a/LibCmo/LibCmo/CK2/MgrImpls/CKObjectManager.cpp b/LibCmo/LibCmo/CK2/MgrImpls/CKObjectManager.cpp index c042902..e9e1d03 100644 --- a/LibCmo/LibCmo/CK2/MgrImpls/CKObjectManager.cpp +++ b/LibCmo/LibCmo/CK2/MgrImpls/CKObjectManager.cpp @@ -185,8 +185,8 @@ namespace LibCmo::CK2::MgrImpls { } // iterate all sub object and check name - for (const auto& objoff : m_ObjectsListByClass[i]) { - ObjImpls::CKObject* obj = m_ObjectsList[objoff]; + for (const auto& objid : m_ObjectsListByClass[i]) { + ObjImpls::CKObject* obj = m_ObjectsList[Id2Offset(objid)]; if (name == nullptr) { // directly add