/*
 *	WADFILE.C
 *
 *	Version 1.0.0
 *
 *	Abstract:
 *	 This module contains all the WADFILE processing routines for the
 *	 WadLib library.
 *
 *	History:
 *	 1.0.0	(March 31, 1994)
 *
 *  Author:
 *	 Michael McMahon
 *
 ****************************************************************************
 * Wadfile Function Directory
 *---------------------------------------------------------------------------
 *Index: Name                  : Description
 *---------------------------------------------------------------------------
 * #1  : WadfileAddLump 	   : Add a lump to a Wadfile being created.
 * #2  : WadfileClose		   : Close file, flush cache, update WAD directory
 * #3  : WadfileCopyEntry	   : Copies a lump from one Wadfile to another.
 * #4  : WadfileCopyMap 	   : Copies a map from one Wadfile to another.
 * #5  : WadfileCreate		   : Create WAD file and initialize Wadfile struct.
 * #6  : WadfileGetDirInfo	   : Read directory info into Wadfile structure.
 * #7  : WadfileGetNextDirInfo : Move to next directory entry and read it in.
 * #8  : WadfileGetPrevDirInfo : Move to last directory entry and read it in.
 * #9  : WadfileInitialize	   : One-time call, set up memory allocators.
 * #10 : WadfileLumpClose	   : Close lump inside WAD file, free its memory.
 * #11 : WadfileLumpCopy	   : Copy lump file from one Wadfile to another.
 * #12 : WadfileLumpOpen	   : Open lump at current directory position.
 * #13 : WadfileOpen		   : Open a WAD file and initialize Wadfile struct.
 * #14 : WadfileSeek		   : Find first dir entry with a search string.
 * #15 : WadfileSeekMap 	   : Find map given episode and map. ExMy
 ****************************************************************************
 *
 *  WADLIB SOFTWARE LICENSE AGREEMENT
 *
 *	1. GRANT OF LICENSE. Michael McMahon and his affiliations (collectively
 *	   the "AUTHOR") grant you (either an individual or an entity) the
 *	   non-exclusive, royalty-free right to use this library source code,
 *	   documentation, and sample code (collectively, the "SOFTWARE") for
 *	   any lawful purpose subject to the terms of this license.  By using the
 *	   SOFTWARE you are agreeing to be bound to all the terms of this license.
 *
 *	2. COPYRIGHT.  The SOFTWARE is Copyright (c) 1994, Michael McMahon,
 *	   PO Box 14807, San Luis Nabisco, CA 93406-4807 USA. All Rights Reserved
 *	   Worldwide.  You may not use, modify, or distribute the SOFTWARE except
 *	   as otherwise provided herein.
 *
 *	3. DECLARATION OF PUBLIC DOMAIN DISTRIBUTION AND USE. The distribution
 *	   and use of the SOFTWARE is hereby designated PUBLIC DOMAIN by the
 *	   the AUTHOR.	You may not sell, rent, or lease this SOFTWARE.  The
 *	   SOFTWARE may be reproduced verbatim in part or in full by any
 *	   reproduction means for any lawful purpose, and may also be subject to
 *	   the following agreement.
 *
 *	4. AGREEMENT FOR USE OF SOFTWARE. The AUTHOR grants you a non-exclusive,
 *	   royalty-free right to incorporate the SOFTWARE into any production for
 *	   any legal purpose as long as you agree
 *		(a) to indemnify, hold harmless, and defend the AUTHOR from and against
 *			any claims or lawsuits, including attorneys' fees, that arise or
 *			result from the use or distribution of your software production; and
 *		(b) no matter how much the SOFTWARE is modified, the AUTHOR owns the
 *			copyright and this original, unmodified copyright notice remains
 *			intact in the source code; and,
 *		(c) the AUTHOR is not held responsible for fixing bugs or making
 *			enhancements or changes to the SOFTWARE for any reason; and,
 *		(d) the SOFTWARE is not redistributed if it is modified in any way; and,
 *      (e) otherwise comply with the terms of this agreement; and,
 *		(f) the AUTHOR is forgiven for making so many demands.
 *
 *	 THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. THE
 *	 AUTHOR FURTHER DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING WITHOUT
 *	 LIMITATION ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR OF FITNESS
 *	 FOR A PARTICULAR PURPOSE.	THE ENTIRE RISK ARISING OUT OF THE USE
 *	 OR PERFORMANCE OF THE SOFTWARE REMAINS WITH YOU.
 *
 *	 The author can be reached at:
 *	  Michael McMahon
 *	  P.O. Box 14807
 *	  San Luis Nabisco, CA 93406-4807 USA
 *	  Internet: mmcmahon@oboe.calpoly.edu
 *	  [Bug reports, suggestions, success stories, etc. are welcome; tech
 *	   support, and other unnecessary two-way mail, is not]
 */

#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <mem.h>
#include "general.h"
#include "wadfile.h"

/* Private wadfile variables */
static int	wadfileInitialized=FALSE; /* Module doesn't work unless TRUE */
static WadfileMalloc  wadfileMalloc;  /* User's allocate memory routine  */
static WadfileFree	  wadfileFree;	  /* User's free memory routine      */
static int wadfileNumAllocations;	  /* Keeps track of the malloc's     */

/*------------------------- Private routines ------------------------------*/

/**[ Internal 1 of 2 ]****************************************************
 *                                                                       *
 *	  wf_malloc 														 *
 *																		 *
 * Desc:                                                                 *
 *	   This calls the user's memory allocation routine and updates       *
 *	   the 'wadfileNumAllocations' variable.                             *
 *																		 *
 * Def: 																 *
 *	   static void * wf_malloc(unsigned long size); 					 *
 *                                                                       *
 * Parm:                                                                 *
 *	   size    Number of bytes to allocate. 							 *
 *																		 *
 * Retn:																 *
 *	   Pointer to allocated memory, or NULL if not enough memory.		 *
 *																		 *
 * Notes:                                                                *
 *	   This is the gateway for the WADFILE routines to access the user's *
 *	   memory allocation routine. Don't call 'wadfileMalloc()' directly. *
 *																		 *
 *************************************************************************/

static void * wf_malloc(unsigned long size)
{
	void * result;

	/* Sanity check */
    assert(wadfileMalloc != NULL);

	/* Allocate memory and bump wadfileNumAllocations if successful */
    result = wadfileMalloc(size);
	if (result != NULL)
	  wadfileNumAllocations++;

	return result;
}

/**[ Internal 2 of 2 ]****************************************************
 *                                                                       *
 *    wf_free                                                            *
 *                                                                       *
 * Desc:                                                                 *
 *     This calls the user's memory de-allocation routine and updates    *
 *     the 'wadfileNumAllocations' variable.                             *
 *                                                                       *
 * Def:                                                                  *
 *	   static void wf_free(void * ptr); 								 *
 *                                                                       *
 * Parm:                                                                 *
 *	   ptr	   Valid memory pointer that was allocated with wf_malloc.	 *
 *                                                                       *
 * Retn:                                                                 *
 *	   None 															 *
 *																		 *
 * Notes:                                                                *
 *     This is the gateway for the WADFILE routines to access the user's *
 *	   memory deallocation routine. Don't call 'wadfileFree()' directly. *
 *                                                                       *
 *************************************************************************/

static void wf_free(void * ptr)
{
	/* Sanity check */
	assert(wadfileFree != NULL);

    /* Bounds check -- wf_malloc doesn't change wadfileNumAllocations
	   with a NULL value, so wf_free behaves the same way with NULL. */
	if (ptr == NULL) return;

	/* Free the memory and update counter */
    wadfileFree(ptr);
	wadfileNumAllocations--;
}


/*-------------------------- Public routines ------------------------------*/

/**[ #1 ]*****************************************************************
 *																		 *
 *	  WadfileAddLump													 *
 *																		 *
 * Desc:                                                                 *
 *	   This writes a lump to a Wadfile being created, and adds a		 *
 *	   directory entry associated with the lump.						 *
 *																		 *
 * Def: 																 *
 *	  int WadfileAddLump(Wadfile * wad, unsigned long size, 			 *
 *						 char * name, char * data); 					 *
 *                                                                       *
 * Parm:                                                                 *
 *	   wad	   Wadfile to add the lump data to. 						 *
 *	   size    Size of lump data in bytes.								 *
 *	   name    Directory name of lump. IT MUST BE 8 CHARACTERS! 		 *
 *	   data    Pointer to lump data.									 *
 *																		 *
 * Retn:																 *
 *	   TRUE if the data is added successfully, FALSE otherwise. 		 *
 *																		 *
 * Notes:																 *
 *	  If compiling for a 16-bit target, the maximum lump size is 64k.	 *
 *																		 *
 *	  'name' should consist of capital letters, numbers, and the under-  *
 *	  score character. If the text is less than eight characters, it	 *
 *	  must be padded with zeroes. For example: "E1M1\0\0\0\0" is correct.*
 *                                                                       *
 *************************************************************************/

int WadfileAddLump(Wadfile * wad, unsigned long size, char * name, char * data)
{
	unsigned long location;
    char * ptr;

	/* Make sure WadfileInitialize has been called */
	assert(wadfileInitialized);

	/* Bounds check */
	if (!(wad->flags & FLAG_WADFILE_OPEN)) return FALSE;
	if (!(wad->flags & FLAG_WADFILE_WRITEONLY)) return FALSE;

	/* Bump up 'entryIndex' */
    if (wad->entryIndex == INVALID_ENTRY)
	  wad->entryIndex = 0;
	else
	  wad->entryIndex++;
	if (wad->entryIndex >= wad->dirNumEntries) return FALSE;

	/* Copy data to 'dirCache' */
	location = ftell(wad->wf);
	ptr = &wad->dirCache[wad->entryIndex*SIZEOF_WAD_DIR_ENTRY];
	memcpy(ptr, &location, SIZEOF_WAD_DIR_LOCATION);
    ptr += SIZEOF_WAD_DIR_LOCATION;
	memcpy(ptr, &size, SIZEOF_WAD_DIR_SIZE);
    ptr += SIZEOF_WAD_DIR_SIZE;
	memcpy(ptr, name, SIZEOF_WAD_DIR_NAME);

	/* Write data to Wadfile on disk */
	if (size != 0)
	 fwrite(data, size, 1, wad->wf);

	return TRUE;
}

/**[ #2 ]*****************************************************************
 *																		 *
 *	  WadfileClose														 *
 *																		 *
 * Desc:                                                                 *
 *	   This closes a Wadfile and flushes its disk cache. If the lump is  *
 *	   open, it is closed and the lump memory is released.	If the		 *
 *	   Wadfile is being created, the directory is written to disk and	 *
 *	   the file header is updated before the Wadfile is closed. 		 *
 *																		 *
 * Def: 																 *
 *	   void WadfileClose(Wadfile * wad);								 *
 *																		 *
 * Parm:																 *
 *	   wad	   The Wadfile to close.									 *
 *																		 *
 * Retn:																 *
 *	   None.															 *
 *																		 *
 *************************************************************************/

void WadfileClose(Wadfile * wad)
{
    uint32 size, location;

    /* Make sure WadfileInitialize has been called */
    assert(wadfileInitialized);

    /* If the file is open, flush caches, close file and clear the open flag */
    if (wad->flags & FLAG_WADFILE_OPEN)
    {
        /* If creating a file, write directory entries and fix header */
        if (wad->flags & FLAG_WADFILE_WRITEONLY)
        {
            if (wad->entryIndex != INVALID_ENTRY)
            {
               /* Initialize variables */
               size = wad->entryIndex + 1;
               location = ftell(wad->wf);

               /* Write directory to disk */
               fwrite(wad->dirCache, SIZEOF_WAD_DIR_ENTRY, size, wad->wf);

               /* Fix WAD header */
               fseek(wad->wf, SIZEOF_WAD_SIGNATURE, SEEK_SET);
               fwrite(&size, sizeof(size), 1, wad->wf);
               fwrite(&location, sizeof(location), 1, wad->wf);

            }
            else
            {
              /* Invalid Wad file, so destroy WAD Signature */
              fseek(wad->wf, 0L, SEEK_SET);
              size = 0;
              fwrite(&size, SIZEOF_WAD_SIGNATURE, 1, wad->wf);
            }
        }

        /* Flush 'dirCache' */
        if (wad->flags & FLAG_WADFILE_DIRCACHED)
        {
          wf_free(wad->dirCache);
          wad->flags ^= FLAG_WADFILE_DIRCACHED;
        }
		if (wad->flags & FLAG_LUMP_OPEN)
		 WadfileLumpClose(wad);

        /* Close file */
        fclose(wad->wf);
        wad->flags ^= FLAG_WADFILE_OPEN;
    }
}

/**[ #3 ]*****************************************************************
 *																		 *
 *	  WadfileCopyEntry													 *
 *																		 *
 * Desc:                                                                 *
 *	   This copies an arbitrary lump from an open Wadfile to a Wadfile	 *
 *	   that is being created. Do not use 'WadfileLumpOpen' or            *
 *	   'WadfileLumpClose' to initialize and release the lump data; this  *
 *	   routine is self-contained and opens, copies, and closes the lump  *
 *	   data in one fell swoop.											 *
 *																		 *
 * Def: 																 *
 *	   int WadfileCopyEntry(Wadfile * dest, char * destName,			 *
 *							Wadfile * src,	char * srcName) 			 *
 *                                                                       *
 * Parm:																 *
 *	   dest 	   A Wadfile that is being created. 					 *
 *	   destName    The directory entry name for the new lump.			 *
 *     src         An existing Wadfile.                                  *
 *	   srcName	   The directory entry name to search for and copy. 	 *
 *                                                                       *
 * Retn:																 *
 *	   TRUE if lump is copied succesfully; FALSE otherwise. 			 *
 *																		 *
 * Notes:																 *
 *	   On a 16-bit platform, this successfully copies lumps larger		 *
 *	   than 64k.														 *
 *																		 *
 *************************************************************************/

int WadfileCopyEntry(Wadfile * dest, char * destName,
                     Wadfile * src,  char * srcName)
{
    /* Sequentially search WAD directory for 'srcName' */
    if (!WadfileSeek(src, srcName)) return FALSE;

    /* Copy lump from source to destination */
    if (!WadfileLumpCopy(dest, destName, src)) return FALSE;

    return TRUE;
}

/**[ #4 ]*****************************************************************
 *																		 *
 *	  WadfileCopyMap													 *
 *																		 *
 * Desc:                                                                 *
 *	   This copies an arbitrary map from an open Wadfile to a Wadfile	 *
 *	   that is being created. Do not use 'WadfileLumpOpen' or            *
 *	   'WadfileLumpClose' to initialize and release the lump data; this  *
 *	   routine is self-contained and opens, copies, and closes the lump  *
 *	   data in one fell swoop.											 *
 *																		 *
 * Def: 																 *
 *	   int WadfileCopyMap(Wadfile * dest, int destEpisode, int destMap,  *
 *						  Wadfile * src,  int srcEpisode,  int srcMap)	 *
 *                                                                       *
 * Parm:																 *
 *	   dest 		A Wadfile that is being created.					 *
 *	   destEpisode	The new episode number for the map data.			 *
 *	   destMap		The new map number for the map data.				 *
 *	   src			An existing Wadfile.								 *
 *	   srcEpisode	The old episode number for the map data.			 *
 *	   srcMap		The old map number for the map data.				 *
 *                                                                       *
 * Retn:																 *
 *	   TRUE if map is copied succesfully; FALSE otherwise.				 *
 *																		 *
 * Notes:																 *
 *	   On a 16-bit platform, this successfully copies lumps larger		 *
 *	   than 64k.														 *
 *																		 *
 *************************************************************************/

int WadfileCopyMap(Wadfile * dest, int destEpisode, int destMap,
                   Wadfile * src,  int srcEpisode,  int srcMap)
{
    int i;
    char targetName[SIZEOF_WAD_DIR_NAME];

    /* Seek to map location, if possible */
    if (!WadfileSeekMap(src, srcEpisode, srcMap)) return FALSE;

    /* Copy the source map to destination map */
    if (!WadfileLumpOpen(src)) return FALSE;
    sprintf(targetName, "E%dM%d\0\0\0\0", destEpisode, destMap);
    if (!WadfileAddLump(dest, src->lumpSize, targetName, src->lumpData))
     return FALSE;
    if (!WadfileLumpClose(src)) return FALSE;

    /* Now that this is copied, go to next entry */
    WadfileGetNextDirInfo(src);

    /* Write out all other sections of the map */
    for (i=0; i<NUM_ENTRIES_PER_MAP-1; i++)
    {
        /* Copy lump from source to destination */
        if (!WadfileLumpCopy(dest, src->entryName, src))
         return FALSE;

        /* Go to next entry in the directory */
        WadfileGetNextDirInfo(src);
    }

    return TRUE;
}

/**[ #5 ]*****************************************************************
 *																		 *
 *	  WadfileCreate 													 *
 *																		 *
 * Desc:                                                                 *
 *	   This initializes a Wadfile structure, and creates a WAD file on	 *
 *	   disk.  This file can be either an IWAD or a PWAD file.			 *
 *																		 *
 * Def: 																 *
 *	   int	WadfileCreate(Wadfile * wad, char * filename,				 *
 *						  int type, int maxEntries);					 *
 *                                                                       *
 * Parm:																 *
 *	   wad		   The Wadfile to create.								 *
 *	   filename    The filename (with path) to use. 					 *
 *	   type 	   The file type, either IWAD or PWAD:					 *
 *			TYPE_IWAD	 Create an IWAD file.							 *
 *			TYPE_PWAD	 Create a PWAD file.							 *
 *	   maxEntries  The maximum number of directory entries that can be	 *
 *				   used in this Wadfile.								 *
 *																		 *
 * Retn:																 *
 *	   TRUE if the Wadfile is created successfully, FALSE otherwise.	 *
 *																		 *
 * Notes:																 *
 *	   If 'filename' exists on disk, the function fails. YOU must delete *
 *	   the file before calling this function.							 *
 *																		 *
 *	   'filename' should have a ".WAD" extension.                        *
 *																		 *
 *	   There is no reason to create an IWAD file.  You should always	 *
 *	   create a PWAD file.												 *
 *																		 *
 *	   Be liberal when choosing 'maxEntries'.  You have less RAM is you  *
 *	   choose too high, the program crashes if you choose too low. Each  *
 *	   entry takes 16 bytes of RAM. The registered DOOM has just over	 *
 *	   2000 entries.													 *
 *																		 *
 *************************************************************************/

int  WadfileCreate(Wadfile * wad, char * filename, int type, int maxEntries)
{
	uint32 longint;
	char   s[SIZEOF_WAD_SIGNATURE+1];

	/* Make sure WadfileInitialize has been called */
	assert(wadfileInitialized);

	/* Bounds check */
	if (filename == NULL) return FALSE;
	if ((type != TYPE_IWAD) && (type != TYPE_PWAD)) return FALSE;
	if (maxEntries <= 0) return FALSE;

	/* Make sure the memory allocation routines exist */
	if (wadfileMalloc == NULL) return FALSE;

	/* Initialize WAD structure */
	wad->flags = 0;
	wad->dirNumEntries = maxEntries;
	wad->entryIndex = INVALID_ENTRY;
	wad->entryName[SIZEOF_WAD_DIR_NAME] = 0; /* Permanent end of string mark */
	wad->dirCache = NULL;

	/* Make sure the file doesn't already exist */
    wad->wf = fopen(filename, "rb");
	if (wad->wf!=NULL) return FALSE;

	/* Create the file, return if error */
    wad->wf = fopen(filename, "wb");
    if (wad->wf==NULL) return FALSE;
    wad->flags ^= FLAG_WADFILE_OPEN;
	wad->flags ^= FLAG_WADFILE_WRITEONLY;

	/* Write signature to the Wadfile */
    if (type==TYPE_IWAD)
		strcpy(s, IWAD_SIGNATURE);
	else
		strcpy(s, PWAD_SIGNATURE);
	s[SIZEOF_WAD_SIGNATURE] = 0;
    fwrite(s, SIZEOF_WAD_SIGNATURE, 1, wad->wf);

	/* Write out dummy values for dir location and size as placeholders */
	longint=0;
	fwrite(&longint, sizeof(longint), 1, wad->wf);
	fwrite(&longint, sizeof(longint), 1, wad->wf);

	/* Create the directory with 'maxEntries' number of entries */
	wad->dirCache = wf_malloc(maxEntries*SIZEOF_WAD_DIR_ENTRY);
	if (wad->dirCache == NULL)
	{
	  WadfileClose(wad);
	  return FALSE;
	}
    wad->flags |= FLAG_WADFILE_DIRCACHED;

	return TRUE;
}

/**[ #6 ]*****************************************************************
 *																		 *
 *	  WadfileGetDirInfo 												 *
 *																		 *
 * Desc:                                                                 *
 *	   This loads a specific Wadfile directory entry into a Wadfile 	 *
 *	   structure.														 *
 *																		 *
 * Def: 																 *
 *	   int WadfileGetDirInfo(Wadfile * wad, int entryIndex);			 *
 *																		 *
 * Parm:																 *
 *	   wad			The Wadfile structure to process.					 *
 *	   entryIndex	Directory index to load in. 						 *
 *																		 *
 * Retn:																 *
 *	   TRUE if entry found and loaded succesfully; FALSE otherwise. 	 *
 *																		 *
 *************************************************************************/

int WadfileGetDirInfo(Wadfile * wad, int entryIndex)
{
    /* Make sure WadfileInitialize has been called */
    assert(wadfileInitialized);

    /* Bounds check */
    if (entryIndex < 0) return FALSE;
    if (!(wad->flags & FLAG_WADFILE_OPEN)) return FALSE;
    if (entryIndex > wad->dirNumEntries) return FALSE;

    /* Copy info from memory or disk */
    if (wad->flags & FLAG_WADFILE_DIRCACHED)
    {
     /* From memory cache */
     memcpy(&wad->entryLocation, &wad->dirCache[entryIndex*SIZEOF_WAD_DIR_ENTRY], SIZEOF_WAD_DIR_ENTRY);
    }
    else
    {
    /* Seek to the correct directory entry and read it in from disk */
    fseek(wad->wf, wad->dirLocation + SIZEOF_WAD_DIR_ENTRY*entryIndex, SEEK_SET);
    fread(&wad->entryLocation, SIZEOF_WAD_DIR_ENTRY, 1, wad->wf);
    }

	/* Update WAD structure */
    wad->entryIndex = entryIndex;

    return TRUE;
}

/**[ #7 ]*****************************************************************
 *																		 *
 *	  WadfileGetNextDirInfo 											 *
 *																		 *
 * Desc:                                                                 *
 *	   This loads the next Wadfile directory entry into a Wadfile		 *
 *	   structure.														 *
 *																		 *
 * Def: 																 *
 *	   int WadfileGetNextDirInfo(Wadfile * wad);						 *
 *																		 *
 * Parm:																 *
 *	   wad			The Wadfile structure to process.					 *
 *																		 *
 * Retn:																 *
 *	   TRUE if entry loaded succesfully; FALSE otherwise.				 *
 *																		 *
 * Notes:																 *
 *	   This routine fails if you haven't called 'WadfileGetDirInfo' at   *
 *	   least once with this Wadfile variable.							 *
 *																		 *
 *************************************************************************/

int WadfileGetNextDirInfo(Wadfile * wad)
{
    /* Make sure WadfileInitialize has been called */
    assert(wadfileInitialized);

    return WadfileGetDirInfo(wad, wad->entryIndex+1);
}

/**[ #8 ]*****************************************************************
 *																		 *
 *	  WadfileGetPrevDirInfo 											 *
 *																		 *
 * Desc:                                                                 *
 *	   This loads the previous Wadfile directory entry into a Wadfile	 *
 *	   structure.														 *
 *																		 *
 * Def: 																 *
 *	   int WadfileGetPrevDirInfo(Wadfile * wad);						 *
 *																		 *
 * Parm:																 *
 *	   wad			The Wadfile structure to process.					 *
 *																		 *
 * Retn:																 *
 *	   TRUE if entry loaded succesfully; FALSE otherwise.				 *
 *																		 *
 * Notes:																 *
 *	   This routine fails if you haven't called 'WadfileGetDirInfo' at   *
 *	   least once with this Wadfile variable.							 *
 *																		 *
 *************************************************************************/

int WadfileGetPrevDirInfo(Wadfile * wad)
{
    /* Make sure WadfileInitialize has been called */
    assert(wadfileInitialized);

    return WadfileGetDirInfo(wad, wad->entryIndex-1);
}


/**[ #9 ]*****************************************************************
 *																		 *
 *	  WadfileInitialize 												 *
 *																		 *
 * Desc:                                                                 *
 *	   This initializes the Wadfile support and must be called before	 *
 *	   any other Wadfile routines are called.							 *
 *																		 *
 * Def: 																 *
 *	   int WadfileInitialize(WadfileMalloc * wfm, WadfileFree * wff);	 *
 *                                                                       *
 * Parm:                                                                 *
 *	   wfm	   Malloc routine for Wadfile routines (can be NULL)		 *
 *	   wff	   Free memory routine for Wadfile routines (can be NULL)	 *
 *																		 *
 * Retn:																 *
 *	   TRUE if the memory routines are operational, FALSE otherwise.	 *
 *																		 *
 * Notes:																 *
 *	 Either both of the parameters are NULL or not. It is a fatal error  *
 *	 to specify one routine and not the other.							 *
 *																		 *
 *	 Any time you want to specify different memory allocation routines	 *
 *	 for the Wadfile library, just call this routine again. If there	 *
 *	 are lingering allocations, the function will fail. 				 *
 *																		 *
 *************************************************************************/

int WadfileInitialize(WadfileMalloc wfm, WadfileFree wff)
{
	/* Bounds check--vital enough to be asserts */
	assert(((wfm==NULL) && (wff==NULL))||
		   ((wfm!=NULL) && (wff!=NULL)));

	/* See if we're getting re-initialized */
    if (wadfileInitialized)
	{
	  /* Any lingering allocations? */
	  if (wadfileNumAllocations)
		wadfileInitialized = FALSE;
	  else
	  {
		/* No? Re-initialize. */
		wadfileMalloc = wfm;
		wadfileFree   = wff;
      }
	}
	else
	{
	 /* Make sure the compiler understands the code */
	 assert(sizeof(int16) == 2);
	 assert(sizeof(uint16) == 2);
	 assert(sizeof(int32) == 4);
	 assert(sizeof(uint32) == 4);

	 /* Initialize the memory handlers */
	 wadfileNumAllocations = 0;
	 wadfileMalloc = wfm;
	 wadfileFree   = wff;

     wadfileInitialized = TRUE;
    }

	return wadfileInitialized;
}

/**[ #10 ]****************************************************************
 *																		 *
 *	  WadfileLumpClose													 *
 *																		 *
 * Desc:                                                                 *
 *	   This releases the memory allocated to the lump that is stored	 *
 *	   in a Wadfile structure.	This memory is allocated and filled 	 *
 *	   by the 'WadfileLumpOpen' routine.                                 *
 *																		 *
 * Def: 																 *
 *	   int WadfileLumpClose(Wadfile * wad); 							 *
 *                                                                       *
 * Parm:																 *
 *	   wad			The Wadfile structure to process.					 *
 *																		 *
 * Retn:																 *
 *	   TRUE if lump memory freed succesfully; FALSE otherwise.			 *
 *																		 *
 *************************************************************************/

int WadfileLumpClose(Wadfile * wad)
{
    /* Make sure WadfileInitialize has been called */
    assert(wadfileInitialized);

	/* Bounds check */
	if (wadfileMalloc==NULL) return FALSE;
	if (!(wad->flags & FLAG_LUMP_OPEN)) return FALSE;

	/* Deallocate the LUMP data */
	if (wad->lumpSize != 0)
	 wf_free(wad->lumpData);

	/* Update Wadfile structure */
	wad->flags ^= FLAG_LUMP_OPEN;

	return TRUE;
}

/**[ #11 ]****************************************************************
 *																		 *
 *	  WadfileLumpCopy													 *
 *																		 *
 * Desc:                                                                 *
 *	   This copies the lump data from an open Wadfile to a Wadfile that  *
 *	   is being created. Do not use 'WadfileLumpOpen' or                 *
 *	   'WadfileLumpClose' to initialize and release the lump data; this  *
 *	   routine is self-contained and opens, copies, and closes the lump  *
 *	   data in one fell swoop.											 *
 *																		 *
 * Def: 																 *
 *	int WadfileLumpCopy(Wadfile * dest, char * destName, Wadfile * src); *
 *                                                                       *
 * Parm:																 *
 *	   dest 	   A Wadfile that is being created. 					 *
 *	   destName    The directory entry name for the new lump.			 *
 *     src         An existing Wadfile.                                  *
 *																		 *
 * Retn:																 *
 *	   TRUE if lump is copied succesfully; FALSE otherwise. 			 *
 *																		 *
 * Notes:																 *
 *	   On a 16-bit platform, this successfully copies lumps larger		 *
 *	   than 64k.														 *
 *																		 *
 *************************************************************************/

int WadfileLumpCopy(Wadfile * dest, char * destName, Wadfile * src)
{
    unsigned long allocSize;
	unsigned long lumpCopySize;
    unsigned long location;
    char * ptr;

    /* Make sure WadfileInitialize has been called */
    assert(wadfileInitialized);

    /* Bounds check */
    if (wadfileMalloc==NULL) return FALSE;
    if (!(dest->flags & FLAG_WADFILE_OPEN)) return FALSE;
    if (!(src->flags & FLAG_WADFILE_OPEN)) return FALSE;
    if (!(dest->flags & FLAG_WADFILE_WRITEONLY)) return FALSE;
    if (src->flags & FLAG_WADFILE_WRITEONLY) return FALSE;
	if (dest->flags & FLAG_LUMP_OPEN) return FALSE;
    if (src->entryIndex == INVALID_ENTRY) return FALSE;
	if (destName == NULL) return FALSE;

	/* Set LUMP variables */
	dest->lumpLocation = src->entryLocation;
	dest->lumpSize	   = src->entrySize;
	memcpy(dest->lumpName, destName, SIZEOF_WAD_DIR_NAME);
    location = ftell(dest->wf);

    /* Seek to start of data to be read */
    fseek(src->wf, src->entryLocation, SEEK_SET);

    /* Set up loop control variable */
	lumpCopySize = dest->lumpSize;

    /* Do the copy */
	while (lumpCopySize != 0)
    {
     /* Allocate a buffer */
	 if (lumpCopySize >= MALLOC_MAX)
        allocSize = MALLOC_MAX;
     else
		allocSize = lumpCopySize;
	 dest->lumpData    = wf_malloc(allocSize);
	 if (dest->lumpData == NULL) return FALSE;

	 /* Read LUMP data from 'src' and write to 'dest' */
	 fread(dest->lumpData, allocSize, 1, src->wf);
	 fwrite(dest->lumpData, allocSize, 1, dest->wf);

     /* Free the buffer */
	 wf_free(dest->lumpData);

     /* Update our byte count */
	 lumpCopySize  -= allocSize;
    } /* while */

    /* Bump up 'entryIndex' */
    if (dest->entryIndex == INVALID_ENTRY)
      dest->entryIndex = 0;
    else
      dest->entryIndex++;
    if (dest->entryIndex >= dest->dirNumEntries) return FALSE;

    /* Copy data to 'dirCache' */
    ptr = &dest->dirCache[dest->entryIndex*SIZEOF_WAD_DIR_ENTRY];
    memcpy(ptr, &location, SIZEOF_WAD_DIR_LOCATION);
    ptr += SIZEOF_WAD_DIR_LOCATION;
	memcpy(ptr, &dest->lumpSize, SIZEOF_WAD_DIR_SIZE);
    ptr += SIZEOF_WAD_DIR_SIZE;
	memcpy(ptr, dest->lumpName, SIZEOF_WAD_DIR_NAME);

    return TRUE;
}

/**[ #12 ]****************************************************************
 *																		 *
 *	  WadfileLumpOpen													 *
 *																		 *
 * Desc:                                                                 *
 *	   This allocates memory and reads in the lump associated with the	 *
 *	   current directory entry in the Wadfile structure.  The memory	 *
 *	   is released later on by using the 'WadfileLumpClose' routine.     *
 *																		 *
 * Def: 																 *
 *	   int WadfileLumpOpen(Wadfile * wad);								 *
 *                                                                       *
 * Parm:																 *
 *	   wad			The Wadfile structure to process.					 *
 *																		 *
 * Retn:																 *
 *	   TRUE if lump allocated and read in succesfully; FALSE otherwise.  *
 *																		 *
 * Notes:																 *
 *	   This routine fails if you haven't called 'WadfileGetDirInfo' at   *
 *	   least once with this Wadfile variable.							 *
 *																		 *
 *	   If you're using a 16-bit compiler, the maximum lump size is 64k.  *
 *	   This function fails if the lump is too big.	Only 1 lump out of	 *
 *	   2045 in the registered version of DOOM is >64kb. (E2M7 SIDEDEFS)  *
 *	   Use 'WadfileLumpCopy' to work around the 64k limitation.          *
 *                                                                       *
 *************************************************************************/

int WadfileLumpOpen(Wadfile * wad)
{
    /* Make sure WadfileInitialize has been called */
    assert(wadfileInitialized);

	/* Bounds check */
    if (wadfileMalloc==NULL) return FALSE;
	if (!(wad->flags & FLAG_WADFILE_OPEN)) return FALSE;
	if (wad->flags & FLAG_WADFILE_WRITEONLY) return FALSE;
	if (wad->flags & FLAG_LUMP_OPEN) return FALSE;
	if (wad->entryIndex == INVALID_ENTRY) return FALSE;

	/* Allocate memory for LUMP */
    if (wad->entrySize != 0)
	{
	 wad->lumpData = wf_malloc(wad->entrySize);
	 if (wad->lumpData == NULL) return FALSE;
	}

	/* Set LUMP variables */
	wad->lumpLocation = wad->entryLocation;
	wad->lumpSize	  = wad->entrySize;
	memcpy(wad->lumpName, wad->entryName, SIZEOF_WAD_DIR_NAME);

	/* Read LUMP data from disk */
	if (wad->lumpSize != 0)
	{
		fseek(wad->wf, wad->entryLocation, SEEK_SET);
		fread(wad->lumpData, wad->lumpSize, 1, wad->wf);
	}

	/* Update Wadfile structure */
	wad->flags |= FLAG_LUMP_OPEN;

	return TRUE;
}

/**[ #13 ]****************************************************************
 *																		 *
 *	  WadfileOpen														 *
 *                                                                       *
 * Desc:																 *
 *	   This opens a WAD file and initializes a Wadfile context. 		 *
 *																		 *
 * Def: 																 *
 *     int WadfileOpen(Wadfile * wad, char * filename, int type);        *
 *																		 *
 * Parm:																 *
 *	   wad			   The Wadfile context								 *
 *	   filename 	   The file name (including path) of the WAD file.	 *
 *	   type 		   The type of WAD file to open:					 *
 *			   WANTIWAD  accept only IWAD files.						 *
 *			   WANTPWAD  accept only PWAD files.						 *
 *			   IWADPWAD  accept either IWAD or PWAD files.				 *
 *																		 *
 * Retn:																 *
 *	   TRUE if the file exists and is open, FALSE otherwise.			 *
 *																		 *
 *************************************************************************/

int WadfileOpen(Wadfile * wad, char * filename, int type)
{
	uint32 longint;
	char   s[SIZEOF_WAD_SIGNATURE+1];

	/* Make sure WadfileInitialize has been called */
	assert(wadfileInitialized);

	/* Initialize WAD structure */
	wad->flags = 0;
	wad->entryIndex = INVALID_ENTRY;
	wad->entryName[SIZEOF_WAD_DIR_NAME] = 0; /* Permanent end of string mark */
	wad->dirCache = NULL;

	/* Open the file, return if error */
    wad->wf = fopen(filename, "rb");
	if (wad->wf==NULL) return FALSE;
	wad->flags ^= FLAG_WADFILE_OPEN;

	/* Read in and check the WAD file signature */
	fread(s, SIZEOF_WAD_SIGNATURE, 1, wad->wf);
	s[SIZEOF_WAD_SIGNATURE] = 0;
	if (strcmp(s, IWAD_SIGNATURE) == 0)
	 wad->flags |= FLAG_WADFILE_IWAD;
	else
	if (strcmp(s, PWAD_SIGNATURE) != 0)
	 goto Exit_Error;

	/* Read in directory location and size */
	fread(&longint, sizeof(longint), 1, wad->wf);
	wad->dirNumEntries = longint;
	fread(&longint, sizeof(longint), 1, wad->wf);
	wad->dirLocation   = longint;

	/* Make sure the file type matches what the caller is asking for */
	if (((type == WANTPWAD)&&(wad->flags & FLAG_WADFILE_IWAD)) ||
		((type == WANTIWAD)&&(!(wad->flags & FLAG_WADFILE_IWAD))))
	{
	Exit_Error:
		WadfileClose(wad);
		return FALSE;
	}

	/* If memory allocation is available, try to cache the directory */
	if (wadfileMalloc != NULL)
	{
		wad->dirCache = wf_malloc(wad->dirNumEntries*SIZEOF_WAD_DIR_ENTRY);
		if (wad->dirCache != NULL)
		{
		  wad->flags |= FLAG_WADFILE_DIRCACHED;
		  fseek(wad->wf, wad->dirLocation, SEEK_SET);
		  fread(wad->dirCache, wad->dirNumEntries*SIZEOF_WAD_DIR_ENTRY,
				1,wad->wf);
		}
	}

    return TRUE;
}

/**[ #14 ]****************************************************************
 *																		 *
 *	  WadfileSeek														 *
 *																		 *
 * Desc:                                                                 *
 *	   This routine does a linear search through the Wadfile's directory *
 *	   starting with the first directory entry and searching until it	 *
 *	   finds its target or hits the end of the directory.				 *
 *																		 *
 * Def: 																 *
 *	   int WadfileSeek(Wadfile * wad, char * targetName);				 *
 *                                                                       *
 * Parm:																 *
 *	   wad			The Wadfile structure to process.					 *
 *	   targetName	The name of the target directory entry. 			 *
 *																		 *
 * Retn:																 *
 *	   TRUE if target was found succesfully; FALSE otherwise.			 *
 *	   If TRUE, then the Wadfile's 'entryIndex', 'entryName',            *
 *     'entrySize', and 'entryLocation' contain meaningful values.       *
 *																		 *
 * Notes:																 *
 *	   'targetName' can be less than 8 characters; that is, you do not   *
 *	   have to pad it with zeroes, like in 'WadfileAddLump'.             *
 *																		 *
 *	   See also 'WadfileSeekMap' if you are looking for a specific map.  *
 *                                                                       *
 *************************************************************************/

int WadfileSeek(Wadfile * wad, char * targetName)
{
    int i;
    char targetString[SIZEOF_WAD_DIR_NAME+1];

	/* Make sure WadfileInitialize has been called */
	assert(wadfileInitialized);

    /* Convert target name to a null-terminated string */
    strncpy(targetString, targetName, SIZEOF_WAD_DIR_NAME);
    targetString[SIZEOF_WAD_DIR_NAME] = '\0';

    /* Bounds Check */
	if (!(wad->flags & FLAG_WADFILE_OPEN)) return FALSE;
	if (targetString == NULL) return FALSE;	 /* Too small */
	if (strlen(targetString)>SIZEOF_WAD_DIR_NAME) return FALSE;  /* Too big   */

	/* Begin linear search of the WAD directory */
    i=0;
	while ((i<wad->dirNumEntries)&&(strcmp(wad->entryName, targetString) !=0))
	{
		WadfileGetDirInfo(wad, i);
		i++;
	}

	return (i<wad->dirNumEntries);
}

/**[ #15 ]****************************************************************
 *																		 *
 *	  WadfileSeekMap													 *
 *																		 *
 * Desc:                                                                 *
 *	   This routine does a linear search through the Wadfile's directory *
 *	   starting with the first directory entry and searching until it	 *
 *	   finds its target map or hits the end of the directory.			 *
 *																		 *
 * Def: 																 *
 *	   int WadfileSeekMap(Wadfile * wad, int episodeNum, int mapNum);	 *
 *                                                                       *
 * Parm:																 *
 *	   wad		   The Wadfile structure to process.					 *
 *	   episodeNum  Episode to find. Valid range is 1...DOOM_LASTEPISODE. *
 *	   mapNum	   Map to find. Valid range is 1...DOOM_LASTMAP.		 *
 *                                                                       *
 * Retn:																 *
 *	   TRUE if target was found succesfully; FALSE otherwise.			 *
 *	   If TRUE, then the Wadfile's 'entryIndex', 'entryName',            *
 *     'entrySize', and 'entryLocation' contain meaningful values.       *
 *																		 *
 * Notes:																 *
 *	   This routine uses 'WadfileSeek' as its search engine.  When you   *
 *	   aren't looking for maps, use 'WadfileSeek'.                       *
 *                                                                       *
 *************************************************************************/

int WadfileSeekMap(Wadfile * wad, int episodeNum, int mapNum)
{
	char targetName[5];

	/* Make sure WadfileInitialize has been called */
	assert(wadfileInitialized);

    /* Bounds Check */
	if ((episodeNum < 0) || (episodeNum > DOOM_LASTEPISODE)) return FALSE;
	if ((mapNum < 0) || (mapNum > DOOM_LASTMAP)) return FALSE;

	/* Compose directory name */
    sprintf(targetName, "E%dM%d",episodeNum, mapNum);

	/* Sequentially search WAD directory for ExMy */
	return WadfileSeek(wad, targetName);
}


