1 /*
2 * levels.cc
3 * Level loading and saving routines,
4 * global variables used to hold the level data.
5 * BW & RQ sometime in 1993 or 1994.
6 */
7
8
9 /*
10 This file is part of Yadex.
11
12 Yadex incorporates code from DEU 5.21 that was put in the public domain in
13 1994 by Rapha�l Quinet and Brendon Wyber.
14
15 The rest of Yadex is Copyright � 1997-2003 Andr� Majorel and others.
16
17 This program is free software; you can redistribute it and/or modify it under
18 the terms of the GNU General Public License as published by the Free Software
19 Foundation; either version 2 of the License, or (at your option) any later
20 version.
21
22 This program is distributed in the hope that it will be useful, but WITHOUT
23 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
24 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
25
26 You should have received a copy of the GNU General Public License along with
27 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
28 Place, Suite 330, Boston, MA 02111-1307, USA.
29 */
30
31
32 #include "yadex.h"
33 #include "bitvec.h"
34 #include "dialog.h"
35 #include "game.h"
36 #include "levels.h"
37 #include "objid.h"
38 #include "wstructs.h"
39 #include "things.h"
40 #include "wadfile.h"
41 #include "wads.h"
42 #include "wads2.h"
43
44
45 /*
46 FIXME
47 All these variables should be turned
48 into members of a "Level" class.
49 */
50 MDirPtr Level; /* master dictionary entry for the level */
51 int NumThings; /* number of things */
52 TPtr Things; /* things data */
53 int NumLineDefs; /* number of line defs */
54 LDPtr LineDefs; /* line defs data */
55 int NumSideDefs; /* number of side defs */
56 SDPtr SideDefs; /* side defs data */
57 int NumVertices; /* number of vertexes */
58 VPtr Vertices; /* vertex data */
59 int NumSectors; /* number of sectors */
60 SPtr Sectors; /* sectors data */
61
62 // FIXME should be somewhere else
63 int NumWTexture; /* number of wall textures */
64 char **WTexture; /* array of wall texture names */
65
66 // FIXME all the flat list stuff should be put in a separate class
67 size_t NumFTexture; /* number of floor/ceiling textures */
68 flat_list_entry_t *flat_list; // List of all flats in the directory
69
70 int MapMaxX = -32767; /* maximum X value of map */
71 int MapMaxY = -32767; /* maximum Y value of map */
72 int MapMinX = 32767; /* minimum X value of map */
73 int MapMinY = 32767; /* minimum Y value of map */
74 bool MadeChanges; /* made changes? */
75 bool MadeMapChanges; /* made changes that need rebuilding? */
76 unsigned long things_angles; // See levels.h for description.
77 unsigned long things_types; // See levels.h for description.
78 char Level_name[WAD_NAME + 1]; /* The name of the level (E.G.
79 "MAP01" or "E1M1"), followed by a
80 NUL. If the Level has been created as
81 the result of a "c" command with no
82 argument, an empty string. The name
83 is not necesarily in upper case but
84 it always a valid lump name, not a
85 command line shortcut like "17". */
86
87 y_file_name_t Level_file_name; /* The name of the file in which
88 the level would be saved. If the
89 level has been created as the result
90 of a "c" command, with or without
91 argument, an empty string. */
92
93 y_file_name_t Level_file_name_saved; /* The name of the file in
94 which the level was last saved. If
95 the Level has never been saved yet,
96 an empty string. */
97
EmptyLevelData(const char * levelname)98 void EmptyLevelData (const char *levelname)
99 {
100 Things = 0;
101 NumThings = 0;
102 things_angles++;
103 things_types++;
104 LineDefs = 0;
105 NumLineDefs = 0;
106 SideDefs = 0;
107 NumSideDefs = 0;
108 Sectors = 0;
109 NumSectors = 0;
110 Vertices = 0;
111 NumVertices = 0;
112 }
113
114
115 /*
116 * texno_texname
117 * A convenience function when loading Doom alpha levels
118 */
119 static char *tex_list = 0;
120 static size_t ntex = 0;
121 static char tex_name[WAD_TEX_NAME + 1];
texno_texname(i16 texno)122 inline const char *texno_texname (i16 texno)
123 {
124 if (texno < 0)
125 return "-";
126 else
127 if (yg_texture_format == YGTF_NAMELESS)
128 {
129 sprintf (tex_name, "TEX%04u", (unsigned) texno);
130 return tex_name;
131 }
132 else
133 {
134 if (texno < (i16) ntex)
135 return tex_list + WAD_TEX_NAME * texno;
136 else
137 return "unknown";
138 }
139 }
140
141
142 /*
143 read in the level data
144 */
145
ReadLevelData(const char * levelname)146 int ReadLevelData (const char *levelname) /* SWAP! */
147 {
148 int rc = 0;
149 MDirPtr dir;
150 int OldNumVertices;
151
152 /* No objects are needed: they may be swapped after they have been read */
153 ObjectsNeeded (0);
154
155 /* Find the various level information from the master directory */
156 DisplayMessage (-1, -1, "Reading data for level %s...", levelname);
157 Level = FindMasterDir (MasterDir, levelname);
158 if (!Level)
159 fatal_error ("level data not found");
160
161 /* Get the number of vertices */
162 i32 v_offset = 42;
163 i32 v_length = 42;
164 {
165 const char *lump_name = "BUG";
166 if (yg_level_format == YGLF_ALPHA) // Doom alpha
167 lump_name = "POINTS";
168 else
169 lump_name = "VERTEXES";
170 dir = FindMasterDir (Level, lump_name);
171 if (dir == 0)
172 OldNumVertices = 0;
173 else
174 {
175 v_offset = dir->dir.start;
176 v_length = dir->dir.size;
177 if (yg_level_format == YGLF_ALPHA) // Doom alpha: skip leading count
178 {
179 v_offset += 4;
180 v_length -= 4;
181 }
182 OldNumVertices = (int) (v_length / WAD_VERTEX_BYTES);
183 if ((i32) (OldNumVertices * WAD_VERTEX_BYTES) != v_length)
184 warn ("the %s lump has a weird size."
185 " The wad might be corrupt.\n", lump_name);
186 }
187 }
188
189 // Read THINGS
190 {
191 const char *lump_name = "THINGS";
192 verbmsg ("Reading %s things", levelname);
193 i32 offset = 42;
194 i32 length;
195 dir = FindMasterDir (Level, lump_name);
196 if (dir == 0)
197 NumThings = 0;
198 else
199 {
200 offset = dir->dir.start;
201 length = dir->dir.size;
202 if (MainWad == Iwad4) // Hexen mode
203 {
204 NumThings = (int) (length / WAD_HEXEN_THING_BYTES);
205 if ((i32) (NumThings * WAD_HEXEN_THING_BYTES) != length)
206 warn ("the %s lump has a weird size."
207 " The wad might be corrupt.\n", lump_name);
208 }
209 else // Doom/Heretic/Strife mode
210 {
211 if (yg_level_format == YGLF_ALPHA) // Doom alpha: skip leading count
212 {
213 offset += 4;
214 length -= 4;
215 }
216 size_t thing_size = yg_level_format == YGLF_ALPHA ? 12 : WAD_THING_BYTES;
217 NumThings = (int) (length / thing_size);
218 if ((i32) (NumThings * thing_size) != length)
219 warn ("the %s lump has a weird size."
220 " The wad might be corrupt.\n", lump_name);
221 }
222 }
223 things_angles++;
224 things_types++;
225 if (NumThings > 0)
226 {
227 Things = (TPtr) GetFarMemory ((unsigned long) NumThings
228 * sizeof (struct Thing));
229 const Wad_file *wf = dir->wadfile;
230 wf->seek (offset);
231 if (wf->error ())
232 {
233 err ("%s: seek error", lump_name);
234 rc = 1;
235 goto byebye;
236 }
237 if (MainWad == Iwad4) // Hexen mode
238 for (long n = 0; n < NumThings; n++)
239 {
240 u8 dummy2[6];
241 wf->read_i16 (); // Tid
242 wf->read_i16 (&Things[n].xpos );
243 wf->read_i16 (&Things[n].ypos );
244 wf->read_i16 (); // Height
245 wf->read_i16 (&Things[n].angle);
246 wf->read_i16 (&Things[n].type );
247 wf->read_i16 (&Things[n].when );
248 wf->read_bytes (dummy2, sizeof dummy2);
249 if (wf->error ())
250 {
251 err ("%s: error reading thing #%ld", lump_name, n);
252 rc = 1;
253 goto byebye;
254 }
255 }
256 else // Doom/Heretic/Strife mode
257 for (long n = 0; n < NumThings; n++)
258 {
259 wf->read_i16 (&Things[n].xpos );
260 wf->read_i16 (&Things[n].ypos );
261 wf->read_i16 (&Things[n].angle);
262 wf->read_i16 (&Things[n].type );
263 if (yg_level_format == YGLF_ALPHA)
264 wf->read_i16 (); // Alpha. Don't know what it's for.
265 wf->read_i16 (&Things[n].when );
266 if (wf->error ())
267 {
268 err ("%s: error reading thing #%ld", lump_name, n);
269 rc = 1;
270 goto byebye;
271 }
272 }
273 }
274 }
275
276 // Read LINEDEFS
277 if (yg_level_format != YGLF_ALPHA)
278 {
279 const char *lump_name = "LINEDEFS";
280 verbmsg (" linedefs");
281 dir = FindMasterDir (Level, lump_name);
282 if (dir == 0)
283 NumLineDefs = 0;
284 else
285 {
286 if (MainWad == Iwad4) // Hexen mode
287 {
288 NumLineDefs = (int) (dir->dir.size / WAD_HEXEN_LINEDEF_BYTES);
289 if ((i32) (NumLineDefs * WAD_HEXEN_LINEDEF_BYTES) != dir->dir.size)
290 warn ("the %s lump has a weird size."
291 " The wad might be corrupt.\n", lump_name);
292 }
293 else // Doom/Heretic/Strife mode
294 {
295 NumLineDefs = (int) (dir->dir.size / WAD_LINEDEF_BYTES);
296 if ((i32) (NumLineDefs * WAD_LINEDEF_BYTES) != dir->dir.size)
297 warn ("the %s lump has a weird size."
298 " The wad might be corrupt.\n", lump_name);
299 }
300 }
301 if (NumLineDefs > 0)
302 {
303 LineDefs = (LDPtr) GetFarMemory ((unsigned long) NumLineDefs
304 * sizeof (struct LineDef));
305 const Wad_file *wf = dir->wadfile;
306 wf->seek (dir->dir.start);
307 if (wf->error ())
308 {
309 err ("%s: seek error", lump_name);
310 rc = 1;
311 goto byebye;
312 }
313 if (MainWad == Iwad4) // Hexen mode
314 for (long n = 0; n < NumLineDefs; n++)
315 {
316 u8 dummy[6];
317 wf->read_i16 (&LineDefs[n].start);
318 wf->read_i16 (&LineDefs[n].end);
319 wf->read_i16 (&LineDefs[n].flags);
320 wf->read_bytes (dummy, sizeof dummy);
321 wf->read_i16 (&LineDefs[n].sidedef1);
322 wf->read_i16 (&LineDefs[n].sidedef2);
323 LineDefs[n].type = dummy[0];
324 LineDefs[n].tag = dummy[1]; // arg1 often contains a tag
325 if (wf->error ())
326 {
327 err ("%s: error reading linedef #%ld", lump_name, n);
328 rc = 1;
329 goto byebye;
330 }
331 }
332 else // Doom/Heretic/Strife mode
333 for (long n = 0; n < NumLineDefs; n++)
334 {
335 wf->read_i16 (&LineDefs[n].start);
336 wf->read_i16 (&LineDefs[n].end);
337 wf->read_i16 (&LineDefs[n].flags);
338 wf->read_i16 (&LineDefs[n].type);
339 wf->read_i16 (&LineDefs[n].tag);
340 wf->read_i16 (&LineDefs[n].sidedef1);
341 wf->read_i16 (&LineDefs[n].sidedef2);
342 if (wf->error ())
343 {
344 err ("%s: error reading linedef #%ld", lump_name, n);
345 rc = 1;
346 goto byebye;
347 }
348 }
349 }
350 }
351
352 // Read SIDEDEFS
353 {
354 const char *lump_name = "SIDEDEFS";
355 verbmsg (" sidedefs");
356 dir = FindMasterDir (Level, lump_name);
357 if (dir)
358 {
359 NumSideDefs = (int) (dir->dir.size / WAD_SIDEDEF_BYTES);
360 if ((i32) (NumSideDefs * WAD_SIDEDEF_BYTES) != dir->dir.size)
361 warn ("the SIDEDEFS lump has a weird size."
362 " The wad might be corrupt.\n");
363 }
364 else
365 NumSideDefs = 0;
366 if (NumSideDefs > 0)
367 {
368 SideDefs = (SDPtr) GetFarMemory ((unsigned long) NumSideDefs
369 * sizeof (struct SideDef));
370 const Wad_file *wf = dir->wadfile;
371 wf->seek (dir->dir.start);
372 if (wf->error ())
373 {
374 err ("%s: seek error", lump_name);
375 rc = 1;
376 goto byebye;
377 }
378 for (long n = 0; n < NumSideDefs; n++)
379 {
380 wf->read_i16 (&SideDefs[n].xoff);
381 wf->read_i16 (&SideDefs[n].yoff);
382 wf->read_bytes (&SideDefs[n].tex1, WAD_TEX_NAME);
383 wf->read_bytes (&SideDefs[n].tex2, WAD_TEX_NAME);
384 wf->read_bytes (&SideDefs[n].tex3, WAD_TEX_NAME);
385 wf->read_i16 (&SideDefs[n].sector);
386 if (wf->error ())
387 {
388 err ("%s: error reading sidedef #%ld", lump_name, n);
389 rc = 1;
390 goto byebye;
391 }
392 }
393 }
394 }
395
396 /* Sanity checkings on linedefs: the 1st and 2nd vertices
397 must exist. The 1st and 2nd sidedefs must exist or be
398 set to -1. */
399 for (long n = 0; n < NumLineDefs; n++)
400 {
401 if (LineDefs[n].sidedef1 != -1
402 && outside (LineDefs[n].sidedef1, 0, NumSideDefs - 1))
403 {
404 err ("linedef %ld has bad 1st sidedef number %d, giving up",
405 n, LineDefs[n].sidedef1);
406 rc = 1;
407 goto byebye;
408 }
409 if (LineDefs[n].sidedef2 != -1
410 && outside (LineDefs[n].sidedef2, 0, NumSideDefs - 1))
411 {
412 err ("linedef %ld has bad 2nd sidedef number %d, giving up",
413 n, LineDefs[n].sidedef2);
414 rc = 1;
415 goto byebye;
416 }
417 if (outside (LineDefs[n].start, 0, OldNumVertices -1))
418 {
419 err ("linedef %ld has bad 1st vertex number %d, giving up",
420 n, LineDefs[n].start);
421 rc = 1;
422 goto byebye;
423 }
424 if (outside (LineDefs[n].end, 0, OldNumVertices - 1))
425 {
426 err ("linedef %ld has bad 2nd vertex number %d, giving up",
427 n, LineDefs[n].end);
428 rc = 1;
429 goto byebye;
430 }
431 }
432
433 // Read LINES (Doom alpha only)
434 if (yg_level_format == YGLF_ALPHA)
435 {
436 const char *lump_name = "LINES";
437 verbmsg (" lines");
438 dir = FindMasterDir (Level, lump_name);
439 if (dir)
440 {
441 if ((dir->dir.size - 4) % 36)
442 warn ("the %s lump has a weird size. The wad might be corrupt.\n",
443 lump_name);
444 const size_t nlines = dir->dir.size / 36;
445 NumLineDefs = nlines;
446 NumSideDefs = 2 * nlines; // Worst case. We'll adjust later.
447 LineDefs = (LDPtr) GetFarMemory ((unsigned long) NumLineDefs
448 * sizeof (struct LineDef));
449 SideDefs = (SDPtr) GetFarMemory ((unsigned long) NumSideDefs
450 * sizeof (struct SideDef));
451 // Read TEXTURES
452 if (yg_texture_format != YGTF_NAMELESS)
453 {
454 const char *lump_name = "TEXTURES";
455 bool success = false;
456 ntex = 0;
457 i32 *offset_table = 0;
458 MDirPtr d = FindMasterDir (MasterDir, lump_name);
459 if (! d)
460 {
461 warn ("%s: lump not found in directory\n", lump_name);
462 goto textures_done;
463 }
464 {
465 const Wad_file *wf = d->wadfile;
466 wf->seek (d->dir.start);
467 if (wf->error ())
468 {
469 warn ("%s: seek error\n", lump_name);
470 goto textures_done;
471 }
472 i32 num;
473 wf->read_i32 (&num);
474 if (wf->error ())
475 {
476 warn ("%s: error reading texture count\n", lump_name);
477 }
478 if (num < 0 || num > 32767)
479 {
480 warn ("%s: bad texture count, giving up\n", lump_name);
481 goto textures_done;
482 }
483 ntex = num;
484 offset_table = new i32[ntex];
485 for (size_t n = 0; n < ntex; n++)
486 {
487 wf->read_i32 (offset_table + n);
488 if (wf->error ())
489 {
490 warn ("%s: error reading offsets table\n");
491 goto textures_done;
492 }
493 }
494 tex_list = (char *) GetMemory (ntex * WAD_TEX_NAME);
495 for (size_t n = 0; n < ntex; n++)
496 {
497 const long offset = d->dir.start + offset_table[n];
498 wf->seek (offset);
499 if (wf->error ())
500 {
501 warn ("%s: seek error\n", lump_name);
502 goto textures_done;
503 }
504 wf->read_bytes (tex_list + WAD_TEX_NAME * n, WAD_TEX_NAME);
505 if (wf->error ())
506 {
507 warn ("%s: error reading texture names\n", lump_name);
508 goto textures_done;
509 }
510 }
511 success = true;
512 }
513
514 textures_done:
515 if (offset_table != 0)
516 delete[] offset_table;
517 if (! success)
518 warn ("%s: errors found, won't be able to import texture names\n",
519 lump_name);
520 }
521
522 const Wad_file *wf = dir->wadfile;
523 wf->seek (dir->dir.start + 4);
524 if (wf->error ())
525 {
526 err ("%s: seek error", lump_name);
527 rc = 1;
528 goto byebye;
529 }
530 size_t s = 0;
531 for (size_t n = 0; n < nlines; n++)
532 {
533 LDPtr ld = LineDefs + n;
534 ld->start = wf->read_i16 ();
535 ld->end = wf->read_i16 ();
536 ld->flags = wf->read_i16 ();
537 wf->read_i16 (); // Unused ?
538 ld->type = wf->read_i16 ();
539 ld->tag = wf->read_i16 ();
540 wf->read_i16 (); // Unused ?
541 i16 sector1 = wf->read_i16 ();
542 i16 xofs1 = wf->read_i16 ();
543 i16 tex1m = wf->read_i16 ();
544 i16 tex1u = wf->read_i16 ();
545 i16 tex1l = wf->read_i16 ();
546 wf->read_i16 (); // Unused ?
547 i16 sector2 = wf->read_i16 ();
548 i16 xofs2 = wf->read_i16 ();
549 i16 tex2m = wf->read_i16 ();
550 i16 tex2u = wf->read_i16 ();
551 i16 tex2l = wf->read_i16 ();
552 if (sector1 >= 0) // Create first sidedef
553 {
554 ld->sidedef1 = s;
555 SDPtr sd = SideDefs + s;
556 sd->xoff = xofs1;
557 sd->yoff = 0;
558 memcpy (sd->tex1, texno_texname (tex1u), sizeof sd->tex1);
559 memcpy (sd->tex2, texno_texname (tex1l), sizeof sd->tex2);
560 memcpy (sd->tex3, texno_texname (tex1m), sizeof sd->tex3);
561 sd->sector = sector1;
562 s++;
563 }
564 else // No first sidedef !
565 ld->sidedef1 = -1;
566 if (ld->flags & 0x04) // Create second sidedef
567 {
568 ld->sidedef2 = s;
569 SDPtr sd = SideDefs + s;
570 sd->xoff = xofs2;
571 sd->yoff = 0;
572 memcpy (sd->tex1, texno_texname (tex2u), sizeof sd->tex1);
573 memcpy (sd->tex2, texno_texname (tex2l), sizeof sd->tex2);
574 memcpy (sd->tex3, texno_texname (tex2m), sizeof sd->tex3);
575 sd->sector = sector2;
576 s++;
577 }
578 else
579 ld->sidedef2 = -1;
580 if (wf->error ())
581 {
582 err ("%s: error reading line #%d", lump_name, int (n));
583 rc = 1;
584 goto byebye;
585 }
586 }
587 // (size_t) to silence GCC warning
588 if ((size_t) NumSideDefs > s) // Almost always true.
589 {
590 NumSideDefs = s;
591 SideDefs = (SDPtr) ResizeFarMemory (SideDefs,
592 (unsigned long) NumSideDefs * sizeof (struct SideDef));
593 }
594 if (tex_list)
595 FreeMemory (tex_list);
596 tex_list = 0;
597 ntex = 0;
598 }
599 }
600
601 /* Read the vertices. If the wad has been run through a nodes
602 builder, there is a bunch of vertices at the end that are not
603 used by any linedefs. Those vertices have been created by the
604 nodes builder for the segs. We ignore them, because they're
605 useless to the level designer. However, we do NOT ignore
606 unused vertices in the middle because that's where the
607 "string art" bug came from.
608
609 Note that there is absolutely no guarantee that the nodes
610 builders add their own vertices at the end, instead of at the
611 beginning or right in the middle, AFAIK. It's just that they
612 all seem to do that (1). What if some don't ? Well, we would
613 end up with many unwanted vertices in the level data. Nothing
614 that a good CheckCrossReferences() couldn't take care of. */
615 {
616 verbmsg (" vertices");
617 int last_used_vertex = -1;
618 for (long n = 0; n < NumLineDefs; n++)
619 {
620 last_used_vertex = y_max (last_used_vertex, LineDefs[n].start);
621 last_used_vertex = y_max (last_used_vertex, LineDefs[n].end);
622 }
623 NumVertices = last_used_vertex + 1;
624 // This block is only here to warn me if (1) is false.
625 {
626 bitvec_c vertex_used (OldNumVertices);
627 for (long n = 0; n < NumLineDefs; n++)
628 {
629 vertex_used.set (LineDefs[n].start);
630 vertex_used.set (LineDefs[n].end);
631 }
632 int unused = 0;
633 for (long n = 0; n <= last_used_vertex; n++)
634 {
635 if (! vertex_used.get (n))
636 unused++;
637 }
638 if (unused > 0)
639 {
640 warn ("this level has unused vertices in the middle.\n");
641 warn ("total %d, tail %d (%d%%), unused %d (",
642 OldNumVertices,
643 OldNumVertices - NumVertices,
644 NumVertices - unused
645 ? 100 * (OldNumVertices - NumVertices) / (NumVertices - unused)
646 : 0,
647 unused);
648 int first = 1;
649 for (int n = 0; n <= last_used_vertex; n++)
650 {
651 if (! vertex_used.get (n))
652 {
653 if (n == 0 || vertex_used.get (n - 1))
654 {
655 if (first)
656 first = 0;
657 else
658 warn (", ");
659 warn ("%d", n);
660 }
661 else if (n == last_used_vertex || vertex_used.get (n + 1))
662 warn ("-%d", n);
663 }
664 }
665 warn (")\n");
666 }
667 }
668 // Now load all the vertices except the unused ones at the end.
669 if (NumVertices > 0)
670 {
671 const char *lump_name = "BUG";
672 Vertices = (VPtr) GetFarMemory ((unsigned long) NumVertices
673 * sizeof (struct Vertex));
674 if (yg_level_format == YGLF_ALPHA) // Doom alpha
675 lump_name = "POINTS";
676 else
677 lump_name = "VERTEXES";
678 dir = FindMasterDir (Level, lump_name);
679 if (dir == 0)
680 goto vertexes_done; // FIXME isn't that fatal ?
681 {
682 const Wad_file *wf = dir->wadfile;
683 wf->seek (v_offset);
684 if (wf->error ())
685 {
686 err ("%s: seek error", lump_name);
687 rc = 1;
688 goto byebye;
689 }
690 MapMaxX = -32767;
691 MapMaxY = -32767;
692 MapMinX = 32767;
693 MapMinY = 32767;
694 for (long n = 0; n < NumVertices; n++)
695 {
696 i16 val;
697 wf->read_i16 (&val);
698 if (val < MapMinX)
699 MapMinX = val;
700 if (val > MapMaxX)
701 MapMaxX = val;
702 Vertices[n].x = val;
703 wf->read_i16 (&val);
704 if (val < MapMinY)
705 MapMinY = val;
706 if (val > MapMaxY)
707 MapMaxY = val;
708 Vertices[n].y = val;
709 if (wf->error ())
710 {
711 err ("%s: error reading vertex #%ld", lump_name, n);
712 rc = 1;
713 goto byebye;
714 }
715 }
716 }
717 vertexes_done:
718 ;
719 }
720 }
721
722 // Ignore SEGS, SSECTORS and NODES
723
724 // Read SECTORS
725 {
726 const char *lump_name = "SECTORS";
727 verbmsg (" sectors\n");
728 dir = FindMasterDir (Level, lump_name);
729 if (yg_level_format != YGLF_ALPHA)
730 {
731 if (dir)
732 {
733 NumSectors = (int) (dir->dir.size / WAD_SECTOR_BYTES);
734 if ((i32) (NumSectors * WAD_SECTOR_BYTES) != dir->dir.size)
735 warn ("the %s lump has a weird size."
736 " The wad might be corrupt.\n", lump_name);
737 }
738 else
739 NumSectors = 0;
740 if (NumSectors > 0)
741 {
742 Sectors = (SPtr) GetFarMemory ((unsigned long) NumSectors
743 * sizeof (struct Sector));
744 const Wad_file *wf = dir->wadfile;
745 wf->seek (dir->dir.start);
746 if (wf->error ())
747 {
748 err ("%s: seek error", lump_name);
749 rc = 1;
750 goto byebye;
751 }
752 for (long n = 0; n < NumSectors; n++)
753 {
754 wf->read_i16 (&Sectors[n].floorh);
755 wf->read_i16 (&Sectors[n].ceilh);
756 wf->read_bytes (&Sectors[n].floort, WAD_FLAT_NAME);
757 wf->read_bytes (&Sectors[n].ceilt, WAD_FLAT_NAME);
758 wf->read_i16 (&Sectors[n].light);
759 wf->read_i16 (&Sectors[n].special);
760 wf->read_i16 (&Sectors[n].tag);
761 if (wf->error ())
762 {
763 err ("%s: error reading sector #%ld", lump_name, n);
764 rc = 1;
765 goto byebye;
766 }
767 }
768 }
769 }
770 else // Doom alpha--a wholly different SECTORS format
771 {
772 i32 *offset_table = 0;
773 i32 nsectors = 0;
774 i32 nflatnames = 0;
775 char *flatnames = 0;
776 if (dir == 0)
777 {
778 warn ("%s: lump not found in directory\n", lump_name); // FIXME fatal ?
779 goto sectors_alpha_done;
780 }
781 {
782 const Wad_file *wf = dir->wadfile;
783 wf->seek (dir->dir.start);
784 if (wf->error ())
785 {
786 err ("%s: seek error", lump_name);
787 rc = 1;
788 goto byebye;
789 }
790 wf->read_i32 (&nsectors);
791 if (wf->error ())
792 {
793 err ("%s: error reading sector count", lump_name);
794 rc = 1;
795 goto byebye;
796 }
797 if (nsectors < 0)
798 {
799 warn ("Negative sector count. Clamping to 0.\n");
800 nsectors = 0;
801 }
802 NumSectors = nsectors;
803 Sectors = (SPtr) GetFarMemory ((unsigned long) NumSectors
804 * sizeof (struct Sector));
805 offset_table = new i32[nsectors];
806 for (size_t n = 0; n < (size_t) nsectors; n++)
807 wf->read_i32 (offset_table + n);
808 if (wf->error ())
809 {
810 err ("%s: error reading offsets table", lump_name);
811 rc = 1;
812 goto sectors_alpha_done;
813 }
814 // Load FLATNAME
815 {
816 const char *lump_name = "FLATNAME";
817 bool success = false;
818 MDirPtr dir2 = FindMasterDir (Level, lump_name);
819 if (dir2 == 0)
820 {
821 warn ("%s: lump not found in directory\n", lump_name);
822 goto flatname_done; // FIXME warn ?
823 }
824 {
825 const Wad_file *wf = dir2->wadfile;
826 wf->seek (dir2->dir.start);
827 if (wf->error ())
828 {
829 warn ("%s: seek error\n", lump_name);
830 goto flatname_done;
831 }
832 wf->read_i32 (&nflatnames);
833 if (wf->error ())
834 {
835 warn ("%s: error reading flat name count\n", lump_name);
836 nflatnames = 0;
837 goto flatname_done;
838 }
839 if (nflatnames < 0 || nflatnames > 32767)
840 {
841 warn ("%s: bad flat name count, giving up\n", lump_name);
842 nflatnames = 0;
843 goto flatname_done;
844 }
845 else
846 {
847 flatnames = new char[WAD_FLAT_NAME * nflatnames];
848 wf->read_bytes (flatnames, WAD_FLAT_NAME * nflatnames);
849 if (wf->error ())
850 {
851 warn ("%s: error reading flat names\n", lump_name);
852 nflatnames = 0;
853 goto flatname_done;
854 }
855 success = true;
856 }
857 }
858 flatname_done:
859 if (! success)
860 warn ("%s: errors found, you'll have to do without flat names\n",
861 lump_name);
862 }
863 for (size_t n = 0; n < (size_t) nsectors; n++)
864 {
865 wf->seek (dir->dir.start + offset_table[n]);
866 if (wf->error ())
867 {
868 err ("%s: seek error", lump_name);
869 rc = 1;
870 goto sectors_alpha_done;
871 }
872 i16 index;
873 wf->read_i16 (&Sectors[n].floorh);
874 wf->read_i16 (&Sectors[n].ceilh );
875 wf->read_i16 (&index);
876 if (nflatnames && flatnames && index >= 0 && index < nflatnames)
877 memcpy (Sectors[n].floort, flatnames + WAD_FLAT_NAME * index,
878 WAD_FLAT_NAME);
879 else
880 strcpy (Sectors[n].floort, "unknown");
881 wf->read_i16 (&index);
882 if (nflatnames && flatnames && index >= 0 && index < nflatnames)
883 memcpy (Sectors[n].ceilt, flatnames + WAD_FLAT_NAME * index,
884 WAD_FLAT_NAME);
885 else
886 strcpy (Sectors[n].ceilt, "unknown");
887 wf->read_i16 (&Sectors[n].light);
888 wf->read_i16 (&Sectors[n].special);
889 wf->read_i16 (&Sectors[n].tag);
890 // Don't know what the tail is for. Ignore it.
891 if (wf->error ())
892 {
893 err ("%s: error reading sector #%ld", lump_name, long (n));
894 rc = 1;
895 goto sectors_alpha_done;
896 }
897 }
898 }
899
900 sectors_alpha_done:
901 if (offset_table != 0)
902 delete[] offset_table;
903 if (flatnames != 0)
904 delete[] flatnames;
905 if (rc != 0)
906 goto byebye;
907 }
908 }
909
910 /* Sanity checking on sidedefs: the sector must exist. I don't
911 make this a fatal error, though, because it's not exceptional
912 to find wads with unused sidedefs with a sector# of -1. Well
913 known ones include dyst3 (MAP06, MAP07, MAP08), mm (MAP16),
914 mm2 (MAP13, MAP28) and requiem (MAP03, MAP08, ...). */
915 for (long n = 0; n < NumSideDefs; n++)
916 {
917 if (outside (SideDefs[n].sector, 0, NumSectors - 1))
918 warn ("sidedef %ld has bad sector number %d\n",
919 n, SideDefs[n].sector);
920 }
921
922 // Ignore REJECT and BLOCKMAP
923
924 // Silly statistics
925 verbmsg (" %d things, %d vertices, %d linedefs, %d sidedefs, %d sectors\n",
926 (int) NumThings, (int) NumVertices, (int) NumLineDefs,
927 (int) NumSideDefs, (int) NumSectors);
928 verbmsg (" Map: (%d,%d)-(%d,%d)\n", MapMinX, MapMinY, MapMaxX, MapMaxY);
929
930 byebye:
931 if (rc != 0)
932 err ("%s: errors found, giving up", levelname);
933 return rc;
934 }
935
936
937
938 /*
939 forget the level data
940 */
941
ForgetLevelData()942 void ForgetLevelData () /* SWAP! */
943 {
944 /* forget the things */
945 ObjectsNeeded (OBJ_THINGS, 0);
946 NumThings = 0;
947 if (Things != 0)
948 FreeFarMemory (Things);
949 Things = 0;
950 things_angles++;
951 things_types++;
952
953 /* forget the vertices */
954 ObjectsNeeded (OBJ_VERTICES, 0);
955 NumVertices = 0;
956 if (Vertices != 0)
957 FreeFarMemory (Vertices);
958 Vertices = 0;
959
960 /* forget the linedefs */
961 ObjectsNeeded (OBJ_LINEDEFS, 0);
962 NumLineDefs = 0;
963 if (LineDefs != 0)
964 FreeFarMemory (LineDefs);
965 LineDefs = 0;
966
967 /* forget the sidedefs */
968 ObjectsNeeded (OBJ_SIDEDEFS, 0);
969 NumSideDefs = 0;
970 if (SideDefs != 0)
971 FreeFarMemory (SideDefs);
972 SideDefs = 0;
973
974 /* forget the sectors */
975 ObjectsNeeded (OBJ_SECTORS, 0);
976 NumSectors = 0;
977 if (Sectors != 0)
978 FreeFarMemory (Sectors);
979 Sectors = 0;
980 ObjectsNeeded (0);
981 }
982
983
984 /*
985 * Save the level data to a pwad file
986 * The name of the level is always obtained from
987 * <level_name>, whether or not the level was created from
988 * scratch.
989 *
990 * The previous contents of the pwad file are lost. Yes, it
991 * sucks but it's not easy to fix.
992 *
993 * The lumps are always written in the same order, the same
994 * as the one in the Doom iwad. The length field of the
995 * marker lump is always set to 0. Its offset field is
996 * always set to the offset of the first lump of the level
997 * (THINGS).
998 *
999 * If the level has been created by editing an existing
1000 * level and has not been changed in a way that calls for a
1001 * rebuild of the nodes, the VERTEXES, SEGS, SSECTORS,
1002 * NODES, REJECT and BLOCKMAP lumps are copied from the
1003 * original level. Otherwise, they are created with a
1004 * length of 0 bytes and an offset equal to the offset of
1005 * the previous lump plus its length.
1006 *
1007 * Returns 0 on success and non-zero on failure (see errno).
1008 */
SaveLevelData(const char * outfile,const char * level_name)1009 int SaveLevelData (const char *outfile, const char *level_name) /* SWAP! */
1010 {
1011 FILE *file;
1012 MDirPtr dir;
1013 int n;
1014 long lump_offset[WAD_LL__];
1015 size_t lump_size[WAD_LL__];
1016 wad_level_lump_no_t l;
1017
1018 if (yg_level_format == YGLF_HEXEN || ! strcmp (Game, "hexen"))
1019 {
1020 Notify (-1, -1, "I refuse to save. Hexen mode is still",
1021 "too badly broken. You would lose data.");
1022 return 1;
1023 }
1024 if (! level_name || ! levelname2levelno (level_name))
1025 {
1026 nf_bug ("SaveLevelData: bad level_name \"%s\", using \"E1M1\" instead.",
1027 level_name);
1028 level_name = "E1M1";
1029 }
1030 DisplayMessage (-1, -1, "Saving data to \"%s\"...", outfile);
1031 LogMessage (": Saving data to \"%s\"...\n", outfile);
1032 if ((file = fopen (outfile, "wb")) == NULL)
1033 {
1034 char buf1[81];
1035 char buf2[81];
1036 y_snprintf (buf1, sizeof buf1, "Can't open \"%.64s\"", outfile);
1037 y_snprintf (buf2, sizeof buf1, "for writing (%.64s)", strerror (errno));
1038 Notify (-1, -1, buf1, buf2);
1039 return 1;
1040 }
1041
1042 /* Can we reuse the old nodes ? Not if this is a new level from
1043 scratch or if the structure of the level has changed. If the
1044 level comes from an alpha version of Doom, we can't either
1045 because that version of Doom didn't have SEGS, NODES, etc. */
1046 bool reuse_nodes = Level
1047 && ! MadeMapChanges
1048 && yg_level_format != YGLF_ALPHA;
1049
1050 // Write the pwad header
1051 WriteBytes (file, "PWAD", 4); // Pwad file
1052 file_write_i32 (file, WAD_LL__); // Number of entries = 11
1053 file_write_i32 (file, 0); // Fix this up later
1054 if (Level)
1055 dir = Level->next;
1056 else
1057 dir = 0; // Useless except to trap accidental dereferences
1058
1059 // The label (EnMm or MAPnm)
1060 l = WAD_LL_LABEL;
1061 lump_offset[l] = ftell (file); // By definition
1062 lump_size[l] = 0; // By definition
1063
1064 // Write the THINGS lump
1065 l = WAD_LL_THINGS;
1066 lump_offset[l] = ftell (file);
1067 ObjectsNeeded (OBJ_THINGS, 0);
1068 for (n = 0; n < NumThings; n++)
1069 {
1070 file_write_i16 (file, Things[n].xpos );
1071 file_write_i16 (file, Things[n].ypos );
1072 file_write_i16 (file, Things[n].angle);
1073 file_write_i16 (file, Things[n].type );
1074 file_write_i16 (file, Things[n].when );
1075 }
1076 lump_size[l] = ftell (file) - lump_offset[l];
1077 if (Level)
1078 dir = dir->next;
1079
1080 // Write the LINEDEFS lump
1081 l = WAD_LL_LINEDEFS;
1082 lump_offset[WAD_LL_LINEDEFS] = ftell (file);
1083 ObjectsNeeded (OBJ_LINEDEFS, 0);
1084 for (n = 0; n < NumLineDefs; n++)
1085 {
1086 file_write_i16 (file, LineDefs[n].start );
1087 file_write_i16 (file, LineDefs[n].end );
1088 file_write_i16 (file, LineDefs[n].flags );
1089 file_write_i16 (file, LineDefs[n].type );
1090 file_write_i16 (file, LineDefs[n].tag );
1091 file_write_i16 (file, LineDefs[n].sidedef1);
1092 file_write_i16 (file, LineDefs[n].sidedef2);
1093 }
1094 lump_size[l] = ftell (file) - lump_offset[l];
1095 if (Level)
1096 dir = dir->next;
1097
1098 // Write the SIDEDEFS lump
1099 l = WAD_LL_SIDEDEFS;
1100 lump_offset[l] = ftell (file);
1101 ObjectsNeeded (OBJ_SIDEDEFS, 0);
1102 for (n = 0; n < NumSideDefs; n++)
1103 {
1104 file_write_i16 (file, SideDefs[n].xoff);
1105 file_write_i16 (file, SideDefs[n].yoff);
1106 WriteBytes (file, &(SideDefs[n].tex1), WAD_TEX_NAME);
1107 WriteBytes (file, &(SideDefs[n].tex2), WAD_TEX_NAME);
1108 WriteBytes (file, &(SideDefs[n].tex3), WAD_TEX_NAME);
1109 file_write_i16 (file, SideDefs[n].sector);
1110 }
1111 lump_size[l] = ftell (file) - lump_offset[l];
1112 if (Level)
1113 dir = dir->next;
1114
1115 // Write the VERTEXES lump
1116 l = WAD_LL_VERTEXES;
1117 lump_offset[WAD_LL_VERTEXES] = ftell (file);
1118 if (reuse_nodes)
1119 {
1120 /* Copy the vertices */
1121 ObjectsNeeded (0);
1122 const Wad_file *wf = dir->wadfile;
1123 wf->seek (dir->dir.start);
1124 if (wf->error ())
1125 {
1126 warn ("%s: seek error\n", wad_level_lump[l]);
1127 }
1128 copy_bytes (file, wf->fp, dir->dir.size);
1129 }
1130 else
1131 {
1132 /* Write the vertices */
1133 ObjectsNeeded (OBJ_VERTICES, 0);
1134 for (n = 0; n < NumVertices; n++)
1135 {
1136 file_write_i16 (file, Vertices[n].x);
1137 file_write_i16 (file, Vertices[n].y);
1138 }
1139 }
1140 lump_size[l] = ftell (file) - lump_offset[l];
1141 if (Level)
1142 dir = dir->next;
1143
1144 // Write the SEGS, SSECTORS and NODES lumps
1145 for (n = 0; n < 3; n++)
1146 {
1147 if (n == 0)
1148 l = WAD_LL_SEGS;
1149 else if (n == 1)
1150 l = WAD_LL_SSECTORS;
1151 else if (n == 2)
1152 l = WAD_LL_NODES;
1153 lump_offset[l] = ftell (file);
1154 if (reuse_nodes)
1155 {
1156 const Wad_file *wf = dir->wadfile;
1157 wf->seek (dir->dir.start);
1158 if (wf->error ())
1159 {
1160 warn ("%s: seek error\n", wad_level_lump[l]);
1161 }
1162 copy_bytes (file, wf->fp, dir->dir.size);
1163 }
1164 lump_size[l] = ftell (file) - lump_offset[l];
1165 if (Level)
1166 dir = dir->next;
1167 }
1168
1169 // Write the SECTORS lump
1170 l = WAD_LL_SECTORS;
1171 lump_offset[l] = ftell (file);
1172 ObjectsNeeded (OBJ_SECTORS, 0);
1173 for (n = 0; n < NumSectors; n++)
1174 {
1175 file_write_i16 (file, Sectors[n].floorh);
1176 file_write_i16 (file, Sectors[n].ceilh );
1177 WriteBytes (file, Sectors[n].floort, WAD_FLAT_NAME);
1178 WriteBytes (file, Sectors[n].ceilt, WAD_FLAT_NAME);
1179 file_write_i16 (file, Sectors[n].light );
1180 file_write_i16 (file, Sectors[n].special);
1181 file_write_i16 (file, Sectors[n].tag );
1182 }
1183 lump_size[l] = ftell (file) - lump_offset[l];
1184 if (Level)
1185 dir = dir->next;
1186
1187 // Write the REJECT lump
1188 l = WAD_LL_REJECT;
1189 lump_offset[l] = ftell (file);
1190 if (reuse_nodes)
1191 {
1192 /* Copy the REJECT data */
1193 ObjectsNeeded (0);
1194 const Wad_file *wf = dir->wadfile;
1195 wf->seek (dir->dir.start);
1196 if (wf->error ())
1197 {
1198 warn ("%s: seek error\n", wad_level_lump[l]);
1199 }
1200 copy_bytes (file, wf->fp, dir->dir.size);
1201 }
1202 lump_size[l] = ftell (file) - lump_offset[l];
1203 if (Level)
1204 dir = dir->next;
1205
1206 // Write the BLOCKMAP lump
1207 l = WAD_LL_BLOCKMAP;
1208 lump_offset[l] = ftell (file);
1209 if (reuse_nodes)
1210 {
1211 ObjectsNeeded (0);
1212 const Wad_file *wf = dir->wadfile;
1213 wf->seek (dir->dir.start);
1214 if (wf->error ())
1215 {
1216 warn ("%s: seek error\n", wad_level_lump[l]);
1217 }
1218 copy_bytes (file, wf->fp, dir->dir.size);
1219 }
1220 lump_size[l] = ftell (file) - lump_offset[l];
1221 if (Level)
1222 dir = dir->next;
1223
1224 // Write the actual directory
1225 long dir_offset = ftell (file);
1226 for (int L = 0; L < (int) WAD_LL__; L++)
1227 {
1228 file_write_i32 (file, lump_offset[L]);
1229 file_write_i32 (file, lump_size[L]);
1230 if (L == (int) WAD_LL_LABEL)
1231 file_write_name (file, level_name);
1232 else
1233 file_write_name (file, wad_level_lump[L].name);
1234 }
1235
1236 /* Fix up the directory start information */
1237 if (fseek (file, 8, SEEK_SET))
1238 {
1239 char buf1[81];
1240 char buf2[81];
1241 y_snprintf (buf1, sizeof buf1, "%.64s: seek error", outfile);
1242 y_snprintf (buf2, sizeof buf2, "(%.64s)", strerror (errno));
1243 Notify (-1, -1, buf1, buf2);
1244 fclose (file);
1245 return 1;
1246 }
1247 file_write_i32 (file, dir_offset);
1248
1249 /* Close the file */
1250 if (fclose (file))
1251 {
1252 char buf1[81];
1253 char buf2[81];
1254 y_snprintf (buf1, sizeof buf1, "%.64s: write error", outfile);
1255 y_snprintf (buf2, sizeof buf2, "(%.64s)", strerror (errno));
1256 Notify (-1, -1, buf1, buf2);
1257 return 1;
1258 }
1259
1260 /* The file is now up to date */
1261 if (! Level || MadeMapChanges)
1262 remind_to_build_nodes = 1;
1263 MadeChanges = 0;
1264 MadeMapChanges = 0;
1265 ObjectsNeeded (0);
1266
1267 /* Update pointers in Master Directory */
1268 OpenPatchWad (outfile);
1269
1270 /* This should free the old "*.bak" file */
1271 CloseUnusedWadFiles ();
1272
1273 /* Update MapMinX, MapMinY, MapMaxX, MapMaxY */
1274 // Probably not necessary anymore -- AYM 1999-04-05
1275 ObjectsNeeded (OBJ_VERTICES, 0);
1276 update_level_bounds ();
1277 return 0;
1278 }
1279
1280
1281 /*
1282 * flat_list_entry_cmp
1283 * Function used by qsort() to sort the flat_list_entry array
1284 * by ascending flat name.
1285 */
flat_list_entry_cmp(const void * a,const void * b)1286 static int flat_list_entry_cmp (const void *a, const void *b)
1287 {
1288 return y_strnicmp (
1289 ((const flat_list_entry_t *) a)->name,
1290 ((const flat_list_entry_t *) b)->name,
1291 WAD_FLAT_NAME);
1292 }
1293
1294
1295 /*
1296 function used by qsort to sort the texture names
1297 */
SortTextures(const void * a,const void * b)1298 static int SortTextures (const void *a, const void *b)
1299 {
1300 return y_strnicmp (*((const char *const *)a), *((const char *const *)b),
1301 WAD_TEX_NAME);
1302 }
1303
1304
1305 /*
1306 read the texture names
1307 */
ReadWTextureNames()1308 void ReadWTextureNames ()
1309 {
1310 MDirPtr dir;
1311 int n;
1312 i32 val;
1313
1314 verbmsg ("Reading texture names\n");
1315
1316 // Doom alpha 0.4 : "TEXTURES", no names
1317 if (yg_texture_lumps == YGTL_TEXTURES
1318 && yg_texture_format == YGTF_NAMELESS)
1319 {
1320 const char *lump_name = "TEXTURES";
1321 dir = FindMasterDir (MasterDir, lump_name);
1322 if (dir == NULL)
1323 {
1324 warn ("%s: lump not found in directory\n", lump_name);
1325 goto textures04_done;
1326 }
1327 {
1328 const Wad_file *wf = dir->wadfile;
1329 wf->seek (dir->dir.start);
1330 if (wf->error ())
1331 {
1332 warn ("%s: seek error\n", lump_name);
1333 goto textures04_done;
1334 }
1335 wf->read_i32 (&val);
1336 if (wf->error ())
1337 {
1338 warn ("%s: error reading texture count\n", lump_name);
1339 goto textures04_done;
1340 }
1341 NumWTexture = (int) val + 1;
1342 WTexture = (char **) GetMemory ((long) NumWTexture * sizeof *WTexture);
1343 WTexture[0] = (char *) GetMemory (WAD_TEX_NAME + 1);
1344 strcpy (WTexture[0], "-");
1345 if (WAD_TEX_NAME < 7) nf_bug ("WAD_TEX_NAME too small"); // Sanity
1346 for (long n = 0; n < val; n++)
1347 {
1348 WTexture[n + 1] = (char *) GetMemory (WAD_TEX_NAME + 1);
1349 if (n > 9999)
1350 {
1351 warn ("more than 10,000 textures. Ignoring excess.\n");
1352 break;
1353 }
1354 sprintf (WTexture[n + 1], "TEX%04ld", n);
1355 }
1356 }
1357 textures04_done:
1358 ;
1359 }
1360
1361 // Doom alpha 0.5 : only "TEXTURES"
1362 else if (yg_texture_lumps == YGTL_TEXTURES
1363 && (yg_texture_format == YGTF_NORMAL
1364 || yg_texture_format == YGTF_STRIFE11))
1365 {
1366 const char *lump_name = "TEXTURES";
1367 i32 *offsets = 0;
1368 dir = FindMasterDir (MasterDir, lump_name);
1369 if (dir == NULL) // In theory it always exists, though
1370 {
1371 warn ("%s: lump not found in directory\n", lump_name);
1372 goto textures05_done;
1373 }
1374 {
1375 const Wad_file *wf = dir->wadfile;
1376 wf->seek (dir->dir.start);
1377 if (wf->error ())
1378 {
1379 warn ("%s: seek error\n", lump_name);
1380 goto textures05_done;
1381 }
1382 wf->read_i32 (&val);
1383 if (wf->error ())
1384 {
1385 warn ("%s: error reading texture count\n", lump_name);
1386 goto textures05_done;
1387 }
1388 NumWTexture = (int) val + 1;
1389 /* read in the offsets for texture1 names */
1390 offsets = (i32 *) GetMemory ((long) NumWTexture * 4);
1391 wf->read_i32 (offsets + 1, NumWTexture - 1);
1392 if (wf->error ())
1393 {
1394 warn ("%s: error reading offsets table\n", lump_name);
1395 goto textures05_done;
1396 }
1397 /* read in the actual names */
1398 WTexture = (char **) GetMemory ((long) NumWTexture * sizeof (char *));
1399 WTexture[0] = (char *) GetMemory (WAD_TEX_NAME + 1);
1400 strcpy (WTexture[0], "-");
1401 for (n = 1; n < NumWTexture; n++)
1402 {
1403 WTexture[n] = (char *) GetMemory (WAD_TEX_NAME + 1);
1404 long offset = dir->dir.start + offsets[n];
1405 wf->seek (offset);
1406 if (wf->error ())
1407 {
1408 warn ("%s: error seeking to error\n", lump_name);
1409 goto textures05_done; // FIXME cleanup
1410 }
1411 wf->read_bytes (WTexture[n], WAD_TEX_NAME);
1412 if (wf->error ())
1413 {
1414 warn ("%s: error reading texture names\n", lump_name);
1415 goto textures05_done; // FIXME cleanup
1416 }
1417 WTexture[n][WAD_TEX_NAME] = '\0';
1418 }
1419 }
1420 textures05_done:
1421 if (offsets != 0)
1422 FreeMemory (offsets);
1423 }
1424 // Other iwads : "TEXTURE1" and possibly "TEXTURE2"
1425 else if (yg_texture_lumps == YGTL_NORMAL
1426 && (yg_texture_format == YGTF_NORMAL
1427 || yg_texture_format == YGTF_STRIFE11))
1428 {
1429 const char *lump_name = "TEXTURE1";
1430 i32 *offsets = 0;
1431 dir = FindMasterDir (MasterDir, lump_name);
1432 if (dir != NULL) // In theory it always exists, though
1433 {
1434 const Wad_file *wf = dir->wadfile;
1435 wf->seek (dir->dir.start);
1436 if (wf->error ())
1437 {
1438 warn ("%s: seek error\n", lump_name);
1439 // FIXME
1440 }
1441 wf->read_i32 (&val);
1442 if (wf->error ())
1443 {
1444 // FIXME
1445 }
1446 NumWTexture = (int) val + 1;
1447 /* read in the offsets for texture1 names */
1448 offsets = (i32 *) GetMemory ((long) NumWTexture * 4);
1449 wf->read_i32 (offsets + 1, NumWTexture - 1);
1450 {
1451 // FIXME
1452 }
1453 /* read in the actual names */
1454 WTexture = (char **) GetMemory ((long) NumWTexture * sizeof (char *));
1455 WTexture[0] = (char *) GetMemory (WAD_TEX_NAME + 1);
1456 strcpy (WTexture[0], "-");
1457 for (n = 1; n < NumWTexture; n++)
1458 {
1459 WTexture[n] = (char *) GetMemory (WAD_TEX_NAME + 1);
1460 wf->seek (dir->dir.start + offsets[n]);
1461 if (wf->error ())
1462 {
1463 warn ("%s: seek error\n", lump_name);
1464 // FIXME
1465 }
1466 wf->read_bytes (WTexture[n], WAD_TEX_NAME);
1467 if (wf->error ())
1468 {
1469 // FIXME
1470 }
1471 WTexture[n][WAD_TEX_NAME] = '\0';
1472 }
1473 FreeMemory (offsets);
1474 }
1475 {
1476 dir = FindMasterDir (MasterDir, "TEXTURE2");
1477 if (dir) /* Doom II has no TEXTURE2 */
1478 {
1479 const Wad_file *wf = dir->wadfile;
1480 wf->seek (dir->dir.start);
1481 if (wf->error ())
1482 {
1483 warn ("%s: seek error\n", lump_name);
1484 // FIXME
1485 }
1486 wf->read_i32 (&val);
1487 if (wf->error ())
1488 {
1489 // FIXME
1490 }
1491 /* read in the offsets for texture2 names */
1492 offsets = (i32 *) GetMemory ((long) val * 4);
1493 wf->read_i32 (offsets, val);
1494 if (wf->error ())
1495 {
1496 // FIXME
1497 }
1498 /* read in the actual names */
1499 WTexture = (char **) ResizeMemory (WTexture,
1500 (NumWTexture + val) * sizeof (char *));
1501 for (n = 0; n < val; n++)
1502 {
1503 WTexture[NumWTexture + n] = (char *) GetMemory (WAD_TEX_NAME + 1);
1504 wf->seek (dir->dir.start + offsets[n]);
1505 if (wf->error ())
1506 {
1507 warn ("%s: seek error\n", lump_name);
1508 // FIXME
1509 }
1510 wf->read_bytes (WTexture[NumWTexture + n], WAD_TEX_NAME);
1511 if (wf->error ())
1512 ; // FIXME
1513 WTexture[NumWTexture + n][WAD_TEX_NAME] = '\0';
1514 }
1515 NumWTexture += val;
1516 FreeMemory (offsets);
1517 }
1518 }
1519 }
1520 else
1521 nf_bug ("Invalid texture_format/texture_lumps combination.");
1522
1523 /* sort the names */
1524 qsort (WTexture, NumWTexture, sizeof (char *), SortTextures);
1525 }
1526
1527
1528
1529 /*
1530 forget the texture names
1531 */
1532
ForgetWTextureNames()1533 void ForgetWTextureNames ()
1534 {
1535 int n;
1536
1537 /* forget all names */
1538 for (n = 0; n < NumWTexture; n++)
1539 FreeMemory (WTexture[n]);
1540
1541 /* forget the array */
1542 NumWTexture = 0;
1543 FreeMemory (WTexture);
1544 }
1545
1546
1547
1548 /*
1549 read the flat names
1550 */
1551
ReadFTextureNames()1552 void ReadFTextureNames ()
1553 {
1554 MDirPtr dir;
1555 int n;
1556
1557 verbmsg ("Reading flat names");
1558 NumFTexture = 0;
1559
1560 for (dir = MasterDir; (dir = FindMasterDir (dir, "F_START", "FF_START"));)
1561 {
1562 bool ff_start = ! y_strnicmp (dir->dir.name, "FF_START", WAD_NAME);
1563 MDirPtr dir0;
1564 /* count the names */
1565 dir = dir->next;
1566 dir0 = dir;
1567 for (n = 0; dir && y_strnicmp (dir->dir.name, "F_END", WAD_NAME)
1568 && (! ff_start || y_strnicmp (dir->dir.name, "FF_END", WAD_NAME));
1569 dir = dir->next)
1570 {
1571 if (dir->dir.start == 0 || dir->dir.size == 0)
1572 {
1573 if (! (toupper (dir->dir.name[0]) == 'F'
1574 && (dir->dir.name[1] == '1'
1575 || dir->dir.name[1] == '2'
1576 || dir->dir.name[1] == '3'
1577 || toupper (dir->dir.name[1]) == 'F')
1578 && dir->dir.name[2] == '_'
1579 && (! y_strnicmp (dir->dir.name + 3, "START", WAD_NAME - 3)
1580 || ! y_strnicmp (dir->dir.name + 3, "END", WAD_NAME - 3))))
1581 warn ("unexpected label \"%.*s\" among flats.\n",
1582 WAD_NAME, dir->dir.name);
1583 continue;
1584 }
1585 if (dir->dir.size != 4096)
1586 warn ("flat \"%.*s\" has weird size %lu."
1587 " Using 4096 instead.\n",
1588 WAD_NAME, dir->dir.name, (unsigned long) dir->dir.size);
1589 n++;
1590 }
1591 /* If FF_START/FF_END followed by F_END (mm2.wad), advance
1592 past F_END. In fact, this does not work because the F_END
1593 that follows has been snatched by OpenPatchWad(), that
1594 thinks it replaces the F_END from the iwad. OpenPatchWad()
1595 needs to be kludged to take this special case into
1596 account. Fortunately, the only consequence is a useless
1597 "this wad uses FF_END" warning. -- AYM 1999-07-10 */
1598 if (ff_start && dir && ! y_strnicmp (dir->dir.name, "FF_END", WAD_NAME))
1599 if (dir->next && ! y_strnicmp (dir->next->dir.name, "F_END", WAD_NAME))
1600 dir = dir->next;
1601
1602 verbmsg (" FF_START/%s %d", dir->dir.name, n);
1603 if (dir && ! y_strnicmp (dir->dir.name, "FF_END", WAD_NAME))
1604 warn ("this wad uses FF_END. That won't work with Doom."
1605 " Use F_END instead.\n");
1606 /* get the actual names from master dir. */
1607 flat_list = (flat_list_entry_t *) ResizeMemory (flat_list,
1608 (NumFTexture + n) * sizeof *flat_list);
1609 for (size_t m = NumFTexture; m < NumFTexture + n; dir0 = dir0->next)
1610 {
1611 // Skip all labels.
1612 if (dir0->dir.start == 0
1613 || dir0->dir.size == 0
1614 || (toupper (dir0->dir.name[0]) == 'F'
1615 && (dir0->dir.name[1] == '1'
1616 || dir0->dir.name[1] == '2'
1617 || dir0->dir.name[1] == '3'
1618 || toupper (dir0->dir.name[1]) == 'F')
1619 && dir0->dir.name[2] == '_'
1620 && (! y_strnicmp (dir0->dir.name + 3, "START", WAD_NAME - 3)
1621 || ! y_strnicmp (dir0->dir.name + 3, "END", WAD_NAME - 3))
1622 ))
1623 continue;
1624 *flat_list[m].name = '\0';
1625 strncat (flat_list[m].name, dir0->dir.name, sizeof flat_list[m].name - 1);
1626 flat_list[m].wadfile = dir0->wadfile;
1627 flat_list[m].offset = dir0->dir.start;
1628 m++;
1629 }
1630 NumFTexture += n;
1631 }
1632
1633 verbmsg ("\n");
1634
1635 /* sort the flats by names */
1636 qsort (flat_list, NumFTexture, sizeof *flat_list, flat_list_entry_cmp);
1637
1638 /* Eliminate all but the last duplicates of a flat. Suboptimal.
1639 Would be smarter to start by the end. */
1640 for (size_t n = 0; n < NumFTexture; n++)
1641 {
1642 size_t m = n;
1643 while (m + 1 < NumFTexture
1644 && ! flat_list_entry_cmp (flat_list + n, flat_list + m + 1))
1645 m++;
1646 // m now contains the index of the last duplicate
1647 int nduplicates = m - n;
1648 if (nduplicates > 0)
1649 {
1650 memmove (flat_list + n, flat_list + m,
1651 (NumFTexture - m) * sizeof *flat_list);
1652 NumFTexture -= nduplicates;
1653 // Note that I'm too lazy to resize flat_list...
1654 }
1655 }
1656 }
1657
1658
1659 /*
1660 * is_flat_name_in_list
1661 * FIXME should use bsearch()
1662 */
is_flat_name_in_list(const char * name)1663 int is_flat_name_in_list (const char *name)
1664 {
1665 if (! flat_list)
1666 return 0;
1667 for (size_t n = 0; n < NumFTexture; n++)
1668 if (! y_strnicmp (name, flat_list[n].name, WAD_FLAT_NAME))
1669 return 1;
1670 return 0;
1671 }
1672
1673
1674 /*
1675 forget the flat names
1676 */
1677
ForgetFTextureNames()1678 void ForgetFTextureNames ()
1679 {
1680 NumFTexture = 0;
1681 FreeMemory (flat_list);
1682 flat_list = 0;
1683 }
1684
1685
1686 /*
1687 * update_level_bounds - update Map{Min,Max}{X,Y}
1688 */
update_level_bounds()1689 void update_level_bounds ()
1690 {
1691 MapMaxX = -32767;
1692 MapMaxY = -32767;
1693 MapMinX = 32767;
1694 MapMinY = 32767;
1695 for (obj_no_t n = 0; n < NumVertices; n++)
1696 {
1697 int x = Vertices[n].x;
1698 if (x < MapMinX)
1699 MapMinX = x;
1700 if (x > MapMaxX)
1701 MapMaxX = x;
1702 int y = Vertices[n].y;
1703 if (y < MapMinY)
1704 MapMinY = y;
1705 if (y > MapMaxY)
1706 MapMaxY = y;
1707 }
1708 }
1709
1710