Skip to main content

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::RngConfig;
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        if !util::check_hash(&rom_bytes) {
24            return Err(Error::msg("Invalid ROM. Should be a US copy, in z64 format (8.00MB)"));
25        };
26        let wasm_bytes = Self::rom_to_wasm_bytes(rom_bytes);
27
28        Ok(SM64GameGenerator { wasm_bytes })
29    }
30    #[cfg(feature = "fs")]
31    pub fn from_file(rom_path: &str) -> Result<Self, Error> {
32        let rom_bytes = std::fs::read(rom_path)?;
33        Self::new(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    pub fn copy(&self, game: &mut SM64Game) -> Result<SM64Game, Error> {
51        let mut cloned_game = self.create_game()?;
52        
53        game.copy_to(&mut cloned_game)?;
54
55        Ok(cloned_game)
56    }
57}
58
59
60pub struct SM64Game {
61    store: Store,
62    instance: Instance,
63    using_rng: bool
64}
65
66impl SM64Game {
67    // from using the twiggy tool (twiggy top sm64.us.wasm). Seems to be constant.
68    // I believe this segment only has constants so no need to save/load it
69    const DATA0_SIZE: usize = 8013002;
70    pub fn new(wasm_bytes: Vec<u8>) -> Result<Self> {
71        let using_rng = false;
72
73        let mut store: Store = Store::default();
74        let module = Module::new(&store, wasm_bytes)?;
75        let import_object = imports! {
76            "wasi_snapshot_preview1" => {
77                "proc_exit" => Function::new_typed(&mut store, |_exit_code: i32| {
78                    println!("proc_exit");
79                }),
80                "fd_write" => Function::new_typed(&mut store, |_fd: i32, _iovs: i32, _iovs_len: i32, _bytes_written: i32| -> i32 {
81                    println!("fd_write");
82                    0
83                }),
84                "fd_close" => Function::new_typed(&mut store, |_fd: i32| -> i32 {
85                    println!("fd_close");
86                    0
87                }),
88                "fd_seek" => Function::new_typed(&mut store, |_fd: i32, _offset: i64, _whence: i32, _new_offset: i32| -> i32 {
89                    println!("fd_seek");
90                    0
91                }),
92            },
93        };
94
95        let instance = Instance::new(&mut store, &module, &import_object).unwrap();
96
97
98        let main_func: TypedFunction<(), ()> = instance.exports.get_typed_function::<(), ()>(&mut store, "main_func")?;
99        main_func.call(&mut store)?;
100
101        Ok(SM64Game {
102            store,
103            instance,
104            using_rng,
105        })
106    }
107
108    pub fn set_rng_config(&mut self, cfg: RngConfig) -> Result<()> {
109        self.using_rng = true;
110        let set_rng_config: TypedFunction<(u32, u32, u32, f32, f32, f32), ()> = self.instance.exports.get_typed_function::<(u32, u32, u32, f32, f32, f32), ()>(&mut self.store, "set_rng_config")?;
111        set_rng_config.call(&mut self.store,
112            cfg.window_length, cfg.random_amount, cfg.random_burst_length,  
113            cfg.a_prob, cfg.b_prob, cfg.z_prob
114        )?;
115        Ok(())
116    }
117
118    pub fn set_rng_seed(&mut self, seed: u32) -> Result<()> {
119        self.using_rng = true;
120        let set_rng_config: TypedFunction<u32, ()> = self.instance.exports.get_typed_function::<u32, ()>(&mut self.store, "set_rng_seed")?;
121        set_rng_config.call(&mut self.store,
122            seed
123        )?;
124        Ok(())
125    }
126
127    pub fn step_game(&mut self, pad: GamePad) -> Result<()> {
128        let step_game: TypedFunction<(u32, i32, i32), ()> = self.instance.exports.get_typed_function::<(u32, i32, i32), ()>(&mut self.store, "step_game")?;
129        step_game.call(&mut self.store, pad.button.into(), pad.stick_x.into(), pad.stick_y.into())?;
130        Ok(())
131    }
132
133    pub fn get_game_state(&mut self) -> Result<GameState, Error> {
134        let get_game_state: TypedFunction<(), i32> = self.instance.exports.get_typed_function::<(), i32>(&mut self.store, "get_game_state")?;
135        let pointer = get_game_state.call(&mut self.store)?;
136
137        let mut buffer: [u8; 64] = [0; 64];
138        let memory = self.instance.exports.get_memory("memory")?;
139        let view = memory.view(&self.store);
140        view.read(pointer.try_into()?, &mut buffer)?;
141        let state = GameState::new(&buffer);
142        Ok(state)
143    }
144
145    pub fn rng_pad(&mut self, pad: GamePad) -> Result<GamePad, Error> {
146        let rng_pad: TypedFunction<(u32, i32, i32), i32> = self.instance.exports.get_typed_function::<(u32, i32, i32), i32>(&mut self.store, "rng_pad")?;
147        let pointer = rng_pad.call(&mut self.store, pad.button.into(), pad.stick_x.into(), pad.stick_y.into())?;
148
149        let mut buffer: [u8; 4] = [0; 4];
150        let memory = self.instance.exports.get_memory("memory")?;
151        let view = memory.view(&self.store);
152        view.read(pointer.try_into()?, &mut buffer)?;
153        let pad = GamePad::from_bytes(&buffer);
154        Ok(pad)
155    }
156    
157    pub fn using_rng(&self) -> bool {
158        self.using_rng
159    }
160
161    fn expand_memory(&mut self, save_state_length: usize) -> Result<(), Error> {
162        // allocate more memory if necessary for the save state
163        let memory = self.instance.exports.get_memory("memory")?;
164        let required_total_bytes = (SM64Game::DATA0_SIZE + save_state_length) as u64;
165        memory.grow_at_least(&mut self.store, required_total_bytes)?;
166        Ok(())
167    }
168
169    pub fn get_memory_bytes(&mut self) -> Result<Vec<u8>, Error> {
170        let memory = self.instance.exports.get_memory("memory")?;
171        let view = memory.view(&self.store);
172
173        let total_size = view.data_size();
174        let buffer = view.copy_range_to_vec((SM64Game::DATA0_SIZE as u64)..total_size)?;
175        Ok(buffer)
176    }
177
178    pub fn write_memory_bytes(&mut self, bytes: &[u8]) -> Result<(), Error> {
179        self.expand_memory(bytes.len())?;
180
181        let memory = self.instance.exports.get_memory("memory")?;
182        
183        let final_view = memory.view(&self.store);
184        Ok(final_view.write(SM64Game::DATA0_SIZE as u64, &bytes)?)
185    }
186
187    pub fn copy_to(&mut self, game_to: &mut SM64Game) -> Result<(), Error> {
188        let b = self.get_memory_bytes()?;
189        game_to.write_memory_bytes(b.as_slice())?;
190        Ok(())
191    }
192    
193    pub fn full_copy_to(&mut self, game_to: &SM64Game) -> Result<(), Error> {
194        let view_from = self.instance.exports.get_memory("memory")?.view(&mut self.store);
195        let view_to = game_to.instance.exports.get_memory("memory")?.view(&game_to.store);
196
197        Ok(view_from.copy_to_memory(view_from.data_size(), &view_to)?)
198    }
199
200}
201