mirror of
https://github.com/IntQuant/noita_entangled_worlds.git
synced 2025-12-08 06:09:46 +00:00
use heapwalk to estimate unk data sizes
This commit is contained in:
parent
040058fa66
commit
092ed2bc49
1 changed files with 140 additions and 43 deletions
|
|
@ -2,6 +2,8 @@ use crate::ExtState;
|
||||||
use noita_api::addr_grabber::Globals;
|
use noita_api::addr_grabber::Globals;
|
||||||
use noita_api::noita::types::*;
|
use noita_api::noita::types::*;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::ops::DerefMut;
|
||||||
|
use windows::Win32::Foundation::HANDLE;
|
||||||
use windows::Win32::System::Memory::*;
|
use windows::Win32::System::Memory::*;
|
||||||
pub fn check_globals(_: &mut ExtState) -> eyre::Result<()> {
|
pub fn check_globals(_: &mut ExtState) -> eyre::Result<()> {
|
||||||
let vftables = include_str!("../vftables.txt");
|
let vftables = include_str!("../vftables.txt");
|
||||||
|
|
@ -30,9 +32,24 @@ pub fn check_globals(_: &mut ExtState) -> eyre::Result<()> {
|
||||||
map.insert(addr, (name, size));
|
map.insert(addr, (name, size));
|
||||||
}
|
}
|
||||||
let globals = Globals::default();
|
let globals = Globals::default();
|
||||||
|
let len = unsafe { GetProcessHeaps(&mut Vec::new()) };
|
||||||
|
let mut heaps = vec![HANDLE::default(); len as usize];
|
||||||
|
unsafe { GetProcessHeaps(&mut heaps) };
|
||||||
|
let mut heapset = HashMap::new();
|
||||||
|
let mut entry: PROCESS_HEAP_ENTRY = PROCESS_HEAP_ENTRY::default();
|
||||||
|
for heap in heaps {
|
||||||
|
while unsafe { HeapWalk(heap, &mut entry) }.is_ok() {
|
||||||
|
heapset.insert(
|
||||||
|
entry.lpData.cast::<usize>().cast_const(),
|
||||||
|
entry.cbData as usize,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[allow(unused)]
|
||||||
let print = |addr: *const usize| {
|
let print = |addr: *const usize| {
|
||||||
check_global(addr, &map, &mut Vec::new(), 0, None).print(0, 0);
|
check_global(addr, &map, &mut Vec::new(), 0, None, &heapset).print(0, 0, None);
|
||||||
};
|
};
|
||||||
|
#[allow(unused)]
|
||||||
macro_rules! maybe_ptr_get {
|
macro_rules! maybe_ptr_get {
|
||||||
(true, $name:tt) => {
|
(true, $name:tt) => {
|
||||||
unsafe { $name.cast::<*const usize>().as_ref().copied().unwrap() }
|
unsafe { $name.cast::<*const usize>().as_ref().copied().unwrap() }
|
||||||
|
|
@ -50,8 +67,10 @@ pub fn check_globals(_: &mut ExtState) -> eyre::Result<()> {
|
||||||
stringify!($t),
|
stringify!($t),
|
||||||
size_of::<$t>(),
|
size_of::<$t>(),
|
||||||
0,
|
0,
|
||||||
|
&heapset,
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
.print(0, 0);
|
.print(0, 0, None);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
print(globals.entity_manager.cast());
|
print(globals.entity_manager.cast());
|
||||||
|
|
@ -65,22 +84,6 @@ pub fn check_globals(_: &mut ExtState) -> eyre::Result<()> {
|
||||||
print_unk!(globals.game_global, GameGlobal, true);
|
print_unk!(globals.game_global, GameGlobal, true);
|
||||||
print_unk!(globals.component_manager, ComponentSystemManager, false);
|
print_unk!(globals.component_manager, ComponentSystemManager, false);
|
||||||
print_unk!(globals.world_state, Entity, true);
|
print_unk!(globals.world_state, Entity, true);
|
||||||
let ptr = globals.game_global().m_cell_factory as *const CellFactory;
|
|
||||||
print_unk!(ptr, CellFactory, false);
|
|
||||||
let ptr = globals.game_global().m_textures as *const Textures;
|
|
||||||
print_unk!(ptr, Textures, false);
|
|
||||||
let ptr = globals.game_global().m_game_world as *const GameWorld;
|
|
||||||
print_unk!(ptr, GameWorld, false);
|
|
||||||
let ptr = unsafe {
|
|
||||||
*(**globals.game_global)
|
|
||||||
.m_grid_world
|
|
||||||
.chunk_map
|
|
||||||
.chunk_array
|
|
||||||
.iter()
|
|
||||||
.find(|a| !a.is_null())
|
|
||||||
.unwrap()
|
|
||||||
} as *const Chunk;
|
|
||||||
print_unk!(ptr, Chunk, false);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn check_global(
|
fn check_global(
|
||||||
|
|
@ -89,15 +92,16 @@ fn check_global(
|
||||||
addrs: &mut Vec<*const usize>,
|
addrs: &mut Vec<*const usize>,
|
||||||
entry: usize,
|
entry: usize,
|
||||||
parent: Option<&str>,
|
parent: Option<&str>,
|
||||||
|
heaps: &HashMap<*const usize, usize>,
|
||||||
) -> Elem {
|
) -> Elem {
|
||||||
if let Some(n) = addrs.iter().position(|n| *n == reference) {
|
if let Some(n) = addrs.iter().position(|n| *n == reference) {
|
||||||
return Elem::Recursive(addrs.len() - n, entry);
|
return Elem::Recursive(addrs.len() - n, entry);
|
||||||
}
|
}
|
||||||
addrs.push(reference);
|
addrs.push(reference);
|
||||||
unsafe {
|
unsafe {
|
||||||
if !in_range(reference) {
|
let Some(addr_size) = in_range(reference, heaps) else {
|
||||||
return Elem::Usize(entry);
|
return Elem::Usize(entry);
|
||||||
}
|
};
|
||||||
let Some(table) = reference.as_ref() else {
|
let Some(table) = reference.as_ref() else {
|
||||||
return Elem::Usize(entry);
|
return Elem::Usize(entry);
|
||||||
};
|
};
|
||||||
|
|
@ -105,15 +109,33 @@ fn check_global(
|
||||||
if Some(name.as_ref()) == parent {
|
if Some(name.as_ref()) == parent {
|
||||||
Elem::VFTable(entry)
|
Elem::VFTable(entry)
|
||||||
} else {
|
} else {
|
||||||
Elem::from_addr(reference, map, addrs, name, *size, entry)
|
Elem::from_addr(
|
||||||
|
reference,
|
||||||
|
map,
|
||||||
|
addrs,
|
||||||
|
name,
|
||||||
|
if addr_size != usize::MAX {
|
||||||
|
addr_size
|
||||||
|
} else {
|
||||||
|
*size
|
||||||
|
},
|
||||||
|
entry,
|
||||||
|
heaps,
|
||||||
|
true,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} else if in_range(table)
|
} else if let Some(size) = in_range(table, heaps)
|
||||||
|
&& ({ size == 4 || size == usize::MAX })
|
||||||
&& let Some(inner) = (table as *const usize)
|
&& let Some(inner) = (table as *const usize)
|
||||||
.cast::<*const usize>()
|
.cast::<*const usize>()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.copied()
|
.copied()
|
||||||
{
|
{
|
||||||
Elem::Ref(Box::new(check_global(inner, map, addrs, entry, None)))
|
Elem::Ref(Box::new(check_global(
|
||||||
|
inner, map, addrs, entry, None, heaps,
|
||||||
|
)))
|
||||||
|
} else if addr_size != usize::MAX && parent.is_none() {
|
||||||
|
Elem::from_addr(reference, map, addrs, "Unk", addr_size, entry, heaps, false)
|
||||||
} else {
|
} else {
|
||||||
Elem::Usize(entry)
|
Elem::Usize(entry)
|
||||||
}
|
}
|
||||||
|
|
@ -123,31 +145,90 @@ pub enum Elem {
|
||||||
Ref(Box<Elem>),
|
Ref(Box<Elem>),
|
||||||
Struct(Struct, usize),
|
Struct(Struct, usize),
|
||||||
VFTable(usize),
|
VFTable(usize),
|
||||||
|
Array(Box<Elem>, usize, usize),
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
Usize(usize),
|
Usize(usize),
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
Recursive(usize, usize),
|
Recursive(usize, usize),
|
||||||
}
|
}
|
||||||
|
impl PartialEq for Elem {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
match (self, other) {
|
||||||
|
(Elem::Array(r1, n1, _), Elem::Array(r2, n2, _)) => r1 == r2 && n1 == n2,
|
||||||
|
(Elem::Ref(r1), Elem::Ref(r2)) => r1 == r2,
|
||||||
|
(Elem::Struct(r1, _), Elem::Struct(r2, _)) => r1 == r2,
|
||||||
|
(Elem::VFTable(_), Elem::VFTable(_)) => true,
|
||||||
|
(Elem::Usize(_), Elem::Usize(_)) => true,
|
||||||
|
(Elem::Recursive(r1, _), Elem::Recursive(r2, _)) => r1 == r2,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
impl Elem {
|
impl Elem {
|
||||||
|
pub fn array_eq(&mut self, other: &Self) -> bool {
|
||||||
|
match self {
|
||||||
|
Elem::Array(e, _, _) => e.deref_mut() == other,
|
||||||
|
e => e == other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn entry(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
Elem::Array(_, _, e) => *e,
|
||||||
|
Elem::Ref(r) => r.entry(),
|
||||||
|
Elem::Struct(_, e) => *e,
|
||||||
|
Elem::VFTable(e) => *e,
|
||||||
|
Elem::Usize(e) => *e,
|
||||||
|
Elem::Recursive(_, e) => *e,
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn from_addr(
|
pub fn from_addr(
|
||||||
reference: *const usize,
|
mut reference: *const usize,
|
||||||
map: &HashMap<usize, (String, usize)>,
|
map: &HashMap<usize, (String, usize)>,
|
||||||
addrs: &mut Vec<*const usize>,
|
addrs: &mut Vec<*const usize>,
|
||||||
name: &str,
|
name: &str,
|
||||||
size: usize,
|
size: usize,
|
||||||
entry: usize,
|
entry: usize,
|
||||||
|
heaps: &HashMap<*const usize, usize>,
|
||||||
|
skip: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut s = Struct::new(name, size);
|
let mut s = Struct::new(name, size);
|
||||||
|
if skip {
|
||||||
|
s.fields.push(Elem::VFTable(0));
|
||||||
|
reference = unsafe { reference.add(1) };
|
||||||
|
}
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
while i < size / 4 {
|
while i < if skip {
|
||||||
|
(size / 4).saturating_sub(1)
|
||||||
|
} else {
|
||||||
|
size / 4
|
||||||
|
} {
|
||||||
let len = addrs.len();
|
let len = addrs.len();
|
||||||
let e = check_global(unsafe { reference.add(i) }, map, addrs, i, Some(name));
|
let e = check_global(
|
||||||
|
unsafe { reference.add(i) },
|
||||||
|
map,
|
||||||
|
addrs,
|
||||||
|
if skip { i + 1 } else { i },
|
||||||
|
Some(name),
|
||||||
|
heaps,
|
||||||
|
);
|
||||||
if let Elem::Struct(_, size) = &e {
|
if let Elem::Struct(_, size) = &e {
|
||||||
i += (*size).max(1);
|
i += (*size).max(1);
|
||||||
} else {
|
} else {
|
||||||
i += 1
|
i += 1
|
||||||
}
|
}
|
||||||
s.fields.push(e);
|
if let Some(last) = s.fields.last_mut()
|
||||||
|
&& last.array_eq(&e)
|
||||||
|
{
|
||||||
|
if let Elem::Array(_, n, _) = last {
|
||||||
|
*n += 1;
|
||||||
|
} else {
|
||||||
|
let entry = last.entry();
|
||||||
|
s.fields.push(Elem::Array(Box::new(e), 2, entry));
|
||||||
|
s.fields.pop();
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
s.fields.push(e);
|
||||||
|
}
|
||||||
while len < addrs.len() {
|
while len < addrs.len() {
|
||||||
addrs.pop();
|
addrs.pop();
|
||||||
}
|
}
|
||||||
|
|
@ -155,17 +236,18 @@ impl Elem {
|
||||||
Elem::Struct(s, entry)
|
Elem::Struct(s, entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Default)]
|
#[derive(Default, PartialEq)]
|
||||||
pub struct Struct {
|
pub struct Struct {
|
||||||
name: String,
|
name: String,
|
||||||
size: usize,
|
size: usize,
|
||||||
fields: Vec<Elem>,
|
fields: Vec<Elem>,
|
||||||
}
|
}
|
||||||
impl Elem {
|
impl Elem {
|
||||||
fn print(&self, n: usize, count: usize) {
|
fn print(&self, n: usize, count: usize, array: Option<(usize, usize)>) {
|
||||||
match self {
|
match self {
|
||||||
Elem::Ref(r) => r.print(n, count + 1),
|
Elem::Ref(r) => r.print(n, count + 1, array),
|
||||||
Elem::Struct(s, e) => s.print(n, count, *e),
|
Elem::Array(r, k, e) => r.print(n, count + 1, Some((*k, *e))),
|
||||||
|
Elem::Struct(s, e) => s.print(n, count, *e, array),
|
||||||
Elem::Usize(_) => {
|
Elem::Usize(_) => {
|
||||||
//noita_api::print!("{}[{e}]{}usize", " ".repeat(n), "&".repeat(count))
|
//noita_api::print!("{}[{e}]{}usize", " ".repeat(n), "&".repeat(count))
|
||||||
}
|
}
|
||||||
|
|
@ -173,7 +255,13 @@ impl Elem {
|
||||||
//noita_api::print!("{}[{e}]{}recursive<{k}>", " ".repeat(n), "&".repeat(count))
|
//noita_api::print!("{}[{e}]{}recursive<{k}>", " ".repeat(n), "&".repeat(count))
|
||||||
}
|
}
|
||||||
Elem::VFTable(e) => {
|
Elem::VFTable(e) => {
|
||||||
noita_api::print!("{}[{e}]{}VFTable", " ".repeat(n), "&".repeat(count))
|
noita_api::print!(
|
||||||
|
"{}[{}]{}VFTable{}",
|
||||||
|
" ".repeat(n),
|
||||||
|
array.map(|a| a.1).unwrap_or(*e),
|
||||||
|
"&".repeat(count),
|
||||||
|
array.map(|a| format!("[{}]", a.0)).unwrap_or(String::new())
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -186,22 +274,24 @@ impl Struct {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn print(&self, n: usize, count: usize, entry: usize) {
|
fn print(&self, n: usize, count: usize, entry: usize, array: Option<(usize, usize)>) {
|
||||||
noita_api::print!(
|
noita_api::print!(
|
||||||
"{}[{entry}]{}{}<{}>",
|
"{}[{}]{}{}<{}>{}",
|
||||||
" ".repeat(n),
|
" ".repeat(n),
|
||||||
|
array.map(|a| a.1).unwrap_or(entry),
|
||||||
"&".repeat(count),
|
"&".repeat(count),
|
||||||
self.name,
|
self.name,
|
||||||
self.size
|
self.size,
|
||||||
|
array.map(|a| format!("[{}]", a.0)).unwrap_or(String::new())
|
||||||
);
|
);
|
||||||
for f in self.fields.iter() {
|
for f in self.fields.iter() {
|
||||||
f.print(n + 1, 0);
|
f.print(n + 1, 0, None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn in_range(reference: *const usize) -> bool {
|
fn in_range(reference: *const usize, heaps: &HashMap<*const usize, usize>) -> Option<usize> {
|
||||||
if reference.is_null() {
|
if reference.is_null() {
|
||||||
return false;
|
return None;
|
||||||
}
|
}
|
||||||
let mut mbi = MEMORY_BASIC_INFORMATION::default();
|
let mut mbi = MEMORY_BASIC_INFORMATION::default();
|
||||||
if unsafe {
|
if unsafe {
|
||||||
|
|
@ -212,15 +302,22 @@ fn in_range(reference: *const usize) -> bool {
|
||||||
)
|
)
|
||||||
} == 0
|
} == 0
|
||||||
{
|
{
|
||||||
return false;
|
return None;
|
||||||
}
|
}
|
||||||
if mbi.State != MEM_COMMIT {
|
if mbi.State != MEM_COMMIT {
|
||||||
return false;
|
return None;
|
||||||
}
|
}
|
||||||
let protect = mbi.Protect;
|
let protect = mbi.Protect;
|
||||||
if protect == PAGE_NOACCESS || protect == PAGE_GUARD {
|
if protect == PAGE_NOACCESS || protect == PAGE_GUARD {
|
||||||
return false;
|
return None;
|
||||||
|
}
|
||||||
|
let region_end = (mbi.BaseAddress as usize).saturating_add(mbi.RegionSize);
|
||||||
|
if region_end < 4 + reference as usize {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if let Some(size) = heaps.get(&reference) {
|
||||||
|
Some(*size)
|
||||||
|
} else {
|
||||||
|
Some(usize::MAX)
|
||||||
}
|
}
|
||||||
//noita_api::print!("{:?} {:?}", reference, mbi);
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue