/* xbmtopbm.c - read an X bitmap file and produce a portable bitmap
**
** Copyright (C) 1988 by Jef Poskanzer.
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose and without fee is hereby granted, provided
** that the above copyright notice appear in all copies and that both that
** copyright notice and this permission notice appear in supporting
** documentation.  This software is provided "as is" without express or
** implied warranty.
*/

#include <string.h>
#include "pbm.h"

#define TRUE 1
#define FALSE 0

static void ReadBitmapFile ARGS(( FILE* stream, int* widthP, int* heightP, char** dataP ));

int
main( argc, argv )
    int argc;
    char* argv[];
    {
    FILE* ifp;
    bit* bitrow;
    register bit* bP;
    int rows, cols, row, col, charcount;
    char* data;
    char mask;

    pbm_init( &argc, argv );

    if ( argc > 2 )
        pm_usage( "[bitmapfile]" );
    
    if ( argc == 2 )
        ifp = pm_openr( argv[1] );
    else
        ifp = stdin;

    ReadBitmapFile( ifp, &cols, &rows, &data );

    pm_close( ifp );

    pbm_writepbminit( stdout, cols, rows, 0 );
    bitrow = pbm_allocrow( cols );

    for ( row = 0; row < rows; ++row )
        {
        charcount = 0;
        mask = 1;
        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
            {
            if ( charcount >= 8 )
                {
                ++data;
                charcount = 0;
                mask = 1;
                }
            *bP = ( *data & mask ) ? PBM_BLACK : PBM_WHITE;
            ++charcount;
            mask = mask << 1;
            }
        ++data;
        pbm_writepbmrow( stdout, bitrow, cols, 0 );
        }

    pm_close( stdout );
    exit( 0 );
    }

#define MAX_LINE 500

static void
ReadBitmapFile( stream, widthP, heightP, dataP )
     FILE* stream;
     int* widthP;
     int* heightP;
     char** dataP;
{
  char line[MAX_LINE], name_and_type[MAX_LINE];
  char* ptr;
  char* t;
  int version10, raster_length, v;
  register int bytes, bytes_per_line, padding;
  register int c1, c2, value1, value2;
  int hex_table[256];
  int found_declaration;
  /* In scanning through the bitmap file, we have found the first
     line of the C declaration of the array (the "static char ..."
     or whatever line)
     */
  int eof;
  /* We've encountered end of file while searching file */

  *widthP = *heightP = -1;

  found_declaration = FALSE;    /* Haven't found it yet; haven't even looked*/
  eof = FALSE;                  /* Haven't encountered end of file yet */

  while (!found_declaration && !eof) {
    if ( fgets( line, MAX_LINE, stream ) == NULL )
      eof = TRUE;
    else {
      if ( strlen( line ) == MAX_LINE - 1 )
        pm_error( "line too long" );

      if ( sscanf( line, "#define %s %d", name_and_type, &v ) == 2 ) {
        if ( ( t = strrchr( name_and_type, '_' ) ) == NULL )
          t = name_and_type;
        else
          ++t;
        if ( ! strcmp( "width", t ) )
          *widthP = v;
        else if ( ! strcmp( "height", t ) )
          *heightP = v;
        continue;
      }
        
      if ( sscanf( line, "static short %s = {", name_and_type ) == 1 ) {
        version10 = TRUE;
        found_declaration = TRUE;
      }
      else if ( sscanf( line, "static char %s = {", name_and_type ) == 1 ) {
        version10 = FALSE;
        found_declaration = TRUE;
      }
      else if (sscanf(line, 
                      "static unsigned char %s = {", name_and_type ) == 1 ) {
        version10 = FALSE;
        found_declaration = TRUE;
      }
    }
  }
 
  if (!found_declaration) 
    pm_error("Unable to find a line in the file containing the start "
             "of C array declaration (\"static char\" or whatever)");

  if ( *widthP == -1 )
    pm_error( "invalid width" );
  if ( *heightP == -1 )
    pm_error( "invalid height" );

  padding = 0;
  if ( ((*widthP % 16) >= 1) && ((*widthP % 16) <= 8) && version10 )
    padding = 1;

  bytes_per_line = (*widthP+7)/8 + padding;
    
  raster_length =  bytes_per_line * *heightP;
  *dataP = (char*) malloc( raster_length );
  if ( *dataP == (char*) 0 )
    pm_error( "out of memory" );

  /* Initialize hex_table. */
  for ( c1 = 0; c1 < 256; ++c1 )
    hex_table[c1] = 256;
  hex_table['0'] = 0;
  hex_table['1'] = 1;
  hex_table['2'] = 2;
  hex_table['3'] = 3;
  hex_table['4'] = 4;
  hex_table['5'] = 5;
  hex_table['6'] = 6;
  hex_table['7'] = 7;
  hex_table['8'] = 8;
  hex_table['9'] = 9;
  hex_table['A'] = 10;
  hex_table['B'] = 11;
  hex_table['C'] = 12;
  hex_table['D'] = 13;
  hex_table['E'] = 14;
  hex_table['F'] = 15;
  hex_table['a'] = 10;
  hex_table['b'] = 11;
  hex_table['c'] = 12;
  hex_table['d'] = 13;
  hex_table['e'] = 14;
  hex_table['f'] = 15;

  if ( version10 )
    for ( bytes = 0, ptr = *dataP; bytes < raster_length; bytes += 2 ) {
      while ( ( c1 = getc( stream ) ) != 'x' )
        if ( c1 == EOF )
          pm_error( "EOF / read error" );
      c1 = getc( stream );
      c2 = getc( stream );
      if ( c1 == EOF || c2 == EOF )
        pm_error( "EOF / read error" );
      value1 = ( hex_table[c1] << 4 ) + hex_table[c2];
      if ( value1 >= 256 )
        pm_error( "syntax error" );
      c1 = getc( stream );
      c2 = getc( stream );
      if ( c1 == EOF || c2 == EOF )
        pm_error( "EOF / read error" );
      value2 = ( hex_table[c1] << 4 ) + hex_table[c2];
      if ( value2 >= 256 )
        pm_error( "syntax error" );
      *ptr++ = value2;
      if ( ( ! padding ) || ( ( bytes + 2 ) % bytes_per_line ) )
        *ptr++ = value1;
    }
  else
    for ( bytes = 0, ptr = *dataP; bytes < raster_length; ++bytes ) {
      /*
       ** Skip until digit is found.
       */
      for ( ; ; )
        {
          c1 = getc( stream );
          if ( c1 == EOF )
            pm_error( "EOF / read error" );
          value1 = hex_table[c1];
          if ( value1 != 256 )
            break;
        }
      /*
       ** Loop on digits.
       */
      for ( ; ; ) {
        c2 = getc( stream );
        if ( c2 == EOF )
          pm_error( "EOF / read error" );
        value2 = hex_table[c2];
        if ( value2 != 256 ) {
          value1 = (value1 << 4) | value2;
          if ( value1 >= 256 )
            pm_error( "syntax error" );
        }
        else if ( c2 == 'x' || c2 == 'X' )
          if ( value1 == 0 )
            continue;
          else pm_error( "syntax error" );
        else break;
      }
      *ptr++ = value1;
    }
}


/*  CHANGE HISTORY:

  99.09.08 bryanh    Recognize "static unsigned char" declaration.





*/
