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