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


struct ImageRec {
  unsigned short xsize, ysize, zsize;
  FILE *file;
  unsigned char *tmp;
  unsigned int *rowStart;
  int *rowSize;
};


void ConvertShort(void *shortnum)
{
  char *ptr;
  char tempval;

  ptr=(char *)shortnum;
  tempval=*ptr;
  *ptr=*(ptr+1);
  *(ptr+1)=tempval;
}


void ConvertLong(unsigned *array, long length)
{
  unsigned b1, b2, b3, b4;
  unsigned char *ptr;

  ptr = (unsigned char *)array;
  while (length--) {
    b1 = *ptr++;
    b2 = *ptr++;
    b3 = *ptr++;
    b4 = *ptr++;
    *array++ = (b1 << 24) | (b2 << 16) | (b3 << 8) | (b4);
  }
}


void rgbtorgba(unsigned char *r,unsigned char *g,unsigned char *b,unsigned char *l,int n)
{
  while (n--) {
    l[2] = r[0];
    l[1] = g[0];
    l[0] = b[0];
    l[3] = 0xff;
    l += 4; r++; g++; b++;
  }
}


struct ImageRec *ImageOpen(const char *fileName)
{
  union {
    int testWord;
    char testByte[4];
  } endianTest;
  struct ImageRec *image;
  int swapFlag;
  int x;
  char tempdata[1000];

  endianTest.testWord = 1;
  if (endianTest.testByte[0] == 1) {
    swapFlag = 1;
  } else {
    swapFlag = 0;
  }

  image = (struct ImageRec *)malloc(sizeof(struct ImageRec));

  if ((image->file = fopen(fileName, "rb")) == NULL) {
    printf("Error opening %s\n", fileName);
    exit(1);
  }

  fread(tempdata, 1, 6, image->file);
  fread(&image->xsize, 1, 2, image->file);
  fread(&image->ysize, 1, 2, image->file);
  fread(&image->zsize, 1, 2, image->file);

  if (swapFlag) {
    ConvertShort(&image->xsize);
    ConvertShort(&image->ysize);
    ConvertShort(&image->zsize);
  }

  x = image->ysize * image->zsize * sizeof(unsigned);
  image->rowStart = (unsigned *)malloc(x);
  image->rowSize = (int *)malloc(x);
  fseek(image->file, 512, SEEK_SET);
  fread(image->rowStart, 1, x, image->file);
  fread(image->rowSize, 1, x, image->file);
  if (swapFlag) {
    ConvertLong(image->rowStart, x/(int)sizeof(unsigned));
    ConvertLong((unsigned *)image->rowSize, x/(int)sizeof(int));
  }

  image->tmp = (unsigned char *)malloc(image->xsize*256);

  return(image);
}


void ImageClose(struct ImageRec *image)
{
  fclose(image->file);
  free(image->tmp);
  free(image);
}


void ImageGetRow(struct ImageRec *image, unsigned char *buf, int y, int z)
{
  unsigned char *iPtr, *oPtr, pixel;
  int count;

  fseek(image->file, (long)image->rowStart[y+z*image->ysize], SEEK_SET);
  fread(image->tmp, 1, (unsigned int)image->rowSize[y+z*image->ysize],
        image->file);

  iPtr = image->tmp;
  oPtr = buf;
  while (1) {
    pixel = *iPtr++;
    count = (int)(pixel & 0x7F);
    if (!count) {
      return;
    }
    if (pixel & 0x80) {
      while (count--) {
        *oPtr++ = *iPtr++;
      }
    } else {
      pixel = *iPtr++;
      while (count--) {
        *oPtr++ = pixel;
      }
    }
  }
}


unsigned *read_texture(const char *name, int *width, int *height, int *components)
{
  unsigned *base, *lptr;
  unsigned char *rbuf, *gbuf, *bbuf;
  struct ImageRec *image;
  int y;

  image = ImageOpen(name);
    
  if (!image) {
    return(NULL);
  }

  (*width)=image->xsize;
  (*height)=image->ysize;
  (*components)=image->zsize;
  base = (unsigned *)malloc(image->xsize*image->ysize*sizeof(unsigned));
  rbuf = (unsigned char *)malloc(image->xsize*sizeof(unsigned char));
  gbuf = (unsigned char *)malloc(image->xsize*sizeof(unsigned char));
  bbuf = (unsigned char *)malloc(image->xsize*sizeof(unsigned char));

  lptr = base;
  for (y=0; y<image->ysize; y++) {
    ImageGetRow(image, rbuf, y, 0);
    ImageGetRow(image, gbuf, y, 1);
    ImageGetRow(image, bbuf, y, 2);
    rgbtorgba(rbuf, gbuf, bbuf, (unsigned char *)lptr, image->xsize);
    lptr += image->xsize;
  }
  ImageClose(image);
  free(rbuf);
  free(gbuf);
  free(bbuf);

  return((unsigned *)base);
}
