aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock2307
-rw-r--r--adapters/fparkan-render-vulkan/src/asset_mesh.rs29
-rw-r--r--adapters/fparkan-render-vulkan/src/ffi/smoke.rs14
-rw-r--r--adapters/fparkan-render-vulkan/src/ffi/smoke_types.rs34
-rw-r--r--apps/fparkan-game/src/main.rs2
-rw-r--r--crates/fparkan-material/src/lib.rs57
-rw-r--r--crates/fparkan-texm/src/lib.rs19
-rw-r--r--docs/reference/original-binaries.md15
-rw-r--r--docs/reference/render-frame.md329
-rw-r--r--docs/tomes/05-render.md45
11 files changed, 2632 insertions, 220 deletions
diff --git a/.gitignore b/.gitignore
index 3aeec0a..96cf9a4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,7 +2,6 @@
/target/
**/*.rs.bk
*.pdb
-Cargo.lock
# Local licensed game data and research output
/testdata/*
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..e9b31b9
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,2307 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "adler2"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
+
+[[package]]
+name = "alsa"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "812947049edcd670a82cd5c73c3661d2e58468577ba8489de58e1a73c04cbd5d"
+dependencies = [
+ "alsa-sys",
+ "bitflags 2.13.2",
+ "cfg-if",
+ "libc",
+]
+
+[[package]]
+name = "alsa-sys"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad7569085a265dd3f607ebecce7458eaab2132a84393534c95b18dcbc3f31e04"
+dependencies = [
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "android-activity"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f2a1bb052857d5dd49572219344a7332b31b76405648eabac5bc68978251bcd"
+dependencies = [
+ "android-properties",
+ "bitflags 2.13.2",
+ "cc",
+ "jni 0.22.4",
+ "libc",
+ "log",
+ "ndk",
+ "ndk-context",
+ "ndk-sys",
+ "num_enum",
+ "thiserror 2.0.21",
+]
+
+[[package]]
+name = "android-properties"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04"
+
+[[package]]
+name = "arrayvec"
+version = "0.7.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56"
+
+[[package]]
+name = "ash"
+version = "0.38.0+1.3.281"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f"
+dependencies = [
+ "libloading",
+]
+
+[[package]]
+name = "ash-window"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52bca67b61cb81e5553babde81b8211f713cb6db79766f80168f3e5f40ea6c82"
+dependencies = [
+ "ash",
+ "raw-window-handle",
+ "raw-window-metal",
+]
+
+[[package]]
+name = "atomic-waker"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
+
+[[package]]
+name = "autocfg"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06"
+
+[[package]]
+name = "block"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
+
+[[package]]
+name = "block2"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f"
+dependencies = [
+ "objc2 0.5.3",
+]
+
+[[package]]
+name = "block2"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5"
+dependencies = [
+ "objc2 0.6.5",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.20.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
+
+[[package]]
+name = "bytemuck"
+version = "1.25.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797"
+
+[[package]]
+name = "bytes"
+version = "1.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
+
+[[package]]
+name = "calloop"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec"
+dependencies = [
+ "bitflags 2.13.2",
+ "log",
+ "polling",
+ "rustix 0.38.44",
+ "slab",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "cc"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50a649af8a827553c29fb0cb4bd4a6f1a0dd695bd3232b9bc98bd9c8a3ffbb8b"
+dependencies = [
+ "find-msvc-tools",
+ "jobserver",
+ "libc",
+ "shlex",
+]
+
+[[package]]
+name = "cesu8"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600"
+
+[[package]]
+name = "cfg_aliases"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527"
+
+[[package]]
+name = "cocoa"
+version = "0.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c"
+dependencies = [
+ "bitflags 1.3.2",
+ "block",
+ "cocoa-foundation",
+ "core-foundation",
+ "core-graphics",
+ "foreign-types",
+ "libc",
+ "objc",
+]
+
+[[package]]
+name = "cocoa-foundation"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7"
+dependencies = [
+ "bitflags 1.3.2",
+ "block",
+ "core-foundation",
+ "core-graphics-types",
+ "libc",
+ "objc",
+]
+
+[[package]]
+name = "combine"
+version = "4.6.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfc320937d09e6de266b31b9afb480f197d7a861be86be7cb2ea7e5d1bfffc5e"
+dependencies = [
+ "bytes",
+ "memchr",
+]
+
+[[package]]
+name = "concurrent-queue"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "core-foundation"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
+[[package]]
+name = "core-graphics"
+version = "0.23.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "core-graphics-types",
+ "foreign-types",
+ "libc",
+]
+
+[[package]]
+name = "core-graphics-types"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "libc",
+]
+
+[[package]]
+name = "core_detect"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f8f80099a98041a3d1622845c271458a2d73e688351bf3cb999266764b81d48"
+
+[[package]]
+name = "coreaudio-rs"
+version = "0.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d5d7dca3ebcf65a035582c9ad4385371a9d9ee6537474d2a278f4e1e475bb58"
+dependencies = [
+ "bitflags 2.13.2",
+ "libc",
+ "objc2-audio-toolbox",
+ "objc2-core-audio",
+ "objc2-core-audio-types",
+ "objc2-core-foundation",
+]
+
+[[package]]
+name = "cpal"
+version = "0.17.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8942da362c0f0d895d7cac616263f2f9424edc5687364dfd1d25ef7eba506d7"
+dependencies = [
+ "alsa",
+ "coreaudio-rs",
+ "dasp_sample",
+ "jni 0.21.1",
+ "js-sys",
+ "libc",
+ "mach2",
+ "ndk",
+ "ndk-context",
+ "num-derive",
+ "num-traits",
+ "objc2 0.6.5",
+ "objc2-audio-toolbox",
+ "objc2-avf-audio",
+ "objc2-core-audio",
+ "objc2-core-audio-types",
+ "objc2-core-foundation",
+ "objc2-foundation 0.3.2",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "windows",
+]
+
+[[package]]
+name = "crc32fast"
+version = "1.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01a7799fd6b852db0e61728dde9a204c423b44d689dbd432522543614b490e78"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6"
+
+[[package]]
+name = "cursor-icon"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f"
+
+[[package]]
+name = "dasp_sample"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f"
+
+[[package]]
+name = "dispatch"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
+
+[[package]]
+name = "dispatch2"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38"
+dependencies = [
+ "bitflags 2.13.2",
+ "objc2 0.6.5",
+]
+
+[[package]]
+name = "dlib"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a"
+dependencies = [
+ "libloading",
+]
+
+[[package]]
+name = "dpi"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76"
+
+[[package]]
+name = "encoding_rs"
+version = "0.8.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e985e0451871ad22fb8d2b6b076e2028a502a0d3950998c2c5c0a4f9b5d9679"
+dependencies = [
+ "cfg-if",
+ "core_detect",
+ "multiversion_no_op",
+ "rustversion",
+ "scopeguard",
+ "simdutf8",
+]
+
+[[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 0.61.2",
+]
+
+[[package]]
+name = "extended"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af9673d8203fcb076b19dfd17e38b3d4ae9f44959416ea532ce72415a6020365"
+
+[[package]]
+name = "find-msvc-tools"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aedcfb3409746eddb02b9e19ebda1c3394f759a152e48ee875a0844d1b955484"
+
+[[package]]
+name = "flate2"
+version = "1.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "foreign-types"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
+dependencies = [
+ "foreign-types-macros",
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-macros"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea5190182e6915eb873ddbc16e23b711b6eb1f9c00a0d0a3a91b5f6228475225"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 3.0.7",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
+
+[[package]]
+name = "fparkan-animation"
+version = "0.1.0"
+
+[[package]]
+name = "fparkan-assets"
+version = "0.1.0"
+dependencies = [
+ "fparkan-material",
+ "fparkan-mission-format",
+ "fparkan-msh",
+ "fparkan-nres",
+ "fparkan-path",
+ "fparkan-prototype",
+ "fparkan-resource",
+ "fparkan-terrain",
+ "fparkan-terrain-format",
+ "fparkan-texm",
+ "fparkan-vfs",
+]
+
+[[package]]
+name = "fparkan-binary"
+version = "0.1.0"
+
+[[package]]
+name = "fparkan-fx"
+version = "0.1.0"
+dependencies = [
+ "fparkan-binary",
+ "fparkan-nres",
+]
+
+[[package]]
+name = "fparkan-game"
+version = "0.1.0"
+dependencies = [
+ "fparkan-animation",
+ "fparkan-assets",
+ "fparkan-fx",
+ "fparkan-msh",
+ "fparkan-path",
+ "fparkan-platform-winit",
+ "fparkan-prototype",
+ "fparkan-render",
+ "fparkan-render-vulkan",
+ "fparkan-resource",
+ "fparkan-runtime",
+ "fparkan-terrain",
+ "fparkan-terrain-format",
+ "fparkan-vfs",
+ "rodio",
+ "serde",
+ "serde_json",
+ "winit",
+]
+
+[[package]]
+name = "fparkan-material"
+version = "0.1.0"
+dependencies = [
+ "encoding_rs",
+ "fparkan-nres",
+ "fparkan-path",
+ "fparkan-resource",
+ "fparkan-vfs",
+]
+
+[[package]]
+name = "fparkan-mission-format"
+version = "0.1.0"
+dependencies = [
+ "encoding_rs",
+ "fparkan-binary",
+]
+
+[[package]]
+name = "fparkan-msh"
+version = "0.1.0"
+dependencies = [
+ "encoding_rs",
+ "fparkan-animation",
+ "fparkan-nres",
+]
+
+[[package]]
+name = "fparkan-nres"
+version = "0.1.0"
+dependencies = [
+ "fparkan-binary",
+ "fparkan-path",
+]
+
+[[package]]
+name = "fparkan-path"
+version = "0.1.0"
+
+[[package]]
+name = "fparkan-platform"
+version = "0.1.0"
+dependencies = [
+ "raw-window-handle",
+]
+
+[[package]]
+name = "fparkan-platform-winit"
+version = "0.1.0"
+dependencies = [
+ "fparkan-platform",
+ "raw-window-handle",
+ "winit",
+]
+
+[[package]]
+name = "fparkan-prototype"
+version = "0.1.0"
+dependencies = [
+ "encoding_rs",
+ "fparkan-binary",
+ "fparkan-nres",
+ "fparkan-path",
+ "fparkan-resource",
+ "fparkan-vfs",
+]
+
+[[package]]
+name = "fparkan-render"
+version = "0.1.0"
+
+[[package]]
+name = "fparkan-render-vulkan"
+version = "0.1.0"
+dependencies = [
+ "ash",
+ "ash-window",
+ "fparkan-animation",
+ "fparkan-binary",
+ "fparkan-msh",
+ "fparkan-platform",
+ "fparkan-render",
+ "fparkan-terrain-format",
+]
+
+[[package]]
+name = "fparkan-resource"
+version = "0.1.0"
+dependencies = [
+ "fparkan-binary",
+ "fparkan-nres",
+ "fparkan-path",
+ "fparkan-rsli",
+ "fparkan-vfs",
+]
+
+[[package]]
+name = "fparkan-rsli"
+version = "0.1.0"
+dependencies = [
+ "flate2",
+ "fparkan-binary",
+]
+
+[[package]]
+name = "fparkan-runtime"
+version = "0.1.0"
+dependencies = [
+ "fparkan-assets",
+ "fparkan-path",
+ "fparkan-platform",
+ "fparkan-prototype",
+ "fparkan-render",
+ "fparkan-resource",
+ "fparkan-script",
+ "fparkan-terrain",
+ "fparkan-vfs",
+ "fparkan-world",
+]
+
+[[package]]
+name = "fparkan-script"
+version = "0.1.0"
+dependencies = [
+ "fparkan-binary",
+]
+
+[[package]]
+name = "fparkan-terrain"
+version = "0.1.0"
+dependencies = [
+ "fparkan-nres",
+ "fparkan-terrain-format",
+]
+
+[[package]]
+name = "fparkan-terrain-format"
+version = "0.1.0"
+dependencies = [
+ "fparkan-binary",
+ "fparkan-nres",
+]
+
+[[package]]
+name = "fparkan-texm"
+version = "0.1.0"
+dependencies = [
+ "fparkan-nres",
+]
+
+[[package]]
+name = "fparkan-vfs"
+version = "0.1.0"
+dependencies = [
+ "fparkan-binary",
+ "fparkan-path",
+]
+
+[[package]]
+name = "fparkan-world"
+version = "0.1.0"
+dependencies = [
+ "fparkan-binary",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e"
+
+[[package]]
+name = "futures-task"
+version = "0.3.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd"
+
+[[package]]
+name = "futures-util"
+version = "0.3.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "pin-project-lite",
+ "slab",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
+
+[[package]]
+name = "hermit-abi"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e17592d60ebacc7d5e169f4663c5f84f9161cc90328abcfe8456f41e4dfcb284"
+
+[[package]]
+name = "indexmap"
+version = "2.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
+
+[[package]]
+name = "jni"
+version = "0.21.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
+dependencies = [
+ "cesu8",
+ "cfg-if",
+ "combine",
+ "jni-sys 0.3.1",
+ "log",
+ "thiserror 1.0.69",
+ "walkdir",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "jni"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498"
+dependencies = [
+ "cfg-if",
+ "combine",
+ "jni-macros",
+ "jni-sys 0.4.1",
+ "log",
+ "simd_cesu8",
+ "thiserror 2.0.21",
+ "walkdir",
+ "windows-link",
+]
+
+[[package]]
+name = "jni-macros"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "rustc_version",
+ "simd_cesu8",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "jni-sys"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258"
+dependencies = [
+ "jni-sys 0.4.1",
+]
+
+[[package]]
+name = "jni-sys"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2"
+dependencies = [
+ "jni-sys-macros",
+]
+
+[[package]]
+name = "jni-sys-macros"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264"
+dependencies = [
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "jobserver"
+version = "0.1.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3"
+dependencies = [
+ "getrandom",
+ "libc",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.106"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7883d941dae510fb2d978fc3fe018c71c9e2892fd38854de3e8b92c2e5ad9cc5"
+dependencies = [
+ "cfg-if",
+ "futures-util",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20870f649af7073d53e38067b2a84312175d56ea15217e1b15bc83506ec50afb"
+
+[[package]]
+name = "libc"
+version = "0.2.190"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce5d3ddc6d3fa000eb1536d85e147bfe31aacaba692ed6a876f95cb7c855be78"
+
+[[package]]
+name = "libloading"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
+dependencies = [
+ "cfg-if",
+ "windows-link",
+]
+
+[[package]]
+name = "libredox"
+version = "0.1.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61ff90caf6077a803a240f62fdbe88645a890bbca49ef8174c3cb0404362171d"
+dependencies = [
+ "bitflags 2.13.2",
+ "libc",
+ "plain",
+ "redox_syscall 0.9.4",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
+
+[[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.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"
+
+[[package]]
+name = "mach2"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a1b95cd5421ec55b445b5ae102f5ea0e768de1f82bd3001e11f426c269c3aea"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "malloc_buf"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "memchr"
+version = "2.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c"
+dependencies = [
+ "adler2",
+ "simd-adler32",
+]
+
+[[package]]
+name = "multiversion_no_op"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "743fb55ba31b18fb1ecef6bdc9aa2743314978ac084044301a7eee33fb99a20d"
+
+[[package]]
+name = "ndk"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4"
+dependencies = [
+ "bitflags 2.13.2",
+ "jni-sys 0.3.1",
+ "log",
+ "ndk-sys",
+ "num_enum",
+ "raw-window-handle",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "ndk-context"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"
+
+[[package]]
+name = "ndk-sys"
+version = "0.6.0+11769913"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873"
+dependencies = [
+ "jni-sys 0.3.1",
+]
+
+[[package]]
+name = "num-bigint"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367"
+dependencies = [
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-derive"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "num-rational"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
+dependencies = [
+ "num-bigint",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_enum"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26"
+dependencies = [
+ "num_enum_derive",
+ "rustversion",
+]
+
+[[package]]
+name = "num_enum_derive"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8"
+dependencies = [
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "objc"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
+dependencies = [
+ "malloc_buf",
+]
+
+[[package]]
+name = "objc-sys"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310"
+
+[[package]]
+name = "objc2"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19d5490aaf8f1d7cf7688dfa9b0ce07900e168852c45cd2c03f534dfd27cfd0b"
+dependencies = [
+ "objc-sys",
+ "objc2-encode",
+]
+
+[[package]]
+name = "objc2"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08849bbd4767dfae9457696856ae1c84fe4e0281bbe4a7abff2d0e06fb7981f8"
+dependencies = [
+ "objc2-encode",
+]
+
+[[package]]
+name = "objc2-app-kit"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff"
+dependencies = [
+ "bitflags 2.13.2",
+ "block2 0.5.1",
+ "libc",
+ "objc2 0.5.3",
+ "objc2-core-data",
+ "objc2-core-image",
+ "objc2-foundation 0.2.2",
+ "objc2-quartz-core",
+]
+
+[[package]]
+name = "objc2-audio-toolbox"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6948501a91121d6399b79abaa33a8aa4ea7857fe019f341b8c23ad6e81b79b08"
+dependencies = [
+ "bitflags 2.13.2",
+ "libc",
+ "objc2 0.6.5",
+ "objc2-core-audio",
+ "objc2-core-audio-types",
+ "objc2-core-foundation",
+ "objc2-foundation 0.3.2",
+]
+
+[[package]]
+name = "objc2-avf-audio"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13a380031deed8e99db00065c45937da434ca987c034e13b87e4441f9e4090be"
+dependencies = [
+ "objc2 0.6.5",
+ "objc2-foundation 0.3.2",
+]
+
+[[package]]
+name = "objc2-cloud-kit"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009"
+dependencies = [
+ "bitflags 2.13.2",
+ "block2 0.5.1",
+ "objc2 0.5.3",
+ "objc2-core-location",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-contacts"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889"
+dependencies = [
+ "block2 0.5.1",
+ "objc2 0.5.3",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-core-audio"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1eebcea8b0dbff5f7c8504f3107c68fc061a3eb44932051c8cf8a68d969c3b2"
+dependencies = [
+ "dispatch2",
+ "objc2 0.6.5",
+ "objc2-core-audio-types",
+ "objc2-core-foundation",
+ "objc2-foundation 0.3.2",
+]
+
+[[package]]
+name = "objc2-core-audio-types"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a89f2ec274a0cf4a32642b2991e8b351a404d290da87bb6a9a9d8632490bd1c"
+dependencies = [
+ "bitflags 2.13.2",
+ "objc2 0.6.5",
+]
+
+[[package]]
+name = "objc2-core-data"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef"
+dependencies = [
+ "bitflags 2.13.2",
+ "block2 0.5.1",
+ "objc2 0.5.3",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-core-foundation"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
+dependencies = [
+ "bitflags 2.13.2",
+ "block2 0.6.2",
+ "dispatch2",
+ "libc",
+ "objc2 0.6.5",
+]
+
+[[package]]
+name = "objc2-core-image"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80"
+dependencies = [
+ "block2 0.5.1",
+ "objc2 0.5.3",
+ "objc2-foundation 0.2.2",
+ "objc2-metal",
+]
+
+[[package]]
+name = "objc2-core-location"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781"
+dependencies = [
+ "block2 0.5.1",
+ "objc2 0.5.3",
+ "objc2-contacts",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-encode"
+version = "4.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
+
+[[package]]
+name = "objc2-foundation"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8"
+dependencies = [
+ "bitflags 2.13.2",
+ "block2 0.5.1",
+ "dispatch",
+ "libc",
+ "objc2 0.5.3",
+]
+
+[[package]]
+name = "objc2-foundation"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
+dependencies = [
+ "bitflags 2.13.2",
+ "block2 0.6.2",
+ "libc",
+ "objc2 0.6.5",
+ "objc2-core-foundation",
+]
+
+[[package]]
+name = "objc2-link-presentation"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398"
+dependencies = [
+ "block2 0.5.1",
+ "objc2 0.5.3",
+ "objc2-app-kit",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-metal"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6"
+dependencies = [
+ "bitflags 2.13.2",
+ "block2 0.5.1",
+ "objc2 0.5.3",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-quartz-core"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a"
+dependencies = [
+ "bitflags 2.13.2",
+ "block2 0.5.1",
+ "objc2 0.5.3",
+ "objc2-foundation 0.2.2",
+ "objc2-metal",
+]
+
+[[package]]
+name = "objc2-symbols"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc"
+dependencies = [
+ "objc2 0.5.3",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-ui-kit"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f"
+dependencies = [
+ "bitflags 2.13.2",
+ "block2 0.5.1",
+ "objc2 0.5.3",
+ "objc2-cloud-kit",
+ "objc2-core-data",
+ "objc2-core-image",
+ "objc2-core-location",
+ "objc2-foundation 0.2.2",
+ "objc2-link-presentation",
+ "objc2-quartz-core",
+ "objc2-symbols",
+ "objc2-uniform-type-identifiers",
+ "objc2-user-notifications",
+]
+
+[[package]]
+name = "objc2-uniform-type-identifiers"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe"
+dependencies = [
+ "block2 0.5.1",
+ "objc2 0.5.3",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-user-notifications"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3"
+dependencies = [
+ "bitflags 2.13.2",
+ "block2 0.5.1",
+ "objc2 0.5.3",
+ "objc2-core-location",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.21.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
+
+[[package]]
+name = "orbclient"
+version = "0.3.55"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5df339f526ea9a60e371768d50efc2f2508c7203290731565d1f7a6f71d21747"
+dependencies = [
+ "libc",
+ "libredox",
+]
+
+[[package]]
+name = "pin-project"
+version = "1.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548"
+
+[[package]]
+name = "plain"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
+
+[[package]]
+name = "polling"
+version = "3.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218"
+dependencies = [
+ "cfg-if",
+ "concurrent-queue",
+ "hermit-abi",
+ "pin-project-lite",
+ "rustix 1.1.5",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "proc-macro-crate"
+version = "3.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f"
+dependencies = [
+ "toml_edit",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.107"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "r-efi"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
+
+[[package]]
+name = "raw-window-handle"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
+
+[[package]]
+name = "raw-window-metal"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76e8caa82e31bb98fee12fa8f051c94a6aa36b07cddb03f0d4fc558988360ff1"
+dependencies = [
+ "cocoa",
+ "core-graphics",
+ "objc",
+ "raw-window-handle",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "737970939a87c6fa31e7acad13307bccbb017a073b695b6089a2c484f929e20e"
+dependencies = [
+ "bitflags 2.13.2",
+]
+
+[[package]]
+name = "rodio"
+version = "0.22.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0a536bb79db59098ef71a4dd4246c02eb87b316deceb1b68e0cde7167ec01eb"
+dependencies = [
+ "cpal",
+ "dasp_sample",
+ "num-rational",
+ "symphonia",
+ "thiserror 2.0.21",
+]
+
+[[package]]
+name = "rustc_version"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustix"
+version = "0.38.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
+dependencies = [
+ "bitflags 2.13.2",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.4.15",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "rustix"
+version = "1.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "891efababe418670775f199f0d233d84843c227a0949a883ce15b37c78d6629d"
+dependencies = [
+ "bitflags 2.13.2",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.12.1",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "semver"
+version = "1.0.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
+
+[[package]]
+name = "serde"
+version = "1.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
+dependencies = [
+ "serde_core",
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 3.0.7",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.152"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1741ab7a6cc54a03a89b5d563ed60075c277d9e3cfa73ad0c1f23f23974703c6"
+dependencies = [
+ "itoa",
+ "memchr",
+ "serde",
+ "serde_core",
+ "zmij",
+]
+
+[[package]]
+name = "shlex"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
+
+[[package]]
+name = "simd-adler32"
+version = "0.3.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea"
+
+[[package]]
+name = "simd_cesu8"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520"
+dependencies = [
+ "rustc_version",
+ "simdutf8",
+]
+
+[[package]]
+name = "simdutf8"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
+
+[[package]]
+name = "slab"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
+
+[[package]]
+name = "smol_str"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "symphonia"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5773a4c030a19d9bfaa090f49746ff35c75dfddfa700df7a5939d5e076a57039"
+dependencies = [
+ "lazy_static",
+ "symphonia-codec-adpcm",
+ "symphonia-codec-pcm",
+ "symphonia-codec-vorbis",
+ "symphonia-core",
+ "symphonia-format-ogg",
+ "symphonia-format-riff",
+ "symphonia-metadata",
+]
+
+[[package]]
+name = "symphonia-codec-adpcm"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dddc50e2bbea4cfe027441eece77c46b9f319748605ab8f3443350129ddd07f"
+dependencies = [
+ "log",
+ "symphonia-core",
+]
+
+[[package]]
+name = "symphonia-codec-pcm"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e89d716c01541ad3ebe7c91ce4c8d38a7cf266a3f7b2f090b108fb0cb031d95"
+dependencies = [
+ "log",
+ "symphonia-core",
+]
+
+[[package]]
+name = "symphonia-codec-vorbis"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f025837c309cd69ffef572750b4a2257b59552c5399a5e49707cc5b1b85d1c73"
+dependencies = [
+ "log",
+ "symphonia-core",
+ "symphonia-utils-xiph",
+]
+
+[[package]]
+name = "symphonia-core"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea00cc4f79b7f6bb7ff87eddc065a1066f3a43fe1875979056672c9ef948c2af"
+dependencies = [
+ "arrayvec",
+ "bitflags 1.3.2",
+ "bytemuck",
+ "lazy_static",
+ "log",
+]
+
+[[package]]
+name = "symphonia-format-ogg"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b4955c67c1ed3aa8ae8428d04ca8397fbef6a19b2b051e73b5da8b1435639cb"
+dependencies = [
+ "log",
+ "symphonia-core",
+ "symphonia-metadata",
+ "symphonia-utils-xiph",
+]
+
+[[package]]
+name = "symphonia-format-riff"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2d7c3df0e7d94efb68401d81906eae73c02b40d5ec1a141962c592d0f11a96f"
+dependencies = [
+ "extended",
+ "log",
+ "symphonia-core",
+ "symphonia-metadata",
+]
+
+[[package]]
+name = "symphonia-metadata"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36306ff42b9ffe6e5afc99d49e121e0bd62fe79b9db7b9681d48e29fa19e6b16"
+dependencies = [
+ "encoding_rs",
+ "lazy_static",
+ "log",
+ "symphonia-core",
+]
+
+[[package]]
+name = "symphonia-utils-xiph"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee27c85ab799a338446b68eec77abf42e1a6f1bb490656e121c6e27bfbab9f16"
+dependencies = [
+ "symphonia-core",
+ "symphonia-metadata",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.119"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "3.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d62a2e0561533f2ca2561d0cf27fd9fedb640a1bf2616ff5d5c80d99017faadc"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
+dependencies = [
+ "thiserror-impl 1.0.69",
+]
+
+[[package]]
+name = "thiserror"
+version = "2.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e"
+dependencies = [
+ "thiserror-impl 2.0.21",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "2.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 3.0.7",
+]
+
+[[package]]
+name = "tokio"
+version = "1.53.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e95f91fcc7a621e8b030f6aa23c71fe9838ae2fb4d8118b75602a328f5144044"
+dependencies = [
+ "pin-project-lite",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "1.1.2+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b86d767906c6c42421dcba507eb9d203e779497710a47782a224bb871653053"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.25.17+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3641d5bbb5349a79e1020a242d251efbc546ad8048d133958323ce9c40a9c9c"
+dependencies = [
+ "indexmap",
+ "toml_datetime",
+ "toml_parser",
+ "winnow",
+]
+
+[[package]]
+name = "toml_parser"
+version = "1.1.5+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baa693a8032d7e1cada7d0041e96126df243179ff061456783ac7f12bda4744c"
+dependencies = [
+ "winnow",
+]
+
+[[package]]
+name = "tracing"
+version = "0.1.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
+dependencies = [
+ "pin-project-lite",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954"
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.13.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
+
+[[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.129"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9bb54f33acc68fd454578d9820b0bde1a1a3d17aa17bb7b6595806d02886d409"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "rustversion",
+ "wasm-bindgen-macro",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3cbab34de2d982e9b48e18d216d04c4a6f641066ff19ffb699980f591ee3610e"
+dependencies = [
+ "js-sys",
+ "tokio",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.129"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e29d0c35b16e224a7eeb5cd2d25e3e1968fbd65604117b44d3b789d00ee8535"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.129"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f501a8bc3719dba86ef8ae4728879c08001bea749eb1333ac5b91e040e2a6b7"
+dependencies = [
+ "bumpalo",
+ "proc-macro2",
+ "quote",
+ "syn 3.0.7",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.129"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23f0c9c52aa7cd7d77769a4cfe2a9adb1b331f489a41d912ce14513d5ab995c6"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "web-sys"
+version = "0.3.106"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88261b9deccee56594c11a3460c462c41f58d148598fe70ad77070126a68aba4"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "web-time"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "winapi-util"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
+dependencies = [
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "windows"
+version = "0.62.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580"
+dependencies = [
+ "windows-collections",
+ "windows-core",
+ "windows-future",
+ "windows-numerics",
+]
+
+[[package]]
+name = "windows-collections"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610"
+dependencies = [
+ "windows-core",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.62.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
+dependencies = [
+ "windows-implement",
+ "windows-interface",
+ "windows-link",
+ "windows-result",
+ "windows-strings",
+]
+
+[[package]]
+name = "windows-future"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb"
+dependencies = [
+ "windows-core",
+ "windows-link",
+ "windows-threading",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "windows-interface"
+version = "0.59.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "windows-link"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+
+[[package]]
+name = "windows-numerics"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26"
+dependencies = [
+ "windows-core",
+ "windows-link",
+]
+
+[[package]]
+name = "windows-result"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets 0.42.2",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[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 = "windows-targets"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
+dependencies = [
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
+]
+
+[[package]]
+name = "windows-threading"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "winit"
+version = "0.30.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6755fa58a9f8350bd1e472d4c3fcc25f824ec358933bba33306d0b63df5978d"
+dependencies = [
+ "android-activity",
+ "atomic-waker",
+ "bitflags 2.13.2",
+ "block2 0.5.1",
+ "calloop",
+ "cfg_aliases",
+ "concurrent-queue",
+ "core-foundation",
+ "core-graphics",
+ "cursor-icon",
+ "dpi",
+ "js-sys",
+ "libc",
+ "ndk",
+ "objc2 0.5.3",
+ "objc2-app-kit",
+ "objc2-foundation 0.2.2",
+ "objc2-ui-kit",
+ "orbclient",
+ "pin-project",
+ "raw-window-handle",
+ "redox_syscall 0.4.1",
+ "rustix 0.38.44",
+ "smol_str",
+ "tracing",
+ "unicode-segmentation",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "web-time",
+ "windows-sys 0.52.0",
+ "xkbcommon-dl",
+]
+
+[[package]]
+name = "winnow"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "xkbcommon-dl"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5"
+dependencies = [
+ "bitflags 2.13.2",
+ "dlib",
+ "log",
+ "once_cell",
+ "xkeysym",
+]
+
+[[package]]
+name = "xkeysym"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56"
+
+[[package]]
+name = "zmij"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
diff --git a/adapters/fparkan-render-vulkan/src/asset_mesh.rs b/adapters/fparkan-render-vulkan/src/asset_mesh.rs
index fbdff67..9d084ae 100644
--- a/adapters/fparkan-render-vulkan/src/asset_mesh.rs
+++ b/adapters/fparkan-render-vulkan/src/asset_mesh.rs
@@ -860,7 +860,7 @@ fn append_node_fallback_batch(
lightmap_index: batch.lightmap_index,
batch_flags: batch.batch_flags,
pipeline_state: world_pipeline_state(),
- alpha_test_reference: 0,
+ alpha_test_reference: VulkanStaticDrawRange::NATIVE_DEFAULT_ALPHA_TEST_REFERENCE,
});
Ok(())
}
@@ -1211,7 +1211,7 @@ fn static_terrain_draw_ranges(
lightmap_index: u8::MAX,
batch_flags: 0,
pipeline_state: world_pipeline_state(),
- alpha_test_reference: 0,
+ alpha_test_reference: VulkanStaticDrawRange::NATIVE_DEFAULT_ALPHA_TEST_REFERENCE,
});
start_face = end_face;
}
@@ -1256,7 +1256,7 @@ fn static_model_indices_and_ranges(
lightmap_index: batch.lightmap_index,
batch_flags: batch.batch_flags,
pipeline_state: world_pipeline_state(),
- alpha_test_reference: 0,
+ alpha_test_reference: VulkanStaticDrawRange::NATIVE_DEFAULT_ALPHA_TEST_REFERENCE,
});
}
if indices.is_empty() {
@@ -1441,6 +1441,14 @@ mod tests {
.expect("representable MSH");
assert_eq!(mesh.indices, vec![1, 2, 3]);
+ let alpha_test_range = VulkanStaticDrawRange {
+ pipeline_state: mesh.draw_ranges[0]
+ .pipeline_state
+ .with_material_category(1)
+ .expect("native alpha-test material category"),
+ ..mesh.draw_ranges[0]
+ };
+ assert_eq!(alpha_test_range.alpha_test_cutoff(), 1.0 / 255.0);
assert_eq!(
mesh.draw_ranges,
vec![VulkanStaticDrawRange {
@@ -1450,7 +1458,7 @@ mod tests {
lightmap_index: 0,
batch_flags: 0,
pipeline_state: world_pipeline_state(),
- alpha_test_reference: 0,
+ alpha_test_reference: VulkanStaticDrawRange::NATIVE_DEFAULT_ALPHA_TEST_REFERENCE,
}]
);
assert_eq!(mesh.vertices[1].position, [-0.8, -0.8, 0.0]);
@@ -1681,6 +1689,10 @@ mod tests {
assert_eq!(mesh.vertices[0].position, [101.0, 0.0, 0.0]);
assert_eq!(mesh.vertices[3].position, [111.0, 0.0, 0.0]);
assert_eq!(mesh.indices, vec![0, 1, 2, 3, 4, 5]);
+ assert_eq!(
+ mesh.draw_ranges[0].alpha_test_reference,
+ VulkanStaticDrawRange::NATIVE_DEFAULT_ALPHA_TEST_REFERENCE
+ );
}
#[test]
@@ -1967,7 +1979,8 @@ mod tests {
lightmap_index: 0,
batch_flags: 0,
pipeline_state: world_pipeline_state(),
- alpha_test_reference: 0,
+ alpha_test_reference:
+ VulkanStaticDrawRange::NATIVE_DEFAULT_ALPHA_TEST_REFERENCE,
},
VulkanStaticDrawRange {
first_index: 3,
@@ -1976,7 +1989,8 @@ mod tests {
lightmap_index: 0,
batch_flags: 0,
pipeline_state: world_pipeline_state(),
- alpha_test_reference: 0,
+ alpha_test_reference:
+ VulkanStaticDrawRange::NATIVE_DEFAULT_ALPHA_TEST_REFERENCE,
},
]
);
@@ -2122,5 +2136,8 @@ mod tests {
assert_eq!(ranges[2].first_index, 9);
assert_eq!(ranges[2].index_count, 3);
assert_eq!(ranges[2].material_index, 0xff01);
+ assert!(ranges.iter().all(|range| {
+ range.alpha_test_reference == VulkanStaticDrawRange::NATIVE_DEFAULT_ALPHA_TEST_REFERENCE
+ }));
}
}
diff --git a/adapters/fparkan-render-vulkan/src/ffi/smoke.rs b/adapters/fparkan-render-vulkan/src/ffi/smoke.rs
index c3565e1..c9de5ba 100644
--- a/adapters/fparkan-render-vulkan/src/ffi/smoke.rs
+++ b/adapters/fparkan-render-vulkan/src/ffi/smoke.rs
@@ -862,9 +862,9 @@ impl VulkanSmokeRenderer {
/// Sets the native per-range alpha reference in legacy 0..=255 units.
///
- /// A value of zero selects the renderer's proven native default (one) for
- /// alpha-test ranges. The update is dynamic and does not recreate a
- /// graphics pipeline.
+ /// Zero remains an explicit value; use the native-default constant when
+ /// matching the renderer's initial state. The update is dynamic and does
+ /// not recreate a graphics pipeline.
pub fn set_draw_range_alpha_test_reference(
&mut self,
range_index: usize,
@@ -1091,10 +1091,10 @@ impl VulkanSmokeRenderer {
.chain(additive_rgb)
.chain(overlay_directional_rgb.into_iter().flatten())
.chain(overlay_additive_rgb.into_iter().flatten())
- .any(|value| !value.is_finite() || value < 0.0)
+ .any(|value| !value.is_finite())
{
return Err(VulkanSmokeRendererError::InvalidStaticMesh {
- context: "dynamic material lighting must be finite and non-negative",
+ context: "dynamic material lighting must be finite",
});
}
let lighting = self.material_lighting.get_mut(material_index).ok_or(
@@ -1155,10 +1155,10 @@ impl VulkanSmokeRenderer {
if specular_rgb
.into_iter()
.chain(overlay_specular_rgb.into_iter().flatten())
- .any(|value| !value.is_finite() || value < 0.0)
+ .any(|value| !value.is_finite())
{
return Err(VulkanSmokeRendererError::InvalidStaticMesh {
- context: "dynamic material specular RGB must be finite and non-negative",
+ context: "dynamic material specular RGB must be finite",
});
}
let specular = self.material_specular.get_mut(material_index).ok_or(
diff --git a/adapters/fparkan-render-vulkan/src/ffi/smoke_types.rs b/adapters/fparkan-render-vulkan/src/ffi/smoke_types.rs
index c41eff9..f628e23 100644
--- a/adapters/fparkan-render-vulkan/src/ffi/smoke_types.rs
+++ b/adapters/fparkan-render-vulkan/src/ffi/smoke_types.rs
@@ -622,9 +622,9 @@ pub(super) fn resolve_draw_texture_indices(
.chain(material.additive_rgb)
.chain(material.specular_rgb)
.chain(material.overlay_specular_rgb.into_iter().flatten())
- .any(|value| !value.is_finite() || value < 0.0)
+ .any(|value| !value.is_finite())
{
- return Err("static material lighting and specular RGB parameters must be finite and non-negative");
+ return Err("static material lighting and specular RGB parameters must be finite");
}
if material
.uv_transform
@@ -645,9 +645,9 @@ pub(super) fn resolve_draw_texture_indices(
.into_iter()
.flatten()
.chain(material.overlay_additive_rgb.into_iter().flatten())
- .any(|value| !value.is_finite() || value < 0.0)
+ .any(|value| !value.is_finite())
{
- return Err("static material overlay RGB parameters must be finite and non-negative");
+ return Err("static material overlay RGB parameters must be finite");
}
if material
.overlay_uv_transform
@@ -1012,15 +1012,15 @@ mod static_mesh_tests {
overlay_detail_texture: None,
diffuse_alpha: 1.0,
overlay_diffuse_alpha: None,
- directional_rgb: [1.0; 3],
- additive_rgb: [0.0; 3],
- specular_rgb: [0.0; 3],
+ directional_rgb: [-0.25; 3],
+ additive_rgb: [-0.5; 3],
+ specular_rgb: [-0.75; 3],
specular_power: 0,
- overlay_specular_rgb: None,
+ overlay_specular_rgb: Some([-1.0; 3]),
overlay_specular_power: None,
uv_transform: [0.0, 0.0, 1.0, 1.0],
- overlay_directional_rgb: None,
- overlay_additive_rgb: None,
+ overlay_directional_rgb: Some([-0.125; 3]),
+ overlay_additive_rgb: Some([-0.375; 3]),
detail_uv_transform: [0.0, 0.0, 1.0, 1.0],
overlay_uv_transform: None,
overlay_detail_uv_transform: None,
@@ -1061,6 +1061,20 @@ mod static_mesh_tests {
resolve_draw_texture_indices(&ranges, &materials),
Ok(vec![1, 0])
);
+
+ let mut non_finite_base = materials.clone();
+ non_finite_base[0].specular_rgb[0] = f32::NAN;
+ assert_eq!(
+ resolve_draw_texture_indices(&ranges, &non_finite_base),
+ Err("static material lighting and specular RGB parameters must be finite")
+ );
+
+ let mut non_finite_overlay = materials.clone();
+ non_finite_overlay[0].overlay_additive_rgb = Some([f32::INFINITY, 0.0, 0.0]);
+ assert_eq!(
+ resolve_draw_texture_indices(&ranges, &non_finite_overlay),
+ Err("static material overlay RGB parameters must be finite")
+ );
}
#[test]
diff --git a/apps/fparkan-game/src/main.rs b/apps/fparkan-game/src/main.rs
index abfb6da..0b1319d 100644
--- a/apps/fparkan-game/src/main.rs
+++ b/apps/fparkan-game/src/main.rs
@@ -1792,7 +1792,7 @@ fn append_environment_range(
lightmap_index: u8::MAX,
batch_flags: 0,
pipeline_state,
- alpha_test_reference: 0,
+ alpha_test_reference: VulkanStaticDrawRange::NATIVE_DEFAULT_ALPHA_TEST_REFERENCE,
});
Ok(EnvironmentGpuRange {
range_index,
diff --git a/crates/fparkan-material/src/lib.rs b/crates/fparkan-material/src/lib.rs
index da0b716..082d10c 100644
--- a/crates/fparkan-material/src/lib.rs
+++ b/crates/fparkan-material/src/lib.rs
@@ -717,6 +717,10 @@ fn interpolate_coefficients(
mask: u32,
fraction: f32,
) -> MaterialCoefficients {
+ // World3D 0x3030 receives source and destination wrappers one dword before
+ // each D3DMATERIAL7 body. Its +4/+14/+24/+34 offsets therefore select
+ // parameters 4..6, 0..2, 8..10, and 12..14; +0x20 selects parameter 3.
+ // The other alpha/extra fields, power, and page remain from the left phase.
let left = left.coefficients();
let right = right.coefficients();
let mut output = left;
@@ -1306,6 +1310,59 @@ mod tests {
}
#[test]
+ fn material_animation_interpolation_masks_match_native_rebased_offsets() {
+ let left = phase_with_parameters(
+ [
+ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180,
+ ],
+ b"LEFT",
+ );
+ let right = phase_with_parameters(
+ [
+ 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 220, 230,
+ 240,
+ ],
+ b"RIGHT",
+ );
+ let rgb = |first: u8| [first, first + 10, first + 20].map(|value| f32::from(value) / 255.0);
+ let cases = [
+ (0, rgb(10), rgb(50), rgb(90), rgb(130), 0.4),
+ (1, rgb(40), rgb(50), rgb(90), rgb(130), 0.4),
+ (2, rgb(10), rgb(80), rgb(90), rgb(130), 0.4),
+ (4, rgb(10), rgb(50), rgb(120), rgb(130), 0.4),
+ (8, rgb(10), rgb(50), rgb(90), rgb(160), 0.4),
+ (16, rgb(10), rgb(50), rgb(90), rgb(130), 0.7),
+ (0x1f, rgb(40), rgb(80), rgb(120), rgb(160), 0.7),
+ ];
+
+ for (mask, additive, directional, specular, extra, opacity) in cases {
+ let output = interpolate_coefficients(&left, &right, mask, 0.5);
+ for (actual, expected) in [
+ (output.additive_rgb, additive),
+ (output.directional_rgb, directional),
+ (output.specular_rgb, specular),
+ (output.extra_rgb, extra),
+ ] {
+ for channel in 0..3 {
+ assert!(
+ (actual[channel] - expected[channel]).abs() < 1e-6,
+ "mask {mask:#x}, channel {channel}: got {}, expected {}",
+ actual[channel],
+ expected[channel]
+ );
+ }
+ }
+
+ assert!((output.opacity - opacity).abs() < 1e-6, "mask {mask:#x}");
+ assert_eq!(output.directional_extra, 80.0 / 255.0);
+ assert_eq!(output.specular_extra, 120.0 / 255.0);
+ assert_eq!(output.extra_alpha, 160.0 / 255.0);
+ assert_eq!(output.power, 170);
+ assert_eq!(output.page_index, -76);
+ }
+ }
+
+ #[test]
fn material_ping_pong_reverses_local_time_and_clamp_holds_last_phase() {
let phases = vec![
phase_with_parameters([0; 18], b"A"),
diff --git a/crates/fparkan-texm/src/lib.rs b/crates/fparkan-texm/src/lib.rs
index 3578dd0..4b7ffaa 100644
--- a/crates/fparkan-texm/src/lib.rs
+++ b/crates/fparkan-texm/src/lib.rs
@@ -665,10 +665,10 @@ fn decode_mip_rgba8_internal(
continue;
}
let out = index.saturating_mul(4);
- rgba[out] = palette[palette_offset];
+ rgba[out] = palette[palette_offset + 2];
rgba[out + 1] = palette[palette_offset + 1];
- rgba[out + 2] = palette[palette_offset + 2];
- rgba[out + 3] = palette[palette_offset + 3];
+ rgba[out + 2] = palette[palette_offset];
+ rgba[out + 3] = 255;
}
}
DiskPixelFormat::Rgb565 => decode_words(level_data, pixel_count, &mut rgba, decode_rgb565),
@@ -996,6 +996,17 @@ mod tests {
}
#[test]
+ fn indexed_palette_decodes_bgrx_as_opaque_rgba() {
+ let document =
+ decode_texm(Arc::from(indexed_payload().into_boxed_slice())).expect("indexed document");
+
+ assert_eq!(
+ decode_mip_rgba8(&document, 0).expect("rgba").rgba8,
+ [30, 20, 10, 255]
+ );
+ }
+
+ #[test]
fn channel_expansion_boundary_values_are_stable() {
let document = decode_texm(Arc::from(
payload(2, 1, 565, &[&[0x00, 0x00, 0xFF, 0xFF]]).into_boxed_slice(),
@@ -1155,7 +1166,7 @@ mod tests {
fn indexed_payload() -> Vec<u8> {
let mut palette = [0_u8; 1024];
- palette[4..8].copy_from_slice(&[10, 20, 30, 255]);
+ palette[4..8].copy_from_slice(&[10, 20, 30, 0]);
let mut out = header(1, 1, 1, 0);
out.extend_from_slice(&palette);
out.push(1);
diff --git a/docs/reference/original-binaries.md b/docs/reference/original-binaries.md
index 05c0141..a988638 100644
--- a/docs/reference/original-binaries.md
+++ b/docs/reference/original-binaries.md
@@ -15,11 +15,16 @@ RVA в книге относится к определённой сборке DL
## Сборка GOG
The canonical disassembly source is the windowed GOG installation, not the
-Part 1/Part 2 test installations. Its `Terrain.dll` is a distinct revision:
-
-| Binary | SHA-256 | Size | Notes |
-| --- | --- | ---: | --- |
-| `Terrain.dll` | `af87d1b2e728a0be73c52be3b44cc196ab46da7799f25a15d40f8c9b0b425ead` | 499712 | GOG camera receiver evidence; do not reuse Part 1/2 RVAs without a matching hash |
+Part 1/Part 2 test installations. The rows below identify GOG-specific DLL
+builds. Match the module size and hash before applying one of their RVAs; the
+Part 1/Part 2 builds remain listed separately above.
+
+| Binary | Size (bytes) | SHA-256 |
+| --- | ---: | --- |
+| `AniMesh.dll` | 172032 | `65659bf67f20e5be77d67a5b2f59968f61b94293143807fa59ca451c560c4f7e` |
+| `Effect.dll` | 163840 | `386a3e8db1ce23f61957825d633c7dae1ed40c25cab0cf10a534385fd7cee789` |
+| `iron3d.dll` | 1175552 | `d1dc4ea8535e2069b0a05c9e8be2724dd438ba44cca7a83007d8b2e50f9e35fa` |
+| `Terrain.dll` | 499712 | `af87d1b2e728a0be73c52be3b44cc196ab46da7799f25a15d40f8c9b0b425ead` |
## Различающиеся модули
diff --git a/docs/reference/render-frame.md b/docs/reference/render-frame.md
index 015f9f3..f8aee38 100644
--- a/docs/reference/render-frame.md
+++ b/docs/reference/render-frame.md
@@ -1,174 +1,169 @@
-# Render frame
+# Кадр отрисовки
-Кадр является последней стадией цикла, а не самостоятельной функцией renderer-а.
-До draw calls уже накоплен input, рассчитан tick, применены отложенные операции,
-выбрана камера и обновлён 3D sound listener.
+Кадр завершает цикл и не является отдельной операцией рендерера. До вызовов
+отрисовки собирается ввод, вычисляется такт, применяются отложенные операции,
+выбирается камера и обновляется слушатель 3D-звука.
-## Frame skeleton
+## Порядок кадра
```text
-system messages and input
- -> simulation calculation
- -> deferred object operations
- -> animation and transforms
- -> camera and sound listener
- -> visibility and render queues
- -> materials and draw passes
- -> renderer completion
- -> end-of-render callbacks and UI
+системные сообщения и ввод
+ -> расчёт симуляции
+ -> отложенные операции над объектами
+ -> анимация и преобразования
+ -> камера и звуковой слушатель
+ -> видимость и очереди отрисовки
+ -> материалы и проходы отрисовки
+ -> завершение рендерера
+ -> обратные вызовы после отрисовки и UI
```
-В `World3D::stdRenderGame` доказан крупный порядок: camera передаётся Terrain,
-настраиваются viewport/matrices, вызываются renderer boundary slots,
-устанавливается `in_render`, выполняется traversal мира, закрывается world/shade
-pass, вызывается renderer completion, снимается `in_render`, рассылается
-end-of-render.
-
-## Draw item
-
-Подготовленный draw item содержит:
-
-- node world matrix;
-- batch flags and index range;
-- WEAR material handle;
-- MAT0 active phase and coefficients;
-- texture handle;
-- optional lightmap handle;
-- render phase and sorting key;
-- legacy pipeline state.
-
-Подготовленный item должен ссылаться на immutable данные кадра. Изменение phase
-или texture cache посреди прохода не должно менять уже собранную очередь.
-
-## MAT0 state and transparent backgrounds
-
-For mission world ranges, the selected MAT0 material supplies its native
-pipeline category. The category sets blend and alpha-test state while preserving
-the range's depth and cull state; an animated material selector updates the
-category with the selector. Queue membership is independent: sampled opacity
-below one or native batch flags `0x108` mark a world range transparent, and a
-queued range with depth enabled tests depth without writing it. Fixed
-environment ranges keep their explicit states.
-
-The fixed-camera preview explicitly marks nebula/stars, dome and cloud ranges as
-transparent backgrounds. Opaque world ranges stay in their world slots; the
-transparent world queue is sorted back-to-front and inserted immediately after
-the last marked background. Other fixed ranges retain their relative order.
-Thus cloud layers can be drawn before transparent foliage, while later sun,
-weather and other foreground ranges keep their existing relative order. This
-is a narrow queue rule, not a reordering of every sky or effects pass.
-
-## Regression evidence
-
-`cargo test -p fparkan-render-vulkan --lib material_category_and_transparency_preserve_world_state_in_either_order --offline`
-checks that category 1 remains source-alpha with alpha testing, retains depth
-and cull settings in either update order, and rejects unsupported categories.
-`cargo test -p fparkan-render-vulkan --lib tree_canopy_queue_follows_sky_and_precedes_foreground_effects --offline`
-checks the explicit queue order: screen gradient, opaque terrain/building,
-shadow, nebula/dome/clouds, far-to-near transparent canopy, then sun/weather.
-
-Viewer-only paired readbacks used `--frames 3 --validation
---legacy-camera-capture` with the pre-fix and patched builds. On AutoDemo TMA
-object 4 (`wor_l_n1.dat`, `fr_l_plant`), the 1280x720 camera at eye
-`[464.319, 904.719, 38.0]`, aimed at `[374.319, 814.719, 12.0]` with FOV
-`0.62`, changed the two black facade panels to tinted, textured portal
-surfaces. On Mission.01 object 12 (`s_tree_04`), the 1280x1024 camera at eye
-`[492.63855, 312.90244, 46.5]` aimed at
-`[492.63855, 442.90244, 55.0]` crosses the canopy against open sky; paired
-three-frame captures at the same synthetic atmosphere phase (600 seconds) show
-the patched build restoring the missing upper/right fronds along that sky
-boundary. Both runs reported clean Vulkan validation. These captures verify our
-preview behavior and do not prove the native renderer's pass order, portal
-transmittance or exact color.
-
-## GOG camera dispatch evidence
-
-For the GOG `World3D.dll` baseline with SHA-256
+По анализу вызовов установлен общий порядок в `World3D::stdRenderGame`: камера
+передаётся в Terrain, задаются `viewport` и матрицы, вызываются граничные слоты
+рендерера, выставляется `in_render`, обходится мир, завершаются проходы мира и
+затенения, вызывается завершение рендерера, снимается `in_render`, затем
+рассылается `end-of-render`.
+
+## Элемент отрисовки
+
+Подготовленный элемент отрисовки содержит:
+
+- мировую матрицу узла;
+- флаги пакета `batch` и диапазон индексов;
+- дескриптор материала WEAR;
+- активную фазу и коэффициенты MAT0;
+- дескриптор текстуры;
+- необязательный дескриптор карты освещения;
+- фазу отрисовки и ключ сортировки;
+- состояние конвейера устаревшей отрисовки.
+
+Подготовленный элемент должен ссылаться на неизменяемые данные кадра. Изменение
+фазы или кэша текстур посреди прохода не должно менять уже собранную очередь.
+
+## Состояние MAT0 и прозрачные фоны
+
+Для диапазонов мира миссии выбранный материал MAT0 задаёт нативную категорию
+конвейера. Категория определяет смешивание и альфа-тест, сохраняя настройки
+проверки глубины и отсечения граней. Если селектор материала анимируется,
+вместе с ним обновляется и категория.
+Принадлежность к очереди определяется отдельно: прозрачным считается диапазон
+мира с выбранной прозрачностью меньше единицы или с нативными флагами пакета
+`batch` `0x108`. Такой диапазон при включённой проверке глубины не записывает
+глубину. За фиксированными диапазонами окружения сохраняются их явные состояния.
+
+В превью с фиксированной камерой диапазоны туманности и звёзд, купола и облаков
+явно помечаются как прозрачный фон. Непрозрачные диапазоны мира остаются на
+своих местах; прозрачная очередь мира сортируется от дальних элементов к
+ближним и вставляется сразу после последнего помеченного фона. Порядок остальных
+фиксированных диапазонов сохраняется. Поэтому облака могут отрисовываться перед
+прозрачной листвой, а расположенные позднее солнце, погода и другие передние
+эффекты сохраняют свой относительный порядок. Это локальное правило очереди, а
+не перестановка всех проходов неба или эффектов.
+
+## Проверки
+
+`cargo test --locked --offline -p fparkan-render-vulkan --lib material_category_and_transparency_preserve_world_state_in_either_order`
+Проверяет, что категория 1 сохраняет смешивание `source-alpha` и альфа-тест,
+сохраняет настройки проверки глубины и отсечения граней при любом порядке
+обновления и отклоняет неподдерживаемые категории.
+`cargo test --locked --offline -p fparkan-render-vulkan --lib tree_canopy_queue_follows_sky_and_precedes_foreground_effects`
+Проверяет заданный порядок очереди: градиент экрана, непрозрачная местность и
+здание, тень, туманность, купол и облака, прозрачная крона от дальних элементов
+к ближним, затем солнце и погода.
+
+## Свидетельства диспетчеризации камеры GOG
+
+Для версии GOG `World3D.dll` с SHA-256
`17e4a3089b2583a8cf2356c9db0390b1aba138356a09130d79b4e7e4791da61e`,
-the exported `stdRenderGame` is RVA `0x13BD0`. It first calls
-`Terrain::stdSetCurrentCamera2(camera)`, stores the camera pointer only for
-the frame, and clears it before return. `sendEndOfRender` is a separate export
-at RVA `0x13D90`. This is frame-order evidence only: the camera ABI, matrices
-and viewport values are not recovered by this call-order analysis. The
-implemented preview path below accepts selector-0 input produced through the
-[native frame capture workflow](../../tools/native-frame-capture/README.md).
-The capture workflow also records an observed mission path and, when
-its thread/generation and clock checks succeed, an `atmosphere_seconds` sample.
-For a fixed-camera preview, the renderer uses that optional absolute schedule
-phase when no `--atmosphere-seconds` override is supplied and keeps schedule
-sampling at that exact phase across readback frames. An explicit CLI value takes
-precedence. The phase sample is not full simulation time: weather, RNG, and
-other mission state remain unknown, so this evidence does not establish
-full-frame parity.
-
-Receiver-side GOG Terrain evidence refines this contract. `stdSetCurrentCamera2`
-(RVA `0x4FD40` in Terrain SHA-256
-`af87d1b2e728a0be73c52be3b44cc196ab46da7799f25a15d40f8c9b0b425ead`) queries
-selector `18` on the supplied object and invokes slot `+12` on that result. It
-does not store the supplied pointer in `stdGetCurrentCamera2`; that getter
-returns a Terrain global populated by selector `8` during Terrain initialization.
-
-`Terrain.dll!LoadCamera` adds a separate factory-side contract. Its GOG entry
-at RVA `0x4EBE0` is `__stdcall` with four machine-word arguments, allocates
-`0x1A4` bytes, calls the constructor at RVA `0x4EC60`, and returns the interface
-at object offset `+0x134`. The constructor passes arguments 3 and 4 into base
-initialization at `this + 4`, writes argument 3 to `this + 0x138`, and calls a
-helper at RVA `0x4CEF0` with arguments 1, 2 and 4 plus `this`. Argument 1 is a
-mode string: only `REFLECTION` and `REFLECTION_SHIFTED` produce a non-zero mode
-value at `this + 0x1A0`. This is factory ABI evidence, not a recovered view or
-projection matrix contract.
-
-## Legacy camera preview path
-
-`fparkan-game --legacy-camera-capture <json>` consumes the selector-0 transform,
-the native D3D7 viewport RECT `[left, top, right, bottom]`, near/far planes and
-field of view. It derives the D3D7 view and clip transform from those inputs,
-carries the resulting `clip_from_world` matrix into the preview camera, and
-keeps the view-matrix camera axes directly, so the captured basis, including
-roll, is not reduced to yaw/pitch. The RECT becomes a physical drawable extent
-of `right - left` by `bottom - top`. The preview window is fixed to that extent;
-startup and non-zero resize events reject a physical-size mismatch instead of
-stretching the captured view. Compare the result with the corresponding
-`[left, top, right, bottom]` crop of the original PNG. `FreeFlightCamera` is not
-created in this mode, but redraws still update environment and projected
-shadows from the captured camera. Before environment updates can emit sound
-events, the audio listener receives the captured position, forward and up axes;
-initial world draw sorting uses the same captured position. A phase capture may
-include an observed mission path and a verified `atmosphere_seconds` value; the
-latter is used only when no explicit CLI override is supplied. This value pins
-the atmosphere schedule sample during a fixed-camera comparison while FX still
-receives real frame time. `simulation_time_seconds`, weather, RNG and complete
-mission state remain unknown, and a camera/phase match alone does not establish
-visual parity with a native frame.
-
-The preview's sky heading is `atan2(forward.x, forward.y)`, matching the second
-output calculated at `Terrain.dll` RVA `0x4F9C0`; `CSky` adds π at RVA `0x481C5`
-when selecting screen-gradient sectors. This mapping is applied only at the sky
-boundary and does not change `FreeFlightCamera` yaw or movement controls.
-Matched same-camera, same-phase three-frame captures at phases 56.227 and
-998.893 showed zero changed pixels before and after this correction; the
-remaining sky and color mismatch is unresolved, so this source-level match does
-not establish visual parity.
-
-## Parity risks
-
-`VulkanStaticCamera::from_legacy_d3d7` keeps the D3D7 view and projection
-reconstruction in `fparkan-render`, then negates the vertical projection term
-once at the Vulkan adapter boundary. D3D7's top-down viewport places positive
-NDC Y at the top; the Vulkan swapchain uses a positive viewport height, so the
-adapter flips NDC Y while the shader preserves `gl_Position`. The generic
-`from_row_major_view_projection` path remains unchanged; its callers supply
-Vulkan-ready matrices, as the free-flight camera already does.
-
-- x87 precision and rounding;
-- scalar/SIMD `g_FastProc` differences;
-- object, batch and transparent primitive order;
-- depth, cull, alpha test and blend transitions;
-- mip-skip, palette and Page coordinates;
-- material fallback and phase selection;
-- RNG sequence for FX and atmosphere;
-- device capability fallback;
-- simulation time quantization.
-
-Для отладки нужен deterministic frame capture: camera state, visible object IDs,
-draw-item list, pipeline keys, matrices и hashes промежуточных buffers.
+экспорт `stdRenderGame` находится по RVA `0x13BD0`. Вначале он вызывает
+`Terrain::stdSetCurrentCamera2(camera)`, сохраняет указатель камеры на время
+кадра и очищает его перед возвратом. `sendEndOfRender` — отдельный экспорт по
+RVA `0x13D90`. Эти данные подтверждают только порядок вызовов в кадре: анализ
+не восстанавливает ABI камеры, матрицы или значения `viewport`. Реализованный
+путь превью принимает входные данные с матрицей `selector0_words`, полученные через
+[процедуру захвата нативного кадра](../../tools/native-frame-capture/README.md).
+
+Процедура захвата записывает наблюдавшийся путь миссии и значение
+`atmosphere_seconds`, если проверка потока, поколения и часов прошла успешно.
+В превью с фиксированной камерой эта абсолютная фаза расписания используется,
+если не задан параметр `--atmosphere-seconds`; на всех кадрах чтения сохраняется
+та же фаза. Явный параметр командной строки имеет приоритет. Эта фаза не равна
+времени полной симуляции: погода, RNG и другие состояния миссии остаются
+неизвестны, поэтому она не подтверждает полного совпадения кадра.
+
+Анализ принимающей стороны уточняет этот контракт. `stdSetCurrentCamera2`
+(RVA `0x4FD40` в Terrain с SHA-256
+`af87d1b2e728a0be73c52be3b44cc196ab46da7799f25a15d40f8c9b0b425ead`)
+запрашивает селектор `18` у переданного объекта и вызывает слот `+12` полученного
+интерфейса. Указатель не сохраняется в `stdGetCurrentCamera2`: этот геттер
+возвращает глобальное значение Terrain, установленное селектором `8` при
+инициализации Terrain.
+
+`Terrain.dll!LoadCamera` задаёт отдельный контракт фабрики. Точка входа GOG по
+RVA `0x4EBE0` использует соглашение о вызовах `__stdcall` и принимает четыре
+машинных слова, выделяет `0x1A4` байта, вызывает конструктор по RVA `0x4EC60` и
+возвращает интерфейс по смещению объекта `+0x134`. Конструктор передаёт
+аргументы 3 и 4 в инициализацию базового объекта по `this + 4`, записывает
+аргумент 3 в `this + 0x138` и вызывает вспомогательную функцию по RVA `0x4CEF0`,
+передавая ей аргументы 1, 2 и 4 вместе с `this`. Аргумент 1 — строка режима:
+ненулевое значение по `this + 0x1A0` дают только `REFLECTION` и
+`REFLECTION_SHIFTED`. Это свидетельство ABI фабрики, а не восстановленный
+контракт матрицы вида или проекции.
+
+## Предпросмотр по захвату камеры D3D7
+
+`fparkan-game --legacy-camera-capture <json>` принимает матрицу камеры
+`selector0_words`, прямоугольник области отображения `viewport` D3D7
+`[left, top, right, bottom]`, ближнюю и дальнюю плоскости и угол поля зрения.
+По этим данным превью строит матрицы вида и проекции D3D7, передаёт полученную
+матрицу `clip_from_world` камере превью и напрямую использует оси камеры из
+матрицы вида. Поэтому базис захвата, включая крен, не сводится к рысканию и
+тангажу. Прямоугольник задаёт физический размер области отрисовки:
+`right - left` на `bottom - top`. Размер окна превью закреплён: если при
+запуске или после ненулевого изменения окна физический размер не совпадает с
+заданным, приложение завершает работу, а не растягивает изображение.
+Сравнивайте результат с фрагментом исходного PNG по прямоугольнику
+`[left, top, right, bottom]`. В этом режиме `FreeFlightCamera` не создаётся,
+но при повторной отрисовке обновляются окружение и проецируемые тени для
+захваченной камеры. До обновления окружения, которое может создавать звуковые
+события, слушателю задаются захваченные позиция, направление вперёд и верхняя
+ось; начальная сортировка диапазонов мира использует ту же позицию.
+
+Захват может содержать наблюдавшийся путь миссии и проверенное значение
+`atmosphere_seconds`; последнее используется только при отсутствии явного
+параметра командной строки. Это значение фиксирует фазу расписания атмосферы
+при сравнении с неподвижной камерой, тогда как FX продолжает получать реальное
+время кадра. `simulation_time_seconds`, погода, RNG и полное состояние миссии
+остаются неизвестными, поэтому совпадение камеры и фазы не подтверждает
+визуального совпадения с нативным кадром.
+
+Для неба превью вычисляет направление как `atan2(forward.x, forward.y)`. Оно
+совпадает со вторым выходным значением, вычисляемым в `Terrain.dll` по RVA
+`0x4F9C0`; при выборе сектора экранного градиента `CSky` добавляет π по RVA
+`0x481C5`. Это преобразование применяется только на границе неба и не меняет
+рыскание или управление движением `FreeFlightCamera`.
+
+## Ограничения точного совпадения
+
+`VulkanStaticCamera::from_legacy_d3d7` выполняет восстановление вида и проекции
+D3D7 в `fparkan-render`, а затем один раз меняет знак вертикального члена
+проекции на границе адаптера Vulkan. В области отображения D3D7 положительная
+координата NDC Y направлена вверх экрана; Vulkan использует положительную высоту
+области отображения, поэтому адаптер меняет знак NDC Y, а шейдер сохраняет
+`gl_Position`. Общий путь
+`from_row_major_view_projection` не меняется: вызывающие его функции передают
+матрицы, уже подготовленные для Vulkan, как и камера свободного полёта.
+
+- точность и округление x87;
+- различия scalar/SIMD в `g_FastProc`;
+- порядок объектов, пакетов и прозрачных примитивов;
+- переходы состояний проверки глубины, отсечения граней, альфа-теста и смешивания;
+- пропуск mip-уровней, палитра и координаты Page;
+- запасной материал и выбор фазы;
+- последовательность RNG для FX и атмосферы;
+- переход на запасной набор возможностей устройства;
+- квантование времени симуляции.
+
+Для отладки требуется детерминированный захват кадра: состояние камеры,
+идентификаторы видимых объектов, список элементов отрисовки, ключи конвейера,
+матрицы и хэши промежуточных буферов.
diff --git a/docs/tomes/05-render.md b/docs/tomes/05-render.md
index 01038b7..dce7724 100644
--- a/docs/tomes/05-render.md
+++ b/docs/tomes/05-render.md
@@ -87,7 +87,9 @@ drivers и video modes, проверяет поддержку 3D, перевод
современная реализация заменяет наблюдаемую fixed-function семантику над этой
границей, а не должна эмулировать COM-объекты DirectX. Равный SHA-256 Ngi32 в
Частях 1 и 2 зафиксирован в [таблице оригинальных модулей](../reference/original-binaries.md);
-это поддерживает единый контракт Vulkan backend.
+это поддерживает единый контракт Vulkan backend. RVA относятся к конкретной
+сборке DLL: перед переносом адреса на другую установку сверяйте размер и
+SHA-256 соответствующего модуля в этой таблице.
```text
enumerate adapters and video modes
@@ -173,10 +175,11 @@ struct Node38 {
};
```
-`slot_index` образует матрицу `3 LOD x 5 groups`. Выбор выполняется как
-`slot_index[lod * 5 + group]`; `0xFFFF` означает отсутствие geometry для этой
-комбинации. Поле `parent_or_link` участвует в иерархии или связи узлов, но
-название остаётся описательным.
+`slot_index` хранит 3 состояния по 5 LOD: `slot_index[state * 5 + lod]`;
+`0xFFFF` означает отсутствие геометрии для комбинации. Публичные Rust-обёртки
+сохраняют исторические имена `Lod` и `Group`: аргумент `Lod` задаёт нативное
+состояние (0..2), а `Group` — нативный уровень LOD (0..4). Поле `parent_or_link`
+участвует в иерархии или связи узлов, но название остаётся описательным.
Type 2 начинается с header `0x8C`, затем содержит slots по 68 байт:
@@ -269,7 +272,7 @@ static_assert(sizeof(Batch20) == 20);
```c
for (Node& node : model.nodes) {
Matrix node_world = parent_world * local_transform(node);
- uint16_t sid = node.slot_index[lod * 5 + group];
+ uint16_t sid = node.slot_index[native_state * 5 + native_lod];
if (sid == 0xFFFF) continue;
Slot& slot = model.slots[sid];
@@ -306,17 +309,17 @@ render queues и сортировка, но связи данных остают
struct AnimKey24 {
float position[3];
float time;
+ int16_t qw;
int16_t qx;
int16_t qy;
int16_t qz;
- int16_t qw;
};
```
-Quaternion components декодируются как signed value / `32767.0`. На диске
-порядок полей XYZ-W, но runtime math использует логическое `[w, x, y, z]`.
-Безусловная современная нормализация после чтения не добавляется без parity
-проверки: она может изменить крайние кадры.
+На диске quaternion записан в порядке WXYZ; каждый signed component
+декодируется делением на `32767.0`. Декодер переставляет поля в portable
+`Pose.rotation = [x, y, z, w]` и оставляет их ненормализованными. Нормализация
+выполняется при подготовке pose к sampling.
Type 19 является массивом `uint16_t`; его `attr2` задаёт общее число кадров
timeline. Для конкретного узла `anim_map_start` указывает на блок длиной
@@ -622,14 +625,18 @@ Lightmap не является обычной diffuse texture. WEAR содерж
отдельным slot или texture stage. Замена lightmap предварительным умножением в
diffuse texture ломает LOD, atlas coordinates и динамическую модуляцию.
-Тени проходят отдельным render pass. Terrain содержит пути для теней зданий и
-роботов, ограничения максимального числа, detail level и smoothing. Реализованы
-native visibility по шести Vulkan clip-плоскостям с радиусом sphere +100,
-порядок кандидатов и лимит 20 до построения страниц, projected-size LOD,
-directional/point light contribution, native page layout, raster, smoothing и
-receiver projection с edge clipping. Unknown settings из `shade.cfg` читаются и
-сохраняются по именам, а не заменяются произвольными modern defaults. Для native ray query
-surface descriptor в `+0x10` требует наличия всех переданных bits, а descriptor
+Vulkan preview строит projected shadows для выбранных зданий и роботов,
+отбрасывает spheres вне шести clip-плоскостей с радиусом `sphere + 100` и
+сохраняет порядок видимых кандидатов; frame builder обрабатывает не более 20
+casters. Текущая реализация выделяет страницы в atlas `256 x 256` с 28 page
+slots и использует одну smoothing pass. Вызов preview передаёт `fade = 1.0` и
+LOD defaults: minimum LOD `1` и detail multiplier `1.0` отдельно для зданий и
+роботов. Rust-путь не загружает `shade.cfg`: эти значения задаются кодом, а
+параметры файла пока не влияют на preview. Проекция учитывает вклад
+directional и point lights.
+
+Receiver projection обрезает projected quad по рёбрам треугольника. Native ray
+query descriptor в `+0x10` требует наличия всех переданных bits, а descriptor
в `+0x14` исключает поверхности с любым переданным bit. Общий shadow query
использует full exclusion mask `0x2000`; actor four-ray path получает compact
mask `0x8008`, который landscape boundary разворачивает в full mask