5f326dace85b6a1c92c84a5e0ce639a16b8ff78a
[picodrive.git] / platform / libretro / libretro.c
1 /*
2  * libretro core glue for PicoDrive
3  * (C) notaz, 2013
4  *
5  * This work is licensed under the terms of MAME license.
6  * See COPYING file in the top-level directory.
7  */
8
9 #define _GNU_SOURCE 1 // mremap
10 #include <stdio.h>
11 #include <stdarg.h>
12 #include <string.h>
13 #ifndef _WIN32
14 #ifndef NO_MMAP
15 #include <sys/mman.h>
16 #endif
17 #else
18 #include <io.h>
19 #include <windows.h>
20 #include <sys/types.h>
21 #endif
22 #include <errno.h>
23 #ifdef __MACH__
24 #include <libkern/OSCacheControl.h>
25 #endif
26
27 #ifdef _3DS
28 #include "3ds/3ds_utils.h"
29 #define MEMOP_MAP     4
30 #define MEMOP_UNMAP   5
31 #define MEMOP_PROT    6
32
33 int svcDuplicateHandle(unsigned int* out, unsigned int original);
34 int svcCloseHandle(unsigned int handle);
35 int svcControlProcessMemory(unsigned int process, void* addr0, void* addr1,
36                             unsigned int size, unsigned int type, unsigned int perm);
37 void* linearMemAlign(size_t size, size_t alignment);
38 void linearFree(void* mem);
39
40 static int ctr_svchack_successful = 0;
41
42 #elif defined(VITA)
43 #define TARGET_SIZE_2 24 // 2^24 = 16 megabytes
44
45 #include <psp2/kernel/sysmem.h>
46 static int sceBlock;
47 int getVMBlock();
48 int _newlib_vm_size_user = 1 << TARGET_SIZE_2;
49
50 #endif
51
52 #include <pico/pico_int.h>
53 #include <pico/state.h>
54 #include "../common/input_pico.h"
55 #include "../common/version.h"
56 #include "libretro.h"
57
58 static retro_log_printf_t log_cb;
59 static retro_video_refresh_t video_cb;
60 static retro_input_poll_t input_poll_cb;
61 static retro_input_state_t input_state_cb;
62 static retro_environment_t environ_cb;
63 static retro_audio_sample_batch_t audio_batch_cb;
64
65 #define VOUT_MAX_WIDTH 320
66 #define VOUT_MAX_HEIGHT 240
67
68 static const float VOUT_PAR = 0.0;
69 static const float VOUT_4_3 = (224.0f * (4.0f / 3.0f));
70 static const float VOUT_CRT = (224.0f * 1.29911f);
71
72 static void *vout_buf;
73 static int vout_width, vout_height, vout_offset;
74 static float user_vout_width = 0.0;
75
76 #ifdef _MSC_VER
77 static short sndBuffer[2*44100/50];
78 #else
79 static short __attribute__((aligned(4))) sndBuffer[2*44100/50];
80 #endif
81
82 static void snd_write(int len);
83
84 #ifdef _WIN32
85 #define SLASH '\\'
86 #else
87 #define SLASH '/'
88 #endif
89
90 /* functions called by the core */
91
92 void cache_flush_d_inval_i(void *start, void *end)
93 {
94 #ifdef __arm__
95    size_t len = (char *)end - (char *)start;
96 #if defined(__BLACKBERRY_QNX__)
97    msync(start, end - start, MS_SYNC | MS_CACHE_ONLY | MS_INVALIDATE_ICACHE);
98 #elif defined(__MACH__)
99    sys_dcache_flush(start, len);
100    sys_icache_invalidate(start, len);
101 #elif defined(_3DS)
102    ctr_flush_invalidate_cache();
103 #elif defined(VITA)
104    sceKernelSyncVMDomain(sceBlock, start, len);
105 #else
106    __clear_cache(start, end);
107 #endif
108 #endif
109 }
110
111 #ifdef _WIN32
112 /* mmap() replacement for Windows
113  *
114  * Author: Mike Frysinger <vapier@gentoo.org>
115  * Placed into the public domain
116  */
117
118 /* References:
119  * CreateFileMapping: http://msdn.microsoft.com/en-us/library/aa366537(VS.85).aspx
120  * CloseHandle:       http://msdn.microsoft.com/en-us/library/ms724211(VS.85).aspx
121  * MapViewOfFile:     http://msdn.microsoft.com/en-us/library/aa366761(VS.85).aspx
122  * UnmapViewOfFile:   http://msdn.microsoft.com/en-us/library/aa366882(VS.85).aspx
123  */
124
125 #define PROT_READ     0x1
126 #define PROT_WRITE    0x2
127 /* This flag is only available in WinXP+ */
128 #ifdef FILE_MAP_EXECUTE
129 #define PROT_EXEC     0x4
130 #else
131 #define PROT_EXEC        0x0
132 #define FILE_MAP_EXECUTE 0
133 #endif
134
135 #define MAP_SHARED    0x01
136 #define MAP_PRIVATE   0x02
137 #define MAP_ANONYMOUS 0x20
138 #define MAP_ANON      MAP_ANONYMOUS
139 #define MAP_FAILED    ((void *) -1)
140
141 #ifdef __USE_FILE_OFFSET64
142 # define DWORD_HI(x) (x >> 32)
143 # define DWORD_LO(x) ((x) & 0xffffffff)
144 #else
145 # define DWORD_HI(x) (0)
146 # define DWORD_LO(x) (x)
147 #endif
148
149 static void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset)
150 {
151    uint32_t flProtect, dwDesiredAccess;
152    off_t end;
153    HANDLE mmap_fd, h;
154    void *ret;
155
156    if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC))
157       return MAP_FAILED;
158    if (fd == -1) {
159       if (!(flags & MAP_ANON) || offset)
160          return MAP_FAILED;
161    } else if (flags & MAP_ANON)
162       return MAP_FAILED;
163
164    if (prot & PROT_WRITE) {
165       if (prot & PROT_EXEC)
166          flProtect = PAGE_EXECUTE_READWRITE;
167       else
168          flProtect = PAGE_READWRITE;
169    } else if (prot & PROT_EXEC) {
170       if (prot & PROT_READ)
171          flProtect = PAGE_EXECUTE_READ;
172       else if (prot & PROT_EXEC)
173          flProtect = PAGE_EXECUTE;
174    } else
175       flProtect = PAGE_READONLY;
176
177    end = length + offset;
178
179    if (fd == -1)
180       mmap_fd = INVALID_HANDLE_VALUE;
181    else
182       mmap_fd = (HANDLE)_get_osfhandle(fd);
183    h = CreateFileMapping(mmap_fd, NULL, flProtect, DWORD_HI(end), DWORD_LO(end), NULL);
184    if (h == NULL)
185       return MAP_FAILED;
186
187    if (prot & PROT_WRITE)
188       dwDesiredAccess = FILE_MAP_WRITE;
189    else
190       dwDesiredAccess = FILE_MAP_READ;
191    if (prot & PROT_EXEC)
192       dwDesiredAccess |= FILE_MAP_EXECUTE;
193    if (flags & MAP_PRIVATE)
194       dwDesiredAccess |= FILE_MAP_COPY;
195    ret = MapViewOfFile(h, dwDesiredAccess, DWORD_HI(offset), DWORD_LO(offset), length);
196    if (ret == NULL) {
197       CloseHandle(h);
198       ret = MAP_FAILED;
199    }
200    return ret;
201 }
202
203 static void munmap(void *addr, size_t length)
204 {
205    UnmapViewOfFile(addr);
206    /* ruh-ro, we leaked handle from CreateFileMapping() ... */
207 }
208 #elif defined(NO_MMAP)
209 #define PROT_EXEC   0x04
210 #define MAP_FAILED 0
211 #define PROT_READ 0
212 #define PROT_WRITE 0
213 #define MAP_PRIVATE 0
214 #define MAP_ANONYMOUS 0
215
216 void* mmap(void *desired_addr, size_t len, int mmap_prot, int mmap_flags, int fildes, size_t off)
217 {
218    return malloc(len);
219 }
220
221 void munmap(void *base_addr, size_t len)
222 {
223    free(base_addr);
224 }
225
226 int mprotect(void *addr, size_t len, int prot)
227 {
228    /* stub - not really needed at this point since this codepath has no dynarecs */
229    return 0;
230 }
231
232 #endif
233
234 #ifndef MAP_ANONYMOUS
235 #define MAP_ANONYMOUS MAP_ANON
236 #endif
237
238 #ifdef _3DS
239 typedef struct
240 {
241    unsigned int requested_map;
242    void* buffer;
243 }pico_mmap_t;
244
245 pico_mmap_t pico_mmaps[] = {
246    {0x02000000, 0},
247    {0x06000000, 0},
248    {NULL,       0}
249 };
250
251 void *plat_mmap(unsigned long addr, size_t size, int need_exec, int is_fixed)
252 {
253    (void)is_fixed;
254
255    if (ctr_svchack_successful)
256    {
257       pico_mmap_t* pico_mmap;
258
259       for (pico_mmap = pico_mmaps; pico_mmap->requested_map; pico_mmap++)
260       {
261          if ((pico_mmap->requested_map == addr))
262          {
263             unsigned int ptr_aligned, tmp;
264             unsigned int currentHandle;
265             unsigned int perm = 0b011;
266
267             if (need_exec)
268                perm = 0b111;
269
270             size = (size + 0xFFF) & ~0xFFF;
271             pico_mmap->buffer = malloc(size + 0x1000);
272             ptr_aligned = (((unsigned int)pico_mmap->buffer) + 0xFFF) & ~0xFFF;
273
274             svcDuplicateHandle(&currentHandle, 0xFFFF8001);
275
276             if(svcControlProcessMemory(currentHandle, pico_mmap->requested_map, ptr_aligned, size, MEMOP_MAP, perm) < 0)
277             {
278                if (log_cb)
279                   log_cb(RETRO_LOG_ERROR, "could not map memory @0x%08X\n", pico_mmap->requested_map);
280                exit(1);
281             }
282
283             svcCloseHandle(currentHandle);
284             return (void*)pico_mmap->requested_map;
285          }
286       }
287    }
288
289    return malloc(size);
290 }
291
292 void *plat_mremap(void *ptr, size_t oldsize, size_t newsize)
293 {
294    if (ctr_svchack_successful)
295    {
296       pico_mmap_t* pico_mmap;
297
298       for (pico_mmap = pico_mmaps; pico_mmap->requested_map; pico_mmap++)
299       {
300          if ((pico_mmap->requested_map == (unsigned int)ptr))
301          {
302             unsigned int ptr_aligned;
303             unsigned int currentHandle;
304             void* tmp;
305
306             oldsize = (oldsize + 0xFFF) & ~0xFFF;
307             newsize = (newsize + 0xFFF) & ~0xFFF;
308             ptr_aligned = (((unsigned int)pico_mmap->buffer) + 0xFFF) & ~0xFFF;
309
310             svcDuplicateHandle(&currentHandle, 0xFFFF8001);
311
312             svcControlProcessMemory(currentHandle, pico_mmap->requested_map, ptr_aligned, oldsize, MEMOP_UNMAP, 0b011);
313
314             tmp = realloc(pico_mmap->buffer, newsize + 0x1000);
315             if(!tmp)
316                return NULL;
317
318             pico_mmap->buffer = tmp;
319             ptr_aligned = (((unsigned int)pico_mmap->buffer) + 0xFFF) & ~0xFFF;
320
321             svcControlProcessMemory(currentHandle, pico_mmap->requested_map, ptr_aligned, newsize, MEMOP_MAP, 0x3);
322
323             svcCloseHandle(currentHandle);
324
325             return ptr;
326          }
327       }
328    }
329
330    return realloc(ptr, newsize);
331
332 }
333 void plat_munmap(void *ptr, size_t size)
334 {
335    if (ctr_svchack_successful)
336    {
337       pico_mmap_t* pico_mmap;
338
339       for (pico_mmap = pico_mmaps; pico_mmap->requested_map; pico_mmap++)
340       {
341          if ((pico_mmap->requested_map == (unsigned int)ptr))
342          {
343             unsigned int ptr_aligned;
344             unsigned int currentHandle;
345
346             size = (size + 0xFFF) & ~0xFFF;
347             ptr_aligned = (((unsigned int)pico_mmap->buffer) + 0xFFF) & ~0xFFF;
348
349             svcDuplicateHandle(&currentHandle, 0xFFFF8001);
350
351             svcControlProcessMemory(currentHandle, (void*)pico_mmap->requested_map, (void*)ptr_aligned, size, MEMOP_UNMAP, 0b011);
352
353             svcCloseHandle(currentHandle);
354
355             free(pico_mmap->buffer);
356             pico_mmap->buffer = NULL;
357             return;
358          }
359       }
360    }
361
362    free(ptr);
363 }
364
365 #else
366 void *plat_mmap(unsigned long addr, size_t size, int need_exec, int is_fixed)
367 {
368    int flags = MAP_PRIVATE | MAP_ANONYMOUS;
369    void *req, *ret;
370
371    req = (void *)addr;
372    ret = mmap(req, size, PROT_READ | PROT_WRITE, flags, -1, 0);
373    if (ret == MAP_FAILED) {
374       if (log_cb)
375          log_cb(RETRO_LOG_ERROR, "mmap(%08lx, %zd) failed: %d\n", addr, size, errno);
376       return NULL;
377    }
378
379    if (addr != 0 && ret != (void *)addr) {
380       if (log_cb)
381          log_cb(RETRO_LOG_WARN, "warning: wanted to map @%08lx, got %p\n",
382                addr, ret);
383
384       if (is_fixed) {
385          munmap(ret, size);
386          return NULL;
387       }
388    }
389
390    return ret;
391 }
392
393 void *plat_mremap(void *ptr, size_t oldsize, size_t newsize)
394 {
395 #ifdef __linux__
396    void *ret = mremap(ptr, oldsize, newsize, 0);
397    if (ret == MAP_FAILED)
398       return NULL;
399
400    return ret;
401 #else
402    void *tmp, *ret;
403    size_t preserve_size;
404
405    preserve_size = oldsize;
406    if (preserve_size > newsize)
407       preserve_size = newsize;
408    tmp = malloc(preserve_size);
409    if (tmp == NULL)
410       return NULL;
411    memcpy(tmp, ptr, preserve_size);
412
413    munmap(ptr, oldsize);
414    ret = mmap(ptr, newsize, PROT_READ | PROT_WRITE,
415          MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
416    if (ret == MAP_FAILED) {
417       free(tmp);
418       return NULL;
419    }
420    memcpy(ret, tmp, preserve_size);
421    free(tmp);
422    return ret;
423 #endif
424 }
425
426 void plat_munmap(void *ptr, size_t size)
427 {
428    if (ptr != NULL)
429       munmap(ptr, size);
430 }
431 #endif
432
433 int plat_mem_set_exec(void *ptr, size_t size)
434 {
435 #ifdef _WIN32
436    int ret = VirtualProtect(ptr,size,PAGE_EXECUTE_READWRITE,0);
437    if (ret == 0 && log_cb)
438       log_cb(RETRO_LOG_ERROR, "mprotect(%p, %zd) failed: %d\n", ptr, size, 0);
439 #elif defined(_3DS)
440    int ret = -1;
441    if (ctr_svchack_successful)
442    {
443       unsigned int currentHandle;
444       svcDuplicateHandle(&currentHandle, 0xFFFF8001);
445       ret = svcControlProcessMemory(currentHandle, ptr, 0x0,
446                               size, MEMOP_PROT, 0b111);
447       svcCloseHandle(currentHandle);
448       ctr_flush_invalidate_cache();
449
450    }
451    else
452    {
453       if (log_cb)
454          log_cb(RETRO_LOG_ERROR, "plat_mem_set_exec called with no svcControlProcessMemory access\n");
455       exit(1);
456    }
457
458 #elif defined(VITA)
459    int ret = sceKernelOpenVMDomain();
460 #else
461    int ret = mprotect(ptr, size, PROT_READ | PROT_WRITE | PROT_EXEC);
462    if (ret != 0 && log_cb)
463       log_cb(RETRO_LOG_ERROR, "mprotect(%p, %zd) failed: %d\n", ptr, size, errno);
464 #endif
465    return ret;
466 }
467
468 void emu_video_mode_change(int start_line, int line_count, int is_32cols)
469 {
470    memset(vout_buf, 0, 320 * 240 * 2);
471    vout_width = is_32cols ? 256 : 320;
472    PicoDrawSetOutBuf(vout_buf, vout_width * 2);
473
474    vout_height = line_count;
475    vout_offset = vout_width * start_line;
476
477    // Update the geometry
478    struct retro_system_av_info av_info;
479    retro_get_system_av_info(&av_info);
480    environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &av_info);
481 }
482
483 void emu_32x_startup(void)
484 {
485 }
486
487 void lprintf(const char *fmt, ...)
488 {
489    char buffer[256];
490    va_list ap;
491    va_start(ap, fmt);
492    vsprintf(buffer, fmt, ap);
493    /* TODO - add 'level' param for warning/error messages? */
494    if (log_cb)
495       log_cb(RETRO_LOG_INFO, "%s", buffer);
496    va_end(ap);
497 }
498
499 /* libretro */
500 void retro_set_environment(retro_environment_t cb)
501 {
502    static const struct retro_variable vars[] = {
503       { "picodrive_input1",      "Input device 1; 3 button pad|6 button pad|None" },
504       { "picodrive_input2",      "Input device 2; 3 button pad|6 button pad|None" },
505       { "picodrive_sprlim",      "No sprite limit; disabled|enabled" },
506       { "picodrive_ramcart",     "MegaCD RAM cart; disabled|enabled" },
507       { "picodrive_region",      "Region; Auto|Japan NTSC|Japan PAL|US|Europe" },
508       { "picodrive_region_fps",  "Region FPS; Auto|NTSC|PAL" },
509       { "picodrive_aspect",      "Core-provided aspect ratio; PAR|4/3|CRT" },
510 #ifdef DRC_SH2
511       { "picodrive_drc", "Dynamic recompilers; enabled|disabled" },
512 #endif
513       { NULL, NULL },
514    };
515
516    environ_cb = cb;
517
518    cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void *)vars);
519 }
520
521 void retro_set_video_refresh(retro_video_refresh_t cb) { video_cb = cb; }
522 void retro_set_audio_sample(retro_audio_sample_t cb) { (void)cb; }
523 void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) { audio_batch_cb = cb; }
524 void retro_set_input_poll(retro_input_poll_t cb) { input_poll_cb = cb; }
525 void retro_set_input_state(retro_input_state_t cb) { input_state_cb = cb; }
526
527 unsigned retro_api_version(void)
528 {
529    return RETRO_API_VERSION;
530 }
531
532 void retro_set_controller_port_device(unsigned port, unsigned device)
533 {
534 }
535
536 void retro_get_system_info(struct retro_system_info *info)
537 {
538    memset(info, 0, sizeof(*info));
539    info->library_name = "PicoDrive";
540    info->library_version = VERSION;
541    info->valid_extensions = "bin|gen|smd|md|32x|cue|iso|sms";
542    info->need_fullpath = true;
543 }
544
545 void retro_get_system_av_info(struct retro_system_av_info *info)
546 {
547    memset(info, 0, sizeof(*info));
548    info->timing.fps            = Pico.m.pal ? 50 : 60;
549    info->timing.sample_rate    = 44100;
550    info->geometry.base_width   = vout_width;
551    info->geometry.base_height  = vout_height;
552    info->geometry.max_width    = vout_width;
553    info->geometry.max_height   = vout_height;
554    
555    float common_width = vout_width;
556    if (user_vout_width != 0)
557       common_width = user_vout_width;
558    
559    info->geometry.aspect_ratio = common_width / vout_height;
560 }
561
562 /* savestates */
563 struct savestate_state {
564    const char *load_buf;
565    char *save_buf;
566    size_t size;
567    size_t pos;
568 };
569
570 size_t state_read(void *p, size_t size, size_t nmemb, void *file)
571 {
572    struct savestate_state *state = file;
573    size_t bsize = size * nmemb;
574
575    if (state->pos + bsize > state->size) {
576       if (log_cb)
577          log_cb(RETRO_LOG_ERROR, "savestate error: %u/%u\n",
578                state->pos + bsize, state->size);
579       bsize = state->size - state->pos;
580       if ((int)bsize <= 0)
581          return 0;
582    }
583
584    memcpy(p, state->load_buf + state->pos, bsize);
585    state->pos += bsize;
586    return bsize;
587 }
588
589 size_t state_write(void *p, size_t size, size_t nmemb, void *file)
590 {
591    struct savestate_state *state = file;
592    size_t bsize = size * nmemb;
593
594    if (state->pos + bsize > state->size) {
595       if (log_cb)
596          log_cb(RETRO_LOG_ERROR, "savestate error: %u/%u\n",
597                state->pos + bsize, state->size);
598       bsize = state->size - state->pos;
599       if ((int)bsize <= 0)
600          return 0;
601    }
602
603    memcpy(state->save_buf + state->pos, p, bsize);
604    state->pos += bsize;
605    return bsize;
606 }
607
608 size_t state_skip(void *p, size_t size, size_t nmemb, void *file)
609 {
610    struct savestate_state *state = file;
611    size_t bsize = size * nmemb;
612
613    state->pos += bsize;
614    return bsize;
615 }
616
617 size_t state_eof(void *file)
618 {
619    struct savestate_state *state = file;
620
621    return state->pos >= state->size;
622 }
623
624 int state_fseek(void *file, long offset, int whence)
625 {
626    struct savestate_state *state = file;
627
628    switch (whence) {
629    case SEEK_SET:
630       state->pos = offset;
631       break;
632    case SEEK_CUR:
633       state->pos += offset;
634       break;
635    case SEEK_END:
636       state->pos = state->size + offset;
637       break;
638    }
639    return (int)state->pos;
640 }
641
642 /* savestate sizes vary wildly depending if cd/32x or
643  * carthw is active, so run the whole thing to get size */
644 size_t retro_serialize_size(void)
645 {
646    struct savestate_state state = { 0, };
647    int ret;
648
649    ret = PicoStateFP(&state, 1, NULL, state_skip, NULL, state_fseek);
650    if (ret != 0)
651       return 0;
652
653    return state.pos;
654 }
655
656 bool retro_serialize(void *data, size_t size)
657 {
658    struct savestate_state state = { 0, };
659    int ret;
660
661    state.save_buf = data;
662    state.size = size;
663    state.pos = 0;
664
665    ret = PicoStateFP(&state, 1, NULL, state_write,
666       NULL, state_fseek);
667    return ret == 0;
668 }
669
670 bool retro_unserialize(const void *data, size_t size)
671 {
672    struct savestate_state state = { 0, };
673    int ret;
674
675    state.load_buf = data;
676    state.size = size;
677    state.pos = 0;
678
679    ret = PicoStateFP(&state, 0, state_read, NULL,
680       state_eof, state_fseek);
681    return ret == 0;
682 }
683
684 /* cheats - TODO */
685 void retro_cheat_reset(void)
686 {
687 }
688
689 void retro_cheat_set(unsigned index, bool enabled, const char *code)
690 {
691 }
692
693 /* multidisk support */
694 static bool disk_ejected;
695 static unsigned int disk_current_index;
696 static unsigned int disk_count;
697 static struct disks_state {
698    char *fname;
699 } disks[8];
700
701 static bool disk_set_eject_state(bool ejected)
702 {
703    // TODO?
704    disk_ejected = ejected;
705    return true;
706 }
707
708 static bool disk_get_eject_state(void)
709 {
710    return disk_ejected;
711 }
712
713 static unsigned int disk_get_image_index(void)
714 {
715    return disk_current_index;
716 }
717
718 static bool disk_set_image_index(unsigned int index)
719 {
720    enum cd_img_type cd_type;
721    int ret;
722
723    if (index >= sizeof(disks) / sizeof(disks[0]))
724       return false;
725
726    if (disks[index].fname == NULL) {
727       if (log_cb)
728          log_cb(RETRO_LOG_ERROR, "missing disk #%u\n", index);
729
730       // RetroArch specifies "no disk" with index == count,
731       // so don't fail here..
732       disk_current_index = index;
733       return true;
734    }
735
736    if (log_cb)
737       log_cb(RETRO_LOG_INFO, "switching to disk %u: \"%s\"\n", index,
738             disks[index].fname);
739
740    ret = -1;
741    cd_type = PicoCdCheck(disks[index].fname, NULL);
742    if (cd_type != CIT_NOT_CD)
743       ret = cdd_load(disks[index].fname, cd_type);
744    if (ret != 0) {
745       if (log_cb)
746          log_cb(RETRO_LOG_ERROR, "Load failed, invalid CD image?\n");
747       return 0;
748    }
749
750    disk_current_index = index;
751    return true;
752 }
753
754 static unsigned int disk_get_num_images(void)
755 {
756    return disk_count;
757 }
758
759 static bool disk_replace_image_index(unsigned index,
760    const struct retro_game_info *info)
761 {
762    bool ret = true;
763
764    if (index >= sizeof(disks) / sizeof(disks[0]))
765       return false;
766
767    if (disks[index].fname != NULL)
768       free(disks[index].fname);
769    disks[index].fname = NULL;
770
771    if (info != NULL) {
772       disks[index].fname = strdup(info->path);
773       if (index == disk_current_index)
774          ret = disk_set_image_index(index);
775    }
776
777    return ret;
778 }
779
780 static bool disk_add_image_index(void)
781 {
782    if (disk_count >= sizeof(disks) / sizeof(disks[0]))
783       return false;
784
785    disk_count++;
786    return true;
787 }
788
789 static struct retro_disk_control_callback disk_control = {
790    disk_set_eject_state,
791    disk_get_eject_state,
792    disk_get_image_index,
793    disk_set_image_index,
794    disk_get_num_images,
795    disk_replace_image_index,
796    disk_add_image_index,
797 };
798
799 static void disk_tray_open(void)
800 {
801    if (log_cb)
802       log_cb(RETRO_LOG_INFO, "cd tray open\n");
803    disk_ejected = 1;
804 }
805
806 static void disk_tray_close(void)
807 {
808    if (log_cb)
809       log_cb(RETRO_LOG_INFO, "cd tray close\n");
810    disk_ejected = 0;
811 }
812
813
814 static const char * const biosfiles_us[] = {
815    "us_scd2_9306", "SegaCDBIOS9303", "us_scd1_9210", "bios_CD_U"
816 };
817 static const char * const biosfiles_eu[] = {
818    "eu_mcd2_9306", "eu_mcd2_9303", "eu_mcd1_9210", "bios_CD_E"
819 };
820 static const char * const biosfiles_jp[] = {
821    "jp_mcd2_921222", "jp_mcd1_9112", "jp_mcd1_9111", "bios_CD_J"
822 };
823
824 static void make_system_path(char *buf, size_t buf_size,
825    const char *name, const char *ext)
826 {
827    const char *dir = NULL;
828
829    if (environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir) && dir) {
830       snprintf(buf, buf_size, "%s%c%s%s", dir, SLASH, name, ext);
831    }
832    else {
833       snprintf(buf, buf_size, "%s%s", name, ext);
834    }
835 }
836
837 static const char *find_bios(int *region, const char *cd_fname)
838 {
839    const char * const *files;
840    static char path[256];
841    int i, count;
842    FILE *f = NULL;
843
844    if (*region == 4) { // US
845       files = biosfiles_us;
846       count = sizeof(biosfiles_us) / sizeof(char *);
847    } else if (*region == 8) { // EU
848       files = biosfiles_eu;
849       count = sizeof(biosfiles_eu) / sizeof(char *);
850    } else if (*region == 1 || *region == 2) {
851       files = biosfiles_jp;
852       count = sizeof(biosfiles_jp) / sizeof(char *);
853    } else {
854       return NULL;
855    }
856
857    for (i = 0; i < count; i++)
858    {
859       make_system_path(path, sizeof(path), files[i], ".bin");
860       f = fopen(path, "rb");
861       if (f != NULL)
862          break;
863
864       make_system_path(path, sizeof(path), files[i], ".zip");
865       f = fopen(path, "rb");
866       if (f != NULL)
867          break;
868    }
869
870    if (f != NULL) {
871       if (log_cb)
872          log_cb(RETRO_LOG_INFO, "using bios: %s\n", path);
873       fclose(f);
874       return path;
875    }
876
877    return NULL;
878 }
879
880 static void sram_reset()
881 {
882    SRam.data = NULL;
883    SRam.start = 0;
884    SRam.end = 0;
885    SRam.flags = '\0';
886    SRam.unused2 = '\0';
887    SRam.changed = '\0' ;
888    SRam.eeprom_type = '\0';
889    SRam.unused3 = '\0';
890    SRam.eeprom_bit_cl = '\0';
891    SRam.eeprom_bit_in = '\0';
892    SRam.eeprom_bit_out = '\0';
893    SRam.size = 0;
894 }
895
896 bool retro_load_game(const struct retro_game_info *info)
897 {
898    enum media_type_e media_type;
899    static char carthw_path[256];
900    size_t i;
901
902    struct retro_input_descriptor desc[] = {
903       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT,  "D-Pad Left" },
904       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP,    "D-Pad Up" },
905       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN,  "D-Pad Down" },
906       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
907       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B,     "B" },
908       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A,     "C" },
909       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X,     "Y" },
910       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y,     "A" },
911       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L,     "X" },
912       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R,     "Z" },
913       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,"Mode" },
914       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
915
916       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT,  "D-Pad Left" },
917       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP,    "D-Pad Up" },
918       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN,  "D-Pad Down" },
919       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
920       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B,     "B" },
921       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A,     "C" },
922       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X,     "Y" },
923       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y,     "A" },
924       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L,     "X" },
925       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R,     "Z" },
926       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,"Mode" },
927       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
928
929       { 0 },
930    };
931
932    struct retro_input_descriptor desc_sms[] = {
933       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT,  "D-Pad Left" },
934       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP,    "D-Pad Up" },
935       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN,  "D-Pad Down" },
936       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
937       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B,     "Button 1 Start" },
938       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A,     "Button 2" },
939       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Button Pause" },
940
941       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT,  "D-Pad Left" },
942       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP,    "D-Pad Up" },
943       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN,  "D-Pad Down" },
944       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
945       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B,     "Button 1 Start" },
946       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A,     "Button 2" },
947       { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Button Pause" },
948
949       { 0 },
950    };
951
952    sram_reset();
953
954    enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_RGB565;
955    if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt)) {
956       if (log_cb)
957          log_cb(RETRO_LOG_ERROR, "RGB565 support required, sorry\n");
958       return false;
959    }
960
961    if (info == NULL || info->path == NULL) {
962       if (log_cb)
963          log_cb(RETRO_LOG_ERROR, "info->path required\n");
964       return false;
965    }
966
967    for (i = 0; i < sizeof(disks) / sizeof(disks[0]); i++) {
968       if (disks[i].fname != NULL) {
969          free(disks[i].fname);
970          disks[i].fname = NULL;
971       }
972    }
973
974    disk_current_index = 0;
975    disk_count = 1;
976    disks[0].fname = strdup(info->path);
977
978    make_system_path(carthw_path, sizeof(carthw_path), "carthw", ".cfg");
979
980    media_type = PicoLoadMedia(info->path, carthw_path,
981          find_bios, NULL);
982
983    switch (media_type) {
984    case PM_BAD_DETECT:
985       if (log_cb)
986          log_cb(RETRO_LOG_ERROR, "Failed to detect ROM/CD image type.\n");
987       return false;
988    case PM_BAD_CD:
989       if (log_cb)
990          log_cb(RETRO_LOG_ERROR, "Invalid CD image\n");
991       return false;
992    case PM_BAD_CD_NO_BIOS:
993       if (log_cb)
994          log_cb(RETRO_LOG_ERROR, "Missing BIOS\n");
995       return false;
996    case PM_ERROR:
997       if (log_cb)
998          log_cb(RETRO_LOG_ERROR, "Load error\n");
999       return false;
1000    default:
1001       break;
1002    }
1003
1004    if (media_type == PM_MARK3)
1005       environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc_sms);
1006    else
1007       environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc);
1008
1009    PicoLoopPrepare();
1010
1011    PicoWriteSound = snd_write;
1012    memset(sndBuffer, 0, sizeof(sndBuffer));
1013    PsndOut = sndBuffer;
1014    PsndRerate(0);
1015
1016    return true;
1017 }
1018
1019 bool retro_load_game_special(unsigned game_type, const struct retro_game_info *info, size_t num_info)
1020 {
1021    return false;
1022 }
1023
1024 void retro_unload_game(void)
1025 {
1026 }
1027
1028 unsigned retro_get_region(void)
1029 {
1030    return Pico.m.pal ? RETRO_REGION_PAL : RETRO_REGION_NTSC;
1031 }
1032
1033 void *retro_get_memory_data(unsigned type)
1034 {
1035    uint8_t* data;
1036
1037    switch(type)
1038    {
1039       case RETRO_MEMORY_SAVE_RAM:
1040          if (PicoAHW & PAHW_MCD)
1041             data = Pico_mcd->bram;
1042          else
1043             data = SRam.data;
1044          break;
1045       case RETRO_MEMORY_SYSTEM_RAM:
1046          if (PicoAHW & PAHW_SMS)
1047             data = Pico.vramb;
1048          else
1049             data = Pico.ram;
1050          break;
1051       default:
1052          data = NULL;
1053          break;
1054    }
1055    
1056    return data;
1057 }
1058
1059 size_t retro_get_memory_size(unsigned type)
1060 {
1061    unsigned int i;
1062    int sum;
1063    
1064    switch(type)
1065    {
1066       case RETRO_MEMORY_SAVE_RAM:
1067          if (PicoAHW & PAHW_MCD)
1068             // bram
1069             return 0x2000;
1070
1071          if (Pico.m.frame_count == 0)
1072             return SRam.size;
1073
1074          // if game doesn't write to sram, don't report it to
1075          // libretro so that RA doesn't write out zeroed .srm
1076          for (i = 0, sum = 0; i < SRam.size; i++)
1077             sum |= SRam.data[i];
1078
1079          return (sum != 0) ? SRam.size : 0;
1080             
1081       case RETRO_MEMORY_SYSTEM_RAM:
1082          if (PicoAHW & PAHW_SMS)
1083             return sizeof(Pico.vramb);
1084          else
1085             return sizeof(Pico.ram);
1086             
1087       default:
1088          return 0;
1089    }
1090    
1091 }
1092
1093 void retro_reset(void)
1094 {
1095    PicoReset();
1096 }
1097
1098 static const unsigned short retro_pico_map[] = {
1099    1 << GBTN_B,
1100    1 << GBTN_A,
1101    1 << GBTN_MODE,
1102    1 << GBTN_START,
1103    1 << GBTN_UP,
1104    1 << GBTN_DOWN,
1105    1 << GBTN_LEFT,
1106    1 << GBTN_RIGHT,
1107    1 << GBTN_C,
1108    1 << GBTN_Y,
1109    1 << GBTN_X,
1110    1 << GBTN_Z,
1111 };
1112 #define RETRO_PICO_MAP_LEN (sizeof(retro_pico_map) / sizeof(retro_pico_map[0]))
1113
1114 static void snd_write(int len)
1115 {
1116    audio_batch_cb(PsndOut, len / 4);
1117 }
1118
1119 static enum input_device input_name_to_val(const char *name)
1120 {
1121    if (strcmp(name, "3 button pad") == 0)
1122       return PICO_INPUT_PAD_3BTN;
1123    if (strcmp(name, "6 button pad") == 0)
1124       return PICO_INPUT_PAD_6BTN;
1125    if (strcmp(name, "None") == 0)
1126       return PICO_INPUT_NOTHING;
1127
1128    if (log_cb)
1129       log_cb(RETRO_LOG_WARN, "invalid picodrive_input: '%s'\n", name);
1130    return PICO_INPUT_PAD_3BTN;
1131 }
1132
1133 static void update_variables(void)
1134 {
1135    struct retro_variable var;
1136
1137    var.value = NULL;
1138    var.key = "picodrive_input1";
1139    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
1140       PicoSetInputDevice(0, input_name_to_val(var.value));
1141
1142    var.value = NULL;
1143    var.key = "picodrive_input2";
1144    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
1145       PicoSetInputDevice(1, input_name_to_val(var.value));
1146
1147    var.value = NULL;
1148    var.key = "picodrive_sprlim";
1149    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1150       if (strcmp(var.value, "enabled") == 0)
1151          PicoOpt |= POPT_DIS_SPRITE_LIM;
1152       else
1153          PicoOpt &= ~POPT_DIS_SPRITE_LIM;
1154    }
1155
1156    var.value = NULL;
1157    var.key = "picodrive_ramcart";
1158    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1159       if (strcmp(var.value, "enabled") == 0)
1160          PicoOpt |= POPT_EN_MCD_RAMCART;
1161       else
1162          PicoOpt &= ~POPT_EN_MCD_RAMCART;
1163    }
1164
1165    int OldPicoRegionOverride = PicoRegionOverride;
1166    var.value = NULL;
1167    var.key = "picodrive_region";
1168    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1169       if (strcmp(var.value, "Auto") == 0)
1170          PicoRegionOverride = 0;
1171       else if (strcmp(var.value, "Japan NTSC") == 0)
1172          PicoRegionOverride = 1;
1173       else if (strcmp(var.value, "Japan PAL") == 0)
1174          PicoRegionOverride = 2;
1175       else if (strcmp(var.value, "US") == 0)
1176          PicoRegionOverride = 4;
1177       else if (strcmp(var.value, "Europe") == 0)
1178          PicoRegionOverride = 8;
1179    }
1180
1181    int OldPicoRegionFPSOverride = PicoRegionFPSOverride;
1182    var.value = NULL;
1183    var.key = "picodrive_region_fps";
1184    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1185       if (strcmp(var.value, "Auto") == 0)
1186          PicoRegionFPSOverride = 0;
1187       else if (strcmp(var.value, "NTSC") == 0)
1188          PicoRegionFPSOverride = 1;
1189       else if (strcmp(var.value, "PAL") == 0)
1190          PicoRegionFPSOverride = 2;         
1191    }
1192
1193    // Update region, fps and sound flags if needed
1194    if (PicoRegionOverride    != OldPicoRegionOverride ||
1195        PicoRegionFPSOverride != OldPicoRegionFPSOverride)
1196    {
1197       PicoDetectRegion();
1198       PicoLoopPrepare();
1199       PsndRerate(1);
1200    }
1201
1202    float old_user_vout_width = user_vout_width;
1203    var.value = NULL;
1204    var.key = "picodrive_aspect";
1205    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1206       if (strcmp(var.value, "4/3") == 0)
1207          user_vout_width = VOUT_4_3;
1208       else if (strcmp(var.value, "CRT") == 0)
1209          user_vout_width = VOUT_CRT;
1210       else
1211          user_vout_width = VOUT_PAR;   
1212    }
1213
1214    if (user_vout_width != old_user_vout_width)
1215    {
1216       // Update the geometry
1217       struct retro_system_av_info av_info;
1218       retro_get_system_av_info(&av_info);
1219       environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &av_info);
1220    }
1221
1222 #ifdef DRC_SH2
1223    var.value = NULL;
1224    var.key = "picodrive_drc";
1225    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1226       if (strcmp(var.value, "enabled") == 0)
1227          PicoOpt |= POPT_EN_DRC;
1228       else
1229          PicoOpt &= ~POPT_EN_DRC;
1230    }
1231 #endif
1232 #ifdef _3DS
1233    if(!ctr_svchack_successful)
1234       PicoOpt &= ~POPT_EN_DRC;
1235 #endif
1236 }
1237
1238 void retro_run(void)
1239 {
1240    bool updated = false;
1241    int pad, i;
1242
1243    if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated)
1244       update_variables();
1245
1246    input_poll_cb();
1247
1248    PicoPad[0] = PicoPad[1] = 0;
1249    for (pad = 0; pad < 2; pad++)
1250       for (i = 0; i < RETRO_PICO_MAP_LEN; i++)
1251          if (input_state_cb(pad, RETRO_DEVICE_JOYPAD, 0, i))
1252             PicoPad[pad] |= retro_pico_map[i];
1253
1254    PicoFrame();
1255
1256    video_cb((short *)vout_buf + vout_offset,
1257       vout_width, vout_height, vout_width * 2);
1258 }
1259
1260 static void check_system_specs(void)
1261 {
1262    /* TODO - set different performance level for 32X - 6 for ARM dynarec, higher for interpreter core */
1263    unsigned level = 5;
1264    environ_cb(RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL, &level);
1265 }
1266
1267 void retro_init(void)
1268 {
1269    struct retro_log_callback log;
1270    int level;
1271
1272    level = 0;
1273    environ_cb(RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL, &level);
1274
1275    if (environ_cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log))
1276       log_cb = log.log;
1277    else
1278       log_cb = NULL;
1279
1280    environ_cb(RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE, &disk_control);
1281
1282 #ifdef _3DS
1283    ctr_svchack_successful = ctr_svchack_init();
1284 #elif defined(VITA)
1285    sceBlock = getVMBlock();
1286 #endif
1287
1288    PicoOpt = POPT_EN_STEREO|POPT_EN_FM|POPT_EN_PSG|POPT_EN_Z80
1289       | POPT_EN_MCD_PCM|POPT_EN_MCD_CDDA|POPT_EN_MCD_GFX
1290       | POPT_EN_32X|POPT_EN_PWM
1291       | POPT_ACC_SPRITES|POPT_DIS_32C_BORDER;
1292 #ifdef __arm__
1293 #ifdef _3DS
1294    if (ctr_svchack_successful)
1295 #endif
1296       PicoOpt |= POPT_EN_DRC;
1297 #endif
1298    PsndRate = 44100;
1299    PicoAutoRgnOrder = 0x184; // US, EU, JP
1300
1301    vout_width = 320;
1302    vout_height = 240;
1303 #ifdef _3DS
1304    vout_buf = linearMemAlign(VOUT_MAX_WIDTH * VOUT_MAX_HEIGHT * 2, 0x80);
1305 #else
1306    vout_buf = malloc(VOUT_MAX_WIDTH * VOUT_MAX_HEIGHT * 2);
1307 #endif
1308
1309    PicoInit();
1310    PicoDrawSetOutFormat(PDF_RGB555, 0);
1311    PicoDrawSetOutBuf(vout_buf, vout_width * 2);
1312
1313    //PicoMessage = plat_status_msg_busy_next;
1314    PicoMCDopenTray = disk_tray_open;
1315    PicoMCDcloseTray = disk_tray_close;
1316
1317    update_variables();
1318 }
1319
1320 void retro_deinit(void)
1321 {
1322 #ifdef _3DS
1323    linearFree(vout_buf);
1324 #else
1325    free(vout_buf);
1326 #endif
1327    vout_buf = NULL;
1328    PicoExit();
1329 }