menu changes
[libpicofe.git] / gp2x / emu.c
CommitLineData
720ee7f6 1// (c) Copyright 2006 notaz, All rights reserved.\r
2// Free for non-commercial use.\r
3\r
4// For commercial use, separate licencing terms must be obtained.\r
5\r
6#include <stdio.h>\r
7#include <stdlib.h>\r
8#include <sys/time.h>\r
4ffd2858 9#include <sys/stat.h>\r
10#include <sys/types.h>\r
720ee7f6 11#include <linux/limits.h>\r
12#include <ctype.h>\r
13#include <unistd.h>\r
14\r
15#include <stdarg.h>\r
16\r
17#include "emu.h"\r
18#include "gp2x.h"\r
19#include "usbjoy.h"\r
20#include "menu.h"\r
21#include "asmutils.h"\r
22#include "cpuctrl.h"\r
23\r
70d2ecc5 24#include <Pico/PicoInt.h>\r
25#include <Pico/Patch.h>\r
26#include <zlib/zlib.h>\r
720ee7f6 27\r
28\r
29#ifdef BENCHMARK\r
30#define OSD_FPS_X 220\r
31#else\r
32#define OSD_FPS_X 260\r
33#endif\r
34\r
35// PicoPad[] format: SACB RLDU\r
36char *actionNames[] = {\r
37 "UP", "DOWN", "LEFT", "RIGHT", "B", "C", "A", "START",\r
38 0, 0, 0, 0, 0, 0, 0, 0, // Z, Y, X, MODE (enabled only when needed), ?, ?, ?, ?\r
39 0, 0, 0, 0, 0, 0, 0, "ENTER MENU", // player2_flag, ?, ?, ?, ?, ?, ?, menu\r
40 "NEXT SAVE SLOT", "PREV SAVE SLOT", "SWITCH RENDERER", "SAVE STATE",\r
41 "LOAD STATE", "VOLUME UP", "VOLUME DOWN", "DONE"\r
42};\r
43\r
44int engineState;\r
45int select_exits = 0;\r
46char *PicoConfigFile = "picoconfig.bin";\r
47currentConfig_t currentConfig;\r
48\r
49char romFileName[PATH_MAX];\r
50unsigned char *rom_data = NULL;\r
51\r
52extern int crashed_940;\r
53\r
3ef975c9 54static short sndBuffer[2*44100/50];\r
720ee7f6 55static char noticeMsg[64]; // notice msg to draw\r
56static struct timeval noticeMsgTime = { 0, 0 }; // when started showing\r
8f1b51ef 57static int osd_fps_x;\r
720ee7f6 58static int combo_keys = 0, combo_acts = 0; // keys and actions which need button combos\r
59static int gp2x_old_gamma = 100;\r
60static unsigned char *movie_data = NULL;\r
61static int movie_size = 0;\r
720ee7f6 62unsigned char *framebuff = 0; // temporary buffer for alt renderer\r
63int state_slot = 0;\r
8f1b51ef 64int reset_timing = 0;\r
720ee7f6 65\r
66/*\r
67// tmp\r
68static FILE *logf = NULL;\r
69\r
70void pprintf(char *texto, ...)\r
71{\r
72 va_list args;\r
73\r
74 va_start(args,texto);\r
75 vfprintf(logf,texto,args);\r
76 va_end(args);\r
77 fflush(logf);\r
78 sync();\r
79}\r
80*/\r
81// utilities\r
82static void strlwr(char* string)\r
83{\r
84 while ( (*string++ = (char)tolower(*string)) );\r
85}\r
86\r
5111820c 87static int try_rfn_cut(void)\r
720ee7f6 88{\r
89 FILE *tmp;\r
90 char *p;\r
91\r
5111820c 92 p = romFileName + strlen(romFileName) - 1;\r
93 for (; p > romFileName; p--)\r
94 if (*p == '.') break;\r
95 *p = 0;\r
720ee7f6 96\r
97 if((tmp = fopen(romFileName, "rb"))) {\r
98 fclose(tmp);\r
99 return 1;\r
100 }\r
101 return 0;\r
102}\r
103\r
daf91588 104static void get_ext(char *file, char *ext)\r
25fced50 105{\r
106 char *p;\r
107\r
daf91588 108 p = file + strlen(file) - 4;\r
109 if (p < file) p = file;\r
25fced50 110 strncpy(ext, p, 4);\r
111 ext[4] = 0;\r
112 strlwr(ext);\r
113}\r
114\r
daf91588 115char *biosfiles_us[] = { "us_scd2_9306", "SegaCDBIOS9303", "us_scd1_9210" };\r
116char *biosfiles_eu[] = { "eu_mcd2_9306", "eu_mcd2_9303", "eu_mcd1_9210" };\r
117char *biosfiles_jp[] = { "jp_mcd1_9112", "jp_mcd1_9111" };\r
118\r
119extern char **g_argv;\r
120\r
121int find_bios(int region, char **bios_file)\r
122{\r
123 static char bios_path[1024];\r
124 int i, j, count;\r
125 char **files;\r
126 FILE *f = NULL;\r
127\r
128 if (region == 4) { // US\r
129 files = biosfiles_us;\r
130 count = sizeof(biosfiles_us) / sizeof(char *);\r
131 } else if (region == 8) { // EU\r
132 files = biosfiles_eu;\r
133 count = sizeof(biosfiles_eu) / sizeof(char *);\r
134 } else if (region == 1 || region == 2) {\r
135 files = biosfiles_jp;\r
136 count = sizeof(biosfiles_jp) / sizeof(char *);\r
137 } else {\r
138 return 0;\r
139 }\r
140\r
141 for (i = 0; i < count; i++)\r
142 {\r
143 strncpy(bios_path, g_argv[0], 1023);\r
144 bios_path[1024-32] = 0;\r
145 for (j = strlen(bios_path); j > 0; j--)\r
146 if (bios_path[j] == '/') { bios_path[j+1] = 0; break; }\r
147 strcat(bios_path, files[i]);\r
148 strcat(bios_path, ".bin");\r
149 f = fopen(bios_path, "rb");\r
150 if (f) break;\r
151\r
152 bios_path[strlen(bios_path) - 4] = 0;\r
153 strcat(bios_path, ".zip");\r
154 f = fopen(bios_path, "rb");\r
155 if (f) break;\r
156 }\r
157\r
158 if (f) {\r
159 printf("using bios: %s\n", bios_path);\r
160 fclose(f);\r
161 if (bios_file) *bios_file = bios_path;\r
162 return 1;\r
163 } else {\r
164 sprintf(menuErrorMsg, "no %s BIOS files found, read docs",\r
165 region != 4 ? (region == 8 ? "EU" : "JAP") : "USA");\r
166 printf("%s\n", menuErrorMsg);\r
167 return 0;\r
168 }\r
169}\r
170\r
171/* checks if romFileName points to valid MegaCD image\r
172 * if so, checks for suitable BIOS */\r
af6e9c49 173static int cd_check(char **bios_file)\r
daf91588 174{\r
175 unsigned char buf[32];\r
af6e9c49 176 pm_file *cd_f;\r
daf91588 177 int type = 0, region = 4; // 1: Japan, 4: US, 8: Europe\r
178\r
af6e9c49 179 cd_f = pm_open(romFileName);\r
daf91588 180 if (!cd_f) return 0; // let the upper level handle this\r
181\r
af6e9c49 182 if (pm_read(buf, 32, cd_f) != 32) {\r
183 pm_close(cd_f);\r
daf91588 184 return 0;\r
185 }\r
186\r
187 if (!strncasecmp("SEGADISCSYSTEM", (char *)buf+0x00, 14)) type = 1; // Sega CD (ISO)\r
188 if (!strncasecmp("SEGADISCSYSTEM", (char *)buf+0x10, 14)) type = 2; // Sega CD (BIN)\r
189 if (type == 0) {\r
af6e9c49 190 pm_close(cd_f);\r
daf91588 191 return 0;\r
192 }\r
193\r
194 /* it seems we have a CD image here. Try to detect region and load a suitable BIOS now.. */\r
af6e9c49 195 pm_seek(cd_f, (type == 1) ? 0x100+0x10B : 0x110+0x10B, SEEK_SET);\r
196 pm_read(buf, 1, cd_f);\r
197 pm_close(cd_f);\r
daf91588 198\r
598e7c06 199 if (buf[0] == 0x64) region = 8; // EU\r
daf91588 200 if (buf[0] == 0xa1) region = 1; // JAP\r
201\r
202 printf("detected %s Sega/Mega CD image with %s region\n",\r
203 type == 2 ? "BIN" : "ISO", region != 4 ? (region == 8 ? "EU" : "JAP") : "USA");\r
204\r
205 if (PicoRegionOverride) {\r
206 region = PicoRegionOverride;\r
207 printf("overrided region to %s\n", region != 4 ? (region == 8 ? "EU" : "JAP") : "USA");\r
208 }\r
209\r
210 if(find_bios(region, bios_file))\r
211 return type; // CD and BIOS detected\r
212\r
213 return -1; // CD detected but load failed\r
214}\r
215\r
720ee7f6 216int emu_ReloadRom(void)\r
217{\r
218 unsigned int rom_size = 0;\r
daf91588 219 char *used_rom_name = romFileName;\r
25fced50 220 char ext[5];\r
af6e9c49 221 pm_file *rom;\r
daf91588 222 int ret, cd_state;\r
720ee7f6 223\r
224 printf("emu_ReloadRom(%s)\n", romFileName);\r
225\r
daf91588 226 get_ext(romFileName, ext);\r
720ee7f6 227\r
daf91588 228 // detect wrong extensions\r
720ee7f6 229 if(!strcmp(ext, ".srm") || !strcmp(ext, "s.gz") || !strcmp(ext, ".mds")) { // s.gz ~ .mds.gz\r
230 sprintf(menuErrorMsg, "Not a ROM selected.");\r
231 return 0;\r
232 }\r
233\r
70d2ecc5 234 PicoPatchUnload();\r
235\r
720ee7f6 236 // check for movie file\r
237 if(movie_data) {\r
238 free(movie_data);\r
239 movie_data = 0;\r
240 }\r
241 if(!strcmp(ext, ".gmv")) {\r
242 // check for both gmv and rom\r
243 int dummy;\r
244 FILE *movie_file = fopen(romFileName, "rb");\r
245 if(!movie_file) {\r
246 sprintf(menuErrorMsg, "Failed to open movie.");\r
247 return 0;\r
248 }\r
249 fseek(movie_file, 0, SEEK_END);\r
250 movie_size = ftell(movie_file);\r
251 fseek(movie_file, 0, SEEK_SET);\r
252 if(movie_size < 64+3) {\r
253 sprintf(menuErrorMsg, "Invalid GMV file.");\r
254 fclose(movie_file);\r
255 return 0;\r
256 }\r
257 movie_data = malloc(movie_size);\r
258 if(movie_data == NULL) {\r
259 sprintf(menuErrorMsg, "low memory.");\r
260 fclose(movie_file);\r
261 return 0;\r
262 }\r
263 fread(movie_data, 1, movie_size, movie_file);\r
264 fclose(movie_file);\r
265 if (strncmp((char *)movie_data, "Gens Movie TEST", 15) != 0) {\r
266 sprintf(menuErrorMsg, "Invalid GMV file.");\r
267 return 0;\r
268 }\r
5111820c 269 dummy = try_rfn_cut() || try_rfn_cut();\r
720ee7f6 270 if (!dummy) {\r
271 sprintf(menuErrorMsg, "Could't find a ROM for movie.");\r
272 return 0;\r
273 }\r
daf91588 274 get_ext(romFileName, ext);\r
720ee7f6 275 }\r
70d2ecc5 276 else if (!strcmp(ext, ".pat")) {\r
277 int dummy;\r
278 PicoPatchLoad(romFileName);\r
279 dummy = try_rfn_cut() || try_rfn_cut();\r
280 if (!dummy) {\r
281 sprintf(menuErrorMsg, "Could't find a ROM to patch.");\r
282 return 0;\r
283 }\r
284 get_ext(romFileName, ext);\r
285 }\r
720ee7f6 286\r
daf91588 287 // check for MegaCD image\r
af6e9c49 288 cd_state = cd_check(&used_rom_name);\r
daf91588 289 if (cd_state > 0) {\r
290 PicoMCD |= 1;\r
291 get_ext(used_rom_name, ext);\r
292 } else if (cd_state == -1) {\r
293 // bios_help() ?\r
294 return 0;\r
295 } else {\r
49fe50f0 296 if (PicoMCD & 1) PicoExitMCD();\r
daf91588 297 PicoMCD &= ~1;\r
298 }\r
299\r
af6e9c49 300 rom = pm_open(used_rom_name);\r
720ee7f6 301 if(!rom) {\r
302 sprintf(menuErrorMsg, "Failed to open rom.");\r
303 return 0;\r
304 }\r
305\r
306 if(rom_data) {\r
307 free(rom_data);\r
308 rom_data = 0;\r
309 rom_size = 0;\r
310 }\r
311\r
af6e9c49 312 if( (ret = PicoCartLoad(rom, &rom_data, &rom_size)) ) {\r
313 sprintf(menuErrorMsg, "PicoCartLoad() failed.");\r
314 printf("%s\n", menuErrorMsg);\r
315 pm_close(rom);\r
316 return 0;\r
720ee7f6 317 }\r
af6e9c49 318 pm_close(rom);\r
720ee7f6 319\r
320 // detect wrong files (Pico crashes on very small files), also see if ROM EP is good\r
321 if(rom_size <= 0x200 || strncmp((char *)rom_data, "Pico", 4) == 0 ||\r
322 ((*(unsigned short *)(rom_data+4)<<16)|(*(unsigned short *)(rom_data+6))) >= (int)rom_size) {\r
323 if (rom_data) free(rom_data);\r
324 rom_data = 0;\r
325 sprintf(menuErrorMsg, "Not a ROM selected.");\r
326 return 0;\r
327 }\r
328\r
daf91588 329 // load config for this ROM (do this before insert to get correct region)\r
330 ret = emu_ReadConfig(1);\r
331 if (!ret)\r
332 emu_ReadConfig(0);\r
333\r
720ee7f6 334 printf("PicoCartInsert(%p, %d);\n", rom_data, rom_size);\r
335 if(PicoCartInsert(rom_data, rom_size)) {\r
336 sprintf(menuErrorMsg, "Failed to load ROM.");\r
337 return 0;\r
338 }\r
339\r
daf91588 340 Pico.m.frame_count = 0;\r
341\r
342 // insert CD if it was detected\r
343 if (cd_state > 0) {\r
344 ret = Insert_CD(romFileName, cd_state == 2);\r
345 if (ret != 0) {\r
346 sprintf(menuErrorMsg, "Insert_CD() failed, invalid CD image?");\r
347 printf("%s\n", menuErrorMsg);\r
348 return 0;\r
349 }\r
350 }\r
720ee7f6 351\r
352 // emu_ReadConfig() might have messed currentConfig.lastRomFile\r
353 strncpy(currentConfig.lastRomFile, romFileName, sizeof(currentConfig.lastRomFile)-1);\r
354 currentConfig.lastRomFile[sizeof(currentConfig.lastRomFile)-1] = 0;\r
355\r
70d2ecc5 356 if (PicoPatches) {\r
357 PicoPatchPrepare();\r
358 PicoPatchApply();\r
359 }\r
360\r
720ee7f6 361 // additional movie stuff\r
362 if(movie_data) {\r
363 if(movie_data[0x14] == '6')\r
364 PicoOpt |= 0x20; // 6 button pad\r
365 else PicoOpt &= ~0x20;\r
1a20dbc8 366 PicoOpt |= 0x40; // accurate timing\r
720ee7f6 367 if(movie_data[0xF] >= 'A') {\r
1a20dbc8 368 if(movie_data[0x16] & 0x80) {\r
369 PicoRegionOverride = 8;\r
370 } else {\r
371 PicoRegionOverride = 4;\r
372 }\r
373 PicoReset(0);\r
720ee7f6 374 // TODO: bits 6 & 5\r
375 }\r
1a20dbc8 376 movie_data[0x18+30] = 0;\r
377 sprintf(noticeMsg, "MOVIE: %s", (char *) &movie_data[0x18]);\r
720ee7f6 378 }\r
379 else\r
380 {\r
381 if(Pico.m.pal) {\r
382 strcpy(noticeMsg, "PAL SYSTEM / 50 FPS");\r
383 } else {\r
384 strcpy(noticeMsg, "NTSC SYSTEM / 60 FPS");\r
385 }\r
386 }\r
387 gettimeofday(&noticeMsgTime, 0);\r
49fe50f0 388\r
720ee7f6 389 // load SRAM for this ROM\r
390 if(currentConfig.EmuOpt & 1)\r
391 emu_SaveLoadGame(1, 1);\r
392\r
720ee7f6 393 return 1;\r
394}\r
395\r
396\r
dfa4c846 397static void emu_msg_cb(const char *msg);\r
398\r
720ee7f6 399void emu_Init(void)\r
400{\r
401 // make temp buffer for alt renderer\r
402 framebuff = malloc((8+320)*(8+240+8));\r
403 if (!framebuff)\r
404 {\r
405 printf("framebuff == 0\n");\r
406 }\r
407\r
4ffd2858 408 // make dirs for saves, cfgs, etc.\r
409 mkdir("mds", 0777);\r
410 mkdir("srm", 0777);\r
411 mkdir("brm", 0777);\r
412 mkdir("cfg", 0777);\r
413\r
720ee7f6 414 PicoInit();\r
dfa4c846 415 PicoMessage = emu_msg_cb;\r
720ee7f6 416\r
417// logf = fopen("log.txt", "w");\r
418}\r
419\r
420\r
4ffd2858 421static void romfname_ext(char *dst, const char *prefix, const char *ext)\r
720ee7f6 422{\r
423 char *p;\r
4ffd2858 424 int prefix_len = 0;\r
720ee7f6 425\r
426 // make save filename\r
427 for (p = romFileName+strlen(romFileName)-1; p >= romFileName && *p != '/'; p--); p++;\r
4ffd2858 428 *dst = 0;\r
429 if (prefix) {\r
430 strcpy(dst, prefix);\r
431 prefix_len = strlen(prefix);\r
432 }\r
433 strncpy(dst + prefix_len, p, 511-prefix_len);\r
720ee7f6 434 dst[511-8] = 0;\r
4ffd2858 435 if (dst[strlen(dst)-4] == '.') dst[strlen(dst)-4] = 0;\r
436 if (ext) strcat(dst, ext);\r
720ee7f6 437}\r
438\r
439\r
440static void find_combos(void)\r
441{\r
442 int act, u;\r
443\r
444 // find out which keys and actions are combos\r
445 combo_keys = combo_acts = 0;\r
446 for (act = 0; act < 32; act++)\r
447 {\r
448 int keyc = 0;\r
449 if (act == 16) continue; // player2 flag\r
450 for (u = 0; u < 32; u++)\r
451 {\r
452 if (currentConfig.KeyBinds[u] & (1 << act)) keyc++;\r
453 }\r
454 if (keyc > 1)\r
455 {\r
456 // loop again and mark those keys and actions as combo\r
457 for (u = 0; u < 32; u++)\r
458 {\r
459 if (currentConfig.KeyBinds[u] & (1 << act)) {\r
460 combo_keys |= 1 << u;\r
461 combo_acts |= 1 << act;\r
462 }\r
463 }\r
464 }\r
465 }\r
466 // printf("combo keys/acts: %08x %08x\n", combo_keys, combo_acts);\r
467}\r
468\r
469\r
470int emu_ReadConfig(int game)\r
471{\r
472 FILE *f;\r
473 char cfg[512];\r
474 int bread = 0;\r
475\r
476 if (!game)\r
477 {\r
478 // set default config\r
479 memset(&currentConfig, 0, sizeof(currentConfig));\r
480 currentConfig.lastRomFile[0] = 0;\r
49fe50f0 481 currentConfig.EmuOpt = 0x1f | 0x400; // | cd_leds\r
482 currentConfig.PicoOpt = 0x0f | 0xe00; // | use_940 | cd_pcm | cd_cdda\r
7bc9cd7f 483 currentConfig.PsndRate = 22050; // 44100;\r
720ee7f6 484 currentConfig.PicoRegion = 0; // auto\r
979ba09f 485 currentConfig.PicoAutoRgnOrder = 0x184; // US, EU, JP\r
720ee7f6 486 currentConfig.Frameskip = -1; // auto\r
487 currentConfig.CPUclock = 200;\r
488 currentConfig.volume = 50;\r
489 currentConfig.KeyBinds[ 0] = 1<<0; // SACB RLDU\r
490 currentConfig.KeyBinds[ 4] = 1<<1;\r
491 currentConfig.KeyBinds[ 2] = 1<<2;\r
492 currentConfig.KeyBinds[ 6] = 1<<3;\r
493 currentConfig.KeyBinds[14] = 1<<4;\r
494 currentConfig.KeyBinds[13] = 1<<5;\r
495 currentConfig.KeyBinds[12] = 1<<6;\r
496 currentConfig.KeyBinds[ 8] = 1<<7;\r
497 currentConfig.KeyBinds[15] = 1<<26; // switch rend\r
498 currentConfig.KeyBinds[10] = 1<<27; // save state\r
499 currentConfig.KeyBinds[11] = 1<<28; // load state\r
500 currentConfig.KeyBinds[23] = 1<<29; // vol up\r
501 currentConfig.KeyBinds[22] = 1<<30; // vol down\r
502 currentConfig.gamma = 100;\r
5f9922e6 503 currentConfig.PicoCDBuffers = 64;\r
720ee7f6 504 strncpy(cfg, PicoConfigFile, 511);\r
505 cfg[511] = 0;\r
506 } else {\r
4ffd2858 507 romfname_ext(cfg, "cfg/", ".pbcfg");\r
508 f = fopen(cfg, "rb");\r
509 if (!f) romfname_ext(cfg, NULL, ".pbcfg");\r
510 else fclose(f);\r
720ee7f6 511 }\r
512\r
513 printf("emu_ReadConfig: %s ", cfg);\r
514 f = fopen(cfg, "rb");\r
515 if (f) {\r
516 bread = fread(&currentConfig, 1, sizeof(currentConfig), f);\r
517 fclose(f);\r
518 }\r
519 printf((bread == sizeof(currentConfig)) ? "(ok)\n" : "(failed)\n");\r
520\r
521 PicoOpt = currentConfig.PicoOpt;\r
522 PsndRate = currentConfig.PsndRate;\r
523 PicoRegionOverride = currentConfig.PicoRegion;\r
979ba09f 524 PicoAutoRgnOrder = currentConfig.PicoAutoRgnOrder;\r
5f9922e6 525 PicoCDBuffers = currentConfig.PicoCDBuffers;\r
720ee7f6 526 if (PicoOpt & 0x20) {\r
527 actionNames[ 8] = "Z"; actionNames[ 9] = "Y";\r
528 actionNames[10] = "X"; actionNames[11] = "MODE";\r
529 }\r
530 // some sanity checks\r
531 if (currentConfig.CPUclock < 1 || currentConfig.CPUclock > 4096) currentConfig.CPUclock = 200;\r
532 if (currentConfig.gamma < 10 || currentConfig.gamma > 300) currentConfig.gamma = 100;\r
533 // if volume keys are unbound, bind them to volume control\r
534 if (!currentConfig.KeyBinds[23] && !currentConfig.KeyBinds[22]) {\r
535 currentConfig.KeyBinds[23] = 1<<29; // vol up\r
536 currentConfig.KeyBinds[22] = 1<<30; // vol down\r
537 }\r
538\r
539 return (bread == sizeof(currentConfig));\r
540}\r
541\r
542\r
543int emu_WriteConfig(int game)\r
544{\r
545 FILE *f;\r
546 char cfg[512];\r
547 int bwrite = 0;\r
548\r
549 if (!game)\r
550 {\r
551 strncpy(cfg, PicoConfigFile, 511);\r
552 cfg[511] = 0;\r
553 } else {\r
2fb6c387 554 romfname_ext(cfg, "cfg/", ".pbcfg");\r
720ee7f6 555 }\r
556\r
557 printf("emu_WriteConfig: %s ", cfg);\r
558 f = fopen(cfg, "wb");\r
559 if (f) {\r
560 currentConfig.PicoOpt = PicoOpt;\r
561 currentConfig.PsndRate = PsndRate;\r
562 currentConfig.PicoRegion = PicoRegionOverride;\r
979ba09f 563 currentConfig.PicoAutoRgnOrder = PicoAutoRgnOrder;\r
5f9922e6 564 currentConfig.PicoCDBuffers = PicoCDBuffers;\r
720ee7f6 565 bwrite = fwrite(&currentConfig, 1, sizeof(currentConfig), f);\r
566 fflush(f);\r
567 fclose(f);\r
568 sync();\r
569 }\r
570 printf((bwrite == sizeof(currentConfig)) ? "(ok)\n" : "(failed)\n");\r
571\r
572 return (bwrite == sizeof(currentConfig));\r
573}\r
574\r
575\r
576void emu_Deinit(void)\r
577{\r
578 // save SRAM\r
579 if((currentConfig.EmuOpt & 1) && SRam.changed) {\r
580 emu_SaveLoadGame(0, 1);\r
581 SRam.changed = 0;\r
582 }\r
583\r
584 if (!(currentConfig.EmuOpt & 0x20))\r
585 emu_WriteConfig(0);\r
586 free(framebuff);\r
587\r
588 PicoExit();\r
589// fclose(logf);\r
590\r
591 // restore gamma\r
592 if (gp2x_old_gamma != 100)\r
593 set_gamma(100);\r
594}\r
595\r
596\r
0805b5b4 597void osd_text(int x, int y, const char *text)\r
720ee7f6 598{\r
599 int len = strlen(text)*8;\r
600\r
601 if ((PicoOpt&0x10)||!(currentConfig.EmuOpt&0x80)) {\r
daf91588 602 int *p, i, h;\r
720ee7f6 603 x &= ~3; // align x\r
604 len = (len+3) >> 2;\r
605 for (h = 0; h < 8; h++) {\r
606 p = (int *) ((unsigned char *) gp2x_screen+x+320*(y+h));\r
daf91588 607 for (i = len; i; i--, p++) *p = 0xe0e0e0e0;\r
720ee7f6 608 }\r
daf91588 609 gp2x_text_out8_2(x, y, text, 0xf0);\r
720ee7f6 610 } else {\r
611 int *p, i, h;\r
612 x &= ~1; // align x\r
613 len = (len+1) >> 1;\r
614 for (h = 0; h < 8; h++) {\r
615 p = (int *) ((unsigned short *) gp2x_screen+x+320*(y+h));\r
616 for (i = len; i; i--, p++) *p = (*p>>2)&0x39e7;\r
617 }\r
618 gp2x_text_out15(x, y, text);\r
619 }\r
620}\r
621\r
daf91588 622static void cd_leds(void)\r
623{\r
8dfb9fd5 624 // mmu problems?\r
625// static\r
626 int old_reg;\r
627// if (!((Pico_mcd->s68k_regs[0] ^ old_reg) & 3)) return; // no change\r
daf91588 628 old_reg = Pico_mcd->s68k_regs[0];\r
629\r
630 if ((PicoOpt&0x10)||!(currentConfig.EmuOpt&0x80)) {\r
631 // 8-bit modes\r
632 unsigned int col_g = (old_reg & 2) ? 0xc0c0c0c0 : 0xe0e0e0e0;\r
633 unsigned int col_r = (old_reg & 1) ? 0xd0d0d0d0 : 0xe0e0e0e0;\r
49fe50f0 634 *(unsigned int *)((char *)gp2x_screen + 320*2+ 4) =\r
635 *(unsigned int *)((char *)gp2x_screen + 320*3+ 4) =\r
636 *(unsigned int *)((char *)gp2x_screen + 320*4+ 4) = col_g;\r
637 *(unsigned int *)((char *)gp2x_screen + 320*2+12) =\r
638 *(unsigned int *)((char *)gp2x_screen + 320*3+12) =\r
639 *(unsigned int *)((char *)gp2x_screen + 320*4+12) = col_r;\r
daf91588 640 } else {\r
641 // 16-bit modes\r
49fe50f0 642 unsigned int *p = (unsigned int *)((short *)gp2x_screen + 320*2+4);\r
daf91588 643 unsigned int col_g = (old_reg & 2) ? 0x06000600 : 0;\r
644 unsigned int col_r = (old_reg & 1) ? 0xc000c000 : 0;\r
49fe50f0 645 *p++ = col_g; *p++ = col_g; p+=2; *p++ = col_r; *p++ = col_r; p += 320/2 - 12/2;\r
646 *p++ = col_g; *p++ = col_g; p+=2; *p++ = col_r; *p++ = col_r; p += 320/2 - 12/2;\r
647 *p++ = col_g; *p++ = col_g; p+=2; *p++ = col_r; *p++ = col_r; p += 320/2 - 12/2;\r
daf91588 648 }\r
649}\r
650\r
720ee7f6 651static int EmuScan16(unsigned int num, void *sdata)\r
652{\r
653 if (!(Pico.video.reg[1]&8)) num += 8;\r
654 DrawLineDest = (unsigned short *) gp2x_screen + 320*(num+1);\r
655\r
656 return 0;\r
657}\r
658\r
659static int EmuScan8(unsigned int num, void *sdata)\r
660{\r
661 if (!(Pico.video.reg[1]&8)) num += 8;\r
662 DrawLineDest = (unsigned char *) gp2x_screen + 320*(num+1);\r
663\r
664 return 0;\r
665}\r
666\r
e5d315a5 667int localPal[0x100];\r
720ee7f6 668static void (*vidCpyM2)(void *dest, void *src) = NULL;\r
669\r
0805b5b4 670static void blit(const char *fps, const char *notice)\r
720ee7f6 671{\r
daf91588 672 int emu_opt = currentConfig.EmuOpt;\r
673\r
720ee7f6 674 if (PicoOpt&0x10) {\r
675 // 8bit fast renderer\r
676 if (Pico.m.dirtyPal) {\r
677 Pico.m.dirtyPal = 0;\r
678 vidConvCpyRGB32(localPal, Pico.cram, 0x40);\r
679 // feed new palette to our device\r
680 gp2x_video_setpalette(localPal, 0x40);\r
681 }\r
682 vidCpyM2((unsigned char *)gp2x_screen+320*8, framebuff+328*8);\r
daf91588 683 } else if (!(emu_opt&0x80)) {\r
720ee7f6 684 // 8bit accurate renderer\r
685 if (Pico.m.dirtyPal) {\r
686 Pico.m.dirtyPal = 0;\r
687 if(Pico.video.reg[0xC]&8) { // shadow/hilight mode\r
688 vidConvCpyRGB32(localPal, Pico.cram, 0x40);\r
689 vidConvCpyRGB32sh(localPal+0x40, Pico.cram, 0x40);\r
690 vidConvCpyRGB32hi(localPal+0x80, Pico.cram, 0x40);\r
691 blockcpy(localPal+0xc0, localPal+0x40, 0x40*4);\r
daf91588 692 localPal[0xc0] = 0x0000c000;\r
693 localPal[0xd0] = 0x00c00000;\r
720ee7f6 694 localPal[0xe0] = 0x00000000; // reserved pixels for OSD\r
695 localPal[0xf0] = 0x00ffffff;\r
696 gp2x_video_setpalette(localPal, 0x100);\r
697 } else if (rendstatus & 0x20) { // mid-frame palette changes\r
698 vidConvCpyRGB32(localPal, Pico.cram, 0x40);\r
699 vidConvCpyRGB32(localPal+0x40, HighPal, 0x40);\r
700 vidConvCpyRGB32(localPal+0x80, HighPal+0x40, 0x40);\r
701 gp2x_video_setpalette(localPal, 0xc0);\r
702 } else {\r
703 vidConvCpyRGB32(localPal, Pico.cram, 0x40);\r
704 gp2x_video_setpalette(localPal, 0x40);\r
705 }\r
706 }\r
707 }\r
708\r
709 if (notice) osd_text(4, 232, notice);\r
daf91588 710 if (emu_opt & 2)\r
720ee7f6 711 osd_text(osd_fps_x, 232, fps);\r
daf91588 712 if ((emu_opt & 0x400) && (PicoMCD & 1))\r
713 cd_leds();\r
720ee7f6 714\r
715 //gp2x_video_wait_vsync();\r
716 gp2x_video_flip();\r
717\r
718 if (!(PicoOpt&0x10)) {\r
719 if (!(Pico.video.reg[1]&8)) {\r
720 if (currentConfig.EmuOpt&0x80) {\r
721 DrawLineDest = (unsigned short *) gp2x_screen + 320*8;\r
722 } else {\r
723 DrawLineDest = (unsigned char *) gp2x_screen + 320*8;\r
724 }\r
725 } else {\r
726 DrawLineDest = gp2x_screen;\r
727 }\r
728 }\r
729}\r
730\r
731\r
732// clears whole screen or just the notice area (in all buffers)\r
733static void clearArea(int full)\r
734{\r
daf91588 735 if ((PicoOpt&0x10)||!(currentConfig.EmuOpt&0x80)) {\r
736 // 8-bit renderers\r
737 if (full) gp2x_memset_all_buffers(0, 0xe0, 320*240);\r
738 else gp2x_memset_all_buffers(320*232, 0xe0, 320*8);\r
739 } else {\r
720ee7f6 740 // 16bit accurate renderer\r
741 if (full) gp2x_memset_all_buffers(0, 0, 320*240*2);\r
742 else gp2x_memset_all_buffers(320*232*2, 0, 320*8*2);\r
720ee7f6 743 }\r
744}\r
745\r
746\r
747static void vidResetMode(void)\r
748{\r
749 if (PicoOpt&0x10) {\r
720ee7f6 750 gp2x_video_changemode(8);\r
720ee7f6 751 } else if (currentConfig.EmuOpt&0x80) {\r
752 gp2x_video_changemode(15);\r
753 PicoDrawSetColorFormat(1);\r
754 PicoScan = EmuScan16;\r
755 PicoScan(0, 0);\r
756 } else {\r
daf91588 757 gp2x_video_changemode(8);\r
758 PicoDrawSetColorFormat(2);\r
759 PicoScan = EmuScan8;\r
760 PicoScan(0, 0);\r
761 }\r
762 if ((PicoOpt&0x10)||!(currentConfig.EmuOpt&0x80)) {\r
763 // setup pal for 8-bit modes\r
764 localPal[0xc0] = 0x0000c000; // MCD LEDs\r
765 localPal[0xd0] = 0x00c00000;\r
720ee7f6 766 localPal[0xe0] = 0x00000000; // reserved pixels for OSD\r
767 localPal[0xf0] = 0x00ffffff;\r
720ee7f6 768 gp2x_video_setpalette(localPal, 0x100);\r
769 gp2x_memset_all_buffers(0, 0xe0, 320*240);\r
770 gp2x_video_flip();\r
720ee7f6 771 }\r
772 Pico.m.dirtyPal = 1;\r
773 // reset scaling\r
774 gp2x_video_RGB_setscaling((PicoOpt&0x100)&&!(Pico.video.reg[12]&1) ? 256 : 320, 240);\r
775}\r
776\r
777\r
dfa4c846 778static void emu_msg_cb(const char *msg)\r
779{\r
780 if ((PicoOpt&0x10)||!(currentConfig.EmuOpt&0x80)) {\r
781 // 8-bit renderers\r
782 gp2x_memset_all_buffers(320*232, 0xe0, 320*8);\r
783 osd_text(4, 232, msg);\r
784 gp2x_memcpy_all_buffers((char *)gp2x_screen+320*232, 320*232, 320*8);\r
785 } else {\r
786 // 16bit accurate renderer\r
787 gp2x_memset_all_buffers(320*232*2, 0, 320*8*2);\r
788 osd_text(4, 232, msg);\r
789 gp2x_memcpy_all_buffers((char *)gp2x_screen+320*232*2, 320*232*2, 320*8*2);\r
790 }\r
791 gettimeofday(&noticeMsgTime, 0);\r
792 noticeMsgTime.tv_sec -= 2;\r
e5589947 793\r
794 /* assumption: emu_msg_cb gets called only when something slow is about to happen */\r
795 reset_timing = 1;\r
dfa4c846 796}\r
797\r
1078e544 798static void emu_state_cb(const char *str)\r
799{\r
800 clearArea(0);\r
801 blit("", str);\r
802}\r
803\r
720ee7f6 804static void RunEvents(unsigned int which)\r
805{\r
806 if(which & 0x1800) { // save or load (but not both)\r
807 int do_it = 1;\r
4ffd2858 808 if (!(which & 0x1000) && (currentConfig.EmuOpt & 0x200) && emu_check_save_file(state_slot)) {\r
720ee7f6 809 unsigned long keys;\r
810 blit("", "OVERWRITE SAVE? (Y=yes, X=no)");\r
811 while( !((keys = gp2x_joystick_read(1)) & (GP2X_X|GP2X_Y)) )\r
812 usleep(50*1024);\r
813 if (keys & GP2X_X) do_it = 0;\r
814 clearArea(0);\r
815 }\r
816 if (do_it) {\r
0805b5b4 817 osd_text(4, 232, (which & 0x1000) ? "LOADING GAME" : "SAVING GAME");\r
1078e544 818 PicoStateProgressCB = emu_state_cb;\r
819 gp2x_memcpy_all_buffers(gp2x_screen, 0, 320*240*2);\r
0805b5b4 820 emu_SaveLoadGame((which & 0x1000) >> 12, 0);\r
1078e544 821 PicoStateProgressCB = NULL;\r
720ee7f6 822 }\r
823\r
824 reset_timing = 1;\r
825 }\r
826 if(which & 0x0400) { // switch renderer\r
827 if ( PicoOpt&0x10) { PicoOpt&=~0x10; currentConfig.EmuOpt |= 0x80; }\r
828 else if (!(currentConfig.EmuOpt&0x80)) PicoOpt|= 0x10;\r
829 else currentConfig.EmuOpt &= ~0x80;\r
830\r
831 vidResetMode();\r
832\r
833 if (PicoOpt&0x10) {\r
834 strcpy(noticeMsg, " 8bit fast renderer");\r
835 } else if (currentConfig.EmuOpt&0x80) {\r
836 strcpy(noticeMsg, "16bit accurate renderer");\r
837 } else {\r
838 strcpy(noticeMsg, " 8bit accurate renderer");\r
839 }\r
840\r
841 gettimeofday(&noticeMsgTime, 0);\r
842 }\r
843 if(which & 0x0300) {\r
844 if(which&0x0200) {\r
845 state_slot -= 1;\r
846 if(state_slot < 0) state_slot = 9;\r
847 } else {\r
848 state_slot += 1;\r
849 if(state_slot > 9) state_slot = 0;\r
850 }\r
4ffd2858 851 sprintf(noticeMsg, "SAVE SLOT %i [%s]", state_slot, emu_check_save_file(state_slot) ? "USED" : "FREE");\r
720ee7f6 852 gettimeofday(&noticeMsgTime, 0);\r
853 }\r
854 if(which & 0x0080) {\r
855 engineState = PGS_Menu;\r
856 }\r
857}\r
858\r
859\r
25fced50 860static void updateMovie(void)\r
861{\r
862 int offs = Pico.m.frame_count*3 + 0x40;\r
863 if (offs+3 > movie_size) {\r
864 free(movie_data);\r
865 movie_data = 0;\r
866 strcpy(noticeMsg, "END OF MOVIE.");\r
867 printf("END OF MOVIE.\n");\r
868 gettimeofday(&noticeMsgTime, 0);\r
869 } else {\r
870 // MXYZ SACB RLDU\r
871 PicoPad[0] = ~movie_data[offs] & 0x8f; // ! SCBA RLDU\r
872 if(!(movie_data[offs] & 0x10)) PicoPad[0] |= 0x40; // A\r
873 if(!(movie_data[offs] & 0x20)) PicoPad[0] |= 0x10; // B\r
874 if(!(movie_data[offs] & 0x40)) PicoPad[0] |= 0x20; // A\r
875 PicoPad[1] = ~movie_data[offs+1] & 0x8f; // ! SCBA RLDU\r
876 if(!(movie_data[offs+1] & 0x10)) PicoPad[1] |= 0x40; // A\r
877 if(!(movie_data[offs+1] & 0x20)) PicoPad[1] |= 0x10; // B\r
878 if(!(movie_data[offs+1] & 0x40)) PicoPad[1] |= 0x20; // A\r
879 PicoPad[0] |= (~movie_data[offs+2] & 0x0A) << 8; // ! MZYX\r
880 if(!(movie_data[offs+2] & 0x01)) PicoPad[0] |= 0x0400; // X\r
881 if(!(movie_data[offs+2] & 0x04)) PicoPad[0] |= 0x0100; // Z\r
882 PicoPad[1] |= (~movie_data[offs+2] & 0xA0) << 4; // ! MZYX\r
883 if(!(movie_data[offs+2] & 0x10)) PicoPad[1] |= 0x0400; // X\r
884 if(!(movie_data[offs+2] & 0x40)) PicoPad[1] |= 0x0100; // Z\r
885 }\r
886}\r
887\r
888\r
720ee7f6 889static void updateKeys(void)\r
890{\r
891 unsigned long keys, allActions[2] = { 0, 0 }, events;\r
892 static unsigned long prevEvents = 0;\r
893 int joy, i;\r
894\r
895 keys = gp2x_joystick_read(0);\r
896 if (keys & GP2X_SELECT) {\r
897 engineState = select_exits ? PGS_Quit : PGS_Menu;\r
898 // wait until select is released, so menu would not resume game\r
899 while (gp2x_joystick_read(1) & GP2X_SELECT) usleep(50*1000);\r
900 }\r
901\r
902 keys &= CONFIGURABLE_KEYS;\r
903\r
904 for (i = 0; i < 32; i++)\r
905 {\r
906 if (keys & (1 << i)) {\r
907 int pl, acts = currentConfig.KeyBinds[i];\r
908 if (!acts) continue;\r
909 pl = (acts >> 16) & 1;\r
910 if (combo_keys & (1 << i)) {\r
911 int u = i+1, acts_c = acts & combo_acts;\r
912 // let's try to find the other one\r
913 if (acts_c)\r
914 for (; u < 32; u++)\r
915 if ( (currentConfig.KeyBinds[u] & acts_c) && (keys & (1 << u)) ) {\r
916 allActions[pl] |= acts_c;\r
917 keys &= ~((1 << i) | (1 << u));\r
918 break;\r
919 }\r
920 // add non-combo actions if combo ones were not found\r
921 if (!acts_c || u == 32)\r
922 allActions[pl] |= acts & ~combo_acts;\r
923 } else {\r
924 allActions[pl] |= acts;\r
925 }\r
926 }\r
927 }\r
928\r
929 // add joy inputs\r
930 if (num_of_joys > 0)\r
931 {\r
932 gp2x_usbjoy_update();\r
933 for (joy = 0; joy < num_of_joys; joy++) {\r
934 int keys = gp2x_usbjoy_check2(joy);\r
935 for (i = 0; i < 32; i++) {\r
936 if (keys & (1 << i)) {\r
937 int acts = currentConfig.JoyBinds[joy][i];\r
938 int pl = (acts >> 16) & 1;\r
939 allActions[pl] |= acts;\r
940 }\r
941 }\r
942 }\r
943 }\r
944\r
25fced50 945 PicoPad[0] = (unsigned short) allActions[0];\r
946 PicoPad[1] = (unsigned short) allActions[1];\r
720ee7f6 947\r
948 events = (allActions[0] | allActions[1]) >> 16;\r
949\r
950 // volume is treated in special way and triggered every frame\r
951 if(events & 0x6000) {\r
952 int vol = currentConfig.volume;\r
953 if (events & 0x2000) {\r
954 if (vol < 90) vol++;\r
955 } else {\r
956 if (vol > 0) vol--;\r
957 }\r
958 gp2x_sound_volume(vol, vol);\r
959 sprintf(noticeMsg, "VOL: %02i", vol);\r
960 gettimeofday(&noticeMsgTime, 0);\r
961 currentConfig.volume = vol;\r
962 }\r
963\r
964 events &= ~prevEvents;\r
965 if (events) RunEvents(events);\r
25fced50 966 if (movie_data) updateMovie();\r
720ee7f6 967\r
968 prevEvents = (allActions[0] | allActions[1]) >> 16;\r
969}\r
970\r
720ee7f6 971\r
3ef975c9 972static void updateSound(int len)\r
720ee7f6 973{\r
3ef975c9 974 if (PicoOpt&8) len<<=1;\r
720ee7f6 975\r
d9a18943 976 /* avoid writing audio when lagging behind to prevent audio lag */\r
977 if (PicoSkipFrame != 2)\r
978 gp2x_sound_write(PsndOut, len<<1);\r
720ee7f6 979}\r
980\r
981\r
d9a18943 982static void SkipFrame(int do_audio)\r
720ee7f6 983{\r
d9a18943 984 PicoSkipFrame=do_audio ? 1 : 2;\r
720ee7f6 985 PicoFrame();\r
986 PicoSkipFrame=0;\r
720ee7f6 987}\r
988\r
989\r
4ffd2858 990void emu_forced_frame(void)\r
991{\r
992 int po_old = PicoOpt;\r
993\r
994 PicoOpt |= 0x10;\r
995 PicoFrameFull();\r
4ffd2858 996\r
997 if (!(Pico.video.reg[12]&1)) {\r
dccc2bd0 998 vidCpyM2 = vidCpyM2_32col;\r
4ffd2858 999 clearArea(1);\r
dccc2bd0 1000 } else vidCpyM2 = vidCpyM2_40col;\r
4ffd2858 1001\r
1002 vidCpyM2((unsigned char *)gp2x_screen+320*8, framebuff+328*8);\r
1003 vidConvCpyRGB32(localPal, Pico.cram, 0x40);\r
1004 gp2x_video_setpalette(localPal, 0x40);\r
e1aa0fa7 1005\r
1006 PicoOpt = po_old;\r
4ffd2858 1007}\r
1008\r
720ee7f6 1009static void simpleWait(int thissec, int lim_time)\r
1010{\r
1011 struct timeval tval;\r
1012\r
1013 spend_cycles(1024);\r
1014 gettimeofday(&tval, 0);\r
1015 if(thissec != tval.tv_sec) tval.tv_usec+=1000000;\r
1016\r
1017 while(tval.tv_usec < lim_time)\r
1018 {\r
1019 spend_cycles(1024);\r
1020 gettimeofday(&tval, 0);\r
1021 if(thissec != tval.tv_sec) tval.tv_usec+=1000000;\r
1022 }\r
1023}\r
1024\r
1025\r
1026void emu_Loop(void)\r
1027{\r
1028 static int gp2x_old_clock = 200;\r
1029 static int PsndRate_old = 0, PicoOpt_old = 0, PsndLen_real = 0, pal_old = 0;\r
1030 char fpsbuff[24]; // fps count c string\r
1031 struct timeval tval; // timing\r
1032 int thissec = 0, frames_done = 0, frames_shown = 0, oldmodes = 0;\r
1033 int target_fps, target_frametime, lim_time, i;\r
1034 char *notice = 0;\r
1035\r
1036 printf("entered emu_Loop()\n");\r
1037\r
1038 if (gp2x_old_clock != currentConfig.CPUclock) {\r
1039 printf("changing clock to %i...", currentConfig.CPUclock); fflush(stdout);\r
1040 set_FCLK(currentConfig.CPUclock);\r
1041 gp2x_old_clock = currentConfig.CPUclock;\r
1042 printf(" done\n");\r
1043 }\r
1044\r
1045 if (gp2x_old_gamma != currentConfig.gamma) {\r
1046 set_gamma(currentConfig.gamma);\r
1047 gp2x_old_gamma = currentConfig.gamma;\r
1048 printf("updated gamma to %i\n", currentConfig.gamma);\r
1049 }\r
1050\r
1051 fpsbuff[0] = 0;\r
1052\r
1053 // make sure we are in correct mode\r
1054 vidResetMode();\r
e5d315a5 1055 Pico.m.dirtyPal = 1;\r
720ee7f6 1056 oldmodes = ((Pico.video.reg[12]&1)<<2) ^ 0xc;\r
1057 find_combos();\r
1058\r
1059 // pal/ntsc might have changed, reset related stuff\r
1060 target_fps = Pico.m.pal ? 50 : 60;\r
1061 target_frametime = 1000000/target_fps;\r
1062 reset_timing = 1;\r
1063\r
1064 // prepare sound stuff\r
1065 if(currentConfig.EmuOpt & 4) {\r
3ef975c9 1066 int snd_excess_add;\r
720ee7f6 1067 if(PsndRate != PsndRate_old || (PicoOpt&0x20b) != (PicoOpt_old&0x20b) || Pico.m.pal != pal_old || crashed_940) {\r
1068 /* if 940 is turned off, we need it to be put back to sleep */\r
1069 if (!(PicoOpt&0x200) && ((PicoOpt^PicoOpt_old)&0x200)) {\r
8dfb9fd5 1070 Reset940(1, 2);\r
720ee7f6 1071 Pause940(1);\r
1072 }\r
3ef975c9 1073 sound_rerate(1);\r
720ee7f6 1074 }\r
1075 //excess_samples = PsndRate - PsndLen*target_fps;\r
720ee7f6 1076 snd_excess_add = ((PsndRate - PsndLen*target_fps)<<16) / target_fps;\r
1077 printf("starting audio: %i len: %i (ex: %04x) stereo: %i, pal: %i\n", PsndRate, PsndLen, snd_excess_add, (PicoOpt&8)>>3, Pico.m.pal);\r
1078 gp2x_start_sound(PsndRate, 16, (PicoOpt&8)>>3);\r
1079 gp2x_sound_volume(currentConfig.volume, currentConfig.volume);\r
1080 PicoWriteSound = updateSound;\r
3ef975c9 1081 memset(sndBuffer, 0, sizeof(sndBuffer));\r
1082 PsndOut = sndBuffer;\r
720ee7f6 1083 PsndRate_old = PsndRate;\r
1084 PsndLen_real = PsndLen;\r
1085 PicoOpt_old = PicoOpt;\r
1086 pal_old = Pico.m.pal;\r
1087 } else {\r
1088 PsndOut = 0;\r
1089 }\r
1090\r
5f9922e6 1091 // prepare CD buffer\r
1092 if (PicoMCD & 1) PicoCDBufferInit();\r
1093\r
720ee7f6 1094 // loop?\r
1095 while (engineState == PGS_Running)\r
1096 {\r
1097 int modes;\r
1098\r
1099 gettimeofday(&tval, 0);\r
1100 if(reset_timing) {\r
1101 reset_timing = 0;\r
1102 thissec = tval.tv_sec;\r
1103 frames_shown = frames_done = tval.tv_usec/target_frametime;\r
1104 }\r
1105\r
1106 // show notice message?\r
1107 if(noticeMsgTime.tv_sec) {\r
1108 static int noticeMsgSum;\r
1109 if((tval.tv_sec*1000000+tval.tv_usec) - (noticeMsgTime.tv_sec*1000000+noticeMsgTime.tv_usec) > 2000000) { // > 2.0 sec\r
1110 noticeMsgTime.tv_sec = noticeMsgTime.tv_usec = 0;\r
1111 clearArea(0);\r
1112 notice = 0;\r
1113 } else {\r
1114 int sum = noticeMsg[0]+noticeMsg[1]+noticeMsg[2];\r
1115 if (sum != noticeMsgSum) { clearArea(0); noticeMsgSum = sum; }\r
1116 notice = noticeMsg;\r
1117 }\r
1118 }\r
1119\r
1120 // check for mode changes\r
1121 modes = ((Pico.video.reg[12]&1)<<2)|(Pico.video.reg[1]&8);\r
1122 if (modes != oldmodes) {\r
1123 int scalex = 320;\r
1124 osd_fps_x = OSD_FPS_X;\r
1125 if (modes & 4) {\r
1126 vidCpyM2 = vidCpyM2_40col;\r
1127 } else {\r
1128 if (PicoOpt & 0x100) {\r
1129 vidCpyM2 = vidCpyM2_32col_nobord;\r
1130 scalex = 256;\r
1131 osd_fps_x = OSD_FPS_X - 64;\r
1132 } else {\r
1133 vidCpyM2 = vidCpyM2_32col;\r
1134 }\r
1135 }\r
1136 gp2x_video_RGB_setscaling(scalex, 240);\r
1137 oldmodes = modes;\r
1138 clearArea(1);\r
1139 }\r
1140\r
1141 // second changed?\r
1142 if(thissec != tval.tv_sec) {\r
1143#ifdef BENCHMARK\r
1144 static int bench = 0, bench_fps = 0, bench_fps_s = 0, bfp = 0, bf[4];\r
1145 if(++bench == 10) {\r
1146 bench = 0;\r
1147 bench_fps_s = bench_fps;\r
1148 bf[bfp++ & 3] = bench_fps;\r
1149 bench_fps = 0;\r
1150 }\r
1151 bench_fps += frames_shown;\r
1152 sprintf(fpsbuff, "%02i/%02i/%02i", frames_shown, bench_fps_s, (bf[0]+bf[1]+bf[2]+bf[3])>>2);\r
1153#else\r
1154 if(currentConfig.EmuOpt & 2)\r
1155 sprintf(fpsbuff, "%02i/%02i", frames_shown, frames_done);\r
1156#endif\r
1157 thissec = tval.tv_sec;\r
1158\r
1159 if(PsndOut == 0 && currentConfig.Frameskip >= 0) {\r
1160 frames_done = frames_shown = 0;\r
1161 } else {\r
1162 // it is quite common for this implementation to leave 1 fame unfinished\r
1163 // when second changes, but we don't want buffer to starve.\r
1164 if(PsndOut && frames_done < target_fps && frames_done > target_fps-5) {\r
1165 updateKeys();\r
d9a18943 1166 SkipFrame(1); frames_done++;\r
720ee7f6 1167 }\r
1168\r
1169 frames_done -= target_fps; if (frames_done < 0) frames_done = 0;\r
1170 frames_shown -= target_fps; if (frames_shown < 0) frames_shown = 0;\r
1171 if (frames_shown > frames_done) frames_shown = frames_done;\r
1172 }\r
1173 }\r
e5589947 1174#if 0\r
1175 sprintf(fpsbuff, "%05i", Pico.m.frame_count);\r
1176#endif\r
720ee7f6 1177 lim_time = (frames_done+1) * target_frametime;\r
1178 if(currentConfig.Frameskip >= 0) { // frameskip enabled\r
1179 for(i = 0; i < currentConfig.Frameskip; i++) {\r
1180 updateKeys();\r
d9a18943 1181 SkipFrame(1); frames_done++;\r
720ee7f6 1182 if (PsndOut) { // do framelimitting if sound is enabled\r
1183 gettimeofday(&tval, 0);\r
1184 if(thissec != tval.tv_sec) tval.tv_usec+=1000000;\r
1185 if(tval.tv_usec < lim_time) { // we are too fast\r
1186 simpleWait(thissec, lim_time);\r
1187 }\r
1188 }\r
1189 lim_time += target_frametime;\r
1190 }\r
1191 } else if(tval.tv_usec > lim_time) { // auto frameskip\r
1192 // no time left for this frame - skip\r
d032c15a 1193 if (tval.tv_usec - lim_time >= 0x300000) {\r
1194 /* something caused a slowdown for us (disk access? cache flush?)\r
1195 * try to recover by resetting timing... */\r
1196 reset_timing = 1;\r
1197 continue;\r
1198 }\r
720ee7f6 1199 updateKeys();\r
8f1b51ef 1200 SkipFrame(tval.tv_usec < lim_time+target_frametime*2); frames_done++;\r
720ee7f6 1201 continue;\r
1202 }\r
1203\r
1204 updateKeys();\r
1205 PicoFrame();\r
1206\r
47f22a1f 1207#if 0\r
1208if (Pico.m.frame_count == 31563) {\r
1209 FILE *f;\r
1210 f = fopen("ram_p.bin", "wb");\r
1211 if (!f) { printf("!f\n"); exit(1); }\r
1212 fwrite(Pico.ram, 1, 0x10000, f);\r
1213 fclose(f);\r
1214 exit(0);\r
1215}\r
1216#endif\r
1a20dbc8 1217#if 0\r
1218 // debug\r
1219 {\r
47f22a1f 1220 #define BYTE unsigned char\r
1221 #define WORD unsigned short\r
1222 struct\r
1223 {\r
1224 BYTE IDLength; /* 00h Size of Image ID field */\r
1225 BYTE ColorMapType; /* 01h Color map type */\r
1226 BYTE ImageType; /* 02h Image type code */\r
1227 WORD CMapStart; /* 03h Color map origin */\r
1228 WORD CMapLength; /* 05h Color map length */\r
1229 BYTE CMapDepth; /* 07h Depth of color map entries */\r
1230 WORD XOffset; /* 08h X origin of image */\r
1231 WORD YOffset; /* 0Ah Y origin of image */\r
1232 WORD Width; /* 0Ch Width of image */\r
1233 WORD Height; /* 0Eh Height of image */\r
1234 BYTE PixelDepth; /* 10h Image pixel size */\r
1235 BYTE ImageDescriptor; /* 11h Image descriptor byte */\r
1236 } __attribute__((packed)) TGAHEAD;\r
1237 static unsigned short oldscr[320*240];\r
1a20dbc8 1238 FILE *f; char name[128]; int i;\r
47f22a1f 1239\r
1240 memset(&TGAHEAD, 0, sizeof(TGAHEAD));\r
1241 TGAHEAD.ImageType = 2;\r
1242 TGAHEAD.Width = 320;\r
1243 TGAHEAD.Height = 240;\r
1244 TGAHEAD.PixelDepth = 16;\r
1245 TGAHEAD.ImageDescriptor = 2<<4; // image starts at top-left\r
1246\r
1247 #define CONV(X) (((X>>1)&0x7fe0)|(X&0x1f)) // 555?\r
1248\r
1249 for (i = 0; i < 320*240; i++)\r
1250 if(oldscr[i] != CONV(((unsigned short *)gp2x_screen)[i])) break;\r
1251 if (i < 320*240)\r
1a20dbc8 1252 {\r
47f22a1f 1253 for (i = 0; i < 320*240; i++)\r
1254 oldscr[i] = CONV(((unsigned short *)gp2x_screen)[i]);\r
1255 sprintf(name, "%05i.tga", Pico.m.frame_count);\r
1a20dbc8 1256 f = fopen(name, "wb");\r
1257 if (!f) { printf("!f\n"); exit(1); }\r
47f22a1f 1258 fwrite(&TGAHEAD, 1, sizeof(TGAHEAD), f);\r
1259 fwrite(oldscr, 1, 320*240*2, f);\r
1a20dbc8 1260 fclose(f);\r
1261 }\r
1262 }\r
1263#endif\r
1264\r
720ee7f6 1265 // check time\r
1266 gettimeofday(&tval, 0);\r
d032c15a 1267 if (thissec != tval.tv_sec) tval.tv_usec+=1000000;\r
720ee7f6 1268\r
d032c15a 1269 if (currentConfig.Frameskip < 0 && tval.tv_usec - lim_time >= 0x300000) // slowdown detection\r
1270 reset_timing = 1;\r
1271 else if (PsndOut != NULL || currentConfig.Frameskip < 0)\r
720ee7f6 1272 {\r
d032c15a 1273 // sleep if we are still too fast\r
720ee7f6 1274 // usleep sleeps for ~20ms minimum, so it is not a solution here\r
720ee7f6 1275 if(tval.tv_usec < lim_time)\r
1276 {\r
1277 // we are too fast\r
1278 simpleWait(thissec, lim_time);\r
1279 }\r
1280 }\r
1281\r
1282 blit(fpsbuff, notice);\r
1283\r
1284 frames_done++; frames_shown++;\r
1285 }\r
1286\r
5f9922e6 1287\r
1288 if (PicoMCD & 1) PicoCDBufferFree();\r
1289\r
720ee7f6 1290 // save SRAM\r
1291 if((currentConfig.EmuOpt & 1) && SRam.changed) {\r
0805b5b4 1292 osd_text(4, 232, "Writing SRAM/BRAM..");\r
720ee7f6 1293 emu_SaveLoadGame(0, 1);\r
1294 SRam.changed = 0;\r
1295 }\r
e5d315a5 1296\r
1297 // if in 16bit mode, generate 8it image for menu background\r
4ffd2858 1298 if (!(PicoOpt&0x10) && (currentConfig.EmuOpt&0x80))\r
1299 emu_forced_frame();\r
0805b5b4 1300\r
1301 // for menu bg\r
4ffd2858 1302 gp2x_memcpy_buffers((1<<2), gp2x_screen, 0, 320*240*2);\r
720ee7f6 1303}\r
1304\r
1305\r
1306void emu_ResetGame(void)\r
1307{\r
1308 PicoReset(0);\r
1309 reset_timing = 1;\r
1310}\r
1311\r
1312\r
1313size_t gzRead2(void *p, size_t _size, size_t _n, void *file)\r
1314{\r
1315 return gzread(file, p, _n);\r
1316}\r
1317\r
1318\r
1319size_t gzWrite2(void *p, size_t _size, size_t _n, void *file)\r
1320{\r
1321 return gzwrite(file, p, _n);\r
1322}\r
1323\r
4ffd2858 1324static int try_ropen_file(const char *fname)\r
1325{\r
1326 FILE *f;\r
1327\r
1328 f = fopen(fname, "rb");\r
1329 if (f) {\r
1330 fclose(f);\r
1331 return 1;\r
1332 }\r
1333 return 0;\r
1334}\r
1335\r
1336char *emu_GetSaveFName(int load, int is_sram, int slot)\r
1337{\r
1338 static char saveFname[512];\r
1339 char ext[16];\r
1340\r
1341 if (is_sram)\r
1342 {\r
1343 romfname_ext(saveFname, (PicoMCD&1) ? "brm/" : "srm/", (PicoMCD&1) ? ".brm" : ".srm");\r
1344 if (load) {\r
1345 if (try_ropen_file(saveFname)) return saveFname;\r
1346 // try in current dir..\r
1347 romfname_ext(saveFname, NULL, (PicoMCD&1) ? ".brm" : ".srm");\r
1348 if (try_ropen_file(saveFname)) return saveFname;\r
1349 return NULL; // give up\r
1350 }\r
1351 }\r
1352 else\r
1353 {\r
1354 ext[0] = 0;\r
1355 if(slot > 0 && slot < 10) sprintf(ext, ".%i", slot);\r
1356 strcat(ext, (currentConfig.EmuOpt & 8) ? ".mds.gz" : ".mds");\r
1357\r
1358 romfname_ext(saveFname, "mds/", ext);\r
1359 if (load) {\r
1360 if (try_ropen_file(saveFname)) return saveFname;\r
1361 romfname_ext(saveFname, NULL, ext);\r
1362 if (try_ropen_file(saveFname)) return saveFname;\r
1363 if (currentConfig.EmuOpt & 8) {\r
1364 ext[0] = 0;\r
1365 if(slot > 0 && slot < 10) sprintf(ext, ".%i", slot);\r
1366 strcat(ext, ".mds");\r
1367\r
1368 romfname_ext(saveFname, "mds/", ext);\r
1369 if (try_ropen_file(saveFname)) return saveFname;\r
1370 romfname_ext(saveFname, NULL, ext);\r
1371 if (try_ropen_file(saveFname)) return saveFname;\r
1372 }\r
1373 return NULL;\r
1374 }\r
1375 }\r
1376\r
1377 return saveFname;\r
1378}\r
1379\r
1380int emu_check_save_file(int slot)\r
1381{\r
1382 return emu_GetSaveFName(1, 0, slot) ? 1 : 0;\r
1383}\r
1384\r
1385void emu_set_save_cbs(int gz)\r
1386{\r
1387 if (gz) {\r
1388 areaRead = gzRead2;\r
1389 areaWrite = gzWrite2;\r
1390 areaEof = (areaeof *) gzeof;\r
1391 areaSeek = (areaseek *) gzseek;\r
1392 areaClose = (areaclose *) gzclose;\r
1393 } else {\r
1394 areaRead = (arearw *) fread;\r
1395 areaWrite = (arearw *) fwrite;\r
1396 areaEof = (areaeof *) feof;\r
1397 areaSeek = (areaseek *) fseek;\r
1398 areaClose = (areaclose *) fclose;\r
1399 }\r
1400}\r
720ee7f6 1401\r
1402int emu_SaveLoadGame(int load, int sram)\r
1403{\r
1404 int ret = 0;\r
4ffd2858 1405 char *saveFname;\r
720ee7f6 1406\r
1407 // make save filename\r
4ffd2858 1408 saveFname = emu_GetSaveFName(load, sram, state_slot);\r
1409 if (saveFname == NULL) {\r
dccc2bd0 1410 if (!sram) {\r
1411 strcpy(noticeMsg, load ? "LOAD FAILED (missing file)" : "SAVE FAILED ");\r
1412 gettimeofday(&noticeMsgTime, 0);\r
1413 }\r
4ffd2858 1414 return -1;\r
720ee7f6 1415 }\r
1416\r
1417 printf("saveLoad (%i, %i): %s\n", load, sram, saveFname);\r
1418\r
1419 if(sram) {\r
1420 FILE *sramFile;\r
15f38574 1421 int sram_size;\r
1422 unsigned char *sram_data;\r
1423 if (PicoMCD&1) {\r
1424 sram_size = 0x2000;\r
1425 sram_data = Pico_mcd->bram;\r
1426 } else {\r
1427 sram_size = SRam.end-SRam.start+1;\r
1428 if(SRam.reg_back & 4) sram_size=0x2000;\r
1429 sram_data = SRam.data;\r
1430 }\r
1431 if(!sram_data) return 0; // SRam forcefully disabled for this game\r
1432\r
720ee7f6 1433 if(load) {\r
1434 sramFile = fopen(saveFname, "rb");\r
1435 if(!sramFile) return -1;\r
15f38574 1436 fread(sram_data, 1, sram_size, sramFile);\r
720ee7f6 1437 fclose(sramFile);\r
1438 } else {\r
1439 // sram save needs some special processing\r
1440 // see if we have anything to save\r
1441 for(; sram_size > 0; sram_size--)\r
15f38574 1442 if(sram_data[sram_size-1]) break;\r
720ee7f6 1443\r
1444 if(sram_size) {\r
1445 sramFile = fopen(saveFname, "wb");\r
15f38574 1446 ret = fwrite(sram_data, 1, sram_size, sramFile);\r
720ee7f6 1447 ret = (ret != sram_size) ? -1 : 0;\r
1448 fclose(sramFile);\r
1449 sync();\r
1450 }\r
1451 }\r
1452 return ret;\r
15f38574 1453 }\r
1454 else\r
1455 {\r
720ee7f6 1456 void *PmovFile = NULL;\r
4ffd2858 1457 if (strcmp(saveFname + strlen(saveFname) - 3, ".gz") == 0) {\r
720ee7f6 1458 if( (PmovFile = gzopen(saveFname, load ? "rb" : "wb")) ) {\r
4ffd2858 1459 emu_set_save_cbs(1);\r
720ee7f6 1460 if(!load) gzsetparams(PmovFile, 9, Z_DEFAULT_STRATEGY);\r
4ffd2858 1461 }\r
720ee7f6 1462 }\r
4ffd2858 1463 else\r
1464 {\r
720ee7f6 1465 if( (PmovFile = fopen(saveFname, load ? "rb" : "wb")) ) {\r
4ffd2858 1466 emu_set_save_cbs(0);\r
720ee7f6 1467 }\r
1468 }\r
1469 if(PmovFile) {\r
98c9d8d9 1470 ret = PmovState(load ? 6 : 5, PmovFile);\r
4ffd2858 1471 areaClose(PmovFile);\r
720ee7f6 1472 PmovFile = 0;\r
1473 if (!load) sync();\r
1474 else Pico.m.dirtyPal=1;\r
98c9d8d9 1475 }\r
1476 else ret = -1;\r
1477 if (!ret)\r
1478 strcpy(noticeMsg, load ? "GAME LOADED " : "GAME SAVED ");\r
1479 else\r
1480 {\r
720ee7f6 1481 strcpy(noticeMsg, load ? "LOAD FAILED " : "SAVE FAILED ");\r
1482 ret = -1;\r
1483 }\r
1484\r
1485 gettimeofday(&noticeMsgTime, 0);\r
1486 return ret;\r
1487 }\r
1488}\r