Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

API to hook in external tooling #215

Open
TimeTravelPenguin opened this issue Jul 31, 2024 · 6 comments
Open

API to hook in external tooling #215

TimeTravelPenguin opened this issue Jul 31, 2024 · 6 comments
Labels
proposal This issue contains a feature or change proposal.

Comments

@TimeTravelPenguin
Copy link

Is there currently support for hooking in an external tool that can control user input? I have been learning Rust and would like to get into playing with algorithms that can perform optimised searches.

I am not sure if this would even be possible as this kind of lower-level systems programming is not my typical domain of interest, so I am not entirely certain how you would typically go about doing it.

As such, I was wondering if it were possible to allow for external tools to connect and serve as a controller, being able to monitor memory, savestates, etc., allowing for autonomous control of Mupen by a 3rd party tool.

Depending on the complexity of Mupen, I assume it would be possible, perhaps through a plugin interface, where the external app would be fed state and would return controller/application input.

If something like this can already be done, I would love to know more about where to look.

Thanks

@TimeTravelPenguin TimeTravelPenguin added the proposal This issue contains a feature or change proposal. label Jul 31, 2024
@Aurumaker72
Copy link
Collaborator

Hi TTP,

there is no official API for full procedural control of Mupen.

However, depending on your requirements, you might be able to get away with generating hotkey presses via SendInput to trigger various actions.

If you only need control over the game input, you can write a custom input plugin in Rust.

@TimeTravelPenguin
Copy link
Author

TimeTravelPenguin commented Aug 1, 2024

Thanks for your reply.

I'll have to think about what I want to do. I've always wanted to experiment with machine learning in something like SM64, but I don't really want to use vision as the primary input, so I may need to work out how to monitor memory.

If I do get around to this project, I may pop into Discord sometime after November if I need help.

Is there any reference or documentation for Mupen?

Edit: the Discord link in the readme is invalid

@Aurumaker72
Copy link
Collaborator

To read Mupen's memory, you can do it externally via ReadProcessMemory like STROOP, or internally by writing to rdram (memory/memory.h) directly or via the helper functions LoadRDRAMSafe and StoreRDRAMSafe.

Mupen currently has no comprehensive docs so feel free to ask anytime in the discord.

@Wade7wastaken
Copy link

Just for fun, here's a proof of concept of reading Mario's speed in Rust using the ReadProcessMemory method mentioned above. You can get the PID of the mupen process using Task Manager's "Details" tab, and the address comes from STROOP (right click on variable > click Show Variable Info > Emulator Address)

[dependencies]
winapi = { version = "0.3.9", features = ["memoryapi", "processthreadsapi", "handleapi"] }
use winapi::{
    ctypes::c_void,
    um::{
        handleapi::CloseHandle, memoryapi::ReadProcessMemory, processthreadsapi::OpenProcess,
        winnt::PROCESS_VM_READ,
    },
};

fn read_float(pid: u32, address: u32) -> f32 {
    let handle = unsafe { OpenProcess(PROCESS_VM_READ, 0, pid) };
    if handle.is_null() {
        panic!("Handle was null");
    }

    let mut buffer = [0u8; 4]; // float is 4 bytes
    let read_result = unsafe {
        ReadProcessMemory(
            handle,
            address as *const c_void,
            buffer.as_mut_ptr() as *mut c_void,
            4, // float is 4 bytes
            &mut 0,
        )
    };

    let close_handle_result = unsafe { CloseHandle(handle) };

    if close_handle_result == 0 {
        eprintln!("Handle didn't close correctly"); // don't panic, we may still have something in read_result
    }

    if read_result == 0 {
        panic!("Read failed");
    }

    f32::from_le_bytes(buffer)
}

fn main() {
    let pid = 9820;
    let address = 0x00BFB184;
    let speed = read_float(pid, address);

    println!("{:?}", speed);
}

Ofc this could be improved with better rust error handling or maybe a generic read function that can read different types (float, s16, etc...), but in my opinion it's better to use C/C++ when using windows apis because you don't have to do any type gymnastics or deal with unsafe.

@Madghostek
Copy link
Collaborator

While this is cool, I don't really see the point of hooking into mupen when there is wafel as a library 🤨

@TimeTravelPenguin
Copy link
Author

While this is cool, I don't really see the point of hooking into mupen when there is wafel as a library 🤨

Oh, interesting. I have never seen this. However, I don't want to be limited to only SM64. This will be a good place to start, though!

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
proposal This issue contains a feature or change proposal.
Projects
None yet
Development

No branches or pull requests

4 participants