scanmem alternative with concurrent memory scanning
1use rand::Rng;
2use std::{
3 io::{self, Read, Write},
4 sync::mpsc,
5 thread,
6 time::Duration,
7};
8
9// This is a practice program to test if the main programs behaviour works fine
10// This program has been validated and confirmed to work as expected with scanmem
11
12// Structure to hold the "real" game state
13struct GameState {
14 // Internal values that control the actual game state
15 values: Vec<i32>,
16}
17
18// Structure to manage what's displayed on screen
19struct GameDisplay {
20 // Pointers to copies of values shown to the player
21 display_values: Vec<Box<i32>>,
22}
23
24impl GameState {
25 fn new(count: usize, rng: &mut impl Rng) -> Self {
26 let mut values = Vec::with_capacity(count);
27 for _ in 0..count {
28 values.push(rng.gen_range(1..10));
29 }
30 Self { values }
31 }
32
33 fn get_value(&self, index: usize) -> i32 {
34 self.values[index]
35 }
36
37 fn set_value(&mut self, index: usize, value: i32) {
38 self.values[index] = value;
39 }
40}
41
42impl GameDisplay {
43 fn new(count: usize) -> Self {
44 let mut display_values = Vec::with_capacity(count);
45 for _ in 0..count {
46 display_values.push(Box::new(0));
47 }
48 Self { display_values }
49 }
50
51 fn update_from_state(&mut self, state: &GameState) {
52 for (i, val) in state.values.iter().enumerate() {
53 *self.display_values[i] = *val;
54 }
55 }
56
57 fn get_display_ptr(&self, index: usize) -> *const i32 {
58 &*self.display_values[index] as *const i32
59 }
60
61 fn get_displayed_value(&self, index: usize) -> i32 {
62 *self.display_values[index]
63 }
64}
65
66fn main() {
67 let mut rng = rand::thread_rng();
68
69 const LEN: usize = 10;
70
71 let mut game_state = GameState::new(LEN, &mut rng);
72 let mut game_display = GameDisplay::new(LEN);
73
74 game_display.update_from_state(&game_state);
75
76 let (tx, rx) = mpsc::channel();
77
78 thread::spawn(move || {
79 let mut buffer = [0; 1];
80 loop {
81 if let Ok(_) = io::stdin().read_exact(&mut buffer) {
82 let _ = tx.send(());
83 }
84 }
85 });
86
87 let mut refresh_counter = 0;
88
89 loop {
90 print!("\x1B[2J\x1B[1;1H");
91
92 refresh_counter += 1;
93 if refresh_counter >= 3 {
94 refresh_counter = 0;
95 game_display.update_from_state(&game_state);
96 println!("(Display refreshed from game state)");
97 }
98
99 println!(
100 "Memory Scanner Target (Realistic Game Structure) - Press ENTER for new values, Ctrl+C to quit"
101 );
102 println!("Displayed values (what you see on screen):");
103
104 for i in 0..LEN {
105 let display_value = game_display.get_displayed_value(i);
106 let display_ptr = game_display.get_display_ptr(i);
107 println!(
108 "Value {}: {} (displayed at {:p})",
109 i, display_value, display_ptr
110 );
111 }
112
113 println!("\nInternal game state (would be hidden in a real game):");
114 for i in 0..LEN {
115 println!("Value {}: {}", i, game_state.get_value(i));
116 }
117
118 println!("\nPress ENTER to change all values...");
119 io::stdout().flush().unwrap();
120
121 if rx.recv_timeout(Duration::from_secs(1)).is_ok() {
122 for i in 0..LEN {
123 game_state.set_value(i, rng.gen_range(1..10));
124 }
125 game_display.update_from_state(&game_state);
126 refresh_counter = 0;
127 }
128 }
129}