use std::{path::PathBuf, sync::Arc}; use crossbeam_channel::Receiver; use egui::mutex::Mutex; use log::{debug, info}; use crate::{config::{self, ConfigProvider}, mod_builder::ModConfiguration, omori_locator}; #[derive(Clone)] pub enum UiState { Loading, KeyRequired(String), PickGame(Result, Vec<(PathBuf, u64)>), PickPlaytest(Vec<(PathBuf, u64)>), Configure(ModConfiguration), Error(String) } #[derive(Clone)] pub struct UiStateHolder { pub state: Arc> } pub enum UiEvent { SetKey(Vec), UsePath(PathBuf), UseSteamPath, UseConfiguration(ModConfiguration) } pub struct AppThread { ui_state: UiStateHolder, context: egui::Context, ui_event_channel: Receiver, decryption_key: Vec } impl AppThread { fn commit(&self, s: UiState) { let mut state = self.ui_state.state.lock(); *state = s; drop(state); self.context.request_repaint(); } fn get_key(&self, reason: String) -> anyhow::Result> { self.commit(UiState::KeyRequired(reason)); loop { match self.ui_event_channel.recv()? { UiEvent::SetKey(key) => { self.commit(UiState::Loading); return Ok(key); } _ => {} } } } fn pick_game(&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))) }; let location_history = provider.get_game_paths()?; self.commit(UiState::PickGame(steam_location.clone(), location_history.clone())); 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")); } _ => {} } } } fn pick_playtest(&self, provider: &mut ConfigProvider) -> anyhow::Result { let playtest_history = provider.get_playtest_paths()?; self.commit(UiState::PickPlaytest(playtest_history.clone())); loop { match self.ui_event_channel.recv()? { UiEvent::UsePath(path) => { provider.set_playtest_path_used(&path)?; self.commit(UiState::Loading); return Ok(path); }, _ => {} } } } fn configure_bundling(&self, provider: &mut ConfigProvider, path: &PathBuf) -> anyhow::Result { let configuration = provider.get_configuration_for_playtest(path); self.commit(UiState::Configure(configuration)); loop { match self.ui_event_channel.recv()? { UiEvent::UseConfiguration(config) => { provider.set_configuration_for_playtest(&path, &config)?; self.commit(UiState::Loading); return Ok(config); }, _ => {} } } } pub fn create(state_holder: &UiStateHolder, context: &egui::Context, ui_event_channel: &Receiver) -> AppThread { AppThread { ui_state: state_holder.clone(), context: context.clone(), ui_event_channel: ui_event_channel.clone(), decryption_key: Vec::new() } } fn real_main(&mut self) -> anyhow::Result<()> { debug!("I am ALIVE! HAHAHAHA!"); self.commit(UiState::Loading); let mut config_provider = config::ConfigProvider::new()?; self.decryption_key = match config_provider.get_key() { Some(key) => key, None => match omori_locator::get_omori_key() { Ok(key) => key, Err(reason) => self.get_key(format!("{:#}", reason))?, } }; config_provider.set_key(&self.decryption_key)?; let game_path = self.pick_game(&mut config_provider)?; info!("Will use {:?}", game_path); let playtest_path = self.pick_playtest(&mut config_provider)?; info!("Playtest {:?}", playtest_path); let config = self.configure_bundling(&mut config_provider, &playtest_path)?; info!("Config {:?}", config); Ok(()) } pub fn main(&mut self) { match self.real_main() { Ok(_) => {}, Err(e) => self.commit(UiState::Error(format!("{:#}", e))), } } }