Commit | Line | Data |
---|---|---|
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 | ||
55 | int svcDuplicateHandle(unsigned int* out, unsigned int original); | |
56 | int svcCloseHandle(unsigned int handle); | |
57 | int svcControlProcessMemory(unsigned int process, void* addr0, void* addr1, | |
58 | unsigned int size, unsigned int type, unsigned int perm); | |
59 | void* linearMemAlign(size_t size, size_t alignment); | |
60 | void linearFree(void* mem); | |
61 | ||
62 | static 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> | |
68 | static int sceBlock; | |
69 | int getVMBlock(); | |
70 | int _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 | ||
76 | static 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 | |
90 | static retro_log_printf_t log_cb; | |
91 | static retro_video_refresh_t video_cb; | |
92 | static retro_input_poll_t input_poll_cb; | |
93 | static retro_input_state_t input_state_cb; | |
94 | static retro_environment_t environ_cb; | |
95 | static 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 | 107 | static const float VOUT_PAR = 0.0; |
466fa079 | 108 | static const float VOUT_4_3 = (4.0f / 3.0f); |
109 | static const float VOUT_CRT = (1.29911f); | |
7612bf90 | 110 | |
e8658312 | 111 | /* Required to allow on the fly changes to 'renderer' */ |
61d76999 | 112 | static int vm_current_start_line = -1; |
113 | static int vm_current_line_count = -1; | |
d5d17782 | 114 | static int vm_current_start_col = -1; |
115 | static int vm_current_col_count = -1; | |
7612bf90 | 116 | |
cd262e4c | 117 | static int vout_16bit = 1; |
118 | static int vout_format = PDF_RGB555; | |
f55ce7bf | 119 | static void *vout_buf, *vout_ghosting_buf; |
7612bf90 | 120 | static int vout_width, vout_height, vout_offset; |
2767adb5 | 121 | static float vout_aspect = 0.0; |
f55ce7bf | 122 | static int vout_ghosting = 0; |
7612bf90 | 123 | |
906cc854 | 124 | static bool libretro_update_av_info = false; |
125 | static 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 | 130 | RETRO_HW_RENDER_INTEFACE_GSKIT_PS2 *ps2 = NULL; |
131 | static void *retro_palette; | |
132 | static struct retro_hw_ps2_insets padding; | |
133 | #endif | |
134 | ||
5c7cd059 | 135 | static short ALIGNED(4) sndBuffer[2*SND_RATE_MAX/50]; |
7612bf90 | 136 | |
137 | static void snd_write(int len); | |
138 | ||
a002255e | 139 | char **g_argv; |
140 | ||
7612bf90 | 141 | #ifdef _WIN32 |
142 | #define SLASH '\\' | |
143 | #else | |
144 | #define SLASH '/' | |
145 | #endif | |
146 | ||
61d76999 | 147 | /* Frameskipping Support */ |
148 | ||
149 | static unsigned frameskip_type = 0; | |
150 | static unsigned frameskip_threshold = 0; | |
151 | static uint16_t frameskip_counter = 0; | |
152 | ||
153 | static bool retro_audio_buff_active = false; | |
154 | static unsigned retro_audio_buff_occupancy = 0; | |
155 | static bool retro_audio_buff_underrun = false; | |
156 | /* Maximum number of consecutive frames that | |
157 | * can be skipped */ | |
158 | #define FRAMESKIP_MAX 60 | |
159 | ||
160 | static unsigned audio_latency = 0; | |
161 | static bool update_audio_latency = false; | |
a5085db3 | 162 | static uint16_t pico_events; |
5f9901e0 | 163 | // Sega Pico stuff |
164 | int pico_inp_mode; | |
a5085db3 | 165 | int pico_pen_x = 320/2, pico_pen_y = 240/2; |
5f9901e0 | 166 | static int pico_page; |
167 | static int pico_w, pico_h; | |
168 | static char pico_overlay_path[PATH_MAX]; | |
169 | static unsigned short *pico_overlay; | |
170 | ||
61d76999 | 171 | |
172 | static 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 | ||
180 | static 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 | ||
223 | void 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 */ |
246 | void _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 | ||
256 | int __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) |
265 | int _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? */ | |
276 | void __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 | |
289 | typedef struct | |
290 | { | |
291 | unsigned int requested_map; | |
292 | void* buffer; | |
293 | }pico_mmap_t; | |
294 | ||
295 | pico_mmap_t pico_mmaps[] = { | |
296 | {0x02000000, 0}, | |
297 | {0x06000000, 0}, | |
298 | {NULL, 0} | |
299 | }; | |
300 | ||
301 | void *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(¤tHandle, 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 | ||
342 | void *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(¤tHandle, 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 | } | |
383 | void 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(¤tHandle, 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 | |
416 | void *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 | ||
443 | void *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 | ||
476 | void 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 |
484 | void *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 | 500 | int 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(¤tHandle, 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 | 537 | static 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 | 548 | void 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 | ||
602 | void 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 | ||
616 | void 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 | 629 | bool libretro_supports_bitmasks = false; |
630 | ||
7612bf90 | 631 | void 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 | ||
666 | void retro_set_video_refresh(retro_video_refresh_t cb) { video_cb = cb; } | |
667 | void retro_set_audio_sample(retro_audio_sample_t cb) { (void)cb; } | |
668 | void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) { audio_batch_cb = cb; } | |
669 | void retro_set_input_poll(retro_input_poll_t cb) { input_poll_cb = cb; } | |
670 | void retro_set_input_state(retro_input_state_t cb) { input_state_cb = cb; } | |
671 | ||
672 | unsigned retro_api_version(void) | |
673 | { | |
674 | return RETRO_API_VERSION; | |
675 | } | |
676 | ||
677 | void retro_set_controller_port_device(unsigned port, unsigned device) | |
678 | { | |
679 | } | |
680 | ||
681 | void 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 | ||
690 | void 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 */ | |
711 | struct savestate_state { | |
712 | const char *load_buf; | |
713 | char *save_buf; | |
714 | size_t size; | |
715 | size_t pos; | |
716 | }; | |
717 | ||
718 | size_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 | ||
737 | size_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 | ||
756 | size_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 | ||
765 | size_t state_eof(void *file) | |
766 | { | |
767 | struct savestate_state *state = file; | |
768 | ||
769 | return state->pos >= state->size; | |
770 | } | |
771 | ||
772 | int 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 */ | |
792 | size_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 | ||
809 | bool 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 | ||
823 | bool 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 | ||
837 | typedef struct patch | |
838 | { | |
839 | unsigned int addr; | |
840 | unsigned short data; | |
841 | unsigned char comp; | |
842 | } patch; | |
843 | ||
844 | extern void decode(char *buff, patch *dest); | |
466fa079 | 845 | extern uint32_t m68k_read16(uint32_t a); |
7612bf90 | 846 | extern void m68k_write16(uint32_t a, uint16_t d); |
847 | ||
848 | void 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 | ||
868 | void 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 | 919 | static unsigned int disk_initial_index; |
7612bf90 | 920 | static bool disk_ejected; |
921 | static unsigned int disk_current_index; | |
922 | static unsigned int disk_count; | |
a5085db3 | 923 | static char disk_initial_path[PATH_MAX]; |
7612bf90 | 924 | static struct disks_state { |
925 | char *fname; | |
a5085db3 | 926 | char *flabel; |
7612bf90 | 927 | } disks[8]; |
928 | ||
a5085db3 | 929 | static 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 | ||
951 | static 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 | 974 | static bool disk_set_eject_state(bool ejected) |
975 | { | |
976 | // TODO? | |
977 | disk_ejected = ejected; | |
978 | return true; | |
979 | } | |
980 | ||
981 | static bool disk_get_eject_state(void) | |
982 | { | |
983 | return disk_ejected; | |
984 | } | |
985 | ||
986 | static unsigned int disk_get_image_index(void) | |
987 | { | |
988 | return disk_current_index; | |
989 | } | |
990 | ||
991 | static 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 | ||
1029 | static unsigned int disk_get_num_images(void) | |
1030 | { | |
1031 | return disk_count; | |
1032 | } | |
1033 | ||
1034 | static 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 | ||
1077 | static 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 | 1086 | static 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 | ||
1102 | static 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 | ||
1123 | static 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 | 1144 | static 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 | 1154 | static 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 | 1167 | static 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 | ||
1174 | static 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 | 1181 | static char base_dir[1024]; |
1182 | ||
1183 | static 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 | ||
1202 | static 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 | ||
1221 | static 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 | |
1261 | static const char * const biosfiles_us[] = { | |
1262 | "us_scd2_9306", "SegaCDBIOS9303", "us_scd1_9210", "bios_CD_U" | |
1263 | }; | |
1264 | static const char * const biosfiles_eu[] = { | |
1265 | "eu_mcd2_9306", "eu_mcd2_9303", "eu_mcd1_9210", "bios_CD_E" | |
1266 | }; | |
1267 | static const char * const biosfiles_jp[] = { | |
1268 | "jp_mcd2_921222", "jp_mcd1_9112", "jp_mcd1_9111", "bios_CD_J" | |
1269 | }; | |
1270 | ||
1271 | static 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 | ||
1284 | static 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 | 1327 | static 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 | 1352 | static 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 | 1372 | bool 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 | ||
1661 | bool retro_load_game_special(unsigned game_type, const struct retro_game_info *info, size_t num_info) | |
1662 | { | |
1663 | return false; | |
1664 | } | |
1665 | ||
1666 | void retro_unload_game(void) | |
1667 | { | |
1668 | } | |
1669 | ||
1670 | unsigned retro_get_region(void) | |
1671 | { | |
1672 | return Pico.m.pal ? RETRO_REGION_PAL : RETRO_REGION_NTSC; | |
1673 | } | |
1674 | ||
1675 | void *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 | ||
1709 | size_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 | ||
1747 | void retro_reset(void) | |
1748 | { | |
1749 | PicoReset(); | |
1750 | } | |
1751 | ||
1752 | static 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 | 1768 | static int has_4_pads; |
1769 | ||
7612bf90 | 1770 | static void snd_write(int len) |
1771 | { | |
6311a3ba | 1772 | audio_batch_cb(PicoIn.sndOut, len / 4); |
7612bf90 | 1773 | } |
1774 | ||
1775 | static 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 | 1793 | static 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 | 2073 | void 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 | 2091 | static 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 | ||
2111 | static 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 | ||
2171 | done: | |
2172 | if (img) free(img); | |
2173 | if (png) free(png); | |
2174 | if (pf) fclose(pf); | |
2175 | rpng_free(rpng); | |
2176 | return ret; | |
2177 | } | |
2178 | ||
2179 | static 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 | ||
2223 | void 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 | 2244 | void 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 | 2319 | void 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 | 2532 | void 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 | ||
2614 | void 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 | } |