15 Commits
16 changed files with 236 additions and 526 deletions
Generated
+8 -346
View File
@@ -301,35 +301,6 @@ 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.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fc4bff745c9b4c7fb1e97b25d13153da2bc7796260141df62378998d070207f"
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"
@@ -413,15 +384,6 @@ version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376"
[[package]]
name = "deranged"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc"
dependencies = [
"powerfmt",
]
[[package]] [[package]]
name = "dispatch" name = "dispatch"
version = "0.2.0" version = "0.2.0"
@@ -727,12 +689,6 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]] [[package]]
name = "foreign-types" name = "foreign-types"
version = "0.5.0" version = "0.5.0"
@@ -779,17 +735,6 @@ dependencies = [
"windows-targets 0.52.6", "windows-targets 0.52.6",
] ]
[[package]]
name = "getrandom"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
dependencies = [
"cfg-if",
"libc",
"wasi 0.11.1+wasi-snapshot-preview1",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.3.3" version = "0.3.3"
@@ -799,7 +744,7 @@ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"r-efi", "r-efi",
"wasi 0.14.3+wasi-0.2.4", "wasi",
] ]
[[package]] [[package]]
@@ -880,7 +825,7 @@ dependencies = [
[[package]] [[package]]
name = "gta-tools" name = "gta-tools"
version = "0.7.0" version = "0.8.0"
dependencies = [ dependencies = [
"catppuccin-egui", "catppuccin-egui",
"chrono", "chrono",
@@ -888,14 +833,11 @@ dependencies = [
"egui_extras", "egui_extras",
"image", "image",
"open", "open",
"semver",
"serde", "serde",
"serde_json", "serde_json",
"static_vcruntime", "static_vcruntime",
"strum", "strum",
"sysinfo", "windows",
"ureq",
"windows 0.62.0",
"winreg", "winreg",
"winresource", "winresource",
] ]
@@ -928,23 +870,6 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
[[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]] [[package]]
name = "iana-time-zone" name = "iana-time-zone"
version = "0.1.63" version = "0.1.63"
@@ -1159,7 +1084,7 @@ version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
dependencies = [ dependencies = [
"getrandom 0.3.3", "getrandom",
"libc", "libc",
] ]
@@ -1337,21 +1262,6 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
[[package]]
name = "ntapi"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
dependencies = [
"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"
@@ -1551,16 +1461,6 @@ dependencies = [
"objc2-core-foundation", "objc2-core-foundation",
] ]
[[package]]
name = "objc2-io-kit"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71c1c64d6120e51cd86033f67176b1cb66780c2efe34dec55176f77befd93c0a"
dependencies = [
"libc",
"objc2-core-foundation",
]
[[package]] [[package]]
name = "objc2-io-surface" name = "objc2-io-surface"
version = "0.3.1" version = "0.3.1"
@@ -1821,12 +1721,6 @@ dependencies = [
"zerovec", "zerovec",
] ]
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]] [[package]]
name = "proc-macro-crate" name = "proc-macro-crate"
version = "3.3.0" version = "3.3.0"
@@ -1937,20 +1831,6 @@ dependencies = [
"bytemuck", "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.16",
"libc",
"untrusted",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "roxmltree" name = "roxmltree"
version = "0.20.0" version = "0.20.0"
@@ -1983,50 +1863,6 @@ dependencies = [
"windows-sys 0.60.2", "windows-sys 0.60.2",
] ]
[[package]]
name = "rustls"
version = "0.23.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc"
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.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79"
dependencies = [
"zeroize",
]
[[package]]
name = "rustls-webpki"
version = "0.103.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc"
dependencies = [
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.22" version = "1.0.22"
@@ -2060,12 +1896,6 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[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"
@@ -2248,12 +2078,6 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]] [[package]]
name = "svgtypes" name = "svgtypes"
version = "0.15.3" version = "0.15.3"
@@ -2286,20 +2110,6 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "sysinfo"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07cec4dc2d2e357ca1e610cfb07de2fa7a10fc3e9fe89f72545f3d244ea87753"
dependencies = [
"libc",
"memchr",
"ntapi",
"objc2-core-foundation",
"objc2-io-kit",
"windows 0.61.3",
]
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.69" version = "1.0.69"
@@ -2334,36 +2144,6 @@ dependencies = [
"zune-jpeg", "zune-jpeg",
] ]
[[package]]
name = "time"
version = "0.3.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83bde6f1ec10e72d583d91623c939f623002284ef622b87de38cfd546cbf2031"
dependencies = [
"deranged",
"num-conv",
"powerfmt",
"serde",
"time-core",
"time-macros",
]
[[package]]
name = "time-core"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b"
[[package]]
name = "time-macros"
version = "0.2.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3"
dependencies = [
"num-conv",
"time-core",
]
[[package]] [[package]]
name = "tiny-skia" name = "tiny-skia"
version = "0.11.4" version = "0.11.4"
@@ -2475,45 +2255,6 @@ version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "ureq"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00432f493971db5d8e47a65aeb3b02f8226b9b11f1450ff86bb772776ebadd70"
dependencies = [
"base64",
"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.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe120bb823a0061680e66e9075942fcdba06d46551548c2c259766b9558bc9a"
dependencies = [
"base64",
"http",
"httparse",
"log",
]
[[package]] [[package]]
name = "url" name = "url"
version = "2.5.7" version = "2.5.7"
@@ -2548,12 +2289,6 @@ dependencies = [
"xmlwriter", "xmlwriter",
] ]
[[package]]
name = "utf-8"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]] [[package]]
name = "utf8_iter" name = "utf8_iter"
version = "1.0.4" version = "1.0.4"
@@ -2576,12 +2311,6 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.14.3+wasi-0.2.4" version = "0.14.3+wasi-0.2.4"
@@ -2795,15 +2524,6 @@ dependencies = [
"web-sys", "web-sys",
] ]
[[package]]
name = "webpki-roots"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2"
dependencies = [
"rustls-pki-types",
]
[[package]] [[package]]
name = "weezl" name = "weezl"
version = "0.1.10" version = "0.1.10"
@@ -2841,39 +2561,17 @@ 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.61.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
dependencies = [
"windows-collections 0.2.0",
"windows-core 0.61.2",
"windows-future 0.2.1",
"windows-link 0.1.3",
"windows-numerics 0.2.0",
]
[[package]] [[package]]
name = "windows" name = "windows"
version = "0.62.0" version = "0.62.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9579d0e6970fd5250aa29aba5994052385ff55cf7b28a059e484bb79ea842e42" checksum = "9579d0e6970fd5250aa29aba5994052385ff55cf7b28a059e484bb79ea842e42"
dependencies = [ dependencies = [
"windows-collections 0.3.0", "windows-collections",
"windows-core 0.62.0", "windows-core 0.62.0",
"windows-future 0.3.0", "windows-future",
"windows-link 0.2.0", "windows-link 0.2.0",
"windows-numerics 0.3.0", "windows-numerics",
]
[[package]]
name = "windows-collections"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
dependencies = [
"windows-core 0.61.2",
] ]
[[package]] [[package]]
@@ -2911,17 +2609,6 @@ dependencies = [
"windows-strings 0.5.0", "windows-strings 0.5.0",
] ]
[[package]]
name = "windows-future"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
dependencies = [
"windows-core 0.61.2",
"windows-link 0.1.3",
"windows-threading 0.1.0",
]
[[package]] [[package]]
name = "windows-future" name = "windows-future"
version = "0.3.0" version = "0.3.0"
@@ -2930,7 +2617,7 @@ checksum = "b2194dee901458cb79e1148a4e9aac2b164cc95fa431891e7b296ff0b2f1d8a6"
dependencies = [ dependencies = [
"windows-core 0.62.0", "windows-core 0.62.0",
"windows-link 0.2.0", "windows-link 0.2.0",
"windows-threading 0.2.0", "windows-threading",
] ]
[[package]] [[package]]
@@ -2967,16 +2654,6 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65"
[[package]]
name = "windows-numerics"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
dependencies = [
"windows-core 0.61.2",
"windows-link 0.1.3",
]
[[package]] [[package]]
name = "windows-numerics" name = "windows-numerics"
version = "0.3.0" version = "0.3.0"
@@ -3107,15 +2784,6 @@ dependencies = [
"windows_x86_64_msvc 0.53.0", "windows_x86_64_msvc 0.53.0",
] ]
[[package]]
name = "windows-threading"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6"
dependencies = [
"windows-link 0.1.3",
]
[[package]] [[package]]
name = "windows-threading" name = "windows-threading"
version = "0.2.0" version = "0.2.0"
@@ -3463,12 +3131,6 @@ 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 = "zerotrie" name = "zerotrie"
version = "0.2.2" version = "0.2.2"
+2 -4
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "gta-tools" name = "gta-tools"
version = "0.7.0" version = "0.8.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
@@ -18,16 +18,14 @@ egui_extras = { version = "0.32.2", default-features = false, features = [
] } ] }
image = { version = "0.25.8", default-features = false, features = ["png"] } image = { version = "0.25.8", default-features = false, features = ["png"] }
open = "5.3.2" open = "5.3.2"
semver = "1.0.26"
serde = { version = "1.0.219", features = ["derive"] } serde = { version = "1.0.219", features = ["derive"] }
serde_json = "1.0.143" serde_json = "1.0.143"
strum = { version = "0.27.2", features = ["derive"] } strum = { version = "0.27.2", features = ["derive"] }
sysinfo = "0.37.0"
ureq = { version = "3.1.0", features = ["json"] }
windows = { version = "0.62.0", features = [ windows = { version = "0.62.0", features = [
"Win32_NetworkManagement_WindowsFirewall", "Win32_NetworkManagement_WindowsFirewall",
"Win32_Security", "Win32_Security",
"Win32_System_Com", "Win32_System_Com",
"Win32_System_Diagnostics_ToolHelp",
"Win32_System_Threading", "Win32_System_Threading",
"Win32_UI_Input_KeyboardAndMouse", "Win32_UI_Input_KeyboardAndMouse",
"Win32_UI_Shell", "Win32_UI_Shell",
+10 -4
View File
@@ -2,7 +2,7 @@
A toolset of convenient things for GTA V Online. A toolset of convenient things for GTA V Online.
![](https://i.vgy.me/aQ1qgw.png) ![](https://i.vgy.me/OrzaR8.png) ![](https://i.vgy.me/9j6ZSO.png) ![](https://i.vgy.me/xJgskM.png)
## Installing ## Installing
@@ -16,13 +16,13 @@ You will need the Rust toolchain, which can be obtained [here](https://rustup.rs
Every feature of GTA Tools is Legacy/Enhanced-agnostic. Some functionality of GTA Tools requires administrator access. If necessary, GTA Tools can either be started as admin manually, or, the user can simply use the <kbd>Elevate</kbd> button to relaunch GTA Tools as admin. Every feature of GTA Tools is Legacy/Enhanced-agnostic. Some functionality of GTA Tools requires administrator access. If necessary, GTA Tools can either be started as admin manually, or, the user can simply use the <kbd>Elevate</kbd> button to relaunch GTA Tools as admin.
It is recommended to use an up-to-date version of GTA Tools from [releases](https://codeberg.org/futile/gta-tools/releases). You can check for updates to your existing installation by going to the <kbd>About</kbd> page of GTA Tools and hovering the <kbd>↓</kbd> button near the version number. It is recommended to always use an up-to-date version of GTA Tools from [releases](https://codeberg.org/futile/gta-tools/releases). You can also easily access the current latest release by going to the <kbd>About</kbd> page of GTA Tools and clicking the <kbd>↓</kbd> button near the version number.
#### Game #### Game
This section is quite simple. This section is quite simple.
There is a <kbd>Launch</kbd>[^1] feature, which will start your game on the chosen launcher and game version. There is a <kbd>Launch</kbd> feature, which will start your game on the chosen launcher and game version.
There is also a <kbd>Force close game</kbd> feature, which simply kills all game processes. This *does not* touch Rockstar Games Launcher or any other processes, only ones named `GTA5_Enhanced.exe` or `GTA5.exe`. There is also a <kbd>Force close game</kbd> feature, which simply kills all game processes. This *does not* touch Rockstar Games Launcher or any other processes, only ones named `GTA5_Enhanced.exe` or `GTA5.exe`.
@@ -56,5 +56,11 @@ This feature is primarily useful for **replay glitching**, which is an exploit t
5. At this point, <kbd>Unblock</kbd> the game's internet access and load back into online 5. At this point, <kbd>Unblock</kbd> the game's internet access and load back into online
6. Finally, spend any amount of money, and you should receive the finale cut, but also be able to go play the finale again immediately 6. Finally, spend any amount of money, and you should receive the finale cut, but also be able to go play the finale again immediately
## Issues
- I have noticed that on my current Windows 10 install, when not elevated (administrator), the <kbd>Force close game</kbd> and <kbd>Empty current session</kbd> features can fail due to being denied access. This was tested by other people and is not guaranteed to happen. If this does happen to you, I recommend using GTA Tools always in elevated mode. Check "Always start elevated" in the Settings tab.
[^1]: Otherwise, if you are, for example, using those keys to fly a plane, <kbd>Anti AFK</kbd> activating could cancel the input. [^1]: Otherwise, if you are, for example, using those keys to fly a plane, <kbd>Anti AFK</kbd> activating could cancel the input.
[^2]: Specifically, the Windows mouse cursor, not the in-game mouse cursor. This check is mostly used to prevent typing while you are in the Rockstar overlay. [^2]: Specifically, the Windows mouse cursor, not the in-game mouse cursor. This check is mostly used to prevent typing while you are in the Rockstar overlay.
+20 -15
View File
@@ -1,9 +1,10 @@
use crate::util::{ use crate::util::{
consts::game::{EXE_ENHANCED, EXE_LEGACY}, consts::game::{EXE_ENHANCED, EXE_LEGACY},
countdown::Countdown, countdown::Countdown,
log,
system_info::SystemInfo,
}; };
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use sysinfo::System;
use windows::Win32::{ use windows::Win32::{
Foundation::{CloseHandle, HANDLE, NTSTATUS}, Foundation::{CloseHandle, HANDLE, NTSTATUS},
System::Threading::{OpenProcess, PROCESS_SUSPEND_RESUME}, System::Threading::{OpenProcess, PROCESS_SUSPEND_RESUME},
@@ -48,27 +49,31 @@ unsafe extern "system" {
unsafe fn NtResumeProcess(ProcessHandle: HANDLE) -> NTSTATUS; unsafe fn NtResumeProcess(ProcessHandle: HANDLE) -> NTSTATUS;
} }
fn get_gta_pid(sysinfo: &mut System) -> u32 { fn get_gta_pid(system_info: &mut SystemInfo) -> Option<u32> {
sysinfo.refresh_all(); system_info.refresh();
if let Some((pid, _)) = sysinfo system_info
.processes() .processes()
.iter() .iter()
.find(|(_, p)| p.name() == EXE_ENHANCED || p.name() == EXE_LEGACY) .find(|p| p.name() == EXE_ENHANCED || p.name() == EXE_LEGACY)
{ .map(|p| p.pid())
return pid.as_u32();
}
u32::MAX
} }
pub fn activate(game_handle: &mut HANDLE, sysinfo: &mut System) { pub fn activate(game_handle: &mut HANDLE, system_info: &mut SystemInfo) -> Result<(), ()> {
let pid = get_gta_pid(sysinfo); let Some(pid) = get_gta_pid(system_info) else {
if pid == u32::MAX { return Err(());
return; };
}
unsafe { unsafe {
*game_handle = OpenProcess(PROCESS_SUSPEND_RESUME, false, pid).unwrap(); match OpenProcess(PROCESS_SUSPEND_RESUME, false, pid) {
Ok(handle) => *game_handle = handle,
Err(why) => {
let message = format!("failed to suspend game for empty session:\n{why}");
log::log(log::LogLevel::Error, &message);
return Err(());
}
}
let _ = NtSuspendProcess(*game_handle); let _ = NtSuspendProcess(*game_handle);
} }
Ok(())
} }
pub fn deactivate(game_handle: &mut HANDLE) { pub fn deactivate(game_handle: &mut HANDLE) {
+19 -11
View File
@@ -1,6 +1,9 @@
use crate::util::consts::game::{EXE_ENHANCED, EXE_LEGACY}; use crate::util::{
consts::game::{EXE_ENHANCED, EXE_LEGACY},
log,
system_info::SystemInfo,
};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use sysinfo::System;
const INTERVAL: Duration = Duration::from_secs(3); const INTERVAL: Duration = Duration::from_secs(3);
@@ -24,7 +27,7 @@ impl Default for ForceClose {
} }
impl ForceClose { impl ForceClose {
pub fn prompt(&mut self, force_close_button_clicked: bool, sysinfo: &mut System) { pub fn prompt(&mut self, force_close_button_clicked: bool, system_info: &mut SystemInfo) {
if force_close_button_clicked && !self.counting { if force_close_button_clicked && !self.counting {
self.button_text = "Are you sure?".to_owned(); self.button_text = "Are you sure?".to_owned();
self.timer = Instant::now(); self.timer = Instant::now();
@@ -34,7 +37,7 @@ impl ForceClose {
if self.counting && self.timer.elapsed() >= INTERVAL { if self.counting && self.timer.elapsed() >= INTERVAL {
self.reset(); self.reset();
} else if force_close_button_clicked && !self.current_frame { } else if force_close_button_clicked && !self.current_frame {
activate(sysinfo); activate(system_info);
self.reset(); self.reset();
} }
self.finish_current_frame(); self.finish_current_frame();
@@ -51,14 +54,19 @@ impl ForceClose {
} }
} }
fn activate(sysinfo: &mut System) { fn activate(system_info: &mut SystemInfo) {
sysinfo.refresh_all(); system_info.refresh();
sysinfo system_info
.processes() .processes()
.iter() .iter()
.filter(|(_, p)| p.name() == EXE_ENHANCED || p.name() == EXE_LEGACY) .filter(|p| p.name() == EXE_ENHANCED || p.name() == EXE_LEGACY)
.for_each(|(_, p)| { .for_each(|p| {
p.kill(); if !p.kill() {
log::log(
log::LogLevel::Error,
"failed to force close game, probably due to access denied",
);
}
}); });
sysinfo.refresh_all(); system_info.refresh();
} }
+11 -13
View File
@@ -1,10 +1,12 @@
use crate::util::consts::game::{EXE_ENHANCED, EXE_LEGACY}; use crate::util::{
consts::game::{EXE_ENHANCED, EXE_LEGACY},
system_info::SystemInfo,
};
use std::{ use std::{
path::Path, path::Path,
time::{Duration, Instant}, time::{Duration, Instant},
}; };
use strum::{Display, EnumIter}; use strum::{Display, EnumIter};
use sysinfo::System;
use windows::{ use windows::{
Win32::{ Win32::{
NetworkManagement::WindowsFirewall::{ NetworkManagement::WindowsFirewall::{
@@ -71,8 +73,8 @@ impl Drop for GameNetworking {
} }
impl GameNetworking { impl GameNetworking {
pub fn block_all(&mut self, sysinfo: &mut System) { pub fn block_all(&mut self, system_info: &mut SystemInfo) {
let Some(exe_path) = get_game_exe_path(sysinfo) else { let Some(exe_path) = get_game_exe_path(system_info) else {
self.blocked_status = BlockedStatus::Failed; self.blocked_status = BlockedStatus::Failed;
return; return;
}; };
@@ -133,15 +135,11 @@ impl GameNetworking {
} }
} }
fn get_game_exe_path(sysinfo: &mut System) -> Option<&Path> { fn get_game_exe_path(system_info: &mut SystemInfo) -> Option<&Path> {
sysinfo.refresh_all(); system_info.refresh();
if let Some((_, process)) = sysinfo system_info
.processes() .processes()
.iter() .iter()
.find(|(_, p)| p.name() == EXE_ENHANCED || p.name() == EXE_LEGACY) .find(|p| p.name() == EXE_ENHANCED || p.name() == EXE_LEGACY)
{ .and_then(|p| p.exe())
process.exe()
} else {
None
}
} }
+30 -27
View File
@@ -1,7 +1,9 @@
use crate::{ use crate::{
features, features,
gui::{colours, settings::Settings, tools, ui_ext::UiExt}, gui::{colours, settings::Settings, tools, ui_ext::UiExt},
util::{consts::game::WINDOW_TITLE, meta::Meta, persistent_state::PersistentState, win}, util::{
consts::game::WINDOW_TITLE, persistent_state::PersistentState, system_info::SystemInfo, win,
},
}; };
use eframe::egui; use eframe::egui;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
@@ -36,11 +38,10 @@ impl Default for Flags {
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct App { pub struct App {
pub meta: Meta,
pub settings: Settings, pub settings: Settings,
stage: Stage, stage: Stage,
pub flags: Flags, pub flags: Flags,
pub sysinfo: sysinfo::System, pub system_info: SystemInfo,
game_handle: windows::Win32::Foundation::HANDLE, game_handle: windows::Win32::Foundation::HANDLE,
pub anti_afk: features::anti_afk::AntiAfk, pub anti_afk: features::anti_afk::AntiAfk,
empty_session: features::empty_session::EmptySession, empty_session: features::empty_session::EmptySession,
@@ -109,17 +110,22 @@ impl App {
egui::Button::new(&self.force_close.button_text), egui::Button::new(&self.force_close.button_text),
); );
self.force_close self.force_close
.prompt(force_close_button.clicked(), &mut self.sysinfo); .prompt(force_close_button.clicked(), &mut self.system_info);
} }
fn show_session_section(&mut self, _ctx: &egui::Context, ui: &mut egui::Ui) { fn show_session_section(&mut self, _ctx: &egui::Context, ui: &mut egui::Ui) {
ui.header("Session"); ui.header("Session");
ui.add_enabled_ui(!self.empty_session.disabled, |ui| { ui.add_enabled_ui(!self.empty_session.disabled, |ui| {
ui.horizontal(|ui| { ui.horizontal(|ui| {
if ui.button("Empty current session").clicked() { if ui.button("Empty current session").clicked()
&& features::empty_session::activate(
&mut self.game_handle,
&mut self.system_info,
)
.is_ok()
{
self.empty_session.interval = Instant::now(); self.empty_session.interval = Instant::now();
self.empty_session.disabled = true; 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.label(&self.empty_session.countdown.i_string);
}); });
@@ -167,7 +173,7 @@ impl App {
.add_sized([button_width, 18.0], egui::Button::new("Block")) .add_sized([button_width, 18.0], egui::Button::new("Block"))
.clicked() .clicked()
{ {
self.game_networking.block_all(&mut self.sysinfo); self.game_networking.block_all(&mut self.system_info);
} }
if ui if ui
.add_sized([button_width, 18.0], egui::Button::new("Unblock")) .add_sized([button_width, 18.0], egui::Button::new("Unblock"))
@@ -219,31 +225,28 @@ impl App {
ui.horizontal(|ui| { ui.horizontal(|ui| {
ui.spacing_mut().item_spacing.x = 0.0; ui.spacing_mut().item_spacing.x = 0.0;
ui.label("with "); ui.label("with ");
ui.style_mut().visuals.hyperlink_color = colours::RED; ui.scope(|ui| {
ui.hyperlink_to("", "https://codeberg.org/futile/gta-tools"); ui.style_mut().visuals.hyperlink_color = colours::RED;
ui.hyperlink_to("", "https://codeberg.org/futile/gta-tools");
});
ui.label(" from "); ui.label(" from ");
ui.style_mut().visuals.hyperlink_color =
catppuccin_egui::Theme::from(self.settings.theme).text;
ui.hyperlink_to("futile", "https://futile.eu"); ui.hyperlink_to("futile", "https://futile.eu");
}); });
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
ui.label(format!( if cfg!(debug_assertions) {
"v{} {}", ui.label("(dev)");
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.label(format!("v{}", env!("CARGO_PKG_VERSION")));
ui.scope(|ui| {
ui.style_mut().spacing.button_padding = egui::Vec2::new(3.0, 0.0);
let button = ui
.button("")
.on_hover_text("Go to current latest version.");
if button.clicked() {
let _ =
open::that("https://codeberg.org/futile/gta-tools/releases/latest");
}
});
}); });
}); });
ui.add(egui::Image::new(egui::include_image!( ui.add(egui::Image::new(egui::include_image!(
+7 -18
View File
@@ -17,11 +17,6 @@ impl App {
if ui.button("open storage path").clicked() { if ui.button("open storage path").clicked() {
open::that_detached(path::APP_STORAGE.as_path()).unwrap(); open::that_detached(path::APP_STORAGE.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| { ui.scope(|ui| {
use windows::Win32::UI::WindowsAndMessaging::{ use windows::Win32::UI::WindowsAndMessaging::{
GetForegroundWindow, GetWindowTextW, GetForegroundWindow, GetWindowTextW,
@@ -42,10 +37,7 @@ impl App {
ui.build_menu(&mut self.game_networking.blocked_status); ui.build_menu(&mut self.game_networking.blocked_status);
}); });
}); });
if ui if ui.add(egui::Button::new("force refresh theme")).clicked() {
.add(egui::Button::new("force refresh theme"))
.clicked()
{
catppuccin_egui::set_theme(ui.ctx(), self.settings.theme.into()); catppuccin_egui::set_theme(ui.ctx(), self.settings.theme.into());
} }
}); });
@@ -56,19 +48,16 @@ impl App {
)); ));
ui.label(format!("can activate: {}", self.anti_afk.can_activate())); ui.label(format!("can activate: {}", self.anti_afk.can_activate()));
}); });
ui.collapsing("sysinfo", |ui| { ui.collapsing("system info", |ui| {
if ui.button("refresh all").clicked() { if ui.button("refresh").clicked() {
self.sysinfo.refresh_all(); self.system_info.refresh();
} }
let pid = self let pid = self
.sysinfo .system_info
.processes() .processes()
.iter() .iter()
.find(|(_, p)| p.name() == EXE_ENHANCED || p.name() == EXE_LEGACY) .find(|p| p.name() == EXE_ENHANCED || p.name() == EXE_LEGACY)
.map_or_else( .map_or_else(|| "no pid found!".to_owned(), |p| p.pid().to_string());
|| "no pid found!".to_owned(),
|(pid, _)| pid.as_u32().to_string(),
);
ui.label(format!("gta pid: {pid}")); ui.label(format!("gta pid: {pid}"));
}); });
ui.collapsing("app state", |ui| ui.label(format!("{self:#?}"))); ui.collapsing("app state", |ui| ui.label(format!("{self:#?}")));
+3 -2
View File
@@ -21,8 +21,8 @@ fn app_creator(
if !app.flags.elevated && app.settings.start_elevated { if !app.flags.elevated && app.settings.start_elevated {
win::elevate(win::ElevationExitMethod::Forced); win::elevate(win::ElevationExitMethod::Forced);
} }
// refresh sysinfo because it initializes with nothing // refresh system info because it initializes with nothing
app.sysinfo.refresh_all(); app.system_info.refresh();
// enable image loading support in egui // enable image loading support in egui
egui_extras::install_image_loaders(&cc.egui_ctx); egui_extras::install_image_loaders(&cc.egui_ctx);
// set our initial theme, from earlier loaded settings. we set the egui theme // set our initial theme, from earlier loaded settings. we set the egui theme
@@ -33,6 +33,7 @@ fn app_creator(
cc.egui_ctx.all_styles_mut(|style| { cc.egui_ctx.all_styles_mut(|style| {
style.spacing.item_spacing = egui::vec2(4.0, 4.0); style.spacing.item_spacing = egui::vec2(4.0, 4.0);
style.interaction.selectable_labels = false; style.interaction.selectable_labels = false;
style.visuals.hyperlink_color = catppuccin_egui::Theme::from(app.settings.theme).text;
}); });
// load any extra fonts that we need // load any extra fonts that we need
let mut fonts = egui::FontDefinitions::default(); let mut fonts = egui::FontDefinitions::default();
+1 -1
View File
@@ -9,7 +9,7 @@ mod util;
fn init_storage() { fn init_storage() {
if !crate::util::consts::path::APP_STORAGE.exists() { if !crate::util::consts::path::APP_STORAGE.exists() {
std::fs::create_dir(crate::util::consts::path::APP_STORAGE.as_path()).unwrap(); std::fs::create_dir_all(crate::util::consts::path::APP_STORAGE.as_path()).unwrap();
} }
} }
+1 -2
View File
@@ -1,7 +1,6 @@
mod codeberg;
pub mod consts; pub mod consts;
pub mod countdown; pub mod countdown;
pub mod log; pub mod log;
pub mod meta;
pub mod persistent_state; pub mod persistent_state;
pub mod system_info;
pub mod win; pub mod win;
-50
View File
@@ -1,50 +0,0 @@
use semver::Version;
use serde::Deserialize;
use std::time::Duration;
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::new(0, 0, 0),
download_url: String::new(),
}
}
}
#[derive(Deserialize)]
struct LatestRelease {
tag_name: String,
assets: Vec<Asset>,
}
#[derive(Deserialize)]
struct Asset {
browser_download_url: String,
}
pub fn get_latest_release() -> Result<Release, Box<dyn std::error::Error>> {
let request_url = format!("{CODEBERG_ENDPOINT_ROOT}/repos/futile/gta-tools/releases/latest");
let mut response = ureq::get(request_url)
.config()
.timeout_global(Some(Duration::from_secs(10)))
.user_agent(APP_USER_AGENT)
.build()
.call()?;
let body = response.body_mut();
let json = body.read_json::<LatestRelease>()?;
let tag_name = json.tag_name;
let browser_download_url = &json.assets[0].browser_download_url;
Ok(Release {
version: Version::parse(&tag_name)?,
download_url: browser_download_url.to_owned(),
})
}
+1 -1
View File
@@ -2,7 +2,7 @@ use crate::util::consts::path;
use std::{fs::File, io::Write}; use std::{fs::File, io::Write};
use strum::Display; use strum::Display;
#[derive(Display)] #[derive(Clone, Copy, Display)]
pub enum LogLevel { pub enum LogLevel {
Error, Error,
Panic, Panic,
-29
View File
@@ -1,29 +0,0 @@
use crate::{util::codeberg, util::log};
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_else(|why| {
let message = format!("failed to get latest codeberg release:\n{why}");
log::log(log::LogLevel::Error, &message);
codeberg::Release::default()
});
let newer_version_available = matches!(
&current_version.cmp_precedence(&latest_release.version),
std::cmp::Ordering::Less
);
Self {
current_version,
latest_release,
newer_version_available,
}
}
}
+120
View File
@@ -0,0 +1,120 @@
use std::{
os::windows::process::CommandExt,
path::{Path, PathBuf},
process::Command,
};
use windows::{
Win32::System::{
Diagnostics::ToolHelp::{
CreateToolhelp32Snapshot, PROCESSENTRY32, Process32First, Process32Next,
TH32CS_SNAPPROCESS,
},
Threading::{
CREATE_NO_WINDOW, OpenProcess, PROCESS_NAME_WIN32, PROCESS_QUERY_LIMITED_INFORMATION,
QueryFullProcessImageNameW,
},
},
core::PWSTR,
};
#[derive(Clone, Debug)]
pub struct Process {
pid: u32,
name: String,
exe: Option<PathBuf>,
}
impl Process {
pub fn pid(&self) -> u32 {
self.pid
}
pub fn name(&self) -> &str {
&self.name
}
pub fn exe(&self) -> Option<&Path> {
self.exe.as_deref()
}
pub fn kill(&self) -> bool {
let mut taskkill = Command::new("taskkill.exe");
taskkill.creation_flags(CREATE_NO_WINDOW.0);
taskkill.arg("/F").arg("/PID").arg(self.pid.to_string());
match taskkill.output() {
Ok(output) => output.status.success(),
Err(_) => false,
}
}
}
#[derive(Debug, Default)]
pub struct SystemInfo {
processes: Vec<Process>,
}
impl SystemInfo {
pub fn refresh(&mut self) {
let mut processes = Vec::new();
let snapshot_handle = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0).unwrap() };
let mut process_entry = PROCESSENTRY32 {
dwSize: size_of::<PROCESSENTRY32>() as u32,
..Default::default()
};
unsafe { Process32First(snapshot_handle, &raw mut process_entry).unwrap() };
let exe_full_path = get_exe_full_path(&process_entry);
processes.push(Process {
pid: process_entry.th32ProcessID,
name: c_char_arr_to_string(&process_entry.szExeFile),
exe: exe_full_path,
});
while unsafe { Process32Next(snapshot_handle, &raw mut process_entry) }.is_ok() {
let exe_full_path = get_exe_full_path(&process_entry);
processes.push(Process {
pid: process_entry.th32ProcessID,
name: c_char_arr_to_string(&process_entry.szExeFile),
exe: exe_full_path,
});
}
self.processes = processes;
}
pub fn processes(&self) -> &[Process] {
&self.processes
}
}
fn get_exe_full_path(process_entry: &PROCESSENTRY32) -> Option<PathBuf> {
let process_handle_result = unsafe {
OpenProcess(
PROCESS_QUERY_LIMITED_INFORMATION,
false,
process_entry.th32ProcessID,
)
};
let mut exename = [0u16; 260];
let mut dwsize = exename.len() as u32;
process_handle_result.map_or(None, |process_handle| {
let image_name_result = unsafe {
QueryFullProcessImageNameW(
process_handle,
PROCESS_NAME_WIN32,
PWSTR(exename.as_mut_ptr()),
&raw mut dwsize,
)
};
match image_name_result {
Ok(()) => Some(PathBuf::from(
unsafe { PWSTR(exename.as_mut_ptr()).to_string() }.unwrap(),
)),
Err(_) => None,
}
})
}
fn c_char_arr_to_string(arr: &[i8]) -> String {
arr.iter()
.take_while(|&&b| b != 0)
.map(|&b| b as u8 as char)
.collect()
}
+3 -3
View File
@@ -26,7 +26,7 @@ pub fn is_cursor_visible() -> bool {
..Default::default() ..Default::default()
}; };
unsafe { unsafe {
GetCursorInfo(&mut ci).unwrap(); GetCursorInfo(&raw mut ci).unwrap();
} }
ci.flags == CURSOR_SHOWING ci.flags == CURSOR_SHOWING
} }
@@ -67,7 +67,7 @@ pub fn elevate(closing: ElevationExitMethod) {
pub fn is_elevated() -> bool { pub fn is_elevated() -> bool {
let mut token: HANDLE = HANDLE::default(); let mut token: HANDLE = HANDLE::default();
unsafe { unsafe {
if OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut token).is_err() { if OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &raw mut token).is_err() {
return false; return false;
} }
let mut elevation = TOKEN_ELEVATION::default(); let mut elevation = TOKEN_ELEVATION::default();
@@ -77,7 +77,7 @@ pub fn is_elevated() -> bool {
TokenElevation, TokenElevation,
Some((&raw mut elevation).cast()), Some((&raw mut elevation).cast()),
size, size,
&mut size, &raw mut size,
); );
CloseHandle(token).unwrap(); CloseHandle(token).unwrap();
result.is_ok() && elevation.TokenIsElevated != 0 result.is_ok() && elevation.TokenIsElevated != 0