partially revert c4052f4d79cf
[pcsx_rearmed.git] / frontend / libretro.c
1 /*
2  * (C) notaz, 2012,2014,2015
3  *
4  * This work is licensed under the terms of the GNU GPLv2 or later.
5  * See the COPYING file in the top-level directory.
6  */
7
8 #define _GNU_SOURCE 1 // strcasestr
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <strings.h>
13
14 #include "../libpcsxcore/misc.h"
15 #include "../libpcsxcore/psxcounters.h"
16 #include "../libpcsxcore/psxmem_map.h"
17 #include "../libpcsxcore/new_dynarec/new_dynarec.h"
18 #include "../libpcsxcore/cdrom.h"
19 #include "../libpcsxcore/cdriso.h"
20 #include "../libpcsxcore/cheat.h"
21 #include "../plugins/dfsound/out.h"
22 #include "../plugins/dfsound/spu_config.h"
23 #include "../plugins/dfinput/externals.h"
24 #include "cspace.h"
25 #include "main.h"
26 #include "plugin.h"
27 #include "plugin_lib.h"
28 #include "arm_features.h"
29 #include "revision.h"
30 #include "libretro.h"
31
32 #ifdef _3DS
33 #include "3ds/3ds_utils.h"
34 #endif
35
36 #define PORTS_NUMBER 8
37
38 static retro_video_refresh_t video_cb;
39 static retro_input_poll_t input_poll_cb;
40 static retro_input_state_t input_state_cb;
41 static retro_environment_t environ_cb;
42 static retro_audio_sample_batch_t audio_batch_cb;
43 static struct retro_rumble_interface rumble;
44
45 static void *vout_buf;
46 static int vout_width, vout_height;
47 static int vout_doffs_old, vout_fb_dirty;
48 static bool vout_can_dupe;
49 static bool duping_enable;
50
51 static int plugins_opened;
52 static int is_pal_mode;
53
54 /* memory card data */
55 extern char Mcd1Data[MCD_SIZE];
56 extern char McdDisable[2];
57
58 /* PCSX ReARMed core calls and stuff */
59 int in_type[8] =  { PSE_PAD_TYPE_NONE, PSE_PAD_TYPE_NONE,
60                   PSE_PAD_TYPE_NONE, PSE_PAD_TYPE_NONE,
61                   PSE_PAD_TYPE_NONE, PSE_PAD_TYPE_NONE,
62                   PSE_PAD_TYPE_NONE, PSE_PAD_TYPE_NONE };
63 int in_analog_left[8][2] = {{ 127, 127 },{ 127, 127 },{ 127, 127 },{ 127, 127 },{ 127, 127 },{ 127, 127 },{ 127, 127 },{ 127, 127 }};
64 int in_analog_right[8][2] = {{ 127, 127 },{ 127, 127 },{ 127, 127 },{ 127, 127 },{ 127, 127 },{ 127, 127 },{ 127, 127 },{ 127, 127 }};
65 unsigned short in_keystate[PORTS_NUMBER];
66 int multitap1 = 0;
67 int multitap2 = 0;
68 int in_enable_vibration = 1;
69
70 /* PSX max resolution is 640x512, but with enhancement it's 1024x512 */
71 #define VOUT_MAX_WIDTH 1024
72 #define VOUT_MAX_HEIGHT 512
73
74 static void init_memcard(char *mcd_data)
75 {
76         unsigned off = 0;
77         unsigned i;
78
79         memset(mcd_data, 0, MCD_SIZE);
80
81         mcd_data[off++] = 'M';
82         mcd_data[off++] = 'C';
83         off += 0x7d;
84         mcd_data[off++] = 0x0e;
85
86         for (i = 0; i < 15; i++) {
87                 mcd_data[off++] = 0xa0;
88                 off += 0x07;
89                 mcd_data[off++] = 0xff;
90                 mcd_data[off++] = 0xff;
91                 off += 0x75;
92                 mcd_data[off++] = 0xa0;
93         }
94
95         for (i = 0; i < 20; i++) {
96                 mcd_data[off++] = 0xff;
97                 mcd_data[off++] = 0xff;
98                 mcd_data[off++] = 0xff;
99                 mcd_data[off++] = 0xff;
100                 off += 0x04;
101                 mcd_data[off++] = 0xff;
102                 mcd_data[off++] = 0xff;
103                 off += 0x76;
104         }
105 }
106
107 static int vout_open(void)
108 {
109         return 0;
110 }
111
112 static void vout_set_mode(int w, int h, int raw_w, int raw_h, int bpp)
113 {
114         vout_width = w;
115         vout_height = h;
116 }
117
118 #ifndef FRONTEND_SUPPORTS_RGB565
119 static void convert(void *buf, size_t bytes)
120 {
121         unsigned int i, v, *p = buf;
122
123         for (i = 0; i < bytes / 4; i++) {
124                 v = p[i];
125                 p[i] = (v & 0x001f001f) | ((v >> 1) & 0x7fe07fe0);
126         }
127 }
128 #endif
129
130 static void vout_flip(const void *vram, int stride, int bgr24, int w, int h)
131 {
132         unsigned short *dest = vout_buf;
133         const unsigned short *src = vram;
134         int dstride = vout_width, h1 = h;
135         int doffs;
136
137         if (vram == NULL) {
138                 // blanking
139                 memset(vout_buf, 0, dstride * h * 2);
140                 goto out;
141         }
142
143         doffs = (vout_height - h) * dstride;
144         doffs += (dstride - w) / 2 & ~1;
145         if (doffs != vout_doffs_old) {
146                 // clear borders
147                 memset(vout_buf, 0, dstride * h * 2);
148                 vout_doffs_old = doffs;
149         }
150         dest += doffs;
151
152         if (bgr24)
153         {
154                 // XXX: could we switch to RETRO_PIXEL_FORMAT_XRGB8888 here?
155                 for (; h1-- > 0; dest += dstride, src += stride)
156                 {
157                         bgr888_to_rgb565(dest, src, w * 3);
158                 }
159         }
160         else
161         {
162                 for (; h1-- > 0; dest += dstride, src += stride)
163                 {
164                         bgr555_to_rgb565(dest, src, w * 2);
165                 }
166         }
167
168 out:
169 #ifndef FRONTEND_SUPPORTS_RGB565
170         convert(vout_buf, vout_width * vout_height * 2);
171 #endif
172         vout_fb_dirty = 1;
173         pl_rearmed_cbs.flip_cnt++;
174 }
175
176 static void vout_close(void)
177 {
178 }
179
180 #ifdef _3DS
181 typedef struct
182 {
183    void* buffer;
184    uint32_t target_map;
185    size_t size;
186    enum psxMapTag tag;
187 }psx_map_t;
188
189 psx_map_t custom_psx_maps[] = {
190    {NULL, 0x13000000, 0x210000, MAP_TAG_RAM},   // 0x80000000
191    {NULL, 0x12800000, 0x010000, MAP_TAG_OTHER}, // 0x1f800000
192    {NULL, 0x12c00000, 0x080000, MAP_TAG_OTHER}, // 0x1fc00000
193    {NULL, 0x11000000, 0x800000, MAP_TAG_LUTS},  // 0x08000000
194    {NULL, 0x12000000, 0x200000, MAP_TAG_VRAM},  // 0x00000000
195 };
196
197 void* pl_3ds_mmap(unsigned long addr, size_t size, int is_fixed,
198         enum psxMapTag tag)
199 {
200    (void)is_fixed;
201    (void)addr;
202
203    if (__ctr_svchax)
204    {
205       psx_map_t* custom_map = custom_psx_maps;
206
207       for (; custom_map->size; custom_map++)
208       {
209          if ((custom_map->size == size) && (custom_map->tag == tag))
210          {
211             uint32_t ptr_aligned, tmp;
212
213             custom_map->buffer = malloc(size + 0x1000);
214             ptr_aligned = (((u32)custom_map->buffer) + 0xFFF) & ~0xFFF;
215
216             if(svcControlMemory(&tmp, (void*)custom_map->target_map, (void*)ptr_aligned, size, MEMOP_MAP, 0x3) < 0)
217             {
218                SysPrintf("could not map memory @0x%08X\n", custom_map->target_map);
219                exit(1);
220             }
221
222             return (void*)custom_map->target_map;
223          }
224       }
225    }
226
227    return malloc(size);
228 }
229
230 void pl_3ds_munmap(void *ptr, size_t size, enum psxMapTag tag)
231 {
232    (void)tag;
233
234    if (__ctr_svchax)
235    {
236       psx_map_t* custom_map = custom_psx_maps;
237
238       for (; custom_map->size; custom_map++)
239       {
240          if ((custom_map->target_map == (uint32_t)ptr))
241          {
242             uint32_t ptr_aligned, tmp;
243
244             ptr_aligned = (((u32)custom_map->buffer) + 0xFFF) & ~0xFFF;
245
246             svcControlMemory(&tmp, (void*)custom_map->target_map, (void*)ptr_aligned, size, MEMOP_UNMAP, 0x3);
247
248             free(custom_map->buffer);
249             custom_map->buffer = NULL;
250             return;
251          }
252       }
253    }
254
255    free(ptr);
256 }
257 #endif
258
259 #ifdef VITA
260 typedef struct
261 {
262    void* buffer;
263    uint32_t target_map;
264    size_t size;
265    enum psxMapTag tag;
266 }psx_map_t;
267
268 psx_map_t custom_psx_maps[] = {
269    {NULL, NULL, 0x210000, MAP_TAG_RAM},   // 0x80000000
270    {NULL, NULL, 0x010000, MAP_TAG_OTHER}, // 0x1f800000
271    {NULL, NULL, 0x080000, MAP_TAG_OTHER}, // 0x1fc00000
272    {NULL, NULL, 0x800000, MAP_TAG_LUTS},  // 0x08000000
273    {NULL, NULL, 0x200000, MAP_TAG_VRAM},  // 0x00000000
274 };
275
276 void* pl_vita_mmap(unsigned long addr, size_t size, int is_fixed,
277         enum psxMapTag tag)
278 {
279    (void)is_fixed;
280    (void)addr;
281
282
283     psx_map_t* custom_map = custom_psx_maps;
284
285     for (; custom_map->size; custom_map++)
286     {
287        if ((custom_map->size == size) && (custom_map->tag == tag))
288        {
289           int block, ret;
290           char blockname[32];
291           sprintf(blockname, "CODE 0x%08X",tag);
292
293           block = sceKernelAllocMemBlock(blockname, size + 0x1000);
294           if(block<=0){
295             sceClibPrintf("could not alloc mem block @0x%08X 0x%08X \n", block, tag);
296             exit(1);
297           }
298
299           // get base address
300           ret = sceKernelGetMemBlockBase(block, &custom_map->buffer);
301           if (ret < 0)
302           {
303             sceClibPrintf("could get address @0x%08X 0x%08X 0x%08X \n", block, ret, tag);
304             exit(1);
305           }
306           custom_map->buffer = (((u32)custom_map->buffer) + 0xFFF) & ~0xFFF;
307           custom_map->target_map = block;
308
309           return custom_map->buffer;
310        }
311     }
312
313
314    return malloc(size);
315 }
316
317 void pl_vita_munmap(void *ptr, size_t size, enum psxMapTag tag)
318 {
319    (void)tag;
320
321    psx_map_t* custom_map = custom_psx_maps;
322
323   for (; custom_map->size; custom_map++)
324   {
325      if ((custom_map->buffer == ptr))
326      {
327         sceKernelFreeMemBlock(custom_map->target_map);
328         custom_map->buffer = NULL;
329         custom_map->target_map = NULL;
330         return;
331      }
332   }
333
334    free(ptr);
335 }
336 #endif
337
338 static void *pl_mmap(unsigned int size)
339 {
340         return psxMap(0, size, 0, MAP_TAG_VRAM);
341 }
342
343 static void pl_munmap(void *ptr, unsigned int size)
344 {
345         psxUnmap(ptr, size, MAP_TAG_VRAM);
346 }
347
348 struct rearmed_cbs pl_rearmed_cbs = {
349         .pl_vout_open = vout_open,
350         .pl_vout_set_mode = vout_set_mode,
351         .pl_vout_flip = vout_flip,
352         .pl_vout_close = vout_close,
353         .mmap = pl_mmap,
354         .munmap = pl_munmap,
355         /* from psxcounters */
356         .gpu_hcnt = &hSyncCount,
357         .gpu_frame_count = &frame_counter,
358 };
359
360 void pl_frame_limit(void)
361 {
362         /* called once per frame, make psxCpu->Execute() above return */
363         stop = 1;
364 }
365
366 void pl_timing_prepare(int is_pal)
367 {
368         is_pal_mode = is_pal;
369 }
370
371 void plat_trigger_vibrate(int pad, int low, int high)
372 {
373     rumble.set_rumble_state(pad, RETRO_RUMBLE_STRONG, high << 8);
374     rumble.set_rumble_state(pad, RETRO_RUMBLE_WEAK, low ? 0xffff : 0x0);
375 }
376
377 void pl_update_gun(int *xn, int *yn, int *xres, int *yres, int *in)
378 {
379 }
380
381 /* sound calls */
382 static int snd_init(void)
383 {
384         return 0;
385 }
386
387 static void snd_finish(void)
388 {
389 }
390
391 static int snd_busy(void)
392 {
393         return 0;
394 }
395
396 static void snd_feed(void *buf, int bytes)
397 {
398         if (audio_batch_cb != NULL)
399                 audio_batch_cb(buf, bytes / 4);
400 }
401
402 void out_register_libretro(struct out_driver *drv)
403 {
404         drv->name = "libretro";
405         drv->init = snd_init;
406         drv->finish = snd_finish;
407         drv->busy = snd_busy;
408         drv->feed = snd_feed;
409 }
410
411 /* libretro */
412 void retro_set_environment(retro_environment_t cb)
413 {
414    static const struct retro_variable vars[] = {
415       { "pcsx_rearmed_frameskip", "Frameskip; 0|1|2|3" },
416       { "pcsx_rearmed_region", "Region; Auto|NTSC|PAL" },
417       { "pcsx_rearmed_pad1type", "Pad 1 Type; default|none|standard|analog|negcon" },
418       { "pcsx_rearmed_pad2type", "Pad 2 Type; default|none|standard|analog|negcon" },
419       { "pcsx_rearmed_pad3type", "Pad 3 Type; default|none|standard|analog|negcon" },
420       { "pcsx_rearmed_pad4type", "Pad 4 Type; default|none|standard|analog|negcon" },
421       { "pcsx_rearmed_pad5type", "Pad 5 Type; default|none|standard|analog|negcon" },
422       { "pcsx_rearmed_pad6type", "Pad 6 Type; default|none|standard|analog|negcon" },
423       { "pcsx_rearmed_pad7type", "Pad 7 Type; default|none|standard|analog|negcon" },
424       { "pcsx_rearmed_pad8type", "Pad 8 Type; default|none|standard|analog|negcon" },
425       { "pcsx_rearmed_multitap1", "Multitap 1; auto|disabled|enabled" },
426       { "pcsx_rearmed_multitap2", "Multitap 2; auto|disabled|enabled" },
427 #ifndef DRC_DISABLE
428       { "pcsx_rearmed_drc", "Dynamic recompiler; enabled|disabled" },
429 #endif
430 #ifdef __ARM_NEON__
431       { "pcsx_rearmed_neon_interlace_enable", "Enable interlacing mode(s); disabled|enabled" },
432       { "pcsx_rearmed_neon_enhancement_enable", "Enhanced resolution (slow); disabled|enabled" },
433       { "pcsx_rearmed_neon_enhancement_no_main", "Enhanced resolution speed hack; disabled|enabled" },
434 #endif
435       { "pcsx_rearmed_duping_enable", "Frame duping; on|off" },
436       { "pcsx_rearmed_spu_reverb", "Sound: Reverb; on|off" },
437       { "pcsx_rearmed_spu_interpolation", "Sound: Interpolation; simple|gaussian|cubic|off" },
438       { "pcsx_rearmed_pe2_fix", "Parasite Eve 2/Vandal Hearts 1/2 Fix; disabled|enabled" },
439       { "pcsx_rearmed_inuyasha_fix", "InuYasha Sengoku Battle Fix; disabled|enabled" },
440       { NULL, NULL },
441    };
442
443    environ_cb = cb;
444
445    cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void*)vars);
446 }
447
448 void retro_set_video_refresh(retro_video_refresh_t cb) { video_cb = cb; }
449 void retro_set_audio_sample(retro_audio_sample_t cb) { (void)cb; }
450 void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) { audio_batch_cb = cb; }
451 void retro_set_input_poll(retro_input_poll_t cb) { input_poll_cb = cb; }
452 void retro_set_input_state(retro_input_state_t cb) { input_state_cb = cb; }
453
454 unsigned retro_api_version(void)
455 {
456         return RETRO_API_VERSION;
457 }
458
459 static int controller_port_variable(unsigned port, struct retro_variable *var)
460 {
461         if (port >= PORTS_NUMBER)
462                 return 0;
463
464         if (!environ_cb)
465                 return 0;
466
467         var->value = NULL;
468         switch (port) {
469         case 0:
470                 var->key = "pcsx_rearmed_pad1type";
471                 break;
472         case 1:
473                 var->key = "pcsx_rearmed_pad2type";
474                 break;
475         case 2:
476                 var->key = "pcsx_rearmed_pad3type";
477                 break;
478         case 3:
479                 var->key = "pcsx_rearmed_pad4type";
480                 break;
481         case 4:
482                 var->key = "pcsx_rearmed_pad5type";
483                 break;
484         case 5:
485                 var->key = "pcsx_rearmed_pad6type";
486                 break;
487         case 6:
488                 var->key = "pcsx_rearmed_pad7type";
489                 break;
490         case 7:
491                 var->key = "pcsx_rearmed_pad8type";
492                 break;
493         }
494
495         return environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, var) || var->value;
496 }
497
498 static void update_controller_port_variable(unsigned port)
499 {
500         if (port >= PORTS_NUMBER)
501                 return;
502
503         struct retro_variable var;
504
505         if (controller_port_variable(port, &var))
506         {
507                 if (strcmp(var.value, "standard") == 0)
508                         in_type[0] = PSE_PAD_TYPE_STANDARD;
509                 else if (strcmp(var.value, "analog") == 0)
510                         in_type[0] = PSE_PAD_TYPE_ANALOGPAD;
511                 else if (strcmp(var.value, "negcon") == 0)
512                         in_type[0] = PSE_PAD_TYPE_NEGCON;
513                 else if (strcmp(var.value, "none") == 0)
514                         in_type[0] = PSE_PAD_TYPE_NONE;
515                 // else 'default' case, do nothing
516         }
517 }
518
519 static void update_controller_port_device(unsigned port, unsigned device)
520 {
521         if (port >= PORTS_NUMBER)
522                 return;
523
524         struct retro_variable var;
525
526         if (!controller_port_variable(port, &var))
527                 return;
528
529         if (strcmp(var.value, "default") != 0)
530                 return;
531
532         switch (device)
533         {
534         case RETRO_DEVICE_JOYPAD:
535                 in_type[port] = PSE_PAD_TYPE_STANDARD;
536                 break;
537         case RETRO_DEVICE_ANALOG:
538                 in_type[port] = PSE_PAD_TYPE_ANALOGPAD;
539                 break;
540         case RETRO_DEVICE_MOUSE:
541                 in_type[port] = PSE_PAD_TYPE_MOUSE;
542                 break;
543         case RETRO_DEVICE_LIGHTGUN:
544                 in_type[port] = PSE_PAD_TYPE_GUN;
545                 break;
546         case RETRO_DEVICE_NONE:
547         default:
548                 in_type[port] = PSE_PAD_TYPE_NONE;
549         }
550 }
551
552 static void update_multitap()
553 {
554         struct retro_variable var;
555         int auto_case, port;
556
557         var.value = NULL;
558         var.key = "pcsx_rearmed_multitap1";
559         auto_case = 0;
560         if (environ_cb && (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value))
561         {
562                 if (strcmp(var.value, "enabled") == 0)
563                         multitap1 = 1;
564                 else if (strcmp(var.value, "disabled") == 0)
565                         multitap1 = 0;
566                 else // 'auto' case
567                         auto_case = 1;
568         }
569         else
570                 auto_case = 1;
571
572         if (auto_case)
573         {
574                 // If a gamepad is plugged after port 2, we need a first multitap.
575                 multitap1 = 0;
576                 for (port = 2; port < PORTS_NUMBER; port++)
577                         multitap1 |= in_type[port] != PSE_PAD_TYPE_NONE;
578         }
579
580         var.value = NULL;
581         var.key = "pcsx_rearmed_multitap2";
582         auto_case = 0;
583         if (environ_cb && (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value))
584         {
585                 if (strcmp(var.value, "enabled") == 0)
586                         multitap2 = 1;
587                 else if (strcmp(var.value, "disabled") == 0)
588                         multitap2 = 0;
589                 else // 'auto' case
590                         auto_case = 1;
591         }
592         else
593                 auto_case = 1;
594
595         if (auto_case)
596         {
597                 // If a gamepad is plugged after port 4, we need a second multitap.
598                 multitap2 = 0;
599                 for (port = 4; port < PORTS_NUMBER; port++)
600                         multitap2 |= in_type[port] != PSE_PAD_TYPE_NONE;
601         }
602 }
603
604 void retro_set_controller_port_device(unsigned port, unsigned device)
605 {
606         SysPrintf("port %u  device %u",port,device);
607
608         if (port >= PORTS_NUMBER)
609                 return;
610
611         update_controller_port_device(port, device);
612         update_multitap();
613 }
614
615 void retro_get_system_info(struct retro_system_info *info)
616 {
617         memset(info, 0, sizeof(*info));
618         info->library_name = "PCSX-ReARMed";
619         info->library_version = "r22";
620         info->valid_extensions = "bin|cue|img|mdf|pbp|toc|cbn|m3u";
621         info->need_fullpath = true;
622 }
623
624 void retro_get_system_av_info(struct retro_system_av_info *info)
625 {
626         memset(info, 0, sizeof(*info));
627         info->timing.fps            = is_pal_mode ? 50 : 60;
628         info->timing.sample_rate    = 44100;
629         info->geometry.base_width   = 320;
630         info->geometry.base_height  = 240;
631         info->geometry.max_width    = VOUT_MAX_WIDTH;
632         info->geometry.max_height   = VOUT_MAX_HEIGHT;
633         info->geometry.aspect_ratio = 4.0 / 3.0;
634 }
635
636 /* savestates */
637 size_t retro_serialize_size(void)
638 {
639         // it's currently 4380651-4397047 bytes,
640         // but have some reserved for future
641         return 0x440000;
642 }
643
644 struct save_fp {
645         char *buf;
646         size_t pos;
647         int is_write;
648 };
649
650 static void *save_open(const char *name, const char *mode)
651 {
652         struct save_fp *fp;
653
654         if (name == NULL || mode == NULL)
655                 return NULL;
656
657         fp = malloc(sizeof(*fp));
658         if (fp == NULL)
659                 return NULL;
660
661         fp->buf = (char *)name;
662         fp->pos = 0;
663         fp->is_write = (mode[0] == 'w' || mode[1] == 'w');
664
665         return fp;
666 }
667
668 static int save_read(void *file, void *buf, u32 len)
669 {
670         struct save_fp *fp = file;
671         if (fp == NULL || buf == NULL)
672                 return -1;
673
674         memcpy(buf, fp->buf + fp->pos, len);
675         fp->pos += len;
676         return len;
677 }
678
679 static int save_write(void *file, const void *buf, u32 len)
680 {
681         struct save_fp *fp = file;
682         if (fp == NULL || buf == NULL)
683                 return -1;
684
685         memcpy(fp->buf + fp->pos, buf, len);
686         fp->pos += len;
687         return len;
688 }
689
690 static long save_seek(void *file, long offs, int whence)
691 {
692         struct save_fp *fp = file;
693         if (fp == NULL)
694                 return -1;
695
696         switch (whence) {
697         case SEEK_CUR:
698                 fp->pos += offs;
699                 return fp->pos;
700         case SEEK_SET:
701                 fp->pos = offs;
702                 return fp->pos;
703         default:
704                 return -1;
705         }
706 }
707
708 static void save_close(void *file)
709 {
710         struct save_fp *fp = file;
711         size_t r_size = retro_serialize_size();
712         if (fp == NULL)
713                 return;
714
715         if (fp->pos > r_size)
716                 SysPrintf("ERROR: save buffer overflow detected\n");
717         else if (fp->is_write && fp->pos < r_size)
718                 // make sure we don't save trash in leftover space
719                 memset(fp->buf + fp->pos, 0, r_size - fp->pos);
720         free(fp);
721 }
722
723 bool retro_serialize(void *data, size_t size)
724 {
725         int ret = SaveState(data);
726         return ret == 0 ? true : false;
727 }
728
729 bool retro_unserialize(const void *data, size_t size)
730 {
731         int ret = LoadState(data);
732         return ret == 0 ? true : false;
733 }
734
735 /* cheats */
736 void retro_cheat_reset(void)
737 {
738         ClearAllCheats();
739 }
740
741 void retro_cheat_set(unsigned index, bool enabled, const char *code)
742 {
743         char buf[256];
744         int ret;
745
746         // cheat funcs are destructive, need a copy..
747         strncpy(buf, code, sizeof(buf));
748         buf[sizeof(buf) - 1] = 0;
749
750         if (index < NumCheats)
751                 ret = EditCheat(index, "", buf);
752         else
753                 ret = AddCheat("", buf);
754
755         if (ret != 0)
756                 SysPrintf("Failed to set cheat %#u\n", index);
757         else if (index < NumCheats)
758                 Cheats[index].Enabled = enabled;
759 }
760
761 /* multidisk support */
762 static bool disk_ejected;
763 static unsigned int disk_current_index;
764 static unsigned int disk_count;
765 static struct disks_state {
766         char *fname;
767         int internal_index; // for multidisk eboots
768 } disks[8];
769
770 static bool disk_set_eject_state(bool ejected)
771 {
772         // weird PCSX API..
773         SetCdOpenCaseTime(ejected ? -1 : (time(NULL) + 2));
774         LidInterrupt();
775
776         disk_ejected = ejected;
777         return true;
778 }
779
780 static bool disk_get_eject_state(void)
781 {
782         /* can't be controlled by emulated software */
783         return disk_ejected;
784 }
785
786 static unsigned int disk_get_image_index(void)
787 {
788         return disk_current_index;
789 }
790
791 static bool disk_set_image_index(unsigned int index)
792 {
793         if (index >= sizeof(disks) / sizeof(disks[0]))
794                 return false;
795
796         CdromId[0] = '\0';
797         CdromLabel[0] = '\0';
798
799         if (disks[index].fname == NULL) {
800                 SysPrintf("missing disk #%u\n", index);
801                 CDR_shutdown();
802
803                 // RetroArch specifies "no disk" with index == count,
804                 // so don't fail here..
805                 disk_current_index = index;
806                 return true;
807         }
808
809         SysPrintf("switching to disk %u: \"%s\" #%d\n", index,
810                 disks[index].fname, disks[index].internal_index);
811
812         cdrIsoMultidiskSelect = disks[index].internal_index;
813         set_cd_image(disks[index].fname);
814         if (ReloadCdromPlugin() < 0) {
815                 SysPrintf("failed to load cdr plugin\n");
816                 return false;
817         }
818         if (CDR_open() < 0) {
819                 SysPrintf("failed to open cdr plugin\n");
820                 return false;
821         }
822
823         if (!disk_ejected) {
824                 SetCdOpenCaseTime(time(NULL) + 2);
825                 LidInterrupt();
826         }
827
828         disk_current_index = index;
829         return true;
830 }
831
832 static unsigned int disk_get_num_images(void)
833 {
834         return disk_count;
835 }
836
837 static bool disk_replace_image_index(unsigned index,
838         const struct retro_game_info *info)
839 {
840         char *old_fname;
841         bool ret = true;
842
843         if (index >= sizeof(disks) / sizeof(disks[0]))
844                 return false;
845
846         old_fname = disks[index].fname;
847         disks[index].fname = NULL;
848         disks[index].internal_index = 0;
849
850         if (info != NULL) {
851                 disks[index].fname = strdup(info->path);
852                 if (index == disk_current_index)
853                         ret = disk_set_image_index(index);
854         }
855
856         if (old_fname != NULL)
857                 free(old_fname);
858
859         return ret;
860 }
861
862 static bool disk_add_image_index(void)
863 {
864         if (disk_count >= 8)
865                 return false;
866
867         disk_count++;
868         return true;
869 }
870
871 static struct retro_disk_control_callback disk_control = {
872         .set_eject_state = disk_set_eject_state,
873         .get_eject_state = disk_get_eject_state,
874         .get_image_index = disk_get_image_index,
875         .set_image_index = disk_set_image_index,
876         .get_num_images = disk_get_num_images,
877         .replace_image_index = disk_replace_image_index,
878         .add_image_index = disk_add_image_index,
879 };
880
881 // just in case, maybe a win-rt port in the future?
882 #ifdef _WIN32
883 #define SLASH '\\'
884 #else
885 #define SLASH '/'
886 #endif
887
888 static char base_dir[PATH_MAX];
889
890 static bool read_m3u(const char *file)
891 {
892         char line[PATH_MAX];
893         char name[PATH_MAX];
894         FILE *f = fopen(file, "r");
895         if (!f)
896                 return false;
897
898         while (fgets(line, sizeof(line), f) && disk_count < sizeof(disks) / sizeof(disks[0])) {
899                 if (line[0] == '#')
900                         continue;
901                 char *carrige_return = strchr(line, '\r');
902                 if (carrige_return)
903                         *carrige_return = '\0';
904                 char *newline = strchr(line, '\n');
905                 if (newline)
906                         *newline = '\0';
907
908                 if (line[0] != '\0')
909                 {
910                         snprintf(name, sizeof(name), "%s%c%s", base_dir, SLASH, line);
911                         disks[disk_count++].fname = strdup(name);
912                 }
913         }
914
915         fclose(f);
916         return (disk_count != 0);
917 }
918
919 static void extract_directory(char *buf, const char *path, size_t size)
920 {
921    char *base;
922    strncpy(buf, path, size - 1);
923    buf[size - 1] = '\0';
924
925    base = strrchr(buf, '/');
926    if (!base)
927       base = strrchr(buf, '\\');
928
929    if (base)
930       *base = '\0';
931    else
932    {
933       buf[0] = '.';
934       buf[1] = '\0';
935    }
936 }
937
938 #if defined(__QNX__) || defined(_WIN32)
939 /* Blackberry QNX doesn't have strcasestr */
940
941 /*
942  * Find the first occurrence of find in s, ignore case.
943  */
944 char *
945 strcasestr(const char *s, const char*find)
946 {
947         char c, sc;
948         size_t len;
949
950         if ((c = *find++) != 0) {
951                 c = tolower((unsigned char)c);
952                 len = strlen(find);
953                 do {
954                         do {
955                                 if ((sc = *s++) == 0)
956                                         return (NULL);
957                         } while ((char)tolower((unsigned char)sc) != c);
958                 } while (strncasecmp(s, find, len) != 0);
959                 s--;
960         }
961         return ((char *)s);
962 }
963 #endif
964
965 bool retro_load_game(const struct retro_game_info *info)
966 {
967         size_t i;
968         bool is_m3u = (strcasestr(info->path, ".m3u") != NULL);
969
970    struct retro_input_descriptor desc[] = {
971       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT,  "D-Pad Left" },
972       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP,    "D-Pad Up" },
973       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN,  "D-Pad Down" },
974       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
975       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B,     "Cross" },
976       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A,     "Circle" },
977       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X,     "Triangle" },
978       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y,     "Square" },
979       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L,     "L1" },
980       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2,    "L2" },
981       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3,    "L3" },
982       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R,     "R1" },
983       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2,    "R2" },
984       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3,    "R3" },
985       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,    "Select" },
986       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START,    "Start" },
987       { 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X" },
988       { 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" },
989       { 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X" },
990       { 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" },
991
992
993       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT,  "D-Pad Left" },
994       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP,    "D-Pad Up" },
995       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN,  "D-Pad Down" },
996       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
997       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B,     "Cross" },
998       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A,     "Circle" },
999       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X,     "Triangle" },
1000       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y,     "Square" },
1001       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L,     "L1" },
1002       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2,    "L2" },
1003       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3,    "L3" },
1004       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R,     "R1" },
1005       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2,    "R2" },
1006       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3,    "R3" },
1007       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,    "Select" },
1008       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START,    "Start" },
1009       { 1, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X" },
1010       { 1, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" },
1011       { 1, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X" },
1012       { 1, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" },
1013
1014       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT,  "D-Pad Left" },
1015       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP,    "D-Pad Up" },
1016       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN,  "D-Pad Down" },
1017       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
1018       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B,     "Cross" },
1019       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A,     "Circle" },
1020       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X,     "Triangle" },
1021       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y,     "Square" },
1022       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L,     "L1" },
1023       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2,    "L2" },
1024       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3,    "L3" },
1025       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R,     "R1" },
1026       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2,    "R2" },
1027       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3,    "R3" },
1028       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,    "Select" },
1029       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START,    "Start" },
1030       { 2, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X" },
1031       { 2, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" },
1032       { 2, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X" },
1033       { 2, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" },
1034
1035       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT,  "D-Pad Left" },
1036       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP,    "D-Pad Up" },
1037       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN,  "D-Pad Down" },
1038       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
1039       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B,     "Cross" },
1040       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A,     "Circle" },
1041       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X,     "Triangle" },
1042       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y,     "Square" },
1043       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L,     "L1" },
1044       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2,    "L2" },
1045       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3,    "L3" },
1046       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R,     "R1" },
1047       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2,    "R2" },
1048       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3,    "R3" },
1049       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,    "Select" },
1050       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START,    "Start" },
1051       { 3, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X" },
1052       { 3, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" },
1053       { 3, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X" },
1054       { 3, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" },
1055
1056       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT,  "D-Pad Left" },
1057       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP,    "D-Pad Up" },
1058       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN,  "D-Pad Down" },
1059       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
1060       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B,     "Cross" },
1061       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A,     "Circle" },
1062       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X,     "Triangle" },
1063       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y,     "Square" },
1064       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L,     "L1" },
1065       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2,    "L2" },
1066       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3,    "L3" },
1067       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R,     "R1" },
1068       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2,    "R2" },
1069       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3,    "R3" },
1070       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,    "Select" },
1071       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START,    "Start" },
1072       { 4, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X" },
1073       { 4, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" },
1074       { 4, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X" },
1075       { 4, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" },
1076
1077       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT,  "D-Pad Left" },
1078       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP,    "D-Pad Up" },
1079       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN,  "D-Pad Down" },
1080       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
1081       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B,     "Cross" },
1082       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A,     "Circle" },
1083       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X,     "Triangle" },
1084       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y,     "Square" },
1085       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L,     "L1" },
1086       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2,    "L2" },
1087       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3,    "L3" },
1088       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R,     "R1" },
1089       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2,    "R2" },
1090       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3,    "R3" },
1091       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,    "Select" },
1092       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START,    "Start" },
1093       { 5, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X" },
1094       { 5, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" },
1095       { 5, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X" },
1096       { 5, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" },
1097
1098       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT,  "D-Pad Left" },
1099       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP,    "D-Pad Up" },
1100       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN,  "D-Pad Down" },
1101       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
1102       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B,     "Cross" },
1103       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A,     "Circle" },
1104       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X,     "Triangle" },
1105       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y,     "Square" },
1106       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L,     "L1" },
1107       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2,    "L2" },
1108       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3,    "L3" },
1109       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R,     "R1" },
1110       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2,    "R2" },
1111       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3,    "R3" },
1112       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,    "Select" },
1113       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START,    "Start" },
1114       { 6, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X" },
1115       { 6, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" },
1116       { 6, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X" },
1117       { 6, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" },
1118
1119       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT,  "D-Pad Left" },
1120       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP,    "D-Pad Up" },
1121       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN,  "D-Pad Down" },
1122       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
1123       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B,     "Cross" },
1124       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A,     "Circle" },
1125       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X,     "Triangle" },
1126       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y,     "Square" },
1127       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L,     "L1" },
1128       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2,    "L2" },
1129       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3,    "L3" },
1130       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R,     "R1" },
1131       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2,    "R2" },
1132       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3,    "R3" },
1133       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,    "Select" },
1134       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START,    "Start" },
1135       { 7, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X" },
1136       { 7, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" },
1137       { 7, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X" },
1138       { 7, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" },
1139
1140       { 0 },
1141    };
1142
1143    environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc);
1144
1145 #ifdef FRONTEND_SUPPORTS_RGB565
1146         enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_RGB565;
1147         if (environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt)) {
1148                 SysPrintf("RGB565 supported, using it\n");
1149         }
1150 #endif
1151
1152         if (info == NULL || info->path == NULL) {
1153                 SysPrintf("info->path required\n");
1154                 return false;
1155         }
1156
1157         if (plugins_opened) {
1158                 ClosePlugins();
1159                 plugins_opened = 0;
1160         }
1161
1162         for (i = 0; i < sizeof(disks) / sizeof(disks[0]); i++) {
1163                 if (disks[i].fname != NULL) {
1164                         free(disks[i].fname);
1165                         disks[i].fname = NULL;
1166                 }
1167                 disks[i].internal_index = 0;
1168         }
1169
1170         disk_current_index = 0;
1171         extract_directory(base_dir, info->path, sizeof(base_dir));
1172
1173         if (is_m3u) {
1174                 if (!read_m3u(info->path)) {
1175                         SysPrintf("failed to read m3u file\n");
1176                         return false;
1177                 }
1178         } else {
1179                 disk_count = 1;
1180                 disks[0].fname = strdup(info->path);
1181         }
1182
1183         set_cd_image(disks[0].fname);
1184
1185         /* have to reload after set_cd_image for correct cdr plugin */
1186         if (LoadPlugins() == -1) {
1187                 SysPrintf("failed to load plugins\n");
1188                 return false;
1189         }
1190
1191         plugins_opened = 1;
1192         NetOpened = 0;
1193
1194         if (OpenPlugins() == -1) {
1195                 SysPrintf("failed to open plugins\n");
1196                 return false;
1197         }
1198
1199         plugin_call_rearmed_cbs();
1200         dfinput_activate();
1201
1202         Config.PsxAuto = 1;
1203         if (CheckCdrom() == -1) {
1204                 SysPrintf("unsupported/invalid CD image: %s\n", info->path);
1205                 return false;
1206         }
1207
1208         SysReset();
1209
1210         if (LoadCdrom() == -1) {
1211                 SysPrintf("could not load CD-ROM!\n");
1212                 return false;
1213         }
1214         emu_on_new_cd(0);
1215
1216         // multidisk images
1217         if (!is_m3u) {
1218                 disk_count = cdrIsoMultidiskCount < 8 ? cdrIsoMultidiskCount : 8;
1219                 for (i = 1; i < sizeof(disks) / sizeof(disks[0]) && i < cdrIsoMultidiskCount; i++) {
1220                         disks[i].fname = strdup(info->path);
1221                         disks[i].internal_index = i;
1222                 }
1223         }
1224
1225         return true;
1226 }
1227
1228 bool retro_load_game_special(unsigned game_type, const struct retro_game_info *info, size_t num_info)
1229 {
1230         return false;
1231 }
1232
1233 void retro_unload_game(void)
1234 {
1235 }
1236
1237 unsigned retro_get_region(void)
1238 {
1239         return is_pal_mode ? RETRO_REGION_PAL : RETRO_REGION_NTSC;
1240 }
1241
1242 void *retro_get_memory_data(unsigned id)
1243 {
1244         if (id == RETRO_MEMORY_SAVE_RAM)
1245                 return Mcd1Data;
1246         else
1247                 return NULL;
1248 }
1249
1250 size_t retro_get_memory_size(unsigned id)
1251 {
1252         if (id == RETRO_MEMORY_SAVE_RAM)
1253                 return MCD_SIZE;
1254         else
1255                 return 0;
1256 }
1257
1258 void retro_reset(void)
1259 {
1260         SysReset();
1261 }
1262
1263 static const unsigned short retro_psx_map[] = {
1264         [RETRO_DEVICE_ID_JOYPAD_B]      = 1 << DKEY_CROSS,
1265         [RETRO_DEVICE_ID_JOYPAD_Y]      = 1 << DKEY_SQUARE,
1266         [RETRO_DEVICE_ID_JOYPAD_SELECT] = 1 << DKEY_SELECT,
1267         [RETRO_DEVICE_ID_JOYPAD_START]  = 1 << DKEY_START,
1268         [RETRO_DEVICE_ID_JOYPAD_UP]     = 1 << DKEY_UP,
1269         [RETRO_DEVICE_ID_JOYPAD_DOWN]   = 1 << DKEY_DOWN,
1270         [RETRO_DEVICE_ID_JOYPAD_LEFT]   = 1 << DKEY_LEFT,
1271         [RETRO_DEVICE_ID_JOYPAD_RIGHT]  = 1 << DKEY_RIGHT,
1272         [RETRO_DEVICE_ID_JOYPAD_A]      = 1 << DKEY_CIRCLE,
1273         [RETRO_DEVICE_ID_JOYPAD_X]      = 1 << DKEY_TRIANGLE,
1274         [RETRO_DEVICE_ID_JOYPAD_L]      = 1 << DKEY_L1,
1275         [RETRO_DEVICE_ID_JOYPAD_R]      = 1 << DKEY_R1,
1276         [RETRO_DEVICE_ID_JOYPAD_L2]     = 1 << DKEY_L2,
1277         [RETRO_DEVICE_ID_JOYPAD_R2]     = 1 << DKEY_R2,
1278         [RETRO_DEVICE_ID_JOYPAD_L3]     = 1 << DKEY_L3,
1279         [RETRO_DEVICE_ID_JOYPAD_R3]     = 1 << DKEY_R3,
1280 };
1281 #define RETRO_PSX_MAP_LEN (sizeof(retro_psx_map) / sizeof(retro_psx_map[0]))
1282
1283 static void update_variables(bool in_flight)
1284 {
1285    struct retro_variable var;
1286    int i;
1287
1288    var.value = NULL;
1289    var.key = "pcsx_rearmed_frameskip";
1290    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
1291       pl_rearmed_cbs.frameskip = atoi(var.value);
1292
1293    var.value = NULL;
1294    var.key = "pcsx_rearmed_region";
1295    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
1296    {
1297       Config.PsxAuto = 0;
1298       if (strcmp(var.value, "Automatic") == 0)
1299          Config.PsxAuto = 1;
1300       else if (strcmp(var.value, "NTSC") == 0)
1301          Config.PsxType = 0;
1302       else if (strcmp(var.value, "PAL") == 0)
1303          Config.PsxType = 1;
1304    }
1305
1306    for (i = 0; i < PORTS_NUMBER; i++)
1307       update_controller_port_variable(i);
1308
1309    update_multitap();
1310
1311 #ifdef __ARM_NEON__
1312    var.value = "NULL";
1313    var.key = "pcsx_rearmed_neon_interlace_enable";
1314
1315    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
1316    {
1317       if (strcmp(var.value, "disabled") == 0)
1318          pl_rearmed_cbs.gpu_neon.allow_interlace = 0;
1319       else if (strcmp(var.value, "enabled") == 0)
1320          pl_rearmed_cbs.gpu_neon.allow_interlace = 1;
1321    }
1322
1323    var.value = NULL;
1324    var.key = "pcsx_rearmed_neon_enhancement_enable";
1325
1326    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
1327    {
1328       if (strcmp(var.value, "disabled") == 0)
1329          pl_rearmed_cbs.gpu_neon.enhancement_enable = 0;
1330       else if (strcmp(var.value, "enabled") == 0)
1331          pl_rearmed_cbs.gpu_neon.enhancement_enable = 1;
1332    }
1333
1334    var.value = NULL;
1335    var.key = "pcsx_rearmed_neon_enhancement_no_main";
1336
1337    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
1338    {
1339       if (strcmp(var.value, "disabled") == 0)
1340          pl_rearmed_cbs.gpu_neon.enhancement_no_main = 0;
1341       else if (strcmp(var.value, "enabled") == 0)
1342          pl_rearmed_cbs.gpu_neon.enhancement_no_main = 1;
1343    }
1344 #endif
1345
1346    var.value = "NULL";
1347    var.key = "pcsx_rearmed_duping_enable";
1348
1349    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
1350    {
1351       if (strcmp(var.value, "off") == 0)
1352          duping_enable = false;
1353       else if (strcmp(var.value, "on") == 0)
1354          duping_enable = true;
1355    }
1356
1357 #ifndef DRC_DISABLE
1358    var.value = NULL;
1359    var.key = "pcsx_rearmed_drc";
1360
1361    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
1362    {
1363       R3000Acpu *prev_cpu = psxCpu;
1364
1365 #ifdef _3DS
1366       if(!__ctr_svchax)
1367          Config.Cpu = CPU_INTERPRETER;
1368       else
1369 #endif
1370       if (strcmp(var.value, "disabled") == 0)
1371          Config.Cpu = CPU_INTERPRETER;
1372       else if (strcmp(var.value, "enabled") == 0)
1373          Config.Cpu = CPU_DYNAREC;
1374
1375       psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
1376       if (psxCpu != prev_cpu) {
1377          prev_cpu->Shutdown();
1378          psxCpu->Init();
1379          psxCpu->Reset(); // not really a reset..
1380       }
1381    }
1382 #endif
1383
1384    var.value = "NULL";
1385    var.key = "pcsx_rearmed_spu_reverb";
1386
1387    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
1388    {
1389       if (strcmp(var.value, "off") == 0)
1390          spu_config.iUseReverb = false;
1391       else if (strcmp(var.value, "on") == 0)
1392          spu_config.iUseReverb = true;
1393    }
1394
1395    var.value = "NULL";
1396    var.key = "pcsx_rearmed_spu_interpolation";
1397
1398    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
1399    {
1400       if (strcmp(var.value, "simple") == 0)
1401          spu_config.iUseInterpolation = 1;
1402       else if (strcmp(var.value, "gaussian") == 0)
1403          spu_config.iUseInterpolation = 2;
1404       else if (strcmp(var.value, "cubic") == 0)
1405          spu_config.iUseInterpolation = 3;
1406       else if (strcmp(var.value, "off") == 0)
1407          spu_config.iUseInterpolation = 0;
1408    }
1409
1410    var.value = "NULL";
1411    var.key = "pcsx_rearmed_pe2_fix";
1412
1413    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
1414    {
1415       if (strcmp(var.value, "disabled") == 0)
1416          Config.RCntFix = 0;
1417       else if (strcmp(var.value, "enabled") == 0)
1418          Config.RCntFix = 1;
1419    }
1420
1421    var.value = "NULL";
1422    var.key = "pcsx_rearmed_inuyasha_fix";
1423
1424    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
1425    {
1426       if (strcmp(var.value, "disabled") == 0)
1427          Config.VSyncWA = 0;
1428       else if (strcmp(var.value, "enabled") == 0)
1429          Config.VSyncWA = 1;
1430    }
1431
1432    if (in_flight) {
1433       // inform core things about possible config changes
1434       plugin_call_rearmed_cbs();
1435
1436       if (GPU_open != NULL && GPU_close != NULL) {
1437          GPU_close();
1438          GPU_open(&gpuDisp, "PCSX", NULL);
1439       }
1440
1441       dfinput_activate();
1442    }
1443 }
1444
1445 void retro_run(void)
1446 {
1447     int i;
1448
1449         input_poll_cb();
1450
1451         bool updated = false;
1452         if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated)
1453                 update_variables(true);
1454
1455         // reset all keystate, query libretro for keystate
1456         int j;
1457         for(i = 0; i < PORTS_NUMBER; i++) {
1458                 in_keystate[i] = 0;
1459
1460                 if (in_type[i] == PSE_PAD_TYPE_NONE)
1461                         continue;
1462
1463                 // query libretro for keystate
1464                 for (j = 0; j < RETRO_PSX_MAP_LEN; j++)
1465                         if (input_state_cb(i, RETRO_DEVICE_JOYPAD, 0, j))
1466                                 in_keystate[i] |= retro_psx_map[j];
1467
1468                 if (in_type[i] == PSE_PAD_TYPE_ANALOGPAD)
1469                 {
1470                         in_analog_left[i][0] = (input_state_cb(i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X) / 256) + 128;
1471                         in_analog_left[i][1] = (input_state_cb(i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y) / 256) + 128;
1472                         in_analog_right[i][0] = (input_state_cb(i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X) / 256) + 128;
1473                         in_analog_right[i][1] = (input_state_cb(i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y) / 256) + 128;
1474                 }
1475         }
1476
1477         stop = 0;
1478         psxCpu->Execute();
1479
1480         video_cb((vout_fb_dirty || !vout_can_dupe || !duping_enable) ? vout_buf : NULL,
1481                 vout_width, vout_height, vout_width * 2);
1482         vout_fb_dirty = 0;
1483 }
1484
1485 static bool try_use_bios(const char *path)
1486 {
1487         FILE *f;
1488         long size;
1489         const char *name;
1490
1491         f = fopen(path, "rb");
1492         if (f == NULL)
1493                 return false;
1494
1495         fseek(f, 0, SEEK_END);
1496         size = ftell(f);
1497         fclose(f);
1498
1499         if (size != 512 * 1024)
1500                 return false;
1501
1502         name = strrchr(path, SLASH);
1503         if (name++ == NULL)
1504                 name = path;
1505         snprintf(Config.Bios, sizeof(Config.Bios), "%s", name);
1506         return true;
1507 }
1508
1509 #ifndef VITA
1510 #include <sys/types.h>
1511 #include <dirent.h>
1512
1513 static bool find_any_bios(const char *dirpath, char *path, size_t path_size)
1514 {
1515         DIR *dir;
1516         struct dirent *ent;
1517         bool ret = false;
1518
1519         dir = opendir(dirpath);
1520         if (dir == NULL)
1521                 return false;
1522
1523         while ((ent = readdir(dir))) {
1524                 if (strncasecmp(ent->d_name, "scph", 4) != 0)
1525                         continue;
1526
1527                 snprintf(path, path_size, "%s/%s", dirpath, ent->d_name);
1528                 ret = try_use_bios(path);
1529                 if (ret)
1530                         break;
1531         }
1532         closedir(dir);
1533         return ret;
1534 }
1535 #else
1536 #define find_any_bios(...) false
1537 #endif
1538
1539 static void check_system_specs(void)
1540 {
1541    unsigned level = 6;
1542    environ_cb(RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL, &level);
1543 }
1544
1545 void retro_init(void)
1546 {
1547         const char *bios[] = { "scph1001", "scph5501", "scph7001" };
1548         const char *dir;
1549         char path[256];
1550         int i, ret;
1551         bool found_bios = false;
1552
1553 #ifdef _3DS
1554    psxMapHook = pl_3ds_mmap;
1555    psxUnmapHook = pl_3ds_munmap;
1556 #endif
1557 #ifdef VITA
1558    psxMapHook = pl_vita_mmap;
1559    psxUnmapHook = pl_vita_munmap;
1560 #endif
1561         ret = emu_core_preinit();
1562 #ifdef _3DS
1563    /* emu_core_preinit sets the cpu to dynarec */
1564    if(!__ctr_svchax)
1565       Config.Cpu = CPU_INTERPRETER;
1566 #endif
1567         ret |= emu_core_init();
1568         if (ret != 0) {
1569                 SysPrintf("PCSX init failed.\n");
1570                 exit(1);
1571         }
1572
1573 #ifdef _3DS
1574    vout_buf = linearMemAlign(VOUT_MAX_WIDTH * VOUT_MAX_HEIGHT * 2, 0x80);
1575 #elif defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L) && !defined(VITA)
1576         posix_memalign(&vout_buf, 16, VOUT_MAX_WIDTH * VOUT_MAX_HEIGHT * 2);
1577 #else
1578         vout_buf = malloc(VOUT_MAX_WIDTH * VOUT_MAX_HEIGHT * 2);
1579 #endif
1580
1581         if (environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir) && dir)
1582         {
1583                 snprintf(Config.BiosDir, sizeof(Config.BiosDir), "%s", dir);
1584
1585                 for (i = 0; i < sizeof(bios) / sizeof(bios[0]); i++) {
1586                         snprintf(path, sizeof(path), "%s/%s.bin", dir, bios[i]);
1587                         found_bios = try_use_bios(path);
1588                         if (found_bios)
1589                                 break;
1590                 }
1591
1592                 if (!found_bios)
1593                         found_bios = find_any_bios(dir, path, sizeof(path));
1594         }
1595         if (found_bios) {
1596                 SysPrintf("found BIOS file: %s\n", Config.Bios);
1597         }
1598         else
1599         {
1600                 SysPrintf("no BIOS files found.\n");
1601                 struct retro_message msg =
1602                 {
1603                         "no BIOS found, expect bugs!",
1604                         180
1605                 };
1606                 environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE, (void*)&msg);
1607         }
1608
1609         environ_cb(RETRO_ENVIRONMENT_GET_CAN_DUPE, &vout_can_dupe);
1610         environ_cb(RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE, &disk_control);
1611         environ_cb(RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE, &rumble);
1612
1613         /* Set how much slower PSX CPU runs * 100 (so that 200 is 2 times)
1614          * we have to do this because cache misses and some IO penalties
1615          * are not emulated. Warning: changing this may break compatibility. */
1616         cycle_multiplier = 175;
1617 #ifdef HAVE_PRE_ARMV7
1618         cycle_multiplier = 200;
1619 #endif
1620         pl_rearmed_cbs.gpu_peops.iUseDither = 1;
1621         spu_config.iUseFixedUpdates = 1;
1622
1623         McdDisable[0] = 0;
1624         McdDisable[1] = 1;
1625         init_memcard(Mcd1Data);
1626
1627         SaveFuncs.open = save_open;
1628         SaveFuncs.read = save_read;
1629         SaveFuncs.write = save_write;
1630         SaveFuncs.seek = save_seek;
1631         SaveFuncs.close = save_close;
1632
1633         update_variables(false);
1634         check_system_specs();
1635 }
1636
1637 void retro_deinit(void)
1638 {
1639         SysClose();
1640 #ifdef _3DS
1641    linearFree(vout_buf);
1642 #else
1643         free(vout_buf);
1644 #endif
1645         vout_buf = NULL;
1646 }
1647
1648 #ifdef VITA
1649 #include <psp2/kernel/threadmgr.h>
1650 int usleep (unsigned long us)
1651 {
1652    sceKernelDelayThread(us);
1653 }
1654 #endif