Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8943e61f3a
|
||
|
|
ed0a828fd4
|
||
|
|
96ef494443
|
||
|
|
9b5340a6c5
|
||
|
|
74132ec322
|
||
|
|
a58606c91d
|
||
|
|
47adf42a48
|
||
|
|
0fb587027c
|
||
|
|
0424b7b114
|
||
|
|
e89c3acc06
|
||
|
|
ab64c29935
|
||
|
|
e216445c7f
|
Generated
+1
-1
@@ -812,7 +812,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gta-tools"
|
||||
version = "0.10.0"
|
||||
version = "0.11.0"
|
||||
dependencies = [
|
||||
"catppuccin-egui",
|
||||
"eframe",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "gta-tools"
|
||||
version = "0.10.0"
|
||||
version = "0.11.0"
|
||||
edition = "2024"
|
||||
license = "Apache-2.0"
|
||||
authors = ["futile <git@futile.eu>"]
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
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">
|
||||
<source srcset="https://i.vgy.me/xt8EfK.png" media="(prefers-color-scheme: dark)">
|
||||
<img src="https://i.vgy.me/lYlZnd.png">
|
||||
</picture>
|
||||
|
||||
## 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.
|
||||
|
||||
**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
|
||||
|
||||
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 this repository by going to the <kbd>About</kbd> page of GTA Tools and clicking the GitHub button beside the version number.
|
||||
|
||||
#### Game
|
||||
|
||||
This section is quite simple.
|
||||
@@ -61,7 +61,7 @@ This feature is primarily useful for **replay glitching**, which is an exploit t
|
||||
|
||||
## 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
|
||||
+111
-44
@@ -1,9 +1,13 @@
|
||||
use crate::util::{
|
||||
use crate::{
|
||||
gui::settings::BlockMethod,
|
||||
util::{
|
||||
consts::game::{EXE_ENHANCED, EXE_LEGACY},
|
||||
system_info::SystemInfo,
|
||||
},
|
||||
};
|
||||
use std::{
|
||||
path::Path,
|
||||
error::Error,
|
||||
path::{Path, PathBuf},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use strum::{Display, EnumIter};
|
||||
@@ -11,7 +15,7 @@ use windows::{
|
||||
Win32::{
|
||||
NetworkManagement::WindowsFirewall::{
|
||||
INetFwPolicy2, INetFwRule, NET_FW_ACTION_BLOCK, NET_FW_IP_PROTOCOL_ANY,
|
||||
NET_FW_RULE_DIR_IN, NET_FW_RULE_DIR_OUT, NetFwPolicy2, NetFwRule,
|
||||
NET_FW_RULE_DIR_OUT, NetFwPolicy2, NetFwRule,
|
||||
},
|
||||
System::Com::{
|
||||
CLSCTX_INPROC_SERVER, COINIT_MULTITHREADED, CoCreateInstance, CoInitializeEx,
|
||||
@@ -21,8 +25,8 @@ use windows::{
|
||||
core::BSTR,
|
||||
};
|
||||
|
||||
const FILTER_NAME_IN: &str = "[GTA Tools] Block all inbound traffic for GTA V";
|
||||
const FILTER_NAME_OUT: &str = "[GTA Tools] Block all outbound traffic for GTA V";
|
||||
const FILTER_NAME_EXE: &str = "[GTA Tools] Block outbound traffic for all of GTA V";
|
||||
const FILTER_NAME_SAVE_SERVER: &str = "[GTA Tools] Block outbound traffic to Rockstar save server";
|
||||
|
||||
const INTERVAL: Duration = Duration::from_secs(3);
|
||||
|
||||
@@ -54,7 +58,11 @@ pub struct GameNetworking {
|
||||
impl Default for GameNetworking {
|
||||
fn default() -> 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(),
|
||||
timer: Instant::now(),
|
||||
counting: false,
|
||||
@@ -70,53 +78,93 @@ impl Drop for GameNetworking {
|
||||
}
|
||||
}
|
||||
|
||||
enum Mode {
|
||||
EntireGame(PathBuf),
|
||||
SaveServer(String),
|
||||
}
|
||||
|
||||
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 {
|
||||
self.blocked_status = BlockedStatus::Failed;
|
||||
return;
|
||||
return Ok(());
|
||||
};
|
||||
let policy: INetFwPolicy2 =
|
||||
unsafe { CoCreateInstance(&NetFwPolicy2, None, CLSCTX_INPROC_SERVER) }.unwrap();
|
||||
let rules = unsafe { policy.Rules() }.unwrap();
|
||||
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),
|
||||
] {
|
||||
unsafe { rules.Remove(&BSTR::from(filter.0)) }.unwrap();
|
||||
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();
|
||||
self.block_generic(Mode::EntireGame(exe_path.to_path_buf()))?;
|
||||
self.blocked_status = Self::is_exe_blocked()?.into();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unblock_all(&mut self) {
|
||||
let policy: INetFwPolicy2 =
|
||||
unsafe { CoCreateInstance(&NetFwPolicy2, None, CLSCTX_INPROC_SERVER) }.unwrap();
|
||||
let rules = unsafe { policy.Rules() }.unwrap();
|
||||
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();
|
||||
pub fn unblock_exe(&mut self) -> Result<(), Box<dyn Error>> {
|
||||
self.unblock_generic(FILTER_NAME_EXE)?;
|
||||
self.blocked_status = Self::is_exe_blocked()?.into();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_blocked() -> bool {
|
||||
let policy: INetFwPolicy2 =
|
||||
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
|
||||
fn is_exe_blocked() -> Result<bool, Box<dyn Error>> {
|
||||
Self::is_blocked_generic(FILTER_NAME_EXE)
|
||||
}
|
||||
|
||||
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 {
|
||||
self.counting = true;
|
||||
self.timer = Instant::now();
|
||||
@@ -126,7 +174,26 @@ impl GameNetworking {
|
||||
&& self.timer.elapsed() >= INTERVAL
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+85
-24
@@ -1,6 +1,10 @@
|
||||
use crate::{
|
||||
features,
|
||||
gui::{settings::Settings, tools, ui_ext::UiExt},
|
||||
gui::{
|
||||
settings::{BlockMethod, ROCKSTAR_SAVE_SERVER, Settings},
|
||||
tools,
|
||||
ui_ext::UiExt,
|
||||
},
|
||||
util::{
|
||||
consts::{colours, game::WINDOW_TITLE},
|
||||
persistent_state::PersistentState,
|
||||
@@ -156,15 +160,15 @@ impl App {
|
||||
egui::Frame::new()
|
||||
.outer_margin(egui::vec2(0.0, -2.0))
|
||||
.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.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.create_indicator_dot(self.game_networking.blocked_status)
|
||||
.on_hover_text(
|
||||
"This turns yellow if GTA Tools\ncannot find your game.",
|
||||
);
|
||||
self.game_networking.if_failed_return_to_boolean();
|
||||
ui.create_indicator_dot(self.game_networking.blocked_status);
|
||||
self.game_networking.reset_indicator_if_failed();
|
||||
label
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
@@ -175,19 +179,36 @@ impl App {
|
||||
.add_sized([button_width, 18.0], egui::Button::new("Block"))
|
||||
.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
|
||||
.add_sized([button_width, 18.0], egui::Button::new("Unblock"))
|
||||
.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(
|
||||
"This requires administrator.\nUse the Elevate button.",
|
||||
);
|
||||
})
|
||||
.response
|
||||
.on_disabled_hover_text("This requires administrator.\nUse the Elevate button.");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -198,17 +219,10 @@ impl App {
|
||||
}
|
||||
|
||||
fn show_settings_stage(&mut self, ctx: &egui::Context, ui: &mut egui::Ui) {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Launch version");
|
||||
egui::ComboBox::from_id_salt("Launch version")
|
||||
.selected_text(self.settings.launch_version.to_string())
|
||||
.show_ui(ui, |ui| {
|
||||
ui.build_menu(&mut self.settings.launch_version);
|
||||
});
|
||||
});
|
||||
ui.collapsing("General", |ui| {
|
||||
ui.checkbox(&mut self.settings.start_elevated, "Always start elevated");
|
||||
ui.horizontal(|ui| {
|
||||
let selection = self.settings.theme;
|
||||
ui.label("Theme");
|
||||
egui::ComboBox::from_id_salt("Theme")
|
||||
.selected_text(self.settings.theme.to_string())
|
||||
.show_ui(ui, |ui| {
|
||||
@@ -217,8 +231,51 @@ impl App {
|
||||
if selection != self.settings.theme {
|
||||
catppuccin_egui::set_theme(ctx, self.settings.theme.into());
|
||||
}
|
||||
ui.label("Theme");
|
||||
});
|
||||
});
|
||||
ui.collapsing("Game", |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
egui::ComboBox::from_id_salt("Launch version")
|
||||
.selected_text(self.settings.launch_version.to_string())
|
||||
.show_ui(ui, |ui| {
|
||||
ui.build_menu(&mut self.settings.launch_version);
|
||||
});
|
||||
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);
|
||||
});
|
||||
ui.horizontal(|ui| {
|
||||
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) {
|
||||
@@ -232,8 +289,12 @@ impl App {
|
||||
ui.label("❤");
|
||||
});
|
||||
ui.label(" from ");
|
||||
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.style_mut().spacing.button_padding = egui::Vec2::new(4.0, 0.0);
|
||||
if ui.button("").on_hover_text("View source code").clicked() {
|
||||
|
||||
+57
-32
@@ -2,6 +2,41 @@ use crate::util::win;
|
||||
use serde::{Deserialize, Serialize};
|
||||
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)]
|
||||
pub enum LaunchVersion {
|
||||
#[default]
|
||||
@@ -10,41 +45,31 @@ pub enum LaunchVersion {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Display, PartialEq, Eq, Serialize, Deserialize, EnumIter)]
|
||||
pub enum Theme {
|
||||
pub enum BlockMethod {
|
||||
#[default]
|
||||
#[strum(to_string = "Auto")]
|
||||
Auto,
|
||||
#[strum(to_string = "Catppuccin Latte")]
|
||||
CatppuccinLatte,
|
||||
#[strum(to_string = "Catppuccin Frappe")]
|
||||
CatppuccinFrappe,
|
||||
#[strum(to_string = "Catppuccin Macchiato")]
|
||||
CatppuccinMacchiato,
|
||||
#[strum(to_string = "Catppuccin Mocha")]
|
||||
CatppuccinMocha,
|
||||
#[strum(to_string = "Entire game")]
|
||||
EntireGame,
|
||||
#[strum(to_string = "Save server")]
|
||||
SaveServer,
|
||||
}
|
||||
|
||||
impl From<Theme> for catppuccin_egui::Theme {
|
||||
fn from(val: Theme) -> Self {
|
||||
match val {
|
||||
Theme::Auto => {
|
||||
if win::is_system_theme_dark() {
|
||||
catppuccin_egui::MOCHA
|
||||
} else {
|
||||
catppuccin_egui::LATTE
|
||||
}
|
||||
}
|
||||
Theme::CatppuccinLatte => catppuccin_egui::LATTE,
|
||||
Theme::CatppuccinFrappe => catppuccin_egui::FRAPPE,
|
||||
Theme::CatppuccinMacchiato => catppuccin_egui::MACCHIATO,
|
||||
Theme::CatppuccinMocha => catppuccin_egui::MOCHA,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Settings {
|
||||
pub launch_version: LaunchVersion,
|
||||
pub theme: Theme,
|
||||
pub start_elevated: bool,
|
||||
pub theme: Theme,
|
||||
pub launch_version: LaunchVersion,
|
||||
pub save_server_ip: String,
|
||||
pub block_method: BlockMethod,
|
||||
}
|
||||
|
||||
impl Default for Settings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
start_elevated: false,
|
||||
theme: Theme::default(),
|
||||
launch_version: LaunchVersion::default(),
|
||||
block_method: BlockMethod::default(),
|
||||
save_server_ip: String::from(ROCKSTAR_SAVE_SERVER),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -4,6 +4,8 @@ mod features;
|
||||
mod gui;
|
||||
mod util;
|
||||
|
||||
use std::fmt::Write;
|
||||
|
||||
fn init_storage() {
|
||||
if !util::consts::path::APP_STORAGE.exists() {
|
||||
std::fs::create_dir_all(util::consts::path::APP_STORAGE.as_path()).unwrap();
|
||||
@@ -14,7 +16,7 @@ fn panic_hook(panic_info: &std::panic::PanicHookInfo<'_>) {
|
||||
let backtrace = std::backtrace::Backtrace::capture();
|
||||
let mut message = format!("{panic_info}");
|
||||
if backtrace.status() == std::backtrace::BacktraceStatus::Captured {
|
||||
message += &format!("\nstack backtrace:\n{backtrace}");
|
||||
write!(message, "\nstack backtrace:\n{backtrace}").unwrap();
|
||||
}
|
||||
log::error!("{message}");
|
||||
}
|
||||
|
||||
+1
-1
@@ -59,6 +59,6 @@ impl Logger {
|
||||
humantime::format_rfc3339_seconds(SystemTime::now()),
|
||||
record.level(),
|
||||
record.args()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-3
@@ -80,7 +80,7 @@ pub fn is_elevated() -> bool {
|
||||
result.is_ok() && elevation.TokenIsElevated != 0
|
||||
}
|
||||
|
||||
pub fn is_system_theme_dark() -> bool {
|
||||
pub fn is_system_theme_light() -> bool {
|
||||
use winreg::RegKey;
|
||||
let hkcu = RegKey::predef(winreg::enums::HKEY_CURRENT_USER);
|
||||
let Ok(subkey) =
|
||||
@@ -88,8 +88,10 @@ pub fn is_system_theme_dark() -> bool {
|
||||
else {
|
||||
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;
|
||||
};
|
||||
dword != 1
|
||||
apps_use_light_theme == 1
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user