1 //------------------------------------------------------------------------
2 //
3 // AJ-BSP Copyright (C) 2000-2019 Andrew Apted, et al
4 // Copyright (C) 1994-1998 Colin Reed
5 // Copyright (C) 1997-1998 Lee Killough
6 //
7 // Originally based on the program 'BSP', version 2.3.
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 "main.h"
22 #include "bsp.h"
23
24 #include "w_rawdef.h"
25 #include "w_wad.h"
26
27 #include <zlib.h>
28
29
30 namespace ajbsp
31 {
32
33 #define DEBUG_BLOCKMAP 0
34 #define DEBUG_REJECT 0
35 #define DEBUG_LOAD 0
36 #define DEBUG_BSP 0
37
38
39 static int block_x, block_y;
40 static int block_w, block_h;
41 static int block_count;
42
43 static int block_mid_x = 0;
44 static int block_mid_y = 0;
45
46 static u16_t ** block_lines;
47
48 static u16_t *block_ptrs;
49 static u16_t *block_dups;
50
51 static int block_compression;
52 static int block_overflowed;
53
54 #define BLOCK_LIMIT 16000
55
56 #define DUMMY_DUP 0xFFFF
57
58
GetBlockmapBounds(int * x,int * y,int * w,int * h)59 void GetBlockmapBounds(int *x, int *y, int *w, int *h)
60 {
61 *x = block_x; *y = block_y;
62 *w = block_w; *h = block_h;
63 }
64
65
CheckLinedefInsideBox(int xmin,int ymin,int xmax,int ymax,int x1,int y1,int x2,int y2)66 int CheckLinedefInsideBox(int xmin, int ymin, int xmax, int ymax,
67 int x1, int y1, int x2, int y2)
68 {
69 int count = 2;
70 int tmp;
71
72 for (;;)
73 {
74 if (y1 > ymax)
75 {
76 if (y2 > ymax)
77 return false;
78
79 x1 = x1 + (int) ((x2-x1) * (double)(ymax-y1) / (double)(y2-y1));
80 y1 = ymax;
81
82 count = 2;
83 continue;
84 }
85
86 if (y1 < ymin)
87 {
88 if (y2 < ymin)
89 return false;
90
91 x1 = x1 + (int) ((x2-x1) * (double)(ymin-y1) / (double)(y2-y1));
92 y1 = ymin;
93
94 count = 2;
95 continue;
96 }
97
98 if (x1 > xmax)
99 {
100 if (x2 > xmax)
101 return false;
102
103 y1 = y1 + (int) ((y2-y1) * (double)(xmax-x1) / (double)(x2-x1));
104 x1 = xmax;
105
106 count = 2;
107 continue;
108 }
109
110 if (x1 < xmin)
111 {
112 if (x2 < xmin)
113 return false;
114
115 y1 = y1 + (int) ((y2-y1) * (double)(xmin-x1) / (double)(x2-x1));
116 x1 = xmin;
117
118 count = 2;
119 continue;
120 }
121
122 count--;
123
124 if (count == 0)
125 break;
126
127 // swap end points
128 tmp=x1; x1=x2; x2=tmp;
129 tmp=y1; y1=y2; y2=tmp;
130 }
131
132 // linedef touches block
133 return true;
134 }
135
136
137 /* ----- create blockmap ------------------------------------ */
138
139 #define BK_NUM 0
140 #define BK_MAX 1
141 #define BK_XOR 2
142 #define BK_FIRST 3
143
144 #define BK_QUANTUM 32
145
BlockAdd(int blk_num,int line_index)146 static void BlockAdd(int blk_num, int line_index)
147 {
148 u16_t *cur = block_lines[blk_num];
149
150 # if DEBUG_BLOCKMAP
151 DebugPrintf("Block %d has line %d\n", blk_num, line_index);
152 # endif
153
154 if (blk_num < 0 || blk_num >= block_count)
155 BugError("BlockAdd: bad block number %d\n", blk_num);
156
157 if (! cur)
158 {
159 // create empty block
160 block_lines[blk_num] = cur = (u16_t *)UtilCalloc(BK_QUANTUM * sizeof(u16_t));
161 cur[BK_NUM] = 0;
162 cur[BK_MAX] = BK_QUANTUM;
163 cur[BK_XOR] = 0x1234;
164 }
165
166 if (BK_FIRST + cur[BK_NUM] == cur[BK_MAX])
167 {
168 // no more room, so allocate some more...
169 cur[BK_MAX] += BK_QUANTUM;
170
171 block_lines[blk_num] = cur = (u16_t *)UtilRealloc(cur, cur[BK_MAX] * sizeof(u16_t));
172 }
173
174 // compute new checksum
175 cur[BK_XOR] = ((cur[BK_XOR] << 4) | (cur[BK_XOR] >> 12)) ^ line_index;
176
177 cur[BK_FIRST + cur[BK_NUM]] = LE_U16(line_index);
178 cur[BK_NUM]++;
179 }
180
181
BlockAddLine(int line_index)182 static void BlockAddLine(int line_index)
183 {
184 const LineDef *L = LineDefs[line_index];
185
186 int x1 = (int) L->Start()->x();
187 int y1 = (int) L->Start()->y();
188 int x2 = (int) L->End()->x();
189 int y2 = (int) L->End()->y();
190
191 int bx1 = (MIN(x1,x2) - block_x) / 128;
192 int by1 = (MIN(y1,y2) - block_y) / 128;
193 int bx2 = (MAX(x1,x2) - block_x) / 128;
194 int by2 = (MAX(y1,y2) - block_y) / 128;
195
196 int bx, by;
197
198 # if DEBUG_BLOCKMAP
199 DebugPrintf("BlockAddLine: %d (%d,%d) -> (%d,%d)\n", line_index,
200 x1, y1, x2, y2);
201 # endif
202
203 // handle truncated blockmaps
204 if (bx1 < 0) bx1 = 0;
205 if (by1 < 0) by1 = 0;
206 if (bx2 >= block_w) bx2 = block_w - 1;
207 if (by2 >= block_h) by2 = block_h - 1;
208
209 if (bx2 < bx1 || by2 < by1)
210 return;
211
212 // handle simple case #1: completely horizontal
213 if (by1 == by2)
214 {
215 for (bx=bx1 ; bx <= bx2 ; bx++)
216 {
217 int blk_num = by1 * block_w + bx;
218 BlockAdd(blk_num, line_index);
219 }
220 return;
221 }
222
223 // handle simple case #2: completely vertical
224 if (bx1 == bx2)
225 {
226 for (by=by1 ; by <= by2 ; by++)
227 {
228 int blk_num = by * block_w + bx1;
229 BlockAdd(blk_num, line_index);
230 }
231 return;
232 }
233
234 // handle the rest (diagonals)
235
236 for (by=by1 ; by <= by2 ; by++)
237 for (bx=bx1 ; bx <= bx2 ; bx++)
238 {
239 int blk_num = by * block_w + bx;
240
241 int minx = block_x + bx * 128;
242 int miny = block_y + by * 128;
243 int maxx = minx + 127;
244 int maxy = miny + 127;
245
246 if (CheckLinedefInsideBox(minx, miny, maxx, maxy, x1, y1, x2, y2))
247 {
248 BlockAdd(blk_num, line_index);
249 }
250 }
251 }
252
253
CreateBlockmap()254 static void CreateBlockmap()
255 {
256 block_lines = (u16_t **) UtilCalloc(block_count * sizeof(u16_t *));
257
258 for (int i=0 ; i < NumLineDefs ; i++)
259 {
260 // ignore zero-length lines
261 if (LineDefs[i]->IsZeroLength())
262 continue;
263
264 BlockAddLine(i);
265 }
266 }
267
268
BlockCompare(const void * p1,const void * p2)269 static int BlockCompare(const void *p1, const void *p2)
270 {
271 int blk_num1 = ((const u16_t *) p1)[0];
272 int blk_num2 = ((const u16_t *) p2)[0];
273
274 const u16_t *A = block_lines[blk_num1];
275 const u16_t *B = block_lines[blk_num2];
276
277 if (A == B)
278 return 0;
279
280 if (A == NULL) return -1;
281 if (B == NULL) return +1;
282
283 if (A[BK_NUM] != B[BK_NUM])
284 {
285 return A[BK_NUM] - B[BK_NUM];
286 }
287
288 if (A[BK_XOR] != B[BK_XOR])
289 {
290 return A[BK_XOR] - B[BK_XOR];
291 }
292
293 return memcmp(A+BK_FIRST, B+BK_FIRST, A[BK_NUM] * sizeof(u16_t));
294 }
295
296
CompressBlockmap(void)297 static void CompressBlockmap(void)
298 {
299 int i;
300 int cur_offset;
301 int dup_count=0;
302
303 int orig_size, new_size;
304
305 block_ptrs = (u16_t *)UtilCalloc(block_count * sizeof(u16_t));
306 block_dups = (u16_t *)UtilCalloc(block_count * sizeof(u16_t));
307
308 // sort duplicate-detecting array. After the sort, all duplicates
309 // will be next to each other. The duplicate array gives the order
310 // of the blocklists in the BLOCKMAP lump.
311
312 for (i=0 ; i < block_count ; i++)
313 block_dups[i] = i;
314
315 qsort(block_dups, block_count, sizeof(u16_t), BlockCompare);
316
317 // scan duplicate array and build up offset array
318
319 cur_offset = 4 + block_count + 2;
320
321 orig_size = 4 + block_count;
322 new_size = cur_offset;
323
324 for (i=0 ; i < block_count ; i++)
325 {
326 int blk_num = block_dups[i];
327 int count;
328
329 // empty block ?
330 if (block_lines[blk_num] == NULL)
331 {
332 block_ptrs[blk_num] = 4 + block_count;
333 block_dups[i] = DUMMY_DUP;
334
335 orig_size += 2;
336 continue;
337 }
338
339 count = 2 + block_lines[blk_num][BK_NUM];
340
341 // duplicate ? Only the very last one of a sequence of duplicates
342 // will update the current offset value.
343
344 if (i+1 < block_count &&
345 BlockCompare(block_dups + i, block_dups + i+1) == 0)
346 {
347 block_ptrs[blk_num] = cur_offset;
348 block_dups[i] = DUMMY_DUP;
349
350 // free the memory of the duplicated block
351 UtilFree(block_lines[blk_num]);
352 block_lines[blk_num] = NULL;
353
354 dup_count++;
355
356 orig_size += count;
357 continue;
358 }
359
360 // OK, this block is either the last of a series of duplicates, or
361 // just a singleton.
362
363 block_ptrs[blk_num] = cur_offset;
364
365 cur_offset += count;
366
367 orig_size += count;
368 new_size += count;
369 }
370
371 if (cur_offset > 65535)
372 {
373 block_overflowed = true;
374 return;
375 }
376
377 # if DEBUG_BLOCKMAP
378 DebugPrintf("Blockmap: Last ptr = %d duplicates = %d\n",
379 cur_offset, dup_count);
380 # endif
381
382 block_compression = (orig_size - new_size) * 100 / orig_size;
383
384 // there's a tiny chance of new_size > orig_size
385 if (block_compression < 0)
386 block_compression = 0;
387 }
388
389
CalcBlockmapSize()390 static int CalcBlockmapSize()
391 {
392 // compute size of final BLOCKMAP lump.
393 // it does not need to be exact, but it *does* need to be bigger
394 // (or equal) to the actual size of the lump.
395
396 // header + null_block + a bit extra
397 int size = 20;
398
399 // the pointers (offsets to the line lists)
400 size = size + block_count * 2;
401
402 // add size of each block
403 for (int i=0 ; i < block_count ; i++)
404 {
405 int blk_num = block_dups[i];
406
407 // ignore duplicate or empty blocks
408 if (blk_num == DUMMY_DUP)
409 continue;
410
411 u16_t *blk = block_lines[blk_num];
412 SYS_ASSERT(blk);
413
414 size += (1 + (int)(blk[BK_NUM]) + 1) * 2;
415 }
416
417 return size;
418 }
419
420
WriteBlockmap(void)421 static void WriteBlockmap(void)
422 {
423 int i;
424
425 int max_size = CalcBlockmapSize();
426
427 Lump_c *lump = CreateLevelLump("BLOCKMAP", max_size);
428
429 u16_t null_block[2] = { 0x0000, 0xFFFF };
430 u16_t m_zero = 0x0000;
431 u16_t m_neg1 = 0xFFFF;
432
433 // fill in header
434 raw_blockmap_header_t header;
435
436 header.x_origin = LE_U16(block_x);
437 header.y_origin = LE_U16(block_y);
438 header.x_blocks = LE_U16(block_w);
439 header.y_blocks = LE_U16(block_h);
440
441 lump->Write(&header, sizeof(header));
442
443 // handle pointers
444 for (i=0 ; i < block_count ; i++)
445 {
446 u16_t ptr = LE_U16(block_ptrs[i]);
447
448 if (ptr == 0)
449 BugError("WriteBlockmap: offset %d not set.\n", i);
450
451 lump->Write(&ptr, sizeof(u16_t));
452 }
453
454 // add the null block which *all* empty blocks will use
455 lump->Write(null_block, sizeof(null_block));
456
457 // handle each block list
458 for (i=0 ; i < block_count ; i++)
459 {
460 int blk_num = block_dups[i];
461
462 // ignore duplicate or empty blocks
463 if (blk_num == DUMMY_DUP)
464 continue;
465
466 u16_t *blk = block_lines[blk_num];
467 SYS_ASSERT(blk);
468
469 lump->Write(&m_zero, sizeof(u16_t));
470 lump->Write(blk + BK_FIRST, blk[BK_NUM] * sizeof(u16_t));
471 lump->Write(&m_neg1, sizeof(u16_t));
472 }
473
474 lump->Finish();
475 }
476
477
FreeBlockmap(void)478 static void FreeBlockmap(void)
479 {
480 for (int i=0 ; i < block_count ; i++)
481 {
482 if (block_lines[i])
483 UtilFree(block_lines[i]);
484 }
485
486 UtilFree(block_lines);
487 UtilFree(block_ptrs);
488 UtilFree(block_dups);
489 }
490
491
FindBlockmapLimits(bbox_t * bbox)492 static void FindBlockmapLimits(bbox_t *bbox)
493 {
494 int mid_x = 0;
495 int mid_y = 0;
496
497 bbox->minx = bbox->miny = SHRT_MAX;
498 bbox->maxx = bbox->maxy = SHRT_MIN;
499
500 for (int i=0 ; i < NumLineDefs ; i++)
501 {
502 const LineDef *L = LineDefs[i];
503
504 if (! L->IsZeroLength())
505 {
506 double x1 = L->Start()->x();
507 double y1 = L->Start()->y();
508 double x2 = L->End()->x();
509 double y2 = L->End()->y();
510
511 int lx = (int)floor(MIN(x1, x2));
512 int ly = (int)floor(MIN(y1, y2));
513 int hx = (int)ceil(MAX(x1, x2));
514 int hy = (int)ceil(MAX(y1, y2));
515
516 if (lx < bbox->minx) bbox->minx = lx;
517 if (ly < bbox->miny) bbox->miny = ly;
518 if (hx > bbox->maxx) bbox->maxx = hx;
519 if (hy > bbox->maxy) bbox->maxy = hy;
520
521 // compute middle of cluster (roughly, so we don't overflow)
522 mid_x += (lx + hx) / 32;
523 mid_y += (ly + hy) / 32;
524 }
525 }
526
527 if (NumLineDefs > 0)
528 {
529 block_mid_x = (mid_x / NumLineDefs) * 16;
530 block_mid_y = (mid_y / NumLineDefs) * 16;
531 }
532
533 # if DEBUG_BLOCKMAP
534 DebugPrintf("Blockmap lines centered at (%d,%d)\n", block_mid_x, block_mid_y);
535 # endif
536 }
537
538
InitBlockmap()539 void InitBlockmap()
540 {
541 bbox_t map_bbox;
542
543 // find limits of linedefs, and store as map limits
544 FindBlockmapLimits(&map_bbox);
545
546 PrintDetail("Map goes from (%d,%d) to (%d,%d)\n",
547 map_bbox.minx, map_bbox.miny, map_bbox.maxx, map_bbox.maxy);
548
549 block_x = map_bbox.minx - (map_bbox.minx & 0x7);
550 block_y = map_bbox.miny - (map_bbox.miny & 0x7);
551
552 block_w = ((map_bbox.maxx - block_x) / 128) + 1;
553 block_h = ((map_bbox.maxy - block_y) / 128) + 1;
554
555 block_count = block_w * block_h;
556 }
557
558
PutBlockmap()559 void PutBlockmap()
560 {
561 if (! cur_info->do_blockmap || NumLineDefs == 0)
562 {
563 // just create an empty blockmap lump
564 CreateLevelLump("BLOCKMAP")->Finish();
565 return;
566 }
567
568 block_overflowed = false;
569
570 // initial phase: create internal blockmap containing the index of
571 // all lines in each block.
572
573 CreateBlockmap();
574
575 // -AJA- second phase: compress the blockmap. We do this by sorting
576 // the blocks, which is a typical way to detect duplicates in
577 // a large list. This also detects BLOCKMAP overflow.
578
579 CompressBlockmap();
580
581 // final phase: write it out in the correct format
582
583 if (block_overflowed)
584 {
585 // leave an empty blockmap lump
586 CreateLevelLump("BLOCKMAP")->Finish();
587
588 Warning("Blockmap overflowed (lump will be empty)\n");
589 }
590 else
591 {
592 WriteBlockmap();
593
594 PrintDetail("Completed blockmap, size %dx%d (compression: %d%%)\n",
595 block_w, block_h, block_compression);
596 }
597
598 FreeBlockmap();
599 }
600
601
602 //------------------------------------------------------------------------
603 // REJECT : Generate the reject table
604 //------------------------------------------------------------------------
605
606
607 static u8_t *rej_matrix;
608 static int rej_total_size; // in bytes
609
610 static std::vector<int> rej_sector_groups;
611
612
613 //
614 // Allocate the matrix, init sectors into individual groups.
615 //
Reject_Init()616 static void Reject_Init()
617 {
618 rej_total_size = (NumSectors * NumSectors + 7) / 8;
619
620 rej_matrix = new u8_t[rej_total_size];
621 memset(rej_matrix, 0, rej_total_size);
622
623 rej_sector_groups.resize(NumSectors);
624
625 for (int i=0 ; i < NumSectors ; i++)
626 {
627 rej_sector_groups[i] = i;
628 }
629 }
630
631
Reject_Free()632 static void Reject_Free()
633 {
634 delete[] rej_matrix;
635 rej_matrix = NULL;
636
637 rej_sector_groups.clear();
638 }
639
640
641 //
642 // Algorithm: Initially all sectors are in individual groups.
643 // Now we scan the linedef list. For each two-sectored line,
644 // merge the two sector groups into one. That's it!
645 //
Reject_GroupSectors()646 static void Reject_GroupSectors()
647 {
648 for (int i=0 ; i < NumLineDefs ; i++)
649 {
650 const LineDef *L = LineDefs[i];
651
652 if (L->right < 0 || L->left < 0)
653 continue;
654
655 int sec1 = L->Right()->sector;
656 int sec2 = L->Left() ->sector;
657
658 if (sec1 < 0 || sec2 < 0 || sec1 == sec2)
659 continue;
660
661 // already in the same group ?
662 int group1 = rej_sector_groups[sec1];
663 int group2 = rej_sector_groups[sec2];
664
665 if (group1 == group2)
666 continue;
667
668 // prefer the group numbers to become lower
669 if (group1 > group2)
670 std::swap(group1, group2);
671
672 // merge the groups
673 for (int s = 0 ; s < NumSectors ; s++)
674 if (rej_sector_groups[s] == group2)
675 rej_sector_groups[s] = group1;
676 }
677 }
678
679
680 #if DEBUG_REJECT
Reject_DebugGroups()681 static void Reject_DebugGroups()
682 {
683 // Note: this routine is destructive to the group numbers
684
685 for (int i=0 ; i < NumSectors ; i++)
686 {
687 int group = rej_sector_groups[i];
688 int count = 0;
689
690 if (group < 0)
691 continue;
692
693 for (int k = i ; k < NumSectors ; k++)
694 {
695 if (rej_sector_groups[k] == group)
696 {
697 rej_sector_groups[k] = -1;
698 count++;
699 }
700 }
701
702 DebugPrintf("Group %4d : Sectors %d\n", group, count);
703 }
704 }
705 #endif
706
707
Reject_ProcessSectors()708 static void Reject_ProcessSectors()
709 {
710 for (int view=0 ; view < NumSectors ; view++)
711 {
712 for (int target=0 ; target < view ; target++)
713 {
714 if (rej_sector_groups[view] == rej_sector_groups[target])
715 continue;
716
717 int p1 = view * NumSectors + target;
718 int p2 = target * NumSectors + view;
719
720 // must do both directions at same time
721 rej_matrix[p1 >> 3] |= (1 << (p1 & 7));
722 rej_matrix[p2 >> 3] |= (1 << (p2 & 7));
723 }
724 }
725 }
726
727
Reject_WriteLump()728 static void Reject_WriteLump()
729 {
730 Lump_c *lump = CreateLevelLump("REJECT", rej_total_size);
731
732 lump->Write(rej_matrix, rej_total_size);
733
734 lump->Finish();
735 }
736
737
738 //
739 // For now we only do very basic reject processing, limited to
740 // determining all isolated groups of sectors (islands that are
741 // surrounded by void space).
742 //
PutReject()743 void PutReject()
744 {
745 if (! cur_info->do_reject || NumSectors == 0)
746 {
747 // just create an empty reject lump
748 CreateLevelLump("REJECT")->Finish();
749 return;
750 }
751
752 Reject_Init();
753 Reject_GroupSectors();
754 Reject_ProcessSectors();
755
756 # if DEBUG_REJECT
757 Reject_DebugGroups();
758 # endif
759
760 Reject_WriteLump();
761 Reject_Free();
762
763 PrintDetail("Added simple reject lump\n");
764 }
765
766
767 //------------------------------------------------------------------------
768 // LEVEL : Level structure read/write functions.
769 //------------------------------------------------------------------------
770
771
772 // Note: ZDoom format support based on code (C) 2002,2003 Randy Heit
773
774
775 #define ALLOC_BLKNUM 1024
776
777
778 // per-level variables
779
780 const char *lev_current_name;
781
782 short lev_current_idx;
783 short lev_current_start;
784
785 int lev_overflows;
786
787
788 std::vector<vertex_t *> lev_vertices;
789 std::vector<seg_t *> lev_segs;
790 std::vector<subsec_t *> lev_subsecs;
791 std::vector<node_t *> lev_nodes;
792 std::vector<walltip_t *> lev_walltips;
793
794 int num_old_vert = 0;
795 int num_new_vert = 0;
796 int num_real_lines = 0;
797
798
799 /* ----- allocation routines ---------------------------- */
800
NewVertex()801 vertex_t *NewVertex()
802 {
803 vertex_t *V = (vertex_t *) UtilCalloc(sizeof(vertex_t));
804 lev_vertices.push_back(V);
805 return V;
806 }
807
NewSeg()808 seg_t *NewSeg()
809 {
810 seg_t *S = (seg_t *) UtilCalloc(sizeof(seg_t));
811 lev_segs.push_back(S);
812 return S;
813 }
814
NewSubsec()815 subsec_t *NewSubsec()
816 {
817 subsec_t *S = (subsec_t *) UtilCalloc(sizeof(subsec_t));
818 lev_subsecs.push_back(S);
819 return S;
820 }
821
NewNode()822 node_t *NewNode()
823 {
824 node_t *N = (node_t *) UtilCalloc(sizeof(node_t));
825 lev_nodes.push_back(N);
826 return N;
827 }
828
NewWallTip()829 walltip_t *NewWallTip()
830 {
831 walltip_t *WT = (walltip_t *) UtilCalloc(sizeof(walltip_t));
832 lev_walltips.push_back(WT);
833 return WT;
834 }
835
836
837 /* ----- free routines ---------------------------- */
838
FreeVertices()839 void FreeVertices()
840 {
841 for (unsigned int i = 0 ; i < lev_vertices.size() ; i++)
842 UtilFree((void *) lev_vertices[i]);
843
844 lev_vertices.clear();
845 }
846
FreeSegs()847 void FreeSegs()
848 {
849 for (unsigned int i = 0 ; i < lev_segs.size() ; i++)
850 UtilFree((void *) lev_segs[i]);
851
852 lev_segs.clear();
853 }
854
FreeSubsecs()855 void FreeSubsecs()
856 {
857 for (unsigned int i = 0 ; i < lev_subsecs.size() ; i++)
858 UtilFree((void *) lev_subsecs[i]);
859
860 lev_subsecs.clear();
861 }
862
FreeNodes()863 void FreeNodes()
864 {
865 for (unsigned int i = 0 ; i < lev_nodes.size() ; i++)
866 UtilFree((void *) lev_nodes[i]);
867
868 lev_nodes.clear();
869 }
870
FreeWallTips()871 void FreeWallTips()
872 {
873 for (unsigned int i = 0 ; i < lev_walltips.size() ; i++)
874 UtilFree((void *) lev_walltips[i]);
875
876 lev_walltips.clear();
877 }
878
879
880 /* ----- reading routines ------------------------------ */
881
GetVertices(void)882 static void GetVertices(void)
883 {
884 for (int i = 0 ; i < NumVertices ; i++)
885 {
886 vertex_t *vert = NewVertex();
887
888 vert->x = Vertices[i]->x();
889 vert->y = Vertices[i]->y();
890
891 vert->index = i;
892 }
893
894 num_old_vert = num_vertices;
895 }
896
897
898 #if 0
899 static inline SideDef *SafeLookupSidedef(u16_t num)
900 {
901 if (num == 0xFFFF)
902 return NULL;
903
904 if ((int)num >= NumSideDefs && (s16_t)(num) < 0)
905 return NULL;
906
907 return SideDefs[num];
908 }
909 #endif
910
911
VanillaSegDist(const seg_t * seg)912 static inline int VanillaSegDist(const seg_t *seg)
913 {
914 const LineDef *L = LineDefs[seg->linedef];
915
916 double lx = seg->side ? L->End()->x() : L->Start()->x();
917 double ly = seg->side ? L->End()->y() : L->Start()->y();
918
919 // use the "true" starting coord (as stored in the wad)
920 double sx = I_ROUND(seg->start->x);
921 double sy = I_ROUND(seg->start->y);
922
923 return (int) floor(hypot(sx - lx, sy - ly) + 0.5);
924 }
925
VanillaSegAngle(const seg_t * seg)926 static inline int VanillaSegAngle(const seg_t *seg)
927 {
928 // compute the "true" delta
929 double dx = I_ROUND(seg->end->x) - I_ROUND(seg->start->x);
930 double dy = I_ROUND(seg->end->y) - I_ROUND(seg->start->y);
931
932 double angle = UtilComputeAngle(dx, dy);
933
934 if (angle < 0)
935 angle += 360.0;
936
937 int result = (int) floor(angle * 65536.0 / 360.0 + 0.5);
938
939 return (result & 0xFFFF);
940 }
941
942
943 /* ----- writing routines ------------------------------ */
944
945 static const u8_t *lev_v2_magic = (u8_t *) "gNd2";
946 static const u8_t *lev_v5_magic = (u8_t *) "gNd5";
947
948
MarkOverflow(int flags)949 void MarkOverflow(int flags)
950 {
951 // flags are ignored
952
953 lev_overflows++;
954 }
955
956
PutVertices(const char * name,int do_gl)957 void PutVertices(const char *name, int do_gl)
958 {
959 int count, i;
960
961 // this size is worst-case scenario
962 int size = num_vertices * (int)sizeof(raw_vertex_t);
963
964 Lump_c *lump = CreateLevelLump(name, size);
965
966 for (i=0, count=0 ; i < num_vertices ; i++)
967 {
968 raw_vertex_t raw;
969
970 vertex_t *vert = lev_vertices[i];
971
972 if ((do_gl ? 1 : 0) != (vert->is_new ? 1 : 0))
973 {
974 continue;
975 }
976
977 raw.x = LE_S16(I_ROUND(vert->x));
978 raw.y = LE_S16(I_ROUND(vert->y));
979
980 lump->Write(&raw, sizeof(raw));
981
982 count++;
983 }
984
985 if (count != (do_gl ? num_new_vert : num_old_vert))
986 BugError("PutVertices miscounted (%d != %d)\n", count,
987 do_gl ? num_new_vert : num_old_vert);
988
989 if (! do_gl && count > 65534)
990 {
991 Failure("Number of vertices has overflowed.\n");
992 MarkOverflow(LIMIT_VERTEXES);
993 }
994 }
995
996
PutGLVertices(int do_v5)997 void PutGLVertices(int do_v5)
998 {
999 int count, i;
1000
1001 // this size is worst-case scenario
1002 int size = 4 + num_vertices * (int)sizeof(raw_v2_vertex_t);
1003
1004 Lump_c *lump = CreateLevelLump("GL_VERT", size);
1005
1006 if (do_v5)
1007 lump->Write(lev_v5_magic, 4);
1008 else
1009 lump->Write(lev_v2_magic, 4);
1010
1011 for (i=0, count=0 ; i < num_vertices ; i++)
1012 {
1013 raw_v2_vertex_t raw;
1014
1015 vertex_t *vert = lev_vertices[i];
1016
1017 if (! vert->is_new)
1018 continue;
1019
1020 raw.x = LE_S32((int)(vert->x * 65536.0));
1021 raw.y = LE_S32((int)(vert->y * 65536.0));
1022
1023 lump->Write(&raw, sizeof(raw));
1024
1025 count++;
1026 }
1027
1028 if (count != num_new_vert)
1029 BugError("PutGLVertices miscounted (%d != %d)\n", count, num_new_vert);
1030 }
1031
1032
VertexIndex16Bit(const vertex_t * v)1033 static inline u16_t VertexIndex16Bit(const vertex_t *v)
1034 {
1035 if (v->is_new)
1036 return (u16_t) (v->index | 0x8000U);
1037
1038 return (u16_t) v->index;
1039 }
1040
1041
VertexIndex_V5(const vertex_t * v)1042 static inline u32_t VertexIndex_V5(const vertex_t *v)
1043 {
1044 if (v->is_new)
1045 return (u32_t) (v->index | 0x80000000U);
1046
1047 return (u32_t) v->index;
1048 }
1049
1050
VertexIndex_XNOD(const vertex_t * v)1051 static inline u32_t VertexIndex_XNOD(const vertex_t *v)
1052 {
1053 if (v->is_new)
1054 return (u32_t) (num_old_vert + v->index);
1055
1056 return (u32_t) v->index;
1057 }
1058
1059
PutSegs(void)1060 void PutSegs(void)
1061 {
1062 int i, count;
1063
1064 // this size is worst-case scenario
1065 int size = num_segs * (int)sizeof(raw_seg_t);
1066
1067 Lump_c *lump = CreateLevelLump("SEGS", size);
1068
1069 for (i=0, count=0 ; i < num_segs ; i++)
1070 {
1071 raw_seg_t raw;
1072
1073 seg_t *seg = lev_segs[i];
1074
1075 raw.start = LE_U16(VertexIndex16Bit(seg->start));
1076 raw.end = LE_U16(VertexIndex16Bit(seg->end));
1077 raw.angle = LE_U16(VanillaSegAngle(seg));
1078 raw.linedef = LE_U16(seg->linedef);
1079 raw.flip = LE_U16(seg->side);
1080 raw.dist = LE_U16(VanillaSegDist(seg));
1081
1082 lump->Write(&raw, sizeof(raw));
1083
1084 count++;
1085
1086 # if DEBUG_BSP
1087 DebugPrintf("PUT SEG: %04X Vert %04X->%04X Line %04X %s "
1088 "Angle %04X (%1.1f,%1.1f) -> (%1.1f,%1.1f)\n", seg->index,
1089 LE_U16(raw.start), LE_U16(raw.end), LE_U16(raw.linedef),
1090 seg->side ? "L" : "R", LE_U16(raw.angle),
1091 seg->start->x, seg->start->y, seg->end->x, seg->end->y);
1092 # endif
1093 }
1094
1095 if (count != num_segs)
1096 BugError("PutSegs miscounted (%d != %d)\n", count, num_segs);
1097
1098 if (count > 65534)
1099 {
1100 Failure("Number of segs has overflowed.\n");
1101 MarkOverflow(LIMIT_SEGS);
1102 }
1103 }
1104
1105
PutGLSegs(void)1106 void PutGLSegs(void)
1107 {
1108 int i, count;
1109
1110 // this size is worst-case scenario
1111 int size = num_segs * (int)sizeof(raw_gl_seg_t);
1112
1113 Lump_c *lump = CreateLevelLump("GL_SEGS", size);
1114
1115 for (i=0, count=0 ; i < num_segs ; i++)
1116 {
1117 raw_gl_seg_t raw;
1118
1119 seg_t *seg = lev_segs[i];
1120
1121 raw.start = LE_U16(VertexIndex16Bit(seg->start));
1122 raw.end = LE_U16(VertexIndex16Bit(seg->end));
1123 raw.side = LE_U16(seg->side);
1124
1125 if (seg->linedef >= 0)
1126 raw.linedef = LE_U16(seg->linedef);
1127 else
1128 raw.linedef = LE_U16(0xFFFF);
1129
1130 if (seg->partner)
1131 raw.partner = LE_U16(seg->partner->index);
1132 else
1133 raw.partner = LE_U16(0xFFFF);
1134
1135 lump->Write(&raw, sizeof(raw));
1136
1137 count++;
1138
1139 # if DEBUG_BSP
1140 DebugPrintf("PUT GL SEG: %04X Line %04X %s Partner %04X "
1141 "(%1.1f,%1.1f) -> (%1.1f,%1.1f)\n", seg->index, LE_U16(raw.linedef),
1142 seg->side ? "L" : "R", LE_U16(raw.partner),
1143 seg->start->x, seg->start->y, seg->end->x, seg->end->y);
1144 # endif
1145 }
1146
1147 if (count != num_segs)
1148 BugError("PutGLSegs miscounted (%d != %d)\n", count, num_segs);
1149
1150 if (count > 65534)
1151 BugError("PutGLSegs with %d (> 65534) segs\n", count);
1152 }
1153
1154
PutGLSegs_V5()1155 void PutGLSegs_V5()
1156 {
1157 int i, count;
1158
1159 // this size is worst-case scenario
1160 int size = num_segs * (int)sizeof(raw_v5_seg_t);
1161
1162 Lump_c *lump = CreateLevelLump("GL_SEGS", size);
1163
1164 for (i=0, count=0 ; i < num_segs ; i++)
1165 {
1166 raw_v5_seg_t raw;
1167
1168 seg_t *seg = lev_segs[i];
1169
1170 raw.start = LE_U32(VertexIndex_V5(seg->start));
1171 raw.end = LE_U32(VertexIndex_V5(seg->end));
1172
1173 raw.side = LE_U16(seg->side);
1174
1175 if (seg->linedef >= 0)
1176 raw.linedef = LE_U16(seg->linedef);
1177 else
1178 raw.linedef = LE_U16(0xFFFF);
1179
1180 if (seg->partner)
1181 raw.partner = LE_U32(seg->partner->index);
1182 else
1183 raw.partner = LE_U32(0xFFFFFFFF);
1184
1185 lump->Write(&raw, sizeof(raw));
1186
1187 count++;
1188
1189 # if DEBUG_BSP
1190 DebugPrintf("PUT V3 SEG: %06X Line %04X %s Partner %06X "
1191 "(%1.1f,%1.1f) -> (%1.1f,%1.1f)\n", seg->index, LE_U16(raw.linedef),
1192 seg->side ? "L" : "R", LE_U32(raw.partner),
1193 seg->start->x, seg->start->y, seg->end->x, seg->end->y);
1194 # endif
1195 }
1196
1197 if (count != num_segs)
1198 BugError("PutGLSegs miscounted (%d != %d)\n", count, num_segs);
1199 }
1200
1201
PutSubsecs(const char * name,int do_gl)1202 void PutSubsecs(const char *name, int do_gl)
1203 {
1204 int i;
1205
1206 int size = num_subsecs * (int)sizeof(raw_subsec_t);
1207
1208 Lump_c * lump = CreateLevelLump(name, size);
1209
1210 for (i=0 ; i < num_subsecs ; i++)
1211 {
1212 raw_subsec_t raw;
1213
1214 subsec_t *sub = lev_subsecs[i];
1215
1216 raw.first = LE_U16(sub->seg_list->index);
1217 raw.num = LE_U16(sub->seg_count);
1218
1219 lump->Write(&raw, sizeof(raw));
1220
1221 # if DEBUG_BSP
1222 DebugPrintf("PUT SUBSEC %04X First %04X Num %04X\n",
1223 sub->index, LE_U16(raw.first), LE_U16(raw.num));
1224 # endif
1225 }
1226
1227 if (num_subsecs > 32767)
1228 {
1229 Failure("Number of %s has overflowed.\n", do_gl ? "GL subsectors" : "subsectors");
1230 MarkOverflow(do_gl ? LIMIT_GL_SSECT : LIMIT_SSECTORS);
1231 }
1232 }
1233
1234
PutGLSubsecs_V5()1235 void PutGLSubsecs_V5()
1236 {
1237 int i;
1238
1239 int size = num_subsecs * (int)sizeof(raw_v5_subsec_t);
1240
1241 Lump_c *lump = CreateLevelLump("GL_SSECT", size);
1242
1243 for (i=0 ; i < num_subsecs ; i++)
1244 {
1245 raw_v5_subsec_t raw;
1246
1247 subsec_t *sub = lev_subsecs[i];
1248
1249 raw.first = LE_U32(sub->seg_list->index);
1250 raw.num = LE_U32(sub->seg_count);
1251
1252 lump->Write(&raw, sizeof(raw));
1253
1254 # if DEBUG_BSP
1255 DebugPrintf("PUT V3 SUBSEC %06X First %06X Num %06X\n",
1256 sub->index, LE_U32(raw.first), LE_U32(raw.num));
1257 # endif
1258 }
1259 }
1260
1261
1262 static int node_cur_index;
1263
PutOneNode(node_t * node,Lump_c * lump)1264 static void PutOneNode(node_t *node, Lump_c *lump)
1265 {
1266 raw_node_t raw;
1267
1268 if (node->r.node)
1269 PutOneNode(node->r.node, lump);
1270
1271 if (node->l.node)
1272 PutOneNode(node->l.node, lump);
1273
1274 node->index = node_cur_index++;
1275
1276 // Note that x/y/dx/dy are always integral in non-UDMF maps
1277 raw.x = LE_S16(I_ROUND(node->x));
1278 raw.y = LE_S16(I_ROUND(node->y));
1279 raw.dx = LE_S16(I_ROUND(node->dx));
1280 raw.dy = LE_S16(I_ROUND(node->dy));
1281
1282 raw.b1.minx = LE_S16(node->r.bounds.minx);
1283 raw.b1.miny = LE_S16(node->r.bounds.miny);
1284 raw.b1.maxx = LE_S16(node->r.bounds.maxx);
1285 raw.b1.maxy = LE_S16(node->r.bounds.maxy);
1286
1287 raw.b2.minx = LE_S16(node->l.bounds.minx);
1288 raw.b2.miny = LE_S16(node->l.bounds.miny);
1289 raw.b2.maxx = LE_S16(node->l.bounds.maxx);
1290 raw.b2.maxy = LE_S16(node->l.bounds.maxy);
1291
1292 if (node->r.node)
1293 raw.right = LE_U16(node->r.node->index);
1294 else if (node->r.subsec)
1295 raw.right = LE_U16(node->r.subsec->index | 0x8000);
1296 else
1297 BugError("Bad right child in node %d\n", node->index);
1298
1299 if (node->l.node)
1300 raw.left = LE_U16(node->l.node->index);
1301 else if (node->l.subsec)
1302 raw.left = LE_U16(node->l.subsec->index | 0x8000);
1303 else
1304 BugError("Bad left child in node %d\n", node->index);
1305
1306 lump->Write(&raw, sizeof(raw));
1307
1308 # if DEBUG_BSP
1309 DebugPrintf("PUT NODE %04X Left %04X Right %04X "
1310 "(%d,%d) -> (%d,%d)\n", node->index, LE_U16(raw.left),
1311 LE_U16(raw.right), node->x, node->y,
1312 node->x + node->dx, node->y + node->dy);
1313 # endif
1314 }
1315
1316
PutOneNode_V5(node_t * node,Lump_c * lump)1317 static void PutOneNode_V5(node_t *node, Lump_c *lump)
1318 {
1319 raw_v5_node_t raw;
1320
1321 if (node->r.node)
1322 PutOneNode_V5(node->r.node, lump);
1323
1324 if (node->l.node)
1325 PutOneNode_V5(node->l.node, lump);
1326
1327 node->index = node_cur_index++;
1328
1329 raw.x = LE_S16(I_ROUND(node->x));
1330 raw.y = LE_S16(I_ROUND(node->y));
1331 raw.dx = LE_S16(I_ROUND(node->dx));
1332 raw.dy = LE_S16(I_ROUND(node->dy));
1333
1334 raw.b1.minx = LE_S16(node->r.bounds.minx);
1335 raw.b1.miny = LE_S16(node->r.bounds.miny);
1336 raw.b1.maxx = LE_S16(node->r.bounds.maxx);
1337 raw.b1.maxy = LE_S16(node->r.bounds.maxy);
1338
1339 raw.b2.minx = LE_S16(node->l.bounds.minx);
1340 raw.b2.miny = LE_S16(node->l.bounds.miny);
1341 raw.b2.maxx = LE_S16(node->l.bounds.maxx);
1342 raw.b2.maxy = LE_S16(node->l.bounds.maxy);
1343
1344 if (node->r.node)
1345 raw.right = LE_U32(node->r.node->index);
1346 else if (node->r.subsec)
1347 raw.right = LE_U32(node->r.subsec->index | 0x80000000U);
1348 else
1349 BugError("Bad right child in V5 node %d\n", node->index);
1350
1351 if (node->l.node)
1352 raw.left = LE_U32(node->l.node->index);
1353 else if (node->l.subsec)
1354 raw.left = LE_U32(node->l.subsec->index | 0x80000000U);
1355 else
1356 BugError("Bad left child in V5 node %d\n", node->index);
1357
1358 lump->Write(&raw, sizeof(raw));
1359
1360 # if DEBUG_BSP
1361 DebugPrintf("PUT V5 NODE %08X Left %08X Right %08X "
1362 "(%d,%d) -> (%d,%d)\n", node->index, LE_U32(raw.left),
1363 LE_U32(raw.right), node->x, node->y,
1364 node->x + node->dx, node->y + node->dy);
1365 # endif
1366 }
1367
1368
PutNodes(const char * name,int do_v5,node_t * root)1369 void PutNodes(const char *name, int do_v5, node_t *root)
1370 {
1371 int struct_size = do_v5 ? (int)sizeof(raw_v5_node_t) : (int)sizeof(raw_node_t);
1372
1373 // this can be bigger than the actual size, but never smaller
1374 int max_size = (num_nodes + 1) * struct_size;
1375
1376 Lump_c *lump = CreateLevelLump(name, max_size);
1377
1378 node_cur_index = 0;
1379
1380 if (root)
1381 {
1382 if (do_v5)
1383 PutOneNode_V5(root, lump);
1384 else
1385 PutOneNode(root, lump);
1386 }
1387
1388 if (node_cur_index != num_nodes)
1389 BugError("PutNodes miscounted (%d != %d)\n",
1390 node_cur_index, num_nodes);
1391
1392 if (!do_v5 && node_cur_index > 32767)
1393 {
1394 Failure("Number of nodes has overflowed.\n");
1395 MarkOverflow(LIMIT_NODES);
1396 }
1397 }
1398
1399
CheckLimits(bool & force_v5,bool & force_xnod)1400 void CheckLimits(bool& force_v5, bool& force_xnod)
1401 {
1402 if (NumSectors > 65534)
1403 {
1404 Failure("Map has too many sectors.\n");
1405 MarkOverflow(LIMIT_SECTORS);
1406 }
1407
1408 if (NumSideDefs > 65534)
1409 {
1410 Failure("Map has too many sidedefs.\n");
1411 MarkOverflow(LIMIT_SIDEDEFS);
1412 }
1413
1414 if (NumLineDefs > 65534)
1415 {
1416 Failure("Map has too many linedefs.\n");
1417 MarkOverflow(LIMIT_LINEDEFS);
1418 }
1419
1420 if (cur_info->gl_nodes && !cur_info->force_v5)
1421 {
1422 if (num_old_vert > 32767 ||
1423 num_new_vert > 32767 ||
1424 num_segs > 65534 ||
1425 num_nodes > 32767)
1426 {
1427 Warning("Forcing V5 of GL-Nodes due to overflows.\n");
1428 force_v5 = true;
1429 }
1430 }
1431
1432 if (! cur_info->force_xnod)
1433 {
1434 if (num_old_vert > 32767 ||
1435 num_new_vert > 32767 ||
1436 num_segs > 32767 ||
1437 num_nodes > 32767)
1438 {
1439 Warning("Forcing XNOD format nodes due to overflows.\n");
1440 force_xnod = true;
1441 }
1442 }
1443 }
1444
1445
1446 struct seg_index_CMP_pred
1447 {
operator ()ajbsp::seg_index_CMP_pred1448 inline bool operator() (const seg_t *A, const seg_t *B) const
1449 {
1450 return A->index < B->index;
1451 }
1452 };
1453
SortSegs()1454 void SortSegs()
1455 {
1456 // do a sanity check
1457 for (int i = 0 ; i < num_segs ; i++)
1458 if (lev_segs[i]->index < 0)
1459 BugError("Seg %p never reached a subsector!\n", i);
1460
1461 // sort segs into ascending index
1462 std::sort(lev_segs.begin(), lev_segs.end(), seg_index_CMP_pred());
1463
1464 // remove unwanted segs
1465 while (lev_segs.size() > 0 && lev_segs.back()->index == SEG_IS_GARBAGE)
1466 {
1467 UtilFree((void *) lev_segs.back());
1468 lev_segs.pop_back();
1469 }
1470 }
1471
1472
1473 /* ----- ZDoom format writing --------------------------- */
1474
1475 static const u8_t *lev_XNOD_magic = (u8_t *) "XNOD";
1476 static const u8_t *lev_XGL3_magic = (u8_t *) "XGL3";
1477 static const u8_t *lev_ZNOD_magic = (u8_t *) "ZNOD";
1478
PutZVertices()1479 void PutZVertices()
1480 {
1481 int count, i;
1482
1483 u32_t orgverts = LE_U32(num_old_vert);
1484 u32_t newverts = LE_U32(num_new_vert);
1485
1486 ZLibAppendLump(&orgverts, 4);
1487 ZLibAppendLump(&newverts, 4);
1488
1489 for (i=0, count=0 ; i < num_vertices ; i++)
1490 {
1491 raw_v2_vertex_t raw;
1492
1493 vertex_t *vert = lev_vertices[i];
1494
1495 if (! vert->is_new)
1496 continue;
1497
1498 raw.x = LE_S32(I_ROUND(vert->x * 65536.0));
1499 raw.y = LE_S32(I_ROUND(vert->y * 65536.0));
1500
1501 ZLibAppendLump(&raw, sizeof(raw));
1502
1503 count++;
1504 }
1505
1506 if (count != num_new_vert)
1507 BugError("PutZVertices miscounted (%d != %d)\n", count, num_new_vert);
1508 }
1509
1510
PutZSubsecs()1511 void PutZSubsecs()
1512 {
1513 int i;
1514 int count;
1515 u32_t raw_num = LE_U32(num_subsecs);
1516
1517 int cur_seg_index = 0;
1518
1519 ZLibAppendLump(&raw_num, 4);
1520
1521 for (i=0 ; i < num_subsecs ; i++)
1522 {
1523 subsec_t *sub = lev_subsecs[i];
1524 seg_t *seg;
1525
1526 raw_num = LE_U32(sub->seg_count);
1527
1528 ZLibAppendLump(&raw_num, 4);
1529
1530 // sanity check the seg index values
1531 count = 0;
1532 for (seg = sub->seg_list ; seg ; seg = seg->next, cur_seg_index++)
1533 {
1534 if (cur_seg_index != seg->index)
1535 BugError("PutZSubsecs: seg index mismatch in sub %d (%d != %d)\n",
1536 i, cur_seg_index, seg->index);
1537
1538 count++;
1539 }
1540
1541 if (count != sub->seg_count)
1542 BugError("PutZSubsecs: miscounted segs in sub %d (%d != %d)\n",
1543 i, count, sub->seg_count);
1544 }
1545
1546 if (cur_seg_index != num_segs)
1547 BugError("PutZSubsecs miscounted segs (%d != %d)\n",
1548 cur_seg_index, num_segs);
1549 }
1550
1551
PutZSegs()1552 void PutZSegs()
1553 {
1554 int i, count;
1555 u32_t raw_num = LE_U32(num_segs);
1556
1557 ZLibAppendLump(&raw_num, 4);
1558
1559 for (i=0, count=0 ; i < num_segs ; i++)
1560 {
1561 seg_t *seg = lev_segs[i];
1562
1563 if (count != seg->index)
1564 BugError("PutZSegs: seg index mismatch (%d != %d)\n",
1565 count, seg->index);
1566
1567 {
1568 u32_t v1 = LE_U32(VertexIndex_XNOD(seg->start));
1569 u32_t v2 = LE_U32(VertexIndex_XNOD(seg->end));
1570
1571 u16_t line = LE_U16(seg->linedef);
1572 u8_t side = seg->side;
1573
1574 ZLibAppendLump(&v1, 4);
1575 ZLibAppendLump(&v2, 4);
1576 ZLibAppendLump(&line, 2);
1577 ZLibAppendLump(&side, 1);
1578 }
1579
1580 count++;
1581 }
1582
1583 if (count != num_segs)
1584 BugError("PutZSegs miscounted (%d != %d)\n", count, num_segs);
1585 }
1586
1587
PutXGL3Segs()1588 void PutXGL3Segs()
1589 {
1590 int i, count;
1591 u32_t raw_num = LE_U32(num_segs);
1592
1593 ZLibAppendLump(&raw_num, 4);
1594
1595 for (i=0, count=0 ; i < num_segs ; i++)
1596 {
1597 seg_t *seg = lev_segs[i];
1598
1599 if (count != seg->index)
1600 BugError("PutXGL3Segs: seg index mismatch (%d != %d)\n",
1601 count, seg->index);
1602
1603 {
1604 u32_t v1 = LE_U32(VertexIndex_XNOD(seg->start));
1605 u32_t partner = LE_U32(seg->partner ? seg->partner->index : -1);
1606 u32_t line = LE_U32(seg->linedef);
1607 u8_t side = seg->side;
1608
1609 # if DEBUG_BSP
1610 fprintf(stderr, "SEG[%d] v1=%d partner=%d line=%d side=%d\n", i, v1, partner, line, side);
1611 # endif
1612
1613 ZLibAppendLump(&v1, 4);
1614 ZLibAppendLump(&partner, 4);
1615 ZLibAppendLump(&line, 4);
1616 ZLibAppendLump(&side, 1);
1617 }
1618
1619 count++;
1620 }
1621
1622 if (count != num_segs)
1623 BugError("PutXGL3Segs miscounted (%d != %d)\n", count, num_segs);
1624 }
1625
1626
PutOneZNode(node_t * node,bool do_xgl3)1627 static void PutOneZNode(node_t *node, bool do_xgl3)
1628 {
1629 raw_v5_node_t raw;
1630
1631 if (node->r.node)
1632 PutOneZNode(node->r.node, do_xgl3);
1633
1634 if (node->l.node)
1635 PutOneZNode(node->l.node, do_xgl3);
1636
1637 node->index = node_cur_index++;
1638
1639 if (do_xgl3)
1640 {
1641 u32_t x = LE_S32(I_ROUND(node->x * 65536.0));
1642 u32_t y = LE_S32(I_ROUND(node->y * 65536.0));
1643 u32_t dx = LE_S32(I_ROUND(node->dx * 65536.0));
1644 u32_t dy = LE_S32(I_ROUND(node->dy * 65536.0));
1645
1646 ZLibAppendLump(&x, 4);
1647 ZLibAppendLump(&y, 4);
1648 ZLibAppendLump(&dx, 4);
1649 ZLibAppendLump(&dy, 4);
1650 }
1651 else
1652 {
1653 raw.x = LE_S16(I_ROUND(node->x));
1654 raw.y = LE_S16(I_ROUND(node->y));
1655 raw.dx = LE_S16(I_ROUND(node->dx));
1656 raw.dy = LE_S16(I_ROUND(node->dy));
1657
1658 ZLibAppendLump(&raw.x, 2);
1659 ZLibAppendLump(&raw.y, 2);
1660 ZLibAppendLump(&raw.dx, 2);
1661 ZLibAppendLump(&raw.dy, 2);
1662 }
1663
1664 raw.b1.minx = LE_S16(node->r.bounds.minx);
1665 raw.b1.miny = LE_S16(node->r.bounds.miny);
1666 raw.b1.maxx = LE_S16(node->r.bounds.maxx);
1667 raw.b1.maxy = LE_S16(node->r.bounds.maxy);
1668
1669 raw.b2.minx = LE_S16(node->l.bounds.minx);
1670 raw.b2.miny = LE_S16(node->l.bounds.miny);
1671 raw.b2.maxx = LE_S16(node->l.bounds.maxx);
1672 raw.b2.maxy = LE_S16(node->l.bounds.maxy);
1673
1674 ZLibAppendLump(&raw.b1, sizeof(raw.b1));
1675 ZLibAppendLump(&raw.b2, sizeof(raw.b2));
1676
1677 if (node->r.node)
1678 raw.right = LE_U32(node->r.node->index);
1679 else if (node->r.subsec)
1680 raw.right = LE_U32(node->r.subsec->index | 0x80000000U);
1681 else
1682 BugError("Bad right child in V5 node %d\n", node->index);
1683
1684 if (node->l.node)
1685 raw.left = LE_U32(node->l.node->index);
1686 else if (node->l.subsec)
1687 raw.left = LE_U32(node->l.subsec->index | 0x80000000U);
1688 else
1689 BugError("Bad left child in V5 node %d\n", node->index);
1690
1691 ZLibAppendLump(&raw.right, 4);
1692 ZLibAppendLump(&raw.left, 4);
1693
1694 # if DEBUG_BSP
1695 DebugPrintf("PUT Z NODE %08X Left %08X Right %08X "
1696 "(%d,%d) -> (%d,%d)\n", node->index, LE_U32(raw.left),
1697 LE_U32(raw.right), node->x, node->y,
1698 node->x + node->dx, node->y + node->dy);
1699 # endif
1700 }
1701
1702
PutZNodes(node_t * root,bool do_xgl3)1703 void PutZNodes(node_t *root, bool do_xgl3)
1704 {
1705 u32_t raw_num = LE_U32(num_nodes);
1706
1707 ZLibAppendLump(&raw_num, 4);
1708
1709 node_cur_index = 0;
1710
1711 if (root)
1712 PutOneZNode(root, do_xgl3);
1713
1714 if (node_cur_index != num_nodes)
1715 BugError("PutZNodes miscounted (%d != %d)\n",
1716 node_cur_index, num_nodes);
1717 }
1718
1719
CalcZDoomNodesSize()1720 static int CalcZDoomNodesSize()
1721 {
1722 // compute size of the ZDoom format nodes.
1723 // it does not need to be exact, but it *does* need to be bigger
1724 // (or equal) to the actual size of the lump.
1725
1726 int size = 32; // header + a bit extra
1727
1728 size += 8 + num_vertices * 8;
1729 size += 4 + num_subsecs * 4;
1730 size += 4 + num_segs * 11;
1731 size += 4 + num_nodes * sizeof(raw_v5_node_t);
1732
1733 if (cur_info->force_compress)
1734 {
1735 // according to RFC1951, the zlib compression worst-case
1736 // scenario is 5 extra bytes per 32KB (0.015% increase).
1737 // we are significantly more conservative!
1738
1739 size += ((size + 255) >> 5);
1740 }
1741
1742 return size;
1743 }
1744
1745
SaveZDFormat(node_t * root_node)1746 void SaveZDFormat(node_t *root_node)
1747 {
1748 // leave SEGS and SSECTORS empty
1749 CreateLevelLump("SEGS")->Finish();
1750 CreateLevelLump("SSECTORS")->Finish();
1751
1752 int max_size = CalcZDoomNodesSize();
1753
1754 Lump_c *lump = CreateLevelLump("NODES", max_size);
1755
1756 if (cur_info->force_compress)
1757 lump->Write(lev_ZNOD_magic, 4);
1758 else
1759 lump->Write(lev_XNOD_magic, 4);
1760
1761 // the ZLibXXX functions do no compression for XNOD format
1762 ZLibBeginLump(lump);
1763
1764 PutZVertices();
1765 PutZSubsecs();
1766 PutZSegs();
1767 PutZNodes(root_node, false /* do_xgl3 */);
1768
1769 ZLibFinishLump();
1770 }
1771
1772
SaveXGL3Format(node_t * root_node)1773 void SaveXGL3Format(node_t *root_node)
1774 {
1775 // WISH : compute a max_size
1776
1777 Lump_c *lump = CreateLevelLump("ZNODES", -1);
1778
1779 lump->Write(lev_XGL3_magic, 4);
1780
1781 // disable compression
1782 cur_info->force_compress = false;
1783
1784 ZLibBeginLump(lump);
1785
1786 PutZVertices();
1787 PutZSubsecs();
1788 PutXGL3Segs();
1789 PutZNodes(root_node, true /* do_xgl3 */);
1790
1791 ZLibFinishLump();
1792 }
1793
1794
1795 /* ----- whole-level routines --------------------------- */
1796
LoadLevel()1797 void LoadLevel()
1798 {
1799 Lump_c *LEV = edit_wad->GetLump(lev_current_start);
1800
1801 lev_current_name = LEV->Name();
1802 lev_overflows = 0;
1803
1804 GB_PrintMsg("Building nodes on %s\n", lev_current_name);
1805
1806 num_new_vert = 0;
1807 num_real_lines = 0;
1808
1809 GetVertices();
1810
1811 for (int ld = 0 ; ld < NumLineDefs ; ld++)
1812 {
1813 LineDef *L = LineDefs[ld];
1814
1815 if (L->right >= 0 || L->left >= 0)
1816 num_real_lines++;
1817
1818 // init some fake flags
1819 L->flags &= ~(MLF_IS_PRECIOUS | MLF_IS_OVERLAP);
1820
1821 if (L->tag >= 900 && L->tag < 1000)
1822 L->flags |= MLF_IS_PRECIOUS;
1823 }
1824
1825 PrintDetail("Loaded %d vertices, %d sectors, %d sides, %d lines, %d things\n",
1826 NumVertices, NumSectors, NumSideDefs, NumLineDefs, NumThings);
1827
1828 DetectOverlappingVertices();
1829 DetectOverlappingLines();
1830
1831 CalculateWallTips();
1832
1833 if (Level_format != MAPF_Doom)
1834 {
1835 // -JL- Find sectors containing polyobjs
1836 DetectPolyobjSectors();
1837 }
1838 }
1839
1840
FreeLevel(void)1841 void FreeLevel(void)
1842 {
1843 FreeVertices();
1844 FreeSegs();
1845 FreeSubsecs();
1846 FreeNodes();
1847 FreeWallTips();
1848 }
1849
1850
CalcGLChecksum(void)1851 static u32_t CalcGLChecksum(void)
1852 {
1853 u32_t crc;
1854
1855 Adler32_Begin(&crc);
1856
1857 Lump_c *lump = FindLevelLump("VERTEXES");
1858
1859 if (lump && lump->Length() > 0)
1860 {
1861 u8_t *data = new u8_t[lump->Length()];
1862
1863 if (! lump->Seek() ||
1864 ! lump->Read(data, lump->Length()))
1865 FatalError("Error reading vertices (for checksum).\n");
1866
1867 Adler32_AddBlock(&crc, data, lump->Length());
1868 delete[] data;
1869 }
1870
1871 lump = FindLevelLump("LINEDEFS");
1872
1873 if (lump && lump->Length() > 0)
1874 {
1875 u8_t *data = new u8_t[lump->Length()];
1876
1877 if (! lump->Seek() ||
1878 ! lump->Read(data, lump->Length()))
1879 FatalError("Error reading linedefs (for checksum).\n");
1880
1881 Adler32_AddBlock(&crc, data, lump->Length());
1882 delete[] data;
1883 }
1884
1885 Adler32_Finish(&crc);
1886
1887 return crc;
1888 }
1889
1890
CalcOptionsString()1891 static const char *CalcOptionsString()
1892 {
1893 static char buffer[256];
1894
1895 snprintf(buffer, sizeof(buffer), "--cost %d%s", cur_info->factor,
1896 cur_info->fast ? " --fast" : "");
1897
1898 return buffer;
1899 }
1900
1901
UpdateGLMarker(Lump_c * marker)1902 void UpdateGLMarker(Lump_c *marker)
1903 {
1904 // this is very conservative, around 4 times the actual size
1905 const int max_size = 512;
1906
1907 // we *must* compute the checksum BEFORE (re)creating the lump
1908 // [ otherwise we write data into the wrong part of the file ]
1909 u32_t crc = CalcGLChecksum();
1910
1911 edit_wad->RecreateLump(marker, max_size);
1912
1913 // when original name is long, need to specify it here
1914 if (strlen(lev_current_name) > 5)
1915 {
1916 marker->Printf("LEVEL=%s\n", lev_current_name);
1917 }
1918
1919 marker->Printf("BUILDER=%s\n", "Eureka " EUREKA_VERSION);
1920 marker->Printf("OPTIONS=%s\n", CalcOptionsString());
1921
1922 char *time_str = UtilTimeString();
1923
1924 if (time_str)
1925 {
1926 marker->Printf("TIME=%s\n", time_str);
1927 StringFree(time_str);
1928 }
1929
1930 marker->Printf("CHECKSUM=0x%08x\n", crc);
1931 marker->Finish();
1932 }
1933
1934
AddMissingLump(const char * name,const char * after)1935 static void AddMissingLump(const char *name, const char *after)
1936 {
1937 if (edit_wad->LevelLookupLump(lev_current_idx, name) >= 0)
1938 return;
1939
1940 short exist = edit_wad->LevelLookupLump(lev_current_idx, after);
1941
1942 // if this happens, the level structure is very broken
1943 if (exist < 0)
1944 {
1945 Warning("Missing %s lump -- level structure is broken\n", after);
1946
1947 exist = edit_wad->LevelLastLump(lev_current_idx);
1948 }
1949
1950 edit_wad->InsertPoint(exist + 1);
1951
1952 edit_wad->AddLump(name)->Finish();
1953 }
1954
1955
SaveLevel(node_t * root_node)1956 build_result_e SaveLevel(node_t *root_node)
1957 {
1958 // Note: root_node may be NULL
1959
1960 edit_wad->BeginWrite();
1961
1962 // remove any existing GL-Nodes
1963 edit_wad->RemoveGLNodes(lev_current_idx);
1964
1965 // ensure all necessary level lumps are present
1966 AddMissingLump("SEGS", "VERTEXES");
1967 AddMissingLump("SSECTORS", "SEGS");
1968 AddMissingLump("NODES", "SSECTORS");
1969 AddMissingLump("REJECT", "SECTORS");
1970 AddMissingLump("BLOCKMAP", "REJECT");
1971
1972 // user preferences
1973 bool force_v5 = cur_info->force_v5;
1974 bool force_xnod = cur_info->force_xnod;
1975
1976 // check for overflows...
1977 // this sets the force_xxx vars if certain limits are breached
1978 CheckLimits(force_v5, force_xnod);
1979
1980
1981 /* --- GL Nodes --- */
1982
1983 Lump_c * gl_marker = NULL;
1984
1985 if (cur_info->gl_nodes && num_real_lines > 0)
1986 {
1987 SortSegs();
1988
1989 // create empty marker now, flesh it out later
1990 gl_marker = CreateGLMarker();
1991
1992 PutGLVertices(force_v5);
1993
1994 if (force_v5)
1995 PutGLSegs_V5();
1996 else
1997 PutGLSegs();
1998
1999 if (force_v5)
2000 PutGLSubsecs_V5();
2001 else
2002 PutSubsecs("GL_SSECT", true);
2003
2004 PutNodes("GL_NODES", force_v5, root_node);
2005
2006 // -JL- Add empty PVS lump
2007 CreateLevelLump("GL_PVS")->Finish();
2008 }
2009
2010
2011 /* --- Normal nodes --- */
2012
2013 // remove all the mini-segs from subsectors
2014 NormaliseBspTree();
2015
2016 if (force_xnod && num_real_lines > 0)
2017 {
2018 SortSegs();
2019
2020 SaveZDFormat(root_node);
2021 }
2022 else
2023 {
2024 // reduce vertex precision for classic DOOM nodes.
2025 // some segs can become "degenerate" after this, and these
2026 // are removed from subsectors.
2027 RoundOffBspTree();
2028
2029 // this also removes minisegs and degenerate segs
2030 SortSegs();
2031
2032 PutVertices("VERTEXES", false);
2033
2034 PutSegs();
2035 PutSubsecs("SSECTORS", false);
2036 PutNodes("NODES", false, root_node);
2037 }
2038
2039 PutBlockmap();
2040 PutReject();
2041
2042 // keyword support (v5.0 of the specs).
2043 // must be done *after* doing normal nodes (for proper checksum).
2044 if (gl_marker)
2045 {
2046 UpdateGLMarker(gl_marker);
2047 }
2048
2049 edit_wad->EndWrite();
2050
2051 if (lev_overflows > 0)
2052 {
2053 cur_info->total_failed_maps++;
2054 GB_PrintMsg("FAILED with %d overflowed lumps\n", lev_overflows);
2055
2056 return BUILD_LumpOverflow;
2057 }
2058
2059 return BUILD_OK;
2060 }
2061
2062
SaveUDMF(node_t * root_node)2063 build_result_e SaveUDMF(node_t *root_node)
2064 {
2065 edit_wad->BeginWrite();
2066
2067 // remove any existing ZNODES lump
2068 edit_wad->RemoveZNodes(lev_current_idx);
2069
2070 if (num_real_lines >= 0)
2071 {
2072 SortSegs();
2073
2074 SaveXGL3Format(root_node);
2075 }
2076
2077 edit_wad->EndWrite();
2078
2079 if (lev_overflows > 0)
2080 {
2081 cur_info->total_failed_maps++;
2082 GB_PrintMsg("FAILED with %d overflowed lumps\n", lev_overflows);
2083
2084 return BUILD_LumpOverflow;
2085 }
2086
2087 return BUILD_OK;
2088 }
2089
2090
2091 //----------------------------------------------------------------------
2092
2093
2094 static Lump_c *zout_lump;
2095 static z_stream zout_stream;
2096 static Bytef zout_buffer[1024];
2097
2098
ZLibBeginLump(Lump_c * lump)2099 void ZLibBeginLump(Lump_c *lump)
2100 {
2101 zout_lump = lump;
2102
2103 if (! cur_info->force_compress)
2104 return;
2105
2106 zout_stream.zalloc = (alloc_func)0;
2107 zout_stream.zfree = (free_func)0;
2108 zout_stream.opaque = (voidpf)0;
2109
2110 if (Z_OK != deflateInit(&zout_stream, Z_DEFAULT_COMPRESSION))
2111 FatalError("Trouble setting up zlib compression\n");
2112
2113 zout_stream.next_out = zout_buffer;
2114 zout_stream.avail_out = sizeof(zout_buffer);
2115 }
2116
2117
ZLibAppendLump(const void * data,int length)2118 void ZLibAppendLump(const void *data, int length)
2119 {
2120 // ASSERT(zout_lump)
2121 // ASSERT(length > 0)
2122
2123 if (! cur_info->force_compress)
2124 {
2125 zout_lump->Write(data, length);
2126 return;
2127 }
2128
2129 zout_stream.next_in = (Bytef*)data; // const override
2130 zout_stream.avail_in = length;
2131
2132 while (zout_stream.avail_in > 0)
2133 {
2134 int err = deflate(&zout_stream, Z_NO_FLUSH);
2135
2136 if (err != Z_OK)
2137 FatalError("Trouble compressing %d bytes (zlib)\n", length);
2138
2139 if (zout_stream.avail_out == 0)
2140 {
2141 zout_lump->Write(zout_buffer, sizeof(zout_buffer));
2142
2143 zout_stream.next_out = zout_buffer;
2144 zout_stream.avail_out = sizeof(zout_buffer);
2145 }
2146 }
2147 }
2148
2149
ZLibFinishLump(void)2150 void ZLibFinishLump(void)
2151 {
2152 if (! cur_info->force_compress)
2153 {
2154 zout_lump = NULL;
2155 return;
2156 }
2157
2158 int left_over;
2159
2160 // ASSERT(zout_stream.avail_out > 0)
2161
2162 zout_stream.next_in = Z_NULL;
2163 zout_stream.avail_in = 0;
2164
2165 for (;;)
2166 {
2167 int err = deflate(&zout_stream, Z_FINISH);
2168
2169 if (err == Z_STREAM_END)
2170 break;
2171
2172 if (err != Z_OK)
2173 FatalError("Trouble finishing compression (zlib)\n");
2174
2175 if (zout_stream.avail_out == 0)
2176 {
2177 zout_lump->Write(zout_buffer, sizeof(zout_buffer));
2178
2179 zout_stream.next_out = zout_buffer;
2180 zout_stream.avail_out = sizeof(zout_buffer);
2181 }
2182 }
2183
2184 left_over = sizeof(zout_buffer) - zout_stream.avail_out;
2185
2186 if (left_over > 0)
2187 zout_lump->Write(zout_buffer, left_over);
2188
2189 deflateEnd(&zout_stream);
2190 zout_lump = NULL;
2191 }
2192
2193
2194 /* ---------------------------------------------------------------- */
2195
2196
FindLevelLump(const char * name)2197 Lump_c * FindLevelLump(const char *name)
2198 {
2199 short idx = edit_wad->LevelLookupLump(lev_current_idx, name);
2200
2201 if (idx < 0)
2202 return NULL;
2203
2204 return edit_wad->GetLump(idx);
2205 }
2206
2207
CreateLevelLump(const char * name,int max_size)2208 Lump_c * CreateLevelLump(const char *name, int max_size)
2209 {
2210 // look for existing one
2211 Lump_c *lump = FindLevelLump(name);
2212
2213 if (lump)
2214 {
2215 edit_wad->RecreateLump(lump, max_size);
2216 }
2217 else
2218 {
2219 short last_idx = edit_wad->LevelLastLump(lev_current_idx);
2220
2221 // in UDMF maps, insert before the ENDMAP lump, otherwise insert
2222 // after the last known lump of the level.
2223 if (Level_format != MAPF_UDMF)
2224 last_idx++;
2225
2226 edit_wad->InsertPoint(last_idx);
2227
2228 lump = edit_wad->AddLump(name, max_size);
2229 }
2230
2231 return lump;
2232 }
2233
2234
CreateGLMarker()2235 Lump_c * CreateGLMarker()
2236 {
2237 char name_buf[64];
2238
2239 if (strlen(lev_current_name) <= 5)
2240 {
2241 snprintf(name_buf, sizeof(name_buf), "GL_%s", lev_current_name);
2242 }
2243 else
2244 {
2245 // names longer than 5 chars use "GL_LEVEL" as marker name
2246 strcpy(name_buf, "GL_LEVEL");
2247 }
2248
2249 short last_idx = edit_wad->LevelLastLump(lev_current_idx);
2250
2251 edit_wad->InsertPoint(last_idx + 1);
2252
2253 Lump_c *marker = edit_wad->AddLump(name_buf);
2254
2255 marker->Finish();
2256
2257 return marker;
2258 }
2259
2260
2261 //------------------------------------------------------------------------
2262 // MAIN STUFF
2263 //------------------------------------------------------------------------
2264
2265 nodebuildinfo_t * cur_info = NULL;
2266
2267
BuildLevel(nodebuildinfo_t * info,short lev_idx)2268 build_result_e BuildLevel(nodebuildinfo_t *info, short lev_idx)
2269 {
2270 cur_info = info;
2271
2272 node_t *root_node = NULL;
2273 subsec_t *root_sub = NULL;
2274 bbox_t root_bbox;
2275
2276 if (cur_info->cancelled)
2277 return BUILD_Cancelled;
2278
2279 lev_current_idx = lev_idx;
2280 lev_current_start = edit_wad->LevelHeader(lev_idx);
2281
2282 LoadLevel();
2283
2284 InitBlockmap();
2285
2286
2287 build_result_e ret = BUILD_OK;
2288
2289 if (num_real_lines > 0)
2290 {
2291 // create initial segs
2292 seg_t *list = CreateSegs();
2293
2294 // recursively create nodes
2295 ret = BuildNodes(list, &root_bbox, &root_node, &root_sub, 0);
2296 }
2297
2298 if (ret == BUILD_OK)
2299 {
2300 PrintDetail("Built %d NODES, %d SSECTORS, %d SEGS, %d VERTEXES\n",
2301 num_nodes, num_subsecs, num_segs, num_old_vert + num_new_vert);
2302
2303 if (root_node)
2304 {
2305 PrintDetail("Heights of left and right subtrees = (%d,%d)\n",
2306 ComputeBspHeight(root_node->r.node),
2307 ComputeBspHeight(root_node->l.node));
2308 }
2309
2310 ClockwiseBspTree();
2311
2312 if (Level_format == MAPF_UDMF)
2313 ret = SaveUDMF(root_node);
2314 else
2315 ret = SaveLevel(root_node);
2316 }
2317 else
2318 {
2319 /* build was Cancelled by the user */
2320 }
2321
2322 FreeLevel();
2323 FreeQuickAllocCuts();
2324
2325 // clear some fake line flags
2326 for (int i = 0 ; i < NumLineDefs ; i++)
2327 LineDefs[i]->flags &= ~(MLF_IS_PRECIOUS | MLF_IS_OVERLAP);
2328
2329 return ret;
2330 }
2331
2332 } // namespace ajbsp
2333
2334
AJBSP_BuildLevel(nodebuildinfo_t * info,short lev_idx)2335 build_result_e AJBSP_BuildLevel(nodebuildinfo_t *info, short lev_idx)
2336 {
2337 return ajbsp::BuildLevel(info, lev_idx);
2338 }
2339
2340 //--- editor settings ---
2341 // vi:ts=4:sw=4:noexpandtab
2342