#include "qtools.h"

/*
 * WAD2-tools
 *
 * entryName    == 0 -> OP_EXTRACT all
 * destDir == 0 -> OP_EXTRACT to current directory
 */

/*
 *
 */

bool CheckWAD2(FILE * wadFile, struct wadheader * Header, bool newWad)
{
  if (wadFile) {
    fseek(wadFile, 0, SEEK_END);
    if (ftell(wadFile) == 0) {
      if (newWad == TRUE) {
	/*
	 * file is new 
	 */
	Header->magic.integer = MAGIC_WAD2;
	Header->offset = LittleLong(sizeof(struct wadheader));

	Header->numentries = LittleLong(0);
	fwrite(Header, 1, sizeof(struct wadheader), wadFile);

	return TRUE;
      }
      else
	return FALSE;
    }
    else {
      fseek(wadFile, 0, SEEK_SET);
      fread(Header, 1, sizeof(struct wadheader), wadFile);
      return Header->magic.integer == MAGIC_WAD2 ? TRUE : FALSE;
    }
  }
  else
    return FALSE;
}

/*
 * findwad2 return ever one entry more than exists!
 */
struct wadentry *FindWAD2(FILE * wadFile, char *entryName, struct wadheader *Header, struct wadentry **Entry, char wadType)
{
  struct wadentry *allEntries;

  fseek(wadFile, LittleLong(Header->offset), SEEK_SET);
  if ((*Entry = allEntries = (struct wadentry *)tmalloc((LittleLong(Header->numentries) + 1) * sizeof(struct wadentry)))) {
    int i;

    fread(allEntries, 1, (LittleLong(Header->numentries)) * sizeof(struct wadentry), wadFile);

    if (entryName) {
      for (i = 0; i < LittleLong(Header->numentries); i++) {
	if (!strncmp(entryName, allEntries->name, NAMELEN_WAD)) {
	  // deactivated cause of complications
	  //if ((allEntries->type == wadType) || (wadType == TYPE_UNKNOWN)) {
	  /*
	   * return offset of valid entry 
	   */
	  return allEntries;
	  //}
	}
	allEntries++;
      }
    }
    /*
     * not in wadFile 
     */
    return 0;
  }
  else
    return (struct wadentry *)-1;
}

struct palpic *GetWAD2Picture(FILE *wadFile, struct wadentry *Entry)
{
  struct palpic *Picture = 0;

  fseek(wadFile, LittleLong(Entry->offset), SEEK_SET);

  switch (Entry->type) {
    case WAD2_PALETTE:
      /*
       * there MUST be only one palette in the wad, which is exactly the one we took above
       */
      if ((Picture = pmalloc(16, 16, 0, Entry->name))) {
	short int i;

	for (i = 0; i < 256; i++)
	  Picture->rawdata[i] = (unsigned char)i;
      }
      break;
    case WAD2_STATUSBAR:
    case WAD2_CONPIC:
      switch (Entry->compr) {
	case CMP_NONE:
	case CMP_MIP0:
	  Picture = GetLMP(wadFile, Entry->name);
	  break;
	case CMP_LZ77:
	case (CMP_LZ77 | CMP_MIP0):{
	    struct lump *Lump;

	    if ((Lump = (struct lump *)GetLZ77(wadFile, LittleLong(Entry->wadsize)))) {
	      Picture = ParseLMP(Lump, Entry->name);
	      tfree(Lump);
	    }
	  }
	  break;
	default:
	  break;
      }
      break;
    case WAD2_MIPMAP:
    case 0:
      /*
       * fix me!!! this is bogus 
       */
      switch (Entry->compr) {
	case CMP_NONE:
	case CMP_MIP0:
	  Picture = GetMipMap(wadFile, 0);
#ifdef DEBUG
	  mprintf("get first 4 bytes %x%x%x%x (%x%x%x,%x%x%x,%x%x%x)\n", Picture->rawdata[0], Picture->rawdata[1], Picture->rawdata[2], Picture->rawdata[3],
		  Picture->palette[0].r, Picture->palette[0].g, Picture->palette[0].b,
		  Picture->palette[1].r, Picture->palette[1].g, Picture->palette[1].b,
		  Picture->palette[2].r, Picture->palette[2].g, Picture->palette[2].b);
#endif
	  break;
	case CMP_LZ77:
	case (CMP_LZ77 | CMP_MIP0):{
	    struct mipmap *MipMap;

	    if ((MipMap = (struct mipmap *)GetLZ77(wadFile, LittleLong(Entry->wadsize)))) {
	      Picture = ParseMipMap(MipMap, 0);
	      tfree(MipMap);
	    }
	  }
	  break;
	default:
	  break;
      }
      break;
    default:
      break;
  }
  return Picture;
}

struct rawdata *GetWAD2Raw(FILE *wadFile, struct wadentry *Entry)
{
  struct rawdata *rawData = 0;

  fseek(wadFile, LittleLong(Entry->offset), SEEK_SET);

  switch (Entry->compr) {
    case CMP_NONE:
      rawData = GetRaw(wadFile, Entry->name, LittleLong(Entry->wadsize));
      break;
    case CMP_MIP0:{
	struct palpic *MipMap;

	if ((MipMap = GetMipMap(wadFile, 0))) {
	  if ((rawData = rmalloc(MIP_MULT(LittleLong(MipMap->width) * LittleLong(MipMap->height)) + sizeof(struct mipmap), Entry->name))) {
	    PasteMipMap((struct mipmap *)rawData->rawdata, MipMap);
	  }
	  pfree(MipMap);
	}
      }
      break;
    case CMP_LZ77:{
	char *Data;

	if ((Data = GetLZ77(wadFile, LittleLong(Entry->wadsize)))) {
	  rawData = ParseRaw(Data, Entry->name, LittleLong(Entry->memsize));
	  tfree(Data);
	}
      }
      break;
    case (CMP_LZ77 | CMP_MIP0):{
	struct mipmap *GetMip;
	struct palpic *MipMap;

	if ((GetMip = (struct mipmap *)GetLZ77(wadFile, LittleLong(Entry->wadsize)))) {
	  if ((MipMap = ParseMipMap(GetMip, 0))) {
	    if ((rawData = rmalloc(MIP_MULT(LittleLong(MipMap->width) * LittleLong(MipMap->height)) + sizeof(struct mipmap), Entry->name))) {
	      PasteMipMap((struct mipmap *)rawData->rawdata, MipMap);
	    }
	    pfree(MipMap);
	  }
	  tfree(GetMip);
	}
      }
      break;
    default:
      break;
  }

  return rawData;
}

bool ExtractWAD2(FILE * wadFile, FILE * script, char *destDir, char *entryName, unsigned char outType, operation procOper, char wadType)
{
  struct wadheader Header;
  struct wadentry *Entry, *allEntries;
  struct rgb *oldCache = cachedPalette;
  bool retval = FALSE;

  if (CheckWAD2(wadFile, &Header, FALSE) == TRUE) {
    /*
     * we assume that the dir is at the end of the file!!!!! 
     */
    switch ((int)(Entry = FindWAD2(wadFile, entryName, &Header, &allEntries, wadType))) {
      case -1:
	eprintf("cannot tmalloc memory for all entries of size %ld\n", (LittleLong(Header.numentries) + 1) * sizeof(struct wadentry));

	break;
      case 0:
	if ((entryName) && (*entryName != '\0') && (outType != TYPE_WAD2)) {
	  eprintf("no entry %s found in wad\n", entryName);
	  break;
	}
      default:{
	  struct rgb *Palette = 0;
	  bool ownPalette = FALSE;
	  int i = 0;

	  if (outType != TYPE_NONE) {
	    /*
	     * first search for a palette in the wad-file and use that instead of default palette 
	     */
	    struct wadentry *palEntry = allEntries;

	    for (i = 0; i < LittleLong(Header.numentries); i++) {
	      if (palEntry->type == WAD2_PALETTE) {
		fseek(wadFile, LittleLong(palEntry->offset), SEEK_SET);
		switch (palEntry->compr) {
		  case CMP_NONE:
		  case CMP_MIP0:
		    if ((Palette = (struct rgb *)tmalloc(256 * 3)))
		      fread(Palette, 1, 256 * 3, wadFile);
		    break;
		  case CMP_LZ77:
		  case (CMP_LZ77 | CMP_MIP0):
		    Palette = (struct rgb *)GetLZ77(wadFile, LittleLong(palEntry->wadsize));
		    break;
		  default:
		    break;
		}
		mprintf("use wads built-in palette\n");
		ownPalette = TRUE;
		break;
	      }
	      palEntry++;
	    }
	    i = 0;
	    /*
	     * we take the new palette for conversions
	     */
	    if (!Palette)
	      Palette = GetPalette();
	    else
	      cachedPalette = Palette;
	  }

	  /*
	   * process only ONE
	   */
	  if (Entry && (outType != TYPE_WAD2))
	    i = LittleLong(Header.numentries) - 1;
	  /*
	   * reset and process ALL
	   */
	  else
	    Entry = allEntries;

	  for (; i < LittleLong(Header.numentries); i++) {
	    char fileName[NAMELEN_PATH];

	    strncpy(fileName, destDir, NAMELEN_PATH - 1);
	    strncat(fileName, Entry->name, NAMELEN_PATH - 1);

	    switch (outType) {
	      case TYPE_PPM:	strncat(fileName, ".ppm", NAMELEN_PATH - 1); break;
	      case TYPE_JPEG:	strncat(fileName, ".jpg", NAMELEN_PATH - 1); break;
	      case TYPE_ILBM:	strncat(fileName, ".iff", NAMELEN_PATH - 1); break;
	      case TYPE_PNG:	strncat(fileName, ".png", NAMELEN_PATH - 1); break;
	      case TYPE_WAD2:	strncat(fileName, ".wad", NAMELEN_PATH - 1); break;
	      case TYPE_NONE:
	      default:
		switch (Entry->type) {
		  case WAD2_PALETTE:	strncat(fileName, ".pal", NAMELEN_PATH - 1); outType = TYPE_NONE; break;
		  case WAD2_STATUSBAR:	strncat(fileName, ".stb", NAMELEN_PATH - 1); outType = TYPE_LUMP; break;
		  case WAD2_MIPMAP:	strncat(fileName, ".mip", NAMELEN_PATH - 1); outType = TYPE_MIPMAP; break;
		  case WAD2_CONPIC:	strncat(fileName, ".lmp", NAMELEN_PATH - 1); outType = TYPE_LUMP; break;
		  default:		strncat(fileName, ".xxx", NAMELEN_PATH - 1); outType = TYPE_NONE; break;
		}
		break;
	    }

	    switch (procOper) {
	      case OP_EXTRACT:{
		  if (outType == TYPE_WAD2) {
		    struct rawdata *rawData;

		    mprintf("extract %s to %s ...\n", Entry->name, entryName);
		    if ((rawData = GetWAD2Raw(wadFile, Entry))) {
		      retval = AddWAD2(0, rawData, entryName, OP_ADD, Entry->type);
		      rfree(rawData);
		    }
		  }
		  else {
		    FILE *fileDst;

		    mprintf("extract %s to %s ...\n", Entry->name, fileName);
		    CreatePath(fileName);
		    if ((fileDst = fopen(fileName, WRITE_BINARY))) {
		      if (outType == TYPE_NONE) {
		        struct rawdata *rawData;

		        if ((rawData = GetWAD2Raw(wadFile, Entry))) {
			  retval = PutRaw(fileDst, rawData);
			  rfree(rawData);
		        }
		      }
		      else if (outType != TYPE_NONE) {
		        struct palpic *Picture;

		        if ((Picture = GetWAD2Picture(wadFile, Entry))) {
		          switch (outType) {
		            case TYPE_LUMP:	retval = PutLMP(fileDst, Picture);            break;
		            case TYPE_MIPMAP:	retval = PutMipMap(fileDst, Picture);         break;
		            default:		retval = PutImage(fileDst, Picture, outType); break;
		          }
			  pfree(Picture);
		        }
		      }
		      fclose(fileDst);
		    }
		    else
		      eprintf("cannot open %s\n", fileName);
		  }
		}
		break;
	      case OP_LIST:
	      case OP_DEFAULT:
	      default:{
		  mprintf("%16s %8d bytes (offset: %8x, type: %c, compr: %1d)\n", Entry->name, LittleLong(Entry->wadsize), LittleLong(Entry->offset), Entry->type, (int)Entry->compr);
		  retval = TRUE;
		}
		break;
	    }
	    if (script)
	      fprintf(script, "update %s as %s as %c\n", fileName, Entry->name, Entry->type);
	    Entry++;
	  }
	}
	break;
    }
    tfree(allEntries);
  }
  else
    eprintf("no valid wadfile\n");

  cachedPalette = oldCache;
  return retval;
}

bool AddWAD2(struct palpic *inPic, struct rawdata *inData, char *wadName, operation procOper, char wadType)
{
  char *procName;
  FILE *wadFile = fopen(wadName, READWRITE_BINARY_OLD);
  struct wadheader Header;
  struct wadentry *Entry, *allEntries;
  bool retval = FALSE;

  if (!wadFile)
    wadFile = fopen(wadName, READWRITE_BINARY_NEW);

  if (wadFile) {
    if (CheckWAD2(wadFile, &Header, TRUE) == TRUE) {
      if (inPic) {
	procName = inPic->name;
      }
      else if (inData) {
	procName = inData->name;
      }
      /*
       * we assume that the dir is at the end of the file!!!!! 
       */
      switch ((int)(Entry = FindWAD2(wadFile, procName, &Header, &allEntries, wadType))) {
	case -1:
	  eprintf("cannot tmalloc memory for all entries of size %ld\n", LittleLong(Header.numentries) * sizeof(struct wadentry));
	  break;
	case 0:
	  switch (procOper) {
	    case OP_REPLACE:
	      eprintf("no entry %s found to replace in wad %s\n", procName, wadName);
	      break;
	    case OP_ADD:
	    case OP_UPDATE:
	    case OP_DEFAULT:
	    default:
	      fseek(wadFile, LittleLong(Header.offset), SEEK_SET);
	      /*
	       * seek to end of data and write data
	       */
	      Entry = allEntries + LittleLong(Header.numentries);
	      Entry->offset = Header.offset;

	      mprintf("add %s to %s ...\n", Entry->name, wadName);
	      if (inData) {
		switch (Compression) {
		  case CMP_NONE:
		  case CMP_MIP0:
		    retval = PutRaw(wadFile, inData);
		    break;
		  case CMP_LZ77:
		  case (CMP_MIP0 | CMP_LZ77):{
		      retval = PutLZ77(wadFile, (char *)inData->rawdata, inData->size) > ERROR ? TRUE : FALSE;
		      Entry->memsize = LittleLong(inData->size);
		    }
		    break;
		  default:
		    break;
		}
	      }
	      else if (inPic) {
		switch (wadType) {
		  case WAD2_PALETTE:
		    switch (Compression) {
		      case CMP_NONE:
		      case CMP_MIP0:
			if (fwrite(inPic->palette, 1, 256 * 3, wadFile) == (256 * 3)) {
			  retval = TRUE;
			}
			break;
		      case CMP_LZ77:
		      case (CMP_MIP0 | CMP_LZ77):{
			  retval = PutLZ77(wadFile, (char *)inPic->palette, (256 * 3)) > ERROR ? TRUE : FALSE;
			  Entry->memsize = LittleLong(256 * 3);
			}
			break;
		      default:
			break;
		    }
		    break;
		  case WAD2_MIPMAP:
		    switch (Compression) {
		      case CMP_NONE:
			retval = PutMipMap(wadFile, inPic);
			break;
		      case CMP_MIP0:
			retval = PutMipMap0(wadFile, inPic);
			break;
		      case CMP_LZ77:{
			  struct mipmap *MipMap;

			  if ((MipMap = (struct mipmap *)tmalloc(MIP_MULT(inPic->width * inPic->height) + sizeof(struct mipmap)))) {
			    if ((retval = PasteMipMap(MipMap, inPic)) == TRUE) {
			      retval = PutLZ77(wadFile, (char *)MipMap, MIP_MULT(inPic->width * inPic->height) + sizeof(struct mipmap)) > ERROR ? TRUE : FALSE;
			      Entry->memsize = LittleLong(MIP_MULT(inPic->width * inPic->height) + sizeof(struct mipmap));
			    }
			    tfree(MipMap);
			  }
			}
			break;
		      case (CMP_MIP0 | CMP_LZ77):{
			  struct mipmap *MipMap;

			  if ((MipMap = (struct mipmap *)tmalloc((inPic->width * inPic->height) + sizeof(struct mipmap)))) {
			    if ((retval = PasteMipMap0(MipMap, inPic)) == TRUE) {
			      retval = PutLZ77(wadFile, (char *)MipMap, (inPic->width * inPic->height) + sizeof(struct mipmap)) > ERROR ? TRUE : FALSE;
			      Entry->memsize = LittleLong((inPic->width * inPic->height) + sizeof(struct mipmap));
			    }
			    tfree(MipMap);
			  }
			}
			break;
		      default:
			break;
		    }
		    break;
		  case WAD2_STATUSBAR:
		  case WAD2_CONPIC:
		    switch (Compression) {
		      case CMP_NONE:
		      case CMP_MIP0:
			retval = PutLMP(wadFile, inPic);
			break;
		      case CMP_LZ77:
		      case (CMP_MIP0 | CMP_LZ77):{
			  struct lump *Lump;

			  if ((Lump = (struct lump *)tmalloc((inPic->width * inPic->height) + sizeof(struct lump)))) {
			    if ((retval = PasteLMP(Lump, inPic)) == TRUE) {
			      retval = PutLZ77(wadFile, (char *)Lump, (inPic->width * inPic->height) + sizeof(struct lump)) > ERROR ? TRUE : FALSE;
			      Entry->memsize = LittleLong(MIP_MULT(inPic->width * inPic->height) + sizeof(struct lump));
			    }
			    tfree(Lump);
			  }
			}
			break;
		      default:
			break;
		    }
		    break;
		  default:
		    break;
		}
	      }
	      else
		eprintf("nothing to add\n");

	      if (retval == TRUE) {
		Entry->wadsize = LittleLong(ftell(wadFile) - LittleLong(Header.offset));
		if ((Entry->compr = Compression) == CMP_NONE)
		  Entry->memsize = Entry->wadsize;
		Entry->type = wadType;
		strncpy(Entry->name, procName, NAMELEN_WAD);
		Header.numentries = LittleLong(LittleLong(Header.numentries) + 1);
		Header.offset = LittleLong(ftell(wadFile));
		/*
		 * write directory
		 */
		if (fwrite(allEntries, 1, LittleLong(Header.numentries) * sizeof(struct wadentry), wadFile) == (LittleLong(Header.numentries) * sizeof(struct wadentry))) {
		  fseek(wadFile, 0, SEEK_SET);
		  /*
		   * write header
		   */
		  fwrite(&Header, 1, sizeof(struct wadheader), wadFile);

		  fflush(wadFile);
		}
		else {
		  eprintf("cannot write full data to wad %s\n", wadName);
		  retval = FALSE;
		}
	      }
	      else
		eprintf("cannot write data %c %s to wad %s\n", wadType, procName, wadName);
	      break;
	  }
	  tfree(allEntries);
	  break;
	default:
	  switch (procOper) {
	    case OP_REPLACE:
	    case OP_UPDATE:
	      /*
	       * DeleteWAD2() 
	       */
	      /*
	       * AddWAD2() 
	       */
	      retval = TRUE;
	      break;
	    case OP_ADD:
	    case OP_DEFAULT:
	    default:
	      eprintf("old entry %s found in wad %s\n", procName, wadName);
	      break;
	  }
	  tfree(allEntries);
	  break;
      }
      fclose(wadFile);
    }
    else
      eprintf("no valid wadfile %s\n", wadName);
  }
  else
    eprintf("cannot open desired wadfile %s\n", wadName);

  return retval;
}
