/* 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" /* ---------------------------------------- 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 IGNORE_LUMP 0 #define THING_LUMP 1 #define VERTEX_LUMP 2 #define LINEDEF_LUMP 3 #define SIDEDEF_LUMP 4 #define SECTOR_LUMP 5 #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", __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 Word GetShort(FILE *fp) { return ((Short)fgetc(fp)|((Short)fgetc(fp))<<8); } static Long GetLong(FILE *fp) { return ((Long)fgetc(fp)| ((Long)fgetc(fp))<<8| ((Long)fgetc(fp))<<16| ((Long)fgetc(fp))<<24); } static char *GetName(FILE *fp) { static DirName d; fread(d,1,8,fp); d[sizeof(d)-1]=0; return(d); } static void PutShort(FILE *fp,Short s) { fputc(s&0xff,fp); fputc((s>>8)&0xff,fp); } static void PutLong(FILE *fp,Long l) { fputc(l&0xff,fp); fputc((l>>8)&0xff,fp); fputc((l>>16)&0xff,fp); fputc((l>>24)&0xff,fp); } 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 IsMapLump(char *n) { return(STREQ("THINGS",n)|| STREQ("LINEDEFS",n)|| STREQ("SIDEDEFS",n)|| STREQ("VERTEXES",n)|| STREQ("SEGS",n)|| STREQ("SSECTORS",n)|| STREQ("NODES",n)|| STREQ("SECTORS",n)|| STREQ("REJECT",n)|| STREQ("BLOCKMAP",n)); } 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}, {NULL,IGNORE_LUMP}, }; int f; f=0; while(lump[f].name) { if (STREQ(lump[f].name,n)) return(lump[f].lump); f++; } return(IGNORE_LUMP); } 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(ret,1,d->size,wf->fp); 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(type,1,4,fp); if (STRNEQ("IWAD",expect)&&(!STRNEQ("IWAD",type))) ERR(WAD_NOT_IWAD,NULL); else if (STRNEQ("PWAD",expect)&&(!STRNEQ("PWAD",type))) ERR(WAD_NOT_PWAD,NULL); 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)); } 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 { wf=Grab(sizeof(WadFile)); if (!(wf->fp=fopen(wad,"rb"))) GFX_exit(EXIT_FAILURE,"BIZARRE: WAD opening failed after " "first open succeeded on\nt%s\n",wad); strcpy(wf->path,wad); wadname=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); } /* ---------------------------------------- EXPORTED FUNCTIONS */ int AddIWAD(char *wad) { return (AddWAD(wad,"IWAD")); } int AddPWAD(char *wad) { return (AddWAD(wad,"PWAD")); } int CloseWad(char *wad) { WadDir *w; Iterator i; if (!(i=ListFindElem(wadlist,FindWAD,wad))) ERR(WAD_FILE_NOT_FOUND,WAD_FILE_NOT_FOUND); i=IteratorDelete(i); IteratorClear(i); i=ListIterator(waddir); while(i) { w=IteratorData(i); if (strcmp(w->wad,wad)==0) i=IteratorDelete(i); else i=IteratorNext(i); } 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); } WadMap *LoadMap(char *name) { Iterator i; WadMap *map; WadDir *dir; WadFile *wf; Thing thing; Vertex vertex; Sidedef sidedef; Linedef linedef; Sector sector; int f; 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); 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)); /* Step through the items following the map in the dir to load the lumps for the level in. */ while(TRUE) { i=IteratorNext(i); dir=IteratorData(i); if (!IsMapLump(dir->name)) ERR(WAD_OK,map); switch (HandledMapLump(dir->name)) { case THING_LUMP: fseek(wf->fp,(long)dir->off,SEEK_SET); 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.opt=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); 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; default: break; } } ERR(WAD_OK,map); } WadMap *NewMap(void) { WadMap *map; 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)); return(map); } WadMap *ClearMap(WadMap *map) { MapClear(map->thing); MapClear(map->vertex); MapClear(map->sidedef); MapClear(map->linedef); MapClear(map->sector); Release(map); return(NULL); } int SaveMap(WadMap *map, char *name, char *wad) { FILE *fp; Long off; int f; if (!(fp=fopen(wad,"wb"))) ERR(WAD_COULD_NOT_CREATE,WAD_COULD_NOT_CREATE); off=MapSize(map->vertex)*VERTEX_SIZE+ MapSize(map->sidedef)*SIDEDEF_SIZE+ MapSize(map->linedef)*LINEDEF_SIZE+ MapSize(map->sector)*SECTOR_SIZE+ MapSize(map->thing)*THING_SIZE+12; /* Put PWAD header */ fputs("PWAD",fp); PutLong(fp,10); PutLong(fp,off); /* Put THINGS */ for(f=0;fthing);f++) { Thing *t; t=MapElem(map->thing,f); PutShort(fp,t->x); PutShort(fp,t->y); PutShort(fp,t->ang); PutShort(fp,t->type); PutShort(fp,t->opt); } /* Put LINEDEFS */ for(f=0;flinedef);f++) { Linedef *l; l=MapElem(map->linedef,f); 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 */ /* Create directory */ off=12; PutDirEnt(fp,off,0,name); PutDirEnt(fp,off,MapSize(map->thing)*THING_SIZE,"THINGS"); off+=MapSize(map->thing)*THING_SIZE; 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"); fclose(fp); ERR(WAD_OK,WAD_OK); } int WadError(void) { return(wad_err); } const char *WadErrorString(void) { return(wad_errstr[wad_err]); } /* END OF FILE */