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