diff --git a/src/device/controller.rs b/src/device/controller.rs index 5298f79..1d500cd 100644 --- a/src/device/controller.rs +++ b/src/device/controller.rs @@ -171,7 +171,14 @@ pub fn pak_switch_event(device: &mut device::Device) { let new_pak_type = match channel.change_pak { PakType::MemPak => PakType::RumblePak, - PakType::RumblePak => PakType::MemPak, + PakType::RumblePak => { + if device.transferpaks[i].cart.rom.is_empty() { + PakType::MemPak + } else { + PakType::TransferPak + } + } + PakType::TransferPak => PakType::MemPak, _ => { panic!("Invalid pak type"); } diff --git a/src/device/controller/gbcart.rs b/src/device/controller/gbcart.rs index e4bfc99..c342c62 100644 --- a/src/device/controller/gbcart.rs +++ b/src/device/controller/gbcart.rs @@ -102,8 +102,12 @@ fn write_mbc5( let value = pif_ram[data + size - 1]; if address < 0x2000 { cart.ram_enabled = value & 0xf == 0xa; + } else if address < 0x3000 { + cart.rom_bank &= 0xff00; + cart.rom_bank |= value as u16; } else if address < 0x4000 { - println!("Unknown MBC5 write address {:x}", address); + cart.rom_bank &= 0x00ff; + cart.rom_bank |= ((value & 0x1) as u16) << 8; } else if address < 0x6000 { cart.ram_bank = (value & 0xf) as u16; } else if address < 0xa000 { diff --git a/src/device/pif.rs b/src/device/pif.rs index 4fe7bf1..e109d76 100644 --- a/src/device/pif.rs +++ b/src/device/pif.rs @@ -245,21 +245,30 @@ pub fn init(device: &mut device::Device) { device.pif.ram[0x26] = device.cart.cic_seed; device.pif.ram[0x27] = device.cart.cic_seed; - let default_handler = device::controller::PakHandler { + let mempak_handler = device::controller::PakHandler { read: device::controller::mempak::read, write: device::controller::mempak::write, pak_type: device::controller::PakType::MemPak, }; + let tpak_handler = device::controller::PakHandler { + read: device::controller::transferpak::read, + write: device::controller::transferpak::write, + pak_type: device::controller::PakType::TransferPak, + }; connect_pif_channels(device); for i in 0..4 { if device.netplay.is_none() { if device.ui.config.input.controller_enabled[i] { - device.pif.channels[i].pak_handler = Some(default_handler); + if device.transferpaks[i].cart.rom.is_empty() { + device.pif.channels[i].pak_handler = Some(mempak_handler); + } else { + device.pif.channels[i].pak_handler = Some(tpak_handler); + } } } else if device.netplay.as_ref().unwrap().player_data[i].reg_id != 0 { - device.pif.channels[i].pak_handler = Some(default_handler); + device.pif.channels[i].pak_handler = Some(mempak_handler); } } if device.ui.config.input.emulate_vru { diff --git a/src/savestates.rs b/src/savestates.rs index 3f9215c..a33385d 100644 --- a/src/savestates.rs +++ b/src/savestates.rs @@ -175,6 +175,15 @@ pub fn load_savestate(device: &mut device::Device) { pak_type: device::controller::PakType::MemPak, }; device.pif.channels[i].pak_handler = Some(mempak_handler); + } else if device.pif.channels[i].pak_handler.unwrap().pak_type + == device::controller::PakType::TransferPak + { + let tpak_handler = device::controller::PakHandler { + read: device::controller::transferpak::read, + write: device::controller::transferpak::write, + pak_type: device::controller::PakType::TransferPak, + }; + device.pif.channels[i].pak_handler = Some(tpak_handler); } } } diff --git a/src/ui/config.rs b/src/ui/config.rs index 09f72d1..9c43789 100644 --- a/src/ui/config.rs +++ b/src/ui/config.rs @@ -37,6 +37,7 @@ pub struct Input { pub input_profile_binding: [String; 4], pub controller_assignment: [Option; 4], pub controller_enabled: [bool; 4], + pub transfer_pak: [bool; 4], pub emulate_vru: bool, } #[derive(serde::Serialize, serde::Deserialize)] @@ -88,6 +89,7 @@ impl Config { controller_assignment: [None, None, None, None], input_profiles, controller_enabled: [true, false, false, false], + transfer_pak: [false, false, false, false], emulate_vru: false, }, video: Video { diff --git a/src/ui/gui.rs b/src/ui/gui.rs index f5eaec5..7d1fbbe 100644 --- a/src/ui/gui.rs +++ b/src/ui/gui.rs @@ -12,6 +12,7 @@ pub struct GopherEguiApp { selected_profile: [String; 4], input_profiles: Vec, controller_enabled: [bool; 4], + transfer_pak: [bool; 4], controller_paths: Vec, upscale: u32, integer_scaling: bool, @@ -36,6 +37,7 @@ struct SaveConfig { selected_controller: [i32; 4], selected_profile: [String; 4], controller_enabled: [bool; 4], + transfer_pak: [bool; 4], upscale: u32, integer_scaling: bool, fullscreen: bool, @@ -110,6 +112,7 @@ impl GopherEguiApp { controller_names, input_profiles: get_input_profiles(&config), controller_enabled: config.input.controller_enabled, + transfer_pak: config.input.transfer_pak, upscale: config.video.upscale, integer_scaling: config.video.integer_scaling, fullscreen: config.video.fullscreen, @@ -144,6 +147,7 @@ fn save_config( config.input.input_profile_binding = save_config_items.selected_profile; config.input.controller_enabled = save_config_items.controller_enabled; + config.input.transfer_pak = save_config_items.transfer_pak; config.video.upscale = save_config_items.upscale; config.video.integer_scaling = save_config_items.integer_scaling; @@ -157,6 +161,7 @@ impl Drop for GopherEguiApp { selected_controller: self.selected_controller, selected_profile: self.selected_profile.clone(), controller_enabled: self.controller_enabled, + transfer_pak: self.transfer_pak, upscale: self.upscale, integer_scaling: self.integer_scaling, fullscreen: self.fullscreen, @@ -291,6 +296,7 @@ pub fn open_rom(app: &mut GopherEguiApp, ctx: &egui::Context) { let selected_controller = app.selected_controller; let selected_profile = app.selected_profile.clone(); let controller_enabled = app.controller_enabled; + let transfer_pak = app.transfer_pak; let upscale = app.upscale; let integer_scaling = app.integer_scaling; let fullscreen = app.fullscreen; @@ -334,12 +340,47 @@ pub fn open_rom(app: &mut GopherEguiApp, ctx: &egui::Context) { let rom_contents = app.netplay.rom_contents.clone(); let gui_ctx = ctx.clone(); - let mut task = None; + let mut select_rom = None; + let mut select_gb_rom = [None, None, None, None]; + let mut select_gb_ram = [None, None, None, None]; + if !netplay { - task = Some(rfd::AsyncFileDialog::new().pick_file()); + select_rom = Some( + rfd::AsyncFileDialog::new() + .set_title("Select ROM") + .pick_file(), + ); + for i in 0..4 { + if transfer_pak[i] { + select_gb_rom[i] = Some( + rfd::AsyncFileDialog::new() + .set_title(format!("GB ROM P{}", i + 1)) + .pick_file(), + ); + select_gb_ram[i] = Some( + rfd::AsyncFileDialog::new() + .set_title(format!("GB RAM P{}", i + 1)) + .pick_file(), + ); + } + } } tokio::spawn(async move { - let file = if !netplay { task.unwrap().await } else { None }; + let file = if !netplay { + select_rom.unwrap().await + } else { + None + }; + let mut gb_rom_path = [None, None, None, None]; + let mut gb_ram_path = [None, None, None, None]; + if !netplay { + for i in 0..4 { + if transfer_pak[i] { + gb_rom_path[i] = select_gb_rom[i].as_mut().unwrap().await; + gb_ram_path[i] = select_gb_ram[i].as_mut().unwrap().await; + } + } + } std::thread::Builder::new() .name("n64".to_string()) @@ -349,6 +390,7 @@ pub fn open_rom(app: &mut GopherEguiApp, ctx: &egui::Context) { selected_controller, selected_profile, controller_enabled, + transfer_pak, upscale, integer_scaling, fullscreen, @@ -379,6 +421,16 @@ pub fn open_rom(app: &mut GopherEguiApp, ctx: &egui::Context) { device::run_game(rom_contents, &mut device); netplay::close(&mut device); } else { + for i in 0..4 { + if gb_rom_path[i].is_some() && gb_ram_path[i].is_some() { + device.transferpaks[i].cart.rom = + std::fs::read(gb_rom_path[i].as_ref().unwrap().path()).unwrap(); + + device.transferpaks[i].cart.ram = + std::fs::read(gb_ram_path[i].as_ref().unwrap().path()).unwrap(); + } + } + if emulate_vru { device.vru_window.window_notifier = Some(vru_window_notifier); device.vru_window.word_receiver = Some(vru_word_receiver); @@ -462,7 +514,7 @@ impl eframe::App for GopherEguiApp { ui.label("Port"); ui.label("Enabled"); ui.label("Emulate VRU"); - //ui.label("Transfer Pak"); + ui.label("Transfer Pak"); ui.label("Profile"); ui.label("Controller"); ui.end_row(); @@ -479,12 +531,11 @@ impl eframe::App for GopherEguiApp { ui.add_enabled(true, egui::Checkbox::new(&mut self.emulate_vru, "")); } }); - /* - let mut tpak: bool = false; + ui.centered_and_justified(|ui| { - ui.checkbox(&mut tpak, ""); + ui.checkbox(&mut self.transfer_pak[i], ""); }); - */ + egui::ComboBox::from_id_salt(format!("profile-combo-{}", i)) .selected_text(self.selected_profile[i].clone()) .show_ui(ui, |ui| { diff --git a/src/ui/gui/gui_netplay.rs b/src/ui/gui/gui_netplay.rs index 6f5e4ad..0c20a80 100644 --- a/src/ui/gui/gui_netplay.rs +++ b/src/ui/gui/gui_netplay.rs @@ -207,7 +207,9 @@ pub fn netplay_create(app: &mut GopherEguiApp, ctx: &egui::Context) { app.netplay.game_info_receiver = Some(rx); let gui_ctx = ctx.clone(); app.netplay.create_rom_label = "Inspecting ROM".to_string(); - let task = rfd::AsyncFileDialog::new().pick_file(); + let task = rfd::AsyncFileDialog::new() + .set_title("Select ROM") + .pick_file(); tokio::spawn(async move { let file = task.await; @@ -621,7 +623,9 @@ pub fn netplay_join(app: &mut GopherEguiApp, ctx: &egui::Context) { app.netplay.game_info_receiver = Some(rx); let gui_ctx = ctx.clone(); app.netplay.join_rom_label = "Inspecting ROM".to_string(); - let task = rfd::AsyncFileDialog::new().pick_file(); + let task = rfd::AsyncFileDialog::new() + .set_title("Select ROM") + .pick_file(); tokio::spawn(async move { let file = task.await;