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