From ad5dc8311a7de457ecf46fd263dcfde68e668778 Mon Sep 17 00:00:00 2001 From: Logan McNaughton <848146+loganmc10@users.noreply.github.com> Date: Tue, 31 Mar 2026 02:54:14 -0600 Subject: [PATCH] remove custom stack size (#698) * remove custom stack size * new command * more * more * more * more * more * more * more * more * more * more * more * more * more * more * more * more * more * more * more * config * more * more --- .cargo/config.toml | 3 + build.rs | 2 - src/device/controller/vru.rs | 6 +- src/main.rs | 139 +++++++++++++++++++++------------ src/ui.rs | 24 +++--- src/ui/gui.rs | 129 ++++++++++++++---------------- src/ui/gui/settings_page.slint | 2 +- src/ui/netplay.rs | 4 - src/ui/usb.rs | 49 +----------- src/ui/vru.rs | 51 ++++++------ 10 files changed, 187 insertions(+), 222 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 9d37267..f214a39 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -4,6 +4,9 @@ rustflags = ["-C", "link-arg=-fuse-ld=lld"] [target.'cfg(any(target_os = "windows", target_os = "macos"))'] linker = "rust-lld" +[target.'cfg(target_os = "windows")'] +rustflags = ["-C", "link-arg=/STACK:8388608"] + [target.'cfg(target_arch = "x86_64")'] rustflags = ["-C", "target-cpu=x86-64-v3"] diff --git a/build.rs b/build.rs index 454e2d5..7307a59 100644 --- a/build.rs +++ b/build.rs @@ -206,6 +206,4 @@ fn main() { let netplay_id = std::env::var("NETPLAY_ID").unwrap_or("gopher64".to_string()); println!("cargo:rustc-env=NETPLAY_ID={netplay_id}"); - - println!("cargo:rustc-env=N64_STACK_SIZE={}", 8 * 1024 * 1024); } diff --git a/src/device/controller/vru.rs b/src/device/controller/vru.rs index 99058ef..68aa98e 100644 --- a/src/device/controller/vru.rs +++ b/src/device/controller/vru.rs @@ -179,11 +179,7 @@ pub fn process(device: &mut device::Device, channel: usize) { device.pif.ram[device.pif.channels[channel].rx_buf.unwrap()] = 0; } JCMD_VRU_READ => { - let index = if let Some(weak) = &device.ui.weak { - ui::vru::prompt_for_match(&device.vru.words, weak, device.vi.frame_time) - } else { - 0x7FFF - }; + let index = ui::vru::prompt_for_match(&device.vru.words, device.vi.frame_time); let num_results = if index == 0x7FFF { 0 } else { 1 }; let data: HashMap = HashMap::from([ (0, 0x8000), diff --git a/src/main.rs b/src/main.rs index ced0156..36b139d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,62 +18,65 @@ use ui::gui; struct Args { game: Option, #[arg(short, long)] - fullscreen: bool, + fullscreen: Option, + #[arg(long)] + overclock: Option, + #[arg(long)] + disable_expansion_pak: Option, + #[arg(long, value_name = "CHEATS_FILE", hide = true)] + cheats: Option, + #[arg(long, value_name = "NETPLAY_PEER_ADDR", hide = true)] + netplay_peer_addr: Option, + #[arg(long, value_name = "NETPLAY_PLAYER_NUMBER", hide = true)] + netplay_player_number: Option, + #[arg(long, value_name = "GB_ROM_PATH", hide = true)] + gb_rom: Option>, + #[arg(long, value_name = "GB_RAM_PATH", hide = true)] + gb_ram: Option>, #[arg( - short, long, value_name = "PROFILE_NAME", help = "Create a new input profile (keyboard/gamepad mappings)" )] configure_input_profile: Option, + #[arg(long, help = "Use DirectInput when configuring a new input profile")] + use_dinput: Option, #[arg( - short, - long, - help = "Use DirectInput when configuring a new input profile" - )] - use_dinput: bool, - #[arg( - short, long, value_name = "DEADZONE_PERCENTAGE", help = "Used along with --configure-input-profile to set the deadzone for analog sticks" )] deadzone: Option, #[arg( - short, long, value_name = "PROFILE_NAME", help = "Must also specify --port. Used to bind a previously created profile to a port" )] bind_input_profile: Option, #[arg( - short, long, help = "Lists connected controllers which can be used in --assign-controller" )] list_controllers: bool, #[arg( - short, long, value_name = "CONTROLLER_NUMBER", help = "Must also specify --port. Used to assign a controller listed in --list-controllers to a port" )] assign_controller: Option, #[arg( - short, long, value_name = "PORT", help = "Valid values: 1-4. To be used alongside --bind-input-profile and --assign-controller" )] port: Option, #[arg( - short = 'z', + short, long, help = "Clear all input profile bindings and controller assignments" )] clear_input_bindings: bool, #[arg( - short = 's', long, value_name = "SLOT", help = "Load savestate from slot 0-9 when starting the game" @@ -81,18 +84,12 @@ struct Args { load_state: Option, } -fn main() -> std::io::Result<()> { - let runtime = tokio::runtime::Builder::new_multi_thread() - .enable_all() - .worker_threads(4) - .thread_name("n64") - .thread_stack_size(env!("N64_STACK_SIZE").parse().unwrap()) - .build() - .unwrap(); - +#[tokio::main] +async fn main() -> std::io::Result<()> { let dirs = ui::get_dirs(); std::fs::create_dir_all(dirs.config_dir)?; + std::fs::create_dir_all(dirs.cache_dir)?; std::fs::create_dir_all(dirs.data_dir.join("saves"))?; std::fs::create_dir_all(dirs.data_dir.join("states"))?; let args = Args::parse(); @@ -111,29 +108,71 @@ fn main() -> std::io::Result<()> { return Err(Error::other("Savestate slot must be between 0 and 9")); } - let handle = runtime.spawn(async move { - let mut device = device::Device::new(); - let overclock = device.ui.config.emulation.overclock; - let disable_expansion_pak = device.ui.config.emulation.disable_expansion_pak; + let mut device = device::Device::new(); + let fullscreen = args.fullscreen.unwrap_or(device.ui.config.video.fullscreen); + let overclock = args + .overclock + .unwrap_or(device.ui.config.emulation.overclock); + let disable_expansion_pak = args + .disable_expansion_pak + .unwrap_or(device.ui.config.emulation.disable_expansion_pak); + let cheats = if let Some(cheats_file) = args.cheats + && let Ok(data) = std::fs::read(cheats_file) + && let Ok(cheats) = serde_json::from_slice(&data) + { + cheats + } else { + let game_crc = ui::storage::get_game_crc(&rom_contents); + ui::config::Cheats::new() + .cheats + .get(&game_crc) + .cloned() + .unwrap_or_default() + }; - let game_cheats = { - let game_crc = ui::storage::get_game_crc(&rom_contents); - let cheats = ui::config::Cheats::new(); - cheats.cheats.get(&game_crc).cloned().unwrap_or_default() - }; - device::run_game( - &mut device, - rom_contents, - ui::gui::GameSettings { - fullscreen: args.fullscreen, - overclock, - disable_expansion_pak, - cheats: game_cheats, - load_savestate_slot: args.load_state, - }, - ); - }); - runtime.block_on(handle).unwrap() + let mut shutdown_tx = None; + + if let Some(peer_addr) = args.netplay_peer_addr + && let Some(player_number) = args.netplay_player_number + { + device.netplay = Some(netplay::init(peer_addr.parse().unwrap(), player_number)); + } else { + if let Some(gb_roms) = args.gb_rom + && let Some(gb_rams) = args.gb_ram + { + for i in 0..4 { + if let Some(gb_rom) = gb_roms.get(i) + && let Some(gb_ram) = gb_rams.get(i) + { + device.transferpaks[i].cart.rom = std::fs::read(gb_rom).unwrap(); + device.transferpaks[i].cart.ram = std::fs::read(gb_ram).unwrap(); + } + } + } + + if device.ui.config.emulation.usb { + (shutdown_tx, device.ui.usb) = ui::usb::init(); + } + } + + device::run_game( + &mut device, + rom_contents, + ui::gui::GameSettings { + fullscreen, + overclock, + disable_expansion_pak, + cheats, + load_savestate_slot: args.load_state, + }, + ); + + if device.netplay.is_some() { + netplay::close(&mut device); + } + if let Some(shutdown_tx) = &shutdown_tx { + ui::usb::close(shutdown_tx); + } } else if std::env::args().count() > 1 { let mut ui = ui::Ui::new(); @@ -157,7 +196,7 @@ fn main() -> std::io::Result<()> { ui::input::configure_input_profile( &mut ui, profile, - args.use_dinput, + args.use_dinput.unwrap_or(false), args.deadzone.unwrap_or(ui::input::DEADZONE_DEFAULT), ); return Ok(()); @@ -175,10 +214,8 @@ fn main() -> std::io::Result<()> { ui::input::bind_input_profile(&mut ui, profile, port); } } else { - runtime.block_on(async { - gui::app_window(); - }); + gui::app_window(); } - runtime.shutdown_timeout(std::time::Duration::from_secs(1)); + Ok(()) } diff --git a/src/ui.rs b/src/ui.rs index 57721c8..19ad343 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -12,6 +12,7 @@ pub mod vru; pub struct Dirs { pub config_dir: std::path::PathBuf, pub data_dir: std::path::PathBuf, + pub cache_dir: std::path::PathBuf, } pub struct Audio { @@ -53,7 +54,6 @@ pub struct Ui { pub storage: Storage, pub video: Video, pub usb: Usb, - pub weak: Option>, } impl Drop for Ui { @@ -94,19 +94,18 @@ pub fn get_dirs() -> Dirs { let exe_path = std::env::current_exe().unwrap(); let portable_dir = exe_path.parent(); let portable = portable_dir.unwrap().join("portable.txt").exists(); - let config_dir; - let data_dir; if portable { - config_dir = portable_dir.unwrap().join("portable_data").join("config"); - data_dir = portable_dir.unwrap().join("portable_data").join("data"); + Dirs { + config_dir: portable_dir.unwrap().join("portable_data").join("config"), + data_dir: portable_dir.unwrap().join("portable_data").join("data"), + cache_dir: portable_dir.unwrap().join("portable_data").join("cache"), + } } else { - config_dir = dirs::config_dir().unwrap().join("gopher64"); - data_dir = dirs::data_dir().unwrap().join("gopher64"); - }; - - Dirs { - config_dir, - data_dir, + Dirs { + config_dir: dirs::config_dir().unwrap().join("gopher64"), + data_dir: dirs::data_dir().unwrap().join("gopher64"), + cache_dir: dirs::cache_dir().unwrap().join("gopher64"), + } } } @@ -196,7 +195,6 @@ impl Ui { usb_tx: None, cart_rx: None, }, - weak: None, dirs, with_sdl, } diff --git a/src/ui/gui.rs b/src/ui/gui.rs index 3358dce..acdf369 100644 --- a/src/ui/gui.rs +++ b/src/ui/gui.rs @@ -1,5 +1,3 @@ -use crate::device; -use crate::netplay; use crate::ui; use slint::Model; @@ -60,23 +58,15 @@ fn check_latest_version(weak: slint::Weak) { }); } -fn local_game_window(app: &AppWindow, controller_paths: &[Option], usb: ui::Usb) { +fn local_game_window(app: &AppWindow, controller_paths: &[Option]) { let dirs = ui::get_dirs(); let weak = app.as_weak(); let controller_paths = controller_paths.to_owned(); app.on_open_rom_button_clicked(move || { let controller_paths = controller_paths.clone(); - let mut usb_tx = None; - if let Some(usb_tx_inner) = usb.usb_tx.as_ref() { - usb_tx = Some(usb_tx_inner.clone()); - } - let mut cart_rx = None; - if let Some(cart_rx_inner) = usb.cart_rx.as_ref() { - cart_rx = Some(cart_rx_inner.resubscribe()); - } weak.upgrade_in_event_loop(move |handle| { save_settings(&handle, &controller_paths); - open_rom(&handle, ui::Usb { usb_tx, cart_rx }) + open_rom(&handle) }) .unwrap(); }); @@ -222,18 +212,25 @@ fn controller_window( weak_dialog .upgrade_in_event_loop(move |handle| { handle.hide().unwrap(); - let profile_name = handle.get_profile_name().into(); + let profile_name = handle.get_profile_name(); let dinput = handle.get_dinput(); let deadzone = handle.get_deadzone(); tokio::spawn(async move { - let mut game_ui = ui::Ui::new(); - ui::input::configure_input_profile( - &mut game_ui, - profile_name, - dinput, - deadzone, - ); + std::process::Command::new(std::env::current_exe().unwrap()) + .args([ + "--configure-input-profile", + &profile_name, + "--use-dinput", + &dinput.to_string(), + "--deadzone", + &deadzone.to_string(), + ]) + .spawn() + .unwrap() + .wait() + .unwrap(); + let game_ui = ui::Ui::new(); update_input_profiles(&weak_app, &game_ui.config); }); }) @@ -304,11 +301,6 @@ fn about_window(app: &AppWindow) { pub fn app_window() { let app = AppWindow::new().unwrap(); about_window(&app); - let mut usb = ui::Usb { - usb_tx: None, - cart_rx: None, - }; - let mut shutdown_tx = None; let mut controller_paths; { let game_ui = ui::Ui::new(); @@ -318,76 +310,72 @@ pub fn app_window() { controller_paths.insert(0, None); settings_window(&app, &game_ui.config); controller_window(&app, &game_ui.config, &controller_names, &controller_paths); - - if game_ui.config.emulation.usb { - (shutdown_tx, usb) = ui::usb::init(app.as_weak()); - } } - local_game_window(&app, &controller_paths, usb); + local_game_window(&app, &controller_paths); ui::netplay::netplay_window(&app, &controller_paths); ui::cheats::cheats_window(&app); app.run().unwrap(); - if let Some(shutdown_tx) = &shutdown_tx { - ui::usb::close(shutdown_tx); - } save_settings(&app, &controller_paths); } pub fn run_rom( gb_paths: GbPaths, file_path: std::path::PathBuf, - mut game_settings: GameSettings, + game_settings: GameSettings, netplay: Option, - usb: ui::Usb, weak: slint::Weak, ) { tokio::spawn(async move { weak.upgrade_in_event_loop(move |handle| handle.set_game_running(true)) .unwrap(); - let mut device = device::Device::new(); - device.ui.config.rom_dir = file_path.parent().unwrap().to_path_buf(); + let mut command = std::process::Command::new(std::env::current_exe().unwrap()); + command.args([ + "--fullscreen", + &game_settings.fullscreen.to_string(), + "--overclock", + &game_settings.overclock.to_string(), + "--disable-expansion-pak", + &game_settings.disable_expansion_pak.to_string(), + ]); + let cheats_path = ui::get_dirs().cache_dir.join("cheats.json"); + if let Some(netplay_device) = netplay { + let f = std::fs::File::create(cheats_path.to_str().unwrap()).unwrap(); + serde_json::to_writer_pretty(f, &game_settings.cheats).unwrap(); + + command.args([ + "--netplay-peer-addr", + &netplay_device.peer_addr.to_string(), + "--netplay-player-number", + &netplay_device.player_number.to_string(), + "--cheats", + cheats_path.to_str().unwrap(), + ]); + } for i in 0..4 { if gb_paths.rom[i].is_some() && gb_paths.ram[i].is_some() { - device.transferpaks[i].cart.rom = - std::fs::read(gb_paths.rom[i].as_ref().unwrap()).unwrap(); - - device.transferpaks[i].cart.ram = - std::fs::read(gb_paths.ram[i].as_ref().unwrap()).unwrap(); + command.args([ + "--gb-rom", + gb_paths.rom[i].as_ref().unwrap().to_str().unwrap(), + "--gb-ram", + gb_paths.ram[i].as_ref().unwrap().to_str().unwrap(), + ]); } } - device.ui.usb.usb_tx = usb.usb_tx; - device.ui.usb.cart_rx = usb.cart_rx; + command + .arg(file_path.to_str().unwrap()) + .spawn() + .unwrap() + .wait() + .unwrap(); - device.ui.weak = Some(weak.clone()); + let _ = std::fs::remove_file(cheats_path.to_str().unwrap()); - if let Some(rom_contents) = device::get_rom_contents(file_path.as_path()) { - if let Some(netplay_device) = netplay { - device.netplay = Some(netplay::init( - netplay_device.peer_addr, - netplay_device.player_number, - )); - } else { - game_settings.cheats = { - let game_crc = ui::storage::get_game_crc(&rom_contents); - let cheats = ui::config::Cheats::new(); - cheats.cheats.get(&game_crc).cloned().unwrap_or_default() - }; - } - device::run_game(&mut device, rom_contents, game_settings); - if device.netplay.is_some() { - netplay::close(&mut device); - } - } else { - println!("Could not read rom file"); - } - - let rom_dir = device.ui.config.rom_dir.clone(); weak.upgrade_in_event_loop(move |handle| { - if let Some(rom_dir_str) = rom_dir.to_str() { - handle.set_rom_dir(rom_dir_str.into()); + if let Some(rom_dir) = file_path.parent().unwrap().to_str() { + handle.set_rom_dir(rom_dir.into()); } handle.set_game_running(false); }) @@ -395,7 +383,7 @@ pub fn run_rom( }); } -fn open_rom(app: &AppWindow, usb: ui::Usb) { +fn open_rom(app: &AppWindow) { let rom_dir = app.get_rom_dir(); let select_rom = if !rom_dir.is_empty() && let Ok(exists) = std::fs::exists(&rom_dir) @@ -462,7 +450,6 @@ fn open_rom(app: &AppWindow, usb: ui::Usb) { load_savestate_slot: None, }, None, - usb, weak, ); } diff --git a/src/ui/gui/settings_page.slint b/src/ui/gui/settings_page.slint index 4a7b401..d8c0617 100644 --- a/src/ui/gui/settings_page.slint +++ b/src/ui/gui/settings_page.slint @@ -110,7 +110,7 @@ export component Settings inherits Page { HorizontalBox { alignment: start; CheckBox { - text: @tr("Emulate UNFLoader (reload emulator after changing)"); + text: @tr("Emulate UNFLoader"); checked: SettingsData.emulate_usb; toggled => { SettingsData.emulate_usb = self.checked; diff --git a/src/ui/netplay.rs b/src/ui/netplay.rs index 60bdf90..a09a7e4 100644 --- a/src/ui/netplay.rs +++ b/src/ui/netplay.rs @@ -1112,10 +1112,6 @@ fn setup_wait_window( peer_addr: socket_addr, player_number: player_number as u8, }), - ui::Usb { - usb_tx: None, - cart_rx: None, - }, weak_app, ); }) diff --git a/src/ui/usb.rs b/src/ui/usb.rs index ebeffae..97d71e6 100644 --- a/src/ui/usb.rs +++ b/src/ui/usb.rs @@ -26,53 +26,12 @@ fn respond_to_handshake(usb_tx: &tokio::sync::broadcast::Sender, data: } } -fn upload_rom( - weak: slint::Weak, - rom: Vec, - usb_tx: &tokio::sync::broadcast::Sender, - cart_rx: &tokio::sync::broadcast::Receiver, -) { - let weak_clone = weak.clone(); - let usb_tx = usb_tx.clone(); - let cart_rx = cart_rx.resubscribe(); - - weak.upgrade_in_event_loop(move |handle| { - let dir = std::env::temp_dir(); - let rom_path = dir.join("rom_upload.bin"); - std::fs::write(rom_path.clone(), rom).unwrap(); - - ui::gui::run_rom( - ui::gui::GbPaths { - rom: [None, None, None, None], - ram: [None, None, None, None], - }, - rom_path, - ui::gui::GameSettings { - fullscreen: handle.get_fullscreen(), - overclock: handle.get_overclock_n64_cpu(), - disable_expansion_pak: handle.get_disable_expansion_pak(), - cheats: std::collections::HashMap::new(), // will be filled in later - load_savestate_slot: None, - }, - None, - ui::Usb { - usb_tx: Some(usb_tx), - cart_rx: Some(cart_rx), - }, - weak_clone, - ); - }) - .unwrap(); -} - async fn handle_connection( conn: tokio::net::TcpStream, mut shutdown_rx: tokio::sync::watch::Receiver<()>, mut usb_rx: tokio::sync::broadcast::Receiver, usb_tx: tokio::sync::broadcast::Sender, cart_tx: tokio::sync::broadcast::Sender, - cart_rx: tokio::sync::broadcast::Receiver, - weak: slint::Weak, ) { let (mut incoming, mut outgoing) = conn.into_split(); @@ -147,7 +106,7 @@ async fn handle_connection( if usb_data.data_type == DATATYPE_TCPTEST { respond_to_handshake(&usb_tx,usb_data.data); } else if usb_data.data_type == DATATYPE_ROMUPLOAD { - upload_rom(weak.clone(), usb_data.data,&usb_tx,&cart_rx); + panic!("ROM upload not supported"); } else { cart_tx.send(usb_data).unwrap(); } @@ -168,9 +127,7 @@ async fn handle_connection( } } -pub fn init( - weak: slint::Weak, -) -> (Option>, ui::Usb) { +pub fn init() -> (Option>, ui::Usb) { let (shutdown_tx, mut shutdown_rx) = tokio::sync::watch::channel(()); let (usb_tx, usb_rx): ( tokio::sync::broadcast::Sender, @@ -192,7 +149,7 @@ pub fn init( tokio::select! { res = listener.accept() => { if let Ok((c,_)) = res { - handle_connection(c,shutdown_rx.clone(),usb_rx.resubscribe(),usb_tx.clone(),cart_tx.clone(),cart_rx.resubscribe(),weak.clone()).await; + handle_connection(c,shutdown_rx.clone(),usb_rx.resubscribe(),usb_tx.clone(),cart_tx.clone()).await; } else { break; } diff --git a/src/ui/vru.rs b/src/ui/vru.rs index 6615450..d5c14ea 100644 --- a/src/ui/vru.rs +++ b/src/ui/vru.rs @@ -1,45 +1,38 @@ use crate::gui; use slint::ComponentHandle; -pub fn prompt_for_match( - words: &[String], - weak_vru: &slint::Weak, - frame_time: f64, -) -> u16 { +pub fn prompt_for_match(words: &[String], frame_time: f64) -> u16 { let mut dedup_words = words.to_owned(); dedup_words.sort(); dedup_words.dedup(); let (tx, mut rx) = tokio::sync::mpsc::channel(1); + tokio::spawn(async move { + let vru_dialog = gui::VruDialog::new().unwrap(); + let vru_dialog_weak = vru_dialog.as_weak(); - weak_vru - .upgrade_in_event_loop(move |_handle| { - let vru_dialog = gui::VruDialog::new().unwrap(); - let vru_dialog_weak = vru_dialog.as_weak(); + let tx_clicked = tx.clone(); - let tx_clicked = tx.clone(); + vru_dialog.on_vru_button_clicked(move |chosen_word| { + tx_clicked.try_send(chosen_word.to_string()).unwrap(); + vru_dialog_weak.unwrap().window().hide().unwrap(); + }); - vru_dialog.on_vru_button_clicked(move |chosen_word| { - tx_clicked.try_send(chosen_word.to_string()).unwrap(); - vru_dialog_weak.unwrap().window().hide().unwrap(); - }); + vru_dialog.window().on_close_requested(move || { + tx.try_send("".to_string()).unwrap(); + slint::CloseRequestResponse::HideWindow + }); - vru_dialog.window().on_close_requested(move || { - tx.try_send("".to_string()).unwrap(); - slint::CloseRequestResponse::HideWindow - }); + let words_vec = slint::VecModel::default(); + for word in dedup_words { + words_vec.push(word.into()); + } + let words_model: std::rc::Rc> = + std::rc::Rc::new(words_vec); + vru_dialog.set_words(slint::ModelRc::from(words_model)); - let words_vec = slint::VecModel::default(); - for word in dedup_words { - words_vec.push(word.into()); - } - let words_model: std::rc::Rc> = - std::rc::Rc::new(words_vec); - vru_dialog.set_words(slint::ModelRc::from(words_model)); - - vru_dialog.show().unwrap(); - }) - .unwrap(); + vru_dialog.run().unwrap(); + }); let mut result = rx.try_recv(); while result.is_err() {