asr/emulator/gba/
mednafen.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
use crate::{file_format::pe, signature::Signature, Address, Address32, Address64, Error, Process};

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct State {
    cached_ewram_pointer: Address,
    cached_iwram_pointer: Address,
    is_64_bit: bool,
}

impl State {
    pub fn find_ram(&mut self, game: &Process) -> Option<[Address; 2]> {
        let main_module_range = super::PROCESS_NAMES
            .iter()
            .filter(|(_, state)| matches!(state, super::State::Mednafen(_)))
            .find_map(|(name, _)| game.get_module_range(name).ok())?;

        self.is_64_bit =
            pe::MachineType::read(game, main_module_range.0) == Some(pe::MachineType::X86_64);

        if self.is_64_bit {
            self.cached_ewram_pointer = {
                const SIG: Signature<13> = Signature::new("48 8B 05 ?? ?? ?? ?? 81 E1 FF FF 03 00");
                let ptr: Address = SIG.scan_process_range(game, main_module_range)? + 3;
                let mut addr: Address = ptr + 0x4 + game.read::<i32>(ptr).ok()?;

                if game.read::<u8>(ptr + 10).ok()? == 0x48 {
                    addr = game.read::<Address64>(addr).ok()?.into();
                    if addr.is_null() {
                        return None;
                    }
                }

                addr
            };

            self.cached_iwram_pointer = {
                const SIG2: Signature<13> =
                    Signature::new("48 8B 05 ?? ?? ?? ?? 81 E1 FF 7F 00 00");
                let ptr: Address = SIG2.scan_process_range(game, main_module_range)? + 3;
                let mut addr: Address = ptr + 0x4 + game.read::<i32>(ptr).ok()?;

                if game.read::<u8>(ptr + 10).ok()? == 0x48 {
                    addr = game.read::<Address64>(addr).ok()?.into();
                    if addr.is_null() {
                        return None;
                    }
                }

                addr
            };

            let ewram = game.read::<Address64>(self.cached_ewram_pointer).ok()?;
            let iwram = game.read::<Address64>(self.cached_iwram_pointer).ok()?;

            Some([ewram.into(), iwram.into()])
        } else {
            self.cached_ewram_pointer = {
                const SIG: Signature<11> = Signature::new("A1 ?? ?? ?? ?? 81 ?? FF FF 03 00");
                let ptr = SIG.scan_process_range(game, main_module_range)?;
                game.read::<Address32>(ptr + 1).ok()?.into()
            };

            self.cached_iwram_pointer = {
                const SIG2: Signature<11> = Signature::new("A1 ?? ?? ?? ?? 81 ?? FF 7F 00 00");
                let ptr = SIG2.scan_process_range(game, main_module_range)?;
                game.read::<Address32>(ptr + 1).ok()?.into()
            };

            let ewram = game.read::<Address32>(self.cached_ewram_pointer).ok()?;
            let iwram = game.read::<Address32>(self.cached_iwram_pointer).ok()?;

            Some([ewram.into(), iwram.into()])
        }
    }

    fn read_pointer(&self, game: &Process, address: Address) -> Result<Address, Error> {
        Ok(match self.is_64_bit {
            true => game.read::<Address64>(address)?.into(),
            false => game.read::<Address32>(address)?.into(),
        })
    }

    pub fn keep_alive(&self, game: &Process, ram: &mut Option<[Address; 2]>) -> bool {
        let ewram = match self.read_pointer(game, self.cached_ewram_pointer) {
            Ok(x) => x,
            _ => return false,
        };

        let iwram = match self.read_pointer(game, self.cached_iwram_pointer) {
            Ok(x) => x,
            _ => return false,
        };

        *ram = Some([ewram, iwram]);
        true
    }

    pub const fn new() -> Self {
        Self {
            cached_ewram_pointer: Address::NULL,
            cached_iwram_pointer: Address::NULL,
            is_64_bit: false,
        }
    }
}