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