/*    SCCS Id: @(#)winchar.c     3.1    93/07/22			*/
/*    Copyright (c) Olaf Seibert (KosmoSoft), 1989, 1992	  */
/*    Copyright (c) Kenneth Lorber, Bethesda, Maryland 1993	  */
/*    Copyright (c) Gregg Wonderly, Naperville Illinois, 1994.		*/
/*    NetHack may be freely redistributed.  See license for details.	*/

#include <exec/types.h>
#include <libraries/iffparse.h>
#include <graphics/scale.h>
#ifndef _DCC
#include <proto/iffparse.h>
#endif

#ifdef TESTING
# include "hack.h"
#else
# include "NH:src/tile.c"
#endif

#include "NH:win/share/tile.h"

#include "NH:sys/amiga/windefs.h"
#include "NH:sys/amiga/winext.h"
#include "NH:sys/amiga/winproto.h"

#ifdef OPT_DISPMAP
# define DISPMAP		/* use display_map() from dispmap.s */
#endif

/* NH:sys/amiga/winvchar.c */
int main ( int  , char ** );
struct BitMap *MyAllocBitMap ( int  , int  , int  , long  );
void MyFreeBitMap ( struct BitMap * );
void FreeImageFiles ( char **, struct BitMap ** );
void amiv_flush_glyph_buffer ( struct Window * );
void amiv_lprint_glyph ( winid  , int  , int  );
void amii_lprint_glyph ( winid  , int  , int  );
void amiv_start_glyphout ( winid  );
void amii_end_glyphout ( winid  );
void SetMazeType ( MazeType  );
int GlyphToIcon ( int  );
void amii_start_glyphout ( winid  );
void amii_end_glyphout ( winid  );
void amii_flush_glyph_buffer( struct Window * );

int amii_extraplanes = 0;
extern int reclip;

struct BitMap *MyAllocBitMap( int xsize, int ysize, int depth, long mflags );
void MyFreeBitMap( struct BitMap *bmp );

#ifdef DISPMAP
extern void display_map( struct Window * );
#endif

/*
 *  These values will be available from tile.c source
 *
 * #define MAXMONTILE 335
 * #define MAXOBJTILE 722
 * #define MAXOTHTILE 841
 */

#define	IMGROWS		12
#define IMGCOLUMNS	20
#define IMGPAGESIZE	(IMGROWS*IMGCOLUMNS)

#define ID_BMAP		MAKE_ID('B','M','A','P')	/* The type of form we use */
#define ID_BMHD		MAKE_ID('B','M','H','D')	/* The ILBM bitmap header */
#define ID_CAMG		MAKE_ID('C','A','M','G')	/* The ILBM camg (ignored) */
#define ID_CMAP		MAKE_ID('C','M','A','P')	/* Standard ILBM color map */
#define ID_PLNE		MAKE_ID('P','L','N','E')	/* The plane data */
#define ID_PDAT		MAKE_ID('P','D','A','T')	/* The PDAT structure below */

struct PDAT pictdata;

#define NUMTILEIMAGES	3
char *tileimages[] =
{
#define TBLMONTILE	0
	"NetHack:tiles/monsters.iff",
#define TBLOBJTILE	1
	"NetHack:tiles/objects.iff",
#define TBLOTHTILE	2
	"NetHack:tiles/other.iff",
	0,
};

struct BitMap *ifftimg[ NUMTILEIMAGES ], *tile;

#ifdef TESTING
short pens[NUMDRIPENS] = { 8, 3, 15, 0, 15, 7, 7, 8, 0 };
main( int argc, char **argv )
{
    BitMapHeader bmhd;
    struct IntuiMessage *imsg;
    long code, class;
    char buf[100];
    int i, x, y, tbl, done = 0, num;
    struct Window *w;
    struct Screen *scr;

    bmhd = ReadTileImageFiles( );

    scr = OpenScreenTags( NULL,
	SA_Depth, pictdata.nplanes + amii_extraplanes,
	SA_DisplayID, DBLNTSC_MONITOR_ID|HIRESLACE_KEY,
	SA_Overscan, OSCAN_TEXT,
	SA_Top, 0,
	SA_Left, 0,
	SA_Width, STDSCREENWIDTH,
	SA_Height, STDSCREENHEIGHT,
	SA_Type, CUSTOMSCREEN,
	SA_DetailPen, 0,
	SA_BlockPen, 1,
	SA_Title, "NetHack Chars",
	SA_Pens, pens,
	TAG_DONE
	);
    if( scr == NULL )
    {
    	printf( "no screen\n" );
#undef exit
    	exit( 1 );
    }

    w = OpenWindowTags( 0,
		WA_CustomScreen, scr,
    		WA_Flags, WFLG_DRAGBAR|WFLG_SIZEGADGET|WFLG_DEPTHGADGET|WFLG_CLOSEGADGET,
    		WA_IDCMP, IDCMP_CLOSEWINDOW|IDCMP_NEWSIZE|IDCMP_MOUSEBUTTONS,
		WA_Left, 0, 
		WA_Top, scr->WBorTop + 1 + 13, 
		WA_MinWidth, 100,
		WA_MinHeight, 100,
		WA_MaxWidth, 700,
		WA_MaxHeight, 1000,
		WA_Width, 640, 
		WA_Height, 340, 
		WA_SmartRefresh, TRUE,
		TAG_DONE );
    if( w )
    {
	while( !done )
	{
	    for( i = 0; i < NUMTILEIMAGES * IMGPAGESIZE; ++i )
	    {
		int dx, dy;
		tbl = i/IMGPAGESIZE;
		x = i % IMGPAGESIZE;
		y = x / IMGCOLUMNS;
		x = x % IMGCOLUMNS;
		dx = i % (IMGCOLUMNS*2);
		dy = i / (IMGCOLUMNS*2);
		BltBitMapRastPort( ifftimg[ tbl ],
				x * pictdata.xsize, y * pictdata.ysize,
				w->RPort,
				w->BorderLeft + 1 + dx*pictdata.xsize,
				w->BorderTop + 1 + dy*pictdata.ysize,
				pictdata.xsize, pictdata.ysize, 0xc0 );
	    }
	    WaitPort( w->UserPort );
	    while( imsg = (struct IntuiMessage *)GetMsg( w->UserPort ) )
	    {
	    	class = imsg->Class;
	    	code = imsg->Code;
	    	ReplyMsg( (struct Message *)imsg );
	    	switch( class )
		{
		case IDCMP_MOUSEBUTTONS:
		    {
			x = imsg->MouseX - w->BorderLeft;
			y = imsg->MouseY - w->BorderTop;
			num = ((y/pictdata.ysize)*IMGCOLUMNS*2)+(x/pictdata.xsize);
			sprintf( buf, "Char #%d", num );
			SetWindowTitles( w, buf, buf );
		    }
		    break;
		case IDCMP_CLOSEWINDOW:
		    done = 1;
		    break;
		}
	    }
	}
	CloseWindow( w );
	CloseScreen( scr );
    }

    FreeImageFiles(tileimages, ifftimg );

    return(0);
}
#endif

BitMapHeader
ReadTileImageFiles(){
    char *errstr = NULL;
    BitMapHeader ret = ReadImageFiles(tileimages, ifftimg, &errstr);
    if(errstr){
	panic(errstr);
    }
    return ret;
}

BitMapHeader
ReadImageFiles(char **filenames, struct BitMap **iffimg, char **errstrp )
{
    BitMapHeader *bmhd = NULL, bmhds;
    unsigned char *cmap;
    extern int errno;
    register int i, j;
    struct IFFHandle *iff;
    struct StoredProperty *prop;

    IFFParseBase = OpenLibrary( "iffparse.library", 0L );
    if( !IFFParseBase )
    {
	*errstrp = "No iffparse.library";
	return bmhds;
    }

/*
    for( i = 0; filenames[i]; ++i )
	memset( iffimg[i], 0, sizeof( struct BitMap ) );
*/
    for( i = 0; filenames[i]; ++i )
    {
	iff = AllocIFF();
	if( !iff )
	{
	    FreeImageFiles(filenames, iffimg );
	    *errstrp = "can't start IFF processing";
	    return bmhds;
	}
	iff->iff_Stream = Open( filenames[i], MODE_OLDFILE );
	if( iff->iff_Stream == 0 )
	{
	    char *buf = malloc(100+strlen(filenames[i]));
	    FreeImageFiles( filenames, iffimg );
	    sprintf(buf, "Can't open %s: %s", filenames[i], strerror( errno ));
	    *errstrp = buf;
	    return bmhds;
	}
	InitIFFasDOS( iff );
	OpenIFF( iff, IFFF_READ );
	PropChunk( iff, ID_BMAP, ID_BMHD );
	PropChunk( iff, ID_BMAP, ID_CMAP );
	PropChunk( iff, ID_BMAP, ID_CAMG );
	PropChunk( iff, ID_BMAP, ID_PDAT );
	StopChunk( iff, ID_BMAP, ID_PLNE );
	if( ( j = ParseIFF( iff, IFFPARSE_SCAN ) ) != 0 )
	{
	    char *buf = malloc(100);
	    FreeImageFiles( filenames, iffimg );
	    sprintf(buf,"ParseIFF failed for image %d, failure code: %d",i,j);
	    *errstrp = buf;
	    return bmhds;
	}

	if( prop = FindProp( iff, ID_BMAP, ID_BMHD ) )
	{
	    bmhd = (BitMapHeader *)prop->sp_Data;
	}
	else
	{
	    FreeImageFiles(filenames, iffimg);
	    CloseIFF( iff );
	    Close( iff->iff_Stream );
	    FreeIFF( iff );
	    *errstrp = "No BMHD CHUNK in file";
	    return bmhds;
	}

	if( prop = FindProp( iff, ID_BMAP, ID_CMAP ) )
	{
	    cmap = prop->sp_Data;
	    for( j = 0; j < (1L << bmhd->nPlanes)*3; j+=3 )
	    {
#if 0
		/* Some day we will want to use the larger palette
		 * resolution available under v39 and later.  i.e.
		 * 32 instead of 12 bits of color.  Ususally this
		 * just means shifting the color left by 16-20 bits
		 * depending on what intensity looks best.  Experience
		 * says that the higher values are better intensities.
		 *
		 * For now though we won't do this. The color table
		 * structure is incompatible with earlier versions of
		 * intuition.  We would have to do some funny things
		 * to make 3*AMII_MAXCOLORS longs work like 3*AMII_MAXCOLORS
		 * UWORD's at run time...  A union would help, but...
		 */
		if( IntuitionBase->LibNode.lib_Version >= 39 )
		{
		    /* 8 bits of color, so shift to left end. */
		    amiv_init_map[ j+0 ] = cmap[j+0]<<24;
		    amiv_init_map[ j+1 ] = cmap[j+1]<<24;
		    amiv_init_map[ j+2 ] = cmap[j+2]<<24;
		}
		else
#endif
		{
		    /* We can only use 4 bits of the 8 that are stored in the
		     * cmap, so mask them and then shift them into position
		     * for the UWORD value to store.
		     */
#ifndef TESTING
		    amii_initmap[ j/3 ] = 
		    amiv_init_map[ j/3 ] =
					((cmap[j+0]>>4)<<8)|
					((cmap[j+1]>>4)<<4)|
					(cmap[j+2]>>4);
#endif
		}
	    }
	}
	else
	{
	    FreeImageFiles(filenames, iffimg);
	    CloseIFF( iff );
	    Close( iff->iff_Stream );
	    FreeIFF( iff );
	    *errstrp = "No CMAP CHUNK in file";
	    return bmhds;
	}

	if( prop = FindProp( iff, ID_BMAP, ID_PDAT ) )
	{
	    struct PDAT *pp;

	    pp = (struct PDAT *)prop->sp_Data;
	    pictdata = *pp;
	}
	else
	{
	    FreeImageFiles(filenames, iffimg);
	    CloseIFF( iff );
	    Close( iff->iff_Stream );
	    FreeIFF( iff );
	    *errstrp = "No PDAT CHUNK in file";
	    return bmhds;
	}

	iffimg[ i ] = MyAllocBitMap( bmhd->w, bmhd->h,
		pictdata.nplanes + amii_extraplanes, MEMF_CHIP|MEMF_CLEAR );
	if( iffimg[ i ] == NULL )
	{
	    char *buf = malloc(80);
	    FreeImageFiles(filenames, iffimg);
	    sprintf(buf, "Can't allocate bitmap for image %d\n", i );
	    *errstrp = buf;
	    return bmhds;
	}
	for( j = 0; j < pictdata.nplanes + amii_extraplanes; ++j )
	{
	    ReadChunkBytes( iff, iffimg[i]->Planes[j], RASSIZE( bmhd->w, bmhd->h ) );
	}
	bmhds = *bmhd;
	CloseIFF( iff );
	Close( iff->iff_Stream );
	FreeIFF( iff );
    }
    CloseLibrary( IFFParseBase );

    tile = MyAllocBitMap( pictdata.xsize, pictdata.ysize,
	    pictdata.nplanes + amii_extraplanes, MEMF_CHIP|MEMF_CLEAR );
    if( tile == NULL )
    {
	FreeImageFiles(filenames, iffimg);
	*errstrp = "Can't allocate tile bitmap for scaling";
    }
    return( bmhds );
}

struct MyBitMap
{
	struct BitMap bm;
	long mflags;
	USHORT xsize, ysize;
};

struct BitMap *
MyAllocBitMap( int xsize, int ysize, int depth, long mflags )
{
    int j;
    struct MyBitMap *bm;

    bm = (struct MyBitMap *)alloc( sizeof( *bm ) );
    if( !bm )
	return( NULL );

    bm->xsize = xsize;
    bm->ysize = ysize;
    InitBitMap( &bm->bm, depth, xsize, ysize );
    for( j = 0; j < depth; ++j )
    {
	if( mflags & MEMF_CHIP )
	    bm->bm.Planes[ j ] = AllocRaster( xsize, ysize );
	else
	    bm->bm.Planes[ j ] = AllocMem( RASSIZE( xsize, ysize ), mflags );

	if( bm->bm.Planes[ j ] == 0 )
	{
	    MyFreeBitMap( &bm->bm );
	    return( NULL );
	}
	if( mflags & MEMF_CLEAR )
	    memset( bm->bm.Planes[ j ], 0, RASSIZE( xsize, ysize ) );
    }
    return( &bm->bm );
}

void
MyFreeBitMap( struct BitMap *bmp )
{
    int j;
    struct MyBitMap *bm = (struct MyBitMap *)bmp;

    for( j = 0; j < bm->bm.Depth; ++j )
    {
	if( bm->bm.Planes[j] )
	{
	    if( bm->mflags & MEMF_CHIP )
		FreeRaster( bm->bm.Planes[j], bm->xsize, bm->ysize );
	    else
		FreeMem( bm->bm.Planes[j], RASSIZE( bm->xsize, bm->ysize ) );
	}
    }
    free( bm );
}

#ifdef	TESTING
void
panic(s,a1,a2,a3,a4)
	char *s;
{
    printf( s, a1, a2, a3, a4 );
    putchar('\n');
}
long *
alloc(unsigned int x){
	long *p = (long *)malloc(x);
	if(!p){panic("malloc failed"); exit(1);}
	return p;
}
#endif

void
FreeTileImageFiles(){
	FreeImageFiles(tileimages,ifftimg);
}

void
FreeImageFiles(char **filenames, struct BitMap **img )
{
    register int i;

    for( i = 0; filenames[i]; ++i )
    {
	if( img[ i ] )
	    MyFreeBitMap( img[ i ] );
    }

	/* REALLY ugly hack alert! */
    if( tile && img==ifftimg)
	MyFreeBitMap( tile );
}

#ifndef TESTING
/*
 * Define some stuff for our special glyph drawing routines
 */
unsigned short glyph_node_index, glyph_buffer_index;
#define NUMBER_GLYPH_NODES  80
#define GLYPH_BUFFER_SIZE   512
struct amiv_glyph_node {
    short	odstx, odsty;
    short	srcx, srcy, dstx, dsty;
    struct BitMap	*bitmap;
};
struct amiv_glyph_node amiv_g_nodes[NUMBER_GLYPH_NODES];
static char amiv_glyph_buffer[GLYPH_BUFFER_SIZE];

void
flush_glyph_buffer( vw )
    struct Window *vw;
{
    if( WINVERS_AMIV )
	amiv_flush_glyph_buffer ( vw );
    else
	amii_flush_glyph_buffer ( vw );
}

/*
 * Routine to flush whatever is buffered
 */
void
amiv_flush_glyph_buffer( vw )
    struct Window *vw;
{
#if !defined(DISPMAP) || defined(OPT_DISPMAP)
    int xsize, ysize, x, y;
    struct BitScaleArgs bsa;
    struct BitScaleArgs bsm;
    struct RastPort rast;
    struct Window *w = NULL;
    struct BitMap *imgbm = 0, *bm = 0;
    int i, k;
    int scaling_needed;
    register struct RastPort *rp = vw->RPort;
#endif

    /* If nothing is buffered, return before we do anything */
    if(glyph_node_index == 0)
	return;

    cursor_off( WIN_MAP );
    amiv_start_glyphout( WIN_MAP );

#ifdef OPT_DISPMAP
    if(flags.fast_map){
#endif
#ifdef DISPMAP
	display_map( vw );
#endif
#ifdef OPT_DISPMAP
    } else {
#endif
#if !defined(DISPMAP) || defined(OPT_DISPMAP)
/* XXX fix indent */
    /* This is a dynamic value based on this relationship. */
    scaling_needed = ( pictdata.xsize != mxsize || pictdata.ysize != mysize );

    /* If overview window is up, set up to render the correct scale there */
    if( WIN_OVER != WIN_ERR && ( w = amii_wins[ WIN_OVER ]->win ) != NULL )
    {
	InitRastPort( &rast );

	/* Calculate the x and y size of each tile for a ROWNO by COLNO map */
	xsize = (w->Width - w->BorderLeft - w->BorderRight) / COLNO;
	ysize = (w->Height - w->BorderTop - w->BorderBottom) / ROWNO;

	/* Get a chip memory bitmap to blit out of */
	bm = MyAllocBitMap( pictdata.xsize, pictdata.ysize,
	    pictdata.nplanes + amii_extraplanes, MEMF_CLEAR|MEMF_CHIP );
	if( bm == NULL )
	{
	    amii_putstr( WIN_MESSAGE, 0, "Can't allocate bitmap for scaling overview window" );
	}

	rast.BitMap = bm;

	memset( &bsa, 0, sizeof( bsa ) );
	bsa.bsa_SrcX = bsa.bsa_SrcY = 0;
	bsa.bsa_SrcBitMap = tile;
	bsa.bsa_SrcWidth = pictdata.xsize;
	bsa.bsa_SrcHeight = pictdata.ysize;
	bsa.bsa_XSrcFactor = pictdata.xsize;
	bsa.bsa_YSrcFactor = pictdata.ysize;
	bsa.bsa_DestX = 0;
	bsa.bsa_DestY = 0;
	bsa.bsa_DestWidth = xsize;
	bsa.bsa_DestHeight = ysize;
	bsa.bsa_XDestFactor = xsize;
	bsa.bsa_YDestFactor = ysize;
	bsa.bsa_DestBitMap = bm;
    }

    if( scaling_needed )
    {
	/* Fill in scaling data for map rendering */
	memset( &bsm, 0, sizeof( bsm ) );
	bsm.bsa_SrcX = bsm.bsa_SrcY = 0;
	bsm.bsa_SrcBitMap = tile;

	bsm.bsa_SrcWidth = pictdata.xsize;
	bsm.bsa_SrcHeight = pictdata.ysize;

	bsm.bsa_XSrcFactor = pictdata.xsize;
	bsm.bsa_YSrcFactor = pictdata.ysize;

	bsm.bsa_DestWidth = mxsize;
	bsm.bsa_DestHeight = mysize;

	bsm.bsa_XDestFactor = mxsize;
	bsm.bsa_YDestFactor = mysize;
	bsm.bsa_DestBitMap = rp->BitMap;
	bsm.bsa_DestY = bsm.bsa_DestX = 0;

	imgbm = MyAllocBitMap( mxsize, mysize,
	    pictdata.nplanes + amii_extraplanes, MEMF_CLEAR|MEMF_CHIP );
	if( imgbm == NULL )
	{
	    amii_putstr( WIN_MESSAGE, 0,
		"Can't allocate scaling bitmap for map window" );
	}
	else
	    bsm.bsa_DestBitMap = imgbm;
    }

    /* Go ahead and start dumping the stuff */
    for( i=0; i<glyph_node_index; ++i )
    {
	/* Do it */
	register int offx, offy, j;
	struct BitMap *nodebm = amiv_g_nodes[ i ].bitmap;

	/* Get the unclipped coordinates */
	x = amiv_g_nodes[ i ].odstx;
	y = amiv_g_nodes[ i ].odsty;

	/* If image is not in CHIP. copy each plane into tile line by line */

	offx = amiv_g_nodes[ i ].srcx / 8;	/* 8 is bits per byte */
	offy = amiv_g_nodes[ i ].srcy * nodebm->BytesPerRow;
	for( j = 0; j < pictdata.nplanes + amii_extraplanes; ++j )
	{
	    for( k = 0; k < pictdata.ysize; ++k )
	    {

	    	/* For a 16x16 tile, this could just be short assignments, but
	    	 * this code is generalized to handle any size tile image...
	    	 */
	    	memcpy( tile->Planes[ j ] + ( ( k * pictdata.ysize ) / 8 ),
			nodebm->Planes[ j ] + offx + offy + ( nodebm->BytesPerRow * k ),
			pictdata.ysize/8 );
	    }
	}

	if( !clipping ||
		( x >= clipx    && y >= clipy &&
		  x <  clipxmax && y <  clipymax ) )
	{
	    /* scaling is needed, do it */
	    if( scaling_needed )
	    {
		BitMapScale( &bsm );
		BltBitMapRastPort( imgbm, 0, 0,
		    rp, amiv_g_nodes[ i ].dstx, amiv_g_nodes[ i ].dsty,
		    mxsize, mysize, 0xc0 );
	    }
	    else
	    {
		BltBitMapRastPort( tile, 0, 0,
		    rp, amiv_g_nodes[ i ].dstx, amiv_g_nodes[ i ].dsty,
		    pictdata.xsize, pictdata.ysize, 0xc0 );
	    }
	}
	/* Draw the overview window unless we are scrolling the map raster around */
	if( bm && w && reclip != 2 )
	{
	    BitMapScale( &bsa );
	    BltBitMapRastPort( rast.BitMap, 0, 0,
		    w->RPort,
		    w->BorderLeft + amiv_g_nodes[ i ].odstx*xsize,
		    w->BorderTop + amiv_g_nodes[ i ].odsty*ysize,
		    xsize, ysize, 0xc0 );
	}
    }

    if( imgbm ) MyFreeBitMap( imgbm );
    if( bm ) MyFreeBitMap( bm );
#endif /* DISPMAP */
#ifdef OPT_DISPMAP
    }
#endif

    amii_end_glyphout( WIN_MAP );

    /* Clean up */
    glyph_node_index = glyph_buffer_index = 0;
}

/*
 * Glyph buffering routine.  Called instead of WindowPuts().
 */
void
amiv_lprint_glyph(window,color_index, glyph)
    winid window;
    int color_index, glyph;
{
    int base;
    struct amii_WinDesc *cw;
    struct Window *w;
    int curx;
    int cury;
    int tbl, icon;
    register int xoff, yoff;

    /* Get the real icon index */
    if( glyph != NO_GLYPH )
	icon = GlyphToIcon( glyph );

    if( ( cw=amii_wins[window] ) == (struct amii_WinDesc *)NULL )
	panic("bad winid in amiv_lprint_glyph: %d", window );

    w = cw->win;

    if( glyph != NO_GLYPH && glyph < 10000)
    {
	/* decide on which image has the needed picture */
	if( icon <= MAXMONTILE )
	{
	    tbl = TBLMONTILE;
	    base = 0;
	}
	else if( icon <= MAXOBJTILE )
	{
	    tbl = TBLOBJTILE;
	    base = MAXMONTILE+1;
	}
	else if( icon <= MAXOTHTILE )
	{
	    tbl = TBLOTHTILE;
	    base = MAXOBJTILE+1;
	}
	else
	    panic( "Bad icon #%d, glyph #%d, only %d icons known\n", icon, glyph, MAXOTHTILE );

	/* Get the relative offset in the page */

	/* How many pixels to account for y distance down */
	yoff = ((icon-base) / pictdata.across) * pictdata.ysize;

	/* How many pixels to account for x distance across */
	xoff = ((icon-base) % pictdata.across) * pictdata.xsize;
    }

    if(glyph >= 10000){
	/* Run a single ASCII character out to the rastport right now */
	char c= glyph-10000;
	int xxx,xxy;
	struct RastPort *rp = w->RPort;

	Move(rp, xxx=(((cw->curx-clipx)*rp->TxWidth) + w->BorderLeft),
	  xxy=(w->BorderTop + (((cw->cury-clipy)+1)* rp->TxHeight)+1));
	Text(rp,&c,1);
		/* XXX this shouldn't be necessary: */
	if(cw->cursx == xxx && cw->cursy == xxy){
	    cw->wflags &= ~FLMAP_CURSUP;
	}
	cw->curx += rp->TxWidth;	/* keep things in sync */
	return;
    }
    
    if( cw->type == NHW_MAP )
    {
	curx = cw->curx - clipx;
	cury = cw->cury - clipy;

	/* See if we're out of glyph nodes */
	if(glyph_node_index >= NUMBER_GLYPH_NODES)
	    amiv_flush_glyph_buffer( w );

	/* Fill in the node. */
	amiv_g_nodes[glyph_node_index].dsty = min( w->BorderTop + (cury * mysize),
						w->Height - 1 );

#ifdef OPT_DISPMAP
	if(flags.fast_map){
#endif /* keni */
#ifdef	DISPMAP
	/* display_map() needs byte-aligned destinations, and we don't want to
	 * overwrite the window border.
	 */
	amiv_g_nodes[glyph_node_index].dstx =
				(w->BorderLeft + 8 + (curx * mxsize)) & -8;
#endif
#ifdef OPT_DISPMAP
} else {
#endif
#if !defined(DISPMAP) || defined(OPT_DISPMAP)
	amiv_g_nodes[glyph_node_index].dstx = min( w->BorderLeft + (curx * mxsize),
						w->Width - 1 );
#endif
#ifdef OPT_DISPMAP
}
#endif
	amiv_g_nodes[glyph_node_index].odsty = cw->cury;
	amiv_g_nodes[glyph_node_index].odstx = cw->curx;
	amiv_g_nodes[glyph_node_index].srcx = xoff;
	amiv_g_nodes[glyph_node_index].srcy = yoff;
	amiv_g_nodes[glyph_node_index].bitmap = ifftimg[ tbl ];
	++glyph_node_index;
    }
    else
    {
	/* Do it */
	register int j, k, x, y, apen;
	struct RastPort *rp = w->RPort;
	x = rp->cp_x - pictdata.xsize - 3;
#ifdef OPT_DISPMAP
	if(flags.fast_map){
#endif
#ifdef DISPMAP
		x &= -8;
		if(x==0) x = 8;
#endif
#ifdef OPT_DISPMAP
	}
#endif

	y = rp->cp_y - pictdata.ysize + 1;

	if( glyph != NO_GLYPH )
	{
	    struct BitMap *bm = ifftimg[ tbl ];

	    /* 8 bits per byte */
	    xoff /= 8;
	    yoff *= bm->BytesPerRow;
	    for( j = 0; j < pictdata.nplanes; ++j )
	    {
		for( k = 0; k < pictdata.ysize; ++k )
		{
		    memcpy( tile->Planes[ j ] + ( ( k * pictdata.ysize ) / 8 ),
			    bm->Planes[ j ] + xoff + yoff + ( bm->BytesPerRow * k ),
			    pictdata.ysize/8 );
		}
	    }

	    BltBitMapRastPort( tile, 0, 0,
		rp, x, y,
		pictdata.xsize, pictdata.ysize, 0xc0 );

	    apen = rp->FgPen;
	    SetAPen( rp, flags.amii_dripens[ SHINEPEN ] );
	    Move( rp, x-1, y + pictdata.ysize );
	    Draw( rp, x-1, y - 1 );
	    Draw( rp, x + pictdata.xsize, y - 1 );
	    SetAPen( rp, flags.amii_dripens[ SHADOWPEN ] );
	    Move( rp, x + pictdata.xsize, y );
	    Draw( rp, x + pictdata.xsize, y + pictdata.ysize );
	    Draw( rp, x, y + pictdata.ysize );
	    SetAPen( rp, apen );
	}
	else if( x > w->BorderLeft )
	{
	    int apen, bpen;
	    apen = rp->FgPen;
	    bpen = rp->BgPen;
	    SetAPen( rp, amii_menuBPen );
	    SetBPen( rp, amii_menuBPen );
	    RectFill( rp, x-1, y-1, x + pictdata.xsize, y + pictdata.ysize );
	    SetAPen( rp, apen );
	    SetBPen( rp, bpen );
	}
    }
}

/*
 * Define some variables which will be used to save context when toggling
 * back and forth between low level text and console I/O.
 */
static long xsave, ysave, modesave, apensave, bpensave;
static int usecolor;

/*
 * The function is called before any glyphs are driven to the screen.  It
 * removes the cursor, saves internal state of the window, then returns.
 */

void
amiv_start_glyphout(window)
    winid window;
{
    struct amii_WinDesc *cw;
    struct Window *w;

    if( ( cw=amii_wins[window] ) == (struct amii_WinDesc *)NULL )
	panic( "bad winid %d in start_glyphout()", window );

    if( cw->wflags & FLMAP_INGLYPH )
	return;

    if( !(w = cw->win ) )
	panic( "bad winid %d, no window ptr set", window );

    /*
     * Save the context of the window
     */
    xsave = w->RPort->cp_x;
    ysave = w->RPort->cp_y;
    modesave = w->RPort->DrawMode;
    apensave = w->RPort->FgPen;
    bpensave = w->RPort->BgPen;

    /*
     * Set the mode, and be done with it
     */
    usecolor = iflags.use_color;
    iflags.use_color = FALSE;
    cw->wflags |= FLMAP_INGLYPH;
}

/*
 * General cleanup routine -- flushes and restores cursor
 */
void
amii_end_glyphout(window)
    winid window;
{
    struct amii_WinDesc *cw;
    struct Window *w;

    if( ( cw = amii_wins[ window ] ) == (struct amii_WinDesc *)NULL )
	panic("bad window id %d in amii_end_glyphout()", window );

    if( ( cw->wflags & FLMAP_INGLYPH ) == 0 )
	return;
    cw->wflags &= ~(FLMAP_INGLYPH);

    if( !(w = cw->win ) )
	panic( "bad winid %d, no window ptr set", window );

    /*
     * Clean up whatever is left in the buffer
     */
    iflags.use_color = usecolor;

    /*
     * Reset internal data structs
     */
    SetAPen(w->RPort, apensave);
    SetBPen(w->RPort, bpensave);
    SetDrMd(w->RPort, modesave);

    Move(w->RPort, xsave, ysave);
}

static maze_type=COL_MAZE_BRICK;

void SetMazeType(MazeType t)
{
    maze_type=t;
}

int GlyphToIcon(int glyph)
{
    if(glyph>10000)return glyph;
    return( glyph2tile[glyph] );
}
#endif

#ifdef AMII_GRAPHICS
# ifdef TESTING
/*
 * Define some stuff for our special glyph drawing routines
 */
static unsigned short glyph_node_index, glyph_buffer_index;
#  define NUMBER_GLYPH_NODES  80
#  define GLYPH_BUFFER_SIZE   512
# endif /* TESTING */

struct amii_glyph_node {
    short	x;
    short	y;
    short	len;
    unsigned char   bg_color;
    unsigned char   fg_color;
    char	*buffer;
};
static struct amii_glyph_node amii_g_nodes[NUMBER_GLYPH_NODES];
static char amii_glyph_buffer[GLYPH_BUFFER_SIZE];

#ifdef TEXTCOLOR
/*
 * Map our amiga-specific colormap into the colormap specified in color.h.
 * See winami.c for the amiga specific colormap.
 */

int foreg[AMII_MAXCOLORS] = { 0, 7, 4, 2, 6, 5, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0 };
int backg[AMII_MAXCOLORS] = { 1, 0, 0, 0, 0, 0, 0, 0, 0, 7, 4, 1, 6, 5, 3, 1 };
#if 0
	#define CLR_BLACK	0
	#define CLR_RED		1
	#define CLR_GREEN	2
	#define CLR_BROWN	3	/* on IBM, low-intensity yellow is brown */
	#define CLR_BLUE	4
	#define CLR_MAGENTA 	5
	#define CLR_CYAN	6
	#define CLR_GRAY	7	/* low-intensity white */
	#define NO_COLOR	8
	#define CLR_ORANGE	9
	#define CLR_BRIGHT_GREEN 10
	#define CLR_YELLOW	11
	#define CLR_BRIGHT_BLUE	12
	#define CLR_BRIGHT_MAGENTA 13
	#define CLR_BRIGHT_CYAN	14
	#define CLR_WHITE	15
	#define CLR_MAX		16
#endif
#endif

#ifndef TESTING
/*
 * Begin Revamped Text display routines
 *
 * Up until version 3.1, the only method for displaying text on the playing
 * field was by using the console.device.  This was nice for a number of
 * reasons, the most signifigant of which was a lot of the nuts and bolts was
 * done for you via escape sequences interpreted by said device.  This did
 * not come without a price however.  And that price was speed. It has now
 * come to a point where the speed has now been deemed unacceptable.
 *
 * The following series of routines are designed to drop into the current
 * nethack display code, using hooks provided for such a measure. It works
 * on similar principals as the WindowPuts(), buffering I/O internally
 * until either an explicit flush or internal buffering is exceeded, thereby
 * forcing the flush.  The output (or glyphs) does not go to the
 * console.device, however.  It is driven directly to the rasterport of the
 * nethack window via the low-level Text() calls, increasing the speed by
 * a very signifigant factor.
 */
/*
 * Routine to simply flush whatever is buffered
 */
void
amii_flush_glyph_buffer( w )
    struct Window *w;
{
    short i, x, y;
    register struct RastPort *rp = w->RPort;

    /* If nothing is buffered, return before we do anything */
    if(glyph_node_index == 0)
	return;

    cursor_off( WIN_MAP );
    amii_start_glyphout( WIN_MAP );

    /* Set up the drawing mode */
    SetDrMd( rp, JAM2);

    /* Go ahead and start dumping the stuff */
    for(i=0; i<glyph_node_index; ++i) {
	/* These coordinate calculations must be synced with the
	 * code in amii_curs() in winfuncs.c.  curs_on_u() calls amii_curs()
	 * to draw the cursor on top of the player
	 */
	y = w->BorderTop + (amii_g_nodes[i].y-2) * rp->TxHeight +
	    rp->TxBaseline + 1;
	x = amii_g_nodes[i].x * rp->TxWidth + w->BorderLeft;

	/* Move pens to correct location */
	Move( rp, (long)x, (long)y);

	/* Setup the colors */
	SetAPen( rp, (long)amii_g_nodes[i].fg_color);
	SetBPen( rp, (long)amii_g_nodes[i].bg_color);

	/* Do it */
	Text( rp, amii_g_nodes[i].buffer, amii_g_nodes[i].len);
    }

    amii_end_glyphout( WIN_MAP );
    /* Clean up */
    glyph_node_index = glyph_buffer_index = 0;
}
void
amiga_print_glyph(window,color_index, glyph)
    winid window;
    int color_index, glyph;
{
    if( WINVERS_AMIV )
	amiv_lprint_glyph(window,color_index, glyph);
    else
	amii_lprint_glyph(window,color_index, glyph);
}

/*
 * Glyph buffering routine.  Called instead of WindowPuts().
 */
void
amii_lprint_glyph(window,color_index, glyph)
    winid window;
    int color_index, glyph;
{
    int fg_color, bg_color;
    struct amii_WinDesc *cw;
    struct Window *w;
    int curx;
    int cury;

    if( ( cw=amii_wins[window] ) == (struct amii_WinDesc *)NULL )
	panic("bad winid in amii_lprint_glyph: %d", window );

    w = cw->win;
    curx=cw->curx;
    cury=cw->cury;

#ifdef TEXTCOLOR
    fg_color = foreg[color_index];
    bg_color = backg[color_index];
#else
    fg_color = 1;
    bg_color = 0;
#endif /* TEXTCOLOR */

    /* See if we have enough character buffer space... */
    if(glyph_buffer_index  >= GLYPH_BUFFER_SIZE)
	amii_flush_glyph_buffer( w );

    /*
     * See if we can append it to the current active node of glyph buffer. It
     * must satisfy the following conditions:
     *
     *    * background colors are the same, AND
     *    * foreground colors are the same, AND
     *    * they are precisely side by side
     */
    if((glyph_buffer_index != 0) &&
       (fg_color == amii_g_nodes[glyph_node_index-1].fg_color) &&
       (bg_color == amii_g_nodes[glyph_node_index-1].bg_color) &&
       (amii_g_nodes[glyph_node_index-1].x+
	amii_g_nodes[glyph_node_index-1].len == curx) &&
       (amii_g_nodes[glyph_node_index-1].y == cury)) {
	/*
	 * Add it to the end of the buffer
	 */
	amii_glyph_buffer[glyph_buffer_index++] = glyph;
	amii_g_nodes[glyph_node_index-1].len ++;
     } else {
	/* See if we're out of glyph nodes */
	if(glyph_node_index >= NUMBER_GLYPH_NODES)
	    amii_flush_glyph_buffer( w );
	amii_g_nodes[glyph_node_index].len = 1;
	amii_g_nodes[glyph_node_index].x = curx;
	amii_g_nodes[glyph_node_index].y = cury;
	amii_g_nodes[glyph_node_index].fg_color = fg_color;
	amii_g_nodes[glyph_node_index].bg_color = bg_color;
	amii_g_nodes[glyph_node_index].buffer = &amii_glyph_buffer[glyph_buffer_index];
	amii_glyph_buffer[glyph_buffer_index] = glyph;
	++glyph_buffer_index;
	++glyph_node_index;
    }
}
#endif /* !TESTING */

#ifdef TESTING
/*
 * Define some variables which will be used to save context when toggling
 * back and forth between low level text and console I/O.
 */
static long xsave, ysave, modesave, apensave, bpensave;
static int usecolor;
#endif /* TESTING */

#ifndef TESTING
/*
 * The function is called before any glyphs are driven to the screen.  It
 * removes the cursor, saves internal state of the window, then returns.
 */

void
amii_start_glyphout(window)
    winid window;
{
    struct amii_WinDesc *cw;
    struct Window *w;

    if( ( cw=amii_wins[window] ) == (struct amii_WinDesc *)NULL )
	panic( "bad winid %d in start_glyphout()", window );

    if( cw->wflags & FLMAP_INGLYPH )
	return;

    if( !(w = cw->win ) )
	panic( "bad winid %d, no window ptr set", window );

    /*
     * Save the context of the window
     */
    xsave = w->RPort->cp_x;
    ysave = w->RPort->cp_y;
    modesave = w->RPort->DrawMode;
    apensave = w->RPort->FgPen;
    bpensave = w->RPort->BgPen;

    /*
     * Set the mode, and be done with it
     */
    usecolor = iflags.use_color;
    iflags.use_color = FALSE;
    cw->wflags |= FLMAP_INGLYPH;
}
#endif /* !TESTING */

# if 0
/*
 * General cleanup routine -- flushes and restores cursor
 */
void
amii_end_glyphout(window)
    winid window;
{
    struct amii_WinDesc *cw;
    struct Window *w;

    if( ( cw = amii_wins[ window ] ) == (struct amii_WinDesc *)NULL )
	panic("bad window id %d in amii_end_glyphout()", window );

    if( ( cw->wflags & FLMAP_INGLYPH ) == 0 )
	return;
    cw->wflags &= ~(FLMAP_INGLYPH);

    if( !(w = cw->win ) )
	panic( "bad winid %d, no window ptr set", window );

    /*
     * Clean up whatever is left in the buffer
     */
    iflags.use_color = usecolor;

    /*
     * Reset internal data structs
     */
    SetAPen(w->RPort, apensave);
    SetBPen(w->RPort, bpensave);
    SetDrMd(w->RPort, modesave);

    Move(w->RPort, xsave, ysave);
}
# endif
#endif

#ifndef TESTING
# ifdef OPT_DISPMAP
/* don't use dispmap unless x & y are 8,16,24,32,48 and equal */
void
dispmap_sanity(){
	if(
	    mxsize != mysize ||
	    dispmap_sanity1(mxsize) ||
	    dispmap_sanity1(mysize)){
		flags.fast_map = 0;
	}
}
int
dispmap_sanity1(x)
	int x;
{
	static unsigned char valid[] = {8,16,24,32,48,0};
	return !!strchr(valid,x);
}
# endif /* OPT_DISPMAP */
#endif /* TESTING */
