/* NewZAP : main body - DJH */

#include <exec/types.h>
#include <intuition/intuition.h>
#include <libraries/dos.h>

#define FAILAT 100  /* exit code if problems */

extern struct Menu menu[3];
extern struct Gadget gadget[11];
extern struct StringInfo ginfo;
extern struct Image GotoImage;
extern struct TextFont ThinFont;
extern struct Window *pwindow;
extern struct Requester arequest,brequest;

extern UWORD BusySprite[];
extern UBYTE filespec[],searchbuf[];

extern fontinit(),OpenBackDrop(),CloseBackDrop(),
       InitHelpRequester(),FreeRequester(),
       InitSearchRequester(),FreeSearchRequester(),
       FakeRequester(),Beep();

/* GLOBALS */

UBYTE title[] = "NewZAP 3.0 : (c) 1986 John Hodgson";

/* Allow user to modify defaults with NewZAP : simply look for PRECUE/POSTCUE
   strings and ZAP the bytes in the middle! */

#define PREAMBLE  "PENCOLORS= "
#define PRELEN (sizeof(PREAMBLE)-1) /* exclude \0 */
#define POSTAMBLE " [default=01213]"

/* this works because AZTEC C supports string merging */

#define BGPEN     "0"  /* background color */
#define STDPEN    "3"  /* information rendering (ex: cursor pos) */
#define HEXPEN    "2"  /* hex window text */
#define MODPEN    "1"  /* hex modify highlight */
#define SEARCHPEN "3"  /* search highlight */

/* sorry, AZTEC won't merge DEFINE'd strings. Preprocess the following
   string by hand as if it was :

   PenBlock[] = PREAMBLE BGPEN STDPEN HEXPEN MODPEN SEARCHPEN POSTABLE
*/
char PenBlock[] = "PENCOLORS= 03213 [default=03213]"; /* (ecch) */

struct PenCache {
  long BgPen,StdPen,HexPen,ModPen,SearchPen;
} PenCache;

long prevx=0,prevy=0,prevasc=0,Read(),Seek(); /* global hex/asc cursor pos */
short cursec,endsec,eof,idnum,searchflag,
      direction=1, /* search fwd by default */
      caps=1, /* LC = UC for searches */
      digitcount=0; /* kybd buf cnt */

char goodfile[50], /* last successful filespec */
     secbuf[16][32], /* 512-byte sector buffer */
     hilite[16][32]; /* corresponding hilite map */
     
struct StringInfo prevgoto;
struct Gadget *gadgetptr;
struct TextFont *topaz,*thin,*OpenFont(),*OpenDiskFont();
struct FileHandle *file,*Open();

void *IntuitionBase,*GfxBase,*DiskfontBase;

/* ------- */

struct TextFont *LoadFont(name,height)
UBYTE *name;
short height;
{
  struct TextAttr newfont;  

  newfont.ta_Name=name;
  newfont.ta_YSize=height;
  newfont.ta_Style=FS_NORMAL;
  newfont.ta_Flags=FPB_ROMFONT|FPB_DISKFONT;

  /* OpenDiskFont will load in the romfonts as well, but it searches
     the fonts: directory first. Slow! */

  if (!strcmp(name,"topaz.font")) return(OpenFont(&newfont));
    else return(OpenDiskFont(&newfont));
}

edit_window(inkey)
short inkey;
{
  char hexbuf[2],accumulator;
  short i,c; /* not char for -1 checks */

  if (prevasc==0) return(0); /* back if no cursor undefined */
  if ((c=fetch_input(inkey))==-1) return(0); /* back if dud */

  accumulator=c; hexconv(&hexbuf[0],accumulator); /* binary to HEX */

  erase_cursors();

  SetFont(pwindow->RPort,thin);   /* hex window font */
  SetAPen(pwindow->RPort,PenCache.ModPen); /* modify hilight */

  prstr(pwindow,(short)prevx,(short)prevy+7,&hexbuf[0],2); /* print digits */
  prstr(pwindow,(short)prevasc,(short)prevy+7,&accumulator,1); /* print char */

  SetFont(pwindow->RPort,topaz);

  i=roundup(&prevx,&prevy,&prevasc); /* compute x-index */
  secbuf[((prevy-26)/8)][i]=accumulator; /* & update buffer */
  
  bump_cursors(); refresh_position(0);
  draw_cursors();
}

fetch_input(inkey)
short inkey;
{
  static unsigned char accumulator=0;
 
  if (idnum==2) { /* ASC mode? */
    digitcount=0; /* reset accumulated chars */
    return((int)inkey); /* and return */
  }

  /* HEX input processing starts here */

  if (!isalnum(inkey)) { /* invalid char? */
    digitcount=0; /* reset digit tabulators */
    return(-1);
  }
  inkey=toupper(inkey); /* fold to uppercase */
  if (inkey>'F') {
    digitcount=0;
    return(-1);
  }

  if (inkey<='9') inkey-='0'; else inkey-=55; /* fold to binary */
  if (digitcount==0) accumulator=0; /* 1st pass? zero accumulator */

  accumulator=(accumulator<<4)+inkey; /* shift & add */

  if (++digitcount==1) return(-1); /* back if only 1 digit stored */

  digitcount=0; /* prepare for fresh pass */
  return((int)accumulator); /* return accumulated hex digit */
}
 
bump_cursors()
{
  short i;
  long hexx,hexy,asc;

  hexx=prevx+18; /* largest skip used by any HEX pair */  
  hexy=prevy; asc=prevasc;

  if (hexx>422+11) { hexx=0; hexy+=8; } /* line wrap */
  if (hexy>146) hexy=0; /* page wrap */

  i=roundup(&hexx,&hexy,&asc); /* remap & compute x-index */

  /* don't bump past EOF */
  if ((cursec==endsec) && ((hexy-26)/8)*32+i >= eof) { Beep(); return(0); }

  prevx=hexx; prevy=hexy; prevasc=asc; /* ok? update! */
}

refresh_position(mode)
short mode; /* mode==1 FORCES refresh of hex/asc window ONLY */
{
  short i,curpos;
  char charbuf[3];

  if (!(prevasc|mode)) { /* cursor undefined? */
    prstr(pwindow,605,18,"   ",-1); /* erase "cursor pos : " argument */
    prstr(pwindow,605,174,"   ",-1); /* erase "Edit mode : " argument */ 
    return(0);
  }

  SetAPen(pwindow->RPort,PenCache.StdPen);

  if (mode==0) {
    i=roundup(&prevx,&prevy,&prevasc); /* compute x-index */
    curpos=((prevy-26)/8)*32+i; /* update cursor pos */

    if (curpos & 0x100) charbuf[0]='1'; else charbuf[0]='0';
    hexconv(&charbuf[1],curpos);
    prstr(pwindow,605,18,&charbuf[0],3);
  }

  if (idnum==1) prstr(pwindow,605,174,"HEX",-1);
    else prstr(pwindow,605,174,"ASC",-1); /* see below */
}

display_record()
{
  short ctr,x,y,flag=0;
  register short i;
  char linebuf[80],hibuf[80];

  SetFont(pwindow->RPort,thin);

  /* fill last record w/dummy chars past EOF */
  if (cursec==endsec) for (ctr=512;ctr>eof;ctr--) secbuf[0][ctr-1]='#';

  searchflag=0; /* assume no hilighting displayed */

  for (y=0;y<16;y++) {
    ctr=flag=0;
    setmem(&hibuf[0],80,0); /* clear working line hilite buffer */

    for (x=0;x<32;x++) {
      hexconv(&linebuf[ctr],secbuf[y][x]);
      if (hilite[y][x]) searchflag=flag=hibuf[ctr]=hibuf[ctr+1]=1; 
      ctr+=2;
      if ((x+1)%4==0) linebuf[ctr++]=' '; /* don't hilite HEX gaps */
    }

    if (flag) { /* take highlighting into account */
      Move(pwindow->RPort,8L,y*8+33L);

      for (i=0;i<71;i++) { /* HEX side w/hilite */
        if (hibuf[i]) SetAPen(pwindow->RPort,PenCache.SearchPen);
          else SetAPen(pwindow->RPort,PenCache.HexPen);
        Text(pwindow->RPort,&linebuf[i],1L);
      }
      
      Move(pwindow->RPort,441L,y*8+33L);

      for (i=0;i<32;i++) { /* ASCii side w/hilite */
        if (hilite[y][i]) SetAPen(pwindow->RPort,PenCache.SearchPen);
          else SetAPen(pwindow->RPort,PenCache.HexPen);
        Text(pwindow->RPort,&secbuf[y][i],1L);
      }
    }
    else { /* no hilighting? fast display! */
      SetAPen(pwindow->RPort,PenCache.HexPen);
      prstr(pwindow,8,y*8+33,&linebuf[0],71);
      prstr(pwindow,441,y*8+33,&secbuf[y][0],32);
    }
  } /* line loop */

  setmem(&hilite[0][0],512,0); /* erase hilite buffer */
  SetFont(pwindow->RPort,topaz); /* back to normal */

  prevx=prevy=prevasc=0; /* undefine cursors; written over! */
}            

BorderInfo()
{
  char charbuf[20];

  SetAPen(pwindow->RPort,PenCache.StdPen);

  refresh_position(0);

  sprintf(&charbuf[0],"%d    ",cursec); /* "Current Sector : " */
  prstr(pwindow,141,166,&charbuf[0],-1);

  /* "End of File : " (can't shrink - no trailing spcs req'd) */

  sprintf(&charbuf[0],"%d",eof);
  prstr(pwindow,605,166,&charbuf[0],-1);

  sprintf(&charbuf[0],"%d    ",endsec); /* "Ending Sector : " */
  prstr(pwindow,141,174,&charbuf[0],-1);
}

prstr(window,x,y,string,len) /* print-at routine; len=-1 for autosizing */
struct Window *window;
short x,y,len;
char *string;
{
  Move(window->RPort,(long)x,(long)y);
  Text(window->RPort,string,(len==-1) ? (long)strlen(string) : (long)len);
}

hexconv(result,byte) /* byte to 2 ASCII hex digits */
unsigned char result[2],byte;
{
  result[0]=byte>>4;
  result[1]=byte & 0xf;

  if (result[0]<10) result[0]|=0x30; else result[0]+=55;
  if (result[1]<10) result[1]|=0x30; else result[1]+=55;
}

hex2bin(source,dest) /* ASCII HEX digit to binary */
unsigned char source[2],*dest;
{
  char msb,lsb;

  /* check for correctness & fold to UC */
  if (!isalnum(source[0]) || !isalnum(source[1])) return(1);  
  source[0]=toupper(source[0]); source[1]=toupper(source[1]);
  if (source[0]>'F' || source[1]>'F') return(1); 

  if (source[0]<='9') msb=source[0]-'0'; else msb=source[0]-55;
  if (source[1]<='9') lsb=source[1]-'0'; else lsb=source[1]-55;

  *dest=(msb<<4)+lsb;
  return(0);
}

Setup()
{
  IntuitionBase=OpenLibrary("intuition.library",0L);
  GfxBase=OpenLibrary("graphics.library",0L);
  DiskfontBase=OpenLibrary("diskfont.library",0L);

  /* cache user-definable pen colors */

  PenCache.BgPen     = PenBlock[PRELEN+0]-'0'; /* strip off ASCII bit */
  PenCache.StdPen    = PenBlock[PRELEN+1]-'0';
  PenCache.HexPen    = PenBlock[PRELEN+2]-'0';
  PenCache.ModPen    = PenBlock[PRELEN+3]-'0';
  PenCache.SearchPen = PenBlock[PRELEN+4]-'0';

  fontinit();
  NoFile(); /* initialize empty environment */

  if (strlen(&filespec[0])>30) filespec[30-1]='\0'; /* truncate long fspecs */
  if (filespec[0]!='\0') OpenFile();

  /* allocation-oriented subs can fail, so ... */

  if (InitHelpRequester()) exit(FAILAT);
  if (InitSearchRequester()) exit(FAILAT);  
  if (OpenBackDrop()) exit(FAILAT);

  SetMenuStrip(pwindow,&menu[0]); /* attach menus */
  EnableMenu(); /* handle Search enable/disable */

  /* Prior to 1.2, string gadgets may not erase correctly. So, we'll save
     the initial state of the GOTO stringinfo so we can force it to redraw
     itself completely later. */
  prevgoto=ginfo;

  /* Cover string gadget w/"GOTO" box until selected */
  DrawImage(pwindow->RPort,&GotoImage,291L,172L);
  
  thin=&ThinFont;
  topaz=LoadFont("topaz.font",8);

  read_sector(); /* read & display */ 
}

OpenFile()
{
  long i;

  NoFile(); /* assume open won't work */

  if (!(file=Open(&filespec[0],MODE_OLDFILE))) {
    Beep(); return(-1); }  /* dud flag */

  Seek(file,0L,OFFSET_END); i=Seek(file,0L,OFFSET_BEGINNING); /* get file len */

  endsec=i/512; eof=i-(endsec*512);
  if (eof) endsec++; /* round up odd-length files */
    else eof=512; /* adjust pseudo-EOF for full-sectored files */

  /* backup successful fspec in case of later unsuccessful files */
  strcpy(&goodfile[0],&filespec[0]);

  return(0); /* good read */
}

NoFile()
{
  cursec=endsec=1; /* current/ending sectors = lowest possible */
  eof=0;    /* sector size = 0 (empty) */
}

ShutDown()
{
  if (eof) Close(file);

  CloseFont(topaz); /* don't expunge romfonts! */
  ClearMenuStrip(pwindow); /* delete menus before closing windows */
  FreeRequester(); FreeSearchRequester();
  CloseBackDrop();
  CloseLibrary(IntuitionBase);
  CloseLibrary(GfxBase);
  CloseLibrary(DiskfontBase);
}

ScanGadgets() /* IDCMP asynchronous scan routine */
{
  struct IntuiMessage *message;
  long class;
  short code,qual;
  static short remap[4]={ 6-1,9-1,7-1,8-1 };
  
  for(;;) { /* FOREVER 'til CLOSEWINDOW */

    WaitPort(pwindow->UserPort);
    while (message=(struct IntuiMessage *)GetMsg(pwindow->UserPort)) {

      class=message->Class;
      code=message->Code;
      qual=message->Qualifier;
      gadgetptr=((struct Gadget *)message->IAddress);
    
      ReplyMsg(message);
     
      if (class==MENUPICK) {
        switch(code) {
          case 0xf800: /* menu=0, menuitem=0, menusub=NOSUB */
            if (ITEMNUM(code)==0) Request(&arequest,pwindow); /* help menu */
            break;
          case 0xf820: /* Quit */
            return(0);
            break;
          case 0xf801: /* menu 1, menuitems0-3, subitem 0 */
          case 0xf821:
          case 0xf841:
          case 0xf861:
            class=GADGETUP;  /* fake gadget input for F,S,E & B */
            gadgetptr=&gadget[remap[ITEMNUM(code)]];
            break;
          case 0xf802: /* menu 2 */
            FakeRequest(); /* search pseudo-requester */
            break;
          case 0x22: /* case dependency ON */
            caps=0;
            break;
          case 0x822: /* case dependency OFF */
            caps=1;
            break;
          case 0x42: /* direction=FORWARD */
            direction=1;
            break;
          case 0x842: /* direction=REVERSE */
            direction=-1;
            break;
          case 0xf862: /* continue search */
            Search();
            break;
        }
      }
      switch(class) {
        case RAWKEY:
          if (code=arrow_filter(code,qual)) edit_window(code);
          break;
        case CLOSEWINDOW:
          return(0); /* up to caller to shutdown */
          break;
        case MOUSEMOVE:
          movecur(); /* mouse events only in edit */
          break;
        case GADGETDOWN:
          gdown();
          break;
        case GADGETUP:
          (* (int (*)()) gadgetptr->UserData ) ();
          break;
      } /* switch (CLASS) */
    } /* while (getmsg) */
  } /* FOREVER */
}

Dummy() { } /* used by dummy gadget # 11 */
Fgadget() { if (cursec<endsec) { cursec++; read_sector(); } }
Sgadget() { cursec=1; read_sector(); }
Egadget() { cursec=endsec; read_sector(); }
Bgadget() { if (cursec>1) { cursec--; read_sector(); } }

gdown() /* GADGETDOWN */
{
  short id=gadgetptr->GadgetID;

  /* Redraw GOTO lid in case of premature deselection */
  if (id!=5) DrawImage(pwindow->RPort,&GotoImage,291L,172L);
  /* redraw filespec in case of premature deselection */
  if (id!=10) update_fspec();

  if (id<3 && eof) { /* select appropriate edit-window */
    idnum=id; /* update edit mode */          
    refresh_position(1); /* just update "HEX/ASC" msgs */
    movecur(); /* display cursor */
  }
  else { /* deselect edit-window */
    erase_cursors();         
    prevx=prevy=prevasc=0; /* undefine cursors */
    refresh_position(0);
  }
}

gotogad() /* GOTO */
{
  long i;

  DrawImage(pwindow->RPort,&GotoImage,291L,172L); /* draw lid */

  i=((struct StringInfo *)gadgetptr->SpecialInfo)->LongInt;
  if (i>0 && i<=endsec) {
    cursec=i;
    read_sector();
  }
  else Beep(); /* beep if error! */
  
  ginfo=prevgoto; /* see message in Setup() */
}

fspecgad()
{
  struct FileLock *lock;

 /* AmigaDOS-recommended way to check for file existence : Note that Lock()
    actually returns a nonzero lock when passed a null string! Bug?! */

  if (filespec[0]=='\0') lock=0; else lock=Lock(&filespec[0],ACCESS_READ);

  if (lock==0) { /* no file? */
    Beep();
    update_fspec(); return(0); /* redraw & return */
 }

 UnLock(lock);

 if (eof) Close(file); /* close previous file */

 OpenFile(); EnableMenu();
 read_sector(); /* new file all ready! */
}

EnableMenu() /* reflect presence of file in Search menu */
{
  if (eof) { 
    OnMenu(pwindow,NOITEM<<5L | 1); /* Move Menu */
    OnMenu(pwindow,0<<5L|2); /* Search : search */
    OnMenu(pwindow,3<<5L|2); /* Search : Continue Search */
  }
  else {
    OffMenu(pwindow,NOITEM<<5L | 1);
    OffMenu(pwindow,0<<5L|2);
    OffMenu(pwindow,3<<5L|2);
  }
}

update_fspec()
{
  strcpy(&filespec[0],&goodfile[0]); /* restore good filespec */

/*
  Note : Before 1.2, string gadgets did not redraw their entire
  display when refreshed. So, to make sure all bits and pieces
  are removed, we have to erase by hand.
 */

  SetAPen(pwindow->RPort,PenCache.BgPen); /* background color */
  RectFill(pwindow->RPort,97L,12L,98L+250,12L+7); /* erase string */

  RefreshGadgets(&gadget[10-1],pwindow,0L); /* and redraw */
}

Search1()
{
  /* FakeRequest() forces this routine to perform inclusive searches
     when the search string is changed; prevents skipping the current
     sector when a previous (different string) search highlighted it. */
   
  searchflag=0; Search();
}

Search() /* sector string search : current to last records */
{
  int c;
  short start,teof,i,n,dest,tcaps,flag=0;
  char convbuf[50],*srcbuf;

  /* flag "zz" busy ptr for search */
  SetPointer(pwindow,&BusySprite[0],25L,16L,0L,0L);

  c=strlen(&searchbuf[0]); srcbuf=(char *)&searchbuf[0];
  tcaps=caps; /* cache uc/lc match flag */

  /* treat '$' search as literal if no args! */
  if (searchbuf[0]=='$' && c>1) {
    for (n=0,i=1;i<c;i++) {
      if (searchbuf[i]==' ') continue;
      if (hex2bin(&searchbuf[i++],&convbuf[n++])) {
        searchbuf[0]='\0'; /* erase dud input */
        goto not_found;
      }
    }  
    convbuf[n]='\0'; srcbuf=&convbuf[0]; c=n;
    tcaps=0; /* don't force UC for HEX searches! */
  }
  else if (c==0) goto not_found; /* no null searches, PLEEZE */

  if (direction==1) dest=endsec +1; else dest=1 -1;

  /* if current record is highlighted already, skip it! This lets the
     "Continue Search" item work intelligently. Searchflag is reset by
     displaying unhighlighted records or changing the Search() string. */

  start=cursec; if (searchflag) start+=direction;

  for (;start!=dest;start+=direction) {
    if (start==endsec) teof=eof-c; else teof=512-c;

    Seek(file,(start-1)*512L,OFFSET_BEGINNING);
    Read(file,&secbuf[0][0],512L);

    for (i=0;i<=teof;i++) /* hilite all matches in sector */
      if (!strNcmp(&secbuf[0][i],srcbuf,c,tcaps)) {
        setmem(&hilite[0][i],c,1); /* hilite string chars */
        searchflag=flag=1; /* set found & mod flags */
      }
   
    if (flag) { /* string found? */
      cursec=start; /* update current sector */
      BorderInfo(); display_record();
      ClearPointer(pwindow);

      return(1);
    }
  }

not_found:
  read_sector(); /* refetch & erase any old hilighting */
  Beep(); /* "not found!" */
  ClearPointer(pwindow);

  return(0);
}

strNcmp(srcbuf,destbuf,cnt,caps) /* strncmp minus eof recognition */
register char *srcbuf,*destbuf;
register short cnt,caps;
{
  if (caps) { /* UC = LC for compares? */
    while (toupper(*srcbuf++) == toupper(*destbuf++) && --cnt);
    return(toupper(*--srcbuf) - toupper(*--destbuf));
  }
  else { /* compare VERBATIM */
    while (*srcbuf++ == *destbuf++ && --cnt);
    return(*--srcbuf - *--destbuf);
  }
}  

read_sector()
{
  if (!eof) return(0); /* back if bad Open() */

  Seek(file,(cursec-1)*512L,OFFSET_BEGINNING);
  Read(file,&secbuf[0][0],512L);
  BorderInfo();
  display_record();
}

write_sector()
{
  if (!eof) return(0); /* back if dud file */

  Seek(file,(cursec-1)*512L,OFFSET_BEGINNING); Beep();
  Write(file,&secbuf[0][0],(cursec==endsec) ? (long)eof : 512L);
  display_record(); /* erase highlighted changes */
}

erase_cursors()
{
  digitcount=0; /* cancel kybd input */
  if (prevasc==0) return(0); /* don't erase undefined cursors ! */

  SetDrMd(pwindow->RPort,COMPLEMENT);

  RectFill(pwindow->RPort,prevx,prevy,prevx+10,prevy+6);
  RectFill(pwindow->RPort,prevasc,prevy,prevasc+4,prevy+7);

  SetDrMd(pwindow->RPort,JAM2);
}

draw_cursors()
{
  if (prevasc==0) return(0); /* don't draw undefined cursors! */

  SetDrMd(pwindow->RPort,COMPLEMENT);

  RectFill(pwindow->RPort,prevx,prevy,prevx+10,prevy+6); /* draw HEX cursor */
  RectFill(pwindow->RPort,prevasc,prevy,prevasc+4,prevy+7); /* ASC cursor */

  SetDrMd(pwindow->RPort,JAM2);
}

movecur()
{
  long hexx,hexy,ascx;
  short i;

  hexx=pwindow->MouseX; hexy=pwindow->MouseY;
  i=roundup(&hexx,&hexy,&ascx); /* compute relative cursor positions */

  /* truncate max-y pos if exceeding EOF boundaries */
  if ((cursec==endsec) && ((hexy-26)/8)*32+i >= eof)
    hexy=((eof/32)-(i>=(eof&31)))*8+26; /* takes odd eof's into acct */
 
  /* return if not enough movement to actually move cursor */
  if ((hexx==prevx) && (hexy==prevy) || (hexy<26)) return(0);
 
  erase_cursors(); /* erase old cursors */
  prevx=hexx; prevy=hexy; prevasc=ascx; /* & update */

  refresh_position(0);
  draw_cursors();
}

roundup(hexx,hexy,ascx) /* map cursor to HEX or ASCII window */
long *hexx,*hexy,*ascx;
{
  static short tilehex[32] = { /* allowed HEX cursor positions */
     8,  20, 32, 44,  62, 74, 86, 98,
     116,128,140,152, 170,182,194,206,
     224,236,248,260, 278,290,302,314,
     332,344,356,368, 386,398,410,422
  };
  static short tileasc[32] = {
     441,447,453,459, 465,471,477,483, /* allowed ASC cursor positions */
     489,495,501,507, 513,519,525,531,
     537,543,549,555, 561,567,573,579,
     585,591,597,603, 609,615,621,627
  };

  short i;

  if (*hexx<440) { /* cursor in HEX window? */
    for (i=0;*hexx>=tilehex[i] && i<32;i++); /* round x to nearest HEX pair */
    if (i) --i;
    *hexx=tilehex[i]; *ascx=tileasc[i]; /* compute ASCII, given HEX */
  }
  else { /* cursor in ASCII window? */
    for (i=0,*ascx=*hexx;*ascx>=tileasc[i] && i<32;i++); /* as above */
    if (i) --i;
    *ascx=tileasc[i]; *hexx=tilehex[i]; /* compute HEX, given ASCII */
  }

  if (*hexy<26) *hexy=26; else if (*hexy>146) *hexy=146;
  *hexy=((*hexy-26)/8)*8+26; /* round y to nearest text line */

  return((int)i); /* return x-index for array */
}

arrow_filter(code,qual) /* arrow & help-key processor */
short code,qual;
{
  short dx=0,dy=0;

  if (code==0x5f) { /* "HELP" requester */
    Request(&arequest,pwindow);
    return(0);
  }

/* Sure, modifying shared resources without the proper exclusion is poor
   style, but in this case it's fast and effective. */

  if (prevasc) { /* quick-edit */
    switch(code) {

      case 0x4c: dy=-8;  break; /* up-arrow */
      case 0x4f: dx=-12; break; /* left-arrow */
      case 0x4e: dx=18;  break; /* right-arrow */
      case 0x4d: dy=8;   break; /* down-arrow */

      default:
        return(key_conv(code,qual)); /* normal keys? just convert */
        break;
    }

        pwindow->MouseX=prevx+dx; pwindow->MouseY=prevy+dy;

        if (pwindow->MouseX > 422+11) {
          pwindow->MouseX=8; pwindow->MouseY+=8; }
        else
          if (pwindow->MouseX<8) {
            pwindow->MouseX=422; pwindow->MouseY-=8; }

        if (pwindow->MouseY > 146) pwindow->MouseY=26;
          else if (pwindow->MouseY < 26) pwindow->MouseY=146;
        
    movecur(); return(0);
  }
}

/* key_conv - Optimized Rawkeys to ASCII converter */

key_conv(code,qualifier)
unsigned char code;
unsigned short qualifier;
{
  char result;

  /* Note : Some pgms may prefer CR's to be returned as LF's, */
  /* which the Amiga compiler defines as '\n'.                */

  static char convtable[] = {
    '`','1','2','3','4','5','6','7','8','9','0','-','=','\\','\0',
    '0','q','w','e','r','t','y','u','i','o','p','[',']','\0','1',
    '2','3','a','s','d','f','g','h','j','k','l',';','\'','\0',
    '\0','4','5','6','\0','z','x','c','v','b','n','m',',','.',
    '/','\0','.','7','8','9',' ','\b','\t',13,13,0x1b,0x7f
  };

  static char shiftable[] = {
    '~','!','@','#','$','%','^','&','*','(',')','_','+','|','\0',
    '0','Q','W','E','R','T','Y','U','I','O','P','{','}','\0','1',
    '2','3','A','S','D','F','G','H','J','K','L',':','\'','\0',
    '\0','4','5','6','\0','Z','X','C','V','B','N','M','<','>',
    '?','\0','.','7','8','9',' ','\b','\t',13,13,0x1b,0x7f
  };

    if (code >0x46) return(0); /* filter upstrokes, CSI keys, etc */

    if (qualifier & 0x3) result=shiftable[code]; /* SHIFT */
      else result=convtable[code];

    /* Caps Lock, unlike SHIFT, does not affect non-alphas */
    if ((qualifier & 0x4)&&(isalpha(result))) result=shiftable[code];

    /* CTRL */
    if ((qualifier & 0x8)&&(isalpha(result))) result=(result & 0x1f);

    if (qualifier & 0x30) result|=0x80; /* ALT */

    return((int)result);
}
  
main(argc,argv)
int argc;
char *argv[];
{
  if (argc>1) strcpy(&filespec[0],argv[1]); /* else null fspec */
  Setup();

  ScanGadgets();

  ShutDown();
}
