get rid of port_config.h
[picodrive.git] / platform / common / emu.c
1 /*\r
2  * PicoDrive\r
3  * (C) notaz, 2007-2010\r
4  *\r
5  * This work is licensed under the terms of MAME license.\r
6  * See COPYING file in the top-level directory.\r
7  */\r
8 \r
9 #include <stdio.h>\r
10 #include <stdlib.h>\r
11 #include <string.h>\r
12 #include <stdarg.h>\r
13 #ifdef __GP2X__\r
14 #include <unistd.h>\r
15 #endif\r
16 \r
17 #include "../libpicofe/posix.h"\r
18 #include "../libpicofe/input.h"\r
19 #include "../libpicofe/fonts.h"\r
20 #include "../libpicofe/sndout.h"\r
21 #include "../libpicofe/lprintf.h"\r
22 #include "../libpicofe/plat.h"\r
23 #include "emu.h"\r
24 #include "input_pico.h"\r
25 #include "menu_pico.h"\r
26 #include "config_file.h"\r
27 \r
28 #include <pico/pico_int.h>\r
29 #include <pico/patch.h>\r
30 \r
31 #ifndef _WIN32\r
32 #define PATH_SEP      "/"\r
33 #define PATH_SEP_C    '/'\r
34 #else\r
35 #define PATH_SEP      "\\"\r
36 #define PATH_SEP_C    '\\'\r
37 #endif\r
38 \r
39 #define STATUS_MSG_TIMEOUT 2000\r
40 \r
41 void *g_screen_ptr;\r
42 \r
43 int g_screen_width  = 320;\r
44 int g_screen_height = 240;\r
45 \r
46 char *PicoConfigFile = "config.cfg";\r
47 currentConfig_t currentConfig, defaultConfig;\r
48 int state_slot = 0;\r
49 int config_slot = 0, config_slot_current = 0;\r
50 int pico_pen_x = 320/2, pico_pen_y = 240/2;\r
51 int pico_inp_mode = 0;\r
52 int engineState = PGS_Menu;\r
53 \r
54 static short __attribute__((aligned(4))) sndBuffer[2*44100/50];\r
55 \r
56 /* tmp buff to reduce stack usage for plats with small stack */\r
57 static char static_buff[512];\r
58 const char *rom_fname_reload;\r
59 char rom_fname_loaded[512];\r
60 int reset_timing = 0;\r
61 static unsigned int notice_msg_time;    /* when started showing */\r
62 static char noticeMsg[40];\r
63 \r
64 unsigned char *movie_data = NULL;\r
65 static int movie_size = 0;\r
66 \r
67 \r
68 /* don't use tolower() for easy old glibc binary compatibility */\r
69 static void strlwr_(char *string)\r
70 {\r
71         char *p;\r
72         for (p = string; *p; p++)\r
73                 if ('A' <= *p && *p <= 'Z')\r
74                         *p += 'a' - 'A';\r
75 }\r
76 \r
77 static int try_rfn_cut(char *fname)\r
78 {\r
79         FILE *tmp;\r
80         char *p;\r
81 \r
82         p = fname + strlen(fname) - 1;\r
83         for (; p > fname; p--)\r
84                 if (*p == '.') break;\r
85         *p = 0;\r
86 \r
87         if((tmp = fopen(fname, "rb"))) {\r
88                 fclose(tmp);\r
89                 return 1;\r
90         }\r
91         return 0;\r
92 }\r
93 \r
94 static void get_ext(const char *file, char *ext)\r
95 {\r
96         const char *p;\r
97 \r
98         p = file + strlen(file) - 4;\r
99         if (p < file) p = file;\r
100         strncpy(ext, p, 4);\r
101         ext[4] = 0;\r
102         strlwr_(ext);\r
103 }\r
104 \r
105 static void fname_ext(char *dst, int dstlen, const char *prefix, const char *ext, const char *fname)\r
106 {\r
107         int prefix_len = 0;\r
108         const char *p;\r
109 \r
110         *dst = 0;\r
111         if (prefix) {\r
112                 int len = plat_get_root_dir(dst, dstlen);\r
113                 strcpy(dst + len, prefix);\r
114                 prefix_len = len + strlen(prefix);\r
115         }\r
116 \r
117         p = fname + strlen(fname) - 1;\r
118         for (; p >= fname && *p != PATH_SEP_C; p--)\r
119                 ;\r
120         p++;\r
121         strncpy(dst + prefix_len, p, dstlen - prefix_len - 1);\r
122 \r
123         dst[dstlen - 8] = 0;\r
124         if (dst[strlen(dst) - 4] == '.')\r
125                 dst[strlen(dst) - 4] = 0;\r
126         if (ext)\r
127                 strcat(dst, ext);\r
128 }\r
129 \r
130 static void romfname_ext(char *dst, int dstlen, const char *prefix, const char *ext)\r
131 {\r
132         fname_ext(dst, dstlen, prefix, ext, rom_fname_loaded);\r
133 }\r
134 \r
135 void emu_status_msg(const char *format, ...)\r
136 {\r
137         va_list vl;\r
138         int ret;\r
139 \r
140         va_start(vl, format);\r
141         ret = vsnprintf(noticeMsg, sizeof(noticeMsg), format, vl);\r
142         va_end(vl);\r
143 \r
144         /* be sure old text gets overwritten */\r
145         for (; ret < 28; ret++)\r
146                 noticeMsg[ret] = ' ';\r
147         noticeMsg[ret] = 0;\r
148 \r
149         notice_msg_time = plat_get_ticks_ms();\r
150 }\r
151 \r
152 static const char * const biosfiles_us[] = {\r
153         "us_scd1_9210", "us_scd2_9306", "SegaCDBIOS9303", "bios_CD_U"\r
154 };\r
155 static const char * const biosfiles_eu[] = {\r
156         "eu_mcd1_9210", "eu_mcd2_9306", "eu_mcd2_9303", "bios_CD_E"\r
157 };\r
158 static const char * const biosfiles_jp[] = {\r
159         "jp_mcd1_9112", "jp_mcd1_9111", "bios_CD_J"\r
160 };\r
161 \r
162 static const char *find_bios(int *region, const char *cd_fname)\r
163 {\r
164         int i, count;\r
165         const char * const *files;\r
166         FILE *f = NULL;\r
167         int ret;\r
168 \r
169         // we need to have config loaded at this point\r
170         ret = emu_read_config(cd_fname, 0);\r
171         if (!ret) emu_read_config(NULL, 0);\r
172 \r
173         if (PicoRegionOverride) {\r
174                 *region = PicoRegionOverride;\r
175                 lprintf("override region to %s\n", *region != 4 ?\r
176                         (*region == 8 ? "EU" : "JAP") : "USA");\r
177         }\r
178 \r
179         if (*region == 4) { // US\r
180                 files = biosfiles_us;\r
181                 count = sizeof(biosfiles_us) / sizeof(char *);\r
182         } else if (*region == 8) { // EU\r
183                 files = biosfiles_eu;\r
184                 count = sizeof(biosfiles_eu) / sizeof(char *);\r
185         } else if (*region == 1 || *region == 2) {\r
186                 files = biosfiles_jp;\r
187                 count = sizeof(biosfiles_jp) / sizeof(char *);\r
188         } else {\r
189                 return 0;\r
190         }\r
191 \r
192         for (i = 0; i < count; i++)\r
193         {\r
194                 emu_make_path(static_buff, files[i], sizeof(static_buff) - 4);\r
195                 strcat(static_buff, ".bin");\r
196                 f = fopen(static_buff, "rb");\r
197                 if (f) break;\r
198 \r
199                 static_buff[strlen(static_buff) - 4] = 0;\r
200                 strcat(static_buff, ".zip");\r
201                 f = fopen(static_buff, "rb");\r
202                 if (f) break;\r
203         }\r
204 \r
205         if (f) {\r
206                 lprintf("using bios: %s\n", static_buff);\r
207                 fclose(f);\r
208                 return static_buff;\r
209         } else {\r
210                 sprintf(static_buff, "no %s BIOS files found, read docs",\r
211                         *region != 4 ? (*region == 8 ? "EU" : "JAP") : "USA");\r
212                 menu_update_msg(static_buff);\r
213                 return NULL;\r
214         }\r
215 }\r
216 \r
217 /* check if the name begins with BIOS name */\r
218 /*\r
219 static int emu_isBios(const char *name)\r
220 {\r
221         int i;\r
222         for (i = 0; i < sizeof(biosfiles_us)/sizeof(biosfiles_us[0]); i++)\r
223                 if (strstr(name, biosfiles_us[i]) != NULL) return 1;\r
224         for (i = 0; i < sizeof(biosfiles_eu)/sizeof(biosfiles_eu[0]); i++)\r
225                 if (strstr(name, biosfiles_eu[i]) != NULL) return 1;\r
226         for (i = 0; i < sizeof(biosfiles_jp)/sizeof(biosfiles_jp[0]); i++)\r
227                 if (strstr(name, biosfiles_jp[i]) != NULL) return 1;\r
228         return 0;\r
229 }\r
230 */\r
231 \r
232 static int extract_text(char *dest, const unsigned char *src, int len, int swab)\r
233 {\r
234         char *p = dest;\r
235         int i;\r
236 \r
237         if (swab) swab = 1;\r
238 \r
239         for (i = len - 1; i >= 0; i--)\r
240         {\r
241                 if (src[i^swab] != ' ') break;\r
242         }\r
243         len = i + 1;\r
244 \r
245         for (i = 0; i < len; i++)\r
246         {\r
247                 unsigned char s = src[i^swab];\r
248                 if (s >= 0x20 && s < 0x7f && s != '#' && s != '|' &&\r
249                         s != '[' && s != ']' && s != '\\')\r
250                 {\r
251                         *p++ = s;\r
252                 }\r
253                 else\r
254                 {\r
255                         sprintf(p, "\\%02x", s);\r
256                         p += 3;\r
257                 }\r
258         }\r
259 \r
260         return p - dest;\r
261 }\r
262 \r
263 static char *emu_make_rom_id(const char *fname)\r
264 {\r
265         static char id_string[3+0xe*3+0x3*3+0x30*3+3];\r
266         int pos, swab = 1;\r
267 \r
268         if (PicoAHW & PAHW_MCD) {\r
269                 strcpy(id_string, "CD|");\r
270                 swab = 0;\r
271         }\r
272         else if (PicoAHW & PAHW_SMS)\r
273                 strcpy(id_string, "MS|");\r
274         else    strcpy(id_string, "MD|");\r
275         pos = 3;\r
276 \r
277         if (!(PicoAHW & PAHW_SMS)) {\r
278                 pos += extract_text(id_string + pos, media_id_header + 0x80, 0x0e, swab); // serial\r
279                 id_string[pos] = '|'; pos++;\r
280                 pos += extract_text(id_string + pos, media_id_header + 0xf0, 0x03, swab); // region\r
281                 id_string[pos] = '|'; pos++;\r
282                 pos += extract_text(id_string + pos, media_id_header + 0x50, 0x30, swab); // overseas name\r
283                 id_string[pos] = 0;\r
284                 if (pos > 5)\r
285                         return id_string;\r
286                 pos = 3;\r
287         }\r
288 \r
289         // can't find name in ROM, use filename\r
290         fname_ext(id_string + 3, sizeof(id_string) - 3, NULL, NULL, fname);\r
291 \r
292         return id_string;\r
293 }\r
294 \r
295 // buffer must be at least 150 byte long\r
296 void emu_get_game_name(char *str150)\r
297 {\r
298         int ret, swab = (PicoAHW & PAHW_MCD) ? 0 : 1;\r
299         char *s, *d;\r
300 \r
301         ret = extract_text(str150, media_id_header + 0x50, 0x30, swab); // overseas name\r
302 \r
303         for (s = d = str150 + 1; s < str150+ret; s++)\r
304         {\r
305                 if (*s == 0) break;\r
306                 if (*s != ' ' || d[-1] != ' ')\r
307                         *d++ = *s;\r
308         }\r
309         *d = 0;\r
310 }\r
311 \r
312 static void system_announce(void)\r
313 {\r
314         const char *sys_name, *tv_standard, *extra = "";\r
315         int fps;\r
316 \r
317         if (PicoAHW & PAHW_SMS) {\r
318                 sys_name = "Master System";\r
319 #ifdef NO_SMS\r
320                 extra = " [no support]";\r
321 #endif\r
322         } else if (PicoAHW & PAHW_PICO) {\r
323                 sys_name = "Pico";\r
324         } else if (PicoAHW & PAHW_MCD) {\r
325                 sys_name = "Mega CD";\r
326                 if ((Pico.m.hardware & 0xc0) == 0x80)\r
327                         sys_name = "Sega CD";\r
328         } else if (PicoAHW & PAHW_32X) {\r
329                 sys_name = "32X";\r
330         } else {\r
331                 sys_name = "MegaDrive";\r
332                 if ((Pico.m.hardware & 0xc0) == 0x80)\r
333                         sys_name = "Genesis";\r
334         }\r
335         tv_standard = Pico.m.pal ? "PAL" : "NTSC";\r
336         fps = Pico.m.pal ? 50 : 60;\r
337 \r
338         emu_status_msg("%s %s / %dFPS%s", tv_standard, sys_name, fps, extra);\r
339 }\r
340 \r
341 static void do_region_override(const char *media_fname)\r
342 {\r
343         // we only need to override region if config tells us so\r
344         int ret = emu_read_config(media_fname, 0);\r
345         if (!ret) emu_read_config(NULL, 0);\r
346 }\r
347 \r
348 int emu_reload_rom(const char *rom_fname_in)\r
349 {\r
350         char *rom_fname = NULL;\r
351         char ext[5];\r
352         enum media_type_e media_type;\r
353         int menu_romload_started = 0;\r
354         char carthw_path[512];\r
355         int retval = 0;\r
356 \r
357         lprintf("emu_ReloadRom(%s)\n", rom_fname_in);\r
358 \r
359         rom_fname = strdup(rom_fname_in);\r
360         if (rom_fname == NULL)\r
361                 return 0;\r
362 \r
363         get_ext(rom_fname, ext);\r
364 \r
365         // early cleanup\r
366         PicoPatchUnload();\r
367         if (movie_data) {\r
368                 free(movie_data);\r
369                 movie_data = 0;\r
370         }\r
371 \r
372         if (!strcmp(ext, ".gmv"))\r
373         {\r
374                 // check for both gmv and rom\r
375                 int dummy;\r
376                 FILE *movie_file = fopen(rom_fname, "rb");\r
377                 if (!movie_file) {\r
378                         menu_update_msg("Failed to open movie.");\r
379                         goto out;\r
380                 }\r
381                 fseek(movie_file, 0, SEEK_END);\r
382                 movie_size = ftell(movie_file);\r
383                 fseek(movie_file, 0, SEEK_SET);\r
384                 if (movie_size < 64+3) {\r
385                         menu_update_msg("Invalid GMV file.");\r
386                         fclose(movie_file);\r
387                         goto out;\r
388                 }\r
389                 movie_data = malloc(movie_size);\r
390                 if (movie_data == NULL) {\r
391                         menu_update_msg("low memory.");\r
392                         fclose(movie_file);\r
393                         goto out;\r
394                 }\r
395                 dummy = fread(movie_data, 1, movie_size, movie_file);\r
396                 fclose(movie_file);\r
397                 if (strncmp((char *)movie_data, "Gens Movie TEST", 15) != 0) {\r
398                         menu_update_msg("Invalid GMV file.");\r
399                         goto out;\r
400                 }\r
401                 dummy = try_rfn_cut(rom_fname) || try_rfn_cut(rom_fname);\r
402                 if (!dummy) {\r
403                         menu_update_msg("Could't find a ROM for movie.");\r
404                         goto out;\r
405                 }\r
406                 get_ext(rom_fname, ext);\r
407                 lprintf("gmv loaded for %s\n", rom_fname);\r
408         }\r
409         else if (!strcmp(ext, ".pat"))\r
410         {\r
411                 int dummy;\r
412                 PicoPatchLoad(rom_fname);\r
413                 dummy = try_rfn_cut(rom_fname) || try_rfn_cut(rom_fname);\r
414                 if (!dummy) {\r
415                         menu_update_msg("Could't find a ROM to patch.");\r
416                         goto out;\r
417                 }\r
418                 get_ext(rom_fname, ext);\r
419         }\r
420 \r
421         menu_romload_prepare(rom_fname); // also CD load\r
422         menu_romload_started = 1;\r
423 \r
424         emu_make_path(carthw_path, "carthw.cfg", sizeof(carthw_path));\r
425 \r
426         media_type = PicoLoadMedia(rom_fname, carthw_path,\r
427                         find_bios, do_region_override);\r
428 \r
429         switch (media_type) {\r
430         case PM_BAD_DETECT:\r
431                 menu_update_msg("Not a ROM/CD img selected.");\r
432                 goto out;\r
433         case PM_BAD_CD:\r
434                 menu_update_msg("Invalid CD image");\r
435                 goto out;\r
436         case PM_BAD_CD_NO_BIOS:\r
437                 // find_bios() prints a message\r
438                 goto out;\r
439         case PM_ERROR:\r
440                 menu_update_msg("Load error");\r
441                 goto out;\r
442         default:\r
443                 break;\r
444         }\r
445 \r
446         menu_romload_end();\r
447         menu_romload_started = 0;\r
448 \r
449         if (PicoPatches) {\r
450                 PicoPatchPrepare();\r
451                 PicoPatchApply();\r
452         }\r
453 \r
454         // additional movie stuff\r
455         if (movie_data)\r
456         {\r
457                 if (movie_data[0x14] == '6')\r
458                      PicoOpt |=  POPT_6BTN_PAD; // 6 button pad\r
459                 else PicoOpt &= ~POPT_6BTN_PAD;\r
460                 PicoOpt |= POPT_DIS_VDP_FIFO; // no VDP fifo timing\r
461                 if (movie_data[0xF] >= 'A') {\r
462                         if (movie_data[0x16] & 0x80) {\r
463                                 PicoRegionOverride = 8;\r
464                         } else {\r
465                                 PicoRegionOverride = 4;\r
466                         }\r
467                         PicoReset();\r
468                         // TODO: bits 6 & 5\r
469                 }\r
470                 movie_data[0x18+30] = 0;\r
471                 emu_status_msg("MOVIE: %s", (char *) &movie_data[0x18]);\r
472         }\r
473         else\r
474         {\r
475                 system_announce();\r
476                 PicoOpt &= ~POPT_DIS_VDP_FIFO;\r
477         }\r
478 \r
479         strncpy(rom_fname_loaded, rom_fname, sizeof(rom_fname_loaded)-1);\r
480         rom_fname_loaded[sizeof(rom_fname_loaded)-1] = 0;\r
481 \r
482         // load SRAM for this ROM\r
483         if (currentConfig.EmuOpt & EOPT_EN_SRAM)\r
484                 emu_save_load_game(1, 1);\r
485 \r
486         retval = 1;\r
487 out:\r
488         if (menu_romload_started)\r
489                 menu_romload_end();\r
490         free(rom_fname);\r
491         return retval;\r
492 }\r
493 \r
494 int emu_swap_cd(const char *fname)\r
495 {\r
496         cd_img_type cd_type;\r
497         int ret = -1;\r
498 \r
499         cd_type = PicoCdCheck(fname, NULL);\r
500         if (cd_type != CIT_NOT_CD)\r
501                 ret = Insert_CD(fname, cd_type);\r
502         if (ret != 0) {\r
503                 menu_update_msg("Load failed, invalid CD image?");\r
504                 return 0;\r
505         }\r
506 \r
507         strncpy(rom_fname_loaded, fname, sizeof(rom_fname_loaded)-1);\r
508         rom_fname_loaded[sizeof(rom_fname_loaded)-1] = 0;\r
509         return 1;\r
510 }\r
511 \r
512 // <base dir><end>\r
513 void emu_make_path(char *buff, const char *end, int size)\r
514 {\r
515         int pos, end_len;\r
516 \r
517         end_len = strlen(end);\r
518         pos = plat_get_root_dir(buff, size);\r
519         strncpy(buff + pos, end, size - pos);\r
520         buff[size - 1] = 0;\r
521         if (pos + end_len > size - 1)\r
522                 lprintf("Warning: path truncated: %s\n", buff);\r
523 }\r
524 \r
525 static void make_config_cfg(char *cfg_buff_512)\r
526 {\r
527         emu_make_path(cfg_buff_512, PicoConfigFile, 512-6);\r
528         if (config_slot != 0)\r
529         {\r
530                 char *p = strrchr(cfg_buff_512, '.');\r
531                 if (p == NULL)\r
532                         p = cfg_buff_512 + strlen(cfg_buff_512);\r
533                 sprintf(p, ".%i.cfg", config_slot);\r
534         }\r
535         cfg_buff_512[511] = 0;\r
536 }\r
537 \r
538 void emu_prep_defconfig(void)\r
539 {\r
540         memset(&defaultConfig, 0, sizeof(defaultConfig));\r
541         defaultConfig.EmuOpt    = 0x9d | EOPT_RAM_TIMINGS|EOPT_EN_CD_LEDS;\r
542         defaultConfig.s_PicoOpt = POPT_EN_STEREO|POPT_EN_FM|POPT_EN_PSG|POPT_EN_Z80 |\r
543                                   POPT_EN_MCD_PCM|POPT_EN_MCD_CDDA|POPT_EN_MCD_GFX |\r
544                                   POPT_EN_SVP_DRC|POPT_ACC_SPRITES |\r
545                                   POPT_EN_32X|POPT_EN_PWM;\r
546         defaultConfig.s_PsndRate = 44100;\r
547         defaultConfig.s_PicoRegion = 0; // auto\r
548         defaultConfig.s_PicoAutoRgnOrder = 0x184; // US, EU, JP\r
549         defaultConfig.s_PicoCDBuffers = 0;\r
550         defaultConfig.confirm_save = EOPT_CONFIRM_SAVE;\r
551         defaultConfig.Frameskip = -1; // auto\r
552         defaultConfig.volume = 50;\r
553         defaultConfig.gamma = 100;\r
554         defaultConfig.scaling = 0;\r
555         defaultConfig.turbo_rate = 15;\r
556         defaultConfig.msh2_khz = PICO_MSH2_HZ / 1000;\r
557         defaultConfig.ssh2_khz = PICO_SSH2_HZ / 1000;\r
558 \r
559         // platform specific overrides\r
560         pemu_prep_defconfig();\r
561 }\r
562 \r
563 void emu_set_defconfig(void)\r
564 {\r
565         memcpy(&currentConfig, &defaultConfig, sizeof(currentConfig));\r
566         PicoOpt = currentConfig.s_PicoOpt;\r
567         PsndRate = currentConfig.s_PsndRate;\r
568         PicoRegionOverride = currentConfig.s_PicoRegion;\r
569         PicoAutoRgnOrder = currentConfig.s_PicoAutoRgnOrder;\r
570         PicoCDBuffers = currentConfig.s_PicoCDBuffers;\r
571 }\r
572 \r
573 int emu_read_config(const char *rom_fname, int no_defaults)\r
574 {\r
575         char cfg[512];\r
576         int ret;\r
577 \r
578         if (!no_defaults)\r
579                 emu_set_defconfig();\r
580 \r
581         if (rom_fname == NULL)\r
582         {\r
583                 // global config\r
584                 make_config_cfg(cfg);\r
585                 ret = config_readsect(cfg, NULL);\r
586         }\r
587         else\r
588         {\r
589                 char ext[16];\r
590                 int vol;\r
591 \r
592                 if (config_slot != 0)\r
593                         snprintf(ext, sizeof(ext), ".%i.cfg", config_slot);\r
594                 else\r
595                         strcpy(ext, ".cfg");\r
596 \r
597                 fname_ext(cfg, sizeof(cfg), "cfg"PATH_SEP, ext, rom_fname);\r
598 \r
599                 // read user's config\r
600                 vol = currentConfig.volume;\r
601                 ret = config_readsect(cfg, NULL);\r
602                 currentConfig.volume = vol; // make vol global (bah)\r
603 \r
604                 if (ret != 0)\r
605                 {\r
606                         // read global config, and apply game_def.cfg on top\r
607                         make_config_cfg(cfg);\r
608                         config_readsect(cfg, NULL);\r
609 \r
610                         emu_make_path(cfg, "game_def.cfg", sizeof(cfg));\r
611                         ret = config_readsect(cfg, emu_make_rom_id(rom_fname));\r
612                 }\r
613         }\r
614 \r
615         pemu_validate_config();\r
616 \r
617         // some sanity checks\r
618 #ifdef PSP\r
619         /* TODO: mv to plat_validate_config() */\r
620         if (currentConfig.CPUclock < 10 || currentConfig.CPUclock > 4096) currentConfig.CPUclock = 200;\r
621         if (currentConfig.gamma < -4 || currentConfig.gamma >  16) currentConfig.gamma = 0;\r
622         if (currentConfig.gamma2 < 0 || currentConfig.gamma2 > 2)  currentConfig.gamma2 = 0;\r
623 #endif\r
624         if (currentConfig.volume < 0 || currentConfig.volume > 99)\r
625                 currentConfig.volume = 50;\r
626 \r
627         if (ret == 0)\r
628                 config_slot_current = config_slot;\r
629 \r
630         return (ret == 0);\r
631 }\r
632 \r
633 \r
634 int emu_write_config(int is_game)\r
635 {\r
636         char cfg[512];\r
637         int ret, write_lrom = 0;\r
638 \r
639         if (!is_game)\r
640         {\r
641                 make_config_cfg(cfg);\r
642                 write_lrom = 1;\r
643         } else {\r
644                 char ext[16];\r
645 \r
646                 if (config_slot != 0)\r
647                         snprintf(ext, sizeof(ext), ".%i.cfg", config_slot);\r
648                 else\r
649                         strcpy(ext, ".cfg");\r
650 \r
651                 romfname_ext(cfg, sizeof(cfg), "cfg"PATH_SEP, ext);\r
652         }\r
653 \r
654         lprintf("emu_write_config: %s ", cfg);\r
655         ret = config_write(cfg);\r
656         if (write_lrom) config_writelrom(cfg);\r
657 #ifdef __GP2X__\r
658         sync();\r
659 #endif\r
660         lprintf((ret == 0) ? "(ok)\n" : "(failed)\n");\r
661 \r
662         if (ret == 0) config_slot_current = config_slot;\r
663         return ret == 0;\r
664 }\r
665 \r
666 \r
667 /* always using built-in font */\r
668 \r
669 #define mk_text_out(name, type, val, topleft, step_x, step_y) \\r
670 void name(int x, int y, const char *text)                               \\r
671 {                                                                       \\r
672         int i, l, len = strlen(text);                                   \\r
673         type *screen = (type *)(topleft) + x * step_x + y * step_y;     \\r
674                                                                         \\r
675         for (i = 0; i < len; i++, screen += 8 * step_x)                 \\r
676         {                                                               \\r
677                 for (l = 0; l < 8; l++)                                 \\r
678                 {                                                       \\r
679                         unsigned char fd = fontdata8x8[text[i] * 8 + l];\\r
680                         type *s = screen + l * step_y;                  \\r
681                         if (fd&0x80) s[step_x * 0] = val;               \\r
682                         if (fd&0x40) s[step_x * 1] = val;               \\r
683                         if (fd&0x20) s[step_x * 2] = val;               \\r
684                         if (fd&0x10) s[step_x * 3] = val;               \\r
685                         if (fd&0x08) s[step_x * 4] = val;               \\r
686                         if (fd&0x04) s[step_x * 5] = val;               \\r
687                         if (fd&0x02) s[step_x * 6] = val;               \\r
688                         if (fd&0x01) s[step_x * 7] = val;               \\r
689                 }                                                       \\r
690         }                                                               \\r
691 }\r
692 \r
693 mk_text_out(emu_text_out8,      unsigned char,    0xf0, g_screen_ptr, 1, g_screen_width)\r
694 mk_text_out(emu_text_out16,     unsigned short, 0xffff, g_screen_ptr, 1, g_screen_width)\r
695 mk_text_out(emu_text_out8_rot,  unsigned char,    0xf0,\r
696         (char *)g_screen_ptr  + (g_screen_width - 1) * g_screen_height, -g_screen_height, 1)\r
697 mk_text_out(emu_text_out16_rot, unsigned short, 0xffff,\r
698         (short *)g_screen_ptr + (g_screen_width - 1) * g_screen_height, -g_screen_height, 1)\r
699 \r
700 #undef mk_text_out\r
701 \r
702 \r
703 void update_movie(void)\r
704 {\r
705         int offs = Pico.m.frame_count*3 + 0x40;\r
706         if (offs+3 > movie_size) {\r
707                 free(movie_data);\r
708                 movie_data = 0;\r
709                 emu_status_msg("END OF MOVIE.");\r
710                 lprintf("END OF MOVIE.\n");\r
711         } else {\r
712                 // MXYZ SACB RLDU\r
713                 PicoPad[0] = ~movie_data[offs]   & 0x8f; // ! SCBA RLDU\r
714                 if(!(movie_data[offs]   & 0x10)) PicoPad[0] |= 0x40; // C\r
715                 if(!(movie_data[offs]   & 0x20)) PicoPad[0] |= 0x10; // A\r
716                 if(!(movie_data[offs]   & 0x40)) PicoPad[0] |= 0x20; // B\r
717                 PicoPad[1] = ~movie_data[offs+1] & 0x8f; // ! SCBA RLDU\r
718                 if(!(movie_data[offs+1] & 0x10)) PicoPad[1] |= 0x40; // C\r
719                 if(!(movie_data[offs+1] & 0x20)) PicoPad[1] |= 0x10; // A\r
720                 if(!(movie_data[offs+1] & 0x40)) PicoPad[1] |= 0x20; // B\r
721                 PicoPad[0] |= (~movie_data[offs+2] & 0x0A) << 8; // ! MZYX\r
722                 if(!(movie_data[offs+2] & 0x01)) PicoPad[0] |= 0x0400; // X\r
723                 if(!(movie_data[offs+2] & 0x04)) PicoPad[0] |= 0x0100; // Z\r
724                 PicoPad[1] |= (~movie_data[offs+2] & 0xA0) << 4; // ! MZYX\r
725                 if(!(movie_data[offs+2] & 0x10)) PicoPad[1] |= 0x0400; // X\r
726                 if(!(movie_data[offs+2] & 0x40)) PicoPad[1] |= 0x0100; // Z\r
727         }\r
728 }\r
729 \r
730 static int try_ropen_file(const char *fname)\r
731 {\r
732         FILE *f;\r
733 \r
734         f = fopen(fname, "rb");\r
735         if (f) {\r
736                 fclose(f);\r
737                 return 1;\r
738         }\r
739         return 0;\r
740 }\r
741 \r
742 char *emu_get_save_fname(int load, int is_sram, int slot)\r
743 {\r
744         char *saveFname = static_buff;\r
745         char ext[16];\r
746 \r
747         if (is_sram)\r
748         {\r
749                 strcpy(ext, (PicoAHW & PAHW_MCD) ? ".brm" : ".srm");\r
750                 romfname_ext(saveFname, sizeof(static_buff),\r
751                         (PicoAHW & PAHW_MCD) ? "brm"PATH_SEP : "srm"PATH_SEP, ext);\r
752                 if (!load)\r
753                         return saveFname;\r
754 \r
755                 if (try_ropen_file(saveFname))\r
756                         return saveFname;\r
757 \r
758                 romfname_ext(saveFname, sizeof(static_buff), NULL, ext);\r
759                 if (try_ropen_file(saveFname))\r
760                         return saveFname;\r
761         }\r
762         else\r
763         {\r
764                 const char *ext_main = (currentConfig.EmuOpt & EOPT_GZIP_SAVES) ? ".mds.gz" : ".mds";\r
765                 const char *ext_othr = (currentConfig.EmuOpt & EOPT_GZIP_SAVES) ? ".mds" : ".mds.gz";\r
766                 ext[0] = 0;\r
767                 if (slot > 0 && slot < 10)\r
768                         sprintf(ext, ".%i", slot);\r
769                 strcat(ext, ext_main);\r
770 \r
771                 if (!load) {\r
772                         romfname_ext(saveFname, sizeof(static_buff), "mds" PATH_SEP, ext);\r
773                         return saveFname;\r
774                 }\r
775                 else {\r
776                         romfname_ext(saveFname, sizeof(static_buff), "mds" PATH_SEP, ext);\r
777                         if (try_ropen_file(saveFname))\r
778                                 return saveFname;\r
779 \r
780                         romfname_ext(saveFname, sizeof(static_buff), NULL, ext);\r
781                         if (try_ropen_file(saveFname))\r
782                                 return saveFname;\r
783 \r
784                         // try the other ext\r
785                         ext[0] = 0;\r
786                         if (slot > 0 && slot < 10)\r
787                                 sprintf(ext, ".%i", slot);\r
788                         strcat(ext, ext_othr);\r
789 \r
790                         romfname_ext(saveFname, sizeof(static_buff), "mds"PATH_SEP, ext);\r
791                         if (try_ropen_file(saveFname))\r
792                                 return saveFname;\r
793                 }\r
794         }\r
795 \r
796         return NULL;\r
797 }\r
798 \r
799 int emu_check_save_file(int slot, int *time)\r
800 {\r
801         return emu_get_save_fname(1, 0, slot) ? 1 : 0;\r
802 }\r
803 \r
804 int emu_save_load_game(int load, int sram)\r
805 {\r
806         int ret = 0;\r
807         char *saveFname;\r
808 \r
809         // make save filename\r
810         saveFname = emu_get_save_fname(load, sram, state_slot);\r
811         if (saveFname == NULL) {\r
812                 if (!sram)\r
813                         emu_status_msg(load ? "LOAD FAILED (missing file)" : "SAVE FAILED");\r
814                 return -1;\r
815         }\r
816 \r
817         lprintf("saveLoad (%i, %i): %s\n", load, sram, saveFname);\r
818 \r
819         if (sram)\r
820         {\r
821                 FILE *sramFile;\r
822                 int sram_size;\r
823                 unsigned char *sram_data;\r
824                 int truncate = 1;\r
825                 if (PicoAHW & PAHW_MCD)\r
826                 {\r
827                         if (PicoOpt & POPT_EN_MCD_RAMCART) {\r
828                                 sram_size = 0x12000;\r
829                                 sram_data = SRam.data;\r
830                                 if (sram_data)\r
831                                         memcpy32((int *)sram_data, (int *)Pico_mcd->bram, 0x2000/4);\r
832                         } else {\r
833                                 sram_size = 0x2000;\r
834                                 sram_data = Pico_mcd->bram;\r
835                                 truncate  = 0; // the .brm may contain RAM cart data after normal brm\r
836                         }\r
837                 } else {\r
838                         sram_size = SRam.size;\r
839                         sram_data = SRam.data;\r
840                 }\r
841                 if (sram_data == NULL)\r
842                         return 0; // SRam forcefully disabled for this game\r
843 \r
844                 if (load)\r
845                 {\r
846                         sramFile = fopen(saveFname, "rb");\r
847                         if (!sramFile)\r
848                                 return -1;\r
849                         ret = fread(sram_data, 1, sram_size, sramFile);\r
850                         ret = ret > 0 ? 0 : -1;\r
851                         fclose(sramFile);\r
852                         if ((PicoAHW & PAHW_MCD) && (PicoOpt&POPT_EN_MCD_RAMCART))\r
853                                 memcpy32((int *)Pico_mcd->bram, (int *)sram_data, 0x2000/4);\r
854                 } else {\r
855                         // sram save needs some special processing\r
856                         // see if we have anything to save\r
857                         for (; sram_size > 0; sram_size--)\r
858                                 if (sram_data[sram_size-1]) break;\r
859 \r
860                         if (sram_size) {\r
861                                 sramFile = fopen(saveFname, truncate ? "wb" : "r+b");\r
862                                 if (!sramFile) sramFile = fopen(saveFname, "wb"); // retry\r
863                                 if (!sramFile) return -1;\r
864                                 ret = fwrite(sram_data, 1, sram_size, sramFile);\r
865                                 ret = (ret != sram_size) ? -1 : 0;\r
866                                 fclose(sramFile);\r
867 #ifdef __GP2X__\r
868                                 sync();\r
869 #endif\r
870                         }\r
871                 }\r
872                 return ret;\r
873         }\r
874         else\r
875         {\r
876                 ret = PicoState(saveFname, !load);\r
877                 if (!ret) {\r
878 #ifdef __GP2X__\r
879                         if (!load) sync();\r
880 #endif\r
881                         emu_status_msg(load ? "STATE LOADED" : "STATE SAVED");\r
882                 } else {\r
883                         emu_status_msg(load ? "LOAD FAILED" : "SAVE FAILED");\r
884                         ret = -1;\r
885                 }\r
886 \r
887                 return ret;\r
888         }\r
889 }\r
890 \r
891 void emu_set_fastforward(int set_on)\r
892 {\r
893         static void *set_PsndOut = NULL;\r
894         static int set_Frameskip, set_EmuOpt, is_on = 0;\r
895 \r
896         if (set_on && !is_on) {\r
897                 set_PsndOut = PsndOut;\r
898                 set_Frameskip = currentConfig.Frameskip;\r
899                 set_EmuOpt = currentConfig.EmuOpt;\r
900                 PsndOut = NULL;\r
901                 currentConfig.Frameskip = 8;\r
902                 currentConfig.EmuOpt &= ~4;\r
903                 currentConfig.EmuOpt |= 0x40000;\r
904                 is_on = 1;\r
905                 emu_status_msg("FAST FORWARD");\r
906         }\r
907         else if (!set_on && is_on) {\r
908                 PsndOut = set_PsndOut;\r
909                 currentConfig.Frameskip = set_Frameskip;\r
910                 currentConfig.EmuOpt = set_EmuOpt;\r
911                 PsndRerate(1);\r
912                 is_on = 0;\r
913         }\r
914 }\r
915 \r
916 static void emu_tray_open(void)\r
917 {\r
918         engineState = PGS_TrayMenu;\r
919 }\r
920 \r
921 static void emu_tray_close(void)\r
922 {\r
923         emu_status_msg("CD tray closed.");\r
924 }\r
925 \r
926 void emu_32x_startup(void)\r
927 {\r
928         plat_video_toggle_renderer(0, 0); // HACK\r
929         system_announce();\r
930 }\r
931 \r
932 void emu_reset_game(void)\r
933 {\r
934         PicoReset();\r
935         reset_timing = 1;\r
936 }\r
937 \r
938 void run_events_pico(unsigned int events)\r
939 {\r
940         int lim_x;\r
941 \r
942         if (events & PEV_PICO_SWINP) {\r
943                 pico_inp_mode++;\r
944                 if (pico_inp_mode > 2)\r
945                         pico_inp_mode = 0;\r
946                 switch (pico_inp_mode) {\r
947                         case 2: emu_status_msg("Input: Pen on Pad"); break;\r
948                         case 1: emu_status_msg("Input: Pen on Storyware"); break;\r
949                         case 0: emu_status_msg("Input: Joystick");\r
950                                 PicoPicohw.pen_pos[0] = PicoPicohw.pen_pos[1] = 0x8000;\r
951                                 break;\r
952                 }\r
953         }\r
954         if (events & PEV_PICO_PPREV) {\r
955                 PicoPicohw.page--;\r
956                 if (PicoPicohw.page < 0)\r
957                         PicoPicohw.page = 0;\r
958                 emu_status_msg("Page %i", PicoPicohw.page);\r
959         }\r
960         if (events & PEV_PICO_PNEXT) {\r
961                 PicoPicohw.page++;\r
962                 if (PicoPicohw.page > 6)\r
963                         PicoPicohw.page = 6;\r
964                 emu_status_msg("Page %i", PicoPicohw.page);\r
965         }\r
966 \r
967         if (pico_inp_mode == 0)\r
968                 return;\r
969 \r
970         /* handle other input modes */\r
971         if (PicoPad[0] & 1) pico_pen_y--;\r
972         if (PicoPad[0] & 2) pico_pen_y++;\r
973         if (PicoPad[0] & 4) pico_pen_x--;\r
974         if (PicoPad[0] & 8) pico_pen_x++;\r
975         PicoPad[0] &= ~0x0f; // release UDLR\r
976 \r
977         lim_x = (Pico.video.reg[12]&1) ? 319 : 255;\r
978         if (pico_pen_y < 8)\r
979                 pico_pen_y = 8;\r
980         if (pico_pen_y > 224 - PICO_PEN_ADJUST_Y)\r
981                 pico_pen_y = 224 - PICO_PEN_ADJUST_Y;\r
982         if (pico_pen_x < 0)\r
983                 pico_pen_x = 0;\r
984         if (pico_pen_x > lim_x - PICO_PEN_ADJUST_X)\r
985                 pico_pen_x = lim_x - PICO_PEN_ADJUST_X;\r
986 \r
987         PicoPicohw.pen_pos[0] = pico_pen_x;\r
988         if (!(Pico.video.reg[12] & 1))\r
989                 PicoPicohw.pen_pos[0] += pico_pen_x / 4;\r
990         PicoPicohw.pen_pos[0] += 0x3c;\r
991         PicoPicohw.pen_pos[1] = pico_inp_mode == 1 ? (0x2f8 + pico_pen_y) : (0x1fc + pico_pen_y);\r
992 }\r
993 \r
994 static void do_turbo(int *pad, int acts)\r
995 {\r
996         static int turbo_pad = 0;\r
997         static unsigned char turbo_cnt[3] = { 0, 0, 0 };\r
998         int inc = currentConfig.turbo_rate * 2;\r
999 \r
1000         if (acts & 0x1000) {\r
1001                 turbo_cnt[0] += inc;\r
1002                 if (turbo_cnt[0] >= 60)\r
1003                         turbo_pad ^= 0x10, turbo_cnt[0] = 0;\r
1004         }\r
1005         if (acts & 0x2000) {\r
1006                 turbo_cnt[1] += inc;\r
1007                 if (turbo_cnt[1] >= 60)\r
1008                         turbo_pad ^= 0x20, turbo_cnt[1] = 0;\r
1009         }\r
1010         if (acts & 0x4000) {\r
1011                 turbo_cnt[2] += inc;\r
1012                 if (turbo_cnt[2] >= 60)\r
1013                         turbo_pad ^= 0x40, turbo_cnt[2] = 0;\r
1014         }\r
1015         *pad |= turbo_pad & (acts >> 8);\r
1016 }\r
1017 \r
1018 static void run_events_ui(unsigned int which)\r
1019 {\r
1020         if (which & (PEV_STATE_LOAD|PEV_STATE_SAVE))\r
1021         {\r
1022                 int do_it = 1;\r
1023                 if ( emu_check_save_file(state_slot, NULL) &&\r
1024                         (((which & PEV_STATE_LOAD) && (currentConfig.confirm_save & EOPT_CONFIRM_LOAD)) ||\r
1025                          ((which & PEV_STATE_SAVE) && (currentConfig.confirm_save & EOPT_CONFIRM_SAVE))) )\r
1026                 {\r
1027                         const char *nm;\r
1028                         char tmp[64];\r
1029                         int keys, len;\r
1030 \r
1031                         strcpy(tmp, (which & PEV_STATE_LOAD) ? "LOAD STATE?" : "OVERWRITE SAVE?");\r
1032                         len = strlen(tmp);\r
1033                         nm = in_get_key_name(-1, -PBTN_MA3);\r
1034                         snprintf(tmp + len, sizeof(tmp) - len, "(%s=yes, ", nm);\r
1035                         len = strlen(tmp);\r
1036                         nm = in_get_key_name(-1, -PBTN_MBACK);\r
1037                         snprintf(tmp + len, sizeof(tmp) - len, "%s=no)", nm);\r
1038 \r
1039                         plat_status_msg_busy_first(tmp);\r
1040 \r
1041                         in_set_config_int(0, IN_CFG_BLOCKING, 1);\r
1042                         while (in_menu_wait_any(NULL, 50) & (PBTN_MA3|PBTN_MBACK))\r
1043                                 ;\r
1044                         while ( !((keys = in_menu_wait_any(NULL, 50)) & (PBTN_MA3|PBTN_MBACK)) )\r
1045                                 ;\r
1046                         if (keys & PBTN_MBACK)\r
1047                                 do_it = 0;\r
1048                         while (in_menu_wait_any(NULL, 50) & (PBTN_MA3|PBTN_MBACK))\r
1049                                 ;\r
1050                         in_set_config_int(0, IN_CFG_BLOCKING, 0);\r
1051                 }\r
1052                 if (do_it) {\r
1053                         plat_status_msg_busy_first((which & PEV_STATE_LOAD) ? "LOADING STATE" : "SAVING STATE");\r
1054                         PicoStateProgressCB = plat_status_msg_busy_next;\r
1055                         emu_save_load_game((which & PEV_STATE_LOAD) ? 1 : 0, 0);\r
1056                         PicoStateProgressCB = NULL;\r
1057                 }\r
1058         }\r
1059         if (which & PEV_SWITCH_RND)\r
1060         {\r
1061                 plat_video_toggle_renderer(1, 0);\r
1062         }\r
1063         if (which & (PEV_SSLOT_PREV|PEV_SSLOT_NEXT))\r
1064         {\r
1065                 if (which & PEV_SSLOT_PREV) {\r
1066                         state_slot -= 1;\r
1067                         if (state_slot < 0)\r
1068                                 state_slot = 9;\r
1069                 } else {\r
1070                         state_slot += 1;\r
1071                         if (state_slot > 9)\r
1072                                 state_slot = 0;\r
1073                 }\r
1074 \r
1075                 emu_status_msg("SAVE SLOT %i [%s]", state_slot,\r
1076                         emu_check_save_file(state_slot, NULL) ? "USED" : "FREE");\r
1077         }\r
1078         if (which & PEV_MENU)\r
1079                 engineState = PGS_Menu;\r
1080 }\r
1081 \r
1082 void emu_update_input(void)\r
1083 {\r
1084         static int prev_events = 0;\r
1085         int actions[IN_BINDTYPE_COUNT] = { 0, };\r
1086         int pl_actions[2];\r
1087         int events;\r
1088 \r
1089         in_update(actions);\r
1090 \r
1091         pl_actions[0] = actions[IN_BINDTYPE_PLAYER12];\r
1092         pl_actions[1] = actions[IN_BINDTYPE_PLAYER12] >> 16;\r
1093 \r
1094         PicoPad[0] = pl_actions[0] & 0xfff;\r
1095         PicoPad[1] = pl_actions[1] & 0xfff;\r
1096 \r
1097         if (pl_actions[0] & 0x7000)\r
1098                 do_turbo(&PicoPad[0], pl_actions[0]);\r
1099         if (pl_actions[1] & 0x7000)\r
1100                 do_turbo(&PicoPad[1], pl_actions[1]);\r
1101 \r
1102         events = actions[IN_BINDTYPE_EMU] & PEV_MASK;\r
1103 \r
1104         // volume is treated in special way and triggered every frame\r
1105         if (events & (PEV_VOL_DOWN|PEV_VOL_UP))\r
1106                 plat_update_volume(1, events & PEV_VOL_UP);\r
1107 \r
1108         if ((events ^ prev_events) & PEV_FF) {\r
1109                 emu_set_fastforward(events & PEV_FF);\r
1110                 plat_update_volume(0, 0);\r
1111                 reset_timing = 1;\r
1112         }\r
1113 \r
1114         events &= ~prev_events;\r
1115 \r
1116         if (PicoAHW == PAHW_PICO)\r
1117                 run_events_pico(events);\r
1118         if (events)\r
1119                 run_events_ui(events);\r
1120         if (movie_data)\r
1121                 update_movie();\r
1122 \r
1123         prev_events = actions[IN_BINDTYPE_EMU] & PEV_MASK;\r
1124 }\r
1125 \r
1126 static void mkdir_path(char *path_with_reserve, int pos, const char *name)\r
1127 {\r
1128         strcpy(path_with_reserve + pos, name);\r
1129         if (plat_is_dir(path_with_reserve))\r
1130                 return;\r
1131         if (mkdir(path_with_reserve, 0777) < 0)\r
1132                 lprintf("failed to create: %s\n", path_with_reserve);\r
1133 }\r
1134 \r
1135 void emu_cmn_forced_frame(int no_scale, int do_emu)\r
1136 {\r
1137         int po_old = PicoOpt;\r
1138 \r
1139         memset32(g_screen_ptr, 0, g_screen_width * g_screen_height * 2 / 4);\r
1140 \r
1141         PicoOpt &= ~POPT_ALT_RENDERER;\r
1142         PicoOpt |= POPT_ACC_SPRITES;\r
1143         if (!no_scale)\r
1144                 PicoOpt |= POPT_EN_SOFTSCALE;\r
1145 \r
1146         PicoDrawSetOutFormat(PDF_RGB555, 1);\r
1147         Pico.m.dirtyPal = 1;\r
1148         if (do_emu)\r
1149                 PicoFrame();\r
1150         else\r
1151                 PicoFrameDrawOnly();\r
1152 \r
1153         PicoOpt = po_old;\r
1154 }\r
1155 \r
1156 void emu_init(void)\r
1157 {\r
1158         char path[512];\r
1159         int pos;\r
1160 \r
1161 #if 0\r
1162         // FIXME: handle through menu, etc\r
1163         FILE *f;\r
1164         f = fopen("32X_M_BIOS.BIN", "rb");\r
1165         p32x_bios_m = malloc(2048);\r
1166         fread(p32x_bios_m, 1, 2048, f);\r
1167         fclose(f);\r
1168         f = fopen("32X_S_BIOS.BIN", "rb");\r
1169         p32x_bios_s = malloc(1024);\r
1170         fread(p32x_bios_s, 1, 1024, f);\r
1171         fclose(f);\r
1172 #endif\r
1173 \r
1174         /* make dirs for saves */\r
1175         pos = plat_get_root_dir(path, sizeof(path) - 4);\r
1176         mkdir_path(path, pos, "mds");\r
1177         mkdir_path(path, pos, "srm");\r
1178         mkdir_path(path, pos, "brm");\r
1179         mkdir_path(path, pos, "cfg");\r
1180 \r
1181         pprof_init();\r
1182 \r
1183         make_config_cfg(path);\r
1184         config_readlrom(path);\r
1185 \r
1186         PicoInit();\r
1187         PicoMessage = plat_status_msg_busy_next;\r
1188         PicoMCDopenTray = emu_tray_open;\r
1189         PicoMCDcloseTray = emu_tray_close;\r
1190 \r
1191         sndout_init();\r
1192 }\r
1193 \r
1194 void emu_finish(void)\r
1195 {\r
1196         // save SRAM\r
1197         if ((currentConfig.EmuOpt & EOPT_EN_SRAM) && SRam.changed) {\r
1198                 emu_save_load_game(0, 1);\r
1199                 SRam.changed = 0;\r
1200         }\r
1201 \r
1202         if (!(currentConfig.EmuOpt & EOPT_NO_AUTOSVCFG)) {\r
1203                 char cfg[512];\r
1204                 make_config_cfg(cfg);\r
1205                 config_writelrom(cfg);\r
1206 #ifdef __GP2X__\r
1207                 sync();\r
1208 #endif\r
1209         }\r
1210 \r
1211         pprof_finish();\r
1212 \r
1213         PicoExit();\r
1214         sndout_exit();\r
1215 }\r
1216 \r
1217 static void snd_write_nonblocking(int len)\r
1218 {\r
1219         sndout_write_nb(PsndOut, len);\r
1220 }\r
1221 \r
1222 void emu_sound_start(void)\r
1223 {\r
1224         PsndOut = NULL;\r
1225 \r
1226         if (currentConfig.EmuOpt & EOPT_EN_SOUND)\r
1227         {\r
1228                 int is_stereo = (PicoOpt & POPT_EN_STEREO) ? 1 : 0;\r
1229 \r
1230                 PsndRerate(Pico.m.frame_count ? 1 : 0);\r
1231 \r
1232                 printf("starting audio: %i len: %i stereo: %i, pal: %i\n",\r
1233                         PsndRate, PsndLen, is_stereo, Pico.m.pal);\r
1234                 sndout_start(PsndRate, is_stereo);\r
1235                 PicoWriteSound = snd_write_nonblocking;\r
1236                 plat_update_volume(0, 0);\r
1237                 memset(sndBuffer, 0, sizeof(sndBuffer));\r
1238                 PsndOut = sndBuffer;\r
1239         }\r
1240 }\r
1241 \r
1242 void emu_sound_stop(void)\r
1243 {\r
1244         sndout_stop();\r
1245 }\r
1246 \r
1247 void emu_sound_wait(void)\r
1248 {\r
1249         sndout_wait();\r
1250 }\r
1251 \r
1252 static void emu_loop_prep(void)\r
1253 {\r
1254         static int pal_old = -1;\r
1255         static int filter_old = -1;\r
1256 \r
1257         if (currentConfig.CPUclock != plat_target_cpu_clock_get())\r
1258                 plat_target_cpu_clock_set(currentConfig.CPUclock);\r
1259 \r
1260         if (Pico.m.pal != pal_old) {\r
1261                 plat_target_lcdrate_set(Pico.m.pal);\r
1262                 pal_old = Pico.m.pal;\r
1263         }\r
1264 \r
1265         if (currentConfig.filter != filter_old) {\r
1266                 plat_target_hwfilter_set(currentConfig.filter);\r
1267                 filter_old = currentConfig.filter;\r
1268         }\r
1269 \r
1270         plat_target_gamma_set(currentConfig.gamma, 0);\r
1271 \r
1272         pemu_loop_prep();\r
1273 }\r
1274 \r
1275 static void skip_frame(int do_audio)\r
1276 {\r
1277         PicoSkipFrame = do_audio ? 1 : 2;\r
1278         PicoFrame();\r
1279         PicoSkipFrame = 0;\r
1280 }\r
1281 \r
1282 /* our tick here is 1 us right now */\r
1283 #define ms_to_ticks(x) (unsigned int)(x * 1000)\r
1284 #define get_ticks() plat_get_ticks_us()\r
1285 \r
1286 void emu_loop(void)\r
1287 {\r
1288         int pframes_done;               /* "period" frames, used for sync */\r
1289         int frames_done, frames_shown;  /* actual frames for fps counter */\r
1290         int target_fps, target_frametime;\r
1291         unsigned int timestamp_base = 0, timestamp_fps;\r
1292         char *notice_msg = NULL;\r
1293         char fpsbuff[24];\r
1294         int i;\r
1295 \r
1296         fpsbuff[0] = 0;\r
1297 \r
1298         PicoLoopPrepare();\r
1299 \r
1300         // prepare CD buffer\r
1301         if (PicoAHW & PAHW_MCD)\r
1302                 PicoCDBufferInit();\r
1303 \r
1304         plat_video_loop_prepare();\r
1305         emu_loop_prep();\r
1306         pemu_sound_start();\r
1307 \r
1308         /* number of ticks per frame */\r
1309         if (Pico.m.pal) {\r
1310                 target_fps = 50;\r
1311                 target_frametime = ms_to_ticks(1000) / 50;\r
1312         } else {\r
1313                 target_fps = 60;\r
1314                 target_frametime = ms_to_ticks(1000) / 60 + 1;\r
1315         }\r
1316 \r
1317         timestamp_fps = get_ticks();\r
1318         reset_timing = 1;\r
1319 \r
1320         frames_done = frames_shown = pframes_done = 0;\r
1321 \r
1322         plat_video_wait_vsync();\r
1323 \r
1324         /* loop with resync every 1 sec. */\r
1325         while (engineState == PGS_Running)\r
1326         {\r
1327                 unsigned int timestamp;\r
1328                 int diff, diff_lim;\r
1329 \r
1330                 pprof_start(main);\r
1331 \r
1332                 timestamp = get_ticks();\r
1333                 if (reset_timing) {\r
1334                         reset_timing = 0;\r
1335                         timestamp_base = timestamp;\r
1336                         pframes_done = 0;\r
1337                 }\r
1338 \r
1339                 // show notice_msg message?\r
1340                 if (notice_msg_time != 0)\r
1341                 {\r
1342                         static int noticeMsgSum;\r
1343                         if (timestamp - ms_to_ticks(notice_msg_time) > ms_to_ticks(STATUS_MSG_TIMEOUT)) {\r
1344                                 notice_msg_time = 0;\r
1345                                 plat_status_msg_clear();\r
1346                                 notice_msg = NULL;\r
1347                         } else {\r
1348                                 int sum = noticeMsg[0] + noticeMsg[1] + noticeMsg[2];\r
1349                                 if (sum != noticeMsgSum) {\r
1350                                         plat_status_msg_clear();\r
1351                                         noticeMsgSum = sum;\r
1352                                 }\r
1353                                 notice_msg = noticeMsg;\r
1354                         }\r
1355                 }\r
1356 \r
1357                 // second changed?\r
1358                 if (timestamp - timestamp_fps >= ms_to_ticks(1000))\r
1359                 {\r
1360 #ifdef BENCHMARK\r
1361                         static int bench = 0, bench_fps = 0, bench_fps_s = 0, bfp = 0, bf[4];\r
1362                         if (++bench == 10) {\r
1363                                 bench = 0;\r
1364                                 bench_fps_s = bench_fps;\r
1365                                 bf[bfp++ & 3] = bench_fps;\r
1366                                 bench_fps = 0;\r
1367                         }\r
1368                         bench_fps += frames_shown;\r
1369                         sprintf(fpsbuff, "%02i/%02i/%02i", frames_shown, bench_fps_s, (bf[0]+bf[1]+bf[2]+bf[3])>>2);\r
1370                         printf("%s\n", fpsbuff);\r
1371 #else\r
1372                         if (currentConfig.EmuOpt & EOPT_SHOW_FPS) {\r
1373                                 sprintf(fpsbuff, "%02i/%02i", frames_shown, frames_done);\r
1374                                 if (fpsbuff[5] == 0) { fpsbuff[5] = fpsbuff[6] = ' '; fpsbuff[7] = 0; }\r
1375                         }\r
1376 #endif\r
1377                         frames_shown = frames_done = 0;\r
1378                         timestamp_fps += ms_to_ticks(1000);\r
1379                 }\r
1380 #ifdef PFRAMES\r
1381                 sprintf(fpsbuff, "%i", Pico.m.frame_count);\r
1382 #endif\r
1383 \r
1384                 if (timestamp - timestamp_base >= ms_to_ticks(1000))\r
1385                 {\r
1386                         if ((currentConfig.EmuOpt & EOPT_NO_FRMLIMIT) && currentConfig.Frameskip >= 0)\r
1387                                 pframes_done = 0;\r
1388                         else\r
1389                                 pframes_done -= target_fps;\r
1390                         if (pframes_done < -2) {\r
1391                                 /* don't drag more than 2 frames behind */\r
1392                                 pframes_done = -2;\r
1393                                 timestamp_base = timestamp - 2 * target_frametime;\r
1394                         }\r
1395                         else\r
1396                                 timestamp_base += ms_to_ticks(1000);\r
1397                 }\r
1398 \r
1399                 diff = timestamp - timestamp_base;\r
1400                 diff_lim = (pframes_done + 1) * target_frametime;\r
1401 \r
1402                 if (currentConfig.Frameskip >= 0) // frameskip enabled\r
1403                 {\r
1404                         for (i = 0; i < currentConfig.Frameskip; i++) {\r
1405                                 emu_update_input();\r
1406                                 skip_frame(1);\r
1407                                 pframes_done++; frames_done++;\r
1408                                 diff_lim += target_frametime;\r
1409 \r
1410                                 if (!(currentConfig.EmuOpt & (EOPT_NO_FRMLIMIT|EOPT_EXT_FRMLIMIT))) {\r
1411                                         timestamp = get_ticks();\r
1412                                         diff = timestamp - timestamp_base;\r
1413                                         if (!reset_timing && diff < diff_lim) // we are too fast\r
1414                                                 plat_wait_till_us(timestamp_base + diff_lim);\r
1415                                 }\r
1416                         }\r
1417                 }\r
1418                 else if (diff > diff_lim)\r
1419                 {\r
1420                         /* no time left for this frame - skip */\r
1421                         /* limit auto frameskip to 8 */\r
1422                         if (frames_done / 8 <= frames_shown) {\r
1423                                 emu_update_input();\r
1424                                 skip_frame(diff < diff_lim + target_frametime * 16);\r
1425                                 pframes_done++; frames_done++;\r
1426                                 continue;\r
1427                         }\r
1428                 }\r
1429 \r
1430                 emu_update_input();\r
1431                 PicoFrame();\r
1432                 pemu_finalize_frame(fpsbuff, notice_msg);\r
1433 \r
1434                 // plat_video_flip();\r
1435 \r
1436                 /* frame limiter */\r
1437                 if (!reset_timing && !(currentConfig.EmuOpt & (EOPT_NO_FRMLIMIT|EOPT_EXT_FRMLIMIT)))\r
1438                 {\r
1439                         timestamp = get_ticks();\r
1440                         diff = timestamp - timestamp_base;\r
1441 \r
1442                         // sleep or vsync if we are still too fast\r
1443                         if (diff < diff_lim)\r
1444                         {\r
1445                                 // we are too fast\r
1446                                 plat_wait_till_us(timestamp_base + diff_lim - target_frametime / 4);\r
1447                                 if (currentConfig.EmuOpt & EOPT_VSYNC)\r
1448                                         plat_video_wait_vsync();\r
1449                         }\r
1450                 }\r
1451 \r
1452                 // XXX: for some plats it might be better to flip before vsync\r
1453                 // (due to shadow registers in display hw)\r
1454                 plat_video_flip();\r
1455 \r
1456                 pframes_done++; frames_done++; frames_shown++;\r
1457 \r
1458                 pprof_end(main);\r
1459         }\r
1460 \r
1461         emu_set_fastforward(0);\r
1462 \r
1463         // save SRAM\r
1464         if ((currentConfig.EmuOpt & EOPT_EN_SRAM) && SRam.changed) {\r
1465                 plat_status_msg_busy_first("Writing SRAM/BRAM...");\r
1466                 emu_save_load_game(0, 1);\r
1467                 SRam.changed = 0;\r
1468         }\r
1469 \r
1470         pemu_loop_end();\r
1471         emu_sound_stop();\r
1472 \r
1473         // pemu_loop_end() might want to do 1 frame for bg image,\r
1474         // so free CD buffer here\r
1475         if (PicoAHW & PAHW_MCD)\r
1476                 PicoCDBufferFree();\r
1477 }\r
1478 \r