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
15#[derive(Debug, Clone)]
16pub struct SM64GameGenerator {
17 wasm_bytes: Vec<u8>,
18}
19
20impl SM64GameGenerator {
21 #[cfg(feature = "fs")]
22 pub fn new(rom_path: &str) -> Result<Self, Error> {
23 match util::check_hash(rom_path) {
24 Ok(_) => {},
25 Err(_) => {return Err(Error::msg("Invalid ROM. Should be a US copy, in z64 format (8.00MB)"));}
26 };
27 let rom_bytes = std::fs::read(rom_path)?;
28 Self::from_rom_bytes(rom_bytes)
29 }
30
31 pub fn from_rom_bytes(rom_bytes: Vec<u8>) -> Result<Self, Error> {
32 let wasm_bytes = Self::rom_to_wasm_bytes(rom_bytes);
33 Ok(SM64GameGenerator { wasm_bytes })
34 }
35
36 pub fn create_game(&self) -> Result<SM64Game, Error> {
37 SM64Game::new(self.wasm_bytes.clone())
38 }
39
40 pub fn rom_to_wasm_bytes(rom_bytes: Vec<u8>) -> Vec<u8> {
41 let rom_len = rom_bytes.len();
42 const XOR_BYTES: &[u8] = 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>) -> Result<Self> {
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 = false;
73
74 let main_func = instance.get_typed_func::<(), ()>(&mut store, "main_func")?;
75 main_func.call(&mut store, ())?;
76
77 Ok(SM64Game {
78 store,
79 instance,
80 memory,
81 using_rng,
82 })
83 }
84
85 pub fn rng_init(&mut self, seed: u32, cfg: RandomConfig) -> Result<()> {
86 self.using_rng = true;
87 let rng_init = self.instance.get_typed_func::<(u32, u32, u32, f32, f32, f32), ()>(&mut self.store, "rng_init")?;
88 rng_init.call(&mut self.store, (
89 seed, cfg.max_random_action, cfg.max_window_length,
90 cfg.a_prob, cfg.b_prob, cfg.z_prob)
91 )?;
92 Ok(())
93 }
94
95
96 pub fn step_game(&mut self, pad: GamePad) -> Result<()> {
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