(Libretro) Add core options - Frameskip and (for ARM NEON only)
[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 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11
12 #include "../libpcsxcore/misc.h"
13 #include "../libpcsxcore/psxcounters.h"
14 #include "../libpcsxcore/psxmem_map.h"
15 #include "../libpcsxcore/new_dynarec/new_dynarec.h"
16 #include "../libpcsxcore/cdrom.h"
17 #include "../libpcsxcore/cdriso.h"
18 #include "../libpcsxcore/cheat.h"
19 #include "../plugins/dfsound/out.h"
20 #include "cspace.h"
21 #include "main.h"
22 #include "plugin.h"
23 #include "plugin_lib.h"
24 #include "revision.h"
25 #include "libretro.h"
26
27 static retro_video_refresh_t video_cb;
28 static retro_input_poll_t input_poll_cb;
29 static retro_input_state_t input_state_cb;
30 static retro_environment_t environ_cb;
31 static retro_audio_sample_batch_t audio_batch_cb;
32
33 static void *vout_buf;
34 static int vout_width, vout_height;
35 static int vout_doffs_old, vout_fb_dirty;
36 static bool vout_can_dupe;
37
38 static int samples_sent, samples_to_send;
39 static int plugins_opened;
40 static int is_pal_mode;
41
42 extern int soft_filter;
43
44 /* memory card data */
45 extern char Mcd1Data[MCD_SIZE];
46 extern char McdDisable[2];
47
48 /* PCSX ReARMed core calls and stuff */
49 int in_type1, in_type2;
50 int in_a1[2] = { 127, 127 }, in_a2[2] = { 127, 127 };
51 int in_keystate;
52 int in_enable_vibration;
53
54 static void init_memcard(char *mcd_data)
55 {
56         unsigned off = 0;
57         unsigned i;
58
59         memset(mcd_data, 0, MCD_SIZE);
60
61         mcd_data[off++] = 'M';
62         mcd_data[off++] = 'C';
63         off += 0x7d;
64         mcd_data[off++] = 0x0e;
65
66         for (i = 0; i < 15; i++) {
67                 mcd_data[off++] = 0xa0;
68                 off += 0x07;
69                 mcd_data[off++] = 0xff;
70                 mcd_data[off++] = 0xff;
71                 off += 0x75;
72                 mcd_data[off++] = 0xa0;
73         }
74
75         for (i = 0; i < 20; i++) {
76                 mcd_data[off++] = 0xff;
77                 mcd_data[off++] = 0xff;
78                 mcd_data[off++] = 0xff;
79                 mcd_data[off++] = 0xff;
80                 off += 0x04;
81                 mcd_data[off++] = 0xff;
82                 mcd_data[off++] = 0xff;
83                 off += 0x76;
84         }
85 }
86
87 static int vout_open(void)
88 {
89         return 0;
90 }
91
92 static void vout_set_mode(int w, int h, int raw_w, int raw_h, int bpp)
93 {
94         vout_width = w;
95         vout_height = h;
96 }
97
98 #ifndef FRONTEND_SUPPORTS_RGB565
99 static void convert(void *buf, size_t bytes)
100 {
101         unsigned int i, v, *p = buf;
102
103         for (i = 0; i < bytes / 4; i++) {
104                 v = p[i];
105                 p[i] = (v & 0x001f001f) | ((v >> 1) & 0x7fe07fe0);
106         }
107 }
108 #endif
109
110 static void vout_flip(const void *vram, int stride, int bgr24, int w, int h)
111 {
112         unsigned short *dest = vout_buf;
113         const unsigned short *src = vram;
114         int dstride = vout_width, h1 = h;
115         int doffs;
116
117         if (vram == NULL) {
118                 // blanking
119                 memset(vout_buf, 0, dstride * h * 2);
120                 goto out;
121         }
122
123         doffs = (vout_height - h) * dstride;
124         doffs += (dstride - w) / 2 & ~1;
125         if (doffs != vout_doffs_old) {
126                 // clear borders
127                 memset(vout_buf, 0, dstride * h * 2);
128                 vout_doffs_old = doffs;
129         }
130         dest += doffs;
131
132         if (bgr24)
133         {
134                 // XXX: could we switch to RETRO_PIXEL_FORMAT_XRGB8888 here?
135                 for (; h1-- > 0; dest += dstride, src += stride)
136                 {
137                         bgr888_to_rgb565(dest, src, w * 3);
138                 }
139         }
140         else
141         {
142                 for (; h1-- > 0; dest += dstride, src += stride)
143                 {
144                         bgr555_to_rgb565(dest, src, w * 2);
145                 }
146         }
147
148 out:
149 #ifndef FRONTEND_SUPPORTS_RGB565
150         convert(vout_buf, vout_width * vout_height * 2);
151 #endif
152         vout_fb_dirty = 1;
153         pl_rearmed_cbs.flip_cnt++;
154 }
155
156 static void vout_close(void)
157 {
158 }
159
160 static void *pl_mmap(unsigned int size)
161 {
162         return psxMap(0, size, 0, MAP_TAG_VRAM);
163 }
164
165 static void pl_munmap(void *ptr, unsigned int size)
166 {
167         psxUnmap(ptr, size, MAP_TAG_VRAM);
168 }
169
170 struct rearmed_cbs pl_rearmed_cbs = {
171         .pl_vout_open = vout_open,
172         .pl_vout_set_mode = vout_set_mode,
173         .pl_vout_flip = vout_flip,
174         .pl_vout_close = vout_close,
175         .mmap = pl_mmap,
176         .munmap = pl_munmap,
177         /* from psxcounters */
178         .gpu_hcnt = &hSyncCount,
179         .gpu_frame_count = &frame_counter,
180 };
181
182 void pl_frame_limit(void)
183 {
184         /* called once per frame, make psxCpu->Execute() above return */
185         stop = 1;
186 }
187
188 void pl_timing_prepare(int is_pal)
189 {
190         is_pal_mode = is_pal;
191 }
192
193 void plat_trigger_vibrate(int is_strong)
194 {
195 }
196
197 void pl_update_gun(int *xn, int *yn, int *xres, int *yres, int *in)
198 {
199 }
200
201 /* sound calls */
202 static int snd_init(void)
203 {
204         return 0;
205 }
206
207 static void snd_finish(void)
208 {
209 }
210
211 static int snd_busy(void)
212 {
213         if (samples_to_send > samples_sent)
214                 return 0; /* give more samples */
215         else
216                 return 1;
217 }
218
219 static void snd_feed(void *buf, int bytes)
220 {
221         audio_batch_cb(buf, bytes / 4);
222         samples_sent += bytes / 4;
223 }
224
225 void out_register_libretro(struct out_driver *drv)
226 {
227         drv->name = "libretro";
228         drv->init = snd_init;
229         drv->finish = snd_finish;
230         drv->busy = snd_busy;
231         drv->feed = snd_feed;
232 }
233
234 /* libretro */
235 void retro_set_environment(retro_environment_t cb)
236 {
237    static const struct retro_variable vars[] = {
238       { "frameskip", "Frameskip; 0|1|2|3" },
239 #ifdef __ARM_NEON__
240       { "soft_filter", "Software filter; none|scale2x|eagle2x" },
241       { "neon_enhancement_enable", "Enhanced resolution (slow); disabled|enabled" },
242 #endif
243       { NULL, NULL },
244    };
245
246    environ_cb = cb;
247
248    cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void*)vars);
249 }
250
251 void retro_set_video_refresh(retro_video_refresh_t cb) { video_cb = cb; }
252 void retro_set_audio_sample(retro_audio_sample_t cb) { (void)cb; }
253 void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) { audio_batch_cb = cb; }
254 void retro_set_input_poll(retro_input_poll_t cb) { input_poll_cb = cb; }
255 void retro_set_input_state(retro_input_state_t cb) { input_state_cb = cb; }
256
257 unsigned retro_api_version(void)
258 {
259         return RETRO_API_VERSION;
260 }
261
262 void retro_set_controller_port_device(unsigned port, unsigned device)
263 {
264 }
265
266 void retro_get_system_info(struct retro_system_info *info)
267 {
268         memset(info, 0, sizeof(*info));
269         info->library_name = "PCSX-ReARMed";
270         info->library_version = "r19";
271         info->valid_extensions = "bin|cue|img|mdf|pbp|toc|cbn|m3u";
272         info->need_fullpath = true;
273 }
274
275 void retro_get_system_av_info(struct retro_system_av_info *info)
276 {
277         memset(info, 0, sizeof(*info));
278         info->timing.fps            = is_pal_mode ? 50 : 60;
279         info->timing.sample_rate    = 44100;
280         info->geometry.base_width   = 320;
281         info->geometry.base_height  = 240;
282         info->geometry.max_width    = 640;
283         info->geometry.max_height   = 512;
284         info->geometry.aspect_ratio = 4.0 / 3.0;
285 }
286
287 /* savestates */
288 size_t retro_serialize_size(void) 
289
290         // it's currently 4380651 bytes, but have some reserved for future
291         return 0x430000;
292 }
293
294 struct save_fp {
295         char *buf;
296         size_t pos;
297         int is_write;
298 };
299
300 static void *save_open(const char *name, const char *mode)
301 {
302         struct save_fp *fp;
303
304         if (name == NULL || mode == NULL)
305                 return NULL;
306
307         fp = malloc(sizeof(*fp));
308         if (fp == NULL)
309                 return NULL;
310
311         fp->buf = (char *)name;
312         fp->pos = 0;
313         fp->is_write = (mode[0] == 'w' || mode[1] == 'w');
314
315         return fp;
316 }
317
318 static int save_read(void *file, void *buf, u32 len)
319 {
320         struct save_fp *fp = file;
321         if (fp == NULL || buf == NULL)
322                 return -1;
323
324         memcpy(buf, fp->buf + fp->pos, len);
325         fp->pos += len;
326         return len;
327 }
328
329 static int save_write(void *file, const void *buf, u32 len)
330 {
331         struct save_fp *fp = file;
332         if (fp == NULL || buf == NULL)
333                 return -1;
334
335         memcpy(fp->buf + fp->pos, buf, len);
336         fp->pos += len;
337         return len;
338 }
339
340 static long save_seek(void *file, long offs, int whence)
341 {
342         struct save_fp *fp = file;
343         if (fp == NULL)
344                 return -1;
345
346         switch (whence) {
347         case SEEK_CUR:
348                 fp->pos += offs;
349                 return fp->pos;
350         case SEEK_SET:
351                 fp->pos = offs;
352                 return fp->pos;
353         default:
354                 return -1;
355         }
356 }
357
358 static void save_close(void *file)
359 {
360         struct save_fp *fp = file;
361         size_t r_size = retro_serialize_size();
362         if (fp == NULL)
363                 return;
364
365         if (fp->pos > r_size)
366                 SysPrintf("ERROR: save buffer overflow detected\n");
367         else if (fp->is_write && fp->pos < r_size)
368                 // make sure we don't save trash in leftover space
369                 memset(fp->buf + fp->pos, 0, r_size - fp->pos);
370         free(fp);
371 }
372
373 bool retro_serialize(void *data, size_t size)
374
375         int ret = SaveState(data);
376         return ret == 0 ? true : false;
377 }
378
379 bool retro_unserialize(const void *data, size_t size)
380 {
381         int ret = LoadState(data);
382         return ret == 0 ? true : false;
383 }
384
385 /* cheats */
386 void retro_cheat_reset(void)
387 {
388         ClearAllCheats();
389 }
390
391 void retro_cheat_set(unsigned index, bool enabled, const char *code)
392 {
393         char buf[256];
394         int ret;
395
396         // cheat funcs are destructive, need a copy..
397         strncpy(buf, code, sizeof(buf));
398         buf[sizeof(buf) - 1] = 0;
399
400         if (index < NumCheats)
401                 ret = EditCheat(index, "", buf);
402         else
403                 ret = AddCheat("", buf);
404
405         if (ret != 0)
406                 SysPrintf("Failed to set cheat %#u\n", index);
407         else if (index < NumCheats)
408                 Cheats[index].Enabled = enabled;
409 }
410
411 /* multidisk support */
412 static bool disk_ejected;
413 static unsigned int disk_current_index;
414 static unsigned int disk_count;
415 static struct disks_state {
416         char *fname;
417         int internal_index; // for multidisk eboots
418 } disks[8];
419
420 static bool disk_set_eject_state(bool ejected)
421 {
422         // weird PCSX API..
423         SetCdOpenCaseTime(ejected ? -1 : (time(NULL) + 2));
424         LidInterrupt();
425
426         disk_ejected = ejected;
427         return true;
428 }
429
430 static bool disk_get_eject_state(void)
431 {
432         /* can't be controlled by emulated software */
433         return disk_ejected;
434 }
435
436 static unsigned int disk_get_image_index(void)
437 {
438         return disk_current_index;
439 }
440
441 static bool disk_set_image_index(unsigned int index)
442 {
443         if (index >= sizeof(disks) / sizeof(disks[0]))
444                 return false;
445
446         CdromId[0] = '\0';
447         CdromLabel[0] = '\0';
448
449         if (disks[index].fname == NULL) {
450                 SysPrintf("missing disk #%u\n", index);
451                 CDR_shutdown();
452
453                 // RetroArch specifies "no disk" with index == count,
454                 // so don't fail here..
455                 disk_current_index = index;
456                 return true;
457         }
458
459         SysPrintf("switching to disk %u: \"%s\" #%d\n", index,
460                 disks[index].fname, disks[index].internal_index);
461
462         cdrIsoMultidiskSelect = disks[index].internal_index;
463         set_cd_image(disks[index].fname);
464         if (ReloadCdromPlugin() < 0) {
465                 SysPrintf("failed to load cdr plugin\n");
466                 return false;
467         }
468         if (CDR_open() < 0) {
469                 SysPrintf("failed to open cdr plugin\n");
470                 return false;
471         }
472
473         if (!disk_ejected) {
474                 SetCdOpenCaseTime(time(NULL) + 2);
475                 LidInterrupt();
476         }
477
478         disk_current_index = index;
479         return true;
480 }
481
482 static unsigned int disk_get_num_images(void)
483 {
484         return disk_count;
485 }
486
487 static bool disk_replace_image_index(unsigned index,
488         const struct retro_game_info *info)
489 {
490         char *old_fname;
491         bool ret = true;
492
493         if (index >= sizeof(disks) / sizeof(disks[0]))
494                 return false;
495
496         old_fname = disks[index].fname;
497         disks[index].fname = NULL;
498         disks[index].internal_index = 0;
499
500         if (info != NULL) {
501                 disks[index].fname = strdup(info->path);
502                 if (index == disk_current_index)
503                         ret = disk_set_image_index(index);
504         }
505
506         if (old_fname != NULL)
507                 free(old_fname);
508
509         return ret;
510 }
511
512 static bool disk_add_image_index(void)
513 {
514         if (disk_count >= 8)
515                 return false;
516
517         disk_count++;
518         return true;
519 }
520
521 static struct retro_disk_control_callback disk_control = {
522         .set_eject_state = disk_set_eject_state,
523         .get_eject_state = disk_get_eject_state,
524         .get_image_index = disk_get_image_index,
525         .set_image_index = disk_set_image_index,
526         .get_num_images = disk_get_num_images,
527         .replace_image_index = disk_replace_image_index,
528         .add_image_index = disk_add_image_index,
529 };
530
531 // just in case, maybe a win-rt port in the future?
532 #ifdef _WIN32
533 #define SLASH '\\'
534 #else
535 #define SLASH '/'
536 #endif
537
538 static char base_dir[PATH_MAX];
539
540 static bool read_m3u(const char *file)
541 {
542         char line[PATH_MAX];
543         char name[PATH_MAX];
544         FILE *f = fopen(file, "r");
545         if (!f)
546                 return false;
547
548         while (fgets(line, sizeof(line), f) && disk_count < sizeof(disks) / sizeof(disks[0])) {
549                 if (line[0] == '#')
550                         continue;
551                 char *carrige_return = strchr(line, '\r');
552                 if (carrige_return)
553                         *carrige_return = '\0';
554                 char *newline = strchr(line, '\n');
555                 if (newline)
556                         *newline = '\0';
557
558                 if (line[0] != '\0')
559                 {
560                         snprintf(name, sizeof(name), "%s%c%s", base_dir, SLASH, line);
561                         disks[disk_count++].fname = strdup(name);
562                 }
563         }
564
565         fclose(f);
566         return (disk_count != 0);
567 }
568
569 static void extract_directory(char *buf, const char *path, size_t size)
570 {
571    char *base;
572    strncpy(buf, path, size - 1);
573    buf[size - 1] = '\0';
574
575    base = strrchr(buf, '/');
576    if (!base)
577       base = strrchr(buf, '\\');
578
579    if (base)
580       *base = '\0';
581    else
582    {
583       buf[0] = '.';
584       buf[1] = '\0';
585    }
586 }
587
588 bool retro_load_game(const struct retro_game_info *info)
589 {
590         size_t i;
591         bool is_m3u = (strcasestr(info->path, ".m3u") != NULL);
592
593 #ifdef FRONTEND_SUPPORTS_RGB565
594         enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_RGB565;
595         if (environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt)) {
596                 SysPrintf("RGB565 supported, using it\n");
597         }
598 #endif
599
600         if (info == NULL || info->path == NULL) {
601                 SysPrintf("info->path required\n");
602                 return false;
603         }
604
605         if (plugins_opened) {
606                 ClosePlugins();
607                 plugins_opened = 0;
608         }
609
610         for (i = 0; i < sizeof(disks) / sizeof(disks[0]); i++) {
611                 if (disks[i].fname != NULL) {
612                         free(disks[i].fname);
613                         disks[i].fname = NULL;
614                 }
615                 disks[i].internal_index = 0;
616         }
617
618         disk_current_index = 0;
619         extract_directory(base_dir, info->path, sizeof(base_dir));
620
621         if (is_m3u) {
622                 if (!read_m3u(info->path)) {
623                         SysPrintf("failed to read m3u file\n");
624                         return false;
625                 }
626         } else {
627                 disk_count = 1;
628                 disks[0].fname = strdup(info->path);
629         }
630
631         set_cd_image(disks[0].fname);
632
633         /* have to reload after set_cd_image for correct cdr plugin */
634         if (LoadPlugins() == -1) {
635                 SysPrintf("failed to load plugins\n");
636                 return false;
637         }
638
639         plugins_opened = 1;
640         NetOpened = 0;
641
642         if (OpenPlugins() == -1) {
643                 SysPrintf("failed to open plugins\n");
644                 return false;
645         }
646
647         plugin_call_rearmed_cbs();
648
649         Config.PsxAuto = 1;
650         if (CheckCdrom() == -1) {
651                 SysPrintf("unsupported/invalid CD image: %s\n", info->path);
652                 return false;
653         }
654
655         SysReset();
656
657         if (LoadCdrom() == -1) {
658                 SysPrintf("could not load CD-ROM!\n");
659                 return false;
660         }
661         emu_on_new_cd(0);
662
663         // multidisk images
664         if (!is_m3u) {
665                 disk_count = cdrIsoMultidiskCount < 8 ? cdrIsoMultidiskCount : 8;
666                 for (i = 1; i < sizeof(disks) / sizeof(disks[0]) && i < cdrIsoMultidiskCount; i++) {
667                         disks[i].fname = strdup(info->path);
668                         disks[i].internal_index = i;
669                 }
670         }
671
672         return true;
673 }
674
675 bool retro_load_game_special(unsigned game_type, const struct retro_game_info *info, size_t num_info)
676 {
677         return false;
678 }
679
680 void retro_unload_game(void) 
681 {
682 }
683
684 unsigned retro_get_region(void)
685 {
686         return is_pal_mode ? RETRO_REGION_PAL : RETRO_REGION_NTSC;
687 }
688
689 void *retro_get_memory_data(unsigned id)
690 {
691         if (id == RETRO_MEMORY_SAVE_RAM)
692                 return Mcd1Data;
693         else
694                 return NULL;
695 }
696
697 size_t retro_get_memory_size(unsigned id)
698 {
699         if (id == RETRO_MEMORY_SAVE_RAM)
700                 return MCD_SIZE;
701         else
702                 return 0;
703 }
704
705 void retro_reset(void)
706 {
707         SysReset();
708 }
709
710 static const unsigned short retro_psx_map[] = {
711         [RETRO_DEVICE_ID_JOYPAD_B]      = 1 << DKEY_CROSS,
712         [RETRO_DEVICE_ID_JOYPAD_Y]      = 1 << DKEY_SQUARE,
713         [RETRO_DEVICE_ID_JOYPAD_SELECT] = 1 << DKEY_SELECT,
714         [RETRO_DEVICE_ID_JOYPAD_START]  = 1 << DKEY_START,
715         [RETRO_DEVICE_ID_JOYPAD_UP]     = 1 << DKEY_UP,
716         [RETRO_DEVICE_ID_JOYPAD_DOWN]   = 1 << DKEY_DOWN,
717         [RETRO_DEVICE_ID_JOYPAD_LEFT]   = 1 << DKEY_LEFT,
718         [RETRO_DEVICE_ID_JOYPAD_RIGHT]  = 1 << DKEY_RIGHT,
719         [RETRO_DEVICE_ID_JOYPAD_A]      = 1 << DKEY_CIRCLE,
720         [RETRO_DEVICE_ID_JOYPAD_X]      = 1 << DKEY_TRIANGLE,
721         [RETRO_DEVICE_ID_JOYPAD_L]      = 1 << DKEY_L1,
722         [RETRO_DEVICE_ID_JOYPAD_R]      = 1 << DKEY_R1,
723         [RETRO_DEVICE_ID_JOYPAD_L2]     = 1 << DKEY_L2,
724         [RETRO_DEVICE_ID_JOYPAD_R2]     = 1 << DKEY_R2,
725         [RETRO_DEVICE_ID_JOYPAD_L3]     = 1 << DKEY_L3,
726         [RETRO_DEVICE_ID_JOYPAD_R3]     = 1 << DKEY_R3,
727 };
728 #define RETRO_PSX_MAP_LEN (sizeof(retro_psx_map) / sizeof(retro_psx_map[0]))
729
730 static void update_variables(void)
731 {
732    struct retro_variable var;
733    
734    var.value = NULL;
735    var.key = "frameskip";
736
737    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
738       pl_rearmed_cbs.frameskip = atoi(var.value);
739 #ifdef __ARM_NEON__
740    var.value = NULL;
741    var.key = "soft_filter";
742
743    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
744    {
745       if (strcmp(var.value, "none"))
746          soft_filter = 0;
747       else if (strcmp(var.value, "scale2x"))
748          soft_filter = 1;
749       else if (strcmp(var.value, "eagle2x"))
750          soft_filter = 2;
751    }
752
753    var.value = NULL;
754    var.key = "neon_enhancement_enable";
755
756    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value)
757    {
758       if (strcmp(var.value, "disabled") == 0)
759          pl_rearmed_cbs.gpu_neon.enhancement_enable = 0;
760       else if (strcmp(var.value, "enabled") == 0)
761          pl_rearmed_cbs.gpu_neon.enhancement_enable = 1;
762    }
763 #endif
764 }
765
766 void retro_run(void) 
767 {
768         int i;
769
770         input_poll_cb();
771
772    bool updated = false;
773    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated)
774       update_variables();
775
776         in_keystate = 0;
777         for (i = 0; i < RETRO_PSX_MAP_LEN; i++)
778                 if (input_state_cb(1, RETRO_DEVICE_JOYPAD, 0, i))
779                         in_keystate |= retro_psx_map[i];
780         in_keystate <<= 16;
781         for (i = 0; i < RETRO_PSX_MAP_LEN; i++)
782                 if (input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, i))
783                         in_keystate |= retro_psx_map[i];
784
785         stop = 0;
786         psxCpu->Execute();
787
788         samples_to_send += is_pal_mode ? 44100 / 50 : 44100 / 60;
789
790         video_cb((vout_fb_dirty || !vout_can_dupe) ? vout_buf : NULL,
791                 vout_width, vout_height, vout_width * 2);
792         vout_fb_dirty = 0;
793 }
794
795 void retro_init(void)
796 {
797         const char *bios[] = { "scph1001", "scph5501", "scph7001" };
798         const char *dir;
799         char path[256];
800         FILE *f = NULL;
801         int i, ret, level;
802
803         ret = emu_core_preinit();
804         ret |= emu_core_init();
805         if (ret != 0) {
806                 SysPrintf("PCSX init failed.\n");
807                 exit(1);
808         }
809
810         vout_buf = malloc(640 * 512 * 2);
811
812         if (environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir) && dir)
813         {
814                 snprintf(Config.BiosDir, sizeof(Config.BiosDir), "%s/", dir);
815
816                 for (i = 0; i < sizeof(bios) / sizeof(bios[0]); i++) {
817                         snprintf(path, sizeof(path), "%s/%s.bin", dir, bios[i]);
818                         f = fopen(path, "r");
819                         if (f != NULL) {
820                                 snprintf(Config.Bios, sizeof(Config.Bios), "%s.bin", bios[i]);
821                                 break;
822                         }
823                 }
824         }
825         if (f != NULL) {
826                 SysPrintf("found BIOS file: %s\n", Config.Bios);
827                 fclose(f);
828         }
829         else
830                 SysPrintf("no BIOS files found.\n");
831
832         level = 1;
833         environ_cb(RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL, &level);
834
835         environ_cb(RETRO_ENVIRONMENT_GET_CAN_DUPE, &vout_can_dupe);
836         environ_cb(RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE, &disk_control);
837
838         /* Set how much slower PSX CPU runs * 100 (so that 200 is 2 times)
839          * we have to do this because cache misses and some IO penalties
840          * are not emulated. Warning: changing this may break compatibility. */
841 #ifdef __ARM_ARCH_7A__
842         cycle_multiplier = 175;
843 #else
844         cycle_multiplier = 200;
845 #endif
846
847         McdDisable[0] = 0;
848         McdDisable[1] = 1;
849         init_memcard(Mcd1Data);
850
851         SaveFuncs.open = save_open;
852         SaveFuncs.read = save_read;
853         SaveFuncs.write = save_write;
854         SaveFuncs.seek = save_seek;
855         SaveFuncs.close = save_close;
856
857    update_variables();
858 }
859
860 void retro_deinit(void)
861 {
862         SysClose();
863         free(vout_buf);
864         vout_buf = NULL;
865 }