diff --git a/src/emulator/gba/mod.rs b/src/emulator/gba/mod.rs index 7a594b1..7cfe868 100644 --- a/src/emulator/gba/mod.rs +++ b/src/emulator/gba/mod.rs @@ -1,5 +1,12 @@ //! Support for attaching to Nintendo Gameboy Advance emulators. +use core::{ + cell::Cell, + future::Future, + pin::Pin, + task::{Context, Poll}, +}; + use crate::{Address, Error, Process}; use bytemuck::CheckedBitPattern; @@ -15,9 +22,9 @@ pub struct Emulator { /// The attached emulator process process: Process, /// An enum stating which emulator is currently attached - state: State, + state: Cell, /// The memory address of the emulated RAM - ram_base: Option<[Address; 2]>, // [ewram, iwram] + ram_base: Cell>, // [ewram, iwram] } impl Emulator { @@ -40,8 +47,8 @@ impl Emulator { Some(Self { process, - state, - ram_base: None, + state: Cell::new(state), + ram_base: Cell::new(None), }) } @@ -51,13 +58,24 @@ impl Emulator { self.process.is_open() } + /// Executes a future until the emulator process closes. + pub const fn until_closes(&self, future: F) -> UntilEmulatorCloses<'_, F> { + UntilEmulatorCloses { + emulator: self, + future, + } + } + /// Calls the internal routines needed in order to find (and update, if /// needed) the address of the emulated RAM. /// /// Returns true if successful, false otherwise. - pub fn update(&mut self) -> bool { - if self.ram_base.is_none() { - self.ram_base = match match &mut self.state { + pub fn update(&self) -> bool { + let mut ram_base = self.ram_base.get(); + let mut state = self.state.get(); + + if ram_base.is_none() { + ram_base = match match &mut state { State::VisualBoyAdvance(x) => x.find_ram(&self.process), State::Mgba(x) => x.find_ram(&self.process), State::NoCashGba(x) => x.find_ram(&self.process), @@ -70,21 +88,23 @@ impl Emulator { }; } - let success = match &self.state { - State::VisualBoyAdvance(x) => x.keep_alive(&self.process, &mut self.ram_base), - State::Mgba(x) => x.keep_alive(&self.process, &self.ram_base), - State::NoCashGba(x) => x.keep_alive(&self.process, &mut self.ram_base), + let success = match &state { + State::VisualBoyAdvance(x) => x.keep_alive(&self.process, &mut ram_base), + State::Mgba(x) => x.keep_alive(&self.process, &ram_base), + State::NoCashGba(x) => x.keep_alive(&self.process, &mut ram_base), State::Retroarch(x) => x.keep_alive(&self.process), - State::EmuHawk(x) => x.keep_alive(&self.process, &self.ram_base), - State::Mednafen(x) => x.keep_alive(&self.process, &mut self.ram_base), + State::EmuHawk(x) => x.keep_alive(&self.process, &ram_base), + State::Mednafen(x) => x.keep_alive(&self.process, &mut ram_base), }; - match success { - true => true, - false => { - self.ram_base = None; - false - } + self.state.set(state); + + if success { + self.ram_base.set(ram_base); + true + } else { + self.ram_base.set(None); + false } } @@ -120,9 +140,7 @@ impl Emulator { return Err(Error {}); } - let Some([ewram, _]) = self.ram_base else { - return Err(Error {}); - }; + let [ewram, _] = self.ram_base.get().ok_or(Error {})?; let end_offset = offset.checked_sub(0x02000000).unwrap_or(offset); self.process.read(ewram + end_offset) @@ -144,15 +162,32 @@ impl Emulator { return Err(Error {}); } - let Some([_, iwram]) = self.ram_base else { - return Err(Error {}); - }; + let [_, iwram] = self.ram_base.get().ok_or(Error {})?; let end_offset = offset.checked_sub(0x03000000).unwrap_or(offset); self.process.read(iwram + end_offset) } } +/// A future that executes a future until the emulator closes. +pub struct UntilEmulatorCloses<'a, F> { + emulator: &'a Emulator, + future: F, +} + +impl> Future for UntilEmulatorCloses<'_, F> { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if !self.emulator.is_open() { + return Poll::Ready(()); + } + self.emulator.update(); + // SAFETY: We are simply projecting the Pin. + unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().future).poll(cx) } + } +} + #[doc(hidden)] #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum State { diff --git a/src/emulator/gcn/mod.rs b/src/emulator/gcn/mod.rs index 9920d89..783205c 100644 --- a/src/emulator/gcn/mod.rs +++ b/src/emulator/gcn/mod.rs @@ -1,5 +1,12 @@ //! Support for attaching to Nintendo Gamecube emulators. +use core::{ + cell::Cell, + future::Future, + pin::Pin, + task::{Context, Poll}, +}; + use crate::{Address, Endian, Error, FromEndian, Process}; use bytemuck::CheckedBitPattern; @@ -11,11 +18,11 @@ pub struct Emulator { /// The attached emulator process process: Process, /// An enum stating which emulator is currently attached - state: State, + state: Cell, /// The memory address of the emulated RAM - mem1_base: Option
, + mem1_base: Cell>, /// The endianness used by the emulator process - endian: Endian, + endian: Cell, } impl Emulator { @@ -33,9 +40,9 @@ impl Emulator { Some(Self { process, - state, - mem1_base: None, - endian: Endian::Big, // Endianness is usually Big across all GCN emulators + state: Cell::new(state), + mem1_base: Cell::new(None), + endian: Cell::new(Endian::Big), // Endianness is usually Big across all GCN emulators }) } @@ -45,32 +52,48 @@ impl Emulator { self.process.is_open() } + /// Executes a future until the emulator process closes. + pub const fn until_closes(&self, future: F) -> UntilEmulatorCloses<'_, F> { + UntilEmulatorCloses { + emulator: self, + future, + } + } + /// Calls the internal routines needed in order to find (and update, if /// needed) the address of the emulated RAM. /// /// Returns true if successful, false otherwise. - pub fn update(&mut self) -> bool { - if self.mem1_base.is_none() { - let mem1_base = match &mut self.state { - State::Dolphin(x) => x.find_ram(&self.process, &mut self.endian), - State::Retroarch(x) => x.find_ram(&self.process, &mut self.endian), + pub fn update(&self) -> bool { + let mut mem1_base = self.mem1_base.get(); + let mut state = self.state.get(); + let mut endian = self.endian.get(); + + if mem1_base.is_none() { + mem1_base = match match &mut state { + State::Dolphin(x) => x.find_ram(&self.process, &mut endian), + State::Retroarch(x) => x.find_ram(&self.process, &mut endian), + } { + None => return false, + something => something, }; - if mem1_base.is_none() { - return false; - } - self.mem1_base = mem1_base; } - let success = match &self.state { - State::Dolphin(x) => x.keep_alive(&self.process, &self.mem1_base), - State::Retroarch(x) => x.keep_alive(&self.process, &self.mem1_base), + let success = match &state { + State::Dolphin(x) => x.keep_alive(&self.process, &mem1_base), + State::Retroarch(x) => x.keep_alive(&self.process, &mem1_base), }; - if !success { - self.mem1_base = None; - } + self.state.set(state); + self.endian.set(endian); - success + if success { + self.mem1_base.set(mem1_base); + true + } else { + self.mem1_base.set(None); + false + } } /// Reads raw data from the emulated RAM ignoring all endianness settings. @@ -92,7 +115,7 @@ impl Emulator { return Err(Error {}); } - let mem1 = self.mem1_base.ok_or(Error {})?; + let mem1 = self.mem1_base.get().ok_or(Error {})?; let end_offset = offset.checked_sub(0x80000000).unwrap_or(offset); self.process.read(mem1 + end_offset) @@ -112,7 +135,26 @@ impl Emulator { pub fn read(&self, offset: u32) -> Result { Ok(self .read_ignoring_endianness::(offset)? - .from_endian(self.endian)) + .from_endian(self.endian.get())) + } +} + +/// A future that executes a future until the emulator closes. +pub struct UntilEmulatorCloses<'a, F> { + emulator: &'a Emulator, + future: F, +} + +impl> Future for UntilEmulatorCloses<'_, F> { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if !self.emulator.is_open() { + return Poll::Ready(()); + } + self.emulator.update(); + // SAFETY: We are simply projecting the Pin. + unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().future).poll(cx) } } } diff --git a/src/emulator/genesis/mod.rs b/src/emulator/genesis/mod.rs index a9cc85f..c96d69b 100644 --- a/src/emulator/genesis/mod.rs +++ b/src/emulator/genesis/mod.rs @@ -1,6 +1,12 @@ //! Support for attaching to SEGA Genesis emulators. -use core::mem; +use core::{ + cell::Cell, + future::Future, + mem, + pin::Pin, + task::{Context, Poll}, +}; use crate::{Address, Endian, Error, FromEndian, Process}; use bytemuck::CheckedBitPattern; @@ -16,11 +22,11 @@ pub struct Emulator { /// The attached emulator process process: Process, /// An enum stating which emulator is currently attached - state: State, + state: Cell, /// The memory address of the emulated RAM - wram_base: Option
, + wram_base: Cell>, /// The endianness used by the emulator process - endian: Endian, + endian: Cell, } impl Emulator { @@ -41,9 +47,9 @@ impl Emulator { Some(Self { process, - state, - wram_base: None, - endian: Endian::Little, // Endianness is supposed to be Little, until stated otherwise in the code + state: Cell::new(state), + wram_base: Cell::new(None), + endian: Cell::new(Endian::Little), // Endianness is supposed to be Little, until stated otherwise in the code }) } @@ -53,36 +59,52 @@ impl Emulator { self.process.is_open() } + /// Executes a future until the emulator process closes. + pub const fn until_closes(&self, future: F) -> UntilEmulatorCloses<'_, F> { + UntilEmulatorCloses { + emulator: self, + future, + } + } + /// Calls the internal routines needed in order to find (and update, if /// needed) the address of the emulated RAM. /// /// Returns true if successful, false otherwise. - pub fn update(&mut self) -> bool { - if self.wram_base.is_none() { - self.wram_base = match match &mut self.state { - State::Retroarch(x) => x.find_wram(&self.process, &mut self.endian), - State::SegaClassics(x) => x.find_wram(&self.process, &mut self.endian), - State::Fusion(x) => x.find_wram(&self.process, &mut self.endian), - State::Gens(x) => x.find_wram(&self.process, &mut self.endian), - State::BlastEm(x) => x.find_wram(&self.process, &mut self.endian), + pub fn update(&self) -> bool { + let mut wram_base = self.wram_base.get(); + let mut endian = self.endian.get(); + let mut state = self.state.get(); + + if wram_base.is_none() { + wram_base = match match &mut state { + State::Retroarch(x) => x.find_wram(&self.process, &mut endian), + State::SegaClassics(x) => x.find_wram(&self.process, &mut endian), + State::Fusion(x) => x.find_wram(&self.process, &mut endian), + State::Gens(x) => x.find_wram(&self.process, &mut endian), + State::BlastEm(x) => x.find_wram(&self.process, &mut endian), } { None => return false, something => something, }; } - let success = match &self.state { + let success = match &state { State::Retroarch(x) => x.keep_alive(&self.process), - State::SegaClassics(x) => x.keep_alive(&self.process, &mut self.wram_base), - State::Fusion(x) => x.keep_alive(&self.process, &mut self.wram_base), + State::SegaClassics(x) => x.keep_alive(&self.process, &mut wram_base), + State::Fusion(x) => x.keep_alive(&self.process, &mut wram_base), State::Gens(x) => x.keep_alive(), State::BlastEm(x) => x.keep_alive(), }; + self.endian.set(endian); + self.state.set(state); + if success { + self.wram_base.set(wram_base); true } else { - self.wram_base = None; + self.wram_base.set(None); false } } @@ -100,8 +122,7 @@ impl Emulator { return Err(Error {}); } - let wram = self.wram_base.ok_or(Error {})?; - + let wram = self.wram_base.get().ok_or(Error {})?; self.process.read(wram + offset) } @@ -118,15 +139,35 @@ impl Emulator { return Err(Error {}); } - let wram = self.wram_base.ok_or(Error {})?; + let wram = self.wram_base.get().ok_or(Error {})?; let mut end_offset = offset.checked_sub(0xFF0000).unwrap_or(offset); + let endian = self.endian.get(); - let toggle = self.endian == Endian::Little && mem::size_of::() == 1; + let toggle = endian == Endian::Little && mem::size_of::() == 1; end_offset ^= toggle as u32; let value = self.process.read::(wram + end_offset)?; - Ok(value.from_endian(self.endian)) + Ok(value.from_endian(endian)) + } +} + +/// A future that executes a future until the emulator closes. +pub struct UntilEmulatorCloses<'a, F> { + emulator: &'a Emulator, + future: F, +} + +impl> Future for UntilEmulatorCloses<'_, F> { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if !self.emulator.process.is_open() { + return Poll::Ready(()); + } + self.emulator.update(); + // SAFETY: We are simply projecting the Pin. + unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().future).poll(cx) } } } diff --git a/src/emulator/ps1/mod.rs b/src/emulator/ps1/mod.rs index b63251d..e5d79f5 100644 --- a/src/emulator/ps1/mod.rs +++ b/src/emulator/ps1/mod.rs @@ -1,5 +1,12 @@ //! Support for attaching to Playstation 1 emulators. +use core::{ + cell::Cell, + future::Future, + pin::Pin, + task::{Context, Poll}, +}; + use crate::{Address, Error, Process}; use bytemuck::CheckedBitPattern; @@ -16,9 +23,9 @@ pub struct Emulator { /// The attached emulator process process: Process, /// An enum stating which emulator is currently attached - state: State, + state: Cell, /// The memory address of the emulated RAM - ram_base: Option
, + ram_base: Cell>, } impl Emulator { @@ -40,8 +47,8 @@ impl Emulator { Some(Self { process, - state, - ram_base: None, + state: Cell::new(state), + ram_base: Cell::new(None), }) } @@ -51,13 +58,24 @@ impl Emulator { self.process.is_open() } + /// Executes a future until the emulator process closes. + pub const fn until_closes(&self, future: F) -> UntilEmulatorCloses<'_, F> { + UntilEmulatorCloses { + emulator: self, + future, + } + } + /// Calls the internal routines needed in order to find (and update, if /// needed) the address of the emulated RAM. /// /// Returns true if successful, false otherwise. - pub fn update(&mut self) -> bool { - if self.ram_base.is_none() { - self.ram_base = match match &mut self.state { + pub fn update(&self) -> bool { + let mut ram_base = self.ram_base.get(); + let mut state = self.state.get(); + + if ram_base.is_none() { + ram_base = match match &mut state { State::Epsxe(x) => x.find_ram(&self.process), State::PsxFin(x) => x.find_ram(&self.process), State::Duckstation(x) => x.find_ram(&self.process), @@ -71,20 +89,23 @@ impl Emulator { }; } - let success = match &self.state { + let success = match &state { State::Epsxe(x) => x.keep_alive(), State::PsxFin(x) => x.keep_alive(), - State::Duckstation(x) => x.keep_alive(&self.process, &mut self.ram_base), + State::Duckstation(x) => x.keep_alive(&self.process, &mut ram_base), State::Retroarch(x) => x.keep_alive(&self.process), State::PcsxRedux(x) => x.keep_alive(&self.process), State::Xebra(x) => x.keep_alive(), State::Mednafen(x) => x.keep_alive(), }; + self.state.set(state); + if success { + self.ram_base.set(ram_base); true } else { - self.ram_base = None; + self.ram_base.set(None); false } } @@ -106,16 +127,32 @@ impl Emulator { return Err(Error {}); }; - let Some(ram_base) = self.ram_base else { - return Err(Error {}); - }; - + let ram_base = self.ram_base.get().ok_or(Error {})?; let end_offset = offset.checked_sub(0x80000000).unwrap_or(offset); self.process.read(ram_base + end_offset) } } +/// A future that executes a future until the emulator closes. +pub struct UntilEmulatorCloses<'a, F> { + emulator: &'a Emulator, + future: F, +} + +impl> Future for UntilEmulatorCloses<'_, F> { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if !self.emulator.is_open() { + return Poll::Ready(()); + } + self.emulator.update(); + // SAFETY: We are simply projecting the Pin. + unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().future).poll(cx) } + } +} + #[doc(hidden)] #[derive(Copy, Clone, Debug, PartialEq, Eq)] enum State { diff --git a/src/emulator/ps2/mod.rs b/src/emulator/ps2/mod.rs index 77c4b6d..bd605a4 100644 --- a/src/emulator/ps2/mod.rs +++ b/src/emulator/ps2/mod.rs @@ -1,5 +1,12 @@ //! Support for attaching to Playstation 2 emulators. +use core::{ + cell::Cell, + future::Future, + pin::Pin, + task::{Context, Poll}, +}; + use crate::{Address, Error, Process}; use bytemuck::CheckedBitPattern; @@ -11,9 +18,9 @@ pub struct Emulator { /// The attached emulator process process: Process, /// An enum stating which emulator is currently attached - state: State, + state: Cell, /// The memory address of the emulated RAM - ram_base: Option
, + ram_base: Cell>, } impl Emulator { @@ -31,8 +38,8 @@ impl Emulator { Some(Self { process, - state, - ram_base: None, + state: Cell::new(state), + ram_base: Cell::new(None), }) } @@ -42,32 +49,46 @@ impl Emulator { self.process.is_open() } + /// Executes a future until the emulator process closes. + pub const fn until_closes(&self, future: F) -> UntilEmulatorCloses<'_, F> { + UntilEmulatorCloses { + emulator: self, + future, + } + } + /// Calls the internal routines needed in order to find (and update, if /// needed) the address of the emulated RAM. /// /// Returns true if successful, false otherwise. - pub fn update(&mut self) -> bool { - if self.ram_base.is_none() { - let ram_base = match &mut self.state { + pub fn update(&self) -> bool { + let mut ram_base = self.ram_base.get(); + let mut state = self.state.get(); + + if ram_base.is_none() { + ram_base = match match &mut state { State::Pcsx2(x) => x.find_ram(&self.process), State::Retroarch(x) => x.find_ram(&self.process), + } { + None => return false, + something => something, }; - if ram_base.is_none() { - return false; - } - self.ram_base = ram_base; } - let success = match &self.state { - State::Pcsx2(x) => x.keep_alive(&self.process, &mut self.ram_base), + let success = match &state { + State::Pcsx2(x) => x.keep_alive(&self.process, &mut ram_base), State::Retroarch(x) => x.keep_alive(&self.process), }; - if !success { - self.ram_base = None; - } + self.state.set(state); - success + if success { + self.ram_base.set(ram_base); + true + } else { + self.ram_base.set(None); + false + } } /// Reads any value from the emulated RAM. @@ -81,14 +102,11 @@ impl Emulator { /// Providing any offset outside the range of the PS2's RAM will return /// `Err()`. pub fn read(&self, address: u32) -> Result { - if !(0x00100000..=0x01FFFFFF).contains(&address) { + if !(0x00100000..0x02000000).contains(&address) { return Err(Error {}); } - let Some(ram_base) = self.ram_base else { - return Err(Error {}); - }; - + let ram_base = self.ram_base.get().ok_or(Error {})?; self.process.read(ram_base + address) } @@ -117,6 +135,25 @@ impl Emulator { } } +/// A future that executes a future until the emulator closes. +pub struct UntilEmulatorCloses<'a, F> { + emulator: &'a Emulator, + future: F, +} + +impl> Future for UntilEmulatorCloses<'_, F> { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if !self.emulator.is_open() { + return Poll::Ready(()); + } + self.emulator.update(); + // SAFETY: We are simply projecting the Pin. + unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().future).poll(cx) } + } +} + #[doc(hidden)] #[derive(Copy, Clone, Debug, PartialEq, Eq)] enum State { diff --git a/src/emulator/sms/mod.rs b/src/emulator/sms/mod.rs index 99206ee..d3d40ef 100644 --- a/src/emulator/sms/mod.rs +++ b/src/emulator/sms/mod.rs @@ -1,5 +1,7 @@ //! Support for attaching to SEGA Master System / SEGA GameGear emulators. +use core::{cell::Cell, pin::Pin, future::Future, task::{Context, Poll}}; + use crate::{Address, Error, Process}; use bytemuck::CheckedBitPattern; @@ -13,9 +15,9 @@ pub struct Emulator { /// The attached emulator process process: Process, /// An enum stating which emulator is currently attached - state: State, + state: Cell, /// The memory address of the emulated RAM - ram_base: Option
, + ram_base: Cell>, } impl Emulator { @@ -35,8 +37,8 @@ impl Emulator { Some(Self { process, - state, - ram_base: None, + state: Cell::new(state), + ram_base: Cell::new(None), }) } @@ -46,13 +48,24 @@ impl Emulator { self.process.is_open() } + /// Executes a future until the emulator process closes. + pub const fn until_closes(&self, future: F) -> UntilEmulatorCloses<'_, F> { + UntilEmulatorCloses { + emulator: self, + future, + } + } + /// Calls the internal routines needed in order to find (and update, if /// needed) the address of the emulated RAM. /// /// Returns true if successful, false otherwise. - pub fn update(&mut self) -> bool { - if self.ram_base.is_none() { - self.ram_base = match match &mut self.state { + pub fn update(&self) -> bool { + let mut ram_base = self.ram_base.get(); + let mut state = self.state.get(); + + if ram_base.is_none() { + ram_base = match match &mut state { State::Retroarch(x) => x.find_ram(&self.process), State::Fusion(x) => x.find_ram(&self.process), State::BlastEm(x) => x.find_ram(&self.process), @@ -63,17 +76,18 @@ impl Emulator { }; } - let success = match &self.state { + let success = match &state { State::Retroarch(x) => x.keep_alive(&self.process), - State::Fusion(x) => x.keep_alive(&self.process, &mut self.ram_base), + State::Fusion(x) => x.keep_alive(&self.process, &mut ram_base), State::BlastEm(x) => x.keep_alive(), State::Mednafen(x) => x.keep_alive(), }; if success { + self.ram_base.set(ram_base); true } else { - self.ram_base = None; + self.ram_base.set(None); false } } @@ -91,13 +105,32 @@ impl Emulator { return Err(Error {}); } - let wram = self.ram_base.ok_or(Error {})?; + let wram = self.ram_base.get().ok_or(Error {})?; let end_offset = offset.checked_sub(0xC000).unwrap_or(offset); self.process.read(wram + end_offset) } } +/// A future that executes a future until the emulator closes. +pub struct UntilEmulatorCloses<'a, F> { + emulator: &'a Emulator, + future: F, +} + +impl> Future for UntilEmulatorCloses<'_, F> { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if !self.emulator.is_open() { + return Poll::Ready(()); + } + self.emulator.update(); + // SAFETY: We are simply projecting the Pin. + unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().future).poll(cx) } + } +} + #[doc(hidden)] #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum State { diff --git a/src/emulator/wii/mod.rs b/src/emulator/wii/mod.rs index 6c11cfe..1686934 100644 --- a/src/emulator/wii/mod.rs +++ b/src/emulator/wii/mod.rs @@ -1,5 +1,12 @@ //! Support for attaching to Nintendo Wii emulators. +use core::{ + cell::Cell, + future::Future, + pin::Pin, + task::{Context, Poll}, +}; + use crate::{Address, Endian, Error, FromEndian, Process}; use bytemuck::CheckedBitPattern; @@ -12,11 +19,11 @@ pub struct Emulator { /// The attached emulator process process: Process, /// An enum stating which emulator is currently attached - state: State, + state: Cell, /// The memory address of the emulated RAM - ram_base: Option<[Address; 2]>, // [MEM1, MEM2] + ram_base: Cell>, // [MEM1, MEM2] /// The endianness used by the emulator process - endian: Endian, + endian: Cell, } impl Emulator { @@ -34,9 +41,9 @@ impl Emulator { Some(Self { process, - state, - ram_base: None, // [MEM1, MEM2] - endian: Endian::Big, // Endianness is usually Big in Wii emulators + state: Cell::new(state), + ram_base: Cell::new(None), // [MEM1, MEM2] + endian: Cell::new(Endian::Big), // Endianness is usually Big in Wii emulators }) } @@ -46,30 +53,46 @@ impl Emulator { self.process.is_open() } + /// Executes a future until the emulator process closes. + pub const fn until_closes(&self, future: F) -> UntilEmulatorCloses<'_, F> { + UntilEmulatorCloses { + emulator: self, + future, + } + } + /// Calls the internal routines needed in order to find (and update, if /// needed) the address of the emulated RAM. /// /// Returns true if successful, false otherwise. - pub fn update(&mut self) -> bool { - if self.ram_base.is_none() { - self.ram_base = match match &mut self.state { - State::Dolphin(x) => x.find_ram(&self.process, &mut self.endian), - State::Retroarch(x) => x.find_ram(&self.process, &mut self.endian), + pub fn update(&self) -> bool { + let mut ram_base = self.ram_base.get(); + let mut state = self.state.get(); + let mut endian = self.endian.get(); + + if ram_base.is_none() { + ram_base = match match &mut state { + State::Dolphin(x) => x.find_ram(&self.process, &mut endian), + State::Retroarch(x) => x.find_ram(&self.process, &mut endian), } { None => return false, something => something, }; } - let success = match &self.state { - State::Dolphin(x) => x.keep_alive(&self.process, &self.ram_base), - State::Retroarch(x) => x.keep_alive(&self.process, &self.ram_base), + let success = match &state { + State::Dolphin(x) => x.keep_alive(&self.process, &ram_base), + State::Retroarch(x) => x.keep_alive(&self.process, &ram_base), }; + self.state.set(state); + self.endian.set(endian); + if success { + self.ram_base.set(ram_base); true } else { - self.ram_base = None; + self.ram_base.set(None); false } } @@ -113,7 +136,7 @@ impl Emulator { pub fn read(&self, address: u32) -> Result { Ok(self .read_ignoring_endianness::(address)? - .from_endian(self.endian)) + .from_endian(self.endian.get())) } /// Follows a path of pointers from the address given and reads a value of the type specified from @@ -166,9 +189,8 @@ impl Emulator { if address < 0x80000000 || address > 0x817FFFFF { return Err(Error {}); } - let Some([mem1, _]) = self.ram_base else { - return Err(Error {}); - }; + + let [mem1, _] = self.ram_base.get().ok_or(Error {})?; let end_offset = address.checked_sub(0x80000000).unwrap_or(address); self.process.read(mem1 + end_offset) } @@ -192,14 +214,31 @@ impl Emulator { if address < 0x90000000 || address > 0x93FFFFFF { return Err(Error {}); } - let Some([_, mem2]) = self.ram_base else { - return Err(Error {}); - }; + let [_, mem2] = self.ram_base.get().ok_or(Error {})?; let end_offset = address.checked_sub(0x90000000).unwrap_or(address); self.process.read(mem2 + end_offset) } } +/// A future that executes a future until the emulator closes. +pub struct UntilEmulatorCloses<'a, F> { + emulator: &'a Emulator, + future: F, +} + +impl> Future for UntilEmulatorCloses<'_, F> { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if !self.emulator.is_open() { + return Poll::Ready(()); + } + self.emulator.update(); + // SAFETY: We are simply projecting the Pin. + unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().future).poll(cx) } + } +} + #[doc(hidden)] #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum State {