Skip to content

Commit

Permalink
Add support for Settings Lists (#77)
Browse files Browse the repository at this point in the history
This adds support for `List`, `i64`, and `f64` values. It also extends
the `Map` API to support iteration. All types also now implement `Clone`
and `Debug`.
  • Loading branch information
CryZe committed Nov 12, 2023
1 parent 9563a92 commit 4e789ef
Show file tree
Hide file tree
Showing 9 changed files with 514 additions and 37 deletions.
11 changes: 5 additions & 6 deletions src/emulator/gba/mednafen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ impl State {
addr
};


self.cached_iwram_pointer = {
const SIG2: Signature<13> = Signature::new("48 8B 05 ?? ?? ?? ?? 81 E1 FF 7F 00 00");
const SIG2: Signature<13> =
Signature::new("48 8B 05 ?? ?? ?? ?? 81 E1 FF 7F 00 00");
let ptr: Address = SIG2.scan_process_range(game, main_module_range)? + 3;
let mut addr: Address = ptr + 0x4 + game.read::<i32>(ptr).ok()?;

Expand All @@ -45,8 +45,8 @@ impl State {
return None;
}
}
addr

addr
};

let ewram = game.read::<Address64>(self.cached_ewram_pointer).ok()?;
Expand All @@ -60,13 +60,12 @@ impl State {
game.read::<Address32>(ptr + 1).ok()?.into()
};


self.cached_iwram_pointer = {
const SIG2: Signature<11> = Signature::new("A1 ?? ?? ?? ?? 81 ?? FF 7F 00 00");
let ptr = SIG2.scan_process_range(game, main_module_range)?;
game.read::<Address32>(ptr + 1).ok()?.into()
};

let ewram = game.read::<Address32>(self.cached_ewram_pointer).ok()?;
let iwram = game.read::<Address32>(self.cached_iwram_pointer).ok()?;

Expand Down
6 changes: 3 additions & 3 deletions src/emulator/gba/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
use crate::{Address, Error, Process};
use bytemuck::CheckedBitPattern;

mod emuhawk;
mod mednafen;
mod mgba;
mod nocashgba;
mod retroarch;
mod vba;
mod emuhawk;
mod mednafen;

/// A Nintendo Gameboy Advance emulator that the auto splitter is attached to.
pub struct Emulator {
Expand Down Expand Up @@ -84,7 +84,7 @@ impl Emulator {
false => {
self.ram_base = None;
false
},
}
}
}

Expand Down
34 changes: 19 additions & 15 deletions src/emulator/gba/vba.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,24 +48,24 @@ impl State {
return None;
}
}

addr
};

addr
};

self.is_emulating = {
const SIG_RUNNING: Signature<19> = Signature::new("83 3D ?? ?? ?? ?? 00 74 ?? 80 3D ?? ?? ?? ?? 00 75 ?? 66");
const SIG_RUNNING2: Signature<16> = Signature::new("48 8B 15 ?? ?? ?? ?? 31 C0 8B 12 85 D2 74 ?? 48");
const SIG_RUNNING: Signature<19> =
Signature::new("83 3D ?? ?? ?? ?? 00 74 ?? 80 3D ?? ?? ?? ?? 00 75 ?? 66");
const SIG_RUNNING2: Signature<16> =
Signature::new("48 8B 15 ?? ?? ?? ?? 31 C0 8B 12 85 D2 74 ?? 48");

if let Some(ptr) = SIG_RUNNING.scan_process_range(game, main_module_range) {
let ptr = ptr + 2;
ptr + 0x4 + game.read::<i32>(ptr).ok()? + 0x1
ptr + 0x4 + game.read::<i32>(ptr).ok()? + 0x1
} else {
let ptr = SIG_RUNNING2.scan_process_range(game, main_module_range)? + 3;
let ptr = ptr + 0x4 + game.read::<i32>(ptr).ok()?;
game.read::<Address64>(ptr).ok()?.into()
}

};

let ewram = game.read::<Address64>(self.cached_ewram_pointer).ok()?;
Expand All @@ -75,25 +75,28 @@ impl State {
} else {
const SIG: Signature<11> = Signature::new("A1 ?? ?? ?? ?? 81 ?? FF FF 03 00");
const SIG_OLD: Signature<12> = Signature::new("81 E6 FF FF 03 00 8B 15 ?? ?? ?? ??");

if let Some(ptr) = SIG.scan_process_range(game, main_module_range) {
self.cached_ewram_pointer = game.read::<Address32>(ptr + 1).ok()?.into();
self.cached_iwram_pointer = {
const SIG2: Signature<11> = Signature::new("A1 ?? ?? ?? ?? 81 ?? FF 7F 00 00");
let ptr = SIG2.scan_process_range(game, main_module_range)?;
game.read::<Address32>(ptr + 1).ok()?.into()
};

self.is_emulating = {
const SIG: Signature<19> = Signature::new("83 3D ?? ?? ?? ?? 00 74 ?? 80 3D ?? ?? ?? ?? 00 75 ?? 66");
const SIG_OLD: Signature<13> = Signature::new("8B 15 ?? ?? ?? ?? 31 C0 85 D2 74 ?? 0F");
const SIG: Signature<19> =
Signature::new("83 3D ?? ?? ?? ?? 00 74 ?? 80 3D ?? ?? ?? ?? 00 75 ?? 66");
const SIG_OLD: Signature<13> =
Signature::new("8B 15 ?? ?? ?? ?? 31 C0 85 D2 74 ?? 0F");

let ptr = SIG.scan_process_range(game, main_module_range)
let ptr = SIG
.scan_process_range(game, main_module_range)
.or_else(|| SIG_OLD.scan_process_range(game, main_module_range))?;

game.read::<Address32>(ptr + 2).ok()?.into()
};

let ewram = game.read::<Address32>(self.cached_ewram_pointer).ok()?;
let iwram = game.read::<Address32>(self.cached_iwram_pointer).ok()?;

Expand All @@ -104,11 +107,12 @@ impl State {
self.cached_iwram_pointer = self.cached_ewram_pointer.add_signed(0x4);

self.is_emulating = {
const SIG_RUNNING: Signature<11> = Signature::new("8B 0D ?? ?? ?? ?? 85 C9 74 ?? 8A");
const SIG_RUNNING: Signature<11> =
Signature::new("8B 0D ?? ?? ?? ?? 85 C9 74 ?? 8A");
let ptr = SIG_RUNNING.scan_process_range(game, main_module_range)? + 2;
game.read::<Address32>(ptr).ok()?.into()
};

let ewram = game.read::<Address32>(self.cached_ewram_pointer).ok()?;
let iwram = game.read::<Address32>(self.cached_iwram_pointer).ok()?;

Expand Down
1 change: 0 additions & 1 deletion src/runtime/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ use super::{sys, Error, MemoryRange};
pub use super::sys::ProcessId;

/// A process that the auto splitter is attached to.
#[derive(Debug)]
#[repr(transparent)]
pub struct Process(pub(super) sys::Process);

Expand Down
109 changes: 109 additions & 0 deletions src/runtime/settings/list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
use core::fmt;

use crate::{runtime::sys, Error};

use super::Value;

/// A list of [`Value`]s that can itself be a [`Value`] and thus be stored in a
/// [`Map`](super::Map).
#[repr(transparent)]
pub struct List(pub(super) sys::SettingsList);

impl fmt::Debug for List {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.iter()).finish()
}
}

impl Drop for List {
#[inline]
fn drop(&mut self) {
// SAFETY: The handle is valid and we own it, so it's our responsibility
// to free it.
unsafe { sys::settings_list_free(self.0) }
}
}

impl Clone for List {
#[inline]
fn clone(&self) -> Self {
// SAFETY: The handle is valid, so we can safely copy it.
Self(unsafe { sys::settings_list_copy(self.0) })
}
}

impl Default for List {
#[inline]
fn default() -> Self {
Self::new()
}
}

impl List {
/// Creates a new empty settings list.
#[inline]
pub fn new() -> Self {
// SAFETY: This is always safe to call.
Self(unsafe { sys::settings_list_new() })
}

/// Returns the number of values in the list.
#[inline]
pub fn len(&self) -> u64 {
// SAFETY: The handle is valid, so we can safely call this function.
unsafe { sys::settings_list_len(self.0) }
}

/// Returns [`true`] if the list has a length of 0.
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}

/// Returns a copy of the value at the given index. Returns [`None`] if the
/// index is out of bounds. Any changes to it are only perceived if it's
/// stored back.
#[inline]
pub fn get(&self, index: u64) -> Option<Value> {
// SAFETY: The settings list handle is valid.
unsafe { sys::settings_list_get(self.0, index).map(Value) }
}

/// Pushes a copy of the value to the end of the list.
#[inline]
pub fn push(&self, value: &Value) {
// SAFETY: The settings list handle is valid and the value handle is
// valid.
unsafe { sys::settings_list_push(self.0, value.0) }
}

/// Inserts a copy of the value at the given index, pushing all values at
/// and after the index one position further. Returns an error if the index
/// is out of bounds. You may specify an index that is equal to the length
/// of the list to append the value to the end of the list.
#[inline]
pub fn insert(&self, index: u64, value: &Value) -> Result<(), Error> {
// SAFETY: The settings list handle is valid and the value handle is
// valid.
unsafe {
if sys::settings_list_insert(self.0, index, value.0) {
Ok(())
} else {
Err(Error {})
}
}
}

/// Returns an iterator over the values in the list. Every value is a copy,
/// so any changes to them are only perceived if they are stored back. The
/// iterator is double-ended, so it can be iterated backwards as well. While
/// it's possible to modify the list while iterating over it, it's not
/// recommended to do so, as the iterator might skip values or return
/// duplicate values. In that case it's better to clone the list before and
/// iterate over the clone or use [`get`](Self::get) to manually handle the
/// iteration.
#[inline]
pub fn iter(&self) -> impl DoubleEndedIterator<Item = Value> + '_ {
(0..self.len()).flat_map(|i| self.get(i))
}
}
Loading

0 comments on commit 4e789ef

Please sign in to comment.