1use bincode::{Decode, Encode}; 2use jgenesis_common::define_controller_inputs; 3use jgenesis_common::frontend::{FiniteF64, MappableInputs, Modal}; 4use jgenesis_common::input::Player; 5use jgenesis_proc_macros::{EnumAll, EnumDisplay, EnumFromStr}; 6 7define_controller_inputs! { 8 buttons: GbaButton { 9 Up -> up "Up", 10 Left -> left "Left", 11 Right -> right "Right", 12 Down -> down "Down", 13 A -> a "A", 14 B -> b "B", 15 L -> l "L", 16 R -> r "R", 17 Start -> start "Start", 18 Select -> select "Select", 19 }, 20 non_gamepad_buttons: [ 21 SolarIncreaseBrightness "Increase brightness", 22 SolarDecreaseBrightness "Decrease brightness", 23 SolarMinBrightness "Set brightness to minimum", 24 SolarMaxBrightness "Set brightness to maximum", 25 ], 26 joypad: GbaJoypadInputs impl with_allow_opposing_directions, 27} 28 29impl GbaButton { 30 #[must_use] 31 pub fn is_joypad(self) -> bool { 32 #[allow(clippy::wildcard_imports)] 33 use GbaButton::*; 34 matches!(self, Up | Left | Right | Down | A | B | L | R | Start | Select) 35 } 36 37 #[must_use] 38 pub fn is_solar_sensor(self) -> bool { 39 #[allow(clippy::wildcard_imports)] 40 use GbaButton::*; 41 matches!( 42 self, 43 SolarIncreaseBrightness 44 | SolarDecreaseBrightness 45 | SolarMinBrightness 46 | SolarMaxBrightness 47 ) 48 } 49} 50 51pub const DEFAULT_SOLAR_BRIGHTNESS_STEP: u8 = 5; 52pub const DEFAULT_SOLAR_MIN_BRIGHTNESS: u8 = 255 - 0xE8; 53pub const DEFAULT_SOLAR_MAX_BRIGHTNESS: u8 = 255 - 0x50; 54 55#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)] 56pub struct SolarSensorState { 57 pub brightness: u8, 58 pub brightness_step: u8, 59 pub min_brightness: u8, 60 pub max_brightness: u8, 61} 62 63impl Default for SolarSensorState { 64 fn default() -> Self { 65 Self { 66 brightness: DEFAULT_SOLAR_MIN_BRIGHTNESS, 67 brightness_step: DEFAULT_SOLAR_BRIGHTNESS_STEP, 68 min_brightness: DEFAULT_SOLAR_MIN_BRIGHTNESS, 69 max_brightness: DEFAULT_SOLAR_MAX_BRIGHTNESS, 70 } 71 } 72} 73 74impl SolarSensorState { 75 pub fn handle_button_press(&mut self, button: GbaButton) { 76 match button { 77 GbaButton::SolarIncreaseBrightness => { 78 self.brightness = 79 self.brightness.saturating_add(self.brightness_step).min(self.max_brightness); 80 } 81 GbaButton::SolarDecreaseBrightness => { 82 self.brightness = 83 self.brightness.saturating_sub(self.brightness_step).max(self.min_brightness); 84 } 85 GbaButton::SolarMinBrightness => { 86 self.brightness = self.min_brightness; 87 } 88 GbaButton::SolarMaxBrightness => { 89 self.brightness = self.max_brightness; 90 } 91 _ => {} 92 } 93 } 94} 95 96#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)] 97pub struct GbaInputs { 98 pub joypad: GbaJoypadInputs, 99 pub solar: SolarSensorState, 100} 101 102impl MappableInputs<GbaButton> for GbaInputs { 103 fn set_field(&mut self, button: GbaButton, _player: Player, pressed: bool) { 104 if button.is_solar_sensor() { 105 if pressed { 106 self.solar.handle_button_press(button); 107 } 108 } else { 109 self.joypad.set_button(button, pressed); 110 } 111 } 112 113 fn modal_for_input(&self, button: GbaButton, _player: Player, pressed: bool) -> Option<Modal> { 114 if !pressed || !button.is_solar_sensor() { 115 return None; 116 } 117 118 let text = format!("Solar sensor brightness: {}", self.solar.brightness); 119 Some(Modal { id: Some("solar_sensor".into()), text }) 120 } 121} 122 123#[derive( 124 Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode, EnumAll, EnumFromStr, EnumDisplay, 125)] 126#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 127#[cfg_attr(feature = "clap", derive(jgenesis_proc_macros::CustomValueEnum))] 128pub enum GbaAspectRatio { 129 #[default] 130 SquarePixels, 131 Stretched, 132} 133 134impl GbaAspectRatio { 135 #[must_use] 136 pub fn to_pixel_aspect_ratio(self) -> Option<FiniteF64> { 137 match self { 138 Self::SquarePixels => Some(FiniteF64::ONE), 139 Self::Stretched => None, 140 } 141 } 142} 143 144#[derive( 145 Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode, EnumAll, EnumFromStr, EnumDisplay, 146)] 147#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 148#[cfg_attr(feature = "clap", derive(jgenesis_proc_macros::CustomValueEnum))] 149pub enum GbaColorCorrection { 150 None, 151 #[default] 152 GbaLcd, 153} 154 155#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode, EnumAll, EnumFromStr, EnumDisplay)] 156#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 157#[cfg_attr(feature = "clap", derive(jgenesis_proc_macros::CustomValueEnum))] 158pub enum GbaSaveMemory { 159 Sram, 160 EepromUnknownSize, 161 Eeprom512, 162 Eeprom8K, 163 FlashRom64K, 164 FlashRom128K, 165 None, 166} 167 168impl GbaSaveMemory { 169 #[must_use] 170 pub fn name(self) -> &'static str { 171 match self { 172 Self::Sram => "SRAM", 173 Self::EepromUnknownSize => "EEPROM (unspecified size)", 174 Self::Eeprom512 => "EEPROM 512 bytes", 175 Self::Eeprom8K => "EEPROM 8 KB", 176 Self::FlashRom64K => "Flash ROM 64 KB", 177 Self::FlashRom128K => "Flash ROM 128 KB", 178 Self::None => "None", 179 } 180 } 181} 182 183#[derive( 184 Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode, EnumAll, EnumFromStr, EnumDisplay, 185)] 186#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 187#[cfg_attr(feature = "clap", derive(jgenesis_proc_macros::CustomValueEnum))] 188pub enum GbaAudioInterpolation { 189 #[default] 190 NearestNeighbor, 191 CubicHermite, 192 WindowedSinc, 193}