Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8943e61f3a
|
||
|
|
ed0a828fd4
|
||
|
|
96ef494443
|
||
|
|
9b5340a6c5
|
||
|
|
74132ec322
|
||
|
|
a58606c91d
|
||
|
|
47adf42a48
|
||
|
|
0fb587027c
|
||
|
|
0424b7b114
|
||
|
|
e89c3acc06
|
||
|
|
ab64c29935
|
||
|
|
e216445c7f
|
||
|
|
aeb99992bb
|
||
|
|
8ab6e14560
|
||
|
|
b9ebdfdb5c
|
||
|
|
2804812691
|
||
|
|
606fb7e191
|
||
|
|
e2a779b7f5
|
||
|
|
a8b04bb72b
|
||
|
|
c34cf187d5
|
||
|
|
f07421e75a
|
||
|
|
8b4a455dc1
|
||
|
|
a1300f8d76
|
||
|
|
9e623219a5
|
||
|
|
09e19b6e16
|
||
|
|
4498c45965
|
||
|
|
af8a5b1746
|
||
|
|
560d9659f4
|
||
|
|
bf086d0442
|
||
|
|
a6ea8ad8e2
|
||
|
|
4c00d1846b
|
||
|
|
e5e335f4f3
|
||
|
|
3ab5987b67
|
||
|
|
2030796337
|
||
|
|
2edb4f492a
|
||
|
|
114292d912
|
||
|
|
8adc1f49ad
|
Generated
+122
-123
@@ -63,21 +63,6 @@ version = "0.2.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04"
|
checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "android-tzdata"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "android_system_properties"
|
|
||||||
version = "0.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arboard"
|
name = "arboard"
|
||||||
version = "3.6.1"
|
version = "3.6.1"
|
||||||
@@ -261,18 +246,6 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "chrono"
|
|
||||||
version = "0.4.41"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
|
|
||||||
dependencies = [
|
|
||||||
"android-tzdata",
|
|
||||||
"iana-time-zone",
|
|
||||||
"num-traits",
|
|
||||||
"windows-link 0.1.3",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clipboard-win"
|
name = "clipboard-win"
|
||||||
version = "5.4.1"
|
version = "5.4.1"
|
||||||
@@ -563,6 +536,20 @@ dependencies = [
|
|||||||
"bytemuck",
|
"bytemuck",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "embed-resource"
|
||||||
|
version = "3.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "55a075fc573c64510038d7ee9abc7990635863992f83ebc52c8b433b8411a02e"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"memchr",
|
||||||
|
"rustc_version",
|
||||||
|
"toml",
|
||||||
|
"vswhom",
|
||||||
|
"winreg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "enum-map"
|
name = "enum-map"
|
||||||
version = "2.7.3"
|
version = "2.7.3"
|
||||||
@@ -825,21 +812,22 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gta-tools"
|
name = "gta-tools"
|
||||||
version = "0.9.0"
|
version = "0.11.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"catppuccin-egui",
|
"catppuccin-egui",
|
||||||
"chrono",
|
|
||||||
"eframe",
|
"eframe",
|
||||||
"egui_extras",
|
"egui_extras",
|
||||||
|
"humantime",
|
||||||
"image",
|
"image",
|
||||||
|
"log",
|
||||||
"open",
|
"open",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"static_vcruntime",
|
"static_vcruntime",
|
||||||
"strum",
|
"strum",
|
||||||
|
"tauri-winres",
|
||||||
"windows",
|
"windows",
|
||||||
"winreg",
|
"winreg",
|
||||||
"winresource",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -854,9 +842,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.15.5"
|
version = "0.16.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
@@ -871,28 +859,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
|
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iana-time-zone"
|
name = "humantime"
|
||||||
version = "0.1.63"
|
version = "2.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
|
checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424"
|
||||||
dependencies = [
|
|
||||||
"android_system_properties",
|
|
||||||
"core-foundation-sys",
|
|
||||||
"iana-time-zone-haiku",
|
|
||||||
"js-sys",
|
|
||||||
"log",
|
|
||||||
"wasm-bindgen",
|
|
||||||
"windows-core 0.61.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "iana-time-zone-haiku"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "icu_collections"
|
name = "icu_collections"
|
||||||
@@ -1023,9 +993,9 @@ checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.11.0"
|
version = "2.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9"
|
checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
@@ -1837,6 +1807,15 @@ version = "0.20.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
|
checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_version"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
||||||
|
dependencies = [
|
||||||
|
"semver",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.44"
|
version = "0.38.44"
|
||||||
@@ -1897,19 +1876,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "semver"
|
||||||
version = "1.0.219"
|
version = "1.0.27"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.228"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_core"
|
||||||
|
version = "1.0.228"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.219"
|
version = "1.0.228"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -1930,11 +1925,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_spanned"
|
name = "serde_spanned"
|
||||||
version = "0.6.9"
|
version = "1.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
|
checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2110,6 +2105,16 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tauri-winres"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd21509dd1fa9bd355dc29894a6ff10635880732396aa38c0066c1e6c1ab8074"
|
||||||
|
dependencies = [
|
||||||
|
"embed-resource",
|
||||||
|
"toml",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.69"
|
version = "1.0.69"
|
||||||
@@ -2182,14 +2187,17 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "0.8.23"
|
version = "0.9.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
|
checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"indexmap",
|
||||||
|
"serde_core",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
"toml_datetime",
|
"toml_datetime 0.7.3",
|
||||||
"toml_edit",
|
"toml_parser",
|
||||||
|
"toml_writer",
|
||||||
|
"winnow",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2197,8 +2205,14 @@ name = "toml_datetime"
|
|||||||
version = "0.6.11"
|
version = "0.6.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
|
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_datetime"
|
||||||
|
version = "0.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2208,18 +2222,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
|
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"serde",
|
"toml_datetime 0.6.11",
|
||||||
"serde_spanned",
|
|
||||||
"toml_datetime",
|
|
||||||
"toml_write",
|
|
||||||
"winnow",
|
"winnow",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_write"
|
name = "toml_parser"
|
||||||
version = "0.1.2"
|
version = "1.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
|
checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e"
|
||||||
|
dependencies = [
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_writer"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing"
|
name = "tracing"
|
||||||
@@ -2301,6 +2321,26 @@ version = "0.9.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vswhom"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"vswhom-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vswhom-sys"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "walkdir"
|
name = "walkdir"
|
||||||
version = "2.5.0"
|
version = "2.5.0"
|
||||||
@@ -2568,7 +2608,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "9579d0e6970fd5250aa29aba5994052385ff55cf7b28a059e484bb79ea842e42"
|
checksum = "9579d0e6970fd5250aa29aba5994052385ff55cf7b28a059e484bb79ea842e42"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-collections",
|
"windows-collections",
|
||||||
"windows-core 0.62.0",
|
"windows-core",
|
||||||
"windows-future",
|
"windows-future",
|
||||||
"windows-link 0.2.0",
|
"windows-link 0.2.0",
|
||||||
"windows-numerics",
|
"windows-numerics",
|
||||||
@@ -2580,20 +2620,7 @@ version = "0.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a90dd7a7b86859ec4cdf864658b311545ef19dbcf17a672b52ab7cefe80c336f"
|
checksum = "a90dd7a7b86859ec4cdf864658b311545ef19dbcf17a672b52ab7cefe80c336f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-core 0.62.0",
|
"windows-core",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-core"
|
|
||||||
version = "0.61.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
|
|
||||||
dependencies = [
|
|
||||||
"windows-implement",
|
|
||||||
"windows-interface",
|
|
||||||
"windows-link 0.1.3",
|
|
||||||
"windows-result 0.3.4",
|
|
||||||
"windows-strings 0.4.2",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2605,8 +2632,8 @@ dependencies = [
|
|||||||
"windows-implement",
|
"windows-implement",
|
||||||
"windows-interface",
|
"windows-interface",
|
||||||
"windows-link 0.2.0",
|
"windows-link 0.2.0",
|
||||||
"windows-result 0.4.0",
|
"windows-result",
|
||||||
"windows-strings 0.5.0",
|
"windows-strings",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2615,7 +2642,7 @@ version = "0.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b2194dee901458cb79e1148a4e9aac2b164cc95fa431891e7b296ff0b2f1d8a6"
|
checksum = "b2194dee901458cb79e1148a4e9aac2b164cc95fa431891e7b296ff0b2f1d8a6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-core 0.62.0",
|
"windows-core",
|
||||||
"windows-link 0.2.0",
|
"windows-link 0.2.0",
|
||||||
"windows-threading",
|
"windows-threading",
|
||||||
]
|
]
|
||||||
@@ -2660,19 +2687,10 @@ version = "0.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2ce3498fe0aba81e62e477408383196b4b0363db5e0c27646f932676283b43d8"
|
checksum = "2ce3498fe0aba81e62e477408383196b4b0363db5e0c27646f932676283b43d8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-core 0.62.0",
|
"windows-core",
|
||||||
"windows-link 0.2.0",
|
"windows-link 0.2.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-result"
|
|
||||||
version = "0.3.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
|
|
||||||
dependencies = [
|
|
||||||
"windows-link 0.1.3",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-result"
|
name = "windows-result"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
@@ -2682,15 +2700,6 @@ dependencies = [
|
|||||||
"windows-link 0.2.0",
|
"windows-link 0.2.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows-strings"
|
|
||||||
version = "0.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
|
|
||||||
dependencies = [
|
|
||||||
"windows-link 0.1.3",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-strings"
|
name = "windows-strings"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
@@ -2990,16 +2999,6 @@ dependencies = [
|
|||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winresource"
|
|
||||||
version = "0.1.23"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "edcacf11b6f48dd21b9ba002f991bdd5de29b2da8cc2800412f4b80f677e4957"
|
|
||||||
dependencies = [
|
|
||||||
"toml",
|
|
||||||
"version_check",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wit-bindgen"
|
name = "wit-bindgen"
|
||||||
version = "0.45.0"
|
version = "0.45.0"
|
||||||
|
|||||||
+7
-3
@@ -1,13 +1,15 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "gta-tools"
|
name = "gta-tools"
|
||||||
version = "0.9.0"
|
version = "0.11.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
license = "Apache-2.0"
|
||||||
|
authors = ["futile <git@futile.eu>"]
|
||||||
|
description = "A toolset of convenient things for GTA V Online."
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
catppuccin-egui = { version = "5.6.0", default-features = false, features = [
|
catppuccin-egui = { version = "5.6.0", default-features = false, features = [
|
||||||
"egui32",
|
"egui32",
|
||||||
] }
|
] }
|
||||||
chrono = { version = "0.4.41", default-features = false, features = ["clock"] }
|
|
||||||
eframe = { version = "0.32.2", default-features = false, features = [
|
eframe = { version = "0.32.2", default-features = false, features = [
|
||||||
"glow",
|
"glow",
|
||||||
"default_fonts",
|
"default_fonts",
|
||||||
@@ -16,7 +18,9 @@ egui_extras = { version = "0.32.2", default-features = false, features = [
|
|||||||
"image",
|
"image",
|
||||||
"svg",
|
"svg",
|
||||||
] }
|
] }
|
||||||
|
humantime = "2.3.0"
|
||||||
image = { version = "0.25.8", default-features = false, features = ["png"] }
|
image = { version = "0.25.8", default-features = false, features = ["png"] }
|
||||||
|
log = "0.4.28"
|
||||||
open = "5.3.2"
|
open = "5.3.2"
|
||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
serde_json = "1.0.143"
|
serde_json = "1.0.143"
|
||||||
@@ -35,4 +39,4 @@ winreg = "0.55.0"
|
|||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
static_vcruntime = "2.0.0"
|
static_vcruntime = "2.0.0"
|
||||||
winresource = "0.1.23"
|
tauri-winres = "0.3.3"
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
A toolset of convenient things for GTA V Online.
|
A toolset of convenient things for GTA V Online.
|
||||||
|
|
||||||
<picture>
|
<picture>
|
||||||
<source srcset="https://i.vgy.me/M4sOHh.png" media="(prefers-color-scheme: dark)">
|
<source srcset="https://i.vgy.me/xt8EfK.png" media="(prefers-color-scheme: dark)">
|
||||||
<img src="https://i.vgy.me/mpO9uc.png">
|
<img src="https://i.vgy.me/lYlZnd.png">
|
||||||
</picture>
|
</picture>
|
||||||
|
|
||||||
## Installing
|
## Installing
|
||||||
@@ -13,14 +13,14 @@ A toolset of convenient things for GTA V Online.
|
|||||||
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.
|
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 Rust, which you can get [here](https://rustup.rs). Then, 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**.
|
||||||
|
|
||||||
|
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 this repository by going to the <kbd>About</kbd> page of GTA Tools and clicking the GitHub button beside the version number.
|
||||||
|
|
||||||
## Guide
|
## Guide
|
||||||
|
|
||||||
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 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.
|
||||||
@@ -61,7 +61,7 @@ This feature is primarily useful for **replay glitching**, which is an exploit t
|
|||||||
|
|
||||||
## Issues
|
## 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.
|
- It is possible that when not elevated (administrator), the <kbd>Force close game</kbd> and <kbd>Empty current session</kbd> features can fail due to being denied access to the game. This is not guaranteed to happen. If this does happen to you, I recommend always using GTA Tools in elevated mode. For convenience, you may check "Always start elevated" in the Settings tab.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
### <ins>todo</ins>
|
||||||
|
|
||||||
|
- rewrite entire codebase
|
||||||
|
- add global hotkeys
|
||||||
|
|
||||||
|
### <ins>doing</ins>
|
||||||
|
|
||||||
|
- better error reporting
|
||||||
|
|
||||||
|
### <ins>done</ins>
|
||||||
|
|
||||||
|
- change `is_system_theme_dark` to `is_system_theme_light`
|
||||||
|
- fix weird changing colour on futile link in about
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,7 +1,23 @@
|
|||||||
fn main() {
|
fn main() {
|
||||||
static_vcruntime::metabuild();
|
static_vcruntime::metabuild();
|
||||||
winresource::WindowsResource::new()
|
tauri_winres::WindowsResource::new()
|
||||||
|
.set("FileDescription", "GTA Tools")
|
||||||
|
.set("ProductName", "GTA Tools")
|
||||||
|
.set("LegalCopyright", "futile <git@futile.eu>")
|
||||||
|
.set_language(0x0009)
|
||||||
.set_icon("assets/icon.ico")
|
.set_icon("assets/icon.ico")
|
||||||
.compile()
|
.compile()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
embed_latest_git_hash();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn embed_latest_git_hash() {
|
||||||
|
let git_rev_parse = std::process::Command::new("git")
|
||||||
|
.args(["rev-parse", "--short=8", "HEAD"])
|
||||||
|
.output()
|
||||||
|
.unwrap();
|
||||||
|
let git_hash = String::from_utf8(git_rev_parse.stdout).unwrap();
|
||||||
|
println!("cargo::rustc-env=LATEST_GIT_COMMIT_HASH={git_hash}");
|
||||||
|
println!("cargo::rerun-if-changed=.git/refs/heads/main");
|
||||||
|
println!("cargo::rerun-if-changed=.git/HEAD");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
set windows-shell := ["pwsh", "-NoLogo", "-NoProfileLoadTime", "-Command"]
|
|
||||||
|
|
||||||
install:
|
|
||||||
cargo build --release
|
|
||||||
cp .\target\release\gta-tools.exe ~\.cargo\bin
|
|
||||||
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
|
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
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,
|
system_info::SystemInfo,
|
||||||
};
|
};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use windows::Win32::{
|
use windows::Win32::{
|
||||||
Foundation::{CloseHandle, HANDLE, NTSTATUS},
|
Foundation::{HANDLE, NTSTATUS},
|
||||||
System::Threading::{OpenProcess, PROCESS_SUSPEND_RESUME},
|
System::Threading::{OpenProcess, PROCESS_SUSPEND_RESUME},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -58,25 +57,24 @@ fn get_gta_pid(system_info: &mut SystemInfo) -> Option<u32> {
|
|||||||
.map(|p| p.pid())
|
.map(|p| p.pid())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn activate(game_handle: &mut HANDLE, system_info: &mut SystemInfo) -> Result<(), ()> {
|
pub fn activate(game_handle: &mut HANDLE, system_info: &mut SystemInfo) -> bool {
|
||||||
let Some(pid) = get_gta_pid(system_info) else {
|
let Some(pid) = get_gta_pid(system_info) else {
|
||||||
return Err(());
|
return false;
|
||||||
};
|
};
|
||||||
match unsafe { OpenProcess(PROCESS_SUSPEND_RESUME, false, pid) } {
|
match unsafe { OpenProcess(PROCESS_SUSPEND_RESUME, false, pid) } {
|
||||||
Ok(handle) => *game_handle = handle,
|
Ok(handle) => *game_handle = handle,
|
||||||
Err(why) => {
|
Err(why) => {
|
||||||
let message = format!("failed to suspend game for empty session:\n{why}");
|
log::error!("failed to suspend game for empty session:\n{why}");
|
||||||
log::log(log::LogLevel::Error, &message);
|
return false;
|
||||||
return Err(());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let _ = unsafe { NtSuspendProcess(*game_handle) };
|
unsafe { NtSuspendProcess(*game_handle) }.unwrap();
|
||||||
Ok(())
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deactivate(game_handle: &mut HANDLE) {
|
pub fn deactivate(game_handle: &mut HANDLE) {
|
||||||
if !game_handle.is_invalid() {
|
if !game_handle.is_invalid() {
|
||||||
|
// ignoring the return because this function behaves very weirdly
|
||||||
let _ = unsafe { NtResumeProcess(*game_handle) };
|
let _ = unsafe { NtResumeProcess(*game_handle) };
|
||||||
let _ = unsafe { CloseHandle(*game_handle) };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use crate::util::{
|
use crate::util::{
|
||||||
consts::game::{EXE_ENHANCED, EXE_LEGACY},
|
consts::game::{EXE_ENHANCED, EXE_LEGACY},
|
||||||
log,
|
|
||||||
system_info::SystemInfo,
|
system_info::SystemInfo,
|
||||||
};
|
};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
@@ -62,10 +61,7 @@ fn activate(system_info: &mut SystemInfo) {
|
|||||||
.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| {
|
||||||
if !p.kill() {
|
if !p.kill() {
|
||||||
log::log(
|
log::error!("failed to force close game, probably due to access denied");
|
||||||
log::LogLevel::Error,
|
|
||||||
"failed to force close game, probably due to access denied",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
system_info.refresh();
|
system_info.refresh();
|
||||||
|
|||||||
+113
-46
@@ -1,9 +1,13 @@
|
|||||||
use crate::util::{
|
use crate::{
|
||||||
consts::game::{EXE_ENHANCED, EXE_LEGACY},
|
gui::settings::BlockMethod,
|
||||||
system_info::SystemInfo,
|
util::{
|
||||||
|
consts::game::{EXE_ENHANCED, EXE_LEGACY},
|
||||||
|
system_info::SystemInfo,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
path::Path,
|
error::Error,
|
||||||
|
path::{Path, PathBuf},
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
use strum::{Display, EnumIter};
|
use strum::{Display, EnumIter};
|
||||||
@@ -11,7 +15,7 @@ use windows::{
|
|||||||
Win32::{
|
Win32::{
|
||||||
NetworkManagement::WindowsFirewall::{
|
NetworkManagement::WindowsFirewall::{
|
||||||
INetFwPolicy2, INetFwRule, NET_FW_ACTION_BLOCK, NET_FW_IP_PROTOCOL_ANY,
|
INetFwPolicy2, INetFwRule, NET_FW_ACTION_BLOCK, NET_FW_IP_PROTOCOL_ANY,
|
||||||
NET_FW_RULE_DIR_IN, NET_FW_RULE_DIR_OUT, NetFwPolicy2, NetFwRule,
|
NET_FW_RULE_DIR_OUT, NetFwPolicy2, NetFwRule,
|
||||||
},
|
},
|
||||||
System::Com::{
|
System::Com::{
|
||||||
CLSCTX_INPROC_SERVER, COINIT_MULTITHREADED, CoCreateInstance, CoInitializeEx,
|
CLSCTX_INPROC_SERVER, COINIT_MULTITHREADED, CoCreateInstance, CoInitializeEx,
|
||||||
@@ -21,8 +25,8 @@ use windows::{
|
|||||||
core::BSTR,
|
core::BSTR,
|
||||||
};
|
};
|
||||||
|
|
||||||
const FILTER_NAME_IN: &str = "[GTA Tools] Block all inbound traffic for GTA V";
|
const FILTER_NAME_EXE: &str = "[GTA Tools] Block outbound traffic for all of GTA V";
|
||||||
const FILTER_NAME_OUT: &str = "[GTA Tools] Block all outbound traffic for GTA V";
|
const FILTER_NAME_SAVE_SERVER: &str = "[GTA Tools] Block outbound traffic to Rockstar save server";
|
||||||
|
|
||||||
const INTERVAL: Duration = Duration::from_secs(3);
|
const INTERVAL: Duration = Duration::from_secs(3);
|
||||||
|
|
||||||
@@ -54,7 +58,11 @@ pub struct GameNetworking {
|
|||||||
impl Default for GameNetworking {
|
impl Default for GameNetworking {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
blocked_status: Self::is_blocked().into(),
|
blocked_status: if Self::is_save_server_blocked().unwrap() {
|
||||||
|
Self::is_save_server_blocked().unwrap().into()
|
||||||
|
} else {
|
||||||
|
Self::is_exe_blocked().unwrap().into()
|
||||||
|
},
|
||||||
com_initialized: unsafe { CoInitializeEx(None, COINIT_MULTITHREADED) }.is_ok(),
|
com_initialized: unsafe { CoInitializeEx(None, COINIT_MULTITHREADED) }.is_ok(),
|
||||||
timer: Instant::now(),
|
timer: Instant::now(),
|
||||||
counting: false,
|
counting: false,
|
||||||
@@ -70,53 +78,93 @@ impl Drop for GameNetworking {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum Mode {
|
||||||
|
EntireGame(PathBuf),
|
||||||
|
SaveServer(String),
|
||||||
|
}
|
||||||
|
|
||||||
impl GameNetworking {
|
impl GameNetworking {
|
||||||
pub fn block_all(&mut self, system_info: &mut SystemInfo) {
|
fn block_generic(&self, mode: Mode) -> Result<(), Box<dyn Error>> {
|
||||||
|
let policy: INetFwPolicy2 =
|
||||||
|
unsafe { CoCreateInstance(&NetFwPolicy2, None, CLSCTX_INPROC_SERVER) }?;
|
||||||
|
let rules = unsafe { policy.Rules() }?;
|
||||||
|
let filter_name = match mode {
|
||||||
|
Mode::EntireGame(_) => FILTER_NAME_EXE,
|
||||||
|
Mode::SaveServer(_) => FILTER_NAME_SAVE_SERVER,
|
||||||
|
};
|
||||||
|
unsafe { rules.Remove(&BSTR::from(filter_name)) }?;
|
||||||
|
let rule: INetFwRule = unsafe { CoCreateInstance(&NetFwRule, None, CLSCTX_INPROC_SERVER) }?;
|
||||||
|
unsafe { rule.SetName(&BSTR::from(filter_name)) }?;
|
||||||
|
match mode {
|
||||||
|
Mode::EntireGame(exe_path) => {
|
||||||
|
let exe_path = BSTR::from(exe_path.to_string_lossy().to_string());
|
||||||
|
unsafe { rule.SetApplicationName(&exe_path) }?;
|
||||||
|
}
|
||||||
|
Mode::SaveServer(save_server_ip) => {
|
||||||
|
unsafe { rule.SetRemoteAddresses(&BSTR::from(save_server_ip)) }?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsafe { rule.SetDirection(NET_FW_RULE_DIR_OUT) }?;
|
||||||
|
unsafe { rule.SetEnabled(true.into()) }?;
|
||||||
|
unsafe { rule.SetAction(NET_FW_ACTION_BLOCK) }?;
|
||||||
|
unsafe { rule.SetProtocol(NET_FW_IP_PROTOCOL_ANY.0) }?;
|
||||||
|
unsafe { rules.Add(&rule) }?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unblock_generic(&self, filter_name: &str) -> Result<(), Box<dyn Error>> {
|
||||||
|
let policy: INetFwPolicy2 =
|
||||||
|
unsafe { CoCreateInstance(&NetFwPolicy2, None, CLSCTX_INPROC_SERVER) }?;
|
||||||
|
let rules = unsafe { policy.Rules() }?;
|
||||||
|
unsafe { rules.Remove(&BSTR::from(filter_name)) }?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_blocked_generic(filter_name: &str) -> Result<bool, Box<dyn Error>> {
|
||||||
|
let policy: INetFwPolicy2 =
|
||||||
|
unsafe { CoCreateInstance(&NetFwPolicy2, None, CLSCTX_INPROC_SERVER) }?;
|
||||||
|
let rules = unsafe { policy.Rules() }?;
|
||||||
|
let rule_exists = unsafe { rules.Item(&BSTR::from(filter_name)) }.is_ok();
|
||||||
|
Ok(rule_exists)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn block_exe(&mut self, system_info: &mut SystemInfo) -> Result<(), Box<dyn Error>> {
|
||||||
let Some(exe_path) = get_game_exe_path(system_info) else {
|
let Some(exe_path) = get_game_exe_path(system_info) else {
|
||||||
self.blocked_status = BlockedStatus::Failed;
|
self.blocked_status = BlockedStatus::Failed;
|
||||||
return;
|
return Ok(());
|
||||||
};
|
};
|
||||||
let policy: INetFwPolicy2 =
|
self.block_generic(Mode::EntireGame(exe_path.to_path_buf()))?;
|
||||||
unsafe { CoCreateInstance(&NetFwPolicy2, None, CLSCTX_INPROC_SERVER) }.unwrap();
|
self.blocked_status = Self::is_exe_blocked()?.into();
|
||||||
let rules = unsafe { policy.Rules() }.unwrap();
|
Ok(())
|
||||||
let exe_path = BSTR::from(exe_path.to_string_lossy().to_string());
|
|
||||||
for filter in [
|
|
||||||
(FILTER_NAME_IN, NET_FW_RULE_DIR_IN),
|
|
||||||
(FILTER_NAME_OUT, NET_FW_RULE_DIR_OUT),
|
|
||||||
] {
|
|
||||||
let _ = unsafe { rules.Remove(&BSTR::from(filter.0)) };
|
|
||||||
let rule: INetFwRule =
|
|
||||||
unsafe { CoCreateInstance(&NetFwRule, None, CLSCTX_INPROC_SERVER) }.unwrap();
|
|
||||||
unsafe { rule.SetName(&BSTR::from(filter.0)) }.unwrap();
|
|
||||||
unsafe { rule.SetApplicationName(&exe_path) }.unwrap();
|
|
||||||
unsafe { rule.SetDirection(filter.1) }.unwrap();
|
|
||||||
unsafe { rule.SetEnabled(true.into()) }.unwrap();
|
|
||||||
unsafe { rule.SetAction(NET_FW_ACTION_BLOCK) }.unwrap();
|
|
||||||
unsafe { rule.SetProtocol(NET_FW_IP_PROTOCOL_ANY.0) }.unwrap();
|
|
||||||
unsafe { rules.Add(&rule) }.unwrap();
|
|
||||||
}
|
|
||||||
self.blocked_status = Self::is_blocked().into();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unblock_all(&mut self) {
|
pub fn unblock_exe(&mut self) -> Result<(), Box<dyn Error>> {
|
||||||
let policy: INetFwPolicy2 =
|
self.unblock_generic(FILTER_NAME_EXE)?;
|
||||||
unsafe { CoCreateInstance(&NetFwPolicy2, None, CLSCTX_INPROC_SERVER) }.unwrap();
|
self.blocked_status = Self::is_exe_blocked()?.into();
|
||||||
let rules = unsafe { policy.Rules() }.unwrap();
|
Ok(())
|
||||||
unsafe { rules.Remove(&BSTR::from(FILTER_NAME_IN)) }.unwrap();
|
|
||||||
unsafe { rules.Remove(&BSTR::from(FILTER_NAME_OUT)) }.unwrap();
|
|
||||||
self.blocked_status = Self::is_blocked().into();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_blocked() -> bool {
|
fn is_exe_blocked() -> Result<bool, Box<dyn Error>> {
|
||||||
let policy: INetFwPolicy2 =
|
Self::is_blocked_generic(FILTER_NAME_EXE)
|
||||||
unsafe { CoCreateInstance(&NetFwPolicy2, None, CLSCTX_INPROC_SERVER) }.unwrap();
|
|
||||||
let rules = unsafe { policy.Rules() }.unwrap();
|
|
||||||
let in_rule_exists = unsafe { rules.Item(&BSTR::from(FILTER_NAME_IN)) }.is_ok();
|
|
||||||
let out_rule_exists = unsafe { rules.Item(&BSTR::from(FILTER_NAME_OUT)) }.is_ok();
|
|
||||||
in_rule_exists || out_rule_exists
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn if_failed_return_to_boolean(&mut self) {
|
pub fn block_save_server(&mut self, save_server_ip: &str) -> Result<(), Box<dyn Error>> {
|
||||||
|
self.block_generic(Mode::SaveServer(save_server_ip.to_owned()))?;
|
||||||
|
self.blocked_status = Self::is_save_server_blocked()?.into();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unblock_save_server(&mut self) -> Result<(), Box<dyn Error>> {
|
||||||
|
self.unblock_generic(FILTER_NAME_SAVE_SERVER)?;
|
||||||
|
self.blocked_status = Self::is_save_server_blocked()?.into();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_save_server_blocked() -> Result<bool, Box<dyn Error>> {
|
||||||
|
Self::is_blocked_generic(FILTER_NAME_SAVE_SERVER)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset_indicator_if_failed(&mut self) {
|
||||||
if self.blocked_status == BlockedStatus::Failed && !self.counting {
|
if self.blocked_status == BlockedStatus::Failed && !self.counting {
|
||||||
self.counting = true;
|
self.counting = true;
|
||||||
self.timer = Instant::now();
|
self.timer = Instant::now();
|
||||||
@@ -126,7 +174,26 @@ impl GameNetworking {
|
|||||||
&& self.timer.elapsed() >= INTERVAL
|
&& self.timer.elapsed() >= INTERVAL
|
||||||
{
|
{
|
||||||
self.counting = false;
|
self.counting = false;
|
||||||
self.blocked_status = Self::is_blocked().into();
|
self.blocked_status = Self::is_exe_blocked().unwrap().into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ensure_not_both_blocked_simultaneously(&mut self, block_method: BlockMethod) {
|
||||||
|
match block_method {
|
||||||
|
BlockMethod::EntireGame => {
|
||||||
|
if Self::is_save_server_blocked().unwrap() {
|
||||||
|
// ignoring the return because if this is an error the user can just thug it out at that point
|
||||||
|
let _ = self.unblock_save_server();
|
||||||
|
self.blocked_status = Self::is_exe_blocked().unwrap().into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BlockMethod::SaveServer => {
|
||||||
|
if Self::is_exe_blocked().unwrap() {
|
||||||
|
// ignoring the return because if this is an error the user can just thug it out at that point
|
||||||
|
let _ = self.unblock_exe();
|
||||||
|
self.blocked_status = Self::is_save_server_blocked().unwrap().into();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ pub fn launch(platform: &Platform, version: &LaunchVersion) {
|
|||||||
LaunchVersion::Enhanced => "steam://run/3240220",
|
LaunchVersion::Enhanced => "steam://run/3240220",
|
||||||
LaunchVersion::Legacy => "steam://run/271590",
|
LaunchVersion::Legacy => "steam://run/271590",
|
||||||
};
|
};
|
||||||
let _ = open::that_detached(steam_url);
|
open::that_detached(steam_url).unwrap();
|
||||||
}
|
}
|
||||||
Platform::Rockstar => {
|
Platform::Rockstar => {
|
||||||
let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
|
let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
|
||||||
@@ -44,6 +44,7 @@ pub fn launch(platform: &Platform, version: &LaunchVersion) {
|
|||||||
};
|
};
|
||||||
let mut play_gtav_path = PathBuf::from(install_folder);
|
let mut play_gtav_path = PathBuf::from(install_folder);
|
||||||
play_gtav_path.push("PlayGTAV.exe");
|
play_gtav_path.push("PlayGTAV.exe");
|
||||||
|
// ignoring the return because if it errors that means GTA isn't installed via Rockstar
|
||||||
let _ = Command::new(play_gtav_path).spawn();
|
let _ = Command::new(play_gtav_path).spawn();
|
||||||
}
|
}
|
||||||
Platform::Epic => {
|
Platform::Epic => {
|
||||||
@@ -55,7 +56,7 @@ pub fn launch(platform: &Platform, version: &LaunchVersion) {
|
|||||||
"com.epicgames.launcher://apps/9d2d0eb64d5c44529cece33fe2a46482?action=launch&silent=true"
|
"com.epicgames.launcher://apps/9d2d0eb64d5c44529cece33fe2a46482?action=launch&silent=true"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let _ = open::that_detached(epic_url);
|
open::that_detached(epic_url).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -1,5 +1,4 @@
|
|||||||
mod app;
|
pub mod app;
|
||||||
mod colours;
|
|
||||||
mod debug;
|
mod debug;
|
||||||
pub mod run;
|
pub mod run;
|
||||||
pub mod settings;
|
pub mod settings;
|
||||||
|
|||||||
+110
-49
@@ -1,8 +1,15 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
features,
|
features,
|
||||||
gui::{colours, settings::Settings, tools, ui_ext::UiExt},
|
gui::{
|
||||||
|
settings::{BlockMethod, ROCKSTAR_SAVE_SERVER, Settings},
|
||||||
|
tools,
|
||||||
|
ui_ext::UiExt,
|
||||||
|
},
|
||||||
util::{
|
util::{
|
||||||
consts::game::WINDOW_TITLE, persistent_state::PersistentState, system_info::SystemInfo, win,
|
consts::{colours, game::WINDOW_TITLE},
|
||||||
|
persistent_state::PersistentState,
|
||||||
|
system_info::SystemInfo,
|
||||||
|
win,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use eframe::egui;
|
use eframe::egui;
|
||||||
@@ -106,7 +113,7 @@ impl App {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
let force_close_button = ui.add_sized(
|
let force_close_button = ui.add_sized(
|
||||||
[104.0, 0.0],
|
[108.0, 0.0],
|
||||||
egui::Button::new(&self.force_close.button_text),
|
egui::Button::new(&self.force_close.button_text),
|
||||||
);
|
);
|
||||||
self.force_close
|
self.force_close
|
||||||
@@ -122,7 +129,6 @@ impl App {
|
|||||||
&mut self.game_handle,
|
&mut self.game_handle,
|
||||||
&mut self.system_info,
|
&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;
|
||||||
@@ -154,15 +160,15 @@ impl App {
|
|||||||
egui::Frame::new()
|
egui::Frame::new()
|
||||||
.outer_margin(egui::vec2(0.0, -2.0))
|
.outer_margin(egui::vec2(0.0, -2.0))
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
let response = ui.add_enabled_ui(self.flags.elevated, |ui| {
|
ui.add_enabled_ui(self.flags.elevated, |ui| {
|
||||||
let label = ui.horizontal(|ui| {
|
let label = ui.horizontal(|ui| {
|
||||||
let label = ui.label("Game's network access");
|
let label = match self.settings.block_method {
|
||||||
|
BlockMethod::EntireGame => ui.label("Game's network access"),
|
||||||
|
BlockMethod::SaveServer => ui.label("Rockstar save server access"),
|
||||||
|
};
|
||||||
ui.add_space(1.0);
|
ui.add_space(1.0);
|
||||||
ui.create_indicator_dot(self.game_networking.blocked_status)
|
ui.create_indicator_dot(self.game_networking.blocked_status);
|
||||||
.on_hover_text(
|
self.game_networking.reset_indicator_if_failed();
|
||||||
"This turns yellow if GTA Tools\ncannot find your game.",
|
|
||||||
);
|
|
||||||
self.game_networking.if_failed_return_to_boolean();
|
|
||||||
label
|
label
|
||||||
});
|
});
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
@@ -173,19 +179,36 @@ 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.system_info);
|
match self.settings.block_method {
|
||||||
|
BlockMethod::EntireGame => {
|
||||||
|
self.game_networking
|
||||||
|
.block_exe(&mut self.system_info)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
BlockMethod::SaveServer => {
|
||||||
|
self.game_networking
|
||||||
|
.block_save_server(&self.settings.save_server_ip)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ui
|
if ui
|
||||||
.add_sized([button_width, 18.0], egui::Button::new("Unblock"))
|
.add_sized([button_width, 18.0], egui::Button::new("Unblock"))
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
{
|
||||||
self.game_networking.unblock_all();
|
match self.settings.block_method {
|
||||||
|
BlockMethod::EntireGame => {
|
||||||
|
self.game_networking.unblock_exe().unwrap();
|
||||||
|
}
|
||||||
|
BlockMethod::SaveServer => {
|
||||||
|
self.game_networking.unblock_save_server().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
})
|
||||||
response.response.on_disabled_hover_text(
|
.response
|
||||||
"This requires administrator.\nUse the Elevate button.",
|
.on_disabled_hover_text("This requires administrator.\nUse the Elevate button.");
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,27 +219,63 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn show_settings_stage(&mut self, ctx: &egui::Context, ui: &mut egui::Ui) {
|
fn show_settings_stage(&mut self, ctx: &egui::Context, ui: &mut egui::Ui) {
|
||||||
ui.horizontal(|ui| {
|
ui.collapsing("General", |ui| {
|
||||||
ui.label("Launch version");
|
ui.checkbox(&mut self.settings.start_elevated, "Always start elevated");
|
||||||
egui::ComboBox::from_id_salt("Launch version")
|
ui.horizontal(|ui| {
|
||||||
.selected_text(self.settings.launch_version.to_string())
|
let selection = self.settings.theme;
|
||||||
.show_ui(ui, |ui| {
|
egui::ComboBox::from_id_salt("Theme")
|
||||||
ui.build_menu(&mut self.settings.launch_version);
|
.selected_text(self.settings.theme.to_string())
|
||||||
});
|
.show_ui(ui, |ui| {
|
||||||
|
ui.build_menu(&mut self.settings.theme);
|
||||||
|
});
|
||||||
|
if selection != self.settings.theme {
|
||||||
|
catppuccin_egui::set_theme(ctx, self.settings.theme.into());
|
||||||
|
}
|
||||||
|
ui.label("Theme");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
ui.horizontal(|ui| {
|
ui.collapsing("Game", |ui| {
|
||||||
let selection = self.settings.theme;
|
ui.horizontal(|ui| {
|
||||||
ui.label("Theme");
|
egui::ComboBox::from_id_salt("Launch version")
|
||||||
egui::ComboBox::from_id_salt("Theme")
|
.selected_text(self.settings.launch_version.to_string())
|
||||||
.selected_text(self.settings.theme.to_string())
|
.show_ui(ui, |ui| {
|
||||||
.show_ui(ui, |ui| {
|
ui.build_menu(&mut self.settings.launch_version);
|
||||||
ui.build_menu(&mut self.settings.theme);
|
});
|
||||||
|
ui.label("Launch version");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
ui.collapsing("Network", |ui| {
|
||||||
|
ui.add_enabled_ui(self.flags.elevated, |ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
egui::ComboBox::from_id_salt("Block method")
|
||||||
|
.selected_text(self.settings.block_method.to_string())
|
||||||
|
.show_ui(ui, |ui| {
|
||||||
|
ui.build_menu(&mut self.settings.block_method);
|
||||||
|
});
|
||||||
|
ui.label("Block method");
|
||||||
|
self.game_networking
|
||||||
|
.ensure_not_both_blocked_simultaneously(self.settings.block_method);
|
||||||
});
|
});
|
||||||
if selection != self.settings.theme {
|
ui.horizontal(|ui| {
|
||||||
catppuccin_egui::set_theme(ctx, self.settings.theme.into());
|
ui.add_enabled_ui(
|
||||||
}
|
self.settings.block_method == BlockMethod::SaveServer,
|
||||||
|
|ui| {
|
||||||
|
ui.add(
|
||||||
|
egui::TextEdit::singleline(&mut self.settings.save_server_ip)
|
||||||
|
.char_limit(15)
|
||||||
|
.desired_width(92.0),
|
||||||
|
);
|
||||||
|
ui.label("Save server IP");
|
||||||
|
if ui.button("↺").clicked() {
|
||||||
|
self.settings.save_server_ip = String::from(ROCKSTAR_SAVE_SERVER);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.response
|
||||||
|
.on_disabled_hover_text("This requires administrator.\nUse the Elevate button.");
|
||||||
});
|
});
|
||||||
ui.checkbox(&mut self.settings.start_elevated, "Always start elevated");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show_about_stage(&self, _ctx: &egui::Context, ui: &mut egui::Ui) {
|
fn show_about_stage(&self, _ctx: &egui::Context, ui: &mut egui::Ui) {
|
||||||
@@ -226,27 +285,28 @@ impl App {
|
|||||||
ui.spacing_mut().item_spacing.x = 0.0;
|
ui.spacing_mut().item_spacing.x = 0.0;
|
||||||
ui.label("with ");
|
ui.label("with ");
|
||||||
ui.scope(|ui| {
|
ui.scope(|ui| {
|
||||||
ui.style_mut().visuals.hyperlink_color = colours::RED;
|
ui.style_mut().visuals.override_text_color = Some(colours::RED);
|
||||||
ui.hyperlink_to("❤", "https://github.com/elituf/gta-tools");
|
ui.label("❤");
|
||||||
});
|
});
|
||||||
ui.label(" from ");
|
ui.label(" from ");
|
||||||
ui.hyperlink_to("futile", "https://futile.eu");
|
ui.scope(|ui| {
|
||||||
|
ui.style_mut().visuals.hyperlink_color =
|
||||||
|
catppuccin_egui::Theme::from(self.settings.theme).text;
|
||||||
|
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.style_mut().spacing.button_padding = egui::Vec2::new(4.0, 0.0);
|
||||||
|
if ui.button("").on_hover_text("View source code").clicked() {
|
||||||
|
open::that("https://github.com/elituf/gta-tools").unwrap();
|
||||||
|
}
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
ui.label("(dev)");
|
ui.label("(dev)");
|
||||||
}
|
}
|
||||||
ui.label(format!("v{}", env!("CARGO_PKG_VERSION")));
|
ui.label(format!("v{}", env!("CARGO_PKG_VERSION")))
|
||||||
ui.scope(|ui| {
|
.on_hover_text(
|
||||||
ui.style_mut().spacing.button_padding = egui::Vec2::new(3.0, 0.0);
|
egui::RichText::new(env!("LATEST_GIT_COMMIT_HASH")).monospace(),
|
||||||
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!(
|
||||||
@@ -261,6 +321,7 @@ impl Drop for App {
|
|||||||
// save any persistent state to config file
|
// save any persistent state to config file
|
||||||
PersistentState {
|
PersistentState {
|
||||||
launcher: self.launch.selected,
|
launcher: self.launch.selected,
|
||||||
|
anti_afk_enabled: self.anti_afk.enabled,
|
||||||
settings: self.settings.clone(),
|
settings: self.settings.clone(),
|
||||||
}
|
}
|
||||||
.set();
|
.set();
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
use crate::features::game_networking::BlockedStatus;
|
|
||||||
use eframe::egui;
|
|
||||||
|
|
||||||
pub const RED: egui::Color32 = egui::Color32::from_rgb(255, 96, 96);
|
|
||||||
pub const YELLOW: egui::Color32 = egui::Color32::from_rgb(255, 255, 96);
|
|
||||||
pub const GREEN: egui::Color32 = egui::Color32::from_rgb(96, 255, 96);
|
|
||||||
|
|
||||||
impl From<BlockedStatus> for egui::Color32 {
|
|
||||||
fn from(value: BlockedStatus) -> Self {
|
|
||||||
match value {
|
|
||||||
BlockedStatus::Blocked => RED,
|
|
||||||
BlockedStatus::Failed => YELLOW,
|
|
||||||
BlockedStatus::Unblocked => GREEN,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+14
-6
@@ -14,8 +14,7 @@ fn app_creator(
|
|||||||
let mut app = Box::new(App::default());
|
let mut app = Box::new(App::default());
|
||||||
// load previously selected launch platform & settings from persistent state
|
// load previously selected launch platform & settings from persistent state
|
||||||
if let Some(persistent_state) = PersistentState::get() {
|
if let Some(persistent_state) = PersistentState::get() {
|
||||||
app.launch.selected = persistent_state.launcher;
|
persistent_state.apply_to(&mut app);
|
||||||
app.settings = persistent_state.settings;
|
|
||||||
}
|
}
|
||||||
// check if we're elevated. if not, and the user wants an elevated launch - relaunch elevated
|
// check if we're elevated. if not, and the user wants an elevated launch - relaunch elevated
|
||||||
if !app.flags.elevated && app.settings.start_elevated {
|
if !app.flags.elevated && app.settings.start_elevated {
|
||||||
@@ -38,12 +37,21 @@ fn app_creator(
|
|||||||
// 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();
|
||||||
fonts.font_data.insert(
|
fonts.font_data.insert(
|
||||||
"Ubuntu-Regular".to_owned(),
|
"Inter 18pt Light".to_owned(),
|
||||||
egui::FontData::from_static(include_bytes!("../../assets/Ubuntu-Regular.ttf")).into(),
|
egui::FontData::from_static(include_bytes!("../../assets/Inter_18pt-Light.ttf")).into(),
|
||||||
|
);
|
||||||
|
fonts
|
||||||
|
.families
|
||||||
|
.get_mut(&egui::FontFamily::Proportional)
|
||||||
|
.unwrap()
|
||||||
|
.insert(0, "Inter 18pt Light".to_owned());
|
||||||
|
fonts.font_data.insert(
|
||||||
|
"Inter 18pt Regular".to_owned(),
|
||||||
|
egui::FontData::from_static(include_bytes!("../../assets/Inter_18pt-Regular.ttf")).into(),
|
||||||
);
|
);
|
||||||
fonts.families.insert(
|
fonts.families.insert(
|
||||||
egui::FontFamily::Name("Ubuntu-Regular".into()),
|
egui::FontFamily::Name("Inter 18pt Regular".into()),
|
||||||
vec!["Ubuntu-Regular".to_owned()],
|
vec!["Inter 18pt Regular".to_owned()],
|
||||||
);
|
);
|
||||||
cc.egui_ctx.set_fonts(fonts);
|
cc.egui_ctx.set_fonts(fonts);
|
||||||
// finally return the App
|
// finally return the App
|
||||||
|
|||||||
+57
-32
@@ -2,6 +2,41 @@ use crate::util::win;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use strum::{Display, EnumIter};
|
use strum::{Display, EnumIter};
|
||||||
|
|
||||||
|
pub const ROCKSTAR_SAVE_SERVER: &str = "192.81.241.171";
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, Display, PartialEq, Eq, Serialize, Deserialize, EnumIter)]
|
||||||
|
pub enum Theme {
|
||||||
|
#[default]
|
||||||
|
#[strum(to_string = "Auto")]
|
||||||
|
Auto,
|
||||||
|
#[strum(to_string = "Latte")]
|
||||||
|
Latte,
|
||||||
|
#[strum(to_string = "Frappe")]
|
||||||
|
Frappe,
|
||||||
|
#[strum(to_string = "Macchiato")]
|
||||||
|
Macchiato,
|
||||||
|
#[strum(to_string = "Mocha")]
|
||||||
|
Mocha,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Theme> for catppuccin_egui::Theme {
|
||||||
|
fn from(val: Theme) -> Self {
|
||||||
|
match val {
|
||||||
|
Theme::Auto => {
|
||||||
|
if win::is_system_theme_light() {
|
||||||
|
catppuccin_egui::LATTE
|
||||||
|
} else {
|
||||||
|
catppuccin_egui::MOCHA
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Theme::Latte => catppuccin_egui::LATTE,
|
||||||
|
Theme::Frappe => catppuccin_egui::FRAPPE,
|
||||||
|
Theme::Macchiato => catppuccin_egui::MACCHIATO,
|
||||||
|
Theme::Mocha => catppuccin_egui::MOCHA,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Display, PartialEq, Eq, Serialize, Deserialize, EnumIter)]
|
#[derive(Clone, Copy, Debug, Default, Display, PartialEq, Eq, Serialize, Deserialize, EnumIter)]
|
||||||
pub enum LaunchVersion {
|
pub enum LaunchVersion {
|
||||||
#[default]
|
#[default]
|
||||||
@@ -10,41 +45,31 @@ pub enum LaunchVersion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Display, PartialEq, Eq, Serialize, Deserialize, EnumIter)]
|
#[derive(Clone, Copy, Debug, Default, Display, PartialEq, Eq, Serialize, Deserialize, EnumIter)]
|
||||||
pub enum Theme {
|
pub enum BlockMethod {
|
||||||
#[default]
|
#[default]
|
||||||
#[strum(to_string = "Auto")]
|
#[strum(to_string = "Entire game")]
|
||||||
Auto,
|
EntireGame,
|
||||||
#[strum(to_string = "Catppuccin Latte")]
|
#[strum(to_string = "Save server")]
|
||||||
CatppuccinLatte,
|
SaveServer,
|
||||||
#[strum(to_string = "Catppuccin Frappe")]
|
|
||||||
CatppuccinFrappe,
|
|
||||||
#[strum(to_string = "Catppuccin Macchiato")]
|
|
||||||
CatppuccinMacchiato,
|
|
||||||
#[strum(to_string = "Catppuccin Mocha")]
|
|
||||||
CatppuccinMocha,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Theme> for catppuccin_egui::Theme {
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
fn from(val: Theme) -> Self {
|
pub struct Settings {
|
||||||
match val {
|
pub start_elevated: bool,
|
||||||
Theme::Auto => {
|
pub theme: Theme,
|
||||||
if win::is_system_theme_dark() {
|
pub launch_version: LaunchVersion,
|
||||||
catppuccin_egui::MOCHA
|
pub save_server_ip: String,
|
||||||
} else {
|
pub block_method: BlockMethod,
|
||||||
catppuccin_egui::LATTE
|
}
|
||||||
}
|
|
||||||
}
|
impl Default for Settings {
|
||||||
Theme::CatppuccinLatte => catppuccin_egui::LATTE,
|
fn default() -> Self {
|
||||||
Theme::CatppuccinFrappe => catppuccin_egui::FRAPPE,
|
Self {
|
||||||
Theme::CatppuccinMacchiato => catppuccin_egui::MACCHIATO,
|
start_elevated: false,
|
||||||
Theme::CatppuccinMocha => catppuccin_egui::MOCHA,
|
theme: Theme::default(),
|
||||||
|
launch_version: LaunchVersion::default(),
|
||||||
|
block_method: BlockMethod::default(),
|
||||||
|
save_server_ip: String::from(ROCKSTAR_SAVE_SERVER),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
|
||||||
pub struct Settings {
|
|
||||||
pub launch_version: LaunchVersion,
|
|
||||||
pub theme: Theme,
|
|
||||||
pub start_elevated: bool,
|
|
||||||
}
|
|
||||||
|
|||||||
+1
-1
@@ -13,7 +13,7 @@ impl UiExt for egui::Ui {
|
|||||||
self.horizontal(|ui| {
|
self.horizontal(|ui| {
|
||||||
ui.label(egui::RichText::new(text).font(egui::FontId::new(
|
ui.label(egui::RichText::new(text).font(egui::FontId::new(
|
||||||
12.5,
|
12.5,
|
||||||
egui::FontFamily::Name("Ubuntu-Regular".into()),
|
egui::FontFamily::Name("Inter 18pt Regular".into()),
|
||||||
)));
|
)));
|
||||||
ui.add(egui::Separator::default().horizontal());
|
ui.add(egui::Separator::default().horizontal());
|
||||||
});
|
});
|
||||||
|
|||||||
+10
-7
@@ -1,26 +1,29 @@
|
|||||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||||
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
|
||||||
compile_error!("This application must be compiled for Windows.");
|
|
||||||
|
|
||||||
mod features;
|
mod features;
|
||||||
mod gui;
|
mod gui;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
fn init_storage() {
|
fn init_storage() {
|
||||||
if !crate::util::consts::path::APP_STORAGE.exists() {
|
if !util::consts::path::APP_STORAGE.exists() {
|
||||||
std::fs::create_dir_all(crate::util::consts::path::APP_STORAGE.as_path()).unwrap();
|
std::fs::create_dir_all(util::consts::path::APP_STORAGE.as_path()).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn panic_hook(panic_info: &std::panic::PanicHookInfo<'_>) {
|
fn panic_hook(panic_info: &std::panic::PanicHookInfo<'_>) {
|
||||||
let backtrace = std::backtrace::Backtrace::capture();
|
let backtrace = std::backtrace::Backtrace::capture();
|
||||||
let message = format!("{panic_info}\nstack backtrace:\n{backtrace}\n");
|
let mut message = format!("{panic_info}");
|
||||||
crate::util::log::log(crate::util::log::LogLevel::Panic, &message);
|
if backtrace.status() == std::backtrace::BacktraceStatus::Captured {
|
||||||
|
write!(message, "\nstack backtrace:\n{backtrace}").unwrap();
|
||||||
|
}
|
||||||
|
log::error!("{message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
init_storage();
|
init_storage();
|
||||||
|
util::logging::Logger::init(log::LevelFilter::Info);
|
||||||
std::panic::set_hook(Box::new(panic_hook));
|
std::panic::set_hook(Box::new(panic_hook));
|
||||||
gui::run::run();
|
gui::run::run();
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
pub mod consts;
|
pub mod consts;
|
||||||
pub mod countdown;
|
pub mod countdown;
|
||||||
pub mod log;
|
pub mod logging;
|
||||||
pub mod persistent_state;
|
pub mod persistent_state;
|
||||||
pub mod system_info;
|
pub mod system_info;
|
||||||
pub mod win;
|
pub mod win;
|
||||||
|
|||||||
@@ -13,3 +13,22 @@ pub mod game {
|
|||||||
pub const EXE_LEGACY: &str = "GTA5.exe";
|
pub const EXE_LEGACY: &str = "GTA5.exe";
|
||||||
pub const WINDOW_TITLE: &str = "Grand Theft Auto V";
|
pub const WINDOW_TITLE: &str = "Grand Theft Auto V";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod colours {
|
||||||
|
use crate::features::game_networking::BlockedStatus;
|
||||||
|
use eframe::egui;
|
||||||
|
|
||||||
|
pub const RED: egui::Color32 = egui::Color32::from_rgb(255, 96, 96);
|
||||||
|
pub const YELLOW: egui::Color32 = egui::Color32::from_rgb(255, 255, 96);
|
||||||
|
pub const GREEN: egui::Color32 = egui::Color32::from_rgb(96, 255, 96);
|
||||||
|
|
||||||
|
impl From<BlockedStatus> for egui::Color32 {
|
||||||
|
fn from(value: BlockedStatus) -> Self {
|
||||||
|
match value {
|
||||||
|
BlockedStatus::Blocked => RED,
|
||||||
|
BlockedStatus::Failed => YELLOW,
|
||||||
|
BlockedStatus::Unblocked => GREEN,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
use crate::util::consts::path;
|
|
||||||
use std::{fs::File, io::Write};
|
|
||||||
use strum::Display;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Display)]
|
|
||||||
pub enum LogLevel {
|
|
||||||
Error,
|
|
||||||
Panic,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn log(level: LogLevel, message: &str) {
|
|
||||||
let mut file = File::options()
|
|
||||||
.create(true)
|
|
||||||
.append(true)
|
|
||||||
.open(path::APP_LOG.as_path())
|
|
||||||
.unwrap();
|
|
||||||
let timestamp = chrono::Local::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, false);
|
|
||||||
let message = format!("[{timestamp}] [{level}]\n{message}\n\n");
|
|
||||||
file.write_all(message.as_bytes()).unwrap();
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
use std::{
|
||||||
|
fs::File,
|
||||||
|
io::Write,
|
||||||
|
sync::{LazyLock, Mutex},
|
||||||
|
time::SystemTime,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Logger {
|
||||||
|
file: Mutex<File>,
|
||||||
|
}
|
||||||
|
|
||||||
|
static LOGGER: LazyLock<Logger> = LazyLock::new(|| Logger {
|
||||||
|
file: File::options()
|
||||||
|
.create(true)
|
||||||
|
.append(true)
|
||||||
|
.open(crate::util::consts::path::APP_LOG.as_path())
|
||||||
|
.unwrap()
|
||||||
|
.into(),
|
||||||
|
});
|
||||||
|
|
||||||
|
impl log::Log for Logger {
|
||||||
|
fn enabled(&self, metadata: &log::Metadata) -> bool {
|
||||||
|
metadata.level() <= log::Level::Info
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&self) {}
|
||||||
|
|
||||||
|
fn log(&self, record: &log::Record) {
|
||||||
|
if self.enabled(record.metadata()) {
|
||||||
|
self.log_to_stderr(record);
|
||||||
|
self.log_to_file(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Logger {
|
||||||
|
pub fn init(level: log::LevelFilter) {
|
||||||
|
log::set_logger(&*LOGGER)
|
||||||
|
.map(|()| log::set_max_level(level))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log_to_file(&self, record: &log::Record) {
|
||||||
|
let mut file = self.file.lock().unwrap();
|
||||||
|
write!(
|
||||||
|
file,
|
||||||
|
"[{}][{}]\n{}\n\n",
|
||||||
|
humantime::format_rfc3339_seconds(SystemTime::now()),
|
||||||
|
record.level(),
|
||||||
|
record.args()
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
file.flush().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log_to_stderr(&self, record: &log::Record) {
|
||||||
|
eprintln!(
|
||||||
|
"[{}][{}]\n{}",
|
||||||
|
humantime::format_rfc3339_seconds(SystemTime::now()),
|
||||||
|
record.level(),
|
||||||
|
record.args()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
use crate::{features::launch::Platform, gui::settings::Settings, util::consts::path};
|
use crate::{
|
||||||
|
features::launch::Platform,
|
||||||
|
gui::{app, settings::Settings},
|
||||||
|
util::consts::path,
|
||||||
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
fs::{self, File},
|
fs::{self, File},
|
||||||
@@ -8,6 +12,7 @@ use std::{
|
|||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct PersistentState {
|
pub struct PersistentState {
|
||||||
pub launcher: Platform,
|
pub launcher: Platform,
|
||||||
|
pub anti_afk_enabled: bool,
|
||||||
pub settings: Settings,
|
pub settings: Settings,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,4 +28,15 @@ impl PersistentState {
|
|||||||
let json = serde_json::to_string_pretty(&self).unwrap();
|
let json = serde_json::to_string_pretty(&self).unwrap();
|
||||||
config_file.write_all(json.as_bytes()).unwrap();
|
config_file.write_all(json.as_bytes()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn apply_to(self, app: &mut app::App) {
|
||||||
|
let Self {
|
||||||
|
launcher,
|
||||||
|
anti_afk_enabled,
|
||||||
|
settings,
|
||||||
|
} = self;
|
||||||
|
app.launch.selected = launcher;
|
||||||
|
app.anti_afk.enabled = anti_afk_enabled;
|
||||||
|
app.settings = settings;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ pub struct Process {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Process {
|
impl Process {
|
||||||
pub fn pid(&self) -> u32 {
|
pub const fn pid(&self) -> u32 {
|
||||||
self.pid
|
self.pid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-3
@@ -80,7 +80,7 @@ pub fn is_elevated() -> bool {
|
|||||||
result.is_ok() && elevation.TokenIsElevated != 0
|
result.is_ok() && elevation.TokenIsElevated != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_system_theme_dark() -> bool {
|
pub fn is_system_theme_light() -> bool {
|
||||||
use winreg::RegKey;
|
use winreg::RegKey;
|
||||||
let hkcu = RegKey::predef(winreg::enums::HKEY_CURRENT_USER);
|
let hkcu = RegKey::predef(winreg::enums::HKEY_CURRENT_USER);
|
||||||
let Ok(subkey) =
|
let Ok(subkey) =
|
||||||
@@ -88,8 +88,10 @@ pub fn is_system_theme_dark() -> bool {
|
|||||||
else {
|
else {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
let Ok(dword): Result<u32, std::io::Error> = subkey.get_value("AppsUseLightTheme") else {
|
let Ok(apps_use_light_theme): Result<u32, std::io::Error> =
|
||||||
|
subkey.get_value("AppsUseLightTheme")
|
||||||
|
else {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
dword != 1
|
apps_use_light_theme == 1
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user