feat: finish dup mesh/mtl/tex check rule in BMapInspector
- also fix "use after free" issue in QuoteObjectNames.
This commit is contained in:
@@ -28,6 +28,7 @@ namespace BMapInspector::Rule {
|
|||||||
rules.emplace_back(new ChirsRule1());
|
rules.emplace_back(new ChirsRule1());
|
||||||
rules.emplace_back(new YYCRule1());
|
rules.emplace_back(new YYCRule1());
|
||||||
rules.emplace_back(new YYCRule2());
|
rules.emplace_back(new YYCRule2());
|
||||||
|
rules.emplace_back(new YYCRule3());
|
||||||
rules.emplace_back(new BBugRule1());
|
rules.emplace_back(new BBugRule1());
|
||||||
rules.emplace_back(new ZZQRule1());
|
rules.emplace_back(new ZZQRule1());
|
||||||
rules.emplace_back(new ZZQRule2());
|
rules.emplace_back(new ZZQRule2());
|
||||||
|
|||||||
@@ -164,10 +164,14 @@ namespace BMapInspector::Rule::Shared {
|
|||||||
requires std::is_pointer_v<std::iter_value_t<InputIt>>
|
requires std::is_pointer_v<std::iter_value_t<InputIt>>
|
||||||
&& std::is_base_of_v<O::CKObject, std::remove_pointer_t<std::iter_value_t<InputIt>>>
|
&& std::is_base_of_v<O::CKObject, std::remove_pointer_t<std::iter_value_t<InputIt>>>
|
||||||
std::u8string QuoteObjectNames(InputIt first, InputIt last) {
|
std::u8string QuoteObjectNames(InputIt first, InputIt last) {
|
||||||
|
std::u8string cache;
|
||||||
return yycc::string::op::join(
|
return yycc::string::op::join(
|
||||||
[&first, &last]() -> std::optional<std::u8string_view> {
|
[&cache, &first, &last]() -> std::optional<std::u8string_view> {
|
||||||
if (first == last) return std::nullopt;
|
if (first == last) return std::nullopt;
|
||||||
return QuoteObjectName(*(first++));
|
// YYC MARK:
|
||||||
|
// We must use "cache", otherwise "use after free" will occur.
|
||||||
|
cache = QuoteObjectName(*(first++));
|
||||||
|
return cache;
|
||||||
},
|
},
|
||||||
u8", ");
|
u8", ");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
namespace L = LibCmo;
|
namespace L = LibCmo;
|
||||||
namespace C = LibCmo::CK2;
|
namespace C = LibCmo::CK2;
|
||||||
@@ -182,7 +183,7 @@ namespace BMapInspector::Rule {
|
|||||||
void update_array(const T* addr, size_t cnt) {
|
void update_array(const T* addr, size_t cnt) {
|
||||||
std::hash<T> hasher;
|
std::hash<T> hasher;
|
||||||
for (size_t i = 0; i < cnt; ++i) {
|
for (size_t i = 0; i < cnt; ++i) {
|
||||||
combine(hasher(addr[i]))
|
combine(hasher(addr[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -416,7 +417,10 @@ namespace BMapInspector::Rule {
|
|||||||
|
|
||||||
// Compare face data arrays
|
// Compare face data arrays
|
||||||
if (!std::equal(lhs->GetFaceIndices(), lhs->GetFaceIndices() + face_count * 3, rhs->GetFaceIndices())) return false;
|
if (!std::equal(lhs->GetFaceIndices(), lhs->GetFaceIndices() + face_count * 3, rhs->GetFaceIndices())) return false;
|
||||||
if (!std::equal(lhs->GetFaceMaterialSlotIndexs(), lhs->GetFaceMaterialSlotIndexs() + face_count, rhs->GetFaceMaterialSlotIndexs())) return false;
|
if (!std::equal(lhs->GetFaceMaterialSlotIndexs(),
|
||||||
|
lhs->GetFaceMaterialSlotIndexs() + face_count,
|
||||||
|
rhs->GetFaceMaterialSlotIndexs()))
|
||||||
|
return false;
|
||||||
|
|
||||||
// Compare material slot count
|
// Compare material slot count
|
||||||
auto material_slot_count = lhs->GetMaterialSlotCount();
|
auto material_slot_count = lhs->GetMaterialSlotCount();
|
||||||
@@ -495,26 +499,21 @@ namespace BMapInspector::Rule {
|
|||||||
#pragma region CKObject Wrapper Hash and Equal
|
#pragma region CKObject Wrapper Hash and Equal
|
||||||
|
|
||||||
struct CKTextureWrapperHash {
|
struct CKTextureWrapperHash {
|
||||||
[[nodiscard]] size_t operator()(const CKTextureWrapper& tex) const noexcept {
|
[[nodiscard]] size_t operator()(const CKTextureWrapper& tex) const noexcept { return tex.GetHash(); }
|
||||||
return tex.GetHash();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CKMaterialWrapperHash {
|
struct CKMaterialWrapperHash {
|
||||||
[[nodiscard]] size_t operator()(const CKMaterialWrapper& mtl) const noexcept {
|
[[nodiscard]] size_t operator()(const CKMaterialWrapper& mtl) const noexcept { return mtl.GetHash(); }
|
||||||
return mtl.GetHash();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CKMeshWrapperHash {
|
struct CKMeshWrapperHash {
|
||||||
[[nodiscard]] size_t operator()(const CKMeshWrapper& mesh) const noexcept {
|
[[nodiscard]] size_t operator()(const CKMeshWrapper& mesh) const noexcept { return mesh.GetHash(); }
|
||||||
return mesh.GetHash();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CKTextureWrapperEqualTo {
|
struct CKTextureWrapperEqualTo {
|
||||||
CKTextureEqualTo equal_to;
|
CKTextureEqualTo equal_to;
|
||||||
[[nodiscard]] bool operator()(const CKTextureWrapper& lhs, const CKTextureWrapper& rhs) const {
|
[[nodiscard]] bool operator()(const CKTextureWrapper& lhs, const CKTextureWrapper& rhs) const {
|
||||||
|
if (lhs.GetHash() != rhs.GetHash()) return false;
|
||||||
return equal_to(lhs.GetTexture(), rhs.GetTexture());
|
return equal_to(lhs.GetTexture(), rhs.GetTexture());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -522,6 +521,7 @@ namespace BMapInspector::Rule {
|
|||||||
struct CKMaterialWrapperEqualTo {
|
struct CKMaterialWrapperEqualTo {
|
||||||
CKMaterialEqualTo equal_to;
|
CKMaterialEqualTo equal_to;
|
||||||
[[nodiscard]] bool operator()(const CKMaterialWrapper& lhs, const CKMaterialWrapper& rhs) const {
|
[[nodiscard]] bool operator()(const CKMaterialWrapper& lhs, const CKMaterialWrapper& rhs) const {
|
||||||
|
if (lhs.GetHash() != rhs.GetHash()) return false;
|
||||||
return equal_to(lhs.GetMaterial(), rhs.GetMaterial());
|
return equal_to(lhs.GetMaterial(), rhs.GetMaterial());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -529,6 +529,7 @@ namespace BMapInspector::Rule {
|
|||||||
struct CKMeshWrapperEqualTo {
|
struct CKMeshWrapperEqualTo {
|
||||||
CKMeshEqualTo equal_to;
|
CKMeshEqualTo equal_to;
|
||||||
[[nodiscard]] bool operator()(const CKMeshWrapper& lhs, const CKMeshWrapper& rhs) const {
|
[[nodiscard]] bool operator()(const CKMeshWrapper& lhs, const CKMeshWrapper& rhs) const {
|
||||||
|
if (lhs.GetHash() != rhs.GetHash()) return false;
|
||||||
return equal_to(lhs.GetMesh(), rhs.GetMesh());
|
return equal_to(lhs.GetMesh(), rhs.GetMesh());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -545,7 +546,85 @@ namespace BMapInspector::Rule {
|
|||||||
return YYC3;
|
return YYC3;
|
||||||
}
|
}
|
||||||
|
|
||||||
void YYCRule3::Check(Reporter::Reporter& reporter, Map::Level& level) const {}
|
void YYCRule3::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||||
|
// Check textures
|
||||||
|
std::unordered_multiset<CKTextureWrapper, CKTextureWrapperHash, CKTextureWrapperEqualTo> textures;
|
||||||
|
for (auto* tex : level.GetTextures()) {
|
||||||
|
textures.emplace(CKTextureWrapper(tex));
|
||||||
|
}
|
||||||
|
// Show result
|
||||||
|
for (auto it = textures.begin(); it != textures.end();) {
|
||||||
|
size_t count = textures.count(*it);
|
||||||
|
|
||||||
|
// all count elements have equivalent keys
|
||||||
|
if (count > 1) {
|
||||||
|
std::vector<O::CKTexture*> dup_texs;
|
||||||
|
for (size_t i = 0; i < count; ++i) {
|
||||||
|
dup_texs.emplace_back(it->GetTexture());
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
|
reporter.FormatInfo(
|
||||||
|
YYC3,
|
||||||
|
u8"Some textures are exactly same. Please consider merge them into one to reduce the final size of map. These textures are: %s",
|
||||||
|
Shared::QuoteObjectNames(dup_texs.begin(), dup_texs.end()).c_str());
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check materials
|
||||||
|
std::unordered_multiset<CKMaterialWrapper, CKMaterialWrapperHash, CKMaterialWrapperEqualTo> materials;
|
||||||
|
for (auto* mat : level.GetMaterials()) {
|
||||||
|
materials.emplace(CKMaterialWrapper(mat));
|
||||||
|
}
|
||||||
|
// Show result
|
||||||
|
for (auto it = materials.begin(); it != materials.end();) {
|
||||||
|
size_t count = materials.count(*it);
|
||||||
|
|
||||||
|
// all count elements have equivalent keys
|
||||||
|
if (count > 1) {
|
||||||
|
std::vector<O::CKMaterial*> dup_mtls;
|
||||||
|
for (size_t i = 0; i < count; ++i) {
|
||||||
|
dup_mtls.emplace_back(it->GetMaterial());
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
|
reporter.FormatInfo(
|
||||||
|
YYC3,
|
||||||
|
u8"Some materials are exactly same. Please consider merge them into one to reduce the final size of map. These materials are: %s",
|
||||||
|
Shared::QuoteObjectNames(dup_mtls.begin(), dup_mtls.end()).c_str());
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check meshes
|
||||||
|
std::unordered_multiset<CKMeshWrapper, CKMeshWrapperHash, CKMeshWrapperEqualTo> meshes;
|
||||||
|
for (auto* mesh : level.GetMeshes()) {
|
||||||
|
meshes.emplace(CKMeshWrapper(mesh));
|
||||||
|
}
|
||||||
|
// Show result
|
||||||
|
for (auto it = meshes.begin(); it != meshes.end();) {
|
||||||
|
size_t count = meshes.count(*it);
|
||||||
|
|
||||||
|
// all count elements have equivalent keys
|
||||||
|
if (count > 1) {
|
||||||
|
std::vector<O::CKMesh*> dup_meshes;
|
||||||
|
for (size_t i = 0; i < count; ++i) {
|
||||||
|
dup_meshes.emplace_back(it->GetMesh());
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
|
reporter.FormatInfo(
|
||||||
|
YYC3,
|
||||||
|
u8"Some meshes are exactly same. Please consider merge them into one to reduce the final size of map. These meshes are: %s",
|
||||||
|
Shared::QuoteObjectNames(dup_meshes.begin(), dup_meshes.end()).c_str());
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
|
|||||||
@@ -129,8 +129,6 @@ namespace BMapInspector::Rule {
|
|||||||
auto left_sector_idx = static_cast<L::CKDWORD>(i + 1);
|
auto left_sector_idx = static_cast<L::CKDWORD>(i + 1);
|
||||||
auto right_sector_idx = static_cast<L::CKDWORD>(j + 1);
|
auto right_sector_idx = static_cast<L::CKDWORD>(j + 1);
|
||||||
|
|
||||||
// Join object together
|
|
||||||
|
|
||||||
// Output result.
|
// Output result.
|
||||||
reporter.FormatWarning(ZZQ2,
|
reporter.FormatWarning(ZZQ2,
|
||||||
u8"Some objects are grouped into sector %" PRIuCKDWORD " and sector %" PRIuCKDWORD
|
u8"Some objects are grouped into sector %" PRIuCKDWORD " and sector %" PRIuCKDWORD
|
||||||
|
|||||||
Reference in New Issue
Block a user