diff --git a/ewext/src/lib.rs b/ewext/src/lib.rs index 1e4212c2..e5052c66 100644 --- a/ewext/src/lib.rs +++ b/ewext/src/lib.rs @@ -1,12 +1,16 @@ use std::{ cell::{LazyCell, RefCell}, ffi::{c_int, c_void}, + mem, sync::LazyLock, }; use iced_x86::Mnemonic; use lua_bindings::{lua_State, Lua51, LUA_GLOBALSINDEX}; -use noita::{NoitaPixelRun, ParticleWorldState}; +use noita::{ + ntypes::{Entity, EntityManager}, + NoitaPixelRun, ParticleWorldState, +}; mod lua_bindings; @@ -29,14 +33,17 @@ thread_local! { struct SavedWorldState { game_global: usize, world_state_entity: usize, - world_state_related: usize, } struct GrabbedGlobals { // These 3 actually point to a pointer. game_global: *mut usize, world_state_entity: *mut usize, - world_state_related: *mut usize, + entity_manager: *mut EntityManager, +} + +struct GrabbedFns { + get_entity: unsafe extern "C" fn(*const EntityManager, u32) -> *mut Entity, } #[derive(Default)] @@ -44,6 +51,7 @@ struct ExtState { particle_world_state: Option, globals: Option, saved_world_state: Option, + fns: Option, } // const EWEXT: [(&'static str, Function); 1] = [("testfn", None)]; @@ -87,11 +95,10 @@ unsafe fn save_world_state() { let mut state = state.borrow_mut(); let game_global = state.globals.as_ref().unwrap().game_global.read(); let world_state_entity = state.globals.as_ref().unwrap().world_state_entity.read(); - let world_state_related = state.globals.as_ref().unwrap().world_state_related.read(); + state.saved_world_state = Some(SavedWorldState { game_global, world_state_entity, - world_state_related, }) }); } @@ -106,9 +113,6 @@ unsafe fn load_world_state() { globals .world_state_entity .write(saved_ws.world_state_entity); - globals - .world_state_related - .write(saved_ws.world_state_related); }); } @@ -154,15 +158,47 @@ unsafe fn grab_addrs(lua: *mut lua_State) { // Pop the last element. LUA.lua_settop(lua, -2); + LUA.lua_getfield(lua, LUA_GLOBALSINDEX, c"EntityGetFilename".as_ptr()); + let base = LUA.lua_tocfunction(lua, -1).unwrap() as *const c_void; + let get_entity = mem::transmute_copy(&addr_grabber::grab_addr_from_instruction( + base, + 0x0079782b - 0x00797570, + Mnemonic::Call, + )); + println!("get_entity addr: 0x{:x}", get_entity as usize); + let entity_manager = + addr_grabber::grab_addr_from_instruction(base, 0x00797821 - 0x00797570, Mnemonic::Mov) + .cast(); + println!("entity_manager addr: 0x{:x}", entity_manager as usize); + // Pop the last element. + LUA.lua_settop(lua, -2); + STATE.with(|state| { state.borrow_mut().globals = Some(GrabbedGlobals { game_global, world_state_entity, - world_state_related: (0x01202ff0 as *mut usize), + entity_manager, }); + state.borrow_mut().fns = Some(GrabbedFns { get_entity }) }); } +unsafe extern "C" fn make_ephemereal(lua: *mut lua_State) -> c_int { + unsafe { + let entity_id = LUA.lua_tointeger(lua, 1) as u32; + println!("Making {} ephemerial", entity_id); + STATE.with(|state| { + let state = state.borrow(); + let entity = dbg!((state.fns.as_ref().unwrap().get_entity)( + state.globals.as_ref().unwrap().entity_manager, + entity_id, + )); + entity.cast::().offset(0x8).cast::().write(0); + }) + } + 0 +} + /// # Safety /// /// Only gets called by lua when loading a module. @@ -180,6 +216,8 @@ pub unsafe extern "C" fn luaopen_ewext0(lua: *mut lua_State) -> c_int { LUA.lua_setfield(lua, -2, c"load_world_state".as_ptr()); LUA.lua_pushcclosure(lua, Some(save_world_state_lua), 0); LUA.lua_setfield(lua, -2, c"save_world_state".as_ptr()); + LUA.lua_pushcclosure(lua, Some(make_ephemereal), 0); + LUA.lua_setfield(lua, -2, c"make_ephemerial".as_ptr()); } println!("Initializing ewext - Ok"); 1 diff --git a/ewext/src/noita.rs b/ewext/src/noita.rs index 99d9ab93..1c9a1170 100644 --- a/ewext/src/noita.rs +++ b/ewext/src/noita.rs @@ -1,6 +1,6 @@ use std::{ffi::c_void, mem}; -mod ntypes; +pub(crate) mod ntypes; #[repr(packed)] pub(crate) struct NoitaPixelRun { @@ -176,4 +176,4 @@ impl ParticleWorldState { self.runner.clear(); runs } -} \ No newline at end of file +} diff --git a/ewext/src/noita/ntypes.rs b/ewext/src/noita/ntypes.rs index 52d0fc59..c48f4171 100644 --- a/ewext/src/noita/ntypes.rs +++ b/ewext/src/noita/ntypes.rs @@ -1,6 +1,6 @@ // Type defs borrowed from NoitaPatcher. -use std::ffi::c_char; +use std::ffi::{c_char, c_void}; pub(crate) const CELLDATA_SIZE: isize = 0x290; @@ -65,3 +65,16 @@ impl Cell { self.material_ptr } } + +#[repr(C)] +pub(crate) struct Entity { + _unknown0: [u8; 8], + _filename_index: u32, + // More stuff, not that relevant currently. +} + +#[repr(C)] +pub(crate) struct EntityManager { + _fld: c_void, + // Unknown +} diff --git a/quant.ew/files/system/ewext_init/ewext_init.lua b/quant.ew/files/system/ewext_init/ewext_init.lua index 5794fda0..21cdedfa 100644 --- a/quant.ew/files/system/ewext_init/ewext_init.lua +++ b/quant.ew/files/system/ewext_init/ewext_init.lua @@ -1,13 +1,15 @@ local ffi = require("ffi") local world_ffi = require("noitapatcher.nsew.world_ffi") +np.CrossCallAdd("make_ephemerial", ewext.make_ephemerial) + local initial_world_state_entity = nil local module = {} function module.on_world_initialized() initial_world_state_entity = GameGetWorldStateEntity() - -- ewext.save_world_state() + ewext.save_world_state() local grid_world = world_ffi.get_grid_world() local chunk_map = grid_world.vtable.get_chunk_map(grid_world) grid_world = tonumber(ffi.cast("intptr_t", grid_world)) @@ -23,8 +25,9 @@ end function module.on_world_update() if GameGetWorldStateEntity() ~= initial_world_state_entity then GamePrint("Whoops WSE is different "..GameGetWorldStateEntity().." "..initial_world_state_entity) + ewext.make_ephemerial(GameGetWorldStateEntity()) -- EntityKill(GameGetWorldStateEntity()) - -- ewext.load_world_state() + ewext.load_world_state() end end