diff --git a/src/card.rs b/src/card.rs index 6f92763..d2aca66 100644 --- a/src/card.rs +++ b/src/card.rs @@ -74,6 +74,8 @@ pub trait Card { None } + fn get_settings(&self) -> String; + fn settings_ui(_settings: ron::Value) -> anyhow::Result where Self: Sized; diff --git a/src/main.rs b/src/main.rs index f2e7211..fb18bd1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -65,9 +65,14 @@ impl AltairEmulator { } else { eframe::get_value(cc.storage.unwrap(), "options").unwrap() }; + let cards = if cc.storage.unwrap().get_string("cards").is_none() { + Vec::new() + } else { + eframe::get_value(cc.storage.unwrap(), "cards").unwrap() + }; Self { textures: Textures::new(&cc.egui_ctx), - state: EmuState::new(audio_tx, options), + state: EmuState::new(audio_tx, options, cards), windows: HashMap::new(), } } @@ -117,6 +122,7 @@ impl AltairEmulator { impl eframe::App for AltairEmulator { fn save(&mut self, storage: &mut dyn eframe::Storage) { eframe::set_value(storage, "options", &self.state.options()); + eframe::set_value(storage, "cards", &self.state.save_cards()); } fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { ctx.set_pixels_per_point(1.0); diff --git a/src/option_window.rs b/src/option_window.rs index 48046eb..3933b16 100644 --- a/src/option_window.rs +++ b/src/option_window.rs @@ -17,7 +17,7 @@ use crate::{ pub struct OptionWindow { options: Options, category: OptionsCategory, - card_options: Vec<(&'static str, Box)>, + card_options: Vec<(&'static Type, Box)>, select_idx: Option, } @@ -30,7 +30,7 @@ enum OptionsCategory { impl OptionWindow { pub fn new(ctx: &egui::Context, state: &EmuState) -> Self { Modal::new(ctx, "options_modal").open(); - let card_options = Vec::new(); + let card_options: Vec<_> = state.cards().iter().map(|(typ, card)| (*typ, typ.settings_ui(ron::from_str(&card.get_settings()).unwrap()).unwrap())).collect(); let select_idx = if card_options.is_empty() { None } else { @@ -43,6 +43,12 @@ impl OptionWindow { select_idx, } } + + fn sync_settings(&self, state: &mut EmuState) { + state.update_options(self.options); + let cards = self.card_options.iter().map(|(typ, settings_ui)| (*typ, typ.new_card(ron::from_str(&settings_ui.serialize_settings()).unwrap()).unwrap())).collect(); + state.set_cards(cards); + } } impl Window for OptionWindow { @@ -75,11 +81,11 @@ impl Window for OptionWindow { }); let mut delete_idx = None; frame.show(ui, |ui| { - for (i, (name, _settings_ui)) in + for (i, (typ, _settings_ui)) in self.card_options.iter().enumerate() { ui.horizontal(|ui| { - ui.selectable_value(&mut self.select_idx, Some(i), *name); + ui.selectable_value(&mut self.select_idx, Some(i), typ.name()); if ui.button("-").clicked() { delete_idx = Some(i); } @@ -107,7 +113,7 @@ impl Window for OptionWindow { .clicked() { self.card_options.push(( - typ.name(), + typ, typ.settings_ui( ron::from_str(&typ.default_settings()) .unwrap(), @@ -131,10 +137,10 @@ impl Window for OptionWindow { } ui.with_layout(Layout::right_to_left(Align::Min), |ui| { if ui.button("Apply").clicked() { - state.update_options(self.options); + self.sync_settings(state); } if modal.button(ui, "OK").clicked() { - state.update_options(self.options); + self.sync_settings(state); } modal.caution_button(ui, "Cancel"); }); diff --git a/src/ram.rs b/src/ram.rs index d988c52..923dd56 100644 --- a/src/ram.rs +++ b/src/ram.rs @@ -58,6 +58,13 @@ impl Card for RamCard { .unwrap() } + fn get_settings(&self) -> String { + ron::to_string(&RamCardSettings { + size: self.ram.len() as u16, + start_addr: self.start_addr, + }).unwrap() + } + fn settings_ui(settings: ron::Value) -> anyhow::Result where Self: Sized, diff --git a/src/state.rs b/src/state.rs index 318dd6c..e6b0919 100644 --- a/src/state.rs +++ b/src/state.rs @@ -4,6 +4,7 @@ use rand::RngCore; use crate::{ audio::AudioMessage, + card::{Card, Type}, cpu::{MemCycle, Status, I8080}, frontpanel::{switch::SwitchState, ActionSwitch, FrontpanelInteraction, FrontpanelState}, Options, @@ -16,12 +17,21 @@ pub struct EmuState { audio_tx: Sender, options: Options, fp_state: FrontpanelState, + cards: Vec<(&'static Type, Box)>, } impl EmuState { - pub fn new(audio_tx: Sender, options: Options) -> Self { + pub fn new( + audio_tx: Sender, + options: Options, + cards: Vec<(String, String)>, + ) -> Self { let mut mem = [0; 65536]; rand::thread_rng().fill_bytes(&mut mem); + let cards = cards.iter().map(|(typ_name, settings)| { + let typ = Type::get(&typ_name).unwrap(); + (typ, typ.new_card(ron::from_str(&settings).unwrap()).unwrap()) + }).collect(); let mut slf = Self { mem, cpu: I8080::new(), @@ -29,6 +39,7 @@ impl EmuState { audio_tx, options, fp_state: FrontpanelState::default(), + cards, }; slf.apply_options(); slf @@ -191,4 +202,16 @@ impl EmuState { pub fn running(&self) -> bool { self.running } + + pub fn cards(&self) -> &[(&'static Type, Box)] { + self.cards.as_ref() + } + + pub fn set_cards(&mut self, cards: Vec<(&'static Type, Box)>) { + self.cards = cards; + } + + pub fn save_cards(&self) -> Vec<(String, String)> { + self.cards.iter().map(|(typ, card)| (typ.name().to_string(), card.get_settings())).collect() + } }