From 8c6b2ff3889bfe09b41ac2dec146b31f6ec2172e Mon Sep 17 00:00:00 2001 From: Centri3 <114838443+Centri3@users.noreply.github.com> Date: Thu, 29 Dec 2022 13:34:14 -0600 Subject: [PATCH] v1.0.0 - refactor and fully new method --- .gitignore | 2 +- Cargo.lock | 255 ++------------------- Cargo.toml | 26 +-- build/seeds.txt | 1 + crates/seedloc-handler/Cargo.toml | 15 ++ crates/seedloc-handler/src/lib.rs | 211 +++++++++++++++++ crates/seedloc/Cargo.toml | 9 + crates/seedloc/src/main.rs | 214 +++++++++++++++++ crates/seedloc/src/seed.rs | 75 ++++++ rustfmt.toml | 3 + src/handler.rs | 57 ----- src/main.rs | 369 ------------------------------ src/seed.rs | 97 -------- 13 files changed, 555 insertions(+), 779 deletions(-) create mode 100644 build/seeds.txt create mode 100644 crates/seedloc-handler/Cargo.toml create mode 100644 crates/seedloc-handler/src/lib.rs create mode 100644 crates/seedloc/Cargo.toml create mode 100644 crates/seedloc/src/main.rs create mode 100644 crates/seedloc/src/seed.rs create mode 100644 rustfmt.toml delete mode 100644 src/handler.rs delete mode 100644 src/main.rs delete mode 100644 src/seed.rs diff --git a/.gitignore b/.gitignore index c41cc9e..ea8c4bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -/target \ No newline at end of file +/target diff --git a/Cargo.lock b/Cargo.lock index 5b69e79..473fcde 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,16 +3,10 @@ version = 3 [[package]] -name = "autocfg" -version = "1.1.0" +name = "bytemuck" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "aaa3a8d9a1ca92e282c96a32d6511b695d7d994d1d102ba85d279f9b2756947f" [[package]] name = "cfg-if" @@ -20,118 +14,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "core-foundation" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" -dependencies = [ - "core-foundation-sys 0.6.2", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" - -[[package]] -name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - -[[package]] -name = "core-graphics" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f15b3cb55687886a6b66953123621e5a1529a91a01666d646fb64baa13f900f0" -dependencies = [ - "bitflags", - "core-foundation", - "foreign-types", - "libc", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348" -dependencies = [ - "autocfg", - "cfg-if", - "crossbeam-utils", - "memoffset", - "scopeguard", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "either" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" - -[[package]] -name = "enigo" -version = "0.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce8d7672e87b3155fd5e8a9226276da5c833e15bc879c7b98a78f743b67814" -dependencies = [ - "core-graphics", - "libc", - "objc", - "pkg-config", - "unicode-segmentation", - "winapi", -] - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "getrandom" version = "0.2.8" @@ -143,66 +25,11 @@ dependencies = [ "wasi", ] -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "libc" -version = "0.2.137" +version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" - -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] - -[[package]] -name = "ntapi" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc51db7b362b205941f71232e56c625156eb9a929f8cf74a428fd5bc094a4afc" -dependencies = [ - "winapi", -] - -[[package]] -name = "num_cpus" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "objc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" -dependencies = [ - "malloc_buf", -] +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "once_cell" @@ -210,12 +37,6 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" -[[package]] -name = "pkg-config" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" - [[package]] name = "ppv-lite86" version = "0.2.17" @@ -253,66 +74,23 @@ dependencies = [ ] [[package]] -name = "rayon" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" -dependencies = [ - "autocfg", - "crossbeam-deque", - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "num_cpus", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "speng-seedloc" +name = "seedloc" version = "0.1.0" dependencies = [ - "enigo", "rand", - "sysinfo", - "windows", + "seedloc-handler", + "winput", ] [[package]] -name = "sysinfo" -version = "0.26.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c375d5fd899e32847b8566e10598d6e9f1d9b55ec6de3cdf9e7da4bdc51371bc" +name = "seedloc-handler" +version = "0.0.0" dependencies = [ - "cfg-if", - "core-foundation-sys 0.8.3", - "libc", - "ntapi", + "bytemuck", "once_cell", - "rayon", - "winapi", + "windows", ] -[[package]] -name = "unicode-segmentation" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -397,3 +175,12 @@ name = "windows_x86_64_msvc" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" + +[[package]] +name = "winput" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd4bec39938e0ae68b300e2a4197b6437f13d53d1c146c6e297e346a71d5dde9" +dependencies = [ + "winapi", +] diff --git a/Cargo.toml b/Cargo.toml index 0a1d7b6..e492696 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,21 +1,5 @@ -[package] -name = "speng-seedloc" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -enigo = "0.0.14" -rand = "0.8.5" -sysinfo = "0.26.4" -windows = { version = "0.43.0", features = [ - "Win32_Foundation", - "Win32_System_Diagnostics_Debug", - "Win32_System_Memory", - "Win32_System_ProcessStatus", - "Win32_System_Threading", -] } - -[profile.dev] -overflow-checks = false +[workspace] +members = [ + "crates/seedloc", + "crates/seedloc-handler", +] diff --git a/build/seeds.txt b/build/seeds.txt new file mode 100644 index 0000000..59ab233 --- /dev/null +++ b/build/seeds.txt @@ -0,0 +1 @@ +-1175671629 299793970 -808771962 -573742822 628935187 \ No newline at end of file diff --git a/crates/seedloc-handler/Cargo.toml b/crates/seedloc-handler/Cargo.toml new file mode 100644 index 0000000..ecdf34b --- /dev/null +++ b/crates/seedloc-handler/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "seedloc-handler" +version = "0.0.0" +edition = "2021" + +[dependencies] +bytemuck = { version = "1.12.3", features = ["min_const_generics"] } +once_cell = "1.16.0" +windows = { version = "0.43.0", features = [ + "Win32_Foundation", + "Win32_System_Diagnostics_Debug", + "Win32_System_Memory", + "Win32_System_ProcessStatus", + "Win32_System_Threading", +] } diff --git a/crates/seedloc-handler/src/lib.rs b/crates/seedloc-handler/src/lib.rs new file mode 100644 index 0000000..3b2d384 --- /dev/null +++ b/crates/seedloc-handler/src/lib.rs @@ -0,0 +1,211 @@ +use std::io::Write; + +use { + bytemuck::Pod, + once_cell::sync::Lazy, + std::{env, fs::File, io::Error, mem, path::PathBuf, process::Command}, + windows::Win32::{ + Foundation, + Foundation::{HANDLE, HINSTANCE, MAX_PATH}, + System::{Diagnostics::Debug, Memory, ProcessStatus, Threading}, + }, +}; + +pub static HANDLER: Lazy = Lazy::new(Handler::init); + +#[repr(transparent)] +#[derive(Debug)] +pub struct Handler { + inner: HANDLE, +} + +// Not sure whether this is needed or not +impl Drop for Handler { + fn drop(&mut self) { + unsafe { Foundation::CloseHandle(self.inner) }; + } +} + +impl Handler { + fn init() -> Self { + let processes = [0u32; 1024usize]; + let mut bytes_needed = 0u32; + + unsafe { + ProcessStatus::K32EnumProcesses( + processes.as_ptr() as _, + mem::size_of_val(&processes) as _, + &mut bytes_needed, + ); + } + + for process in &processes[..bytes_needed as usize / mem::size_of::()] { + let mut name = [0u16; MAX_PATH as _]; + let handle = match unsafe { + Threading::OpenProcess( + Threading::PROCESS_QUERY_INFORMATION + | Threading::PROCESS_VM_OPERATION + | Threading::PROCESS_VM_READ + | Threading::PROCESS_VM_WRITE, + false, + *process, + ) + } { + Ok(ph) => ph, + Err(_) => continue, + }; + + unsafe { ProcessStatus::K32GetModuleFileNameExW(handle, None, &mut name) }; + + if !String::from_utf16_lossy(&name).contains("SpaceEngine.exe") { + continue; + } + + return Self { inner: handle }; + } + + panic!("failed to find process: SpaceEngine.exe, maybe try opening it!"); + } + + #[must_use] + pub fn exe(&self) -> PathBuf { + let mut exe = [0u16; MAX_PATH as _]; + + unsafe { ProcessStatus::K32GetModuleFileNameExW(self.inner, None, &mut exe) }; + + PathBuf::from(String::from_utf16_lossy(&exe).replace('\0', "")) + } + + #[must_use] + pub fn base(&self) -> usize { + self.base_of("SpaceEngine.exe") + } + + #[must_use] + pub fn base_of(&self, module_name: impl AsRef) -> usize { + let modules = [0usize; 1024usize]; + let mut bytes_needed = 0u32; + + unsafe { + ProcessStatus::K32EnumProcessModules( + self.inner, + modules.as_ptr() as _, + mem::size_of_val(&modules) as _, + &mut bytes_needed, + ); + } + + // This should convert both a String and &str to &str + let module_name = module_name.as_ref(); + + for module in &modules[..bytes_needed as usize / mem::size_of::()] { + let mut name = [0u16; MAX_PATH as _]; + + unsafe { + ProcessStatus::K32GetModuleBaseNameW( + self.inner, + HINSTANCE(*module as _), + &mut name, + ); + } + + if String::from_utf16_lossy(&name).contains(module_name) { + return *module; + } + } + + panic!("failed to find module: {module_name}"); + } + + #[must_use] + pub fn read_bytes(&self, base: usize, size: usize) -> Vec { + let buffer = vec![0u8; size]; + + unsafe { + if !Debug::ReadProcessMemory( + self.inner, + base as _, + buffer.as_ptr() as _, + buffer.len(), + None, + ) + .as_bool() + { + panic!( + "failed to read bytes: {base:x}, {size:x}, {}", + Error::last_os_error() + ) + } + } + + buffer + } + + /// Convenience function to call `read_bytes` with any type implementing + /// `Pod`, rather than `Vec`. + #[must_use] + pub fn read(&self, base: usize) -> T { + *bytemuck::from_bytes::(&self.read_bytes(base, mem::size_of::())) + } + + pub fn write_bytes(&self, base: usize, buffer: &[u8]) { + let mut old_protection = Memory::PAGE_PROTECTION_FLAGS(0u32); + + unsafe { + if !Memory::VirtualProtectEx( + self.inner, + base as _, + buffer.len(), + Memory::PAGE_EXECUTE_READWRITE, + &mut old_protection, + ) + .as_bool() + { + // Pretty sure I don't need to duplicate this to the other 2 functions here. + panic!( + "failed to write bytes: {base:x}, {buffer:x?}, {}", + Error::last_os_error() + ) + } + + Debug::WriteProcessMemory( + self.inner, + base as _, + buffer.as_ptr().cast(), + buffer.len(), + None, + ); + + Memory::VirtualProtectEx( + self.inner, + base as _, + buffer.len(), + old_protection, + &mut old_protection, + ); + + // This is entirely useless if some variable's being modified instead of + // executable code, but I don't care. + Debug::FlushInstructionCache(self.inner, Some(base as _), buffer.len()); + } + } + + /// Convenience function to call `write_bytes` with any type implementing + /// `Pod`, rather than `&[u8]`. + pub fn write(&self, buffer: T, base: usize) { + self.write_bytes(base, bytemuck::bytes_of(&buffer)); + } + + /// Create and run an SE script. + pub fn run_script(&self, name: impl AsRef, buffer: impl AsRef) { + let path = env::current_dir().unwrap().join(name.as_ref()); + + // Write bytes to script + File::create(path.clone()) + .unwrap() + .write_all(buffer.as_ref().as_bytes()) + .unwrap(); + + Command::new(self.exe()).arg(path).spawn().unwrap(); + } +} diff --git a/crates/seedloc/Cargo.toml b/crates/seedloc/Cargo.toml new file mode 100644 index 0000000..7b3547d --- /dev/null +++ b/crates/seedloc/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "seedloc" +version = "0.1.0" +edition = "2021" + +[dependencies] +seedloc-handler = { path = "../seedloc-handler" } +rand = "0.8.5" +winput = "0.2.5" diff --git a/crates/seedloc/src/main.rs b/crates/seedloc/src/main.rs new file mode 100644 index 0000000..9abc00a --- /dev/null +++ b/crates/seedloc/src/main.rs @@ -0,0 +1,214 @@ +mod seed; + +use { + rand::prelude::*, + seed::seed, + seedloc_handler::HANDLER, + std::{ + error::Error, + fs::{self, File}, + io::Write, + thread, + time::{Duration, Instant}, + }, + winput::{press, release, Button, Mouse}, +}; + +// Address to the code of the selected object. +// Example: RS 0-3-397-1581-20880-7-556321-30 A3. Go visit it yourself! (: +const SELECTED_OBJECT_CODE: usize = 0x19a9e40usize; +// Pointer to the parameters of the selected object. +const SELECTED_OBJECT_POINTER: usize = 0x19a9ec0usize; +// Pointer to stars the Star browser found. seedloc will read this +const STAR_BROWSER_STARS_POINTER: usize = 0x1024440usize; +// Number of stars the Star browser has found +const STAR_BROWSER_STAR_LIST_LEN: usize = 0x102410cusize; +// Max stars the Star browser will search +const STAR_BROWSER_STAR_LIST_MAX: usize = 0x1024430usize; +// Whether the Star browser's currently searching; 0 = searching, 1 = idle +const STAR_BROWSER_SEARCHING: usize = 0x104a181usize; +// SE's current GUI scale. +const GUI_SCALE: usize = 0xe69434; + +// Offsets from SELECTED_OBJECT_POINTER +const GALAXY_TYPE: usize = 0x8usize; +const GALAXY_SIZE: usize = 0x20usize; + +// Coordinates to some GUI elements. +// These are always the same whenever ran, despite being initialized at runtime. +const STAR_BROWSER_SEARCH_BUTTON: usize = 0x1025a78usize; +const STAR_BROWSER_CLEAR_BUTTON: usize = 0x1025d60usize; + +// Coordinates offsets +const GENERIC_OFFSET: i32 = 0xai32; +const WINDOWED_OFFSET: i32 = 0x14i32; + +fn main() -> Result<(), Box> { + let mut finds = File::create("finds.log")?; + + let seeds = fs::read_to_string("seeds.txt")? + .split(' ') + .map(|s| s.parse::().unwrap()) + .collect::>(); + + let mut rng = thread_rng(); + // This is easier to write 1000 times. + let base = HANDLER.base(); + + loop { + // Select RG 0-3-397-1581, this is so we can reset the code of the currently + // selected object. If we don't do this, it'll select nothing. + HANDLER.run_script("select_rg_397.se", "Select \"RG 0-3-397-1581\""); + + // Not entirely sure how long we need to sleep for, but we need to give SE time + // to update the currently selected object (Or anything else). + thread::sleep(Duration::from_millis(160u64)); + + loop { + // Generate a random galaxy + let level = rng.gen_range(1u32..9u32); + let block = rng.gen_range(0u32..8u32.pow(level)); + let number = rng.gen_range(0u32..2500u32); + + // Write galaxy code to memory + HANDLER.write(level, base + SELECTED_OBJECT_CODE + 0x4); + HANDLER.write(block, base + SELECTED_OBJECT_CODE + 0x8); + HANDLER.write(number, base + SELECTED_OBJECT_CODE + 0x10); + + thread::sleep(Duration::from_millis(160u64)); + + let selected_object = HANDLER.read::(base + SELECTED_OBJECT_POINTER); + + // This could mean that the galaxy doesn't exist, or my code is too fast. Skip. + // Also, skip any galaxies with a type of E/Irr or isn't 10% of max size + if selected_object == 0usize + // Ellipticals check + || (1u32..=8u32).contains(&HANDLER.read(selected_object + GALAXY_TYPE)) + // Irregular check + || HANDLER.read::(selected_object + GALAXY_TYPE) == 16u32 + // Size check + || HANDLER.read::(selected_object + GALAXY_SIZE) <= 5000.0f32 + { + continue; + } + + // Goto the selected galaxy. If we've gotten this far, it's a desired galaxy + HANDLER.run_script("goto_galaxy.se", "Goto { DistRad 0 Time 0 }"); + + thread::sleep(Duration::from_millis(160u64)); + + // This is still vile. + let scale = HANDLER.read::(base + GUI_SCALE); + let search = ( + (HANDLER.read::(base + STAR_BROWSER_SEARCH_BUTTON) * scale) as i32 + + GENERIC_OFFSET, + (HANDLER.read::(base + STAR_BROWSER_SEARCH_BUTTON + 0x4) * scale) as i32 + + GENERIC_OFFSET + + WINDOWED_OFFSET, + ); + let clear = ( + (HANDLER.read::(base + STAR_BROWSER_CLEAR_BUTTON) * scale) as i32 + + GENERIC_OFFSET, + (HANDLER.read::(base + STAR_BROWSER_CLEAR_BUTTON + 0x4) * scale) as i32 + + GENERIC_OFFSET + + WINDOWED_OFFSET, + ); + + Mouse::set_position(clear.0, clear.1)?; + + for _ in 0u32..=2u32 { + press(Button::Left); + + thread::sleep(Duration::from_millis(32u64)); + + release(Button::Left); + } + + // Not entirely sure why, but I must wait a second or so for SE to catch up. + thread::sleep(Duration::from_secs(1u64)); + + Mouse::set_position(search.0, search.1)?; + + press(Button::Left); + + thread::sleep(Duration::from_millis(160u64)); + + release(Button::Left); + + let star_list_max = HANDLER.read::(base + STAR_BROWSER_STAR_LIST_MAX); + let star_list = HANDLER.read::(base + STAR_BROWSER_STARS_POINTER); + + let now = Instant::now(); + // Wait until systems found == max systems found, or until 10s has passed + while HANDLER.read::(base + STAR_BROWSER_STAR_LIST_LEN) < star_list_max + || now.elapsed().as_secs_f32() > 10.0f32 + {} + + // Stop search *hopefully* before it begins. + HANDLER.write(1u8, base + STAR_BROWSER_SEARCHING); + + thread::sleep(Duration::from_millis(160u64)); + + for i in 0usize..HANDLER.read::(base + STAR_BROWSER_STAR_LIST_LEN) as _ { + let star = star_list + i * 0x78; + + // Get the code of the system + let galaxy_universe_sector = HANDLER.read::(star + 0x10); + let galaxy_level = HANDLER.read::(star + 0x14); + let galaxy_block = HANDLER.read::(star + 0x18); + let galaxy_number = HANDLER.read::(star + 0x20); + let cluster_number = HANDLER.read::(star + 0x24); + let galaxy_sector = HANDLER.read::(star + 0x28); + let star_level = HANDLER.read::(star + 0x2C); + let star_block = HANDLER.read::(star + 0x30); + let star_number = HANDLER.read::(star + 0x38); + let unflipped_x = HANDLER.read::<[u8; 16usize]>(star + 0x40); + let unflipped_y = HANDLER.read::<[u8; 16usize]>(star + 0x50); + let unflipped_z = HANDLER.read::<[u8; 16usize]>(star + 0x60); + + // We must flip the lower and upper 8-bytes of the star's coordinates + let x = u128::from_le_bytes( + [&unflipped_x[8usize..], &unflipped_x[..8usize]] + .concat() + .try_into() + .unwrap(), + ); + let y = u128::from_le_bytes( + [&unflipped_y[8usize..], &unflipped_y[..8usize]] + .concat() + .try_into() + .unwrap(), + ); + let z = u128::from_le_bytes( + [&unflipped_z[8usize..], &unflipped_z[..8usize]] + .concat() + .try_into() + .unwrap(), + ); + + // Construct the code to the system. This is vile, but it works! + let code = format!( + "{} {}-{}-{star_level}-{star_block}-{star_number}", + if cluster_number == -1i32 { "RS" } else { "RSC" }, + if galaxy_universe_sector == -1i32 { + galaxy_number.to_string() + } else { + format_args!("{galaxy_universe_sector}-{galaxy_level}-{galaxy_block}-{galaxy_number}").to_string() + }, + if cluster_number == -1i32 { + galaxy_sector.to_string() + } else { + cluster_number.to_string() + } + ); + + // Calculate the seed of the system, derived from the star's coordinates + let seed = seed((x, y, z)); + + if seeds.contains(&seed) { + finds.write_all(format!("CODE: {code}, SEED: {seed}").as_bytes())?; + } + } + } + } +} diff --git a/crates/seedloc/src/seed.rs b/crates/seedloc/src/seed.rs new file mode 100644 index 0000000..6997815 --- /dev/null +++ b/crates/seedloc/src/seed.rs @@ -0,0 +1,75 @@ +// Ghidra's Internal Decompiler Functions + +fn concat_44(x: u32, y: u32) -> u64 { + let mut bytes = vec![]; + + for byte in &y.to_le_bytes() { + bytes.push(*byte); + } + + for byte in &x.to_le_bytes() { + bytes.push(*byte); + } + + u64::from_le_bytes(bytes.try_into().unwrap()) +} + +// Decompiled by Ghidra, thanks! +pub fn hash(point: u128) -> f64 { + let upper = (point >> 64u32) as u64; + let lower = point as u64; + + let mut f = upper; + let mut s = lower; + + if upper > i64::MAX as u64 { + f = !upper; + s = !lower + 1u64; + if lower == 0u64 { + f += 1u64; + } + } + + let hashed = (s & u32::MAX as u64) as f64 * 8.271806125530277e-25f64 + + (f & u32::MAX as u64) as f64 * 1.52587890625e-05f64 + + (s >> 32u64) as f64 * 3.552713678800501e-15f64 + + (f >> 32u64) as f64 * 65536.0f64; + + if upper > i64::MAX as u64 { + return -hashed; + } + + hashed +} + +// Also decompiled by Ghidra, thanks again! +pub fn seed(coords: (u128, u128, u128)) -> i32 { + let mut d_var1: f64; + + let d_var10 = 100.0; + let d_var11 = 2147483647.0f64; + let d_var12 = 0.5f64; + + d_var1 = hash(coords.0) * d_var10; + let u_var4 = (u64::from_le_bytes(d_var1.to_le_bytes())) as u32; + let u_var7 = (u64::from_le_bytes(d_var1.to_le_bytes()) >> 0x20u64) as u32; + d_var1 = f64::floor(d_var1 / d_var11 * d_var12 + d_var12); + let d_var2 = (d_var1 + d_var1) * d_var11; + + d_var1 = hash(coords.1) * d_var10; + let u_var5 = (u64::from_le_bytes(d_var1.to_le_bytes())) as u32; + let u_var8 = (u64::from_le_bytes(d_var1.to_le_bytes()) >> 0x20u64) as u32; + d_var1 = f64::floor(d_var1 / d_var11 * d_var12 + d_var12); + let d_var3 = (d_var1 + d_var1) * d_var11; + + d_var1 = hash(coords.2) * d_var10; + let u_var6 = (u64::from_le_bytes(d_var1.to_le_bytes())) as u32; + let u_var9 = (u64::from_le_bytes(d_var1.to_le_bytes()) >> 0x20u64) as u32; + d_var1 = f64::floor(d_var1 / d_var11 * d_var12 + d_var12); + + (f64::from_le_bytes(concat_44(u_var8, u_var5).to_le_bytes()) - d_var3) as i32 * 0xe35adi32 + + (f64::from_le_bytes(concat_44(u_var9, u_var6).to_le_bytes()) + - (d_var1 + d_var1) * d_var11) as i32 + * -0x2309fb + + (f64::from_le_bytes(concat_44(u_var7, u_var4).to_le_bytes()) - d_var2) as i32 * 0x28842 +} diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..f9eb353 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,3 @@ +format_strings = true +imports_granularity = "One" +wrap_comments = true diff --git a/src/handler.rs b/src/handler.rs deleted file mode 100644 index 8199d7c..0000000 --- a/src/handler.rs +++ /dev/null @@ -1,57 +0,0 @@ -use std::mem; -use sysinfo::{PidExt, ProcessExt, System, SystemExt}; -use windows::Win32::{ - Foundation::{self, HANDLE}, - System::{ProcessStatus, Threading}, -}; - -#[derive(Clone, Copy)] -pub struct Handler { - pub handle: HANDLE, - pub base_address: usize, -} - -impl Handler { - pub fn new(sys: &mut System) -> Self { - sys.refresh_all(); - - let handle = match unsafe { - Threading::OpenProcess( - Threading::PROCESS_QUERY_INFORMATION - | Threading::PROCESS_VM_OPERATION - | Threading::PROCESS_VM_READ - | Threading::PROCESS_VM_WRITE, - false, - match sys.processes_by_exact_name("SpaceEngine.exe").nth(0usize) { - Some(pc) => pc.pid().as_u32(), - None => todo!(), - }, - ) - } { - Ok(ph) => ph, - Err(_) => todo!(), - }; - - let modules = vec![0usize; 1024usize]; - - unsafe { - ProcessStatus::K32EnumProcessModules( - handle, - modules.as_ptr() as _, - mem::size_of::<[usize; 1024usize]>() as _, - &mut 0u32, - ) - }; - - let base_address = modules[0usize]; - - Self { - handle, - base_address, - } - } - - pub fn close(&self) { - unsafe { Foundation::CloseHandle(self.handle) }; - } -} diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 7c8b9e6..0000000 --- a/src/main.rs +++ /dev/null @@ -1,369 +0,0 @@ -mod handler; -mod seed; - -use enigo::{Enigo, MouseButton, MouseControllable}; -use handler::Handler; -use rand::{rngs::ThreadRng, Rng}; -use std::{env, fs::File, io::Write, mem, process::Command, thread, time::Duration}; -use sysinfo::{ProcessExt, System, SystemExt}; -use windows::Win32::System::Diagnostics::Debug; - -const SEEDS: [i32; 4usize] = [-1175671629i32, 299793970i32, -808771962i32, -573742822i32]; - -const GLOBAL_COORDS: usize = 0xe27740usize; -const LOCAL_COORDS: usize = 0x1984e54usize; -const SYSTEMS_FOUND: usize = 0x1022dc8usize; -const SEARCH_BUTTON_COORDS: usize = 0x1024728usize; -const FILTER_SORT_COORDS: usize = 0x1026720usize; -const SELECTED_OBJECT_CODE: usize = 0x19a8ab0usize; -const SELECTED_OBJECT_POINTER: usize = 0x19a8b30usize; - -fn get_global(handler: Handler) -> (u64, u64, u64) { - let x = unsafe { - let buffer = [0u8; 8usize]; - - Debug::ReadProcessMemory( - handler.handle, - (handler.base_address + GLOBAL_COORDS) as _, - buffer.as_ptr() as _, - mem::size_of::<[u8; 8usize]>(), - None, - ); - - u64::from_le_bytes(buffer) - }; - - let y = unsafe { - let buffer = [0u8; 8usize]; - - Debug::ReadProcessMemory( - handler.handle, - (handler.base_address + GLOBAL_COORDS + 0x10) as _, - buffer.as_ptr() as _, - mem::size_of::<[u8; 8usize]>(), - None, - ); - - u64::from_le_bytes(buffer) - }; - - let z = unsafe { - let buffer = [0u8; 8usize]; - - Debug::ReadProcessMemory( - handler.handle, - (handler.base_address + GLOBAL_COORDS + 0x20) as _, - buffer.as_ptr() as _, - mem::size_of::<[u8; 8usize]>(), - None, - ); - - u64::from_le_bytes(buffer) - }; - - (x, y, z) -} - -fn create_run_file(name: &str, contents: &str, sys: &mut System) { - sys.refresh_all(); - - let mut file = File::create(name).unwrap(); - writeln!(file, "{}", contents).unwrap(); - - let mut path = env::current_dir().unwrap(); - path.push(name); - - Command::new( - sys.processes_by_exact_name("SpaceEngine.exe") - .nth(0usize) - .unwrap() - .exe(), - ) - .arg(path) - .spawn() - .unwrap(); -} - -fn goto_galaxy(handler: Handler, mut rng: &mut ThreadRng, mut sys: &mut System) { - loop { - let octree_level = rng.gen_range(0u32..=4u32); - let octree_block = rng.gen_range(0u32..=8u32.pow(octree_level)); - let number = rng.gen_range(0u32..=2500u32); - - create_run_file("select_rg_397.se", "Select \"RG 0-3-397-1581\"", &mut sys); - - thread::sleep(Duration::from_millis(500u64)); - - unsafe { - Debug::WriteProcessMemory( - handler.handle, - (handler.base_address + SELECTED_OBJECT_CODE + 0x4) as _, - octree_level.to_le_bytes().as_ptr() as _, - mem::size_of::() as _, - None, - ); - - Debug::WriteProcessMemory( - handler.handle, - (handler.base_address + SELECTED_OBJECT_CODE + 0x8) as _, - octree_block.to_le_bytes().as_ptr() as _, - mem::size_of::() as _, - None, - ); - - Debug::WriteProcessMemory( - handler.handle, - (handler.base_address + SELECTED_OBJECT_CODE + 0x10) as _, - number.to_le_bytes().as_ptr() as _, - mem::size_of::() as _, - None, - ); - }; - - thread::sleep(Duration::from_millis(100u64)); - - let buffer = [0u8; 4usize]; - - unsafe { - Debug::ReadProcessMemory( - handler.handle, - (handler.base_address + SELECTED_OBJECT_CODE + 0x1D8) as _, - buffer.as_ptr() as _, - mem::size_of::<[u8; 4usize]>(), - None, - ) - }; - - if u32::from_le_bytes(buffer) != 0 { - continue; - } - - thread::sleep(Duration::from_millis(100u64)); - - let buffer = [0u8; 8usize]; - - unsafe { - Debug::ReadProcessMemory( - handler.handle, - (handler.base_address + SELECTED_OBJECT_POINTER) as _, - buffer.as_ptr() as _, - mem::size_of::<[u8; 8usize]>(), - None, - ) - }; - - let selected_object_address = usize::from_le_bytes(buffer); - - let buffer = [0u8; 4usize]; - - unsafe { - Debug::ReadProcessMemory( - handler.handle, - (selected_object_address + 0x20) as _, - buffer.as_ptr() as _, - mem::size_of::<[u8; 4usize]>(), - None, - ) - }; - - if f32::from_le_bytes(buffer) != 50000.0f32 { - continue; - } - - let lon = rng.gen_range(-180.0f64..180.0f64); - let dist_rad = rng.gen_range(0.0f64..0.25f64); - - create_run_file( - "goto_galaxy.se", - format!("Goto {{ Lat {} Time 0 }}", lon).as_str(), - &mut sys, - ); - - thread::sleep(Duration::from_millis(100u64)); - - create_run_file("center_galaxy.se", "Goto { Lon 90 Time 0 }", &mut sys); - - thread::sleep(Duration::from_millis(100u64)); - - create_run_file( - "randomize_galaxy.se", - format!("Goto {{ DistRad {} Time 0 }}", dist_rad).as_str(), - sys, - ); - - break; - } -} - -fn main() { - let mut sys = System::new_all(); - sys.refresh_all(); - - let handler = Handler::new(&mut sys); - let mut rng = rand::thread_rng(); - - loop { - goto_galaxy(handler, &mut rng, &mut sys); - - create_run_file("select_sol.se", "Select \"Solar System\"", &mut sys); - - create_run_file("follow.se", "Follow", &mut sys); - - thread::sleep(Duration::from_millis(100u64)); - - let global = get_global(handler); - - for _ in 1u32..10000000u32 { - let x = rng.gen_range(global.0 - 0x1329999u64..global.0 + 0x1329999u64); - let y = rng.gen_range(global.1 - 0x1329999u64..global.1 + 0x1329999u64); - let z = rng.gen_range(global.2 - 0x1329999u64..global.2 + 0x1329999u64); - - let seed = seed::seed((x, y, z)); - - if SEEDS.contains(&seed) { - let coords = unsafe { - Debug::WriteProcessMemory( - handler.handle, - (handler.base_address + GLOBAL_COORDS) as _, - x.to_le_bytes().as_ptr() as _, - mem::size_of::(), - None, - ); - - Debug::WriteProcessMemory( - handler.handle, - (handler.base_address + LOCAL_COORDS + 0x10) as _, - y.to_le_bytes().as_ptr() as _, - mem::size_of::(), - None, - ); - - Debug::WriteProcessMemory( - handler.handle, - (handler.base_address + LOCAL_COORDS + 0x20) as _, - z.to_le_bytes().as_ptr() as _, - mem::size_of::(), - None, - ); - - let buffer = [0u8; 4usize]; - - Debug::ReadProcessMemory( - handler.handle, - (handler.base_address + SEARCH_BUTTON_COORDS) as _, - buffer.as_ptr() as _, - mem::size_of::(), - None, - ); - - let x = f32::from_le_bytes(buffer); - - let buffer = [0u8; 4usize]; - - Debug::ReadProcessMemory( - handler.handle, - (handler.base_address + SEARCH_BUTTON_COORDS + 0x4) as _, - buffer.as_ptr() as _, - mem::size_of::(), - None, - ); - - let y = f32::from_le_bytes(buffer); - - (x, y) - }; - - let mut enigo = Enigo::new(); - - enigo.mouse_move_to(coords.0 as i32 + 10i32, coords.1 as i32 + 30i32); - - thread::sleep(Duration::from_millis(1000u64)); - - let coords = unsafe { - let buffer = [0u8; 4usize]; - - Debug::ReadProcessMemory( - handler.handle, - (handler.base_address + FILTER_SORT_COORDS) as _, - buffer.as_ptr() as _, - mem::size_of::(), - None, - ); - - let x = f32::from_le_bytes(buffer); - - let buffer = [0u8; 4usize]; - - Debug::ReadProcessMemory( - handler.handle, - (handler.base_address + FILTER_SORT_COORDS + 0x4) as _, - buffer.as_ptr() as _, - mem::size_of::(), - None, - ); - - let y = f32::from_le_bytes(buffer); - - (x, y) - }; - - enigo.mouse_move_to(coords.0 as i32 + 10i32, coords.1 as i32 + 30i32); - - let systems_found = unsafe { - let buffer = [0u8; 4usize]; - - Debug::ReadProcessMemory( - handler.handle, - (handler.base_address + SYSTEMS_FOUND) as _, - buffer.as_ptr() as _, - mem::size_of::(), - None, - ); - - u32::from_le_bytes(buffer) - }; - - if systems_found != 0u32 { - enigo.mouse_move_relative(0i32, 0x25i32); - - enigo.mouse_click(MouseButton::Left); - - thread::sleep(Duration::from_millis(100u64)); - - let system_seed = unsafe { - let buffer = [0u8; 8usize]; - - Debug::ReadProcessMemory( - handler.handle, - (handler.base_address + 0x19a8b38usize) as _, - buffer.as_ptr() as _, - 8usize, - None, - ); - - let address = usize::from_le_bytes(buffer); - let buffer = [0u8; 4usize]; - - Debug::ReadProcessMemory( - handler.handle, - (address + 0x170usize) as _, - buffer.as_ptr() as _, - 4usize, - None, - ); - - i32::from_le_bytes(buffer) - }; - - if seed == system_seed { - create_run_file( - "print_to_log.se", - format!("Log \"Found seed: {}\"\nPrintNames true", seed).as_str(), - &mut sys, - ); - } - } - } - } - } -} diff --git a/src/seed.rs b/src/seed.rs deleted file mode 100644 index a1cdc74..0000000 --- a/src/seed.rs +++ /dev/null @@ -1,97 +0,0 @@ -#![allow(dead_code)] - -// Ghidra's Internal Decompiler Functions - -fn concat_44(x: u32, y: u32) -> u64 { - let mut bytes = vec![]; - - for byte in y.to_le_bytes().iter() { - bytes.push(*byte); - } - - for byte in x.to_le_bytes().iter() { - bytes.push(*byte); - } - - u64::from_le_bytes(bytes.try_into().unwrap()) -} - -// Decompiled by Ghidra, thanks! -fn hash(param_1: u64, param_2: u64) -> f64 { - let u_var1: u64; - let mut u_var2: u64; - let mut u_var3: u64; - let mut d_var4: f64; - - u_var1 = param_1; - u_var3 = param_2; - u_var2 = u_var1; - - if 0x7fffffffffffffff < u_var1 { - u_var2 = !u_var1; - u_var3 = !u_var3 + 1u64; - if u_var3 == 0u64 { - u_var2 += 1u64; - } - } - - d_var4 = (u_var3 >> 0x20u64) as f64 * 3.552713678800501e-15f64 - + (u_var3 & 0xffffffff) as f64 * 8.271806125530277e-25f64 - + (u_var2 & 0xffffffff) as f64 * 1.52587890625e-05f64 - + (u_var2 >> 0x20u64) as f64 * 65536.0f64; - - if 0x7fffffffffffffff < u_var1 { - d_var4 = f64::from_le_bytes( - (u64::from_le_bytes(d_var4.to_le_bytes()) ^ 0x8000000000000000u64).to_le_bytes(), - ); - } - - d_var4 -} - -// Decompilated also by Ghidra, thanks again! -pub fn seed(coords: (u64, u64, u64)) -> i32 { - loop { - let mut d_var1: f64; - let d_var2: f64; - let d_var3: f64; - let u_var4: u32; - let u_var5: u32; - let u_var6: u32; - let u_var7: u32; - let u_var8: u32; - let u_var9: u32; - let d_var10: f64; - let d_var11: f64; - let d_var12: f64; - - d_var10 = 100.0; - d_var11 = 2147483647.0f64; - d_var12 = 0.5f64; - - d_var1 = hash(coords.0, 0u64) * d_var10; - u_var4 = (u64::from_le_bytes(d_var1.to_le_bytes())) as u32; - u_var7 = (u64::from_le_bytes(d_var1.to_le_bytes()) >> 0x20u64) as u32; - d_var1 = f64::floor(d_var1 / d_var11 * d_var12 + d_var12); - d_var2 = (d_var1 + d_var1) * d_var11; - - d_var1 = hash(coords.1, 0u64) * d_var10; - u_var5 = (u64::from_le_bytes(d_var1.to_le_bytes())) as u32; - u_var8 = (u64::from_le_bytes(d_var1.to_le_bytes()) >> 0x20u64) as u32; - d_var1 = f64::floor(d_var1 / d_var11 * d_var12 + d_var12); - d_var3 = (d_var1 + d_var1) * d_var11; - - d_var1 = hash(coords.2, 0u64) * d_var10; - u_var6 = (u64::from_le_bytes(d_var1.to_le_bytes())) as u32; - u_var9 = (u64::from_le_bytes(d_var1.to_le_bytes()) >> 0x20u64) as u32; - d_var1 = f64::floor(d_var1 / d_var11 * d_var12 + d_var12); - - return (f64::from_le_bytes(concat_44(u_var8, u_var5).to_le_bytes()) - d_var3) as i32 - * 0xe35adi32 - + (f64::from_le_bytes(concat_44(u_var9, u_var6).to_le_bytes()) - - (d_var1 + d_var1) * d_var11) as i32 - * -0x2309fb - + (f64::from_le_bytes(concat_44(u_var7, u_var4).to_le_bytes()) - d_var2) as i32 - * 0x28842; - } -}