Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod structs;

use structs::ReplayFrame;
use tracy_client::*;

use crate::structs::{
Expand Down Expand Up @@ -316,6 +317,14 @@ fn process_reading_loop(
.timing_point_at(values.playtime as f64)
.beat_len;
}

let frames_addr = p.read_i32(score_base + 0x34)? as usize;
{
let _span = span!("reading replay frames");

let mut frames = p.read_struct_ptr_array::<ReplayFrame>(frames_addr, values.frames.len())?;
values.frames.append(&mut frames);
}

// Placing at the very end cuz we should
// keep up with current_bpm & unstable rate
Expand Down
100 changes: 100 additions & 0 deletions src/memory/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#![allow(clippy::size_of_in_element_count)]

use std::mem::{size_of, align_of};
use std::path::PathBuf;

use super::error::ProcessError;
Expand Down Expand Up @@ -106,6 +107,105 @@ pub trait ProcessTraits where Self: Sized {
buff: &mut [u8]
) -> Result<(), ProcessError>;

fn read_struct<T: Sized>(
&self,
addr: usize
) -> Result<T, ProcessError> {
let mut uninit = std::mem::MaybeUninit::<T>::uninit();
let ptr: *mut u8 = uninit.as_mut_ptr().cast();

let byte_buff = unsafe {
std::slice::from_raw_parts_mut(
ptr,
std::mem::size_of::<T>()
)
};

self.read(addr, byte_buff.len(), byte_buff)?;

Ok(unsafe { uninit.assume_init() })
}

fn read_struct_array<T: Sized>(
&self,
addr: usize,
len: usize
) -> Result<Vec<T>, ProcessError> {
let mut buff: Vec<_> = std::iter::repeat_with(std::mem::MaybeUninit::<T>::uninit)
.take(len)
.collect();

let ptr: *mut u8 = buff.as_mut_ptr().cast();
let byte_buff = unsafe {
std::slice::from_raw_parts_mut(
ptr,
size_of::<T>() * len
)
};

self.read(addr, byte_buff.len(), byte_buff)?;

let ptr: *mut T = buff.as_mut_ptr().cast();
let len = buff.len();
let cap = buff.capacity();

std::mem::forget(buff);

Ok(unsafe { Vec::from_raw_parts(ptr, len, cap) })
}

fn read_struct_ptr_array<T: Sized + Clone>(
&self,
addr: usize,
skip: usize
) -> Result<Vec<T>, ProcessError> {
let mut ptrs = Vec::new();
self.read_u32_array(addr, &mut ptrs)?;

let len = ptrs.len() - skip;

let mut arr = Vec::with_capacity(len);
if len == 0 {
return Ok(arr)
}
let size = size_of::<T>();
let size_with_align = size + align_of::<T>();
let mut chunk: usize = 1;
let mut last_ptr = 0;

// Reading all values one-by-one is slow and wasteful
// but List<> elements are stored in chunks
// so we can find those and read multiple values at once
for (i, ptr) in ptrs.iter().skip(skip).enumerate() {
if i == 0 { last_ptr = *ptr; continue }
// BRUH
if (ptr.overflowing_sub(last_ptr).0) as usize == size_with_align {
chunk += 1;
} else {
if chunk > 1 {
let mut a = self.read_struct_array(last_ptr as usize - size_with_align * (chunk - 1), chunk)?;
arr.append(&mut a);
chunk = 1;
} else {
let a = self.read_struct(last_ptr as usize)?;
arr.push(a);
}
}
last_ptr = *ptr;
}

if chunk > 1 {
let mut a = self.read_struct_array(last_ptr as usize - size_with_align * (chunk - 1), chunk)?;
assert!(a.len() == chunk);
arr.append(&mut a);
} else {
let a = self.read_struct(last_ptr as usize)?;
arr.push(a);
}

Ok(arr)
}

fn read_uleb128(
&self,
mut addr: usize
Expand Down
21 changes: 21 additions & 0 deletions src/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,22 @@ impl From<i16> for BeatmapStatus {
}
}

#[repr(C)]
#[derive(Debug, Clone)]
pub struct ReplayFrame {
pub v_table: u32,
pub mouse_x: f32,
pub mouse_y: f32,
pub mouse_bits: i32,
pub time: i32,
pub mouse_left: u8,
pub mouse_right: u8,
pub mouse_left1: u8,
pub mouse_right1: u8,
pub mouse_left2: u8,
pub mouse_right2: u8,
}

#[derive(Default)]
pub struct StaticAddresses {
pub base: usize,
Expand Down Expand Up @@ -134,6 +150,9 @@ pub struct Values {
pub current_bpm: f64,
pub kiai_now: bool,

#[serde(skip)]
pub frames: Vec<ReplayFrame>,

// Calculated each iteration
pub current_pp: f64,
pub fc_pp: f64,
Expand Down Expand Up @@ -182,6 +201,8 @@ impl Values {
self.bpm = 0.0;
self.current_bpm = 0.0;
self.kiai_now = false;

self.frames.clear();
}

// TODO PR to rosu-pp to add From<u8> trait?
Expand Down