/* テスト */
/* 
 * Debug Status: Not checked.
 * - Fixed __T on this page.
 */
/*
 * File: cmd4.c
 * Purpose: Various kinds of browsing functions.
 *
 * Copyright (c) 1997-2007 Robert A. Koeneke, James E. Wilson, Ben Harrison,
 * Eytan Zweig, Andrew Doull, Pete Mack.
 * HTML dump code (c) 2004 DarkGod 
 *
 * This work is free software; you can redistribute it and/or modify it
 * under the terms of either:
 *
 * a) the GNU General Public License as published by the Free Software
 *    Foundation, version 2, or
 *
 * b) the "Angband licence":
 *    This software may be copied and distributed for educational, research,
 *    and not for profit purposes provided that this copyright and statement
 *    are included in all such copies.  Other copyrights may also apply.
 */
#include "angband.h"
#include "object/tvalsval.h"
#include "cmds.h"
#include "option.h"
#include "ui.h"
#include "externs.h"
#include "ui-menu.h"

static void dump_pref_file(void (*dump)(ang_file *), const _TCHAR *title, int row)
{
	_TCHAR ftmp[80];
	_TCHAR buf[1024];

	/* Prompt */
	prt(format(__T("%s to a pref file"), title), row, 0);
	
	/* Prompt */
	prt(__T("File: "), row + 2, 0);
	
	/* Default filename */
	strnfmt(ftmp, sizeof ftmp, __T("%s.prf"), op_ptr->base_name);
	
	/* Get a filename */
	if (!askfor_aux(ftmp, sizeof ftmp, NULL)) return;

	/* Build the filename */
	path_build(buf, _countof(buf), ANGBAND_DIR_USER, ftmp);
	
	if (!prefs_save(buf, dump, title))
	{
		prt(__T(""), 0, 0);
		msg_print(__T("Failed"));
		return;
	}
	/* Message */
	prt(__T(""), 0, 0);
	msg_print(format(__T("Dumped %s"), _tcsstr(title, __T(" ")) + 1));
}

static void do_cmd_pref_file_hack(long row);

/* Flag value for missing array entry */
#define MISSING -17

#define APP_MACRO	101
#define ASK_MACRO	103
#define DEL_MACRO	104
#define NEW_MACRO	105
#define APP_KEYMAP	106
#define ASK_KEYMAP	107
#define DEL_KEYMAP	108
#define NEW_KEYMAP	109
#define ENTER_ACT	110
#define LOAD_PREF	111
#define DUMP_MON	112
#define DUMP_OBJ	113
#define DUMP_FEAT	114
#define DUMP_FLAV	115
#define DUMP_COL	120
#define MOD_COL		121
#define RESET_VIS	122

#define INFO_SCREENS 2 /* Number of screens in character info mode */

/*
 * Hack -- redraw the screen
 *
 * This command performs various low level updates, clears all the "extra"
 * windows, does a total redraw of the main window, and requests all of the
 * interesting updates and redraws that I can think of.
 *
 * This command is also used to "instantiate" the results of the user
 * selecting various things, such as graphics mode, so it must call
 * the "TERM_XTRA_REACT" hook before redrawing the windows.
 *
 */
void do_cmd_redraw(void)
{
	int j;

	term *old = Term;

	/* Low level flush */
	Term_flush();

	/* Reset "inkey()" */
	flush();
	
	if (character_dungeon)
		verify_panel();

	/* Hack -- React to changes */
	Term_xtra(TERM_XTRA_REACT, 0);

	/* Combine and Reorder the pack (later) */
	p_ptr->notice |= (PN_COMBINE | PN_REORDER);

	/* Update torch */
	p_ptr->update |= (PU_TORCH);

	/* Update stuff */
	p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS);

	/* Fully update the visuals */
	p_ptr->update |= (PU_FORGET_VIEW | PU_UPDATE_VIEW | PU_MONSTERS);

	/* Redraw everything */
	p_ptr->redraw |= (PR_BASIC | PR_EXTRA | PR_MAP | PR_INVEN | PR_EQUIP |
	                  PR_MESSAGE | PR_MONSTER | PR_OBJECT |
					  PR_MONLIST | PR_ITEMLIST);

	/* Clear screen */
	Term_clear();

	/* Hack -- update */
	handle_stuff();

	/* Place the cursor on the player */
	if (0 != character_dungeon)
		move_cursor_relative(p_ptr->px, p_ptr->py);

	/* Redraw every window */
	for (j = 0; j < ANGBAND_TERM_MAX; j++)
	{
		/* Dead window */
		if (!angband_term[j]) continue;

		/* Activate */
		Term_activate(angband_term[j]);

		/* Redraw */
		Term_redraw();

		/* Refresh */
		Term_fresh();

		/* Restore */
		Term_activate(old);
	}
}

/*
 * Hack -- change name
 */
void do_cmd_change_name(void)
{
	ui_event_data ke;

	int mode = 0;

	const _TCHAR *p;

	/* Prompt */
	p = __T("['c' to change name, 'f' to file, 'h' to change mode, or ESC]");

	/* Save screen */
	screen_save();

	/* Forever */
	while (1)
	{
		/* Display the player */
		display_player(mode);

		/* Prompt */
		Term_putstr(2, 23, -1, TERM_WHITE, p);

		/* Query */
		ke = inkey_ex();

		/* Exit */
		if (ke.key == ESCAPE) 
			break;

		/* Change name */
		if ((ke.key == 'c') || ((ke.key == MOUSEY) && (ke.mousey == 2) && (ke.mousex < 26))) 
		{
			_TCHAR namebuf[32] = __T("");

			if (get_name(namebuf, sizeof namebuf))
			{
				/* Set player name */
				_tcscpy_s(op_ptr->full_name, _countof(op_ptr->full_name), namebuf);

				/* Don't change savefile name. */
				process_player_name(FALSE);
			}
		}
		/* File dump */
		else if (ke.key == 'f')
		{
			_TCHAR buf[1024];
			_TCHAR fname[80];

			strnfmt(fname, sizeof fname, __T("%s.txt"), op_ptr->base_name);

			if (get_file(fname, buf, sizeof buf))
			{
				if (file_character(buf, FALSE) != 0)
					msg_print(__T("Character dump failed!"));
				else
					msg_print(__T("Character dump successful."));
			}
		}
		/* Toggle mode */
		else if ((ke.key == 'h') || (ke.key == MOUSEY) || 
		         (ke.key == ARROW_LEFT) || (ke.key == ' '))
		{
			mode = (mode + 1) % INFO_SCREENS;
		}
		/* Toggle mode */
		else if ((ke.key == 'l') || ke.key == ARROW_RIGHT)
		{
			mode = (mode - 1) % INFO_SCREENS;
		}
		else /* Oops */
		{
			bell(NULL);
		}
		/* Flush messages */
		message_flush();
	}
	/* Load screen */
	screen_load();
}

/*
 * Recall the most recent message
 */
void do_cmd_message_one(void)
{
	/* Recall one message XXX XXX XXX */
	c_prt(message_color(0), format( __T("> %s"), message_str(0)), 0, 0);
}

/*
 * Show previous messages to the user
 *
 * The screen format uses line 0 and 23 for headers and prompts,
 * skips line 1 and 22, and uses line 2 thru 21 for old messages.
 *
 * This command shows you which commands you are viewing, and allows
 * you to "search" for strings in the recall.
 *
 * Note that messages may be longer than 80 characters, but they are
 * displayed using "infinite" length, with a special sub-command to
 * "slide" the virtual display to the left or right.
 *
 * Attempt to only hilite the matching portions of the string.
 */
void do_cmd_messages(void)
{
	ui_event_data ke;

	int i, j, n, q;
	int wid, hgt;

	_TCHAR shower[80] = __T("");

	/* Total messages */
	n = messages_num();

	/* Start on first message */
	i = 0;

	/* Start at leftmost edge */
	q = 0;

	/* Get size */
	Term_get_size(&wid, &hgt);

	/* Save screen */
	screen_save();

	/* Process requests until done */
	while (1)
	{
		/* Clear screen */
		Term_clear();

		/* Dump messages */
		for (j = 0; (j < hgt - 4) && (i + j < n); j++)
		{
			const _TCHAR *msg;
			const _TCHAR *str = message_str(i + j);
			byte attr = message_color(i + j);
			u16b count = message_count(i + j);

			if (count == 1)
				msg = str;
			else
				msg = format(__T("%s <%dx>"), str, count);

			/* Apply horizontal scroll */
			msg = ((int)_tcslen(msg) >= q) ? (msg + q) : __T("");

			/* Dump the messages, bottom to top */
			Term_putstr(0, hgt - 3 - j, -1, attr, msg);

			/* Hilite "shower" */
			if (shower[0])
			{
				str = msg;

				/* Display matches */
				while ((str = my_stristr(str, shower)) != NULL)
				{
					int len = _tcslen(shower);

					/* Display the match */
					Term_putstr(str-msg, hgt - 3 - j, len, TERM_YELLOW, str);

					/* Advance */
					str += len;
				}
			}
		}
		/* Display header */
		prt(format(__T("Message recall (%d-%d of %d), offset %d"), i, i + j - 1, n, q), 0, 0);

		/* Display prompt (not very informative) */
		if (shower[0])
		    prt(__T("[Movement keys to navigate, '-' for next, '=' to find]"), hgt - 1, 0);
		else
			prt(__T("[Movement keys to navigate, '=' to find, or ESCAPE to exit]"), hgt - 1, 0);
			
		/* Get a command */
		ke = inkey_ex();

		/* Exit on Escape */
		if (ke.key == ESCAPE)
		{
			break;
		}
		/* Find text */
		else if (ke.key == '=')
		{
			/* Get the string to find */
			prt(__T("Find: "), hgt - 1, 0);
			if (!askfor_aux(shower, sizeof shower, NULL)) continue;

			/* Set to find */
			ke.key = '-';
		}
		/* Horizontal scroll */
		else if (ke.key == '4' || ke.key == ARROW_LEFT)
		{
			/* Scroll left */
			q = (q >= wid / 2) ? (q - wid / 2) : 0;

			/* Success */
			continue;
		}
		/* Horizontal scroll */
		else if (ke.key == '6'|| ke.key == ARROW_RIGHT)
		{
			/* Scroll right */
			q = q + wid / 2;

			/* Success */
			continue;
		}
		/* Recall 1 older message */
		else if (ke.key == '8' || ke.key == ARROW_UP)
		{
			/* Go older if legal */
			if (i + 1 < n) i += 1;
		}
		/* Recall 1 newer messages */
		else if (ke.key == '2' || ke.key == ARROW_DOWN || ke.key == '\r' || ke.key == '\n')
		{
			/* Go newer (if able) */
			i = (i >= 1) ? (i - 1) : 0;
		}
		/* Recall 20 older messages */
		else if ((ke.key == 'p') || (ke.key == KTRL('P')) || (ke.key == ' '))
		{
			/* Go older if legal */
			if (i + 20 < n) i += 20;
		}
		/* Recall 20 newer messages */
		else if ((ke.key == 'n') || (ke.key == KTRL('N')))
		{
			/* Go newer (if able) */
			i = (i >= 20) ? (i - 20) : 0;
		}
		/* Scroll forwards or backwards using mouse clicks */
		else if (ke.key == MOUSEY) 
		{
			if (ke.index)
			{
				if (ke.mousey <= hgt / 2)
				{
					/* Go older if legal */
					if (i + 20 < n) i += 20;
				}
				else
				{
					/* Go newer (if able) */
					i = (i >= 20) ? (i - 20) : 0;
				}
			}
		}
		else /* Error time */
		{
			bell(NULL);
		}
		/* Find the next item */
		if (ke.key == '-' && shower[0])
		{
			s16b z;

			/* Scan messages */
			for (z = i + 1; z < n; z++)
			{
				/* Search for it */
				if (my_stristr(message_str(z), shower))
				{
					/* New location */
					i = z;

					/* Done */
					break;
				}
			}
		}
	}
	/* Load screen */
	screen_load();
}

/*** Options display and setting ***/

/*
 * Displays an option entry.
 */
static void display_option(menu_type *menu, int oid,
		bool cursor, int row, int col, int width)
{
	byte attr = curs_attrs[CURS_KNOWN][(int)cursor];
	UNREFERENCED_PARAMETER(menu);
	UNREFERENCED_PARAMETER(width);

	c_prt(attr, format(__T("%-45s: %s  (%s)"), option_desc(oid),
			op_ptr->opt[oid] ? __T("yes") : __T("no "), option_name(oid)),
			row, col);
}

/*
 * Handle keypresses for an option entry.
 */
static bool update_option(_TCHAR key, void *pgdb, int oid)
{
	(void)pgdb;

	/* Ignore arrow events */
	if (key == ARROW_LEFT || key == ARROW_RIGHT)
		return TRUE;

	switch (_totupper(key))
	{
		case 'Y':
		{
			op_ptr->opt[oid] = TRUE;
			break;
		}
		case 'N':
		{
			op_ptr->opt[oid] = FALSE;
			break;
		}
		case '?':
		{
			show_file(format(__T("option.txt#%s"), option_name(oid)), NULL, 0, 0);
			break;
		}
		default:
		{
			op_ptr->opt[oid] = !op_ptr->opt[oid];
			break;
		}

	}
	return TRUE;
}

static const menu_iter options_toggle_iter =
{
	NULL,
	NULL,
	display_option,		/* label */
	update_option		/* updater */
};

static menu_type option_toggle_menu;

/*
 * Interact with some options
 */
static void do_cmd_options_aux(void *vpage, const _TCHAR *info)
{
	int page = (int)vpage;
	int opt[OPT_PAGE_PER];
	int i, n = 0;
	int cursor_pos = 0;

	menu_type *menu = &option_toggle_menu;
	menu->title = info;
	menu_layout(menu, &SCREEN_REGION);

	screen_save();
	clear_from(0);

	/* Filter the options for this page */
	for (i = 0; i < OPT_PAGE_PER; i++)
	{
		if (option_page[page][i] != OPT_NONE)
			opt[n++] = option_page[page][i];
	}
	menu_set_filter(menu, opt, n);
	menu->menu_data = vpage;

	menu_layout(menu, &SCREEN_REGION);

	while (TRUE)
	{
		ui_event_data cx;

		cx = menu_select(menu, &cursor_pos, EVT_MOVE);

		if (cx.key == ESCAPE)
			break;
		else if (cx.type == EVT_MOVE)
			cursor_pos = cx.index;
		else if (cx.type == EVT_SELECT && _tcschr(__T("YN"), _totupper(cx.key)))
			cursor_pos++;

		cursor_pos = (cursor_pos+n) % n;
	}
	/* Hack -- Notice use of any "cheat" options */
	for (i = OPT_CHEAT; i < OPT_ADULT; i++)
	{
		if (op_ptr->opt[i])
		{
			/* Set score option */
			op_ptr->opt[OPT_SCORE + (i - OPT_CHEAT)] = TRUE;
		}
	}
	screen_load();
}


/*
 * Modify the "window" options
 */
static void do_cmd_options_win(void)
{
	int i, j, d;

	int y = 0;
	int x = 0;

	ui_event_data ke;

	u32b new_flags[ANGBAND_TERM_MAX];

	/* Set new flags to the old values */
	for (j = 0; j < ANGBAND_TERM_MAX; j++)
	{
		new_flags[j] = op_ptr->window_flag[j];
	}
	/* Clear screen */
	clear_from(0);

	/* Interact */
	while (1)
	{
		/* Prompt */
		prt(__T("Window flags (<dir> to move, 't'/Enter to toggle, or ESC)"), 0, 0);

		/* Display the windows */
		for (j = 0; j < ANGBAND_TERM_MAX; j++)
		{
			byte a = TERM_WHITE;

			const _TCHAR *s = angband_term_name[j];

			/* Use color */
			if (j == x) a = TERM_L_BLUE;

			/* Window name, staggered, centered */
			Term_putstr(35 + j * 5 - _tcslen(s) / 2, 2 + j % 2, -1, a, s);
		}
		/* Display the options */
		for (i = 0; i < PW_MAX_FLAGS; i++)
		{
			byte a = TERM_WHITE;

			const _TCHAR *str = window_flag_desc[i];

			/* Use color */
			if (i == y) 
				a = TERM_L_BLUE;

			/* Unused option */
			if (!str) 
				str = __T("(Unused option)");

			/* Flag name */
			Term_putstr(0, i + 5, -1, a, str);

			/* Display the windows */
			for (j = 0; j < ANGBAND_TERM_MAX; j++)
			{
				char c = '.';

				a = TERM_WHITE;

				/* Use color */
				if ((i == y) && (j == x)) a = TERM_L_BLUE;

				/* Active flag */
				if (new_flags[j] & (1L << i)) c = 'X';

				/* Flag value */
				Term_putch(35 + j * 5, i + 5, a, c);
			}
		}

		/* Place Cursor */
		Term_gotoxy(35 + x * 5, y + 5);

		/* Get key */
		ke = inkey_ex();

		/* Allow escape */
		if ((ke.key == ESCAPE) || (ke.key == 'q')) 
			break;

		/* Mouse interaction */
		if (ke.key == MOUSEY) 
		{
			int choicey = ke.mousey - 5;
			int choicex = (ke.mousex - 35)/5;

			if ((choicey >= 0) && (choicey < PW_MAX_FLAGS)
				&& (choicex > 0) && (choicex < ANGBAND_TERM_MAX)
				&& !(ke.mousex % 5))
			{
				y = choicey;
				x = (ke.mousex - 35)/5;
			}

			/* Toggle using mousebutton later */
			if (!ke.index) continue;
		}
		/* Toggle */
		if ((ke.key == '5') || (ke.key == 't') || (ke.key == '\n') || (ke.key == '\r') || ((ke.key == MOUSEY) && (ke.index)))
		{
			/* Hack -- ignore the main window */
			if (x == 0)
			{
				bell(__T("Cannot set main window flags!"));
			}
			/* Toggle flag (off) */
			else if (new_flags[x] & (1L << y))
			{
				new_flags[x] &= ~(1L << y);
			}
			else /* Toggle flag (on) */
			{
				new_flags[x] |= (1L << y);
			}

			/* Continue */
			continue;
		}
		/* Extract direction */
		d = target_dir(ke.key);

		/* Move */
		if (d != 0)
		{
			x = (x + ddx[d] + 8) % ANGBAND_TERM_MAX;
			y = (y + ddy[d] + 16) % PW_MAX_FLAGS;
		}
		else /* Oops */
		{
			bell(__T("Illegal command for window options!"));
		}
	}
	/* Notice changes */
	subwindows_set_flags(new_flags, ANGBAND_TERM_MAX);
}

#ifdef ALLOW_MACROS

/*
 * Hack -- ask for a "trigger" (see below)
 *
 * Note the complex use of the "inkey()" function from "util.c".
 *
 * Note that both "flush()" calls are extremely important.  This may
 * no longer be true, since "util.c" is much simpler now.  XXX XXX XXX
 */
static void do_cmd_macro_aux(_TCHAR *buf)
{
	_TCHAR ch;

	int n = 0;
	int curs_x, curs_y;

	_TCHAR tmp[1024] = __T("");

	/* Get cursor position */
	Term_locate(&curs_x, &curs_y);

	/* Flush */
	flush();

	/* Do not process macros */
	inkey_base = TRUE;

	/* First key */
	ch = inkey();

	/* Read the pattern */
	while (ch != 0 && ch != MOUSEY) 
	{
		/* Save the key */
		buf[n++] = ch;
		buf[n] = 0;

		/* Get representation of the sequence so far */
		ascii_to_text(tmp, _countof(tmp), buf);

		/* Echo it after the prompt */
		Term_erase(curs_x, curs_y, 80);
		Term_gotoxy(curs_x, curs_y);
		Term_addstr(-1, TERM_WHITE, tmp);
		
		/* Do not process macros */
		inkey_base = TRUE;

		/* Do not wait for keys */
		inkey_scan = SCAN_INSTANT;

		/* Attempt to read a key */
		ch = inkey();
	}
	/* Convert the trigger */
	ascii_to_text(tmp, _countof(tmp), buf);
}

/*
 * Hack -- ask for a keymap "trigger" (see below)
 *
 * Note that both "flush()" calls are extremely important.  This may
 * no longer be true, since "util.c" is much simpler now.  XXX XXX XXX
 */
static void do_cmd_macro_aux_keymap(_TCHAR *buf)
{
	_TCHAR tmp[1024];

	/* Flush */
	flush();

	/* Get a key */
	buf[0] = inkey();
	buf[1] = 0;

	/* Convert to ascii */
	ascii_to_text(tmp, _countof(tmp), buf);

	/* Hack -- display the trigger */
	Term_addstr(-1, TERM_WHITE, tmp);

	/* Flush */
	flush();
}

#endif

/*
 * Interact with "macros"
 *
 * Could use some helpful instructions on this page.  XXX XXX XXX
 * CLEANUP
 */
static menu_action macro_actions[] =
{
	{ LOAD_PREF,  __T("Load a user pref file"),    0, 0 },
#ifdef ALLOW_MACROS
	{ APP_MACRO,  __T("Append macros to a file"),  0, 0 },
	{ ASK_MACRO,  __T("Query a macro"),            0, 0 },
	{ NEW_MACRO,  __T("Create a macro"),           0, 0 },
	{ DEL_MACRO,  __T("Remove a macro"),           0, 0 },
	{ APP_KEYMAP, __T("Append keymaps to a file"), 0, 0 },
	{ ASK_KEYMAP, __T("Query a keymap"),           0, 0 },
	{ NEW_KEYMAP, __T("Create a keymap"),          0, 0 },
	{ DEL_KEYMAP, __T("Remove a keymap"),          0, 0 },
	{ ENTER_ACT,  __T("Enter a new action"),       0, 0 },
#endif /* ALLOW_MACROS */
};

static menu_type macro_menu;


void do_cmd_macros(void)
{
	_TCHAR tmp[1024];

	_TCHAR pat[1024];

	int mode;
	int cursor = 0;

	region loc = {0, 0, 0, 12};

	if (OPT(rogue_like_commands))
		mode = KEYMAP_MODE_ROGUE;
	else
		mode = KEYMAP_MODE_ORIG;

	screen_save();

	menu_layout(&macro_menu, &loc);

	/* Process requests until done */
	while (1)
	{
		ui_event_data c;
		int evt;

		/* Clear screen */
		clear_from(0);

		/* Describe current action */
		prt(__T("Current action (if any) shown below:"), 13, 0);

		/* Analyze the current action */
		ascii_to_text(tmp, _countof(tmp), macro_buffer);

		/* Display the current action */
		prt(tmp, 14, 0);
		c = menu_select(&macro_menu, &cursor, EVT_CMD);

		if (ESCAPE == c.key) break;
		if (c.key == ARROW_LEFT || c.key == ARROW_RIGHT) continue;
		evt = macro_actions[cursor].id;

		switch(evt)
		{
		case LOAD_PREF:
		{
			do_cmd_pref_file_hack(16);
			break;
		}
#ifdef ALLOW_MACROS
		case APP_MACRO:
		{
			/* Dump the macros */
			(void)dump_pref_file(macro_dump, __T("Dump Macros"), 15);

			break;
		}
		case ASK_MACRO:
		{
			int k;

			/* Prompt */
			prt(__T("Command: Query a macro"), 16, 0);

			/* Prompt */
			prt(__T("Trigger: "), 18, 0);

			/* Get a macro trigger */
			do_cmd_macro_aux(pat);

			/* Get the action */
			k = macro_find_exact(pat);

			/* Nothing found */
			if (k < 0)
			{
				/* Prompt */
				prt(__T(""), 0, 0);
				msg_print(__T("Found no macro."));
			}
			/* Found one */
			else
			{
				/* Obtain the action */
				_tcscpy_s(macro_buffer, _countof(macro_buffer), macro__act[k]);

				/* Analyze the current action */
				ascii_to_text(tmp, _countof(tmp), macro_buffer);

				/* Display the current action */
				prt(tmp, 22, 0);

				/* Prompt */
				prt(__T(""), 0, 0);
				msg_print(__T("Found a macro."));
			}
			break;
		}
		case NEW_MACRO:
		{
			/* Prompt */
			prt(__T("Command: Create a macro"), 16, 0);

			/* Prompt */
			prt(__T("Trigger: "), 18, 0);

			/* Get a macro trigger */
			do_cmd_macro_aux(pat);

			/* Clear */
			clear_from(20);

			/* Prompt */
			prt(__T("Action: "), 20, 0);

			/* Convert to text */
			ascii_to_text(tmp, _countof(tmp), macro_buffer);

			/* Get an encoded action */
			if (askfor_aux(tmp, sizeof tmp, NULL))
			{
				/* Convert to ascii */
				text_to_ascii(macro_buffer, _countof(macro_buffer), tmp);

				/* Link the macro */
				macro_add(pat, macro_buffer);

				/* Prompt */
				prt(__T(""), 0, 0);
				msg_print(__T("Added a macro."));
			}
			break;
		}
		case DEL_MACRO:
		{
			/* Prompt */
			prt(__T("Command: Remove a macro"), 16, 0);

			/* Prompt */
			prt(__T("Trigger: "), 18, 0);

			/* Get a macro trigger */
			do_cmd_macro_aux(pat);

			/* Link the macro */
			macro_add(pat, pat);

			/* Prompt */
			prt(__T(""), 0, 0);
			msg_print(__T("Removed a macro."));
			break;
		}
		case APP_KEYMAP:
		{
			/* Dump the keymaps */
			(void)dump_pref_file(keymap_dump, __T("Dump Keymaps"), 15);
			break;
		}
		case ASK_KEYMAP:
		{
			const _TCHAR *act;

			/* Prompt */
			prt(__T("Command: Query a keymap"), 16, 0);

			/* Prompt */
			prt(__T("Keypress: "), 18, 0);

			/* Get a keymap trigger */
			do_cmd_macro_aux_keymap(pat);

			/* Look up the keymap */
			act = keymap_act[mode][pat[0]];

			/* Nothing found */
			if (!act)
			{
				/* Prompt */
				prt(__T(""), 0, 0);
				msg_print(__T("Found no keymap."));
			}
			/* Found one */
			else
			{
				/* Obtain the action */
				_tcscpy_s(macro_buffer, _countof(macro_buffer), act);

				/* Analyze the current action */
				ascii_to_text(tmp, _countof(tmp), macro_buffer);

				/* Display the current action */
				prt(tmp, 22, 0);

				/* Prompt */
				prt(__T(""), 0, 0);
				msg_print(__T("Found a keymap."));
			}
			break;
		}
		case NEW_KEYMAP:
		{
			/* Prompt */
			prt(__T("Command: Create a keymap"), 16, 0);

			/* Prompt */
			prt(__T("Keypress: "), 18, 0);

			/* Get a keymap trigger */
			do_cmd_macro_aux_keymap(pat);

			/* Clear */
			clear_from(20);

			/* Prompt */
			prt(__T("Action: "), 20, 0);

			/* Convert to text */
			ascii_to_text(tmp, _countof(tmp), macro_buffer);

			/* Get an encoded action */
			if (askfor_aux(tmp, sizeof tmp, NULL))
			{
				/* Convert to ascii */
				text_to_ascii(macro_buffer, _countof(macro_buffer), tmp);

				/* Free old keymap */
				string_free(keymap_act[mode][pat[0]]);

				/* Make new keymap */
				keymap_act[mode][pat[0]] = _tcsdup(macro_buffer);

				/* Prompt */
				prt(__T(""), 0, 0);
				msg_print(__T("Added a keymap."));
			}
			break;
		}
		case DEL_KEYMAP:
		{
			/* Prompt */
			prt(__T("Command: Remove a keymap"), 16, 0);

			/* Prompt */
			prt(__T("Keypress: "), 18, 0);

			/* Get a keymap trigger */
			do_cmd_macro_aux_keymap(pat);

			/* Free old keymap */
			string_free(keymap_act[mode][pat[0]]);

			/* Make new keymap */
			keymap_act[mode][pat[0]] = NULL;

			/* Prompt */
			prt(__T(""), 0, 0);
			msg_print(__T("Removed a keymap."));
			break;
		}
		case ENTER_ACT: /* Enter a new action */
		{
			/* Prompt */
			prt(__T("Command: Enter a new action"), 16, 0);

			/* Go to the correct location */
			Term_gotoxy(0, 22);

			/* Analyze the current action */
			ascii_to_text(tmp, _countof(tmp), macro_buffer);

			/* Get an encoded action */
			if (askfor_aux(tmp, sizeof tmp, NULL))
			{
				/* Extract an action */
				text_to_ascii(macro_buffer, _countof(macro_buffer), tmp);
			}
			break;
		}
#endif /* ALLOW_MACROS */
		}
		/* Flush messages */
		message_flush();
	}
	/* Load screen */
	screen_load();
}

menu_action visual_menu_items [] =
{
	{ LOAD_PREF, __T("Load a user pref file"), 0, 0},
	{ DUMP_MON,  __T("Dump monster attr/chars"), 0, 0},
	{ DUMP_OBJ,  __T("Dump object attr/chars"), 0, 0 },
	{ DUMP_FEAT, __T("Dump feature attr/chars"), 0, 0 },
	{ DUMP_FLAV, __T("Dump flavor attr/chars"), 0, 0 },
	{ RESET_VIS, __T("Reset visuals"), 0, 0 },
};

static menu_type visual_menu;

/*
 * Interact with "visuals"
 */
void do_cmd_visuals(void)
{
	int cursor = 0;

	/* Save screen */
	screen_save();

	menu_layout(&visual_menu, &SCREEN_REGION);

	/* Interact until done */
	while (1)
	{
		ui_event_data key;
		int evt = -1;
		clear_from(0);
		key = menu_select(&visual_menu, &cursor, EVT_CMD);
		if (key.key == ESCAPE) 
			break;

		if (key.key == ARROW_LEFT || key.key == ARROW_RIGHT)
			continue;

		assert(cursor >= 0 && cursor < visual_menu.count);

		evt = visual_menu_items[cursor].id;

		if (evt == LOAD_PREF)
		{
			/* Ask for and load a user pref file */
			do_cmd_pref_file_hack(15);
		}
#ifdef ALLOW_VISUALS
		else if (evt == DUMP_MON)
		{
			dump_pref_file(dump_monsters, __T("Dump Monster attr/chars"), 15);
		}
		else if (evt == DUMP_OBJ)
		{
			dump_pref_file(dump_objects, __T("Dump Object attr/chars"), 15);
		}
		else if (evt == DUMP_FEAT)
		{
			dump_pref_file(dump_features, __T("Dump Feature attr/chars"), 15);
		}
		/* Dump flavor attr/chars */
		else if (evt == DUMP_FLAV) 
		{
			dump_pref_file(dump_flavors, __T("Dump Flavor attr/chars"), 15);
		}
#endif /* ALLOW_VISUALS */

		/* Reset visuals */
		else if (evt == RESET_VIS)
		{
			/* Reset */
			reset_visuals(TRUE);

			/* Message */
			prt(__T(""), 0, 0);
			msg_print(__T("Visual attr/char tables reset."));
		}
		message_flush();
	}
	/* Load screen */
	screen_load();
}

static menu_action color_events [] =
{
	{LOAD_PREF, __T("Load a user pref file"), 0, 0},
#ifdef ALLOW_COLORS
	{DUMP_COL, __T("Dump colors"), 0, 0},
	{MOD_COL, __T("Modify colors"), 0, 0}
#endif
};

static menu_type color_menu;

/*
 * Interact with "colors"
 */
void do_cmd_colors(void)
{
	int i;
	int cx;
	int cursor = 0;

	screen_save();

	menu_layout(&color_menu, &SCREEN_REGION);

	/* Interact until done */
	while (1)
	{
		ui_event_data key;
		int evt;
		clear_from(0);
		key = menu_select(&color_menu, &cursor, EVT_CMD);

		/* Done */
		if (key.key == ESCAPE) break;

		if (key.key == ARROW_RIGHT || key.key == ARROW_LEFT) continue;

		evt = color_events[cursor].id;

		/* Load a user pref file */
		if (evt == LOAD_PREF)
		{
			/* Ask for and load a user pref file */
			do_cmd_pref_file_hack(8);

			/* Could skip the following if loading cancelled XXX XXX XXX */

			/* Mega-Hack -- React to color changes */
			Term_xtra(TERM_XTRA_REACT, 0);

			/* Mega-Hack -- Redraw physical windows */
			Term_redraw();
		}
#ifdef ALLOW_COLORS
		/* Dump colors */
		else if (evt == DUMP_COL)
		{
			dump_pref_file(dump_colors, __T("Dump Colors"), 15);
		}
		/* Edit colors */
		else if (evt == MOD_COL)
		{
			static byte a = 0;

			/* Prompt */
			prt(__T("Command: Modify colors"), 8, 0);

			/* Hack -- query until done */
			while (1)
			{
				/* Describe the color */
				const _TCHAR *name = ((a < BASIC_COLORS) ? color_names[a] : __T("undefined"));;

				/* Clear */
				clear_from(10);

				/* Exhibit the normal colors */
				for (i = 0; i < BASIC_COLORS; i++)
				{
					/* Exhibit this color */
					Term_putstr(i*4, 20, -1, a, __T("###"));

					/* Exhibit all colors */
					Term_putstr(i*4, 22, -1, (byte)i, format(__T("%3d"), i));
				}
				/* Describe the color */
				Term_putstr(5, 10, -1, TERM_WHITE,
					    format(__T("Color = %d, Name = %s"), a, name));

				/* Label the Current values */
				Term_putstr(5, 12, -1, TERM_WHITE,
						format(__T("K = 0x%02x / R,G,B = 0x%02x,0x%02x,0x%02x"),
						angband_color_table[a][0],
						angband_color_table[a][1],
						angband_color_table[a][2],
						angband_color_table[a][3]));

				/* Prompt */
				Term_putstr(0, 14, -1, TERM_WHITE,
						__T("Command (n/N/k/K/r/R/g/G/b/B): "));

				/* Get a command */
				cx = inkey();

				/* All done */
				if (cx == ESCAPE) break;

				/* Analyze */
				if (cx == 'n') 
					a = (byte)(a + 1);
				if (cx == 'N') 
					a = (byte)(a - 1);
				if (cx == 'k') 
					angband_color_table[a][0] = (byte)(angband_color_table[a][0] + 1);
				if (cx == 'K') 
					angband_color_table[a][0] = (byte)(angband_color_table[a][0] - 1);
				if (cx == 'r') 
					angband_color_table[a][1] = (byte)(angband_color_table[a][1] + 1);
				if (cx == 'R') 
					angband_color_table[a][1] = (byte)(angband_color_table[a][1] - 1);
				if (cx == 'g') 
					angband_color_table[a][2] = (byte)(angband_color_table[a][2] + 1);
				if (cx == 'G') 
					angband_color_table[a][2] = (byte)(angband_color_table[a][2] - 1);
				if (cx == 'b') 
					angband_color_table[a][3] = (byte)(angband_color_table[a][3] + 1);
				if (cx == 'B') 
					angband_color_table[a][3] = (byte)(angband_color_table[a][3] - 1);

				/* Hack -- react to changes */
				Term_xtra(TERM_XTRA_REACT, 0);

				/* Hack -- redraw */
				Term_redraw();
			}
		}
#endif /* ALLOW_COLORS */
		message_flush();

		/* Clear screen */
		clear_from(0);
	}
	/* Load screen */
	screen_load();
}

/*** Non-complex menu actions ***/

static bool askfor_aux_numbers(_TCHAR *buf, size_t buflen, size_t *curs, size_t *len, _TCHAR keypress, bool firsttime)
{
	switch (keypress)
	{
		case ESCAPE:
		case '\n':
		case '\r':
		case ARROW_LEFT:
		case ARROW_RIGHT:
		case 0x7F: /* Delete */
		case 8: /* Backspace */
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
			return askfor_aux_keypress(buf, buflen, curs, len, keypress, firsttime);
	}
	return FALSE;
}

/*
 * Set base delay factor
 */
static void do_cmd_delay(void)
{
	bool res;
	long delay_factor; 
	_TCHAR tmp[4] = __T("");
	int msec = op_ptr->delay_factor * op_ptr->delay_factor;

	strnfmt(tmp, _countof(tmp), __T("%i"), op_ptr->delay_factor);

	/* Prompt */
	prt(__T("Command: Base Delay Factor"), 20, 0);

	prt(format(__T("Current base delay factor: %d (%d msec)"),
			   op_ptr->delay_factor, msec), 22, 0);
	prt(__T("New base delay factor (0-255): "), 21, 0);

	/* Ask the user for a string */
	res = askfor_aux(tmp, _countof(tmp), askfor_aux_numbers);

	/* Process input */
	if (res)
	{
		delay_factor = _tcstoul(tmp, NULL, 0);
		if((delay_factor >= 0) && (delay_factor <= 255))
			op_ptr->delay_factor = (byte) delay_factor;
		else
			bell(__T("Invalid delay factor (must be 0-255)"));
	}
}

/*
 * Set hitpoint warning level
 */
static void do_cmd_hp_warn(void)
{
	bool res;
	_TCHAR tmp[4] = __T("");
	byte warn;

	strnfmt(tmp, _countof(tmp), __T("%i"), op_ptr->hitpoint_warn);

	/* Prompt */
	prt(__T("Command: Hitpoint Warning"), 20, 0);

	prt(format(__T("Current hitpoint warning: %d (%d%%)"),
			   op_ptr->hitpoint_warn, op_ptr->hitpoint_warn * 10), 22, 0);
	prt(__T("New hitpoint warning (0-9): "), 21, 0);

	/* Ask the user for a string */
	res = askfor_aux(tmp, _countof(tmp), askfor_aux_numbers);

	/* Process input */
	if (res)
	{
		warn = (byte) _tcstoul(tmp, NULL, 0);
		
		/* Reset nonsensical warnings */
		if (warn <= 9)
			op_ptr->hitpoint_warn = warn;
		else
			bell(__T("Invalid hitpoint warning value (must be 0 - 9)"));
	}
}

/*
 * Set "lazy-movement" delay
 */
static void do_cmd_lazymove_delay(void)
{
	bool res;
	_TCHAR tmp[4] = __T("");

	strnfmt(tmp, _countof(tmp), __T("%i"), lazymove_delay);

	/* Prompt */
	prt(__T("Command: Movement Delay Factor"), 20, 0);

	prt(format(__T("Current movement delay: %d (%d msec)"),
			   lazymove_delay, lazymove_delay * 10), 22, 0);
	prt(__T("New movement delay: "), 21, 0);

	/* Ask the user for a string */
	res = askfor_aux(tmp, _countof(tmp), askfor_aux_numbers);

	/* Process input */
	if (res)
	{
		lazymove_delay = (u16b) _tcstoul(tmp, NULL, 0);
	}
}

/*
 * Ask for a "user pref file" and process it.
 *
 * This function should only be used by standard interaction commands,
 * in which a standard "Command:" prompt is present on the given row.
 *
 * Allow absolute file names?  XXX XXX XXX
 */
static void do_cmd_pref_file_hack(long row)
{
	_TCHAR ftmp[80];

	/* Prompt */
	prt(__T("Command: Load a user pref file"), row, 0);

	/* Prompt */
	prt(__T("File: "), row + 2, 0);

	/* Default filename */
	strnfmt(ftmp, sizeof ftmp, __T("%s.prf"), op_ptr->base_name);

	/* Ask for a file (or cancel) */
	if (!askfor_aux(ftmp, sizeof ftmp, NULL)) return;

	/* Process the given filename */
	if (process_pref_file(ftmp))
	{
		/* Mention failure */
		prt(__T(""), 0, 0);
		msg_format(__T("Failed to load '%s'!"), ftmp);
	}
	else
	{
		/* Mention success */
		prt(__T(""), 0, 0);
		msg_format(__T("Loaded '%s'."), ftmp);
	}
}

/*
 * Write options to a file.
 */
static void do_dump_options(void *unused, const _TCHAR *title)
{
	UNREFERENCED_PARAMETER(unused);
	UNREFERENCED_PARAMETER(title);
	dump_pref_file(option_dump, __T("Dump options"), 20);
}

/*** Main menu definitions and display ***/

/*
 * Definition of the options menu.
 *
 * XXX Too many entries.
 */

static menu_action option_actions [] = 
{
	{'a', __T("Interface options"), do_cmd_options_aux, (void*)0}, 
	{'b', __T("Display options"), do_cmd_options_aux, (void*)1},
	{'e', __T("Warning and disturbance options"), do_cmd_options_aux, (void*)2}, 
	{'f', __T("Birth (difficulty) options"), do_cmd_options_aux, (void*)3}, 
	{'g', __T("Cheat options"), do_cmd_options_aux, (void*)4}, 
	{0, 0, 0, 0}, /* Load and append */
	{'w', __T("Subwindow display settings"), (action_f) do_cmd_options_win, 0}, 
	{'s', __T("Item squelch settings"), (action_f) do_cmd_options_item, 0}, 
	{'d', __T("Set base delay factor"), (action_f) do_cmd_delay, 0}, 
	{'h', __T("Set hitpoint warning"), (action_f) do_cmd_hp_warn, 0}, 
	{'i', __T("Set movement delay"), (action_f) do_cmd_lazymove_delay, 0}, 
	{'l', __T("Load a user pref file"), (action_f) do_cmd_pref_file_hack, (void*)20},
	{'o', __T("Save options"), do_dump_options, 0}, 
	{0, 0, 0, 0}, /* Interact with */	
	{'m', __T("Interact with macros (advanced)"), (action_f) do_cmd_macros, 0},
	{'v', __T("Interact with visuals (advanced)"), (action_f) do_cmd_visuals, 0},
	{'c', __T("Interact with colours (advanced)"), (action_f) do_cmd_colors, 0},
};

static menu_type option_menu;

static _TCHAR tag_opt_main(menu_type *menu, int oid)
{
	(void)menu;
	if (option_actions[oid].id)
		return option_actions[oid].id;

	return 0;
}

static int valid_opt_main(menu_type *menu, int oid)
{
	(void)menu;
	if (option_actions[oid].name)
		return 1;

	return 0;
}

static void display_opt_main(menu_type *menu, int oid, bool cursor, int row, int col, int width)
{
	byte attr = curs_attrs[CURS_KNOWN][(int)cursor];

	(void)menu;
	(void)width;
	if (option_actions[oid].name)
		c_prt(attr, option_actions[oid].name, row, col);
}

static const menu_iter options_iter =
{
	tag_opt_main,
	valid_opt_main,
	display_opt_main,
	NULL
};

/*
 * Display the options main menu.
 */
void do_cmd_options(void)
{
	int cursor = 0;
	ui_event_data c = EVENT_EMPTY;

	screen_save();
	menu_layout(&option_menu, &SCREEN_REGION);

	while (c.key != ESCAPE)
	{
		clear_from(0);
		c = menu_select(&option_menu, &cursor, 0);
		if (c.type == EVT_SELECT && option_actions[cursor].action)
		{
			option_actions[cursor].action(option_actions[cursor].data,
			                              option_actions[cursor].name);
		}
		message_flush();
	}
	screen_load();
}

/*
 * Initialise all menus used here.
 */
void init_cmd4_c(void)
{
	/* some useful standard command keys */
	static const _TCHAR cmd_keys[] = { ARROW_LEFT, ARROW_RIGHT, 0 };

	/* Initialize the menus */
	menu_type *menu;

	/* options screen selection menu */
	menu = &option_menu;
	WIPE(menu, menu_type);
	menu->title = __T("Options Menu");
	menu->menu_data = option_actions;
	menu->flags = MN_CASELESS_TAGS;
	menu->cmd_keys = cmd_keys;
	menu->count = N_ELEMENTS(option_actions);
	menu_init(menu, MN_SKIN_SCROLL, &options_iter, &SCREEN_REGION);

	/* Initialize the options toggle menu */
	menu = &option_toggle_menu;
	WIPE(menu, menu_type);
	menu->prompt = __T("Set option (y/n/t), '?' for information");
	menu->cmd_keys = __T("?Yy\n\rNnTt\x8C"); /* \x8c = ARROW_RIGHT */
	menu->selections = __T("abcdefghijklmopqrsuvwxz");
	menu->count = OPT_PAGE_PER;
	menu->flags = MN_DBL_TAP;
	menu_init(menu, MN_SKIN_SCROLL, &options_toggle_iter, &SCREEN_REGION);

	/* macro menu */
	menu = &macro_menu;
	WIPE(menu, menu_type);
	menu->title = __T("Interact with macros");
	menu->cmd_keys = cmd_keys;
	menu->selections = lower_case;
	menu->menu_data = macro_actions;
	menu->count = N_ELEMENTS(macro_actions);
	menu_init(menu, MN_SKIN_SCROLL, find_menu_iter(MN_ITER_ACTIONS), &SCREEN_REGION);

	/* visuals menu */
	menu = &visual_menu;
	WIPE(menu, menu_type);
	menu->title = __T("Interact with visuals");
	menu->cmd_keys = cmd_keys;
	menu->selections = lower_case;
	menu->menu_data = visual_menu_items;
	menu->count = N_ELEMENTS(visual_menu_items);
	menu_init(menu, MN_SKIN_SCROLL, find_menu_iter(MN_ITER_ACTIONS), &SCREEN_REGION);

	/* colors menu */
	menu = &color_menu;
	WIPE(menu, menu_type);
	menu->title = __T("Interact with colors");
	menu->cmd_keys = cmd_keys;
	menu->selections = lower_case;
	menu->menu_data = color_events;
	menu->count = N_ELEMENTS(color_events);
	menu_init(menu, MN_SKIN_SCROLL, find_menu_iter(MN_ITER_ACTIONS), &SCREEN_REGION);
}

/*** Non-knowledge/option stuff ***/

/*
 * Note something in the message recall
 */
void do_cmd_note(void)
{
	_TCHAR tmp[80];

	/* Default */
	_tcscpy_s(tmp, _countof(tmp), __T(""));

	/* Input */
	if (!get_string(__T("Note: "), tmp, 80)) return;

	/* Ignore empty notes */
	if (!tmp[0] || (tmp[0] == ' ')) return;

	/* Add the note to the message recall */
	msg_format(__T("Note: %s"), tmp);

	/* Add a history entry */
	history_add(tmp, HISTORY_USER_INPUT, 0);
}

/*
 * Mention the current version
 */
void do_cmd_version(void)
{
	/* Silly message */
	msg_format(__T("You are playing %s %s.  Type '?' for more info."),
		       VERSION_NAME, VERSION_STRING);
}

/*
 * Ask for a "user pref line" and process it
 */
void do_cmd_pref(void)
{
	_TCHAR tmp[80];

	/* Default */
	_tcscpy_s(tmp, _countof(tmp), __T(""));

	/* Ask for a "user pref command" */
	if (!get_string(__T("Pref: "), tmp, 80)) return;

	/* Process that pref command */
	(void)process_pref_file_command(tmp);
}

/*
 * Array of feeling strings
 */
static const _TCHAR *feeling_text[] =
{
	__T("Looks like any other level."),
	__T("You feel there is something special here..."),
	__T("You have a superb feeling about this level."),
	__T("You have an excellent feeling..."),
	__T("You have a very good feeling..."),
	__T("You have a good feeling..."),
	__T("You feel a little lucky."),
	__T("You are unsure about this place."),
	__T("This place seems reasonably safe."),
	__T("This seems a quiet, peaceful place."),
	__T("This place looks uninteresting."),
};

/*
 * Note that "feeling" is set to zero unless some time has passed.
 * Note that this is done when the level is GENERATED, not entered.
 */
void do_cmd_feeling(void)
{
	/* Don't show feelings for cold-hearted characters */
	if (OPT(birth_feelings)) return;

	/* Verify the feeling */
	if (feeling >= N_ELEMENTS(feeling_text))
		feeling = N_ELEMENTS(feeling_text) - 1;

	/* No useful feeling in town */
	if (!p_ptr->depth)
	{
		msg_print(__T("Looks like a typical town."));
		return;
	}
	/* Display the feeling */
	msg_print(feeling_text[feeling]);
}

/*** Screenshot loading/saving code ***/

/*
 * Encode the screen colors
 */
static const _TCHAR hack[BASIC_COLORS+1] = __T("dwsorgbuDWvyRGBU");

/*
 * Hack -- load a screen dump from a file
 *
 * ToDo: Add support for loading/saving screen-dumps with graphics
 * and pseudo-graphics.  Allow the player to specify the filename
 * of the dump.
 */
void do_cmd_load_screen(void)
{
	int i, y, x;

	byte a = 0;
	_TCHAR c = ' ';

	bool okay = TRUE;

	ang_file *fp;

	_TCHAR buf[1024];

	/* Build the filename */
	path_build(buf, 1024, ANGBAND_DIR_USER, __T("dump.txt"));
	fp = file_open(buf, MODE_READ, -1);
	if (!fp) 
		return;

	/* Save screen */
	screen_save();

	/* Clear the screen */
	Term_clear();

	/* Load the screen */
	for (y = 0; okay && (y < 24); y++)
	{
		/* Get a line of data */
		if (!file_getl(fp, buf, _countof(buf))) okay = FALSE;


		/* Show each row */
		for (x = 0; x < 79; x++)
		{
			/* Put the attr/char */
			Term_draw(x, y, TERM_WHITE, buf[x]);
		}
	}
	/* Get the blank line */
	if (!file_getl(fp, buf, _countof(buf))) okay = FALSE;

	/* Dump the screen */
	for (y = 0; okay && (y < 24); y++)
	{
		/* Get a line of data */
		if (!file_getl(fp, buf, _countof(buf))) okay = FALSE;

		/* Dump each row */
		for (x = 0; x < 79; x++)
		{
			/* Get the attr/char */
			(void)(Term_what(x, y, &a, &c));

			/* Look up the attr */
			for (i = 0; i < BASIC_COLORS; i++)
			{
				/* Use attr matches */
				if (hack[i] == buf[x]) a = i;
			}
			/* Put the attr/char */
			Term_draw(x, y, a, c);
		}
	}
	/* Close it */
	file_close(fp);

	/* Message */
	msg_print(__T("Screen dump loaded."));
	message_flush();

	/* Load screen */
	screen_load();
}

/*
 * If in English mode, switch to Japanese (and vice versa)
 */
void do_cmd_switch_lang(void)
{
	if(arg_language == LNG_ENGLISH)
	{
		_TCHAR buf[1000];
		arg_language = LNG_JAPANESE;
		msg_print(spaceme( buf, __T("言語を日本語に変えました。"))); /* Translation TBC */
	}
	else
	{
		arg_language = LNG_ENGLISH;
		msg_print(__T("Switched language to English."));
	}
}

/*
 * Save a simple text screendump.
 */
static void do_cmd_save_screen_text(void)
{
	int y, x;

	byte a = 0;
	_TCHAR c = ' ';

	ang_file *fff;

	_TCHAR buf[1024];

	/* Build the filename */
	path_build(buf, 1024, ANGBAND_DIR_USER, __T("dump.txt"));
	fff = file_open(buf, MODE_WRITE, FTYPE_TEXT);
	if (!fff) 
		return;

	/* Save screen */
	screen_save();

	/* Dump the screen */
	for (y = 0; y < 24; y++)
	{
		/* Dump each row */
		for (x = 0; x < 79; x++)
		{
			/* Get the attr/char */
			(void)(Term_what(x, y, &a, &c));

			/* Dump it */
			buf[x] = c;
		}

		/* Terminate */
		buf[x] = 0;

		/* End the row */
		file_putf(fff, __T("%s\n"), buf);
	}

	/* Skip a line */
	file_putf(fff, __T("\n"));

	/* Dump the screen */
	for (y = 0; y < 24; y++)
	{
		/* Dump each row */
		for (x = 0; x < 79; x++)
		{
			/* Get the attr/char */
			(void)(Term_what(x, y, &a, &c));

			/* Dump it */
			buf[x] = hack[a & 0x0F];
		}

		/* Terminate */
		buf[x] = 0;

		/* End the row */
		file_putf(fff, __T("%s\n"), buf);
	}

	/* Skip a line */
	file_putf(fff, __T("\n"));


	/* Close it */
	file_close(fff);

	/* Message */
	msg_print(__T("Screen dump saved."));
	message_flush();

	/* Load screen */
	screen_load();
}

/*
 * Hack -- save a screen dump to a file in html format
 */
static void do_cmd_save_screen_html(int mode)
{
	size_t i;

	ang_file *fff;
	_TCHAR file_name[1024];
	_TCHAR tmp_val[256];

	typedef void (*dump_func)(ang_file *);
	dump_func dump_visuals [] = 
		{ dump_monsters, dump_features, dump_objects, dump_flavors, dump_colors };

	if (mode == 0)
		_tcscpy_s(tmp_val, _countof(tmp_val), __T("dump.html"));
	else
		_tcscpy_s(tmp_val, _countof(tmp_val), __T("dump.txt"));

	/* Ask for a file */
	if (!get_string(__T("File: "), tmp_val, _countof(tmp_val))) return;

	/* Save current preferences */
	path_build(file_name, 1024, ANGBAND_DIR_USER, __T("dump.prf"));
	fff = file_open(file_name, MODE_WRITE, (mode == 0 ? FTYPE_HTML : FTYPE_TEXT));

	/* Check for failure */
	if (!fff)
	{
		msg_print(__T("Screen dump failed."));
		message_flush();
		return;
	}

	/* Dump all the visuals */
	for (i = 0; i < N_ELEMENTS(dump_visuals); i++)
		dump_visuals[i](fff);

	file_close(fff);

	/* Dump the screen with raw character attributes */
	reset_visuals(FALSE);
	do_cmd_redraw();
	html_screenshot(tmp_val, mode);

	/* Recover current graphics settings */
	reset_visuals(TRUE);
	process_pref_file(file_name);
	file_delete(file_name);
	do_cmd_redraw();

	msg_print(__T("HTML screen dump saved."));
	message_flush();
}

/*
 * Hack -- save a screen dump to a file
 */
void do_cmd_save_screen(void)
{
	msg_print(__T("Dump type [(t)ext; (h)tml; (f)orum embedded html]:"));

	while (TRUE)
	{
		_TCHAR ch = inkey();

		switch (ch)
		{
			case ESCAPE:
				return;

			case 't':
				do_cmd_save_screen_text();
				return;

			case 'h':
				do_cmd_save_screen_html(0);
				return;

			case 'f':
				do_cmd_save_screen_html(1);
				return;
		}
	}
}
