1use crate::{buttons::InputPort, error::*};
2use apollo_hyper_api_standard::{ApolloEmulator, ApolloMultiEmulator};
3use apollo_hyper_libretro_sys::*;
4use libc::c_char;
5use libloading::{Library, Symbol};
6use std::{
7 ffi::{c_void, CStr, CString},
8 fs::File,
9 io::Read,
10 marker::PhantomData,
11 panic,
12 path::{Path, PathBuf},
13 ptr,
14};
15
16type NotSendSync = *const [u8; 0];
17
18static mut EMULATOR: *mut EmulatorCore = ptr::null_mut();
19static mut CONTEXT: *mut EmulatorContext = ptr::null_mut();
20
21struct EmulatorCore {
22 core_lib: Box<Library>,
23 #[allow(dead_code)]
24 core_path: CString,
25 system_path: CString,
26 rom_path: CString,
27 core: CoreAPI,
28 _marker: PhantomData<NotSendSync>,
29}
30
31struct EmulatorContext {
32 audio_sample: Vec<i16>,
33 input_ports: [InputPort; 2],
34 frame_ptr: *const c_void,
35 frame_pitch: usize,
36 frame_width: u32,
37 frame_height: u32,
38 pixfmt: PixelFormat,
39 image_depth: usize,
40 memory_map: Vec<MemoryDescriptor>,
41 _marker: PhantomData<NotSendSync>,
42}
43
44#[derive(Debug, Clone, PartialEq, Eq, Hash)]
46pub struct MemoryRegion {
47 which: usize,
48 pub flags: u64,
49 pub len: usize,
50 pub start: usize,
51 pub offset: usize,
52 pub name: String,
53 pub select: usize,
54 pub disconnect: usize,
55}
56
57pub struct Emulator {
59 phantom: PhantomData<NotSendSync>,
60 system_info: SystemInfo,
61 system_av_info: SystemAvInfo,
62}
63
64pub struct MultiEmulator {}
65
66pub struct RetroSystemInfo {
67 pub library_name: CString,
68 pub extensions: CString,
69}
70
71fn create_core(core_path: &Path) -> (Box<Library>, CoreAPI) {
72 let suffix = if cfg!(target_os = "windows") {
73 "dll"
74 } else if cfg!(target_os = "macos") {
75 "dylib"
76 } else if cfg!(target_os = "linux") {
77 "so"
78 } else {
79 panic!("Unsupported platform")
80 };
81 let path: PathBuf = core_path.with_extension(suffix);
82 #[cfg(target_os = "linux")]
83 let library: Library = unsafe {
84 ::libloading::os::unix::Library::open(Some(path), 0x2 | 0x1000)
86 .unwrap()
87 .into()
88 };
89
90 #[cfg(not(target_os = "linux"))]
91 let library = unsafe { Library::new(path).unwrap() };
92 let dll = Box::new(library);
93
94 unsafe {
95 let retro_set_environment = *(dll.get(b"retro_set_environment").unwrap());
96 let retro_set_video_refresh = *(dll.get(b"retro_set_video_refresh").unwrap());
97 let retro_set_audio_sample = *(dll.get(b"retro_set_audio_sample").unwrap());
98 let retro_set_audio_sample_batch = *(dll.get(b"retro_set_audio_sample_batch").unwrap());
99 let retro_set_input_poll = *(dll.get(b"retro_set_input_poll").unwrap());
100 let retro_set_input_state = *(dll.get(b"retro_set_input_state").unwrap());
101 let retro_init = *(dll.get(b"retro_init").unwrap());
102 let retro_deinit = *(dll.get(b"retro_deinit").unwrap());
103 let retro_api_version = *(dll.get(b"retro_api_version").unwrap());
104 let retro_get_system_info = *(dll.get(b"retro_get_system_info").unwrap());
105 let retro_get_system_av_info = *(dll.get(b"retro_get_system_av_info").unwrap());
106 let retro_set_controller_port_device =
107 *(dll.get(b"retro_set_controller_port_device").unwrap());
108 let retro_reset = *(dll.get(b"retro_reset").unwrap());
109 let retro_run = *(dll.get(b"retro_run").unwrap());
110 let retro_serialize_size = *(dll.get(b"retro_serialize_size").unwrap());
111 let retro_serialize = *(dll.get(b"retro_serialize").unwrap());
112 let retro_unserialize = *(dll.get(b"retro_unserialize").unwrap());
113 let retro_cheat_reset = *(dll.get(b"retro_cheat_reset").unwrap());
114 let retro_cheat_set = *(dll.get(b"retro_cheat_set").unwrap());
115 let retro_load_game = *(dll.get(b"retro_load_game").unwrap());
116 let retro_load_game_special = *(dll.get(b"retro_load_game_special").unwrap());
117 let retro_unload_game = *(dll.get(b"retro_unload_game").unwrap());
118 let retro_get_region = *(dll.get(b"retro_get_region").unwrap());
119 let retro_get_memory_data = *(dll.get(b"retro_get_memory_data").unwrap());
120 let retro_get_memory_size = *(dll.get(b"retro_get_memory_size").unwrap());
121 let core = CoreAPI {
122 retro_set_environment,
123 retro_set_video_refresh,
124 retro_set_audio_sample,
125 retro_set_audio_sample_batch,
126 retro_set_input_poll,
127 retro_set_input_state,
128
129 retro_init,
130 retro_deinit,
131
132 retro_api_version,
133
134 retro_get_system_info,
135 retro_get_system_av_info,
136 retro_set_controller_port_device,
137
138 retro_reset,
139 retro_run,
140
141 retro_serialize_size,
142 retro_serialize,
143 retro_unserialize,
144
145 retro_cheat_reset,
146 retro_cheat_set,
147
148 retro_load_game,
149 retro_load_game_special,
150 retro_unload_game,
151
152 retro_get_region,
153 retro_get_memory_data,
154 retro_get_memory_size,
155 };
156 (dll, core)
157 }
158}
159
160impl ApolloMultiEmulator for MultiEmulator {
161 fn create_emulator(core_path: &Path, rom_path: &Path) -> Box<dyn ApolloEmulator> {
163 unsafe {
164 assert!(EMULATOR.is_null());
165 assert!(CONTEXT.is_null());
166 }
167
168 let (dll, core) = create_core(core_path);
169
170 let emu = EmulatorCore {
171 core_lib: dll,
172 rom_path: CString::new(rom_path.to_str().unwrap()).unwrap(),
173 core_path: CString::new(core_path.to_str().unwrap()).unwrap(),
174 system_path: { CString::new(core_path.with_extension("").to_str().unwrap()).unwrap() },
175 core: core.clone(),
176 _marker: PhantomData,
177 };
178
179 unsafe {
180 let emup = Box::new(emu);
181 EMULATOR = Box::leak(emup);
183 let ctx = EmulatorContext {
185 audio_sample: Vec::new(),
186 input_ports: [InputPort::new(), InputPort::new()],
187 frame_ptr: ptr::null(),
188 frame_pitch: 0,
189 frame_width: 0,
190 frame_height: 0,
191 pixfmt: PixelFormat::ARGB1555,
192 image_depth: 0,
193 memory_map: Vec::new(),
194 _marker: PhantomData,
195 };
196 let ctxp = Box::new(ctx);
198 CONTEXT = Box::leak(ctxp);
199 let emu = &(*EMULATOR);
200 (emu.core.retro_set_environment)(callback_environment);
202 (emu.core.retro_set_video_refresh)(callback_video_refresh);
203 (emu.core.retro_set_audio_sample)(callback_audio_sample);
204 (emu.core.retro_set_audio_sample_batch)(callback_audio_sample_batch);
205 (emu.core.retro_set_input_poll)(callback_input_poll);
206 (emu.core.retro_set_input_state)(callback_input_state);
207 (emu.core.retro_init)();
209 let rom_cstr = &(*EMULATOR).rom_path;
210
211 let mut rom_file = File::open(rom_path).unwrap();
212 let mut buffer = Vec::new();
213 rom_file.read_to_end(&mut buffer).unwrap();
214 buffer.shrink_to_fit();
215 let game_info = GameInfo {
216 path: rom_cstr.as_ptr(),
217 data: buffer.as_ptr() as *const c_void,
218 size: buffer.len(),
219 meta: ptr::null(),
220 };
221
222 let load_game_successful = (emu.core.retro_load_game)(&game_info);
223 assert!(load_game_successful);
224
225 let mut system_info = SystemInfo {
226 library_name: ptr::null(),
227 library_version: ptr::null(),
228 valid_extensions: ptr::null(),
229 need_fullpath: false,
230 block_extract: false,
231 };
232 let mut system_av_info = SystemAvInfo {
233 geometry: GameGeometry {
234 base_width: 0,
235 base_height: 0,
236 max_width: 0,
237 max_height: 0,
238 aspect_ratio: 0.0,
239 },
240 timing: SystemTiming {
241 fps: 0.0,
242 sample_rate: 0.0,
243 },
244 };
245
246 (core.retro_get_system_info)(&mut system_info);
247 (core.retro_get_system_av_info)(&mut system_av_info);
248
249 Box::new(Emulator {
250 phantom: PhantomData,
251 system_info,
252 system_av_info,
253 })
254 }
255 }
256}
257
258impl ApolloEmulator for Emulator {
259 fn boot(&self, _: &Path) {}
260}
261
262impl Emulator {
263 pub fn create_for_system_info(core_path: &Path) -> RetroSystemInfo {
264 let (_, core) = create_core(core_path);
265 let mut system_info = SystemInfo {
266 library_name: ptr::null(),
267 library_version: ptr::null(),
268 valid_extensions: ptr::null(),
269 need_fullpath: false,
270 block_extract: false,
271 };
272
273 let to_cstring = |ptr| unsafe { CStr::from_ptr(ptr).to_owned() };
274
275 unsafe {
276 (core.retro_get_system_info)(&mut system_info);
277 RetroSystemInfo {
278 library_name: to_cstring(system_info.library_name),
279 extensions: to_cstring(system_info.valid_extensions),
280 }
281 }
282 }
283
284 pub fn get_library(&mut self) -> &Library {
285 unsafe { &(*EMULATOR).core_lib }
286 }
287 pub fn get_symbol<'a, T>(&'a self, symbol: &[u8]) -> Option<Symbol<'a, T>> {
288 let dll = unsafe { &(*EMULATOR).core_lib };
289 let sym: Result<Symbol<T>, _> = unsafe { dll.get(symbol) };
290 if sym.is_err() {
291 return None;
292 }
293 Some(sym.unwrap())
294 }
295 pub fn run(&mut self, inputs: [InputPort; 2]) {
296 unsafe {
297 (*CONTEXT).audio_sample.clear();
299 (*CONTEXT).input_ports = inputs;
301 ((*EMULATOR).core.retro_run)()
303 }
304 }
305 pub fn reset(&mut self) {
306 unsafe {
307 (*CONTEXT).audio_sample.clear();
309 (*CONTEXT).input_ports = [InputPort::new(), InputPort::new()];
311 (*CONTEXT).frame_ptr = ptr::null();
313 ((*EMULATOR).core.retro_reset)()
314 }
315 }
316 fn get_ram_size(&self, rtype: libc::c_uint) -> usize {
317 unsafe { ((*EMULATOR).core.retro_get_memory_size)(rtype) }
318 }
319 pub fn get_video_ram_size(&self) -> usize {
320 self.get_ram_size(MEMORY_VIDEO_RAM)
321 }
322 pub fn get_system_ram_size(&self) -> usize {
323 self.get_ram_size(MEMORY_SYSTEM_RAM)
324 }
325 pub fn get_save_ram_size(&self) -> usize {
326 self.get_ram_size(MEMORY_SAVE_RAM)
327 }
328 pub fn video_ram_ref(&self) -> &[u8] {
329 self.get_ram(MEMORY_VIDEO_RAM)
330 }
331 pub fn system_ram_ref(&self) -> &[u8] {
332 self.get_ram(MEMORY_SYSTEM_RAM)
333 }
334 pub fn system_ram_mut(&mut self) -> &mut [u8] {
335 self.get_ram_mut(MEMORY_SYSTEM_RAM)
336 }
337 pub fn save_ram(&self) -> &[u8] {
338 self.get_ram(MEMORY_SAVE_RAM)
339 }
340
341 fn get_ram(&self, ramtype: libc::c_uint) -> &[u8] {
342 let len = self.get_ram_size(ramtype);
343 unsafe {
344 let ptr: *const u8 = ((*EMULATOR).core.retro_get_memory_data)(ramtype).cast();
345 std::slice::from_raw_parts(ptr, len)
346 }
347 }
348
349 fn get_ram_mut(&mut self, ramtype: libc::c_uint) -> &mut [u8] {
350 let len = self.get_ram_size(ramtype);
351 unsafe {
352 let ptr: *mut u8 = ((*EMULATOR).core.retro_get_memory_data)(ramtype).cast();
353 std::slice::from_raw_parts_mut(ptr, len)
354 }
355 }
356
357 pub fn memory_regions(&self) -> Vec<MemoryRegion> {
358 let map = unsafe { &((*CONTEXT).memory_map) };
359 map.iter()
360 .enumerate()
361 .map(|(i, mdesc)| MemoryRegion {
362 which: i,
363 flags: mdesc.flags,
364 len: mdesc.len,
365 start: mdesc.start,
366 offset: mdesc.offset,
367 select: mdesc.select,
368 disconnect: mdesc.disconnect,
369 name: if mdesc.addrspace.is_null() {
370 "".to_owned()
371 } else {
372 unsafe { CStr::from_ptr(mdesc.addrspace) }
373 .to_string_lossy()
374 .into_owned()
375 },
376 })
377 .collect()
378 }
379 pub fn memory_ref(&self, start: usize) -> Result<&[u8], RetroRsError> {
380 for mr in self.memory_regions() {
381 if mr.select != 0 && (start & mr.select) == 0 {
382 continue;
383 }
384 if start >= mr.start && start < mr.start + mr.len {
385 return self.memory_ref_mut(mr, start).map(|slice| &*slice);
386 }
387 }
388 Err(RetroRsError::RAMCopyNotMappedIntoMemoryRegionError)
389 }
390 pub fn memory_ref_mut(
391 &self,
392 mr: MemoryRegion,
393 start: usize,
394 ) -> Result<&mut [u8], RetroRsError> {
395 let maps = unsafe { &(*CONTEXT).memory_map };
396 if mr.which >= maps.len() {
397 return Err(RetroRsError::RAMMapOutOfRangeError);
399 }
400 if start < mr.start {
401 return Err(RetroRsError::RAMCopySrcOutOfBoundsError);
402 }
403 let start = (start - mr.start) & !mr.disconnect;
404 let map = &maps[mr.which];
405 let ptr: *mut u8 = map.ptr.cast();
407 let slice = unsafe {
408 let ptr = ptr.add(start).add(map.offset);
409 std::slice::from_raw_parts_mut(ptr, map.len - start)
410 };
411 Ok(slice)
412 }
413
414 pub fn pixel_format(&self) -> PixelFormat {
415 unsafe { (*CONTEXT).pixfmt }
416 }
417 pub fn framebuffer_size(&self) -> (usize, usize) {
418 unsafe {
419 (
420 (*CONTEXT).frame_width as usize,
421 (*CONTEXT).frame_height as usize,
422 )
423 }
424 }
425 pub fn framebuffer_pitch(&self) -> usize {
426 unsafe { (*CONTEXT).frame_pitch }
427 }
428 pub fn peek_framebuffer<FBPeek, FBPeekRet>(&self, f: FBPeek) -> Result<FBPeekRet, RetroRsError>
429 where
430 FBPeek: FnOnce(&[u8]) -> FBPeekRet,
431 {
432 unsafe {
433 if (*CONTEXT).frame_ptr.is_null() {
434 Err(RetroRsError::NoFramebufferError)
435 } else {
436 let frame_slice = std::slice::from_raw_parts(
437 (*CONTEXT).frame_ptr as *const u8,
438 ((*CONTEXT).frame_height * ((*CONTEXT).frame_pitch as u32)) as usize,
439 );
440 Ok(f(frame_slice))
441 }
442 }
443 }
444
445 pub fn peek_audio_buffer<F, R>(&self, f: F) -> Result<R, RetroRsError>
446 where
447 F: FnOnce(&[i16]) -> R,
448 {
449 unsafe {
450 let slice = &(*CONTEXT).audio_sample[..];
451 Ok(f(slice))
452 }
453 }
454
455 pub fn save(&self, bytes: &mut [u8]) {
456 let size = self.save_size();
457 assert!(bytes.len() >= size);
458 unsafe { ((*EMULATOR).core.retro_serialize)(bytes.as_mut_ptr() as *mut c_void, size) }
459 }
460 pub fn load(&mut self, bytes: &[u8]) -> bool {
461 let size = self.save_size();
462 assert!(bytes.len() >= size);
463 unsafe { ((*EMULATOR).core.retro_unserialize)(bytes.as_ptr() as *const c_void, size) }
464 }
465 pub fn save_size(&self) -> usize {
466 unsafe { ((*EMULATOR).core.retro_serialize_size)() }
467 }
468 pub fn clear_cheats(&mut self) {
469 unsafe { ((*EMULATOR).core.retro_cheat_reset)() }
470 }
471 pub fn set_cheat(&mut self, index: usize, enabled: bool, code: &str) {
472 unsafe {
473 ((*EMULATOR).core.retro_cheat_set)(
475 index as u32,
476 enabled,
477 CString::new(code).unwrap().into_raw(),
478 )
479 }
480 }
481
482 pub fn system_info(&self) -> &SystemInfo {
483 &self.system_info
484 }
485
486 pub fn system_av_info(&self) -> &SystemAvInfo {
487 &self.system_av_info
488 }
489}
490
491unsafe extern "C" fn callback_environment(cmd: u32, data: *mut c_void) -> bool {
492 let result = panic::catch_unwind(|| {
493 match cmd {
494 ENVIRONMENT_SET_CONTROLLER_INFO => true,
495 ENVIRONMENT_SET_PIXEL_FORMAT => {
496 let pixfmti = *(data as *const u32);
497 let pixfmt = PixelFormat::from_uint(pixfmti);
498 if pixfmt.is_none() {
499 return false;
500 }
501 let pixfmt = pixfmt.unwrap();
502 (*CONTEXT).image_depth = match pixfmt {
503 PixelFormat::ARGB1555 => 15,
504 PixelFormat::ARGB8888 => 32,
505 PixelFormat::RGB565 => 16,
506 };
507 (*CONTEXT).pixfmt = pixfmt;
508 true
509 }
510 ENVIRONMENT_GET_SYSTEM_DIRECTORY => {
511 *(data as *mut *const c_char) = (*EMULATOR).system_path.as_ptr();
512 println!("{}", (*EMULATOR).system_path.to_str().unwrap());
513 true
514 }
515 ENVIRONMENT_GET_CAN_DUPE => {
516 *(data as *mut bool) = true;
517 true
518 }
519 ENVIRONMENT_SET_MEMORY_MAPS => {
520 let map = data as *const MemoryMap;
521 let desc_slice =
522 std::slice::from_raw_parts((*map).descriptors, (*map).num_descriptors as usize);
523 (*CONTEXT).memory_map = Vec::new();
525 (*CONTEXT).memory_map.extend_from_slice(desc_slice);
527 true
529 }
530
531 ENVIRONMENT_GET_VARIABLE => {
533 let variable = data as *const Variable;
534 let key = CStr::from_ptr((*(variable)).key).to_str().unwrap();
535
536 let value = match key {
537 "pcsx2_fastboot" => Some("disabled"),
539 "pcsx2_memcard_slot_1" => Some("shared8"),
540 "pcsx2_memcard_slot_2" => Some("empty"),
541 "pcsx2_renderer" => Some("Auto"),
542
543 "pcsx2_upscale_multiplier" => Some("1"),
544 "pcsx2_palette_conversion" => Some("disabled"),
545 "pcsx2_userhack_fb_conversion" => Some("disabled"),
546 "pcsx2_userhack_auto_flush" => Some("disabled"),
547 "pcsx2_fast_invalidation" => Some("disabled"),
548 "pcsx2_speedhacks_presets" => Some("1"),
549 "pcsx2_fastcdvd" => Some("disabled"),
550 "pcsx2_deinterlace_mode" => Some("7"),
551 "pcsx2_enable_60fps_patches" => Some("disabled"),
552 "pcsx2_enable_widescreen_patches" => Some("disabled"),
553 "pcsx2_frameskip" => Some("disabled"),
554 "pcsx2_frames_to_draw" => Some("1"),
555 "pcsx2_frames_to_skip" => Some("1"),
556 "pcsx2_vsync_mtgs_queue" => Some("2"),
557 "pcsx2_enable_cheats" => Some("disabled"),
558 "pcsx2_clamping_mode" => Some("1"),
559 "pcsx2_round_mode" => Some("3"),
560 "pcsx2_vu_clamping_mode" => Some("1"),
561 "pcsx2_vu_round_mode" => Some("3"),
562 "pcsx2_gamepad_l_deadzone" => Some("0"),
563 "pcsx2_gamepad_r_deadzone" => Some("0"),
564 "pcsx2_rumble_intensity" => Some("100"),
565 "pcsx2_rumble_enable" => Some("enabled"),
566
567 "beetle_psx_renderer" => Some("software"),
568 "beetle_psx_hw_renderer" => Some("software"),
569
570 "desmume_pointer_mouse" => Some("enabled"),
571 "desmume_pointer_device_l" => Some("absolute"),
572 "desmume_pointer_device_r" => Some("absolute"),
573 key => {
575 eprintln!("INTERNAL: Unrecognized variable: {key}");
576 None
577 }
578 };
579
580 value.is_some()
582 }
583 ENVIRONMENT_GET_LOG_INTERFACE => {
584 unsafe extern "C" fn log_function(
586 level: LogLevel,
587 fmt: *const libc::c_char,
588 mut args: ...
589 ) {
590 let level = match level {
591 LogLevel::Debug => "DEBUG",
592 LogLevel::Info => "INFO",
593 LogLevel::Warn => "WARN",
594 LogLevel::Error => "ERROR",
595 };
596 let format_args = printf_compat::output::display(fmt, args.as_va_list());
597 eprint!("{level}: {format_args}");
598 }
599
600 let log_callback = data as *mut LogCallback;
601 (*log_callback).log = log_function;
602 true
603 }
604 ENVIRONMENT_SET_MESSAGE => {
605 let msg = data as *const Message;
606 eprint!(
607 "--- MESSAGE: {}",
608 CStr::from_ptr((*msg).msg).to_str().unwrap()
609 );
610 true
611 }
612 _ => false,
613 }
614 });
615 result.unwrap_or(false)
616}
617
618extern "C" fn callback_video_refresh(data: *const c_void, width: u32, height: u32, pitch: usize) {
619 unsafe {
621 if !data.is_null() {
623 (*CONTEXT).frame_ptr = data;
624 (*CONTEXT).frame_pitch = pitch;
625 (*CONTEXT).frame_width = width;
626 (*CONTEXT).frame_height = height;
627 }
628 }
629}
630extern "C" fn callback_audio_sample(left: i16, right: i16) {
631 unsafe {
633 let sample_buf = &mut (*CONTEXT).audio_sample;
634 sample_buf.push(left);
635 sample_buf.push(right);
636 }
637}
638extern "C" fn callback_audio_sample_batch(data: *const i16, frames: usize) -> usize {
639 unsafe {
641 let sample_buf = &mut (*CONTEXT).audio_sample;
642 let slice = std::slice::from_raw_parts(data, frames * 2);
643 sample_buf.clear();
644 sample_buf.extend_from_slice(slice);
645 frames
646 }
647}
648
649extern "C" fn callback_input_poll() {}
650
651extern "C" fn callback_input_state(port: u32, device: u32, index: u32, id: u32) -> i16 {
652 if port > 1 {
653 println!("Unsupported port {port} (only two controllers are implemented)");
654 return 0;
655 }
656
657 let port = port as usize;
658
659 match (device, index, id) {
660 (DEVICE_NONE, ..) => 0,
662 (DEVICE_JOYPAD, 0, id) => {
664 if id > 16 {
665 println!("Unexpected button id {id}");
666 return 0;
667 }
668 unsafe {
669 if (*CONTEXT).input_ports[port].buttons.get(id) {
670 1
671 } else {
672 0
673 }
674 }
675 }
676 (DEVICE_MOUSE, 0, id) => unsafe {
678 println!("MOUSE REQUESTED!!!!");
679 let left = (*CONTEXT).input_ports[port].mouse_left_down;
680 let right = (*CONTEXT).input_ports[port].mouse_right_down;
681 let middle = (*CONTEXT).input_ports[port].mouse_middle_down;
682 let bool_to_i16 = |b| if b { 1 } else { 0 };
683
684 match id {
685 DEVICE_ID_MOUSE_X => (*CONTEXT).input_ports[port].mouse_x,
686 DEVICE_ID_MOUSE_Y => (*CONTEXT).input_ports[port].mouse_y,
687 DEVICE_ID_MOUSE_LEFT => bool_to_i16(left),
688 DEVICE_ID_MOUSE_RIGHT => bool_to_i16(right),
689 DEVICE_ID_MOUSE_MIDDLE => bool_to_i16(middle),
690 _ => 0,
691 }
692 },
693 (DEVICE_KEYBOARD, ..) => {
695 println!("Keyboard input method unsupported");
696 0
697 }
698 (DEVICE_LIGHTGUN, ..) => {
700 println!("Lightgun input method unsupported");
701 0
702 }
703 (DEVICE_ANALOG, DEVICE_INDEX_ANALOG_LEFT, DEVICE_ID_ANALOG_X) => unsafe {
708 (*CONTEXT).input_ports[port].joystick_x
709 },
710 (DEVICE_ANALOG, DEVICE_INDEX_ANALOG_LEFT, DEVICE_ID_ANALOG_Y) => unsafe {
711 (*CONTEXT).input_ports[port].joystick_y
712 },
713 (6, ..) => {
715 println!("Pointer input method unsupported");
716 0
717 }
718 _ => {
719 println!("Unsupported device/index/id ({device}/{index}/{id})");
720 0
721 }
722 }
723}
724
725impl Drop for Emulator {
726 fn drop(&mut self) {
727 unsafe {
728 ((*EMULATOR).core.retro_unload_game)();
729 ((*EMULATOR).core.retro_deinit)();
730 }
731 unsafe {
733 let _ctx = Box::from_raw(CONTEXT);
735 let _emu = Box::from_raw(EMULATOR);
736 CONTEXT = ptr::null_mut();
737 EMULATOR = ptr::null_mut();
738 }
739 }
741}