Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
authors = ["Gabriel"]
license = "MIT"
name = "steam"
version = "0.3.1"
version = "0.4.1"
edition = "2024"

[lib]
Expand All @@ -24,7 +24,7 @@ opt-level = 3

[dependencies]
eframe = { version = "0.33.3", features = ["wgpu", "persistence"] }
egui_code_editor = "0.2.20"
egui_code_editor = "0.2.21"
egui_extras = { version = "0.33.3", features = ["all_loaders"] }
image = { version = "0.25.9", features = ["jpeg"] }
reqwest = { version = "0.13.2", features = ["json","blocking"] }
Expand Down
15 changes: 12 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# 🧰 Steamtools

**Steamtools** is a lightweight tool written in **Rust** that allows you to easily **view, install, and remove Lua Manifests**. The source code of the proxy dll: xinput1_4.dll is currently not available since I only ship the binary for it. May change in Future !
**Steamtools** is a lightweight tool written in **Rust** that allows you to easily **view, install, and remove Lua Manifests**. The source code of the proxy dll: xinput1_4.dll is currently not available since I only ship the binary for it. May **change** in the **future** !

---

Expand All @@ -11,7 +11,7 @@
- [x] Add tool: Install
- [x] Add tool: Uninstall
- [X] Add tool: View
- [X] Add Window: Settings (experimental)
- [X] Add Window: Settings

---

Expand All @@ -25,10 +25,19 @@

---

## Donating

Since this is a personal project and I invest my free time in making this you could support me by donating!
SOL: GYepZXvMAyQ8y54HuWx8QR3yBBg9EFEqn7dDJo93GbTM (SOL Network)

[![Donate](https://img.shields.io/badge/Donate-Ko--fi-orange)](https://ko-fi.com/realviper)

## Prerequisites

- VC_Runtime: [x64]("https://aka.ms/vs/17/release/vc_redist.x64.exe")
- Opengl Drivers: [AMD]("https://www.amd.com/en/support/download/drivers.html") [NVIDIA]("https://www.nvidia.com/de-de/drivers/")
- Opengl Drivers:
- [AMD]("https://www.amd.com/en/support/download/drivers.html")
- [NVIDIA]("https://www.nvidia.com/de-de/drivers/")

**Language:** Rust 🦀
**Status:** In active development 🚀
Expand Down
89 changes: 72 additions & 17 deletions gui/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![cfg_attr(not(debug_assertions), windows_subsystem="windows")]


use std::collections::HashMap;
use std::fmt::Write;
use std::process;
use std::{fs, path::PathBuf, process::exit, sync::{Arc, Mutex}, thread};
Expand Down Expand Up @@ -34,7 +34,7 @@ struct App {
st: Steam,
settings: Settings,
state: State,
games: Arc<Mutex<Vec<Game>>>,
games: Arc<Mutex<HashMap<u32, Game>>>,
cached_games: GameMap,
loaded: bool,
view: ViewPopup,
Expand Down Expand Up @@ -121,7 +121,7 @@ impl App {
});

storage_ref.get_string("games" ).map(|games| {
app.games = serde_json::from_str::<Arc<Mutex<Vec<Game>>>>(&games).unwrap();
app.games = serde_json::from_str::<Arc<Mutex<HashMap<u32, Game>>>>(&games).unwrap();
app.loaded = true;
});

Expand Down Expand Up @@ -229,7 +229,10 @@ impl eframe::App for App {
ui.horizontal(|ui| {
ui.label("Made by");
ui.hyperlink_to("RealViper", "https://github.com/RealViper8/Steamtools");
ui.add_space((ui.available_width()/2.0)+85.0);
ui.label(format!("Version: {}", VERSION));
});

});
});

Expand Down Expand Up @@ -281,14 +284,45 @@ impl eframe::App for App {
path.push(&self.st.path);
path.push("config\\stplug-in");
match files {
Some(files) => {
Some(ref files) => {
files.iter().for_each(|file| {
path.push(&file.file_stem().unwrap());
fs::copy(file.as_path(),format!("{}.lua", &path.to_string_lossy())).unwrap();
});
},
None => ()
}

if files.is_none() {
return;
}

// for file in files.unwrap() {
// let games_lock = self.games.clone();
// match file.file_stem().unwrap().to_str().unwrap().parse::<u32>().ok() {
// Some(appid) => {
// let appid = appid;
// thread::spawn(move || {
// let url = format!("{}{}", STEAM_URL, appid);
// println!("[FETCHING] {}", appid);
// dbg!(&url);
// let resp: HashMap<String, GameDetails> = match blocking::get(url).ok().unwrap().json() {
// Ok(r) => {
// r
// },
// Err(e) => {
// eprintln!("[FETCHING ERROR] {}", e);
// return
// }
// };


// });
// },
// None => println!("error: failed to read .lua make sure the filename is the appid !"),
// };
// }
self.loaded = false;
}

if ui.checkbox(&mut self.unlock, "Unlock").changed() {
Expand Down Expand Up @@ -319,16 +353,20 @@ impl eframe::App for App {
let height = ui.available_height();
egui::ScrollArea::vertical().auto_shrink([false; 2]).show(ui, |ui| {
egui::Grid::new("games").striped(false).show(ui, |ui| {
for (i, game) in self.games.lock().unwrap().iter().enumerate() {
let t = {
self.games.lock().unwrap().clone()
};

for (i, game) in t.iter().enumerate() {
ui.with_layout(egui::Layout::left_to_right(egui::Align::Center), |ui| {
self.buffer.clear();
write!(&mut self.buffer, "file://icons/{}.jpg", game.appid).unwrap();
write!(&mut self.buffer, "file://icons/{}.jpg", game.0).unwrap();
ui.add(
egui::Image::new(&self.buffer)
.fit_to_exact_size(egui::vec2(width * 0.4, height * 0.4))
);
self.buffer.clear();
ui.add_sized([width * 0.3, height * 0.3], egui::Label::new(RichText::new(&game.details.name).strong()).wrap());
ui.add_sized([width * 0.3, height * 0.3], egui::Label::new(RichText::new(&game.1.details.name).strong()).wrap());

ui.vertical(|ui| {
let height = ui.available_height();
Expand All @@ -337,37 +375,46 @@ impl eframe::App for App {
p.push("config");
p.push("stplug-in");
self.buffer.clear();
write!(&mut self.buffer, "{}.lua", game.appid).unwrap();
write!(&mut self.buffer, "{}.lua", game.0).unwrap();
p.push(&self.buffer);
self.buffer.clear();
if fs::remove_file(p).is_err() {
if fs::remove_file(&p).is_err() {
rfd::MessageDialog::new()
.set_title("Error")
.set_description("Failed to delete")
.set_buttons(rfd::MessageButtons::Ok);
}

self.loaded = false;
p.clear();
p.push("icons");
p.push(format!("{}.jpg", game.0));
dbg!(&p);

fs::remove_file(&p).ok();

if let Ok(ref mut g) = self.games.try_lock() {
g.remove(game.0);
}
}

if game.installed {
if game.1.installed {
if ui.add_sized([width * 0.3, height * 0.3], egui::Button::new(RichText::new("\u{1F5D1} Uninstall").strong().raised())).on_hover_text("Prompts steam to uninstall the game").clicked() {
write!(&mut self.buffer, "start steam://uninstall/{}", game.appid).unwrap();
write!(&mut self.buffer, "start steam://uninstall/{}", game.1.appid).unwrap();
#[cfg(target_os="windows")]
process::Command::new("cmd").args(["/C", &self.buffer]).spawn().expect("Failed to uninstall");
self.buffer.clear();
}
} else {
if ui.add_sized([width * 0.3, height * 0.3], egui::Button::new(RichText::new("\u{2795} Install").strong().raised())).on_hover_text("Prompts steam to install the game").clicked() {
write!(&mut self.buffer, "start steam://install/{}", game.appid).unwrap();
write!(&mut self.buffer, "start steam://install/{}", game.1.appid).unwrap();
#[cfg(target_os="windows")]
process::Command::new("cmd").args(["/C", &self.buffer]).spawn().expect("Failed to install");
self.buffer.clear();
}
}

if ui.add_sized([width * 0.3, height * 0.3], egui::Button::new(RichText::new("\u{1F50D} View").strong())).on_hover_text("View information about the game").clicked() {
self.view.game_id = game.appid;
self.view.game_id = game.1.appid;
self.view.current_game = i;
self.view.active = true;
}
Expand All @@ -386,9 +433,17 @@ impl eframe::App for App {
let s = self.st.path.clone();
let games_arc = self.games.clone();
thread::spawn(move || {
let result = get_games(&s);
let mut games_lock = games_arc.lock().unwrap();
*games_lock = result;
let mut sbin = fs::File::create(STEAM_BINARY_PATH).unwrap();

let current_games = {
games_arc.lock().unwrap().clone()
};
GameMap::write_to(&mut sbin, &current_games).unwrap();

let result = get_games(&s, current_games);

let mut games = games_arc.lock().unwrap();
*games = result;
});

self.loaded = true;
Expand Down
8 changes: 4 additions & 4 deletions gui/utils/bserializer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ fn read_string(reader: &mut impl Read, len_buf: &mut [u8; 4], buf: &mut Vec<u8>)
pub struct GameMap(pub HashMap<u32, Game>);

impl GameMap {
pub fn write_to(file: &mut impl Write, map: HashMap<u32, &Game>) -> io::Result<()> {
pub fn write_to(file: &mut impl Write, map: &HashMap<u32, Game>) -> io::Result<()> {
let mut writer = BufWriter::new(file);

writer.write_all(&(map.len() as u32).to_le_bytes())?; // Length
Expand Down Expand Up @@ -105,11 +105,11 @@ mod tests {

#[test]
fn write_read() {
let mut f = fs::File::create("test.lua").unwrap();
let mut f = fs::File::create("test.bin").unwrap();
let mut map = HashMap::new();
let g = Game::default();
map.insert(1, &g);
let gm = GameMap::write_to(&mut f, map);
map.insert(1, g);
let gm = GameMap::write_to(&mut f, &map);
gm.unwrap()
}
}
10 changes: 5 additions & 5 deletions gui/window/mods.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use std::collections::HashMap;
use std::fs::File;
use std::path::Path;

use steamtools::{Game, install_melonloader};
use steamtools::{install_melonloader};

use crate::STEAM_BINARY_PATH;
use crate::utils::bserializer::GameMap;
Expand All @@ -27,10 +26,11 @@ impl WindowPopup for ModsPopup {

if ui.button("Get").clicked() {
if app.cached_games.0.is_empty() && !Path::new(STEAM_BINARY_PATH).exists() {
let games_guard = app.games.lock().unwrap();
let mut stfile = File::create(STEAM_BINARY_PATH).unwrap();
dbg!(&games_guard.iter().map(|g| (g.appid, g)).collect::<HashMap<u32, &Game>>());
GameMap::write_to(&mut stfile, games_guard.iter().map(|g| (g.appid, g)).collect::<HashMap<u32, &Game>>()).unwrap();
{
let games_guard = app.games.lock().unwrap();
GameMap::write_to(&mut stfile, &games_guard).unwrap();
}
} else if app.cached_games.0.is_empty() && Path::new(STEAM_BINARY_PATH).exists() {
let mut stfile = File::open(STEAM_BINARY_PATH).unwrap();
app.cached_games.0 = GameMap::read_from(&mut stfile).unwrap();
Expand Down
2 changes: 0 additions & 2 deletions gui/window/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ impl WindowPopup for ViewPopup {
write!(&mut app.buffer, "APPID: {}", app.view.game_id).unwrap();
ui.label(&app.buffer);
app.buffer.clear();
// ui.horizontal(|ui| {
// })
});
}
}
Expand Down
Loading