Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
vswarte committed Nov 10, 2024
1 parent 140bb25 commit 1aba3d3
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 74 deletions.
2 changes: 2 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[build]
target = "x86_64-pc-windows-msvc"
17 changes: 11 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,28 @@ codegen-units = 1
opt-level = "z"

[dependencies]
log = "0.4.1"
serde = { version = "1.0.160", features = ["derive"]}
toml = "0.7.2"
broadsword = { git = "https://github.com/vswarte/broadsword.git" }
region = "3.0.2"
pelite = "0.10"
tracing = "0.1"
tracing-subscriber = "0.3"
tracing-panic = "0.1"
tracing-appender = "0.2.3"

[dependencies.windows]
version = "0.48.0"
features = [
"Win32_Foundation",
"Win32_System_LibraryLoader",
]

[dependencies.retour]
version = "0.3"
features = [
"static-detour",
]
features = ["static-detour"]

[dependencies.serde]
version = "1"
features = ["derive"]

[patch.crates-io]
libudis86-sys = { git = 'https://github.com/vars1ty/libudis86-sys.git' }
10 changes: 1 addition & 9 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,11 @@ static mut CONFIG: sync::OnceLock<Config> = sync::OnceLock::new();
#[derive(Deserialize)]
pub struct Config {
pub extension: String,
pub seamless_extension: Option<String>,
}

impl Default for Config {
fn default() -> Self {
Config {
extension: ".mod".to_string(),
seamless_extension: Some(".mod.co2".to_string())
}
Config { extension: ".mod".to_string() }
}
}

Expand All @@ -38,7 +34,3 @@ fn read_config_file() -> Option<Config> {
pub fn get_rewrite_extension() -> &'static str {
get_config_file().extension.as_ref()
}

pub fn get_seamless_rewrite_extension() -> Option<&'static String> {
get_config_file().seamless_extension.as_ref()
}
16 changes: 3 additions & 13 deletions src/file.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::config::{get_rewrite_extension, get_seamless_rewrite_extension};
use crate::config::get_rewrite_extension;

use std::mem::transmute;

Expand All @@ -8,8 +8,6 @@ use windows::{core::{HSTRING, PCWSTR}, Win32::Foundation::HANDLE};

const SAVEGAME_EXTENSION: &str = ".sl2";
const SAVEGAME_BACKUP_EXTENSION: &str = ".sl2.bak";
const SC_SAVEGAME_EXTENSION: &str = ".co2";
const SC_SAVEGAME_BACKUP_EXTENSION: &str = ".co2.bak";

static_detour! {
static CREATE_FILE_W_HOOK: unsafe extern "C" fn(PCWSTR, u32, u32, u64, u32, u32, HANDLE) -> u64;
Expand Down Expand Up @@ -75,15 +73,7 @@ unsafe fn transform_path(path: PCWSTR) -> Option<String> {
path_string.ends_with(SAVEGAME_BACKUP_EXTENSION) {

Some(path_string.replace(SAVEGAME_EXTENSION, get_rewrite_extension()))
} else if path_string.ends_with(SC_SAVEGAME_EXTENSION) ||
path_string.ends_with(SC_SAVEGAME_BACKUP_EXTENSION) {

let extension = get_seamless_rewrite_extension()
.map(|f| f.as_str())
.unwrap_or(get_rewrite_extension());

Some(path_string.replace(SC_SAVEGAME_EXTENSION, extension))
} else {
None
}

None
}
211 changes: 165 additions & 46 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,59 +1,178 @@
mod config;
mod file;
mod regulation;
use std::error::Error;
use std::ffi::CString;

Check warning on line 2 in src/lib.rs

View workflow job for this annotation

GitHub Actions / Cargo build

unused import: `std::ffi::CString`
use std::fs::read_to_string;
use std::mem::transmute;
use std::path;

use std::slice;
use pelite::pattern;
use pelite::pattern::Atom;
use pelite::pe::{imports::Import, Pe, PeFile, PeObject, PeView};

Check warning on line 9 in src/lib.rs

View workflow job for this annotation

GitHub Actions / Cargo build

unused imports: `PeFile`, `PeObject`, `PeView`, `Pe`, and `imports::Import`
use pelite::util::CStr;

Check warning on line 10 in src/lib.rs

View workflow job for this annotation

GitHub Actions / Cargo build

unused import: `pelite::util::CStr`
use retour::static_detour;
use serde::Deserialize;
use tracing_panic::panic_hook;
use windows::core::{s, HSTRING, PCSTR, PCWSTR};

Check warning on line 14 in src/lib.rs

View workflow job for this annotation

GitHub Actions / Cargo build

unused import: `PCSTR`
use windows::Win32::Foundation::HANDLE;
use windows::Win32::System::LibraryLoader::{GetModuleHandleA, GetProcAddress};

use broadsword::{dll, runtime, scanner};
static_detour! {
static CREATE_FILE_W_HOOK: unsafe extern "C" fn(PCWSTR, u32, u32, u64, u32, u32, HANDLE) -> u64;
}

const REGBIN_SAFETY_CHECK_PATCH: &[Atom] = pattern!("");

Check warning on line 22 in src/lib.rs

View workflow job for this annotation

GitHub Actions / Cargo build

constant `REGBIN_SAFETY_CHECK_PATCH` is never used

#[no_mangle]
pub unsafe extern "C" fn DllMain(_base: usize, reason: u32) -> bool {
match reason {
1 => {
std::panic::set_hook(Box::new(panic_hook));
let appender = tracing_appender::rolling::never("./", "altsaves.log");
tracing_subscriber::fmt().with_writer(appender).init();

init().expect("Could not initialize altsaves.");
}
_ => {}
}

#[dll::entrypoint]
pub fn entry(_: usize) -> bool {
broadsword::logging::init("alt-saves.log");
file::hook();
regulation::hook();
true
}

/// Takes an instruction pattern and looks for its location
pub fn match_instruction_pattern(pattern: &str) -> Option<PatternResult> {
// Find .text section details since that's where the code lives
let text_section = runtime::get_module_section_range("eldenring.exe", ".text")
.or_else(|_| runtime::get_module_section_range("start_protected_game.exe", ".text"))
.unwrap();

// Represent search area as a slice
let scan_slice = unsafe {
slice::from_raw_parts(
text_section.start as *const u8,
text_section.end - text_section.start,
)
};

let pattern = scanner::Pattern::from_bit_pattern(pattern).unwrap();

scanner::simple::scan(scan_slice, &pattern)
// TODO: this kinda of rebasing can be done in broadsword probably
.map(|result| PatternResult {
location: text_section.start + result.location,
captures: result.captures.into_iter()
.map(|capture| {
PatternCapture {
location: text_section.start + capture.location,
bytes: capture.bytes,
fn init() -> Result<(), Box<dyn Error>> {
// Read config from file or get default
let config: Config = path::absolute(path::PathBuf::from("./altsaves.toml"))
.ok()
.and_then(|p| read_to_string(p).ok())
.and_then(|c| toml::from_str(&c).ok())
.unwrap_or_default();

// Find CreateFileW. For some reason I couldn't use pelite to parse the IAT so :shrug:
let create_file_w = unsafe {
let kernel32 = GetModuleHandleA(s!("kernel32"))?;
GetProcAddress(kernel32, s!("CreateFileW")).expect("Could find CreateFileW")
} as usize;

// Hook the CreateFileW so we can swap out paths where required
unsafe {
CREATE_FILE_W_HOOK
.initialize(
transmute(create_file_w),
move |path, access, share, security, disposition, attributes, template| {
let result_path = if path.as_wide().ends_with(&[0x2E, 0x73, 0x6C, 0x32]) {
let path = path.to_string().unwrap();
RequestedPath::Rewritten(HSTRING::from(format!(
"{}{}",
&path[..path.len() - 4],
&config.extension
)))
} else {
RequestedPath::Untouched(path)
};

tracing::info!("Rewritten {:?}", result_path);

unsafe {

Check warning on line 73 in src/lib.rs

View workflow job for this annotation

GitHub Actions / Cargo build

unnecessary `unsafe` block
CREATE_FILE_W_HOOK.call(
path,
access,
share,
security,
disposition,
attributes,
template,
)
}
})
.collect()
})
},
)?
.enable()?;
}

tracing::info!("CreateFileW {create_file_w:x}");
Ok(())
}

#[derive(Debug)]
pub struct PatternResult {
location: usize,
captures: Vec<PatternCapture>,
#[derive(Deserialize)]
pub struct Config {
pub extension: String,
}

impl Default for Config {
fn default() -> Self {
Config {
extension: ".mod".to_string(),
}
}
}

#[derive(Debug)]
pub struct PatternCapture {
location: usize,
bytes: Vec<u8>,
pub enum RequestedPath {
Untouched(PCWSTR),
Rewritten(HSTRING),
}

impl RequestedPath {
fn as_pcwstr(&self) -> PCWSTR {

Check warning on line 113 in src/lib.rs

View workflow job for this annotation

GitHub Actions / Cargo build

method `as_pcwstr` is never used
match self {
RequestedPath::Untouched(p) => *p,
RequestedPath::Rewritten(s) => {

Check warning on line 116 in src/lib.rs

View workflow job for this annotation

GitHub Actions / Cargo build

unused variable: `s`
todo!()
}
}
}
}

// #[dll::entrypoint]
// pub fn entry(_: usize) -> bool {
// broadsword::logging::init("alt-saves.log");
//
// // file::hook();
// // regulation::hook();
// true
// }

// #[derive(Deserialize)]
// pub struct Config {
// pub extension: String,
// }
//
// /// Takes an instruction pattern and looks for its location
// pub fn match_instruction_pattern(pattern: &str) -> Option<PatternResult> {
// // Find .text section details since that's where the code lives
// let text_section = runtime::get_module_section_range("eldenring.exe", ".text")
// .or_else(|_| runtime::get_module_section_range("start_protected_game.exe", ".text"))
// .unwrap();
//
// // Represent search area as a slice
// let scan_slice = unsafe {
// slice::from_raw_parts(
// text_section.start as *const u8,
// text_section.end - text_section.start,
// )
// };
//
// let pattern = scanner::Pattern::from_bit_pattern(pattern).unwrap();
// scanner::simple::scan(scan_slice, &pattern)
// // TODO: this kinda of rebasing can be done in broadsword probably
// .map(|result| PatternResult {
// location: text_section.start + result.location,
// captures: result.captures.into_iter()
// .map(|capture| {
// PatternCapture {
// location: text_section.start + capture.location,
// bytes: capture.bytes,
// }
// })
// .collect()
// })
// }
//
// #[derive(Debug)]
// pub struct PatternResult {
// location: usize,
// captures: Vec<PatternCapture>,
// }
//
// #[derive(Debug)]
// pub struct PatternCapture {
// location: usize,
// bytes: Vec<u8>,
// }

0 comments on commit 1aba3d3

Please sign in to comment.