1 //------------------------------------------------------------------------
2 // WAD : WAD read/write functions.
3 //------------------------------------------------------------------------
4 //
5 //  GL-Friendly Node Builder (C) 2000-2005 Andrew Apted
6 //
7 //  Based on 'BSP 2.3' by Colin Reed, Lee Killough and others.
8 //
9 //  This program is free software; you can redistribute it and/or
10 //  modify it under the terms of the GNU General Public License
11 //  as published by the Free Software Foundation; either version 2
12 //  of the License, or (at your option) any later version.
13 //
14 //  This program is distributed in the hope that it will be useful,
15 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 //  GNU General Public License for more details.
18 //
19 //------------------------------------------------------------------------
20 
21 #include "system.h"
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdarg.h>
27 #include <ctype.h>
28 #include <math.h>
29 #include <limits.h>
30 #include <errno.h>
31 
32 #include <zlib.h>
33 
34 #include "blockmap.h"
35 #include "level.h"
36 #include "node.h"
37 #include "seg.h"
38 #include "structs.h"
39 #include "util.h"
40 #include "wad.h"
41 
42 
43 static FILE *in_file = NULL;
44 static FILE *out_file = NULL;
45 
46 
47 #define DEBUG_DIR   0
48 #define DEBUG_LUMP  0
49 #define DEBUG_KEYS  0
50 
51 #define APPEND_BLKSIZE  256
52 #define LEVNAME_BUNCH   20
53 
54 #define ALIGN_LEN(len)  ((((len) + 3) / 4) * 4)
55 
56 
57 // current wad info
58 static wad_t wad;
59 
60 
61 /* ---------------------------------------------------------------- */
62 
63 
64 #define NUM_LEVEL_LUMPS  12
65 #define NUM_GL_LUMPS     5
66 
67 static const char *level_lumps[NUM_LEVEL_LUMPS]=
68 {
69   "THINGS", "LINEDEFS", "SIDEDEFS", "VERTEXES", "SEGS",
70   "SSECTORS", "NODES", "SECTORS", "REJECT", "BLOCKMAP",
71   "BEHAVIOR",  // <-- hexen support
72   "SCRIPTS"  // -JL- Lump with script sources
73 };
74 
75 static const char *gl_lumps[NUM_GL_LUMPS]=
76 {
77   "GL_VERT", "GL_SEGS", "GL_SSECT", "GL_NODES",
78   "GL_PVS"  // -JL- PVS (Potentially Visible Set) lump
79 };
80 
81 static const char align_filler[4] = { 0, 0, 0, 0 };
82 
83 
84 //
85 // CheckMagic
86 //
CheckMagic(const char type[4])87 static int CheckMagic(const char type[4])
88 {
89   if ((type[0] == 'I' || type[0] == 'P') &&
90        type[1] == 'W' && type[2] == 'A' && type[3] == 'D')
91   {
92     return TRUE;
93   }
94 
95   return FALSE;
96 }
97 
98 
99 //
100 // CheckLevelName
101 //
CheckLevelName(const char * name)102 static int CheckLevelName(const char *name)
103 {
104   int n;
105 
106   for (n=0; n < wad.num_level_names; n++)
107   {
108     if (strcmp(wad.level_names[n], name) == 0)
109       return TRUE;
110   }
111 
112   return FALSE;
113 }
114 
115 
116 //
117 // CheckLevelLumpName
118 //
119 // Tests if the entry name is one of the level lumps.
120 //
CheckLevelLumpName(const char * name)121 static int CheckLevelLumpName(const char *name)
122 {
123   int i;
124 
125   for (i=0; i < NUM_LEVEL_LUMPS; i++)
126   {
127     if (strcmp(name, level_lumps[i]) == 0)
128       return TRUE;
129   }
130 
131   return FALSE;
132 }
133 
134 
135 //
136 // CheckGLLumpName
137 //
138 // Tests if the entry name matches GL_ExMy or GL_MAPxx, or one of the
139 // GL lump names.
140 //
CheckGLLumpName(const char * name)141 static int CheckGLLumpName(const char *name)
142 {
143   int i;
144 
145   if (name[0] != 'G' || name[1] != 'L' || name[2] != '_')
146     return FALSE;
147 
148   for (i=0; i < NUM_GL_LUMPS; i++)
149   {
150     if (strcmp(name, gl_lumps[i]) == 0)
151       return TRUE;
152   }
153 
154   return CheckLevelName(name+3);
155 }
156 
157 
158 //
159 // Level name helper
160 //
AddLevelName(const char * name)161 static INLINE_G void AddLevelName(const char *name)
162 {
163   if ((wad.num_level_names % LEVNAME_BUNCH) == 0)
164   {
165     wad.level_names = (const char **) UtilRealloc((void *)wad.level_names,
166         (wad.num_level_names + LEVNAME_BUNCH) * sizeof(const char *));
167   }
168 
169   wad.level_names[wad.num_level_names] = UtilStrDup(name);
170   wad.num_level_names++;
171 }
172 
173 
174 //
175 // NewLevel
176 //
177 // Create new level information
178 //
NewLevel(int flags)179 static level_t *NewLevel(int flags)
180 {
181   level_t *cur;
182 
183   cur = UtilCalloc(sizeof(level_t));
184 
185   cur->flags = flags;
186 
187   return cur;
188 }
189 
190 
191 //
192 // NewLump
193 //
194 // Create new lump.  'name' must be allocated storage.
195 //
NewLump(char * name)196 static lump_t *NewLump(char *name)
197 {
198   lump_t *cur;
199 
200   cur = UtilCalloc(sizeof(lump_t));
201 
202   cur->name = name;
203   cur->start = cur->new_start = -1;
204   cur->flags = LUMP_NEW;
205   cur->length = 0;
206   cur->space = 0;
207   cur->data = NULL;
208   cur->lev_info = NULL;
209 
210   return cur;
211 }
212 
213 
214 static void FreeLump(lump_t *lump);
215 
216 //
217 // FreeWadLevel
218 //
FreeWadLevel(level_t * level)219 static void FreeWadLevel(level_t *level)
220 {
221   while (level->children)
222   {
223     lump_t *head = level->children;
224     level->children = head->next;
225 
226     // the ol' recursion trick... :)
227     FreeLump(head);
228   }
229 
230   UtilFree(level);
231 }
232 
233 //
234 // FreeLump
235 //
FreeLump(lump_t * lump)236 static void FreeLump(lump_t *lump)
237 {
238   // free level lumps, if any
239   if (lump->lev_info)
240   {
241     FreeWadLevel(lump->lev_info);
242   }
243 
244   // check 'data' here, since it gets freed in WriteLumpData()
245   if (lump->data)
246     UtilFree(lump->data);
247 
248   UtilFree(lump->name);
249   UtilFree(lump);
250 }
251 
252 
253 //
254 // ReadHeader
255 //
256 // Returns TRUE if successful, or FALSE if there was a problem (in
257 // which case the error message as been setup).
258 //
ReadHeader(const char * filename)259 static int ReadHeader(const char *filename)
260 {
261   size_t len;
262   raw_wad_header_t header;
263   char strbuf[1024];
264 
265   len = fread(&header, sizeof(header), 1, in_file);
266 
267   if (len != 1)
268   {
269     sprintf(strbuf, "Trouble reading wad header for %s [%s]",
270       filename, strerror(errno));
271 
272     SetErrorMsg(strbuf);
273     return FALSE;
274   }
275 
276   if (! CheckMagic(header.type))
277   {
278     sprintf(strbuf, "%s does not appear to be a wad file (bad magic)",
279         filename);
280 
281     SetErrorMsg(strbuf);
282     return FALSE;
283   }
284 
285   wad.kind = (header.type[0] == 'I') ? IWAD : PWAD;
286 
287   wad.num_entries = UINT32(header.num_entries);
288   wad.dir_start   = UINT32(header.dir_start);
289 
290   // initialise stuff
291   wad.dir_head = NULL;
292   wad.dir_tail = NULL;
293   wad.current_level = NULL;
294   wad.level_names = NULL;
295   wad.num_level_names = 0;
296 
297   return TRUE;
298 }
299 
300 
301 //
302 // ReadDirEntry
303 //
ReadDirEntry(void)304 static void ReadDirEntry(void)
305 {
306   size_t len;
307   raw_wad_entry_t entry;
308   lump_t *lump;
309 
310   DisplayTicker();
311 
312   len = fread(&entry, sizeof(entry), 1, in_file);
313 
314   if (len != 1)
315     FatalError("Trouble reading wad directory");
316 
317   lump = NewLump(UtilStrNDup(entry.name, 8));
318 
319   lump->start  = UINT32(entry.start);
320   lump->length = UINT32(entry.length);
321 
322 # if DEBUG_DIR
323   PrintDebug("Read dir... %s\n", lump->name);
324 # endif
325 
326   // link it in
327   lump->next = NULL;
328   lump->prev = wad.dir_tail;
329 
330   if (wad.dir_tail)
331     wad.dir_tail->next = lump;
332   else
333     wad.dir_head = lump;
334 
335   wad.dir_tail = lump;
336 }
337 
338 //
339 // DetermineLevelNames
340 //
DetermineLevelNames(void)341 static void DetermineLevelNames(void)
342 {
343   lump_t *L, *N;
344   int i;
345 
346   for (L=wad.dir_head; L; L=L->next)
347   {
348     // check if the next four lumps after the current lump match the
349     // level-lump names.
350     //
351     for (i=0, N=L->next; (i < 4) && N; i++, N=N->next)
352       if (strcmp(N->name, level_lumps[i]) != 0)
353         break;
354 
355     if (i != 4)
356       continue;
357 
358 #   if DEBUG_DIR
359     PrintDebug("Found level name: %s\n", L->name);
360 #   endif
361 
362     // check for duplicate levels (ignored)
363     if (CheckLevelName(L->name))
364     {
365       PrintWarn("Level name '%s' found twice in wad\n", L->name);
366       continue;
367     }
368 
369     // check for long names
370     if (strlen(L->name) > 5)
371       PrintWarn("Long level name '%s' found in wad\n", L->name);
372 
373     AddLevelName(L->name);
374   }
375 }
376 
377 //
378 // ProcessDirEntry
379 //
ProcessDirEntry(lump_t * lump)380 static void ProcessDirEntry(lump_t *lump)
381 {
382   DisplayTicker();
383 
384   // ignore previous GL lump info
385   if (CheckGLLumpName(lump->name))
386   {
387 #   if DEBUG_DIR
388     PrintDebug("Discarding previous GL info: %s\n", lump->name);
389 #   endif
390 
391     FreeLump(lump);
392     wad.num_entries--;
393 
394     return;
395   }
396 
397   // mark the lump as 'ignorable' when in GWA mode.
398   if (cur_info->gwa_mode)
399     lump->flags |= LUMP_IGNORE_ME;
400 
401   // --- LEVEL MARKERS ---
402 
403   if (CheckLevelName(lump->name))
404   {
405     /* NOTE !  Level marks can have data (in Hexen anyway) */
406     if (cur_info->load_all)
407       lump->flags |= LUMP_READ_ME;
408     else
409       lump->flags |= LUMP_COPY_ME;
410 
411     // OK, start a new level
412 
413     lump->lev_info = NewLevel(0);
414 
415     wad.current_level = lump;
416 
417 #   if DEBUG_DIR
418     PrintDebug("Process dir... %s :\n", lump->name);
419 #   endif
420 
421     // link it in
422     lump->next = NULL;
423     lump->prev = wad.dir_tail;
424 
425     if (wad.dir_tail)
426       wad.dir_tail->next = lump;
427     else
428       wad.dir_head = lump;
429 
430     wad.dir_tail = lump;
431 
432     return;
433   }
434 
435   // --- LEVEL LUMPS ---
436 
437   if (wad.current_level)
438   {
439     if (CheckLevelLumpName(lump->name))
440     {
441       // check for duplicates
442       if (FindLevelLump(lump->name))
443       {
444         PrintWarn("Duplicate entry '%s' ignored in %s\n",
445             lump->name, wad.current_level->name);
446 
447         FreeLump(lump);
448         wad.num_entries--;
449 
450         return;
451       }
452 
453 #     if DEBUG_DIR
454       PrintDebug("Process dir... |--- %s\n", lump->name);
455 #     endif
456 
457       // mark it to be loaded
458       lump->flags |= LUMP_READ_ME;
459 
460       // link it in
461       lump->next = wad.current_level->lev_info->children;
462       lump->prev = NULL;
463 
464       if (lump->next)
465         lump->next->prev = lump;
466 
467       wad.current_level->lev_info->children = lump;
468       return;
469     }
470 
471     // OK, non-level lump.  End the previous level.
472 
473     wad.current_level = NULL;
474   }
475 
476   // --- ORDINARY LUMPS ---
477 
478 # if DEBUG_DIR
479   PrintDebug("Process dir... %s\n", lump->name);
480 # endif
481 
482   if (CheckLevelLumpName(lump->name))
483     PrintWarn("Level lump '%s' found outside any level\n", lump->name);
484 
485   // maybe load data
486   if (cur_info->load_all)
487     lump->flags |= LUMP_READ_ME;
488   else
489     lump->flags |= LUMP_COPY_ME;
490 
491   // link it in
492   lump->next = NULL;
493   lump->prev = wad.dir_tail;
494 
495   if (wad.dir_tail)
496     wad.dir_tail->next = lump;
497   else
498     wad.dir_head = lump;
499 
500   wad.dir_tail = lump;
501 }
502 
503 //
504 // ReadDirectory
505 //
ReadDirectory(void)506 static void ReadDirectory(void)
507 {
508   int i;
509   int total_entries = wad.num_entries;
510   lump_t *prev_list;
511 
512   fseek(in_file, wad.dir_start, SEEK_SET);
513 
514   for (i=0; i < total_entries; i++)
515   {
516     ReadDirEntry();
517   }
518 
519   DetermineLevelNames();
520 
521   // finally, unlink all lumps and process each one in turn
522 
523   prev_list = wad.dir_head;
524   wad.dir_head = wad.dir_tail = NULL;
525 
526   while (prev_list)
527   {
528     lump_t *cur = prev_list;
529     prev_list = cur->next;
530 
531     ProcessDirEntry(cur);
532   }
533 }
534 
535 
536 //
537 // ReadLumpData
538 //
ReadLumpData(lump_t * lump)539 static void ReadLumpData(lump_t *lump)
540 {
541   size_t len;
542 
543   cur_comms->file_pos++;
544   DisplaySetBar(1, cur_comms->file_pos);
545   DisplayTicker();
546 
547 # if DEBUG_LUMP
548   PrintDebug("Reading... %s (%d)\n", lump->name, lump->length);
549 # endif
550 
551   if (lump->length == 0)
552     return;
553 
554   lump->data = UtilCalloc(lump->length);
555 
556   fseek(in_file, lump->start, SEEK_SET);
557 
558   len = fread(lump->data, lump->length, 1, in_file);
559 
560   if (len != 1)
561   {
562     if (wad.current_level)
563       PrintWarn("Trouble reading lump '%s' in %s\n",
564           lump->name, wad.current_level->name);
565     else
566       PrintWarn("Trouble reading lump '%s'\n", lump->name);
567   }
568 
569   lump->flags &= ~LUMP_READ_ME;
570 }
571 
572 
573 //
574 // ReadAllLumps
575 //
576 // Returns number of entries read.
577 //
ReadAllLumps(void)578 static int ReadAllLumps(void)
579 {
580   lump_t *cur, *L;
581   int count = 0;
582 
583   for (cur=wad.dir_head; cur; cur=cur->next)
584   {
585     count++;
586 
587     if (cur->flags & LUMP_READ_ME)
588       ReadLumpData(cur);
589 
590     if (cur->lev_info && ! (cur->lev_info->flags & LEVEL_IS_GL))
591     {
592       for (L=cur->lev_info->children; L; L=L->next)
593       {
594         count++;
595 
596         if (L->flags & LUMP_READ_ME)
597           ReadLumpData(L);
598       }
599     }
600   }
601 
602   return count;
603 }
604 
605 
606 //
607 // CountLumpTypes
608 //
609 // Returns number of entries with matching flags.  Used to predict how
610 // many ReadLumpData() or WriteLumpData() calls will be made (for the
611 // progress bars).
612 //
CountLumpTypes(int flag_mask,int flag_match)613 static int CountLumpTypes(int flag_mask, int flag_match)
614 {
615   lump_t *cur, *L;
616   int count = 0;
617 
618   for (cur=wad.dir_head; cur; cur=cur->next)
619   {
620     if ((cur->flags & flag_mask) == flag_match)
621       count++;
622 
623     if (cur->lev_info)
624     {
625       for (L=cur->lev_info->children; L; L=L->next)
626         if ((L->flags & flag_mask) == flag_match)
627           count++;
628     }
629   }
630 
631   return count;
632 }
633 
634 
635 /* ---------------------------------------------------------------- */
636 
637 
638 //
639 // WriteHeader
640 //
WriteHeader(void)641 static void WriteHeader(void)
642 {
643   size_t len;
644   raw_wad_header_t header;
645 
646   switch (wad.kind)
647   {
648     case IWAD:
649       strncpy(header.type, "IWAD", 4);
650       break;
651 
652     case PWAD:
653       strncpy(header.type, "PWAD", 4);
654       break;
655   }
656 
657   header.num_entries = UINT32(wad.num_entries);
658   header.dir_start   = UINT32(wad.dir_start);
659 
660   len = fwrite(&header, sizeof(header), 1, out_file);
661 
662   if (len != 1)
663     PrintWarn("Trouble writing wad header\n");
664 }
665 
666 
667 //
668 // CreateGLMarker
669 //
CreateGLMarker(void)670 lump_t *CreateGLMarker(void)
671 {
672   lump_t *level = wad.current_level;
673   lump_t *cur;
674 
675   char name_buf[16];
676   boolean_g long_name = FALSE;
677 
678   if (strlen(level->name) <= 5)
679   {
680     sprintf(name_buf, "GL_%s", level->name);
681   }
682   else
683   {
684     // support for level names longer than 5 letters
685     strcpy(name_buf, "GL_LEVEL");
686     long_name = TRUE;
687   }
688 
689   cur = NewLump(UtilStrDup(name_buf));
690 
691   cur->lev_info = NewLevel(LEVEL_IS_GL);
692 
693   // link it in
694   cur->next = level->next;
695   cur->prev = level;
696 
697   if (cur->next)
698     cur->next->prev = cur;
699 
700   level->next = cur;
701   level->lev_info->buddy = cur;
702 
703   if (long_name)
704   {
705     AddGLTextLine("LEVEL", level->name);
706   }
707 
708   return cur;
709 }
710 
711 
712 //
713 // SortLumps
714 //
715 // Algorithm is pretty simple: for each of the names, if such a lump
716 // exists in the list, move it to the head of the list.  By going
717 // backwards through the names, we ensure the correct order.
718 //
SortLumps(lump_t ** list,const char ** names,int count)719 static void SortLumps(lump_t ** list, const char **names, int count)
720 {
721   int i;
722   lump_t *cur;
723 
724   for (i=count-1; i >= 0; i--)
725   {
726     for (cur=(*list); cur; cur=cur->next)
727     {
728       if (strcmp(cur->name, names[i]) != 0)
729         continue;
730 
731       // unlink it
732       if (cur->next)
733         cur->next->prev = cur->prev;
734 
735       if (cur->prev)
736         cur->prev->next = cur->next;
737       else
738         (*list) = cur->next;
739 
740       // add to head
741       cur->next = (*list);
742       cur->prev = NULL;
743 
744       if (cur->next)
745         cur->next->prev = cur;
746 
747       (*list) = cur;
748 
749       // continue with next name (important !)
750       break;
751     }
752   }
753 }
754 
755 
756 //
757 // RecomputeDirectory
758 //
759 // Calculates all the lump offsets for the directory.
760 //
RecomputeDirectory(void)761 static void RecomputeDirectory(void)
762 {
763   lump_t *cur, *L;
764   level_t *lev;
765 
766   wad.num_entries = 0;
767   wad.dir_start = sizeof(raw_wad_header_t);
768 
769   // run through all the lumps, computing the 'new_start' fields, the
770   // number of lumps in the directory, the directory starting pos, and
771   // also sorting the lumps in the levels.
772 
773   for (cur=wad.dir_head; cur; cur=cur->next)
774   {
775     if (cur->flags & LUMP_IGNORE_ME)
776       continue;
777 
778     cur->new_start = wad.dir_start;
779 
780     wad.dir_start += ALIGN_LEN(cur->length);
781     wad.num_entries++;
782 
783     lev = cur->lev_info;
784 
785     if (lev)
786     {
787       if (lev->flags & LEVEL_IS_GL)
788         SortLumps(&lev->children, gl_lumps, NUM_GL_LUMPS);
789       else
790         SortLumps(&lev->children, level_lumps, NUM_LEVEL_LUMPS);
791 
792       for (L=lev->children; L; L=L->next)
793       {
794         if (L->flags & LUMP_IGNORE_ME)
795           continue;
796 
797         L->new_start = wad.dir_start;
798 
799         wad.dir_start += ALIGN_LEN(L->length);
800         wad.num_entries++;
801       }
802     }
803   }
804 }
805 
806 
807 //
808 // WriteLumpData
809 //
WriteLumpData(lump_t * lump)810 static void WriteLumpData(lump_t *lump)
811 {
812   size_t len;
813   int align_size;
814 
815   cur_comms->file_pos++;
816   DisplaySetBar(1, cur_comms->file_pos);
817   DisplayTicker();
818 
819 # if DEBUG_LUMP
820   if (lump->flags & LUMP_COPY_ME)
821     PrintDebug("Copying... %s (%d)\n", lump->name, lump->length);
822   else
823     PrintDebug("Writing... %s (%d)\n", lump->name, lump->length);
824 # endif
825 
826   if (ftell(out_file) != lump->new_start)
827     PrintWarn("Consistency failure writing %s (%08X, %08X\n",
828       lump->name, ftell(out_file), lump->new_start);
829 
830   if (lump->length == 0)
831     return;
832 
833   if (lump->flags & LUMP_COPY_ME)
834   {
835     lump->data = UtilCalloc(lump->length);
836 
837     fseek(in_file, lump->start, SEEK_SET);
838 
839     len = fread(lump->data, lump->length, 1, in_file);
840 
841     if (len != 1)
842       PrintWarn("Trouble reading lump %s to copy\n", lump->name);
843   }
844 
845   len = fwrite(lump->data, lump->length, 1, out_file);
846 
847   if (len != 1)
848     PrintWarn("Trouble writing lump %s\n", lump->name);
849 
850   align_size = ALIGN_LEN(lump->length) - lump->length;
851 
852   if (align_size > 0)
853     fwrite(align_filler, align_size, 1, out_file);
854 
855   UtilFree(lump->data);
856 
857   lump->data = NULL;
858 }
859 
860 
861 //
862 // WriteAllLumps
863 //
864 // Returns number of entries written.
865 //
WriteAllLumps(void)866 static int WriteAllLumps(void)
867 {
868   lump_t *cur, *L;
869   int count = 0;
870 
871   for (cur=wad.dir_head; cur; cur=cur->next)
872   {
873     if (cur->flags & LUMP_IGNORE_ME)
874       continue;
875 
876     WriteLumpData(cur);
877     count++;
878 
879     if (cur->lev_info)
880     {
881       for (L=cur->lev_info->children; L; L=L->next)
882       {
883         if (L->flags & LUMP_IGNORE_ME)
884           continue;
885 
886         WriteLumpData(L);
887         count++;
888       }
889     }
890   }
891 
892   fflush(out_file);
893 
894   return count;
895 }
896 
897 
898 //
899 // WriteDirEntry
900 //
WriteDirEntry(lump_t * lump)901 static void WriteDirEntry(lump_t *lump)
902 {
903   size_t len;
904   raw_wad_entry_t entry;
905 
906   DisplayTicker();
907 
908   strncpy(entry.name, lump->name, 8);
909 
910   entry.start  = UINT32(lump->new_start);
911   entry.length = UINT32(lump->length);
912 
913   len = fwrite(&entry, sizeof(entry), 1, out_file);
914 
915   if (len != 1)
916     PrintWarn("Trouble writing wad directory\n");
917 }
918 
919 
920 //
921 // WriteDirectory
922 //
923 // Returns number of entries written.
924 //
WriteDirectory(void)925 static int WriteDirectory(void)
926 {
927   lump_t *cur, *L;
928   int count = 0;
929 
930   if (ftell(out_file) != wad.dir_start)
931     PrintWarn("Consistency failure writing lump directory "
932       "(%08X,%08X)\n", ftell(out_file), wad.dir_start);
933 
934   for (cur=wad.dir_head; cur; cur=cur->next)
935   {
936     if (cur->flags & LUMP_IGNORE_ME)
937       continue;
938 
939     WriteDirEntry(cur);
940     count++;
941 
942 #   if DEBUG_DIR
943     if (cur->lev_info)
944       PrintDebug("Write dir... %s :\n", cur->name);
945     else
946       PrintDebug("Write dir... %s\n", cur->name);
947 #   endif
948 
949     if (cur->lev_info)
950     {
951       for (L=cur->lev_info->children; L; L=L->next)
952       {
953         if (cur->flags & LUMP_IGNORE_ME)
954           continue;
955 
956 #       if DEBUG_DIR
957         PrintDebug("Write dir... |--- %s\n", L->name);
958 #       endif
959 
960         WriteDirEntry(L);
961         count++;
962       }
963     }
964   }
965 
966   fflush(out_file);
967 
968   return count;
969 }
970 
971 
972 /* ---------------------------------------------------------------- */
973 
974 
975 //
976 // CheckExtension
977 //
CheckExtension(const char * filename,const char * ext)978 int CheckExtension(const char *filename, const char *ext)
979 {
980   int A = (int)strlen(filename) - 1;
981   int B = (int)strlen(ext) - 1;
982 
983   for (; B >= 0; B--, A--)
984   {
985     if (A < 0)
986       return FALSE;
987 
988     if (toupper(filename[A]) != toupper(ext[B]))
989       return FALSE;
990   }
991 
992   return (A >= 1) && (filename[A] == '.');
993 }
994 
995 //
996 // ReplaceExtension
997 //
ReplaceExtension(const char * filename,const char * ext)998 char *ReplaceExtension(const char *filename, const char *ext)
999 {
1000   char *dot_pos;
1001   char buffer[512];
1002 
1003   strcpy(buffer, filename);
1004 
1005   dot_pos = strrchr(buffer, '.');
1006 
1007   if (dot_pos)
1008     dot_pos[1] = 0;
1009   else
1010     strcat(buffer, ".");
1011 
1012   strcat(buffer, ext);
1013 
1014   return UtilStrDup(buffer);
1015 }
1016 
1017 
1018 //
1019 // CreateLevelLump
1020 //
CreateLevelLump(const char * name)1021 lump_t *CreateLevelLump(const char *name)
1022 {
1023   lump_t *cur;
1024 
1025 # if DEBUG_DIR
1026   PrintDebug("Create Lump... %s\n", name);
1027 # endif
1028 
1029   // already exists ?
1030   for (cur=wad.current_level->lev_info->children; cur; cur=cur->next)
1031   {
1032     if (strcmp(name, cur->name) == 0)
1033       break;
1034   }
1035 
1036   if (cur)
1037   {
1038     if (cur->data)
1039       UtilFree(cur->data);
1040 
1041     cur->data = NULL;
1042     cur->length = 0;
1043     cur->space  = 0;
1044 
1045     return cur;
1046   }
1047 
1048   // nope, allocate new one
1049   cur = NewLump(UtilStrDup(name));
1050 
1051   // link it in
1052   cur->next = wad.current_level->lev_info->children;
1053   cur->prev = NULL;
1054 
1055   if (cur->next)
1056     cur->next->prev = cur;
1057 
1058   wad.current_level->lev_info->children = cur;
1059 
1060   return cur;
1061 }
1062 
1063 
1064 //
1065 // CreateGLLump
1066 //
CreateGLLump(const char * name)1067 lump_t *CreateGLLump(const char *name)
1068 {
1069   lump_t *cur;
1070   lump_t *gl_level;
1071 
1072 # if DEBUG_DIR
1073   PrintDebug("Create GL Lump... %s\n", name);
1074 # endif
1075 
1076   // create GL level marker if necessary
1077   if (! wad.current_level->lev_info->buddy)
1078     CreateGLMarker();
1079 
1080   gl_level = wad.current_level->lev_info->buddy;
1081 
1082   // check if already exists
1083   for (cur=gl_level->lev_info->children; cur; cur=cur->next)
1084   {
1085     if (strcmp(name, cur->name) == 0)
1086       break;
1087   }
1088 
1089   if (cur)
1090   {
1091     if (cur->data)
1092       UtilFree(cur->data);
1093 
1094     cur->data = NULL;
1095     cur->length = 0;
1096     cur->space  = 0;
1097 
1098     return cur;
1099   }
1100 
1101   // nope, allocate new one
1102   cur = NewLump(UtilStrDup(name));
1103 
1104   // link it in
1105   cur->next = gl_level->lev_info->children;
1106   cur->prev = NULL;
1107 
1108   if (cur->next)
1109     cur->next->prev = cur;
1110 
1111   gl_level->lev_info->children = cur;
1112 
1113   return cur;
1114 }
1115 
1116 
1117 //
1118 // AppendLevelLump
1119 //
AppendLevelLump(lump_t * lump,const void * data,int length)1120 void AppendLevelLump(lump_t *lump, const void *data, int length)
1121 {
1122   if (length == 0)
1123     return;
1124 
1125   if (lump->length == 0)
1126   {
1127     lump->space = MAX(length, APPEND_BLKSIZE);
1128     lump->data = UtilCalloc(lump->space);
1129   }
1130   else if (lump->space < length)
1131   {
1132     lump->space = MAX(length, APPEND_BLKSIZE);
1133     lump->data = UtilRealloc(lump->data, lump->length + lump->space);
1134   }
1135 
1136   memcpy(((char *)lump->data) + lump->length, data, length);
1137 
1138   lump->length += length;
1139   lump->space  -= length;
1140 }
1141 
1142 
1143 //
1144 // AddGLTextLine
1145 //
AddGLTextLine(const char * keyword,const char * value)1146 void AddGLTextLine(const char *keyword, const char *value)
1147 {
1148   lump_t *gl_level;
1149 
1150   // create GL level marker if necessary
1151   if (! wad.current_level->lev_info->buddy)
1152     CreateGLMarker();
1153 
1154   gl_level = wad.current_level->lev_info->buddy;
1155 
1156 # if DEBUG_KEYS
1157   PrintDebug("[%s] Adding: %s=%s\n", gl_level->name, keyword, value);
1158 # endif
1159 
1160   AppendLevelLump(gl_level, keyword, strlen(keyword));
1161   AppendLevelLump(gl_level, "=", 1);
1162 
1163   AppendLevelLump(gl_level, value, strlen(value));
1164   AppendLevelLump(gl_level, "\n", 1);
1165 }
1166 
1167 
1168 //
1169 // CountLevels
1170 //
CountLevels(void)1171 int CountLevels(void)
1172 {
1173   lump_t *cur;
1174   int result = 0;
1175 
1176   for (cur=wad.dir_head; cur; cur=cur->next)
1177   {
1178     if (cur->lev_info && ! (cur->lev_info->flags & LEVEL_IS_GL))
1179       result++;
1180   }
1181 
1182   return result;
1183 }
1184 
1185 //
1186 // FindNextLevel
1187 //
FindNextLevel(void)1188 int FindNextLevel(void)
1189 {
1190   lump_t *cur;
1191 
1192   if (wad.current_level)
1193     cur = wad.current_level->next;
1194   else
1195     cur = wad.dir_head;
1196 
1197   while (cur && ! (cur->lev_info && ! (cur->lev_info->flags & LEVEL_IS_GL)))
1198     cur=cur->next;
1199 
1200   wad.current_level = cur;
1201 
1202   return (cur != NULL);
1203 }
1204 
1205 //
1206 // GetLevelName
1207 //
GetLevelName(void)1208 const char *GetLevelName(void)
1209 {
1210   if (!wad.current_level)
1211     InternalError("GetLevelName: no current level");
1212 
1213   return wad.current_level->name;
1214 }
1215 
1216 //
1217 // FindLevelLump
1218 //
FindLevelLump(const char * name)1219 lump_t *FindLevelLump(const char *name)
1220 {
1221   lump_t *cur = wad.current_level->lev_info->children;
1222 
1223   while (cur && (strcmp(cur->name, name) != 0))
1224     cur=cur->next;
1225 
1226   return cur;
1227 }
1228 
1229 //
1230 // CheckLevelLumpZero
1231 //
CheckLevelLumpZero(lump_t * lump)1232 int CheckLevelLumpZero(lump_t *lump)
1233 {
1234   int i;
1235 
1236   if (lump->length == 0)
1237     return TRUE;
1238 
1239   // ASSERT(lump->data)
1240 
1241   for (i=0; i < lump->length; i++)
1242   {
1243     if (((uint8_g *)lump->data)[i])
1244       return FALSE;
1245   }
1246 
1247   return TRUE;
1248 }
1249 
1250 //
1251 // ReadWadFile
1252 //
ReadWadFile(const char * filename)1253 glbsp_ret_e ReadWadFile(const char *filename)
1254 {
1255   int check;
1256   char strbuf[1024];
1257 
1258   // open input wad file & read header
1259   in_file = fopen(filename, "rb");
1260 
1261   if (! in_file)
1262   {
1263     if (errno == ENOENT)
1264       sprintf(strbuf, "Cannot open WAD file: %s", filename);
1265     else
1266       sprintf(strbuf, "Cannot open WAD file: %s [%s]", filename,
1267           strerror(errno));
1268 
1269     SetErrorMsg(strbuf);
1270 
1271     return GLBSP_E_ReadError;
1272   }
1273 
1274   if (! ReadHeader(filename))
1275   {
1276     fclose(in_file);
1277     return GLBSP_E_ReadError;
1278   }
1279 
1280   PrintMsg("Opened %cWAD file : %s\n", (wad.kind == IWAD) ? 'I' : 'P',
1281       filename);
1282   PrintVerbose("Reading %d dir entries at 0x%X\n", wad.num_entries,
1283       wad.dir_start);
1284 
1285   // read directory
1286   ReadDirectory();
1287 
1288   DisplayOpen(DIS_FILEPROGRESS);
1289   DisplaySetTitle("glBSP Reading Wad");
1290 
1291   sprintf(strbuf, "Reading: %s", filename);
1292 
1293   DisplaySetBarText(1, strbuf);
1294   DisplaySetBarLimit(1, CountLumpTypes(LUMP_READ_ME, LUMP_READ_ME));
1295   DisplaySetBar(1, 0);
1296 
1297   cur_comms->file_pos = 0;
1298 
1299   // now read lumps
1300   check = ReadAllLumps();
1301 
1302   if (check != wad.num_entries)
1303     InternalError("Read directory count consistency failure (%d,%d)",
1304       check, wad.num_entries);
1305 
1306   wad.current_level = NULL;
1307 
1308   DisplayClose();
1309 
1310   return GLBSP_E_OK;
1311 }
1312 
1313 
1314 //
1315 // WriteWadFile
1316 //
WriteWadFile(const char * filename)1317 glbsp_ret_e WriteWadFile(const char *filename)
1318 {
1319   int check1, check2;
1320   char strbuf[1024];
1321 
1322   PrintMsg("\n");
1323   PrintMsg("Saving WAD as %s\n", filename);
1324 
1325   if (cur_info->gwa_mode)
1326     wad.kind = PWAD;
1327 
1328   RecomputeDirectory();
1329 
1330   // create output wad file & write the header
1331   out_file = fopen(filename, "wb");
1332 
1333   if (! out_file)
1334   {
1335     sprintf(strbuf, "Cannot create WAD file: %s [%s]", filename,
1336         strerror(errno));
1337 
1338     SetErrorMsg(strbuf);
1339 
1340     return GLBSP_E_WriteError;
1341   }
1342 
1343   WriteHeader();
1344 
1345   DisplayOpen(DIS_FILEPROGRESS);
1346   DisplaySetTitle("glBSP Writing Wad");
1347 
1348   sprintf(strbuf, "Writing: %s", filename);
1349 
1350   DisplaySetBarText(1, strbuf);
1351   DisplaySetBarLimit(1, CountLumpTypes(LUMP_IGNORE_ME, 0));
1352   DisplaySetBar(1, 0);
1353 
1354   cur_comms->file_pos = 0;
1355 
1356   // now write all the lumps to the output wad
1357   check1 = WriteAllLumps();
1358 
1359   DisplayClose();
1360 
1361   // finally, write out the directory
1362   check2 = WriteDirectory();
1363 
1364   if (check1 != wad.num_entries || check2 != wad.num_entries)
1365     InternalError("Write directory count consistency failure (%d,%d,%d)",
1366       check1, check2, wad.num_entries);
1367 
1368   return GLBSP_E_OK;
1369 }
1370 
1371 
1372 //
1373 // DeleteGwaFile
1374 //
DeleteGwaFile(const char * base_wad_name)1375 void DeleteGwaFile(const char *base_wad_name)
1376 {
1377   char *gwa_file = ReplaceExtension(base_wad_name, "gwa");
1378 
1379   if (remove(gwa_file) == 0)
1380     PrintMsg("Deleted GWA file: %s\n", gwa_file);
1381 
1382   UtilFree(gwa_file);
1383 }
1384 
1385 
1386 //
1387 // CloseWads
1388 //
CloseWads(void)1389 void CloseWads(void)
1390 {
1391   int i;
1392 
1393   if (in_file)
1394   {
1395     fclose(in_file);
1396     in_file = NULL;
1397   }
1398 
1399   if (out_file)
1400   {
1401     fclose(out_file);
1402     out_file = NULL;
1403   }
1404 
1405   /* free directory entries */
1406   while (wad.dir_head)
1407   {
1408     lump_t *head = wad.dir_head;
1409     wad.dir_head = head->next;
1410 
1411     FreeLump(head);
1412   }
1413 
1414   /* free the level names */
1415   if (wad.level_names)
1416   {
1417     for (i=0; i < wad.num_level_names; i++)
1418       UtilFree((char *) wad.level_names[i]);
1419 
1420     UtilFree((void *)wad.level_names);
1421     wad.level_names = NULL;
1422   }
1423 }
1424 
1425 
1426 /* ---------------------------------------------------------------- */
1427 
1428 static lump_t  *zout_lump;
1429 static z_stream zout_stream;
1430 static Bytef    zout_buffer[1024];
1431 
1432 //
1433 // ZLibBeginLump
1434 //
ZLibBeginLump(lump_t * lump)1435 void ZLibBeginLump(lump_t *lump)
1436 {
1437   zout_lump = lump;
1438 
1439   zout_stream.zalloc = (alloc_func)0;
1440   zout_stream.zfree  = (free_func)0;
1441   zout_stream.opaque = (voidpf)0;
1442 
1443   if (Z_OK != deflateInit(&zout_stream, Z_DEFAULT_COMPRESSION))
1444     FatalError("Trouble setting up zlib compression");
1445 
1446   zout_stream.next_out  = zout_buffer;
1447   zout_stream.avail_out = sizeof(zout_buffer);
1448 }
1449 
1450 //
1451 // ZLibAppendLump
1452 //
ZLibAppendLump(const void * data,int length)1453 void ZLibAppendLump(const void *data, int length)
1454 {
1455   // ASSERT(zout_lump)
1456   // ASSERT(length > 0)
1457 
1458   zout_stream.next_in  = (Bytef*)data;   // const override
1459   zout_stream.avail_in = length;
1460 
1461   while (zout_stream.avail_in > 0)
1462   {
1463     int err = deflate(&zout_stream, Z_NO_FLUSH);
1464 
1465     if (err != Z_OK)
1466       FatalError("Trouble compressing %d bytes (zlib)\n", length);
1467 
1468     if (zout_stream.avail_out == 0)
1469     {
1470       AppendLevelLump(zout_lump, zout_buffer, sizeof(zout_buffer));
1471 
1472       zout_stream.next_out  = zout_buffer;
1473       zout_stream.avail_out = sizeof(zout_buffer);
1474     }
1475   }
1476 }
1477 
1478 //
1479 // ZLibFinishLump
1480 //
ZLibFinishLump(void)1481 void ZLibFinishLump(void)
1482 {
1483   int left_over;
1484 
1485   // ASSERT(zout_stream.avail_out > 0)
1486 
1487   zout_stream.next_in  = Z_NULL;
1488   zout_stream.avail_in = 0;
1489 
1490   for (;;)
1491   {
1492     int err = deflate(&zout_stream, Z_FINISH);
1493 
1494     if (err == Z_STREAM_END)
1495       break;
1496 
1497     if (err != Z_OK)
1498       FatalError("Trouble finishing compression (zlib)\n");
1499 
1500     if (zout_stream.avail_out == 0)
1501     {
1502       AppendLevelLump(zout_lump, zout_buffer, sizeof(zout_buffer));
1503 
1504       zout_stream.next_out  = zout_buffer;
1505       zout_stream.avail_out = sizeof(zout_buffer);
1506     }
1507   }
1508 
1509   left_over = sizeof(zout_buffer) - zout_stream.avail_out;
1510 
1511   if (left_over > 0)
1512     AppendLevelLump(zout_lump, zout_buffer, left_over);
1513 
1514   deflateEnd(&zout_stream);
1515   zout_lump = NULL;
1516 }
1517 
1518 
1519 /* ---------------------------------------------------------------- */
1520 
1521 
1522 //
1523 // Mark failure routines
1524 //
MarkSoftFailure(int soft)1525 void MarkSoftFailure(int soft)
1526 {
1527   wad.current_level->lev_info->soft_limit |= soft;
1528 }
1529 
MarkHardFailure(int hard)1530 void MarkHardFailure(int hard)
1531 {
1532   wad.current_level->lev_info->hard_limit |= hard;
1533 }
1534 
MarkV5Switch(int v5)1535 void MarkV5Switch(int v5)
1536 {
1537   wad.current_level->lev_info->v5_switch |= v5;
1538 }
1539 
MarkZDSwitch(void)1540 void MarkZDSwitch(void)
1541 {
1542   level_t *lev = wad.current_level->lev_info;
1543 
1544   lev->v5_switch |= LIMIT_ZDBSP;
1545 
1546   lev->soft_limit &= ~ (LIMIT_VERTEXES);
1547   lev->hard_limit &= ~ (LIMIT_VERTEXES);
1548 }
1549 
1550 
1551 //
1552 // ReportOneOverflow(
1553 //
ReportOneOverflow(const lump_t * lump,int limit,boolean_g hard)1554 void ReportOneOverflow(const lump_t *lump, int limit, boolean_g hard)
1555 {
1556   const char *msg = hard ? "overflowed the absolute limit" :
1557     "overflowed the original limit";
1558 
1559   PrintMsg("%-8s: ", lump->name);
1560 
1561   switch (limit)
1562   {
1563     case LIMIT_VERTEXES: PrintMsg("Number of Vertices %s.\n", msg); break;
1564     case LIMIT_SECTORS:  PrintMsg("Number of Sectors %s.\n", msg); break;
1565     case LIMIT_SIDEDEFS: PrintMsg("Number of Sidedefs %s\n", msg); break;
1566     case LIMIT_LINEDEFS: PrintMsg("Number of Linedefs\n", msg); break;
1567 
1568     case LIMIT_SEGS:     PrintMsg("Number of Segs %s.\n", msg); break;
1569     case LIMIT_SSECTORS: PrintMsg("Number of Subsectors %s.\n", msg); break;
1570     case LIMIT_NODES:    PrintMsg("Number of Nodes %s.\n", msg); break;
1571 
1572     case LIMIT_GL_VERT:  PrintMsg("Number of GL vertices %s.\n", msg); break;
1573     case LIMIT_GL_SEGS:  PrintMsg("Number of GL segs %s.\n", msg); break;
1574     case LIMIT_GL_SSECT: PrintMsg("Number of GL subsectors %s.\n", msg); break;
1575     case LIMIT_GL_NODES: PrintMsg("Number of GL nodes %s.\n", msg); break;
1576 
1577     case LIMIT_BAD_SIDE:   PrintMsg("One or more linedefs has a bad sidedef.\n"); break;
1578     case LIMIT_BMAP_TRUNC: PrintMsg("Blockmap area was too big - truncated.\n"); break;
1579     case LIMIT_BLOCKMAP:   PrintMsg("Blockmap lump %s.\n", msg); break;
1580 
1581     default:
1582       InternalError("UNKNOWN LIMIT BIT: 0x%06x", limit);
1583   }
1584 }
1585 
1586 // ReportOverflows
1587 //
ReportOverflows(boolean_g hard)1588 void ReportOverflows(boolean_g hard)
1589 {
1590   lump_t *cur;
1591 
1592   if (hard)
1593   {
1594     PrintMsg(
1595       "ERRORS.  The following levels failed to be built, and won't\n"
1596       "work in any Doom port (and may even crash it).\n\n"
1597     );
1598   }
1599   else  // soft
1600   {
1601     PrintMsg(
1602       "POTENTIAL FAILURES.  The following levels should work in a\n"
1603       "modern Doom port, but may fail (or even crash) in older ports.\n\n"
1604     );
1605   }
1606 
1607   for (cur=wad.dir_head; cur; cur=cur->next)
1608   {
1609     level_t *lev = cur->lev_info;
1610 
1611     int limits, one_lim;
1612 
1613     if (! (lev && ! (lev->flags & LEVEL_IS_GL)))
1614       continue;
1615 
1616     limits = hard ? lev->hard_limit : lev->soft_limit;
1617 
1618     if (limits == 0)
1619       continue;
1620 
1621     for (one_lim = (1<<20); one_lim; one_lim >>= 1)
1622     {
1623       if (limits & one_lim)
1624         ReportOneOverflow(cur, one_lim, hard);
1625     }
1626   }
1627 }
1628 
1629 //
1630 // ReportV5Switches
1631 //
ReportV5Switches(void)1632 void ReportV5Switches(void)
1633 {
1634   lump_t *cur;
1635   int saw_zdbsp = FALSE;
1636 
1637   PrintMsg(
1638     "V5 FORMAT UPGRADES.  The following levels require a Doom port\n"
1639     "which supports V5 GL-Nodes, otherwise they will fail (or crash).\n\n"
1640   );
1641 
1642   for (cur=wad.dir_head; cur; cur=cur->next)
1643   {
1644     level_t *lev = cur->lev_info;
1645 
1646     if (! (lev && ! (lev->flags & LEVEL_IS_GL)))
1647       continue;
1648 
1649     if (lev->v5_switch == 0)
1650       continue;
1651 
1652     if ((lev->v5_switch & LIMIT_ZDBSP) && ! saw_zdbsp)
1653     {
1654       PrintMsg("ZDBSP FORMAT has also been used for regular nodes.\n\n");
1655       saw_zdbsp = TRUE;
1656     }
1657 
1658     if (lev->v5_switch & LIMIT_VERTEXES)
1659     {
1660       PrintMsg("%-8s: Number of Vertices overflowed the limit.\n", cur->name);
1661     }
1662 
1663     if (lev->v5_switch & LIMIT_GL_SSECT)
1664     {
1665       PrintMsg("%-8s: Number of GL segs overflowed the limit.\n", cur->name);
1666     }
1667   }
1668 }
1669 
1670 //
1671 // ReportFailedLevels
1672 //
ReportFailedLevels(void)1673 void ReportFailedLevels(void)
1674 {
1675   lump_t *cur;
1676   int lev_count = 0;
1677 
1678   int fail_soft = 0;
1679   int fail_hard = 0;
1680   int fail_v5   = 0;
1681 
1682   boolean_g need_spacer = FALSE;
1683 
1684   for (cur=wad.dir_head; cur; cur=cur->next)
1685   {
1686     if (! (cur->lev_info && ! (cur->lev_info->flags & LEVEL_IS_GL)))
1687       continue;
1688 
1689     lev_count++;
1690 
1691     if (cur->lev_info->soft_limit != 0) fail_soft++;
1692     if (cur->lev_info->hard_limit != 0) fail_hard++;
1693     if (cur->lev_info->v5_switch  != 0) fail_v5++;
1694   }
1695 
1696   PrintMsg("\n");
1697 
1698   if (fail_soft + fail_hard + fail_v5 == 0)
1699   {
1700     PrintMsg("All levels were built successfully.\n");
1701     return;
1702   }
1703 
1704   PrintMsg("*** Problem Report ***\n\n");
1705 
1706   if (fail_soft > 0)
1707   {
1708     ReportOverflows(FALSE);
1709     need_spacer = TRUE;
1710   }
1711 
1712   if (fail_v5 > 0)
1713   {
1714     if (need_spacer)
1715       PrintMsg("\n");
1716 
1717     ReportV5Switches();
1718     need_spacer = TRUE;
1719   }
1720 
1721   if (fail_hard > 0)
1722   {
1723     if (need_spacer)
1724       PrintMsg("\n");
1725 
1726     ReportOverflows(TRUE);
1727   }
1728 
1729   PrintMsg("\nEnd of problem report.\n");
1730 }
1731 
1732