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