rust_libretro_example_core/
lib.rs1use rust_libretro::{
2 contexts::*, core::Core, env_version, input_descriptors, proc::*, retro_core, sys::*, types::*,
3};
4use std::ffi::CString;
5
6const INPUT_DESCRIPTORS: &[retro_input_descriptor] = &input_descriptors!(
7 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Up" },
8 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "Down" },
9 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Left" },
10 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Right" },
11 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "Action" },
12);
13
14#[derive(CoreOptions)]
15#[categories({
16 "advanced_settings",
17 "Advanced",
18 "Options affecting low-level emulation performance and accuracy."
19},{
20 "not_so_advanced_settings",
21 "Not So Advanced",
22 "Options not affecting low-level emulation performance and accuracy."
23})]
24#[options({
25 "foo_option_1",
26 "Advanced > Speed hack coprocessor X",
27 "Speed hack coprocessor X",
28 "Setting 'Advanced > Speed hack coprocessor X' to 'true' or 'Turbo' provides increased performance at the expense of reduced accuracy",
29 "Setting 'Speed hack coprocessor X' to 'true' or 'Turbo' provides increased performance at the expense of reduced accuracy",
30 "advanced_settings",
31 {
32 { "false" },
33 { "true" },
34 { "unstable", "Turbo (Unstable)" },
35 }
36}, {
37 "foo_option_2",
38 "Simple > Toggle Something",
39 "Toggle Something",
40 "Setting 'Simple > Toggle Something' to 'true' does something.",
41 "Setting 'Toggle Something' to 'true' does something.",
42 "not_so_advanced_settings",
43 {
44 { "false" },
45 { "true" },
46 }
47})]
48struct ExampleCore {
49 option_1: bool,
50 option_2: bool,
51
52 pixels: Vec<u8>,
53 timer: i64,
54 even: bool,
55}
56
57retro_core!(ExampleCore {
58 option_1: false,
59 option_2: true,
60
61 pixels: vec![0; 800 * 600 * 4],
62 timer: 5_000_001,
63 even: true,
64});
65
66impl Core for ExampleCore {
67 fn get_info(&self) -> SystemInfo {
68 SystemInfo {
69 library_name: CString::new("Example Core").unwrap(),
70 library_version: CString::new(env_version!("CARGO_PKG_VERSION").to_string()).unwrap(),
71 valid_extensions: CString::new("").unwrap(),
72
73 need_fullpath: false,
74 block_extract: false,
75 }
76 }
77
78 fn on_set_environment(&mut self, initial: bool, ctx: &mut SetEnvironmentContext) {
79 if !initial {
80 return;
81 }
82
83 ctx.set_support_no_game(true);
84 }
85
86 fn on_init(&mut self, ctx: &mut InitContext) {
87 let gctx: GenericContext = ctx.into();
88 gctx.set_input_descriptors(INPUT_DESCRIPTORS);
89 }
90
91 fn on_get_av_info(&mut self, _ctx: &mut GetAvInfoContext) -> retro_system_av_info {
92 retro_system_av_info {
93 geometry: retro_game_geometry {
94 base_width: 800,
95 base_height: 600,
96 max_width: 800,
97 max_height: 600,
98 aspect_ratio: 0.0,
99 },
100 timing: retro_system_timing {
101 fps: 60.0,
102 sample_rate: 0.0,
103 },
104 }
105 }
106
107 fn on_load_game(
108 &mut self,
109 _info: Option<retro_game_info>,
110 ctx: &mut LoadGameContext,
111 ) -> Result<(), Box<dyn std::error::Error>> {
112 ctx.set_pixel_format(PixelFormat::XRGB8888);
113 ctx.set_performance_level(0);
114 ctx.enable_frame_time_callback((1000000.0f64 / 60.0).round() as retro_usec_t);
115
116 let gctx: GenericContext = ctx.into();
117 gctx.enable_audio_callback();
118
119 Ok(())
120 }
121
122 fn on_options_changed(&mut self, ctx: &mut OptionsChangedContext) {
123 match ctx.get_variable("foo_option_1") {
124 Some("true") => self.option_1 = true,
125 Some("false") => self.option_1 = false,
126 _ => (),
127 }
128
129 match ctx.get_variable("foo_option_2") {
130 Some("true") => self.option_2 = true,
131 Some("false") => self.option_2 = false,
132 _ => (),
133 }
134 }
135
136 #[inline]
137 fn on_run(&mut self, ctx: &mut RunContext, delta_us: Option<i64>) {
138 let gctx: GenericContext = ctx.into();
139
140 self.timer += delta_us.unwrap_or(16_666);
141
142 let input = unsafe { ctx.get_joypad_bitmask(0, 0) };
143
144 if input.contains(JoypadState::START) && input.contains(JoypadState::SELECT) {
145 return gctx.shutdown();
146 }
147
148 if !ctx.can_dupe() || self.timer >= 1_000_000 || input.contains(JoypadState::A) {
149 self.timer = 0;
150 self.even = !self.even;
151
152 let width = 800u32;
153 let height = 600u32;
154
155 let color_a = if self.even { 0xFF } else { 0 };
156 let color_b = !color_a;
157
158 for (i, chunk) in self.pixels.chunks_exact_mut(4).enumerate() {
159 let x = (i % width as usize) as f64 / width as f64;
160 let y = (i / width as usize) as f64 / height as f64;
161
162 let total = (50.0f64 * x).floor() + (37.5f64 * y).floor();
163 let even = total as usize % 2 == 0;
164
165 let color = if even { color_a } else { color_b };
166
167 chunk.fill(color);
168 }
169
170 ctx.draw_frame(self.pixels.as_ref(), width, height, width as usize * 4);
171 } else if ctx.can_dupe() {
172 ctx.dupe_frame();
173 }
174 }
175
176 fn on_write_audio(&mut self, ctx: &mut AudioContext) {
177 ctx.queue_audio_sample(0, 0);
178 }
179}