lib.rsannotatedlib.rssource153 lines · 4.2 KB · raw
1use bincode::{Decode, Encode};
2use jgenesis_common::define_controller_inputs;
3use jgenesis_common::frontend::{FiniteF64, FrameSize};
4use jgenesis_proc_macros::{EnumAll, EnumDisplay, EnumFromStr};
5
6#[derive(
7    Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode, EnumDisplay, EnumFromStr, EnumAll,
8)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10#[cfg_attr(feature = "clap", derive(jgenesis_proc_macros::CustomValueEnum))]
11pub enum SnesAspectRatio {
12    #[default]
13    Ntsc,
14    Pal,
15    SquarePixels,
16    Stretched,
17}
18
19impl SnesAspectRatio {
20    #[inline]
21    #[must_use]
22    pub fn to_pixel_aspect_ratio_f64(self) -> Option<f64> {
23        match self {
24            Self::Ntsc => Some(8.0 / 7.0),
25            Self::Pal => Some(11.0 / 8.0),
26            Self::SquarePixels => Some(1.0),
27            Self::Stretched => None,
28        }
29    }
30
31    #[inline]
32    #[must_use]
33    #[allow(clippy::missing_panics_doc)]
34    pub fn to_pixel_aspect_ratio(self, frame_size: FrameSize) -> Option<FiniteF64> {
35        let mut pixel_aspect_ratio = self.to_pixel_aspect_ratio_f64()?;
36
37        if frame_size.width == 512 && frame_size.height < 240 {
38            // Cut pixel aspect ratio in half to account for the screen being squished horizontally
39            pixel_aspect_ratio *= 0.5;
40        }
41
42        if frame_size.width == 256 && frame_size.height >= 240 {
43            // Double pixel aspect ratio to account for the screen being stretched horizontally
44            pixel_aspect_ratio *= 2.0;
45        }
46
47        Some(FiniteF64::try_from(pixel_aspect_ratio).unwrap())
48    }
49}
50
51#[derive(
52    Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode, EnumDisplay, EnumFromStr, EnumAll,
53)]
54#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
55#[cfg_attr(feature = "clap", derive(jgenesis_proc_macros::CustomValueEnum))]
56pub enum AudioInterpolationMode {
57    #[default]
58    Gaussian,
59    Hermite,
60}
61
62define_controller_inputs! {
63    buttons: SnesButton {
64        Up -> up "Up",
65        Left -> left "Left",
66        Right -> right "Right",
67        Down -> down "Down",
68        A -> a "A",
69        B -> b "B",
70        X -> x "X",
71        Y -> y "Y",
72        L -> l "L",
73        R -> r "R",
74        Start -> start "Start",
75        Select -> select "Select",
76    },
77    non_gamepad_buttons: [
78        MouseLeft "Left Button",
79        MouseRight "Right Button",
80        SuperScopeFire "Fire",
81        SuperScopeCursor "Cursor",
82        SuperScopePause "Pause",
83        SuperScopeTurboToggle "Turbo (Toggle)",
84    ],
85    joypad: SnesJoypadState impl with_allow_opposing_directions,
86}
87
88#[derive(Debug, Clone, Copy, PartialEq, Eq)]
89pub enum SuperScopeButton {
90    Fire,
91    Cursor,
92    Pause,
93    TurboToggle,
94}
95
96impl SnesButton {
97    #[inline]
98    #[must_use]
99    pub fn to_super_scope(self) -> Option<SuperScopeButton> {
100        match self {
101            Self::SuperScopeFire => Some(SuperScopeButton::Fire),
102            Self::SuperScopeCursor => Some(SuperScopeButton::Cursor),
103            Self::SuperScopePause => Some(SuperScopeButton::Pause),
104            Self::SuperScopeTurboToggle => Some(SuperScopeButton::TurboToggle),
105            _ => None,
106        }
107    }
108
109    #[inline]
110    #[must_use]
111    pub fn is_gamepad(self) -> bool {
112        matches!(
113            self,
114            Self::Up
115                | Self::Left
116                | Self::Right
117                | Self::Down
118                | Self::A
119                | Self::B
120                | Self::X
121                | Self::Y
122                | Self::L
123                | Self::R
124                | Self::Start
125                | Self::Select
126        )
127    }
128
129    #[inline]
130    #[must_use]
131    pub fn is_mouse(self) -> bool {
132        matches!(self, Self::MouseLeft | Self::MouseRight)
133    }
134
135    #[inline]
136    #[must_use]
137    pub fn is_super_scope(self) -> bool {
138        self.to_super_scope().is_some()
139    }
140}
141
142impl SuperScopeButton {
143    #[inline]
144    #[must_use]
145    pub fn to_snes_button(self) -> SnesButton {
146        match self {
147            Self::Fire => SnesButton::SuperScopeFire,
148            Self::Cursor => SnesButton::SuperScopeCursor,
149            Self::Pause => SnesButton::SuperScopePause,
150            Self::TurboToggle => SnesButton::SuperScopeTurboToggle,
151        }
152    }
153}