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