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