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