feat: finish one rule in BMapInspector.
- finish one rule in BMapInspector. - fix CKObjectManager find object by name feature.
This commit is contained in:
@@ -138,6 +138,11 @@ static void CheckRules(BMapInspector::Cli::Args& args, BMapInspector::Map::Level
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
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();
|
auto args = AcceptArgs();
|
||||||
if (args.has_value()) {
|
if (args.has_value()) {
|
||||||
PrintSplash();
|
PrintSplash();
|
||||||
@@ -148,5 +153,9 @@ int main(int argc, char* argv[]) {
|
|||||||
CheckRules(args.value(), level.value());
|
CheckRules(args.value(), level.value());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Shutdown CK2 engine.
|
||||||
|
LibCmo::CK2::CKShutdown();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ PRIVATE
|
|||||||
Map.cpp
|
Map.cpp
|
||||||
Rule.cpp
|
Rule.cpp
|
||||||
# Rules
|
# Rules
|
||||||
|
Rule/Shared.cpp
|
||||||
Rule/GpRules.cpp
|
Rule/GpRules.cpp
|
||||||
Rule/ChirsRules.cpp
|
Rule/ChirsRules.cpp
|
||||||
Rule/YYCRules.cpp
|
Rule/YYCRules.cpp
|
||||||
@@ -29,6 +30,7 @@ FILES
|
|||||||
Map.hpp
|
Map.hpp
|
||||||
Rule.hpp
|
Rule.hpp
|
||||||
# Rules
|
# Rules
|
||||||
|
Rule/Shared.hpp
|
||||||
Rule/GpRules.hpp
|
Rule/GpRules.hpp
|
||||||
Rule/ChirsRules.hpp
|
Rule/ChirsRules.hpp
|
||||||
Rule/YYCRules.hpp
|
Rule/YYCRules.hpp
|
||||||
|
|||||||
@@ -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;
|
rhs.m_Context = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
YYCC_IMPL_MOVE_OPER(Level, rhs) {
|
YYCC_IMPL_MOVE_OPER(Level, rhs) {
|
||||||
this->m_Context = rhs.m_Context;
|
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;
|
rhs.m_Context = nullptr;
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,10 +20,11 @@ namespace BMapInspector::Rule {
|
|||||||
|
|
||||||
Ruleset::Ruleset() : rules() {
|
Ruleset::Ruleset() : rules() {
|
||||||
// Add rule into list.
|
// Add rule into list.
|
||||||
rules.emplace_back(new Gp1Rule());
|
//rules.emplace_back(new Gp1Rule());
|
||||||
rules.emplace_back(new Gp2Rule());
|
//rules.emplace_back(new Gp2Rule());
|
||||||
rules.emplace_back(new Gp3Rule());
|
//rules.emplace_back(new Gp3Rule());
|
||||||
rules.emplace_back(new Chirs1Rule());
|
//rules.emplace_back(new Chirs1Rule());
|
||||||
|
rules.emplace_back(new YYCRule1());
|
||||||
// Add more rules...
|
// Add more rules...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ namespace BMapInspector::Rule {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
virtual std::u8string_view GetRuleName() const = 0;
|
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 {
|
class Ruleset {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace BMapInspector::Rule {
|
|||||||
std::u8string_view Chirs1Rule::GetRuleName() const {
|
std::u8string_view Chirs1Rule::GetRuleName() const {
|
||||||
return u8"CHIRS1";
|
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
|
// Report error if there is some material named Laterne_Verlauf
|
||||||
// but its texture is not pointed to Laterne_Verlauf texture.
|
// but its texture is not pointed to Laterne_Verlauf texture.
|
||||||
|
|
||||||
@@ -14,6 +14,5 @@ namespace BMapInspector::Rule {
|
|||||||
// but its name is not Laterne_Verlauf.
|
// but its name is not Laterne_Verlauf.
|
||||||
|
|
||||||
// Report error if there is multiple Laterne_Verlauf material.
|
// Report error if there is multiple Laterne_Verlauf material.
|
||||||
reporter.WriteError(this->GetRuleName(), u8"Fork you!");
|
|
||||||
}
|
}
|
||||||
} // namespace BMapInspector::Rule
|
} // namespace BMapInspector::Rule
|
||||||
|
|||||||
@@ -19,6 +19,6 @@ namespace BMapInspector::Rule {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
std::u8string_view GetRuleName() const override;
|
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;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace BMapInspector::Rule {
|
|||||||
return u8"GP1";
|
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
|
#pragma endregion
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ namespace BMapInspector::Rule {
|
|||||||
return u8"GP2";
|
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
|
#pragma endregion
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ namespace BMapInspector::Rule {
|
|||||||
return u8"GP3";
|
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.
|
// TODO: Mesh hash is not implemented.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ namespace BMapInspector::Rule {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
std::u8string_view GetRuleName() const override;
|
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:
|
public:
|
||||||
std::u8string_view GetRuleName() const override;
|
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:
|
public:
|
||||||
std::u8string_view GetRuleName() const override;
|
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
#include "Shared.hpp"
|
||||||
|
#include <yycc.hpp>
|
||||||
|
#include <yycc/carton/termcolor.hpp>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
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<O::CKGroup*>(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<O::CK3dEntity*> Iter3dEntities(O::CKGroup* group) {
|
||||||
|
std::vector<O::CK3dEntity*> 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<O::CK3dEntity*>(group_object);
|
||||||
|
rv.emplace_back(group_3dentity);
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<O::CKMaterial*> IterMaterial(O::CKMesh* mesh) {
|
||||||
|
std::vector<O::CKMaterial*> 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"<anonymous>", termcolor::Color::LightMagenta);
|
||||||
|
auto name = obj->GetName();
|
||||||
|
if (name == nullptr) {
|
||||||
|
return ANONYMOUS.c_str();
|
||||||
|
} else {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
} // namespace BMapInspector::Rule::Shared
|
||||||
|
|||||||
@@ -1,5 +1,62 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include <VTAll.hpp>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace BMapInspector::Rule::Shared {
|
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<O::CK3dEntity*> Iter3dEntities(O::CKGroup* group);
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
* @param[in] mesh Can not be nullptr.
|
||||||
|
* @return All nullptr reference are removed.
|
||||||
|
*/
|
||||||
|
std::vector<O::CKMaterial*> 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
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
#include "YYCRules.hpp"
|
||||||
|
#include "Shared.hpp"
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
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<O::CKMesh*> 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
|
||||||
|
|||||||
@@ -3,4 +3,21 @@
|
|||||||
|
|
||||||
namespace BMapInspector::Rule {
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -185,8 +185,8 @@ namespace LibCmo::CK2::MgrImpls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// iterate all sub object and check name
|
// iterate all sub object and check name
|
||||||
for (const auto& objoff : m_ObjectsListByClass[i]) {
|
for (const auto& objid : m_ObjectsListByClass[i]) {
|
||||||
ObjImpls::CKObject* obj = m_ObjectsList[objoff];
|
ObjImpls::CKObject* obj = m_ObjectsList[Id2Offset(objid)];
|
||||||
|
|
||||||
if (name == nullptr) {
|
if (name == nullptr) {
|
||||||
// directly add
|
// directly add
|
||||||
|
|||||||
Reference in New Issue
Block a user