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