From 165a9fc1396ee6d06bf7fe67bf2442a6e9610d2a Mon Sep 17 00:00:00 2001 From: "Rph :3" <11350302+rphsoftware@users.noreply.github.com> Date: Thu, 17 Apr 2025 00:39:10 +0200 Subject: [PATCH] game picker --- Cargo.lock | 171 +++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 5 +- src/app_logic.rs | 28 ++++++-- src/config.rs | 6 +- src/main.rs | 47 +++++++++++-- 5 files changed, 243 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0b6b910..215b5f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -165,6 +165,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" 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" @@ -689,6 +695,29 @@ dependencies = [ "libc", ] +[[package]] +name = "chrono" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "chrono-humanize" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799627e6b4d27827a814e837b9d8a504832086081806d45b1afa34dc982b023b" +dependencies = [ + "chrono", +] + [[package]] name = "clipboard-win" version = "5.4.0" @@ -1041,6 +1070,20 @@ dependencies = [ "egui", ] +[[package]] +name = "egui_extras" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624659a2e972a46f4d5f646557906c55f1cd5a0836eddbe610fdf1afba1b4226" +dependencies = [ + "ahash", + "egui", + "enum-map", + "log", + "mime_guess2", + "profiling", +] + [[package]] name = "egui_glow" version = "0.31.1" @@ -1074,6 +1117,27 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" +[[package]] +name = "enum-map" +version = "2.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9" +dependencies = [ + "enum-map-derive", + "serde", +] + +[[package]] +name = "enum-map-derive" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "enumflags2" version = "0.7.11" @@ -1539,6 +1603,30 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "iana-time-zone" +version = "0.1.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[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]] name = "icu_collections" version = "1.5.0" @@ -1937,6 +2025,24 @@ dependencies = [ "paste", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess2" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f54028747dfea8e8bf00d3c2d4e83cf023c1accfd5d436335456e9864940cb85" +dependencies = [ + "mime", + "phf", + "phf_shared", + "unicase", +] + [[package]] name = "miniz_oxide" version = "0.8.5" @@ -2446,6 +2552,50 @@ dependencies = [ "sha2", ] +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn", + "unicase", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", + "unicase", +] + [[package]] name = "pin-project" version = "1.1.10" @@ -2942,6 +3092,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + [[package]] name = "slab" version = "0.4.9" @@ -3249,12 +3405,15 @@ name = "tundlebool" version = "0.1.0" dependencies = [ "anyhow", + "chrono", + "chrono-humanize", "crossbeam-channel", "dirs", "eframe", "egui", "egui-modal", "egui_alignments", + "egui_extras", "env_logger", "hex", "keyvalues-parser", @@ -3299,6 +3458,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "unicase" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" + [[package]] name = "unicode-ident" version = "1.0.18" @@ -3798,6 +3963,12 @@ dependencies = [ "syn", ] +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + [[package]] name = "windows-result" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 75d84b1..986de6e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,9 @@ hex = "0.4.3" egui_alignments = "0.3.4" log = "0.4.27" egui-modal = { path = "./egui-modal" } +chrono-humanize = "0.2.3" +chrono = "0.4.40" +egui_extras = "0.31.1" [workspace] -members = ["egui-modal"] \ No newline at end of file +members = ["egui-modal"] diff --git a/src/app_logic.rs b/src/app_logic.rs index 2f67275..2244be8 100644 --- a/src/app_logic.rs +++ b/src/app_logic.rs @@ -2,7 +2,7 @@ use std::{f32::consts::E, path::PathBuf, sync::Arc, thread, time::Duration}; use crossbeam_channel::Receiver; use egui::{mutex::Mutex}; -use log::debug; +use log::{debug, info}; use crate::{config::{self, ConfigProvider}, omori_locator}; @@ -10,7 +10,7 @@ use crate::{config::{self, ConfigProvider}, omori_locator}; pub enum UiState { Loading, KeyRequired(String), - PickGame(Result, Vec), + PickGame(Result, Vec<(PathBuf, u64)>), Error(String) } @@ -20,7 +20,9 @@ pub struct UiStateHolder { } pub enum UiEvent { - SetKey(Vec) + SetKey(Vec), + UsePath(PathBuf), + UseSteamPath } pub struct AppThread { @@ -49,7 +51,7 @@ impl AppThread { } } - fn pick_game(&mut self, provider: &ConfigProvider) -> anyhow::Result<()> { + fn pick_game(&mut self, provider: &mut ConfigProvider) -> anyhow::Result { let steam_location = match omori_locator::get_omori_path() { Ok(l) => Ok(l), Err(e) => Err(String::from(format!("{:#}", e))) @@ -59,7 +61,20 @@ impl AppThread { self.commit(UiState::PickGame(steam_location.clone(), location_history.clone())); - Ok(()) + loop { + match self.ui_event_channel.recv()? { + UiEvent::UsePath(path) => { + provider.set_game_path_used(&path)?; + self.commit(UiState::Loading); + return Ok(path); + }, + UiEvent::UseSteamPath => { + self.commit(UiState::Loading); + return Ok(steam_location.expect("The steam location was not set even if the UI told us to use it. This should never happen")); + } + _ => {} + } + } } pub fn create(state_holder: &UiStateHolder, context: &egui::Context, ui_event_channel: &Receiver) -> AppThread { @@ -87,7 +102,8 @@ impl AppThread { config_provider.set_key(&self.decryption_key)?; - self.pick_game(&config_provider); + let game_path = self.pick_game(&mut config_provider)?; + info!("Will use {:?}", game_path); Ok(()) } diff --git a/src/config.rs b/src/config.rs index 8ace72d..02d918a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -66,11 +66,11 @@ impl ConfigProvider { Ok(()) } - pub fn get_game_paths(&self) -> anyhow::Result> { + pub fn get_game_paths(&self) -> anyhow::Result> { let mut paths: Vec<(&PathBuf, &u64)> = self.config.recently_used_game_paths.iter().collect(); - paths.sort_by(|a, b| a.1.cmp(b.1)); + paths.sort_by(|a, b| a.1.cmp(b.1).reverse()); - Ok(paths.iter().map(|v| v.0.clone()).collect()) + Ok(paths.iter().map(|v| (v.0.clone(), *v.1)).collect()) } pub fn set_game_path_used(&mut self, which: &PathBuf) -> anyhow::Result<()> { diff --git a/src/main.rs b/src/main.rs index b0a5a5e..3375f80 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,13 +2,14 @@ mod omori_locator; mod app_logic; mod config; -use std::{sync::Arc, thread, time::Duration}; +use std::{sync::Arc, thread, time::{Duration, SystemTime}}; use app_logic::{AppThread, UiEvent, UiState, UiStateHolder}; use crossbeam_channel::{Receiver, Sender}; use eframe::egui; use egui::{mutex::{Mutex, RwLock}, Align, Layout, RichText, ThemePreference}; use egui_alignments::{center_horizontal, center_vertical, top_horizontal, Aligner}; +use egui_extras::{Column, TableBuilder}; use egui_modal::Modal; use sha2::Digest; @@ -39,6 +40,9 @@ fn main() -> anyhow::Result<()> { eframe::run_native("TundleBool", options, Box::new(|cc| { cc.egui_ctx.set_theme(ThemePreference::System); + cc.egui_ctx.style_mut(|style| { + style.interaction.selectable_labels = false; + }); let mut app_thread = AppThread::create(&app_state, &cc.egui_ctx, &receiver); @@ -164,7 +168,9 @@ impl eframe::App for Application { Ok(path) => { ui.label(RichText::new(format!("Found game at:\n{}", path.display())).italics()); ui.separator(); - ui.button(RichText::new("Use Steam version").size(16.0)); + if ui.button(RichText::new("Use Steam version").size(16.0)).clicked() { + self.sender.send(UiEvent::UseSteamPath).expect("Failed to send"); + } }, Err(e) => { ui.label(RichText::new(format!("Failed to find game:\n{}", e)).color(ui.visuals().error_fg_color)); @@ -175,11 +181,44 @@ impl eframe::App for Application { columns[1].vertical(|ui| { ui.label(RichText::new("Custom").size(24.0)); if ui.button(RichText::new("Pick game location").size(16.0)).clicked() { - rfd::FileDialog::new().pick_folder(); - invalid_path_modal.open(); + match rfd::FileDialog::new().pick_folder() { + Some(path) => { + if omori_locator::validate_omori_installation(&path) { + self.sender.send(UiEvent::UsePath(path)).expect("Failed to send"); + } else { + invalid_path_modal.open(); + } + }, + None => {} + } } ui.separator(); + if others.len() > 0 { + ui.label("History of game locations"); + + TableBuilder::new(ui) + .striped(true) + .column(Column::remainder()) + .column(Column::auto()) + .body(|body| { + body.rows(20.0, others.len(), |mut row| { + let item = &others[row.index()]; + let dt = + (item.1 as i64) - + (SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).expect("Failed to get the time").as_secs() as i64); + + let dt = chrono::Duration::from(chrono::TimeDelta::seconds(dt as i64)); + + row.col(|ui| { + ui.label(format!("{}", item.0.display())); + }); + + row.col(|ui| { + ui.label(format!("{}", chrono_humanize::HumanTime::from(dt))); + }); + }); + }); } else { ui.label("Once you use a custom location, it will be remembered here.");