one_fpga/
core.rs

1use std::any::Any;
2use std::cell::UnsafeCell;
3use std::io::{Read, Seek, Write};
4use std::rc::Rc;
5use std::time::SystemTime;
6
7pub use bios::Bios;
8use image::DynamicImage;
9pub use null::NullCore;
10pub use rom::Rom;
11use serde::Serialize;
12
13use crate::inputs::{gamepad, keyboard};
14
15pub mod bios;
16pub mod null;
17pub mod rom;
18
19/// An ID that is given by the core implementation for a config menu. This
20/// ID is used to identify the menu item that was selected by the user.
21#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
22pub struct SettingId(u32);
23
24impl Serialize for SettingId {
25    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
26        self.0.serialize(serializer)
27    }
28}
29
30impl From<u32> for SettingId {
31    fn from(value: u32) -> Self {
32        Self(value)
33    }
34}
35
36impl From<SettingId> for u32 {
37    fn from(value: SettingId) -> Self {
38        value.0
39    }
40}
41
42impl From<&str> for SettingId {
43    fn from(value: &str) -> Self {
44        Self::from_label(value)
45    }
46}
47
48impl From<&String> for SettingId {
49    fn from(value: &String) -> Self {
50        Self::from_label(value)
51    }
52}
53
54impl SettingId {
55    pub fn new(id: u32) -> Self {
56        Self(id)
57    }
58
59    pub fn from_label(label: &str) -> Self {
60        let mut s: u32 = 0;
61        for c in label.as_bytes() {
62            s = s.wrapping_mul(223).wrapping_add(*c as u32);
63        }
64        Self(s)
65    }
66
67    pub fn as_u32(&self) -> u32 {
68        self.0
69    }
70}
71
72/// Core Settings, which is a list of setting items that can be displayed
73/// in the core's setting menu. This is an abstraction over all possible
74/// settings that a core can have.
75#[derive(Debug, Serialize)]
76pub struct CoreSettings {
77    title: String,
78    items: Vec<CoreSettingItem>,
79}
80
81impl CoreSettings {
82    pub fn new(title: String, items: Vec<CoreSettingItem>) -> Self {
83        Self { title, items }
84    }
85
86    pub fn items(&self) -> &[CoreSettingItem] {
87        &self.items
88    }
89}
90
91/// A core setting item that can be displayed in the core's setting menu.
92/// This is used to configure the core's settings, in an abstract way.
93///
94/// Please note that a core can create menu items that don't have actual
95/// effect on the core's behavior.
96#[derive(Debug, Serialize)]
97#[serde(rename_all = "camelCase", tag = "kind")]
98pub enum CoreSettingItem {
99    /// A menu page that contains more menu items. This is purely cosmetic.
100    Page {
101        /// A unique ID for this page.
102        id: SettingId,
103
104        /// The label that would be shown in a top-level menu.
105        label: String,
106
107        /// The title of this menu. Can be different than label.
108        title: String,
109
110        /// Menu Items contained by this menu.
111        items: Vec<CoreSettingItem>,
112
113        /// Whether this page is disabled from being selected.
114        disabled: bool,
115    },
116
117    /// A separator (horizontal line normally). Cosmetic.
118    Separator,
119
120    /// A simple label that might be selectable, but is not considered
121    /// actionable. This is used to display information to the user.
122    Label {
123        selectable: bool,
124        label: String,
125        disabled: bool,
126    },
127
128    /// A file select menu item that can be used to select a file from the
129    /// file system. This is used to load files into the core.
130    #[serde(rename = "file")]
131    FileSelect {
132        id: SettingId,
133        label: String,
134        extensions: Vec<String>,
135        disabled: bool,
136    },
137
138    /// A trigger that can be used to perform an action.
139    Trigger {
140        id: SettingId,
141        label: String,
142        disabled: bool,
143    },
144
145    /// An option that can be selected by the user and contains a boolean
146    /// value (on or off).
147    #[serde(rename = "bool")]
148    BoolOption {
149        id: SettingId,
150        label: String,
151        value: bool,
152        disabled: bool,
153    },
154
155    /// An option that can be selected by the user and contains an integer
156    /// value. This is used for options that have a range of values, but
157    /// can also represent options with 2 choices.
158    ///
159    /// It is an error to have choices less than 2 items (and will result
160    /// in an error when dealing with core menus).
161    #[serde(rename = "int")]
162    IntOption {
163        id: SettingId,
164        label: String,
165        choices: Vec<String>,
166        value: usize,
167        disabled: bool,
168    },
169}
170
171impl CoreSettingItem {
172    pub fn with_disabled(mut self, disabled: bool) -> Self {
173        self.set_disable(disabled);
174        self
175    }
176
177    pub fn set_disable(&mut self, new_disabled: bool) {
178        match self {
179            CoreSettingItem::Page { disabled, .. }
180            | CoreSettingItem::Label { disabled, .. }
181            | CoreSettingItem::Trigger { disabled, .. }
182            | CoreSettingItem::FileSelect { disabled, .. }
183            | CoreSettingItem::BoolOption { disabled, .. }
184            | CoreSettingItem::IntOption { disabled, .. } => {
185                *disabled = new_disabled;
186            }
187            _ => {}
188        }
189    }
190
191    pub fn page(
192        id: impl Into<SettingId>,
193        label: &str,
194        title: &str,
195        items: Vec<CoreSettingItem>,
196    ) -> Self {
197        CoreSettingItem::Page {
198            id: id.into(),
199            label: label.to_string(),
200            title: title.to_string(),
201            items,
202            disabled: false,
203        }
204    }
205
206    pub fn items(&self) -> Option<&Vec<CoreSettingItem>> {
207        match self {
208            CoreSettingItem::Page { items, .. } => Some(items),
209            _ => None,
210        }
211    }
212
213    pub fn items_mut(&mut self) -> Option<&mut Vec<CoreSettingItem>> {
214        match self {
215            CoreSettingItem::Page { items, .. } => Some(items),
216            _ => None,
217        }
218    }
219
220    pub fn separator() -> Self {
221        CoreSettingItem::Separator
222    }
223
224    pub fn label(selectable: bool, label: &str) -> Self {
225        CoreSettingItem::Label {
226            selectable,
227            label: label.to_string(),
228            disabled: false,
229        }
230    }
231
232    pub fn file_select(id: impl Into<SettingId>, label: &str, extensions: Vec<String>) -> Self {
233        CoreSettingItem::FileSelect {
234            id: id.into(),
235            label: label.to_string(),
236            extensions,
237            disabled: false,
238        }
239    }
240
241    pub fn trigger(id: impl Into<SettingId>, label: &str) -> Self {
242        CoreSettingItem::Trigger {
243            id: id.into(),
244            label: label.to_string(),
245            disabled: false,
246        }
247    }
248
249    pub fn bool_option(id: impl Into<SettingId>, label: &str, value: Option<bool>) -> Self {
250        CoreSettingItem::BoolOption {
251            id: id.into(),
252            label: label.to_string(),
253            value: value.unwrap_or_default(),
254            disabled: false,
255        }
256    }
257
258    pub fn int_option(
259        id: impl Into<SettingId>,
260        label: &str,
261        choices: Vec<String>,
262        value: Option<usize>,
263    ) -> Self {
264        CoreSettingItem::IntOption {
265            id: id.into(),
266            label: label.to_string(),
267            choices,
268            value: value.unwrap_or_default(),
269            disabled: false,
270        }
271    }
272
273    pub fn add_item(&mut self, sub: CoreSettingItem) {
274        if let CoreSettingItem::Page { items, .. } = self {
275            items.push(sub);
276        }
277    }
278}
279
280/// A save state, if cores support it. This is used to save the state of the
281/// internal memory of the core, so that it can be restored later.
282pub trait SaveState {
283    /// Returns true if the save state is dirty and needs to be saved.
284    fn is_dirty(&self) -> bool;
285
286    /// Save the state of the core to a buffer.
287    fn save(&mut self, writer: &mut dyn Write) -> Result<(), Error>;
288
289    /// Load the state of the core from a buffer.
290    fn load(&mut self, reader: &mut dyn Read) -> Result<(), Error>;
291}
292
293/// A mounted file to the core. This could be a SAV file, a hard drive, a memory card, or
294/// any other kind of file that can be mounted to the core.
295pub trait MountedFile: Read + Write + Seek {}
296
297/// An error that can be returned by a core.
298#[derive(Debug, thiserror::Error)]
299pub enum Error {
300    #[error("An error occurred: {0}")]
301    Generic(String),
302
303    #[error("An error occurred: {0}")]
304    IoError(#[from] std::io::Error),
305
306    #[error("An error occurred: {0}")]
307    Message(String),
308
309    #[error("An error occurred: {0}")]
310    AnyError(#[from] Box<dyn std::error::Error>),
311}
312
313impl From<String> for Error {
314    fn from(value: String) -> Self {
315        Error::Generic(value)
316    }
317}
318
319/// An iterator over the save states of a core.
320#[derive(Copy, Clone)]
321pub struct SaveStateIter<'a, T: Core> {
322    core: &'a T,
323    valid: bool,
324    index: usize,
325}
326
327impl<'a, T: Core> SaveStateIter<'a, T> {
328    pub fn new(core: &'a T) -> Self {
329        Self {
330            core,
331            valid: true,
332            index: 0,
333        }
334    }
335}
336
337impl<'a, T: Core> Iterator for SaveStateIter<'a, T> {
338    type Item = &'a dyn SaveState;
339
340    fn next(&mut self) -> Option<Self::Item> {
341        if !self.valid {
342            return None;
343        }
344
345        match self.core.save_state(self.index) {
346            Ok(Some(state)) => {
347                self.index += 1;
348                Some(state)
349            }
350            Ok(None) => None,
351            Err(_e) => {
352                self.valid = false;
353                None
354            }
355        }
356    }
357}
358
359pub trait Core {
360    /// Initialize the core and anything needed for it to run.
361    fn init(&mut self) -> Result<(), Error>;
362
363    /// Return a human-readable name for the core.
364    fn name(&self) -> &str;
365
366    /// Reset the core, restarting the ROM from the beginning.
367    fn reset(&mut self) -> Result<(), Error>;
368
369    /// Update the volume of a core. This value is to be taken as a linear
370    /// scale from 0-256 requested from the user. If the core takes a
371    /// logarithmic scale, it should convert it to the appropriate value.
372    fn set_volume(&mut self, volume: u8) -> Result<(), Error>;
373
374    /// Sets the time in the core's RTC.
375    fn set_rtc(&mut self, time: SystemTime) -> Result<(), Error>;
376
377    /// Take a screenshot (if supported), returning the image.
378    fn screenshot(&self) -> Result<DynamicImage, Error>;
379
380    /// Get the save state at a specific slot.
381    /// If the core does not support save states, this should return `None` for all slots.
382    /// If the core supports save states, but the slot index is out of bound, this should
383    /// return `None`.
384    fn save_state_mut(&mut self, slot: usize) -> Result<Option<&mut dyn SaveState>, Error>;
385
386    /// Get the save state at a specific slot.
387    /// If the core does not support save states, this should return `None` for all slots.
388    /// If the core supports save states, but the slot index is out of bound, this should
389    /// return `None`.
390    fn save_state(&self, slot: usize) -> Result<Option<&dyn SaveState>, Error>;
391
392    /// Get the mounted save file at a specific slot.
393    /// If the core does not support mounting files, this should return `None` for all slots.
394    /// If the core supports mounting files, but the slot index is out of bound, this should
395    /// return `None`.
396    fn mounted_file_mut(&mut self, slot: usize) -> Result<Option<&mut dyn MountedFile>, Error>;
397
398    /// Load a ROM into the core.
399    fn send_rom(&mut self, rom: Rom) -> Result<(), Error>;
400
401    /// Load a BIOS into the core. For cores that support multiple BIOS, the BIOS should be
402    /// selected based on the core's configuration and the slot information should be part
403    /// of [`&dyn Bios`].
404    fn send_bios(&mut self, bios: Bios) -> Result<(), Error>;
405
406    /// Send a key up event to the core.
407    fn key_up(&mut self, key: keyboard::Scancode) -> Result<(), Error>;
408
409    /// Send a key down event to the core.
410    fn key_down(&mut self, key: keyboard::Scancode) -> Result<(), Error>;
411
412    /// Set the keys that are currently pressed, releasing the keys that are not in
413    /// the list.
414    fn keys_set(&mut self, keys: keyboard::ScancodeSet) -> Result<(), Error>;
415
416    /// Return a slice of the keys that are currently pressed. The order of the
417    /// keys in the slice is not guaranteed.
418    fn keys(&self) -> Result<keyboard::ScancodeSet, Error>;
419
420    fn gamepad_button_up(&mut self, index: usize, button: gamepad::Button) -> Result<(), Error>;
421    fn gamepad_button_down(&mut self, index: usize, button: gamepad::Button) -> Result<(), Error>;
422    fn gamepad_buttons_set(
423        &mut self,
424        index: usize,
425        buttons: gamepad::ButtonSet,
426    ) -> Result<(), Error>;
427
428    /// Returns the gamepad buttons that are currently pressed. The order of the
429    /// buttons in the slice is not guaranteed.
430    /// If the core does not support gamepads at the index requested, this should
431    /// return `None`.
432    fn gamepad_buttons(&self, index: usize) -> Result<Option<gamepad::ButtonSet>, Error>;
433
434    /// Returns the menu items that the core supports. This would correspond to the
435    /// top level page of config items. If the core does not support a menu, this
436    /// should return an empty vector.
437    fn settings(&self) -> Result<CoreSettings, Error>;
438
439    /// Trigger a menu item in the core. This is used to perform an action based on
440    /// the menu item selected by the user. It can also be linked to a shortcut.
441    fn trigger(&mut self, id: SettingId) -> Result<(), Error>;
442
443    /// Send a file path to the core. This is used to load files into the core.
444    fn file_select(&mut self, id: SettingId, path: String) -> Result<(), Error>;
445
446    /// Set an integer option in the core. This is used to set an option that has a
447    /// positive integer value. Returns the new value (e.g. if value is out of bound,
448    /// the core might clip it or reset it).
449    fn int_option(&mut self, id: SettingId, value: u32) -> Result<u32, Error>;
450
451    /// Set a boolean option in the core. This is used to set an option that has a
452    /// boolean value that can be toggled by the user. Returns the new value
453    /// (e.g. if the core cannot change the value, returns the previous one).
454    fn bool_option(&mut self, id: SettingId, value: bool) -> Result<bool, Error>;
455
456    fn as_any(&self) -> &dyn Any;
457    fn as_any_mut(&mut self) -> &mut dyn Any;
458
459    /// Indicates to the core that it needs to prepare quitting.
460    /// This is used to perform any cleanup that the core needs to do before quitting.
461    /// When the core is ready, [should_quit()] should return true.
462    fn quit(&mut self);
463
464    /// Returns true if the core should quit. Some cores might want to quit
465    /// back to the main menu by themselves.
466    fn should_quit(&self) -> bool;
467}
468
469/// A core that be used in the `1FPGA` platform. This is a wrapper around a core
470/// that implements the [`Core`] trait. It can be used to pass around a core
471/// without knowing its implementation.
472///
473/// # Safety
474/// We can use UnsafeCell here because we are not sharing the core across threads.
475/// Although this can still lead to undefined behaviour, the underlying core still
476/// has access to physical memory and this does not make it less safe.
477#[derive(Clone)]
478pub struct OneFpgaCore {
479    name: String,
480    inner: Rc<UnsafeCell<dyn Core + 'static>>,
481}
482
483impl OneFpgaCore {
484    pub fn new(core: impl Core + 'static) -> Self {
485        Self {
486            name: core.name().to_string(),
487            inner: Rc::new(UnsafeCell::new(core)),
488        }
489    }
490
491    pub fn null() -> Self {
492        Self::new(NullCore)
493    }
494}
495
496impl Core for OneFpgaCore {
497    fn init(&mut self) -> Result<(), Error> {
498        unsafe { &mut *self.inner.get() }.init()
499    }
500
501    fn name(&self) -> &str {
502        &self.name
503    }
504
505    fn reset(&mut self) -> Result<(), Error> {
506        unsafe { &mut *self.inner.get() }.reset()
507    }
508
509    fn set_volume(&mut self, volume: u8) -> Result<(), Error> {
510        unsafe { &mut *self.inner.get() }.set_volume(volume)
511    }
512
513    fn set_rtc(&mut self, time: SystemTime) -> Result<(), Error> {
514        unsafe { &mut *self.inner.get() }.set_rtc(time)
515    }
516
517    fn screenshot(&self) -> Result<DynamicImage, Error> {
518        unsafe { &mut *self.inner.get() }.screenshot()
519    }
520
521    fn save_state_mut(&mut self, slot: usize) -> Result<Option<&mut dyn SaveState>, Error> {
522        unsafe { &mut *self.inner.get() }.save_state_mut(slot)
523    }
524
525    fn save_state(&self, slot: usize) -> Result<Option<&dyn SaveState>, Error> {
526        unsafe { &mut *self.inner.get() }.save_state(slot)
527    }
528
529    fn mounted_file_mut(&mut self, slot: usize) -> Result<Option<&mut dyn MountedFile>, Error> {
530        unsafe { &mut *self.inner.get() }.mounted_file_mut(slot)
531    }
532
533    fn send_rom(&mut self, rom: Rom) -> Result<(), Error> {
534        unsafe { &mut *self.inner.get() }.send_rom(rom)
535    }
536
537    fn send_bios(&mut self, bios: Bios) -> Result<(), Error> {
538        unsafe { &mut *self.inner.get() }.send_bios(bios)
539    }
540
541    fn key_up(&mut self, key: keyboard::Scancode) -> Result<(), Error> {
542        unsafe { &mut *self.inner.get() }.key_up(key)
543    }
544
545    fn key_down(&mut self, key: keyboard::Scancode) -> Result<(), Error> {
546        unsafe { &mut *self.inner.get() }.key_down(key)
547    }
548
549    fn keys_set(&mut self, keys: keyboard::ScancodeSet) -> Result<(), Error> {
550        unsafe { &mut *self.inner.get() }.keys_set(keys)
551    }
552
553    fn keys(&self) -> Result<keyboard::ScancodeSet, Error> {
554        unsafe { &mut *self.inner.get() }.keys()
555    }
556
557    fn gamepad_button_up(&mut self, index: usize, button: gamepad::Button) -> Result<(), Error> {
558        unsafe { &mut *self.inner.get() }.gamepad_button_up(index, button)
559    }
560
561    fn gamepad_button_down(&mut self, index: usize, button: gamepad::Button) -> Result<(), Error> {
562        unsafe { &mut *self.inner.get() }.gamepad_button_down(index, button)
563    }
564
565    fn gamepad_buttons_set(
566        &mut self,
567        index: usize,
568        buttons: gamepad::ButtonSet,
569    ) -> Result<(), Error> {
570        unsafe { &mut *self.inner.get() }.gamepad_buttons_set(index, buttons)
571    }
572
573    fn gamepad_buttons(&self, index: usize) -> Result<Option<gamepad::ButtonSet>, Error> {
574        unsafe { &mut *self.inner.get() }.gamepad_buttons(index)
575    }
576
577    fn settings(&self) -> Result<CoreSettings, Error> {
578        unsafe { &mut *self.inner.get() }.settings()
579    }
580
581    fn trigger(&mut self, id: SettingId) -> Result<(), Error> {
582        unsafe { &mut *self.inner.get() }.trigger(id)
583    }
584
585    fn file_select(&mut self, id: SettingId, path: String) -> Result<(), Error> {
586        unsafe { &mut *self.inner.get() }.file_select(id, path)
587    }
588
589    fn int_option(&mut self, id: SettingId, value: u32) -> Result<u32, Error> {
590        unsafe { &mut *self.inner.get() }.int_option(id, value)
591    }
592
593    fn bool_option(&mut self, id: SettingId, value: bool) -> Result<bool, Error> {
594        unsafe { &mut *self.inner.get() }.bool_option(id, value)
595    }
596
597    fn as_any(&self) -> &dyn Any {
598        unsafe { &mut *self.inner.get() }.as_any()
599    }
600
601    fn as_any_mut(&mut self) -> &mut dyn Any {
602        unsafe { &mut *self.inner.get() }.as_any_mut()
603    }
604
605    fn quit(&mut self) {
606        unsafe { &mut *self.inner.get() }.quit();
607    }
608
609    fn should_quit(&self) -> bool {
610        unsafe { &*self.inner.get() }.should_quit()
611    }
612}