frontend: update libpicofe, fix missed callbacks
[pcsx_rearmed.git] / frontend / libretro.c
... / ...
CommitLineData
1/*
2 * (C) notaz, 2012,2014,2015
3 *
4 * This work is licensed under the terms of the GNU GPLv2 or later.
5 * See the COPYING file in the top-level directory.
6 */
7
8#define _GNU_SOURCE 1 // strcasestr
9#include <stdio.h>
10#include <stdlib.h>
11#include <stdint.h>
12#include <string.h>
13#include <strings.h>
14#include <assert.h>
15#ifdef __MACH__
16#include <unistd.h>
17#include <sys/syscall.h>
18#endif
19
20#include "retro_miscellaneous.h"
21#ifdef SWITCH
22#include <switch.h>
23#endif
24
25#include "../libpcsxcore/misc.h"
26#include "../libpcsxcore/psxcounters.h"
27#include "../libpcsxcore/psxmem_map.h"
28#include "../libpcsxcore/new_dynarec/new_dynarec.h"
29#include "../libpcsxcore/cdrom.h"
30#include "../libpcsxcore/cdrom-async.h"
31#include "../libpcsxcore/cdriso.h"
32#include "../libpcsxcore/cheat.h"
33#include "../libpcsxcore/r3000a.h"
34#include "../libpcsxcore/gpu.h"
35#include "../libpcsxcore/database.h"
36#include "../plugins/dfsound/out.h"
37#include "../plugins/dfsound/spu_config.h"
38#include "cspace.h"
39#include "main.h"
40#include "menu.h"
41#include "plugin.h"
42#include "plugin_lib.h"
43#include "arm_features.h"
44#include "revision.h"
45
46#include <libretro.h>
47#include "libretro_core_options.h"
48
49#ifdef USE_LIBRETRO_VFS
50#include <streams/file_stream_transforms.h>
51#endif
52
53#ifdef _3DS
54#include <3ds/svc.h>
55#include <3ds/services/apt.h>
56#include <3ds/allocator/linear.h>
57#include "3ds/3ds_utils.h"
58#endif
59
60#ifndef MAP_FAILED
61#define MAP_FAILED ((void *)(intptr_t)-1)
62#endif
63
64#define PORTS_NUMBER 8
65
66#ifndef MIN
67#define MIN(a, b) ((a) < (b) ? (a) : (b))
68#endif
69
70#ifndef MAX
71#define MAX(a, b) ((a) > (b) ? (a) : (b))
72#endif
73
74#define ISHEXDEC ((buf[cursor] >= '0') && (buf[cursor] <= '9')) || ((buf[cursor] >= 'a') && (buf[cursor] <= 'f')) || ((buf[cursor] >= 'A') && (buf[cursor] <= 'F'))
75
76//hack to prevent retroarch freezing when reseting in the menu but not while running with the hot key
77static int rebootemu = 0;
78
79static retro_video_refresh_t video_cb;
80static retro_input_poll_t input_poll_cb;
81static retro_input_state_t input_state_cb;
82static retro_environment_t environ_cb;
83static retro_audio_sample_batch_t audio_batch_cb;
84static retro_set_rumble_state_t rumble_cb;
85static struct retro_log_callback logging;
86static retro_log_printf_t log_cb;
87
88#define LogWarn(fmt, ...) do { \
89 if (log_cb) log_cb(RETRO_LOG_WARN, fmt, ##__VA_ARGS__); \
90} while (0)
91#define LogErr(fmt, ...) do { \
92 if (log_cb) log_cb(RETRO_LOG_ERROR, fmt, ##__VA_ARGS__); \
93} while (0)
94
95static unsigned msg_interface_version = 0;
96
97static void *vout_buf;
98static void *vout_buf_ptr;
99static int vout_width = 256, vout_height = 240, vout_pitch_b = 256*2;
100static int vout_fb_dirty;
101static int psx_w, psx_h;
102static bool vout_can_dupe;
103static bool found_bios;
104static int display_internal_fps;
105static bool libretro_supports_bitmasks = false;
106static bool libretro_supports_option_categories = false;
107static bool show_input_settings = true;
108#ifdef GPU_PEOPS
109static bool show_advanced_gpu_peops_settings = true;
110#endif
111#ifdef GPU_UNAI
112static bool show_advanced_gpu_unai_settings = true;
113#endif
114static float mouse_sensitivity = 1.0f;
115static unsigned int disk_current_index;
116
117typedef enum
118{
119 FRAMESKIP_NONE = 0,
120 FRAMESKIP_AUTO,
121 FRAMESKIP_AUTO_THRESHOLD,
122 FRAMESKIP_FIXED_INTERVAL
123} frameskip_type_t;
124
125static unsigned frameskip_type = FRAMESKIP_NONE;
126static unsigned frameskip_threshold = 0;
127static unsigned frameskip_interval = 0;
128static unsigned frameskip_counter = 0;
129
130static int retro_audio_buff_active = false;
131static unsigned retro_audio_buff_occupancy = 0;
132static int retro_audio_buff_underrun = false;
133
134static unsigned retro_audio_latency = 0;
135static int update_audio_latency = false;
136
137static unsigned int current_width;
138static unsigned int current_height;
139static enum retro_pixel_format current_fmt;
140
141static int plugins_opened;
142
143#define is_pal_mode Config.PsxType
144
145/* memory card data */
146extern char Mcd1Data[MCD_SIZE];
147extern char Mcd2Data[MCD_SIZE];
148extern char McdDisable[2];
149
150/* PCSX ReARMed core calls and stuff */
151int in_type[8] = {
152 PSE_PAD_TYPE_NONE, PSE_PAD_TYPE_NONE,
153 PSE_PAD_TYPE_NONE, PSE_PAD_TYPE_NONE,
154 PSE_PAD_TYPE_NONE, PSE_PAD_TYPE_NONE,
155 PSE_PAD_TYPE_NONE, PSE_PAD_TYPE_NONE
156};
157int in_analog_left[8][2] = { { 127, 127 }, { 127, 127 }, { 127, 127 }, { 127, 127 }, { 127, 127 }, { 127, 127 }, { 127, 127 }, { 127, 127 } };
158int in_analog_right[8][2] = { { 127, 127 }, { 127, 127 }, { 127, 127 }, { 127, 127 }, { 127, 127 }, { 127, 127 }, { 127, 127 }, { 127, 127 } };
159unsigned short in_keystate[PORTS_NUMBER];
160int in_mouse[8][2];
161int multitap1 = 0;
162int multitap2 = 0;
163int in_enable_vibration = 1;
164static int in_enable_crosshair[2] = { 0, 0 };
165static int in_dualshock_analog_combo = 0;
166static bool in_dualshock_toggling = false;
167
168// NegCon adjustment parameters
169// > The NegCon 'twist' action is somewhat awkward when mapped
170// to a standard analog stick -> user should be able to tweak
171// response/deadzone for comfort
172// > When response is linear, 'additional' deadzone (set here)
173// may be left at zero, since this is normally handled via in-game
174// options menus
175// > When response is non-linear, deadzone should be set to match the
176// controller being used (otherwise precision may be lost)
177// > negcon_linearity:
178// - 1: Response is linear - recommended when using racing wheel
179// peripherals, not recommended for standard gamepads
180// - 2: Response is quadratic - optimal setting for gamepads
181// - 3: Response is cubic - enables precise fine control, but
182// difficult to use...
183#define NEGCON_RANGE 0x7FFF
184static int negcon_deadzone = 0;
185static int negcon_linearity = 1;
186
187static bool axis_bounds_modifier;
188
189/* PSX max resolution is 640x512, but with enhancement it's 1024x512 */
190#ifdef GPU_NEON
191#define VOUT_MAX_WIDTH 1024
192#else
193#define VOUT_MAX_WIDTH 640
194#endif
195#define VOUT_MAX_HEIGHT 512
196
197//Dummy functions
198bool retro_load_game_special(unsigned game_type, const struct retro_game_info *info, size_t num_info) { return false; }
199void retro_unload_game(void) {}
200static int vout_open(void) { return 0; }
201static void vout_close(void) {}
202static int snd_init(void) { return 0; }
203static void snd_finish(void) {}
204static int snd_busy(void) { return 0; }
205
206#define GPU_PEOPS_ODD_EVEN_BIT (1 << 0)
207#define GPU_PEOPS_EXPAND_SCREEN_WIDTH (1 << 1)
208#define GPU_PEOPS_IGNORE_BRIGHTNESS (1 << 2)
209#define GPU_PEOPS_DISABLE_COORD_CHECK (1 << 3)
210#define GPU_PEOPS_LAZY_SCREEN_UPDATE (1 << 6)
211#define GPU_PEOPS_OLD_FRAME_SKIP (1 << 7)
212#define GPU_PEOPS_REPEATED_TRIANGLES (1 << 8)
213#define GPU_PEOPS_QUADS_WITH_TRIANGLES (1 << 9)
214#define GPU_PEOPS_FAKE_BUSY_STATE (1 << 10)
215
216static void init_memcard(char *mcd_data)
217{
218 unsigned off = 0;
219 unsigned i;
220
221 memset(mcd_data, 0, MCD_SIZE);
222
223 mcd_data[off++] = 'M';
224 mcd_data[off++] = 'C';
225 off += 0x7d;
226 mcd_data[off++] = 0x0e;
227
228 for (i = 0; i < 15; i++)
229 {
230 mcd_data[off++] = 0xa0;
231 off += 0x07;
232 mcd_data[off++] = 0xff;
233 mcd_data[off++] = 0xff;
234 off += 0x75;
235 mcd_data[off++] = 0xa0;
236 }
237
238 for (i = 0; i < 20; i++)
239 {
240 mcd_data[off++] = 0xff;
241 mcd_data[off++] = 0xff;
242 mcd_data[off++] = 0xff;
243 mcd_data[off++] = 0xff;
244 off += 0x04;
245 mcd_data[off++] = 0xff;
246 mcd_data[off++] = 0xff;
247 off += 0x76;
248 }
249}
250
251static void bgr_to_fb_empty(void *dst, const void *src, int bytes)
252{
253}
254
255typedef void (bgr_to_fb_func)(void *dst, const void *src, int bytes);
256static bgr_to_fb_func *g_bgr_to_fb = bgr_to_fb_empty;
257
258static void set_bgr_to_fb_func(int bgr24)
259{
260 switch (current_fmt)
261 {
262 case RETRO_PIXEL_FORMAT_XRGB8888:
263 g_bgr_to_fb = bgr24 ? bgr888_to_xrgb8888 : bgr555_to_xrgb8888;
264 break;
265 case RETRO_PIXEL_FORMAT_RGB565:
266 g_bgr_to_fb = bgr24 ? bgr888_to_rgb565 : bgr555_to_rgb565;
267 break;
268 default:
269 LogErr("unsupported current_fmt: %d\n", current_fmt);
270 g_bgr_to_fb = bgr_to_fb_empty;
271 break;
272 }
273}
274
275static void set_vout_fb(void)
276{
277 struct retro_framebuffer fb = { 0 };
278 bool ret;
279
280 fb.width = vout_width;
281 fb.height = vout_height;
282 fb.access_flags = RETRO_MEMORY_ACCESS_WRITE;
283
284 ret = environ_cb(RETRO_ENVIRONMENT_GET_CURRENT_SOFTWARE_FRAMEBUFFER, &fb);
285 if (ret && vout_can_dupe &&
286 (fb.format == RETRO_PIXEL_FORMAT_RGB565 || fb.format == RETRO_PIXEL_FORMAT_XRGB8888))
287 {
288 int bytes_pp = (fb.format == RETRO_PIXEL_FORMAT_XRGB8888) ? 4 : 2;
289 if (current_fmt != fb.format) {
290 LogWarn("fb.format changed: %d->%d\n", current_fmt, fb.format);
291 current_fmt = fb.format;
292 }
293 vout_buf_ptr = fb.data;
294 vout_pitch_b = fb.pitch;
295 if (fb.pitch != vout_width * bytes_pp)
296 LogWarn("got unusual pitch %zd for fmt %d resolution %dx%d\n",
297 fb.pitch, fb.format, vout_width, vout_height);
298 }
299 else
300 {
301 int bytes_pp = (current_fmt == RETRO_PIXEL_FORMAT_XRGB8888) ? 4 : 2;
302 vout_buf_ptr = vout_buf;
303 vout_pitch_b = vout_width * bytes_pp;
304 }
305}
306
307static void vout_set_mode(int w, int h, int raw_w, int raw_h, int bpp)
308{
309 vout_width = w;
310 vout_height = h;
311 psx_w = raw_w;
312 psx_h = raw_h;
313
314 /* it may seem like we could do RETRO_ENVIRONMENT_SET_PIXEL_FORMAT here to
315 * switch to something that can accommodate bgr24 for FMVs, but although it
316 * succeeds it doesn't actually change the format at least on Linux, and the
317 * docs say only retro_load_game() can do it */
318
319 if (current_width != vout_width || current_height != vout_height)
320 {
321 current_width = vout_width;
322 current_height = vout_height;
323
324 struct retro_system_av_info info;
325 retro_get_system_av_info(&info);
326 environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &info.geometry);
327 }
328
329 set_vout_fb();
330 set_bgr_to_fb_func(bpp == 24);
331}
332
333// Function to add crosshairs
334static void addCrosshair(int port, int crosshair_color, unsigned short *buffer, int bufferStride, int pos_x, int pos_y, int thickness, int size_x, int size_y)
335{
336 for (port = 0; port < 2; port++) {
337 // Draw the horizontal line of the crosshair
338 int i, j;
339 for (i = pos_y - thickness / 2; i <= pos_y + thickness / 2; i++) {
340 for (j = pos_x - size_x / 2; j <= pos_x + size_x / 2; j++) {
341 if ((i + vout_height) >= 0 && (i + vout_height) < bufferStride && j >= 0 && j < bufferStride && in_enable_crosshair[port] > 0)
342 buffer[i * bufferStride + j] = crosshair_color;
343 }
344 }
345
346 // Draw the vertical line of the crosshair
347 for (i = pos_x - thickness / 2; i <= pos_x + thickness / 2; i++) {
348 for (j = pos_y - size_y / 2; j <= pos_y + size_y / 2; j++) {
349 if (i >= 0 && i < bufferStride && (j + vout_height) >= 0 && (j + vout_height) < bufferStride && in_enable_crosshair[port] > 0)
350 buffer[j * bufferStride + i] = crosshair_color;
351 }
352 }
353 }
354}
355
356struct CrosshairInfo {
357 int pos_x, pos_y, thickness, size_x, size_y;
358};
359
360// Calculate size and position of crosshairs
361static void CrosshairDimensions(int port, struct CrosshairInfo *info) {
362 int gunx = input_state_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X);
363 int guny = input_state_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y);
364 if (gunx == 32767) // Prevent crosshairs from wrapping around right side of screen to left
365 info->pos_x = (gunx + 32767.0f) * vout_width / 65534.0f - 0.5f;
366 else
367 info->pos_x = (gunx + 32767.0f) * vout_width / 65534.0f;
368 info->pos_y = (guny + 32767.0f) * vout_height / 65534.0f - vout_height;
369 info->thickness = pl_rearmed_cbs.gpu_neon.enhancement_enable ? 4 : 2;
370 info->size_x = psx_w * (pl_rearmed_cbs.gpu_neon.enhancement_enable ? 2 : 1) / 40.0f;
371 info->size_y = psx_h * (pl_rearmed_cbs.gpu_neon.enhancement_enable ? 2 : 1) * (4.0f / 3.0f) / 40.0f;
372}
373
374static void vout_flip(const void *vram_, int vram_ofs, int bgr24,
375 int x, int y, int w, int h, int dims_changed)
376{
377 int bytes_pp = (current_fmt == RETRO_PIXEL_FORMAT_XRGB8888) ? 4 : 2;
378 int bytes_pp_s = bgr24 ? 3 : 2;
379 bgr_to_fb_func *bgr_to_fb = g_bgr_to_fb;
380 unsigned char *dest = vout_buf_ptr;
381 const unsigned char *vram = vram_;
382 int dstride = vout_pitch_b, h1 = h;
383 int enhres = w > psx_w;
384 u32 vram_mask = enhres ? ~0 : 0xfffff;
385 int port = 0, hwrapped;
386
387 if (vram == NULL || dims_changed || (in_enable_crosshair[0] + in_enable_crosshair[1]) > 0)
388 {
389 unsigned char *dest2 = dest;
390 int h2 = h, ll = vout_width * bytes_pp;
391 if (dstride == ll)
392 memset(dest2, 0, dstride * vout_height);
393 else
394 for (; h2-- > 0; dest2 += dstride)
395 memset(dest2, 0, ll);
396 // blanking
397 if (vram == NULL)
398 goto out;
399 }
400
401 dest += x * bytes_pp + y * dstride;
402
403 for (; h1-- > 0; dest += dstride) {
404 bgr_to_fb(dest, vram + vram_ofs, w * bytes_pp_s);
405 vram_ofs = (vram_ofs + 2048) & vram_mask;
406 }
407
408 hwrapped = (vram_ofs & 2047) + w * bytes_pp_s - 2048;
409 if (!enhres && hwrapped > 0) {
410 // this is super-rare so just fix-up
411 vram_ofs = (vram_ofs - h * 2048) & 0xff800;
412 dest -= dstride * h;
413 dest += (w - hwrapped / bytes_pp_s) * bytes_pp;
414 for (h1 = h; h1-- > 0; dest += dstride) {
415 bgr_to_fb(dest, vram + vram_ofs, hwrapped);
416 vram_ofs = (vram_ofs + 2048) & 0xfffff;
417 }
418 }
419
420 if (current_fmt == RETRO_PIXEL_FORMAT_RGB565)
421 for (port = 0; port < 2; port++) {
422 if (in_enable_crosshair[port] > 0 && (in_type[port] == PSE_PAD_TYPE_GUNCON || in_type[port] == PSE_PAD_TYPE_GUN))
423 {
424 struct CrosshairInfo crosshairInfo;
425 CrosshairDimensions(port, &crosshairInfo);
426 addCrosshair(port, in_enable_crosshair[port], (unsigned short *)dest,
427 dstride / 2, crosshairInfo.pos_x, crosshairInfo.pos_y,
428 crosshairInfo.thickness, crosshairInfo.size_x, crosshairInfo.size_y);
429 }
430 }
431
432out:
433 vout_fb_dirty = 1;
434 pl_rearmed_cbs.flip_cnt++;
435}
436
437#ifdef _3DS
438static u32 mapped_addrs[8];
439static u32 mapped_ram, mapped_ram_src;
440static void *vram_mem;
441
442// http://3dbrew.org/wiki/Memory_layout#ARM11_User-land_memory_regions
443static void *pl_3ds_mmap(unsigned long addr, size_t size,
444 enum psxMapTag tag, int *can_retry_addr)
445{
446 void *ret = MAP_FAILED;
447 *can_retry_addr = 0;
448 (void)addr;
449
450 if (tag == MAP_TAG_VRAM && vram_mem)
451 return vram_mem;
452
453 if (__ctr_svchax) do
454 {
455 // idea from fbalpha2012_neogeo
456 s32 addr = 0x10000000 - 0x1000;
457 u32 found_addr = 0;
458 MemInfo mem_info;
459 PageInfo page_info;
460 size_t i;
461 int r;
462
463 for (i = 0; i < sizeof(mapped_addrs) / sizeof(mapped_addrs[0]); i++)
464 if (mapped_addrs[i] == 0)
465 break;
466 if (i == sizeof(mapped_addrs) / sizeof(mapped_addrs[0]))
467 break;
468
469 size = (size + 0xfff) & ~0xfff;
470
471 while (addr >= 0x08000000)
472 {
473 if ((r = svcQueryMemory(&mem_info, &page_info, addr)) < 0) {
474 LogErr("svcQueryMemory failed: %d\n", r);
475 break;
476 }
477
478 if (mem_info.state == MEMSTATE_FREE && mem_info.size >= size) {
479 found_addr = mem_info.base_addr + mem_info.size - size;
480 break;
481 }
482
483 addr = mem_info.base_addr - 0x1000;
484 }
485 if (found_addr == 0) {
486 LogErr("no addr space for %u bytes\n", size);
487 break;
488 }
489
490 // https://libctru.devkitpro.org/svc_8h.html#a8046e9b23b1b209a4e278cb1c19c7a5a
491 if ((r = svcControlMemory(&mapped_addrs[i], found_addr, 0, size, MEMOP_ALLOC, MEMPERM_READWRITE)) < 0) {
492 LogErr("svcControlMemory failed for %08x %u: %d\n", found_addr, size, r);
493 break;
494 }
495 if (mapped_addrs[i] == 0) // needed?
496 mapped_addrs[i] = found_addr;
497 ret = (void *)mapped_addrs[i];
498
499 // "round" address helps the dynarec slightly, map ram at 0x13000000
500 if (tag == MAP_TAG_RAM && !mapped_ram) {
501 u32 target = 0x13000000;
502 if ((r = svcControlMemory(&mapped_ram, target, mapped_addrs[i], size, MEMOP_MAP, MEMPERM_READWRITE)) < 0)
503 LogErr("could not map ram %08x -> %08x: %d\n", mapped_addrs[i], target, r);
504 else {
505 mapped_ram_src = mapped_addrs[i];
506 mapped_ram = target;
507 ret = (void *)mapped_ram;
508 }
509 }
510 memset(ret, 0, size);
511 return ret;
512 }
513 while (0);
514
515 ret = calloc(size, 1);
516 return ret ? ret : MAP_FAILED;
517}
518
519static void pl_3ds_munmap(void *ptr, size_t size, enum psxMapTag tag)
520{
521 (void)tag;
522
523 if (ptr && ptr == vram_mem)
524 return;
525
526 if (ptr && __ctr_svchax)
527 {
528 size_t i;
529 u32 tmp;
530
531 size = (size + 0xfff) & ~0xfff;
532
533 if (ptr == (void *)mapped_ram) {
534 svcControlMemory(&tmp, mapped_ram, mapped_ram_src, size, MEMOP_UNMAP, 0);
535 ptr = (void *)mapped_ram_src;
536 mapped_ram = mapped_ram_src = 0;
537 }
538 for (i = 0; i < sizeof(mapped_addrs) / sizeof(mapped_addrs[0]); i++) {
539 if (ptr == (void *)mapped_addrs[i]) {
540 svcControlMemory(&tmp, mapped_addrs[i], 0, size, MEMOP_FREE, 0);
541 mapped_addrs[i] = 0;
542 return;
543 }
544 }
545 }
546
547 free(ptr);
548}
549
550// debug
551static int ctr_get_tlbe_k(u32 ptr)
552{
553 u32 tlb_base = -1, tlb_ctl = -1, *l1;
554 s32 tlb_mask = 0xffffc000;
555
556 asm volatile("mrc p15, 0, %0, c2, c0, 0" : "=r"(tlb_base));
557 asm volatile("mrc p15, 0, %0, c2, c0, 2" : "=r"(tlb_ctl));
558 tlb_mask >>= tlb_ctl & 7;
559 l1 = (u32 *)((tlb_base & tlb_mask) | 0xe0000000);
560 return l1[ptr >> 20];
561}
562
563static int ctr_get_tlbe(void *ptr)
564{
565 if (svcConvertVAToPA((void *)0xe0000000, 0) != 0x20000000)
566 return -1;
567 return svcCustomBackdoor(ctr_get_tlbe_k, ptr, NULL, NULL);
568}
569#endif
570
571#ifdef HAVE_LIBNX
572static void *pl_switch_mmap(unsigned long addr, size_t size,
573 enum psxMapTag tag, int *can_retry_addr)
574{
575 void *ret = MAP_FAILED;
576 *can_retry_addr = 0;
577 (void)addr;
578
579 // there's svcMapPhysicalMemory() but user logs show it doesn't hand out
580 // any desired addresses, so don't even bother
581 ret = aligned_alloc(0x1000, size);
582 if (!ret)
583 return MAP_FAILED;
584 memset(ret, 0, size);
585 return ret;
586}
587
588static void pl_switch_munmap(void *ptr, size_t size, enum psxMapTag tag)
589{
590 (void)size;
591 (void)tag;
592 free(ptr);
593}
594#endif
595
596#ifdef VITA
597typedef struct
598{
599 void *buffer;
600 size_t size;
601 enum psxMapTag tag;
602 int used;
603} psx_map_t;
604
605static void *addr = NULL;
606
607static psx_map_t custom_psx_maps[] = {
608 { NULL, 0x800000, MAP_TAG_LUTS },
609 { NULL, 0x080000, MAP_TAG_OTHER },
610 { NULL, 0x010000, MAP_TAG_OTHER },
611 { NULL, 0x201000, MAP_TAG_VRAM },
612 { NULL, 0x802000, MAP_TAG_VRAM }, // enhanced renderer
613 { NULL, 0x210000, MAP_TAG_RAM },
614};
615
616static int init_vita_mmap()
617{
618 void *tmpaddr;
619 addr = malloc(64 * 1024 * 1024);
620 if (addr == NULL)
621 return -1;
622 tmpaddr = (void *)(((size_t)addr + 0xFFFFFF) & ~0xFFFFFF);
623 custom_psx_maps[0].buffer = tmpaddr + 0x0000000;
624 custom_psx_maps[1].buffer = tmpaddr + 0x0800000;
625 custom_psx_maps[2].buffer = tmpaddr + 0x0880000;
626 custom_psx_maps[3].buffer = tmpaddr + 0x0900000;
627 custom_psx_maps[4].buffer = tmpaddr + 0x1000000;
628 custom_psx_maps[5].buffer = tmpaddr + 0x2000000;
629 memset(tmpaddr, 0, 0x2210000);
630#if 0
631 int n;
632 for(n = 0; n < 5; n++){
633 sceClibPrintf("addr reserved %x\n",custom_psx_maps[n].buffer);
634 }
635#endif
636 return 0;
637}
638
639static void deinit_vita_mmap()
640{
641 size_t i;
642 for (i = 0; i < sizeof(custom_psx_maps) / sizeof(custom_psx_maps[0]); i++) {
643 custom_psx_maps[i].buffer = NULL;
644 custom_psx_maps[i].used = 0;
645 }
646 free(addr);
647}
648
649static void *pl_vita_mmap(unsigned long addr, size_t size,
650 enum psxMapTag tag, int *can_retry_addr)
651{
652 void *ret;
653 (void)addr;
654 *can_retry_addr = 0;
655
656 psx_map_t *custom_map = custom_psx_maps;
657
658 for (; custom_map->size; custom_map++)
659 {
660 if (custom_map->size == size && custom_map->tag == tag && !custom_map->used)
661 {
662 custom_map->used = 1;
663 return custom_map->buffer;
664 }
665 }
666
667 ret = calloc(size, 1);
668 return ret ? ret : MAP_FAILED;
669}
670
671static void pl_vita_munmap(void *ptr, size_t size, enum psxMapTag tag)
672{
673 (void)tag;
674
675 psx_map_t *custom_map = custom_psx_maps;
676
677 for (; custom_map->size; custom_map++)
678 {
679 if ((custom_map->buffer == ptr))
680 {
681 custom_map->used = 0;
682 return;
683 }
684 }
685
686 free(ptr);
687}
688#endif
689
690static void log_mem_usage(void)
691{
692#ifdef _3DS
693 extern u32 __heap_size, __linear_heap_size, __stacksize__;
694 extern char __end__; // 3dsx.ld
695 u32 app_memory = *((volatile u32 *)0x1FF80040);
696 s64 mem_used = 0;
697 if (__ctr_svchax)
698 svcGetSystemInfo(&mem_used, 0, 1);
699
700 SysPrintf("mem: %d/%d heap: %d linear: %d/%d stack: %d exe: %d\n",
701 (int)mem_used, app_memory, __heap_size, __linear_heap_size - linearSpaceFree(),
702 __linear_heap_size, __stacksize__, (int)&__end__ - 0x100000);
703#endif
704}
705
706static void *pl_mmap(unsigned int size)
707{
708 return psxMap(0, size, 0, MAP_TAG_VRAM);
709}
710
711static void pl_munmap(void *ptr, unsigned int size)
712{
713 psxUnmap(ptr, size, MAP_TAG_VRAM);
714}
715
716struct rearmed_cbs pl_rearmed_cbs = {
717 .pl_vout_open = vout_open,
718 .pl_vout_set_mode = vout_set_mode,
719 .pl_vout_flip = vout_flip,
720 .pl_vout_close = vout_close,
721 .mmap = pl_mmap,
722 .munmap = pl_munmap,
723 .gpu_state_change = gpu_state_change,
724 /* from psxcounters */
725 .gpu_hcnt = &hSyncCount,
726 .gpu_frame_count = &frame_counter,
727};
728
729void pl_frame_limit(void)
730{
731 /* called once per frame, make psxCpu->Execute() above return */
732 psxRegs.stop++;
733}
734
735void pl_timing_prepare(int is_pal)
736{
737}
738
739void plat_trigger_vibrate(int pad, int low, int high)
740{
741 if (!rumble_cb)
742 return;
743
744 if (in_enable_vibration)
745 {
746 rumble_cb(pad, RETRO_RUMBLE_STRONG, high << 8);
747 rumble_cb(pad, RETRO_RUMBLE_WEAK, low ? 0xffff : 0x0);
748 }
749}
750
751//Percentage distance of screen to adjust for Konami Gun
752static float KonamiGunAdjustX = 0;
753static float KonamiGunAdjustY = 0;
754
755void pl_gun_byte2(int port, unsigned char byte)
756{
757 int irq_count = 4;
758 float justifier_multiplier = 0;
759 int justifier_width = psx_w;
760 int justifier_height = psx_h;
761 int justifier_offscreen = input_state_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN);
762 int justifier_reload = input_state_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_RELOAD);
763
764 if (justifier_width == 256)
765 justifier_multiplier = is_pal_mode ? .157086f : .158532f;
766 else if (justifier_width == 320)
767 justifier_multiplier = is_pal_mode ? .196358f : .198166f;
768 else if (justifier_width == 384)
769 justifier_multiplier = is_pal_mode ? .224409f : .226475f;
770 else if (justifier_width == 512)
771 justifier_multiplier = is_pal_mode ? .314173f : .317065f;
772 else // (justifier_width == 640)
773 justifier_multiplier = is_pal_mode ? .392717f : .396332f;
774
775 int gunx = input_state_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X);
776 int guny = input_state_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y);
777
778 //Default offset of +105 for X and -12 for Y is chosen to obtain alignment in Die Hard Trilogy, which has no calibration feature
779 int gunx_scaled = ((gunx + 32767.0f) / 65534.0f + KonamiGunAdjustX) * justifier_width / justifier_multiplier + 105.0f;
780 int guny_scaled = ((guny + 32767.0f) / 65534.0f + KonamiGunAdjustY) * justifier_height - 12.0f;
781
782 if ((byte & 0x10) && !justifier_offscreen && !justifier_reload)
783 {
784 psxScheduleIrq10(irq_count, gunx_scaled, guny_scaled);
785 }
786}
787
788/* sound calls */
789static void snd_feed(void *buf, int bytes)
790{
791 if (audio_batch_cb != NULL)
792 audio_batch_cb(buf, bytes / 4);
793}
794
795void out_register_libretro(struct out_driver *drv)
796{
797 drv->name = "libretro";
798 drv->init = snd_init;
799 drv->finish = snd_finish;
800 drv->busy = snd_busy;
801 drv->feed = snd_feed;
802}
803
804#define RETRO_DEVICE_PSE_STANDARD RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 0)
805#define RETRO_DEVICE_PSE_ANALOG RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_ANALOG, 0)
806#define RETRO_DEVICE_PSE_DUALSHOCK RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_ANALOG, 1)
807#define RETRO_DEVICE_PSE_NEGCON RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_ANALOG, 2)
808#define RETRO_DEVICE_PSE_GUNCON RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_LIGHTGUN, 0)
809#define RETRO_DEVICE_PSE_JUSTIFIER RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_LIGHTGUN, 1)
810#define RETRO_DEVICE_PSE_MOUSE RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_MOUSE, 0)
811
812static char *get_pse_pad_label[] = {
813 "none", "mouse", "negcon", "konami gun", "standard", "analog", "guncon", "dualshock"
814};
815
816static const struct retro_controller_description pads[8] =
817{
818 { "standard", RETRO_DEVICE_JOYPAD },
819 { "analog", RETRO_DEVICE_PSE_ANALOG },
820 { "dualshock", RETRO_DEVICE_PSE_DUALSHOCK },
821 { "negcon", RETRO_DEVICE_PSE_NEGCON },
822 { "guncon", RETRO_DEVICE_PSE_GUNCON },
823 { "konami gun", RETRO_DEVICE_PSE_JUSTIFIER },
824 { "mouse", RETRO_DEVICE_PSE_MOUSE },
825 { NULL, 0 },
826};
827
828static const struct retro_controller_info ports[9] =
829{
830 { pads, 7 },
831 { pads, 7 },
832 { pads, 7 },
833 { pads, 7 },
834 { pads, 7 },
835 { pads, 7 },
836 { pads, 7 },
837 { pads, 7 },
838 { NULL, 0 },
839};
840
841/* libretro */
842
843static bool update_option_visibility(void)
844{
845 struct retro_variable var = {0};
846 struct retro_core_option_display option_display = {0};
847 bool updated = false;
848 unsigned i;
849
850 /* If frontend supports core option categories
851 * then show/hide core option entries are ignored
852 * and no options should be hidden */
853 if (libretro_supports_option_categories)
854 return false;
855
856 var.key = "pcsx_rearmed_show_input_settings";
857 var.value = NULL;
858
859 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
860 {
861 bool show_input_settings_prev =
862 show_input_settings;
863
864 show_input_settings = true;
865 if (strcmp(var.value, "disabled") == 0)
866 show_input_settings = false;
867
868 if (show_input_settings !=
869 show_input_settings_prev)
870 {
871 char input_option[][50] = {
872 "pcsx_rearmed_analog_axis_modifier",
873 "pcsx_rearmed_vibration",
874 "pcsx_rearmed_multitap",
875 "pcsx_rearmed_negcon_deadzone",
876 "pcsx_rearmed_negcon_response",
877 "pcsx_rearmed_input_sensitivity",
878 "pcsx_rearmed_crosshair1",
879 "pcsx_rearmed_crosshair2",
880 "pcsx_rearmed_konamigunadjustx",
881 "pcsx_rearmed_konamigunadjusty",
882 "pcsx_rearmed_gunconadjustx",
883 "pcsx_rearmed_gunconadjusty",
884 "pcsx_rearmed_gunconadjustratiox",
885 "pcsx_rearmed_gunconadjustratioy"
886 };
887
888 option_display.visible = show_input_settings;
889
890 for (i = 0;
891 i < (sizeof(input_option) /
892 sizeof(input_option[0]));
893 i++)
894 {
895 option_display.key = input_option[i];
896 environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY,
897 &option_display);
898 }
899
900 updated = true;
901 }
902 }
903#ifdef GPU_PEOPS
904 var.key = "pcsx_rearmed_show_gpu_peops_settings";
905 var.value = NULL;
906
907 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
908 {
909 bool show_advanced_gpu_peops_settings_prev =
910 show_advanced_gpu_peops_settings;
911
912 show_advanced_gpu_peops_settings = true;
913 if (strcmp(var.value, "disabled") == 0)
914 show_advanced_gpu_peops_settings = false;
915
916 if (show_advanced_gpu_peops_settings !=
917 show_advanced_gpu_peops_settings_prev)
918 {
919 unsigned i;
920 struct retro_core_option_display option_display;
921 char gpu_peops_option[][45] = {
922 "pcsx_rearmed_gpu_peops_odd_even_bit",
923 "pcsx_rearmed_gpu_peops_expand_screen_width",
924 "pcsx_rearmed_gpu_peops_ignore_brightness",
925 "pcsx_rearmed_gpu_peops_disable_coord_check",
926 "pcsx_rearmed_gpu_peops_lazy_screen_update",
927 "pcsx_rearmed_gpu_peops_repeated_triangles",
928 "pcsx_rearmed_gpu_peops_quads_with_triangles",
929 "pcsx_rearmed_gpu_peops_fake_busy_state"
930 };
931
932 option_display.visible = show_advanced_gpu_peops_settings;
933
934 for (i = 0;
935 i < (sizeof(gpu_peops_option) /
936 sizeof(gpu_peops_option[0]));
937 i++)
938 {
939 option_display.key = gpu_peops_option[i];
940 environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY,
941 &option_display);
942 }
943
944 updated = true;
945 }
946 }
947#endif
948#ifdef GPU_UNAI
949 var.key = "pcsx_rearmed_show_gpu_unai_settings";
950 var.value = NULL;
951
952 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
953 {
954 bool show_advanced_gpu_unai_settings_prev =
955 show_advanced_gpu_unai_settings;
956
957 show_advanced_gpu_unai_settings = true;
958 if (strcmp(var.value, "disabled") == 0)
959 show_advanced_gpu_unai_settings = false;
960
961 if (show_advanced_gpu_unai_settings !=
962 show_advanced_gpu_unai_settings_prev)
963 {
964 unsigned i;
965 struct retro_core_option_display option_display;
966 char gpu_unai_option[][40] = {
967 "pcsx_rearmed_gpu_unai_blending",
968 "pcsx_rearmed_gpu_unai_skipline",
969 "pcsx_rearmed_gpu_unai_lighting",
970 "pcsx_rearmed_gpu_unai_fast_lighting",
971 "pcsx_rearmed_gpu_unai_scale_hires",
972 };
973
974 option_display.visible = show_advanced_gpu_unai_settings;
975
976 for (i = 0;
977 i < (sizeof(gpu_unai_option) /
978 sizeof(gpu_unai_option[0]));
979 i++)
980 {
981 option_display.key = gpu_unai_option[i];
982 environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY,
983 &option_display);
984 }
985
986 updated = true;
987 }
988 }
989#endif
990 return updated;
991}
992
993void retro_set_environment(retro_environment_t cb)
994{
995 bool option_categories = false;
996#ifdef USE_LIBRETRO_VFS
997 struct retro_vfs_interface_info vfs_iface_info;
998#endif
999
1000 environ_cb = cb;
1001
1002 if (cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &logging))
1003 log_cb = logging.log;
1004
1005 environ_cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports);
1006
1007 /* Set core options
1008 * An annoyance: retro_set_environment() can be called
1009 * multiple times, and depending upon the current frontend
1010 * state various environment callbacks may be disabled.
1011 * This means the reported 'categories_supported' status
1012 * may change on subsequent iterations. We therefore have
1013 * to record whether 'categories_supported' is true on any
1014 * iteration, and latch the result */
1015 libretro_set_core_options(environ_cb, &option_categories);
1016 libretro_supports_option_categories |= option_categories;
1017
1018 /* If frontend supports core option categories,
1019 * any show/hide core option entries are unused
1020 * and should be hidden */
1021 if (libretro_supports_option_categories)
1022 {
1023 struct retro_core_option_display option_display;
1024 option_display.visible = false;
1025
1026 option_display.key = "pcsx_rearmed_show_input_settings";
1027 environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY,
1028 &option_display);
1029
1030#ifdef GPU_PEOPS
1031 option_display.key = "pcsx_rearmed_show_gpu_peops_settings";
1032 environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY,
1033 &option_display);
1034#endif
1035#ifdef GPU_UNAI
1036 option_display.key = "pcsx_rearmed_show_gpu_unai_settings";
1037 environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY,
1038 &option_display);
1039#endif
1040 }
1041 /* If frontend does not support core option
1042 * categories, core options may be shown/hidden
1043 * at runtime. In this case, register 'update
1044 * display' callback, so frontend can update
1045 * core options menu without calling retro_run() */
1046 else
1047 {
1048 struct retro_core_options_update_display_callback update_display_cb;
1049 update_display_cb.callback = update_option_visibility;
1050
1051 environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_UPDATE_DISPLAY_CALLBACK,
1052 &update_display_cb);
1053 }
1054
1055#ifdef USE_LIBRETRO_VFS
1056 vfs_iface_info.required_interface_version = 1;
1057 vfs_iface_info.iface = NULL;
1058 if (environ_cb(RETRO_ENVIRONMENT_GET_VFS_INTERFACE, &vfs_iface_info))
1059 filestream_vfs_init(&vfs_iface_info);
1060#endif
1061}
1062
1063void retro_set_video_refresh(retro_video_refresh_t cb) { video_cb = cb; }
1064void retro_set_audio_sample(retro_audio_sample_t cb) { (void)cb; }
1065void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) { audio_batch_cb = cb; }
1066void retro_set_input_poll(retro_input_poll_t cb) { input_poll_cb = cb; }
1067void retro_set_input_state(retro_input_state_t cb) { input_state_cb = cb; }
1068
1069unsigned retro_api_version(void)
1070{
1071 return RETRO_API_VERSION;
1072}
1073
1074static void update_multitap(void)
1075{
1076 struct retro_variable var = { 0 };
1077
1078 multitap1 = 0;
1079 multitap2 = 0;
1080
1081 var.value = NULL;
1082 var.key = "pcsx_rearmed_multitap";
1083 if (environ_cb && (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value))
1084 {
1085 if (strcmp(var.value, "port 1") == 0)
1086 multitap1 = 1;
1087 else if (strcmp(var.value, "port 2") == 0)
1088 multitap2 = 1;
1089 else if (strcmp(var.value, "ports 1 and 2") == 0)
1090 {
1091 multitap1 = 1;
1092 multitap2 = 1;
1093 }
1094 }
1095}
1096
1097void retro_set_controller_port_device(unsigned port, unsigned device)
1098{
1099 if (port >= PORTS_NUMBER)
1100 return;
1101
1102 switch (device)
1103 {
1104 case RETRO_DEVICE_JOYPAD:
1105 case RETRO_DEVICE_PSE_STANDARD:
1106 in_type[port] = PSE_PAD_TYPE_STANDARD;
1107 break;
1108 case RETRO_DEVICE_PSE_ANALOG:
1109 in_type[port] = PSE_PAD_TYPE_ANALOGJOY;
1110 break;
1111 case RETRO_DEVICE_PSE_DUALSHOCK:
1112 in_type[port] = PSE_PAD_TYPE_ANALOGPAD;
1113 break;
1114 case RETRO_DEVICE_PSE_MOUSE:
1115 in_type[port] = PSE_PAD_TYPE_MOUSE;
1116 break;
1117 case RETRO_DEVICE_PSE_NEGCON:
1118 in_type[port] = PSE_PAD_TYPE_NEGCON;
1119 break;
1120 case RETRO_DEVICE_PSE_GUNCON:
1121 in_type[port] = PSE_PAD_TYPE_GUNCON;
1122 break;
1123 case RETRO_DEVICE_PSE_JUSTIFIER:
1124 in_type[port] = PSE_PAD_TYPE_GUN;
1125 break;
1126 case RETRO_DEVICE_NONE:
1127 default:
1128 in_type[port] = PSE_PAD_TYPE_NONE;
1129 break;
1130 }
1131
1132 SysPrintf("port: %u device: %s\n", port + 1, get_pse_pad_label[in_type[port]]);
1133}
1134
1135void retro_get_system_info(struct retro_system_info *info)
1136{
1137#ifndef GIT_VERSION
1138#define GIT_VERSION ""
1139#endif
1140 memset(info, 0, sizeof(*info));
1141 info->library_name = "PCSX-ReARMed";
1142 info->library_version = "r25" GIT_VERSION;
1143 info->valid_extensions = "bin|cue|img|mdf|pbp|toc|cbn|m3u|chd|iso|exe";
1144 info->need_fullpath = true;
1145}
1146
1147void retro_get_system_av_info(struct retro_system_av_info *info)
1148{
1149 unsigned geom_height = vout_height;
1150 unsigned geom_width = vout_width;
1151
1152 memset(info, 0, sizeof(*info));
1153 info->timing.fps = psxGetFps();
1154 info->timing.sample_rate = 44100.0;
1155 info->geometry.base_width = geom_width;
1156 info->geometry.base_height = geom_height;
1157 info->geometry.max_width = VOUT_MAX_WIDTH;
1158 info->geometry.max_height = VOUT_MAX_HEIGHT;
1159 info->geometry.aspect_ratio = 4.0 / 3.0;
1160}
1161
1162/* savestates */
1163size_t retro_serialize_size(void)
1164{
1165 // it's currently 4380651-4397047 bytes,
1166 // but have some reserved for future
1167 return 0x440000;
1168}
1169
1170struct save_fp
1171{
1172 char *buf;
1173 size_t pos;
1174 int is_write;
1175};
1176
1177static void *save_open(const char *name, const char *mode)
1178{
1179 struct save_fp *fp;
1180
1181 if (name == NULL || mode == NULL)
1182 return NULL;
1183
1184 fp = malloc(sizeof(*fp));
1185 if (fp == NULL)
1186 return NULL;
1187
1188 fp->buf = (char *)name;
1189 fp->pos = 0;
1190 fp->is_write = (mode[0] == 'w' || mode[1] == 'w');
1191
1192 return fp;
1193}
1194
1195static int save_read(void *file, void *buf, u32 len)
1196{
1197 struct save_fp *fp = file;
1198 if (fp == NULL || buf == NULL)
1199 return -1;
1200
1201 memcpy(buf, fp->buf + fp->pos, len);
1202 fp->pos += len;
1203 return len;
1204}
1205
1206static int save_write(void *file, const void *buf, u32 len)
1207{
1208 struct save_fp *fp = file;
1209 if (fp == NULL || buf == NULL)
1210 return -1;
1211
1212 memcpy(fp->buf + fp->pos, buf, len);
1213 fp->pos += len;
1214 return len;
1215}
1216
1217static long save_seek(void *file, long offs, int whence)
1218{
1219 struct save_fp *fp = file;
1220 if (fp == NULL)
1221 return -1;
1222
1223 switch (whence)
1224 {
1225 case SEEK_CUR:
1226 fp->pos += offs;
1227 return fp->pos;
1228 case SEEK_SET:
1229 fp->pos = offs;
1230 return fp->pos;
1231 default:
1232 return -1;
1233 }
1234}
1235
1236static void save_close(void *file)
1237{
1238 struct save_fp *fp = file;
1239 size_t r_size = retro_serialize_size();
1240 if (fp == NULL)
1241 return;
1242
1243 if (fp->pos > r_size)
1244 LogErr("ERROR: save buffer overflow detected\n");
1245 else if (fp->is_write && fp->pos < r_size)
1246 // make sure we don't save trash in leftover space
1247 memset(fp->buf + fp->pos, 0, r_size - fp->pos);
1248 free(fp);
1249}
1250
1251bool retro_serialize(void *data, size_t size)
1252{
1253 int ret;
1254 CdromFrontendId = disk_current_index;
1255 ret = SaveState(data);
1256 return ret == 0 ? true : false;
1257}
1258
1259static bool disk_set_image_index(unsigned int index);
1260
1261bool retro_unserialize(const void *data, size_t size)
1262{
1263 int ret;
1264 CdromFrontendId = -1;
1265 ret = LoadState(data);
1266 if (ret)
1267 return false;
1268 if (CdromFrontendId != -1 && CdromFrontendId != disk_current_index)
1269 disk_set_image_index(CdromFrontendId);
1270 return true;
1271}
1272
1273/* cheats */
1274void retro_cheat_reset(void)
1275{
1276 ClearAllCheats();
1277}
1278
1279void retro_cheat_set(unsigned index, bool enabled, const char *code)
1280{
1281 int ret = -1;
1282 char *buf;
1283
1284 // cheat funcs are destructive, need a copy...
1285 buf = strdup(code);
1286 if (buf == NULL)
1287 goto finish;
1288
1289 //Prepare buffered cheat for PCSX's AddCheat fucntion.
1290 int cursor = 0;
1291 int nonhexdec = 0;
1292 while (buf[cursor])
1293 {
1294 if (!(ISHEXDEC))
1295 {
1296 if (++nonhexdec % 2)
1297 {
1298 buf[cursor] = ' ';
1299 }
1300 else
1301 {
1302 buf[cursor] = '\n';
1303 }
1304 }
1305 cursor++;
1306 }
1307
1308 if (index < NumCheats)
1309 ret = EditCheat(index, "", buf);
1310 else
1311 ret = AddCheat("", buf);
1312
1313finish:
1314 if (ret != 0)
1315 LogErr("Failed to set cheat %#u\n", index);
1316 else if (index < NumCheats)
1317 Cheats[index].Enabled = enabled;
1318 free(buf);
1319}
1320
1321// just in case, maybe a win-rt port in the future?
1322#ifdef _WIN32
1323#define SLASH '\\'
1324#else
1325#define SLASH '/'
1326#endif
1327
1328#ifndef PATH_MAX
1329#define PATH_MAX 4096
1330#endif
1331
1332/* multidisk support */
1333static unsigned int disk_initial_index;
1334static char disk_initial_path[PATH_MAX];
1335static bool disk_ejected;
1336static unsigned int disk_count;
1337static struct disks_state
1338{
1339 char *fname;
1340 char *flabel;
1341 int internal_index; // for multidisk eboots
1342} disks[8];
1343
1344static void get_disk_label(char *disk_label, const char *disk_path, size_t len)
1345{
1346 const char *base = NULL;
1347
1348 if (!disk_path || (*disk_path == '\0'))
1349 return;
1350
1351 base = strrchr(disk_path, SLASH);
1352 if (!base)
1353 base = disk_path;
1354
1355 if (*base == SLASH)
1356 base++;
1357
1358 strncpy(disk_label, base, len - 1);
1359 disk_label[len - 1] = '\0';
1360
1361 char *ext = strrchr(disk_label, '.');
1362 if (ext)
1363 *ext = '\0';
1364}
1365
1366static void disk_init(void)
1367{
1368 size_t i;
1369
1370 disk_ejected = false;
1371 disk_current_index = 0;
1372 disk_count = 0;
1373
1374 for (i = 0; i < sizeof(disks) / sizeof(disks[0]); i++)
1375 {
1376 if (disks[i].fname != NULL)
1377 {
1378 free(disks[i].fname);
1379 disks[i].fname = NULL;
1380 }
1381 if (disks[i].flabel != NULL)
1382 {
1383 free(disks[i].flabel);
1384 disks[i].flabel = NULL;
1385 }
1386 disks[i].internal_index = 0;
1387 }
1388}
1389
1390static bool disk_set_eject_state(bool ejected)
1391{
1392 if (ejected != disk_ejected)
1393 SysPrintf("new eject_state: %d\n", ejected);
1394
1395 // weird PCSX API...
1396 SetCdOpenCaseTime(ejected ? -1 : (time(NULL) + 2));
1397 LidInterrupt();
1398
1399#ifdef HAVE_CDROM
1400 if (cdra_is_physical() && ejected != disk_ejected) {
1401 cdra_stop_thread();
1402 if (!ejected) {
1403 // likely the real cd was also changed - rescan
1404 cdra_close();
1405 cdra_open();
1406 }
1407 }
1408#endif
1409 disk_ejected = ejected;
1410 return true;
1411}
1412
1413static bool disk_get_eject_state(void)
1414{
1415 /* can't be controlled by emulated software */
1416 return disk_ejected;
1417}
1418
1419static unsigned int disk_get_image_index(void)
1420{
1421 return disk_current_index;
1422}
1423
1424static bool disk_set_image_index(unsigned int index)
1425{
1426 if (index >= sizeof(disks) / sizeof(disks[0]))
1427 return false;
1428
1429 CdromId[0] = '\0';
1430 CdromLabel[0] = '\0';
1431
1432 if (disks[index].fname == NULL)
1433 {
1434 LogErr("missing disk #%u\n", index);
1435 cdra_shutdown();
1436
1437 // RetroArch specifies "no disk" with index == count,
1438 // so don't fail here..
1439 disk_current_index = index;
1440 return true;
1441 }
1442
1443 LogErr("switching to disk %u: \"%s\" #%d\n", index,
1444 disks[index].fname, disks[index].internal_index);
1445
1446 cdrIsoMultidiskSelect = disks[index].internal_index;
1447 set_cd_image(disks[index].fname);
1448 if (ReloadCdromPlugin() < 0)
1449 {
1450 LogErr("failed to load cdr plugin\n");
1451 return false;
1452 }
1453 if (cdra_open() < 0)
1454 {
1455 LogErr("failed to open cdr plugin\n");
1456 return false;
1457 }
1458
1459 if (!disk_ejected)
1460 {
1461 disk_set_eject_state(disk_ejected);
1462 }
1463
1464 disk_current_index = index;
1465 return true;
1466}
1467
1468static unsigned int disk_get_num_images(void)
1469{
1470 return disk_count;
1471}
1472
1473static bool disk_replace_image_index(unsigned index,
1474 const struct retro_game_info *info)
1475{
1476 char *old_fname = NULL;
1477 char *old_flabel = NULL;
1478 bool ret = true;
1479
1480 if (index >= sizeof(disks) / sizeof(disks[0]))
1481 return false;
1482
1483 old_fname = disks[index].fname;
1484 old_flabel = disks[index].flabel;
1485
1486 disks[index].fname = NULL;
1487 disks[index].flabel = NULL;
1488 disks[index].internal_index = 0;
1489
1490 if (info != NULL)
1491 {
1492 char disk_label[PATH_MAX];
1493 disk_label[0] = '\0';
1494
1495 disks[index].fname = strdup(info->path);
1496
1497 get_disk_label(disk_label, info->path, PATH_MAX);
1498 disks[index].flabel = strdup(disk_label);
1499
1500 if (index == disk_current_index)
1501 ret = disk_set_image_index(index);
1502 }
1503
1504 if (old_fname != NULL)
1505 free(old_fname);
1506
1507 if (old_flabel != NULL)
1508 free(old_flabel);
1509
1510 return ret;
1511}
1512
1513static bool disk_add_image_index(void)
1514{
1515 if (disk_count >= 8)
1516 return false;
1517
1518 disk_count++;
1519 return true;
1520}
1521
1522static bool disk_set_initial_image(unsigned index, const char *path)
1523{
1524 if (index >= sizeof(disks) / sizeof(disks[0]))
1525 return false;
1526
1527 if (!path || (*path == '\0'))
1528 return false;
1529
1530 disk_initial_index = index;
1531
1532 strncpy(disk_initial_path, path, sizeof(disk_initial_path) - 1);
1533 disk_initial_path[sizeof(disk_initial_path) - 1] = '\0';
1534
1535 return true;
1536}
1537
1538static bool disk_get_image_path(unsigned index, char *path, size_t len)
1539{
1540 const char *fname = NULL;
1541
1542 if (len < 1)
1543 return false;
1544
1545 if (index >= sizeof(disks) / sizeof(disks[0]))
1546 return false;
1547
1548 fname = disks[index].fname;
1549
1550 if (!fname || (*fname == '\0'))
1551 return false;
1552
1553 strncpy(path, fname, len - 1);
1554 path[len - 1] = '\0';
1555
1556 return true;
1557}
1558
1559static bool disk_get_image_label(unsigned index, char *label, size_t len)
1560{
1561 const char *flabel = NULL;
1562
1563 if (len < 1)
1564 return false;
1565
1566 if (index >= sizeof(disks) / sizeof(disks[0]))
1567 return false;
1568
1569 flabel = disks[index].flabel;
1570
1571 if (!flabel || (*flabel == '\0'))
1572 return false;
1573
1574 strncpy(label, flabel, len - 1);
1575 label[len - 1] = '\0';
1576
1577 return true;
1578}
1579
1580static struct retro_disk_control_callback disk_control = {
1581 .set_eject_state = disk_set_eject_state,
1582 .get_eject_state = disk_get_eject_state,
1583 .get_image_index = disk_get_image_index,
1584 .set_image_index = disk_set_image_index,
1585 .get_num_images = disk_get_num_images,
1586 .replace_image_index = disk_replace_image_index,
1587 .add_image_index = disk_add_image_index,
1588};
1589
1590static struct retro_disk_control_ext_callback disk_control_ext = {
1591 .set_eject_state = disk_set_eject_state,
1592 .get_eject_state = disk_get_eject_state,
1593 .get_image_index = disk_get_image_index,
1594 .set_image_index = disk_set_image_index,
1595 .get_num_images = disk_get_num_images,
1596 .replace_image_index = disk_replace_image_index,
1597 .add_image_index = disk_add_image_index,
1598 .set_initial_image = disk_set_initial_image,
1599 .get_image_path = disk_get_image_path,
1600 .get_image_label = disk_get_image_label,
1601};
1602
1603static char base_dir[1024];
1604
1605static bool read_m3u(const char *file)
1606{
1607 char line[1024];
1608 char name[PATH_MAX];
1609 FILE *fp = fopen(file, "r");
1610 if (!fp)
1611 return false;
1612
1613 while (fgets(line, sizeof(line), fp) && disk_count < sizeof(disks) / sizeof(disks[0]))
1614 {
1615 if (line[0] == '#')
1616 continue;
1617 char *carrige_return = strchr(line, '\r');
1618 if (carrige_return)
1619 *carrige_return = '\0';
1620 char *newline = strchr(line, '\n');
1621 if (newline)
1622 *newline = '\0';
1623
1624 if (line[0] != '\0')
1625 {
1626 char disk_label[PATH_MAX];
1627 disk_label[0] = '\0';
1628
1629 snprintf(name, sizeof(name), "%s%c%s", base_dir, SLASH, line);
1630 disks[disk_count].fname = strdup(name);
1631
1632 get_disk_label(disk_label, name, PATH_MAX);
1633 disks[disk_count].flabel = strdup(disk_label);
1634
1635 disk_count++;
1636 }
1637 }
1638
1639 fclose(fp);
1640 return (disk_count != 0);
1641}
1642
1643static void extract_directory(char *buf, const char *path, size_t size)
1644{
1645 char *base;
1646 strncpy(buf, path, size - 1);
1647 buf[size - 1] = '\0';
1648
1649 base = strrchr(buf, '/');
1650 if (!base)
1651 base = strrchr(buf, '\\');
1652
1653 if (base)
1654 *base = '\0';
1655 else
1656 {
1657 buf[0] = '.';
1658 buf[1] = '\0';
1659 }
1660}
1661
1662#if defined(__QNX__) || defined(_WIN32)
1663/* Blackberry QNX doesn't have strcasestr */
1664
1665/*
1666 * Find the first occurrence of find in s, ignore case.
1667 */
1668char *
1669strcasestr(const char *s, const char *find)
1670{
1671 char c, sc;
1672 size_t len;
1673
1674 if ((c = *find++) != 0)
1675 {
1676 c = tolower((unsigned char)c);
1677 len = strlen(find);
1678 do
1679 {
1680 do
1681 {
1682 if ((sc = *s++) == 0)
1683 return (NULL);
1684 } while ((char)tolower((unsigned char)sc) != c);
1685 } while (strncasecmp(s, find, len) != 0);
1686 s--;
1687 }
1688 return ((char *)s);
1689}
1690#endif
1691
1692static void set_retro_memmap(void)
1693{
1694 uint64_t flags_ram = RETRO_MEMDESC_SYSTEM_RAM;
1695 struct retro_memory_map retromap = { 0 };
1696 struct retro_memory_descriptor descs[] = {
1697 { flags_ram, psxM, 0, 0x00000000, 0x5fe00000, 0, 0x200000 },
1698 { flags_ram, psxH, 0, 0x1f800000, 0x7ffffc00, 0, 0x000400 },
1699 // not ram but let the frontend patch it if it wants; should be last
1700 { flags_ram, psxR, 0, 0x1fc00000, 0x5ff80000, 0, 0x080000 },
1701 };
1702
1703 retromap.descriptors = descs;
1704 retromap.num_descriptors = sizeof(descs) / sizeof(descs[0]);
1705 if (Config.HLE)
1706 retromap.num_descriptors--;
1707
1708 environ_cb(RETRO_ENVIRONMENT_SET_MEMORY_MAPS, &retromap);
1709}
1710
1711static void show_notification(const char *msg_str,
1712 unsigned duration_ms, unsigned priority)
1713{
1714 if (msg_interface_version >= 1)
1715 {
1716 struct retro_message_ext msg = {
1717 msg_str,
1718 duration_ms,
1719 3,
1720 RETRO_LOG_WARN,
1721 RETRO_MESSAGE_TARGET_ALL,
1722 RETRO_MESSAGE_TYPE_NOTIFICATION,
1723 -1
1724 };
1725 environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE_EXT, &msg);
1726 }
1727 else
1728 {
1729 struct retro_message msg = {
1730 msg_str,
1731 180
1732 };
1733 environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE, &msg);
1734 }
1735}
1736
1737static void retro_audio_buff_status_cb(
1738 bool active, unsigned occupancy, bool underrun_likely)
1739{
1740 retro_audio_buff_active = active;
1741 retro_audio_buff_occupancy = occupancy;
1742 retro_audio_buff_underrun = underrun_likely;
1743}
1744
1745static void retro_set_audio_buff_status_cb(void)
1746{
1747 if (frameskip_type == FRAMESKIP_NONE)
1748 {
1749 environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK, NULL);
1750 retro_audio_latency = 0;
1751 }
1752 else
1753 {
1754 bool calculate_audio_latency = true;
1755
1756 if (frameskip_type == FRAMESKIP_FIXED_INTERVAL)
1757 environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK, NULL);
1758 else
1759 {
1760 struct retro_audio_buffer_status_callback buf_status_cb;
1761 buf_status_cb.callback = retro_audio_buff_status_cb;
1762 if (!environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK,
1763 &buf_status_cb))
1764 {
1765 retro_audio_buff_active = false;
1766 retro_audio_buff_occupancy = 0;
1767 retro_audio_buff_underrun = false;
1768 retro_audio_latency = 0;
1769 calculate_audio_latency = false;
1770 }
1771 }
1772
1773 if (calculate_audio_latency)
1774 {
1775 /* Frameskip is enabled - increase frontend
1776 * audio latency to minimise potential
1777 * buffer underruns */
1778 uint32_t frame_time_usec = 1000000.0 / (is_pal_mode ? 50.0 : 60.0);
1779
1780 /* Set latency to 6x current frame time... */
1781 retro_audio_latency = (unsigned)(6 * frame_time_usec / 1000);
1782
1783 /* ...then round up to nearest multiple of 32 */
1784 retro_audio_latency = (retro_audio_latency + 0x1F) & ~0x1F;
1785 }
1786 }
1787
1788 update_audio_latency = true;
1789 frameskip_counter = 0;
1790}
1791
1792static void update_variables(bool in_flight);
1793
1794static int get_bool_variable(const char *key)
1795{
1796 struct retro_variable var = { NULL, };
1797
1798 var.key = key;
1799 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
1800 {
1801 if (strcmp(var.value, "enabled") == 0)
1802 return 1;
1803 }
1804 return 0;
1805}
1806
1807bool retro_load_game(const struct retro_game_info *info)
1808{
1809 size_t i;
1810 unsigned int cd_index = 0;
1811 bool is_m3u, is_exe;
1812 int ret;
1813
1814 struct retro_input_descriptor desc[] = {
1815#define JOYP(port) \
1816 { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" }, \
1817 { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" }, \
1818 { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" }, \
1819 { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" }, \
1820 { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "Cross" }, \
1821 { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "Circle" }, \
1822 { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Triangle" }, \
1823 { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Square" }, \
1824 { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "L1" }, \
1825 { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2, "L2" }, \
1826 { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3, "L3" }, \
1827 { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "R1" }, \
1828 { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2, "R2" }, \
1829 { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3, "R3" }, \
1830 { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" }, \
1831 { port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" }, \
1832 { port, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X" }, \
1833 { port, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" }, \
1834 { port, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X" }, \
1835 { port, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" }, \
1836 { port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_TRIGGER, "Gun Trigger" }, \
1837 { port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_RELOAD, "Gun Reload" }, \
1838 { port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_AUX_A, "Gun Aux A" }, \
1839 { port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_AUX_B, "Gun Aux B" }, \
1840 { port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_START, "Gun Start" },
1841
1842 JOYP(0)
1843 JOYP(1)
1844 JOYP(2)
1845 JOYP(3)
1846 JOYP(4)
1847 JOYP(5)
1848 JOYP(6)
1849 JOYP(7)
1850
1851 { 0 },
1852 };
1853
1854 environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc);
1855
1856 enum retro_pixel_format fmt = get_bool_variable("pcsx_rearmed_rgb32_output")
1857 ? RETRO_PIXEL_FORMAT_XRGB8888 : RETRO_PIXEL_FORMAT_RGB565;
1858 if (environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt))
1859 current_fmt = fmt;
1860 else
1861 LogErr("SET_PIXEL_FORMAT failed\n");
1862 SysPrintf("Using PIXEL_FORMAT %d\n", current_fmt);
1863 set_bgr_to_fb_func(0);
1864
1865 if (info == NULL || info->path == NULL)
1866 {
1867 LogErr("info->path required\n");
1868 return false;
1869 }
1870 is_m3u = (strcasestr(info->path, ".m3u") != NULL);
1871 is_exe = (strcasestr(info->path, ".exe") != NULL);
1872
1873 update_variables(false);
1874
1875 if (plugins_opened)
1876 {
1877 ClosePlugins();
1878 plugins_opened = 0;
1879 }
1880
1881 disk_init();
1882
1883 extract_directory(base_dir, info->path, sizeof(base_dir));
1884
1885 if (is_m3u)
1886 {
1887 if (!read_m3u(info->path))
1888 {
1889 LogErr("failed to read m3u file\n");
1890 return false;
1891 }
1892 }
1893 else
1894 {
1895 char disk_label[PATH_MAX];
1896 disk_label[0] = '\0';
1897
1898 disk_count = 1;
1899 disks[0].fname = strdup(info->path);
1900
1901 get_disk_label(disk_label, info->path, PATH_MAX);
1902 disks[0].flabel = strdup(disk_label);
1903 }
1904
1905 /* If this is an M3U file, attempt to set the
1906 * initial disk image */
1907 if (is_m3u && (disk_initial_index > 0) && (disk_initial_index < disk_count))
1908 {
1909 const char *fname = disks[disk_initial_index].fname;
1910
1911 if (fname && (*fname != '\0'))
1912 if (strcmp(disk_initial_path, fname) == 0)
1913 cd_index = disk_initial_index;
1914 }
1915
1916 set_cd_image(disks[cd_index].fname);
1917 disk_current_index = cd_index;
1918
1919 /* have to reload after set_cd_image for correct cdr plugin */
1920 if (LoadPlugins() == -1)
1921 {
1922 LogErr("failed to load plugins\n");
1923 return false;
1924 }
1925 if (!strncmp(info->path, "cdrom:", 6))
1926 {
1927#if !defined(HAVE_CDROM) && !defined(USE_LIBRETRO_VFS)
1928 ReleasePlugins();
1929 LogErr("%s\n", "Physical CD-ROM support is not compiled in.");
1930 show_notification("Physical CD-ROM support is not compiled in.", 6000, 3);
1931 return false;
1932#endif
1933 }
1934
1935 plugins_opened = 1;
1936
1937 if (OpenPlugins() == -1)
1938 {
1939 LogErr("failed to open plugins\n");
1940 return false;
1941 }
1942
1943 /* Handle multi-disk images (i.e. PBP)
1944 * > Cannot do this until after OpenPlugins() is
1945 * called (since this sets the value of
1946 * cdrIsoMultidiskCount) */
1947 if (!is_m3u && (cdrIsoMultidiskCount > 1))
1948 {
1949 disk_count = cdrIsoMultidiskCount < 8 ? cdrIsoMultidiskCount : 8;
1950
1951 /* Small annoyance: We need to change the label
1952 * of disk 0, so have to clear existing entries */
1953 if (disks[0].fname != NULL)
1954 free(disks[0].fname);
1955 disks[0].fname = NULL;
1956
1957 if (disks[0].flabel != NULL)
1958 free(disks[0].flabel);
1959 disks[0].flabel = NULL;
1960
1961 for (i = 0; i < sizeof(disks) / sizeof(disks[0]) && i < cdrIsoMultidiskCount; i++)
1962 {
1963 char disk_name[PATH_MAX - 16] = { 0 };
1964 char disk_label[PATH_MAX] = { 0 };
1965
1966 disks[i].fname = strdup(info->path);
1967
1968 get_disk_label(disk_name, info->path, sizeof(disk_name));
1969 snprintf(disk_label, sizeof(disk_label), "%s #%u", disk_name, (unsigned)i + 1);
1970 disks[i].flabel = strdup(disk_label);
1971
1972 disks[i].internal_index = i;
1973 }
1974
1975 /* This is not an M3U file, so initial disk
1976 * image has not yet been set - attempt to
1977 * do so now */
1978 if ((disk_initial_index > 0) && (disk_initial_index < disk_count))
1979 {
1980 const char *fname = disks[disk_initial_index].fname;
1981
1982 if (fname && (*fname != '\0'))
1983 if (strcmp(disk_initial_path, fname) == 0)
1984 cd_index = disk_initial_index;
1985 }
1986
1987 if (cd_index > 0)
1988 {
1989 CdromId[0] = '\0';
1990 CdromLabel[0] = '\0';
1991
1992 cdrIsoMultidiskSelect = disks[cd_index].internal_index;
1993 disk_current_index = cd_index;
1994 set_cd_image(disks[cd_index].fname);
1995
1996 if (ReloadCdromPlugin() < 0)
1997 {
1998 LogErr("failed to reload cdr plugins\n");
1999 return false;
2000 }
2001 if (cdra_open() < 0)
2002 {
2003 LogErr("failed to open cdr plugin\n");
2004 return false;
2005 }
2006 }
2007 }
2008
2009 /* set ports to use "standard controller" initially */
2010 for (i = 0; i < 8; ++i)
2011 in_type[i] = PSE_PAD_TYPE_STANDARD;
2012
2013 if (!is_exe && CheckCdrom() == -1)
2014 {
2015 LogErr("unsupported/invalid CD image: %s\n", info->path);
2016 return false;
2017 }
2018
2019 plugin_call_rearmed_cbs();
2020 SysReset();
2021
2022 if (is_exe)
2023 ret = Load(info->path);
2024 else
2025 ret = LoadCdrom();
2026 if (ret != 0)
2027 {
2028 LogErr("could not load %s (%d)\n", is_exe ? "exe" : "CD", ret);
2029 return false;
2030 }
2031 emu_on_new_cd(0);
2032
2033 set_retro_memmap();
2034 retro_set_audio_buff_status_cb();
2035 log_mem_usage();
2036
2037 if (check_unsatisfied_libcrypt())
2038 show_notification("LibCrypt protected game with missing SBI detected", 3000, 3);
2039 if (Config.TurboCD)
2040 show_notification("TurboCD is ON", 700, 2);
2041
2042 return true;
2043}
2044
2045unsigned retro_get_region(void)
2046{
2047 return is_pal_mode ? RETRO_REGION_PAL : RETRO_REGION_NTSC;
2048}
2049
2050void *retro_get_memory_data(unsigned id)
2051{
2052 if (id == RETRO_MEMORY_SAVE_RAM)
2053 return Mcd1Data;
2054 else if (id == RETRO_MEMORY_SYSTEM_RAM)
2055 return psxM;
2056 else
2057 return NULL;
2058}
2059
2060size_t retro_get_memory_size(unsigned id)
2061{
2062 if (id == RETRO_MEMORY_SAVE_RAM)
2063 return MCD_SIZE;
2064 else if (id == RETRO_MEMORY_SYSTEM_RAM)
2065 return 0x200000;
2066 else
2067 return 0;
2068}
2069
2070void retro_reset(void)
2071{
2072 //hack to prevent retroarch freezing when reseting in the menu but not while running with the hot key
2073 rebootemu = 1;
2074 //SysReset();
2075}
2076
2077static const unsigned short retro_psx_map[] = {
2078 [RETRO_DEVICE_ID_JOYPAD_B] = 1 << DKEY_CROSS,
2079 [RETRO_DEVICE_ID_JOYPAD_Y] = 1 << DKEY_SQUARE,
2080 [RETRO_DEVICE_ID_JOYPAD_SELECT] = 1 << DKEY_SELECT,
2081 [RETRO_DEVICE_ID_JOYPAD_START] = 1 << DKEY_START,
2082 [RETRO_DEVICE_ID_JOYPAD_UP] = 1 << DKEY_UP,
2083 [RETRO_DEVICE_ID_JOYPAD_DOWN] = 1 << DKEY_DOWN,
2084 [RETRO_DEVICE_ID_JOYPAD_LEFT] = 1 << DKEY_LEFT,
2085 [RETRO_DEVICE_ID_JOYPAD_RIGHT] = 1 << DKEY_RIGHT,
2086 [RETRO_DEVICE_ID_JOYPAD_A] = 1 << DKEY_CIRCLE,
2087 [RETRO_DEVICE_ID_JOYPAD_X] = 1 << DKEY_TRIANGLE,
2088 [RETRO_DEVICE_ID_JOYPAD_L] = 1 << DKEY_L1,
2089 [RETRO_DEVICE_ID_JOYPAD_R] = 1 << DKEY_R1,
2090 [RETRO_DEVICE_ID_JOYPAD_L2] = 1 << DKEY_L2,
2091 [RETRO_DEVICE_ID_JOYPAD_R2] = 1 << DKEY_R2,
2092 [RETRO_DEVICE_ID_JOYPAD_L3] = 1 << DKEY_L3,
2093 [RETRO_DEVICE_ID_JOYPAD_R3] = 1 << DKEY_R3,
2094};
2095#define RETRO_PSX_MAP_LEN (sizeof(retro_psx_map) / sizeof(retro_psx_map[0]))
2096
2097//Percentage distance of screen to adjust for Guncon
2098static int GunconAdjustX = 0;
2099static int GunconAdjustY = 0;
2100
2101//Used when out by a percentage with Guncon
2102static float GunconAdjustRatioX = 1;
2103static float GunconAdjustRatioY = 1;
2104
2105static void update_variables(bool in_flight)
2106{
2107 struct retro_variable var;
2108#ifdef GPU_PEOPS
2109 // Always enable GPU_PEOPS_OLD_FRAME_SKIP flag
2110 // (this is set in standalone, with no option
2111 // to change it)
2112 int gpu_peops_fix = GPU_PEOPS_OLD_FRAME_SKIP;
2113#endif
2114 frameskip_type_t prev_frameskip_type;
2115 double old_fps = psxGetFps();
2116
2117 var.value = NULL;
2118 var.key = "pcsx_rearmed_frameskip_type";
2119
2120 prev_frameskip_type = frameskip_type;
2121 frameskip_type = FRAMESKIP_NONE;
2122 pl_rearmed_cbs.frameskip = 0;
2123
2124 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2125 {
2126 if (strcmp(var.value, "auto") == 0)
2127 frameskip_type = FRAMESKIP_AUTO;
2128 if (strcmp(var.value, "auto_threshold") == 0)
2129 frameskip_type = FRAMESKIP_AUTO_THRESHOLD;
2130 if (strcmp(var.value, "fixed_interval") == 0)
2131 frameskip_type = FRAMESKIP_FIXED_INTERVAL;
2132 }
2133
2134 if (frameskip_type != 0)
2135 pl_rearmed_cbs.frameskip = -1;
2136
2137 var.value = NULL;
2138 var.key = "pcsx_rearmed_frameskip_threshold";
2139 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2140 {
2141 frameskip_threshold = strtol(var.value, NULL, 10);
2142 }
2143
2144 var.value = NULL;
2145 var.key = "pcsx_rearmed_frameskip_interval";
2146 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2147 {
2148 frameskip_interval = strtol(var.value, NULL, 10);
2149 }
2150
2151 var.value = NULL;
2152 var.key = "pcsx_rearmed_region";
2153 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2154 {
2155 Config.PsxAuto = 0;
2156 if (strcmp(var.value, "auto") == 0)
2157 Config.PsxAuto = 1;
2158 else if (strcmp(var.value, "NTSC") == 0)
2159 Config.PsxType = 0;
2160 else if (strcmp(var.value, "PAL") == 0)
2161 Config.PsxType = 1;
2162 }
2163
2164 update_multitap();
2165
2166 var.value = NULL;
2167 var.key = "pcsx_rearmed_negcon_deadzone";
2168 negcon_deadzone = 0;
2169 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2170 {
2171 negcon_deadzone = (int)(atoi(var.value) * 0.01f * NEGCON_RANGE);
2172 }
2173
2174 var.value = NULL;
2175 var.key = "pcsx_rearmed_negcon_response";
2176 negcon_linearity = 1;
2177 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2178 {
2179 if (strcmp(var.value, "quadratic") == 0)
2180 {
2181 negcon_linearity = 2;
2182 }
2183 else if (strcmp(var.value, "cubic") == 0)
2184 {
2185 negcon_linearity = 3;
2186 }
2187 }
2188
2189 var.value = NULL;
2190 var.key = "pcsx_rearmed_analog_axis_modifier";
2191 axis_bounds_modifier = true;
2192 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2193 {
2194 if (strcmp(var.value, "square") == 0)
2195 {
2196 axis_bounds_modifier = true;
2197 }
2198 else
2199 {
2200 axis_bounds_modifier = false;
2201 }
2202 }
2203
2204 var.value = NULL;
2205 var.key = "pcsx_rearmed_vibration";
2206
2207 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2208 {
2209 if (strcmp(var.value, "disabled") == 0)
2210 in_enable_vibration = 0;
2211 else if (strcmp(var.value, "enabled") == 0)
2212 in_enable_vibration = 1;
2213 }
2214
2215 var.value = NULL;
2216 var.key = "pcsx_rearmed_analog_combo";
2217
2218 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2219 {
2220 if (strcmp(var.value, "l1+r1+select") == 0)
2221 in_dualshock_analog_combo = (1 << RETRO_DEVICE_ID_JOYPAD_L) |
2222 (1 << RETRO_DEVICE_ID_JOYPAD_R) | (1 << RETRO_DEVICE_ID_JOYPAD_SELECT);
2223 else if (strcmp(var.value, "l1+r1+start") == 0)
2224 in_dualshock_analog_combo = (1 << RETRO_DEVICE_ID_JOYPAD_L) |
2225 (1 << RETRO_DEVICE_ID_JOYPAD_R) | (1 << RETRO_DEVICE_ID_JOYPAD_START);
2226 else if (strcmp(var.value, "l1+r1+l3") == 0)
2227 in_dualshock_analog_combo = (1 << RETRO_DEVICE_ID_JOYPAD_L) |
2228 (1 << RETRO_DEVICE_ID_JOYPAD_R) | (1 << RETRO_DEVICE_ID_JOYPAD_L3);
2229 else if (strcmp(var.value, "l1+r1+r3") == 0)
2230 in_dualshock_analog_combo = (1 << RETRO_DEVICE_ID_JOYPAD_L) |
2231 (1 << RETRO_DEVICE_ID_JOYPAD_R) | (1 << RETRO_DEVICE_ID_JOYPAD_R3);
2232 else if (strcmp(var.value, "l3+r3") == 0)
2233 in_dualshock_analog_combo = (1 << RETRO_DEVICE_ID_JOYPAD_L3) |
2234 (1 << RETRO_DEVICE_ID_JOYPAD_R3);
2235 else
2236 in_dualshock_analog_combo = 0;
2237 }
2238
2239 var.value = NULL;
2240 var.key = "pcsx_rearmed_dithering";
2241
2242 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2243 {
2244 if (strcmp(var.value, "force") == 0)
2245 {
2246 pl_rearmed_cbs.dithering = 2;
2247 pl_rearmed_cbs.gpu_peopsgl.bDrawDither = 1;
2248 }
2249 else if (strcmp(var.value, "disabled") == 0)
2250 {
2251 pl_rearmed_cbs.dithering = 0;
2252 pl_rearmed_cbs.gpu_peopsgl.bDrawDither = 0;
2253 }
2254 else
2255 {
2256 pl_rearmed_cbs.dithering = 1;
2257 pl_rearmed_cbs.gpu_peopsgl.bDrawDither = 1;
2258 }
2259 }
2260
2261#ifdef GPU_NEON
2262 var.value = NULL;
2263 var.key = "pcsx_rearmed_neon_interlace_enable_v2";
2264
2265 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2266 {
2267 if (strcmp(var.value, "disabled") == 0)
2268 pl_rearmed_cbs.gpu_neon.allow_interlace = 0;
2269 else if (strcmp(var.value, "enabled") == 0)
2270 pl_rearmed_cbs.gpu_neon.allow_interlace = 1;
2271 else // auto
2272 pl_rearmed_cbs.gpu_neon.allow_interlace = 2;
2273 }
2274
2275 var.value = NULL;
2276 var.key = "pcsx_rearmed_neon_enhancement_enable";
2277
2278 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2279 {
2280 if (strcmp(var.value, "disabled") == 0)
2281 pl_rearmed_cbs.gpu_neon.enhancement_enable = 0;
2282 else if (strcmp(var.value, "enabled") == 0)
2283 pl_rearmed_cbs.gpu_neon.enhancement_enable = 1;
2284 }
2285
2286 var.value = NULL;
2287 var.key = "pcsx_rearmed_neon_enhancement_no_main";
2288
2289 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2290 {
2291 if (strcmp(var.value, "enabled") == 0)
2292 pl_rearmed_cbs.gpu_neon.enhancement_no_main = 1;
2293 else
2294 pl_rearmed_cbs.gpu_neon.enhancement_no_main = 0;
2295 }
2296
2297 var.value = NULL;
2298 var.key = "pcsx_rearmed_neon_enhancement_tex_adj_v2";
2299
2300 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2301 {
2302 if (strcmp(var.value, "enabled") == 0)
2303 pl_rearmed_cbs.gpu_neon.enhancement_tex_adj = 1;
2304 else
2305 pl_rearmed_cbs.gpu_neon.enhancement_tex_adj = 0;
2306 }
2307#endif
2308
2309 var.value = NULL;
2310 var.key = "pcsx_rearmed_display_fps_v2";
2311
2312 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2313 {
2314 if (strcmp(var.value, "extra") == 0)
2315 display_internal_fps = 2;
2316 else if (strcmp(var.value, "enabled") == 0)
2317 display_internal_fps = 1;
2318 else
2319 display_internal_fps = 0;
2320 }
2321
2322 var.value = NULL;
2323 var.key = "pcsx_rearmed_cd_turbo";
2324 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2325 {
2326 if (strcmp(var.value, "enabled") == 0)
2327 Config.TurboCD = true;
2328 else
2329 Config.TurboCD = false;
2330 }
2331
2332#if defined(HAVE_CDROM) || defined(USE_ASYNC_CDROM)
2333 var.value = NULL;
2334 var.key = "pcsx_rearmed_cd_readahead";
2335 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2336 {
2337 cdra_set_buf_count(strtol(var.value, NULL, 10));
2338 }
2339#endif
2340
2341 //
2342 // CPU emulation related config
2343#ifndef DRC_DISABLE
2344 var.value = NULL;
2345 var.key = "pcsx_rearmed_drc";
2346
2347 if (!environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var))
2348 var.value = "enabled";
2349
2350 {
2351 R3000Acpu *prev_cpu = psxCpu;
2352
2353#ifdef _3DS
2354 if (!__ctr_svchax)
2355 Config.Cpu = CPU_INTERPRETER;
2356 else
2357#endif
2358 if (strcmp(var.value, "disabled") == 0)
2359 Config.Cpu = CPU_INTERPRETER;
2360 else if (strcmp(var.value, "enabled") == 0)
2361 Config.Cpu = CPU_DYNAREC;
2362
2363 psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
2364 if (psxCpu != prev_cpu)
2365 {
2366 prev_cpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
2367 prev_cpu->Shutdown();
2368 psxCpu->Init();
2369 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
2370 }
2371 }
2372#endif // !DRC_DISABLE
2373
2374 var.value = NULL;
2375 var.key = "pcsx_rearmed_psxclock";
2376 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2377 {
2378 int psxclock = atoi(var.value);
2379 if (strcmp(var.value, "auto") == 0 || psxclock == 0)
2380 Config.cycle_multiplier = CYCLE_MULT_DEFAULT;
2381 else
2382 Config.cycle_multiplier = 10000 / psxclock;
2383 }
2384
2385#if !defined(DRC_DISABLE) && !defined(LIGHTREC)
2386#ifdef NDRC_THREAD
2387 var.value = NULL;
2388 var.key = "pcsx_rearmed_drc_thread";
2389 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2390 {
2391 ndrc_g.hacks &= ~(NDHACK_THREAD_FORCE | NDHACK_THREAD_FORCE_ON);
2392 if (strcmp(var.value, "disabled") == 0)
2393 ndrc_g.hacks |= NDHACK_THREAD_FORCE;
2394 else if (strcmp(var.value, "enabled") == 0)
2395 ndrc_g.hacks |= NDHACK_THREAD_FORCE | NDHACK_THREAD_FORCE_ON;
2396 // psxCpu->ApplyConfig(); will start/stop the thread
2397 }
2398#endif
2399
2400 var.value = NULL;
2401 var.key = "pcsx_rearmed_nosmccheck";
2402 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2403 {
2404 if (strcmp(var.value, "enabled") == 0)
2405 ndrc_g.hacks |= NDHACK_NO_SMC_CHECK;
2406 else
2407 ndrc_g.hacks &= ~NDHACK_NO_SMC_CHECK;
2408 }
2409
2410 var.value = NULL;
2411 var.key = "pcsx_rearmed_gteregsunneeded";
2412 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2413 {
2414 if (strcmp(var.value, "enabled") == 0)
2415 ndrc_g.hacks |= NDHACK_GTE_UNNEEDED;
2416 else
2417 ndrc_g.hacks &= ~NDHACK_GTE_UNNEEDED;
2418 }
2419
2420 var.value = NULL;
2421 var.key = "pcsx_rearmed_nogteflags";
2422 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2423 {
2424 if (strcmp(var.value, "enabled") == 0)
2425 ndrc_g.hacks |= NDHACK_GTE_NO_FLAGS;
2426 else
2427 ndrc_g.hacks &= ~NDHACK_GTE_NO_FLAGS;
2428 }
2429
2430 var.value = NULL;
2431 var.key = "pcsx_rearmed_nocompathacks";
2432 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2433 {
2434 if (strcmp(var.value, "enabled") == 0)
2435 ndrc_g.hacks |= NDHACK_NO_COMPAT_HACKS;
2436 else
2437 ndrc_g.hacks &= ~NDHACK_NO_COMPAT_HACKS;
2438 }
2439#endif /* !DRC_DISABLE && !LIGHTREC */
2440
2441 var.value = NULL;
2442 var.key = "pcsx_rearmed_nostalls";
2443 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2444 {
2445 if (strcmp(var.value, "enabled") == 0)
2446 Config.DisableStalls = 1;
2447 else
2448 Config.DisableStalls = 0;
2449 }
2450
2451 var.value = NULL;
2452 var.key = "pcsx_rearmed_icache_emulation";
2453 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2454 {
2455 if (strcmp(var.value, "disabled") == 0)
2456 Config.icache_emulation = 0;
2457 else if (strcmp(var.value, "enabled") == 0)
2458 Config.icache_emulation = 1;
2459 }
2460
2461 var.value = NULL;
2462 var.key = "pcsx_rearmed_exception_emulation";
2463 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2464 {
2465 if (strcmp(var.value, "enabled") == 0)
2466 Config.PreciseExceptions = 1;
2467 else
2468 Config.PreciseExceptions = 0;
2469 }
2470
2471 psxCpu->ApplyConfig();
2472
2473 // end of CPU emu config
2474 //
2475
2476 var.value = NULL;
2477 var.key = "pcsx_rearmed_spu_reverb";
2478
2479 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2480 {
2481 if (strcmp(var.value, "disabled") == 0)
2482 spu_config.iUseReverb = false;
2483 else if (strcmp(var.value, "enabled") == 0)
2484 spu_config.iUseReverb = true;
2485 }
2486
2487 var.value = NULL;
2488 var.key = "pcsx_rearmed_spu_interpolation";
2489
2490 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2491 {
2492 if (strcmp(var.value, "simple") == 0)
2493 spu_config.iUseInterpolation = 1;
2494 else if (strcmp(var.value, "gaussian") == 0)
2495 spu_config.iUseInterpolation = 2;
2496 else if (strcmp(var.value, "cubic") == 0)
2497 spu_config.iUseInterpolation = 3;
2498 else if (strcmp(var.value, "off") == 0)
2499 spu_config.iUseInterpolation = 0;
2500 }
2501
2502#if P_HAVE_PTHREAD
2503 var.value = NULL;
2504 var.key = "pcsx_rearmed_spu_thread";
2505 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2506 {
2507 if (strcmp(var.value, "enabled") == 0)
2508 spu_config.iUseThread = 1;
2509 else
2510 spu_config.iUseThread = 0;
2511 }
2512#endif
2513
2514 var.value = NULL;
2515 var.key = "pcsx_rearmed_noxadecoding";
2516 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2517 {
2518 if (strcmp(var.value, "disabled") == 0)
2519 Config.Xa = 1;
2520 else
2521 Config.Xa = 0;
2522 }
2523
2524 var.value = NULL;
2525 var.key = "pcsx_rearmed_nocdaudio";
2526 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2527 {
2528 if (strcmp(var.value, "disabled") == 0)
2529 Config.Cdda = 1;
2530 else
2531 Config.Cdda = 0;
2532 }
2533
2534 var.value = NULL;
2535 var.key = "pcsx_rearmed_gpu_slow_llists";
2536 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2537 {
2538 if (strcmp(var.value, "disabled") == 0)
2539 Config.GpuListWalking = 0;
2540 else if (strcmp(var.value, "enabled") == 0)
2541 Config.GpuListWalking = 1;
2542 else // auto
2543 Config.GpuListWalking = -1;
2544 }
2545
2546 var.value = NULL;
2547 var.key = "pcsx_rearmed_fractional_framerate";
2548 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2549 {
2550 if (strcmp(var.value, "disabled") == 0)
2551 Config.FractionalFramerate = 0;
2552 else if (strcmp(var.value, "enabled") == 0)
2553 Config.FractionalFramerate = 1;
2554 else // auto
2555 Config.FractionalFramerate = -1;
2556 }
2557
2558 var.value = NULL;
2559 var.key = "pcsx_rearmed_screen_centering";
2560 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2561 {
2562 if (strcmp(var.value, "game") == 0)
2563 pl_rearmed_cbs.screen_centering_type = 1;
2564 else if (strcmp(var.value, "borderless") == 0)
2565 pl_rearmed_cbs.screen_centering_type = 2;
2566 else if (strcmp(var.value, "manual") == 0)
2567 pl_rearmed_cbs.screen_centering_type = 3;
2568 else // auto
2569 pl_rearmed_cbs.screen_centering_type = 0;
2570 }
2571
2572 var.value = NULL;
2573 var.key = "pcsx_rearmed_screen_centering_x";
2574 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2575 {
2576 pl_rearmed_cbs.screen_centering_x = atoi(var.value);
2577 }
2578
2579 var.value = NULL;
2580 var.key = "pcsx_rearmed_screen_centering_y";
2581 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2582 {
2583 pl_rearmed_cbs.screen_centering_y = atoi(var.value);
2584 }
2585
2586 var.value = NULL;
2587 var.key = "pcsx_rearmed_screen_centering_h_adj";
2588 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2589 {
2590 pl_rearmed_cbs.screen_centering_h_adj = atoi(var.value);
2591 }
2592
2593 var.value = NULL;
2594 var.key = "pcsx_rearmed_show_overscan";
2595 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2596 {
2597 if (strcmp(var.value, "auto") == 0)
2598 pl_rearmed_cbs.show_overscan = 1;
2599 else if (strcmp(var.value, "hack") == 0)
2600 pl_rearmed_cbs.show_overscan = 2;
2601 else
2602 pl_rearmed_cbs.show_overscan = 0;
2603 }
2604
2605#ifdef THREAD_RENDERING
2606 var.key = "pcsx_rearmed_gpu_thread_rendering";
2607 var.value = NULL;
2608
2609 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2610 {
2611 if (strcmp(var.value, "disabled") == 0)
2612 pl_rearmed_cbs.thread_rendering = THREAD_RENDERING_OFF;
2613 else if (strcmp(var.value, "sync") == 0)
2614 pl_rearmed_cbs.thread_rendering = THREAD_RENDERING_SYNC;
2615 else if (strcmp(var.value, "async") == 0)
2616 pl_rearmed_cbs.thread_rendering = THREAD_RENDERING_ASYNC;
2617 }
2618#endif
2619
2620#ifdef GPU_PEOPS
2621 var.value = NULL;
2622 var.key = "pcsx_rearmed_gpu_peops_odd_even_bit";
2623
2624 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2625 {
2626 if (strcmp(var.value, "enabled") == 0)
2627 gpu_peops_fix |= GPU_PEOPS_ODD_EVEN_BIT;
2628 }
2629
2630 var.value = NULL;
2631 var.key = "pcsx_rearmed_gpu_peops_expand_screen_width";
2632
2633 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2634 {
2635 if (strcmp(var.value, "enabled") == 0)
2636 gpu_peops_fix |= GPU_PEOPS_EXPAND_SCREEN_WIDTH;
2637 }
2638
2639 var.value = NULL;
2640 var.key = "pcsx_rearmed_gpu_peops_ignore_brightness";
2641
2642 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2643 {
2644 if (strcmp(var.value, "enabled") == 0)
2645 gpu_peops_fix |= GPU_PEOPS_IGNORE_BRIGHTNESS;
2646 }
2647
2648 var.value = NULL;
2649 var.key = "pcsx_rearmed_gpu_peops_disable_coord_check";
2650
2651 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2652 {
2653 if (strcmp(var.value, "enabled") == 0)
2654 gpu_peops_fix |= GPU_PEOPS_DISABLE_COORD_CHECK;
2655 }
2656
2657 var.value = NULL;
2658 var.key = "pcsx_rearmed_gpu_peops_lazy_screen_update";
2659
2660 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2661 {
2662 if (strcmp(var.value, "enabled") == 0)
2663 gpu_peops_fix |= GPU_PEOPS_LAZY_SCREEN_UPDATE;
2664 }
2665
2666 var.value = NULL;
2667 var.key = "pcsx_rearmed_gpu_peops_repeated_triangles";
2668
2669 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2670 {
2671 if (strcmp(var.value, "enabled") == 0)
2672 gpu_peops_fix |= GPU_PEOPS_REPEATED_TRIANGLES;
2673 }
2674
2675 var.value = NULL;
2676 var.key = "pcsx_rearmed_gpu_peops_quads_with_triangles";
2677
2678 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2679 {
2680 if (strcmp(var.value, "enabled") == 0)
2681 gpu_peops_fix |= GPU_PEOPS_QUADS_WITH_TRIANGLES;
2682 }
2683
2684 var.value = NULL;
2685 var.key = "pcsx_rearmed_gpu_peops_fake_busy_state";
2686
2687 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2688 {
2689 if (strcmp(var.value, "enabled") == 0)
2690 gpu_peops_fix |= GPU_PEOPS_FAKE_BUSY_STATE;
2691 }
2692
2693 if (pl_rearmed_cbs.gpu_peops.dwActFixes != gpu_peops_fix)
2694 pl_rearmed_cbs.gpu_peops.dwActFixes = gpu_peops_fix;
2695#endif
2696
2697#ifdef GPU_UNAI
2698 /* Note: This used to be an option, but it only works
2699 * (correctly) when running high resolution games
2700 * (480i, 512i) and has been obsoleted by
2701 * pcsx_rearmed_gpu_unai_scale_hires */
2702 pl_rearmed_cbs.gpu_unai.ilace_force = 0;
2703
2704 var.key = "pcsx_rearmed_gpu_unai_old_renderer";
2705 var.value = NULL;
2706
2707 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2708 {
2709 if (strcmp(var.value, "enabled") == 0)
2710 pl_rearmed_cbs.gpu_unai.old_renderer = 1;
2711 else
2712 pl_rearmed_cbs.gpu_unai.old_renderer = 0;
2713 }
2714
2715 var.key = "pcsx_rearmed_gpu_unai_skipline";
2716 var.value = NULL;
2717
2718 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2719 {
2720 if (strcmp(var.value, "disabled") == 0)
2721 pl_rearmed_cbs.gpu_unai.ilace_force = 0;
2722 else if (strcmp(var.value, "enabled") == 0)
2723 pl_rearmed_cbs.gpu_unai.ilace_force = 1;
2724 }
2725
2726 var.key = "pcsx_rearmed_gpu_unai_lighting";
2727 var.value = NULL;
2728
2729 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2730 {
2731 if (strcmp(var.value, "disabled") == 0)
2732 pl_rearmed_cbs.gpu_unai.lighting = 0;
2733 else if (strcmp(var.value, "enabled") == 0)
2734 pl_rearmed_cbs.gpu_unai.lighting = 1;
2735 }
2736
2737 var.key = "pcsx_rearmed_gpu_unai_fast_lighting";
2738 var.value = NULL;
2739
2740 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2741 {
2742 if (strcmp(var.value, "disabled") == 0)
2743 pl_rearmed_cbs.gpu_unai.fast_lighting = 0;
2744 else if (strcmp(var.value, "enabled") == 0)
2745 pl_rearmed_cbs.gpu_unai.fast_lighting = 1;
2746 }
2747
2748 var.key = "pcsx_rearmed_gpu_unai_blending";
2749 var.value = NULL;
2750
2751 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2752 {
2753 if (strcmp(var.value, "disabled") == 0)
2754 pl_rearmed_cbs.gpu_unai.blending = 0;
2755 else if (strcmp(var.value, "enabled") == 0)
2756 pl_rearmed_cbs.gpu_unai.blending = 1;
2757 }
2758
2759 var.key = "pcsx_rearmed_gpu_unai_scale_hires";
2760 var.value = NULL;
2761
2762 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2763 {
2764 if (strcmp(var.value, "disabled") == 0)
2765 pl_rearmed_cbs.gpu_unai.scale_hires = 0;
2766 else if (strcmp(var.value, "enabled") == 0)
2767 pl_rearmed_cbs.gpu_unai.scale_hires = 1;
2768 }
2769#endif // GPU_UNAI
2770
2771 var.value = NULL;
2772 var.key = "pcsx_rearmed_crosshair1";
2773
2774 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2775 {
2776 if (strcmp(var.value, "disabled") == 0)
2777 in_enable_crosshair[0] = 0;
2778 else if (strcmp(var.value, "blue") == 0)
2779 in_enable_crosshair[0] = 0x1F;
2780 else if (strcmp(var.value, "green") == 0)
2781 in_enable_crosshair[0] = 0x7E0;
2782 else if (strcmp(var.value, "red") == 0)
2783 in_enable_crosshair[0] = 0xF800;
2784 else if (strcmp(var.value, "white") == 0)
2785 in_enable_crosshair[0] = 0xFFFF;
2786 }
2787
2788 var.value = NULL;
2789 var.key = "pcsx_rearmed_crosshair2";
2790
2791 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2792 {
2793 if (strcmp(var.value, "disabled") == 0)
2794 in_enable_crosshair[1] = 0;
2795 else if (strcmp(var.value, "blue") == 0)
2796 in_enable_crosshair[1] = 0x1F;
2797 else if (strcmp(var.value, "green") == 0)
2798 in_enable_crosshair[1] = 0x7E0;
2799 else if (strcmp(var.value, "red") == 0)
2800 in_enable_crosshair[1] = 0xF800;
2801 else if (strcmp(var.value, "white") == 0)
2802 in_enable_crosshair[1] = 0xFFFF;
2803 }
2804
2805 //This adjustment process gives the user the ability to manually align the mouse up better
2806 //with where the shots are in the emulator.
2807
2808 var.value = NULL;
2809 var.key = "pcsx_rearmed_konamigunadjustx";
2810
2811 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2812 {
2813 KonamiGunAdjustX = atof(var.value) / 100.0f;
2814 }
2815
2816 var.value = NULL;
2817 var.key = "pcsx_rearmed_konamigunadjusty";
2818
2819 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2820 {
2821 KonamiGunAdjustY = atof(var.value) / 100.0f;
2822 }
2823
2824 var.value = NULL;
2825 var.key = "pcsx_rearmed_gunconadjustx";
2826
2827 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2828 {
2829 GunconAdjustX = atoi(var.value);
2830 }
2831
2832 var.value = NULL;
2833 var.key = "pcsx_rearmed_gunconadjusty";
2834
2835 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2836 {
2837 GunconAdjustY = atoi(var.value);
2838 }
2839
2840 var.value = NULL;
2841 var.key = "pcsx_rearmed_gunconadjustratiox";
2842
2843 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2844 {
2845 GunconAdjustRatioX = atof(var.value);
2846 }
2847
2848 var.value = NULL;
2849 var.key = "pcsx_rearmed_gunconadjustratioy";
2850
2851 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2852 {
2853 GunconAdjustRatioY = atof(var.value);
2854 }
2855
2856 var.value = NULL;
2857 var.key = "pcsx_rearmed_input_sensitivity";
2858 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2859 {
2860 mouse_sensitivity = atof(var.value);
2861 }
2862
2863#ifdef _3DS
2864 var.value = NULL;
2865 var.key = "pcsx_rearmed_3ds_appcputime";
2866 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2867 {
2868 APT_SetAppCpuTimeLimit(strtol(var.value, NULL, 10));
2869 }
2870#endif
2871
2872 if (found_bios)
2873 {
2874 var.value = NULL;
2875 var.key = "pcsx_rearmed_show_bios_bootlogo";
2876 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2877 {
2878 Config.SlowBoot = 0;
2879 if (strcmp(var.value, "enabled") == 0)
2880 Config.SlowBoot = 1;
2881 }
2882 }
2883
2884 if (in_flight)
2885 {
2886 // inform core things about possible config changes
2887 plugin_call_rearmed_cbs();
2888
2889 if (GPU_open != NULL && GPU_close != NULL)
2890 {
2891 GPU_close();
2892 GPU_open(&gpuDisp, "PCSX", NULL);
2893 }
2894
2895 /* Reinitialise frameskipping, if required */
2896 if (((frameskip_type != prev_frameskip_type)))
2897 retro_set_audio_buff_status_cb();
2898
2899 /* dfinput_activate(); */
2900 }
2901
2902 update_option_visibility();
2903
2904 if (in_flight && old_fps != psxGetFps())
2905 {
2906 struct retro_system_av_info info;
2907 retro_get_system_av_info(&info);
2908 environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &info);
2909 }
2910}
2911
2912// Taken from beetle-psx-libretro
2913static uint16_t get_analog_button(int16_t ret, retro_input_state_t input_state_cb, int player_index, int id)
2914{
2915 // NOTE: Analog buttons were added Nov 2017. Not all front-ends support this
2916 // feature (or pre-date it) so we need to handle this in a graceful way.
2917
2918 // First, try and get an analog value using the new libretro API constant
2919 uint16_t button = input_state_cb(player_index,
2920 RETRO_DEVICE_ANALOG,
2921 RETRO_DEVICE_INDEX_ANALOG_BUTTON,
2922 id);
2923 button = MIN(button / 128, 255);
2924
2925 if (button == 0)
2926 {
2927 // If we got exactly zero, we're either not pressing the button, or the front-end
2928 // is not reporting analog values. We need to do a second check using the classic
2929 // digital API method, to at least get some response - better than nothing.
2930
2931 // NOTE: If we're really just not holding the button, we're still going to get zero.
2932
2933 button = (ret & (1 << id)) ? 255 : 0;
2934 }
2935
2936 return button;
2937}
2938
2939static unsigned char axis_range_modifier(int axis_value, bool is_square)
2940{
2941 int modifier_axis_range;
2942
2943 if (is_square)
2944 modifier_axis_range = roundf((axis_value >> 8) / 0.785f) + 128;
2945 else
2946 modifier_axis_range = (axis_value >> 8) + 128;
2947
2948 if (modifier_axis_range < 0)
2949 modifier_axis_range = 0;
2950 else if (modifier_axis_range > 255)
2951 modifier_axis_range = 255;
2952
2953 return modifier_axis_range;
2954}
2955
2956static void update_input_guncon(int port, int ret)
2957{
2958 //ToDo:
2959 //Separate pointer and lightgun control types
2960
2961 //Mouse range is -32767 -> 32767
2962 //1% is about 655
2963 //Use the left analog stick field to store the absolute coordinates
2964
2965 int gunx = input_state_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X);
2966 int guny = input_state_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y);
2967
2968 //Have the Libretro API let /libpcsxcore/plugins.c know when the lightgun is pointed offscreen
2969 //Offscreen value is chosen to be well out of range of any possible scaling done via core options
2970 if (input_state_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN) || input_state_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_RELOAD))
2971 {
2972 in_analog_left[port][0] = 65536;
2973 in_analog_left[port][1] = 65536;
2974 }
2975 else
2976 {
2977 in_analog_left[port][0] = ((gunx * GunconAdjustRatioX) + (GunconAdjustX * 655)) / 64 + 512;
2978 in_analog_left[port][1] = ((guny * GunconAdjustRatioY) + (GunconAdjustY * 655)) / 64 + 512;
2979 }
2980
2981 //GUNCON has 3 controls, Trigger,A,B which equal Circle,Start,Cross
2982
2983 // Trigger
2984 if (input_state_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_TRIGGER) || input_state_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_RELOAD))
2985 in_keystate[port] |= (1 << DKEY_CIRCLE);
2986
2987 // A
2988 if (input_state_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_AUX_A))
2989 in_keystate[port] |= (1 << DKEY_START);
2990
2991 // B
2992 if (input_state_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_AUX_B))
2993 in_keystate[port] |= (1 << DKEY_CROSS);
2994
2995}
2996
2997static void update_input_justifier(int port, int ret)
2998{
2999 //ToDo:
3000 //Separate pointer and lightgun control types
3001
3002 //RetroArch lightgun range is -32767 -> 32767 on both axes (positive Y is down)
3003
3004 //JUSTIFIER has 3 controls, Trigger,Special,Start which equal Square,Cross,Start
3005
3006 // Trigger
3007 if (input_state_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_TRIGGER) || input_state_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_RELOAD))
3008 in_keystate[port] |= (1 << DKEY_SQUARE);
3009
3010 // Special
3011 if (input_state_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_AUX_A))
3012 in_keystate[port] |= (1 << DKEY_CROSS);
3013
3014 // Start
3015 if (input_state_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_START))
3016 in_keystate[port] |= (1 << DKEY_START);
3017
3018}
3019
3020static void update_input_negcon(int port, int ret)
3021{
3022 int lsx;
3023 int rsy;
3024 int negcon_i_rs;
3025 int negcon_ii_rs;
3026 float negcon_twist_amplitude;
3027
3028 // Query digital inputs
3029 //
3030 // > Pad-Up
3031 if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_UP))
3032 in_keystate[port] |= (1 << DKEY_UP);
3033 // > Pad-Right
3034 if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_RIGHT))
3035 in_keystate[port] |= (1 << DKEY_RIGHT);
3036 // > Pad-Down
3037 if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_DOWN))
3038 in_keystate[port] |= (1 << DKEY_DOWN);
3039 // > Pad-Left
3040 if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_LEFT))
3041 in_keystate[port] |= (1 << DKEY_LEFT);
3042 // > Start
3043 if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_START))
3044 in_keystate[port] |= (1 << DKEY_START);
3045 // > neGcon A
3046 if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_A))
3047 in_keystate[port] |= (1 << DKEY_CIRCLE);
3048 // > neGcon B
3049 if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_X))
3050 in_keystate[port] |= (1 << DKEY_TRIANGLE);
3051 // > neGcon R shoulder (digital)
3052 if (ret & (1 << RETRO_DEVICE_ID_JOYPAD_R))
3053 in_keystate[port] |= (1 << DKEY_R1);
3054 // Query analog inputs
3055 //
3056 // From studying 'libpcsxcore/plugins.c' and 'frontend/plugin.c':
3057 // >> pad->leftJoyX == in_analog_left[port][0] == NeGcon II
3058 // >> pad->leftJoyY == in_analog_left[port][1] == NeGcon L
3059 // >> pad->rightJoyX == in_analog_right[port][0] == NeGcon twist
3060 // >> pad->rightJoyY == in_analog_right[port][1] == NeGcon I
3061 // So we just have to map in_analog_left/right to more
3062 // appropriate inputs...
3063 //
3064 // > NeGcon twist
3065 // >> Get raw analog stick value and account for deadzone
3066 lsx = input_state_cb(port, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X);
3067 if (lsx > negcon_deadzone)
3068 lsx = lsx - negcon_deadzone;
3069 else if (lsx < -negcon_deadzone)
3070 lsx = lsx + negcon_deadzone;
3071 else
3072 lsx = 0;
3073 // >> Convert to an 'amplitude' [-1.0,1.0] and adjust response
3074 negcon_twist_amplitude = (float)lsx / (float)(NEGCON_RANGE - negcon_deadzone);
3075 if (negcon_linearity == 2)
3076 {
3077 if (negcon_twist_amplitude < 0.0)
3078 negcon_twist_amplitude = -(negcon_twist_amplitude * negcon_twist_amplitude);
3079 else
3080 negcon_twist_amplitude = negcon_twist_amplitude * negcon_twist_amplitude;
3081 }
3082 else if (negcon_linearity == 3)
3083 negcon_twist_amplitude = negcon_twist_amplitude * negcon_twist_amplitude * negcon_twist_amplitude;
3084 // >> Convert to final 'in_analog' integer value [0,255]
3085 in_analog_right[port][0] = MAX(MIN((int)(negcon_twist_amplitude * 128.0f) + 128, 255), 0);
3086 // > NeGcon I + II
3087 // >> Handle right analog stick vertical axis mapping...
3088 // - Up (-Y) == accelerate == neGcon I
3089 // - Down (+Y) == brake == neGcon II
3090 negcon_i_rs = 0;
3091 negcon_ii_rs = 0;
3092 rsy = input_state_cb(port, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y);
3093 if (rsy >= 0)
3094 {
3095 // Account for deadzone
3096 // (Note: have never encountered a gamepad with significant differences
3097 // in deadzone between left/right analog sticks, so use the regular 'twist'
3098 // deadzone here)
3099 if (rsy > negcon_deadzone)
3100 rsy = rsy - negcon_deadzone;
3101 else
3102 rsy = 0;
3103 // Convert to 'in_analog' integer value [0,255]
3104 negcon_ii_rs = MIN((int)(((float)rsy / (float)(NEGCON_RANGE - negcon_deadzone)) * 255.0f), 255);
3105 }
3106 else
3107 {
3108 if (rsy < -negcon_deadzone)
3109 rsy = -1 * (rsy + negcon_deadzone);
3110 else
3111 rsy = 0;
3112 negcon_i_rs = MIN((int)(((float)rsy / (float)(NEGCON_RANGE - negcon_deadzone)) * 255.0f), 255);
3113 }
3114 // >> NeGcon I
3115 in_analog_right[port][1] = MAX(
3116 MAX(
3117 get_analog_button(ret, input_state_cb, port, RETRO_DEVICE_ID_JOYPAD_R2),
3118 get_analog_button(ret, input_state_cb, port, RETRO_DEVICE_ID_JOYPAD_B)),
3119 negcon_i_rs);
3120 // >> NeGcon II
3121 in_analog_left[port][0] = MAX(
3122 MAX(
3123 get_analog_button(ret, input_state_cb, port, RETRO_DEVICE_ID_JOYPAD_L2),
3124 get_analog_button(ret, input_state_cb, port, RETRO_DEVICE_ID_JOYPAD_Y)),
3125 negcon_ii_rs);
3126 // > NeGcon L
3127 in_analog_left[port][1] = get_analog_button(ret, input_state_cb, port, RETRO_DEVICE_ID_JOYPAD_L);
3128}
3129
3130static void update_input_mouse(int port, int ret)
3131{
3132 float raw_x = input_state_cb(port, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_X);
3133 float raw_y = input_state_cb(port, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_Y);
3134
3135 int x = (int)roundf(raw_x * mouse_sensitivity);
3136 int y = (int)roundf(raw_y * mouse_sensitivity);
3137
3138 if (x > 127) x = 127;
3139 else if (x < -128) x = -128;
3140
3141 if (y > 127) y = 127;
3142 else if (y < -128) y = -128;
3143
3144 in_mouse[port][0] = x; /* -128..+128 left/right movement, 0 = no movement */
3145 in_mouse[port][1] = y; /* -128..+128 down/up movement, 0 = no movement */
3146
3147 /* left mouse button state */
3148 if (input_state_cb(port, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT))
3149 in_keystate[port] |= 1 << 11;
3150
3151 /* right mouse button state */
3152 if (input_state_cb(port, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_RIGHT))
3153 in_keystate[port] |= 1 << 10;
3154}
3155
3156static void update_input(void)
3157{
3158 int i;
3159 int j;
3160
3161 // reset all keystate, query libretro for keystate
3162 for (i = 0; i < PORTS_NUMBER; i++)
3163 {
3164 int32_t ret = 0;
3165 int type = in_type[i];
3166
3167 in_keystate[i] = 0;
3168
3169 if (type == PSE_PAD_TYPE_NONE)
3170 continue;
3171
3172 if (libretro_supports_bitmasks)
3173 {
3174 ret = input_state_cb(i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK);
3175 // undo int16 sign extension (why input_state_cb returns int16 in the first place?)
3176 ret &= (1 << (RETRO_DEVICE_ID_JOYPAD_R3 + 1)) - 1;
3177 }
3178 else
3179 {
3180 for (j = 0; j < (RETRO_DEVICE_ID_JOYPAD_R3 + 1); j++)
3181 {
3182 if (input_state_cb(i, RETRO_DEVICE_JOYPAD, 0, j))
3183 ret |= (1 << j);
3184 }
3185 }
3186
3187 switch (type)
3188 {
3189 case PSE_PAD_TYPE_GUNCON:
3190 update_input_guncon(i, ret);
3191 break;
3192 case PSE_PAD_TYPE_GUN:
3193 update_input_justifier(i, ret);
3194 break;
3195 case PSE_PAD_TYPE_NEGCON:
3196 update_input_negcon(i, ret);
3197 break;
3198 case PSE_PAD_TYPE_MOUSE:
3199 update_input_mouse(i, ret);
3200 break;
3201 default:
3202 // dualshock ANALOG toggle?
3203 if (type == PSE_PAD_TYPE_ANALOGPAD && in_dualshock_analog_combo != 0
3204 && ret == in_dualshock_analog_combo)
3205 {
3206 if (!in_dualshock_toggling)
3207 {
3208 int state = padToggleAnalog(i);
3209 char msg[32];
3210 snprintf(msg, sizeof(msg), "ANALOG %s", state ? "ON" : "OFF");
3211 show_notification(msg, 800, 1);
3212 in_dualshock_toggling = true;
3213 }
3214 return;
3215 }
3216 in_dualshock_toggling = false;
3217
3218 // Set digital inputs
3219 for (j = 0; j < RETRO_PSX_MAP_LEN; j++)
3220 if (ret & (1 << j))
3221 in_keystate[i] |= retro_psx_map[j];
3222
3223 // Query analog inputs
3224 if (type == PSE_PAD_TYPE_ANALOGJOY || type == PSE_PAD_TYPE_ANALOGPAD)
3225 {
3226 in_analog_left[i][0] = axis_range_modifier(input_state_cb(i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X), axis_bounds_modifier);
3227 in_analog_left[i][1] = axis_range_modifier(input_state_cb(i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y), axis_bounds_modifier);
3228 in_analog_right[i][0] = axis_range_modifier(input_state_cb(i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X), axis_bounds_modifier);
3229 in_analog_right[i][1] = axis_range_modifier(input_state_cb(i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y), axis_bounds_modifier);
3230 }
3231 }
3232 }
3233}
3234
3235static void print_internal_fps(void)
3236{
3237 if (display_internal_fps)
3238 {
3239 static u32 fps, frame_count_s;
3240 static time_t last_time;
3241 static u32 psx_vsync_count;
3242 u32 psx_vsync_rate = is_pal_mode ? 50 : 60;
3243 time_t now;
3244
3245 psx_vsync_count++;
3246 frame_count_s++;
3247 now = time(NULL);
3248 if (now != last_time)
3249 {
3250 fps = frame_count_s;
3251 frame_count_s = 0;
3252 last_time = now;
3253 }
3254
3255 if (psx_vsync_count >= psx_vsync_rate)
3256 {
3257 int pos = 0, cd_count;
3258 char str[64];
3259
3260 if (display_internal_fps > 1) {
3261#if !defined(DRC_DISABLE) && !defined(LIGHTREC)
3262 if (ndrc_g.did_compile) {
3263 pos = snprintf(str, sizeof(str), "DRC: %d ", ndrc_g.did_compile);
3264 ndrc_g.did_compile = 0;
3265 }
3266#endif
3267 cd_count = cdra_get_buf_count();
3268 if (cd_count) {
3269 pos += snprintf(str + pos, sizeof(str) - pos, "CD: %2d/%d ",
3270 cdra_get_buf_cached_approx(), cd_count);
3271 }
3272 }
3273 snprintf(str + pos, sizeof(str) - pos, "FPS: %2d/%2d",
3274 pl_rearmed_cbs.flip_cnt, fps);
3275
3276 pl_rearmed_cbs.flip_cnt = 0;
3277 psx_vsync_count = 0;
3278
3279 if (msg_interface_version >= 1)
3280 {
3281 struct retro_message_ext msg = {
3282 str,
3283 3000,
3284 1,
3285 RETRO_LOG_INFO,
3286 RETRO_MESSAGE_TARGET_OSD,
3287 RETRO_MESSAGE_TYPE_STATUS,
3288 -1
3289 };
3290 environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE_EXT, &msg);
3291 }
3292 else
3293 {
3294 struct retro_message msg = {
3295 str,
3296 180
3297 };
3298 environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE, &msg);
3299 }
3300 }
3301 }
3302}
3303
3304void retro_run(void)
3305{
3306 //SysReset must be run while core is running,Not in menu (Locks up Retroarch)
3307 if (rebootemu != 0)
3308 {
3309 rebootemu = 0;
3310 SysReset();
3311 if (Config.HLE)
3312 LoadCdrom();
3313 }
3314
3315 set_vout_fb();
3316 print_internal_fps();
3317
3318 /* Check whether current frame should
3319 * be skipped */
3320 pl_rearmed_cbs.fskip_force = 0;
3321 pl_rearmed_cbs.fskip_dirty = 0;
3322
3323 if (frameskip_type != FRAMESKIP_NONE)
3324 {
3325 bool skip_frame = false;
3326
3327 switch (frameskip_type)
3328 {
3329 case FRAMESKIP_AUTO:
3330 skip_frame = retro_audio_buff_active && retro_audio_buff_underrun;
3331 break;
3332 case FRAMESKIP_AUTO_THRESHOLD:
3333 skip_frame = retro_audio_buff_active && (retro_audio_buff_occupancy < frameskip_threshold);
3334 break;
3335 case FRAMESKIP_FIXED_INTERVAL:
3336 skip_frame = true;
3337 break;
3338 default:
3339 break;
3340 }
3341
3342 if (skip_frame && frameskip_counter < frameskip_interval)
3343 pl_rearmed_cbs.fskip_force = 1;
3344 }
3345
3346 /* If frameskip/timing settings have changed,
3347 * update frontend audio latency
3348 * > Can do this before or after the frameskip
3349 * check, but doing it after means we at least
3350 * retain the current frame's audio output */
3351 if (update_audio_latency)
3352 {
3353 environ_cb(RETRO_ENVIRONMENT_SET_MINIMUM_AUDIO_LATENCY,
3354 &retro_audio_latency);
3355 update_audio_latency = false;
3356 }
3357
3358 input_poll_cb();
3359
3360 update_input();
3361
3362 bool updated = false;
3363 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated)
3364 update_variables(true);
3365
3366 psxRegs.stop = 0;
3367 psxCpu->Execute(&psxRegs);
3368
3369 if (pl_rearmed_cbs.fskip_dirty == 1) {
3370 if (frameskip_counter < frameskip_interval)
3371 frameskip_counter++;
3372 else if (frameskip_counter >= frameskip_interval || !pl_rearmed_cbs.fskip_force)
3373 frameskip_counter = 0;
3374 }
3375
3376 video_cb((vout_fb_dirty || !vout_can_dupe) ? vout_buf_ptr : NULL,
3377 vout_width, vout_height, vout_pitch_b);
3378 vout_fb_dirty = 0;
3379
3380#ifdef HAVE_CDROM
3381 int inserted;
3382 if (cdra_check_eject(&inserted) > 0) {
3383 bool media_inserted = inserted != 0;
3384 if (!media_inserted != disk_ejected)
3385 disk_set_eject_state(!media_inserted);
3386 }
3387#endif
3388}
3389
3390static bool try_use_bios(const char *path, bool preferred_only)
3391{
3392 long size;
3393 const char *name;
3394 FILE *fp = fopen(path, "rb");
3395 if (fp == NULL)
3396 return false;
3397
3398 fseek(fp, 0, SEEK_END);
3399 size = ftell(fp);
3400 fclose(fp);
3401
3402 name = strrchr(path, SLASH);
3403 if (name++ == NULL)
3404 name = path;
3405
3406 if (preferred_only && size != 512 * 1024)
3407 return false;
3408 if (size != 512 * 1024 && size != 4 * 1024 * 1024)
3409 return false;
3410 if (strstr(name, "unirom"))
3411 return false;
3412 // jp bios have an addidional region check
3413 if (preferred_only && (strcasestr(name, "00.") || strcasestr(name, "j.bin")))
3414 return false;
3415
3416 snprintf(Config.Bios, sizeof(Config.Bios), "%s", name);
3417 return true;
3418}
3419
3420#ifndef VITA
3421#include <sys/types.h>
3422#include <dirent.h>
3423
3424static bool find_any_bios(const char *dirpath, char *path, size_t path_size)
3425{
3426 static const char *substr_pref[] = { "scph", "ps" };
3427 static const char *substr_alt[] = { "scph", "ps", "openbios" };
3428 DIR *dir;
3429 struct dirent *ent;
3430 bool ret = false;
3431 size_t i;
3432
3433 dir = opendir(dirpath);
3434 if (dir == NULL)
3435 return false;
3436
3437 // try to find a "better" bios
3438 while ((ent = readdir(dir)))
3439 {
3440 for (i = 0; i < sizeof(substr_pref) / sizeof(substr_pref[0]); i++)
3441 {
3442 const char *substr = substr_pref[i];
3443 if ((strncasecmp(ent->d_name, substr, strlen(substr)) != 0))
3444 continue;
3445 snprintf(path, path_size, "%s%c%s", dirpath, SLASH, ent->d_name);
3446 ret = try_use_bios(path, true);
3447 if (ret)
3448 goto finish;
3449 }
3450 }
3451
3452 // another pass to look for anything fitting, even ps2 bios
3453 rewinddir(dir);
3454 while ((ent = readdir(dir)))
3455 {
3456 for (i = 0; i < sizeof(substr_alt) / sizeof(substr_alt[0]); i++)
3457 {
3458 const char *substr = substr_alt[i];
3459 if ((strncasecmp(ent->d_name, substr, strlen(substr)) != 0))
3460 continue;
3461 snprintf(path, path_size, "%s%c%s", dirpath, SLASH, ent->d_name);
3462 ret = try_use_bios(path, false);
3463 if (ret)
3464 goto finish;
3465 }
3466 }
3467
3468
3469finish:
3470 closedir(dir);
3471 return ret;
3472}
3473#else
3474#define find_any_bios(...) false
3475#endif
3476
3477static void check_system_specs(void)
3478{
3479 unsigned level = 6;
3480 environ_cb(RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL, &level);
3481}
3482
3483static int init_memcards(void)
3484{
3485 int ret = 0;
3486 const char *dir;
3487 struct retro_variable var = { .key = "pcsx_rearmed_memcard2", .value = NULL };
3488 static const char CARD2_FILE[] = "pcsx-card2.mcd";
3489
3490 // Memcard2 will be handled and is re-enabled if needed using core
3491 // operations.
3492 // Memcard1 is handled by libretro, doing this will set core to
3493 // skip file io operations for memcard1 like SaveMcd
3494 snprintf(Config.Mcd1, sizeof(Config.Mcd1), "none");
3495 snprintf(Config.Mcd2, sizeof(Config.Mcd2), "none");
3496 init_memcard(Mcd1Data);
3497 // Memcard 2 is managed by the emulator on the filesystem,
3498 // There is no need to initialize Mcd2Data like Mcd1Data.
3499
3500 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
3501 {
3502 SysPrintf("Memcard 2: %s\n", var.value);
3503 if (memcmp(var.value, "enabled", 7) == 0)
3504 {
3505 if (environ_cb(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, &dir) && dir)
3506 {
3507 if (strlen(dir) + strlen(CARD2_FILE) + 2 > sizeof(Config.Mcd2))
3508 {
3509 LogErr("Path '%s' is too long. Cannot use memcard 2. Use a shorter path.\n", dir);
3510 ret = -1;
3511 }
3512 else
3513 {
3514 McdDisable[1] = 0;
3515 snprintf(Config.Mcd2, sizeof(Config.Mcd2), "%s/%s", dir, CARD2_FILE);
3516 SysPrintf("Use memcard 2: %s\n", Config.Mcd2);
3517 }
3518 }
3519 else
3520 {
3521 LogErr("Could not get save directory! Could not create memcard 2.");
3522 ret = -1;
3523 }
3524 }
3525 }
3526 return ret;
3527}
3528
3529static void loadPSXBios(void)
3530{
3531 const char *dir;
3532 char path[PATH_MAX];
3533 unsigned useHLE = 0;
3534
3535 const char *bios[] = {
3536 "PSXONPSP660", "psxonpsp660",
3537 "SCPH101", "scph101",
3538 "SCPH5501", "scph5501",
3539 "SCPH7001", "scph7001",
3540 "SCPH1001", "scph1001"
3541 };
3542
3543 struct retro_variable var = {
3544 .key = "pcsx_rearmed_bios",
3545 .value = NULL
3546 };
3547
3548 found_bios = 0;
3549
3550 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
3551 {
3552 if (!strcmp(var.value, "HLE"))
3553 useHLE = 1;
3554 }
3555
3556 if (!useHLE)
3557 {
3558 if (environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir) && dir)
3559 {
3560 unsigned i;
3561 snprintf(Config.BiosDir, sizeof(Config.BiosDir), "%s", dir);
3562
3563 for (i = 0; i < sizeof(bios) / sizeof(bios[0]); i++)
3564 {
3565 snprintf(path, sizeof(path), "%s%c%s.bin", dir, SLASH, bios[i]);
3566 found_bios = try_use_bios(path, true);
3567 if (found_bios)
3568 break;
3569 }
3570
3571 if (!found_bios)
3572 found_bios = find_any_bios(dir, path, sizeof(path));
3573 }
3574 if (found_bios)
3575 {
3576 SysPrintf("found BIOS file: %s\n", Config.Bios);
3577 }
3578 }
3579
3580 if (!found_bios)
3581 {
3582 const char *msg_str;
3583 unsigned duration;
3584 if (useHLE)
3585 {
3586 msg_str = "BIOS set to \'hle\'";
3587 SysPrintf("Using HLE BIOS.\n");
3588 // shorter as the user probably intentionally wants to use HLE
3589 duration = 700;
3590 }
3591 else
3592 {
3593 msg_str = "No PlayStation BIOS file found - add for better compatibility";
3594 SysPrintf("No BIOS files found.\n");
3595 duration = 3000;
3596 }
3597 show_notification(msg_str, duration, 2);
3598 }
3599}
3600
3601void retro_init(void)
3602{
3603 unsigned dci_version = 0;
3604 struct retro_rumble_interface rumble;
3605 int ret;
3606
3607 log_mem_usage();
3608
3609 msg_interface_version = 0;
3610 environ_cb(RETRO_ENVIRONMENT_GET_MESSAGE_INTERFACE_VERSION, &msg_interface_version);
3611
3612#if defined(__MACH__) && !defined(TVOS)
3613 // magic sauce to make the dynarec work on iOS
3614 syscall(SYS_ptrace, 0 /*PTRACE_TRACEME*/, 0, 0, 0);
3615#endif
3616
3617#if defined(_3DS)
3618 psxMapHook = pl_3ds_mmap;
3619 psxUnmapHook = pl_3ds_munmap;
3620#elif defined(HAVE_LIBNX)
3621 psxMapHook = pl_switch_mmap;
3622 psxUnmapHook = pl_switch_munmap;
3623#elif defined(VITA)
3624 if (init_vita_mmap() < 0)
3625 abort();
3626 psxMapHook = pl_vita_mmap;
3627 psxUnmapHook = pl_vita_munmap;
3628#endif
3629 ret = emu_core_preinit();
3630#ifdef _3DS
3631 /* emu_core_preinit sets the cpu to dynarec */
3632 if (!__ctr_svchax)
3633 Config.Cpu = CPU_INTERPRETER;
3634#endif
3635 ret |= init_memcards();
3636
3637 ret |= emu_core_init();
3638 if (ret != 0)
3639 {
3640 LogErr("PCSX init failed.\n");
3641 exit(1);
3642 }
3643
3644 // alloc enough for RETRO_PIXEL_FORMAT_XRGB8888
3645 size_t vout_buf_size = VOUT_MAX_WIDTH * VOUT_MAX_HEIGHT * 4;
3646#ifdef _3DS
3647 // Place psx vram in linear mem to take advantage of it's supersection mapping.
3648 // The emu allocs 2x (0x201000 to be exact) but doesn't really need that much,
3649 // so place vout_buf below to also act as an overdraw guard.
3650 vram_mem = linearMemAlign(1024*1024 + 4096 + vout_buf_size, 4096);
3651 if (vram_mem) {
3652 vout_buf = (char *)vram_mem + 1024*1024 + 4096;
3653 if (__ctr_svchax)
3654 SysPrintf("vram: %p PA %08x tlb %08x\n", vram_mem,
3655 svcConvertVAToPA(vram_mem, 0), ctr_get_tlbe(vram_mem));
3656 }
3657#elif defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L) && P_HAVE_POSIX_MEMALIGN
3658 if (posix_memalign(&vout_buf, 16, vout_buf_size) != 0)
3659 vout_buf = NULL;
3660 else
3661 memset(vout_buf, 0, vout_buf_size);
3662#else
3663 vout_buf = calloc(vout_buf_size, 1);
3664#endif
3665 if (vout_buf == NULL)
3666 {
3667 LogErr("OOM for vout_buf.\n");
3668 // may be able to continue if we get retro_framebuffer access
3669 }
3670
3671 vout_buf_ptr = vout_buf;
3672
3673 loadPSXBios();
3674
3675 environ_cb(RETRO_ENVIRONMENT_GET_CAN_DUPE, &vout_can_dupe);
3676 if (!vout_can_dupe)
3677 LogWarn("CAN_DUPE reports false\n");
3678
3679 disk_initial_index = 0;
3680 disk_initial_path[0] = '\0';
3681 if (environ_cb(RETRO_ENVIRONMENT_GET_DISK_CONTROL_INTERFACE_VERSION, &dci_version) && (dci_version >= 1))
3682 environ_cb(RETRO_ENVIRONMENT_SET_DISK_CONTROL_EXT_INTERFACE, &disk_control_ext);
3683 else
3684 environ_cb(RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE, &disk_control);
3685
3686 rumble_cb = NULL;
3687 if (environ_cb(RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE, &rumble))
3688 rumble_cb = rumble.set_rumble_state;
3689
3690 pl_rearmed_cbs.gpu_peops.dwActFixes = GPU_PEOPS_OLD_FRAME_SKIP;
3691
3692 SaveFuncs.open = save_open;
3693 SaveFuncs.read = save_read;
3694 SaveFuncs.write = save_write;
3695 SaveFuncs.seek = save_seek;
3696 SaveFuncs.close = save_close;
3697
3698 if (environ_cb(RETRO_ENVIRONMENT_GET_INPUT_BITMASKS, NULL))
3699 libretro_supports_bitmasks = true;
3700
3701 check_system_specs();
3702}
3703
3704void retro_deinit(void)
3705{
3706 if (plugins_opened)
3707 {
3708 ClosePlugins();
3709 plugins_opened = 0;
3710 }
3711 SysClose();
3712#ifdef _3DS
3713 linearFree(vram_mem);
3714 vram_mem = NULL;
3715#else
3716 free(vout_buf);
3717#endif
3718 vout_buf = NULL;
3719
3720#ifdef VITA
3721 deinit_vita_mmap();
3722#endif
3723 libretro_supports_bitmasks = false;
3724 libretro_supports_option_categories = false;
3725
3726 show_input_settings = true;
3727#ifdef GPU_PEOPS
3728 show_advanced_gpu_peops_settings = true;
3729#endif
3730#ifdef GPU_UNAI
3731 show_advanced_gpu_unai_settings = true;
3732#endif
3733
3734 /* Have to reset disks struct, otherwise
3735 * fnames/flabels will leak memory */
3736 disk_init();
3737 frameskip_type = FRAMESKIP_NONE;
3738 frameskip_threshold = 0;
3739 frameskip_interval = 0;
3740 frameskip_counter = 0;
3741 retro_audio_buff_active = false;
3742 retro_audio_buff_occupancy = 0;
3743 retro_audio_buff_underrun = false;
3744 retro_audio_latency = 0;
3745 update_audio_latency = false;
3746}
3747
3748void SysPrintf(const char *fmt, ...)
3749{
3750 va_list list;
3751 char msg[512];
3752
3753 va_start(list, fmt);
3754 vsprintf(msg, fmt, list);
3755 va_end(list);
3756
3757 if (log_cb)
3758 log_cb(RETRO_LOG_INFO, "%s", msg);
3759}
3760
3761/* Prints debug-level logs */
3762void SysDLog(const char *fmt, ...)
3763{
3764 va_list list;
3765 char msg[512];
3766
3767 va_start(list, fmt);
3768 vsprintf(msg, fmt, list);
3769 va_end(list);
3770
3771 if (log_cb)
3772 log_cb(RETRO_LOG_DEBUG, "%s", msg);
3773}
3774
3775// vim:sw=3:ts=3:expandtab