/* File: main-win.c */

/* Purpose: Support for Windows Angband */

/*
 * Written by Skirmantas Kligys (kligys@scf.usc.edu)
 * looking at main-mac.c by Ben Harrison
 *
 * Note that this file needs MAJOR updating for Angband 2.7.9!!!
 *
 * See "term.c" for info on the "generic terminal" that we support.
 */

#ifdef WINDOWS

#undef  COLOR_DEBUG


/* #include "main-win.h" */

/* Note -- Begin "main-win.h" */

/* main-win.h */

#define IDM_FILE_NEW             101
#define IDM_FILE_OPEN            102
#define IDM_FILE_SAVE            103
#define IDM_FILE_EXIT            104
#define IDM_FILE_QUIT            105

#define IDM_OPTIONS_FONT_ANGBAND 201
#define IDM_OPTIONS_FONT_RECALL  202
#define IDM_OPTIONS_FONT_CHOICE  203
#define IDM_OPTIONS_FONT_MIRROR  204

#define IDM_OPTIONS_RECALL       212
#define IDM_OPTIONS_CHOICE       213
#define IDM_OPTIONS_MIRROR       214

#define IDM_OPTIONS_RESIZABLE    221
#define IDM_OPTIONS_SOUND        222

#define MAX_SOUND_EVENTS         7

#define STRICT
#include <windows.h>
#include <commdlg.h>

#include "angband.h"

#include <stdarg.h>

#include "ext-w32\itsybits.h"
#include "ext-w32\readdib.h"

/* string.h excludes this because __STDC__=1 */
int stricmp(const char *, const char *);

/*
 * Extra "term" data
 */

#define WTY_SCREEN    1
#define WTY_RECALL    2
#define WTY_CHOICE    3
#define WTY_MIRROR    4

typedef struct term_data term_data;

struct term_data {
  term     t;
  cptr     s;
  HWND     w;
  DWORD    dwStyle;
  uint     type;
  uint     keys;

  uint     rows;
  uint     cols;
  uint     vis_rows;
  uint     vis_cols;
  uint     scroll_vpos;
  uint     scroll_hpos;

  uint     pos_x;
  uint     pos_y;
  uint     size_wid;
  uint     size_hgt;
  uint     client_wid;
  uint     client_hgt;
  uint     size_ow1;
  uint     size_oh1;
  uint     size_ow2;
  uint     size_oh2;
  uint     cap_size;

  uint     mapped;
  HFONT    font_id;
  char_ptr font_file;
  uint     font_wid;
  uint     font_hgt;
};

/*
 * Some information for every "term" window
 */
term_data screen;
term_data recall;
term_data choice;
term_data mirror;

/*
 * Various boolean flags
 */
bool game_in_progress  = FALSE;  /* game in progress */
bool save_enabled      = FALSE;  /* game can be saved */
bool screen_resizable  = FALSE;  /* main window ("screen") resizable */
bool use_graphics      = TRUE;   /* use graphical chars */
bool use_sound         = TRUE;   /* use sound effects */
bool initialized       = FALSE;  /* note when "open"/"new" become valid */
bool creating          = 0;      /* Hack: set it to WTY_SCREEN, WTY_RECALL or */
				 /* WTY_CHOICE so that WM_NCCREATE handler */
				 /* knows which window is indeed created */
bool paletted          = FALSE;  /* screen paletted, i.e. 256 colors */
bool colors16          = FALSE;  /* 16 colors screen, don't use RGB() */

bool use_recall_win    = TRUE;
bool use_choice_win    = TRUE;
bool use_screen_win    = TRUE;
bool use_mirror_win    = TRUE;

/*
 * Saved instance handle
 */
static HINSTANCE hInstance = 0;

/*
 * XXX XXX XXX Consider using "ANGBAND_DIR_XTRA" if desired
 */
static char_ptr ANGBAND_DIR_RSRC = 0;

static HBRUSH   hbrYellow = 0;
static HBRUSH   hbrRed    = 0;
static HICON    hIcon     = 0;
static HPALETTE hPal      = 0;
static DIBINIT  infGraph = {0, 0, 0, 8, 13};


/* full path to ANGBAND.INI */
static cptr     ini_file = NULL;

static cptr AppName = "ANGBAND";

/*
 * Table of sound files used for sound events.
 */

static char win_sounds[MAX_SOUND_EVENTS][MAX_PATH];

/*
 * The "term.c" color set:
 *   Black, White, Slate, Orange,    Red, Blue, Green, Umber
 *   D-Gray, L-Gray, Violet, Yellow, L-Red, L-Blue, L-Green, L-Umber
 *
 * This happens (surprise) to be the same as the "Angband" color set.
 *
 * Colors 8 to 15 are basically "enhanced" versions of Colors 0 to 7.
 * Note that on B/W machines, all non-zero colors can be white (on black).
 *
 * Note that all characters are assumed to be drawn on a black background.
 * This may require calling "Term_wipe()" before "Term_text()", etc.
 *
 * The Windows "Term_curs()" ignores the symbol under the cursor.
 */

/* The colors come from MAIN-X11.C */
static const COLORREF win_clr[16] = {
  PALETTERGB(0x00, 0x00, 0x00),  /* BLACK 15 */
  PALETTERGB(0xFF, 0xFF, 0xFF),  /* WHITE 0 */
  PALETTERGB(0xA0, 0xA0, 0xA0),  /* GRAY 13 */
  PALETTERGB(0xFF, 0x92, 0x00),  /* ORANGE 2 */
  PALETTERGB(0xB0, 0x00, 0x00),  /* RED 3 */
  PALETTERGB(0x00, 0xB0, 0x00),  /* GREEN 9 */
  PALETTERGB(0x00, 0x00, 0xFF),  /* BLUE 6 */
  PALETTERGB(0xC8, 0x64, 0x00),  /* UMBER 10 */
  PALETTERGB(0x70, 0x70, 0x70),  /* DARKGRAY 14 */
  PALETTERGB(0xD0, 0xD0, 0xD0),  /* LIGHTGRAY 12 */
  PALETTERGB(0xA5, 0x00, 0xFF),  /* VIOLET 5 */
  PALETTERGB(0xFF, 0xFD, 0x00),  /* YELLOW 1 */
  PALETTERGB(0xFF, 0x00, 0xBC),  /* LIGHTRED 4 */
  PALETTERGB(0x00, 0xFF, 0x00),  /* LIGHTGREEN 8 */
  PALETTERGB(0x00, 0xC8, 0xFF),  /* LIGHTBLUE 7 */
  PALETTERGB(0xFF, 0xCC, 0x80)   /* LIGHTUMBER 11 */
};

/* Palette indices for 16 colors */
static const BYTE win_pal[16] =
  {0, 15, 8, 4, 1, 2, 3, 4, 7, 8, 13, 12, 9, 10, 11, 12};

/* Note -- End "main-win.h" */



/*
 * General utilities
 */
static char *extract_file_name(char *s)
{
  char *p;

  for (p = s + strlen(s) - 1; (p >= s)&&(*p != ':')&&(*p != '\\'); p--);
  return ++p;
}


static void _cdecl formatted_quit(char *format, ...)
{
  va_list parg;
  char    tmp[128];

  va_start(parg, format);
  vsprintf(tmp, format, parg);
  va_end(parg);
  quit(tmp);
}


static void validate_file(char *s)
{
  if (GetFileAttributes (s) == 0xFFFFFFFF)
    formatted_quit("Cannot find file:\n%s", s);
}


static void validate_dir(char *s)
{
  if (GetFileAttributes (s) == 0xFFFFFFFF)
    formatted_quit("Cannot find directory:\n%s", s);
}


/*
 * Get the "size" for a window
 */
static void term_getsize(term_data *td)
{
  HDC         hdcDesktop;
  HFONT       hfOld;
  TEXTMETRIC  tm;
  RECT        rc;

  /* all this trouble to get the cell size*/
  hdcDesktop = GetDC(HWND_DESKTOP);
  hfOld = SelectObject(hdcDesktop, td->font_id);
  GetTextMetrics(hdcDesktop, &tm);
  SelectObject(hdcDesktop, hfOld);
  ReleaseDC(HWND_DESKTOP, hdcDesktop);

  td->font_wid = tm.tmAveCharWidth;
  td->font_hgt = tm.tmHeight;

  td->client_wid = td->vis_cols * td->font_wid + td->size_ow1 + td->size_ow2;
  td->client_hgt = td->vis_rows * td->font_hgt + td->size_oh1 + td->size_oh2;

  /* position not important */
  rc.left = rc.top = 0;
  rc.right = rc.left + td->client_wid;
  rc.bottom = rc.top + td->client_hgt;
  if (td->type == WTY_SCREEN)
  {
    if (screen_resizable)
    {
      rc.right  += GetSystemMetrics(SM_CXVSCROLL) - 1;
      rc.bottom += GetSystemMetrics(SM_CYHSCROLL) - 1;
    }
  }
  else
    rc.right += GetSystemMetrics(SM_CXVSCROLL) - 1;

  if (td->type == WTY_SCREEN)
  {
    AdjustWindowRect(&rc, td->dwStyle, TRUE);
    /* no idea why this needed */
    rc.bottom++;
  }
  else
    ibAdjustWindowRect(&rc, td->dwStyle, FALSE, td->cap_size);

  td->size_wid = rc.right - rc.left;
  td->size_hgt = rc.bottom - rc.top;

  /* don't update td->pos_* if called from inside CreateWindow */
  if (!td->w) return;
  GetWindowRect(td->w, &rc);
  td->pos_x = rc.left;
  td->pos_y = rc.top;
}


/*
 * Write the "preference" data to the .INI file
 */
static void save_prefs(void)
{
  char       buf[32];
  RECT       rc;
  term_data *td;

  strcpy(buf, use_sound ? "1" : "0");
  WritePrivateProfileString("Angband", "Sound", buf, ini_file);

  strcpy(buf, screen_resizable ? "1" : "0");
  WritePrivateProfileString("Main window", "Resizable", buf, ini_file);

  td = &screen;
  if (td->w)
  {
    if (td->font_file)
      WritePrivateProfileString("Main window", "FontFile", td->font_file, ini_file);
    wsprintf(buf, "%d", td->vis_cols);
    WritePrivateProfileString("Main window", "Columns", buf, ini_file);
    wsprintf(buf, "%d", td->vis_rows);
    WritePrivateProfileString("Main window", "Rows", buf, ini_file);
    GetWindowRect(screen.w, &rc);
    wsprintf(buf, "%d", rc.left);
    WritePrivateProfileString("Main window", "PositionX", buf, ini_file);
    wsprintf(buf, "%d", rc.top);
    WritePrivateProfileString("Main window", "PositionY", buf, ini_file);
  }

  td = &recall;
  if (td->w)
  {
    strcpy(buf, td->mapped ? "1" : "0");
    WritePrivateProfileString("Recall window", "UseRecallWin", buf, ini_file);
    if (td->font_file)
      WritePrivateProfileString("Recall window", "FontFile", td->font_file, ini_file);
    wsprintf(buf, "%d", td->vis_cols);
    WritePrivateProfileString("Recall window", "Columns", buf, ini_file);
    wsprintf(buf, "%d", td->vis_rows);
    WritePrivateProfileString("Recall window", "Rows", buf, ini_file);
    GetWindowRect(td->w, &rc);
    wsprintf(buf, "%d", rc.left);
    WritePrivateProfileString("Recall window", "PositionX", buf, ini_file);
    wsprintf(buf, "%d", rc.top);
    WritePrivateProfileString("Recall window", "PositionY", buf, ini_file);
    wsprintf(buf, "%d", td->cap_size);
    WritePrivateProfileString("Recall window", "CapSize", buf, ini_file);
  }

  td = &choice;
  if (td->w)
  {
    strcpy(buf, td->mapped ? "1" : "0");
    WritePrivateProfileString("Choice window", "UseChoiceWin", buf, ini_file);
    if (td->font_file)
      WritePrivateProfileString("Choice window", "FontFile", td->font_file, ini_file);
    wsprintf(buf, "%d", td->vis_cols);
    WritePrivateProfileString("Choice window", "Columns", buf, ini_file);
    wsprintf(buf, "%d", td->vis_rows);
    WritePrivateProfileString("Choice window", "Rows", buf, ini_file);
    GetWindowRect(td->w, &rc);
    wsprintf(buf, "%d", rc.left);
    WritePrivateProfileString("Choice window", "PositionX", buf, ini_file);
    wsprintf(buf, "%d", rc.top);
    WritePrivateProfileString("Choice window", "PositionY", buf, ini_file);
    wsprintf(buf, "%d", td->cap_size);
    WritePrivateProfileString("Choice window", "CapSize", buf, ini_file);
  }

  td = &mirror;
  if (td->w)
  {
    strcpy(buf, td->mapped ? "1" : "0");
    WritePrivateProfileString("Mirror window", "UseMirrorWin", buf, ini_file);
    if (td->font_file)
      WritePrivateProfileString("Mirror window", "FontFile", td->font_file, ini_file);
    wsprintf(buf, "%d", td->vis_cols);
    WritePrivateProfileString("Mirror window", "Columns", buf, ini_file);
    wsprintf(buf, "%d", td->vis_rows);
    WritePrivateProfileString("Mirror window", "Rows", buf, ini_file);
    GetWindowRect(td->w, &rc);
    wsprintf(buf, "%d", rc.left);
    WritePrivateProfileString("Mirror window", "PositionX", buf, ini_file);
    wsprintf(buf, "%d", rc.top);
    WritePrivateProfileString("Mirror window", "PositionY", buf, ini_file);
    wsprintf(buf, "%d", td->cap_size);
    WritePrivateProfileString("Mirror window", "CapSize", buf, ini_file);
  }
}


/*
 * Load the preferences from the .INI file
 */
static void load_prefs(void)
{
  char  buf[128];
  char *p;

  use_sound = GetPrivateProfileInt("Angband", "Sound", 1, ini_file) != 0;

  GetPrivateProfileString ("Sound", "Hit", "lib\\win-ext\\hit.wav",
                           win_sounds[0], sizeof win_sounds[0], ini_file);
  GetPrivateProfileString ("Sound", "Miss", "lib\\win-ext\\miss.wav",
                           win_sounds[1], sizeof win_sounds[1], ini_file);
  GetPrivateProfileString ("Sound", "Flee", "lib\\win-ext\\flee.wav",
                           win_sounds[2], sizeof win_sounds[2], ini_file);
  GetPrivateProfileString ("Sound", "Drop", "lib\\win-ext\\drop.wav",
                           win_sounds[3], sizeof win_sounds[3], ini_file);
  GetPrivateProfileString ("Sound", "Kill", "lib\\win-ext\\kill.wav",
                           win_sounds[4], sizeof win_sounds[4], ini_file);
  GetPrivateProfileString ("Sound", "Level", "lib\\win-ext\\level.wav",
                           win_sounds[5], sizeof win_sounds[5], ini_file);
  GetPrivateProfileString ("Sound", "Death", "lib\\win-ext\\death.wav",
                           win_sounds[6], sizeof win_sounds[6], ini_file);
  
  screen_resizable =
      GetPrivateProfileInt("Main window", "Resizable", 0, ini_file) != 0;
  GetPrivateProfileString("Main window", "FontFile",
		     "\\angband\\lib-win\\rsrc\\7x13.fon", buf, 128, ini_file);
  screen.font_file = (char *)string_make(buf);
  validate_file(screen.font_file);
  screen.vis_cols = GetPrivateProfileInt("Main window", "Columns", 80, ini_file);
  screen.vis_rows = GetPrivateProfileInt("Main window", "Rows", 24, ini_file);
  screen.pos_x = GetPrivateProfileInt("Main window", "PositionX", 0, ini_file);
  screen.pos_y = GetPrivateProfileInt("Main window", "PositionY", 0, ini_file);

  use_recall_win = GetPrivateProfileInt("Recall window", "UseRecallWin", 1, ini_file) != 0;
  recall.mapped = use_recall_win;
  GetPrivateProfileString("Recall window", "FontFile",
		     "\\angband\\lib-win\\rsrc\\7x13.fon", buf, 128, ini_file);
  recall.font_file = (char *)string_make(buf);
  validate_file(recall.font_file);
  recall.vis_cols = GetPrivateProfileInt("Recall window", "Columns", 80, ini_file);
  recall.vis_rows = GetPrivateProfileInt("Recall window", "Rows", 8, ini_file);
  recall.pos_x = GetPrivateProfileInt("Recall window", "PositionX", 0, ini_file);
  recall.pos_y = GetPrivateProfileInt("Recall window", "PositionY", 0, ini_file);
  recall.cap_size = min(
      GetPrivateProfileInt("Recall window", "CapSize", 0, ini_file),
      127);

  use_choice_win = GetPrivateProfileInt("Choice window", "UseChoiceWin", 0, ini_file) != 0;
  choice.mapped = use_choice_win;
  GetPrivateProfileString("Choice window", "FontFile",
		     "\\angband\\lib-win\\rsrc\\7x13.fon", buf, 128, ini_file);
  choice.font_file = (char *)string_make(buf);
  validate_file(choice.font_file);
  choice.vis_cols = GetPrivateProfileInt("Choice window", "Columns", 80, ini_file);
  choice.vis_rows = GetPrivateProfileInt("Choice window", "Rows", 5, ini_file);
  choice.pos_x = GetPrivateProfileInt("Choice window", "PositionX", 0, ini_file);
  choice.pos_y = GetPrivateProfileInt("Choice window", "PositionY", 0, ini_file);
  choice.cap_size = min(
      GetPrivateProfileInt("Choice window", "CapSize", 0, ini_file),
      127);

  use_mirror_win = GetPrivateProfileInt("Mirror window", "UseMirrorWin", 0, ini_file) != 0;
  mirror.mapped = use_mirror_win;
  GetPrivateProfileString("Mirror window", "FontFile",
		     "\\angband\\lib-win\\rsrc\\7x13.fon", buf, 128, ini_file);
  mirror.font_file = (char *)string_make(buf);
  validate_file(mirror.font_file);
  mirror.vis_cols = GetPrivateProfileInt("Mirror window", "Columns", 80, ini_file);
  mirror.vis_rows = GetPrivateProfileInt("Mirror window", "Rows", 5, ini_file);
  mirror.pos_x = GetPrivateProfileInt("Mirror window", "PositionX", 0, ini_file);
  mirror.pos_y = GetPrivateProfileInt("Mirror window", "PositionY", 0, ini_file);
  mirror.cap_size = min(
      GetPrivateProfileInt("Mirror window", "CapSize", 0, ini_file),
      127);

  strcpy(buf, screen.font_file);
  p = extract_file_name(buf);
  *(--p) = '\0';
  ANGBAND_DIR_RSRC = (char *)string_make(buf);
  validate_dir(ANGBAND_DIR_RSRC);
}


/*
 *  Loads font file specified in td->font_file, initializes td->font_xxx,
 *  td->size_xxx.
 */
static void term_load_font(term_data *td)
{
  char       fontname[9];
  int        len;
  char       *p, *q, *s, *d;
  int        x, y;

  if (!td->font_file) quit("Bug: font_file == NULL");
  validate_file(td->font_file);

  td->font_id = 0;
  len = strlen(td->font_file);
  q = td->font_file + len - 4;
  if ((len < 5) || (stricmp(q, ".FON") != 0))
    formatted_quit("font_file doesn't end in .FON:\n%s", td->font_file);

  p = extract_file_name(td->font_file);
  if (q - p > 8)
    formatted_quit("Too long filename in font_file:\n%s", td->font_file);

  x = atoi(p);
  y = 0;
  for (s = p, d = fontname; (*s != '\0') && (s < q);)
  {
    if ((toupper(*s) == 'X') && (y == 0)) y = atoi(s+1);
    *(d++) = toupper(*(s++));
  }
  *d = '\0';

  if (AddFontResource(td->font_file))
  {
    if ((x == 0) || (y == 0)) x = y = 0;
    td->font_id = CreateFont(y, x, 0, 0, FW_DONTCARE, 0, 0, 0,
	       ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
	       DEFAULT_QUALITY, FIXED_PITCH | FF_DONTCARE,
	       fontname);
  }
  else
    formatted_quit("Font file corrupted:\n%s", td->font_file);

  term_getsize(td);
}


/*
 *  Loads BMP corresponding to font specified in filename, inits infGraph.
 */
static void term_load_bitmap(char *font)
{
  char *p, *q, *s, *d;

  MessageBox(NULL, NULL, font, MB_OK);
  p = (char *)string_make(font);
  q = p + strlen(p) - 4;
  strcpy(q, ".BMP");
  validate_file(p);

  if (infGraph.hDIB) GlobalFree(infGraph.hDIB);
  if (infGraph.hPalette) DeleteObject(infGraph.hPalette);
  if (infGraph.hBitmap) DeleteObject(infGraph.hBitmap);

  if (!ReadDIB(p, &infGraph))
    formatted_quit("Bitmap corrupted:\n%s", p);

  d = extract_file_name(p);

  infGraph.CellWidth = atoi(d);
  infGraph.CellHeight = 0;
  for (s = d; (*s != '\0') && (s < q); s++)
    if (toupper(*s) == 'X')
    {
      infGraph.CellHeight = atoi(s+1);
      break;
    }

  string_free(p);
}


static void term_window_resize(term_data *td)
{
  RECT  rc;
  POINT pt;

  if (td->type == WTY_SCREEN)
  {
    /* get old window center */
    GetWindowRect(td->w, &rc);
    pt.x = (rc.left + rc.right) / 2;
    pt.y = (rc.top  + rc.bottom) / 2;

    /* determine left top corner, adjust it */
    pt.x -= td->size_wid / 2;
    pt.y -= td->size_hgt/ 2;
    if (pt.x < 0) pt.x = 0;
    if (pt.y < 0) pt.y = 0;

    SetWindowPos(td->w, 0, pt.x, pt.y,
      td->size_wid, td->size_hgt, SWP_NOZORDER);
  }
  else
    SetWindowPos(td->w, 0, 0, 0,
      td->size_wid, td->size_hgt, SWP_NOMOVE | SWP_NOZORDER);

  InvalidateRect(td->w, NULL, TRUE);
}


static void term_change_font(term_data *td)
{
  OPENFILENAME ofn;
  char         tmp[128];

  if (td->font_file) strcpy(tmp, extract_file_name(td->font_file));
  else tmp[0] = '\0';

  memset(&ofn, 0, sizeof(ofn));
  ofn.lStructSize = sizeof(ofn);
  ofn.hwndOwner = screen.w;
  ofn.lpstrFilter = "Font Files (*.fon)\0*.fon\0";
  ofn.nFilterIndex = 1;
  ofn.lpstrFile = tmp;
  ofn.nMaxFile = 128;
  ofn.lpstrInitialDir = ANGBAND_DIR_RSRC;
  ofn.Flags = OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR;
  ofn.lpstrDefExt = "fon";

  if (GetOpenFileName(&ofn))
  {
    if (td->font_id) DeleteObject(td->font_id);
    if ((td != &screen) && (stricmp(screen.font_file, td->font_file) == 0) ||
	(td != &recall) && (stricmp(recall.font_file, td->font_file) == 0) ||
	(td != &choice) && (stricmp(choice.font_file, td->font_file) == 0) ||
	(td != &mirror) && (stricmp(mirror.font_file, td->font_file) == 0))
      ; /* do nothing !! */
    else RemoveFontResource(td->font_file);
    string_free(td->font_file);
    td->font_file = (char *)string_make(tmp);
    term_load_font(td);
    term_window_resize(td);
  }
}


static void term_change_bitmap(term_data *td)
{
  OPENFILENAME ofn;
  char         tmp[128];

  if (td->font_file)
  {
    strcpy(tmp, extract_file_name(td->font_file));
    strcpy(tmp + strlen(tmp) - 4, ".bmp");
  }
  else tmp[0] = '\0';

  memset(&ofn, 0, sizeof(ofn));
  ofn.lStructSize = sizeof(ofn);
  ofn.hwndOwner = screen.w;
  ofn.lpstrFilter = "Bitmap Files (*.bmp)\0*.bmp\0";
  ofn.nFilterIndex = 1;
  ofn.lpstrFile = tmp;
  ofn.nMaxFile = 128;
  ofn.lpstrInitialDir = ANGBAND_DIR_RSRC;
  ofn.Flags = OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR;
  ofn.lpstrDefExt = "bmp";

  if (GetOpenFileName(&ofn))
  {
    term_load_bitmap(tmp);

    if (td->font_id) DeleteObject(td->font_id);
    if ((td != &screen) && (stricmp(screen.font_file, td->font_file) == 0) ||
	(td != &recall) && (stricmp(recall.font_file, td->font_file) == 0) ||
	(td != &choice) && (stricmp(choice.font_file, td->font_file) == 0) ||
	(td != &mirror) && (stricmp(mirror.font_file, td->font_file) == 0))
      ; /* do nothing !! */
    else RemoveFontResource(td->font_file);
    string_free(td->font_file);
    strcpy(tmp + strlen(tmp) - 4, ".fon");
    td->font_file = (char *)string_make(tmp);
    term_load_font(td);
    term_window_resize(td);
  }

}


static void term_force_font(term_data *td, char *font_name)
{
  char tmp[128];

  sprintf(tmp, "%s%s%s", ANGBAND_DIR_RSRC, PATH_SEP, font_name);
  validate_file(tmp);

  if (td->font_id) DeleteObject(td->font_id);
  if ((td != &screen) && (stricmp(screen.font_file, td->font_file) == 0) ||
      (td != &recall) && (stricmp(recall.font_file, td->font_file) == 0) ||
      (td != &choice) && (stricmp(choice.font_file, td->font_file) == 0) ||
      (td != &mirror) && (stricmp(mirror.font_file, td->font_file) == 0))
    ; /* do nothing !! */
  else RemoveFontResource(td->font_file);
  string_free(td->font_file);
  td->font_file = (char *)string_make(tmp);
  term_load_font(td);
  term_load_bitmap(td->font_file);
  term_window_resize(td);
}


/*** Hooks for the "term.c" functions ***/
/*
 * Hack -- redraw a term_data
 */
static void term_data_redraw(term_data *td)
{
    if ((td->type == WTY_SCREEN) && !screen_resizable)
    {
      HPEN hpenOld, hpenLtGray;
      HDC  hdc = GetDC(td->w);

      /* Frame the window in light_gray */
      hpenLtGray = CreatePen(PS_SOLID, 1, win_clr[2]);
      hpenOld = SelectObject(hdc, hpenLtGray);

#ifdef _WIN32
      MoveToEx(hdc, 0, 0, NULL);
#else
      MoveTo(hdc, 0, 0);
#endif
      LineTo(hdc, 0, td->client_hgt-1);
      LineTo(hdc, td->client_wid-1, td->client_hgt-1);
      LineTo(hdc, td->client_wid-1, 0);
      LineTo(hdc, 0, 0);

      SelectObject(hdc, hpenOld);
      DeleteObject(hpenLtGray);
      ReleaseDC(td->w, hdc);
    }

    /* Redraw the contents */
    InvalidateRect (td->w, NULL, TRUE);
    Term_redraw();
}


/*** Function hooks needed by "Term" ***/
/*
 * Initialize a new Term
 */
static void Term_init_win(term *t)
{
    /* XXX */
}


/*
 * Nuke an old Term
 */
static void Term_nuke_win(term *t)
{
    /* XXX */
}


/*
 * React to changing global options
 */
static errr Term_xtra_win_react(void)
{
  DWORD dw;

  /* Show */
  if (use_screen_win && !screen.mapped)
  {
    screen.mapped = TRUE;
    ShowWindow(screen.w, SW_SHOW);
    term_data_redraw(&screen);
  }
  /* Hide */
  if (!use_screen_win && screen.mapped)
  {
    screen.mapped = FALSE;
    ShowWindow(screen.w, SW_HIDE);
  }

  /* Show */
  if (use_recall_win && !recall.mapped)
  {
    recall.mapped = TRUE;
    ShowWindow(recall.w, SW_SHOWNOACTIVATE);
  }
  /* Hide */
  if (!use_recall_win && recall.mapped)
  {
    recall.mapped = FALSE;
    ShowWindow(recall.w, SW_HIDE);
  }

  /* Show */
  if (use_choice_win && !choice.mapped)
  {
    choice.mapped = TRUE;
    ShowWindow(choice.w, SW_SHOWNOACTIVATE);
  }
  /* Hide */
  if (!use_choice_win && choice.mapped)
  {
    choice.mapped = FALSE;
    ShowWindow(choice.w, SW_HIDE);
  }

  /* Show */
  if (use_mirror_win && !mirror.mapped)
  {
    mirror.mapped = TRUE;
    ShowWindow(mirror.w, SW_SHOWNOACTIVATE);
  }
  /* Hide */
  if (!use_mirror_win && mirror.mapped)
  {
    mirror.mapped = FALSE;
    ShowWindow(mirror.w, SW_HIDE);
  }

  /* do something about resizability of main window */
  dw = GetWindowLong(screen.w, GWL_STYLE);
  /* Make it resizable */
  if (screen_resizable && !(dw & WS_THICKFRAME))
  {
    screen.dwStyle = dw | WS_THICKFRAME | WS_VSCROLL | WS_HSCROLL | WS_MAXIMIZEBOX;
    SetWindowLong(screen.w, GWL_STYLE, screen.dwStyle);
    term_getsize(&screen);
    term_window_resize(&screen);
  }
  /* Make it non-resizable */
  if (!screen_resizable && (dw & WS_THICKFRAME))
  {
    screen.dwStyle = dw & ~WS_THICKFRAME & ~WS_VSCROLL & ~WS_HSCROLL & ~WS_MAXIMIZEBOX;
    SetWindowLong(screen.w, GWL_STYLE, screen.dwStyle);
    screen.vis_rows = screen.rows;
    screen.vis_cols = screen.cols;
    screen.scroll_vpos = 0;
    screen.scroll_hpos = 0;
    term_getsize(&screen);
    term_window_resize(&screen);
  }

  return 0;
}


/*
 * Scan for events (do not block)
 */
static errr Term_xtra_win_check(int v)
{
    MSG msg;

    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }

    return 0;
}


/*
 * Do a "special thing"
 */
static errr Term_xtra_win(int n, int v)
    {
    HDC hdc;
    RECT rc;

    term_data *td = (term_data*)(Term->data);

    /* Handle a subset of the legal requests */
    switch (n)
    {
       /* Handle "beep" request */
	case TERM_XTRA_NOISE:
        MessageBeep(0);
        return 0;
	
    case TERM_XTRA_EVENT:
        return Term_xtra_win_check(v);
    
    case TERM_XTRA_SOUND:
	if (use_sound)
	    PlaySound (win_sounds[v - 1], NULL, SND_FILENAME | SND_ASYNC);
        break;

    case TERM_XTRA_FROSH:
        return 0;

    case TERM_XTRA_CLEAR:
        GetClientRect (td->w, &rc);
        hdc = GetDC (td->w);
        FillRect (hdc, &rc, GetStockObject (BLACK_BRUSH));
        ReleaseDC (td->w, hdc);
        ValidateRect (td->w, NULL);
        return 0;
    }
    /* Oops */
    return 1;
    }


/*
 * Low level graphics (Assumes valid input).
 * Draw a "cursor" at (x,y), using a "yellow box".
 */
static errr Term_curs_win(int x, int y)
    {
    term_data *td = (term_data*)(Term->data);
    RECT   rc;
    HDC    hdc;

    /* No cursor on recall/choice windows */
    if (td->type != WTY_SCREEN) return 0;

    /* clip to visible part of window */
    x -= td->scroll_hpos;
    y -= td->scroll_vpos;
    if ((x < 0) || (x > td->vis_cols) || (y < 0) || (y > td->vis_rows))
	return 0;

    /* Frame the grid */
    rc.left   = x * td->font_wid + td->size_ow1;
    rc.right  = rc.left + td->font_wid;
    rc.top    = y * td->font_hgt + td->size_oh1;
    rc.bottom = rc.top + td->font_hgt;

    hdc = GetDC(screen.w);
    FrameRect(hdc, &rc, hbrYellow); /* Cursor is done as a yellow "box" */
    ReleaseDC(screen.w, hdc);

    /* Success */
    return 0;
}


/*
 * Low level graphics (Assumes valid input).
 *
 * Erase a "n" characters starting at (x,y)
 */
static errr Term_wipe_win(int x, int y, int n)
{
    term_data *td = (term_data*)(Term->data);
    HDC  hdc;
    RECT rc;

    /* Rectangle to erase in client coords */
    rc.left   = x * td->font_wid + td->size_ow1;
    rc.right  = rc.left + n * td->font_wid;
    rc.top    = y * td->font_hgt + td->size_oh1;
    rc.bottom = rc.top + td->font_hgt;

    hdc = GetDC(td->w);
    SetBkColor(hdc, RGB(0,0,0));
    SelectObject(hdc, td->font_id);
    ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
    ReleaseDC(td->w, hdc);

    /* Success */
    return 0;
}


#define BM_COLS  64

static u16b char_to_grfx(byte a, byte c)
{
    if (c == 32) return 32;  /* spaces sent with any atributes */
    else return (c + (a-128)*128);
}


/*
 * Low level graphics.  Assumes valid input.
 * Draw several ("n") chars, with an attr, at a given location.
 * If *s >= 128, draw it all as graphics.
 */
static errr Term_text_win(int x, int y, int n, byte a, const char *s)
{
    term_data *td = (term_data*)(Term->data);
    RECT rc;
    HDC  hdc;
    HDC hdcMem, hdcSrc;
    HBITMAP hbmSrcOld, hbmMemOld, hbmMem;
    int  scr_x, scr_y, mem_x, i;
    int  w, h;
    u16b g;

    if ((a <= 127) || !use_graphics)   /* text output */
    {
	/* Stop illegal attributes */
	if ((a == 0) || (a >= 16)) a = TERM_RED;

	rc.left   = x * td->font_wid + td->size_ow1;
	rc.right  = rc.left + n * td->font_wid;
	rc.top    = y * td->font_hgt + td->size_oh1;
	rc.bottom = rc.top + td->font_hgt;

	/* Draw the string (will only work for mono-spaced fonts) */
	hdc = GetDC(td->w);
	SetBkColor(hdc, RGB(0,0,0));

	SetTextColor(hdc, colors16 ? PALETTEINDEX(win_pal[a]) : win_clr[a]);
	SelectObject(hdc, td->font_id);
	ExtTextOut(hdc, rc.left, rc.top, ETO_OPAQUE | ETO_CLIPPED, &rc,
		   s, n, NULL);
	ReleaseDC(td->w, hdc);
    }
    else        /* graphics output */
    {
       scr_x = x * td->font_wid + td->size_ow1;
       scr_y = y * td->font_hgt + td->size_oh1;
       w = td->font_wid;
       h = td->font_hgt;

       hdc = GetDC(td->w);
       hdcSrc = CreateCompatibleDC(hdc);
       hbmSrcOld = SelectObject(hdcSrc, infGraph.hBitmap);

       /* Only double-buffer if more than one graphics char */
       if (n == 1)
       {
	   g = char_to_grfx(a, *s);
	   BitBlt(hdc, scr_x, scr_y, w, h,
		  hdcSrc, g % BM_COLS * infGraph.CellWidth,
		  g / BM_COLS * infGraph.CellHeight, SRCCOPY);
       }
       else
       {
	   hdcMem = CreateCompatibleDC(hdc);
	   hbmMem = CreateCompatibleBitmap(hdc, n * w, h);
	   hbmMemOld = SelectObject(hdcMem, hbmMem);

	   for (i = 0, mem_x = 0; i < n; i++, mem_x += w)
	   {
	       /* Translate to graphics index */
	       g = char_to_grfx(a, s[i]);
	       BitBlt(hdcMem, mem_x, 0, w, h,
		      hdcSrc, g % BM_COLS * infGraph.CellWidth,
		      g / BM_COLS * infGraph.CellHeight, SRCCOPY);
	   }

	   BitBlt(hdc, scr_x, scr_y, n * w, h, hdcMem, 0, 0, SRCCOPY);

	   hbmMem = SelectObject(hdcMem, hbmMemOld);
	   DeleteObject(hbmMem);
	   DeleteDC(hdcMem);
       }

       SelectObject(hdcSrc, hbmSrcOld);
       DeleteDC(hdcSrc);
       ReleaseDC(td->w, hdc);
    }

    /* Success */
    return 0;
}


/*
 * Create and initialize a "term_data" given a title
 */
static void term_data_link(term_data *td)
{
    term *t = &td->t;


    /* Initialize the term */
    term_init(t, 80, 24, td->keys);

    /* Prepare the template values */
    t->soft_cursor = TRUE;
    t->scan_events = FALSE;

    /* Prepare the init/nuke hooks */
    t->init_hook = Term_init_win;
    t->nuke_hook = Term_nuke_win;
    
    /* Prepare the template hooks */
    t->xtra_hook = Term_xtra_win;
    t->curs_hook = Term_curs_win;
    t->wipe_hook = Term_wipe_win;
    t->text_hook = Term_text_win;

    /* Remember where we came from */
    t->data = (vptr)(td);
    
    /* Activate it */
    //Term_activate(t);
}


/*
 * Read the preference file, Create the windows.
 */
static void init_windows(void)
{
  term_data *td;
  MSG        msg;

  td = &screen;
  WIPE(td, term_data);
  td->s = "Angband 2.7.9v2";
  td->type = WTY_SCREEN;
  td->keys = 1024;
  td->rows = 24;
  td->cols = 80;
  td->mapped = TRUE;
  td->size_ow1 = 2;
  td->size_ow2 = 2;
  td->size_oh1 = 2;
  td->size_oh2 = 2;

  td = &recall;
  WIPE(td, term_data);
  td->s = "Recall";
  td->type = WTY_RECALL;
  td->keys = 16;
  td->rows = 12;
  td->cols = 80;
  td->mapped = TRUE;
  td->size_ow1 = 1;
  td->size_ow2 = 1;
  td->size_oh1 = 1;
  td->size_oh2 = 1;

  td = &choice;
  WIPE(td, term_data);
  td->s = "Choice";
  td->type = WTY_CHOICE;
  td->keys = 16;
  td->rows = 24;
  td->cols = 80;
  td->mapped = TRUE;
  td->size_ow1 = 1;
  td->size_ow2 = 1;
  td->size_oh1 = 1;
  td->size_oh2 = 1;

  td = &mirror;
  WIPE(td, term_data);
  td->s = "Mirror";
  td->type = WTY_MIRROR;
  td->keys = 16;
  td->rows = 24;
  td->cols = 80;
  td->mapped = TRUE;
  td->size_ow1 = 1;
  td->size_ow2 = 1;
  td->size_oh1 = 1;
  td->size_oh2 = 1;

  /* Load .INI preferences */
  load_prefs();

  /* Need these before term_getsize gets called */
  screen.dwStyle =
    WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_VISIBLE;
  if (screen_resizable)
    screen.dwStyle |= WS_THICKFRAME | WS_VSCROLL | WS_HSCROLL | WS_MAXIMIZEBOX;

  recall.dwStyle =
    IBS_VERTCAPTION | WS_OVERLAPPED | WS_THICKFRAME | WS_VSCROLL |
    WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
  //if (use_recall_win) recall.dwStyle |= WS_VISIBLE;

  choice.dwStyle =
    IBS_VERTCAPTION | WS_OVERLAPPED | WS_THICKFRAME | WS_VSCROLL |
    WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
  //if (use_choice_win) choice.dwStyle |= WS_VISIBLE;

  mirror.dwStyle =
    IBS_VERTCAPTION | WS_OVERLAPPED | WS_THICKFRAME | WS_VSCROLL |
    WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
  //if (use_choice_win) choice.dwStyle |= WS_VISIBLE;

  /* Load the fonts */
  term_load_font(&screen);
  term_load_font(&recall);
  term_load_font(&choice);
  term_load_font(&mirror);
  if (use_graphics) term_load_bitmap(screen.font_file);

  /* create two frequently used brushes (for cursor box) */
  hbrRed    = CreateSolidBrush(win_clr[TERM_RED]);
  hbrYellow = CreateSolidBrush(win_clr[TERM_YELLOW]);

  creating = WTY_SCREEN;
  screen.w = CreateWindow(AppName, screen.s, screen.dwStyle,
    screen.pos_x, screen.pos_y, screen.size_wid, screen.size_hgt,
    HWND_DESKTOP, NULL, hInstance, NULL);
  creating = 0;
  if (screen.w == 0) quit("Failed to create Angband window");

  creating = WTY_RECALL;
  recall.w = CreateWindow("AngbandList", recall.s, recall.dwStyle,
    recall.pos_x, recall.pos_y, recall.size_wid, recall.size_hgt,
    HWND_DESKTOP, NULL, hInstance, NULL);
  creating = 0;
  if (recall.w == 0) quit("Failed to create recall window");
  if (recall.cap_size)
    ibSetCaptionSize(recall.w, recall.cap_size);
  if (use_recall_win) ShowWindow(recall.w, SW_SHOWNA);

  creating = WTY_CHOICE;
  choice.w = CreateWindow("AngbandList", choice.s, choice.dwStyle,
    choice.pos_x, choice.pos_y, choice.size_wid, choice.size_hgt,
    HWND_DESKTOP, NULL, hInstance, NULL);
  creating = 0;
  if (choice.w == 0) quit("Failed to create choice window");
  if (choice.cap_size)
    ibSetCaptionSize(choice.w, choice.cap_size);
  if (use_choice_win) ShowWindow(choice.w, SW_SHOWNA);

  creating = WTY_MIRROR;
  mirror.w = CreateWindow("AngbandList", mirror.s, mirror.dwStyle,
    mirror.pos_x, mirror.pos_y, mirror.size_wid, mirror.size_hgt,
    HWND_DESKTOP, NULL, hInstance, NULL);
  creating = 0;
  if (mirror.w == 0) quit("Failed to create mirror window");
  if (mirror.cap_size)
    ibSetCaptionSize(mirror.w, mirror.cap_size);
  if (use_mirror_win) ShowWindow(mirror.w, SW_SHOWNA);

  /* Link the Mirror "term" */
  term_data_link(&mirror);
  term_mirror = &mirror.t;

  /* Link the Choice "term" */
  term_data_link(&choice);
  term_choice = &choice.t;

  /* Link the Recall "term" */
  term_data_link(&recall);
  term_recall = &recall.t;

  /* Link the Screen "term" */
  term_data_link(&screen);
  term_screen = &screen.t;

  /* Bring screen.w back to top */
  SetWindowPos(screen.w, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);

  /* Process WM_PAINT messages before printing anything */
  while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
}


/*
 * Delay for "x" milliseconds
 */
void delay(int x)
{
    DWORD t;
    MSG   msg;

    t = GetTickCount() + x;
    while(GetTickCount() < t)
    {
      if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
      {
        TranslateMessage(&msg);
	DispatchMessage(&msg);
      }
    }
}


/*
 * Delay for "x" seconds
 */
void sleep(unsigned x)
{
    delay(1000 * x);
}


/*
 * disables new and open from file menu
 */
static void disable_start(void)
{
  HMENU hm = GetMenu(screen.w);

  EnableMenuItem(hm, IDM_FILE_NEW, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
  EnableMenuItem(hm, IDM_FILE_OPEN, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
}


static void setup_menus(void)
{
  HMENU hm = GetMenu(screen.w);

  /* Hack -- extract the "can I save" flag */
  save_enabled = character_generated;

  if (save_enabled)
  {
    EnableMenuItem(hm, IDM_FILE_SAVE, MF_BYCOMMAND | MF_ENABLED);
    EnableMenuItem(hm, IDM_FILE_EXIT, MF_BYCOMMAND | MF_ENABLED);
  }
  else
  {
    EnableMenuItem(hm, IDM_FILE_SAVE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
    EnableMenuItem(hm, IDM_FILE_EXIT, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
  }

  /* Options/Font/Recall window */
  EnableMenuItem(hm, IDM_OPTIONS_FONT_RECALL, MF_BYCOMMAND |
      (use_recall_win ? MF_ENABLED : MF_DISABLED | MF_GRAYED));

  /* Options/Font/Choice window */
  EnableMenuItem(hm, IDM_OPTIONS_FONT_CHOICE, MF_BYCOMMAND |
      (use_choice_win ? MF_ENABLED : MF_DISABLED | MF_GRAYED));

  /* Options/Font/Mirror window */
  EnableMenuItem(hm, IDM_OPTIONS_FONT_MIRROR, MF_BYCOMMAND |
      (use_mirror_win ? MF_ENABLED : MF_DISABLED | MF_GRAYED));

  /* Item "Recall Window" */
  CheckMenuItem(hm, IDM_OPTIONS_RECALL, MF_BYCOMMAND |
      (use_recall_win ? MF_CHECKED : MF_UNCHECKED));

  /* Item "Choice Window" */
  CheckMenuItem(hm, IDM_OPTIONS_CHOICE, MF_BYCOMMAND |
      (use_choice_win ? MF_CHECKED : MF_UNCHECKED));

  /* Item "Mirror Window" */
  CheckMenuItem(hm, IDM_OPTIONS_MIRROR, MF_BYCOMMAND |
      (use_mirror_win ? MF_CHECKED : MF_UNCHECKED));

  /* Item "Main window resizable" */
  CheckMenuItem(hm, IDM_OPTIONS_RESIZABLE, MF_BYCOMMAND |
      (screen_resizable ? MF_CHECKED : MF_UNCHECKED));

  /* Item "Sound" */
  CheckMenuItem(hm, IDM_OPTIONS_SOUND, MF_BYCOMMAND |
      (use_sound ? MF_CHECKED : MF_UNCHECKED));
}


static void check_for_save_file(LPSTR cmd_line)
{
  char *p;

  /* isolate first argument in command line */
  p = strchr(cmd_line, ' ');
  if (p) *p = '\0';

  if (strlen(cmd_line) == 0) return;

  /* XXX XXX XXX Note the change */
  sprintf(savefile, "%s%s", ANGBAND_DIR_SAVE, cmd_line);
  validate_file(savefile);

  game_in_progress = TRUE;
  disable_start();

  play_game(FALSE);

  quit(NULL);
}


static void process_menus(WPARAM wParam)
{
  OPENFILENAME ofn;

  switch (wParam)
  {
    case IDM_FILE_NEW: /* new game */
      if (!initialized)
      {
        MessageBox(screen.w, "You cannot do that yet...",
           "Warning", MB_ICONEXCLAMATION | MB_OK);
      }
      else if (game_in_progress)
      {
        MessageBox(screen.w,
           "You can't start a new game while you're still playing!",
           "Warning", MB_ICONEXCLAMATION | MB_OK);
      }
      else
      {
        game_in_progress = TRUE;
        disable_start();
        Term_flush();
        play_game(TRUE);
        quit(NULL);
      }
      break;

    case IDM_FILE_OPEN: /* open game */
      if (!initialized)
      {
	MessageBox(screen.w, "You cannot do that yet...",
           "Warning", MB_ICONEXCLAMATION | MB_OK);
      }
      else if (game_in_progress)
      {
        MessageBox(screen.w,
           "You can't open a new game while you're still playing!",
           "Warning", MB_ICONEXCLAMATION | MB_OK);
      }
      else
      {
        memset(&ofn, 0, sizeof(ofn));
        ofn.lStructSize = sizeof(ofn);
        ofn.hwndOwner = screen.w;
        ofn.lpstrFilter = "Save Files (*.)\0*\0";
        ofn.nFilterIndex = 1;
        ofn.lpstrFile = savefile;
        ofn.nMaxFile = 1024;

        /* XXX XXX XXX Note that "ANGBAND_DIR_SAVE" now ends in a "\\" */
        
        ofn.lpstrInitialDir = ANGBAND_DIR_SAVE;
        ofn.Flags = OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR;

        if (GetOpenFileName(&ofn))
        {
          /* Load 'savefile' */
          validate_file(savefile);
          game_in_progress = TRUE;
          disable_start();
          Term_flush();
          play_game(FALSE);
          quit(NULL);
        }
      }
      break;

    case IDM_FILE_SAVE: /* save game */
      if (!game_in_progress)
      {
        MessageBox(screen.w, "No game in progress.",
           "Warning", MB_ICONEXCLAMATION | MB_OK);
      }
      else
      {
        Term_fresh();
        //validate_file(savefile);

        /* The player is not dead */
        (void)strcpy(died_from, "(saved)");

	/* Save the player, note the result */
        prt("Saving game...", 0, 0);
        if (save_player()) prt("done.", 0, 14);
        else prt("Save failed.", 0, 14);
        Term_fresh();
        Term_keypress(ESCAPE);

        /* Forget that the player was saved */
        character_saved = 0;

	/* Hilite the player */
        move_cursor_relative(py,px);

        /* Note that the player is not dead */
        (void)strcpy(died_from, "(alive and well)");
      }
      break;

    case IDM_FILE_EXIT: /* save and quit */

      if (game_in_progress && save_enabled)
      {
        /* Save it */
        //validate_file(savefile);
        (void)strcpy(died_from, "(saved)");
        prt("Saving game...", 0, 0);
        if (!save_player())
        {
           prt("Save failed.", 0, 14);
           Term_fresh();
        }
      }
      quit(NULL);
      break;

    case IDM_FILE_QUIT: /* quit */

      save_enabled = character_generated;

      if (game_in_progress && save_enabled)
      {
        if (IDCANCEL == MessageBox(screen.w,
           "Your character will be not saved!",
           "Warning", MB_ICONEXCLAMATION | MB_OKCANCEL)) break;
      }
      quit(NULL);
      break;

    case IDM_OPTIONS_FONT_ANGBAND:
      if (use_graphics) term_change_bitmap(&screen);
      else term_change_font(&screen);
      break;

    case IDM_OPTIONS_FONT_RECALL:
      term_change_font(&recall);
      break;

    case IDM_OPTIONS_FONT_CHOICE:
      term_change_font(&choice);
      break;

    case IDM_OPTIONS_FONT_MIRROR:
      term_change_font(&mirror);
      break;

    case IDM_OPTIONS_RECALL:
      use_recall_win = !use_recall_win;
      Term_xtra_win_react();
      break;

    case IDM_OPTIONS_CHOICE:
      use_choice_win = !use_choice_win;
      Term_xtra_win_react();
      break;

    case IDM_OPTIONS_MIRROR:
      use_mirror_win = !use_mirror_win;
      Term_xtra_win_react();
      break;

    case IDM_OPTIONS_RESIZABLE:
      screen_resizable = !screen_resizable;
      Term_xtra_win_react();
      break;

   case IDM_OPTIONS_SOUND:
       use_sound = !use_sound;
       break;
  }
}


static int process_scrollbar(HWND hWnd, WPARAM wParam, LPARAM lParam, int fnBar)
{
  term_data  *td;
  uint       *scroll_pos;
  int         rows_cols, vis_rows_cols;

  td = (term_data *)GetWindowLong(hWnd, 0);
  switch (fnBar)
  {
    case SB_VERT:
      scroll_pos = &(td->scroll_vpos);
      rows_cols = td->rows;
      vis_rows_cols = td->vis_rows;
      break;
    case SB_HORZ:
      scroll_pos = &(td->scroll_hpos);
      rows_cols = td->cols;
      vis_rows_cols = td->vis_cols;
      break;
    default:
      return 1;
  }

  switch (wParam)
  {
    /* note that SB_TOP == SB_LEFT, SB_BOTTOM == SB_RIGHT etc. */
    case SB_TOP:
      *scroll_pos = 0;
      break;

    case SB_BOTTOM:
      *scroll_pos = rows_cols - vis_rows_cols;
      break;

    case SB_LINEUP:
      if (*scroll_pos > 0) (*scroll_pos)--;
      break;

    case SB_LINEDOWN:
      if (*scroll_pos < rows_cols - vis_rows_cols) (*scroll_pos)++;
      break;

    case SB_PAGEUP:
      *scroll_pos = max((int)*scroll_pos - vis_rows_cols, 0);
      break;

    case SB_PAGEDOWN:
      *scroll_pos = min((int)*scroll_pos + vis_rows_cols, rows_cols - vis_rows_cols);
      break;

    case SB_THUMBPOSITION:
      *scroll_pos = max(min(LOWORD(lParam), rows_cols - vis_rows_cols), 0);
      break;

    default:
      return 1;
  }
  SetScrollPos(hWnd, fnBar, *scroll_pos, TRUE);
  InvalidateRect(hWnd, NULL, TRUE);
  return 0;
}


/* arrow keys translation */
static char TransDir[8] = {'9','3','1','7','4','8','6','2'};

LRESULT FAR PASCAL AngbandWndProc(HWND hWnd, UINT uMsg,
  WPARAM wParam, LPARAM lParam)
{
  PAINTSTRUCT     ps;
  HDC             hdc;
  term_data      *td;
  MINMAXINFO FAR *lpmmi;
  RECT            rc;
  BYTE            KeyState;
  int             i;

  switch (uMsg)
  {
    case WM_NCCREATE:
      if (creating == WTY_SCREEN)
        SetWindowLong(hWnd, 0, (LONG)(&screen));
      return DefWindowProc(hWnd, uMsg, wParam, lParam);

    case WM_CREATE:
      if (screen_resizable)
      {
        td = (term_data *)GetWindowLong(hWnd, 0);
        SetScrollRange(hWnd, SB_VERT, 0, max(td->rows - td->vis_rows, 1), FALSE);
        SetScrollRange(hWnd, SB_HORZ, 0, max(td->cols - td->vis_cols, 1), FALSE);
        return 0;
      }
      else
        return DefWindowProc(hWnd, uMsg, wParam, lParam);

    case WM_GETMINMAXINFO:
      lpmmi = (MINMAXINFO FAR *)lParam;
      td = (term_data *)GetWindowLong(hWnd, 0);
      if (!td) return 1;  /* this message was sent before WM_NCCREATE */

      /* minimum window size is 15x3, otherwise have problems with */
      /* menu line wrapping to two lines */
      rc.left = rc.top = 0;
      rc.right = rc.left + 15 * td->font_wid + td->size_ow1 + td->size_ow2;
      rc.bottom = rc.top + 3 * td->font_hgt + td->size_oh1 + td->size_oh2;
      if (screen_resizable)
      {
        rc.right  += GetSystemMetrics(SM_CXVSCROLL) - 1;
        rc.bottom += GetSystemMetrics(SM_CYHSCROLL) - 1;
      }
      AdjustWindowRect(&rc, td->dwStyle, TRUE);
      /* no idea why this needed */
      rc.bottom++;
      lpmmi->ptMinTrackSize.x = rc.right - rc.left;
      lpmmi->ptMinTrackSize.y = rc.bottom - rc.top;

      /* maximum window size is td->cols x td->rows */
      rc.left = rc.top = 0;
      rc.right = rc.left + td->cols * td->font_wid + td->size_ow1 + td->size_ow2;
      rc.bottom = rc.top + td->rows * td->font_hgt + td->size_oh1 + td->size_oh2;
      if (screen_resizable)
      {
        rc.right  += GetSystemMetrics(SM_CXVSCROLL) - 1;
        rc.bottom += GetSystemMetrics(SM_CYHSCROLL) - 1;
      }
      AdjustWindowRect(&rc, td->dwStyle, TRUE);
      /* no idea why this needed */
      rc.bottom++;
      lpmmi->ptMaxSize.x      = rc.right - rc.left;
      lpmmi->ptMaxSize.y      = rc.bottom - rc.top;
      lpmmi->ptMaxTrackSize.x = rc.right - rc.left;
      lpmmi->ptMaxTrackSize.y = rc.bottom - rc.top;

      return 0;

    case WM_PAINT:
      td = (term_data *)GetWindowLong(hWnd, 0);
      BeginPaint(hWnd, &ps);
      Term = &td->t;
      
      term_data_redraw(td);
      
      Term = term_screen;
      EndPaint(hWnd, &ps);
//      ValidateRect(hWnd, NULL);   /* why needed ?? */
      return 0;

    case WM_VSCROLL:
      return process_scrollbar(hWnd, wParam, lParam, SB_VERT);

    case WM_HSCROLL:
      return process_scrollbar(hWnd, wParam, lParam, SB_HORZ);

    case WM_KEYDOWN:
      KeyState = 0x00;
      if (GetKeyState(VK_SHIFT)   & 0x8000) KeyState |= 0x01;
      if (GetKeyState(VK_CONTROL) & 0x8000) KeyState |= 0x02;
      if ((wParam >= VK_PRIOR) && (wParam <= VK_DOWN))
          {
          if (!KeyState)
              {
              /* map plain keypad or arrow keys to corresp. numbers */
              Term_keypress(TransDir[wParam - VK_PRIOR]);
              return 0;
              }
          else goto enhanced;
          }
      else if ((wParam >= VK_F1) && (wParam <= VK_F12) ||
               (wParam == VK_INSERT) || (wParam == VK_DELETE))
          {
enhanced:
        /* Hack -- a special "macro introducer" */
        Term_keypress(31);
        
        /* Send the modifiers */
        if (KeyState & 0x01) Term_keypress('S');
        if (KeyState & 0x02) Term_keypress('C');

        /* The bits 16..23 in lParam are the scancode */
        i = LOBYTE(HIWORD(lParam));

        /* Hack -- encode the keypress (in decimal) */
        Term_keypress('0' + (i % 1000) / 100);
        Term_keypress('0' + (i % 100) / 10);
        Term_keypress('0' + (i % 10));

        /* End the macro with "return" */
        Term_keypress(13);

        return 0;
      }
      else return DefWindowProc(hWnd, uMsg, wParam, lParam);

    case WM_CHAR:
      Term_keypress(wParam);
      return 0;

    case WM_INITMENU:
      setup_menus();
      return 0;

    case WM_CLOSE:
    case WM_QUIT:
      quit(NULL);
      return 0;

    case WM_COMMAND:
      process_menus(wParam);
      return 0;

    case WM_SIZE:
      td = (term_data *)GetWindowLong(hWnd, 0);
      if (!td) return 1;    /* this message was sent before WM_NCCREATE */
      if (!td->w) return 1; /* it was sent from inside CreateWindow */

      switch (wParam)
      {
        case SIZE_MINIMIZED:
          if (use_recall_win) ShowWindow(recall.w, SW_HIDE);
          if (use_choice_win) ShowWindow(choice.w, SW_HIDE);
          if (use_mirror_win) ShowWindow(mirror.w, SW_HIDE);
          return 0;

        case SIZE_MAXIMIZED:
          td->vis_cols = td->cols;
          td->vis_rows = td->rows;
          /* fall through!!! */

        case SIZE_RESTORED:
          if (screen_resizable)
          {
            td->vis_cols = (LOWORD(lParam) - td->size_ow1 - td->size_ow2) / td->font_wid;
            td->vis_cols = min(td->vis_cols, td->cols);
            td->vis_rows = (HIWORD(lParam) - td->size_oh1 - td->size_oh2) / td->font_hgt;
            td->vis_rows = min(td->vis_rows, td->rows);
            td->scroll_vpos = 0;
            td->scroll_hpos = 0;
            SetScrollRange(hWnd, SB_VERT, 0, max(td->rows - td->vis_rows, 1), FALSE);
            SetScrollRange(hWnd, SB_HORZ, 0, max(td->cols - td->vis_cols, 1), FALSE);
            term_getsize(td);
            MoveWindow(hWnd, td->pos_x, td->pos_y, td->size_wid, td->size_hgt, TRUE);
          }
          if (use_recall_win) ShowWindow(recall.w, SW_SHOWNOACTIVATE);
          if (use_choice_win) ShowWindow(choice.w, SW_SHOWNOACTIVATE);
          if (use_mirror_win) ShowWindow(mirror.w, SW_SHOWNOACTIVATE);
          return 0;

        default:
           return DefWindowProc(hWnd, uMsg, wParam, lParam);
      }

    case WM_PALETTECHANGED:
      if (!paletted)
	return ibDefWindowProc(hWnd, uMsg, wParam, lParam); /* ignore */
      /* also ignore if palette change caused by itself */
      else if ((HWND)wParam == hWnd) return 0;

      /* otherwise, fall through!!! */

    case WM_QUERYNEWPALETTE:
      if (!paletted)
	return ibDefWindowProc(hWnd, uMsg, wParam, lParam); /* ignore */
      hdc = GetDC(hWnd);
      SelectPalette (hdc, hPal, FALSE);
      i = RealizePalette(hdc);
      ReleaseDC(hWnd, hdc);

      /* if any palette entries changed, repaint the window. */
      if (i > 0) InvalidateRect(hWnd, NULL, TRUE);
      return i;

    case WM_ACTIVATE:
      if (wParam && !HIWORD(lParam))
      {
	SetWindowPos(recall.w, hWnd, 0, 0, 0, 0, SWP_NOACTIVATE |
	  SWP_NOMOVE | SWP_NOSIZE);
	SetWindowPos(choice.w, hWnd, 0, 0, 0, 0, SWP_NOACTIVATE |
	  SWP_NOMOVE | SWP_NOSIZE);
	SetWindowPos(mirror.w, hWnd, 0, 0, 0, 0, SWP_NOACTIVATE |
	  SWP_NOMOVE | SWP_NOSIZE);
	SetFocus(hWnd);
	return 0;
      }
      /* fall through */

    default:
      return DefWindowProc(hWnd, uMsg, wParam, lParam);
  }
}


LRESULT FAR PASCAL AngbandListProc(HWND hWnd, UINT uMsg,
  WPARAM wParam, LPARAM lParam)
{
  term_data      *td;
  MINMAXINFO FAR *lpmmi;
  RECT            rc;
  PAINTSTRUCT     ps;
  HDC             hdc;
  int             i;

  switch (uMsg)
  {
    case WM_NCCREATE:
      if (creating == WTY_RECALL)
	SetWindowLong(hWnd, 0, (LONG)(&recall));
      else if (creating == WTY_CHOICE)
	SetWindowLong(hWnd, 0, (LONG)(&choice));
      else if (creating == WTY_MIRROR)
	SetWindowLong(hWnd, 0, (LONG)(&mirror));
      return ibDefWindowProc(hWnd, uMsg, wParam, lParam);

    case WM_CREATE:
      td = (term_data *)GetWindowLong(hWnd, 0);
      SetScrollRange(hWnd, SB_VERT, 0, td->rows - td->vis_rows, FALSE);
      return 0;

    case WM_GETMINMAXINFO:
      lpmmi = (MINMAXINFO FAR *)lParam;
      td = (term_data *)GetWindowLong(hWnd, 0);
      if (!td) return 1;  /* this message was sent before WM_NCCREATE */

      /* minimum window size is 5x3 */
      rc.left = rc.top = 0;
      rc.right = rc.left + 5 * td->font_wid + td->size_ow1 + td->size_ow2 +
                 GetSystemMetrics(SM_CXVSCROLL) - 1;
      rc.bottom = rc.top + 3 * td->font_hgt + td->size_oh1 + td->size_oh2;
      ibAdjustWindowRect(&rc, td->dwStyle, FALSE, td->cap_size);
      lpmmi->ptMinTrackSize.x = rc.right - rc.left;
      lpmmi->ptMinTrackSize.y = rc.bottom - rc.top;

      /* maximum window size is td->cols x td->rows */
      rc.left = rc.top = 0;
      rc.right = rc.left + td->cols * td->font_wid + td->size_ow1 + td->size_ow2 +
                 GetSystemMetrics(SM_CXVSCROLL) - 1;
      rc.bottom = rc.top + td->rows * td->font_hgt + td->size_oh1 + td->size_oh2;
      ibAdjustWindowRect(&rc, td->dwStyle, FALSE, td->cap_size);
      lpmmi->ptMaxTrackSize.x = rc.right - rc.left;
      lpmmi->ptMaxTrackSize.y = rc.bottom - rc.top;

      return 0;

    case WM_SIZE:
      td = (term_data *)GetWindowLong(hWnd, 0);
      if (!td) return 1;    /* this message was sent before WM_NCCREATE */
      if (!td->w) return 1; /* it was sent from inside CreateWindow */
      td->vis_cols = (LOWORD(lParam) - td->size_ow1 - td->size_ow2) / td->font_wid;
      td->vis_rows = (HIWORD(lParam) - td->size_oh1 - td->size_oh2) / td->font_hgt;
      td->scroll_vpos = 0;
      td->scroll_hpos = 0;
      SetScrollRange(hWnd, SB_VERT, 0, td->rows - td->vis_rows, FALSE);
      term_getsize(td);
      MoveWindow(hWnd, td->pos_x, td->pos_y, td->size_wid, td->size_hgt, TRUE);
      return 0;

    case WM_PAINT:
      td = (term_data *)GetWindowLong(hWnd, 0);
      Term = &td->t;
      BeginPaint(hWnd, &ps);
      Term_redraw();
      EndPaint(hWnd, &ps);
      Term = &screen.t;
      return 0;

    case WM_VSCROLL:
      return process_scrollbar(hWnd, wParam, lParam, SB_VERT);

    case WM_PALETTECHANGED:
      if (!paletted)
	return ibDefWindowProc(hWnd, uMsg, wParam, lParam); /* ignore */
      /* also ignore if palette change caused by itself */
      else if ((HWND)wParam == hWnd) return 0;

      /* otherwise, fall through!!! */

    case WM_QUERYNEWPALETTE:
      if (!paletted)
	return ibDefWindowProc(hWnd, uMsg, wParam, lParam); /* ignore */
      hdc = GetDC(hWnd);
      SelectPalette (hdc, hPal, FALSE);
      i = RealizePalette(hdc);
      ReleaseDC(hWnd, hdc);

      /* if any palette entries changed, repaint the window. */
      if (i > 0) InvalidateRect(hWnd, NULL, TRUE);
      return i;

    case WM_SYSCOMMAND:
      switch (wParam)
      {
        case SC_MINIMIZE:
          td = (term_data *)GetWindowLong(hWnd, 0);
          td->cap_size = max(ibGetCaptionSize(hWnd) - 1, 0);
          ibSetCaptionSize(hWnd, td->cap_size);
          SendMessage(hWnd, WM_NCPAINT, 0, 0);
          return 0;
        case SC_MAXIMIZE:
          td = (term_data *)GetWindowLong(hWnd, 0);
          td->cap_size = min(ibGetCaptionSize(hWnd) + 1, 127);
          ibSetCaptionSize(hWnd, td->cap_size);
          SendMessage(hWnd, WM_NCPAINT, 0, 0);
          return 0;
	default:
          return ibDefWindowProc(hWnd, uMsg, wParam, lParam);
      }

    case WM_NCLBUTTONDOWN:
      if (wParam == HTSYSMENU)
	  {
          td = (term_data *)GetWindowLong(hWnd, 0);

	  if (td->type == WTY_RECALL)
	      use_recall_win = FALSE;
	  else if (td->type == WTY_CHOICE)
	      use_choice_win = FALSE;
	  else if (td->type == WTY_MIRROR)
	      use_mirror_win = FALSE;
	  
          Term_xtra_win_react();
          return 0;
      }
    /* fall through */

    default:
      return ibDefWindowProc(hWnd, uMsg, wParam, lParam);
  }
}


/*
 * Reads ANGBAND.INI, gets lib_path, checks for existence,
 * initializes the various file paths
 */
static void init_stuff(void)
{
  int   i;

  char path[1024];
  
  GetPrivateProfileString("Angband", "LibPath", "c:\\angband\\lib", path, 128, ini_file);

  /* subtract \\ from the end if present */
  if ((i = strlen(path)) == 0)
      quit("LibPath shouldn't be empty in ANGBAND.INI");
  if (path[i-1] == '\\') path[i-1] = '\0';
  else i++;

  validate_dir(path);

  /* add \ back */
  path[i-1] = '\\';
  path[i] = '\0';

  /* Init the file paths */
  init_file_paths(path);

  /* Validate important directories */
}


/*** Some Hooks for various routines ***/
/*
 * See "z-virt.c"
 */
static vptr hook_ralloc(huge size)
{
    if (size > 0xFFFF)
	quit("Tried to malloc more than 64K");
    return (vptr)malloc((size_t)size);
}


static errr hook_rnfree(vptr v, huge size)
{
    free(v);
    return 0;
}


/*
 * See "z-util.c"
 */
static void hook_plog(cptr str)
{
    MessageBox(screen.w, str, "Warning", MB_OK);
}


static void hook_quit(cptr str)
{
    if (str)
	MessageBox(screen.w, str, "Error", MB_OK | MB_ICONSTOP);

    save_prefs();

    if (recall.w)
    {
        DestroyWindow(recall.w);
        recall.w = 0;
    }
    if (choice.w)
    {
        DestroyWindow(choice.w);
        choice.w = 0;
    }
    if (mirror.w)
    {
        DestroyWindow(mirror.w);
        mirror.w = 0;
    }
    if (screen.w)
    {
        DestroyWindow(screen.w);
        screen.w = 0;
    }

    DeleteObject(hbrRed);
    DeleteObject(hbrYellow);
    if (hPal) DeleteObject(hPal);
    if (infGraph.hDIB) GlobalFree(infGraph.hDIB);
    if (infGraph.hPalette) DeleteObject(infGraph.hPalette);
    if (infGraph.hBitmap) DeleteObject(infGraph.hBitmap);

    if (screen.font_id) DeleteObject(screen.font_id);
    if (screen.font_file)
    {
      RemoveFontResource(screen.font_file);
      string_free(screen.font_file);
    }
    
    if (recall.font_id) DeleteObject(recall.font_id);
    if (recall.font_file)
    {
      RemoveFontResource(recall.font_file);
      string_free(recall.font_file);
    }

    if (choice.font_id) DeleteObject(choice.font_id);
    if (choice.font_file)
    {
      RemoveFontResource(choice.font_file);
      string_free(choice.font_file);
    }

    if (mirror.font_id) DeleteObject(mirror.font_id);
    if (mirror.font_file)
    {
      RemoveFontResource(mirror.font_file);
      string_free(mirror.font_file);
    }

    UnregisterClass(AppName, hInstance);
    if (hIcon) DestroyIcon(hIcon);

    exit(0);
}


static void hook_core(cptr str)
{
    if (str)
      MessageBox(screen.w, str, "Error", MB_OK);

    MessageBox(screen.w, "I will now attempt to save and quit.",
      "Fatal error", MB_OK | MB_ICONSTOP);
    save_player();
    quit(NULL);
}


int FAR PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrevInst,
  LPSTR lpCmdLine, int nCmdShow)
{
  WNDCLASS wc;
  HDC      hdc;
  MSG      msg;
  char    *tmp;

  hInstance = hInst;  /* save in a global var */

  if (hPrevInst == NULL)
  {
    wc.style         = CS_CLASSDC;
    wc.lpfnWndProc   = AngbandWndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 4; /* one long pointer to term_data */
    wc.hInstance     = hInst;
    wc.hIcon         = hIcon = LoadIcon(hInst, AppName);
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = CreateSolidBrush(RGB(0,0,0));
    wc.lpszMenuName  = AppName;
    wc.lpszClassName = AppName;

    if (!RegisterClass(&wc)) exit(1);

    wc.lpfnWndProc   = AngbandListProc;
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = "AngbandList";

    if (!RegisterClass(&wc)) exit(2);
  }

  /* Hook in some "z-virt.c" hooks */
  ralloc_aux = hook_ralloc;
  rnfree_aux = hook_rnfree;

  /* Hooks in some "z-util.c" hooks */
  plog_aux = hook_plog;
  quit_aux = hook_quit;
  core_aux = hook_core;

  use_sound = TRUE;

  /* get ANGBAND.INI full path */
  tmp = (char *)ralloc(512);
  GetModuleFileName(hInstance, tmp, 512);
  strcpy(tmp + strlen(tmp) - 4, ".INI");
  ini_file = string_make(tmp);
  rnfree(tmp, 512);
  validate_file((char *)ini_file);

  /* Initialize some stuff */
  init_stuff();

  /* XXX XXX XXX */
  
  /* No name (yet) */
  strcpy(player_name, "");

  /* Hack -- assume wizard permissions */
  can_be_wizard = TRUE;

  /* Hack -- Choose a "pref-xxx.prf" file */
  ANGBAND_SYS = (use_graphics ? "gfw" : "txw");

  /* Prepare the windows */
  init_windows();

  /* Determine if display is 16/256/true color */
  hdc = GetDC(NULL);
  colors16 = GetDeviceCaps(hdc, BITSPIXEL) == 4;
  paletted =
    (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) ? TRUE : FALSE;
  ReleaseDC(NULL, hdc);

  /* If 256 colors, create/realize palette to use with 16 entries */
  if (paletted)
  {
    int          i;
    LPLOGPALETTE pLogPal =
      (LPLOGPALETTE)ralloc(sizeof(LOGPALETTE) + 16*sizeof(PALETTEENTRY));

    pLogPal->palVersion = 0x300;
    pLogPal->palNumEntries = 16;
    for (i = 0; i < 16; i++)
    {
      pLogPal->palPalEntry[i].peRed = GetRValue(win_clr[i]);
      pLogPal->palPalEntry[i].peGreen = GetGValue(win_clr[i]);
      pLogPal->palPalEntry[i].peBlue = GetBValue(win_clr[i]);
      pLogPal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
    }
    if (!(hPal = CreatePalette((LPLOGPALETTE) pLogPal)))
      quit("Cannot create palette");
    rnfree(pLogPal, 1);

    hdc = GetDC(screen.w);
    SelectPalette(hdc, hPal, 0);
    i = RealizePalette(hdc);
    ReleaseDC(screen.w, hdc);
    if (i == 0) quit("Cannot realize palette");

    hdc = GetDC(recall.w);
    SelectPalette(hdc, hPal, 0);
    //i = RealizePalette(hdc);
    ReleaseDC(recall.w, hdc);
    //if (i == 0) quit("Cannot realize palette");

    hdc = GetDC(choice.w);
    SelectPalette(hdc, hPal, 0);
    //i = RealizePalette(hdc);
    ReleaseDC(choice.w, hdc);
    //if (i == 0) quit("Cannot realize palette");

    hdc = GetDC(mirror.w);
    SelectPalette(hdc, hPal, 0);
    //i = RealizePalette(hdc);
    ReleaseDC(mirror.w, hdc);
    //if (i == 0) quit("Cannot realize palette");
  }

  /* Display the "news" message */
  Term_activate(term_screen);
  show_news();

  /* Allocate and Initialize various arrays */
  init_some_arrays();

  /* We are now initialized */
  initialized = TRUE;
    
  /* Did the user double click on a save file? */
  check_for_save_file(lpCmdLine);

  /* Prompt the user */
  Term_activate(term_screen);
  prt("[Choose 'New' or 'Open' from the 'File' menu]", 23, 17);
  Term_fresh();

  /* Process messages (until "play_game()" is called) */
  while (GetMessage(&msg, NULL, 0, 0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  quit(NULL);
  return 0;
}

#endif /* _Windows */

