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