/*
 *	*****************
 *	*  L F N T . C  *
 *	*****************
 *
 * This program loads character patterns into the P/OS
 * terminal subsystem's character generator area in
 * main memory.  It reads the pixel patterns from a
 * standard ASCII file which contains the patterns in an
 * editable form (more on this later).  The replacement
 * characters are loaded into the area normally occupied
 * by the "supplemental graphics" characters (a.k.a.
 * "European" characters).
 *
 * With release 2, this program also supports a binary font
 * format for faster loading and smaller disk usage.  The
 * format of the binary file is R.VAR with no carriage control.
 * The 1st record is the font header, and each succeeding
 * record is the data for a character.  Character data consists
 * of 3 words of character header info, followed by 10 bytes or
 * 10 words (132 or 80 col. format, respectively) of pixel
 * row data.
 *
 * By:
 *       Robert B. Denny
 *       Alisa Systems, Inc.
 *       Mezzanine Floor
 *       234 E. Colorado Blvd.
 *       Pasadena, CA   91101
 *
 * Edits:
 *
 * X1.1 06-Dec-83 RBD	Add 132-column support - new PXL file format
 * Y1.2 10-Dec-83 RBD	Add help text on startup per M. Otte
 * Y1.3 16-Dec-83 RBD	Fencepost error -- RH column not being loaded.
 *			Add help text for 132-column fonts.
 * Y1.4 20-Dec-83 RBD	Change font file locations to [ZZSYS].  Add
 *			conditional code for DCS test version which
 *			will not support direct user interface, just
 *			spawning from CT*OS, and has no licensing.
 * Y1.5 22-Dec-83 TTC   Display loading font message.
 * Y2.0 11-Jan-83 RBD	Major changes.  Use RMS, remove serial number
 *			checking, add binary font file capability,
 *			make silent version for CT*OS, spawn-only,
 *			requires password on command line.  Make
 *			non-silent version use P/OS menu's etc.
 *			MUST be built using LFNTGEN now, as the
 *			PAB files are different, etc.  Automatically
 *			loads both 80 and 132 column fonts if the
 *			font name doesn't contain those strings.
 *			If one of the two don't exist, the non-SILENT
 *			one issues a warning.  The interactive version
 *			keeps its files in [FONTS].
 * Y2.1 12-Jan-83 RBD   Removed CT*OS proprietary stuff prior to releasing
 *                      to M. Minow & friends at Digital.
 */


/*
 *			*************
 *			** N O T E **
 *			*************
 *
 *	Use the LFNTGEN.CMD file to generate this program
 *	in either the interactive or quiet version.
 */
#define DEBUGoff
#define TESTINGoff

#include <rmstdio.h>
#include <cx.h>
#include <rdbdf.h>
#include <wdbdf.h>
#include "lfcnd.h"

/*
 * The following definition establishes the location
 * on the P/OS system of the font files.
 */
#ifdef INTERACTIVE
#define FONT_LOC "LB:[FONTS]"
#else
#define FONT_LOC "LB:[ZZSYS]"
#endif

int $$narg = 1;			/* Supress the startup prompt */

/*
 * Data structures for PLAS access to the VDFNTS partition.
 *
 * NOTE:
 *	Addresses within VDFNTS are APR4-relative, and the following
 *	code assumes that we map through APR4 as well.  If you
 *	make this program too big, it'll bump into APR4's VA's
 *	and you'll have to make it even bigger, translating VA's
 *	to APR6-relative (or whatever).
 */
static struct rdb vrdb =	/* Define the VDFNTS partition as a region */
   {
   0, 0,
   0105046, 055263,
   026226, 0,
   RS_RED | RS_WRT,
   0
   };

static struct wdb vwdb = 	/* Set up an address window at 100000 VA */
   { 0, 4, 0, 0, 0, 0, 0, WS_MAP|WS_64B|WS_WRT, 0 };

/*
 * Data structures which map the contents of the VDFNTS
 * partition.  The actual structure is variable, driven
 * by descriptive information also contained within the
 * partition.  Hence the looseness of definition.
 */

/*
 * Partition header - points to index and map areas for
 * each of the fonts resident in the partition.  There
 * are currently 3 separate fonts in VDFNTS.  The first
 * contains the 80-column characters for ALL 3 CHARACTER
 * SETS (ASCII, Special Graphics and Supplemental Graphics).
 * The second font appears to contain the 132-column char-
 * acters for the same sets of characters.  I don't (yet)
 * know what is in the third font (may belong to $GIDIS).
 *
 * The Indexing arrays contain bitmap indexes which identify
 * the character bitmaps for each of the 96 characters in
 * each of the three character sets.  These are used to
 * translate the character code into an address of a
 * character bitmap.  The bitmaps are packed end-to-end
 * and can be viewed as an array of character cell maps
 * of whatever size.  The "index" is the index of the cell
 * array to use.  For example, if an octal 111 is received,
 * then the 112th (octal) index is used to locate the char-
 * acter cell.  If this index is 29, then the 29th cell map
 * is used to display the character.
 *
 * The important thing to note is that the mapping index
 * correspond to the three 96-character "sets", and refer
 * to a single FONT.  Normally, the first font is in use,
 * and the values in all three mapping indexes refer to
 * cell maps in the first font.  When the system is
 * switched to 132-column mode, the indexes all refer to
 * the second font ... Nuff said?
 */
struct fnt_hdr	{
		word	xx;
		word	n_fnts;		/* Number of fonts */
		address	bitmap[8];	/* APR-4 based VA's of font bitmaps */
		word	n_indx;		/* Number of indexing arrays */
		address	index[8];	/* APR-4 based VA's of index arrays */
		};

static int bin_fmt;			/* TRUE if font file is binary */

#ifdef INTERACTIVE
/*
 * Menu data
 */
static int stat[8];				/* Used everywhere */
static char action[8];
static char *msg1 = "";
static char *msg2 = "";

static char *clrseq = "\033[1;1H\033[J";
static char *wrgkey = "You pressed the wrong function key";
static char *unsup = "Not supported yet";
static char m1buf[80];
#endif

extern int $dsw;
extern int $$ferr;


main(argc, argv)
int argc;
char *argv[];
   {
   register address pxlp;
   register char *cp;
   word chcode, charset, idxval, i, j;
   struct fnt_hdr *hdr;
   word fntnum, nbpr, nbpc, nbits, bits;
   char buf[64];
   char filename[64];
   FILE *ffp;
   char *np;
   word *wp;
   byte *bp;
   char *aflavor;
   char *bflavor;
   int pass;

   /*
    * Get the name of the font file to load
    */
#ifdef INTERACTIVE

#ifdef TESTING
   /*
    * The following 2 POSRES calls may be eliminated when the
    * program is run via an application installation file.
    */
   if(p$mfile("LFNT.MNU", stat) != 1)
      my_err("Failed to open LFNT.MNU");
   if(p$hfile("LFNT.HLP", "LFMAIN", stat) != 1)
      my_err("Failed to open LFNT.HLP");
#endif
   np = NULL;
   while(np == NULL)
      {
      display("LFMAIN");
      switch(stat[0])
         {
         case 1:	/* DO key */
            switch(stat[1])		/*** HACK -- use action string!! ***/
               {
               case 1:
                  np = "ASCII"; break;
               case 2:
                  np = "SPCGRF"; break;
               case 3:
                  np = "SUPGRF"; break;
               case 4:
                  np = "GRKMTH"; break;
               case 5:
                  np = "USER1"; break;
               case 6:
                  np = "USER2"; break;
               case 7:
                  np = "USER3"; break;
               case 8:
                  np = "USER4"; break;
               default:
                  msg1 = unsup;
               }
            break;

         case -14:	/* Function key */
            switch(stat[1])
               {
               case 10:                 /* Exit */
               case 9:                  /* Main Screen */
                  printf("%s", clrseq); fflush(stdout);
                  p$hclose(stat);
                  p$mclose(stat);
                  exits(1);

               default:
                  msg1 = wrgkey;
               }
            break;

         default:
            my_error("MENU error");
         }
      }

#else
   if(argc < 3)				/* Bad command line */
      exits(1);				/* Silently exit */
   else
      np = argv[1];			/* OK, font name is first arg */
#endif

#ifdef DEBUG
   printf("Font name \"%s\"\n", np);
#endif

   /*
    * Map to the VDFNTS partition via APR4
    */
   if(atrg(&vrdb) != IS_SUC)
     my_err("Attach failed.  Status = %d", $dsw);
   vwdb.w_nsiz = vrdb.r_gsiz;
   vwdb.w_nrid = vrdb.r_gid;
   if(craw(&vwdb) != IS_SUC)
     my_err("Map failed.  Status = %d", $dsw);

   hdr = (struct fnt_hdr *)vwdb.w_nbas;		/* Point to header */
   fntnum = -1;				/* Font header not yet seen */

#ifdef DEBUG
   printf("VDFNTS partition mapped at %06o with size %06o\n",
      hdr, vwdb.w_nsiz * 64);
   printf("There are %d fonts at %06o, %06o and %06o\n",
     hdr->n_fnts, hdr->bitmap[0], hdr->bitmap[1], hdr->bitmap[2]);
   printf(" and %d character sets at %06o, %06o and %06o\n",
     hdr->n_indx, hdr->index[0], hdr->index[1], hdr->index[2]);
#endif

   /*
    * Determine the font file(s) to open, and whether they
    * are binary or ASCII format.
    */
   bflavor = "80.DAT";					/* Do 80-col first */
   aflavor = "80.FNT";
   pass = 1;
nxtflavor:						/* Branch-back */

   concat(filename, FONT_LOC, np, bflavor, 0);		/* Make a binary name */
   if((ffp = fopen(filename, "run")) == NULL)		/* Try for binary */
      {
      concat(filename, FONT_LOC, np, aflavor, 0);	/* Make ASCII name */
      if((ffp = fopen(filename, "r")) == NULL)		/* Try for ASCII */
         my_err("Missing font file", $$ferr);
      else
         bin_fmt = FALSE;
      }
   else
      bin_fmt = TRUE;

   /*
    * Load the font/character set.  THERE IS NO ERROR CHECKING, THE
    * FILE'd BETTER DAMN WELL BE CORRECT!!
    */
   while(!feof(ffp))			/* Read till EOF */
      {
      if(fget(buf, 64, ffp) == EOF)	/* Quit if EOF encountered here */
         break;

      if(bin_fmt)			/* BINARY FLAVOR */
         {
         wp = (word *)buf;			/* Make a word pointer */
         if(fntnum == -1)			/* First record */
            {					/* (font header) */
            fntnum = *wp++;			/* Font number, */
            nbpr = *wp++;  			/* # bytes per row */
            nbpc = *wp;				/* # bytes/char */
            continue;				/* Go back & read next rec */
            }
         else					/* Character definition rec */
            {					/* so get its index info */
            charset = *wp++;			/* Character set */
            chcode  = *wp++;			/* Character code for index */
            idxval = *wp++;			/* Offset in index to bash */
            bp = (char *)wp;			/* bp & wp --> 1st bitmap */
            }
         }
      else				/* ASCII FLAVOR */
         {
         if(buf[0] == '[')			/* If is font header line */
            {
            sscanf(buf, "[%d %d %d", &fntnum, &nbpr, &nbpc); /* Get info */
            continue;
            }
         if(buf[0] != '<')			/* Read lines till start-cell */
            continue;
         if(fntnum == -1)			/* NO FONT HEADER??? */
            my_err("Missing font header", 0);
         sscanf(buf, "<%d %d %d", &charset, &chcode, &idxval);
         }

#ifdef DEBUG
      printf("Code %d  Character set %d  Index %d\n",
         chcode, charset, idxval);
#endif


      if(chcode > 95 || idxval > 221)		/* Check values */
         {
         printf("Code %h  Character set %d  Index %h:\n",
            chcode, charset, idxval);
         my_err("Illegal cell map in PXL file", 0);
         }

#ifdef DEBUG
      printf("Loading index entry %d (%06o) with %d\n",
         chcode, &(hdr->index[charset][chcode]), idxval);
#endif

      hdr->index[charset][chcode] = idxval;	/* Load the mapping index */
      if(idxval == 0)				/* If undefined character */
         continue;				/* Finished, ignore noise */

      /*
       * Point to the cell map to be bashed and
       * read in pixel rows, bashing the cell.
       */
      pxlp = hdr->bitmap[fntnum] + (nbpc * idxval);
      nbits = nbpr << 3;
      for(i=0; i<nbpc/nbpr; i++)
         {
         if(bin_fmt)			/* BINARY FLAVOR */
            {
            if(nbpr == 1)			/* Byte sized rows */
               bits = *bp++;
	    else
               bits = *wp++;
            }
         else				/* ASCII FLAVOR */
            {
            if(fget(buf, 64, ffp) == EOF)	/* Read a pixel row */
               my_err("Unexpected EOF", 0);
            bits = 0;
            cp = buf + nbits;			/* Decode row data to bin */
            for(j=0; j<(nbits-1); j++)
               {
               if(*(--cp) == '*')
                  bits |= 1;
               bits <<= 1;
               }
            if(*(--cp) == '*')
               bits |= 1;
            }

#ifdef DEBUG
         printf("Loc %06o: %06o\n", pxlp, bits);
#endif

         if(nbpr == 2)
            *((word *)(pxlp)) = bits;
         else
            *pxlp = bits & 0377;
         pxlp += nbpr;
         }
      }

#ifdef INTERACTIVE
   /*
    * Display the new font
    */
   printf(clrseq);
   if(nbpr == 1)
      printf("\033[?3h");		/* 80-column -> 132 */
   switch(charset)			/* HACK - ASSUMES ALL SAME SET */
      {
      case 0: i = 'B'; break;	/* G0 is ASCII (default) */
      case 1: i = '0'; break;	/* G0 is Special Graphics */
      case 2: i = '<';		/* G0 is Supplemental Graphics */
      }
   printf("\033(%c\n\n\t", i);
   for(i=0; i<6; i++)
      {
      for(j=0; j<16; j++)
         printf("%c ", 040 + (16 * i) + j);
      printf("\n\t");
      }
   if(pass == 1)
      printf("\033(B\nPress RETURN to see the 132-column set ...");
   else
      printf("\033(B\nPress RETURN to exit ...");
   fflush(stdout);
   gets(buf);
   if(nbpr == 1)			/* 132-column -> 80 */
      {
      printf("\033[?3l");
      fflush(stdout);
      }
#endif
   if(pass++ == 1)			/* If we just did the 80-column */
      {
      fclose(ffp);
      fntnum = -1;			/* Reset to see header in bin file! */
      aflavor = "132.FNT";
      bflavor = "132.DAT";
      goto nxtflavor;			/* Go back for 132-column */
      }
   else
      exits(1);
   }

/*
 * MY_ERR - Handle error conditions
 *
 * Non-interactive  version silently returns with severe error,
 * otherwise calls FATLER for application error.
 */
my_err(fmt, val)
char *fmt;
int val;
   {
   char mb[80];

   sprintf(mb, fmt, val);
#ifdef DEBUG
   printf(fmt, val);
#endif
#ifdef INTERACTIVE
   p$fatler(mb);
#endif
   exits(EX$SEV);
   }

#ifdef INTERACTIVE
/*
 * DISPLAY - Load and display selected menu
 */
display(frame)
char *frame;
   {
   if(p$mframe(frame, action, sizeof(action), stat) != 1)
      my_err("MFRAME error");
   p$menu(action, sizeof(action), FALSE, msg1, msg2, stat);
   }
#endif
