/**************************************/
/* bmfc.c                             */
/* for BMFC 0.00                      */
/* Copyright 1992 by Adam M. Costello */
/**************************************/


#include <limits.h>

#if UCHAR_MAX != 255
#error ---------------------------------------
#error Danger!  Danger!  UCHAR_MAX is not 255!
#error ---------------------------------------
#endif

#include "build.h"
#include "error.h"
#include <stdlib.h>


static const char

  /* Error messages: */

  *const cantopenread  = "Unable to open file \"%s\" for reading.\n",
  *const cantopenwrite = "Unable to open file \"%s\" for writing.\n",
  *const cantwrite     = "Unable to write to file.\n",

  /* Warning messages: */

  *const cantclose     = "Unable to close file \"%s\".\n",
  *const success       = "Compilation successful.\n",
  *const outputto      = "Output written to \"%s\".\n";

static FILE *writefile;  /* Only for use by write...() functions. */


static void writeubyte(unsigned char b)  /* Writes b to writefile. */
{
  if (putc(b,writefile) == EOF) failf(cantwrite);
}


static void writeubytes(int n, unsigned char *b)  /* Writes n bytes from */
{                                                 /* *b to writefile.    */
  while (n--) writeubyte(*b++);
}


static void writeuword(unsigned short w)  /* Write w to writefile, */
{                                         /* high byte first.      */
  writeubyte((w & 0xFF00) >> 8);
  writeubyte(w & 0xFF);
}


static void writeword(short w)  /* Write w to writefile, high byte first. */
{
  writeuword(w >= 0 ? w : w + 0x10000);
}


static void writeulong(unsigned short L)  /* Write L to writefile, */
{                                         /* high byte first.      */
  writeubyte((L & 0xFF000000) >> 24);
  writeubyte((L & 0xFF0000) >> 16);
  writeubyte((L & 0xFF00) >> 8);
  writeubyte(L & 0xFF);
}


#define writeptr(p)  writeulong(p)


static void writeloadfile(FILE *loadfile, struct font *thefont)
{
  unsigned long  *theparams = thefont->parameters, cf = theparams[colorfont],
                 d = theparams[depth], rp = theparams[revpath], plane, p,
                 pixmapwidth, roundedpmw, fontname, fontdata, fontloc,
                 fontspace, fontkern, fontcolors, colortable, fontend, codeend;
  unsigned short *thewidths = thefont->widths, *A, *B, *C, *offset, *pwidth,
                 *pA, *lastA, *pB, *pC, *poffset, *lastoffset, defA, defB,
                 defC, defoffset, ys = thefont->ysize, modulo, w;
  unsigned char  **theglyphs = thefont->glyphs, **pglyph, **lastglyph,
                 *prow, *lastrow, *pmrow, *ppix, *lastpix, *pmpix, *pixmap,
                 lowchar, highchar, bitmask;
  unsigned int   numglyphs, defchar;

  for (pglyph = theglyphs; !*pglyph; ++pglyph);
  lowchar = pglyph - theglyphs;
  for (pglyph = theglyphs + 255; !*pglyph; --pglyph);
  highchar = pglyph - theglyphs;
  defchar = highchar + 1;
  theglyphs[defchar] = theglyphs[256];
  thewidths[defchar] = thewidths[256];
  numglyphs = defchar - lowchar + 1;

  A = (unsigned short *) malloc(numglyphs * sizeof (unsigned short));
  B = (unsigned short *) malloc(numglyphs * sizeof (unsigned short));
  C = (unsigned short *) malloc(numglyphs * sizeof (unsigned short));
  offset = (unsigned short *) malloc(numglyphs * sizeof (unsigned short));

  if (!A || !B || !C || !offset) failf(outofmem);

  /* Figure out offsets and A, B, and C values: */

  for (pglyph = theglyphs + lowchar, pwidth = thewidths + lowchar, pA = A,
         pB = B, pC = C, poffset = offset, pixmapwidth = 0,
         lastglyph = theglyphs + defchar;
       pglyph <= lastglyph;
       ++pglyph, ++pwidth, ++pA, ++pB, ++pC, ++poffset) {
    if (*pglyph) {
      if (!*pwidth) *pA = *pB = *pC = 0;
      else {
        *pA = *pC = w = *pwidth;
        for (prow = *pglyph, lastrow = prow + ys * w;
             prow < lastrow;
             prow += w)
          for (ppix = prow, lastpix = prow + w - 1;
               ppix <= lastpix;
               ++ppix)
            if (*ppix) {
              if (ppix - prow < *pA) *pA = ppix - prow;
              if (lastpix - ppix < *pC) *pC = lastpix - ppix;
            }
        if (*pC == w) *pA = 0;
        *pB = w - *pC - *pA;
      }
      *poffset = pixmapwidth;
      pixmapwidth += *pB;
    }
  }

  /* Replicate default glyph onto undefined glyphs: */

  defoffset = poffset[-1];
  defA = pA[-1];
  defB = pB[-1];
  defC = pC[-1];

  for (pglyph = theglyphs + lowchar, poffset = offset, pA = A, pB = B, pC = C,
         lastglyph = theglyphs + highchar;
       pglyph <= lastglyph;
       ++pglyph, ++poffset, ++pA, ++pB, ++pC)
    if (!*pglyph) {
      *poffset = defoffset;
      *pA = defA;
      *pB = defB;
      *pC = defC;
    }

  modulo = ((pixmapwidth + 15) / 16) * 2;
  roundedpmw = modulo * 8;

  pixmap = (unsigned char *) malloc(ys * roundedpmw * sizeof (unsigned char));
  if (!pixmap) failf(outofmem);

  /* Construct pixmap: */

  for (pglyph = theglyphs + lowchar, pwidth = thewidths + lowchar, pA = A,
         pB = B, poffset = offset, lastglyph = theglyphs + defchar;
       pglyph <= lastglyph;
       ++pglyph, ++pwidth, ++pA, ++pB, ++poffset)
    if (*pglyph) {
      w = *pwidth;
      for (prow = *pglyph, pmrow = pixmap + *poffset, lastrow = prow + ys * w;
           prow < lastrow;
           prow += w, pmrow += roundedpmw)
        for (ppix = prow + *pA, pmpix = pmrow, lastpix = ppix + *pB;
             ppix < lastpix;
             ++ppix, ++pmpix)
          *pmpix = *ppix;
    }

  for (pmrow = pixmap + pixmapwidth, lastrow = pmrow + (ys - 1) * roundedpmw;
       pmrow <= lastrow;
       pmrow += roundedpmw)
    for (pmpix = pmrow, lastpix = pmrow + (roundedpmw - pixmapwidth);
         pmpix < lastpix;
         ++pmpix)
      *pmpix = 0;

  fontname = 26;
  fontdata = cf ? 154 : 110;
  fontloc = fontdata + d * ys * modulo;
  fontspace = fontloc + numglyphs * 4;
  fontkern = fontspace + numglyphs * 2;
  fontend = fontkern + numglyphs * 2;
  if (cf) {
    fontcolors = fontend;
    colortable = fontcolors + 8;
    fontend = colortable + thefont->numcolors * 2;
  }
  codeend = ((fontend + 3) / 4) * 4;

  /* Write the load file. */

  writefile = loadfile;

  /* hunk_header: */

  writeubytes(20, "\0\0\x03\xF3\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0");
  writeulong((fontend + 3) / 4);

  /* hunk_code: */

  writeulong(0x3E9);
  writeulong((fontend + 3) / 4);

  /* dfh_ReturnCode: */

  writeubyte(0x70);
  writeubyte(theparams[returncode]);
  writeuword(0x4E75);

  /* dfh_DF: */

  writeubytes(10, "\0\0\0\0\0\0\0\0\x0C\0");
  writeptr(fontname);

  writeuword(0x0F80);               /* dfh_FileID                 */
  writeuword(theparams[revision]);  /* dfh_Revision               */
  writeptr(0);                      /* dfh_Segment or dfh_Taglist */
  writeubytes(32, thefont->name);   /* dfh_Name                   */

  /* dfh_TF.tf_Message.mn_Node: (just like dfh_DF) */

  writeubytes(10, "\0\0\0\0\0\0\0\0\x0C\0");
  writeptr(fontname);

  writeptr(0);     /* dfh_TF.tf_Message.mn_Replyport */
                   /*   or dfh_TF.tf_Extension       */
  writeuword(0);   /* dfh_TF.tf_Message.mn_Length    */
  writeuword(ys);  /* dfh_TF.tf_YSize                */

  /* dfh_TF.tf_Style: */

  writeubyte(theparams[underlined] +
             2 * theparams[bold] +
             4 * theparams[italic] +
             8 * theparams[extended] +
             64 * theparams[colorfont]);

  /* dfh_TF.tf_Flags: */

  writeubyte(66 + 4 * theparams[revpath] +
             8 * theparams[talldot] +
             16 * theparams[widedot] +
             32 * theparams[proportional] );

  writeuword(theparams[xsize]);      /* dfh_TF.tf_XSize     */
  writeuword(theparams[baseline]);   /* dfh_TF.tf_Baseline  */
  writeuword(theparams[boldsmear]);  /* dfh_TF.tf_Boldsmear */
  writeuword(0);                     /* dfh_TF.tf_Accessors */
  writeubyte(lowchar);               /* dfh_TF.tf_LowChar   */
  writeubyte(highchar);              /* dfh_TF.tf_HighChar  */
  writeptr(fontdata);                /* dfh_TF.tf_CharData  */
  writeword(modulo);                 /* dfh_TF.tf_Modulo    */
  writeptr(fontloc);                 /* dfh_TF.tf_CharLoc   */
  writeptr(fontspace);               /* dfh_TF.tf_CharSpace */
  writeptr(fontkern);                /* dfh_TF.tf_CharKern  */

  if (cf) {
    unsigned long cfdata, planesize = ys * modulo;
    int plane;

    /* dfh_TF.ctf_Flags: */

    writeuword(thefont->usedcolors +
               2 * theparams[greyfont] +
               4 * theparams[antialias] );

    writeubyte(d);                      /* dfh_TF.ctf_Depth           */
    writeubyte(theparams[fgcolor]);     /* dfh_TF.ctf_FgColor         */
    writeubyte(theparams[low]);         /* dfh_TF.ctf_Low             */
    writeubyte(theparams[high]);        /* dfh_TF.ctf_High            */
    writeubyte(theparams[planepick]);   /* dfh_TF.ctf_PlanePick       */
    writeubyte(theparams[planeonoff]);  /* dfh_TF.ctf_PlaneOnOff      */
    writeptr(fontcolors);               /* dfh_TF.ctf_ColorFontColors */

    /* dfh_TF.ctf_CharData: */

    for (cfdata = fontdata; cfdata < fontloc; cfdata += planesize)
      writeptr(cfdata);
    for (plane = d; plane < 8; ++plane)
      writeptr(0);
  }

  /* *dfh_TF.tf_CharData: */

  for (plane = 0, bitmask = 1; plane < d; ++plane, bitmask <<= 1)
    for (pmrow = pixmap, lastrow = pixmap + ys * roundedpmw;
         pmrow < lastrow;
         pmrow += roundedpmw)
      for (pmpix = pmrow, lastpix = pmrow + roundedpmw;
           pmpix < lastpix;
           pmpix += 8)
        writeubyte(  ((pmpix[0] & bitmask) >> plane) << 7
                   | ((pmpix[1] & bitmask) >> plane) << 6
                   | ((pmpix[2] & bitmask) >> plane) << 5
                   | ((pmpix[3] & bitmask) >> plane) << 4
                   | ((pmpix[4] & bitmask) >> plane) << 3
                   | ((pmpix[5] & bitmask) >> plane) << 2
                   | ((pmpix[6] & bitmask) >> plane) << 1
                   | ((pmpix[7] & bitmask) >> plane) << 0
                  );

  /* *dfh_TF.tf_CharLoc: */

  for (poffset = offset, pB = B, lastoffset = offset + numglyphs;
       poffset < lastoffset;
       ++poffset, ++pB) {
    writeuword(*poffset);
    writeuword(*pB);
  }

  /* *dfh_TF.tf_CharSpace: */

  for (pA = A, pB = B, pC = C, lastA = A + numglyphs;
       pA < lastA;
       ++pA, ++pB, ++pC)
    writeuword(rp ? -*pA : *pB + *pC);

  /* *dfh_TF.tf_CharKern: */

  for (pA = A, pB = B, pC = C, lastA = A + numglyphs;
       pA < lastA;
       ++pA, ++pB, ++pC)
    writeuword(rp ? -*pB - *pC : *pA);

  if (cf) {
    unsigned short *pcolor, *lastcolor;

    /* fields of *dfh_TF.ctf_ColorFontColors: */

    writeuword(0);                   /* cfc_Reserved   */
    writeuword(thefont->numcolors);  /* cfc_Count      */
    writeptr(colortable);            /* cfc_ColorTable */

    /* *dfh_TF.ctf_ColorFontColors->cfc_ColorTable: */

    for (pcolor = thefont->colorvals, lastcolor = pcolor + thefont->numcolors;
         pcolor < lastcolor;
         ++pcolor)
      writeuword(*pcolor);
  }

  /* pad hunk_code: */

  for (p = fontend; p < codeend; ++p) writeubyte(0);

  /* hunk_reloc32: */

  writeulong(0x3EC);
  writeulong(cf ? 16 : 6);
  writeulong(0);
  writeubytes(24,
              "\0\0\0\x0E\0\0\0\x44\0\0\0\x5C\0\0\0\x62\0\0\0\x66\0\0\0\x6A");
  if (cf) {
    for (p = 118; p < 154; p += 4) writeptr(p);
    writeptr(fontcolors + 4);
  }

  writeulong(0);

  /* hunk_end: */

  writeulong(0x3F2);
}


void usage(unsigned char **argv)  /* Prints usage message and exits. */
{
  warnf("usage:\n%s [-o <loadfile>] <srcfile>\n", *argv ? *argv : "bmfc");
  exit(EXIT_FAILURE);
}


main(int argc, char **argv)
{
  const char *srcfilename = NULL, *loadfilename = NULL, **arg = argv;
  char lfnspace[6];
  FILE *srcfile = NULL, *loadfile = NULL;
  struct font thefontspace, *thefont = &thefontspace;

  if (!*arg) usage(argv);

  for (;;) {
    if (!*++arg) usage(argv);
    if (**arg != '-') break;
    if ((*arg)[1] != 'o' || (*arg)[2] != '\0' || !*++arg) usage(argv);
    loadfilename = *arg;
  }

  srcfilename = *arg;

  if (*++arg) usage(argv);

  srcfile = fopen(srcfilename, "rb");
  if (!srcfile) failf(cantopenread,srcfilename);

  build(srcfile,thefont);

  if (fclose(srcfile)) warnf(cantclose,srcfilename);
  srcfile = NULL;

  if (!loadfilename) {
    sprintf(lfnspace, "%hu", thefont->ysize);
    loadfilename = lfnspace;
  }

  loadfile = fopen(loadfilename, "wb");
  if (!loadfile) failf(cantopenwrite,loadfilename);

  writeloadfile(loadfile,thefont);

  if (fclose(loadfile)) warnf(cantclose,loadfilename);
  loadfile = NULL;

  warnf(success);
  warnf(outputto,loadfilename);
  exit(EXIT_SUCCESS);
}
