/* xbitmap.c:
 *
 * at one time this was XRdBitF.c.  it bears very little resemblence to it
 * now.  that was ugly code.  this is cleaner, faster, and more reliable
 * in most cases.
 *
 * jim frost 10.06.89
 *
 * Copyright, 1987, Massachusetts Institute of Technology
 *
 * Copyright 1989 Jim Frost.  See included file "copyright.h" for complete
 * copyright information.
 */

#include "mit.cpyrght"
#include "copyright.h"
#include "image.h"
#include <ctype.h>

/* SUPPRESS 560 */

#if defined(SYSV) || defined(VMS) || 1
#include <string.h>
#define rindex strrchr
#else
char *rindex();
#endif

#define MAX_SIZE 255

static short        HexTable[256];  /* conversion value */
static unsigned int Initialized= 0; /* easier to fill in at run time */

#define b0000 0 /* things make more sense if you see them by bit */
#define b0001 1
#define b0010 2
#define b0011 3
#define b0100 4
#define b0101 5
#define b0110 6
#define b0111 7
#define b1000 8
#define b1001 9
#define b1010 10
#define b1011 11
#define b1100 12
#define b1101 13
#define b1110 14
#define b1111 15

#define HEXSTART -1
#define HEXDELIM -2
#define HEXBAD   -3

/* build a hex digit value table with the bits inverted
 */

static void initHexTable()
{ int a;

  for (a= 0; a < 256; a++)
    HexTable[a]= HEXBAD;

  HexTable['0']= b0000;
  HexTable['1']= b1000;
  HexTable['2']= b0100;
  HexTable['3']= b1100;
  HexTable['4']= b0010;
  HexTable['5']= b1010;
  HexTable['6']= b0110;
  HexTable['7']= b1110;
  HexTable['8']= b0001;
  HexTable['9']= b1001;
  HexTable['A']= b0101; HexTable['a']= HexTable['A'];
  HexTable['B']= b1101; HexTable['b']= HexTable['B'];
  HexTable['C']= b0011; HexTable['c']= HexTable['C'];
  HexTable['D']= b1011; HexTable['d']= HexTable['D'];
  HexTable['E']= b0111; HexTable['e']= HexTable['E'];
  HexTable['F']= b1111; HexTable['f']= HexTable['F'];
  HexTable['x']= HEXSTART;
  HexTable['\r']= HEXDELIM;
  HexTable['\n']= HEXDELIM;
  HexTable['\t']= HEXDELIM;
  HexTable[' ']= HEXDELIM;
  HexTable[',']= HEXDELIM;
  HexTable['}']= HEXDELIM;

  Initialized = 1;
}

/* read a hex value and return its value
 */

static int nextInt(zf)
     ZFILE *zf;
{ int c;
  int value= 0;
  int shift= 0;
    
  for (;;) {
    c= zgetc(zf);
    if (c == EOF)
      return(-1);
    else {
      c= HexTable[c & 0xff];
      switch(c) {
      case HEXSTART:
	shift= 0; /* reset shift counter */
	break;
      case HEXDELIM:
	if (shift)
	  return(value);
	break;
      case HEXBAD:
	return(-1);
      default:
	value += (c << shift);
	shift += 4;
      }
    }
  }
}

static void badFile(name)
     char *name;
{
  fprintf(stderr, "%s: bad X bitmap file\n", name);
  cleanup(-1);
}

Image *xbitmapLoad(fullname, name, verbose)
     char         *fullname, *name;
     unsigned int  verbose;
{ ZFILE        *zf;
  Image        *image;
  char          line[MAX_SIZE];
  char          name_and_type[MAX_SIZE];
  char         *type;
  int           value;
  int           v10p=0;
  unsigned int  linelen, dlinelen;
  unsigned int  x, y;
  unsigned int  w = 0, h = 0;
  byte         *dataptr;

  if (!Initialized)
    initHexTable();

  if (! (zf= zopen(fullname)))
    return(NULL);

  /* get width/height values */

  while (zgets((byte *)line, MAX_SIZE, zf)) {
    if (strlen(line) == MAX_SIZE-1) {
      zclose(zf);
      return(NULL);
    }

    /* width/height/hot_x/hot_y scanning
     */

    if (sscanf(line,"#define %s %d", name_and_type, &value) == 2) {
      if (!(type = rindex(name_and_type, '_')))
	type = name_and_type;
      else
	type++;

      if (!strcmp("width", type))
	w= (unsigned int)value;
      if (!strcmp("height", type))
	h= (unsigned int)value;
    }

    /* if start of data, determine if it's X10 or X11 data and break
     */

    if (sscanf(line, "static short %s = {", name_and_type) == 1) {
      v10p = 1;
      break;
    }
    if ((sscanf(line,"static unsigned char %s = {", name_and_type) == 1) ||
	(sscanf(line, "static char %s = {", name_and_type) == 1)) {
      v10p = 0;
      break;
    }
  }

  if (!w || !h) {
    zclose(zf);
    return(NULL);
  }
  image= newBitImage(w, h);

  /* get title of bitmap if any
   */

  if ((type = rindex(name_and_type, '_')) && !strcmp("bits[]", type + 1)) {
    *type= '\0';
    image->title= dupString(name_and_type);
  }
    
  /* read bitmap data
   */

  linelen= (w / 8) + (w % 8 ? 1 : 0); /* internal line length */
  if (v10p) {
    dlinelen= (w / 8) + (w % 16 ? 2 : 0);
    dataptr= image->data;
    for (y= 0; y < h; y++) {
      for (x= 0; x < dlinelen; x++) {
	if ((value= nextInt(zf)) < 0) {
	  freeImage(image);
	  zclose(zf);
	  return(NULL);
	}
	*(dataptr++)= value >> 8;
	if (++x < linelen)
	  *(dataptr++)= value & 0xff;
      }
    }
  }
  else {
    dataptr= image->data;
    for (y= 0; y < h; y++)
      for (x= 0; x < linelen; x++) {
	if ((value= nextInt(zf)) < 0)
	  badFile(name);
	*(dataptr++)= value;
      }
  }

  if (verbose) {
    fprintf(stderr, "%s is a %dx%d X", name, image->width, image->height);
    if (v10p)
      fprintf(stderr, "10");
    else
      fprintf(stderr, "11");
    if (image->title)
      fprintf(stderr, " bitmap file titled '%s'", image->title);
    fprintf(stderr, "\n");
  }
  zclose(zf);
  return(image);
}

/* this is the easiest way to do this.  it's not likely we'll have mondo
 * x bitmaps anyway given their size
 */

int xbitmapIdent(fullname, name)
     char         *fullname, *name;
{ Image *image;

  if ((image = xbitmapLoad(fullname, name, (unsigned int)1))) {
    freeImage(image);
    return(1);
  }
  return(0);
}
