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