Compare commits
19 Commits
0e72675965
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
3f91386763
|
|||
|
560562cc25
|
|||
|
f404f597c1
|
|||
|
b3126d1798
|
|||
|
bdd5d849eb
|
|||
|
ddf76602be
|
|||
|
4a152f07da
|
|||
|
afc3f68f36
|
|||
|
fcc65f68bb
|
|||
|
03a127f24b
|
|||
|
ed9e23ed3b
|
|||
|
3949723303
|
|||
|
281327d69c
|
|||
|
9e3bdeacc6
|
|||
|
aacc9795d9
|
|||
|
22c27a2491
|
|||
|
132f784d58
|
|||
|
9df1ec77ef
|
|||
|
e0fd8aa8ea
|
608
Cargo.lock
generated
608
Cargo.lock
generated
@@ -8,6 +8,17 @@ version = "2.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aes"
|
||||||
|
version = "0.8.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cipher",
|
||||||
|
"cpufeatures",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "1.1.4"
|
version = "1.1.4"
|
||||||
@@ -97,21 +108,6 @@ version = "0.22.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bit-set"
|
|
||||||
version = "0.6.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f0481a0e032742109b1133a095184ee93d88f3dc9e0d28a5d033dc77a073f44f"
|
|
||||||
dependencies = [
|
|
||||||
"bit-vec",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bit-vec"
|
|
||||||
version = "0.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d2c54ff287cfc0a34f38a6b832ea1bd8e448a330b3e40a50859e6488bee07f22"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.11.0"
|
version = "2.11.0"
|
||||||
@@ -127,6 +123,15 @@ dependencies = [
|
|||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-padding"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bstr"
|
name = "bstr"
|
||||||
version = "1.12.1"
|
version = "1.12.1"
|
||||||
@@ -143,18 +148,30 @@ version = "3.20.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
|
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "byteorder"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.11.1"
|
version = "1.11.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
|
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bzip2"
|
||||||
|
version = "0.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3a53fac24f34a81bc9954b5d6cfce0c21e18ec6959f44f56e8e90e4bb7c346c"
|
||||||
|
dependencies = [
|
||||||
|
"libbz2-rs-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cbc"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6"
|
||||||
|
dependencies = [
|
||||||
|
"cipher",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.56"
|
version = "1.2.56"
|
||||||
@@ -162,6 +179,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2"
|
checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"find-msvc-tools",
|
"find-msvc-tools",
|
||||||
|
"jobserver",
|
||||||
|
"libc",
|
||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -172,12 +191,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chrono"
|
name = "cipher"
|
||||||
version = "0.4.44"
|
version = "0.4.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
|
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-traits",
|
"crypto-common",
|
||||||
|
"inout",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -241,11 +261,17 @@ version = "0.1.16"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
|
checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom 0.2.17",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"tiny-keccak",
|
"tiny-keccak",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "constant_time_eq"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cookie"
|
name = "cookie"
|
||||||
version = "0.18.1"
|
version = "0.18.1"
|
||||||
@@ -284,21 +310,6 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crc"
|
|
||||||
version = "3.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d"
|
|
||||||
dependencies = [
|
|
||||||
"crc-catalog",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crc-catalog"
|
|
||||||
version = "2.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crc32fast"
|
name = "crc32fast"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@@ -349,6 +360,12 @@ dependencies = [
|
|||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deflate64"
|
||||||
|
version = "0.1.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "807800ff3288b621186fe0a8f3392c4652068257302709c24efd918c3dffcdc2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deranged"
|
name = "deranged"
|
||||||
version = "0.5.8"
|
version = "0.5.8"
|
||||||
@@ -366,6 +383,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"block-buffer",
|
"block-buffer",
|
||||||
"crypto-common",
|
"crypto-common",
|
||||||
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -474,28 +492,6 @@ dependencies = [
|
|||||||
"unicase",
|
"unicase",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "filetime"
|
|
||||||
version = "0.2.27"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
"libredox",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "filetime_creation"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c25b5d475550e559de5b0c0084761c65325444e3b6c9e298af9cefe7a9ef3a5f"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"filetime",
|
|
||||||
"windows-sys 0.52.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "find-msvc-tools"
|
name = "find-msvc-tools"
|
||||||
version = "0.1.9"
|
version = "0.1.9"
|
||||||
@@ -516,6 +512,7 @@ checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"crc32fast",
|
"crc32fast",
|
||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
|
"zlib-rs",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -542,12 +539,14 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"quick-xml",
|
"quick-xml",
|
||||||
"serde",
|
"serde",
|
||||||
"sevenz-rust",
|
"sevenz-rust2",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"toml",
|
"toml",
|
||||||
|
"unrar",
|
||||||
"ureq",
|
"ureq",
|
||||||
"url",
|
"url",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
|
"zip",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -580,6 +579,33 @@ dependencies = [
|
|||||||
"wasi",
|
"wasi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"r-efi 5.3.0",
|
||||||
|
"wasip2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"js-sys",
|
||||||
|
"libc",
|
||||||
|
"r-efi 6.0.0",
|
||||||
|
"wasip2",
|
||||||
|
"wasip3",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "globset"
|
name = "globset"
|
||||||
version = "0.4.18"
|
version = "0.4.18"
|
||||||
@@ -634,6 +660,15 @@ version = "0.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hmac"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
@@ -731,6 +766,12 @@ dependencies = [
|
|||||||
"zerovec",
|
"zerovec",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "id-arena"
|
||||||
|
version = "2.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@@ -760,6 +801,18 @@ checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown 0.16.1",
|
"hashbrown 0.16.1",
|
||||||
|
"serde",
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inout"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
|
||||||
|
dependencies = [
|
||||||
|
"block-padding",
|
||||||
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -798,6 +851,16 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jobserver"
|
||||||
|
version = "0.1.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.3.4",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.91"
|
version = "0.3.91"
|
||||||
@@ -817,6 +880,18 @@ dependencies = [
|
|||||||
"pest",
|
"pest",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "leb128fmt"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libbz2-rs-sys"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.182"
|
version = "0.2.182"
|
||||||
@@ -866,10 +941,7 @@ version = "0.1.14"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a"
|
checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
|
||||||
"libc",
|
"libc",
|
||||||
"plain",
|
|
||||||
"redox_syscall",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -904,12 +976,12 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lzma-rust"
|
name = "lzma-rust2"
|
||||||
version = "0.1.7"
|
version = "0.16.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5baab2bbbd7d75a144d671e9ff79270e903957d92fb7386fd39034c709bd2661"
|
checksum = "47bb1e988e6fb779cf720ad431242d3f03167c1b3f2b1aae7f1a94b2495b36ae"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"sha2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -937,16 +1009,6 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "nt-time"
|
|
||||||
version = "0.8.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2de419e64947cd8830e66beb584acc3fb42ed411d103e3c794dda355d1b374b5"
|
|
||||||
dependencies = [
|
|
||||||
"chrono",
|
|
||||||
"time",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-conv"
|
name = "num-conv"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@@ -999,6 +1061,16 @@ dependencies = [
|
|||||||
"hashbrown 0.14.5",
|
"hashbrown 0.14.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pbkdf2"
|
||||||
|
version = "0.12.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
"hmac",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.3.2"
|
version = "2.3.2"
|
||||||
@@ -1028,10 +1100,10 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "plain"
|
name = "pkg-config"
|
||||||
version = "0.2.3"
|
version = "0.3.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
|
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "portable-atomic"
|
name = "portable-atomic"
|
||||||
@@ -1063,6 +1135,22 @@ version = "0.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppmd-rust"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "efca4c95a19a79d1c98f791f10aebd5c1363b473244630bb7dbde1dc98455a24"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prettyplease"
|
||||||
|
version = "0.2.37"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.106"
|
version = "1.0.106"
|
||||||
@@ -1091,6 +1179,18 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "r-efi"
|
||||||
|
version = "5.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "r-efi"
|
||||||
|
version = "6.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rayon"
|
name = "rayon"
|
||||||
version = "1.11.0"
|
version = "1.11.0"
|
||||||
@@ -1111,22 +1211,13 @@ dependencies = [
|
|||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "redox_syscall"
|
|
||||||
version = "0.7.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_users"
|
name = "redox_users"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
|
checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom 0.2.17",
|
||||||
"libredox",
|
"libredox",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
@@ -1178,7 +1269,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"getrandom",
|
"getrandom 0.2.17",
|
||||||
"libc",
|
"libc",
|
||||||
"untrusted",
|
"untrusted",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
@@ -1274,6 +1365,12 @@ dependencies = [
|
|||||||
"hashlink",
|
"hashlink",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "1.0.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.228"
|
version = "1.0.228"
|
||||||
@@ -1327,22 +1424,34 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sevenz-rust"
|
name = "sevenz-rust2"
|
||||||
version = "0.6.1"
|
version = "0.20.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "26482cf1ecce4540dc782fc70019eba89ffc4d87b3717eb5ec524b5db6fdefef"
|
checksum = "29225600349ef74beda5a9fffb36ac660a24613c0bde9315d0c49be1d51e9c24"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bit-set",
|
"aes",
|
||||||
"byteorder",
|
"bzip2",
|
||||||
"crc",
|
"cbc",
|
||||||
"filetime_creation",
|
"crc32fast",
|
||||||
|
"getrandom 0.4.2",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"lzma-rust",
|
"lzma-rust2",
|
||||||
"nt-time",
|
"ppmd-rust",
|
||||||
"sha2",
|
"sha2",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha1"
|
||||||
|
version = "0.10.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha2"
|
name = "sha2"
|
||||||
version = "0.10.9"
|
version = "0.10.9"
|
||||||
@@ -1440,6 +1549,7 @@ checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"deranged",
|
"deranged",
|
||||||
"itoa",
|
"itoa",
|
||||||
|
"js-sys",
|
||||||
"num-conv",
|
"num-conv",
|
||||||
"powerfmt",
|
"powerfmt",
|
||||||
"serde_core",
|
"serde_core",
|
||||||
@@ -1521,6 +1631,12 @@ version = "1.0.6+spec-1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607"
|
checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typed-path"
|
||||||
|
version = "0.12.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.19.0"
|
version = "1.19.0"
|
||||||
@@ -1545,6 +1661,35 @@ version = "1.0.24"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unrar"
|
||||||
|
version = "0.5.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "92ec61343a630d2b50d13216dea5125e157d3fc180a7d3f447d22fe146b648fc"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"regex",
|
||||||
|
"unrar_sys",
|
||||||
|
"widestring",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unrar_sys"
|
||||||
|
version = "0.5.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b77675b883cfbe6bf41e6b7a5cd6008e0a83ba497de3d96e41a064bbeead765"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "untrusted"
|
name = "untrusted"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
@@ -1635,6 +1780,24 @@ version = "0.11.1+wasi-snapshot-preview1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasip2"
|
||||||
|
version = "1.0.2+wasi-0.2.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
|
||||||
|
dependencies = [
|
||||||
|
"wit-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasip3"
|
||||||
|
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
|
||||||
|
dependencies = [
|
||||||
|
"wit-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.114"
|
version = "0.2.114"
|
||||||
@@ -1680,6 +1843,40 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-encoder"
|
||||||
|
version = "0.244.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
|
||||||
|
dependencies = [
|
||||||
|
"leb128fmt",
|
||||||
|
"wasmparser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-metadata"
|
||||||
|
version = "0.244.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"indexmap",
|
||||||
|
"wasm-encoder",
|
||||||
|
"wasmparser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasmparser"
|
||||||
|
version = "0.244.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"hashbrown 0.15.5",
|
||||||
|
"indexmap",
|
||||||
|
"semver",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webpki-roots"
|
name = "webpki-roots"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
@@ -1689,6 +1886,28 @@ dependencies = [
|
|||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "widestring"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471"
|
||||||
|
|
||||||
|
[[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]]
|
[[package]]
|
||||||
name = "winapi-util"
|
name = "winapi-util"
|
||||||
version = "0.1.11"
|
version = "0.1.11"
|
||||||
@@ -1698,6 +1917,12 @@ dependencies = [
|
|||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[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]]
|
[[package]]
|
||||||
name = "windows-link"
|
name = "windows-link"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@@ -1801,6 +2026,94 @@ version = "0.7.14"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
|
checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen"
|
||||||
|
version = "0.51.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
|
||||||
|
dependencies = [
|
||||||
|
"wit-bindgen-rust-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen-core"
|
||||||
|
version = "0.51.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"heck",
|
||||||
|
"wit-parser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen-rust"
|
||||||
|
version = "0.51.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"heck",
|
||||||
|
"indexmap",
|
||||||
|
"prettyplease",
|
||||||
|
"syn",
|
||||||
|
"wasm-metadata",
|
||||||
|
"wit-bindgen-core",
|
||||||
|
"wit-component",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-bindgen-rust-macro"
|
||||||
|
version = "0.51.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"prettyplease",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wit-bindgen-core",
|
||||||
|
"wit-bindgen-rust",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-component"
|
||||||
|
version = "0.244.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"bitflags",
|
||||||
|
"indexmap",
|
||||||
|
"log",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"wasm-encoder",
|
||||||
|
"wasm-metadata",
|
||||||
|
"wasmparser",
|
||||||
|
"wit-parser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wit-parser"
|
||||||
|
version = "0.244.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"id-arena",
|
||||||
|
"indexmap",
|
||||||
|
"log",
|
||||||
|
"semver",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"unicode-xid",
|
||||||
|
"wasmparser",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "writeable"
|
name = "writeable"
|
||||||
version = "0.6.2"
|
version = "0.6.2"
|
||||||
@@ -1856,6 +2169,20 @@ name = "zeroize"
|
|||||||
version = "1.8.2"
|
version = "1.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
|
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
|
||||||
|
dependencies = [
|
||||||
|
"zeroize_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeroize_derive"
|
||||||
|
version = "1.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerotrie"
|
name = "zerotrie"
|
||||||
@@ -1890,8 +2217,81 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zip"
|
||||||
|
version = "8.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b680f2a0cd479b4cff6e1233c483fdead418106eae419dc60200ae9850f6d004"
|
||||||
|
dependencies = [
|
||||||
|
"aes",
|
||||||
|
"bzip2",
|
||||||
|
"constant_time_eq",
|
||||||
|
"crc32fast",
|
||||||
|
"deflate64",
|
||||||
|
"flate2",
|
||||||
|
"getrandom 0.4.2",
|
||||||
|
"hmac",
|
||||||
|
"indexmap",
|
||||||
|
"lzma-rust2",
|
||||||
|
"memchr",
|
||||||
|
"pbkdf2",
|
||||||
|
"ppmd-rust",
|
||||||
|
"sha1",
|
||||||
|
"time",
|
||||||
|
"typed-path",
|
||||||
|
"zeroize",
|
||||||
|
"zopfli",
|
||||||
|
"zstd",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zlib-rs"
|
||||||
|
version = "0.6.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3be3d40e40a133f9c916ee3f9f4fa2d9d63435b5fbe1bfc6d9dae0aa0ada1513"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zmij"
|
name = "zmij"
|
||||||
version = "1.0.21"
|
version = "1.0.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zopfli"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"crc32fast",
|
||||||
|
"log",
|
||||||
|
"simd-adler32",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zstd"
|
||||||
|
version = "0.13.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"
|
||||||
|
dependencies = [
|
||||||
|
"zstd-safe",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zstd-safe"
|
||||||
|
version = "7.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d"
|
||||||
|
dependencies = [
|
||||||
|
"zstd-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zstd-sys"
|
||||||
|
version = "2.0.16+zstd.1.5.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|||||||
@@ -12,9 +12,11 @@ libloot = "0.29.0"
|
|||||||
log = "0.4.29"
|
log = "0.4.29"
|
||||||
quick-xml = { version = "0.39.2", features = ["serde-types", "serialize"] }
|
quick-xml = { version = "0.39.2", features = ["serde-types", "serialize"] }
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
sevenz-rust = { version = "0.6.1", default-features = false }
|
sevenz-rust2 = { version = "0.20.2" }
|
||||||
thiserror = "2.0.18"
|
thiserror = "2.0.18"
|
||||||
toml = "1.0.3"
|
toml = "1.0.3"
|
||||||
|
unrar = "0.5.8"
|
||||||
ureq = { version = "3.2.0", features = ["json"] }
|
ureq = { version = "3.2.0", features = ["json"] }
|
||||||
url = "2.5.8"
|
url = "2.5.8"
|
||||||
walkdir = "2.5.0"
|
walkdir = "2.5.0"
|
||||||
|
zip = "8.2.0"
|
||||||
|
|||||||
11
src/actions.rs
Normal file
11
src/actions.rs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
mod activate;
|
||||||
|
mod download;
|
||||||
|
mod include;
|
||||||
|
mod install;
|
||||||
|
mod load_order;
|
||||||
|
|
||||||
|
pub use activate::{ActivationError, activate_instance};
|
||||||
|
pub use download::handle_nxm;
|
||||||
|
pub use include::insert_mod_to_instance;
|
||||||
|
pub use install::resolve_files_for_install;
|
||||||
|
pub use load_order::{LoadOrderError, create_loadorder};
|
||||||
@@ -7,6 +7,7 @@ use std::io::Write;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::{fs, io, os::unix, path::Path};
|
use std::{fs, io, os::unix, path::Path};
|
||||||
|
|
||||||
|
/// Create the symlinks for a instance in a given directory
|
||||||
pub fn activate_instance(
|
pub fn activate_instance(
|
||||||
root_config: &RootConfig,
|
root_config: &RootConfig,
|
||||||
instance: &ModdedInstance,
|
instance: &ModdedInstance,
|
||||||
@@ -18,7 +19,7 @@ pub fn activate_instance(
|
|||||||
|
|
||||||
check_target_valid(&target)?;
|
check_target_valid(&target)?;
|
||||||
|
|
||||||
let resolved_links = resolve_links(root_config, instance, game)?;
|
let resolved_links = resolve_links(root_config, instance, &game)?;
|
||||||
|
|
||||||
resolved_links
|
resolved_links
|
||||||
.iter()
|
.iter()
|
||||||
44
src/actions/download.rs
Normal file
44
src/actions/download.rs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
use anyhow::anyhow;
|
||||||
|
use log::error;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
nexus::{NXMUrl, download_nxm},
|
||||||
|
types::{ModConfig, RootConfig},
|
||||||
|
unpacker::unpack,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Handles a nexus mod url. Downloads, unpacks and adds the mod to the config
|
||||||
|
pub fn handle_nxm(root_config: &mut RootConfig, raw_url: &str) -> anyhow::Result<()> {
|
||||||
|
let Some(dl_location) = root_config.download_location() else {
|
||||||
|
return Err(anyhow!("No download location set"));
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(api_key) = root_config.nexus_api_key() else {
|
||||||
|
return Err(anyhow!("No API key provided"));
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(nxm_url) = NXMUrl::parse_url(raw_url) else {
|
||||||
|
return Err(anyhow!("Failed to parse URL"));
|
||||||
|
};
|
||||||
|
|
||||||
|
let (dl_file, mod_info) = download_nxm(api_key, &nxm_url, dl_location)?;
|
||||||
|
|
||||||
|
let mod_id = format!("{}-{}", mod_info.generate_id(), nxm_url.file);
|
||||||
|
if root_config.game_by_id(&mod_id).is_some() {
|
||||||
|
error!(
|
||||||
|
"Generated mod id already exists. Pleas install downloaded mod manually. Downloaded at {}",
|
||||||
|
&dl_file.to_string_lossy()
|
||||||
|
);
|
||||||
|
return Err(anyhow!("Mod with generated id already exists"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let extract_to = root_config.mod_location().join(&mod_id);
|
||||||
|
unpack(dl_file, extract_to)?;
|
||||||
|
|
||||||
|
let file_id: u64 = nxm_url.file.parse()?;
|
||||||
|
let new_mod = ModConfig::from_mod_info(&mod_id, &mod_id, &mod_info, file_id);
|
||||||
|
|
||||||
|
root_config.add_mod(&new_mod);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
70
src/actions/include.rs
Normal file
70
src/actions/include.rs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
use log::warn;
|
||||||
|
use std::{collections::HashMap, path::PathBuf};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
file_conflict_solver::ConflictSolver,
|
||||||
|
types::{InstalledMod, ModConfig, ModFile, ModdedInstance},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn insert_mod_to_instance(
|
||||||
|
instance: &mut ModdedInstance,
|
||||||
|
from_mod: &ModConfig,
|
||||||
|
files_to_add: &[ModFile],
|
||||||
|
priority: isize,
|
||||||
|
) -> Option<FileConflict> {
|
||||||
|
let mut solver = ConflictSolver::new();
|
||||||
|
|
||||||
|
let mut installed_files: Vec<(ModFile, &InstalledMod)> = Vec::new();
|
||||||
|
for installed_mod in instance.mods() {
|
||||||
|
for link in installed_mod.files() {
|
||||||
|
let recreated_mod_file = ModFile::new(link.src(), link.dst(), 0);
|
||||||
|
installed_files.push((recreated_mod_file, installed_mod));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (file, from_mod) in &installed_files {
|
||||||
|
if let Some(conflict) = solver.add_file(file, from_mod) {
|
||||||
|
warn!("File conflict on already added file: {:?}", conflict);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_mod = InstalledMod::new(from_mod.id(), priority);
|
||||||
|
for file in files_to_add {
|
||||||
|
if let Some(conflict) = solver.add_file(file, &new_mod) {
|
||||||
|
return Some(FileConflict {
|
||||||
|
lhs_mod_id: conflict.lhs_mod.mod_id().to_owned(),
|
||||||
|
rhs_mod_id: conflict.rhs_mod.mod_id().to_owned(),
|
||||||
|
path: conflict.rhs_file.dst().to_owned(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_link_tree = solver.export_files();
|
||||||
|
|
||||||
|
let mut map: HashMap<String, InstalledMod> = HashMap::new();
|
||||||
|
|
||||||
|
for (file, from_mod) in new_link_tree {
|
||||||
|
match map.get_mut(from_mod.mod_id()) {
|
||||||
|
Some(existing) => {
|
||||||
|
existing.add_file(file);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let mut new_mod = InstalledMod::new(from_mod.mod_id(), from_mod.priority());
|
||||||
|
new_mod.add_file(file);
|
||||||
|
map.insert(new_mod.mod_id().to_owned(), new_mod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (_, installed_mod) in map {
|
||||||
|
instance.update_or_create_mod(&installed_mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FileConflict {
|
||||||
|
pub lhs_mod_id: String,
|
||||||
|
pub rhs_mod_id: String,
|
||||||
|
pub path: PathBuf,
|
||||||
|
}
|
||||||
@@ -1,79 +1,19 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
|
||||||
io,
|
io,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
use globset::{Glob, GlobSet, GlobSetBuilder};
|
use globset::{Glob, GlobSet, GlobSetBuilder};
|
||||||
use log::{debug, trace, warn};
|
use log::{debug, trace};
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
file_conflict_solver::ConflictSolver,
|
|
||||||
fomod, install_prompt,
|
fomod, install_prompt,
|
||||||
mod_config_installer::run_fomod_installer,
|
mod_config_installer::run_fomod_installer,
|
||||||
types::{InstalledMod, ModConfig, ModFile, ModdedInstance, RootConfig},
|
types::{ModConfig, ModFile, ModdedInstance, RootConfig},
|
||||||
utils::{resolve_case_insensitive, walk_all_files},
|
utils::{resolve_case_insensitive, walk_all_files},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn insert_mod_to_instance(
|
pub fn resolve_files_for_install(
|
||||||
instance: &mut ModdedInstance,
|
|
||||||
from_mod: &ModConfig,
|
|
||||||
files: &[ModFile],
|
|
||||||
priority: isize,
|
|
||||||
) -> Result<(), InststanceError> {
|
|
||||||
let mut solver = ConflictSolver::new();
|
|
||||||
|
|
||||||
let mut installed_files: Vec<(ModFile, &InstalledMod)> = Vec::new();
|
|
||||||
for installed_mod in instance.mods() {
|
|
||||||
for link in installed_mod.files() {
|
|
||||||
let recreated_mod_file = ModFile::new(link.src(), link.dst(), 0);
|
|
||||||
installed_files.push((recreated_mod_file, installed_mod));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (file, from_mod) in &installed_files {
|
|
||||||
if let Some(conflict) = solver.add_file(file, from_mod) {
|
|
||||||
warn!("File conflict on already added file: {:?}", conflict);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let new_mod = InstalledMod::new(from_mod.id(), priority);
|
|
||||||
for file in files {
|
|
||||||
if let Some(conflict) = solver.add_file(file, &new_mod) {
|
|
||||||
return Err(InststanceError::FileConflict {
|
|
||||||
lhs_mod_id: conflict.lhs_mod.mod_id().to_owned(),
|
|
||||||
rhs_mod_id: conflict.rhs_mod.mod_id().to_owned(),
|
|
||||||
path: conflict.rhs_file.dst().to_owned(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let new_link_tree = solver.export_files();
|
|
||||||
|
|
||||||
let mut map: HashMap<String, InstalledMod> = HashMap::new();
|
|
||||||
|
|
||||||
for (file, from_mod) in new_link_tree {
|
|
||||||
match map.get_mut(from_mod.mod_id()) {
|
|
||||||
Some(existing) => {
|
|
||||||
existing.add_file(file);
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
let mut new_mod = InstalledMod::new(from_mod.mod_id(), from_mod.priority());
|
|
||||||
new_mod.add_file(file);
|
|
||||||
map.insert(new_mod.mod_id().to_owned(), new_mod);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (_, installed_mod) in map {
|
|
||||||
instance.update_or_create_mod(&installed_mod);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn files_to_install_mod(
|
|
||||||
root_config: &RootConfig,
|
root_config: &RootConfig,
|
||||||
instance: &ModdedInstance,
|
instance: &ModdedInstance,
|
||||||
mod_to_install: &ModConfig,
|
mod_to_install: &ModConfig,
|
||||||
@@ -126,14 +66,11 @@ fn install_fomod(
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
trace!("Current loded plugins: {:?}", active_plugins);
|
trace!("Current loded plugins: {:?}", active_plugins);
|
||||||
let files = run_fomod_installer(module_config, &active_plugins, install_prompt::prompt)
|
let files = run_fomod_installer(module_config, &active_plugins, install_prompt::prompt)?;
|
||||||
.map_err(|_| InststanceError::FomodRunInstaller)?;
|
|
||||||
|
|
||||||
let mod_files: Vec<_> = files
|
let mod_files: Vec<_> = files
|
||||||
.iter()
|
.iter()
|
||||||
.map(|f| {
|
.map(|f| ModFile::from_installer(f.clone(), &mod_root))
|
||||||
ModFile::from_installer(f.clone(), &mod_root).map_err(InststanceError::FomodFinalize)
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<_>, _>>()?
|
.collect::<Result<Vec<_>, _>>()?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
@@ -223,22 +160,7 @@ fn should_be_included(path: impl AsRef<Path>) -> bool {
|
|||||||
| "ilstrings"
|
| "ilstrings"
|
||||||
| "dlstrings"
|
| "dlstrings"
|
||||||
| "dll"
|
| "dll"
|
||||||
|
| "swf"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
pub enum InststanceError {
|
|
||||||
#[error("Two mods write the same file")]
|
|
||||||
FileConflict {
|
|
||||||
lhs_mod_id: String,
|
|
||||||
rhs_mod_id: String,
|
|
||||||
path: PathBuf,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("Failed to run fomod installer")]
|
|
||||||
FomodRunInstaller,
|
|
||||||
|
|
||||||
#[error("Failed to handle results of fomod installer")]
|
|
||||||
FomodFinalize(io::Error),
|
|
||||||
}
|
|
||||||
@@ -10,7 +10,10 @@ use std::{
|
|||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use crate::{types::{self, ModdedInstance, RootConfig}, utils::is_plugin_file};
|
use crate::{
|
||||||
|
types::{self, ModdedInstance, RootConfig},
|
||||||
|
utils::is_plugin_file,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn create_loadorder(
|
pub fn create_loadorder(
|
||||||
root_config: &RootConfig,
|
root_config: &RootConfig,
|
||||||
@@ -15,9 +15,8 @@ pub struct Args {
|
|||||||
#[derive(Subcommand, Debug)]
|
#[derive(Subcommand, Debug)]
|
||||||
pub enum Commands {
|
pub enum Commands {
|
||||||
Activate { instance: String, target: PathBuf },
|
Activate { instance: String, target: PathBuf },
|
||||||
Add { instance: String, mod_id: String },
|
Include { instance: String, mod_id: String },
|
||||||
LoadOrder { instance: String },
|
LoadOrder { instance: String },
|
||||||
ApiCheck,
|
ApiCheck,
|
||||||
Download { url: String },
|
Download { url: String },
|
||||||
Unpack { id: String, path: String },
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ pub enum PluginTypeDescriptorEnum {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct DependencyPluginType {
|
pub struct DependencyPluginType {
|
||||||
|
#[serde(rename = "defaultType")]
|
||||||
pub default_type: PluginType,
|
pub default_type: PluginType,
|
||||||
pub patterns: DependencyPatternList,
|
pub patterns: DependencyPatternList,
|
||||||
}
|
}
|
||||||
@@ -129,7 +130,7 @@ pub struct DependencyPatternList {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct DependencyPattern {
|
pub struct DependencyPattern {
|
||||||
pub dependencies: CompositeDependency,
|
pub dependencies: Vec<CompositeDependency>,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub typ: PluginType,
|
pub typ: PluginType,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
pub mod activator;
|
|
||||||
pub mod cli;
|
pub mod cli;
|
||||||
pub mod file_conflict_solver;
|
pub mod file_conflict_solver;
|
||||||
pub mod fomod;
|
pub mod fomod;
|
||||||
pub mod install_prompt;
|
pub mod install_prompt;
|
||||||
pub mod instance;
|
|
||||||
pub mod load_order;
|
|
||||||
pub mod mod_config_installer;
|
pub mod mod_config_installer;
|
||||||
pub mod nexus;
|
pub mod nexus;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
pub mod unpacker;
|
pub mod unpacker;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
pub mod actions;
|
||||||
|
|||||||
94
src/main.rs
94
src/main.rs
@@ -4,13 +4,13 @@ use log::{debug, error, info};
|
|||||||
use std::{error::Error, path::Path};
|
use std::{error::Error, path::Path};
|
||||||
|
|
||||||
use fomod_manager::{
|
use fomod_manager::{
|
||||||
activator::activate_instance,
|
actions::{
|
||||||
|
activate_instance, create_loadorder, handle_nxm, insert_mod_to_instance,
|
||||||
|
resolve_files_for_install,
|
||||||
|
},
|
||||||
cli::{self, Args},
|
cli::{self, Args},
|
||||||
instance::{self, files_to_install_mod, insert_mod_to_instance},
|
nexus::NexusAPI,
|
||||||
load_order,
|
|
||||||
nexus::{NexusAPI, download_nxm},
|
|
||||||
types::RootConfig,
|
types::RootConfig,
|
||||||
unpacker::unpack,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fn command_activate(
|
fn command_activate(
|
||||||
@@ -29,40 +29,23 @@ fn command_add(root_config: &RootConfig, instance_id: &str, mod_id: &str) -> any
|
|||||||
.mod_by_id(mod_id)
|
.mod_by_id(mod_id)
|
||||||
.ok_or(anyhow!("Can't find mod in config"))?;
|
.ok_or(anyhow!("Can't find mod in config"))?;
|
||||||
|
|
||||||
let files = files_to_install_mod(root_config, &instance, &mod_to_install)?;
|
let files = resolve_files_for_install(root_config, &instance, &mod_to_install)?;
|
||||||
|
|
||||||
match insert_mod_to_instance(&mut instance, &mod_to_install, &files, 0) {
|
match insert_mod_to_instance(&mut instance, &mod_to_install, &files, 0) {
|
||||||
Ok(_) => {
|
None => {
|
||||||
instance.save_to_file()?;
|
instance.save_to_file()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Some(conflict) => {
|
||||||
match &err {
|
|
||||||
instance::InststanceError::FileConflict {
|
|
||||||
lhs_mod_id,
|
|
||||||
rhs_mod_id,
|
|
||||||
path,
|
|
||||||
} => {
|
|
||||||
error!(
|
error!(
|
||||||
"File conflict between {} and {} at {}",
|
"File conflict between {} and {} at {}",
|
||||||
lhs_mod_id,
|
conflict.lhs_mod_id,
|
||||||
rhs_mod_id,
|
conflict.rhs_mod_id,
|
||||||
path.to_string_lossy()
|
conflict.path.to_string_lossy()
|
||||||
);
|
);
|
||||||
info!("To resolve file conflicts give one mod a higher priority in the config");
|
info!("To resolve file conflicts give one mod a higher priority in the config");
|
||||||
}
|
|
||||||
instance::InststanceError::FomodRunInstaller => {
|
|
||||||
error!("Failed to run FOMod installer");
|
|
||||||
}
|
|
||||||
instance::InststanceError::FomodFinalize(error) => {
|
|
||||||
error!(
|
|
||||||
"FOMod installer finished but failed to finalize result: {}",
|
|
||||||
error
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Err(err.into())
|
Err(anyhow!("File conflict"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,57 +54,15 @@ fn command_order(root_config: &RootConfig, instance_id: &str) -> anyhow::Result<
|
|||||||
let mut instance = root_config.load_instance_by_id(instance_id)?;
|
let mut instance = root_config.load_instance_by_id(instance_id)?;
|
||||||
let game = root_config.game_by_id(instance.game_id()).unwrap();
|
let game = root_config.game_by_id(instance.game_id()).unwrap();
|
||||||
|
|
||||||
let new_load_order = load_order::create_loadorder(root_config, game, &instance)?;
|
let new_load_order = create_loadorder(root_config, &game, &instance)?;
|
||||||
|
|
||||||
instance.set_load_order(new_load_order);
|
instance.set_load_order(new_load_order);
|
||||||
|
|
||||||
instance.save_to_file()?;
|
instance.save_to_file()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn command_download(root_config: &mut RootConfig, nxm_url: &str) -> anyhow::Result<()> {
|
fn command_download(root_config: &mut RootConfig, raw_url: &str) -> anyhow::Result<()> {
|
||||||
let Some(dl_location) = root_config.download_location() else {
|
handle_nxm(root_config, raw_url)?;
|
||||||
return Err(anyhow!("No download location set"));
|
|
||||||
};
|
|
||||||
|
|
||||||
let Some(api_key) = root_config.nexus_api_key() else {
|
|
||||||
return Err(anyhow!("No API key provided"));
|
|
||||||
};
|
|
||||||
|
|
||||||
let (dl_file, mod_info) = download_nxm(api_key, nxm_url, dl_location)?;
|
|
||||||
|
|
||||||
let mod_id = mod_info.generate_id();
|
|
||||||
if root_config.game_by_id(&mod_id).is_some() {
|
|
||||||
error!(
|
|
||||||
"Generated mod id already exists. Pleas install downloaded mod manually. Downloaded at {}",
|
|
||||||
&dl_file.to_string_lossy()
|
|
||||||
);
|
|
||||||
return Err(anyhow!("Mod with generated already exists"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let new_mod = unpack(root_config, &mod_id, dl_file)?;
|
|
||||||
|
|
||||||
root_config.add_mod(&new_mod);
|
|
||||||
|
|
||||||
root_config.save_to_file()?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn command_unpack(
|
|
||||||
root_config: &mut RootConfig,
|
|
||||||
id: &str,
|
|
||||||
file: impl AsRef<Path>,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
if root_config.game_by_id(id).is_some() {
|
|
||||||
error!("Mod already present");
|
|
||||||
return Err(anyhow!("Mod already exists"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let new_mod = unpack(root_config, id, file)?;
|
|
||||||
|
|
||||||
root_config.add_mod(&new_mod);
|
|
||||||
|
|
||||||
root_config.save_to_file()?;
|
root_config.save_to_file()?;
|
||||||
|
|
||||||
@@ -148,7 +89,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
cli::Commands::Activate { instance, target } => {
|
cli::Commands::Activate { instance, target } => {
|
||||||
command_activate(&root_config, &instance, &target)?;
|
command_activate(&root_config, &instance, &target)?;
|
||||||
}
|
}
|
||||||
cli::Commands::Add { instance, mod_id } => {
|
cli::Commands::Include { instance, mod_id } => {
|
||||||
command_add(&root_config, &instance, &mod_id)?;
|
command_add(&root_config, &instance, &mod_id)?;
|
||||||
}
|
}
|
||||||
cli::Commands::LoadOrder { instance } => {
|
cli::Commands::LoadOrder { instance } => {
|
||||||
@@ -161,9 +102,6 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
cli::Commands::Download { url } => {
|
cli::Commands::Download { url } => {
|
||||||
command_download(&mut root_config, &url)?;
|
command_download(&mut root_config, &url)?;
|
||||||
}
|
}
|
||||||
cli::Commands::Unpack { id, path } => {
|
|
||||||
command_unpack(&mut root_config, &id, path)?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -176,7 +176,11 @@ fn resolve_plugin_type(
|
|||||||
PluginTypeDescriptorEnum::PluginType(plugin_type) => plugin_type.name,
|
PluginTypeDescriptorEnum::PluginType(plugin_type) => plugin_type.name,
|
||||||
PluginTypeDescriptorEnum::DependencyType(dependency_plugin_type) => {
|
PluginTypeDescriptorEnum::DependencyType(dependency_plugin_type) => {
|
||||||
for dep in &dependency_plugin_type.patterns.pattern {
|
for dep in &dependency_plugin_type.patterns.pattern {
|
||||||
if evaluate_dependency(&dep.dependencies, state, installed_plugins) {
|
if dep
|
||||||
|
.dependencies
|
||||||
|
.iter()
|
||||||
|
.all(|e| evaluate_dependency(e, state, installed_plugins))
|
||||||
|
{
|
||||||
return dep.typ.name;
|
return dep.typ.name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ mod api;
|
|||||||
mod downloader;
|
mod downloader;
|
||||||
mod url;
|
mod url;
|
||||||
|
|
||||||
pub use api::NexusAPI;
|
pub use api::{ModInfo, NexusAPI};
|
||||||
pub use downloader::download_nxm;
|
pub use downloader::download_nxm;
|
||||||
pub use url::NXMUrl;
|
pub use url::NXMUrl;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::nexus::NXMUrl;
|
use crate::{nexus::NXMUrl, types::GameType};
|
||||||
|
|
||||||
const NEXUS_ENDPOINT: &str = "https://api.nexusmods.com";
|
const NEXUS_ENDPOINT: &str = "https://api.nexusmods.com";
|
||||||
|
|
||||||
@@ -95,7 +95,7 @@ pub struct ModInfo {
|
|||||||
pub mod_id: u64,
|
pub mod_id: u64,
|
||||||
// pub game_id: u64,
|
// pub game_id: u64,
|
||||||
// pub allow_rating: bool,
|
// pub allow_rating: bool,
|
||||||
// pub domain_name: String,
|
pub domain_name: String,
|
||||||
// pub category_id: u64,
|
// pub category_id: u64,
|
||||||
pub version: String,
|
pub version: String,
|
||||||
// pub endorsement_count: u64,
|
// pub endorsement_count: u64,
|
||||||
@@ -145,6 +145,10 @@ impl ModInfo {
|
|||||||
if short_name.len() > MAX_CHARS {
|
if short_name.len() > MAX_CHARS {
|
||||||
short_name.truncate(MAX_CHARS);
|
short_name.truncate(MAX_CHARS);
|
||||||
}
|
}
|
||||||
format!("{}-{}-{}", short_name, self.mod_id, self.version)
|
format!("{}-{}", short_name.to_lowercase(), self.mod_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_game_type(&self) -> GameType {
|
||||||
|
GameType::from_nexus_domain(&self.domain_name).unwrap_or(GameType::Unknown)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,21 +14,18 @@ use crate::nexus::{
|
|||||||
|
|
||||||
pub fn download_nxm(
|
pub fn download_nxm(
|
||||||
api_key: &str,
|
api_key: &str,
|
||||||
link: &str,
|
nxm_url: &NXMUrl,
|
||||||
target_dir: impl AsRef<Path>,
|
target_dir: impl AsRef<Path>,
|
||||||
) -> anyhow::Result<(PathBuf, ModInfo)> {
|
) -> anyhow::Result<(PathBuf, ModInfo)> {
|
||||||
let api = NexusAPI::new(api_key);
|
let api = NexusAPI::new(api_key);
|
||||||
let Some(parsed_url) = NXMUrl::parse_url(link) else {
|
|
||||||
return Err(anyhow!("Failed to parse url"));
|
|
||||||
};
|
|
||||||
|
|
||||||
let mod_info = api.mod_info(&parsed_url.game, &parsed_url.mod_id)?;
|
let mod_info = api.mod_info(&nxm_url.game, &nxm_url.mod_id)?;
|
||||||
let links = api.generate_download_link_for_file(&parsed_url)?;
|
let links = api.generate_download_link_for_file(nxm_url)?;
|
||||||
let selected_mirror = links.first().unwrap();
|
let selected_mirror = links.first().unwrap();
|
||||||
let url = selected_mirror.parse_url()?;
|
let url = selected_mirror.parse_url()?;
|
||||||
let original_filename = url.path_segments().and_then(|mut e| e.next_back()).unwrap();
|
let original_filename = url.path_segments().and_then(|mut e| e.next_back()).unwrap();
|
||||||
let filename = gen_filename_for_mod(&mod_info, original_filename);
|
let filename = gen_filename_for_mod(&mod_info, &nxm_url.file, original_filename);
|
||||||
let download_path = target_dir.as_ref().join(parsed_url.game).join(filename);
|
let download_path = target_dir.as_ref().join(&nxm_url.game).join(filename);
|
||||||
|
|
||||||
if let Some(parent) = download_path.parent() {
|
if let Some(parent) = download_path.parent() {
|
||||||
create_dir_all(parent)?;
|
create_dir_all(parent)?;
|
||||||
@@ -58,12 +55,12 @@ fn download_mod(mod_dl_link: &DownloadLocation, target: impl AsRef<Path>) -> any
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen_filename_for_mod(mod_info: &ModInfo, dl_filename: &str) -> String {
|
fn gen_filename_for_mod(mod_info: &ModInfo, file_id: &str, dl_filename: &str) -> String {
|
||||||
let filename_from_url = PathBuf::from(dl_filename);
|
let filename_from_url = PathBuf::from(dl_filename);
|
||||||
let ext = filename_from_url
|
let ext = filename_from_url
|
||||||
.extension()
|
.extension()
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.to_string_lossy();
|
.to_string_lossy();
|
||||||
|
|
||||||
format!("{}-{}.{}", mod_info.mod_id, mod_info.version, ext)
|
format!("{}-{}.{}", mod_info.mod_id, file_id, ext)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,24 @@
|
|||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
mod game;
|
mod game;
|
||||||
|
mod game_type;
|
||||||
mod installed_mod;
|
mod installed_mod;
|
||||||
mod link;
|
mod link;
|
||||||
mod mod_config;
|
mod mod_config;
|
||||||
mod mod_file;
|
mod mod_file;
|
||||||
mod modded_instance;
|
mod modded_instance;
|
||||||
mod root_config;
|
mod root_config;
|
||||||
|
mod nexus_id;
|
||||||
|
|
||||||
pub use game::*;
|
pub use game::*;
|
||||||
|
pub use game_type::GameType;
|
||||||
pub use installed_mod::*;
|
pub use installed_mod::*;
|
||||||
pub use link::*;
|
pub use link::*;
|
||||||
pub use mod_config::*;
|
pub use mod_config::*;
|
||||||
pub use mod_file::*;
|
pub use mod_file::*;
|
||||||
pub use modded_instance::*;
|
pub use modded_instance::*;
|
||||||
pub use root_config::*;
|
pub use root_config::*;
|
||||||
|
pub use nexus_id::*;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum ConfigReadWriteError {
|
pub enum ConfigReadWriteError {
|
||||||
|
|||||||
@@ -1,27 +1,33 @@
|
|||||||
use std::{
|
use std::{
|
||||||
|
collections::HashSet,
|
||||||
io,
|
io,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{types::link::Link, utils::walk_all_files};
|
use crate::{
|
||||||
|
types::{GameType, link::Link},
|
||||||
|
utils::walk_all_files,
|
||||||
|
};
|
||||||
|
|
||||||
/// Available game
|
/// Available game
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct Game {
|
pub struct Game {
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
|
kind: GameType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Game {
|
impl Game {
|
||||||
pub fn new(path: impl AsRef<Path>) -> Self {
|
pub fn new(path: impl AsRef<Path>, game_type: GameType) -> Self {
|
||||||
Self {
|
Self {
|
||||||
path: path.as_ref().to_owned(),
|
path: path.as_ref().to_owned(),
|
||||||
|
kind: game_type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn export_links(&self) -> Result<Vec<Link>, io::Error> {
|
pub fn export_links(&self) -> Result<HashSet<Link>, io::Error> {
|
||||||
let links: Vec<Link> = walk_all_files(&self.path)?
|
let links: HashSet<Link> = walk_all_files(&self.path)?
|
||||||
.map(|entry| {
|
.map(|entry| {
|
||||||
Link::new(
|
Link::new(
|
||||||
entry.path(),
|
entry.path(),
|
||||||
@@ -39,4 +45,8 @@ impl Game {
|
|||||||
pub fn install_location(&self) -> &Path {
|
pub fn install_location(&self) -> &Path {
|
||||||
&self.path
|
&self.path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn game_type(&self) -> GameType {
|
||||||
|
self.kind.clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
186
src/types/game_type.rs
Normal file
186
src/types/game_type.rs
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Deserializer, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||||
|
pub enum GameType {
|
||||||
|
Oblivion,
|
||||||
|
Skyrim,
|
||||||
|
Fallout3,
|
||||||
|
FalloutNV,
|
||||||
|
Fallout4,
|
||||||
|
SkyrimSE,
|
||||||
|
Fallout4VR,
|
||||||
|
SkyrimVR,
|
||||||
|
Morrowind,
|
||||||
|
Starfield,
|
||||||
|
OpenMW,
|
||||||
|
OblivionRemastered,
|
||||||
|
Custom(String),
|
||||||
|
|
||||||
|
#[default]
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GameType {
|
||||||
|
pub fn to_libloot_type(self) -> Option<libloot::GameType> {
|
||||||
|
match self {
|
||||||
|
GameType::Oblivion => Some(libloot::GameType::Oblivion),
|
||||||
|
GameType::Skyrim => Some(libloot::GameType::Skyrim),
|
||||||
|
GameType::Fallout3 => Some(libloot::GameType::Fallout3),
|
||||||
|
GameType::FalloutNV => Some(libloot::GameType::FalloutNV),
|
||||||
|
GameType::Fallout4 => Some(libloot::GameType::Fallout4),
|
||||||
|
GameType::SkyrimSE => Some(libloot::GameType::SkyrimSE),
|
||||||
|
GameType::Fallout4VR => Some(libloot::GameType::Fallout4VR),
|
||||||
|
GameType::SkyrimVR => Some(libloot::GameType::SkyrimVR),
|
||||||
|
GameType::Morrowind => Some(libloot::GameType::Morrowind),
|
||||||
|
GameType::Starfield => Some(libloot::GameType::Starfield),
|
||||||
|
GameType::OpenMW => Some(libloot::GameType::OpenMW),
|
||||||
|
GameType::OblivionRemastered => Some(libloot::GameType::OblivionRemastered),
|
||||||
|
GameType::Custom(_) => None,
|
||||||
|
GameType::Unknown => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_nexus_domain(self) -> Option<String> {
|
||||||
|
match self {
|
||||||
|
GameType::Oblivion => Some("oblivion".to_owned()),
|
||||||
|
GameType::Skyrim => Some("skyrim".to_owned()),
|
||||||
|
GameType::Fallout3 => Some("fallout3".to_owned()),
|
||||||
|
GameType::FalloutNV => Some("newvegas".to_owned()),
|
||||||
|
GameType::Fallout4 => Some("fallout4".to_owned()),
|
||||||
|
GameType::SkyrimSE => Some("skyrimspecialedition".to_owned()),
|
||||||
|
GameType::Fallout4VR => Some("fallout4".to_owned()),
|
||||||
|
GameType::SkyrimVR => Some("skyrimspecialedition".to_owned()),
|
||||||
|
GameType::Morrowind => Some("morrowind".to_owned()),
|
||||||
|
GameType::Starfield => Some("starfield".to_owned()),
|
||||||
|
GameType::OpenMW => Some("morrowind".to_owned()),
|
||||||
|
GameType::OblivionRemastered => Some("oblivionremastered".to_owned()),
|
||||||
|
GameType::Custom(_) => None,
|
||||||
|
GameType::Unknown => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_nexus_domain(domain: &str) -> Option<Self> {
|
||||||
|
match domain {
|
||||||
|
"oblivion" => Some(GameType::Oblivion),
|
||||||
|
"skyrim" => Some(GameType::Skyrim),
|
||||||
|
"fallout3" => Some(GameType::Fallout3),
|
||||||
|
"newvegas" => Some(GameType::FalloutNV),
|
||||||
|
"fallout4" => Some(GameType::Fallout4),
|
||||||
|
"skyrimspecialedition" => Some(GameType::SkyrimSE),
|
||||||
|
"morrowind" => Some(GameType::Morrowind),
|
||||||
|
"starfield" => Some(GameType::Starfield),
|
||||||
|
"oblivionremastered" => Some(GameType::OblivionRemastered),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for GameType {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let s = match self {
|
||||||
|
GameType::Oblivion => "Oblivion",
|
||||||
|
GameType::Skyrim => "Skyrim",
|
||||||
|
GameType::Fallout3 => "Fallout 3",
|
||||||
|
GameType::FalloutNV => "Fallout New Vegas",
|
||||||
|
GameType::Fallout4 => "Fallout 4",
|
||||||
|
GameType::SkyrimSE => "Skyrim Special Edition",
|
||||||
|
GameType::Fallout4VR => "Fallout 4 VR",
|
||||||
|
GameType::SkyrimVR => "Skyrim VR",
|
||||||
|
GameType::Morrowind => "Morrowind",
|
||||||
|
GameType::Starfield => "Starfield",
|
||||||
|
GameType::OpenMW => "OpenMW",
|
||||||
|
GameType::OblivionRemastered => "Oblivion Remastered",
|
||||||
|
GameType::Custom(name) => name,
|
||||||
|
GameType::Unknown => "Unknown",
|
||||||
|
};
|
||||||
|
|
||||||
|
write!(f, "{}", s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for GameType {
|
||||||
|
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||||
|
let s = String::deserialize(deserializer)?;
|
||||||
|
Ok(match s.as_str() {
|
||||||
|
"oblivion" => Self::Oblivion,
|
||||||
|
"skyrim" => Self::Skyrim,
|
||||||
|
"fo3" => Self::Fallout3,
|
||||||
|
"fonv" => Self::FalloutNV,
|
||||||
|
"fo4" => Self::Fallout4,
|
||||||
|
"sse" => Self::SkyrimSE,
|
||||||
|
"fo4vr" => Self::Fallout4VR,
|
||||||
|
"skyrimvr" => Self::SkyrimVR,
|
||||||
|
"morrowind" => Self::Morrowind,
|
||||||
|
"starfield" => Self::Starfield,
|
||||||
|
"openmw" => Self::OpenMW,
|
||||||
|
"oblivionrm" => Self::OblivionRemastered,
|
||||||
|
"unknown" => Self::Unknown,
|
||||||
|
_ => Self::Custom(s),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for GameType {
|
||||||
|
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||||
|
let s = match self {
|
||||||
|
Self::Custom(s) => s,
|
||||||
|
Self::Oblivion => "oblivion",
|
||||||
|
Self::Skyrim => "skyrim",
|
||||||
|
Self::Fallout3 => "fo3",
|
||||||
|
Self::FalloutNV => "fonv",
|
||||||
|
Self::Fallout4 => "fo4",
|
||||||
|
Self::SkyrimSE => "sse",
|
||||||
|
Self::Fallout4VR => "fo4vr",
|
||||||
|
Self::SkyrimVR => "skyrimvr",
|
||||||
|
Self::Morrowind => "morrowind",
|
||||||
|
Self::Starfield => "starfield",
|
||||||
|
Self::OpenMW => "openmw",
|
||||||
|
Self::OblivionRemastered => "oblivionrm",
|
||||||
|
Self::Unknown => "unknown",
|
||||||
|
};
|
||||||
|
|
||||||
|
serializer.serialize_str(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, PartialEq, Debug)]
|
||||||
|
struct Wrapper {
|
||||||
|
value: GameType,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn roundtrip(game_type: GameType) {
|
||||||
|
let val = Wrapper { value: game_type };
|
||||||
|
let serialized = toml::to_string(&val).unwrap();
|
||||||
|
let deserialized: Wrapper = toml::from_str(&serialized).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(val, deserialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_back_and_forth_all() {
|
||||||
|
for e in [
|
||||||
|
GameType::Oblivion,
|
||||||
|
GameType::Skyrim,
|
||||||
|
GameType::Fallout3,
|
||||||
|
GameType::FalloutNV,
|
||||||
|
GameType::Fallout4,
|
||||||
|
GameType::SkyrimSE,
|
||||||
|
GameType::Fallout4VR,
|
||||||
|
GameType::SkyrimVR,
|
||||||
|
GameType::Morrowind,
|
||||||
|
GameType::Starfield,
|
||||||
|
GameType::OpenMW,
|
||||||
|
GameType::OblivionRemastered,
|
||||||
|
GameType::Custom("custom".to_owned()),
|
||||||
|
GameType::Unknown,
|
||||||
|
] {
|
||||||
|
roundtrip(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
|
use serde::{
|
||||||
|
Deserialize, Deserializer, Serialize, Serializer,
|
||||||
|
de::{self, Visitor},
|
||||||
|
};
|
||||||
use std::{
|
use std::{
|
||||||
fmt::Debug,
|
fmt::{self, Debug},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::types::mod_file::ModFile;
|
use crate::types::mod_file::ModFile;
|
||||||
|
|
||||||
/// A link between a file from a mod and a destination in a ModdedInstance
|
/// A link between a file from a mod and a destination in a ModdedInstance
|
||||||
#[derive(Clone, Deserialize, Serialize, PartialEq, Eq, Hash)]
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||||
#[serde(from = "(PathBuf, PathBuf)", into = "(PathBuf,PathBuf)")]
|
|
||||||
pub struct Link {
|
pub struct Link {
|
||||||
src: PathBuf,
|
src: PathBuf,
|
||||||
dst: PathBuf,
|
dst: PathBuf,
|
||||||
@@ -36,18 +37,46 @@ impl Link {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<(PathBuf, PathBuf)> for Link {
|
impl Serialize for Link {
|
||||||
fn from(value: (PathBuf, PathBuf)) -> Self {
|
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||||
Self {
|
if self.src == self.dst {
|
||||||
src: value.0,
|
serializer.serialize_str(&self.src.to_string_lossy())
|
||||||
dst: value.1,
|
} else {
|
||||||
|
serializer.serialize_str(&format!(
|
||||||
|
"{} -> {}",
|
||||||
|
self.src.to_string_lossy(),
|
||||||
|
self.dst.to_string_lossy()
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Link> for (PathBuf, PathBuf) {
|
impl<'de> Deserialize<'de> for Link {
|
||||||
fn from(value: Link) -> Self {
|
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||||
(value.src, value.dst)
|
struct LinkVisitor;
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for LinkVisitor {
|
||||||
|
type Value = Link;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
formatter.write_str(r#"a string like "src -> dst" or "path" if they are the same"#)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E: de::Error>(self, value: &str) -> Result<Link, E> {
|
||||||
|
match value.split_once(" -> ") {
|
||||||
|
Some((src, dst)) => Ok(Link {
|
||||||
|
src: PathBuf::from(src),
|
||||||
|
dst: PathBuf::from(dst),
|
||||||
|
}),
|
||||||
|
None => Ok(Link {
|
||||||
|
src: PathBuf::from(value),
|
||||||
|
dst: PathBuf::from(value),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.deserialize_str(LinkVisitor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,11 @@ use std::path::{Path, PathBuf};
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
nexus::ModInfo,
|
||||||
|
types::{GameType, NexusID},
|
||||||
|
};
|
||||||
|
|
||||||
/// Config for an available mod
|
/// Config for an available mod
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
@@ -22,6 +27,14 @@ pub struct ModConfig {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
ignore: Vec<String>,
|
ignore: Vec<String>,
|
||||||
|
|
||||||
|
name: Option<String>,
|
||||||
|
|
||||||
|
nexus_id: Option<NexusID>,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
|
#[serde(skip_serializing_if = "is_default")]
|
||||||
|
game: GameType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModConfig {
|
impl ModConfig {
|
||||||
@@ -31,9 +44,26 @@ impl ModConfig {
|
|||||||
path: source.as_ref().to_owned(),
|
path: source.as_ref().to_owned(),
|
||||||
root_mod: false,
|
root_mod: false,
|
||||||
ignore: Vec::new(),
|
ignore: Vec::new(),
|
||||||
|
name: None,
|
||||||
|
nexus_id: None,
|
||||||
|
game: GameType::Unknown,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_mod_info(
|
||||||
|
id: &str,
|
||||||
|
source: impl AsRef<Path>,
|
||||||
|
mod_info: &ModInfo,
|
||||||
|
file_id: u64,
|
||||||
|
) -> Self {
|
||||||
|
let mut normal = Self::new(id, source);
|
||||||
|
normal.name = Some(mod_info.name.clone());
|
||||||
|
normal.game = mod_info.get_game_type();
|
||||||
|
normal.nexus_id = Some(NexusID::new(mod_info.mod_id, file_id));
|
||||||
|
|
||||||
|
normal
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_id(mut self, id: &str) -> Self {
|
pub fn add_id(mut self, id: &str) -> Self {
|
||||||
self.id = id.to_owned();
|
self.id = id.to_owned();
|
||||||
self
|
self
|
||||||
@@ -55,8 +85,20 @@ impl ModConfig {
|
|||||||
pub fn ignore(&self) -> &[String] {
|
pub fn ignore(&self) -> &[String] {
|
||||||
&self.ignore
|
&self.ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn name(&self) -> Option<&str> {
|
||||||
|
self.name.as_deref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nexus_id(&self) -> Option<&NexusID> {
|
||||||
|
self.nexus_id.as_ref()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_false(b: &bool) -> bool {
|
fn is_false(b: &bool) -> bool {
|
||||||
!b
|
!b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_default<T: Default + PartialEq>(t: &T) -> bool {
|
||||||
|
t == &T::default()
|
||||||
|
}
|
||||||
|
|||||||
70
src/types/nexus_id.rs
Normal file
70
src/types/nexus_id.rs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct NexusID {
|
||||||
|
mod_id: u64,
|
||||||
|
file_id: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NexusID {
|
||||||
|
pub fn new(mod_id: u64, file_id: u64) -> Self {
|
||||||
|
Self { mod_id, file_id }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for NexusID {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
let s = format!("{}:{}", self.mod_id, self.file_id);
|
||||||
|
serializer.serialize_str(&s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for NexusID {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let s = String::deserialize(deserializer)?;
|
||||||
|
let mut parts = s.split(':');
|
||||||
|
|
||||||
|
let mod_id = parts
|
||||||
|
.next()
|
||||||
|
.ok_or_else(|| serde::de::Error::custom("missing first value"))
|
||||||
|
.and_then(|p| u64::from_str(p).map_err(serde::de::Error::custom))?;
|
||||||
|
|
||||||
|
let file_id = parts
|
||||||
|
.next()
|
||||||
|
.ok_or_else(|| serde::de::Error::custom("missing second value"))
|
||||||
|
.and_then(|p| u64::from_str(p).map_err(serde::de::Error::custom))?;
|
||||||
|
|
||||||
|
if parts.next().is_some() {
|
||||||
|
return Err(serde::de::Error::custom("too many parts"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self { mod_id, file_id })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, PartialEq, Debug)]
|
||||||
|
struct Wrapper {
|
||||||
|
value: NexusID,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serde_roundtrip() {
|
||||||
|
let val = Wrapper {
|
||||||
|
value: NexusID::new(1234, 5678),
|
||||||
|
};
|
||||||
|
let serialized = toml::to_string(&val).unwrap();
|
||||||
|
let deserialized: Wrapper = toml::from_str(&serialized).unwrap();
|
||||||
|
assert_eq!(val, deserialized);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -57,14 +57,30 @@ impl RootConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn save_to_file(&self) -> Result<(), ConfigReadWriteError> {
|
pub fn save_to_file(&self) -> Result<(), ConfigReadWriteError> {
|
||||||
|
debug!(
|
||||||
|
"Saving root_config to: {}",
|
||||||
|
self.self_path.to_string_lossy()
|
||||||
|
);
|
||||||
let content = toml::to_string_pretty(self)?;
|
let content = toml::to_string_pretty(self)?;
|
||||||
let mut file = fs::File::create(&self.self_path)?;
|
let mut file = fs::File::create(&self.self_path)?;
|
||||||
write!(file, "{}", content)?;
|
write!(file, "{}", content)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn game_by_id(&self, id: &str) -> Option<&Game> {
|
pub fn game_by_id(&self, id: &str) -> Option<Game> {
|
||||||
self.games.get(id)
|
self.games.get(id).map(|parsed_game| {
|
||||||
|
if parsed_game.install_location().is_relative() {
|
||||||
|
let abs_path = self.self_parent.join(parsed_game.install_location());
|
||||||
|
debug!(
|
||||||
|
"game path for {} is relative. Resolving to {}",
|
||||||
|
id,
|
||||||
|
abs_path.to_string_lossy()
|
||||||
|
);
|
||||||
|
Game::new(abs_path, parsed_game.game_type())
|
||||||
|
} else {
|
||||||
|
parsed_game.clone()
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mod_by_id(&self, id: &str) -> Option<ModConfig> {
|
pub fn mod_by_id(&self, id: &str) -> Option<ModConfig> {
|
||||||
@@ -76,13 +92,19 @@ impl RootConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_instance_by_id(&self, id: &str) -> Result<ModdedInstance, ConfigReadWriteError> {
|
pub fn load_instance_by_id(&self, id: &str) -> Result<ModdedInstance, ConfigReadWriteError> {
|
||||||
|
debug!("Loading instance {}", id);
|
||||||
let conf = self
|
let conf = self
|
||||||
.instances
|
.instances
|
||||||
.get(id)
|
.get(id)
|
||||||
.ok_or(ConfigReadWriteError::IDNotFound)?;
|
.ok_or(ConfigReadWriteError::IDNotFound)?;
|
||||||
|
|
||||||
if conf.path.is_relative() {
|
if conf.path.is_relative() {
|
||||||
ModdedInstance::load_from_file(self.self_parent.join(&conf.path))
|
let abs_path = self.self_parent.join(&conf.path);
|
||||||
|
debug!(
|
||||||
|
"instance path is relative. Resolving to {}",
|
||||||
|
abs_path.to_string_lossy()
|
||||||
|
);
|
||||||
|
ModdedInstance::load_from_file(abs_path)
|
||||||
} else {
|
} else {
|
||||||
ModdedInstance::load_from_file(&conf.path)
|
ModdedInstance::load_from_file(&conf.path)
|
||||||
}
|
}
|
||||||
@@ -90,7 +112,12 @@ impl RootConfig {
|
|||||||
|
|
||||||
pub fn mod_location(&self) -> PathBuf {
|
pub fn mod_location(&self) -> PathBuf {
|
||||||
if self.mod_location.is_relative() {
|
if self.mod_location.is_relative() {
|
||||||
self.self_parent.join(&self.mod_location)
|
let abs_path = self.self_parent.join(&self.mod_location);
|
||||||
|
debug!(
|
||||||
|
"mod_location path is relative. Resolving to {}",
|
||||||
|
abs_path.to_string_lossy()
|
||||||
|
);
|
||||||
|
abs_path
|
||||||
} else {
|
} else {
|
||||||
self.mod_location.clone()
|
self.mod_location.clone()
|
||||||
}
|
}
|
||||||
@@ -103,7 +130,12 @@ impl RootConfig {
|
|||||||
pub fn download_location(&self) -> Option<PathBuf> {
|
pub fn download_location(&self) -> Option<PathBuf> {
|
||||||
self.download_location.as_ref().map(|e| {
|
self.download_location.as_ref().map(|e| {
|
||||||
if e.is_relative() {
|
if e.is_relative() {
|
||||||
self.self_parent.join(e)
|
let abs_path = self.self_parent.join(e);
|
||||||
|
debug!(
|
||||||
|
"download_location path is relative. Resolving to {}",
|
||||||
|
abs_path.to_string_lossy()
|
||||||
|
);
|
||||||
|
abs_path
|
||||||
} else {
|
} else {
|
||||||
e.clone()
|
e.clone()
|
||||||
}
|
}
|
||||||
@@ -118,11 +150,16 @@ struct InstancePointer {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::types::GameType;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
fn create_config() -> RootConfig {
|
fn create_config() -> RootConfig {
|
||||||
RootConfig {
|
RootConfig {
|
||||||
games: HashMap::from([("sse".to_owned(), Game::new("/games/sse"))]),
|
games: HashMap::from([(
|
||||||
|
"sse".to_owned(),
|
||||||
|
Game::new("/games/sse", GameType::SkyrimSE),
|
||||||
|
)]),
|
||||||
mod_location: PathBuf::from("mods"),
|
mod_location: PathBuf::from("mods"),
|
||||||
download_location: Some(PathBuf::from("download")),
|
download_location: Some(PathBuf::from("download")),
|
||||||
nexus_api_key: Some("1234".to_owned()),
|
nexus_api_key: Some("1234".to_owned()),
|
||||||
@@ -151,6 +188,7 @@ mod tests {
|
|||||||
|
|
||||||
let unwraped = game.expect("Asserted before");
|
let unwraped = game.expect("Asserted before");
|
||||||
assert_eq!(unwraped.install_location(), "/games/sse");
|
assert_eq!(unwraped.install_location(), "/games/sse");
|
||||||
|
assert_eq!(unwraped.game_type(), GameType::SkyrimSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
108
src/unpacker.rs
108
src/unpacker.rs
@@ -1,28 +1,104 @@
|
|||||||
use std::{fs, path::Path};
|
use std::{
|
||||||
|
fs,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
|
||||||
use anyhow::anyhow;
|
use anyhow::{Ok, anyhow};
|
||||||
|
use log::error;
|
||||||
use crate::types::{ModConfig, RootConfig};
|
use zip::ZipArchive;
|
||||||
|
|
||||||
pub fn unpack(
|
|
||||||
root_config: &RootConfig,
|
|
||||||
id: &str,
|
|
||||||
path: impl AsRef<Path>,
|
|
||||||
) -> anyhow::Result<ModConfig> {
|
|
||||||
let extract_to = root_config.mod_location().join(id);
|
|
||||||
|
|
||||||
|
pub fn unpack(archive_path: impl AsRef<Path>, extract_to: impl AsRef<Path>) -> anyhow::Result<()> {
|
||||||
if fs::exists(&extract_to)? {
|
if fs::exists(&extract_to)? {
|
||||||
return Err(anyhow!("File already exists"));
|
return Err(anyhow!(
|
||||||
|
"File already exists: {}",
|
||||||
|
extract_to.as_ref().to_string_lossy()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
unpack_7z_file(path, &extract_to)?;
|
match archive_path.as_ref().extension().and_then(|e| e.to_str()) {
|
||||||
|
Some("7z") => unpack_7z_file(archive_path, &extract_to),
|
||||||
|
Some("zip") => unpack_zip_file(archive_path, &extract_to),
|
||||||
|
Some("rar") => unpack_rar(archive_path, &extract_to),
|
||||||
|
Some(ext) => {
|
||||||
|
error!("Unsupported archive format: {}", ext);
|
||||||
|
Err(anyhow!("Unsupported archive format: {}", ext))
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
error!(
|
||||||
|
"Failed to determine the file extension for {}",
|
||||||
|
&archive_path.as_ref().to_string_lossy()
|
||||||
|
);
|
||||||
|
Err(anyhow!("Failed to determine file extension"))
|
||||||
|
}
|
||||||
|
}?;
|
||||||
|
|
||||||
let new_mod = ModConfig::new(id, id);
|
unnest_dir(extract_to)?;
|
||||||
|
|
||||||
Ok(new_mod)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unpack_7z_file(path: impl AsRef<Path>, to: impl AsRef<Path>) -> anyhow::Result<()> {
|
fn unpack_7z_file(path: impl AsRef<Path>, to: impl AsRef<Path>) -> anyhow::Result<()> {
|
||||||
sevenz_rust::decompress_file(path, to)?;
|
sevenz_rust2::decompress_file(path, to)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn unpack_zip_file(path: impl AsRef<Path>, to: impl AsRef<Path>) -> anyhow::Result<()> {
|
||||||
|
let file = fs::File::open(path)?;
|
||||||
|
let mut archive = ZipArchive::new(file)?;
|
||||||
|
archive.extract(to)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unpack_rar(path: impl AsRef<Path>, to: impl AsRef<Path>) -> anyhow::Result<()> {
|
||||||
|
let mut archive = unrar::Archive::new(path.as_ref()).open_for_processing()?;
|
||||||
|
|
||||||
|
while let Some(header) = archive.read_header()? {
|
||||||
|
archive = header.extract_with_base(&to)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Moves a directorys content into the parent if it is the only dir
|
||||||
|
fn unnest_dir(path: impl AsRef<Path>) -> anyhow::Result<()> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
|
||||||
|
let Some(nested_dir) = check_nested_dir(path) else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
for entry in fs::read_dir(&nested_dir)? {
|
||||||
|
let entry = entry?;
|
||||||
|
let src = entry.path();
|
||||||
|
let dest = path.join(entry.file_name());
|
||||||
|
fs::rename(&src, &dest)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::remove_dir(&nested_dir)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if the extracted archive has a single directory in it which contains the mod files
|
||||||
|
fn check_nested_dir(path: impl AsRef<Path>) -> Option<PathBuf> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
|
||||||
|
let entries: Vec<_> = fs::read_dir(path).ok()?.filter_map(|e| e.ok()).collect();
|
||||||
|
|
||||||
|
if entries.len() == 1 {
|
||||||
|
let entry = &entries[0];
|
||||||
|
let entry_path = entry.path();
|
||||||
|
|
||||||
|
if entry_path
|
||||||
|
.file_name()
|
||||||
|
.is_some_and(|e| e == "Data" || e == "data")
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if entry_path.is_dir() {
|
||||||
|
return Some(entry_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ pub fn path_to_lowercase(path: impl AsRef<Path>) -> PathBuf {
|
|||||||
PathBuf::from(path.as_ref().to_string_lossy().to_lowercase())
|
PathBuf::from(path.as_ref().to_string_lossy().to_lowercase())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Searches for a path but ignores case. Returns the first it finds.
|
||||||
pub fn resolve_case_insensitive(
|
pub fn resolve_case_insensitive(
|
||||||
base: impl AsRef<Path>,
|
base: impl AsRef<Path>,
|
||||||
rel: impl AsRef<Path>,
|
rel: impl AsRef<Path>,
|
||||||
@@ -44,7 +45,7 @@ pub fn resolve_case_insensitive(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Use walkdir to walk all actual files in a dir
|
/// Use walkdir to walk all actual files in a dir
|
||||||
/// Returns early id any error occurs
|
/// Returns early if any error occurs
|
||||||
pub fn walk_all_files(
|
pub fn walk_all_files(
|
||||||
path: impl AsRef<Path>,
|
path: impl AsRef<Path>,
|
||||||
) -> Result<impl Iterator<Item = walkdir::DirEntry>, walkdir::Error> {
|
) -> Result<impl Iterator<Item = walkdir::DirEntry>, walkdir::Error> {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::{collections::HashSet, error::Error, path::PathBuf};
|
use std::{collections::HashSet, error::Error, path::PathBuf};
|
||||||
|
|
||||||
use fomod_manager::{
|
use fomod_manager::{
|
||||||
instance::{files_to_install_mod, insert_mod_to_instance},
|
actions::{insert_mod_to_instance, resolve_files_for_install},
|
||||||
types::{Link, RootConfig},
|
types::{Link, RootConfig},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -20,9 +20,9 @@ fn add_plain() -> Result<(), Box<dyn Error>> {
|
|||||||
let mod_to_install = root_config
|
let mod_to_install = root_config
|
||||||
.mod_by_id("add_test_plain")
|
.mod_by_id("add_test_plain")
|
||||||
.expect("Mod not found");
|
.expect("Mod not found");
|
||||||
let files_to_add = files_to_install_mod(&root_config, &instance, &mod_to_install)?;
|
let files_to_add = resolve_files_for_install(&root_config, &instance, &mod_to_install)?;
|
||||||
|
|
||||||
insert_mod_to_instance(&mut instance, &mod_to_install, &files_to_add, 0)?;
|
insert_mod_to_instance(&mut instance, &mod_to_install, &files_to_add, 0);
|
||||||
|
|
||||||
let installed_mods = instance.mods();
|
let installed_mods = instance.mods();
|
||||||
|
|
||||||
@@ -53,9 +53,9 @@ fn add_nested() -> Result<(), Box<dyn Error>> {
|
|||||||
let mod_to_install = root_config
|
let mod_to_install = root_config
|
||||||
.mod_by_id("add_test_nested")
|
.mod_by_id("add_test_nested")
|
||||||
.expect("Mod not found");
|
.expect("Mod not found");
|
||||||
let files_to_add = files_to_install_mod(&root_config, &instance, &mod_to_install)?;
|
let files_to_add = resolve_files_for_install(&root_config, &instance, &mod_to_install)?;
|
||||||
|
|
||||||
insert_mod_to_instance(&mut instance, &mod_to_install, &files_to_add, 0)?;
|
insert_mod_to_instance(&mut instance, &mod_to_install, &files_to_add, 0);
|
||||||
|
|
||||||
let installed_mods = instance.mods();
|
let installed_mods = instance.mods();
|
||||||
|
|
||||||
@@ -86,9 +86,9 @@ fn add_root() -> Result<(), Box<dyn Error>> {
|
|||||||
let mod_to_install = root_config
|
let mod_to_install = root_config
|
||||||
.mod_by_id("add_test_root")
|
.mod_by_id("add_test_root")
|
||||||
.expect("Mod not found");
|
.expect("Mod not found");
|
||||||
let files_to_add = files_to_install_mod(&root_config, &instance, &mod_to_install)?;
|
let files_to_add = resolve_files_for_install(&root_config, &instance, &mod_to_install)?;
|
||||||
|
|
||||||
insert_mod_to_instance(&mut instance, &mod_to_install, &files_to_add, 0)?;
|
insert_mod_to_instance(&mut instance, &mod_to_install, &files_to_add, 0);
|
||||||
|
|
||||||
let installed_mods = instance.mods();
|
let installed_mods = instance.mods();
|
||||||
|
|
||||||
@@ -117,9 +117,9 @@ fn add_filter() -> Result<(), Box<dyn Error>> {
|
|||||||
let mod_to_install = root_config
|
let mod_to_install = root_config
|
||||||
.mod_by_id("add_test_filter")
|
.mod_by_id("add_test_filter")
|
||||||
.expect("Mod not found");
|
.expect("Mod not found");
|
||||||
let files_to_add = files_to_install_mod(&root_config, &instance, &mod_to_install)?;
|
let files_to_add = resolve_files_for_install(&root_config, &instance, &mod_to_install)?;
|
||||||
|
|
||||||
insert_mod_to_instance(&mut instance, &mod_to_install, &files_to_add, 0)?;
|
insert_mod_to_instance(&mut instance, &mod_to_install, &files_to_add, 0);
|
||||||
|
|
||||||
let installed_mods = instance.mods();
|
let installed_mods = instance.mods();
|
||||||
|
|
||||||
|
|||||||
67
tests/data/fomod/moduleconfig/po3tweaks.xml
Normal file
67
tests/data/fomod/moduleconfig/po3tweaks.xml
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://qconsulting.ca/fo3/ModConfig5.0.xsd">
|
||||||
|
<moduleName>powerofthree's Tweaks</moduleName>
|
||||||
|
<requiredInstallFiles>
|
||||||
|
<folder source="Required" destination="" />
|
||||||
|
</requiredInstallFiles>
|
||||||
|
<installSteps order="Explicit">
|
||||||
|
<installStep name="Main">
|
||||||
|
<optionalFileGroups order="Explicit">
|
||||||
|
<group name="DLL" type="SelectExactlyOne">
|
||||||
|
<plugins order="Explicit">
|
||||||
|
<plugin name="SSE v1.6.629+ ("Anniversary Edition")">
|
||||||
|
<description>Select this if you are using Skyrim Anniversary Edition v1.6.629 or higher.</description>
|
||||||
|
<files>
|
||||||
|
<folder source="AE/SKSE/Plugins" destination="SKSE/Plugins" priority="0" />
|
||||||
|
</files>
|
||||||
|
<typeDescriptor>
|
||||||
|
<dependencyType>
|
||||||
|
<defaultType name="Optional" />
|
||||||
|
<patterns>
|
||||||
|
<pattern>
|
||||||
|
<dependencies>
|
||||||
|
<gameDependency version="1.6" />
|
||||||
|
</dependencies>
|
||||||
|
<type name="Recommended" />
|
||||||
|
</pattern>
|
||||||
|
<pattern>
|
||||||
|
<dependencies>
|
||||||
|
<gameDependency version="1.5" />
|
||||||
|
</dependencies>
|
||||||
|
<type name="Optional" />
|
||||||
|
</pattern>
|
||||||
|
</patterns>
|
||||||
|
</dependencyType>
|
||||||
|
</typeDescriptor>
|
||||||
|
</plugin>
|
||||||
|
<plugin name="SSE v1.5.97 ("Special Edition")">
|
||||||
|
<description>Select this if you are using Skyrim Special Edition v1.5.97.</description>
|
||||||
|
<files>
|
||||||
|
<folder source="SE/SKSE/Plugins" destination="SKSE/Plugins" priority="0" />
|
||||||
|
</files>
|
||||||
|
<typeDescriptor>
|
||||||
|
<dependencyType>
|
||||||
|
<defaultType name="Optional" />
|
||||||
|
<patterns>
|
||||||
|
<pattern>
|
||||||
|
<dependencies>
|
||||||
|
<gameDependency version="1.6" />
|
||||||
|
</dependencies>
|
||||||
|
<type name="Optional" />
|
||||||
|
</pattern>
|
||||||
|
<pattern>
|
||||||
|
<dependencies>
|
||||||
|
<gameDependency version="1.5" />
|
||||||
|
</dependencies>
|
||||||
|
<type name="Recommended" />
|
||||||
|
</pattern>
|
||||||
|
</patterns>
|
||||||
|
</dependencyType>
|
||||||
|
</typeDescriptor>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</group>
|
||||||
|
</optionalFileGroups>
|
||||||
|
</installStep>
|
||||||
|
</installSteps>
|
||||||
|
</config>
|
||||||
@@ -10,171 +10,99 @@ load_order = [
|
|||||||
"ccBGSSSE037-Curios.esl",
|
"ccBGSSSE037-Curios.esl",
|
||||||
"ccBGSSSE025-AdvDSGS.esm",
|
"ccBGSSSE025-AdvDSGS.esm",
|
||||||
"_ResourcePack.esl",
|
"_ResourcePack.esl",
|
||||||
|
"RaceMenu.esp",
|
||||||
"SkyUI_SE.esp",
|
"SkyUI_SE.esp",
|
||||||
|
"RaceMenuPlugin.esp",
|
||||||
]
|
]
|
||||||
game_file_overrides = [[
|
game_file_overrides = [
|
||||||
"skse64_loader.exe",
|
"skse64_loader.exe -> SkyrimSELauncher.exe"
|
||||||
"SkyrimSELauncher.exe",
|
|
||||||
]]
|
|
||||||
|
|
||||||
[[mods]]
|
|
||||||
id = "skyui"
|
|
||||||
files = [
|
|
||||||
[
|
|
||||||
"SkyUI_SE.esp",
|
|
||||||
"Data/SkyUI_SE.esp",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"SkyUI_SE.bsa",
|
|
||||||
"Data/SkyUI_SE.bsa",
|
|
||||||
],
|
|
||||||
]
|
]
|
||||||
priority = 0
|
|
||||||
|
|
||||||
[[mods]]
|
[[mods]]
|
||||||
id = "skse"
|
id = "skse"
|
||||||
files = [
|
files = [
|
||||||
[
|
"Data/Scripts/math.pex",
|
||||||
"Data/Scripts/actorbase.pex",
|
"Data/Scripts/form.pex",
|
||||||
"Data/Scripts/actorbase.pex",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"Data/Scripts/weather.pex",
|
|
||||||
"Data/Scripts/weather.pex",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"skse64_loader.exe",
|
|
||||||
"skse64_loader.exe",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"Data/Scripts/headpart.pex",
|
|
||||||
"Data/Scripts/headpart.pex",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"Data/Scripts/soulgem.pex",
|
"Data/Scripts/soulgem.pex",
|
||||||
"Data/Scripts/soulgem.pex",
|
"Data/Scripts/formlist.pex",
|
||||||
],
|
"Data/Scripts/stringutil.pex",
|
||||||
[
|
"Data/Scripts/colorcomponent.pex",
|
||||||
"Data/Scripts/modevent.pex",
|
"Data/Scripts/quest.pex",
|
||||||
"Data/Scripts/modevent.pex",
|
"Data/Scripts/faction.pex",
|
||||||
],
|
"Data/Scripts/combatstyle.pex",
|
||||||
[
|
"Data/Scripts/actorbase.pex",
|
||||||
"Data/Scripts/actorvalueinfo.pex",
|
|
||||||
"Data/Scripts/actorvalueinfo.pex",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"Data/Scripts/book.pex",
|
|
||||||
"Data/Scripts/book.pex",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"Data/Scripts/potion.pex",
|
"Data/Scripts/potion.pex",
|
||||||
"Data/Scripts/potion.pex",
|
"Data/Scripts/actor.pex",
|
||||||
],
|
"Data/Scripts/game.pex",
|
||||||
[
|
"Data/Scripts/armor.pex",
|
||||||
"Data/Scripts/spell.pex",
|
"Data/Scripts/headpart.pex",
|
||||||
"Data/Scripts/spell.pex",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"Data/Scripts/perk.pex",
|
|
||||||
"Data/Scripts/perk.pex",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"Data/Scripts/objectreference.pex",
|
"Data/Scripts/objectreference.pex",
|
||||||
"Data/Scripts/objectreference.pex",
|
"Data/Scripts/weapon.pex",
|
||||||
],
|
"Data/Scripts/perk.pex",
|
||||||
|
"Data/Scripts/constructibleobject.pex",
|
||||||
|
"Data/Scripts/armoraddon.pex",
|
||||||
|
"Data/Scripts/textureset.pex",
|
||||||
|
"Data/Scripts/scroll.pex",
|
||||||
|
"Data/Scripts/actorvalueinfo.pex",
|
||||||
|
"Data/Scripts/equipslot.pex",
|
||||||
|
"Data/Scripts/art.pex",
|
||||||
|
"Data/Scripts/colorform.pex",
|
||||||
|
"Data/Scripts/weather.pex",
|
||||||
|
"Data/Scripts/gamedata.pex",
|
||||||
|
"Data/Scripts/skse.pex",
|
||||||
|
"Data/Scripts/sound.pex",
|
||||||
|
"Data/Scripts/formtype.pex",
|
||||||
|
"Data/Scripts/spawnertask.pex",
|
||||||
|
"Data/Scripts/netimmerse.pex",
|
||||||
|
"Data/Scripts/ingredient.pex",
|
||||||
|
"Data/Scripts/book.pex",
|
||||||
|
"Data/Scripts/ui.pex",
|
||||||
|
"Data/Scripts/leveleditem.pex",
|
||||||
|
"Data/Scripts/spell.pex",
|
||||||
|
"Data/Scripts/leveledspell.pex",
|
||||||
|
"Data/Scripts/modevent.pex",
|
||||||
|
"Data/Scripts/keyword.pex",
|
||||||
|
"Data/Scripts/activemagiceffect.pex",
|
||||||
|
"Data/Scripts/utility.pex",
|
||||||
|
"Data/Scripts/shout.pex",
|
||||||
|
"Data/Scripts/input.pex",
|
||||||
|
"Data/Scripts/race.pex",
|
||||||
|
"Data/Scripts/sounddescriptor.pex",
|
||||||
|
"Data/Scripts/wornobject.pex",
|
||||||
|
"Data/Scripts/ammo.pex",
|
||||||
|
"Data/Scripts/defaultobjectmanager.pex",
|
||||||
|
"Data/Scripts/camera.pex",
|
||||||
|
"Data/Scripts/apparatus.pex",
|
||||||
|
"skse64_1_6_1170.dll",
|
||||||
|
"Data/Scripts/magiceffect.pex",
|
||||||
|
"Data/Scripts/location.pex",
|
||||||
|
"Data/Scripts/alias.pex",
|
||||||
|
"Data/Scripts/treeobject.pex",
|
||||||
|
"Data/Scripts/leveledactor.pex",
|
||||||
|
"Data/Scripts/enchantment.pex",
|
||||||
|
"Data/Scripts/uicallback.pex",
|
||||||
|
"Data/Scripts/flora.pex",
|
||||||
|
"Data/Scripts/outfit.pex",
|
||||||
|
"Data/Scripts/cell.pex",
|
||||||
]
|
]
|
||||||
priority = 0
|
priority = 0
|
||||||
|
|
||||||
[[mods]]
|
[[mods]]
|
||||||
id = "deadly_spells"
|
id = "SkyUI-12604-35407"
|
||||||
files = [
|
files = [
|
||||||
[
|
"SkyUI_SE.bsa -> Data/SkyUI_SE.bsa",
|
||||||
"000 Core Files/textures/impactdecals/decalsnowhole01_n.dds",
|
"SkyUI_SE.esp -> Data/SkyUI_SE.esp",
|
||||||
"Data/textures/impactdecals/decalsnowhole01_n.dds",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"40 Two Fire Esp/textures/impactdecals/decalflamespread01_g.dds",
|
|
||||||
"Data/textures/impactdecals/decalflamespread01_g.dds",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"000 Core Files/textures/impactdecals/decalsparkburn01_g.dds",
|
|
||||||
"Data/textures/impactdecals/decalsparkburn01_g.dds",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"40 Two Fire Esp/textures/impactdecals/decalflamespread01.dds",
|
|
||||||
"Data/textures/impactdecals/decalflamespread01.dds",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"000 Core Files/textures/impactdecals/decalfrostimpact01_n.dds",
|
|
||||||
"Data/textures/impactdecals/decalfrostimpact01_n.dds",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"000 Core Files/textures/impactdecals/decalspitimpact01_n.dds",
|
|
||||||
"Data/textures/impactdecals/decalspitimpact01_n.dds",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"40 Two Fire Esp/DeadlySpellImpacts - Two Fire.esp",
|
|
||||||
"Data/DeadlySpellImpacts - Two Fire.esp",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"000 Core Files/textures/impactdecals/decalsnowmelt01.dds",
|
|
||||||
"Data/textures/impactdecals/decalsnowmelt01.dds",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"000 Core Files/textures/impactdecals/decalspitimpact01.dds",
|
|
||||||
"Data/textures/impactdecals/decalspitimpact01.dds",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"000 Core Files/textures/impactdecals/decalsnowhole01.dds",
|
|
||||||
"Data/textures/impactdecals/decalsnowhole01.dds",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"000 Core Files/textures/impactdecals/decalsparkburn01.dds",
|
|
||||||
"Data/textures/impactdecals/decalsparkburn01.dds",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"10 Fire Cracks/textures/impactdecals/decalflameburn01_g.dds",
|
|
||||||
"Data/textures/impactdecals/decalflameburn01_g.dds",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"000 Core Files/textures/impactdecals/decalsnowmelt01_n.dds",
|
|
||||||
"Data/textures/impactdecals/decalsnowmelt01_n.dds",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"000 Core Files/textures/impactdecals/decalfrostimpact01.dds",
|
|
||||||
"Data/textures/impactdecals/decalfrostimpact01.dds",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"10 Fire Cracks/textures/impactdecals/decalflameburn01_n.dds",
|
|
||||||
"Data/textures/impactdecals/decalflameburn01_n.dds",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"000 Core Files/textures/impactdecals/decalsnowmelt01_g.dds",
|
|
||||||
"Data/textures/impactdecals/decalsnowmelt01_g.dds",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"000 Core Files/textures/impactdecals/decalsparkburn01_n.dds",
|
|
||||||
"Data/textures/impactdecals/decalsparkburn01_n.dds",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"000 Core Files/textures/impactdecals/decalsnowhole01_g.dds",
|
|
||||||
"Data/textures/impactdecals/decalsnowhole01_g.dds",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"40 Two Fire Esp/Manual Installation of the Two Fire Option.txt",
|
|
||||||
"Data/Manual Installation of the Two Fire Option.txt",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"10 Fire Cracks/textures/impactdecals/decalflameburn01.dds",
|
|
||||||
"Data/textures/impactdecals/decalflameburn01.dds",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"40 Two Fire Esp/textures/impactdecals/decalflamespread01_n.dds",
|
|
||||||
"Data/textures/impactdecals/decalflamespread01_n.dds",
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"000 Core Files/DeadlySpellImpacts.esp",
|
|
||||||
"Data/DeadlySpellImpacts.esp",
|
|
||||||
],
|
|
||||||
]
|
]
|
||||||
priority = 1
|
priority = 0
|
||||||
|
|
||||||
|
[[mods]]
|
||||||
|
id = "racemenu-19080-465102"
|
||||||
|
files = [
|
||||||
|
"RaceMenu.esp -> Data/RaceMenu.esp",
|
||||||
|
"SKSE/Plugins/skee64.ini -> Data/SKSE/Plugins/skee64.ini",
|
||||||
|
"RaceMenu.bsa -> Data/RaceMenu.bsa",
|
||||||
|
"RaceMenuPlugin.esp -> Data/RaceMenuPlugin.esp",
|
||||||
|
"SKSE/Plugins/skee64.dll -> Data/SKSE/Plugins/skee64.dll",
|
||||||
|
]
|
||||||
|
priority = 0
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ nexus_api_key = "1234"
|
|||||||
|
|
||||||
[games.example_game]
|
[games.example_game]
|
||||||
path = "/home/user/games/sse"
|
path = "/home/user/games/sse"
|
||||||
|
kind = "sse"
|
||||||
|
|
||||||
[games.sse]
|
[games.sse]
|
||||||
path = "games/sse"
|
path = "games/sse"
|
||||||
|
kind = "unkown"
|
||||||
|
|
||||||
[instances.example1]
|
[instances.example1]
|
||||||
path = "example1.toml"
|
path = "example1.toml"
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ fn parse() {
|
|||||||
"example_04.xml",
|
"example_04.xml",
|
||||||
"example_05.xml",
|
"example_05.xml",
|
||||||
"banana.xml",
|
"banana.xml",
|
||||||
|
"po3tweaks.xml"
|
||||||
] {
|
] {
|
||||||
fomod::Config::load_from_file(get_xml(xml))
|
fomod::Config::load_from_file(get_xml(xml))
|
||||||
.unwrap_or_else(|e| panic!("Parse for {xml} with {}", err_to_string(e)));
|
.unwrap_or_else(|e| panic!("Parse for {xml} with {}", err_to_string(e)));
|
||||||
|
|||||||
27
tests/game_test.rs
Normal file
27
tests/game_test.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use fomod_manager::types::RootConfig;
|
||||||
|
|
||||||
|
fn get_parent() -> PathBuf {
|
||||||
|
PathBuf::from(file!()).parent().unwrap().to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_root() -> RootConfig {
|
||||||
|
RootConfig::load_from_file(get_parent().join("data/root_config_complex.toml")).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn export_gamefiles() {
|
||||||
|
let root_config = load_root();
|
||||||
|
|
||||||
|
let game = root_config.game_by_id("sse").expect("No game found");
|
||||||
|
|
||||||
|
let links = game.export_links().expect("Failed to export game links");
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
links.iter().all(|e| e.src().is_absolute()),
|
||||||
|
"Link src is not absolute"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(links.len(), 4, "Not all files linked");
|
||||||
|
}
|
||||||
@@ -38,14 +38,14 @@ fn parse_complex() {
|
|||||||
let unwraped = inst.expect("Asserted before");
|
let unwraped = inst.expect("Asserted before");
|
||||||
|
|
||||||
assert_eq!(unwraped.game_id(), "sse");
|
assert_eq!(unwraped.game_id(), "sse");
|
||||||
assert_eq!(unwraped.load_order().len(), 11);
|
assert_eq!(unwraped.load_order().len(), 13);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unwraped.game_file_overrides().first().unwrap(),
|
unwraped.game_file_overrides().first().unwrap(),
|
||||||
&Link::new("skse64_loader.exe", "SkyrimSELauncher.exe")
|
&Link::new("skse64_loader.exe", "SkyrimSELauncher.exe")
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(unwraped.mods().len(), 3);
|
assert_eq!(unwraped.mods().len(), 3);
|
||||||
let test_mod = unwraped.mods().iter().find(|e| e.mod_id() == "skyui");
|
let test_mod = unwraped.mods().iter().find(|e| e.mod_id() == "SkyUI-12604-35407");
|
||||||
assert!(test_mod.is_some());
|
assert!(test_mod.is_some());
|
||||||
|
|
||||||
assert_eq!(test_mod.unwrap().priority(), 0);
|
assert_eq!(test_mod.unwrap().priority(), 0);
|
||||||
|
|||||||
@@ -39,6 +39,13 @@ fn parse_complex() {
|
|||||||
"Installed game wrong path"
|
"Installed game wrong path"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
config
|
||||||
|
.game_by_id("sse")
|
||||||
|
.is_some_and(|e| e.install_location().is_absolute()),
|
||||||
|
"Relative game path was not resolved to absolute"
|
||||||
|
);
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
config
|
config
|
||||||
.game_by_id("sse")
|
.game_by_id("sse")
|
||||||
|
|||||||
Reference in New Issue
Block a user