From fd3863c0d4ee66087d2dbced248e5e5f55198e50 Mon Sep 17 00:00:00 2001 From: tommy Date: Fri, 14 Mar 2025 14:24:29 -0400 Subject: [PATCH] The Rewrite: Rustification --- Cargo.lock | 90 +++++++++++++++++++++++- Cargo.toml | 5 +- src/main.rs | 192 ++++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 234 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b9d2ecc..da6de95 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,7 +1,95 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "alt_enter_fix" version = "0.1.0" +dependencies = [ + "serde_json", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "proc-macro2" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" diff --git a/Cargo.toml b/Cargo.toml index 4499b17..27249a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [target.x86_64-pc-windows-gnu] -linker = "x86_64-w64-mingw32-gcc" \ No newline at end of file +linker = "x86_64-w64-mingw32-gcc" + +[dependencies] +serde_json = "1.0.140" diff --git a/src/main.rs b/src/main.rs index 92cf60a..c3d3666 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,60 +1,150 @@ -use std::{env, fs, io::{self, stdin, Read, Write}, path::Path}; -fn main() { - let mut rblxpath = String::new(); - if let Some(lad) = env::var("LOCALAPPDATA").ok() { - let temprbx_lad = format!("{}\\Roblox", lad); - if Path::new(&temprbx_lad).is_dir() { - rblxpath = temprbx_lad; - } - } else if let Some(pfx86) = env::var("ProgramFiles(x86)").ok() { - let temprbx_pfx86 = format!("{}\\Roblox", pfx86); - if Path::new(&temprbx_pfx86).is_dir() { - rblxpath = temprbx_pfx86; - } - } - if rblxpath != "" { - println!("Found path: {}", rblxpath); - } else { +use serde_json::{json, Value}; +use std::{env, fs, io::{self, stdin, Read, Write}, path::{Path, PathBuf}}; + +const UNLIMITED_FPS_CAP_NUM: i32 = i32::max_value(); + +fn main() -> io::Result<()> { + let roblox_path = find_roblox_path().ok_or_else(|| { println!("Roblox not found (Die)"); - return; - } + io::Error::new(io::ErrorKind::NotFound, "Roblox installation not found") + })?; + + println!("Found path: {}", roblox_path.display()); + println!("What FPS would you like to cap the fps to? (0 for no cap at all)"); - let _ = io::stdout().flush(); + io::stdout().flush()?; + + let fps = read_fps_input()?; + let desired_fps = if fps == 0 { UNLIMITED_FPS_CAP_NUM } else { fps }; + + println!("FPS cap will be set to {desired_fps}"); + + let required_settings = json!({ + // Restore original 'alt-enter' behaviour + "FFlagHandleAltEnterFullscreenManually": "False", + + // Disable Roblox's built-in 240 fps hard-coded cap and replace with custom cap + "DFIntTaskSchedulerTargetFps": desired_fps, + "FFlagGameBasicSettingsFramerateCap5": "False", + "FFlagTaskSchedulerLimitTargetFpsTo2402": "False", + + // Ensure Direct3D11 is the rendering API for optimal performance + "FFlagDebugGraphicsDisableDirect3D11": "False", + "FFlagDebugGraphicsPreferD3D11": "True", + + // Disable telemetry related FFlags + "FFlagDebugDisableTelemetryEphemeralCounter": "True", + "FFlagDebugDisableTelemetryEphemeralStat": "True", + "FFlagDebugDisableTelemetryEventIngest": "True", + "FFlagDebugDisableTelemetryPoint": "True", + "FFlagDebugDisableTelemetryV2Counter": "True", + "FFlagDebugDisableTelemetryV2Event": "True", + "FFlagDebugDisableTelemetryV2Stat": "True" + }); + + update_client_settings(&roblox_path, required_settings)?; + + println!("Press enter to exit"); + let _ = io::stdin().read(&mut [0u8])?; + + Ok(()) +} + +fn find_roblox_path() -> Option { + if let Some(local_app_data) = env::var("LOCALAPPDATA").ok() { + let path = PathBuf::from(local_app_data).join("Roblox"); + if path.is_dir() { + return Some(path); + } + } + + // This is here because Roblox installs into Program Files (x86) if you run the installer as administrator (??? lol) + if let Some(program_files) = env::var("ProgramFiles(x86)").ok() { + let path = PathBuf::from(program_files).join("Roblox"); + if path.is_dir() { + return Some(path); + } + } + + None +} + +fn read_fps_input() -> io::Result { let mut input = String::new(); - stdin().read_line(&mut input).expect("invalid input, try again"); - if let Some('\n')=input.chars().next_back() { - input.pop(); - } - if let Some('\r')=input.chars().next_back() { - input.pop(); - } - let fps = input.parse::().unwrap(); - let desired_fps = match fps { - 0 => 5588562, - _ => fps - }; - println!("fps cap will be set to {desired_fps}"); - let just_make_this_work = format!("{{\"FFlagHandleAltEnterFullscreenManually\":\"False\",\"DFIntTaskSchedulerTargetFps\":{desired_fps},\"FFlagEnableInGameMenuChromeABTest3\":\"False\",\"FFlagGameBasicSettingsFramerateCap5\":\"False\",\"FFlagTaskSchedulerLimitTargetFpsTo2402\":\"False\"}}"); - let fflag_contents = just_make_this_work.as_str(); - let versions = format!("{}\\Versions\\", rblxpath); - for folder in fs::read_dir(versions).unwrap() { - let cur_folder = folder.unwrap().path().display().to_string(); - if cur_folder.to_lowercase().contains("version") { - let executable = format!("{}\\RobloxPlayerBeta.exe", cur_folder); - if Path::new(&executable).is_file() { - let cs_folder = format!("{}\\ClientSettings", cur_folder); - if !Path::new(&cs_folder).is_dir() { - fs::create_dir(&cs_folder).ok(); - println!("Created ClientSettings folder in {}", cur_folder); + stdin().read_line(&mut input)?; + + let input = input.trim(); + + input.parse::().map_err(|_| { + io::Error::new(io::ErrorKind::InvalidInput, "Invalid FPS value") + }) +} + +fn update_client_settings(roblox_path: &Path, required_settings: Value) -> io::Result<()> { + let versions_path = roblox_path.join("Versions"); + + for entry in fs::read_dir(versions_path)? { + let entry = entry?; + let path = entry.path(); + let path_str = path.to_string_lossy().to_lowercase(); + + if path_str.contains("version") { + let executable = path.join("RobloxPlayerBeta.exe"); + + if executable.is_file() { + let client_settings_dir = path.join("ClientSettings"); + + if !client_settings_dir.exists() { + fs::create_dir(&client_settings_dir)?; + println!("Created ClientSettings folder in {}", path.display()); } else { - println!("ClientSettings folder already exists in {}", cur_folder); + println!("ClientSettings folder already exists in {}", path.display()); } - let cas_json = format!("{}\\ClientAppSettings.json", cs_folder); - fs::write(cas_json, fflag_contents).ok(); - println!("Wrote ClientAppSettings.json to {}", cur_folder) + + let settings_file = client_settings_dir.join("ClientAppSettings.json"); + update_settings_file(&settings_file, &required_settings)?; + println!("Updated ClientAppSettings.json in {}", path.display()); } } } - println!("Press enter to exit"); - let _ = io::stdin().read(&mut [0u8]).unwrap(); + + Ok(()) } + +fn update_settings_file(file_path: &Path, required_settings: &Value) -> io::Result<()> { + // Funky code to edit or create the ClientAppSettings.json file (shoutout claude for the help with the object stuff (json sucks)) + let mut settings = if file_path.exists() { + match fs::read_to_string(file_path) { + Ok(content) if !content.trim().is_empty() => { + match serde_json::from_str::(&content) { + Ok(existing) => existing, + Err(e) => { + println!("Warning: Could not parse existing settings ({}), creating new file", e); + json!({}) + } + } + }, + _ => json!({}) + } + } else { + json!({}) + }; + + if let Some(required_obj) = required_settings.as_object() { + if !settings.is_object() { + settings = json!({}); + } + + if let Some(settings_obj) = settings.as_object_mut() { + for (key, value) in required_obj { + println!("Adding {}: {}", key, value); + settings_obj.insert(key.clone(), value.clone()); + } + } + } + + let formatted = serde_json::to_string_pretty(&settings)?; + fs::write(file_path, formatted)?; + + Ok(()) +} \ No newline at end of file