home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
HomeWare 14
/
HOMEWARE14.bin
/
games
/
doom
/
vnb1050.arj
/
VNB.C
< prev
next >
Wrap
C/C++ Source or Header
|
1994-05-25
|
21KB
|
514 lines
/******************************************************************************
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;
}