From 6540a3755af00e2b7a86a945be0128559a5e41af Mon Sep 17 00:00:00 2001 From: xavo95 Date: Tue, 15 Apr 2025 21:59:17 +0700 Subject: [PATCH] Initial commit --- .gitignore | 4 + Cargo.lock | 1630 +++++++++++++++++ Cargo.toml | 36 + .../wicked-waifus-archiver-cli/Cargo.toml | 6 + .../wicked-waifus-archiver-cli/src/main.rs | 3 + .../wicked-waifus-downloader-cli/Cargo.toml | 19 + .../wicked-waifus-downloader-cli/src/error.rs | 23 + .../wicked-waifus-downloader-cli/src/game.rs | 170 ++ .../src/launcher.rs | 46 + .../wicked-waifus-downloader-cli/src/main.rs | 51 + .../wicked-waifus-downloader-cli/src/utils.rs | 48 + .../Cargo.toml | 7 + .../src/game.rs | 122 ++ .../src/launcher.rs | 92 + .../src/lib.rs | 42 + .../src/resources.rs | 47 + .../wicked-waifus-downloader-lib/Cargo.toml | 12 + .../wicked-waifus-downloader-lib/src/lib.rs | 165 ++ 18 files changed, 2523 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 wicked-waifus-archiver/wicked-waifus-archiver-cli/Cargo.toml create mode 100644 wicked-waifus-archiver/wicked-waifus-archiver-cli/src/main.rs create mode 100644 wicked-waifus-downloader/wicked-waifus-downloader-cli/Cargo.toml create mode 100644 wicked-waifus-downloader/wicked-waifus-downloader-cli/src/error.rs create mode 100644 wicked-waifus-downloader/wicked-waifus-downloader-cli/src/game.rs create mode 100644 wicked-waifus-downloader/wicked-waifus-downloader-cli/src/launcher.rs create mode 100644 wicked-waifus-downloader/wicked-waifus-downloader-cli/src/main.rs create mode 100644 wicked-waifus-downloader/wicked-waifus-downloader-cli/src/utils.rs create mode 100644 wicked-waifus-downloader/wicked-waifus-downloader-domain/Cargo.toml create mode 100644 wicked-waifus-downloader/wicked-waifus-downloader-domain/src/game.rs create mode 100644 wicked-waifus-downloader/wicked-waifus-downloader-domain/src/launcher.rs create mode 100644 wicked-waifus-downloader/wicked-waifus-downloader-domain/src/lib.rs create mode 100644 wicked-waifus-downloader/wicked-waifus-downloader-domain/src/resources.rs create mode 100644 wicked-waifus-downloader/wicked-waifus-downloader-lib/Cargo.toml create mode 100644 wicked-waifus-downloader/wicked-waifus-downloader-lib/src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..49ac205 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/target +/test_game_dl +/test_launcher_dl +/.idea diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..f3b44c5 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1630 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +dependencies = [ + "anstyle", + "once_cell", + "windows-sys 0.59.0", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cc" +version = "1.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" +dependencies = [ + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.5.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2df961d8c8a0d08aa9945718ccf584145eee3f3aa06cddbeac12933781102e04" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "colog" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c426b7af8d5e0ad79de6713996632ce31f0d68ba84068fb0d654b396e519df0" +dependencies = [ + "colored", + "env_logger", + "log", +] + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "colored" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" +dependencies = [ + "lazy_static", + "windows-sys 0.59.0", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "console" +version = "0.15.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "unicode-width", + "windows-sys 0.59.0", +] + +[[package]] +name = "constant_time_eq" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" + +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "cookie_store" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eac901828f88a5241ee0600950ab981148a18f2f756900ffba1b125ca6a3ef9" +dependencies = [ + "cookie", + "document-features", + "idna", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] + +[[package]] +name = "core-foundation" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[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 = "deranged" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "document-features" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" +dependencies = [ + "litrs", +] + +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "env_filter" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "flate2" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[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.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "indicatif" +version = "0.17.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235" +dependencies = [ + "console", + "number_prefix", + "portable-atomic", + "unicode-width", + "web-time", +] + +[[package]] +name = "indicatif-log-bridge" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63703cf9069b85dbe6fe26e1c5230d013dee99d3559cd3d02ba39e099ef7ab02" +dependencies = [ + "indicatif", + "log", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jiff" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ad87c89110f55e4cd4dc2893a9790820206729eaf221555f742d540b0724a0" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde", +] + +[[package]] +name = "jiff-static" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d076d5b64a7e2fe6f0743f02c43ca4a6725c0f904203bfe276a5b3e793103605" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" + +[[package]] +name = "litemap" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" + +[[package]] +name = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "miniz_oxide" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +dependencies = [ + "adler2", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "portable-atomic" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "proc-macro2" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.23.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" + +[[package]] +name = "rustls-platform-verifier" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5467026f437b4cb2a533865eaa73eb840019a0916f4b9ec563c6e617e086c9" +dependencies = [ + "core-foundation", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + +[[package]] +name = "rustls-webpki" +version = "0.103.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[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 = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "security-framework" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "smallvec" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" + +[[package]] +name = "time-macros" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "3.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b0351ca625c7b41a8e4f9bb6c5d9755f67f62c2187ebedecacd9974674b271d" +dependencies = [ + "base64", + "cookie_store", + "encoding_rs", + "flate2", + "log", + "percent-encoding", + "rustls", + "rustls-pemfile", + "rustls-pki-types", + "rustls-platform-verifier", + "serde", + "serde_json", + "ureq-proto", + "url", + "utf-8", + "webpki-roots", +] + +[[package]] +name = "ureq-proto" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae239d0a3341aebc94259414d1dc67cfce87d41cbebc816772c91b77902fafa4" +dependencies = [ + "base64", + "http", + "httparse", + "log", +] + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "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.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-root-certs" +version = "0.26.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09aed61f5e8d2c18344b3faa33a4c837855fe56642757754775548fee21386c4" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webpki-roots" +version = "0.26.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2210b291f7ea53617fbafcc4939f10914214ec15aace5ba62293a668f322c5c9" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "wicked-waifus-archiver-cli" +version = "0.1.0" + +[[package]] +name = "wicked-waifus-downloader-cli" +version = "0.1.0" +dependencies = [ + "clap", + "colog", + "constant_time_eq", + "hex", + "indicatif", + "indicatif-log-bridge", + "log", + "md-5", + "thiserror 2.0.12", + "url", + "wicked-waifus-downloader-domain", + "wicked-waifus-downloader-lib", +] + +[[package]] +name = "wicked-waifus-downloader-domain" +version = "0.1.0" +dependencies = [ + "serde", +] + +[[package]] +name = "wicked-waifus-downloader-lib" +version = "0.1.0" +dependencies = [ + "indicatif", + "log", + "serde", + "thiserror 2.0.12", + "ureq", + "wicked-waifus-downloader-domain", +] + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..43bfcf2 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,36 @@ +[workspace] +resolver = "2" +members = [ + "wicked-waifus-archiver/wicked-waifus-archiver-cli", + "wicked-waifus-downloader/wicked-waifus-downloader-cli", + "wicked-waifus-downloader/wicked-waifus-downloader-domain", + "wicked-waifus-downloader/wicked-waifus-downloader-lib" +] + +[workspace.package] +version = "0.1.0" +edition = "2024" + +[workspace.dependencies] +clap = { version = "4.5.36", features = ["derive"] } +colog = "1.3.0" +constant_time_eq = "0.4.2" +hex = "0.4.3" +indicatif = "0.17.11" +indicatif-log-bridge = "0.2.3" +log = "0.4.27" +md-5 = "0.10.6" +serde = { version = "1.0.219", features = ["derive"] } +serde_json = "1.0.140" +thiserror = "2.0.12" +ureq = { version = "3.0.10", features = ["charset", "cookies", "gzip", "json", "platform-verifier", "rustls"] } +url = "2.5.4" +wicked-waifus-downloader-domain = { path = "wicked-waifus-downloader/wicked-waifus-downloader-domain" } +wicked-waifus-downloader-lib = { path = "wicked-waifus-downloader/wicked-waifus-downloader-lib" } + +[profile.release] +strip = true # Automatically strip symbols from the binary. +lto = true # Link-time optimization. +opt-level = 3 # Optimization level 3. +codegen-units = 1 # Maximum size reduction optimizations. + diff --git a/wicked-waifus-archiver/wicked-waifus-archiver-cli/Cargo.toml b/wicked-waifus-archiver/wicked-waifus-archiver-cli/Cargo.toml new file mode 100644 index 0000000..8ceee4b --- /dev/null +++ b/wicked-waifus-archiver/wicked-waifus-archiver-cli/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "wicked-waifus-archiver-cli" +version.workspace = true +edition.workspace = true + +[dependencies] diff --git a/wicked-waifus-archiver/wicked-waifus-archiver-cli/src/main.rs b/wicked-waifus-archiver/wicked-waifus-archiver-cli/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/wicked-waifus-archiver/wicked-waifus-archiver-cli/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/wicked-waifus-downloader/wicked-waifus-downloader-cli/Cargo.toml b/wicked-waifus-downloader/wicked-waifus-downloader-cli/Cargo.toml new file mode 100644 index 0000000..fadf6e3 --- /dev/null +++ b/wicked-waifus-downloader/wicked-waifus-downloader-cli/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "wicked-waifus-downloader-cli" +version.workspace = true +edition.workspace = true + +[dependencies] +clap.workspace = true +colog.workspace = true +constant_time_eq.workspace = true +hex.workspace = true +indicatif.workspace = true +indicatif-log-bridge.workspace = true +log.workspace = true +md-5.workspace = true +thiserror.workspace = true +url.workspace = true + +wicked-waifus-downloader-domain.workspace = true +wicked-waifus-downloader-lib.workspace = true \ No newline at end of file diff --git a/wicked-waifus-downloader/wicked-waifus-downloader-cli/src/error.rs b/wicked-waifus-downloader/wicked-waifus-downloader-cli/src/error.rs new file mode 100644 index 0000000..1454809 --- /dev/null +++ b/wicked-waifus-downloader/wicked-waifus-downloader-cli/src/error.rs @@ -0,0 +1,23 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub(crate) enum Error { + #[error("Hex FromHex Error {0}")] + HexFromHex(#[from] hex::FromHexError), + #[error("Url Parse error Error {0}")] + UrlParse(#[from] url::ParseError), + #[error("Io Error {0}")] + Io(#[from] std::io::Error), + #[error("Wicked Waifus Downloader Lib Error {0}")] + WickedWaifusDownloaderLib(#[from] wicked_waifus_downloader_lib::Error), + #[error("No more CDN available")] + CdnListExhausted, + #[error("CRC Mismatch, expected: {0}, got: {1}")] + CrcMismatch(String, String), + #[error("Length mismatch, expected: {0}, got: {1}")] + LengthMismatch(u64, u64), + #[error("Preload selected but no preload switch exist")] + PreloadSwitchMissing, + #[error("Preload selected but no preload data exist")] + PreloadDataMissing, +} diff --git a/wicked-waifus-downloader/wicked-waifus-downloader-cli/src/game.rs b/wicked-waifus-downloader/wicked-waifus-downloader-cli/src/game.rs new file mode 100644 index 0000000..699c4e2 --- /dev/null +++ b/wicked-waifus-downloader/wicked-waifus-downloader-cli/src/game.rs @@ -0,0 +1,170 @@ +use crate::error::Error; +use crate::utils; +use crate::utils::verify_download; +use log::info; +use url::Url; +use wicked_waifus_downloader_domain::game::{ + GameConfig, GameDefaultConfig, GameDefaultConfigV2Config, ResourcesData, +}; +use wicked_waifus_downloader_domain::CdnInfo; +use wicked_waifus_downloader_lib::{ + download_cdn_asset_in_mem, request_chunk_resources, request_resources, +}; + +pub(crate) fn download_game( + use_predownload: bool, + use_chunked: bool, + full: bool, + game_config: GameConfig, + proxy: Option<&str>, + path: &str, +) -> Result<(), Error> { + if use_predownload { + game_config + .predownload_switch + .ok_or(Error::PreloadSwitchMissing)?; + let _predownload = game_config.predownload.ok_or(Error::PreloadDataMissing)?; + + // TODO: Implement logic for download predownload + unreachable!("Implement logic for download predownload"); + } + match game_config.default { + GameDefaultConfig::GameDefaultConfigV2(mut config) => { + utils::sort_cdn_list(&mut config.cdn_list); + let chunk_enabled = game_config.chunk_download_switch.unwrap_or(0) == 1; + + if full && use_chunked && chunk_enabled { + download_full_game_chunked(&config.cdn_list, config.config, proxy, path) + } else if full { + download_full_game(&config.cdn_list, config.resources_data, proxy, path) + } else { + download_upgrade_game(&config.cdn_list, config.config, proxy, path) + } + } + GameDefaultConfig::GameDefaultConfigV1(mut config) => { + utils::sort_cdn_list(&mut config.cdn_list); + download_full_game(&config.cdn_list, config.resources_data, proxy, path) + } + } +} + +fn try_fetch_from_cdn( + cdn_list: &[CdnInfo], + build_url: impl Fn(&str) -> Option, + fetch_fn: impl Fn(&str) -> Result, +) -> Result { + cdn_list + .iter() + .find_map(|cdn| { + let url = build_url(&cdn.url)?; + fetch_fn(url.as_str()).ok() + }) + .ok_or(Error::CdnListExhausted) +} + +fn download_full_game( + cdn_list: &[CdnInfo], + resources_data: ResourcesData, + proxy: Option<&str>, + path: &str, +) -> Result<(), Error> { + let resources = try_fetch_from_cdn( + cdn_list, + |base| Url::parse(base).ok()?.join(&resources_data.resources).ok(), + |url| Ok(request_resources(url, proxy)?), + )?; + + let base_url = format!( + "{}/", + resources_data.resources_base_path.trim_end_matches('/') + ); + let total_elements = resources.resource.len(); + for (i, resource) in resources.resource.iter().enumerate() { + let expected_md5 = hex::decode(&resource.md5)?; + + let data = download_resource_file( + cdn_list, + &base_url, + &resource.dest, + resource.size, + &expected_md5, + i, + total_elements, + proxy, + )?; + + utils::save_file_safe(path, &resource.dest, &data)?; + } + + Ok(()) +} + +fn download_full_game_chunked( + cdn_list: &[CdnInfo], + resources_data: GameDefaultConfigV2Config, + proxy: Option<&str>, + path: &str, +) -> Result<(), Error> { + let resources = try_fetch_from_cdn( + cdn_list, + |base| Url::parse(base).ok()?.join(&resources_data.index_file).ok(), + |url| Ok(request_chunk_resources(url, proxy)?), + )?; + + let base_url = format!("{}/", resources_data.base_url.trim_end_matches('/')); + let total_elements = resources.resource.len(); + for (i, resource) in resources.resource.iter().enumerate() { + let expected_md5 = hex::decode(&resource.md5)?; + + let data = if let Some(chunk_info) = &resource.chunk_infos { + for _chunk in chunk_info {} + unreachable!("Implement logic for chunk download"); + } else { + download_resource_file( + cdn_list, + &base_url, + &resource.dest, + resource.size, + &expected_md5, + i, + total_elements, + proxy, + )? + }; + + utils::save_file_safe(path, &resource.dest, &data)?; + } + + Ok(()) +} + +fn download_upgrade_game( + _cdn_list: &[CdnInfo], + _resources_data: GameDefaultConfigV2Config, + _proxy: Option<&str>, + _path: &str, +) -> Result<(), Error> { + unreachable!("implement upgrade/patch download"); +} + +fn download_resource_file( + cdn_list: &[CdnInfo], + base_url: &str, + dest: &str, + expected_length: u64, + expected_md5: &[u8], + cnt: usize, + total_elements: usize, + proxy: Option<&str>, +) -> Result, Error> { + try_fetch_from_cdn( + cdn_list, + |base| Url::parse(base).ok()?.join(base_url).ok()?.join(dest).ok(), + |url| { + info!("[{}/{}] Download started: {}", cnt + 1, total_elements, url); + let data = download_cdn_asset_in_mem(url, proxy)?; + verify_download(&data, expected_length, expected_md5)?; + Ok(data) + }, + ) +} diff --git a/wicked-waifus-downloader/wicked-waifus-downloader-cli/src/launcher.rs b/wicked-waifus-downloader/wicked-waifus-downloader-cli/src/launcher.rs new file mode 100644 index 0000000..ae8c028 --- /dev/null +++ b/wicked-waifus-downloader/wicked-waifus-downloader-cli/src/launcher.rs @@ -0,0 +1,46 @@ +use crate::error::Error; +use crate::utils; +use crate::utils::verify_download; +use log::{info, warn}; +use url::Url; +use wicked_waifus_downloader_domain::launcher::LauncherConfig; +use wicked_waifus_downloader_lib::{download_cdn_asset_in_mem, InstallerInfo}; + +pub(crate) fn download_launcher( + launcher_config: LauncherConfig, + proxy: Option<&str>, + path: &str, +) -> Result<(), Error> { + let mut info = InstallerInfo::from(launcher_config); + let expected_md5 = hex::decode(&info.expected_md5)?; + utils::sort_cdn_list(&mut info.cdn_list); + + info.cdn_list.iter().find_map(|cdn| { + let Ok(url) = Url::parse(&cdn.url).and_then(|base| base.join(&info.installer)) else { + warn!("Invalid URL in CDN list: {}", cdn.url); + return None; + }; + + info!("Download started: {}", url); + + let data = match download_cdn_asset_in_mem(url.as_str(), proxy) { + Ok(data) => data, + Err(err) => { + warn!("Download failed from {}: {:?}", url, err); + return None; + } + }; + + if let Err(e) = verify_download(&data, info.expected_length, &expected_md5) { + warn!("Verification failed for {}: {:?}", url, e); + return None; + } + + if let Err(e) = utils::save_file_safe(path, "installer.zip", &data) { + warn!("Failed to save file: {:?}", e); + return None; + } + + Some(()) + }).ok_or(Error::CdnListExhausted) +} diff --git a/wicked-waifus-downloader/wicked-waifus-downloader-cli/src/main.rs b/wicked-waifus-downloader/wicked-waifus-downloader-cli/src/main.rs new file mode 100644 index 0000000..80a7606 --- /dev/null +++ b/wicked-waifus-downloader/wicked-waifus-downloader-cli/src/main.rs @@ -0,0 +1,51 @@ +mod error; +mod game; +mod launcher; +mod utils; + +use crate::game::download_game; +use crate::launcher::download_launcher; +use indicatif::MultiProgress; +use indicatif_log_bridge::LogWrapper; +use log::info; + +fn main() { + let logger = colog::default_builder() + .filter_level(log::LevelFilter::Info) + .build(); + let multi = MultiProgress::new(); + LogWrapper::new(multi.clone(), logger).try_init().unwrap(); + + wicked_waifus_downloader_lib::enable_progress_bar(multi); + + test_launcher_download(); + test_game_download(false, false, true); +} + +fn test_launcher_download() { + let launcher_config = wicked_waifus_downloader_lib::request_launcher( + "https://prod-cn-alicdn-gamestarter.kurogame.com/launcher/launcher/10008_Pa0Q0EMFxukjEqX33pF9Uyvdc8MaGPSz/G152/index.json", + None, + ).unwrap(); + info!("{:#?}", launcher_config); + + download_launcher(launcher_config, None, "test_launcher_dl").unwrap(); +} + +fn test_game_download(use_predownload: bool, use_chunked: bool, full: bool) { + let game_config = wicked_waifus_downloader_lib::request_game( + "https://prod-cn-alicdn-gamestarter.kurogame.com/launcher/game/G152/10008_Pa0Q0EMFxukjEqX33pF9Uyvdc8MaGPSz/index.json", + None, + ).unwrap(); + info!("{:#?}", game_config); + + download_game( + use_predownload, + use_chunked, + full, + game_config, + None, + "test_game_dl", + ) + .unwrap(); +} diff --git a/wicked-waifus-downloader/wicked-waifus-downloader-cli/src/utils.rs b/wicked-waifus-downloader/wicked-waifus-downloader-cli/src/utils.rs new file mode 100644 index 0000000..c481353 --- /dev/null +++ b/wicked-waifus-downloader/wicked-waifus-downloader-cli/src/utils.rs @@ -0,0 +1,48 @@ +use crate::error::Error; +use constant_time_eq::constant_time_eq; +use md5::{Digest, Md5}; +use std::io::Write; +use wicked_waifus_downloader_domain::CdnInfo; + +pub(crate) fn save_file_safe(prefix: &str, filename: &str, data: &[u8]) -> Result { + let root = std::path::Path::new(prefix); + let location = root.join(filename); + let parent = location.parent().unwrap(); + std::fs::create_dir_all(parent)?; + + let mut f = std::fs::OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open(&location)?; + f.write_all(data)?; + f.flush()?; + + Ok(location.to_string_lossy().to_string()) +} + +pub(crate) fn sort_cdn_list(cdn_list: &mut Vec) { + // Highest priority first + // TODO: Implement K1 and K2 sorting + cdn_list.sort_by(|a, b| b.p.cmp(&a.p)); +} + +pub(crate) fn verify_download( + data: &[u8], + expected_length: u64, + expected_md5: &[u8], +) -> Result<(), Error> { + let actual_length = data.len() as u64; + if actual_length != expected_length { + return Err(Error::LengthMismatch(expected_length, actual_length)); + } + let actual_md5 = Md5::digest(data); + let actual_md5_bytes = actual_md5.as_slice(); + match constant_time_eq(expected_md5, actual_md5_bytes) { + true => Ok(()), + false => Err(Error::CrcMismatch( + hex::encode(expected_md5), + hex::encode(actual_md5_bytes), + )), + } +} diff --git a/wicked-waifus-downloader/wicked-waifus-downloader-domain/Cargo.toml b/wicked-waifus-downloader/wicked-waifus-downloader-domain/Cargo.toml new file mode 100644 index 0000000..e8c74fa --- /dev/null +++ b/wicked-waifus-downloader/wicked-waifus-downloader-domain/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "wicked-waifus-downloader-domain" +version.workspace = true +edition.workspace = true + +[dependencies] +serde.workspace = true diff --git a/wicked-waifus-downloader/wicked-waifus-downloader-domain/src/game.rs b/wicked-waifus-downloader/wicked-waifus-downloader-domain/src/game.rs new file mode 100644 index 0000000..2806b77 --- /dev/null +++ b/wicked-waifus-downloader/wicked-waifus-downloader-domain/src/game.rs @@ -0,0 +1,122 @@ +use crate::{CdnInfo, Experiment, LanguageMap}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GameConfig { + pub chunk_download_switch: Option, + pub default: GameDefaultConfig, + pub key_file_check_switch: Option, + pub predownload_switch: Option, + pub predownload: Option, // TODO: Might need changes + #[serde(rename = "RHIOptionSwitch")] + pub rhioption_switch: Option, + #[serde(rename = "RHIOptionList")] + pub rhioption_list: Option>, + pub resources_login: Option, + pub experiment: Option, + pub check_exe_is_running: Option, + pub hash_cache_check_acc_switch: u32, + pub key_file_check_list: Option>, + pub fingerprints: Option>, +} + +/// The order V2 before V1, is because they share a common structure and only V2 has extra fields. +/// If V1 it's first will be included in V2 too +#[derive(Serialize, Deserialize, Debug)] +#[serde(untagged)] +pub enum GameDefaultConfig { + GameDefaultConfigV2(GameDefaultConfigV2), + GameDefaultConfigV1(GameDefaultConfigV1), +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GameDefaultConfigV1 { + pub cdn_list: Vec, + #[serde(flatten)] + pub resources_data: ResourcesData, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GameDefaultConfigV2 { + pub cdn_list: Vec, + #[serde(flatten)] + pub resources_data: ResourcesData, + pub config: GameDefaultConfigV2Config, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ResourcesData { + pub changelog: LanguageMap, + pub resources: String, + pub resources_base_path: String, + pub resources_diff: ResourcesDiff, + pub resources_exclude_path: Vec, + pub resources_exclude_path_need_update: Vec, + pub sample_hash_switch: u32, + pub version: String, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GameDefaultConfigV2Config { + pub index_file_md5: String, + pub un_compress_size: u64, + pub base_url: String, + pub size: u64, + pub patch_type: String, // TODO: Enum + pub index_file: String, + pub version: String, + pub patch_config: Vec, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GameDefaultConfigV2PatchConfig { + pub index_file_md5: String, + pub un_compress_size: u64, + pub ext: GameDefaultConfigV2PatchConfigExt, + pub base_url: String, + pub size: u64, + pub index_file: String, + pub version: String, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GameDefaultConfigV2PatchConfigExt { + pub max_file_size: u64, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ResourcesDiff { + pub current_game_info: GameInfo, + pub previous_game_info: Option, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GameInfo { + pub file_name: String, + pub md5: String, + pub version: String, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RHIOption { + pub cmd_option: String, + pub is_show: u32, + pub text: LanguageMap, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ResourcesLogin { + pub host: String, + pub login_switch: u32, +} diff --git a/wicked-waifus-downloader/wicked-waifus-downloader-domain/src/launcher.rs b/wicked-waifus-downloader/wicked-waifus-downloader-domain/src/launcher.rs new file mode 100644 index 0000000..d80e5e5 --- /dev/null +++ b/wicked-waifus-downloader/wicked-waifus-downloader-domain/src/launcher.rs @@ -0,0 +1,92 @@ +use crate::{CdnInfo, Experiment, LanguageMap}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +#[derive(Serialize, Deserialize, Debug)] +#[serde(untagged)] +pub enum LauncherConfig { + LauncherConfigV1(LauncherConfigV1), + LauncherConfigV2(LauncherConfigV2), +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct LauncherConfigV1 { + pub default: LauncherDefaultConfigV1, + pub crash_init_switch: u32, + pub animate_bg_switch: u32, + pub animate_background: Option, + pub animate_background_language: Option>, + pub navigation_bar_switch: Option, + pub navigation_bar_language: Option, + pub resources_gray: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct LauncherDefaultConfigCommon { + pub cdn_list: Vec, + pub changelog: LanguageMap, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct LauncherDefaultConfigV1 { + #[serde(flatten)] + pub common: LauncherDefaultConfigCommon, + pub installer: String, + #[serde(rename = "installerMD5")] + pub installer_md5: String, + pub installer_size: u64, + pub version: String, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct AnimateBackground { + pub url: String, + pub md5: String, + pub frame_rate: u32, + pub duration_in_second: u32, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct LauncherConfigV2 { + pub default: LauncherDefaultConfigV2, + pub crash_init_switch: u32, + pub navigation_bar_switch: Option, + pub navigation_bar_language: Option, + pub resources_gray: Option, + pub function_code: Option, + pub experiment: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct LauncherDefaultConfigV2 { + #[serde(flatten)] + pub common: LauncherDefaultConfigCommon, + pub resource: LauncherDefaultConfigResource, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct LauncherDefaultConfigResource { + pub file_check_list_md5: String, + pub md5: String, + pub path: String, + pub size: u64, + pub version: String, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct ResourcesGray { + pub gray_url: Option, + pub gray_switch: u32, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct FunctionCode {} diff --git a/wicked-waifus-downloader/wicked-waifus-downloader-domain/src/lib.rs b/wicked-waifus-downloader/wicked-waifus-downloader-domain/src/lib.rs new file mode 100644 index 0000000..99f6110 --- /dev/null +++ b/wicked-waifus-downloader/wicked-waifus-downloader-domain/src/lib.rs @@ -0,0 +1,42 @@ +pub mod launcher; +pub mod game; +pub mod resources; + +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +pub type LanguageMap = HashMap>; + +#[derive(Debug, Serialize, Deserialize)] +pub struct CdnInfo { + #[serde(rename = "K1")] + pub k1: u32, + #[serde(rename = "K2")] + pub k2: u32, + #[serde(rename = "P")] + pub p: u32, + pub url: String, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct Experiment { + pub download: Option, + #[serde(rename = "res_check")] + pub res_check: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct ExperimentDownload { + pub download_read_block_timeout: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct ExperimentResCheck { + pub file_chunk_check_switch: Option, + pub file_size_check_switch: Option, + pub res_valid_check_time_out: Option, + pub file_check_white_list_config: Option, +} \ No newline at end of file diff --git a/wicked-waifus-downloader/wicked-waifus-downloader-domain/src/resources.rs b/wicked-waifus-downloader/wicked-waifus-downloader-domain/src/resources.rs new file mode 100644 index 0000000..983e3f3 --- /dev/null +++ b/wicked-waifus-downloader/wicked-waifus-downloader-domain/src/resources.rs @@ -0,0 +1,47 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Resources { + pub resource: Vec, + pub sample_hash_info: SampleHashInfo, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Resource { + pub dest: String, + pub md5: String, + pub sample_hash: String, + pub size: u64, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SampleHashInfo { + pub sample_num: u32, + pub sample_block_max_size: u32, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ChunkedResources { + pub resource: Vec, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ChunkedResource { + pub dest: String, + pub md5: String, + pub size: u64, + pub chunk_infos: Option>, +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ChunkInfo { + pub start: u64, + pub end: u64, + pub md5: String, +} \ No newline at end of file diff --git a/wicked-waifus-downloader/wicked-waifus-downloader-lib/Cargo.toml b/wicked-waifus-downloader/wicked-waifus-downloader-lib/Cargo.toml new file mode 100644 index 0000000..cdd5828 --- /dev/null +++ b/wicked-waifus-downloader/wicked-waifus-downloader-lib/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "wicked-waifus-downloader-lib" +version.workspace = true +edition.workspace = true + +[dependencies] +indicatif.workspace = true +log.workspace = true +serde.workspace = true +thiserror.workspace = true +ureq.workspace = true +wicked-waifus-downloader-domain.workspace = true \ No newline at end of file diff --git a/wicked-waifus-downloader/wicked-waifus-downloader-lib/src/lib.rs b/wicked-waifus-downloader/wicked-waifus-downloader-lib/src/lib.rs new file mode 100644 index 0000000..927150d --- /dev/null +++ b/wicked-waifus-downloader/wicked-waifus-downloader-lib/src/lib.rs @@ -0,0 +1,165 @@ +use indicatif::{MultiProgress, ProgressBar}; +use log::{info, warn}; +use std::fmt::Write; +use std::sync::OnceLock; +use serde::de::DeserializeOwned; +use thiserror::Error; +use ureq::http::Response; +use ureq::tls::{RootCerts, TlsConfig}; +use ureq::{Agent, Body, Proxy}; +use wicked_waifus_downloader_domain::game::GameConfig; +use wicked_waifus_downloader_domain::launcher::LauncherConfig; +use wicked_waifus_downloader_domain::resources::{ChunkedResources, Resources}; +use wicked_waifus_downloader_domain::CdnInfo; + +#[derive(Error, Debug)] +pub enum Error { + #[error("UREQ Error {0}")] + Ureq(#[from] ureq::Error), + #[error("ToStr Error {0}")] + ToStr(#[from] ureq::http::header::ToStrError), + #[error("ParseInt Error {0}")] + ParseInt(#[from] std::num::ParseIntError), + #[error("Io Error {0}")] + Io(#[from] std::io::Error), + #[error("Content-Length header missing")] + ContentLengthHeaderMissing, +} + +pub struct InstallerInfo { + pub cdn_list: Vec, + pub installer: String, + pub expected_md5: String, + pub expected_length: u64, +} + +impl From for InstallerInfo { + fn from(config: LauncherConfig) -> Self { + match config { + LauncherConfig::LauncherConfigV1(cfg) => { + let default = cfg.default; + let common = default.common; + Self { + cdn_list: common.cdn_list, + installer: default.installer, + expected_md5: default.installer_md5, + expected_length: default.installer_size, + } + } + LauncherConfig::LauncherConfigV2(cfg) => { + let default = cfg.default; + let common = default.common; + let resource = default.resource; + Self { + cdn_list: common.cdn_list, + installer: resource.path, + expected_md5: resource.md5, + expected_length: resource.size, + } + } + } + } +} + +static MULTI_PROGRESS_BAR: OnceLock = OnceLock::new(); + +pub fn enable_progress_bar(multi: MultiProgress) { + if MULTI_PROGRESS_BAR.set(multi).is_err() { + warn!("MultiProgress already set, ignoring."); + } +} + +macro_rules! define_request_fn { + ($name:ident, $ty:ty) => { + pub fn $name(url: &str, proxy: Option<&str>) -> Result<$ty, Error> { + request_json::<$ty>(url, proxy) + } + }; +} + +define_request_fn!(request_launcher, LauncherConfig); +define_request_fn!(request_game, GameConfig); +define_request_fn!(request_resources, Resources); +define_request_fn!(request_chunk_resources, ChunkedResources); + +pub fn download_cdn_asset_in_mem(url: &str, proxy: Option<&str>) -> Result, Error> { + let (mut res, len) = prepare_download(url, proxy)?; + let mut bytes = Vec::with_capacity(len); + download_internal(url, &mut res, len as u64, &mut bytes)?; + Ok(bytes) +} + +pub fn download_cdn_asset_writer( + url: &str, + proxy: Option<&str>, + writer: &mut W, +) -> Result<(), Error> { + let (mut res, len) = prepare_download(url, proxy)?; + download_internal(url, &mut res, len as u64, writer) +} + +fn get_agent(proxy: Option<&str>) -> Result { + Ok(Agent::config_builder() + .tls_config( + TlsConfig::builder() + .root_certs(RootCerts::PlatformVerifier) + .build(), + ) + .proxy(proxy.map(Proxy::new).transpose()?) + .build() + .new_agent()) +} + +#[inline(always)] +fn request_json(url: &str, proxy: Option<&str>) -> Result { + Ok(get_agent(proxy)? + .get(url) + .call()? + .body_mut() + .read_json::()?) +} + +fn prepare_download(url: &str, proxy: Option<&str>) -> Result<(Response, usize), Error> { + let res = get_agent(proxy)?.get(url).call()?; + let content_length = res + .headers() + .get("Content-Length") + .ok_or(Error::ContentLengthHeaderMissing)? + .to_str()? + .parse::()?; + Ok((res, content_length)) +} + +fn create_progress_bar(len: u64, multi: &MultiProgress) -> ProgressBar { + let pb = multi.add(ProgressBar::new(len)); + pb.set_style( + indicatif::ProgressStyle::with_template( + "{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({eta})" + ) + .unwrap() + .with_key("eta", |state: &indicatif::ProgressState, w: &mut dyn Write| { + write!(w, "{:.1}s", state.eta().as_secs_f64()).unwrap(); + }) + .progress_chars("#>-") + ); + pb +} + +fn download_internal( + url: &str, + res: &mut Response, + len: u64, + writer: &mut W, +) -> Result<(), Error> { + if let Some(multi) = MULTI_PROGRESS_BAR.get() { + let pb = create_progress_bar(len, multi); + std::io::copy(&mut pb.wrap_read(res.body_mut().as_reader()), writer)?; + pb.finish(); + multi.remove(&pb); + } else { + std::io::copy(&mut res.body_mut().as_reader(), writer)?; + } + + info!("Download completed: {}", url); + Ok(()) +}