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