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
This commit is contained in:
Logan McNaughton
2026-03-31 02:54:14 -06:00
committed by GitHub
parent cc538f942f
commit ad5dc8311a
10 changed files with 187 additions and 222 deletions
+3
View File
@@ -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"]
-2
View File
@@ -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);
}
+1 -5
View File
@@ -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<usize, u16> = HashMap::from([
(0, 0x8000),
+88 -51
View File
@@ -18,62 +18,65 @@ use ui::gui;
struct Args {
game: Option<String>,
#[arg(short, long)]
fullscreen: bool,
fullscreen: Option<bool>,
#[arg(long)]
overclock: Option<bool>,
#[arg(long)]
disable_expansion_pak: Option<bool>,
#[arg(long, value_name = "CHEATS_FILE", hide = true)]
cheats: Option<String>,
#[arg(long, value_name = "NETPLAY_PEER_ADDR", hide = true)]
netplay_peer_addr: Option<String>,
#[arg(long, value_name = "NETPLAY_PLAYER_NUMBER", hide = true)]
netplay_player_number: Option<u8>,
#[arg(long, value_name = "GB_ROM_PATH", hide = true)]
gb_rom: Option<Vec<String>>,
#[arg(long, value_name = "GB_RAM_PATH", hide = true)]
gb_ram: Option<Vec<String>>,
#[arg(
short,
long,
value_name = "PROFILE_NAME",
help = "Create a new input profile (keyboard/gamepad mappings)"
)]
configure_input_profile: Option<String>,
#[arg(long, help = "Use DirectInput when configuring a new input profile")]
use_dinput: Option<bool>,
#[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<i32>,
#[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<String>,
#[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<i32>,
#[arg(
short,
long,
value_name = "PORT",
help = "Valid values: 1-4. To be used alongside --bind-input-profile and --assign-controller"
)]
port: Option<usize>,
#[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<u32>,
}
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(())
}
+11 -13
View File
@@ -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<slint::Weak<gui::AppWindow>>,
}
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,
}
+58 -71
View File
@@ -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<AppWindow>) {
});
}
fn local_game_window(app: &AppWindow, controller_paths: &[Option<String>], usb: ui::Usb) {
fn local_game_window(app: &AppWindow, controller_paths: &[Option<String>]) {
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<NetplayDevice>,
usb: ui::Usb,
weak: slint::Weak<AppWindow>,
) {
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,
);
}
+1 -1
View File
@@ -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;
-4
View File
@@ -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,
);
})
+3 -46
View File
@@ -26,53 +26,12 @@ fn respond_to_handshake(usb_tx: &tokio::sync::broadcast::Sender<UsbData>, data:
}
}
fn upload_rom(
weak: slint::Weak<ui::gui::AppWindow>,
rom: Vec<u8>,
usb_tx: &tokio::sync::broadcast::Sender<UsbData>,
cart_rx: &tokio::sync::broadcast::Receiver<UsbData>,
) {
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<UsbData>,
usb_tx: tokio::sync::broadcast::Sender<UsbData>,
cart_tx: tokio::sync::broadcast::Sender<UsbData>,
cart_rx: tokio::sync::broadcast::Receiver<UsbData>,
weak: slint::Weak<ui::gui::AppWindow>,
) {
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<ui::gui::AppWindow>,
) -> (Option<tokio::sync::watch::Sender<()>>, ui::Usb) {
pub fn init() -> (Option<tokio::sync::watch::Sender<()>>, ui::Usb) {
let (shutdown_tx, mut shutdown_rx) = tokio::sync::watch::channel(());
let (usb_tx, usb_rx): (
tokio::sync::broadcast::Sender<UsbData>,
@@ -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;
}
+22 -29
View File
@@ -1,45 +1,38 @@
use crate::gui;
use slint::ComponentHandle;
pub fn prompt_for_match(
words: &[String],
weak_vru: &slint::Weak<gui::AppWindow>,
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<slint::VecModel<slint::SharedString>> =
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<slint::VecModel<slint::SharedString>> =
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() {