pandora: fix readme and pxml version
[picodrive.git] / platform / libretro / libretro.c
CommitLineData
7612bf90 1/*
2 * libretro core glue for PicoDrive
3 * (C) notaz, 2013
4 * (C) aliaspider, 2016
5 * (C) Daniel De Matteis, 2013
7bf552b5 6 * (C) irixxxx, 2020-2024
7612bf90 7 *
8 * This work is licensed under the terms of MAME license.
9 * See COPYING file in the top-level directory.
10 */
11
12#define _GNU_SOURCE 1 // mremap
13#include <stdio.h>
35f2b65e 14#include <stdlib.h>
7612bf90 15#include <stdarg.h>
16#include <string.h>
7612bf90 17#include <errno.h>
18#ifdef __MACH__
19#include <libkern/OSCacheControl.h>
20#endif
21
5f9901e0 22#include "libretro-common/include/formats/image.h" // really, for IMAGE_PROCESS_NEXT?!?
23#include "libretro-common/include/formats/rpng.h"
24#include "libretro-common/include/file/file_path.h"
25
a5085db3 26#include "libretro-common/include/memmap.h"
27/* Ouf, libretro-common defines replacement functions, but not the flags :-| */
28#ifndef PROT_READ
29#define PROT_READ 0x1
30#define PROT_WRITE 0x2
31#define PROT_READWRITE 0x3
32#define PROT_EXEC 0x4
33#define MAP_FAILED ((void *) -1)
34#define MAP_ANONYMOUS 0x1
35#define MAP_PRIVATE 0x2
36#endif
37
61d76999 38#if defined(RENDER_GSKIT_PS2)
f821bb70 39#include <malloc.h>
1b31b841 40#include <kernel.h>
61d76999 41#include "libretro-common/include/libretro_gskit_ps2.h"
f821bb70 42#include "ps2/asm.h"
f55ce7bf 43#else
44#include <platform/common/upscale.h>
61d76999 45#endif
1bbe9abf 46#include <platform/common/emu.h>
5f9901e0 47#include <platform/libpicofe/plat.h> // need this for PXMAKE in readpng :-/
61d76999 48
7612bf90 49#ifdef _3DS
50#include "3ds/3ds_utils.h"
51#define MEMOP_MAP 4
52#define MEMOP_UNMAP 5
53#define MEMOP_PROT 6
54
55int svcDuplicateHandle(unsigned int* out, unsigned int original);
56int svcCloseHandle(unsigned int handle);
57int svcControlProcessMemory(unsigned int process, void* addr0, void* addr1,
58 unsigned int size, unsigned int type, unsigned int perm);
59void* linearMemAlign(size_t size, size_t alignment);
60void linearFree(void* mem);
61
62static int ctr_svchack_successful = 0;
63
64#elif defined(VITA)
65#define TARGET_SIZE_2 24 // 2^24 = 16 megabytes
66
67#include <psp2/kernel/sysmem.h>
68static int sceBlock;
69int getVMBlock();
70int _newlib_vm_size_user = 1 << TARGET_SIZE_2;
08be5f1d
O
71
72#elif defined(__PS3__)
73#include <sys/process.h>
74#include <ps3mapi_ps3_lib.h>
75
76static uint64_t page_table[2] = {0, 0};
7612bf90 77#endif
78
61d76999 79#include "libretro_core_options.h"
80
7612bf90 81#include <pico/pico_int.h>
82#include <pico/state.h>
83#include <pico/patch.h>
30969671 84#include <pico/sound/mix.h>
7612bf90 85#include "../common/input_pico.h"
86#include "../common/version.h"
61d76999 87#include <libretro.h>
a5085db3 88#include <compat/strcasestr.h>
7612bf90 89
90static retro_log_printf_t log_cb;
91static retro_video_refresh_t video_cb;
92static retro_input_poll_t input_poll_cb;
93static retro_input_state_t input_state_cb;
94static retro_environment_t environ_cb;
95static retro_audio_sample_batch_t audio_batch_cb;
96
97#define VOUT_MAX_WIDTH 320
98#define VOUT_MAX_HEIGHT 240
cd262e4c 99
5c7cd059 100#define SND_RATE_DEFAULT 44100
101#define SND_RATE_MAX 53267
7612bf90 102
a5085db3 103#ifndef PATH_MAX
104#define PATH_MAX 4096
105#endif
106
7612bf90 107static const float VOUT_PAR = 0.0;
466fa079 108static const float VOUT_4_3 = (4.0f / 3.0f);
109static const float VOUT_CRT = (1.29911f);
7612bf90 110
e8658312 111/* Required to allow on the fly changes to 'renderer' */
61d76999 112static int vm_current_start_line = -1;
113static int vm_current_line_count = -1;
d5d17782 114static int vm_current_start_col = -1;
115static int vm_current_col_count = -1;
7612bf90 116
cd262e4c 117static int vout_16bit = 1;
118static int vout_format = PDF_RGB555;
f55ce7bf 119static void *vout_buf, *vout_ghosting_buf;
7612bf90 120static int vout_width, vout_height, vout_offset;
2767adb5 121static float vout_aspect = 0.0;
f55ce7bf 122static int vout_ghosting = 0;
7612bf90 123
906cc854 124static bool libretro_update_av_info = false;
125static bool libretro_update_geometry = false;
126
61d76999 127#if defined(RENDER_GSKIT_PS2)
7e5b769d 128#define VOUT_8BIT_WIDTH 328
129#define VOUT_8BIT_HEIGHT 256
61d76999 130RETRO_HW_RENDER_INTEFACE_GSKIT_PS2 *ps2 = NULL;
131static void *retro_palette;
132static struct retro_hw_ps2_insets padding;
133#endif
134
5c7cd059 135static short ALIGNED(4) sndBuffer[2*SND_RATE_MAX/50];
7612bf90 136
137static void snd_write(int len);
138
a002255e 139char **g_argv;
140
7612bf90 141#ifdef _WIN32
142#define SLASH '\\'
143#else
144#define SLASH '/'
145#endif
146
61d76999 147/* Frameskipping Support */
148
149static unsigned frameskip_type = 0;
150static unsigned frameskip_threshold = 0;
151static uint16_t frameskip_counter = 0;
152
153static bool retro_audio_buff_active = false;
154static unsigned retro_audio_buff_occupancy = 0;
155static bool retro_audio_buff_underrun = false;
156/* Maximum number of consecutive frames that
157 * can be skipped */
158#define FRAMESKIP_MAX 60
159
160static unsigned audio_latency = 0;
161static bool update_audio_latency = false;
a5085db3 162static uint16_t pico_events;
5f9901e0 163// Sega Pico stuff
164int pico_inp_mode;
a5085db3 165int pico_pen_x = 320/2, pico_pen_y = 240/2;
5f9901e0 166static int pico_page;
167static int pico_w, pico_h;
168static char pico_overlay_path[PATH_MAX];
169static unsigned short *pico_overlay;
170
61d76999 171
172static void retro_audio_buff_status_cb(
173 bool active, unsigned occupancy, bool underrun_likely)
174{
175 retro_audio_buff_active = active;
176 retro_audio_buff_occupancy = occupancy;
177 retro_audio_buff_underrun = underrun_likely;
178}
179
180static void init_frameskip(void)
181{
182 if (frameskip_type > 0)
183 {
184 struct retro_audio_buffer_status_callback buf_status_cb;
185
186 buf_status_cb.callback = retro_audio_buff_status_cb;
187 if (!environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK,
188 &buf_status_cb))
189 {
190 if (log_cb)
191 log_cb(RETRO_LOG_WARN, "Frameskip disabled - frontend does not support audio buffer status monitoring.\n");
192
193 retro_audio_buff_active = false;
194 retro_audio_buff_occupancy = 0;
195 retro_audio_buff_underrun = false;
196 audio_latency = 0;
197 }
198 else
199 {
200 /* Frameskip is enabled - increase frontend
201 * audio latency to minimise potential
202 * buffer underruns */
203 float frame_time_msec = 1000.0f / (Pico.m.pal ? 50.0f : 60.0f);
204
205 /* Set latency to 6x current frame time... */
206 audio_latency = (unsigned)((6.0f * frame_time_msec) + 0.5f);
207
208 /* ...then round up to nearest multiple of 32 */
209 audio_latency = (audio_latency + 0x1F) & ~0x1F;
210 }
211 }
212 else
213 {
214 environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK, NULL);
215 audio_latency = 0;
216 }
217
218 update_audio_latency = true;
219}
220
7612bf90 221/* functions called by the core */
222
223void cache_flush_d_inval_i(void *start, void *end)
224{
225#ifdef __arm__
226 size_t len = (char *)end - (char *)start;
227#if defined(__BLACKBERRY_QNX__)
60ef69d6 228 msync(start, len, MS_SYNC | MS_CACHE_ONLY | MS_INVALIDATE_ICACHE);
7612bf90 229#elif defined(__MACH__)
230 sys_dcache_flush(start, len);
231 sys_icache_invalidate(start, len);
232#elif defined(_3DS)
60ef69d6 233 (void)len;
7612bf90 234 ctr_flush_invalidate_cache();
235#elif defined(VITA)
236 sceKernelSyncVMDomain(sceBlock, start, len);
237#else
60ef69d6 238 (void)len;
7612bf90 239 __clear_cache(start, end);
240#endif
241#endif
242}
243
7e5b769d 244#ifdef RENDER_GSKIT_PS2
a5085db3 245/* In PS2 toolchain these aren't yet defined */
246void _flush_cache(void *b, void *e)
7e5b769d 247{
248#if 0 /* which of these is overall faster for lots of small cache updates? */
249 SyncDCache(b, e);
250#else
251 FlushCache(0); /* WRITEBACK_DCACHE */
252#endif
253 FlushCache(2); /* INVALIDATE_ICACHE */
254}
255
256int __builtin_parity(unsigned v)
257{
258 /* credits to bit twiddling hacks, https://graphics.stanford.edu/~seander/bithacks.html */
259 v ^= v >> 16;
260 v ^= v >> 8;
261 v ^= v >> 4;
262 return (0x6996 >> (v&0xf)) & 1;
263}
a5085db3 264#elif defined(PSP)
265int _flush_cache(char *addr, const int size, const int op)
266{
267 //sceKernelDcacheWritebackAll();
268 sceKernelDcacheWritebackRange(addr, size);
269 sceKernelIcacheInvalidateRange(addr, size);
270 return 0;
271}
7e5b769d 272#endif
273
512898fe 274#ifdef __MACH__
275/* calls to this may be generated by the compiler, but it's missing in libc? */
276void __clear_cache(void *start, void *end)
277{
278 size_t len = (char *)end - (char *)start;
279 sys_dcache_flush(start, len);
280 sys_icache_invalidate(start, len);
281}
282#endif
283
7612bf90 284#ifndef MAP_ANONYMOUS
285#define MAP_ANONYMOUS MAP_ANON
286#endif
287
288#ifdef _3DS
289typedef struct
290{
291 unsigned int requested_map;
292 void* buffer;
293}pico_mmap_t;
294
295pico_mmap_t pico_mmaps[] = {
296 {0x02000000, 0},
297 {0x06000000, 0},
298 {NULL, 0}
299};
300
301void *plat_mmap(unsigned long addr, size_t size, int need_exec, int is_fixed)
302{
303 (void)is_fixed;
304
305 if (ctr_svchack_successful)
306 {
307 pico_mmap_t* pico_mmap;
308
309 for (pico_mmap = pico_mmaps; pico_mmap->requested_map; pico_mmap++)
310 {
311 if ((pico_mmap->requested_map == addr))
312 {
313 unsigned int ptr_aligned, tmp;
314 unsigned int currentHandle;
315 unsigned int perm = 0b011;
316
317 if (need_exec)
318 perm = 0b111;
319
320 size = (size + 0xFFF) & ~0xFFF;
321 pico_mmap->buffer = malloc(size + 0x1000);
322 ptr_aligned = (((unsigned int)pico_mmap->buffer) + 0xFFF) & ~0xFFF;
323
324 svcDuplicateHandle(&currentHandle, 0xFFFF8001);
325
326 if(svcControlProcessMemory(currentHandle, pico_mmap->requested_map, ptr_aligned, size, MEMOP_MAP, perm) < 0)
327 {
328 if (log_cb)
329 log_cb(RETRO_LOG_ERROR, "could not map memory @0x%08X\n", pico_mmap->requested_map);
330 exit(1);
331 }
332
333 svcCloseHandle(currentHandle);
334 return (void*)pico_mmap->requested_map;
335 }
336 }
337 }
338
339 return malloc(size);
340}
341
342void *plat_mremap(void *ptr, size_t oldsize, size_t newsize)
343{
344 if (ctr_svchack_successful)
345 {
346 pico_mmap_t* pico_mmap;
347
348 for (pico_mmap = pico_mmaps; pico_mmap->requested_map; pico_mmap++)
349 {
350 if ((pico_mmap->requested_map == (unsigned int)ptr))
351 {
352 unsigned int ptr_aligned;
353 unsigned int currentHandle;
354 void* tmp;
355
356 oldsize = (oldsize + 0xFFF) & ~0xFFF;
357 newsize = (newsize + 0xFFF) & ~0xFFF;
358 ptr_aligned = (((unsigned int)pico_mmap->buffer) + 0xFFF) & ~0xFFF;
359
360 svcDuplicateHandle(&currentHandle, 0xFFFF8001);
361
362 svcControlProcessMemory(currentHandle, pico_mmap->requested_map, ptr_aligned, oldsize, MEMOP_UNMAP, 0b011);
363
364 tmp = realloc(pico_mmap->buffer, newsize + 0x1000);
365 if(!tmp)
366 return NULL;
367
368 pico_mmap->buffer = tmp;
369 ptr_aligned = (((unsigned int)pico_mmap->buffer) + 0xFFF) & ~0xFFF;
370
371 svcControlProcessMemory(currentHandle, pico_mmap->requested_map, ptr_aligned, newsize, MEMOP_MAP, 0x3);
372
373 svcCloseHandle(currentHandle);
374
375 return ptr;
376 }
377 }
378 }
379
380 return realloc(ptr, newsize);
381
382}
383void plat_munmap(void *ptr, size_t size)
384{
385 if (ctr_svchack_successful)
386 {
387 pico_mmap_t* pico_mmap;
388
389 for (pico_mmap = pico_mmaps; pico_mmap->requested_map; pico_mmap++)
390 {
391 if ((pico_mmap->requested_map == (unsigned int)ptr))
392 {
393 unsigned int ptr_aligned;
394 unsigned int currentHandle;
395
396 size = (size + 0xFFF) & ~0xFFF;
397 ptr_aligned = (((unsigned int)pico_mmap->buffer) + 0xFFF) & ~0xFFF;
398
399 svcDuplicateHandle(&currentHandle, 0xFFFF8001);
400
401 svcControlProcessMemory(currentHandle, (void*)pico_mmap->requested_map, (void*)ptr_aligned, size, MEMOP_UNMAP, 0b011);
402
403 svcCloseHandle(currentHandle);
404
405 free(pico_mmap->buffer);
406 pico_mmap->buffer = NULL;
407 return;
408 }
409 }
410 }
411
412 free(ptr);
413}
414
415#else
416void *plat_mmap(unsigned long addr, size_t size, int need_exec, int is_fixed)
417{
418 int flags = MAP_PRIVATE | MAP_ANONYMOUS;
419 void *req, *ret;
420
48c9e01b 421 req = (void *)(uintptr_t)addr;
7612bf90 422 ret = mmap(req, size, PROT_READ | PROT_WRITE, flags, -1, 0);
423 if (ret == MAP_FAILED) {
424 if (log_cb)
425 log_cb(RETRO_LOG_ERROR, "mmap(%08lx, %zd) failed: %d\n", addr, size, errno);
426 return NULL;
427 }
428
48c9e01b 429 if (addr != 0 && ret != (void *)(uintptr_t)addr) {
7612bf90 430 if (log_cb)
431 log_cb(RETRO_LOG_WARN, "warning: wanted to map @%08lx, got %p\n",
432 addr, ret);
433
434 if (is_fixed) {
435 munmap(ret, size);
436 return NULL;
437 }
438 }
439
440 return ret;
441}
442
443void *plat_mremap(void *ptr, size_t oldsize, size_t newsize)
444{
61d76999 445#if defined(__linux__) && !defined(__SWITCH__)
7612bf90 446 void *ret = mremap(ptr, oldsize, newsize, 0);
447 if (ret == MAP_FAILED)
448 return NULL;
449
450 return ret;
451#else
452 void *tmp, *ret;
453 size_t preserve_size;
454
455 preserve_size = oldsize;
456 if (preserve_size > newsize)
457 preserve_size = newsize;
458 tmp = malloc(preserve_size);
459 if (tmp == NULL)
460 return NULL;
461 memcpy(tmp, ptr, preserve_size);
462
463 munmap(ptr, oldsize);
464 ret = mmap(ptr, newsize, PROT_READ | PROT_WRITE,
465 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
466 if (ret == MAP_FAILED) {
467 free(tmp);
468 return NULL;
469 }
470 memcpy(ret, tmp, preserve_size);
471 free(tmp);
472 return ret;
473#endif
474}
475
476void plat_munmap(void *ptr, size_t size)
477{
478 if (ptr != NULL)
479 munmap(ptr, size);
480}
481#endif
482
df925153 483// if NULL is returned, static buffer is used
484void *plat_mem_get_for_drc(size_t size)
485{
486 void *mem = NULL;
cbc2ca03 487#if defined VITA
df925153 488 sceKernelGetMemBlockBase(sceBlock, &mem);
cbc2ca03 489#elif defined HW_WUP
490 // For WiiU, a slice of RWX memory left from the exploit is used, see:
491 // https://github.com/embercold/pcsx_rearmed/commit/af0453223
492 mem = (void *)(0x01000000 - size);
08be5f1d
O
493#elif defined __PS3__
494 ps3mapi_process_page_allocate(sysProcessGetPid(), size, PAGE_SIZE_AUTO, 0x2F, 1, page_table);
495 mem = (void *)page_table[0];
df925153 496#endif
497 return mem;
498}
499
7612bf90 500int plat_mem_set_exec(void *ptr, size_t size)
501{
df925153 502 int ret = -1;
7612bf90 503#ifdef _WIN32
a0b95da1 504 DWORD oldProtect = 0;
505 ret = VirtualProtect(ptr, size, PAGE_EXECUTE_READWRITE, &oldProtect);
7612bf90 506 if (ret == 0 && log_cb)
df925153 507 log_cb(RETRO_LOG_ERROR, "VirtualProtect(%p, %d) failed: %d\n", ptr, (int)size,
508 GetLastError());
7612bf90 509#elif defined(_3DS)
7612bf90 510 if (ctr_svchack_successful)
511 {
512 unsigned int currentHandle;
513 svcDuplicateHandle(&currentHandle, 0xFFFF8001);
514 ret = svcControlProcessMemory(currentHandle, ptr, 0x0,
515 size, MEMOP_PROT, 0b111);
516 svcCloseHandle(currentHandle);
517 ctr_flush_invalidate_cache();
518
519 }
520 else
521 {
522 if (log_cb)
523 log_cb(RETRO_LOG_ERROR, "plat_mem_set_exec called with no svcControlProcessMemory access\n");
524 exit(1);
525 }
526
527#elif defined(VITA)
df925153 528 ret = sceKernelOpenVMDomain();
7612bf90 529#else
df925153 530 ret = mprotect(ptr, size, PROT_READ | PROT_WRITE | PROT_EXEC);
7612bf90 531 if (ret != 0 && log_cb)
532 log_cb(RETRO_LOG_ERROR, "mprotect(%p, %zd) failed: %d\n", ptr, size, errno);
533#endif
534 return ret;
535}
536
1bbe9abf 537static void apply_renderer()
538{
539 PicoIn.opt &= ~(POPT_ALT_RENDERER|POPT_EN_SOFTSCALE);
540 PicoIn.opt |= POPT_DIS_32C_BORDER;
541 if (vout_format == PDF_NONE)
542 PicoIn.opt |= POPT_ALT_RENDERER;
543 PicoDrawSetOutFormat(vout_format, 0);
544 if (!vout_16bit && vout_format == PDF_8BIT)
545 PicoDrawSetOutBuf(Pico.est.Draw2FB, 328);
546}
547
d5d17782 548void emu_video_mode_change(int start_line, int line_count, int start_col, int col_count)
7612bf90 549{
61d76999 550 vm_current_start_line = start_line;
551 vm_current_line_count = line_count;
d5d17782 552 vm_current_start_col = start_col;
553 vm_current_col_count = col_count;
61d76999 554
60ef69d6 555 // 8bit renderers create a 328x256 CLUT image, 16bit creates 320x240 RGB
61d76999 556#if defined(RENDER_GSKIT_PS2)
7e5b769d 557 // calculate the borders of the real image inside the picodrive image
558 vout_width = (vout_16bit ? VOUT_MAX_WIDTH : VOUT_8BIT_WIDTH);
559 vout_height = (vout_16bit ? VOUT_MAX_HEIGHT : VOUT_8BIT_HEIGHT);
96948bdf 560 vout_offset = (vout_16bit ? 0 : col_count == 248 ? 16 : 8); // 8bit has overlap area on the left
d5d17782 561 padding = (struct retro_hw_ps2_insets){start_line, vout_offset, vout_height - line_count - start_line, vout_width - col_count - vout_offset};
61d76999 562
7e5b769d 563 int pxsz = (vout_16bit ? 2 : 1); // pixel size: RGB = 16 bits, CLUT = 8 bits
564 memset(vout_buf, 0, pxsz * vout_width * vout_height);
61d76999 565 memset(retro_palette, 0, gsKit_texture_size_ee(16, 16, GS_PSM_CT16));
7e5b769d 566 PicoDrawSetOutBuf(vout_buf, pxsz * vout_width);
567 if (ps2) {
568 // prepare image as texture for rendering
569 ps2->coreTexture->Width = vout_width;
570 ps2->coreTexture->Height = vout_height;
571 ps2->coreTexture->PSM = (vout_16bit ? GS_PSM_CT16 : GS_PSM_T8);
572 ps2->padding = padding;
573 }
61d76999 574#else
d5d17782 575 vout_width = col_count;
61d76999 576 memset(vout_buf, 0, VOUT_MAX_WIDTH * VOUT_MAX_HEIGHT * 2);
cd262e4c 577 if (vout_16bit)
578 PicoDrawSetOutBuf(vout_buf, vout_width * 2);
7612bf90 579
e8658312 580 vout_height = line_count;
581 /* Note: We multiply by 2 here to account for pitch */
582 vout_offset = vout_width * start_line * 2;
61d76999 583
584 /* Redundant sanity check... */
585 vout_height = (vout_height > VOUT_MAX_HEIGHT) ?
586 VOUT_MAX_HEIGHT : vout_height;
587 vout_offset = (vout_offset > vout_width * (VOUT_MAX_HEIGHT - 1) * 2) ?
588 vout_width * (VOUT_MAX_HEIGHT - 1) * 2 : vout_offset;
f55ce7bf 589
590 /* LCD ghosting */
591 if (vout_ghosting && vout_height == 144) {
592 vout_ghosting_buf = realloc(vout_ghosting_buf, VOUT_MAX_HEIGHT*vout_width*2);
593 memset(vout_ghosting_buf, 0, vout_width*vout_height*2);
594 }
61d76999 595#endif
cd262e4c 596 Pico.m.dirtyPal = 1;
7612bf90 597
906cc854 598 /* Notify frontend of geometry update */
599 libretro_update_geometry = true;
7612bf90 600}
601
602void emu_32x_startup(void)
603{
1bbe9abf 604 PicoIn.filter = EOPT_FILTER_SMOOTHER; // for H32 upscaling
cd262e4c 605 PicoDrawSetOutFormat(vout_format, 0);
1bbe9abf 606 vout_16bit = 1;
607
bccd8832 608 if (vout_buf &&
609 (vm_current_start_line != -1) && (vm_current_line_count != -1) &&
610 (vm_current_start_col != -1) && (vm_current_col_count != -1))
cd262e4c 611 emu_video_mode_change(
d5d17782 612 vm_current_start_line, vm_current_line_count,
613 vm_current_start_col, vm_current_col_count);
7612bf90 614}
615
616void lprintf(const char *fmt, ...)
617{
618 char buffer[256];
619 va_list ap;
620 va_start(ap, fmt);
621 vsprintf(buffer, fmt, ap);
622 /* TODO - add 'level' param for warning/error messages? */
623 if (log_cb)
624 log_cb(RETRO_LOG_INFO, "%s", buffer);
625 va_end(ap);
626}
627
628/* libretro */
b106e5c7 629bool libretro_supports_bitmasks = false;
630
7612bf90 631void retro_set_environment(retro_environment_t cb)
632{
8b2f9d02 633 bool option_categories_supported;
61d76999 634#ifdef USE_LIBRETRO_VFS
635 struct retro_vfs_interface_info vfs_iface_info;
7612bf90 636#endif
7612bf90 637
a5085db3 638 static const struct retro_system_content_info_override content_overrides[] = {
639 {
44a6c678 640 "bin|gen|smd|md|32x|sms|gg|sg|sc|68k|sgd|pco", /* extensions */
a5085db3 641#if defined(LOW_MEMORY)
642 true, /* need_fullpath */
643#else
644 false, /* need_fullpath */
645#endif
646 false /* persistent_data */
647 },
648 { NULL, false, false }
649 };
650
7612bf90 651 environ_cb = cb;
652
8b2f9d02 653 libretro_set_core_options(environ_cb,
654 &option_categories_supported);
a5085db3 655 environ_cb(RETRO_ENVIRONMENT_SET_CONTENT_INFO_OVERRIDE,
656 (void*)content_overrides);
61d76999 657
658#ifdef USE_LIBRETRO_VFS
659 vfs_iface_info.required_interface_version = 1;
660 vfs_iface_info.iface = NULL;
661 if (environ_cb(RETRO_ENVIRONMENT_GET_VFS_INTERFACE, &vfs_iface_info))
662 filestream_vfs_init(&vfs_iface_info);
663#endif
7612bf90 664}
665
666void retro_set_video_refresh(retro_video_refresh_t cb) { video_cb = cb; }
667void retro_set_audio_sample(retro_audio_sample_t cb) { (void)cb; }
668void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) { audio_batch_cb = cb; }
669void retro_set_input_poll(retro_input_poll_t cb) { input_poll_cb = cb; }
670void retro_set_input_state(retro_input_state_t cb) { input_state_cb = cb; }
671
672unsigned retro_api_version(void)
673{
674 return RETRO_API_VERSION;
675}
676
677void retro_set_controller_port_device(unsigned port, unsigned device)
678{
679}
680
681void retro_get_system_info(struct retro_system_info *info)
682{
683 memset(info, 0, sizeof(*info));
684 info->library_name = "PicoDrive";
3a770905 685 info->library_version = VERSION;
6ce10a43 686 info->valid_extensions = "bin|gen|smd|md|32x|cue|iso|chd|sms|gg|sg|sc|m3u|68k|sgd|pco";
7612bf90 687 info->need_fullpath = true;
688}
689
690void retro_get_system_av_info(struct retro_system_av_info *info)
691{
e8658312 692 float tv_height = (vout_height > 144 ? Pico.m.pal ? 240 : 224 : 144);
24aab4da 693 float common_width;
694
7612bf90 695 memset(info, 0, sizeof(*info));
696 info->timing.fps = Pico.m.pal ? 50 : 60;
61d76999 697 info->timing.sample_rate = PicoIn.sndRate;
7612bf90 698 info->geometry.base_width = vout_width;
699 info->geometry.base_height = vout_height;
700 info->geometry.max_width = vout_width;
701 info->geometry.max_height = vout_height;
702
24aab4da 703 common_width = vout_width;
2767adb5 704 if (vout_aspect != 0)
e8658312 705 common_width = vout_aspect * tv_height;
7612bf90 706
e8658312 707 info->geometry.aspect_ratio = common_width / vout_height;
7612bf90 708}
709
710/* savestates */
711struct savestate_state {
712 const char *load_buf;
713 char *save_buf;
714 size_t size;
715 size_t pos;
716};
717
718size_t state_read(void *p, size_t size, size_t nmemb, void *file)
719{
720 struct savestate_state *state = file;
721 size_t bsize = size * nmemb;
722
723 if (state->pos + bsize > state->size) {
724 if (log_cb)
725 log_cb(RETRO_LOG_ERROR, "savestate error: %u/%u\n",
726 state->pos + bsize, state->size);
727 bsize = state->size - state->pos;
728 if ((int)bsize <= 0)
729 return 0;
730 }
731
732 memcpy(p, state->load_buf + state->pos, bsize);
733 state->pos += bsize;
734 return bsize;
735}
736
737size_t state_write(void *p, size_t size, size_t nmemb, void *file)
738{
739 struct savestate_state *state = file;
740 size_t bsize = size * nmemb;
741
742 if (state->pos + bsize > state->size) {
743 if (log_cb)
744 log_cb(RETRO_LOG_ERROR, "savestate error: %u/%u\n",
745 state->pos + bsize, state->size);
746 bsize = state->size - state->pos;
747 if ((int)bsize <= 0)
748 return 0;
749 }
750
751 memcpy(state->save_buf + state->pos, p, bsize);
752 state->pos += bsize;
753 return bsize;
754}
755
756size_t state_skip(void *p, size_t size, size_t nmemb, void *file)
757{
758 struct savestate_state *state = file;
759 size_t bsize = size * nmemb;
760
761 state->pos += bsize;
762 return bsize;
763}
764
765size_t state_eof(void *file)
766{
767 struct savestate_state *state = file;
768
769 return state->pos >= state->size;
770}
771
772int state_fseek(void *file, long offset, int whence)
773{
774 struct savestate_state *state = file;
775
776 switch (whence) {
777 case SEEK_SET:
778 state->pos = offset;
779 break;
780 case SEEK_CUR:
781 state->pos += offset;
782 break;
783 case SEEK_END:
784 state->pos = state->size + offset;
785 break;
786 }
787 return (int)state->pos;
788}
789
790/* savestate sizes vary wildly depending if cd/32x or
791 * carthw is active, so run the whole thing to get size */
792size_t retro_serialize_size(void)
793{
794 struct savestate_state state = { 0, };
92f7a430 795 unsigned AHW = PicoIn.AHW;
7612bf90 796 int ret;
797
92f7a430 798 /* we need the max possible size here, so include 32X for MD and MCD */
799 if (!(AHW & (PAHW_SMS|PAHW_PICO|PAHW_SVP)))
800 PicoIn.AHW |= PAHW_32X;
7612bf90 801 ret = PicoStateFP(&state, 1, NULL, state_skip, NULL, state_fseek);
92f7a430 802 PicoIn.AHW = AHW;
7612bf90 803 if (ret != 0)
804 return 0;
805
806 return state.pos;
807}
808
809bool retro_serialize(void *data, size_t size)
810{
811 struct savestate_state state = { 0, };
812 int ret;
813
814 state.save_buf = data;
815 state.size = size;
816 state.pos = 0;
817
818 ret = PicoStateFP(&state, 1, NULL, state_write,
819 NULL, state_fseek);
820 return ret == 0;
821}
822
823bool retro_unserialize(const void *data, size_t size)
824{
825 struct savestate_state state = { 0, };
826 int ret;
827
828 state.load_buf = data;
829 state.size = size;
830 state.pos = 0;
831
832 ret = PicoStateFP(&state, 0, state_read, NULL,
833 state_eof, state_fseek);
834 return ret == 0;
835}
836
837typedef struct patch
838{
839 unsigned int addr;
840 unsigned short data;
841 unsigned char comp;
842} patch;
843
844extern void decode(char *buff, patch *dest);
466fa079 845extern uint32_t m68k_read16(uint32_t a);
7612bf90 846extern void m68k_write16(uint32_t a, uint16_t d);
847
848void retro_cheat_reset(void)
849{
850 int i=0;
851 unsigned int addr;
852
853 for (i = 0; i < PicoPatchCount; i++)
854 {
855 addr = PicoPatches[i].addr;
856 if (addr < Pico.romsize) {
857 if (PicoPatches[i].active)
858 *(unsigned short *)(Pico.rom + addr) = PicoPatches[i].data_old;
859 } else {
860 if (PicoPatches[i].active)
861 m68k_write16(PicoPatches[i].addr,PicoPatches[i].data_old);
862 }
863 }
864
865 PicoPatchUnload();
866}
867
868void retro_cheat_set(unsigned index, bool enabled, const char *code)
869{
870 patch pt;
871 int array_len = PicoPatchCount;
872 char codeCopy[256];
873 char *buff;
874
61d76999 875 if (*code == '\0')
876 return;
877 strcpy(codeCopy, code);
7612bf90 878 buff = strtok(codeCopy,"+");
879
880 while (buff != NULL)
881 {
882 decode(buff, &pt);
883 if (pt.addr == (uint32_t) -1 || pt.data == (uint16_t) -1)
884 {
885 log_cb(RETRO_LOG_ERROR,"CHEATS: Invalid code: %s\n",buff);
886 return;
887 }
888
889 /* code was good, add it */
890 if (array_len < PicoPatchCount + 1)
891 {
892 void *ptr;
893 array_len *= 2;
894 array_len++;
895 ptr = realloc(PicoPatches, array_len * sizeof(PicoPatches[0]));
896 if (ptr == NULL) {
897 log_cb(RETRO_LOG_ERROR,"CHEATS: Failed to allocate memory for: %s\n",buff);
898 return;
899 }
900 PicoPatches = ptr;
901 }
902 strcpy(PicoPatches[PicoPatchCount].code, buff);
903
904 PicoPatches[PicoPatchCount].active = enabled;
905 PicoPatches[PicoPatchCount].addr = pt.addr;
906 PicoPatches[PicoPatchCount].data = pt.data;
907 PicoPatches[PicoPatchCount].comp = pt.comp;
908 if (PicoPatches[PicoPatchCount].addr < Pico.romsize)
909 PicoPatches[PicoPatchCount].data_old = *(uint16_t *)(Pico.rom + PicoPatches[PicoPatchCount].addr);
910 else
911 PicoPatches[PicoPatchCount].data_old = (uint16_t) m68k_read16(PicoPatches[PicoPatchCount].addr);
912 PicoPatchCount++;
913
914 buff = strtok(NULL,"+");
915 }
916}
917
918/* multidisk support */
a5085db3 919static unsigned int disk_initial_index;
7612bf90 920static bool disk_ejected;
921static unsigned int disk_current_index;
922static unsigned int disk_count;
a5085db3 923static char disk_initial_path[PATH_MAX];
7612bf90 924static struct disks_state {
925 char *fname;
a5085db3 926 char *flabel;
7612bf90 927} disks[8];
928
a5085db3 929static void get_disk_label(char *disk_label, const char *disk_path, size_t len)
930{
931 const char *base = NULL;
932
933 if (!disk_path || (*disk_path == '\0'))
934 return;
935
936 base = strrchr(disk_path, SLASH);
937 if (!base)
938 base = disk_path;
939
940 if (*base == SLASH)
941 base++;
942
943 strncpy(disk_label, base, len - 1);
944 disk_label[len - 1] = '\0';
945
946 char *ext = strrchr(disk_label, '.');
947 if (ext)
948 *ext = '\0';
949}
950
951static void disk_init(void)
952{
953 size_t i;
954
955 disk_ejected = false;
956 disk_current_index = 0;
957 disk_count = 0;
958
959 for (i = 0; i < sizeof(disks) / sizeof(disks[0]); i++)
960 {
961 if (disks[i].fname != NULL)
962 {
963 free(disks[i].fname);
964 disks[i].fname = NULL;
965 }
966 if (disks[i].flabel != NULL)
967 {
968 free(disks[i].flabel);
969 disks[i].flabel = NULL;
970 }
971 }
972}
973
7612bf90 974static bool disk_set_eject_state(bool ejected)
975{
976 // TODO?
977 disk_ejected = ejected;
978 return true;
979}
980
981static bool disk_get_eject_state(void)
982{
983 return disk_ejected;
984}
985
986static unsigned int disk_get_image_index(void)
987{
988 return disk_current_index;
989}
990
991static bool disk_set_image_index(unsigned int index)
992{
15ca7152 993 enum cd_track_type cd_type;
7612bf90 994 int ret;
995
996 if (index >= sizeof(disks) / sizeof(disks[0]))
997 return false;
998
999 if (disks[index].fname == NULL) {
1000 if (log_cb)
1001 log_cb(RETRO_LOG_ERROR, "missing disk #%u\n", index);
1002
1003 // RetroArch specifies "no disk" with index == count,
1004 // so don't fail here..
1005 disk_current_index = index;
1006 return true;
1007 }
1008
1009 if (log_cb)
1010 log_cb(RETRO_LOG_INFO, "switching to disk %u: \"%s\"\n", index,
1011 disks[index].fname);
1012
1013 ret = -1;
db2350aa 1014 if (PicoIn.AHW & PAHW_MCD) {
1015 cd_type = PicoCdCheck(disks[index].fname, NULL);
1016 if (cd_type >= 0 && cd_type != CT_UNKNOWN)
1017 ret = cdd_load(disks[index].fname, cd_type);
1018 }
7612bf90 1019 if (ret != 0) {
1020 if (log_cb)
1021 log_cb(RETRO_LOG_ERROR, "Load failed, invalid CD image?\n");
1022 return 0;
1023 }
1024
1025 disk_current_index = index;
1026 return true;
1027}
1028
1029static unsigned int disk_get_num_images(void)
1030{
1031 return disk_count;
1032}
1033
1034static bool disk_replace_image_index(unsigned index,
1035 const struct retro_game_info *info)
1036{
a5085db3 1037 char *old_fname = NULL;
1038 char *old_flabel = NULL;
1039 bool ret = true;
7612bf90 1040
1041 if (index >= sizeof(disks) / sizeof(disks[0]))
1042 return false;
1043
a5085db3 1044 old_fname = disks[index].fname;
1045 old_flabel = disks[index].flabel;
1046
7612bf90 1047 if (disks[index].fname != NULL)
1048 free(disks[index].fname);
1049 disks[index].fname = NULL;
1050
a5085db3 1051 if (disks[index].flabel != NULL)
1052 free(disks[index].flabel);
1053 disks[index].flabel = NULL;
1054
7612bf90 1055 if (info != NULL) {
a5085db3 1056 char disk_label[PATH_MAX];
1057 disk_label[0] = '\0';
1058
7612bf90 1059 disks[index].fname = strdup(info->path);
a5085db3 1060
1061 get_disk_label(disk_label, info->path, PATH_MAX);
1062 disks[index].flabel = strdup(disk_label);
1063
7612bf90 1064 if (index == disk_current_index)
1065 ret = disk_set_image_index(index);
1066 }
1067
a5085db3 1068 if (old_fname != NULL)
1069 free(old_fname);
1070
1071 if (old_flabel != NULL)
1072 free(old_flabel);
1073
7612bf90 1074 return ret;
1075}
1076
1077static bool disk_add_image_index(void)
1078{
1079 if (disk_count >= sizeof(disks) / sizeof(disks[0]))
1080 return false;
1081
1082 disk_count++;
1083 return true;
1084}
1085
a5085db3 1086static bool disk_set_initial_image(unsigned index, const char *path)
1087{
1088 if (index >= sizeof(disks) / sizeof(disks[0]))
1089 return false;
1090
1091 if (!path || (*path == '\0'))
1092 return false;
1093
1094 disk_initial_index = index;
1095
1096 strncpy(disk_initial_path, path, sizeof(disk_initial_path) - 1);
1097 disk_initial_path[sizeof(disk_initial_path) - 1] = '\0';
1098
1099 return true;
1100}
1101
1102static bool disk_get_image_path(unsigned index, char *path, size_t len)
1103{
1104 const char *fname = NULL;
1105
1106 if (len < 1)
1107 return false;
1108
1109 if (index >= sizeof(disks) / sizeof(disks[0]))
1110 return false;
1111
1112 fname = disks[index].fname;
1113
1114 if (!fname || (*fname == '\0'))
1115 return false;
1116
1117 strncpy(path, fname, len - 1);
1118 path[len - 1] = '\0';
1119
1120 return true;
1121}
1122
1123static bool disk_get_image_label(unsigned index, char *label, size_t len)
1124{
1125 const char *flabel = NULL;
1126
1127 if (len < 1)
1128 return false;
1129
1130 if (index >= sizeof(disks) / sizeof(disks[0]))
1131 return false;
1132
1133 flabel = disks[index].flabel;
1134
1135 if (!flabel || (*flabel == '\0'))
1136 return false;
1137
1138 strncpy(label, flabel, len - 1);
1139 label[len - 1] = '\0';
1140
1141 return true;
1142}
1143
7612bf90 1144static struct retro_disk_control_callback disk_control = {
1145 disk_set_eject_state,
1146 disk_get_eject_state,
1147 disk_get_image_index,
1148 disk_set_image_index,
1149 disk_get_num_images,
1150 disk_replace_image_index,
1151 disk_add_image_index,
1152};
1153
a5085db3 1154static struct retro_disk_control_ext_callback disk_control_ext = {
1155 .set_eject_state = disk_set_eject_state,
1156 .get_eject_state = disk_get_eject_state,
1157 .get_image_index = disk_get_image_index,
1158 .set_image_index = disk_set_image_index,
1159 .get_num_images = disk_get_num_images,
1160 .replace_image_index = disk_replace_image_index,
1161 .add_image_index = disk_add_image_index,
1162 .set_initial_image = disk_set_initial_image,
1163 .get_image_path = disk_get_image_path,
1164 .get_image_label = disk_get_image_label,
1165};
1166
7612bf90 1167static void disk_tray_open(void)
1168{
1169 if (log_cb)
1170 log_cb(RETRO_LOG_INFO, "cd tray open\n");
1171 disk_ejected = 1;
1172}
1173
1174static void disk_tray_close(void)
1175{
1176 if (log_cb)
1177 log_cb(RETRO_LOG_INFO, "cd tray close\n");
1178 disk_ejected = 0;
1179}
1180
a5085db3 1181static char base_dir[1024];
1182
1183static void extract_directory(char *buf, const char *path, size_t size)
1184{
1185 char *base;
1186 strncpy(buf, path, size - 1);
1187 buf[size - 1] = '\0';
1188
1189 base = strrchr(buf, '/');
1190 if (!base)
1191 base = strrchr(buf, '\\');
1192
1193 if (base)
1194 *base = '\0';
1195 else
1196 {
1197 buf[0] = '.';
1198 buf[1] = '\0';
1199 }
1200}
1201
1202static void extract_basename(char *buf, const char *path, size_t size)
1203{
1204 const char *base = strrchr(path, '/');
1205 if (!base)
1206 base = strrchr(path, '\\');
1207 if (!base)
1208 base = path;
1209
1210 if (*base == '\\' || *base == '/')
1211 base++;
1212
1213 strncpy(buf, base, size - 1);
1214 buf[size - 1] = '\0';
1215
1216 char *ext = strrchr(buf, '.');
1217 if (ext)
1218 *ext = '\0';
1219}
1220
1221static bool read_m3u(const char *file)
1222{
1223 char line[1024];
1224 char name[PATH_MAX];
1225 FILE *f = fopen(file, "r");
1226 if (!f)
1227 return false;
1228
1229 while (fgets(line, sizeof(line), f) && disk_count < sizeof(disks) / sizeof(disks[0]))
1230 {
1231 if (line[0] == '#')
1232 continue;
1233 char *carrige_return = strchr(line, '\r');
1234 if (carrige_return)
1235 *carrige_return = '\0';
1236 char *newline = strchr(line, '\n');
1237 if (newline)
1238 *newline = '\0';
1239
1240 if (line[0] != '\0')
1241 {
1242 char disk_label[PATH_MAX];
1243 disk_label[0] = '\0';
1244
1245 snprintf(name, sizeof(name), "%s%c%s", base_dir, SLASH, line);
1246 disks[disk_count].fname = strdup(name);
1247
1248 get_disk_label(disk_label, name, PATH_MAX);
1249 disks[disk_count].flabel = strdup(disk_label);
1250
1251 disk_count++;
1252 }
1253 }
1254
1255 fclose(f);
1256 return (disk_count != 0);
1257}
1258
1259/* end of multi disk support */
7612bf90 1260
1261static const char * const biosfiles_us[] = {
1262 "us_scd2_9306", "SegaCDBIOS9303", "us_scd1_9210", "bios_CD_U"
1263};
1264static const char * const biosfiles_eu[] = {
1265 "eu_mcd2_9306", "eu_mcd2_9303", "eu_mcd1_9210", "bios_CD_E"
1266};
1267static const char * const biosfiles_jp[] = {
1268 "jp_mcd2_921222", "jp_mcd1_9112", "jp_mcd1_9111", "bios_CD_J"
1269};
1270
1271static void make_system_path(char *buf, size_t buf_size,
1272 const char *name, const char *ext)
1273{
1274 const char *dir = NULL;
1275
1276 if (environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir) && dir) {
1277 snprintf(buf, buf_size, "%s%c%s%s", dir, SLASH, name, ext);
1278 }
1279 else {
1280 snprintf(buf, buf_size, "%s%s", name, ext);
1281 }
1282}
1283
1284static const char *find_bios(int *region, const char *cd_fname)
1285{
1286 const char * const *files;
1287 static char path[256];
1288 int i, count;
1289 FILE *f = NULL;
1290
1291 if (*region == 4) { // US
1292 files = biosfiles_us;
1293 count = sizeof(biosfiles_us) / sizeof(char *);
1294 } else if (*region == 8) { // EU
1295 files = biosfiles_eu;
1296 count = sizeof(biosfiles_eu) / sizeof(char *);
1297 } else if (*region == 1 || *region == 2) {
1298 files = biosfiles_jp;
1299 count = sizeof(biosfiles_jp) / sizeof(char *);
1300 } else {
1301 return NULL;
1302 }
1303
1304 for (i = 0; i < count; i++)
1305 {
1306 make_system_path(path, sizeof(path), files[i], ".bin");
1307 f = fopen(path, "rb");
1308 if (f != NULL)
1309 break;
1310
1311 make_system_path(path, sizeof(path), files[i], ".zip");
1312 f = fopen(path, "rb");
1313 if (f != NULL)
1314 break;
1315 }
1316
1317 if (f != NULL) {
1318 if (log_cb)
1319 log_cb(RETRO_LOG_INFO, "using bios: %s\n", path);
1320 fclose(f);
1321 return path;
1322 }
1323
1324 return NULL;
1325}
1326
549dd407 1327static const char *find_msu(const char *cd_fname)
1328{
1329 static char path[256];
1330 FILE *f = NULL;
1331 int i;
1332
1333 // look for MSU.MD rom file. XXX another extension list? ugh...
1334 static const char *md_exts[] = { "gen", "smd", "md", "32x" };
1335 char *ext = strrchr(cd_fname, '.');
1336 int extpos = ext ? ext-cd_fname : strlen(cd_fname);
1337 strcpy(path, cd_fname);
1338 path[extpos++] = '.';
1339 for (i = 0; i < ARRAY_SIZE(md_exts); i++) {
1340 strcpy(path+extpos, md_exts[i]);
1341 f = fopen(path, "rb");
1342 if (f != NULL) {
1343 log_cb(RETRO_LOG_INFO, "found MSU rom: %s\n", path);
1344 fclose(f);
1345 return path;
1346 }
1347 }
d7e18f86 1348
1349 return NULL;
549dd407 1350}
1351
61d76999 1352static void set_memory_maps(void)
1353{
1354 if (PicoIn.AHW & PAHW_MCD)
1355 {
1356 const size_t SCD_BIT = 1ULL << 31ULL;
1357 const uint64_t mem = RETRO_MEMDESC_SYSTEM_RAM;
1358 struct retro_memory_map mmaps;
1359 struct retro_memory_descriptor descs[] = {
1360 { mem, PicoMem.ram, 0, 0xFF0000, 0, 0, 0x10000, "68KRAM" },
1361 /* virtual address using SCD_BIT so all 512M of prg_ram can be accessed */
1362 /* at address $80020000 */
1363 { mem, Pico_mcd->prg_ram, 0, SCD_BIT | 0x020000, 0, 0, 0x80000, "PRGRAM" },
1364 };
1365
1366 mmaps.descriptors = descs;
1367 mmaps.num_descriptors = sizeof(descs) / sizeof(descs[0]);
1368 environ_cb(RETRO_ENVIRONMENT_SET_MEMORY_MAPS, &mmaps);
1369 }
1370}
1371
7612bf90 1372bool retro_load_game(const struct retro_game_info *info)
1373{
a5085db3 1374 const struct retro_game_info_ext *info_ext = NULL;
1375 const unsigned char *content_data = NULL;
1376 size_t content_size = 0;
1377 char content_path[PATH_MAX];
1378 char content_ext[8];
1379 char carthw_path[PATH_MAX];
7612bf90 1380 enum media_type_e media_type;
7612bf90 1381 size_t i;
1382
a5085db3 1383#if defined(_WIN32)
1384 char slash = '\\';
1385#else
1386 char slash = '/';
1387#endif
1388
1389 content_path[0] = '\0';
1390 content_ext[0] = '\0';
1391 carthw_path[0] = '\0';
1392
1393 unsigned int cd_index = 0;
1394 bool is_m3u = false;
1395
7612bf90 1396 struct retro_input_descriptor desc[] = {
1397 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" },
1398 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" },
1399 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" },
1400 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
1401 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" },
1402 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "C" },
1403 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Y" },
1404 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "A" },
1405 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "X" },
1406 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "Z" },
1407 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,"Mode" },
1408 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
1409
1410 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" },
1411 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" },
1412 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" },
1413 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
1414 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" },
1415 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "C" },
1416 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Y" },
1417 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "A" },
1418 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "X" },
1419 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "Z" },
1420 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,"Mode" },
1421 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
1422
1d5885dd 1423
1424 { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" },
1425 { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" },
1426 { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" },
1427 { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
1428 { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" },
1429 { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "C" },
1430 { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Y" },
1431 { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "A" },
1432 { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "X" },
1433 { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "Z" },
1434 { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,"Mode" },
1435 { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
1436
1437
1438 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" },
1439 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" },
1440 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" },
1441 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
1442 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" },
1443 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "C" },
1444 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Y" },
1445 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "A" },
1446 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "X" },
1447 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "Z" },
1448 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,"Mode" },
1449 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
1450
7612bf90 1451 { 0 },
1452 };
1453
1454 struct retro_input_descriptor desc_sms[] = {
1455 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" },
1456 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" },
1457 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" },
1458 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
1459 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "Button 1 Start" },
1460 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "Button 2" },
1461 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Button Pause" },
1462
1463 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" },
1464 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" },
1465 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" },
1466 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
1467 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "Button 1 Start" },
1468 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "Button 2" },
1469 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Button Pause" },
1470
1471 { 0 },
1472 };
1473
a5085db3 1474 struct retro_input_descriptor desc_pico[] = {
1475 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left (violet)" },
1476 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up (white)" },
1477 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down (orange)" },
1478 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right (green)" },
1479 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "Red Button" },
1480 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "Pen Button" },
5f9901e0 1481 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Pen State" },
62e763ec 1482 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Pen on Storyware" },
5f9901e0 1483 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Pen on Pad" },
1484 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "Previous Page" },
1485 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "Next Page" },
a5085db3 1486
1487 { 0 },
1488 };
1489
1490 /* Attempt to fetch extended game info */
1491 if (environ_cb(RETRO_ENVIRONMENT_GET_GAME_INFO_EXT, &info_ext))
1492 {
1493#if !defined(LOW_MEMORY)
1494 content_data = (const unsigned char *)info_ext->data;
1495 content_size = info_ext->size;
1496#endif
1497 strncpy(base_dir, info_ext->dir, sizeof(base_dir));
1498 base_dir[sizeof(base_dir) - 1] = '\0';
1499
1500 strncpy(content_ext, info_ext->ext, sizeof(content_ext));
1501 content_ext[sizeof(content_ext) - 1] = '\0';
1502
1503 if (info_ext->file_in_archive)
1504 {
1505 /* We don't have a 'physical' file in this
1506 * case, but the core still needs a filename
1507 * in order to detect media type. We therefore
1508 * fake it, using the content directory,
1509 * canonical content name, and content file
1510 * extension */
1511 snprintf(content_path, sizeof(content_path), "%s%c%s.%s",
1512 base_dir, slash, info_ext->name, content_ext);
1513 }
1514 else
1515 {
1516 strncpy(content_path, info_ext->full_path, sizeof(content_path));
1517 content_path[sizeof(content_path) - 1] = '\0';
1518 }
1519 }
1520 else
1521 {
1522 const char *ext = NULL;
1523
1524 if (!info || !info->path)
1525 {
1526 if (log_cb)
1527 log_cb(RETRO_LOG_ERROR, "info->path required\n");
1528 return false;
1529 }
1530
1531 extract_directory(base_dir, info->path, sizeof(base_dir));
1532
1533 strncpy(content_path, info->path, sizeof(content_path));
1534 content_path[sizeof(content_path) - 1] = '\0';
1535
1536 if ((ext = strrchr(info->path, '.')))
1537 {
1538 strncpy(content_ext, ext + 1, sizeof(content_ext));
1539 content_ext[sizeof(content_ext) - 1] = '\0';
1540 }
1541 }
1542
7612bf90 1543 enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_RGB565;
1544 if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt)) {
1545 if (log_cb)
1546 log_cb(RETRO_LOG_ERROR, "RGB565 support required, sorry\n");
1547 return false;
1548 }
1549
a5085db3 1550 disk_init();
7612bf90 1551
a5085db3 1552 is_m3u = (strcasestr(content_ext, "m3u") != NULL);
1553 if (is_m3u)
1554 {
1555 if (!read_m3u(content_path))
1556 {
1557 log_cb(RETRO_LOG_INFO, "failed to read m3u file\n");
1558 return false;
7612bf90 1559 }
a5085db3 1560
1561 strncpy(content_path, disks[0].fname, sizeof(content_path));
1562 content_path[sizeof(content_path) - 1] = '\0';
1563 }
1564 else
1565 {
1566 char disk_label[PATH_MAX];
1567 disk_label[0] = '\0';
1568
1569 disk_current_index = 0;
1570 disk_count = 1;
1571 disks[0].fname = strdup(content_path);
1572
1573 get_disk_label(disk_label, content_path, PATH_MAX);
1574 disks[0].flabel = strdup(disk_label);
7612bf90 1575 }
1576
a5085db3 1577 /* If this is an M3U file, attempt to set the
1578 * initial disk image */
1579 if (is_m3u && (disk_initial_index > 0) && (disk_initial_index < disk_count))
1580 {
1581 const char *fname = disks[disk_initial_index].fname;
1582
1583 if (fname && (*fname != '\0'))
1584 if (strcmp(disk_initial_path, fname) == 0)
1585 cd_index = disk_initial_index;
1586
1587 /* If we are not loading the first disk in the
1588 * M3U list, update the content_path string
1589 * that will be passed to PicoLoadMedia() */
1590 if (cd_index != 0)
1591 {
1592 strncpy(content_path, disks[cd_index].fname, sizeof(content_path));
1593 content_path[sizeof(content_path) - 1] = '\0';
1594 }
1595 }
7612bf90 1596
1597 make_system_path(carthw_path, sizeof(carthw_path), "carthw", ".cfg");
1598
a5085db3 1599 media_type = PicoLoadMedia(content_path, content_data, content_size,
549dd407 1600 carthw_path, find_bios, find_msu, NULL);
a5085db3 1601
1602 disk_current_index = cd_index;
7612bf90 1603
1604 switch (media_type) {
1605 case PM_BAD_DETECT:
1606 if (log_cb)
1607 log_cb(RETRO_LOG_ERROR, "Failed to detect ROM/CD image type.\n");
1608 return false;
1609 case PM_BAD_CD:
1610 if (log_cb)
1611 log_cb(RETRO_LOG_ERROR, "Invalid CD image\n");
1612 return false;
1613 case PM_BAD_CD_NO_BIOS:
1614 if (log_cb)
1615 log_cb(RETRO_LOG_ERROR, "Missing BIOS\n");
1616 return false;
1617 case PM_ERROR:
1618 if (log_cb)
1619 log_cb(RETRO_LOG_ERROR, "Load error\n");
1620 return false;
1621 default:
1622 break;
1623 }
1624
5f9901e0 1625 strncpy(pico_overlay_path, content_path, sizeof(pico_overlay_path)-4);
a5085db3 1626 if (PicoIn.AHW & PAHW_PICO)
1627 environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc_pico);
1628 else if (PicoIn.AHW & PAHW_SMS)
7612bf90 1629 environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc_sms);
1630 else
1631 environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc);
1632
1633 PicoLoopPrepare();
1634
6311a3ba 1635 PicoIn.writeSound = snd_write;
7612bf90 1636 memset(sndBuffer, 0, sizeof(sndBuffer));
6311a3ba 1637 PicoIn.sndOut = sndBuffer;
a990d9c4 1638 if (PicoIn.sndRate > 52000 && PicoIn.sndRate < 54000)
882f697a 1639 PicoIn.sndRate = YM2612_NATIVE_RATE();
7612bf90 1640 PsndRerate(0);
1641
1bbe9abf 1642 apply_renderer();
63235526 1643
61d76999 1644 /* Setup retro memory maps */
1645 set_memory_maps();
1646
1647 init_frameskip();
1648
906cc854 1649 /* Initialisation routines may have 'triggered'
1650 * a libretro AV info or geometry update; this
1651 * happens automatically after retro_load_game(),
1652 * so disable the relevant flags here to avoid
1653 * redundant updates on the first call of
1654 * retro_run() */
1655 libretro_update_av_info = false;
1656 libretro_update_geometry = false;
1657
7612bf90 1658 return true;
1659}
1660
1661bool retro_load_game_special(unsigned game_type, const struct retro_game_info *info, size_t num_info)
1662{
1663 return false;
1664}
1665
1666void retro_unload_game(void)
1667{
1668}
1669
1670unsigned retro_get_region(void)
1671{
1672 return Pico.m.pal ? RETRO_REGION_PAL : RETRO_REGION_NTSC;
1673}
1674
1675void *retro_get_memory_data(unsigned type)
1676{
60ef69d6 1677 void *data;
7612bf90 1678
1679 switch(type)
1680 {
1681 case RETRO_MEMORY_SAVE_RAM:
61d76999 1682 /* Note: MCD RAM cart uses Pico.sv.data */
b9312048 1683 if ((PicoIn.AHW & PAHW_MCD) && Pico.romsize == 0 &&
61d76999 1684 !(PicoIn.opt & POPT_EN_MCD_RAMCART))
7612bf90 1685 data = Pico_mcd->bram;
62efb13d 1686 else // TODO: handle copying to/from bram somewhere
28a5b392 1687 data = Pico.sv.data;
7612bf90 1688 break;
1689 case RETRO_MEMORY_SYSTEM_RAM:
93f9619e 1690 if (PicoIn.AHW & PAHW_SMS)
28a5b392 1691 data = PicoMem.zram;
7612bf90 1692 else
28a5b392 1693 data = PicoMem.ram;
7612bf90 1694 break;
60ef69d6 1695 case RETRO_MEMORY_VIDEO_RAM:
1696 data = PicoMem.vram;
1697 break;
1698 case 4:
1699 data = PicoMem.cram;
1700 break;
7612bf90 1701 default:
1702 data = NULL;
1703 break;
1704 }
1705
1706 return data;
1707}
1708
1709size_t retro_get_memory_size(unsigned type)
1710{
1711 unsigned int i;
1712 int sum;
1713
1714 switch(type)
1715 {
1716 case RETRO_MEMORY_SAVE_RAM:
fe90daf2 1717 if ((PicoIn.AHW & PAHW_MCD) && Pico.romsize == 0)
61d76999 1718 {
fe90daf2 1719 if (PicoIn.opt & POPT_EN_MCD_RAMCART)
61d76999 1720 return 0x12000;
1721 else /* bram */
1722 return 0x2000;
1723 }
7612bf90 1724
1725 if (Pico.m.frame_count == 0)
28a5b392 1726 return Pico.sv.size;
7612bf90 1727
1728 // if game doesn't write to sram, don't report it to
1729 // libretro so that RA doesn't write out zeroed .srm
28a5b392 1730 for (i = 0, sum = 0; i < Pico.sv.size; i++)
1731 sum |= Pico.sv.data[i];
7612bf90 1732
28a5b392 1733 return (sum != 0) ? Pico.sv.size : 0;
7612bf90 1734
1735 case RETRO_MEMORY_SYSTEM_RAM:
93f9619e 1736 if (PicoIn.AHW & PAHW_SMS)
7612bf90 1737 return 0x2000;
1738 else
28a5b392 1739 return sizeof(PicoMem.ram);
7612bf90 1740
1741 default:
1742 return 0;
1743 }
1744
1745}
1746
1747void retro_reset(void)
1748{
1749 PicoReset();
1750}
1751
1752static const unsigned short retro_pico_map[] = {
e8658312 1753 [RETRO_DEVICE_ID_JOYPAD_B] = 1 << GBTN_B,
1754 [RETRO_DEVICE_ID_JOYPAD_Y] = 1 << GBTN_A,
1755 [RETRO_DEVICE_ID_JOYPAD_SELECT] = 1 << GBTN_MODE,
1756 [RETRO_DEVICE_ID_JOYPAD_START] = 1 << GBTN_START,
1757 [RETRO_DEVICE_ID_JOYPAD_UP] = 1 << GBTN_UP,
1758 [RETRO_DEVICE_ID_JOYPAD_DOWN] = 1 << GBTN_DOWN,
1759 [RETRO_DEVICE_ID_JOYPAD_LEFT] = 1 << GBTN_LEFT,
1760 [RETRO_DEVICE_ID_JOYPAD_RIGHT] = 1 << GBTN_RIGHT,
1761 [RETRO_DEVICE_ID_JOYPAD_A] = 1 << GBTN_C,
1762 [RETRO_DEVICE_ID_JOYPAD_X] = 1 << GBTN_Y,
1763 [RETRO_DEVICE_ID_JOYPAD_L] = 1 << GBTN_X,
1764 [RETRO_DEVICE_ID_JOYPAD_R] = 1 << GBTN_Z,
7612bf90 1765};
1766#define RETRO_PICO_MAP_LEN (sizeof(retro_pico_map) / sizeof(retro_pico_map[0]))
1767
c88b729b 1768static int has_4_pads;
1769
7612bf90 1770static void snd_write(int len)
1771{
6311a3ba 1772 audio_batch_cb(PicoIn.sndOut, len / 4);
7612bf90 1773}
1774
1775static enum input_device input_name_to_val(const char *name)
1776{
1777 if (strcmp(name, "3 button pad") == 0)
1778 return PICO_INPUT_PAD_3BTN;
1779 if (strcmp(name, "6 button pad") == 0)
1780 return PICO_INPUT_PAD_6BTN;
1d5885dd 1781 if (strcmp(name, "team player") == 0)
1782 return PICO_INPUT_PAD_TEAM;
1783 if (strcmp(name, "4way play") == 0)
1784 return PICO_INPUT_PAD_4WAY;
7612bf90 1785 if (strcmp(name, "None") == 0)
1786 return PICO_INPUT_NOTHING;
1787
1788 if (log_cb)
1789 log_cb(RETRO_LOG_WARN, "invalid picodrive_input: '%s'\n", name);
1790 return PICO_INPUT_PAD_3BTN;
1791}
1792
61d76999 1793static void update_variables(bool first_run)
7612bf90 1794{
1795 struct retro_variable var;
24aab4da 1796 int OldPicoRegionOverride;
2767adb5 1797 float old_vout_aspect;
61d76999 1798 unsigned old_frameskip_type;
cd262e4c 1799 int old_vout_format;
61d76999 1800 double new_sound_rate;
30969671 1801 unsigned short old_snd_filter;
1802 int32_t old_snd_filter_range;
7612bf90 1803
1804 var.value = NULL;
1805 var.key = "picodrive_input1";
c88b729b 1806 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1807 int input = input_name_to_val(var.value);
1808 PicoSetInputDevice(0, input);
1809 has_4_pads = input == PICO_INPUT_PAD_TEAM || input == PICO_INPUT_PAD_4WAY;
1810 }
7612bf90 1811
1812 var.value = NULL;
1813 var.key = "picodrive_input2";
1814 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
1815 PicoSetInputDevice(1, input_name_to_val(var.value));
1816
1817 var.value = NULL;
280bfc3c 1818 var.key = "picodrive_ramcart";
7612bf90 1819 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1820 if (strcmp(var.value, "enabled") == 0)
280bfc3c 1821 PicoIn.opt |= POPT_EN_MCD_RAMCART;
7612bf90 1822 else
280bfc3c 1823 PicoIn.opt &= ~POPT_EN_MCD_RAMCART;
7612bf90 1824 }
1825
1826 var.value = NULL;
280bfc3c 1827 var.key = "picodrive_smstype";
7612bf90 1828 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
280bfc3c 1829 if (strcmp(var.value, "Auto") == 0)
1830 PicoIn.hwSelect = PHWS_AUTO;
1831 else if (strcmp(var.value, "Game Gear") == 0)
1832 PicoIn.hwSelect = PHWS_GG;
df6c895c 1833 else if (strcmp(var.value, "SG-1000") == 0)
1834 PicoIn.hwSelect = PHWS_SG;
cab84f29 1835 else if (strcmp(var.value, "SC-3000") == 0)
1836 PicoIn.hwSelect = PHWS_SC;
7612bf90 1837 else
280bfc3c 1838 PicoIn.hwSelect = PHWS_SMS;
7612bf90 1839 }
1840
6791d847 1841 var.value = NULL;
1842 var.key = "picodrive_smsfm";
1843 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1844 if (strcmp(var.value, "on") == 0)
1845 PicoIn.opt |= POPT_EN_YM2413;
1846 else
1847 PicoIn.opt &= ~POPT_EN_YM2413;
1848 }
1849
214a6c62 1850 var.value = NULL;
1851 var.key = "picodrive_smstms";
1852 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1853 if (strcmp(var.value, "SG-1000") == 0)
1854 PicoIn.tmsPalette = 1;
1855 else
1856 PicoIn.tmsPalette = 0;
1857 }
1858
f9ea940f 1859 var.value = NULL;
1860 var.key = "picodrive_smsmapper";
1861 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1862 if (strcmp(var.value, "Auto") == 0)
06d963c7 1863 PicoIn.mapper = PMS_MAP_AUTO;
f9ea940f 1864 else if (strcmp(var.value, "Codemasters") == 0)
06d963c7 1865 PicoIn.mapper = PMS_MAP_CODEM;
f9ea940f 1866 else if (strcmp(var.value, "Korea") == 0)
06d963c7 1867 PicoIn.mapper = PMS_MAP_KOREA;
f9ea940f 1868 else if (strcmp(var.value, "Korea MSX") == 0)
06d963c7 1869 PicoIn.mapper = PMS_MAP_MSX;
f9ea940f 1870 else if (strcmp(var.value, "Korea X-in-1") == 0)
06d963c7 1871 PicoIn.mapper = PMS_MAP_N32K;
f9ea940f 1872 else if (strcmp(var.value, "Korea 4-Pak") == 0)
06d963c7 1873 PicoIn.mapper = PMS_MAP_N16K;
f9ea940f 1874 else if (strcmp(var.value, "Korea Janggun") == 0)
06d963c7 1875 PicoIn.mapper = PMS_MAP_JANGGUN;
f9ea940f 1876 else if (strcmp(var.value, "Korea Nemesis") == 0)
06d963c7 1877 PicoIn.mapper = PMS_MAP_NEMESIS;
171fb8cc 1878 else if (strcmp(var.value, "Taiwan 8K RAM") == 0)
df6c895c 1879 PicoIn.mapper = PMS_MAP_8KBRAM;
f9ea940f 1880 else
06d963c7 1881 PicoIn.mapper = PMS_MAP_SEGA;
f9ea940f 1882 }
1883
f55ce7bf 1884 var.value = NULL;
1885 var.key = "picodrive_ggghost";
1886 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1887 if (strcmp(var.value, "normal") == 0)
1888 vout_ghosting = 2;
1889 else if (strcmp(var.value, "weak") == 0)
1890 vout_ghosting = 1;
1891 else
1892 vout_ghosting = 0;
1893 }
1894
93f9619e 1895 OldPicoRegionOverride = PicoIn.regionOverride;
7612bf90 1896 var.value = NULL;
1897 var.key = "picodrive_region";
1898 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1899 if (strcmp(var.value, "Auto") == 0)
93f9619e 1900 PicoIn.regionOverride = 0;
7612bf90 1901 else if (strcmp(var.value, "Japan NTSC") == 0)
93f9619e 1902 PicoIn.regionOverride = 1;
7612bf90 1903 else if (strcmp(var.value, "Japan PAL") == 0)
93f9619e 1904 PicoIn.regionOverride = 2;
7612bf90 1905 else if (strcmp(var.value, "US") == 0)
93f9619e 1906 PicoIn.regionOverride = 4;
7612bf90 1907 else if (strcmp(var.value, "Europe") == 0)
93f9619e 1908 PicoIn.regionOverride = 8;
7612bf90 1909 }
1910
7612bf90 1911 // Update region, fps and sound flags if needed
93f9619e 1912 if (Pico.rom && PicoIn.regionOverride != OldPicoRegionOverride)
7612bf90 1913 {
1914 PicoDetectRegion();
1915 PicoLoopPrepare();
a990d9c4 1916 if (PicoIn.sndRate > 52000 && PicoIn.sndRate < 54000)
882f697a 1917 PicoIn.sndRate = YM2612_NATIVE_RATE();
1918 PsndRerate(!first_run);
7612bf90 1919 }
1920
2767adb5 1921 old_vout_aspect = vout_aspect;
7612bf90 1922 var.value = NULL;
1923 var.key = "picodrive_aspect";
1924 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1925 if (strcmp(var.value, "4/3") == 0)
2767adb5 1926 vout_aspect = VOUT_4_3;
7612bf90 1927 else if (strcmp(var.value, "CRT") == 0)
2767adb5 1928 vout_aspect = VOUT_CRT;
7612bf90 1929 else
2767adb5 1930 vout_aspect = VOUT_PAR;
7612bf90 1931 }
1932
906cc854 1933 /* Notify frontend of geometry update */
2767adb5 1934 if (vout_aspect != old_vout_aspect)
906cc854 1935 libretro_update_geometry = true;
61d76999 1936
280bfc3c 1937 var.value = NULL;
1938 var.key = "picodrive_sprlim";
1939 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1940 if (strcmp(var.value, "enabled") == 0)
1941 PicoIn.opt |= POPT_DIS_SPRITE_LIM;
1942 else
1943 PicoIn.opt &= ~POPT_DIS_SPRITE_LIM;
1944 }
1945
35f2b65e 1946 var.value = NULL;
1947 var.key = "picodrive_overclk68k";
1948 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1949 PicoIn.overclockM68k = 0;
1950 if (var.value[0] == '+')
1951 PicoIn.overclockM68k = atoi(var.value + 1);
1952 }
1953
7612bf90 1954#ifdef DRC_SH2
1955 var.value = NULL;
1956 var.key = "picodrive_drc";
1957 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1958 if (strcmp(var.value, "enabled") == 0)
93f9619e 1959 PicoIn.opt |= POPT_EN_DRC;
7612bf90 1960 else
93f9619e 1961 PicoIn.opt &= ~POPT_EN_DRC;
7612bf90 1962 }
1963#endif
1964#ifdef _3DS
1965 if(!ctr_svchack_successful)
93f9619e 1966 PicoIn.opt &= ~POPT_EN_DRC;
7612bf90 1967#endif
61d76999 1968
23cd73bc 1969 var.value = NULL;
1970 var.key = "picodrive_dacnoise";
1971 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1972 if (strcmp(var.value, "enabled") == 0)
1973 PicoIn.opt |= POPT_EN_FM_DAC;
1974 else
1975 PicoIn.opt &= ~POPT_EN_FM_DAC;
1976 }
1977
68a95087 1978 var.value = NULL;
1979 var.key = "picodrive_fm_filter";
1980 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1981 if (strcmp(var.value, "on") == 0)
1982 PicoIn.opt |= POPT_EN_FM_FILTER;
1983 else
1984 PicoIn.opt &= ~POPT_EN_FM_FILTER;
1985 }
1986
37631374 1987 old_snd_filter = PicoIn.opt & POPT_EN_SNDFILTER;
61d76999 1988 var.value = NULL;
1989 var.key = "picodrive_audio_filter";
37631374 1990 PicoIn.opt &= ~POPT_EN_SNDFILTER;
61d76999 1991 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1992 if (strcmp(var.value, "low-pass") == 0)
37631374 1993 PicoIn.opt |= POPT_EN_SNDFILTER;
61d76999 1994 }
1995
37631374 1996 old_snd_filter_range = PicoIn.sndFilterAlpha;
61d76999 1997 var.value = NULL;
1998 var.key = "picodrive_lowpass_range";
37631374 1999 PicoIn.sndFilterAlpha = (60 * 0x10000 / 100);
61d76999 2000 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
37631374 2001 PicoIn.sndFilterAlpha = (atoi(var.value) * 0x10000 / 100);
61d76999 2002 }
2003
37631374 2004 if (((old_snd_filter ^ PicoIn.opt) & POPT_EN_SNDFILTER) ||
2005 old_snd_filter_range != PicoIn.sndFilterAlpha) {
2006 mix_reset (PicoIn.opt & POPT_EN_SNDFILTER ? PicoIn.sndFilterAlpha : 0);
30969671 2007 }
2008
61d76999 2009 old_frameskip_type = frameskip_type;
2010 frameskip_type = 0;
2011 var.key = "picodrive_frameskip";
2012 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
2013 if (strcmp(var.value, "auto") == 0)
2014 frameskip_type = 1;
2015 else if (strcmp(var.value, "manual") == 0)
2016 frameskip_type = 2;
2017 }
2018
2019 frameskip_threshold = 33;
2020 var.key = "picodrive_frameskip_threshold";
2021 var.value = NULL;
2022 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2023 frameskip_threshold = strtol(var.value, NULL, 10);
2024
cd262e4c 2025 old_vout_format = vout_format;
61d76999 2026 var.value = NULL;
2027 var.key = "picodrive_renderer";
2028 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
2029 if (strcmp(var.value, "fast") == 0)
cd262e4c 2030 vout_format = PDF_NONE;
2031 else if (strcmp(var.value, "good") == 0)
2032 vout_format = PDF_8BIT;
2033 else if (strcmp(var.value, "accurate") == 0)
2034 vout_format = PDF_RGB555;
1bbe9abf 2035 vout_16bit = vout_format == PDF_RGB555 || (PicoIn.AHW & PAHW_32X);
cd262e4c 2036
1bbe9abf 2037 apply_renderer();
61d76999 2038 }
2039
2040 var.value = NULL;
2041 var.key = "picodrive_sound_rate";
2042 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
882f697a 2043 if (!strcmp(var.value, "native"))
906cc854 2044 new_sound_rate = YM2612_NATIVE_RATE();
2045 else
2046 new_sound_rate = atoi(var.value);
61d76999 2047 if (new_sound_rate != PicoIn.sndRate) {
2048 /* Update the sound rate */
2049 PicoIn.sndRate = new_sound_rate;
882f697a 2050 PsndRerate(!first_run);
906cc854 2051 libretro_update_av_info = true;
61d76999 2052 }
2053 }
2054
cd262e4c 2055 /* setup video if required */
e8658312 2056 if (vout_format != old_vout_format)
cd262e4c 2057 {
bccd8832 2058 if (vout_buf &&
2059 (vm_current_start_line != -1) && (vm_current_line_count != -1) &&
2060 (vm_current_start_col != -1) && (vm_current_col_count != -1))
cd262e4c 2061 emu_video_mode_change(
d5d17782 2062 vm_current_start_line, vm_current_line_count,
2063 vm_current_start_col, vm_current_col_count);
cd262e4c 2064 }
2065
61d76999 2066 /* Reinitialise frameskipping, if required */
2067 if (((frameskip_type != old_frameskip_type) ||
2068 (Pico.rom && PicoIn.regionOverride != OldPicoRegionOverride)) &&
2069 !first_run)
2070 init_frameskip();
7612bf90 2071}
2072
a5085db3 2073void emu_status_msg(const char *format, ...)
2074{
2075 va_list vl;
2076 int ret;
2077 static char msg[512];
2078
2079 memset (msg, 0, sizeof(msg));
2080
2081 va_start(vl, format);
2082 ret = vsnprintf(msg, sizeof(msg), format, vl);
2083 va_end(vl);
2084
2085 static struct retro_message rmsg;
2086 rmsg.msg = msg;
2087 rmsg.frames = 600;
2088 environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE, &rmsg);
2089}
2090
c87e36d7 2091static void draw_pico_ptr(void)
2092{
2093 int up = (PicoPicohw.pen_pos[0]|PicoPicohw.pen_pos[1]) & 0x8000;
5f9901e0 2094 int x, y, pitch = vout_width;
c87e36d7 2095 unsigned short *p = (unsigned short *)((char *)vout_buf + vout_offset);
2096 int o = (up ? 0x0000 : 0xffff), _ = (up ? 0xffff : 0x0000);
5f9901e0 2097 // storyware pages are actually squished, 2:1
2098 int h = (pico_inp_mode == 1 ? 160 : vout_height);
2099 if (h < 224) y++;
c87e36d7 2100
5f9901e0 2101 x = ((pico_pen_x * vout_width * ((1ULL<<32) / 320 + 1)) >> 32);
2102 y = ((pico_pen_y * h * ((1ULL<<32) / 224 + 1)) >> 32);
c87e36d7 2103 p += x + y * pitch;
2104
5f9901e0 2105 p[-pitch-1] ^= o; p[-pitch] ^= _; p[-pitch+1] ^= _; p[-pitch+2] ^= o;
2106 p[-1] ^= _; p[0] ^= o; p[1] ^= o; p[2] ^= _;
2107 p[pitch-1] ^= _; p[pitch] ^= o; p[pitch+1] ^= o; p[pitch+2] ^= _;
2108 p[2*pitch-1]^= o; p[2*pitch]^= _; p[2*pitch+1]^= _; p[2*pitch+2]^= o;
2109}
2110
2111static int readpng(unsigned short *dest, const char *fname, int req_w, int req_h)
2112{
2113 rpng_t *rpng = rpng_alloc();
2114 FILE *pf = fopen(fname, "rb");
2115 void *png = NULL, *img = NULL;
2116 size_t len;
2117 unsigned int x, y, w = req_w, h = req_h;
2118 int ret = -1;
2119
2120 if (!rpng || !pf) {
2121 lprintf("can't read png file %s", fname);
2122 goto done;
2123 }
2124
2125 // who designed this, reading the whole file for inflating, really?
2126 fseek(pf, 0, SEEK_END);
2127 len = ftell(pf);
2128 fseek(pf, 0, SEEK_SET);
2129 if (!(png = malloc(len))) {
2130 lprintf("oom while reading png file %s", fname);
2131 goto done;
2132 }
2133 fread(png, 1, len, pf);
2134
2135 // again, who designed this? why all this superfluous iterating here?
2136 rpng_set_buf_ptr(rpng, png, len);
2137 rpng_start(rpng);
2138 while (rpng_iterate_image(rpng));
2139 do {
2140 ret = rpng_process_image(rpng, &img, len, &w, &h);
2141 } while (ret == IMAGE_PROCESS_NEXT);
2142
2143 // there's already a scaled pngread in libpicofe, but who cares :-/
2144 if (img && rpng_is_valid(rpng)) {
2145 int x_scale = w*65536 / req_w;
2146 int y_scale = h*65536 / req_h;
2147 int x_ofs, y_ofs, x_pos, y_pos;
2148
2149 if (x_scale < y_scale)
2150 x_scale = y_scale;
2151 else y_scale = x_scale;
2152 x_ofs = req_w - w*65536 / x_scale;
2153 y_ofs = req_h - h*65536 / y_scale;
2154
2155 dest += y_ofs/2*req_w + x_ofs/2;
2156 for (y_pos = 0; y_pos < h*65536; y_pos += y_scale+1)
2157 {
2158 unsigned char *src = (unsigned char *)img + 4*w*(y_pos >> 16);
2159 for (x_pos = 0, len = 0; x_pos < w*65536; x_pos += x_scale+1, len++)
2160 {
2161 // to add insult to injury, rpng writes the image endian dependant!
2162 unsigned int d = *(unsigned int *)&src[4*(x_pos >> 16)];
2163 int r = d >> 16, g = d >> 8, b = d;
2164 *dest++ = PXMAKE(r & 0xff, g & 0xff, b & 0xff);
2165 }
2166 dest += req_w - len;
2167 }
2168 }
2169 ret = 0;
2170
2171done:
2172 if (img) free(img);
2173 if (png) free(png);
2174 if (pf) fclose(pf);
2175 rpng_free(rpng);
2176 return ret;
2177}
2178
2179static unsigned short *load_pico_overlay(int page, int w, int h)
2180{
2181 static const char *pic_exts[] = { "png", "PNG" };
2182 char buffer[PATH_MAX];
2183 char *ext, *fname = NULL;
2184 int extpos, i;
2185
2186 if (pico_page == page && pico_w == w && pico_h == h)
2187 return pico_overlay;
2188 pico_page = page;
2189 pico_w = w, pico_h = h;
2190
2191 ext = strrchr(pico_overlay_path, '.');
2192 extpos = ext ? ext-pico_overlay_path : strlen(pico_overlay_path);
2193 strcpy(buffer, pico_overlay_path);
2194 buffer[extpos++] = '_';
2195 if (page < 0) {
2196 buffer[extpos++] = 'p';
2197 buffer[extpos++] = 'a';
2198 buffer[extpos++] = 'd';
2199 } else
2200 buffer[extpos++] = '0'+PicoPicohw.page;
2201 buffer[extpos++] = '.';
2202
2203 for (i = 0; i < ARRAY_SIZE(pic_exts); i++) {
2204 strcpy(buffer+extpos, pic_exts[i]);
2205 if (path_is_valid(buffer) == RETRO_VFS_STAT_IS_VALID) {
2206 printf("found Pico file: %s\n", buffer);
2207 fname = buffer;
2208 break;
2209 }
2210 }
2211
2212 pico_overlay = realloc(pico_overlay, w*h*2);
2213 memset(pico_overlay, 0, w*h*2);
2214 if (!fname || !pico_overlay || readpng(pico_overlay, fname, w, h)) {
2215 if (pico_overlay)
2216 free(pico_overlay);
2217 pico_overlay = NULL;
2218 }
2219
2220 return pico_overlay;
2221}
2222
2223void emu_pico_overlay(u16 *pd, int w, int h, int pitch)
2224{
2225 u16 *overlay = NULL;
2226 int y, oh = h;
2227
2228 // get overlay
2229 if (pico_inp_mode == 1) {
2230 oh = (w/2 < h ? w/2 : h); // storyware has squished h
2231 overlay = load_pico_overlay(PicoPicohw.page, w, oh);
2232 } else if (pico_inp_mode == 2)
2233 overlay = load_pico_overlay(-1, w, oh);
2234
2235 // copy overlay onto buffer
2236 if (overlay) {
2237 for (y = 0; y < oh; y++)
2238 memcpy(pd + y*pitch, overlay + y*w, w*2);
2239 if (y < h)
2240 memset(pd + y*pitch, 0, w*2);
2241 }
c87e36d7 2242}
2243
a5085db3 2244void run_events_pico(unsigned int events)
2245{
2246 int lim_x;
2247
a5085db3 2248 if (events & (1 << RETRO_DEVICE_ID_JOYPAD_L)) {
2249 PicoPicohw.page--;
2250 if (PicoPicohw.page < 0)
2251 PicoPicohw.page = 0;
2252 emu_status_msg("Page %i", PicoPicohw.page);
2253 }
2254 if (events & (1 << RETRO_DEVICE_ID_JOYPAD_R)) {
2255 PicoPicohw.page++;
fa968c5e 2256 if (PicoPicohw.page > 7)
2257 PicoPicohw.page = 7;
2258 if (PicoPicohw.page == 7) {
2259 // Used in games that require the Keyboard Pico peripheral
2260 emu_status_msg("Test Page");
2261 }
2262 else {
2263 emu_status_msg("Page %i", PicoPicohw.page);
2264 }
a5085db3 2265 }
c87e36d7 2266 if (events & (1 << RETRO_DEVICE_ID_JOYPAD_X)) {
5f9901e0 2267 if (pico_inp_mode == 2) {
2268 pico_inp_mode = 0;
2269 emu_status_msg("Input: D-Pad");
2270 } else {
2271 pico_inp_mode = 2;
2272 emu_status_msg("Input: Pen on Pad");
2273 }
2274 }
2409e64e 2275 if (events & (1 << RETRO_DEVICE_ID_JOYPAD_Y)) {
5f9901e0 2276 if (pico_inp_mode == 1) {
2277 pico_inp_mode = 0;
2278 emu_status_msg("Input: D-Pad");
2279 } else {
2280 pico_inp_mode = 1;
2281 emu_status_msg("Input: Pen on Storyware");
2282 }
c87e36d7 2283 }
2284 if (events & (1 << RETRO_DEVICE_ID_JOYPAD_START)) {
2285 PicoPicohw.pen_pos[0] ^= 0x8000;
2286 PicoPicohw.pen_pos[1] ^= 0x8000;
2287 emu_status_msg("Pen %s", PicoPicohw.pen_pos[0] & 0x8000 ? "Up" : "Down");
2288 }
a5085db3 2289
5f9901e0 2290 if ((PicoIn.pad[0] & 0x20) && pico_inp_mode && pico_overlay) {
2291 pico_inp_mode = 0;
2292 emu_status_msg("Input: D-Pad");
2293 }
a5085db3 2294 if (pico_inp_mode == 0)
2295 return;
2296
2297 /* handle other input modes */
2298 if (PicoIn.pad[0] & 1) pico_pen_y--;
2299 if (PicoIn.pad[0] & 2) pico_pen_y++;
2300 if (PicoIn.pad[0] & 4) pico_pen_x--;
2301 if (PicoIn.pad[0] & 8) pico_pen_x++;
2302 PicoIn.pad[0] &= ~0x0f; // release UDLR
2303
dca20eff 2304 if (pico_pen_y < PICO_PEN_ADJUST_Y)
2305 pico_pen_y = PICO_PEN_ADJUST_Y;
5f9901e0 2306 if (pico_pen_y > 223-1 - PICO_PEN_ADJUST_Y)
2307 pico_pen_y = 223-1 - PICO_PEN_ADJUST_Y;
dca20eff 2308 if (pico_pen_x < PICO_PEN_ADJUST_X)
2309 pico_pen_x = PICO_PEN_ADJUST_X;
5f9901e0 2310 if (pico_pen_x > 319-1 - PICO_PEN_ADJUST_X)
2311 pico_pen_x = 319-1 - PICO_PEN_ADJUST_X;
a5085db3 2312
c87e36d7 2313 PicoPicohw.pen_pos[0] &= 0x8000;
2314 PicoPicohw.pen_pos[1] &= 0x8000;
2315 PicoPicohw.pen_pos[0] |= 0x3c + pico_pen_x;
2316 PicoPicohw.pen_pos[1] |= (pico_inp_mode == 1 ? 0x2f8 : 0x1fc) + pico_pen_y;
a5085db3 2317}
2318
7612bf90 2319void retro_run(void)
2320{
2321 bool updated = false;
c88b729b 2322 int pad, i, padcount;
61d76999 2323 static void *buff;
2324
2325 PicoIn.skipFrame = 0;
7612bf90 2326
2327 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated)
61d76999 2328 update_variables(false);
7612bf90 2329
2330 input_poll_cb();
2331
1d5885dd 2332 PicoIn.pad[0] = PicoIn.pad[1] = PicoIn.pad[2] = PicoIn.pad[3] = 0;
a5085db3 2333 if (PicoIn.AHW & PAHW_PICO)
2334 padcount = 1;
2335 else if (PicoIn.AHW & PAHW_SMS)
2336 padcount = 2;
2337 else
2338 padcount = has_4_pads ? 4 : 2;
2339
2340 int16_t input[4] = {0, 0};
2341
2342 if (libretro_supports_bitmasks)
2343 {
2344 for (pad = 0; pad < padcount; pad++)
2345 {
2346 input[pad] = input_state_cb(
2347 pad, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK);
2348 }
2349 }
2350 else
2351 {
2352 for (pad = 0; pad < padcount; pad++)
2353 {
b106e5c7 2354 for (i = 0; i < RETRO_PICO_MAP_LEN; i++)
2355 if (input_state_cb(pad, RETRO_DEVICE_JOYPAD, 0, i))
a5085db3 2356 input[pad] |= 1 << i;
b106e5c7 2357 }
2358 }
7612bf90 2359
a5085db3 2360 for (pad = 0; pad < padcount; pad++)
2361 for (i = 0; i < RETRO_PICO_MAP_LEN; i++)
2362 if (input[pad] & (1 << i))
2363 PicoIn.pad[pad] |= retro_pico_map[i];
2364
2365 if (PicoIn.AHW == PAHW_PICO) {
c87e36d7 2366 uint16_t ev = input[0] &
2367 ((1 << RETRO_DEVICE_ID_JOYPAD_L) | (1 << RETRO_DEVICE_ID_JOYPAD_R) |
c1ce5c47
L
2368 (1 << RETRO_DEVICE_ID_JOYPAD_X) | (1 << RETRO_DEVICE_ID_JOYPAD_Y) |
2369 (1 << RETRO_DEVICE_ID_JOYPAD_START));
a5085db3 2370 uint16_t new_ev = ev & ~pico_events;
2371 pico_events = ev;
2372 run_events_pico(new_ev);
2373 }
2374
61d76999 2375 if (PicoPatches)
2376 PicoPatchApply();
2377
2378 /* Check whether current frame should
2379 * be skipped */
2380 if ((frameskip_type > 0) && retro_audio_buff_active) {
2381 switch (frameskip_type)
2382 {
2383 case 1: /* auto */
2384 PicoIn.skipFrame = retro_audio_buff_underrun ? 1 : 0;
2385 break;
2386 case 2: /* manual */
2387 PicoIn.skipFrame = (retro_audio_buff_occupancy < frameskip_threshold) ? 1 : 0;
2388 break;
2389 default:
2390 PicoIn.skipFrame = 0;
2391 break;
2392 }
2393
2394 if (!PicoIn.skipFrame || (frameskip_counter >= FRAMESKIP_MAX)) {
2395 PicoIn.skipFrame = 0;
2396 frameskip_counter = 0;
2397 } else
2398 frameskip_counter++;
2399 }
2400
2401 /* If frameskip settings have changed, update
2402 * frontend audio latency */
2403 if (update_audio_latency) {
2404 environ_cb(RETRO_ENVIRONMENT_SET_MINIMUM_AUDIO_LATENCY,
2405 &audio_latency);
2406 update_audio_latency = false;
2407 }
2408
7612bf90 2409 PicoFrame();
2410
906cc854 2411 /* Check whether frontend needs to be notified
2412 * of timing/geometry changes */
2413 if (libretro_update_av_info || libretro_update_geometry) {
2414 struct retro_system_av_info av_info;
2415 retro_get_system_av_info(&av_info);
2416 environ_cb(libretro_update_av_info ?
2417 RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO :
2418 RETRO_ENVIRONMENT_SET_GEOMETRY,
2419 &av_info);
2420 libretro_update_av_info = false;
2421 libretro_update_geometry = false;
2422 }
2423
61d76999 2424 /* If frame was skipped, call video_cb() with
2425 * a NULL buffer and return immediately */
2426 if (PicoIn.skipFrame) {
2427 video_cb(NULL, vout_width, vout_height, vout_width * 2);
2428 return;
2429 }
2430
2431#if defined(RENDER_GSKIT_PS2)
2432 buff = (uint32_t *)RETRO_HW_FRAME_BUFFER_VALID;
2433
2434 if (!ps2) {
7e5b769d 2435 // get access to the graphics hardware
61d76999 2436 if (!environ_cb(RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE, (void **)&ps2) || !ps2) {
2437 printf("Failed to get HW rendering interface!\n");
2438 return;
7e5b769d 2439 }
61d76999 2440
2441 if (ps2->interface_version != RETRO_HW_RENDER_INTERFACE_GSKIT_PS2_VERSION) {
2442 printf("HW render interface mismatch, expected %u, got %u!\n",
2443 RETRO_HW_RENDER_INTERFACE_GSKIT_PS2_VERSION, ps2->interface_version);
2444 return;
2445 }
2446
61d76999 2447 ps2->coreTexture->ClutPSM = GS_PSM_CT16;
2448 ps2->coreTexture->Filter = GS_FILTER_LINEAR;
2449 ps2->coreTexture->Clut = retro_palette;
61d76999 2450
61d76999 2451 ps2->coreTexture->Mem = vout_buf;
7e5b769d 2452 ps2->coreTexture->Width = vout_width;
2453 ps2->coreTexture->Height = vout_height;
2454 ps2->coreTexture->PSM = (vout_16bit ? GS_PSM_CT16 : GS_PSM_T8);
2455 ps2->padding = padding;
61d76999 2456 }
2457
7e5b769d 2458 // CLUT update needed?
2459 if (!vout_16bit && Pico.m.dirtyPal) {
2460 PicoDrawUpdateHighPal();
2461
2462 // Rotate CLUT. PS2 is special since entries in CLUT are not in sequence.
2463 unsigned short int *pal=(void *)retro_palette;
2464 for (i = 0; i < 256; i+=8) {
2465 if ((i&0x18) == 0x08)
2466 memcpy(pal+i,Pico.est.HighPal+i+8,16);
2467 else if ((i&0x18) == 0x10)
2468 memcpy(pal+i,Pico.est.HighPal+i-8,16);
2469 else
2470 memcpy(pal+i,Pico.est.HighPal+i,16);
2471 }
2472 }
61d76999 2473#else
cd262e4c 2474 if (!vout_16bit) {
2475 /* The 8 bit renderers write a CLUT image in Pico.est.Draw2FB, while libretro wants RGB in vout_buf.
2476 * We need to manually copy that to vout_buf, applying the CLUT on the way. Especially
2477 * with the fast renderer this is improving performance, at the expense of accuracy.
61d76999 2478 */
2479 /* This section is mostly copied from pemu_finalize_frame in platform/linux/emu.c */
e8658312 2480 unsigned short *pd = (unsigned short *)((char *)vout_buf + vout_offset);
cd262e4c 2481 /* Skip the leftmost 8 columns (it is used as an overlap area for rendering) */
e8658312 2482 unsigned char *ps = Pico.est.Draw2FB + vm_current_start_line * 328 + 8;
61d76999 2483 unsigned short *pal = Pico.est.HighPal;
2484 int x;
2485 if (Pico.m.dirtyPal)
2486 PicoDrawUpdateHighPal();
96948bdf 2487 /* 8 bit renderers have an extra offset for SMS wíth 1st tile blanked */
2488 if (vout_width == 248)
2489 ps += 8;
e8658312 2490 /* Copy, and skip the leftmost 8 columns again */
2491 for (i = 0; i < vout_height; i++, ps += 8) {
cd262e4c 2492 for (x = 0; x < vout_width; x+=4) {
2493 *pd++ = pal[*ps++];
2494 *pd++ = pal[*ps++];
61d76999 2495 *pd++ = pal[*ps++];
cd262e4c 2496 *pd++ = pal[*ps++];
2497 }
2498 ps += 320-vout_width; /* Advance to next line in case of 32col mode */
2499 }
61d76999 2500 }
2501
f55ce7bf 2502 if (vout_ghosting && vout_height == 144) {
2503 unsigned short *pd = (unsigned short *)vout_buf;
2504 unsigned short *ps = (unsigned short *)vout_ghosting_buf;
2505 int y;
e8658312 2506 for (y = 0; y < vout_height; y++) {
f55ce7bf 2507 if (vout_ghosting == 1)
2508 v_blend(pd, ps, vout_width, p_075_round);
2509 else
2510 v_blend(pd, ps, vout_width, p_05_round);
2511 pd += vout_width;
2512 ps += vout_width;
2513 }
2514 }
2515
5f9901e0 2516 if (PicoIn.AHW & PAHW_PICO) {
2517 int h = vout_height, w = vout_width;
2518 unsigned short *pd = (unsigned short *)((char *)vout_buf + vout_offset);
2519
2520 if (pico_inp_mode)
2521 emu_pico_overlay(pd, w, h, vout_width);
2522 if (pico_inp_mode /*== 2 || overlay*/)
2523 draw_pico_ptr();
2524 }
c87e36d7 2525
61d76999 2526 buff = (char*)vout_buf + vout_offset;
2527#endif
2528
36b2f293 2529 video_cb((short *)buff, vout_width, vout_height, vout_width * 2);
7612bf90 2530}
2531
7612bf90 2532void retro_init(void)
2533{
a5085db3 2534 unsigned dci_version = 0;
7612bf90 2535 struct retro_log_callback log;
2536 int level;
2537
2538 level = 0;
2539 environ_cb(RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL, &level);
2540
2541 if (environ_cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log))
2542 log_cb = log.log;
2543 else
2544 log_cb = NULL;
2545
2546 environ_cb(RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE, &disk_control);
2547
b106e5c7 2548 if (environ_cb(RETRO_ENVIRONMENT_GET_INPUT_BITMASKS, NULL))
2549 libretro_supports_bitmasks = true;
2550
a5085db3 2551 disk_initial_index = 0;
2552 disk_initial_path[0] = '\0';
2553 if (environ_cb(RETRO_ENVIRONMENT_GET_DISK_CONTROL_INTERFACE_VERSION, &dci_version) && (dci_version >= 1))
2554 environ_cb(RETRO_ENVIRONMENT_SET_DISK_CONTROL_EXT_INTERFACE, &disk_control_ext);
2555 else
2556 environ_cb(RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE, &disk_control);
2557
7612bf90 2558#ifdef _3DS
2559 ctr_svchack_successful = ctr_svchack_init();
61d76999 2560 check_rosalina();
7612bf90 2561#elif defined(VITA)
2562 sceBlock = getVMBlock();
2563#endif
2564
466fa079 2565 PicoIn.opt = POPT_EN_STEREO|POPT_EN_FM
6791d847 2566 | POPT_EN_PSG|POPT_EN_Z80|POPT_EN_GG_LCD
7612bf90 2567 | POPT_EN_MCD_PCM|POPT_EN_MCD_CDDA|POPT_EN_MCD_GFX
2568 | POPT_EN_32X|POPT_EN_PWM
2569 | POPT_ACC_SPRITES|POPT_DIS_32C_BORDER;
31efd454 2570#ifdef DRC_SH2
7612bf90 2571#ifdef _3DS
2572 if (ctr_svchack_successful)
2573#endif
93f9619e 2574 PicoIn.opt |= POPT_EN_DRC;
7612bf90 2575#endif
61d76999 2576
2577 struct retro_variable var = { .key = "picodrive_sound_rate" };
2578 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
2579 PicoIn.sndRate = atoi(var.value);
2580 else
5c7cd059 2581 PicoIn.sndRate = SND_RATE_DEFAULT;
61d76999 2582
93f9619e 2583 PicoIn.autoRgnOrder = 0x184; // US, EU, JP
7612bf90 2584
61d76999 2585 vout_width = VOUT_MAX_WIDTH;
2586 vout_height = VOUT_MAX_HEIGHT;
7612bf90 2587#ifdef _3DS
2588 vout_buf = linearMemAlign(VOUT_MAX_WIDTH * VOUT_MAX_HEIGHT * 2, 0x80);
61d76999 2589#elif defined(RENDER_GSKIT_PS2)
7e5b769d 2590 vout_buf = memalign(4096, VOUT_MAX_WIDTH * VOUT_MAX_HEIGHT * 2);
61d76999 2591 retro_palette = memalign(128, gsKit_texture_size_ee(16, 16, GS_PSM_CT16));
7612bf90 2592#else
2593 vout_buf = malloc(VOUT_MAX_WIDTH * VOUT_MAX_HEIGHT * 2);
2594#endif
2595
2596 PicoInit();
7612bf90 2597
6311a3ba 2598 //PicoIn.osdMessage = plat_status_msg_busy_next;
2599 PicoIn.mcdTrayOpen = disk_tray_open;
2600 PicoIn.mcdTrayClose = disk_tray_close;
7612bf90 2601
61d76999 2602 frameskip_type = 0;
2603 frameskip_threshold = 0;
2604 frameskip_counter = 0;
2605 retro_audio_buff_active = false;
2606 retro_audio_buff_occupancy = 0;
2607 retro_audio_buff_underrun = false;
2608 audio_latency = 0;
2609 update_audio_latency = false;
2610
2611 update_variables(true);
7612bf90 2612}
2613
2614void retro_deinit(void)
2615{
27005bdb 2616 size_t i;
2617
bccd8832 2618 PicoExit();
2619
2620 disk_init();
2621
7612bf90 2622#ifdef _3DS
2623 linearFree(vout_buf);
61d76999 2624#elif defined(RENDER_GSKIT_PS2)
2625 free(vout_buf);
2626 free(retro_palette);
2627 ps2 = NULL;
08be5f1d
O
2628#elif defined(__PS3__)
2629 free(vout_buf);
2630 if (page_table[0] > 0 && page_table[1] > 0)
2631 ps3mapi_process_page_free(sysProcessGetPid(), 0x2F, page_table);
7612bf90 2632#else
2633 free(vout_buf);
2634#endif
2635 vout_buf = NULL;
5f9901e0 2636
f55ce7bf 2637 if (vout_ghosting_buf)
2638 free(vout_ghosting_buf);
2639 vout_ghosting_buf = NULL;
5f9901e0 2640 if (pico_overlay)
2641 free(pico_overlay);
2642 pico_overlay = NULL;
f55ce7bf 2643
b106e5c7 2644 libretro_supports_bitmasks = false;
7612bf90 2645}