/* viDOOM - level editor for DOOM Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ------------------------------------------------------------------------- WAD file definitions and readers */ static const char rcs_id[]="$Id$"; #include "config.h" #include "globals.h" #include #include #include "wad.h" #include "gfx.h" #include "mem.h" #include "file.h" #include "util.h" /* ---------------------------------------- TYPES */ typedef struct WadEnt { DirName name; Long off; Long size; } WadEnt; typedef struct WadDirTable { int no; WadEnt *ent; } WadDirTable; typedef struct WadFile { FILE *fp; char path[PATH_MAX+1]; } WadFile; /* ---------------------------------------- VARS */ #define NO_MAP_LUMPS 7 #define NOT_MAP_LUMP -2 #define IGNORE_LUMP -1 #define THING_LUMP 0 #define VERTEX_LUMP 1 #define LINEDEF_LUMP 2 #define SIDEDEF_LUMP 3 #define SECTOR_LUMP 4 #define BEHAVIOR_LUMP 5 #define SCRIPTS_LUMP 6 #define ERR(e,r) do {wad_err=e;return(r);} while(0) static const char *wad_errstr[WAD_NOERROR]= { "no error", "File not found", "File not an IWAD or PWAD", "File not an IWAD", "File not a PWAD", "Map not found", "Lump not found", "Could not create file", "Could not create MAPINFO.WAD", "MAP mangled - spread over more than one file", __FILE__ " - ??? BROKEN ???", }; static int wad_err=WAD_OK; static List waddir=NULL; static List wadlist=NULL; /* ---------------------------------------- MACROS */ #define ERR(e,r) do {wad_err=e;return(r);} while(0) /* ---------------------------------------- PREDICATE FUNCTIONS */ static int FindDirEnt(void *a,void *b) { WadDir *aa; aa=(WadDir *)a; return(STREQ(aa->name,b)); } static int FindWAD(void *a,void *b) { WadFile *aa; aa=a; return(FilenamesEqual(aa->path,b)); } /* ---------------------------------------- PRIVATE FUNCTIONS */ static char *GetName(FILE *fp) { static DirName d; FRead(fp,d,8); d[sizeof(d)-1]=0; return(d); } static void PutName(FILE *fp,char *p) { int f; f=0; while(f<8) { fputc(*p,fp); if (*p) p++; f++; } } static void PutDirEnt(FILE *fp,Long off,Long size,char *p) { PutLong(fp,off); PutLong(fp,size); PutName(fp,p); } static int HandledMapLump(char *n) { static struct { char *name; int lump; } lump[]= { {"THINGS",THING_LUMP}, {"VERTEXES",VERTEX_LUMP}, {"SIDEDEFS",SIDEDEF_LUMP}, {"LINEDEFS",LINEDEF_LUMP}, {"SECTORS",SECTOR_LUMP}, {"BEHAVIOR",BEHAVIOR_LUMP}, {"SCRIPTS",SCRIPTS_LUMP}, {"SEGS",IGNORE_LUMP}, {"SSECTORS",IGNORE_LUMP}, {"NODES",IGNORE_LUMP}, {"REJECT",IGNORE_LUMP}, {"BLOCKMAP",IGNORE_LUMP}, {NULL,NOT_MAP_LUMP}, }; int f; f=0; while(lump[f].name) { if (STREQ(lump[f].name,n)) return(lump[f].lump); f++; } return(NOT_MAP_LUMP); } static void GetMapLumps(Iterator i, WadDir *l[]) { WadDir *dir; int lc; int f; for(f=0;fname); switch(lc) { case IGNORE_LUMP: break; case NOT_MAP_LUMP: i=IteratorClear(i); break; default: l[lc]=dir; break; } if (i) i=IteratorNext(i); } } static WadDir *GetDir(char *name) { Iterator i; WadDir *w; if (!(i=ListFindElem(waddir,FindDirEnt,name))) return(NULL); w=IteratorData(i); IteratorClear(i); return(w); } static WadFile *GetWADFile(char *name) { Iterator i; WadFile *w; if (!(i=ListFindElem(wadlist,FindWAD,name))) return(NULL); w=IteratorData(i); IteratorClear(i); return(w); } static void *LoadDirEnt(WadDir *d,Long *size) { void *ret; WadFile *wf; if (!(wf=GetWADFile(d->wad))) return(NULL); ret=Grab(d->size); fseek(wf->fp,(long)d->off,SEEK_SET); FRead(wf->fp,ret,d->size); if (size) *size=d->size; return(ret); } static WadDirTable *ReadDir(char *wad, char *expect) { char type[4]; WadDirTable *d; int f; FILE *fp; if (!(fp=fopen(wad,"rb"))) ERR(WAD_FILE_NOT_FOUND,NULL); FRead(fp,type,4); if (STRNEQ("IWAD",expect)&&(!STRNEQ("IWAD",type))) { fclose(fp); ERR(WAD_NOT_IWAD,NULL); } else if (STRNEQ("PWAD",expect)&&(!STRNEQ("PWAD",type))) { ERR(WAD_NOT_PWAD,NULL); fclose(fp); } d=Grab(sizeof(WadDirTable)); d->no=GetLong(fp); d->ent=Grab(sizeof(WadEnt)*d->no); fseek(fp,(long)GetLong(fp),SEEK_SET); for(f=0;fno;f++) { d->ent[f].off=GetLong(fp); d->ent[f].size=GetLong(fp); strcpy(d->ent[f].name,GetName(fp)); } fclose(fp); return(d); } static int AddWAD(char *wad,char *type) { Iterator i; WadDirTable *d; WadDir w; int f; char *wadname; WadFile wf; int check; int e2m1; int map01; /* Read in the directory from the WAD */ if (!waddir) { waddir=ListNew(sizeof(WadDir)); wadlist=ListNew(sizeof(WadFile)); } if (!(d=ReadDir(wad,type))) return(wad_err); /* Find the WAD name in the WAD list */ if ((i=ListFindElem(wadlist,FindWAD,wad))) { wadname=((WadFile *)IteratorData(i))->path; IteratorClear(i); } else { if (!(wf.fp=fopen(wad,"rb"))) GFX_exit(EXIT_FAILURE,"BIZARRE: WAD opening failed after " "first open succeeded on\n\t%s\n",wad); strcpy(wf.path,wad); wadname=Strdup(wf.path); ListInsert(wadlist,&wf); } /* Add the directory entries to the global dir */ e2m1=FALSE; map01=FALSE; check=STRNEQ("IWAD",type); for(f=d->no-1;f>=0;f--) { w.wad=wadname; w.off=d->ent[f].off; w.size=d->ent[f].size; strcpy(w.name,d->ent[f].name); if (check) { if (STREQ("E2M1",d->ent[f].name)) e2m1=TRUE; if (STREQ("MAP01",d->ent[f].name)) map01=TRUE; } ListInsert(waddir,&w); } /* Shareware checks fo IWAD files */ if (check) switch(level_style) { case DOOM_LEVELS: case ULTIMATE_DOOM_LEVELS: if (e2m1) break; case DOOM_2_LEVELS: if (map01) break; default: GFX_exit(EXIT_FAILURE, "You MUST have an IWAD from the registered " "version of\nDOOM, ULTIMATE DOOM, DOOM II " "or FINAL DOOM\n"); break; } Release(d->ent); Release(d); ERR(WAD_OK,WAD_OK); } /* ---------------------------------------- MAPINFO FUNCTIONS */ static char *LoadMAPINFO(void) { char path[PATH_MAX+1]; char *lump; Long size; strcpy(path,PWAD_dir); strcat(path,"mapinfo.wad"); if (AddPWAD(path)!=WAD_OK) return(NULL); lump=GetLump("MAPINFO",&size); CloseWad(path); if (lump) { lump=ReGrab(lump,size+1); lump[size]=0; } return(lump); } int SaveMAPINFO(char *info) { char path[PATH_MAX+1]; FILE *fp; Long off; strcpy(path,PWAD_dir); strcat(path,"mapinfo.wad"); if (!(fp=fopen(path,"wb"))) return(FALSE); off=strlen(info)+12; /* Put PWAD header */ fputs("PWAD",fp); PutLong(fp,1); PutLong(fp,off); /* Put MAPINFO */ fputs(info,fp); /* Create directory */ PutDirEnt(fp,12,strlen(info),"MAPINFO"); fclose(fp); return(TRUE); } /* ---------------------------------------- EXPORTED FUNCTIONS */ int AddIWAD(char *wad) { return (AddWAD(wad,"IWAD")); } int AddPWAD(char *wad) { return (AddWAD(wad,"PWAD")); } int CloseWad(char *wad) { WadDir *w; WadFile *wf; Iterator i; char *p=NULL; if (!(i=ListFindElem(wadlist,FindWAD,wad))) ERR(WAD_FILE_NOT_FOUND,WAD_FILE_NOT_FOUND); wf=IteratorData(i); if (wf->fp) fclose(wf->fp); i=IteratorDelete(i); IteratorClear(i); i=ListIterator(waddir); while(i) { w=IteratorData(i); if (FilenamesEqual(w->wad,wad)) { p=w->wad; i=IteratorDelete(i); } else i=IteratorNext(i); } if (p) Release(p); IteratorClear(i); ERR(WAD_OK,WAD_OK); } char *OpenWads(void) { Iterator i; WadFile *w; char *ret; if (!(i=ListIterator(wadlist))) ret=Strdup(""); else { w=IteratorData(i); ret=Grab(PATH_MAX+1); strcpy(ret,w->path); i=IteratorNext(i); while(i) { strcat(ret,"|"); w=IteratorData(i); ret=ReGrab(ret,strlen(ret)+PATH_MAX+1); strcat(ret,w->path); i=IteratorNext(i); } } return(ret); } Iterator GetWadDir(void) { return(ListIterator(waddir)); } int GetWadDirSize(void) { return(ListSize(waddir)); } void *GetLump(char *name, Long *size) { WadDir *d; void *ret; if (size) *size=0; if (!(d=GetDir(name))) ERR(WAD_LUMP_NOT_FOUND,NULL); if ((ret=LoadDirEnt(d,size))) ERR(WAD_OK,ret); else ERR(WAD_FILE_NOT_FOUND,NULL); } void *GetLumpFrom(WadDir *d, Long *size) { void *ret; if (size) *size=0; if ((ret=LoadDirEnt(d,size))) ERR(WAD_OK,ret); else ERR(WAD_FILE_NOT_FOUND,NULL); } WadMap *LoadMap(char *name) { Iterator i; WadMap *map; WadDir *dir; WadFile *wf; Thing thing; Vertex vertex; Sidedef sidedef; Linedef linedef; Sector sector; int f; int lc; WadDir *lump[NO_MAP_LUMPS]; if (!(i=ListFindElem(waddir,FindDirEnt,name))) ERR(WAD_MAP_NOT_FOUND,NULL); dir=IteratorData(i); if (!(wf=GetWADFile(dir->wad))) ERR(WAD_FILE_NOT_FOUND,NULL); GetMapLumps(i,lump); /* Check for a mangled map */ for(f=0;fpath,lump[f]->wad))) ERR(WAD_MAP_MANGLED,NULL); map=Grab(sizeof(WadMap)); map->thing=MapNew(sizeof(Thing)); map->vertex=MapNew(sizeof(Vertex)); map->sidedef=MapNew(sizeof(Sidedef)); map->linedef=MapNew(sizeof(Linedef)); map->sector=MapNew(sizeof(Sector)); map->scripts=NULL; map->behavior=NULL; map->mapinfo=NULL; if (lump[BEHAVIOR_LUMP]) map->hexen=TRUE; else map->hexen=FALSE; for(lc=0;lcfp,(long)dir->off,SEEK_SET); if (map->hexen) for(f=0;fsize/THING_SIZE_HEXEN;f++) { int a; thing.id=GetUShort(wf->fp); thing.x=GetShort(wf->fp); thing.y=GetShort(wf->fp); thing.z=GetShort(wf->fp); thing.ang=GetShort(wf->fp); thing.type=GetShort(wf->fp); thing.flags=GetShort(wf->fp); thing.special=GetByte(wf->fp); for(a=0;a<5;a++) thing.args[a]=GetByte(wf->fp); MapAdd(map->thing,f,&thing); } else for(f=0;fsize/THING_SIZE;f++) { thing.x=GetShort(wf->fp); thing.y=GetShort(wf->fp); thing.ang=GetShort(wf->fp); thing.type=GetShort(wf->fp); thing.flags=GetShort(wf->fp); MapAdd(map->thing,f,&thing); } break; case VERTEX_LUMP: fseek(wf->fp,(long)dir->off,SEEK_SET); for(f=0;fsize/VERTEX_SIZE;f++) { vertex.x=GetShort(wf->fp); vertex.y=GetShort(wf->fp); MapAdd(map->vertex,f,&vertex); } break; case SIDEDEF_LUMP: fseek(wf->fp,(long)dir->off,SEEK_SET); for(f=0;fsize/SIDEDEF_SIZE;f++) { sidedef.x=GetShort(wf->fp); sidedef.y=GetShort(wf->fp); strcpy(sidedef.upper,GetName(wf->fp)); strcpy(sidedef.lower,GetName(wf->fp)); strcpy(sidedef.middle,GetName(wf->fp)); sidedef.sector=GetShort(wf->fp); MapAdd(map->sidedef,f,&sidedef); } break; case LINEDEF_LUMP: fseek(wf->fp,(long)dir->off,SEEK_SET); if (map->hexen) for(f=0;fsize/LINEDEF_SIZE_HEXEN;f++) { int a; linedef.from=GetShort(wf->fp); linedef.to=GetShort(wf->fp); linedef.flags=GetShort(wf->fp); linedef.type=GetByte(wf->fp); for(a=0;a<5;a++) linedef.args[a]=GetByte(wf->fp); linedef.right=GetShort(wf->fp); linedef.left=GetShort(wf->fp); MapAdd(map->linedef,f,&linedef); } else for(f=0;fsize/LINEDEF_SIZE;f++) { linedef.from=GetShort(wf->fp); linedef.to=GetShort(wf->fp); linedef.flags=GetShort(wf->fp); linedef.type=GetShort(wf->fp); linedef.tag=GetShort(wf->fp); linedef.right=GetShort(wf->fp); linedef.left=GetShort(wf->fp); MapAdd(map->linedef,f,&linedef); } break; case SECTOR_LUMP: fseek(wf->fp,(long)dir->off,SEEK_SET); for(f=0;fsize/SECTOR_SIZE;f++) { sector.floor=GetShort(wf->fp); sector.ceiling=GetShort(wf->fp); strcpy(sector.floor_t,GetName(wf->fp)); strcpy(sector.ceiling_t,GetName(wf->fp)); sector.light=GetShort(wf->fp); sector.special=GetShort(wf->fp); sector.tag=GetShort(wf->fp); MapAdd(map->sector,f,§or); } break; /* No action yet for these - will simply be written back */ case BEHAVIOR_LUMP: map->behavior_size=dir->size; map->behavior=Grab(dir->size); fseek(wf->fp,(long)dir->off,SEEK_SET); FRead(wf->fp,map->behavior,dir->size); break; case SCRIPTS_LUMP: map->scripts_size=dir->size; map->scripts=Grab(dir->size+1); fseek(wf->fp,(long)dir->off,SEEK_SET); FRead(wf->fp,map->scripts,dir->size); map->scripts[map->scripts_size]=0; break; default: break; } } /* Locate the MAPINFO lump if config says so */ if (mapinfo_lump) map->mapinfo=UnMSDOS(LoadMAPINFO()); ERR(WAD_OK,map); } WadMap *NewMap(int hexen) { WadMap *map; map=Grab(sizeof(WadMap)); map->hexen=hexen; map->thing=MapNew(sizeof(Thing)); map->vertex=MapNew(sizeof(Vertex)); map->sidedef=MapNew(sizeof(Sidedef)); map->linedef=MapNew(sizeof(Linedef)); map->sector=MapNew(sizeof(Sector)); if (mapinfo_lump) map->mapinfo=UnMSDOS(LoadMAPINFO()); else map->mapinfo=NULL; map->behavior_size=0; map->behavior=NULL; map->scripts_size=0; map->scripts=NULL; return(map); } WadMap *ClearMap(WadMap *map) { MapClear(map->thing); MapClear(map->vertex); MapClear(map->sidedef); MapClear(map->linedef); MapClear(map->sector); if (map->mapinfo) Release(map->mapinfo); if (map->behavior) Release(map->behavior); if (map->scripts) Release(map->scripts); Release(map); return(NULL); } int SaveMap(WadMap *map, char *name, char *wad) { FILE *fp; Long off; int f; Long no; if (!(fp=fopen(wad,"wb"))) ERR(WAD_COULD_NOT_CREATE,WAD_COULD_NOT_CREATE); /* Calc number of entries in MAP */ if (map->hexen) no=13; else no=11; /* Calc offset for directory */ off=12; off+=MapSize(map->vertex)*VERTEX_SIZE+ MapSize(map->sidedef)*SIDEDEF_SIZE+ MapSize(map->sector)*SECTOR_SIZE; if (map->hexen) { off+=MapSize(map->linedef)*LINEDEF_SIZE_HEXEN+ MapSize(map->thing)*THING_SIZE_HEXEN; off+=map->behavior_size+map->scripts_size; } else { off+=MapSize(map->linedef)*LINEDEF_SIZE+ MapSize(map->thing)*THING_SIZE; } /* Save MAPINFO.WAD */ if ((map->mapinfo)&&(strlen(map->mapinfo))) { char *info; info=ApplyMSDOS(map->mapinfo,FALSE); if (!SaveMAPINFO(info)) { Release(info); ERR(WAD_MAPINFO_FAILED,WAD_MAPINFO_FAILED); } Release(info); } /* Put PWAD header */ fputs("PWAD",fp); PutLong(fp,no); PutLong(fp,off); /* Put THINGS */ for(f=0;fthing);f++) { Thing *t; t=MapElem(map->thing,f); if (map->hexen) { int i; PutUShort(fp,t->id); PutShort(fp,t->x); PutShort(fp,t->y); PutShort(fp,t->z); PutShort(fp,t->ang); PutShort(fp,t->type); PutShort(fp,t->flags); PutByte(fp,t->special); for(i=0;i<5;i++) PutByte(fp,t->args[i]); } else { PutShort(fp,t->x); PutShort(fp,t->y); PutShort(fp,t->ang); PutShort(fp,t->type); PutShort(fp,t->flags); } } /* Put LINEDEFS */ for(f=0;flinedef);f++) { Linedef *l; l=MapElem(map->linedef,f); if (map->hexen) { int i; PutShort(fp,l->from); PutShort(fp,l->to); PutShort(fp,l->flags); PutByte(fp,(Byte)l->type); for(i=0;i<5;i++) PutByte(fp,l->args[i]); PutShort(fp,l->right); PutShort(fp,l->left); } else { PutShort(fp,l->from); PutShort(fp,l->to); PutShort(fp,l->flags); PutShort(fp,l->type); PutShort(fp,l->tag); PutShort(fp,l->right); PutShort(fp,l->left); } } /* Put SIDEDEFS */ for(f=0;fsidedef);f++) { Sidedef *s; s=MapElem(map->sidedef,f); PutShort(fp,s->x); PutShort(fp,s->y); PutName(fp,s->upper); PutName(fp,s->lower); PutName(fp,s->middle); PutShort(fp,s->sector); } /* Put VERTEXES */ for(f=0;fvertex);f++) { Vertex *v; v=MapElem(map->vertex,f); PutShort(fp,v->x); PutShort(fp,v->y); } /* Skip SEGS, SSECTORS, NODE */ /* Put SECTORS */ for(f=0;fsector);f++) { Sector *s; s=MapElem(map->sector,f); PutShort(fp,s->floor); PutShort(fp,s->ceiling); PutName(fp,s->floor_t); PutName(fp,s->ceiling_t); PutShort(fp,s->light); PutShort(fp,s->special); PutShort(fp,s->tag); } /* Skip REJECT, BLOCKMAP */ /* Put BEHAVIOR and SCRIPTS */ if (map->hexen) { int f; if (map->behavior) for(f=0;fbehavior_size;f++) PutByte(fp,map->behavior[f]); if (map->scripts) for(f=0;fscripts_size;f++) PutByte(fp,map->scripts[f]); } /* Create directory */ off=12; PutDirEnt(fp,off,0,name); if (map->hexen) { PutDirEnt(fp,off,MapSize(map->thing)*THING_SIZE_HEXEN,"THINGS"); off+=MapSize(map->thing)*THING_SIZE_HEXEN; } else { PutDirEnt(fp,off,MapSize(map->thing)*THING_SIZE,"THINGS"); off+=MapSize(map->thing)*THING_SIZE; } if (map->hexen) { PutDirEnt(fp,off,MapSize(map->linedef)*LINEDEF_SIZE_HEXEN,"LINEDEFS"); off+=MapSize(map->linedef)*LINEDEF_SIZE_HEXEN; } else { PutDirEnt(fp,off,MapSize(map->linedef)*LINEDEF_SIZE,"LINEDEFS"); off+=MapSize(map->linedef)*LINEDEF_SIZE; } PutDirEnt(fp,off,MapSize(map->sidedef)*SIDEDEF_SIZE,"SIDEDEFS"); off+=MapSize(map->sidedef)*SIDEDEF_SIZE; PutDirEnt(fp,off,MapSize(map->vertex)*VERTEX_SIZE,"VERTEXES"); off+=MapSize(map->vertex)*VERTEX_SIZE; PutDirEnt(fp,off,0,"SEGS"); PutDirEnt(fp,off,0,"SSECTORS"); PutDirEnt(fp,off,0,"NODES"); PutDirEnt(fp,off,MapSize(map->sector)*SECTOR_SIZE,"SECTORS"); off+=MapSize(map->sector)*SECTOR_SIZE; PutDirEnt(fp,off,0,"REJECT"); PutDirEnt(fp,off,0,"BLOCKMAP"); if (map->hexen) { PutDirEnt(fp,off,map->behavior_size,"BEHAVIOR"); off+=map->behavior_size; PutDirEnt(fp,off,map->scripts_size,"SCRIPTS"); off+=map->scripts_size; } fclose(fp); ERR(WAD_OK,WAD_OK); } int WadError(void) { return(wad_err); } const char *WadErrorString(void) { return(wad_errstr[wad_err]); } int WadFileType(char *wad) { FILE *fp; char type[4]; if (!(fp=fopen(wad,"rb"))) return(FILE_IS_NOT_WAD); FRead(fp,type,4); fclose(fp); if (STRNEQ("PWAD",type)) return(FILE_IS_PWAD); if (STRNEQ("IWAD",type)) return(FILE_IS_IWAD); return(FILE_IS_NOT_WAD); } /* END OF FILE */