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-2021 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  r_textures.c
12 /// \brief Texture generation.
13 
14 #include "doomdef.h"
15 #include "g_game.h"
16 #include "i_video.h"
17 #include "r_local.h"
18 #include "r_sky.h"
19 #include "p_local.h"
20 #include "m_misc.h"
21 #include "r_data.h"
22 #include "r_textures.h"
23 #include "r_patch.h"
24 #include "r_picformats.h"
25 #include "w_wad.h"
26 #include "z_zone.h"
27 #include "p_setup.h" // levelflats
28 #include "byteptr.h"
29 #include "dehacked.h"
30 
31 #ifdef HWRENDER
32 #include "hardware/hw_glob.h" // HWR_LoadMapTextures
33 #endif
34 
35 #include <errno.h>
36 
37 //
38 // TEXTURE_T CACHING
39 // When a texture is first needed, it counts the number of composite columns
40 //  required in the texture and allocates space for a column directory and
41 //  any new columns.
42 // The directory will simply point inside other patches if there is only one
43 //  patch in a given column, but any columns with multiple patches will have
44 //  new column_ts generated.
45 //
46 
47 INT32 numtextures = 0; // total number of textures found,
48 // size of following tables
49 
50 texture_t **textures = NULL;
51 UINT32 **texturecolumnofs; // column offset lookup table for each texture
52 UINT8 **texturecache; // graphics data for each generated full-size texture
53 
54 INT32 *texturewidth;
55 fixed_t *textureheight; // needed for texture pegging
56 
57 INT32 *texturetranslation;
58 
59 // Painfully simple texture id cacheing to make maps load faster. :3
60 static struct {
61 	char name[9];
62 	INT32 id;
63 } *tidcache = NULL;
64 static INT32 tidcachelen = 0;
65 
66 //
67 // MAPTEXTURE_T CACHING
68 // When a texture is first needed, it counts the number of composite columns
69 //  required in the texture and allocates space for a column directory and
70 //  any new columns.
71 // The directory will simply point inside other patches if there is only one
72 //  patch in a given column, but any columns with multiple patches will have
73 //  new column_ts generated.
74 //
75 
76 //
77 // R_DrawColumnInCache
78 // Clip and draw a column from a patch into a cached post.
79 //
R_DrawColumnInCache(column_t * patch,UINT8 * cache,texpatch_t * originPatch,INT32 cacheheight,INT32 patchheight)80 static inline void R_DrawColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight)
81 {
82 	INT32 count, position;
83 	UINT8 *source;
84 	INT32 topdelta, prevdelta = -1;
85 	INT32 originy = originPatch->originy;
86 
87 	(void)patchheight; // This parameter is unused
88 
89 	while (patch->topdelta != 0xff)
90 	{
91 		topdelta = patch->topdelta;
92 		if (topdelta <= prevdelta)
93 			topdelta += prevdelta;
94 		prevdelta = topdelta;
95 		source = (UINT8 *)patch + 3;
96 		count = patch->length;
97 		position = originy + topdelta;
98 
99 		if (position < 0)
100 		{
101 			count += position;
102 			source -= position; // start further down the column
103 			position = 0;
104 		}
105 
106 		if (position + count > cacheheight)
107 			count = cacheheight - position;
108 
109 		if (count > 0)
110 			M_Memcpy(cache + position, source, count);
111 
112 		patch = (column_t *)((UINT8 *)patch + patch->length + 4);
113 	}
114 }
115 
116 //
117 // R_DrawFlippedColumnInCache
118 // Similar to R_DrawColumnInCache; it draws the column inverted, however.
119 //
R_DrawFlippedColumnInCache(column_t * patch,UINT8 * cache,texpatch_t * originPatch,INT32 cacheheight,INT32 patchheight)120 static inline void R_DrawFlippedColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight)
121 {
122 	INT32 count, position;
123 	UINT8 *source, *dest;
124 	INT32 topdelta, prevdelta = -1;
125 	INT32 originy = originPatch->originy;
126 
127 	while (patch->topdelta != 0xff)
128 	{
129 		topdelta = patch->topdelta;
130 		if (topdelta <= prevdelta)
131 			topdelta += prevdelta;
132 		prevdelta = topdelta;
133 		topdelta = patchheight-patch->length-topdelta;
134 		source = (UINT8 *)patch + 2 + patch->length; // patch + 3 + (patch->length-1)
135 		count = patch->length;
136 		position = originy + topdelta;
137 
138 		if (position < 0)
139 		{
140 			count += position;
141 			source += position; // start further UP the column
142 			position = 0;
143 		}
144 
145 		if (position + count > cacheheight)
146 			count = cacheheight - position;
147 
148 		dest = cache + position;
149 		if (count > 0)
150 		{
151 			for (; dest < cache + position + count; --source)
152 				*dest++ = *source;
153 		}
154 
155 		patch = (column_t *)((UINT8 *)patch + patch->length + 4);
156 	}
157 }
158 
159 //
160 // R_DrawBlendColumnInCache
161 // Draws a translucent column into the cache, applying a half-cooked equation to get a proper translucency value (Needs code in R_GenerateTexture()).
162 //
R_DrawBlendColumnInCache(column_t * patch,UINT8 * cache,texpatch_t * originPatch,INT32 cacheheight,INT32 patchheight)163 static inline void R_DrawBlendColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight)
164 {
165 	INT32 count, position;
166 	UINT8 *source, *dest;
167 	INT32 topdelta, prevdelta = -1;
168 	INT32 originy = originPatch->originy;
169 
170 	(void)patchheight; // This parameter is unused
171 
172 	while (patch->topdelta != 0xff)
173 	{
174 		topdelta = patch->topdelta;
175 		if (topdelta <= prevdelta)
176 			topdelta += prevdelta;
177 		prevdelta = topdelta;
178 		source = (UINT8 *)patch + 3;
179 		count = patch->length;
180 		position = originy + topdelta;
181 
182 		if (position < 0)
183 		{
184 			count += position;
185 			source -= position; // start further down the column
186 			position = 0;
187 		}
188 
189 		if (position + count > cacheheight)
190 			count = cacheheight - position;
191 
192 		dest = cache + position;
193 		if (count > 0)
194 		{
195 			for (; dest < cache + position + count; source++, dest++)
196 				if (*source != 0xFF)
197 					*dest = ASTBlendPaletteIndexes(*dest, *source, originPatch->style, originPatch->alpha);
198 		}
199 
200 		patch = (column_t *)((UINT8 *)patch + patch->length + 4);
201 	}
202 }
203 
204 //
205 // R_DrawBlendFlippedColumnInCache
206 // Similar to the one above except that the column is inverted.
207 //
R_DrawBlendFlippedColumnInCache(column_t * patch,UINT8 * cache,texpatch_t * originPatch,INT32 cacheheight,INT32 patchheight)208 static inline void R_DrawBlendFlippedColumnInCache(column_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight)
209 {
210 	INT32 count, position;
211 	UINT8 *source, *dest;
212 	INT32 topdelta, prevdelta = -1;
213 	INT32 originy = originPatch->originy;
214 
215 	while (patch->topdelta != 0xff)
216 	{
217 		topdelta = patch->topdelta;
218 		if (topdelta <= prevdelta)
219 			topdelta += prevdelta;
220 		prevdelta = topdelta;
221 		topdelta = patchheight-patch->length-topdelta;
222 		source = (UINT8 *)patch + 2 + patch->length; // patch + 3 + (patch->length-1)
223 		count = patch->length;
224 		position = originy + topdelta;
225 
226 		if (position < 0)
227 		{
228 			count += position;
229 			source += position; // start further UP the column
230 			position = 0;
231 		}
232 
233 		if (position + count > cacheheight)
234 			count = cacheheight - position;
235 
236 		dest = cache + position;
237 		if (count > 0)
238 		{
239 			for (; dest < cache + position + count; --source, dest++)
240 				if (*source != 0xFF)
241 					*dest = ASTBlendPaletteIndexes(*dest, *source, originPatch->style, originPatch->alpha);
242 		}
243 
244 		patch = (column_t *)((UINT8 *)patch + patch->length + 4);
245 	}
246 }
247 
248 //
249 // R_GenerateTexture
250 //
251 // Allocate space for full size texture, either single patch or 'composite'
252 // Build the full textures from patches.
253 // The texture caching system is a little more hungry of memory, but has
254 // been simplified for the sake of highcolor (lol), dynamic ligthing, & speed.
255 //
256 // This is not optimised, but it's supposed to be executed only once
257 // per level, when enough memory is available.
258 //
R_GenerateTexture(size_t texnum)259 UINT8 *R_GenerateTexture(size_t texnum)
260 {
261 	UINT8 *block;
262 	UINT8 *blocktex;
263 	texture_t *texture;
264 	texpatch_t *patch;
265 	softwarepatch_t *realpatch;
266 	UINT8 *pdata;
267 	int x, x1, x2, i, width, height;
268 	size_t blocksize;
269 	column_t *patchcol;
270 	UINT8 *colofs;
271 
272 	UINT16 wadnum;
273 	lumpnum_t lumpnum;
274 	size_t lumplength;
275 
276 	I_Assert(texnum <= (size_t)numtextures);
277 	texture = textures[texnum];
278 	I_Assert(texture != NULL);
279 
280 	// allocate texture column offset lookup
281 
282 	// single-patch textures can have holes in them and may be used on
283 	// 2sided lines so they need to be kept in 'packed' format
284 	// BUT this is wrong for skies and walls with over 255 pixels,
285 	// so check if there's holes and if not strip the posts.
286 	if (texture->patchcount == 1)
287 	{
288 		boolean holey = false;
289 		patch = texture->patches;
290 
291 		wadnum = patch->wad;
292 		lumpnum = patch->lump;
293 		lumplength = W_LumpLengthPwad(wadnum, lumpnum);
294 		pdata = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE);
295 		realpatch = (softwarepatch_t *)pdata;
296 
297 #ifndef NO_PNG_LUMPS
298 		if (Picture_IsLumpPNG((UINT8 *)realpatch, lumplength))
299 			goto multipatch;
300 #endif
301 #ifdef WALLFLATS
302 		if (texture->type == TEXTURETYPE_FLAT)
303 			goto multipatch;
304 #endif
305 
306 		// Check the patch for holes.
307 		if (texture->width > SHORT(realpatch->width) || texture->height > SHORT(realpatch->height))
308 			holey = true;
309 		colofs = (UINT8 *)realpatch->columnofs;
310 		for (x = 0; x < texture->width && !holey; x++)
311 		{
312 			column_t *col = (column_t *)((UINT8 *)realpatch + LONG(*(UINT32 *)&colofs[x<<2]));
313 			INT32 topdelta, prevdelta = -1, y = 0;
314 			while (col->topdelta != 0xff)
315 			{
316 				topdelta = col->topdelta;
317 				if (topdelta <= prevdelta)
318 					topdelta += prevdelta;
319 				prevdelta = topdelta;
320 				if (topdelta > y)
321 					break;
322 				y = topdelta + col->length + 1;
323 				col = (column_t *)((UINT8 *)col + col->length + 4);
324 			}
325 			if (y < texture->height)
326 				holey = true; // this texture is HOLEy! D:
327 		}
328 
329 		// If the patch uses transparency, we have to save it this way.
330 		if (holey)
331 		{
332 			texture->holes = true;
333 			texture->flip = patch->flip;
334 			blocksize = lumplength;
335 			block = Z_Calloc(blocksize, PU_STATIC, // will change tag at end of this function
336 				&texturecache[texnum]);
337 			M_Memcpy(block, realpatch, blocksize);
338 			texturememory += blocksize;
339 
340 			// use the patch's column lookup
341 			colofs = (block + 8);
342 			texturecolumnofs[texnum] = (UINT32 *)colofs;
343 			blocktex = block;
344 			if (patch->flip & 1) // flip the patch horizontally
345 			{
346 				UINT8 *realcolofs = (UINT8 *)realpatch->columnofs;
347 				for (x = 0; x < texture->width; x++)
348 					*(UINT32 *)&colofs[x<<2] = realcolofs[( texture->width-1-x )<<2]; // swap with the offset of the other side of the texture
349 			}
350 			// we can't as easily flip the patch vertically sadly though,
351 			//  we have wait until the texture itself is drawn to do that
352 			for (x = 0; x < texture->width; x++)
353 				*(UINT32 *)&colofs[x<<2] = LONG(LONG(*(UINT32 *)&colofs[x<<2]) + 3);
354 			goto done;
355 		}
356 
357 		// Otherwise, do multipatch format.
358 	}
359 
360 	// multi-patch textures (or 'composite')
361 	multipatch:
362 	texture->holes = false;
363 	texture->flip = 0;
364 	blocksize = (texture->width * 4) + (texture->width * texture->height);
365 	texturememory += blocksize;
366 	block = Z_Malloc(blocksize+1, PU_STATIC, &texturecache[texnum]);
367 
368 	memset(block, TRANSPARENTPIXEL, blocksize+1); // Transparency hack
369 
370 	// columns lookup table
371 	colofs = block;
372 	texturecolumnofs[texnum] = (UINT32 *)colofs;
373 
374 	// texture data after the lookup table
375 	blocktex = block + (texture->width*4);
376 
377 	// Composite the columns together.
378 	for (i = 0, patch = texture->patches; i < texture->patchcount; i++, patch++)
379 	{
380 		boolean dealloc = true;
381 		static void (*ColumnDrawerPointer)(column_t *, UINT8 *, texpatch_t *, INT32, INT32); // Column drawing function pointer.
382 		if (patch->style != AST_COPY)
383 			ColumnDrawerPointer = (patch->flip & 2) ? R_DrawBlendFlippedColumnInCache : R_DrawBlendColumnInCache;
384 		else
385 			ColumnDrawerPointer = (patch->flip & 2) ? R_DrawFlippedColumnInCache : R_DrawColumnInCache;
386 
387 		wadnum = patch->wad;
388 		lumpnum = patch->lump;
389 		pdata = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE);
390 		lumplength = W_LumpLengthPwad(wadnum, lumpnum);
391 		realpatch = (softwarepatch_t *)pdata;
392 		dealloc = true;
393 
394 #ifndef NO_PNG_LUMPS
395 		if (Picture_IsLumpPNG((UINT8 *)realpatch, lumplength))
396 			realpatch = (softwarepatch_t *)Picture_PNGConvert((UINT8 *)realpatch, PICFMT_DOOMPATCH, NULL, NULL, NULL, NULL, lumplength, NULL, 0);
397 		else
398 #endif
399 #ifdef WALLFLATS
400 		if (texture->type == TEXTURETYPE_FLAT)
401 			realpatch = (softwarepatch_t *)Picture_Convert(PICFMT_FLAT, pdata, PICFMT_DOOMPATCH, 0, NULL, texture->width, texture->height, 0, 0, 0);
402 		else
403 #endif
404 		{
405 			(void)lumplength;
406 			dealloc = false;
407 		}
408 
409 		x1 = patch->originx;
410 		width = SHORT(realpatch->width);
411 		height = SHORT(realpatch->height);
412 		x2 = x1 + width;
413 
414 		if (x1 > texture->width || x2 < 0)
415 		{
416 			if (dealloc)
417 				Z_Free(realpatch);
418 			continue; // patch not located within texture's x bounds, ignore
419 		}
420 
421 		if (patch->originy > texture->height || (patch->originy + height) < 0)
422 		{
423 			if (dealloc)
424 				Z_Free(realpatch);
425 			continue; // patch not located within texture's y bounds, ignore
426 		}
427 
428 		// patch is actually inside the texture!
429 		// now check if texture is partly off-screen and adjust accordingly
430 
431 		// left edge
432 		if (x1 < 0)
433 			x = 0;
434 		else
435 			x = x1;
436 
437 		// right edge
438 		if (x2 > texture->width)
439 			x2 = texture->width;
440 
441 		for (; x < x2; x++)
442 		{
443 			if (patch->flip & 1)
444 				patchcol = (column_t *)((UINT8 *)realpatch + LONG(realpatch->columnofs[(x1+width-1)-x]));
445 			else
446 				patchcol = (column_t *)((UINT8 *)realpatch + LONG(realpatch->columnofs[x-x1]));
447 
448 			// generate column ofset lookup
449 			*(UINT32 *)&colofs[x<<2] = LONG((x * texture->height) + (texture->width*4));
450 			ColumnDrawerPointer(patchcol, block + LONG(*(UINT32 *)&colofs[x<<2]), patch, texture->height, height);
451 		}
452 
453 		if (dealloc)
454 			Z_Free(realpatch);
455 	}
456 
457 done:
458 	// Now that the texture has been built in column cache, it is purgable from zone memory.
459 	Z_ChangeTag(block, PU_CACHE);
460 	return blocktex;
461 }
462 
463 //
464 // R_GenerateTextureAsFlat
465 //
466 // Generates a flat picture for a texture.
467 //
R_GenerateTextureAsFlat(size_t texnum)468 UINT8 *R_GenerateTextureAsFlat(size_t texnum)
469 {
470 	texture_t *texture = textures[texnum];
471 	UINT8 *converted = NULL;
472 	size_t size = (texture->width * texture->height);
473 
474 	// The flat picture for this texture was not generated yet.
475 	if (!texture->flat)
476 	{
477 		// Well, let's do it now, then.
478 		texture->flat = Z_Malloc(size, PU_STATIC, NULL);
479 
480 		// Picture_TextureToFlat handles everything for us.
481 		converted = (UINT8 *)Picture_TextureToFlat(texnum);
482 		M_Memcpy(texture->flat, converted, size);
483 		Z_Free(converted);
484 	}
485 
486 	return texture->flat;
487 }
488 
489 //
490 // R_GetTextureNum
491 //
492 // Returns the actual texture id that we should use.
493 // This can either be texnum, the current frame for texnum's anim (if animated),
494 // or 0 if not valid.
495 //
R_GetTextureNum(INT32 texnum)496 INT32 R_GetTextureNum(INT32 texnum)
497 {
498 	if (texnum < 0 || texnum >= numtextures)
499 		return 0;
500 	return texturetranslation[texnum];
501 }
502 
503 //
504 // R_CheckTextureCache
505 //
506 // Use this if you need to make sure the texture is cached before R_GetColumn calls
507 // e.g.: midtextures and FOF walls
508 //
R_CheckTextureCache(INT32 tex)509 void R_CheckTextureCache(INT32 tex)
510 {
511 	if (!texturecache[tex])
512 		R_GenerateTexture(tex);
513 }
514 
515 //
516 // R_GetColumn
517 //
R_GetColumn(fixed_t tex,INT32 col)518 UINT8 *R_GetColumn(fixed_t tex, INT32 col)
519 {
520 	UINT8 *data;
521 	INT32 width = texturewidth[tex];
522 
523 	if (width & (width - 1))
524 		col = (UINT32)col % width;
525 	else
526 		col &= (width - 1);
527 
528 	data = texturecache[tex];
529 	if (!data)
530 		data = R_GenerateTexture(tex);
531 
532 	return data + LONG(texturecolumnofs[tex][col]);
533 }
534 
R_GetFlat(lumpnum_t flatlumpnum)535 void *R_GetFlat(lumpnum_t flatlumpnum)
536 {
537 	return W_CacheLumpNum(flatlumpnum, PU_CACHE);
538 }
539 
540 //
541 // R_GetLevelFlat
542 //
543 // If needed, convert a texture or patch to a flat.
544 //
R_GetLevelFlat(levelflat_t * levelflat)545 void *R_GetLevelFlat(levelflat_t *levelflat)
546 {
547 	boolean isleveltexture = (levelflat->type == LEVELFLAT_TEXTURE);
548 	texture_t *texture = (isleveltexture ? textures[levelflat->u.texture.num] : NULL);
549 	boolean texturechanged = (isleveltexture ? (levelflat->u.texture.num != levelflat->u.texture.lastnum) : false);
550 	UINT8 *flatdata = NULL;
551 
552 	// Check if the texture changed.
553 	if (isleveltexture && (!texturechanged))
554 	{
555 		if (texture->flat)
556 		{
557 			flatdata = texture->flat;
558 			ds_flatwidth = texture->width;
559 			ds_flatheight = texture->height;
560 			texturechanged = false;
561 		}
562 		else
563 			texturechanged = true;
564 	}
565 
566 	// If the texture changed, or the flat wasn't generated, convert.
567 	if (levelflat->picture == NULL || texturechanged)
568 	{
569 		// Level texture
570 		if (isleveltexture)
571 		{
572 			levelflat->picture = R_GenerateTextureAsFlat(levelflat->u.texture.num);
573 			ds_flatwidth = levelflat->width = texture->width;
574 			ds_flatheight = levelflat->height = texture->height;
575 		}
576 		else
577 		{
578 #ifndef NO_PNG_LUMPS
579 			if (levelflat->type == LEVELFLAT_PNG)
580 			{
581 				INT32 pngwidth, pngheight;
582 
583 				levelflat->picture = Picture_PNGConvert(W_CacheLumpNum(levelflat->u.flat.lumpnum, PU_CACHE), PICFMT_FLAT, &pngwidth, &pngheight, NULL, NULL, W_LumpLength(levelflat->u.flat.lumpnum), NULL, 0);
584 				levelflat->width = (UINT16)pngwidth;
585 				levelflat->height = (UINT16)pngheight;
586 
587 				ds_flatwidth = levelflat->width;
588 				ds_flatheight = levelflat->height;
589 			}
590 			else
591 #endif
592 			if (levelflat->type == LEVELFLAT_PATCH)
593 			{
594 				UINT8 *converted;
595 				size_t size;
596 				softwarepatch_t *patch = W_CacheLumpNum(levelflat->u.flat.lumpnum, PU_CACHE);
597 
598 				levelflat->width = ds_flatwidth = SHORT(patch->width);
599 				levelflat->height = ds_flatheight = SHORT(patch->height);
600 
601 				levelflat->picture = Z_Malloc(levelflat->width * levelflat->height, PU_LEVEL, NULL);
602 				converted = Picture_FlatConvert(PICFMT_DOOMPATCH, patch, PICFMT_FLAT, 0, &size, levelflat->width, levelflat->height, SHORT(patch->topoffset), SHORT(patch->leftoffset), 0);
603 				M_Memcpy(levelflat->picture, converted, size);
604 				Z_Free(converted);
605 			}
606 		}
607 	}
608 	else
609 	{
610 		ds_flatwidth = levelflat->width;
611 		ds_flatheight = levelflat->height;
612 	}
613 
614 	levelflat->u.texture.lastnum = levelflat->u.texture.num;
615 
616 	if (flatdata == NULL)
617 		flatdata = levelflat->picture;
618 	return flatdata;
619 }
620 
621 //
622 // R_CheckPowersOfTwo
623 //
624 // Sets ds_powersoftwo true if the flat's dimensions are powers of two, and returns that.
625 //
R_CheckPowersOfTwo(void)626 boolean R_CheckPowersOfTwo(void)
627 {
628 	boolean wpow2 = (!(ds_flatwidth & (ds_flatwidth - 1)));
629 	boolean hpow2 = (!(ds_flatheight & (ds_flatheight - 1)));
630 
631 	// Initially, the flat isn't powers-of-two-sized.
632 	ds_powersoftwo = false;
633 
634 	// But if the width and height are powers of two,
635 	// and are EQUAL, then it's okay :]
636 	if ((ds_flatwidth == ds_flatheight) && (wpow2 && hpow2))
637 		ds_powersoftwo = true;
638 
639 	// Just return ds_powersoftwo.
640 	return ds_powersoftwo;
641 }
642 
643 //
644 // R_CheckFlatLength
645 //
646 // Determine the flat's dimensions from its lump length.
647 //
R_CheckFlatLength(size_t size)648 void R_CheckFlatLength(size_t size)
649 {
650 	switch (size)
651 	{
652 		case 4194304: // 2048x2048 lump
653 			nflatmask = 0x3FF800;
654 			nflatxshift = 21;
655 			nflatyshift = 10;
656 			nflatshiftup = 5;
657 			ds_flatwidth = ds_flatheight = 2048;
658 			break;
659 		case 1048576: // 1024x1024 lump
660 			nflatmask = 0xFFC00;
661 			nflatxshift = 22;
662 			nflatyshift = 12;
663 			nflatshiftup = 6;
664 			ds_flatwidth = ds_flatheight = 1024;
665 			break;
666 		case 262144:// 512x512 lump
667 			nflatmask = 0x3FE00;
668 			nflatxshift = 23;
669 			nflatyshift = 14;
670 			nflatshiftup = 7;
671 			ds_flatwidth = ds_flatheight = 512;
672 			break;
673 		case 65536: // 256x256 lump
674 			nflatmask = 0xFF00;
675 			nflatxshift = 24;
676 			nflatyshift = 16;
677 			nflatshiftup = 8;
678 			ds_flatwidth = ds_flatheight = 256;
679 			break;
680 		case 16384: // 128x128 lump
681 			nflatmask = 0x3F80;
682 			nflatxshift = 25;
683 			nflatyshift = 18;
684 			nflatshiftup = 9;
685 			ds_flatwidth = ds_flatheight = 128;
686 			break;
687 		case 1024: // 32x32 lump
688 			nflatmask = 0x3E0;
689 			nflatxshift = 27;
690 			nflatyshift = 22;
691 			nflatshiftup = 11;
692 			ds_flatwidth = ds_flatheight = 32;
693 			break;
694 		default: // 64x64 lump
695 			nflatmask = 0xFC0;
696 			nflatxshift = 26;
697 			nflatyshift = 20;
698 			nflatshiftup = 10;
699 			ds_flatwidth = ds_flatheight = 64;
700 			break;
701 	}
702 }
703 
704 //
705 // Empty the texture cache (used for load wad at runtime)
706 //
R_FlushTextureCache(void)707 void R_FlushTextureCache(void)
708 {
709 	INT32 i;
710 
711 	if (numtextures)
712 		for (i = 0; i < numtextures; i++)
713 			Z_Free(texturecache[i]);
714 }
715 
716 // Need these prototypes for later; defining them here instead of r_textures.h so they're "private"
717 int R_CountTexturesInTEXTURESLump(UINT16 wadNum, UINT16 lumpNum);
718 void R_ParseTEXTURESLump(UINT16 wadNum, UINT16 lumpNum, INT32 *index);
719 
720 #ifdef WALLFLATS
721 static INT32
Rloadflats(INT32 i,INT32 w)722 Rloadflats (INT32 i, INT32 w)
723 {
724 	UINT16 j;
725 	UINT16 texstart, texend;
726 	texture_t *texture;
727 	texpatch_t *patch;
728 
729 	// Yes
730 	if (wadfiles[w]->type == RET_PK3)
731 	{
732 		texstart = W_CheckNumForFolderStartPK3("flats/", (UINT16)w, 0);
733 		texend = W_CheckNumForFolderEndPK3("flats/", (UINT16)w, texstart);
734 	}
735 	else
736 	{
737 		texstart = W_CheckNumForMarkerStartPwad("F_START", (UINT16)w, 0);
738 		texend = W_CheckNumForNamePwad("F_END", (UINT16)w, texstart);
739 	}
740 
741 	if (!( texstart == INT16_MAX || texend == INT16_MAX ))
742 	{
743 		// Work through each lump between the markers in the WAD.
744 		for (j = 0; j < (texend - texstart); j++)
745 		{
746 			UINT8 *flatlump;
747 			UINT16 wadnum = (UINT16)w;
748 			lumpnum_t lumpnum = texstart + j;
749 			size_t lumplength;
750 			size_t flatsize = 0;
751 
752 			if (wadfiles[w]->type == RET_PK3)
753 			{
754 				if (W_IsLumpFolder(wadnum, lumpnum)) // Check if lump is a folder
755 					continue; // If it is then SKIP IT
756 			}
757 
758 			flatlump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE);
759 			lumplength = W_LumpLengthPwad(wadnum, lumpnum);
760 
761 			switch (lumplength)
762 			{
763 				case 4194304: // 2048x2048 lump
764 					flatsize = 2048;
765 					break;
766 				case 1048576: // 1024x1024 lump
767 					flatsize = 1024;
768 					break;
769 				case 262144:// 512x512 lump
770 					flatsize = 512;
771 					break;
772 				case 65536: // 256x256 lump
773 					flatsize = 256;
774 					break;
775 				case 16384: // 128x128 lump
776 					flatsize = 128;
777 					break;
778 				case 1024: // 32x32 lump
779 					flatsize = 32;
780 					break;
781 				default: // 64x64 lump
782 					flatsize = 64;
783 					break;
784 			}
785 
786 			//CONS_Printf("\n\"%s\" is a flat, dimensions %d x %d",W_CheckNameForNumPwad((UINT16)w,texstart+j),flatsize,flatsize);
787 			texture = textures[i] = Z_Calloc(sizeof(texture_t) + sizeof(texpatch_t), PU_STATIC, NULL);
788 
789 			// Set texture properties.
790 			M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name));
791 
792 #ifndef NO_PNG_LUMPS
793 			if (Picture_IsLumpPNG((UINT8 *)flatlump, lumplength))
794 			{
795 				INT32 width, height;
796 				Picture_PNGDimensions((UINT8 *)flatlump, &width, &height, NULL, NULL, lumplength);
797 				texture->width = (INT16)width;
798 				texture->height = (INT16)height;
799 			}
800 			else
801 #endif
802 				texture->width = texture->height = flatsize;
803 
804 			texture->type = TEXTURETYPE_FLAT;
805 			texture->patchcount = 1;
806 			texture->holes = false;
807 			texture->flip = 0;
808 
809 			// Allocate information for the texture's patches.
810 			patch = &texture->patches[0];
811 
812 			patch->originx = patch->originy = 0;
813 			patch->wad = (UINT16)w;
814 			patch->lump = texstart + j;
815 			patch->flip = 0;
816 
817 			Z_Unlock(flatlump);
818 
819 			texturewidth[i] = texture->width;
820 			textureheight[i] = texture->height << FRACBITS;
821 			i++;
822 		}
823 	}
824 
825 	return i;
826 }
827 #endif/*WALLFLATS*/
828 
829 #define TX_START "TX_START"
830 #define TX_END "TX_END"
831 
832 static INT32
Rloadtextures(INT32 i,INT32 w)833 Rloadtextures (INT32 i, INT32 w)
834 {
835 	UINT16 j;
836 	UINT16 texstart, texend, texturesLumpPos;
837 	texture_t *texture;
838 	softwarepatch_t *patchlump;
839 	texpatch_t *patch;
840 
841 	// Get the lump numbers for the markers in the WAD, if they exist.
842 	if (wadfiles[w]->type == RET_PK3)
843 	{
844 		texstart = W_CheckNumForFolderStartPK3("textures/", (UINT16)w, 0);
845 		texend = W_CheckNumForFolderEndPK3("textures/", (UINT16)w, texstart);
846 		texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0);
847 		while (texturesLumpPos != INT16_MAX)
848 		{
849 			R_ParseTEXTURESLump(w, texturesLumpPos, &i);
850 			texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, texturesLumpPos + 1);
851 		}
852 	}
853 	else
854 	{
855 		texstart = W_CheckNumForMarkerStartPwad(TX_START, (UINT16)w, 0);
856 		texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0);
857 		texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0);
858 		if (texturesLumpPos != INT16_MAX)
859 			R_ParseTEXTURESLump(w, texturesLumpPos, &i);
860 	}
861 
862 	if (!( texstart == INT16_MAX || texend == INT16_MAX ))
863 	{
864 		// Work through each lump between the markers in the WAD.
865 		for (j = 0; j < (texend - texstart); j++)
866 		{
867 			UINT16 wadnum = (UINT16)w;
868 			lumpnum_t lumpnum = texstart + j;
869 #ifndef NO_PNG_LUMPS
870 			size_t lumplength;
871 #endif
872 
873 			if (wadfiles[w]->type == RET_PK3)
874 			{
875 				if (W_IsLumpFolder(wadnum, lumpnum)) // Check if lump is a folder
876 					continue; // If it is then SKIP IT
877 			}
878 
879 			patchlump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE);
880 #ifndef NO_PNG_LUMPS
881 			lumplength = W_LumpLengthPwad(wadnum, lumpnum);
882 #endif
883 
884 			//CONS_Printf("\n\"%s\" is a single patch, dimensions %d x %d",W_CheckNameForNumPwad((UINT16)w,texstart+j),patchlump->width, patchlump->height);
885 			texture = textures[i] = Z_Calloc(sizeof(texture_t) + sizeof(texpatch_t), PU_STATIC, NULL);
886 
887 			// Set texture properties.
888 			M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name));
889 
890 #ifndef NO_PNG_LUMPS
891 			if (Picture_IsLumpPNG((UINT8 *)patchlump, lumplength))
892 			{
893 				INT32 width, height;
894 				Picture_PNGDimensions((UINT8 *)patchlump, &width, &height, NULL, NULL, lumplength);
895 				texture->width = (INT16)width;
896 				texture->height = (INT16)height;
897 			}
898 			else
899 #endif
900 			{
901 				texture->width = SHORT(patchlump->width);
902 				texture->height = SHORT(patchlump->height);
903 			}
904 
905 			texture->type = TEXTURETYPE_SINGLEPATCH;
906 			texture->patchcount = 1;
907 			texture->holes = false;
908 			texture->flip = 0;
909 
910 			// Allocate information for the texture's patches.
911 			patch = &texture->patches[0];
912 
913 			patch->originx = patch->originy = 0;
914 			patch->wad = (UINT16)w;
915 			patch->lump = texstart + j;
916 			patch->flip = 0;
917 
918 			Z_Unlock(patchlump);
919 
920 			texturewidth[i] = texture->width;
921 			textureheight[i] = texture->height << FRACBITS;
922 			i++;
923 		}
924 	}
925 
926 	return i;
927 }
928 
929 //
930 // R_LoadTextures
931 // Initializes the texture list with the textures from the world map.
932 //
R_LoadTextures(void)933 void R_LoadTextures(void)
934 {
935 	INT32 i, w;
936 	UINT16 j;
937 	UINT16 texstart, texend, texturesLumpPos;
938 
939 	// Free previous memory before numtextures change.
940 	if (numtextures)
941 	{
942 		for (i = 0; i < numtextures; i++)
943 		{
944 			Z_Free(textures[i]);
945 			Z_Free(texturecache[i]);
946 		}
947 		Z_Free(texturetranslation);
948 		Z_Free(textures);
949 	}
950 
951 	// Load patches and textures.
952 
953 	// Get the number of textures to check.
954 	// NOTE: Make SURE the system does not process
955 	// the markers.
956 	// This system will allocate memory for all duplicate/patched textures even if it never uses them,
957 	// but the alternative is to spend a ton of time checking and re-checking all previous entries just to skip any potentially patched textures.
958 	for (w = 0, numtextures = 0; w < numwadfiles; w++)
959 	{
960 #ifdef WALLFLATS
961 		// Count flats
962 		if (wadfiles[w]->type == RET_PK3)
963 		{
964 			texstart = W_CheckNumForFolderStartPK3("flats/", (UINT16)w, 0);
965 			texend = W_CheckNumForFolderEndPK3("flats/", (UINT16)w, texstart);
966 		}
967 		else
968 		{
969 			texstart = W_CheckNumForMarkerStartPwad("F_START", (UINT16)w, 0);
970 			texend = W_CheckNumForNamePwad("F_END", (UINT16)w, texstart);
971 		}
972 
973 		if (!( texstart == INT16_MAX || texend == INT16_MAX ))
974 		{
975 			// PK3s have subfolders, so we can't just make a simple sum
976 			if (wadfiles[w]->type == RET_PK3)
977 			{
978 				for (j = texstart; j < texend; j++)
979 				{
980 					if (!W_IsLumpFolder((UINT16)w, j)) // Check if lump is a folder; if not, then count it
981 						numtextures++;
982 				}
983 			}
984 			else // Add all the textures between F_START and F_END
985 			{
986 				numtextures += (UINT32)(texend - texstart);
987 			}
988 		}
989 #endif/*WALLFLATS*/
990 
991 		// Count the textures from TEXTURES lumps
992 		texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, 0);
993 		while (texturesLumpPos != INT16_MAX)
994 		{
995 			numtextures += R_CountTexturesInTEXTURESLump((UINT16)w, (UINT16)texturesLumpPos);
996 			texturesLumpPos = W_CheckNumForNamePwad("TEXTURES", (UINT16)w, texturesLumpPos + 1);
997 		}
998 
999 		// Count single-patch textures
1000 		if (wadfiles[w]->type == RET_PK3)
1001 		{
1002 			texstart = W_CheckNumForFolderStartPK3("textures/", (UINT16)w, 0);
1003 			texend = W_CheckNumForFolderEndPK3("textures/", (UINT16)w, texstart);
1004 		}
1005 		else
1006 		{
1007 			texstart = W_CheckNumForMarkerStartPwad(TX_START, (UINT16)w, 0);
1008 			texend = W_CheckNumForNamePwad(TX_END, (UINT16)w, 0);
1009 		}
1010 
1011 		if (texstart == INT16_MAX || texend == INT16_MAX)
1012 			continue;
1013 
1014 		// PK3s have subfolders, so we can't just make a simple sum
1015 		if (wadfiles[w]->type == RET_PK3)
1016 		{
1017 			for (j = texstart; j < texend; j++)
1018 			{
1019 				if (!W_IsLumpFolder((UINT16)w, j)) // Check if lump is a folder; if not, then count it
1020 					numtextures++;
1021 			}
1022 		}
1023 		else // Add all the textures between TX_START and TX_END
1024 		{
1025 			numtextures += (UINT32)(texend - texstart);
1026 		}
1027 	}
1028 
1029 	// If no textures found by this point, bomb out
1030 	if (!numtextures)
1031 		I_Error("No textures detected in any WADs!\n");
1032 
1033 	// Allocate memory and initialize to 0 for all the textures we are initialising.
1034 	// There are actually 5 buffers allocated in one for convenience.
1035 	textures = Z_Calloc((numtextures * sizeof(void *)) * 5, PU_STATIC, NULL);
1036 
1037 	// Allocate texture column offset table.
1038 	texturecolumnofs = (void *)((UINT8 *)textures + (numtextures * sizeof(void *)));
1039 	// Allocate texture referencing cache.
1040 	texturecache     = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 2));
1041 	// Allocate texture width table.
1042 	texturewidth     = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 3));
1043 	// Allocate texture height table.
1044 	textureheight    = (void *)((UINT8 *)textures + ((numtextures * sizeof(void *)) * 4));
1045 	// Create translation table for global animation.
1046 	texturetranslation = Z_Malloc((numtextures + 1) * sizeof(*texturetranslation), PU_STATIC, NULL);
1047 
1048 	for (i = 0; i < numtextures; i++)
1049 		texturetranslation[i] = i;
1050 
1051 	for (i = 0, w = 0; w < numwadfiles; w++)
1052 	{
1053 #ifdef WALLFLATS
1054 		i = Rloadflats(i, w);
1055 #endif
1056 		i = Rloadtextures(i, w);
1057 	}
1058 
1059 #ifdef HWRENDER
1060 	if (rendermode == render_opengl)
1061 		HWR_LoadMapTextures(numtextures);
1062 #endif
1063 }
1064 
R_ParsePatch(boolean actuallyLoadPatch)1065 static texpatch_t *R_ParsePatch(boolean actuallyLoadPatch)
1066 {
1067 	char *texturesToken;
1068 	size_t texturesTokenLength;
1069 	char *endPos;
1070 	char *patchName = NULL;
1071 	INT16 patchXPos;
1072 	INT16 patchYPos;
1073 	UINT8 flip = 0;
1074 	UINT8 alpha = 255;
1075 	enum patchalphastyle style = AST_COPY;
1076 	texpatch_t *resultPatch = NULL;
1077 	lumpnum_t patchLumpNum;
1078 
1079 	// Patch identifier
1080 	texturesToken = M_GetToken(NULL);
1081 	if (texturesToken == NULL)
1082 	{
1083 		I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch name should be");
1084 	}
1085 	texturesTokenLength = strlen(texturesToken);
1086 	if (texturesTokenLength>8)
1087 	{
1088 		I_Error("Error parsing TEXTURES lump: Patch name \"%s\" exceeds 8 characters",texturesToken);
1089 	}
1090 	else
1091 	{
1092 		if (patchName != NULL)
1093 		{
1094 			Z_Free(patchName);
1095 		}
1096 		patchName = (char *)Z_Malloc((texturesTokenLength+1)*sizeof(char),PU_STATIC,NULL);
1097 		M_Memcpy(patchName,texturesToken,texturesTokenLength*sizeof(char));
1098 		patchName[texturesTokenLength] = '\0';
1099 	}
1100 
1101 	// Comma 1
1102 	Z_Free(texturesToken);
1103 	texturesToken = M_GetToken(NULL);
1104 	if (texturesToken == NULL)
1105 	{
1106 		I_Error("Error parsing TEXTURES lump: Unexpected end of file where comma after \"%s\"'s patch name should be",patchName);
1107 	}
1108 	if (strcmp(texturesToken,",")!=0)
1109 	{
1110 		I_Error("Error parsing TEXTURES lump: Expected \",\" after %s's patch name, got \"%s\"",patchName,texturesToken);
1111 	}
1112 
1113 	// XPos
1114 	Z_Free(texturesToken);
1115 	texturesToken = M_GetToken(NULL);
1116 	if (texturesToken == NULL)
1117 	{
1118 		I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch \"%s\"'s x coordinate should be",patchName);
1119 	}
1120 	endPos = NULL;
1121 #ifndef AVOID_ERRNO
1122 	errno = 0;
1123 #endif
1124 	patchXPos = strtol(texturesToken,&endPos,10);
1125 	(void)patchXPos; //unused for now
1126 	if (endPos == texturesToken // Empty string
1127 		|| *endPos != '\0' // Not end of string
1128 #ifndef AVOID_ERRNO
1129 		|| errno == ERANGE // Number out-of-range
1130 #endif
1131 		)
1132 	{
1133 		I_Error("Error parsing TEXTURES lump: Expected an integer for patch \"%s\"'s x coordinate, got \"%s\"",patchName,texturesToken);
1134 	}
1135 
1136 	// Comma 2
1137 	Z_Free(texturesToken);
1138 	texturesToken = M_GetToken(NULL);
1139 	if (texturesToken == NULL)
1140 	{
1141 		I_Error("Error parsing TEXTURES lump: Unexpected end of file where comma after patch \"%s\"'s x coordinate should be",patchName);
1142 	}
1143 	if (strcmp(texturesToken,",")!=0)
1144 	{
1145 		I_Error("Error parsing TEXTURES lump: Expected \",\" after patch \"%s\"'s x coordinate, got \"%s\"",patchName,texturesToken);
1146 	}
1147 
1148 	// YPos
1149 	Z_Free(texturesToken);
1150 	texturesToken = M_GetToken(NULL);
1151 	if (texturesToken == NULL)
1152 	{
1153 		I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch \"%s\"'s y coordinate should be",patchName);
1154 	}
1155 	endPos = NULL;
1156 #ifndef AVOID_ERRNO
1157 	errno = 0;
1158 #endif
1159 	patchYPos = strtol(texturesToken,&endPos,10);
1160 	(void)patchYPos; //unused for now
1161 	if (endPos == texturesToken // Empty string
1162 		|| *endPos != '\0' // Not end of string
1163 #ifndef AVOID_ERRNO
1164 		|| errno == ERANGE // Number out-of-range
1165 #endif
1166 		)
1167 	{
1168 		I_Error("Error parsing TEXTURES lump: Expected an integer for patch \"%s\"'s y coordinate, got \"%s\"",patchName,texturesToken);
1169 	}
1170 	Z_Free(texturesToken);
1171 
1172 	// Patch parameters block (OPTIONAL)
1173 	// added by Monster Iestyn (22/10/16)
1174 
1175 	// Left Curly Brace
1176 	texturesToken = M_GetToken(NULL);
1177 	if (texturesToken == NULL)
1178 		; // move on and ignore, R_ParseTextures will deal with this
1179 	else
1180 	{
1181 		if (strcmp(texturesToken,"{")==0)
1182 		{
1183 			Z_Free(texturesToken);
1184 			texturesToken = M_GetToken(NULL);
1185 			if (texturesToken == NULL)
1186 			{
1187 				I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch \"%s\"'s parameters should be",patchName);
1188 			}
1189 			while (strcmp(texturesToken,"}")!=0)
1190 			{
1191 				if (stricmp(texturesToken, "ALPHA")==0)
1192 				{
1193 					Z_Free(texturesToken);
1194 					texturesToken = M_GetToken(NULL);
1195 					alpha = 255*strtof(texturesToken, NULL);
1196 				}
1197 				else if (stricmp(texturesToken, "STYLE")==0)
1198 				{
1199 					Z_Free(texturesToken);
1200 					texturesToken = M_GetToken(NULL);
1201 					if (stricmp(texturesToken, "TRANSLUCENT")==0)
1202 						style = AST_TRANSLUCENT;
1203 					else if (stricmp(texturesToken, "ADD")==0)
1204 						style = AST_ADD;
1205 					else if (stricmp(texturesToken, "SUBTRACT")==0)
1206 						style = AST_SUBTRACT;
1207 					else if (stricmp(texturesToken, "REVERSESUBTRACT")==0)
1208 						style = AST_REVERSESUBTRACT;
1209 					else if (stricmp(texturesToken, "MODULATE")==0)
1210 						style = AST_MODULATE;
1211 				}
1212 				else if (stricmp(texturesToken, "FLIPX")==0)
1213 					flip |= 1;
1214 				else if (stricmp(texturesToken, "FLIPY")==0)
1215 					flip |= 2;
1216 				Z_Free(texturesToken);
1217 
1218 				texturesToken = M_GetToken(NULL);
1219 				if (texturesToken == NULL)
1220 				{
1221 					I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch \"%s\"'s parameters or right curly brace should be",patchName);
1222 				}
1223 			}
1224 		}
1225 		else
1226 		{
1227 			 // this is not what we wanted...
1228 			 // undo last read so R_ParseTextures can re-get the token for its own purposes
1229 			M_UnGetToken();
1230 		}
1231 		Z_Free(texturesToken);
1232 	}
1233 
1234 	if (actuallyLoadPatch == true)
1235 	{
1236 		// Check lump exists
1237 		patchLumpNum = W_GetNumForName(patchName);
1238 		// If so, allocate memory for texpatch_t and fill 'er up
1239 		resultPatch = (texpatch_t *)Z_Malloc(sizeof(texpatch_t),PU_STATIC,NULL);
1240 		resultPatch->originx = patchXPos;
1241 		resultPatch->originy = patchYPos;
1242 		resultPatch->lump = patchLumpNum & 65535;
1243 		resultPatch->wad = patchLumpNum>>16;
1244 		resultPatch->flip = flip;
1245 		resultPatch->alpha = alpha;
1246 		resultPatch->style = style;
1247 		// Clean up a little after ourselves
1248 		Z_Free(patchName);
1249 		// Then return it
1250 		return resultPatch;
1251 	}
1252 	else
1253 	{
1254 		Z_Free(patchName);
1255 		return NULL;
1256 	}
1257 }
1258 
R_ParseTexture(boolean actuallyLoadTexture)1259 static texture_t *R_ParseTexture(boolean actuallyLoadTexture)
1260 {
1261 	char *texturesToken;
1262 	size_t texturesTokenLength;
1263 	char *endPos;
1264 	INT32 newTextureWidth;
1265 	INT32 newTextureHeight;
1266 	texture_t *resultTexture = NULL;
1267 	texpatch_t *newPatch;
1268 	char newTextureName[9]; // no longer dynamically allocated
1269 
1270 	// Texture name
1271 	texturesToken = M_GetToken(NULL);
1272 	if (texturesToken == NULL)
1273 	{
1274 		I_Error("Error parsing TEXTURES lump: Unexpected end of file where texture name should be");
1275 	}
1276 	texturesTokenLength = strlen(texturesToken);
1277 	if (texturesTokenLength>8)
1278 	{
1279 		I_Error("Error parsing TEXTURES lump: Texture name \"%s\" exceeds 8 characters",texturesToken);
1280 	}
1281 	else
1282 	{
1283 		memset(&newTextureName, 0, 9);
1284 		M_Memcpy(newTextureName, texturesToken, texturesTokenLength);
1285 		// ^^ we've confirmed that the token is <= 8 characters so it will never overflow a 9 byte char buffer
1286 		strupr(newTextureName); // Just do this now so we don't have to worry about it
1287 	}
1288 	Z_Free(texturesToken);
1289 
1290 	// Comma 1
1291 	texturesToken = M_GetToken(NULL);
1292 	if (texturesToken == NULL)
1293 	{
1294 		I_Error("Error parsing TEXTURES lump: Unexpected end of file where comma after texture \"%s\"'s name should be",newTextureName);
1295 	}
1296 	else if (strcmp(texturesToken,",")!=0)
1297 	{
1298 		I_Error("Error parsing TEXTURES lump: Expected \",\" after texture \"%s\"'s name, got \"%s\"",newTextureName,texturesToken);
1299 	}
1300 	Z_Free(texturesToken);
1301 
1302 	// Width
1303 	texturesToken = M_GetToken(NULL);
1304 	if (texturesToken == NULL)
1305 	{
1306 		I_Error("Error parsing TEXTURES lump: Unexpected end of file where texture \"%s\"'s width should be",newTextureName);
1307 	}
1308 	endPos = NULL;
1309 #ifndef AVOID_ERRNO
1310 	errno = 0;
1311 #endif
1312 	newTextureWidth = strtol(texturesToken,&endPos,10);
1313 	if (endPos == texturesToken // Empty string
1314 		|| *endPos != '\0' // Not end of string
1315 #ifndef AVOID_ERRNO
1316 		|| errno == ERANGE // Number out-of-range
1317 #endif
1318 		|| newTextureWidth < 0) // Number is not positive
1319 	{
1320 		I_Error("Error parsing TEXTURES lump: Expected a positive integer for texture \"%s\"'s width, got \"%s\"",newTextureName,texturesToken);
1321 	}
1322 	Z_Free(texturesToken);
1323 
1324 	// Comma 2
1325 	texturesToken = M_GetToken(NULL);
1326 	if (texturesToken == NULL)
1327 	{
1328 		I_Error("Error parsing TEXTURES lump: Unexpected end of file where comma after texture \"%s\"'s width should be",newTextureName);
1329 	}
1330 	if (strcmp(texturesToken,",")!=0)
1331 	{
1332 		I_Error("Error parsing TEXTURES lump: Expected \",\" after texture \"%s\"'s width, got \"%s\"",newTextureName,texturesToken);
1333 	}
1334 	Z_Free(texturesToken);
1335 
1336 	// Height
1337 	texturesToken = M_GetToken(NULL);
1338 	if (texturesToken == NULL)
1339 	{
1340 		I_Error("Error parsing TEXTURES lump: Unexpected end of file where texture \"%s\"'s height should be",newTextureName);
1341 	}
1342 	endPos = NULL;
1343 #ifndef AVOID_ERRNO
1344 	errno = 0;
1345 #endif
1346 	newTextureHeight = strtol(texturesToken,&endPos,10);
1347 	if (endPos == texturesToken // Empty string
1348 		|| *endPos != '\0' // Not end of string
1349 #ifndef AVOID_ERRNO
1350 		|| errno == ERANGE // Number out-of-range
1351 #endif
1352 		|| newTextureHeight < 0) // Number is not positive
1353 	{
1354 		I_Error("Error parsing TEXTURES lump: Expected a positive integer for texture \"%s\"'s height, got \"%s\"",newTextureName,texturesToken);
1355 	}
1356 	Z_Free(texturesToken);
1357 
1358 	// Left Curly Brace
1359 	texturesToken = M_GetToken(NULL);
1360 	if (texturesToken == NULL)
1361 	{
1362 		I_Error("Error parsing TEXTURES lump: Unexpected end of file where open curly brace for texture \"%s\" should be",newTextureName);
1363 	}
1364 	if (strcmp(texturesToken,"{")==0)
1365 	{
1366 		if (actuallyLoadTexture)
1367 		{
1368 			// Allocate memory for a zero-patch texture. Obviously, we'll be adding patches momentarily.
1369 			resultTexture = (texture_t *)Z_Calloc(sizeof(texture_t),PU_STATIC,NULL);
1370 			M_Memcpy(resultTexture->name, newTextureName, 8);
1371 			resultTexture->width = newTextureWidth;
1372 			resultTexture->height = newTextureHeight;
1373 			resultTexture->type = TEXTURETYPE_COMPOSITE;
1374 		}
1375 		Z_Free(texturesToken);
1376 		texturesToken = M_GetToken(NULL);
1377 		if (texturesToken == NULL)
1378 		{
1379 			I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch definition for texture \"%s\" should be",newTextureName);
1380 		}
1381 		while (strcmp(texturesToken,"}")!=0)
1382 		{
1383 			if (stricmp(texturesToken, "PATCH")==0)
1384 			{
1385 				Z_Free(texturesToken);
1386 				if (resultTexture)
1387 				{
1388 					// Get that new patch
1389 					newPatch = R_ParsePatch(true);
1390 					// Make room for the new patch
1391 					resultTexture = Z_Realloc(resultTexture, sizeof(texture_t) + (resultTexture->patchcount+1)*sizeof(texpatch_t), PU_STATIC, NULL);
1392 					// Populate the uninitialized values in the new patch entry of our array
1393 					M_Memcpy(&resultTexture->patches[resultTexture->patchcount], newPatch, sizeof(texpatch_t));
1394 					// Account for the new number of patches in the texture
1395 					resultTexture->patchcount++;
1396 					// Then free up the memory assigned to R_ParsePatch, as it's unneeded now
1397 					Z_Free(newPatch);
1398 				}
1399 				else
1400 				{
1401 					R_ParsePatch(false);
1402 				}
1403 			}
1404 			else
1405 			{
1406 				I_Error("Error parsing TEXTURES lump: Expected \"PATCH\" in texture \"%s\", got \"%s\"",newTextureName,texturesToken);
1407 			}
1408 
1409 			texturesToken = M_GetToken(NULL);
1410 			if (texturesToken == NULL)
1411 			{
1412 				I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch declaration or right curly brace for texture \"%s\" should be",newTextureName);
1413 			}
1414 		}
1415 		if (resultTexture && resultTexture->patchcount == 0)
1416 		{
1417 			I_Error("Error parsing TEXTURES lump: Texture \"%s\" must have at least one patch",newTextureName);
1418 		}
1419 	}
1420 	else
1421 	{
1422 		I_Error("Error parsing TEXTURES lump: Expected \"{\" for texture \"%s\", got \"%s\"",newTextureName,texturesToken);
1423 	}
1424 	Z_Free(texturesToken);
1425 
1426 	if (actuallyLoadTexture) return resultTexture;
1427 	else return NULL;
1428 }
1429 
1430 // Parses the TEXTURES lump... but just to count the number of textures.
R_CountTexturesInTEXTURESLump(UINT16 wadNum,UINT16 lumpNum)1431 int R_CountTexturesInTEXTURESLump(UINT16 wadNum, UINT16 lumpNum)
1432 {
1433 	char *texturesLump;
1434 	size_t texturesLumpLength;
1435 	char *texturesText;
1436 	UINT32 numTexturesInLump = 0;
1437 	char *texturesToken;
1438 
1439 	// Since lumps AREN'T \0-terminated like I'd assumed they should be, I'll
1440 	// need to make a space of memory where I can ensure that it will terminate
1441 	// correctly. Start by loading the relevant data from the WAD.
1442 	texturesLump = (char *)W_CacheLumpNumPwad(wadNum, lumpNum, PU_STATIC);
1443 	// If that didn't exist, we have nothing to do here.
1444 	if (texturesLump == NULL) return 0;
1445 	// If we're still here, then it DOES exist; figure out how long it is, and allot memory accordingly.
1446 	texturesLumpLength = W_LumpLengthPwad(wadNum, lumpNum);
1447 	texturesText = (char *)Z_Malloc((texturesLumpLength+1)*sizeof(char),PU_STATIC,NULL);
1448 	// Now move the contents of the lump into this new location.
1449 	memmove(texturesText,texturesLump,texturesLumpLength);
1450 	// Make damn well sure the last character in our new memory location is \0.
1451 	texturesText[texturesLumpLength] = '\0';
1452 	// Finally, free up the memory from the first data load, because we really
1453 	// don't need it.
1454 	Z_Free(texturesLump);
1455 
1456 	texturesToken = M_GetToken(texturesText);
1457 	while (texturesToken != NULL)
1458 	{
1459 		if (stricmp(texturesToken, "WALLTEXTURE") == 0 || stricmp(texturesToken, "TEXTURE") == 0)
1460 		{
1461 			numTexturesInLump++;
1462 			Z_Free(texturesToken);
1463 			R_ParseTexture(false);
1464 		}
1465 		else
1466 		{
1467 			I_Error("Error parsing TEXTURES lump: Expected \"WALLTEXTURE\" or \"TEXTURE\", got \"%s\"",texturesToken);
1468 		}
1469 		texturesToken = M_GetToken(NULL);
1470 	}
1471 	Z_Free(texturesToken);
1472 	Z_Free((void *)texturesText);
1473 
1474 	return numTexturesInLump;
1475 }
1476 
1477 // Parses the TEXTURES lump... for real, this time.
R_ParseTEXTURESLump(UINT16 wadNum,UINT16 lumpNum,INT32 * texindex)1478 void R_ParseTEXTURESLump(UINT16 wadNum, UINT16 lumpNum, INT32 *texindex)
1479 {
1480 	char *texturesLump;
1481 	size_t texturesLumpLength;
1482 	char *texturesText;
1483 	char *texturesToken;
1484 	texture_t *newTexture;
1485 
1486 	I_Assert(texindex != NULL);
1487 
1488 	// Since lumps AREN'T \0-terminated like I'd assumed they should be, I'll
1489 	// need to make a space of memory where I can ensure that it will terminate
1490 	// correctly. Start by loading the relevant data from the WAD.
1491 	texturesLump = (char *)W_CacheLumpNumPwad(wadNum, lumpNum, PU_STATIC);
1492 	// If that didn't exist, we have nothing to do here.
1493 	if (texturesLump == NULL) return;
1494 	// If we're still here, then it DOES exist; figure out how long it is, and allot memory accordingly.
1495 	texturesLumpLength = W_LumpLengthPwad(wadNum, lumpNum);
1496 	texturesText = (char *)Z_Malloc((texturesLumpLength+1)*sizeof(char),PU_STATIC,NULL);
1497 	// Now move the contents of the lump into this new location.
1498 	memmove(texturesText,texturesLump,texturesLumpLength);
1499 	// Make damn well sure the last character in our new memory location is \0.
1500 	texturesText[texturesLumpLength] = '\0';
1501 	// Finally, free up the memory from the first data load, because we really
1502 	// don't need it.
1503 	Z_Free(texturesLump);
1504 
1505 	texturesToken = M_GetToken(texturesText);
1506 	while (texturesToken != NULL)
1507 	{
1508 		if (stricmp(texturesToken, "WALLTEXTURE") == 0 || stricmp(texturesToken, "TEXTURE") == 0)
1509 		{
1510 			Z_Free(texturesToken);
1511 			// Get the new texture
1512 			newTexture = R_ParseTexture(true);
1513 			// Store the new texture
1514 			textures[*texindex] = newTexture;
1515 			texturewidth[*texindex] = newTexture->width;
1516 			textureheight[*texindex] = newTexture->height << FRACBITS;
1517 			// Increment i back in R_LoadTextures()
1518 			(*texindex)++;
1519 		}
1520 		else
1521 		{
1522 			I_Error("Error parsing TEXTURES lump: Expected \"WALLTEXTURE\" or \"TEXTURE\", got \"%s\"",texturesToken);
1523 		}
1524 		texturesToken = M_GetToken(NULL);
1525 	}
1526 	Z_Free(texturesToken);
1527 	Z_Free((void *)texturesText);
1528 }
1529 
1530 // Search for flat name.
R_GetFlatNumForName(const char * name)1531 lumpnum_t R_GetFlatNumForName(const char *name)
1532 {
1533 	INT32 i;
1534 	lumpnum_t lump;
1535 	lumpnum_t start;
1536 	lumpnum_t end;
1537 
1538 	// Scan wad files backwards so patched flats take preference.
1539 	for (i = numwadfiles - 1; i >= 0; i--)
1540 	{
1541 		switch (wadfiles[i]->type)
1542 		{
1543 		case RET_WAD:
1544 			if ((start = W_CheckNumForMarkerStartPwad("F_START", (UINT16)i, 0)) == INT16_MAX)
1545 			{
1546 				if ((start = W_CheckNumForMarkerStartPwad("FF_START", (UINT16)i, 0)) == INT16_MAX)
1547 					continue;
1548 				else if ((end = W_CheckNumForNamePwad("FF_END", (UINT16)i, start)) == INT16_MAX)
1549 					continue;
1550 			}
1551 			else
1552 				if ((end = W_CheckNumForNamePwad("F_END", (UINT16)i, start)) == INT16_MAX)
1553 					continue;
1554 			break;
1555 		case RET_PK3:
1556 			if ((start = W_CheckNumForFolderStartPK3("Flats/", i, 0)) == INT16_MAX)
1557 				continue;
1558 			if ((end = W_CheckNumForFolderEndPK3("Flats/", i, start)) == INT16_MAX)
1559 				continue;
1560 			break;
1561 		default:
1562 			continue;
1563 		}
1564 
1565 		// Now find lump with specified name in that range.
1566 		lump = W_CheckNumForNamePwad(name, (UINT16)i, start);
1567 		if (lump < end)
1568 		{
1569 			lump += (i<<16); // found it, in our constraints
1570 			break;
1571 		}
1572 		lump = LUMPERROR;
1573 	}
1574 
1575 	return lump;
1576 }
1577 
R_ClearTextureNumCache(boolean btell)1578 void R_ClearTextureNumCache(boolean btell)
1579 {
1580 	if (tidcache)
1581 		Z_Free(tidcache);
1582 	tidcache = NULL;
1583 	if (btell)
1584 		CONS_Debug(DBG_SETUP, "Fun Fact: There are %d textures used in this map.\n", tidcachelen);
1585 	tidcachelen = 0;
1586 }
1587 
1588 //
1589 // R_CheckTextureNumForName
1590 //
1591 // Check whether texture is available. Filter out NoTexture indicator.
1592 //
R_CheckTextureNumForName(const char * name)1593 INT32 R_CheckTextureNumForName(const char *name)
1594 {
1595 	INT32 i;
1596 
1597 	// "NoTexture" marker.
1598 	if (name[0] == '-')
1599 		return 0;
1600 
1601 	for (i = 0; i < tidcachelen; i++)
1602 		if (!strncasecmp(tidcache[i].name, name, 8))
1603 			return tidcache[i].id;
1604 
1605 	// Need to parse the list backwards, so textures loaded more recently are used in lieu of ones loaded earlier
1606 	//for (i = 0; i < numtextures; i++) <- old
1607 	for (i = (numtextures - 1); i >= 0; i--) // <- new
1608 		if (!strncasecmp(textures[i]->name, name, 8))
1609 		{
1610 			tidcachelen++;
1611 			Z_Realloc(tidcache, tidcachelen * sizeof(*tidcache), PU_STATIC, &tidcache);
1612 			strncpy(tidcache[tidcachelen-1].name, name, 8);
1613 			tidcache[tidcachelen-1].name[8] = '\0';
1614 #ifndef ZDEBUG
1615 			CONS_Debug(DBG_SETUP, "texture #%s: %s\n", sizeu1(tidcachelen), tidcache[tidcachelen-1].name);
1616 #endif
1617 			tidcache[tidcachelen-1].id = i;
1618 			return i;
1619 		}
1620 
1621 	return -1;
1622 }
1623 
1624 //
1625 // R_TextureNumForName
1626 //
1627 // Calls R_CheckTextureNumForName, aborts with error message.
1628 //
R_TextureNumForName(const char * name)1629 INT32 R_TextureNumForName(const char *name)
1630 {
1631 	const INT32 i = R_CheckTextureNumForName(name);
1632 
1633 	if (i == -1)
1634 	{
1635 		static INT32 redwall = -2;
1636 		CONS_Debug(DBG_SETUP, "WARNING: R_TextureNumForName: %.8s not found\n", name);
1637 		if (redwall == -2)
1638 			redwall = R_CheckTextureNumForName("REDWALL");
1639 		if (redwall != -1)
1640 			return redwall;
1641 		return 1;
1642 	}
1643 	return i;
1644 }
1645