Compare commits
30 Commits
cf0966e6d3
...
v0.4.0
| Author | SHA1 | Date | |
|---|---|---|---|
| a5aee0a464 | |||
| dfaf911b57 | |||
| 5b02733d20 | |||
| 11336807d7 | |||
| c05dae9429 | |||
| 23b14ac69f | |||
| c9fdd30185 | |||
| db0bdc8618 | |||
| a87ff6d2db | |||
| 3f6d131d0d | |||
| 4c71a20935 | |||
| 49a729078c | |||
| f0160ce7c2 | |||
| b78732f30c | |||
| 86353305e8 | |||
| d1f4a37097 | |||
| 1a36a8b6d7 | |||
| eaa7814b18 | |||
| 49c9b00c11 | |||
| 4072285425 | |||
| 0ce752b7ba | |||
| 1de0196f26 | |||
| c2da274a11 | |||
| c9d369d2c4 | |||
| 54fed7e37b | |||
| e73f649187 | |||
| 2f9da2e852 | |||
| 8dbce47d8a | |||
| d31a98a859 | |||
| 9c4c4a7fa4 |
13
.github/scripts/windows.bat
vendored
13
.github/scripts/windows.bat
vendored
@@ -9,6 +9,19 @@ MKDIR install
|
||||
|
||||
:: Build with x64 architecture in Release mode
|
||||
CD build
|
||||
:: We set this to revert the incompatible ABI for MSVC STL.
|
||||
:: See: https://github.com/microsoft/STL/wiki/VS-2022-Changelog#vs-2022-1710
|
||||
::
|
||||
:: Ideally, I can install new VCRedist to resolve this issue.
|
||||
:: However, Blender embeds its own VCRedist when distribution which has lower VCRedist version.
|
||||
:: And at the same time, Blender will load our BMap.dll built by new VCRedist, so it trigger the incompatible ABI issue.
|
||||
::
|
||||
:: Currently, the VCRedist distributed by Blender 4.5 LTS is incompatible with our BMap.dll.
|
||||
:: So I write it in script, rather than in CMake file, to temporaryly fix this issue.
|
||||
::
|
||||
:: This fix also is written in dependency build scripts, please remove them together if you remove this,
|
||||
:: when Blender migrate to the new VCRedist.
|
||||
set CXXFLAGS=/D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR=1
|
||||
cmake -A x64 -DCMAKE_CXX_STANDARD=23 -DNEMO_BUILD_UNVIRT=ON -DNEMO_BUILD_BALLANCE=ON -DNEMO_BUILD_BMAP=ON -DNEMO_BUILD_BMAPINSPECTOR=ON -DYYCCommonplace_ROOT=%YYCCommonplace_ROOT% -DSTB_ROOT=%STB_ROOT% -DZLIB_ROOT=%ZLIB_ROOT% ../..
|
||||
cmake --build . --config Release
|
||||
cmake --install . --prefix=../install --config Release
|
||||
|
||||
1
.github/scripts/yycc/windows.bat
vendored
1
.github/scripts/yycc/windows.bat
vendored
@@ -9,6 +9,7 @@ MKDIR install
|
||||
|
||||
:: Build with x64 architecture in Release mode
|
||||
CD build
|
||||
set CXXFLAGS=/D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR=1
|
||||
cmake -A x64 -DCMAKE_CXX_STANDARD=23 ../..
|
||||
cmake --build . --config Release
|
||||
cmake --install . --prefix=../install --config Release
|
||||
|
||||
2
.github/workflows/linux.yml
vendored
2
.github/workflows/linux.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'yyc12345/YYCCommonplace'
|
||||
ref: 'master'
|
||||
ref: 'v2.0.0'
|
||||
path: 'extern/YYCCommonplace'
|
||||
- name: Build YYCCommonplace
|
||||
shell: bash
|
||||
|
||||
2
.github/workflows/macos.yml
vendored
2
.github/workflows/macos.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'yyc12345/YYCCommonplace'
|
||||
ref: 'master'
|
||||
ref: 'v2.0.0'
|
||||
path: 'extern/YYCCommonplace'
|
||||
- name: Build YYCCommonplace
|
||||
shell: bash
|
||||
|
||||
2
.github/workflows/windows.yml
vendored
2
.github/workflows/windows.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'yyc12345/YYCCommonplace'
|
||||
ref: 'master'
|
||||
ref: 'v2.0.0'
|
||||
path: 'extern/YYCCommonplace'
|
||||
- name: Build YYCCommonplace
|
||||
shell: cmd
|
||||
|
||||
@@ -269,6 +269,16 @@ namespace BMapSharp.BMapWrapper {
|
||||
public float GetSpecularPower() => GetGenericValue<float>(BMap.BMMaterial_GetSpecularPower);
|
||||
public void SetSpecularPower(float val) => SetGenericValue<float>(BMap.BMMaterial_SetSpecularPower, val);
|
||||
|
||||
public BMTexture GetTexture() {
|
||||
BMapException.ThrowIfFailed(BMap.BMMaterial_GetTexture(GetPointer(), GetCKID(), out uint out_texid));
|
||||
if (out_texid == Utils.INVALID_CKID) return null;
|
||||
else return new BMTexture(GetPointer(), out_texid);
|
||||
}
|
||||
public void SetTexture(BMTexture tex) {
|
||||
uint texid = (tex is null) ? Utils.INVALID_CKID : tex.GetCKID();
|
||||
BMapException.ThrowIfFailed(BMap.BMMaterial_SetTexture(GetPointer(), GetCKID(), texid));
|
||||
}
|
||||
|
||||
public VxColor GetTextureBorderColor() {
|
||||
BMapException.ThrowIfFailed(BMap.BMMaterial_GetTextureBorderColor(GetPointer(), GetCKID(), out uint out_val));
|
||||
return new VxColor(out_val);
|
||||
|
||||
@@ -68,6 +68,10 @@ namespace BMapSharpTest.TestSuits {
|
||||
|
||||
Console.WriteLine($"\tSpecular Power: {mtl.GetSpecularPower()}");
|
||||
|
||||
var current_texture = mtl.GetTexture();
|
||||
var texture_name = current_texture is null ? "<null>" : current_texture.GetName();
|
||||
Console.WriteLine($"\tTexture: {texture_name}");
|
||||
|
||||
Console.WriteLine($"\tTexture Border Color: {mtl.GetTextureBorderColor().ToManagedRGBA()}");
|
||||
|
||||
Console.WriteLine($"\tTexture Blend Mode: {mtl.GetTextureBlendMode()}");
|
||||
|
||||
398
Assets/BMapBindings/bmap-rs/Cargo.lock
generated
398
Assets/BMapBindings/bmap-rs/Cargo.lock
generated
@@ -2,13 +2,164 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.102"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
|
||||
|
||||
[[package]]
|
||||
name = "bmap-rs"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasip2",
|
||||
"wasip3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
dependencies = [
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "id-arena"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.16.1",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
||||
|
||||
[[package]]
|
||||
name = "leb128fmt"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.182"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
@@ -27,6 +178,73 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.149"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.115"
|
||||
@@ -38,6 +256,19 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.18"
|
||||
@@ -63,3 +294,170 @@ name = "unicode-ident"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "537dd038a89878be9b64dd4bd1b260315c1bb94f4d784956b81e27a088d9a09e"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "wasip2"
|
||||
version = "1.0.2+wasi-0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
|
||||
dependencies = [
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasip3"
|
||||
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
|
||||
dependencies = [
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-encoder"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
|
||||
dependencies = [
|
||||
"leb128fmt",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-metadata"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
"wasm-encoder",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"hashbrown 0.15.5",
|
||||
"indexmap",
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.61.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
|
||||
dependencies = [
|
||||
"wit-bindgen-rust-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-core"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"heck",
|
||||
"indexmap",
|
||||
"prettyplease",
|
||||
"syn",
|
||||
"wasm-metadata",
|
||||
"wit-bindgen-core",
|
||||
"wit-component",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rust-macro"
|
||||
version = "0.51.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wit-bindgen-core",
|
||||
"wit-bindgen-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-component"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags",
|
||||
"indexmap",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"wasm-encoder",
|
||||
"wasm-metadata",
|
||||
"wasmparser",
|
||||
"wit-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-parser"
|
||||
version = "0.244.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"id-arena",
|
||||
"indexmap",
|
||||
"log",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"unicode-xid",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||
|
||||
@@ -9,3 +9,6 @@ license = "SPDX:MIT"
|
||||
[dependencies]
|
||||
thiserror = "2.0.12"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.26.0"
|
||||
|
||||
|
||||
@@ -34,3 +34,10 @@ This project will find it in `build.rs` script and tell Rust compiler how to lin
|
||||
> You also may need rename some contents of other files involving this rename change.
|
||||
|
||||
Also due to this, when distributing your Rust project, please do not forget copy the built BMap library with your Rust artifacts.
|
||||
|
||||
## Test
|
||||
|
||||
The test code located in `tests` directory is NOT designed for plain "cargo test".
|
||||
It is more close to an independent test program, so executing it may require some extra work.
|
||||
First you need configure all environment variables for testing.
|
||||
Then you can execute `cargo test complete -- --nocapture` to run the complete test program.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -67,9 +67,10 @@ pub unsafe fn from_native_string_array(ptr: PCKSTRING) -> Result<Vec<String>> {
|
||||
Ok(rv)
|
||||
}
|
||||
|
||||
pub unsafe fn to_native_string_array<T>(words: &[T]) -> Result<BMStringArray>
|
||||
pub unsafe fn to_native_string_array<I, T>(words: I) -> Result<BMStringArray>
|
||||
where
|
||||
T: Into<Vec<u8>> + Copy,
|
||||
I: Iterator<Item = T>,
|
||||
T: Into<Vec<u8>>,
|
||||
{
|
||||
BMStringArray::new(words)
|
||||
}
|
||||
@@ -80,14 +81,14 @@ pub struct BMStringArray {
|
||||
}
|
||||
|
||||
impl BMStringArray {
|
||||
fn new<T>(words: &[T]) -> Result<Self>
|
||||
fn new<I, T>(words: I) -> Result<Self>
|
||||
where
|
||||
T: Into<Vec<u8>> + Copy,
|
||||
I: Iterator<Item = T>,
|
||||
T: Into<Vec<u8>>,
|
||||
{
|
||||
// Build array items
|
||||
let array_items = words
|
||||
.iter()
|
||||
.map(|word| CString::new(*word))
|
||||
.map(|word| CString::new(word))
|
||||
.collect::<std::result::Result<Vec<CString>, std::ffi::NulError>>()?;
|
||||
// Build array body.
|
||||
// In theory, move operation will not affect data allocated on heap.
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
//! BMap wrapper used utility stuff
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,288 @@
|
||||
use bmap_rs::bmap_wrapper as bmap;
|
||||
use std::io::Read;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[test]
|
||||
fn test_complete() {
|
||||
assert!(bmap::BMap::is_available());
|
||||
bmap::BMap::with_bmap(|b| {
|
||||
let r = b.create_file_reader("", "", "", &[""]);
|
||||
});
|
||||
// Parse arguments
|
||||
let cliopts = cli::Cli::new();
|
||||
|
||||
// Check BMap status.
|
||||
if !bmap::BMap::is_available() {
|
||||
panic!("Fail to initialize native BMap.");
|
||||
}
|
||||
|
||||
// Waiting debugger
|
||||
println!(
|
||||
"Rust PID is {}. Waiting for debugger, press any key to continue...",
|
||||
std::process::id()
|
||||
);
|
||||
let buffer = &mut [0u8];
|
||||
std::io::stdin().read_exact(buffer).unwrap();
|
||||
|
||||
// Start testbench
|
||||
let file_name = cliopts.file_name.clone();
|
||||
let temp_folder = tempfile::tempdir().unwrap();
|
||||
let mut texture_folder = cliopts.ballance_dir.clone();
|
||||
texture_folder.push("Textures");
|
||||
let encodings = cliopts.encodings.clone();
|
||||
bmap::BMap::with_bmap(|b| {
|
||||
let r = b
|
||||
.create_file_reader(
|
||||
file_name.to_str().unwrap(),
|
||||
temp_folder.path().to_str().unwrap(),
|
||||
texture_folder.to_str().unwrap(),
|
||||
encodings.iter().map(|e| e.as_str()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
testsuits::common::test(&r);
|
||||
testsuits::eq::test(&r);
|
||||
});
|
||||
drop(temp_folder);
|
||||
}
|
||||
|
||||
mod cli {
|
||||
use super::PathBuf;
|
||||
|
||||
pub struct Cli {
|
||||
/// The path to the map for loading.
|
||||
pub file_name: PathBuf,
|
||||
/// The path to the Ballance directory for finding textures
|
||||
pub ballance_dir: PathBuf,
|
||||
/// The name of encodings used by BMap for loading map.
|
||||
pub encodings: Vec<String>,
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
pub fn new() -> Self {
|
||||
let file_name = std::env::var("BMAP_FILE_NAME").expect(
|
||||
"You must specify BMAP_FILE_NAME environment variable before running this test.",
|
||||
);
|
||||
let ballance_dir = std::env::var("BMAP_BALLANCE_DIR").expect(
|
||||
"You must specify BMAP_BALLANCE_DIR environment variable before running this test.",
|
||||
);
|
||||
let encodings = std::env::var("BMAP_ENCODINGS").expect(
|
||||
"You must specify BMAP_ENCODINGS environment variable before running this test.",
|
||||
);
|
||||
|
||||
Self {
|
||||
file_name: PathBuf::from(file_name),
|
||||
ballance_dir: PathBuf::from(ballance_dir),
|
||||
encodings: encodings
|
||||
.split(",")
|
||||
.map(|e| e.to_string())
|
||||
.collect::<Vec<_>>(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod testsuits {
|
||||
use super::bmap;
|
||||
|
||||
pub mod common {
|
||||
use super::bmap;
|
||||
use bmap::{
|
||||
BM3dEntityDecl, BMCameraDecl, BMLightDecl, BMMaterialDecl, BMMeshDecl, BMObjectDecl,
|
||||
BMTextureDecl, BMGroupDecl
|
||||
};
|
||||
|
||||
pub fn test(reader: &bmap::BMFileReader) {
|
||||
println!("===== Groups =====");
|
||||
test_group(reader);
|
||||
println!("===== 3dObjects =====");
|
||||
test_3dobject(reader);
|
||||
println!("===== Meshes =====");
|
||||
test_mesh(reader);
|
||||
println!("===== Materials =====");
|
||||
test_material(reader);
|
||||
println!("===== Textures =====");
|
||||
test_texture(reader);
|
||||
println!("===== Target Lights =====");
|
||||
test_target_light(reader);
|
||||
println!("===== Target Cameras =====");
|
||||
test_target_camera(reader);
|
||||
println!("===== END =====");
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn test_group(reader: &bmap::BMFileReader) {
|
||||
for gp in reader.get_groups().unwrap() {
|
||||
let gp = gp.unwrap();
|
||||
|
||||
println!("{:?}", gp.get_name().unwrap());
|
||||
for gp_item in gp.get_objects().unwrap() {
|
||||
let gp_item = gp_item.unwrap();
|
||||
println!("\t{:?}", gp_item.get_name().unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn test_3dobject(reader: &bmap::BMFileReader) {
|
||||
for obj in reader.get_3dobjects().unwrap() {
|
||||
let obj = obj.unwrap();
|
||||
|
||||
println!("{:?}", obj.get_name().unwrap());
|
||||
|
||||
let current_mesh = obj.get_current_mesh().unwrap();
|
||||
let mesh_name = match current_mesh {
|
||||
Some(mesh) => format!("{:?}", mesh.get_name()),
|
||||
None => "<null>".to_string(),
|
||||
};
|
||||
println!("\tMesh: {}", mesh_name);
|
||||
println!("\tVisibility: {}", obj.get_visibility().unwrap());
|
||||
println!("\tMatrix: {:?}", obj.get_world_matrix().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn test_mesh(reader: &bmap::BMFileReader) {
|
||||
for mesh in reader.get_meshes().unwrap() {
|
||||
let mesh = mesh.unwrap();
|
||||
|
||||
println!("{:?}", mesh.get_name().unwrap());
|
||||
|
||||
println!("\tLit Mode: {:?}", mesh.get_lit_mode().unwrap());
|
||||
println!("\tVertex Count: {}", mesh.get_vertex_count().unwrap());
|
||||
println!("\tFace Count: {}", mesh.get_face_count().unwrap());
|
||||
println!("\tMaterial Slot Count: {}", mesh.get_material_slot_count().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn test_material(reader: &bmap::BMFileReader) {
|
||||
for mtl in reader.get_materials().unwrap() {
|
||||
let mtl = mtl.unwrap();
|
||||
|
||||
println!("{:?}", mtl.get_name().unwrap());
|
||||
|
||||
println!("\tDiffuse: {:?}", mtl.get_diffuse().unwrap());
|
||||
println!("\tAmbient: {:?}", mtl.get_ambient().unwrap());
|
||||
println!("\tSpecular: {:?}", mtl.get_specular().unwrap());
|
||||
println!("\tEmissive: {:?}", mtl.get_emissive().unwrap());
|
||||
|
||||
println!("\tSpecular Power: {}", mtl.get_specular_power().unwrap());
|
||||
|
||||
println!("\tTexture Border Color: {:?}", mtl.get_texture_border_color().unwrap());
|
||||
|
||||
println!("\tTexture Blend Mode: {:?}", mtl.get_texture_blend_mode().unwrap());
|
||||
println!("\tTexture Min Mode: {:?}", mtl.get_texture_min_mode().unwrap());
|
||||
println!("\tTexture Mag Mode: {:?}", mtl.get_texture_mag_mode().unwrap());
|
||||
println!("\tSource Blend: {:?}", mtl.get_source_blend().unwrap());
|
||||
println!("\tDest Blend: {:?}", mtl.get_dest_blend().unwrap());
|
||||
println!("\tFill Mode: {:?}", mtl.get_fill_mode().unwrap());
|
||||
println!("\tShade Mode: {:?}", mtl.get_shade_mode().unwrap());
|
||||
|
||||
println!("\tAlpha Test Enabled: {}", mtl.get_alpha_test_enabled().unwrap());
|
||||
println!("\tAlpha Blend Enabled: {}", mtl.get_alpha_blend_enabled().unwrap());
|
||||
println!("\tPerspective Correction Enabled: {}", mtl.get_perspective_correction_enabled().unwrap());
|
||||
println!("\tZ Write Enabled: {}", mtl.get_z_write_enabled().unwrap());
|
||||
println!("\tTwo Sided Enabled: {}", mtl.get_two_sided_enabled().unwrap());
|
||||
|
||||
println!("\tAlpha Ref: {}", mtl.get_alpha_ref().unwrap());
|
||||
|
||||
println!("\tAlpha Func: {:?}", mtl.get_alpha_func().unwrap());
|
||||
println!("\tZ Func: {:?}", mtl.get_z_func().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn test_texture(reader: &bmap::BMFileReader) {
|
||||
for tex in reader.get_textures().unwrap() {
|
||||
let tex = tex.unwrap();
|
||||
|
||||
println!("{:?}", tex.get_name().unwrap());
|
||||
|
||||
println!("\tFile Name: {:?}", tex.get_file_name().unwrap());
|
||||
println!("\tSave Options: {:?}", tex.get_save_options().unwrap());
|
||||
println!("\tVideo Format: {:?}", tex.get_video_format().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn test_target_light(reader: &bmap::BMFileReader) {
|
||||
for lit in reader.get_target_lights().unwrap() {
|
||||
let lit = lit.unwrap();
|
||||
|
||||
println!("{:?}", lit.get_name().unwrap());
|
||||
|
||||
println!("\tVisibility: {:?}", lit.get_visibility().unwrap());
|
||||
println!("\tMatrix: {:?}", lit.get_world_matrix().unwrap());
|
||||
|
||||
println!("\tType: {:?}", lit.get_type().unwrap());
|
||||
println!("\tColor: {:?}", lit.get_color().unwrap());
|
||||
println!("\tConstant Attenuation: {}", lit.get_constant_attenuation().unwrap());
|
||||
println!("\tLinear Attenuation: {}", lit.get_linear_attenuation().unwrap());
|
||||
println!("\tQuadratic Attenuation: {}", lit.get_quadratic_attenuation().unwrap());
|
||||
println!("\tRange: {}", lit.get_range().unwrap());
|
||||
println!("\tHot Spot: {}", lit.get_hot_spot().unwrap());
|
||||
println!("\tFalloff: {}", lit.get_falloff().unwrap());
|
||||
println!("\tFalloff Shape: {}", lit.get_falloff_shape().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn test_target_camera(reader: &bmap::BMFileReader) {
|
||||
for cam in reader.get_target_cameras().unwrap() {
|
||||
let cam = cam.unwrap();
|
||||
|
||||
println!("{:?}", cam.get_name().unwrap());
|
||||
|
||||
println!("\tVisibility: {:?}", cam.get_visibility().unwrap());
|
||||
println!("\tMatrix: {:?}", cam.get_world_matrix().unwrap());
|
||||
|
||||
println!("\tType: {:?}", cam.get_projection_type().unwrap());
|
||||
println!("\tOrthographic Zoom: {}", cam.get_orthographic_zoom().unwrap());
|
||||
println!("\tFront Plane: {}", cam.get_front_plane().unwrap());
|
||||
println!("\tBack Plane: {}", cam.get_back_plane().unwrap());
|
||||
println!("\tFov: {}", cam.get_fov().unwrap());
|
||||
|
||||
let (width, height) = cam.get_aspect_ratio().unwrap();
|
||||
println!("\tAspect Ratio: {}:{}", width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod eq {
|
||||
use super::bmap;
|
||||
use std::collections::{BTreeSet, HashSet};
|
||||
|
||||
pub fn test(reader: &bmap::BMFileReader) {
|
||||
// Check requirements
|
||||
assert!(
|
||||
reader.get_3dobject_count().unwrap() >= 2,
|
||||
r#"Invalid file for test Eq.
|
||||
We can not perform Eq test because the length of 3dObject is too short (must greater than 2). Please choose another file to perform."#
|
||||
);
|
||||
|
||||
// Prepare variables
|
||||
let all_3dobjects = reader
|
||||
.get_3dobjects()
|
||||
.unwrap()
|
||||
.map(|o| o.unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
let first_3dobj = &all_3dobjects[0];
|
||||
let second_3dobj = &all_3dobjects[1];
|
||||
let all_3dobjects_again = reader
|
||||
.get_3dobjects()
|
||||
.unwrap()
|
||||
.map(|o| o.unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
let first_3dobj_again = &all_3dobjects_again[0];
|
||||
|
||||
// Test HashSet
|
||||
let mut test_hashset = HashSet::new();
|
||||
assert!(test_hashset.insert(first_3dobj));
|
||||
assert!(!test_hashset.insert(first_3dobj_again));
|
||||
assert!(test_hashset.insert(second_3dobj));
|
||||
|
||||
// Test BTreeSet
|
||||
let mut test_btreeset = BTreeSet::new();
|
||||
assert!(test_btreeset.insert(first_3dobj));
|
||||
assert!(!test_btreeset.insert(first_3dobj_again));
|
||||
assert!(test_btreeset.insert(second_3dobj));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
#include "Rule.hpp"
|
||||
#include <VTAll.hpp>
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/carton/termcolor.hpp>
|
||||
#include <yycc/string/op.hpp>
|
||||
#include <yycc/patch/stream.hpp>
|
||||
#include <yycc/windows/console.hpp>
|
||||
#include <yycc/carton/termcolor.hpp>
|
||||
#include <yycc/carton/ironpad.hpp>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
|
||||
@@ -101,14 +102,16 @@ static void CheckRules(BMapInspector::Cli::Args& args, BMapInspector::Map::Level
|
||||
BMapInspector::Reporter::Reporter reporter;
|
||||
|
||||
// Get rule collection
|
||||
BMapInspector::Rule::Ruleset ruleset;
|
||||
BMapInspector::Rule::RuleCollection rule_collection;
|
||||
// Show rule infos
|
||||
std::cout << strop::printf(u8"Total %" PRIuSIZET " rule(s) are loaded.", ruleset.GetRuleCount()) << std::endl
|
||||
std::cout << strop::printf(u8"Total %" PRIuSIZET " rule(s) are loaded.", rule_collection.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 : ruleset.GetRules()) {
|
||||
for (auto* rule : rule_collection.GetRules()) {
|
||||
reporter.EnterRule(rule->GetRuleName());
|
||||
rule->Check(reporter, level);
|
||||
reporter.LeaveRule();
|
||||
}
|
||||
|
||||
// Show report conclusion
|
||||
|
||||
@@ -11,15 +11,18 @@ PRIVATE
|
||||
Map.cpp
|
||||
Rule.cpp
|
||||
# Rules
|
||||
Rule/Shared.cpp
|
||||
Rule/GpRules.cpp
|
||||
Rule/ChirsRules.cpp
|
||||
Rule/YYCRules.cpp
|
||||
Rule/ZZQRules.cpp
|
||||
Rule/BBugRules.cpp
|
||||
Rule/SOneRules.cpp
|
||||
Rule/SSBRules.cpp
|
||||
Rule/LXRules.cpp
|
||||
Ruleset/Shared/Utility.cpp
|
||||
Ruleset/Shared/Name.cpp
|
||||
Ruleset/Shared/Sector.cpp
|
||||
Ruleset/Shared/DupCmp.cpp
|
||||
Ruleset/GpRules.cpp
|
||||
Ruleset/ChirsRules.cpp
|
||||
Ruleset/YYCRules.cpp
|
||||
Ruleset/ZZQRules.cpp
|
||||
Ruleset/BBugRules.cpp
|
||||
Ruleset/SOneRules.cpp
|
||||
Ruleset/SSBRules.cpp
|
||||
Ruleset/LXRules.cpp
|
||||
)
|
||||
# Setup headers
|
||||
target_sources(BMapInspector
|
||||
@@ -33,15 +36,18 @@ FILES
|
||||
Map.hpp
|
||||
Rule.hpp
|
||||
# Rules
|
||||
Rule/Shared.hpp
|
||||
Rule/GpRules.hpp
|
||||
Rule/ChirsRules.hpp
|
||||
Rule/YYCRules.hpp
|
||||
Rule/ZZQRules.hpp
|
||||
Rule/BBugRules.hpp
|
||||
Rule/SOneRules.hpp
|
||||
Rule/SSBRules.hpp
|
||||
Rule/LXRules.hpp
|
||||
Ruleset/Shared/Utility.hpp
|
||||
Ruleset/Shared/Name.hpp
|
||||
Ruleset/Shared/Sector.hpp
|
||||
Ruleset/Shared/DupCmp.hpp
|
||||
Ruleset/GpRules.hpp
|
||||
Ruleset/ChirsRules.hpp
|
||||
Ruleset/YYCRules.hpp
|
||||
Ruleset/ZZQRules.hpp
|
||||
Ruleset/BBugRules.hpp
|
||||
Ruleset/SOneRules.hpp
|
||||
Ruleset/SSBRules.hpp
|
||||
Ruleset/LXRules.hpp
|
||||
)
|
||||
# Setup header infomation
|
||||
target_include_directories(BMapInspector
|
||||
|
||||
@@ -1,54 +1,70 @@
|
||||
#include "Reporter.hpp"
|
||||
#include <yycc/string/op.hpp>
|
||||
#include <cstdarg>
|
||||
#include <stdexcept>
|
||||
|
||||
using BMapInspector::Utils::ReportLevel;
|
||||
namespace strop = yycc::string::op;
|
||||
|
||||
namespace BMapInspector::Reporter {
|
||||
|
||||
#pragma region Reporter
|
||||
|
||||
Reporter::Reporter() {}
|
||||
Reporter::Reporter() : current_rule(std::nullopt), reports() {}
|
||||
|
||||
Reporter::~Reporter() {}
|
||||
|
||||
void Reporter::AddReport(Utils::ReportLevel level, const std::u8string_view &rule, const std::u8string_view &content) {
|
||||
void Reporter::EnterRule(const std::u8string_view &rule) {
|
||||
if (this->current_rule.has_value()) throw std::logic_error("can not enter rule multiple times");
|
||||
else this->current_rule = std::u8string(rule);
|
||||
}
|
||||
|
||||
void Reporter::LeaveRule() {
|
||||
if (this->current_rule.has_value()) this->current_rule = std::nullopt;
|
||||
else throw std::logic_error("can not leave rule without any existing rule");
|
||||
}
|
||||
|
||||
void Reporter::AddReport(ReportLevel level, const std::u8string_view &content) {
|
||||
if (this->current_rule.has_value()) {
|
||||
this->reports.emplace_back(Report{
|
||||
.level = level,
|
||||
.rule = std::u8string(rule),
|
||||
.rule = std::u8string(this->current_rule.value()),
|
||||
.content = std::u8string(content),
|
||||
});
|
||||
} else {
|
||||
throw std::logic_error("can not add report without any rule scope");
|
||||
}
|
||||
}
|
||||
|
||||
void Reporter::WriteInfo(const std::u8string_view &rule, const std::u8string_view &content) {
|
||||
this->AddReport(Utils::ReportLevel::Info, rule, content);
|
||||
void Reporter::WriteInfo(const std::u8string_view &content) {
|
||||
this->AddReport(ReportLevel::Info, content);
|
||||
}
|
||||
|
||||
void Reporter::FormatInfo(const std::u8string_view &rule, const char8_t *fmt, ...) {
|
||||
void Reporter::FormatInfo(const char8_t *fmt, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, fmt);
|
||||
this->WriteInfo(rule, strop::vprintf(fmt, argptr));
|
||||
this->WriteInfo(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::WriteWarning(const std::u8string_view &content) {
|
||||
this->AddReport(ReportLevel::Warning, content);
|
||||
}
|
||||
void Reporter::FormatWarning(const std::u8string_view &rule, const char8_t *fmt, ...) {
|
||||
void Reporter::FormatWarning(const char8_t *fmt, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, fmt);
|
||||
this->WriteWarning(rule, strop::vprintf(fmt, argptr));
|
||||
this->WriteWarning(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::WriteError(const std::u8string_view &content) {
|
||||
this->AddReport(ReportLevel::Error, content);
|
||||
}
|
||||
|
||||
void Reporter::FormatError(const std::u8string_view &rule, const char8_t *fmt, ...) {
|
||||
void Reporter::FormatError(const char8_t *fmt, ...) {
|
||||
va_list argptr;
|
||||
va_start(argptr, fmt);
|
||||
this->WriteError(rule, strop::vprintf(fmt, argptr));
|
||||
this->WriteError(strop::vprintf(fmt, argptr));
|
||||
va_end(argptr);
|
||||
}
|
||||
|
||||
@@ -56,13 +72,13 @@ namespace BMapInspector::Reporter {
|
||||
ReporterDigest digest{.cnt_err = 0, .cnt_warn = 0, .cnt_info = 0};
|
||||
for (const auto &report : this->reports) {
|
||||
switch (report.level) {
|
||||
case Utils::ReportLevel::Error:
|
||||
case ReportLevel::Error:
|
||||
++digest.cnt_err;
|
||||
break;
|
||||
case Utils::ReportLevel::Warning:
|
||||
case ReportLevel::Warning:
|
||||
++digest.cnt_warn;
|
||||
break;
|
||||
case Utils::ReportLevel::Info:
|
||||
case ReportLevel::Info:
|
||||
++digest.cnt_info;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
namespace BMapInspector::Reporter {
|
||||
|
||||
@@ -26,22 +27,27 @@ namespace BMapInspector::Reporter {
|
||||
~Reporter();
|
||||
YYCC_DEFAULT_COPY_MOVE(Reporter)
|
||||
|
||||
public:
|
||||
void EnterRule(const std::u8string_view& rule);
|
||||
void LeaveRule();
|
||||
|
||||
private:
|
||||
void AddReport(Utils::ReportLevel level, const std::u8string_view& rule, const std::u8string_view& content);
|
||||
void AddReport(Utils::ReportLevel level, 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, ...);
|
||||
void WriteInfo(const std::u8string_view& content);
|
||||
void FormatInfo(const char8_t* fmt, ...);
|
||||
void WriteWarning(const std::u8string_view& content);
|
||||
void FormatWarning(const char8_t* fmt, ...);
|
||||
void WriteError(const std::u8string_view& content);
|
||||
void FormatError(const char8_t* fmt, ...);
|
||||
|
||||
public:
|
||||
ReporterDigest GetDigest() const;
|
||||
const std::vector<Report>& GetReports() const;
|
||||
|
||||
private:
|
||||
std::optional<std::u8string> current_rule;
|
||||
std::vector<Report> reports;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#include "Rule.hpp"
|
||||
|
||||
#include "Rule/GpRules.hpp"
|
||||
#include "Rule/ChirsRules.hpp"
|
||||
#include "Rule/YYCRules.hpp"
|
||||
#include "Rule/BBugRules.hpp"
|
||||
#include "Rule/ZZQRules.hpp"
|
||||
#include "Rule/SOneRules.hpp"
|
||||
#include "Rule/SSBRules.hpp"
|
||||
#include "Rule/LXRules.hpp"
|
||||
#include "Ruleset/GpRules.hpp"
|
||||
#include "Ruleset/ChirsRules.hpp"
|
||||
#include "Ruleset/YYCRules.hpp"
|
||||
#include "Ruleset/BBugRules.hpp"
|
||||
#include "Ruleset/ZZQRules.hpp"
|
||||
#include "Ruleset/SOneRules.hpp"
|
||||
#include "Ruleset/SSBRules.hpp"
|
||||
#include "Ruleset/LXRules.hpp"
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
|
||||
@@ -19,38 +19,46 @@ namespace BMapInspector::Rule {
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Ruleset
|
||||
#pragma region RuleCollection
|
||||
|
||||
Ruleset::Ruleset() : rules() {
|
||||
RuleCollection::RuleCollection() : rules() {
|
||||
// Add rule into list.
|
||||
//rules.emplace_back(new GpRule1());
|
||||
//rules.emplace_back(new GpRule2());
|
||||
//rules.emplace_back(new Gp3Rule());
|
||||
//rules.emplace_back(new Chirs1Rule());
|
||||
rules.emplace_back(new YYCRule1());
|
||||
rules.emplace_back(new YYCRule2());
|
||||
rules.emplace_back(new BBugRule1());
|
||||
rules.emplace_back(new SOneRule1());
|
||||
rules.emplace_back(new SSBRule1());
|
||||
rules.emplace_back(new LXRule1());
|
||||
rules.emplace_back(new Ruleset::GpRule1());
|
||||
rules.emplace_back(new Ruleset::GpRule2());
|
||||
rules.emplace_back(new Ruleset::ChirsRule1());
|
||||
rules.emplace_back(new Ruleset::YYCRule1());
|
||||
rules.emplace_back(new Ruleset::YYCRule2());
|
||||
rules.emplace_back(new Ruleset::YYCRule3());
|
||||
rules.emplace_back(new Ruleset::YYCRule4());
|
||||
rules.emplace_back(new Ruleset::YYCRule5());
|
||||
rules.emplace_back(new Ruleset::YYCRule6());
|
||||
rules.emplace_back(new Ruleset::BBugRule1());
|
||||
rules.emplace_back(new Ruleset::BBugRule2());
|
||||
rules.emplace_back(new Ruleset::BBugRule3());
|
||||
rules.emplace_back(new Ruleset::ZZQRule1());
|
||||
rules.emplace_back(new Ruleset::ZZQRule2());
|
||||
rules.emplace_back(new Ruleset::ZZQRule3());
|
||||
rules.emplace_back(new Ruleset::SOneRule1());
|
||||
rules.emplace_back(new Ruleset::SSBRule1());
|
||||
rules.emplace_back(new Ruleset::LXRule1());
|
||||
// Add more rules...
|
||||
}
|
||||
|
||||
Ruleset::~Ruleset() {
|
||||
RuleCollection::~RuleCollection() {
|
||||
// Free rule from list.
|
||||
for (const auto* rule : this->rules) {
|
||||
delete rule;
|
||||
}
|
||||
}
|
||||
|
||||
size_t Ruleset::GetRuleCount() const {
|
||||
size_t RuleCollection::GetRuleCount() const {
|
||||
return this->rules.size();
|
||||
}
|
||||
|
||||
const std::vector<IRule*>& Ruleset::GetRules() const {
|
||||
const std::vector<IRule*>& RuleCollection::GetRules() const {
|
||||
return this->rules;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Ruleset
|
||||
} // namespace BMapInspector::Rule
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
|
||||
/**
|
||||
* @brief The interface of a rule.
|
||||
*/
|
||||
class IRule {
|
||||
public:
|
||||
IRule();
|
||||
@@ -21,11 +24,14 @@ namespace BMapInspector::Rule {
|
||||
virtual void Check(Reporter::Reporter& reporter, Map::Level& level) const = 0;
|
||||
};
|
||||
|
||||
class Ruleset {
|
||||
/**
|
||||
* @brief A collection of rules.
|
||||
*/
|
||||
class RuleCollection {
|
||||
public:
|
||||
Ruleset();
|
||||
~Ruleset();
|
||||
YYCC_DELETE_COPY_MOVE(Ruleset)
|
||||
RuleCollection();
|
||||
~RuleCollection();
|
||||
YYCC_DELETE_COPY_MOVE(RuleCollection)
|
||||
|
||||
public:
|
||||
size_t GetRuleCount() const;
|
||||
@@ -35,4 +41,4 @@ namespace BMapInspector::Rule {
|
||||
std::vector<IRule*> rules;
|
||||
};
|
||||
|
||||
} // namespace BMapInspector::Ruleset
|
||||
} // namespace BMapInspector::Rule
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
#include "BBugRules.hpp"
|
||||
#include "Shared.hpp"
|
||||
|
||||
namespace L = LibCmo;
|
||||
namespace C = LibCmo::CK2;
|
||||
namespace O = LibCmo::CK2::ObjImpls;
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
|
||||
#pragma region BBug Rule 1
|
||||
|
||||
constexpr char8_t BBUG1[] = u8"BBUG1";
|
||||
|
||||
BBugRule1::BBugRule1() : IRule() {}
|
||||
|
||||
BBugRule1::~BBugRule1() {}
|
||||
|
||||
std::u8string_view BBugRule1::GetRuleName() const {
|
||||
return BBUG1;
|
||||
}
|
||||
|
||||
void BBugRule1::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
if (!level.GetTargetLights().empty()) {
|
||||
reporter.WriteInfo(BBUG1, u8"Using light in map is not suggested.");
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Rule
|
||||
@@ -1,22 +0,0 @@
|
||||
#pragma once
|
||||
#include "../Rule.hpp"
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
|
||||
/**
|
||||
* @brief BBug Rule 1
|
||||
* @details
|
||||
* Using light in map is not suggested.
|
||||
*/
|
||||
class BBugRule1 : public IRule {
|
||||
public:
|
||||
BBugRule1();
|
||||
virtual ~BBugRule1();
|
||||
YYCC_DELETE_COPY_MOVE(BBugRule1)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
#include "ChirsRules.hpp"
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
Chirs1Rule::Chirs1Rule() : IRule() {}
|
||||
Chirs1Rule::~Chirs1Rule() {}
|
||||
std::u8string_view Chirs1Rule::GetRuleName() const {
|
||||
return u8"CHIRS1";
|
||||
}
|
||||
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.
|
||||
|
||||
// Report error if some materials' texture is Laterne_Verlauf,
|
||||
// but its name is not Laterne_Verlauf.
|
||||
|
||||
// Report error if there is multiple Laterne_Verlauf material.
|
||||
}
|
||||
} // namespace BMapInspector::Rule
|
||||
@@ -1,53 +0,0 @@
|
||||
#include "GpRules.hpp"
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
|
||||
#pragma region GP1 Rule
|
||||
|
||||
GpRule1::GpRule1() : IRule() {}
|
||||
|
||||
GpRule1::~GpRule1() {}
|
||||
|
||||
std::u8string_view GpRule1::GetRuleName() const {
|
||||
return u8"GP1";
|
||||
}
|
||||
|
||||
void GpRule1::Check(Reporter::Reporter& reporter, Map::Level& level) const {}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region GP2 Rule
|
||||
|
||||
constexpr char8_t GP2[] = u8"GP2";
|
||||
|
||||
GpRule2::GpRule2() : IRule() {}
|
||||
|
||||
GpRule2::~GpRule2() {}
|
||||
|
||||
std::u8string_view GpRule2::GetRuleName() const {
|
||||
return GP2;
|
||||
}
|
||||
|
||||
void GpRule2::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region GP3 Rule
|
||||
//
|
||||
// Gp3Rule::Gp3Rule() : IRule() {}
|
||||
//
|
||||
// Gp3Rule::~Gp3Rule() {}
|
||||
//
|
||||
// std::u8string_view Gp3Rule::GetRuleName() const {
|
||||
// return u8"GP3";
|
||||
// }
|
||||
//
|
||||
// void Gp3Rule::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
// // TODO: Mesh hash is not implemented.
|
||||
// }
|
||||
//
|
||||
//#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Rule
|
||||
@@ -1,63 +0,0 @@
|
||||
#pragma once
|
||||
#include "../Rule.hpp"
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
|
||||
// Reference: https://tieba.baidu.com/p/3182981807
|
||||
|
||||
/**
|
||||
* @brief Gamepiaynmo Rule 1
|
||||
* @details
|
||||
* The most comprehensive group checker inspired from Ballance Blender Plugin.
|
||||
*/
|
||||
class GpRule1 : public IRule {
|
||||
public:
|
||||
GpRule1();
|
||||
virtual ~GpRule1();
|
||||
YYCC_DELETE_COPY_MOVE(GpRule1)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Gamepiaynmo Rule 2
|
||||
* @details
|
||||
* Every Ballance group should not have any groups with same name.
|
||||
*/
|
||||
class GpRule2 : public IRule {
|
||||
public:
|
||||
GpRule2();
|
||||
virtual ~GpRule2();
|
||||
YYCC_DELETE_COPY_MOVE(GpRule2)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
///**
|
||||
// * @brief Gamepiaynmo Rule 2
|
||||
// * @details
|
||||
// * This rule make sure that one Ballance element must be grouped into only one sector group.
|
||||
// * Multiple grouping and none grouping will throw error.
|
||||
// */
|
||||
///**
|
||||
// * @brief Gamepiaynmo Rule 3
|
||||
// * @details
|
||||
// * This rule make sure that all Ballance element is grouped into correct element group.
|
||||
// * This rule will check the mesh of PH and guess which element it is.
|
||||
// */
|
||||
//class Gp3Rule : public IRule {
|
||||
//public:
|
||||
// Gp3Rule();
|
||||
// virtual ~Gp3Rule();
|
||||
// YYCC_DELETE_COPY_MOVE(Gp3Rule)
|
||||
|
||||
//public:
|
||||
// std::u8string_view GetRuleName() const override;
|
||||
// void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
//};
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
#include "SOneRules.hpp"
|
||||
#include "Shared.hpp"
|
||||
|
||||
namespace L = LibCmo;
|
||||
namespace C = LibCmo::CK2;
|
||||
namespace O = LibCmo::CK2::ObjImpls;
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
|
||||
#pragma region SOne Rule 1
|
||||
|
||||
constexpr char8_t SONE1[] = u8"SONE1";
|
||||
|
||||
SOneRule1::SOneRule1() : IRule() {}
|
||||
|
||||
SOneRule1::~SOneRule1() {}
|
||||
|
||||
std::u8string_view SOneRule1::GetRuleName() const {
|
||||
return SONE1;
|
||||
}
|
||||
|
||||
void SOneRule1::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
auto* ctx = level.GetCKContext();
|
||||
auto physicalized_3dobjects = Shared::FetchPhysicalized3dObjects(ctx);
|
||||
|
||||
for (auto* physicalized_3dobject : physicalized_3dobjects) {
|
||||
auto* mesh = physicalized_3dobject->GetCurrentMesh();
|
||||
if (mesh == nullptr) {
|
||||
reporter.FormatError(
|
||||
SONE1,
|
||||
u8R"(Object "%s" is grouped into physicalization group, but it doesn't have any associated mesh. This will cause itself and following objects can not be physicalized.)",
|
||||
Shared::RenderObjectName(physicalized_3dobject));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Rule
|
||||
@@ -1,110 +0,0 @@
|
||||
#pragma once
|
||||
#include <VTAll.hpp>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
namespace BMapInspector::Rule::Shared {
|
||||
|
||||
namespace L = LibCmo;
|
||||
namespace C = LibCmo::CK2;
|
||||
namespace O = LibCmo::CK2::ObjImpls;
|
||||
|
||||
#pragma region Constant Values
|
||||
|
||||
namespace GroupNames {
|
||||
// clang-format off
|
||||
constexpr char8_t PHYS_FLOORS[] = u8"Phys_Floors";
|
||||
constexpr char8_t PHYS_FLOORRAILS[] = u8"Phys_FloorRails";
|
||||
constexpr char8_t PHYS_FLOORSTOPPER[] = u8"Phys_FloorStopper";
|
||||
|
||||
constexpr std::array ALL_PH{
|
||||
u8"P_Extra_Life",
|
||||
u8"P_Extra_Point",
|
||||
u8"P_Trafo_Paper",
|
||||
u8"P_Trafo_Stone",
|
||||
u8"P_Trafo_Wood",
|
||||
u8"P_Ball_Paper",
|
||||
u8"P_Ball_Stone",
|
||||
u8"P_Ball_Wood",
|
||||
u8"P_Box",
|
||||
u8"P_Dome",
|
||||
u8"P_Modul_01",
|
||||
u8"P_Modul_03",
|
||||
u8"P_Modul_08",
|
||||
u8"P_Modul_17",
|
||||
u8"P_Modul_18",
|
||||
u8"P_Modul_19",
|
||||
u8"P_Modul_25",
|
||||
u8"P_Modul_26",
|
||||
u8"P_Modul_29",
|
||||
u8"P_Modul_30",
|
||||
u8"P_Modul_34",
|
||||
u8"P_Modul_37",
|
||||
u8"P_Modul_41",
|
||||
u8"PS_Levelstart",
|
||||
u8"PE_Levelende",
|
||||
u8"PC_Checkpoints",
|
||||
u8"PR_Resetpoints",
|
||||
};
|
||||
|
||||
// clang-format on
|
||||
} // namespace GroupNames
|
||||
|
||||
namespace TextureNames {
|
||||
// clang-format off
|
||||
constexpr char8_t RAIL_ENVIRONMENT[] = u8"Rail_Environment.bmp";
|
||||
// clang-format on
|
||||
|
||||
} // namespace TextureNames
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Check Functions
|
||||
|
||||
/**
|
||||
* @brief Check whether given 2 float point values are equal with given tolerance.
|
||||
* @param lhs
|
||||
* @param rhs
|
||||
* @param tolerance
|
||||
* @return
|
||||
*/
|
||||
bool FPEqual(L::CKFLOAT lhs, L::CKFLOAT rhs, L::CKFLOAT tolerance);
|
||||
/**
|
||||
* @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);
|
||||
std::vector<O::CK3dObject*> FetchPhysicalized3dObjects(C::CKContext* ctx);
|
||||
/**
|
||||
* @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::CK3dObject*> Iter3dObjects(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
|
||||
@@ -1,132 +0,0 @@
|
||||
#include "YYCRules.hpp"
|
||||
#include "Shared.hpp"
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
|
||||
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() : IRule() {}
|
||||
|
||||
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_3dobjects = Shared::Iter3dObjects(phys_floorrails);
|
||||
for (auto* group_3dobject : group_3dobjects) {
|
||||
// Then we iterate their current meshes
|
||||
auto* mesh = group_3dobject->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 some parts of this object be smooth unexpectly.)",
|
||||
Shared::RenderObjectName(group_3dobject),
|
||||
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
|
||||
|
||||
#pragma region YYC Rule 2
|
||||
|
||||
constexpr char8_t YYC2[] = u8"YYC2";
|
||||
|
||||
YYCRule2::YYCRule2() : IRule() {}
|
||||
|
||||
YYCRule2::~YYCRule2() {}
|
||||
|
||||
std::u8string_view YYCRule2::GetRuleName() const {
|
||||
return YYC2;
|
||||
}
|
||||
|
||||
void YYCRule2::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
auto* ctx = level.GetCKContext();
|
||||
auto physicalized_3dobjects = Shared::FetchPhysicalized3dObjects(ctx);
|
||||
|
||||
// Iterate all physicalized 3dobject
|
||||
for (auto* physicalized_3dobject : physicalized_3dobjects) {
|
||||
// Get its mesh
|
||||
auto* mesh = physicalized_3dobject->GetCurrentMesh();
|
||||
if (mesh == nullptr) continue;
|
||||
|
||||
// Create a bool vector with vertex count and false init value.
|
||||
std::vector<bool> used_vertex(mesh->GetVertexCount(), false);
|
||||
// Iterate all face and set their vertex as used.
|
||||
auto* face_indices = mesh->GetFaceIndices();
|
||||
for (L::CKDWORD face_idx = 0; face_idx < mesh->GetFaceCount(); ++face_idx) {
|
||||
used_vertex[face_indices[face_idx * 3]] = true;
|
||||
used_vertex[face_indices[face_idx * 3 + 1]] = true;
|
||||
used_vertex[face_indices[face_idx * 3 + 2]] = true;
|
||||
}
|
||||
// Check whether there is unused vertex
|
||||
auto has_unused_vertex = std::any_of(used_vertex.begin(), used_vertex.end(), [](bool v) { return v == false; });
|
||||
// If there is unused vertex, report error
|
||||
if (has_unused_vertex) {
|
||||
reporter.FormatError(
|
||||
YYC2,
|
||||
u8R"(Object "%s" is grouped into physicalization groups, and its referred mesh "%s" has isolated vertex. This will cause it can not be physicalized.)",
|
||||
Shared::RenderObjectName(physicalized_3dobject),
|
||||
Shared::RenderObjectName(mesh));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Rule
|
||||
@@ -1,40 +0,0 @@
|
||||
#pragma once
|
||||
#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();
|
||||
virtual ~YYCRule1();
|
||||
YYCC_DELETE_COPY_MOVE(YYCRule1)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief YYC12345 Rule 2
|
||||
* @details
|
||||
* The object grouped into physicalization group should not have isolated vertex,
|
||||
* otherwise it will fail to be physicalized.
|
||||
*/
|
||||
class YYCRule2 : public IRule {
|
||||
public:
|
||||
YYCRule2();
|
||||
virtual ~YYCRule2();
|
||||
YYCC_DELETE_COPY_MOVE(YYCRule2)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
#include "../Rule.hpp"
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
|
||||
}
|
||||
64
Ballance/BMapInspector/Ruleset/BBugRules.cpp
Normal file
64
Ballance/BMapInspector/Ruleset/BBugRules.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#include "BBugRules.hpp"
|
||||
|
||||
namespace L = LibCmo;
|
||||
namespace C = LibCmo::CK2;
|
||||
namespace O = LibCmo::CK2::ObjImpls;
|
||||
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
#pragma region BBug Rule 1
|
||||
|
||||
BBugRule1::BBugRule1() : Rule::IRule() {}
|
||||
|
||||
BBugRule1::~BBugRule1() {}
|
||||
|
||||
std::u8string_view BBugRule1::GetRuleName() const {
|
||||
return u8"BBUG1";
|
||||
}
|
||||
|
||||
void BBugRule1::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
if (!level.GetTargetLights().empty()) {
|
||||
reporter.WriteInfo(u8"Using light in map is not suggested.");
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region BBug Rule 2
|
||||
|
||||
BBugRule2::BBugRule2() : Rule::IRule() {}
|
||||
|
||||
BBugRule2::~BBugRule2() {}
|
||||
|
||||
std::u8string_view BBugRule2::GetRuleName() const {
|
||||
return u8"BBUG2";
|
||||
}
|
||||
|
||||
void BBugRule2::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
if (!level.GetTargetCameras().empty()) {
|
||||
reporter.WriteInfo(u8"Using camera in map is not suggested.");
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region BBug Rule 3
|
||||
|
||||
BBugRule3::BBugRule3() : Rule::IRule() {}
|
||||
|
||||
BBugRule3::~BBugRule3() {}
|
||||
|
||||
std::u8string_view BBugRule3::GetRuleName() const {
|
||||
return u8"BBUG3";
|
||||
}
|
||||
|
||||
void BBugRule3::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
// TODO:
|
||||
// This rule is complex and can be done by Ballance Blender Plugin.
|
||||
// So we are not urgently to implement it in there.
|
||||
// Just make a rule placeholder in there and may finish it in future.
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Ruleset
|
||||
54
Ballance/BMapInspector/Ruleset/BBugRules.hpp
Normal file
54
Ballance/BMapInspector/Ruleset/BBugRules.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
#include "../Rule.hpp"
|
||||
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
/**
|
||||
* @brief BBug Rule 1
|
||||
* @details
|
||||
* Using light in map is not suggested.
|
||||
*/
|
||||
class BBugRule1 : public Rule::IRule {
|
||||
public:
|
||||
BBugRule1();
|
||||
virtual ~BBugRule1();
|
||||
YYCC_DELETE_COPY_MOVE(BBugRule1)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief BBug Rule 2
|
||||
* @details
|
||||
* Using camera in map is not suggested.
|
||||
*/
|
||||
class BBugRule2 : public Rule::IRule {
|
||||
public:
|
||||
BBugRule2();
|
||||
virtual ~BBugRule2();
|
||||
YYCC_DELETE_COPY_MOVE(BBugRule2)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief BBug Rule 3
|
||||
* @details
|
||||
* Check whether the parameters of all materials is same with Ballance vanilla settings.
|
||||
*/
|
||||
class BBugRule3 : public Rule::IRule {
|
||||
public:
|
||||
BBugRule3();
|
||||
virtual ~BBugRule3();
|
||||
YYCC_DELETE_COPY_MOVE(BBugRule3)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
}
|
||||
80
Ballance/BMapInspector/Ruleset/ChirsRules.cpp
Normal file
80
Ballance/BMapInspector/Ruleset/ChirsRules.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
#include "ChirsRules.hpp"
|
||||
#include "Shared/Utility.hpp"
|
||||
#include "Shared/Name.hpp"
|
||||
|
||||
namespace L = LibCmo;
|
||||
namespace C = LibCmo::CK2;
|
||||
namespace O = LibCmo::CK2::ObjImpls;
|
||||
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
#pragma region Chirs Rule 1
|
||||
|
||||
// Reference: https://tieba.baidu.com/p/5913556704
|
||||
|
||||
ChirsRule1::ChirsRule1() : Rule::IRule() {}
|
||||
|
||||
ChirsRule1::~ChirsRule1() {}
|
||||
|
||||
std::u8string_view ChirsRule1::GetRuleName() const {
|
||||
return u8"CHIRS1";
|
||||
}
|
||||
|
||||
void ChirsRule1::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
constexpr char8_t MTL_LATERNE_VERLAUF[] = u8"Laterne_Verlauf";
|
||||
auto* ctx = level.GetCKContext();
|
||||
|
||||
// Fetch Laterne_Verlauf first
|
||||
auto* latern = Shared::Utility::FetchMaterial(ctx, MTL_LATERNE_VERLAUF);
|
||||
if (latern == nullptr) return;
|
||||
|
||||
// Report warning if this material's texture is not Laterne_Verlauf.tga
|
||||
auto* latern_tex = latern->GetTexture();
|
||||
if (latern_tex == nullptr) {
|
||||
reporter.FormatWarning(
|
||||
u8"Find a material named %s but it doesn't have associated texture. "
|
||||
u8"It occupies the magic material %s which affect the ray of latern in game. Please confirm this is your intention.",
|
||||
Shared::Utility::QuoteText(MTL_LATERNE_VERLAUF).c_str(),
|
||||
Shared::Utility::QuoteText(MTL_LATERNE_VERLAUF).c_str());
|
||||
} else {
|
||||
if (!Shared::Utility::CheckTextureFileName(latern_tex, Shared::Name::Texture::LATERNE_VERLAUF)) {
|
||||
reporter.FormatWarning(
|
||||
u8"Find a material named %s but its texture is not %s. "
|
||||
u8"It occupies the magic material %s which affect the ray of latern in game. Please confirm this is your intention.",
|
||||
Shared::Utility::QuoteText(MTL_LATERNE_VERLAUF).c_str(),
|
||||
Shared::Utility::QuoteText(Shared::Name::Texture::LATERNE_VERLAUF).c_str(),
|
||||
Shared::Utility::QuoteText(MTL_LATERNE_VERLAUF).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Report warning if there is multiple Laterne_Verlauf material.
|
||||
auto next_latern = ctx->GetObjectByNameAndClass(MTL_LATERNE_VERLAUF, C::CK_CLASSID::CKCID_MATERIAL, latern);
|
||||
if (next_latern != nullptr) {
|
||||
reporter.FormatWarning(u8"There are multiple materials named %s. This will cause the disappearance of some latern's rays.",
|
||||
Shared::Utility::QuoteText(MTL_LATERNE_VERLAUF).c_str());
|
||||
}
|
||||
|
||||
// Report warning if some materials' texture is Laterne_Verlauf,
|
||||
// but its name is not Laterne_Verlauf.
|
||||
for (auto* other_mtl : level.GetMaterials()) {
|
||||
if (C::CKStrEqual(other_mtl->GetName(), MTL_LATERNE_VERLAUF)) continue;
|
||||
|
||||
auto other_mtl_tex = other_mtl->GetTexture();
|
||||
if (other_mtl_tex == nullptr) continue;
|
||||
|
||||
if (Shared::Utility::CheckTextureFileName(other_mtl_tex, Shared::Name::Texture::LATERNE_VERLAUF)) {
|
||||
reporter.FormatWarning(
|
||||
u8"Find material %s referring texture %s, but its name is not %s. "
|
||||
u8"Please confirm the usage of this material. If it is used as %s, please rename it into %s to have correct latern ray.",
|
||||
Shared::Utility::QuoteObjectName(other_mtl).c_str(),
|
||||
Shared::Utility::QuoteText(Shared::Name::Texture::LATERNE_VERLAUF).c_str(),
|
||||
Shared::Utility::QuoteText(MTL_LATERNE_VERLAUF).c_str(),
|
||||
Shared::Utility::QuoteText(MTL_LATERNE_VERLAUF).c_str(),
|
||||
Shared::Utility::QuoteText(MTL_LATERNE_VERLAUF).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Ruleset
|
||||
@@ -1,21 +1,19 @@
|
||||
#pragma once
|
||||
#include "../Rule.hpp"
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
|
||||
// Reference: https://tieba.baidu.com/p/5913556704
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
/**
|
||||
* @brief Chirs241097 Rule 1
|
||||
* @brief chirs241097 Rule 1
|
||||
* @details
|
||||
* This rule will make sure that there is only 1 texture named Laterne_Verlauf in map,
|
||||
* which represent the ray of latern.
|
||||
*/
|
||||
class Chirs1Rule : public IRule {
|
||||
class ChirsRule1 : public Rule::IRule {
|
||||
public:
|
||||
Chirs1Rule();
|
||||
virtual ~Chirs1Rule();
|
||||
YYCC_DELETE_COPY_MOVE(Chirs1Rule)
|
||||
ChirsRule1();
|
||||
virtual ~ChirsRule1();
|
||||
YYCC_DELETE_COPY_MOVE(ChirsRule1)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
139
Ballance/BMapInspector/Ruleset/GpRules.cpp
Normal file
139
Ballance/BMapInspector/Ruleset/GpRules.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
#include "GpRules.hpp"
|
||||
#include "Shared/Utility.hpp"
|
||||
#include "Shared/Sector.hpp"
|
||||
#include <set>
|
||||
|
||||
namespace L = LibCmo;
|
||||
namespace C = LibCmo::CK2;
|
||||
namespace O = LibCmo::CK2::ObjImpls;
|
||||
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
#pragma region GP1 Rule
|
||||
|
||||
// Reference: https://tieba.baidu.com/p/3182981807
|
||||
|
||||
GpRule1::GpRule1() : Rule::IRule() {}
|
||||
|
||||
GpRule1::~GpRule1() {}
|
||||
|
||||
std::u8string_view GpRule1::GetRuleName() const {
|
||||
return u8"GP1";
|
||||
}
|
||||
|
||||
void GpRule1::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
// TODO:
|
||||
// This rule is complex and can be done by Ballance Blender Plugin.
|
||||
// So we are not urgently to implement it in there.
|
||||
// Just make a rule placeholder in there and may finish it in future.
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region GP2 Rule
|
||||
|
||||
GpRule2::GpRule2() : Rule::IRule() {}
|
||||
|
||||
GpRule2::~GpRule2() {}
|
||||
|
||||
std::u8string_view GpRule2::GetRuleName() const {
|
||||
return u8"GP2";
|
||||
}
|
||||
|
||||
void GpRule2::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
auto* ctx = level.GetCKContext();
|
||||
Shared::Sector::SectorNameBuilder builder;
|
||||
|
||||
// We need collect all group names first,
|
||||
// becuase in following code we need frequent visit them
|
||||
std::set<std::u8string> group_names;
|
||||
for (auto* group : level.GetGroups()) {
|
||||
auto group_name = group->GetName();
|
||||
if (group_name != nullptr) {
|
||||
group_names.emplace(std::u8string(group_name));
|
||||
}
|
||||
}
|
||||
|
||||
// Check the sector count of this game.
|
||||
L::CKDWORD sector_count;
|
||||
for (sector_count = Shared::Sector::MIN_SECTOR; sector_count <= Shared::Sector::MAX_SECTOR; ++sector_count) {
|
||||
// Build name first with special treat for sector 9
|
||||
if (sector_count != 9) {
|
||||
auto sector_name = builder.get_name(sector_count);
|
||||
if (!group_names.contains(sector_name)) {
|
||||
if (sector_count == Shared::Sector::MIN_SECTOR) {
|
||||
reporter.WriteError(u8"Can not find any reasonable sector group in your map.");
|
||||
return;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
auto sector_names = builder.get_sector9_names();
|
||||
bool has_legacy_sector = group_names.contains(sector_names.legacy_name);
|
||||
bool has_intuitive_sector = group_names.contains(sector_names.intuitive_name);
|
||||
if (!has_legacy_sector && !has_intuitive_sector) {
|
||||
break;
|
||||
}
|
||||
if (has_legacy_sector && has_intuitive_sector) {
|
||||
reporter.FormatError(u8"Found %s and %s at the same map. This is not allowed.",
|
||||
Shared::Utility::QuoteText(sector_names.legacy_name).c_str(),
|
||||
Shared::Utility::QuoteText(sector_names.intuitive_name).c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now sector_count is the first sector which can not find,
|
||||
// so we need minus one on it.
|
||||
--sector_count;
|
||||
|
||||
// Report sector count info.
|
||||
reporter.FormatInfo(u8"Your map has %" PRIuCKDWORD " sector(s).", sector_count);
|
||||
// Report special warning for map which only contain 1 sector.
|
||||
if (sector_count == 1) {
|
||||
reporter.WriteWarning(u8"Your map only have one sector. "
|
||||
u8"This is okey but not suggested because it will cause mosaic issue on the flames of checkpoint. "
|
||||
u8"Consider adding another sector to resolve this issue.");
|
||||
}
|
||||
// Report warning for sector greater than 8.
|
||||
if (sector_count > 8) {
|
||||
reporter.WriteWarning(u8"You are creating a map with more than 8 sectors. "
|
||||
u8"This will cause vanilla Ballance freezed when loading it. "
|
||||
u8"Please make sure that all players of your map have properly set 999 sector loader up.");
|
||||
}
|
||||
// If there is sector 9, check its kind and report wanring if it is intuitive kind.
|
||||
if (sector_count > 8) {
|
||||
auto sector_names = builder.get_sector9_names();
|
||||
if (group_names.contains(sector_names.intuitive_name)) {
|
||||
reporter.FormatWarning(u8"You are using intuitive sector name, %s, for sector 9. "
|
||||
u8"This is only accepted by new 999 sector loader.",
|
||||
Shared::Utility::QuoteText(sector_names.intuitive_name).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// We continue check following sectors to make sure that all sector is successive.
|
||||
for (L::CKDWORD i = sector_count + 1; i <= Shared::Sector::MAX_SECTOR; ++i) {
|
||||
if (i != 9) {
|
||||
auto sector_name = builder.get_name(i);
|
||||
if (group_names.contains(sector_name)) {
|
||||
reporter.FormatError(u8"Found group %s unexpected. "
|
||||
u8"Please check whether sector groups are successive in your map.",
|
||||
Shared::Utility::QuoteText(sector_name).c_str());
|
||||
}
|
||||
} else {
|
||||
auto sector_names = builder.get_sector9_names();
|
||||
bool has_legacy_sector = group_names.contains(sector_names.legacy_name);
|
||||
bool has_intuitive_sector = group_names.contains(sector_names.intuitive_name);
|
||||
if (has_legacy_sector || has_intuitive_sector) {
|
||||
reporter.FormatError(u8"Found group %s or %s unexpected. "
|
||||
u8"Please check whether sector groups are successive in your map.",
|
||||
Shared::Utility::QuoteText(sector_names.legacy_name).c_str(),
|
||||
Shared::Utility::QuoteText(sector_names.intuitive_name).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Ruleset
|
||||
44
Ballance/BMapInspector/Ruleset/GpRules.hpp
Normal file
44
Ballance/BMapInspector/Ruleset/GpRules.hpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
#include "../Rule.hpp"
|
||||
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
/**
|
||||
* @brief Gamepiaynmo Rule 1
|
||||
* @details
|
||||
* The most comprehensive group checker inspired from Ballance Blender Plugin.
|
||||
*/
|
||||
class GpRule1 : public Rule::IRule {
|
||||
public:
|
||||
GpRule1();
|
||||
virtual ~GpRule1();
|
||||
YYCC_DELETE_COPY_MOVE(GpRule1)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Gamepiaynmo Rule 2
|
||||
* @details
|
||||
* This rule will:
|
||||
* \li Show how many sector located in given map.
|
||||
* \li Check whether there is sector group.
|
||||
* \li Check whether use intuitive sector name for sector 9.
|
||||
* \li Warn for sector count is equal to 1. It will cause mosaic issue on the flames of checkpoint.
|
||||
* \li Warn for sector count greater than 8. It will cause vanilla game freezed without 999 sector loader.
|
||||
* \li Check whether sector group is successive.
|
||||
*/
|
||||
class GpRule2 : public Rule::IRule {
|
||||
public:
|
||||
GpRule2();
|
||||
virtual ~GpRule2();
|
||||
YYCC_DELETE_COPY_MOVE(GpRule2)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,23 +1,22 @@
|
||||
#include "LXRules.hpp"
|
||||
#include "Shared.hpp"
|
||||
#include "Shared/Utility.hpp"
|
||||
#include "Shared/Name.hpp"
|
||||
#include <set>
|
||||
|
||||
namespace L = LibCmo;
|
||||
namespace C = LibCmo::CK2;
|
||||
namespace O = LibCmo::CK2::ObjImpls;
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
#pragma region LX Rule 1
|
||||
|
||||
constexpr char8_t LX1[] = u8"LX1";
|
||||
|
||||
LXRule1::LXRule1() : IRule() {}
|
||||
LXRule1::LXRule1() : Rule::IRule() {}
|
||||
|
||||
LXRule1::~LXRule1() {}
|
||||
|
||||
std::u8string_view LXRule1::GetRuleName() const {
|
||||
return LX1;
|
||||
return u8"LX1";
|
||||
}
|
||||
|
||||
void LXRule1::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
@@ -25,10 +24,10 @@ namespace BMapInspector::Rule {
|
||||
|
||||
// First we fetch all Ballance element and push them into set.
|
||||
std::set<O::CK3dObject*> elements;
|
||||
for (auto* group_name : Shared::GroupNames::ALL_PH) {
|
||||
auto* group = Shared::FetchGroup(ctx, group_name);
|
||||
for (auto* group_name : Shared::Name::Group::ALL_PH) {
|
||||
auto* group = Shared::Utility::FetchGroup(ctx, group_name);
|
||||
if (group == nullptr) continue;
|
||||
auto group_objects = Shared::Iter3dObjects(group);
|
||||
auto group_objects = Shared::Utility::Iter3dObjects(group);
|
||||
for (auto* group_object : group_objects) {
|
||||
elements.emplace(group_object);
|
||||
}
|
||||
@@ -49,7 +48,7 @@ namespace BMapInspector::Rule {
|
||||
// because we do not want to duplicatedly process it.
|
||||
if (mesh_insert_rv.second) {
|
||||
// Iterate all meshes
|
||||
auto mtls = Shared::IterMaterial(mesh);
|
||||
auto mtls = Shared::Utility::IterMaterial(mesh);
|
||||
for (auto* mtl : mtls) {
|
||||
// Add into material set
|
||||
auto mtl_insert_rv = element_materials.emplace(mtl);
|
||||
@@ -76,23 +75,21 @@ namespace BMapInspector::Rule {
|
||||
if (mesh == nullptr) continue;
|
||||
// And check mesh
|
||||
if (element_meshes.contains(mesh)) {
|
||||
reporter.FormatError(
|
||||
LX1,
|
||||
u8R"(Object "%s" used mesh "%s" is already used by a Ballance element. This will cause this object can not be rendered correctly in level.)",
|
||||
Shared::RenderObjectName(other_object),
|
||||
Shared::RenderObjectName(mesh));
|
||||
reporter.FormatError(u8"Object %s used mesh %s is already used by a Ballance element. "
|
||||
u8"This will cause this object can not be rendered correctly in level.",
|
||||
Shared::Utility::QuoteObjectName(other_object).c_str(),
|
||||
Shared::Utility::QuoteObjectName(mesh).c_str());
|
||||
} else {
|
||||
// If not, check material.
|
||||
// Iterate all meshes
|
||||
auto mtls = Shared::IterMaterial(mesh);
|
||||
auto mtls = Shared::Utility::IterMaterial(mesh);
|
||||
for (auto* mtl : mtls) {
|
||||
if (element_materials.contains(mtl)) {
|
||||
reporter.FormatError(
|
||||
LX1,
|
||||
u8R"(Object "%s" used material "%s" (referred by mesh "%s") is already used by a Ballance element. This will cause this object can not be rendered correctly in level.)",
|
||||
Shared::RenderObjectName(other_object),
|
||||
Shared::RenderObjectName(mtl),
|
||||
Shared::RenderObjectName(mesh));
|
||||
reporter.FormatError(u8"Object %s used material %s (referred by mesh %s) is already used by a Ballance element. "
|
||||
u8"This will cause this object can not be rendered correctly in level.",
|
||||
Shared::Utility::QuoteObjectName(other_object).c_str(),
|
||||
Shared::Utility::QuoteObjectName(mtl).c_str(),
|
||||
Shared::Utility::QuoteObjectName(mesh).c_str());
|
||||
} else {
|
||||
// Still not, check texture.
|
||||
// Fetch texture
|
||||
@@ -101,12 +98,12 @@ namespace BMapInspector::Rule {
|
||||
// And check it
|
||||
if (element_textures.contains(texture)) {
|
||||
reporter.FormatError(
|
||||
LX1,
|
||||
u8R"(Object "%s" used texture "%s" (referred by mesh "%s" and material "%s") is already used by a Ballance element. This will cause this object can not be rendered correctly in level.)",
|
||||
Shared::RenderObjectName(other_object),
|
||||
Shared::RenderObjectName(texture),
|
||||
Shared::RenderObjectName(mesh),
|
||||
Shared::RenderObjectName(mtl));
|
||||
u8"Object %s used texture %s (referred by mesh %s and material %s) is already used by a Ballance element. "
|
||||
u8"This will cause this object can not be rendered correctly in level.",
|
||||
Shared::Utility::QuoteObjectName(other_object).c_str(),
|
||||
Shared::Utility::QuoteObjectName(texture).c_str(),
|
||||
Shared::Utility::QuoteObjectName(mesh).c_str(),
|
||||
Shared::Utility::QuoteObjectName(mtl).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,4 +113,4 @@ namespace BMapInspector::Rule {
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Rule
|
||||
} // namespace BMapInspector::Ruleset
|
||||
@@ -1,14 +1,14 @@
|
||||
#pragma once
|
||||
#include "../Rule.hpp"
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
/**
|
||||
* @brief LengXi Rule 1
|
||||
* @details
|
||||
* All meshes, materials and textures used by Ballance elements should not be used by any other objects.
|
||||
*/
|
||||
class LXRule1 : public IRule {
|
||||
class LXRule1 : public Rule::IRule {
|
||||
public:
|
||||
LXRule1();
|
||||
virtual ~LXRule1();
|
||||
36
Ballance/BMapInspector/Ruleset/SOneRules.cpp
Normal file
36
Ballance/BMapInspector/Ruleset/SOneRules.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "SOneRules.hpp"
|
||||
#include "Shared/Utility.hpp"
|
||||
|
||||
namespace L = LibCmo;
|
||||
namespace C = LibCmo::CK2;
|
||||
namespace O = LibCmo::CK2::ObjImpls;
|
||||
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
#pragma region SOne Rule 1
|
||||
|
||||
SOneRule1::SOneRule1() : Rule::IRule() {}
|
||||
|
||||
SOneRule1::~SOneRule1() {}
|
||||
|
||||
std::u8string_view SOneRule1::GetRuleName() const {
|
||||
return u8"SONE1";
|
||||
}
|
||||
|
||||
void SOneRule1::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
auto* ctx = level.GetCKContext();
|
||||
auto physicalized_3dobjects = Shared::Utility::FetchPhysicalized3dObjects(ctx);
|
||||
|
||||
for (auto* physicalized_3dobject : physicalized_3dobjects) {
|
||||
auto* mesh = physicalized_3dobject->GetCurrentMesh();
|
||||
if (mesh == nullptr) {
|
||||
reporter.FormatError(u8"Object %s is grouped into physicalization group, but it doesn't have any associated mesh. "
|
||||
u8"This will cause itself and following objects can not be physicalized.",
|
||||
Shared::Utility::QuoteObjectName(physicalized_3dobject).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Ruleset
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
#include "../Rule.hpp"
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
/**
|
||||
* @brief SomeOne_001 Rule 1
|
||||
@@ -9,7 +9,7 @@ namespace BMapInspector::Rule {
|
||||
* If there is a physicalized object without any mesh,
|
||||
* itself and following objects will not be physicalized.
|
||||
*/
|
||||
class SOneRule1 : public IRule {
|
||||
class SOneRule1 : public Rule::IRule {
|
||||
public:
|
||||
SOneRule1();
|
||||
virtual ~SOneRule1();
|
||||
@@ -20,4 +20,4 @@ namespace BMapInspector::Rule {
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
} // namespace BMapInspector::Rule
|
||||
} // namespace BMapInspector::Ruleset
|
||||
@@ -1,28 +1,28 @@
|
||||
#include "SSBRules.hpp"
|
||||
#include "Shared.hpp"
|
||||
#include "Shared/Utility.hpp"
|
||||
|
||||
namespace L = LibCmo;
|
||||
namespace C = LibCmo::CK2;
|
||||
namespace V = LibCmo::VxMath;
|
||||
namespace O = LibCmo::CK2::ObjImpls;
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
#pragma region SSB Rule 1
|
||||
|
||||
constexpr char8_t SSB1[] = u8"SSB1";
|
||||
constexpr L::CKFLOAT TOLERANCE = 0.001f;
|
||||
|
||||
SSBRule1::SSBRule1() : IRule() {}
|
||||
SSBRule1::SSBRule1() : Rule::IRule() {}
|
||||
|
||||
SSBRule1::~SSBRule1() {}
|
||||
|
||||
std::u8string_view SSBRule1::GetRuleName() const {
|
||||
return SSB1;
|
||||
return u8"SSB1";
|
||||
}
|
||||
|
||||
void SSBRule1::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
auto* ctx = level.GetCKContext();
|
||||
auto physicalized_3dobjects = Shared::FetchPhysicalized3dObjects(ctx);
|
||||
auto physicalized_3dobjects = Shared::Utility::FetchPhysicalized3dObjects(ctx);
|
||||
|
||||
// Iterate all physicalized 3dobject
|
||||
for (auto* physicalized_3dobject : physicalized_3dobjects) {
|
||||
@@ -41,19 +41,18 @@ namespace BMapInspector::Rule {
|
||||
V::VxVector3 col3(matrix[0][2], matrix[1][2], matrix[2][2]);
|
||||
// Compute their length, then check their value with tolerance.
|
||||
bool has_scale = false;
|
||||
if (!Shared::FPEqual(col1.Length(), 1.0f, TOLERANCE)) has_scale = true;
|
||||
if (!Shared::FPEqual(col2.Length(), 1.0f, TOLERANCE)) has_scale = true;
|
||||
if (!Shared::FPEqual(col3.Length(), 1.0f, TOLERANCE)) has_scale = true;
|
||||
if (!Shared::Utility::FPEqual(col1.Length(), 1.0f, TOLERANCE)) has_scale = true;
|
||||
if (!Shared::Utility::FPEqual(col2.Length(), 1.0f, TOLERANCE)) has_scale = true;
|
||||
if (!Shared::Utility::FPEqual(col3.Length(), 1.0f, TOLERANCE)) has_scale = true;
|
||||
// If it has scale factor, report error
|
||||
if (has_scale) {
|
||||
reporter.FormatError(
|
||||
SSB1,
|
||||
u8R"(Object "%s" grouped into physicalization groups has scale factor. This will cause its collision shape is different with its render shape.)",
|
||||
Shared::RenderObjectName(physicalized_3dobject));
|
||||
reporter.FormatError(u8"Object %s grouped into physicalization groups has scale factor. "
|
||||
u8"This will cause its collision shape is different with its render shape.",
|
||||
Shared::Utility::QuoteObjectName(physicalized_3dobject).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Rule
|
||||
} // namespace BMapInspector::Ruleset
|
||||
@@ -1,14 +1,14 @@
|
||||
#pragma once
|
||||
#include "../Rule.hpp"
|
||||
|
||||
namespace BMapInspector::Rule {
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
/**
|
||||
* @brief speedystoneball Rule 1
|
||||
* @details
|
||||
* Pjysicalized object should not have scale factor, especially negative scale factor (mirror).
|
||||
* Physicalized object should not have scale factor, especially negative scale factor (mirror).
|
||||
*/
|
||||
class SSBRule1 : public IRule {
|
||||
class SSBRule1 : public Rule::IRule {
|
||||
public:
|
||||
SSBRule1();
|
||||
virtual ~SSBRule1();
|
||||
@@ -19,4 +19,4 @@ namespace BMapInspector::Rule {
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
} // namespace BMapInspector::Rule
|
||||
} // namespace BMapInspector::Ruleset
|
||||
318
Ballance/BMapInspector/Ruleset/Shared/DupCmp.cpp
Normal file
318
Ballance/BMapInspector/Ruleset/Shared/DupCmp.cpp
Normal file
@@ -0,0 +1,318 @@
|
||||
#include "DupCmp.hpp"
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/string/op.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
namespace L = LibCmo;
|
||||
namespace C = LibCmo::CK2;
|
||||
namespace V = LibCmo::VxMath;
|
||||
namespace O = LibCmo::CK2::ObjImpls;
|
||||
namespace strop = yycc::string::op;
|
||||
|
||||
namespace std {
|
||||
|
||||
#pragma region Primitive CK Hasher
|
||||
|
||||
using BMapInspector::Ruleset::Shared::DupCmp::Hasher;
|
||||
|
||||
template<>
|
||||
struct hash<V::VxColor> {
|
||||
[[nodiscard]] size_t operator()(const V::VxColor& color) const noexcept {
|
||||
Hasher combiner;
|
||||
combiner.update(color.r);
|
||||
combiner.update(color.g);
|
||||
combiner.update(color.b);
|
||||
combiner.update(color.a);
|
||||
return combiner.finish();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct hash<V::VxVector2> {
|
||||
[[nodiscard]] size_t operator()(const V::VxVector2& vec) const noexcept {
|
||||
Hasher combiner;
|
||||
combiner.update(vec.x);
|
||||
combiner.update(vec.y);
|
||||
return combiner.finish();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct hash<V::VxVector3> {
|
||||
[[nodiscard]] size_t operator()(const V::VxVector3& vec) const noexcept {
|
||||
Hasher combiner;
|
||||
combiner.update(vec.x);
|
||||
combiner.update(vec.y);
|
||||
combiner.update(vec.z);
|
||||
return combiner.finish();
|
||||
}
|
||||
};
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace std
|
||||
|
||||
namespace BMapInspector::Ruleset::Shared::DupCmp {
|
||||
|
||||
#pragma region Hash Combiner
|
||||
|
||||
Hasher::Hasher() : seed(FNV_OFFSET_BASIS) {}
|
||||
|
||||
Hasher::~Hasher() {}
|
||||
|
||||
void Hasher::combine(ValueType h) {
|
||||
this->seed ^= h;
|
||||
this->seed *= FNV_PRIME;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region CKObject Hash and Equal
|
||||
|
||||
size_t CKTextureHash::operator()(const O::CKTexture* tex) const noexcept {
|
||||
const auto& texdata = tex->GetUnderlyingData();
|
||||
Hasher combiner;
|
||||
|
||||
auto filename = texdata.GetSlotFileName(0);
|
||||
if (filename == nullptr) {
|
||||
combiner.update(nullptr);
|
||||
} else {
|
||||
auto lower_filename = strop::to_lower(filename);
|
||||
combiner.update(lower_filename);
|
||||
}
|
||||
combiner.update(texdata.GetSaveOptions());
|
||||
combiner.update(tex->GetVideoFormat());
|
||||
|
||||
return combiner.finish();
|
||||
}
|
||||
|
||||
size_t CKMaterialHash::operator()(const O::CKMaterial* mtl) const noexcept {
|
||||
Hasher combiner;
|
||||
|
||||
combiner.update(mtl->GetDiffuse());
|
||||
combiner.update(mtl->GetAmbient());
|
||||
combiner.update(mtl->GetSpecular());
|
||||
combiner.update(mtl->GetEmissive());
|
||||
combiner.update(mtl->GetSpecularPower());
|
||||
|
||||
// TODO:
|
||||
// Use raw pointer for hash is dangerous.
|
||||
// But who cares? I simply assume that there is no memory reallocation.
|
||||
combiner.update(mtl->GetTexture());
|
||||
combiner.update(mtl->GetTextureBorderColor());
|
||||
|
||||
combiner.update(mtl->GetTextureBlendMode());
|
||||
combiner.update(mtl->GetTextureMinMode());
|
||||
combiner.update(mtl->GetTextureMagMode());
|
||||
combiner.update(mtl->GetTextureAddressMode());
|
||||
|
||||
combiner.update(mtl->GetSourceBlend());
|
||||
combiner.update(mtl->GetDestBlend());
|
||||
combiner.update(mtl->GetFillMode());
|
||||
combiner.update(mtl->GetShadeMode());
|
||||
|
||||
// TODO:
|
||||
// We also need use these "Enabled" variable to switch on/off
|
||||
// for some field's hashing according to the Virtools layout.
|
||||
// But I am lazy now.
|
||||
// I guess there is the same default values for those fields
|
||||
// controlled by some disable "Enabled" variable.
|
||||
combiner.update(mtl->GetAlphaTestEnabled());
|
||||
combiner.update(mtl->GetAlphaBlendEnabled());
|
||||
combiner.update(mtl->GetPerspectiveCorrectionEnabled());
|
||||
combiner.update(mtl->GetZWriteEnabled());
|
||||
combiner.update(mtl->GetTwoSidedEnabled());
|
||||
|
||||
combiner.update(mtl->GetAlphaRef());
|
||||
combiner.update(mtl->GetAlphaFunc());
|
||||
combiner.update(mtl->GetZFunc());
|
||||
|
||||
return combiner.finish();
|
||||
}
|
||||
|
||||
size_t CKMeshHash::operator()(const O::CKMesh* _mesh) const noexcept {
|
||||
O::CKMesh* mesh = const_cast<O::CKMesh*>(_mesh);
|
||||
Hasher combiner;
|
||||
|
||||
combiner.update(mesh->GetLitMode());
|
||||
|
||||
auto vertex_count = mesh->GetVertexCount();
|
||||
combiner.update(vertex_count);
|
||||
combiner.update_array(mesh->GetVertexPositions(), vertex_count);
|
||||
combiner.update_array(mesh->GetVertexNormals(), vertex_count);
|
||||
combiner.update_array(mesh->GetVertexUVs(), vertex_count);
|
||||
|
||||
// TODO:
|
||||
// In theory, we need remap face material slot index to underlying material CKID,
|
||||
// but its too complex. I give up.
|
||||
auto face_count = mesh->GetFaceCount();
|
||||
combiner.update(face_count);
|
||||
combiner.update_array(mesh->GetFaceIndices(), face_count * 3);
|
||||
combiner.update_array(mesh->GetFaceMaterialSlotIndexs(), face_count);
|
||||
|
||||
auto material_slot_count = mesh->GetMaterialSlotCount();
|
||||
combiner.update(material_slot_count);
|
||||
// TODO:
|
||||
// Same dangerous usage of raw pointer.
|
||||
combiner.update_array(mesh->GetMaterialSlots(), material_slot_count);
|
||||
|
||||
return combiner.finish();
|
||||
}
|
||||
|
||||
bool CKTextureEqualTo::operator()(const O::CKTexture* lhs, const O::CKTexture* rhs) const {
|
||||
// Compare underlying data
|
||||
const auto& lhs_data = lhs->GetUnderlyingData();
|
||||
const auto& rhs_data = rhs->GetUnderlyingData();
|
||||
|
||||
// Compare filename (case insensitive)
|
||||
auto lhs_filename = lhs_data.GetSlotFileName(0);
|
||||
auto rhs_filename = rhs_data.GetSlotFileName(0);
|
||||
if (!C::CKStrEqualI(lhs_filename, rhs_filename)) return false;
|
||||
|
||||
// Compare save options
|
||||
if (lhs_data.GetSaveOptions() != rhs_data.GetSaveOptions()) return false;
|
||||
// Compare video format
|
||||
if (lhs->GetVideoFormat() != rhs->GetVideoFormat()) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CKMaterialEqualTo::operator()(const O::CKMaterial* lhs, const O::CKMaterial* rhs) const {
|
||||
// Compare color properties
|
||||
if (lhs->GetDiffuse() != rhs->GetDiffuse()) return false;
|
||||
if (lhs->GetAmbient() != rhs->GetAmbient()) return false;
|
||||
if (lhs->GetSpecular() != rhs->GetSpecular()) return false;
|
||||
if (lhs->GetEmissive() != rhs->GetEmissive()) return false;
|
||||
if (lhs->GetSpecularPower() != rhs->GetSpecularPower()) return false;
|
||||
|
||||
// Compare texture properties
|
||||
if (lhs->GetTexture() != rhs->GetTexture()) return false;
|
||||
if (lhs->GetTextureBorderColor() != rhs->GetTextureBorderColor()) return false;
|
||||
|
||||
// Compare texture modes
|
||||
if (lhs->GetTextureBlendMode() != rhs->GetTextureBlendMode()) return false;
|
||||
if (lhs->GetTextureMinMode() != rhs->GetTextureMinMode()) return false;
|
||||
if (lhs->GetTextureMagMode() != rhs->GetTextureMagMode()) return false;
|
||||
if (lhs->GetTextureAddressMode() != rhs->GetTextureAddressMode()) return false;
|
||||
|
||||
// Compare blend modes
|
||||
if (lhs->GetSourceBlend() != rhs->GetSourceBlend()) return false;
|
||||
if (lhs->GetDestBlend() != rhs->GetDestBlend()) return false;
|
||||
if (lhs->GetFillMode() != rhs->GetFillMode()) return false;
|
||||
if (lhs->GetShadeMode() != rhs->GetShadeMode()) return false;
|
||||
|
||||
// Compare enable flags
|
||||
if (lhs->GetAlphaTestEnabled() != rhs->GetAlphaTestEnabled()) return false;
|
||||
if (lhs->GetAlphaBlendEnabled() != rhs->GetAlphaBlendEnabled()) return false;
|
||||
if (lhs->GetPerspectiveCorrectionEnabled() != rhs->GetPerspectiveCorrectionEnabled()) return false;
|
||||
if (lhs->GetZWriteEnabled() != rhs->GetZWriteEnabled()) return false;
|
||||
if (lhs->GetTwoSidedEnabled() != rhs->GetTwoSidedEnabled()) return false;
|
||||
|
||||
// Compare alpha and z function properties
|
||||
if (lhs->GetAlphaRef() != rhs->GetAlphaRef()) return false;
|
||||
if (lhs->GetAlphaFunc() != rhs->GetAlphaFunc()) return false;
|
||||
if (lhs->GetZFunc() != rhs->GetZFunc()) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CKMeshEqualTo::operator()(const O::CKMesh* _lhs, const O::CKMesh* _rhs) const {
|
||||
O::CKMesh* lhs = const_cast<O::CKMesh*>(_lhs);
|
||||
O::CKMesh* rhs = const_cast<O::CKMesh*>(_rhs);
|
||||
|
||||
// Compare lit mode
|
||||
if (lhs->GetLitMode() != rhs->GetLitMode()) return false;
|
||||
|
||||
// Compare vertex count
|
||||
auto vertex_count = lhs->GetVertexCount();
|
||||
if (vertex_count != rhs->GetVertexCount()) return false;
|
||||
|
||||
// Compare vertex data arrays
|
||||
if (!std::equal(lhs->GetVertexPositions(), lhs->GetVertexPositions() + vertex_count, rhs->GetVertexPositions())) return false;
|
||||
if (!std::equal(lhs->GetVertexNormals(), lhs->GetVertexNormals() + vertex_count, rhs->GetVertexNormals())) return false;
|
||||
if (!std::equal(lhs->GetVertexUVs(), lhs->GetVertexUVs() + vertex_count, rhs->GetVertexUVs())) return false;
|
||||
|
||||
// Compare face count
|
||||
auto face_count = lhs->GetFaceCount();
|
||||
if (face_count != rhs->GetFaceCount()) return false;
|
||||
|
||||
// Compare face data arrays
|
||||
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;
|
||||
|
||||
// Compare material slot count
|
||||
auto material_slot_count = lhs->GetMaterialSlotCount();
|
||||
if (material_slot_count != rhs->GetMaterialSlotCount()) return false;
|
||||
|
||||
// Compare material slots array
|
||||
if (!std::equal(lhs->GetMaterialSlots(), lhs->GetMaterialSlots() + material_slot_count, rhs->GetMaterialSlots())) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region CKObject Wrapper
|
||||
|
||||
CKTextureWrapper::CKTextureWrapper(O::CKTexture* texture) : texture(texture), hasher(), hash(std::nullopt) {}
|
||||
|
||||
CKTextureWrapper::~CKTextureWrapper() {}
|
||||
|
||||
O::CKTexture* CKTextureWrapper::GetTexture() const { return texture; }
|
||||
|
||||
size_t CKTextureWrapper::GetHash() const {
|
||||
if (!hash.has_value()) hash = hasher(texture);
|
||||
return hash.value();
|
||||
}
|
||||
|
||||
CKMaterialWrapper::CKMaterialWrapper(O::CKMaterial* material) : material(material), hasher(), hash(std::nullopt) {}
|
||||
|
||||
CKMaterialWrapper::~CKMaterialWrapper() {}
|
||||
|
||||
O::CKMaterial* CKMaterialWrapper::GetMaterial() const { return material; }
|
||||
|
||||
size_t CKMaterialWrapper::GetHash() const {
|
||||
if (!hash.has_value()) hash = hasher(material);
|
||||
return hash.value();
|
||||
}
|
||||
|
||||
CKMeshWrapper::CKMeshWrapper(O::CKMesh* mesh) : mesh(mesh), hasher(), hash(std::nullopt) {}
|
||||
|
||||
CKMeshWrapper::~CKMeshWrapper() {}
|
||||
|
||||
O::CKMesh* CKMeshWrapper::GetMesh() const { return mesh; }
|
||||
|
||||
size_t CKMeshWrapper::GetHash() const {
|
||||
if (!hash.has_value()) hash = hasher(mesh);
|
||||
return hash.value();
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region CKObject Wrapper Hash and Equal
|
||||
|
||||
size_t CKTextureWrapperHash::operator()(const CKTextureWrapper& tex) const noexcept { return tex.GetHash(); }
|
||||
|
||||
size_t CKMaterialWrapperHash::operator()(const CKMaterialWrapper& mtl) const noexcept { return mtl.GetHash(); }
|
||||
|
||||
size_t CKMeshWrapperHash::operator()(const CKMeshWrapper& mesh) const noexcept { return mesh.GetHash(); }
|
||||
|
||||
bool CKTextureWrapperEqualTo::operator()(const CKTextureWrapper& lhs, const CKTextureWrapper& rhs) const {
|
||||
if (lhs.GetHash() != rhs.GetHash()) return false;
|
||||
return equal_to(lhs.GetTexture(), rhs.GetTexture());
|
||||
}
|
||||
|
||||
bool CKMaterialWrapperEqualTo::operator()(const CKMaterialWrapper& lhs, const CKMaterialWrapper& rhs) const {
|
||||
if (lhs.GetHash() != rhs.GetHash()) return false;
|
||||
return equal_to(lhs.GetMaterial(), rhs.GetMaterial());
|
||||
}
|
||||
|
||||
bool CKMeshWrapperEqualTo::operator()(const CKMeshWrapper& lhs, const CKMeshWrapper& rhs) const {
|
||||
if (lhs.GetHash() != rhs.GetHash()) return false;
|
||||
return equal_to(lhs.GetMesh(), rhs.GetMesh());
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Ruleset::Shared::DupCmp
|
||||
185
Ballance/BMapInspector/Ruleset/Shared/DupCmp.hpp
Normal file
185
Ballance/BMapInspector/Ruleset/Shared/DupCmp.hpp
Normal file
@@ -0,0 +1,185 @@
|
||||
#pragma once
|
||||
#include <VTAll.hpp>
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/macro/class_copy_move.hpp>
|
||||
#include <yycc/macro/ptr_size_detector.hpp>
|
||||
#include <utility>
|
||||
#include <optional>
|
||||
|
||||
#define BMAPINSP_L LibCmo
|
||||
#define BMAPINSP_C LibCmo::CK2
|
||||
#define BMAPINSP_O LibCmo::CK2::ObjImpls
|
||||
|
||||
namespace BMapInspector::Ruleset::Shared::DupCmp {
|
||||
|
||||
#pragma region Hash Combiner
|
||||
|
||||
/**
|
||||
* @brief FNV-1a Hash Combiner
|
||||
*/
|
||||
class Hasher {
|
||||
public:
|
||||
using ValueType = size_t;
|
||||
|
||||
private:
|
||||
#if defined(YYCC_PTRSIZE_32)
|
||||
static constexpr ValueType FNV_OFFSET_BASIS = 2166136261U;
|
||||
static constexpr ValueType FNV_PRIME = 16777619U;
|
||||
#else
|
||||
static constexpr ValueType FNV_OFFSET_BASIS = 14695981039346656037ULL;
|
||||
static constexpr ValueType FNV_PRIME = 1099511628211ULL;
|
||||
#endif
|
||||
|
||||
public:
|
||||
Hasher();
|
||||
~Hasher();
|
||||
YYCC_DEFAULT_COPY_MOVE(Hasher)
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Update this hash combiner with new hash.
|
||||
* @param h
|
||||
*/
|
||||
void combine(ValueType h);
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Get final produced hash.
|
||||
* @return
|
||||
*/
|
||||
[[nodiscard]] ValueType finish() const noexcept { return this->seed; }
|
||||
template<typename T>
|
||||
void update(const T& v) {
|
||||
std::hash<T> hasher;
|
||||
combine(hasher(v));
|
||||
}
|
||||
template<typename T>
|
||||
void update_array(const T* addr, size_t cnt) {
|
||||
std::hash<T> hasher;
|
||||
for (size_t i = 0; i < cnt; ++i) {
|
||||
combine(hasher(addr[i]));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
ValueType seed;
|
||||
};
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Ruleset::Shared::DupCmp
|
||||
|
||||
namespace BMapInspector::Ruleset::Shared::DupCmp {
|
||||
|
||||
#pragma region CKObject Hash and Equal
|
||||
|
||||
struct CKTextureHash {
|
||||
[[nodiscard]] size_t operator()(const BMAPINSP_O::CKTexture* tex) const noexcept;
|
||||
};
|
||||
|
||||
struct CKMaterialHash {
|
||||
[[nodiscard]] size_t operator()(const BMAPINSP_O::CKMaterial* mtl) const noexcept;
|
||||
};
|
||||
|
||||
struct CKMeshHash {
|
||||
[[nodiscard]] size_t operator()(const BMAPINSP_O::CKMesh* _mesh) const noexcept;
|
||||
};
|
||||
|
||||
struct CKTextureEqualTo {
|
||||
[[nodiscard]] bool operator()(const BMAPINSP_O::CKTexture* lhs, const BMAPINSP_O::CKTexture* rhs) const;
|
||||
};
|
||||
|
||||
struct CKMaterialEqualTo {
|
||||
[[nodiscard]] bool operator()(const BMAPINSP_O::CKMaterial* lhs, const BMAPINSP_O::CKMaterial* rhs) const;
|
||||
};
|
||||
|
||||
struct CKMeshEqualTo {
|
||||
[[nodiscard]] bool operator()(const BMAPINSP_O::CKMesh* _lhs, const BMAPINSP_O::CKMesh* _rhs) const;
|
||||
};
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region CKObject Wrapper
|
||||
|
||||
class CKTextureWrapper {
|
||||
public:
|
||||
CKTextureWrapper(BMAPINSP_O::CKTexture* texture);
|
||||
~CKTextureWrapper();
|
||||
YYCC_DEFAULT_COPY_MOVE(CKTextureWrapper)
|
||||
|
||||
public:
|
||||
BMAPINSP_O::CKTexture* GetTexture() const;
|
||||
size_t GetHash() const;
|
||||
|
||||
private:
|
||||
BMAPINSP_O::CKTexture* texture;
|
||||
CKTextureHash hasher;
|
||||
mutable std::optional<size_t> hash;
|
||||
};
|
||||
|
||||
class CKMaterialWrapper {
|
||||
public:
|
||||
CKMaterialWrapper(BMAPINSP_O::CKMaterial* material);
|
||||
~CKMaterialWrapper();
|
||||
YYCC_DEFAULT_COPY_MOVE(CKMaterialWrapper)
|
||||
|
||||
public:
|
||||
BMAPINSP_O::CKMaterial* GetMaterial() const;
|
||||
size_t GetHash() const;
|
||||
|
||||
private:
|
||||
BMAPINSP_O::CKMaterial* material;
|
||||
CKMaterialHash hasher;
|
||||
mutable std::optional<size_t> hash;
|
||||
};
|
||||
|
||||
class CKMeshWrapper {
|
||||
public:
|
||||
CKMeshWrapper(BMAPINSP_O::CKMesh* mesh);
|
||||
~CKMeshWrapper();
|
||||
YYCC_DEFAULT_COPY_MOVE(CKMeshWrapper)
|
||||
|
||||
public:
|
||||
BMAPINSP_O::CKMesh* GetMesh() const;
|
||||
size_t GetHash() const;
|
||||
|
||||
private:
|
||||
BMAPINSP_O::CKMesh* mesh;
|
||||
CKMeshHash hasher;
|
||||
mutable std::optional<size_t> hash;
|
||||
};
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region CKObject Wrapper Hash and Equal
|
||||
|
||||
struct CKTextureWrapperHash {
|
||||
[[nodiscard]] size_t operator()(const CKTextureWrapper& tex) const noexcept;
|
||||
};
|
||||
|
||||
struct CKMaterialWrapperHash {
|
||||
[[nodiscard]] size_t operator()(const CKMaterialWrapper& mtl) const noexcept;
|
||||
};
|
||||
|
||||
struct CKMeshWrapperHash {
|
||||
[[nodiscard]] size_t operator()(const CKMeshWrapper& mesh) const noexcept;
|
||||
};
|
||||
|
||||
struct CKTextureWrapperEqualTo {
|
||||
CKTextureEqualTo equal_to;
|
||||
[[nodiscard]] bool operator()(const CKTextureWrapper& lhs, const CKTextureWrapper& rhs) const;
|
||||
};
|
||||
|
||||
struct CKMaterialWrapperEqualTo {
|
||||
CKMaterialEqualTo equal_to;
|
||||
[[nodiscard]] bool operator()(const CKMaterialWrapper& lhs, const CKMaterialWrapper& rhs) const;
|
||||
};
|
||||
|
||||
struct CKMeshWrapperEqualTo {
|
||||
CKMeshEqualTo equal_to;
|
||||
[[nodiscard]] bool operator()(const CKMeshWrapper& lhs, const CKMeshWrapper& rhs) const;
|
||||
};
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Ruleset::Shared::DupCmp
|
||||
3
Ballance/BMapInspector/Ruleset/Shared/Name.cpp
Normal file
3
Ballance/BMapInspector/Ruleset/Shared/Name.cpp
Normal file
@@ -0,0 +1,3 @@
|
||||
#include "Name.hpp"
|
||||
|
||||
namespace BMapInspector::Ruleset::Shared::Name {}
|
||||
227
Ballance/BMapInspector/Ruleset/Shared/Name.hpp
Normal file
227
Ballance/BMapInspector/Ruleset/Shared/Name.hpp
Normal file
@@ -0,0 +1,227 @@
|
||||
#pragma once
|
||||
#include <array>
|
||||
|
||||
namespace BMapInspector::Ruleset::Shared::Name {
|
||||
|
||||
namespace Group {
|
||||
// clang-format off
|
||||
constexpr char8_t PS_LEVELSTART[] = u8"PS_Levelstart";
|
||||
constexpr char8_t PE_LEVELENDE[] = u8"PE_Levelende";
|
||||
constexpr char8_t PC_CHECKPOINTS[] = u8"PC_Checkpoints";
|
||||
constexpr char8_t PR_RESETPOINTS[] = u8"PR_Resetpoints";
|
||||
|
||||
constexpr char8_t PHYS_FLOORS[] = u8"Phys_Floors";
|
||||
constexpr char8_t PHYS_FLOORRAILS[] = u8"Phys_FloorRails";
|
||||
constexpr char8_t PHYS_FLOORSTOPPER[] = u8"Phys_FloorStopper";
|
||||
|
||||
constexpr std::array ALL_PH{
|
||||
u8"P_Extra_Life",
|
||||
u8"P_Extra_Point",
|
||||
u8"P_Trafo_Paper",
|
||||
u8"P_Trafo_Stone",
|
||||
u8"P_Trafo_Wood",
|
||||
u8"P_Ball_Paper",
|
||||
u8"P_Ball_Stone",
|
||||
u8"P_Ball_Wood",
|
||||
u8"P_Box",
|
||||
u8"P_Dome",
|
||||
u8"P_Modul_01",
|
||||
u8"P_Modul_03",
|
||||
u8"P_Modul_08",
|
||||
u8"P_Modul_17",
|
||||
u8"P_Modul_18",
|
||||
u8"P_Modul_19",
|
||||
u8"P_Modul_25",
|
||||
u8"P_Modul_26",
|
||||
u8"P_Modul_29",
|
||||
u8"P_Modul_30",
|
||||
u8"P_Modul_34",
|
||||
u8"P_Modul_37",
|
||||
u8"P_Modul_41",
|
||||
u8"PS_Levelstart",
|
||||
u8"PE_Levelende",
|
||||
u8"PC_Checkpoints",
|
||||
u8"PR_Resetpoints",
|
||||
};
|
||||
|
||||
// clang-format on
|
||||
} // namespace Group
|
||||
|
||||
namespace Texture {
|
||||
// clang-format off
|
||||
constexpr char8_t RAIL_ENVIRONMENT[] = u8"Rail_Environment.bmp";
|
||||
constexpr char8_t LATERNE_VERLAUF[] = u8"Laterne_Verlauf.tga";
|
||||
|
||||
constexpr std::array OPAQUE_TEXS{
|
||||
u8"atari.bmp",
|
||||
u8"Ball_LightningSphere1.bmp",
|
||||
u8"Ball_LightningSphere2.bmp",
|
||||
u8"Ball_LightningSphere3.bmp",
|
||||
u8"Ball_Paper.bmp",
|
||||
u8"Ball_Stone.bmp",
|
||||
u8"Ball_Wood.bmp",
|
||||
u8"Brick.bmp",
|
||||
u8"Column_beige.bmp",
|
||||
u8"Column_blue.bmp",
|
||||
u8"Dome.bmp",
|
||||
u8"DomeEnvironment.bmp",
|
||||
u8"ExtraBall.bmp",
|
||||
u8"ExtraParticle.bmp",
|
||||
u8"E_Holzbeschlag.bmp",
|
||||
u8"FloorGlow.bmp",
|
||||
u8"Floor_Side.bmp",
|
||||
u8"Floor_Top_Border.bmp",
|
||||
u8"Floor_Top_Borderless.bmp",
|
||||
u8"Floor_Top_Checkpoint.bmp",
|
||||
u8"Floor_Top_Flat.bmp",
|
||||
u8"Floor_Top_Profil.bmp",
|
||||
u8"Floor_Top_ProfilFlat.bmp",
|
||||
u8"Gravitylogo_intro.bmp",
|
||||
u8"HardShadow.bmp",
|
||||
u8"Laterne_Glas.bmp",
|
||||
u8"Logo.bmp",
|
||||
u8"Metal_stained.bmp",
|
||||
u8"Misc_Ufo.bmp",
|
||||
u8"Misc_UFO_Flash.bmp",
|
||||
u8"Modul03_Floor.bmp",
|
||||
u8"Modul03_Wall.bmp",
|
||||
u8"Modul11_13_Wood.bmp",
|
||||
u8"Modul11_Wood.bmp",
|
||||
u8"Modul15.bmp",
|
||||
u8"Modul16.bmp",
|
||||
u8"Modul18.bmp",
|
||||
u8"Modul30_d_Seiten.bmp",
|
||||
u8"Particle_Flames.bmp",
|
||||
u8"Particle_Smoke.bmp",
|
||||
u8"PE_Bal_balloons.bmp",
|
||||
u8"PE_Bal_platform.bmp",
|
||||
u8"PE_Ufo_env.bmp",
|
||||
u8"P_Extra_Life_Oil.bmp",
|
||||
u8"P_Extra_Life_Particle.bmp",
|
||||
u8"P_Extra_Life_Shadow.bmp",
|
||||
u8"Rail_Environment.bmp",
|
||||
u8"sandsack.bmp",
|
||||
u8"SkyLayer.bmp",
|
||||
u8"Sky_Vortex.bmp",
|
||||
u8"Stick_Stripes.bmp",
|
||||
u8"Target.bmp",
|
||||
u8"Tower_Roof.bmp",
|
||||
u8"Trafo_Environment.bmp",
|
||||
u8"Trafo_FlashField.bmp",
|
||||
u8"Wood_Metal.bmp",
|
||||
u8"Wood_MetalStripes.bmp",
|
||||
u8"Wood_Misc.bmp",
|
||||
u8"Wood_Nailed.bmp",
|
||||
u8"Wood_Old.bmp",
|
||||
u8"Wood_Panel.bmp",
|
||||
u8"Wood_Plain.bmp",
|
||||
u8"Wood_Plain2.bmp",
|
||||
u8"Wood_Raft.bmp",
|
||||
};
|
||||
constexpr std::array TRANSPARENT_TEXS{
|
||||
u8"Button01_deselect.tga",
|
||||
u8"Button01_select.tga",
|
||||
u8"Button01_special.tga",
|
||||
u8"Column_beige_fade.tga",
|
||||
u8"Cursor.tga",
|
||||
u8"DomeShadow.tga",
|
||||
u8"Font_1.tga",
|
||||
u8"Laterne_Schatten.tga",
|
||||
u8"Laterne_Verlauf.tga",
|
||||
u8"Modul18_Gitter.tga",
|
||||
u8"Pfeil.tga",
|
||||
u8"Stick_Bottom.tga",
|
||||
u8"Trafo_Shadow_Big.tga",
|
||||
u8"Tut_Pfeil01.tga",
|
||||
u8"Tut_Pfeil_Hoch.tga",
|
||||
u8"Wolken_intro.tga",
|
||||
};
|
||||
|
||||
constexpr std::array ALL{
|
||||
// u8"atari.avi",
|
||||
u8"atari.bmp",
|
||||
u8"Ball_LightningSphere1.bmp",
|
||||
u8"Ball_LightningSphere2.bmp",
|
||||
u8"Ball_LightningSphere3.bmp",
|
||||
u8"Ball_Paper.bmp",
|
||||
u8"Ball_Stone.bmp",
|
||||
u8"Ball_Wood.bmp",
|
||||
u8"Brick.bmp",
|
||||
u8"Button01_deselect.tga",
|
||||
u8"Button01_select.tga",
|
||||
u8"Button01_special.tga",
|
||||
u8"Column_beige.bmp",
|
||||
u8"Column_beige_fade.tga",
|
||||
u8"Column_blue.bmp",
|
||||
u8"Cursor.tga",
|
||||
u8"Dome.bmp",
|
||||
u8"DomeEnvironment.bmp",
|
||||
u8"DomeShadow.tga",
|
||||
u8"ExtraBall.bmp",
|
||||
u8"ExtraParticle.bmp",
|
||||
u8"E_Holzbeschlag.bmp",
|
||||
u8"FloorGlow.bmp",
|
||||
u8"Floor_Side.bmp",
|
||||
u8"Floor_Top_Border.bmp",
|
||||
u8"Floor_Top_Borderless.bmp",
|
||||
u8"Floor_Top_Checkpoint.bmp",
|
||||
u8"Floor_Top_Flat.bmp",
|
||||
u8"Floor_Top_Profil.bmp",
|
||||
u8"Floor_Top_ProfilFlat.bmp",
|
||||
u8"Font_1.tga",
|
||||
u8"Gravitylogo_intro.bmp",
|
||||
u8"HardShadow.bmp",
|
||||
u8"Laterne_Glas.bmp",
|
||||
u8"Laterne_Schatten.tga",
|
||||
u8"Laterne_Verlauf.tga",
|
||||
u8"Logo.bmp",
|
||||
u8"Metal_stained.bmp",
|
||||
u8"Misc_Ufo.bmp",
|
||||
u8"Misc_UFO_Flash.bmp",
|
||||
u8"Modul03_Floor.bmp",
|
||||
u8"Modul03_Wall.bmp",
|
||||
u8"Modul11_13_Wood.bmp",
|
||||
u8"Modul11_Wood.bmp",
|
||||
u8"Modul15.bmp",
|
||||
u8"Modul16.bmp",
|
||||
u8"Modul18.bmp",
|
||||
u8"Modul18_Gitter.tga",
|
||||
u8"Modul30_d_Seiten.bmp",
|
||||
u8"Particle_Flames.bmp",
|
||||
u8"Particle_Smoke.bmp",
|
||||
u8"PE_Bal_balloons.bmp",
|
||||
u8"PE_Bal_platform.bmp",
|
||||
u8"PE_Ufo_env.bmp",
|
||||
u8"Pfeil.tga",
|
||||
u8"P_Extra_Life_Oil.bmp",
|
||||
u8"P_Extra_Life_Particle.bmp",
|
||||
u8"P_Extra_Life_Shadow.bmp",
|
||||
u8"Rail_Environment.bmp",
|
||||
u8"sandsack.bmp",
|
||||
u8"SkyLayer.bmp",
|
||||
u8"Sky_Vortex.bmp",
|
||||
u8"Stick_Bottom.tga",
|
||||
u8"Stick_Stripes.bmp",
|
||||
u8"Target.bmp",
|
||||
u8"Tower_Roof.bmp",
|
||||
u8"Trafo_Environment.bmp",
|
||||
u8"Trafo_FlashField.bmp",
|
||||
u8"Trafo_Shadow_Big.tga",
|
||||
u8"Tut_Pfeil01.tga",
|
||||
u8"Tut_Pfeil_Hoch.tga",
|
||||
u8"Wolken_intro.tga",
|
||||
u8"Wood_Metal.bmp",
|
||||
u8"Wood_MetalStripes.bmp",
|
||||
u8"Wood_Misc.bmp",
|
||||
u8"Wood_Nailed.bmp",
|
||||
u8"Wood_Old.bmp",
|
||||
u8"Wood_Panel.bmp",
|
||||
u8"Wood_Plain.bmp",
|
||||
u8"Wood_Plain2.bmp",
|
||||
u8"Wood_Raft.bmp",
|
||||
};
|
||||
|
||||
// clang-format on
|
||||
} // namespace Texture
|
||||
|
||||
} // namespace BMapInspector::Ruleset::Shared::Name
|
||||
31
Ballance/BMapInspector/Ruleset/Shared/Sector.cpp
Normal file
31
Ballance/BMapInspector/Ruleset/Shared/Sector.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#include "Sector.hpp"
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/string/op.hpp>
|
||||
#include <VTAll.hpp>
|
||||
|
||||
namespace strop = yycc::string::op;
|
||||
namespace L = LibCmo;
|
||||
|
||||
namespace BMapInspector::Ruleset::Shared::Sector {
|
||||
|
||||
SectorNameBuilder::SectorNameBuilder() {}
|
||||
|
||||
SectorNameBuilder::~SectorNameBuilder() {}
|
||||
|
||||
SectorName SectorNameBuilder::get_name(L::CKDWORD sector) const {
|
||||
if (sector < MIN_SECTOR || sector > MAX_SECTOR) {
|
||||
throw std::logic_error("invalid sector number");
|
||||
} else {
|
||||
if (sector < 9) {
|
||||
return strop::printf(u8"Sector_%02" PRIuCKDWORD, sector);
|
||||
} else {
|
||||
return strop::printf(u8"Sector_%" PRIuCKDWORD, sector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Sector9Names SectorNameBuilder::get_sector9_names() const {
|
||||
return Sector9Names{.legacy_name = u8"Sector_9", .intuitive_name = u8"Sector_09"};
|
||||
}
|
||||
|
||||
}
|
||||
49
Ballance/BMapInspector/Ruleset/Shared/Sector.hpp
Normal file
49
Ballance/BMapInspector/Ruleset/Shared/Sector.hpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#include <VTAll.hpp>
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/macro/class_copy_move.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace BMapInspector::Ruleset::Shared::Sector {
|
||||
|
||||
constexpr LibCmo::CKDWORD MIN_SECTOR = 1;
|
||||
constexpr LibCmo::CKDWORD MAX_SECTOR = 999;
|
||||
|
||||
/**
|
||||
* @brief The type for sector name.
|
||||
*/
|
||||
using SectorName = std::u8string;
|
||||
|
||||
struct Sector9Names {
|
||||
/** The Sector 9 name with "Sector_9" pattern which is accepted by all 999 sector loader */
|
||||
std::u8string legacy_name;
|
||||
/** The Sector 9 name with "Sector_09" pattern which is only accepted by new 999 sector loader */
|
||||
std::u8string intuitive_name;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The class for building Ballance sector group name.
|
||||
*/
|
||||
class SectorNameBuilder {
|
||||
public:
|
||||
SectorNameBuilder();
|
||||
~SectorNameBuilder();
|
||||
YYCC_DEFAULT_COPY_MOVE(SectorNameBuilder)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Get the sector name.
|
||||
* @param[in] sector The sector index.
|
||||
* @return Sector name.
|
||||
* @remarks
|
||||
* If you deliver sector index with 9, its return name is "Sector_9" which is accepted by all 999 sector loader.
|
||||
*/
|
||||
SectorName get_name(LibCmo::CKDWORD sector) const;
|
||||
/**
|
||||
* @brief Get the special sector 9 names.
|
||||
* @return Special built sector 9 names.
|
||||
*/
|
||||
Sector9Names get_sector9_names() const;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,14 +1,21 @@
|
||||
#include "Shared.hpp"
|
||||
#include "Utility.hpp"
|
||||
#include "Name.hpp"
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/carton/termcolor.hpp>
|
||||
#include <filesystem>
|
||||
#include <stdexcept>
|
||||
#include <cmath>
|
||||
|
||||
namespace strop = yycc::string::op;
|
||||
namespace termcolor = yycc::carton::termcolor;
|
||||
namespace L = LibCmo;
|
||||
namespace C = LibCmo::CK2;
|
||||
namespace O = LibCmo::CK2::ObjImpls;
|
||||
|
||||
namespace BMapInspector::Rule::Shared {
|
||||
namespace BMapInspector::Ruleset::Shared::Utility {
|
||||
|
||||
#pragma region Utilities
|
||||
|
||||
#pragma region Check Functions
|
||||
|
||||
bool FPEqual(L::CKFLOAT lhs, L::CKFLOAT rhs, L::CKFLOAT tolerance) {
|
||||
auto diff = lhs - rhs;
|
||||
@@ -16,6 +23,10 @@ namespace BMapInspector::Rule::Shared {
|
||||
return absolute_diff <= tolerance;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Virtools Stuff
|
||||
|
||||
O::CKGroup* FetchGroup(C::CKContext* ctx, L::CKSTRING name) {
|
||||
return static_cast<O::CKGroup*>(ctx->GetObjectByNameAndClass(name, C::CK_CLASSID::CKCID_GROUP, nullptr));
|
||||
}
|
||||
@@ -29,28 +40,39 @@ namespace BMapInspector::Rule::Shared {
|
||||
}
|
||||
}
|
||||
|
||||
O::CKMaterial* FetchMaterial(C::CKContext* ctx, L::CKSTRING name) {
|
||||
return static_cast<O::CKMaterial*>(ctx->GetObjectByNameAndClass(name, C::CK_CLASSID::CKCID_MATERIAL, nullptr));
|
||||
}
|
||||
|
||||
std::vector<O::CK3dObject*> FetchPhysicalized3dObjects(C::CKContext* ctx) {
|
||||
std::vector<O::CK3dObject*> rv;
|
||||
|
||||
auto* phys_floors = FetchGroup(ctx, GroupNames::PHYS_FLOORS);
|
||||
auto* phys_floors = FetchGroup(ctx, Name::Group::PHYS_FLOORS);
|
||||
if (phys_floors != nullptr) Iter3dObjectsEx(rv, phys_floors);
|
||||
auto* phys_floorrails = FetchGroup(ctx, GroupNames::PHYS_FLOORRAILS);
|
||||
auto* phys_floorrails = FetchGroup(ctx, Name::Group::PHYS_FLOORRAILS);
|
||||
if (phys_floorrails != nullptr) Iter3dObjectsEx(rv, phys_floorrails);
|
||||
auto* phys_floorstopper = FetchGroup(ctx, GroupNames::PHYS_FLOORSTOPPER);
|
||||
auto* phys_floorstopper = FetchGroup(ctx, Name::Group::PHYS_FLOORSTOPPER);
|
||||
if (phys_floorstopper != nullptr) Iter3dObjectsEx(rv, phys_floorstopper);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool CheckTextureFileName(O::CKTexture* tex, L::CKSTRING name) {
|
||||
std::optional<std::u8string> ExtractTextureFileName(O::CKTexture* tex) {
|
||||
// Get file name
|
||||
auto filename = tex->GetUnderlyingData().GetSlotFileName(0);
|
||||
if (filename == nullptr) return false;
|
||||
if (filename == nullptr) return std::nullopt;
|
||||
// Extract file name part
|
||||
std::filesystem::path filepath(filename);
|
||||
auto filename_part = filepath.filename().u8string();
|
||||
return filename_part;
|
||||
}
|
||||
|
||||
bool CheckTextureFileName(O::CKTexture* tex, L::CKSTRING name) {
|
||||
// Get file name part
|
||||
auto filename_part = ExtractTextureFileName(tex);
|
||||
if (!filename_part.has_value()) return false;
|
||||
// Return result.
|
||||
return C::CKStrEqualI(filename_part.c_str(), name);
|
||||
return C::CKStrEqualI(filename_part.value().c_str(), name);
|
||||
}
|
||||
|
||||
std::vector<O::CK3dObject*> Iter3dObjects(O::CKGroup* group) {
|
||||
@@ -70,16 +92,31 @@ namespace BMapInspector::Rule::Shared {
|
||||
return rv;
|
||||
}
|
||||
|
||||
const char8_t* RenderObjectName(O::CKObject* obj) {
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Presentation
|
||||
|
||||
std::u8string QuoteText(const std::u8string_view& words) {
|
||||
std::u8string rv;
|
||||
rv.reserve(words.size() + 2);
|
||||
|
||||
rv.push_back('"');
|
||||
rv.append(words);
|
||||
rv.push_back('"');
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
std::u8string QuoteObjectName(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();
|
||||
return QuoteText(ANONYMOUS);
|
||||
} else {
|
||||
return name;
|
||||
return QuoteText(name);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Rule::Shared
|
||||
} // namespace BMapInspector::Ruleset::Shared::Utility
|
||||
118
Ballance/BMapInspector/Ruleset/Shared/Utility.hpp
Normal file
118
Ballance/BMapInspector/Ruleset/Shared/Utility.hpp
Normal file
@@ -0,0 +1,118 @@
|
||||
#pragma once
|
||||
#include <VTAll.hpp>
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/string/op.hpp>
|
||||
#include <vector>
|
||||
#include <type_traits>
|
||||
|
||||
#define BMAPINSP_L LibCmo
|
||||
#define BMAPINSP_C LibCmo::CK2
|
||||
#define BMAPINSP_O LibCmo::CK2::ObjImpls
|
||||
|
||||
namespace BMapInspector::Ruleset::Shared::Utility {
|
||||
|
||||
#pragma region Utilities
|
||||
|
||||
/**
|
||||
* @brief Check whether given 2 float point values are equal with given tolerance.
|
||||
* @param[in] lhs The left value to compare.
|
||||
* @param[in] rhs The right value to compare.
|
||||
* @param[in] tolerance The tolerance to compare.
|
||||
* @return True if they are equal with given tolerance, otherwise false.
|
||||
*/
|
||||
bool FPEqual(BMAPINSP_L::CKFLOAT lhs, BMAPINSP_L::CKFLOAT rhs, BMAPINSP_L::CKFLOAT tolerance);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Virtools Stuff
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* @param[in] ctx Can not be nullptr.
|
||||
* @param[in] name Can not be nullptr.
|
||||
* @return Found pointer to CKGroup, otherwise nullptr.
|
||||
*/
|
||||
BMAPINSP_O::CKGroup* FetchGroup(BMAPINSP_C::CKContext* ctx, BMAPINSP_L::CKSTRING name);
|
||||
/**
|
||||
* @brief
|
||||
* @param[in] ctx Can not be nullptr.
|
||||
* @param[in] name Can not be nullptr.
|
||||
* @return Found pointer to CKMaterial, otherwise nullptr.
|
||||
*/
|
||||
BMAPINSP_O::CKMaterial* FetchMaterial(BMAPINSP_C::CKContext* ctx, BMAPINSP_L::CKSTRING name);
|
||||
std::vector<BMAPINSP_O::CK3dObject*> FetchPhysicalized3dObjects(BMAPINSP_C::CKContext* ctx);
|
||||
|
||||
/**
|
||||
* @brief Extract the file name part of the texture slot associated file path in given CKTexture.
|
||||
* @param[in] tex The texture for extracting. Can not be nullptr.
|
||||
* @return Extracted file name part or nothing (there is no associated file path).
|
||||
*/
|
||||
std::optional<std::u8string> ExtractTextureFileName(BMAPINSP_O::CKTexture* tex);
|
||||
/**
|
||||
* @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(BMAPINSP_O::CKTexture* tex, BMAPINSP_L::CKSTRING name);
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* @param[in] group Can not be nullptr.
|
||||
* @return All objects is the child class of CK3dEntity.
|
||||
*/
|
||||
std::vector<BMAPINSP_O::CK3dObject*> Iter3dObjects(BMAPINSP_O::CKGroup* group);
|
||||
/**
|
||||
* @brief
|
||||
* @param[in] mesh Can not be nullptr.
|
||||
* @return All nullptr reference are removed.
|
||||
*/
|
||||
std::vector<BMAPINSP_O::CKMaterial*> IterMaterial(BMAPINSP_O::CKMesh* mesh);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Presentation
|
||||
|
||||
/**
|
||||
* @brief Quote given string.
|
||||
* @param[in] words The words to quote.
|
||||
* @return The quoted words.
|
||||
*/
|
||||
std::u8string QuoteText(const std::u8string_view& words);
|
||||
/**
|
||||
* @brief Quote given CKObject's name.
|
||||
* @param[in] obj The CKObject for quoting. Can not be nullptr.
|
||||
* @remarks If there is no name for given CKObject, a colorful name will be quoted and return.
|
||||
* @return The quoted name.
|
||||
*/
|
||||
std::u8string QuoteObjectName(BMAPINSP_O::CKObject* obj);
|
||||
/**
|
||||
* @brief Quote given range of CKObject's names.
|
||||
* @tparam InputIt The type of iterator which iterate non-nullptr pointer to CKObject.
|
||||
* @param[in] first The iterator to the first element.
|
||||
* @param[in] last The iterator to the last element.
|
||||
* @return The quoted names with quote as separator.
|
||||
*/
|
||||
template<std::input_iterator InputIt>
|
||||
requires std::is_pointer_v<std::iter_value_t<InputIt>>
|
||||
&& std::is_base_of_v<BMAPINSP_O::CKObject, std::remove_pointer_t<std::iter_value_t<InputIt>>>
|
||||
std::u8string QuoteObjectNames(InputIt first, InputIt last) {
|
||||
std::u8string cache;
|
||||
return yycc::string::op::join(
|
||||
[&cache, &first, &last]() -> std::optional<std::u8string_view> {
|
||||
if (first == last) return std::nullopt;
|
||||
// YYC MARK:
|
||||
// We must use "cache", otherwise "use after free" will occur.
|
||||
cache = QuoteObjectName(*(first++));
|
||||
return cache;
|
||||
},
|
||||
u8", ");
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Ruleset::Shared::Utility
|
||||
|
||||
#undef BMAPINSP_O
|
||||
#undef BMAPINSP_C
|
||||
#undef BMAPINSP_L
|
||||
374
Ballance/BMapInspector/Ruleset/YYCRules.cpp
Normal file
374
Ballance/BMapInspector/Ruleset/YYCRules.cpp
Normal file
@@ -0,0 +1,374 @@
|
||||
#include "YYCRules.hpp"
|
||||
#include "Shared/Utility.hpp"
|
||||
#include "Shared/Name.hpp"
|
||||
#include "Shared/DupCmp.hpp"
|
||||
#include <yycc.hpp>
|
||||
#include <yycc/string/op.hpp>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <optional>
|
||||
#include <algorithm>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace L = LibCmo;
|
||||
namespace C = LibCmo::CK2;
|
||||
namespace V = LibCmo::VxMath;
|
||||
namespace O = LibCmo::CK2::ObjImpls;
|
||||
namespace strop = yycc::string::op;
|
||||
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
#pragma region YYC Rule 1
|
||||
|
||||
YYCRule1::YYCRule1() : Rule::IRule() {}
|
||||
|
||||
YYCRule1::~YYCRule1() {}
|
||||
|
||||
std::u8string_view YYCRule1::GetRuleName() const {
|
||||
return u8"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::Utility::FetchGroup(ctx, Shared::Name::Group::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_3dobjects = Shared::Utility::Iter3dObjects(phys_floorrails);
|
||||
for (auto* group_3dobject : group_3dobjects) {
|
||||
// Then we iterate their current meshes
|
||||
auto* mesh = group_3dobject->GetCurrentMesh();
|
||||
if (mesh == nullptr) continue;
|
||||
|
||||
// Iterate all meshes
|
||||
auto mtls = Shared::Utility::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::Utility::CheckTextureFileName(texture, Shared::Name::Texture::RAIL_ENVIRONMENT)) {
|
||||
// No, this is not rail texture, throw error.
|
||||
reporter.FormatError(
|
||||
u8"Object %s is grouped into %s, but its texture %s (referred by mesh %s and material %s) seems not the rail texture. "
|
||||
u8"This will cause some parts of this object be smooth unexpectly.",
|
||||
Shared::Utility::QuoteObjectName(group_3dobject).c_str(),
|
||||
Shared::Utility::QuoteText(Shared::Name::Group::PHYS_FLOORRAILS).c_str(),
|
||||
Shared::Utility::QuoteObjectName(texture).c_str(),
|
||||
Shared::Utility::QuoteObjectName(mesh).c_str(),
|
||||
Shared::Utility::QuoteObjectName(mtl).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// 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(u8"Object %s is not grouped into %s, but some objects grouped into %s refer its mesh %s. "
|
||||
u8"This will cause this object be smooth unexpectly.",
|
||||
Shared::Utility::QuoteObjectName(obj).c_str(),
|
||||
Shared::Utility::QuoteText(Shared::Name::Group::PHYS_FLOORRAILS).c_str(),
|
||||
Shared::Utility::QuoteText(Shared::Name::Group::PHYS_FLOORRAILS).c_str(),
|
||||
Shared::Utility::QuoteObjectName(mesh).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region YYC Rule 2
|
||||
|
||||
YYCRule2::YYCRule2() : Rule::IRule() {}
|
||||
|
||||
YYCRule2::~YYCRule2() {}
|
||||
|
||||
std::u8string_view YYCRule2::GetRuleName() const {
|
||||
return u8"YYC2";
|
||||
}
|
||||
|
||||
void YYCRule2::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
auto* ctx = level.GetCKContext();
|
||||
auto physicalized_3dobjects = Shared::Utility::FetchPhysicalized3dObjects(ctx);
|
||||
|
||||
// Iterate all physicalized 3dobject
|
||||
for (auto* physicalized_3dobject : physicalized_3dobjects) {
|
||||
// Get its mesh
|
||||
auto* mesh = physicalized_3dobject->GetCurrentMesh();
|
||||
if (mesh == nullptr) continue;
|
||||
|
||||
// Create a bool vector with vertex count and false init value.
|
||||
std::vector<bool> used_vertex(mesh->GetVertexCount(), false);
|
||||
// Iterate all face and set their vertex as used.
|
||||
auto* face_indices = mesh->GetFaceIndices();
|
||||
for (L::CKDWORD face_idx = 0; face_idx < mesh->GetFaceCount(); ++face_idx) {
|
||||
used_vertex[face_indices[face_idx * 3]] = true;
|
||||
used_vertex[face_indices[face_idx * 3 + 1]] = true;
|
||||
used_vertex[face_indices[face_idx * 3 + 2]] = true;
|
||||
}
|
||||
// Check whether there is unused vertex
|
||||
auto has_unused_vertex = std::any_of(used_vertex.begin(), used_vertex.end(), [](bool v) { return v == false; });
|
||||
// If there is unused vertex, report error
|
||||
if (has_unused_vertex) {
|
||||
reporter.FormatError(u8"Object %s is grouped into physicalization groups, and its referred mesh %s has isolated vertex. "
|
||||
u8"This will cause it can not be physicalized.",
|
||||
Shared::Utility::QuoteObjectName(physicalized_3dobject).c_str(),
|
||||
Shared::Utility::QuoteObjectName(mesh).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region YYC Rule 3
|
||||
|
||||
YYCRule3::YYCRule3() : Rule::IRule() {}
|
||||
|
||||
YYCRule3::~YYCRule3() {}
|
||||
|
||||
std::u8string_view YYCRule3::GetRuleName() const {
|
||||
return u8"YYC3";
|
||||
}
|
||||
|
||||
void YYCRule3::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
// Using utility structs
|
||||
using Shared::DupCmp::CKMaterialWrapper;
|
||||
using Shared::DupCmp::CKMaterialWrapperEqualTo;
|
||||
using Shared::DupCmp::CKMaterialWrapperHash;
|
||||
using Shared::DupCmp::CKMeshWrapper;
|
||||
using Shared::DupCmp::CKMeshWrapperEqualTo;
|
||||
using Shared::DupCmp::CKMeshWrapperHash;
|
||||
using Shared::DupCmp::CKTextureWrapper;
|
||||
using Shared::DupCmp::CKTextureWrapperEqualTo;
|
||||
using Shared::DupCmp::CKTextureWrapperHash;
|
||||
|
||||
// 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(u8"Some textures are visually identical. Please consider merging them to reduce the final map size. "
|
||||
u8"These textures are: %s.",
|
||||
Shared::Utility::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(u8"Some materials are visually identical. Please consider merging them to reduce the final map size. "
|
||||
u8"These materials are: %s.",
|
||||
Shared::Utility::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(u8"Some meshes are visually identical. Please consider merging them to reduce the final map size. "
|
||||
u8"These meshes are: %s.",
|
||||
Shared::Utility::QuoteObjectNames(dup_meshes.begin(), dup_meshes.end()).c_str());
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region YYC Rule 4
|
||||
|
||||
YYCRule4::YYCRule4() : Rule::IRule() {}
|
||||
|
||||
YYCRule4::~YYCRule4() {}
|
||||
|
||||
std::u8string_view YYCRule4::GetRuleName() const {
|
||||
return u8"YYC4";
|
||||
}
|
||||
|
||||
void YYCRule4::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
// Build lowercase texture name set first.
|
||||
std::set<std::u8string> opaque_texs;
|
||||
for (const auto* tex_name : Shared::Name::Texture::OPAQUE_TEXS) {
|
||||
opaque_texs.emplace(strop::to_lower(tex_name));
|
||||
}
|
||||
std::set<std::u8string> transparent_texs;
|
||||
for (const auto* tex_name : Shared::Name::Texture::TRANSPARENT_TEXS) {
|
||||
transparent_texs.emplace(strop::to_lower(tex_name));
|
||||
}
|
||||
|
||||
// Check texture one by one
|
||||
for (auto& tex : level.GetTextures()) {
|
||||
auto tex_filename = Shared::Utility::ExtractTextureFileName(tex);
|
||||
if (!tex_filename.has_value()) continue;
|
||||
|
||||
auto lower_tex_filename = strop::to_lower(tex_filename.value());
|
||||
if (opaque_texs.contains(lower_tex_filename)) {
|
||||
if (tex->GetVideoFormat() != V::VX_PIXELFORMAT::_16_ARGB1555) {
|
||||
reporter.FormatInfo(u8"Texture %s is Ballance opaque texture. But its video format is not ARGB1555. "
|
||||
u8"This is mismatched with vanilla Ballance.",
|
||||
Shared::Utility::QuoteObjectName(tex).c_str());
|
||||
}
|
||||
} else if (transparent_texs.contains(lower_tex_filename)) {
|
||||
if (tex->GetVideoFormat() != V::VX_PIXELFORMAT::_32_ARGB8888) {
|
||||
reporter.FormatInfo(u8"Texture %s is Ballance transparent texture. But its video format is not ARGB8888. "
|
||||
u8"This is mismatched with vanilla Ballance.",
|
||||
Shared::Utility::QuoteObjectName(tex).c_str());
|
||||
}
|
||||
} else {
|
||||
switch (tex->GetVideoFormat()) {
|
||||
case V::VX_PIXELFORMAT::_16_ARGB1555:
|
||||
// Do nothing.
|
||||
break;
|
||||
case V::VX_PIXELFORMAT::_32_ARGB8888:
|
||||
reporter.FormatInfo(u8"Texture %s is not Ballance texture. Its video format is ARGB8888. "
|
||||
u8"This may cause useless performance consumption if there is no transparent inside it. "
|
||||
u8"Please check whether this is essential.",
|
||||
Shared::Utility::QuoteObjectName(tex).c_str());
|
||||
break;
|
||||
default:
|
||||
reporter.FormatInfo(
|
||||
u8"Texture %s is not Ballance texture. Its video format is not ARGB1555 or ARGB8888. "
|
||||
u8"This is mismatched with vanilla Ballance. "
|
||||
u8"Please set it to ARGB1555 for opaque texture, or ARGB8888 for transaprent texture, except special scenario.",
|
||||
Shared::Utility::QuoteObjectName(tex).c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region YYC Rule 5
|
||||
|
||||
YYCRule5::YYCRule5() : Rule::IRule() {}
|
||||
|
||||
YYCRule5::~YYCRule5() {}
|
||||
|
||||
std::u8string_view YYCRule5::GetRuleName() const {
|
||||
return u8"YYC5";
|
||||
}
|
||||
|
||||
void YYCRule5::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
// Build lowercase texture name set first.
|
||||
std::set<std::u8string> texs;
|
||||
for (const auto* tex_name : Shared::Name::Texture::ALL) {
|
||||
texs.emplace(strop::to_lower(tex_name));
|
||||
}
|
||||
|
||||
// Check texture one by one
|
||||
for (auto& tex : level.GetTextures()) {
|
||||
auto tex_filename = Shared::Utility::ExtractTextureFileName(tex);
|
||||
if (!tex_filename.has_value()) continue;
|
||||
|
||||
auto lower_tex_filename = strop::to_lower(tex_filename.value());
|
||||
bool is_ballance_tex = texs.contains(lower_tex_filename);
|
||||
|
||||
using C::CK_TEXTURE_SAVEOPTIONS;
|
||||
switch (tex->GetUnderlyingData().GetSaveOptions()) {
|
||||
case CK_TEXTURE_SAVEOPTIONS::CKTEXTURE_USEGLOBAL:
|
||||
reporter.FormatInfo(u8"The save option of texture %s rely on global Virtools settings. "
|
||||
u8"This cause ambiguity and different behavior on different Virtools by different user settings. "
|
||||
u8"Please consider change it to explicit option, such as External or Raw Data.",
|
||||
Shared::Utility::QuoteObjectName(tex).c_str());
|
||||
break;
|
||||
case CK_TEXTURE_SAVEOPTIONS::CKTEXTURE_EXTERNAL:
|
||||
if (!is_ballance_tex) {
|
||||
reporter.FormatWarning(u8"Texture %s is not Ballance texture, but its save option is External. "
|
||||
u8"This may cause texture loss when rendering in game. Please consider store it inside map.",
|
||||
Shared::Utility::QuoteObjectName(tex).c_str());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (is_ballance_tex) {
|
||||
reporter.FormatInfo(u8"Texture %s is Ballance texture, but its save option is not External. "
|
||||
u8"Please consider storing it as External to reduce the final size of map file, "
|
||||
u8"and let user specified texture pack work.",
|
||||
Shared::Utility::QuoteObjectName(tex).c_str());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region YYC Rule 6
|
||||
|
||||
YYCRule6::YYCRule6() : Rule::IRule() {}
|
||||
|
||||
YYCRule6::~YYCRule6() {}
|
||||
|
||||
std::u8string_view YYCRule6::GetRuleName() const {
|
||||
return u8"YYC6";
|
||||
}
|
||||
|
||||
void YYCRule6::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
// TODO:
|
||||
// This rule is not so essential.
|
||||
// So we are not urgently to implement it in there.
|
||||
// Just make a rule placeholder in there and may finish it in future.
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Ruleset
|
||||
106
Ballance/BMapInspector/Ruleset/YYCRules.hpp
Normal file
106
Ballance/BMapInspector/Ruleset/YYCRules.hpp
Normal file
@@ -0,0 +1,106 @@
|
||||
#pragma once
|
||||
#include "../Rule.hpp"
|
||||
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
/**
|
||||
* @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 Rule::IRule {
|
||||
public:
|
||||
YYCRule1();
|
||||
virtual ~YYCRule1();
|
||||
YYCC_DELETE_COPY_MOVE(YYCRule1)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief YYC12345 Rule 2
|
||||
* @details
|
||||
* The object grouped into physicalization group should not have isolated vertex,
|
||||
* otherwise it will fail to be physicalized.
|
||||
*/
|
||||
class YYCRule2 : public Rule::IRule {
|
||||
public:
|
||||
YYCRule2();
|
||||
virtual ~YYCRule2();
|
||||
YYCC_DELETE_COPY_MOVE(YYCRule2)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief YYC12345 Rule 3
|
||||
* @details
|
||||
* Exactly same mesh, material and texture can be merged.
|
||||
*/
|
||||
class YYCRule3 : public Rule::IRule {
|
||||
public:
|
||||
YYCRule3();
|
||||
virtual ~YYCRule3();
|
||||
YYCC_DELETE_COPY_MOVE(YYCRule3)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief YYC12345 Rule 4
|
||||
* @details
|
||||
* Check the video format for Ballance and user-defined texture respectively.
|
||||
* Report if there is non-vanilla Ballance settings.
|
||||
*/
|
||||
class YYCRule4 : public Rule::IRule {
|
||||
public:
|
||||
YYCRule4();
|
||||
virtual ~YYCRule4();
|
||||
YYCC_DELETE_COPY_MOVE(YYCRule4)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief YYC12345 Rule 5
|
||||
* @details
|
||||
* Check the save options for Ballance and user-defined texture respectively
|
||||
* for reducing map size and avoid ambiguity.
|
||||
*/
|
||||
class YYCRule5 : public Rule::IRule {
|
||||
public:
|
||||
YYCRule5();
|
||||
virtual ~YYCRule5();
|
||||
YYCC_DELETE_COPY_MOVE(YYCRule5)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief YYC12345 Rule 6
|
||||
* @details
|
||||
* Show info hint for the skip of progressbar when loading map.
|
||||
*/
|
||||
class YYCRule6 : public Rule::IRule {
|
||||
public:
|
||||
YYCRule6();
|
||||
virtual ~YYCRule6();
|
||||
YYCC_DELETE_COPY_MOVE(YYCRule6)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
}
|
||||
224
Ballance/BMapInspector/Ruleset/ZZQRules.cpp
Normal file
224
Ballance/BMapInspector/Ruleset/ZZQRules.cpp
Normal file
@@ -0,0 +1,224 @@
|
||||
#include "ZZQRules.hpp"
|
||||
#include "Shared/Utility.hpp"
|
||||
#include "Shared/Name.hpp"
|
||||
#include "Shared/Sector.hpp"
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
|
||||
namespace L = LibCmo;
|
||||
namespace C = LibCmo::CK2;
|
||||
namespace O = LibCmo::CK2::ObjImpls;
|
||||
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
#pragma region ZZQ Rule 1
|
||||
|
||||
ZZQRule1::ZZQRule1() : Rule::IRule() {}
|
||||
|
||||
ZZQRule1::~ZZQRule1() {}
|
||||
|
||||
std::u8string_view ZZQRule1::GetRuleName() const {
|
||||
return u8"ZZQ1";
|
||||
}
|
||||
|
||||
void ZZQRule1::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
auto* ctx = level.GetCKContext();
|
||||
|
||||
// We get "Phys_FloorStopper" group first.
|
||||
auto* phys_floorstopper = Shared::Utility::FetchGroup(ctx, Shared::Name::Group::PHYS_FLOORSTOPPER);
|
||||
if (phys_floorstopper == nullptr) return;
|
||||
// We iterate all object grouped into it.
|
||||
auto group_3dobjects = Shared::Utility::Iter3dObjects(phys_floorstopper);
|
||||
|
||||
// Show the first object if it have.
|
||||
if (!group_3dobjects.empty()) {
|
||||
auto* first_3dobjects = group_3dobjects.front();
|
||||
reporter.FormatInfo(u8"Object %s is the first object grouped into %s. "
|
||||
u8"It is the only stopper which can make sound in game.",
|
||||
Shared::Utility::QuoteObjectName(first_3dobjects).c_str(),
|
||||
Shared::Utility::QuoteText(Shared::Name::Group::PHYS_FLOORSTOPPER).c_str());
|
||||
}
|
||||
|
||||
// Warning for other objects
|
||||
for (size_t i = 1; i < group_3dobjects.size(); ++i) {
|
||||
auto* other_3dobject = group_3dobjects[i];
|
||||
reporter.FormatWarning(u8"Object %s is grouped into %s but it is not the only object. "
|
||||
u8"This will cause it can not make sound in game. Please confirm this is by your intention. "
|
||||
u8"If you want it can make sound, please join it into the first object located in that group.",
|
||||
Shared::Utility::QuoteObjectName(other_3dobject).c_str(),
|
||||
Shared::Utility::QuoteText(Shared::Name::Group::PHYS_FLOORSTOPPER).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region ZZQ Rule 2
|
||||
|
||||
ZZQRule2::ZZQRule2() : Rule::IRule() {}
|
||||
|
||||
ZZQRule2::~ZZQRule2() {}
|
||||
|
||||
std::u8string_view ZZQRule2::GetRuleName() const {
|
||||
return u8"ZZQ2";
|
||||
}
|
||||
|
||||
void ZZQRule2::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
auto* ctx = level.GetCKContext();
|
||||
Shared::Sector::SectorNameBuilder builder;
|
||||
|
||||
// Extract group objects info
|
||||
std::vector<std::set<O::CK3dObject*>> sector_objects;
|
||||
for (L::CKDWORD i = Shared::Sector::MIN_SECTOR; i <= Shared::Sector::MAX_SECTOR; ++i) {
|
||||
// Prepare inserted object set.
|
||||
std::set<O::CK3dObject*> object_set;
|
||||
|
||||
// Build name first with special treat for sector 9
|
||||
// and fill objects into set.
|
||||
if (i != 9) {
|
||||
auto sector_name = builder.get_name(i);
|
||||
auto* sector = Shared::Utility::FetchGroup(ctx, sector_name.c_str());
|
||||
if (sector == nullptr) break;
|
||||
|
||||
auto group_3dobjects = Shared::Utility::Iter3dObjects(sector);
|
||||
for (auto* group_3dobject : group_3dobjects) {
|
||||
object_set.emplace(group_3dobject);
|
||||
}
|
||||
} else {
|
||||
auto sector_names = builder.get_sector9_names();
|
||||
auto* legacy_sector = Shared::Utility::FetchGroup(ctx, sector_names.legacy_name.c_str());
|
||||
auto* intuitive_sector = Shared::Utility::FetchGroup(ctx, sector_names.intuitive_name.c_str());
|
||||
if (legacy_sector == nullptr && intuitive_sector == nullptr) break;
|
||||
|
||||
if (legacy_sector != nullptr) {
|
||||
auto group_3dobjects = Shared::Utility::Iter3dObjects(legacy_sector);
|
||||
for (auto* group_3dobject : group_3dobjects) {
|
||||
object_set.emplace(group_3dobject);
|
||||
}
|
||||
}
|
||||
if (intuitive_sector != nullptr) {
|
||||
auto group_3dobjects = Shared::Utility::Iter3dObjects(intuitive_sector);
|
||||
for (auto* group_3dobject : group_3dobjects) {
|
||||
object_set.emplace(group_3dobject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Insert object set
|
||||
sector_objects.emplace_back(std::move(object_set));
|
||||
}
|
||||
|
||||
// Check the intersection one by one
|
||||
for (size_t i = 0; i < sector_objects.size(); ++i) {
|
||||
for (size_t j = i + 1; j < sector_objects.size(); ++j) {
|
||||
// Fetch 2 set repsectively.
|
||||
const auto& left_sector = sector_objects[i];
|
||||
const auto& right_sector = sector_objects[j];
|
||||
// Check duplicated objects
|
||||
std::vector<O::CK3dObject*> intersection;
|
||||
std::set_intersection(left_sector.begin(),
|
||||
left_sector.end(),
|
||||
right_sector.begin(),
|
||||
right_sector.end(),
|
||||
std::back_inserter(intersection));
|
||||
|
||||
// Output if there is intersection
|
||||
if (!intersection.empty()) {
|
||||
// Get sector index.
|
||||
auto left_sector_idx = static_cast<L::CKDWORD>(i + 1);
|
||||
auto right_sector_idx = static_cast<L::CKDWORD>(j + 1);
|
||||
|
||||
// Output result.
|
||||
reporter.FormatWarning(u8"Some objects are grouped into sector %" PRIuCKDWORD " and sector %" PRIuCKDWORD
|
||||
" represented group bothly. This is not allowed. These objects are: %s.",
|
||||
left_sector_idx,
|
||||
right_sector_idx,
|
||||
Shared::Utility::QuoteObjectNames(intersection.begin(), intersection.end()).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region ZZQ Rule 3
|
||||
|
||||
ZZQRule3::ZZQRule3() : Rule::IRule() {}
|
||||
|
||||
ZZQRule3::~ZZQRule3() {}
|
||||
|
||||
std::u8string_view ZZQRule3::GetRuleName() const {
|
||||
return u8"ZZQ3";
|
||||
}
|
||||
|
||||
void ZZQRule3::Check(Reporter::Reporter& reporter, Map::Level& level) const {
|
||||
auto* ctx = level.GetCKContext();
|
||||
Shared::Sector::SectorNameBuilder builder;
|
||||
|
||||
auto* level_start = Shared::Utility::FetchGroup(ctx, Shared::Name::Group::PS_LEVELSTART);
|
||||
if (level_start == nullptr) {
|
||||
reporter.FormatError(u8"Incomplete level: can not find %s group.",
|
||||
Shared::Utility::QuoteText(Shared::Name::Group::PS_LEVELSTART).c_str());
|
||||
} else {
|
||||
switch (level_start->GetObjectCount()) {
|
||||
case 0:
|
||||
reporter.FormatError(u8"Incomplete level: there is no object grouped into %s group.",
|
||||
Shared::Utility::QuoteText(Shared::Name::Group::PS_LEVELSTART).c_str());
|
||||
break;
|
||||
case 1:
|
||||
// OK. Do nothing.
|
||||
break;
|
||||
default:
|
||||
reporter.FormatError(u8"Bad level: there are more than one objects grouped into %s group.",
|
||||
Shared::Utility::QuoteText(Shared::Name::Group::PS_LEVELSTART).c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto* level_end = Shared::Utility::FetchGroup(ctx, Shared::Name::Group::PE_LEVELENDE);
|
||||
if (level_end == nullptr) {
|
||||
reporter.FormatError(u8"Incomplete level: can not find %s group.",
|
||||
Shared::Utility::QuoteText(Shared::Name::Group::PE_LEVELENDE).c_str());
|
||||
} else {
|
||||
switch (level_end->GetObjectCount()) {
|
||||
case 0:
|
||||
reporter.FormatError(u8"Incomplete level: there is no object grouped into %s group.",
|
||||
Shared::Utility::QuoteText(Shared::Name::Group::PE_LEVELENDE).c_str());
|
||||
break;
|
||||
case 1:
|
||||
// OK. Do nothing.
|
||||
break;
|
||||
default:
|
||||
reporter.FormatError(u8"Bad level: there are more than one objects grouped into %s group.",
|
||||
Shared::Utility::QuoteText(Shared::Name::Group::PE_LEVELENDE).c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto* check_points = Shared::Utility::FetchGroup(ctx, Shared::Name::Group::PC_CHECKPOINTS);
|
||||
if (check_points == nullptr) {
|
||||
reporter.FormatWarning(u8"Can not find %s group. This will cause bad render of particle at the level start point.",
|
||||
Shared::Utility::QuoteText(Shared::Name::Group::PC_CHECKPOINTS).c_str());
|
||||
}
|
||||
|
||||
auto* reset_points = Shared::Utility::FetchGroup(ctx, Shared::Name::Group::PR_RESETPOINTS);
|
||||
if (reset_points == nullptr) {
|
||||
reporter.FormatError(u8"Incomplete level: can not find %s group.",
|
||||
Shared::Utility::QuoteText(Shared::Name::Group::PR_RESETPOINTS).c_str());
|
||||
} else {
|
||||
if (reset_points->GetObjectCount() == 0) {
|
||||
reporter.FormatError(u8"Incomplete level: there is no object grouped into %s group.",
|
||||
Shared::Utility::QuoteText(Shared::Name::Group::PR_RESETPOINTS).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
auto sector1_name = builder.get_name(1);
|
||||
auto* sector1 = Shared::Utility::FetchGroup(ctx, sector1_name.c_str());
|
||||
if (sector1 == nullptr) {
|
||||
reporter.FormatError(u8"Incomplete level: can not find %s group.", Shared::Utility::QuoteText(sector1_name).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
} // namespace BMapInspector::Ruleset
|
||||
64
Ballance/BMapInspector/Ruleset/ZZQRules.hpp
Normal file
64
Ballance/BMapInspector/Ruleset/ZZQRules.hpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
#include "../Rule.hpp"
|
||||
|
||||
namespace BMapInspector::Ruleset {
|
||||
|
||||
/**
|
||||
* @brief ZZQ Rule 1
|
||||
* @details
|
||||
* Only the first object grouped into "Phys_FloorStopper" can make sound in game.
|
||||
* So it would be better to make "Phys_FloorStopper" only have one item.
|
||||
*
|
||||
* At the same time, show which object is the first object in "Phys_FloorStopper"
|
||||
* to know which object can make sound in game,
|
||||
* if mapper require the stopper which can not make sound them by design.
|
||||
*/
|
||||
class ZZQRule1 : public Rule::IRule {
|
||||
public:
|
||||
ZZQRule1();
|
||||
virtual ~ZZQRule1();
|
||||
YYCC_DELETE_COPY_MOVE(ZZQRule1)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief ZZQ Rule 2
|
||||
* @details
|
||||
* The Ballance should only be included only one group.
|
||||
* This rule will check whether there is intersection between different sector group.
|
||||
*/
|
||||
class ZZQRule2 : public Rule::IRule {
|
||||
public:
|
||||
ZZQRule2();
|
||||
virtual ~ZZQRule2();
|
||||
YYCC_DELETE_COPY_MOVE(ZZQRule2)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief ZZQ Rule 3
|
||||
* @details
|
||||
* A minimalist level must contains following items:
|
||||
* \li One start point.
|
||||
* \li One end point (spaceship).
|
||||
* \li One reset point.
|
||||
* \li "Sector_01" group.
|
||||
*/
|
||||
class ZZQRule3 : public Rule::IRule {
|
||||
public:
|
||||
ZZQRule3();
|
||||
virtual ~ZZQRule3();
|
||||
YYCC_DELETE_COPY_MOVE(ZZQRule3)
|
||||
|
||||
public:
|
||||
std::u8string_view GetRuleName() const override;
|
||||
void Check(Reporter::Reporter& reporter, Map::Level& level) const override;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -16,6 +16,11 @@ namespace BMapInspector::Utils {
|
||||
Info = 2,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Parse given string as report level.
|
||||
* @param[in] value The string for parsing.
|
||||
* @return Parsed level or nothing (error occurs).
|
||||
*/
|
||||
std::optional<ReportLevel> ParseReportLevel(const std::u8string_view& value);
|
||||
|
||||
/**
|
||||
|
||||
@@ -50,8 +50,12 @@ It would be okey for you to use any Antlr you like.
|
||||
|
||||
### Passing Dependencies
|
||||
|
||||
When executing Java code relying on these dependencies,
|
||||
you can use `-cp` option of Java runtime to pass the directory where you can find those dependency's JAR files.
|
||||
When compiling or executing Java code relying on these dependencies,
|
||||
you can use `-cp` option to pass these dependency's JAR files.
|
||||
All dependencies should be specified in one `-cp` option with `;` as separator.
|
||||
|
||||
For example, when compiling, you can pass `-cp "<path-to-antlr4-jar>;<path-to-gson-jar>"` as option.
|
||||
Or pass `-cp ".;<path-to-antlr4-jar>;<path-to-gson-jar>"` when executing.
|
||||
|
||||
## Python
|
||||
|
||||
|
||||
@@ -34,3 +34,8 @@ add_custom_target (LibCmoDocuments
|
||||
install (DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html
|
||||
DESTINATION ${NEMO_INSTALL_DOC_PATH}
|
||||
)
|
||||
|
||||
# Install UNVIRT.md documentation
|
||||
install(FILES ${CMAKE_CURRENT_LIST_DIR}/UNVIRT.md
|
||||
DESTINATION ${NEMO_INSTALL_DOC_PATH}
|
||||
)
|
||||
|
||||
168
Docs/UNVIRT.md
Normal file
168
Docs/UNVIRT.md
Normal file
@@ -0,0 +1,168 @@
|
||||
# Unvirt Command Reference
|
||||
|
||||
## File Operations
|
||||
|
||||
### Load File
|
||||
|
||||
Syntax: `load [deep | shallow] <filepath>`
|
||||
|
||||
Description: Load a Virtools composition.
|
||||
|
||||
Arguments:
|
||||
- `[deep | shallow]`: The stage of loading.
|
||||
* `shallow` will load to CKStateChunk stage.
|
||||
* `deep` will load to CKObject stage.
|
||||
- `<filepath>`: The path to loaded file.
|
||||
|
||||
### Unload File
|
||||
|
||||
Syntax: `unload`
|
||||
|
||||
Description: Release loaded Virtools composition.
|
||||
|
||||
### Save File
|
||||
|
||||
Syntax: `save <filepath>`
|
||||
|
||||
Description: Save the loaded file into a new file.
|
||||
|
||||
Arguments:
|
||||
- `<filepath>`: The path to saved file.
|
||||
|
||||
## List & Search
|
||||
|
||||
### Show Header Info
|
||||
|
||||
Syntax: `info`
|
||||
|
||||
Description: Show the header infomation of loaded Virtools composition.
|
||||
|
||||
### List Objects, Managers or Search Result
|
||||
|
||||
Syntax: `ls [obj | mgr | search] <page>`
|
||||
|
||||
Description: List objects, managers of loaded Virtools composition,
|
||||
or the search result of them.
|
||||
|
||||
Arguments:
|
||||
- `[obj | mgr | search]`: Which one you want to list.
|
||||
- `<page>`: The page index. Start with 1.
|
||||
|
||||
### Show Object or Manager Data
|
||||
|
||||
Syntax: `data [obj | mgr] <index>`
|
||||
|
||||
Description: Show the data of specified object or manager.
|
||||
|
||||
Arguments:
|
||||
- `[obj | mgr]`: Which one you want to show data.
|
||||
- `<index>`: The index of object or manager which can be fetched by list command. Start with 0.
|
||||
|
||||
### Show State Chunk Data
|
||||
|
||||
Syntax: `chunk [obj | mgr] <index>`
|
||||
|
||||
Description: Show the specific CKStateChunk data.
|
||||
|
||||
Arguments:
|
||||
- `[obj | mgr]`: Which one you want to show chunk
|
||||
- `<index>`: The index of object or manager which can be fetched by list command. Start with 0.
|
||||
|
||||
### Search Objects or Managers
|
||||
|
||||
Syntax: `search [obj | mgr] [plain | re] <text>`
|
||||
|
||||
Description: Search the name of object or manager= by plain text or regex.
|
||||
Please note that the regex have limited UTF8 support and may cause undefined behavior.
|
||||
|
||||
Arguments:
|
||||
- `[obj | mgr]`: Which one you want to search.
|
||||
- `[plain | re]`: The search mode.
|
||||
* `plain` will search by plain string. It simply check whether name has your given substring.
|
||||
* `re` will do regex search.
|
||||
- `<text>`: The plain text or regex to search.
|
||||
|
||||
## Display
|
||||
|
||||
### Page Item Count
|
||||
|
||||
Syntax: `items <count>`
|
||||
|
||||
Description: Set up how many items should be listed in one page when using 'ls' command.
|
||||
|
||||
Arguments:
|
||||
- `<count>`: The count of items you want to show in one page
|
||||
|
||||
### Display Style
|
||||
|
||||
Syntax: `style [full | simple]`
|
||||
|
||||
Description: Change the detail level of shown data in `ls` command.
|
||||
|
||||
Arguments:
|
||||
- `[full | simple]`: The style of shown content.
|
||||
* `full` will show more detailed information.
|
||||
* `simple` will show less information.
|
||||
|
||||
## Environment
|
||||
|
||||
### Set Encoding
|
||||
|
||||
Syntax: `encoding <enc>`
|
||||
|
||||
Description: Set the encoding series for Virtools composition loading ans saving.
|
||||
|
||||
Arguments:
|
||||
- `<enc>`: CKContext used encoding separated by ','. Support multiple encoding.
|
||||
|
||||
### Temp Path
|
||||
|
||||
Syntax: `temp <temppath>`
|
||||
|
||||
Description: Set the Temp path for CKContext.
|
||||
|
||||
Arguments:
|
||||
- `<temppath>`: The path to Temp folder
|
||||
|
||||
### Data Resource Path
|
||||
|
||||
#### Clear Data Resource Path
|
||||
|
||||
Syntax: `rsc clear`
|
||||
|
||||
Description: Clear all data resources paths.
|
||||
|
||||
#### Add Data Resource Path
|
||||
|
||||
Syntax: `rsc add <datares>`
|
||||
|
||||
Description: Manage data resources paths.
|
||||
|
||||
Arguments:
|
||||
- `<datares>`: The data resources path.
|
||||
|
||||
## System
|
||||
|
||||
### Debug Test
|
||||
|
||||
Syntax: `test`
|
||||
|
||||
Description: Call custom debugging function (only available in Debug mode).
|
||||
|
||||
### Show Version
|
||||
|
||||
Syntax: `version`
|
||||
|
||||
Description: Show the version info about this program.
|
||||
|
||||
### Show Help
|
||||
|
||||
Syntax: `help`
|
||||
|
||||
Description: Output this help page.
|
||||
|
||||
### Exit Program
|
||||
|
||||
Syntax: `exit`
|
||||
|
||||
Description: Exit program.
|
||||
@@ -343,6 +343,10 @@ namespace LibCmo::CK2::ObjImpls {
|
||||
return m_ImageHost;
|
||||
}
|
||||
|
||||
const CKBitmapData& CKTexture::GetUnderlyingData() const {
|
||||
return m_ImageHost;
|
||||
}
|
||||
|
||||
bool CKTexture::LoadImage(CKSTRING filename, CKDWORD slot) {
|
||||
// check file name
|
||||
if (filename == nullptr) return false;
|
||||
|
||||
@@ -20,6 +20,7 @@ namespace LibCmo::CK2::ObjImpls {
|
||||
virtual bool Load(CKStateChunk* chunk, CKFileVisitor* file) override;
|
||||
|
||||
CKBitmapData& GetUnderlyingData();
|
||||
const CKBitmapData& GetUnderlyingData() const;
|
||||
|
||||
/**
|
||||
* @brief A wrapper of underlying CKBitmapData::LoadImage. Not only load image, but also set file name.
|
||||
|
||||
@@ -538,6 +538,121 @@ namespace LibCmo::VxMath {
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region VxImageDescEx
|
||||
|
||||
VxImageDescEx::VxImageDescEx() : m_Width(0), m_Height(0), m_Image(nullptr) {}
|
||||
|
||||
VxImageDescEx::VxImageDescEx(CKDWORD width, CKDWORD height) : m_Width(width), m_Height(height), m_Image(nullptr) {
|
||||
CreateImage(width, height);
|
||||
}
|
||||
|
||||
YYCC_IMPL_COPY_CTOR(VxImageDescEx, rhs) : m_Width(rhs.m_Width), m_Height(rhs.m_Height), m_Image(nullptr) {
|
||||
// copy image
|
||||
if (rhs.m_Image != nullptr) {
|
||||
CreateImage(rhs.m_Width, rhs.m_Height, rhs.m_Image);
|
||||
}
|
||||
}
|
||||
|
||||
YYCC_IMPL_COPY_OPER(VxImageDescEx, rhs) {
|
||||
FreeImage();
|
||||
|
||||
m_Width = rhs.m_Width;
|
||||
m_Height = rhs.m_Height;
|
||||
if (rhs.m_Image != nullptr) {
|
||||
CreateImage(rhs.m_Width, rhs.m_Height, rhs.m_Image);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
YYCC_IMPL_MOVE_CTOR(VxImageDescEx, rhs) : m_Width(rhs.m_Width), m_Height(rhs.m_Height), m_Image(rhs.m_Image) {
|
||||
// move image
|
||||
rhs.m_Height = 0;
|
||||
rhs.m_Width = 0;
|
||||
rhs.m_Image = nullptr;
|
||||
}
|
||||
|
||||
YYCC_IMPL_MOVE_OPER(VxImageDescEx, rhs) {
|
||||
FreeImage();
|
||||
|
||||
m_Height = rhs.m_Height;
|
||||
m_Width = rhs.m_Width;
|
||||
m_Image = rhs.m_Image;
|
||||
rhs.m_Height = 0;
|
||||
rhs.m_Width = 0;
|
||||
rhs.m_Image = nullptr;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
VxImageDescEx::~VxImageDescEx() {
|
||||
FreeImage();
|
||||
}
|
||||
|
||||
void VxImageDescEx::CreateImage(CKDWORD Width, CKDWORD Height) {
|
||||
FreeImage();
|
||||
m_Width = Width;
|
||||
m_Height = Height;
|
||||
m_Image = new CKBYTE[GetImageSize()];
|
||||
}
|
||||
|
||||
void VxImageDescEx::CreateImage(CKDWORD Width, CKDWORD Height, const void* dataptr) {
|
||||
CreateImage(Width, Height);
|
||||
std::memcpy(m_Image, dataptr, GetImageSize());
|
||||
}
|
||||
|
||||
void VxImageDescEx::FreeImage() {
|
||||
m_Width = 0;
|
||||
m_Height = 0;
|
||||
if (m_Image != nullptr) {
|
||||
delete[] m_Image;
|
||||
m_Image = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
CKDWORD VxImageDescEx::GetImageSize() const {
|
||||
return static_cast<CKDWORD>(PIXEL_SIZE * m_Width * m_Height);
|
||||
}
|
||||
|
||||
const CKBYTE* VxImageDescEx::GetImage() const { return m_Image; }
|
||||
|
||||
CKBYTE* VxImageDescEx::GetMutableImage() { return m_Image; }
|
||||
|
||||
CKDWORD VxImageDescEx::GetPixelCount() const {
|
||||
return static_cast<CKDWORD>(m_Width * m_Height);
|
||||
}
|
||||
|
||||
const CKDWORD* VxImageDescEx::GetPixels() const {
|
||||
return reinterpret_cast<CKDWORD*>(m_Image);
|
||||
}
|
||||
|
||||
CKDWORD* VxImageDescEx::GetMutablePixels() {
|
||||
return reinterpret_cast<CKDWORD*>(m_Image);
|
||||
}
|
||||
|
||||
CKDWORD VxImageDescEx::GetWidth() const { return m_Width; }
|
||||
|
||||
CKDWORD VxImageDescEx::GetHeight() const { return m_Height; }
|
||||
|
||||
bool VxImageDescEx::IsValid() const {
|
||||
return (m_Width != 0u && m_Height != 0u && m_Image != nullptr);
|
||||
}
|
||||
|
||||
bool VxImageDescEx::IsHWEqual(const VxImageDescEx& rhs) const {
|
||||
return (m_Width == rhs.m_Width && m_Height == rhs.m_Height);
|
||||
}
|
||||
|
||||
// bool VxImageDescEx::IsMaskEqual(const VxImageDescEx& rhs) const {
|
||||
// return (
|
||||
// m_RedMask == rhs.m_RedMask &&
|
||||
// m_GreenMask == rhs.m_GreenMask &&
|
||||
// m_BlueMask == rhs.m_BlueMask &&
|
||||
// m_AlphaMask == rhs.m_AlphaMask
|
||||
// );
|
||||
// }
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Patched
|
||||
|
||||
namespace NSVxVector {
|
||||
|
||||
@@ -341,53 +341,20 @@ namespace LibCmo::VxMath {
|
||||
public:
|
||||
static constexpr CKDWORD FACTOR_SIZE = 1u; /**< Single color factor (one of ARGB) occpied size in byte. */
|
||||
static constexpr CKDWORD PIXEL_SIZE = FACTOR_SIZE * 4u; /**< Single pixel occpied size in byte. */
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Create a blank (invalid) image.
|
||||
*/
|
||||
VxImageDescEx() : m_Width(0), m_Height(0), m_Image(nullptr) {}
|
||||
VxImageDescEx();
|
||||
/**
|
||||
* @brief Create a image with given width and height.
|
||||
* @param[in] width The width of image.
|
||||
* @param[in] height The height of image.
|
||||
*/
|
||||
VxImageDescEx(CKDWORD width, CKDWORD height) : m_Width(width), m_Height(height), m_Image(nullptr) { CreateImage(width, height); }
|
||||
VxImageDescEx(const VxImageDescEx& rhs) : m_Width(rhs.m_Width), m_Height(rhs.m_Height), m_Image(nullptr) {
|
||||
// copy image
|
||||
if (rhs.m_Image != nullptr) {
|
||||
CreateImage(rhs.m_Width, rhs.m_Height, rhs.m_Image);
|
||||
}
|
||||
}
|
||||
VxImageDescEx(VxImageDescEx&& rhs) noexcept : m_Width(rhs.m_Width), m_Height(rhs.m_Height), m_Image(rhs.m_Image) {
|
||||
// move image
|
||||
rhs.m_Height = 0;
|
||||
rhs.m_Width = 0;
|
||||
rhs.m_Image = nullptr;
|
||||
}
|
||||
VxImageDescEx& operator=(const VxImageDescEx& rhs) {
|
||||
FreeImage();
|
||||
|
||||
m_Width = rhs.m_Width;
|
||||
m_Height = rhs.m_Height;
|
||||
if (rhs.m_Image != nullptr) {
|
||||
CreateImage(rhs.m_Width, rhs.m_Height, rhs.m_Image);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
VxImageDescEx& operator=(VxImageDescEx&& rhs) noexcept {
|
||||
FreeImage();
|
||||
|
||||
m_Height = rhs.m_Height;
|
||||
m_Width = rhs.m_Width;
|
||||
m_Image = rhs.m_Image;
|
||||
rhs.m_Height = 0;
|
||||
rhs.m_Width = 0;
|
||||
rhs.m_Image = nullptr;
|
||||
|
||||
return *this;
|
||||
}
|
||||
~VxImageDescEx() { FreeImage(); }
|
||||
VxImageDescEx(CKDWORD width, CKDWORD height);
|
||||
~VxImageDescEx();
|
||||
YYCC_DECL_COPY_MOVE(VxImageDescEx)
|
||||
|
||||
/**
|
||||
* @brief Create image with given width and height
|
||||
@@ -397,12 +364,7 @@ namespace LibCmo::VxMath {
|
||||
* \li There is no initialization (fill with zero) for this new created image.
|
||||
* \li Old image will be free first before creating.
|
||||
*/
|
||||
void CreateImage(CKDWORD Width, CKDWORD Height) {
|
||||
FreeImage();
|
||||
m_Width = Width;
|
||||
m_Height = Height;
|
||||
m_Image = new CKBYTE[GetImageSize()];
|
||||
}
|
||||
void CreateImage(CKDWORD Width, CKDWORD Height);
|
||||
/**
|
||||
* @brief Create image with given width, height and data.
|
||||
* @param[in] Width The width of image.
|
||||
@@ -413,86 +375,69 @@ namespace LibCmo::VxMath {
|
||||
* an undefined behavior is raised.
|
||||
* @remarks Old image will be free first before creating.
|
||||
*/
|
||||
void CreateImage(CKDWORD Width, CKDWORD Height, const void* dataptr) {
|
||||
CreateImage(Width, Height);
|
||||
std::memcpy(m_Image, dataptr, GetImageSize());
|
||||
}
|
||||
void CreateImage(CKDWORD Width, CKDWORD Height, const void* dataptr);
|
||||
/**
|
||||
* @brief Free current image. Reset this to invalid status.
|
||||
*/
|
||||
void FreeImage() {
|
||||
m_Width = 0;
|
||||
m_Height = 0;
|
||||
if (m_Image != nullptr) {
|
||||
delete[] m_Image;
|
||||
m_Image = nullptr;
|
||||
}
|
||||
}
|
||||
void FreeImage();
|
||||
|
||||
/**
|
||||
* @brief Get the allocated memory size of image.
|
||||
* @return The allocated memory size of image.
|
||||
* Basically it is image width * height * (single pixel size).
|
||||
*/
|
||||
CKDWORD GetImageSize() const { return static_cast<CKDWORD>(PIXEL_SIZE * m_Width * m_Height); }
|
||||
CKDWORD GetImageSize() const;
|
||||
/**
|
||||
* @brief Get a constant pointer to image in memory unit for viewing.
|
||||
* @return A constant pointer to image in memory unit.
|
||||
*/
|
||||
const CKBYTE* GetImage() const { return m_Image; }
|
||||
const CKBYTE* GetImage() const;
|
||||
/**
|
||||
* @brief Get a mutable pointer to image in memory unit for modifying.
|
||||
* @return A mutable pointer to image in memory uint.
|
||||
*/
|
||||
CKBYTE* GetMutableImage() { return m_Image; }
|
||||
CKBYTE* GetMutableImage();
|
||||
|
||||
/**
|
||||
* @brief Get the full count of pixel in image.
|
||||
* @return The count of image. Basically it is image width * height.
|
||||
*/
|
||||
CKDWORD GetPixelCount() const { return static_cast<CKDWORD>(m_Width * m_Height); }
|
||||
CKDWORD GetPixelCount() const;
|
||||
/**
|
||||
* @brief Get a constant pointer to image in pixel unit for viewing.
|
||||
* @return A constant pointer to image in pixel unit.
|
||||
*/
|
||||
const CKDWORD* GetPixels() const { return reinterpret_cast<CKDWORD*>(m_Image); }
|
||||
const CKDWORD* GetPixels() const;
|
||||
/**
|
||||
* @brief Get a mutable pointer to image in pixel uint for modifying.
|
||||
* @return A mutable pointer to image in pixel uint.
|
||||
*/
|
||||
CKDWORD* GetMutablePixels() { return reinterpret_cast<CKDWORD*>(m_Image); }
|
||||
CKDWORD* GetMutablePixels();
|
||||
|
||||
/**
|
||||
* @brief Get the width of this image in pixel.
|
||||
* @return The width of this image in pixel.
|
||||
*/
|
||||
CKDWORD GetWidth() const { return m_Width; }
|
||||
CKDWORD GetWidth() const;
|
||||
/**
|
||||
* @brief Get the height of this image in pixel.
|
||||
* @return The height of this image in pixel.
|
||||
*/
|
||||
CKDWORD GetHeight() const { return m_Height; }
|
||||
CKDWORD GetHeight() const;
|
||||
|
||||
/**
|
||||
* @brief Check whether this image is valid image for using.
|
||||
* @details If one of width and height is zero, or underlying image pointer, this image is invalid.
|
||||
* @return True if it is, otherwise false.
|
||||
*/
|
||||
bool IsValid() const { return (m_Width != 0u && m_Height != 0u && m_Image != nullptr); }
|
||||
bool IsValid() const;
|
||||
/**
|
||||
* @brief Check whether the width and height of this image are equal to another image.
|
||||
* @param[in] rhs Another image for comparing.
|
||||
* @return True if their width and height are equal, otherwise false.
|
||||
*/
|
||||
bool IsHWEqual(const VxImageDescEx& rhs) const { return (m_Width == rhs.m_Width && m_Height == rhs.m_Height); }
|
||||
// bool IsMaskEqual(const VxImageDescEx& rhs) const {
|
||||
// return (
|
||||
// m_RedMask == rhs.m_RedMask &&
|
||||
// m_GreenMask == rhs.m_GreenMask &&
|
||||
// m_BlueMask == rhs.m_BlueMask &&
|
||||
// m_AlphaMask == rhs.m_AlphaMask
|
||||
// );
|
||||
// }
|
||||
bool IsHWEqual(const VxImageDescEx& rhs) const;
|
||||
// bool IsMaskEqual(const VxImageDescEx& rhs) const;
|
||||
|
||||
//public:
|
||||
// CKDWORD m_RedMask;
|
||||
|
||||
Reference in New Issue
Block a user