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