From 3ffee69c504e786d66bff58e109534025701d86e Mon Sep 17 00:00:00 2001
From: notaz <notasas@gmail.com>
Date: Fri, 6 Mar 2009 22:21:23 +0000
Subject: [PATCH] add support for doublesized menu fonts

git-svn-id: file:///home/notaz/opt/svn/PicoDrive/platform@645 be3aeb3a-fb24-0410-a615-afba39da0efa
---
 common/emu.c          |  36 ++++----
 common/menu.c         | 195 ++++++++++++++++++++++++++----------------
 common/menu.h         |  13 ++-
 gp2x/940ctl.c         |   2 +-
 gp2x/port_config.h    |   1 +
 linux/port_config.h   |   1 +
 pandora/port_config.h |   1 +
 psp/port_config.h     |   1 +
 8 files changed, 148 insertions(+), 102 deletions(-)

diff --git a/common/emu.c b/common/emu.c
index f8b4e65..b03f8f8 100644
--- a/common/emu.c
+++ b/common/emu.c
@@ -129,9 +129,9 @@ int emu_findBios(int region, char **bios_file)
 		if (bios_file) *bios_file = bios_path;
 		return 1;
 	} else {
-		sprintf(menuErrorMsg, "no %s BIOS files found, read docs",
+		sprintf(bios_path, "no %s BIOS files found, read docs",
 			region != 4 ? (region == 8 ? "EU" : "JAP") : "USA");
-		lprintf("%s\n", menuErrorMsg);
+		me_update_msg(bios_path);
 		return 0;
 	}
 }
@@ -307,7 +307,7 @@ int emu_ReloadRom(char *rom_fname)
 
 	// detect wrong extensions
 	if (!strcmp(ext, ".srm") || !strcmp(ext, "s.gz") || !strcmp(ext, ".mds")) { // s.gz ~ .mds.gz
-		sprintf(menuErrorMsg, "Not a ROM/CD selected.");
+		me_update_msg("Not a ROM/CD selected.");
 		return 0;
 	}
 
@@ -324,32 +324,32 @@ int emu_ReloadRom(char *rom_fname)
 		int dummy;
 		FILE *movie_file = fopen(rom_fname, "rb");
 		if(!movie_file) {
-			sprintf(menuErrorMsg, "Failed to open movie.");
+			me_update_msg("Failed to open movie.");
 			return 0;
 		}
 		fseek(movie_file, 0, SEEK_END);
 		movie_size = ftell(movie_file);
 		fseek(movie_file, 0, SEEK_SET);
 		if(movie_size < 64+3) {
-			sprintf(menuErrorMsg, "Invalid GMV file.");
+			me_update_msg("Invalid GMV file.");
 			fclose(movie_file);
 			return 0;
 		}
 		movie_data = malloc(movie_size);
 		if(movie_data == NULL) {
-			sprintf(menuErrorMsg, "low memory.");
+			me_update_msg("low memory.");
 			fclose(movie_file);
 			return 0;
 		}
 		fread(movie_data, 1, movie_size, movie_file);
 		fclose(movie_file);
 		if (strncmp((char *)movie_data, "Gens Movie TEST", 15) != 0) {
-			sprintf(menuErrorMsg, "Invalid GMV file.");
+			me_update_msg("Invalid GMV file.");
 			return 0;
 		}
 		dummy = try_rfn_cut(rom_fname) || try_rfn_cut(rom_fname);
 		if (!dummy) {
-			sprintf(menuErrorMsg, "Could't find a ROM for movie.");
+			me_update_msg("Could't find a ROM for movie.");
 			return 0;
 		}
 		get_ext(rom_fname, ext);
@@ -361,7 +361,7 @@ int emu_ReloadRom(char *rom_fname)
 		PicoPatchLoad(rom_fname);
 		dummy = try_rfn_cut(rom_fname) || try_rfn_cut(rom_fname);
 		if (!dummy) {
-			sprintf(menuErrorMsg, "Could't find a ROM to patch.");
+			me_update_msg("Could't find a ROM to patch.");
 			return 0;
 		}
 		get_ext(rom_fname, ext);
@@ -401,12 +401,12 @@ int emu_ReloadRom(char *rom_fname)
 
 	rom = pm_open(used_rom_name);
 	if (!rom) {
-		sprintf(menuErrorMsg, "Failed to open ROM/CD image");
+		me_update_msg("Failed to open ROM/CD image");
 		goto fail;
 	}
 
 	if (cd_state < 0) {
-		sprintf(menuErrorMsg, "Invalid CD image");
+		me_update_msg("Invalid CD image");
 		goto fail;
 	}
 
@@ -416,10 +416,9 @@ int emu_ReloadRom(char *rom_fname)
 	rom_loaded = 0;
 
 	if ( (ret = PicoCartLoad(rom, &rom_data, &rom_size)) ) {
-		if      (ret == 2) sprintf(menuErrorMsg, "Out of memory");
-		else if (ret == 3) sprintf(menuErrorMsg, "Read failed");
-		else               sprintf(menuErrorMsg, "PicoCartLoad() failed.");
-		lprintf("%s\n", menuErrorMsg);
+		if      (ret == 2) me_update_msg("Out of memory");
+		else if (ret == 3) me_update_msg("Read failed");
+		else               me_update_msg("PicoCartLoad() failed.");
 		goto fail2;
 	}
 	pm_close(rom);
@@ -429,7 +428,7 @@ int emu_ReloadRom(char *rom_fname)
 	if (rom_size <= 0x200 || strncmp((char *)rom_data, "Pico", 4) == 0 ||
 	  ((*(unsigned char *)(rom_data+4)<<16)|(*(unsigned short *)(rom_data+6))) >= (int)rom_size) {
 		if (rom_data) free(rom_data);
-		sprintf(menuErrorMsg, "Not a ROM selected.");
+		me_update_msg("Not a ROM selected.");
 		goto fail2;
 	}
 
@@ -443,7 +442,7 @@ int emu_ReloadRom(char *rom_fname)
 
 	lprintf("PicoCartInsert(%p, %d);\n", rom_data, rom_size);
 	if (PicoCartInsert(rom_data, rom_size)) {
-		sprintf(menuErrorMsg, "Failed to load ROM.");
+		me_update_msg("Failed to load ROM.");
 		goto fail2;
 	}
 
@@ -451,8 +450,7 @@ int emu_ReloadRom(char *rom_fname)
 	if (cd_state != CIT_NOT_CD) {
 		ret = Insert_CD(rom_fname, cd_state);
 		if (ret != 0) {
-			sprintf(menuErrorMsg, "Insert_CD() failed, invalid CD image?");
-			lprintf("%s\n", menuErrorMsg);
+			me_update_msg("Insert_CD() failed, invalid CD image?");
 			goto fail2;
 		}
 	}
diff --git a/common/menu.c b/common/menu.c
index dc53393..2f29e87 100644
--- a/common/menu.c
+++ b/common/menu.c
@@ -25,14 +25,19 @@
 #define array_size(x) (sizeof(x) / sizeof(x[0]))
 
 static char static_buff[64];
-char menuErrorMsg[64] = { 0, };
-
+static char menu_error_msg[64] = { 0, };
+static int  menu_error_time = 0;
 
 #ifndef UIQ3
 
-static unsigned char menu_font_data[10240];
+static unsigned char *menu_font_data = NULL;
 static int menu_text_color = 0xffff; // default to white
 static int menu_sel_color = -1; // disabled
+#if MENU_X2
+static const int me_mfont_w = 16, me_mfont_h = 20;
+#else
+static const int me_mfont_w = 8, me_mfont_h = 10;
+#endif
 
 // draws text to current bbp16 screen
 static void text_out16_(int x, int y, const char *text, int color)
@@ -59,11 +64,11 @@ static void text_out16_(int x, int y, const char *text, int color)
 
 	for (i = 0; i < len; i++)
 	{
-		unsigned char  *src = menu_font_data + (unsigned int)text[i]*4*10;
+		unsigned char  *src = menu_font_data + (unsigned int)text[i] * me_mfont_w * me_mfont_h / 2;
 		unsigned short *dst = dest;
-		for (l = 0; l < 10; l++, dst += g_screen_width - 8)
+		for (l = 0; l < me_mfont_h; l++, dst += g_screen_width - me_mfont_w)
 		{
-			for (u = 8/2; u > 0; u--, src++)
+			for (u = me_mfont_w / 2; u > 0; u--, src++)
 			{
 				int c, r, g, b;
 				c = *src >> 4;
@@ -84,7 +89,7 @@ static void text_out16_(int x, int y, const char *text, int color)
 				*dst++ = ((r<<8)&0xf800) | ((g<<3)&0x07e0) | (b>>3);
 			}
 		}
-		dest += 8;
+		dest += me_mfont_w;
 	}
 }
 
@@ -92,7 +97,7 @@ void text_out16(int x, int y, const char *texto, ...)
 {
 	va_list args;
 	char    buffer[256];
-	int     maxw = (g_screen_width - x) / 8;
+	int     maxw = (g_screen_width - x) / me_mfont_w;
 
 	if (maxw < 0)
 		return;
@@ -141,7 +146,7 @@ static void smalltext_out16_(int x, int y, const char *texto, int color)
 	}
 }
 
-void smalltext_out16(int x, int y, const char *texto, int color)
+static void smalltext_out16(int x, int y, const char *texto, int color)
 {
 	char buffer[128];
 	int maxw = (g_screen_width - x) / 6;
@@ -168,7 +173,7 @@ static void menu_draw_selection(int x, int y, int w)
 
 	if (y > 0) y--;
 	dest = (unsigned short *)g_screen_ptr + x + y * g_screen_width + 14;
-	for (h = 11; h > 0; h--)
+	for (h = me_mfont_h + 1; h > 0; h--)
 	{
 		dst = dest;
 		for (i = w; i > 0; i--)
@@ -192,33 +197,65 @@ static int parse_hex_color(char *buff)
 
 void menu_init(void)
 {
-	int c, l;
-	unsigned char *fd = menu_font_data;
+	int i, c, l;
+	unsigned char *fd, *fds;
 	char buff[256];
 	FILE *f;
 
-	// generate default font from fontdata8x8
-	memset(menu_font_data, 0, sizeof(menu_font_data));
-	for (c = 0; c < 256; c++)
+	if (menu_font_data != NULL)
+		free(menu_font_data);
+
+	menu_font_data = calloc((MENU_X2 ? 256 * 320 : 128 * 160) / 2, 1);
+	if (menu_font_data == NULL)
+		return;
+
+	// generate default 8x10 font from fontdata8x8
+	for (c = 0, fd = menu_font_data; c < 256; c++)
 	{
 		for (l = 0; l < 8; l++)
 		{
 			unsigned char fd8x8 = fontdata8x8[c*8+l];
-			if (fd8x8&0x80) *fd |= 0xf0;
+			if (fd8x8&0x80) *fd  = 0xf0;
 			if (fd8x8&0x40) *fd |= 0x0f; fd++;
-			if (fd8x8&0x20) *fd |= 0xf0;
+			if (fd8x8&0x20) *fd  = 0xf0;
 			if (fd8x8&0x10) *fd |= 0x0f; fd++;
-			if (fd8x8&0x08) *fd |= 0xf0;
+			if (fd8x8&0x08) *fd  = 0xf0;
 			if (fd8x8&0x04) *fd |= 0x0f; fd++;
-			if (fd8x8&0x02) *fd |= 0xf0;
+			if (fd8x8&0x02) *fd  = 0xf0;
 			if (fd8x8&0x01) *fd |= 0x0f; fd++;
 		}
 		fd += 8*2/2; // 2 empty lines
 	}
 
+	if (MENU_X2) {
+		// expand default font
+		fds = menu_font_data + 128 * 160 / 2 - 4;
+		fd  = menu_font_data + 256 * 320 / 2 - 1;
+		for (c = 255; c >= 0; c--)
+		{
+			for (l = 9; l >= 0; l--, fds -= 4)
+			{
+				for (i = 3; i >= 0; i--) {
+					int px = fds[i] & 0x0f;
+					*fd-- = px | (px << 4);
+					px = (fds[i] >> 4) & 0x0f;
+					*fd-- = px | (px << 4);
+				}
+				for (i = 3; i >= 0; i--) {
+					int px = fds[i] & 0x0f;
+					*fd-- = px | (px << 4);
+					px = (fds[i] >> 4) & 0x0f;
+					*fd-- = px | (px << 4);
+				}
+			}
+		}
+	}
+
 	// load custom font and selector (stored as 1st symbol in font table)
 	readpng(menu_font_data, "skin/font.png", READPNG_FONT);
-	memcpy(menu_font_data, menu_font_data + ((int)'>')*4*10, 4*10); // default selector symbol is '>'
+	// default selector symbol is '>'
+	memcpy(menu_font_data, menu_font_data + ((int)'>') * me_mfont_w * me_mfont_h / 2,
+		me_mfont_w * me_mfont_h / 2);
 	readpng(menu_font_data, "skin/selector.png", READPNG_SELECTOR);
 
 	// load custom colors
@@ -281,7 +318,7 @@ static void me_draw(const menu_entry *entries, int sel)
 {
 	const menu_entry *ent;
 	int x, y, w = 0, h = 0;
-	int offs, opt_offs = 27*8;
+	int offs, opt_offs = 27 * me_mfont_w;
 	const char *name;
 	int asel = 0;
 	int i, n;
@@ -298,22 +335,22 @@ static void me_draw(const menu_entry *entries, int sel)
 			asel = n;
 
 		name = NULL;
-		wt = strlen(ent->name) * 8;	/* FIXME: unhardcode font width */
+		wt = strlen(ent->name) * me_mfont_w;
 		if (wt == 0 && ent->generate_name)
 			name = ent->generate_name(ent->id, &offs);
 		if (name != NULL)
-			wt = strlen(name) * 8;
+			wt = strlen(name) * me_mfont_w;
 
 		if (ent->beh != MB_NONE)
 		{
 			if (wt > opt_offs)
-				opt_offs = wt + 8;
+				opt_offs = wt + me_mfont_w;
 			wt = opt_offs;
 
 			switch (ent->beh) {
 			case MB_NONE: break;
 			case MB_OPT_ONOFF:
-			case MB_OPT_RANGE: wt += 8*3; break;
+			case MB_OPT_RANGE: wt += me_mfont_w * 3; break;
 			case MB_OPT_CUSTOM:
 			case MB_OPT_CUSTONOFF:
 			case MB_OPT_CUSTRANGE:
@@ -322,7 +359,7 @@ static void me_draw(const menu_entry *entries, int sel)
 				if (ent->generate_name != NULL)
 					name = ent->generate_name(ent->id, &offs);
 				if (name != NULL)
-					wt += (strlen(name) + offs) * 8;
+					wt += (strlen(name) + offs) * me_mfont_w;
 				break;
 			}
 		}
@@ -331,8 +368,8 @@ static void me_draw(const menu_entry *entries, int sel)
 			w = wt;
 		n++;
 	}
-	h = n * 10;
-	w += 16; /* selector */
+	h = n * me_mfont_h;
+	w += me_mfont_w * 2; /* selector */
 
 	if (w > g_screen_width) {
 		lprintf("width %d > %d\n", w, g_screen_width);
@@ -348,7 +385,8 @@ static void me_draw(const menu_entry *entries, int sel)
 
 	/* draw */
 	plat_video_menu_begin();
-	menu_draw_selection(x, y + asel * 10, w);
+	menu_draw_selection(x, y + asel * me_mfont_h, w);
+	x += me_mfont_w * 2;
 
 	for (ent = entries; ent->name; ent++)
 	{
@@ -361,16 +399,16 @@ static void me_draw(const menu_entry *entries, int sel)
 				name = ent->generate_name(ent->id, &offs);
 		}
 		if (name != NULL)
-			text_out16(x + 16, y, name);
+			text_out16(x, y, name);
 
 		switch (ent->beh) {
 		case MB_NONE:
 			break;
 		case MB_OPT_ONOFF:
-			text_out16(x + 16 + opt_offs, y, (*(int *)ent->var & ent->mask) ? "ON" : "OFF");
+			text_out16(x + opt_offs, y, (*(int *)ent->var & ent->mask) ? "ON" : "OFF");
 			break;
 		case MB_OPT_RANGE:
-			text_out16(x + 16 + opt_offs, y, "%i", *(int *)ent->var);
+			text_out16(x + opt_offs, y, "%i", *(int *)ent->var);
 			break;
 		case MB_OPT_CUSTOM:
 		case MB_OPT_CUSTONOFF:
@@ -380,25 +418,22 @@ static void me_draw(const menu_entry *entries, int sel)
 			if (ent->generate_name)
 				name = ent->generate_name(ent->id, &offs);
 			if (name != NULL)
-				text_out16(x + 16 + opt_offs + offs * 8, y, "%s", name);
+				text_out16(x + opt_offs + offs * me_mfont_w, y, "%s", name);
 			break;
 		}
 
-		y += 10;
+		y += me_mfont_h;
 	}
 
 	/* display message if we have one */
-	if (menuErrorMsg[0] != 0) {
-		static int msg_redraws = 0;
-		if (g_screen_height - h >= 2*10)
-			text_out16(5, 226, menuErrorMsg);
+	if (menu_error_msg[0] != 0) {
+		if (g_screen_height - h >= 2 * me_mfont_h)
+			text_out16(5, g_screen_height - me_mfont_h - 4, menu_error_msg);
 		else
 			lprintf("menu msg doesn't fit!\n");
 
-		if (++msg_redraws > 4) {
-			menuErrorMsg[0] = 0;
-			msg_redraws = 0;
-		}
+		if (plat_get_ticks_ms() - menu_error_time > 2048)
+			menu_error_msg[0] = 0;
 	}
 
 	plat_video_menu_end();
@@ -510,14 +545,14 @@ static void draw_menu_credits(void)
 		p++;
 	}
 
-	x = g_screen_width  / 2 - w *  8 / 2;
-	y = g_screen_height / 2 - h * 10 / 2;
+	x = g_screen_width  / 2 - w * me_mfont_w / 2;
+	y = g_screen_height / 2 - h * me_mfont_h / 2;
 	if (x < 0) x = 0;
 	if (y < 0) y = 0;
 
 	plat_video_menu_begin();
 
-	for (p = creds; *p != 0 && y <= g_screen_height - 10; y += 10) {
+	for (p = creds; *p != 0 && y <= g_screen_height - me_mfont_h; y += me_mfont_h) {
 		text_out16(x, y, p);
 
 		for (; *p != 0 && *p != '\n'; p++)
@@ -643,7 +678,7 @@ static void draw_dirlist(char *curdir, struct dirent **namelist, int n, int sel)
 			smalltext_out16(14,   pos*10, namelist[i+1]->d_name, color);
 		}
 	}
-	text_out16(5, max_cnt/2 * 10, ">");
+	smalltext_out16(5, max_cnt/2 * 10, ">", 0xffff);
 	plat_video_menu_end();
 }
 
@@ -930,8 +965,8 @@ static void draw_savestate_menu(int menu_sel, int is_loading)
 	if (state_slot_flags & (1 << menu_sel))
 		draw_savestate_bg(menu_sel);
 
-	w = 13 * 8 + 16;
-	h = (1+2+10+1) * 10;
+	w = 13 * me_mfont_w + me_mfont_w * 2;
+	h = (1+2+10+1) * me_mfont_h;
 	x = g_screen_width / 2 - w / 2;
 	if (x < 0) x = 0;
 	y = g_screen_height / 2 - h / 2;
@@ -940,12 +975,12 @@ static void draw_savestate_menu(int menu_sel, int is_loading)
 	plat_video_menu_begin();
 
 	text_out16(x, y, is_loading ? "Load state" : "Save state");
-	y += 3*10;
+	y += 3 * me_mfont_h;
 
-	menu_draw_selection(x - 16, y + menu_sel * 10, 13 * 8 + 4);
+	menu_draw_selection(x - me_mfont_w * 2, y + menu_sel * me_mfont_h, 13 * me_mfont_w + 4);
 
 	/* draw all 10 slots */
-	for (i = 0; i < 10; i++, y += 10)
+	for (i = 0; i < 10; i++, y += me_mfont_h)
 	{
 		text_out16(x, y, "SLOT %i (%s)", i, (state_slot_flags & (1 << i)) ? "USED" : "free");
 	}
@@ -984,7 +1019,7 @@ static int menu_loop_savestate(int is_loading)
 			if (menu_sel < 10) {
 				state_slot = menu_sel;
 				if (emu_SaveLoadGame(is_loading, 0)) {
-					strcpy(menuErrorMsg, is_loading ? "Load failed" : "Save failed");
+					me_update_msg(is_loading ? "Load failed" : "Save failed");
 					return 0;
 				}
 				return 1;
@@ -1056,42 +1091,45 @@ static int count_bound_keys(int dev_id, int action_mask, int player_idx)
 static void draw_key_config(const me_bind_action *opts, int opt_cnt, int player_idx,
 		int sel, int dev_id, int dev_count, int is_bind)
 {
-	int x, y = 30, w, i;
+	int x, y, w, i;
 	const char *dev_name;
 
-	x = g_screen_width / 2 - 32*8 / 2;
-	if (x < 0) x = 0;
+	x = g_screen_width / 2 - 20 * me_mfont_w / 2;
+	y = (g_screen_height - 4 * me_mfont_h) / 2 - (2 + opt_cnt) * me_mfont_h / 2;
+	if (x < me_mfont_w * 2)
+		x = me_mfont_w * 2;
 
 	plat_video_menu_begin();
 	if (player_idx >= 0)
-		text_out16(x, 10, "Player %i controls", player_idx + 1);
+		text_out16(x, y, "Player %i controls", player_idx + 1);
 	else
-		text_out16(x, 10, "Emulator controls");
+		text_out16(x, y, "Emulator controls");
 
-	menu_draw_selection(x - 16, y + sel*10, (player_idx >= 0) ? 66 : 140);
+	y += 2 * me_mfont_h;
+	menu_draw_selection(x - me_mfont_w * 2, y + sel * me_mfont_h, me_mfont_w); // FIXME last arg
 
-	for (i = 0; i < opt_cnt; i++, y+=10)
+	for (i = 0; i < opt_cnt; i++, y += me_mfont_h)
 		text_out16(x, y, "%s : %s", opts[i].name,
 			action_binds(player_idx, opts[i].mask, dev_id));
 
 	dev_name = in_get_dev_name(dev_id, 1, 1);
-	w = strlen(dev_name) * 8;
-	if (w < 30 * 8)
-		w = 30 * 8;
+	w = strlen(dev_name) * me_mfont_w;
+	if (w < 30 * me_mfont_w)
+		w = 30 * me_mfont_w;
 	if (w > g_screen_width)
 		w = g_screen_width;
 
 	x = g_screen_width / 2 - w / 2;
 
 	if (dev_count > 1) {
-		text_out16(x, g_screen_height - 4*10, "Viewing binds for:");
-		text_out16(x, g_screen_height - 3*10, dev_name);
+		text_out16(x, g_screen_height - 4 * me_mfont_h, "Viewing binds for:");
+		text_out16(x, g_screen_height - 3 * me_mfont_h, dev_name);
 	}
 
 	if (is_bind)
-		text_out16(x, g_screen_height - 2*10, "Press a button to bind/unbind");
+		text_out16(x, g_screen_height - 2 * me_mfont_h, "Press a button to bind/unbind");
 	else if (dev_count > 1)
-		text_out16(x, g_screen_height - 2*10, "Press left/right for other devs");
+		text_out16(x, g_screen_height - 2 * me_mfont_h, "Press left/right for other devs");
 
 	plat_video_menu_end();
 }
@@ -1501,15 +1539,15 @@ static int mh_saveloadcfg(menu_id id, int keys)
 	case MA_OPT_SAVECFG:
 	case MA_OPT_SAVECFG_GAME:
 		if (emu_WriteConfig(id == MA_OPT_SAVECFG_GAME ? 1 : 0))
-			strcpy(menuErrorMsg, "config saved");
+			me_update_msg("config saved");
 		else
-			strcpy(menuErrorMsg, "failed to write config");
+			me_update_msg("failed to write config");
 		break;
 	case MA_OPT_LOADCFG:
 		ret = emu_ReadConfig(1, 1);
 		if (!ret) ret = emu_ReadConfig(0, 1);
-		if (ret)  strcpy(menuErrorMsg, "config loaded");
-		else      strcpy(menuErrorMsg, "failed to load config");
+		if (ret)  me_update_msg("config loaded");
+		else      me_update_msg("failed to load config");
 		break;
 	default:
 		return 0;
@@ -1711,7 +1749,8 @@ static void debug_menu_loop(void)
 				emu_platformDebugCat(tmp);
 				draw_text_debug(tmp, 0, 0);
 				if (dumped) {
-					smalltext_out16(g_screen_width - 6*10, g_screen_height - 8, "dumped", 0xffff);
+					smalltext_out16(g_screen_width - 6 * me_mfont_h,
+						g_screen_height - me_mfont_h, "dumped", 0xffff);
 					dumped = 0;
 				}
 				break;
@@ -1833,7 +1872,7 @@ static int main_menu_handler(menu_id id, int keys)
 		if (rom_loaded && PicoPatches) {
 			menu_loop_patches();
 			PicoPatchApply();
-			strcpy(menuErrorMsg, "Patches applied");
+			me_update_msg("Patches applied");
 		}
 		break;
 	default:
@@ -1897,8 +1936,7 @@ static int mh_tray_load_cd(menu_id id, int keys)
 	if (cd_type != CIT_NOT_CD)
 		ret = Insert_CD(ret_name, cd_type);
 	if (ret != 0) {
-		sprintf(menuErrorMsg, "Load failed, invalid CD image?");
-		lprintf("%s\n", menuErrorMsg);
+		me_update_msg("Load failed, invalid CD image?");
 		return 0;
 	}
 
@@ -1943,6 +1981,15 @@ int menu_loop_tray(void)
 
 #endif // !UIQ3
 
+void me_update_msg(const char *msg)
+{
+	strncpy(menu_error_msg, msg, sizeof(menu_error_msg));
+	menu_error_msg[sizeof(menu_error_msg) - 1] = 0;
+
+	menu_error_time = plat_get_ticks_ms();
+	lprintf("msg: %s\n", menu_error_msg);
+}
+
 // ------------ util ------------
 
 /* TODO: rename */
diff --git a/common/menu.h b/common/menu.h
index c4aaaf0..3f77603 100644
--- a/common/menu.h
+++ b/common/menu.h
@@ -1,11 +1,4 @@
-// (c) Copyright 2006-2008 notaz, All rights reserved.
-
-
-void menu_init(void);
-void text_out16(int x, int y, const char *texto, ...);
-void smalltext_out16(int x, int y, const char *texto, int color);
-
-extern char menuErrorMsg[64];
+// (c) Copyright 2006-2009 notaz, All rights reserved.
 
 typedef enum
 {
@@ -159,6 +152,10 @@ typedef struct
 extern me_bind_action me_ctrl_actions[15];
 extern me_bind_action emuctrl_actions[];	// platform code
 
+void menu_init(void);
+void text_out16(int x, int y, const char *texto, ...);
+void me_update_msg(const char *msg);
+
 menu_entry *me_list_get_first(void);
 menu_entry *me_list_get_next(void);
 
diff --git a/gp2x/940ctl.c b/gp2x/940ctl.c
index d219d8a..77083c9 100644
--- a/gp2x/940ctl.c
+++ b/gp2x/940ctl.c
@@ -150,7 +150,7 @@ static void wait_busy_940(int job)
 		gp2x_memregs[0x3b46>>1], gp2x_memregl[0x4500>>2], gp2x_memregl[0x4510>>2]);
 	printf("last lr: %08x, lastjob: %i\n", shared_ctl->last_lr, shared_ctl->lastjob);
 
-	strcpy(menuErrorMsg, "940 crashed, too much overclock?");
+	me_update_msg("940 crashed, too much overclock?");
 	engineState = PGS_Menu;
 	crashed_940 = 1;
 }
diff --git a/gp2x/port_config.h b/gp2x/port_config.h
index ba7acee..94b7bfe 100644
--- a/gp2x/port_config.h
+++ b/gp2x/port_config.h
@@ -33,5 +33,6 @@
 #define PLAT_HAVE_JOY 1
 #define PATH_SEP      "/"
 #define PATH_SEP_C    '/'
+#define MENU_X2       0
 
 #endif //PORT_CONFIG_H
diff --git a/linux/port_config.h b/linux/port_config.h
index a8048e4..8c8eca3 100644
--- a/linux/port_config.h
+++ b/linux/port_config.h
@@ -38,6 +38,7 @@
 #define PLAT_HAVE_JOY 1
 #define PATH_SEP      "/"
 #define PATH_SEP_C    '/'
+#define MENU_X2       0
 
 #endif //PORT_CONFIG_H
 
diff --git a/pandora/port_config.h b/pandora/port_config.h
index b82b89c..02bc39c 100644
--- a/pandora/port_config.h
+++ b/pandora/port_config.h
@@ -33,5 +33,6 @@
 #define PLAT_HAVE_JOY 1
 #define PATH_SEP      "/"
 #define PATH_SEP_C    '/'
+#define MENU_X2       1
 
 #endif //PORT_CONFIG_H
diff --git a/psp/port_config.h b/psp/port_config.h
index a59692c..7d543f1 100644
--- a/psp/port_config.h
+++ b/psp/port_config.h
@@ -37,5 +37,6 @@ extern void blit1(void);
 #define PLAT_HAVE_JOY 0
 #define PATH_SEP      "/"
 #define PATH_SEP_C    '/'
+#define MENU_X2       0
 
 #endif //PORT_CONFIG_H
-- 
2.39.5