sm64_binds/
sm64.rs

1use wasmer::*;
2
3use anyhow::{Result, Error};
4use std::convert::TryInto;
5
6mod game_pad;
7mod game_state;
8mod rng_config;
9mod util;
10
11pub use game_pad::GamePad;
12pub use game_state::GameState;
13pub use rng_config::RandomConfig;
14use util::unzip_bytes;
15
16#[derive(Debug, Clone)]
17pub struct SM64GameGenerator {
18    wasm_bytes: Vec<u8>,
19}
20
21impl SM64GameGenerator {
22    pub fn new(rom_bytes: Vec<u8>) -> Result<Self, Error> {
23        let wasm_bytes = Self::rom_to_wasm_bytes(rom_bytes);
24        Ok(SM64GameGenerator { wasm_bytes })
25    }
26    #[cfg(feature = "fs")]
27    pub fn from_file(rom_path: &str) -> Result<Self, Error> {
28        match util::check_hash(rom_path) {
29            Ok(_) => {},
30            Err(_) => {return Err(Error::msg("Invalid ROM. Should be a US copy, in z64 format (8.00MB)"));}
31        };
32        let rom_bytes = std::fs::read(rom_path)?;
33        Self::from_rom_bytes(rom_bytes)
34    }
35    pub fn create_game(&self) -> Result<SM64Game, Error>  {
36        SM64Game::new(self.wasm_bytes.clone())
37    }
38
39    pub fn rom_to_wasm_bytes(rom_bytes: Vec<u8>) -> Vec<u8> {
40        let rom_len = rom_bytes.len();
41        const XOR_BYTES: &[u8] = include_bytes!("../pkg/sm64_headless.us.wasm.zip.xor");
42        let mut wasm_zip_bytes: Vec<u8> = Vec::new();
43        for i in 0..XOR_BYTES.len() {
44            wasm_zip_bytes.push(XOR_BYTES[i] ^ rom_bytes[i % rom_len]);
45        }
46
47        unzip_bytes(&wasm_zip_bytes)
48    }
49}
50
51
52pub struct SM64Game {
53    store: Store,
54    instance: Instance,
55    using_rng: bool
56}
57
58impl SM64Game {
59    pub fn new(wasm_bytes: Vec<u8>) -> Result<Self> {
60        let mut store: Store = Store::default();
61        let module = Module::new(&store, wasm_bytes)?;
62        let import_object = imports! {
63            "wasi_snapshot_preview1" => {
64                "proc_exit" => Function::new_typed(&mut store, |_exit_code: i32| {
65                    println!("proc_exit");
66                }),
67                "fd_write" => Function::new_typed(&mut store, |_fd: i32, _iovs: i32, _iovs_len: i32, _bytes_written: i32| -> i32 {
68                    println!("fd_write");
69                    0
70                }),
71                "fd_close" => Function::new_typed(&mut store, |_fd: i32| -> i32 {
72                    println!("fd_close");
73                    0
74                }),
75                "fd_seek" => Function::new_typed(&mut store, |_fd: i32, _offset: i64, _whence: i32, _new_offset: i32| -> i32 {
76                    println!("fd_seek");
77                    0
78                }),
79            },
80        };
81
82        let instance = Instance::new(&mut store, &module, &import_object).unwrap();
83
84        let using_rng = false;
85
86        let main_func: TypedFunction<(), ()> = instance.exports.get_typed_function::<(), ()>(&mut store, "main_func")?;
87        main_func.call(&mut store)?;
88
89        Ok(SM64Game {
90            store,
91            instance,
92            using_rng,
93        })
94    }
95
96    pub fn rng_init(&mut self, seed: u32, cfg: RandomConfig) -> Result<()> {
97        self.using_rng = true;
98        let rng_init: TypedFunction<(u32, u32, u32, f32, f32, f32), ()> = self.instance.exports.get_typed_function::<(u32, u32, u32, f32, f32, f32), ()>(&mut self.store, "rng_init")?;
99        rng_init.call(&mut self.store,
100            seed, cfg.max_random_action, cfg.max_window_length, 
101            cfg.a_prob, cfg.b_prob, cfg.z_prob
102        )?;
103        Ok(())
104    }
105
106
107    pub fn step_game(&mut self, pad: GamePad) -> Result<()> {
108        let step_game: TypedFunction<(u32, i32, i32), ()> = self.instance.exports.get_typed_function::<(u32, i32, i32), ()>(&mut self.store, "step_game")?;
109        step_game.call(&mut self.store, pad.button.into(), pad.stick_x.into(), pad.stick_y.into())?;
110        Ok(())
111    }
112
113    pub fn get_game_state(&mut self) -> Result<GameState, Error> {
114        let get_game_state: TypedFunction<(), i32> = self.instance.exports.get_typed_function::<(), i32>(&mut self.store, "get_game_state")?;
115        let pointer = get_game_state.call(&mut self.store)?;
116
117        let mut buffer: [u8; 60] = [0; 60];
118        let memory = self.instance.exports.get_memory("memory")?;
119        let view = memory.view(&self.store);
120        view.read(pointer.try_into()?, &mut buffer)?;
121        let state = GameState::new(&buffer);
122        Ok(state)
123    }
124
125    pub fn rng_pad(&mut self, pad: GamePad) -> Result<GamePad, Error> {
126        let rng_pad: TypedFunction<(u32, i32, i32), i32> = self.instance.exports.get_typed_function::<(u32, i32, i32), i32>(&mut self.store, "rng_pad")?;
127        let pointer = rng_pad.call(&mut self.store, pad.button.into(), pad.stick_x.into(), pad.stick_y.into())?;
128
129        let mut buffer: [u8; 4] = [0; 4];
130        let memory = self.instance.exports.get_memory("memory")?;
131        let view = memory.view(&self.store);
132        view.read(pointer.try_into()?, &mut buffer)?;
133        let pad = GamePad::from_bytes(&buffer);
134        Ok(pad)
135    }
136    pub fn using_rng(&self) -> bool {
137        self.using_rng
138    }
139}
140