Skip to content

Commit

Permalink
Start on detecting 64-bit vs 32-bit (#42)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexKnauth authored Jan 28, 2024
1 parent 28cfdcd commit 8dbfb2f
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 39 deletions.
12 changes: 3 additions & 9 deletions src/auto_splitter_settings.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use alloc::collections::BTreeSet;
use alloc::vec::Vec;
use asr::future::retry;
use std::{path::Path, fs::File, io::{self, Read}};
use std::path::Path;
use xmltree::{Element, XMLNode};

use ugly_widget::radio_button::options_str;

use crate::{legacy_xml, splits::Split};
use crate::{file, legacy_xml, splits::Split};

Check failure on line 10 in src/auto_splitter_settings.rs

View workflow job for this annotation

GitHub Actions / Check formatting

Diff in /home/runner/work/hollowknight-autosplit-wasm/hollowknight-autosplit-wasm/src/auto_splitter_settings.rs
pub async fn wait_asr_settings_init() -> asr::settings::Map {
let settings1 = asr::settings::Map::load();
Expand Down Expand Up @@ -58,7 +58,7 @@ fn asr_settings_from_splits(splits: &[Split]) -> asr::settings::Map {
}

fn file_find_auto_splitter_settings<P: AsRef<Path>>(path: P) -> Option<Vec<XMLNode>> {
let bs = file_read_all_bytes(path).ok()?;
let bs = file::file_read_all_bytes(path).ok()?;
let es = Element::parse_all(bs.as_slice()).ok()?;
let auto_splitter_settings = es.iter().find_map(xml_find_auto_splitter_settings)?;
Some(auto_splitter_settings)
Expand Down Expand Up @@ -150,9 +150,3 @@ fn maybe_asr_settings_list_merge(old: Option<asr::settings::List>, new: &asr::se

// --------------------------------------------------------

fn file_read_all_bytes<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
let mut f = File::open(path)?;
let mut buffer: Vec<u8> = Vec::new();
f.read_to_end(&mut buffer)?;
Ok(buffer)
}
11 changes: 11 additions & 0 deletions src/file.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

Check failure on line 1 in src/file.rs

View workflow job for this annotation

GitHub Actions / Check formatting

Diff in /home/runner/work/hollowknight-autosplit-wasm/hollowknight-autosplit-wasm/src/file.rs
use alloc::vec::Vec;

use std::{path::Path, fs::File, io::{self, Read}};

pub fn file_read_all_bytes<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
let mut f = File::open(path)?;
let mut buffer: Vec<u8> = Vec::new();
f.read_to_end(&mut buffer)?;
Ok(buffer)
}
138 changes: 108 additions & 30 deletions src/hollow_knight_memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,19 @@ use core::cell::OnceCell;
use std::cmp::min;
use std::mem;
use std::collections::BTreeMap;
use asr::file_format::pe;
use asr::future::{next_tick, retry};
use asr::watcher::Pair;
use asr::{Process, Address64};
use asr::{Address, Address32, Address64, Process};
use asr::game_engine::unity::mono::{self, UnityPointer};
use asr::string::ArrayWString;
use ugly_widget::store::StoreGui;

#[cfg(debug_assertions)]
use std::string::String;

use crate::file;

// --------------------------------------------------------

static HOLLOW_KNIGHT_NAMES: [&str; 3] = [
Expand All @@ -23,13 +26,19 @@ static HOLLOW_KNIGHT_NAMES: [&str; 3] = [

pub const SCENE_PATH_SIZE: usize = 64;

const STRING_LEN_OFFSET: u64 = 0x10;
const STRING_CONTENTS_OFFSET: u64 = 0x14;
const STRING_LEN_OFFSET_64: u64 = 0x10;
const STRING_LEN_OFFSET_32: u32 = 0x8;
const STRING_CONTENTS_OFFSET_64: u64 = 0x14;
const STRING_CONTENTS_OFFSET_32: u32 = 0xc;

const LIST_ARRAY_OFFSET: u64 = 0x10;
const ARRAY_LEN_OFFSET: u64 = 0x18;
const ARRAY_CONTENTS_OFFSET: u64 = 0x20;
const POINTER_SIZE: u64 = 8;
const LIST_ARRAY_OFFSET_64: u64 = 0x10;
const LIST_ARRAY_OFFSET_32: u32 = 0x8;
const ARRAY_LEN_OFFSET_64: u64 = 0x18;
const ARRAY_LEN_OFFSET_32: u32 = 0xc;
const ARRAY_CONTENTS_OFFSET_64: u64 = 0x20;
const ARRAY_CONTENTS_OFFSET_32: u32 = 0x10;
const POINTER_SIZE_64: u64 = 8;
const POINTER_SIZE_32: u32 = 4;

const PRE_MENU_INTRO: &str = "Pre_Menu_Intro";
pub const MENU_TITLE: &str = "Menu_Title";
Expand Down Expand Up @@ -926,6 +935,7 @@ pub struct BossSequenceDoorCompletion {
// --------------------------------------------------------

pub struct GameManagerFinder {
is_64_bit: bool,
module: mono::Module,
image: mono::Image,
pointers: Box<GameManagerPointers>,
Expand All @@ -935,8 +945,9 @@ pub struct GameManagerFinder {
}

impl GameManagerFinder {
fn new(module: mono::Module, image: mono::Image) -> GameManagerFinder {
fn new(is_64_bit: bool, module: mono::Module, image: mono::Image) -> GameManagerFinder {
GameManagerFinder {
is_64_bit,
module,
image,
pointers: Box::new(GameManagerPointers::new()),
Expand All @@ -947,6 +958,7 @@ impl GameManagerFinder {
}

pub async fn wait_attach(process: &Process) -> GameManagerFinder {
let is_64_bit = process_is_64_bit(process).unwrap_or(true);
asr::print_message("GameManagerFinder wait_attach: Module wait_attach_auto_detect...");
next_tick().await;
let mut found_module = false;
Expand All @@ -962,7 +974,7 @@ impl GameManagerFinder {
if let Some(image) = module.get_default_image(process) {
asr::print_message("GameManagerFinder wait_attach: got module and image");
next_tick().await;
return GameManagerFinder::new(module, image);
return GameManagerFinder::new(is_64_bit, module, image);
}
next_tick().await;
}
Expand All @@ -975,13 +987,21 @@ impl GameManagerFinder {
}

pub fn get_scene_name(&self, process: &Process) -> Option<String> {
let s = self.pointers.scene_name.deref(process, &self.module, &self.image).ok()?;
read_string_object::<SCENE_PATH_SIZE>(process, s)
let s: Address = if self.is_64_bit {
self.pointers.scene_name.deref::<Address64>(process, &self.module, &self.image).ok()?.into()
} else {
self.pointers.scene_name.deref::<Address32>(process, &self.module, &self.image).ok()?.into()
};
read_string_object::<SCENE_PATH_SIZE>(process, self.is_64_bit, s)
}

pub fn get_next_scene_name(&self, process: &Process) -> Option<String> {
let s = self.pointers.next_scene_name.deref(process, &self.module, &self.image).ok()?;
read_string_object::<SCENE_PATH_SIZE>(process, s)
let s: Address = if self.is_64_bit {
self.pointers.next_scene_name.deref::<Address64>(process, &self.module, &self.image).ok()?.into()
} else {
self.pointers.next_scene_name.deref::<Address32>(process, &self.module, &self.image).ok()?.into()
};
read_string_object::<SCENE_PATH_SIZE>(process, self.is_64_bit, s)
}

pub fn get_game_state(&self, process: &Process) -> Option<i32> {
Expand Down Expand Up @@ -1066,10 +1086,14 @@ impl GameManagerFinder {
}

pub fn get_version_string(&self, process: &Process) -> Option<String> {
let s = [&self.pointers.version_number, &self.player_data_pointers.version].into_iter().find_map(|ptr| {
ptr.deref(process, &self.module, &self.image).ok()
let s: Address = [&self.pointers.version_number, &self.player_data_pointers.version].into_iter().find_map(|ptr| {
if self.is_64_bit {
Some(ptr.deref::<Address64>(process, &self.module, &self.image).ok()?.into())
} else {
Some(ptr.deref::<Address32>(process, &self.module, &self.image).ok()?.into())
}
})?;
read_string_object::<SCENE_PATH_SIZE>(process, s)
read_string_object::<SCENE_PATH_SIZE>(process, self.is_64_bit, s)
}

pub fn get_version_vec(&self, process: &Process) -> Option<Vec<i32>> {
Expand Down Expand Up @@ -1625,8 +1649,12 @@ impl GameManagerFinder {
}

pub fn scenes_grub_rescued(&self, process: &Process) -> Option<Vec<String>> {
let l = self.player_data_pointers.scenes_grub_rescued.deref(process, &self.module, &self.image).ok()?;
read_string_list_object::<SCENE_PATH_SIZE>(process, l)
let l: Address = if self.is_64_bit {
self.player_data_pointers.scenes_grub_rescued.deref::<Address64>(process, &self.module, &self.image).ok()?.into()
} else {
self.player_data_pointers.scenes_grub_rescued.deref::<Address32>(process, &self.module, &self.image).ok()?.into()
};
read_string_list_object::<SCENE_PATH_SIZE>(process, self.is_64_bit, l)
}

pub fn grub_waterways_isma(&self, process: &Process) -> Option<bool> {
Expand All @@ -1642,8 +1670,12 @@ impl GameManagerFinder {
}

pub fn scenes_encountered_dream_plant_c(&self, process: &Process) -> Option<Vec<String>> {
let l = self.player_data_pointers.scenes_encountered_dream_plant_c.deref(process, &self.module, &self.image).ok()?;
read_string_list_object::<SCENE_PATH_SIZE>(process, l)
let l: Address = if self.is_64_bit {
self.player_data_pointers.scenes_encountered_dream_plant_c.deref::<Address64>(process, &self.module, &self.image).ok()?.into()
} else {
self.player_data_pointers.scenes_encountered_dream_plant_c.deref::<Address32>(process, &self.module, &self.image).ok()?.into()
};
read_string_list_object::<SCENE_PATH_SIZE>(process, self.is_64_bit, l)
}

pub fn map_dirtmouth(&self, process: &Process) -> Option<bool> {
Expand Down Expand Up @@ -3094,26 +3126,72 @@ pub async fn wait_attach_hollow_knight<G: StoreGui>(gui: &mut G) -> Process {
}).await
}

fn read_string_object<const N: usize>(process: &Process, a: Address64) -> Option<String> {
let n: u32 = process.read_pointer_path64(a, &[STRING_LEN_OFFSET]).ok()?;
fn process_is_64_bit(process: &Process) -> Option<bool> {
let path = process.get_path().ok()?;
let bytes = file::file_read_all_bytes(path).ok()?;
if bytes.starts_with(&[0x4D, 0x5A]) {
// PE
let mono_addr = ["mono.dll", "mono-2.0-bdwgc.dll"].into_iter().find_map(|mono_name| {
process.get_module_address(mono_name).ok()
})?;
Some(pe::MachineType::read(process, mono_addr)? == pe::MachineType::X86_64)
} else if bytes.starts_with(&[0x7F, 0x45, 0x4C, 0x46]) {
// ELF
None
} else if bytes.starts_with(&[0xFE, 0xED, 0xFA, 0xCE])
| bytes.starts_with(&[0xCE, 0xFA, 0xED, 0xFE]) {
// MachO 32-bit
Some(false)
} else if bytes.starts_with(&[0xFE, 0xED, 0xFA, 0xCF])
| bytes.starts_with(&[0xCF, 0xFA, 0xED, 0xFE]) {
// MachO 64-bit
Some(true)
} else {
None
}
}

fn read_string_object<const N: usize>(process: &Process, is_64_bit: bool, a: Address) -> Option<String> {
let n: u32 = if is_64_bit {
process.read_pointer_path64(a, &[STRING_LEN_OFFSET_64]).ok()?
} else {
process.read_pointer_path32(a, &[STRING_LEN_OFFSET_32]).ok()?
};
if !(n < 2048) { return None; }

Check warning on line 3160 in src/hollow_knight_memory.rs

View workflow job for this annotation

GitHub Actions / Check clippy lints

this boolean expression can be simplified
let w: ArrayWString<N> = process.read_pointer_path64(a, &[STRING_CONTENTS_OFFSET]).ok()?;
let w: ArrayWString<N> = if is_64_bit {
process.read_pointer_path64(a, &[STRING_CONTENTS_OFFSET_64]).ok()?
} else {
process.read_pointer_path32(a, &[STRING_CONTENTS_OFFSET_32]).ok()?
};
if !(w.len() == min(n as usize, N)) { return None; }

Check warning on line 3166 in src/hollow_knight_memory.rs

View workflow job for this annotation

GitHub Actions / Check clippy lints

this boolean expression can be simplified
String::from_utf16(&w.to_vec()).ok()

Check warning on line 3167 in src/hollow_knight_memory.rs

View workflow job for this annotation

GitHub Actions / Check clippy lints

unnecessary use of `to_vec`
}

fn read_string_list_object<const SN: usize>(process: &Process, a: Address64) -> Option<Vec<String>> {
let array_ptr: Address64 = process.read_pointer_path64(a, &[LIST_ARRAY_OFFSET]).ok()?;
let vn: u32 = process.read_pointer_path64(array_ptr, &[ARRAY_LEN_OFFSET]).ok()?;
fn read_string_list_object<const SN: usize>(process: &Process, is_64_bit: bool, a: Address) -> Option<Vec<String>> {
let array_ptr: Address = if is_64_bit {
process.read_pointer_path64::<Address64>(a, &[LIST_ARRAY_OFFSET_64]).ok()?.into()
} else {
process.read_pointer_path32::<Address32>(a, &[LIST_ARRAY_OFFSET_32]).ok()?.into()
};
let vn: u32 = if is_64_bit {
process.read_pointer_path64(array_ptr, &[ARRAY_LEN_OFFSET_64]).ok()?
} else {
process.read_pointer_path32(array_ptr, &[ARRAY_LEN_OFFSET_32]).ok()?
};

let mut v = Vec::with_capacity(vn as usize);
for i in 0..(vn as u64) {
let item_offset = ARRAY_CONTENTS_OFFSET + POINTER_SIZE * i;
let item_ptr: Address64 = process.read_pointer_path64(array_ptr, &[item_offset]).ok()?;
for i in 0..vn {
let item_ptr: Address = if is_64_bit {
let item_offset = ARRAY_CONTENTS_OFFSET_64 + POINTER_SIZE_64 * (i as u64);
process.read_pointer_path64::<Address64>(array_ptr, &[item_offset]).ok()?.into()
} else {
let item_offset: u32 = ARRAY_CONTENTS_OFFSET_32 + POINTER_SIZE_32 * i;
process.read_pointer_path32::<Address32>(array_ptr, &[item_offset]).ok()?.into()
};
if item_ptr.is_null() {
continue;
}
let s = read_string_object::<SN>(process, item_ptr)?;
let s = read_string_object::<SN>(process, is_64_bit, item_ptr)?;
v.push(s);
}
Some(v)
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
extern crate alloc;

mod auto_splitter_settings;
mod file;
mod hollow_knight_memory;
mod legacy_xml;
mod settings_gui;
Expand Down

0 comments on commit 8dbfb2f

Please sign in to comment.