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