/*  UNWAD2.C
    Scott Mitting of CircleA Entertainment (smitting@netusa1.net)
    This is a program I made at the request of a user to hold him
    over until I release the full version of EdQuake.
    -= http://www.netusa1.net/~smitting/edquake/ =-
    Feel free to use this code in anyways shape or form, and if
    you want to credit me.  I used the qkspec31 file when neccessary,
    (it's not a difficult format, but I wanted to save time).
    I would appreciate an email telling me what you're working on
    if you use my code though...I'm always looking for good quake
    utils :)  Gamma correction and speed omptimizations have been
    implemented.

    usage:  UNWAD2 -x <wad2file.wad> <file within wad>
       extracts raw data
	    UNWAD2 -b <wad2file.wad> <file within wad>
       writes a .bmp with the same name as the file in the wad2.
	    UNWAD2 -l <wad2file.wad>
       lists files in a wad2
	    UNWAD2 -v <wad2file.wad> <file within wad>
       views texture in 320x200x256
	    UNWAD2 -h
       help

    Enjoy.
    COMPILED WITH: Borland Turbo C++ v3.0
    */

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

typedef struct
{
   char magic[4]; 			//should be WAD2
   long num;				//number of entries
   long offset;				//location of directory
} wad2_t;

typedef struct
{
   long offset;                 	// Position of the entry in WAD
   long dsize;                  	// Size of the entry in WAD file
   long size;                   	// Size of the entry in memory
   char type;                   	// type of entry
   char cmprs;                  	// Compression. 0 if none.
   short dummy;                 	// Not used
   char name[16];               	// we use only first 8
} wad2entry_t;

typedef struct
{
   char name[16];               // Name of the texture.
   long width;                  // width of picture, must be a multiple of 8
   long height;                 // height of picture, must be a multiple of 8
   long offset1;                // offset to u_char Pix[width   * height]
   long offset2;                // offset to u_char Pix[width/2 * height/2]
   long offset4;                // offset to u_char Pix[width/4 * height/4]
   long offset8;                // offset to u_char Pix[width/8 * height/8]
} miptex_t;

typedef struct
{    /* bmfh */
    int    bfType;
    long   bfSize;
    int    bfReserved1;
    int    bfReserved2;
    long   bfOffBits;
    long   biSize;
    long    biWidth;
    long    biHeight;
    int     biPlanes;
    int     biBitCount;
    long    biCompression;
    long    biSizeImage;
    long    biXPelsPerMeter;
    long    biYPelsPerMeter;
    long    biClrUsed;
    long    biClrImportant;
} bmpheader_t;


wad2_t wad2;				//wad file header
wad2entry_t  wad2entry;			//wad file entry
miptex_t miptex;			//mip texture
FILE *wadptr;				//wad file pointer
char pal[768];				//quake palette

unsigned char far *video_buffer = (unsigned char far *) 0xa0000000;

//prepares wad2 for use.
void wad2open(char *filename)
{
   wadptr = fopen(filename, "rb");	//open file
   if (!wadptr) return;			//check load result
   fread(&wad2,12,1,wadptr);		//read header
   wad2.magic[4] = 0;			//add ASCIIZ end of string
}

//close wad2 file
void wad2close()
{
   fclose(wadptr);                      //close file
}

//checks to see if it loaded ok
int wad2ok()
{
   if (!wadptr) return 0;		//verify pointer
   return 1;
}

void wad2getentry(int n)
{
   fseek(wadptr,wad2.offset+(n*32),0);		//set pointer
   fread(&wad2entry,32,1,wadptr);            	//read entry
}

int wad2findfile(char *filename)
{
   int t;
   if (!wad2ok()) return -1;
   for (t = 0; t < wad2.num; t++)
   {
      wad2getentry(t);
      //to port change to strcmp...(i=not case sensitive)
      if (strcmpi(filename, wad2entry.name) == 0) t += 1000;
   }
   if (t < 1000) return -1;
   t-=1001;
   return t;
}

void wad2getmip(int n)
{
   wad2getentry(n);                             //find offset
   fseek(wadptr,wad2entry.offset,0);		//set pointer
   fread(&miptex,40,1,wadptr);            	//read mip header
}

void wad2getpalette()
{
   FILE *p;
   int t;
   p = fopen("quake.pal","rb");
   fseek(p, 0, 0);
   for (t = 0; t < 768; t++)
      pal[t] = fgetc(p);
   fclose(p);
}

void wad2list()
{
   int t;
   for (t = 0; t < wad2.num; t++)
   {
      wad2getentry(t);
      printf("%20s %10li 0x%08lx\n",
		wad2entry.name,
		wad2entry.dsize,
		wad2entry.offset);
   }
}

void setvideomode(int mode)
{
   _AX = mode;
   asm int 0x10
}

#define PALETTE_MASK        0x3c6
#define PALETTE_REGISTER_RD 0x3c7
#define PALETTE_REGISTER_WR 0x3c8
#define PALETTE_DATA        0x3c9

void setpal(unsigned char attr,unsigned char red, unsigned char green, unsigned char blue)
{
   outp(PALETTE_MASK,0xff);
   outp(PALETTE_REGISTER_WR, attr);
   outp(PALETTE_DATA,red);
   outp(PALETTE_DATA,green);
   outp(PALETTE_DATA,blue);
}

void setquakepal()
{
   int t;
   for (t = 0; t < 256; t++)
      setpal(t,pal[t*3],pal[t*3+1],pal[t*3+2]);
}

void putpixel(int x, int y, char c)
{
   video_buffer[(y<<8)+(y<<6)+x] = c;
}

void wad2view(int n)
{
   int x, y;
    if (n < 0) return;
   wad2getmip(n);
   fseek(wadptr,wad2entry.offset + miptex.offset1,0);
   setvideomode(0x0013);
   wad2getpalette();
   setquakepal();
   for (y = 0; y < miptex.height; y++)
      for (x = 0; x < miptex.width; x++)
	 putpixel(x,y,fgetc(wadptr));
   getch();
   setvideomode(0x0003);
}

void wad2savebmp(int n)
{
    bmpheader_t bmpfile;
    FILE *out;
    char *r;
    long i, x, y;
    if (n < 0) return;
    wad2getmip(n);
    fseek(wadptr,wad2entry.offset + miptex.offset1,0);
    wad2getpalette();
    bmpfile.bfType = 0x4d42; // 'BM'
    bmpfile.bfSize = sizeof(bmpfile) + 1024 + (miptex.width * miptex.height);
    bmpfile.bfReserved1=bmpfile.bfReserved2=0;
    bmpfile.bfOffBits = 0x436;
    bmpfile.biSize = 0x28; //this is crazy here!!!
    bmpfile.biWidth = miptex.width;
    bmpfile.biHeight = miptex.height;
    bmpfile.biPlanes = 1;
    bmpfile.biBitCount = 8;
    bmpfile.biCompression = 0;
    bmpfile.biSizeImage = 0;
    bmpfile.biXPelsPerMeter = 0xec3; //==96 ppi (english)
    bmpfile.biYPelsPerMeter = 0xec3;
    bmpfile.biClrUsed = 0;
    bmpfile.biClrImportant = 0;

    //set up bmp name
    if ((i = strlen(miptex.name)) > 8) i = 8;
    miptex.name[i] = '.';
    miptex.name[i+1] = 'b';
    miptex.name[i+2] = 'm';
    miptex.name[i+3] = 'p';
    miptex.name[i+4] = 0;

    out = fopen(miptex.name,"wb");
    fwrite(&bmpfile, sizeof(bmpfile), 1, out);

    //0's for gamma.
    for (i = 0; i < 256; i++)
    {
	fputc(pal[(i*3) + 2]<<2, out);
	fputc(pal[(i*3) + 1]<<2, out);
	fputc(pal[(i*3) + 0]<<2, out);
	fputc(0, out);

    }
   i = ftell(wadptr);
   r = malloc(miptex.width);
   for (y = miptex.height; y > 0; y--)
   {
      fseek(wadptr,i+(y*miptex.width),0);
      fread(r,miptex.width,1,wadptr);
      fwrite(r,miptex.width,1,out);
   }
   free(r);
   fclose(out);
}


void wad2extract(int n)
{
   long t,qs;
   FILE *p;
   char *r;
   if (n < 0) return;
   r = malloc(1000);
   wad2getentry(n);
   p = fopen(wad2entry.name,"wb");
   if (!p)
   {
      printf("Error opening output file.\n");
      return;
   }
   printf("saving file...");
   qs = (wad2entry.dsize / 1000);
   for (t = 0; t < qs; t++)
   {
      fread(r,1000,1,wadptr);
      fwrite(r,1000,1,p);
   }
   t = wad2entry.dsize - (qs * 1000);
   fread(r,t,1,wadptr);
   fwrite(r,t,1,p);
   printf("done.\n");
   fclose(p);
   free(r);
}


//check out WINDOW03
int main(int argc, char *argv[])
{
   printf("UNWAD2 v1.0  by Scott Mitting of CircleA (smitting@netusa1.net)\n");
   printf("public domain. please distibute with source code\n");
   printf("get EdQuake at http://www.netusa1.net/~smitting/edquake/\n");
   printf("-h for help\n\n");
   if (!strncmpi(argv[1],"-h",2))
   {
      printf("usage: UNWAD2  -[xblvh]  <wad2file> <internal file>\n");
      printf("    x - extracts <internal file> from <wad2file>\n");
      printf("	  b - saves <internal file> to a bmp.\n");
      printf("    l - lists contents of <wad2file> (use with |more)\n");
      printf("    v - views <internal file>\n");
      printf("    h - shows this help screen\n\n");
   }
   if (!strncmpi(argv[1],"-x",2))
   {
      wad2open(argv[2]);
      printf("opening %s...\n",argv[2]);
      if (!wad2ok())
      {
	 printf("Error opening wad2 file\n");
	 return 0;
      }
      wad2extract(wad2findfile(argv[3]));
      wad2close();
   }
   if (!strncmpi(argv[1],"-b",2))
   {
      wad2open(argv[2]);
      printf("opening %s...\n",argv[2]);
      if (!wad2ok())
      {
	 printf("Error opening wad2 file\n");
	 return 0;
      }
      wad2savebmp(wad2findfile(argv[3]));
      wad2close();
   }
   if (!strncmpi(argv[1],"-l",2))
   {
      wad2open(argv[2]);
      printf("opening %s...\n",argv[2]);
      if (!wad2ok())
      {
	 printf("Error opening wad2 file\n");
	 return 0;
      }
      wad2list();
      wad2close();
   }
   if (!strncmpi(argv[1],"-v",2))
   {
      wad2open(argv[2]);
      printf("opening %s...\n",argv[2]);
      if (!wad2ok())
      {
	 printf("Error opening wad2 file\n");
	 return 0;
      }
      wad2view(wad2findfile(argv[3]));
      wad2close();
   }
   return 0;
}