From 8e0df74541d692bfa57e99a4741d410d058299c6 Mon Sep 17 00:00:00 2001 From: Daniel Seiller Date: Tue, 7 Mar 2023 20:05:56 +0100 Subject: [PATCH] Add WIP model parser (works for levels and Blender importer --- tools/remaster/scrap_parse/Cargo.lock | 802 ++++++++++++++++ tools/remaster/scrap_parse/Cargo.toml | 21 + tools/remaster/scrap_parse/blender_import.py | 507 ++++++++++ tools/remaster/scrap_parse/packed_loader.py | 103 ++ tools/remaster/scrap_parse/src/main.rs | 906 ++++++++++++++++++ .../remaster/scrap_parse/src/pixel_shader.rs | 26 + 6 files changed, 2365 insertions(+) create mode 100644 tools/remaster/scrap_parse/Cargo.lock create mode 100644 tools/remaster/scrap_parse/Cargo.toml create mode 100644 tools/remaster/scrap_parse/blender_import.py create mode 100644 tools/remaster/scrap_parse/packed_loader.py create mode 100644 tools/remaster/scrap_parse/src/main.rs create mode 100644 tools/remaster/scrap_parse/src/pixel_shader.rs diff --git a/tools/remaster/scrap_parse/Cargo.lock b/tools/remaster/scrap_parse/Cargo.lock new file mode 100644 index 0000000..90147a3 --- /dev/null +++ b/tools/remaster/scrap_parse/Cargo.lock @@ -0,0 +1,802 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" + +[[package]] +name = "array-init" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d62b7694a562cdf5a74227903507c56ab2cc8bdd1f781ed5cb4cf9c9f810bfc" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "binrw" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "272caaf6e0bfb7d508c0606e541e2c68f85c0d6352b62d0b299924eed59fe384" +dependencies = [ + "array-init", + "binrw_derive", + "bytemuck", +] + +[[package]] +name = "binrw_derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb4b28c1e534d96213c8966bb9240095757aa0909128985f97d16afd2e7257a8" +dependencies = [ + "either", + "owo-colors", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + +[[package]] +name = "bytemuck" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c041d3eab048880cb0b86b256447da3f18859a163c3b8d8893f4e6368abe6393" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-integer", + "num-traits", + "serde", + "time", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "chrono-humanize" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32dce1ea1988dbdf9f9815ff11425828523bd2a134ec0805d2ac8af26ee6096e" +dependencies = [ + "chrono", +] + +[[package]] +name = "clap" +version = "4.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0b0588d44d4d63a87dbd75c136c166bbfd9a86a31cb89e09906521c7d3f5e3" +dependencies = [ + "bitflags", + "clap_derive", + "clap_lex", + "is-terminal", + "once_cell", + "strsim", + "termcolor", +] + +[[package]] +name = "clap_derive" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "configparser" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5458d9d1a587efaf5091602c59d299696a3877a439c8f6d461a2d3cce11df87a" +dependencies = [ + "indexmap", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "cxx" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "flate2" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fs-err" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0845fa252299212f0389d64ba26f34fa32cfe41588355f21ed507c59a0f64541" + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[package]] +name = "iana-time-zone" +version = "0.1.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown", + "serde", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "is-terminal" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef" +dependencies = [ + "hermit-abi", + "io-lifetimes", + "rustix", + "windows-sys", +] + +[[package]] +name = "itoa" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" + +[[package]] +name = "link-cplusplus" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "modular-bitfield" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74" +dependencies = [ + "modular-bitfield-impl", + "static_assertions", +] + +[[package]] +name = "modular-bitfield-impl" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "os_str_bytes" +version = "6.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" + +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rhexdump" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e9af64574935e39f24d1c0313a997c8b880ca0e087c888bc6af8af31579847" + +[[package]] +name = "rustix" +version = "0.36.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "ryu" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" + +[[package]] +name = "scrap_parse" +version = "0.1.0" +dependencies = [ + "anyhow", + "binrw", + "chrono", + "chrono-humanize", + "clap", + "configparser", + "flate2", + "fs-err", + "indexmap", + "modular-bitfield", + "rhexdump", + "serde", + "serde_json", +] + +[[package]] +name = "scratch" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" + +[[package]] +name = "serde" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi", + "winapi", +] + +[[package]] +name = "unicode-ident" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasm-bindgen" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" diff --git a/tools/remaster/scrap_parse/Cargo.toml b/tools/remaster/scrap_parse/Cargo.toml new file mode 100644 index 0000000..bdb3554 --- /dev/null +++ b/tools/remaster/scrap_parse/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "scrap_parse" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.69" +binrw = "0.11.1" +chrono = { version = "0.4.23", features = ["serde"] } +chrono-humanize = "0.2.2" +clap = { version = "4.1.6", features = ["derive"] } +configparser = { version = "3.0.2", features = ["indexmap"] } +flate2 = "1.0.25" +fs-err = "2.9.0" +indexmap = { version = "1.9.2", features = ["serde"] } +modular-bitfield = "0.11.2" +rhexdump = "0.1.1" +serde = { version = "1.0.152", features = ["derive"] } +serde_json = { version = "1.0.93", features = ["unbounded_depth"] } diff --git a/tools/remaster/scrap_parse/blender_import.py b/tools/remaster/scrap_parse/blender_import.py new file mode 100644 index 0000000..eeae6cb --- /dev/null +++ b/tools/remaster/scrap_parse/blender_import.py @@ -0,0 +1,507 @@ +import bpy +import sys +import os +import re +import json +import gzip +import argparse +import shutil +from glob import glob +from mathutils import Vector +from pathlib import Path +import numpy as np +import itertools as ITT +from pprint import pprint +import bmesh +from bpy.props import StringProperty, BoolProperty +from bpy_extras.io_utils import ImportHelper +from bpy.types import Operator + +cmdline = None +if "--" in sys.argv: + args = sys.argv[sys.argv.index("--") + 1 :] + parser = argparse.ArgumentParser() + parser.add_argument("--save", action="store_true") + parser.add_argument("file_list", nargs="+") + cmdline = parser.parse_args(args) + + +def fix_pos(xyz): + x, y, z = xyz + return x, z, y + + +class ScrapImporter(object): + def __init__(self, options): + self.unhandled = set() + filepath = options.pop("filepath") + self.options = options + self.model_scale = 1000.0 + self.spawn_pos = {} + self.objects = {} + print("Loading", filepath) + with gzip.open(filepath, "r") as fh: + data = json.load(fh) + self.path = data.pop("path") + self.root = data.pop("root") + self.config = data.pop("config") + self.dummies = data.pop("dummies")["DUM"]["dummies"] + self.moredummies = data.pop("moredummies") + self.emi = data.pop("emi")["EMI"] + self.sm3 = data.pop("sm3")["SM3"] + + def make_empty(self, name, pos, rot=None): + empty = bpy.data.objects.new(name, None) + empty.empty_display_type = "PLAIN_AXES" + empty.empty_display_size = 0.1 + empty.location = Vector(pos).xzy / self.model_scale + if rot: + empty.rotation_euler = Vector(rot).xzy + empty.name = name + empty.show_name = True + bpy.context.scene.collection.objects.link(empty) + + def create_tracks(self): + points = {} + for dummy in self.dummies: + if dummy["name"].startswith("DM_Track"): + try: + _, name, idx = dummy["name"].split("_") + except ValueError: + continue + pos = Vector(dummy["pos"]).xzy / self.model_scale + points.setdefault(name, []).append((int(idx), pos)) + self.dummies=[d for d in self.dummies if not d["name"].startswith("DM_Track")] + for name, points in points.items(): + crv = bpy.data.curves.new(name, "CURVE") + crv.dimensions = "3D" + crv.path_duration = ( + (bpy.context.scene.frame_end - bpy.context.scene.frame_start) + 1 + ) + crv.twist_mode = "Z_UP" + crv.twist_smooth = 1.0 + spline = crv.splines.new(type="NURBS") + spline.points.add(len(points) - 1) + spline.use_endpoint_u = True + spline.use_cyclic_u = True + spline.use_endpoint_v = True + spline.use_cyclic_v = True + points.sort() + for p, (_, co) in zip(spline.points, points): + p.co = list(co) + [1.0] + obj = bpy.data.objects.new(name, crv) + bpy.context.scene.collection.objects.link(obj) + + def create_dummies(self): + for dummy in self.dummies: + self.make_empty(dummy["name"], dummy["pos"], dummy["rot"]) + if dummy["name"].startswith("DM_Player_Spawn"): + self.spawn_pos[dummy["name"]] = dummy["pos"] + for name, dummy in self.moredummies.items(): + if not "Pos" in dummy: + continue + pos = list(float(v) for v in dummy["Pos"]) + rot = [0, 0, 0] + if "Rot" in dummy: + rot = list(float(v) for v in dummy["Rot"]) + self.make_empty(name, pos, rot) + + def add_light(self, name, node): + light = bpy.data.lights.new(name, "POINT") + light.energy = 100 + r = node["unk_10"][0] / 255 # *(node['unk_10'][3]/255) + g = node["unk_10"][1] / 255 # *(node['unk_10'][3]/255) + b = node["unk_10"][2] / 255 # *(node['unk_10'][3]/255) + light.color = (r, g, b) + light = bpy.data.objects.new(name, light) + light.location = Vector(node["pos"]).xzy / self.model_scale + light.rotation_euler = Vector(node["rot"]).xzy + bpy.context.scene.collection.objects.link(light) + + def create_nodes(self): + for node in self.sm3["scene"]["nodes"]: + node_name = node["name"] + node = node.get("content", {}) + if not node: + continue + if node["type"] == "Camera": + print(f"CAM:{node_name}") + pprint(node) + elif node["type"] == "Light": + print(f"LIGHT:{node_name}") + # self.add_light(node_name, node) + + def run(self): + self.import_emi() + self.join_objects(self.options['merge_objects']) + if self.options['create_tracks']: + self.create_tracks() + if self.options['create_dummies']: + self.create_dummies() + if self.options['create_nodes']: + self.create_nodes() + if self.unhandled: + print("Unhandled textures:",self.unhandled) + + def join_objects(self, do_merge=False): + bpy.ops.object.select_all(action="DESELECT") + ctx = {} + for name, objs in self.objects.items(): + if len(objs) <= 1: + continue + ctx = { + "active_object": objs[0], + "object": objs[0], + "selected_objects": objs, + "selected_editable_objects": objs, + } + with bpy.context.temp_override(**ctx): + if do_merge: + bpy.ops.object.join() + objs[0].name=name + else: + coll=bpy.data.collections.new(name) + bpy.context.scene.collection.children.link(coll) + for n,obj in enumerate(objs): + obj.name=f"{name}_{n:04}" + coll.objects.link(obj) + bpy.ops.object.select_all(action="DESELECT") + + def import_emi(self): + mats = {0: None} + maps = {0: None} + for mat in self.emi["materials"]: + mats[mat[0]] = mat[1] + for tex_map in self.emi["maps"]: + maps[tex_map["key"]] = tex_map["data"] + for tri in self.emi["tri"]: + name = tri["name"] + if tri["data"]: + tris = tri["data"]["tris"] + for n, verts in enumerate( + [tri["data"]["verts_1"], tri["data"]["verts_2"]], 1 + ): + if not (tris and verts): + continue + self.create_mesh( + name=f"{name}_{n}", + verts=verts, + faces=tris, + m_map=(tri["data"]["map_key"], maps[tri["data"]["map_key"]]), + m_mat=(tri["data"]["mat_key"], mats[tri["data"]["mat_key"]]), + ) + + def normalize_path(self, path): + return path.replace("\\", os.sep).replace("/", os.sep) + + def resolve_path(self, path): + file_extensions = [".png", ".bmp", ".dds", ".tga", ".alpha.dds"] + root_path = Path(self.normalize_path(self.root).lower()) + start_folder = Path(self.normalize_path(self.path).lower()).parent + try: + texture_path = Path(self.config["model"]["texturepath"] + "/") + except KeyError: + texture_path = None + path = Path(path.replace("/", os.sep).lower()) + if texture_path: + folders = ITT.chain( + [start_folder], + start_folder.parents, + [texture_path], + texture_path.parents, + ) + else: + folders = ITT.chain([start_folder], start_folder.parents) + for folder in folders: + for suffix in file_extensions: + for dds in [".", "dds"]: + resolved_path = ( + root_path / folder / path.parent / dds / path.name + ).with_suffix(suffix) + if resolved_path.exists(): + return str(resolved_path) + print(f"Failed to resolve {path}") + return None + + def get_input(self, node, name, dtype): + return list(filter(lambda i: (i.type, i.name) == (dtype, name), node.inputs)) + + + def build_material(self, mat_key, mat_def): + mat_props = dict(m.groups() for m in re.finditer(r"\(\+(\w+)(?::(\w*))?\)",mat_key)) + for k,v in mat_props.items(): + mat_props[k]=v or True + skip_names = ["entorno", "noise_trazado", "noise128", "pbasicometal"] + overrides = { + "zonaautoiluminada-a.dds" : { + # "light" + }, + "flecha.000.dds": { + "shader": "hologram" + }, + "mayor.000.dds": { + "shader": "hologram" + }, + } + settings = { + "Emission Strength": 10.0, + "Specular": 0.0, + "Roughness": 0.0, + "Metallic": 0.0, + } + transparent_settings = { + "Transmission": 1.0, + "Transmission Roughness": 0.0, + "IOR": 1.0, + } + glass_settings = { + "Base Color": ( .8, .8, .8, 1.0), + "Metallic": 0.2, + "Roughness": 0.0, + "Specular": 0.2, + } + tex_slots=[ + "Base Color", + "Metallic", + None, # "Clearcoat" ? env map? + "Normal", + "Emission" + ] + + mat = bpy.data.materials.new(mat_key) + mat.use_nodes = True + node_tree = mat.node_tree + nodes = node_tree.nodes + imgs = {} + animated_textures={} + is_transparent = True + for slot,tex in zip(tex_slots,mat_def["maps"]): + if (slot is None) and tex: + self.unhandled.add(tex["texture"]) + print(f"Don't know what to do with {tex}") + if not (tex and slot): + continue + tex_file = self.resolve_path(tex["texture"]) + if tex_file is None: + continue + tex_name = os.path.basename(tex_file) + if ".000." in tex_name: + tex_files=glob(tex_file.replace(".000.",".*.")) + num_frames=len(tex_files) + animated_textures[slot]=num_frames + mat_props.update(overrides.get(tex_name,{})) + if any( + tex_name.find(fragment) != -1 + for fragment in skip_names + ): + continue + else: + is_transparent = False + imgs[slot]=bpy.data.images.load(tex_file) + for n in nodes: + nodes.remove(n) + out = nodes.new("ShaderNodeOutputMaterial") + out.name = "Output" + shader = nodes.new("ShaderNodeBsdfPrincipled") + is_transparent|=mat_props.get("shader")=="glass" + is_transparent|=mat_props.get("transp") in {"premult","filter"} + if is_transparent: + settings.update(transparent_settings) + if mat_props.get("shader")=="glass": + settings.update(glass_settings) + for name, value in settings.items(): + shader.inputs[name].default_value = value + sockets_used = set() + for socket,img in imgs.items(): + img_node = nodes.new("ShaderNodeTexImage") + img_node.name = img.name + img_node.image = img + if socket in animated_textures: + img.source="SEQUENCE" + num_frames=animated_textures[socket] + fps_div = 2 # TODO: read from .emi file + drv=img_node.image_user.driver_add("frame_offset") + drv.driver.type="SCRIPTED" + drv.driver.expression=f"((frame/{fps_div})%{num_frames})-1" + img_node.image_user.frame_duration=1 + img_node.image_user.use_cyclic=True + img_node.image_user.use_auto_refresh=True + tex_mix_node = nodes.new("ShaderNodeMixRGB") + tex_mix_node.blend_type = "MULTIPLY" + tex_mix_node.inputs["Fac"].default_value = 0.0 + node_tree.links.new( + img_node.outputs["Color"], tex_mix_node.inputs["Color1"] + ) + node_tree.links.new( + img_node.outputs["Alpha"], tex_mix_node.inputs["Color2"] + ) + imgs[socket] = tex_mix_node + output_node = tex_mix_node.outputs["Color"] + print(img.name, "->", socket) + if socket == "Normal": + normal_map = nodes.new("ShaderNodeNormalMap") + node_tree.links.new(output_node, normal_map.inputs["Color"]) + output_node = normal_map.outputs["Normal"] + normal_map.inputs["Strength"].default_value = 0.4 + node_tree.links.new(output_node, shader.inputs[socket]) + shader_out=shader.outputs["BSDF"] + if mat_props.get("shader")=="hologram": + mix_shader = nodes.new("ShaderNodeMixShader") + transp_shader = nodes.new("ShaderNodeBsdfTransparent") + mix_in_1 = self.get_input(mix_shader,"Shader","SHADER")[0] + mix_in_2 = self.get_input(mix_shader,"Shader","SHADER")[1] + node_tree.links.new(transp_shader.outputs["BSDF"], mix_in_1) + node_tree.links.new(shader.outputs["BSDF"], mix_in_2) + node_tree.links.new(imgs["Base Color"].outputs["Color"],mix_shader.inputs["Fac"]) + node_tree.links.new(imgs["Base Color"].outputs["Color"],shader.inputs["Emission"]) + shader.inputs["Emission Strength"].default_value=50.0 + shader_out=mix_shader.outputs["Shader"] + if settings.get("Transmission",0.0)>0.0: + light_path = nodes.new("ShaderNodeLightPath") + mix_shader = nodes.new("ShaderNodeMixShader") + transp_shader = nodes.new("ShaderNodeBsdfTransparent") + mix_in_1 = self.get_input(mix_shader,"Shader","SHADER")[0] + mix_in_2 = self.get_input(mix_shader,"Shader","SHADER")[1] + node_tree.links.new(shader.outputs["BSDF"], mix_in_1) + node_tree.links.new(transp_shader.outputs["BSDF"], mix_in_2) + node_tree.links.new(light_path.outputs["Is Shadow Ray"], mix_shader.inputs["Fac"]) + if mat_props.get("transp")=="filter" or mat_props.get("shader")=="glass": + node_tree.links.new(imgs["Base Color"].outputs["Color"],transp_shader.inputs["Color"]) + shader_out=mix_shader.outputs["Shader"] + node_tree.links.new(shader_out, out.inputs["Surface"]) + return mat + + def apply_maps(self, ob, m_mat, m_map): + mat_key, m_mat = m_mat + map_key, m_map = m_map # TODO?: MAP + if mat_key == 0: + return + mat_name = m_mat.get("name", f"MAT:{mat_key:08X}") + map_name = f"MAP:{map_key:08X}" + if mat_name not in bpy.data.materials: + ob.active_material = self.build_material(mat_name, m_mat) + else: + ob.active_material = bpy.data.materials[mat_name] + + def create_mesh(self, name, verts, faces, m_mat, m_map): + if not verts["inner"]: + return + me = bpy.data.meshes.new(name) + me.use_auto_smooth = True + pos = np.array([Vector(v["xyz"]).xzy for v in verts["inner"]["data"]]) + pos /= self.model_scale + me.from_pydata(pos, [], faces) + normals = [v["normal"] for v in verts["inner"]["data"]] + vcols = [v["diffuse"] for v in verts["inner"]["data"]] + if all(normals): + normals = np.array(normals) + me.vertices.foreach_set("normal", normals.flatten()) + if not me.vertex_colors: + me.vertex_colors.new() + for (vcol, vert) in zip(vcols, me.vertex_colors[0].data): + vert.color = [vcol["r"], vcol["g"], vcol["b"], vcol["a"]] + uvlayers = {} + tex = [f"tex_{n+1}" for n in range(8)] + for face in me.polygons: + for vert_idx, loop_idx in zip(face.vertices, face.loop_indices): + vert = verts["inner"]["data"][vert_idx] + for tex_name in tex: + if not vert[tex_name]: + continue + if not tex_name in uvlayers: + uvlayers[tex_name] = me.uv_layers.new(name=tex_name) + u, v = vert[tex_name] + uvlayers[tex_name].data[loop_idx].uv = (u, 1.0 - v) + bm = bmesh.new() + bm.from_mesh(me) + if self.options['remove_dup_verts']: + bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.0001) + bm.to_mesh(me) + me.update(calc_edges=True) + bm.clear() + for poly in me.polygons: + poly.use_smooth = True + ob = bpy.data.objects.new(name, me) + self.apply_maps(ob, m_mat, m_map) + bpy.context.scene.collection.objects.link(ob) + self.objects.setdefault(name, []).append(ob) + return ob + + +class Scrap_Load(Operator, ImportHelper): + + bl_idname = "scrap_utils.import_json" + bl_label = "Import JSON" + + filename_ext = ".json.gz" + filter_glob: StringProperty(default="*.json.gz", options={"HIDDEN"}) + + create_dummies: BoolProperty( + name="Import dummies", + default=True + ) + + create_nodes: BoolProperty( + name="Import nodes (lights, cameras, etc)", + default=True + ) + + create_tracks: BoolProperty( + name="Create track curves", + default=True + ) + + merge_objects: BoolProperty( + name="Merge objects by name", + default=False + ) + + remove_dup_verts: BoolProperty( + name="Remove overlapping vertices\nfor smoother meshes", + default=True + ) + + # remove_dup_verts: BoolProperty( + # name="Remove overlapping vertices for smoother meshes", + # default=False + # ) + + + def execute(self, context): + bpy.ops.preferences.addon_enable(module = "node_arrange") + bpy.ops.outliner.orphans_purge(do_recursive=True) + importer = ScrapImporter(self.as_keywords()) + importer.run() + dg = bpy.context.evaluated_depsgraph_get() + dg.update() + return {"FINISHED"} + + +def register(): + bpy.utils.register_class(Scrap_Load) + + +def unregister(): + bpy.utils.unregister_class(Scrap_Load) + + +if __name__ == "__main__": + if cmdline is None or not cmdline.file_list: + register() + bpy.ops.scrap_utils.import_json("INVOKE_DEFAULT") + else: + for file in cmdline.file_list: + bpy.context.preferences.view.show_splash = False + objs = bpy.data.objects + for obj in objs.keys(): + objs.remove(objs[obj], do_unlink=True) + cols=bpy.data.collections + for col in cols: + cols.remove(col) + importer = ScrapImporter(file) + importer.run() + if cmdline.save: + targetpath = Path(file).name.partition(".")[0] + ".blend" + targetpath = os.path.abspath(targetpath) + print("Saving", targetpath) + bpy.ops.wm.save_as_mainfile(filepath=targetpath) diff --git a/tools/remaster/scrap_parse/packed_loader.py b/tools/remaster/scrap_parse/packed_loader.py new file mode 100644 index 0000000..ffa6b34 --- /dev/null +++ b/tools/remaster/scrap_parse/packed_loader.py @@ -0,0 +1,103 @@ +bl_info = { + "name": "Riot Archive File (RAF)", + "blender": (2, 71, 0), + "location": "File > Import", + "description": "Import LoL data of an Riot Archive File", + "category": "Import-Export"} + + +import bpy +from io_scene_lolraf import raf_utils +from bpy.props import (StringProperty, BoolProperty, CollectionProperty, + IntProperty) + + +class ImportFilearchives(bpy.types.Operator): + """Import whole filearchives directory.""" + bl_idname = "import_scene.rafs" + bl_label = 'Import LoL filearchives' + + directory = StringProperty(name="'filearchives' folder", + subtype="DIR_PATH", options={'HIDDEN'}) + filter_folder = BoolProperty(default=True, options={'HIDDEN'}) + filter_glob = StringProperty(default="", options={'HIDDEN'}) + + def invoke(self, context, event): + context.window_manager.fileselect_add(self) + return {'RUNNING_MODAL'} + + def execute(self, context): + # TODO: Validate filepath + bpy.ops.ui.raf_browser('INVOKE_DEFAULT',filepath=self.directory) + return {'FINISHED'} + + +class RAFEntry(bpy.types.PropertyGroup): + name = bpy.props.StringProperty() + selected = bpy.props.BoolProperty(name="") + + +archive = None +class RAFBrowser(bpy.types.Operator): + bl_idname = "ui.raf_browser" + bl_label = "RAF-browser" + bl_options = {'INTERNAL'} + + filepath = StringProperty() + current_dir = CollectionProperty(type=RAFEntry) + selected_index = IntProperty(default=0) + + def invoke(self, context, event): + global archive + archive = raf_utils.RAFArchive(self.filepath) + return context.window_manager.invoke_props_dialog(self) + + def draw(self, context): + if self.selected_index != -1: + print("new selected_index: " + str(self.selected_index)) + global archive + # TODO: change current directory of archive + self.current_dir.clear() + for dir in archive.current_dir(): + entry = self.current_dir.add() + entry.name = dir + self.selected_index = -1 + self.layout.template_list("RAFDirList", "", self, "current_dir", self, "selected_index") + + def execute(self, context): + print("execute") + return {'FINISHED'} + + +class RAFDirList(bpy.types.UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname): + operator = data + raf_entry = item + + if self.layout_type in {'DEFAULT', 'COMPACT'}: + layout.prop(raf_entry, "name", text="", emboss=False, icon_value=icon) + layout.prop(raf_entry, "selected") + elif self.layout_type in {'GRID'}: + layout.alignment = 'CENTER' + layout.label(text="", icon_value=icon) + + +def menu_func_import(self, context): + self.layout.operator(ImportFilearchives.bl_idname, text="LoL Filearchives") + + +def register(): + bpy.utils.register_module(__name__) + bpy.types.INFO_MT_file_import.append(menu_func_import) + + +def unregister(): + bpy.utils.unregister_module(__name__) + bpy.types.INFO_MT_file_import.remove(menu_func_import) + + +if __name__ == "__main__": + import imp + imp.reload(raf_utils) + bpy.utils.register_module(__name__) + diff --git a/tools/remaster/scrap_parse/src/main.rs b/tools/remaster/scrap_parse/src/main.rs new file mode 100644 index 0000000..8d41f58 --- /dev/null +++ b/tools/remaster/scrap_parse/src/main.rs @@ -0,0 +1,906 @@ +#![allow(clippy::upper_case_acronyms, non_camel_case_types)] +use anyhow::{anyhow, bail, Result}; +use binrw::args; +use binrw::prelude::*; +use binrw::until_exclusive; +use chrono::{DateTime, NaiveDateTime, Utc}; +use clap::Parser; +use configparser::ini::Ini; +use flate2::write::GzEncoder; +use flate2::Compression; +use fs_err as fs; +use indexmap::IndexMap; +use modular_bitfield::bitfield; +use modular_bitfield::specifiers::B2; +use modular_bitfield::specifiers::B4; +use modular_bitfield::BitfieldSpecifier; +use serde::Serialize; +use serde_json::Map; +use serde_json::Value; +use std::collections::HashMap; +use std::fmt::Debug; +use std::fs::File; +use std::io::{BufReader, Read, Seek}; +use std::path::Path; +use std::path::PathBuf; + +#[binread] +#[derive(Serialize, Debug)] +#[br(import(msg: &'static str))] +struct Unparsed { + #[br(count=SIZE, try_map=|data: Vec| Err(anyhow!("Unparsed data: {}\n{}", msg, rhexdump::hexdump(&data))))] + data: (), +} + +#[binread] +#[derive(Serialize, Debug)] +struct RawTable { + num_entries: u32, + #[br(assert(entry_size==SIZE))] + entry_size: u32, + #[br(count=num_entries, args {inner: args!{count: entry_size.try_into().unwrap()}})] + data: Vec>, +} + +#[binread] +#[derive(Serialize, Debug)] +struct Table BinRead = ()> + 'static> { + num_entries: u32, + entry_size: u32, + #[br(count=num_entries)] + data: Vec, +} + +// impl BinRead = ()>> Serialize for Table where T: Serialize { +// fn serialize(&self, serializer: S) -> std::result::Result +// where +// S: serde::Serializer { +// self.data.serialize(serializer) +// } +// } + +#[binread] +struct Optional BinRead = ()>> { + #[br(temp)] + has_value: u32, + #[br(if(has_value!=0))] + value: Option, +} + +impl BinRead = ()> + Debug> Debug for Optional +where + T: Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.value.fmt(f) + } +} + +impl BinRead = ()> + std::ops::Deref> std::ops::Deref for Optional { + type Target = Option; + + fn deref(&self) -> &Self::Target { + &self.value + } +} + +impl BinRead = ()> + Serialize> Serialize for Optional { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + self.value.serialize(serializer) + } +} + +#[binread] +#[derive(Serialize, Debug)] +struct Chunk { + #[br(map=|c:[u8;4]| c.into_iter().map(|v| v as char).collect())] + magic: Vec, + size: u32, + #[br(temp,count=size)] + data: Vec, +} + +#[binread] +struct PascalString { + #[br(temp)] + length: u32, + #[br(count=length, map=|bytes: Vec| { + String::from_utf8_lossy(&bytes.iter().copied().take_while(|&v| v!=0).collect::>()).into_owned() + })] + string: String, +} + +impl Serialize for PascalString { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + self.string.serialize(serializer) + } +} + +impl Debug for PascalString { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.string.fmt(f) + } +} + +#[binread] +#[derive(Debug, Serialize)] +struct IniSection { + #[br(temp)] + num_lines: u32, + #[br(count=num_lines)] + sections: Vec, +} + +#[binread] +#[br(magic = b"INI\0")] +#[derive(Debug)] +struct INI { + size: u32, + #[br(temp)] + num_sections: u32, + #[br(count=num_sections)] + sections: Vec, +} + +impl Serialize for INI { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + let blocks: Vec = self + .sections + .iter() + .flat_map(|s| s.sections.iter()) + .map(|s| s.string.clone()) + .collect(); + Ini::new().read(blocks.join("\n")).serialize(serializer) + } +} + +#[binread] +#[derive(Debug, Serialize, Clone)] +struct RGBA { + r: u8, + g: u8, + b: u8, + a: u8, +} + +#[binread] +#[derive(Debug, Serialize, Clone)] +#[br(import(n_dims: usize))] +struct TexCoords(#[br(count=n_dims)] Vec); + +#[binread] +#[derive(Debug, Serialize, Clone)] +#[br(import(vert_fmt: FVF))] +// https://github.com/elishacloud/dxwrapper/blob/23ffb74c4c93c4c760bb5f1de347a0b039897210/ddraw/IDirect3DDeviceX.cpp#L2642 +struct Vertex { + xyz: [f32; 3], + // #[br(if(vert_fmt.pos()==Pos::XYZRHW))] // seems to be unused + // rhw: Option, + #[br(if(vert_fmt.normal()))] + normal: Option<[f32; 3]>, + #[br(if(vert_fmt.point_size()))] + point_size: Option<[f32; 3]>, + #[br(if(vert_fmt.diffuse()))] + diffuse: Option, + #[br(if(vert_fmt.specular()))] + specular: Option, + #[br(if(vert_fmt.tex_count()>=1), args (vert_fmt.tex_dims(0),))] + tex_1: Option, + #[br(if(vert_fmt.tex_count()>=2), args (vert_fmt.tex_dims(1),))] + tex_2: Option, + #[br(if(vert_fmt.tex_count()>=3), args (vert_fmt.tex_dims(2),))] + tex_3: Option, + #[br(if(vert_fmt.tex_count()>=4), args (vert_fmt.tex_dims(3),))] + tex_4: Option, + #[br(if(vert_fmt.tex_count()>=5), args (vert_fmt.tex_dims(4),))] + tex_5: Option, + #[br(if(vert_fmt.tex_count()>=6), args (vert_fmt.tex_dims(5),))] + tex_6: Option, + #[br(if(vert_fmt.tex_count()>=7), args (vert_fmt.tex_dims(6),))] + tex_7: Option, + #[br(if(vert_fmt.tex_count()>=8), args (vert_fmt.tex_dims(7),))] + tex_8: Option, +} + +#[derive(Debug, Serialize, PartialEq, Eq, BitfieldSpecifier)] +#[bits = 3] +enum Pos { + XYZ, + XYZRHW, + XYZB1, + XYZB2, + XYZB3, + XYZB4, + XYZB5, +} + +#[bitfield] +#[repr(u32)] +#[derive(Debug, Serialize, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct FVF { + reserved_1: bool, + pos: Pos, + normal: bool, + point_size: bool, + diffuse: bool, + specular: bool, + tex_count: B4, + tex_1: B2, + tex_2: B2, + tex_3: B2, + tex_4: B2, + tex_5: B2, + tex_6: B2, + tex_7: B2, + tex_8: B2, + rest: B4, +} + +impl FVF { + fn tex_dims(&self, tex: u8) -> usize { + let tex: u8 = match tex { + 0 => self.tex_1(), + 1 => self.tex_2(), + 2 => self.tex_3(), + 3 => self.tex_4(), + 4 => self.tex_5(), + 5 => self.tex_6(), + 6 => self.tex_7(), + 7 => self.tex_8(), + _ => unreachable!(), + }; + match tex { + 0 => 2, + 1 => 3, + 2 => 4, + 3 => 1, + _ => unreachable!(), + } + } + + fn num_w(&self) -> usize { + use Pos::*; + match self.pos() { + XYZ | XYZRHW => 0, + XYZB1 => 1, + XYZB2 => 2, + XYZB3 => 3, + XYZB4 => 4, + XYZB5 => 5, + } + } +} + +fn vertex_size_from_id(fmt_id: u32) -> Result { + let fmt_size = match fmt_id { + 0 => 0x0, + 1 | 8 | 10 => 0x20, + 2 => 0x28, + 3 | 0xd => 0x1c, + 4 | 7 => 0x24, + 5 => 0x2c, + 6 => 0x34, + 0xb => 4, + 0xc => 0x18, + 0xe => 0x12, + 0xf | 0x10 => 0x16, + 0x11 => 0x1a, + other => bail!("Invalid vertex format id: {other}"), + }; + Ok(fmt_size) +} + +fn vertex_format_from_id(fmt_id: u32, fmt: u32) -> Result { + let fvf = match fmt_id { + 0 => 0x0, + 1 => 0x112, + 2 => 0x212, + 3 => 0x1c2, + 4 => 0x116, + 5 => 0x252, + 6 => 0x352, + 7 => 0x152, + 8 => 0x1c4, + 10 => 0x242, + other => bail!("Invalid vertex format id: {other}"), + }; + if fvf != fmt { + bail!("Vertex format mismatch: {fvf}!={fmt}"); + } + Ok(FVF::from(fvf)) +} + +#[binread] +#[br(import(fmt_id: u32))] +#[derive(Debug, Serialize, Clone)] +struct LFVFInner { + #[br(try_map=|v: u32| vertex_format_from_id(fmt_id,v))] + vert_fmt: FVF, + #[br(assert(vert_size==vertex_size_from_id(fmt_id).unwrap()))] + vert_size: u32, + num_verts: u32, + #[br(count=num_verts, args {inner: (vert_fmt,)})] + data: Vec, +} + +#[binread] +#[br(magic = b"LFVF")] +#[derive(Debug, Serialize)] +struct LFVF { + size: u32, + #[br(assert(version==1,"invalid LFVF version"))] + version: u32, + #[br(assert((0..=0x11).contains(&fmt_id),"invalid LFVF format_id"))] + fmt_id: u32, + #[br(if(fmt_id!=0),args(fmt_id))] + inner: Option, +} + +#[binread] +#[br(magic = b"MD3D")] +#[derive(Debug, Serialize)] +struct MD3D { + // TODO: mesh + size: u32, + #[br(assert(version==1,"Invalid MD3D version"))] + version: u32, + name: PascalString, + num_tris: u32, + #[br(assert(tri_size==6,"Invalid MD3D tri size"))] + tri_size: u32, + #[br(count=num_tris)] + tris: Vec<[u16; 3]>, + mesh_data: LFVF, + unk_table_1: RawTable<2>, + // TODO: + // == + // unk_t1_count: u32, + // #[br(assert(unk_t1_size==2))] + // unk_t1_size: u32, + // #[br(count=unk_t1_count)] + // unk_t1_list: Vec, + // // == + // unk_t2_count: u32, + // #[br(assert(unk_t1_size==2))] + // unk_t2_size: u32, + // #[br(count=unk_t1_count)] + // unk_t2_list: Vec, +} + +#[binread] +#[derive(Debug, Serialize)] +#[serde(tag = "type")] +enum NodeData { + #[br(magic = 0x0u32)] + Null, + #[br(magic = 0xa1_00_00_01_u32)] + TriangleMesh, // Empty? + #[br(magic = 0xa1_00_00_02_u32)] + Mesh(MD3D), + #[br(magic = 0xa2_00_00_04_u32)] + Camera(CAM), + #[br(magic = 0xa3_00_00_08_u32)] + Light(LUZ), + #[br(magic = 0xa4_00_00_10_u32)] + Ground(SUEL), + #[br(magic = 0xa5_00_00_20_u32)] + SisPart(Unparsed<0x10>), // TODO: Particles + #[br(magic = 0xa6_00_00_40_u32)] + Graphic3D(SPR3), + #[br(magic = 0xa6_00_00_80_u32)] + Flare(Unparsed<0x10>), // TODO: LensFlare? + #[br(magic = 0xa7_00_01_00u32)] + Portal(PORT), +} + +#[binread] +#[br(magic = b"SPR3")] +#[derive(Debug, Serialize)] +struct SPR3 { + size: u32, + #[br(assert(version==1,"Invalid SPR3 version"))] + version: u32, + pos: [f32; 3], + unk_1: [u8; 8], + name_1: PascalString, + name_2: PascalString, + unk_2: u32, +} + +#[binread] +#[br(magic = b"SUEL")] +#[derive(Debug, Serialize)] +struct SUEL { + size: u32, + #[br(assert(version==1,"Invalid SUEL version"))] + version: u32, + bbox: [[f32; 3]; 2], + pos: [f32; 3], + unk_3: [u8; 4], + num_nodes: u32, + unk_4: [u8; 4], + bbox_2: [[f32; 3]; 2], +} + +#[binread] +#[br(magic = b"CAM\0")] +#[derive(Debug, Serialize)] +struct CAM { + size: u32, + #[br(assert(version==1,"Invalid CAM version"))] + version: u32, + unk_1: [f32; 3], + origin: [f32; 3], + destination: [f32; 3], + unk_4: [u8; 4], + unk_5: [u8; 4], + unk_6: [u8; 4], + unk_7: [u8; 4], + unk_8: [u8; 4], + unk_9: [u8; 4], + unk_10: [u8; 4], + unk_11: [u8; 4], +} + +#[binread] +#[br(magic = b"LUZ\0")] +#[derive(Debug, Serialize)] +struct LUZ { + size: u32, + #[br(assert(version==1,"Invalid LUZ version"))] + version: u32, + col: u32, + brightness: u32, + unk_3: u8, + pos: [f32; 3], + rot: [f32; 3], + unk_6: [u8; 8], + unk_7: [u8; 4], + unk_8: [u8; 4], + unk_9: [u8; 4], + unk_10: [u8; 4], + unk_11: [u8; 4], + unk_12: [u8; 4], + unk_13: u32, +} + +#[binread] +#[br(magic = b"PORT")] +#[derive(Debug, Serialize)] +struct PORT { + size: u32, + #[br(assert(version==1,"Invalid PORT version"))] + version: u32, + width: u32, + height: u32, + sides: [u32; 2], +} + +#[binread] +#[derive(Debug, Serialize)] +struct Node { + unk_f17_0x44: u32, + unk_f18_0x48: u32, + unk_f19_0x4c: u32, + flags: u32, + unk_f20_0x50: u32, + name: PascalString, + parent: PascalString, + unk_f7_0x1c: [f32; 3], // 0xc + unk_f10_0x28: [f32; 4], // 0x10 + unk_f14_0x38: f32, // 0x4 + unk_f23_0x5c: [[f32; 4]; 4], // 0x40 4x4 Matrix + unk_f39_0x9c: [[f32; 4]; 4], // 0x40 4x4 Matrix + unk_f55_0xdc: [f32; 4], // 0x10 Vector? + unk_f59_0xec: [f32; 3], // 0xc Vector? + node_info: Optional, + content: Optional, +} + +#[binread] +#[br(magic = b"MAP\0")] +#[derive(Debug, Serialize)] +struct MAP { + size: u32, + #[br(assert((2..=3).contains(&version),"invalid MAP version"))] + version: u32, + texture: PascalString, + unk_1: [u8; 7], + unk_bbox: [[f32; 2]; 2], + unk_2: f32, + #[br(if(version==3))] + unk_3: Option<[u8; 0xc]>, +} + +#[binread] +#[br(magic = b"MAT\0")] +#[derive(Debug, Serialize)] +struct MAT { + size: u32, + #[br(assert((1..=3).contains(&version),"invalid MAT version"))] + version: u32, + #[br(if(version>1))] + name: Option, + unk_f: [RGBA; 7], + unk_data: [RGBA; 0x18 / 4], + maps: [Optional; 5], // Base Color, Metallic?, ???, Normal, Emission +} + +#[binread] +#[br(magic = b"SCN\0")] +#[derive(Debug, Serialize)] +struct SCN { + // 0x650220 + size: u32, + #[br(temp,assert(version==1))] + version: u32, + model_name: PascalString, + node_name: PascalString, + node_props: Optional, + unk_f_1: [f32; (8 + 8) / 4], + unk_1: [f32; 0x18 / 4], + unk_f_2: f32, + user_props: Optional, + num_materials: u32, + #[br(count=num_materials)] + mat: Vec, + #[br(temp,assert(unk_3==1))] + unk_3: u32, + num_nodes: u32, + #[br(count = num_nodes)] // 32 + nodes: Vec, + ani: Optional, // TODO:? +} + +fn convert_timestamp(dt: u32) -> Result> { + let Some(dt) = NaiveDateTime::from_timestamp_opt(dt.into(),0) else { + bail!("Invalid timestamp"); + }; + Ok(DateTime::from_utc(dt, Utc)) +} + +#[binread] +#[derive(Debug, Serialize)] +struct VertexAnim { + n_tr: u32, + maybe_duration: f32, + #[br(count=n_tr)] + tris: Vec<[u8; 3]>, +} + +#[binread] +#[br(magic = b"EVA\0")] +#[derive(Debug, Serialize)] +struct EVA { + size: u32, + #[br(assert(version==1,"Invalid EVA version"))] + version: u32, + num_verts: u32, + #[br(count=num_verts)] + verts: Vec>, +} + +#[binread] +#[br(magic = b"NAM\0")] +#[derive(Debug, Serialize)] +struct NAM { + size: u32, + #[br(assert(version==1))] + version: u32, + primer_frames: u32, + frames: u32, + #[br(assert(flags&0xffffef60==0,"Invalid NAM flags"))] + flags: u32, + #[br(assert(opt_flags&0xfff8==0,"Invalid NAM opt_flags"))] + opt_flags: u32, + #[br(assert(stm_flags&0xfff8==0,"Invalid NAM stm_flags"))] + stm_flags: u32, + #[br(map=|_:()| flags&(opt_flags|0x8000)&stm_flags)] + combined_flags: u32, + #[br(if(combined_flags&0x1!=0))] + unk_flags_1: Option, + #[br(if(combined_flags&0x2!=0))] + unk_flags_2: Option, + #[br(if(combined_flags&0x4!=0))] + unk_flags_3: Option, + #[br(if(combined_flags&0x8!=0))] + unk_flags_4: Option, + #[br(if(combined_flags&0x10!=0))] + unk_flags_5: Option, + #[br(if(combined_flags&0x80!=0))] + unk_flags_6: Option, + #[br(if(flags&0x1000!=0))] + eva: Option, +} + +#[binread] +#[br(magic = b"NABK")] +#[derive(Debug, Serialize)] +struct NABK { + size: u32, + #[br(temp,count=size)] + data: Vec, +} + +#[binread] +#[br(magic = b"ANI\0")] +#[derive(Debug, Serialize)] +struct ANI { + size: u32, + #[br(assert(version==2, "Invalid ANI version"))] + version: u32, + fps: f32, + unk_1: u32, + unk_2: u32, + num_objects: u32, + unk_flags: u32, + num: u32, + #[br(temp,count=num)] + data: Vec, + nabk: NABK, + #[br(count=num_objects)] + nam: Vec, +} + +#[binread] +#[br(magic = b"SM3\0")] +#[derive(Debug, Serialize)] +struct SM3 { + size: u32, + #[br(temp,assert(const_1==0x6515f8,"Invalid timestamp"))] + const_1: u32, + #[br(try_map=convert_timestamp)] + time_1: DateTime, + #[br(try_map=convert_timestamp)] + time_2: DateTime, + scene: SCN, +} + +#[binread] +#[br(magic = b"CM3\0")] +#[derive(Debug, Serialize)] +struct CM3 { + size: u32, + #[br(temp,assert(const_1==0x6515f8,"Invalid timestamp"))] + const_1: u32, + #[br(try_map=convert_timestamp)] + time_1: DateTime, + #[br(try_map=convert_timestamp)] + time_2: DateTime, + scene: SCN, +} + +#[binread] +#[derive(Debug, Serialize)] +struct Dummy { + name: PascalString, + pos: [f32; 3], + rot: [f32; 3], + info: Optional, + has_next: u32, +} + +#[binread] +#[br(magic = b"DUM\0")] +#[derive(Debug, Serialize)] +struct DUM { + size: u32, + #[br(assert(version==1, "Invalid DUM version"))] + version: u32, + num_dummies: u32, + unk_1: u32, + #[br(count=num_dummies)] + dummies: Vec, +} + +#[binread] +#[br(magic = b"QUAD")] +#[derive(Debug, Serialize)] +struct QUAD { + size: u32, + #[br(assert(version==1, "Invalid QUAD version"))] + version: u32, + mesh: u32, + table: Table, + f_4: [f32; 4], + num_children: u32, + #[br(count=num_children)] + children: Vec, +} + +#[binread] +#[br(magic = b"CMSH")] +#[derive(Debug, Serialize)] +struct CMSH { + size: u32, + #[br(assert(version==2, "Invalid CMSH version"))] + version: u32, + #[br(assert(collide_mesh_size==0x34, "Invalid collision mesh size"))] + collide_mesh_size: u32, + name: PascalString, + unk_1: u16, + sector: u16, + unk_2: u16, + index: u8, + unk_4: u8, + bbox_1: [[f32; 3]; 2], + #[br(temp)] + t_1: Table<[f32; 3]>, + #[br(temp)] + t_2: RawTable<0x1c>, +} + +#[binread] +#[br(magic = b"AMC\0")] +#[derive(Debug, Serialize)] +struct AMC { + size: u32, + #[br(assert(version==100,"Invalid AMC version"))] + version: u32, + #[br(assert(version_code==0, "Invalid AMC version_code"))] + version_code: u32, + bbox_1: [[f32; 3]; 2], + scale: f32, + bbox_2: [[f32; 3]; 2], + unk: [f32; 3], + cmsh: [CMSH; 2], + num_sectors: u32, + #[br(count=num_sectors)] + sector_col: Vec<[CMSH; 2]>, + unk_num_1: u32, + unk_num_2: u32, + unk_f: [f32; 4], + num_quads: u32, + #[br(count=num_quads)] + quads: Vec, +} + +#[binread] +#[br(import(version: u32))] +#[derive(Debug, Serialize)] +struct TriV104 { + #[br(if(version>=0x69))] + name_2: Option, + mat_key: u32, + map_key: u32, + num_tris: u32, + #[br(count=num_tris)] + tris: Vec<[u16; 3]>, + verts_1: LFVF, + verts_2: LFVF, +} + +#[binread] +#[br(magic = b"TRI\0", import(version: u32))] +#[derive(Debug, Serialize)] +struct TRI { + size: u32, + unk_int: u32, + name: PascalString, + unk_int_2: u32, // if 0xffffffff sometimes TriV104 has no name_2 field + #[br(args(version))] + data: TriV104, +} + +#[binread] +#[derive(Debug, Serialize)] +struct EMI_Textures { + key: u32, + #[br(if(key!=0))] + data: Option<(PascalString, u32, PascalString)>, +} + +#[binread] +#[br(magic = b"EMI\0")] +#[derive(Debug, Serialize)] +struct EMI { + size: u32, + #[br(assert((103..=105).contains(&version)))] + version: u32, + num_materials: u32, + #[br(count=num_materials)] + materials: Vec<(u32, MAT)>, + #[br(parse_with = until_exclusive(|v: &EMI_Textures| v.key==0))] + maps: Vec, + num_lists: u32, + #[br(count=num_lists,args{inner: (version,)})] + tri: Vec, +} + +#[binread] +#[derive(Debug, Serialize)] +enum Data { + SM3(SM3), + CM3(CM3), + DUM(DUM), + AMC(AMC), + EMI(EMI), +} + +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +struct Args { + root: PathBuf, + path: PathBuf, +} + +fn parse_file(path: &PathBuf) -> Result { + let mut rest_size = 0; + let mut fh = BufReader::new(fs::File::open(path)?); + let ret = fh.read_le()?; + let pos = fh + .stream_position() + .unwrap_or(0) + .try_into() + .unwrap_or(u32::MAX); + println!("Read {} bytes from {}", pos, path.display()); + let mut buffer = [0u8; 0x1000]; + if let Ok(n) = fh.read(&mut buffer) { + if n != 0 { + println!("Rest:\n{}", rhexdump::hexdump_offset(&buffer[..n], pos)); + } + }; + while let Ok(n) = fh.read(&mut buffer) { + if n == 0 { + break; + } + rest_size += n; + } + println!("+{rest_size} unparsed bytes"); + Ok(ret) +} + +fn load_ini(path: &PathBuf) -> IndexMap>> { + Ini::new().load(path).unwrap_or_default() +} + +fn load_data(root: &Path, path: &Path) -> Result { + let full_path = &root.join(path); + let emi_path = full_path.join("map").join("map3d.emi"); + let sm3_path = emi_path.with_extension("sm3"); + let dum_path = emi_path.with_extension("dum"); + let config_file = emi_path.with_extension("ini"); + let moredummies = emi_path.with_file_name("moredummies").with_extension("ini"); + let mut data = serde_json::to_value(HashMap::<(), ()>::default())?; + data["config"] = serde_json::to_value(load_ini(&config_file))?; + data["moredummies"] = serde_json::to_value(load_ini(&moredummies))?; + data["emi"] = serde_json::to_value(parse_file(&emi_path)?)?; + data["sm3"] = serde_json::to_value(parse_file(&sm3_path)?)?; + data["dummies"] = serde_json::to_value(parse_file(&dum_path)?)?; + data["path"] = serde_json::to_value(path)?; + data["root"] = serde_json::to_value(root)?; + Ok(data) +} + +fn main() -> Result<()> { + let args = Args::try_parse()?; + let out_path = PathBuf::from( + args.path + .components() + .last() + .unwrap() + .as_os_str() + .to_string_lossy() + .into_owned(), + ) + .with_extension("json.gz"); + let full_path = &args.root.join(&args.path); + let data = if full_path.is_dir() { + load_data(&args.root, &args.path)? + } else { + serde_json::to_value(parse_file(full_path)?)? + }; + let mut dumpfile = GzEncoder::new(File::create(&out_path)?, Compression::best()); + serde_json::to_writer_pretty(&mut dumpfile, &data)?; + println!("Wrote {path}", path = out_path.display()); + Ok(()) +} diff --git a/tools/remaster/scrap_parse/src/pixel_shader.rs b/tools/remaster/scrap_parse/src/pixel_shader.rs new file mode 100644 index 0000000..7bc87a3 --- /dev/null +++ b/tools/remaster/scrap_parse/src/pixel_shader.rs @@ -0,0 +1,26 @@ +// #[derive(Debug)] +// enum VecArg { +// Tex(f32,f32,f32,f32), +// Reg(f32,f32,f32,f32), +// Ver(f32,f32,f32,f32), +// Col(f32,f32,f32,f32), +// Vec(f32,f32,f32,f32), +// } + +// struct Arg { +// arg: VecArg, +// idx: Option +// } + +// #[derive(Debug)] +// enum Inst { +// Tex(Arg), +// Add(Arg,Arg,Arag), +// Sub(Arg,Arg,Arag), +// Mul(Arg,Arg,Arag), +// Mov(Arg,Arg), +// } + +fn parse(path: &str) { + +} \ No newline at end of file