Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb27334f57
|
||
|
|
1e13905885
|
||
|
|
8fc73b2992
|
||
|
|
43b72995dd
|
||
|
|
d3473d46e3
|
||
|
|
1363f5b125
|
||
|
|
d15b8070ec | ||
|
|
efccd6ed50 | ||
|
|
5c73ee7bd5 | ||
|
|
445b63fa4c | ||
|
|
941868dcc8 | ||
|
|
762a3b9b26 | ||
|
|
0cb211c103 | ||
|
|
90b32c5c5a | ||
|
|
71c3c69ea2 | ||
|
|
b553e418f5 | ||
|
|
ab026c0bc6 | ||
|
|
32e21f58a1 | ||
|
|
b0c339cc5f | ||
|
|
c2584b5d01 | ||
|
|
3322f9582f | ||
|
|
5348a1d6ee | ||
|
|
05ce68cb47 | ||
|
|
f63d24ea43 | ||
|
|
356d5f17e5 | ||
|
|
bc5e80aaca | ||
|
|
e050231692 |
@@ -0,0 +1,31 @@
|
|||||||
|
name: Create release for pushed tag
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "*"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release-on-tag:
|
||||||
|
runs-on: windows-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v5
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
- name: Create release
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
TAG: ${{ github.ref_name }}
|
||||||
|
run: |
|
||||||
|
if gh release view "$TAG" >/dev/null 2>&1; then
|
||||||
|
echo "A release already exists for $TAG, skipping"
|
||||||
|
else
|
||||||
|
cargo build --release
|
||||||
|
prev_tag=$(gh release list --limit 100 --json tagName --jq '.[0].tagName')
|
||||||
|
notes="**Full Changelog**: https://github.com/${{ github.repository }}/compare/${prev_tag}...${TAG}"
|
||||||
|
gh release create "$TAG" target/release/gta-tools.exe \
|
||||||
|
--title "$TAG" \
|
||||||
|
--notes "$notes"
|
||||||
|
fi
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
name: Create releases for existing tags
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release-retroactively:
|
||||||
|
runs-on: windows-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- name: Install Rust toolchain
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
- name: Get tags and create release for each
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
mapfile -t tags < <(git tag -l)
|
||||||
|
echo "${#tags[@]} tags found."
|
||||||
|
prev_tag=""
|
||||||
|
for tag in "${tags[@]}"; do
|
||||||
|
if gh release view "$tag" >/dev/null 2>&1; then
|
||||||
|
echo "A release already exists for $tag, skipping"
|
||||||
|
else
|
||||||
|
echo "Proceeding with tag $tag"
|
||||||
|
git checkout "$tag"
|
||||||
|
cargo build --release
|
||||||
|
if [ -n "$prev_tag" ]; then
|
||||||
|
notes="**Full Changelog**: https://github.com/${{ github.repository }}/compare/${prev_tag}...${tag}"
|
||||||
|
else
|
||||||
|
notes="**Full Changelog**: https://github.com/${{ github.repository }}/commits/${tag}"
|
||||||
|
fi
|
||||||
|
gh release create "$tag" target/release/gta-tools.exe \
|
||||||
|
--title "$tag" \
|
||||||
|
--notes "$notes"
|
||||||
|
fi
|
||||||
|
prev_tag="$tag"
|
||||||
|
done
|
||||||
Generated
+8
-346
@@ -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.9.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
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "gta-tools"
|
name = "gta-tools"
|
||||||
version = "0.7.0"
|
version = "0.9.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",
|
||||||
|
|||||||
@@ -2,12 +2,15 @@
|
|||||||
|
|
||||||
A toolset of convenient things for GTA V Online.
|
A toolset of convenient things for GTA V Online.
|
||||||
|
|
||||||
 
|
<picture>
|
||||||
|
<source srcset="https://i.vgy.me/M4sOHh.png" media="(prefers-color-scheme: dark)">
|
||||||
|
<img src="https://i.vgy.me/mpO9uc.png">
|
||||||
|
</picture>
|
||||||
|
|
||||||
## Installing
|
## Installing
|
||||||
|
|
||||||
**Option 1** — <ins>Download</ins>
|
**Option 1** — <ins>Download</ins>
|
||||||
Download the latest release [here](https://codeberg.org/futile/gta-tools/releases/download/latest/gta-tools.exe) and place it somewhere convenient for you, such as Documents. You could then make a shortcut titled "GTA Tools", and pin it to taskbar or Start.
|
Download the latest release [here](https://github.com/elituf/gta-tools/releases/latest/download/gta-tools.exe) and place it somewhere convenient for you, such as Documents. You could then make a shortcut titled "GTA Tools", and pin it to taskbar or Start.
|
||||||
|
|
||||||
**Option 2** — <ins>Build from source</ins>
|
**Option 2** — <ins>Build from source</ins>
|
||||||
You will need the Rust toolchain, which can be obtained [here](https://rustup.rs). Follow the instructions of its installer. Once you have Rust installed, clone this repo and navigate to it. At this point, you should probably `git checkout x.x.x`, where `x.x.x` is the latest tag. You can then run `cargo build --release`. Once you do that, you can use the binary located at `.\target\release\gta-tools.exe` in the same way as **Option 1**.
|
You will need the Rust toolchain, which can be obtained [here](https://rustup.rs). Follow the instructions of its installer. Once you have Rust installed, clone this repo and navigate to it. At this point, you should probably `git checkout x.x.x`, where `x.x.x` is the latest tag. You can then run `cargo build --release`. Once you do that, you can use the binary located at `.\target\release\gta-tools.exe` in the same way as **Option 1**.
|
||||||
@@ -16,15 +19,15 @@ 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://github.com/elituf/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 button requires a second press after the first one for confirmation. This *does not* touch Rockstar Games Launcher or any other processes, only ones named `GTA5_Enhanced.exe` or `GTA5.exe`.
|
||||||
|
|
||||||
#### Session
|
#### Session
|
||||||
|
|
||||||
@@ -56,5 +59,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.
|
||||||
|
|||||||
@@ -4,3 +4,12 @@ install:
|
|||||||
cargo build --release
|
cargo build --release
|
||||||
cp .\target\release\gta-tools.exe ~\.cargo\bin
|
cp .\target\release\gta-tools.exe ~\.cargo\bin
|
||||||
cp .\target\release\gta-tools.exe ~\Documents
|
cp .\target\release\gta-tools.exe ~\Documents
|
||||||
|
|
||||||
|
lint:
|
||||||
|
cargo clippy -- -W clippy::pedantic -W clippy::nursery -A clippy::cast_sign_loss -A clippy::cast_possible_truncation -A clippy::cast_possible_wrap
|
||||||
|
|
||||||
|
lint-full:
|
||||||
|
cargo clippy -- -W clippy::pedantic -W clippy::nursery -W clippy::unwrap_used
|
||||||
|
|
||||||
|
lint-unwraps:
|
||||||
|
cargo clippy -- -W clippy::unwrap_used
|
||||||
@@ -42,7 +42,7 @@ impl AntiAfk {
|
|||||||
fn send(vk_codes: &[VIRTUAL_KEY]) {
|
fn send(vk_codes: &[VIRTUAL_KEY]) {
|
||||||
let mut inputs = Vec::new();
|
let mut inputs = Vec::new();
|
||||||
for &vk_code in vk_codes {
|
for &vk_code in vk_codes {
|
||||||
let scan_code = unsafe { MapVirtualKeyW(u32::from(vk_code.0), MAPVK_VK_TO_VSC) as u16 };
|
let scan_code = unsafe { MapVirtualKeyW(u32::from(vk_code.0), MAPVK_VK_TO_VSC) } as u16;
|
||||||
for event in [KEYBD_EVENT_FLAGS(0), KEYEVENTF_KEYUP] {
|
for event in [KEYBD_EVENT_FLAGS(0), KEYEVENTF_KEYUP] {
|
||||||
let mut input = INPUT {
|
let mut input = INPUT {
|
||||||
r#type: INPUT_KEYBOARD,
|
r#type: INPUT_KEYBOARD,
|
||||||
|
|||||||
@@ -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,34 +49,34 @@ 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;
|
};
|
||||||
}
|
match unsafe { OpenProcess(PROCESS_SUSPEND_RESUME, false, pid) } {
|
||||||
unsafe {
|
Ok(handle) => *game_handle = handle,
|
||||||
*game_handle = OpenProcess(PROCESS_SUSPEND_RESUME, false, pid).unwrap();
|
Err(why) => {
|
||||||
let _ = NtSuspendProcess(*game_handle);
|
let message = format!("failed to suspend game for empty session:\n{why}");
|
||||||
|
log::log(log::LogLevel::Error, &message);
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
let _ = unsafe { NtSuspendProcess(*game_handle) };
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deactivate(game_handle: &mut HANDLE) {
|
pub fn deactivate(game_handle: &mut HANDLE) {
|
||||||
unsafe {
|
if !game_handle.is_invalid() {
|
||||||
if !game_handle.is_invalid() {
|
let _ = unsafe { NtResumeProcess(*game_handle) };
|
||||||
let _ = NtResumeProcess(*game_handle);
|
let _ = unsafe { CloseHandle(*game_handle) };
|
||||||
let _ = CloseHandle(*game_handle);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-11
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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::{
|
||||||
@@ -62,59 +64,55 @@ impl Default for GameNetworking {
|
|||||||
|
|
||||||
impl Drop for GameNetworking {
|
impl Drop for GameNetworking {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
if self.com_initialized {
|
||||||
if self.com_initialized {
|
unsafe { CoUninitialize() };
|
||||||
CoUninitialize();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
};
|
};
|
||||||
let policy: INetFwPolicy2 =
|
let policy: INetFwPolicy2 =
|
||||||
unsafe { CoCreateInstance(&NetFwPolicy2, None, CLSCTX_INPROC_SERVER).unwrap() };
|
unsafe { CoCreateInstance(&NetFwPolicy2, None, CLSCTX_INPROC_SERVER) }.unwrap();
|
||||||
let rules = unsafe { policy.Rules().unwrap() };
|
let rules = unsafe { policy.Rules() }.unwrap();
|
||||||
let exe_path = BSTR::from(exe_path.to_string_lossy().to_string());
|
let exe_path = BSTR::from(exe_path.to_string_lossy().to_string());
|
||||||
for filter in [
|
for filter in [
|
||||||
(FILTER_NAME_IN, NET_FW_RULE_DIR_IN),
|
(FILTER_NAME_IN, NET_FW_RULE_DIR_IN),
|
||||||
(FILTER_NAME_OUT, NET_FW_RULE_DIR_OUT),
|
(FILTER_NAME_OUT, NET_FW_RULE_DIR_OUT),
|
||||||
] {
|
] {
|
||||||
let _ = unsafe { rules.Remove(&BSTR::from(filter.0)) };
|
let _ = unsafe { rules.Remove(&BSTR::from(filter.0)) };
|
||||||
unsafe {
|
let rule: INetFwRule =
|
||||||
let rule: INetFwRule =
|
unsafe { CoCreateInstance(&NetFwRule, None, CLSCTX_INPROC_SERVER) }.unwrap();
|
||||||
CoCreateInstance(&NetFwRule, None, CLSCTX_INPROC_SERVER).unwrap();
|
unsafe { rule.SetName(&BSTR::from(filter.0)) }.unwrap();
|
||||||
rule.SetName(&BSTR::from(filter.0)).unwrap();
|
unsafe { rule.SetApplicationName(&exe_path) }.unwrap();
|
||||||
rule.SetApplicationName(&exe_path).unwrap();
|
unsafe { rule.SetDirection(filter.1) }.unwrap();
|
||||||
rule.SetDirection(filter.1).unwrap();
|
unsafe { rule.SetEnabled(true.into()) }.unwrap();
|
||||||
rule.SetEnabled(true.into()).unwrap();
|
unsafe { rule.SetAction(NET_FW_ACTION_BLOCK) }.unwrap();
|
||||||
rule.SetAction(NET_FW_ACTION_BLOCK).unwrap();
|
unsafe { rule.SetProtocol(NET_FW_IP_PROTOCOL_ANY.0) }.unwrap();
|
||||||
rule.SetProtocol(NET_FW_IP_PROTOCOL_ANY.0).unwrap();
|
unsafe { rules.Add(&rule) }.unwrap();
|
||||||
rules.Add(&rule).unwrap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self.blocked_status = Self::is_blocked().into();
|
self.blocked_status = Self::is_blocked().into();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unblock_all(&mut self) {
|
pub fn unblock_all(&mut self) {
|
||||||
let policy: INetFwPolicy2 =
|
let policy: INetFwPolicy2 =
|
||||||
unsafe { CoCreateInstance(&NetFwPolicy2, None, CLSCTX_INPROC_SERVER).unwrap() };
|
unsafe { CoCreateInstance(&NetFwPolicy2, None, CLSCTX_INPROC_SERVER) }.unwrap();
|
||||||
let rules = unsafe { policy.Rules().unwrap() };
|
let rules = unsafe { policy.Rules() }.unwrap();
|
||||||
unsafe { rules.Remove(&BSTR::from(FILTER_NAME_IN)).unwrap() };
|
unsafe { rules.Remove(&BSTR::from(FILTER_NAME_IN)) }.unwrap();
|
||||||
unsafe { rules.Remove(&BSTR::from(FILTER_NAME_OUT)).unwrap() };
|
unsafe { rules.Remove(&BSTR::from(FILTER_NAME_OUT)) }.unwrap();
|
||||||
self.blocked_status = Self::is_blocked().into();
|
self.blocked_status = Self::is_blocked().into();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_blocked() -> bool {
|
fn is_blocked() -> bool {
|
||||||
let policy: INetFwPolicy2 =
|
let policy: INetFwPolicy2 =
|
||||||
unsafe { CoCreateInstance(&NetFwPolicy2, None, CLSCTX_INPROC_SERVER).unwrap() };
|
unsafe { CoCreateInstance(&NetFwPolicy2, None, CLSCTX_INPROC_SERVER) }.unwrap();
|
||||||
let rules = unsafe { policy.Rules().unwrap() };
|
let rules = unsafe { policy.Rules() }.unwrap();
|
||||||
let in_rule_exists = unsafe { rules.Item(&BSTR::from(FILTER_NAME_IN)).is_ok() };
|
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() };
|
let out_rule_exists = unsafe { rules.Item(&BSTR::from(FILTER_NAME_OUT)) }.is_ok();
|
||||||
in_rule_exists || out_rule_exists
|
in_rule_exists || out_rule_exists
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,15 +131,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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-12
@@ -22,19 +22,17 @@ pub struct Launch {
|
|||||||
pub fn launch(platform: &Platform, version: &LaunchVersion) {
|
pub fn launch(platform: &Platform, version: &LaunchVersion) {
|
||||||
match platform {
|
match platform {
|
||||||
Platform::Steam => {
|
Platform::Steam => {
|
||||||
let steam_url = if *version == LaunchVersion::Enhanced {
|
let steam_url = match version {
|
||||||
"steam://run/3240220"
|
LaunchVersion::Enhanced => "steam://run/3240220",
|
||||||
} else {
|
LaunchVersion::Legacy => "steam://run/271590",
|
||||||
"steam://run/271590"
|
|
||||||
};
|
};
|
||||||
let _ = open::that_detached(steam_url);
|
let _ = open::that_detached(steam_url);
|
||||||
}
|
}
|
||||||
Platform::Rockstar => {
|
Platform::Rockstar => {
|
||||||
let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
|
let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
|
||||||
let rockstar_url = if *version == LaunchVersion::Enhanced {
|
let rockstar_url = match version {
|
||||||
r"SOFTWARE\WOW6432Node\Rockstar Games\GTAV Enhanced"
|
LaunchVersion::Enhanced => r"SOFTWARE\WOW6432Node\Rockstar Games\GTAV Enhanced",
|
||||||
} else {
|
LaunchVersion::Legacy => r"SOFTWARE\WOW6432Node\Rockstar Games\Grand Theft Auto V",
|
||||||
r"SOFTWARE\WOW6432Node\Rockstar Games\Grand Theft Auto V"
|
|
||||||
};
|
};
|
||||||
let Ok(gta_v_enhanced) = hklm.open_subkey(rockstar_url) else {
|
let Ok(gta_v_enhanced) = hklm.open_subkey(rockstar_url) else {
|
||||||
return;
|
return;
|
||||||
@@ -49,10 +47,13 @@ pub fn launch(platform: &Platform, version: &LaunchVersion) {
|
|||||||
let _ = Command::new(play_gtav_path).spawn();
|
let _ = Command::new(play_gtav_path).spawn();
|
||||||
}
|
}
|
||||||
Platform::Epic => {
|
Platform::Epic => {
|
||||||
let epic_url = if *version == LaunchVersion::Enhanced {
|
let epic_url = match version {
|
||||||
"com.epicgames.launcher://apps/8769e24080ea413b8ebca3f1b8c50951?action=launch&silent=true"
|
LaunchVersion::Enhanced => {
|
||||||
} else {
|
"com.epicgames.launcher://apps/8769e24080ea413b8ebca3f1b8c50951?action=launch&silent=true"
|
||||||
"com.epicgames.launcher://apps/9d2d0eb64d5c44529cece33fe2a46482?action=launch&silent=true"
|
}
|
||||||
|
LaunchVersion::Legacy => {
|
||||||
|
"com.epicgames.launcher://apps/9d2d0eb64d5c44529cece33fe2a46482?action=launch&silent=true"
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let _ = open::that_detached(epic_url);
|
let _ = open::that_detached(epic_url);
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-27
@@ -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://github.com/elituf/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://github.com/elituf/gta-tools/releases/latest");
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
ui.add(egui::Image::new(egui::include_image!(
|
ui.add(egui::Image::new(egui::include_image!(
|
||||||
|
|||||||
+11
-22
@@ -17,19 +17,14 @@ 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,
|
||||||
};
|
};
|
||||||
let mut buffer = [0; 512];
|
let current_title = {
|
||||||
let current_title = unsafe {
|
let mut buffer = [0; 512];
|
||||||
let hwnd = GetForegroundWindow();
|
let hwnd = unsafe { GetForegroundWindow() };
|
||||||
let length = GetWindowTextW(hwnd, &mut buffer);
|
let length = unsafe { GetWindowTextW(hwnd, &mut buffer) };
|
||||||
String::from_utf16_lossy(&buffer[..length as usize])
|
String::from_utf16_lossy(&buffer[..length as usize])
|
||||||
};
|
};
|
||||||
ui.label(format!("focused: \"{current_title}\""));
|
ui.label(format!("focused: \"{current_title}\""));
|
||||||
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||||
|
|||||||
@@ -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
@@ -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,
|
||||||
|
|||||||
@@ -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!(
|
|
||||||
¤t_version.cmp_precedence(&latest_release.version),
|
|
||||||
std::cmp::Ordering::Less
|
|
||||||
);
|
|
||||||
Self {
|
|
||||||
current_version,
|
|
||||||
latest_release,
|
|
||||||
newer_version_available,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
use std::{
|
||||||
|
ffi::{OsStr, OsString},
|
||||||
|
os::windows::{ffi::OsStringExt, process::CommandExt},
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
process::Command,
|
||||||
|
};
|
||||||
|
use windows::{
|
||||||
|
Win32::System::{
|
||||||
|
Diagnostics::ToolHelp::{
|
||||||
|
CreateToolhelp32Snapshot, PROCESSENTRY32W, Process32FirstW, Process32NextW,
|
||||||
|
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: OsString,
|
||||||
|
exe: Option<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Process {
|
||||||
|
pub fn pid(&self) -> u32 {
|
||||||
|
self.pid
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(&self) -> &OsStr {
|
||||||
|
&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 = PROCESSENTRY32W {
|
||||||
|
dwSize: size_of::<PROCESSENTRY32W>() as u32,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
unsafe { Process32FirstW(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: wide_array_to_os_string(&process_entry.szExeFile),
|
||||||
|
exe: exe_full_path,
|
||||||
|
});
|
||||||
|
while unsafe { Process32NextW(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: wide_array_to_os_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: &PROCESSENTRY32W) -> Option<PathBuf> {
|
||||||
|
let process_handle_result = unsafe {
|
||||||
|
OpenProcess(
|
||||||
|
PROCESS_QUERY_LIMITED_INFORMATION,
|
||||||
|
false,
|
||||||
|
process_entry.th32ProcessID,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
process_handle_result.map_or(None, |process_handle| {
|
||||||
|
let mut exe_name = [0u16; 260];
|
||||||
|
let mut dw_size = exe_name.len() as u32;
|
||||||
|
let image_name_result = unsafe {
|
||||||
|
QueryFullProcessImageNameW(
|
||||||
|
process_handle,
|
||||||
|
PROCESS_NAME_WIN32,
|
||||||
|
PWSTR(exe_name.as_mut_ptr()),
|
||||||
|
&raw mut dw_size,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
match image_name_result {
|
||||||
|
Ok(()) => Some(PathBuf::from(wide_array_to_os_string(&exe_name))),
|
||||||
|
Err(_) => None,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wide_array_to_os_string(wide: &[u16]) -> OsString {
|
||||||
|
let null_pos = wide.iter().position(|&x| x == 0).unwrap_or(wide.len());
|
||||||
|
OsString::from_wide(&wide[..null_pos])
|
||||||
|
}
|
||||||
+18
-22
@@ -25,25 +25,21 @@ pub fn is_cursor_visible() -> bool {
|
|||||||
cbSize: u32::try_from(std::mem::size_of::<CURSORINFO>()).unwrap(),
|
cbSize: u32::try_from(std::mem::size_of::<CURSORINFO>()).unwrap(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
unsafe {
|
unsafe { GetCursorInfo(&raw mut ci) }.unwrap();
|
||||||
GetCursorInfo(&mut ci).unwrap();
|
|
||||||
}
|
|
||||||
ci.flags == CURSOR_SHOWING
|
ci.flags == CURSOR_SHOWING
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_window_focused(target_title: &str) -> bool {
|
pub fn is_window_focused(target_title: &str) -> bool {
|
||||||
let mut buffer = [0; 512];
|
let mut buffer = [0; 512];
|
||||||
unsafe {
|
let hwnd = unsafe { GetForegroundWindow() };
|
||||||
let hwnd = GetForegroundWindow();
|
let length = unsafe { GetWindowTextW(hwnd, &mut buffer) };
|
||||||
let length = GetWindowTextW(hwnd, &mut buffer);
|
let current_title = String::from_utf16_lossy(&buffer[..length as usize]);
|
||||||
let current_title = String::from_utf16_lossy(&buffer[..length as usize]);
|
current_title == target_title
|
||||||
current_title == target_title
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_any_key_pressed(keys: &[VIRTUAL_KEY]) -> bool {
|
pub fn is_any_key_pressed(keys: &[VIRTUAL_KEY]) -> bool {
|
||||||
keys.iter()
|
keys.iter()
|
||||||
.any(|&key| unsafe { (GetAsyncKeyState(i32::from(key.0)) & i16::MIN) != 0 })
|
.any(|&key| unsafe { GetAsyncKeyState(i32::from(key.0)) } & i16::MIN != 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn elevate(closing: ElevationExitMethod) {
|
pub fn elevate(closing: ElevationExitMethod) {
|
||||||
@@ -66,22 +62,22 @@ 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 {
|
if unsafe { OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &raw mut token) }.is_err() {
|
||||||
if OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut token).is_err() {
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
let mut elevation = TOKEN_ELEVATION::default();
|
||||||
let mut elevation = TOKEN_ELEVATION::default();
|
let mut size = u32::try_from(std::mem::size_of::<TOKEN_ELEVATION>()).unwrap();
|
||||||
let mut size = u32::try_from(std::mem::size_of::<TOKEN_ELEVATION>()).unwrap();
|
let result = unsafe {
|
||||||
let result = GetTokenInformation(
|
GetTokenInformation(
|
||||||
token,
|
token,
|
||||||
TokenElevation,
|
TokenElevation,
|
||||||
Some((&raw mut elevation).cast()),
|
Some((&raw mut elevation).cast()),
|
||||||
size,
|
size,
|
||||||
&mut size,
|
&raw mut size,
|
||||||
);
|
)
|
||||||
CloseHandle(token).unwrap();
|
};
|
||||||
result.is_ok() && elevation.TokenIsElevated != 0
|
unsafe { CloseHandle(token) }.unwrap();
|
||||||
}
|
result.is_ok() && elevation.TokenIsElevated != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_system_theme_dark() -> bool {
|
pub fn is_system_theme_dark() -> bool {
|
||||||
|
|||||||
Reference in New Issue
Block a user