Skip to content

Commit

Permalink
Merge branch 'master' into wii
Browse files Browse the repository at this point in the history
  • Loading branch information
Jujstme authored Oct 26, 2023
2 parents e1398da + d6d99f3 commit 9766dbf
Show file tree
Hide file tree
Showing 35 changed files with 2,803 additions and 711 deletions.
6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ wasi-no-std = ["libm"]
unity = ["signature", "asr-derive?/unity"]

# Emulators
gba = []
gba = ["flags", "signature"]
gcn = ["flags"]
genesis = ["flags", "signature"]
ps1 = ["flags", "signature"]
wii = ["flags"]
ps2 = ["flags", "signature"]
wii = ["flags"]
62 changes: 41 additions & 21 deletions asr-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ use proc_macro::TokenStream;
use quote::{quote, quote_spanned};
use syn::{spanned::Spanned, Data, DeriveInput, Expr, ExprLit, Lit, Meta};

/// Generates a `register` method for a struct that automatically registers its
/// fields as settings and returns the struct with the user's settings applied.
/// Implements the `Gui` trait for a struct that allows you to register its
/// fields as settings widgets and returns the struct with the user's settings
/// applied.
///
/// # Example
///
/// ```no_run
/// #[derive(Settings)]
/// struct MySettings {
/// #[derive(Gui)]
/// struct Settings {
/// /// General Settings
/// _general_settings: Title,
/// /// Use Game Time
Expand All @@ -20,19 +21,17 @@ use syn::{spanned::Spanned, Data, DeriveInput, Expr, ExprLit, Lit, Meta};
/// }
/// ```
///
/// This will generate the following code:
/// The type can then be used like so:
///
/// ```no_run
/// impl MySettings {
/// pub fn register() -> Self {
/// asr::user_settings::add_title("_general_settings", "General Settings", 0);
/// let use_game_time = asr::user_settings::add_bool("use_game_time", "Use Game Time", false);
/// asr::user_settings::set_tooltip("use_game_time", "This is the tooltip.");
/// Self { use_game_time }
/// }
/// let mut settings = Settings::register();
///
/// loop {
/// settings.update();
/// // Do something with the settings.
/// }
/// ```
#[proc_macro_derive(Settings, attributes(default, heading_level))]
#[proc_macro_derive(Gui, attributes(default, heading_level))]
pub fn settings_macro(input: TokenStream) -> TokenStream {
let ast: DeriveInput = syn::parse(input).unwrap();

Expand All @@ -58,14 +57,21 @@ pub fn settings_macro(input: TokenStream) -> TokenStream {
let mut tooltip_string = String::new();
let mut is_in_tooltip = false;
for attr in &field.attrs {
let Meta::NameValue(nv) = &attr.meta else { continue };
let Some(ident) = nv.path.get_ident() else { continue };
let Meta::NameValue(nv) = &attr.meta else {
continue;
};
let Some(ident) = nv.path.get_ident() else {
continue;
};
if ident != "doc" {
continue;
}
let Expr::Lit(ExprLit {
lit: Lit::Str(s), ..
}) = &nv.value else { continue };
}) = &nv.value
else {
continue;
};
let value = s.value();
let value = value.trim();
let target_string = if is_in_tooltip {
Expand Down Expand Up @@ -102,7 +108,9 @@ pub fn settings_macro(input: TokenStream) -> TokenStream {
.attrs
.iter()
.filter_map(|x| {
let Meta::NameValue(nv) = &x.meta else { return None };
let Meta::NameValue(nv) = &x.meta else {
return None;
};
let span = nv.span();
if nv.path.is_ident("default") {
let value = &nv.value;
Expand All @@ -119,18 +127,30 @@ pub fn settings_macro(input: TokenStream) -> TokenStream {
}

quote! {
impl #struct_name {
pub fn register() -> Self {
impl asr::settings::Gui for #struct_name {
fn register() -> Self {
Self {
#(#field_names: {
let mut args = <#field_tys as asr::user_settings::Setting>::Args::default();
let mut args = <#field_tys as asr::settings::gui::Widget>::Args::default();
#args_init
let mut value = asr::user_settings::Setting::register(#field_name_strings, #field_descs, args);
let mut value = asr::settings::gui::Widget::register(#field_name_strings, #field_descs, args);
#field_tooltips
value
},)*
}
}

fn update_from(&mut self, settings_map: &asr::settings::Map) {
#({
let mut args = <#field_tys as asr::settings::gui::Widget>::Args::default();
#args_init
asr::settings::gui::Widget::update_from(&mut self.#field_names, settings_map, #field_name_strings, args);
})*
}

fn update(&mut self) {
self.update_from(&asr::settings::Map::load());
}
}
}
.into()
Expand Down
18 changes: 13 additions & 5 deletions asr-derive/src/unity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ pub fn process(input: TokenStream, mono_module: impl ToTokens) -> TokenStream {
let field_name = field.ident.clone().unwrap();
let span = field_name.span();
let is_static = field.attrs.iter().any(|x| {
let Meta::Path(path) = &x.meta else { return false };
let Meta::Path(path) = &x.meta else {
return false;
};
path.is_ident("static_field")
});
field_reads.push(if is_static {
Expand All @@ -42,13 +44,19 @@ pub fn process(input: TokenStream, mono_module: impl ToTokens) -> TokenStream {
.attrs
.iter()
.find_map(|x| {
let Meta::NameValue(name_value) = &x.meta else { return None };
let Meta::NameValue(name_value) = &x.meta else {
return None;
};
if !name_value.path.is_ident("rename") {
return None;
}
let Expr::Lit(ExprLit {
lit: Lit::Str(name), ..
}) = &name_value.value else { return None };
lit: Lit::Str(name),
..
}) = &name_value.value
else {
return None;
};
Some(name.value())
})
.unwrap_or_else(|| field.ident.clone().unwrap().to_string());
Expand Down Expand Up @@ -97,7 +105,7 @@ pub fn process(input: TokenStream, mono_module: impl ToTokens) -> TokenStream {
let class = image.wait_get_class(process, module, #stuct_name_string).await;

#(
let #field_names = class.wait_get_field(process, module, #lookup_names).await;
let #field_names = class.wait_get_field_offset(process, module, #lookup_names).await;
)*

#binding_name {
Expand Down
81 changes: 81 additions & 0 deletions src/deep_pointer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//! Support for storing pointer paths for easy dereferencing inside the autosplitter logic.
use arrayvec::ArrayVec;
use bytemuck::CheckedBitPattern;

use crate::{Address, Address32, Address64, Error, Process};

/// An abstraction of a pointer path, usable for easy dereferencing inside an autosplitter logic.
///
/// The maximum depth of the pointer path is given by the generic parameter `CAP`.
/// Of note, `CAP` must be higher or equal to the number of offsets provided in `path`,
/// otherwise calling `new()` on this struct will trigger a ***Panic***.
#[derive(Clone)]
pub struct DeepPointer<const CAP: usize> {
base_address: Address,
path: ArrayVec<u64, CAP>,
deref_type: DerefType,
}

impl<const CAP: usize> Default for DeepPointer<CAP> {
/// Creates a new empty DeepPointer.
#[inline]
fn default() -> Self {
Self {
base_address: Address::default(),
path: ArrayVec::default(),
deref_type: DerefType::default(),
}
}
}

impl<const CAP: usize> DeepPointer<CAP> {
/// Creates a new DeepPointer and specify the pointer size dereferencing
#[inline]
pub fn new(base_address: Address, deref_type: DerefType, path: &[u64]) -> Self {
assert!(CAP != 0 && CAP >= path.len());
Self {
base_address,
path: path.iter().cloned().collect(),
deref_type,
}
}

/// Creates a new DeepPointer with 32bit pointer size dereferencing
pub fn new_32bit(base_address: Address, path: &[u64]) -> Self {
Self::new(base_address, DerefType::Bit32, path)
}

/// Creates a new DeepPointer with 64bit pointer size dereferencing
pub fn new_64bit(base_address: Address, path: &[u64]) -> Self {
Self::new(base_address, DerefType::Bit64, path)
}

/// Dereferences the pointer path, returning the memory address of the value of interest
pub fn deref_offsets(&self, process: &Process) -> Result<Address, Error> {
let mut address = self.base_address;
let (&last, path) = self.path.split_last().ok_or(Error {})?;
for &offset in path {
address = match self.deref_type {
DerefType::Bit32 => process.read::<Address32>(address + offset)?.into(),
DerefType::Bit64 => process.read::<Address64>(address + offset)?.into(),
};
}
Ok(address + last)
}

/// Dereferences the pointer path, returning the value stored at the final memory address
pub fn deref<T: CheckedBitPattern>(&self, process: &Process) -> Result<T, Error> {
process.read(self.deref_offsets(process)?)
}
}

/// Describes the pointer size that should be used while deferecencing a pointer path
#[derive(Copy, Clone, Default)]
pub enum DerefType {
/// 4-byte pointer size, used in 32bit processes
Bit32,
/// 8-byte pointer size, used in 64bit processes
#[default]
Bit64,
}
80 changes: 0 additions & 80 deletions src/emulator/gba.rs

This file was deleted.

36 changes: 36 additions & 0 deletions src/emulator/gba/emuhawk.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use crate::{Address, MemoryRangeFlags, Process};

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct State {
base_addr: Address,
}

impl State {
pub fn find_ram(&mut self, game: &Process) -> Option<[Address; 2]> {
self.base_addr = game.get_module_address("mgba.dll").ok()?;

let addr = game
.memory_ranges()
.find(|range| {
range.size().is_ok_and(|size| size == 0x48000)
&& range.flags().is_ok_and(|flag| {
flag.contains(MemoryRangeFlags::WRITE | MemoryRangeFlags::READ)
})
})?
.address()
.ok()?;

Some([addr, addr + 0x40000])
}

pub fn keep_alive(&self, game: &Process, ram_base: &Option<[Address; 2]>) -> bool {
ram_base.is_some_and(|[ewram, _]| game.read::<u8>(ewram).is_ok())
&& game.read::<u8>(self.base_addr).is_ok()
}

pub const fn new() -> Self {
Self {
base_addr: Address::NULL,
}
}
}
Loading

0 comments on commit 9766dbf

Please sign in to comment.