meru_interface/
lib.rs

1#[cfg(target_arch = "wasm32")]
2#[macro_use]
3extern crate base64_serde;
4
5pub mod config;
6pub mod key_assign;
7
8pub use config::File;
9
10use schemars::{
11    gen::SchemaGenerator,
12    schema::{Schema, SchemaObject},
13    JsonSchema,
14};
15use serde::{de::DeserializeOwned, Deserialize, Serialize};
16
17pub use crate::key_assign::{
18    Gamepad, GamepadAxis, GamepadAxisType, GamepadButton, GamepadButtonType, InputState, KeyAssign,
19    KeyCode, MultiKey, SingleKey,
20};
21
22pub struct CoreInfo {
23    pub system_name: &'static str,
24    pub abbrev: &'static str,
25    pub file_extensions: &'static [&'static str],
26}
27
28#[derive(Default)]
29pub struct FrameBuffer {
30    pub width: usize,
31    pub height: usize,
32    pub buffer: Vec<Color>,
33}
34
35impl FrameBuffer {
36    pub fn new(width: usize, height: usize) -> Self {
37        let mut ret = Self::default();
38        ret.resize(width, height);
39        ret
40    }
41
42    pub fn resize(&mut self, width: usize, height: usize) {
43        if (width, height) == (self.width, self.height) {
44            return;
45        }
46        self.width = width;
47        self.height = height;
48        self.buffer.resize(width * height, Color::default());
49    }
50
51    pub fn pixel(&self, x: usize, y: usize) -> &Color {
52        &self.buffer[y * self.width + x]
53    }
54
55    pub fn pixel_mut(&mut self, x: usize, y: usize) -> &mut Color {
56        &mut self.buffer[y * self.width + x]
57    }
58}
59
60#[derive(Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
61#[serde(try_from = "String", into = "String")]
62pub struct Color {
63    pub r: u8,
64    pub g: u8,
65    pub b: u8,
66}
67
68impl JsonSchema for Color {
69    fn schema_name() -> String {
70        "Color".to_string()
71    }
72
73    fn json_schema(gen: &mut SchemaGenerator) -> Schema {
74        let mut schema: SchemaObject = <String>::json_schema(gen).into();
75        schema.format = Some("color".to_owned());
76        schema.into()
77    }
78}
79
80#[derive(thiserror::Error, Debug)]
81pub enum ParseColorError {
82    #[error("Color string must be hex color code: `#RRGGBB`")]
83    InvalidFormat,
84}
85
86impl TryFrom<String> for Color {
87    type Error = ParseColorError;
88
89    fn try_from(s: String) -> Result<Self, Self::Error> {
90        if s.len() != 7 || &s[0..1] != "#" || !s[1..].chars().all(|c| c.is_ascii_hexdigit()) {
91            Err(ParseColorError::InvalidFormat)?;
92        }
93
94        Ok(Color {
95            r: u8::from_str_radix(&s[1..3], 16).unwrap(),
96            g: u8::from_str_radix(&s[3..5], 16).unwrap(),
97            b: u8::from_str_radix(&s[5..7], 16).unwrap(),
98        })
99    }
100}
101
102impl From<Color> for String {
103    fn from(c: Color) -> Self {
104        format!("#{:02X}{:02X}{:02X}", c.r, c.g, c.b)
105    }
106}
107
108impl Color {
109    pub const fn new(r: u8, g: u8, b: u8) -> Self {
110        Self { r, g, b }
111    }
112}
113
114pub struct AudioBuffer {
115    pub sample_rate: u32,
116    pub channels: u16,
117    pub samples: Vec<AudioSample>,
118}
119
120impl Default for AudioBuffer {
121    fn default() -> Self {
122        Self {
123            sample_rate: 48000,
124            channels: 2,
125            samples: vec![],
126        }
127    }
128}
129
130impl AudioBuffer {
131    pub fn new(sample_rate: u32, channels: u16) -> Self {
132        Self {
133            sample_rate,
134            channels,
135            samples: vec![],
136        }
137    }
138}
139
140#[derive(Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
141pub struct AudioSample {
142    pub left: i16,
143    pub right: i16,
144}
145
146impl AudioSample {
147    pub fn new(left: i16, right: i16) -> Self {
148        Self { left, right }
149    }
150}
151
152#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
153pub struct KeyConfig {
154    pub controllers: Vec<Vec<(String, KeyAssign)>>,
155}
156
157impl KeyConfig {
158    pub fn input(&self, input_state: &impl InputState) -> InputData {
159        let controllers = self
160            .controllers
161            .iter()
162            .map(|keys| {
163                keys.iter()
164                    .map(|(key, assign)| (key.clone(), assign.pressed(input_state)))
165                    .collect()
166            })
167            .collect();
168
169        InputData { controllers }
170    }
171}
172
173#[derive(Default)]
174pub struct InputData {
175    pub controllers: Vec<Vec<(String, bool)>>,
176}
177
178pub trait EmulatorCore {
179    type Error: std::error::Error + Send + Sync + 'static;
180    type Config: JsonSchema + Serialize + DeserializeOwned + Default;
181
182    fn core_info() -> &'static CoreInfo;
183
184    fn try_from_file(
185        data: &[u8],
186        backup: Option<&[u8]>,
187        config: &Self::Config,
188    ) -> Result<Self, Self::Error>
189    where
190        Self: Sized;
191    fn game_info(&self) -> Vec<(String, String)>;
192
193    fn set_config(&mut self, config: &Self::Config);
194
195    fn exec_frame(&mut self, render_graphics: bool);
196    fn reset(&mut self);
197
198    fn frame_buffer(&self) -> &FrameBuffer;
199    fn audio_buffer(&self) -> &AudioBuffer;
200
201    fn default_key_config() -> KeyConfig;
202    fn set_input(&mut self, input: &InputData);
203
204    fn backup(&self) -> Option<Vec<u8>>;
205
206    fn save_state(&self) -> Vec<u8>;
207    fn load_state(&mut self, data: &[u8]) -> Result<(), Self::Error>;
208}