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}