/* dem2tga.c (c) 1997 Jon Larimer
 *
 *  This is a program to convert USGS Digital Elevation Model (DEM) data
 *  into a .TGA file, viewable by many graphics applications.  This program
 *  is public domain, do whatever you want with it, except claim it as your
 *  own or sell it. If you find a way to make money from my work, I want a
 *  piece of the action.
 *
 *  This should compile with any ANSI standard compiler, including Unix
 *  and DOS versions of gcc and many other C compilers.  If you have any
 *  questions or comments email jonl@alltel.net.
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/errno.h>


typedef struct _tagTGAHDR {

     char tgaID;        /* number of chars in ID field */
     char tgaMapType;   /* color map (0=no map; 1=map present) */
     char tgaImageType; /* codes: 
                            0  -  No image data included.
                            1  -  Uncompressed, color-mapped images.
                            2  -  Uncompressed, RGB images.
                            3  -  Uncompressed, black and white images.   
                            9  -  Runlength encoded color-mapped images.
                            10  -  Runlength encoded RGB images.
                            11  -  Compressed, black and white images.
                            32  -  Compressed color-mapped data, using Huffman, Delta, and
                                   runlength encoding.
                            33  -  Compressed color-mapped data, using Huffman, Delta, and
                                   runlength encoding.  4-pass quadtree-type process.  */
     int tgaMapOrigin;  /* Origin of colormap */
     int tgaMapLen;     /* number of entries in map */
     char tgaMapEntry;  /* length of map entry size in bits */
     int tgaImgX;       /* X origin of image (lower left) */
     int tgaIMGY;       /* Y origin of image (lower left) */
     int tgaIMGW;       /* width of image */
     int tgaIMGH;       /* height of image */
     char tgaPixelSize; /* pixel size (number of bits per pixel) */
     char tgaImgDesc;   /* image descriptor (should be 0) */
     char tgaDesc[255]; /* image description (may be less than 255 chars) */

} TGAHEADER;

int writetgaheader(FILE *fptr, int r, int c) {
  int i;
 
  fputc(0, fptr);  /* chars in ID field */
  fputc(1, fptr);  /* color map type */
  fputc(1, fptr);  /* image type (1=uncompressed colormap) */
  fputc(0, fptr);  /* colormap origin (int) */
  fputc(0, fptr);  /* colormap origin (int) */
  fputc(0, fptr);  /* num colormap entries (int) */
  fputc(1, fptr);  /* num colormap entries (int) */
  fputc(24, fptr); /* colormap entry size (in bits) */
  fputc(0, fptr);  /* X origin of image (lower left corner) (int) */
  fputc(0, fptr);  /* X origin of image (lower left corner) (int) */
  fputc(0, fptr);  /* Y origin of image (lower left corner) (int) */
  fputc(0, fptr);  /* Y origin of image (lower left corner) (int) */
  fputc((char)(c & 0x00ff), fptr);          /* pixel width of image (int) */
  fputc((char)((c & 0xff00) >> 8), fptr);   /* pixel width of image (int) */
  fputc((char)(r & 0x00ff), fptr);          /* pixel height of image (int) */
  fputc((char)((r & 0xff00) >> 8), fptr);   /* pixel height of image (int) */
  fputc(8, fptr);  /* number of bits per pixel */
  fputc(0, fptr);  /* image descriptor */
  for(i=0; i<=255; i++) {   /* color map data */
    fputc(i, fptr);         /* in blue-green-red order for 24-bit images */
    fputc(i, fptr);
    fputc(i, fptr);
  }
  fflush(fptr);
  return 0;
}

int writetgaheader24(FILE *fptr, int r, int c) {
  int i;
 
  fputc(0, fptr);  /* chars in ID field */
  fputc(0, fptr);  /* color map type */
  fputc(2, fptr);  /* image type (2=uncompressed RGB) */
  fputc(0, fptr);  /* colormap origin (int) */
  fputc(0, fptr);  /* colormap origin (int) */
  fputc(0, fptr);  /* num colormap entries (int) */
  fputc(0, fptr);  /* num colormap entries (int) */
  fputc(0, fptr); /* colormap entry size (in bits) */
  fputc(0, fptr);  /* X origin of image (lower left corner) (int) */
  fputc(0, fptr);  /* X origin of image (lower left corner) (int) */
  fputc(0, fptr);  /* Y origin of image (lower left corner) (int) */
  fputc(0, fptr);  /* Y origin of image (lower left corner) (int) */
  fputc((char)(c & 0x00ff), fptr);          /* pixel width of image (int) */
  fputc((char)((c & 0xff00) >> 8), fptr);   /* pixel width of image (int) */
  fputc((char)(r & 0x00ff), fptr);          /* pixel height of image (int) */
  fputc((char)((r & 0xff00) >> 8), fptr);   /* pixel height of image (int) */
  fputc(24, fptr);  /* number of bits per pixel */
  fputc(0, fptr);  /* image descriptor */

/* the storage for POV-Ray is the red and green for high and low bytes of the 16-bit
   heightfield (respectively) [the Blue byte can be left to zero]
   Byte ordering is blue-green-red, or 0-low-high */
 
  fflush(fptr);
  return 0;
}

/* j is the elevation to write.
   Values written in 0-lowbyte-highbyte order */
int fwritec24(int j, FILE *fptr) {
  fputc(0, fptr);
  fputc((char)(j & 0x00ff), fptr);
  fputc((char)((j & 0xff00) >> 8), fptr);
  return 0;
}


int fgetcb(FILE *fptr)
{
  if (feof(fptr))
     return 0;
  else
     return fgetc(fptr);
}


int nextint(FILE * fptr) {
  char ch;
  int i;
  char in[64];

  while(isspace(ch = fgetcb(fptr))) { }
  i=0;
  in[0] = ch;              
  while(!isspace(ch = fgetcb(fptr))) { i++; in[i] = ch; }
  in[i+1] = '\0';
  return(atoi(in));
} 



double nextfloat(FILE * fptr) {
  char ch;
  int i;
  char in[64];

  while(isspace(ch = fgetcb(fptr))) { }
  i=0;
  in[0] = ch;
  while(!isspace(ch = fgetcb(fptr))) { 
    i++; 
    if(ch == 'D') { ch = 'E'; }
    in[i] = ch; 
  }
  in[i+1] = '\0';
  atof(in);
}

/* int argc, char **argv */

int main(int argc, char **argv) 
{
  FILE *demfile;
  FILE *outfile;
  int i, j, l;
  long int x;
  float k;
  int maxelev, minelev;
  int rows, columns;
  int irows, icols;
  int elevrange;
  float colormap_scale;
  int r, c, omode;
  int cur_r, cur_c;
  char ch;
  char name[144];


  fprintf(stdout, "dem2tga v1.0 (c) 1997 Jon Larimer\n");
  fprintf(stdout, "  enhanced 1997 by Greg Billock\n");
  fprintf(stdout, "This program is public domain.\n\n");

  if (argc < 2) {
    fprintf(stdout, "usage: dem2tga input_file output_file mode\n");
    fprintf(stdout, "  input_file is the DEM you want to convert\n");
    fprintf(stdout, "  output_file is the name you want to save the image under.\n");
    fprintf(stdout, "    You should supply a .tga at the end of the file.\n");
    fprintf(stdout, "  mode is the 8 or 16 bit mode:  the numbers 8 (default) or 16\n");
    exit(1);
  }

  omode = 1;  /* default to 8-bit */
  if (argc>=3)
  {
     if ( strncmp(argv[3], "16", 2) == 0 )
        omode = 2;   /* 16-bit mode */
  }

  if((demfile = fopen(argv[1], "r")) == NULL) {
    fprintf(stderr, "%s: fopen: %s", argv[0], strerror(errno));
    exit(1);
  }
  
  /****** DEM TYPE A RECORDS ******/
  /* get the name field (144 characters) */
  for(i=0; i < 144; i++) { name[i] = fgetc(demfile); }
  name[i+1] = '\0';
  /* clean off the whitespace at the end */
  for(i=strlen(name)-2; i > 0; i--) {
    if(!isspace(name[i])) { i=0; } else { name[i] = '\0'; }
   
  }
  fprintf(stdout, "Quad name field: %s\n", name);
  /* don't need the next 19 items for anything */
  for(i=0; i < 19; i++) { (void)nextint(demfile); }
  fprintf(stdout, "Units code (ground planametric coordinates): %i\n", nextint(demfile));
  fprintf(stdout, "Units code (elevation coordinates): %i\n", nextint(demfile));
  (void)nextint(demfile);
  printf("Ground coordinates of 4 corners of DEM: (in arc-seconds) \n");
  for(i=1; i<=4; i++)
    fprintf(stdout, "  %.4f  %.4f\n", nextfloat(demfile), nextfloat(demfile));
  minelev = (int)nextfloat(demfile);
  maxelev = (int)nextfloat(demfile);
  fprintf(stdout, "\nMinimum elevation: %i\n", minelev);
  fprintf(stdout, "Maximum elevation: %i\n", maxelev);
  elevrange = maxelev-minelev;
  fprintf(stdout, "Elevation range: %i\n", elevrange);
  /* this is the value to multiply elevations by to get a number between 0
     and 255 to determine the color of the pixel */
  colormap_scale = (float)255 / (float)elevrange;
  fprintf(stdout, "Colormap scaling value: %.5f\n", colormap_scale);
  /* don't need the next 3 items */
  for(i=1; i<3; i++) { (void)nextint(demfile); }
  cur_r = nextint(demfile);
  columns = nextint(demfile);

  /* DEM TYPE B RECORDS */
  /* Format:  current_row, current_column, num_rows, num_cols + 5 */
  /*         numbers:  el. of local datum, profile max, profile min, etc. */

  cur_c = 1;
  fprintf(stdout, "Writing image...\n");
  fflush(stdout);
  outfile = fopen(argv[2], "wb+");
  l = 1;
  /* we need to see how many rows of data before we write the header */
  cur_r = nextint(demfile);
  cur_c = nextint(demfile);
  rows = nextint(demfile);
  x = 0L;

  if (omode==1)
    writetgaheader(outfile, rows, columns);
  else
    writetgaheader24(outfile,rows,columns);
  irows = rows;
  icols = columns;

#ifdef VERBOSE
   fprintf(stdout, "FILE SIZE:  %d COLUMNS, %d ROWS\n", icols, irows);
   fprintf(stdout, "Writing col %d\n", cur_c);
#endif

  /* now ready to write the image data */
  while (cur_c <= columns) 
  {

    (void)nextint(demfile); (void)nextint(demfile);
    (void)nextint(demfile); (void)nextint(demfile);
    (void)nextint(demfile); (void)nextint(demfile);

    for (i=1; i<=rows; i++) 
    { 
      j = nextint(demfile) - minelev; 
      k = (float)j * colormap_scale;
      if (omode==1)
         fputc((int)k, outfile);
      else    
         fwritec24(j+minelev, outfile);
      x++;
    }

    /* try to trap overflow */
    if (cur_c == columns)
       break;

    if (cur_c < columns)
    {
       cur_r = nextint(demfile);
       cur_c = nextint(demfile);
       rows = nextint(demfile);
    }

#ifdef VERBOSE
    fprintf(stdout, "Writing col %d\n", cur_c);
#endif

    /* try to trap underflow */
    if (cur_r==0 && cur_c==0 && rows==0)
       break;

  }

  /* pad file if too short */
  if (x < ((long)irows * (long)icols))
  {
     while (x < ((long)irows * (long)icols))
     {
      fprintf(stdout,"%ld bytes read |  padding file...", x);
      if (omode==1)
         fputc((int)0, outfile);
      else    
         fwritec24(0, outfile);
      x++;
     }
  }

  fprintf(stdout, "done.\n");
  fprintf(stdout, "Wrote %d rows, %d columns, %ld pixels\n\n",
            irows, icols, x);
  fprintf(stdout, "Image must be rotated 90 degrees counter-clockwise and flipped\n");
  fprintf(stdout, "horizontally to align the top of the image with north and the left with west.\n\n");

  fclose(outfile);
  fclose(demfile);
  return 0;

}
  
  
