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 }
65
66
67pub struct SM64Game {
68 store: Store,
69 instance: Instance,
70 using_rng: bool
71}
72
73impl SM64Game {
74 const DATA0_SIZE: usize = 8013002;
77 pub fn new(wasm_bytes: Vec<u8>) -> Result<Self> {
78 let using_rng = false;
79
80 let mut store: Store = Store::default();
81 let module = Module::new(&store, wasm_bytes)?;
82 let import_object = imports! {
83 "wasi_snapshot_preview1" => {
84 "proc_exit" => Function::new_typed(&mut store, |_exit_code: i32| {
85 println!("proc_exit");
86 }),
87 "fd_write" => Function::new_typed(&mut store, |_fd: i32, _iovs: i32, _iovs_len: i32, _bytes_written: i32| -> i32 {
88 println!("fd_write");
89 0
90 }),
91 "fd_close" => Function::new_typed(&mut store, |_fd: i32| -> i32 {
92 println!("fd_close");
93 0
94 }),
95 "fd_seek" => Function::new_typed(&mut store, |_fd: i32, _offset: i64, _whence: i32, _new_offset: i32| -> i32 {
96 println!("fd_seek");
97 0
98 }),
99 },
100 };
101
102 let instance = Instance::new(&mut store, &module, &import_object).unwrap();
103
104
105 let main_func: TypedFunction<(), ()> = instance.exports.get_typed_function::<(), ()>(&mut store, "main_func")?;
106 main_func.call(&mut store)?;
107
108 Ok(SM64Game {
109 store,
110 instance,
111 using_rng,
112 })
113 }
114
115 pub fn set_rng_config(&mut self, cfg: RngConfig) -> Result<()> {
116 self.using_rng = true;
117 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")?;
118 set_rng_config.call(&mut self.store,
119 cfg.window_length, cfg.random_amount, cfg.random_burst_length,
120 cfg.a_prob, cfg.b_prob, cfg.z_prob
121 )?;
122 Ok(())
123 }
124
125 pub fn set_rng_seed(&mut self, seed: u32) -> Result<()> {
126 self.using_rng = true;
127 let set_rng_config: TypedFunction<u32, ()> = self.instance.exports.get_typed_function::<u32, ()>(&mut self.store, "set_rng_seed")?;
128 set_rng_config.call(&mut self.store,
129 seed
130 )?;
131 Ok(())
132 }
133
134 pub fn step_game(&mut self, pad: GamePad) -> Result<()> {
135 let step_game: TypedFunction<(u32, i32, i32), ()> = self.instance.exports.get_typed_function::<(u32, i32, i32), ()>(&mut self.store, "step_game")?;
136 step_game.call(&mut self.store, pad.button.into(), pad.stick_x.into(), pad.stick_y.into())?;
137 Ok(())
138 }
139
140 pub fn get_game_state(&mut self) -> Result<GameState, Error> {
141 let get_game_state: TypedFunction<(), i32> = self.instance.exports.get_typed_function::<(), i32>(&mut self.store, "get_game_state")?;
142 let pointer = get_game_state.call(&mut self.store)?;
143
144 let mut buffer: [u8; 60] = [0; 60];
145 let memory = self.instance.exports.get_memory("memory")?;
146 let view = memory.view(&self.store);
147 view.read(pointer.try_into()?, &mut buffer)?;
148 let state = GameState::new(&buffer);
149 Ok(state)
150 }
151
152 pub fn rng_pad(&mut self, pad: GamePad) -> Result<GamePad, Error> {
153 let rng_pad: TypedFunction<(u32, i32, i32), i32> = self.instance.exports.get_typed_function::<(u32, i32, i32), i32>(&mut self.store, "rng_pad")?;
154 let pointer = rng_pad.call(&mut self.store, pad.button.into(), pad.stick_x.into(), pad.stick_y.into())?;
155
156 let mut buffer: [u8; 4] = [0; 4];
157 let memory = self.instance.exports.get_memory("memory")?;
158 let view = memory.view(&self.store);
159 view.read(pointer.try_into()?, &mut buffer)?;
160 let pad = GamePad::from_bytes(&buffer);
161 Ok(pad)
162 }
163
164 pub fn using_rng(&self) -> bool {
165 self.using_rng
166 }
167
168 fn expand_memory(&mut self, save_state_length: usize) -> Result<(), Error> {
169 let memory = self.instance.exports.get_memory("memory")?;
171
172 let required_total_bytes = (SM64Game::DATA0_SIZE + save_state_length) as u64;
173 let current_total_bytes = memory.view(&self.store).data_size();
174
175 if required_total_bytes > current_total_bytes {
176 let diff = required_total_bytes - current_total_bytes;
177 const PAGE_SIZE: u64 = 65536;
178 let pages_to_grow = (diff + PAGE_SIZE - 1) / PAGE_SIZE;
179 memory.grow(&mut self.store, pages_to_grow as u32)
180 .map_err(|e| Error::msg(format!("WASM Memory OOM: Could not grow by {} pages. {}", pages_to_grow, e)))?;
181 }
182
183 Ok(())
184 }
185
186 pub fn get_memory_bytes(&mut self) -> Result<Vec<u8>, Error> {
187 let memory = self.instance.exports.get_memory("memory")?;
188 let view = memory.view(&self.store);
189
190 let total_size = view.data_size();
191 let buffer = view.copy_range_to_vec((SM64Game::DATA0_SIZE as u64)..total_size)?;
192 Ok(buffer)
193 }
194
195 pub fn write_memory_bytes(&mut self, bytes: Vec<u8>) -> Result<(), Error> {
196 self.expand_memory(bytes.len())?;
197
198 let memory = self.instance.exports.get_memory("memory")?;
199
200 let final_view = memory.view(&self.store);
201 Ok(final_view.write(SM64Game::DATA0_SIZE as u64, &bytes)?)
202 }
203
204 pub fn copy_to(&mut self, game_to: &mut SM64Game) -> Result<(), Error> {
205 game_to.write_memory_bytes(self.get_memory_bytes()?)?;
206 Ok(())
207 }
208
209}
210