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