1mod finitefloat;
2
3pub use finitefloat::{FiniteF32, FiniteF64};
4
5use crate::input::Player;
6use bincode::{Decode, Encode};
7use jgenesis_proc_macros::{EnumAll, EnumDisplay, EnumFromStr};
8use std::borrow::Cow;
9use std::error::Error;
10use std::fmt::{Debug, Display, Formatter};
11use std::hash::Hash;
12
13#[repr(C)]
14#[derive(Debug, Clone, Copy, PartialEq, Eq, bytemuck::Pod, bytemuck::Zeroable, Encode, Decode)]
15pub struct Color {
16    pub r: u8,
17    pub g: u8,
18    pub b: u8,
19    pub a: u8,
20}
21
22impl Color {
23    pub const BLACK: Self = Self::rgb(0, 0, 0);
24
25    pub const TRANSPARENT: Self = Self::rgba(0, 0, 0, 0);
26
27    #[must_use]
28    #[inline]
29    pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
30        Self { r, g, b, a: 255 }
31    }
32
33    #[must_use]
34    #[inline]
35    pub const fn rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
36        Self { r, g, b, a }
37    }
38}
39
40impl Default for Color {
41    #[inline]
42    fn default() -> Self {
43        Self::BLACK
44    }
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
48pub struct FrameSize {
49    pub width: u32,
50    pub height: u32,
51}
52
53impl FrameSize {
54    #[allow(clippy::len_without_is_empty)]
55    #[must_use]
56    pub fn len(self) -> u32 {
57        self.width * self.height
58    }
59}
60
61#[derive(Debug, Clone, Copy)]
62pub struct DisplayArea {
63    pub width: u32,
64    pub height: u32,
65    pub x: u32,
66    pub y: u32,
67    pub pixel_density: f32,
68}
69
70#[derive(Debug, Clone, Copy)]
71pub enum Rotation {
72    None,
73    Clockwise,
74    OneEighty,
75    Counterclockwise,
76}
77
78#[derive(Debug, Clone, Copy)]
79pub struct DisplayInfo {
80    pub frame_size: FrameSize,
81    pub display_area: DisplayArea,
82    pub rotation: Rotation,
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Encode, Decode)]
86pub enum ColorCorrection {
87    #[default]
88    None,
89    GbcLcd {
90        screen_gamma: FiniteF32,
91    },
92    GbaLcd {
93        screen_gamma: FiniteF32,
94    },
95}
96
97impl Display for ColorCorrection {
98    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
99        match self {
100            Self::None => write!(f, "None"),
101            &Self::GbcLcd { screen_gamma } => {
102                write!(f, "Game Boy Color LCD (gamma {:.1})", f32::from(screen_gamma))
103            }
104            &Self::GbaLcd { screen_gamma } => {
105                write!(f, "Game Boy Advance LCD (gamma {:.1})", f32::from(screen_gamma))
106            }
107        }
108    }
109}
110
111#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
112pub enum SamplesPerColorCycle {
113    Twelve,  // ~42.95 MHz sample rate (NES, SNES)
114    Fifteen, // ~53.69 MHz sample rate (SMS, Genesis)
115}
116
117impl From<SamplesPerColorCycle> for u32 {
118    fn from(value: SamplesPerColorCycle) -> Self {
119        match value {
120            SamplesPerColorCycle::Twelve => 12,
121            SamplesPerColorCycle::Fifteen => 15,
122        }
123    }
124}
125
126impl From<SamplesPerColorCycle> for u64 {
127    fn from(value: SamplesPerColorCycle) -> Self {
128        u32::from(value).into()
129    }
130}
131
132#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
133pub struct CompositeParams {
134    // How many times to repeat each frame buffer pixel
135    pub upscale_factor: u32,
136    pub samples_per_color_cycle: SamplesPerColorCycle,
137}
138
139#[derive(Debug, Clone, Copy, PartialEq, Eq)]
140pub struct NtscPerFrameParams {
141    pub frame_phase_offset: u64,
142    pub per_line_phase_offset: u64,
143}
144
145/// Rendering options that are not required to be explicitly specified, unlike frame size
146#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
147pub struct RenderFrameOptions {
148    pub pixel_aspect_ratio: Option<FiniteF64>,
149    pub color_correction: ColorCorrection,
150    pub frame_blending: bool,
151    pub composite_params: Option<CompositeParams>,
152    pub emulate_nes_ntsc_output: bool,
153    pub ntsc_per_frame_params: Option<NtscPerFrameParams>,
154}
155
156/// [`RenderFrameOptions`] excluding per-frame parameters
157#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
158pub struct RenderFrameOptionsHashable {
159    pub pixel_aspect_ratio: Option<FiniteF64>,
160    pub color_correction: ColorCorrection,
161    pub frame_blending: bool,
162    pub composite_params: Option<CompositeParams>,
163    pub emulate_nes_ntsc_output: bool,
164}
165
166impl RenderFrameOptions {
167    #[must_use]
168    pub fn pixel_aspect_ratio(pixel_aspect_ratio: Option<FiniteF64>) -> Self {
169        Self { pixel_aspect_ratio, ..Self::default() }
170    }
171
172    #[must_use]
173    pub fn to_hashable(self) -> RenderFrameOptionsHashable {
174        RenderFrameOptionsHashable {
175            pixel_aspect_ratio: self.pixel_aspect_ratio,
176            color_correction: self.color_correction,
177            frame_blending: self.frame_blending,
178            composite_params: self.composite_params,
179            emulate_nes_ntsc_output: self.emulate_nes_ntsc_output,
180        }
181    }
182}
183
184pub trait Renderer {
185    type Err: Debug + Display + Send + Sync + 'static;
186
187    /// Render a frame.
188    ///
189    /// The frame buffer may be larger than the specified frame size, but the len must be at least
190    /// (`frame_width` * `frame_height`). Colors past the first (`frame_width` * `frame_height`)
191    /// will be ignored.
192    ///
193    /// `target_fps` must be a finite positive value.
194    ///
195    /// If pixel aspect ratio is None, the frame will be stretched to fill the window. If it is
196    /// Some, the frame will be rendered in the largest possible area that maintains the specified
197    /// pixel aspect ratio.
198    ///
199    /// # Errors
200    ///
201    /// This method will return an error if it is unable to render the frame.
202    fn render_frame(
203        &mut self,
204        frame_buffer: &[Color],
205        frame_size: FrameSize,
206        target_fps: f64,
207        options: RenderFrameOptions,
208    ) -> Result<(), Self::Err>;
209}
210
211pub trait AudioOutput {
212    type Err: Debug + Display + Send + Sync + 'static;
213
214    /// Push a stereo audio sample.
215    ///
216    /// # Errors
217    ///
218    /// This method will return an error if it is unable to push the sample to the audio device.
219    fn push_sample(&mut self, sample_l: f64, sample_r: f64) -> Result<(), Self::Err>;
220}
221
222pub trait SaveWriter {
223    type Err: Debug + Display + Send + Sync + 'static;
224
225    /// Read an array of bytes using the given extension.
226    ///
227    /// # Errors
228    ///
229    /// Will propagate any errors encountered while reading the file.
230    fn load_bytes(&mut self, extension: &str) -> Result<Vec<u8>, Self::Err>;
231
232    /// Write a slice of bytes using the given extension.
233    ///
234    /// # Errors
235    ///
236    /// Will propagate any errors encountered while writing the file.
237    fn persist_bytes(&mut self, extension: &str, bytes: &[u8]) -> Result<(), Self::Err>;
238
239    /// Load a serialized value using the given extension.
240    ///
241    /// For loading raw bytes, use `load_bytes` instead which does not assume that the length is serialized.
242    ///
243    /// # Errors
244    ///
245    /// Will propagate any errors encountered while reading the file or deserializing the data.
246    fn load_serialized<D: Decode<()>>(&mut self, extension: &str) -> Result<D, Self::Err>;
247
248    /// Write a serialized value using the given extension.
249    ///
250    /// For writing raw bytes, use `persist_bytes` instead which does not serialize the slice length.
251    ///
252    /// # Errors
253    ///
254    /// Will propagate any errors encountered while writing the file or serializing the data.
255    fn persist_serialized<E: Encode>(&mut self, extension: &str, data: E) -> Result<(), Self::Err>;
256}
257
258pub trait PartialClone {
259    /// Create a partial clone of `self`, which clones all emulation state but may not clone
260    /// read-only fields such as ROMs and frame buffers.
261    #[must_use]
262    fn partial_clone(&self) -> Self;
263}
264
265impl<T: PartialClone> PartialClone for Option<T> {
266    fn partial_clone(&self) -> Self {
267        self.as_ref().map(T::partial_clone)
268    }
269}
270
271pub use jgenesis_proc_macros::PartialClone;
272
273#[derive(
274    Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode, EnumDisplay, EnumFromStr, EnumAll,
275)]
276#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
277#[cfg_attr(feature = "clap", derive(jgenesis_proc_macros::CustomValueEnum))]
278pub enum TimingMode {
279    #[default]
280    Ntsc,
281    Pal,
282}
283
284#[derive(Debug, Clone, Copy, PartialEq, Eq)]
285pub enum TickEffect {
286    None,
287    FrameRendered,
288}
289
290pub type TickResult<Err> = Result<TickEffect, Err>;
291
292#[derive(Debug, Clone)]
293pub struct Modal {
294    pub id: Option<Cow<'static, str>>,
295    pub text: String,
296}
297
298pub trait MappableInputs<Button> {
299    fn set_field(&mut self, button: Button, player: Player, pressed: bool);
300
301    // Value is always non-negative (0-32767)
302    #[allow(unused_variables)]
303    fn set_analog(&mut self, button: Button, player: Player, value: i16) {}
304
305    #[allow(unused_variables)]
306    fn handle_mouse_motion(
307        &mut self,
308        position: (f32, f32),
309        delta: (f32, f32),
310        display_info: DisplayInfo,
311    ) {
312    }
313
314    fn handle_mouse_leave(&mut self) {}
315
316    // Should return true when emulating a peripheral that needs relative mouse mode, e.g. an
317    // emulated mouse
318    fn needs_relative_mouse_mode(&self) -> bool {
319        false
320    }
321
322    #[allow(unused_variables)]
323    fn modal_for_input(&self, button: Button, player: Player, pressed: bool) -> Option<Modal> {
324        None
325    }
326}
327
328pub trait InputPoller<Inputs> {
329    fn poll(&mut self) -> &Inputs;
330}
331
332pub struct ConstantInputPoller<'a, Inputs>(pub &'a Inputs);
333
334impl<Inputs> InputPoller<Inputs> for ConstantInputPoller<'_, Inputs> {
335    fn poll(&mut self) -> &Inputs {
336        self.0
337    }
338}
339
340pub trait EmulatorConfigTrait: Clone + Send + Sync + 'static {
341    #[must_use]
342    fn with_overclocking_disabled(&self) -> Self {
343        self.clone()
344    }
345}
346
347pub trait EmulatorTrait: 'static {
348    type Button: Debug + Copy + Eq + Hash;
349    type Inputs: Clone + Eq + Default + MappableInputs<Self::Button> + Send + Sync + 'static;
350    type Config: EmulatorConfigTrait;
351    type SaveState: Encode + Decode<()> + Send + Sync + 'static;
352
353    type Err<RErr: Debug + Display + Send + Sync + 'static, AErr: Debug + Display + Send + Sync + 'static, SErr: Debug + Display + Send + Sync + 'static>: Error + Send + Sync + 'static;
354
355    /// Tick the emulator for a small amount of time, e.g. a single CPU instruction.
356    ///
357    /// # Errors
358    ///
359    /// This method should propagate any errors encountered while rendering frames, pushing audio
360    /// samples, or persisting save files.
361    #[allow(clippy::type_complexity)]
362    fn tick<R, A, I, S>(
363        &mut self,
364        renderer: &mut R,
365        audio_output: &mut A,
366        input_poller: &mut I,
367        save_writer: &mut S,
368    ) -> TickResult<Self::Err<R::Err, A::Err, S::Err>>
369    where
370        R: Renderer,
371        A: AudioOutput,
372        I: InputPoller<Self::Inputs>,
373        S: SaveWriter;
374
375    /// Forcibly render the current frame buffer.
376    ///
377    /// # Errors
378    ///
379    /// This method can propagate any error returned by the renderer.
380    fn force_render<R>(&mut self, renderer: &mut R) -> Result<(), R::Err>
381    where
382        R: Renderer;
383
384    fn reload_config(&mut self, config: &Self::Config);
385
386    fn soft_reset(&mut self);
387
388    fn hard_reset<S: SaveWriter>(&mut self, save_writer: &mut S);
389
390    fn load_state(&mut self, state: Self::SaveState);
391
392    fn to_save_state(&self) -> Self::SaveState;
393
394    #[must_use]
395    fn save_state_version() -> &'static str {
396        "0.14.0-1"
397    }
398
399    fn target_fps(&self) -> f64;
400
401    fn update_audio_output_frequency(&mut self, output_frequency: u64);
402
403    fn startup_modals(&self) -> Vec<Modal> {
404        vec![]
405    }
406}