/* File: main-ami.c */

/*
 * Copyright (c) 1997 Ben Harrison, Lars Haugseth, and others
 *
 * 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.
 */

/*
   File:     main-ami.c

   Version:  2.7.9v6 (09.May.96)

   Purpose:  Amiga module for Angband with graphics and sound

   Author:   Lars Haugseth
   Email:    larshau@ifi.uio.no
   WWW:      http://www.ifi.uio.no/~larshau

*/

#define VERSION "Angband 2.7.9v6"

///{ "includes"

#include "angband.h"
#include "sound-ami.h"
#include <exec/memory.h>
#include <intuition/intuitionbase.h>
#include <intuition/intuition.h>
#include <intuition/screens.h>
#include <devices/inputevent.h>
#include <graphics/gfxbase.h>
#include <graphics/modeid.h>
#include <graphics/scale.h>
#include <graphics/text.h>
#include <libraries/asl.h>
#include <libraries/gadtools.h>
#include <proto/asl.h>
#include <proto/exec.h>
#include <proto/diskfont.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/keymap.h>
#include <proto/gadtools.h>

///}
///{ "macros"

/* Pen number convertion */
#define PEN( p ) ( penconv[ p ] )

/* Graphics pen number convertion */
#define GPEN( p ) ( use_pub ? pubpens[ p ] : p )

/* Failure */
#define FAIL( str ) return ( amiga_fail( str ))

/* Message */
#define MSG( x, y, txt ) amiga_text( x, y, strlen( txt ), 1, txt );

/* Char and attr under cursor */
#define CUR_A ( td->t.scr->a[ td->cursor_ypos ][ td->cursor_xpos ] )
#define CUR_C ( td->t.scr->c[ td->cursor_ypos ][ td->cursor_xpos ] )

///}
///{ "defines"

/* Color to use for cursor */
#define CURSOR_PEN 4

/* Size of graphical tile */
#define TILEW 8
#define TILEH 8

/* Size of tile image */
#define GFXW 256
#define GFXH 256
#define GFXB 32

/* Size of tombstone image */
#define TOMW 512
#define TOMH 168
#define TOMB 64

/* Filename of tile image */
#define MGFX "Angband:xtra/gfx/tiles.raw"

/* Filename of tombstone image */
#define MTOM "Angband:xtra/gfx/tomb.raw"

/* Filename of preferences file */
#define WPRF "Angband:user/settings.prf"

///}
///{ "globals"

/* DisplayID specified with option -m */
char modestr[ 256 ] = "";

/* Font specified with option -f */
char fontstr[ 32 ] = "";

/* Library bases */
struct Library *DiskfontBase = NULL;
struct Library *KeymapBase = NULL;
struct Library *GadToolsBase = NULL;

/* Term data structure */
typedef struct term_data
{
   term t;                  /* Term structure */
   cptr name;               /* Name string */

   BYTE use;                /* Use this window */

   BYTE cols;               /* Number of columns */
   BYTE rows;               /* Number of rows */

   UWORD wx;                /* Window x-pos */
   UWORD wy;                /* Window y-pos */
   UWORD ww;                /* Window width */
   UWORD wh;                /* Window height */

   BYTE fw;                 /* Font width */
   BYTE fh;                 /* Font height */
   BYTE fb;                 /* Font baseline */

   struct TextFont *font;   /* Font pointers */

   BYTE ownf;               /* Font is owned by this term */

   struct Window *win;      /* Window pointer */
   struct RastPort *wrp;    /* RastPort of window */
   struct RastPort *rp;     /* RastPort of screen or window */

   struct BitMap *gfxbm;
   struct BitMap *mapbm;
   struct BitMap *mskbm;

   int gfx_w;
   int gfx_h;

   int map_w;
   int map_h;
   int map_x;
   int map_y;

   int mpt_w;
   int mpt_h;

   int cursor_xpos;
   int cursor_ypos;
   int cursor_visible;
   int cursor_lit;
   int cursor_frame;
   int cursor_map;

   int notitle;
   int avoidbar;
}
term_data;

/* Term data for all windows */
static term_data screen;
static term_data mirror;
static term_data recall;
static term_data choice;

/* Window names */
static char screen_name[] = "Main";
static char choice_name[] = "Choice";
static char recall_name[] = "Recall";
static char mirror_name[] = "Mirror";

/* Screen pointers */
static struct Screen *amiscr = NULL;
static struct Screen *pubscr = NULL;

/* Visual info for gadtools menus */
static APTR *visinfo;

/* TextAttr for screen font */
static struct TextAttr ScrAttr, *scrattr = &ScrAttr;

/* Screen DisplayID */
static ULONG scr_m = 0;

/* Last term for cursor */
static term_data *term_curs = NULL;

/* Temporary string */
static char tmpstr[ 256 ];

/* Iconify status of windows */
static int iconified = FALSE;

/* Use intuition menus? */
static int use_menus = TRUE;

/* Use mouseblanking? */
static int blankmouse = FALSE;

/* Window input event */
static struct InputEvent ie;

/* KickStart 3.0+ present */
static int v39 = FALSE;

/* Use a public screen */
static int use_pub = FALSE;

/* Public screen lock */
static int publock = FALSE;

/* Use a backdrop main window */
static int backdrop = FALSE;

/* Use 32 colors on custom screen */
static int deep = FALSE;

/* Invisible pointer for blanking */
static __chip UWORD blankpointer[] = { 0,0,0,0,0,0,0,0 };

/* Pointer visibility status */
static int pointer_visible = TRUE;

/* Convert textual pens to screen pens */
static UWORD penconv[ 16 ] =
{
   0,1,2,4,11,15,9,6,3,1,13,4,11,15,8,5
};

/* Public screen obtained pens */
static LONG pubpens[ 32 ] =
{
  -1,-1,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,-1,
  -1,-1,-1,-1,-1,-1,-1,-1
};

/* Default color palette, 16 for graphics, 16 for text */
static ULONG default_colors[ 32 ] =
{
   0x000000, 0xf0e0d0, 0x808080, 0x505050,
   0xe0b000, 0xc0a070, 0x806040, 0x403020,
   0x00a0f0, 0x0000f0, 0x000070, 0xf00000,
   0x800000, 0x9000b0, 0x006010, 0x60f040,

   0x000000, 0xffffff, 0xc7c7c7, 0xff9200,
   0xff0000, 0x00cd00, 0x0000fe, 0xc86400,
   0x8a8a8a, 0xe0e0e0, 0xa500ff, 0xfffd00,
   0xff00bc, 0x00ff00, 0x00c8ff, 0xffcc80
};

/* Palette, 32 bits per gun */
static ULONG palette32[ 32 * 3 + 2 ];

/* Palette, 4 bits per gun */
static UWORD palette4[ 32 ];

/* Version string */
static char ver[] = "$VER: " VERSION " (" __DATE__ ")";

///}
///{ "sound"

#define NSAMPLES 25

struct AmiSound
{
   char *Name;
   int Volume;
   int Channel;
   int Rate;
   int Repeats;
   int Memory;
   struct SoundInfo *Address;
};

static struct AmiSound sound_data[ NSAMPLES ] =
{
   { "intro.8svx",     64, 0, 0, 1, 0, NULL }, /* Intro */
   { "hit.8svx",       64, 0, 0, 1, 1, NULL }, /* Hit */
   { "miss.8svx",      64, 3, 0, 1, 1, NULL }, /* Miss */
   { "flee.8svx",      64, 1, 0, 1, 1, NULL }, /* Flee */
   { "drop.8svx",      64, 2, 0, 1, 1, NULL }, /* Drop */
   { "kill.8svx",      64, 1, 0, 1, 1, NULL }, /* Kill */
   { "study.8svx",     64, 2, 0, 1, 1, NULL }, /* Level */
   { "death.8svx",     64, 0, 0, 1, 0, NULL }, /* Death */
   { "study.8svx",     64, 2, 0, 1, 1, NULL }, /* Study */
   { "teleport.8svx",  64, 3, 0, 1, 1, NULL }, /* Teleport */
   { "shoot.8svx",     64, 0, 0, 1, 1, NULL }, /* Shoot */
   { "quaff.8svx",     64, 1, 0, 1, 1, NULL }, /* Quaff */
   { "zap.8svx",       64, 3, 0, 1, 1, NULL }, /* Zap */
   { "walk.8svx",      64, 0, 0, 1, 1, NULL }, /* Walk */
   { "tpother.8svx",   64, 3, 0, 1, 1, NULL }, /* Teleport Other */
   { "hitwall.8svx",   64, 3, 0, 1, 1, NULL }, /* Hit Wall */
   { "eat.8svx",       64, 3, 0, 1, 1, NULL }, /* Eat */
   { "store1.8svx",    64, 0, 0, 1, 0, NULL }, /* Shopkeeper furious */
   { "store2.8svx",    64, 0, 0, 1, 0, NULL }, /* Shopkeeper angry */
   { "store3.8svx",    64, 0, 0, 1, 0, NULL }, /* Shopkeeper glad */
   { "store4.8svx",    64, 0, 0, 1, 0, NULL }, /* Shopkeeper happy */
   { "dig.8svx",       64, 0, 0, 1, 1, NULL }, /* Dig */
   { "opendoor.8svx",  64, 0, 0, 1, 1, NULL }, /* Open door */
   { "closedoor.8svx", 64, 0, 0, 1, 1, NULL }, /* Close door */
   { "tplevel.8svx",   64, 0, 0, 1, 0, NULL }, /* Teleport level */
};

static int channel_last[ 4 ] = { -1, -1, -1, -1 };

static int has_sound = FALSE;

///}
///{ "menus"

#define MENUMAX 256

/* Menu userdata indexes */
#define MNU_SCALEDMAP   1001

/* Special offset indexes */
#define MNU_KEYCOM      2001
#define MNU_CKEYCOM     3001
#define MNU_OPTION      4001
#define MNU_HELP        5001

/* Macro for menu userdata keycodes and help */
#define MKC( c ) (void *)( MNU_KEYCOM + c )
#define MCC( c ) (void *)( MNU_CKEYCOM + c )
#define MHL( c ) (void *)( MNU_HELP + c )

struct Menu *menu = NULL;

/* Menu items that comes before the options */
struct NewMenu pre_item[] =
{
   { NM_TITLE, "Options", 0, 0, 0, 0 },
   { 255, 0, 0, 0, 0, 0 }
};

/* Option menu items, titles of submenus */
struct NewMenu opt_item[] =
{
   { NM_ITEM, "User Interface", 0, 0, 0, 0 },
   { NM_ITEM, "Disturbance", 0, 0, 0, 0 },
   { NM_ITEM, "Inventory", 0, 0, 0, 0 },
   { NM_ITEM, "Game-Play", 0, 0, 0, 0 },
   { NM_ITEM, "Efficiency", 0, 0, 0, 0 },
   { NM_ITEM, "Special", 0, 0, 0, 0 },
   { NM_ITEM, "Cheating", 0, 0, 0, 0 },
   { 255, 0, 0, 0, 0, 0 }
};

/* Menu items that comes after the options */
struct NewMenu post_item[] =
{
     /*
     { NM_ITEM, "Hitpoint Warning", 0, 0, 0, 0 },
       { NM_SUB, "90%", 0, CHECKIT, ~1, 0 },
       { NM_SUB, "80%", 0, CHECKIT, ~2, 0 },
       { NM_SUB, "70%", 0, CHECKIT, ~4, 0 },
       { NM_SUB, "60%", 0, CHECKIT, ~8, 0 },
       { NM_SUB, "50%", 0, CHECKIT, ~16, 0 },
       { NM_SUB, "40%", 0, CHECKIT, ~32, 0 },
       { NM_SUB, "30%", 0, CHECKIT, ~64, 0 },
       { NM_SUB, "20%", 0, CHECKIT, ~128, 0 },
       { NM_SUB, "10%", 0, CHECKIT, ~256, 0 },
       { NM_SUB, "Off", 0, CHECKIT, ~512, 0 },
     */

   { NM_TITLE, "Cmd1", 0, 0, 0, 0 },
     { NM_ITEM, "a  Aim a wand", 0, 0, 0, MKC('a') },
     { NM_ITEM, "b  Browse a book", 0, 0, 0, MKC('b') },
     { NM_ITEM, "c  Close a door", 0, 0, 0, MKC('c') },
     { NM_ITEM, "d  Drop an item", 0, 0, 0, MKC('d') },
     { NM_ITEM, "e  Equipment list", 0, 0, 0, MKC('e') },
     { NM_ITEM, "f  Fire an item", 0, 0, 0, MKC('f') },
     { NM_ITEM, "g  Stay still", 0, 0, 0, MKC('g') },
     { NM_ITEM, "i  Inventory list", 0, 0, 0, MKC('i') },
     { NM_ITEM, "j  Jam a door", 0, 0, 0, MKC('j') },
     { NM_ITEM, "k  Destroy an item", 0, 0, 0, MKC('k') },
     { NM_ITEM, "l  Look around", 0, 0, 0, MKC('l') },

   { NM_TITLE, "Cmd2", 0, 0, 0, 0 },
     { NM_ITEM, "m  Cast a spell", 0, 0, 0, MKC('m') },
     { NM_ITEM, "o  Open a door or chest", 0, 0, 0, MKC('o') },
     { NM_ITEM, "p  Pray a prayer", 0, 0, 0, MKC('p') },
     { NM_ITEM, "q  Quaff a potion", 0, 0, 0, MKC('q') },
     { NM_ITEM, "r  Read a scroll", 0, 0, 0, MKC('r') },
     { NM_ITEM, "s  Search for traps/doors", 0, 0, 0, MKC('s') },
     { NM_ITEM, "t  Take off equipment", 0, 0, 0, MKC('t') },
     { NM_ITEM, "u  Use a staff", 0, 0, 0, MKC('u') },
     { NM_ITEM, "v  Throw an item", 0, 0, 0, MKC('v') },
     { NM_ITEM, "w  Wear/wield equipment", 0, 0, 0, MKC('w') },
     { NM_ITEM, "z  Zap a rod", 0, 0, 0, MKC('z') },

   { NM_TITLE, "Cmd3", 0, 0, 0, 0 },
     { NM_ITEM, "A  Activate an artifact", 0, 0, 0, MKC('A') },
     { NM_ITEM, "B  Bash a door", 0, 0, 0, MKC('B') },
     { NM_ITEM, "C  Character description", 0, 0, 0, MKC('C') },
     { NM_ITEM, "D  Disarm a trap", 0, 0, 0, MKC('D') },
     { NM_ITEM, "E  Eat some food", 0, 0, 0, MKC('E') },
     { NM_ITEM, "F  Fuel your lantern/torch", 0, 0, 0, MKC('F') },
     { NM_ITEM, "G  Gain new spells/prayers", 0, 0, 0, MKC('G') },
     { NM_ITEM, "I  Observe an item", 0, 0, 0, MKC('I') },
     { NM_ITEM, "L  Locate player on map", 0, 0, 0, MKC('L') },
     { NM_ITEM, "M  Full dungeon map", 0, 0, 0, MKC('M') },
     { NM_ITEM, "Q  Quit (commit suicide)", 0, 0, 0, MKC('Q') },
     { NM_ITEM, "R  Rest for a period", 0, 0, 0, MKC('R') },
     { NM_ITEM, "S  Toggle search mode", 0, 0, 0, MKC('S') },
     { NM_ITEM, "T  Dig a tunnel", 0, 0, 0, MKC('T') },
     { NM_ITEM, "V  Version info", 0, 0, 0, MKC('V') },

   { NM_TITLE, "Cmd4", 0, 0, 0, 0 },
     { NM_ITEM, "@  Interact with macros", 0, 0, 0, MKC('@') },
     { NM_ITEM, "%  Interact with visuals", 0, 0, 0, MKC('%') },
     { NM_ITEM, "&  Interact with colors", 0, 0, 0, MKC('&') },
     { NM_ITEM, "*  Target monster or location", 0, 0, 0, MKC('*') },
     { NM_ITEM, "(  Load screen dump", 0, 0, 0, MKC('(') },
     { NM_ITEM, ")  Dump screen dump", 0, 0, 0, MKC(')') },
     { NM_ITEM, "{  Inscribe an object", 0, 0, 0, MKC('{') },
     { NM_ITEM, "}  Uninscribe an object", 0, 0, 0, MKC('}') },
     { NM_ITEM, "-  Walk (flip pickup)", 0, 0, 0, MKC('-') },

   { NM_TITLE, "Cmd5", 0, 0, 0, 0 },
     { NM_ITEM, "+  Dig tunnel", 0, 0, 0, MKC('+') },
     { NM_ITEM, "=  Set options", 0, 0, 0, MKC('=') },
     { NM_ITEM, ";  Walk (with pickup)", 0, 0, 0, MKC(';') },
     { NM_ITEM, ":  Take notes", 0, 0, 0, MKC(':') },
     { NM_ITEM, "\"  Enter a user pref command", 0, 0, 0, MKC('\"') },
     { NM_ITEM, ",  Stay still (with pickup)", 0, 0, 0, MKC(',') },
     { NM_ITEM, "<  Go up staircase", 0, 0, 0, MKC('<') },
     { NM_ITEM, ".  Run", 0, 0, 0, MKC('.') },
     { NM_ITEM, ">  Go down staircase", 0, 0, 0, MKC('>') },
     { NM_ITEM, "/  Identify symbol", 0, 0, 0, MKC('/') },
     { NM_ITEM, "|  Check uniques", 0, 0, 0, MKC('|') },
     { NM_ITEM, "~  Check artifacts", 0, 0, 0, MKC('~') },
     { NM_ITEM, "?  Help", 0, 0, 0, MKC('?') },

   { NM_TITLE, "Cmd6", 0, 0, 0, 0 },
     { NM_ITEM, "^f  Repeat level feeling", 0, 0, 0, MCC('f') },
     { NM_ITEM, "^k  Quit", 0, 0, 0, MCC('k') },
     { NM_ITEM, "^p  Show previous messages", 0, 0, 0, MCC('p') },
     { NM_ITEM, "^r  Redraw the screen", 0, 0, 0, MCC('r') },
     { NM_ITEM, "^s  Save and don't quit", 0, 0, 0, MCC('s') },
     { NM_ITEM, "^x  Save and quit", 0, 0, 0, MCC('x') },
     { NM_ITEM,  NM_BARLABEL, 0, 0, 0, 0 },
     { NM_ITEM, "Draw dungeon map", "m", 0, 0, (void *)MNU_SCALEDMAP },

   { NM_TITLE, "Help", 0, 0, 0, 0 },
     { NM_ITEM, "General Information", 0, 0, 0, MHL('1') },
     { NM_ITEM, "Creating a Character", 0, 0, 0, MHL('2') },
     { NM_ITEM, "Exploring the Dungeon", 0, 0, 0, MHL('3') },
     { NM_ITEM, "Attacking Monsters", 0, 0, 0, MHL('4') },
     { NM_ITEM, "List of Commands", 0, 0, 0, MHL('5') },
     { NM_ITEM, "List of Options", 0, 0, 0, MHL('6') },
     { NM_ITEM, "Version Information", 0, 0, 0, MHL('7') },

   { NM_END, NULL, 0, 0, 0, 0 },
   { 255, 0, 0, 0, 0, 0 }
};

/* Menu array */
struct NewMenu newmenu[ MENUMAX ];

///}
///{ "protos"

extern void map_info( int y, int x, byte *ap, char *cp );
extern void center_string( char *buf, cptr str );
errr init_ami( void );
static void init_term( term_data *td );
static term *link_term( term_data *td );
static void free_term( term_data *td );
static char *stripstr( char *src, char *dst );
void request_font( char *str );
void request_mode( char *str );
int read_prefs( void );
static void amiga_open( term *t );
static void amiga_nuke( term *t );
static errr amiga_curs( int x, int y );
static errr amiga_wipe( int x, int y, int n );
static errr amiga_clear( void );
static errr amiga_pict( int x, int y, byte a, char c );
static errr amiga_text( int x, int y, int n, byte a, cptr s );
static errr amiga_xtra( int n, int v );
static errr amiga_flush( int v );
static errr amiga_event( int v );
static errr amiga_react( int v );
static errr amiga_fail( char *msg );
static void cursor_on( term_data *td );
static void cursor_off( term_data *td );
void amiga_tomp( void );
void tomb_str( int y, char *str );
int load_gfx( void );
int conv_gfx( void );
int size_gfx( term_data *td );
void scale_bitmap( struct BitMap *srcbm, int srcw, int srch, struct BitMap *dstbm, int dstw, int dsth );
void remap_bitmap( struct BitMap *srcbm, struct BitMap *dstbm, long *pens, int width, int height );
static void put_gfx( struct RastPort *rp, int x, int y, int chr, int col );
static void put_gfx_map( term_data *td, int x, int y, int c, int a );
static void cursor_anim( void );
void load_palette( void );
static void amiga_map( void );
int init_sound( void );
void free_sound( void );
static void play_sound( int v );
struct BitMap *alloc_bitmap( int width, int height, int depth, ULONG flags, struct BitMap *friend );
void free_bitmap( struct BitMap *bitmap );
int depth_of_bitmap( struct BitMap *bm );
int create_menus( void );
void update_menus( void );
void handle_menupick( int mnum );
void handle_rawkey( UWORD code, UWORD qual, APTR addr );

///}

///{ "init_ami()" - Initialize all Amiga spesific stuff

errr init_ami( void )
{
   char *s;
   int i;
   LONG pen;
   struct DimensionInfo diminfo;
   int pw,ph,maxw,maxh,th,barh;

   /* XXX XXX XXX */
   use_sound = arg_sound;
   use_graphics = arg_graphics;

   /* Term data pointers */
   term_data *ts = &screen;
   term_data *tc = &choice;
   term_data *tr = &recall;
   term_data *tm = &mirror;

   /* Clear the term data */
   init_term( ts );
   init_term( tc );
   init_term( tr );
   init_term( tm );

   /* Always use the main term */
   ts->use = TRUE;

   /* We *must* have kickstart 37 or later */
   if ( IntuitionBase->LibNode.lib_Version < 37 )
      FAIL( "Sorry, this program requires KickStart 2.04 or later." );

   /* Check if we have kickstart 39 or later */
   v39 = ( IntuitionBase->LibNode.lib_Version >= 39 );

   /* Open diskfont.library */
   if (( DiskfontBase = OpenLibrary( "diskfont.library", 0 )) == NULL )
      FAIL( "Unable to open diskfont.library." );

   /* Read preferences file */
   read_prefs();

   /* Initialize keyboard stuff */
   ie.ie_NextEvent = NULL;
   ie.ie_Class     = IECLASS_RAWKEY;
   ie.ie_SubClass  = 0;
   if (( KeymapBase = OpenLibrary( "keymap.library", 36 )) == NULL )
      FAIL( "Unable to open keymap.library v36+." );

   /* Open gadtools.library */
   if ( use_menus )
   {
      if (( GadToolsBase = OpenLibrary( "gadtools.library", 36 )) == NULL )
      {
         use_menus = FALSE;
      }
   }

   /* Initialize color palette */
   for ( i = 0; i < 32; i++ )
   {
      /* If undefined, use default palette */
      if ( color_table[ i ][ 0 ] == 0 &&
           color_table[ i ][ 1 ] == 0 &&
           color_table[ i ][ 2 ] == 0 &&
           color_table[ i ][ 3 ] == 0 )
      {
         color_table[ i ][ 0 ] = 1;
         color_table[ i ][ 1 ] = ( default_colors[ i ] & 0xff0000 ) >> 16;
         color_table[ i ][ 2 ] = ( default_colors[ i ] & 0x00ff00 ) >> 8;
         color_table[ i ][ 3 ] = ( default_colors[ i ] & 0x0000ff );
      }
   }

   /* Search for prefered screenmode or public screen */
   if ( strlen( modestr ) > 0 )
   {
      /* Convert string to long */
      scr_m = strtol( modestr, &s, 0 );

      /* It was not a number, so treat it as a public screen name */
      if ( scr_m == 0 )
      {
         /* We need kickstart 3.0+ to use a public screen */
         if ( !v39 )
         {
            FAIL( "Public screen can only be used on kickstart 3.0 or later." );
         }

         /* Try to lock the named public screen if it isn't already */
         if ( !pubscr )
         {
            pubscr = LockPubScreen( modestr );
         }

         /* Failed */
         if ( !pubscr ) {
            sprintf( tmpstr, "Unable to get a lock on screen '%s'.", modestr );
            FAIL( tmpstr );
         }

         /* We got a lock now */
         publock = TRUE;

         scr_m = -1;

         use_pub = TRUE;

         /* Don't blank mouse on public screen */
         blankmouse = FALSE;

         /* Find suitable pens to use on public screen */
         for ( i = 0; i < 32; i++ )
         {
            pen = ObtainBestPen( pubscr->ViewPort.ColorMap,
                                 color_table[ i ][ 1 ] << 24,
                                 color_table[ i ][ 2 ] << 24,
                                 color_table[ i ][ 3 ] << 24,
                                 OBP_Precision, PRECISION_EXACT );
            if( pen == -1 )
            {
               FAIL( "Unable to obtain suitable pens to use on public screen. ");
            }

            pubpens[ i ] = pen;
         }

         for ( i = 0; i < 16; i++ ) penconv[ i ] = (UWORD) pubpens[ i + 16 ];
      }

      /* Use specified screenmode if available */
      else
      {
         /* Check if requested mode is available */
         if ( ModeNotAvailable( scr_m )) scr_m = 0;
      }
   }

   if ( !use_pub )
   {
      /* Extra windows not allowed on custom screen */
      tc->use = tr->use = tm->use = FALSE;
   }

   /* Calculate window dimensions */
   ts->ww = ts->fw * ts->cols; ts->wh = ts->fh * ts->rows;
   tc->ww = tc->fw * tc->cols; tc->wh = tc->fh * tc->rows;
   tr->ww = tr->fw * tr->cols; tr->wh = tr->fh * tr->rows;
   tm->ww = tm->fw * tm->cols; tm->wh = tm->fh * tm->rows;

   /* Find a nice screenmode */
   if ( scr_m == 0 && v39 )
   {
      scr_m = BestModeID(
                  BIDTAG_NominalWidth, ts->ww,
                  BIDTAG_NominalHeight, ts->wh,
                  BIDTAG_DesiredWidth, ts->ww,
                  BIDTAG_DesiredHeight, ts->wh,
                  BIDTAG_Depth, 4,
                  TAG_END );
   }

   /* Use default screenmode if we don't have any */
   if ( scr_m == 0 || scr_m == INVALID_ID )
   {
      scr_m = ( DEFAULT_MONITOR_ID | HIRES_KEY );
   }

   /* Open custom screen */
   if ( !use_pub )
   {
      /* Use 32 colors with graphics */
      if ( use_graphics )
      {
         /* Get dimension data for screenmode */
         if ( GetDisplayInfoData( NULL, (UBYTE *) &diminfo, sizeof( struct DimensionInfo ), DTAG_DIMS, scr_m ))
         {
            /* Check if we support deep screens */
            if ( diminfo.MaxDepth > 4 )
            {
               /* Use 32 colors */
               deep = TRUE;

               /* Use colors 16..31 for text */
               for ( i = 0; i < 16; i++ ) penconv[ i ] = i + 16;
            }
         }
      }

      /* Use only 16 colors with no graphics */
      if ( !use_graphics )
      {
         /* Use 16 colors */
         deep = FALSE;

         /* Use colors 0..15 for text */
         for ( i = 0; i < 16; i++ ) penconv[ i ] = i;
      }

      if (( amiscr = OpenScreenTags( NULL,
             SA_Width, ts->ww,
             SA_Height, ts->wh,
             SA_Depth, deep ? 5 : 4,
             SA_DisplayID, scr_m,
             SA_Font, scrattr,
             SA_Type, CUSTOMSCREEN,
             SA_Title, "Angband Screen",
             SA_ShowTitle, FALSE,
             SA_Quiet, TRUE,
             SA_Behind, TRUE,
             SA_AutoScroll, TRUE,
             SA_Overscan, OSCAN_TEXT,
             v39 ? SA_Interleaved : TAG_IGNORE, TRUE,
             TAG_END )) == NULL)
      {
         FAIL( "Unable to open screen." );
      }

      /* Initialize screen rastport */
      ts->rp = &amiscr->RastPort;
      SetRast( ts->rp, PEN( 0 ));
      SetAPen( ts->rp, 1 );
      SetBPen( ts->rp, 0 );
      SetDrMd( ts->rp, JAM2 );
      SetFont( ts->rp, ts->font );

      /* Always use backdrop window on custom screen */
      backdrop = TRUE;
   }

   /* We are using a public screen */
   else
   {
      /* Size of public screen */
      pw = pubscr->Width;
      ph = pubscr->Height;

      /* Height difference between a window with or without a title bar */
      th = pubscr->Font->ta_YSize + 1;

      /* Find width of widest window */
      maxw = ts->ww;
      maxw = tc->ww > maxw ? tc->ww : maxw;
      maxw = tr->ww > maxw ? tr->ww : maxw;
      maxw = tm->ww > maxw ? tm->ww : maxw;
      maxw += pubscr->WBorLeft + pubscr->WBorRight;

      /* Find height of tallest window */
      maxh = ts->wh + ts->notitle ? 0 : th;
      maxh = ( tc->wh + tc->notitle ? 0 : th ) > maxh ? ( tc->wh + tc->notitle ? 0 : th ) : maxh;
      maxh = ( tr->wh + tr->notitle ? 0 : th ) > maxh ? ( tr->wh + tr->notitle ? 0 : th ) : maxh;
      maxh = ( tm->wh + tm->notitle ? 0 : th ) > maxh ? ( tm->wh + tm->notitle ? 0 : th ) : maxh;
      maxh += pubscr->WBorTop + pubscr->WBorBottom;

      /* Check if the public screen is large enough */
      if ( pw < maxw || ph < maxh )
      {
         sprintf( tmpstr, "Public screen is too small for window (%d x %d).", maxw, maxh );
         FAIL( tmpstr );
      }

      /* Use backdrop window if pubscreen is quiet */
      backdrop = ( pubscr->Flags & SCREENQUIET ) ? TRUE : FALSE;

      /* Calculate screen bar height */
      barh = pubscr->BarHeight + 1;

      /* Check if windows are to be positioned at the right or bottom */
      if ( ts->wx == -1 ) ts->wx = pw - 1;
      if ( ts->wy == -1 ) ts->wy = ph - 1;
      if ( tc->wx == -1 ) tc->wx = pw - 1;
      if ( tc->wy == -1 ) tc->wy = ph - 1;
      if ( tr->wx == -1 ) tr->wx = pw - 1;
      if ( tr->wy == -1 ) tr->wy = ph - 1;
      if ( tm->wx == -1 ) tm->wx = pw - 1;
      if ( tm->wy == -1 ) tm->wy = ph - 1;

      /* Position windows below screen bar if requested */
      if ( ts->wy == -2 ) ts->wy = barh;
      if ( tc->wy == -2 ) tc->wy = barh;
      if ( tr->wy == -2 ) tr->wy = barh;
      if ( tm->wy == -2 ) tm->wy = barh;
   }

   /* Get visual info for GadTools */
   if ( use_menus )
   {
      if (( visinfo = GetVisualInfo( use_pub ? pubscr : amiscr, TAG_END )) == NULL )
      {
         use_menus = FALSE;
      }
   }

   /* Open window, backdrop if on custom screen */
   if (( ts->win = OpenWindowTags( NULL,
          WA_Left, ts->wx,
          WA_Top, ts->wy,
          WA_InnerWidth, ts->ww,
          WA_InnerHeight, ts->wh,
          use_pub ? WA_PubScreen : WA_CustomScreen, use_pub ? pubscr : amiscr,
          WA_Backdrop, backdrop,
          WA_Borderless, backdrop,
          WA_GimmeZeroZero, !backdrop,
          WA_DragBar, !backdrop && !ts->notitle,
          WA_DepthGadget, !backdrop && !ts->notitle,
          WA_NewLookMenus, TRUE,
          backdrop ? TAG_IGNORE : WA_ScreenTitle, VERSION,
          ( backdrop || ts->notitle ) ? TAG_IGNORE : WA_Title, ts->name,
          WA_Activate, TRUE,
          WA_RMBTrap, !use_menus,
          WA_ReportMouse, TRUE,
          WA_IDCMP, IDCMP_RAWKEY | IDCMP_INTUITICKS | IDCMP_MOUSEMOVE | IDCMP_MOUSEBUTTONS | IDCMP_MENUPICK | IDCMP_MENUVERIFY,
          TAG_END )) == NULL )
   {
      FAIL("Unable to open window.");
   }

   /* Unlock public screen */
   if ( publock )
   {
      UnlockPubScreen( NULL, pubscr );
      publock = FALSE;
   }

   /* Initialize main rastport */
   ts->wrp = ts->win->RPort;
   SetRast( ts->wrp, PEN( 0 ));
   SetAPen( ts->wrp, 1 );
   SetBPen( ts->wrp, 0 );
   SetDrMd( ts->wrp, JAM2 );
   SetFont( ts->wrp, ts->font );

   /* Never use screen's rastport on public screen */
   if ( use_pub )
   {
      ts->rp = ts->wrp;
   }

   /* Use mirror window? */
   if ( tm->use )
   {
      /* Open mirror window */
      if (( tm->win = OpenWindowTags( NULL,
              WA_Left, tm->wx,
              WA_Top, tm->wy,
              WA_InnerWidth, tm->ww,
              WA_InnerHeight, tm->wh,
              WA_PubScreen, pubscr,
              WA_GimmeZeroZero, TRUE,
              WA_DragBar, !tm->notitle,
              WA_DepthGadget, !tm->notitle,
              WA_NewLookMenus, TRUE,
              WA_ScreenTitle, VERSION,
              tm->notitle ? TAG_IGNORE : WA_Title, tm->name,
              WA_ReportMouse, TRUE,
              TAG_END )) == NULL )
      {
         FAIL("Unable to open recall window.");
      }

      /* Initialize mirror rastport */
      tm->rp = tm->wrp = tm->win->RPort;
      SetRast( tm->rp, PEN( 0 ));
      SetAPen( tm->rp, 1 );
      SetBPen( tm->rp, 0 );
      SetDrMd( tm->rp, JAM2 );
      SetFont( tm->rp, tm->font );
   }

   /* Use recall window? */
   if ( tr->use )
   {
      /* Open recall window */
      if (( tr->win = OpenWindowTags( NULL,
              WA_Left, tr->wx,
              WA_Top, tr->wy,
              WA_InnerWidth, tr->ww,
              WA_InnerHeight, tr->wh,
              WA_PubScreen, pubscr,
              WA_GimmeZeroZero, TRUE,
              WA_DragBar, !tr->notitle,
              WA_DepthGadget, !tr->notitle,
              WA_NewLookMenus, TRUE,
              WA_ScreenTitle, VERSION,
              tr->notitle ? TAG_IGNORE : WA_Title, tr->name,
              WA_ReportMouse, TRUE,
              TAG_END )) == NULL )
      {
         FAIL("Unable to open recall window.");
      }

      /* Initialize recall rastport */
      tr->rp = tr->wrp = tr->win->RPort;
      SetRast( tr->rp, PEN( 0 ));
      SetAPen( tr->rp, 1 );
      SetBPen( tr->rp, 0 );
      SetDrMd( tr->rp, JAM2 );
      SetFont( tr->rp, tr->font );
   }

   /* Use choice window? */
   if ( tc->use )
   {
      /* Open choice window */
      if (( tc->win = OpenWindowTags( NULL,
              WA_Left, tc->wx,
              WA_Top, tc->wy,
              WA_InnerWidth, tc->ww,
              WA_InnerHeight, tc->wh,
              WA_PubScreen, pubscr,
              WA_GimmeZeroZero, TRUE,
              WA_DragBar, !tc->notitle,
              WA_DepthGadget, !tc->notitle,
              WA_NewLookMenus, TRUE,
              WA_ScreenTitle, VERSION,
              tc->notitle ? TAG_IGNORE : WA_Title, tc->name,
              WA_ReportMouse, TRUE,
              TAG_END )) == NULL )
      {
         FAIL("Unable to open recall window.");
      }

      /* Initialize choice rastport */
      tc->rp = tc->wrp = tc->win->RPort;
      SetRast( tc->rp, PEN( 0 ));
      SetAPen( tc->rp, 1 );
      SetBPen( tc->rp, 0 );
      SetDrMd( tc->rp, JAM2 );
      SetFont( tc->rp, tc->font );
   }

   /* Create palette for screen */
   load_palette();

   /* Link terms with the metaterm */
   if ( tc->use ) term_choice = link_term( tc );
   if ( tr->use ) term_recall = link_term( tr );
   if ( tm->use ) term_mirror = link_term( tm );
   if ( ts->use ) term_screen = link_term( ts );

   /* Bring main window to front */
   if ( !backdrop ) WindowToFront( ts->win );

   /* Bring screen to front */
   ScreenToFront( use_pub ? pubscr : amiscr );

   /* Load and convert graphics */
   if ( use_graphics )
   {
      MSG( 0, 0, "Loading graphics" );
      if ( !load_gfx() ) FAIL( NULL );

      /* Check if conversion is necessary */
      if ( use_pub || ( depth_of_bitmap( ts->rp->BitMap ) != depth_of_bitmap( ts->gfxbm )))
      {
         MSG( 0, 1, "Remapping graphics" );
         if ( !conv_gfx() ) FAIL( "Out of memory while remapping graphics." );
      }

      /* Scale the graphics to fit font sizes */
      if ( tm->use ) if ( !size_gfx( tm ) ) FAIL( "Out of memory while scaling graphics." );
      if ( tr->use ) if ( !size_gfx( tr ) ) FAIL( "Out of memory while scaling graphics." );
      if ( tc->use ) if ( !size_gfx( tc ) ) FAIL( "Out of memory while scaling graphics." );
      if ( ts->use ) if ( !size_gfx( ts ) ) FAIL( "Out of memory while scaling graphics." );

      /* Use graphics */
      use_graphics = TRUE;
   }

   /* Load sound effects */
   if ( use_sound )
   {
      MSG( 0, 2, "Loading sound effects" );
      init_sound();
   }

   /* Success */
   return ( 0 );
}
///}
///{ "init_term()"

static void init_term( term_data *td )
{
   /* Set term name */
   if ( td == &screen ) td->name = screen_name;
   if ( td == &choice ) td->name = choice_name;
   if ( td == &recall ) td->name = recall_name;
   if ( td == &mirror ) td->name = mirror_name;

   /* Term off */
   td->use = FALSE;

   /* Term size */
   td->cols = 80;
   td->rows = 24;

   /* Term dimension */
   td->wx = 0;
   td->wy = 0;
   td->ww = 0;
   td->wh = 0;

   /* System default font */
   td->font = GfxBase->DefaultFont;
   td->ownf = FALSE;
   td->fw   = td->font->tf_XSize;
   td->fh   = td->font->tf_YSize;
   td->fb   = td->font->tf_Baseline;

   /* No window or rastports */
   td->win  = NULL;
   td->wrp  = NULL;
   td->rp   = NULL;

   /* No bitmaps */
   td->gfxbm = NULL;
   td->mskbm = NULL;
   td->mapbm = NULL;

   /* Cursor status */
   td->cursor_xpos = 0;
   td->cursor_ypos = 0;
   td->cursor_visible = FALSE;
   td->cursor_lit = FALSE;
   td->cursor_frame = 0;
   td->cursor_map = FALSE;

   /* Use window title */
   td->notitle = FALSE;
}

///}
///{ "link_term()"

static term *link_term( term_data *td )
{
   term *t;

   /* Term pointer */
   t = &td->t;

   /* Initialize the term */
   term_init( t, td->cols, td->rows, 256 );

   /* Hooks */
   t->init_hook = amiga_open;
   t->nuke_hook = amiga_nuke;
   t->text_hook = amiga_text;
   t->pict_hook = amiga_pict;
   t->wipe_hook = amiga_wipe;
   t->curs_hook = amiga_curs;
   t->xtra_hook = amiga_xtra;

   /* We are emulating a hardware cursor */
   t->soft_cursor = FALSE;

   /* Draw graphical tiles one by one */
   t->always_text = FALSE;
   t->always_pict = FALSE;
   t->higher_pict = TRUE;

   /* Misc. efficiency flags */
   t->never_bored = TRUE;
   t->never_frosh = TRUE;

   /* Erase with "white space" */
   t->attr_blank = TERM_WHITE;
   t->char_blank = ' ';

   /* Remember where we come from */
   t->data = (vptr) td;

   /* Activate it */
   Term_activate( t );

   /* Return pointer to the term */
   return( t );
}

///}
///{ "free_term()"

static void free_term( term_data *td )
{
   /* Do we own the font? */
   if ( td->ownf && td->font ) CloseFont( td->font );

   /* Is the window opened? */
   if ( td->win ) CloseWindow( td->win );

   /* Free bitmaps */
   if ( td->gfxbm ) free_bitmap( td->gfxbm );
   if ( td->mskbm ) free_bitmap( td->mskbm );
   if ( td->mapbm ) free_bitmap( td->mapbm );
}

///}
///{ "stripstr()"

static char *stripstr( char *src, char *dst )
{
   int len;

   /* Ignore leading spaces */
   while ( *src == ' ' ) src++;

   /* Copy string */
   for ( len = 0; *src != 0; len++ ) *dst++ = *src++; *dst = 0;

   /* Remove trailing spaces */
   for ( dst--; *dst == ' ' && len; len-- ) *dst-- = 0;

   /* Return pointer to destination */
   return( dst );
}

///}
///{ "yesno()"

static int yesno( char *str )
{
   char tmp[ 256 ];
   char *s;

   /* Strip spaces around string */
   stripstr( str, tmp);

   /* Make lowercase */
   for ( s = tmp; *s != 0; s++ ) *s = tolower( *s );

   return ( !strcmp( tmp, "y" ) || !strcmp( tmp, "yes" ) || !strcmp( tmp, "true" ) || !strcmp( tmp, "on" ));
}

///}
///{ "request_font()"

void request_font( char *str )
{
   struct Library *AslBase = NULL;
   struct FontRequester *req = NULL;

   /* Blank string as default */
   *str = 0;

   /* Open asl.library */
   if ( AslBase = OpenLibrary ( "asl.library", 37 ))
   {
      /* Allocate screenmode requester */
      if ( req = AllocAslRequestTags( ASL_FontRequest, TAG_DONE ))
      {
         /* Open screenmode requester */
         if( AslRequestTags( req, ASLFO_FixedWidthOnly, TRUE, TAG_DONE ))
         {
            /* Store font name and size */
            sprintf( str, "%s/%d", req->fo_Attr.ta_Name, req->fo_Attr.ta_YSize );
         }
         /* Free requester */
         FreeAslRequest( req );
      }
      /* Close asl.library */
      CloseLibrary( AslBase );
   }
}

///}
///{ "request_mode()"

void request_mode( char *str )
{
   struct Library *AslBase = NULL;
   struct ScreenModeRequester *req = NULL;

   /* Blank string as default */
   *str = 0;

   /* Open asl.library */
   if ( AslBase = OpenLibrary ( "asl.library", 37 ))
   {
      /* Allocate screenmode requester */
      if ( req = AllocAslRequestTags( ASL_ScreenModeRequest, TAG_DONE ))
      {
         /* Open screenmode requester */
         if( AslRequestTags( req, TAG_DONE ))
         {
            /* Store font name and size */
            sprintf( str, "0x%X", req->sm_DisplayID );
         }
         /* Free requester */
         FreeAslRequest( req );
      }
      /* Close asl.library */
      CloseLibrary( AslBase );
   }
}

///}
///{ "read_prefs()"

int read_prefs( void )
{
   BPTR file;
   char line[ 256 ];
   char fname[ 256 ];
   char *tmp, *s;
   int len;
   int fsize;
   int val;
   struct TextAttr attr;
   term_data *td;

   /* Open config file */
   if (( file = Open( WPRF, MODE_OLDFILE )) == NULL )
   {
      fprintf( stderr, "\nUnable to open file '%s'.\n", WPRF );
      return( FALSE );
   }

   /* Read next line from file */
   while ( FGets( file, line, 256 ))
   {
      /* Cut off comments */
      if ( tmp = strchr( line, ';' )) *tmp = 0;

      /* Length of line */
      len = strlen( line );

      /* Cut off trailing newline and blanks */
      for ( tmp = line + len - 1; len > 0 && ( *tmp == '\n' || *tmp == ' ' ); len-- ) *tmp-- = 0;

      /* Ignore blank lines */
      if ( len == 0 ) continue;

      /* Extract term */
      if ( strncmp( line, "MAIN.", 5 ) == 0 ) td = &screen;
      else if ( strncmp( line, "CHOICE.", 7 ) == 0 ) td = &choice;
      else if ( strncmp( line, "RECALL.", 7 ) == 0 ) td = &recall;
      else if ( strncmp( line, "MIRROR.", 7 ) == 0 ) td = &mirror;
      else if ( strncmp( line, "SCREEN.", 7 ) != 0 &&
                strncmp( line, "ANGBAND.", 8 ) != 0 )
      {
         printf( "PREFS: Error in line '%s'\n", line );
         continue;
      }

      /* Find start of option */
      tmp = strchr( line, '.' ) + 1;

      /* Ignore blank options */
      if ( *tmp == 0 ) continue;

      /* Option 'use' - Use this term */
      if ( !strncmp( tmp, "use", 3 ))
      {
         td->use = yesno( tmp + 3 );
      }

      /* Option 'cols' - Set number of columns for term */
      else if ( !strncmp( tmp, "cols", 4 ) && sscanf( tmp + 4, "%d", &val )) td->cols = val;

      /* Option 'rows' - Set number of rows for term */
      else if ( !strncmp( tmp, "rows", 4 ) && sscanf( tmp + 4, "%d", &val )) td->rows = val;

      /* Option 'xpos' - Set horizontal position for window */
      else if ( !strncmp( tmp, "xpos", 4 ) && sscanf( tmp + 4, "%d", &val )) td->wx = val;

      /* Option 'ypos' - Set vertical position for window */
      else if ( !strncmp( tmp, "ypos", 4 ) && sscanf( tmp + 4, "%d", &val )) td->wy = val;

      /* Option 'name' - Set window title */
      else if ( !strncmp( tmp, "title", 5 ))
      {
         /* Get parameter */
         stripstr( tmp + 5, fname );

         /* Make a copy of the title */
         if ( strlen( fname ) > 0 )
         {
            td->name = strdup( fname );
         }

         /* Don't use a title bar on this window */
         else
         {
            td->notitle = TRUE;
         }

         continue;
      }

      /* Option 'font' - Set font for this window */
      else if ( !strncmp( tmp, "font", 4 ))
      {
         /* Get value */
         stripstr( tmp + 4, fname );

         /* Ask for font? */
         if ( !strcmp( fname, "?" ))
         {
            /* Open font requester */
            request_font( fname );
         }

         /* No font specification given */
         if ( strlen( fname ) == 0 )
         {
            /* Main window */
            if ( td == &screen )
            {
               /* System default font */
               td->font = GfxBase->DefaultFont;

               /* Use default font as screen font */
               scrattr = NULL;
            }

            /* Extra window */
            else
            {
               /* Copy main window's font */
               td->font = screen.font;
            }

            /* Set font dimensions */
            td->fw   = td->font->tf_XSize;
            td->fh   = td->font->tf_YSize;
            td->fb   = td->font->tf_Baseline;

            /* This font is not opened by us */
            td->ownf = FALSE;

            /* Next line */
            continue;
         }

         /* Get name and size */
         else
         {
            /* Find font name/size delimiter */
            if (( s = strchr( fname, '/' )) == NULL )
            {
               printf( "\nPREFS: Illegal font specification: '%s'.\n", fname );
               continue;
            }

            /* End fontname here */
            *s++ = 0;

            /* Convert size string to integer */
            fsize = atoi( s );

            /* Make sure the font name ends with .font */
            if ( !strstr( fname, ".font" )) strcat( fname, ".font" );
         }

         /* Set font attributes */
         attr.ta_Name  = fname;
         attr.ta_YSize = fsize;
         attr.ta_Style = FS_NORMAL;
         attr.ta_Flags = ( !strcmp( fname, "topaz.font" ) && ( fsize == 8 || fsize == 9 )) ?
                         FPF_ROMFONT : FPF_DISKFONT;

         /* Open font from disk */
         if ( td->font = OpenDiskFont( &attr ))
         {
            /* We own this font */
            td->ownf = TRUE;

            /* Set font dimensions */
            td->fw = td->font->tf_XSize;
            td->fh = td->font->tf_YSize;
            td->fb = td->font->tf_Baseline;

            /* Copy font attr to screen font */
            if ( td == &screen )
            {
               scrattr->ta_Name = strdup( fname );
               scrattr->ta_YSize = fsize;
               scrattr->ta_Style = FS_NORMAL;
               scrattr->ta_Flags = attr.ta_Flags;
            }
         }

         /* The font could not be opened */
         else
         {
            /* Fallback to default font */
            td->font = GfxBase->DefaultFont;

            /* Use default font as screen font */
            scrattr = NULL;

            /* Output error message */
            printf( "\nUnable to open font '%s/%d'.\n", fname, fsize );
         }
      }

      /* Option .name - Set public screen name. KS 3.0+ required */
      else if ( !strncmp( tmp, "name", 4 ) && v39 )
      {
         /* Copy name */
         stripstr( tmp + 4, modestr );
      }

      /* Option .mode - Set custom screen mode */
      else if ( !strncmp( tmp, "mode", 4 ))
      {
         /* If a public screen was also specified, check if it exist */
         if ( strlen( modestr ) > 0 )
         {
            /* Don't lock it twice */
            if ( pubscr ) continue;

            /* Try to lock the named public screen */
            if ( pubscr = LockPubScreen( modestr ))
            {
               /* We got a lock now */
               publock = TRUE;

               /* Screen exist. Skip this option */
               continue;
            }
         }

         /* Get parameter */
         stripstr( tmp + 4, modestr );

         /* Ask for mode? */
         if ( !strcmp( modestr, "?" ))
         {
            /* Open screenmode requester */
            request_mode( modestr );
         }
      }

      /* Option .blankmouse - Use mouseblanking */
      else if ( !strncmp( tmp, "blankmouse", 10 ))
      {
         blankmouse = yesno( tmp + 10 );
      }

      /* Option .blankmouse - Use intuition menus */
      else if ( !strncmp( tmp, "menus", 5 ))
      {
         use_menus = yesno( tmp + 5 );
      }

      else if ( !strncmp( tmp, "sound", 5 ))
      {
         use_sound = yesno( tmp + 5 );
      }

      else if ( !strncmp( tmp, "gfx", 3 ))
      {
         use_graphics = yesno( tmp + 5 );
      }

      /* Unknown option */
      else
      {
         /* Output error message */
         printf ( "\nPREFS: Unknown option '%s'.\n", tmp );
      }
   }

   /* Close the file */
   Close( file );
}

///}
///{ "amiga_nuke()" - Free all allocated resources

static void amiga_nuke( term *t )
{
   if ( t == term_screen )
   {
      amiga_fail( NULL );
      /* Flush the output */
      fflush( stdout );
   }
}

///}
///{ "amiga_open()" - Initialize terminal

static void amiga_open( term *t )
{
   /* Nothing to do here */
}

///}
///{ "amiga_curs()" - Move the cursor to a new location

static errr amiga_curs( int x, int y )
{
   term_data *td = term_curs;

   if ( td->cursor_visible )
   {
      cursor_off( td );
   }

   td->cursor_xpos = x;
   td->cursor_ypos = y;
   td->cursor_frame = 0;

   if ( td->cursor_visible )
   {
      cursor_on( td );
   }

   return ( 0 );
}

///}
///{ "amiga_wipe()" - Erase a part of a line

static errr amiga_wipe( int x, int y, int n )
{
   term_data *td = (term_data*)(Term->data);

   if (( n > 0 ) && !iconified )
   {
      /* Erase rectangular area on screen */
      SetAPen( td->rp, PEN( 0 ) );
      RectFill( td->rp, x * td->fw, y * td->fh, ( x + n ) * td->fw - 1, ( y + 1 ) * td->fh - 1 );
   }

   return ( 0 );
}

///}
///{ "amiga_clear()" - Clear whole window

static errr amiga_clear( void )
{
   term_data *td = (term_data*)(Term->data);

   /* Fill window with background color */
   SetRast( td->rp, PEN( 0 ));

   return ( 0 );
}

///}
///{ "amiga_pict()" - Place one tile on the screen

static errr amiga_pict( int x, int y, byte a, char c )
{
   term_data *td = (term_data*)(Term->data);
   char s[2];

   /* Graphical tile */
   if ( a & 0x80 )
   {
      put_gfx( td->rp, x, y, c & 0x7f, a & 0x7f );
   }

   /* Textual character */
   else
   {
      s[0] = c;
      s[1] = 0;
      SetAPen( td->rp, PEN( a & 0x0f ));
      SetBPen( td->rp, PEN( 0 ));
      Move( td->rp, x * td->fw, y * td->fh + td->fb );
      Text( td->rp, (char *) s, 1 );
   }

   return ( 0 );
}

///}
///{ "amiga_text()" - Place some text on the screen using an attribute

static errr amiga_text( int x, int y, int n, byte a, cptr s )
{
   term_data *td = (term_data*)(Term->data);
   int i;

   if ( x >= 0 && y >= 0 && n > 0 && !iconified )
   {
      /* Draw gfx one char at a time */
      if (( a & 0xc0 ))
      {
         for ( i = 0; i < n; i++ ) put_gfx( td->rp, x + i, y, s[ i ] & 0x7f, a & 0x7f );
      }

      /* Draw the string on screen */
      else
      {
         SetAPen( td->rp, PEN( a&0x0f ));
         SetBPen( td->rp, PEN( 0 ));
         Move( td->rp, x * td->fw, y * td->fh + td->fb );
         Text( td->rp, (char *) s, n );
      }
   }

   return ( 0 );
}

///}
///{ "amiga_xtra()" - Handle a "special request"

static errr amiga_xtra( int n, int v )
{
   term_data *td = (term_data*)(Term->data);

   /* Analyze the request */
   switch ( n )
   {
      /* Wait for event */
      case TERM_XTRA_EVENT:

         return ( amiga_event( v ));


      /* Flush input */
      case TERM_XTRA_FLUSH:

         return ( amiga_flush( v ));


      /* Make a noise */
      case TERM_XTRA_CLEAR:

         return ( amiga_clear());


      /* Change cursor visibility */
      case TERM_XTRA_SHAPE:

         /* Cursor on */
         if ( v )
         {
            cursor_on( td );
            td->cursor_visible = TRUE;
         }
         /* Cursor off */
         else
         {
            cursor_off( td );
            td->cursor_visible = FALSE;
         }
         return ( 0 );


      /* Flash screen */
      case TERM_XTRA_NOISE:

         DisplayBeep( use_pub ? pubscr : amiscr );
         return ( 0 );


      /* Play a sound */
      case TERM_XTRA_SOUND:

         if ( has_sound )
         {
            play_sound( v );
         }
         return ( 0 );


      /* React on global changes */
      case TERM_XTRA_REACT:

         return ( amiga_react( v ));


      case TERM_XTRA_LEVEL:

         term_curs = td;
         return( 0 );

      case TERM_XTRA_DELAY:

         if (v >= 20) Delay(v / 20);
         return (0);

      /* Unknown request type */
      default:

         return ( 1 );

   }

   /* Shouldn't be able to get here */
   return ( 1 );
}

///}
///{ "amiga_flush()" - Flush input buffer

static errr amiga_flush( int v )
{
   struct IntuiMessage *imsg;

   /* Ignore all messages at the port */
   while ( imsg = (struct IntuiMessage *) GetMsg( screen.win->UserPort ))
   {
      ReplyMsg(( struct Message *) imsg );
   }

   return ( 1 );
}

///}
///{ "amiga_event()" - Wait for an event, and handle it.

static errr amiga_event( int v )
{
   struct IntuiMessage *imsg;
   ULONG iclass;
   UWORD icode;
   UWORD iqual;
   APTR iaddr;

   /* Check for messages to the window */
   if (( imsg = (struct IntuiMessage *) GetMsg( screen.win->UserPort )) == NULL )
   {
      /* If we don't want blocking, return */
      if ( !v )
      {
         return ( 0 );
      }

      /* No messages, so wait for one */
      Wait( 1 << screen.win->UserPort->mp_SigBit );

      /* Get the new message */
      imsg = (struct IntuiMessage *) GetMsg( screen.win->UserPort );
   }

   /* Handle message */
   if ( imsg )
   {
      /* Get message attributes */
      iclass = imsg->Class;
      icode = imsg->Code;
      iqual = imsg->Qualifier;
      iaddr = imsg->IAddress;

      /* Update menus before displaying */
      if ( iclass == IDCMP_MENUVERIFY && icode == MENUHOT && use_menus )
      {
         update_menus();
      }

      /* Reply the message */
      ReplyMsg(( struct Message *) imsg );

      /* Do we have a keypress? */
      if ( iclass == IDCMP_RAWKEY )
      {
         handle_rawkey( icode, iqual, iaddr );
         return ( 0 );
      }

      /* Mouse event - Make pointer visible */
      if ( iclass == IDCMP_MOUSEMOVE ||
           iclass == IDCMP_MOUSEBUTTONS ||
           iclass == IDCMP_MENUVERIFY )
      {
         if ( blankmouse && !pointer_visible )
         {
            ClearPointer( screen.win );
            pointer_visible = TRUE;
         }

         return ( 0 );
      }

      /* Time for some cursor anim? */
      if ( iclass == IDCMP_INTUITICKS )
      {
         cursor_anim();
         return ( 0 );
      }

      /* Menu item picked? */
      if ( iclass == IDCMP_MENUPICK )
      {
         handle_menupick( icode );
      }

      /* Unknown message class */
      return ( 1 );
   }

   /* No events */
   return ( 1 );
}

///}
///{ "amiga_react()"

static errr amiga_react( int v )
{
   /* Apply color palette, in case it has changed */
   load_palette();

   /* Create menus if we don't have any */
   if ( !menu && use_menus )
   {
      create_menus();
   }

   return ( 0 );
}

///}
///{ "amiga_tomb()"

int amiga_tomb( void )
{
   cptr p;
   char tmp[160];
   time_t ct = time((time_t)0);
   BPTR file;

   char *pp;
   int plane, row, error = FALSE;
   struct BitMap *filebm, *scalbm, *convbm;
   long stdpens[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
   int depth;
   BYTE *src, *dst;
   int byt;

   int tw = screen.fw * 64;
   int th = screen.fh * 21;

   /* Allocate bitmap for tomb graphics file */
   if (( filebm = alloc_bitmap( 512, 168, 4, BMF_CLEAR, screen.rp->BitMap )) == NULL )
   {
      return( FALSE );
   }

   /* Open tomb file */
   if (( file = Open( MTOM, MODE_OLDFILE )) == NULL )
   {
      free_bitmap( filebm );
      return( FALSE );
   }

   /* Read file into bitmap */
   for ( plane = 0; plane < 4 && !error; plane++ )
   {
      pp = filebm->Planes[ plane ];
      for ( row = 0; row < 168 && !error; row++ )
      {
         error = ( Read( file, pp, 64 ) != 64 );
         pp += filebm->BytesPerRow;
      }
   }

   /* Close tomb file */
   Close( file );

   /* Get depth of display */
   depth = depth_of_bitmap( screen.rp->BitMap );

   /* Remapping needed? */
   if ( depth != 4 || use_pub )
   {
      /* Allocate bitmap for remapped image */
      if (( convbm = alloc_bitmap( 512, 168, depth, BMF_CLEAR, screen.rp->BitMap )) == NULL )
      {
         free_bitmap( filebm );
         return( FALSE );
      }

      /* Simple remapping from 4 to 5 planes? */
      if ( depth == 5 && !use_pub )
      {
         for ( plane = 0; plane < 4; plane++ )
         {
            src = filebm->Planes[ plane ];
            dst = convbm->Planes[ plane ];
            for ( row = 0; row < 168; row++ )
            {
               for ( byt = 0; byt < 64; byt++ )
               {
                  dst[ byt ] = src[ byt ];
               }
               src += filebm->BytesPerRow;
               dst += convbm->BytesPerRow;
            }
         }
      }

      /* Complex remapping */
      else
      {
         /* Remap old bitmap into new bitmap */
         remap_bitmap( filebm, convbm, use_pub ? pubpens : stdpens, 512, 168 );
      }

      /* Free original bitmap */
      free_bitmap( filebm );
   }

   /* No remapping needed */
   else
   {
      convbm = filebm;
   }

   /* Allocate bitmap for scaled graphics */
   if (( scalbm = alloc_bitmap( tw, th, depth, BMF_CLEAR, screen.rp->BitMap )) == NULL )
   {
      free_bitmap( convbm );
      return ( FALSE );
   }

   /* Scale the tomb bitmap */
   scale_bitmap( convbm, 512, 168, scalbm, tw, th );

   /* Free old bitmap */
   free_bitmap( convbm );

   /* Copy the tomb graphics to the screen, centered */
   BltBitMapRastPort( scalbm, 0, 0, screen.rp, ( screen.ww - tw ) / 2, 0, tw, th, 0xc0 );

   /* Free bitmap */
   free_bitmap( scalbm );

   /* King or Queen */
   if (p_ptr->total_winner || (p_ptr->lev > PY_MAX_LEVEL))
   {
      p = "Magnificent";
   }

   /* Normal */
   else
   {
      p =  player_title[p_ptr->pclass][(p_ptr->lev-1)/5];
   }

   tomb_str( 3, " R.I.P." );

   tomb_str( 5, p_ptr->full_name );

   tomb_str( 6, "the" );

   tomb_str( 7, (char *)p );

   tomb_str( 9, (char *)cp_ptr->title );

   sprintf( tmp, "Level: %d", (int)p_ptr->lev );
   tomb_str( 10, tmp );

   sprintf( tmp, "Exp: %ld", (long)p_ptr->exp );
   tomb_str( 11, tmp );

   sprintf( tmp, "AU: %ld", (long)p_ptr->au );
   tomb_str( 12, tmp );

   sprintf( tmp, "Killed on Level %d", p_ptr->depth );
   tomb_str( 13, tmp );

   sprintf( tmp, "by %s", p_ptr->died_from );
   tomb_str( 14, tmp );

   sprintf( tmp, "%-.24s", ctime(&ct));
   tomb_str( 16, tmp );

   return( TRUE );
}

///}
///{ "tomb_str()"

void tomb_str( int y, char *str )
{
   term_data *td = &screen;
   int l = strlen( str );
   int xp = ( 39 * td->fw ) - (( l + 1 ) * td->fw ) / 2;

   SetDrMd( td->rp, JAM1 );

   SetAPen( td->rp, PEN( 1 ));
   Move( td->rp, xp + 1, y * td->fh + td->fb + 1 );
   Text( td->rp, str, l );

   SetAPen( td->rp, PEN( 0 ));
   Move( td->rp, xp, y * td->fh + td->fb );
   Text( td->rp, str, l );

   SetDrMd( td->rp, JAM2 );
}

///}
///{ "handle_rawkey()"

void handle_rawkey( UWORD code, UWORD qual, APTR addr )
{
   char buf[ 80 ];
   int i;
   int len;
   UWORD q;

   /* Use a blank mouse-pointer on this window */
   if ( blankmouse && pointer_visible )
   {
      SetPointer( screen.win, blankpointer, 2, 16, 0, 0 );
      pointer_visible = FALSE;
   }

   /* Numeric keypad pressed with qualifier? */
   if (( qual & IEQUALIFIER_NUMERICPAD ) && ( qual & 0xff ))
   {
      /* Direction key? (1,2,3,4,6,7,8,9) */
      if (( code >= 0x1d && code <= 0x1f ) ||
          ( code == 0x2d || code == 0x2f ) ||
          ( code >= 0x3d && code <= 0x3f ))
      {
         /* Shift/Ctrl/Alt/Amiga keys */
         q = qual & 0xff;

         /* Shift + Direction */
         if ( q == IEQUALIFIER_LSHIFT || q == IEQUALIFIER_RSHIFT )
         {
            /* Fake a keypress 'run' */
            Term_keypress( '.' );

            /* Remove shift key from event */
            qual &= ~( IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT );
         }

         /* Alt + Direction */
         else if ( q == IEQUALIFIER_LALT || q == IEQUALIFIER_RALT )
         {
            /* Fake a keypress 'tunnel' */
            Term_keypress( 'T' );

            /* Remove alt key from event */
            qual &= ~( IEQUALIFIER_LALT | IEQUALIFIER_RALT );
         }

         /* Ctrl + Direction */
         else if ( q == IEQUALIFIER_CONTROL )
         {
            /* Fake a keypress 'open' */
            Term_keypress( 'o' );

            /* Remove ctrl key from event */
            qual &= ~IEQUALIFIER_CONTROL;
         }
      }
   }

   /* Convert raw keycode to ANSI sequence */
   ie.ie_Code = code;
   ie.ie_Qualifier = qual;
   ie.ie_EventAddress = (APTR *) *((ULONG *) addr );
   len = MapRawKey( &ie, buf, 80, NULL );

   /* Send ANSI sequence to meta-terminal */
   for ( i = 0; i < len; i++ )
   {
      if( !iconified )
      {
         Term_keypress( (unsigned char) buf[ i ]);
      }
   }
}

///}
///{ "handle_menupick()"

void handle_menupick( int mnum )
{
   struct MenuItem *item;
   ULONG ud;
   int i;

   /* Be sure to handle all selections */
   while ( mnum != MENUNULL )
   {
      /* Find address of menuitem */
      item = ItemAddress( menu, mnum );

      /* Find userdata of menuitem */
      ud = (ULONG) GTMENUITEM_USERDATA( item );

      /* Is this a help item? */
      if ( ud >= MNU_HELP )
      {
         /* Send keypresses */
         Term_keypress( '\\' );
         Term_keypress( '?' );
         Term_keypress( ud - MNU_HELP );
      }

      /* Is this an option item? */
      else if ( ud >= MNU_OPTION )
      {
         /* Option index */
         i = ud - MNU_OPTION;

         /* Set option according to checkmark status */
         *options[ i ].o_var = item->Flags & CHECKED ? TRUE : FALSE;

         /* Fake a dummy keypress to cause update */
         Term_keypress( ' ' );
      }

      /* Control key shortcuts */
      else if ( ud >= MNU_CKEYCOM )
      {
         /* Send keycode */
         Term_keypress( '\\' );
         Term_keypress( '^' );
         Term_keypress( ud - MNU_CKEYCOM );
      }

      /* Key shortcuts */
      else if ( ud >= MNU_KEYCOM )
      {
         /* Key code */
         i = ud - MNU_KEYCOM;

         /* Some functions need underlying keymap */
         if ( i != 't' && i != 'w' && i != 'T' )
         {
            Term_keypress( '\\' );
         }

         /* Send keycode */
         Term_keypress( i );
      }

      /* Scaled down Map of the dungeon */
      else if ( ud == MNU_SCALEDMAP )
      {
         /* Draw the map */
         amiga_map();
      }

      /* Find next menunumber */
      mnum = item->NextSelect;
   }
}

///}
///{ "cursor_on()"

static void cursor_on( term_data *td )
{
   int x0, y0, x1, y1;

   if ( !td->cursor_lit && !iconified )
   {
      td->cursor_frame = 0;

      /* Hack - Don't draw cursor at (0,0) */
      if ( td->cursor_xpos == 0 && td->cursor_ypos == 0 ) return;

      /* Draw an outlined cursor */
      if( CUR_A & 0xf0 && use_graphics )
      {
         x0 = td->cursor_xpos * td->fw;
         y0 = td->cursor_ypos * td->fh;
         x1 = x0 + td->fw - 1;
         y1 = y0 + td->fh - 1;
         SetAPen( td->wrp, PEN( CURSOR_PEN ));
         Move( td->wrp, x0, y0 );
         Draw( td->wrp, x1, y0 );
         Draw( td->wrp, x1, y1 );
         Draw( td->wrp, x0, y1 );
         Draw( td->wrp, x0, y0 );
      }

      /* Draw a filled cursor */
      else
      {
         SetAPen( td->wrp, PEN( CUR_A & 0x0f ));
         SetBPen( td->wrp, PEN( CURSOR_PEN ));
         Move( td->wrp, td->fw * td->cursor_xpos, td->fh * td->cursor_ypos + td->fb );
         Text( td->wrp, &CUR_C, 1 );
      }

      td->cursor_lit = TRUE;
   }
}

///}
///{ "cursor_off()"

static void cursor_off( term_data *td )
{
   if ( td->cursor_lit && !iconified )
   {
      /* Restore graphics under cursor */
      if ( CUR_A & 0xf0 && use_graphics )
      {
         put_gfx( td->wrp, td->cursor_xpos, td->cursor_ypos, CUR_C, CUR_A );
      }

      /* Restore char/attr under cursor */
      else
      {
         SetAPen( td->wrp, PEN( CUR_A & 0x0f ));
         SetBPen( td->wrp, PEN( 0 ));
         Move( td->wrp, td->fw * td->cursor_xpos, td->fh * td->cursor_ypos + td->fb );
         Text( td->wrp, &CUR_C, 1 );
      }
      td->cursor_lit = FALSE;
   }
}

///}
///{ "cursor_anim()"

static void cursor_anim( void )
{
   term_data *td = term_curs;
   int x0, y0, x1, y1;

   /* XXX XXX XXX */
   int i = p_ptr->px;
   int j = p_ptr->py;

   byte tc,ta;

   if ( !term_curs ) return;

   td->cursor_frame = ++(td->cursor_frame) % 8;

   /* Small cursor on map */
   if ( td->cursor_map )
   {
      if ( td->cursor_frame & 2 )
      {
         SetAPen( td->wrp, PEN( CURSOR_PEN ));
         RectFill( td->wrp,
                   td->map_x + i * td->mpt_w,
                   td->map_y + j * td->mpt_h,
                   td->map_x + ( i + 1) * td->mpt_w - 1,
                   td->map_y + ( j + 1 ) * td->mpt_h - 1
                 );
      }
      else
      {
         ta = (( p_ptr->pclass * 10 + p_ptr->prace) >> 5 ) + 12;
         tc = (( p_ptr->pclass * 10 + p_ptr->prace) & 0x1f );
         put_gfx_map( td, i, j, tc, ta );
      }
   }

   else if ( td->cursor_visible && !iconified )
   {
      /* Hack - Don't draw cursor at (0,0) */
      if ( td->cursor_xpos == 0 && td->cursor_ypos == 0 ) return;

      /* Draw an outlined cursor */
      if ( CUR_A & 0x80 && use_graphics )
      {
         /* First draw the tile under cursor */
         put_gfx( td->wrp, td->cursor_xpos, td->cursor_ypos, CUR_C, CUR_A );

         if ( td->cursor_frame < 4 )
         {
            x0 = td->cursor_xpos * td->fw;
            y0 = td->cursor_ypos * td->fh;
            x1 = x0 + td->fw - 1;
            y1 = y0 + td->fh - 1;
            SetAPen( td->wrp, PEN( CURSOR_PEN ));
            Move( td->wrp, x0, y0 );
            Draw( td->wrp, x1, y0 );
            Draw( td->wrp, x1, y1 );
            Draw( td->wrp, x0, y1 );
            Draw( td->wrp, x0, y0 );
         }
      }

      /* Draw a filled cursor */
      else
      {
         SetAPen( td->wrp, PEN( CUR_A & 0x0f ));
         SetBPen( td->wrp, ( td->cursor_frame < 4 ) ? PEN( CURSOR_PEN ) : PEN( 0 ));
         Move( td->wrp, td->fw * td->cursor_xpos, td->fh * td->cursor_ypos + td->fb );
         Text( td->wrp, &CUR_C, 1 );
      }
   }
}

///}
///{ "load_gfx()"

int load_gfx( void )
{
   term_data *ts = &screen;
   BPTR file;
   char *p;
   int plane, row, error = FALSE;

   /* Allocate bitmaps */
   if (( ts->gfxbm = alloc_bitmap( GFXW, GFXH, 4, BMF_CLEAR, ts->rp->BitMap )) == NULL ) return( FALSE );
   if (( ts->mskbm = alloc_bitmap( GFXW, GFXH, 1, BMF_CLEAR, ts->rp->BitMap )) == NULL ) return( FALSE );

   /* Open file */
   if (( file = Open( MGFX, MODE_OLDFILE )) == NULL )
   {
      MSG( 0, 0, "Unable to open graphics file" ); Delay( 100 );
      return ( FALSE );
   }

   /* Read file into bitmap */
   for ( plane = 0; plane < 4 && !error; plane++ )
   {
      p = ts->gfxbm->Planes[ plane ];
      for ( row = 0; row < GFXH && !error; row++ )
      {
         error = ( Read( file, p, GFXB ) != GFXB );
         p += ts->gfxbm->BytesPerRow;
      }
   }

   /* Read mask data into bitmap */
   p = ts->mskbm->Planes[ 0 ];
   for ( row = 0; row < GFXH && !error; row++ )
   {
      error = ( Read( file, p, GFXB ) != GFXB );
      p += ts->mskbm->BytesPerRow;
   }

   /* Close file */
   Close( file );

   /* Did we get any errors while reading? */
   if ( error )
   {
      MSG( 0, 0, "Error while reading graphics file" ); Delay( 100 );
      return ( FALSE );
   }

   /* Success */
   return ( TRUE );
}
///}
///{ "conv_gfx()"

int conv_gfx( void )
{
   term_data *ts = &screen;
   struct BitMap *tmpbm;
   struct BitMap *sbm = ts->rp->BitMap;
   long stdpens[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
   long mskpens[] = { 0, -1 };
   int depth;
   BYTE *src, *dst;
   int plane, row, byt;

   /* Get depth of display */
   depth = depth_of_bitmap( sbm );

   /* We don't want 16 or 24 bit displays (yet) */
   if ( depth > 8 )
   {
      MSG( 0, 1, "Sorry, max. 8 bit display supported." ); Delay( 100 );
      return ( FALSE );
   }

   /* Allocate new bitmap with screen's depth */
   if (( tmpbm = alloc_bitmap( GFXW, GFXH, depth, BMF_CLEAR, sbm )) == NULL )
   {
      MSG( 0, 1, "Unable to allocate temporary bitmap." ); Delay( 100 );
      return ( FALSE );
   }

   /* Simple remapping from 4 to 5 planes? */
   if ( depth == 5 && !use_pub )
   {
      for ( plane = 0; plane < 4; plane++ )
      {
         src = ts->gfxbm->Planes[ plane ];
         dst = tmpbm->Planes[ plane ];
         for ( row = 0; row < GFXH; row++ )
         {
            for ( byt = 0; byt < GFXB; byt++ )
            {
               dst[ byt ] = src[ byt ];
            }
            src += ts->gfxbm->BytesPerRow;
            dst += tmpbm->BytesPerRow;
         }
      }
   }

   /* Complex remapping */
   else
   {
      /* Remap old bitmat into new bitmap */
      remap_bitmap( ts->gfxbm, tmpbm, use_pub ? pubpens : stdpens, GFXW, GFXH );
   }

   /* Free old bitmap */
   free_bitmap( ts->gfxbm );
   ts->gfxbm = tmpbm;

   /* Allocate new bitmap with screen's depth */
   if (( tmpbm = alloc_bitmap( GFXW, GFXH, depth, BMF_CLEAR, sbm )) == NULL )
   {
      MSG( 0, 1, "Unable to allocate temporary bitmap." ); Delay( 100 );
      return ( FALSE );
   }

   /* Simple remapping from 4 to 5 planes? */
   if ( depth == 5 && !use_pub )
   {
      for ( plane = 0; plane < 4; plane++ )
      {
         src = ts->mskbm->Planes[ 0 ];
         dst = tmpbm->Planes[ plane ];
         for ( row = 0; row < GFXH; row++ )
         {
            for ( byt = 0; byt < GFXB; byt++ )
            {
               dst[ byt ] = src[ byt ];
            }
            src += ts->mskbm->BytesPerRow;
            dst += tmpbm->BytesPerRow;
         }
      }
   }

   /* Complex remapping */
   else
   {
      /* Remap old bitmap into new bitmap */
      remap_bitmap( ts->mskbm, tmpbm, mskpens, GFXW, GFXH );
   }

   /* Free old bitmap */
   free_bitmap( ts->mskbm );
   ts->mskbm = tmpbm;

   /* Done */
   return ( TRUE );
}

///}
///{ "size_gfx()"

int size_gfx( term_data *td )
{
   term_data *ts = &screen;
   int depth;
   struct BitMap *sbm = td->rp->BitMap;
   struct BitMap *tmpbm;

   /* Calculate tile bitmap dimensions */
   td->gfx_w = 32 * td->fw;
   td->gfx_h = 32 * td->fh;

   /* Calculate map bitmap dimensions */
   td->mpt_w = td->ww / DUNGEON_WID;
   td->mpt_h = td->wh / DUNGEON_HGT;
   td->map_w = td->mpt_w * 32;
   td->map_h = td->mpt_h * 32;

   /* Scale tile graphics into map size */
   depth = depth_of_bitmap( ts->gfxbm );
   if (( td->mapbm = alloc_bitmap( td->map_w, td->map_h, depth, BMF_CLEAR, sbm )) == NULL ) return( FALSE );
   scale_bitmap( ts->gfxbm, GFXW, GFXH, td->mapbm, td->map_w, td->map_h );

   /* Scale tile graphics */
   depth = depth_of_bitmap( ts->gfxbm );
   if (( tmpbm = alloc_bitmap( td->gfx_w, td->gfx_h, depth, BMF_CLEAR, sbm )) == NULL ) return( FALSE );
   scale_bitmap( ts->gfxbm, GFXW, GFXH, tmpbm, td->gfx_w, td->gfx_h );
   if ( td->gfxbm ) free_bitmap( td->gfxbm );
   td->gfxbm = tmpbm;

   /* Scale tile mask */
   depth = depth_of_bitmap( ts->mskbm );
   if (( tmpbm = alloc_bitmap( td->gfx_w, td->gfx_h, depth, BMF_CLEAR, sbm )) == NULL ) return( FALSE );
   scale_bitmap( ts->mskbm, GFXW, GFXH, tmpbm, td->gfx_w, td->gfx_h );
   if ( td->mskbm ) free_bitmap( td->mskbm );
   td->mskbm = tmpbm;

   /* Success */
   return( TRUE );
}

///}
///{ "put_gfx()"

static void put_gfx( struct RastPort *rp, int x, int y, int chr, int col )
{
   term_data *td = (term_data*)(Term->data);
   int fw = td->fw;
   int fh = td->fh;
   int x0 = x * fw;
   int y0 = y * fh;
   int x1 = x0 + fw - 1;
   int y1 = y0 + fh - 1;
   int a  = col & 0x1f;
   int c  = chr & 0x1f;

   /* Just a black tile */
   if ( a == 0 && c == 0 )
   {
      SetAPen( rp, PEN( 0 ));
      RectFill( rp, x0, y0, x1, y1 );
      return;
   }

   /* Player - Remap for race and class */
   if ( a == 12 && c == 0 )
   {
      a = (( p_ptr->pclass * 10 + p_ptr->prace) >> 5 ) + 12;
      c = (( p_ptr->pclass * 10 + p_ptr->prace) & 0x1f );
   }

   /* Draw tile through mask */
   if ( col & 0x40 )
   {
      SetAPen( rp, PEN(0) );
      RectFill( rp, x0, y0, x1, y1 );
      BltMaskBitMapRastPort( td->gfxbm, c * fw, a * fh, rp, x0, y0, fw, fh, (ABC|ANBC|ABNC), td->mskbm->Planes[ 0 ] );
   }

   /* Draw full tile */
   else
   {
      BltBitMapRastPort( td->gfxbm, c * fw, a * fh, rp, x0, y0, fw, fh, 0xc0 );
   }
}

///}
///{ "amiga_fail()"

static int amiga_fail( char *msg )
{
   int i;

   /* Print error message */
   if ( msg )
   {
       fprintf( stderr, "%s\n", msg );
   }

   /* Free sound memory */
   free_sound();

   /* Unlock public screen */
   if ( publock )
   {
      UnlockPubScreen( NULL, pubscr );
      publock = FALSE;
   }

   /* Remove menu from window */
   if ( menu && screen.win )
   {
      ClearMenuStrip( screen.win );
   }

   /* Free menus */
   if ( menu )
   {
      FreeMenus( menu );
      menu = NULL;
   }

   /* Free term resources */
   free_term( &mirror );
   free_term( &recall );
   free_term( &choice );
   free_term( &screen );

   /* Free obtained pens */
   if ( pubscr )
   {
      for ( i = 0; i < 32; i++)
      {
         if ( pubpens[ i ] != -1 )
         {
            ReleasePen( pubscr->ViewPort.ColorMap, pubpens[ i ]);
         }
      }
   }

   /* Free visual info */
   if ( visinfo )
   {
      FreeVisualInfo( visinfo );
      visinfo = NULL;
   }

   /* Close intuition screen */
   if ( amiscr )
   {
      CloseScreen( amiscr );
      amiscr = NULL;
   }

   /* Close gadtools.library */
   if ( GadToolsBase )
   {
      CloseLibrary( GadToolsBase );
      GadToolsBase = NULL;
   }

   /* Close keymap.library */
   if ( KeymapBase )
   {
      CloseLibrary( KeymapBase );
      KeymapBase = NULL;
   }

   /* Close keymap.library */
   if ( DiskfontBase )
   {
      CloseLibrary( DiskfontBase );
      DiskfontBase = NULL;
   }

   // Return failure
   return(-1);
}

///}
///{ "amiga_map()"

static void amiga_map( void )
{
   term_data *td = &screen; // (term_data*)(Term->data);
   int i,j;
   byte ta,tc;

   /* Only in graphics mode */
   if ( !use_graphics ) return;

   /* Turn off cursor */
   if ( td->cursor_visible ) cursor_off( td );

   /* Save screen */
   Term_save();

   /* Clear screen */
   Term_clear();
   Term_fresh();

   /* Calculate offset values */
   td->map_x = (( td->fw * 80 ) - ( td->mpt_w * DUNGEON_WID )) / 2;
   td->map_y = (( td->fh * 24 ) - ( td->mpt_h * DUNGEON_HGT )) / 2;

   /* Draw all "interesting" features */
   for ( i = 0; i < DUNGEON_WID; i++ )
   {
      for ( j = 0; j < DUNGEON_HGT; j++ )
      {
         /* Get frame tile */
         if ( i==0 || i == DUNGEON_WID - 1 || j == 0 || j == DUNGEON_HGT - 1 )
         {
            ta = f_info[ 63 ].z_attr;
            tc = f_info[ 63 ].z_char;
         }

         /* Get tile from cave table */
         else
         {
            map_info( j, i, &ta, (char *) &tc );
         }

         /* Ignore non-graphics */
         if ( ta & 0x80 )
         {
            ta &= 0x1f;
            tc &= 0x1f;

            /* Player XXX XXX XXX */
            if ( ta == 12 && tc ==0 )
            {
               ta = (( p_ptr->pclass * 10 + p_ptr->prace ) >> 5 ) + 12;
               tc = (( p_ptr->pclass * 10 + p_ptr->prace ) & 0x1f );
            }

            /* Put the graphics to the screen */
            put_gfx_map( td, i, j, tc, ta );
         }
      }
   }

   /* Draw a small cursor now */
   td->cursor_map = TRUE;

   /* Wait for a keypress, flush key buffer */
   Term_inkey( &tc, TRUE, TRUE );
   Term_flush();

   /* Normal cursor again */
   td->cursor_map = FALSE;

   /* Restore screen */
   Term_clear();
   Term_fresh();
   Term_load();
   Term_fresh();

   /* Turn cursor back on */
   if ( td->cursor_visible ) cursor_on( td );
}

///}
///{ "load_palette()"

void load_palette( void )
{
   int i;
   int n;

   if ( amiscr == NULL ) return;

   n = deep ? 32 : 16;

   if ( v39 )
   {
      palette32[ 0 ] = n << 16;
      palette32[ n * 3 + 1 ] = 0;
      for ( i = 0; i < n; i++ )
      {
         palette32[ i * 3 + 1 ] = color_table[ use_graphics ? i : i + 16 ][ 1 ] << 24;
         palette32[ i * 3 + 2 ] = color_table[ use_graphics ? i : i + 16 ][ 2 ] << 24;
         palette32[ i * 3 + 3 ] = color_table[ use_graphics ? i : i + 16 ][ 3 ] << 24;
      }
      LoadRGB32( &amiscr->ViewPort, palette32 );
   }
   else
   {
      for ( i = 0; i < n; i++ )
      {
         palette4[ i ] =  ( color_table[ use_graphics ? i : i + 16 ][ 1 ] >> 4 ) << 8;
         palette4[ i ] |= ( color_table[ use_graphics ? i : i + 16 ][ 2 ] >> 4 ) << 4;
         palette4[ i ] |= ( color_table[ use_graphics ? i : i + 16 ][ 3 ] >> 4 );
      }
      LoadRGB4( &amiscr->ViewPort, palette4, n );
   }
}

///}
///{ "create_menus()"

int create_menus( void )
{
   option_type *opt;
   struct NewMenu *item = newmenu;
   int nmsize = sizeof ( struct NewMenu );
   int page = -1;
   int pg = 0;
   int i,o;

   /* Copy all pre-items into array */
   for ( i = 0; pre_item[ i ].nm_Type != 255; i++ )
   {
      memcpy( item++, &pre_item[ i ], nmsize );
   }

   /* Find next option */
   for ( opt = options, o = 0; opt->o_desc; opt++, o++ )
   {
      /* Cheating options are skipped */
      if ( opt->o_page > 6 ) continue;

      /* New page of options? */
      if ( page != opt->o_page )
      {
         page = opt->o_page;

         /* Copy option header */
         memcpy( item++, &opt_item[ pg++ ], nmsize );
      }

      /* Insert fields for this option */
      item->nm_Type = NM_SUB;
      item->nm_CommKey = 0;
      item->nm_MutualExclude = 0;

      /* Use option description as menu text */
      item->nm_Label = (STRPTR) opt->o_desc;

      /* Insert option index into userdata field */
      item->nm_UserData = (void *)( MNU_OPTION + o );

      /* Use checkmark on this item */
      item->nm_Flags = CHECKIT | MENUTOGGLE;

      /* Done with this item */
      item++;
   }

   /* Copy all post-items into array */
   for ( i = 0; post_item[ i ].nm_Type != 255; i++ )
   {
      memcpy( item++, &post_item[ i ], nmsize );
   }

   /* Actually create the menu structures */
   menu = CreateMenus( newmenu, NULL );

   if ( menu )
   {
      /* Layout menus */
      if ( LayoutMenus( menu, visinfo, v39 ? GTMN_NewLookMenus : TAG_IGNORE, TRUE, TAG_END ))
      {
         /* Attach menu to window */
         SetMenuStrip( screen.win , menu );
      }

      /* Free menus */
      else
      {
         FreeMenus( menu );
         menu = NULL;
      }
   }

   /* Success */
   return ( menu != NULL );
}

///}
///{ "update_menus()"

void update_menus( void )
{
   struct MenuItem *father, *item;
   int i;

   /* Require a window and a menu */
   if ( !screen.win || !menu ) return;

   /* Detach the menu from the window */
//   ClearMenuStrip( screen.win );

   /* Initial menuitem and subitem for options */
   father = menu->FirstItem;
   item   = father->SubItem;

   /* Find next option */
   for ( i = 0; item && options[ i ].o_desc; i++ )
   {
      /* Did we find it? */
      if ( item )
      {
         /* Option is set, add a checkmark */
         if ( *(options[ i ].o_var ))
         {
            item->Flags |= CHECKED;
         }

         /* Option is not set, remove checkmark */
         else
         {
            item->Flags &= ~CHECKED;
         }
      }

      /* Menuitem not found */
      else
      {
         fprintf( stderr, "ERROR: menuitem #%d not found.\n", i );
         return;
      }

      /* Find next menuitem */
      if ((item = item->NextItem ) == NULL )
      {
         /* New set */
         father = father->NextItem;
         if ( father )
         {
            item = father->SubItem;
         }
      }
   }

   /* Enable/Disable the amiga map according to use_graphics */
   if ( item = ItemAddress( menu, FULLMENUNUM( 6, 7, 0 )))
   {
      item->Flags = use_graphics ? item->Flags | ITEMENABLED : item->Flags & ~ITEMENABLED;
   }

   /* Attach menu to window again */
//   ResetMenuStrip( screen.win, menu );
}

///}
///{ "init_sound()"

int init_sound( void )
{
   int i;
   char tmp[256];
   char buf[256];
   struct AmiSound *snd;

   /* Load samples */
   for ( i = 0; i < NSAMPLES; i++ )
   {
      /* Pointer to sound data */
      snd = &sound_data[ i ];

      /* Should this sample be loaded into memory? */
      if ( snd->Memory )
      {
         /* Construct filename */
         path_build(buf, 255, ANGBAND_DIR_XTRA, "sound");

         /* Construct filename */
         path_build(tmp, 255, buf, snd->Name );

         /* Load the sample into memory */
         snd->Address = (struct SoundInfo *) PrepareSound( tmp );
      }
   }

   /* Success */
   has_sound = TRUE;
   use_sound = TRUE;

   return ( TRUE );
}

///}
///{ "free_sound()"

void free_sound( void )
{
   int i;

   /* Stop all channels */
   StopSound( LEFT0 );
   StopSound( LEFT1 );
   StopSound( RIGHT0 );
   StopSound( RIGHT1 );

   /* Remove all sounds from memory */
   for ( i = 0; i < NSAMPLES; i++ )
   {
      if ( sound_data[ i ].Address )
      {
         /* Remove the sound from memory */
         RemoveSound( sound_data[ i ].Address );

         /* Clear address field */
         sound_data[ i ].Address = NULL;
      }
   }

   /* Done */
   has_sound = FALSE;
   use_sound = FALSE;
}

///}
///{ "play_sound()"

static void play_sound( int v )
{
   struct AmiSound *snd;
   struct AmiSound *old_snd;
   int rate;
   int channel;
   int old;
   char buf[256];
   char tmp[256];

   if ( has_sound )
   {
      /* Pointer to sound data */
      snd = &sound_data[ v ];

      /* Channel number */
      channel = snd->Channel;

      /* Last sample played on channel */
      old = channel_last[ channel ];

      /* Sample Rate */
      rate = snd->Rate;

      /* Random rate on some sounds */
      if ( v == SOUND_HIT || v == SOUND_MISS )
      {
         rate = rate - 50 + rand_int( 150 );
      }

      /* Pointer to old sound data */
      old_snd = old >= 0 ? &sound_data[ old ] : NULL;

      /* Stop sound currently playing on this channel */
      StopSound( channel );

      /* Free old sample if required */
      if ( !old_snd->Memory && old_snd->Address && old != v )
      {
         /* Remove it from memory */
         RemoveSound( old_snd->Address );

         /* Clear address field */
         old_snd->Address = NULL;
      }

      /* Load new sample into memory if required */
      if ( !snd->Memory && snd->Address == NULL )
      {
         /* Construct filename */
         path_build(buf, 255, ANGBAND_DIR_XTRA, "sound");

         /* Construct filename */
         path_build(tmp, 255, buf, snd->Name );

         /* Load the sample into memory */
         snd->Address = (struct SoundInfo *) PrepareSound( tmp );
      }

      /* Make sure the sample is loaded into memory */
      if ( snd->Address )
      {
         /* Start playing the sound */
         PlaySound( snd->Address, snd->Volume, channel, rate, snd->Repeats );
      }

      /* Store sample number */
      channel_last[ channel ] = v;
   }
}

///}
///{ put_gfx_map()

static void put_gfx_map( term_data *td, int x, int y, int c, int a )
{
   BltBitMapRastPort(
      td->mapbm,
      c * td->mpt_w,
      a * td->mpt_h,
      td->wrp,
      td->map_x + x * td->mpt_w,
      td->map_y + y * td->mpt_h,
      td->mpt_w,
      td->mpt_h,
      0xc0
   );
}

///}
///{ "alloc_bitmap()"

struct BitMap *alloc_bitmap( int width, int height, int depth, ULONG flags, struct BitMap *friend )
{
   int p;
   struct BitMap *bitmap;
   unsigned char *bp;

   /* Kickstart 39+ */
   if( v39 ) return ( AllocBitMap( width, height, depth, flags, friend ));

   /* Kickstart 38- */
   else
   {
      /* Allocate bitmap structure */
      if (( bitmap = AllocMem( sizeof( struct BitMap ), MEMF_PUBLIC | MEMF_CLEAR )))
      {
         InitBitMap( bitmap, depth, width, height );
         /* Allocate bitplanes */
         for ( p = 0; p < depth; p++ )
         {
            bp = AllocRaster( width, height );
            if ( !bp ) break;
            bitmap->Planes[ p ] = bp;
         }

         /* Out of memory */
         if ( p != depth )
         {
            /* Free bitplanes */
            while ( --p >= 0 )
            {
               FreeRaster( bitmap->Planes[ p ], width, height );
            }
            /* Free bitmap structure */
            FreeMem( bitmap, sizeof( struct BitMap ));
            bitmap = NULL;
         }
      }
      return ( bitmap );
   }
}

///}
///{ "free_bitmap()"

void free_bitmap( struct BitMap *bitmap )
{
   int p;

   /* Check for NULL */
   if ( !bitmap ) return;

   /* Kickstart 39+ */
   if ( v39 ) FreeBitMap( bitmap );

   /* Kickstart 38- */
   else
   {
      /* Free bitplanes */
      for ( p = 0; p < bitmap->Depth; p++ )
      {
         FreeRaster( bitmap->Planes[ p ], bitmap->BytesPerRow * 8, bitmap->Rows );
      }
      /* Free bitmap structure */
      FreeMem( bitmap, sizeof( struct BitMap ));
   }
}

///}
///{ scale_bitmap()

void scale_bitmap( struct BitMap *srcbm, int srcw, int srch, struct BitMap *dstbm, int dstw, int dsth )
{
   struct BitScaleArgs bsa;

   /* Scale bitmap */
   bsa.bsa_SrcBitMap   = srcbm;
   bsa.bsa_DestBitMap  = dstbm;
   bsa.bsa_SrcX        = 0;
   bsa.bsa_SrcY        = 0;
   bsa.bsa_SrcWidth    = srcw;
   bsa.bsa_SrcHeight   = srch;
   bsa.bsa_DestX       = 0;
   bsa.bsa_DestY       = 0;
   bsa.bsa_XSrcFactor  = srcw;
   bsa.bsa_YSrcFactor  = srch;
   bsa.bsa_XDestFactor = dstw;
   bsa.bsa_YDestFactor = dsth;
   bsa.bsa_Flags       = 0;
   BitMapScale( &bsa );
}

///}
///{ "remap_bitmap()"

void remap_bitmap( struct BitMap *srcbm, struct BitMap *dstbm, long *pens, int width, int height )
{
   int x,y,p,c,ox,lpr,sd,dd;
   int bm[] = { 1, 2, 4, 8, 16, 32, 64, 128 };
   UBYTE *sp[ 4 ];
   UBYTE *dp[ 8 ];
   ULONG ls[ 4 ];
   ULONG ld[ 8 ];
   ULONG mask;

   /* Source bitplanes */
   sd = depth_of_bitmap( srcbm );
   for ( p = 0; p < sd; p++ )
   {
      sp[ p ] = srcbm->Planes[ p ];
   }

   /* Destination bitplanes */
   dd = depth_of_bitmap( dstbm );
   for ( p = 0; p < dd; p++ )
   {
      dp[ p ] = dstbm->Planes[ p ];
   }

   /* Number of longwords per row */
   lpr = width / 32;

   /* Convert graphics */
   for( y = 0; y < height; y++ )
   {
      ox = 0;
      for ( x = 0 ; x < lpr; x++ )
      {
         /* Read source longwords */
         for ( p = 0; p < sd; p++ ) ls[ p ] = *(ULONG *)( sp[ p ] + ox);

         /* Clear destination longwords */
         for ( p = 0; p < dd; ld[ p++ ] = 0 );

         /* Remap */
         for ( mask = 0x80000000; mask != 0; mask >>= 1)
         {
            /* Find color index */
            for ( p = c = 0; p < sd; p++ ) if ( ls[ p ] & mask ) c |= bm[ p ];

            /* Remap */
            c = pens[ c ];

            /* Update destination longwords */
            for ( p = 0; p < dd; p++ ) if ( c & bm[ p ] ) ld[ p ] |= mask;
         }

         /* Write destination longwords */
         for ( p = 0; p < dd; p++ ) *(ULONG *)( dp[ p ] + ox ) = ld[ p ];

         /* Update offset */
         ox += 4;
      }

      /* Update pointers to get to next line */
      for ( p = 0; p < sd; sp[ p++ ] += srcbm->BytesPerRow );
      for ( p = 0; p < dd; dp[ p++ ] += dstbm->BytesPerRow );
   }
}

///}
///{ "depth_of_bitmap()"

int depth_of_bitmap( struct BitMap *bm )
{
   int depth;

   if ( v39 )
   {
      depth = GetBitMapAttr( bm, BMA_DEPTH );
   }
   else
   {
      depth = bm->Depth;
   }

   return ( depth );
}

///}

