bios CD player runs
[picodrive.git] / platform / gp2x / emu.c
CommitLineData
cc68a136 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
9#include <linux/limits.h>\r
10#include <ctype.h>\r
11#include <unistd.h>\r
12\r
13#include <stdarg.h>\r
14\r
15#include "emu.h"\r
16#include "gp2x.h"\r
17#include "usbjoy.h"\r
18#include "menu.h"\r
19#include "asmutils.h"\r
20#include "cpuctrl.h"\r
21\r
22#include "Pico/PicoInt.h"\r
23#include "zlib/zlib.h"\r
24\r
25\r
26#ifdef BENCHMARK\r
27#define OSD_FPS_X 220\r
28#else\r
29#define OSD_FPS_X 260\r
30#endif\r
31\r
32// PicoPad[] format: SACB RLDU\r
33char *actionNames[] = {\r
34 "UP", "DOWN", "LEFT", "RIGHT", "B", "C", "A", "START",\r
35 0, 0, 0, 0, 0, 0, 0, 0, // Z, Y, X, MODE (enabled only when needed), ?, ?, ?, ?\r
36 0, 0, 0, 0, 0, 0, 0, "ENTER MENU", // player2_flag, ?, ?, ?, ?, ?, ?, menu\r
37 "NEXT SAVE SLOT", "PREV SAVE SLOT", "SWITCH RENDERER", "SAVE STATE",\r
38 "LOAD STATE", "VOLUME UP", "VOLUME DOWN", "DONE"\r
39};\r
40\r
41int engineState;\r
42int select_exits = 0;\r
43char *PicoConfigFile = "picoconfig.bin";\r
44currentConfig_t currentConfig;\r
45\r
46char romFileName[PATH_MAX];\r
47unsigned char *rom_data = NULL;\r
48\r
49extern int crashed_940;\r
50\r
51static char noticeMsg[64]; // notice msg to draw\r
52static struct timeval noticeMsgTime = { 0, 0 }; // when started showing\r
53static int reset_timing, osd_fps_x;\r
54static int combo_keys = 0, combo_acts = 0; // keys and actions which need button combos\r
55static int gp2x_old_gamma = 100;\r
56static unsigned char *movie_data = NULL;\r
57static int movie_size = 0;\r
cc68a136 58unsigned char *framebuff = 0; // temporary buffer for alt renderer\r
59int state_slot = 0;\r
60\r
61/*\r
62// tmp\r
63static FILE *logf = NULL;\r
64\r
65void pprintf(char *texto, ...)\r
66{\r
67 va_list args;\r
68\r
69 va_start(args,texto);\r
70 vfprintf(logf,texto,args);\r
71 va_end(args);\r
72 fflush(logf);\r
73 sync();\r
74}\r
75*/\r
76// utilities\r
77static void strlwr(char* string)\r
78{\r
79 while ( (*string++ = (char)tolower(*string)) );\r
80}\r
81\r
672ad671 82static int try_rfn_cut(void)\r
cc68a136 83{\r
84 FILE *tmp;\r
85 char *p;\r
86\r
672ad671 87 p = romFileName + strlen(romFileName) - 1;\r
88 for (; p > romFileName; p--)\r
89 if (*p == '.') break;\r
90 *p = 0;\r
cc68a136 91\r
92 if((tmp = fopen(romFileName, "rb"))) {\r
93 fclose(tmp);\r
94 return 1;\r
95 }\r
96 return 0;\r
97}\r
98\r
8c1952f0 99static void get_ext(char *ext)\r
100{\r
101 char *p;\r
102\r
103 p = romFileName + strlen(romFileName) - 4;\r
104 if (p < romFileName) p = romFileName;\r
105 strncpy(ext, p, 4);\r
106 ext[4] = 0;\r
107 strlwr(ext);\r
108}\r
109\r
cc68a136 110int emu_ReloadRom(void)\r
111{\r
112 unsigned int rom_size = 0;\r
8c1952f0 113 char ext[5];\r
cc68a136 114 FILE *rom;\r
115 int ret;\r
116\r
117 printf("emu_ReloadRom(%s)\n", romFileName);\r
118\r
8c1952f0 119 // detect wrong extensions\r
120 get_ext(ext);\r
cc68a136 121\r
122 if(!strcmp(ext, ".srm") || !strcmp(ext, "s.gz") || !strcmp(ext, ".mds")) { // s.gz ~ .mds.gz\r
123 sprintf(menuErrorMsg, "Not a ROM selected.");\r
124 return 0;\r
125 }\r
126\r
127 // check for movie file\r
128 if(movie_data) {\r
129 free(movie_data);\r
130 movie_data = 0;\r
131 }\r
132 if(!strcmp(ext, ".gmv")) {\r
133 // check for both gmv and rom\r
134 int dummy;\r
135 FILE *movie_file = fopen(romFileName, "rb");\r
136 if(!movie_file) {\r
137 sprintf(menuErrorMsg, "Failed to open movie.");\r
138 return 0;\r
139 }\r
140 fseek(movie_file, 0, SEEK_END);\r
141 movie_size = ftell(movie_file);\r
142 fseek(movie_file, 0, SEEK_SET);\r
143 if(movie_size < 64+3) {\r
144 sprintf(menuErrorMsg, "Invalid GMV file.");\r
145 fclose(movie_file);\r
146 return 0;\r
147 }\r
148 movie_data = malloc(movie_size);\r
149 if(movie_data == NULL) {\r
150 sprintf(menuErrorMsg, "low memory.");\r
151 fclose(movie_file);\r
152 return 0;\r
153 }\r
154 fread(movie_data, 1, movie_size, movie_file);\r
155 fclose(movie_file);\r
156 if (strncmp((char *)movie_data, "Gens Movie TEST", 15) != 0) {\r
157 sprintf(menuErrorMsg, "Invalid GMV file.");\r
158 return 0;\r
159 }\r
672ad671 160 dummy = try_rfn_cut() || try_rfn_cut();\r
cc68a136 161 if (!dummy) {\r
162 sprintf(menuErrorMsg, "Could't find a ROM for movie.");\r
163 return 0;\r
164 }\r
8c1952f0 165 get_ext(ext);\r
cc68a136 166 }\r
167\r
168 rom = fopen(romFileName, "rb");\r
169 if(!rom) {\r
170 sprintf(menuErrorMsg, "Failed to open rom.");\r
171 return 0;\r
172 }\r
173\r
174 if(rom_data) {\r
175 free(rom_data);\r
176 rom_data = 0;\r
177 rom_size = 0;\r
178 }\r
179\r
180 // zipfile support\r
181 if(!strcasecmp(ext, ".zip")) {\r
182 fclose(rom);\r
183 ret = CartLoadZip(romFileName, &rom_data, &rom_size);\r
184 if(ret) {\r
185 if (ret == 4) strcpy(menuErrorMsg, "No ROMs in zip found.");\r
186 else sprintf(menuErrorMsg, "Unzip failed with code %i", ret);\r
187 printf("%s\n", menuErrorMsg);\r
188 return 0;\r
189 }\r
190 } else {\r
191 if( (ret = PicoCartLoad(rom, &rom_data, &rom_size)) ) {\r
192 sprintf(menuErrorMsg, "PicoCartLoad() failed.");\r
193 printf("%s\n", menuErrorMsg);\r
194 fclose(rom);\r
195 return 0;\r
196 }\r
197 fclose(rom);\r
198 }\r
199\r
200 // detect wrong files (Pico crashes on very small files), also see if ROM EP is good\r
201 if(rom_size <= 0x200 || strncmp((char *)rom_data, "Pico", 4) == 0 ||\r
202 ((*(unsigned short *)(rom_data+4)<<16)|(*(unsigned short *)(rom_data+6))) >= (int)rom_size) {\r
203 if (rom_data) free(rom_data);\r
204 rom_data = 0;\r
205 sprintf(menuErrorMsg, "Not a ROM selected.");\r
206 return 0;\r
207 }\r
208\r
209 printf("PicoCartInsert(%p, %d);\n", rom_data, rom_size);\r
210 if(PicoCartInsert(rom_data, rom_size)) {\r
211 sprintf(menuErrorMsg, "Failed to load ROM.");\r
212 return 0;\r
213 }\r
214\r
215 // load config for this ROM\r
216 ret = emu_ReadConfig(1);\r
217 if (!ret)\r
218 emu_ReadConfig(0);\r
219\r
220 // emu_ReadConfig() might have messed currentConfig.lastRomFile\r
221 strncpy(currentConfig.lastRomFile, romFileName, sizeof(currentConfig.lastRomFile)-1);\r
222 currentConfig.lastRomFile[sizeof(currentConfig.lastRomFile)-1] = 0;\r
223\r
224 // additional movie stuff\r
225 if(movie_data) {\r
226 if(movie_data[0x14] == '6')\r
227 PicoOpt |= 0x20; // 6 button pad\r
228 else PicoOpt &= ~0x20;\r
4f672280 229 PicoOpt |= 0x40; // accurate timing\r
cc68a136 230 if(movie_data[0xF] >= 'A') {\r
4f672280 231 if(movie_data[0x16] & 0x80) {\r
232 PicoRegionOverride = 8;\r
233 } else {\r
234 PicoRegionOverride = 4;\r
235 }\r
236 PicoReset(0);\r
cc68a136 237 // TODO: bits 6 & 5\r
238 }\r
4f672280 239 movie_data[0x18+30] = 0;\r
240 sprintf(noticeMsg, "MOVIE: %s", (char *) &movie_data[0x18]);\r
cc68a136 241 }\r
242 else\r
243 {\r
244 if(Pico.m.pal) {\r
245 strcpy(noticeMsg, "PAL SYSTEM / 50 FPS");\r
246 } else {\r
247 strcpy(noticeMsg, "NTSC SYSTEM / 60 FPS");\r
248 }\r
249 }\r
250 gettimeofday(&noticeMsgTime, 0);\r
251\r
252 // load SRAM for this ROM\r
253 if(currentConfig.EmuOpt & 1)\r
254 emu_SaveLoadGame(1, 1);\r
255\r
312e9ce1 256 Pico.m.frame_count = 0;\r
cc68a136 257\r
258 return 1;\r
259}\r
260\r
261\r
262void emu_Init(void)\r
263{\r
264 // make temp buffer for alt renderer\r
265 framebuff = malloc((8+320)*(8+240+8));\r
266 if (!framebuff)\r
267 {\r
268 printf("framebuff == 0\n");\r
269 }\r
270\r
271 PicoInit();\r
272\r
273// logf = fopen("log.txt", "w");\r
274}\r
275\r
276\r
277static void romfname_ext(char *dst, char *ext)\r
278{\r
279 char *p;\r
280\r
281 // make save filename\r
282 for (p = romFileName+strlen(romFileName)-1; p >= romFileName && *p != '/'; p--); p++;\r
283 strncpy(dst, p, 511);\r
284 dst[511-8] = 0;\r
285 if(dst[strlen(dst)-4] == '.') dst[strlen(dst)-4] = 0;\r
286 strcat(dst, ext);\r
287}\r
288\r
289\r
290static void find_combos(void)\r
291{\r
292 int act, u;\r
293\r
294 // find out which keys and actions are combos\r
295 combo_keys = combo_acts = 0;\r
296 for (act = 0; act < 32; act++)\r
297 {\r
298 int keyc = 0;\r
299 if (act == 16) continue; // player2 flag\r
300 for (u = 0; u < 32; u++)\r
301 {\r
302 if (currentConfig.KeyBinds[u] & (1 << act)) keyc++;\r
303 }\r
304 if (keyc > 1)\r
305 {\r
306 // loop again and mark those keys and actions as combo\r
307 for (u = 0; u < 32; u++)\r
308 {\r
309 if (currentConfig.KeyBinds[u] & (1 << act)) {\r
310 combo_keys |= 1 << u;\r
311 combo_acts |= 1 << act;\r
312 }\r
313 }\r
314 }\r
315 }\r
316 // printf("combo keys/acts: %08x %08x\n", combo_keys, combo_acts);\r
317}\r
318\r
319\r
320int emu_ReadConfig(int game)\r
321{\r
322 FILE *f;\r
323 char cfg[512];\r
324 int bread = 0;\r
325\r
326 if (!game)\r
327 {\r
328 // set default config\r
329 memset(&currentConfig, 0, sizeof(currentConfig));\r
330 currentConfig.lastRomFile[0] = 0;\r
331 currentConfig.EmuOpt = 0x1f;\r
332 currentConfig.PicoOpt = 0x0f;\r
333 currentConfig.PsndRate = 22050;\r
334 currentConfig.PicoRegion = 0; // auto\r
335 currentConfig.Frameskip = -1; // auto\r
336 currentConfig.CPUclock = 200;\r
337 currentConfig.volume = 50;\r
338 currentConfig.KeyBinds[ 0] = 1<<0; // SACB RLDU\r
339 currentConfig.KeyBinds[ 4] = 1<<1;\r
340 currentConfig.KeyBinds[ 2] = 1<<2;\r
341 currentConfig.KeyBinds[ 6] = 1<<3;\r
342 currentConfig.KeyBinds[14] = 1<<4;\r
343 currentConfig.KeyBinds[13] = 1<<5;\r
344 currentConfig.KeyBinds[12] = 1<<6;\r
345 currentConfig.KeyBinds[ 8] = 1<<7;\r
346 currentConfig.KeyBinds[15] = 1<<26; // switch rend\r
347 currentConfig.KeyBinds[10] = 1<<27; // save state\r
348 currentConfig.KeyBinds[11] = 1<<28; // load state\r
349 currentConfig.KeyBinds[23] = 1<<29; // vol up\r
350 currentConfig.KeyBinds[22] = 1<<30; // vol down\r
351 currentConfig.gamma = 100;\r
352 strncpy(cfg, PicoConfigFile, 511);\r
353 cfg[511] = 0;\r
354 } else {\r
355 romfname_ext(cfg, ".pbcfg");\r
356 }\r
357\r
358 printf("emu_ReadConfig: %s ", cfg);\r
359 f = fopen(cfg, "rb");\r
360 if (f) {\r
361 bread = fread(&currentConfig, 1, sizeof(currentConfig), f);\r
362 fclose(f);\r
363 }\r
364 printf((bread == sizeof(currentConfig)) ? "(ok)\n" : "(failed)\n");\r
365\r
366 PicoOpt = currentConfig.PicoOpt;\r
367 PsndRate = currentConfig.PsndRate;\r
368 PicoRegionOverride = currentConfig.PicoRegion;\r
369 if (PicoOpt & 0x20) {\r
370 actionNames[ 8] = "Z"; actionNames[ 9] = "Y";\r
371 actionNames[10] = "X"; actionNames[11] = "MODE";\r
372 }\r
373 // some sanity checks\r
374 if (currentConfig.CPUclock < 1 || currentConfig.CPUclock > 4096) currentConfig.CPUclock = 200;\r
375 if (currentConfig.gamma < 10 || currentConfig.gamma > 300) currentConfig.gamma = 100;\r
376 // if volume keys are unbound, bind them to volume control\r
377 if (!currentConfig.KeyBinds[23] && !currentConfig.KeyBinds[22]) {\r
378 currentConfig.KeyBinds[23] = 1<<29; // vol up\r
379 currentConfig.KeyBinds[22] = 1<<30; // vol down\r
380 }\r
381\r
382 return (bread == sizeof(currentConfig));\r
383}\r
384\r
385\r
386int emu_WriteConfig(int game)\r
387{\r
388 FILE *f;\r
389 char cfg[512];\r
390 int bwrite = 0;\r
391\r
392 if (!game)\r
393 {\r
394 strncpy(cfg, PicoConfigFile, 511);\r
395 cfg[511] = 0;\r
396 } else {\r
397 romfname_ext(cfg, ".pbcfg");\r
398 }\r
399\r
400 printf("emu_WriteConfig: %s ", cfg);\r
401 f = fopen(cfg, "wb");\r
402 if (f) {\r
403 currentConfig.PicoOpt = PicoOpt;\r
404 currentConfig.PsndRate = PsndRate;\r
405 currentConfig.PicoRegion = PicoRegionOverride;\r
406 bwrite = fwrite(&currentConfig, 1, sizeof(currentConfig), f);\r
407 fflush(f);\r
408 fclose(f);\r
409 sync();\r
410 }\r
411 printf((bwrite == sizeof(currentConfig)) ? "(ok)\n" : "(failed)\n");\r
412\r
413 return (bwrite == sizeof(currentConfig));\r
414}\r
415\r
416\r
417void emu_Deinit(void)\r
418{\r
419 // save SRAM\r
420 if((currentConfig.EmuOpt & 1) && SRam.changed) {\r
421 emu_SaveLoadGame(0, 1);\r
422 SRam.changed = 0;\r
423 }\r
424\r
425 if (!(currentConfig.EmuOpt & 0x20))\r
426 emu_WriteConfig(0);\r
427 free(framebuff);\r
428\r
429 PicoExit();\r
430// fclose(logf);\r
431\r
432 // restore gamma\r
433 if (gp2x_old_gamma != 100)\r
434 set_gamma(100);\r
435}\r
436\r
437\r
438void osd_text(int x, int y, char *text)\r
439{\r
440 int len = strlen(text)*8;\r
441\r
442 if ((PicoOpt&0x10)||!(currentConfig.EmuOpt&0x80)) {\r
443 int *p, i, h, black, white;\r
444 if (PicoOpt&0x10) {\r
445 black = 0x40404040; white = 0x41;\r
446 } else {\r
447 black = 0xe0e0e0e0; white = 0xf0;\r
448 }\r
449 x &= ~3; // align x\r
450 len = (len+3) >> 2;\r
451 for (h = 0; h < 8; h++) {\r
452 p = (int *) ((unsigned char *) gp2x_screen+x+320*(y+h));\r
453 for (i = len; i; i--, p++) *p = black;\r
454 }\r
455 gp2x_text_out8_2(x, y, text, white);\r
456 } else {\r
457 int *p, i, h;\r
458 x &= ~1; // align x\r
459 len = (len+1) >> 1;\r
460 for (h = 0; h < 8; h++) {\r
461 p = (int *) ((unsigned short *) gp2x_screen+x+320*(y+h));\r
462 for (i = len; i; i--, p++) *p = (*p>>2)&0x39e7;\r
463 }\r
464 gp2x_text_out15(x, y, text);\r
465 }\r
466}\r
467\r
468static int EmuScan16(unsigned int num, void *sdata)\r
469{\r
470 if (!(Pico.video.reg[1]&8)) num += 8;\r
471 DrawLineDest = (unsigned short *) gp2x_screen + 320*(num+1);\r
472\r
473 return 0;\r
474}\r
475\r
476static int EmuScan8(unsigned int num, void *sdata)\r
477{\r
478 if (!(Pico.video.reg[1]&8)) num += 8;\r
479 DrawLineDest = (unsigned char *) gp2x_screen + 320*(num+1);\r
480\r
481 return 0;\r
482}\r
483\r
484static int localPal[0x100];\r
485static void (*vidCpyM2)(void *dest, void *src) = NULL;\r
486\r
487static void blit(char *fps, char *notice)\r
488{\r
489 if (PicoOpt&0x10) {\r
490 // 8bit fast renderer\r
491 if (Pico.m.dirtyPal) {\r
492 Pico.m.dirtyPal = 0;\r
493 vidConvCpyRGB32(localPal, Pico.cram, 0x40);\r
494 // feed new palette to our device\r
495 gp2x_video_setpalette(localPal, 0x40);\r
496 }\r
497 vidCpyM2((unsigned char *)gp2x_screen+320*8, framebuff+328*8);\r
498 } else if (!(currentConfig.EmuOpt&0x80)) {\r
499 // 8bit accurate renderer\r
500 if (Pico.m.dirtyPal) {\r
501 Pico.m.dirtyPal = 0;\r
502 if(Pico.video.reg[0xC]&8) { // shadow/hilight mode\r
503 vidConvCpyRGB32(localPal, Pico.cram, 0x40);\r
504 vidConvCpyRGB32sh(localPal+0x40, Pico.cram, 0x40);\r
505 vidConvCpyRGB32hi(localPal+0x80, Pico.cram, 0x40);\r
506 blockcpy(localPal+0xc0, localPal+0x40, 0x40*4);\r
507 localPal[0xe0] = 0x00000000; // reserved pixels for OSD\r
508 localPal[0xf0] = 0x00ffffff;\r
509 gp2x_video_setpalette(localPal, 0x100);\r
510 } else if (rendstatus & 0x20) { // mid-frame palette changes\r
511 vidConvCpyRGB32(localPal, Pico.cram, 0x40);\r
512 vidConvCpyRGB32(localPal+0x40, HighPal, 0x40);\r
513 vidConvCpyRGB32(localPal+0x80, HighPal+0x40, 0x40);\r
514 gp2x_video_setpalette(localPal, 0xc0);\r
515 } else {\r
516 vidConvCpyRGB32(localPal, Pico.cram, 0x40);\r
517 gp2x_video_setpalette(localPal, 0x40);\r
518 }\r
519 }\r
520 }\r
521\r
522 if (notice) osd_text(4, 232, notice);\r
523 if (currentConfig.EmuOpt & 2)\r
524 osd_text(osd_fps_x, 232, fps);\r
525\r
526 //gp2x_video_wait_vsync();\r
527 gp2x_video_flip();\r
528\r
529 if (!(PicoOpt&0x10)) {\r
530 if (!(Pico.video.reg[1]&8)) {\r
531 if (currentConfig.EmuOpt&0x80) {\r
532 DrawLineDest = (unsigned short *) gp2x_screen + 320*8;\r
533 } else {\r
534 DrawLineDest = (unsigned char *) gp2x_screen + 320*8;\r
535 }\r
536 } else {\r
537 DrawLineDest = gp2x_screen;\r
538 }\r
539 }\r
540}\r
541\r
542\r
543// clears whole screen or just the notice area (in all buffers)\r
544static void clearArea(int full)\r
545{\r
546 if (PicoOpt&0x10) {\r
547 // 8bit fast renderer\r
548 if (full) gp2x_memset_all_buffers(0, 0x40, 320*240);\r
549 else gp2x_memset_all_buffers(320*232, 0x40, 320*8);\r
550 } else if (currentConfig.EmuOpt&0x80) {\r
551 // 16bit accurate renderer\r
552 if (full) gp2x_memset_all_buffers(0, 0, 320*240*2);\r
553 else gp2x_memset_all_buffers(320*232*2, 0, 320*8*2);\r
554 } else {\r
555 // 8bit accurate renderer\r
556 if (full) gp2x_memset_all_buffers(0, 0xe0, 320*240);\r
557 else gp2x_memset_all_buffers(320*232, 0xe0, 320*8);\r
558 }\r
559}\r
560\r
561\r
562static void vidResetMode(void)\r
563{\r
564 if (PicoOpt&0x10) {\r
565 localPal[0x40] = 0;\r
566 localPal[0x41] = 0x00ffffff;\r
567 gp2x_video_changemode(8);\r
568 gp2x_video_setpalette(localPal, 0x42);\r
569 gp2x_memset_all_buffers(0, 0x40, 320*240);\r
570 gp2x_video_flip();\r
571 } else if (currentConfig.EmuOpt&0x80) {\r
572 gp2x_video_changemode(15);\r
573 PicoDrawSetColorFormat(1);\r
574 PicoScan = EmuScan16;\r
575 PicoScan(0, 0);\r
576 } else {\r
577 localPal[0xe0] = 0x00000000; // reserved pixels for OSD\r
578 localPal[0xf0] = 0x00ffffff;\r
579 gp2x_video_changemode(8);\r
580 gp2x_video_setpalette(localPal, 0x100);\r
581 gp2x_memset_all_buffers(0, 0xe0, 320*240);\r
582 gp2x_video_flip();\r
583 PicoDrawSetColorFormat(2);\r
584 PicoScan = EmuScan8;\r
585 PicoScan(0, 0);\r
586 }\r
587 Pico.m.dirtyPal = 1;\r
588 // reset scaling\r
589 gp2x_video_RGB_setscaling((PicoOpt&0x100)&&!(Pico.video.reg[12]&1) ? 256 : 320, 240);\r
590}\r
591\r
592\r
593static int check_save_file(void)\r
594{\r
595 char saveFname[512];\r
596 char ext[16];\r
597 FILE *f;\r
598\r
599 ext[0] = 0;\r
600 if(state_slot > 0 && state_slot < 10) sprintf(ext, ".%i", state_slot);\r
601 strcat(ext, ".mds");\r
602 if(currentConfig.EmuOpt & 8) strcat(ext, ".gz");\r
603\r
604 romfname_ext(saveFname, ext);\r
605 if ((f = fopen(saveFname, "rb"))) {\r
606 fclose(f);\r
607 return 1;\r
608 }\r
609 return 0;\r
610}\r
611\r
612static void RunEvents(unsigned int which)\r
613{\r
614 if(which & 0x1800) { // save or load (but not both)\r
615 int do_it = 1;\r
616 if (!(which & 0x1000) && (currentConfig.EmuOpt & 0x200) && check_save_file()) {\r
617 unsigned long keys;\r
618 blit("", "OVERWRITE SAVE? (Y=yes, X=no)");\r
619 while( !((keys = gp2x_joystick_read(1)) & (GP2X_X|GP2X_Y)) )\r
620 usleep(50*1024);\r
621 if (keys & GP2X_X) do_it = 0;\r
622 clearArea(0);\r
623 }\r
624 if (do_it) {\r
625 blit("", (which & 0x1000) ? "LOADING GAME" : "SAVING GAME");\r
8c1952f0 626 if(movie_data) {\r
627 }\r
cc68a136 628 emu_SaveLoadGame(which & 0x1000, 0);\r
629 }\r
630\r
631 reset_timing = 1;\r
632 }\r
633 if(which & 0x0400) { // switch renderer\r
634 if ( PicoOpt&0x10) { PicoOpt&=~0x10; currentConfig.EmuOpt |= 0x80; }\r
635 else if (!(currentConfig.EmuOpt&0x80)) PicoOpt|= 0x10;\r
636 else currentConfig.EmuOpt &= ~0x80;\r
637\r
638 vidResetMode();\r
639\r
640 if (PicoOpt&0x10) {\r
641 strcpy(noticeMsg, " 8bit fast renderer");\r
642 } else if (currentConfig.EmuOpt&0x80) {\r
643 strcpy(noticeMsg, "16bit accurate renderer");\r
644 } else {\r
645 strcpy(noticeMsg, " 8bit accurate renderer");\r
646 }\r
647\r
648 gettimeofday(&noticeMsgTime, 0);\r
649 }\r
650 if(which & 0x0300) {\r
651 if(which&0x0200) {\r
652 state_slot -= 1;\r
653 if(state_slot < 0) state_slot = 9;\r
654 } else {\r
655 state_slot += 1;\r
656 if(state_slot > 9) state_slot = 0;\r
657 }\r
658 sprintf(noticeMsg, "SAVE SLOT %i [%s]", state_slot, check_save_file() ? "USED" : "FREE");\r
659 gettimeofday(&noticeMsgTime, 0);\r
660 }\r
661 if(which & 0x0080) {\r
662 engineState = PGS_Menu;\r
663 }\r
664}\r
665\r
666\r
8c1952f0 667static void updateMovie(void)\r
668{\r
669 int offs = Pico.m.frame_count*3 + 0x40;\r
670 if (offs+3 > movie_size) {\r
671 free(movie_data);\r
672 movie_data = 0;\r
673 strcpy(noticeMsg, "END OF MOVIE.");\r
674 printf("END OF MOVIE.\n");\r
675 gettimeofday(&noticeMsgTime, 0);\r
676 } else {\r
677 // MXYZ SACB RLDU\r
678 PicoPad[0] = ~movie_data[offs] & 0x8f; // ! SCBA RLDU\r
679 if(!(movie_data[offs] & 0x10)) PicoPad[0] |= 0x40; // A\r
680 if(!(movie_data[offs] & 0x20)) PicoPad[0] |= 0x10; // B\r
681 if(!(movie_data[offs] & 0x40)) PicoPad[0] |= 0x20; // A\r
682 PicoPad[1] = ~movie_data[offs+1] & 0x8f; // ! SCBA RLDU\r
683 if(!(movie_data[offs+1] & 0x10)) PicoPad[1] |= 0x40; // A\r
684 if(!(movie_data[offs+1] & 0x20)) PicoPad[1] |= 0x10; // B\r
685 if(!(movie_data[offs+1] & 0x40)) PicoPad[1] |= 0x20; // A\r
686 PicoPad[0] |= (~movie_data[offs+2] & 0x0A) << 8; // ! MZYX\r
687 if(!(movie_data[offs+2] & 0x01)) PicoPad[0] |= 0x0400; // X\r
688 if(!(movie_data[offs+2] & 0x04)) PicoPad[0] |= 0x0100; // Z\r
689 PicoPad[1] |= (~movie_data[offs+2] & 0xA0) << 4; // ! MZYX\r
690 if(!(movie_data[offs+2] & 0x10)) PicoPad[1] |= 0x0400; // X\r
691 if(!(movie_data[offs+2] & 0x40)) PicoPad[1] |= 0x0100; // Z\r
692 }\r
693}\r
694\r
695\r
cc68a136 696static void updateKeys(void)\r
697{\r
698 unsigned long keys, allActions[2] = { 0, 0 }, events;\r
699 static unsigned long prevEvents = 0;\r
700 int joy, i;\r
701\r
702 keys = gp2x_joystick_read(0);\r
703 if (keys & GP2X_SELECT) {\r
704 engineState = select_exits ? PGS_Quit : PGS_Menu;\r
705 // wait until select is released, so menu would not resume game\r
706 while (gp2x_joystick_read(1) & GP2X_SELECT) usleep(50*1000);\r
707 }\r
708\r
709 keys &= CONFIGURABLE_KEYS;\r
710\r
711 for (i = 0; i < 32; i++)\r
712 {\r
713 if (keys & (1 << i)) {\r
714 int pl, acts = currentConfig.KeyBinds[i];\r
715 if (!acts) continue;\r
716 pl = (acts >> 16) & 1;\r
717 if (combo_keys & (1 << i)) {\r
718 int u = i+1, acts_c = acts & combo_acts;\r
719 // let's try to find the other one\r
720 if (acts_c)\r
721 for (; u < 32; u++)\r
722 if ( (currentConfig.KeyBinds[u] & acts_c) && (keys & (1 << u)) ) {\r
723 allActions[pl] |= acts_c;\r
724 keys &= ~((1 << i) | (1 << u));\r
725 break;\r
726 }\r
727 // add non-combo actions if combo ones were not found\r
728 if (!acts_c || u == 32)\r
729 allActions[pl] |= acts & ~combo_acts;\r
730 } else {\r
731 allActions[pl] |= acts;\r
732 }\r
733 }\r
734 }\r
735\r
736 // add joy inputs\r
737 if (num_of_joys > 0)\r
738 {\r
739 gp2x_usbjoy_update();\r
740 for (joy = 0; joy < num_of_joys; joy++) {\r
741 int keys = gp2x_usbjoy_check2(joy);\r
742 for (i = 0; i < 32; i++) {\r
743 if (keys & (1 << i)) {\r
744 int acts = currentConfig.JoyBinds[joy][i];\r
745 int pl = (acts >> 16) & 1;\r
746 allActions[pl] |= acts;\r
747 }\r
748 }\r
749 }\r
750 }\r
751\r
8c1952f0 752 PicoPad[0] = (unsigned short) allActions[0];\r
753 PicoPad[1] = (unsigned short) allActions[1];\r
cc68a136 754\r
755 events = (allActions[0] | allActions[1]) >> 16;\r
756\r
757 // volume is treated in special way and triggered every frame\r
758 if(events & 0x6000) {\r
759 int vol = currentConfig.volume;\r
760 if (events & 0x2000) {\r
761 if (vol < 90) vol++;\r
762 } else {\r
763 if (vol > 0) vol--;\r
764 }\r
765 gp2x_sound_volume(vol, vol);\r
766 sprintf(noticeMsg, "VOL: %02i", vol);\r
767 gettimeofday(&noticeMsgTime, 0);\r
768 currentConfig.volume = vol;\r
769 }\r
770\r
771 events &= ~prevEvents;\r
772 if (events) RunEvents(events);\r
8c1952f0 773 if (movie_data) updateMovie();\r
cc68a136 774\r
775 prevEvents = (allActions[0] | allActions[1]) >> 16;\r
776}\r
777\r
778static int snd_excess_add = 0, snd_excess_cnt = 0; // hack\r
779\r
780static void updateSound(void)\r
781{\r
782 int len = (PicoOpt&8)?PsndLen*2:PsndLen;\r
783\r
784 snd_excess_cnt += snd_excess_add;\r
785 if (snd_excess_cnt >= 0x10000) {\r
786 snd_excess_cnt -= 0x10000;\r
787 if (PicoOpt&8) {\r
788 PsndOut[len] = PsndOut[len-2];\r
789 PsndOut[len+1] = PsndOut[len-1];\r
790 len+=2;\r
791 } else {\r
792 PsndOut[len] = PsndOut[len-1];\r
793 len++;\r
794 }\r
795 }\r
796\r
797 gp2x_sound_write(PsndOut, len<<1);\r
798}\r
799\r
800\r
801static void SkipFrame(int do_sound)\r
802{\r
803 void *sndbuff_tmp = 0;\r
804 if (PsndOut && !do_sound) {\r
805 sndbuff_tmp = PsndOut;\r
806 PsndOut = 0;\r
807 }\r
808\r
809 PicoSkipFrame=1;\r
810 PicoFrame();\r
811 PicoSkipFrame=0;\r
812\r
813 if (sndbuff_tmp && !do_sound) {\r
814 PsndOut = sndbuff_tmp;\r
815 }\r
816}\r
817\r
818\r
819static void simpleWait(int thissec, int lim_time)\r
820{\r
821 struct timeval tval;\r
822\r
823 spend_cycles(1024);\r
824 gettimeofday(&tval, 0);\r
825 if(thissec != tval.tv_sec) tval.tv_usec+=1000000;\r
826\r
827 while(tval.tv_usec < lim_time)\r
828 {\r
829 spend_cycles(1024);\r
830 gettimeofday(&tval, 0);\r
831 if(thissec != tval.tv_sec) tval.tv_usec+=1000000;\r
832 }\r
833}\r
834\r
835\r
836void emu_Loop(void)\r
837{\r
838 static int gp2x_old_clock = 200;\r
839 static int PsndRate_old = 0, PicoOpt_old = 0, PsndLen_real = 0, pal_old = 0;\r
840 char fpsbuff[24]; // fps count c string\r
841 struct timeval tval; // timing\r
842 int thissec = 0, frames_done = 0, frames_shown = 0, oldmodes = 0;\r
843 int target_fps, target_frametime, lim_time, i;\r
844 char *notice = 0;\r
845\r
846 printf("entered emu_Loop()\n");\r
847\r
848 if (gp2x_old_clock != currentConfig.CPUclock) {\r
849 printf("changing clock to %i...", currentConfig.CPUclock); fflush(stdout);\r
850 set_FCLK(currentConfig.CPUclock);\r
851 gp2x_old_clock = currentConfig.CPUclock;\r
852 printf(" done\n");\r
853 }\r
854\r
855 if (gp2x_old_gamma != currentConfig.gamma) {\r
856 set_gamma(currentConfig.gamma);\r
857 gp2x_old_gamma = currentConfig.gamma;\r
858 printf("updated gamma to %i\n", currentConfig.gamma);\r
859 }\r
860\r
861 fpsbuff[0] = 0;\r
862\r
863 // make sure we are in correct mode\r
864 vidResetMode();\r
865 oldmodes = ((Pico.video.reg[12]&1)<<2) ^ 0xc;\r
866 find_combos();\r
867\r
868 // pal/ntsc might have changed, reset related stuff\r
869 target_fps = Pico.m.pal ? 50 : 60;\r
870 target_frametime = 1000000/target_fps;\r
871 reset_timing = 1;\r
872\r
873 // prepare sound stuff\r
874 if(currentConfig.EmuOpt & 4) {\r
875 if(PsndRate != PsndRate_old || (PicoOpt&0x20b) != (PicoOpt_old&0x20b) || Pico.m.pal != pal_old || crashed_940) {\r
876 /* if 940 is turned off, we need it to be put back to sleep */\r
877 if (!(PicoOpt&0x200) && ((PicoOpt^PicoOpt_old)&0x200)) {\r
878 Reset940(1);\r
879 Pause940(1);\r
880 }\r
881 sound_rerate();\r
882 }\r
883 //excess_samples = PsndRate - PsndLen*target_fps;\r
884 snd_excess_cnt = 0;\r
885 snd_excess_add = ((PsndRate - PsndLen*target_fps)<<16) / target_fps;\r
886 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
887 gp2x_start_sound(PsndRate, 16, (PicoOpt&8)>>3);\r
888 gp2x_sound_volume(currentConfig.volume, currentConfig.volume);\r
889 PicoWriteSound = updateSound;\r
890 PsndOut = calloc((PicoOpt&8) ? (PsndLen*4+4) : (PsndLen*2+2), 1);\r
891 PsndRate_old = PsndRate;\r
892 PsndLen_real = PsndLen;\r
893 PicoOpt_old = PicoOpt;\r
894 pal_old = Pico.m.pal;\r
895 } else {\r
896 PsndOut = 0;\r
897 }\r
898\r
899 // loop?\r
900 while (engineState == PGS_Running)\r
901 {\r
902 int modes;\r
903\r
904 gettimeofday(&tval, 0);\r
905 if(reset_timing) {\r
906 reset_timing = 0;\r
907 thissec = tval.tv_sec;\r
908 frames_shown = frames_done = tval.tv_usec/target_frametime;\r
909 }\r
910\r
911 // show notice message?\r
912 if(noticeMsgTime.tv_sec) {\r
913 static int noticeMsgSum;\r
914 if((tval.tv_sec*1000000+tval.tv_usec) - (noticeMsgTime.tv_sec*1000000+noticeMsgTime.tv_usec) > 2000000) { // > 2.0 sec\r
915 noticeMsgTime.tv_sec = noticeMsgTime.tv_usec = 0;\r
916 clearArea(0);\r
917 notice = 0;\r
918 } else {\r
919 int sum = noticeMsg[0]+noticeMsg[1]+noticeMsg[2];\r
920 if (sum != noticeMsgSum) { clearArea(0); noticeMsgSum = sum; }\r
921 notice = noticeMsg;\r
922 }\r
923 }\r
924\r
925 // check for mode changes\r
926 modes = ((Pico.video.reg[12]&1)<<2)|(Pico.video.reg[1]&8);\r
927 if (modes != oldmodes) {\r
928 int scalex = 320;\r
929 osd_fps_x = OSD_FPS_X;\r
930 if (modes & 4) {\r
931 vidCpyM2 = vidCpyM2_40col;\r
932 } else {\r
933 if (PicoOpt & 0x100) {\r
934 vidCpyM2 = vidCpyM2_32col_nobord;\r
935 scalex = 256;\r
936 osd_fps_x = OSD_FPS_X - 64;\r
937 } else {\r
938 vidCpyM2 = vidCpyM2_32col;\r
939 }\r
940 }\r
941 gp2x_video_RGB_setscaling(scalex, 240);\r
942 oldmodes = modes;\r
943 clearArea(1);\r
944 }\r
945\r
946 // second changed?\r
947 if(thissec != tval.tv_sec) {\r
948#ifdef BENCHMARK\r
949 static int bench = 0, bench_fps = 0, bench_fps_s = 0, bfp = 0, bf[4];\r
950 if(++bench == 10) {\r
951 bench = 0;\r
952 bench_fps_s = bench_fps;\r
953 bf[bfp++ & 3] = bench_fps;\r
954 bench_fps = 0;\r
955 }\r
956 bench_fps += frames_shown;\r
957 sprintf(fpsbuff, "%02i/%02i/%02i", frames_shown, bench_fps_s, (bf[0]+bf[1]+bf[2]+bf[3])>>2);\r
958#else\r
959 if(currentConfig.EmuOpt & 2)\r
960 sprintf(fpsbuff, "%02i/%02i", frames_shown, frames_done);\r
961#endif\r
962 thissec = tval.tv_sec;\r
963\r
964 if(PsndOut == 0 && currentConfig.Frameskip >= 0) {\r
965 frames_done = frames_shown = 0;\r
966 } else {\r
967 // it is quite common for this implementation to leave 1 fame unfinished\r
968 // when second changes, but we don't want buffer to starve.\r
969 if(PsndOut && frames_done < target_fps && frames_done > target_fps-5) {\r
970 updateKeys();\r
971 SkipFrame(1); frames_done++;\r
972 }\r
973\r
974 frames_done -= target_fps; if (frames_done < 0) frames_done = 0;\r
975 frames_shown -= target_fps; if (frames_shown < 0) frames_shown = 0;\r
976 if (frames_shown > frames_done) frames_shown = frames_done;\r
977 }\r
978 }\r
979\r
980 lim_time = (frames_done+1) * target_frametime;\r
981 if(currentConfig.Frameskip >= 0) { // frameskip enabled\r
982 for(i = 0; i < currentConfig.Frameskip; i++) {\r
983 updateKeys();\r
984 SkipFrame(1); frames_done++;\r
985 if (PsndOut) { // do framelimitting if sound is enabled\r
986 gettimeofday(&tval, 0);\r
987 if(thissec != tval.tv_sec) tval.tv_usec+=1000000;\r
988 if(tval.tv_usec < lim_time) { // we are too fast\r
989 simpleWait(thissec, lim_time);\r
990 }\r
991 }\r
992 lim_time += target_frametime;\r
993 }\r
994 } else if(tval.tv_usec > lim_time) { // auto frameskip\r
995 // no time left for this frame - skip\r
996 updateKeys();\r
997 SkipFrame(tval.tv_usec < lim_time+target_frametime); frames_done++;\r
998 continue;\r
999 }\r
1000\r
1001 updateKeys();\r
1002 PicoFrame();\r
1003\r
312e9ce1 1004#if 0\r
1005if (Pico.m.frame_count == 31563) {\r
1006 FILE *f;\r
1007 f = fopen("ram_p.bin", "wb");\r
1008 if (!f) { printf("!f\n"); exit(1); }\r
1009 fwrite(Pico.ram, 1, 0x10000, f);\r
1010 fclose(f);\r
1011 exit(0);\r
1012}\r
1013#endif\r
4f672280 1014#if 0\r
1015 // debug\r
1016 {\r
312e9ce1 1017 #define BYTE unsigned char\r
1018 #define WORD unsigned short\r
1019 struct\r
1020 {\r
1021 BYTE IDLength; /* 00h Size of Image ID field */\r
1022 BYTE ColorMapType; /* 01h Color map type */\r
1023 BYTE ImageType; /* 02h Image type code */\r
1024 WORD CMapStart; /* 03h Color map origin */\r
1025 WORD CMapLength; /* 05h Color map length */\r
1026 BYTE CMapDepth; /* 07h Depth of color map entries */\r
1027 WORD XOffset; /* 08h X origin of image */\r
1028 WORD YOffset; /* 0Ah Y origin of image */\r
1029 WORD Width; /* 0Ch Width of image */\r
1030 WORD Height; /* 0Eh Height of image */\r
1031 BYTE PixelDepth; /* 10h Image pixel size */\r
1032 BYTE ImageDescriptor; /* 11h Image descriptor byte */\r
1033 } __attribute__((packed)) TGAHEAD;\r
1034 static unsigned short oldscr[320*240];\r
4f672280 1035 FILE *f; char name[128]; int i;\r
312e9ce1 1036\r
1037 memset(&TGAHEAD, 0, sizeof(TGAHEAD));\r
1038 TGAHEAD.ImageType = 2;\r
1039 TGAHEAD.Width = 320;\r
1040 TGAHEAD.Height = 240;\r
1041 TGAHEAD.PixelDepth = 16;\r
1042 TGAHEAD.ImageDescriptor = 2<<4; // image starts at top-left\r
1043\r
1044 #define CONV(X) (((X>>1)&0x7fe0)|(X&0x1f)) // 555?\r
1045\r
1046 for (i = 0; i < 320*240; i++)\r
1047 if(oldscr[i] != CONV(((unsigned short *)gp2x_screen)[i])) break;\r
1048 if (i < 320*240)\r
4f672280 1049 {\r
312e9ce1 1050 for (i = 0; i < 320*240; i++)\r
1051 oldscr[i] = CONV(((unsigned short *)gp2x_screen)[i]);\r
1052 sprintf(name, "%05i.tga", Pico.m.frame_count);\r
4f672280 1053 f = fopen(name, "wb");\r
1054 if (!f) { printf("!f\n"); exit(1); }\r
312e9ce1 1055 fwrite(&TGAHEAD, 1, sizeof(TGAHEAD), f);\r
1056 fwrite(oldscr, 1, 320*240*2, f);\r
4f672280 1057 fclose(f);\r
1058 }\r
1059 }\r
1060#endif\r
1061\r
cc68a136 1062 // check time\r
1063 gettimeofday(&tval, 0);\r
1064 if(thissec != tval.tv_sec) tval.tv_usec+=1000000;\r
1065\r
1066 // sleep if we are still too fast\r
1067 if(PsndOut != 0 || currentConfig.Frameskip < 0)\r
1068 {\r
1069 // usleep sleeps for ~20ms minimum, so it is not a solution here\r
1070 gettimeofday(&tval, 0);\r
1071 if(thissec != tval.tv_sec) tval.tv_usec+=1000000;\r
1072 if(tval.tv_usec < lim_time)\r
1073 {\r
1074 // we are too fast\r
1075 simpleWait(thissec, lim_time);\r
1076 }\r
1077 }\r
1078\r
1079 blit(fpsbuff, notice);\r
1080\r
1081 frames_done++; frames_shown++;\r
1082 }\r
1083\r
1084 // save SRAM\r
1085 if((currentConfig.EmuOpt & 1) && SRam.changed) {\r
1086 emu_SaveLoadGame(0, 1);\r
1087 SRam.changed = 0;\r
1088 }\r
1089\r
1090 if (PsndOut != 0) {\r
1091 free(PsndOut);\r
1092 PsndOut = 0;\r
1093 }\r
1094}\r
1095\r
1096\r
1097void emu_ResetGame(void)\r
1098{\r
1099 PicoReset(0);\r
1100 reset_timing = 1;\r
1101}\r
1102\r
1103\r
1104size_t gzRead2(void *p, size_t _size, size_t _n, void *file)\r
1105{\r
1106 return gzread(file, p, _n);\r
1107}\r
1108\r
1109\r
1110size_t gzWrite2(void *p, size_t _size, size_t _n, void *file)\r
1111{\r
1112 return gzwrite(file, p, _n);\r
1113}\r
1114\r
1115typedef unsigned int (*STATE_SL_FUNC)(void *, unsigned int, unsigned int, void *);\r
1116\r
1117int emu_SaveLoadGame(int load, int sram)\r
1118{\r
1119 int ret = 0;\r
1120 char saveFname[512];\r
1121\r
1122 // make save filename\r
1123 romfname_ext(saveFname, "");\r
1124 if(sram) strcat(saveFname, ".srm");\r
1125 else {\r
1126 if(state_slot > 0 && state_slot < 10) sprintf(saveFname, "%s.%i", saveFname, state_slot);\r
1127 strcat(saveFname, ".mds");\r
1128 }\r
1129\r
1130 printf("saveLoad (%i, %i): %s\n", load, sram, saveFname);\r
1131\r
1132 if(sram) {\r
1133 FILE *sramFile;\r
1134 int sram_size = SRam.end-SRam.start+1;\r
1135 if(SRam.reg_back & 4) sram_size=0x2000;\r
1136 if(!SRam.data) return 0; // SRam forcefully disabled for this game\r
1137 if(load) {\r
1138 sramFile = fopen(saveFname, "rb");\r
1139 if(!sramFile) return -1;\r
1140 fread(SRam.data, 1, sram_size, sramFile);\r
1141 fclose(sramFile);\r
1142 } else {\r
1143 // sram save needs some special processing\r
1144 // see if we have anything to save\r
1145 for(; sram_size > 0; sram_size--)\r
1146 if(SRam.data[sram_size-1]) break;\r
1147\r
1148 if(sram_size) {\r
1149 sramFile = fopen(saveFname, "wb");\r
1150 ret = fwrite(SRam.data, 1, sram_size, sramFile);\r
1151 ret = (ret != sram_size) ? -1 : 0;\r
1152 fclose(sramFile);\r
1153 sync();\r
1154 }\r
1155 }\r
1156 return ret;\r
1157 } else {\r
1158 void *PmovFile = NULL;\r
1159 // try gzip first\r
1160 if(currentConfig.EmuOpt & 8) {\r
1161 strcat(saveFname, ".gz");\r
1162 if( (PmovFile = gzopen(saveFname, load ? "rb" : "wb")) ) {\r
1163 areaRead = gzRead2;\r
1164 areaWrite = gzWrite2;\r
1165 if(!load) gzsetparams(PmovFile, 9, Z_DEFAULT_STRATEGY);\r
1166 } else\r
1167 saveFname[strlen(saveFname)-3] = 0;\r
1168 }\r
1169 if(!PmovFile) { // gzip failed or was disabled\r
1170 if( (PmovFile = fopen(saveFname, load ? "rb" : "wb")) ) {\r
1171 areaRead = (STATE_SL_FUNC) fread;\r
1172 areaWrite = (STATE_SL_FUNC) fwrite;\r
1173 }\r
1174 }\r
1175 if(PmovFile) {\r
1176 PmovState(load ? 6 : 5, PmovFile);\r
1177 strcpy(noticeMsg, load ? "GAME LOADED " : "GAME SAVED ");\r
1178 if(areaRead == gzRead2)\r
1179 gzclose(PmovFile);\r
1180 else fclose ((FILE *) PmovFile);\r
1181 PmovFile = 0;\r
1182 if (!load) sync();\r
1183 else Pico.m.dirtyPal=1;\r
1184 } else {\r
1185 strcpy(noticeMsg, load ? "LOAD FAILED " : "SAVE FAILED ");\r
1186 ret = -1;\r
1187 }\r
1188\r
1189 gettimeofday(&noticeMsgTime, 0);\r
1190 return ret;\r
1191 }\r
1192}\r