pandora: fix readme and pxml version
[picodrive.git] / platform / common / emu.c
CommitLineData
cff531af 1/*\r
2 * PicoDrive\r
3 * (C) notaz, 2007-2010\r
15cc45c0 4 * (C) irixxxx, 2019-2024\r
cff531af 5 *\r
6 * This work is licensed under the terms of MAME license.\r
7 * See COPYING file in the top-level directory.\r
8 */\r
ea8c405f 9\r
10#include <stdio.h>\r
11#include <stdlib.h>\r
4c2e3554 12#include <string.h>\r
b24e0f6c 13#include <stdarg.h>\r
9c9cda8c 14#ifdef __GP2X__\r
ea8c405f 15#include <unistd.h>\r
16#endif\r
17\r
e743be20 18#include "../libpicofe/posix.h"\r
19#include "../libpicofe/input.h"\r
20#include "../libpicofe/fonts.h"\r
df92fbd1 21#include "../libpicofe/sndout.h"\r
e743be20 22#include "../libpicofe/lprintf.h"\r
15cc45c0 23#include "../libpicofe/readpng.h"\r
e743be20 24#include "../libpicofe/plat.h"\r
ea8c405f 25#include "emu.h"\r
bac44b18 26#include "keyboard.h"\r
e743be20 27#include "input_pico.h"\r
28#include "menu_pico.h"\r
39e6f973 29#include "config_file.h"\r
ea8c405f 30\r
efcba75f 31#include <pico/pico_int.h>\r
32#include <pico/patch.h>\r
ea8c405f 33\r
d0eab7da 34#if defined(__GNUC__) && __GNUC__ >= 7\r
35#pragma GCC diagnostic ignored "-Wformat-truncation"\r
36#endif\r
37\r
9c9cda8c 38#ifndef _WIN32\r
39#define PATH_SEP "/"\r
40#define PATH_SEP_C '/'\r
41#else\r
42#define PATH_SEP "\\"\r
43#define PATH_SEP_C '\\'\r
44#endif\r
ea8c405f 45\r
54646a39 46#define STATUS_MSG_TIMEOUT 2000\r
47\r
e2de9939 48void *g_screen_ptr;\r
49\r
9c9cda8c 50int g_screen_width = 320;\r
51int g_screen_height = 240;\r
9cdfc191 52int g_screen_ppitch = 320; // pitch in pixels\r
e2de9939 53\r
b59172e3 54const char *PicoConfigFile = "config2.cfg";\r
58c86d00 55currentConfig_t currentConfig, defaultConfig;\r
ea8c405f 56int state_slot = 0;\r
57int config_slot = 0, config_slot_current = 0;\r
f2cf8472 58int pico_pen_x = 320/2, pico_pen_y = 240/2;\r
b7d64dbd 59int pico_inp_mode;\r
60int flip_after_sync;\r
713c9224 61int engineState = PGS_Menu;\r
62\r
44be7e5c 63int grab_mode;\r
bac44b18 64int kbd_mode;\r
65struct vkbd *vkbd;\r
44be7e5c 66int mouse_x, mouse_y;\r
576c1b8a 67\r
68static int pico_page;\r
69static int pico_w, pico_h;\r
70static u16 *pico_overlay;\r
037f58e1 71static int pico_pad;\r
576c1b8a 72\r
68a95087 73static short __attribute__((aligned(4))) sndBuffer[2*54000/50];\r
df92fbd1 74\r
ae87bffa 75/* tmp buff to reduce stack usage for plats with small stack */\r
76static char static_buff[512];\r
636d5f25 77const char *rom_fname_reload;\r
ae87bffa 78char rom_fname_loaded[512];\r
f2cf8472 79int reset_timing = 0;\r
b24e0f6c 80static unsigned int notice_msg_time; /* when started showing */\r
81static char noticeMsg[40];\r
ea8c405f 82\r
83unsigned char *movie_data = NULL;\r
84static int movie_size = 0;\r
85\r
ea8c405f 86\r
ee2a3bdf 87/* don't use tolower() for easy old glibc binary compatibility */\r
f8af9634 88static void strlwr_(char *string)\r
ea8c405f 89{\r
f8af9634 90 char *p;\r
91 for (p = string; *p; p++)\r
ee2a3bdf 92 if ('A' <= *p && *p <= 'Z')\r
93 *p += 'a' - 'A';\r
ea8c405f 94}\r
95\r
ca482e5d 96static int try_rfn_cut(char *fname)\r
ea8c405f 97{\r
98 FILE *tmp;\r
99 char *p;\r
100\r
ca482e5d 101 p = fname + strlen(fname) - 1;\r
102 for (; p > fname; p--)\r
ea8c405f 103 if (*p == '.') break;\r
104 *p = 0;\r
105\r
ca482e5d 106 if((tmp = fopen(fname, "rb"))) {\r
ea8c405f 107 fclose(tmp);\r
108 return 1;\r
109 }\r
110 return 0;\r
111}\r
112\r
35e3031a 113static void get_ext(const char *file, char *ext)\r
ea8c405f 114{\r
35e3031a 115 const char *p;\r
ea8c405f 116\r
117 p = file + strlen(file) - 4;\r
118 if (p < file) p = file;\r
119 strncpy(ext, p, 4);\r
120 ext[4] = 0;\r
121 strlwr_(ext);\r
122}\r
123\r
fcdefcf6 124static void fname_ext(char *dst, int dstlen, const char *prefix, const char *ext, const char *fname)\r
125{\r
126 int prefix_len = 0;\r
127 const char *p;\r
128\r
129 *dst = 0;\r
130 if (prefix) {\r
131 int len = plat_get_root_dir(dst, dstlen);\r
132 strcpy(dst + len, prefix);\r
133 prefix_len = len + strlen(prefix);\r
134 }\r
135\r
136 p = fname + strlen(fname) - 1;\r
137 for (; p >= fname && *p != PATH_SEP_C; p--)\r
138 ;\r
139 p++;\r
140 strncpy(dst + prefix_len, p, dstlen - prefix_len - 1);\r
141\r
142 dst[dstlen - 8] = 0;\r
a20300bf 143 if ((p = strrchr(dst, '.')) != NULL)\r
144 dst[p-dst] = 0;\r
fcdefcf6 145 if (ext)\r
146 strcat(dst, ext);\r
147}\r
148\r
149static void romfname_ext(char *dst, int dstlen, const char *prefix, const char *ext)\r
150{\r
151 fname_ext(dst, dstlen, prefix, ext, rom_fname_loaded);\r
152}\r
153\r
b24e0f6c 154void emu_status_msg(const char *format, ...)\r
155{\r
156 va_list vl;\r
54646a39 157 int ret;\r
b24e0f6c 158\r
159 va_start(vl, format);\r
54646a39 160 ret = vsnprintf(noticeMsg, sizeof(noticeMsg), format, vl);\r
b24e0f6c 161 va_end(vl);\r
162\r
54646a39 163 /* be sure old text gets overwritten */\r
164 for (; ret < 28; ret++)\r
165 noticeMsg[ret] = ' ';\r
166 noticeMsg[ret] = 0;\r
167\r
b24e0f6c 168 notice_msg_time = plat_get_ticks_ms();\r
169}\r
170\r
2446536b 171static const char * const biosfiles_us[] = {\r
b08a2950 172 "us_scd2_9306", "SegaCDBIOS9303", "us_scd1_9210", "bios_CD_U"\r
2446536b 173};\r
174static const char * const biosfiles_eu[] = {\r
b08a2950 175 "eu_mcd2_9306", "eu_mcd2_9303", "eu_mcd1_9210", "bios_CD_E"\r
2446536b 176};\r
177static const char * const biosfiles_jp[] = {\r
b08a2950 178 "jp_mcd2_921222", "jp_mcd1_9112", "jp_mcd1_9111", "bios_CD_J"\r
2446536b 179};\r
ea8c405f 180\r
4c2e3554 181static const char *find_bios(int *region, const char *cd_fname)\r
ea8c405f 182{\r
ea8c405f 183 int i, count;\r
54646a39 184 const char * const *files;\r
ea8c405f 185 FILE *f = NULL;\r
4c2e3554 186 int ret;\r
187\r
188 // we need to have config loaded at this point\r
189 ret = emu_read_config(cd_fname, 0);\r
190 if (!ret) emu_read_config(NULL, 0);\r
191\r
93f9619e 192 if (PicoIn.regionOverride) {\r
193 *region = PicoIn.regionOverride;\r
4c2e3554 194 lprintf("override region to %s\n", *region != 4 ?\r
195 (*region == 8 ? "EU" : "JAP") : "USA");\r
196 }\r
ea8c405f 197\r
02f3222f 198 // locate BIOS file\r
4c2e3554 199 if (*region == 4) { // US\r
ea8c405f 200 files = biosfiles_us;\r
201 count = sizeof(biosfiles_us) / sizeof(char *);\r
4c2e3554 202 } else if (*region == 8) { // EU\r
ea8c405f 203 files = biosfiles_eu;\r
204 count = sizeof(biosfiles_eu) / sizeof(char *);\r
4c2e3554 205 } else if (*region == 1 || *region == 2) {\r
ea8c405f 206 files = biosfiles_jp;\r
207 count = sizeof(biosfiles_jp) / sizeof(char *);\r
208 } else {\r
209 return 0;\r
210 }\r
211\r
212 for (i = 0; i < count; i++)\r
213 {\r
ae87bffa 214 emu_make_path(static_buff, files[i], sizeof(static_buff) - 4);\r
215 strcat(static_buff, ".bin");\r
216 f = fopen(static_buff, "rb");\r
ea8c405f 217 if (f) break;\r
218\r
ae87bffa 219 static_buff[strlen(static_buff) - 4] = 0;\r
220 strcat(static_buff, ".zip");\r
221 f = fopen(static_buff, "rb");\r
ea8c405f 222 if (f) break;\r
e994ebeb 223\r
224 strcpy(static_buff, files[i]);\r
225 strcat(static_buff, ".bin");\r
226 f = fopen(static_buff, "rb");\r
227 if (f) break;\r
228\r
229 static_buff[strlen(static_buff) - 4] = 0;\r
230 strcat(static_buff, ".zip");\r
231 f = fopen(static_buff, "rb");\r
232 if (f) break;\r
ea8c405f 233 }\r
234\r
235 if (f) {\r
ae87bffa 236 lprintf("using bios: %s\n", static_buff);\r
ea8c405f 237 fclose(f);\r
4c2e3554 238 return static_buff;\r
ea8c405f 239 } else {\r
ae87bffa 240 sprintf(static_buff, "no %s BIOS files found, read docs",\r
4c2e3554 241 *region != 4 ? (*region == 8 ? "EU" : "JAP") : "USA");\r
e743be20 242 menu_update_msg(static_buff);\r
4c2e3554 243 return NULL;\r
ea8c405f 244 }\r
245}\r
246\r
549dd407 247static const char *find_msu(const char *cd_fname)\r
248{\r
249 int i;\r
250\r
251 // look for MSU.MD or MD+ rom file. XXX another extension list? ugh...\r
252 static const char *md_exts[] = { "gen", "smd", "md", "32x" };\r
253 char *ext = strrchr(cd_fname, '.');\r
254 int extpos = ext ? ext-cd_fname : strlen(cd_fname);\r
255 strcpy(static_buff, cd_fname);\r
256 static_buff[extpos++] = '.';\r
257 for (i = 0; i < ARRAY_SIZE(md_exts); i++) {\r
258 strcpy(static_buff+extpos, md_exts[i]);\r
259 if (access(static_buff, R_OK) == 0) {\r
260 printf("found MSU rom: %s\n",static_buff);\r
261 return static_buff;\r
262 }\r
263 }\r
264 return NULL;\r
265}\r
266\r
c6196c0f 267/* check if the name begins with BIOS name */\r
1ca2ea4f 268/*\r
c6196c0f 269static int emu_isBios(const char *name)\r
270{\r
271 int i;\r
272 for (i = 0; i < sizeof(biosfiles_us)/sizeof(biosfiles_us[0]); i++)\r
273 if (strstr(name, biosfiles_us[i]) != NULL) return 1;\r
274 for (i = 0; i < sizeof(biosfiles_eu)/sizeof(biosfiles_eu[0]); i++)\r
275 if (strstr(name, biosfiles_eu[i]) != NULL) return 1;\r
276 for (i = 0; i < sizeof(biosfiles_jp)/sizeof(biosfiles_jp[0]); i++)\r
277 if (strstr(name, biosfiles_jp[i]) != NULL) return 1;\r
278 return 0;\r
279}\r
1ca2ea4f 280*/\r
c6196c0f 281\r
7a87643e 282static int extract_text(char *dest, const unsigned char *src, int len, int swab)\r
58c86d00 283{\r
284 char *p = dest;\r
285 int i;\r
286\r
287 if (swab) swab = 1;\r
288\r
289 for (i = len - 1; i >= 0; i--)\r
290 {\r
291 if (src[i^swab] != ' ') break;\r
292 }\r
293 len = i + 1;\r
294\r
295 for (i = 0; i < len; i++)\r
296 {\r
297 unsigned char s = src[i^swab];\r
298 if (s >= 0x20 && s < 0x7f && s != '#' && s != '|' &&\r
299 s != '[' && s != ']' && s != '\\')\r
300 {\r
301 *p++ = s;\r
302 }\r
303 else\r
304 {\r
305 sprintf(p, "\\%02x", s);\r
306 p += 3;\r
307 }\r
308 }\r
309\r
310 return p - dest;\r
311}\r
312\r
fcdefcf6 313static char *emu_make_rom_id(const char *fname)\r
58c86d00 314{\r
ca482e5d 315 static char id_string[3+0xe*3+0x3*3+0x30*3+3];\r
bdec53c9 316 int pos, swab = 1;\r
58c86d00 317\r
93f9619e 318 if (PicoIn.AHW & PAHW_MCD) {\r
bdec53c9 319 strcpy(id_string, "CD|");\r
320 swab = 0;\r
321 }\r
93f9619e 322 else if (PicoIn.AHW & PAHW_SMS)\r
fcdefcf6 323 strcpy(id_string, "MS|");\r
324 else strcpy(id_string, "MD|");\r
58c86d00 325 pos = 3;\r
326\r
93f9619e 327 if (!(PicoIn.AHW & PAHW_SMS)) {\r
4c2e3554 328 pos += extract_text(id_string + pos, media_id_header + 0x80, 0x0e, swab); // serial\r
fcdefcf6 329 id_string[pos] = '|'; pos++;\r
4c2e3554 330 pos += extract_text(id_string + pos, media_id_header + 0xf0, 0x03, swab); // region\r
fcdefcf6 331 id_string[pos] = '|'; pos++;\r
4c2e3554 332 pos += extract_text(id_string + pos, media_id_header + 0x50, 0x30, swab); // overseas name\r
fcdefcf6 333 id_string[pos] = 0;\r
334 if (pos > 5)\r
335 return id_string;\r
336 pos = 3;\r
337 }\r
338\r
339 // can't find name in ROM, use filename\r
340 fname_ext(id_string + 3, sizeof(id_string) - 3, NULL, NULL, fname);\r
58c86d00 341\r
58c86d00 342 return id_string;\r
343}\r
344\r
ca482e5d 345// buffer must be at least 150 byte long\r
a47dd663 346void emu_get_game_name(char *str150)\r
ca482e5d 347{\r
93f9619e 348 int ret, swab = (PicoIn.AHW & PAHW_MCD) ? 0 : 1;\r
ca482e5d 349 char *s, *d;\r
350\r
4c2e3554 351 ret = extract_text(str150, media_id_header + 0x50, 0x30, swab); // overseas name\r
ca482e5d 352\r
353 for (s = d = str150 + 1; s < str150+ret; s++)\r
354 {\r
355 if (*s == 0) break;\r
356 if (*s != ' ' || d[-1] != ' ')\r
357 *d++ = *s;\r
358 }\r
359 *d = 0;\r
360}\r
361\r
974fdb5b 362static void system_announce(void)\r
363{\r
f3a57b2d 364 const char *sys_name, *tv_standard, *extra = "";\r
974fdb5b 365 int fps;\r
366\r
93f9619e 367 if (PicoIn.AHW & PAHW_SMS) {\r
974fdb5b 368 sys_name = "Master System";\r
0aa63fce 369 if (PicoIn.AHW & PAHW_GG)\r
466fa079 370 sys_name = "Game Gear";\r
0aa63fce 371 else if (PicoIn.AHW & PAHW_SG)\r
df6c895c 372 sys_name = "SG-1000";\r
0aa63fce 373 else if (PicoIn.AHW & PAHW_SC)\r
cab84f29 374 sys_name = "SC-3000";\r
df6c895c 375 else if (Pico.m.hardware & PMS_HW_JAP)\r
b23725dd 376 sys_name = "Mark III";\r
f3a57b2d 377#ifdef NO_SMS\r
378 extra = " [no support]";\r
379#endif\r
93f9619e 380 } else if (PicoIn.AHW & PAHW_PICO) {\r
974fdb5b 381 sys_name = "Pico";\r
93f9619e 382 } else if ((PicoIn.AHW & (PAHW_32X|PAHW_MCD)) == (PAHW_32X|PAHW_MCD)) {\r
fa8fb754 383 sys_name = "32X + Mega CD";\r
384 if ((Pico.m.hardware & 0xc0) == 0x80)\r
385 sys_name = "32X + Sega CD";\r
93f9619e 386 } else if (PicoIn.AHW & PAHW_MCD) {\r
974fdb5b 387 sys_name = "Mega CD";\r
388 if ((Pico.m.hardware & 0xc0) == 0x80)\r
389 sys_name = "Sega CD";\r
93f9619e 390 } else if (PicoIn.AHW & PAHW_32X) {\r
974fdb5b 391 sys_name = "32X";\r
392 } else {\r
d818246c 393 sys_name = "Mega Drive";\r
974fdb5b 394 if ((Pico.m.hardware & 0xc0) == 0x80)\r
395 sys_name = "Genesis";\r
396 }\r
397 tv_standard = Pico.m.pal ? "PAL" : "NTSC";\r
398 fps = Pico.m.pal ? 50 : 60;\r
399\r
f3a57b2d 400 emu_status_msg("%s %s / %dFPS%s", tv_standard, sys_name, fps, extra);\r
974fdb5b 401}\r
402\r
4c2e3554 403static void do_region_override(const char *media_fname)\r
404{\r
405 // we only need to override region if config tells us so\r
406 int ret = emu_read_config(media_fname, 0);\r
407 if (!ret) emu_read_config(NULL, 0);\r
408}\r
409\r
636d5f25 410int emu_reload_rom(const char *rom_fname_in)\r
ea8c405f 411{\r
68af34fe 412 // use setting before rom config is loaded\r
413 int autoload = g_autostateld_opt;\r
636d5f25 414 char *rom_fname = NULL;\r
ea8c405f 415 char ext[5];\r
4c2e3554 416 enum media_type_e media_type;\r
636d5f25 417 int menu_romload_started = 0;\r
4c2e3554 418 char carthw_path[512];\r
636d5f25 419 int retval = 0;\r
ea8c405f 420\r
636d5f25 421 lprintf("emu_ReloadRom(%s)\n", rom_fname_in);\r
ea8c405f 422\r
636d5f25 423 rom_fname = strdup(rom_fname_in);\r
424 if (rom_fname == NULL)\r
425 return 0;\r
426\r
ca482e5d 427 get_ext(rom_fname, ext);\r
ea8c405f 428\r
77189b7d 429 // early cleanup\r
430 PicoPatchUnload();\r
eacee137 431 if (movie_data) {\r
ea8c405f 432 free(movie_data);\r
433 movie_data = 0;\r
434 }\r
77189b7d 435\r
c7e1c39b 436 if (!strcasecmp(ext, ".gmv"))\r
2d2247c2 437 {\r
ea8c405f 438 // check for both gmv and rom\r
439 int dummy;\r
ca482e5d 440 FILE *movie_file = fopen(rom_fname, "rb");\r
b8a1c09a 441 if (!movie_file) {\r
e743be20 442 menu_update_msg("Failed to open movie.");\r
636d5f25 443 goto out;\r
ea8c405f 444 }\r
445 fseek(movie_file, 0, SEEK_END);\r
446 movie_size = ftell(movie_file);\r
447 fseek(movie_file, 0, SEEK_SET);\r
b8a1c09a 448 if (movie_size < 64+3) {\r
e743be20 449 menu_update_msg("Invalid GMV file.");\r
ea8c405f 450 fclose(movie_file);\r
636d5f25 451 goto out;\r
ea8c405f 452 }\r
453 movie_data = malloc(movie_size);\r
b8a1c09a 454 if (movie_data == NULL) {\r
e743be20 455 menu_update_msg("low memory.");\r
ea8c405f 456 fclose(movie_file);\r
636d5f25 457 goto out;\r
ea8c405f 458 }\r
b8a1c09a 459 dummy = fread(movie_data, 1, movie_size, movie_file);\r
ea8c405f 460 fclose(movie_file);\r
461 if (strncmp((char *)movie_data, "Gens Movie TEST", 15) != 0) {\r
e743be20 462 menu_update_msg("Invalid GMV file.");\r
636d5f25 463 goto out;\r
ea8c405f 464 }\r
ca482e5d 465 dummy = try_rfn_cut(rom_fname) || try_rfn_cut(rom_fname);\r
ea8c405f 466 if (!dummy) {\r
e743be20 467 menu_update_msg("Could't find a ROM for movie.");\r
636d5f25 468 goto out;\r
ea8c405f 469 }\r
ca482e5d 470 get_ext(rom_fname, ext);\r
f8af9634 471 lprintf("gmv loaded for %s\n", rom_fname);\r
ea8c405f 472 }\r
c7e1c39b 473 else if (!strcasecmp(ext, ".pat"))\r
ca482e5d 474 {\r
ea8c405f 475 int dummy;\r
ca482e5d 476 PicoPatchLoad(rom_fname);\r
477 dummy = try_rfn_cut(rom_fname) || try_rfn_cut(rom_fname);\r
ea8c405f 478 if (!dummy) {\r
e743be20 479 menu_update_msg("Could't find a ROM to patch.");\r
636d5f25 480 goto out;\r
ea8c405f 481 }\r
ca482e5d 482 get_ext(rom_fname, ext);\r
ea8c405f 483 }\r
484\r
4c2e3554 485 menu_romload_prepare(rom_fname); // also CD load\r
486 menu_romload_started = 1;\r
ea8c405f 487\r
4c2e3554 488 emu_make_path(carthw_path, "carthw.cfg", sizeof(carthw_path));\r
3e49ffd0 489\r
a5085db3 490 media_type = PicoLoadMedia(rom_fname, NULL, 0, carthw_path,\r
549dd407 491 find_bios, find_msu, do_region_override);\r
ea8c405f 492\r
4c2e3554 493 switch (media_type) {\r
494 case PM_BAD_DETECT:\r
495 menu_update_msg("Not a ROM/CD img selected.");\r
636d5f25 496 goto out;\r
4c2e3554 497 case PM_BAD_CD:\r
498 menu_update_msg("Invalid CD image");\r
636d5f25 499 goto out;\r
4c2e3554 500 case PM_BAD_CD_NO_BIOS:\r
501 // find_bios() prints a message\r
636d5f25 502 goto out;\r
4c2e3554 503 case PM_ERROR:\r
504 menu_update_msg("Load error");\r
636d5f25 505 goto out;\r
4c2e3554 506 default:\r
507 break;\r
ea8c405f 508 }\r
509\r
a76fad41 510 // make quirks visible in UI\r
93f9619e 511 if (PicoIn.quirks & PQUIRK_FORCE_6BTN)\r
a76fad41 512 currentConfig.input_dev0 = PICO_INPUT_PAD_6BTN;\r
513\r
4b167c12 514 menu_romload_end();\r
636d5f25 515 menu_romload_started = 0;\r
4b167c12 516\r
ea8c405f 517 if (PicoPatches) {\r
518 PicoPatchPrepare();\r
519 PicoPatchApply();\r
520 }\r
521\r
522 // additional movie stuff\r
f9f40f10 523 if (movie_data)\r
524 {\r
531a8f38 525 enum input_device indev = (movie_data[0x14] == '6') ?\r
526 PICO_INPUT_PAD_6BTN : PICO_INPUT_PAD_3BTN;\r
527 PicoSetInputDevice(0, indev);\r
528 PicoSetInputDevice(1, indev);\r
529\r
93f9619e 530 PicoIn.opt |= POPT_DIS_VDP_FIFO; // no VDP fifo timing\r
f9f40f10 531 if (movie_data[0xF] >= 'A') {\r
532 if (movie_data[0x16] & 0x80) {\r
93f9619e 533 PicoIn.regionOverride = 8;\r
ea8c405f 534 } else {\r
93f9619e 535 PicoIn.regionOverride = 4;\r
ea8c405f 536 }\r
1cb1584b 537 PicoReset();\r
ea8c405f 538 // TODO: bits 6 & 5\r
539 }\r
540 movie_data[0x18+30] = 0;\r
b24e0f6c 541 emu_status_msg("MOVIE: %s", (char *) &movie_data[0x18]);\r
ea8c405f 542 }\r
543 else\r
544 {\r
a54716ec 545 PicoSetInputDevice(0, currentConfig.input_dev0);\r
546 PicoSetInputDevice(1, currentConfig.input_dev1);\r
547\r
974fdb5b 548 system_announce();\r
93f9619e 549 PicoIn.opt &= ~POPT_DIS_VDP_FIFO;\r
ea8c405f 550 }\r
ea8c405f 551\r
bcc9eda0 552 strncpy(rom_fname_loaded, rom_fname, sizeof(rom_fname_loaded)-1);\r
553 rom_fname_loaded[sizeof(rom_fname_loaded)-1] = 0;\r
bcc9eda0 554\r
ea8c405f 555 // load SRAM for this ROM\r
a47dd663 556 if (currentConfig.EmuOpt & EOPT_EN_SRAM)\r
557 emu_save_load_game(1, 1);\r
ea8c405f 558\r
c7074ddb 559 // state autoload?\r
68af34fe 560 if (autoload) {\r
c7074ddb 561 int time, newest = 0, newest_slot = -1;\r
562 int slot;\r
563\r
564 for (slot = 0; slot < 10; slot++) {\r
565 if (emu_check_save_file(slot, &time)) {\r
566 if (time > newest) {\r
567 newest = time;\r
568 newest_slot = slot;\r
569 }\r
570 }\r
571 }\r
572\r
573 if (newest_slot >= 0) {\r
574 lprintf("autoload slot %d\n", newest_slot);\r
575 state_slot = newest_slot;\r
576 emu_save_load_game(1, 0);\r
577 }\r
578 else {\r
579 lprintf("no save to autoload.\n");\r
580 }\r
581 }\r
582\r
636d5f25 583 retval = 1;\r
584out:\r
636d5f25 585 if (menu_romload_started)\r
586 menu_romload_end();\r
587 free(rom_fname);\r
588 return retval;\r
ea8c405f 589}\r
590\r
35e3031a 591int emu_swap_cd(const char *fname)\r
592{\r
15ca7152 593 enum cd_track_type cd_type;\r
35e3031a 594 int ret = -1;\r
595\r
4c2e3554 596 cd_type = PicoCdCheck(fname, NULL);\r
15ca7152 597 if (cd_type != CT_UNKNOWN)\r
274fcc35 598 ret = cdd_load(fname, cd_type);\r
35e3031a 599 if (ret != 0) {\r
e743be20 600 menu_update_msg("Load failed, invalid CD image?");\r
35e3031a 601 return 0;\r
602 }\r
603\r
604 strncpy(rom_fname_loaded, fname, sizeof(rom_fname_loaded)-1);\r
274fcc35 605 rom_fname_loaded[sizeof(rom_fname_loaded) - 1] = 0;\r
606\r
35e3031a 607 return 1;\r
608}\r
609\r
92064e2f 610int emu_play_tape(const char *fname)\r
611{\r
612 int ret;\r
613\r
614 ret = PicoPlayTape(fname);\r
615 if (ret != 0) {\r
616 menu_update_msg("loading tape failed");\r
617 return 0;\r
618 }\r
619 return 1;\r
620}\r
621\r
59f8f7d1 622int emu_record_tape(const char *ext)\r
623{\r
624 int ret;\r
625\r
626 fname_ext(static_buff, sizeof(static_buff), "tape"PATH_SEP, ext, rom_fname_loaded);\r
627 ret = PicoRecordTape(static_buff);\r
628 if (ret != 0) {\r
629 menu_update_msg("recording tape failed");\r
630 return 0;\r
631 }\r
632 return 1;\r
633}\r
634\r
ae87bffa 635// <base dir><end>\r
27701801 636void emu_make_path(char *buff, const char *end, int size)\r
08fe8094 637{\r
27701801 638 int pos, end_len;\r
639\r
640 end_len = strlen(end);\r
641 pos = plat_get_root_dir(buff, size);\r
642 strncpy(buff + pos, end, size - pos);\r
643 buff[size - 1] = 0;\r
644 if (pos + end_len > size - 1)\r
645 lprintf("Warning: path truncated: %s\n", buff);\r
646}\r
647\r
648static void make_config_cfg(char *cfg_buff_512)\r
649{\r
650 emu_make_path(cfg_buff_512, PicoConfigFile, 512-6);\r
08fe8094 651 if (config_slot != 0)\r
652 {\r
27701801 653 char *p = strrchr(cfg_buff_512, '.');\r
654 if (p == NULL)\r
655 p = cfg_buff_512 + strlen(cfg_buff_512);\r
08fe8094 656 sprintf(p, ".%i.cfg", config_slot);\r
657 }\r
27701801 658 cfg_buff_512[511] = 0;\r
08fe8094 659}\r
660\r
697746df 661void emu_prep_defconfig(void)\r
662{\r
663 memset(&defaultConfig, 0, sizeof(defaultConfig));\r
37631374 664 defaultConfig.EmuOpt = EOPT_EN_SRAM | EOPT_EN_SOUND | EOPT_16BPP |\r
15cc45c0 665 EOPT_EN_CD_LEDS | EOPT_GZIP_SAVES | EOPT_PICO_PEN;\r
1a9da199 666 defaultConfig.s_PicoOpt = POPT_EN_SNDFILTER|POPT_EN_GG_LCD|POPT_EN_YM2413 |\r
37631374 667 POPT_EN_STEREO|POPT_EN_FM|POPT_EN_PSG|POPT_EN_Z80 |\r
720bfc5d 668 POPT_EN_MCD_PCM|POPT_EN_MCD_CDDA|POPT_EN_MCD_GFX |\r
92dfd9af 669 POPT_EN_DRC|POPT_ACC_SPRITES |\r
697746df 670 POPT_EN_32X|POPT_EN_PWM;\r
671 defaultConfig.s_PsndRate = 44100;\r
672 defaultConfig.s_PicoRegion = 0; // auto\r
673 defaultConfig.s_PicoAutoRgnOrder = 0x184; // US, EU, JP\r
280bfc3c 674 defaultConfig.s_hwSelect = PHWS_AUTO;\r
697746df 675 defaultConfig.s_PicoCDBuffers = 0;\r
37631374 676 defaultConfig.s_PicoSndFilterAlpha = 0x10000 * 60 / 100;\r
fcdefcf6 677 defaultConfig.confirm_save = EOPT_CONFIRM_SAVE;\r
697746df 678 defaultConfig.Frameskip = -1; // auto\r
531a8f38 679 defaultConfig.input_dev0 = PICO_INPUT_PAD_3BTN;\r
680 defaultConfig.input_dev1 = PICO_INPUT_PAD_3BTN;\r
697746df 681 defaultConfig.volume = 50;\r
682 defaultConfig.gamma = 100;\r
683 defaultConfig.scaling = 0;\r
684 defaultConfig.turbo_rate = 15;\r
ed4402a7 685 defaultConfig.msh2_khz = PICO_MSH2_HZ / 1000;\r
686 defaultConfig.ssh2_khz = PICO_SSH2_HZ / 1000;\r
e7ee7bc0 687 defaultConfig.max_skip = 4;\r
697746df 688\r
689 // platform specific overrides\r
690 pemu_prep_defconfig();\r
691}\r
692\r
d90f5bd7 693void emu_set_defconfig(void)\r
ca482e5d 694{\r
a47dd663 695 memcpy(&currentConfig, &defaultConfig, sizeof(currentConfig));\r
93f9619e 696 PicoIn.opt = currentConfig.s_PicoOpt;\r
6311a3ba 697 PicoIn.sndRate = currentConfig.s_PsndRate;\r
93f9619e 698 PicoIn.regionOverride = currentConfig.s_PicoRegion;\r
699 PicoIn.autoRgnOrder = currentConfig.s_PicoAutoRgnOrder;\r
280bfc3c 700 PicoIn.hwSelect = currentConfig.s_hwSelect;\r
37631374 701 PicoIn.sndFilterAlpha = currentConfig.s_PicoSndFilterAlpha;\r
ca482e5d 702}\r
703\r
fcdefcf6 704int emu_read_config(const char *rom_fname, int no_defaults)\r
ea8c405f 705{\r
58c86d00 706 char cfg[512];\r
58c86d00 707 int ret;\r
ea8c405f 708\r
490eb480 709 if (!no_defaults)\r
710 emu_set_defconfig();\r
711\r
fcdefcf6 712 if (rom_fname == NULL)\r
ea8c405f 713 {\r
fcdefcf6 714 // global config\r
08fe8094 715 make_config_cfg(cfg);\r
58c86d00 716 ret = config_readsect(cfg, NULL);\r
ea8c405f 717 }\r
58c86d00 718 else\r
719 {\r
504e2f56 720 char ext[16];\r
721 int vol;\r
ea8c405f 722\r
58c86d00 723 if (config_slot != 0)\r
504e2f56 724 snprintf(ext, sizeof(ext), ".%i.cfg", config_slot);\r
a39b7867 725 else\r
504e2f56 726 strcpy(ext, ".cfg");\r
727\r
728 fname_ext(cfg, sizeof(cfg), "cfg"PATH_SEP, ext, rom_fname);\r
729\r
730 // read user's config\r
731 vol = currentConfig.volume;\r
732 ret = config_readsect(cfg, NULL);\r
733 currentConfig.volume = vol; // make vol global (bah)\r
734\r
735 if (ret != 0)\r
08fe8094 736 {\r
737 // read global config, and apply game_def.cfg on top\r
738 make_config_cfg(cfg);\r
739 config_readsect(cfg, NULL);\r
58c86d00 740\r
504e2f56 741 emu_make_path(cfg, "game_def.cfg", sizeof(cfg));\r
742 ret = config_readsect(cfg, emu_make_rom_id(rom_fname));\r
58c86d00 743 }\r
ea8c405f 744 }\r
58c86d00 745\r
697746df 746 pemu_validate_config();\r
35f2b65e 747 PicoIn.overclockM68k = currentConfig.overclock_68k;\r
ee2a3bdf 748\r
ea8c405f 749 // some sanity checks\r
ee2a3bdf 750 if (currentConfig.volume < 0 || currentConfig.volume > 99)\r
751 currentConfig.volume = 50;\r
752\r
753 if (ret == 0)\r
754 config_slot_current = config_slot;\r
991473ad 755\r
58c86d00 756 return (ret == 0);\r
ea8c405f 757}\r
758\r
759\r
a47dd663 760int emu_write_config(int is_game)\r
ea8c405f 761{\r
504e2f56 762 char cfg[512];\r
58c86d00 763 int ret, write_lrom = 0;\r
ea8c405f 764\r
58c86d00 765 if (!is_game)\r
ea8c405f 766 {\r
ca482e5d 767 make_config_cfg(cfg);\r
58c86d00 768 write_lrom = 1;\r
ea8c405f 769 } else {\r
504e2f56 770 char ext[16];\r
771\r
ea8c405f 772 if (config_slot != 0)\r
504e2f56 773 snprintf(ext, sizeof(ext), ".%i.cfg", config_slot);\r
774 else\r
775 strcpy(ext, ".cfg");\r
776\r
777 romfname_ext(cfg, sizeof(cfg), "cfg"PATH_SEP, ext);\r
ea8c405f 778 }\r
779\r
a47dd663 780 lprintf("emu_write_config: %s ", cfg);\r
504e2f56 781 ret = config_write(cfg);\r
1ca2ea4f 782 if (write_lrom) config_writelrom(cfg);\r
9c9cda8c 783#ifdef __GP2X__\r
58c86d00 784 sync();\r
ea8c405f 785#endif\r
58c86d00 786 lprintf((ret == 0) ? "(ok)\n" : "(failed)\n");\r
ea8c405f 787\r
58c86d00 788 if (ret == 0) config_slot_current = config_slot;\r
789 return ret == 0;\r
ea8c405f 790}\r
791\r
792\r
e2de9939 793/* always using built-in font */\r
794\r
cc41eb4f 795#define mk_text_out(name, type, val, topleft, step_x, step_y) \\r
e2de9939 796void name(int x, int y, const char *text) \\r
797{ \\r
798 int i, l, len = strlen(text); \\r
cc41eb4f 799 type *screen = (type *)(topleft) + x * step_x + y * step_y; \\r
e2de9939 800 \\r
cc41eb4f 801 for (i = 0; i < len; i++, screen += 8 * step_x) \\r
e2de9939 802 { \\r
803 for (l = 0; l < 8; l++) \\r
804 { \\r
805 unsigned char fd = fontdata8x8[text[i] * 8 + l];\\r
cc41eb4f 806 type *s = screen + l * step_y; \\r
807 if (fd&0x80) s[step_x * 0] = val; \\r
808 if (fd&0x40) s[step_x * 1] = val; \\r
809 if (fd&0x20) s[step_x * 2] = val; \\r
810 if (fd&0x10) s[step_x * 3] = val; \\r
811 if (fd&0x08) s[step_x * 4] = val; \\r
812 if (fd&0x04) s[step_x * 5] = val; \\r
813 if (fd&0x02) s[step_x * 6] = val; \\r
814 if (fd&0x01) s[step_x * 7] = val; \\r
e2de9939 815 } \\r
816 } \\r
ea8c405f 817}\r
818\r
9cdfc191 819mk_text_out(emu_text_out8, unsigned char, 0xf0, g_screen_ptr, 1, g_screen_ppitch)\r
820mk_text_out(emu_text_out16, unsigned short, 0xffff, g_screen_ptr, 1, g_screen_ppitch)\r
cc41eb4f 821mk_text_out(emu_text_out8_rot, unsigned char, 0xf0,\r
9cdfc191 822 (char *)g_screen_ptr + (g_screen_ppitch - 1) * g_screen_height, -g_screen_height, 1)\r
cc41eb4f 823mk_text_out(emu_text_out16_rot, unsigned short, 0xffff,\r
9cdfc191 824 (short *)g_screen_ptr + (g_screen_ppitch - 1) * g_screen_height, -g_screen_height, 1)\r
ea8c405f 825\r
e2de9939 826#undef mk_text_out\r
ea8c405f 827\r
f7e40c9b 828void emu_osd_text16(int x, int y, const char *text)\r
829{\r
830 int len = strlen(text) * 8;\r
831 int i, h;\r
832\r
833 len++;\r
834 if (x + len > g_screen_width)\r
835 len = g_screen_width - x;\r
836\r
837 for (h = 0; h < 8; h++) {\r
838 unsigned short *p;\r
839 p = (unsigned short *)g_screen_ptr\r
9cdfc191 840 + x + g_screen_ppitch * (y + h);\r
f7e40c9b 841 for (i = len; i > 0; i--, p++)\r
842 *p = (*p >> 2) & 0x39e7;\r
843 }\r
844 emu_text_out16(x, y, text);\r
845}\r
ea8c405f 846\r
f7e40c9b 847static void update_movie(void)\r
ea8c405f 848{\r
849 int offs = Pico.m.frame_count*3 + 0x40;\r
850 if (offs+3 > movie_size) {\r
851 free(movie_data);\r
852 movie_data = 0;\r
b24e0f6c 853 emu_status_msg("END OF MOVIE.");\r
ea8c405f 854 lprintf("END OF MOVIE.\n");\r
ea8c405f 855 } else {\r
856 // MXYZ SACB RLDU\r
93f9619e 857 PicoIn.pad[0] = ~movie_data[offs] & 0x8f; // ! SCBA RLDU\r
858 if(!(movie_data[offs] & 0x10)) PicoIn.pad[0] |= 0x40; // C\r
859 if(!(movie_data[offs] & 0x20)) PicoIn.pad[0] |= 0x10; // A\r
860 if(!(movie_data[offs] & 0x40)) PicoIn.pad[0] |= 0x20; // B\r
861 PicoIn.pad[1] = ~movie_data[offs+1] & 0x8f; // ! SCBA RLDU\r
862 if(!(movie_data[offs+1] & 0x10)) PicoIn.pad[1] |= 0x40; // C\r
863 if(!(movie_data[offs+1] & 0x20)) PicoIn.pad[1] |= 0x10; // A\r
864 if(!(movie_data[offs+1] & 0x40)) PicoIn.pad[1] |= 0x20; // B\r
865 PicoIn.pad[0] |= (~movie_data[offs+2] & 0x0A) << 8; // ! MZYX\r
866 if(!(movie_data[offs+2] & 0x01)) PicoIn.pad[0] |= 0x0400; // X\r
867 if(!(movie_data[offs+2] & 0x04)) PicoIn.pad[0] |= 0x0100; // Z\r
868 PicoIn.pad[1] |= (~movie_data[offs+2] & 0xA0) << 4; // ! MZYX\r
869 if(!(movie_data[offs+2] & 0x10)) PicoIn.pad[1] |= 0x0400; // X\r
870 if(!(movie_data[offs+2] & 0x40)) PicoIn.pad[1] |= 0x0100; // Z\r
ea8c405f 871 }\r
872}\r
873\r
c7074ddb 874static int try_ropen_file(const char *fname, int *time)\r
ea8c405f 875{\r
c7074ddb 876 struct stat st;\r
ea8c405f 877 FILE *f;\r
878\r
879 f = fopen(fname, "rb");\r
880 if (f) {\r
c7074ddb 881 if (time != NULL) {\r
882 *time = 0;\r
883 if (fstat(fileno(f), &st) == 0)\r
884 *time = (int)st.st_mtime;\r
885 }\r
ea8c405f 886 fclose(f);\r
887 return 1;\r
888 }\r
889 return 0;\r
890}\r
891\r
c7074ddb 892char *emu_get_save_fname(int load, int is_sram, int slot, int *time)\r
ea8c405f 893{\r
ae87bffa 894 char *saveFname = static_buff;\r
ea8c405f 895 char ext[16];\r
896\r
897 if (is_sram)\r
898 {\r
b9312048 899 strcpy(ext, (PicoIn.AHW & PAHW_MCD) && Pico.romsize == 0 ? ".brm" : ".srm");\r
fcdefcf6 900 romfname_ext(saveFname, sizeof(static_buff),\r
b9312048 901 (PicoIn.AHW & PAHW_MCD) && Pico.romsize == 0 ? "brm"PATH_SEP : "srm"PATH_SEP, ext);\r
6bc00695 902 if (!load)\r
903 return saveFname;\r
904\r
c7074ddb 905 if (try_ropen_file(saveFname, time))\r
6bc00695 906 return saveFname;\r
907\r
fcdefcf6 908 romfname_ext(saveFname, sizeof(static_buff), NULL, ext);\r
c7074ddb 909 if (try_ropen_file(saveFname, time))\r
6bc00695 910 return saveFname;\r
ea8c405f 911 }\r
912 else\r
913 {\r
6bc00695 914 const char *ext_main = (currentConfig.EmuOpt & EOPT_GZIP_SAVES) ? ".mds.gz" : ".mds";\r
915 const char *ext_othr = (currentConfig.EmuOpt & EOPT_GZIP_SAVES) ? ".mds" : ".mds.gz";\r
ea8c405f 916 ext[0] = 0;\r
6bc00695 917 if (slot > 0 && slot < 10)\r
918 sprintf(ext, ".%i", slot);\r
919 strcat(ext, ext_main);\r
920\r
921 if (!load) {\r
fcdefcf6 922 romfname_ext(saveFname, sizeof(static_buff), "mds" PATH_SEP, ext);\r
6bc00695 923 return saveFname;\r
924 }\r
925 else {\r
fcdefcf6 926 romfname_ext(saveFname, sizeof(static_buff), "mds" PATH_SEP, ext);\r
c7074ddb 927 if (try_ropen_file(saveFname, time))\r
6bc00695 928 return saveFname;\r
ea8c405f 929\r
fcdefcf6 930 romfname_ext(saveFname, sizeof(static_buff), NULL, ext);\r
c7074ddb 931 if (try_ropen_file(saveFname, time))\r
6bc00695 932 return saveFname;\r
933\r
934 // try the other ext\r
935 ext[0] = 0;\r
936 if (slot > 0 && slot < 10)\r
937 sprintf(ext, ".%i", slot);\r
938 strcat(ext, ext_othr);\r
939\r
fcdefcf6 940 romfname_ext(saveFname, sizeof(static_buff), "mds"PATH_SEP, ext);\r
c7074ddb 941 if (try_ropen_file(saveFname, time))\r
6bc00695 942 return saveFname;\r
ea8c405f 943 }\r
944 }\r
945\r
6bc00695 946 return NULL;\r
ea8c405f 947}\r
948\r
cca8800d 949int emu_check_save_file(int slot, int *time)\r
ea8c405f 950{\r
c7074ddb 951 return emu_get_save_fname(1, 0, slot, time) ? 1 : 0;\r
ea8c405f 952}\r
953\r
a47dd663 954int emu_save_load_game(int load, int sram)\r
ea8c405f 955{\r
956 int ret = 0;\r
957 char *saveFname;\r
958\r
959 // make save filename\r
c7074ddb 960 saveFname = emu_get_save_fname(load, sram, state_slot, NULL);\r
ea8c405f 961 if (saveFname == NULL) {\r
d34a42f9 962 if (!sram)\r
b24e0f6c 963 emu_status_msg(load ? "LOAD FAILED (missing file)" : "SAVE FAILED");\r
ea8c405f 964 return -1;\r
965 }\r
966\r
967 lprintf("saveLoad (%i, %i): %s\n", load, sram, saveFname);\r
968\r
da42200b 969 if (sram)\r
970 {\r
ea8c405f 971 FILE *sramFile;\r
972 int sram_size;\r
973 unsigned char *sram_data;\r
974 int truncate = 1;\r
b9312048 975 if ((PicoIn.AHW & PAHW_MCD) && Pico.romsize == 0)\r
602133e1 976 {\r
93f9619e 977 if (PicoIn.opt & POPT_EN_MCD_RAMCART) {\r
ea8c405f 978 sram_size = 0x12000;\r
88fd63ad 979 sram_data = Pico.sv.data;\r
ea8c405f 980 if (sram_data)\r
fdcfd323 981 memcpy(sram_data, Pico_mcd->bram, 0x2000);\r
ea8c405f 982 } else {\r
983 sram_size = 0x2000;\r
984 sram_data = Pico_mcd->bram;\r
985 truncate = 0; // the .brm may contain RAM cart data after normal brm\r
986 }\r
987 } else {\r
88fd63ad 988 sram_size = Pico.sv.size;\r
989 sram_data = Pico.sv.data;\r
ea8c405f 990 }\r
b8a1c09a 991 if (sram_data == NULL)\r
88fd63ad 992 return 0; // cart saves forcefully disabled for this game\r
ea8c405f 993\r
602133e1 994 if (load)\r
995 {\r
ea8c405f 996 sramFile = fopen(saveFname, "rb");\r
b8a1c09a 997 if (!sramFile)\r
998 return -1;\r
999 ret = fread(sram_data, 1, sram_size, sramFile);\r
1000 ret = ret > 0 ? 0 : -1;\r
ea8c405f 1001 fclose(sramFile);\r
b9312048 1002 if ((PicoIn.AHW & PAHW_MCD) && Pico.romsize == 0 && (PicoIn.opt&POPT_EN_MCD_RAMCART))\r
fdcfd323 1003 memcpy(Pico_mcd->bram, sram_data, 0x2000);\r
ea8c405f 1004 } else {\r
1005 // sram save needs some special processing\r
1006 // see if we have anything to save\r
1007 for (; sram_size > 0; sram_size--)\r
1008 if (sram_data[sram_size-1]) break;\r
1009\r
1010 if (sram_size) {\r
1011 sramFile = fopen(saveFname, truncate ? "wb" : "r+b");\r
1012 if (!sramFile) sramFile = fopen(saveFname, "wb"); // retry\r
1013 if (!sramFile) return -1;\r
1014 ret = fwrite(sram_data, 1, sram_size, sramFile);\r
1015 ret = (ret != sram_size) ? -1 : 0;\r
1016 fclose(sramFile);\r
9c9cda8c 1017#ifdef __GP2X__\r
ea8c405f 1018 sync();\r
1019#endif\r
1020 }\r
1021 }\r
1022 return ret;\r
1023 }\r
1024 else\r
1025 {\r
bcc9eda0 1026 ret = PicoState(saveFname, !load);\r
1027 if (!ret) {\r
9c9cda8c 1028#ifdef __GP2X__\r
bcc9eda0 1029 if (!load) sync();\r
ea8c405f 1030#endif\r
54646a39 1031 emu_status_msg(load ? "STATE LOADED" : "STATE SAVED");\r
bcc9eda0 1032 } else {\r
b24e0f6c 1033 emu_status_msg(load ? "LOAD FAILED" : "SAVE FAILED");\r
ea8c405f 1034 ret = -1;\r
1035 }\r
1036\r
ea8c405f 1037 return ret;\r
1038 }\r
1039}\r
bdec53c9 1040\r
a47dd663 1041void emu_set_fastforward(int set_on)\r
c060a9ab 1042{\r
1043 static void *set_PsndOut = NULL;\r
1044 static int set_Frameskip, set_EmuOpt, is_on = 0;\r
1045\r
1046 if (set_on && !is_on) {\r
6311a3ba 1047 set_PsndOut = PicoIn.sndOut;\r
c060a9ab 1048 set_Frameskip = currentConfig.Frameskip;\r
1049 set_EmuOpt = currentConfig.EmuOpt;\r
6311a3ba 1050 PicoIn.sndOut = NULL;\r
c060a9ab 1051 currentConfig.Frameskip = 8;\r
9f1d5acd 1052 currentConfig.EmuOpt &= ~EOPT_EN_SOUND;\r
37631374 1053 currentConfig.EmuOpt |= EOPT_NO_FRMLIMIT;\r
c060a9ab 1054 is_on = 1;\r
b24e0f6c 1055 emu_status_msg("FAST FORWARD");\r
c060a9ab 1056 }\r
1057 else if (!set_on && is_on) {\r
6311a3ba 1058 PicoIn.sndOut = set_PsndOut;\r
c060a9ab 1059 currentConfig.Frameskip = set_Frameskip;\r
1060 currentConfig.EmuOpt = set_EmuOpt;\r
1061 PsndRerate(1);\r
1062 is_on = 0;\r
1063 }\r
1064}\r
1065\r
d687ef50 1066static void emu_tray_open(void)\r
c060a9ab 1067{\r
d687ef50 1068 engineState = PGS_TrayMenu;\r
1069}\r
1070\r
1071static void emu_tray_close(void)\r
1072{\r
1073 emu_status_msg("CD tray closed.");\r
f2cf8472 1074}\r
1075\r
974fdb5b 1076void emu_32x_startup(void)\r
1077{\r
c7eb229a 1078 plat_video_toggle_renderer(0, 0); // HACK\r
974fdb5b 1079 system_announce();\r
1080}\r
1081\r
f2cf8472 1082void emu_reset_game(void)\r
1083{\r
1084 PicoReset();\r
1085 reset_timing = 1;\r
1086}\r
1087\r
15cc45c0 1088static u16 *load_pico_overlay(int page, int w, int h)\r
f2cf8472 1089{\r
15cc45c0 1090 static const char *pic_exts[] = { "png", "PNG" };\r
1091 char *ext, *fname = NULL;\r
1092 int extpos, i;\r
1093\r
1094 if (pico_page == page && pico_w == w && pico_h == h)\r
1095 return pico_overlay;\r
1096 pico_page = page;\r
1097 pico_w = w, pico_h = h;\r
1098\r
1099 ext = strrchr(rom_fname_loaded, '.');\r
1100 extpos = ext ? ext-rom_fname_loaded : strlen(rom_fname_loaded);\r
1101 strcpy(static_buff, rom_fname_loaded);\r
1102 static_buff[extpos++] = '_';\r
1103 if (page < 0) {\r
1104 static_buff[extpos++] = 'p';\r
1105 static_buff[extpos++] = 'a';\r
1106 static_buff[extpos++] = 'd';\r
1107 } else\r
1108 static_buff[extpos++] = '0'+PicoPicohw.page;\r
1109 static_buff[extpos++] = '.';\r
1110\r
1111 for (i = 0; i < ARRAY_SIZE(pic_exts); i++) {\r
1112 strcpy(static_buff+extpos, pic_exts[i]);\r
1113 if (access(static_buff, R_OK) == 0) {\r
1114 printf("found Pico file: %s\n", static_buff);\r
1115 fname = static_buff;\r
1116 break;\r
c060a9ab 1117 }\r
c060a9ab 1118 }\r
15cc45c0 1119\r
1120 pico_overlay = realloc(pico_overlay, w*h*2);\r
1121 memset(pico_overlay, 0, w*h*2);\r
1122 if (!fname || !pico_overlay || readpng(pico_overlay, fname, READPNG_SCALE, w, h)) {\r
1123 if (pico_overlay)\r
1124 free(pico_overlay);\r
1125 pico_overlay = NULL;\r
1126 }\r
1127\r
1128 return pico_overlay;\r
1129}\r
1130\r
1131void emu_pico_overlay(u16 *pd, int w, int h, int pitch)\r
1132{\r
1133 u16 *overlay = NULL;\r
1134 int y, oh = h;\r
1135\r
1136 // get overlay\r
1137 if (pico_inp_mode == 1) {\r
1138 oh = (w/2 < h ? w/2 : h); // storyware has squished h\r
1139 overlay = load_pico_overlay(PicoPicohw.page, w, oh);\r
1140 } else if (pico_inp_mode == 2)\r
1141 overlay = load_pico_overlay(-1, w, oh);\r
1142\r
1143 // copy overlay onto buffer\r
1144 if (overlay) {\r
1145 for (y = 0; y < oh; y++)\r
1146 memcpy(pd + y*pitch, overlay + y*w, w*2);\r
1147 if (y < h)\r
1148 memset(pd + y*pitch, 0, w*2);\r
1149 }\r
1150}\r
1151\r
1152void run_events_pico(unsigned int events)\r
1153{\r
037f58e1 1154 // treat pad ports equal to support pad in one and mouse in the other\r
1155 PicoIn.pad[0] |= PicoIn.pad[1];\r
1156\r
f2cf8472 1157 if (events & PEV_PICO_PPREV) {\r
c060a9ab 1158 PicoPicohw.page--;\r
d34a42f9 1159 if (PicoPicohw.page < 0)\r
1160 PicoPicohw.page = 0;\r
b24e0f6c 1161 emu_status_msg("Page %i", PicoPicohw.page);\r
c060a9ab 1162 }\r
f2cf8472 1163 if (events & PEV_PICO_PNEXT) {\r
c060a9ab 1164 PicoPicohw.page++;\r
fa968c5e 1165 if (PicoPicohw.page > 7)\r
1166 PicoPicohw.page = 7;\r
576c1b8a 1167 if (PicoPicohw.page == 7) {\r
1168 // Used in games that require the Keyboard Pico peripheral\r
1169 emu_status_msg("Test Page");\r
1170 } else {\r
1171 emu_status_msg("Page %i", PicoPicohw.page);\r
1172 }\r
c060a9ab 1173 }\r
15cc45c0 1174 if (events & PEV_PICO_STORY) {\r
1175 if (pico_inp_mode == 1) {\r
1176 pico_inp_mode = 0;\r
1177 emu_status_msg("Input: D-Pad");\r
1178 } else {\r
1179 pico_inp_mode = 1;\r
1180 emu_status_msg("Input: Pen on Storyware");\r
1181 }\r
1182 }\r
1183 if (events & PEV_PICO_PAD) {\r
1184 if (pico_inp_mode == 2) {\r
1185 pico_inp_mode = 0;\r
1186 emu_status_msg("Input: D-Pad");\r
1187 } else {\r
1188 pico_inp_mode = 2;\r
1189 emu_status_msg("Input: Pen on Pad");\r
1190 }\r
dca20eff 1191 }\r
f2cf8472 1192\r
576c1b8a 1193 if ((currentConfig.EmuOpt & EOPT_PICO_PEN) &&\r
1194 (PicoIn.pad[0]&0x20) && pico_inp_mode && pico_overlay) {\r
15cc45c0 1195 pico_inp_mode = 0;\r
1196 emu_status_msg("Input: D-Pad");\r
1197 }\r
0e732d77 1198\r
bac44b18 1199 PicoPicohw.kb.active = (PicoIn.opt & POPT_EN_KBD ? kbd_mode : 0);\r
0e732d77 1200\r
576c1b8a 1201 if (pico_inp_mode == 0)\r
f2cf8472 1202 return;\r
1203\r
037f58e1 1204 /* handle other input modes using the pen */\r
44be7e5c 1205 if (PicoIn.opt & POPT_EN_MOUSE) {\r
037f58e1 1206 pico_pen_x = PicoIn.mouse[0];\r
1207 pico_pen_y = PicoIn.mouse[1];\r
037f58e1 1208 } else {\r
1209 if (PicoIn.pad[0] & 1) pico_pen_y--;\r
1210 if (PicoIn.pad[0] & 2) pico_pen_y++;\r
1211 if (PicoIn.pad[0] & 4) pico_pen_x--;\r
1212 if (PicoIn.pad[0] & 8) pico_pen_x++;\r
1213 PicoIn.pad[0] &= ~0x0f; // release UDLR\r
1214 }\r
f2cf8472 1215\r
44be7e5c 1216 if ((pico_pad ^ PicoIn.pad[0]) & PicoIn.pad[0] & (1<<GBTN_START)) {\r
1217 PicoPicohw.pen_pos[0] ^= 0x8000;\r
1218 PicoPicohw.pen_pos[1] ^= 0x8000;\r
1219 emu_status_msg("Pen %s", PicoPicohw.pen_pos[0] & 0x8000 ? "Up" : "Down");\r
1220 }\r
1221 pico_pad = PicoIn.pad[0];\r
1222\r
15cc45c0 1223 /* cursor position, cursor drawing must not cross screen borders */\r
dca20eff 1224 if (pico_pen_y < PICO_PEN_ADJUST_Y)\r
1225 pico_pen_y = PICO_PEN_ADJUST_Y;\r
15cc45c0 1226 if (pico_pen_y > 223-1 - PICO_PEN_ADJUST_Y)\r
1227 pico_pen_y = 223-1 - PICO_PEN_ADJUST_Y;\r
dca20eff 1228 if (pico_pen_x < PICO_PEN_ADJUST_X)\r
1229 pico_pen_x = PICO_PEN_ADJUST_X;\r
15cc45c0 1230 if (pico_pen_x > 319-1 - PICO_PEN_ADJUST_X)\r
1231 pico_pen_x = 319-1 - PICO_PEN_ADJUST_X;\r
dca20eff 1232\r
c87e36d7 1233 PicoPicohw.pen_pos[0] &= 0x8000;\r
1234 PicoPicohw.pen_pos[1] &= 0x8000;\r
1235 PicoPicohw.pen_pos[0] |= 0x03c + pico_pen_x;\r
1236 PicoPicohw.pen_pos[1] |= (pico_inp_mode == 1 ? 0x2f8 : 0x1fc) + pico_pen_y;\r
c060a9ab 1237}\r
1238\r
93f9619e 1239static void do_turbo(unsigned short *pad, int acts)\r
f0f0d2df 1240{\r
1241 static int turbo_pad = 0;\r
1242 static unsigned char turbo_cnt[3] = { 0, 0, 0 };\r
1243 int inc = currentConfig.turbo_rate * 2;\r
1244\r
1245 if (acts & 0x1000) {\r
1246 turbo_cnt[0] += inc;\r
1247 if (turbo_cnt[0] >= 60)\r
1248 turbo_pad ^= 0x10, turbo_cnt[0] = 0;\r
1249 }\r
1250 if (acts & 0x2000) {\r
1251 turbo_cnt[1] += inc;\r
1252 if (turbo_cnt[1] >= 60)\r
1253 turbo_pad ^= 0x20, turbo_cnt[1] = 0;\r
1254 }\r
1255 if (acts & 0x4000) {\r
1256 turbo_cnt[2] += inc;\r
1257 if (turbo_cnt[2] >= 60)\r
1258 turbo_pad ^= 0x40, turbo_cnt[2] = 0;\r
1259 }\r
1260 *pad |= turbo_pad & (acts >> 8);\r
1261}\r
c060a9ab 1262\r
f2cf8472 1263static void run_events_ui(unsigned int which)\r
d34a42f9 1264{\r
1265 if (which & (PEV_STATE_LOAD|PEV_STATE_SAVE))\r
1266 {\r
1267 int do_it = 1;\r
cca8800d 1268 if ( emu_check_save_file(state_slot, NULL) &&\r
fcdefcf6 1269 (((which & PEV_STATE_LOAD) && (currentConfig.confirm_save & EOPT_CONFIRM_LOAD)) ||\r
1270 ((which & PEV_STATE_SAVE) && (currentConfig.confirm_save & EOPT_CONFIRM_SAVE))) )\r
d34a42f9 1271 {\r
1272 const char *nm;\r
1273 char tmp[64];\r
1274 int keys, len;\r
1275\r
868cc0cc 1276 strcpy(tmp, (which & PEV_STATE_LOAD) ? "LOAD STATE? " : "OVERWRITE SAVE? ");\r
d34a42f9 1277 len = strlen(tmp);\r
868cc0cc 1278 nm = in_get_key_name(-1, -PBTN_MOK);\r
d34a42f9 1279 snprintf(tmp + len, sizeof(tmp) - len, "(%s=yes, ", nm);\r
1280 len = strlen(tmp);\r
1281 nm = in_get_key_name(-1, -PBTN_MBACK);\r
1282 snprintf(tmp + len, sizeof(tmp) - len, "%s=no)", nm);\r
1283\r
1284 plat_status_msg_busy_first(tmp);\r
1285\r
45285368 1286 in_set_config_int(0, IN_CFG_BLOCKING, 1);\r
868cc0cc 1287 while (in_menu_wait_any(NULL, 50) & (PBTN_MOK | PBTN_MBACK))\r
d34a42f9 1288 ;\r
868cc0cc 1289 while ( !((keys = in_menu_wait_any(NULL, 50)) & (PBTN_MOK | PBTN_MBACK)))\r
d34a42f9 1290 ;\r
1291 if (keys & PBTN_MBACK)\r
1292 do_it = 0;\r
868cc0cc 1293 while (in_menu_wait_any(NULL, 50) & (PBTN_MOK | PBTN_MBACK))\r
d34a42f9 1294 ;\r
45285368 1295 in_set_config_int(0, IN_CFG_BLOCKING, 0);\r
758abbeb 1296 plat_status_msg_clear();\r
d34a42f9 1297 }\r
1298 if (do_it) {\r
54646a39 1299 plat_status_msg_busy_first((which & PEV_STATE_LOAD) ? "LOADING STATE" : "SAVING STATE");\r
d34a42f9 1300 PicoStateProgressCB = plat_status_msg_busy_next;\r
a47dd663 1301 emu_save_load_game((which & PEV_STATE_LOAD) ? 1 : 0, 0);\r
d34a42f9 1302 PicoStateProgressCB = NULL;\r
1303 }\r
95b08943 1304 plat_status_msg_busy_done();\r
d34a42f9 1305 }\r
d34a42f9 1306 if (which & (PEV_SSLOT_PREV|PEV_SSLOT_NEXT))\r
1307 {\r
1308 if (which & PEV_SSLOT_PREV) {\r
1309 state_slot -= 1;\r
1310 if (state_slot < 0)\r
1311 state_slot = 9;\r
1312 } else {\r
1313 state_slot += 1;\r
1314 if (state_slot > 9)\r
1315 state_slot = 0;\r
1316 }\r
1317\r
b24e0f6c 1318 emu_status_msg("SAVE SLOT %i [%s]", state_slot,\r
cca8800d 1319 emu_check_save_file(state_slot, NULL) ? "USED" : "FREE");\r
d34a42f9 1320 }\r
576c1b8a 1321 if (which & PEV_SWITCH_RND)\r
1322 {\r
1323 plat_video_toggle_renderer(1, 0);\r
1324 }\r
44be7e5c 1325 if (which & PEV_GRAB_INPUT)\r
1326 {\r
1327 if (PicoIn.opt & POPT_EN_MOUSE) {\r
1328 grab_mode = !grab_mode;\r
1329 in_update_analog(0, 2, &mouse_x);\r
1330 in_update_analog(0, 3, &mouse_y);\r
1331 in_update_analog(0, 0, &mouse_x);\r
1332 in_update_analog(0, 1, &mouse_y);\r
1333 emu_status_msg("Mouse capture %s", grab_mode ? "on" : "off");\r
1334 } else {\r
1335 grab_mode = 0;\r
1336 emu_status_msg("No mouse configured");\r
1337 }\r
1338\r
1777b86d 1339 plat_grab_cursor(grab_mode);\r
44be7e5c 1340 }\r
576c1b8a 1341 if (which & PEV_SWITCH_KBD)\r
1342 {\r
bac44b18 1343 if (! (PicoIn.opt & POPT_EN_KBD)) {\r
1344 kbd_mode = 0;\r
92064e2f 1345 emu_status_msg("No keyboard configured");\r
bac44b18 1346 } else {\r
1347 kbd_mode = !kbd_mode;\r
1348 emu_status_msg("Keyboard %s", kbd_mode ? "on" : "off");\r
1349 }\r
1350 if (! kbd_mode)\r
1351 plat_video_clear_buffers();\r
576c1b8a 1352 }\r
9db6a544 1353 if (which & PEV_RESET)\r
1354 emu_reset_game();\r
d34a42f9 1355 if (which & PEV_MENU)\r
1356 engineState = PGS_Menu;\r
1357}\r
1358\r
1359void emu_update_input(void)\r
1360{\r
093b8a42 1361 static int prev_events = 0;\r
1362 int actions[IN_BINDTYPE_COUNT] = { 0, };\r
20c9a3ba 1363 int actions_kbd[IN_BIND_LAST] = { 0, };\r
1d5885dd 1364 int pl_actions[4];\r
037f58e1 1365 int count_kbd = 0, buttons = 0;\r
0738c83e 1366 int events, i = 0;\r
d34a42f9 1367\r
093b8a42 1368 in_update(actions);\r
d34a42f9 1369\r
093b8a42 1370 pl_actions[0] = actions[IN_BINDTYPE_PLAYER12];\r
1371 pl_actions[1] = actions[IN_BINDTYPE_PLAYER12] >> 16;\r
1d5885dd 1372 pl_actions[2] = actions[IN_BINDTYPE_PLAYER34];\r
1373 pl_actions[3] = actions[IN_BINDTYPE_PLAYER34] >> 16;\r
d34a42f9 1374\r
093b8a42 1375 events = actions[IN_BINDTYPE_EMU] & PEV_MASK;\r
d34a42f9 1376\r
0738c83e 1377 // update mouse coordinates if there is an emulated mouse\r
44be7e5c 1378 if (PicoIn.opt & POPT_EN_MOUSE) {\r
1379 if (!grab_mode) {\r
1380 in_update_analog(0, 0, &PicoIn.mouse[0]);\r
1381 in_update_analog(0, 1, &PicoIn.mouse[1]);\r
1382 // scale mouse coordinates from -1024..1024 to 0..screen_w/h\r
1383 PicoIn.mouse[0] = (PicoIn.mouse[0]+1024) * g_screen_width /2048;\r
1384 PicoIn.mouse[1] = (PicoIn.mouse[1]+1024) * g_screen_height/2048;\r
1385 } else {\r
1386 int xrel, yrel;\r
1387 in_update_analog(0, 2, &xrel);\r
1388 in_update_analog(0, 3, &yrel);\r
1389 mouse_x += xrel, mouse_y += yrel;\r
1390 // scale mouse coordinates from -1024..1024 to 0..screen_w/h\r
1391 PicoIn.mouse[0] = (mouse_x+1024) * g_screen_width /2048;\r
1392 PicoIn.mouse[1] = (mouse_y+1024) * g_screen_height/2048;\r
1393 }\r
037f58e1 1394\r
1395 in_update_analog(0, -1, &i); // get mouse buttons, bit 2-0 = RML\r
44be7e5c 1396 if (PicoIn.AHW & PAHW_PICO) {\r
1397 // TODO is maintaining 2 different mappings necessary?\r
1398 if (i & 1) buttons |= 1<<GBTN_C; // pen button\r
1399 if (i & 2) buttons |= 1<<GBTN_B; // red button\r
1400 if (i & 4) buttons |= 1<<GBTN_START; // pen up/down\r
1401 } else {\r
1402 if (i & 1) buttons |= 1<<GBTN_B; // as Sega Mouse\r
1403 if (i & 2) buttons |= 1<<GBTN_START;\r
1404 if (i & 4) buttons |= 1<<GBTN_C;\r
1405 }\r
1406\r
037f58e1 1407 if (currentConfig.input_dev0 == PICO_INPUT_MOUSE)\r
1408 pl_actions[0] |= buttons;\r
1409 if (currentConfig.input_dev1 == PICO_INPUT_MOUSE)\r
1410 pl_actions[1] |= buttons;\r
1411 }\r
0738c83e 1412\r
576c1b8a 1413 if (kbd_mode) {\r
1414 int mask = (PicoIn.AHW & PAHW_PICO ? 0xf : 0x0);\r
4814d19e 1415 if (currentConfig.keyboard == 2)\r
bac44b18 1416 count_kbd = in_update_kbd(actions_kbd);\r
4814d19e 1417 else if (currentConfig.keyboard == 1)\r
bac44b18 1418 count_kbd = vkbd_update(vkbd, pl_actions[0], actions_kbd);\r
576c1b8a 1419\r
0e732d77 1420 // FIXME: Only passthrough joystick input to avoid collisions\r
1421 // with PS/2 bindings. Ideally we should check if the device this\r
1422 // input originated from is the same as the device used for\r
1423 // PS/2 input, and passthrough if they are different devices.\r
576c1b8a 1424 PicoIn.pad[0] = pl_actions[0] & mask;\r
1425 PicoIn.pad[1] = pl_actions[1] & mask;\r
1426 PicoIn.pad[2] = pl_actions[2] & mask;\r
1427 PicoIn.pad[3] = pl_actions[3] & mask;\r
0e732d77 1428\r
1429 // Ignore events mapped to bindings that collide with PS/2 peripherals.\r
1430 // Note that calls to emu_set_fastforward() should be avoided as well,\r
1431 // since fast-forward activates even with parameter set_on = 0.\r
576c1b8a 1432 events &= PEV_SWITCH_KBD;\r
0e732d77 1433 } else {\r
1434 PicoIn.pad[0] = pl_actions[0] & 0xfff;\r
1435 PicoIn.pad[1] = pl_actions[1] & 0xfff;\r
1436 PicoIn.pad[2] = pl_actions[2] & 0xfff;\r
1437 PicoIn.pad[3] = pl_actions[3] & 0xfff;\r
1438\r
1439 if (pl_actions[0] & 0x7000)\r
1440 do_turbo(&PicoIn.pad[0], pl_actions[0]);\r
1441 if (pl_actions[1] & 0x7000)\r
1442 do_turbo(&PicoIn.pad[1], pl_actions[1]);\r
1443 if (pl_actions[2] & 0x7000)\r
1444 do_turbo(&PicoIn.pad[2], pl_actions[2]);\r
1445 if (pl_actions[3] & 0x7000)\r
1446 do_turbo(&PicoIn.pad[3], pl_actions[3]);\r
1447\r
1448 if ((events ^ prev_events) & PEV_FF) {\r
1449 emu_set_fastforward(events & PEV_FF);\r
1450 plat_update_volume(0, 0);\r
1451 reset_timing = 1;\r
1452 }\r
1453 }\r
1454\r
d34a42f9 1455 // volume is treated in special way and triggered every frame\r
1456 if (events & (PEV_VOL_DOWN|PEV_VOL_UP))\r
1457 plat_update_volume(1, events & PEV_VOL_UP);\r
1458\r
093b8a42 1459 events &= ~prev_events;\r
d34a42f9 1460\r
576c1b8a 1461 // update keyboard input, actions only updated if keyboard mode active\r
1462 PicoIn.kbd = 0;\r
bac44b18 1463 for (i = 0; i < count_kbd; i++) {\r
576c1b8a 1464 if (actions_kbd[i]) {\r
bac44b18 1465 unsigned int key = (actions_kbd[i] & 0xff);\r
576c1b8a 1466 if (key == PEVB_KBD_LSHIFT || key == PEVB_KBD_RSHIFT ||\r
1467 key == PEVB_KBD_CTRL || key == PEVB_KBD_FUNC) {\r
bac44b18 1468 PicoIn.kbd = (PicoIn.kbd & 0x00ff) | (key << 8);\r
576c1b8a 1469 } else {\r
bac44b18 1470 PicoIn.kbd = (PicoIn.kbd & 0xff00) | key;\r
576c1b8a 1471 }\r
1472 }\r
1473 }\r
1474\r
1475 if (PicoIn.AHW & PAHW_PICO)\r
f2cf8472 1476 run_events_pico(events);\r
d34a42f9 1477 if (events)\r
f2cf8472 1478 run_events_ui(events);\r
d34a42f9 1479 if (movie_data)\r
1480 update_movie();\r
1481\r
093b8a42 1482 prev_events = actions[IN_BINDTYPE_EMU] & PEV_MASK;\r
d34a42f9 1483}\r
1484\r
f2cf8472 1485static void mkdir_path(char *path_with_reserve, int pos, const char *name)\r
1486{\r
1487 strcpy(path_with_reserve + pos, name);\r
1488 if (plat_is_dir(path_with_reserve))\r
1489 return;\r
9b4c95a9 1490 if (mkdir(path_with_reserve, 0755) < 0)\r
f2cf8472 1491 lprintf("failed to create: %s\n", path_with_reserve);\r
1492}\r
1493\r
832faed3 1494void emu_cmn_forced_frame(int no_scale, int do_emu, void *buf)\r
a4edca53 1495{\r
93f9619e 1496 int po_old = PicoIn.opt;\r
9cdfc191 1497 int y;\r
a4edca53 1498\r
9cdfc191 1499 for (y = 0; y < g_screen_height; y++)\r
1500 memset32((short *)g_screen_ptr + g_screen_ppitch * y, 0,\r
1501 g_screen_width * 2 / 4);\r
a4edca53 1502\r
d5d17782 1503 PicoIn.opt &= ~(POPT_ALT_RENDERER|POPT_EN_SOFTSCALE);\r
93f9619e 1504 PicoIn.opt |= POPT_ACC_SPRITES;\r
13e22071 1505 if (!no_scale && currentConfig.scaling)\r
93f9619e 1506 PicoIn.opt |= POPT_EN_SOFTSCALE;\r
a4edca53 1507\r
1508 PicoDrawSetOutFormat(PDF_RGB555, 1);\r
832faed3 1509 PicoDrawSetOutBuf(buf, g_screen_ppitch * 2);\r
a4edca53 1510 Pico.m.dirtyPal = 1;\r
1511 if (do_emu)\r
1512 PicoFrame();\r
1513 else\r
1514 PicoFrameDrawOnly();\r
1515\r
93f9619e 1516 PicoIn.opt = po_old;\r
a4edca53 1517}\r
1518\r
f2cf8472 1519void emu_init(void)\r
1520{\r
27701801 1521 char path[512];\r
f2cf8472 1522 int pos;\r
1523\r
83ff19ec 1524#if 0\r
1525 // FIXME: handle through menu, etc\r
1526 FILE *f;\r
1527 f = fopen("32X_M_BIOS.BIN", "rb");\r
1528 p32x_bios_m = malloc(2048);\r
1529 fread(p32x_bios_m, 1, 2048, f);\r
1530 fclose(f);\r
1531 f = fopen("32X_S_BIOS.BIN", "rb");\r
1532 p32x_bios_s = malloc(1024);\r
1533 fread(p32x_bios_s, 1, 1024, f);\r
1534 fclose(f);\r
1535#endif\r
1536\r
f2cf8472 1537 /* make dirs for saves */\r
27701801 1538 pos = plat_get_root_dir(path, sizeof(path) - 4);\r
1539 mkdir_path(path, pos, "mds");\r
1540 mkdir_path(path, pos, "srm");\r
1541 mkdir_path(path, pos, "brm");\r
59f8f7d1 1542 mkdir_path(path, pos, "tape");\r
504e2f56 1543 mkdir_path(path, pos, "cfg");\r
27701801 1544\r
f6c49d38 1545 pprof_init();\r
1546\r
27701801 1547 make_config_cfg(path);\r
1548 config_readlrom(path);\r
f2cf8472 1549\r
1550 PicoInit();\r
6311a3ba 1551 PicoIn.osdMessage = plat_status_msg_busy_next;\r
1552 PicoIn.mcdTrayOpen = emu_tray_open;\r
1553 PicoIn.mcdTrayClose = emu_tray_close;\r
df92fbd1 1554\r
1555 sndout_init();\r
f2cf8472 1556}\r
1557\r
1558void emu_finish(void)\r
1559{\r
1560 // save SRAM\r
88fd63ad 1561 if ((currentConfig.EmuOpt & EOPT_EN_SRAM) && Pico.sv.changed) {\r
a47dd663 1562 emu_save_load_game(0, 1);\r
88fd63ad 1563 Pico.sv.changed = 0;\r
f2cf8472 1564 }\r
1565\r
27701801 1566 if (!(currentConfig.EmuOpt & EOPT_NO_AUTOSVCFG)) {\r
1567 char cfg[512];\r
1568 make_config_cfg(cfg);\r
1569 config_writelrom(cfg);\r
9c9cda8c 1570#ifdef __GP2X__\r
27701801 1571 sync();\r
1572#endif\r
1573 }\r
f2cf8472 1574\r
f6c49d38 1575 pprof_finish();\r
1576\r
f2cf8472 1577 PicoExit();\r
df92fbd1 1578 sndout_exit();\r
1579}\r
1580\r
1581static void snd_write_nonblocking(int len)\r
1582{\r
6311a3ba 1583 sndout_write_nb(PicoIn.sndOut, len);\r
df92fbd1 1584}\r
1585\r
1586void emu_sound_start(void)\r
1587{\r
6311a3ba 1588 PicoIn.sndOut = NULL;\r
df92fbd1 1589\r
882f697a 1590 // auto-select rate?\r
a990d9c4 1591 if (PicoIn.sndRate > 52000 && PicoIn.sndRate < 54000)\r
882f697a 1592 PicoIn.sndRate = YM2612_NATIVE_RATE();\r
df92fbd1 1593 if (currentConfig.EmuOpt & EOPT_EN_SOUND)\r
1594 {\r
93f9619e 1595 int is_stereo = (PicoIn.opt & POPT_EN_STEREO) ? 1 : 0;\r
df92fbd1 1596\r
e28fd20f 1597 memset(sndBuffer, 0, sizeof(sndBuffer));\r
1598 PicoIn.sndOut = sndBuffer;\r
df92fbd1 1599 PsndRerate(Pico.m.frame_count ? 1 : 0);\r
1600\r
1601 printf("starting audio: %i len: %i stereo: %i, pal: %i\n",\r
6311a3ba 1602 PicoIn.sndRate, Pico.snd.len, is_stereo, Pico.m.pal);\r
e28fd20f 1603\r
6311a3ba 1604 sndout_start(PicoIn.sndRate, is_stereo);\r
1605 PicoIn.writeSound = snd_write_nonblocking;\r
df92fbd1 1606 plat_update_volume(0, 0);\r
df92fbd1 1607 }\r
1608}\r
1609\r
1610void emu_sound_stop(void)\r
1611{\r
1612 sndout_stop();\r
1613}\r
1614\r
1615void emu_sound_wait(void)\r
1616{\r
1617 sndout_wait();\r
f2cf8472 1618}\r
1619\r
d4d62665 1620static void emu_loop_prep(void)\r
1621{\r
1622 static int pal_old = -1;\r
1623 static int filter_old = -1;\r
1624\r
1625 if (currentConfig.CPUclock != plat_target_cpu_clock_get())\r
1626 plat_target_cpu_clock_set(currentConfig.CPUclock);\r
1627\r
1628 if (Pico.m.pal != pal_old) {\r
1629 plat_target_lcdrate_set(Pico.m.pal);\r
1630 pal_old = Pico.m.pal;\r
1631 }\r
1632\r
1633 if (currentConfig.filter != filter_old) {\r
1634 plat_target_hwfilter_set(currentConfig.filter);\r
1635 filter_old = currentConfig.filter;\r
1636 }\r
1637\r
1638 plat_target_gamma_set(currentConfig.gamma, 0);\r
1639\r
bac44b18 1640 vkbd = NULL;\r
4814d19e 1641 if (currentConfig.keyboard == 1) {\r
aeda6aba 1642 if (PicoIn.AHW & PAHW_SMS) vkbd = vkbd_init(0);\r
1643 else if (PicoIn.AHW & PAHW_PICO) vkbd = vkbd_init(1);\r
bac44b18 1644 }\r
1645 PicoIn.opt &= ~POPT_EN_KBD;\r
a03469c2 1646 if (((PicoIn.AHW & PAHW_PICO) || (PicoIn.AHW & PAHW_SC)) && currentConfig.keyboard)\r
bac44b18 1647 PicoIn.opt |= POPT_EN_KBD;\r
1648\r
44be7e5c 1649 PicoIn.opt &= ~POPT_EN_MOUSE;\r
1650 if (!(PicoIn.AHW & PAHW_8BIT) && (currentConfig.input_dev0 == PICO_INPUT_MOUSE ||\r
1651 currentConfig.input_dev1 == PICO_INPUT_MOUSE)) {\r
1652 PicoIn.opt |= POPT_EN_MOUSE;\r
1777b86d 1653 plat_grab_cursor(grab_mode);\r
44be7e5c 1654 }\r
1655\r
d4d62665 1656 pemu_loop_prep();\r
1657}\r
1658\r
b24e0f6c 1659/* our tick here is 1 us right now */\r
b4bc2624 1660#define ms_to_ticks(x) (int)(x * 1000)\r
1661#define get_ticks() plat_get_ticks_us()\r
1662#define vsync_delay ms_to_ticks(1)\r
b24e0f6c 1663\r
1664void emu_loop(void)\r
1665{\r
b24e0f6c 1666 int frames_done, frames_shown; /* actual frames for fps counter */\r
b4bc2624 1667 int target_frametime;\r
1668 unsigned int timestamp = 0;\r
1669 unsigned int timestamp_aim = 0;\r
1670 unsigned int timestamp_fps = 0;\r
b24e0f6c 1671 char *notice_msg = NULL;\r
1672 char fpsbuff[24];\r
b59172e3 1673 int fskip_cnt = 0;\r
b24e0f6c 1674\r
1675 fpsbuff[0] = 0;\r
1676\r
46bcb899 1677 PicoLoopPrepare();\r
1678\r
636d5f25 1679 plat_video_loop_prepare();\r
d4d62665 1680 emu_loop_prep();\r
df92fbd1 1681 pemu_sound_start();\r
46bcb899 1682\r
b24e0f6c 1683 /* number of ticks per frame */\r
b59172e3 1684 if (Pico.m.pal)\r
b4bc2624 1685 target_frametime = ms_to_ticks(1000) / 50;\r
b59172e3 1686 else\r
b4bc2624 1687 target_frametime = ms_to_ticks(1000) / 60;\r
b24e0f6c 1688\r
b24e0f6c 1689 reset_timing = 1;\r
b59172e3 1690 frames_done = frames_shown = 0;\r
7b436906 1691\r
b24e0f6c 1692 /* loop with resync every 1 sec. */\r
1693 while (engineState == PGS_Running)\r
1694 {\r
b59172e3 1695 int skip = 0;\r
1696 int diff;\r
b24e0f6c 1697\r
f6c49d38 1698 pprof_start(main);\r
1699\r
b24e0f6c 1700 if (reset_timing) {\r
1701 reset_timing = 0;\r
b59172e3 1702 plat_video_wait_vsync();\r
b4bc2624 1703 timestamp_aim = get_ticks();\r
1704 timestamp_fps = timestamp_aim;\r
b59172e3 1705 fskip_cnt = 0;\r
1706 }\r
1707 else if (currentConfig.EmuOpt & EOPT_NO_FRMLIMIT) {\r
b4bc2624 1708 timestamp_aim = get_ticks();\r
b24e0f6c 1709 }\r
1710\r
b4bc2624 1711 timestamp = get_ticks();\r
b59172e3 1712\r
b24e0f6c 1713 // show notice_msg message?\r
1714 if (notice_msg_time != 0)\r
1715 {\r
1716 static int noticeMsgSum;\r
b4bc2624 1717 if (timestamp - ms_to_ticks(notice_msg_time)\r
1718 > ms_to_ticks(STATUS_MSG_TIMEOUT))\r
b59172e3 1719 {\r
b24e0f6c 1720 notice_msg_time = 0;\r
b24e0f6c 1721 notice_msg = NULL;\r
758abbeb 1722 plat_status_msg_clear();\r
b59172e3 1723 }\r
1724 else {\r
b24e0f6c 1725 int sum = noticeMsg[0] + noticeMsg[1] + noticeMsg[2];\r
1726 if (sum != noticeMsgSum) {\r
1727 plat_status_msg_clear();\r
1728 noticeMsgSum = sum;\r
1729 }\r
1730 notice_msg = noticeMsg;\r
1731 }\r
1732 }\r
1733\r
b24e0f6c 1734 // second changed?\r
b4bc2624 1735 if (timestamp - timestamp_fps >= ms_to_ticks(1000))\r
b24e0f6c 1736 {\r
1737#ifdef BENCHMARK\r
1738 static int bench = 0, bench_fps = 0, bench_fps_s = 0, bfp = 0, bf[4];\r
1739 if (++bench == 10) {\r
1740 bench = 0;\r
1741 bench_fps_s = bench_fps;\r
1742 bf[bfp++ & 3] = bench_fps;\r
1743 bench_fps = 0;\r
1744 }\r
1745 bench_fps += frames_shown;\r
1746 sprintf(fpsbuff, "%02i/%02i/%02i", frames_shown, bench_fps_s, (bf[0]+bf[1]+bf[2]+bf[3])>>2);\r
07e5dbab 1747 printf("%s\n", fpsbuff);\r
b24e0f6c 1748#else\r
b011c2af 1749 if (currentConfig.EmuOpt & EOPT_SHOW_FPS)\r
88a80885 1750 snprintf(fpsbuff, 8, "%02i/%02i ", frames_shown, frames_done);\r
b24e0f6c 1751#endif\r
1752 frames_shown = frames_done = 0;\r
b4bc2624 1753 timestamp_fps += ms_to_ticks(1000);\r
b24e0f6c 1754 }\r
1755#ifdef PFRAMES\r
1756 sprintf(fpsbuff, "%i", Pico.m.frame_count);\r
1757#endif\r
1758\r
b4bc2624 1759 diff = timestamp_aim - timestamp;\r
b24e0f6c 1760\r
b59172e3 1761 if (currentConfig.Frameskip >= 0) // frameskip enabled (or 0)\r
b24e0f6c 1762 {\r
b59172e3 1763 if (fskip_cnt < currentConfig.Frameskip) {\r
1764 fskip_cnt++;\r
1765 skip = 1;\r
1766 }\r
1767 else {\r
1768 fskip_cnt = 0;\r
b24e0f6c 1769 }\r
1770 }\r
b4bc2624 1771 else if (diff < -target_frametime)\r
b24e0f6c 1772 {\r
1773 /* no time left for this frame - skip */\r
e7ee7bc0 1774 /* limit auto frameskip to max_skip */\r
1775 if (fskip_cnt < currentConfig.max_skip) {\r
1776 fskip_cnt++;\r
b59172e3 1777 skip = 1;\r
e7ee7bc0 1778 }\r
1779 else {\r
1780 fskip_cnt = 0;\r
1781 }\r
1782 } else\r
1783 fskip_cnt = 0;\r
b59172e3 1784\r
1785 // don't go in debt too much\r
b4bc2624 1786 while (diff < -target_frametime * 3) {\r
1787 timestamp_aim += target_frametime;\r
1788 diff = timestamp_aim - timestamp;\r
b24e0f6c 1789 }\r
1790\r
1791 emu_update_input();\r
721f2651 1792\r
1793 // 3D glasses\r
1794 skip |= (PicoIn.AHW & PAHW_SMS) &&\r
1795 (Pico.m.hardware & PMS_HW_3D) &&\r
1796 (PicoMem.zram[0x1ffb] & 1);\r
1797\r
b59172e3 1798 if (skip) {\r
b4bc2624 1799 int do_audio = diff > -target_frametime * 2;\r
93f9619e 1800 PicoIn.skipFrame = do_audio ? 1 : 2;\r
b59172e3 1801 PicoFrame();\r
93f9619e 1802 PicoIn.skipFrame = 0;\r
b59172e3 1803 }\r
1804 else {\r
1805 PicoFrame();\r
1806 pemu_finalize_frame(fpsbuff, notice_msg);\r
1807 frames_shown++;\r
1808 }\r
1809 frames_done++;\r
b4bc2624 1810 timestamp_aim += target_frametime;\r
d08e7326 1811\r
b59172e3 1812 if (!skip && !flip_after_sync)\r
b7d64dbd 1813 plat_video_flip();\r
b24e0f6c 1814\r
1815 /* frame limiter */\r
b59172e3 1816 if (!skip && !reset_timing\r
1817 && !(currentConfig.EmuOpt & (EOPT_NO_FRMLIMIT|EOPT_EXT_FRMLIMIT)))\r
b24e0f6c 1818 {\r
b59172e3 1819 unsigned int timestamp = get_ticks();\r
b4bc2624 1820 diff = timestamp_aim - timestamp;\r
b24e0f6c 1821\r
1822 // sleep or vsync if we are still too fast\r
b4bc2624 1823 if (diff > target_frametime + vsync_delay && (currentConfig.EmuOpt & EOPT_VSYNC)) {\r
b24e0f6c 1824 // we are too fast\r
b59172e3 1825 plat_video_wait_vsync();\r
1826 timestamp = get_ticks();\r
b4bc2624 1827 diff = timestamp_aim - timestamp;\r
b59172e3 1828 }\r
b4bc2624 1829 if (diff > target_frametime + vsync_delay) {\r
b59172e3 1830 // still too fast\r
b4bc2624 1831 plat_wait_till_us(timestamp + (diff - target_frametime));\r
b24e0f6c 1832 }\r
1833 }\r
1834\r
b59172e3 1835 if (!skip && flip_after_sync)\r
b7d64dbd 1836 plat_video_flip();\r
b24e0f6c 1837\r
f6c49d38 1838 pprof_end(main);\r
b24e0f6c 1839 }\r
1840\r
1841 emu_set_fastforward(0);\r
1842\r
b24e0f6c 1843 // save SRAM\r
88fd63ad 1844 if ((currentConfig.EmuOpt & EOPT_EN_SRAM) && Pico.sv.changed) {\r
b24e0f6c 1845 plat_status_msg_busy_first("Writing SRAM/BRAM...");\r
1846 emu_save_load_game(0, 1);\r
88fd63ad 1847 Pico.sv.changed = 0;\r
b24e0f6c 1848 }\r
1849\r
b24e0f6c 1850 pemu_loop_end();\r
df92fbd1 1851 emu_sound_stop();\r
1777b86d 1852 plat_grab_cursor(0);\r
b24e0f6c 1853}\r