/*

   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/text.h>
#include <proto/exec.h>
#include <proto/diskfont.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/console.h>

///}
///{ "macros"

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

/* Graphics pen number convertion */
#define GPEN( 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"

/* 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;

/* */
struct Library *ConsoleDevice;
struct IOStdReq iostdreq;

/* Screen pointers */
static struct Screen *amiscr;
static struct NewScreen newscreen = {
   0, 0, 640, 200, 4,
   0, 1,
   HIRES,
   CUSTOMSCREEN | SCREENQUIET | SCREENBEHIND | AUTOSCROLL,
   0,
   "Angband Screen",
   0,
   0
};

struct NewWindow newwindow = {
   0, 0, 640, 200,
   0xff, 0xff,
   IDCMP_RAWKEY, /* | IDCMP_INTUITICKS | IDCMP_MOUSEMOVE | IDCMP_MOUSEBUTTONS,*/
   WFLG_BACKDROP | WFLG_BORDERLESS | WFLG_ACTIVATE, /* WFLG_REPORTMOUSE | WFLG_RMBTRAP, */
   0, 0, 0, 0, 0,
   640, 200,
   640, 200,
   CUSTOMSCREEN
};


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

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

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

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

/* 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
};

/* 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 */
   { "level.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 };

///}
///{ "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 );
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 );
int load_gfx( void );
static void put_gfx( struct RastPort *rp, int x, int y, int chr, int col );
void load_palette( 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 );
void handle_rawkey( UWORD code, UWORD qual, APTR addr );
void load_font(void);

///}

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

errr init_ami( void )
{
   int i;

   /* Term data pointers */
   term_data *ts = &screen;

   /* Clear the term data */
   init_term( ts );

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

   /* Initialize keyboard stuff */
   ie.ie_NextEvent = NULL;
   ie.ie_Class     = IECLASS_RAWKEY;
   ie.ie_SubClass  = 0;

    /* Open console device for library pointer used by RawKeyConvert. */
	if ( 0 != OpenDevice("console.device", -1, (struct IORequest *)&iostdreq, 0) )
	{
       FAIL( "Unable to open console device." );
	}
	ConsoleDevice = (struct Library *)iostdreq.io_Device;


   /* 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 );
      }
   }

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

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

   load_font();

   newscreen.Font = scrattr;
   if (0 == (amiscr = OpenScreen(&newscreen)))
   {
      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 );

   newwindow.Screen = amiscr;
   /* Open window, backdrop if on custom screen */
   if (0 == (ts->win = OpenWindow(&newwindow)))
   {
      FAIL("Unable to open window.");
   }

   /* 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 );

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

   /* Link terms with the metaterm */
   if ( ts->use ) term_screen = link_term( ts );

   /* Bring screen to front */
   ScreenToFront( amiscr );

   /* Load and convert graphics */
   MSG( 0, 0, "Loading graphics" );
   if ( !load_gfx() ) FAIL( NULL );
   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 )
{
   /* 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" ));
}

///}
///{ "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 )
   {
      /* 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 )
   {
      /* 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( amiscr );
         return ( 0 );


      /* Play a sound */
      case TERM_XTRA_SOUND:

         if ( use_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 );

      /* 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;

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

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

      /* 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();

   return ( 0 );
}

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

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

   /* 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 = RawKeyConvert( &ie, buf, 80, NULL );

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

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

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

   if ( !td->cursor_lit )
   {
      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 )
   {
      /* 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;
   }
}

///}
///{ "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 );
}
///}
///{ "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 )
{
   /* Print error message */
   if ( msg )
   {
       fprintf( stderr, "%s\n", msg );
   }

   /* Free sound memory */
   free_sound();

   /* Free term resources */
   free_term( &screen );

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

   if ( ConsoleDevice )
   {
      CloseDevice((struct IORequest *)&iostdreq);
   }

   // Return failure
   return(-1);
}

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

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

   if ( amiscr == NULL ) return;

   n = 32;

   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 );
}

///}
///{ "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;

   /* 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;

   /* 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 ));
}

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

int depth_of_bitmap( struct BitMap *bm )
{
   return bm->Depth;
}

///}
void load_font(void)
{
   struct TextAttr attr;
   term_data *td = &screen;

   /* Set font attributes */
   attr.ta_Name  = "moria.font";
   attr.ta_YSize = 8;
   attr.ta_Style = FS_NORMAL;
   attr.ta_Flags = 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 = "moria.font";
         scrattr->ta_YSize = 8;
         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", "moria", 8);
   }
}

void amiga_tomb(void)
{
   return 0;
}

///{ "init_sound()"

int init_sound( void )
{
   int i;
   char tmp[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 */
         sprintf( tmp, "%ssound/%s", ANGBAND_DIR_XTRA, snd->Name );

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

   /* Success */
   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 */
   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 tmp[256];

   if ( use_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 */
         sprintf( tmp, "%ssound/%s", ANGBAND_DIR_XTRA, 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;
   }
}

///}
