Compare commits
85 Commits
Author | SHA1 | Date | |
---|---|---|---|
949ff87bca | |||
8685961eed | |||
7965314c6b | |||
cb70b8c415 | |||
79a1533eaa | |||
665e580c79 | |||
d5de7244e3 | |||
c2e90e17db | |||
c81518aec7 | |||
f48cd874f0 | |||
228abe5317 | |||
9efd34600a | |||
572c66cc16 | |||
cf87f7c741 | |||
dc8b01b4c8 | |||
9e8893ce75 | |||
6f92e1fb71 | |||
96c3e504f8 | |||
00c14d2a02 | |||
0378f19a75 | |||
64f4ae6429 | |||
b2cc21aed0 | |||
dc5957ea41 | |||
54abce4d30 | |||
6ae33cdcb3 | |||
c9d30fa95a | |||
dc81430f6b | |||
9c122506ce | |||
f2ba92c31d | |||
c92fb88e83 | |||
1e0e0090f5 | |||
48f6a96a01 | |||
f4fe1c6699 | |||
59f1b09337 | |||
237fa8c16f | |||
78e155408e | |||
3033dd2315 | |||
48851fa8d3 | |||
84ea4e48af | |||
b408f13cce | |||
959c3a8a61 | |||
73df20ebb6 | |||
08814a8c3e | |||
65094d4701 | |||
a20b1acdda | |||
75b1d6254b | |||
65f05a5a1c | |||
fa6bdc9f29 | |||
7595a46b44 | |||
c41a843315 | |||
a90a60ec3a | |||
209b599120 | |||
9f00b0c0e7 | |||
aaede9b6cb | |||
8a14008706 | |||
ad7e78ec47 | |||
140f4c335f | |||
3e1a266c3e | |||
92663425ef | |||
ffa379e6dd | |||
a41375a791 | |||
ac76e9bb89 | |||
546d8c4cdf | |||
12ccc9554f | |||
c117eaeb31 | |||
50c7b9fbd7 | |||
f09271c29c | |||
b71c15cb8e | |||
a3000bd2fa | |||
cc00cad4f2 | |||
6a16299cee | |||
565a69566c | |||
1727446cfc | |||
4282eb7b75 | |||
a2922540c5 | |||
601abd008f | |||
322fd2f98a | |||
e061bca7a3 | |||
87e86cc954 | |||
d5d847df75 | |||
d620bc9838 | |||
76a7d8b2c6 | |||
6c7fbd1ff1 | |||
18651a68ca | |||
79559fe46a |
40
.github/workflows/lint.yml
vendored
40
.github/workflows/lint.yml
vendored
@ -1,9 +1,3 @@
|
|||||||
# Based on https://github.com/actions-rs/meta/blob/master/recipes/quickstart.md
|
|
||||||
#
|
|
||||||
# While our "example" application has the platform-specific code,
|
|
||||||
# for simplicity we are compiling and testing everything on the Ubuntu environment only.
|
|
||||||
# For multi-OS testing see the `cross.yml` workflow.
|
|
||||||
|
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
name: lint
|
name: lint
|
||||||
@ -14,18 +8,16 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install stable toolchain
|
- name: Install stable toolchain
|
||||||
uses: actions-rs/toolchain@v1
|
uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
|
||||||
toolchain: stable
|
toolchain: stable
|
||||||
override: true
|
|
||||||
|
|
||||||
- name: Run cargo check
|
- name: Run cargo check
|
||||||
uses: actions-rs/cargo@v1
|
uses: clechasseur/rs-cargo@v2
|
||||||
continue-on-error: true # WARNING: only for this example, remove it!
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
command: check
|
command: check
|
||||||
|
|
||||||
@ -34,18 +26,16 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install stable toolchain
|
- name: Install stable toolchain
|
||||||
uses: actions-rs/toolchain@v1
|
uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
|
||||||
toolchain: stable
|
toolchain: stable
|
||||||
override: true
|
|
||||||
|
|
||||||
- name: Run cargo test
|
- name: Run cargo test
|
||||||
uses: actions-rs/cargo@v1
|
uses: clechasseur/rs-cargo@v2
|
||||||
continue-on-error: true # WARNING: only for this example, remove it!
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
command: test
|
command: test
|
||||||
|
|
||||||
@ -54,26 +44,24 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install stable toolchain
|
- name: Install stable toolchain
|
||||||
uses: actions-rs/toolchain@v1
|
uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
|
||||||
toolchain: stable
|
toolchain: stable
|
||||||
override: true
|
|
||||||
components: rustfmt, clippy
|
components: rustfmt, clippy
|
||||||
|
|
||||||
- name: Run cargo fmt
|
- name: Run cargo fmt
|
||||||
uses: actions-rs/cargo@v1
|
uses: clechasseur/rs-cargo@v2
|
||||||
continue-on-error: true # WARNING: only for this example, remove it!
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
command: fmt
|
command: fmt
|
||||||
args: --all -- --check
|
args: --all -- --check
|
||||||
|
|
||||||
- name: Run cargo clippy
|
- name: Run cargo clippy
|
||||||
uses: actions-rs/cargo@v1
|
uses: clechasseur/rs-cargo@v2
|
||||||
continue-on-error: true # WARNING: only for this example, remove it!
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
command: clippy
|
command: clippy
|
||||||
args: -- -D warnings
|
args: -- -D warnings
|
||||||
|
17
.github/workflows/release.yml
vendored
17
.github/workflows/release.yml
vendored
@ -9,7 +9,7 @@ jobs:
|
|||||||
create-release:
|
create-release:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- uses: taiki-e/create-gh-release-action@v1
|
- uses: taiki-e/create-gh-release-action@v1
|
||||||
with:
|
with:
|
||||||
draft: true
|
draft: true
|
||||||
@ -18,14 +18,21 @@ jobs:
|
|||||||
upload-assets:
|
upload-assets:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os:
|
include:
|
||||||
- ubuntu-20.04
|
- target: x86_64-unknown-linux-gnu
|
||||||
- windows-latest
|
os: ubuntu-20.04
|
||||||
|
- target: i686-unknown-linux-gnu
|
||||||
|
os: ubuntu-20.04
|
||||||
|
- target: x86_64-pc-windows-msvc
|
||||||
|
os: windows-latest
|
||||||
|
- target: i686-pc-windows-msvc
|
||||||
|
os: windows-latest
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- uses: taiki-e/upload-rust-binary-action@v1
|
- uses: taiki-e/upload-rust-binary-action@v1
|
||||||
with:
|
with:
|
||||||
|
target: ${{ matrix.target }}
|
||||||
bin: alterware-launcher
|
bin: alterware-launcher
|
||||||
tar: unix
|
tar: unix
|
||||||
zip: windows
|
zip: windows
|
||||||
|
436
Cargo.lock
generated
436
Cargo.lock
generated
@ -2,6 +2,23 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "adler"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aes"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cipher",
|
||||||
|
"cpufeatures",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "1.0.3"
|
version = "1.0.3"
|
||||||
@ -13,8 +30,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "alterware-launcher"
|
name = "alterware-launcher"
|
||||||
version = "0.4.3"
|
version = "0.5.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"colored",
|
||||||
"http_req",
|
"http_req",
|
||||||
"mslnk",
|
"mslnk",
|
||||||
"rand",
|
"rand",
|
||||||
@ -24,15 +42,21 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"sha1_smol",
|
"sha1_smol",
|
||||||
"steamlocate",
|
"steamlocate",
|
||||||
"windows-sys",
|
|
||||||
"winres",
|
"winres",
|
||||||
|
"zip",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.13.1"
|
version = "0.21.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64ct"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
@ -67,11 +91,35 @@ version = "1.4.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bzip2"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8"
|
||||||
|
dependencies = [
|
||||||
|
"bzip2-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bzip2-sys"
|
||||||
|
version = "0.1.11+1.0.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.79"
|
version = "1.0.79"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
||||||
|
dependencies = [
|
||||||
|
"jobserver",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
@ -79,6 +127,33 @@ version = "1.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cipher"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
||||||
|
dependencies = [
|
||||||
|
"crypto-common",
|
||||||
|
"inout",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colored"
|
||||||
|
version = "2.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6"
|
||||||
|
dependencies = [
|
||||||
|
"is-terminal",
|
||||||
|
"lazy_static",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "constant_time_eq"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.2.9"
|
version = "0.2.9"
|
||||||
@ -88,6 +163,24 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crc32fast"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.8.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-common"
|
name = "crypto-common"
|
||||||
version = "0.1.6"
|
version = "0.1.6"
|
||||||
@ -98,6 +191,12 @@ dependencies = [
|
|||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deranged"
|
||||||
|
version = "0.3.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.7"
|
version = "0.10.7"
|
||||||
@ -106,27 +205,27 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"block-buffer",
|
"block-buffer",
|
||||||
"crypto-common",
|
"crypto-common",
|
||||||
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dirs"
|
name = "dirs"
|
||||||
version = "5.0.1"
|
version = "3.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
|
checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dirs-sys",
|
"dirs-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dirs-sys"
|
name = "dirs-sys"
|
||||||
version = "0.4.1"
|
version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
|
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"option-ext",
|
|
||||||
"redox_users",
|
"redox_users",
|
||||||
"windows-sys",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -165,6 +264,16 @@ version = "2.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764"
|
checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flate2"
|
||||||
|
version = "1.0.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010"
|
||||||
|
dependencies = [
|
||||||
|
"crc32fast",
|
||||||
|
"miniz_oxide",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generic-array"
|
name = "generic-array"
|
||||||
version = "0.14.7"
|
version = "0.14.7"
|
||||||
@ -187,17 +296,42 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http_req"
|
name = "hermit-abi"
|
||||||
version = "0.9.2"
|
version = "0.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9f680177f2ebe4aabd573d07b322d15a5e0fbc97cd739fd627b08043c89041f8"
|
checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hmac"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "http_req"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "158d4edacc70c9bdb0464314063b8d9d60fa776442dc13b00a13581b88b0a0a0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustls",
|
"rustls",
|
||||||
|
"rustls-pemfile",
|
||||||
"unicase",
|
"unicase",
|
||||||
"webpki",
|
"webpki",
|
||||||
"webpki-roots",
|
"webpki-roots",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inout"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "instant"
|
name = "instant"
|
||||||
version = "0.1.12"
|
version = "0.1.12"
|
||||||
@ -207,12 +341,32 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is-terminal"
|
||||||
|
version = "0.4.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"rustix",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
|
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jobserver"
|
||||||
|
version = "0.1.26"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.63"
|
version = "0.3.63"
|
||||||
@ -247,6 +401,12 @@ dependencies = [
|
|||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.147"
|
version = "0.2.147"
|
||||||
@ -271,6 +431,15 @@ version = "2.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miniz_oxide"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
|
||||||
|
dependencies = [
|
||||||
|
"adler",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mslnk"
|
name = "mslnk"
|
||||||
version = "0.1.8"
|
version = "0.1.8"
|
||||||
@ -282,12 +451,6 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "nom"
|
|
||||||
version = "1.2.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a5b8c256fd9471521bcb84c3cdba98921497f1a331cbc15b8030fc63b82050ce"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.17.2"
|
version = "1.17.2"
|
||||||
@ -295,10 +458,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "9670a07f94779e00908f3e686eab508878ebb390ba6e604d3a284c00e8d0487b"
|
checksum = "9670a07f94779e00908f3e686eab508878ebb390ba6e604d3a284c00e8d0487b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "option-ext"
|
name = "password-hash"
|
||||||
version = "0.2.0"
|
version = "0.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700"
|
||||||
|
dependencies = [
|
||||||
|
"base64ct",
|
||||||
|
"rand_core",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "paste"
|
name = "paste"
|
||||||
@ -306,6 +474,18 @@ version = "1.0.14"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
|
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pbkdf2"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
"hmac",
|
||||||
|
"password-hash",
|
||||||
|
"sha2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pest"
|
name = "pest"
|
||||||
version = "2.7.2"
|
version = "2.7.2"
|
||||||
@ -350,6 +530,12 @@ dependencies = [
|
|||||||
"sha2",
|
"sha2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkg-config"
|
||||||
|
version = "0.3.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
@ -471,12 +657,26 @@ dependencies = [
|
|||||||
"cc",
|
"cc",
|
||||||
"libc",
|
"libc",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"spin",
|
"spin 0.5.2",
|
||||||
"untrusted",
|
"untrusted 0.7.1",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ring"
|
||||||
|
version = "0.17.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "911b295d2d302948838c8ac142da1ee09fa7863163b44e6715bc9357905878b8"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"getrandom",
|
||||||
|
"libc",
|
||||||
|
"spin 0.9.8",
|
||||||
|
"untrusted 0.9.0",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.8"
|
version = "0.38.8"
|
||||||
@ -492,15 +692,33 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls"
|
name = "rustls"
|
||||||
version = "0.19.1"
|
version = "0.21.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7"
|
checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"ring 0.16.20",
|
||||||
|
"rustls-webpki",
|
||||||
|
"sct",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-pemfile"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"log",
|
]
|
||||||
"ring",
|
|
||||||
"sct",
|
[[package]]
|
||||||
"webpki",
|
name = "rustls-webpki"
|
||||||
|
version = "0.101.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe"
|
||||||
|
dependencies = [
|
||||||
|
"ring 0.16.20",
|
||||||
|
"untrusted 0.7.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -511,19 +729,19 @@ checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sct"
|
name = "sct"
|
||||||
version = "0.6.1"
|
version = "0.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce"
|
checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ring",
|
"ring 0.16.20",
|
||||||
"untrusted",
|
"untrusted 0.7.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "self-replace"
|
name = "self-replace"
|
||||||
version = "1.3.5"
|
version = "1.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a0e7c919783db74b5995f13506069227e4721d388bea4a8ac3055acac864ac16"
|
checksum = "525db198616b2bcd0f245daf7bfd8130222f7ee6af9ff9984c19a61bf1160c55"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fastrand 1.9.0",
|
"fastrand 1.9.0",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
@ -532,24 +750,24 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "1.0.18"
|
version = "1.0.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
|
checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.183"
|
version = "1.0.189"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c"
|
checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.183"
|
version = "1.0.189"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816"
|
checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -558,15 +776,26 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.104"
|
version = "1.0.107"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c"
|
checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha1"
|
||||||
|
version = "0.10.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha1_smol"
|
name = "sha1_smol"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@ -591,27 +820,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "steamlocate"
|
name = "spin"
|
||||||
version = "1.2.1"
|
version = "0.9.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4ec01c74611d14a808cb212d17c6e03f0e30736a15ed1d5736f8a53154cea3ae"
|
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "steamlocate"
|
||||||
|
version = "2.0.0-alpha.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b1568c4a70a26c4373fe1131ffa4eff055459631b6e40c6bc118615f2d870c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dirs",
|
"dirs",
|
||||||
"keyvalues-parser",
|
"keyvalues-parser",
|
||||||
"keyvalues-serde",
|
"keyvalues-serde",
|
||||||
"serde",
|
"serde",
|
||||||
"steamy-vdf",
|
|
||||||
"winreg",
|
"winreg",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "steamy-vdf"
|
name = "subtle"
|
||||||
version = "0.2.0"
|
version = "2.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "533127ad49314bfe71c3d3fd36b3ebac3d24f40618092e70e1cfe8362c7fac79"
|
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
|
||||||
dependencies = [
|
|
||||||
"nom",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
@ -657,6 +888,23 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time"
|
||||||
|
version = "0.3.28"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "17f6bb557fd245c28e6411aa56b6403c689ad95061f50e4be16c274e70a17e48"
|
||||||
|
dependencies = [
|
||||||
|
"deranged",
|
||||||
|
"serde",
|
||||||
|
"time-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time-core"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "0.5.11"
|
version = "0.5.11"
|
||||||
@ -680,9 +928,9 @@ checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicase"
|
name = "unicase"
|
||||||
version = "2.6.0"
|
version = "2.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
|
checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
@ -699,6 +947,12 @@ version = "0.7.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "untrusted"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.4"
|
version = "0.9.4"
|
||||||
@ -777,22 +1031,19 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webpki"
|
name = "webpki"
|
||||||
version = "0.21.4"
|
version = "0.22.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea"
|
checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ring",
|
"ring 0.17.2",
|
||||||
"untrusted",
|
"untrusted 0.9.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webpki-roots"
|
name = "webpki-roots"
|
||||||
version = "0.21.1"
|
version = "0.25.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940"
|
checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc"
|
||||||
dependencies = [
|
|
||||||
"webpki",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
@ -884,11 +1135,10 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winreg"
|
name = "winreg"
|
||||||
version = "0.11.0"
|
version = "0.10.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "76a1a57ff50e9b408431e8f97d5456f2807f8eb2a2cd79b06068fc87f8ecf189"
|
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -900,3 +1150,53 @@ checksum = "b68db261ef59e9e52806f688020631e987592bd83619edccda9c47d42cde4f6c"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"toml",
|
"toml",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zip"
|
||||||
|
version = "0.6.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261"
|
||||||
|
dependencies = [
|
||||||
|
"aes",
|
||||||
|
"byteorder",
|
||||||
|
"bzip2",
|
||||||
|
"constant_time_eq",
|
||||||
|
"crc32fast",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"flate2",
|
||||||
|
"hmac",
|
||||||
|
"pbkdf2",
|
||||||
|
"sha1",
|
||||||
|
"time",
|
||||||
|
"zstd",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zstd"
|
||||||
|
version = "0.11.2+zstd.1.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4"
|
||||||
|
dependencies = [
|
||||||
|
"zstd-safe",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zstd-safe"
|
||||||
|
version = "5.0.2+zstd.1.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"zstd-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zstd-sys"
|
||||||
|
version = "2.0.8+zstd.1.5.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
20
Cargo.toml
20
Cargo.toml
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "alterware-launcher"
|
name = "alterware-launcher"
|
||||||
version = "0.4.3"
|
version = "0.5.4"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
build = "res/build.rs"
|
build = "res/build.rs"
|
||||||
|
|
||||||
@ -15,23 +15,21 @@ panic = "abort"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
http_req = { version = "0.9.2", default-features = false, features = [
|
http_req = { version = "0.10.0", default-features = false, features = [
|
||||||
"rust-tls",
|
"rust-tls",
|
||||||
] }
|
] }
|
||||||
sha1_smol = "1.0.0"
|
sha1_smol = "1.0.0"
|
||||||
serde = { version = "1.0.183", features = ["derive"] }
|
serde = { version = "1.0.189", features = ["derive"] }
|
||||||
serde_json = "1.0.104"
|
serde_json = "1.0.107"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
semver = "1.0.18"
|
semver = "1.0.20"
|
||||||
|
zip = "0.6.6"
|
||||||
|
colored = "2.0.4"
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
steamlocate = "1.2.1"
|
steamlocate = "2.0.0-alpha.0"
|
||||||
mslnk = "0.1.8"
|
mslnk = "0.1.8"
|
||||||
# https://github.com/mitsuhiko/self-replace/pull/16/
|
self-replace = "1.3.7"
|
||||||
windows-sys = { version = "0.48", features = [
|
|
||||||
"Win32_Security",
|
|
||||||
] }
|
|
||||||
self-replace = "1.3.5"
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
winres = "0.1.12"
|
winres = "0.1.12"
|
||||||
|
94
README.md
94
README.md
@ -1,14 +1,94 @@
|
|||||||
# alterware-launcher
|
# AlterWare Launcher
|
||||||
|
|
||||||
1. Download [latest release](https://github.com/mxve/alterware-launcher/releases/latest/download/alterware-launcher-x86_64-pc-windows-msvc.zip)
|
### [AlterWare.dev](https://alterware.dev)
|
||||||
2. Unpack the archive and place alterware-launcher.exe in the game directory
|
|
||||||
|
##### IW4x | IW4-SP | IW5-Mod | IW6-Mod | S1-Mod
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Installation
|
||||||
|
|
||||||
|
1. Download the [latest alterware-launcher.exe](https://github.com/mxve/alterware-launcher/releases/latest/download/alterware-launcher.exe)
|
||||||
|
2. Place alterware-launcher.exe in the game directory
|
||||||
3. Run alterware-launcher.exe, after updating the game will launch automatically
|
3. Run alterware-launcher.exe, after updating the game will launch automatically
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
- Passing ```iw4-sp```, ```iw5-mod```, ```iw6-mod``` or ```s1-mod``` as the first argument will skip automatic game detection
|
#### Command line arguments
|
||||||
- Passing ```update``` will stop the launcher from launching the game
|
|
||||||
- ```skip-launcher-update``` skips self-update
|
- ```iw4-sp```, ```iw4x```, ```iw5-mod```, ```iw6-mod```, ```s1-mod```
|
||||||
|
- Skip automatic detection and launch the specified game
|
||||||
|
- This should always be the first argument if used
|
||||||
|
- ```--update```, ```-u```
|
||||||
|
- Only update the game, don't launch it
|
||||||
|
- ```--skip-launcher-update```
|
||||||
|
- Don't update the launcher
|
||||||
|
- ```--bonus```
|
||||||
|
- Download bonus content
|
||||||
|
- ```--force```, ```-f```
|
||||||
|
- Force file hash recheck
|
||||||
|
- ```--path```, ```-p```
|
||||||
|
- Set the game path
|
||||||
|
- Do not include a trailing backslash in the path
|
||||||
|
- ```--pass```
|
||||||
|
- Pass additional arguments to the game
|
||||||
|
- See [client-args.md](client-args.md)
|
||||||
|
- ```--version```, ```-v```
|
||||||
|
- Print the launcher version
|
||||||
|
|
||||||
|
Example: ```alterware-launcher.exe iw4x --bonus -u --path "C:\Games\IW4x" --pass "-console"```
|
||||||
|
|
||||||
|
Some arguments can be set in alterware-launcher.json, args generally override the values of the config.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Config file
|
||||||
|
alterware-launcher.json
|
||||||
|
|
||||||
|
- ```update_only```
|
||||||
|
- See --update
|
||||||
|
- Default: false
|
||||||
|
- ```skip_self_update```
|
||||||
|
- See --skip-launcher-update
|
||||||
|
- Default: false
|
||||||
|
- ```download_bonus_content```
|
||||||
|
- See --bonus
|
||||||
|
- Default: false
|
||||||
|
- ```ask_bonus_content```
|
||||||
|
- Ask the user if they want to download bonus content
|
||||||
|
- Default: true; false after asking
|
||||||
|
- ```force_update```
|
||||||
|
- See --force
|
||||||
|
- Default: false
|
||||||
|
- ```args```
|
||||||
|
- See --pass
|
||||||
|
- Default: ""
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Support
|
||||||
|
|
||||||
|
Visit the [AlterWare Forum](https://forum.alterware.dev/) or [Discord](https://discord.gg/2ETE8engZM) for support.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Building from Source
|
||||||
|
|
||||||
|
- [Install Rust](https://rustup.rs/)
|
||||||
|
- Clone the repository
|
||||||
|
- Run ```cargo build --release```
|
||||||
|
- The executable will be located in ```target/release```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### Note for server owners:
|
### Note for server owners:
|
||||||
When the launcher updates itself it needs to be restarted. It will return exit code 101 in this case.
|
When the launcher updates itself it needs to be restarted. It will return exit code 201 in this case.
|
||||||
|
|
||||||
|
```
|
||||||
|
@echo off
|
||||||
|
:loop
|
||||||
|
start /wait alterware-launcher.exe --update
|
||||||
|
if %errorlevel% equ 201 (
|
||||||
|
goto loop
|
||||||
|
)
|
||||||
|
```
|
32
client-args.md
Normal file
32
client-args.md
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# IW4x
|
||||||
|
[github.com/iw4x/iw4x-client#command-line-arguments](https://github.com/iw4x/iw4x-client#command-line-arguments)
|
||||||
|
|
||||||
|
| Argument | Description |
|
||||||
|
|:------------------------|:-----------------------------------------------|
|
||||||
|
| `-tests` | Perform unit tests. |
|
||||||
|
| `-entries` | Print to the console a list of every asset as they are loaded from zonefiles. |
|
||||||
|
| `-stdout` | Redirect all logging output to the terminal iw4x is started from, or if there is none, creates a new terminal window to write log information in. |
|
||||||
|
| `-console` | Allow the game to display its own separate interactive console window. |
|
||||||
|
| `-dedicated` | Starts the game as a headless dedicated server. |
|
||||||
|
| `-bigminidumps` | Include all code sections from loaded modules in the dump. |
|
||||||
|
| `-reallybigminidumps` | Include data sections from all loaded modules in the dump. |
|
||||||
|
| `-dump` | Write info of loaded assets to the raw folder as they are being loaded. |
|
||||||
|
| `-nointro` | Skip game's cinematic intro. |
|
||||||
|
| `-version` | Print IW4x build info on startup. |
|
||||||
|
| `-nosteam` | Disable friends feature and do not update Steam about the game's current status just like an invisible mode. |
|
||||||
|
| `-unprotect-dvars` | Allow the server to modify saved/archive dvars. |
|
||||||
|
| `-zonebuilder` | Start the interactive zonebuilder tool console instead of starting the game. |
|
||||||
|
| `-disable-notifies` | Disable "Anti-CFG" checks |
|
||||||
|
| `-disable-mongoose` | Disable Mongoose HTTP server |
|
||||||
|
| `-disable-rate-limit-check` | Disable RCOn rate limit checks |
|
||||||
|
| `+<command>` | Execute game command (ex. `+set net_port 1337`)|
|
||||||
|
|
||||||
|
|
||||||
|
# S1-Mod, IW6-Mod
|
||||||
|
| Argument | Description |
|
||||||
|
|:------------------------|:-----------------------------------------------|
|
||||||
|
| `-headless` | Use system console |
|
||||||
|
| `-dedicated` | Dedicated server |
|
||||||
|
| `-singleplayer` | Start singleplayer; Skip launcher |
|
||||||
|
| `-multiplayer` | Start multiplayer; Skip launcher |
|
||||||
|
| `+<command>` | Execute game command (ex. `+set net_port 1337`)|
|
42
src/config.rs
Normal file
42
src/config.rs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
use crate::structs::Config;
|
||||||
|
|
||||||
|
use std::{fs, path::PathBuf};
|
||||||
|
|
||||||
|
pub fn load(config_path: PathBuf) -> Config {
|
||||||
|
if config_path.exists() {
|
||||||
|
let cfg = fs::read_to_string(&config_path).unwrap();
|
||||||
|
let cfg: Config = serde_json::from_str(&cfg).unwrap_or(Config::default());
|
||||||
|
return cfg;
|
||||||
|
}
|
||||||
|
save(config_path.clone(), Config::default());
|
||||||
|
Config::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save(config_path: PathBuf, config: Config) {
|
||||||
|
match fs::write(
|
||||||
|
config_path.clone(),
|
||||||
|
serde_json::to_string_pretty(&config).unwrap(),
|
||||||
|
) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => match e.kind() {
|
||||||
|
std::io::ErrorKind::NotFound => {
|
||||||
|
fs::create_dir_all(config_path.parent().unwrap()).unwrap();
|
||||||
|
save(config_path, config);
|
||||||
|
}
|
||||||
|
_ => println!("Could not save config file, got:\n{}\n", e),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save_value(config_path: PathBuf, key: &str, value: bool) {
|
||||||
|
let mut config = load(config_path.clone());
|
||||||
|
match key {
|
||||||
|
"update_only" => config.update_only = value,
|
||||||
|
"skip_self_update" => config.skip_self_update = value,
|
||||||
|
"download_bonus_content" => config.download_bonus_content = value,
|
||||||
|
"ask_bonus_content" => config.ask_bonus_content = value,
|
||||||
|
"force_update" => config.force_update = value,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
save(config_path, config);
|
||||||
|
}
|
22
src/github.rs
Normal file
22
src/github.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
use semver::Version;
|
||||||
|
|
||||||
|
pub fn latest_tag(owner: &str, repo: &str) -> String {
|
||||||
|
let github_body = crate::http::get_body_string(
|
||||||
|
format!(
|
||||||
|
"https://api.github.com/repos/{}/{}/releases/latest",
|
||||||
|
owner, repo
|
||||||
|
)
|
||||||
|
.as_str(),
|
||||||
|
);
|
||||||
|
let github_json: serde_json::Value = serde_json::from_str(&github_body).unwrap();
|
||||||
|
github_json["tag_name"].to_string().replace('"', "")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn latest_version(owner: &str, repo: &str) -> Version {
|
||||||
|
let tag = latest_tag(owner, repo).replace('v', "");
|
||||||
|
Version::parse(&tag).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn latest_release_url(owner: &str, repo: &str) -> String {
|
||||||
|
format!("https://github.com/{}/{}/releases/latest", owner, repo)
|
||||||
|
}
|
5
src/global.rs
Normal file
5
src/global.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
pub const MASTER: &str = "https://master.alterware.dev";
|
||||||
|
pub const GH_OWNER: &str = "mxve";
|
||||||
|
pub const GH_REPO: &str = "alterware-launcher";
|
||||||
|
pub const GH_IW4X_OWNER: &str = "iw4x";
|
||||||
|
pub const GH_IW4X_REPO: &str = "iw4x-client";
|
76
src/http.rs
76
src/http.rs
@ -1,20 +1,40 @@
|
|||||||
|
use crate::global;
|
||||||
|
use crate::misc;
|
||||||
use std::{fs, io::Write, path::Path, str};
|
use std::{fs, io::Write, path::Path, str};
|
||||||
|
|
||||||
pub fn get_body(url: &str) -> Vec<u8> {
|
pub fn get_body(url: &str) -> Vec<u8> {
|
||||||
let mut res: Vec<u8> = Vec::new();
|
let mut res: Vec<u8> = Vec::new();
|
||||||
let req = http_req::request::Request::new(&url.try_into().unwrap())
|
|
||||||
|
match http_req::request::Request::new(&url.try_into().unwrap())
|
||||||
.header(
|
.header(
|
||||||
"User-Agent",
|
"User-Agent",
|
||||||
"AlterWare Launcher | github.com/mxve/alterware-launcher",
|
&format!(
|
||||||
|
"AlterWare Launcher | github.com/{}/{}",
|
||||||
|
global::GH_OWNER,
|
||||||
|
global::GH_REPO
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.send(&mut res)
|
.send(&mut res)
|
||||||
.unwrap_or_else(|error| {
|
{
|
||||||
panic!("\n\n{}:\n{:?}", "Error", error);
|
Ok(req) => {
|
||||||
});
|
if req.status_code() == http_req::response::StatusCode::new(302)
|
||||||
|
|| req.status_code() == http_req::response::StatusCode::new(301)
|
||||||
|
{
|
||||||
|
let location = req.headers().get("Location").unwrap().as_str();
|
||||||
|
return get_body(location);
|
||||||
|
}
|
||||||
|
|
||||||
if req.status_code() == http_req::response::StatusCode::new(302) {
|
if req.status_code() != http_req::response::StatusCode::new(200) {
|
||||||
let location = req.headers().get("Location").unwrap().as_str();
|
misc::fatal_error(&format!(
|
||||||
return get_body(location);
|
"Could not get body from {}, got {}",
|
||||||
|
url,
|
||||||
|
req.status_code()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
misc::fatal_error(&format!("Could not get body from {}, got:\n{}", url, e));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res
|
res
|
||||||
@ -27,10 +47,38 @@ pub fn get_body_string(url: &str) -> String {
|
|||||||
pub fn download_file(url: &str, file_path: &Path) {
|
pub fn download_file(url: &str, file_path: &Path) {
|
||||||
let body = get_body(url);
|
let body = get_body(url);
|
||||||
|
|
||||||
let mut f = fs::File::create(file_path).unwrap_or_else(|error| {
|
match fs::File::create(file_path) {
|
||||||
panic!("\n\n{}:\n{:?}", "Error", error);
|
Ok(mut file) => match file.write_all(&body) {
|
||||||
});
|
Ok(_) => (),
|
||||||
f.write_all(&body).unwrap_or_else(|error| {
|
Err(e) => {
|
||||||
panic!("\n\n{}:\n{:?}", "Error", error);
|
misc::fatal_error(&format!(
|
||||||
});
|
"Could not write to file {}, got:\n{}",
|
||||||
|
file_path.to_str().unwrap(),
|
||||||
|
e
|
||||||
|
));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
match e.kind() {
|
||||||
|
std::io::ErrorKind::NotFound => {
|
||||||
|
fs::create_dir_all(file_path.parent().unwrap()).unwrap();
|
||||||
|
return download_file(url, file_path);
|
||||||
|
}
|
||||||
|
std::io::ErrorKind::PermissionDenied => {
|
||||||
|
misc::fatal_error(&format!(
|
||||||
|
"Permission to {} denied.\n Please try:\n 1. Running the launcher as administrator.\n 2. Manually deleting the last downloaded file.\n 3. If your game is in the program files directory try moving it to another location.\n 4. Create an exception/exclusion in your Anti-Virus Software for either the last downloaded file or the entire game directory.\n\n\n{}",
|
||||||
|
file_path.to_str().unwrap(),
|
||||||
|
e
|
||||||
|
));
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
misc::fatal_error(&format!(
|
||||||
|
"Could not create file {}, got:\n{}",
|
||||||
|
file_path.to_str().unwrap(),
|
||||||
|
e
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
22
src/io.rs
Normal file
22
src/io.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
use std::{fs, path::Path};
|
||||||
|
|
||||||
|
pub fn unzip(zip_path: &Path, out_path: &Path) {
|
||||||
|
let mut archive = zip::ZipArchive::new(fs::File::open(zip_path).unwrap()).unwrap();
|
||||||
|
for i in 0..archive.len() {
|
||||||
|
let mut file = archive.by_index(i).unwrap();
|
||||||
|
let outpath = out_path.join(file.name());
|
||||||
|
|
||||||
|
if (*file.name()).ends_with('/') {
|
||||||
|
fs::create_dir_all(outpath).unwrap();
|
||||||
|
} else {
|
||||||
|
println!("Unpacking {}", file.name());
|
||||||
|
if let Some(p) = outpath.parent() {
|
||||||
|
if !p.exists() {
|
||||||
|
fs::create_dir_all(p).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut outfile = fs::File::create(&outpath).unwrap();
|
||||||
|
std::io::copy(&mut file, &mut outfile).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
42
src/iw4x.rs
Normal file
42
src/iw4x.rs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
use crate::github;
|
||||||
|
use crate::global::*;
|
||||||
|
use crate::http;
|
||||||
|
use crate::misc;
|
||||||
|
|
||||||
|
use colored::*;
|
||||||
|
use std::{fs, path::Path};
|
||||||
|
|
||||||
|
pub fn local_revision(dir: &Path) -> u16 {
|
||||||
|
if let Ok(revision) = fs::read_to_string(dir.join(".iw4xrevision")) {
|
||||||
|
misc::rev_to_int(&revision)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remote_revision() -> u16 {
|
||||||
|
misc::rev_to_int(&github::latest_tag(GH_IW4X_OWNER, GH_IW4X_REPO))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(dir: &Path) {
|
||||||
|
let remote = remote_revision();
|
||||||
|
let local = local_revision(dir);
|
||||||
|
|
||||||
|
if remote <= local && dir.join("iw4x.dll").exists() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"[{}] {}",
|
||||||
|
"Downloading".bright_yellow(),
|
||||||
|
dir.join("iw4x.dll").display()
|
||||||
|
);
|
||||||
|
http::download_file(
|
||||||
|
&format!(
|
||||||
|
"{}/download/iw4x.dll",
|
||||||
|
github::latest_release_url(GH_IW4X_OWNER, GH_IW4X_REPO)
|
||||||
|
),
|
||||||
|
&dir.join("iw4x.dll"),
|
||||||
|
);
|
||||||
|
fs::write(dir.join(".iw4xrevision"), format!("r{}", remote)).unwrap();
|
||||||
|
}
|
581
src/main.rs
581
src/main.rs
@ -1,122 +1,32 @@
|
|||||||
|
mod config;
|
||||||
|
mod github;
|
||||||
|
mod global;
|
||||||
mod http;
|
mod http;
|
||||||
|
mod iw4x;
|
||||||
|
mod misc;
|
||||||
|
mod self_update;
|
||||||
|
mod structs;
|
||||||
|
|
||||||
|
use global::*;
|
||||||
|
use structs::*;
|
||||||
|
|
||||||
|
use colored::*;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use mslnk::ShellLink;
|
use mslnk::ShellLink;
|
||||||
use semver::Version;
|
use std::{borrow::Cow, collections::HashMap, fs, path::Path, path::PathBuf};
|
||||||
use std::{fs, path::PathBuf};
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
use std::{thread, time};
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use steamlocate::SteamDir;
|
use steamlocate::SteamDir;
|
||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
|
||||||
struct CdnFile {
|
|
||||||
name: String,
|
|
||||||
size: u32,
|
|
||||||
hash: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
|
||||||
struct Game<'a> {
|
|
||||||
engine: &'a str,
|
|
||||||
client: Vec<&'a str>,
|
|
||||||
references: Vec<&'a str>,
|
|
||||||
app_id: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
const MASTER: &str = "https://master.alterware.dev";
|
|
||||||
const REPO: &str = "mxve/alterware-launcher";
|
|
||||||
|
|
||||||
fn get_file_sha1(path: &PathBuf) -> String {
|
|
||||||
let mut sha1 = sha1_smol::Sha1::new();
|
|
||||||
sha1.update(&fs::read(path).unwrap());
|
|
||||||
sha1.digest().to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_input() -> String {
|
|
||||||
let mut input = String::new();
|
|
||||||
std::io::stdin().read_line(&mut input).unwrap();
|
|
||||||
input.trim().to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn self_update_available() -> bool {
|
|
||||||
let current_version: Version = Version::parse(env!("CARGO_PKG_VERSION")).unwrap();
|
|
||||||
let github_body = http::get_body_string(
|
|
||||||
format!("https://api.github.com/repos/{}/releases/latest", REPO).as_str(),
|
|
||||||
);
|
|
||||||
let github_json: serde_json::Value = serde_json::from_str(&github_body).unwrap();
|
|
||||||
let latest_version = github_json["tag_name"]
|
|
||||||
.to_string()
|
|
||||||
.replace(['v', '"'].as_ref(), "");
|
|
||||||
let latest_version = Version::parse(&latest_version).unwrap();
|
|
||||||
|
|
||||||
current_version < latest_version
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
fn self_update(_update_only: bool) {
|
|
||||||
if self_update_available() {
|
|
||||||
println!("A new version of the AlterWare launcher is available.");
|
|
||||||
println!("Download it at https://github.com/{}/releases/latest", REPO);
|
|
||||||
println!("Launching in 10 seconds..");
|
|
||||||
thread::sleep(time::Duration::from_secs(10));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
fn self_update(update_only: bool) {
|
|
||||||
let working_dir = std::env::current_dir().unwrap();
|
|
||||||
let files = fs::read_dir(&working_dir).unwrap();
|
|
||||||
|
|
||||||
for file in files {
|
|
||||||
let file = file.unwrap();
|
|
||||||
let file_name = file.file_name().into_string().unwrap();
|
|
||||||
|
|
||||||
if file_name.contains("alterware-launcher")
|
|
||||||
&& (file_name.contains(".__relocated__.exe")
|
|
||||||
|| file_name.contains(".__selfdelete__.exe"))
|
|
||||||
{
|
|
||||||
fs::remove_file(file.path()).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self_update_available() {
|
|
||||||
println!("Performing launcher self-update.");
|
|
||||||
println!("If you run into any issues, please download the latest version at https://github.com/{}/releases/latest", REPO);
|
|
||||||
|
|
||||||
let update_binary = PathBuf::from("alterware-launcher-update.exe");
|
|
||||||
let file_path = working_dir.join(&update_binary);
|
|
||||||
|
|
||||||
if update_binary.exists() {
|
|
||||||
fs::remove_file(&update_binary).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
http::download_file(
|
|
||||||
&format!(
|
|
||||||
"https://github.com/{}/releases/latest/download/alterware-launcher.exe",
|
|
||||||
REPO
|
|
||||||
),
|
|
||||||
&file_path,
|
|
||||||
);
|
|
||||||
|
|
||||||
if !file_path.exists() {
|
|
||||||
println!("Failed to download launcher update.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self_replace::self_replace("alterware-launcher-update.exe").unwrap();
|
|
||||||
fs::remove_file(&file_path).unwrap();
|
|
||||||
println!("Launcher updated. Please run it again.");
|
|
||||||
if !update_only {
|
|
||||||
std::io::stdin().read_line(&mut String::new()).unwrap();
|
|
||||||
}
|
|
||||||
std::process::exit(101);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn get_installed_games(games: &Vec<Game>) -> Vec<(u32, PathBuf)> {
|
fn get_installed_games(games: &Vec<Game>) -> Vec<(u32, PathBuf)> {
|
||||||
let mut installed_games = Vec::new();
|
let mut installed_games = Vec::new();
|
||||||
let mut steamdir = SteamDir::locate().unwrap();
|
let mut steamdir = match SteamDir::locate() {
|
||||||
|
Some(steamdir) => steamdir,
|
||||||
|
None => {
|
||||||
|
println!("{}", "Steam not found!".yellow());
|
||||||
|
return installed_games;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
for game in games {
|
for game in games {
|
||||||
if let Some(app) = steamdir.app(&game.app_id) {
|
if let Some(app) = steamdir.app(&game.app_id) {
|
||||||
@ -128,43 +38,84 @@ fn get_installed_games(games: &Vec<Game>) -> Vec<(u32, PathBuf)> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn setup_client_links(game: &Game, game_dir: &PathBuf) {
|
fn create_shortcut(path: &Path, target: &Path, icon: String, args: String) {
|
||||||
if game.client.len() > 1 {
|
if let Ok(mut sl) = ShellLink::new(target) {
|
||||||
println!("Multiple clients installed, use the shortcuts (launch-<client>.lnk in the game directory or desktop shortcuts) to launch a specific client.");
|
sl.set_arguments(Some(args));
|
||||||
}
|
sl.set_icon_location(Some(icon));
|
||||||
|
sl.create_lnk(path).unwrap_or_else(|error| {
|
||||||
let target = game_dir.join("alterware-launcher.exe");
|
println!("Error creating shortcut.\n{:#?}", error);
|
||||||
|
});
|
||||||
for c in game.client.iter() {
|
} else {
|
||||||
let lnk = game_dir.join(format!("launch-{}.lnk", c));
|
println!("Error creating shortcut.");
|
||||||
|
|
||||||
let mut sl = ShellLink::new(target.clone()).unwrap();
|
|
||||||
sl.set_arguments(Some(c.to_string()));
|
|
||||||
sl.set_icon_location(Some(
|
|
||||||
game_dir
|
|
||||||
.join(format!("{}.exe", c))
|
|
||||||
.to_string_lossy()
|
|
||||||
.into_owned(),
|
|
||||||
));
|
|
||||||
sl.create_lnk(&lnk).unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn setup_client_links(game: &Game, game_dir: &Path) {
|
||||||
|
if game.client.len() > 1 {
|
||||||
|
println!("Multiple clients installed, use the shortcuts (launch-<client>.lnk in the game directory or on the desktop) to launch a specific client.");
|
||||||
|
}
|
||||||
|
|
||||||
|
for c in game.client.iter() {
|
||||||
|
create_shortcut(
|
||||||
|
&game_dir.join(format!("launch-{}.lnk", c)),
|
||||||
|
&game_dir.join("alterware-launcher.exe"),
|
||||||
|
game_dir
|
||||||
|
.join(format!("{}.exe", c))
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned(),
|
||||||
|
c.to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn setup_desktop_links(path: &Path, game: &Game) {
|
||||||
|
println!("Create Desktop shortcut? (Y/n)");
|
||||||
|
let input = misc::stdin().to_ascii_lowercase();
|
||||||
|
|
||||||
|
if input == "y" || input.is_empty() {
|
||||||
|
let desktop = PathBuf::from(&format!(
|
||||||
|
"{}\\Desktop",
|
||||||
|
std::env::var("USERPROFILE").unwrap()
|
||||||
|
));
|
||||||
|
|
||||||
|
for c in game.client.iter() {
|
||||||
|
create_shortcut(
|
||||||
|
&desktop.join(format!("{}.lnk", c)),
|
||||||
|
&path.join("alterware-launcher.exe"),
|
||||||
|
path.join(format!("{}.exe", c))
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned(),
|
||||||
|
c.to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn auto_install(path: &Path, game: &Game) {
|
||||||
|
setup_client_links(game, path);
|
||||||
|
setup_desktop_links(path, game);
|
||||||
|
update(game, path, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn windows_launcher_install(games: &Vec<Game>) {
|
fn windows_launcher_install(games: &Vec<Game>) {
|
||||||
println!("No game specified/found. Checking for installed Steam games..");
|
println!(
|
||||||
|
"{}",
|
||||||
|
"No game specified/found. Checking for installed Steam games..".yellow()
|
||||||
|
);
|
||||||
let installed_games = get_installed_games(games);
|
let installed_games = get_installed_games(games);
|
||||||
|
|
||||||
if !installed_games.is_empty() {
|
if !installed_games.is_empty() {
|
||||||
// if current directory is in the steamapps/common folder of a game, use that game
|
|
||||||
let current_dir = std::env::current_dir().unwrap();
|
let current_dir = std::env::current_dir().unwrap();
|
||||||
for (id, path) in installed_games.iter() {
|
for (id, path) in installed_games.iter() {
|
||||||
if current_dir.starts_with(path) {
|
if current_dir.starts_with(path) {
|
||||||
println!("Found game in current directory.");
|
println!("Found game in current directory.");
|
||||||
println!("Installing AlterWare client for {}.", id);
|
println!("Installing AlterWare client for {}.", id);
|
||||||
let game = games.iter().find(|&g| g.app_id == *id).unwrap();
|
let game = games.iter().find(|&g| g.app_id == *id).unwrap();
|
||||||
setup_client_links(game, path);
|
auto_install(path, game);
|
||||||
update(&game, path);
|
|
||||||
println!("Installation complete. Please run the launcher again or use a shortcut to launch the game.");
|
println!("Installation complete. Please run the launcher again or use a shortcut to launch the game.");
|
||||||
std::io::stdin().read_line(&mut String::new()).unwrap();
|
std::io::stdin().read_line(&mut String::new()).unwrap();
|
||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
@ -177,8 +128,12 @@ fn windows_launcher_install(games: &Vec<Game>) {
|
|||||||
println!("{}: {}", id, path.display());
|
println!("{}: {}", id, path.display());
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("Enter the ID of the game you want to install the AlterWare client for:");
|
println!("Enter the ID of the game you want to install the AlterWare client for, enter 0 for manual selection:");
|
||||||
let input: u32 = get_input().parse().unwrap();
|
let input: u32 = misc::stdin().parse().unwrap();
|
||||||
|
|
||||||
|
if input == 0 {
|
||||||
|
return manual_install(games);
|
||||||
|
}
|
||||||
|
|
||||||
for (id, path) in installed_games.iter() {
|
for (id, path) in installed_games.iter() {
|
||||||
if *id == input {
|
if *id == input {
|
||||||
@ -191,109 +146,282 @@ fn windows_launcher_install(games: &Vec<Game>) {
|
|||||||
fs::copy(launcher_path, target_path).unwrap();
|
fs::copy(launcher_path, target_path).unwrap();
|
||||||
println!("Launcher copied to {}", path.display());
|
println!("Launcher copied to {}", path.display());
|
||||||
}
|
}
|
||||||
setup_client_links(game, path);
|
auto_install(path, game);
|
||||||
|
|
||||||
println!("Create Desktop shortcut? (Y/n)");
|
|
||||||
let input = get_input().to_ascii_lowercase();
|
|
||||||
|
|
||||||
if input == "y" || input.is_empty() {
|
|
||||||
let desktop = PathBuf::from(&format!(
|
|
||||||
"{}\\Desktop",
|
|
||||||
std::env::var("USERPROFILE").unwrap()
|
|
||||||
));
|
|
||||||
|
|
||||||
let target = path.join("alterware-launcher.exe");
|
|
||||||
|
|
||||||
for c in game.client.iter() {
|
|
||||||
let lnk = desktop.join(format!("{}.lnk", c));
|
|
||||||
|
|
||||||
let mut sl = ShellLink::new(target.clone()).unwrap();
|
|
||||||
sl.set_arguments(Some(c.to_string()));
|
|
||||||
sl.set_icon_location(Some(
|
|
||||||
path.join(format!("{}.exe", c))
|
|
||||||
.to_string_lossy()
|
|
||||||
.into_owned(),
|
|
||||||
));
|
|
||||||
sl.create_lnk(lnk).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
update(game, path);
|
|
||||||
println!("Installation complete. Please run the launcher again or use a shortcut to launch the game.");
|
println!("Installation complete. Please run the launcher again or use a shortcut to launch the game.");
|
||||||
std::io::stdin().read_line(&mut String::new()).unwrap();
|
std::io::stdin().read_line(&mut String::new()).unwrap();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
|
} else {
|
||||||
|
manual_install(games);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
println!("No installed Steam games found. Please install a supported game first or place the launcher in the game folder.");
|
fn prompt_client_selection(games: &[Game]) -> String {
|
||||||
|
println!(
|
||||||
|
"Couldn't detect any games, please select a client to install in the current directory:"
|
||||||
|
);
|
||||||
|
for (i, g) in games.iter().enumerate() {
|
||||||
|
for c in g.client.iter() {
|
||||||
|
println!("{}: {}", i, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let input: usize = misc::stdin().parse().unwrap();
|
||||||
|
String::from(games[input].client[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn manual_install(games: &[Game]) {
|
||||||
|
let selection = prompt_client_selection(games);
|
||||||
|
let game = games.iter().find(|&g| g.client[0] == selection).unwrap();
|
||||||
|
update(game, &std::env::current_dir().unwrap(), false, false);
|
||||||
|
println!("Installation complete. Please run the launcher again or use a shortcut to launch the game.");
|
||||||
|
std::io::stdin().read_line(&mut String::new()).unwrap();
|
||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(game: &Game, dir: &PathBuf) {
|
fn total_download_size(cdn_info: &Vec<CdnFile>, remote_dir: &str) -> u64 {
|
||||||
|
let remote_dir = format!("{}/", remote_dir);
|
||||||
|
let mut size: u64 = 0;
|
||||||
|
for file in cdn_info {
|
||||||
|
if !file.name.starts_with(&remote_dir) || file.name == "iw4/iw4x.dll" {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
size += file.size as u64;
|
||||||
|
}
|
||||||
|
size
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_dir(
|
||||||
|
cdn_info: &Vec<CdnFile>,
|
||||||
|
remote_dir: &str,
|
||||||
|
dir: &Path,
|
||||||
|
hashes: &mut HashMap<String, String>,
|
||||||
|
) {
|
||||||
|
let remote_dir_pre = format!("{}/", remote_dir);
|
||||||
|
|
||||||
|
let mut files_to_download: Vec<CdnFile> = vec![];
|
||||||
|
|
||||||
|
for file in cdn_info {
|
||||||
|
if !file.name.starts_with(&remote_dir_pre) || file.name == "iw4/iw4x.dll" {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sha1_remote = file.hash.to_lowercase();
|
||||||
|
let file_name = &file.name.replace(remote_dir_pre.as_str(), "");
|
||||||
|
let file_path = dir.join(file_name);
|
||||||
|
if file_path.exists() {
|
||||||
|
let sha1_local = hashes
|
||||||
|
.get(file_name)
|
||||||
|
.map(Cow::Borrowed)
|
||||||
|
.unwrap_or_else(|| Cow::Owned(misc::get_file_sha1(&file_path)))
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
if sha1_local != sha1_remote {
|
||||||
|
files_to_download.push(file.clone());
|
||||||
|
} else {
|
||||||
|
println!("[{}] {}", "Checked".bright_blue(), file_path.display());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
files_to_download.push(file.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if files_to_download.is_empty() {
|
||||||
|
println!(
|
||||||
|
"[{}] No files to download for {}",
|
||||||
|
"Info".bright_magenta(),
|
||||||
|
remote_dir
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
println!(
|
||||||
|
"[{}] Downloading outdated or missing files for {}, {}",
|
||||||
|
"Info".bright_magenta(),
|
||||||
|
remote_dir,
|
||||||
|
misc::human_readable_bytes(total_download_size(&files_to_download, remote_dir))
|
||||||
|
);
|
||||||
|
for file in files_to_download {
|
||||||
|
let file_name = &file.name.replace(&remote_dir_pre, "");
|
||||||
|
let file_path = dir.join(file_name);
|
||||||
|
println!(
|
||||||
|
"[{}] {} ({})",
|
||||||
|
"Downloading".bright_yellow(),
|
||||||
|
file_path.display(),
|
||||||
|
misc::human_readable_bytes(file.size as u64)
|
||||||
|
);
|
||||||
|
if let Some(parent) = file_path.parent() {
|
||||||
|
if !parent.exists() {
|
||||||
|
fs::create_dir_all(parent).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
http::download_file(&format!("{}/{}", MASTER, file.name), &file_path);
|
||||||
|
hashes.insert(file_name.to_owned(), file.hash.to_lowercase());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(game: &Game, dir: &Path, bonus_content: bool, force: bool) {
|
||||||
let cdn_info: Vec<CdnFile> = serde_json::from_str(&http::get_body_string(
|
let cdn_info: Vec<CdnFile> = serde_json::from_str(&http::get_body_string(
|
||||||
format!("{}/files.json", MASTER).as_str(),
|
format!("{}/files.json", MASTER).as_str(),
|
||||||
))
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
for file in cdn_info {
|
let mut hashes = HashMap::new();
|
||||||
if !file.name.starts_with(game.engine) {
|
let hash_file = dir.join(".sha-sums");
|
||||||
continue;
|
if hash_file.exists() && !force {
|
||||||
}
|
let hash_file = fs::read_to_string(hash_file).unwrap();
|
||||||
|
for line in hash_file.lines() {
|
||||||
let file_path = dir.join(&file.name.replace(&format!("{}/", game.engine), ""));
|
let mut split = line.split_whitespace();
|
||||||
if file_path.exists() {
|
let hash = split.next().unwrap();
|
||||||
let sha1_local = get_file_sha1(&file_path).to_lowercase();
|
let file = split.next().unwrap();
|
||||||
let sha1_remote = file.hash.to_lowercase();
|
hashes.insert(file.to_owned(), hash.to_owned());
|
||||||
if sha1_local != sha1_remote {
|
|
||||||
println!(
|
|
||||||
"Updating {}...\nLocal hash: {}\nRemote hash: {}",
|
|
||||||
file_path.display(),
|
|
||||||
sha1_local,
|
|
||||||
sha1_remote
|
|
||||||
);
|
|
||||||
http::download_file(&format!("{}/{}", MASTER, file.name), &file_path);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
println!("Downloading {}...", file_path.display());
|
|
||||||
if let Some(parent) = file_path.parent() {
|
|
||||||
if !parent.exists() {
|
|
||||||
fs::create_dir_all(parent).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
http::download_file(&format!("{}/{}", MASTER, file.name), &file_path);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
update_dir(&cdn_info, game.engine, dir, &mut hashes);
|
||||||
|
|
||||||
|
if game.engine == "iw4" {
|
||||||
|
iw4x::update(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
if bonus_content && !game.bonus.is_empty() {
|
||||||
|
for bonus in game.bonus.iter() {
|
||||||
|
update_dir(&cdn_info, bonus, dir, &mut hashes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut hash_file_content = String::new();
|
||||||
|
for (file, hash) in hashes.iter() {
|
||||||
|
hash_file_content.push_str(&format!("{} {}\n", hash, file));
|
||||||
|
}
|
||||||
|
fs::write(dir.join(".sha-sums"), hash_file_content).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn launch(file_path: &PathBuf) {
|
fn launch(file_path: &PathBuf, args: &str) {
|
||||||
println!("Launching {}...", file_path.display());
|
println!("Launching {} {}", file_path.display(), args);
|
||||||
std::process::Command::new(file_path)
|
std::process::Command::new(file_path)
|
||||||
|
.args(args.trim().split(' '))
|
||||||
|
.current_dir(file_path.parent().unwrap())
|
||||||
.spawn()
|
.spawn()
|
||||||
.expect("Failed to launch the game")
|
.expect("Failed to launch the game")
|
||||||
.wait()
|
.wait()
|
||||||
.expect("Failed to wait for the game process to finish");
|
.expect("Failed to wait for the game process to finish");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn setup_env() {
|
||||||
|
colored::control::set_virtual_terminal(true).unwrap_or_else(|error| {
|
||||||
|
println!("{:#?}", error);
|
||||||
|
colored::control::SHOULD_COLORIZE.set_override(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn arg_value(args: &[String], arg: &str) -> Option<String> {
|
||||||
|
args.iter()
|
||||||
|
.position(|r| r == arg)
|
||||||
|
.map(|e| args[e + 1].clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn arg_bool(args: &[String], arg: &str) -> bool {
|
||||||
|
args.iter().any(|r| r == arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn arg_remove(args: &mut Vec<String>, arg: &str) {
|
||||||
|
args.iter().position(|r| r == arg).map(|e| args.remove(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn arg_remove_value(args: &mut Vec<String>, arg: &str) {
|
||||||
|
if let Some(e) = args.iter().position(|r| r == arg) {
|
||||||
|
args.remove(e);
|
||||||
|
args.remove(e);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
#[cfg(windows)]
|
||||||
|
setup_env();
|
||||||
|
|
||||||
let mut args: Vec<String> = std::env::args().collect();
|
let mut args: Vec<String> = std::env::args().collect();
|
||||||
|
|
||||||
let mut update_only = false;
|
if arg_bool(&args, "--help") {
|
||||||
if args.contains(&String::from("update")) {
|
println!("CLI Args:");
|
||||||
update_only = true;
|
println!(" <client>: Specify the client to launch");
|
||||||
args.iter()
|
println!(" --help: Display this help message");
|
||||||
.position(|r| r == "update")
|
println!(" --version: Display the launcher version");
|
||||||
.map(|e| args.remove(e));
|
println!(" --path/-p <path>: Specify the game directory");
|
||||||
|
println!(" --update/-u: Update only, don't launch the game");
|
||||||
|
println!(" --bonus: Download bonus content");
|
||||||
|
println!(" --force/-f: Force file hash recheck");
|
||||||
|
println!(" --pass <args>: Pass arguments to the game");
|
||||||
|
println!(" --skip-launcher-update: Skip launcher self-update");
|
||||||
|
println!(
|
||||||
|
"\nExample:\n alterware-launcher.exe iw4x --bonus --pass \"-console -nointro\""
|
||||||
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !args.contains(&String::from("skip-launcher-update")) {
|
if arg_bool(&args, "--version") || arg_bool(&args, "-v") {
|
||||||
self_update(update_only);
|
println!(
|
||||||
|
"{} v{}",
|
||||||
|
"AlterWare Launcher".bright_green(),
|
||||||
|
env!("CARGO_PKG_VERSION")
|
||||||
|
);
|
||||||
|
println!("https://github.com/{}/{}", GH_OWNER, GH_REPO);
|
||||||
|
println!(
|
||||||
|
"\n{}{}{}{}{}{}{}",
|
||||||
|
"For ".on_black(),
|
||||||
|
"Alter".bright_blue().on_black().underline(),
|
||||||
|
"Ware".white().on_black().underline(),
|
||||||
|
".dev".on_black().underline(),
|
||||||
|
" by ".on_black(),
|
||||||
|
"mxve".bright_magenta().on_black().underline(),
|
||||||
|
".de".on_black().underline()
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let install_path: PathBuf;
|
||||||
|
if let Some(path) = arg_value(&args, "--path") {
|
||||||
|
install_path = PathBuf::from(path);
|
||||||
|
arg_remove_value(&mut args, "--path");
|
||||||
|
} else if let Some(path) = arg_value(&args, "-p") {
|
||||||
|
install_path = PathBuf::from(path);
|
||||||
|
arg_remove_value(&mut args, "-p");
|
||||||
} else {
|
} else {
|
||||||
args.iter()
|
install_path = std::env::current_dir().unwrap();
|
||||||
.position(|r| r == "skip-launcher-update")
|
}
|
||||||
.map(|e| args.remove(e));
|
|
||||||
|
let mut cfg = config::load(install_path.join("alterware-launcher.json"));
|
||||||
|
|
||||||
|
if !arg_bool(&args, "--skip-launcher-update") && !cfg.skip_self_update {
|
||||||
|
self_update::run(cfg.update_only);
|
||||||
|
} else {
|
||||||
|
arg_remove(&mut args, "--skip-launcher-update");
|
||||||
|
}
|
||||||
|
|
||||||
|
if arg_bool(&args, "--update") || arg_bool(&args, "-u") {
|
||||||
|
cfg.update_only = true;
|
||||||
|
arg_remove(&mut args, "--update");
|
||||||
|
arg_remove(&mut args, "-u");
|
||||||
|
}
|
||||||
|
|
||||||
|
if arg_bool(&args, "--bonus") {
|
||||||
|
cfg.download_bonus_content = true;
|
||||||
|
cfg.ask_bonus_content = false;
|
||||||
|
arg_remove(&mut args, "--bonus");
|
||||||
|
}
|
||||||
|
|
||||||
|
if arg_bool(&args, "--force") || arg_bool(&args, "-f") {
|
||||||
|
cfg.force_update = true;
|
||||||
|
arg_remove(&mut args, "--force");
|
||||||
|
arg_remove(&mut args, "-f");
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(pass) = arg_value(&args, "--pass") {
|
||||||
|
cfg.args = pass;
|
||||||
|
arg_remove_value(&mut args, "--pass");
|
||||||
|
} else if cfg.args.is_empty() {
|
||||||
|
cfg.args = String::from("");
|
||||||
}
|
}
|
||||||
|
|
||||||
let games_json = http::get_body_string(format!("{}/games.json", MASTER).as_str());
|
let games_json = http::get_body_string(format!("{}/games.json", MASTER).as_str());
|
||||||
@ -305,18 +433,23 @@ fn main() {
|
|||||||
} else {
|
} else {
|
||||||
'main: for g in games.iter() {
|
'main: for g in games.iter() {
|
||||||
for r in g.references.iter() {
|
for r in g.references.iter() {
|
||||||
if std::path::Path::new(r).exists() {
|
if install_path.join(r).exists() {
|
||||||
if g.client.len() > 1 {
|
if g.client.len() > 1 {
|
||||||
|
if cfg.update_only {
|
||||||
|
game = String::from(g.client[0]);
|
||||||
|
break 'main;
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
setup_client_links(g, &std::env::current_dir().unwrap());
|
setup_client_links(g, &std::env::current_dir().unwrap());
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
println!("Multiple clients installed, set the client as the first argument to launch a specific client.");
|
println!("Multiple clients installed, set the client as the first argument to launch a specific client.");
|
||||||
|
println!("Select a client to launch:");
|
||||||
for (i, c) in g.client.iter().enumerate() {
|
for (i, c) in g.client.iter().enumerate() {
|
||||||
println!("{}: {}", i, c);
|
println!("{}: {}", i, c);
|
||||||
}
|
}
|
||||||
game = String::from(g.client[get_input().parse::<usize>().unwrap()]);
|
game = String::from(g.client[misc::stdin().parse::<usize>().unwrap()]);
|
||||||
break 'main;
|
break 'main;
|
||||||
}
|
}
|
||||||
game = String::from(g.client[0]);
|
game = String::from(g.client[0]);
|
||||||
@ -329,9 +462,30 @@ fn main() {
|
|||||||
for g in games.iter() {
|
for g in games.iter() {
|
||||||
for c in g.client.iter() {
|
for c in g.client.iter() {
|
||||||
if c == &game {
|
if c == &game {
|
||||||
update(g, &std::env::current_dir().unwrap());
|
if cfg.ask_bonus_content && !g.bonus.is_empty() {
|
||||||
if !update_only {
|
println!("Download bonus content? (Y/n)");
|
||||||
launch(&PathBuf::from(format!("{}.exe", c)));
|
let input = misc::stdin().to_ascii_lowercase();
|
||||||
|
cfg.download_bonus_content = input != "n";
|
||||||
|
config::save_value(
|
||||||
|
install_path.join("alterware-launcher.json"),
|
||||||
|
"download_bonus_content",
|
||||||
|
cfg.download_bonus_content,
|
||||||
|
);
|
||||||
|
config::save_value(
|
||||||
|
install_path.join("alterware-launcher.json"),
|
||||||
|
"ask_bonus_content",
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(
|
||||||
|
g,
|
||||||
|
install_path.as_path(),
|
||||||
|
cfg.download_bonus_content,
|
||||||
|
cfg.force_update,
|
||||||
|
);
|
||||||
|
if !cfg.update_only {
|
||||||
|
launch(&install_path.join(format!("{}.exe", c)), &cfg.args);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -341,7 +495,10 @@ fn main() {
|
|||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
windows_launcher_install(&games);
|
windows_launcher_install(&games);
|
||||||
|
|
||||||
println!("Game not found!");
|
#[cfg(not(windows))]
|
||||||
|
manual_install(&games);
|
||||||
|
|
||||||
|
println!("{}", "Game not found!".bright_red());
|
||||||
println!("Place the launcher in the game folder, if that doesn't work specify the client on the command line (ex. alterware-launcher.exe iw4-sp)");
|
println!("Place the launcher in the game folder, if that doesn't work specify the client on the command line (ex. alterware-launcher.exe iw4-sp)");
|
||||||
println!("Press enter to exit...");
|
println!("Press enter to exit...");
|
||||||
std::io::stdin().read_line(&mut String::new()).unwrap();
|
std::io::stdin().read_line(&mut String::new()).unwrap();
|
||||||
|
39
src/misc.rs
Normal file
39
src/misc.rs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
use std::{fs, path::PathBuf};
|
||||||
|
|
||||||
|
use colored::Colorize;
|
||||||
|
|
||||||
|
pub fn get_file_sha1(path: &PathBuf) -> String {
|
||||||
|
let mut sha1 = sha1_smol::Sha1::new();
|
||||||
|
sha1.update(&fs::read(path).unwrap());
|
||||||
|
sha1.digest().to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stdin() -> String {
|
||||||
|
let mut input = String::new();
|
||||||
|
std::io::stdin().read_line(&mut input).unwrap();
|
||||||
|
input.trim().to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rev_to_int(rev: &str) -> u16 {
|
||||||
|
rev.strip_prefix('r')
|
||||||
|
.unwrap_or("0")
|
||||||
|
.parse::<u16>()
|
||||||
|
.unwrap_or(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fatal_error(error: &str) {
|
||||||
|
println!("\n\n{}:\n{}", "Error".bright_red(), error);
|
||||||
|
stdin();
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn human_readable_bytes(bytes: u64) -> String {
|
||||||
|
let mut bytes = bytes as f64;
|
||||||
|
let mut i = 0;
|
||||||
|
let units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
||||||
|
while bytes > 1024.0 {
|
||||||
|
bytes /= 1024.0;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
format!("{:.2} {}", bytes, units[i])
|
||||||
|
}
|
96
src/self_update.rs
Normal file
96
src/self_update.rs
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
use crate::github;
|
||||||
|
use crate::global::*;
|
||||||
|
|
||||||
|
use semver::Version;
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
use std::{thread, time};
|
||||||
|
|
||||||
|
pub fn self_update_available() -> bool {
|
||||||
|
let current_version: Version = Version::parse(env!("CARGO_PKG_VERSION")).unwrap();
|
||||||
|
let latest_version = github::latest_version(GH_OWNER, GH_REPO);
|
||||||
|
|
||||||
|
current_version < latest_version
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
pub fn run(_update_only: bool) {
|
||||||
|
if self_update_available() {
|
||||||
|
println!("A new version of the AlterWare launcher is available.");
|
||||||
|
println!(
|
||||||
|
"Download it at {}",
|
||||||
|
github::latest_release_url(GH_OWNER, GH_REPO)
|
||||||
|
);
|
||||||
|
println!("Launching in 10 seconds..");
|
||||||
|
thread::sleep(time::Duration::from_secs(10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
pub fn run(update_only: bool) {
|
||||||
|
use std::{fs, path::PathBuf};
|
||||||
|
|
||||||
|
use crate::http;
|
||||||
|
use crate::misc;
|
||||||
|
|
||||||
|
let working_dir = std::env::current_dir().unwrap();
|
||||||
|
let files = fs::read_dir(&working_dir).unwrap();
|
||||||
|
|
||||||
|
for file in files {
|
||||||
|
let file = file.unwrap();
|
||||||
|
let file_name = file.file_name().into_string().unwrap();
|
||||||
|
|
||||||
|
if file_name.contains("alterware-launcher")
|
||||||
|
&& (file_name.contains(".__relocated__.exe")
|
||||||
|
|| file_name.contains(".__selfdelete__.exe"))
|
||||||
|
{
|
||||||
|
fs::remove_file(file.path()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self_update_available() {
|
||||||
|
println!("Performing launcher self-update.");
|
||||||
|
println!(
|
||||||
|
"If you run into any issues, please download the latest version at {}",
|
||||||
|
github::latest_release_url(GH_OWNER, GH_REPO)
|
||||||
|
);
|
||||||
|
|
||||||
|
let update_binary = PathBuf::from("alterware-launcher-update.exe");
|
||||||
|
let file_path = working_dir.join(&update_binary);
|
||||||
|
|
||||||
|
if update_binary.exists() {
|
||||||
|
fs::remove_file(&update_binary).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let launcher_name = if cfg!(target_arch = "x86") {
|
||||||
|
"alterware-launcher-x86.exe"
|
||||||
|
} else {
|
||||||
|
"alterware-launcher.exe"
|
||||||
|
};
|
||||||
|
println!("{}", launcher_name);
|
||||||
|
http::download_file(
|
||||||
|
&format!(
|
||||||
|
"{}/download/{}",
|
||||||
|
github::latest_release_url(GH_OWNER, GH_REPO),
|
||||||
|
launcher_name
|
||||||
|
),
|
||||||
|
&file_path,
|
||||||
|
);
|
||||||
|
|
||||||
|
if !file_path.exists() {
|
||||||
|
println!("Failed to download launcher update.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self_replace::self_replace("alterware-launcher-update.exe").unwrap();
|
||||||
|
fs::remove_file(&file_path).unwrap();
|
||||||
|
println!(
|
||||||
|
"Launcher updated. View the changelog at https://github.com/{}/{}/releases/latest",
|
||||||
|
GH_OWNER, GH_REPO,
|
||||||
|
);
|
||||||
|
println!("Please restart the launcher.");
|
||||||
|
if !update_only {
|
||||||
|
misc::stdin();
|
||||||
|
}
|
||||||
|
std::process::exit(201);
|
||||||
|
}
|
||||||
|
}
|
38
src/structs.rs
Normal file
38
src/structs.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#[derive(serde::Deserialize, serde::Serialize, Clone)]
|
||||||
|
pub struct CdnFile {
|
||||||
|
pub name: String,
|
||||||
|
pub size: u32,
|
||||||
|
pub hash: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize, serde::Serialize)]
|
||||||
|
pub struct Game<'a> {
|
||||||
|
pub engine: &'a str,
|
||||||
|
pub client: Vec<&'a str>,
|
||||||
|
pub references: Vec<&'a str>,
|
||||||
|
pub app_id: u32,
|
||||||
|
pub bonus: Vec<&'a str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize, serde::Serialize)]
|
||||||
|
pub struct Config {
|
||||||
|
pub update_only: bool,
|
||||||
|
pub skip_self_update: bool,
|
||||||
|
pub download_bonus_content: bool,
|
||||||
|
pub ask_bonus_content: bool,
|
||||||
|
pub force_update: bool,
|
||||||
|
pub args: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
update_only: false,
|
||||||
|
skip_self_update: false,
|
||||||
|
download_bonus_content: false,
|
||||||
|
ask_bonus_content: true,
|
||||||
|
force_update: false,
|
||||||
|
args: String::from(""),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user