#include<stdio.h>
#include<errno.h>

/*
** read PDT10 header
*/

int read_header( int *width, int *height, int *alpha_offset, FILE *fp )
{
  unsigned char buf[32];

  /* read header */

  if( fread(buf,32,1,fp) < 1 )
    return -1;
  if( strncmp(buf,"PDT10",5) != 0 )
    return -1;
  *width = (int)buf[12] | ((int)buf[13] << 8);
  *height = (int)buf[16] | ((int)buf[17] << 8);
  *alpha_offset = (int)buf[28] | (int)buf[29] << 8
  			| (int)buf[30] << 16 | (int)buf[31] << 24;
  return 0;
}


/*
** create RGB image data from PDT10 input
**
**   return pointer to image data or NULL whel failed.
*/

unsigned char *pdt_image( FILE *fp, int width, int height )
{
  unsigned char *p,*dic,*dst;
  unsigned char buf[32];
  int size;
  int i,count;
  unsigned char flag,mask;

  size = width * height;

  	/* to prevent buffer-overflow, make buffer-size be big */
  if( (p = (unsigned char*)malloc((size+4096)*3)) == NULL )
    return NULL;
  dst = p;

  /* create image (LZ decompression) */

  mask = 0;

  while( size > 0 ){

    if( mask == 0  ){
      if( fread(&flag,1,1,fp) < 1 )
        break;
      mask = 0x80;
    }

    if( flag & mask ){

    	/* if bit is 1, new 3-bytes date from input */
      if( fread(buf,3,1,fp) < 1 )
        break;
      p[2] = buf[0];
      p[1] = buf[1];
      p[0] = buf[2];
      size--;
      p+=3;

    }else{

	/* if bit is 0, read from dictionary (repeat backward data) */
      if( fread(buf,2,1,fp) < 1 )
        break;
      i = (int)buf[0] | ((int)buf[1] << 8);
      count = (i & 0x0f)+1;
      size -= count;
      count *= 3;
      dic = p - (i >> 4)*3 - 3;

      while( count-- )
        *p++ = *dic++;
    }

    mask >>= 1;
  }

  if( feof(fp) || ferror(fp) ){
    free(dst);
    return NULL;
  }

  return dst;
}

/*
** create Alpha-channel data from PDT10 input
**
**   return pointer to Alpha-channel data or NULL whel failed.
*/

unsigned char *pdt_alpha( FILE *fp, int width, int height )
{
  unsigned char *p,*dic,*dst;
  unsigned char buf[32];
  int size;
  int i,count;
  unsigned char flag,mask;

  size = width * height;

  	/* to prevent buffer-overflow, make buffer-size be big */
  if( (p = (unsigned char*)malloc(size+257)) == NULL )
    return NULL;
  dst = p;

  /* create image (LZ decompression) */

  mask = 0;

  while( size > 0 ){

    if( mask == 0  ){
      if( fread(&flag,1,1,fp) < 1 )
        break;
      mask = 0x80;
    }

    if( flag & mask ){

    	/* if bit is 1, new byte date from input */
      if( fread(p,1,1,fp) < 1 )
        break;
      p++;
      size--;

    }else{

	/* if bit is 0, read from dictionary (repeat backward data) */
      if( fread(buf,2,1,fp) < 1 )
        break;
      count = (int)buf[0] + 2;
      size -= count;
      dic = p - (int)buf[1] - 1;
      while( count-- )
        *p++ = *dic++;
    }

    mask >>= 1;
  }

  if( feof(fp) || ferror(fp) ){
    free(dst);
    return NULL;
  }

  return dst;
}

void error_invalid( void )
{
  fprintf(stderr,"Invalid input data.\n");
}

int main( int argc, char **argv )
{
  unsigned char *p;
  FILE *fp;
  int width,height,alpha_offset;
  int alpha;
  char *myname;

  alpha = 0;
  myname = argv[0];

  while( argc > 1 && argv[1][0] == '-' ){
    if( argv[1][1] == '\0' ){
      argc--;
      argv++;
      break;
    }else if( strcmp(argv[1],"-alpha") == 0 ){
      alpha = 1;
      argc--;
      argv++;
    }else
      break;
  }

  if( argc < 2 )
    fp = stdin;
  else if( argc > 2 ){
    fprintf(stderr,"usage: %s [-alpha] [-] [PDT10 file]\n",myname );
    return 1;
  }else if( (fp = fopen(argv[1],"r")) == NULL ){
    fprintf(stderr,"cannot open '%s'\n",argv[1]);
    return 1;
  }

  if( read_header(&width, &height, &alpha_offset, fp) < 0 ){
    fprintf(stderr,"Not PDT10 data.\n");
    return 1;
  }

  if( !alpha ){
    p = pdt_image(fp,width,height);
    if( p == NULL ){
      error_invalid();
      return 1;
    }
    printf("P6 %d %d 255\n",width,height);
    if( fwrite(p,width*height*3,1,stdout) < 1 )
      return 1;
  }else{
    if( alpha_offset < 32 ){
      fprintf(stderr,"No alpha-channel data.\n");
      return 1;
    }
    if( fseek(fp,alpha_offset,SEEK_SET) < 0 ){
      if( errno != EBADF ){
        error_invalid();
        return 1;
      }
      alpha_offset -= 32;
      while( alpha_offset-- ){
        if( fgetc(fp) == EOF ){
          error_invalid();
          return 1;
        }
      }
    }
    p = pdt_alpha(fp,width,height);
    if( p == NULL ){
      error_invalid();
      return 1;
    }
    printf("P5 %d %d 255\n",width,height);
    if( fwrite(p,width*height,1,stdout) < 1 )
      return 1;
  }
  return 0;
}
