/* program to read in either 24-bit tga or 16-bit tga and write a 24--bit
   iff file */


static char amiga_version[] = "$VER: Version 2.1\n";


#include<stdio.h>
#include<stdlib.h>
#include<stddef.h>
#include<exec/types.h>


/* define file types */
#define UNK_TYPE 0
#define TIF_TYPE 1
#define DKB_TYPE 2
#define TGA_TYPE 3
#define IFF24_TYPE 4
#define BMP_TYPE 5

/* Locations of important bytes in the headers */
#define DKB_WIDTH   0
#define DKB_HEIGHT  2
#define TGA_HEADER_SIZE 18
#define TGA_WIDTH_LO 12
#define TGA_WIDTH_HI 13
#define TGA_HEIGHT_LO 14
#define TGA_HEIGHT_HI 15
#define TGA_COMP    2
#define TGA_BITPLANES 16

/* other defines */
#define OFF 0
#define YES 1
#define FALSE 0
#define MAX_STR 512
#define neq !=
#define eq ==
#ifndef DOOPS
#define DOOPS 15
#define OKDOKEY 0
#endif
#define mod %

/* A typedef */
typedef struct
{
  UBYTE r;
  UBYTE g;
  UBYTE b;
} pixel_type;

typedef struct
{
  ULONG width;
  ULONG height;
  ULONG current_line;  /* used only by DKB */
  FILE *file;
  UBYTE compression;
  UBYTE bitplanes;
  UBYTE file_type;
  UBYTE pad_one;       /* Used by iff: does file need an extra null byte
                                       tacked onto the end of odd-length
                                       chunks/records/blocks/files? */
} INFO_REC;


/* define IFF structures */

struct BMHD {
  UWORD w,h;
  WORD x,y;
  UBYTE npl;
  UBYTE masking,compression,pad1;
  UWORD tcolor;
  UBYTE xas,yas;
  WORD pWid,pHig;
};
	
/* sometimes data is faster than a function .. */
int pow_2[8] = {1,2,4,8,16,32,64,128};

/* Forward references now.  begin */
unsigned char get_bit(unsigned char *, int);
/* unsigned char extract_bit(unsigned char, int); */
/* unsigned char reverse_bits(unsigned char); */
unsigned char get_bit(unsigned char *, int);
int write_long_int(unsigned long, FILE *);
int force_switch(char *);
int read_intel_word(int *, FILE *);
int write_iff_24_header(INFO_REC *);
/* end forward references */


/* -------------------------------------------------------------------------- */
/* extract bit, returns a 1 or 0, depending on the bit specified in "bit" */
/* bit order:  7 6 5 4 3 2 1 0 */
/* This routine works.  Don't edit it at ALL */
unsigned char extract_bit(bbyte, bit)
unsigned char bbyte;
int bit;
{
  bit &= 7;
  return ((unsigned char) ((bbyte >> bit) & 1));
}


/* -------------------------------------------------------------------------- */
/* Reverse_bits.  Takes a given char/byte and returns a char/byte containing
   the reversed bit-pattern.  So a byte with order:
     "0 1 2 3 4 5 6 7"
   becomes:
     "7 6 5 4 3 2 1 0".
   In a in the interest of run-time speed, this function is not implemented
   as a loop.  Cope. */
/* This routine works.  Don't edit it at ALL */
unsigned char reverse_bits(x)
unsigned char x;
{
  register unsigned char i;
  i =  (x >> 7) & 1;
  i |= (x >> 5) & 2;
  i |= (x >> 3) & 4;
  i |= (x >> 1) & 8;
  i |= (x << 1) & 16;
  i |= (x << 3) & 32;
  i |= (x << 5) & 64;
  i |= (x << 7) & 128;
  return(i);
}


/* -------------------------------------------------------------------------- */
/* this function returns the specified bit from the
   specified bit array */
unsigned char get_bit(buffer, bit_dex)
unsigned char *buffer;
int bit_dex;
{
  unsigned char i;
  i = extract_bit(reverse_bits(buffer[bit_dex / 8]), bit_dex & 255);
  return(i);
}


/* -------------------------------------------------------------------------- */
int write_long_int(i, f)
unsigned long i;
FILE *f;
{
  int status;
  if ((status = fputc((int) ((i >> 24) & 0xff), f)) eq EOF) return (DOOPS);
  if ((status = fputc((int) ((i >> 16) & 0xff), f)) eq EOF) return (DOOPS);
  if ((status = fputc((int) ((i >> 8) & 0xff), f)) eq EOF) return (DOOPS);
  if ((status = fputc((int) (i & 0xff), f)) eq EOF) return (DOOPS);
  return(OKDOKEY);
}

/* -------------------------------------------------------------------------- */
/* force_switch.  Forcible switched high and low bytes without any of this
   signed/unsigned optimizing bullcrap. */
int force_switch(b)
char *b;
{
  int result = 0;
  char *fake_ptr;

  fake_ptr = (char *) &result;
  fake_ptr[2] = b[1];
  fake_ptr[3] = b[0];
  return(result);
}


/* -------------------------------------------------------------------------- */
/* Next bit reads an intel little injun int from the specified file */
int read_intel_word(int *i, FILE *f)
{
  int rc;
  if ((rc = fgetc(f)) != EOF) {
    *i = rc;
    if ((rc = fgetc(f)) != EOF) {
      *i += rc * 256;
      return(OKDOKEY); /* A-Ok! */
    }
  }
  return(DOOPS);  /* error */
}


/* --- tempory kludge debugging util */
/* pre_fuck */
unsigned char prefuck1(i)
unsigned char i;
{
/*   unsigned char temp;
  temp = i >> 3; */
  return(i);
}

unsigned char pre_fuck(i)
unsigned char i;
{
  return(reverse_bits(i));
}


/* -------------------------------------------------------------------------- */
int read_pixel(pixel, file)
pixel_type *pixel;
FILE *file;
{
  int status;
  if ((status = fread((char *) pixel, 3, 1, file)) eq 0) return (DOOPS);
  return(OKDOKEY);
}


/*--------------------------------------------------------------------------*/
int read_tga_header(info_rec)
INFO_REC *info_rec;
{
  char stock_tga_header[18] = {
       0,0,
       2,         /* compression OFF */
       0,0,0,0,0,0,0,0,0,
       0x40,0x01, /* width  */
       0xc8,0,    /* height */
       0x18,0x20  /* bitplanes = 24 */
  };
  int status;
  if ((status = fread(stock_tga_header, 18, 1, info_rec->file)) eq 0)
    return (DOOPS);
  info_rec->bitplanes = stock_tga_header[TGA_BITPLANES];
  if ((info_rec->bitplanes != 24) && (info_rec->bitplanes != 16)
      && (info_rec->bitplanes != 32))
    return (DOOPS);
  switch ((int) stock_tga_header[TGA_COMP])  {
    case  2 : info_rec->compression = 0;   /* no comp */
              break;
    case 10 : info_rec->compression = 1;   /* yes comp */
              break;
    default : return (DOOPS);
  }  /* end switch */
  info_rec->width = (0xff & (unsigned int) stock_tga_header[TGA_WIDTH_HI]) * 256;
  info_rec->width += 0xff & (unsigned int) stock_tga_header[TGA_WIDTH_LO];
  info_rec->height = (0xff & (unsigned int) stock_tga_header[TGA_HEIGHT_HI]) * 256;
  info_rec->height += 0xff & (unsigned int) stock_tga_header[TGA_HEIGHT_LO];
  if ((info_rec->width < 2) || (info_rec->height < 2)) return (DOOPS);
  info_rec->file_type = TGA_TYPE;
  return(OKDOKEY);
}


/* -------------------------------------------------------------------------- */
int read_tga_16(buffer, info_rec)
char *buffer;
INFO_REC *info_rec;
{
  int status, i, temp1, r, g, b;
  char *buf;
  if ((buf = malloc((size_t) info_rec->width * 2)) eq NULL) return(DOOPS);
  if ((status = fread(buf, (size_t) info_rec->width * 2, 1, info_rec->file)) eq 0) {
    free(buf);
    return(DOOPS);
  }
  /* Now copy/convert into our standard internal format */
  for (i = 0; i != info_rec->width; i++ ) {
    /* switch hi & lo bytes of intel word */
    temp1 = force_switch(&buf[i * 2]);
    /* Isolate RGB values, and scale seperately */
    r = (temp1 >> 10) & 0x01f;
    g = (temp1 >>  5) & 0x01f;
    b = temp1 & 0x01f;
    /* scale 5 bit components to 8 bits */
    buffer[i * 3] =     (r << 3) | ((r >> 2) & 7);
    buffer[i * 3 + 1] = (g << 3) | ((g >> 2) & 7);
    buffer[i * 3 + 2] = (b << 3) | ((b >> 2) & 7);
  }
  free(buf);
  return(OKDOKEY);
}


/* -------------------------------------------------------------------------- */
/* Read in one line from a compressed-format (run length compress type 10)
   24-bit tga file */
int read_comp_tga_line(line, info_rec)
char *line;
INFO_REC *info_rec;
{
  int c, i, index, status, temp;
  pixel_type pixel;

  for (index = 0; index != info_rec->width;) {
    if ((c = fgetc(info_rec->file)) eq EOF) return(DOOPS);
    if (c > 127) {    /* It is a compressed run */
      c = c - 128;
      if ((status = read_pixel(&pixel, info_rec->file)) eq DOOPS) return(status);
      if ((index + c) >= info_rec->width) return (DOOPS);
      c++; 
      temp = index * 3;
      for (i = 0; i < c; i++) {
        if (index > info_rec->width) {
           return (DOOPS); /* array bounds */
        }
        line[temp++] = pixel.b;
        line[temp++] = pixel.g;
        line[temp++] = pixel.r;
      }
    } else {   /* it is a non-compressed run */
      if ((index + c) >= info_rec->width) return (DOOPS);   /* array bounds */
      c++;
      temp = index * 3;
      for (i = 0; i < c; i++) {
        if ((status = read_pixel(&pixel, info_rec->file)) eq DOOPS)
          return(status);
        line[ temp++] = pixel.b;
        line[ temp++] = pixel.g;
        line[ temp++] = pixel.r;
      }
    }
    index +=c;
  } /* end of line */
  return (OKDOKEY);
}


/* -------------------------------------------------------------------------- */
/* read an uncompressed 24-bit tga line */
int read_tga_24(buffer, info_rec)
char *buffer;
INFO_REC *info_rec;
{
  int column,status;
  char *buff;

  if ((buff = malloc((size_t) info_rec->width * 3)) eq NULL) {
    fprintf(stderr,"read_tga_24: bad malloc of %d pixels\n",info_rec->width);
    return(DOOPS);
  }
  if ((status = fread(buff, (size_t) info_rec->width * 3,1,info_rec->file))
        eq NULL) {
    fprintf(stderr,"read_tga_24: bad fread call (%d pixels)\n",
                  info_rec->width);
    free(buff);
    return(DOOPS);
  }
  /* Now translate to internal format */
  for (column = 0; column != info_rec->width; column++) {
    buffer[column * 3]     = buff[column * 3 + 2];
    buffer[column * 3 + 1] = buff[column * 3 + 1];
    buffer[column * 3 + 2] = buff[column * 3];
  }
  free(buff);
  return(OKDOKEY);
}


/* -------------------------------------------------------------------------- */
/* Routine to read an uncompressed 32-bit tga scanline
   Pixel Format = B G R Alpha.  Ignore alpha and return our usual
   internal 24-bit format  */
int read_tga_32(buffer, info_rec)
char *buffer;
INFO_REC *info_rec;
{
  int column,status;
  char *buff;

  if ((buff = malloc((size_t) info_rec->width * 4)) eq NULL)
    return(DOOPS);
  if ((status = fread(buff, (size_t) info_rec->width * 4,1,info_rec->file))
         eq NULL) {
    free(buff);
    return(DOOPS);
  }
  /* Now translate to internal format.  We just ignore the alpha. */
  for (column = 0; column != info_rec->width; column++) {
    buffer[column * 3]     = buff[column * 4 + 2];
    buffer[column * 3 + 1] = buff[column * 4 + 1];
    buffer[column * 3 + 2] = buff[column * 4];
  }
  free(buff);
  return(OKDOKEY);
}


/* -------------------------------------------------------------------------- */
int read_tga_line(buffer, info_rec)
char *buffer;
INFO_REC *info_rec;
{
  int status;

  switch (info_rec->bitplanes)
  {
    case 16 :
      if ((status = read_tga_16(buffer, info_rec)) eq DOOPS)
        return(status);
      break;
    case 24 :
      if (info_rec->compression eq YES)
        status = read_comp_tga_line(buffer, info_rec);
      else
        status = read_tga_24(buffer, info_rec);
      if (status eq DOOPS)
        return(status);
      break;
    case 32 :
      if ((status = read_tga_32(buffer, info_rec)) eq DOOPS)
        return(status);
      break;
    default:
      return(DOOPS);
  } /* end switch */
  return(OKDOKEY);
}


/* -------------------------------------------------------------------------- */
/* Write_iff_24. Writes one line to a 24 BIT uncompressed iff.
   Writes in the strange intervleaved method that IFFs are infamous for.
 */
int write_iff_24(buffer, info_rec)
char *buffer;
INFO_REC *info_rec;
{
  int status, w, pixel, stupid, bit, shift;
  char *bufftemp, *bufftemp2;
  if ((bufftemp = malloc((size_t) info_rec->width * 3)) eq NULL)
    return(DOOPS);
  if ((bufftemp2 = malloc((size_t) info_rec->width * 3)) eq NULL) {
    free(bufftemp);
    return(DOOPS);
  }
  /* First loop.  Convert internal to DKB */
  for (w = 0; w neq info_rec->width; w++) {
    bufftemp[w]                         = pre_fuck(buffer[w * 3]);
    bufftemp[w + info_rec->width]       = pre_fuck(buffer[w * 3 + 1]);
    bufftemp[w + (info_rec->width * 2)] = pre_fuck(buffer[w * 3 + 2]);
  }
  /* now for second loop.  DKB to IFF.  Gotta handle the weird
     interleaving */
  stupid = 0;  /* RGB component: 0 to 2 */
  bit = 0;     /* bit */
  shift = 0;   /* amount to shift when placing in pixel byte */
  pixel = 0;    /* this line not necisary but it keeps the compiler quiet */
  for (stupid = 0; stupid neq 3; stupid++) {  /* RGB component */
    for (shift = 0; shift neq 8; shift++) {   /* Bitplane number */
      int phase;
      phase = 0;
      for (w = 0; w neq info_rec->width; w++) {  /* width */
        pixel *= 2;
        pixel += get_bit(&bufftemp[stupid * info_rec->width + w], shift);
        phase++;
        if (phase eq 8) {
          phase = 0;
          bufftemp2[info_rec->width * stupid +
                    w / 8 +
                    (info_rec->width / 8) * shift] = pixel;
          pixel = 0;
        }
      }
    }
  }
  /* Ok!  second loop done.  Got bits sorted into bytes.  Now we have a
     IFF24 line.   Write it uncompressed. */
  status = fwrite(bufftemp2, (size_t) info_rec->width * 3, 1, info_rec->file);
  free(bufftemp2);
  free(bufftemp);
  if (status eq NULL)
    return(DOOPS);
  return(OKDOKEY);
}


/*--------------------------------------------------------------------------*/
/* write_iff_24_header: read the header from an IFF 24-bit file */
int write_iff_24_header(info_rec)
INFO_REC *info_rec;
{
  int status;
  unsigned long chunklength;
  unsigned long bmhdlength;
  struct BMHD header;
  /* First: calculate physical size of file, including even-byte padding */
  bmhdlength = sizeof(struct BMHD);
  if ((bmhdlength & 1) eq 1) {
    fprintf(stderr,"FATAL!  Compiler data packing error! BMHD format\
 incorrect!!  DO NOT USE THIS PROGRAM!\n");
    return(DOOPS);
  }
  chunklength = info_rec->width * info_rec->height * 3;
  if ((chunklength & 1) eq 1) {
    chunklength++;
    info_rec->pad_one = 1;
  } else
    info_rec->pad_one = 0;
  /* At this point, chunklength has the size of the BODY chunk */
  fprintf(info_rec->file,"FORM");
  /* write total file size minus 8 bytes */
  if (
      (
       status = (int)
       write_long_int(
         (chunklength + bmhdlength + 20), 
         info_rec->file
       )
      )
      eq
      DOOPS
     )
    return(status);
  fprintf(info_rec->file,"ILBMBMHD");
  if ((status = write_long_int(bmhdlength, info_rec->file)) eq DOOPS)
    return(status);
  /* Now write BHMD.  First prepare BMHD. */
  header.w = info_rec->width;
  header.h = info_rec->height;
  header.x = 0;
  header.y = 0;
  header.npl = 24;                 /* 24-bit output only */
  header.masking = 0;              /* Homy don't play that shit */
  header.compression = 0;          /* no compressed IFF output */
  header.pad1 = 0;                 /* always */
  header.tcolor = (UWORD) 0;       /* transparent color */
  header.xas = 0xA;                /* 10 (horizontal aspect ratio component) */
  header.yas = 0xB;                /* 11 (vertical aspect ratio component) */
  header.pWid = info_rec->width;   /* page width */
  header.pHig = info_rec->height;  /* page height */
  if ((status =
                (int) fwrite( (char *) &header,
                sizeof(struct BMHD),
                1,
                info_rec->file)) != 1)
    return(DOOPS);
  fprintf(info_rec->file,"BODY");
  if ((status = write_long_int(chunklength, info_rec->file)) eq DOOPS)
    return(DOOPS);
  return(OKDOKEY);
}


void create_output_name(out_name, in_name)
char *out_name;
char *in_name;
{
  int len;

  out_name[0] = '\0'; /* zero out the output name string */
  /* does in_name have a .tga on the end? */
  len = strlen(in_name);
  if (len > 4) {  /* is the name long enough to have a ".tga" in it? */
    if (stricmp(&in_name[len - 4], ".tga") eq 0) {  /* is there a ".tga" */
      strcpy(out_name, in_name);
      out_name[(len - 4)] = '\0';    /* chop off the ".tga" */
      strcat(out_name, ".iff24");    /* add a ".iff24" */
    }
  }
  /* at this point, if the output name string is not empty, then
     we are done.  So only do this next part if the output name
     string is empty */
  if (out_name[0] eq '\0') {
    strcpy(out_name, in_name);
    strcat(out_name, ".iff24");
  }
}


main(paramcount, params)
int paramcount;
char *params[];
{
  int status;
  ULONG i;
  char *buffer;
  char output_file_name[MAX_STR];
  INFO_REC info_rec_in, info_rec_out;
    
  if (paramcount eq 0) exit(OKDOKEY);
  if ((paramcount < 2) || (paramcount > 3)) {
    fprintf(stderr,"Usage:  %s <infile> [<outfile>]\n\
Where infile is a TGA file and outfile is the 24-bit IFF\n\
file to create\n",params[0]);
    exit(DOOPS);
  }
  if ((info_rec_in.file = fopen(params[1],"rb")) eq NULL) {
    fprintf(stderr,"Error opening input TGA file\n");
    exit(DOOPS);
  } 
  if ((status = setvbuf(info_rec_in.file, NULL, _IOFBF, 16384)) neq 0) {
    fprintf(stderr, "setvbuf error on input file\n");
    exit(DOOPS);
  }
  if ((status = read_tga_header(&info_rec_in)) eq DOOPS) {
    printf("main:  error reading header from input file\n");
    return(DOOPS);
  }
  info_rec_out.width = info_rec_in.width;
/* Next bit compensates for a nasty bug in all programs that read
   IFF files ... All IFF files have to have a width that is
   evenly divided by 16. So we just make the output file a little
   wider so that it to is divisable by 16. */
  if (info_rec_out.width mod 16 neq 0) {
    info_rec_out.width /= 16;
    info_rec_out.width++;
    info_rec_out.width *= 16;
  }
  info_rec_out.height = info_rec_in.height;
  info_rec_out.compression = OFF;
  info_rec_out.bitplanes = 24;
  info_rec_out.file_type = IFF24_TYPE;
  if (paramcount eq 3)
    strcpy(&output_file_name[0], params[2]);
  else {
    create_output_name(&output_file_name[0], params[1]);
  }
  if ((info_rec_out.file = fopen(&output_file_name[0],"wb")) eq NULL) {
    fprintf(stderr,"Error creating output iff24 file \"%s\"\n",
           &output_file_name[0]);
    exit(DOOPS);
  }
  if ((status = setvbuf(info_rec_out.file, NULL, _IOFBF, 16384)) neq 0) {
    fprintf(stderr, "setvbuf error on output file\n");
    exit(DOOPS);
  }
  if ((buffer = malloc((size_t) info_rec_out.width * 3)) eq NULL) {
    printf("Bad low memory error\n");
    exit(DOOPS);
  }
  /* Fill extra IFF "fill" pixels on right with black */
  if (info_rec_out.width > info_rec_in.width) {
    for (i = (info_rec_in.width * 3); i < (info_rec_out.width * 3); i++)
      buffer[i] = 0;
  }
  printf("Input file   %d x %d x %d  \"%s\"\n",
         info_rec_in.width, info_rec_in.height, info_rec_in.bitplanes,
         params[1]);
  printf("Output file  %d x %d x %d  \"%s\"\n",
         info_rec_out.width, info_rec_out.height, info_rec_out.bitplanes,
         &output_file_name[0]);
  if ((status = write_iff_24_header(&info_rec_out)) eq DOOPS) {
    printf("main:  error writing header to output file\n");
    return(DOOPS);
  }
  for (i = 0; i neq info_rec_in.height; i++) {
    if ((status = read_tga_line(buffer, &info_rec_in)) eq DOOPS) {
      fprintf(stderr,"Error reading from TGA file\n");
      exit(DOOPS);
    }
    if ((status = write_iff_24(buffer, &info_rec_out)) eq DOOPS) {
      fprintf(stderr,"Error writing to output IFF24 file\n");
      exit(DOOPS);
    }
    printf("\015%05lu",i);
    fflush(stdout);
  }
  fclose(info_rec_out.file);
  fclose(info_rec_in.file);
  printf("\nDone\n");
  exit(OKDOKEY);
}


/* actual end of this file */
