//------------------------------------------------------------------------ // WAD : WAD read/write functions. //------------------------------------------------------------------------ // // GL-Friendly Node Builder (C) 2000-2005 Andrew Apted // // Based on 'BSP 2.3' by Colin Reed, Lee Killough and others. // // 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. // //------------------------------------------------------------------------ #include "system.h" #include #include #include #include #include #include #include #include #include #include "blockmap.h" #include "level.h" #include "node.h" #include "seg.h" #include "structs.h" #include "util.h" #include "wad.h" static FILE *in_file = NULL; static FILE *out_file = NULL; #define DEBUG_DIR 0 #define DEBUG_LUMP 0 #define DEBUG_KEYS 0 #define APPEND_BLKSIZE 256 #define LEVNAME_BUNCH 20 #define ALIGN_LEN(len) ((((len) + 3) / 4) * 4) // current wad info static wad_t wad; /* ---------------------------------------------------------------- */ #define NUM_LEVEL_LUMPS 12 #define NUM_GL_LUMPS 5 static const char *level_lumps[NUM_LEVEL_LUMPS]= { "THINGS", "LINEDEFS", "SIDEDEFS", "VERTEXES", "SEGS", "SSECTORS", "NODES", "SECTORS", "REJECT", "BLOCKMAP", "BEHAVIOR", // <-- hexen support "SCRIPTS" // -JL- Lump with script sources }; static const char *gl_lumps[NUM_GL_LUMPS]= { "GL_VERT", "GL_SEGS", "GL_SSECT", "GL_NODES", "GL_PVS" // -JL- PVS (Potentially Visible Set) lump }; static const char align_filler[4] = { 0, 0, 0, 0 }; // // CheckMagic // static int CheckMagic(const char type[4]) { if ((type[0] == 'I' || type[0] == 'P') && type[1] == 'W' && type[2] == 'A' && type[3] == 'D') { return TRUE; } return FALSE; } // // CheckLevelName // static int CheckLevelName(const char *name) { int n; for (n=0; n < wad.num_level_names; n++) { if (strcmp(wad.level_names[n], name) == 0) return TRUE; } return FALSE; } // // CheckLevelLumpName // // Tests if the entry name is one of the level lumps. // static int CheckLevelLumpName(const char *name) { int i; for (i=0; i < NUM_LEVEL_LUMPS; i++) { if (strcmp(name, level_lumps[i]) == 0) return TRUE; } return FALSE; } // // CheckGLLumpName // // Tests if the entry name matches GL_ExMy or GL_MAPxx, or one of the // GL lump names. // static int CheckGLLumpName(const char *name) { int i; if (name[0] != 'G' || name[1] != 'L' || name[2] != '_') return FALSE; for (i=0; i < NUM_GL_LUMPS; i++) { if (strcmp(name, gl_lumps[i]) == 0) return TRUE; } return CheckLevelName(name+3); } // // Level name helper // static INLINE_G void AddLevelName(const char *name) { if ((wad.num_level_names % LEVNAME_BUNCH) == 0) { wad.level_names = (const char **) UtilRealloc((void *)wad.level_names, (wad.num_level_names + LEVNAME_BUNCH) * sizeof(const char *)); } wad.level_names[wad.num_level_names] = UtilStrDup(name); wad.num_level_names++; } // // NewLevel // // Create new level information // static level_t *NewLevel(int flags) { level_t *cur; cur = UtilCalloc(sizeof(level_t)); cur->flags = flags; return cur; } // // NewLump // // Create new lump. 'name' must be allocated storage. // static lump_t *NewLump(char *name) { lump_t *cur; cur = UtilCalloc(sizeof(lump_t)); cur->name = name; cur->start = cur->new_start = -1; cur->flags = LUMP_NEW; cur->length = 0; cur->space = 0; cur->data = NULL; cur->lev_info = NULL; return cur; } static void FreeLump(lump_t *lump); // // FreeWadLevel // static void FreeWadLevel(level_t *level) { while (level->children) { lump_t *head = level->children; level->children = head->next; // the ol' recursion trick... :) FreeLump(head); } UtilFree(level); } // // FreeLump // static void FreeLump(lump_t *lump) { // free level lumps, if any if (lump->lev_info) { FreeWadLevel(lump->lev_info); } // check 'data' here, since it gets freed in WriteLumpData() if (lump->data) UtilFree(lump->data); UtilFree(lump->name); UtilFree(lump); } // // ReadHeader // // Returns TRUE if successful, or FALSE if there was a problem (in // which case the error message as been setup). // static int ReadHeader(const char *filename) { size_t len; raw_wad_header_t header; char strbuf[1024]; len = fread(&header, sizeof(header), 1, in_file); if (len != 1) { sprintf(strbuf, "Trouble reading wad header for %s [%s]", filename, strerror(errno)); SetErrorMsg(strbuf); return FALSE; } if (! CheckMagic(header.type)) { sprintf(strbuf, "%s does not appear to be a wad file (bad magic)", filename); SetErrorMsg(strbuf); return FALSE; } wad.kind = (header.type[0] == 'I') ? IWAD : PWAD; wad.num_entries = UINT32(header.num_entries); wad.dir_start = UINT32(header.dir_start); // initialise stuff wad.dir_head = NULL; wad.dir_tail = NULL; wad.current_level = NULL; wad.level_names = NULL; wad.num_level_names = 0; return TRUE; } // // ReadDirEntry // static void ReadDirEntry(void) { size_t len; raw_wad_entry_t entry; lump_t *lump; DisplayTicker(); len = fread(&entry, sizeof(entry), 1, in_file); if (len != 1) FatalError("Trouble reading wad directory"); lump = NewLump(UtilStrNDup(entry.name, 8)); lump->start = UINT32(entry.start); lump->length = UINT32(entry.length); # if DEBUG_DIR PrintDebug("Read dir... %s\n", lump->name); # endif // link it in lump->next = NULL; lump->prev = wad.dir_tail; if (wad.dir_tail) wad.dir_tail->next = lump; else wad.dir_head = lump; wad.dir_tail = lump; } // // DetermineLevelNames // static void DetermineLevelNames(void) { lump_t *L, *N; int i; for (L=wad.dir_head; L; L=L->next) { // check if the next four lumps after the current lump match the // level-lump names. // for (i=0, N=L->next; (i < 4) && N; i++, N=N->next) if (strcmp(N->name, level_lumps[i]) != 0) break; if (i != 4) continue; # if DEBUG_DIR PrintDebug("Found level name: %s\n", L->name); # endif // check for duplicate levels (ignored) if (CheckLevelName(L->name)) { PrintWarn("Level name '%s' found twice in wad\n", L->name); continue; } // check for long names if (strlen(L->name) > 5) PrintWarn("Long level name '%s' found in wad\n", L->name); AddLevelName(L->name); } } // // ProcessDirEntry // static void ProcessDirEntry(lump_t *lump) { DisplayTicker(); // ignore previous GL lump info if (CheckGLLumpName(lump->name)) { # if DEBUG_DIR PrintDebug("Discarding previous GL info: %s\n", lump->name); # endif FreeLump(lump); wad.num_entries--; return; } // mark the lump as 'ignorable' when in GWA mode. if (cur_info->gwa_mode) lump->flags |= LUMP_IGNORE_ME; // --- LEVEL MARKERS --- if (CheckLevelName(lump->name)) { /* NOTE ! Level marks can have data (in Hexen anyway) */ if (cur_info->load_all) lump->flags |= LUMP_READ_ME; else lump->flags |= LUMP_COPY_ME; // OK, start a new level lump->lev_info = NewLevel(0); wad.current_level = lump; # if DEBUG_DIR PrintDebug("Process dir... %s :\n", lump->name); # endif // link it in lump->next = NULL; lump->prev = wad.dir_tail; if (wad.dir_tail) wad.dir_tail->next = lump; else wad.dir_head = lump; wad.dir_tail = lump; return; } // --- LEVEL LUMPS --- if (wad.current_level) { if (CheckLevelLumpName(lump->name)) { // check for duplicates if (FindLevelLump(lump->name)) { PrintWarn("Duplicate entry '%s' ignored in %s\n", lump->name, wad.current_level->name); FreeLump(lump); wad.num_entries--; return; } # if DEBUG_DIR PrintDebug("Process dir... |--- %s\n", lump->name); # endif // mark it to be loaded lump->flags |= LUMP_READ_ME; // link it in lump->next = wad.current_level->lev_info->children; lump->prev = NULL; if (lump->next) lump->next->prev = lump; wad.current_level->lev_info->children = lump; return; } // OK, non-level lump. End the previous level. wad.current_level = NULL; } // --- ORDINARY LUMPS --- # if DEBUG_DIR PrintDebug("Process dir... %s\n", lump->name); # endif if (CheckLevelLumpName(lump->name)) PrintWarn("Level lump '%s' found outside any level\n", lump->name); // maybe load data if (cur_info->load_all) lump->flags |= LUMP_READ_ME; else lump->flags |= LUMP_COPY_ME; // link it in lump->next = NULL; lump->prev = wad.dir_tail; if (wad.dir_tail) wad.dir_tail->next = lump; else wad.dir_head = lump; wad.dir_tail = lump; } // // ReadDirectory // static void ReadDirectory(void) { int i; int total_entries = wad.num_entries; lump_t *prev_list; fseek(in_file, wad.dir_start, SEEK_SET); for (i=0; i < total_entries; i++) { ReadDirEntry(); } DetermineLevelNames(); // finally, unlink all lumps and process each one in turn prev_list = wad.dir_head; wad.dir_head = wad.dir_tail = NULL; while (prev_list) { lump_t *cur = prev_list; prev_list = cur->next; ProcessDirEntry(cur); } } // // ReadLumpData // static void ReadLumpData(lump_t *lump) { size_t len; cur_comms->file_pos++; DisplaySetBar(1, cur_comms->file_pos); DisplayTicker(); # if DEBUG_LUMP PrintDebug("Reading... %s (%d)\n", lump->name, lump->length); # endif if (lump->length == 0) return; lump->data = UtilCalloc(lump->length); fseek(in_file, lump->start, SEEK_SET); len = fread(lump->data, lump->length, 1, in_file); if (len != 1) { if (wad.current_level) PrintWarn("Trouble reading lump '%s' in %s\n", lump->name, wad.current_level->name); else PrintWarn("Trouble reading lump '%s'\n", lump->name); } lump->flags &= ~LUMP_READ_ME; } // // ReadAllLumps // // Returns number of entries read. // static int ReadAllLumps(void) { lump_t *cur, *L; int count = 0; for (cur=wad.dir_head; cur; cur=cur->next) { count++; if (cur->flags & LUMP_READ_ME) ReadLumpData(cur); if (cur->lev_info && ! (cur->lev_info->flags & LEVEL_IS_GL)) { for (L=cur->lev_info->children; L; L=L->next) { count++; if (L->flags & LUMP_READ_ME) ReadLumpData(L); } } } return count; } // // CountLumpTypes // // Returns number of entries with matching flags. Used to predict how // many ReadLumpData() or WriteLumpData() calls will be made (for the // progress bars). // static int CountLumpTypes(int flag_mask, int flag_match) { lump_t *cur, *L; int count = 0; for (cur=wad.dir_head; cur; cur=cur->next) { if ((cur->flags & flag_mask) == flag_match) count++; if (cur->lev_info) { for (L=cur->lev_info->children; L; L=L->next) if ((L->flags & flag_mask) == flag_match) count++; } } return count; } /* ---------------------------------------------------------------- */ // // WriteHeader // static void WriteHeader(void) { size_t len; raw_wad_header_t header; switch (wad.kind) { case IWAD: strncpy(header.type, "IWAD", 4); break; case PWAD: strncpy(header.type, "PWAD", 4); break; } header.num_entries = UINT32(wad.num_entries); header.dir_start = UINT32(wad.dir_start); len = fwrite(&header, sizeof(header), 1, out_file); if (len != 1) PrintWarn("Trouble writing wad header\n"); } // // CreateGLMarker // lump_t *CreateGLMarker(void) { lump_t *level = wad.current_level; lump_t *cur; char name_buf[16]; boolean_g long_name = FALSE; if (strlen(level->name) <= 5) { sprintf(name_buf, "GL_%s", level->name); } else { // support for level names longer than 5 letters strcpy(name_buf, "GL_LEVEL"); long_name = TRUE; } cur = NewLump(UtilStrDup(name_buf)); cur->lev_info = NewLevel(LEVEL_IS_GL); // link it in cur->next = level->next; cur->prev = level; if (cur->next) cur->next->prev = cur; level->next = cur; level->lev_info->buddy = cur; if (long_name) { AddGLTextLine("LEVEL", level->name); } return cur; } // // SortLumps // // Algorithm is pretty simple: for each of the names, if such a lump // exists in the list, move it to the head of the list. By going // backwards through the names, we ensure the correct order. // static void SortLumps(lump_t ** list, const char **names, int count) { int i; lump_t *cur; for (i=count-1; i >= 0; i--) { for (cur=(*list); cur; cur=cur->next) { if (strcmp(cur->name, names[i]) != 0) continue; // unlink it if (cur->next) cur->next->prev = cur->prev; if (cur->prev) cur->prev->next = cur->next; else (*list) = cur->next; // add to head cur->next = (*list); cur->prev = NULL; if (cur->next) cur->next->prev = cur; (*list) = cur; // continue with next name (important !) break; } } } // // RecomputeDirectory // // Calculates all the lump offsets for the directory. // static void RecomputeDirectory(void) { lump_t *cur, *L; level_t *lev; wad.num_entries = 0; wad.dir_start = sizeof(raw_wad_header_t); // run through all the lumps, computing the 'new_start' fields, the // number of lumps in the directory, the directory starting pos, and // also sorting the lumps in the levels. for (cur=wad.dir_head; cur; cur=cur->next) { if (cur->flags & LUMP_IGNORE_ME) continue; cur->new_start = wad.dir_start; wad.dir_start += ALIGN_LEN(cur->length); wad.num_entries++; lev = cur->lev_info; if (lev) { if (lev->flags & LEVEL_IS_GL) SortLumps(&lev->children, gl_lumps, NUM_GL_LUMPS); else SortLumps(&lev->children, level_lumps, NUM_LEVEL_LUMPS); for (L=lev->children; L; L=L->next) { if (L->flags & LUMP_IGNORE_ME) continue; L->new_start = wad.dir_start; wad.dir_start += ALIGN_LEN(L->length); wad.num_entries++; } } } } // // WriteLumpData // static void WriteLumpData(lump_t *lump) { size_t len; int align_size; cur_comms->file_pos++; DisplaySetBar(1, cur_comms->file_pos); DisplayTicker(); # if DEBUG_LUMP if (lump->flags & LUMP_COPY_ME) PrintDebug("Copying... %s (%d)\n", lump->name, lump->length); else PrintDebug("Writing... %s (%d)\n", lump->name, lump->length); # endif if (ftell(out_file) != lump->new_start) PrintWarn("Consistency failure writing %s (%08X, %08X\n", lump->name, ftell(out_file), lump->new_start); if (lump->length == 0) return; if (lump->flags & LUMP_COPY_ME) { lump->data = UtilCalloc(lump->length); fseek(in_file, lump->start, SEEK_SET); len = fread(lump->data, lump->length, 1, in_file); if (len != 1) PrintWarn("Trouble reading lump %s to copy\n", lump->name); } len = fwrite(lump->data, lump->length, 1, out_file); if (len != 1) PrintWarn("Trouble writing lump %s\n", lump->name); align_size = ALIGN_LEN(lump->length) - lump->length; if (align_size > 0) fwrite(align_filler, align_size, 1, out_file); UtilFree(lump->data); lump->data = NULL; } // // WriteAllLumps // // Returns number of entries written. // static int WriteAllLumps(void) { lump_t *cur, *L; int count = 0; for (cur=wad.dir_head; cur; cur=cur->next) { if (cur->flags & LUMP_IGNORE_ME) continue; WriteLumpData(cur); count++; if (cur->lev_info) { for (L=cur->lev_info->children; L; L=L->next) { if (L->flags & LUMP_IGNORE_ME) continue; WriteLumpData(L); count++; } } } fflush(out_file); return count; } // // WriteDirEntry // static void WriteDirEntry(lump_t *lump) { size_t len; raw_wad_entry_t entry; DisplayTicker(); strncpy(entry.name, lump->name, 8); entry.start = UINT32(lump->new_start); entry.length = UINT32(lump->length); len = fwrite(&entry, sizeof(entry), 1, out_file); if (len != 1) PrintWarn("Trouble writing wad directory\n"); } // // WriteDirectory // // Returns number of entries written. // static int WriteDirectory(void) { lump_t *cur, *L; int count = 0; if (ftell(out_file) != wad.dir_start) PrintWarn("Consistency failure writing lump directory " "(%08X,%08X)\n", ftell(out_file), wad.dir_start); for (cur=wad.dir_head; cur; cur=cur->next) { if (cur->flags & LUMP_IGNORE_ME) continue; WriteDirEntry(cur); count++; # if DEBUG_DIR if (cur->lev_info) PrintDebug("Write dir... %s :\n", cur->name); else PrintDebug("Write dir... %s\n", cur->name); # endif if (cur->lev_info) { for (L=cur->lev_info->children; L; L=L->next) { if (cur->flags & LUMP_IGNORE_ME) continue; # if DEBUG_DIR PrintDebug("Write dir... |--- %s\n", L->name); # endif WriteDirEntry(L); count++; } } } fflush(out_file); return count; } /* ---------------------------------------------------------------- */ // // CheckExtension // int CheckExtension(const char *filename, const char *ext) { int A = (int)strlen(filename) - 1; int B = (int)strlen(ext) - 1; for (; B >= 0; B--, A--) { if (A < 0) return FALSE; if (toupper(filename[A]) != toupper(ext[B])) return FALSE; } return (A >= 1) && (filename[A] == '.'); } // // ReplaceExtension // char *ReplaceExtension(const char *filename, const char *ext) { char *dot_pos; char buffer[512]; strcpy(buffer, filename); dot_pos = strrchr(buffer, '.'); if (dot_pos) dot_pos[1] = 0; else strcat(buffer, "."); strcat(buffer, ext); return UtilStrDup(buffer); } // // CreateLevelLump // lump_t *CreateLevelLump(const char *name) { lump_t *cur; # if DEBUG_DIR PrintDebug("Create Lump... %s\n", name); # endif // already exists ? for (cur=wad.current_level->lev_info->children; cur; cur=cur->next) { if (strcmp(name, cur->name) == 0) break; } if (cur) { if (cur->data) UtilFree(cur->data); cur->data = NULL; cur->length = 0; cur->space = 0; return cur; } // nope, allocate new one cur = NewLump(UtilStrDup(name)); // link it in cur->next = wad.current_level->lev_info->children; cur->prev = NULL; if (cur->next) cur->next->prev = cur; wad.current_level->lev_info->children = cur; return cur; } // // CreateGLLump // lump_t *CreateGLLump(const char *name) { lump_t *cur; lump_t *gl_level; # if DEBUG_DIR PrintDebug("Create GL Lump... %s\n", name); # endif // create GL level marker if necessary if (! wad.current_level->lev_info->buddy) CreateGLMarker(); gl_level = wad.current_level->lev_info->buddy; // check if already exists for (cur=gl_level->lev_info->children; cur; cur=cur->next) { if (strcmp(name, cur->name) == 0) break; } if (cur) { if (cur->data) UtilFree(cur->data); cur->data = NULL; cur->length = 0; cur->space = 0; return cur; } // nope, allocate new one cur = NewLump(UtilStrDup(name)); // link it in cur->next = gl_level->lev_info->children; cur->prev = NULL; if (cur->next) cur->next->prev = cur; gl_level->lev_info->children = cur; return cur; } // // AppendLevelLump // void AppendLevelLump(lump_t *lump, const void *data, int length) { if (length == 0) return; if (lump->length == 0) { lump->space = MAX(length, APPEND_BLKSIZE); lump->data = UtilCalloc(lump->space); } else if (lump->space < length) { lump->space = MAX(length, APPEND_BLKSIZE); lump->data = UtilRealloc(lump->data, lump->length + lump->space); } memcpy(((char *)lump->data) + lump->length, data, length); lump->length += length; lump->space -= length; } // // AddGLTextLine // void AddGLTextLine(const char *keyword, const char *value) { lump_t *gl_level; // create GL level marker if necessary if (! wad.current_level->lev_info->buddy) CreateGLMarker(); gl_level = wad.current_level->lev_info->buddy; # if DEBUG_KEYS PrintDebug("[%s] Adding: %s=%s\n", gl_level->name, keyword, value); # endif AppendLevelLump(gl_level, keyword, strlen(keyword)); AppendLevelLump(gl_level, "=", 1); AppendLevelLump(gl_level, value, strlen(value)); AppendLevelLump(gl_level, "\n", 1); } // // CountLevels // int CountLevels(void) { lump_t *cur; int result = 0; for (cur=wad.dir_head; cur; cur=cur->next) { if (cur->lev_info && ! (cur->lev_info->flags & LEVEL_IS_GL)) result++; } return result; } // // FindNextLevel // int FindNextLevel(void) { lump_t *cur; if (wad.current_level) cur = wad.current_level->next; else cur = wad.dir_head; while (cur && ! (cur->lev_info && ! (cur->lev_info->flags & LEVEL_IS_GL))) cur=cur->next; wad.current_level = cur; return (cur != NULL); } // // GetLevelName // const char *GetLevelName(void) { if (!wad.current_level) InternalError("GetLevelName: no current level"); return wad.current_level->name; } // // FindLevelLump // lump_t *FindLevelLump(const char *name) { lump_t *cur = wad.current_level->lev_info->children; while (cur && (strcmp(cur->name, name) != 0)) cur=cur->next; return cur; } // // CheckLevelLumpZero // int CheckLevelLumpZero(lump_t *lump) { int i; if (lump->length == 0) return TRUE; // ASSERT(lump->data) for (i=0; i < lump->length; i++) { if (((uint8_g *)lump->data)[i]) return FALSE; } return TRUE; } // // ReadWadFile // glbsp_ret_e ReadWadFile(const char *filename) { int check; char strbuf[1024]; // open input wad file & read header in_file = fopen(filename, "rb"); if (! in_file) { if (errno == ENOENT) sprintf(strbuf, "Cannot open WAD file: %s", filename); else sprintf(strbuf, "Cannot open WAD file: %s [%s]", filename, strerror(errno)); SetErrorMsg(strbuf); return GLBSP_E_ReadError; } if (! ReadHeader(filename)) { fclose(in_file); return GLBSP_E_ReadError; } PrintMsg("Opened %cWAD file : %s\n", (wad.kind == IWAD) ? 'I' : 'P', filename); PrintVerbose("Reading %d dir entries at 0x%X\n", wad.num_entries, wad.dir_start); // read directory ReadDirectory(); DisplayOpen(DIS_FILEPROGRESS); DisplaySetTitle("glBSP Reading Wad"); sprintf(strbuf, "Reading: %s", filename); DisplaySetBarText(1, strbuf); DisplaySetBarLimit(1, CountLumpTypes(LUMP_READ_ME, LUMP_READ_ME)); DisplaySetBar(1, 0); cur_comms->file_pos = 0; // now read lumps check = ReadAllLumps(); if (check != wad.num_entries) InternalError("Read directory count consistency failure (%d,%d)", check, wad.num_entries); wad.current_level = NULL; DisplayClose(); return GLBSP_E_OK; } // // WriteWadFile // glbsp_ret_e WriteWadFile(const char *filename) { int check1, check2; char strbuf[1024]; PrintMsg("\n"); PrintMsg("Saving WAD as %s\n", filename); if (cur_info->gwa_mode) wad.kind = PWAD; RecomputeDirectory(); // create output wad file & write the header out_file = fopen(filename, "wb"); if (! out_file) { sprintf(strbuf, "Cannot create WAD file: %s [%s]", filename, strerror(errno)); SetErrorMsg(strbuf); return GLBSP_E_WriteError; } WriteHeader(); DisplayOpen(DIS_FILEPROGRESS); DisplaySetTitle("glBSP Writing Wad"); sprintf(strbuf, "Writing: %s", filename); DisplaySetBarText(1, strbuf); DisplaySetBarLimit(1, CountLumpTypes(LUMP_IGNORE_ME, 0)); DisplaySetBar(1, 0); cur_comms->file_pos = 0; // now write all the lumps to the output wad check1 = WriteAllLumps(); DisplayClose(); // finally, write out the directory check2 = WriteDirectory(); if (check1 != wad.num_entries || check2 != wad.num_entries) InternalError("Write directory count consistency failure (%d,%d,%d)", check1, check2, wad.num_entries); return GLBSP_E_OK; } // // DeleteGwaFile // void DeleteGwaFile(const char *base_wad_name) { char *gwa_file = ReplaceExtension(base_wad_name, "gwa"); if (remove(gwa_file) == 0) PrintMsg("Deleted GWA file: %s\n", gwa_file); UtilFree(gwa_file); } // // CloseWads // void CloseWads(void) { int i; if (in_file) { fclose(in_file); in_file = NULL; } if (out_file) { fclose(out_file); out_file = NULL; } /* free directory entries */ while (wad.dir_head) { lump_t *head = wad.dir_head; wad.dir_head = head->next; FreeLump(head); } /* free the level names */ if (wad.level_names) { for (i=0; i < wad.num_level_names; i++) UtilFree((char *) wad.level_names[i]); UtilFree((void *)wad.level_names); wad.level_names = NULL; } } /* ---------------------------------------------------------------- */ //static lump_t *zout_lump; //static z_stream zout_stream; //static Bytef zout_buffer[1024]; // // ZLibBeginLump // void ZLibBeginLump(lump_t *lump) { /* zout_lump = lump; zout_stream.zalloc = (alloc_func)0; zout_stream.zfree = (free_func)0; zout_stream.opaque = (voidpf)0; if (Z_OK != deflateInit(&zout_stream, Z_DEFAULT_COMPRESSION)) FatalError("Trouble setting up zlib compression"); zout_stream.next_out = zout_buffer; zout_stream.avail_out = sizeof(zout_buffer); */ } // // ZLibAppendLump // void ZLibAppendLump(const void *data, int length) { // ASSERT(zout_lump) // ASSERT(length > 0) /* zout_stream.next_in = (Bytef*)data; // const override zout_stream.avail_in = length; while (zout_stream.avail_in > 0) { int err = deflate(&zout_stream, Z_NO_FLUSH); if (err != Z_OK) FatalError("Trouble compressing %d bytes (zlib)\n", length); if (zout_stream.avail_out == 0) { AppendLevelLump(zout_lump, zout_buffer, sizeof(zout_buffer)); zout_stream.next_out = zout_buffer; zout_stream.avail_out = sizeof(zout_buffer); } } */ } // // ZLibFinishLump // void ZLibFinishLump(void) { //int left_over; // ASSERT(zout_stream.avail_out > 0) /* zout_stream.next_in = Z_NULL; zout_stream.avail_in = 0; for (;;) { int err = deflate(&zout_stream, Z_FINISH); if (err == Z_STREAM_END) break; if (err != Z_OK) FatalError("Trouble finishing compression (zlib)\n"); if (zout_stream.avail_out == 0) { AppendLevelLump(zout_lump, zout_buffer, sizeof(zout_buffer)); zout_stream.next_out = zout_buffer; zout_stream.avail_out = sizeof(zout_buffer); } } left_over = sizeof(zout_buffer) - zout_stream.avail_out; if (left_over > 0) AppendLevelLump(zout_lump, zout_buffer, left_over); deflateEnd(&zout_stream); zout_lump = NULL; */ } /* ---------------------------------------------------------------- */ // // Mark failure routines // void MarkSoftFailure(int soft) { wad.current_level->lev_info->soft_limit |= soft; } void MarkHardFailure(int hard) { wad.current_level->lev_info->hard_limit |= hard; } void MarkV5Switch(int v5) { wad.current_level->lev_info->v5_switch |= v5; } void MarkZDSwitch(void) { level_t *lev = wad.current_level->lev_info; lev->v5_switch |= LIMIT_ZDBSP; lev->soft_limit &= ~ (LIMIT_VERTEXES); lev->hard_limit &= ~ (LIMIT_VERTEXES); } // // ReportOneOverflow( // void ReportOneOverflow(const lump_t *lump, int limit, boolean_g hard) { const char *msg = hard ? "overflowed the absolute limit" : "overflowed the original limit"; PrintMsg("%-8s: ", lump->name); switch (limit) { case LIMIT_VERTEXES: PrintMsg("Number of Vertices %s.\n", msg); break; case LIMIT_SECTORS: PrintMsg("Number of Sectors %s.\n", msg); break; case LIMIT_SIDEDEFS: PrintMsg("Number of Sidedefs %s\n", msg); break; case LIMIT_LINEDEFS: PrintMsg("Number of Linedefs\n", msg); break; case LIMIT_SEGS: PrintMsg("Number of Segs %s.\n", msg); break; case LIMIT_SSECTORS: PrintMsg("Number of Subsectors %s.\n", msg); break; case LIMIT_NODES: PrintMsg("Number of Nodes %s.\n", msg); break; case LIMIT_GL_VERT: PrintMsg("Number of GL vertices %s.\n", msg); break; case LIMIT_GL_SEGS: PrintMsg("Number of GL segs %s.\n", msg); break; case LIMIT_GL_SSECT: PrintMsg("Number of GL subsectors %s.\n", msg); break; case LIMIT_GL_NODES: PrintMsg("Number of GL nodes %s.\n", msg); break; case LIMIT_BAD_SIDE: PrintMsg("One or more linedefs has a bad sidedef.\n"); break; case LIMIT_BMAP_TRUNC: PrintMsg("Blockmap area was too big - truncated.\n"); break; case LIMIT_BLOCKMAP: PrintMsg("Blockmap lump %s.\n", msg); break; default: InternalError("UNKNOWN LIMIT BIT: 0x%06x", limit); } } // ReportOverflows // void ReportOverflows(boolean_g hard) { lump_t *cur; if (hard) { PrintMsg( "ERRORS. The following levels failed to be built, and won't\n" "work in any Doom port (and may even crash it).\n\n" ); } else // soft { PrintMsg( "POTENTIAL FAILURES. The following levels should work in a\n" "modern Doom port, but may fail (or even crash) in older ports.\n\n" ); } for (cur=wad.dir_head; cur; cur=cur->next) { level_t *lev = cur->lev_info; int limits, one_lim; if (! (lev && ! (lev->flags & LEVEL_IS_GL))) continue; limits = hard ? lev->hard_limit : lev->soft_limit; if (limits == 0) continue; for (one_lim = (1<<20); one_lim; one_lim >>= 1) { if (limits & one_lim) ReportOneOverflow(cur, one_lim, hard); } } } // // ReportV5Switches // void ReportV5Switches(void) { lump_t *cur; int saw_zdbsp = FALSE; PrintMsg( "V5 FORMAT UPGRADES. The following levels require a Doom port\n" "which supports V5 GL-Nodes, otherwise they will fail (or crash).\n\n" ); for (cur=wad.dir_head; cur; cur=cur->next) { level_t *lev = cur->lev_info; if (! (lev && ! (lev->flags & LEVEL_IS_GL))) continue; if (lev->v5_switch == 0) continue; if ((lev->v5_switch & LIMIT_ZDBSP) && ! saw_zdbsp) { PrintMsg("ZDBSP FORMAT has also been used for regular nodes.\n\n"); saw_zdbsp = TRUE; } if (lev->v5_switch & LIMIT_VERTEXES) { PrintMsg("%-8s: Number of Vertices overflowed the limit.\n", cur->name); } if (lev->v5_switch & LIMIT_GL_SSECT) { PrintMsg("%-8s: Number of GL segs overflowed the limit.\n", cur->name); } } } // // ReportFailedLevels // void ReportFailedLevels(void) { lump_t *cur; int lev_count = 0; int fail_soft = 0; int fail_hard = 0; int fail_v5 = 0; boolean_g need_spacer = FALSE; for (cur=wad.dir_head; cur; cur=cur->next) { if (! (cur->lev_info && ! (cur->lev_info->flags & LEVEL_IS_GL))) continue; lev_count++; if (cur->lev_info->soft_limit != 0) fail_soft++; if (cur->lev_info->hard_limit != 0) fail_hard++; if (cur->lev_info->v5_switch != 0) fail_v5++; } PrintMsg("\n"); if (fail_soft + fail_hard + fail_v5 == 0) { PrintMsg("All levels were built successfully.\n"); return; } PrintMsg("*** Problem Report ***\n\n"); if (fail_soft > 0) { ReportOverflows(FALSE); need_spacer = TRUE; } if (fail_v5 > 0) { if (need_spacer) PrintMsg("\n"); ReportV5Switches(); need_spacer = TRUE; } if (fail_hard > 0) { if (need_spacer) PrintMsg("\n"); ReportOverflows(TRUE); } PrintMsg("\nEnd of problem report.\n"); }