[comp.sources.amiga] v02i068: nethack - D&D-like game

page@swan.ulowell.edu (Bob Page) (12/02/88)

Submitted-by: u211344@hnykun11.bitnet (Olaf 'Rhialto' Seibert)
Posting-number: Volume 2, Issue 68
Archive-name: fun/nethack.a1

[The Amiga additional files plus the diffs, when applied to the
original nethack source, should produce an Amiga executable version of
nethack.  ..Bob]

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#	Run the following text with /bin/sh to create:
#	amigaDos.c
#	amigaMenu.c
#	amigaTTY.c
#	amigaTermcap.c
#	amigaUnix.c
#	amigaWindow.c
# This archive created: Thu Dec  1 17:50:06 1988
cat << \SHAR_EOF > amigaDos.c
/*    SCCS Id: @(#)amigaDos.c msdos.c - Amiga version   2.3    88/07/24
/* An assortment of MSDOS functions.
 */

#include <stdio.h>
#include "hack.h"

#undef TRUE
#undef FALSE
#undef COUNT

#include <libraries/dos.h>

extern char Initialized;

struct FileLock *Lock(), *CurrentDir(); /* Cheating - BCPL pointers */
struct FileHandle *Open();              /* Cheating - BCPL pointer */
long Read(), Write(), IoErr(), AvailMem();
void *malloc();
char *rindex(), *index();

int Enable_Abort = 0;   /* for stdio package */

/* Initial path, so we can find NetHack.cnf */

char PATH[PATHLEN] = "Ram:;df0:;NetHack:";

void
flushout()
{
    (void) fflush(stdout);
}

getuid() {
    return 1;
}

/*
 *  Actually make up a process id.
 *  Makes sure one can mess less with saved levels...
 */

int getpid()
{
    static short pid;

    while (pid == 0) {
        struct DateStamp dateStamp;
        pid = rnd(30000);
        pid += DateStamp(&dateStamp);    /* More or less random */
        pid ^= (short) (dateStamp.ds_Days >> 16) ^
               (short) (dateStamp.ds_Days)       ^
               (short) (dateStamp.ds_Minute)     +
               (short) (dateStamp.ds_Tick);
        pid %= 30000;
    }

    return pid;
}

char *
getlogin() {
    return ((char *) NULL);
}

int
abs(x)
int x;
{
    return x < 0? -x: x;
}

#ifdef REDO
tgetch() {
    char ch, popch();

    if (!(ch = popch())) {
        ch = WindowGetchar();
    }
    return ((ch == '\r') ? '\n' : ch);
}
#else /* REDO /**/
tgetch() {
    char ch;

    ch = WindowGetchar();
    return ((ch == '\r') ? '\n' : ch);
}
#endif /* REDO /**/


#ifdef DGK
# include <ctype.h>
# include <fcntl.h>

# define Sprintf (void) sprintf

# ifdef SHELL
dosh()
{
    pline("No mysterious force prevented you from using multitasking.");
    return 0;
}
# endif /* SHELL */


/* construct the string  file.level */
void
name_file(file, level)
char *file;
int level;
{
    char *tf;

    if (tf = rindex(file, '.'))
        Sprintf(tf+1, "%d", level);
}

#define ID_DOS1_DISK    'DOS\1'

/*
 *  This routine uses an approximation of the free bytes on a disk.
 *  How large a file you can actually write depends on the number of
 *  extension blocks you need for it.
 *  An iterative approach is used that (hopefully) mimics the AmigaDOG
 *  (yuck, tripos) block allocation sequence used when a file grows.
 */
long
freediskspace(path)
char *path;
{
    register long freeBytes = 0;
    register struct InfoData *infoData; /* Remember... longword aligned */
    char fileName[32];

    /*
     *  Find a valid path on the device of which we want the free space.
     *  If there is a colon in the name, it is an absolute path
     *  and all up to the colon is everything we need.
     *  Remember slashes in a volume name are allowed!
     *  If there is no colon, it is relative to the current directory,
     *  so must be on the current device, so "" is enough...
     */
    {
        register char *colon;

        strncpy(fileName, path, sizeof(fileName)-1);
        fileName[31] = 0;
        if (colon = index(fileName, ':'))
            colon[1] = '\0';
        else
            fileName[0] = '\0';
    }

    if (infoData = malloc(sizeof(*infoData))) {
        struct FileLock *fileLock;  /* Cheating */
        if (fileLock = Lock(fileName, SHARED_LOCK)) {
            if (Info(fileLock, infoData)) {
                /* We got a kind of DOS volume, since we can Lock it. */
                /* Calculate number of blocks available for new file */
                /* Kludge for the ever-full VOID: (oops RAM:) device */
                if (infoData->id_UnitNumber == -1 &&
                    infoData->id_NumBlocks == infoData->id_NumBlocksUsed) {
                    freeBytes = AvailMem(0L) - 48*1024;
                    /* Just a stupid guess at the */
                    /* Ram-Handler overhead per block: */
                    freeBytes -= freeBytes/16;
                } else {
                    /* Normal kind of DOS file system device/volume */
                    register long fileBlocks;
                    register long extensionPointers;
                    freeBytes = infoData->id_NumBlocks -
                                infoData->id_NumBlocksUsed -
                                1;  /* for file header */
                    if (infoData->id_DiskType == ID_DOS_DISK)
                        /* BytesPerBlock is 488 on floppies */
                        extensionPointers = (infoData->id_BytesPerBlock-200)/4;
                    else
                        /* Presumably it is 512 on ID_DOS1_DISK. */
                        extensionPointers = (infoData->id_BytesPerBlock-224)/4;
                    /* Number of blocks left to simulate */
                    fileBlocks = freeBytes;
                    /* Need another extension block? */
                    while (fileBlocks > extensionPointers) {
                        /* Get an extension block. One block less free. */
                        freeBytes--;
                        fileBlocks--;
                        /* to accomodate 72 extra data blocks */
                        fileBlocks -= extensionPointers;
                    }
                    freeBytes *= infoData->id_BytesPerBlock;
                }
                if (freeBytes < 0)
                    freeBytes = 0;
            }
            UnLock(fileLock);
        }
        free(infoData);
    }
    return freeBytes;
}


long
filesize(file)
char *file;
{
    register struct FileLock *fileLock;
    register struct FileInfoBlock *fileInfoBlock;
    register long size = 0;

    if (fileInfoBlock = malloc(sizeof(*fileInfoBlock))) {
        if (fileLock = Lock(file, SHARED_LOCK)) {
            if (Examine(fileLock, fileInfoBlock)) {
                size = fileInfoBlock->fib_Size;
            }
            UnLock(fileLock);
        }
        free(fileInfoBlock);
    }
    return size;
}

/*
 *  On the Amiga, looking if a specific file exists is much faster
 *  than sequentially reading a directory.
 */

void
eraseall(path, files)
char *path, *files;
{
    char buf[FILENAME];
    short i;
    struct FileLock *fileLock, *dirLock;

    if (dirLock = Lock(path)) {
        dirLock = CurrentDir(dirLock);

        strcpy(buf, files);
        for (i = 0; i < MAXLEVEL; i++) {
            name_file(buf, i);
            if (fileLock = Lock(buf, SHARED_LOCK)) {
                UnLock(fileLock);
                DeleteFile(buf);
            }
        }

        UnLock(CurrentDir(dirLock));
    }
}

/* This size makes that most files can be copied with two Read()/Write()s */

#define COPYSIZE    4096

char *CopyFile(from, to)
char *from, *to;
{
    register struct FileHandle *fromFile, *toFile;
    register char *buffer;
    register long size;
    char *error = NULL;

    if (buffer = malloc(COPYSIZE)) {
        if (fromFile = Open(from, MODE_OLDFILE)) {
            if (toFile = Open(to, MODE_NEWFILE)) {
                while (size = Read(fromFile, buffer, (long)COPYSIZE)) {
                    if (size != Write(toFile, buffer, size)) {
                        error = "Write error";
                        break;
                    }
                }
                Close(toFile);
            } else /* Can't open destination file */
                error = "Cannot open destination";
            Close(fromFile);
        } else /* Cannot open source file. Should not happen. */
            error = "Huh?? Cannot open source??";
        free(buffer);
        return error;
    } else /* Cannot obtain buffer for copying */
        return "No Memory !";
}

void
copybones(mode)
int mode;
{
    struct FileLock *fileLock;
    char from[FILENAME], to[FILENAME];
    char *frompath, *topath, *status;
    short i;
    extern int saveprompt;

    if (!ramdisk)
        return;

    frompath = (mode != TOPERM) ? permbones : levels;
    topath = (mode == TOPERM) ? permbones : levels;

    /* Remove any bones files in `to' directory. */
    eraseall(topath, allbones);

    /* Copy `from' to `to' */
    strcpy(from, frompath);
    strcat(from, allbones);
    strcpy(to, topath);
    strcat(to, allbones);

    for (i = 1; i < MAXLEVEL; i++) {
        name_file(from, i);
        name_file(to, i);
        if (fileLock = Lock(from, SHARED_LOCK)) {
            UnLock(fileLock);
            if (status = CopyFile(from, to))
                goto failed;
        }
    }

    /*
     * The last file got there.  Remove the ramdisk bones files.
     */
    if (mode == TOPERM)
        eraseall(frompath, allbones);
    return;

    /* Last file didn't get there. */

failed:
    msmsg("Cannot copy `%s' to `%s'\n(%s)\n", from, to, status);

    if (mode == TOPERM) {
        msmsg("Bones will be left in `%s'\n",
            *frompath ? frompath : hackdir);
        return;
    } else {
        /* Remove all bones files on the RAMdisk */
        eraseall(levels, allbones);
        playwoRAMdisk();
    }
}

playwoRAMdisk()
{
    msmsg("Do you wish to play without a RAMdisk (y/n) ? ");

    /* Set ramdisk false *before* exit'ing (because msexit calls
     * copybones)
     */
    ramdisk = FALSE;
    if (getchar() != 'y') {
        settty("Be seeing you ...\n");
        exit(0);
    }
    set_lock_and_bones();
    return;
}

saveDiskPrompt(start)
{
    extern int saveprompt;
    char buf[BUFSIZ], *bp;
    struct FileLock *fileLock;

    if (saveprompt) {
        /* Don't prompt if you can find the save file */
        if (fileLock = Lock(SAVEF, SHARED_LOCK)) {
            UnLock(fileLock);
            return 1;
        }
        remember_topl();
        home();
        cl_end();
        msmsg("If save file is on a SAVE disk, put that disk in now.\n");
        cl_end();
        msmsg("File name (default `%s'%s) ? ", SAVEF,
            start ? "" : ", <Esc> cancels save");
        getlin(buf);
        home();
        cl_end();
        curs(1, 2);
        cl_end();
        if (!start && *buf == '\033')
            return 0;

        /* Strip any whitespace. Also, if nothing was entered except
         * whitespace, do not change the value of SAVEF.
         */
        for (bp = buf; *bp; bp++)
            if (!isspace(*bp)) {
                strncpy(SAVEF, bp, PATHLEN);
                break;
            }
    }
    return 1;
}

/* Return 1 if the record file was found */
static boolean
record_exists()
{
    FILE *file;

    if (file = fopenp(RECORD, "r")) {
        fclose(file);
        return TRUE;
    }
    return FALSE;
}

/* Prompt for game disk, then check for record file.
 */
void
gameDiskPrompt()
{
    extern int saveprompt;

    if (record_exists())
        return;

    if (saveprompt) {
        (void) putchar('\n');
        getreturn("when the GAME disk has been put in");
    }

    if (!record_exists()) {
        msmsg("\n\nWARNING: can't find record file `%s'!\n", RECORD);

        msmsg("If the GAME disk is not in, put it in now.\n");
        getreturn("to continue");
    }
}

/* Read configuration */
void
read_config_file()
{
    char    tmp_ramdisk[PATHLEN], tmp_levels[PATHLEN];
    char    buf[BUFSZ], *bufp;
    FILE    *fp, *fopenp();
    extern  char plname[];
    extern  int saveprompt;

    tmp_ramdisk[0] = 0;
    tmp_levels[0] = 0;
    if ((fp = fopenp(configfile, "r")) == NULL) {
        msmsg("Warning: no configuration file!\n");
        getreturn("to continue");
        return;
    }
    while (fgets(buf, BUFSZ, fp)) {
        if (*buf == '#')
            continue;

        /* remove trailing whitespace */

        bufp = index(buf, '\n');
        while (bufp > buf && isspace(*bufp))
            bufp--;
        if (bufp == buf)
            continue;        /* skip all-blank lines */
        else
            *(bufp + 1) = 0;    /* 0 terminate line */

        /* find the '=' */
        if (!(bufp = index(buf, '='))) {
            msmsg("Bad option line: '%s'\n", buf);
            getreturn("to continue");
            continue;
        }

        /* skip  whitespace between '=' and value */
        while (isspace(*++bufp))
            ;

        /* Go through possible variables */
        if (!strncmp(buf, "HACKDIR", 4)) {
            strncpy(hackdir, bufp, PATHLEN);

        } else if (!strncmp(buf, "RAMDISK", 3)) {
            strncpy(tmp_ramdisk, bufp, PATHLEN);

        } else if (!strncmp(buf, "LEVELS", 4)) {
            strncpy(tmp_levels, bufp, PATHLEN);

        } else if (!strncmp(buf, "OPTIONS", 4)) {
            parseoptions(bufp, TRUE);
            if (plname[0])        /* If a name was given */
                plnamesuffix();    /* set the character class */

        } else if (!strncmp(buf, "SAVE", 4)) {
            char *ptr;
            if (ptr = index(bufp, ';')) {
                *ptr = '\0';
                if (*(ptr+1) == 'n' || *(ptr+1) == 'N')
                    saveprompt = FALSE;
            }
            (void) strncpy(SAVEF, bufp, PATHLEN);
            append_slash(SAVEF);
#ifdef GRAPHICS
        } else if (!strncmp(buf, "GRAPHICS", 4)) {
            unsigned int translate[MAXPCHARS];
            int  i;

            if ((i = sscanf(bufp, "%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d",
                &translate[ 0], &translate[ 1], &translate[ 2],
                &translate[ 3], &translate[ 4], &translate[ 5],
                &translate[ 6], &translate[ 7], &translate[ 8],
                &translate[ 9], &translate[10], &translate[11],
                &translate[12], &translate[13], &translate[14],
                &translate[15], &translate[16], &translate[17])) < 0) {
                    msmsg ("Syntax error in GRAPHICS\n");
                    getreturn("to continue");
            } /* Yuck! Worked only with low-byte first!!! */
/*
 * You could have problems here if you configure FOUNTAINS, SPIDERS or NEWCLASS
 * in or out and forget to change the tail entries in your graphics string.
 */
#define SETPCHAR(f, n)    showsyms.f = (i > n) ? translate[n] : defsyms.f
            SETPCHAR(stone, 0);
            SETPCHAR(vwall, 1);
            SETPCHAR(hwall, 2);
            SETPCHAR(tlcorn, 3);
            SETPCHAR(trcorn, 4);
            SETPCHAR(blcorn, 5);
            SETPCHAR(brcorn, 6);
            SETPCHAR(door, 7);
            SETPCHAR(room, 8);
            SETPCHAR(corr, 9);
            SETPCHAR(upstair, 10);
            SETPCHAR(dnstair, 11);
            SETPCHAR(trap, 12);
#ifdef FOUNTAINS
            SETPCHAR(pool, 13);
            SETPCHAR(fountain, 14);
#endif
#ifdef NEWCLASS
            SETPCHAR(throne, 15);
#endif
#ifdef SPIDERS
            SETPCHAR(web, 16);
#endif
#ifdef SINKS
            SETPCHAR(sink, 17);
#endif
#undef SETPCHAR
#endif /* GRAPHICS */
        } else if (!strncmp(buf, "PATH", 4)) {
            strncpy(PATH, bufp, PATHLEN);

        } else {
            msmsg("Bad option line: '%s'\n", buf);
            getreturn("to continue");
        }
    }
    fclose(fp);

    strcpy(permbones, tmp_levels);
    if (tmp_ramdisk[0]) {
        strcpy(levels, tmp_ramdisk);
        if (strcmpi(permbones, levels))        /* if not identical */
            ramdisk = TRUE;
    } else
        strcpy(levels, tmp_levels);
    strcpy(bones, levels);
}

/* Set names for bones[] and lock[] */

void
set_lock_and_bones()
{
    if (!ramdisk) {
        strcpy(levels, permbones);
        strcpy(bones, permbones);
    }
    append_slash(permbones);
    append_slash(levels);
    append_slash(bones);
    strcat(bones, allbones);
    strcpy(lock, levels);
    strcat(lock, alllevels);
}

/*
 * Add a slash to any name not ending in / or :.  There must
 * be room for the /.
 */
void
append_slash(name)
char *name;
{
    char *ptr;

    if (!*name)
        return;
    ptr = name + (strlen(name) - 1);
    if (*ptr != '/' && *ptr != ':') {
        *++ptr = '/';
        *++ptr = '\0';
    }
}


void
getreturn(str)
char *str;
{
    int ch;

    msmsg("Hit <RETURN> %s.", str);
    while ((ch = getchar()) != '\n')
        ;
}

void
msmsg(fmt, a1, a2, a3)
char *fmt;
long a1, a2, a3;
{
    printf(fmt, a1, a2, a3);
    (void) fflush(stdout);
}

/* Follow the PATH, trying to fopen the file.
 */
#define PATHSEP    ';'
#undef fopen

FILE *
fopenp(name, mode)
register char *name, *mode;
{
    char buf[BUFSIZ], *bp, *pp, lastch;
    FILE *fp;
    register struct FileLock *theLock;

    /* Try the default directory first.  Then look along PATH.
     */
    strcpy(buf, name);
    if (theLock = Lock(buf, SHARED_LOCK)) {
        UnLock(theLock);
        if (fp = fopen(buf, mode))
            return fp;
    }
    pp = PATH;
    while (pp && *pp) {
        bp = buf;
        while (*pp && *pp != PATHSEP)
            lastch = *bp++ = *pp++;
        if (lastch != ':' && lastch != '/' && bp != buf)
            *bp++ = '/';
        strcpy(bp, name);
        if (theLock = Lock(buf, SHARED_LOCK)) {
            UnLock(theLock);
            if (fp = fopen(buf, mode))
                return fp;
        }
        if (*pp)
            pp++;
    }
    return NULL;
}
#endif /* DGK */

#ifdef CHDIR

/*
 *  A not general-purpose directory changing routine.
 *  Assumes you want to return to the original directory eventually,
 *  by chdir()ing to orgdir.
 *  Assumes -1 is not a valid lock, since 0 is valid.
 */

#define NO_LOCK     ((struct FileLock *) -1)

char orgdir[1];
static struct FileLock *OrgDirLock = NO_LOCK;

chdir(dir)
char *dir;
{
    if (dir == orgdir) {
        /* We want to go back to where we came from. */
        if (OrgDirLock != NO_LOCK) {
            UnLock(CurrentDir(OrgDirLock));
            OrgDirLock = NO_LOCK;
        }
    } else {
        /*
         * Go to some new place. If still at the original
         * directory, save the FileLock.
         */
        struct FileLock *newDir;

        if (newDir = Lock(dir, SHARED_LOCK)) {
            if (OrgDirLock == NO_LOCK) {
                OrgDirLock = CurrentDir(newDir);
            } else {
                UnLock(CurrentDir(newDir));
            }
        } else {
            return -1;  /* Failed */
        }
    }
    /* CurrentDir always succeeds if you have a lock */
    return 0;
}

#endif

/* Chdir back to original directory
 */
#undef exit
void
msexit(code)
{
#ifdef CHDIR
    extern char orgdir[];
#endif

#ifdef DGK
    (void) fflush(stdout);
    if (ramdisk)
        copybones(TOPERM);
#endif
#ifdef CHDIR
    chdir(orgdir);      /* chdir, not chdirx */
#endif
    /* Never know when exit is called... */
    if (Initialized && (curx != 1 || cury != 1))
        getret();

    CleanUp();
    exit(code);
}

/*
 *  Strcmp while ignoring case. Not general-purpose, so static.
 */

static int strcmpi(a, b)
register char *a, *b;
{
    while (tolower(*a) == tolower(*b)) {
        if (!*a)        /* *a == *b, so at end of both strings */
            return 0;   /* equal. */
        a++;
        b++;
    }
    return 1;
}

/*
 *  cmpmem - used to compare two struct symbols, in lev.c
 */

cmpmem(a, b, size)
register unsigned char *a, *b;
register int size;
{
    while (size--) {
        if (*a++ != *b++)
            return 1;   /* not equal */
    }

    return 0;           /* equal */
}
SHAR_EOF
cat << \SHAR_EOF > amigaMenu.c
/*
 *  amigaMenu.c     (C) Copyright 1988 by Olaf Seibert (KosmoSoft)
 *
 *  Originally by John Toebes.
 */

#define TEXT(nam,str) \
static struct IntuiText nam = {0,1,JAM2,0,0,NULL,(UBYTE *)str,NULL}

        /* Commands */
        TEXT(T_HELP,  "?   display help");
        TEXT(T_o,     "O   set options");
        TEXT(T_SHELL, "!   AMIGADOS commands");
        TEXT(T_v,     "v   version number");
        TEXT(T_CR,    "^R  redraw screen");
        TEXT(T_CP,    "^P  repeat last message");
        TEXT(T_Q,     "Q   quit game");
        TEXT(T_S,     "S   save the game");

        /* Inventory */
        TEXT(T_i,     "i   inventory");
        TEXT(T_p,     "p   pay your bill");
        TEXT(T_d,     "d   drop an object");
        TEXT(T_D,     "D   Drop several things");
        TEXT(T_COMMA, ",   Pickup an object");
        TEXT(T_SLASH, "/   identify something");
        TEXT(T_c,     "c   call class of objects");
        TEXT(T_C,     "C   Christen a monster");

        /* Actions */
        TEXT(T_a,     "a   apply/use something");
        TEXT(T_e,     "e   eat something");
        TEXT(T_q,     "q   quaff a potion");
        TEXT(T_r,     "r   read a scroll");
        TEXT(T_t,     "t   throw/shoot weapon");
        TEXT(T_z,     "z   zap a wand");

        /* Preparations */
        TEXT(T_w,     "w   wield a weapon");
        TEXT(T_P,     "P   Put on ring");
        TEXT(T_R,     "R   Remove ring");
        TEXT(T_T,     "T   Take off armor");
        TEXT(T_W,     "W   Wear armor");
        TEXT(T_WPN,   ")   current weapon");
        TEXT(T_ARMOR, "[   current armor");
        TEXT(T_RING,  "=   current rings");

        /* Movement */
        TEXT(T_E,     "E   Engrave msg on floor");
        TEXT(T_s,     "s   search");
        TEXT(T_UP,    "<   Go up stairs");
        TEXT(T_DOWN,  ">   Go down stairs");
        TEXT(T_WAIT,  ".   wait a moment");
        TEXT(T_CT,    "^T  Teleport");

#define IFLAGS ITEMENABLED|ITEMTEXT|HIGHCOMP
#define IDATA(cmd,str,off) 0,off,WDT,9,IFLAGS,0,(APTR)&str,NULL,cmd,NULL,NULL

/* Commands */

#undef  WDT
#define WDT 184

static struct MenuItem cmdsub[] = {
    { &cmdsub[1], IDATA('?', T_HELP,   0) }, /*   display help */
    { &cmdsub[2], IDATA('O', T_o,     10) }, /*   set options */
    { &cmdsub[3], IDATA('!', T_SHELL, 20) }, /*   AMIGADOS commands */
    { &cmdsub[4], IDATA('v', T_v,     30) }, /*   version number */
    { &cmdsub[5], IDATA(022, T_CR,    40) }, /*R  redraw screen */
    { &cmdsub[6], IDATA(024 ,T_CP,    50) }, /*P  repeat last message */
    { &cmdsub[7], IDATA('Q', T_Q,     60) }, /*   quit game */
    { NULL,       IDATA('S', T_S,     70) }  /*   save the game */
};

/* Inventory */

#undef  WDT
#define WDT 200

static struct MenuItem invsub[] = {
    { &invsub[1], IDATA('i', T_i,      0) }, /*   inventory */
    { &invsub[2], IDATA('p', T_p,     10) }, /*   pay your bill */
    { &invsub[3], IDATA('d', T_d,     20) }, /*   drop an object */
    { &invsub[4], IDATA('D', T_D,     30) }, /*   Drop several things */
    { &invsub[5], IDATA(',', T_COMMA, 40) }, /*   Pickup an object */
    { &invsub[6], IDATA('/', T_SLASH, 50) }, /*   identify something */
    { &invsub[7], IDATA('c', T_c,     60) }, /*   call a class of objects */
    { NULL,       IDATA('C', T_C,     70) }  /*   Christen a monster */
};

/* Actions */

#undef  WDT
#define WDT 184

static struct MenuItem actsub[] = {
    { &actsub[1], IDATA('a', T_a,     0) }, /*   apply/use something */
    { &actsub[2], IDATA('e', T_e,    10) }, /*   eat something */
    { &actsub[3], IDATA('q', T_q,    20) }, /*   quaff a potion */
    { &actsub[4], IDATA('r', T_r,    30) }, /*   read a scroll */
    { &actsub[5], IDATA('t', T_t,    40) }, /*   throw/shoot weapon */
    { NULL,       IDATA('z', T_z,    50) }  /*   zap a wand */
};

/* Preparations */

#undef  WDT
#define WDT 144

static struct MenuItem armsub[] = {
    { &armsub[1], IDATA('w', T_w,      0) }, /*   wield a weapon */
    { &armsub[2], IDATA('P', T_P,     10) }, /*   Put on ring */
    { &armsub[3], IDATA('R', T_R,     20) }, /*   Remove ring */
    { &armsub[4], IDATA('T', T_T,     30) }, /*   Take off armor */
    { &armsub[5], IDATA('W', T_W,     40) }, /*   Wear armor */
    { &armsub[6], IDATA(')', T_WPN,   50) }, /*   current weapon */
    { &armsub[7], IDATA('[', T_ARMOR, 60) }, /*   current armor */
    { NULL,       IDATA('=', T_RING,  70) }  /*   current rings */
};

/* Movement */

#undef  WDT
#define WDT 192

static struct MenuItem movsub[] = {
    { &movsub[1], IDATA('E', T_E,     0) }, /*   Engrave msg on floor */
    { &movsub[2], IDATA('s', T_s,    10) }, /*   search */
    { &movsub[3], IDATA('<', T_UP,   20) }, /*   Go up stairs */
    { &movsub[4], IDATA('>', T_DOWN, 30) }, /*   Go down stairs */
    { &movsub[5], IDATA('.', T_WAIT, 40) }, /*   wait a moment */
    { NULL,       IDATA(024, T_CT,   50) }  /*T  Teleport */
};

/* Menustrip */

/* Width = #letters * 8 + 8 */

struct Menu HackMenu[] = {
   { &HackMenu[1], 10,0, 72,0,MENUENABLED,"Commands",     &cmdsub[0] }, /*8*/
   { &HackMenu[2], 82,0, 80,0,MENUENABLED,"Inventory",    &invsub[0] }, /*9*/
   { &HackMenu[3],162,0, 64,0,MENUENABLED,"Actions",      &actsub[0] }, /*7*/
   { &HackMenu[4],226,0,104,0,MENUENABLED,"Preparations", &armsub[0] }, /*12*/
   { NULL,        330,0, 72,0,MENUENABLED,"Movement",     &movsub[0] }  /*8*/
};

SHAR_EOF
cat << \SHAR_EOF > amigaTTY.c
/*    SCCS Id: @(#)amigaTTY.c    2.3    88/07/25
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* tty.c - (Amiga) version */


#include <stdio.h>
#include "hack.h"
#include "func_tab.h"

extern int Enable_Abort;

static char erase_char, kill_char;

/*
 * Get initial state of terminal, set ospeed (for termcap routines)
 * and switch off tab expansion if necessary.
 * Called by startup() in termcap.c and after returning from ! or ^Z
 */
gettty()
{
    erase_char = 127;       /* DEL */
    kill_char = 21;         /* cntl-U */
    flags.cbreak = TRUE;
    Enable_Abort = 0;
    curx = 1;
    cury = 1;
}

/* reset terminal to original state */
settty(s)
char *s;
{
    end_screen();
    if (s) {
        printf(s);
        curx = FAR;     /* For msexit(): don't exit immediately */
    }
    (void) fflush(stdout);
    /* Do not close the screen, that is done in msexit() */
}


/* fatal error */
/*VARARGS1*/
error(s,x,y)
char *s;
{
    end_screen();
    putchar('\n');
    printf(s,x,y);
    putchar('\n');
    abort(1);
}

/*
 * Read a line closed with '\n' into the array char bufp[BUFSZ].
 * (The '\n' is not stored. The string is closed with a '\0'.)
 * Reading can be interrupted by an escape ('\033') - now the
 * resulting string is "\033".
 */
getlin(bufp)
register char *bufp;
{
    register char *obufp = bufp;
    register int c;

    flags.toplin = 2;        /* nonempty, no --More-- required */
    for(;;) {
        (void) fflush(stdout);
        if((c = getchar()) == EOF) {
            *bufp = 0;
            return;
        }
        if(c == '\033') {
            *obufp = c;
            obufp[1] = 0;
            return;
        }
        if(c == erase_char || c == '\b') {
            if(bufp != obufp) {
                bufp--;
                putstr("\b \b"); /* putsym converts \b */
            } else    bell();
        } else if(c == '\n') {
            *bufp = 0;
            return;
        } else if(' ' <= c && c < '\177') {
                /* avoid isprint() - some people don't have it
                   ' ' is not always a printing char */
            *bufp = c;
            bufp[1] = 0;
            putstr(bufp);
            if(bufp-obufp < BUFSZ-1 && bufp-obufp < COLNO)
                bufp++;
        } else if(c == kill_char || c == '\177') { /* Robert Viduya */
                /* this test last - @ might be the kill_char */
            while(bufp != obufp) {
                bufp--;
                putstr("\b \b");
            }
        } else
            bell();
    }
}

getret()
{
    cgetret("");
}

cgetret(s)
register char *s;
{
    putsym('\n');
    if(flags.standout)
        standoutbeg();
    putstr("Hit ");
    putstr(flags.cbreak ? "space" : "return");
    putstr(" to continue: ");
    if(flags.standout)
        standoutend();
    xwaitforspace(s);
}

char morc;    /* tell the outside world what char he used */

xwaitforspace(s)
register char *s;    /* chars allowed besides space or return */
{
register int c;

    morc = 0;
    while((c = readchar()) != '\n') {
        if(flags.cbreak) {
            if(c == ' ') break;
            if(s && index(s,c)) {
                morc = c;
                break;
            }
            bell();
        }
    }
}

static int last_multi;

char *
parse()
{
    static char inline[COLNO];
    register foo;

    flags.move = 1;
    if(!Invisible) curs_on_u(); else home();
    multi = 0;
#ifdef DGK
    while((foo = readchar()) >= '0' && foo <= '9') {
        multi = 10*multi+foo-'0';
        if (multi < 0 || multi > LARGEST_INT)
            multi = LARGEST_INT;
        if (multi > 9) {
            remember_topl();
            home();
            cl_end();
            printf("Count: %d", multi);
        }
        last_multi = multi;
    }
# ifdef REDO
    if (foo == DOAGAIN || in_doagain)
        multi = last_multi;
    else {
        savech(0);    /* reset input queue */
        savech(foo);
    }
# endif

#else /* DGK */

    while((foo = readchar()) >= '0' && foo <= '9')
        multi = 10*multi+foo-'0';

#endif /* DGK */

    if(multi) {
        multi--;
        save_cm = inline;
    }
    inline[0] = foo;
    inline[1] = 0;
    if(foo == 'g' || foo == 'G'){
        inline[1] = getchar();
#ifdef REDO
        savech(inline[1]);
#endif
        inline[2] = 0;
    }
    if(foo == 'm' || foo == 'M'){
        inline[1] = getchar();
#ifdef REDO
        savech(inline[1]);
#endif
        inline[2] = 0;
    }
    clrlin();
    return(inline);
}

char
readchar() {
    register int sym;

    (void) fflush(stdout);
    sym = getchar();
    if(flags.toplin == 1)
        flags.toplin = 2;
    return((char) sym);
}
#ifdef COM_COMPL
/* Read in an extended command - doing command line completion for
 * when enough character have been entered to make a unique command.
 * This is just a modified getlin().   -jsb
 */
get_ext_cmd(bufp)
register char *bufp;
{
    register char *obufp = bufp;
    register int c;
    int com_index, index;

    flags.toplin = 2;        /* nonempty, no --More-- required */

    for(;;) {
        (void) fflush(stdout);
        if((c = readchar()) == EOF) {
            *bufp = 0;
            return;
        }
        if(c == '\033') {
            *obufp = c;
            obufp[1] = 0;
            return;
        }
        if(c == erase_char || c == '\b') {
            if(bufp != obufp) {
                bufp--;
                putstr("\b \b"); /* putsym converts \b */
            } else    bell();
        } else if(c == '\n') {
            *bufp = 0;
            return;
        } else if(' ' <= c && c < '\177') {
                /* avoid isprint() - some people don't have it
                   ' ' is not always a printing char */
            *bufp = c;
            bufp[1] = 0;
            index = 0;
            com_index = -1;

            while(extcmdlist[index].ef_txt != (char *) 0){
                if(!strncmp(obufp, extcmdlist[index].ef_txt,
                strlen(obufp)))
                    if(com_index == -1) /* No matches yet*/
                        com_index = index;
                    else /* More than 1 match */
                        com_index = -2;
                index++;
            }
            if(com_index >= 0){
                strcpy(obufp,
                extcmdlist[com_index].ef_txt);
                /* finish print our string */
                putstr(bufp);
                bufp = obufp; /* reset it */
                if(strlen(obufp) < BUFSIZ-1 &&
                 strlen(obufp) < COLNO)
                    /* set bufp at the end of our
                     * string
                     */
                    bufp += strlen(obufp);
            } else {
                putstr(bufp);
                if(bufp-obufp < BUFSZ-1 && bufp-obufp < COLNO)
                    bufp++;
            }
        } else if(c == kill_char || c == '\177') { /* Robert Viduya */
                /* this test last - @ might be the kill_char */
            while(bufp != obufp) {
                bufp--;
                putstr("\b \b");
            }
        } else
            bell();
    }

}
#endif COM_COMPL

SHAR_EOF
cat << \SHAR_EOF > amigaTermcap.c
/*    SCCS Id: @(#)termcap.c    2.1    87/10/19
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */

#include <stdio.h>
#include <ctype.h>    /* for isdigit() */
#include "hack.h"     /* for ROWNO, COLNO, *HI, *HE */

#define xputc(c)    WindowPutchar(c)
#define xputs(s)    WindowFPuts(s)

extern char *getenv();
extern long *alloc();

static char HO[] = "\x9BH";         /* Home */
static char CL[] = "\x1Bc";         /* Clear */
static char CE[] = "\x9BK";         /* Erase end of line */
static char UP[] = "\x0B";          /* Cursor up */
static char CM[] = "\x9B%d;%dH";    /* used with function tgoto() */
static char ND[] = "\x9BC";         /* Cursor right */
static char XD[] = "\x9BB";         /* Cursor down */
static char BC[] = "\x08";          /* Cursor left */

#ifdef MSDOSCOLOR    /* creps@silver.bacs.indiana.edu */
static char SO[] = "\x9B33m";       /* Standout: Color #3 (red) */
static char SE[] = "\x9B0m";

#else

static char SO[] = "\x9B7m";        /* Inverse video */
static char SE[] = "\x9B0m";
#endif

char *CD = "\x9BJ"; /* char *CD; tested in pri.c: docorner() */

/* #if defined(DGK) || defined(SORTING) */
#ifdef DGK
# define XXX
#endif
#ifdef SORTING
# define XXX
#endif

#ifdef XXX
# ifdef MSDOSCOLOR
char *HI_OBJ = "\x9B31;42m";        /* White on Black */
char *HI_MON = "\x9B33;42m";        /* Orange on black */
char *HI = "\x9B1m";                /* Bold (hilight) */
char *HE = "\x9B0m";                /* Plain */
# else
char *HI_OBJ = "";
char *HI_MON = "";
char *HI = "\x9B4m";                /* Underline */
char *HE = "\x9B0m";                /* Plain */
# endif
#endif
#undef XXX

int CO = COLNO;
int LI = ROWNO;         /* used in pri.c and whatis.c */

static char tgotobuf[20];
#define tgoto(fmt, x, y)    (sprintf(tgotobuf, fmt, y+1, x+1), tgotobuf)

startup()
{
    (void) Initialize();    /* This opens screen, window, console, &c */
}

start_screen()
{
}

end_screen()
{
    clear_screen();
}

/* Cursor movements */
extern xchar curx, cury;

#ifdef TERMCAP

curs(x, y)
register int x, y;    /* not xchar: perhaps xchar is unsigned and
               curx-x would be unsigned as well */
{

    if (y == cury && x == curx)
        return;
    if(!ND && (curx != x || x <= 3)) {    /* Extremely primitive */
        cmov(x, y);            /* bunker!wtm */
        return;
    }
    if(abs(cury-y) <= 3 && abs(curx-x) <= 3)
        nocmov(x, y);
    else if((x <= 3 && abs(cury-y)<= 3) || (!CM && x<abs(curx-x))) {
        (void) putchar('\r');
        curx = 1;
        nocmov(x, y);
    } else if(!CM) {
        nocmov(x, y);
    } else
        cmov(x, y);
}

nocmov(x, y)
{
    if (cury > y) {
        if(UP) {
            while (cury > y) {    /* Go up. */
                xputs(UP);
                cury--;
            }
        } else if(CM) {
            cmov(x, y);
        } else if(HO) {
            home();
            curs(x, y);
        } /* else impossible("..."); */
    } else if (cury < y) {
        if(XD) {
            while(cury < y) {
                xputs(XD);
                cury++;
            }
        } else if(CM) {
            cmov(x, y);
        } else {
            while(cury < y) {
                xputc('\n');
                curx = 1;
                cury++;
            }
        }
    }
    if (curx < x) {        /* Go to the right. */
        if(!ND) cmov(x, y); else    /* bah */
            /* should instead print what is there already */
        while (curx < x) {
            xputs(ND);
            curx++;
        }
    } else if (curx > x) {
        while (curx > x) {    /* Go to the left. */
            xputs(BC);
            curx--;
        }
    }
}

cmov(x, y)
register x, y;
{
    xputs(tgoto(CM, x-1, y-1));
    cury = y;
    curx = x;
}

#else /* !TERMCAP */

curs(x, y)
register x, y;
{
    if (x != curx || y != cury) {
        xputs(tgoto(CM, x-1, y-1));
        cury = y;
        curx = x;
    }
}
#endif /* TERMCAP */

#ifndef xputc
xputc(c) char c; {
    (void) putchar(c, stdout);
}
#endif

#ifndef xputs
xputs(s) char *s; {
    WindowFPuts(s);
}
#endif

cl_end() {
#ifdef TERMCAP
    if(CE)
        xputs(CE);
    else {    /* no-CE fix - free after Harold Rynes */
        /* this looks terrible, especially on a slow terminal
           but is better than nothing */
        register cx = curx, cy = cury;

        while(curx < COLNO) {
            xputc(' ');
            curx++;
        }
        curs(cx, cy);
    }
#else
    xputs(CE);
#endif
}

clear_screen() {
    xputs(CL);
    home();
}

home()
{
#ifdef TERMCAP
    if(HO)
        xputs(HO);
    else if(CM)
        xputs(tgoto(CM, 0, 0));
    else
        curs(1, 1);    /* using UP ... */
#else
    xputs(HO);
#endif
    curx = cury = 1;
}

standoutbeg()
{
#ifdef TERMCAP
    if(SO)
#endif
        xputs(SO);
}

standoutend()
{
#ifdef TERMCAP
    if(SE)
#endif
        xputs(SE);
}

backsp()
{
    xputs(BC);
    curx--;
}

bell()
{
#ifdef DGKMOD
    if (flags.silent) return;
#endif /* DGKMOD /**/
    (void) putchar('\007');        /* curx does not change */
    (void) fflush(stdout);
}

delay_output() {
    /* delay 50 ms - could also use a 'nap'-system call */
    Delay(2L);
}

cl_eos()            /* free after Robert Viduya */
{                /* must only be called with curx = 1 */
#ifdef TERMCAP
    if(CD)
        xputs(CD);
    else {
        register int cx = curx, cy = cury;
        while(cury <= LI-2) {
            cl_end();
            xputc('\n');
            curx = 1;
            cury++;
        }
        cl_end();
        curs(cx, cy);
    }
#else
    xputs(CD);
#endif
}

SHAR_EOF
cat << \SHAR_EOF > amigaUnix.c
/*    SCCS Id: @(#)pcunix.c    1.4    87/08/08
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* unix.c - version 1.0.3 */

/* This file collects some Unix dependencies; pager.c contains some more */

/*
 * The time is used for:
 *    - seed for rand()
 *    - year on tombstone and yymmdd in record file
 *    - phase of the moon (various monsters react to NEW_MOON or FULL_MOON)
 *    - night and midnight (the undead are dangerous at midnight)
 *    - determination of what files are "very old"
 */

#include <stdio.h>    /* mainly for NULL */
#include "hack.h"    /* mainly for index() which depends on BSD */

#include    <time.h>
#include    <stat.h>

extern time_t time();
static struct stat buf, hbuf;

setrandom()
{
    (void) srand((int) time ((time_t *) 0));
}

struct tm *
getlt()
{
    time_t date;
    struct tm *localtime();

    (void) time(&date);
    return(localtime(&date));
}

getyear()
{
    return(1900 + getlt()->tm_year);
}

char *
getdate()
{
    static char datestr[7];
    register struct tm *lt = getlt();

    (void) sprintf(datestr, "%2d%2d%2d",
        lt->tm_year, lt->tm_mon + 1, lt->tm_mday);
    if(datestr[2] == ' ') datestr[2] = '0';
    if(datestr[4] == ' ') datestr[4] = '0';
    return(datestr);
}

phase_of_the_moon()            /* 0-7, with 0: new, 4: full */
{                    /* moon period: 29.5306 days */
                    /* year: 365.2422 days */
    register struct tm *lt = getlt();
    register int epact, diy, golden;

    diy = lt->tm_yday;
    golden = (lt->tm_year % 19) + 1;
    epact = (11 * golden + 18) % 30;
    if ((epact == 25 && golden > 11) || epact == 24)
        epact++;

    return( (((((diy + epact) * 6) + 11) % 177) / 22) & 7 );
}

night()
{
    register int hour = getlt()->tm_hour;

    return(hour < 6 || hour > 21);
}

midnight()
{
    return(getlt()->tm_hour == 0);
}

gethdate(name) char *name; {
/* old version - for people short of space */
/*
/* register char *np;
/*      if(stat(name, &hbuf))
/*              error("Cannot get status of %s.",
/*                      (np = rindex(name, '/')) ? np+1 : name);
/*
/* version using PATH from: seismo!gregc@ucsf-cgl.ARPA (Greg Couch) */

/*
 * The problem with   #include  <sys/param.h> is that this include file
 * does not exist on all systems, and moreover, that it sometimes includes
 * <sys/types.h> again, so that the compiler sees these typedefs twice.
 */
#define         MAXPATHLEN      80

extern char PATH[];     /* In amigaDOS.c */

register char *np, *path;
char filename[MAXPATHLEN+1];

    if (index(name, '/') != NULL || (path = PATH) == NULL)
        path = "";

    for (;;) {
        if ((np = index(path, ':')) == NULL)
            np = path + strlen(path);       /* point to end str */
        if (np - path <= 1)                     /* %% */
            (void) strcpy(filename, name);
        else {
            (void) strncpy(filename, path, np - path);
            filename[np - path] = '/';
            (void) strcpy(filename + (np - path) + 1, name);
        }
        if (stat(filename, &hbuf) == 0)
            return;
        if (*np == '\0')
        path = "";
        path = np + 1;
    }
    error("Cannot get status of %s.", (np = rindex(name, '/')) ? np+1 : name);
}

uptodate(fd) {
#ifndef AMIGA   /* Amiga has no fstat */
    if(fstat(fd, &buf)) {
        pline("Cannot get status of saved level? ");
        return(0);
    }
    if(buf.st_mtime < hbuf.st_mtime) {
        pline("Saved level is out of date. ");
        return(0);
    }
#endif
    return(1);
}

regularize(s)    /* normalize file name - we don't like :'s or /'s */
register char *s;
{
    register char *lp;

    while((lp = index(s, ':')) || (lp = index(s, '/')))
        *lp = '_';
}
SHAR_EOF
cat << \SHAR_EOF > amigaWindow.c
/*
 *  amigaWindow.c   (C) Copyright 1988 by Olaf Seibert (KosmoSoft)
 */

/*
 *  Here is some very Amiga specific stuff, dealing with
 *  screens, windows and menus.
 */

#include "config.h"

#define HACKFONT

#undef TRUE
#undef FALSE
#undef COUNT

#include <exec/types.h>
#include <exec/alerts.h>
#include <exec/io.h>
#include <exec/devices.h>
#include <devices/console.h>
#include <intuition/intuition.h>
#include <libraries/dosextens.h>

#include "amigaMenu.c"

/*  First, external declarations... */

struct Library *OpenLibrary();
struct Screen *OpenScreen();
struct Window *OpenWindow();
struct TextFont *OpenDiskFont();
struct IntuiMessage *GetMsg();
struct MenuItem *ItemAddress();
struct Process *FindTask();         /* Cheating */
long DeadKeyConvert(), OpenDevice(), CloseDevice();
extern struct Library *IconBase;

/*  Now our own variables */

struct Library *IntuitionBase;
struct Screen *HackScreen;
struct Window *HackWindow;
struct Window *pr_WindowPtr;
struct IOStdReq ConsoleIO;
char Initialized = FALSE;

#ifdef HACKFONT
struct Library *GfxBase;
struct Library *DiskfontBase;
#endif

struct Device *ConsoleDevice;

#define CSI         '\x9b'
#define NO_CHAR     -1
#define RAWHELP     0x5F        /* Rawkey code of the HELP key */

/*
 *  It is assumed that all multiple-character outputs are
 *  at most CONBUFFER characters each.
 */

#define CONBUFFER   BUFSZ               /* 512 */
static char ConsoleBuffer[CONBUFFER];
static unsigned short Buffered;

#define KBDBUFFER   10
static unsigned char KbdBuffer[KBDBUFFER];
static unsigned char KbdBuffered;

#define BufferQueueChar(ch) (KbdBuffer[KbdBuffered++] = ch)

/*
 *  It seems Intuition won't OpenDiskFont our diskFont, so we get the
 *  closest match, which is of course topaz/8. (and if not, it is still
 *  an 8-pixel font, so everything still looks ok)
 */

struct TextAttr Hack80 = {
#ifdef HACKFONT
    (UBYTE *) "NetHack:Hack.font",
#else
    (UBYTE *) "topaz.font",
#endif
    TOPAZ_EIGHTY, FS_NORMAL, FPF_DISKFONT
};

#ifdef HACKFONT
struct TextFont *HackFont;
#endif

#define BARHEIGHT       11
#define WINDOWHEIGHT    192
#define WIDTH           640
#define DEPTH           2

struct NewScreen NewHackScreen = {
    0, 0, WIDTH, BARHEIGHT + WINDOWHEIGHT, DEPTH,
    0, 1,     /* DetailPen, BlockPen */
    HIRES,
    CUSTOMSCREEN,
    &Hack80,  /* Font */
    (UBYTE *) "NetHack 2.3 - Ported by Olaf Seibert (KosmoSoft)",
    NULL,     /* Gadgets */
    NULL,     /* CustomBitmap */
};

struct NewWindow NewHackWindow = {
    /* left, top, width, height, detailpen, blockpen */
    0, BARHEIGHT, WIDTH, WINDOWHEIGHT, -1, -1,
    RAWKEY | MENUPICK /* | MOUSEBUTTONS | GADGETDOWN */ ,
    BORDERLESS | BACKDROP | ACTIVATE,
    NULL, NULL, NULL,
    NULL, NULL, -1,-1,-1,-1, CUSTOMSCREEN
};

static int BufferGetchar()
{
    register unsigned char *from, *to;
    register int c;
    register short i;

    if (KbdBuffered) {
        c = KbdBuffer[0];
        KbdBuffered--;
        to = KbdBuffer;
        from = to + 1;
        /* Move the remaining characters */
        for (i = KbdBuffered; i > 0; i--) {
            *to++ = *from++;
        }
        return c;
    }

    return NO_CHAR;
}

/*
 *  This should remind you remotely of DeadKeyConvert,
 *  but we are cheating a bit.
 *  We want complete control over the numeric keypad, and no
 *  dead keys... (they are assumed to be on Alted keys)
 *  Also assumed is that the IntuiMessage is of type RAWKEY.
 *  For some reason, IECODE_UP_PREFIX events seem to be lost when they
 *  occur while our console window is inactive. This is particulary
 *  troublesome with qualifier keys... Is this because I never
 *  RawKeyConvert those events???
 */

int ConvertKey(message)
register struct IntuiMessage *message;
{
    static struct InputEvent theEvent;
    static char       numpad[] = "bjnh.lyku";
    static char  ctrl_numpad[] = "\x02\x0A\x0E\x08.\x0C\x19\x0B\x15";
    static char shift_numpad[] = "BJNH.LYKU";

    unsigned char buffer[1];
    register char length;
    register ULONG qualifier = message->Qualifier;
    char numeric_pad, shift, control, alt;

    control = (qualifier &  IEQUALIFIER_CONTROL) != 0;
    shift   = (qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)) != 0;

    /*
     *  Shortcut for HELP and arrow keys... I suppose this is allowed...
     *  the defines are in intuition/intuition.h, and the keys
     *  don't serve 'text' input, normally.
     *  Also, parsing their escape sequences is such a mess...
     */

    switch (message->Code) {
    case RAWHELP:
        length = '?';
        goto no_arrow;
    case CURSORLEFT:
        length = 'h'; goto arrow;
    case CURSORDOWN:
        length = 'j'; goto arrow;
    case CURSORUP:
        length = 'k'; goto arrow;
    case CURSORRIGHT:
        length = 'l';
    arrow:
        if (control)
            length &= 0x1F;         /* ToControl... */
        else if (shift)
            length &= 0x5F;         /* ToUpper... */
    no_arrow:
        BufferQueueChar(length);
        return;
    }

#ifdef BETA
    if (!ConsoleDevice) { /* Should never happen */
        abort(AG_IOError | AO_ConsoleDev);
        return;
    }
#endif

    numeric_pad = (qualifier & IEQUALIFIER_NUMERICPAD) != 0;
    if (alt = (qualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT)) != 0)
                /* Don't want dead keys... */
        qualifier &= ~(IEQUALIFIER_LALT | IEQUALIFIER_RALT);

    theEvent.ie_Class = IECLASS_RAWKEY;
    theEvent.ie_Code = message->Code;
    theEvent.ie_Qualifier = numeric_pad ? IEQUALIFIER_NUMERICPAD :
                                          qualifier;
    theEvent.ie_EventAddress = (APTR) *(message->IAddress);

    length = RawKeyConvert(&theEvent, buffer, (long) sizeof(buffer), NULL);

    if (length == 1) {   /* Plain ASCII character */
        length = buffer[0];
        if (numeric_pad && length >= '1' && length <= '9') {
            length -= '1';
            if (control) {
                length = ctrl_numpad[length];
            } else if (shift) {
                length = shift_numpad[length];
            } else {
                length = numpad[length];
            }
        }
        BufferQueueChar(length);
    } /* else shift, ctrl, alt, amiga, F-key, shift-tab, etc */
}
/*
 *  Process an incoming IntuiMessage.
 *  It would certainly look nicer if this could be done using a
 *  PA_SOFTINT message port, but we cannot call RawKeyConvert()
 *  during a software interrupt.
 *  Anyway, kbhit() is called often enough, and usually gets
 *  ahead of input demands, when the user types ahead.
 */

static char ProcessMessage(message)
register struct IntuiMessage *message;
{
    switch(message->Class) {
    case MENUPICK:
        {
            USHORT thismenu;
            struct MenuItem *item = NULL;

            thismenu = message->Code;
            while (thismenu != MENUNULL) {
                item = ItemAddress(&HackMenu, (ULONG) thismenu);
                if (KbdBuffered < KBDBUFFER)
                    BufferQueueChar(item->Command); /* Unused: No COMMSEQ */
                thismenu = item->NextSelect;
            }
        }
        break;
    case RAWKEY:
        if (!(message->Code & IECODE_UP_PREFIX))
            ConvertKey(message);    /* May queue multiple characters */
        break;                      /* but doesn't do that yet       */
#if 0
    case CLOSEWINDOW:
        BufferQueueChar('Q');
        break;
#endif
#if 0
    case GADGETDOWN:
        BufferQueueChar( ((struct Gadget *)message->IAddress)->GadgetID );
        break;
#endif
    }
    ReplyMsg(message);
}

/*
 *  Get all incoming messages and fill up the keyboard buffer,
 *  thus allowing Intuition to (maybe) free up the IntuiMessages.
 *  Return when no more messages left, or keyboard buffer half full.
 *  We need to do this since there is no one-to-one correspondence
 *  between characters and incoming messages.
 */

int kbhit()
{
    register struct IntuiMessage *message;

    while( (KbdBuffered < KBDBUFFER / 2) &&
            (message = GetMsg(HackWindow->UserPort)) )
        ProcessMessage(message);

    return KbdBuffered;
}

/*
 *  Get a character from the keyboard buffer, waiting if
 *  not available.
 */

int WindowGetchar()
{
    while ( !kbhit() ) {
        Wait( 1L << HackWindow->UserPort->mp_SigBit );
    }
    return BufferGetchar();
}

/*
 *  Flush the output waiting in the console output buffer.
 */

void WindowFlush()
{
#ifdef BETA
    if (!ConsoleDevice) { /* Should never happen */
        abort(AG_IOError | AO_ConsoleDev);
        return;
    }
#endif

    if (Buffered) {
        ConsoleIO.io_Command = CMD_WRITE;
        ConsoleIO.io_Data = (APTR)ConsoleBuffer;
        ConsoleIO.io_Length = Buffered;
        DoIO(&ConsoleIO);
        Buffered = 0;
    }
}

/*
 *  Queue a single character for output to the console screen.
 */

int WindowPutchar(c)
char c;
{
    if (Buffered >= CONBUFFER)
        WindowFlush();

    ConsoleBuffer[Buffered++] = c;
}

/*
 *  Queue an entire string for output to the console screen,
 *  flushing the existing characters first, if necessary.
 *  Do not append a newline.
 */

void WindowFPuts(string)
char *string;
{
    register int len = _BUILTIN_strlen(string);

    if (len + Buffered >= CONBUFFER)
        WindowFlush();

    _BUILTIN_strcpy(ConsoleBuffer + Buffered, string);
    Buffered += len;
}

/*
 *  Queue an entire string for output to the console screen,
 *  flushing the existing characters first, if necessary.
 *  Append a newline.
 */

void WindowPuts(string)
char *string;
{
    WindowFPuts(string);
    WindowPutchar('\n');
}

/*
 *  Queue a formatted string for output to the console screen,
 *  flushing the existing characters first, if necessary.
 */

void WindowPrintf(fmt, args, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
char *fmt;
long args, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9;
{
#ifdef AZTEC_C      /* Efficient but not portable */
    format(WindowPutchar, fmt, &args);
#else
    WindowFlush();  /* Don't know if all will fit */
# ifdef __STDC__    /* Cheap and portable way */
    vsprintf(ConsoleBuffer, fmt, &args);
# else              /* Expensive... */
    sprintf(ConsoleBuffer, fmt, args, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
arg9);
# endif
    ConsoleIO.io_Command = CMD_WRITE;
    ConsoleIO.io_Data = (APTR)ConsoleBuffer;
    ConsoleIO.io_Length = -1;
    DoIO(&ConsoleIO);
#endif
}

void CleanUp()
{
    struct IntuiMessage *msg;

    /* Clean up resources */

    if (ConsoleIO.io_Device) {
        CloseDevice(&ConsoleIO);
        ConsoleDevice = NULL;
    }
    if (HackWindow) {
        FindTask(NULL)->pr_WindowPtr = (APTR) pr_WindowPtr;
        ClearMenuStrip(HackWindow);
        Forbid();
        while (msg = GetMsg(HackWindow->UserPort))
            ReplyMsg(msg);
        CloseWindow(HackWindow);
        Permit();
        HackWindow = NULL;
    }
    if (HackScreen) {
        CloseScreen(HackScreen);
        HackScreen = NULL;
    }
    /* if (IconBase) {
        CloseLibrary(IconBase);
        IconBase = NULL;
    } */
#ifdef HACKFONT
    if (HackFont) {
        CloseFont(HackFont);
        HackFont = NULL;
    }
    if (DiskfontBase) {
        CloseLibrary(DiskfontBase);
        DiskfontBase = NULL;
    }
    if (GfxBase) {
        CloseLibrary(GfxBase);
        GfxBase = NULL;
    }
#endif
    if (IntuitionBase) {
        CloseLibrary(IntuitionBase);
        IntuitionBase = NULL;
    }

    Initialized = FALSE;
}

void abort(rc)
long rc;
{
#ifdef CHDIR
    extern char orgdir[];
    chdir(orgdir);
#endif
    if (Initialized && ConsoleDevice) {
        printf("\n\nAbort with alert code %08lx...\n", rc);
        getret();
    } else
        Alert(rc, 0L);
    CleanUp();
#undef exit
    exit(rc);
}

/*  Used by library routines, and the debugger */

void _abort()
{
    abort(-10L);
}

/*
 *  Open everything we need.
 */

void Initialize()
{
    if (Initialized)
        return;

    if ( (IntuitionBase = OpenLibrary("intuition.library", LIBRARY_VERSION))
          == NULL)
        abort(AG_OpenLib | AO_Intuition);

#ifdef HACKFONT

    if ( (GfxBase = OpenLibrary("graphics.library",LIBRARY_VERSION)) == NULL)
        abort(AG_OpenLib | AO_GraphicsLib);

    /*
     *  Force our own font to be loaded, if possible.
     *  If we can open diskfont.library, but not our font, we can close
     *  the diskfont.library again since it just wastes memory.
     *  Even if we can open the font, we don't need the diskfont.library
     *  anymore, since CloseFont is a graphics.library function.
     */

    if (DiskfontBase = OpenLibrary("diskfont.library", LIBRARY_VERSION)) {
        HackFont = OpenDiskFont(&Hack80);
        CloseLibrary(DiskfontBase);
        DiskfontBase = NULL;
    }
#endif

    /* if ( (IconBase = OpenLibrary("icon.library",LIBRARY_VERSION)) == NULL)
        abort(AG_OpenLib | AO_IconLib); */

    /*
     *  Now Intuition is supposed to use our HackFont for the screen,
     *  since we have a corresponding TextAttr, but it *doesn't*.
     *  So, we need to do a SetFont() a bit later on.
     */
    if ( (HackScreen = OpenScreen(&NewHackScreen)) == NULL)
        abort(AN_OpenScreen & ~AT_DeadEnd);

    NewHackWindow.Screen = HackScreen;

    if ( (HackWindow = OpenWindow(&NewHackWindow)) == NULL)
        abort(AN_OpenWindow & ~AT_DeadEnd);

    SetMenuStrip(HackWindow,&HackMenu);
    {
        register struct Process *myProcess = FindTask(NULL);
        pr_WindowPtr = (struct Window *)myProcess->pr_WindowPtr;
        myProcess->pr_WindowPtr = (APTR) HackWindow;
    }
#ifdef HACKFONT
    if (HackFont)
        SetFont(HackWindow->RPort, HackFont);
#endif

    ConsoleIO.io_Data = (APTR) HackWindow;
    ConsoleIO.io_Length = sizeof(*HackWindow);
    if (OpenDevice("console.device",0L , &ConsoleIO, 0L) != 0)
        abort(AG_OpenDev | AO_ConsoleDev);

    ConsoleDevice = ConsoleIO.io_Device;

    Buffered = 0;
    KbdBuffered = 0;

    /* set CRMOD on */
    WindowFPuts("\x9B20h");

    Initialized = TRUE;
}
SHAR_EOF
#	End of shell archive
exit 0
-- 
Bob Page, U of Lowell CS Dept.  page@swan.ulowell.edu  ulowell!page
Have five nice days.