/* VICEtoPS Copyright 2004 Paul David Buchan (pdbuchan@yahoo.com)

   This program simulates Commodore (tm) dot-matrix printers
   by generating a PostScript file that can be printed on
   most modern PostScript-enabled printers. VICEtoPS takes as
   input the printer output file from the VICE (www.viceteam.org)
   Commodore emulator.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int
main (int argc, char **argv)
{
  int c, dec, i, m, nchar, row, col, offset, p[8];
  int xmin, maxchar, ymax, ymin, y;
  int linespace, pg, flag, chr[8192], *tmp, *dat;
  FILE *fi, *fo;

  if (argc != 4) {
    printf ("\nVICEtoPS - Paul David Buchan, 2004\n");
    printf ("Usage: vicetops[exe] case_flag ");
    printf ("VICE_output_filename PostScript_filename\n");
    printf ("For upper case character set, case_flag = u\n");
    printf ("For lower case character set, case_flag = l\n");
    printf ("e.g., vicetops u viceprnt.out viceprnt.ps\n\n");
    return (EXIT_SUCCESS);
  }

  if (strcmp (argv[1], "u") == 0 || strcmp (argv[1], "U") == 0) {
    offset = 0;
  } else if (strcmp (argv[1], "l") == 0 || strcmp (argv[1], "L") == 0) {
    offset = 2048;
  } else {
    printf ("\nInvalid case flag. Type vicetops[.exe] for usage.\n\n");
    exit (EXIT_FAILURE);
  }

  /* Read in character set */
  fi = fopen ("characters.390059-01.bin", "rb");
  if (fi == NULL) {
    printf ("Can't open character set file characters.390059-01.bin.\n");
    exit (EXIT_FAILURE);
  }
  i = 0;
  while ((c = fgetc (fi)) != EOF) {
    chr[i] = c;
    i++;
  }
  fclose (fi);

  /* Open VICE output file and count bytes */
  fi = fopen (argv[2], "rb");
  if (fi == NULL) {
    printf ("Can't open VICE output file.\n");
    exit (EXIT_FAILURE);
  }
  nchar = 0;
  while (fgetc (fi) != EOF) {
    nchar++;
  }
  rewind (fi);

  /* Allocate memory for arrays dat and tmp for VICE output file data */
  dat = (int *) malloc (nchar * sizeof (*dat));
  tmp = (int *) malloc (nchar * sizeof (*tmp));

  /* Read VICE output file into array dat */
  i = 0;
  while ((c = fgetc (fi)) != EOF) {
    dat[i] = c;
    i++;
  }
  fclose (fi);

  /* Remove all line-feeds from file and store in tmp */
  c = 0;
  for (i=0; i<nchar; i++) {
    if (dat[i] != 10) {
      tmp[c] = dat[i];
      c++;
    }
  }
  nchar = c;

  /* Re-map ASCII to CBM for each character in viceprnt.out */
  for (i=0; i<nchar; i++) {
    if (tmp[i] >= 0 && tmp[i] <= 31) {
      dat[i] = tmp[i] + 128;
    } else if (tmp[i] >= 32 && tmp[i] <= 63) {
      dat[i] = tmp[i];
    } else if (tmp[i] >= 64 && tmp[i] <= 95) {
      dat[i] = tmp[i] - 64;
    } else if (tmp[i] >= 128 && tmp[i] <= 159) {
      dat[i] = tmp[i] + 64;
    } else if (tmp[i] >= 160 && tmp[i] <= 191) {
      dat[i] = tmp[i] - 64;
    } else if (tmp[i] >= 192 && tmp[i] <= 223) {
      dat[i] = tmp[i] - 128;
    }
  }

  /* Write header info for PostScript file */
  fo = fopen (argv[3], "w");
  if (fo == NULL) {
    printf ("Can't open new PostScript file.\n");
    exit (EXIT_FAILURE);
  }

  fprintf (fo, "%%!PS-Adobe-3.0\n");
  fprintf (fo, "%%%%Title: Commodore Printout\n");
  fprintf (fo, "%%%%Creator: vicetops.c - Paul David Buchan, 2004\n");
  fprintf (fo, "%%%%Pages: (atend)\n");
  fprintf (fo, "%%%%Orientation: Portrait\n");
  fprintf (fo, "0.000 0.000 0.000 setrgbcolor\n");
  fprintf (fo, "8 dict begin\n");
  fprintf (fo, "/FontType 3 def\n");
  fprintf (fo, "/FontMatrix [.001 0 0 .001 0 0] def\n");
  fprintf (fo, "/FontBBox [0 0 750 750] def\n\n");
  fprintf (fo, "/Encoding 256 array def\n");
  fprintf (fo, "0 1 255 {Encoding exch /.notdef put} for\n");
  for (dec=0; dec<=255; dec++) {
    fprintf (fo, "Encoding %u /cbm%u put\n", dec, dec);
  }
  fprintf (fo, "\n/CharProcs 3 dict def\n");
  fprintf (fo, "CharProcs begin\n");
  fprintf (fo, "/.notdef {} def\n");

  /* Decode CBM character set and redefine default PostScript font set */
  m = 0;  
  for (dec=0; dec<256; dec++) {
    fprintf (fo, "/cbm%u\n", dec);
    fprintf (fo, "{ 40.0 setlinewidth\n");
    fprintf (fo, "2 setlinecap\n");
    fprintf (fo, "[] 0 setdash\n");
    for (row=0; row<8; row++) {
      for (col=0; col<8; col++) {
        p[col] = (chr[m+offset] >> (7 - col)) & 1;
      }
      m++;
      for (col=0; col<8; col++) {
        if (p[col] == 1) {
          fprintf (fo, "%.2f %.2f moveto\n", col * 93.75, (750.0 - (row * 93.75)));
          fprintf (fo, "%.2f %.2f lineto\n", (col * 93.75) + 31.25, 750.0 - (row * 93.75));
        }
      }
    }
    fprintf (fo, "stroke\n");
    fprintf (fo, "} bind def\n\n");
  }
  fprintf (fo, "end\n");
     
  /* Build new character set */
  fprintf (fo, "/BuildGlyph\n");
  fprintf (fo, "{1000 0\n");
  fprintf (fo, "0 0 750 750\n");
  fprintf (fo, "setcachedevice\n");
  fprintf (fo, "exch /CharProcs get exch\n");
  fprintf (fo, "2 copy known not\n");
  fprintf (fo, "{pop /.notdef}\n");
  fprintf (fo, "if\n");
  fprintf (fo, "get exec\n");
  fprintf (fo, "} bind def\n\n");
  fprintf (fo, "/BuildChar\n");
  fprintf (fo, "{ 1 index /Encoding get exch get\n");
  fprintf (fo, "  1 index /BuildGlyph get exec\n");
  fprintf (fo, "} bind def\n");
  fprintf (fo, "currentdict\n");
  fprintf (fo, "end\n");
  fprintf (fo, "/ExampleFont exch definefont pop\n");
  fprintf (fo, "/ExampleFont findfont 10 scalefont setfont\n");

  /* Set left margin as one inch (72 dpi) */
  xmin = 72;

  /* Set maximum number of characters per line */
  maxchar = 70;

  /* Set top and bottom page margins as one-half inch */
  ymax = 756;
  ymin = 36;

  /* Set spacing between lines */
  linespace = 12;

  /* Start printing at top of page */
  y = ymax;

  /* Print out Commodore file */
  pg = 1;
  fprintf (fo, "%%%%Page: Page %u\n", pg);
  c = 0;
  flag = 0;
  i = 0;
  while (i < nchar) {
    /* At beginning of a new line */
    if (flag == 0) {
      /* Found line-feed instead of text */
      if (dat[i] == 141) {
        y -= linespace;
        i++;
      /* Start new line of text */
      } else {
        fprintf (fo, "%u %u moveto\n", xmin, y);
        fprintf (fo, "-3 0 <");
        c = 0;
        flag = 1;
      }

    /* Continuing with a line of text already started */
    } else {
      /* Found line-feed which will terminate line of text */
      if (dat[i] == 141) {
        flag = 0;
        c = 0;
        fprintf (fo, "> ashow\n");
        i++;
        y -= linespace;
      /* Length of line has reached maximum */
      } else if (c == maxchar) {
        flag = 0;
        c = 0;
        fprintf (fo, "> ashow\n");
        y -= linespace;
      /* Print next character in file */
      } else {
        if (dat[i] < 16) {
          fprintf (fo, "0%1x", dat[i]);
        } else {
          fprintf (fo, "%2x", dat[i]);
        }
        i++;
        c++;
        /* Last character in file? */
        if (i == nchar) {
          fprintf (fo, "> ashow\n");
        }
      }
    }
    /* Reached bottom of page */
    if (y < ymin) {
      y = ymax;
      fprintf (fo, "showpage\n");
      pg++;
      fprintf (fo, "%%%%Page: Page %u\n", pg);
    }
  }

  fprintf (fo, "showpage\n");
  fprintf (fo, "%%%%Trailer\n");
  fprintf (fo, "%%%%Pages: %u\n", pg);
  fprintf (fo, "%%%%EOF\n");
  fclose (fo);

  /* De-allocate memory used for arrays dat and tmp */
  free (dat);
  free (tmp);

  return (EXIT_SUCCESS);
}
