1 // SONIC ROBO BLAST 2
2 //-----------------------------------------------------------------------------
3 // Copyright (C) 1993-1996 by id Software, Inc.
4 // Copyright (C) 1998-2000 by DooM Legacy Team.
5 // Copyright (C) 1999-2020 by Sonic Team Junior.
6 //
7 // This program is free software distributed under the
8 // terms of the GNU General Public License, version 2.
9 // See the 'LICENSE' file for more details.
10 //-----------------------------------------------------------------------------
11 /// \file  p_setup.c
12 /// \brief Do all the WAD I/O, get map description, set up initial state and misc. LUTs
13 
14 #include "doomdef.h"
15 #include "d_main.h"
16 #include "byteptr.h"
17 #include "g_game.h"
18 
19 #include "p_local.h"
20 #include "p_setup.h"
21 #include "p_spec.h"
22 #include "p_saveg.h"
23 
24 #include "i_sound.h" // for I_PlayCD()..
25 #include "i_video.h" // for I_FinishUpdate()..
26 #include "r_sky.h"
27 #include "i_system.h"
28 
29 #include "r_data.h"
30 #include "r_things.h" // for R_AddSpriteDefs
31 #include "r_textures.h"
32 #include "r_patch.h"
33 #include "r_picformats.h"
34 #include "r_sky.h"
35 #include "r_draw.h"
36 
37 #include "s_sound.h"
38 #include "st_stuff.h"
39 #include "w_wad.h"
40 #include "z_zone.h"
41 #include "r_splats.h"
42 
43 #include "hu_stuff.h"
44 #include "console.h"
45 
46 #include "m_misc.h"
47 #include "m_fixed.h"
48 #include "m_random.h"
49 
50 #include "dehacked.h" // for map headers
51 #include "r_main.h"
52 #include "m_cond.h" // for emblems
53 
54 #include "m_argv.h"
55 
56 #include "p_polyobj.h"
57 
58 #include "v_video.h"
59 
60 #include "filesrch.h" // refreshdirmenu
61 
62 #include "lua_hud.h" // level title
63 
64 #include "f_finale.h" // wipes
65 
66 #include "md5.h" // map MD5
67 
68 // for LUAh_MapLoad
69 #include "lua_script.h"
70 #include "lua_hook.h"
71 
72 #ifdef _WIN32
73 #include <malloc.h>
74 #include <math.h>
75 #endif
76 #ifdef HWRENDER
77 #include "hardware/hw_main.h"
78 #include "hardware/hw_light.h"
79 #include "hardware/hw_model.h"
80 #endif
81 
82 #include "p_slopes.h"
83 
84 #include "fastcmp.h" // textmap parsing
85 
86 #include "taglist.h"
87 
88 //
89 // Map MD5, calculated on level load.
90 // Sent to clients in PT_SERVERINFO.
91 //
92 unsigned char mapmd5[16];
93 
94 //
95 // MAP related Lookup tables.
96 // Store VERTEXES, LINEDEFS, SIDEDEFS, etc.
97 //
98 
99 boolean udmf;
100 size_t numvertexes, numsegs, numsectors, numsubsectors, numnodes, numlines, numsides, nummapthings;
101 vertex_t *vertexes;
102 seg_t *segs;
103 sector_t *sectors;
104 subsector_t *subsectors;
105 node_t *nodes;
106 line_t *lines;
107 side_t *sides;
108 mapthing_t *mapthings;
109 sector_t *spawnsectors;
110 line_t *spawnlines;
111 side_t *spawnsides;
112 INT32 numstarposts;
113 UINT16 bossdisabled;
114 boolean stoppedclock;
115 boolean levelloading;
116 UINT8 levelfadecol;
117 
118 // BLOCKMAP
119 // Created from axis aligned bounding box
120 // of the map, a rectangular array of
121 // blocks of size ...
122 // Used to speed up collision detection
123 // by spatial subdivision in 2D.
124 //
125 // Blockmap size.
126 INT32 bmapwidth, bmapheight; // size in mapblocks
127 
128 INT32 *blockmap; // INT32 for large maps
129 // offsets in blockmap are from here
130 INT32 *blockmaplump; // Big blockmap
131 
132 // origin of block map
133 fixed_t bmaporgx, bmaporgy;
134 // for thing chains
135 mobj_t **blocklinks;
136 
137 // REJECT
138 // For fast sight rejection.
139 // Speeds up enemy AI by skipping detailed LineOf Sight calculation.
140 // Without special effect, this could be used as a PVS lookup as well.
141 //
142 UINT8 *rejectmatrix;
143 
144 // Maintain single and multi player starting spots.
145 INT32 numdmstarts, numcoopstarts, numredctfstarts, numbluectfstarts;
146 
147 mapthing_t *deathmatchstarts[MAX_DM_STARTS];
148 mapthing_t *playerstarts[MAXPLAYERS];
149 mapthing_t *bluectfstarts[MAXPLAYERS];
150 mapthing_t *redctfstarts[MAXPLAYERS];
151 
152 // Maintain waypoints
153 mobj_t *waypoints[NUMWAYPOINTSEQUENCES][WAYPOINTSEQUENCESIZE];
154 UINT16 numwaypoints[NUMWAYPOINTSEQUENCES];
155 
P_AddWaypoint(UINT8 sequence,UINT8 id,mobj_t * waypoint)156 void P_AddWaypoint(UINT8 sequence, UINT8 id, mobj_t *waypoint)
157 {
158 	waypoints[sequence][id] = waypoint;
159 	if (id >= numwaypoints[sequence])
160 		numwaypoints[sequence] = id + 1;
161 }
162 
P_ResetWaypoints(void)163 static void P_ResetWaypoints(void)
164 {
165 	UINT16 sequence, id;
166 	for (sequence = 0; sequence < NUMWAYPOINTSEQUENCES; sequence++)
167 	{
168 		for (id = 0; id < numwaypoints[sequence]; id++)
169 			waypoints[sequence][id] = NULL;
170 
171 		numwaypoints[sequence] = 0;
172 	}
173 }
174 
P_GetFirstWaypoint(UINT8 sequence)175 mobj_t *P_GetFirstWaypoint(UINT8 sequence)
176 {
177 	return waypoints[sequence][0];
178 }
179 
P_GetLastWaypoint(UINT8 sequence)180 mobj_t *P_GetLastWaypoint(UINT8 sequence)
181 {
182 	return waypoints[sequence][numwaypoints[sequence] - 1];
183 }
184 
P_GetPreviousWaypoint(mobj_t * current,boolean wrap)185 mobj_t *P_GetPreviousWaypoint(mobj_t *current, boolean wrap)
186 {
187 	UINT8 sequence = current->threshold;
188 	UINT8 id = current->health;
189 
190 	if (id == 0)
191 	{
192 		if (!wrap)
193 			return NULL;
194 
195 		id = numwaypoints[sequence] - 1;
196 	}
197 	else
198 		id--;
199 
200 	return waypoints[sequence][id];
201 }
202 
P_GetNextWaypoint(mobj_t * current,boolean wrap)203 mobj_t *P_GetNextWaypoint(mobj_t *current, boolean wrap)
204 {
205 	UINT8 sequence = current->threshold;
206 	UINT8 id = current->health;
207 
208 	if (id == numwaypoints[sequence] - 1)
209 	{
210 		if (!wrap)
211 			return NULL;
212 
213 		id = 0;
214 	}
215 	else
216 		id++;
217 
218 	return waypoints[sequence][id];
219 }
220 
P_GetClosestWaypoint(UINT8 sequence,mobj_t * mo)221 mobj_t *P_GetClosestWaypoint(UINT8 sequence, mobj_t *mo)
222 {
223 	UINT8 wp;
224 	mobj_t *mo2, *result = NULL;
225 	fixed_t bestdist = 0;
226 	fixed_t curdist;
227 
228 	for (wp = 0; wp < numwaypoints[sequence]; wp++)
229 	{
230 		mo2 = waypoints[sequence][wp];
231 
232 		if (!mo2)
233 			continue;
234 
235 		curdist = P_AproxDistance(P_AproxDistance(mo->x - mo2->x, mo->y - mo2->y), mo->z - mo2->z);
236 
237 		if (result && curdist > bestdist)
238 			continue;
239 
240 		result = mo2;
241 		bestdist = curdist;
242 	}
243 
244 	return result;
245 }
246 
247 // Return true if all waypoints are in the same location
P_IsDegeneratedWaypointSequence(UINT8 sequence)248 boolean P_IsDegeneratedWaypointSequence(UINT8 sequence)
249 {
250 	mobj_t *first, *waypoint;
251 	UINT8 wp;
252 
253 	if (numwaypoints[sequence] <= 1)
254 		return true;
255 
256 	first = waypoints[sequence][0];
257 
258 	for (wp = 1; wp < numwaypoints[sequence]; wp++)
259 	{
260 		waypoint = waypoints[sequence][wp];
261 
262 		if (!waypoint)
263 			continue;
264 
265 		if (waypoint->x != first->x)
266 			return false;
267 
268 		if (waypoint->y != first->y)
269 			return false;
270 
271 		if (waypoint->z != first->z)
272 			return false;
273 	}
274 
275 	return true;
276 }
277 
278 
279 /** Logs an error about a map being corrupt, then terminate.
280   * This allows reporting highly technical errors for usefulness, without
281   * confusing a novice map designer who simply needs to run ZenNode.
282   *
283   * If logging is disabled in this compile, or the log file is not opened, the
284   * full technical details are printed in the I_Error() message.
285   *
286   * \param msg The message to log. This message can safely result from a call
287   *            to va(), since that function is not used here.
288   * \todo Fix the I_Error() message. On some implementations the logfile may
289   *       not be called log.txt.
290   * \sa CON_LogMessage, I_Error
291   */
CorruptMapError(const char * msg)292 FUNCNORETURN static ATTRNORETURN void CorruptMapError(const char *msg)
293 {
294 	// don't use va() because the calling function probably uses it
295 	char mapnum[10];
296 
297 	sprintf(mapnum, "%hd", gamemap);
298 	CON_LogMessage("Map ");
299 	CON_LogMessage(mapnum);
300 	CON_LogMessage(" is corrupt: ");
301 	CON_LogMessage(msg);
302 	CON_LogMessage("\n");
303 	I_Error("Invalid or corrupt map.\nLook in log file or text console for technical details.");
304 }
305 
306 /** Sets a header's flickies to be equivalent to the original Freed Animals
307   *
308   * \param i The header to set flickies for
309   */
P_SetDemoFlickies(INT16 i)310 void P_SetDemoFlickies(INT16 i)
311 {
312 	mapheaderinfo[i]->numFlickies = 5;
313 	mapheaderinfo[i]->flickies = Z_Realloc(mapheaderinfo[i]->flickies, 5*sizeof(mobjtype_t), PU_STATIC, NULL);
314 	mapheaderinfo[i]->flickies[0] = MT_FLICKY_02/*MT_BUNNY*/;
315 	mapheaderinfo[i]->flickies[1] = MT_FLICKY_01/*MT_BIRD*/;
316 	mapheaderinfo[i]->flickies[2] = MT_FLICKY_12/*MT_MOUSE*/;
317 	mapheaderinfo[i]->flickies[3] = MT_FLICKY_11/*MT_COW*/;
318 	mapheaderinfo[i]->flickies[4] = MT_FLICKY_03/*MT_CHICKEN*/;
319 }
320 
321 /** Clears a header's flickies
322   *
323   * \param i The header to clear flickies for
324   */
P_DeleteFlickies(INT16 i)325 void P_DeleteFlickies(INT16 i)
326 {
327 	if (mapheaderinfo[i]->flickies)
328 		Z_Free(mapheaderinfo[i]->flickies);
329 	mapheaderinfo[i]->flickies = NULL;
330 	mapheaderinfo[i]->numFlickies = 0;
331 }
332 
333 #define NUMLAPS_DEFAULT 4
334 
335 /** Clears the data from a single map header.
336   *
337   * \param i Map number to clear header for.
338   * \sa P_ClearMapHeaderInfo
339   */
P_ClearSingleMapHeaderInfo(INT16 i)340 static void P_ClearSingleMapHeaderInfo(INT16 i)
341 {
342 	const INT16 num = (INT16)(i-1);
343 	mapheaderinfo[num]->lvlttl[0] = '\0';
344 	mapheaderinfo[num]->selectheading[0] = '\0';
345 	mapheaderinfo[num]->subttl[0] = '\0';
346 	mapheaderinfo[num]->ltzzpatch[0] = '\0';
347 	mapheaderinfo[num]->ltzztext[0] = '\0';
348 	mapheaderinfo[num]->ltactdiamond[0] = '\0';
349 	mapheaderinfo[num]->actnum = 0;
350 	mapheaderinfo[num]->typeoflevel = 0;
351 	mapheaderinfo[num]->nextlevel = (INT16)(i + 1);
352 	mapheaderinfo[num]->marathonnext = 0;
353 	mapheaderinfo[num]->startrings = 0;
354 	mapheaderinfo[num]->sstimer = 90;
355 	mapheaderinfo[num]->ssspheres = 1;
356 	mapheaderinfo[num]->gravity = FRACUNIT/2;
357 	mapheaderinfo[num]->keywords[0] = '\0';
358 	snprintf(mapheaderinfo[num]->musname, 7, "%sM", G_BuildMapName(i));
359 	mapheaderinfo[num]->musname[6] = 0;
360 	mapheaderinfo[num]->mustrack = 0;
361 	mapheaderinfo[num]->muspos = 0;
362 	mapheaderinfo[num]->musinterfadeout = 0;
363 	mapheaderinfo[num]->musintername[0] = 0;
364 	mapheaderinfo[num]->muspostbossname[0] = 0;
365 	mapheaderinfo[num]->muspostbosstrack = 0;
366 	mapheaderinfo[num]->muspostbosspos = 0;
367 	mapheaderinfo[num]->muspostbossfadein = 0;
368 	mapheaderinfo[num]->musforcereset = -1;
369 	mapheaderinfo[num]->forcecharacter[0] = '\0';
370 	mapheaderinfo[num]->weather = 0;
371 	mapheaderinfo[num]->skynum = 1;
372 	mapheaderinfo[num]->skybox_scalex = 16;
373 	mapheaderinfo[num]->skybox_scaley = 16;
374 	mapheaderinfo[num]->skybox_scalez = 16;
375 	mapheaderinfo[num]->interscreen[0] = '#';
376 	mapheaderinfo[num]->runsoc[0] = '#';
377 	mapheaderinfo[num]->scriptname[0] = '#';
378 	mapheaderinfo[num]->precutscenenum = 0;
379 	mapheaderinfo[num]->cutscenenum = 0;
380 	mapheaderinfo[num]->countdown = 0;
381 	mapheaderinfo[num]->palette = UINT16_MAX;
382 	mapheaderinfo[num]->numlaps = NUMLAPS_DEFAULT;
383 	mapheaderinfo[num]->unlockrequired = -1;
384 	mapheaderinfo[num]->levelselect = 0;
385 	mapheaderinfo[num]->bonustype = 0;
386 	mapheaderinfo[num]->maxbonuslives = -1;
387 	mapheaderinfo[num]->levelflags = 0;
388 	mapheaderinfo[num]->menuflags = 0;
389 #if 1 // equivalent to "FlickyList = DEMO"
390 	P_SetDemoFlickies(num);
391 #else // equivalent to "FlickyList = NONE"
392 	P_DeleteFlickies(num);
393 #endif
394 	P_DeleteGrades(num);
395 	mapheaderinfo[num]->customopts = NULL;
396 	mapheaderinfo[num]->numCustomOptions = 0;
397 }
398 
399 /** Allocates a new map-header structure.
400   *
401   * \param i Index of header to allocate.
402   */
P_AllocMapHeader(INT16 i)403 void P_AllocMapHeader(INT16 i)
404 {
405 	if (!mapheaderinfo[i])
406 	{
407 		mapheaderinfo[i] = Z_Malloc(sizeof(mapheader_t), PU_STATIC, NULL);
408 		mapheaderinfo[i]->flickies = NULL;
409 		mapheaderinfo[i]->grades = NULL;
410 	}
411 	P_ClearSingleMapHeaderInfo(i + 1);
412 }
413 
414 /** NiGHTS Grades are a special structure,
415   * we initialize them here.
416   *
417   * \param i Index of header to allocate grades for
418   * \param mare The mare we're adding grades for
419   * \param grades the string from DeHackEd, we work with it ourselves
420   */
P_AddGradesForMare(INT16 i,UINT8 mare,char * gtext)421 void P_AddGradesForMare(INT16 i, UINT8 mare, char *gtext)
422 {
423 	INT32 g;
424 	char *spos = gtext;
425 
426 	CONS_Debug(DBG_SETUP, "Map %d Mare %d: ", i+1, (UINT16)mare+1);
427 
428 	if (mapheaderinfo[i]->numGradedMares < mare+1)
429 	{
430 		mapheaderinfo[i]->numGradedMares = mare+1;
431 		mapheaderinfo[i]->grades = Z_Realloc(mapheaderinfo[i]->grades, sizeof(nightsgrades_t) * mapheaderinfo[i]->numGradedMares, PU_STATIC, NULL);
432 	}
433 
434 	for (g = 0; g < 6; ++g)
435 	{
436 		// Allow "partial" grading systems
437 		if (spos != NULL)
438 		{
439 			mapheaderinfo[i]->grades[mare].grade[g] = atoi(spos);
440 			CONS_Debug(DBG_SETUP, "%u ", atoi(spos));
441 			// Grab next comma
442 			spos = strchr(spos, ',');
443 			if (spos)
444 				++spos;
445 		}
446 		else
447 		{
448 			// Grade not reachable
449 			mapheaderinfo[i]->grades[mare].grade[g] = UINT32_MAX;
450 		}
451 	}
452 
453 	CONS_Debug(DBG_SETUP, "\n");
454 }
455 
456 /** And this removes the grades safely.
457   *
458   * \param i The header to remove grades from
459   */
P_DeleteGrades(INT16 i)460 void P_DeleteGrades(INT16 i)
461 {
462 	if (mapheaderinfo[i]->grades)
463 		Z_Free(mapheaderinfo[i]->grades);
464 
465 	mapheaderinfo[i]->grades = NULL;
466 	mapheaderinfo[i]->numGradedMares = 0;
467 }
468 
469 /** And this fetches the grades
470   *
471   * \param pscore The player's score.
472   * \param map The game map.
473   * \param mare The mare to test.
474   */
P_GetGrade(UINT32 pscore,INT16 map,UINT8 mare)475 UINT8 P_GetGrade(UINT32 pscore, INT16 map, UINT8 mare)
476 {
477 	INT32 i;
478 
479 	// Determining the grade
480 	if (mapheaderinfo[map-1] && mapheaderinfo[map-1]->grades && mapheaderinfo[map-1]->numGradedMares >= mare + 1)
481 	{
482 		INT32 pgrade = 0;
483 		for (i = 0; i < 6; ++i)
484 		{
485 			if (pscore >= mapheaderinfo[map-1]->grades[mare].grade[i])
486 				++pgrade;
487 		}
488 		return (UINT8)pgrade;
489 	}
490 	return 0;
491 }
492 
P_HasGrades(INT16 map,UINT8 mare)493 UINT8 P_HasGrades(INT16 map, UINT8 mare)
494 {
495 	// Determining the grade
496 	// Mare 0 is treated as overall and is true if ANY grades exist
497 	if (mapheaderinfo[map-1] && mapheaderinfo[map-1]->grades
498 		&& (mare == 0 || mapheaderinfo[map-1]->numGradedMares >= mare))
499 		return true;
500 	return false;
501 }
502 
P_GetScoreForGrade(INT16 map,UINT8 mare,UINT8 grade)503 UINT32 P_GetScoreForGrade(INT16 map, UINT8 mare, UINT8 grade)
504 {
505 	// Get the score for the grade... if it exists
506 	if (grade == GRADE_F || grade > GRADE_S || !P_HasGrades(map, mare)) return 0;
507 
508 	return mapheaderinfo[map-1]->grades[mare].grade[grade-1];
509 }
510 
511 //
512 // levelflats
513 //
514 #define MAXLEVELFLATS 256
515 
516 size_t numlevelflats;
517 levelflat_t *levelflats;
518 levelflat_t *foundflats;
519 
520 //SoM: Other files want this info.
P_PrecacheLevelFlats(void)521 size_t P_PrecacheLevelFlats(void)
522 {
523 	lumpnum_t lump;
524 	size_t i;
525 
526 	//SoM: 4/18/2000: New flat code to make use of levelflats.
527 	flatmemory = 0;
528 	for (i = 0; i < numlevelflats; i++)
529 	{
530 		if (levelflats[i].type == LEVELFLAT_FLAT)
531 		{
532 			lump = levelflats[i].u.flat.lumpnum;
533 			if (devparm)
534 				flatmemory += W_LumpLength(lump);
535 			R_GetFlat(lump);
536 		}
537 	}
538 	return flatmemory;
539 }
540 
541 /*
542 levelflat refers to an array of level flats,
543 or NULL if we want to allocate it now.
544 */
545 static INT32
Ploadflat(levelflat_t * levelflat,const char * flatname,boolean resize)546 Ploadflat (levelflat_t *levelflat, const char *flatname, boolean resize)
547 {
548 #ifndef NO_PNG_LUMPS
549 	UINT8         buffer[8];
550 #endif
551 
552 	lumpnum_t    flatnum;
553 	int       texturenum;
554 	UINT8     *flatpatch;
555 	size_t    lumplength;
556 
557 	size_t i;
558 
559 	// Scan through the already found flats, return if it matches.
560 	for (i = 0; i < numlevelflats; i++)
561 	{
562 		if (strnicmp(levelflat[i].name, flatname, 8) == 0)
563 			return i;
564 	}
565 
566 	if (resize)
567 	{
568 		// allocate new flat memory
569 		levelflats = Z_Realloc(levelflats, (numlevelflats + 1) * sizeof(*levelflats), PU_LEVEL, NULL);
570 		levelflat  = levelflats + numlevelflats;
571 	}
572 	else
573 	{
574 		if (numlevelflats >= MAXLEVELFLATS)
575 			I_Error("Too many flats in level\n");
576 
577 		levelflat += numlevelflats;
578 	}
579 
580 	// Store the name.
581 	strlcpy(levelflat->name, flatname, sizeof (levelflat->name));
582 	strupr(levelflat->name);
583 
584 	/* If we can't find a flat, try looking for a texture! */
585 	if (( flatnum = R_GetFlatNumForName(levelflat->name) ) == LUMPERROR)
586 	{
587 		if (( texturenum = R_CheckTextureNumForName(levelflat->name) ) == -1)
588 		{
589 			// check for REDWALL
590 			if (( texturenum = R_CheckTextureNumForName("REDWALL") ) != -1)
591 				goto texturefound;
592 			// check for REDFLR
593 			else if (( flatnum = R_GetFlatNumForName("REDFLR") ) != LUMPERROR)
594 				goto flatfound;
595 			// nevermind
596 			levelflat->type = LEVELFLAT_NONE;
597 		}
598 		else
599 		{
600 texturefound:
601 			levelflat->type = LEVELFLAT_TEXTURE;
602 			levelflat->u.texture.    num = texturenum;
603 			levelflat->u.texture.lastnum = texturenum;
604 			/* start out unanimated */
605 			levelflat->u.texture.basenum = -1;
606 		}
607 	}
608 	else
609 	{
610 flatfound:
611 		/* This could be a flat, patch, or PNG. */
612 		flatpatch = W_CacheLumpNum(flatnum, PU_CACHE);
613 		lumplength = W_LumpLength(flatnum);
614 		if (Picture_CheckIfDoomPatch((softwarepatch_t *)flatpatch, lumplength))
615 			levelflat->type = LEVELFLAT_PATCH;
616 		else
617 		{
618 #ifndef NO_PNG_LUMPS
619 			/*
620 			Only need eight bytes for PNG headers.
621 			FIXME: Put this elsewhere.
622 			*/
623 			W_ReadLumpHeader(flatnum, buffer, 8, 0);
624 			if (Picture_IsLumpPNG(buffer, lumplength))
625 				levelflat->type = LEVELFLAT_PNG;
626 			else
627 #endif/*NO_PNG_LUMPS*/
628 				levelflat->type = LEVELFLAT_FLAT;/* phew */
629 		}
630 		if (flatpatch)
631 			Z_Free(flatpatch);
632 
633 		levelflat->u.flat.    lumpnum = flatnum;
634 		levelflat->u.flat.baselumpnum = LUMPERROR;
635 	}
636 
637 #ifndef ZDEBUG
638 	CONS_Debug(DBG_SETUP, "flat #%03d: %s\n", atoi(sizeu1(numlevelflats)), levelflat->name);
639 #endif
640 
641 	return ( numlevelflats++ );
642 }
643 
644 // Auxiliary function. Find a flat in the active wad files,
645 // allocate an id for it, and set the levelflat (to speedup search)
P_AddLevelFlat(const char * flatname,levelflat_t * levelflat)646 INT32 P_AddLevelFlat(const char *flatname, levelflat_t *levelflat)
647 {
648 	return Ploadflat(levelflat, flatname, false);
649 }
650 
651 // help function for Lua and $$$.sav reading
652 // same as P_AddLevelFlat, except this is not setup so we must realloc levelflats to fit in the new flat
653 // no longer a static func in lua_maplib.c because p_saveg.c also needs it
654 //
P_AddLevelFlatRuntime(const char * flatname)655 INT32 P_AddLevelFlatRuntime(const char *flatname)
656 {
657 	return Ploadflat(levelflats, flatname, true);
658 }
659 
660 // help function for $$$.sav checking
661 // this simply returns the flat # for the name given
662 //
P_CheckLevelFlat(const char * flatname)663 INT32 P_CheckLevelFlat(const char *flatname)
664 {
665 	size_t i;
666 	levelflat_t *levelflat = levelflats;
667 
668 	//
669 	//  scan through the already found flats
670 	//
671 	for (i = 0; i < numlevelflats; i++, levelflat++)
672 		if (strnicmp(levelflat->name,flatname,8)==0)
673 			break;
674 
675 	if (i == numlevelflats)
676 		return 0; // ??? flat was not found, this should not happen!
677 
678 	// level flat id
679 	return (INT32)i;
680 }
681 
682 //
683 // P_ReloadRings
684 // Used by NiGHTS, clears all ring/sphere/hoop/etc items and respawns them
685 //
P_ReloadRings(void)686 void P_ReloadRings(void)
687 {
688 	mobj_t *mo;
689 	thinker_t *th;
690 	size_t i, numHoops = 0;
691 	// Okay, if you have more than 4000 hoops in your map,
692 	// you're insane.
693 	mapthing_t *hoopsToRespawn[4096];
694 	mapthing_t *mt = mapthings;
695 
696 	// scan the thinkers to find rings/spheres/hoops to unset
697 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
698 	{
699 		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
700 			continue;
701 
702 		mo = (mobj_t *)th;
703 
704 		if (mo->type == MT_HOOPCENTER)
705 		{
706 			// Hoops give me a headache
707 			if (mo->threshold == 4242) // Dead hoop
708 			{
709 				hoopsToRespawn[numHoops++] = mo->spawnpoint;
710 				P_RemoveMobj(mo);
711 			}
712 			continue;
713 		}
714 		if (!(mo->type == MT_RING || mo->type == MT_COIN
715 			|| mo->type == MT_BLUESPHERE || mo->type == MT_BOMBSPHERE
716 			|| mo->type == MT_NIGHTSCHIP || mo->type == MT_NIGHTSSTAR))
717 			continue;
718 
719 		// Don't auto-disintegrate things being pulled to us
720 		if (mo->flags2 & MF2_NIGHTSPULL)
721 			continue;
722 
723 		P_RemoveMobj(mo);
724 	}
725 
726 	// Reiterate through mapthings
727 	for (i = 0; i < nummapthings; i++, mt++)
728 	{
729 		// Notice an omission? We handle hoops differently.
730 		if (mt->type == mobjinfo[MT_RING].doomednum || mt->type == mobjinfo[MT_COIN].doomednum
731 			|| mt->type == mobjinfo[MT_REDTEAMRING].doomednum || mt->type == mobjinfo[MT_BLUETEAMRING].doomednum
732 			|| mt->type == mobjinfo[MT_BLUESPHERE].doomednum || mt->type == mobjinfo[MT_BOMBSPHERE].doomednum)
733 		{
734 			mt->mobj = NULL;
735 			P_SetBonusTime(P_SpawnMapThing(mt));
736 		}
737 		else if (mt->type >= 600 && mt->type <= 609) // Item patterns
738 		{
739 			mt->mobj = NULL;
740 			P_SpawnItemPattern(mt, true);
741 		}
742 	}
743 	for (i = 0; i < numHoops; i++)
744 	{
745 		P_SpawnHoop(hoopsToRespawn[i]);
746 	}
747 }
748 
P_SwitchSpheresBonusMode(boolean bonustime)749 void P_SwitchSpheresBonusMode(boolean bonustime)
750 {
751 	mobj_t *mo;
752 	thinker_t *th;
753 
754 	// scan the thinkers to find spheres to switch
755 	for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
756 	{
757 		if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
758 			continue;
759 
760 		mo = (mobj_t *)th;
761 
762 		if (mo->type != MT_BLUESPHERE && mo->type != MT_NIGHTSCHIP
763 			&& mo->type != MT_FLINGBLUESPHERE && mo->type != MT_FLINGNIGHTSCHIP)
764 			continue;
765 
766 		if (!mo->health)
767 			continue;
768 
769 		P_SetMobjState(mo, ((bonustime) ? mo->info->raisestate : mo->info->spawnstate));
770 	}
771 }
772 
773 #ifdef SCANTHINGS
P_ScanThings(INT16 mapnum,INT16 wadnum,INT16 lumpnum)774 void P_ScanThings(INT16 mapnum, INT16 wadnum, INT16 lumpnum)
775 {
776 	size_t i, n;
777 	UINT8 *data, *datastart;
778 	UINT16 type, maprings;
779 	INT16 tol;
780 	UINT32 flags;
781 
782 	tol = mapheaderinfo[mapnum-1]->typeoflevel;
783 	if (!(tol & TOL_SP))
784 		return;
785 	flags = mapheaderinfo[mapnum-1]->levelflags;
786 
787 	n = W_LumpLengthPwad(wadnum, lumpnum) / (5 * sizeof (INT16));
788 	//CONS_Printf("%u map things found!\n", n);
789 
790 	maprings = 0;
791 	data = datastart = W_CacheLumpNumPwad(wadnum, lumpnum, PU_STATIC);
792 	for (i = 0; i < n; i++)
793 	{
794 		data += 3 * sizeof (INT16); // skip x y position, angle
795 		type = READUINT16(data) & 4095;
796 		data += sizeof (INT16); // skip options
797 
798 		switch (type)
799 		{
800 		case 300: // MT_RING
801 		case 1800: // MT_COIN
802 		case 308: // red team ring
803 		case 309: // blue team ring
804 			maprings++;
805 			break;
806 		case 400: // MT_SUPERRINGBOX
807 		case 414: // red ring box
808 		case 415: // blue ring box
809 		case 603: // 10 diagonal rings
810 			maprings += 10;
811 			break;
812 		case 600: // 5 vertical rings
813 		case 601: // 5 vertical rings
814 		case 602: // 5 diagonal rings
815 			maprings += 5;
816 			break;
817 		case 604: // 8 circle rings
818 		case 609: // 16 circle rings & wings
819 			maprings += 8;
820 			break;
821 		case 605: // 16 circle rings
822 			maprings += 16;
823 			break;
824 		case 608: // 8 circle rings & wings
825 			maprings += 4;
826 			break;
827 		}
828 	}
829 	Z_Free(datastart);
830 
831 	if (maprings)
832 		CONS_Printf("%s has %u rings\n", G_BuildMapName(mapnum), maprings);
833 }
834 #endif
835 
P_SpawnEmeraldHunt(void)836 static void P_SpawnEmeraldHunt(void)
837 {
838 	INT32 emer[3], num[MAXHUNTEMERALDS], i, randomkey;
839 	fixed_t x, y, z;
840 
841 	for (i = 0; i < numhuntemeralds; i++)
842 		num[i] = i;
843 
844 	for (i = 0; i < 3; i++)
845 	{
846 		// generate random index, shuffle afterwards
847 		randomkey = P_RandomKey(numhuntemeralds--);
848 		emer[i] = num[randomkey];
849 		num[randomkey] = num[numhuntemeralds];
850 		num[numhuntemeralds] = emer[i];
851 
852 		// spawn emerald
853 		x = huntemeralds[emer[i]]->x<<FRACBITS;
854 		y = huntemeralds[emer[i]]->y<<FRACBITS;
855 		z = P_GetMapThingSpawnHeight(MT_EMERHUNT, huntemeralds[emer[i]], x, y);
856 		P_SetMobjStateNF(P_SpawnMobj(x, y, z, MT_EMERHUNT),
857 			mobjinfo[MT_EMERHUNT].spawnstate+i);
858 	}
859 }
860 
P_SpawnMapThings(boolean spawnemblems)861 static void P_SpawnMapThings(boolean spawnemblems)
862 {
863 	size_t i;
864 	mapthing_t *mt;
865 
866         // Spawn axis points first so they are at the front of the list for fast searching.
867 	for (i = 0, mt = mapthings; i < nummapthings; i++, mt++)
868 	{
869 		switch (mt->type)
870 		{
871 			case 1700: // MT_AXIS
872 			case 1701: // MT_AXISTRANSFER
873 			case 1702: // MT_AXISTRANSFERLINE
874 				mt->mobj = NULL;
875 				P_SpawnMapThing(mt);
876 				break;
877 			default:
878 				break;
879 		}
880 	}
881 
882 	numhuntemeralds = 0;
883 
884 	for (i = 0, mt = mapthings; i < nummapthings; i++, mt++)
885 	{
886 		if (mt->type == 1700 // MT_AXIS
887 			|| mt->type == 1701 // MT_AXISTRANSFER
888 			|| mt->type == 1702) // MT_AXISTRANSFERLINE
889 			continue; // These were already spawned
890 
891 		if (!spawnemblems && mt->type == mobjinfo[MT_EMBLEM].doomednum)
892 			continue;
893 
894 		mt->mobj = NULL;
895 
896 		if (mt->type >= 600 && mt->type <= 609) // item patterns
897 			P_SpawnItemPattern(mt, false);
898 		else if (mt->type == 1705 || mt->type == 1713) // hoops
899 			P_SpawnHoop(mt);
900 		else // Everything else
901 			P_SpawnMapThing(mt);
902 	}
903 
904 	// random emeralds for hunt
905 	if (numhuntemeralds)
906 		P_SpawnEmeraldHunt();
907 }
908 
909 // Experimental groovy write function!
P_WriteThings(void)910 void P_WriteThings(void)
911 {
912 	size_t i, length;
913 	mapthing_t *mt;
914 	UINT8 *savebuffer, *savebuf_p;
915 	INT16 temp;
916 
917 	savebuf_p = savebuffer = (UINT8 *)malloc(nummapthings * sizeof (mapthing_t));
918 
919 	if (!savebuf_p)
920 	{
921 		CONS_Alert(CONS_ERROR, M_GetText("No more free memory for thing writing!\n"));
922 		return;
923 	}
924 
925 	mt = mapthings;
926 	for (i = 0; i < nummapthings; i++, mt++)
927 	{
928 		WRITEINT16(savebuf_p, mt->x);
929 		WRITEINT16(savebuf_p, mt->y);
930 
931 		WRITEINT16(savebuf_p, mt->angle);
932 
933 		temp = (INT16)(mt->type + ((INT16)mt->extrainfo << 12));
934 		WRITEINT16(savebuf_p, temp);
935 		WRITEUINT16(savebuf_p, mt->options);
936 	}
937 
938 	length = savebuf_p - savebuffer;
939 
940 	FIL_WriteFile(va("newthings%d.lmp", gamemap), savebuffer, length);
941 	free(savebuffer);
942 	savebuf_p = NULL;
943 
944 	CONS_Printf(M_GetText("newthings%d.lmp saved.\n"), gamemap);
945 }
946 
947 //
948 // MAP LOADING FUNCTIONS
949 //
950 
P_LoadVertices(UINT8 * data)951 static void P_LoadVertices(UINT8 *data)
952 {
953 	mapvertex_t *mv = (mapvertex_t *)data;
954 	vertex_t *v = vertexes;
955 	size_t i;
956 
957 	// Copy and convert vertex coordinates, internal representation as fixed.
958 	for (i = 0; i < numvertexes; i++, v++, mv++)
959 	{
960 		v->x = SHORT(mv->x)<<FRACBITS;
961 		v->y = SHORT(mv->y)<<FRACBITS;
962 		v->floorzset = v->ceilingzset = false;
963 		v->floorz = v->ceilingz = 0;
964 	}
965 }
966 
P_InitializeSector(sector_t * ss)967 static void P_InitializeSector(sector_t *ss)
968 {
969 	memset(&ss->soundorg, 0, sizeof(ss->soundorg));
970 
971 	ss->validcount = 0;
972 
973 	ss->thinglist = NULL;
974 
975 	ss->floordata = NULL;
976 	ss->ceilingdata = NULL;
977 	ss->lightingdata = NULL;
978 	ss->fadecolormapdata = NULL;
979 
980 	ss->heightsec = -1;
981 	ss->camsec = -1;
982 
983 	ss->floorlightsec = ss->ceilinglightsec = -1;
984 	ss->crumblestate = CRUMBLE_NONE;
985 
986 	ss->touching_thinglist = NULL;
987 
988 	ss->linecount = 0;
989 	ss->lines = NULL;
990 
991 	ss->ffloors = NULL;
992 	ss->attached = NULL;
993 	ss->attachedsolid = NULL;
994 	ss->numattached = 0;
995 	ss->maxattached = 1;
996 	ss->lightlist = NULL;
997 	ss->numlights = 0;
998 	ss->moved = true;
999 
1000 	ss->extra_colormap = NULL;
1001 
1002 	ss->gravity = NULL;
1003 	ss->verticalflip = false;
1004 	ss->flags = SF_FLIPSPECIAL_FLOOR;
1005 
1006 	ss->cullheight = NULL;
1007 
1008 	ss->floorspeed = ss->ceilspeed = 0;
1009 
1010 	ss->preciplist = NULL;
1011 	ss->touching_preciplist = NULL;
1012 
1013 	ss->f_slope = NULL;
1014 	ss->c_slope = NULL;
1015 	ss->hasslope = false;
1016 
1017 	ss->spawn_lightlevel = ss->lightlevel;
1018 
1019 	ss->spawn_extra_colormap = NULL;
1020 }
1021 
P_LoadSectors(UINT8 * data)1022 static void P_LoadSectors(UINT8 *data)
1023 {
1024 	mapsector_t *ms = (mapsector_t *)data;
1025 	sector_t *ss = sectors;
1026 	size_t i;
1027 
1028 	// For each counted sector, copy the sector raw data from our cache pointer ms, to the global table pointer ss.
1029 	for (i = 0; i < numsectors; i++, ss++, ms++)
1030 	{
1031 		ss->floorheight = SHORT(ms->floorheight)<<FRACBITS;
1032 		ss->ceilingheight = SHORT(ms->ceilingheight)<<FRACBITS;
1033 
1034 		ss->floorpic = P_AddLevelFlat(ms->floorpic, foundflats);
1035 		ss->ceilingpic = P_AddLevelFlat(ms->ceilingpic, foundflats);
1036 
1037 		ss->lightlevel = SHORT(ms->lightlevel);
1038 		ss->special = SHORT(ms->special);
1039 		Tag_FSet(&ss->tags, SHORT(ms->tag));
1040 
1041 		ss->floor_xoffs = ss->floor_yoffs = 0;
1042 		ss->ceiling_xoffs = ss->ceiling_yoffs = 0;
1043 
1044 		ss->floorpic_angle = ss->ceilingpic_angle = 0;
1045 
1046 		ss->colormap_protected = false;
1047 
1048 		P_InitializeSector(ss);
1049 	}
1050 }
1051 
P_InitializeLinedef(line_t * ld)1052 static void P_InitializeLinedef(line_t *ld)
1053 {
1054 	vertex_t *v1 = ld->v1;
1055 	vertex_t *v2 = ld->v2;
1056 	UINT8 j;
1057 
1058 	ld->dx = v2->x - v1->x;
1059 	ld->dy = v2->y - v1->y;
1060 
1061 	ld->bbox[BOXLEFT] = min(v1->x, v2->x);
1062 	ld->bbox[BOXRIGHT] = max(v1->x, v2->x);
1063 	ld->bbox[BOXBOTTOM] = min(v1->y, v2->y);
1064 	ld->bbox[BOXTOP] = max(v1->y, v2->y);
1065 
1066 	if (!ld->dx)
1067 		ld->slopetype = ST_VERTICAL;
1068 	else if (!ld->dy)
1069 		ld->slopetype = ST_HORIZONTAL;
1070 	else if ((ld->dy > 0) == (ld->dx > 0))
1071 		ld->slopetype = ST_POSITIVE;
1072 	else
1073 		ld->slopetype = ST_NEGATIVE;
1074 
1075 	ld->frontsector = ld->backsector = NULL;
1076 
1077 	ld->validcount = 0;
1078 	ld->polyobj = NULL;
1079 
1080 	ld->text = NULL;
1081 	ld->callcount = 0;
1082 
1083 	// cph 2006/09/30 - fix sidedef errors right away.
1084 	// cph 2002/07/20 - these errors are fatal if not fixed, so apply them
1085 	for (j = 0; j < 2; j++)
1086 		if (ld->sidenum[j] != 0xffff && ld->sidenum[j] >= (UINT16)numsides)
1087 		{
1088 			ld->sidenum[j] = 0xffff;
1089 			CONS_Debug(DBG_SETUP, "P_InitializeLinedef: Linedef %s has out-of-range sidedef number\n", sizeu1((size_t)(ld - lines)));
1090 		}
1091 
1092 	// killough 11/98: fix common wad errors (missing sidedefs):
1093 	if (ld->sidenum[0] == 0xffff)
1094 	{
1095 		ld->sidenum[0] = 0;  // Substitute dummy sidedef for missing right side
1096 		// cph - print a warning about the bug
1097 		CONS_Debug(DBG_SETUP, "P_InitializeLinedef: Linedef %s missing first sidedef\n", sizeu1((size_t)(ld - lines)));
1098 	}
1099 
1100 	if ((ld->sidenum[1] == 0xffff) && (ld->flags & ML_TWOSIDED))
1101 	{
1102 		ld->flags &= ~ML_TWOSIDED;  // Clear 2s flag for missing left side
1103 		// cph - print a warning about the bug
1104 		CONS_Debug(DBG_SETUP, "P_InitializeLinedef: Linedef %s has two-sided flag set, but no second sidedef\n", sizeu1((size_t)(ld - lines)));
1105 	}
1106 
1107 	if (ld->sidenum[0] != 0xffff)
1108 	{
1109 		sides[ld->sidenum[0]].special = ld->special;
1110 		sides[ld->sidenum[0]].line = ld;
1111 	}
1112 	if (ld->sidenum[1] != 0xffff)
1113 	{
1114 		sides[ld->sidenum[1]].special = ld->special;
1115 		sides[ld->sidenum[1]].line = ld;
1116 	}
1117 }
1118 
P_SetLinedefV1(size_t i,UINT16 vertex_num)1119 static void P_SetLinedefV1(size_t i, UINT16 vertex_num)
1120 {
1121 	if (vertex_num >= numvertexes)
1122 	{
1123 		CONS_Debug(DBG_SETUP, "P_SetLinedefV1: linedef %s has out-of-range v1 num %u\n", sizeu1(i), vertex_num);
1124 		vertex_num = 0;
1125 	}
1126 	lines[i].v1 = &vertexes[vertex_num];
1127 }
1128 
P_SetLinedefV2(size_t i,UINT16 vertex_num)1129 static void P_SetLinedefV2(size_t i, UINT16 vertex_num)
1130 {
1131 	if (vertex_num >= numvertexes)
1132 	{
1133 		CONS_Debug(DBG_SETUP, "P_SetLinedefV2: linedef %s has out-of-range v2 num %u\n", sizeu1(i), vertex_num);
1134 		vertex_num = 0;
1135 	}
1136 	lines[i].v2 = &vertexes[vertex_num];
1137 }
1138 
P_LoadLinedefs(UINT8 * data)1139 static void P_LoadLinedefs(UINT8 *data)
1140 {
1141 	maplinedef_t *mld = (maplinedef_t *)data;
1142 	line_t *ld = lines;
1143 	size_t i;
1144 
1145 	for (i = 0; i < numlines; i++, mld++, ld++)
1146 	{
1147 		ld->flags = SHORT(mld->flags);
1148 		ld->special = SHORT(mld->special);
1149 		Tag_FSet(&ld->tags, SHORT(mld->tag));
1150 		memset(ld->args, 0, NUMLINEARGS*sizeof(*ld->args));
1151 		memset(ld->stringargs, 0x00, NUMLINESTRINGARGS*sizeof(*ld->stringargs));
1152 		ld->alpha = FRACUNIT;
1153 		ld->executordelay = 0;
1154 		P_SetLinedefV1(i, SHORT(mld->v1));
1155 		P_SetLinedefV2(i, SHORT(mld->v2));
1156 
1157 		ld->sidenum[0] = SHORT(mld->sidenum[0]);
1158 		ld->sidenum[1] = SHORT(mld->sidenum[1]);
1159 
1160 		P_InitializeLinedef(ld);
1161 	}
1162 }
1163 
P_SetSidedefSector(size_t i,UINT16 sector_num)1164 static void P_SetSidedefSector(size_t i, UINT16 sector_num)
1165 {
1166 	// cph 2006/09/30 - catch out-of-range sector numbers; use sector 0 instead
1167 	if (sector_num >= numsectors)
1168 	{
1169 		CONS_Debug(DBG_SETUP, "P_SetSidedefSector: sidedef %s has out-of-range sector num %u\n", sizeu1(i), sector_num);
1170 		sector_num = 0;
1171 	}
1172 	sides[i].sector = &sectors[sector_num];
1173 }
1174 
P_InitializeSidedef(side_t * sd)1175 static void P_InitializeSidedef(side_t *sd)
1176 {
1177 	if (!sd->line)
1178 	{
1179 		CONS_Debug(DBG_SETUP, "P_LoadSidedefs: Sidedef %s is not used by any linedef\n", sizeu1((size_t)(sd - sides)));
1180 		sd->line = &lines[0];
1181 		sd->special = sd->line->special;
1182 	}
1183 
1184 	sd->text = NULL;
1185 	sd->colormap_data = NULL;
1186 }
1187 
P_LoadSidedefs(UINT8 * data)1188 static void P_LoadSidedefs(UINT8 *data)
1189 {
1190 	mapsidedef_t *msd = (mapsidedef_t*)data;
1191 	side_t *sd = sides;
1192 	size_t i;
1193 
1194 	for (i = 0; i < numsides; i++, sd++, msd++)
1195 	{
1196 		INT16 textureoffset = SHORT(msd->textureoffset);
1197 		boolean isfrontside;
1198 
1199 		P_InitializeSidedef(sd);
1200 
1201 		isfrontside = sd->line->sidenum[0] == i;
1202 
1203 		// Repeat count for midtexture
1204 		if (((sd->line->flags & (ML_TWOSIDED|ML_EFFECT5)) == (ML_TWOSIDED|ML_EFFECT5))
1205 			&& !(sd->special >= 300 && sd->special < 500)) // exempt linedef exec specials
1206 		{
1207 			sd->repeatcnt = (INT16)(((UINT16)textureoffset) >> 12);
1208 			sd->textureoffset = (((UINT16)textureoffset) & 2047) << FRACBITS;
1209 		}
1210 		else
1211 		{
1212 			sd->repeatcnt = 0;
1213 			sd->textureoffset = textureoffset << FRACBITS;
1214 		}
1215 		sd->rowoffset = SHORT(msd->rowoffset)<<FRACBITS;
1216 
1217 		P_SetSidedefSector(i, SHORT(msd->sector));
1218 
1219 		// Special info stored in texture fields!
1220 		switch (sd->special)
1221 		{
1222 			case 606: //SoM: 4/4/2000: Just colormap transfer
1223 			case 447: // Change colormap of tagged sectors! -- Monster Iestyn 14/06/18
1224 			case 455: // Fade colormaps! mazmazz 9/12/2018 (:flag_us:)
1225 				// SoM: R_CreateColormap will only create a colormap in software mode...
1226 				// Perhaps we should just call it instead of doing the calculations here.
1227 				if (!udmf)
1228 				{
1229 					sd->colormap_data = R_CreateColormapFromLinedef(msd->toptexture, msd->midtexture, msd->bottomtexture);
1230 					sd->toptexture = sd->midtexture = sd->bottomtexture = 0;
1231 				}
1232 				break;
1233 
1234 			case 413: // Change music
1235 			{
1236 				char process[8+1];
1237 
1238 				sd->toptexture = sd->midtexture = sd->bottomtexture = 0;
1239 				if (msd->bottomtexture[0] != '-' || msd->bottomtexture[1] != '\0')
1240 				{
1241 					M_Memcpy(process,msd->bottomtexture,8);
1242 					process[8] = '\0';
1243 					sd->bottomtexture = get_number(process);
1244 				}
1245 
1246 				if (!(msd->midtexture[0] == '-' && msd->midtexture[1] == '\0') || msd->midtexture[1] != '\0')
1247 				{
1248 					M_Memcpy(process,msd->midtexture,8);
1249 					process[8] = '\0';
1250 					sd->midtexture = get_number(process);
1251 				}
1252 
1253  				sd->text = Z_Malloc(7, PU_LEVEL, NULL);
1254 				if (isfrontside && !(msd->toptexture[0] == '-' && msd->toptexture[1] == '\0'))
1255 				{
1256 					M_Memcpy(process,msd->toptexture,8);
1257 					process[8] = '\0';
1258 
1259 					// If they type in O_ or D_ and their music name, just shrug,
1260 					// then copy the rest instead.
1261 					if ((process[0] == 'O' || process[0] == 'D') && process[7])
1262 						M_Memcpy(sd->text, process+2, 6);
1263 					else // Assume it's a proper music name.
1264 						M_Memcpy(sd->text, process, 6);
1265 					sd->text[6] = 0;
1266 				}
1267 				else
1268 					sd->text[0] = 0;
1269 				break;
1270 			}
1271 
1272 			case 4: // Speed pad parameters
1273 			case 414: // Play SFX
1274 			{
1275 				sd->toptexture = sd->midtexture = sd->bottomtexture = 0;
1276 				if (msd->toptexture[0] != '-' || msd->toptexture[1] != '\0')
1277 				{
1278 					char process[8+1];
1279 					M_Memcpy(process,msd->toptexture,8);
1280 					process[8] = '\0';
1281 					sd->toptexture = get_number(process);
1282 				}
1283 				break;
1284 			}
1285 
1286 			case 9: // Mace parameters
1287 			case 14: // Bustable block parameters
1288 			case 15: // Fan particle spawner parameters
1289 			case 334: // Trigger linedef executor: Object dye - Continuous
1290 			case 335: // Trigger linedef executor: Object dye - Each time
1291 			case 336: // Trigger linedef executor: Object dye - Once
1292 			case 425: // Calls P_SetMobjState on calling mobj
1293 			case 434: // Custom Power
1294 			case 442: // Calls P_SetMobjState on mobjs of a given type in the tagged sectors
1295 			case 461: // Spawns an object on the map based on texture offsets
1296 			case 463: // Colorizes an object
1297 			{
1298 				char process[8*3+1];
1299 				memset(process,0,8*3+1);
1300 				sd->toptexture = sd->midtexture = sd->bottomtexture = 0;
1301 				if (msd->toptexture[0] == '-' && msd->toptexture[1] == '\0')
1302 					break;
1303 				else
1304 					M_Memcpy(process,msd->toptexture,8);
1305 				if (msd->midtexture[0] != '-' || msd->midtexture[1] != '\0')
1306 					M_Memcpy(process+strlen(process), msd->midtexture, 8);
1307 				if (msd->bottomtexture[0] != '-' || msd->bottomtexture[1] != '\0')
1308 					M_Memcpy(process+strlen(process), msd->bottomtexture, 8);
1309 				sd->toptexture = get_number(process);
1310 				break;
1311 			}
1312 
1313 			case 331: // Trigger linedef executor: Skin - Continuous
1314 			case 332: // Trigger linedef executor: Skin - Each time
1315 			case 333: // Trigger linedef executor: Skin - Once
1316 			case 443: // Calls a named Lua function
1317 			case 459: // Control text prompt (named tag)
1318 			{
1319 				char process[8*3+1];
1320 				memset(process,0,8*3+1);
1321 				sd->toptexture = sd->midtexture = sd->bottomtexture = 0;
1322 				if (msd->toptexture[0] == '-' && msd->toptexture[1] == '\0')
1323 					break;
1324 				else
1325 					M_Memcpy(process,msd->toptexture,8);
1326 				if (msd->midtexture[0] != '-' || msd->midtexture[1] != '\0')
1327 					M_Memcpy(process+strlen(process), msd->midtexture, 8);
1328 				if (msd->bottomtexture[0] != '-' || msd->bottomtexture[1] != '\0')
1329 					M_Memcpy(process+strlen(process), msd->bottomtexture, 8);
1330 				sd->text = Z_Malloc(strlen(process)+1, PU_LEVEL, NULL);
1331 				M_Memcpy(sd->text, process, strlen(process)+1);
1332 				break;
1333 			}
1334 
1335 			case 259: // Custom FOF
1336 				if (!isfrontside)
1337 				{
1338 					if ((msd->toptexture[0] >= '0' && msd->toptexture[0] <= '9')
1339 						|| (msd->toptexture[0] >= 'A' && msd->toptexture[0] <= 'F'))
1340 						sd->toptexture = axtoi(msd->toptexture);
1341 					else
1342 						I_Error("Custom FOF (line id %s) needs a value in the linedef's back side upper texture field.", sizeu1(sd->line - lines));
1343 
1344 					sd->midtexture = R_TextureNumForName(msd->midtexture);
1345 					sd->bottomtexture = R_TextureNumForName(msd->bottomtexture);
1346 					break;
1347 				}
1348 				// FALLTHRU
1349 			default: // normal cases
1350 				if (msd->toptexture[0] == '#')
1351 				{
1352 					char *col = msd->toptexture;
1353 					sd->toptexture = sd->bottomtexture =
1354 						((col[1]-'0')*100 + (col[2]-'0')*10 + col[3]-'0') + 1;
1355 					sd->midtexture = R_TextureNumForName(msd->midtexture);
1356 				}
1357 				else
1358 				{
1359 					sd->midtexture = R_TextureNumForName(msd->midtexture);
1360 					sd->toptexture = R_TextureNumForName(msd->toptexture);
1361 					sd->bottomtexture = R_TextureNumForName(msd->bottomtexture);
1362 				}
1363 				break;
1364 		}
1365 	}
1366 }
1367 
P_LoadThings(UINT8 * data)1368 static void P_LoadThings(UINT8 *data)
1369 {
1370 	mapthing_t *mt;
1371 	size_t i;
1372 
1373 	for (i = 0, mt = mapthings; i < nummapthings; i++, mt++)
1374 	{
1375 		mt->x = READINT16(data);
1376 		mt->y = READINT16(data);
1377 
1378 		mt->angle = READINT16(data);
1379 		mt->type = READUINT16(data);
1380 		mt->options = READUINT16(data);
1381 		mt->extrainfo = (UINT8)(mt->type >> 12);
1382 		Tag_FSet(&mt->tags, 0);
1383 		mt->scale = FRACUNIT;
1384 		memset(mt->args, 0, NUMMAPTHINGARGS*sizeof(*mt->args));
1385 		memset(mt->stringargs, 0x00, NUMMAPTHINGSTRINGARGS*sizeof(*mt->stringargs));
1386 		mt->pitch = mt->roll = 0;
1387 
1388 		mt->type &= 4095;
1389 
1390 		if (mt->type == 1705 || (mt->type == 750 && mt->extrainfo))
1391 			mt->z = mt->options; // NiGHTS Hoops use the full flags bits to set the height.
1392 		else
1393 			mt->z = mt->options >> ZSHIFT;
1394 
1395 		mt->mobj = NULL;
1396 	}
1397 }
1398 
1399 // Stores positions for relevant map data spread through a TEXTMAP.
1400 UINT32 mapthingsPos[UINT16_MAX];
1401 UINT32 linesPos[UINT16_MAX];
1402 UINT32 sidesPos[UINT16_MAX];
1403 UINT32 vertexesPos[UINT16_MAX];
1404 UINT32 sectorsPos[UINT16_MAX];
1405 
1406 // Determine total amount of map data in TEXTMAP.
TextmapCount(UINT8 * data,size_t size)1407 static boolean TextmapCount(UINT8 *data, size_t size)
1408 {
1409 	char *tkn = M_GetToken((char *)data);
1410 	UINT8 brackets = 0;
1411 
1412 	nummapthings = 0;
1413 	numlines = 0;
1414 	numsides = 0;
1415 	numvertexes = 0;
1416 	numsectors = 0;
1417 
1418 	// Look for namespace at the beginning.
1419 	if (!fastcmp(tkn, "namespace"))
1420 	{
1421 		Z_Free(tkn);
1422 		CONS_Alert(CONS_ERROR, "No namespace at beginning of lump!\n");
1423 		return false;
1424 	}
1425 	Z_Free(tkn);
1426 
1427 	// Check if namespace is valid.
1428 	tkn = M_GetToken(NULL);
1429 	if (!fastcmp(tkn, "srb2"))
1430 		CONS_Alert(CONS_WARNING, "Invalid namespace '%s', only 'srb2' is supported.\n", tkn);
1431 	Z_Free(tkn);
1432 
1433 	tkn = M_GetToken(NULL);
1434 	while (tkn && M_GetTokenPos() < size)
1435 	{
1436 		// Avoid anything inside bracketed stuff, only look for external keywords.
1437 		if (brackets)
1438 		{
1439 			if (fastcmp(tkn, "}"))
1440 				brackets--;
1441 		}
1442 		else if (fastcmp(tkn, "{"))
1443 			brackets++;
1444 		// Check for valid fields.
1445 		else if (fastcmp(tkn, "thing"))
1446 			mapthingsPos[nummapthings++] = M_GetTokenPos();
1447 		else if (fastcmp(tkn, "linedef"))
1448 			linesPos[numlines++] = M_GetTokenPos();
1449 		else if (fastcmp(tkn, "sidedef"))
1450 			sidesPos[numsides++] = M_GetTokenPos();
1451 		else if (fastcmp(tkn, "vertex"))
1452 			vertexesPos[numvertexes++] = M_GetTokenPos();
1453 		else if (fastcmp(tkn, "sector"))
1454 			sectorsPos[numsectors++] = M_GetTokenPos();
1455 		else
1456 			CONS_Alert(CONS_NOTICE, "Unknown field '%s'.\n", tkn);
1457 
1458 		Z_Free(tkn);
1459 		tkn = M_GetToken(NULL);
1460 	}
1461 
1462 	Z_Free(tkn);
1463 
1464 	if (brackets)
1465 	{
1466 		CONS_Alert(CONS_ERROR, "Unclosed brackets detected in textmap lump.\n");
1467 		return false;
1468 	}
1469 
1470 	return true;
1471 }
1472 
ParseTextmapVertexParameter(UINT32 i,char * param,char * val)1473 static void ParseTextmapVertexParameter(UINT32 i, char *param, char *val)
1474 {
1475 	if (fastcmp(param, "x"))
1476 		vertexes[i].x = FLOAT_TO_FIXED(atof(val));
1477 	else if (fastcmp(param, "y"))
1478 		vertexes[i].y = FLOAT_TO_FIXED(atof(val));
1479 	else if (fastcmp(param, "zfloor"))
1480 	{
1481 		vertexes[i].floorz = FLOAT_TO_FIXED(atof(val));
1482 		vertexes[i].floorzset = true;
1483 	}
1484 	else if (fastcmp(param, "zceiling"))
1485 	{
1486 		vertexes[i].ceilingz = FLOAT_TO_FIXED(atof(val));
1487 		vertexes[i].ceilingzset = true;
1488 	}
1489 }
1490 
1491 typedef struct textmap_colormap_s {
1492 	boolean used;
1493 	INT32 lightcolor;
1494 	UINT8 lightalpha;
1495 	INT32 fadecolor;
1496 	UINT8 fadealpha;
1497 	UINT8 fadestart;
1498 	UINT8 fadeend;
1499 	UINT8 flags;
1500 } textmap_colormap_t;
1501 
1502 textmap_colormap_t textmap_colormap = { false, 0, 25, 0, 25, 0, 31, 0 };
1503 
ParseTextmapSectorParameter(UINT32 i,char * param,char * val)1504 static void ParseTextmapSectorParameter(UINT32 i, char *param, char *val)
1505 {
1506 	if (fastcmp(param, "heightfloor"))
1507 		sectors[i].floorheight = atol(val) << FRACBITS;
1508 	else if (fastcmp(param, "heightceiling"))
1509 		sectors[i].ceilingheight = atol(val) << FRACBITS;
1510 	if (fastcmp(param, "texturefloor"))
1511 		sectors[i].floorpic = P_AddLevelFlat(val, foundflats);
1512 	else if (fastcmp(param, "textureceiling"))
1513 		sectors[i].ceilingpic = P_AddLevelFlat(val, foundflats);
1514 	else if (fastcmp(param, "lightlevel"))
1515 		sectors[i].lightlevel = atol(val);
1516 	else if (fastcmp(param, "special"))
1517 		sectors[i].special = atol(val);
1518 	else if (fastcmp(param, "id"))
1519 		Tag_FSet(&sectors[i].tags, atol(val));
1520 	else if (fastcmp(param, "moreids"))
1521 	{
1522 		char* id = val;
1523 		while (id)
1524 		{
1525 			Tag_Add(&sectors[i].tags, atol(id));
1526 			if ((id = strchr(id, ' ')))
1527 				id++;
1528 		}
1529 	}
1530 	else if (fastcmp(param, "xpanningfloor"))
1531 		sectors[i].floor_xoffs = FLOAT_TO_FIXED(atof(val));
1532 	else if (fastcmp(param, "ypanningfloor"))
1533 		sectors[i].floor_yoffs = FLOAT_TO_FIXED(atof(val));
1534 	else if (fastcmp(param, "xpanningceiling"))
1535 		sectors[i].ceiling_xoffs = FLOAT_TO_FIXED(atof(val));
1536 	else if (fastcmp(param, "ypanningceiling"))
1537 		sectors[i].ceiling_yoffs = FLOAT_TO_FIXED(atof(val));
1538 	else if (fastcmp(param, "rotationfloor"))
1539 		sectors[i].floorpic_angle = FixedAngle(FLOAT_TO_FIXED(atof(val)));
1540 	else if (fastcmp(param, "rotationceiling"))
1541 		sectors[i].ceilingpic_angle = FixedAngle(FLOAT_TO_FIXED(atof(val)));
1542 	else if (fastcmp(param, "lightcolor"))
1543 	{
1544 		textmap_colormap.used = true;
1545 		textmap_colormap.lightcolor = atol(val);
1546 	}
1547 	else if (fastcmp(param, "lightalpha"))
1548 	{
1549 		textmap_colormap.used = true;
1550 		textmap_colormap.lightalpha = atol(val);
1551 	}
1552 	else if (fastcmp(param, "fadecolor"))
1553 	{
1554 		textmap_colormap.used = true;
1555 		textmap_colormap.fadecolor = atol(val);
1556 	}
1557 	else if (fastcmp(param, "fadealpha"))
1558 	{
1559 		textmap_colormap.used = true;
1560 		textmap_colormap.fadealpha = atol(val);
1561 	}
1562 	else if (fastcmp(param, "fadestart"))
1563 	{
1564 		textmap_colormap.used = true;
1565 		textmap_colormap.fadestart = atol(val);
1566 	}
1567 	else if (fastcmp(param, "fadeend"))
1568 	{
1569 		textmap_colormap.used = true;
1570 		textmap_colormap.fadeend = atol(val);
1571 	}
1572 	else if (fastcmp(param, "colormapfog") && fastcmp("true", val))
1573 	{
1574 		textmap_colormap.used = true;
1575 		textmap_colormap.flags |= CMF_FOG;
1576 	}
1577 	else if (fastcmp(param, "colormapfadesprites") && fastcmp("true", val))
1578 	{
1579 		textmap_colormap.used = true;
1580 		textmap_colormap.flags |= CMF_FADEFULLBRIGHTSPRITES;
1581 	}
1582 	else if (fastcmp(param, "colormapprotected") && fastcmp("true", val))
1583 		sectors[i].colormap_protected = true;
1584 }
1585 
ParseTextmapSidedefParameter(UINT32 i,char * param,char * val)1586 static void ParseTextmapSidedefParameter(UINT32 i, char *param, char *val)
1587 {
1588 	if (fastcmp(param, "offsetx"))
1589 		sides[i].textureoffset = atol(val)<<FRACBITS;
1590 	else if (fastcmp(param, "offsety"))
1591 		sides[i].rowoffset = atol(val)<<FRACBITS;
1592 	else if (fastcmp(param, "texturetop"))
1593 		sides[i].toptexture = R_TextureNumForName(val);
1594 	else if (fastcmp(param, "texturebottom"))
1595 		sides[i].bottomtexture = R_TextureNumForName(val);
1596 	else if (fastcmp(param, "texturemiddle"))
1597 		sides[i].midtexture = R_TextureNumForName(val);
1598 	else if (fastcmp(param, "sector"))
1599 		P_SetSidedefSector(i, atol(val));
1600 	else if (fastcmp(param, "repeatcnt"))
1601 		sides[i].repeatcnt = atol(val);
1602 }
1603 
ParseTextmapLinedefParameter(UINT32 i,char * param,char * val)1604 static void ParseTextmapLinedefParameter(UINT32 i, char *param, char *val)
1605 {
1606 	if (fastcmp(param, "id"))
1607 		Tag_FSet(&lines[i].tags, atol(val));
1608 	else if (fastcmp(param, "moreids"))
1609 	{
1610 		char* id = val;
1611 		while (id)
1612 		{
1613 			Tag_Add(&lines[i].tags, atol(id));
1614 			if ((id = strchr(id, ' ')))
1615 				id++;
1616 		}
1617 	}
1618 	else if (fastcmp(param, "special"))
1619 		lines[i].special = atol(val);
1620 	else if (fastcmp(param, "v1"))
1621 		P_SetLinedefV1(i, atol(val));
1622 	else if (fastcmp(param, "v2"))
1623 		P_SetLinedefV2(i, atol(val));
1624 	else if (strlen(param) == 7 && fastncmp(param, "arg", 3) && fastncmp(param + 4, "str", 3))
1625 	{
1626 		size_t argnum = param[3] - '0';
1627 		if (argnum >= NUMLINESTRINGARGS)
1628 			return;
1629 		lines[i].stringargs[argnum] = Z_Malloc(strlen(val) + 1, PU_LEVEL, NULL);
1630 		M_Memcpy(lines[i].stringargs[argnum], val, strlen(val) + 1);
1631 	}
1632 	else if (fastncmp(param, "arg", 3) && strlen(param) > 3)
1633 	{
1634 		size_t argnum = atol(param + 3);
1635 		if (argnum >= NUMLINEARGS)
1636 			return;
1637 		lines[i].args[argnum] = atol(val);
1638 	}
1639 	else if (fastcmp(param, "sidefront"))
1640 		lines[i].sidenum[0] = atol(val);
1641 	else if (fastcmp(param, "sideback"))
1642 		lines[i].sidenum[1] = atol(val);
1643 	else if (fastcmp(param, "alpha"))
1644 		lines[i].alpha = FLOAT_TO_FIXED(atof(val));
1645 	else if (fastcmp(param, "executordelay"))
1646 		lines[i].executordelay = atol(val);
1647 
1648 	// Flags
1649 	else if (fastcmp(param, "blocking") && fastcmp("true", val))
1650 		lines[i].flags |= ML_IMPASSIBLE;
1651 	else if (fastcmp(param, "blockmonsters") && fastcmp("true", val))
1652 		lines[i].flags |= ML_BLOCKMONSTERS;
1653 	else if (fastcmp(param, "twosided") && fastcmp("true", val))
1654 		lines[i].flags |= ML_TWOSIDED;
1655 	else if (fastcmp(param, "dontpegtop") && fastcmp("true", val))
1656 		lines[i].flags |= ML_DONTPEGTOP;
1657 	else if (fastcmp(param, "dontpegbottom") && fastcmp("true", val))
1658 		lines[i].flags |= ML_DONTPEGBOTTOM;
1659 	else if (fastcmp(param, "skewtd") && fastcmp("true", val))
1660 		lines[i].flags |= ML_EFFECT1;
1661 	else if (fastcmp(param, "noclimb") && fastcmp("true", val))
1662 		lines[i].flags |= ML_NOCLIMB;
1663 	else if (fastcmp(param, "noskew") && fastcmp("true", val))
1664 		lines[i].flags |= ML_EFFECT2;
1665 	else if (fastcmp(param, "midpeg") && fastcmp("true", val))
1666 		lines[i].flags |= ML_EFFECT3;
1667 	else if (fastcmp(param, "midsolid") && fastcmp("true", val))
1668 		lines[i].flags |= ML_EFFECT4;
1669 	else if (fastcmp(param, "wrapmidtex") && fastcmp("true", val))
1670 		lines[i].flags |= ML_EFFECT5;
1671 	else if (fastcmp(param, "effect6") && fastcmp("true", val))
1672 		lines[i].flags |= ML_EFFECT6;
1673 	else if (fastcmp(param, "nonet") && fastcmp("true", val))
1674 		lines[i].flags |= ML_NONET;
1675 	else if (fastcmp(param, "netonly") && fastcmp("true", val))
1676 		lines[i].flags |= ML_NETONLY;
1677 	else if (fastcmp(param, "bouncy") && fastcmp("true", val))
1678 		lines[i].flags |= ML_BOUNCY;
1679 	else if (fastcmp(param, "transfer") && fastcmp("true", val))
1680 		lines[i].flags |= ML_TFERLINE;
1681 }
1682 
ParseTextmapThingParameter(UINT32 i,char * param,char * val)1683 static void ParseTextmapThingParameter(UINT32 i, char *param, char *val)
1684 {
1685 	if (fastcmp(param, "id"))
1686 		Tag_FSet(&mapthings[i].tags, atol(val));
1687 	else if (fastcmp(param, "moreids"))
1688 	{
1689 		char* id = val;
1690 		while (id)
1691 		{
1692 			Tag_Add(&mapthings[i].tags, atol(id));
1693 			if ((id = strchr(id, ' ')))
1694 				id++;
1695 		}
1696 	}
1697 	else if (fastcmp(param, "x"))
1698 		mapthings[i].x = atol(val);
1699 	else if (fastcmp(param, "y"))
1700 		mapthings[i].y = atol(val);
1701 	else if (fastcmp(param, "height"))
1702 		mapthings[i].z = atol(val);
1703 	else if (fastcmp(param, "angle"))
1704 		mapthings[i].angle = atol(val);
1705 	else if (fastcmp(param, "pitch"))
1706 		mapthings[i].pitch = atol(val);
1707 	else if (fastcmp(param, "roll"))
1708 		mapthings[i].roll = atol(val);
1709 	else if (fastcmp(param, "type"))
1710 		mapthings[i].type = atol(val);
1711 	else if (fastcmp(param, "scale") || fastcmp(param, "scalex") || fastcmp(param, "scaley"))
1712 		mapthings[i].scale = FLOAT_TO_FIXED(atof(val));
1713 	// Flags
1714 	else if (fastcmp(param, "extra") && fastcmp("true", val))
1715 		mapthings[i].options |= MTF_EXTRA;
1716 	else if (fastcmp(param, "flip") && fastcmp("true", val))
1717 		mapthings[i].options |= MTF_OBJECTFLIP;
1718 	else if (fastcmp(param, "objectspecial") && fastcmp("true", val))
1719 		mapthings[i].options |= MTF_OBJECTSPECIAL;
1720 	else if (fastcmp(param, "ambush") && fastcmp("true", val))
1721 		mapthings[i].options |= MTF_AMBUSH;
1722 
1723 	else if (strlen(param) == 7 && fastncmp(param, "arg", 3) && fastncmp(param + 4, "str", 3))
1724 	{
1725 		size_t argnum = param[3] - '0';
1726 		if (argnum >= NUMMAPTHINGSTRINGARGS)
1727 			return;
1728 		mapthings[i].stringargs[argnum] = Z_Malloc(strlen(val) + 1, PU_LEVEL, NULL);
1729 		M_Memcpy(mapthings[i].stringargs[argnum], val, strlen(val) + 1);
1730 	}
1731 	else if (fastncmp(param, "arg", 3) && strlen(param) > 3)
1732 	{
1733 		size_t argnum = atol(param + 3);
1734 		if (argnum >= NUMMAPTHINGARGS)
1735 			return;
1736 		mapthings[i].args[argnum] = atol(val);
1737 	}
1738 }
1739 
1740 /** From a given position table, run a specified parser function through a {}-encapsuled text.
1741   *
1742   * \param Position of the data to parse, in the textmap.
1743   * \param Structure number (mapthings, sectors, ...).
1744   * \param Parser function pointer.
1745   */
TextmapParse(UINT32 dataPos,size_t num,void (* parser)(UINT32,char *,char *))1746 static void TextmapParse(UINT32 dataPos, size_t num, void (*parser)(UINT32, char *, char *))
1747 {
1748 	char *param, *val;
1749 
1750 	M_SetTokenPos(dataPos);
1751 	param = M_GetToken(NULL);
1752 	if (!fastcmp(param, "{"))
1753 	{
1754 		Z_Free(param);
1755 		CONS_Alert(CONS_WARNING, "Invalid UDMF data capsule!\n");
1756 		return;
1757 	}
1758 	Z_Free(param);
1759 
1760 	while (true)
1761 	{
1762 		param = M_GetToken(NULL);
1763 		if (fastcmp(param, "}"))
1764 		{
1765 			Z_Free(param);
1766 			break;
1767 		}
1768 		val = M_GetToken(NULL);
1769 		parser(num, param, val);
1770 		Z_Free(param);
1771 		Z_Free(val);
1772 	}
1773 }
1774 
1775 /** Provides a fix to the flat alignment coordinate transform from standard Textmaps.
1776  */
TextmapFixFlatOffsets(sector_t * sec)1777 static void TextmapFixFlatOffsets(sector_t *sec)
1778 {
1779 	if (sec->floorpic_angle)
1780 	{
1781 		fixed_t pc = FINECOSINE(sec->floorpic_angle>>ANGLETOFINESHIFT);
1782 		fixed_t ps = FINESINE  (sec->floorpic_angle>>ANGLETOFINESHIFT);
1783 		fixed_t xoffs = sec->floor_xoffs;
1784 		fixed_t yoffs = sec->floor_yoffs;
1785 		sec->floor_xoffs = (FixedMul(xoffs, pc) % MAXFLATSIZE) - (FixedMul(yoffs, ps) % MAXFLATSIZE);
1786 		sec->floor_yoffs = (FixedMul(xoffs, ps) % MAXFLATSIZE) + (FixedMul(yoffs, pc) % MAXFLATSIZE);
1787 	}
1788 
1789 	if (sec->ceilingpic_angle)
1790 	{
1791 		fixed_t pc = FINECOSINE(sec->ceilingpic_angle>>ANGLETOFINESHIFT);
1792 		fixed_t ps = FINESINE  (sec->ceilingpic_angle>>ANGLETOFINESHIFT);
1793 		fixed_t xoffs = sec->ceiling_xoffs;
1794 		fixed_t yoffs = sec->ceiling_yoffs;
1795 		sec->ceiling_xoffs = (FixedMul(xoffs, pc) % MAXFLATSIZE) - (FixedMul(yoffs, ps) % MAXFLATSIZE);
1796 		sec->ceiling_yoffs = (FixedMul(xoffs, ps) % MAXFLATSIZE) + (FixedMul(yoffs, pc) % MAXFLATSIZE);
1797 	}
1798 }
1799 
P_ColorToRGBA(INT32 color,UINT8 alpha)1800 static INT32 P_ColorToRGBA(INT32 color, UINT8 alpha)
1801 {
1802 	UINT8 r = (color >> 16) & 0xFF;
1803 	UINT8 g = (color >> 8) & 0xFF;
1804 	UINT8 b = color & 0xFF;
1805 	return R_PutRgbaRGBA(r, g, b, alpha);
1806 }
1807 
1808 /** Loads the textmap data, after obtaining the elements count and allocating their respective space.
1809   */
P_LoadTextmap(void)1810 static void P_LoadTextmap(void)
1811 {
1812 	UINT32 i;
1813 
1814 	vertex_t   *vt;
1815 	sector_t   *sc;
1816 	line_t     *ld;
1817 	side_t     *sd;
1818 	mapthing_t *mt;
1819 
1820 	CONS_Alert(CONS_NOTICE, "UDMF support is still a work-in-progress; its specs and features are prone to change until it is fully implemented.\n");
1821 
1822 	/// Given the UDMF specs, some fields are given a default value.
1823 	/// If an element's field has a default value set, it is omitted
1824 	/// from the textmap, and therefore we have to account for it by
1825 	/// preemptively setting that value beforehand.
1826 
1827 	for (i = 0, vt = vertexes; i < numvertexes; i++, vt++)
1828 	{
1829 		// Defaults.
1830 		vt->x = vt->y = INT32_MAX;
1831 		vt->floorzset = vt->ceilingzset = false;
1832 		vt->floorz = vt->ceilingz = 0;
1833 
1834 		TextmapParse(vertexesPos[i], i, ParseTextmapVertexParameter);
1835 
1836 		if (vt->x == INT32_MAX)
1837 			I_Error("P_LoadTextmap: vertex %s has no x value set!\n", sizeu1(i));
1838 		if (vt->y == INT32_MAX)
1839 			I_Error("P_LoadTextmap: vertex %s has no y value set!\n", sizeu1(i));
1840 	}
1841 
1842 	for (i = 0, sc = sectors; i < numsectors; i++, sc++)
1843 	{
1844 		// Defaults.
1845 		sc->floorheight = 0;
1846 		sc->ceilingheight = 0;
1847 
1848 		sc->floorpic = 0;
1849 		sc->ceilingpic = 0;
1850 
1851 		sc->lightlevel = 255;
1852 
1853 		sc->special = 0;
1854 		Tag_FSet(&sc->tags, 0);
1855 
1856 		sc->floor_xoffs = sc->floor_yoffs = 0;
1857 		sc->ceiling_xoffs = sc->ceiling_yoffs = 0;
1858 
1859 		sc->floorpic_angle = sc->ceilingpic_angle = 0;
1860 
1861 		sc->colormap_protected = false;
1862 
1863 		textmap_colormap.used = false;
1864 		textmap_colormap.lightcolor = 0;
1865 		textmap_colormap.lightalpha = 25;
1866 		textmap_colormap.fadecolor = 0;
1867 		textmap_colormap.fadealpha = 25;
1868 		textmap_colormap.fadestart = 0;
1869 		textmap_colormap.fadeend = 31;
1870 		textmap_colormap.flags = 0;
1871 		TextmapParse(sectorsPos[i], i, ParseTextmapSectorParameter);
1872 
1873 		P_InitializeSector(sc);
1874 		if (textmap_colormap.used)
1875 		{
1876 			INT32 rgba = P_ColorToRGBA(textmap_colormap.lightcolor, textmap_colormap.lightalpha);
1877 			INT32 fadergba = P_ColorToRGBA(textmap_colormap.fadecolor, textmap_colormap.fadealpha);
1878 			sc->extra_colormap = sc->spawn_extra_colormap = R_CreateColormap(rgba, fadergba, textmap_colormap.fadestart, textmap_colormap.fadeend, textmap_colormap.flags);
1879 		}
1880 		TextmapFixFlatOffsets(sc);
1881 	}
1882 
1883 	for (i = 0, ld = lines; i < numlines; i++, ld++)
1884 	{
1885 		// Defaults.
1886 		ld->v1 = ld->v2 = NULL;
1887 		ld->flags = 0;
1888 		ld->special = 0;
1889 		Tag_FSet(&ld->tags, 0);
1890 
1891 		memset(ld->args, 0, NUMLINEARGS*sizeof(*ld->args));
1892 		memset(ld->stringargs, 0x00, NUMLINESTRINGARGS*sizeof(*ld->stringargs));
1893 		ld->alpha = FRACUNIT;
1894 		ld->executordelay = 0;
1895 		ld->sidenum[0] = 0xffff;
1896 		ld->sidenum[1] = 0xffff;
1897 
1898 		TextmapParse(linesPos[i], i, ParseTextmapLinedefParameter);
1899 
1900 		if (!ld->v1)
1901 			I_Error("P_LoadTextmap: linedef %s has no v1 value set!\n", sizeu1(i));
1902 		if (!ld->v2)
1903 			I_Error("P_LoadTextmap: linedef %s has no v2 value set!\n", sizeu1(i));
1904 		if (ld->sidenum[0] == 0xffff)
1905 			I_Error("P_LoadTextmap: linedef %s has no sidefront value set!\n", sizeu1(i));
1906 
1907 		P_InitializeLinedef(ld);
1908 	}
1909 
1910 	for (i = 0, sd = sides; i < numsides; i++, sd++)
1911 	{
1912 		// Defaults.
1913 		sd->textureoffset = 0;
1914 		sd->rowoffset = 0;
1915 		sd->toptexture = R_TextureNumForName("-");
1916 		sd->midtexture = R_TextureNumForName("-");
1917 		sd->bottomtexture = R_TextureNumForName("-");
1918 		sd->sector = NULL;
1919 		sd->repeatcnt = 0;
1920 
1921 		TextmapParse(sidesPos[i], i, ParseTextmapSidedefParameter);
1922 
1923 		if (!sd->sector)
1924 			I_Error("P_LoadTextmap: sidedef %s has no sector value set!\n", sizeu1(i));
1925 
1926 		P_InitializeSidedef(sd);
1927 	}
1928 
1929 	for (i = 0, mt = mapthings; i < nummapthings; i++, mt++)
1930 	{
1931 		// Defaults.
1932 		mt->x = mt->y = 0;
1933 		mt->angle = mt->pitch = mt->roll = 0;
1934 		mt->type = 0;
1935 		mt->options = 0;
1936 		mt->z = 0;
1937 		mt->extrainfo = 0;
1938 		Tag_FSet(&mt->tags, 0);
1939 		mt->scale = FRACUNIT;
1940 		memset(mt->args, 0, NUMMAPTHINGARGS*sizeof(*mt->args));
1941 		memset(mt->stringargs, 0x00, NUMMAPTHINGSTRINGARGS*sizeof(*mt->stringargs));
1942 		mt->mobj = NULL;
1943 
1944 		TextmapParse(mapthingsPos[i], i, ParseTextmapThingParameter);
1945 	}
1946 }
1947 
P_ProcessLinedefsAfterSidedefs(void)1948 static void P_ProcessLinedefsAfterSidedefs(void)
1949 {
1950 	size_t i = numlines;
1951 	register line_t *ld = lines;
1952 	for (; i--; ld++)
1953 	{
1954 		ld->frontsector = sides[ld->sidenum[0]].sector; //e6y: Can't be -1 here
1955 		ld->backsector = ld->sidenum[1] != 0xffff ? sides[ld->sidenum[1]].sector : 0;
1956 
1957 		switch (ld->special)
1958 		{
1959 		// Compile linedef 'text' from both sidedefs 'text' for appropriate specials.
1960 		case 331: // Trigger linedef executor: Skin - Continuous
1961 		case 332: // Trigger linedef executor: Skin - Each time
1962 		case 333: // Trigger linedef executor: Skin - Once
1963 		case 443: // Calls a named Lua function
1964 			if (sides[ld->sidenum[0]].text)
1965 			{
1966 				size_t len = strlen(sides[ld->sidenum[0]].text) + 1;
1967 				if (ld->sidenum[1] != 0xffff && sides[ld->sidenum[1]].text)
1968 					len += strlen(sides[ld->sidenum[1]].text);
1969 				ld->text = Z_Malloc(len, PU_LEVEL, NULL);
1970 				M_Memcpy(ld->text, sides[ld->sidenum[0]].text, strlen(sides[ld->sidenum[0]].text) + 1);
1971 				if (ld->sidenum[1] != 0xffff && sides[ld->sidenum[1]].text)
1972 					M_Memcpy(ld->text + strlen(ld->text) + 1, sides[ld->sidenum[1]].text, strlen(sides[ld->sidenum[1]].text) + 1);
1973 			}
1974 			break;
1975 		case 447: // Change colormap
1976 		case 455: // Fade colormap
1977 			if (udmf)
1978 				break;
1979 			if (ld->flags & ML_DONTPEGBOTTOM) // alternate alpha (by texture offsets)
1980 			{
1981 				extracolormap_t *exc = R_CopyColormap(sides[ld->sidenum[0]].colormap_data, false);
1982 				INT16 alpha = max(min(sides[ld->sidenum[0]].textureoffset >> FRACBITS, 25), -25);
1983 				INT16 fadealpha = max(min(sides[ld->sidenum[0]].rowoffset >> FRACBITS, 25), -25);
1984 
1985 				// If alpha is negative, set "subtract alpha" flag and store absolute value
1986 				if (alpha < 0)
1987 				{
1988 					alpha *= -1;
1989 					ld->args[2] |= TMCF_SUBLIGHTA;
1990 				}
1991 				if (fadealpha < 0)
1992 				{
1993 					fadealpha *= -1;
1994 					ld->args[2] |= TMCF_SUBFADEA;
1995 				}
1996 
1997 				exc->rgba = R_GetRgbaRGB(exc->rgba) + R_PutRgbaA(alpha);
1998 				exc->fadergba = R_GetRgbaRGB(exc->fadergba) + R_PutRgbaA(fadealpha);
1999 
2000 				if (!(sides[ld->sidenum[0]].colormap_data = R_GetColormapFromList(exc)))
2001 				{
2002 					exc->colormap = R_CreateLightTable(exc);
2003 					R_AddColormapToList(exc);
2004 					sides[ld->sidenum[0]].colormap_data = exc;
2005 				}
2006 				else
2007 					Z_Free(exc);
2008 			}
2009 			break;
2010 		}
2011 	}
2012 }
2013 
P_LoadMapData(const virtres_t * virt)2014 static boolean P_LoadMapData(const virtres_t *virt)
2015 {
2016 	virtlump_t *virtvertexes = NULL, *virtsectors = NULL, *virtsidedefs = NULL, *virtlinedefs = NULL, *virtthings = NULL;
2017 
2018 	// Count map data.
2019 	if (udmf) // Count how many entries for each type we got in textmap.
2020 	{
2021 		virtlump_t *textmap = vres_Find(virt, "TEXTMAP");
2022 		if (!TextmapCount(textmap->data, textmap->size))
2023 			return false;
2024 	}
2025 	else
2026 	{
2027 		virtthings   = vres_Find(virt, "THINGS");
2028 		virtvertexes = vres_Find(virt, "VERTEXES");
2029 		virtsectors  = vres_Find(virt, "SECTORS");
2030 		virtsidedefs = vres_Find(virt, "SIDEDEFS");
2031 		virtlinedefs = vres_Find(virt, "LINEDEFS");
2032 
2033 		if (!virtthings)
2034 			I_Error("THINGS lump not found");
2035 		if (!virtvertexes)
2036 			I_Error("VERTEXES lump not found");
2037 		if (!virtsectors)
2038 			I_Error("SECTORS lump not found");
2039 		if (!virtsidedefs)
2040 			I_Error("SIDEDEFS lump not found");
2041 		if (!virtlinedefs)
2042 			I_Error("LINEDEFS lump not found");
2043 
2044 		// Traditional doom map format just assumes the number of elements from the lump sizes.
2045 		numvertexes  = virtvertexes->size / sizeof (mapvertex_t);
2046 		numsectors   = virtsectors->size  / sizeof (mapsector_t);
2047 		numsides     = virtsidedefs->size / sizeof (mapsidedef_t);
2048 		numlines     = virtlinedefs->size / sizeof (maplinedef_t);
2049 		nummapthings = virtthings->size   / (5 * sizeof (INT16));
2050 	}
2051 
2052 	if (numvertexes <= 0)
2053 		I_Error("Level has no vertices");
2054 	if (numsectors <= 0)
2055 		I_Error("Level has no sectors");
2056 	if (numsides <= 0)
2057 		I_Error("Level has no sidedefs");
2058 	if (numlines <= 0)
2059 		I_Error("Level has no linedefs");
2060 
2061 	vertexes  = Z_Calloc(numvertexes * sizeof (*vertexes), PU_LEVEL, NULL);
2062 	sectors   = Z_Calloc(numsectors * sizeof (*sectors), PU_LEVEL, NULL);
2063 	sides     = Z_Calloc(numsides * sizeof (*sides), PU_LEVEL, NULL);
2064 	lines     = Z_Calloc(numlines * sizeof (*lines), PU_LEVEL, NULL);
2065 	mapthings = Z_Calloc(nummapthings * sizeof (*mapthings), PU_LEVEL, NULL);
2066 
2067 	// Allocate a big chunk of memory as big as our MAXLEVELFLATS limit.
2068 	//Fab : FIXME: allocate for whatever number of flats - 512 different flats per level should be plenty
2069 	foundflats = calloc(MAXLEVELFLATS, sizeof (*foundflats));
2070 	if (foundflats == NULL)
2071 		I_Error("Ran out of memory while loading sectors\n");
2072 
2073 	numlevelflats = 0;
2074 
2075 	// Load map data.
2076 	if (udmf)
2077 		P_LoadTextmap();
2078 	else
2079 	{
2080 		P_LoadVertices(virtvertexes->data);
2081 		P_LoadSectors(virtsectors->data);
2082 		P_LoadLinedefs(virtlinedefs->data);
2083 		P_LoadSidedefs(virtsidedefs->data);
2084 		P_LoadThings(virtthings->data);
2085 	}
2086 
2087 	P_ProcessLinedefsAfterSidedefs();
2088 
2089 	R_ClearTextureNumCache(true);
2090 
2091 	// set the sky flat num
2092 	skyflatnum = P_AddLevelFlat(SKYFLATNAME, foundflats);
2093 
2094 	// copy table for global usage
2095 	levelflats = M_Memcpy(Z_Calloc(numlevelflats * sizeof (*levelflats), PU_LEVEL, NULL), foundflats, numlevelflats * sizeof (levelflat_t));
2096 	free(foundflats);
2097 
2098 	// search for animated flats and set up
2099 	P_SetupLevelFlatAnims();
2100 
2101 	return true;
2102 }
2103 
P_InitializeSubsector(subsector_t * ss)2104 static void P_InitializeSubsector(subsector_t *ss)
2105 {
2106 	ss->sector = NULL;
2107 	ss->validcount = 0;
2108 }
2109 
P_LoadSubsectors(UINT8 * data)2110 static inline void P_LoadSubsectors(UINT8 *data)
2111 {
2112 	mapsubsector_t *ms = (mapsubsector_t*)data;
2113 	subsector_t *ss = subsectors;
2114 	size_t i;
2115 
2116 	for (i = 0; i < numsubsectors; i++, ss++, ms++)
2117 	{
2118 		ss->numlines = SHORT(ms->numsegs);
2119 		ss->firstline = SHORT(ms->firstseg);
2120 		P_InitializeSubsector(ss);
2121 	}
2122 }
2123 
P_LoadNodes(UINT8 * data)2124 static void P_LoadNodes(UINT8 *data)
2125 {
2126 	UINT8 j, k;
2127 	mapnode_t *mn = (mapnode_t*)data;
2128 	node_t *no = nodes;
2129 	size_t i;
2130 
2131 	for (i = 0; i < numnodes; i++, no++, mn++)
2132 	{
2133 		no->x = SHORT(mn->x)<<FRACBITS;
2134 		no->y = SHORT(mn->y)<<FRACBITS;
2135 		no->dx = SHORT(mn->dx)<<FRACBITS;
2136 		no->dy = SHORT(mn->dy)<<FRACBITS;
2137 		for (j = 0; j < 2; j++)
2138 		{
2139 			no->children[j] = SHORT(mn->children[j]);
2140 			for (k = 0; k < 4; k++)
2141 				no->bbox[j][k] = SHORT(mn->bbox[j][k])<<FRACBITS;
2142 		}
2143 	}
2144 }
2145 
2146 /** Computes the length of a seg in fracunits.
2147   *
2148   * \param seg Seg to compute length for.
2149   * \return Length in fracunits.
2150   */
P_SegLength(seg_t * seg)2151 static fixed_t P_SegLength(seg_t *seg)
2152 {
2153 	INT64 dx = (seg->v2->x - seg->v1->x)>>1;
2154 	INT64 dy = (seg->v2->y - seg->v1->y)>>1;
2155 	return FixedHypot(dx, dy)<<1;
2156 }
2157 
2158 #ifdef HWRENDER
2159 /** Computes the length of a seg as a float.
2160   * This is needed for OpenGL.
2161   *
2162   * \param seg Seg to compute length for.
2163   * \return Length as a float.
2164   */
P_SegLengthFloat(seg_t * seg)2165 static inline float P_SegLengthFloat(seg_t *seg)
2166 {
2167 	float dx, dy;
2168 
2169 	// make a vector (start at origin)
2170 	dx = FIXED_TO_FLOAT(seg->v2->x - seg->v1->x);
2171 	dy = FIXED_TO_FLOAT(seg->v2->y - seg->v1->y);
2172 
2173 	return (float)hypot(dx, dy);
2174 }
2175 #endif
2176 
P_InitializeSeg(seg_t * seg)2177 static void P_InitializeSeg(seg_t *seg)
2178 {
2179 	if (seg->linedef)
2180 	{
2181 		UINT16 side = seg->linedef->sidenum[seg->side];
2182 
2183 		if (side == 0xffff)
2184 			I_Error("P_InitializeSeg: Seg %s refers to side %d of linedef %s, which doesn't exist!\n", sizeu1((size_t)(seg - segs)), seg->side, sizeu1((size_t)(seg->linedef - lines)));
2185 
2186 		seg->sidedef = &sides[side];
2187 
2188 		seg->frontsector = seg->sidedef->sector;
2189 		seg->backsector = (seg->linedef->flags & ML_TWOSIDED) ? sides[seg->linedef->sidenum[seg->side ^ 1]].sector : NULL;
2190 	}
2191 
2192 #ifdef HWRENDER
2193 	seg->pv1 = seg->pv2 = NULL;
2194 
2195 	//Hurdler: 04/12/2000: for now, only used in hardware mode
2196 	seg->lightmaps = NULL; // list of static lightmap for this seg
2197 #endif
2198 
2199 	seg->numlights = 0;
2200 	seg->rlights = NULL;
2201 	seg->polyseg = NULL;
2202 	seg->dontrenderme = false;
2203 }
2204 
P_LoadSegs(UINT8 * data)2205 static void P_LoadSegs(UINT8 *data)
2206 {
2207 	mapseg_t *ms = (mapseg_t*)data;
2208 	seg_t *seg = segs;
2209 	size_t i;
2210 
2211 	for (i = 0; i < numsegs; i++, seg++, ms++)
2212 	{
2213 		seg->v1 = &vertexes[SHORT(ms->v1)];
2214 		seg->v2 = &vertexes[SHORT(ms->v2)];
2215 
2216 		seg->side = SHORT(ms->side);
2217 
2218 		seg->offset = (SHORT(ms->offset)) << FRACBITS;
2219 
2220 		seg->angle = (SHORT(ms->angle)) << FRACBITS;
2221 
2222 		seg->linedef = &lines[SHORT(ms->linedef)];
2223 
2224 		seg->length = P_SegLength(seg);
2225 #ifdef HWRENDER
2226 		seg->flength = (rendermode == render_opengl) ? P_SegLengthFloat(seg) : 0;
2227 #endif
2228 
2229 		seg->glseg = false;
2230 		P_InitializeSeg(seg);
2231 	}
2232 }
2233 
2234 typedef enum {
2235 	NT_DOOM,
2236 	NT_XNOD,
2237 	NT_ZNOD,
2238 	NT_XGLN,
2239 	NT_ZGLN,
2240 	NT_XGL2,
2241 	NT_ZGL2,
2242 	NT_XGL3,
2243 	NT_ZGL3,
2244 	NT_UNSUPPORTED,
2245 	NUMNODETYPES
2246 } nodetype_t;
2247 
2248 // Find out the BSP format.
P_GetNodetype(const virtres_t * virt,UINT8 ** nodedata)2249 static nodetype_t P_GetNodetype(const virtres_t *virt, UINT8 **nodedata)
2250 {
2251 	boolean supported[NUMNODETYPES] = {0};
2252 	nodetype_t nodetype = NT_UNSUPPORTED;
2253 	char signature[4 + 1];
2254 
2255 	if (udmf)
2256 	{
2257 		*nodedata = vres_Find(virt, "ZNODES")->data;
2258 		supported[NT_XGLN] = supported[NT_XGL3] = true;
2259 	}
2260 	else
2261 	{
2262 		virtlump_t *virtsegs = vres_Find(virt, "SEGS");
2263 		virtlump_t *virtssectors;
2264 
2265 		if (virtsegs && virtsegs->size)
2266 		{
2267 			*nodedata = vres_Find(virt, "NODES")->data;
2268 			return NT_DOOM; // Traditional map format BSP tree.
2269 		}
2270 
2271 		virtssectors = vres_Find(virt, "SSECTORS");
2272 
2273 		if (virtssectors && virtssectors->size)
2274 		{ // Possibly GL nodes: NODES ignored, SSECTORS takes precedence as nodes lump, (It is confusing yeah) and has a signature.
2275 			*nodedata = virtssectors->data;
2276 			supported[NT_XGLN] = supported[NT_ZGLN] = supported[NT_XGL3] = true;
2277 		}
2278 		else
2279 		{ // Possibly ZDoom extended nodes: SSECTORS is empty, NODES has a signature.
2280 			*nodedata = vres_Find(virt, "NODES")->data;
2281 			supported[NT_XNOD] = supported[NT_ZNOD] = true;
2282 		}
2283 	}
2284 
2285 	M_Memcpy(signature, *nodedata, 4);
2286 	signature[4] = '\0';
2287 	(*nodedata) += 4;
2288 
2289 	if (!strcmp(signature, "XNOD"))
2290 		nodetype = NT_XNOD;
2291 	else if (!strcmp(signature, "ZNOD"))
2292 		nodetype = NT_ZNOD;
2293 	else if (!strcmp(signature, "XGLN"))
2294 		nodetype = NT_XGLN;
2295 	else if (!strcmp(signature, "ZGLN"))
2296 		nodetype = NT_ZGLN;
2297 	else if (!strcmp(signature, "XGL3"))
2298 		nodetype = NT_XGL3;
2299 
2300 	return supported[nodetype] ? nodetype : NT_UNSUPPORTED;
2301 }
2302 
2303 // Extended node formats feature additional vertices; useful for OpenGL, but totally useless in gamelogic.
P_LoadExtraVertices(UINT8 ** data)2304 static boolean P_LoadExtraVertices(UINT8 **data)
2305 {
2306 	UINT32 origvrtx = READUINT32((*data));
2307 	UINT32 xtrvrtx = READUINT32((*data));
2308 	line_t* ld = lines;
2309 	vertex_t *oldpos = vertexes;
2310 	ssize_t offset;
2311 	size_t i;
2312 
2313 	if (numvertexes != origvrtx) // If native vertex count doesn't match node original vertex count, bail out (broken data?).
2314 	{
2315 		CONS_Alert(CONS_WARNING, "Vertex count in map data and nodes differ!\n");
2316 		return false;
2317 	}
2318 
2319 	if (!xtrvrtx)
2320 		return true;
2321 
2322 	// If extra vertexes were generated, reallocate the vertex array and fix the pointers.
2323 	numvertexes += xtrvrtx;
2324 	vertexes = Z_Realloc(vertexes, numvertexes*sizeof(*vertexes), PU_LEVEL, NULL);
2325 	offset = (size_t)(vertexes - oldpos);
2326 
2327 	for (i = 0, ld = lines; i < numlines; i++, ld++)
2328 	{
2329 		ld->v1 += offset;
2330 		ld->v2 += offset;
2331 	}
2332 
2333 	// Read extra vertex data.
2334 	for (i = origvrtx; i < numvertexes; i++)
2335 	{
2336 		vertexes[i].x = READFIXED((*data));
2337 		vertexes[i].y = READFIXED((*data));
2338 	}
2339 
2340 	return true;
2341 }
2342 
P_LoadExtendedSubsectorsAndSegs(UINT8 ** data,nodetype_t nodetype)2343 static boolean P_LoadExtendedSubsectorsAndSegs(UINT8 **data, nodetype_t nodetype)
2344 {
2345 	size_t i, k;
2346 	INT16 m;
2347 	seg_t *seg;
2348 
2349 	// Subsectors
2350 	numsubsectors = READUINT32((*data));
2351 	subsectors = Z_Calloc(numsubsectors*sizeof(*subsectors), PU_LEVEL, NULL);
2352 
2353 	for (i = 0; i < numsubsectors; i++)
2354 		subsectors[i].numlines = READUINT32((*data));
2355 
2356 	// Segs
2357 	numsegs = READUINT32((*data));
2358 	segs = Z_Calloc(numsegs*sizeof(*segs), PU_LEVEL, NULL);
2359 
2360 	for (i = 0, k = 0; i < numsubsectors; i++)
2361 	{
2362 		subsectors[i].firstline = k;
2363 		P_InitializeSubsector(&subsectors[i]);
2364 
2365 		switch (nodetype)
2366 		{
2367 		case NT_XGLN:
2368 		case NT_XGL3:
2369 			for (m = 0; m < subsectors[i].numlines; m++, k++)
2370 			{
2371 				UINT32 vertexnum = READUINT32((*data));
2372 				UINT16 linenum;
2373 
2374 				if (vertexnum >= numvertexes)
2375 					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid vertex %d!\n", sizeu1(k), m, vertexnum);
2376 
2377 				segs[k - 1 + ((m == 0) ? subsectors[i].numlines : 0)].v2 = segs[k].v1 = &vertexes[vertexnum];
2378 
2379 				READUINT32((*data)); // partner, can be ignored by software renderer
2380 
2381 				linenum = (nodetype == NT_XGL3) ? READUINT32((*data)) : READUINT16((*data));
2382 				if (linenum != 0xFFFF && linenum >= numlines)
2383 					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid linedef %d!\n", sizeu1(k), m, linenum);
2384 				segs[k].glseg = (linenum == 0xFFFF);
2385 				segs[k].linedef = (linenum == 0xFFFF) ? NULL : &lines[linenum];
2386 				segs[k].side = READUINT8((*data));
2387 			}
2388 			break;
2389 
2390 		case NT_XNOD:
2391 			for (m = 0; m < subsectors[i].numlines; m++, k++)
2392 			{
2393 				UINT32 v1num = READUINT32((*data));
2394 				UINT32 v2num = READUINT32((*data));
2395 				UINT16 linenum = READUINT16((*data));
2396 
2397 				if (v1num >= numvertexes)
2398 					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid v1 %d!\n", sizeu1(k), m, v1num);
2399 				if (v2num >= numvertexes)
2400 					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid v2 %d!\n", sizeu1(k), m, v2num);
2401 				if (linenum >= numlines)
2402 					I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid linedef %d!\n", sizeu1(k), m, linenum);
2403 
2404 				segs[k].v1 = &vertexes[v1num];
2405 				segs[k].v2 = &vertexes[v2num];
2406 				segs[k].linedef = &lines[linenum];
2407 				segs[k].side = READUINT8((*data));
2408 				segs[k].glseg = false;
2409 			}
2410 			break;
2411 
2412 		default:
2413 			return false;
2414 		}
2415 	}
2416 
2417 	for (i = 0, seg = segs; i < numsegs; i++, seg++)
2418 	{
2419 		vertex_t *v1 = seg->v1;
2420 		vertex_t *v2 = seg->v2;
2421 		P_InitializeSeg(seg);
2422 		seg->angle = R_PointToAngle2(v1->x, v1->y, v2->x, v2->y);
2423 		if (seg->linedef)
2424 			segs[i].offset = FixedHypot(v1->x - seg->linedef->v1->x, v1->y - seg->linedef->v1->y);
2425 		seg->length = P_SegLength(seg);
2426 #ifdef HWRENDER
2427 		seg->flength = (rendermode == render_opengl) ? P_SegLengthFloat(seg) : 0;
2428 #endif
2429 	}
2430 
2431 	return true;
2432 }
2433 
2434 // Auxiliary function: Shrink node ID from 32-bit to 16-bit.
ShrinkNodeID(UINT32 x)2435 static UINT16 ShrinkNodeID(UINT32 x) {
2436 	UINT16 mask = (x >> 16) & 0xC000;
2437 	UINT16 result = x;
2438 	return result | mask;
2439 }
2440 
P_LoadExtendedNodes(UINT8 ** data,nodetype_t nodetype)2441 static void P_LoadExtendedNodes(UINT8 **data, nodetype_t nodetype)
2442 {
2443 	node_t *mn;
2444 	size_t i, j, k;
2445 	boolean xgl3 = (nodetype == NT_XGL3);
2446 
2447 	numnodes = READINT32((*data));
2448 	nodes = Z_Calloc(numnodes*sizeof(*nodes), PU_LEVEL, NULL);
2449 
2450 	for (i = 0, mn = nodes; i < numnodes; i++, mn++)
2451 	{
2452 		// Splitter
2453 		mn->x = xgl3 ? READINT32((*data)) : (READINT16((*data)) << FRACBITS);
2454 		mn->y = xgl3 ? READINT32((*data)) : (READINT16((*data)) << FRACBITS);
2455 		mn->dx = xgl3 ? READINT32((*data)) : (READINT16((*data)) << FRACBITS);
2456 		mn->dy = xgl3 ? READINT32((*data)) : (READINT16((*data)) << FRACBITS);
2457 
2458 		// Bounding boxes
2459 		for (j = 0; j < 2; j++)
2460 			for (k = 0; k < 4; k++)
2461 				mn->bbox[j][k] = READINT16((*data)) << FRACBITS;
2462 
2463 		//Children
2464 		mn->children[0] = ShrinkNodeID(READUINT32((*data))); /// \todo Use UINT32 for node children in a future, instead?
2465 		mn->children[1] = ShrinkNodeID(READUINT32((*data)));
2466 	}
2467 }
2468 
P_LoadMapBSP(const virtres_t * virt)2469 static void P_LoadMapBSP(const virtres_t *virt)
2470 {
2471 	UINT8 *nodedata = NULL;
2472 	nodetype_t nodetype = P_GetNodetype(virt, &nodedata);
2473 
2474 	switch (nodetype)
2475 	{
2476 	case NT_DOOM:
2477 	{
2478 		virtlump_t *virtssectors = vres_Find(virt, "SSECTORS");
2479 		virtlump_t* virtnodes = vres_Find(virt, "NODES");
2480 		virtlump_t *virtsegs = vres_Find(virt, "SEGS");
2481 
2482 		numsubsectors = virtssectors->size / sizeof(mapsubsector_t);
2483 		numnodes      = virtnodes->size    / sizeof(mapnode_t);
2484 		numsegs       = virtsegs->size     / sizeof(mapseg_t);
2485 
2486 		if (numsubsectors <= 0)
2487 			I_Error("Level has no subsectors (did you forget to run it through a nodesbuilder?)");
2488 		if (numnodes <= 0)
2489 			I_Error("Level has no nodes");
2490 		if (numsegs <= 0)
2491 			I_Error("Level has no segs");
2492 
2493 		subsectors = Z_Calloc(numsubsectors * sizeof(*subsectors), PU_LEVEL, NULL);
2494 		nodes      = Z_Calloc(numnodes * sizeof(*nodes), PU_LEVEL, NULL);
2495 		segs       = Z_Calloc(numsegs * sizeof(*segs), PU_LEVEL, NULL);
2496 
2497 		P_LoadSubsectors(virtssectors->data);
2498 		P_LoadNodes(virtnodes->data);
2499 		P_LoadSegs(virtsegs->data);
2500 		break;
2501 	}
2502 	case NT_XNOD:
2503 	case NT_XGLN:
2504 	case NT_XGL3:
2505 		if (!P_LoadExtraVertices(&nodedata))
2506 			return;
2507 		if (!P_LoadExtendedSubsectorsAndSegs(&nodedata, nodetype))
2508 			return;
2509 		P_LoadExtendedNodes(&nodedata, nodetype);
2510 		break;
2511 	default:
2512 		CONS_Alert(CONS_WARNING, "Unsupported BSP format detected.\n");
2513 		return;
2514 	}
2515 	return;
2516 }
2517 
2518 // Split from P_LoadBlockMap for convenience
2519 // -- Monster Iestyn 08/01/18
P_ReadBlockMapLump(INT16 * wadblockmaplump,size_t count)2520 static void P_ReadBlockMapLump(INT16 *wadblockmaplump, size_t count)
2521 {
2522 	size_t i;
2523 	blockmaplump = Z_Calloc(sizeof (*blockmaplump) * count, PU_LEVEL, NULL);
2524 
2525 	// killough 3/1/98: Expand wad blockmap into larger internal one,
2526 	// by treating all offsets except -1 as unsigned and zero-extending
2527 	// them. This potentially doubles the size of blockmaps allowed,
2528 	// because Doom originally considered the offsets as always signed.
2529 
2530 	blockmaplump[0] = SHORT(wadblockmaplump[0]);
2531 	blockmaplump[1] = SHORT(wadblockmaplump[1]);
2532 	blockmaplump[2] = (INT32)(SHORT(wadblockmaplump[2])) & 0xffff;
2533 	blockmaplump[3] = (INT32)(SHORT(wadblockmaplump[3])) & 0xffff;
2534 
2535 	for (i = 4; i < count; i++)
2536 	{
2537 		INT16 t = SHORT(wadblockmaplump[i]);          // killough 3/1/98
2538 		blockmaplump[i] = t == -1 ? (INT32)-1 : (INT32) t & 0xffff;
2539 	}
2540 }
2541 
2542 // This needs to be a separate function
2543 // because making both the WAD and PK3 loading code use
2544 // the same functions is trickier than it looks for blockmap
2545 // -- Monster Iestyn 09/01/18
P_LoadBlockMap(UINT8 * data,size_t count)2546 static boolean P_LoadBlockMap(UINT8 *data, size_t count)
2547 {
2548 	if (!count || count >= 0x20000)
2549 		return false;
2550 
2551 	//CONS_Printf("Reading blockmap lump for pk3...\n");
2552 
2553 	// no need to malloc anything, assume the data is uncompressed for now
2554 	count /= 2;
2555 	P_ReadBlockMapLump((INT16 *)data, count);
2556 
2557 	bmaporgx = blockmaplump[0]<<FRACBITS;
2558 	bmaporgy = blockmaplump[1]<<FRACBITS;
2559 	bmapwidth = blockmaplump[2];
2560 	bmapheight = blockmaplump[3];
2561 
2562 	// clear out mobj chains
2563 	count = sizeof (*blocklinks)* bmapwidth*bmapheight;
2564 	blocklinks = Z_Calloc(count, PU_LEVEL, NULL);
2565 	blockmap = blockmaplump+4;
2566 
2567 	// haleyjd 2/22/06: setup polyobject blockmap
2568 	count = sizeof(*polyblocklinks) * bmapwidth * bmapheight;
2569 	polyblocklinks = Z_Calloc(count, PU_LEVEL, NULL);
2570 	return true;
2571 }
2572 
LineInBlock(fixed_t cx1,fixed_t cy1,fixed_t cx2,fixed_t cy2,fixed_t bx1,fixed_t by1)2573 static boolean LineInBlock(fixed_t cx1, fixed_t cy1, fixed_t cx2, fixed_t cy2, fixed_t bx1, fixed_t by1)
2574 {
2575 	fixed_t bbox[4];
2576 	line_t testline;
2577 	vertex_t vtest;
2578 
2579 	bbox[BOXRIGHT] = bx1 + MAPBLOCKUNITS;
2580 	bbox[BOXTOP] = by1 + MAPBLOCKUNITS;
2581 	bbox[BOXLEFT] = bx1;
2582 	bbox[BOXBOTTOM] = by1;
2583 
2584 	// Trivial rejection
2585 	if (cx1 < bbox[BOXLEFT] && cx2 < bbox[BOXLEFT])
2586 		return false;
2587 
2588 	if (cx1 > bbox[BOXRIGHT] && cx2 > bbox[BOXRIGHT])
2589 		return false;
2590 
2591 	if (cy1 < bbox[BOXBOTTOM] && cy2 < bbox[BOXBOTTOM])
2592 		return false;
2593 
2594 	if (cy1 > bbox[BOXTOP] && cy2 > bbox[BOXTOP])
2595 		return false;
2596 
2597 	// Rats, guess we gotta check
2598 	// if the line intersects
2599 	// any sides of the block.
2600 	cx1 <<= FRACBITS;
2601 	cy1 <<= FRACBITS;
2602 	cx2 <<= FRACBITS;
2603 	cy2 <<= FRACBITS;
2604 	bbox[BOXTOP] <<= FRACBITS;
2605 	bbox[BOXBOTTOM] <<= FRACBITS;
2606 	bbox[BOXLEFT] <<= FRACBITS;
2607 	bbox[BOXRIGHT] <<= FRACBITS;
2608 
2609 	testline.v1 = &vtest;
2610 
2611 	testline.v1->x = cx1;
2612 	testline.v1->y = cy1;
2613 	testline.dx = cx2 - cx1;
2614 	testline.dy = cy2 - cy1;
2615 
2616 	if ((testline.dx > 0) ^ (testline.dy > 0))
2617 		testline.slopetype = ST_NEGATIVE;
2618 	else
2619 		testline.slopetype = ST_POSITIVE;
2620 
2621 	return P_BoxOnLineSide(bbox, &testline) == -1;
2622 }
2623 
2624 //
2625 // killough 10/98:
2626 //
2627 // Rewritten to use faster algorithm.
2628 //
2629 // SSN Edit: Killough's wasn't accurate enough, sometimes excluding
2630 // blocks that the line did in fact exist in, so now we use
2631 // a fail-safe approach that puts a 'box' around each line.
2632 //
2633 // Please note: This section of code is not interchangable with TeamTNT's
2634 // code which attempts to fix the same problem.
P_CreateBlockMap(void)2635 static void P_CreateBlockMap(void)
2636 {
2637 	register size_t i;
2638 	fixed_t minx = INT32_MAX, miny = INT32_MAX, maxx = INT32_MIN, maxy = INT32_MIN;
2639 	// First find limits of map
2640 
2641 	for (i = 0; i < numvertexes; i++)
2642 	{
2643 		if (vertexes[i].x>>FRACBITS < minx)
2644 			minx = vertexes[i].x>>FRACBITS;
2645 		else if (vertexes[i].x>>FRACBITS > maxx)
2646 			maxx = vertexes[i].x>>FRACBITS;
2647 		if (vertexes[i].y>>FRACBITS < miny)
2648 			miny = vertexes[i].y>>FRACBITS;
2649 		else if (vertexes[i].y>>FRACBITS > maxy)
2650 			maxy = vertexes[i].y>>FRACBITS;
2651 	}
2652 
2653 	// Save blockmap parameters
2654 	bmaporgx = minx << FRACBITS;
2655 	bmaporgy = miny << FRACBITS;
2656 	bmapwidth = ((maxx-minx) >> MAPBTOFRAC) + 1;
2657 	bmapheight = ((maxy-miny) >> MAPBTOFRAC)+ 1;
2658 
2659 	// Compute blockmap, which is stored as a 2d array of variable-sized lists.
2660 	//
2661 	// Pseudocode:
2662 	//
2663 	// For each linedef:
2664 	//
2665 	//   Map the starting and ending vertices to blocks.
2666 	//
2667 	//   Starting in the starting vertex's block, do:
2668 	//
2669 	//     Add linedef to current block's list, dynamically resizing it.
2670 	//
2671 	//     If current block is the same as the ending vertex's block, exit loop.
2672 	//
2673 	//     Move to an adjacent block by moving towards the ending block in
2674 	//     either the x or y direction, to the block which contains the linedef.
2675 
2676 	{
2677 		typedef struct
2678 		{
2679 			INT32 n, nalloc;
2680 			INT32 *list;
2681 		} bmap_t; // blocklist structure
2682 
2683 		size_t tot = bmapwidth * bmapheight; // size of blockmap
2684 		bmap_t *bmap = calloc(tot, sizeof (*bmap)); // array of blocklists
2685 		boolean straight;
2686 
2687 		if (bmap == NULL) I_Error("%s: Out of memory making blockmap", "P_CreateBlockMap");
2688 
2689 		for (i = 0; i < numlines; i++)
2690 		{
2691 			// starting coordinates
2692 			INT32 x = (lines[i].v1->x>>FRACBITS) - minx;
2693 			INT32 y = (lines[i].v1->y>>FRACBITS) - miny;
2694 			INT32 bxstart, bxend, bystart, byend, v2x, v2y, curblockx, curblocky;
2695 
2696 			v2x = lines[i].v2->x>>FRACBITS;
2697 			v2y = lines[i].v2->y>>FRACBITS;
2698 
2699 			// Draw a "box" around the line.
2700 			bxstart = (x >> MAPBTOFRAC);
2701 			bystart = (y >> MAPBTOFRAC);
2702 
2703 			v2x -= minx;
2704 			v2y -= miny;
2705 
2706 			bxend = ((v2x) >> MAPBTOFRAC);
2707 			byend = ((v2y) >> MAPBTOFRAC);
2708 
2709 			if (bxend < bxstart)
2710 			{
2711 				INT32 temp = bxstart;
2712 				bxstart = bxend;
2713 				bxend = temp;
2714 			}
2715 
2716 			if (byend < bystart)
2717 			{
2718 				INT32 temp = bystart;
2719 				bystart = byend;
2720 				byend = temp;
2721 			}
2722 
2723 			// Catch straight lines
2724 			// This fixes the error where straight lines
2725 			// directly on a blockmap boundary would not
2726 			// be included in the proper blocks.
2727 			if (lines[i].v1->y == lines[i].v2->y)
2728 			{
2729 				straight = true;
2730 				bystart--;
2731 				byend++;
2732 			}
2733 			else if (lines[i].v1->x == lines[i].v2->x)
2734 			{
2735 				straight = true;
2736 				bxstart--;
2737 				bxend++;
2738 			}
2739 			else
2740 				straight = false;
2741 
2742 			// Now we simply iterate block-by-block until we reach the end block.
2743 			for (curblockx = bxstart; curblockx <= bxend; curblockx++)
2744 			for (curblocky = bystart; curblocky <= byend; curblocky++)
2745 			{
2746 				size_t b = curblocky * bmapwidth + curblockx;
2747 
2748 				if (b >= tot)
2749 					continue;
2750 
2751 				if (!straight && !(LineInBlock((fixed_t)x, (fixed_t)y, (fixed_t)v2x, (fixed_t)v2y, (fixed_t)(curblockx << MAPBTOFRAC), (fixed_t)(curblocky << MAPBTOFRAC))))
2752 					continue;
2753 
2754 				// Increase size of allocated list if necessary
2755 				if (bmap[b].n >= bmap[b].nalloc)
2756 				{
2757 					// Graue 02-29-2004: make code more readable, don't realloc a null pointer
2758 					// (because it crashes for me, and because the comp.lang.c FAQ says so)
2759 					if (bmap[b].nalloc == 0)
2760 						bmap[b].nalloc = 8;
2761 					else
2762 						bmap[b].nalloc *= 2;
2763 					bmap[b].list = Z_Realloc(bmap[b].list, bmap[b].nalloc * sizeof (*bmap->list), PU_CACHE, &bmap[b].list);
2764 					if (!bmap[b].list)
2765 						I_Error("Out of Memory in P_CreateBlockMap");
2766 				}
2767 
2768 				// Add linedef to end of list
2769 				bmap[b].list[bmap[b].n++] = (INT32)i;
2770 			}
2771 		}
2772 
2773 		// Compute the total size of the blockmap.
2774 		//
2775 		// Compression of empty blocks is performed by reserving two offset words
2776 		// at tot and tot+1.
2777 		//
2778 		// 4 words, unused if this routine is called, are reserved at the start.
2779 		{
2780 			size_t count = tot + 6; // we need at least 1 word per block, plus reserved's
2781 
2782 			for (i = 0; i < tot; i++)
2783 				if (bmap[i].n)
2784 					count += bmap[i].n + 2; // 1 header word + 1 trailer word + blocklist
2785 
2786 			// Allocate blockmap lump with computed count
2787 			blockmaplump = Z_Calloc(sizeof (*blockmaplump) * count, PU_LEVEL, NULL);
2788 		}
2789 
2790 		// Now compress the blockmap.
2791 		{
2792 			size_t ndx = tot += 4; // Advance index to start of linedef lists
2793 			bmap_t *bp = bmap; // Start of uncompressed blockmap
2794 
2795 			blockmaplump[ndx++] = 0; // Store an empty blockmap list at start
2796 			blockmaplump[ndx++] = -1; // (Used for compression)
2797 
2798 			for (i = 4; i < tot; i++, bp++)
2799 				if (bp->n) // Non-empty blocklist
2800 				{
2801 					blockmaplump[blockmaplump[i] = (INT32)(ndx++)] = 0; // Store index & header
2802 					do
2803 						blockmaplump[ndx++] = bp->list[--bp->n]; // Copy linedef list
2804 					while (bp->n);
2805 					blockmaplump[ndx++] = -1; // Store trailer
2806 					Z_Free(bp->list); // Free linedef list
2807 				}
2808 				else // Empty blocklist: point to reserved empty blocklist
2809 					blockmaplump[i] = (INT32)tot;
2810 
2811 			free(bmap); // Free uncompressed blockmap
2812 		}
2813 	}
2814 	{
2815 		size_t count = sizeof (*blocklinks) * bmapwidth * bmapheight;
2816 		// clear out mobj chains (copied from from P_LoadBlockMap)
2817 		blocklinks = Z_Calloc(count, PU_LEVEL, NULL);
2818 		blockmap = blockmaplump + 4;
2819 
2820 		// haleyjd 2/22/06: setup polyobject blockmap
2821 		count = sizeof(*polyblocklinks) * bmapwidth * bmapheight;
2822 		polyblocklinks = Z_Calloc(count, PU_LEVEL, NULL);
2823 	}
2824 }
2825 
2826 // PK3 version
2827 // -- Monster Iestyn 09/01/18
P_LoadReject(UINT8 * data,size_t count)2828 static void P_LoadReject(UINT8 *data, size_t count)
2829 {
2830 	if (!count) // zero length, someone probably used ZDBSP
2831 	{
2832 		rejectmatrix = NULL;
2833 		CONS_Debug(DBG_SETUP, "P_LoadReject: REJECT lump has size 0, will not be loaded\n");
2834 	}
2835 	else
2836 	{
2837 		rejectmatrix = Z_Malloc(count, PU_LEVEL, NULL); // allocate memory for the reject matrix
2838 		M_Memcpy(rejectmatrix, data, count); // copy the data into it
2839 	}
2840 }
2841 
P_LoadMapLUT(const virtres_t * virt)2842 static void P_LoadMapLUT(const virtres_t *virt)
2843 {
2844 	virtlump_t* virtblockmap = vres_Find(virt, "BLOCKMAP");
2845 	virtlump_t* virtreject   = vres_Find(virt, "REJECT");
2846 
2847 	// Lookup tables
2848 	if (virtreject)
2849 		P_LoadReject(virtreject->data, virtreject->size);
2850 	else
2851 		rejectmatrix = NULL;
2852 
2853 	if (!(virtblockmap && P_LoadBlockMap(virtblockmap->data, virtblockmap->size)))
2854 		P_CreateBlockMap();
2855 }
2856 
2857 //
2858 // P_LinkMapData
2859 // Builds sector line lists and subsector sector numbers.
2860 // Finds block bounding boxes for sectors.
2861 //
P_LinkMapData(void)2862 static void P_LinkMapData(void)
2863 {
2864 	size_t i, j;
2865 	line_t *li;
2866 	sector_t *sector;
2867 	subsector_t *ss = subsectors;
2868 	size_t sidei;
2869 	seg_t *seg;
2870 	fixed_t bbox[4];
2871 
2872 	// look up sector number for each subsector
2873 	for (i = 0; i < numsubsectors; i++, ss++)
2874 	{
2875 		if (ss->firstline >= numsegs)
2876 			CorruptMapError(va("P_LinkMapData: ss->firstline invalid "
2877 				"(subsector %s, firstline refers to %d of %s)", sizeu1(i), ss->firstline,
2878 				sizeu2(numsegs)));
2879 		seg = &segs[ss->firstline];
2880 		sidei = (size_t)(seg->sidedef - sides);
2881 		if (!seg->sidedef)
2882 			CorruptMapError(va("P_LinkMapData: seg->sidedef is NULL "
2883 				"(subsector %s, firstline is %d)", sizeu1(i), ss->firstline));
2884 		if (seg->sidedef - sides < 0 || seg->sidedef - sides > (UINT16)numsides)
2885 			CorruptMapError(va("P_LinkMapData: seg->sidedef refers to sidedef %s of %s "
2886 				"(subsector %s, firstline is %d)", sizeu1(sidei), sizeu2(numsides),
2887 				sizeu3(i), ss->firstline));
2888 		if (!seg->sidedef->sector)
2889 			CorruptMapError(va("P_LinkMapData: seg->sidedef->sector is NULL "
2890 				"(subsector %s, firstline is %d, sidedef is %s)", sizeu1(i), ss->firstline,
2891 				sizeu1(sidei)));
2892 		ss->sector = seg->sidedef->sector;
2893 	}
2894 
2895 	// count number of lines in each sector
2896 	for (i = 0, li = lines; i < numlines; i++, li++)
2897 	{
2898 		li->frontsector->linecount++;
2899 
2900 		if (li->backsector && li->backsector != li->frontsector)
2901 			li->backsector->linecount++;
2902 	}
2903 
2904 	// allocate linebuffers for each sector
2905 	for (i = 0, sector = sectors; i < numsectors; i++, sector++)
2906 	{
2907 		if (sector->linecount == 0) // no lines found?
2908 		{
2909 			sector->lines = NULL;
2910 			CONS_Debug(DBG_SETUP, "P_LinkMapData: sector %s has no lines\n", sizeu1(i));
2911 		}
2912 		else
2913 		{
2914 			sector->lines = Z_Calloc(sector->linecount * sizeof(line_t*), PU_LEVEL, NULL);
2915 
2916 			// zero the count, since we'll later use this to track how many we've recorded
2917 			sector->linecount = 0;
2918 		}
2919 	}
2920 
2921 	// iterate through lines, assigning them to sectors' linebuffers,
2922 	// and recalculate the counts in the process
2923 	for (i = 0, li = lines; i < numlines; i++, li++)
2924 	{
2925 		li->frontsector->lines[li->frontsector->linecount++] = li;
2926 
2927 		if (li->backsector && li->backsector != li->frontsector)
2928 			li->backsector->lines[li->backsector->linecount++] = li;
2929 	}
2930 
2931 	// set soundorg's position for each sector
2932 	for (i = 0, sector = sectors; i < numsectors; i++, sector++)
2933 	{
2934 		M_ClearBox(bbox);
2935 
2936 		if (sector->linecount != 0)
2937 		{
2938 			for (j = 0; j < sector->linecount; j++)
2939 			{
2940 				li = sector->lines[j];
2941 				M_AddToBox(bbox, li->v1->x, li->v1->y);
2942 				M_AddToBox(bbox, li->v2->x, li->v2->y);
2943 			}
2944 		}
2945 
2946 		// set the degenmobj_t to the middle of the bounding box
2947 		sector->soundorg.x = (((bbox[BOXRIGHT]>>FRACBITS) + (bbox[BOXLEFT]>>FRACBITS))/2)<<FRACBITS;
2948 		sector->soundorg.y = (((bbox[BOXTOP]>>FRACBITS) + (bbox[BOXBOTTOM]>>FRACBITS))/2)<<FRACBITS;
2949 		sector->soundorg.z = sector->floorheight; // default to sector's floor height
2950 	}
2951 }
2952 
2953 //For maps in binary format, converts setup of specials to UDMF format.
P_ConvertBinaryMap(void)2954 static void P_ConvertBinaryMap(void)
2955 {
2956 	size_t i;
2957 
2958 	for (i = 0; i < numlines; i++)
2959 	{
2960 		mtag_t tag = Tag_FGet(&lines[i].tags);
2961 
2962 		switch (lines[i].special)
2963 		{
2964 		case 20: //PolyObject first line
2965 		{
2966 			INT32 check = -1;
2967 			INT32 paramline = -1;
2968 
2969 			TAG_ITER_DECLARECOUNTER(0);
2970 
2971 			TAG_ITER_LINES(0, tag, check)
2972 			{
2973 				if (lines[check].special == 22)
2974 				{
2975 					paramline = check;
2976 					break;
2977 				}
2978 			}
2979 
2980 			//PolyObject ID
2981 			lines[i].args[0] = tag;
2982 
2983 			//Default: Invisible planes
2984 			lines[i].args[3] |= TMPF_INVISIBLEPLANES;
2985 
2986 			//Linedef executor tag
2987 			lines[i].args[4] = 32000 + lines[i].args[0];
2988 
2989 			if (paramline == -1)
2990 				break; // no extra settings to apply, let's leave it
2991 
2992 			//Parent ID
2993 			lines[i].args[1] = lines[paramline].frontsector->special;
2994 			//Translucency
2995 			lines[i].args[2] = (lines[paramline].flags & ML_DONTPEGTOP)
2996 						? (sides[lines[paramline].sidenum[0]].textureoffset >> FRACBITS)
2997 						: ((lines[paramline].frontsector->floorheight >> FRACBITS) / 100);
2998 
2999 			//Flags
3000 			if (lines[paramline].flags & ML_EFFECT1)
3001 				lines[i].args[3] |= TMPF_NOINSIDES;
3002 			if (lines[paramline].flags & ML_EFFECT2)
3003 				lines[i].args[3] |= TMPF_INTANGIBLE;
3004 			if (lines[paramline].flags & ML_EFFECT3)
3005 				lines[i].args[3] |= TMPF_PUSHABLESTOP;
3006 			if (lines[paramline].flags & ML_EFFECT4)
3007 				lines[i].args[3] &= ~TMPF_INVISIBLEPLANES;
3008 			/*if (lines[paramline].flags & ML_EFFECT5)
3009 				lines[i].args[3] |= TMPF_DONTCLIPPLANES;*/
3010 			if (lines[paramline].flags & ML_EFFECT6)
3011 				lines[i].args[3] |= TMPF_SPLAT;
3012 			if (lines[paramline].flags & ML_NOCLIMB)
3013 				lines[i].args[3] |= TMPF_EXECUTOR;
3014 
3015 			break;
3016 		}
3017 		case 443: //Call Lua function
3018 			if (lines[i].text)
3019 			{
3020 				lines[i].stringargs[0] = Z_Malloc(strlen(lines[i].text) + 1, PU_LEVEL, NULL);
3021 				M_Memcpy(lines[i].stringargs[0], lines[i].text, strlen(lines[i].text) + 1);
3022 			}
3023 			else
3024 				CONS_Alert(CONS_WARNING, "Linedef %s is missing the hook name of the Lua function to call! (This should be given in the front texture fields)\n", sizeu1(i));
3025 			break;
3026 		case 447: //Change colormap
3027 			lines[i].args[0] = Tag_FGet(&lines[i].tags);
3028 			if (lines[i].flags & ML_EFFECT3)
3029 				lines[i].args[2] |= TMCF_RELATIVE;
3030 			if (lines[i].flags & ML_EFFECT1)
3031 				lines[i].args[2] |= TMCF_SUBLIGHTR|TMCF_SUBFADER;
3032 			if (lines[i].flags & ML_NOCLIMB)
3033 				lines[i].args[2] |= TMCF_SUBLIGHTG|TMCF_SUBFADEG;
3034 			if (lines[i].flags & ML_EFFECT2)
3035 				lines[i].args[2] |= TMCF_SUBLIGHTB|TMCF_SUBFADEB;
3036 			break;
3037 		case 455: //Fade colormap
3038 		{
3039 			INT32 speed = (INT32)((((lines[i].flags & ML_DONTPEGBOTTOM) || !sides[lines[i].sidenum[0]].rowoffset) && lines[i].sidenum[1] != 0xFFFF) ?
3040 				abs(sides[lines[i].sidenum[1]].rowoffset >> FRACBITS)
3041 				: abs(sides[lines[i].sidenum[0]].rowoffset >> FRACBITS));
3042 
3043 			lines[i].args[0] = Tag_FGet(&lines[i].tags);
3044 			if (lines[i].flags & ML_EFFECT4)
3045 				lines[i].args[2] = speed;
3046 			else
3047 				lines[i].args[2] = (256 + speed - 1)/speed;
3048 			if (lines[i].flags & ML_EFFECT3)
3049 				lines[i].args[3] |= TMCF_RELATIVE;
3050 			if (lines[i].flags & ML_EFFECT1)
3051 				lines[i].args[3] |= TMCF_SUBLIGHTR|TMCF_SUBFADER;
3052 			if (lines[i].flags & ML_NOCLIMB)
3053 				lines[i].args[3] |= TMCF_SUBLIGHTG|TMCF_SUBFADEG;
3054 			if (lines[i].flags & ML_EFFECT2)
3055 				lines[i].args[3] |= TMCF_SUBLIGHTB|TMCF_SUBFADEB;
3056 			if (lines[i].flags & ML_BOUNCY)
3057 				lines[i].args[3] |= TMCF_FROMBLACK;
3058 			if (lines[i].flags & ML_EFFECT5)
3059 				lines[i].args[3] |= TMCF_OVERRIDE;
3060 			break;
3061 		}
3062 		case 456: //Stop fading colormap
3063 			lines[i].args[0] = Tag_FGet(&lines[i].tags);
3064 			break;
3065 		case 606: //Colormap
3066 			lines[i].args[0] = Tag_FGet(&lines[i].tags);
3067 			break;
3068 		case 700: //Slope front sector floor
3069 		case 701: //Slope front sector ceiling
3070 		case 702: //Slope front sector floor and ceiling
3071 		case 703: //Slope front sector floor and back sector ceiling
3072 		case 710: //Slope back sector floor
3073 		case 711: //Slope back sector ceiling
3074 		case 712: //Slope back sector floor and ceiling
3075 		case 713: //Slope back sector floor and front sector ceiling
3076 		{
3077 			boolean frontfloor = (lines[i].special == 700 || lines[i].special == 702 || lines[i].special == 703);
3078 			boolean backfloor = (lines[i].special == 710 || lines[i].special == 712 || lines[i].special == 713);
3079 			boolean frontceil = (lines[i].special == 701 || lines[i].special == 702 || lines[i].special == 713);
3080 			boolean backceil = (lines[i].special == 711 || lines[i].special == 712 || lines[i].special == 703);
3081 
3082 			lines[i].args[0] = backfloor ? TMS_BACK : (frontfloor ? TMS_FRONT : TMS_NONE);
3083 			lines[i].args[1] = backceil ? TMS_BACK : (frontceil ? TMS_FRONT : TMS_NONE);
3084 
3085 			if (lines[i].flags & ML_NETONLY)
3086 				lines[i].args[2] |= TMSL_NOPHYSICS;
3087 			if (lines[i].flags & ML_NONET)
3088 				lines[i].args[2] |= TMSL_DYNAMIC;
3089 
3090 			lines[i].special = 700;
3091 			break;
3092 		}
3093 		case 704: //Slope front sector floor by 3 tagged vertices
3094 		case 705: //Slope front sector ceiling by 3 tagged vertices
3095 		case 714: //Slope back sector floor by 3 tagged vertices
3096 		case 715: //Slope back sector ceiling  by 3 tagged vertices
3097 		{
3098 			if (lines[i].special == 704)
3099 				lines[i].args[0] = TMSP_FRONTFLOOR;
3100 			else if (lines[i].special == 705)
3101 				lines[i].args[0] = TMSP_FRONTCEILING;
3102 			else if (lines[i].special == 714)
3103 				lines[i].args[0] = TMSP_BACKFLOOR;
3104 			else if (lines[i].special == 715)
3105 				lines[i].args[0] = TMSP_BACKCEILING;
3106 
3107 			lines[i].args[1] = tag;
3108 
3109 			if (lines[i].flags & ML_EFFECT6)
3110 			{
3111 				UINT8 side = lines[i].special >= 714;
3112 
3113 				if (side == 1 && lines[i].sidenum[1] == 0xffff)
3114 					CONS_Debug(DBG_GAMELOGIC, "P_ConvertBinaryMap: Line special %d (line #%s) missing 2nd side!\n", lines[i].special, sizeu1(i));
3115 				else
3116 				{
3117 					lines[i].args[2] = sides[lines[i].sidenum[side]].textureoffset >> FRACBITS;
3118 					lines[i].args[3] = sides[lines[i].sidenum[side]].rowoffset >> FRACBITS;
3119 				}
3120 			}
3121 			else
3122 			{
3123 				lines[i].args[2] = lines[i].args[1];
3124 				lines[i].args[3] = lines[i].args[1];
3125 			}
3126 
3127 			if (lines[i].flags & ML_NETONLY)
3128 				lines[i].args[4] |= TMSL_NOPHYSICS;
3129 			if (lines[i].flags & ML_NONET)
3130 				lines[i].args[4] |= TMSL_DYNAMIC;
3131 
3132 			lines[i].special = 704;
3133 			break;
3134 		}
3135 		case 720: //Copy front side floor slope
3136 		case 721: //Copy front side ceiling slope
3137 		case 722: //Copy front side floor and ceiling slope
3138 			if (lines[i].special != 721)
3139 				lines[i].args[0] = tag;
3140 			if (lines[i].special != 720)
3141 				lines[i].args[1] = tag;
3142 			lines[i].special = 720;
3143 			break;
3144 		case 900: //Translucent wall (10%)
3145 		case 901: //Translucent wall (20%)
3146 		case 902: //Translucent wall (30%)
3147 		case 903: //Translucent wall (40%)
3148 		case 904: //Translucent wall (50%)
3149 		case 905: //Translucent wall (60%)
3150 		case 906: //Translucent wall (70%)
3151 		case 907: //Translucent wall (80%)
3152 		case 908: //Translucent wall (90%)
3153 			lines[i].alpha = ((909 - lines[i].special) << FRACBITS)/10;
3154 			break;
3155 		default:
3156 			break;
3157 		}
3158 
3159 		//Linedef executor delay
3160 		if (lines[i].special >= 400 && lines[i].special < 500)
3161 		{
3162 			//Dummy value to indicate that this executor is delayed.
3163 			//The real value is taken from the back sector at runtime.
3164 			if (lines[i].flags & ML_DONTPEGTOP)
3165 				lines[i].executordelay = 1;
3166 		}
3167 	}
3168 
3169 	for (i = 0; i < nummapthings; i++)
3170 	{
3171 		switch (mapthings[i].type)
3172 		{
3173 		case 750:
3174 			Tag_FSet(&mapthings[i].tags, mapthings[i].angle);
3175 			break;
3176 		case 760:
3177 		case 761:
3178 			Tag_FSet(&mapthings[i].tags, mapthings[i].angle);
3179 			break;
3180 		case 762:
3181 		{
3182 			INT32 check = -1;
3183 			INT32 firstline = -1;
3184 			mtag_t tag = mapthings[i].angle;
3185 
3186 			TAG_ITER_DECLARECOUNTER(0);
3187 
3188 			Tag_FSet(&mapthings[i].tags, tag);
3189 
3190 			TAG_ITER_LINES(0, tag, check)
3191 			{
3192 				if (lines[check].special == 20)
3193 				{
3194 					firstline = check;
3195 					break;
3196 				}
3197 			}
3198 
3199 			if (firstline != -1)
3200 				lines[firstline].args[3] |= TMPF_CRUSH;
3201 
3202 			mapthings[i].type = 761;
3203 			break;
3204 		}
3205 		case 780:
3206 			Tag_FSet(&mapthings[i].tags, mapthings[i].extrainfo);
3207 			break;
3208 		default:
3209 			break;
3210 		}
3211 	}
3212 }
3213 
3214 /** Compute MD5 message digest for bytes read from memory source
3215   *
3216   * The resulting message digest number will be written into the 16 bytes
3217   * beginning at RESBLOCK.
3218   *
3219   * \param filename path of file
3220   * \param resblock resulting MD5 checksum
3221   * \return 0 if MD5 checksum was made, and is at resblock, 1 if error was found
3222   */
P_MakeBufferMD5(const char * buffer,size_t len,void * resblock)3223 static INT32 P_MakeBufferMD5(const char *buffer, size_t len, void *resblock)
3224 {
3225 #ifdef NOMD5
3226 	(void)buffer;
3227 	(void)len;
3228 	memset(resblock, 0x00, 16);
3229 	return 1;
3230 #else
3231 	tic_t t = I_GetTime();
3232 	CONS_Debug(DBG_SETUP, "Making MD5\n");
3233 	if (md5_buffer(buffer, len, resblock) == NULL)
3234 		return 1;
3235 	CONS_Debug(DBG_SETUP, "MD5 calc took %f seconds\n", (float)(I_GetTime() - t)/NEWTICRATE);
3236 	return 0;
3237 #endif
3238 }
3239 
P_MakeMapMD5(virtres_t * virt,void * dest)3240 static void P_MakeMapMD5(virtres_t *virt, void *dest)
3241 {
3242 	unsigned char resmd5[16];
3243 
3244 	if (udmf)
3245 	{
3246 		virtlump_t *textmap = vres_Find(virt, "TEXTMAP");
3247 		P_MakeBufferMD5((char*)textmap->data, textmap->size, resmd5);
3248 	}
3249 	else
3250 	{
3251 		unsigned char linemd5[16];
3252 		unsigned char sectormd5[16];
3253 		unsigned char thingmd5[16];
3254 		unsigned char sidedefmd5[16];
3255 		UINT8 i;
3256 
3257 		// Create a hash for the current map
3258 		// get the actual lumps!
3259 		virtlump_t* virtlines   = vres_Find(virt, "LINEDEFS");
3260 		virtlump_t* virtsectors = vres_Find(virt, "SECTORS");
3261 		virtlump_t* virtmthings = vres_Find(virt, "THINGS");
3262 		virtlump_t* virtsides   = vres_Find(virt, "SIDEDEFS");
3263 
3264 		P_MakeBufferMD5((char*)virtlines->data,   virtlines->size, linemd5);
3265 		P_MakeBufferMD5((char*)virtsectors->data, virtsectors->size,  sectormd5);
3266 		P_MakeBufferMD5((char*)virtmthings->data, virtmthings->size,   thingmd5);
3267 		P_MakeBufferMD5((char*)virtsides->data,   virtsides->size, sidedefmd5);
3268 
3269 		for (i = 0; i < 16; i++)
3270 			resmd5[i] = (linemd5[i] + sectormd5[i] + thingmd5[i] + sidedefmd5[i]) & 0xFF;
3271 	}
3272 
3273 	M_Memcpy(dest, &resmd5, 16);
3274 }
3275 
P_LoadMapFromFile(void)3276 static boolean P_LoadMapFromFile(void)
3277 {
3278 	virtres_t *virt = vres_GetMap(lastloadedmaplumpnum);
3279 	virtlump_t *textmap = vres_Find(virt, "TEXTMAP");
3280 	size_t i;
3281 	udmf = textmap != NULL;
3282 
3283 	if (!P_LoadMapData(virt))
3284 		return false;
3285 	P_LoadMapBSP(virt);
3286 	P_LoadMapLUT(virt);
3287 
3288 	P_LinkMapData();
3289 
3290 	Taglist_InitGlobalTables();
3291 
3292 	if (!udmf)
3293 		P_ConvertBinaryMap();
3294 
3295 	// Copy relevant map data for NetArchive purposes.
3296 	spawnsectors = Z_Calloc(numsectors * sizeof(*sectors), PU_LEVEL, NULL);
3297 	spawnlines = Z_Calloc(numlines * sizeof(*lines), PU_LEVEL, NULL);
3298 	spawnsides = Z_Calloc(numsides * sizeof(*sides), PU_LEVEL, NULL);
3299 
3300 	memcpy(spawnsectors, sectors, numsectors * sizeof(*sectors));
3301 	memcpy(spawnlines, lines, numlines * sizeof(*lines));
3302 	memcpy(spawnsides, sides, numsides * sizeof(*sides));
3303 
3304 	for (i = 0; i < numsectors; i++)
3305 		if (sectors[i].tags.count)
3306 			spawnsectors[i].tags.tags = memcpy(Z_Malloc(sectors[i].tags.count*sizeof(mtag_t), PU_LEVEL, NULL), sectors[i].tags.tags, sectors[i].tags.count*sizeof(mtag_t));
3307 
3308 	P_MakeMapMD5(virt, &mapmd5);
3309 
3310 	vres_Free(virt);
3311 	return true;
3312 }
3313 
3314 //
3315 // LEVEL INITIALIZATION FUNCTIONS
3316 //
3317 
3318 /** Sets up a sky texture to use for the level.
3319   * The sky texture is used instead of F_SKY1.
3320   */
P_SetupLevelSky(INT32 skynum,boolean global)3321 void P_SetupLevelSky(INT32 skynum, boolean global)
3322 {
3323 	char skytexname[12];
3324 
3325 	sprintf(skytexname, "SKY%d", skynum);
3326 	skytexture = R_TextureNumForName(skytexname);
3327 	levelskynum = skynum;
3328 
3329 	// Global change
3330 	if (global)
3331 		globallevelskynum = levelskynum;
3332 
3333 	// Don't go beyond for dedicated servers
3334 	if (dedicated)
3335 		return;
3336 
3337 	// scale up the old skies, if needed
3338 	R_SetupSkyDraw();
3339 }
3340 
3341 static const char *maplumpname;
3342 lumpnum_t lastloadedmaplumpnum; // for comparative savegame
3343 
3344 //
3345 // P_LevelInitStuff
3346 //
3347 // Some player initialization for map start.
3348 //
P_InitLevelSettings(void)3349 static void P_InitLevelSettings(void)
3350 {
3351 	INT32 i;
3352 	boolean canresetlives = true;
3353 
3354 	leveltime = 0;
3355 
3356 	modulothing = 0;
3357 
3358 	// special stage tokens, emeralds, and ring total
3359 	tokenbits = 0;
3360 	runemeraldmanager = false;
3361 	emeraldspawndelay = 60*TICRATE;
3362 	if ((netgame || multiplayer) && !G_IsSpecialStage(gamemap))
3363 		nummaprings = -1;
3364 	else
3365 		nummaprings = mapheaderinfo[gamemap-1]->startrings;
3366 
3367 	// emerald hunt
3368 	hunt1 = hunt2 = hunt3 = NULL;
3369 
3370 	// map time limit
3371 	if (mapheaderinfo[gamemap-1]->countdown)
3372 	{
3373 		tic_t maxtime = 0;
3374 		countdowntimer = mapheaderinfo[gamemap-1]->countdown * TICRATE;
3375 		for (i = 0; i < MAXPLAYERS; i++)
3376 		{
3377 			if (!playeringame[i])
3378 				continue;
3379 			if (players[i].starposttime > maxtime)
3380 				maxtime = players[i].starposttime;
3381 		}
3382 		countdowntimer -= maxtime;
3383 	}
3384 	else
3385 		countdowntimer = 0;
3386 	countdowntimeup = false;
3387 
3388 	// clear ctf pointers
3389 	redflag = blueflag = NULL;
3390 	rflagpoint = bflagpoint = NULL;
3391 
3392 	// circuit, race and competition stuff
3393 	circuitmap = false;
3394 	numstarposts = 0;
3395 	ssspheres = timeinmap = 0;
3396 
3397 	// special stage
3398 	stagefailed = true; // assume failed unless proven otherwise - P_GiveEmerald or emerald touchspecial
3399 	// Reset temporary record data
3400 	memset(&ntemprecords, 0, sizeof(nightsdata_t));
3401 
3402 	// earthquake camera
3403 	memset(&quake,0,sizeof(struct quake));
3404 
3405 	if ((netgame || multiplayer) && G_GametypeUsesCoopStarposts() && cv_coopstarposts.value == 2)
3406 	{
3407 		for (i = 0; i < MAXPLAYERS; i++)
3408 		{
3409 			if (playeringame[i] && players[i].lives > 0)
3410 			{
3411 				canresetlives = false;
3412 				break;
3413 			}
3414 		}
3415 	}
3416 
3417 	countdown = countdown2 = exitfadestarted = 0;
3418 
3419 	for (i = 0; i < MAXPLAYERS; i++)
3420 	{
3421 		G_PlayerReborn(i, true);
3422 
3423 		if (canresetlives && (netgame || multiplayer) && playeringame[i] && (G_CompetitionGametype() || players[i].lives <= 0))
3424 		{
3425 			// In Co-Op, replenish a user's lives if they are depleted.
3426 			players[i].lives = cv_startinglives.value;
3427 		}
3428 
3429 		// obliteration station...
3430 		players[i].numboxes = players[i].totalring =\
3431 		 players[i].laps = players[i].marescore = players[i].lastmarescore =\
3432 		 players[i].mare = players[i].exiting = 0;
3433 
3434 		players[i].drillmeter = 40*20;
3435 
3436 		// hit these too
3437 		players[i].pflags &= ~(PF_GAMETYPEOVER);
3438 	}
3439 
3440 	if (botingame)
3441 		CV_SetValue(&cv_analog[1], true);
3442 }
3443 
3444 // Respawns all the mapthings and mobjs in the map from the already loaded map data.
P_RespawnThings(void)3445 void P_RespawnThings(void)
3446 {
3447 	// Search through all the thinkers.
3448 	thinker_t *think;
3449 	INT32 i, viewid = -1, centerid = -1; // for skyboxes
3450 
3451 	// check if these are any of the normal viewpoint/centerpoint mobjs in the level or not
3452 	if (skyboxmo[0] || skyboxmo[1])
3453 		for (i = 0; i < 16; i++)
3454 		{
3455 			if (skyboxmo[0] && skyboxmo[0] == skyboxviewpnts[i])
3456 				viewid = i; // save id just in case
3457 			if (skyboxmo[1] && skyboxmo[1] == skyboxcenterpnts[i])
3458 				centerid = i; // save id just in case
3459 		}
3460 
3461 	for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
3462 	{
3463 		if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
3464 			continue;
3465 		P_RemoveMobj((mobj_t *)think);
3466 	}
3467 
3468 	P_InitLevelSettings();
3469 
3470 	localaiming = 0;
3471 	localaiming2 = 0;
3472 
3473 	P_SpawnMapThings(true);
3474 
3475 	// restore skybox viewpoint/centerpoint if necessary, set them to defaults if we can't do that
3476 	skyboxmo[0] = skyboxviewpnts[(viewid >= 0) ? viewid : 0];
3477 	skyboxmo[1] = skyboxcenterpnts[(centerid >= 0) ? centerid : 0];
3478 }
3479 
P_RunLevelScript(const char * scriptname)3480 static void P_RunLevelScript(const char *scriptname)
3481 {
3482 	if (!(mapheaderinfo[gamemap-1]->levelflags & LF_SCRIPTISFILE))
3483 	{
3484 		lumpnum_t lumpnum;
3485 		char newname[9];
3486 
3487 		strncpy(newname, scriptname, 8);
3488 
3489 		newname[8] = '\0';
3490 
3491 		lumpnum = W_CheckNumForName(newname);
3492 
3493 		if (lumpnum == LUMPERROR || W_LumpLength(lumpnum) == 0)
3494 		{
3495 			CONS_Debug(DBG_SETUP, "SOC Error: script lump %s not found/not valid.\n", newname);
3496 			return;
3497 		}
3498 
3499 		COM_BufInsertText(W_CacheLumpNum(lumpnum, PU_CACHE));
3500 	}
3501 	else
3502 	{
3503 		COM_BufAddText(va("exec %s\n", scriptname));
3504 	}
3505 	COM_BufExecute(); // Run it!
3506 }
3507 
P_ForceCharacter(const char * forcecharskin)3508 static void P_ForceCharacter(const char *forcecharskin)
3509 {
3510 	if (netgame)
3511 	{
3512 		char skincmd[33];
3513 		if (splitscreen)
3514 		{
3515 			sprintf(skincmd, "skin2 %s\n", forcecharskin);
3516 			CV_Set(&cv_skin2, forcecharskin);
3517 		}
3518 
3519 		sprintf(skincmd, "skin %s\n", forcecharskin);
3520 		COM_BufAddText(skincmd);
3521 	}
3522 	else
3523 	{
3524 		if (splitscreen)
3525 		{
3526 			SetPlayerSkin(secondarydisplayplayer, forcecharskin);
3527 			if ((unsigned)cv_playercolor2.value != skins[players[secondarydisplayplayer].skin].prefcolor)
3528 			{
3529 				CV_StealthSetValue(&cv_playercolor2, skins[players[secondarydisplayplayer].skin].prefcolor);
3530 				players[secondarydisplayplayer].skincolor = skins[players[secondarydisplayplayer].skin].prefcolor;
3531 			}
3532 		}
3533 
3534 		SetPlayerSkin(consoleplayer, forcecharskin);
3535 		// normal player colors in single player
3536 		if ((unsigned)cv_playercolor.value != skins[players[consoleplayer].skin].prefcolor)
3537 		{
3538 			CV_StealthSetValue(&cv_playercolor, skins[players[consoleplayer].skin].prefcolor);
3539 			players[consoleplayer].skincolor = skins[players[consoleplayer].skin].prefcolor;
3540 		}
3541 	}
3542 }
3543 
P_ResetSpawnpoints(void)3544 static void P_ResetSpawnpoints(void)
3545 {
3546 	UINT8 i;
3547 
3548 	numdmstarts = numredctfstarts = numbluectfstarts = 0;
3549 
3550 	// reset the player starts
3551 	for (i = 0; i < MAXPLAYERS; i++)
3552 		playerstarts[i] = bluectfstarts[i] = redctfstarts[i] = NULL;
3553 
3554 	for (i = 0; i < MAX_DM_STARTS; i++)
3555 		deathmatchstarts[i] = NULL;
3556 
3557 	for (i = 0; i < 2; i++)
3558 		skyboxmo[i] = NULL;
3559 
3560 	for (i = 0; i < 16; i++)
3561 		skyboxviewpnts[i] = skyboxcenterpnts[i] = NULL;
3562 }
3563 
P_LoadRecordGhosts(void)3564 static void P_LoadRecordGhosts(void)
3565 {
3566 	const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1;
3567 	char *gpath = malloc(glen);
3568 	INT32 i;
3569 
3570 	if (!gpath)
3571 		return;
3572 
3573 	sprintf(gpath,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(gamemap));
3574 
3575 	// Best Score ghost
3576 	if (cv_ghost_bestscore.value)
3577 	{
3578 		for (i = 0; i < numskins; ++i)
3579 		{
3580 			if (cv_ghost_bestscore.value == 1 && players[consoleplayer].skin != i)
3581 				continue;
3582 
3583 			if (FIL_FileExists(va("%s-%s-score-best.lmp", gpath, skins[i].name)))
3584 				G_AddGhost(va("%s-%s-score-best.lmp", gpath, skins[i].name));
3585 		}
3586 	}
3587 
3588 	// Best Time ghost
3589 	if (cv_ghost_besttime.value)
3590 	{
3591 		for (i = 0; i < numskins; ++i)
3592 		{
3593 			if (cv_ghost_besttime.value == 1 && players[consoleplayer].skin != i)
3594 				continue;
3595 
3596 			if (FIL_FileExists(va("%s-%s-time-best.lmp", gpath, skins[i].name)))
3597 				G_AddGhost(va("%s-%s-time-best.lmp", gpath, skins[i].name));
3598 		}
3599 	}
3600 
3601 	// Best Rings ghost
3602 	if (cv_ghost_bestrings.value)
3603 	{
3604 		for (i = 0; i < numskins; ++i)
3605 		{
3606 			if (cv_ghost_bestrings.value == 1 && players[consoleplayer].skin != i)
3607 				continue;
3608 
3609 			if (FIL_FileExists(va("%s-%s-rings-best.lmp", gpath, skins[i].name)))
3610 				G_AddGhost(va("%s-%s-rings-best.lmp", gpath, skins[i].name));
3611 		}
3612 	}
3613 
3614 	// Last ghost
3615 	if (cv_ghost_last.value)
3616 	{
3617 		for (i = 0; i < numskins; ++i)
3618 		{
3619 			if (cv_ghost_last.value == 1 && players[consoleplayer].skin != i)
3620 				continue;
3621 
3622 			if (FIL_FileExists(va("%s-%s-last.lmp", gpath, skins[i].name)))
3623 				G_AddGhost(va("%s-%s-last.lmp", gpath, skins[i].name));
3624 		}
3625 	}
3626 
3627 	// Guest ghost
3628 	if (cv_ghost_guest.value && FIL_FileExists(va("%s-guest.lmp", gpath)))
3629 		G_AddGhost(va("%s-guest.lmp", gpath));
3630 
3631 	free(gpath);
3632 }
3633 
P_LoadNightsGhosts(void)3634 static void P_LoadNightsGhosts(void)
3635 {
3636 	const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1;
3637 	char *gpath = malloc(glen);
3638 	INT32 i;
3639 
3640 	if (!gpath)
3641 		return;
3642 
3643 	sprintf(gpath,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(gamemap));
3644 
3645 	// Best Score ghost
3646 	if (cv_ghost_bestscore.value)
3647 	{
3648 		for (i = 0; i < numskins; ++i)
3649 		{
3650 			if (cv_ghost_bestscore.value == 1 && players[consoleplayer].skin != i)
3651 				continue;
3652 
3653 			if (FIL_FileExists(va("%s-%s-score-best.lmp", gpath, skins[i].name)))
3654 				G_AddGhost(va("%s-%s-score-best.lmp", gpath, skins[i].name));
3655 		}
3656 	}
3657 
3658 	// Best Time ghost
3659 	if (cv_ghost_besttime.value)
3660 	{
3661 		for (i = 0; i < numskins; ++i)
3662 		{
3663 			if (cv_ghost_besttime.value == 1 && players[consoleplayer].skin != i)
3664 				continue;
3665 
3666 			if (FIL_FileExists(va("%s-%s-time-best.lmp", gpath, skins[i].name)))
3667 				G_AddGhost(va("%s-%s-time-best.lmp", gpath, skins[i].name));
3668 		}
3669 	}
3670 
3671 	// Last ghost
3672 	if (cv_ghost_last.value)
3673 	{
3674 		for (i = 0; i < numskins; ++i)
3675 		{
3676 			if (cv_ghost_last.value == 1 && players[consoleplayer].skin != i)
3677 				continue;
3678 
3679 			if (FIL_FileExists(va("%s-%s-last.lmp", gpath, skins[i].name)))
3680 				G_AddGhost(va("%s-%s-last.lmp", gpath, skins[i].name));
3681 		}
3682 	}
3683 
3684 	// Guest ghost
3685 	if (cv_ghost_guest.value && FIL_FileExists(va("%s-guest.lmp", gpath)))
3686 		G_AddGhost(va("%s-guest.lmp", gpath));
3687 
3688 	free(gpath);
3689 }
3690 
P_InitTagGametype(void)3691 static void P_InitTagGametype(void)
3692 {
3693 	UINT8 i;
3694 	INT32 realnumplayers = 0;
3695 	INT32 playersactive[MAXPLAYERS];
3696 
3697 	//I just realized how problematic this code can be.
3698 	//D_NumPlayers() will not always cover the scope of the netgame.
3699 	//What if one player is node 0 and the other node 31?
3700 	//The solution? Make a temp array of all players that are currently playing and pick from them.
3701 	//Future todo? When a player leaves, shift all nodes down so D_NumPlayers() can be used as intended?
3702 	//Also, you'd never have to loop through all 32 players slots to find anything ever again.
3703 	for (i = 0; i < MAXPLAYERS; i++)
3704 	{
3705 		if (playeringame[i] && !(players[i].spectator || players[i].quittime))
3706 		{
3707 			playersactive[realnumplayers] = i; //stores the player's node in the array.
3708 			realnumplayers++;
3709 		}
3710 	}
3711 
3712 	if (!realnumplayers) //this should also fix the dedicated crash bug. You only pick a player if one exists to be picked.
3713 	{
3714 		CONS_Printf(M_GetText("No player currently available to become IT. Awaiting available players.\n"));
3715 		return;
3716 	}
3717 
3718 	i = P_RandomKey(realnumplayers);
3719 	players[playersactive[i]].pflags |= PF_TAGIT; //choose our initial tagger before map starts.
3720 
3721 	// Taken and modified from G_DoReborn()
3722 	// Remove the player so he can respawn elsewhere.
3723 	// first disassociate the corpse
3724 	if (players[playersactive[i]].mo)
3725 		P_RemoveMobj(players[playersactive[i]].mo);
3726 
3727 	G_SpawnPlayer(playersactive[i]); //respawn the lucky player in his dedicated spawn location.
3728 }
3729 
P_SetupCamera(void)3730 static void P_SetupCamera(void)
3731 {
3732 	if (players[displayplayer].mo && (server || addedtogame))
3733 	{
3734 		camera.x = players[displayplayer].mo->x;
3735 		camera.y = players[displayplayer].mo->y;
3736 		camera.z = players[displayplayer].mo->z;
3737 		camera.angle = players[displayplayer].mo->angle;
3738 		camera.subsector = R_PointInSubsector(camera.x, camera.y); // make sure camera has a subsector set -- Monster Iestyn (12/11/18)
3739 	}
3740 	else
3741 	{
3742 		mapthing_t *thing;
3743 
3744 		if (gametyperules & GTR_DEATHMATCHSTARTS)
3745 			thing = deathmatchstarts[0];
3746 		else
3747 			thing = playerstarts[0];
3748 
3749 		if (thing)
3750 		{
3751 			camera.x = thing->x;
3752 			camera.y = thing->y;
3753 			camera.z = thing->z;
3754 			camera.angle = FixedAngle((fixed_t)thing->angle << FRACBITS);
3755 			camera.subsector = R_PointInSubsector(camera.x, camera.y); // make sure camera has a subsector set -- Monster Iestyn (12/11/18)
3756 		}
3757 	}
3758 }
3759 
P_InitCamera(void)3760 static void P_InitCamera(void)
3761 {
3762 	if (!dedicated)
3763 	{
3764 		P_SetupCamera();
3765 
3766 		// Salt: CV_ClearChangedFlags() messes with your settings :(
3767 		/*if (!cv_cam_height.changed)
3768 			CV_Set(&cv_cam_height, cv_cam_height.defaultvalue);
3769 		if (!cv_cam2_height.changed)
3770 			CV_Set(&cv_cam2_height, cv_cam2_height.defaultvalue);
3771 
3772 		if (!cv_cam_dist.changed)
3773 			CV_Set(&cv_cam_dist, cv_cam_dist.defaultvalue);
3774 		if (!cv_cam2_dist.changed)
3775 			CV_Set(&cv_cam2_dist, cv_cam2_dist.defaultvalue);*/
3776 
3777 			// Though, I don't think anyone would care about cam_rotate being reset back to the only value that makes sense :P
3778 		if (!cv_cam_rotate.changed)
3779 			CV_Set(&cv_cam_rotate, cv_cam_rotate.defaultvalue);
3780 		if (!cv_cam2_rotate.changed)
3781 			CV_Set(&cv_cam2_rotate, cv_cam2_rotate.defaultvalue);
3782 
3783 		if (!cv_analog[0].changed)
3784 			CV_SetValue(&cv_analog[0], 0);
3785 		if (!cv_analog[1].changed)
3786 			CV_SetValue(&cv_analog[1], 0);
3787 
3788 		displayplayer = consoleplayer; // Start with your OWN view, please!
3789 	}
3790 
3791 	if (twodlevel)
3792 	{
3793 		CV_SetValue(&cv_analog[0], false);
3794 		CV_SetValue(&cv_analog[1], false);
3795 	}
3796 	else
3797 	{
3798 		if (cv_useranalog[0].value)
3799 			CV_SetValue(&cv_analog[0], true);
3800 
3801 		if ((splitscreen && cv_useranalog[1].value) || botingame)
3802 			CV_SetValue(&cv_analog[1], true);
3803 	}
3804 }
3805 
P_RunSpecialStageWipe(void)3806 static void P_RunSpecialStageWipe(void)
3807 {
3808 	tic_t starttime = I_GetTime();
3809 	tic_t endtime = starttime + (3*TICRATE)/2;
3810 	tic_t nowtime;
3811 
3812 	S_StartSound(NULL, sfx_s3kaf);
3813 
3814 	// Fade music! Time it to S3KAF: 0.25 seconds is snappy.
3815 	if (RESETMUSIC ||
3816 		strnicmp(S_MusicName(),
3817 		(mapmusflags & MUSIC_RELOADRESET) ? mapheaderinfo[gamemap - 1]->musname : mapmusname, 7))
3818 		S_FadeOutStopMusic(MUSICRATE/4); //FixedMul(FixedDiv(F_GetWipeLength(wipedefs[wipe_speclevel_towhite])*NEWTICRATERATIO, NEWTICRATE), MUSICRATE)
3819 
3820 	F_WipeStartScreen();
3821 	wipestyleflags |= (WSF_FADEOUT|WSF_TOWHITE);
3822 
3823 #ifdef HWRENDER
3824 	// uh..........
3825 	if (rendermode == render_opengl)
3826 		F_WipeColorFill(0);
3827 #endif
3828 
3829 	F_WipeEndScreen();
3830 	F_RunWipe(wipedefs[wipe_speclevel_towhite], false);
3831 
3832 	I_OsPolling();
3833 	I_FinishUpdate(); // page flip or blit buffer
3834 	if (moviemode)
3835 		M_SaveFrame();
3836 
3837 	nowtime = lastwipetic;
3838 
3839 	// Hold on white for extra effect.
3840 	while (nowtime < endtime)
3841 	{
3842 		// wait loop
3843 		while (!((nowtime = I_GetTime()) - lastwipetic))
3844 			I_Sleep();
3845 		lastwipetic = nowtime;
3846 		if (moviemode) // make sure we save frames for the white hold too
3847 			M_SaveFrame();
3848 	}
3849 }
3850 
P_RunLevelWipe(void)3851 static void P_RunLevelWipe(void)
3852 {
3853 	F_WipeStartScreen();
3854 	wipestyleflags |= WSF_FADEOUT;
3855 
3856 #ifdef HWRENDER
3857 	// uh..........
3858 	if (rendermode == render_opengl)
3859 		F_WipeColorFill(31);
3860 #endif
3861 
3862 	F_WipeEndScreen();
3863 	// for titlemap: run a specific wipe if specified
3864 	// needed for exiting time attack
3865 	if (wipetypepre != INT16_MAX)
3866 		F_RunWipe(
3867 		(wipetypepre >= 0 && F_WipeExists(wipetypepre)) ? wipetypepre : wipedefs[wipe_level_toblack],
3868 			false);
3869 	wipetypepre = -1;
3870 }
3871 
P_InitPlayers(void)3872 static void P_InitPlayers(void)
3873 {
3874 	UINT8 i;
3875 
3876 	for (i = 0; i < MAXPLAYERS; i++)
3877 	{
3878 		if (!playeringame[i])
3879 			continue;
3880 
3881 		// Start players with pity shields if possible
3882 		players[i].pity = -1;
3883 
3884 		players[i].mo = NULL;
3885 
3886 		if (!G_PlatformGametype())
3887 			G_DoReborn(i);
3888 		else // gametype is GT_COOP or GT_RACE
3889 		{
3890 			G_SpawnPlayer(i);
3891 			if (players[i].starposttime)
3892 				P_ClearStarPost(players[i].starpostnum);
3893 		}
3894 	}
3895 }
3896 
P_WriteLetter(void)3897 static void P_WriteLetter(void)
3898 {
3899 	char *buf, *b;
3900 
3901 	if (!unlockables[27].unlocked) // pandora's box
3902 		return;
3903 
3904 	if (modeattacking)
3905 		return;
3906 
3907 #ifndef DEVELOP
3908 	if (modifiedgame)
3909 		return;
3910 #endif
3911 
3912 	if (netgame || multiplayer)
3913 		return;
3914 
3915 	if (gamemap != 0x1d35 - 016464)
3916 		return;
3917 
3918 	P_SpawnMobj(0640370000, 0x11000000, 0x3180000, MT_LETTER)->angle = ANGLE_90;
3919 
3920 	if (textprompts[199]->page[1].backcolor == 259)
3921 		return;
3922 
3923 	buf = W_CacheLumpName("WATERMAP", PU_STATIC);
3924 	b = buf;
3925 
3926 	while ((*b != 65) && (b - buf < 256))
3927 	{
3928 		*b = (*b - 65) & 255;
3929 		b++;
3930 	}
3931 	*b = '\0';
3932 
3933 	Z_Free(textprompts[199]->page[1].text);
3934 	textprompts[199]->page[1].text = Z_StrDup(buf);
3935 	textprompts[199]->page[1].lines = 4;
3936 	textprompts[199]->page[1].backcolor = 259;
3937 	Z_Free(buf);
3938 }
3939 
P_InitGametype(void)3940 static void P_InitGametype(void)
3941 {
3942 	UINT8 i;
3943 
3944 	P_InitPlayers();
3945 
3946 	// restore time in netgame (see also g_game.c)
3947 	if ((netgame || multiplayer) && G_GametypeUsesCoopStarposts() && cv_coopstarposts.value == 2)
3948 	{
3949 		// is this a hack? maybe
3950 		tic_t maxstarposttime = 0;
3951 		for (i = 0; i < MAXPLAYERS; i++)
3952 		{
3953 			if (playeringame[i] && players[i].starposttime > maxstarposttime)
3954 				maxstarposttime = players[i].starposttime;
3955 		}
3956 		leveltime = maxstarposttime;
3957 	}
3958 
3959 	P_WriteLetter();
3960 
3961 	if (modeattacking == ATTACKING_RECORD && !demoplayback)
3962 		P_LoadRecordGhosts();
3963 	else if (modeattacking == ATTACKING_NIGHTS && !demoplayback)
3964 		P_LoadNightsGhosts();
3965 
3966 	if (G_TagGametype())
3967 		P_InitTagGametype();
3968 	else if (((gametyperules & (GTR_RACE|GTR_LIVES)) == GTR_RACE) && server)
3969 		CV_StealthSetValue(&cv_numlaps,
3970 		(cv_basenumlaps.value)
3971 			? cv_basenumlaps.value
3972 			: mapheaderinfo[gamemap - 1]->numlaps);
3973 }
3974 
3975 /** Loads a level from a lump or external wad.
3976   *
3977   * \param fromnetsave If true, skip some stuff because we're loading a netgame snapshot.
3978   * \todo Clean up, refactor, split up; get rid of the bloat.
3979   */
P_LoadLevel(boolean fromnetsave,boolean reloadinggamestate)3980 boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
3981 {
3982 	// use gamemap to get map number.
3983 	// 99% of the things already did, so.
3984 	// Map header should always be in place at this point
3985 	INT32 i, ranspecialwipe = 0;
3986 	sector_t *ss;
3987 	levelloading = true;
3988 
3989 	// This is needed. Don't touch.
3990 	maptol = mapheaderinfo[gamemap-1]->typeoflevel;
3991 	gametyperules = gametypedefaultrules[gametype];
3992 
3993 	CON_Drawer(); // let the user know what we are going to do
3994 	I_FinishUpdate(); // page flip or blit buffer
3995 
3996 	// Reset the palette
3997 	if (rendermode != render_none)
3998 		V_SetPaletteLump("PLAYPAL");
3999 
4000 	// Initialize sector node list.
4001 	P_Initsecnode();
4002 
4003 	if (netgame || multiplayer)
4004 		cv_debug = botskin = 0;
4005 
4006 	if (metalplayback)
4007 		G_StopMetalDemo();
4008 
4009 	// Clear CECHO messages
4010 	HU_ClearCEcho();
4011 
4012 	if (mapheaderinfo[gamemap-1]->runsoc[0] != '#')
4013 		P_RunSOC(mapheaderinfo[gamemap-1]->runsoc);
4014 
4015 	if (cv_runscripts.value && mapheaderinfo[gamemap-1]->scriptname[0] != '#')
4016 		P_RunLevelScript(mapheaderinfo[gamemap-1]->scriptname);
4017 
4018 	P_InitLevelSettings();
4019 
4020 	postimgtype = postimgtype2 = postimg_none;
4021 
4022 	if (mapheaderinfo[gamemap-1]->forcecharacter[0] != '\0')
4023 		P_ForceCharacter(mapheaderinfo[gamemap-1]->forcecharacter);
4024 
4025 	if (!dedicated)
4026 	{
4027 		// chasecam on in first-person gametypes and 2D
4028 		boolean chase = (!(gametyperules & GTR_FIRSTPERSON)) || (maptol & TOL_2D);
4029 
4030 		// Salt: CV_ClearChangedFlags() messes with your settings :(
4031 		/*if (!cv_cam_speed.changed)
4032 			CV_Set(&cv_cam_speed, cv_cam_speed.defaultvalue);*/
4033 
4034 		CV_UpdateCamDist();
4035 		CV_UpdateCam2Dist();
4036 
4037 		if (!cv_chasecam.changed)
4038 			CV_SetValue(&cv_chasecam, chase);
4039 
4040 		// same for second player
4041 		if (!cv_chasecam2.changed)
4042 			CV_SetValue(&cv_chasecam2, chase);
4043 	}
4044 
4045 	// Initial height of PointOfView
4046 	// will be set by player think.
4047 	players[consoleplayer].viewz = 1;
4048 
4049 	// Cancel all d_main.c fadeouts (keep fade in though).
4050 	if (reloadinggamestate)
4051 		wipegamestate = gamestate; // Don't fade if reloading the gamestate
4052 	else
4053 		wipegamestate = FORCEWIPEOFF;
4054 	wipestyleflags = 0;
4055 
4056 	// Special stage & record attack retry fade to white
4057 	// This is handled BEFORE sounds are stopped.
4058 	if (G_GetModeAttackRetryFlag())
4059 	{
4060 		if (modeattacking && !demoplayback)
4061 		{
4062 			ranspecialwipe = 2;
4063 			wipestyleflags |= (WSF_FADEOUT|WSF_TOWHITE);
4064 		}
4065 		G_ClearModeAttackRetryFlag();
4066 	}
4067 	else if (rendermode != render_none && G_IsSpecialStage(gamemap))
4068 	{
4069 		P_RunSpecialStageWipe();
4070 		ranspecialwipe = 1;
4071 	}
4072 
4073 	// Make sure all sounds are stopped before Z_FreeTags.
4074 	S_StopSounds();
4075 	S_ClearSfx();
4076 
4077 	// Fade out music here. Deduct 2 tics so the fade volume actually reaches 0.
4078 	// But don't halt the music! S_Start will take care of that. This dodges a MIDI crash bug.
4079 	if (!(reloadinggamestate || titlemapinaction) && (RESETMUSIC ||
4080 		strnicmp(S_MusicName(),
4081 			(mapmusflags & MUSIC_RELOADRESET) ? mapheaderinfo[gamemap-1]->musname : mapmusname, 7)))
4082 	{
4083 		S_FadeMusic(0, FixedMul(
4084 			FixedDiv((F_GetWipeLength(wipedefs[wipe_level_toblack])-2)*NEWTICRATERATIO, NEWTICRATE), MUSICRATE));
4085 	}
4086 
4087 	// Let's fade to black here
4088 	// But only if we didn't do the special stage wipe
4089 	if (rendermode != render_none && !(ranspecialwipe || reloadinggamestate))
4090 		P_RunLevelWipe();
4091 
4092 	if (!(reloadinggamestate || titlemapinaction))
4093 	{
4094 		if (ranspecialwipe == 2)
4095 		{
4096 			pausedelay = -3; // preticker plus one
4097 			S_StartSound(NULL, sfx_s3k73);
4098 		}
4099 
4100 		// Print "SPEEDING OFF TO [ZONE] [ACT 1]..."
4101 		if (rendermode != render_none)
4102 		{
4103 			// Don't include these in the fade!
4104 			char tx[64];
4105 			V_DrawSmallString(1, 191, V_ALLOWLOWERCASE|V_TRANSLUCENT|V_SNAPTOLEFT|V_SNAPTOBOTTOM, M_GetText("Speeding off to..."));
4106 			snprintf(tx, 63, "%s%s%s",
4107 				mapheaderinfo[gamemap-1]->lvlttl,
4108 				(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE) ? "" : " Zone",
4109 				(mapheaderinfo[gamemap-1]->actnum > 0) ? va(" %d",mapheaderinfo[gamemap-1]->actnum) : "");
4110 			V_DrawSmallString(1, 195, V_ALLOWLOWERCASE|V_TRANSLUCENT|V_SNAPTOLEFT|V_SNAPTOBOTTOM, tx);
4111 			I_UpdateNoVsync();
4112 		}
4113 
4114 		// As oddly named as this is, this handles music only.
4115 		// We should be fine starting it here.
4116 		// Don't do this during titlemap, because the menu code handles music by itself.
4117 		S_Start();
4118 	}
4119 
4120 	levelfadecol = (ranspecialwipe) ? 0 : 31;
4121 
4122 	// Close text prompt before freeing the old level
4123 	F_EndTextPrompt(false, true);
4124 
4125 	LUA_InvalidateLevel();
4126 
4127 	for (ss = sectors; sectors+numsectors != ss; ss++)
4128 	{
4129 		Z_Free(ss->attached);
4130 		Z_Free(ss->attachedsolid);
4131 	}
4132 
4133 	// Clear pointers that would be left dangling by the purge
4134 	R_FlushTranslationColormapCache();
4135 
4136 #ifdef HWRENDER
4137 	// Free GPU textures before freeing patches.
4138 	if (rendermode == render_opengl && (vid.glstate == VID_GL_LIBRARY_LOADED))
4139 		HWR_ClearAllTextures();
4140 #endif
4141 
4142 	Patch_FreeTag(PU_PATCH_LOWPRIORITY);
4143 	Patch_FreeTag(PU_PATCH_ROTATED);
4144 	Z_FreeTags(PU_LEVEL, PU_PURGELEVEL - 1);
4145 
4146 	P_InitThinkers();
4147 	P_InitCachedActions();
4148 
4149 	if (!fromnetsave && savedata.lives > 0)
4150 	{
4151 		numgameovers = savedata.numgameovers;
4152 		players[consoleplayer].continues = savedata.continues;
4153 		players[consoleplayer].lives = savedata.lives;
4154 		players[consoleplayer].score = savedata.score;
4155 		if ((botingame = ((botskin = savedata.botskin) != 0)))
4156 			botcolor = skins[botskin-1].prefcolor;
4157 		emeralds = savedata.emeralds;
4158 		savedata.lives = 0;
4159 	}
4160 
4161 	// internal game map
4162 	maplumpname = G_BuildMapName(gamemap);
4163 	lastloadedmaplumpnum = W_CheckNumForName(maplumpname);
4164 	if (lastloadedmaplumpnum == LUMPERROR)
4165 		I_Error("Map %s not found.\n", maplumpname);
4166 
4167 	R_ReInitColormaps(mapheaderinfo[gamemap-1]->palette);
4168 	CON_SetupBackColormap();
4169 
4170 	// SRB2 determines the sky texture to be used depending on the map header.
4171 	P_SetupLevelSky(mapheaderinfo[gamemap-1]->skynum, true);
4172 
4173 	P_ResetSpawnpoints();
4174 
4175 	P_ResetWaypoints();
4176 
4177 	P_MapStart();
4178 
4179 	if (!P_LoadMapFromFile())
4180 		return false;
4181 
4182 	// init anything that P_SpawnSlopes/P_LoadThings needs to know
4183 	P_InitSpecials();
4184 
4185 	P_SpawnSlopes(fromnetsave);
4186 
4187 	P_SpawnMapThings(!fromnetsave);
4188 	skyboxmo[0] = skyboxviewpnts[0];
4189 	skyboxmo[1] = skyboxcenterpnts[0];
4190 
4191 	for (numcoopstarts = 0; numcoopstarts < MAXPLAYERS; numcoopstarts++)
4192 		if (!playerstarts[numcoopstarts])
4193 			break;
4194 
4195 	// set up world state
4196 	P_SpawnSpecials(fromnetsave);
4197 
4198 	if (!fromnetsave) //  ugly hack for P_NetUnArchiveMisc (and P_LoadNetGame)
4199 		P_SpawnPrecipitation();
4200 
4201 #ifdef HWRENDER // not win32 only 19990829 by Kin
4202 	gl_maploaded = false;
4203 
4204 	// Lactozilla: Free extrasubsectors regardless of renderer.
4205 	HWR_FreeExtraSubsectors();
4206 
4207 	// Create plane polygons.
4208 	if (rendermode == render_opengl)
4209 		HWR_LoadLevel();
4210 #endif
4211 
4212 	// oh god I hope this helps
4213 	// (addendum: apparently it does!
4214 	//  none of this needs to be done because it's not the beginning of the map when
4215 	//  a netgame save is being loaded, and could actively be harmful by messing with
4216 	//  the client's view of the data.)
4217 	if (!fromnetsave)
4218 		P_InitGametype();
4219 
4220 	if (!reloadinggamestate)
4221 	{
4222 		P_InitCamera();
4223 		localaiming = 0;
4224 		localaiming2 = 0;
4225 	}
4226 
4227 	// clear special respawning que
4228 	iquehead = iquetail = 0;
4229 
4230 	P_MapEnd();
4231 
4232 	// Remove the loading shit from the screen
4233 	if (rendermode != render_none && !(titlemapinaction || reloadinggamestate))
4234 		F_WipeColorFill(levelfadecol);
4235 
4236 	if (precache || dedicated)
4237 		R_PrecacheLevel();
4238 
4239 	nextmapoverride = 0;
4240 	skipstats = 0;
4241 
4242 	if (!(netgame || multiplayer || demoplayback) && (!modifiedgame || savemoddata))
4243 		mapvisited[gamemap-1] |= MV_VISITED;
4244 	else if (netgame || multiplayer)
4245 		mapvisited[gamemap-1] |= MV_MP; // you want to record that you've been there this session, but not permanently
4246 
4247 	levelloading = false;
4248 
4249 	P_RunCachedActions();
4250 
4251 	// Took me 3 hours to figure out why my progression kept on getting overwritten with the titlemap...
4252 	if (!titlemapinaction)
4253 	{
4254 		if (!lastmaploaded) // Start a new game?
4255 		{
4256 			// I'd love to do this in the menu code instead of here, but everything's a mess and I can't guarantee saving proper player struct info before the first act's started. You could probably refactor it, but it'd be a lot of effort. Easier to just work off known good code. ~toast 22/06/2020
4257 			if (!(ultimatemode || netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking || marathonmode)
4258 			&& (!modifiedgame || savemoddata) && cursaveslot > 0)
4259 				G_SaveGame((UINT32)cursaveslot, gamemap);
4260 			// If you're looking for saving sp file progression (distinct from G_SaveGameOver), check G_DoCompleted.
4261 		}
4262 		lastmaploaded = gamemap; // HAS to be set after saving!!
4263 	}
4264 
4265 	if (!fromnetsave) // uglier hack
4266 	{ // to make a newly loaded level start on the second frame.
4267 		INT32 buf = gametic % BACKUPTICS;
4268 		for (i = 0; i < MAXPLAYERS; i++)
4269 		{
4270 			if (playeringame[i])
4271 				G_CopyTiccmd(&players[i].cmd, &netcmds[buf][i], 1);
4272 		}
4273 		P_PreTicker(2);
4274 		LUAh_MapLoad();
4275 	}
4276 
4277 	// No render mode or reloading gamestate, stop here.
4278 	if (rendermode == render_none || reloadinggamestate)
4279 		return true;
4280 
4281 	// Title card!
4282 	G_StartTitleCard();
4283 
4284 	// Can the title card actually run, though?
4285 	if (!WipeStageTitle)
4286 		return true;
4287 	if (ranspecialwipe == 2)
4288 		return true;
4289 
4290 	// If so...
4291 	G_PreLevelTitleCard();
4292 
4293 	return true;
4294 }
4295 
4296 //
4297 // P_RunSOC
4298 //
4299 // Runs a SOC file or a lump, depending on if ".SOC" exists in the filename
4300 //
P_RunSOC(const char * socfilename)4301 boolean P_RunSOC(const char *socfilename)
4302 {
4303 	lumpnum_t lump;
4304 
4305 	if (strstr(socfilename, ".soc") != NULL)
4306 		return P_AddWadFile(socfilename);
4307 
4308 	lump = W_CheckNumForName(socfilename);
4309 	if (lump == LUMPERROR)
4310 		return false;
4311 
4312 	CONS_Printf(M_GetText("Loading SOC lump: %s\n"), socfilename);
4313 	DEH_LoadDehackedLump(lump);
4314 
4315 	return true;
4316 }
4317 
4318 // Auxiliary function for PK3 loading - looks for sound replacements.
4319 // NOTE: it does not really add any new sound entry or anything.
P_LoadSoundsRange(UINT16 wadnum,UINT16 first,UINT16 num)4320 void P_LoadSoundsRange(UINT16 wadnum, UINT16 first, UINT16 num)
4321 {
4322 	size_t j;
4323 	lumpinfo_t *lumpinfo = wadfiles[wadnum]->lumpinfo + first;
4324 	for (; num > 0; num--, lumpinfo++)
4325 	{
4326 		// Let's check whether it's replacing an existing sound or it's a brand new one.
4327 		for (j = 1; j < NUMSFX; j++)
4328 		{
4329 			if (S_sfx[j].name && !strnicmp(S_sfx[j].name, lumpinfo->name + 2, 6))
4330 			{
4331 				// the sound will be reloaded when needed,
4332 				// since sfx->data will be NULL
4333 				CONS_Debug(DBG_SETUP, "Sound %.8s replaced\n", lumpinfo->name);
4334 
4335 				I_FreeSfx(&S_sfx[j]);
4336 				break; // there shouldn't be two sounds with the same name, so stop looking
4337 			}
4338 		}
4339 	}
4340 }
4341 
4342 // Auxiliary function for PK3 loading - looks for music and music replacements.
4343 // NOTE: does nothing but print debug messages. The code is handled somewhere else.
P_LoadMusicsRange(UINT16 wadnum,UINT16 first,UINT16 num)4344 void P_LoadMusicsRange(UINT16 wadnum, UINT16 first, UINT16 num)
4345 {
4346 	lumpinfo_t *lumpinfo = wadfiles[wadnum]->lumpinfo + first;
4347 	char *name;
4348 	for (; num > 0; num--, lumpinfo++)
4349 	{
4350 		name = lumpinfo->name;
4351 		if (name[0] == 'O' && name[1] == '_')
4352 		{
4353 			CONS_Debug(DBG_SETUP, "Music %.8s replaced\n", name);
4354 		}
4355 		else if (name[0] == 'D' && name[1] == '_')
4356 		{
4357 			CONS_Debug(DBG_SETUP, "Music %.8s replaced\n", name);
4358 		}
4359 	}
4360 	return;
4361 }
4362 
4363 // Auxiliary function - input a folder name and gives us the resource markers positions.
FindFolder(const char * folName,UINT16 * start,UINT16 * end,lumpinfo_t * lumpinfo,UINT16 * pnumlumps,size_t * pi)4364 static lumpinfo_t* FindFolder(const char *folName, UINT16 *start, UINT16 *end, lumpinfo_t *lumpinfo, UINT16 *pnumlumps, size_t *pi)
4365 {
4366 	UINT16 numlumps = *pnumlumps;
4367 	size_t i = *pi;
4368 	if (!stricmp(lumpinfo->fullname, folName))
4369 	{
4370 		lumpinfo++;
4371 		*start = ++i;
4372 		for (; i < numlumps; i++, lumpinfo++)
4373 			if (strnicmp(lumpinfo->fullname, folName, strlen(folName)))
4374 				break;
4375 		lumpinfo--;
4376 		*end = i-- - *start;
4377 		*pi = i;
4378 		*pnumlumps = numlumps;
4379 		return lumpinfo;
4380 	}
4381 	return lumpinfo;
4382 }
4383 
4384 //
4385 // Add a wadfile to the active wad files,
4386 // replace sounds, musics, patches, textures, sprites and maps
4387 //
P_AddWadFile(const char * wadfilename)4388 boolean P_AddWadFile(const char *wadfilename)
4389 {
4390 	size_t i, j, sreplaces = 0, mreplaces = 0, digmreplaces = 0;
4391 	UINT16 numlumps, wadnum;
4392 	char *name;
4393 	lumpinfo_t *lumpinfo;
4394 
4395 	//boolean texturechange = false; ///\todo Useless; broken when back-frontporting PK3 changes?
4396 	boolean mapsadded = false;
4397 	boolean replacedcurrentmap = false;
4398 
4399 	// Vars to help us with the position start and amount of each resource type.
4400 	// Useful for PK3s since they use folders.
4401 	// WADs use markers for some resources, but others such as sounds are checked lump-by-lump anyway.
4402 //	UINT16 luaPos, luaNum = 0;
4403 //	UINT16 socPos, socNum = 0;
4404 	UINT16 sfxPos = 0, sfxNum = 0;
4405 	UINT16 musPos = 0, musNum = 0;
4406 //	UINT16 sprPos, sprNum = 0;
4407 	UINT16 texPos = 0, texNum = 0;
4408 //	UINT16 patPos, patNum = 0;
4409 //	UINT16 flaPos, flaNum = 0;
4410 //	UINT16 mapPos, mapNum = 0;
4411 
4412 	// Init file.
4413 	if ((numlumps = W_InitFile(wadfilename, false, false)) == INT16_MAX)
4414 	{
4415 		refreshdirmenu |= REFRESHDIR_NOTLOADED;
4416 		return false;
4417 	}
4418 	else
4419 		wadnum = (UINT16)(numwadfiles-1);
4420 
4421 	switch(wadfiles[wadnum]->type)
4422 	{
4423 	case RET_PK3:
4424 		// Look for the lumps that act as resource delimitation markers.
4425 		lumpinfo = wadfiles[wadnum]->lumpinfo;
4426 		for (i = 0; i < numlumps; i++, lumpinfo++)
4427 		{
4428 //			lumpinfo = FindFolder("Lua/",      &luaPos, &luaNum, lumpinfo, &numlumps, &i);
4429 //			lumpinfo = FindFolder("SOC/",      &socPos, &socNum, lumpinfo, &numlumps, &i);
4430 			lumpinfo = FindFolder("Sounds/",   &sfxPos, &sfxNum, lumpinfo, &numlumps, &i);
4431 			lumpinfo = FindFolder("Music/",    &musPos, &musNum, lumpinfo, &numlumps, &i);
4432 //			lumpinfo = FindFolder("Sprites/",  &sprPos, &sprNum, lumpinfo, &numlumps, &i);
4433 			lumpinfo = FindFolder("Textures/", &texPos, &texNum, lumpinfo, &numlumps, &i);
4434 //			lumpinfo = FindFolder("Patches/",  &patPos, &patNum, lumpinfo, &numlumps, &i);
4435 //			lumpinfo = FindFolder("Flats/",    &flaPos, &flaNum, lumpinfo, &numlumps, &i);
4436 //			lumpinfo = FindFolder("Maps/",     &mapPos, &mapNum, lumpinfo, &numlumps, &i);
4437 		}
4438 
4439 		// Update the detected resources.
4440 		// Note: ALWAYS load Lua scripts first, SOCs right after, and the remaining resources afterwards.
4441 //		if (luaNum) // Lua scripts.
4442 //			P_LoadLuaScrRange(wadnum, luaPos, luaNum);
4443 //		if (socNum) // SOCs.
4444 //			P_LoadDehackRange(wadnum, socPos, socNum);
4445 		if (sfxNum) // Sounds. TODO: Function currently only updates already existing sounds, the rest is handled somewhere else.
4446 			P_LoadSoundsRange(wadnum, sfxPos, sfxNum);
4447 		if (musNum) // Music. TODO: Useless function right now.
4448 			P_LoadMusicsRange(wadnum, musPos, musNum);
4449 //		if (sprNum) // Sprites.
4450 //			R_LoadSpritsRange(wadnum, sprPos, sprNum);
4451 //		if (texNum) // Textures. TODO: R_LoadTextures() does the folder positioning once again. New function maybe?
4452 //			R_LoadTextures();
4453 //		if (mapNum) // Maps. TODO: Actually implement the map WAD loading code, lulz.
4454 //			P_LoadWadMapRange(wadnum, mapPos, mapNum);
4455 		break;
4456 	default:
4457 		lumpinfo = wadfiles[wadnum]->lumpinfo;
4458 		for (i = 0; i < numlumps; i++, lumpinfo++)
4459 		{
4460 			name = lumpinfo->name;
4461 			if (name[0] == 'D')
4462 			{
4463 				if (name[1] == 'S')
4464 				{
4465 					for (j = 1; j < NUMSFX; j++)
4466 					{
4467 						if (S_sfx[j].name && !strnicmp(S_sfx[j].name, name + 2, 6))
4468 						{
4469 							// the sound will be reloaded when needed,
4470 							// since sfx->data will be NULL
4471 							CONS_Debug(DBG_SETUP, "Sound %.8s replaced\n", name);
4472 
4473 							I_FreeSfx(&S_sfx[j]);
4474 
4475 							sreplaces++;
4476 							break; // there shouldn't be two sounds with the same name, so stop looking
4477 						}
4478 					}
4479 				}
4480 				else if (name[1] == '_')
4481 				{
4482 					CONS_Debug(DBG_SETUP, "Music %.8s replaced\n", name);
4483 					mreplaces++;
4484 				}
4485 			}
4486 			else if (name[0] == 'O' && name[1] == '_')
4487 			{
4488 				CONS_Debug(DBG_SETUP, "Music %.8s replaced\n", name);
4489 				digmreplaces++;
4490 			}
4491 		}
4492 		break;
4493 	}
4494 	if (!devparm && sreplaces)
4495 		CONS_Printf(M_GetText("%s sounds replaced\n"), sizeu1(sreplaces));
4496 	if (!devparm && mreplaces)
4497 		CONS_Printf(M_GetText("%s midi musics replaced\n"), sizeu1(mreplaces));
4498 	if (!devparm && digmreplaces)
4499 		CONS_Printf(M_GetText("%s digital musics replaced\n"), sizeu1(digmreplaces));
4500 
4501 #ifdef HWRENDER
4502 	// Free GPU textures before freeing patches.
4503 	if (rendermode == render_opengl && (vid.glstate == VID_GL_LIBRARY_LOADED))
4504 		HWR_ClearAllTextures();
4505 #endif
4506 
4507 	//
4508 	// search for sprite replacements
4509 	//
4510 	Patch_FreeTag(PU_SPRITE);
4511 	Patch_FreeTag(PU_PATCH_ROTATED);
4512 	R_AddSpriteDefs(wadnum);
4513 
4514 	// Reload it all anyway, just in case they
4515 	// added some textures but didn't insert a
4516 	// TEXTURES/etc. list.
4517 	R_LoadTextures(); // numtexture changes
4518 
4519 	// Reload ANIMDEFS
4520 	P_InitPicAnims();
4521 
4522 	// Flush and reload HUD graphics
4523 	ST_UnloadGraphics();
4524 	HU_LoadGraphics();
4525 	ST_LoadGraphics();
4526 
4527 	//
4528 	// look for skins
4529 	//
4530 	R_AddSkins(wadnum); // faB: wadfile index in wadfiles[]
4531 	R_PatchSkins(wadnum); // toast: PATCH PATCH
4532 	ST_ReloadSkinFaceGraphics();
4533 
4534 	//
4535 	// edit music defs
4536 	//
4537 	S_LoadMusicDefs(wadnum);
4538 
4539 	//
4540 	// search for maps
4541 	//
4542 	lumpinfo = wadfiles[wadnum]->lumpinfo;
4543 	for (i = 0; i < numlumps; i++, lumpinfo++)
4544 	{
4545 		name = lumpinfo->name;
4546 		if (name[0] == 'M' && name[1] == 'A' && name[2] == 'P') // Ignore the headers
4547 		{
4548 			INT16 num;
4549 			if (name[5]!='\0')
4550 				continue;
4551 			num = (INT16)M_MapNumber(name[3], name[4]);
4552 
4553 			//If you replaced the map you're on, end the level when done.
4554 			if (num == gamemap)
4555 				replacedcurrentmap = true;
4556 
4557 			CONS_Printf("%s\n", name);
4558 			mapsadded = true;
4559 		}
4560 	}
4561 	if (!mapsadded)
4562 		CONS_Printf(M_GetText("No maps added\n"));
4563 
4564 	R_LoadSpriteInfoLumps(wadnum, numlumps);
4565 
4566 #ifdef HWRENDER
4567 	HWR_ReloadModels();
4568 #endif
4569 
4570 	// reload status bar (warning should have valid player!)
4571 	if (gamestate == GS_LEVEL)
4572 		ST_Start();
4573 
4574 	// Prevent savefile cheating
4575 	if (cursaveslot > 0)
4576 		cursaveslot = 0;
4577 
4578 	if (replacedcurrentmap && gamestate == GS_LEVEL && (netgame || multiplayer))
4579 	{
4580 		CONS_Printf(M_GetText("Current map %d replaced by added file, ending the level to ensure consistency.\n"), gamemap);
4581 		if (server)
4582 			SendNetXCmd(XD_EXITLEVEL, NULL, 0);
4583 	}
4584 
4585 	return true;
4586 }
4587