From 45f38885ec22b770b38735f5b6b4171bee32620d Mon Sep 17 00:00:00 2001 From: Daniel Seiller Date: Mon, 6 Feb 2023 17:22:59 +0100 Subject: [PATCH 1/4] Add Scrap_analyze.py to locate and mark up engine vars and callbacks --- tools/ghidra_scripts/Scrap_analyze.py | 134 ++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 tools/ghidra_scripts/Scrap_analyze.py diff --git a/tools/ghidra_scripts/Scrap_analyze.py b/tools/ghidra_scripts/Scrap_analyze.py new file mode 100644 index 0000000..7c4a7d4 --- /dev/null +++ b/tools/ghidra_scripts/Scrap_analyze.py @@ -0,0 +1,134 @@ + +try: + import ghidra_bridge + has_bridge=True +except ImportError: + has_bridge=False + +from contextlib import contextmanager + +if has_bridge: + import ghidra_bridge + b = ghidra_bridge.GhidraBridge(namespace=globals(), hook_import=True) + @contextmanager + def transaction(): + start() + try: + yield + except Exception as e: + end(False) + raise e + end(True) +else: + @contextmanager + def transaction(): + yield + +import ghidra.program.model.symbol.SymbolType as SymbolType +import ghidra.program.model.symbol.SourceType as SourceType +from ghidra.app.cmd.label import CreateNamespacesCmd +from ghidra.program.model.data.DataUtilities import createData +from ghidra.program.model.data.DataUtilities import ClearDataMode +from ghidra.program.model.listing.CodeUnit import PLATE_COMMENT +def make_namespace(parts): + ns_cmd = CreateNamespacesCmd("::".join(parts), SourceType.USER_DEFINED) + ns_cmd.applyTo(currentProgram) + return ns_cmd.getNamespace() + + +callback_refs = [ref.fromAddress for ref in getReferencesTo(toAddr(0x590C70)).tolist()] +engine_var_refs = [ + ref.fromAddress for ref in getReferencesTo(toAddr(0x5319D0)).tolist() +] + +dtm = currentProgram.getDataTypeManager() +engine_var_dt = dtm.getDataType("/EngineVar") +callback_dt = dtm.getDataType("/CCallback") + +def create_data(addr,dtype): + return createData(currentProgram,addr,dtype,0,False,ClearDataMode.CLEAR_ALL_CONFLICT_DATA) + +def create_str(addr): + str_len = (findBytes(addr, b"\0").offset - addr.offset) + 1 + clearListing(addr, addr.add(str_len)) + return createAsciiString(addr) + + +def make_namespace(parts): + ns_cmd = CreateNamespacesCmd("::".join(parts), SourceType.USER_DEFINED) + ns_cmd.applyTo(currentProgram) + return ns_cmd.getNamespace() + + +def get_call_obj(addr): + func = getFunctionContaining(addr) + if func is None: + disassemble(addr) + func = createFunction(addr,None) + call_obj = {"this": None, "stack": []} + for inst in currentProgram.listing.getInstructions(func.body, True): + affected_objs = [r.toString() for r in inst.resultObjects.tolist()] + inst_name = inst.getMnemonicString() + if inst_name == "PUSH": + val=inst.getScalar(0) + if val is not None: + call_obj["stack"].insert(0, toAddr(val.getValue()).toString()) + elif inst_name == "MOV" and "ECX" in affected_objs: + this = inst.getScalar(1) + if this is not None: + call_obj["this"] = toAddr(this.getValue()).toString() + elif inst_name == "CALL": + break + return func, call_obj + + +with transaction(): + for ref in callback_refs: + register_callback, call_obj = get_call_obj(ref) + name, addr = call_obj["stack"] + this = toAddr(call_obj["this"]) + addr = toAddr(addr) + name = create_str(toAddr(name)).getValue() + callback_ns = make_namespace(["Callbacks"]) + ns = make_namespace(["Callbacks", name]) + clearListing(addr) + disassemble(addr) + func = createFunction(addr,None) + print(name,func) + createLabel(addr, name, callback_ns, True, SourceType.USER_DEFINED) + createLabel( + register_callback.getEntryPoint(), + "register", + ns, + True, + SourceType.USER_DEFINED, + ) + createLabel(this, name, None, True, SourceType.USER_DEFINED) + create_data(this,callback_dt) + + for ref in engine_var_refs: + register_engine_var, call_obj = get_call_obj(ref) + engine_var = call_obj['this'] + try: + name,flags,desc = call_obj['stack'][:3] + except ValueError: + continue + name=create_str(toAddr(name)).getValue() + desc=create_str(toAddr(desc)).getValue() + print(name,ref) + ev_ns = make_namespace(["EngineVars"]) + ns = make_namespace(["EngineVars", name]) + clearListing(toAddr(engine_var)) + create_data(toAddr(engine_var),engine_var_dt).setComment(PLATE_COMMENT,desc) + createLabel(toAddr(engine_var), name, ev_ns, True, SourceType.USER_DEFINED) + clearListing(register_engine_var.getEntryPoint()) + createLabel(register_engine_var.getEntryPoint(), "register", ns, True, SourceType.USER_DEFINED) + +# listing = currentProgram.getListing() +# codeUnit = listing.getCodeUnitAt(minAddress) +# codeUnit.setComment(codeUnit.PLATE_COMMENT, "AddCommentToProgramScript - This is an added comment!") + + +# dtm = currentProgram.getDataTypeManager() +# dt_engine_var = dtm.getDataType("/EngineVar") +# dt_engine_ptr = dtm.getPointer(dt_engine_var) From 8e0df74541d692bfa57e99a4741d410d058299c6 Mon Sep 17 00:00:00 2001 From: Daniel Seiller Date: Tue, 7 Mar 2023 20:05:56 +0100 Subject: [PATCH 2/4] 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 From 58407ecc9fb80000bf2d788e651d30e82e5a3e15 Mon Sep 17 00:00:00 2001 From: Daniel Seiller Date: Sun, 7 May 2023 21:29:21 +0200 Subject: [PATCH 3/4] Add web-based .packed explorer, updated parser and ghidra untility script --- scrapper_web/.gitignore | 24 + scrapper_web/.vscode/extensions.json | 3 + scrapper_web/README.md | 47 + scrapper_web/index.html | 13 + scrapper_web/jsconfig.json | 33 + scrapper_web/package.json | 26 + scrapper_web/pnpm-lock.yaml | 1777 +++++++++++++++++ scrapper_web/postcss.config.cjs | 11 + scrapper_web/public/vite.svg | 1 + scrapper_web/scrapper/.gitignore | 14 + scrapper_web/scrapper/Cargo.toml | 31 + scrapper_web/scrapper/README.md | 23 + scrapper_web/scrapper/src/lib.rs | 155 ++ scrapper_web/src/App.svelte | 13 + scrapper_web/src/app.pcss | 109 + scrapper_web/src/lib/Explorer.svelte | 52 + scrapper_web/src/lib/TreeView.svelte | 56 + scrapper_web/src/main.js | 6 + scrapper_web/src/scrapper.worker.js | 28 + scrapper_web/src/vite-env.d.ts | 2 + scrapper_web/svelte.config.js | 6 + scrapper_web/tailwind.config.cjs | 36 + scrapper_web/vite.config.js | 10 + tools/ghidra_scripts/mark_up_py.py | 125 ++ tools/remaster/scrap_parse/.gitignore | 177 ++ tools/remaster/scrap_parse/Cargo.lock | 747 +++++-- tools/remaster/scrap_parse/Cargo.toml | 9 +- .../scrap_parse/blender_plugin/__init__.py | 23 + .../level_import.py} | 101 +- .../blender_plugin/packed_browser.py | 118 ++ .../remaster/scrap_parse/get_vertex_size.cpp | 66 + tools/remaster/scrap_parse/packed_loader.py | 103 - tools/remaster/scrap_parse/src/find_scrap.rs | 15 + tools/remaster/scrap_parse/src/main.rs | 286 ++- .../remaster/scrap_parse/src/pixel_shader.rs | 4 + 35 files changed, 3897 insertions(+), 353 deletions(-) create mode 100644 scrapper_web/.gitignore create mode 100644 scrapper_web/.vscode/extensions.json create mode 100644 scrapper_web/README.md create mode 100644 scrapper_web/index.html create mode 100644 scrapper_web/jsconfig.json create mode 100644 scrapper_web/package.json create mode 100644 scrapper_web/pnpm-lock.yaml create mode 100644 scrapper_web/postcss.config.cjs create mode 100644 scrapper_web/public/vite.svg create mode 100644 scrapper_web/scrapper/.gitignore create mode 100644 scrapper_web/scrapper/Cargo.toml create mode 100644 scrapper_web/scrapper/README.md create mode 100644 scrapper_web/scrapper/src/lib.rs create mode 100644 scrapper_web/src/App.svelte create mode 100644 scrapper_web/src/app.pcss create mode 100644 scrapper_web/src/lib/Explorer.svelte create mode 100644 scrapper_web/src/lib/TreeView.svelte create mode 100644 scrapper_web/src/main.js create mode 100644 scrapper_web/src/scrapper.worker.js create mode 100644 scrapper_web/src/vite-env.d.ts create mode 100644 scrapper_web/svelte.config.js create mode 100644 scrapper_web/tailwind.config.cjs create mode 100644 scrapper_web/vite.config.js create mode 100644 tools/ghidra_scripts/mark_up_py.py create mode 100644 tools/remaster/scrap_parse/.gitignore create mode 100644 tools/remaster/scrap_parse/blender_plugin/__init__.py rename tools/remaster/scrap_parse/{blender_import.py => blender_plugin/level_import.py} (87%) create mode 100644 tools/remaster/scrap_parse/blender_plugin/packed_browser.py create mode 100644 tools/remaster/scrap_parse/get_vertex_size.cpp delete mode 100644 tools/remaster/scrap_parse/packed_loader.py create mode 100644 tools/remaster/scrap_parse/src/find_scrap.rs diff --git a/scrapper_web/.gitignore b/scrapper_web/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/scrapper_web/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/scrapper_web/.vscode/extensions.json b/scrapper_web/.vscode/extensions.json new file mode 100644 index 0000000..bdef820 --- /dev/null +++ b/scrapper_web/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["svelte.svelte-vscode"] +} diff --git a/scrapper_web/README.md b/scrapper_web/README.md new file mode 100644 index 0000000..69c2ac5 --- /dev/null +++ b/scrapper_web/README.md @@ -0,0 +1,47 @@ +# Svelte + Vite + +This template should help get you started developing with Svelte in Vite. + +## Recommended IDE Setup + +[VS Code](https://code.visualstudio.com/) + [Svelte](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode). + +## Need an official Svelte framework? + +Check out [SvelteKit](https://github.com/sveltejs/kit#readme), which is also powered by Vite. Deploy anywhere with its serverless-first approach and adapt to various platforms, with out of the box support for TypeScript, SCSS, and Less, and easily-added support for mdsvex, GraphQL, PostCSS, Tailwind CSS, and more. + +## Technical considerations + +**Why use this over SvelteKit?** + +- It brings its own routing solution which might not be preferable for some users. +- It is first and foremost a framework that just happens to use Vite under the hood, not a Vite app. + +This template contains as little as possible to get started with Vite + Svelte, while taking into account the developer experience with regards to HMR and intellisense. It demonstrates capabilities on par with the other `create-vite` templates and is a good starting point for beginners dipping their toes into a Vite + Svelte project. + +Should you later need the extended capabilities and extensibility provided by SvelteKit, the template has been structured similarly to SvelteKit so that it is easy to migrate. + +**Why `global.d.ts` instead of `compilerOptions.types` inside `jsconfig.json` or `tsconfig.json`?** + +Setting `compilerOptions.types` shuts out all other types not explicitly listed in the configuration. Using triple-slash references keeps the default TypeScript setting of accepting type information from the entire workspace, while also adding `svelte` and `vite/client` type information. + +**Why include `.vscode/extensions.json`?** + +Other templates indirectly recommend extensions via the README, but this file allows VS Code to prompt the user to install the recommended extension upon opening the project. + +**Why enable `checkJs` in the JS template?** + +It is likely that most cases of changing variable types in runtime are likely to be accidental, rather than deliberate. This provides advanced typechecking out of the box. Should you like to take advantage of the dynamically-typed nature of JavaScript, it is trivial to change the configuration. + +**Why is HMR not preserving my local component state?** + +HMR state preservation comes with a number of gotchas! It has been disabled by default in both `svelte-hmr` and `@sveltejs/vite-plugin-svelte` due to its often surprising behavior. You can read the details [here](https://github.com/rixo/svelte-hmr#svelte-hmr). + +If you have state that's important to retain within a component, consider creating an external store which would not be replaced by HMR. + +```js +// store.js +// An extremely simple external store +import { writable } from 'svelte/store' +export default writable(0) +``` diff --git a/scrapper_web/index.html b/scrapper_web/index.html new file mode 100644 index 0000000..26c29c4 --- /dev/null +++ b/scrapper_web/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite + Svelte + + +
+ + + diff --git a/scrapper_web/jsconfig.json b/scrapper_web/jsconfig.json new file mode 100644 index 0000000..2df8094 --- /dev/null +++ b/scrapper_web/jsconfig.json @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "moduleResolution": "Node", + "target": "ESNext", + "module": "ESNext", + /** + * svelte-preprocess cannot figure out whether you have + * a value or a type, so tell TypeScript to enforce using + * `import type` instead of `import` for Types. + */ + "importsNotUsedAsValues": "error", + "isolatedModules": true, + "resolveJsonModule": true, + /** + * To have warnings / errors of the Svelte compiler at the + * correct position, enable source maps by default. + */ + "sourceMap": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + /** + * Typecheck JS in `.svelte` and `.js` files by default. + * Disable this if you'd like to use dynamic types. + */ + "checkJs": false + }, + /** + * Use global.d.ts instead of compilerOptions.types + * to avoid limiting type declarations. + */ + "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"] +} diff --git a/scrapper_web/package.json b/scrapper_web/package.json new file mode 100644 index 0000000..b66da50 --- /dev/null +++ b/scrapper_web/package.json @@ -0,0 +1,26 @@ +{ + "name": "scrapper_web", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "wasm-pack build ./scrapper -t web && vite build", + "preview": "vite preview" + }, + "devDependencies": { + "@sveltejs/vite-plugin-svelte": "^2.0.2", + "@tailwindcss/forms": "^0.5.3", + "autoprefixer": "^10.4.13", + "cssnano": "^5.1.14", + "cssnano-preset-advanced": "^5.3.9", + "daisyui": "^2.50.0", + "filedrop-svelte": "^0.1.2", + "postcss": "^8.4.21", + "svelte": "^3.55.1", + "svelte-preprocess": "^5.0.1", + "tailwindcss": "^3.2.4", + "vite": "^4.1.0", + "vite-plugin-wasm-pack": "^0.1.12" + } +} \ No newline at end of file diff --git a/scrapper_web/pnpm-lock.yaml b/scrapper_web/pnpm-lock.yaml new file mode 100644 index 0000000..739e35e --- /dev/null +++ b/scrapper_web/pnpm-lock.yaml @@ -0,0 +1,1777 @@ +lockfileVersion: 5.4 + +specifiers: + '@sveltejs/vite-plugin-svelte': ^2.0.2 + '@tailwindcss/forms': ^0.5.3 + autoprefixer: ^10.4.13 + cssnano: ^5.1.14 + cssnano-preset-advanced: ^5.3.9 + daisyui: ^2.50.0 + filedrop-svelte: ^0.1.2 + postcss: ^8.4.21 + svelte: ^3.55.1 + svelte-preprocess: ^5.0.1 + tailwindcss: ^3.2.4 + vite: ^4.1.0 + vite-plugin-wasm-pack: ^0.1.12 + +devDependencies: + '@sveltejs/vite-plugin-svelte': 2.0.2_svelte@3.55.1+vite@4.1.1 + '@tailwindcss/forms': 0.5.3_tailwindcss@3.2.6 + autoprefixer: 10.4.13_postcss@8.4.21 + cssnano: 5.1.14_postcss@8.4.21 + cssnano-preset-advanced: 5.3.9_postcss@8.4.21 + daisyui: 2.50.0_gbtt6ss3tbiz4yjtvdr6fbrj44 + filedrop-svelte: 0.1.2 + postcss: 8.4.21 + svelte: 3.55.1 + svelte-preprocess: 5.0.1_pehl75e5jsy22vp33udjja4soi + tailwindcss: 3.2.6_postcss@8.4.21 + vite: 4.1.1 + vite-plugin-wasm-pack: 0.1.12 + +packages: + + /@esbuild/android-arm/0.16.17: + resolution: {integrity: sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm64/0.16.17: + resolution: {integrity: sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64/0.16.17: + resolution: {integrity: sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64/0.16.17: + resolution: {integrity: sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64/0.16.17: + resolution: {integrity: sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64/0.16.17: + resolution: {integrity: sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64/0.16.17: + resolution: {integrity: sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm/0.16.17: + resolution: {integrity: sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64/0.16.17: + resolution: {integrity: sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32/0.16.17: + resolution: {integrity: sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64/0.16.17: + resolution: {integrity: sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el/0.16.17: + resolution: {integrity: sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64/0.16.17: + resolution: {integrity: sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64/0.16.17: + resolution: {integrity: sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x/0.16.17: + resolution: {integrity: sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64/0.16.17: + resolution: {integrity: sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64/0.16.17: + resolution: {integrity: sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64/0.16.17: + resolution: {integrity: sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64/0.16.17: + resolution: {integrity: sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64/0.16.17: + resolution: {integrity: sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32/0.16.17: + resolution: {integrity: sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64/0.16.17: + resolution: {integrity: sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@jridgewell/sourcemap-codec/1.4.14: + resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} + dev: true + + /@nodelib/fs.scandir/2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat/2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk/1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.15.0 + dev: true + + /@sveltejs/vite-plugin-svelte/2.0.2_svelte@3.55.1+vite@4.1.1: + resolution: {integrity: sha512-xCEan0/NNpQuL0l5aS42FjwQ6wwskdxC3pW1OeFtEKNZwRg7Evro9lac9HesGP6TdFsTv2xMes5ASQVKbCacxg==} + engines: {node: ^14.18.0 || >= 16} + peerDependencies: + svelte: ^3.54.0 + vite: ^4.0.0 + dependencies: + debug: 4.3.4 + deepmerge: 4.3.0 + kleur: 4.1.5 + magic-string: 0.27.0 + svelte: 3.55.1 + svelte-hmr: 0.15.1_svelte@3.55.1 + vite: 4.1.1 + vitefu: 0.2.4_vite@4.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@tailwindcss/forms/0.5.3_tailwindcss@3.2.6: + resolution: {integrity: sha512-y5mb86JUoiUgBjY/o6FJSFZSEttfb3Q5gllE4xoKjAAD+vBrnIhE4dViwUuow3va8mpH4s9jyUbUbrRGoRdc2Q==} + peerDependencies: + tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1' + dependencies: + mini-svg-data-uri: 1.4.4 + tailwindcss: 3.2.6_postcss@8.4.21 + dev: true + + /@trysound/sax/0.2.0: + resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} + engines: {node: '>=10.13.0'} + dev: true + + /@types/node/18.13.0: + resolution: {integrity: sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg==} + dev: true + + /@types/pug/2.0.6: + resolution: {integrity: sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==} + dev: true + + /@types/sass/1.43.1: + resolution: {integrity: sha512-BPdoIt1lfJ6B7rw35ncdwBZrAssjcwzI5LByIrYs+tpXlj/CAkuVdRsgZDdP4lq5EjyWzwxZCqAoFyHKFwp32g==} + dependencies: + '@types/node': 18.13.0 + dev: true + + /acorn-node/1.8.2: + resolution: {integrity: sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==} + dependencies: + acorn: 7.4.1 + acorn-walk: 7.2.0 + xtend: 4.0.2 + dev: true + + /acorn-walk/7.2.0: + resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==} + engines: {node: '>=0.4.0'} + dev: true + + /acorn/7.4.1: + resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /ansi-styles/4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: true + + /anymatch/3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + dev: true + + /arg/5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + dev: true + + /autoprefixer/10.4.13_postcss@8.4.21: + resolution: {integrity: sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + dependencies: + browserslist: 4.21.5 + caniuse-lite: 1.0.30001450 + fraction.js: 4.2.0 + normalize-range: 0.1.2 + picocolors: 1.0.0 + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /balanced-match/1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /binary-extensions/2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + dev: true + + /boolbase/1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + dev: true + + /brace-expansion/1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /braces/3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: true + + /browserslist/4.21.5: + resolution: {integrity: sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001450 + electron-to-chromium: 1.4.288 + node-releases: 2.0.10 + update-browserslist-db: 1.0.10_browserslist@4.21.5 + dev: true + + /buffer-crc32/0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + dev: true + + /camelcase-css/2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + dev: true + + /caniuse-api/3.0.0: + resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} + dependencies: + browserslist: 4.21.5 + caniuse-lite: 1.0.30001450 + lodash.memoize: 4.1.2 + lodash.uniq: 4.5.0 + dev: true + + /caniuse-lite/1.0.30001450: + resolution: {integrity: sha512-qMBmvmQmFXaSxexkjjfMvD5rnDL0+m+dUMZKoDYsGG8iZN29RuYh9eRoMvKsT6uMAWlyUUGDEQGJJYjzCIO9ew==} + dev: true + + /chalk/4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /chokidar/3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /color-convert/2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: true + + /color-name/1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: true + + /color-string/1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + dev: true + + /color/4.2.3: + resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} + engines: {node: '>=12.5.0'} + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + dev: true + + /colord/2.9.3: + resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} + dev: true + + /commander/7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + dev: true + + /concat-map/0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /css-declaration-sorter/6.3.1_postcss@8.4.21: + resolution: {integrity: sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==} + engines: {node: ^10 || ^12 || >=14} + peerDependencies: + postcss: ^8.0.9 + dependencies: + postcss: 8.4.21 + dev: true + + /css-select/4.3.0: + resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 4.3.1 + domutils: 2.8.0 + nth-check: 2.1.1 + dev: true + + /css-selector-tokenizer/0.8.0: + resolution: {integrity: sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==} + dependencies: + cssesc: 3.0.0 + fastparse: 1.1.2 + dev: true + + /css-tree/1.1.3: + resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} + engines: {node: '>=8.0.0'} + dependencies: + mdn-data: 2.0.14 + source-map: 0.6.1 + dev: true + + /css-what/6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + engines: {node: '>= 6'} + dev: true + + /cssesc/3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /cssnano-preset-advanced/5.3.9_postcss@8.4.21: + resolution: {integrity: sha512-njnh4pp1xCsibJcEHnWZb4EEzni0ePMqPuPNyuWT4Z+YeXmsgqNuTPIljXFEXhxGsWs9183JkXgHxc1TcsahIg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + autoprefixer: 10.4.13_postcss@8.4.21 + cssnano-preset-default: 5.2.13_postcss@8.4.21 + postcss: 8.4.21 + postcss-discard-unused: 5.1.0_postcss@8.4.21 + postcss-merge-idents: 5.1.1_postcss@8.4.21 + postcss-reduce-idents: 5.2.0_postcss@8.4.21 + postcss-zindex: 5.1.0_postcss@8.4.21 + dev: true + + /cssnano-preset-default/5.2.13_postcss@8.4.21: + resolution: {integrity: sha512-PX7sQ4Pb+UtOWuz8A1d+Rbi+WimBIxJTRyBdgGp1J75VU0r/HFQeLnMYgHiCAp6AR4rqrc7Y4R+1Rjk3KJz6DQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + css-declaration-sorter: 6.3.1_postcss@8.4.21 + cssnano-utils: 3.1.0_postcss@8.4.21 + postcss: 8.4.21 + postcss-calc: 8.2.4_postcss@8.4.21 + postcss-colormin: 5.3.0_postcss@8.4.21 + postcss-convert-values: 5.1.3_postcss@8.4.21 + postcss-discard-comments: 5.1.2_postcss@8.4.21 + postcss-discard-duplicates: 5.1.0_postcss@8.4.21 + postcss-discard-empty: 5.1.1_postcss@8.4.21 + postcss-discard-overridden: 5.1.0_postcss@8.4.21 + postcss-merge-longhand: 5.1.7_postcss@8.4.21 + postcss-merge-rules: 5.1.3_postcss@8.4.21 + postcss-minify-font-values: 5.1.0_postcss@8.4.21 + postcss-minify-gradients: 5.1.1_postcss@8.4.21 + postcss-minify-params: 5.1.4_postcss@8.4.21 + postcss-minify-selectors: 5.2.1_postcss@8.4.21 + postcss-normalize-charset: 5.1.0_postcss@8.4.21 + postcss-normalize-display-values: 5.1.0_postcss@8.4.21 + postcss-normalize-positions: 5.1.1_postcss@8.4.21 + postcss-normalize-repeat-style: 5.1.1_postcss@8.4.21 + postcss-normalize-string: 5.1.0_postcss@8.4.21 + postcss-normalize-timing-functions: 5.1.0_postcss@8.4.21 + postcss-normalize-unicode: 5.1.1_postcss@8.4.21 + postcss-normalize-url: 5.1.0_postcss@8.4.21 + postcss-normalize-whitespace: 5.1.1_postcss@8.4.21 + postcss-ordered-values: 5.1.3_postcss@8.4.21 + postcss-reduce-initial: 5.1.1_postcss@8.4.21 + postcss-reduce-transforms: 5.1.0_postcss@8.4.21 + postcss-svgo: 5.1.0_postcss@8.4.21 + postcss-unique-selectors: 5.1.1_postcss@8.4.21 + dev: true + + /cssnano-utils/3.1.0_postcss@8.4.21: + resolution: {integrity: sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + dev: true + + /cssnano/5.1.14_postcss@8.4.21: + resolution: {integrity: sha512-Oou7ihiTocbKqi0J1bB+TRJIQX5RMR3JghA8hcWSw9mjBLQ5Y3RWqEDoYG3sRNlAbCIXpqMoZGbq5KDR3vdzgw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + cssnano-preset-default: 5.2.13_postcss@8.4.21 + lilconfig: 2.0.6 + postcss: 8.4.21 + yaml: 1.10.2 + dev: true + + /csso/4.2.0: + resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==} + engines: {node: '>=8.0.0'} + dependencies: + css-tree: 1.1.3 + dev: true + + /daisyui/2.50.0_gbtt6ss3tbiz4yjtvdr6fbrj44: + resolution: {integrity: sha512-KiqRvqMXi9rgoH84M8D69gXPg6x+cbdiaHqm8pFHOsXXN1rTl/+OcCKkSnkEwTtIge9VJVDGU6l4B8/n+Juc5g==} + peerDependencies: + autoprefixer: ^10.0.2 + postcss: ^8.1.6 + dependencies: + autoprefixer: 10.4.13_postcss@8.4.21 + color: 4.2.3 + css-selector-tokenizer: 0.8.0 + postcss: 8.4.21 + postcss-js: 4.0.1_postcss@8.4.21 + tailwindcss: 3.2.6_postcss@8.4.21 + transitivePeerDependencies: + - ts-node + dev: true + + /debug/4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: true + + /deepmerge/4.3.0: + resolution: {integrity: sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og==} + engines: {node: '>=0.10.0'} + dev: true + + /defined/1.0.1: + resolution: {integrity: sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==} + dev: true + + /detect-indent/6.1.0: + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + engines: {node: '>=8'} + dev: true + + /detective/5.2.1: + resolution: {integrity: sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==} + engines: {node: '>=0.8.0'} + hasBin: true + dependencies: + acorn-node: 1.8.2 + defined: 1.0.1 + minimist: 1.2.7 + dev: true + + /didyoumean/1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + dev: true + + /dlv/1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + dev: true + + /dom-serializer/1.4.1: + resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + entities: 2.2.0 + dev: true + + /domelementtype/2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + dev: true + + /domhandler/4.3.1: + resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} + engines: {node: '>= 4'} + dependencies: + domelementtype: 2.3.0 + dev: true + + /domutils/2.8.0: + resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} + dependencies: + dom-serializer: 1.4.1 + domelementtype: 2.3.0 + domhandler: 4.3.1 + dev: true + + /electron-to-chromium/1.4.288: + resolution: {integrity: sha512-8s9aJf3YiokIrR+HOQzNOGmEHFXVUQzXM/JaViVvKdCkNUjS+lEa/uT7xw3nDVG/IgfxiIwUGkwJ6AR1pTpYsQ==} + dev: true + + /entities/2.2.0: + resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} + dev: true + + /es6-promise/3.3.1: + resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==} + dev: true + + /esbuild/0.16.17: + resolution: {integrity: sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.16.17 + '@esbuild/android-arm64': 0.16.17 + '@esbuild/android-x64': 0.16.17 + '@esbuild/darwin-arm64': 0.16.17 + '@esbuild/darwin-x64': 0.16.17 + '@esbuild/freebsd-arm64': 0.16.17 + '@esbuild/freebsd-x64': 0.16.17 + '@esbuild/linux-arm': 0.16.17 + '@esbuild/linux-arm64': 0.16.17 + '@esbuild/linux-ia32': 0.16.17 + '@esbuild/linux-loong64': 0.16.17 + '@esbuild/linux-mips64el': 0.16.17 + '@esbuild/linux-ppc64': 0.16.17 + '@esbuild/linux-riscv64': 0.16.17 + '@esbuild/linux-s390x': 0.16.17 + '@esbuild/linux-x64': 0.16.17 + '@esbuild/netbsd-x64': 0.16.17 + '@esbuild/openbsd-x64': 0.16.17 + '@esbuild/sunos-x64': 0.16.17 + '@esbuild/win32-arm64': 0.16.17 + '@esbuild/win32-ia32': 0.16.17 + '@esbuild/win32-x64': 0.16.17 + dev: true + + /escalade/3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + dev: true + + /fast-glob/3.2.12: + resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fastparse/1.1.2: + resolution: {integrity: sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==} + dev: true + + /fastq/1.15.0: + resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + dependencies: + reusify: 1.0.4 + dev: true + + /file-selector/0.2.4: + resolution: {integrity: sha512-ZDsQNbrv6qRi1YTDOEWzf5J2KjZ9KMI1Q2SGeTkCJmNNW25Jg4TW4UMcmoqcg4WrAyKRcpBXdbWRxkfrOzVRbA==} + engines: {node: '>= 10'} + dependencies: + tslib: 2.5.0 + dev: true + + /filedrop-svelte/0.1.2: + resolution: {integrity: sha512-S0Z+zCO2dy/g3tRjswD8pLEcorj78olkLWvhKEp511gwHUI6x+uWjXacuvFmRQnjbCCPByXzGlC5Asb9nPzUKw==} + dependencies: + file-selector: 0.2.4 + pretty-bytes: 6.1.0 + dev: true + + /fill-range/7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /fraction.js/4.2.0: + resolution: {integrity: sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==} + dev: true + + /fs-extra/10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + dependencies: + graceful-fs: 4.2.10 + jsonfile: 6.1.0 + universalify: 2.0.0 + dev: true + + /fs.realpath/1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + + /fsevents/2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /function-bind/1.1.1: + resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + dev: true + + /glob-parent/5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob-parent/6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob/7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /graceful-fs/4.2.10: + resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} + dev: true + + /has-flag/4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + + /has/1.0.3: + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} + dependencies: + function-bind: 1.1.1 + dev: true + + /inflight/1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits/2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true + + /is-arrayish/0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + dev: true + + /is-binary-path/2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.2.0 + dev: true + + /is-core-module/2.11.0: + resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} + dependencies: + has: 1.0.3 + dev: true + + /is-extglob/2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true + + /is-glob/4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-number/7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /jsonfile/6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + dependencies: + universalify: 2.0.0 + optionalDependencies: + graceful-fs: 4.2.10 + dev: true + + /kleur/4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + dev: true + + /lilconfig/2.0.6: + resolution: {integrity: sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==} + engines: {node: '>=10'} + dev: true + + /lodash.memoize/4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + dev: true + + /lodash.uniq/4.5.0: + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + dev: true + + /magic-string/0.27.0: + resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.14 + dev: true + + /mdn-data/2.0.14: + resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} + dev: true + + /merge2/1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch/4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /min-indent/1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + dev: true + + /mini-svg-data-uri/1.4.4: + resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==} + hasBin: true + dev: true + + /minimatch/3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimist/1.2.7: + resolution: {integrity: sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==} + dev: true + + /mkdirp/0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + dependencies: + minimist: 1.2.7 + dev: true + + /ms/2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true + + /nanoid/3.3.4: + resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + + /narrowing/1.5.0: + resolution: {integrity: sha512-DUu4XdKgkfAPTAL28k79pdnshDE2W5T24QAnidSPo2F/W1TX6CjNzmEeXQfE5O1lxQvC0GYI6ZRDsLcyzugEYA==} + dev: true + + /node-releases/2.0.10: + resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==} + dev: true + + /normalize-path/3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: true + + /normalize-range/0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + dev: true + + /normalize-url/6.1.0: + resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} + engines: {node: '>=10'} + dev: true + + /nth-check/2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + dependencies: + boolbase: 1.0.0 + dev: true + + /object-hash/3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + dev: true + + /once/1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: true + + /path-is-absolute/1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + + /path-parse/1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: true + + /picocolors/1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true + + /picomatch/2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true + + /pify/2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + dev: true + + /postcss-calc/8.2.4_postcss@8.4.21: + resolution: {integrity: sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==} + peerDependencies: + postcss: ^8.2.2 + dependencies: + postcss: 8.4.21 + postcss-selector-parser: 6.0.11 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-colormin/5.3.0_postcss@8.4.21: + resolution: {integrity: sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + browserslist: 4.21.5 + caniuse-api: 3.0.0 + colord: 2.9.3 + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-convert-values/5.1.3_postcss@8.4.21: + resolution: {integrity: sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + browserslist: 4.21.5 + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-discard-comments/5.1.2_postcss@8.4.21: + resolution: {integrity: sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + dev: true + + /postcss-discard-duplicates/5.1.0_postcss@8.4.21: + resolution: {integrity: sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + dev: true + + /postcss-discard-empty/5.1.1_postcss@8.4.21: + resolution: {integrity: sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + dev: true + + /postcss-discard-overridden/5.1.0_postcss@8.4.21: + resolution: {integrity: sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + dev: true + + /postcss-discard-unused/5.1.0_postcss@8.4.21: + resolution: {integrity: sha512-KwLWymI9hbwXmJa0dkrzpRbSJEh0vVUd7r8t0yOGPcfKzyJJxFM8kLyC5Ev9avji6nY95pOp1W6HqIrfT+0VGw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + postcss-selector-parser: 6.0.11 + dev: true + + /postcss-import/14.1.0_postcss@8.4.21: + resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==} + engines: {node: '>=10.0.0'} + peerDependencies: + postcss: ^8.0.0 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.1 + dev: true + + /postcss-js/4.0.1_postcss@8.4.21: + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + dependencies: + camelcase-css: 2.0.1 + postcss: 8.4.21 + dev: true + + /postcss-load-config/3.1.4_postcss@8.4.21: + resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} + engines: {node: '>= 10'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + dependencies: + lilconfig: 2.0.6 + postcss: 8.4.21 + yaml: 1.10.2 + dev: true + + /postcss-merge-idents/5.1.1_postcss@8.4.21: + resolution: {integrity: sha512-pCijL1TREiCoog5nQp7wUe+TUonA2tC2sQ54UGeMmryK3UFGIYKqDyjnqd6RcuI4znFn9hWSLNN8xKE/vWcUQw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + cssnano-utils: 3.1.0_postcss@8.4.21 + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-merge-longhand/5.1.7_postcss@8.4.21: + resolution: {integrity: sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + stylehacks: 5.1.1_postcss@8.4.21 + dev: true + + /postcss-merge-rules/5.1.3_postcss@8.4.21: + resolution: {integrity: sha512-LbLd7uFC00vpOuMvyZop8+vvhnfRGpp2S+IMQKeuOZZapPRY4SMq5ErjQeHbHsjCUgJkRNrlU+LmxsKIqPKQlA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + browserslist: 4.21.5 + caniuse-api: 3.0.0 + cssnano-utils: 3.1.0_postcss@8.4.21 + postcss: 8.4.21 + postcss-selector-parser: 6.0.11 + dev: true + + /postcss-minify-font-values/5.1.0_postcss@8.4.21: + resolution: {integrity: sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-minify-gradients/5.1.1_postcss@8.4.21: + resolution: {integrity: sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + colord: 2.9.3 + cssnano-utils: 3.1.0_postcss@8.4.21 + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-minify-params/5.1.4_postcss@8.4.21: + resolution: {integrity: sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + browserslist: 4.21.5 + cssnano-utils: 3.1.0_postcss@8.4.21 + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-minify-selectors/5.2.1_postcss@8.4.21: + resolution: {integrity: sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + postcss-selector-parser: 6.0.11 + dev: true + + /postcss-nested/6.0.0_postcss@8.4.21: + resolution: {integrity: sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + dependencies: + postcss: 8.4.21 + postcss-selector-parser: 6.0.11 + dev: true + + /postcss-normalize-charset/5.1.0_postcss@8.4.21: + resolution: {integrity: sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + dev: true + + /postcss-normalize-display-values/5.1.0_postcss@8.4.21: + resolution: {integrity: sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-normalize-positions/5.1.1_postcss@8.4.21: + resolution: {integrity: sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-normalize-repeat-style/5.1.1_postcss@8.4.21: + resolution: {integrity: sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-normalize-string/5.1.0_postcss@8.4.21: + resolution: {integrity: sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-normalize-timing-functions/5.1.0_postcss@8.4.21: + resolution: {integrity: sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-normalize-unicode/5.1.1_postcss@8.4.21: + resolution: {integrity: sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + browserslist: 4.21.5 + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-normalize-url/5.1.0_postcss@8.4.21: + resolution: {integrity: sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + normalize-url: 6.1.0 + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-normalize-whitespace/5.1.1_postcss@8.4.21: + resolution: {integrity: sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-ordered-values/5.1.3_postcss@8.4.21: + resolution: {integrity: sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + cssnano-utils: 3.1.0_postcss@8.4.21 + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-reduce-idents/5.2.0_postcss@8.4.21: + resolution: {integrity: sha512-BTrLjICoSB6gxbc58D5mdBK8OhXRDqud/zodYfdSi52qvDHdMwk+9kB9xsM8yJThH/sZU5A6QVSmMmaN001gIg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-reduce-initial/5.1.1_postcss@8.4.21: + resolution: {integrity: sha512-//jeDqWcHPuXGZLoolFrUXBDyuEGbr9S2rMo19bkTIjBQ4PqkaO+oI8wua5BOUxpfi97i3PCoInsiFIEBfkm9w==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + browserslist: 4.21.5 + caniuse-api: 3.0.0 + postcss: 8.4.21 + dev: true + + /postcss-reduce-transforms/5.1.0_postcss@8.4.21: + resolution: {integrity: sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + dev: true + + /postcss-selector-parser/6.0.11: + resolution: {integrity: sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==} + engines: {node: '>=4'} + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + dev: true + + /postcss-svgo/5.1.0_postcss@8.4.21: + resolution: {integrity: sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + postcss-value-parser: 4.2.0 + svgo: 2.8.0 + dev: true + + /postcss-unique-selectors/5.1.1_postcss@8.4.21: + resolution: {integrity: sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + postcss-selector-parser: 6.0.11 + dev: true + + /postcss-value-parser/4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + dev: true + + /postcss-zindex/5.1.0_postcss@8.4.21: + resolution: {integrity: sha512-fgFMf0OtVSBR1va1JNHYgMxYk73yhn/qb4uQDq1DLGYolz8gHCyr/sesEuGUaYs58E3ZJRcpoGuPVoB7Meiq9A==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + postcss: 8.4.21 + dev: true + + /postcss/8.4.21: + resolution: {integrity: sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.4 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: true + + /pretty-bytes/6.1.0: + resolution: {integrity: sha512-Rk753HI8f4uivXi4ZCIYdhmG1V+WKzvRMg/X+M42a6t7D07RcmopXJMDNk6N++7Bl75URRGsb40ruvg7Hcp2wQ==} + engines: {node: ^14.13.1 || >=16.0.0} + dev: true + + /queue-microtask/1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /quick-lru/5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + dev: true + + /read-cache/1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + dependencies: + pify: 2.3.0 + dev: true + + /readdirp/3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + dev: true + + /resolve/1.22.1: + resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} + hasBin: true + dependencies: + is-core-module: 2.11.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /reusify/1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rimraf/2.7.1: + resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /rollup/3.14.0: + resolution: {integrity: sha512-o23sdgCLcLSe3zIplT9nQ1+r97okuaiR+vmAPZPTDYB7/f3tgWIYNyiQveMsZwshBT0is4eGax/HH83Q7CG+/Q==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /run-parallel/1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /sander/0.5.1: + resolution: {integrity: sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==} + dependencies: + es6-promise: 3.3.1 + graceful-fs: 4.2.10 + mkdirp: 0.5.6 + rimraf: 2.7.1 + dev: true + + /simple-swizzle/0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + dependencies: + is-arrayish: 0.3.2 + dev: true + + /sorcery/0.11.0: + resolution: {integrity: sha512-J69LQ22xrQB1cIFJhPfgtLuI6BpWRiWu1Y3vSsIwK/eAScqJxd/+CJlUuHQRdX2C9NGFamq+KqNywGgaThwfHw==} + hasBin: true + dependencies: + '@jridgewell/sourcemap-codec': 1.4.14 + buffer-crc32: 0.2.13 + minimist: 1.2.7 + sander: 0.5.1 + dev: true + + /source-map-js/1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + dev: true + + /source-map/0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + dev: true + + /stable/0.1.8: + resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==} + deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility' + dev: true + + /strip-indent/3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + dependencies: + min-indent: 1.0.1 + dev: true + + /stylehacks/5.1.1_postcss@8.4.21: + resolution: {integrity: sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + dependencies: + browserslist: 4.21.5 + postcss: 8.4.21 + postcss-selector-parser: 6.0.11 + dev: true + + /supports-color/7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-preserve-symlinks-flag/1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + dev: true + + /svelte-hmr/0.15.1_svelte@3.55.1: + resolution: {integrity: sha512-BiKB4RZ8YSwRKCNVdNxK/GfY+r4Kjgp9jCLEy0DuqAKfmQtpL38cQK3afdpjw4sqSs4PLi3jIPJIFp259NkZtA==} + engines: {node: ^12.20 || ^14.13.1 || >= 16} + peerDependencies: + svelte: '>=3.19.0' + dependencies: + svelte: 3.55.1 + dev: true + + /svelte-preprocess/5.0.1_pehl75e5jsy22vp33udjja4soi: + resolution: {integrity: sha512-0HXyhCoc9rsW4zGOgtInylC6qj259E1hpFnJMJWTf+aIfeqh4O/QHT31KT2hvPEqQfdjmqBR/kO2JDkkciBLrQ==} + engines: {node: '>= 14.10.0'} + requiresBuild: true + peerDependencies: + '@babel/core': ^7.10.2 + coffeescript: ^2.5.1 + less: ^3.11.3 || ^4.0.0 + postcss: ^7 || ^8 + postcss-load-config: ^2.1.0 || ^3.0.0 || ^4.0.0 + pug: ^3.0.0 + sass: ^1.26.8 + stylus: ^0.55.0 + sugarss: ^2.0.0 || ^3.0.0 || ^4.0.0 + svelte: ^3.23.0 + typescript: ^3.9.5 || ^4.0.0 + peerDependenciesMeta: + '@babel/core': + optional: true + coffeescript: + optional: true + less: + optional: true + postcss: + optional: true + postcss-load-config: + optional: true + pug: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + typescript: + optional: true + dependencies: + '@types/pug': 2.0.6 + '@types/sass': 1.43.1 + detect-indent: 6.1.0 + magic-string: 0.27.0 + postcss: 8.4.21 + sorcery: 0.11.0 + strip-indent: 3.0.0 + svelte: 3.55.1 + dev: true + + /svelte/3.55.1: + resolution: {integrity: sha512-S+87/P0Ve67HxKkEV23iCdAh/SX1xiSfjF1HOglno/YTbSTW7RniICMCofWGdJJbdjw3S+0PfFb1JtGfTXE0oQ==} + engines: {node: '>= 8'} + dev: true + + /svgo/2.8.0: + resolution: {integrity: sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==} + engines: {node: '>=10.13.0'} + hasBin: true + dependencies: + '@trysound/sax': 0.2.0 + commander: 7.2.0 + css-select: 4.3.0 + css-tree: 1.1.3 + csso: 4.2.0 + picocolors: 1.0.0 + stable: 0.1.8 + dev: true + + /tailwindcss/3.2.6_postcss@8.4.21: + resolution: {integrity: sha512-BfgQWZrtqowOQMC2bwaSNe7xcIjdDEgixWGYOd6AL0CbKHJlvhfdbINeAW76l1sO+1ov/MJ93ODJ9yluRituIw==} + engines: {node: '>=12.13.0'} + hasBin: true + peerDependencies: + postcss: ^8.0.9 + dependencies: + arg: 5.0.2 + chokidar: 3.5.3 + color-name: 1.1.4 + detective: 5.2.1 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.2.12 + glob-parent: 6.0.2 + is-glob: 4.0.3 + lilconfig: 2.0.6 + micromatch: 4.0.5 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.0.0 + postcss: 8.4.21 + postcss-import: 14.1.0_postcss@8.4.21 + postcss-js: 4.0.1_postcss@8.4.21 + postcss-load-config: 3.1.4_postcss@8.4.21 + postcss-nested: 6.0.0_postcss@8.4.21 + postcss-selector-parser: 6.0.11 + postcss-value-parser: 4.2.0 + quick-lru: 5.1.1 + resolve: 1.22.1 + transitivePeerDependencies: + - ts-node + dev: true + + /to-regex-range/5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: true + + /tslib/2.5.0: + resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} + dev: true + + /universalify/2.0.0: + resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} + engines: {node: '>= 10.0.0'} + dev: true + + /update-browserslist-db/1.0.10_browserslist@4.21.5: + resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.21.5 + escalade: 3.1.1 + picocolors: 1.0.0 + dev: true + + /util-deprecate/1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + dev: true + + /vite-plugin-wasm-pack/0.1.12: + resolution: {integrity: sha512-WliYvQp9HXluir4OKGbngkcKxtYtifU11cqLurRRJGsl770Sjr1iIkp5RuvU3IC1poT4A57Z2/YgAKI2Skm7ZA==} + dependencies: + chalk: 4.1.2 + fs-extra: 10.1.0 + narrowing: 1.5.0 + dev: true + + /vite/4.1.1: + resolution: {integrity: sha512-LM9WWea8vsxhr782r9ntg+bhSFS06FJgCvvB0+8hf8UWtvaiDagKYWXndjfX6kGl74keHJUcpzrQliDXZlF5yg==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + esbuild: 0.16.17 + postcss: 8.4.21 + resolve: 1.22.1 + rollup: 3.14.0 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /vitefu/0.2.4_vite@4.1.1: + resolution: {integrity: sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 + peerDependenciesMeta: + vite: + optional: true + dependencies: + vite: 4.1.1 + dev: true + + /wrappy/1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true + + /xtend/4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + dev: true + + /yaml/1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + dev: true diff --git a/scrapper_web/postcss.config.cjs b/scrapper_web/postcss.config.cjs new file mode 100644 index 0000000..059077c --- /dev/null +++ b/scrapper_web/postcss.config.cjs @@ -0,0 +1,11 @@ +let cssnano_plugin = {}; +if (process.env.NODE_ENV === "production") { + cssnano_plugin = { cssnano: { preset: "advanced" } }; +} +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + ...cssnano_plugin, + }, +}; diff --git a/scrapper_web/public/vite.svg b/scrapper_web/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/scrapper_web/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/scrapper_web/scrapper/.gitignore b/scrapper_web/scrapper/.gitignore new file mode 100644 index 0000000..6985cf1 --- /dev/null +++ b/scrapper_web/scrapper/.gitignore @@ -0,0 +1,14 @@ +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb diff --git a/scrapper_web/scrapper/Cargo.toml b/scrapper_web/scrapper/Cargo.toml new file mode 100644 index 0000000..ee8e4d6 --- /dev/null +++ b/scrapper_web/scrapper/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "scrapper" +version = "0.1.0" +authors = [] +edition = "2021" + +[lib] +crate-type = ["cdylib", "rlib"] + +[profile.release] +lto = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +aes = "0.8.2" +anyhow = "1.0.69" +binrw = "0.11.1" +cbc = "0.1.2" +console_error_panic_hook = "0.1.7" +derivative = "2.2.0" +js-sys = "0.3.61" +pelite = "0.10.0" +serde = { version = "1.0.152", features = ["derive"] } +serde-wasm-bindgen = "0.4.5" +wasm-bindgen = "0.2.83" +wasm-bindgen-file-reader = "1.0.0" +web-sys = { version = "0.3.61", features = ["File", "BlobPropertyBag", "Blob", "Url"] } + +[package.metadata.wasm-pack.profile.release] +wasm-opt = ["-O4"] diff --git a/scrapper_web/scrapper/README.md b/scrapper_web/scrapper/README.md new file mode 100644 index 0000000..a923c54 --- /dev/null +++ b/scrapper_web/scrapper/README.md @@ -0,0 +1,23 @@ +# scrapper + +## Usage + +[rsw-rs doc](https://github.com/lencx/rsw-rs) + +```bash +# install rsw +cargo install rsw + +# --- help --- +# rsw help +rsw -h +# new help +rsw new -h + +# --- usage --- +# dev +rsw watch + +# production +rsw build +``` diff --git a/scrapper_web/scrapper/src/lib.rs b/scrapper_web/scrapper/src/lib.rs new file mode 100644 index 0000000..fda2f4b --- /dev/null +++ b/scrapper_web/scrapper/src/lib.rs @@ -0,0 +1,155 @@ +use binrw::{binread, BinReaderExt}; +use serde::Serialize; +use std::collections::BTreeMap; +use std::io::{Read, Seek, SeekFrom}; +use wasm_bindgen::prelude::*; +use wasm_bindgen_file_reader::WebSysFile; +use web_sys::{Blob, File}; + +type JsResult = Result; + +#[binread] +#[derive(Serialize, Debug)] +struct ScrapFile { + #[br(temp)] + name_len: u32, + #[br(count = name_len)] + #[br(map = |s: Vec| String::from_utf8_lossy(&s).to_string())] + path: String, + size: u32, + offset: u32, +} + +#[binread] +#[br(magic = b"BFPK", little)] +#[derive(Serialize, Debug)] +struct PackedHeader { + version: u32, + #[br(temp)] + num_files: u32, + #[br(count= num_files)] + files: Vec, +} + +#[derive(Serialize, Debug)] +#[serde(tag = "type", rename_all = "snake_case")] +enum DirectoryTree { + File { + size: u32, + offset: u32, + file_index: u8, + }, + Directory { + entries: BTreeMap, + }, +} + +#[wasm_bindgen(inspectable)] +pub struct MultiPack { + files: Vec<(String,WebSysFile)>, + tree: DirectoryTree, +} + +fn blob_url(buffer: &[u8]) -> JsResult { + let uint8arr = + js_sys::Uint8Array::new(&unsafe { js_sys::Uint8Array::view(buffer) }.into()); + let array = js_sys::Array::new(); + array.push(&uint8arr.buffer()); + let blob = Blob::new_with_u8_array_sequence_and_options( + &array, + web_sys::BlobPropertyBag::new().type_("application/octet-stream"), + ) + .unwrap(); + web_sys::Url::create_object_url_with_blob(&blob) +} + +#[wasm_bindgen] +impl MultiPack { + #[wasm_bindgen(constructor)] + pub fn parse(files: Vec) -> Self { + let mut tree = DirectoryTree::default(); + let mut web_files = vec![]; + for (file_index, file) in files.into_iter().enumerate() { + let file_name = file.name(); + let mut fh = WebSysFile::new(file); + let header = fh.read_le::().unwrap(); + tree.merge(&header.files, file_index.try_into().unwrap()); + web_files.push((file_name,fh)); + } + Self { + tree, + files: web_files, + } + } + + #[wasm_bindgen] + pub fn tree(&self) -> JsValue { + serde_wasm_bindgen::to_value(&self.tree).unwrap() + } + + #[wasm_bindgen] + pub fn download( + &mut self, + file_index: u8, + offset: u32, + size: u32, + ) -> Result { + let Some((_,file)) = self.files.get_mut(file_index as usize) else { + return Err("File not found".into()); + }; + let mut buffer = vec![0u8; size as usize]; + file.seek(SeekFrom::Start(offset as u64)) + .map_err(|e| format!("Failed to seek file: {e}"))?; + file.read(&mut buffer) + .map_err(|e| format!("Failed to read from file: {e}"))?; + Ok(blob_url(&buffer)?.into()) + } +} + +impl Default for DirectoryTree { + fn default() -> Self { + Self::Directory { + entries: Default::default(), + } + } +} + +impl DirectoryTree { + fn add_child(&mut self, name: &str, node: Self) -> &mut Self { + match self { + Self::File { .. } => panic!("Can't add child to file!"), + Self::Directory { + entries + } => entries.entry(name.to_owned()).or_insert(node), + } + } + + fn merge(&mut self, files: &[ScrapFile], file_index: u8) { + for file in files { + let mut folder = &mut *self; + let path: Vec<_> = file.path.split('/').collect(); + if let Some((filename, path)) = path.as_slice().split_last() { + for part in path { + let DirectoryTree::Directory { entries } = folder else { + unreachable!(); + }; + folder = entries.entry(part.to_string()).or_default(); + } + folder.add_child( + filename, + DirectoryTree::File { + size: file.size, + offset: file.offset, + file_index, + }, + ); + } + } + } +} + +#[wasm_bindgen(start)] +pub fn main() -> Result<(), JsValue> { + console_error_panic_hook::set_once(); + Ok(()) +} diff --git a/scrapper_web/src/App.svelte b/scrapper_web/src/App.svelte new file mode 100644 index 0000000..70adfa0 --- /dev/null +++ b/scrapper_web/src/App.svelte @@ -0,0 +1,13 @@ + + +
+
+

Scrapland .packed explorer

+ +
+
+ + diff --git a/scrapper_web/src/app.pcss b/scrapper_web/src/app.pcss new file mode 100644 index 0000000..19ea342 --- /dev/null +++ b/scrapper_web/src/app.pcss @@ -0,0 +1,109 @@ +:root { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +.card { + padding: 2em; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +li { + text-align: left; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} + +.lds-dual-ring { + display: inline-block; + width: 80px; + height: 80px; +} +.lds-dual-ring:after { + content: " "; + display: block; + width: 64px; + height: 64px; + margin: 8px; + border-radius: 50%; + border: 6px solid #fff; + border-color: #fff transparent #fff transparent; + animation: lds-dual-ring 1.2s linear infinite; +} +@keyframes lds-dual-ring { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} diff --git a/scrapper_web/src/lib/Explorer.svelte b/scrapper_web/src/lib/Explorer.svelte new file mode 100644 index 0000000..5a7b44b --- /dev/null +++ b/scrapper_web/src/lib/Explorer.svelte @@ -0,0 +1,52 @@ + + +
+ +
+ +{#if tree} + {#each [...tree.entries] as [name, child]} + + {/each} +{/if} diff --git a/scrapper_web/src/lib/TreeView.svelte b/scrapper_web/src/lib/TreeView.svelte new file mode 100644 index 0000000..f2699f6 --- /dev/null +++ b/scrapper_web/src/lib/TreeView.svelte @@ -0,0 +1,56 @@ + + + +
    +
  • + {#if tree.type == "directory" && tree.entries} + + {#if expanded} + [-] + {:else} + [+] + {/if} + {label} + + {#if tree.entries && expanded} + {#each [...tree.entries] as [name, child]} + + {/each} + {/if} + {:else} + + + {label} + + {/if} +
  • +
+ + diff --git a/scrapper_web/src/main.js b/scrapper_web/src/main.js new file mode 100644 index 0000000..e7acd1d --- /dev/null +++ b/scrapper_web/src/main.js @@ -0,0 +1,6 @@ +import './app.pcss' +import App from './App.svelte' + +export default new App({ + target: document.getElementById('app'), +}); diff --git a/scrapper_web/src/scrapper.worker.js b/scrapper_web/src/scrapper.worker.js new file mode 100644 index 0000000..a1146b1 --- /dev/null +++ b/scrapper_web/src/scrapper.worker.js @@ -0,0 +1,28 @@ +import wasm, { MultiPack } from "scrapper"; + +async function initialize() { + await wasm(); + let pack; + let handlers = { + parse(data) { + pack = new MultiPack(data); + return pack.tree(); + }, + download(data) { + if (pack) { + let { label, file_index, offset, size } = data; + return [label, pack.download(file_index, offset, size)]; + } + }, + }; + self.onmessage = (event) => { + for (var [name, func] of Object.entries(handlers)) { + let data = event.data[name]; + if (data) { + postMessage(Object.fromEntries([[name, func(data)]])); + } + } + }; +} + +initialize(); diff --git a/scrapper_web/src/vite-env.d.ts b/scrapper_web/src/vite-env.d.ts new file mode 100644 index 0000000..4078e74 --- /dev/null +++ b/scrapper_web/src/vite-env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/scrapper_web/svelte.config.js b/scrapper_web/svelte.config.js new file mode 100644 index 0000000..66fa030 --- /dev/null +++ b/scrapper_web/svelte.config.js @@ -0,0 +1,6 @@ +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte' +export default { + // Consult https://svelte.dev/docs#compile-time-svelte-preprocess + // for more information about preprocessors + preprocess: vitePreprocess(), +} diff --git a/scrapper_web/tailwind.config.cjs b/scrapper_web/tailwind.config.cjs new file mode 100644 index 0000000..ad70a37 --- /dev/null +++ b/scrapper_web/tailwind.config.cjs @@ -0,0 +1,36 @@ +module.exports = { + content: ["./src/**/*.{svelte,js,ts}"], + plugins: [require("@tailwindcss/forms"),require("daisyui")], + theme: { + container: { + center: true, + }, + }, + daisyui: { + styled: true, + themes: true, + base: true, + utils: true, + logs: true, + rtl: false, + prefix: "", + darkTheme: "scraptool", + themes: [ + { + scraptool: { + primary: "#F28C18", + secondary: "#b45309", + accent: "#22d3ee", + neutral: "#1B1D1D", + "base-100": "#212121", + info: "#2463EB", + success: "#16A249", + warning: "#DB7706", + error: "#DC2828", + // "--rounded-box": "0.4rem", + // "--rounded-btn": "0.2rem" + }, + }, + ], + }, +}; diff --git a/scrapper_web/vite.config.js b/scrapper_web/vite.config.js new file mode 100644 index 0000000..6935bc3 --- /dev/null +++ b/scrapper_web/vite.config.js @@ -0,0 +1,10 @@ +import { defineConfig } from 'vite' +import { svelte } from '@sveltejs/vite-plugin-svelte' +import wasmPack from 'vite-plugin-wasm-pack'; +import preprocess from 'svelte-preprocess'; + +export default defineConfig({ + plugins: [wasmPack("./scrapper/"),svelte({ + preprocess: preprocess({ postcss: true }) + })] +}); diff --git a/tools/ghidra_scripts/mark_up_py.py b/tools/ghidra_scripts/mark_up_py.py new file mode 100644 index 0000000..5dc9d71 --- /dev/null +++ b/tools/ghidra_scripts/mark_up_py.py @@ -0,0 +1,125 @@ +import time +try: + import ghidra_bridge + has_bridge=True +except ImportError: + has_bridge=False + +from contextlib import contextmanager + +if has_bridge: + import ghidra_bridge + b = ghidra_bridge.GhidraBridge(namespace=globals(), hook_import=True) + @contextmanager + def transaction(): + start() + try: + yield + except Exception as e: + end(False) + raise e + end(True) +else: + @contextmanager + def transaction(): + yield + +import ghidra.program.model.symbol.SymbolType as SymbolType +import ghidra.program.model.symbol.SourceType as SourceType +from ghidra.app.cmd.label import CreateNamespacesCmd +from ghidra.program.model.data.DataUtilities import createData +from ghidra.program.model.data.DataUtilities import ClearDataMode +from ghidra.program.model.listing.CodeUnit import PLATE_COMMENT + +listing = currentProgram.getListing() +dtm = currentProgram.getDataTypeManager() +py_mod = dtm.getDataType("/PyModuleDef") +py_meth = dtm.getDataType("/PyMethodDef") + +NULL=toAddr(0) + +def make_namespace(parts): + ns_cmd = CreateNamespacesCmd("::".join(parts), SourceType.USER_DEFINED) + ns_cmd.applyTo(currentProgram) + return ns_cmd.getNamespace() + +def create_data(addr,dtype): + return createData(currentProgram,addr,dtype,0,False,ClearDataMode.CLEAR_ALL_CONFLICT_DATA) + +def create_str(addr): + if addr.equals(NULL): + return None + str_len = (findBytes(addr, b"\0").offset - addr.offset) + 1 + clearListing(addr, addr.add(str_len)) + return createAsciiString(addr) + +def get_call_obj(addr): + func = getFunctionContaining(addr) + if func is None: + disassemble(addr) + func = createFunction(addr,None) + call_obj = {"this": None, "stack": []} + for inst in currentProgram.listing.getInstructions(func.body, True): + affected_objs = [r.toString() for r in inst.resultObjects.tolist()] + inst_name = inst.getMnemonicString() + if inst_name == "PUSH": + val=inst.getScalar(0) + if val is not None: + call_obj["stack"].insert(0, toAddr(val.getValue()).toString()) + elif inst_name == "MOV" and "ECX" in affected_objs: + this = inst.getScalar(1) + if this is not None: + call_obj["this"] = toAddr(this.getValue()).toString() + elif inst_name == "CALL": + break + func=func.symbol.address + return func, call_obj + +def data_to_dict(data): + ret={} + for idx in range(data.dataType.getNumComponents()): + name=data.dataType.getComponent(idx).getFieldName() + value=data.getComponent(idx).getValue() + ret[name]=value + return ret + +def try_create_str(addr): + ret=create_str(addr) + if ret: + return ret.getValue() + +with transaction(): + PyInitModule=getSymbolAt(toAddr("006f31c0")) + for ref in getReferencesTo(PyInitModule.address).tolist(): + func,args=get_call_obj(ref.fromAddress) + print(func,args) + module_name=create_str(toAddr(args['stack'][0])).getValue() + methods=toAddr(args['stack'][1]) + module_doc=create_str(toAddr(args['stack'][2])) + if module_doc: + module_doc=module_doc.getValue() + print(methods,module_name,module_doc) + mod_ns = make_namespace(["Python", module_name]) + createLabel(func, "__init__", mod_ns, True, SourceType.USER_DEFINED) + if module_doc: + listing.getCodeUnitAt(func).setComment(PLATE_COMMENT,module_doc) + while True: + mod_data=data_to_dict(create_data(methods,py_meth)) + if mod_data['name'] is None: + clearListing(methods, methods.add(16)) + break + mod_data['name']=try_create_str(mod_data['name']) + try: + mod_data['doc']=try_create_str(mod_data['doc']) + except: + mod_data['doc']=None + print(mod_data) + createLabel(mod_data['ml_method'], mod_data['name'], mod_ns, True, SourceType.USER_DEFINED) + if mod_data['doc']: + listing.getCodeUnitAt(mod_data['ml_method']).setComment(PLATE_COMMENT,module_doc) + methods=methods.add(16) + try: + if getBytes(methods,4).tolist()==[0,0,0,0]: + break + except: + break \ No newline at end of file diff --git a/tools/remaster/scrap_parse/.gitignore b/tools/remaster/scrap_parse/.gitignore new file mode 100644 index 0000000..758f49c --- /dev/null +++ b/tools/remaster/scrap_parse/.gitignore @@ -0,0 +1,177 @@ +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +*.pkl.gz \ No newline at end of file diff --git a/tools/remaster/scrap_parse/Cargo.lock b/tools/remaster/scrap_parse/Cargo.lock index 90147a3..533cb1d 100644 --- a/tools/remaster/scrap_parse/Cargo.lock +++ b/tools/remaster/scrap_parse/Cargo.lock @@ -8,6 +8,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aho-corasick" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +dependencies = [ + "memchr", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -18,10 +27,59 @@ dependencies = [ ] [[package]] -name = "anyhow" -version = "1.0.69" +name = "anstream" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" +checksum = "6342bd4f5a1205d7f41e94a41a901f5647c938cdfa96036338e8533c9d6c2450" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" + +[[package]] +name = "anstyle-parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + +[[package]] +name = "anyhow" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" [[package]] name = "array-init" @@ -56,7 +114,7 @@ dependencies = [ "owo-colors", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -66,16 +124,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] -name = "bumpalo" -version = "3.12.0" +name = "block-buffer" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" [[package]] name = "bytemuck" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c041d3eab048880cb0b86b256447da3f18859a163c3b8d8893f4e6368abe6393" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "cc" @@ -91,9 +164,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" dependencies = [ "iana-time-zone", "js-sys", @@ -106,50 +179,46 @@ dependencies = [ ] [[package]] -name = "chrono-humanize" -version = "0.2.2" +name = "clap" +version = "4.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32dce1ea1988dbdf9f9815ff11425828523bd2a134ec0805d2ac8af26ee6096e" +checksum = "956ac1f6381d8d82ab4684768f89c0ea3afe66925ceadb4eeb3fc452ffc55d62" dependencies = [ - "chrono", + "clap_builder", + "clap_derive", + "once_cell", ] [[package]] -name = "clap" -version = "4.1.6" +name = "clap_builder" +version = "4.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0b0588d44d4d63a87dbd75c136c166bbfd9a86a31cb89e09906521c7d3f5e3" +checksum = "84080e799e54cff944f4b4a4b0e71630b0e0443b25b985175c7dddc1a859b749" dependencies = [ + "anstream", + "anstyle", "bitflags", - "clap_derive", "clap_lex", - "is-terminal", - "once_cell", "strsim", - "termcolor", ] [[package]] name = "clap_derive" -version = "4.1.0" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" +checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" dependencies = [ "heck", - "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] name = "clap_lex" -version = "0.3.1" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" -dependencies = [ - "os_str_bytes", -] +checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" [[package]] name = "codespan-reporting" @@ -161,6 +230,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "configparser" version = "3.0.2" @@ -172,9 +247,18 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "cpufeatures" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +dependencies = [ + "libc", +] [[package]] name = "crc32fast" @@ -186,10 +270,20 @@ dependencies = [ ] [[package]] -name = "cxx" -version = "1.0.91" +name = "crypto-common" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cxx" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" dependencies = [ "cc", "cxxbridge-flags", @@ -199,9 +293,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.91" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690" +checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" dependencies = [ "cc", "codespan-reporting", @@ -209,24 +303,54 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn", + "syn 2.0.15", ] [[package]] name = "cxxbridge-flags" -version = "1.0.91" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf" +checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" [[package]] name = "cxxbridge-macro" -version = "1.0.91" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892" +checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dirs" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dece029acd3353e3a58ac2e3eb3c8d6c35827a892edc6cc4138ef9c33df46ecd" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04414300db88f70d74c5ff54e50f9e1d1737d9a5b90f53fcf2e95ca2a9ab554b" +dependencies = [ + "libc", + "redox_users", + "windows-sys 0.45.0", ] [[package]] @@ -237,13 +361,13 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "errno" -version = "0.2.8" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -272,6 +396,27 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0845fa252299212f0389d64ba26f34fa32cfe41588355f21ed507c59a0f64541" +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -292,16 +437,16 @@ checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" [[package]] name = "iana-time-zone" -version = "0.1.53" +version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "winapi", + "windows", ] [[package]] @@ -316,9 +461,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown", @@ -327,31 +472,38 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.5" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" dependencies = [ + "hermit-abi", "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "is-terminal" -version = "0.4.3" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ "hermit-abi", "io-lifetimes", "rustix", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] -name = "itoa" -version = "1.0.5" +name = "iter-read" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" +checksum = "c397ca3ea05ad509c4ec451fea28b4771236a376ca1c69fd5143aae0cf8f93c4" + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "js-sys" @@ -363,10 +515,35 @@ dependencies = [ ] [[package]] -name = "libc" -version = "0.2.139" +name = "keyvalues-parser" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "7d990301996c856ea07a84bc291e76f1273db52683663efc05c8d355976897e5" +dependencies = [ + "pest", + "pest_derive", + "thiserror", +] + +[[package]] +name = "keyvalues-serde" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da419ac133bb3ddf0dbf9c12fcc0ce01d994fcb65f6f1713faf15cc689320b5f" +dependencies = [ + "keyvalues-parser", + "once_cell", + "paste", + "regex", + "serde", + "thiserror", +] + +[[package]] +name = "libc" +version = "0.2.142" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" [[package]] name = "link-cplusplus" @@ -379,9 +556,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.1.4" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf" [[package]] name = "log" @@ -392,6 +569,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + [[package]] name = "miniz_oxide" version = "0.6.2" @@ -419,7 +602,24 @@ checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", +] + +[[package]] +name = "nom" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5b8c256fd9471521bcb84c3cdba98921497f1a331cbc15b8030fc63b82050ce" + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", ] [[package]] @@ -441,18 +641,18 @@ dependencies = [ "autocfg", ] +[[package]] +name = "obj" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "059c95245738cdc7b40078cdd51a23200252a4c0a0a6dd005136152b3f467a4a" + [[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" @@ -460,47 +660,110 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" [[package]] -name = "proc-macro-error" -version = "1.0.4" +name = "paste" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" + +[[package]] +name = "pest" +version = "2.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1403e8401ad5dedea73c626b99758535b342502f8d1e361f4a2dd952749122" dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", + "thiserror", + "ucd-trie", ] [[package]] -name = "proc-macro-error-attr" -version = "1.0.4" +name = "pest_derive" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +checksum = "be99c4c1d2fc2769b1d00239431d711d08f6efedcecb8b6e30707160aee99c15" dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e56094789873daa36164de2e822b3888c6ae4b4f9da555a1103587658c805b1e" +dependencies = [ + "pest", + "pest_meta", "proc-macro2", "quote", - "version_check", + "syn 2.0.15", +] + +[[package]] +name = "pest_meta" +version = "2.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6733073c7cff3d8459fda0e42f13a047870242aed8b509fe98000928975f359e" +dependencies = [ + "once_cell", + "pest", + "sha2", ] [[package]] name = "proc-macro2" -version = "1.0.51" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.23" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" + [[package]] name = "rhexdump" version = "0.1.1" @@ -509,23 +772,32 @@ checksum = "c5e9af64574935e39f24d1c0313a997c8b880ca0e087c888bc6af8af31579847" [[package]] name = "rustix" -version = "0.36.8" +version = "0.37.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" +checksum = "d9b864d3c18a5785a05953adeed93e2dca37ed30f18e69bba9f30079d51f363f" dependencies = [ "bitflags", "errno", "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "ryu" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] [[package]] name = "scrap_parse" @@ -534,61 +806,112 @@ dependencies = [ "anyhow", "binrw", "chrono", - "chrono-humanize", "clap", "configparser", "flate2", "fs-err", "indexmap", "modular-bitfield", + "obj", "rhexdump", "serde", + "serde-pickle", "serde_json", + "steamlocate", + "walkdir", ] [[package]] name = "scratch" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" +checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" [[package]] name = "serde" -version = "1.0.152" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" dependencies = [ "serde_derive", ] [[package]] -name = "serde_derive" -version = "1.0.152" +name = "serde-pickle" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +checksum = "c762ad136a26407c6a80825813600ceeab5e613660d93d79a41f0ec877171e71" +dependencies = [ + "byteorder", + "iter-read", + "num-bigint", + "num-traits", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.160" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] name = "serde_json" -version = "1.0.93" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ + "indexmap", "itoa", "ryu", "serde", ] +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "steamlocate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec01c74611d14a808cb212d17c6e03f0e30736a15ed1d5736f8a53154cea3ae" +dependencies = [ + "dirs", + "keyvalues-parser", + "keyvalues-serde", + "serde", + "steamy-vdf", + "winreg", +] + +[[package]] +name = "steamy-vdf" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "533127ad49314bfe71c3d3fd36b3ebac3d24f40618092e70e1cfe8362c7fac79" +dependencies = [ + "nom", +] + [[package]] name = "strsim" version = "0.10.0" @@ -597,9 +920,20 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.107" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" dependencies = [ "proc-macro2", "quote", @@ -615,6 +949,26 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "thiserror" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + [[package]] name = "time" version = "0.1.45" @@ -622,15 +976,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" dependencies = [ "libc", - "wasi", + "wasi 0.10.0+wasi-snapshot-preview1", "winapi", ] [[package]] -name = "unicode-ident" -version = "1.0.6" +name = "typenum" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "ucd-trie" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "unicode-width" @@ -638,18 +1004,40 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "walkdir" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "wasm-bindgen" version = "0.2.84" @@ -671,7 +1059,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-shared", ] @@ -693,7 +1081,7 @@ checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -735,68 +1123,153 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.0", +] + [[package]] name = "windows-sys" version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", ] [[package]] name = "windows-targets" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" 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", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "winreg" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a1a57ff50e9b408431e8f97d5456f2807f8eb2a2cd79b06068fc87f8ecf189" +dependencies = [ + "cfg-if", + "winapi", +] diff --git a/tools/remaster/scrap_parse/Cargo.toml b/tools/remaster/scrap_parse/Cargo.toml index bdb3554..77904e2 100644 --- a/tools/remaster/scrap_parse/Cargo.toml +++ b/tools/remaster/scrap_parse/Cargo.toml @@ -9,13 +9,18 @@ edition = "2021" anyhow = "1.0.69" binrw = "0.11.1" chrono = { version = "0.4.23", features = ["serde"] } -chrono-humanize = "0.2.2" +# 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"] } +# memmap2 = "0.5.10" 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"] } +serde-pickle = "1.1.1" +serde_json = { version = "1.0.95", features = ["preserve_order", "unbounded_depth"] } +steamlocate = "1.1.0" +walkdir = "2.3.3" +obj = "0.10.2" diff --git a/tools/remaster/scrap_parse/blender_plugin/__init__.py b/tools/remaster/scrap_parse/blender_plugin/__init__.py new file mode 100644 index 0000000..84c8e51 --- /dev/null +++ b/tools/remaster/scrap_parse/blender_plugin/__init__.py @@ -0,0 +1,23 @@ +import pickle +import subprocess as SP + +from . import packed_browser +from . import level_import + +def scrap_bridge(*cmd): + cmd=["scrap_parse",*cmd] + proc=SP.Popen(cmd,stderr=None,stdin=None,stdout=SP.PIPE,shell=True,text=False) + stdout,stderr=proc.communicate() + code=proc.wait() + if code: + raise RuntimeError(str(stderr,"utf8")) + return pickle.loads(stdout) + +def register(): + packed_browser.register() + level_import.regiser() + +def unregister(): + packed_browser.unregister() + level_import.unregister() + diff --git a/tools/remaster/scrap_parse/blender_import.py b/tools/remaster/scrap_parse/blender_plugin/level_import.py similarity index 87% rename from tools/remaster/scrap_parse/blender_import.py rename to tools/remaster/scrap_parse/blender_plugin/level_import.py index eeae6cb..d0ef765 100644 --- a/tools/remaster/scrap_parse/blender_import.py +++ b/tools/remaster/scrap_parse/blender_plugin/level_import.py @@ -2,16 +2,16 @@ import bpy import sys import os import re -import json import gzip +import pickle 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 +# from .. import scrap_bridge import bmesh from bpy.props import StringProperty, BoolProperty from bpy_extras.io_utils import ImportHelper @@ -25,12 +25,6 @@ if "--" in sys.argv: 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() @@ -39,16 +33,22 @@ class ScrapImporter(object): self.model_scale = 1000.0 self.spawn_pos = {} self.objects = {} - print("Loading", filepath) - with gzip.open(filepath, "r") as fh: - data = json.load(fh) + # print("Loading", filepath) + # scrapland_path=scrap_bridge("find-scrapland") + # print(scrapland_path) + # packed_data=scrap_bridge("parse-packed",scrapland_path) + # print(packed_data) + # get_output(["scrap_parse","parse-file","--stdout",scrapland_path,"levels/temple"]) + # raise NotImplementedError() + with gzip.open(filepath, "rb") as fh: + data = pickle.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.dummies = data.pop("dummies")["dummies"] self.moredummies = data.pop("moredummies") - self.emi = data.pop("emi")["EMI"] - self.sm3 = data.pop("sm3")["SM3"] + self.emi = data.pop("emi") + self.sm3 = data.pop("sm3") def make_empty(self, name, pos, rot=None): empty = bpy.data.objects.new(name, None) @@ -119,7 +119,7 @@ class ScrapImporter(object): bpy.context.scene.collection.objects.link(light) def create_nodes(self): - for node in self.sm3["scene"]["nodes"]: + for node in self.sm3["scene"].get("nodes",[]): node_name = node["name"] node = node.get("content", {}) if not node: @@ -212,6 +212,8 @@ class ScrapImporter(object): ) else: folders = ITT.chain([start_folder], start_folder.parents) + folders=list(folders) + print(f"Looking for {path} in {folders}") for folder in folders: for suffix in file_extensions: for dds in [".", "dds"]: @@ -227,7 +229,7 @@ class ScrapImporter(object): return list(filter(lambda i: (i.type, i.name) == (dtype, name), node.inputs)) - def build_material(self, mat_key, mat_def): + def build_material(self, mat_key, mat_def, map_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 @@ -260,13 +262,13 @@ class ScrapImporter(object): "Roughness": 0.0, "Specular": 0.2, } - tex_slots=[ - "Base Color", - "Metallic", - None, # "Clearcoat" ? env map? - "Normal", - "Emission" - ] + tex_slot_map={ + "base": "Base Color", + "metallic":"Metallic", + "unk_1":None, # "Clearcoat" ? env map? + "bump":"Normal", + "glow":"Emission" + } mat = bpy.data.materials.new(mat_key) mat.use_nodes = True @@ -275,7 +277,13 @@ class ScrapImporter(object): imgs = {} animated_textures={} is_transparent = True - for slot,tex in zip(tex_slots,mat_def["maps"]): + print(map_def) + if map_def[0]: + print("=== MAP[0]:",self.resolve_path(map_def[0])) + if map_def[2]: + print("=== MAP[2]:",self.resolve_path(map_def[2])) + for slot,tex in mat_def["maps"].items(): + slot=tex_slot_map.get(slot) if (slot is None) and tex: self.unhandled.add(tex["texture"]) print(f"Don't know what to do with {tex}") @@ -286,9 +294,7 @@ class ScrapImporter(object): 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 + animated_textures[slot]=len(glob(tex_file.replace(".000.",".*."))) mat_props.update(overrides.get(tex_name,{})) if any( tex_name.find(fragment) != -1 @@ -297,7 +303,7 @@ class ScrapImporter(object): continue else: is_transparent = False - imgs[slot]=bpy.data.images.load(tex_file) + imgs[slot]=bpy.data.images.load(tex_file,check_existing=True) for n in nodes: nodes.remove(n) out = nodes.new("ShaderNodeOutputMaterial") @@ -311,7 +317,6 @@ class ScrapImporter(object): 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 @@ -369,17 +374,20 @@ class ScrapImporter(object): 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"]) + # try: + # bpy.ops.node.button() + # except: + # pass 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 + map_key, m_map = m_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) + ob.active_material = self.build_material(mat_name, m_mat, m_map) else: ob.active_material = bpy.data.materials[mat_name] @@ -424,17 +432,17 @@ class ScrapImporter(object): 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) + self.objects.setdefault(name.split("(")[0], []).append(ob) return ob class Scrap_Load(Operator, ImportHelper): - bl_idname = "scrap_utils.import_json" - bl_label = "Import JSON" + bl_idname = "scrap_utils.import_pickle" + bl_label = "Import Pickle" - filename_ext = ".json.gz" - filter_glob: StringProperty(default="*.json.gz", options={"HIDDEN"}) + filename_ext = ".pkl.gz" + filter_glob: StringProperty(default="*.pkl.gz", options={"HIDDEN"}) create_dummies: BoolProperty( name="Import dummies", @@ -447,25 +455,20 @@ class Scrap_Load(Operator, ImportHelper): ) create_tracks: BoolProperty( - name="Create track curves", - default=True + name="Create track curves", + default=True ) merge_objects: BoolProperty( - name="Merge objects by name", - default=False + name="Merge objects by name", + default=False ) remove_dup_verts: BoolProperty( - name="Remove overlapping vertices\nfor smoother meshes", - default=True + 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") @@ -488,7 +491,7 @@ def unregister(): if __name__ == "__main__": if cmdline is None or not cmdline.file_list: register() - bpy.ops.scrap_utils.import_json("INVOKE_DEFAULT") + bpy.ops.scrap_utils.import_pickle("INVOKE_DEFAULT") else: for file in cmdline.file_list: bpy.context.preferences.view.show_splash = False diff --git a/tools/remaster/scrap_parse/blender_plugin/packed_browser.py b/tools/remaster/scrap_parse/blender_plugin/packed_browser.py new file mode 100644 index 0000000..3981c53 --- /dev/null +++ b/tools/remaster/scrap_parse/blender_plugin/packed_browser.py @@ -0,0 +1,118 @@ +import sys +from .. import scrap_bridge +from bpy.props import (StringProperty, BoolProperty, CollectionProperty, + IntProperty) + +bl_info = { + "name": "Packed Archive File", + "blender": (2, 71, 0), + "location": "File > Import", + "description": "Import data from Scrapland .packed Archive", + "category": "Import-Export"} + + + + +class ImportFilearchives(bpy.types.Operator): + """Import whole filearchives directory.""" + bl_idname = "import_scene.packed" + bl_label = 'Import Scrapland .packed' + + directory = StringProperty(name="'Scrapland' 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.packed_browser('INVOKE_DEFAULT',filepath=self.directory) + return {'FINISHED'} + + +class PackedFile(bpy.types.PropertyGroup): + path = bpy.props.StringProperty() + packed_file = bpy.props.StringProperty() + selected = bpy.props.BoolProperty(name="") + offset = bpy.props.IntProperty() + size = bpy.props.IntProperty() + + +archive = None +class PackedBrowser(bpy.types.Operator): + bl_idname = "ui.packed_browser" + bl_label = "Packed Browser" + bl_options = {'INTERNAL'} + + files = CollectionProperty(type=PackedFile) + selected_index = IntProperty(default=0) + + def invoke(self, context, event): + scrapland_path=scrap_bridge("find-scrapland") + print(scrapland_path) + packed_data=scrap_bridge("parse-packed",scrapland_path) + print(packed_data) + self.packed_data=packed_data + 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)) + self.files.clear() + for packed_name,files in self.archive: + for file in files: + entry = self.files.add() + entry.packed_file = packed_name + [entry.path,entry.offset,entry.size]=file + self.selected_index = -1 + self.layout.template_list("PackedDirList", "", self, "current_dir", self, "selected_index") + + def execute(self, context): + print("execute") + return {'FINISHED'} + + +class PackedDirList(bpy.types.UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname): + operator = data + packed_entry = item + + if self.layout_type in {'DEFAULT', 'COMPACT'}: + layout.prop(packed_entry, "name", text="", emboss=False, icon_value=icon) + layout.prop(packed_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="Scrapland .packed") + +classes=[ + PackedFile, + PackedDirList, + PackedBrowser, + ImportFilearchives, +] + +def register(): + for cls in classes: + bpy.utils.regiser_class(cls) + bpy.types.INFO_MT_file_import.append(menu_func_import) + + +def unregister(): + for cls in reversed(classes): + bpy.utils.unregister_class(cls) + bpy.types.INFO_MT_file_import.remove(menu_func_import) + + +if __name__ == "__main__": + import imp + imp.reload(sys.modules[__name__]) + for cls in classes: + bpy.utils.regiser_class(cls) + diff --git a/tools/remaster/scrap_parse/get_vertex_size.cpp b/tools/remaster/scrap_parse/get_vertex_size.cpp new file mode 100644 index 0000000..783eec4 --- /dev/null +++ b/tools/remaster/scrap_parse/get_vertex_size.cpp @@ -0,0 +1,66 @@ + +int _D3DXGetFVFVertexSize(uint fvf) + +{ + uint uVar1; + uint uVar2; + uint uVar3; + int vert_size; + + uVar1 = fvf & 0xe; + vert_size = 0; + if (uVar1 == 2) { + vert_size = 0xc; + } + else if ((uVar1 == 4) || (uVar1 == 6)) { + vert_size = 0x10; + } + else if (uVar1 == 8) { + vert_size = 0x14; + } + else if (uVar1 == 0xa) { + vert_size = 0x18; + } + else if (uVar1 == 0xc) { + vert_size = 0x1c; + } + else if (uVar1 == 0xe) { + vert_size = 0x20; + } + if ((fvf & 0x10) != 0) { + vert_size += 0xc; + } + if ((fvf & 0x20) != 0) { + vert_size += 4; + } + if ((fvf & 0x40) != 0) { + vert_size += 4; + } + if (fvf < '\0') { + vert_size += 4; + } + uVar1 = fvf >> 8 & 0xf; + uVar3 = fvf >> 16; + if (uVar3 == 0) { + vert_size += uVar1 * 8; + } + else { + for (; uVar1 != 0; uVar1 -= 1) { + uVar2 = uVar3 & 3; + if (uVar2 == 0) { + vert_size += 8; + } + else if (uVar2 == 1) { + vert_size += 0xc; + } + else if (uVar2 == 2) { + vert_size += 0x10; + } + else if (uVar2 == 3) { + vert_size += 4; + } + uVar3 >>= 2; + } + } + return vert_size; +} \ No newline at end of file diff --git a/tools/remaster/scrap_parse/packed_loader.py b/tools/remaster/scrap_parse/packed_loader.py deleted file mode 100644 index ffa6b34..0000000 --- a/tools/remaster/scrap_parse/packed_loader.py +++ /dev/null @@ -1,103 +0,0 @@ -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/find_scrap.rs b/tools/remaster/scrap_parse/src/find_scrap.rs new file mode 100644 index 0000000..4c69b3f --- /dev/null +++ b/tools/remaster/scrap_parse/src/find_scrap.rs @@ -0,0 +1,15 @@ +use std::path::PathBuf; + +use steamlocate::SteamDir; +use anyhow::{bail,Result}; +const APP_ID: u32 = 897610; + +pub(crate) fn get_executable() -> Result { + let Some(mut steam) = SteamDir::locate() else { + bail!("Failed to find steam folder"); + }; + let Some(app) = steam.app(&APP_ID) else { + bail!("App {APP_ID} is not installed!"); + }; + Ok(app.path.clone()) +} \ No newline at end of file diff --git a/tools/remaster/scrap_parse/src/main.rs b/tools/remaster/scrap_parse/src/main.rs index 8d41f58..1dfaca3 100644 --- a/tools/remaster/scrap_parse/src/main.rs +++ b/tools/remaster/scrap_parse/src/main.rs @@ -5,6 +5,7 @@ use binrw::prelude::*; use binrw::until_exclusive; use chrono::{DateTime, NaiveDateTime, Utc}; use clap::Parser; +use clap::Subcommand; use configparser::ini::Ini; use flate2::write::GzEncoder; use flate2::Compression; @@ -15,14 +16,37 @@ 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::io::{BufReader, Cursor, Read, Seek}; use std::path::Path; use std::path::PathBuf; +use walkdir::WalkDir; + +mod find_scrap; + +type IniData = IndexMap>>; + +#[binread] +#[derive(Serialize, Debug)] +struct PackedFile{ + path: PascalString, + size: u32, + offset: u32 +} + +#[binread] +#[br(magic = b"BFPK")] +#[derive(Serialize, Debug)] +struct PackedHeader { + #[br(temp,assert(version==0))] + version: u32, + #[br(temp)] + num_files: u32, + #[br(count=num_files)] + files: Vec, +} #[binread] #[derive(Serialize, Debug)] @@ -141,6 +165,7 @@ struct IniSection { #[br(magic = b"INI\0")] #[derive(Debug)] struct INI { + #[br(temp)] size: u32, #[br(temp)] num_sections: u32, @@ -153,13 +178,17 @@ impl Serialize for INI { where S: serde::Serializer, { + use serde::ser::Error; 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) + Ini::new() + .read(blocks.join("\n")) + .map_err(Error::custom)? + .serialize(serializer) } } @@ -227,7 +256,7 @@ enum Pos { #[repr(u32)] #[derive(Debug, Serialize, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct FVF { - reserved_1: bool, + reserved: bool, pos: Pos, normal: bool, point_size: bool, @@ -267,17 +296,17 @@ impl FVF { } } - fn num_w(&self) -> usize { - use Pos::*; - match self.pos() { - XYZ | XYZRHW => 0, - XYZB1 => 1, - XYZB2 => 2, - XYZB3 => 3, - XYZB4 => 4, - XYZB5 => 5, - } - } + // 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 { @@ -361,6 +390,7 @@ struct MD3D { tris: Vec<[u16; 3]>, mesh_data: LFVF, unk_table_1: RawTable<2>, + rest: Unparsed<0x100> // TODO: // == // unk_t1_count: u32, @@ -383,7 +413,7 @@ enum NodeData { #[br(magic = 0x0u32)] Null, #[br(magic = 0xa1_00_00_01_u32)] - TriangleMesh, // Empty? + TriangleMesh(Unparsed<0x10>), // TODO: Empty or unused? #[br(magic = 0xa1_00_00_02_u32)] Mesh(MD3D), #[br(magic = 0xa2_00_00_04_u32)] @@ -393,7 +423,7 @@ enum NodeData { #[br(magic = 0xa4_00_00_10_u32)] Ground(SUEL), #[br(magic = 0xa5_00_00_20_u32)] - SisPart(Unparsed<0x10>), // TODO: Particles + SistPart(Unparsed<0x10>), // TODO: Particles #[br(magic = 0xa6_00_00_40_u32)] Graphic3D(SPR3), #[br(magic = 0xa6_00_00_80_u32)] @@ -521,6 +551,16 @@ struct MAP { unk_3: Option<[u8; 0xc]>, } +#[binread] +#[derive(Debug, Serialize)] +struct Textures { + base: Optional, + metallic: Optional, + unk_1: Optional, + bump: Optional, + glow: Optional +} + #[binread] #[br(magic = b"MAT\0")] #[derive(Debug, Serialize)] @@ -532,7 +572,7 @@ struct MAT { name: Option, unk_f: [RGBA; 7], unk_data: [RGBA; 0x18 / 4], - maps: [Optional; 5], // Base Color, Metallic?, ???, Normal, Emission + maps: Textures } #[binread] @@ -556,9 +596,9 @@ struct SCN { #[br(temp,assert(unk_3==1))] unk_3: u32, num_nodes: u32, - #[br(count = num_nodes)] // 32 + #[br(count = 1)] // 32 nodes: Vec, - ani: Optional, // TODO:? + // ani: Optional, // TODO: ? } fn convert_timestamp(dt: u32) -> Result> { @@ -682,11 +722,11 @@ struct CM3 { #[binread] #[derive(Debug, Serialize)] struct Dummy { + has_next: u32, name: PascalString, pos: [f32; 3], rot: [f32; 3], info: Optional, - has_next: u32, } #[binread] @@ -697,7 +737,6 @@ struct DUM { #[br(assert(version==1, "Invalid DUM version"))] version: u32, num_dummies: u32, - unk_1: u32, #[br(count=num_dummies)] dummies: Vec, } @@ -826,13 +865,6 @@ enum Data { 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)?); @@ -842,11 +874,11 @@ fn parse_file(path: &PathBuf) -> Result { .unwrap_or(0) .try_into() .unwrap_or(u32::MAX); - println!("Read {} bytes from {}", pos, path.display()); + eprintln!("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)); + eprintln!("Rest:\n{}", rhexdump::hexdump_offset(&buffer[..n], pos)); } }; while let Ok(n) = fh.read(&mut buffer) { @@ -855,52 +887,182 @@ fn parse_file(path: &PathBuf) -> Result { } rest_size += n; } - println!("+{rest_size} unparsed bytes"); + eprintln!("+{rest_size} unparsed bytes"); Ok(ret) } -fn load_ini(path: &PathBuf) -> IndexMap>> { +fn load_ini(path: &PathBuf) -> IniData { 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) +#[derive(Serialize, Debug)] + +struct Level { + config: IniData, + moredummies: IniData, + emi: EMI, + sm3: SM3, + dummies: DUM, + path: PathBuf, + root: PathBuf, } -fn main() -> Result<()> { - let args = Args::try_parse()?; +impl Level { + fn load(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 config = load_ini(&config_file); + let moredummies = load_ini(&moredummies); + let Data::EMI(emi) = parse_file(&emi_path)? else { + bail!("Failed to parse EMI at {emi_path}", emi_path=emi_path.display()); + }; + let Data::SM3(sm3) = parse_file(&sm3_path)? else { + bail!("Failed to parse SM3 at {sm3_path}", sm3_path=sm3_path.display()); + }; + let Data::DUM(dummies) = parse_file(&dum_path)? else { + bail!("Failed to parse DUM at {dum_path}", dum_path=dum_path.display()); + }; + Ok(Level { + config, + moredummies, + emi, + sm3, + dummies, + path: path.into(), + root: root.into(), + }) + } +} + +#[derive(Subcommand, Debug)] +enum Commands { + FindScrapland, + ParsePacked { + scrap_path: PathBuf, + }, + ParseFile { + #[clap(long)] + /// Write to stdout + stdout: bool, + /// Scrapland root path + root: PathBuf, + /// Level to parse and convert + level: PathBuf, + }, +} + +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +#[command(propagate_version = true)] +struct Args { + #[arg(long,short)] + /// Write data as JSON + json: bool, + #[command(subcommand)] + command: Commands, +} + +fn cmd_parse_packed(root: &Path) -> Result>> { + let mut packed_map = HashMap::new(); + for entry in WalkDir::new(root).into_iter().filter_map(|e| e.ok()) { + let path = entry.path(); + if path + .extension() + .map(|e| e.to_str() == Some("packed")) + .unwrap_or(false) + { + let path = entry.path().to_owned(); + let header: PackedHeader = BufReader::new(File::open(&path)?).read_le()?; + packed_map.insert(path, header.files); + } + } + Ok(packed_map) +} + +fn to_bytes(data: &T, json: bool) -> Result> where T: Serialize { + if json { + Ok(serde_json::to_vec_pretty(data)?) + } else { + Ok(serde_pickle::to_vec(data,Default::default())?) + } +} + +fn cmd_parse_file(stdout: bool, root: &Path, path: &Path, json: bool) -> Result<()> { let out_path = PathBuf::from( - args.path + 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)? + ); + let out_path = if json { + out_path.with_extension("json.gz") } else { - serde_json::to_value(parse_file(full_path)?)? + out_path.with_extension("pkl.gz") + }; + let full_path = &root.join(path); + let data = if full_path.is_dir() { + let level = Level::load(root, path)?; + to_bytes(&level,json)? + } else { + let data = parse_file(full_path)?; + to_bytes(&data,json)? + }; + let mut data = Cursor::new(data); + if stdout { + let mut stdout = std::io::stdout().lock(); + std::io::copy(&mut data, &mut stdout)?; + } else { + let mut fh = GzEncoder::new(File::create(&out_path)?, Compression::best()); + std::io::copy(&mut data, &mut fh)?; + eprintln!("Wrote {path}", path = out_path.display()); }; - 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(()) +} + +fn emi_to_obj(emi: EMI) -> ! { + // let mut obj_data = obj::ObjData::default(); + + // for mesh in emi.tri { + // for vert in mesh.data.verts_1.inner.map(|d| d.data).unwrap_or_default() { + // obj_data.position.push(vert.xyz); + // obj_data.normal.push(vert.normal.unwrap_or_default()); + // obj_data.texture.push(vert.tex_1.unwrap_or_default().0.try_into().unwrap()); + // } + // for vert in mesh.data.verts_2.inner.map(|d| d.data).unwrap_or_default() { + // obj_data.position.push(vert.xyz); + // obj_data.normal.push(vert.normal.unwrap_or_default()); + // } + // } + todo!("EMI to OBJ converter"); +} + +fn main() -> Result<()> { + let args = Args::try_parse()?; + match args.command { + Commands::FindScrapland => { + let data = to_bytes(&find_scrap::get_executable()?,args.json)?; + let mut stdout = std::io::stdout().lock(); + std::io::copy(&mut &data[..], &mut stdout)?; + } + Commands::ParsePacked { scrap_path } => { + let data = to_bytes(&cmd_parse_packed(&scrap_path)?,args.json)?; + let mut stdout = std::io::stdout().lock(); + std::io::copy(&mut &data[..], &mut stdout)?; + } + Commands::ParseFile { + stdout, + root, + level, + } => { + cmd_parse_file(stdout, &root, &level, args.json)?; + } + } Ok(()) } diff --git a/tools/remaster/scrap_parse/src/pixel_shader.rs b/tools/remaster/scrap_parse/src/pixel_shader.rs index 7bc87a3..1430524 100644 --- a/tools/remaster/scrap_parse/src/pixel_shader.rs +++ b/tools/remaster/scrap_parse/src/pixel_shader.rs @@ -1,3 +1,7 @@ +// https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx9-graphics-reference-asm-ps-1-x +// +// ################################################ +// // #[derive(Debug)] // enum VecArg { // Tex(f32,f32,f32,f32), From 63962c95cce6c01f2e9bb2c3435398860e12e87f Mon Sep 17 00:00:00 2001 From: Daniel Seiller Date: Sun, 7 May 2023 21:36:20 +0200 Subject: [PATCH 4/4] add Rust ScrapHacks prototype and network sniffer/parser --- tools/remaster/scrap_net/.gitignore | 2 + tools/remaster/scrap_net/Cargo.lock | 1015 +++++++++++ tools/remaster/scrap_net/Cargo.toml | 28 + tools/remaster/scrap_net/get_app.py | 22 + tools/remaster/scrap_net/src/hex_ii.rs | 93 + tools/remaster/scrap_net/src/main.rs | 640 +++++++ tools/remaster/scrap_net/src/parser.rs | 151 ++ .../remaster/scraphacks_rs/.cargo/config.toml | 2 + tools/remaster/scraphacks_rs/Cargo.lock | 1624 +++++++++++++++++ tools/remaster/scraphacks_rs/Cargo.toml | 45 + tools/remaster/scraphacks_rs/Pipfile | 11 + tools/remaster/scraphacks_rs/Save0.sav.json | 242 +++ tools/remaster/scraphacks_rs/build.rs | 3 + tools/remaster/scraphacks_rs/build.sh | 4 + tools/remaster/scraphacks_rs/notes.md | 16 + tools/remaster/scraphacks_rs/run.py | 34 + tools/remaster/scraphacks_rs/scrap.hpp | 16 + tools/remaster/scraphacks_rs/src/config.rs | 7 + tools/remaster/scraphacks_rs/src/discord.rs | 94 + tools/remaster/scraphacks_rs/src/lib.rs | 96 + tools/remaster/scraphacks_rs/src/lua.rs | 204 +++ tools/remaster/scraphacks_rs/src/mem.rs | 94 + tools/remaster/scraphacks_rs/src/parser.rs | 177 ++ tools/remaster/scraphacks_rs/src/scrap.rs | 381 ++++ .../scraphacks_rs/target/.rustc_info.json | 1 + .../scraphacks_rs/target/CACHEDIR.TAG | 3 + .../target/i686-pc-windows-msvc/CACHEDIR.TAG | 3 + 27 files changed, 5008 insertions(+) create mode 100644 tools/remaster/scrap_net/.gitignore create mode 100644 tools/remaster/scrap_net/Cargo.lock create mode 100644 tools/remaster/scrap_net/Cargo.toml create mode 100644 tools/remaster/scrap_net/get_app.py create mode 100644 tools/remaster/scrap_net/src/hex_ii.rs create mode 100644 tools/remaster/scrap_net/src/main.rs create mode 100644 tools/remaster/scrap_net/src/parser.rs create mode 100644 tools/remaster/scraphacks_rs/.cargo/config.toml create mode 100644 tools/remaster/scraphacks_rs/Cargo.lock create mode 100644 tools/remaster/scraphacks_rs/Cargo.toml create mode 100644 tools/remaster/scraphacks_rs/Pipfile create mode 100644 tools/remaster/scraphacks_rs/Save0.sav.json create mode 100644 tools/remaster/scraphacks_rs/build.rs create mode 100644 tools/remaster/scraphacks_rs/build.sh create mode 100644 tools/remaster/scraphacks_rs/notes.md create mode 100644 tools/remaster/scraphacks_rs/run.py create mode 100644 tools/remaster/scraphacks_rs/scrap.hpp create mode 100644 tools/remaster/scraphacks_rs/src/config.rs create mode 100644 tools/remaster/scraphacks_rs/src/discord.rs create mode 100644 tools/remaster/scraphacks_rs/src/lib.rs create mode 100644 tools/remaster/scraphacks_rs/src/lua.rs create mode 100644 tools/remaster/scraphacks_rs/src/mem.rs create mode 100644 tools/remaster/scraphacks_rs/src/parser.rs create mode 100644 tools/remaster/scraphacks_rs/src/scrap.rs create mode 100644 tools/remaster/scraphacks_rs/target/.rustc_info.json create mode 100644 tools/remaster/scraphacks_rs/target/CACHEDIR.TAG create mode 100644 tools/remaster/scraphacks_rs/target/i686-pc-windows-msvc/CACHEDIR.TAG diff --git a/tools/remaster/scrap_net/.gitignore b/tools/remaster/scrap_net/.gitignore new file mode 100644 index 0000000..9623fa0 --- /dev/null +++ b/tools/remaster/scrap_net/.gitignore @@ -0,0 +1,2 @@ +/target +/.history \ No newline at end of file diff --git a/tools/remaster/scrap_net/Cargo.lock b/tools/remaster/scrap_net/Cargo.lock new file mode 100644 index 0000000..11f08a4 --- /dev/null +++ b/tools/remaster/scrap_net/Cargo.lock @@ -0,0 +1,1015 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" + +[[package]] +name = "array-init" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb6d71005dc22a708c7496eee5c8dc0300ee47355de6256c3b35b12b5fef596" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[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 = "block-padding" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a90ec2df9600c28a01c56c4784c9207a96d2451833aeceb8cc97e4c9548bb78" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bytemuck" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f5715e491b5a1598fc2bef5a606847b5dc1d48ea625bd3c02c00de8285591da" + +[[package]] +name = "bytes" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chacha20" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fc89c7c5b9e7a02dfe45cd2367bae382f9ed31c61ca8debe5f827c420a2f08" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "cipher" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1873270f8f7942c191139cb8a40fd228da6c3fd2fc376d7e92d47aa14aeb59e" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "clap" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c9484ccdc4cb8e7b117cbd0eb150c7c0f04464854e4679aeb50ef03b32d003" +dependencies = [ + "atty", + "bitflags", + "clap_derive", + "clap_lex", + "once_cell", + "strsim", + "termcolor", +] + +[[package]] +name = "clap_derive" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca689d7434ce44517a12a89456b2be4d1ea1cafcd8f581978c03d45f5a5c12a7" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "console" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c050367d967ced717c04b65d8c619d863ef9292ce0c5760028655a2fb298718c" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "terminal_size", + "unicode-width", + "winapi", +] + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "crossterm" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" +dependencies = [ + "bitflags", + "crossterm_winapi", + "futures-core", + "libc", + "mio", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c" +dependencies = [ + "winapi", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "dialoguer" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92e7e37ecef6857fdc0c0c5d42fd5b0938e46590c2183cc92dd310a6d078eb1" +dependencies = [ + "console", + "tempfile", + "zeroize", +] + +[[package]] +name = "either" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "fastrand" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +dependencies = [ + "instant", +] + +[[package]] +name = "futures" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" + +[[package]] +name = "futures-executor" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" + +[[package]] +name = "futures-macro" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" + +[[package]] +name = "futures-task" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" + +[[package]] +name = "futures-util" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.134" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mio" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + +[[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_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "os_str_bytes" +version = "6.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" + +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "pin-project" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[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.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "rhexdump" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e9af64574935e39f24d1c0313a997c8b880ca0e087c888bc6af8af31579847" + +[[package]] +name = "rustyline-async" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00e1a02d2c727fac4c73a019de82183d990af0e13d6bed2e3a6f37a525ff591d" +dependencies = [ + "crossterm", + "futures", + "pin-project", + "thingbuf", + "thiserror", + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "scrap_net" +version = "0.1.0" +dependencies = [ + "anyhow", + "binrw", + "chacha20", + "clap", + "dialoguer", + "futures-util", + "hex", + "itertools", + "lazy_static", + "modular-bitfield", + "poly1305", + "rand", + "rhexdump", + "rustyline-async", + "tokio", +] + +[[package]] +name = "signal-hook" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" + +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi", +] + +[[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 = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "terminal_size" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "thingbuf" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee2d290fe0db0225026350bc1f0bd02b2daad43b09b56780e29e549effcd54a" +dependencies = [ + "parking_lot", + "pin-project", +] + +[[package]] +name = "thiserror" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio" +version = "1.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "unicode-ident" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" + +[[package]] +name = "unicode-segmentation" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "universal-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5" +dependencies = [ + "crypto-common", + "subtle", +] + +[[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.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[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.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "zeroize" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" diff --git a/tools/remaster/scrap_net/Cargo.toml b/tools/remaster/scrap_net/Cargo.toml new file mode 100644 index 0000000..0c17ba6 --- /dev/null +++ b/tools/remaster/scrap_net/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "scrap_net" +version = "0.1.0" +edition = "2021" +authors = ["Daniel Seiller "] +description = "Scrapland Remastered network sniffer, proxy (and soon hopefully parser)" +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +chacha20 = { version = "0.9", features = ["std"] } +poly1305 = { version = "0.8", features = ["std"] } +rhexdump = "0.1" +tokio = { version = "1.21", features = ["full"] } +clap = {version = "4.0", features = ["derive"]} +rand = "0.8" +dialoguer = "0.10" +binrw = "0.11" +modular-bitfield = "0.11" +hex = "0.4" +lazy_static = "1.4.0" +rustyline-async = "0.3" +futures-util = "0.3.24" +itertools = "0.10.5" +anyhow = "1.0.68" + +[profile.release] +lto="fat" +opt-level = 3 diff --git a/tools/remaster/scrap_net/get_app.py b/tools/remaster/scrap_net/get_app.py new file mode 100644 index 0000000..2c0cada --- /dev/null +++ b/tools/remaster/scrap_net/get_app.py @@ -0,0 +1,22 @@ +from distutils.command.install_data import install_data +import winreg as reg +import vdf +from pathlib import Path +import pefile +app_id="897610" +try: + key = reg.OpenKey(reg.HKEY_LOCAL_MACHINE,"SOFTWARE\\Valve\\Steam") +except FileNotFoundError: + key = reg.OpenKey(reg.HKEY_LOCAL_MACHINE,"SOFTWARE\\Wow6432Node\\Valve\\Steam") +path=Path(reg.QueryValueEx(key,"InstallPath")[0]) +libraryfolders=vdf.load((path/"steamapps"/"libraryfolders.vdf").open("r"))['libraryfolders'] +for folder in libraryfolders.values(): + path=Path(folder['path']) + if app_id in folder['apps']: + install_dir = vdf.load((path/"steamapps"/f"appmanifest_{app_id}.acf").open("r"))['AppState']['installdir'] + install_dir=path/"steamapps"/"common"/install_dir + for file in install_dir.glob("**/*.exe"): + pe = pefile.PE(file, fast_load=True) + entry = pe.OPTIONAL_HEADER.AddressOfEntryPoint + if pe.get_dword_at_rva(entry) == 0xE8: + print(file) \ No newline at end of file diff --git a/tools/remaster/scrap_net/src/hex_ii.rs b/tools/remaster/scrap_net/src/hex_ii.rs new file mode 100644 index 0000000..70ddc47 --- /dev/null +++ b/tools/remaster/scrap_net/src/hex_ii.rs @@ -0,0 +1,93 @@ +use itertools::Itertools; +use std::fmt::Display; +use std::ops::{Deref, DerefMut}; + +#[derive(Debug, PartialEq, Eq)] +enum HexII { + Ascii(char), + Byte(u8), + Null, + Full, + Eof, +} + +impl From<&u8> for HexII { + fn from(v: &u8) -> Self { + match v { + 0x00 => Self::Null, + 0xFF => Self::Full, + c if c.is_ascii_graphic() => Self::Ascii(*c as char), + v => Self::Byte(*v), + } + } +} + +impl Display for HexII { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + HexII::Ascii(v) => write!(f, ".{}", v)?, + HexII::Byte(v) => write!(f, "{:02x}", v)?, + HexII::Null => write!(f, " ")?, + HexII::Full => write!(f, "##")?, + HexII::Eof => write!(f, " ]")?, + } + Ok(()) + } +} + +struct HexIILine(Vec); + +impl Display for HexIILine { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for (i, v) in self.0.iter().enumerate() { + if i != 0 { + write!(f, " ")?; + } + write!(f, "{}", v)?; + } + Ok(()) + } +} + +impl From<&[u8]> for HexIILine { + fn from(l: &[u8]) -> Self { + Self(l.iter().map(HexII::from).collect()) + } +} + +impl Deref for HexIILine { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for HexIILine { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +pub fn hex_ii_dump>(data: T, base_offset: usize, total: usize) { + const CHUNK_SIZE: usize = 0x10; + let mut num_digits = (std::mem::size_of_val(&total) * 8) - (total.leading_zeros() as usize); + if (num_digits % 8) != 0 { + num_digits += 8 - (num_digits % 8) + } + num_digits >>= 2; + for (mut offset, line) in data.chunks(CHUNK_SIZE).into_iter().enumerate() { + offset += base_offset; + let mut line = HexIILine::from(line.collect::>().as_slice()); + if line.len() < CHUNK_SIZE { + line.push(HexII::Eof); + } + while line.len() < CHUNK_SIZE { + line.push(HexII::Null); + } + if line.iter().all(|v| v == &HexII::Null) { + continue; + } + let offset = format!("{:digits$x}", offset * CHUNK_SIZE, digits = num_digits); + println!("{} | {:<16} |", offset, line); + } +} diff --git a/tools/remaster/scrap_net/src/main.rs b/tools/remaster/scrap_net/src/main.rs new file mode 100644 index 0000000..0b8cbd0 --- /dev/null +++ b/tools/remaster/scrap_net/src/main.rs @@ -0,0 +1,640 @@ +use anyhow::{bail, ensure, Result}; +use binrw::BinReaderExt; +use binrw::{BinRead, NullString}; +use chacha20::cipher::KeyInit; +use chacha20::cipher::{KeyIvInit, StreamCipher, StreamCipherSeek}; +use chacha20::ChaCha20; +use clap::Parser; +use dialoguer::theme::ColorfulTheme; +use dialoguer::Select; +use futures_util::FutureExt; +use poly1305::Poly1305; +use rand::{thread_rng, Rng}; +use rhexdump::hexdump; +use rustyline_async::{Readline, ReadlineError, SharedWriter}; +use std::collections::BTreeMap; +use std::error::Error; +use std::fmt::Display; +use std::io::Cursor; +use std::io::Write; +use std::iter; +use std::net::SocketAddr; +use std::net::ToSocketAddrs; +use std::net::{IpAddr, Ipv4Addr}; +use std::path::PathBuf; +use std::time::{Duration, Instant}; +use tokio::io::AsyncBufReadExt; +use tokio::net::UdpSocket; +use tokio::time; + +mod hex_ii; +mod parser; + +const KEY: &[u8; 32] = b"\x02\x04\x06\x08\x0a\x0c\x0e\x10\x12\x14\x16\x18\x1a\x1c\x1e\x20\x22\x24\x26\x28\x2a\x2c\x2e\x30\x32\x34\x36\x38\x3a\x3c\x3e\x40"; +const INFO_PACKET: &[u8] = b"\x7f\x01\x00\x00\x07"; + +#[derive(Debug, Clone)] +struct ServerFlags { + dedicated: bool, + force_vehicle: bool, + _rest: u8, +} + +impl Display for ServerFlags { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let force_vehicle = if self.force_vehicle { "F" } else { " " }; + let dedicated = if self.dedicated { "D" } else { " " }; + write!(f, "{}{}", force_vehicle, dedicated)?; + Ok(()) + } +} + +impl From for ServerFlags { + fn from(v: u8) -> Self { + ServerFlags { + dedicated: v & 0b1 != 0, + force_vehicle: v & 0b10 != 0, + _rest: (v & 0b11111100) >> 2, + } + } +} + +#[derive(Debug, Clone, BinRead)] +#[br(little, magic = b"\xba\xce", import(rtt: Duration, addr: SocketAddr))] +pub struct Server { + #[br(calc=addr)] + addr: SocketAddr, + #[br(calc=rtt)] + rtt: Duration, + #[br(map = |v: (u8,u8)| format!("{}.{}",v.0,v.1))] + version: String, + port: u16, + max_players: u16, + cur_players: u16, + #[br(map = u8::into)] + flags: ServerFlags, + #[br(pad_size_to(0x20), map = |s :NullString| s.to_string())] + name: String, + #[br(pad_size_to(0x10), map = |s :NullString| s.to_string())] + mode: String, + #[br(pad_size_to(0x20), map = |s :NullString| s.to_string())] + map: String, + _pad: u8, +} + +fn pad_copy(d: &[u8], l: usize) -> Vec { + let diff = d.len() % l; + if diff != 0 { + d.iter() + .copied() + .chain(iter::repeat(0).take(l - diff)) + .collect() + } else { + d.to_vec() + } +} + +fn pad(d: &mut Vec, l: usize) { + let diff = d.len() % l; + if diff != 0 { + d.extend(iter::repeat(0).take(l - diff)) + } +} + +struct Packet { + nonce: Vec, + data: Vec, +} + +impl Packet { + fn encrypt(data: &[u8]) -> Packet { + let mut data: Vec = data.to_vec(); + let mut rng = thread_rng(); + let mut nonce = vec![0u8; 12]; + rng.fill(nonce.as_mut_slice()); + let mut cipher = ChaCha20::new(KEY.into(), nonce.as_slice().into()); + cipher.seek(KEY.len() + 32); + cipher.apply_keystream(&mut data); + Packet { nonce, data } + } + + fn get_tag(&self) -> Vec { + let mut sign_data = vec![]; + sign_data.extend(pad_copy(&self.nonce, 16).iter()); + sign_data.extend(pad_copy(&self.data, 16).iter()); + sign_data.extend((self.nonce.len() as u64).to_le_bytes().iter()); + sign_data.extend((self.data.len() as u64).to_le_bytes().iter()); + let mut cipher = ChaCha20::new(KEY.into(), self.nonce.as_slice().into()); + let mut poly_key = *KEY; + cipher.apply_keystream(&mut poly_key); + let signer = Poly1305::new(&poly_key.into()); + signer.compute_unpadded(&sign_data).into_iter().collect() + } + + fn bytes(&self) -> Vec { + let mut data = vec![]; + data.extend(pad_copy(&self.nonce, 16).iter()); + data.extend(pad_copy(&self.data, 16).iter()); + data.extend((self.nonce.len() as u64).to_le_bytes().iter()); + data.extend((self.data.len() as u64).to_le_bytes().iter()); + data.extend(self.get_tag().iter()); + data + } + + fn decrypt(&self) -> Result> { + let mut data = self.data.clone(); + let mut sign_data = data.clone(); + pad(&mut sign_data, 16); + let mut nonce = self.nonce.clone(); + pad(&mut nonce, 16); + let sign_data = nonce + .iter() + .chain(sign_data.iter()) + .chain((self.nonce.len() as u64).to_le_bytes().iter()) + .chain((self.data.len() as u64).to_le_bytes().iter()) + .copied() + .collect::>(); + let mut poly_key = *KEY; + let mut cipher = ChaCha20::new(KEY.into(), self.nonce.as_slice().into()); + cipher.apply_keystream(&mut poly_key); + let signer = Poly1305::new(&poly_key.into()); + let signature: Vec = signer.compute_unpadded(&sign_data).into_iter().collect(); + + if signature != self.get_tag() { + bail!("Invalid signature!"); + }; + cipher.seek(poly_key.len() + 32); + cipher.apply_keystream(&mut data); + Ok(data) + } +} + +impl TryFrom<&[u8]> for Packet { + type Error = anyhow::Error; + fn try_from(data: &[u8]) -> Result { + let (mut nonce, data) = data.split_at(16); + let (mut data, tag) = data.split_at(data.len() - 16); + let nonce_len = u64::from_le_bytes(data[data.len() - 16..][..8].try_into()?) as usize; + let data_len = u64::from_le_bytes(data[data.len() - 8..].try_into()?) as usize; + data = &data[..data_len]; + nonce = &nonce[..nonce_len]; + let pkt = Packet { + nonce: nonce.into(), + data: data.into(), + }; + if pkt.get_tag() != tag { + bail!("Invalid signature!"); + } + Ok(pkt) + } +} + +#[derive(Debug, Clone)] +pub enum ServerEntry { + Alive(Server), + Dead { addr: SocketAddr, reason: String }, +} + +impl Display for ServerEntry { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ServerEntry::Alive(srv) => write!( + f, + "[{}] {} ({} {}/{} Players on {}) version {} [{}] RTT: {:?}", + srv.addr, + srv.name, + srv.mode, + srv.cur_players, + srv.max_players, + srv.map, + srv.version, + srv.flags, + srv.rtt + ), + ServerEntry::Dead { addr, reason } => write!(f, "[{}] (error: {})", addr, reason), + } + } +} + +fn encrypt(data: &[u8]) -> Vec { + Packet::encrypt(data).bytes() +} + +fn decrypt(data: &[u8]) -> Result> { + Packet::try_from(data)?.decrypt() +} + +async fn recv_from_timeout( + sock: &UdpSocket, + buf: &mut [u8], + timeout: f64, +) -> Result<(usize, SocketAddr)> { + Ok(time::timeout(Duration::from_secs_f64(timeout), sock.recv_from(buf)).await??) +} + +async fn query_server<'a>(addr: SocketAddr) -> Result { + let mut buf = [0; 32 * 1024]; + let socket = UdpSocket::bind("0.0.0.0:0").await?; + socket.connect(addr).await?; + let msg = encrypt(INFO_PACKET); + let t_start = Instant::now(); + socket.send(&msg).await?; + let size = recv_from_timeout(&socket, &mut buf, 5.0).await?.0; + let rtt = t_start.elapsed(); + let data = decrypt(&buf[..size])?; + if !data.starts_with(&[0xba, 0xce]) { + // Server Info + bail!("Invalid response"); + } + let mut cur = Cursor::new(&data); + let info: Server = cur.read_le_args((rtt, addr))?; + if info.port != addr.port() { + eprint!("[WARN] Port differs for {}: {}", addr, info.port); + } + if cur.position() != (data.len() as u64) { + bail!("Leftover data"); + } + Ok(info) +} + +async fn get_servers(master_addr: &str) -> Result<(Duration, Vec)> { + let master_addr: SocketAddr = master_addr.to_socket_addrs()?.next().unwrap(); + let mut rtt = std::time::Duration::from_secs_f32(0.0); + let mut servers = vec![]; + let mut buf = [0; 32 * 1024]; + let master = UdpSocket::bind("0.0.0.0:0").await?; + master.connect(master_addr).await?; + for n in 0..(256 / 32) { + let data = format!("Brw={},{}\0", n * 32, (n + 1) * 32); + let data = &encrypt(data.as_bytes()); + let t_start = Instant::now(); + master.send(data).await?; + let size = master.recv(&mut buf).await?; + rtt += t_start.elapsed(); + let data = decrypt(&buf[..size])?; + if data.starts_with(b"\0\0\0\0}") { + for chunk in data[5..].chunks(6) { + if chunk.iter().all(|v| *v == 0) { + break; + } + let port = u16::from_le_bytes(chunk[chunk.len() - 2..].try_into()?); + let addr = SocketAddr::from(([chunk[0], chunk[1], chunk[2], chunk[3]], port)); + let server = match query_server(addr).await { + Ok(server) => ServerEntry::Alive(server), + Err(err) => ServerEntry::Dead { + addr, + reason: err.to_string(), + }, + }; + servers.push(server); + } + } + } + rtt = Duration::from_secs_f64(rtt.as_secs_f64() / ((256 / 32) as f64)); + Ok((rtt, servers)) +} + +fn indent_hexdump(data: &[u8], indentation: usize, label: &str) -> String { + let mut out = String::new(); + let indent = " ".repeat(indentation); + out.push_str(&indent); + out.push_str(label); + out.push('\n'); + for line in rhexdump::hexdump(data).lines() { + out.push_str(&indent); + out.push_str(line); + out.push('\n'); + } + out.trim_end().to_owned() +} + +#[derive(Default, Debug)] +struct State { + client: BTreeMap>, + server: BTreeMap>, +} + +impl State { + fn update_client(&mut self, data: &[u8]) { + data.iter().enumerate().for_each(|(pos, b)| { + *self.client.entry(pos).or_default().entry(*b).or_default() += 1; + }); + } + fn update_server(&mut self, data: &[u8]) { + data.iter().enumerate().for_each(|(pos, b)| { + *self.server.entry(pos).or_default().entry(*b).or_default() += 1; + }); + } +} + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +enum Direction { + Client, + Server, + Both, +} + +#[derive(Debug)] +enum CmdResult { + Exit, + Packet { + data: Vec, + direction: Direction, + }, + Fuzz { + direction: Direction, + start: usize, + end: usize, + chance: (u32, u32), + }, + NoFuzz, + Log(bool), +} + +async fn handle_line( + line: &str, + state: &State, + stdout: &mut SharedWriter, +) -> Result> { + use CmdResult::*; + let cmd: Vec<&str> = line.trim().split_ascii_whitespace().collect(); + match cmd[..] { + ["log", "off"] => Ok(Some(Log(false))), + ["log", "on"] => Ok(Some(Log(true))), + ["state", pos] => { + let pos = pos.parse()?; + writeln!(stdout, "Client: {:?}", state.client.get(&pos))?; + writeln!(stdout, "Server: {:?}", state.server.get(&pos))?; + Ok(None) + } + [dir @ ("client" | "server"), ref args @ ..] => { + let mut data: Vec = vec![]; + for args in args.iter() { + let args = hex::decode(args)?; + data.extend(args); + } + Ok(Some(CmdResult::Packet { + data, + direction: match dir { + "client" => Direction::Client, + "server" => Direction::Server, + _ => unreachable!(), + }, + })) + } + ["fuzz", dir @ ("client" | "server" | "both"), start, end, chance_num, chance_den] => { + let direction = match dir { + "client" => Direction::Client, + "server" => Direction::Server, + "both" => Direction::Both, + _ => unreachable!(), + }; + let start = start.parse()?; + let end = end.parse()?; + if start > end { + bail!("Fuzz start>end"); + } + let res = CmdResult::Fuzz { + direction, + start, + end, + chance: (chance_num.parse()?, chance_den.parse()?), + }; + Ok(Some(res)) + } + ["fuzz", "off"] => Ok(Some(CmdResult::NoFuzz)), + ["exit"] => Ok(Some(CmdResult::Exit)), + [""] => Ok(None), + _ => bail!("Unknown command: {:?}", line), + } +} + +async fn run_proxy( + remote_addr: &SocketAddr, + local_addr: &SocketAddr, + logfile: &Option, +) -> Result<()> { + let mut print_log = false; + let mut state = State::default(); + let mut logfile = match logfile { + Some(path) => Some(std::fs::File::create(path)?), + None => None, + }; + let mut fuzz = None; + let mut rng = thread_rng(); + let mut client_addr: Option = None; + let local = UdpSocket::bind(local_addr).await?; + let remote = UdpSocket::bind("0.0.0.0:0").await?; + remote.connect(remote_addr).await?; + let mut local_buf = vec![0; 32 * 1024]; + let mut remote_buf = vec![0; 32 * 1024]; + println!("Proxy listening on {}", local_addr); + let (mut rl, mut stdout) = Readline::new(format!("{}> ", remote_addr)).unwrap(); + loop { + tokio::select! { + line = rl.readline().fuse() => { + match line { + Ok(line) => { + let line=line.trim(); + rl.add_history_entry(line.to_owned()); + match handle_line(line, &state, &mut stdout).await { + Ok(Some(result)) => { + match result { + CmdResult::Packet{data,direction} => { + let data=encrypt(&data); + match direction { + Direction::Client => { + if client_addr.is_some() { + local + .send_to(&data, client_addr.unwrap()) + .await?; + } else { + writeln!(stdout,"Error: No client address")?; + } + }, + Direction::Server => { + remote.send(&data).await?; + } + Direction::Both => unreachable!() + }; + } + CmdResult::Log(log) => { + print_log=log; + } + CmdResult::Exit => break Ok(()), + CmdResult::NoFuzz => { + fuzz=None; + } + CmdResult::Fuzz { .. } => { + fuzz=Some(result) + }, + } + }, + Ok(None) => (), + Err(msg) => { + writeln!(stdout, "Error: {}", msg)?; + } + } + }, + Err(ReadlineError::Eof) =>{ writeln!(stdout, "Exiting...")?; break Ok(()) }, + Err(ReadlineError::Interrupted) => { + writeln!(stdout, "^C")?; + break Ok(()); + }, + Err(err) => { + writeln!(stdout, "Received err: {:?}", err)?; + writeln!(stdout, "Exiting...")?; + break Ok(()); + } + } + } + local_res = local.recv_from(&mut local_buf) => { + let (size, addr) = local_res?; + client_addr.get_or_insert(addr); + let mut data = Packet::try_from(&local_buf[..size])?.decrypt()?; + state.update_client(&data); + if print_log { + writeln!(stdout,"{}", indent_hexdump(&data, 0, &format!("OUT: {}", addr)))?; + } + if let Some(lf) = logfile.as_mut() { + writeln!(lf, ">{:?} {} {}", addr, data.len(), hex::encode(&data))?; + }; + if let Some(CmdResult::Fuzz{direction,start,end,chance}) = fuzz { + if (direction==Direction::Server || direction==Direction::Both) && rng.gen_ratio(chance.0,chance.1) { + rng.fill(&mut data[start..end]); + } + } + remote.send(&encrypt(&data)).await?; + } + remote_res = remote.recv_from(&mut remote_buf) => { + let (size, addr) = remote_res?; + let mut data = Packet::try_from(&remote_buf[..size])?.decrypt()?; + state.update_server(&data); + if print_log { + writeln!(stdout,"\r{}", indent_hexdump(&data, 5, &format!("IN: {}", addr)))?; + } + if let Some(lf) = logfile.as_mut() { + writeln!(lf, "<{:?} {} {}", addr, data.len(), hex::encode(&data))?; + }; + if client_addr.is_some() { + if let Some(CmdResult::Fuzz{direction,start,end,chance}) = &fuzz { + if (*direction==Direction::Client || *direction==Direction::Both) && rng.gen_ratio(chance.0,chance.1) { + rng.fill(&mut data[*start..*end]); + } + } + local + .send_to(&encrypt(&data), client_addr.unwrap()) + .await?; + } + } + } + } +} + +async fn send_master_cmd(sock: &UdpSocket, cmd: &str) -> Result> { + let mut buf = [0; 32 * 1024]; + let mut data: Vec = cmd.as_bytes().to_vec(); + data.push(0); + let data = &encrypt(&data); + sock.send(data).await?; + let size = recv_from_timeout(sock, &mut buf, 5.0).await?.0; + decrypt(&buf[..size]) +} + +async fn run_master_shell(master_addr: &str) -> Result<()> { + let master = UdpSocket::bind("0.0.0.0:0").await?; + master.connect(master_addr).await?; + let (mut rl, mut stdout) = Readline::new(format!("{}> ", master_addr)).unwrap(); + loop { + tokio::select! { + line = rl.readline().fuse() => { + match line { + Ok(line) => { + let line=line.trim(); + rl.add_history_entry(line.to_owned()); + writeln!(stdout,"[CMD] {line}")?; + match send_master_cmd(&master,line).await { + Ok(data) => writeln!(stdout,"{}",hexdump(&data))?, + Err(e) => writeln!(stdout,"Error: {e}")? + } + } + Err(ReadlineError::Eof) =>{ writeln!(stdout, "Exiting...")?; break Ok(()) }, + Err(ReadlineError::Interrupted) => { + writeln!(stdout, "^C")?; + break Ok(()); + }, + Err(err) => { + writeln!(stdout, "Receive error: {err}")?; + break Err(err.into()); + } + } + } + } + } +} + +#[derive(Parser, Debug)] +#[clap(author, version, about, long_about = None)] +struct Args { + /// Server to connect to (if unspecified will query the master server) + server: Option, + /// Only list servers without starting proxy + #[clap(short, long, action)] + list: bool, + /// Local Address to bind to + #[clap(short,long, default_value_t = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 28086))] + addr: SocketAddr, + /// Master server to query for running games + #[clap(short, long, default_value = "scrapland.mercurysteam.com:5000")] + master: String, + /// Path of file to log decrypted packets to + #[clap(short = 'f', long)] + logfile: Option, +} + +#[tokio::main] +async fn main() -> Result<()> { + let args = Args::parse(); + if args.list && args.server.is_some() { + let addr = args.server.unwrap(); + let server = match query_server(addr).await { + Ok(server) => ServerEntry::Alive(server), + Err(msg) => ServerEntry::Dead { + addr, + reason: msg.to_string(), + }, + }; + println!("{}", server); + return Ok(()); + } + if let Some(server) = args.server { + run_proxy(&server, &args.addr, &args.logfile).await?; + return Ok(()); + } + loop { + let (rtt, servers) = get_servers(&args.master).await?; + println!("Master RTT: {:?}", rtt); + if args.list { + for server in servers { + println!("{}", server); + } + return Ok(()); + } + let selection = Select::with_theme(&ColorfulTheme::default()) + .items(&servers) + .with_prompt("Select server (press Esc to drop into master server command shell)") + .interact_opt()? + .map(|v| &servers[v]); + match selection { + Some(ServerEntry::Dead { addr, reason }) => { + eprintln!("{:?} returned an error: {}", addr, reason) + } + Some(ServerEntry::Alive(srv)) => { + return run_proxy(&srv.addr, &args.addr, &args.logfile).await; + } + None => { + return run_master_shell(&args.master).await; + } + } + } +} diff --git a/tools/remaster/scrap_net/src/parser.rs b/tools/remaster/scrap_net/src/parser.rs new file mode 100644 index 0000000..e8cbcd4 --- /dev/null +++ b/tools/remaster/scrap_net/src/parser.rs @@ -0,0 +1,151 @@ +use std::collections::HashMap; +use std::error::Error; + +use crate::hex_ii::hex_ii_dump; +use crate::ServerFlags; +use binrw::BinReaderExt; +use binrw::{binread, BinRead, NullString}; + +/* +00000000: 7f 4c 00 00 06 ba ce 01 01 06 63 61 63 6f 74 61 | .L........cacota +00000010: 10 5b 42 4a 5a 5d 20 45 61 72 74 68 6e 75 6b 65 | .[BJZ].Earthnuke +00000020: 72 06 53 50 6f 6c 69 31 37 00 08 50 5f 50 6f 6c | r.SPoli17..P_Pol +00000030: 69 63 65 06 4d 50 4f 4c 49 31 00 00 00 0d 30 2c | ice.MPOLI1....0, +00000040: 30 2c 30 2c 31 2c 30 2c 30 2c 31 00 00 00 00 | 0,0,1,0,0,1.... + +00000000: 7f 49 00 00 06 ba ce 01 01 06 63 61 63 6f 74 61 | .I........cacota +00000010: 0e 55 6e 6e 61 6d 65 64 20 50 6c 61 79 65 72 07 | .Unnamed.Player. +00000020: 53 42 65 74 74 79 31 50 00 07 50 5f 42 65 74 74 | SBetty1P..P_Bett +00000030: 79 07 4d 42 65 74 74 79 31 00 00 00 0b 31 2c 31 | y.MBetty1....1,1 +00000040: 2c 30 2c 31 2c 33 2c 30 00 00 00 00 | ,0,1,3,0.... +*/ + +#[derive(Debug, Clone, BinRead)] +#[br(big)] +#[br(magic = b"\xba\xce")] +struct ServerInfoJoin { + #[br(map = |v: (u8,u8)| format!("{}.{}",v.0,v.1))] + version: String, +} + +struct Data { + player_id: u32, + num_vals: u32, + pos: [f32; 3], + player_index: u32, + rtt: u32, +} + +#[binread] +#[br(big)] +#[derive(Debug, Clone)] +enum PacketData { + #[br(magic = b"\x7f")] + PlayerJoin { + data_len: u8, + _1: u8, + cur_players: u8, + max_players: u8, + info: ServerInfoJoin, + #[br(temp)] + pw_len: u8, + #[br(count = pw_len, map = |bytes: Vec| String::from_utf8_lossy(&bytes).into_owned())] + password: String, + #[br(temp)] + player_name_len: u8, + #[br(count = player_name_len, map = |bytes: Vec| String::from_utf8_lossy(&bytes).into_owned())] + player_name: String, + #[br(temp)] + ship_model_len: u8, + #[br(count = ship_model_len, map = |bytes: Vec| String::from_utf8_lossy(&bytes).into_owned())] + ship_model: String, + #[br(little)] + max_health: u16, + #[br(temp)] + pilot_model_len: u8, + #[br(count = pilot_model_len, map = |bytes: Vec| String::from_utf8_lossy(&bytes).into_owned())] + pilot_model: String, + #[br(temp)] + engine_model_r_len: u8, + #[br(count = engine_model_r_len, map = |bytes: Vec| String::from_utf8_lossy(&bytes).into_owned())] + engine_model_r: String, + #[br(temp)] + engine_model_l_len: u8, + #[br(count = engine_model_r_len, map = |bytes: Vec| String::from_utf8_lossy(&bytes).into_owned())] + engine_model_l: String, + _2: u16, + #[br(temp)] + loadout_len: u8, + #[br(count = loadout_len, map = |bytes: Vec| String::from_utf8_lossy(&bytes).into_owned())] + loadout: String, + team_number: u16, + padding: [u8; 2], + }, + #[br(magic = b"\x80\x15")] + MapInfo { + #[br(temp)] + map_len: u32, + #[br(count = map_len, map = |bytes: Vec| String::from_utf8_lossy(&bytes).into_owned())] + map: String, + #[br(temp)] + mode_len: u8, + #[br(count = mode_len, map = |bytes: Vec| String::from_utf8_lossy(&bytes).into_owned())] + mode: String, + _2: u16, + item_count: u8, + // _3: u32, + // #[br(count = item_count)] + // items: Vec<[u8;0x11]> + }, + #[br(magic = b"\xba\xce")] + ServerInfo { + #[br(map = |v: (u8,u8)| format!("{}.{}",v.1,v.0))] + version: String, + port: u16, + max_players: u16, + cur_players: u16, + #[br(map = u8::into)] + flags: ServerFlags, + #[br(pad_size_to(0x20), map=|s: NullString| s.to_string())] + name: String, + #[br(pad_size_to(0x10), map=|s: NullString| s.to_string())] + mode: String, + #[br(pad_size_to(0x20), map=|s: NullString| s.to_string())] + map: String, + _pad: u8, + }, +} + +fn parse(data: &[u8]) -> Result<(PacketData, Vec), Box> { + use std::io::Cursor; + let mut rdr = Cursor::new(data); + let pkt: PacketData = rdr.read_le()?; + let rest = data[rdr.position() as usize..].to_vec(); + println!("{}", rhexdump::hexdump(data)); + Ok((pkt, rest)) +} + +#[test] +fn test_parser() { + let log = include_str!("../test_.log").lines(); + let mut hm = HashMap::new(); + for line in log { + let data = line.split_ascii_whitespace().nth(1).unwrap(); + let data = hex::decode(data).unwrap(); + *hm.entry(data[0..1].to_vec()).or_insert(0usize) += 1; + match parse(&data) { + Ok((pkt, rest)) => { + println!("{:#x?}", pkt); + } + Err(e) => (), + } + } + let mut hm: Vec<(_, _)> = hm.iter().collect(); + hm.sort_by_key(|(_, v)| *v); + for (k, v) in hm { + let k = k.iter().map(|v| format!("{:02x}", v)).collect::(); + println!("{} {}", k, v); + } + // println!("{:#x?}",parse("8015000000094c6576656c732f465a08466c616748756e7400000100000000000000000000000000000000000004105feb0006003e1125f3bc1300000019007e9dfa0404d5f9003f00000000000000000000")); + // println!("{:#x?}",parse("8015000000094c6576656c732f465a08466c616748756e7400002000000000000000000000000000000000000004105feb0006003e1125f3bc1300000019007e9dfa0404d5f9003f000000000000000000001f020b0376a8e2475b6e5b467c1e99461e020903982d14c5ec79cb45b2ee96471d020e03b29dbc46caa433464a28a0c71c020603aa80514658b8ab458db025c71b020803ce492f4658b8ab4514d320c71a02070344532f4658b8ab4587cf16c7190205031b3a0d4658b8ab459eaf25c7180206030ac34c4669e1fd469891ca47170208032e8c2a4669e1fd465500cd4716020703a4952a4669e1fd461b02d247150205037b7c084669e1fd460f92ca4714020603da6b7ec714aa3746b77c5a4713020803c87c83c714aa3746305a5f47120207039a7b83c714aa3746bd5d694711020503bfbe87c714aa3746a67d5a4710020803c5c719474ad5d445a7b3d2c60f0206037c5522474ad5d4459a6edcc60e02070323ca19474ad5d4458dacbec60d020503d84311474ad5d445bb6cdcc60c020603a9b16b47d52d974602dd15470b020803f2236347d52d97467bba1a470a02070350266347d52d974608be24470902050305a05a47d52d9746f1dd1547080206031f4066c6384b9c46955bd345070208037e3b84c6384b9c466147fa4506020703c33684c6384b9c46e431254605020503574395c6384b9c461063d34504020603ba349bc77a60294640f387c103020803957b9fc77a602946658f994402020703677a9fc77a60294680006d45010205038cbda3c77a602946807880c1")); +} diff --git a/tools/remaster/scraphacks_rs/.cargo/config.toml b/tools/remaster/scraphacks_rs/.cargo/config.toml new file mode 100644 index 0000000..bf040d8 --- /dev/null +++ b/tools/remaster/scraphacks_rs/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target = "i686-pc-windows-msvc" \ No newline at end of file diff --git a/tools/remaster/scraphacks_rs/Cargo.lock b/tools/remaster/scraphacks_rs/Cargo.lock new file mode 100644 index 0000000..b636b23 --- /dev/null +++ b/tools/remaster/scraphacks_rs/Cargo.lock @@ -0,0 +1,1624 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + +[[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.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" + +[[package]] +name = "app_dirs2" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47a8d2d8dbda5fca0a522259fb88e4f55d2b10ad39f5f03adeebf85031eba501" +dependencies = [ + "jni", + "ndk-context", + "winapi", + "xdg", +] + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "async-trait" +version = "0.1.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff18d764974428cf3a9328e23fc5c986f5fbed46e6cd4cdf42544df5d297ec1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "brownstone" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5839ee4f953e811bfdcf223f509cb2c6a3e1447959b0bff459405575bc17f22" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "memchr", +] + +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + +[[package]] +name = "bytecount" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c" + +[[package]] +name = "bytes" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" + +[[package]] +name = "cc" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[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 = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "comma" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55b672471b4e9f9e95499ea597ff64941a309b2cdbffcc46f2cc5e2d971fd335" + +[[package]] +name = "const_fn" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" + +[[package]] +name = "const_format" +version = "0.2.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7309d9b4d3d2c0641e018d449232f2e28f1b22933c137f157d3dbc14228b8c0e" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f47bf7270cf70d370f8f98c1abb6d2d4cf60a6845d30e05bfb90c6568650" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "crossbeam-channel" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "custom-print" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb50a9067f6b0c4ecdac7986070032d9d299f69078ed69d6131bb5ede3345bd2" + +[[package]] +name = "cxx" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d1075c37807dcf850c379432f0df05ba52cc30f279c5cfc43cc221ce7f8579" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5044281f61b27bc598f2f6647d480aed48d2bf52d6eb0b627d84c0361b17aa70" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61b50bc93ba22c27b0d31128d2d130a0a6b3d267ae27ef7e4fae2167dfe8781c" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e61fda7e62115119469c7b3591fd913ecca96fb766cfd3f2e2502ab7bc87a5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dataview" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50eb3a329e19d78c3a3dfa4ec5a51ecb84fa3a20c06edad04be25356018218f9" +dependencies = [ + "derive_pod", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_pod" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2ea6706d74fca54e15f1d40b5cf7fe7f764aaec61352a9fcec58fe27e042fc8" + +[[package]] +name = "detour3" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0246383803197ff0315ef865f2daf6611f43b5782562e49f08bfa9de8a125ee" +dependencies = [ + "cfg-if", + "generic-array", + "lazy_static", + "libc", + "libudis86-sys", + "mmap-fixed-fixed", + "region", + "slice-pool", +] + +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "discord-sdk" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f0df260a6baf1d63a08dc38a7b75f30d4722a9eb2bf6d1172d33e0221bd72ae" +dependencies = [ + "anyhow", + "app_dirs2", + "async-trait", + "base64", + "bitflags", + "crossbeam-channel", + "num-traits", + "parking_lot", + "serde", + "serde_json", + "serde_repr", + "thiserror", + "time", + "tokio", + "tracing", + "url", + "winreg", +] + +[[package]] +name = "either" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" + +[[package]] +name = "erased-serde" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4ca605381c017ec7a5fef5e548f1cfaa419ed0f6df6367339300db74c92aa7d" +dependencies = [ + "serde", +] + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" + +[[package]] +name = "futures-executor" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" + +[[package]] +name = "futures-macro" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" + +[[package]] +name = "futures-task" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" + +[[package]] +name = "futures-util" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "git2" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf7f68c2995f392c49fffb4f95ae2c873297830eb25c6bc4c114ce8f4562acc" +dependencies = [ + "bitflags", + "libc", + "libgit2-sys", + "log", + "url", +] + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[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 = "iced-x86" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd04b950d75b3498320253b17fb92745b2cc79ead8814aede2f7c1bab858bec" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indent_write" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cfe9645a18782869361d9c8732246be7b410ad4e919d3609ebabdac00ba12c3" + +[[package]] +name = "is_debug" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06d198e9919d9822d5f7083ba8530e04de87841eaf21ead9af8f2304efd57c89" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" + +[[package]] +name = "jni" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" +dependencies = [ + "libc", +] + +[[package]] +name = "joinery" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72167d68f5fce3b8655487b8038691a3c9984ee769590f93f2a631f4ad64e4f5" + +[[package]] +name = "js-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" + +[[package]] +name = "libgit2-sys" +version = "0.14.2+1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f3d95f6b51075fe9810a7ae22c7095f12b98005ab364d8544797a825ce946a4" +dependencies = [ + "cc", + "libc", + "libz-sys", + "pkg-config", +] + +[[package]] +name = "libudis86-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139bbf9ddb1bfc90c1ac64dd2923d9c957cd433cee7315c018125d72ab08a6b0" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "libz-sys" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "link-cplusplus" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "lua-src" +version = "544.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "708ba3c844d5e9d38def4a09dd871c17c370f519b3c4b7261fbabe4a613a814c" +dependencies = [ + "cc", +] + +[[package]] +name = "luajit-src" +version = "210.4.5+resty2cf5186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b7992a40e602786272d84c6f2beca44a588ededcfd57b48ec6f82008a7cb97" +dependencies = [ + "cc", +] + +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "mio" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + +[[package]] +name = "mlua" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee2ad7a9aa69056b148d9d590344bc155d3ce0d2200e3b2838f7034f6ba33c1" +dependencies = [ + "bstr", + "cc", + "erased-serde", + "lua-src", + "luajit-src", + "mlua_derive", + "num-traits", + "once_cell", + "pkg-config", + "rustc-hash", + "serde", +] + +[[package]] +name = "mlua_derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9214e60d3cf1643013b107330fcd374ccec1e4ba1eef76e7e5da5e8202e71c0" +dependencies = [ + "itertools", + "once_cell", + "proc-macro-error", + "proc-macro2", + "quote", + "regex", + "syn", +] + +[[package]] +name = "mmap-fixed-fixed" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0681853891801e4763dc252e843672faf32bcfee27a0aa3b19733902af450acc" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "no-std-compat" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nom-greedyerror" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f359007d505b20cd6e4974ff0d5c8e4565f0f9e15823937238221ccb74b516" +dependencies = [ + "nom", + "nom_locate", +] + +[[package]] +name = "nom-supreme" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bd3ae6c901f1959588759ff51c95d24b491ecb9ff91aa9c2ef4acc5b1dcab27" +dependencies = [ + "brownstone", + "indent_write", + "joinery", + "memchr", + "nom", +] + +[[package]] +name = "nom_locate" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e299bf5ea7b212e811e71174c5d1a5d065c4c0ad0c8691ecb1f97e3e66025e" +dependencies = [ + "bytecount", + "memchr", + "nom", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + +[[package]] +name = "once_cell" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "parse_int" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d695b79916a2c08bcff7be7647ab60d1402885265005a6658ffe6d763553c5a" +dependencies = [ + "num-traits", +] + +[[package]] +name = "pelite" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88dccf4bd32294364aeb7bd55d749604450e9db54605887551f21baea7617685" +dependencies = [ + "dataview", + "libc", + "no-std-compat", + "pelite-macros", + "winapi", +] + +[[package]] +name = "pelite-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a7cf3f8ecebb0f4895f4892a8be0a0dc81b498f9d56735cb769dc31bf00815b" + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[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.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" +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 = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "region" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" +dependencies = [ + "bitflags", + "libc", + "mach", + "winapi", +] + +[[package]] +name = "rhexdump" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e9af64574935e39f24d1c0313a997c8b880ca0e087c888bc6af8af31579847" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "ryu" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "scraphacks_rs" +version = "0.1.0" +dependencies = [ + "anyhow", + "comma", + "custom-print", + "derivative", + "detour3", + "discord-sdk", + "futures", + "hex", + "iced-x86", + "mlua", + "nom", + "nom-greedyerror", + "nom-supreme", + "nom_locate", + "num-traits", + "once_cell", + "parse_int", + "pelite", + "region", + "rhexdump", + "rustc-hash", + "shadow-rs", + "struct_layout", + "tokio", + "viable", + "winsafe", +] + +[[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.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a5ec9fa74a20ebbe5d9ac23dac1fc96ba0ecfe9f50f2843b52e537b10fbcb4e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "shadow-rs" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "427f07ab5f873000cf55324882e12a88c0a7ea7025df4fc1e7e35e688877a583" +dependencies = [ + "const_format", + "git2", + "is_debug", + "time", + "tzdb", +] + +[[package]] +name = "slab" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slice-pool" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "733fc6e5f1bd3a8136f842c9bdea4e5f17c910c2fcc98c90c3aa7604ef5e2e7a" + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "struct_layout" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4d806456ba83a5a07a5c09f984c7b5339c576d38d9656d464956ec4a74e5d7" + +[[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 = "thiserror" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" +dependencies = [ + "itoa", + "libc", + "num_threads", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" + +[[package]] +name = "time-macros" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" +dependencies = [ + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb" +dependencies = [ + "autocfg", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "windows-sys", +] + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "tz-rs" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33851b15c848fad2cf4b105c6bb66eb9512b6f6c44a4b13f57c53c73c707e2b4" +dependencies = [ + "const_fn", +] + +[[package]] +name = "tzdb" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b882d864be6a5d7c3c916719944458b1e03c85f86dbc825ec98155117c4408" +dependencies = [ + "iana-time-zone", + "tz-rs", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + +[[package]] +name = "unicode-ident" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "viable" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7746c1a6e7b03a755198414f4a364d8515f688f5f2e5fdd1db323891d299cb60" +dependencies = [ + "viable-impl", +] + +[[package]] +name = "viable-impl" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55bccf43d6bb2790e856edc1f250e25d8eeca72b2e6dd659386c85f794d53a6f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" + +[[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.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +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" + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + +[[package]] +name = "winsafe" +version = "0.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62a4f20d0e60f34bfbb2624564d0bfda0a6eaee8f7f4494e5c3c883c3a62fdd2" + +[[package]] +name = "xdg" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4583db5cbd4c4c0303df2d15af80f0539db703fa1c68802d4cbbd2dd0f88f6" +dependencies = [ + "dirs", +] diff --git a/tools/remaster/scraphacks_rs/Cargo.toml b/tools/remaster/scraphacks_rs/Cargo.toml new file mode 100644 index 0000000..a4bf58c --- /dev/null +++ b/tools/remaster/scraphacks_rs/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "scraphacks_rs" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[profile.release] +debug = 0 + +[lib] +crate-type = ["dylib"] + +[features] +console=[] + +[dependencies] +anyhow = "1.0.68" +comma = "1.0.0" +custom-print = "0.1.0" +derivative = "2.2.0" +detour3 = "0.1.0" +discord-sdk = "0.3.2" +futures = "0.3.25" +hex = "0.4.3" +iced-x86 = "1.18.0" +mlua = { version = "0.8.7", features = ["luajit", "vendored", "macros", "serialize", "mlua_derive"] } +nom = "7.1.3" +nom-greedyerror = "0.5.0" +nom-supreme = "0.8.0" +nom_locate = "4.1.0" +num-traits = "0.2.15" +once_cell = "1.17.0" +parse_int = "0.6.0" +pelite = "0.10.0" +region = "3.0.0" +rhexdump = "0.1.1" +rustc-hash = "1.1.0" +shadow-rs = "0.21.0" +struct_layout = "0.1.0" +tokio = "1.24.2" +viable = "0.2.0" +winsafe = { version = "0.0.15", features = ["kernel", "user", "dshow"] } + +[build-dependencies] +shadow-rs = "0.21.0" diff --git a/tools/remaster/scraphacks_rs/Pipfile b/tools/remaster/scraphacks_rs/Pipfile new file mode 100644 index 0000000..71e4f7c --- /dev/null +++ b/tools/remaster/scraphacks_rs/Pipfile @@ -0,0 +1,11 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] + +[dev-packages] + +[requires] +python_version = "3.9" diff --git a/tools/remaster/scraphacks_rs/Save0.sav.json b/tools/remaster/scraphacks_rs/Save0.sav.json new file mode 100644 index 0000000..f7d1504 --- /dev/null +++ b/tools/remaster/scraphacks_rs/Save0.sav.json @@ -0,0 +1,242 @@ +{ + "id": "Outskirts - 07:41:13", + "title": "Scrapland savegame", + "data": { + "CTFFinishPlayerLoosesTextCad": "Mission_CTFAgainstBankers_RemoteMessage3", + "CTFFinishPlayerWinsTextCad": "Mission_CTFAgainstBankers_RemoteMessage2", + "CTFStartTextCad": "Mission_CTFAgainstBankers_RemoteMessage1", + "CTFOnDeathSpawnTime": "5.0", + "CTFFriendProfile": "BankersCTFFriends", + "CTFEnemyProfile": "BankersCTFEnemies", + "CTFFriendHead": "Functionary", + "CTFFriendType": "Functionary", + "CTFEnemyType": "BankDirector", + "CTFNumFriends": "5", + "CTFNumEnemies": "5", + "CTFFlags": "5", + "CombatFriendProfile": "ArrangeBankersCombatFriends", + "CombatFriendType": "Police", + "CombatNumFriends": "4", + "CombatNumEnemies": "5", + "PlayerWinsCombat": "1", + "OnVictory": "import SaveGame; SaveGame.QWayPoint();import MissionsFuncs; MissionsFuncs.SetNextMission(\"Mission_BackFromMortalRace\", \"GamblinDen\");Scrap.SetSaveVar(\"Map\", \"Levels/GamblinDen\");import SaveGame; SaveGame.QLoad(\"Levels/GamblinDen\");Scrap.SetSaveVar('MapPress_BureauExists','1')", + "OnAbort": "import MissionsFuncs; MissionsFuncs.EndOfSuperDeal('SuperDeal_Faliure_SystemMessage1')", + "CombatFinishPlayerLoosesTextCad": "SuperDeal_First_RemoteMessage3", + "CombatFinishPlayerWinsTextCad": "SuperDeal_First_RemoteMessage2", + "CombatStartTextCad": "SuperDeal_First_RemoteMessage1", + "CombatEnemyProfile": "SuperDealFirstElite", + "CombatEnemyTypeHead": "CrazyGambler", + "CombatEnemyType": "BankDirector", + "CombatDeaths": "5", + "SuperDealType": "", + "IgorFirstContactMissionState": "TalkToMercenaries", + "Stats.MadHunter": "10", + "Stats.Nurse.Dazed": "2", + "Stats.BankMaster": "6758", + "Bank.Circuit.36": "0", + "Bank.Circuit.35": "0", + "Bank.Circuit.34": "0", + "Bank.Circuit.33": "0", + "Bank.Circuit.32": "0", + "Bank.Circuit.31": "0", + "Bank.Circuit.30": "0", + "Bank.Circuit.29": "0", + "Bank.Circuit.28": "0", + "Bank.Circuit.27": "0", + "Bank.Circuit.26": "0", + "Bank.Circuit.25": "0", + "Bank.Circuit.24": "0", + "Bank.Circuit.23": "0", + "Bank.Circuit.22": "0", + "Bank.Circuit.21": "0", + "Bank.Circuit.20": "0", + "Bank.Circuit.19": "0", + "Bank.Circuit.18": "0", + "Bank.Circuit.17": "0", + "Bank.Circuit.16": "0", + "Bank.Circuit.15": "0", + "Bank.Circuit.14": "0", + "Bank.Circuit.13": "0", + "Bank.Circuit.12": "0", + "Bank.Circuit.11": "0", + "Bank.Circuit.10": "0", + "Bank.Circuit.9": "0", + "Bank.Circuit.8": "0", + "Bank.Circuit.7": "0", + "Bank.Circuit.6": "0", + "Bank.Circuit.5": "0", + "Bank.Circuit.4": "0", + "Bank.Circuit.3": "0", + "Bank.Circuit.2": "0", + "Bank.Circuit.1": "0", + "Bank.Circuit.0": "0", + "Stats.Mosquito": "116", + "PoliceBossAtTownHall": "0", + "Stats.Parking": "18", + "Police.FicusDeath": "1", + "CostumeAtPolice": "0", + "Hangar.HangarShip10": "SLifeBoat<-<-<-<-<-0,0,0,0,0,0,1<-30<-0,0,0,0,0,0", + "Hangar.iHangarShips": "8", + "AutoSaveGameOnLoad": "0", + "GameAct": "3rdMurder", + "BankDebt": "0", + "Map": "Levels/Outskirts", + "Mission.Library": "Mission_DestroyBadDebtors", + "EnergyBarActive": "1", + "SpecialActionActive": "2", + "CrazyWing.Status": "1", + "Conversor.ActiveConversors": "1", + "Hangar.iHangarShip": "0", + "Player.NumLives": "100", + "Hangar.shipsToEditList": "['SPoli1', 'SPoli2', 'SPoli3', 'SPoli4', 'SPoli5', 'SPoliBoss1', 'SMerc1', 'SMerc2', 'SMerc3', 'SMayor1', 'SBanker1', 'SBankMaster1', 'SBishop1', 'SArchbishop1', 'SFunc1', 'SBerto1', 'SBetty1', 'SHump1', 'SBoss1', 'SPoli4']", + "Hangar.availableEnginesList": "['MPOLI4', 'MPOLI5', 'MPOLIBOSS1', 'MPOLI2', 'MBERTO1', 'MBETTY1', 'MPOLI1', 'MMERC1', 'MMERC2', 'MPOLI3', 'MMAYOR1', 'MFUNC1', 'MBANKER1', 'MBANKMASTER1', 'MBISHOP1', 'MARCHBISHOP1', 'MHUMP1', 'MBOSS1', 'MMERC3']", + "Hangar.availableWeaponsList": "['Vulcan', 'Devastator', 'Swarm', 'Inferno', 'Tesla', 'ATPC', 'Swarm', 'Devastator']", + "Hangar.availableUpgradesList": "[\"VulcanUpgrade1\", \"VulcanUpgrade2\", \"DevastatorUpgrade1\", \"DevastatorUpgrade2\", \"SwarmUpgrade1\", \"SwarmUpgrade2\", \"InfernoUpgrade1\", \"InfernoUpgrade2\", \"TeslaUpgrade1\", \"TeslaUpgrade2\", \"ATPCUpgrade1\", \"ATPCUpgrade2\"]", + "JackInTheBox.Active": "1", + "Parked.Police": "['SPOLI1', 'SPOLI2', 'SPOLI3', 'SPOLI4']", + "Parked.Mercs": "['SMERC1', 'SMERC2']", + "Parked.TownHall": "['SFUNC1']", + "Parked.Bank": "['SBANKER1']", + "Parked.Press": "['SBERTO1', 'SBETTY1', 'SHUMP1']", + "Parked.Temple": "['SBISHOP1']", + "PoliceBlueprints.Ships": "['SPoli2', 'SPoli3', 'SPoli5', 'SPoliBoss1']", + "PoliceBlueprints.Engines": "['MPoli2', 'MPoli3', 'MPoli4', 'MPoli5', 'MPoliBoss1']", + "PressBlueprints.Ships": "['SBerto1', 'SBetty1', 'SHump1']", + "PressBlueprints.Engines": "['MBerto1', 'MBetty1', 'MHump1']", + "MayorAtGambinDen": "0", + "PolicesAtGambinDen": "1", + "PoliceBossAtPolice": "0", + "CrazyGamblerAtGambinDen": "1", + "FunctionaryTwinsAtGambinDen": "1", + "BankersAtGambinDen": "0", + "BishopsAtGambinDen": "0", + "GameSkill": "0", + "CreateBertos": "0", + "MercsHelpDtritus": "0", + "RobotsControlledByBoss": "0", + "Player.InfiniteLives": "0", + "PrevMap": "Levels/GamblinDen", + "Spawn": "DM_Player_Spawn_GamblinDen", + "Char": "Dtritus", + "ComeFrom": "DoorElevator", + "AlarmActive": "0", + "AlarmStatus": "0.0", + "Money": "2147483647", + "Challenge": "1", + "Challenge.Type": "", + "Challenge.Foe": "", + "Challenge.NumEnemies": "", + "Challenge.Money": "0", + "Revenge.Type": "", + "Revenge.Foe": "", + "Revenge.NumEnemies": "", + "Revenge.LastWinner": "", + "Mission.Map": "Outskirts", + "BadReputation": "3", + "GameplayTime": "27673.5857997", + "LonelyMercInGamblinDen": "0", + "LonelyMercLairActive": "1", + "LonelyMercDataActive": "0", + "ComSatsMissionsMapsFinished": "[]", + "Conversor.AvailableChars": "['Police', 'Nurse', 'BankDirector', 'Desktop', 'Sentinel', 'Gear', 'Bishop', 'Messenger', 'Functionary', 'Betty', 'Berto', 'BankMaster', 'Dtritus']", + "Batteries": "5", + "AcBatteries": "2", + "PrimaryMissionDesc": "Ich muss den Bankdirektor besuchen. Er treibt gerade ausstehende Kreditzahlungen mit seinem Kampfraumschiff ein. Ich sehe mal, ob ich ihm helfen kann.", + "SecondaryMissionDesc": "", + "TakePhotoMsg": "0", + "Race.Num": "3", + "Race.FirstTime": "0", + "Race.Profile": "Pilot", + "Combat.FirstTime": "1", + "Combat.Profile": "Rookie", + "Traffic.AcShips": "[\"SPoli6\", \"SMerc1\",\"SMerc2\",\"SBanker1\"]", + "IsSecondMission": "0", + "CrazyDeal.1.Var": "Stats.Dtritus", + "CrazyDeal.1.Tgt": "5", + "CrazyDeal.2.Var": "Stats.Parking", + "CrazyDeal.2.Tgt": "3", + "CrazyDeal.3.Var": "Stats.Battery", + "CrazyDeal.3.Tgt": "5", + "SuperDeal.Map": "FZ", + "SuperDeal.Library": "SuperDeal_Second", + "SuperDeal.Type": "MortalRace", + "CrazyWing.List": "['Sentinel', 'Betty', 'CrazyGambler', 'Functionary', 'Bishop']", + "Journalist.Humphrey_Defaut": "NoPlace", + "Journalist.Betty_Defaut": "NoPlace", + "Journalist.Berto_Defaut": "Press", + "Conversor.FirstConversion": "0", + "Conversor.FirstPossession": "0", + "WindowsError": "0", + "Orbit.Decontaminated": "yes", + "MercFriends.MercenaryA_Smartie": "0", + "MercFriends.MercenaryC_Brutus": "0", + "MercFriends.MercenaryB_Dumber": "0", + "StdShipAIProfile": "Elite", + "usrShip.Ammo00": "1000.0", + "usrShip.Ammo01": "500.0", + "usrShip.Ammo02": "1500.0", + "usrShip.AcWeap": "6", + "Parking.Desolate": "0", + "Hangar.HangarShip1": "SBoss1<-MBOSS1<-MBOSS1<-<-<-15,15,15,15,15,15,1<-187<-1,8,6,9,11,3", + "Hangar.HangarShip2": "", + "Hangar.HangarShip3": "", + "Hangar.HangarShip4": "", + "Hangar.HangarShip5": "", + "Hangar.HangarShip6": "", + "Hangar.HangarShip7": "", + "Hangar.HangarShip8": "", + "Hangar.HangarShip9": "", + "Hangar.HangarShipAux": "SLifeBoat<-<-<-<-<-0,0,0,0,0,0,1<-50<-0,0,0,0,0,0", + "Hangar.DestroyedShips": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]", + "NewBluePrintAvaliable": "11", + "DebugSave": "1", + "OutMusicRelax": "141", + "MayorAtTownHall": "1", + "GamblinMusic": "1", + "OutMusicAction": "122", + "Stats.Traffic": "767", + "Stats.OutPolice": "110", + "MapPress_HiAlarm": "0", + "MapPress_BureauExists": "1", + "Stats.PossessBerto": "1", + "Stats.ConvertIntoDtritus": "1", + "Stats.WinHumphreyRace": "1", + "Stats.Possession": "57", + "Stats.Betty": "49", + "Stats.Killer": "119", + "Stats.Jump": "1", + "Stats.Jump.Police": "1", + "Stats.Bishop": "10", + "Stats.Battery": "0", + "Stats.Dtritus": "0", + "Stats.Race.Press": "1", + "Stats.TotalRaces.Press": "1", + "BankMasterAtBank": "1", + "DM_ExtraLife_00": "-60", + "DM_ExtraLife_01": "-60", + "DM_ExtraLife_02": "-60", + "DM_ExtraLife_03": "-60", + "DM_ExtraLife_04": "16294.7919922", + "Stats.TempleLife": "4", + "Mission_CatchTrurl_Data": "[]", + "Mission_CatchTrurl_MapsPos": "[]", + "Mission_CatchTrurl_NumMapsDropped": "0", + "Mission_CatchTrurl_NumMapsTaken": "0", + "GDB.BishopsMsg": "1", + "Stats.GDB": "6", + "LonelyMercActive": "0", + "Stats.Messenger": "2", + "MortalRaceRace": "[((71611.59375, 18231.6992188, 93232.796875), 422.337219238), ((45388.4140625, 14599.3476563, 79622.6640625), 400.984222412), ((25194.9804688, 18783.4863281, 59759.296875), 404.390136719), ((-433.664245605, 26340.1289063, 34561.0898438), 409.718261719), ((-38229.3671875, 26457.5292969, 679.068054199), 449.472442627), ((-107464.132813, 19331.875, 3288.328125), 528.452758789), ((-113911.117188, 14331.4462891, 40812.9414063), 558.054199219), ((-102532.132813, 11236.1474609, 75072.375), 630.567077637), ((-58177.6289063, 6282.20654297, 74209.3515625), 673.615478516), ((-24157.5449219, 7054.30419922, 43223.1679688), 630.510681152), ((33550.1445313, 15480.2402344, 41122.5820313), -55.4565696716), ((56201.6054688, 15587.5126953, 24649.8496094), 23.7488441467), ((26343.9511719, 22077.8789063, -32317.0292969), 90.5086135864), ((-13835.4755859, 26276.8730469, -31975.1582031), 145.932754517), ((-29244.3652344, 26745.4667969, -2544.81738281), -892.995666504), ((-23808.9570313, 27246.9980469, 32018.1816406), -819.383483887), ((21584.3066406, 29452.4667969, 41221.6171875), -822.313781738), ((54026.796875, 24611.7421875, 42694.0898438), -802.188171387), ((95732.015625, 16516.8085938, 36323.546875), -872.699890137), ((113450.46875, 12325.5195313, 77796.75), -969.003662109)]", + "MortalRaceWaypoints": "20", + "MortalRaceRacers": "['ArchBishop', 'BankDirector', 'Functionary', 'MercenaryA', 'MercenaryB']", + "MortalRaceRacersProfile": "MortalRace", + "MortalRaceFightingFreezeControlAim": "4", + "MortalRaceRespawnTime": "5.0", + "MortalRaceStartTextCad": "Mission_WinMortalRace_RemoteMessage1", + "MortalRaceFinishPlayerLoosesTextCad": "Mission_WinMortalRace_RemoteMessage2", + "MortalRaceFinishPlayerLoosesTextFoe": "Messenger", + "MortalRaceFinishPlayerWinsTextCad": "Mission_WinMortalRace_RemoteMessage3", + "MortalRaceFinishPlayerWinsTextFoe": "Messenger", + "MortalRaceAutoRestart": "1" + } +} \ No newline at end of file diff --git a/tools/remaster/scraphacks_rs/build.rs b/tools/remaster/scraphacks_rs/build.rs new file mode 100644 index 0000000..4a0dfc4 --- /dev/null +++ b/tools/remaster/scraphacks_rs/build.rs @@ -0,0 +1,3 @@ +fn main() -> shadow_rs::SdResult<()> { + shadow_rs::new() +} diff --git a/tools/remaster/scraphacks_rs/build.sh b/tools/remaster/scraphacks_rs/build.sh new file mode 100644 index 0000000..cf4b138 --- /dev/null +++ b/tools/remaster/scraphacks_rs/build.sh @@ -0,0 +1,4 @@ +impo +cargo b -r +cp D:/devel/Git_Repos/Scrapland-RE/tools/remaster/scraphacks_rs/target/i686-pc-windows-msvc/release/scraphacks_rs.dll E:/Games/Steam/steamapps/common/Scrapland/lib/ScrapHack.pyd +# x32dbg E:/Games/Steam/steamapps/common/Scrapland/Bin/Scrap.unpacked.exe "-debug:10 -console" \ No newline at end of file diff --git a/tools/remaster/scraphacks_rs/notes.md b/tools/remaster/scraphacks_rs/notes.md new file mode 100644 index 0000000..705b494 --- /dev/null +++ b/tools/remaster/scraphacks_rs/notes.md @@ -0,0 +1,16 @@ +# Notes + +## Snippets + +Map name: `Scrap.GetLangStr("Station_" + Scrap.GetLevelPath()[7:])` + +## Run + +`steam://run/897610/` + +## Signatures + +- World pointer: `a3 *{'} e8 ? ? ? ? 6a 00 68 *"World initialized"` +- print: `6a0068 *{"Scrap engine"} 6a?e8 $'` +- console handler: `68 *{"import Viewer"} e8 $'` +- DirectX Device: `` \ No newline at end of file diff --git a/tools/remaster/scraphacks_rs/run.py b/tools/remaster/scraphacks_rs/run.py new file mode 100644 index 0000000..2cda825 --- /dev/null +++ b/tools/remaster/scraphacks_rs/run.py @@ -0,0 +1,34 @@ +import subprocess as SP +import shutil as sh +import json +from pathlib import Path +import psutil +import os +import sys +os.environ['DISCORD_INSTANCE_ID']='1' +SP.check_call(["cargo","b","-r"]) +info=[json.loads(line) for line in SP.check_output(["cargo","b", "-r" ,"-q","--message-format=json"]).splitlines()] +dll_path=None +for line in info: + if line.get('reason')=="compiler-artifact" and ("dylib" in line.get("target",{}).get("crate_types",[])): + dll_path=Path(line['filenames'][0]) + +sh.copy(dll_path,"E:/Games/Steam/steamapps/common/Scrapland/lib/ScrapHack.pyd") + +if "--run" not in sys.argv[1:]: + exit(0) + +os.startfile("steam://run/897610/") +pid=None +while pid is None: + for proc in psutil.process_iter(): + try: + if proc.name()=="Scrap.exe": + pid=proc.pid + except: + pass +print(f"PID: {pid:x}") +if "--dbg" in sys.argv[1:]: + SP.run(["x32dbg","-p",str(pid)]) +# cp D:/devel/Git_Repos/Scrapland-RE/tools/remaster/scraphacks_rs/target/i686-pc-windows-msvc/release/scraphacks_rs.dll E:/Games/Steam/steamapps/common/Scrapland/lib/ScrapHack.pyd +# x32dbg E:/Games/Steam/steamapps/common/Scrapland/Bin/Scrap.unpacked.exe "-debug:10 -console" \ No newline at end of file diff --git a/tools/remaster/scraphacks_rs/scrap.hpp b/tools/remaster/scraphacks_rs/scrap.hpp new file mode 100644 index 0000000..a6c95ee --- /dev/null +++ b/tools/remaster/scraphacks_rs/scrap.hpp @@ -0,0 +1,16 @@ +#include +struct HashTable { + uint32_t num_slots; + struct HashTableEntry **chains; +}; + +struct HashTableEntry { + void *data; + const char *name; + HashTableEntry *next; +}; + +struct World { + void** VMT; + HashTable *entities; +}; \ No newline at end of file diff --git a/tools/remaster/scraphacks_rs/src/config.rs b/tools/remaster/scraphacks_rs/src/config.rs new file mode 100644 index 0000000..95585b7 --- /dev/null +++ b/tools/remaster/scraphacks_rs/src/config.rs @@ -0,0 +1,7 @@ +enum FilePatch { + +} + +pub struct Config { + file_patches: FxHashMap +} \ No newline at end of file diff --git a/tools/remaster/scraphacks_rs/src/discord.rs b/tools/remaster/scraphacks_rs/src/discord.rs new file mode 100644 index 0000000..acc021b --- /dev/null +++ b/tools/remaster/scraphacks_rs/src/discord.rs @@ -0,0 +1,94 @@ +use std::{num::NonZeroU32, thread::JoinHandle, time::SystemTime}; + +use crate::{cdbg, ceprintln, cprint, cprintln}; +use anyhow::{bail, Result}; +use discord_sdk::{ + activity::{ActivityBuilder, Assets, PartyPrivacy, Secrets}, + registration::{register_app, Application, LaunchCommand}, + wheel::Wheel, + Discord, DiscordApp, Subscriptions, +}; +const APP_ID: discord_sdk::AppId = 1066820570097930342; +const STEAM_APP_ID: u32 = 897610; +pub struct Client { + pub discord: discord_sdk::Discord, + pub user: discord_sdk::user::User, + pub wheel: discord_sdk::wheel::Wheel, +} + +impl Client { + pub fn run() -> Result>> { + let rt = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build()?; + register_app(Application { + id: APP_ID, + name: Some("Scrapland Remastered".to_owned()), + command: LaunchCommand::Steam(STEAM_APP_ID), + })?; + Ok(std::thread::spawn(move || rt.block_on(Self::run_async()))) + } + async fn run_async() -> Result<()> { + let (wheel, handler) = Wheel::new(Box::new(|err| { + ceprintln!("Encountered an error: {}", err); + })); + let mut user = wheel.user(); + let discord = Discord::new( + DiscordApp::PlainId(APP_ID), + Subscriptions::ACTIVITY, + Box::new(handler), + )?; + user.0.changed().await?; + let user = match &*user.0.borrow() { + discord_sdk::wheel::UserState::Connected(user) => user.clone(), + discord_sdk::wheel::UserState::Disconnected(err) => { + ceprintln!("Failed to connect to Discord: {err}"); + bail!("{}", err); + } + }; + let uid = user.id; + cprintln!( + "Logged in as: {user}#{discriminator}", + user = user.username, + discriminator = user + .discriminator + .map(|d| d.to_string()) + .unwrap_or_else(|| "????".to_owned()) + ); + let mut activity = ActivityBuilder::new() + .state("Testing") + .assets(Assets::default().large("scrap_logo", Some("Testing"))) + .timestamps(Some(SystemTime::now()), Option::::None) + .details("Testing ScrapHack"); + if false { + // (SCRAP.is_server()||SCRAP.is_client()) + let players = 1; + let capacity = 32; + activity = activity + .instance(true) + .party( + "Testt", + NonZeroU32::new(players), + NonZeroU32::new(capacity), + if false { + PartyPrivacy::Private + } else { + PartyPrivacy::Public + } + ) + .secrets(Secrets { + r#match: Some("MATCH".to_owned()), // Use server_ip+port + join: Some("JOIN".to_owned()), // Use server_ip+port + spectate: Some("SPECTATE".to_owned()), // Use server_ip+port + }); + } + + discord.update_activity(activity).await?; + loop { + if let Ok(req) = wheel.activity().0.try_recv() { + cprintln!("Got Join request: {req:?}"); + } + } + Ok(()) + } +} diff --git a/tools/remaster/scraphacks_rs/src/lib.rs b/tools/remaster/scraphacks_rs/src/lib.rs new file mode 100644 index 0000000..c1bca1c --- /dev/null +++ b/tools/remaster/scraphacks_rs/src/lib.rs @@ -0,0 +1,96 @@ +#![feature(abi_thiscall)] +#![feature(c_variadic)] +mod discord; +mod lua; +mod mem; +mod parser; +mod scrap; +use std::ffi::{c_char, c_void, CString}; +use anyhow::Result; +use crate::mem::search; +use crate::scrap::SCRAP; +use shadow_rs::shadow; +use winsafe::{co::{MB, CS, WS}, prelude::*, HWND, WNDCLASSEX, RegisterClassEx, WString}; + +shadow!(build); + +custom_print::define_macros!({cprint, cprintln, cdbg}, fmt, |value: &str| {crate::scrap::SCRAP.print(value)}); +custom_print::define_macros!({ceprint, ceprintln}, fmt, |value: &str| {crate::scrap::SCRAP.print_c(0x800000,value)}); + +#[allow(clippy::single_component_path_imports)] +pub(crate) use {cdbg, cprint, cprintln}; +#[warn(clippy::single_component_path_imports)] +pub(crate) use {ceprint, ceprintln}; + +#[repr(C)] +#[derive(Debug)] +struct PyMethodDef { + name: *const c_char, + func: *const (*const c_void, *const c_void), + ml_flags: i32, + doc: *const c_char, +} + +#[repr(C)] +#[derive(Debug)] +struct PyModuleDef { + name: *const c_char, + methods: *const PyMethodDef, +} + +fn init_py_mod() { + let py_init_module: fn( + *const c_char, // name + *const PyMethodDef, // methods + *const c_char, // doc + *const (), // passthrough + i32, // module_api_version + ) -> *const () = + unsafe { std::mem::transmute(search("68 *{\"Scrap\" 00} e8 ${'}", 1, None).unwrap_or_default()) }; + let name = CString::new("ScrapHack").unwrap_or_default(); + let desc = CString::new("ScrapHack Rust version").unwrap_or_default(); + let methods: &[PyMethodDef] = &[PyMethodDef { + name: 0 as _, + func: 0 as _, + ml_flags: 0, + doc: 0 as _, + }]; + assert!( + !py_init_module(name.as_ptr(), methods.as_ptr(), desc.as_ptr(), 0 as _, 1007).is_null() + ); +} + +#[no_mangle] +pub extern "system" fn initScrapHack() { + #[cfg(feature = "console")] + unsafe { + AllocConsole(); + } + std::panic::set_hook(Box::new(|info| { + ceprintln!("ScrapHacks: {info}"); + HWND::DESKTOP + .MessageBox(&format!("{info}"), "ScrapHacks error", MB::ICONERROR) + .unwrap(); + std::process::exit(1); + })); + init_py_mod(); + print_version_info(); + cprintln!("{SCRAP:#x?}"); +} + +#[no_mangle] +pub extern "system" fn DllMain(_inst: isize, _reason: u32, _: *const u8) -> u32 { + 1 +} + +fn print_version_info() { + cprintln!( + "{} v{} ({} {}), built for {} by {}.", + build::PROJECT_NAME, + build::PKG_VERSION, + build::SHORT_COMMIT, + build::BUILD_TIME, + build::BUILD_TARGET, + build::RUST_VERSION + ); +} diff --git a/tools/remaster/scraphacks_rs/src/lua.rs b/tools/remaster/scraphacks_rs/src/lua.rs new file mode 100644 index 0000000..9442ece --- /dev/null +++ b/tools/remaster/scraphacks_rs/src/lua.rs @@ -0,0 +1,204 @@ +use std::{path::PathBuf, sync::Arc}; + +use crate::{ + cprintln, + mem::{get_module, get_modules}, + parser::Cmd, +}; +use anyhow::{anyhow, bail, Result}; +use detour3::GenericDetour; +use mlua::{prelude::*, Variadic}; +use pelite::pattern; +use pelite::pe32::{Pe, PeObject}; +use rustc_hash::FxHashMap; +use winsafe::{prelude::*, HINSTANCE}; + +struct Ptr(*const ()); + +impl LuaUserData for Ptr { + fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { + methods.add_meta_method(LuaMetaMethod::ToString, |_, this, _: ()| { + Ok(format!("{:p}", this.0)) + }); + methods.add_method("read", |_, this, (size,): (usize,)| { + let addr = this.0 as u32; + let ptr = this.0 as *const u8; + let info = region::query(ptr).map_err(mlua::Error::external)?; + let end = info.as_ptr_range::<()>().end as u32; + let size = ((end - addr) as usize).min(size); + if !info.is_readable() { + return Err(LuaError::external(anyhow!("No read permission on page"))); + } + let data = unsafe { std::slice::from_raw_parts(ptr, size) }; + Ok(data.to_vec()) + }); + methods.add_method("write", |_, this, data: Vec| { + let data = data.as_slice(); + let addr = this.0 as *const u8; + unsafe { + let handle = region::protect_with_handle( + addr, + data.len(), + region::Protection::READ_WRITE_EXECUTE, + ) + .map_err(mlua::Error::external)?; + std::ptr::copy(data.as_ptr(), addr as *mut u8, data.len()); + drop(handle); + }; + Ok(()) + }); + // methods.add_method("hook", |_, this, func: LuaFunction| -> LuaResult<()> { + // let addr = this.0; + // cprintln!("Hook: {func:?} @ {addr:p}"); + // let dt = unsafe { GenericDetour:: u32>::new(std::mem::transmute(addr), hook_func) }.unwrap(); + // Err(LuaError::external(anyhow!("TODO: hook"))) + // }); + } +} + +// extern "thiscall" fn hook_func(this: *const (), args: (u32,u32,u32)) -> u32 { +// return 0; +// } + +pub(crate) fn init() -> Result { + let lua = unsafe { Lua::unsafe_new() }; + { + let globals = lua.globals(); + globals.set("scan", lua.create_function(lua_scan)?)?; + globals.set("print", lua.create_function(lua_print)?)?; + globals.set("hook", lua.create_function(lua_hook)?)?; + globals.set("imports", lua.create_function(lua_imports)?)?; + globals.set( + "ptr", + lua.create_function(|_, addr: u32| Ok(Ptr(addr as _)))?, + )?; + globals.set( + "ptr", + lua.create_function(lua_alloc)?, + )?; + } + Ok(lua) +} + +fn lua_val_to_string(val: &LuaValue) -> LuaResult { + Ok(match val { + LuaNil => "Nil".to_owned(), + LuaValue::Boolean(b) => format!("{b}"), + LuaValue::LightUserData(u) => format!("{u:?}"), + LuaValue::Integer(i) => format!("{i}"), + LuaValue::Number(n) => format!("{n}"), + LuaValue::String(s) => (s.to_str()?).to_string(), + LuaValue::Table(t) => { + let mut vals = vec![]; + for res in t.clone().pairs() { + let (k, v): (LuaValue, LuaValue) = res?; + vals.push(format!( + "{k} = {v}", + k = lua_val_to_string(&k)?, + v = lua_val_to_string(&v)? + )); + } + format!("{{{vals}}}", vals = vals.join(", ")) + } + LuaValue::Function(f) => format!("{f:?}"), + LuaValue::Thread(t) => format!("{t:?}"), + LuaValue::UserData(u) => format!("{u:?}"), + LuaValue::Error(e) => format!("{e:?}"), + }) +} + +fn lua_alloc(lua: &Lua, size: usize) -> LuaResult { + let data = vec![0u8;size].into_boxed_slice(); + Ok(Ptr(Box::leak(data).as_ptr() as _)) +} + +fn lua_hook(lua: &Lua, (addr, func): (u32, LuaFunction)) -> LuaResult<()> { + cprintln!("Hook: {func:?} @ {addr:08x}"); + Err(LuaError::external(anyhow!("TODO: hook"))) +} + +fn lua_imports(lua: &Lua, (): ()) -> LuaResult<()> { + Err(LuaError::external(anyhow!("TODO: imports"))) +} + +fn lua_print(lua: &Lua, args: Variadic) -> LuaResult<()> { + let msg: Vec = args + .into_iter() + .map(|v| lua_val_to_string(&v)) + .collect::>>()?; + cprintln!("{}", msg.join(" ")); + Ok(()) +} + +#[derive(Debug)] +enum ScanMode { + MainModule, + Modules(Vec), + All, +} + +impl FromLua<'_> for ScanMode { + fn from_lua<'lua>(lua_value: LuaValue<'lua>, lua: &'lua Lua) -> LuaResult { + match &lua_value { + LuaValue::Nil => Ok(ScanMode::MainModule), + LuaValue::Boolean(true) => Ok(ScanMode::All), + LuaValue::Table(t) => Ok(ScanMode::Modules(FromLua::from_lua(lua_value, lua)?)), + _ => Err(LuaError::FromLuaConversionError { + from: lua_value.type_name(), + to: "scan_mode", + message: None, + }), + } + } +} + +fn lua_scan(lua: &Lua, (pattern, scan_mode): (String, ScanMode)) -> LuaResult { + let pat = pattern::parse(&pattern).map_err(mlua::Error::external)?; + let mut ret = FxHashMap::default(); + let modules = match scan_mode { + ScanMode::MainModule => vec![get_module(None).map_err(mlua::Error::external)?], + ScanMode::Modules(modules) => modules + .into_iter() + .map(|m| get_module(Some(m))) + .collect::>() + .map_err(mlua::Error::external)?, + ScanMode::All => get_modules().map_err(mlua::Error::external)?, + }; + 'outer: for module in modules { + let regions = region::query_range(module.image().as_ptr(), module.image().len()) + .map_err(mlua::Error::external)?; + for region in regions { + let Ok(region)=region else { + continue 'outer; + }; + if !region.is_readable() { + continue 'outer; + } + } + let h_module = unsafe { HINSTANCE::from_ptr(module.image().as_ptr() as _) }; + let module_name = PathBuf::from( + h_module + .GetModuleFileName() + .map_err(mlua::Error::external)?, + ) + .file_name() + .unwrap() + .to_str() + .unwrap() + .to_owned(); + if let Ok(res) = crate::mem::scan(&pat, &module) { + if !res.is_empty() { + let res: Vec> = res + .into_iter() + .map(|res| res.into_iter().map(|a| Ptr(a as _)).collect()) + .collect(); + ret.insert(module_name, res); + } + }; + } + lua.create_table_from(ret.into_iter()) +} + +pub(crate) fn exec(chunk: &str) -> Result<()> { + Ok(init()?.load(chunk).set_name("ScrapLua")?.exec()?) +} diff --git a/tools/remaster/scraphacks_rs/src/mem.rs b/tools/remaster/scraphacks_rs/src/mem.rs new file mode 100644 index 0000000..87ccda5 --- /dev/null +++ b/tools/remaster/scraphacks_rs/src/mem.rs @@ -0,0 +1,94 @@ +use std::str::FromStr; + +use anyhow::anyhow; +use anyhow::bail; +use anyhow::Result; +use pelite::pattern::parse; +use pelite::pattern::save_len; +use pelite::pattern::Atom; +use pelite::pe32::{Pe, PeView}; +use winsafe::co::TH32CS; +use winsafe::prelude::*; +use winsafe::HINSTANCE; +use winsafe::HPROCESSLIST; +pub(crate) struct Pattern(Vec, usize); + +impl Pattern { + pub(crate) fn set_index(mut self, idx: usize) -> Self { + self.1 = idx; + self + } +} + +impl FromStr for Pattern { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + Ok(Self(parse(s)?, 0)) + } +} + +impl Pattern { + pub(crate) fn scan(&self, module: Option) -> Result { + let pe = get_module(module)?; + let scan = pe.scanner(); + let mut save = vec![0u32; save_len(&self.0)]; + if !scan.finds(&self.0, 0..u32::MAX, &mut save) { + bail!("Pattern not found"); + } + save.get(self.1) + .ok_or_else(|| anyhow!("Result index out of range")) + .and_then(|r| pe.rva_to_va(*r).map_err(|e| e.into())) + } +} + +pub(crate) fn get_modules() -> Result>> { + let mut res = vec![]; + let pid = std::process::id(); + let mut h_snap = HPROCESSLIST::CreateToolhelp32Snapshot(TH32CS::SNAPMODULE, Some(pid))?; + for module in h_snap.iter_modules() { + res.push(unsafe { PeView::module(module?.hModule.as_ptr() as *const u8) }); + } + Ok(res) +} + +pub(crate) fn get_module(module: Option) -> Result> { + let hmodule = HINSTANCE::GetModuleHandle(module.as_deref())?; + Ok(unsafe { PeView::module(hmodule.as_ptr() as *const u8) }) +} + +pub(crate) fn scan(pat: &[Atom], pe: &PeView) -> Result>> { + let mut ret = vec![]; + let scan = pe.scanner(); + let mut m = scan.matches(pat, 0..u32::MAX); + let mut save = vec![0u32; save_len(pat)]; + while m.next(&mut save) { + ret.push( + save.iter() + .map(|rva| pe.rva_to_va(*rva).map_err(|e| e.into())) + .collect::>>()?, + ); + } + Ok(ret) +} + +pub(crate) fn search(pat: &str, idx: usize, module: Option) -> Result { + pat.parse::()?.set_index(idx).scan(module) +} + +fn addr_info(addr: u32) -> Result<()> { + let pid = std::process::id(); + let mut h_snap = HPROCESSLIST::CreateToolhelp32Snapshot(TH32CS::SNAPMODULE, Some(pid))?; + for module in h_snap.iter_modules() { + let module = module?; + let module_name = module.szModule(); + if module_name.to_lowercase() == "kernel32.dll" { + continue; + } + let mod_range = + unsafe { module.modBaseAddr..module.modBaseAddr.offset(module.modBaseSize as isize) }; + println!("{module_name}: {mod_range:?}"); + // let module = unsafe { PeView::module(module.modBaseAddr as *const u8) }; + } + Ok(()) +} diff --git a/tools/remaster/scraphacks_rs/src/parser.rs b/tools/remaster/scraphacks_rs/src/parser.rs new file mode 100644 index 0000000..700c7b2 --- /dev/null +++ b/tools/remaster/scraphacks_rs/src/parser.rs @@ -0,0 +1,177 @@ +// use crate::{cdbg, ceprintln, cprint, cprintln}; +use std::path::PathBuf; +use std::str::FromStr; +use anyhow::{anyhow, Result}; +use nom::branch::alt; +use nom::bytes::complete::{take_till, take_while1}; +use nom::character::complete::{digit1, hex_digit1}; +use nom::character::streaming::char; +use nom::combinator::{eof, opt, rest}; +use nom::sequence::{separated_pair, tuple}; +use nom::{IResult, Parser}; +use nom_locate::LocatedSpan; +use nom_supreme::error::ErrorTree; +use nom_supreme::final_parser::final_parser; +use nom_supreme::tag::complete::{tag, tag_no_case}; +use nom_supreme::ParserExt; +use pelite::pattern::{self, Atom}; + +type Span<'a> = LocatedSpan<&'a str>; + +type ParseResult<'a, 'b, T> = IResult, T, ErrorTree>>; + +#[derive(Debug, Clone)] +pub enum Cmd { + Imports, + Read(u32, usize), + ReadPE(u32, usize), + Write(u32, Vec), + Disams(u32, usize), + Info(Option), + Script(PathBuf), + Unload, + ScanModule(Vec, Option), + Lua(String), +} + +impl FromStr for Cmd { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + match parse(s) { + Ok(cmd) => Ok(cmd), + Err(err) => Err(anyhow!("{}", err)), + } + } +} + +fn ws(input: Span) -> ParseResult<()> { + take_while1(|c: char| c.is_whitespace()) + .value(()) + .context("Whitepace") + .parse(input) +} + +// + +/// Test + +fn hex_bytes(input: Span) -> ParseResult> { + hex_digit1 + .map_res_cut(hex::decode) + .context("Hex string") + .parse(input) +} + +fn num(input: Span) -> ParseResult { + digit1 + .map_res_cut(|n: Span| parse_int::parse(&n)) + .context("Number") + .parse(input) +} + +fn address(input: Span) -> ParseResult { + tag_no_case("0x") + .precedes(hex_digit1) + .recognize() + .map_res_cut(|addr: Span| parse_int::parse::(&addr)) + .context("Memory address") + .parse(input) +} + +fn parse_read_pe(input: Span) -> ParseResult { + tag("read_pe") + .precedes(ws) + .precedes(separated_pair(address, ws, num.opt())) + .map(|(addr, size)| Cmd::ReadPE(addr, size.unwrap_or(0x100))) + .parse(input) +} + +fn parse_read(input: Span) -> ParseResult { + tag("read") + .precedes(ws) + .precedes(separated_pair(address, ws, num.opt())) + .map(|(addr, size)| Cmd::Read(addr, size.unwrap_or(0x100))) + .parse(input) +} + +fn parse_disasm(input: Span) -> ParseResult { + tag("disasm") + .precedes(ws) + .precedes(separated_pair(address, ws, num.opt())) + .map(|(addr, size)| Cmd::Disams(addr, size.unwrap_or(50))) + .parse(input) +} + +fn parse_write(input: Span) -> ParseResult { + tag("write") + .precedes(ws) + .precedes(separated_pair(address, ws, hex_bytes)) + .map(|(addr, data)| Cmd::Write(addr, data)) + .parse(input) +} + +fn parse_info(input: Span) -> ParseResult { + tag("info") + .precedes(eof) + .value(Cmd::Info(None)) + .or(tag("info") + .precedes(ws) + .precedes(address) + .map(|addr| Cmd::Info(Some(addr)))) + .parse(input) +} + +fn parse_scan(input: Span) -> ParseResult { + let (input, _) = tag("scan").parse(input)?; + let (input, module) = + opt(tuple((char(':'), take_till(|c: char| c.is_whitespace())))).parse(input)?; + let module = module.map(|(_, module)| module.fragment().to_string()); + let (input, _) = ws.parse(input)?; + let (input, pattern) = rest + .map_res(|pat: Span| pattern::parse(&pat)) + .parse(input)?; + Ok((input, Cmd::ScanModule(pattern, module))) +} + +fn parse_unload(input: Span) -> ParseResult { + tag("unload").value(Cmd::Unload).parse(input) +} + +fn parse_imports(input: Span) -> ParseResult { + tag("imports").value(Cmd::Imports).parse(input) +} + +fn parse_lua(input: Span) -> ParseResult { + tag("lua") + .precedes(ws) + .precedes(rest) + .map(|s| Cmd::Lua(s.fragment().to_string())) + .parse(input) +} + +fn parse_script(input: Span) -> ParseResult { + tag("script") + .precedes(ws) + .precedes(rest) + .map(|s| Cmd::Script(PathBuf::from(s.fragment()))) + .parse(input) +} + +fn parse(input: &str) -> Result>> { + final_parser( + alt(( + parse_imports, + parse_unload, + parse_scan, + parse_info, + parse_write, + parse_read, + parse_read_pe, + parse_script, + parse_disasm, + parse_lua, + )) + .context("command"), + )(Span::new(input)) +} diff --git a/tools/remaster/scraphacks_rs/src/scrap.rs b/tools/remaster/scraphacks_rs/src/scrap.rs new file mode 100644 index 0000000..05790c1 --- /dev/null +++ b/tools/remaster/scraphacks_rs/src/scrap.rs @@ -0,0 +1,381 @@ +use crate::{ + cdbg, ceprintln, cprint, cprintln, lua, + mem::{get_module, scan, search}, + parser::Cmd, discord, +}; +use anyhow::{bail, Result}; +use derivative::Derivative; +use detour3::GenericDetour; +use futures::executor::block_on; +use iced_x86::{Decoder, DecoderOptions, Formatter, Instruction, NasmFormatter}; +use once_cell::sync::Lazy; +use pelite::{pe::PeView, pe32::Pe}; +use std::{ + collections::HashMap, + ffi::{c_char, CStr, CString}, + fmt::Debug, + ptr, + thread::JoinHandle, + time::Duration, +}; +use winsafe::HINSTANCE; +use winsafe::{co::TH32CS, prelude::*, HPROCESSLIST}; + +const POINTER_SIZE: usize = std::mem::size_of::<*const ()>(); + +#[repr(C)] +struct VirtualMethodTable(*const *const ()); + +impl Debug for VirtualMethodTable { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut methods = vec![]; + for idx in 0.. { + let ptr = self.get::<()>(idx); + if ptr.is_null() + || !region::query(ptr) + .map(|r| r.is_executable()) + .unwrap_or(false) + { + break; + } + methods.push(ptr); + } + f.debug_tuple("VMT").field(&methods).finish() + } +} + +impl VirtualMethodTable { + fn get(&self, offset: usize) -> *const T { + unsafe { self.0.add(POINTER_SIZE * offset).read() as *const T } + } +} + +#[derive(Derivative)] +#[derivative(Debug)] +pub struct Scrap { + print: extern "C" fn(u32, *const c_char, u8), + console_detour: GenericDetour, + world: WorldPointer, + discord_thread_handle: JoinHandle>, +} + +#[repr(C)] +#[derive(Debug)] +struct Entity { + vmt: VirtualMethodTable, +} + +#[repr(C)] +#[derive(Debug)] +struct HashTableEntry { + data: *const T, + name: *const c_char, + next: *const Self, +} + +#[repr(C)] +struct HashTable { + num_slots: u32, + chains: *const *const HashTableEntry, +} + +fn try_read(ptr: *const T) -> Option { + (!ptr.is_null()).then(|| unsafe { ptr.read() }) +} + +impl std::fmt::Debug for HashTable { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut entries: HashMap> = HashMap::default(); + for offset in 0..self.num_slots { + let offset = offset as _; + // let chain=vec![]; + let mut chain_ptr = unsafe { self.chains.offset(offset).read() }; + while !chain_ptr.is_null() { + let entry = unsafe { chain_ptr.read() }; + let data = try_read(entry.data); + let key = unsafe { CStr::from_ptr(entry.name) } + .to_str() + .unwrap() + .to_owned(); + chain_ptr = entry.next; + entries.insert(key, data); + } + } + f.debug_struct(&format!("HashTable @ {self:p} ")) + .field("num_slots", &self.num_slots) + .field("entries", &entries) + .finish() + } +} + +#[repr(C)] +struct World { + vmt: VirtualMethodTable, + entities: HashTable, +} + +impl Debug for World { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("World") + .field("vmt", &self.vmt) + .field("entities", &self.entities) + .finish() + } +} + +struct WorldPointer(u32); + +impl std::fmt::Debug for WorldPointer { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let ptr = self.ptr(); + let world = unsafe { ptr.read() }; + f.debug_tuple(&format!("WorldPointer @ {ptr:p} ")) + .field(&world) + .finish() + } +} + +impl WorldPointer { + fn ptr(&self) -> *const World { + let ptr = self.0 as *const *const World; + unsafe { ptr.read() } + } + + fn get_hashtable(&self) { + // let ents = unsafe { self.ptr().read().entities.read() }; + // cprintln!("Ents: {ents:?}"); + } +} + +pub(crate) static SCRAP: Lazy = + Lazy::new(|| Scrap::init().expect("Failed to initialize Scrap data structure")); + +impl Scrap { + const PRINT_PATTERN: &str = r#"6a0068 *{"Scrap engine"} 6a?e8 $'"#; + const PY_EXEC: &str = r#"68 *{"import Viewer"} e8 $'"#; + const WORLD_PATTERN: &str = r#"8b 0d *{'} 68 *"CTFFriends""#; + fn init() -> Result { + let scrap = unsafe { + Self { + world: WorldPointer(search(Self::WORLD_PATTERN, 1, None)? as _), + print: std::mem::transmute(search(Self::PRINT_PATTERN, 1, None)?), + console_detour: GenericDetour::::new( + std::mem::transmute(search(Self::PY_EXEC, 1, None)?), + Self::console_input, + )?, + discord_thread_handle: discord::Client::run()?, + } + }; + unsafe { scrap.console_detour.enable()? } + Ok(scrap) + } + + extern "C" fn console_input(orig_line: *const c_char) { + let line = unsafe { CStr::from_ptr(orig_line) }.to_str(); + let Ok(line) = line else { + return SCRAP.console_detour.call(orig_line); + }; + if let Some(cmd) = line.strip_prefix('$') { + let res = cmd.parse().and_then(|cmd: Cmd| cmd.exec()); + if let Err(err) = res { + ceprintln!("Error: {err}"); + } + return; + }; + SCRAP.console_detour.call(orig_line) + } + + pub fn println(&self, msg: &str) { + self.println_c(0x008000, msg) + } + + pub fn print(&self, msg: &str) { + self.print_c(0x008000, msg) + } + + pub fn print_c(&self, col: u32, msg: &str) { + let col = (col & 0xffffff).swap_bytes() >> 8; // 0xRRGGBB -> 0xBBGGRR + let msg = CString::new(msg.to_string()).unwrap(); + (self.print)(col, msg.as_ptr(), 0); + } + + pub fn println_c(&self, col: u32, msg: &str) { + let msg = msg.to_owned() + "\n"; + self.print_c(col, &msg) + } +} + +impl Cmd { + pub(crate) fn exec(&self) -> Result<()> { + let pe = get_module(None)?; + match self { + Cmd::Imports => { + for import in pe.imports()? { + let iat = import.iat()?; + let int = import.int()?; + for (func, imp) in iat.zip(int) { + let imp = imp?; + cprintln!( + "{addr:p}: {name} {imp:?}", + name = import.dll_name()?, + addr = func + ); + } + } + } + Cmd::Read(addr, size) => { + let ptr = *addr as *const u8; + let info = region::query(ptr)?; + let end = info.as_ptr_range::<()>().end as u32; + let size = ((end - addr) as usize).min(*size); + if !info.is_readable() { + bail!("No read permission on page"); + } + let data = unsafe { std::slice::from_raw_parts(ptr, size) }; + cprintln!("{}", &rhexdump::hexdump_offset(data, *addr)); + } + Cmd::Disams(addr, size) => { + let ptr = *addr as *const u8; + let info = region::query(ptr)?; + let end = info.as_ptr_range::<()>().end as u32; + let size = ((end - addr) as usize).min(*size); + if !info.is_readable() { + bail!("No read permission on page"); + } + let data = unsafe { std::slice::from_raw_parts(ptr, size) }; + let mut decoder = Decoder::with_ip(32, data, *addr as u64, DecoderOptions::NONE); + let mut instruction = Instruction::default(); + let mut output = String::new(); + let mut formatter = NasmFormatter::new(); + while decoder.can_decode() { + decoder.decode_out(&mut instruction); + output.clear(); + formatter.format(&instruction, &mut output); + cprint!("{:016X} ", instruction.ip()); + let start_index = (instruction.ip() - (*addr as u64)) as usize; + let instr_bytes = &data[start_index..start_index + instruction.len()]; + for b in instr_bytes.iter() { + cprint!("{:02X}", b); + } + cprintln!(" {}", output); + } + } + Cmd::Write(addr, data) => { + let data = data.as_slice(); + let addr = *addr as *const u8; + unsafe { + let handle = region::protect_with_handle( + addr, + data.len(), + region::Protection::READ_WRITE_EXECUTE, + )?; + std::ptr::copy(data.as_ptr(), addr as *mut u8, data.len()); + drop(handle); + }; + } + Cmd::ReadPE(addr, size) => { + if !region::query(*addr as *const ())?.is_readable() { + bail!("No read permission for 0x{addr:x}"); + } + let data = pe.read_bytes(*addr)?; + cprintln!("{}", &rhexdump::hexdump_offset(&data[..*size], *addr)); + } + Cmd::Info(None) => { + let regions = region::query_range(ptr::null::<()>(), usize::MAX)?; + for region in regions.flatten() { + cprintln!( + "{:?}: {}", + region.as_ptr_range::<*const ()>(), + region.protection() + ); + } + } + Cmd::Info(Some(addr)) => { + let info = region::query(*addr as *const ())?; + cprintln!( + "{:?}: {}", + info.as_ptr_range::<*const ()>(), + info.protection() + ); + } + Cmd::ScanModule(pat, module) => { + cprintln!("{:?}", pat); + let mut total_hits = 0; + let mut modules = vec![]; + let is_wildcard = matches!(module.as_deref(), Some("*")); + if is_wildcard { + let pid = std::process::id(); + let mut h_snap = + HPROCESSLIST::CreateToolhelp32Snapshot(TH32CS::SNAPMODULE, Some(pid))?; + for module in h_snap.iter_modules() { + let module = module?; + let module_name = module.szModule(); + let module_addr = module.hModule.as_ptr() as *const u8; + let module = region::query_range(module_addr, module.modBaseSize as usize)? + .all(|m| m.ok().map(|m| m.is_readable()).unwrap_or(false)) + .then(|| unsafe { PeView::module(module_addr) }); + if let Some(module) = module { + modules.push((module_name, module)); + } + } + } else { + let module = HINSTANCE::GetModuleHandle(module.as_deref())?; + let module_name = module.GetModuleFileName()?; + let module_addr = module.as_ptr() as *const u8; + let module = region::query(module_addr) + .map(|m| m.is_readable()) + .unwrap_or(false) + .then(|| unsafe { PeView::module(module_addr) }); + if let Some(module) = module { + modules.push((module_name, module)); + }; + } + for (module_name, pe) in modules { + let res = scan(pat, &pe)?; + if res.is_empty() { + continue; + } + total_hits += res.len(); + cprintln!("Module: {module_name}"); + let sections = pe.section_headers(); + for hit in &res { + for (idx, addr) in hit.iter().enumerate() { + let mut section_name = String::from(""); + if let Ok(section_rva) = pe.va_to_rva(*addr) { + if let Some(section) = sections.by_rva(section_rva) { + section_name = match section.name() { + Ok(name) => name.to_string(), + Err(name_bytes) => format!("{name_bytes:?}"), + }; + } else { + section_name = String::from(""); + } + }; + if let Ok(region) = region::query(addr) { + cprintln!( + "\t{}: {:?} {} [{}] {:p}", + idx, + region.as_ptr_range::<()>(), + region.protection(), + section_name, + addr + ) + } + } + } + } + cprintln!("Results: {total_hits}"); + } + Cmd::Lua(code) => { + lua::exec(code)?; + } + Cmd::Script(path) => { + for line in std::fs::read_to_string(path)?.lines() { + line.parse().and_then(|cmd: Cmd| cmd.exec())?; + } + } + other => bail!("Not implemented: {other:?}"), + } + Ok(()) + } +} diff --git a/tools/remaster/scraphacks_rs/target/.rustc_info.json b/tools/remaster/scraphacks_rs/target/.rustc_info.json new file mode 100644 index 0000000..c0733dc --- /dev/null +++ b/tools/remaster/scraphacks_rs/target/.rustc_info.json @@ -0,0 +1 @@ +{"rustc_fingerprint":18143952876974389501,"outputs":{"16636649553340150347":{"success":true,"status":"","code":0,"stdout":"___.exe\nlib___.rlib\n___.dll\n___.dll\n___.lib\n___.dll\nC:\\Users\\Earthnuker\\scoop\\persist\\rustup-msvc\\.rustup\\toolchains\\nightly-x86_64-pc-windows-msvc\npacked\n___\ndebug_assertions\nfeature=\"cargo-clippy\"\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86\"\ntarget_endian=\"little\"\ntarget_env=\"msvc\"\ntarget_family=\"windows\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_has_atomic_equal_alignment=\"16\"\ntarget_has_atomic_equal_alignment=\"32\"\ntarget_has_atomic_equal_alignment=\"64\"\ntarget_has_atomic_equal_alignment=\"8\"\ntarget_has_atomic_equal_alignment=\"ptr\"\ntarget_has_atomic_load_store\ntarget_has_atomic_load_store=\"16\"\ntarget_has_atomic_load_store=\"32\"\ntarget_has_atomic_load_store=\"64\"\ntarget_has_atomic_load_store=\"8\"\ntarget_has_atomic_load_store=\"ptr\"\ntarget_os=\"windows\"\ntarget_pointer_width=\"32\"\ntarget_vendor=\"pc\"\nwindows\n","stderr":""},"4614504638168534921":{"success":true,"status":"","code":0,"stdout":"rustc 1.70.0-nightly (9df3a39fb 2023-04-11)\nbinary: rustc\ncommit-hash: 9df3a39fb30575d808e70800f9fad5362aac57a2\ncommit-date: 2023-04-11\nhost: x86_64-pc-windows-msvc\nrelease: 1.70.0-nightly\nLLVM version: 16.0.2\n","stderr":""},"1185988223601034215":{"success":true,"status":"","code":0,"stdout":"___.exe\nlib___.rlib\n___.dll\n___.dll\n___.lib\n___.dll\nC:\\Users\\Earthnuker\\scoop\\persist\\rustup-msvc\\.rustup\\toolchains\\nightly-x86_64-pc-windows-msvc\npacked\n___\ndebug_assertions\nfeature=\"cargo-clippy\"\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"msvc\"\ntarget_family=\"windows\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_has_atomic_equal_alignment=\"16\"\ntarget_has_atomic_equal_alignment=\"32\"\ntarget_has_atomic_equal_alignment=\"64\"\ntarget_has_atomic_equal_alignment=\"8\"\ntarget_has_atomic_equal_alignment=\"ptr\"\ntarget_has_atomic_load_store\ntarget_has_atomic_load_store=\"16\"\ntarget_has_atomic_load_store=\"32\"\ntarget_has_atomic_load_store=\"64\"\ntarget_has_atomic_load_store=\"8\"\ntarget_has_atomic_load_store=\"ptr\"\ntarget_os=\"windows\"\ntarget_pointer_width=\"64\"\ntarget_thread_local\ntarget_vendor=\"pc\"\nwindows\n","stderr":""}},"successes":{}} \ No newline at end of file diff --git a/tools/remaster/scraphacks_rs/target/CACHEDIR.TAG b/tools/remaster/scraphacks_rs/target/CACHEDIR.TAG new file mode 100644 index 0000000..20d7c31 --- /dev/null +++ b/tools/remaster/scraphacks_rs/target/CACHEDIR.TAG @@ -0,0 +1,3 @@ +Signature: 8a477f597d28d172789f06886806bc55 +# This file is a cache directory tag created by cargo. +# For information about cache directory tags see https://bford.info/cachedir/ diff --git a/tools/remaster/scraphacks_rs/target/i686-pc-windows-msvc/CACHEDIR.TAG b/tools/remaster/scraphacks_rs/target/i686-pc-windows-msvc/CACHEDIR.TAG new file mode 100644 index 0000000..20d7c31 --- /dev/null +++ b/tools/remaster/scraphacks_rs/target/i686-pc-windows-msvc/CACHEDIR.TAG @@ -0,0 +1,3 @@ +Signature: 8a477f597d28d172789f06886806bc55 +# This file is a cache directory tag created by cargo. +# For information about cache directory tags see https://bford.info/cachedir/