Add WIP model parser (works for levels and Blender importer

This commit is contained in:
Daniel S. 2023-03-07 20:05:56 +01:00
parent 45f38885ec
commit 8e0df74541
6 changed files with 2365 additions and 0 deletions

802
tools/remaster/scrap_parse/Cargo.lock generated Normal file
View file

@ -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"

View file

@ -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"] }

View file

@ -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)

View file

@ -0,0 +1,103 @@
bl_info = {
"name": "Riot Archive File (RAF)",
"blender": (2, 71, 0),
"location": "File &gt; 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__)

View file

@ -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<const SIZE: u64> {
#[br(count=SIZE, try_map=|data: Vec<u8>| Err(anyhow!("Unparsed data: {}\n{}", msg, rhexdump::hexdump(&data))))]
data: (),
}
#[binread]
#[derive(Serialize, Debug)]
struct RawTable<const SIZE: u32> {
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<Vec<u8>>,
}
#[binread]
#[derive(Serialize, Debug)]
struct Table<T: for<'a> BinRead<Args<'a> = ()> + 'static> {
num_entries: u32,
entry_size: u32,
#[br(count=num_entries)]
data: Vec<T>,
}
// impl<T: for<'a> BinRead<Args<'a> = ()>> Serialize for Table<T> where T: Serialize {
// fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
// where
// S: serde::Serializer {
// self.data.serialize(serializer)
// }
// }
#[binread]
struct Optional<T: for<'a> BinRead<Args<'a> = ()>> {
#[br(temp)]
has_value: u32,
#[br(if(has_value!=0))]
value: Option<T>,
}
impl<T: for<'a> BinRead<Args<'a> = ()> + Debug> Debug for Optional<T>
where
T: Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.value.fmt(f)
}
}
impl<T: for<'a> BinRead<Args<'a> = ()> + std::ops::Deref> std::ops::Deref for Optional<T> {
type Target = Option<T>;
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl<T: for<'a> BinRead<Args<'a> = ()> + Serialize> Serialize for Optional<T> {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
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<char>,
size: u32,
#[br(temp,count=size)]
data: Vec<u8>,
}
#[binread]
struct PascalString {
#[br(temp)]
length: u32,
#[br(count=length, map=|bytes: Vec<u8>| {
String::from_utf8_lossy(&bytes.iter().copied().take_while(|&v| v!=0).collect::<Vec<u8>>()).into_owned()
})]
string: String,
}
impl Serialize for PascalString {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
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<PascalString>,
}
#[binread]
#[br(magic = b"INI\0")]
#[derive(Debug)]
struct INI {
size: u32,
#[br(temp)]
num_sections: u32,
#[br(count=num_sections)]
sections: Vec<IniSection>,
}
impl Serialize for INI {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let blocks: Vec<String> = 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<f32>);
#[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<f32>,
#[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<RGBA>,
#[br(if(vert_fmt.specular()))]
specular: Option<RGBA>,
#[br(if(vert_fmt.tex_count()>=1), args (vert_fmt.tex_dims(0),))]
tex_1: Option<TexCoords>,
#[br(if(vert_fmt.tex_count()>=2), args (vert_fmt.tex_dims(1),))]
tex_2: Option<TexCoords>,
#[br(if(vert_fmt.tex_count()>=3), args (vert_fmt.tex_dims(2),))]
tex_3: Option<TexCoords>,
#[br(if(vert_fmt.tex_count()>=4), args (vert_fmt.tex_dims(3),))]
tex_4: Option<TexCoords>,
#[br(if(vert_fmt.tex_count()>=5), args (vert_fmt.tex_dims(4),))]
tex_5: Option<TexCoords>,
#[br(if(vert_fmt.tex_count()>=6), args (vert_fmt.tex_dims(5),))]
tex_6: Option<TexCoords>,
#[br(if(vert_fmt.tex_count()>=7), args (vert_fmt.tex_dims(6),))]
tex_7: Option<TexCoords>,
#[br(if(vert_fmt.tex_count()>=8), args (vert_fmt.tex_dims(7),))]
tex_8: Option<TexCoords>,
}
#[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<u32> {
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<FVF> {
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<Vertex>,
}
#[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<LFVFInner>,
}
#[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<u16>,
// // ==
// unk_t2_count: u32,
// #[br(assert(unk_t1_size==2))]
// unk_t2_size: u32,
// #[br(count=unk_t1_count)]
// unk_t2_list: Vec<u16>,
}
#[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<INI>,
content: Optional<NodeData>,
}
#[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<PascalString>,
unk_f: [RGBA; 7],
unk_data: [RGBA; 0x18 / 4],
maps: [Optional<MAP>; 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<INI>,
unk_f_1: [f32; (8 + 8) / 4],
unk_1: [f32; 0x18 / 4],
unk_f_2: f32,
user_props: Optional<INI>,
num_materials: u32,
#[br(count=num_materials)]
mat: Vec<MAT>,
#[br(temp,assert(unk_3==1))]
unk_3: u32,
num_nodes: u32,
#[br(count = num_nodes)] // 32
nodes: Vec<Node>,
ani: Optional<ANI>, // TODO:?
}
fn convert_timestamp(dt: u32) -> Result<DateTime<Utc>> {
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<Optional<VertexAnim>>,
}
#[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<u32>,
#[br(if(combined_flags&0x2!=0))]
unk_flags_2: Option<u32>,
#[br(if(combined_flags&0x4!=0))]
unk_flags_3: Option<u32>,
#[br(if(combined_flags&0x8!=0))]
unk_flags_4: Option<u32>,
#[br(if(combined_flags&0x10!=0))]
unk_flags_5: Option<u32>,
#[br(if(combined_flags&0x80!=0))]
unk_flags_6: Option<u32>,
#[br(if(flags&0x1000!=0))]
eva: Option<EVA>,
}
#[binread]
#[br(magic = b"NABK")]
#[derive(Debug, Serialize)]
struct NABK {
size: u32,
#[br(temp,count=size)]
data: Vec<u8>,
}
#[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<u8>,
nabk: NABK,
#[br(count=num_objects)]
nam: Vec<NAM>,
}
#[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<Utc>,
#[br(try_map=convert_timestamp)]
time_2: DateTime<Utc>,
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<Utc>,
#[br(try_map=convert_timestamp)]
time_2: DateTime<Utc>,
scene: SCN,
}
#[binread]
#[derive(Debug, Serialize)]
struct Dummy {
name: PascalString,
pos: [f32; 3],
rot: [f32; 3],
info: Optional<INI>,
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<Dummy>,
}
#[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<u16>,
f_4: [f32; 4],
num_children: u32,
#[br(count=num_children)]
children: Vec<QUAD>,
}
#[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<QUAD>,
}
#[binread]
#[br(import(version: u32))]
#[derive(Debug, Serialize)]
struct TriV104 {
#[br(if(version>=0x69))]
name_2: Option<PascalString>,
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<EMI_Textures>,
num_lists: u32,
#[br(count=num_lists,args{inner: (version,)})]
tri: Vec<TRI>,
}
#[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<Data> {
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<String, IndexMap<String, Option<String>>> {
Ini::new().load(path).unwrap_or_default()
}
fn load_data(root: &Path, path: &Path) -> Result<Value> {
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(())
}

View file

@ -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<usize>
// }
// #[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) {
}