sm64_binds/
sm64.rs

1use wasmtime::*;
2
3use std::convert::TryInto;
4
5mod game_pad;
6mod game_state;
7mod rng_config;
8mod util;
9
10pub use game_pad::GamePad;
11pub use game_state::GameState;
12pub use rng_config::RandomConfig;
13use util::unzip_bytes;
14
15pub struct SM64GameGenerator {
16    wasm_bytes: Vec<u8>,
17}
18
19impl SM64GameGenerator {
20    #[cfg(feature = "fs")]
21    pub fn new(rom_path: &str) -> Result<Self, Error> {
22        match util::check_hash(rom_path) {
23            Ok(_) => {},
24            Err(_) => {return Err(Error::msg("Invalid ROM. Should be a US copy, in z64 format (8.00MB)"));}
25        };
26        let rom_bytes = std::fs::read(rom_path)?;
27        Self::from_rom_bytes(rom_bytes)
28    }
29
30    pub fn from_rom_bytes(rom_bytes: Vec<u8>) -> Result<Self, Error> {
31        let wasm_bytes = Self::rom_to_wasm_bytes(rom_bytes);
32        Ok(SM64GameGenerator { wasm_bytes })
33    }
34
35    pub fn create_game(&self, cfg: Option<RandomConfig>) -> Result<SM64Game, Error>  {
36        SM64Game::new(self.wasm_bytes.clone(), cfg)
37    }
38
39    pub fn rom_to_wasm_bytes(rom_bytes: Vec<u8>) -> Vec<u8> {
40        let rom_len = rom_bytes.len();
41        // include_bytes!(concat!(env!("OUT_DIR"), "/sm64_headless.us.wasm.xor"));
42        let xor_bytes = include_bytes!("../pkg/sm64_headless.us.wasm.zip.xor");
43        let mut wasm_zip_bytes: Vec<u8> = Vec::new();
44        for i in 0..xor_bytes.len() {
45            wasm_zip_bytes.push(xor_bytes[i] ^ rom_bytes[i % rom_len]);
46        }
47
48        unzip_bytes(&wasm_zip_bytes)
49    }
50}
51
52
53pub struct SM64Game {
54    store: Store<u32>,
55    instance: Instance,
56    memory: Memory,
57    using_rng: bool
58}
59
60impl SM64Game {
61    pub fn new(wasm_bytes: Vec<u8>, cfg: Option<RandomConfig>) -> Result<Self, Error> {
62        let engine = Engine::default();
63        let mut store: Store<u32> = Store::new(&engine, 4);
64        let module = Module::new(&engine, wasm_bytes)?;
65        
66        let mut linker = Linker::new(&engine);
67        linker.define_unknown_imports_as_traps(&module)?;
68        
69        let instance = linker.instantiate(&mut store, &module)?;
70        let memory = instance.get_memory(&mut store, "memory").unwrap();
71
72        let using_rng;
73        if let Some(c) = cfg {
74            using_rng = true;
75            let rng_init = instance.get_typed_func::<(u32, u32, u32, f32, f32, f32), ()>(&mut store, "rng_init")?;
76            rng_init.call(&mut store, (
77                c.seed, c.max_random_action, c.max_window_length, 
78                c.a_prob, c.b_prob, c.z_prob)
79            )?;
80        } else {
81            using_rng = false;
82        }
83
84        let main_func = instance.get_typed_func::<(), ()>(&mut store, "main_func")?;
85        main_func.call(&mut store, ())?;
86
87        Ok(SM64Game {
88            store,
89            instance,
90            memory,
91            using_rng,
92        })
93    }
94
95
96    pub fn step_game(&mut self, pad: GamePad) -> Result<(), Error> {
97        let step_game = self.instance.get_typed_func::<(u32, i32, i32), ()>(&mut self.store, "step_game")?;
98        step_game.call(&mut self.store, (pad.button.into(), pad.stick_x.into(), pad.stick_y.into()))?;
99        Ok(())
100    }
101
102    pub fn get_game_state(&mut self) -> Result<GameState, Error> {
103        let get_game_state = self.instance.get_typed_func::<(), i32>(&mut self.store, "get_game_state")?;
104        let pointer = get_game_state.call(&mut self.store, ())?;
105        let mut buffer: [u8; 60] = [0; 60];
106        self.memory.read(&mut self.store, pointer.try_into()?, &mut buffer)?;
107        let state = GameState::new(&buffer);
108        Ok(state)
109    }
110
111    pub fn rng_pad(&mut self, pad: GamePad) -> Result<GamePad, Error> {
112        let rng_pad = self.instance.get_typed_func::<(u32, i32, i32), i32>(&mut self.store, "rng_pad")?;
113        let pointer = rng_pad.call(&mut self.store, (pad.button.into(), pad.stick_x.into(), pad.stick_y.into()))?;
114        let mut buffer: [u8; 4] = [0; 4];
115        self.memory.read(&mut self.store, pointer.try_into()?, &mut buffer)?;
116        let pad = GamePad::from_bytes(&buffer);
117        Ok(pad)
118    }
119    pub fn using_rng(&self) -> bool {
120        self.using_rng
121    }
122}
123