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