/******************************************************************************
	PROGRAM:	VNB.C
	WRITTEN BY:	Robert Fenske, Jr. (rfenske@swri.edu)
				Southwest Research Institute
				Electromagnetics Division
				6220 Culebra
				San Antonio, Texas 78238-5166
	CREATED:	May  1994
	DESCRIPTION:	This program is the VERDA Node Builder.  It extracts
			the data from a DOOM-related PWAD, IWAD, or VERDA
			patch file and builds the BSP-related structures segs,
			subsectors, and nodes.  It can build these structures
			for all the levels in the input file, or for a specific
			level found in the input file.  The command line
			arguments are as follows:
				[-e#m#] <input file> [output file]
			where -e#m# specifies a particular level within the
			input level, input file is the input filename, and
			output file is the optional output filename.  If no
			output file is specified, the input file is rewritten
			with the new data.

			This program has been compiled and run under SunOS
			using cc and under MS-DOS using DJGPP.

			There is a number of byte swapping calls used in the
			SunOS case; these calls are ugly, but necessary since
			the WAD files store data in little-endian order.

			DOOM is a trademark of id Software, Inc.
******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "dmglobal.i"

#define SOFTVER		1.050			/* software version */
#define SOFTDATE	"May 1994"		/* software version date */

#define RESOURCES_NEEDED ((1<<LINES)|(1<<VERTS))

#define update_resource(s,d,n) \
			(blockfree(WInfo.data[WInfo.lvlndx+(s)]),\
			 WInfo.data[WInfo.lvlndx+(s)] = (char *)(d),\
			 WInfo.dir[WInfo.lvlndx+(s)].nbytes = \
			                             (long)(n)*datasize[s],\
			 WInfo.changed[WInfo.lvlndx+(s)] = TRUE)

global WAD_INFO WInfo;				/* WAD information */

local long datasize[] = { 1, sizeof(DOOM_VERT), sizeof(DOOM_LINE),
			  sizeof(DOOM_SIDE), sizeof(DOOM_VERT),
			  sizeof(DOOM_SEGS), sizeof(DOOM_SSECTOR),
			  sizeof(DOOM_NODE), sizeof(DOOM_SECTOR),
			  sizeof(DOOM_REJECT), sizeof(DOOM_BLOCKMAP)
};


/******************************************************************************
	ROUTINE:	main(ac,av)
	WRITTEN BY:	Robert Fenske, Jr.
	CREATED:	May  1994
	DESCRIPTION:	This routine processes the command line arguments
			and executes the appropriate build function.
******************************************************************************/
main(ac,av)
int ac;
char *av[];
{
  char *ifile = NULL, *ofile = NULL;		/* input/output filenames */
  boolean help = FALSE;				/* whether to display help */
  boolean good = FALSE;				/* whether read/write worked */
  boolean rewrite;				/* whether input rewritten */
  boolean wad = FALSE,				/* (I/P)WAD file flag */
          patch = FALSE;			/* VERDA patch file flag */
  int epdo = 0, mpdo = 0;			/* specific ep/map to do */
  register FILE *ifp, *ofp;			/* input/output file ptrs */
  register int i;

  setbuf(stdout,(char *)NULL);			/* make stdout unbuffered */
  printf("\n\t\t\t\tVERDA Node Builder\n");
  printf("\tVersion %5.3lf of %s by Robert Fenske, Jr (rfenske@swri.edu)\n",
         SOFTVER,SOFTDATE);
  for (i = 1; i < ac; i++) {			/* scan all arguments */
    if (av[i][0] == '-') {
      if (2 != sscanf(av[i],"-e%1dm%1d",&epdo,&mpdo)) help = TRUE;
    }else if (ifile == NULL)
      ifile = ofile = av[i];
    else
      ofile = av[i];
  }
  if (ifile == NULL || help) {			/* show now to do corectly */
    printf("\n%s [-e#m#] <input file> [output file]\n\n",av[0]);
    printf("%*s -e#m#          specify level to process;\n",
           strlen(av[0]),"");
    printf("%*s                otherwise does all levels\n",
           strlen(av[0]),"");
    printf("%*s <input file>   PWAD, IWAD, or VERDA patch file\n",
            strlen(av[0]),"");
    printf("%*s <output file>  output file; if none specified,\n",
            strlen(av[0]),"");
    printf("%*s                input file is rewritten\n",
           strlen(av[0]),"");
    return 1;
  }
  ifp = fopen(ifile,"rb");			/* open input file */
  if (ifp == NULL) {
    fprintf(stderr,"\nunable to open %s for reading\n",ifile);
    return 1;
  }
  fread((char *)&WInfo.head,sizeof WInfo.head,1,ifp);
  if (strncmp(WInfo.head.ident,"PWAD",4) == 0 ||
      strncmp(WInfo.head.ident,"IWAD",4) == 0) wad = TRUE;
  rewind(ifp);
  if (2 == fscanf(ifp," %d %d %*d %*lf",&WInfo.ep,&WInfo.mp)) patch = TRUE;
  if (!wad && !patch) {				/* not a valid file */
    fprintf(stderr,"\n%s is not a PWAD, IWAD, nor VERDA patch file\n",ifile);
    return 1;
  }
  rewrite = !patch && ifile == ofile;		/* whether input rewritten */
  WInfo.lvlndx = -ALL;
  do {						/* process file until done */
    printf("\nReading %s file %s...",patch?"patch":"WAD",ifile);
    if (patch)     good = patch_read(ifp,epdo,mpdo,RESOURCES_NEEDED);
    else if (wad)  good = wad_read(ifp,epdo,mpdo,RESOURCES_NEEDED);
    if (good) {					/* process new level data */
      DOOM_LINE *lines = Lines;
      DOOM_VERT *verts = Verts;
      DOOM_SEGS *segs = Segs;
      DOOM_SSECTOR *ssecs = Ssecs;
      DOOM_NODE *nodes = Nodes;
      DOOM_BLOCKMAP *blockmaps = Blockmaps;
      printf("done\n");
      printf("Building BSP Tree for level E%1dM%1d...",WInfo.ep,WInfo.mp);
      nodes_make(&nodes,&NNodes,&ssecs,&NSsecs,&segs,&NSegs,&verts,&NVerts,
                 &lines,&NLines);
      update_resource(VERTS,verts,NVerts);
      update_resource(SEGS,segs,NSegs);
      update_resource(SSECTS,ssecs,NSsecs);
      update_resource(NODES,nodes,NNodes);
      printf("%d nodes, %d segs          \n",NNodes,NSegs);
      printf("Building BLOCKMAP for level E%1dM%1d...",WInfo.ep,WInfo.mp);
      NBlockmaps = blockmap_make(&blockmaps,Lines,NLines,Verts);
      for (i = 0; i < NBlockmaps; i++)
        blockmaps[i] = bswapw(blockmaps[i]);
      update_resource(BLKMAPS,blockmaps,NBlockmaps);
      printf("done\n");
    }else if (WInfo.lvlndx >= 0)		/* no more in file */
      printf("no more levels\n");
    else					/* oops: bogus data */
      printf("failed\n");
    if (good) {					/* if processed, write it */
      ofp = fopen(ofile,rewrite?"r+b":"wb");
      if (ofp == NULL) {
        fprintf(stderr,"unable to open %s for writing\n",ofile);
        return 1;
      }
      printf("Writing %s file %s...",patch?"patch":"WAD",ofile);
      if (patch)     good = patch_write(ifp,ofp,rewrite);
      else if (wad)  good = wad_write(ifp,ofp,rewrite);
      fclose(ofp);
      if (good) printf("done\n");
      else      printf("failed\n");
    }
  } while (good);
  fclose(ifp);
  return 0;					/* everything is okay */
}


/******************************************************************************
	ROUTINE:	patch_read(patch,epdo,mpdo,resources_needed)
	WRITTEN BY:	Robert Fenske, Jr.
	CREATED:	May  1994
	DESCRIPTION:	This routine reads a VERDA patch file.  It reads the
			file twice; the first time is to obtain the sizes of
			each of the resources.  The input resources_needed is
			ignored--everything in the patch file is read.  Also,
			only one level's data is stored in a patch file.
******************************************************************************/
patch_read(patch,epdo,mpdo,resources_needed)
register FILE *patch;
int epdo, mpdo, resources_needed;
{
  char str[256];
  int type;
  int i = 0;
  register int t = 0, l = 0, s = 0, v = 0, c = 0;

  WInfo.lvlndx += ALL;
  if (WInfo.lvlndx != 0)			/* can't do more than one */
    return FALSE;				/* level with patch file  */
  WInfo.dir = blockmem(DIR_ENTRY,WInfo.head.count = ALL);
  WInfo.data = blockmem(char *,WInfo.head.count);
  WInfo.changed = blockmem(boolean,WInfo.head.count);
  WInfo.count = blockmem(int,WInfo.head.count);
  sprintf(WInfo.dir[WInfo.lvlndx+MAINS].name,"E%1dM%1d",WInfo.ep,WInfo.mp);
  rewind(patch);
  while (fgets(str,sizeof str,patch) != NULL) {
    if (i == 0) {
      i = sscanf(str," %d %d %d %lf",&WInfo.ep,&WInfo.mp,&type,&WInfo.ver);
      if (i && epdo && mpdo && (WInfo.mp+1 != epdo || WInfo.ep+1 != mpdo))
        return FALSE;				/* doesn't match desired */
      continue;					/* level                 */
    }
    switch (type) {
      bcase THINGS: i = str[0] != '%'; if (i) t++;
      bcase LINES:  i = str[0] != '%'; if (i) l++;
      bcase SIDES:  i = str[0] != '%'; if (i) s++;
      bcase VERTS:  i = str[0] != '%'; if (i) v++;
      bcase SEGS:   i = str[0] != '%';
      bcase SSECTS: i = str[0] != '%';
      bcase NODES:  i = str[0] != '%';
      bcase SECTS:  i = str[0] != '%'; if (i) c++;
    }
  }
  WInfo.data[THINGS] = (char *)blockmem(DOOM_THING, WInfo.count[THINGS] = t);
  WInfo.data[LINES]  = (char *)blockmem(DOOM_LINE,  WInfo.count[LINES]  = l);
  WInfo.data[SIDES]  = (char *)blockmem(DOOM_SIDE,  WInfo.count[SIDES]  = s);
  WInfo.data[VERTS]  = (char *)blockmem(DOOM_VERT,  WInfo.count[VERTS]  = v);
  WInfo.data[SECTS]  = (char *)blockmem(DOOM_SECTOR,WInfo.count[SECTS]  = c);
  i = t = l = s = v = c = 0;
  rewind(patch);
  while (fgets(str,sizeof str,patch) != NULL) {
    if (i == 0) {
      i = sscanf(str," %d %d %d %lf",&WInfo.ep,&WInfo.mp,&type,&WInfo.ver);
      continue;
    }
    switch (type) {
      bcase THINGS:				/* THINGS */
	i = sscanf(str,"%*d %4hx %4hx %3hd %4hx %4hx",
		   &Things[t].x,&Things[t].y,&Things[t].angle,
		   &Things[t].flag,&Things[t].item);
	if (i==5) t++;
      bcase LINES:				/* LINES */
	i = sscanf(str,"%*d %4hx %4hx %4hx %4hx %4hx %4hx %4hx",
		   &Lines[l].fndx,&Lines[l].tndx,&Lines[l].flag,
		   &Lines[l].action_flag,&Lines[l].sect_tag,
		   &Lines[l].rsidndx,&Lines[l].lsidndx);
	if (i==7) l++;
      bcase SIDES:				/* SIDES */
	i = sscanf(str,"%*d %4hx %4hx %8c %8c %8c %4hx",
		   &Sides[s].image_xoff,&Sides[s].image_yoff,
		   Sides[s].lowwall,Sides[s].highwall,Sides[s].fullwall,
		   &Sides[s].sectndx);
	if (i==6) s++; else i = 0;
      bcase VERTS:				/* VERTEXES */
	i = sscanf(str,"%*d %4hx %4hx",&Verts[v].x,&Verts[v].y);
	if (i==2) v++;
      bcase SEGS:    i = str[0] != '%';		/* SEGS */
      bcase SSECTS:  i = str[0] != '%';		/* SSECTORS */
      bcase NODES:   i = str[0] != '%';		/* NODES */
      bcase SECTS:				/* SECTORS */
	i = sscanf(str,"%*d %4hx %4hx %8c %8c %4hx %4hx %4hx",
		   &Sects[c].floor_ht,&Sects[c].ceil_ht,Sects[c].floor_desc,
		   Sects[c].ceil_desc,&Sects[c].light_lvl,&Sects[c].property,
		   &Sects[c].line_tag);
	if (i==7) c++; else i = 0;
    }
  }
  NThings = t;
  NLines = l;
  NSides = s;
  NVerts = v;
  NSects = c;
  WInfo.ep++; WInfo.mp++;
  return TRUE;
}


/******************************************************************************
	ROUTINE:	patch_write(rpatch,wpatch,rewrite)
	WRITTEN BY:	Robert Fenske, Jr.
	CREATED:	May  1994
	DESCRIPTION:	This routine writes a VERDA patch file.  Only one
			level's data is stored in a patch file.
******************************************************************************/
patch_write(rpatch,wpatch,rewrite)
register FILE *rpatch, *wpatch;
boolean rewrite;
{
  register int i;

  if (WInfo.lvlndx != 0)			/* can't do more than one */
    return FALSE;				/* level with patch file  */
  fprintf(wpatch,"%d %d %d %4.2lf\n",--WInfo.ep,--WInfo.mp,THINGS,WInfo.ver);
  for (i = 0; i < NThings; i++)
    fprintf(wpatch,"%03d %04x %04x %03d %04x %02x\n",i,
	   (ushort)Things[i].x,(ushort)Things[i].y,Things[i].angle,
	   Things[i].flag,Things[i].item);
  fprintf(wpatch,"%%\n%d %d %d %4.2lf\n",WInfo.ep,WInfo.mp,LINES,WInfo.ver);
  for (i = 0; i < NLines; i++)
    fprintf(wpatch,"%03d %04x %04x %04x %04x %04x %04x %04x\n",i,
	   Lines[i].fndx,Lines[i].tndx,
	   (ushort)Lines[i].flag,(ushort)Lines[i].action_flag,
	   (ushort)Lines[i].sect_tag,
	   (ushort)Lines[i].rsidndx,(ushort)Lines[i].lsidndx);
  fprintf(wpatch,"%%\n%d %d %d %4.2lf\n",WInfo.ep,WInfo.mp,SIDES,WInfo.ver);
  for (i = 0; i < NSides; i++)
    fprintf(wpatch,"%03d %04x %04x %-8.8s %-8.8s %-8.8s %03x\n",i,
	   (ushort)Sides[i].image_xoff,(ushort)Sides[i].image_yoff,
	   Sides[i].lowwall,Sides[i].highwall,Sides[i].fullwall,
	   Sides[i].sectndx);
  fprintf(wpatch,"%%\n%d %d %d %4.2lf\n",WInfo.ep,WInfo.mp,VERTS,WInfo.ver);
  for (i = 0; i < NVerts; i++)
    fprintf(wpatch,"%03d %04x %04x\n",i,
	   (ushort)Verts[i].x,(ushort)Verts[i].y);
  fprintf(wpatch,"%%\n%d %d %d %4.2lf\n",WInfo.ep,WInfo.mp,SEGS,WInfo.ver);
  for (i = 0; i < NSegs; i++)
    fprintf(wpatch,"%03d %04x %04x %04x %04x %04x %04x\n",i,
	   Segs[i].fndx,Segs[i].tndx,(ushort)Segs[i].angle,
	   Segs[i].lndx,Segs[i].sndx,Segs[i].loffset);
  fprintf(wpatch,"%%\n%d %d %d %4.2lf\n",WInfo.ep,WInfo.mp,SSECTS,WInfo.ver);
  for (i = 0; i < NSsecs; i++)
    fprintf(wpatch,"%03d %04x %04x\n",i,
	   Ssecs[i].count,Ssecs[i].sndx);
  fprintf(wpatch,"%%\n%d %d %d %4.2lf\n",WInfo.ep,WInfo.mp,NODES,WInfo.ver);
  for (i = 0; i < NNodes; i++)
    fprintf(wpatch,"%03d \
%04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x\n",i,
	   (ushort)Nodes[i].x,(ushort)Nodes[i].y,
	   (ushort)Nodes[i].xdel,(ushort)Nodes[i].ydel,
	   (ushort)Nodes[i].rymax,(ushort)Nodes[i].rymin,
	   (ushort)Nodes[i].rxmin,(ushort)Nodes[i].rxmax,
	   (ushort)Nodes[i].lymax,(ushort)Nodes[i].lymin,
	   (ushort)Nodes[i].lxmin,(ushort)Nodes[i].lxmax,
	   (ushort)Nodes[i].nndx[0],(ushort)Nodes[i].nndx[1]);
  fprintf(wpatch,"%%\n%d %d %d %4.2lf\n",WInfo.ep,WInfo.mp,SECTS,WInfo.ver);
  for (i = 0; i < NSects; i++)
    fprintf(wpatch,"%03d %04x %04x %-8.8s %-8.8s %04x %04x %04x\n",i,
	   (ushort)Sects[i].floor_ht,(ushort)Sects[i].ceil_ht,
	   Sects[i].floor_desc,Sects[i].ceil_desc,
	   Sects[i].light_lvl,Sects[i].property,(ushort)Sects[i].line_tag);
  return TRUE;
}


/******************************************************************************
	ROUTINE:	wad_bswap(resource,type)
	WRITTEN BY:	Robert Fenske, Jr.
	CREATED:	May  1994
	DESCRIPTION:	This routine swaps the bytes in the short integer
			fields of the various resources.  This is necessary
			since the data files store integers in Intel little-
			endian order, while all real systems use big-endian
			order.
******************************************************************************/
void wad_bswap(resource,type)
int resource, type;
{
#if defined(BSWAP)				/* non-Intel only */
  int cnt = WInfo.dir[resource].nbytes/datasize[type];/* # items */
  short *data = (short *)WInfo.data[resource];	/* where data is */
  register int d, w;

  if (data != NULL)
    switch (type) {
      bcase THINGS:
       case LINES:
       case VERTS:
       case SEGS:
       case SSECTS:
       case NODES:
        for (d = 0; d < cnt; d++)
          for (w = 0; w < datasize[type]/2; w++)
            data[d*datasize[type]/2+w] = bswapw(data[d*datasize[type]/2+w]);
      bcase SIDES:				/* only swap integer fields */
        for (d = 0; d < cnt; d++) {
          Sides[d].image_xoff = bswapw(Sides[d].image_xoff);
          Sides[d].image_yoff = bswapw(Sides[d].image_yoff);
          Sides[d].sectndx = bswapw(Sides[d].sectndx);
        }
      bcase SECTS:				/* only swap integer fields */
        for (d = 0; d < cnt; d++) {
          Sects[d].floor_ht = bswapw(Sects[d].floor_ht);
          Sects[d].ceil_ht = bswapw(Sects[d].ceil_ht);
          Sects[d].light_lvl = bswapw(Sects[d].light_lvl);
          Sects[d].property = bswapw(Sects[d].property);
          Sects[d].line_tag = bswapw(Sects[d].line_tag);
        }
    }
#endif
}


/******************************************************************************
	ROUTINE:	wad_read(wad,epdo,mpdo,resources_needed)
	WRITTEN BY:	Robert Fenske, Jr.
	CREATED:	May  1994
	DESCRIPTION:	This routine reads a WAD file.  It reads the next
			unprocessed level if epdo and mpdo are zero, or
			reads the level referenced by epdo and mpdo.
			resources_needed governs which resources from the
			level are actually read.  The WAD file header is read,
			then the resource directory, then the requested
			resources.  The resource directory is held in two
			locations--one for modification and one to preserve
			the original directory.
******************************************************************************/
wad_read(wad,epdo,mpdo,resources_needed)
register FILE *wad;
int epdo, mpdo, resources_needed;
{
  register int e, i;

  rewind(wad);
  fread((char *)&WInfo.head,sizeof WInfo.head,1,wad);
  WInfo.head.count = bswapl(WInfo.head.count);
  WInfo.head.offset = bswapl(WInfo.head.offset);
  WInfo.origdir = blockmem(DIR_ENTRY,WInfo.head.count);
  WInfo.dir = blockmem(DIR_ENTRY,WInfo.head.count);
  WInfo.data = blockmem(char *,WInfo.head.count);
  WInfo.changed = blockmem(boolean,WInfo.head.count);
  WInfo.count = blockmem(int,WInfo.head.count);
  fseek(wad,WInfo.head.offset,0L);		/* read directory */
  fread((char *)WInfo.origdir,(unsigned)WInfo.head.count,
        sizeof(*WInfo.origdir),wad);
  for (e = 0; e < WInfo.head.count; e++) {
    WInfo.dir[e] = WInfo.origdir[e];
    WInfo.dir[e].offset =
    WInfo.origdir[e].offset = bswapl(WInfo.origdir[e].offset);
    WInfo.dir[e].nbytes =
    WInfo.origdir[e].nbytes = bswapl(WInfo.origdir[e].nbytes);
    
  }
  for (e = 0; e < WInfo.lvlndx; e++) blockfree(WInfo.data[e]);
  for (e = WInfo.lvlndx+ALL; e < WInfo.head.count; e++)
    if (2 == sscanf(WInfo.dir[e].name,"E%1dM%1d",&WInfo.ep,&WInfo.mp) &&
        (epdo == 0 && mpdo == 0 || epdo == WInfo.ep && mpdo == WInfo.mp))
      break;					/* found level */
  if (e >= WInfo.head.count)			/* no more levels to process */
    return FALSE;
  WInfo.lvlndx = e;                              /* this map's info is here */
  for (i = 0; i < ALL; i++) {
    if (resources_needed & (1<<i)) {		/* get requested resources */
      WInfo.data[e+i] = blockmem(char,WInfo.dir[e+i].nbytes);
      fseek(wad,WInfo.dir[e+i].offset,0L);
      fread(WInfo.data[e+i],(unsigned)WInfo.dir[e+i].nbytes,1,wad);
      wad_bswap(e+i,i);
    }else
      WInfo.data[e+i] = NULL;			/* no data for resource */
    WInfo.count[e+i] = WInfo.dir[e+i].nbytes/datasize[i];
  }
  return TRUE;
}


/******************************************************************************
	ROUTINE:	wad_write(rwad,wwad,rewrite)
	WRITTEN BY:	Robert Fenske, Jr.
	CREATED:	May  1994
	DESCRIPTION:	This routine writes a WAD file.  If rewrite is TRUE,
			then the input file is being rewritten with the new
			data; otherwise, a new file is written.  The resources
			are written out in reverse order; this easily handles
			the case where the input file is being rewritten and
			some of the resources have grown in size.  Also in
			the rewrite case, if a resource is smaller than before,
			it is marked with the new smaller size but the
			following resources are not "shifted down".  Thus there
			will be some parts of the file that will be unused.
******************************************************************************/
wad_write(rwad,wwad,rewrite)
register FILE *rwad, *wwad;
boolean rewrite;
{
  long dir_offset;				/* new directory offset */
  char *buf;					/* temporary data buffer */
  register int e;

  for (e = 0; e < ALL; e++) wad_bswap(WInfo.lvlndx+e,e);
  for (e = 1; e < WInfo.head.count; e++) {	/* compute new directory */
    if (!rewrite || WInfo.origdir[e-1].offset == 0)
      WInfo.dir[e].offset = WInfo.dir[e-1].offset + WInfo.dir[e-1].nbytes;
    else
      WInfo.dir[e].offset = WInfo.dir[e-1].offset +
                            max(WInfo.dir[e-1].nbytes,
                                WInfo.origdir[e].offset-
                                WInfo.origdir[e-1].offset);
  }
  for (e = WInfo.head.count-1; e >= WInfo.lvlndx; e--) {/* write resources */
    if (WInfo.changed[e]) {
      fseek(wwad,WInfo.dir[e].offset,0L);
      fwrite(WInfo.data[e],(unsigned)WInfo.dir[e].nbytes,1,wwad);
      WInfo.changed[e] = FALSE;
    }else if (!rewrite || WInfo.origdir[e].offset != WInfo.dir[e].offset) {
      buf = blockmem(char,WInfo.origdir[e].nbytes);
      fseek(rwad,WInfo.origdir[e].offset,0L);
      fread(buf,(unsigned)WInfo.origdir[e].nbytes,sizeof(*buf),rwad);
      fseek(wwad,WInfo.dir[e].offset,0L);
      fwrite(buf,(unsigned)WInfo.origdir[e].nbytes,sizeof(*buf),wwad);
      blockfree(buf);
    }
  }
  blockfree(WInfo.origdir);			/* done with original dir */
  dir_offset = WInfo.dir[WInfo.head.count-1].offset +
               WInfo.dir[WInfo.head.count-1].nbytes;
  for (e = 0; e < WInfo.head.count; e++) {
    WInfo.dir[e].offset = bswapl(WInfo.dir[e].offset);
    WInfo.dir[e].nbytes = bswapl(WInfo.dir[e].nbytes);
  }
  fseek(wwad,dir_offset,0L);			/* write new directory */
  fwrite(WInfo.dir,(unsigned)WInfo.head.count,sizeof(*WInfo.dir),wwad);
  WInfo.head.offset = bswapl(dir_offset);
  WInfo.head.count = bswapl(WInfo.head.count);
  fseek(wwad,0L,0L);
  fwrite(&WInfo.head,sizeof(WInfo.head),1,wwad);/* write new header */
  blockfree(WInfo.dir);				/* done with these */
  blockfree(WInfo.data);
  blockfree(WInfo.changed);
  blockfree(WInfo.count);
  return TRUE;
}
