use std::sync::atomic::{AtomicBool, AtomicU32};
pub const SPOOLER_CAPACITY: u32 = 256;
pub const SPOOLER_MASK: u32 = 255;
pub const SPOOLER_TOTAL_SIZE: usize = 8256;
pub const MILLIUNIT_WIDTH: i32 = 640_000;
pub const MILLIUNIT_HEIGHT: i32 = 480_000;
#[repr(C, align(64))]
pub struct SpoolerHeader {
pub is_agent_active: AtomicBool,
pub _pad0: [u8; 3],
pub current_macro_id: AtomicU32,
pub head_index: AtomicU32,
pub tail_index: AtomicU32,
pub _reserved: [u8; 48],
}
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct AgentInputFrame {
pub buttons: u16,
pub _pad: u16,
pub left_stick_x: f32,
pub left_stick_y: f32,
pub right_stick_x: f32,
pub right_stick_y: f32,
pub left_trigger: f32,
pub right_trigger: f32,
pub _pad_end: u32,
}
impl Default for AgentInputFrame {
fn default() -> Self {
Self {
buttons: 0,
_pad: 0,
left_stick_x: 0.0,
left_stick_y: 0.0,
right_stick_x: 0.0,
right_stick_y: 0.0,
left_trigger: 0.0,
right_trigger: 0.0,
_pad_end: 0,
}
}
}
#[derive(Debug, Clone)]
pub struct ActuationReport {
pub macro_id: u32,
pub ticks_executed: u32,
pub buffer_overflow: bool,
pub user_override: bool,
pub override_tick: Option<u32>,
pub final_position: (i32, i32),
pub final_frame: AgentInputFrame,
}
pub struct SpoolerState<'a> {
buf: &'a mut [u8],
}
impl<'a> SpoolerState<'a> {
pub fn new(buf: &'a mut [u8]) -> Self {
assert_eq!(buf.len(), SPOOLER_TOTAL_SIZE);
Self { buf }
}
pub fn zero_init(&mut self) {
self.buf.fill(0);
}
fn read_u32_le(&self, offset: usize) -> u32 {
let bytes: [u8; 4] = self.buf[offset..offset + 4].try_into().unwrap();
u32::from_le_bytes(bytes)
}
fn write_u32_le(&mut self, offset: usize, val: u32) {
self.buf[offset..offset + 4].copy_from_slice(&val.to_le_bytes());
}
pub fn is_agent_active(&self) -> bool {
self.buf[0] != 0
}
pub fn set_agent_active(&mut self, active: bool) {
self.buf[0] = if active { 1 } else { 0 };
}
pub fn current_macro_id(&self) -> u32 {
self.read_u32_le(4)
}
pub fn set_macro_id(&mut self, id: u32) {
self.write_u32_le(4, id);
}
pub fn head_index(&self) -> u32 {
self.read_u32_le(8)
}
pub fn tail_index(&self) -> u32 {
self.read_u32_le(12)
}
fn set_head_index(&mut self, val: u32) {
self.write_u32_le(8, val);
}
fn set_tail_index(&mut self, val: u32) {
self.write_u32_le(12, val);
}
fn frame_offset(index: u32) -> usize {
64 + ((index & SPOOLER_MASK) as usize) * 32
}
fn write_frame_at(&mut self, index: u32, frame: &AgentInputFrame) {
let off = Self::frame_offset(index);
let src = unsafe {
std::slice::from_raw_parts(frame as *const AgentInputFrame as *const u8, 32)
};
self.buf[off..off + 32].copy_from_slice(src);
}
fn read_frame_at(&self, index: u32) -> AgentInputFrame {
let off = Self::frame_offset(index);
let mut frame = AgentInputFrame::default();
let dst = unsafe {
std::slice::from_raw_parts_mut(&mut frame as *mut AgentInputFrame as *mut u8, 32)
};
dst.copy_from_slice(&self.buf[off..off + 32]);
frame
}
pub fn write_macro(&mut self, macro_id: u32, frames: &[AgentInputFrame]) -> Result<(), SpoolerError> {
let head = self.head_index();
let tail = self.tail_index();
let pending = head.wrapping_sub(tail);
if pending.wrapping_add(frames.len() as u32) > SPOOLER_CAPACITY {
return Err(SpoolerError::BufferOverflow {
head,
tail,
attempted: frames.len() as u32,
});
}
for (i, frame) in frames.iter().enumerate() {
self.write_frame_at(head.wrapping_add(i as u32), frame);
}
self.set_macro_id(macro_id);
self.set_agent_active(true);
self.set_head_index(head.wrapping_add(frames.len() as u32));
Ok(())
}
pub fn drain_one(
&mut self,
xinput_buttons: u16,
tick: u32,
report: &mut DrainState,
) -> Option<AgentInputFrame> {
if !self.is_agent_active() {
return None;
}
let head = self.head_index();
let tail = self.tail_index();
if head.wrapping_sub(tail) > SPOOLER_CAPACITY {
report.buffer_overflow = true;
}
if xinput_buttons != 0 && !report.user_override {
report.user_override = true;
report.override_tick = Some(tick);
}
if tail == head {
return None;
}
let frame = self.read_frame_at(tail);
report.ticks_executed += 1;
report.last_frame = frame;
self.set_tail_index(tail.wrapping_add(1));
Some(frame)
}
pub fn finish_macro(&self, state: &DrainState, position: (i32, i32)) -> ActuationReport {
let clamped_x = position.0.clamp(0, MILLIUNIT_WIDTH);
let clamped_y = position.1.clamp(0, MILLIUNIT_HEIGHT);
ActuationReport {
macro_id: self.current_macro_id(),
ticks_executed: state.ticks_executed,
buffer_overflow: state.buffer_overflow,
user_override: state.user_override,
override_tick: state.override_tick,
final_position: (clamped_x, clamped_y),
final_frame: state.last_frame,
}
}
}
#[derive(Debug, Clone)]
pub struct DrainState {
pub ticks_executed: u32,
pub buffer_overflow: bool,
pub user_override: bool,
pub override_tick: Option<u32>,
pub last_frame: AgentInputFrame,
}
impl Default for DrainState {
fn default() -> Self {
Self {
ticks_executed: 0,
buffer_overflow: false,
user_override: false,
override_tick: None,
last_frame: AgentInputFrame::default(),
}
}
}
#[derive(Debug)]
pub enum SpoolerError {
BufferOverflow {
head: u32,
tail: u32,
attempted: u32,
},
#[cfg(target_os = "windows")]
WindowsError(String),
}
impl std::fmt::Display for SpoolerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SpoolerError::BufferOverflow { head, tail, attempted } => {
write!(
f,
"spooler overflow: head={}, tail={}, pending={}, attempted={}",
head,
tail,
head.wrapping_sub(*tail),
attempted
)
}
#[cfg(target_os = "windows")]
SpoolerError::WindowsError(msg) => write!(f, "windows error: {}", msg),
}
}
}
impl std::error::Error for SpoolerError {}
#[cfg(target_os = "windows")]
pub mod shared_mem {
use super::*;
use windows::core::PCWSTR;
use windows::Win32::Foundation::{CloseHandle, HANDLE};
use windows::Win32::System::Memory::{
CreateFileMappingW, MapViewOfFile, OpenFileMappingW, UnmapViewOfFile,
FILE_MAP_ALL_ACCESS, FILE_MAP_READ, PAGE_READWRITE,
};
const SPOOLER_NAME: &str = "Local\\ForgeSpooler";
fn wide_name() -> Vec<u16> {
SPOOLER_NAME.encode_utf16().chain(std::iter::once(0)).collect()
}
pub struct InputSpooler {
handle: HANDLE,
ptr: *mut u8,
}
unsafe impl Send for InputSpooler {}
impl InputSpooler {
pub fn create() -> Result<Self, SpoolerError> {
let name = wide_name();
unsafe {
let handle = CreateFileMappingW(
HANDLE(-1isize as _), None,
PAGE_READWRITE,
0,
SPOOLER_TOTAL_SIZE as u32,
PCWSTR(name.as_ptr()),
)
.map_err(|e| SpoolerError::WindowsError(format!("CreateFileMappingW: {}", e)))?;
let ptr = MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, SPOOLER_TOTAL_SIZE)
.Value as *mut u8;
if ptr.is_null() {
CloseHandle(handle).ok();
return Err(SpoolerError::WindowsError(
"MapViewOfFile returned null".into(),
));
}
std::ptr::write_bytes(ptr, 0, SPOOLER_TOTAL_SIZE);
Ok(Self { handle, ptr })
}
}
pub fn open() -> Result<Self, SpoolerError> {
let name = wide_name();
unsafe {
let handle = OpenFileMappingW(FILE_MAP_READ.0 | FILE_MAP_ALL_ACCESS.0, false, PCWSTR(name.as_ptr()))
.map_err(|e| SpoolerError::WindowsError(format!("OpenFileMappingW: {}", e)))?;
let ptr = MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, SPOOLER_TOTAL_SIZE)
.Value as *mut u8;
if ptr.is_null() {
CloseHandle(handle).ok();
return Err(SpoolerError::WindowsError(
"MapViewOfFile returned null".into(),
));
}
Ok(Self { handle, ptr })
}
}
pub fn state(&mut self) -> SpoolerState<'_> {
let slice = unsafe { std::slice::from_raw_parts_mut(self.ptr, SPOOLER_TOTAL_SIZE) };
SpoolerState::new(slice)
}
}
impl Drop for InputSpooler {
fn drop(&mut self) {
unsafe {
let view = windows::Win32::System::Memory::MEMORY_MAPPED_VIEW_ADDRESS {
Value: self.ptr as *mut _,
};
UnmapViewOfFile(view).ok();
CloseHandle(self.handle).ok();
}
}
}
}
const _: () = {
assert!(std::mem::size_of::<SpoolerHeader>() == 64);
assert!(std::mem::align_of::<SpoolerHeader>() == 64);
assert!(std::mem::size_of::<AgentInputFrame>() == 32);
assert!(SPOOLER_TOTAL_SIZE == 64 + (SPOOLER_CAPACITY as usize) * 32);
};
#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;
fn test_spooler() -> Vec<u8> {
vec![0u8; SPOOLER_TOTAL_SIZE]
}
fn arb_agent_frame() -> impl Strategy<Value = AgentInputFrame> {
(
any::<u16>(), -1.0f32..=1.0f32, -1.0f32..=1.0f32, -1.0f32..=1.0f32, -1.0f32..=1.0f32, 0.0f32..=1.0f32, 0.0f32..=1.0f32, )
.prop_map(|(buttons, lsx, lsy, rsx, rsy, lt, rt)| AgentInputFrame {
buttons,
_pad: 0,
left_stick_x: lsx,
left_stick_y: lsy,
right_stick_x: rsx,
right_stick_y: rsy,
left_trigger: lt,
right_trigger: rt,
_pad_end: 0,
})
}
proptest! {
#[test]
fn prop20_tick_drain_determinism(
frames in proptest::collection::vec(arb_agent_frame(), 1..=256)
) {
let mut buf = test_spooler();
let mut spooler = SpoolerState::new(&mut buf);
spooler.zero_init();
let n = frames.len();
spooler.write_macro(42, &frames).unwrap();
let mut drain_state = DrainState::default();
let mut drained = Vec::new();
for tick in 0..n as u32 {
let frame = spooler.drain_one(0, tick, &mut drain_state);
prop_assert!(frame.is_some(), "drain {} of {} returned None", tick, n);
drained.push(frame.unwrap());
}
prop_assert_eq!(drained.len(), n);
for (i, (got, expected)) in drained.iter().zip(frames.iter()).enumerate() {
prop_assert_eq!(got, expected, "frame {} mismatch", i);
}
let extra = spooler.drain_one(0, n as u32, &mut drain_state);
prop_assert!(extra.is_none(), "(N+1)th drain should be None");
}
}
proptest! {
#[test]
fn prop21_agent_inactive_passthrough(
frames in proptest::collection::vec(arb_agent_frame(), 1..=128)
) {
let mut buf = test_spooler();
let mut spooler = SpoolerState::new(&mut buf);
spooler.zero_init();
spooler.write_macro(1, &frames).unwrap();
spooler.set_agent_active(false);
let mut drain_state = DrainState::default();
for tick in 0..frames.len() as u32 {
let result = spooler.drain_one(0, tick, &mut drain_state);
prop_assert!(result.is_none(),
"drain at tick {} should be None when agent inactive", tick);
}
}
}
proptest! {
#[test]
fn prop22_buffer_overflow_detection(
overflow_amount in 1u32..=100u32
) {
let mut buf = test_spooler();
let mut spooler = SpoolerState::new(&mut buf);
spooler.zero_init();
let tail = 10u32;
let head = tail + SPOOLER_CAPACITY + overflow_amount;
spooler.set_tail_index(tail);
spooler.set_head_index(head);
spooler.set_agent_active(true);
let mut drain_state = DrainState::default();
let _ = spooler.drain_one(0, 0, &mut drain_state);
prop_assert!(drain_state.buffer_overflow,
"overflow not detected: head={}, tail={}, diff={}",
head, tail, head.wrapping_sub(tail));
}
}
proptest! {
#[test]
fn prop23_user_override_detection(
frames in proptest::collection::vec(arb_agent_frame(), 1..=64),
override_tick in 0u32..64u32,
xinput_buttons in 1u16..=u16::MAX,
) {
let mut buf = test_spooler();
let mut spooler = SpoolerState::new(&mut buf);
spooler.zero_init();
let n = frames.len() as u32;
let override_tick = override_tick % n;
spooler.write_macro(99, &frames).unwrap();
let mut drain_state = DrainState::default();
for tick in 0..n {
let buttons = if tick == override_tick { xinput_buttons } else { 0 };
let _ = spooler.drain_one(buttons, tick, &mut drain_state);
}
prop_assert!(drain_state.user_override,
"user override not detected at tick {}", override_tick);
prop_assert_eq!(drain_state.override_tick, Some(override_tick),
"override_tick mismatch");
}
}
proptest! {
#[test]
fn prop28_milliunit_position_bounds(
x in proptest::num::i32::ANY,
y in proptest::num::i32::ANY,
) {
let mut buf = test_spooler();
let mut spooler = SpoolerState::new(&mut buf);
spooler.zero_init();
let frame = AgentInputFrame::default();
spooler.write_macro(1, &[frame]).unwrap();
let drain_state = DrainState::default();
let report = spooler.finish_macro(&drain_state, (x, y));
let (rx, ry) = report.final_position;
prop_assert!(rx >= 0 && rx <= MILLIUNIT_WIDTH,
"x={} out of bounds [0, {}]", rx, MILLIUNIT_WIDTH);
prop_assert!(ry >= 0 && ry <= MILLIUNIT_HEIGHT,
"y={} out of bounds [0, {}]", ry, MILLIUNIT_HEIGHT);
}
}
}