Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e72964a9f8 | ||
|
|
18052d0c78 | ||
|
|
09f5a3895b | ||
|
|
89892ef592 | ||
|
|
61de3aeee3 | ||
|
|
4ed00e6ac3 | ||
|
|
332db0ea03 | ||
|
|
64d13e3964 | ||
|
|
feb3a793d9 | ||
|
|
585f454bc0 | ||
|
|
27524e855a | ||
|
|
0257834d04 | ||
|
|
64e94bf790 | ||
|
|
668a6eb38b | ||
|
|
0de79769d2 | ||
|
|
b79c5f62b3 | ||
|
|
2c3092026a | ||
|
|
69eda1103d | ||
|
|
19b1ade758 | ||
|
|
18f667a91a | ||
|
|
186371f835 | ||
|
|
ce2022db55 | ||
|
|
d992d6b8f6 | ||
|
|
b71dbbdf03 | ||
|
|
c85a169765 | ||
|
|
9df757c169 | ||
|
|
92cbec7934 | ||
|
|
41cfb12ea8 | ||
|
|
e328eb91f6 | ||
|
|
10c840ac4d | ||
|
|
2fc0aa2005 | ||
|
|
29f726b1bd | ||
|
|
16d3c2cd96 | ||
|
|
4ce7f3459d | ||
|
|
c8cbb51f43 | ||
|
|
9c53239276 | ||
|
|
af5ea259e9 | ||
|
|
162350699a | ||
|
|
2977398437 | ||
|
|
36dce5aa1e | ||
|
|
6935b852ce | ||
|
|
1c048e6aff | ||
|
|
84a659a2b1 | ||
|
|
db4ca22f95 | ||
|
|
284c4bb46d | ||
|
|
5fe697223c | ||
|
|
33fbbc7fc0 | ||
|
|
5bcdccdfea | ||
|
|
31ac09cd25 | ||
|
|
1283f34174 | ||
|
|
072c8ae406 | ||
|
|
e283393889 | ||
|
|
cf5c37c7dc | ||
|
|
c6cd0cf2f9 | ||
|
|
d182ca5318 | ||
|
|
42ee688639 | ||
|
|
ba49ed0e67 | ||
|
|
f9f326904e | ||
|
|
c3a1e864a7 | ||
|
|
7f795ba52e | ||
|
|
62efe3f411 | ||
|
|
4bb0f151a9 | ||
|
|
1aebc6b17c | ||
|
|
861ebe97d6 | ||
|
|
53b20a437a | ||
|
|
03ca861a09 | ||
|
|
a4ebfc515c | ||
|
|
52ffb34d83 | ||
|
|
6248e8379f | ||
|
|
99bc15b5ed | ||
|
|
e84d1347fd | ||
|
|
f49bb56398 | ||
|
|
99b6519ffb | ||
|
|
807cb5242f | ||
|
|
c1c1547e4d | ||
|
|
6e3aa70398 | ||
|
|
9331f79038 |
Generated
+532
-60
@@ -156,6 +156,12 @@ version = "0.2.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04"
|
checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "android-tzdata"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "android_system_properties"
|
name = "android_system_properties"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
@@ -418,6 +424,18 @@ version = "1.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.21.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.22.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bit-set"
|
name = "bit-set"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
@@ -596,6 +614,20 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono"
|
||||||
|
version = "0.4.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c"
|
||||||
|
dependencies = [
|
||||||
|
"android-tzdata",
|
||||||
|
"iana-time-zone",
|
||||||
|
"js-sys",
|
||||||
|
"num-traits",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clipboard-win"
|
name = "clipboard-win"
|
||||||
version = "5.4.0"
|
version = "5.4.0"
|
||||||
@@ -634,6 +666,35 @@ dependencies = [
|
|||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cookie"
|
||||||
|
version = "0.18.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
|
||||||
|
dependencies = [
|
||||||
|
"percent-encoding",
|
||||||
|
"time",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cookie_store"
|
||||||
|
version = "0.21.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2eac901828f88a5241ee0600950ab981148a18f2f756900ffba1b125ca6a3ef9"
|
||||||
|
dependencies = [
|
||||||
|
"cookie",
|
||||||
|
"document-features",
|
||||||
|
"idna",
|
||||||
|
"indexmap",
|
||||||
|
"log",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"time",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation"
|
name = "core-foundation"
|
||||||
version = "0.9.4"
|
version = "0.9.4"
|
||||||
@@ -724,6 +785,21 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991"
|
checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "data-url"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deranged"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
|
||||||
|
dependencies = [
|
||||||
|
"powerfmt",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.7"
|
version = "0.10.7"
|
||||||
@@ -734,6 +810,27 @@ dependencies = [
|
|||||||
"crypto-common",
|
"crypto-common",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs"
|
||||||
|
version = "6.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
|
||||||
|
dependencies = [
|
||||||
|
"dirs-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs-sys"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"option-ext",
|
||||||
|
"redox_users",
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dispatch"
|
name = "dispatch"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@@ -896,6 +993,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"mime_guess2",
|
"mime_guess2",
|
||||||
"profiling",
|
"profiling",
|
||||||
|
"resvg",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1065,6 +1163,18 @@ dependencies = [
|
|||||||
"miniz_oxide",
|
"miniz_oxide",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "float-cmp"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fnv"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "foldhash"
|
name = "foldhash"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
@@ -1345,15 +1455,22 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gta-tools"
|
name = "gta-tools"
|
||||||
version = "0.2.0"
|
version = "0.5.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"catppuccin-egui",
|
"catppuccin-egui",
|
||||||
|
"chrono",
|
||||||
|
"dirs",
|
||||||
"eframe",
|
"eframe",
|
||||||
"egui_extras",
|
"egui_extras",
|
||||||
"image",
|
"image",
|
||||||
"open",
|
"open",
|
||||||
|
"semver",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"static_vcruntime",
|
"static_vcruntime",
|
||||||
|
"strum 0.27.1",
|
||||||
"sysinfo",
|
"sysinfo",
|
||||||
|
"ureq",
|
||||||
"windows 0.61.1",
|
"windows 0.61.1",
|
||||||
"winreg",
|
"winreg",
|
||||||
"winresource",
|
"winresource",
|
||||||
@@ -1401,6 +1518,47 @@ dependencies = [
|
|||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "http"
|
||||||
|
version = "1.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"fnv",
|
||||||
|
"itoa",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "httparse"
|
||||||
|
version = "1.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone"
|
||||||
|
version = "0.1.63"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
|
||||||
|
dependencies = [
|
||||||
|
"android_system_properties",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"iana-time-zone-haiku",
|
||||||
|
"js-sys",
|
||||||
|
"log",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"windows-core 0.61.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone-haiku"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "icu_collections"
|
name = "icu_collections"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@@ -1553,6 +1711,12 @@ dependencies = [
|
|||||||
"tiff",
|
"tiff",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "imagesize"
|
||||||
|
version = "0.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "immutable-chunkmap"
|
name = "immutable-chunkmap"
|
||||||
version = "2.0.6"
|
version = "2.0.6"
|
||||||
@@ -1591,6 +1755,12 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jni"
|
name = "jni"
|
||||||
version = "0.21.1"
|
version = "0.21.1"
|
||||||
@@ -1656,6 +1826,15 @@ version = "3.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
|
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kurbo"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bd85a5776cd9500c2e2059c8c76c3b01528566b7fcbaf8098b55a33fc298849b"
|
||||||
|
dependencies = [
|
||||||
|
"arrayvec",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.171"
|
version = "0.2.171"
|
||||||
@@ -1815,7 +1994,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"spirv",
|
"spirv",
|
||||||
"strum",
|
"strum 0.26.3",
|
||||||
"termcolor",
|
"termcolor",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
@@ -1888,6 +2067,12 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-conv"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.19"
|
version = "0.2.19"
|
||||||
@@ -2212,6 +2397,12 @@ dependencies = [
|
|||||||
"pathdiff",
|
"pathdiff",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "option-ext"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "orbclient"
|
name = "orbclient"
|
||||||
version = "0.3.48"
|
version = "0.3.48"
|
||||||
@@ -2336,10 +2527,16 @@ version = "0.11.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
|
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"siphasher",
|
"siphasher 1.0.1",
|
||||||
"unicase",
|
"unicase",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pico-args"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project"
|
name = "pin-project"
|
||||||
version = "1.1.10"
|
version = "1.1.10"
|
||||||
@@ -2417,6 +2614,12 @@ dependencies = [
|
|||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "powerfmt"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.21"
|
version = "0.2.21"
|
||||||
@@ -2520,6 +2723,12 @@ version = "0.6.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
|
checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rctree"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@@ -2538,12 +2747,66 @@ dependencies = [
|
|||||||
"bitflags 2.9.0",
|
"bitflags 2.9.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_users"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.2.15",
|
||||||
|
"libredox",
|
||||||
|
"thiserror 2.0.12",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "renderdoc-sys"
|
name = "renderdoc-sys"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
|
checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "resvg"
|
||||||
|
version = "0.37.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cadccb3d99a9efb8e5e00c16fbb732cbe400db2ec7fc004697ee7d97d86cf1f4"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"pico-args",
|
||||||
|
"rgb",
|
||||||
|
"svgtypes",
|
||||||
|
"tiny-skia",
|
||||||
|
"usvg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rgb"
|
||||||
|
version = "0.8.50"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ring"
|
||||||
|
version = "0.17.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"cfg-if",
|
||||||
|
"getrandom 0.2.15",
|
||||||
|
"libc",
|
||||||
|
"untrusted",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "roxmltree"
|
||||||
|
version = "0.19.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-hash"
|
name = "rustc-hash"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@@ -2576,12 +2839,59 @@ dependencies = [
|
|||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls"
|
||||||
|
version = "0.23.26"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"ring",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"rustls-webpki",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-pemfile"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
|
||||||
|
dependencies = [
|
||||||
|
"rustls-pki-types",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-pki-types"
|
||||||
|
version = "1.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-webpki"
|
||||||
|
version = "0.103.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03"
|
||||||
|
dependencies = [
|
||||||
|
"ring",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"untrusted",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.20"
|
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 = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
|
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "same-file"
|
name = "same-file"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
@@ -2616,6 +2926,12 @@ dependencies = [
|
|||||||
"tiny-skia",
|
"tiny-skia",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "1.0.26"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.219"
|
version = "1.0.219"
|
||||||
@@ -2636,6 +2952,18 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.140"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"memchr",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_repr"
|
name = "serde_repr"
|
||||||
version = "0.1.20"
|
version = "0.1.20"
|
||||||
@@ -2688,6 +3016,21 @@ 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 = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simplecss"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "siphasher"
|
||||||
|
version = "0.3.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "siphasher"
|
name = "siphasher"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@@ -2795,6 +3138,9 @@ name = "strict-num"
|
|||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
|
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
|
||||||
|
dependencies = [
|
||||||
|
"float-cmp",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strum"
|
name = "strum"
|
||||||
@@ -2802,7 +3148,16 @@ version = "0.26.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
|
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"strum_macros",
|
"strum_macros 0.26.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strum"
|
||||||
|
version = "0.27.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32"
|
||||||
|
dependencies = [
|
||||||
|
"strum_macros 0.27.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2818,6 +3173,35 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strum_macros"
|
||||||
|
version = "0.27.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rustversion",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtle"
|
||||||
|
version = "2.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "svgtypes"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6e44e288cd960318917cbd540340968b90becc8bc81f171345d706e7a89d9d70"
|
||||||
|
dependencies = [
|
||||||
|
"kurbo",
|
||||||
|
"siphasher 0.3.11",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.100"
|
version = "2.0.100"
|
||||||
@@ -2843,14 +3227,13 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "sysinfo"
|
name = "sysinfo"
|
||||||
version = "0.34.2"
|
version = "0.34.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/GuillaumeGomez/sysinfo?rev=bcbb7b2b389b8800ff457773f48b130e710458fc#bcbb7b2b389b8800ff457773f48b130e710458fc"
|
||||||
checksum = "a4b93974b3d3aeaa036504b8eefd4c039dced109171c1ae973f1dc63b2c7e4b2"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"memchr",
|
"memchr",
|
||||||
"ntapi",
|
"ntapi",
|
||||||
"objc2-core-foundation",
|
"objc2-core-foundation",
|
||||||
"windows 0.57.0",
|
"windows 0.61.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2926,6 +3309,37 @@ dependencies = [
|
|||||||
"weezl",
|
"weezl",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time"
|
||||||
|
version = "0.3.41"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"
|
||||||
|
dependencies = [
|
||||||
|
"deranged",
|
||||||
|
"itoa",
|
||||||
|
"num-conv",
|
||||||
|
"powerfmt",
|
||||||
|
"serde",
|
||||||
|
"time-core",
|
||||||
|
"time-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time-core"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time-macros"
|
||||||
|
version = "0.2.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49"
|
||||||
|
dependencies = [
|
||||||
|
"num-conv",
|
||||||
|
"time-core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tiny-skia"
|
name = "tiny-skia"
|
||||||
version = "0.11.4"
|
version = "0.11.4"
|
||||||
@@ -2937,6 +3351,7 @@ dependencies = [
|
|||||||
"bytemuck",
|
"bytemuck",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"log",
|
"log",
|
||||||
|
"png",
|
||||||
"tiny-skia-path",
|
"tiny-skia-path",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -3088,6 +3503,45 @@ version = "0.2.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "untrusted"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ureq"
|
||||||
|
version = "3.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b7a3e9af6113ecd57b8c63d3cd76a385b2e3881365f1f489e54f49801d0c83ea"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.22.1",
|
||||||
|
"cookie_store",
|
||||||
|
"flate2",
|
||||||
|
"log",
|
||||||
|
"percent-encoding",
|
||||||
|
"rustls",
|
||||||
|
"rustls-pemfile",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"ureq-proto",
|
||||||
|
"utf-8",
|
||||||
|
"webpki-roots",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ureq-proto"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fadf18427d33828c311234884b7ba2afb57143e6e7e69fda7ee883b624661e36"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.22.1",
|
||||||
|
"http",
|
||||||
|
"httparse",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.5.4"
|
version = "2.5.4"
|
||||||
@@ -3099,6 +3553,56 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "usvg"
|
||||||
|
version = "0.37.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38b0a51b72ab80ca511d126b77feeeb4fb1e972764653e61feac30adc161a756"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.21.7",
|
||||||
|
"log",
|
||||||
|
"pico-args",
|
||||||
|
"usvg-parser",
|
||||||
|
"usvg-tree",
|
||||||
|
"xmlwriter",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "usvg-parser"
|
||||||
|
version = "0.37.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9bd4e3c291f45d152929a31f0f6c819245e2921bfd01e7bd91201a9af39a2bdc"
|
||||||
|
dependencies = [
|
||||||
|
"data-url",
|
||||||
|
"flate2",
|
||||||
|
"imagesize",
|
||||||
|
"kurbo",
|
||||||
|
"log",
|
||||||
|
"roxmltree",
|
||||||
|
"simplecss",
|
||||||
|
"siphasher 0.3.11",
|
||||||
|
"svgtypes",
|
||||||
|
"usvg-tree",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "usvg-tree"
|
||||||
|
version = "0.37.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ee3d202ebdb97a6215604b8f5b4d6ef9024efd623cf2e373a6416ba976ec7d3"
|
||||||
|
dependencies = [
|
||||||
|
"rctree",
|
||||||
|
"strict-num",
|
||||||
|
"svgtypes",
|
||||||
|
"tiny-skia-path",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf-8"
|
||||||
|
version = "0.7.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf16_iter"
|
name = "utf16_iter"
|
||||||
version = "1.0.5"
|
version = "1.0.5"
|
||||||
@@ -3359,6 +3863,15 @@ dependencies = [
|
|||||||
"web-sys",
|
"web-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "webpki-roots"
|
||||||
|
version = "0.26.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2210b291f7ea53617fbafcc4939f10914214ec15aace5ba62293a668f322c5c9"
|
||||||
|
dependencies = [
|
||||||
|
"rustls-pki-types",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "weezl"
|
name = "weezl"
|
||||||
version = "0.1.8"
|
version = "0.1.8"
|
||||||
@@ -3499,16 +4012,6 @@ version = "0.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows"
|
|
||||||
version = "0.57.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143"
|
|
||||||
dependencies = [
|
|
||||||
"windows-core 0.57.0",
|
|
||||||
"windows-targets 0.52.6",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows"
|
name = "windows"
|
||||||
version = "0.58.0"
|
version = "0.58.0"
|
||||||
@@ -3541,18 +4044,6 @@ dependencies = [
|
|||||||
"windows-core 0.61.0",
|
"windows-core 0.61.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-core"
|
|
||||||
version = "0.57.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
|
|
||||||
dependencies = [
|
|
||||||
"windows-implement 0.57.0",
|
|
||||||
"windows-interface 0.57.0",
|
|
||||||
"windows-result 0.1.2",
|
|
||||||
"windows-targets 0.52.6",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-core"
|
name = "windows-core"
|
||||||
version = "0.58.0"
|
version = "0.58.0"
|
||||||
@@ -3589,17 +4080,6 @@ dependencies = [
|
|||||||
"windows-link",
|
"windows-link",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-implement"
|
|
||||||
version = "0.57.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-implement"
|
name = "windows-implement"
|
||||||
version = "0.58.0"
|
version = "0.58.0"
|
||||||
@@ -3622,17 +4102,6 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-interface"
|
|
||||||
version = "0.57.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-interface"
|
name = "windows-interface"
|
||||||
version = "0.58.0"
|
version = "0.58.0"
|
||||||
@@ -3671,15 +4140,6 @@ dependencies = [
|
|||||||
"windows-link",
|
"windows-link",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-result"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
|
|
||||||
dependencies = [
|
|
||||||
"windows-targets 0.52.6",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-result"
|
name = "windows-result"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@@ -4097,6 +4557,12 @@ version = "0.8.25"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c5b940ebc25896e71dd073bad2dbaa2abfe97b0a391415e22ad1326d9c54e3c4"
|
checksum = "c5b940ebc25896e71dd073bad2dbaa2abfe97b0a391415e22ad1326d9c54e3c4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xmlwriter"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yoke"
|
name = "yoke"
|
||||||
version = "0.7.5"
|
version = "0.7.5"
|
||||||
@@ -4281,6 +4747,12 @@ dependencies = [
|
|||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeroize"
|
||||||
|
version = "1.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerovec"
|
name = "zerovec"
|
||||||
version = "0.10.4"
|
version = "0.10.4"
|
||||||
|
|||||||
+11
-3
@@ -1,23 +1,31 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "gta-tools"
|
name = "gta-tools"
|
||||||
version = "0.2.0"
|
version = "0.5.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
catppuccin-egui = { version = "5.5.0", default-features = false, features = [
|
catppuccin-egui = { version = "5.5.0", default-features = false, features = [
|
||||||
"egui31",
|
"egui31",
|
||||||
] }
|
] }
|
||||||
|
chrono = "0.4.40"
|
||||||
|
dirs = "6.0.0"
|
||||||
eframe = "0.31.1"
|
eframe = "0.31.1"
|
||||||
egui_extras = { version = "0.31.1", features = ["image"] }
|
egui_extras = { version = "0.31.1", features = ["image", "svg"] }
|
||||||
image = { version = "0.25.6", default-features = false, features = ["png"] }
|
image = { version = "0.25.6", default-features = false, features = ["png"] }
|
||||||
open = "5.3.2"
|
open = "5.3.2"
|
||||||
sysinfo = "0.34.2"
|
semver = "1.0.26"
|
||||||
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
|
serde_json = "1.0.140"
|
||||||
|
strum = { version = "0.27.1", features = ["derive"] }
|
||||||
|
sysinfo = { git = "https://github.com/GuillaumeGomez/sysinfo", rev = "bcbb7b2b389b8800ff457773f48b130e710458fc" }
|
||||||
|
ureq = { version = "3.0.11", features = ["json"] }
|
||||||
windows = { version = "0.61.1", features = [
|
windows = { version = "0.61.1", features = [
|
||||||
"Win32_UI_Input_KeyboardAndMouse",
|
"Win32_UI_Input_KeyboardAndMouse",
|
||||||
"Win32_System_Threading",
|
"Win32_System_Threading",
|
||||||
"Win32_UI_Shell",
|
"Win32_UI_Shell",
|
||||||
"Win32_UI_WindowsAndMessaging",
|
"Win32_UI_WindowsAndMessaging",
|
||||||
"Win32_Security",
|
"Win32_Security",
|
||||||
|
"Win32_NetworkManagement_WindowsFirewall",
|
||||||
] }
|
] }
|
||||||
winreg = "0.55.0"
|
winreg = "0.55.0"
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,176 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
more spaghetti than the nation of italy
|
# GTA Tools
|
||||||
|
A toolset of convenient things for GTA 5 Online.
|
||||||
|
|
||||||

|

|
||||||
|
|||||||
Binary file not shown.
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="512"
|
||||||
|
height="512"
|
||||||
|
viewBox="0 0 512 512"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs
|
||||||
|
id="defs1" />
|
||||||
|
<g
|
||||||
|
id="layer1">
|
||||||
|
<circle
|
||||||
|
style="fill:#ffffff"
|
||||||
|
id="path1"
|
||||||
|
cx="256"
|
||||||
|
cy="256"
|
||||||
|
r="256" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 458 B |
@@ -2,5 +2,5 @@ set windows-shell := ["pwsh", "-NoLogo", "-NoProfileLoadTime", "-Command"]
|
|||||||
|
|
||||||
install:
|
install:
|
||||||
cargo build --release
|
cargo build --release
|
||||||
upx .\target\release\gta-tools.exe --best
|
cp .\target\release\gta-tools.exe ~\.cargo\bin
|
||||||
cp .\target\release\gta-tools.exe ~\.cargo\bin\
|
cp .\target\release\gta-tools.exe ~\Documents
|
||||||
|
|||||||
+37
-14
@@ -1,11 +1,14 @@
|
|||||||
|
use crate::util::{self, consts::game::WINDOW_TITLE};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use windows::Win32::UI::Input::KeyboardAndMouse::{
|
use windows::Win32::UI::Input::KeyboardAndMouse::{
|
||||||
KEYBD_EVENT_FLAGS, MAP_VIRTUAL_KEY_TYPE, MapVirtualKeyW, keybd_event,
|
KEYBD_EVENT_FLAGS, MAP_VIRTUAL_KEY_TYPE, MapVirtualKeyW, VIRTUAL_KEY, VK_NUMPAD4, VK_NUMPAD6,
|
||||||
|
keybd_event,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const INTERVAL: Duration = Duration::from_secs(60);
|
const INTERVAL: Duration = Duration::from_secs(60);
|
||||||
const VK_SHIFT: u8 = 16;
|
const PRESS_KEYS: [VIRTUAL_KEY; 2] = [VK_NUMPAD4, VK_NUMPAD6];
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct AntiAfk {
|
pub struct AntiAfk {
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
pub interval: Instant,
|
pub interval: Instant,
|
||||||
@@ -20,23 +23,43 @@ impl Default for AntiAfk {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send(vk_code: u8) {
|
impl AntiAfk {
|
||||||
unsafe {
|
pub fn can_activate(&self) -> bool {
|
||||||
|
use util::win::{is_any_key_pressed, is_cursor_visible, is_window_focused};
|
||||||
|
is_window_focused(WINDOW_TITLE) && !is_any_key_pressed(&PRESS_KEYS) && !is_cursor_visible()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn should_activate(&self) -> bool {
|
||||||
|
self.enabled && self.interval.elapsed() >= INTERVAL
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn activate(&mut self) {
|
||||||
|
send(&PRESS_KEYS);
|
||||||
|
self.interval = Instant::now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send(vk_codes: &[VIRTUAL_KEY]) {
|
||||||
|
vk_codes.iter().for_each(|vk_code| unsafe {
|
||||||
keybd_event(
|
keybd_event(
|
||||||
vk_code,
|
vk_code.0 as u8,
|
||||||
u8::try_from(MapVirtualKeyW(u32::from(vk_code), MAP_VIRTUAL_KEY_TYPE(0))).unwrap(),
|
u8::try_from(MapVirtualKeyW(
|
||||||
|
u32::from(vk_code.0),
|
||||||
|
MAP_VIRTUAL_KEY_TYPE(0),
|
||||||
|
))
|
||||||
|
.unwrap(),
|
||||||
KEYBD_EVENT_FLAGS(0),
|
KEYBD_EVENT_FLAGS(0),
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
keybd_event(
|
keybd_event(
|
||||||
vk_code,
|
vk_code.0 as u8,
|
||||||
u8::try_from(MapVirtualKeyW(u32::from(vk_code), MAP_VIRTUAL_KEY_TYPE(0))).unwrap(),
|
u8::try_from(MapVirtualKeyW(
|
||||||
|
u32::from(vk_code.0),
|
||||||
|
MAP_VIRTUAL_KEY_TYPE(0),
|
||||||
|
))
|
||||||
|
.unwrap(),
|
||||||
KEYBD_EVENT_FLAGS(2),
|
KEYBD_EVENT_FLAGS(2),
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
}
|
|
||||||
|
|
||||||
pub fn activate() {
|
|
||||||
send(VK_SHIFT);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
use crate::{features, gui::App, util::countdown::Countdown};
|
use crate::util::{
|
||||||
|
consts::game::{EXE_ENHANCED, EXE_LEGACY},
|
||||||
|
countdown::Countdown,
|
||||||
|
};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use sysinfo::System;
|
use sysinfo::System;
|
||||||
use windows::Win32::{
|
use windows::Win32::{
|
||||||
@@ -6,10 +9,9 @@ use windows::Win32::{
|
|||||||
System::Threading::{OpenProcess, PROCESS_SUSPEND_RESUME},
|
System::Threading::{OpenProcess, PROCESS_SUSPEND_RESUME},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const INTERVAL: Duration = Duration::from_secs(8);
|
const INTERVAL: Duration = Duration::from_secs(10);
|
||||||
const ENHANCED: &str = "GTA5_Enhanced.exe";
|
|
||||||
const LEGACY: &str = "GTA5.exe";
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct EmptySession {
|
pub struct EmptySession {
|
||||||
pub disabled: bool,
|
pub disabled: bool,
|
||||||
pub interval: Instant,
|
pub interval: Instant,
|
||||||
@@ -21,15 +23,29 @@ impl Default for EmptySession {
|
|||||||
Self {
|
Self {
|
||||||
disabled: false,
|
disabled: false,
|
||||||
interval: Instant::now(),
|
interval: Instant::now(),
|
||||||
countdown: Countdown::new(features::empty_session::INTERVAL.as_secs()),
|
countdown: Countdown::new(INTERVAL.as_secs()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EmptySession {
|
||||||
|
pub fn run_timers(&mut self, game_handle: &mut HANDLE) {
|
||||||
|
if self.disabled {
|
||||||
|
self.countdown.count();
|
||||||
|
} else {
|
||||||
|
self.countdown.reset();
|
||||||
|
}
|
||||||
|
if self.interval.elapsed() >= INTERVAL {
|
||||||
|
deactivate(game_handle);
|
||||||
|
self.disabled = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[link(name = "ntdll")]
|
#[link(name = "ntdll")]
|
||||||
unsafe extern "system" {
|
unsafe extern "system" {
|
||||||
pub unsafe fn NtSuspendProcess(ProcessHandle: HANDLE) -> NTSTATUS;
|
unsafe fn NtSuspendProcess(ProcessHandle: HANDLE) -> NTSTATUS;
|
||||||
pub unsafe fn NtResumeProcess(ProcessHandle: HANDLE) -> NTSTATUS;
|
unsafe fn NtResumeProcess(ProcessHandle: HANDLE) -> NTSTATUS;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_gta_pid(sysinfo: &mut System) -> u32 {
|
fn get_gta_pid(sysinfo: &mut System) -> u32 {
|
||||||
@@ -37,27 +53,29 @@ fn get_gta_pid(sysinfo: &mut System) -> u32 {
|
|||||||
if let Some((pid, _)) = sysinfo
|
if let Some((pid, _)) = sysinfo
|
||||||
.processes()
|
.processes()
|
||||||
.iter()
|
.iter()
|
||||||
.find(|(_, p)| p.name() == ENHANCED || p.name() == LEGACY)
|
.find(|(_, p)| p.name() == EXE_ENHANCED || p.name() == EXE_LEGACY)
|
||||||
{
|
{
|
||||||
return pid.as_u32();
|
return pid.as_u32();
|
||||||
};
|
}
|
||||||
u32::MAX
|
u32::MAX
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn activate(app: &mut App) {
|
pub fn activate(game_handle: &mut HANDLE, sysinfo: &mut System) {
|
||||||
let pid = get_gta_pid(&mut app.sysinfo);
|
let pid = get_gta_pid(sysinfo);
|
||||||
if pid == u32::MAX {
|
if pid == u32::MAX {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
unsafe {
|
unsafe {
|
||||||
app.game_handle = OpenProcess(PROCESS_SUSPEND_RESUME, false, pid).unwrap();
|
*game_handle = OpenProcess(PROCESS_SUSPEND_RESUME, false, pid).unwrap();
|
||||||
let _ = NtSuspendProcess(app.game_handle);
|
let _ = NtSuspendProcess(*game_handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deactivate(app: &mut App) {
|
pub fn deactivate(game_handle: &mut HANDLE) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let _ = NtResumeProcess(app.game_handle);
|
if !game_handle.is_invalid() {
|
||||||
let _ = CloseHandle(app.game_handle);
|
let _ = NtResumeProcess(*game_handle);
|
||||||
|
let _ = CloseHandle(*game_handle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-11
@@ -1,37 +1,58 @@
|
|||||||
use crate::util::consts::{ENHANCED, LEGACY};
|
use crate::util::consts::game::{EXE_ENHANCED, EXE_LEGACY};
|
||||||
use std::time::Instant;
|
use std::time::{Duration, Instant};
|
||||||
use sysinfo::System;
|
use sysinfo::System;
|
||||||
|
|
||||||
|
const INTERVAL: Duration = Duration::from_secs(3);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct ForceClose {
|
pub struct ForceClose {
|
||||||
pub button_text: String,
|
pub button_text: String,
|
||||||
pub prompting: bool,
|
prompting: bool,
|
||||||
pub interval: Instant,
|
interval: Instant,
|
||||||
|
current_frame: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ForceClose {
|
impl Default for ForceClose {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
button_text: "Force close game".to_string(),
|
button_text: "Force close game".to_owned(),
|
||||||
prompting: false,
|
prompting: false,
|
||||||
interval: Instant::now(),
|
interval: Instant::now(),
|
||||||
|
current_frame: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ForceClose {
|
impl ForceClose {
|
||||||
pub fn prompting(&mut self) {
|
pub fn prompt(&mut self, force_close_button_clicked: bool, sysinfo: &mut System) {
|
||||||
self.button_text = "Are you sure?".to_string();
|
if force_close_button_clicked && !self.prompting {
|
||||||
self.prompting = true;
|
*self = Self {
|
||||||
self.interval = Instant::now();
|
button_text: "Are you sure?".to_owned(),
|
||||||
|
prompting: true,
|
||||||
|
interval: Instant::now(),
|
||||||
|
current_frame: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.prompting && self.interval.elapsed() <= INTERVAL {
|
||||||
|
if force_close_button_clicked && !self.current_frame {
|
||||||
|
activate(sysinfo);
|
||||||
|
*self = Self::default();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*self = Self::default();
|
||||||
|
}
|
||||||
|
if self.current_frame {
|
||||||
|
self.current_frame = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn activate(sysinfo: &mut System) {
|
fn activate(sysinfo: &mut System) {
|
||||||
sysinfo.refresh_all();
|
sysinfo.refresh_all();
|
||||||
sysinfo
|
sysinfo
|
||||||
.processes()
|
.processes()
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, p)| p.name() == ENHANCED || p.name() == LEGACY)
|
.filter(|(_, p)| p.name() == EXE_ENHANCED || p.name() == EXE_LEGACY)
|
||||||
.for_each(|(_, p)| {
|
.for_each(|(_, p)| {
|
||||||
p.kill();
|
p.kill();
|
||||||
});
|
});
|
||||||
|
|||||||
+114
-58
@@ -1,72 +1,128 @@
|
|||||||
#![allow(clippy::zombie_processes)]
|
use crate::util::consts::game::{EXE_ENHANCED, EXE_LEGACY};
|
||||||
|
use std::path::Path;
|
||||||
use crate::util::consts::{ENHANCED, LEGACY};
|
|
||||||
use std::{os::windows::process::CommandExt, path::Path, process::Command};
|
|
||||||
use sysinfo::System;
|
use sysinfo::System;
|
||||||
|
use windows::{
|
||||||
|
Win32::{
|
||||||
|
Foundation::E_INVALIDARG,
|
||||||
|
NetworkManagement::WindowsFirewall::{
|
||||||
|
INetFwPolicy2, INetFwRule, NET_FW_ACTION_BLOCK, NET_FW_IP_PROTOCOL_ANY,
|
||||||
|
NET_FW_RULE_DIR_IN, NET_FW_RULE_DIR_OUT, NetFwPolicy2, NetFwRule,
|
||||||
|
},
|
||||||
|
System::Com::{
|
||||||
|
CLSCTX_INPROC_SERVER, COINIT_MULTITHREADED, CoCreateInstance, CoInitializeEx,
|
||||||
|
CoUninitialize,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
core::{BSTR, HRESULT},
|
||||||
|
};
|
||||||
|
|
||||||
const FILTER_NAME: &str = "[GTA Tools] Block all traffic for GTA V";
|
const FILTER_NAME_IN: &str = "[GTA Tools] Block all inbound traffic for GTA V";
|
||||||
const CREATE_NO_WINDOW: u32 = 0x0800_0000;
|
const FILTER_NAME_OUT: &str = "[GTA Tools] Block all outbound traffic for GTA V";
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct GameNetworking {
|
||||||
|
pub is_blocked: bool,
|
||||||
|
com_initialized: bool,
|
||||||
|
policy: INetFwPolicy2,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for GameNetworking {
|
||||||
|
fn default() -> Self {
|
||||||
|
let result = unsafe { CoInitializeEx(None, COINIT_MULTITHREADED) };
|
||||||
|
let mut gn = Self {
|
||||||
|
is_blocked: false,
|
||||||
|
com_initialized: result != HRESULT(0x80010106u32 as i32),
|
||||||
|
policy: unsafe { CoCreateInstance(&NetFwPolicy2, None, CLSCTX_INPROC_SERVER).unwrap() },
|
||||||
|
};
|
||||||
|
gn.is_blocked = gn.is_blocked();
|
||||||
|
gn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for GameNetworking {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
if self.com_initialized {
|
||||||
|
CoUninitialize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GameNetworking {
|
||||||
|
pub fn block_all(&mut self, sysinfo: &mut System) {
|
||||||
|
let Some(exe_path) = get_game_exe_path(sysinfo) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let rules = unsafe { self.policy.Rules().unwrap() };
|
||||||
|
let filter_name_in = BSTR::from(FILTER_NAME_IN);
|
||||||
|
let filter_name_out = BSTR::from(FILTER_NAME_OUT);
|
||||||
|
unsafe {
|
||||||
|
let _ = rules.Remove(&filter_name_in);
|
||||||
|
let _ = rules.Remove(&filter_name_out);
|
||||||
|
}
|
||||||
|
let exe_path = BSTR::from(exe_path.to_string_lossy().to_string());
|
||||||
|
unsafe {
|
||||||
|
let inbound_rule: INetFwRule =
|
||||||
|
CoCreateInstance(&NetFwRule, None, CLSCTX_INPROC_SERVER).unwrap();
|
||||||
|
inbound_rule.SetName(&filter_name_in).unwrap();
|
||||||
|
inbound_rule.SetApplicationName(&exe_path).unwrap();
|
||||||
|
inbound_rule.SetDirection(NET_FW_RULE_DIR_IN).unwrap();
|
||||||
|
inbound_rule.SetEnabled(true.into()).unwrap();
|
||||||
|
inbound_rule.SetAction(NET_FW_ACTION_BLOCK).unwrap();
|
||||||
|
inbound_rule.SetProtocol(NET_FW_IP_PROTOCOL_ANY.0).unwrap();
|
||||||
|
rules.Add(&inbound_rule).unwrap();
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
let outbound_rule: INetFwRule =
|
||||||
|
CoCreateInstance(&NetFwRule, None, CLSCTX_INPROC_SERVER).unwrap();
|
||||||
|
outbound_rule.SetName(&filter_name_out).unwrap();
|
||||||
|
outbound_rule.SetApplicationName(&exe_path).unwrap();
|
||||||
|
outbound_rule.SetDirection(NET_FW_RULE_DIR_OUT).unwrap();
|
||||||
|
outbound_rule.SetEnabled(true.into()).unwrap();
|
||||||
|
outbound_rule.SetAction(NET_FW_ACTION_BLOCK).unwrap();
|
||||||
|
outbound_rule.SetProtocol(NET_FW_IP_PROTOCOL_ANY.0).unwrap();
|
||||||
|
rules.Add(&outbound_rule).unwrap();
|
||||||
|
}
|
||||||
|
self.is_blocked = self.is_blocked();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unblock_all(&mut self) {
|
||||||
|
let rules = unsafe { self.policy.Rules().unwrap() };
|
||||||
|
unsafe {
|
||||||
|
let result = rules.Remove(&BSTR::from(FILTER_NAME_IN));
|
||||||
|
if let Err(ref why) = result {
|
||||||
|
if why.code() != E_INVALIDARG {
|
||||||
|
result.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let result = rules.Remove(&BSTR::from(FILTER_NAME_OUT));
|
||||||
|
if let Err(ref why) = result {
|
||||||
|
if why.code() != E_INVALIDARG {
|
||||||
|
result.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.is_blocked = self.is_blocked();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_blocked(&self) -> bool {
|
||||||
|
let rules = unsafe { self.policy.Rules().unwrap() };
|
||||||
|
let in_rule_exists = unsafe { rules.Item(&BSTR::from(FILTER_NAME_IN)).is_ok() };
|
||||||
|
let out_rule_exists = unsafe { rules.Item(&BSTR::from(FILTER_NAME_OUT)).is_ok() };
|
||||||
|
in_rule_exists || out_rule_exists
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn get_game_exe_path(sysinfo: &mut System) -> Option<&Path> {
|
fn get_game_exe_path(sysinfo: &mut System) -> Option<&Path> {
|
||||||
sysinfo.refresh_all();
|
sysinfo.refresh_all();
|
||||||
if let Some((_, process)) = sysinfo
|
if let Some((_, process)) = sysinfo
|
||||||
.processes()
|
.processes()
|
||||||
.iter()
|
.iter()
|
||||||
.find(|(_, p)| p.name() == ENHANCED || p.name() == LEGACY)
|
.find(|(_, p)| p.name() == EXE_ENHANCED || p.name() == EXE_LEGACY)
|
||||||
{
|
{
|
||||||
process.exe()
|
process.exe()
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn block_all(sysinfo: &mut System) {
|
|
||||||
let Some(exe_path) = get_game_exe_path(sysinfo) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let exe_path = exe_path.display().to_string();
|
|
||||||
Command::new("netsh")
|
|
||||||
.args([
|
|
||||||
"advfirewall",
|
|
||||||
"firewall",
|
|
||||||
"add",
|
|
||||||
"rule",
|
|
||||||
&format!("name={FILTER_NAME}"),
|
|
||||||
"dir=out",
|
|
||||||
"action=block",
|
|
||||||
"protocol=ANY",
|
|
||||||
&format!("program={exe_path}"),
|
|
||||||
])
|
|
||||||
.creation_flags(CREATE_NO_WINDOW)
|
|
||||||
.spawn()
|
|
||||||
.unwrap();
|
|
||||||
Command::new("netsh")
|
|
||||||
.args([
|
|
||||||
"advfirewall",
|
|
||||||
"firewall",
|
|
||||||
"add",
|
|
||||||
"rule",
|
|
||||||
&format!("name={FILTER_NAME}"),
|
|
||||||
"dir=in",
|
|
||||||
"action=block",
|
|
||||||
"protocol=ANY",
|
|
||||||
&format!("program={exe_path}"),
|
|
||||||
])
|
|
||||||
.creation_flags(CREATE_NO_WINDOW)
|
|
||||||
.spawn()
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unblock_all() {
|
|
||||||
Command::new("netsh")
|
|
||||||
.args([
|
|
||||||
"advfirewall",
|
|
||||||
"firewall",
|
|
||||||
"delete",
|
|
||||||
"rule",
|
|
||||||
&format!("name={FILTER_NAME}"),
|
|
||||||
])
|
|
||||||
.creation_flags(CREATE_NO_WINDOW)
|
|
||||||
.spawn()
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|||||||
+7
-14
@@ -1,26 +1,19 @@
|
|||||||
use std::{fmt::Display, path::PathBuf, process::Command};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{path::PathBuf, process::Command};
|
||||||
|
use strum::{Display, EnumIter};
|
||||||
use winreg::{RegKey, enums::HKEY_LOCAL_MACHINE};
|
use winreg::{RegKey, enums::HKEY_LOCAL_MACHINE};
|
||||||
|
|
||||||
#[derive(Default, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Default, Debug, Display, PartialEq, Eq, Serialize, Deserialize, EnumIter)]
|
||||||
pub enum Platform {
|
pub enum Platform {
|
||||||
#[default]
|
#[default]
|
||||||
Steam,
|
Steam,
|
||||||
|
#[strum(to_string = "Rockstar Games")]
|
||||||
Rockstar,
|
Rockstar,
|
||||||
|
#[strum(to_string = "Epic Games")]
|
||||||
Epic,
|
Epic,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Platform {
|
#[derive(Debug, Default)]
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let x = match self {
|
|
||||||
Self::Steam => "Steam",
|
|
||||||
Self::Rockstar => "Rockstar Games",
|
|
||||||
Self::Epic => "Epic Games",
|
|
||||||
};
|
|
||||||
write!(f, "{x}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct Launch {
|
pub struct Launch {
|
||||||
pub selected: Platform,
|
pub selected: Platform,
|
||||||
}
|
}
|
||||||
|
|||||||
-298
@@ -1,298 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
features::{
|
|
||||||
self,
|
|
||||||
anti_afk::AntiAfk,
|
|
||||||
empty_session::EmptySession,
|
|
||||||
force_close::ForceClose,
|
|
||||||
launch::{Launch, Platform},
|
|
||||||
},
|
|
||||||
util::{
|
|
||||||
consts::{ENHANCED, LEGACY},
|
|
||||||
elevation,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use eframe::egui;
|
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
use sysinfo::System;
|
|
||||||
use windows::Win32::Foundation::HANDLE;
|
|
||||||
|
|
||||||
const THEME: catppuccin_egui::Theme = catppuccin_egui::MOCHA;
|
|
||||||
|
|
||||||
#[derive(Default, PartialEq, Eq)]
|
|
||||||
pub enum Stage {
|
|
||||||
#[default]
|
|
||||||
Main,
|
|
||||||
About,
|
|
||||||
Debug,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::struct_excessive_bools)]
|
|
||||||
pub struct App {
|
|
||||||
stage: Stage,
|
|
||||||
debug: bool,
|
|
||||||
initialized: bool,
|
|
||||||
elevated: bool,
|
|
||||||
current_frame: bool,
|
|
||||||
pub sysinfo: System,
|
|
||||||
pub game_handle: HANDLE,
|
|
||||||
launch: Launch,
|
|
||||||
force_close: ForceClose,
|
|
||||||
empty_session: EmptySession,
|
|
||||||
anti_afk: AntiAfk,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for App {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
stage: Stage::default(),
|
|
||||||
initialized: false,
|
|
||||||
debug: false,
|
|
||||||
elevated: elevation::is_elevated(),
|
|
||||||
current_frame: false,
|
|
||||||
sysinfo: System::new_all(),
|
|
||||||
game_handle: HANDLE::default(),
|
|
||||||
launch: Launch::default(),
|
|
||||||
force_close: ForceClose::default(),
|
|
||||||
empty_session: EmptySession::default(),
|
|
||||||
anti_afk: AntiAfk::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl eframe::App for App {
|
|
||||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
|
||||||
if !self.initialized {
|
|
||||||
catppuccin_egui::set_theme(ctx, THEME);
|
|
||||||
egui_extras::install_image_loaders(ctx);
|
|
||||||
self.initialized = true;
|
|
||||||
}
|
|
||||||
ctx.request_repaint_after(Duration::from_millis(100));
|
|
||||||
ctx.input(|i| {
|
|
||||||
if i.modifiers.all() && i.key_pressed(egui::Key::D) {
|
|
||||||
self.debug = !self.debug;
|
|
||||||
if !self.debug && self.stage == Stage::Debug {
|
|
||||||
self.stage = Stage::Main;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
self.run_timers();
|
|
||||||
egui::TopBottomPanel::bottom("bottom_panel").show(ctx, |ui| {
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.selectable_value(&mut self.stage, Stage::Main, "Main");
|
|
||||||
ui.selectable_value(&mut self.stage, Stage::About, "About");
|
|
||||||
if self.debug {
|
|
||||||
ui.selectable_value(&mut self.stage, Stage::Debug, "Debug");
|
|
||||||
}
|
|
||||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
|
||||||
let button = ui
|
|
||||||
.add_enabled(!self.elevated, egui::Button::new("Elevate"))
|
|
||||||
.on_hover_text("Relaunch ourselves as administrator.")
|
|
||||||
.on_disabled_hover_text("We are already running elevated.");
|
|
||||||
if button.clicked() {
|
|
||||||
elevation::elevate();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
|
||||||
egui::ScrollArea::both()
|
|
||||||
.auto_shrink([false, false])
|
|
||||||
.show(ui, |ui| match self.stage {
|
|
||||||
Stage::Main => {
|
|
||||||
self.header(ui, "Game");
|
|
||||||
self.show_game(ctx, ui);
|
|
||||||
self.header(ui, "Session");
|
|
||||||
self.show_session(ctx, ui);
|
|
||||||
self.header(ui, "Network");
|
|
||||||
self.show_network(ctx, ui);
|
|
||||||
}
|
|
||||||
Stage::About => {
|
|
||||||
self.show_about(ctx, ui);
|
|
||||||
}
|
|
||||||
Stage::Debug => {
|
|
||||||
self.show_debug(ctx, ui);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl App {
|
|
||||||
#[allow(clippy::unused_self)]
|
|
||||||
fn header(&self, ui: &mut egui::Ui, text: &str) {
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.label(text);
|
|
||||||
ui.add(egui::Separator::default().horizontal());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn show_game(&mut self, _ctx: &egui::Context, ui: &mut egui::Ui) {
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
if ui.button("Launch").clicked() {
|
|
||||||
features::launch::launch(&self.launch.selected);
|
|
||||||
};
|
|
||||||
egui::ComboBox::from_label("")
|
|
||||||
.selected_text(self.launch.selected.to_string())
|
|
||||||
.width(120.0)
|
|
||||||
.show_ui(ui, |ui| {
|
|
||||||
ui.selectable_value(
|
|
||||||
&mut self.launch.selected,
|
|
||||||
Platform::Steam,
|
|
||||||
Platform::Steam.to_string(),
|
|
||||||
);
|
|
||||||
ui.selectable_value(
|
|
||||||
&mut self.launch.selected,
|
|
||||||
Platform::Rockstar,
|
|
||||||
Platform::Rockstar.to_string(),
|
|
||||||
);
|
|
||||||
ui.selectable_value(
|
|
||||||
&mut self.launch.selected,
|
|
||||||
Platform::Epic,
|
|
||||||
Platform::Epic.to_string(),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
let force_close_button = ui.button(&self.force_close.button_text);
|
|
||||||
if force_close_button.clicked() && !self.force_close.prompting {
|
|
||||||
self.force_close.prompting();
|
|
||||||
self.current_frame = true;
|
|
||||||
};
|
|
||||||
if self.force_close.prompting
|
|
||||||
&& self.force_close.interval.elapsed() <= Duration::from_secs(3)
|
|
||||||
{
|
|
||||||
if force_close_button.clicked() && !self.current_frame {
|
|
||||||
features::force_close::activate(&mut self.sysinfo);
|
|
||||||
self.force_close = ForceClose::default();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.force_close = ForceClose::default();
|
|
||||||
}
|
|
||||||
if self.current_frame {
|
|
||||||
self.current_frame = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn show_session(&mut self, _ctx: &egui::Context, ui: &mut egui::Ui) {
|
|
||||||
ui.add_enabled_ui(!self.empty_session.disabled, |ui| {
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
if ui.button("Empty current session").clicked() {
|
|
||||||
self.empty_session.interval = Instant::now();
|
|
||||||
self.empty_session.disabled = true;
|
|
||||||
features::empty_session::activate(self);
|
|
||||||
}
|
|
||||||
ui.label(&self.empty_session.countdown.i_string);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
ui.checkbox(&mut self.anti_afk.enabled, "Anti AFK")
|
|
||||||
.on_hover_text("You should be tabbed in\nwhile this is enabled.");
|
|
||||||
if self.anti_afk.enabled && self.anti_afk.interval.elapsed() >= features::anti_afk::INTERVAL
|
|
||||||
{
|
|
||||||
features::anti_afk::activate();
|
|
||||||
self.anti_afk.interval = Instant::now();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn show_network(&mut self, _ctx: &egui::Context, ui: &mut egui::Ui) {
|
|
||||||
egui::Frame::new()
|
|
||||||
.inner_margin(egui::vec2(4.0, 4.0))
|
|
||||||
.stroke(egui::Stroke::new(1.0, THEME.overlay1))
|
|
||||||
.show(ui, |ui| {
|
|
||||||
let response = ui.add_enabled_ui(self.elevated, |ui| {
|
|
||||||
ui.label("Game's network access");
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
if ui.button("Block").clicked() {
|
|
||||||
features::game_networking::block_all(&mut self.sysinfo);
|
|
||||||
};
|
|
||||||
if ui.button("Unblock").clicked() {
|
|
||||||
features::game_networking::unblock_all();
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
response
|
|
||||||
.response
|
|
||||||
.on_disabled_hover_text("This requires administrator.\nUse the Elevate button.")
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::unused_self)]
|
|
||||||
fn show_about(&self, _ctx: &egui::Context, ui: &mut egui::Ui) {
|
|
||||||
ui.with_layout(egui::Layout::bottom_up(egui::Align::Center), |ui| {
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.spacing_mut().item_spacing.x = 0.0;
|
|
||||||
ui.label("with love from ");
|
|
||||||
ui.hyperlink_to("futile", "http://futile.eu");
|
|
||||||
});
|
|
||||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
|
||||||
ui.label(format!(
|
|
||||||
"v{} {}",
|
|
||||||
env!("CARGO_PKG_VERSION"),
|
|
||||||
if cfg!(debug_assertions) { "(dev)" } else { "" }
|
|
||||||
));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
ui.add(egui::Image::new(egui::include_image!("../assets/icon.png")))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn show_debug(&mut self, _ctx: &egui::Context, ui: &mut egui::Ui) {
|
|
||||||
let pid = self
|
|
||||||
.sysinfo
|
|
||||||
.processes()
|
|
||||||
.iter()
|
|
||||||
.find(|(_, p)| p.name() == ENHANCED || p.name() == LEGACY)
|
|
||||||
.map_or_else(
|
|
||||||
|| "no pid found!".to_string(),
|
|
||||||
|(pid, _)| pid.as_u32().to_string(),
|
|
||||||
);
|
|
||||||
if ui.button("refresh sysinfo").clicked() {
|
|
||||||
self.sysinfo.refresh_all();
|
|
||||||
}
|
|
||||||
ui.label(format!("gta pid: {pid}"));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_timers(&mut self) {
|
|
||||||
if self.empty_session.disabled {
|
|
||||||
self.empty_session.countdown.count();
|
|
||||||
} else {
|
|
||||||
self.empty_session.countdown.reset();
|
|
||||||
}
|
|
||||||
if self.empty_session.interval.elapsed() >= features::empty_session::INTERVAL {
|
|
||||||
features::empty_session::deactivate(self);
|
|
||||||
self.empty_session.disabled = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_icon() -> eframe::egui::IconData {
|
|
||||||
let (icon_rgba, icon_width, icon_height) = {
|
|
||||||
let icon = include_bytes!("../assets/icon.png");
|
|
||||||
let image = image::load_from_memory(icon).unwrap().into_rgba8();
|
|
||||||
let (width, height) = image.dimensions();
|
|
||||||
let rgba = image.into_raw();
|
|
||||||
(rgba, width, height)
|
|
||||||
};
|
|
||||||
eframe::egui::IconData {
|
|
||||||
rgba: icon_rgba,
|
|
||||||
width: icon_width,
|
|
||||||
height: icon_height,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run() {
|
|
||||||
let options = eframe::NativeOptions {
|
|
||||||
viewport: egui::ViewportBuilder::default()
|
|
||||||
.with_resizable(false)
|
|
||||||
.with_maximize_button(false)
|
|
||||||
.with_inner_size([256.0, 232.0])
|
|
||||||
.with_icon(load_icon()),
|
|
||||||
centered: true,
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
eframe::run_native(
|
|
||||||
"GTA Tools",
|
|
||||||
options,
|
|
||||||
Box::new(|_cc| Ok(Box::<App>::default())),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
+246
@@ -0,0 +1,246 @@
|
|||||||
|
use crate::{
|
||||||
|
features,
|
||||||
|
gui::{settings::Settings, tools},
|
||||||
|
util::{consts::game::WINDOW_TITLE, meta::Meta, persistent_state::PersistentState, win},
|
||||||
|
};
|
||||||
|
use eframe::egui;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
use strum::{Display, EnumIter};
|
||||||
|
|
||||||
|
pub const WINDOW_SIZE: [f32; 2] = [240.0, 240.0];
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, Display, PartialEq, Eq, EnumIter)]
|
||||||
|
enum Stage {
|
||||||
|
#[default]
|
||||||
|
Main,
|
||||||
|
Settings,
|
||||||
|
About,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct Flags {
|
||||||
|
pub elevated: bool,
|
||||||
|
pub debug: bool,
|
||||||
|
closing: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct App {
|
||||||
|
pub meta: Meta,
|
||||||
|
pub settings: Settings,
|
||||||
|
stage: Stage,
|
||||||
|
pub flags: Flags,
|
||||||
|
pub sysinfo: sysinfo::System,
|
||||||
|
game_handle: windows::Win32::Foundation::HANDLE,
|
||||||
|
pub anti_afk: features::anti_afk::AntiAfk,
|
||||||
|
empty_session: features::empty_session::EmptySession,
|
||||||
|
force_close: features::force_close::ForceClose,
|
||||||
|
game_networking: features::game_networking::GameNetworking,
|
||||||
|
pub launch: features::launch::Launch,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl eframe::App for App {
|
||||||
|
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||||
|
ctx.request_repaint_after(Duration::from_millis(100));
|
||||||
|
self.empty_session.run_timers(&mut self.game_handle);
|
||||||
|
egui::TopBottomPanel::bottom("bottom_panel")
|
||||||
|
.exact_height(25.0)
|
||||||
|
.show(ctx, |ui| {
|
||||||
|
ui.with_layout(egui::Layout::left_to_right(egui::Align::Center), |ui| {
|
||||||
|
tools::build_menu::<Stage>(ui, &mut self.stage);
|
||||||
|
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||||
|
let button = ui
|
||||||
|
.add_enabled(!self.flags.elevated, egui::Button::new("Elevate"))
|
||||||
|
.on_hover_text("Relaunch ourselves as administrator.")
|
||||||
|
.on_disabled_hover_text("We are already running elevated.");
|
||||||
|
if button.clicked() {
|
||||||
|
win::elevate(win::ElevationExitMethod::Gentle(&mut self.flags.closing));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
|
egui::ScrollArea::vertical()
|
||||||
|
.auto_shrink([false, true])
|
||||||
|
.show(ui, |ui| match self.stage {
|
||||||
|
Stage::Main => self.show_main_stage(ctx, ui),
|
||||||
|
Stage::Settings => self.show_settings_stage(ctx, ui),
|
||||||
|
Stage::About => self.show_about_stage(ctx, ui),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if tools::debug_keycombo_pressed(ctx) || tools::debug_viewport_close_pressed(ctx) {
|
||||||
|
self.flags.debug = !self.flags.debug;
|
||||||
|
}
|
||||||
|
if self.flags.debug {
|
||||||
|
self.show_debug_viewport(ctx);
|
||||||
|
}
|
||||||
|
if self.flags.closing {
|
||||||
|
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl App {
|
||||||
|
fn show_game_section(&mut self, _ctx: &egui::Context, ui: &mut egui::Ui) {
|
||||||
|
tools::header(ui, "Game");
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
if ui.button("Launch").clicked() {
|
||||||
|
features::launch::launch(&self.launch.selected);
|
||||||
|
}
|
||||||
|
egui::ComboBox::from_id_salt("Launch")
|
||||||
|
.selected_text(self.launch.selected.to_string())
|
||||||
|
.width(120.0)
|
||||||
|
.show_ui(ui, |ui| {
|
||||||
|
tools::build_menu(ui, &mut self.launch.selected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
let force_close_button = ui.add_sized(
|
||||||
|
[104.0, 0.0],
|
||||||
|
egui::Button::new(&self.force_close.button_text),
|
||||||
|
);
|
||||||
|
self.force_close
|
||||||
|
.prompt(force_close_button.clicked(), &mut self.sysinfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_session_section(&mut self, _ctx: &egui::Context, ui: &mut egui::Ui) {
|
||||||
|
tools::header(ui, "Session");
|
||||||
|
ui.add_enabled_ui(!self.empty_session.disabled, |ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
if ui.button("Empty current session").clicked() {
|
||||||
|
self.empty_session.interval = Instant::now();
|
||||||
|
self.empty_session.disabled = true;
|
||||||
|
features::empty_session::activate(&mut self.game_handle, &mut self.sysinfo);
|
||||||
|
}
|
||||||
|
ui.label(&self.empty_session.countdown.i_string);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.checkbox(&mut self.anti_afk.enabled, "Anti AFK")
|
||||||
|
.on_hover_text("You should be tabbed in\nfor this to work.");
|
||||||
|
if self.anti_afk.enabled {
|
||||||
|
ui.add_space(8.0);
|
||||||
|
ui.add_enabled_ui(false, |ui| {
|
||||||
|
ui.label(if win::is_window_focused(WINDOW_TITLE) {
|
||||||
|
"GTA is focused."
|
||||||
|
} else {
|
||||||
|
"GTA is not focused!"
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if self.anti_afk.can_activate() && self.anti_afk.should_activate() {
|
||||||
|
self.anti_afk.activate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_network_section(&mut self, _ctx: &egui::Context, ui: &mut egui::Ui) {
|
||||||
|
tools::header(ui, "Network");
|
||||||
|
egui::Frame::new()
|
||||||
|
.outer_margin(egui::vec2(0.0, -2.0))
|
||||||
|
.show(ui, |ui| {
|
||||||
|
let response = ui.add_enabled_ui(self.flags.elevated, |ui| {
|
||||||
|
let label = ui.label("Game's network access");
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
let available_width = label.rect.width();
|
||||||
|
let spacing = ui.spacing().item_spacing.x;
|
||||||
|
let button_width = (available_width - spacing) / 2.0;
|
||||||
|
if ui
|
||||||
|
.add_sized([button_width, 18.0], egui::Button::new("Block"))
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
self.game_networking.block_all(&mut self.sysinfo);
|
||||||
|
}
|
||||||
|
if ui
|
||||||
|
.add_sized([button_width, 18.0], egui::Button::new("Unblock"))
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
self.game_networking.unblock_all();
|
||||||
|
}
|
||||||
|
let tint = match self.game_networking.is_blocked {
|
||||||
|
true => egui::Color32::from_hex("#f96554").unwrap(),
|
||||||
|
false => egui::Color32::from_hex("#68f954").unwrap(),
|
||||||
|
};
|
||||||
|
ui.add(
|
||||||
|
egui::Image::new(egui::include_image!("../../assets/circle.svg"))
|
||||||
|
.max_size([4.0, 4.0].into())
|
||||||
|
.tint(tint),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
response.response.on_disabled_hover_text(
|
||||||
|
"This requires administrator.\nUse the Elevate button.",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_main_stage(&mut self, ctx: &egui::Context, ui: &mut egui::Ui) {
|
||||||
|
self.show_game_section(ctx, ui);
|
||||||
|
self.show_session_section(ctx, ui);
|
||||||
|
self.show_network_section(ctx, ui);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_settings_stage(&mut self, ctx: &egui::Context, ui: &mut egui::Ui) {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
let selection = self.settings.theme;
|
||||||
|
ui.label("Theme");
|
||||||
|
egui::ComboBox::from_id_salt("Theme")
|
||||||
|
.selected_text(self.settings.theme.to_string())
|
||||||
|
.show_ui(ui, |ui| {
|
||||||
|
tools::build_menu(ui, &mut self.settings.theme);
|
||||||
|
});
|
||||||
|
if selection != self.settings.theme {
|
||||||
|
catppuccin_egui::set_theme(ctx, self.settings.theme.into());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ui.checkbox(&mut self.settings.start_elevated, "Always start elevated");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_about_stage(&self, _ctx: &egui::Context, ui: &mut egui::Ui) {
|
||||||
|
ui.with_layout(egui::Layout::bottom_up(egui::Align::Center), |ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.spacing_mut().item_spacing.x = 0.0;
|
||||||
|
ui.label("with ");
|
||||||
|
ui.hyperlink_to("❤", "https://codeberg.org/futile/gta-tools");
|
||||||
|
ui.label(" from ");
|
||||||
|
ui.hyperlink_to("futile", "http://futile.eu");
|
||||||
|
});
|
||||||
|
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||||
|
ui.label(format!(
|
||||||
|
"v{} {}",
|
||||||
|
self.meta.current_version,
|
||||||
|
if cfg!(debug_assertions) { "(dev)" } else { "" }
|
||||||
|
));
|
||||||
|
let button = ui.add_enabled_ui(self.meta.newer_version_available, |ui| {
|
||||||
|
ui.style_mut().spacing.button_padding = egui::Vec2::new(3.0, 0.0);
|
||||||
|
ui.button("⬇")
|
||||||
|
.on_disabled_hover_text("Already up to date.")
|
||||||
|
.on_hover_text(format!(
|
||||||
|
"New version available! ({})",
|
||||||
|
self.meta.latest_release.version
|
||||||
|
))
|
||||||
|
});
|
||||||
|
if button.inner.clicked() {
|
||||||
|
open::that(&self.meta.latest_release.download_url).unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
ui.add(egui::Image::new(egui::include_image!(
|
||||||
|
"../../assets/icon.png"
|
||||||
|
)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for App {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// save any persistent state to config file
|
||||||
|
PersistentState {
|
||||||
|
launcher: self.launch.selected,
|
||||||
|
settings: self.settings.clone(),
|
||||||
|
}
|
||||||
|
.set();
|
||||||
|
// make sure we are not suspending game
|
||||||
|
features::empty_session::deactivate(&mut self.game_handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
use crate::{
|
||||||
|
gui::{
|
||||||
|
app::{App, WINDOW_SIZE},
|
||||||
|
tools,
|
||||||
|
},
|
||||||
|
util::consts::{
|
||||||
|
APP_STORAGE_PATH,
|
||||||
|
game::{EXE_ENHANCED, EXE_LEGACY},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use eframe::egui;
|
||||||
|
|
||||||
|
impl App {
|
||||||
|
pub fn show_debug_viewport(&mut self, ctx: &egui::Context) {
|
||||||
|
let main = ctx.input(|i| i.viewport().outer_rect.unwrap_or(egui::Rect::EVERYTHING));
|
||||||
|
let builder = egui::ViewportBuilder::default()
|
||||||
|
.with_title("GTA Tools Debug")
|
||||||
|
.with_minimize_button(false)
|
||||||
|
.with_maximize_button(false)
|
||||||
|
.with_inner_size(WINDOW_SIZE)
|
||||||
|
.with_position([main.right() - 12.0, main.min.y])
|
||||||
|
.with_icon(tools::load_icon());
|
||||||
|
ctx.show_viewport_immediate(
|
||||||
|
egui::ViewportId::from_hash_of("debug_viewport"),
|
||||||
|
builder,
|
||||||
|
|ctx, _class| {
|
||||||
|
if tools::debug_keycombo_pressed(ctx) {
|
||||||
|
self.flags.debug = !self.flags.debug;
|
||||||
|
}
|
||||||
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
|
egui::ScrollArea::both()
|
||||||
|
.auto_shrink([false, true])
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.collapsing("misc", |ui| {
|
||||||
|
if ui.button("open storage path").clicked() {
|
||||||
|
open::that_detached(APP_STORAGE_PATH.as_path()).unwrap();
|
||||||
|
}
|
||||||
|
ui.checkbox(
|
||||||
|
&mut self.meta.newer_version_available,
|
||||||
|
"spoof new version available",
|
||||||
|
)
|
||||||
|
.on_hover_text("(this could already be checked if\nthere actually IS a new version)");
|
||||||
|
ui.scope(|ui| {
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::{GetForegroundWindow, GetWindowTextW};
|
||||||
|
let mut buffer = [0; 512];
|
||||||
|
let current_title = unsafe {
|
||||||
|
let hwnd = GetForegroundWindow();
|
||||||
|
let length = GetWindowTextW(hwnd, &mut buffer);
|
||||||
|
String::from_utf16_lossy(&buffer[..length as usize])
|
||||||
|
};
|
||||||
|
ui.label(format!("focused: \"{current_title}\""));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
ui.collapsing("anti afk", |ui| {
|
||||||
|
ui.label(format!(
|
||||||
|
"timer: {}",
|
||||||
|
self.anti_afk.interval.elapsed().as_secs()
|
||||||
|
));
|
||||||
|
ui.label(format!(
|
||||||
|
"can activate: {}",
|
||||||
|
self.anti_afk.can_activate()
|
||||||
|
));
|
||||||
|
});
|
||||||
|
ui.collapsing("sysinfo", |ui| {
|
||||||
|
if ui.button("refresh all").clicked() {
|
||||||
|
self.sysinfo.refresh_all();
|
||||||
|
}
|
||||||
|
let pid = self
|
||||||
|
.sysinfo
|
||||||
|
.processes()
|
||||||
|
.iter()
|
||||||
|
.find(|(_, p)| p.name() == EXE_ENHANCED || p.name() == EXE_LEGACY)
|
||||||
|
.map_or_else(
|
||||||
|
|| "no pid found!".to_owned(),
|
||||||
|
|(pid, _)| pid.as_u32().to_string(),
|
||||||
|
);
|
||||||
|
ui.label(format!("gta pid: {pid}"));
|
||||||
|
});
|
||||||
|
ui.collapsing("app state", |ui| ui.label(format!("{self:#?}")));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
mod app;
|
||||||
|
mod debug;
|
||||||
|
pub mod run;
|
||||||
|
pub mod settings;
|
||||||
|
mod tools;
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
use crate::{
|
||||||
|
gui::{
|
||||||
|
app::{App, WINDOW_SIZE},
|
||||||
|
tools,
|
||||||
|
},
|
||||||
|
util::{self, consts::APP_STORAGE_PATH, persistent_state::PersistentState},
|
||||||
|
};
|
||||||
|
use eframe::egui;
|
||||||
|
use std::{fs::File, io::Write};
|
||||||
|
|
||||||
|
fn panic_hook(panic_info: &std::panic::PanicHookInfo<'_>) {
|
||||||
|
let log_path = APP_STORAGE_PATH.join("panic.log");
|
||||||
|
let mut file = File::options()
|
||||||
|
.create(true)
|
||||||
|
.append(true)
|
||||||
|
.open(log_path)
|
||||||
|
.unwrap();
|
||||||
|
let timestamp = chrono::Local::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, false);
|
||||||
|
let backtrace = std::backtrace::Backtrace::force_capture();
|
||||||
|
let message = format!("[{timestamp}]\n{panic_info}\nstack backtrace:\n{backtrace}\n");
|
||||||
|
file.write_all(message.as_bytes()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn app_creator(
|
||||||
|
cc: &eframe::CreationContext<'_>,
|
||||||
|
) -> Result<Box<dyn eframe::App>, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
// use our own panic hook which logs all panics to a file
|
||||||
|
std::panic::set_hook(Box::new(panic_hook));
|
||||||
|
// initialize App early to modify some things before returning it
|
||||||
|
let mut app = Box::<App>::default();
|
||||||
|
// load previously selected launch platform & settings from persistent state
|
||||||
|
if let Some(persistent_state) = PersistentState::get() {
|
||||||
|
app.launch.selected = persistent_state.launcher;
|
||||||
|
app.settings = persistent_state.settings;
|
||||||
|
}
|
||||||
|
// check if we're elevated. if not, and the user wants an elevated launch - relaunch elevated
|
||||||
|
let elevated = util::win::is_elevated();
|
||||||
|
if app.settings.start_elevated && !elevated {
|
||||||
|
util::win::elevate(util::win::ElevationExitMethod::Forced);
|
||||||
|
}
|
||||||
|
app.flags.elevated = elevated;
|
||||||
|
// refresh sysinfo because it initializes with nothing
|
||||||
|
app.sysinfo.refresh_all();
|
||||||
|
// enable image loading support in egui
|
||||||
|
egui_extras::install_image_loaders(&cc.egui_ctx);
|
||||||
|
// set our initial theme, from earlier loaded settings
|
||||||
|
catppuccin_egui::set_theme(&cc.egui_ctx, app.settings.theme.into());
|
||||||
|
// apply some global styling that we like
|
||||||
|
cc.egui_ctx.style_mut(|style| {
|
||||||
|
style.spacing.item_spacing = egui::vec2(4.0, 4.0);
|
||||||
|
style.interaction.selectable_labels = false;
|
||||||
|
});
|
||||||
|
// load any extra fonts that we need
|
||||||
|
let mut fonts = egui::FontDefinitions::default();
|
||||||
|
fonts.font_data.insert(
|
||||||
|
"Ubuntu-Regular".to_owned(),
|
||||||
|
egui::FontData::from_static(include_bytes!("../../assets/Ubuntu-Regular.ttf")).into(),
|
||||||
|
);
|
||||||
|
fonts.families.insert(
|
||||||
|
egui::FontFamily::Name("Ubuntu-Regular".into()),
|
||||||
|
vec!["Ubuntu-Regular".to_owned()],
|
||||||
|
);
|
||||||
|
cc.egui_ctx.set_fonts(fonts);
|
||||||
|
// finally return the App
|
||||||
|
Ok(app)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run() {
|
||||||
|
let options = eframe::NativeOptions {
|
||||||
|
viewport: egui::ViewportBuilder::default()
|
||||||
|
.with_resizable(false)
|
||||||
|
.with_maximize_button(false)
|
||||||
|
.with_inner_size(WINDOW_SIZE)
|
||||||
|
.with_icon(tools::load_icon()),
|
||||||
|
centered: true,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
eframe::run_native("GTA Tools", options, Box::new(app_creator)).unwrap();
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use strum::{Display, EnumIter};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Display, PartialEq, Eq, Serialize, Deserialize, EnumIter)]
|
||||||
|
pub enum Theme {
|
||||||
|
#[strum(to_string = "Catppuccin Latte")]
|
||||||
|
CatppuccinLatte,
|
||||||
|
#[strum(to_string = "Catppuccin Frappe")]
|
||||||
|
CatppuccinFrappe,
|
||||||
|
#[strum(to_string = "Catppuccin Macchiato")]
|
||||||
|
CatppuccinMacchiato,
|
||||||
|
#[strum(to_string = "Catppuccin Mocha")]
|
||||||
|
CatppuccinMocha,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Theme> for catppuccin_egui::Theme {
|
||||||
|
fn from(val: Theme) -> Self {
|
||||||
|
match val {
|
||||||
|
Theme::CatppuccinLatte => catppuccin_egui::LATTE,
|
||||||
|
Theme::CatppuccinFrappe => catppuccin_egui::FRAPPE,
|
||||||
|
Theme::CatppuccinMacchiato => catppuccin_egui::MACCHIATO,
|
||||||
|
Theme::CatppuccinMocha => catppuccin_egui::MOCHA,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Settings {
|
||||||
|
pub theme: Theme,
|
||||||
|
pub start_elevated: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Settings {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
theme: Theme::CatppuccinMocha,
|
||||||
|
start_elevated: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
use eframe::egui;
|
||||||
|
|
||||||
|
pub fn load_icon() -> egui::IconData {
|
||||||
|
let icon = include_bytes!("../../assets/icon.png");
|
||||||
|
let image = image::load_from_memory(icon).unwrap().into_rgba8();
|
||||||
|
let (width, height) = image.dimensions();
|
||||||
|
egui::IconData {
|
||||||
|
rgba: image.into_raw(),
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn header(ui: &mut egui::Ui, text: &str) {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label(egui::RichText::new(text).font(egui::FontId::new(
|
||||||
|
12.5,
|
||||||
|
egui::FontFamily::Name("Ubuntu-Regular".into()),
|
||||||
|
)));
|
||||||
|
ui.add(egui::Separator::default().horizontal());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_menu<E>(ui: &mut egui::Ui, current_value: &mut E)
|
||||||
|
where
|
||||||
|
E: strum::IntoEnumIterator + std::fmt::Display + std::cmp::PartialEq + Copy,
|
||||||
|
{
|
||||||
|
E::iter().for_each(|variant| {
|
||||||
|
ui.selectable_value(current_value, variant, variant.to_string());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn debug_keycombo_pressed(ctx: &egui::Context) -> bool {
|
||||||
|
ctx.input(|i| i.modifiers.all() && i.key_pressed(egui::Key::D))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn debug_viewport_close_pressed(ctx: &egui::Context) -> bool {
|
||||||
|
ctx.input(|i| {
|
||||||
|
i.raw
|
||||||
|
.viewports
|
||||||
|
.get(&egui::ViewportId::from_hash_of("debug_viewport"))
|
||||||
|
.filter(|vp| vp.close_requested())
|
||||||
|
.is_some()
|
||||||
|
})
|
||||||
|
}
|
||||||
+1
-1
@@ -5,5 +5,5 @@ mod gui;
|
|||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
gui::run();
|
gui::run::run();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
use semver::Version;
|
||||||
|
|
||||||
|
const APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));
|
||||||
|
const CODEBERG_ENDPOINT_ROOT: &str = "https://codeberg.org/api/v1";
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Release {
|
||||||
|
pub version: Version,
|
||||||
|
pub download_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Release {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
version: Version::parse(env!("CARGO_PKG_VERSION")).unwrap(),
|
||||||
|
download_url: String::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_latest_release() -> Option<Release> {
|
||||||
|
let request_url = format!("{CODEBERG_ENDPOINT_ROOT}/repos/futile/gta-tools/releases/latest");
|
||||||
|
let ureq: ureq::Agent = ureq::Agent::config_builder()
|
||||||
|
.user_agent(APP_USER_AGENT)
|
||||||
|
.build()
|
||||||
|
.into();
|
||||||
|
let mut response = ureq.get(request_url).call().ok()?;
|
||||||
|
let json = response.body_mut().read_json::<serde_json::Value>().ok()?;
|
||||||
|
let tag_name = json["tag_name"].as_str()?;
|
||||||
|
let browser_download_url = json["assets"][0]["browser_download_url"].as_str()?;
|
||||||
|
Some(Release {
|
||||||
|
version: Version::parse(tag_name).expect("expected a valid semver pattern"),
|
||||||
|
download_url: String::from(browser_download_url),
|
||||||
|
})
|
||||||
|
}
|
||||||
+10
-2
@@ -1,2 +1,10 @@
|
|||||||
pub const ENHANCED: &str = "GTA5_Enhanced.exe";
|
use std::{path::PathBuf, sync::LazyLock};
|
||||||
pub const LEGACY: &str = "GTA5.exe";
|
|
||||||
|
pub static APP_STORAGE_PATH: LazyLock<PathBuf> =
|
||||||
|
LazyLock::new(|| dirs::config_local_dir().unwrap().join("GTA Tools"));
|
||||||
|
|
||||||
|
pub mod game {
|
||||||
|
pub const EXE_ENHANCED: &str = "GTA5_Enhanced.exe";
|
||||||
|
pub const EXE_LEGACY: &str = "GTA5.exe";
|
||||||
|
pub const WINDOW_TITLE: &str = "Grand Theft Auto V";
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Countdown {
|
pub struct Countdown {
|
||||||
pub i: u64,
|
i: u64,
|
||||||
pub i_original: u64,
|
i_original: u64,
|
||||||
pub i_string: String,
|
pub i_string: String,
|
||||||
pub interval: Instant,
|
interval: Instant,
|
||||||
pub first_count: bool,
|
first_count: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Countdown {
|
impl Countdown {
|
||||||
@@ -35,6 +36,6 @@ impl Countdown {
|
|||||||
self.i_string = self.i.to_string();
|
self.i_string = self.i.to_string();
|
||||||
if self.i == 0 {
|
if self.i == 0 {
|
||||||
self.reset();
|
self.reset();
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
use windows::{
|
|
||||||
Win32::Foundation::{CloseHandle, HANDLE},
|
|
||||||
Win32::Security::{GetTokenInformation, TOKEN_ELEVATION, TOKEN_QUERY, TokenElevation},
|
|
||||||
Win32::System::Threading::{GetCurrentProcess, OpenProcessToken},
|
|
||||||
Win32::UI::{Shell::ShellExecuteW, WindowsAndMessaging::SW_HIDE},
|
|
||||||
core::{HSTRING, PCWSTR},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn elevate() {
|
|
||||||
let exe = std::env::current_exe().unwrap();
|
|
||||||
unsafe {
|
|
||||||
ShellExecuteW(
|
|
||||||
None,
|
|
||||||
&HSTRING::from("runas"),
|
|
||||||
&HSTRING::from(exe.as_path()),
|
|
||||||
PCWSTR::null(),
|
|
||||||
PCWSTR::null(),
|
|
||||||
SW_HIDE,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
std::process::exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_elevated() -> bool {
|
|
||||||
unsafe {
|
|
||||||
let mut token: HANDLE = HANDLE::default();
|
|
||||||
if OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut token).is_ok() {
|
|
||||||
let mut elevation = TOKEN_ELEVATION::default();
|
|
||||||
let size = u32::try_from(std::mem::size_of::<TOKEN_ELEVATION>()).unwrap();
|
|
||||||
let mut ret_size = size;
|
|
||||||
let result = GetTokenInformation(
|
|
||||||
token,
|
|
||||||
TokenElevation,
|
|
||||||
Some((&raw mut elevation).cast()),
|
|
||||||
size,
|
|
||||||
&mut ret_size,
|
|
||||||
);
|
|
||||||
CloseHandle(token).unwrap();
|
|
||||||
if result.is_ok() && elevation.TokenIsElevated != 0 {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
use crate::util::codeberg;
|
||||||
|
use semver::Version;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Meta {
|
||||||
|
pub current_version: Version,
|
||||||
|
pub latest_release: codeberg::Release,
|
||||||
|
pub newer_version_available: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Meta {
|
||||||
|
fn default() -> Self {
|
||||||
|
let current_version = Version::parse(env!("CARGO_PKG_VERSION")).unwrap();
|
||||||
|
let latest_release = codeberg::get_latest_release().unwrap_or_default();
|
||||||
|
let newer_version_available = matches!(
|
||||||
|
¤t_version.cmp_precedence(&latest_release.version),
|
||||||
|
std::cmp::Ordering::Less
|
||||||
|
);
|
||||||
|
Self {
|
||||||
|
current_version,
|
||||||
|
latest_release,
|
||||||
|
newer_version_available,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-1
@@ -1,3 +1,6 @@
|
|||||||
|
mod codeberg;
|
||||||
pub mod consts;
|
pub mod consts;
|
||||||
pub mod countdown;
|
pub mod countdown;
|
||||||
pub mod elevation;
|
pub mod meta;
|
||||||
|
pub mod persistent_state;
|
||||||
|
pub mod win;
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
use crate::{features::launch::Platform, gui::settings::Settings, util::consts::APP_STORAGE_PATH};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{
|
||||||
|
fs::{self, File},
|
||||||
|
io::Write,
|
||||||
|
path::PathBuf,
|
||||||
|
sync::LazyLock,
|
||||||
|
};
|
||||||
|
|
||||||
|
static CONFIG_PATH: LazyLock<PathBuf> = LazyLock::new(|| APP_STORAGE_PATH.join("config.json"));
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct PersistentState {
|
||||||
|
pub launcher: Platform,
|
||||||
|
pub settings: Settings,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PersistentState {
|
||||||
|
pub fn get() -> Option<Self> {
|
||||||
|
fs::read_to_string(CONFIG_PATH.as_path())
|
||||||
|
.ok()
|
||||||
|
.and_then(|config| serde_json::from_str::<Self>(&config).ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(&self) {
|
||||||
|
let config_path = CONFIG_PATH.as_path();
|
||||||
|
let config_path_parent = config_path.parent().unwrap();
|
||||||
|
if !config_path_parent.exists() {
|
||||||
|
fs::create_dir(config_path_parent).unwrap();
|
||||||
|
}
|
||||||
|
let mut config_file = File::create(config_path).unwrap();
|
||||||
|
let json = serde_json::to_string_pretty(&self).unwrap();
|
||||||
|
config_file.write_all(json.as_bytes()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
use windows::{
|
||||||
|
Win32::{
|
||||||
|
Foundation::{CloseHandle, HANDLE},
|
||||||
|
Security::{GetTokenInformation, TOKEN_ELEVATION, TOKEN_QUERY, TokenElevation},
|
||||||
|
System::Threading::{GetCurrentProcess, OpenProcessToken},
|
||||||
|
UI::{
|
||||||
|
Input::KeyboardAndMouse::{GetAsyncKeyState, VIRTUAL_KEY},
|
||||||
|
Shell::ShellExecuteW,
|
||||||
|
WindowsAndMessaging::{
|
||||||
|
CURSOR_SHOWING, CURSORINFO, GetCursorInfo, GetForegroundWindow, GetWindowTextW,
|
||||||
|
SW_NORMAL,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
core::{HSTRING, PCWSTR},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub enum ElevationExitMethod<'a> {
|
||||||
|
Gentle(&'a mut bool),
|
||||||
|
Forced,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_cursor_visible() -> bool {
|
||||||
|
let mut ci = CURSORINFO {
|
||||||
|
cbSize: u32::try_from(std::mem::size_of::<CURSORINFO>()).unwrap(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
unsafe {
|
||||||
|
GetCursorInfo(&mut ci).unwrap();
|
||||||
|
}
|
||||||
|
ci.flags == CURSOR_SHOWING
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_window_focused(target_title: &str) -> bool {
|
||||||
|
let mut buffer = [0; 512];
|
||||||
|
unsafe {
|
||||||
|
let hwnd = GetForegroundWindow();
|
||||||
|
let length = GetWindowTextW(hwnd, &mut buffer);
|
||||||
|
let current_title = String::from_utf16_lossy(&buffer[..length as usize]);
|
||||||
|
current_title == target_title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_any_key_pressed(keys: &[VIRTUAL_KEY]) -> bool {
|
||||||
|
keys.iter()
|
||||||
|
.any(|&key| unsafe { (GetAsyncKeyState(i32::from(key.0)) & i16::MIN) != 0 })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn elevate(closing: ElevationExitMethod) {
|
||||||
|
let exe = std::env::current_exe().unwrap();
|
||||||
|
unsafe {
|
||||||
|
ShellExecuteW(
|
||||||
|
None,
|
||||||
|
&HSTRING::from("runas"),
|
||||||
|
&HSTRING::from(exe.as_path()),
|
||||||
|
PCWSTR::null(),
|
||||||
|
PCWSTR::null(),
|
||||||
|
SW_NORMAL,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
match closing {
|
||||||
|
ElevationExitMethod::Gentle(closing) => *closing = true,
|
||||||
|
ElevationExitMethod::Forced => std::process::exit(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_elevated() -> bool {
|
||||||
|
let mut token: HANDLE = HANDLE::default();
|
||||||
|
unsafe {
|
||||||
|
if OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut token).is_err() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let mut elevation = TOKEN_ELEVATION::default();
|
||||||
|
let mut size = u32::try_from(std::mem::size_of::<TOKEN_ELEVATION>()).unwrap();
|
||||||
|
let result = GetTokenInformation(
|
||||||
|
token,
|
||||||
|
TokenElevation,
|
||||||
|
Some((&raw mut elevation).cast()),
|
||||||
|
size,
|
||||||
|
&mut size,
|
||||||
|
);
|
||||||
|
CloseHandle(token).unwrap();
|
||||||
|
result.is_ok() && elevation.TokenIsElevated != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user