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