/*
  pktopbm, adapted from "pktopx in C by Tomas Rokicki" by AJCD 1/8/90
  
  compile with: cc -lpbm -o pktopbm pktopbm.c
  */

#include <stdio.h>
#include <pbm.h>

#define NAMELENGTH 80
#define MAXROWWIDTH 3200
#define MAXPKCHAR 256

#define round(a) ((int)(a+0.5))

typedef int integer ;
typedef unsigned char quarterword ;
typedef char boolean ;
typedef quarterword eightbits ;
typedef FILE *bytefile ;

bytefile pkfile ;
char pkname[NAMELENGTH+1] ;
integer pkloc ;
integer i, j ;
char *filename[MAXPKCHAR] ;
bit **bitmap = NULL ;
integer dynf ;
eightbits inputbyte ;
eightbits bitweight ;
integer repeatcount ;
integer flagbyte ;

add_suffix(name, suffix)
     char *name, *suffix ;
{
  int haveext = 0;
  if (name) {
    while (*name) {
      if (*name == '/') haveext = 0 ;
      else if (*name == '.') haveext = 1 ;
      name++ ;
    }
    if (!haveext) {
      *name++ = '.';
      strcpy(name,suffix) ;
    }
  }
}

initialize()
{
  integer i ;
  
  fprintf(stderr, "This is PKtoPBM, C Version 2.3\n") ;
  for (i = 0 ; i < MAXPKCHAR ; i ++) filename[i] = NULL ;
}

jumpout()
{
  exit(1) ;
}

openpkfile()
{
  if ((pkfile = fopen(pkname, "r")) == NULL) {
    fprintf(stderr, " Can't open pk file %s!\n", pkname) ;
  }
  pkloc = 0 ;
}

eightbits pkbyte()
{
  pkloc++ ;
  return(getc(pkfile)) ;
}

integer get16()
{
  integer a = pkbyte() ;
  return((a<<8) + pkbyte()) ;
}

integer get32()
{
  integer a = get16() ;
  if (a > 32767) a -= 65536 ;
  return((a<<16) + get16()) ;
}

integer getnyb()
{
  eightbits temp ;
  if (bitweight == 0) {
    inputbyte = pkbyte() ;
    bitweight = 16 ;
  }
  temp = inputbyte / bitweight ;
  inputbyte -= temp * bitweight ;
  bitweight >>= 4 ;
  return(temp) ;
}

boolean getbit()
{
  boolean temp ;
  bitweight >>= 1 ;
  if (bitweight == 0) {
    inputbyte = pkbyte() ;
    bitweight = 128 ;
  }
  temp = (inputbyte >= bitweight) ;
  if (temp) inputbyte -= bitweight ;
  return(temp) ;
}

integer pkpackednum()
{
  integer i, j ;
  i = getnyb() ;
  if (i == 0) {
    do {
      j = getnyb() ;
      i++ ;
    } while (!(j != 0)) ;
    while (i > 0) {
      j = (j<<4) + getnyb() ;
      i-- ;
    }
    return(j - 15 +((13 - dynf)<<4) + dynf) ;
  } else if (i <= dynf) return(i) ;
  else if (i < 14) return(((i - dynf - 1)<<4) + getnyb() + dynf + 1) ;
  else {
    if (i == 14) repeatcount = pkpackednum() ;
    else repeatcount = 1 ;
    return(pkpackednum()) ;
  }
}

skipspecials()
{
  integer i, j, k ;
  do {
    flagbyte = pkbyte() ;
    if (flagbyte >= 240)
      switch(flagbyte) {
      case 240:
      case 241:
      case 242:
      case 243:
	i = 0 ;
	for (j = 240 ; j <= flagbyte ; j ++) i = (i<<8) + pkbyte() ;
	for (j = 1 ; j <= i ; j ++) k = pkbyte() ;
	break ;
      case 244:
	i = get32() ;
	break ;
      case 245:
	break ;
      case 246:
	break ;
      case 247:
      case 248:
      case 249:
      case 250:
      case 251:
      case 252:
      case 253:
      case 254:
      case 255:
	fprintf(stderr, " Unexpected flag byte %d!\n", flagbyte) ;
	jumpout() ;
      }
  } while (!((flagbyte < 240) || (flagbyte == 245))) ;
}

usage()
{
  fprintf(stderr, " Usage: pktopbm pkfile[.pk] [[-c num] pbmfile]...\n");
  jumpout() ;
}

dialog(gargc, gargv)
     int gargc ;
     char **gargv ;
{
  integer car ;
  
  if (--gargc < 1) usage() ;
  strcpy(pkname, *++gargv) ;
  add_suffix(pkname, "pk") ;
  car = 0 ;
  while (++gargv, --gargc) {
    if (gargv[0][0] == '-' && gargv[0][1])
      switch (gargv[0][1]) {
      case 'c':
	if (gargv[0][2]) car = atoi(*gargv+2) ;
	else if (++gargv, --gargc) car = atoi(*gargv) ;
	else usage() ;
	break ;
      default:
	usage() ;
      } else if (car < 0 || car >= MAXPKCHAR) {
	fprintf(stderr, " Character must be in range 0 to %d (-c)!\n",
		MAXPKCHAR-1) ;
	jumpout() ;
      } else filename[car++] = *gargv ;
  }
}

main(argc, argv)
     int argc ;
     char *argv[] ;
{
  integer endofpacket ;
  integer designsize ;
  integer checksum ;
  integer hppp, vppp ;
  integer cheight, cwidth ;
  integer horesc ;
  integer packetlength ;
  integer rowsleft ;
  boolean turnon ;
  integer hbit ;
  integer count ;
  integer rp ;
  integer i, j, k ;
  integer car ;
  bit row[MAXROWWIDTH+1] ;
  
  initialize() ;
  dialog(argc, argv) ;
  openpkfile() ;
  if (pkbyte() != 247) {
    fprintf(stderr, " Bad pk file (pre command missing)!\n") ;
    jumpout() ;
  }
  if (pkbyte() != 89) {
    fprintf(stderr, " Wrong version of packed file!\n") ;
    jumpout() ;
  }
  j = pkbyte() ;
  for (i = 1 ; i <= j ; i ++) k = pkbyte() ;
  designsize = get32() ;
  checksum = get32() ;
  hppp = get32() ;
  vppp = get32() ;
  if (hppp != vppp) fprintf(stderr, " Warning: aspect ratio not 1:1!\n") ;
  skipspecials() ;
  while (flagbyte != 245) {
    dynf = (flagbyte>>4) ;
    flagbyte &= 15 ;
    turnon = (flagbyte >= 8) ;
    if (turnon) flagbyte &= 7 ;
    if (flagbyte == 7) {
      packetlength = get32() ;
      car = get32() ;
      endofpacket = packetlength + pkloc ;
      if ((car >= MAXPKCHAR) || (car < 0)) goto lab9997 ;
      i = get32() ; /* tfmwidth */
      horesc = get32() ;
      i = get32() ;
      cwidth = get32() ;
      cheight = get32() ;
      if ((cwidth < 0) || (cheight < 0) || (cwidth > 65535) || (cheight > 65535)) goto lab9997 ;
      i = get32() ;
      j = get32() ;
    } else if (flagbyte > 3) {
      packetlength =((flagbyte - 4)<<16) + get16() ;
      car = pkbyte() ;
      endofpacket = packetlength + pkloc ;
      if (car >= MAXPKCHAR) goto lab9997 ;
      i = pkbyte() ; /* tfmwidth */
      i = get16() ;
      horesc = get16() ;
      cwidth = get16() ;
      cheight = get16() ;
      i = get16() ;
      j = get16() ;
    } else {
      packetlength = (flagbyte<<8) + pkbyte() ;
      car = pkbyte() ;
      endofpacket = packetlength + pkloc ;
      if (car >= MAXPKCHAR) goto lab9997 ;
      i = pkbyte() ; /* tfmwidth */
      i = get16() ;
      horesc = pkbyte() ;
      cwidth = pkbyte() ;
      cheight = pkbyte() ;
      i = pkbyte() ;
      j = pkbyte() ;
    }
    if (filename[car]) {
      bitmap = pbm_allocarray(cwidth, cheight) ;
      if (bitmap == NULL) {
	fprintf(stderr, " Out of memory allocating bitmap!\n") ;
	jumpout() ;
      }
    } else goto lab9997 ;
    bitweight = 0 ;
    if (dynf == 14) {
      for (i = 0 ; i < cheight ; i ++)
	for (j = 0 ; j < cwidth ; j ++) {
	  if (getbit())
	    bitmap[i][j] = PBM_BLACK ;
	  else
	    bitmap[i][j] = PBM_WHITE ;
	}
    } else {
      rowsleft = cheight ;
      hbit = cwidth ;
      repeatcount = rp =0 ;
      while (rowsleft > 0) {
	count = pkpackednum() ;
	while (count > 0) {
	  if (count < hbit) {
	    hbit -= count ;
	    while (count--) {
	      if (turnon)
		row[rp++] = PBM_BLACK ;
	      else
		row[rp++] = PBM_WHITE ;
	    }
	  } else {
	    count -= hbit ;
	    while (hbit--) {
	      if (turnon)
		row[rp++] = PBM_BLACK ;
	      else
		row[rp++] = PBM_WHITE ;
	    }
	    for (i = 0; i <= repeatcount; i++)
	      for (j = 0; j < cwidth; j++)
		bitmap[i + cheight-rowsleft][j] = row[j] ;
	    rowsleft -= repeatcount + 1;
	    repeatcount = rp = 0 ;
	    hbit = cwidth ;
	  }
	}
	turnon = ! turnon ;
      }
      if ((rowsleft != 0) || (hbit != cwidth)) {
	fprintf(stderr, " Bad pk file (more bits than required)!\n") ;
	jumpout() ;
      }
    }
    if (endofpacket != pkloc) {
      fprintf(stderr, " Bad pk file (bad packet length)!\n") ;
      jumpout() ;
    }
    /* output bitmap to file */
    {
      FILE *fp ;
      if (!strcmp(filename[car], "-"))
	fp = stdout ;
      else {
	if ((fp = fopen(filename[car], "w")) == NULL) {
	  fprintf(stderr, " Can't open file %s!\n", filename[car]) ;
	  jumpout() ;
	}
      }
      filename[car] = NULL;
      pbm_writepbm(fp, bitmap, cwidth, cheight, 0) ;
      pbm_freearray(bitmap, cheight) ;
      if (fp != stdout) (void)fclose(fp) ;
    }
    goto lab9998 ;
  lab9997: while (pkloc != endofpacket) i = pkbyte() ;
    if (car < 0 || car >= MAXPKCHAR)
      fprintf(stderr, " Character %d out of range!\n", car) ;
  lab9998: skipspecials() ;
  }
  while (! feof(pkfile)) i = pkbyte() ;
  pkloc-- ;
  for (car = 0; car < MAXPKCHAR; car++)
    if (filename[car])
      fprintf(stderr, " Warning: No character in position %d (file %s).\n",
	      car, filename[car]) ;
  fprintf(stderr, "%d bytes read from packed file.\n", pkloc) ;
  exit(0);
}
