drc: something works on arm64
[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 #ifdef __MACH__
14 #include <unistd.h>
15 #include <sys/syscall.h>
16 #endif
17
18 #include "../libpcsxcore/misc.h"
19 #include "../libpcsxcore/psxcounters.h"
20 #include "../libpcsxcore/psxmem_map.h"
21 #include "../libpcsxcore/new_dynarec/new_dynarec.h"
22 #include "../libpcsxcore/cdrom.h"
23 #include "../libpcsxcore/cdriso.h"
24 #include "../libpcsxcore/cheat.h"
25 #include "../plugins/dfsound/out.h"
26 #include "../plugins/dfsound/spu_config.h"
27 #include "../plugins/dfinput/externals.h"
28 #include "cspace.h"
29 #include "main.h"
30 #include "plugin.h"
31 #include "plugin_lib.h"
32 #include "arm_features.h"
33 #include "revision.h"
34 #include "libretro.h"
35
36 static retro_video_refresh_t video_cb;
37 static retro_input_poll_t input_poll_cb;
38 static retro_input_state_t input_state_cb;
39 static retro_environment_t environ_cb;
40 static retro_audio_sample_batch_t audio_batch_cb;
41 static struct retro_rumble_interface rumble;
42
43 static void *vout_buf;
44 static int vout_width, vout_height;
45 static int vout_doffs_old, vout_fb_dirty;
46 static bool vout_can_dupe;
47 static bool duping_enable;
48
49 static int plugins_opened;
50 static int is_pal_mode;
51
52 /* memory card data */
53 extern char Mcd1Data[MCD_SIZE];
54 extern char McdDisable[2];
55
56 /* PCSX ReARMed core calls and stuff */
57 int in_type1, in_type2;
58 int in_a1[2] = { 127, 127 }, in_a2[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 static void *pl_mmap(unsigned int size)
173 {
174         return psxMap(0, size, 0, MAP_TAG_VRAM);
175 }
176
177 static void pl_munmap(void *ptr, unsigned int size)
178 {
179         psxUnmap(ptr, size, MAP_TAG_VRAM);
180 }
181
182 struct rearmed_cbs pl_rearmed_cbs = {
183         .pl_vout_open = vout_open,
184         .pl_vout_set_mode = vout_set_mode,
185         .pl_vout_flip = vout_flip,
186         .pl_vout_close = vout_close,
187         .mmap = pl_mmap,
188         .munmap = pl_munmap,
189         /* from psxcounters */
190         .gpu_hcnt = &hSyncCount,
191         .gpu_frame_count = &frame_counter,
192 };
193
194 void pl_frame_limit(void)
195 {
196         /* called once per frame, make psxCpu->Execute() above return */
197         stop = 1;
198 }
199
200 void pl_timing_prepare(int is_pal)
201 {
202         is_pal_mode = is_pal;
203 }
204
205 void plat_trigger_vibrate(int pad, int low, int high)
206 {
207     rumble.set_rumble_state(pad, RETRO_RUMBLE_STRONG, high << 8);
208     rumble.set_rumble_state(pad, RETRO_RUMBLE_WEAK, low ? 0xffff : 0x0);
209 }
210
211 void pl_update_gun(int *xn, int *yn, int *xres, int *yres, int *in)
212 {
213 }
214
215 /* sound calls */
216 static int snd_init(void)
217 {
218         return 0;
219 }
220
221 static void snd_finish(void)
222 {
223 }
224
225 static int snd_busy(void)
226 {
227         return 0;
228 }
229
230 static void snd_feed(void *buf, int bytes)
231 {
232         if (audio_batch_cb != NULL)
233                 audio_batch_cb(buf, bytes / 4);
234 }
235
236 void out_register_libretro(struct out_driver *drv)
237 {
238         drv->name = "libretro";
239         drv->init = snd_init;
240         drv->finish = snd_finish;
241         drv->busy = snd_busy;
242         drv->feed = snd_feed;
243 }
244
245 /* libretro */
246 void retro_set_environment(retro_environment_t cb)
247 {
248    static const struct retro_variable vars[] = {
249       { "pcsx_rearmed_frameskip", "Frameskip; 0|1|2|3" },
250       { "pcsx_rearmed_region", "Region; Auto|NTSC|PAL" },
251       { "pcsx_rearmed_pad1type", "Pad 1 Type; standard|analog" },
252       { "pcsx_rearmed_pad2type", "Pad 2 Type; standard|analog" },
253 #ifndef DRC_DISABLE
254       { "pcsx_rearmed_drc", "Dynamic recompiler; enabled|disabled" },
255 #endif
256 #ifdef __ARM_NEON__
257       { "pcsx_rearmed_neon_interlace_enable", "Enable interlacing mode(s); disabled|enabled" },
258       { "pcsx_rearmed_neon_enhancement_enable", "Enhanced resolution (slow); disabled|enabled" },
259       { "pcsx_rearmed_neon_enhancement_no_main", "Enhanced resolution speed hack; disabled|enabled" },
260 #endif
261       { "pcsx_rearmed_duping_enable", "Frame duping; on|off" },
262       { "pcsx_rearmed_spu_reverb", "Sound: Reverb; on|off" },
263       { "pcsx_rearmed_spu_interpolation", "Sound: Interpolation; simple|gaussian|cubic|off" },
264       { NULL, NULL },
265    };
266
267    environ_cb = cb;
268
269    cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void*)vars);
270 }
271
272 void retro_set_video_refresh(retro_video_refresh_t cb) { video_cb = cb; }
273 void retro_set_audio_sample(retro_audio_sample_t cb) { (void)cb; }
274 void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) { audio_batch_cb = cb; }
275 void retro_set_input_poll(retro_input_poll_t cb) { input_poll_cb = cb; }
276 void retro_set_input_state(retro_input_state_t cb) { input_state_cb = cb; }
277
278 unsigned retro_api_version(void)
279 {
280         return RETRO_API_VERSION;
281 }
282
283 void retro_set_controller_port_device(unsigned port, unsigned device)
284 {
285 }
286
287 void retro_get_system_info(struct retro_system_info *info)
288 {
289         memset(info, 0, sizeof(*info));
290         info->library_name = "PCSX-ReARMed";
291         info->library_version = "r22";
292         info->valid_extensions = "bin|cue|img|mdf|pbp|toc|cbn|m3u";
293         info->need_fullpath = true;
294 }
295
296 void retro_get_system_av_info(struct retro_system_av_info *info)
297 {
298         memset(info, 0, sizeof(*info));
299         info->timing.fps            = is_pal_mode ? 50 : 60;
300         info->timing.sample_rate    = 44100;
301         info->geometry.base_width   = 320;
302         info->geometry.base_height  = 240;
303         info->geometry.max_width    = VOUT_MAX_WIDTH;
304         info->geometry.max_height   = VOUT_MAX_HEIGHT;
305         info->geometry.aspect_ratio = 4.0 / 3.0;
306 }
307
308 /* savestates */
309 size_t retro_serialize_size(void) 
310
311         // it's currently 4380651-4397047 bytes,
312         // but have some reserved for future
313         return 0x440000;
314 }
315
316 struct save_fp {
317         char *buf;
318         size_t pos;
319         int is_write;
320 };
321
322 static void *save_open(const char *name, const char *mode)
323 {
324         struct save_fp *fp;
325
326         if (name == NULL || mode == NULL)
327                 return NULL;
328
329         fp = malloc(sizeof(*fp));
330         if (fp == NULL)
331                 return NULL;
332
333         fp->buf = (char *)name;
334         fp->pos = 0;
335         fp->is_write = (mode[0] == 'w' || mode[1] == 'w');
336
337         return fp;
338 }
339
340 static int save_read(void *file, void *buf, u32 len)
341 {
342         struct save_fp *fp = file;
343         if (fp == NULL || buf == NULL)
344                 return -1;
345
346         memcpy(buf, fp->buf + fp->pos, len);
347         fp->pos += len;
348         return len;
349 }
350
351 static int save_write(void *file, const void *buf, u32 len)
352 {
353         struct save_fp *fp = file;
354         if (fp == NULL || buf == NULL)
355                 return -1;
356
357         memcpy(fp->buf + fp->pos, buf, len);
358         fp->pos += len;
359         return len;
360 }
361
362 static long save_seek(void *file, long offs, int whence)
363 {
364         struct save_fp *fp = file;
365         if (fp == NULL)
366                 return -1;
367
368         switch (whence) {
369         case SEEK_CUR:
370                 fp->pos += offs;
371                 return fp->pos;
372         case SEEK_SET:
373                 fp->pos = offs;
374                 return fp->pos;
375         default:
376                 return -1;
377         }
378 }
379
380 static void save_close(void *file)
381 {
382         struct save_fp *fp = file;
383         size_t r_size = retro_serialize_size();
384         if (fp == NULL)
385                 return;
386
387         if (fp->pos > r_size)
388                 SysPrintf("ERROR: save buffer overflow detected\n");
389         else if (fp->is_write && fp->pos < r_size)
390                 // make sure we don't save trash in leftover space
391                 memset(fp->buf + fp->pos, 0, r_size - fp->pos);
392         free(fp);
393 }
394
395 bool retro_serialize(void *data, size_t size)
396
397         int ret = SaveState(data);
398         return ret == 0 ? true : false;
399 }
400
401 bool retro_unserialize(const void *data, size_t size)
402 {
403         int ret = LoadState(data);
404         return ret == 0 ? true : false;
405 }
406
407 /* cheats */
408 void retro_cheat_reset(void)
409 {
410         ClearAllCheats();
411 }
412
413 void retro_cheat_set(unsigned index, bool enabled, const char *code)
414 {
415         char buf[256];
416         int ret;
417
418         // cheat funcs are destructive, need a copy..
419         strncpy(buf, code, sizeof(buf));
420         buf[sizeof(buf) - 1] = 0;
421
422         if (index < NumCheats)
423                 ret = EditCheat(index, "", buf);
424         else
425                 ret = AddCheat("", buf);
426
427         if (ret != 0)
428                 SysPrintf("Failed to set cheat %#u\n", index);
429         else if (index < NumCheats)
430                 Cheats[index].Enabled = enabled;
431 }
432
433 /* multidisk support */
434 static bool disk_ejected;
435 static unsigned int disk_current_index;
436 static unsigned int disk_count;
437 static struct disks_state {
438         char *fname;
439         int internal_index; // for multidisk eboots
440 } disks[8];
441
442 static bool disk_set_eject_state(bool ejected)
443 {
444         // weird PCSX API..
445         SetCdOpenCaseTime(ejected ? -1 : (time(NULL) + 2));
446         LidInterrupt();
447
448         disk_ejected = ejected;
449         return true;
450 }
451
452 static bool disk_get_eject_state(void)
453 {
454         /* can't be controlled by emulated software */
455         return disk_ejected;
456 }
457
458 static unsigned int disk_get_image_index(void)
459 {
460         return disk_current_index;
461 }
462
463 static bool disk_set_image_index(unsigned int index)
464 {
465         if (index >= sizeof(disks) / sizeof(disks[0]))
466                 return false;
467
468         CdromId[0] = '\0';
469         CdromLabel[0] = '\0';
470
471         if (disks[index].fname == NULL) {
472                 SysPrintf("missing disk #%u\n", index);
473                 CDR_shutdown();
474
475                 // RetroArch specifies "no disk" with index == count,
476                 // so don't fail here..
477                 disk_current_index = index;
478                 return true;
479         }
480
481         SysPrintf("switching to disk %u: \"%s\" #%d\n", index,
482                 disks[index].fname, disks[index].internal_index);
483
484         cdrIsoMultidiskSelect = disks[index].internal_index;
485         set_cd_image(disks[index].fname);
486         if (ReloadCdromPlugin() < 0) {
487                 SysPrintf("failed to load cdr plugin\n");
488                 return false;
489         }
490         if (CDR_open() < 0) {
491                 SysPrintf("failed to open cdr plugin\n");
492                 return false;
493         }
494
495         if (!disk_ejected) {
496                 SetCdOpenCaseTime(time(NULL) + 2);
497                 LidInterrupt();
498         }
499
500         disk_current_index = index;
501         return true;
502 }
503
504 static unsigned int disk_get_num_images(void)
505 {
506         return disk_count;
507 }
508
509 static bool disk_replace_image_index(unsigned index,
510         const struct retro_game_info *info)
511 {
512         char *old_fname;
513         bool ret = true;
514
515         if (index >= sizeof(disks) / sizeof(disks[0]))
516                 return false;
517
518         old_fname = disks[index].fname;
519         disks[index].fname = NULL;
520         disks[index].internal_index = 0;
521
522         if (info != NULL) {
523                 disks[index].fname = strdup(info->path);
524                 if (index == disk_current_index)
525                         ret = disk_set_image_index(index);
526         }
527
528         if (old_fname != NULL)
529                 free(old_fname);
530
531         return ret;
532 }
533
534 static bool disk_add_image_index(void)
535 {
536         if (disk_count >= 8)
537                 return false;
538
539         disk_count++;
540         return true;
541 }
542
543 static struct retro_disk_control_callback disk_control = {
544         .set_eject_state = disk_set_eject_state,
545         .get_eject_state = disk_get_eject_state,
546         .get_image_index = disk_get_image_index,
547         .set_image_index = disk_set_image_index,
548         .get_num_images = disk_get_num_images,
549         .replace_image_index = disk_replace_image_index,
550         .add_image_index = disk_add_image_index,
551 };
552
553 // just in case, maybe a win-rt port in the future?
554 #ifdef _WIN32
555 #define SLASH '\\'
556 #else
557 #define SLASH '/'
558 #endif
559
560 static char base_dir[PATH_MAX];
561
562 static bool read_m3u(const char *file)
563 {
564         char line[PATH_MAX];
565         char name[PATH_MAX];
566         FILE *f = fopen(file, "r");
567         if (!f)
568                 return false;
569
570         while (fgets(line, sizeof(line), f) && disk_count < sizeof(disks) / sizeof(disks[0])) {
571                 if (line[0] == '#')
572                         continue;
573                 char *carrige_return = strchr(line, '\r');
574                 if (carrige_return)
575                         *carrige_return = '\0';
576                 char *newline = strchr(line, '\n');
577                 if (newline)
578                         *newline = '\0';
579
580                 if (line[0] != '\0')
581                 {
582                         snprintf(name, sizeof(name), "%s%c%s", base_dir, SLASH, line);
583                         disks[disk_count++].fname = strdup(name);
584                 }
585         }
586
587         fclose(f);
588         return (disk_count != 0);
589 }
590
591 static void extract_directory(char *buf, const char *path, size_t size)
592 {
593    char *base;
594    strncpy(buf, path, size - 1);
595    buf[size - 1] = '\0';
596
597    base = strrchr(buf, '/');
598    if (!base)
599       base = strrchr(buf, '\\');
600
601    if (base)
602       *base = '\0';
603    else
604    {
605       buf[0] = '.';
606       buf[1] = '\0';
607    }
608 }
609
610 #if defined(__QNX__) || defined(_WIN32)
611 /* Blackberry QNX doesn't have strcasestr */
612
613 /*
614  * Find the first occurrence of find in s, ignore case.
615  */
616 char *
617 strcasestr(const char *s, const char*find)
618 {
619         char c, sc;
620         size_t len;
621
622         if ((c = *find++) != 0) {
623                 c = tolower((unsigned char)c);
624                 len = strlen(find);
625                 do {
626                         do {
627                                 if ((sc = *s++) == 0)
628                                         return (NULL);
629                         } while ((char)tolower((unsigned char)sc) != c);
630                 } while (strncasecmp(s, find, len) != 0);
631                 s--;
632         }
633         return ((char *)s);
634 }
635 #endif
636
637 bool retro_load_game(const struct retro_game_info *info)
638 {
639         size_t i;
640         bool is_m3u = (strcasestr(info->path, ".m3u") != NULL);
641
642    struct retro_input_descriptor desc[] = {
643       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT,  "D-Pad Left" },
644       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP,    "D-Pad Up" },
645       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN,  "D-Pad Down" },
646       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
647       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B,     "Cross" },
648       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A,     "Circle" },
649       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X,     "Triangle" },
650       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y,     "Square" },
651       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L,     "L1" },
652       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2,    "L2" },
653       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3,    "L3" },
654       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R,     "R1" },
655       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2,    "R2" },
656       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3,    "R3" },
657       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,    "Select" },
658       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START,    "Start" },
659       { 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X" },
660       { 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" },
661       { 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X" },
662       { 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" },
663
664
665       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT,  "D-Pad Left" },
666       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP,    "D-Pad Up" },
667       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN,  "D-Pad Down" },
668       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
669       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B,     "Cross" },
670       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A,     "Circle" },
671       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X,     "Triangle" },
672       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y,     "Square" },
673       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L,     "L1" },
674       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2,    "L2" },
675       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3,    "L3" },
676       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R,     "R1" },
677       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2,    "R2" },
678       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3,    "R3" },
679       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,    "Select" },
680       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START,    "Start" },
681       { 1, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X" },
682       { 1, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" },
683       { 1, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X" },
684       { 1, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" },
685
686       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT,  "D-Pad Left" },
687       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP,    "D-Pad Up" },
688       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN,  "D-Pad Down" },
689       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
690       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B,     "Cross" },
691       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A,     "Circle" },
692       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X,     "Triangle" },
693       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y,     "Square" },
694       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L,     "L1" },
695       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2,    "L2" },
696       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3,    "L3" },
697       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R,     "R1" },
698       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2,    "R2" },
699       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3,    "R3" },
700       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,    "Select" },
701       { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START,    "Start" },
702       { 2, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X" },
703       { 2, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" },
704       { 2, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X" },
705       { 2, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" },
706
707       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT,  "D-Pad Left" },
708       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP,    "D-Pad Up" },
709       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN,  "D-Pad Down" },
710       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
711       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B,     "Cross" },
712       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A,     "Circle" },
713       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X,     "Triangle" },
714       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y,     "Square" },
715       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L,     "L1" },
716       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2,    "L2" },
717       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3,    "L3" },
718       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R,     "R1" },
719       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2,    "R2" },
720       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3,    "R3" },
721       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,    "Select" },
722       { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START,    "Start" },
723       { 3, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X" },
724       { 3, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" },
725       { 3, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X" },
726       { 3, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" },
727
728       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT,  "D-Pad Left" },
729       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP,    "D-Pad Up" },
730       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN,  "D-Pad Down" },
731       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
732       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B,     "Cross" },
733       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A,     "Circle" },
734       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X,     "Triangle" },
735       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y,     "Square" },
736       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L,     "L1" },
737       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2,    "L2" },
738       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3,    "L3" },
739       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R,     "R1" },
740       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2,    "R2" },
741       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3,    "R3" },
742       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,    "Select" },
743       { 4, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START,    "Start" },
744       { 4, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X" },
745       { 4, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" },
746       { 4, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X" },
747       { 4, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" },
748
749       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT,  "D-Pad Left" },
750       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP,    "D-Pad Up" },
751       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN,  "D-Pad Down" },
752       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
753       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B,     "Cross" },
754       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A,     "Circle" },
755       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X,     "Triangle" },
756       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y,     "Square" },
757       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L,     "L1" },
758       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2,    "L2" },
759       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3,    "L3" },
760       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R,     "R1" },
761       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2,    "R2" },
762       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3,    "R3" },
763       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,    "Select" },
764       { 5, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START,    "Start" }, 
765       { 5, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X" },
766       { 5, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" },
767       { 5, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X" },
768       { 5, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" },
769
770       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT,  "D-Pad Left" },
771       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP,    "D-Pad Up" },
772       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN,  "D-Pad Down" },
773       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
774       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B,     "Cross" },
775       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A,     "Circle" },
776       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X,     "Triangle" },
777       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y,     "Square" },
778       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L,     "L1" },
779       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2,    "L2" },
780       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3,    "L3" },
781       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R,     "R1" },
782       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2,    "R2" },
783       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3,    "R3" },
784       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,    "Select" },
785       { 6, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START,    "Start" }, 
786       { 6, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X" },
787       { 6, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" },
788       { 6, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X" },
789       { 6, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" },
790
791       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT,  "D-Pad Left" },
792       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP,    "D-Pad Up" },
793       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN,  "D-Pad Down" },
794       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
795       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B,     "Cross" },
796       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A,     "Circle" },
797       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X,     "Triangle" },
798       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y,     "Square" },
799       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L,     "L1" },
800       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2,    "L2" },
801       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3,    "L3" },
802       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R,     "R1" },
803       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2,    "R2" },
804       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3,    "R3" },
805       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,    "Select" },
806       { 7, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START,    "Start" }, 
807       { 7, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X" },
808       { 7, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" },
809       { 7, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X" },
810       { 7, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" },
811
812       { 0 },
813    };
814
815    environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc);
816
817 #ifdef FRONTEND_SUPPORTS_RGB565
818         enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_RGB565;
819         if (environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt)) {
820                 SysPrintf("RGB565 supported, using it\n");
821         }
822 #endif
823
824         if (info == NULL || info->path == NULL) {
825                 SysPrintf("info->path required\n");
826                 return false;
827         }
828
829         if (plugins_opened) {
830                 ClosePlugins();
831                 plugins_opened = 0;
832         }
833
834         for (i = 0; i < sizeof(disks) / sizeof(disks[0]); i++) {
835                 if (disks[i].fname != NULL) {
836                         free(disks[i].fname);
837                         disks[i].fname = NULL;
838                 }
839                 disks[i].internal_index = 0;
840         }
841
842         disk_current_index = 0;
843         extract_directory(base_dir, info->path, sizeof(base_dir));
844
845         if (is_m3u) {
846                 if (!read_m3u(info->path)) {
847                         SysPrintf("failed to read m3u file\n");
848                         return false;
849                 }
850         } else {
851                 disk_count = 1;
852                 disks[0].fname = strdup(info->path);
853         }
854
855         set_cd_image(disks[0].fname);
856
857         /* have to reload after set_cd_image for correct cdr plugin */
858         if (LoadPlugins() == -1) {
859                 SysPrintf("failed to load plugins\n");
860                 return false;
861         }
862
863         plugins_opened = 1;
864         NetOpened = 0;
865
866         if (OpenPlugins() == -1) {
867                 SysPrintf("failed to open plugins\n");
868                 return false;
869         }
870
871         plugin_call_rearmed_cbs();
872         dfinput_activate();
873
874         Config.PsxAuto = 1;
875         if (CheckCdrom() == -1) {
876                 SysPrintf("unsupported/invalid CD image: %s\n", info->path);
877                 return false;
878         }
879
880         SysReset();
881
882         if (LoadCdrom() == -1) {
883                 SysPrintf("could not load CD-ROM!\n");
884                 return false;
885         }
886         emu_on_new_cd(0);
887
888         // multidisk images
889         if (!is_m3u) {
890                 disk_count = cdrIsoMultidiskCount < 8 ? cdrIsoMultidiskCount : 8;
891                 for (i = 1; i < sizeof(disks) / sizeof(disks[0]) && i < cdrIsoMultidiskCount; i++) {
892                         disks[i].fname = strdup(info->path);
893                         disks[i].internal_index = i;
894                 }
895         }
896
897         return true;
898 }
899
900 bool retro_load_game_special(unsigned game_type, const struct retro_game_info *info, size_t num_info)
901 {
902         return false;
903 }
904
905 void retro_unload_game(void) 
906 {
907 }
908
909 unsigned retro_get_region(void)
910 {
911         return is_pal_mode ? RETRO_REGION_PAL : RETRO_REGION_NTSC;
912 }
913
914 void *retro_get_memory_data(unsigned id)
915 {
916         if (id == RETRO_MEMORY_SAVE_RAM)
917                 return Mcd1Data;
918         else
919                 return NULL;
920 }
921
922 size_t retro_get_memory_size(unsigned id)
923 {
924         if (id == RETRO_MEMORY_SAVE_RAM)
925                 return MCD_SIZE;
926         else
927                 return 0;
928 }
929
930 void retro_reset(void)
931 {
932         SysReset();
933 }
934
935 static const unsigned short retro_psx_map[] = {
936         [RETRO_DEVICE_ID_JOYPAD_B]      = 1 << DKEY_CROSS,
937         [RETRO_DEVICE_ID_JOYPAD_Y]      = 1 << DKEY_SQUARE,
938         [RETRO_DEVICE_ID_JOYPAD_SELECT] = 1 << DKEY_SELECT,
939         [RETRO_DEVICE_ID_JOYPAD_START]  = 1 << DKEY_START,
940         [RETRO_DEVICE_ID_JOYPAD_UP]     = 1 << DKEY_UP,
941         [RETRO_DEVICE_ID_JOYPAD_DOWN]   = 1 << DKEY_DOWN,
942         [RETRO_DEVICE_ID_JOYPAD_LEFT]   = 1 << DKEY_LEFT,
943         [RETRO_DEVICE_ID_JOYPAD_RIGHT]  = 1 << DKEY_RIGHT,
944         [RETRO_DEVICE_ID_JOYPAD_A]      = 1 << DKEY_CIRCLE,
945         [RETRO_DEVICE_ID_JOYPAD_X]      = 1 << DKEY_TRIANGLE,
946         [RETRO_DEVICE_ID_JOYPAD_L]      = 1 << DKEY_L1,
947         [RETRO_DEVICE_ID_JOYPAD_R]      = 1 << DKEY_R1,
948         [RETRO_DEVICE_ID_JOYPAD_L2]     = 1 << DKEY_L2,
949         [RETRO_DEVICE_ID_JOYPAD_R2]     = 1 << DKEY_R2,
950         [RETRO_DEVICE_ID_JOYPAD_L3]     = 1 << DKEY_L3,
951         [RETRO_DEVICE_ID_JOYPAD_R3]     = 1 << DKEY_R3,
952 };
953 #define RETRO_PSX_MAP_LEN (sizeof(retro_psx_map) / sizeof(retro_psx_map[0]))
954
955 static void update_variables(bool in_flight)
956 {
957    struct retro_variable var;
958    
959    var.value = NULL;
960    var.key = "pcsx_rearmed_frameskip";
961
962    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
963       pl_rearmed_cbs.frameskip = atoi(var.value);
964
965    var.value = NULL;
966    var.key = "pcsx_rearmed_region";
967
968    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
969    {
970       Config.PsxAuto = 0;
971       if (strcmp(var.value, "Automatic") == 0)
972          Config.PsxAuto = 1;
973       else if (strcmp(var.value, "NTSC") == 0)
974          Config.PsxType = 0;
975       else if (strcmp(var.value, "PAL") == 0)
976          Config.PsxType = 1;
977    }
978
979    var.value = NULL;
980    var.key = "pcsx_rearmed_pad1type";
981
982    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
983    {
984       in_type1 = PSE_PAD_TYPE_STANDARD;
985       if (strcmp(var.value, "analog") == 0)
986          in_type1 = PSE_PAD_TYPE_ANALOGPAD;
987    }
988
989    var.value = NULL;
990    var.key = "pcsx_rearmed_pad2type";
991
992    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
993    {
994       in_type2 = PSE_PAD_TYPE_STANDARD;
995       if (strcmp(var.value, "analog") == 0)
996          in_type2 = PSE_PAD_TYPE_ANALOGPAD;
997    }
998
999 #ifdef __ARM_NEON__
1000    var.value = "NULL";
1001    var.key = "pcsx_rearmed_neon_interlace_enable";
1002
1003    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
1004    {
1005       if (strcmp(var.value, "disabled") == 0)
1006          pl_rearmed_cbs.gpu_neon.allow_interlace = 0;
1007       else if (strcmp(var.value, "enabled") == 0)
1008          pl_rearmed_cbs.gpu_neon.allow_interlace = 1;
1009    }
1010
1011    var.value = NULL;
1012    var.key = "pcsx_rearmed_neon_enhancement_enable";
1013
1014    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
1015    {
1016       if (strcmp(var.value, "disabled") == 0)
1017          pl_rearmed_cbs.gpu_neon.enhancement_enable = 0;
1018       else if (strcmp(var.value, "enabled") == 0)
1019          pl_rearmed_cbs.gpu_neon.enhancement_enable = 1;
1020    }
1021
1022    var.value = NULL;
1023    var.key = "pcsx_rearmed_neon_enhancement_no_main";
1024
1025    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
1026    {
1027       if (strcmp(var.value, "disabled") == 0)
1028          pl_rearmed_cbs.gpu_neon.enhancement_no_main = 0;
1029       else if (strcmp(var.value, "enabled") == 0)
1030          pl_rearmed_cbs.gpu_neon.enhancement_no_main = 1;
1031    }
1032 #endif
1033
1034    var.value = "NULL";
1035    var.key = "pcsx_rearmed_duping_enable";
1036
1037    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
1038    {
1039       if (strcmp(var.value, "off") == 0)
1040          duping_enable = false;
1041       else if (strcmp(var.value, "on") == 0)
1042          duping_enable = true;
1043    }
1044
1045 #ifndef DRC_DISABLE
1046    var.value = NULL;
1047    var.key = "pcsx_rearmed_drc";
1048
1049    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
1050    {
1051       R3000Acpu *prev_cpu = psxCpu;
1052
1053       if (strcmp(var.value, "disabled") == 0)
1054          Config.Cpu = CPU_INTERPRETER;
1055       else if (strcmp(var.value, "enabled") == 0)
1056          Config.Cpu = CPU_DYNAREC;
1057
1058       psxCpu = (Config.Cpu == CPU_INTERPRETER) ? &psxInt : &psxRec;
1059       if (psxCpu != prev_cpu) {
1060          prev_cpu->Shutdown();
1061          psxCpu->Init();
1062          psxCpu->Reset(); // not really a reset..
1063       }
1064    }
1065 #endif
1066
1067    var.value = "NULL";
1068    var.key = "pcsx_rearmed_spu_reverb";
1069
1070    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
1071    {
1072       if (strcmp(var.value, "off") == 0)
1073          spu_config.iUseReverb = false;
1074       else if (strcmp(var.value, "on") == 0)
1075          spu_config.iUseReverb = true;
1076    }
1077
1078    var.value = "NULL";
1079    var.key = "pcsx_rearmed_spu_interpolation";
1080
1081    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
1082    {
1083       if (strcmp(var.value, "simple") == 0)
1084          spu_config.iUseInterpolation = 1;
1085       else if (strcmp(var.value, "gaussian") == 0)
1086          spu_config.iUseInterpolation = 2;
1087       else if (strcmp(var.value, "cubic") == 0)
1088          spu_config.iUseInterpolation = 3;
1089       else if (strcmp(var.value, "off") == 0)
1090          spu_config.iUseInterpolation = 0;
1091    }
1092
1093    if (in_flight) {
1094       // inform core things about possible config changes
1095       plugin_call_rearmed_cbs();
1096
1097       if (GPU_open != NULL && GPU_close != NULL) {
1098          GPU_close();
1099          GPU_open(&gpuDisp, "PCSX", NULL);
1100       }
1101
1102       dfinput_activate();
1103    }
1104 }
1105
1106 void retro_run(void) 
1107 {
1108         int i;
1109
1110         input_poll_cb();
1111
1112         bool updated = false;
1113         if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated)
1114                 update_variables(true);
1115
1116         in_keystate = 0;
1117         for (i = 0; i < RETRO_PSX_MAP_LEN; i++)
1118                 if (input_state_cb(1, RETRO_DEVICE_JOYPAD, 0, i))
1119                         in_keystate |= retro_psx_map[i];
1120         in_keystate <<= 16;
1121         for (i = 0; i < RETRO_PSX_MAP_LEN; i++)
1122                 if (input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, i))
1123                         in_keystate |= retro_psx_map[i];
1124
1125         if (in_type1 == PSE_PAD_TYPE_ANALOGPAD)
1126         {
1127                 in_a1[0] = (input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X) / 256) + 128;
1128                 in_a1[1] = (input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y) / 256) + 128;
1129                 in_a2[0] = (input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X) / 256) + 128;
1130                 in_a2[1] = (input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y) / 256) + 128;
1131         }
1132
1133         stop = 0;
1134         psxCpu->Execute();
1135
1136         video_cb((vout_fb_dirty || !vout_can_dupe || !duping_enable) ? vout_buf : NULL,
1137                 vout_width, vout_height, vout_width * 2);
1138         vout_fb_dirty = 0;
1139 }
1140
1141 static bool try_use_bios(const char *path)
1142 {
1143         FILE *f;
1144         long size;
1145         const char *name;
1146
1147         f = fopen(path, "rb");
1148         if (f == NULL)
1149                 return false;
1150
1151         fseek(f, 0, SEEK_END);
1152         size = ftell(f);
1153         fclose(f);
1154
1155         if (size != 512 * 1024)
1156                 return false;
1157
1158         name = strrchr(path, SLASH);
1159         if (name++ == NULL)
1160                 name = path;
1161         snprintf(Config.Bios, sizeof(Config.Bios), "%s", name);
1162         return true;
1163 }
1164
1165 #if 1
1166 #include <sys/types.h>
1167 #include <dirent.h>
1168
1169 static bool find_any_bios(const char *dirpath, char *path, size_t path_size)
1170 {
1171         DIR *dir;
1172         struct dirent *ent;
1173         bool ret = false;
1174
1175         dir = opendir(dirpath);
1176         if (dir == NULL)
1177                 return false;
1178
1179         while ((ent = readdir(dir))) {
1180                 if (strncasecmp(ent->d_name, "scph", 4) != 0)
1181                         continue;
1182
1183                 snprintf(path, path_size, "%s/%s", dirpath, ent->d_name);
1184                 ret = try_use_bios(path);
1185                 if (ret)
1186                         break;
1187         }
1188         closedir(dir);
1189         return ret;
1190 }
1191 #else
1192 #define find_any_bios(...) false
1193 #endif
1194
1195 static void check_system_specs(void)
1196 {
1197    unsigned level = 6;
1198    environ_cb(RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL, &level);
1199 }
1200
1201 void retro_init(void)
1202 {
1203         const char *bios[] = { "scph1001", "scph5501", "scph7001" };
1204         const char *dir;
1205         char path[256];
1206         int i, ret;
1207         bool found_bios = false;
1208
1209 #ifdef __MACH__
1210         // magic sauce to make the dynarec work on iOS
1211         syscall(SYS_ptrace, 0 /*PTRACE_TRACEME*/, 0, 0, 0);
1212 #endif
1213
1214         ret = emu_core_preinit();
1215         ret |= emu_core_init();
1216         if (ret != 0) {
1217                 SysPrintf("PCSX init failed.\n");
1218                 exit(1);
1219         }
1220
1221 #if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L)
1222         posix_memalign(&vout_buf, 16, VOUT_MAX_WIDTH * VOUT_MAX_HEIGHT * 2);
1223 #else
1224         vout_buf = malloc(VOUT_MAX_WIDTH * VOUT_MAX_HEIGHT * 2);
1225 #endif
1226
1227         if (environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir) && dir)
1228         {
1229                 snprintf(Config.BiosDir, sizeof(Config.BiosDir), "%s/", dir);
1230
1231                 for (i = 0; i < sizeof(bios) / sizeof(bios[0]); i++) {
1232                         snprintf(path, sizeof(path), "%s/%s.bin", dir, bios[i]);
1233                         found_bios = try_use_bios(path);
1234                         if (found_bios)
1235                                 break;
1236                 }
1237
1238                 if (!found_bios)
1239                         found_bios = find_any_bios(dir, path, sizeof(path));
1240         }
1241         if (found_bios) {
1242                 SysPrintf("found BIOS file: %s\n", Config.Bios);
1243         }
1244         else
1245         {
1246                 SysPrintf("no BIOS files found.\n");
1247                 struct retro_message msg = 
1248                 {
1249                         "no BIOS found, expect bugs!",
1250                         180
1251                 };
1252                 environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE, (void*)&msg);
1253         }
1254
1255         environ_cb(RETRO_ENVIRONMENT_GET_CAN_DUPE, &vout_can_dupe);
1256         environ_cb(RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE, &disk_control);
1257         environ_cb(RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE, &rumble);
1258
1259         /* Set how much slower PSX CPU runs * 100 (so that 200 is 2 times)
1260          * we have to do this because cache misses and some IO penalties
1261          * are not emulated. Warning: changing this may break compatibility. */
1262         cycle_multiplier = 175;
1263 #ifdef HAVE_PRE_ARMV7
1264         cycle_multiplier = 200;
1265 #endif
1266         pl_rearmed_cbs.gpu_peops.iUseDither = 1;
1267         spu_config.iUseFixedUpdates = 1;
1268
1269         McdDisable[0] = 0;
1270         McdDisable[1] = 1;
1271         init_memcard(Mcd1Data);
1272
1273         SaveFuncs.open = save_open;
1274         SaveFuncs.read = save_read;
1275         SaveFuncs.write = save_write;
1276         SaveFuncs.seek = save_seek;
1277         SaveFuncs.close = save_close;
1278
1279         update_variables(false);
1280         check_system_specs();
1281 }
1282
1283 void retro_deinit(void)
1284 {
1285         SysClose();
1286         free(vout_buf);
1287         vout_buf = NULL;
1288 }