1 // SONIC ROBO BLAST 2
2 //-----------------------------------------------------------------------------
3 // Copyright (C) 1993-1996 by id Software, Inc.
4 // Copyright (C) 2005-2009 by Andrey "entryway" Budko.
5 // Copyright (C) 2018-2020 by Jaime "Lactozilla" Passos.
6 // Copyright (C) 2019-2020 by Sonic Team Junior.
7 //
8 // This program is free software distributed under the
9 // terms of the GNU General Public License, version 2.
10 // See the 'LICENSE' file for more details.
11 //-----------------------------------------------------------------------------
12 /// \file  r_picformats.c
13 /// \brief Picture generation.
14 
15 #include "byteptr.h"
16 #include "dehacked.h"
17 #include "i_video.h"
18 #include "r_data.h"
19 #include "r_patch.h"
20 #include "r_picformats.h"
21 #include "r_textures.h"
22 #include "r_things.h"
23 #include "r_draw.h"
24 #include "v_video.h"
25 #include "z_zone.h"
26 #include "w_wad.h"
27 
28 #ifdef HWRENDER
29 #include "hardware/hw_glob.h"
30 #endif
31 
32 #ifdef HAVE_PNG
33 
34 #ifndef _MSC_VER
35 #ifndef _LARGEFILE64_SOURCE
36 #define _LARGEFILE64_SOURCE
37 #endif
38 #endif
39 
40 #ifndef _LFS64_LARGEFILE
41 #define _LFS64_LARGEFILE
42 #endif
43 
44 #ifndef _FILE_OFFSET_BITS
45 #define _FILE_OFFSET_BITS 0
46 #endif
47 
48 #include "png.h"
49 #ifndef PNG_READ_SUPPORTED
50 #undef HAVE_PNG
51 #endif
52 #endif
53 
54 static unsigned char imgbuf[1<<26];
55 
56 #ifdef PICTURE_PNG_USELOOKUP
57 static colorlookup_t png_colorlookup;
58 #endif
59 
60 /** Converts a picture between two formats.
61   *
62   * \param informat Input picture format.
63   * \param picture Input picture data.
64   * \param outformat Output picture format.
65   * \param insize Input picture size.
66   * \param outsize Output picture size, as a pointer.
67   * \param inwidth Input picture width.
68   * \param inheight Input picture height.
69   * \param inleftoffset Input picture left offset, for patches.
70   * \param intopoffset Input picture top offset, for patches.
71   * \param flags Input picture flags.
72   * \return A pointer to the converted picture.
73   * \sa Picture_PatchConvert
74   * \sa Picture_FlatConvert
75   */
Picture_Convert(pictureformat_t informat,void * picture,pictureformat_t outformat,size_t insize,size_t * outsize,INT32 inwidth,INT32 inheight,INT32 inleftoffset,INT32 intopoffset,pictureflags_t flags)76 void *Picture_Convert(
77 	pictureformat_t informat, void *picture, pictureformat_t outformat,
78 	size_t insize, size_t *outsize,
79 	INT32 inwidth, INT32 inheight, INT32 inleftoffset, INT32 intopoffset,
80 	pictureflags_t flags)
81 {
82 	if (informat == PICFMT_NONE)
83 		I_Error("Picture_Convert: input format was PICFMT_NONE!");
84 	else if (outformat == PICFMT_NONE)
85 		I_Error("Picture_Convert: output format was PICFMT_NONE!");
86 	else if (informat == outformat)
87 		I_Error("Picture_Convert: input and output formats were the same!");
88 
89 	if (Picture_IsPatchFormat(outformat))
90 		return Picture_PatchConvert(informat, picture, outformat, insize, outsize, inwidth, inheight, inleftoffset, intopoffset, flags);
91 	else if (Picture_IsFlatFormat(outformat))
92 		return Picture_FlatConvert(informat, picture, outformat, insize, outsize, inwidth, inheight, inleftoffset, intopoffset, flags);
93 	else
94 		I_Error("Picture_Convert: unsupported input format!");
95 
96 	return NULL;
97 }
98 
99 /** Converts a picture to a patch.
100   *
101   * \param informat Input picture format.
102   * \param picture Input picture data.
103   * \param outformat Output picture format.
104   * \param insize Input picture size.
105   * \param outsize Output picture size, as a pointer.
106   * \param inwidth Input picture width.
107   * \param inheight Input picture height.
108   * \param inleftoffset Input picture left offset, for patches.
109   * \param intopoffset Input picture top offset, for patches.
110   * \param flags Input picture flags.
111   * \return A pointer to the converted picture.
112   */
Picture_PatchConvert(pictureformat_t informat,void * picture,pictureformat_t outformat,size_t insize,size_t * outsize,INT16 inwidth,INT16 inheight,INT16 inleftoffset,INT16 intopoffset,pictureflags_t flags)113 void *Picture_PatchConvert(
114 	pictureformat_t informat, void *picture, pictureformat_t outformat,
115 	size_t insize, size_t *outsize,
116 	INT16 inwidth, INT16 inheight, INT16 inleftoffset, INT16 intopoffset,
117 	pictureflags_t flags)
118 {
119 	INT16 x, y;
120 	UINT8 *img;
121 	UINT8 *imgptr = imgbuf;
122 	UINT8 *colpointers, *startofspan;
123 	size_t size = 0;
124 	patch_t *inpatch = NULL;
125 	INT32 inbpp = Picture_FormatBPP(informat);
126 
127 	(void)insize; // ignore
128 
129 	if (informat == PICFMT_NONE)
130 		I_Error("Picture_PatchConvert: input format was PICFMT_NONE!");
131 	else if (outformat == PICFMT_NONE)
132 		I_Error("Picture_PatchConvert: output format was PICFMT_NONE!");
133 	else if (informat == outformat)
134 		I_Error("Picture_PatchConvert: input and output formats were the same!");
135 
136 	if (inbpp == PICDEPTH_NONE)
137 		I_Error("Picture_PatchConvert: unknown input bits per pixel?!");
138 	if (Picture_FormatBPP(outformat) == PICDEPTH_NONE)
139 		I_Error("Picture_PatchConvert: unknown output bits per pixel?!");
140 
141 	// If it's a patch, you can just figure out
142 	// the dimensions from the header.
143 	if (Picture_IsPatchFormat(informat))
144 	{
145 		inpatch = (patch_t *)picture;
146 		if (Picture_IsDoomPatchFormat(informat))
147 		{
148 			softwarepatch_t *doompatch = (softwarepatch_t *)picture;
149 			inwidth = SHORT(doompatch->width);
150 			inheight = SHORT(doompatch->height);
151 			inleftoffset = SHORT(doompatch->leftoffset);
152 			intopoffset = SHORT(doompatch->topoffset);
153 		}
154 		else
155 		{
156 			inwidth = inpatch->width;
157 			inheight = inpatch->height;
158 			inleftoffset = inpatch->leftoffset;
159 			intopoffset = inpatch->topoffset;
160 		}
161 	}
162 
163 	// Write image size and offset
164 	WRITEINT16(imgptr, inwidth);
165 	WRITEINT16(imgptr, inheight);
166 	WRITEINT16(imgptr, inleftoffset);
167 	WRITEINT16(imgptr, intopoffset);
168 
169 	// Leave placeholder to column pointers
170 	colpointers = imgptr;
171 	imgptr += inwidth*4;
172 
173 	// Write columns
174 	for (x = 0; x < inwidth; x++)
175 	{
176 		int lastStartY = 0;
177 		int spanSize = 0;
178 		startofspan = NULL;
179 
180 		// Write column pointer
181 		WRITEINT32(colpointers, imgptr - imgbuf);
182 
183 		// Write pixels
184 		for (y = 0; y < inheight; y++)
185 		{
186 			void *input = NULL;
187 			boolean opaque = false;
188 
189 			// Read pixel
190 			if (Picture_IsPatchFormat(informat))
191 				input = Picture_GetPatchPixel(inpatch, informat, x, y, flags);
192 			else if (Picture_IsFlatFormat(informat))
193 			{
194 				size_t offs = ((y * inwidth) + x);
195 				switch (informat)
196 				{
197 					case PICFMT_FLAT32:
198 						input = (UINT32 *)picture + offs;
199 						break;
200 					case PICFMT_FLAT16:
201 						input = (UINT16 *)picture + offs;
202 						break;
203 					case PICFMT_FLAT:
204 						input = (UINT8 *)picture + offs;
205 						break;
206 					default:
207 						I_Error("Picture_PatchConvert: unsupported flat input format!");
208 						break;
209 				}
210 			}
211 			else
212 				I_Error("Picture_PatchConvert: unsupported input format!");
213 
214 			// Determine opacity
215 			if (input != NULL)
216 			{
217 				UINT8 alpha = 0xFF;
218 				if (inbpp == PICDEPTH_32BPP)
219 				{
220 					RGBA_t px = *(RGBA_t *)input;
221 					alpha = px.s.alpha;
222 				}
223 				else if (inbpp == PICDEPTH_16BPP)
224 				{
225 					UINT16 px = *(UINT16 *)input;
226 					alpha = (px & 0xFF00) >> 8;
227 				}
228 				else if (inbpp == PICDEPTH_8BPP)
229 				{
230 					UINT8 px = *(UINT8 *)input;
231 					if (px == TRANSPARENTPIXEL)
232 						alpha = 0;
233 				}
234 				opaque = (alpha > 1);
235 			}
236 
237 			// End span if we have a transparent pixel
238 			if (!opaque)
239 			{
240 				if (startofspan)
241 					WRITEUINT8(imgptr, 0);
242 				startofspan = NULL;
243 				continue;
244 			}
245 
246 			// Start new column if we need to
247 			if (!startofspan || spanSize == 255)
248 			{
249 				int writeY = y;
250 
251 				// If we reached the span size limit, finish the previous span
252 				if (startofspan)
253 					WRITEUINT8(imgptr, 0);
254 
255 				if (y > 254)
256 				{
257 					// Make sure we're aligned to 254
258 					if (lastStartY < 254)
259 					{
260 						WRITEUINT8(imgptr, 254);
261 						WRITEUINT8(imgptr, 0);
262 						imgptr += 2;
263 						lastStartY = 254;
264 					}
265 
266 					// Write stopgap empty spans if needed
267 					writeY = y - lastStartY;
268 
269 					while (writeY > 254)
270 					{
271 						WRITEUINT8(imgptr, 254);
272 						WRITEUINT8(imgptr, 0);
273 						imgptr += 2;
274 						writeY -= 254;
275 					}
276 				}
277 
278 				startofspan = imgptr;
279 				WRITEUINT8(imgptr, writeY);
280 				imgptr += 2;
281 				spanSize = 0;
282 
283 				lastStartY = y;
284 			}
285 
286 			// Write the pixel
287 			switch (outformat)
288 			{
289 				case PICFMT_PATCH32:
290 				case PICFMT_DOOMPATCH32:
291 				{
292 					if (inbpp == PICDEPTH_32BPP)
293 					{
294 						RGBA_t out = *(RGBA_t *)input;
295 						WRITEUINT32(imgptr, out.rgba);
296 					}
297 					else if (inbpp == PICDEPTH_16BPP)
298 					{
299 						RGBA_t out = pMasterPalette[*((UINT16 *)input) & 0xFF];
300 						WRITEUINT32(imgptr, out.rgba);
301 					}
302 					else // PICFMT_PATCH
303 					{
304 						RGBA_t out = pMasterPalette[*((UINT8 *)input) & 0xFF];
305 						WRITEUINT32(imgptr, out.rgba);
306 					}
307 					break;
308 				}
309 				case PICFMT_PATCH16:
310 				case PICFMT_DOOMPATCH16:
311 					if (inbpp == PICDEPTH_32BPP)
312 					{
313 						RGBA_t in = *(RGBA_t *)input;
314 						UINT8 out = NearestColor(in.s.red, in.s.green, in.s.blue);
315 						WRITEUINT16(imgptr, (0xFF00 | out));
316 					}
317 					else if (inbpp == PICDEPTH_16BPP)
318 						WRITEUINT16(imgptr, *(UINT16 *)input);
319 					else // PICFMT_PATCH
320 						WRITEUINT16(imgptr, (0xFF00 | (*(UINT8 *)input)));
321 					break;
322 				default: // PICFMT_PATCH
323 				{
324 					if (inbpp == PICDEPTH_32BPP)
325 					{
326 						RGBA_t in = *(RGBA_t *)input;
327 						UINT8 out = NearestColor(in.s.red, in.s.green, in.s.blue);
328 						WRITEUINT8(imgptr, out);
329 					}
330 					else if (inbpp == PICDEPTH_16BPP)
331 					{
332 						UINT16 out = *(UINT16 *)input;
333 						WRITEUINT8(imgptr, (out & 0xFF));
334 					}
335 					else // PICFMT_PATCH
336 						WRITEUINT8(imgptr, *(UINT8 *)input);
337 					break;
338 				}
339 			}
340 
341 			spanSize++;
342 			startofspan[1] = spanSize;
343 		}
344 
345 		if (startofspan)
346 			WRITEUINT8(imgptr, 0);
347 
348 		WRITEUINT8(imgptr, 0xFF);
349 	}
350 
351 	size = imgptr-imgbuf;
352 	img = Z_Malloc(size, PU_STATIC, NULL);
353 	memcpy(img, imgbuf, size);
354 
355 	if (Picture_IsInternalPatchFormat(outformat))
356 	{
357 		patch_t *converted = Patch_Create((softwarepatch_t *)img, size, NULL);
358 
359 #ifdef HWRENDER
360 		Patch_CreateGL(converted);
361 #endif
362 
363 		Z_Free(img);
364 
365 		if (outsize != NULL)
366 			*outsize = sizeof(patch_t);
367 		return converted;
368 	}
369 	else
370 	{
371 		if (outsize != NULL)
372 			*outsize = size;
373 		return img;
374 	}
375 }
376 
377 /** Converts a picture to a flat.
378   *
379   * \param informat Input picture format.
380   * \param picture Input picture data.
381   * \param outformat Output picture format.
382   * \param insize Input picture size.
383   * \param outsize Output picture size, as a pointer.
384   * \param inwidth Input picture width.
385   * \param inheight Input picture height.
386   * \param inleftoffset Input picture left offset, for patches.
387   * \param intopoffset Input picture top offset, for patches.
388   * \param flags Input picture flags.
389   * \return A pointer to the converted picture.
390   */
Picture_FlatConvert(pictureformat_t informat,void * picture,pictureformat_t outformat,size_t insize,size_t * outsize,INT16 inwidth,INT16 inheight,INT16 inleftoffset,INT16 intopoffset,pictureflags_t flags)391 void *Picture_FlatConvert(
392 	pictureformat_t informat, void *picture, pictureformat_t outformat,
393 	size_t insize, size_t *outsize,
394 	INT16 inwidth, INT16 inheight, INT16 inleftoffset, INT16 intopoffset,
395 	pictureflags_t flags)
396 {
397 	void *outflat;
398 	patch_t *inpatch = NULL;
399 	INT32 inbpp = Picture_FormatBPP(informat);
400 	INT32 outbpp = Picture_FormatBPP(outformat);
401 	INT32 x, y;
402 	size_t size;
403 
404 	(void)insize; // ignore
405 	(void)inleftoffset; // ignore
406 	(void)intopoffset; // ignore
407 
408 	if (informat == PICFMT_NONE)
409 		I_Error("Picture_FlatConvert: input format was PICFMT_NONE!");
410 	else if (outformat == PICFMT_NONE)
411 		I_Error("Picture_FlatConvert: output format was PICFMT_NONE!");
412 	else if (informat == outformat)
413 		I_Error("Picture_FlatConvert: input and output formats were the same!");
414 
415 	if (inbpp == PICDEPTH_NONE)
416 		I_Error("Picture_FlatConvert: unknown input bits per pixel?!");
417 	if (outbpp == PICDEPTH_NONE)
418 		I_Error("Picture_FlatConvert: unknown output bits per pixel?!");
419 
420 	// If it's a patch, you can just figure out
421 	// the dimensions from the header.
422 	if (Picture_IsPatchFormat(informat))
423 	{
424 		inpatch = (patch_t *)picture;
425 		if (Picture_IsDoomPatchFormat(informat))
426 		{
427 			softwarepatch_t *doompatch = ((softwarepatch_t *)picture);
428 			inwidth = SHORT(doompatch->width);
429 			inheight = SHORT(doompatch->height);
430 		}
431 		else
432 		{
433 			inwidth = inpatch->width;
434 			inheight = inpatch->height;
435 		}
436 	}
437 
438 	size = (inwidth * inheight) * (outbpp / 8);
439 	outflat = Z_Calloc(size, PU_STATIC, NULL);
440 	if (outsize)
441 		*outsize = size;
442 
443 	// Set transparency
444 	if (outbpp == PICDEPTH_8BPP)
445 		memset(outflat, TRANSPARENTPIXEL, size);
446 
447 	for (y = 0; y < inheight; y++)
448 		for (x = 0; x < inwidth; x++)
449 		{
450 			void *input;
451 			size_t offs = ((y * inwidth) + x);
452 
453 			// Read pixel
454 			if (Picture_IsPatchFormat(informat))
455 				input = Picture_GetPatchPixel(inpatch, informat, x, y, flags);
456 			else if (Picture_IsFlatFormat(informat))
457 				input = (UINT8 *)picture + (offs * (inbpp / 8));
458 			else
459 				I_Error("Picture_FlatConvert: unsupported input format!");
460 
461 			if (!input)
462 				continue;
463 
464 			switch (outformat)
465 			{
466 				case PICFMT_FLAT32:
467 				{
468 					UINT32 *f32 = (UINT32 *)outflat;
469 					if (inbpp == PICDEPTH_32BPP)
470 					{
471 						RGBA_t out = *(RGBA_t *)input;
472 						f32[offs] = out.rgba;
473 					}
474 					else if (inbpp == PICDEPTH_16BPP)
475 					{
476 						RGBA_t out = pMasterPalette[*((UINT16 *)input) & 0xFF];
477 						f32[offs] = out.rgba;
478 					}
479 					else // PICFMT_PATCH
480 					{
481 						RGBA_t out = pMasterPalette[*((UINT8 *)input) & 0xFF];
482 						f32[offs] = out.rgba;
483 					}
484 					break;
485 				}
486 				case PICFMT_FLAT16:
487 				{
488 					UINT16 *f16 = (UINT16 *)outflat;
489 					if (inbpp == PICDEPTH_32BPP)
490 					{
491 						RGBA_t in = *(RGBA_t *)input;
492 						UINT8 out = NearestColor(in.s.red, in.s.green, in.s.blue);
493 						f16[offs] = (0xFF00 | out);
494 					}
495 					else if (inbpp == PICDEPTH_16BPP)
496 						f16[offs] = *(UINT16 *)input;
497 					else // PICFMT_PATCH
498 						f16[offs] = (0xFF00 | *((UINT8 *)input));
499 					break;
500 				}
501 				case PICFMT_FLAT:
502 				{
503 					UINT8 *f8 = (UINT8 *)outflat;
504 					if (inbpp == PICDEPTH_32BPP)
505 					{
506 						RGBA_t in = *(RGBA_t *)input;
507 						UINT8 out = NearestColor(in.s.red, in.s.green, in.s.blue);
508 						f8[offs] = out;
509 					}
510 					else if (inbpp == PICDEPTH_16BPP)
511 					{
512 						UINT16 out = *(UINT16 *)input;
513 						f8[offs] = (out & 0xFF);
514 					}
515 					else // PICFMT_PATCH
516 						f8[offs] = *(UINT8 *)input;
517 					break;
518 				}
519 				default:
520 					I_Error("Picture_FlatConvert: unsupported output format!");
521 			}
522 		}
523 
524 	return outflat;
525 }
526 
527 /** Returns a pixel from a patch.
528   *
529   * \param patch Input patch.
530   * \param informat Input picture format.
531   * \param x Pixel X position.
532   * \param y Pixel Y position.
533   * \param flags Input picture flags.
534   * \return A pointer to a pixel in the patch. Returns NULL if not opaque.
535   */
Picture_GetPatchPixel(patch_t * patch,pictureformat_t informat,INT32 x,INT32 y,pictureflags_t flags)536 void *Picture_GetPatchPixel(
537 	patch_t *patch, pictureformat_t informat,
538 	INT32 x, INT32 y,
539 	pictureflags_t flags)
540 {
541 	fixed_t ofs;
542 	column_t *column;
543 	UINT8 *s8 = NULL;
544 	UINT16 *s16 = NULL;
545 	UINT32 *s32 = NULL;
546 	softwarepatch_t *doompatch = (softwarepatch_t *)patch;
547 	boolean isdoompatch = Picture_IsDoomPatchFormat(informat);
548 	INT16 width;
549 
550 	if (patch == NULL)
551 		I_Error("Picture_GetPatchPixel: patch == NULL");
552 
553 	width = (isdoompatch ? SHORT(doompatch->width) : patch->width);
554 
555 	if (x >= 0 && x < width)
556 	{
557 		INT32 colx = (flags & PICFLAGS_XFLIP) ? (width-1)-x : x;
558 		INT32 topdelta, prevdelta = -1;
559 		INT32 colofs = (isdoompatch ? LONG(doompatch->columnofs[colx]) : patch->columnofs[colx]);
560 
561 		// Column offsets are pointers, so no casting is required.
562 		if (isdoompatch)
563 			column = (column_t *)((UINT8 *)doompatch + colofs);
564 		else
565 			column = (column_t *)((UINT8 *)patch->columns + colofs);
566 
567 		while (column->topdelta != 0xff)
568 		{
569 			topdelta = column->topdelta;
570 			if (topdelta <= prevdelta)
571 				topdelta += prevdelta;
572 			prevdelta = topdelta;
573 			s8 = (UINT8 *)(column) + 3;
574 			if (Picture_FormatBPP(informat) == PICDEPTH_32BPP)
575 				s32 = (UINT32 *)s8;
576 			else if (Picture_FormatBPP(informat) == PICDEPTH_16BPP)
577 				s16 = (UINT16 *)s8;
578 			for (ofs = 0; ofs < column->length; ofs++)
579 			{
580 				if ((topdelta + ofs) == y)
581 				{
582 					if (Picture_FormatBPP(informat) == PICDEPTH_32BPP)
583 						return &s32[ofs];
584 					else if (Picture_FormatBPP(informat) == PICDEPTH_16BPP)
585 						return &s16[ofs];
586 					else // PICDEPTH_8BPP
587 						return &s8[ofs];
588 				}
589 			}
590 			if (Picture_FormatBPP(informat) == PICDEPTH_32BPP)
591 				column = (column_t *)((UINT32 *)column + column->length);
592 			else if (Picture_FormatBPP(informat) == PICDEPTH_16BPP)
593 				column = (column_t *)((UINT16 *)column + column->length);
594 			else
595 				column = (column_t *)((UINT8 *)column + column->length);
596 			column = (column_t *)((UINT8 *)column + 4);
597 		}
598 	}
599 
600 	return NULL;
601 }
602 
603 /** Returns the amount of bits per pixel in the specified picture format.
604   *
605   * \param format Input picture format.
606   * \return The bits per pixel amount of the picture format.
607   */
Picture_FormatBPP(pictureformat_t format)608 INT32 Picture_FormatBPP(pictureformat_t format)
609 {
610 	INT32 bpp = PICDEPTH_NONE;
611 	switch (format)
612 	{
613 		case PICFMT_PATCH32:
614 		case PICFMT_FLAT32:
615 		case PICFMT_DOOMPATCH32:
616 		case PICFMT_PNG:
617 			bpp = PICDEPTH_32BPP;
618 			break;
619 		case PICFMT_PATCH16:
620 		case PICFMT_FLAT16:
621 		case PICFMT_DOOMPATCH16:
622 			bpp = PICDEPTH_16BPP;
623 			break;
624 		case PICFMT_PATCH:
625 		case PICFMT_FLAT:
626 		case PICFMT_DOOMPATCH:
627 			bpp = PICDEPTH_8BPP;
628 			break;
629 		default:
630 			break;
631 	}
632 	return bpp;
633 }
634 
635 /** Checks if the specified picture format is a patch.
636   *
637   * \param format Input picture format.
638   * \return True if the picture format is a patch, false if not.
639   */
Picture_IsPatchFormat(pictureformat_t format)640 boolean Picture_IsPatchFormat(pictureformat_t format)
641 {
642 	return (Picture_IsInternalPatchFormat(format) || Picture_IsDoomPatchFormat(format));
643 }
644 
645 /** Checks if the specified picture format is an internal patch.
646   *
647   * \param format Input picture format.
648   * \return True if the picture format is an internal patch, false if not.
649   */
Picture_IsInternalPatchFormat(pictureformat_t format)650 boolean Picture_IsInternalPatchFormat(pictureformat_t format)
651 {
652 	switch (format)
653 	{
654 		case PICFMT_PATCH:
655 		case PICFMT_PATCH16:
656 		case PICFMT_PATCH32:
657 			return true;
658 		default:
659 			return false;
660 	}
661 }
662 
663 /** Checks if the specified picture format is a Doom patch.
664   *
665   * \param format Input picture format.
666   * \return True if the picture format is a Doom patch, false if not.
667   */
Picture_IsDoomPatchFormat(pictureformat_t format)668 boolean Picture_IsDoomPatchFormat(pictureformat_t format)
669 {
670 	switch (format)
671 	{
672 		case PICFMT_DOOMPATCH:
673 		case PICFMT_DOOMPATCH16:
674 		case PICFMT_DOOMPATCH32:
675 			return true;
676 		default:
677 			return false;
678 	}
679 }
680 
681 /** Checks if the specified picture format is a flat.
682   *
683   * \param format Input picture format.
684   * \return True if the picture format is a flat, false if not.
685   */
Picture_IsFlatFormat(pictureformat_t format)686 boolean Picture_IsFlatFormat(pictureformat_t format)
687 {
688 	return (format == PICFMT_FLAT || format == PICFMT_FLAT16 || format == PICFMT_FLAT32);
689 }
690 
691 /** Returns true if the lump is a valid Doom patch.
692   * PICFMT_DOOMPATCH only.
693   *
694   * \param patch Input patch.
695   * \param picture Input patch size.
696   * \return True if the input patch is valid.
697   */
Picture_CheckIfDoomPatch(softwarepatch_t * patch,size_t size)698 boolean Picture_CheckIfDoomPatch(softwarepatch_t *patch, size_t size)
699 {
700 	INT16 width, height;
701 	boolean result;
702 
703 	// minimum length of a valid Doom patch
704 	if (size < 13)
705 		return false;
706 
707 	width = SHORT(patch->width);
708 	height = SHORT(patch->height);
709 	result = (height > 0 && height <= 16384 && width > 0 && width <= 16384);
710 
711 	if (result)
712 	{
713 		// The dimensions seem like they might be valid for a patch, so
714 		// check the column directory for extra security. All columns
715 		// must begin after the column directory, and none of them must
716 		// point past the end of the patch.
717 		INT16 x;
718 
719 		for (x = 0; x < width; x++)
720 		{
721 			UINT32 ofs = LONG(patch->columnofs[x]);
722 
723 			// Need one byte for an empty column (but there's patches that don't know that!)
724 			if (ofs < (UINT32)width * 4 + 8 || ofs >= (UINT32)size)
725 			{
726 				result = false;
727 				break;
728 			}
729 		}
730 	}
731 
732 	return result;
733 }
734 
735 /** Converts a texture to a flat.
736   *
737   * \param trickytex The texture number.
738   * \return The converted flat.
739   */
Picture_TextureToFlat(size_t trickytex)740 void *Picture_TextureToFlat(size_t trickytex)
741 {
742 	texture_t *texture;
743 	size_t tex;
744 
745 	UINT8 *converted;
746 	size_t flatsize;
747 	fixed_t col, ofs;
748 	column_t *column;
749 	UINT8 *desttop, *dest, *deststop;
750 	UINT8 *source;
751 
752 	if (trickytex >= (unsigned)numtextures)
753 		I_Error("Picture_TextureToFlat: invalid texture number!");
754 
755 	// Check the texture cache
756 	// If the texture's not there, it'll be generated right now
757 	tex = trickytex;
758 	texture = textures[tex];
759 	R_CheckTextureCache(tex);
760 
761 	// Allocate the flat
762 	flatsize = (texture->width * texture->height);
763 	converted = Z_Malloc(flatsize, PU_STATIC, NULL);
764 	memset(converted, TRANSPARENTPIXEL, flatsize);
765 
766 	// Now we're gonna write to it
767 	desttop = converted;
768 	deststop = desttop + flatsize;
769 	for (col = 0; col < texture->width; col++, desttop++)
770 	{
771 		// no post_t info
772 		if (!texture->holes)
773 		{
774 			column = (column_t *)(R_GetColumn(tex, col));
775 			source = (UINT8 *)(column);
776 			dest = desttop;
777 			for (ofs = 0; dest < deststop && ofs < texture->height; ofs++)
778 			{
779 				if (source[ofs] != TRANSPARENTPIXEL)
780 					*dest = source[ofs];
781 				dest += texture->width;
782 			}
783 		}
784 		else
785 		{
786 			INT32 topdelta, prevdelta = -1;
787 			column = (column_t *)((UINT8 *)R_GetColumn(tex, col) - 3);
788 			while (column->topdelta != 0xff)
789 			{
790 				topdelta = column->topdelta;
791 				if (topdelta <= prevdelta)
792 					topdelta += prevdelta;
793 				prevdelta = topdelta;
794 
795 				dest = desttop + (topdelta * texture->width);
796 				source = (UINT8 *)column + 3;
797 				for (ofs = 0; dest < deststop && ofs < column->length; ofs++)
798 				{
799 					if (source[ofs] != TRANSPARENTPIXEL)
800 						*dest = source[ofs];
801 					dest += texture->width;
802 				}
803 				column = (column_t *)((UINT8 *)column + column->length + 4);
804 			}
805 		}
806 	}
807 
808 	return converted;
809 }
810 
811 /** Returns true if the lump is a valid PNG.
812   *
813   * \param d The lump to be checked.
814   * \param s The lump size.
815   * \return True if the lump is a PNG image.
816   */
Picture_IsLumpPNG(const UINT8 * d,size_t s)817 boolean Picture_IsLumpPNG(const UINT8 *d, size_t s)
818 {
819 	if (s < 67) // http://garethrees.org/2007/11/14/pngcrush/
820 		return false;
821 	// Check for PNG file signature using memcmp
822 	// As it may be faster on CPUs with slow unaligned memory access
823 	// Ref: http://www.libpng.org/pub/png/spec/1.2/PNG-Rationale.html#R.PNG-file-signature
824 	return (memcmp(&d[0], "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a", 8) == 0);
825 }
826 
827 #ifndef NO_PNG_LUMPS
828 #ifdef HAVE_PNG
829 
830 /*#if PNG_LIBPNG_VER_DLLNUM < 14
831 typedef PNG_CONST png_byte *png_const_bytep;
832 #endif*/
833 typedef struct
834 {
835 	const UINT8 *buffer;
836 	UINT32 size;
837 	UINT32 position;
838 } png_io_t;
839 
PNG_IOReader(png_structp png_ptr,png_bytep data,png_size_t length)840 static void PNG_IOReader(png_structp png_ptr, png_bytep data, png_size_t length)
841 {
842 	png_io_t *f = png_get_io_ptr(png_ptr);
843 	if (length > (f->size - f->position))
844 		png_error(png_ptr, "PNG_IOReader: buffer overrun");
845 	memcpy(data, f->buffer + f->position, length);
846 	f->position += length;
847 }
848 
849 typedef struct
850 {
851 	char name[4];
852 	void *data;
853 	size_t size;
854 } png_chunk_t;
855 
856 static png_byte *chunkname = NULL;
857 static png_chunk_t chunk;
858 
PNG_ChunkReader(png_structp png_ptr,png_unknown_chunkp chonk)859 static int PNG_ChunkReader(png_structp png_ptr, png_unknown_chunkp chonk)
860 {
861 	(void)png_ptr;
862 	if (!memcmp(chonk->name, chunkname, 4))
863 	{
864 		memcpy(chunk.name, chonk->name, 4);
865 		chunk.size = chonk->size;
866 		chunk.data = Z_Malloc(chunk.size, PU_STATIC, NULL);
867 		memcpy(chunk.data, chonk->data, chunk.size);
868 		return 1;
869 	}
870 	return 0;
871 }
872 
PNG_error(png_structp PNG,png_const_charp pngtext)873 static void PNG_error(png_structp PNG, png_const_charp pngtext)
874 {
875 	CONS_Debug(DBG_RENDER, "libpng error at %p: %s", PNG, pngtext);
876 	//I_Error("libpng error at %p: %s", PNG, pngtext);
877 }
878 
PNG_warn(png_structp PNG,png_const_charp pngtext)879 static void PNG_warn(png_structp PNG, png_const_charp pngtext)
880 {
881 	CONS_Debug(DBG_RENDER, "libpng warning at %p: %s", PNG, pngtext);
882 }
883 
884 static png_byte grAb_chunk[5] = {'g', 'r', 'A', 'b', (png_byte)'\0'};
885 
PNG_Read(const UINT8 * png,INT32 * w,INT32 * h,INT16 * topoffset,INT16 * leftoffset,boolean * use_palette,size_t size)886 static png_bytep *PNG_Read(
887 	const UINT8 *png,
888 	INT32 *w, INT32 *h, INT16 *topoffset, INT16 *leftoffset,
889 	boolean *use_palette, size_t size)
890 {
891 	png_structp png_ptr;
892 	png_infop png_info_ptr;
893 	png_uint_32 width, height;
894 	int bit_depth, color_type;
895 	png_uint_32 y;
896 
897 	png_colorp palette;
898 	int palette_size;
899 
900 	png_bytep trans;
901 	int trans_num;
902 	png_color_16p trans_values;
903 
904 #ifdef PNG_SETJMP_SUPPORTED
905 #ifdef USE_FAR_KEYWORD
906 	jmp_buf jmpbuf;
907 #endif
908 #endif
909 
910 	png_io_t png_io;
911 	png_bytep *row_pointers;
912 	png_voidp *user_chunk_ptr;
913 
914 	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, PNG_error, PNG_warn);
915 	if (!png_ptr)
916 		I_Error("PNG_Read: Couldn't initialize libpng!");
917 
918 	png_info_ptr = png_create_info_struct(png_ptr);
919 	if (!png_info_ptr)
920 	{
921 		png_destroy_read_struct(&png_ptr, NULL, NULL);
922 		I_Error("PNG_Read: libpng couldn't allocate memory!");
923 	}
924 
925 #ifdef USE_FAR_KEYWORD
926 	if (setjmp(jmpbuf))
927 #else
928 	if (setjmp(png_jmpbuf(png_ptr)))
929 #endif
930 	{
931 		png_destroy_read_struct(&png_ptr, &png_info_ptr, NULL);
932 		I_Error("PNG_Read: libpng load error!");
933 	}
934 #ifdef USE_FAR_KEYWORD
935 	png_memcpy(png_jmpbuf(png_ptr), jmpbuf, sizeof jmp_buf);
936 #endif
937 
938 	png_io.buffer = png;
939 	png_io.size = size;
940 	png_io.position = 0;
941 	png_set_read_fn(png_ptr, &png_io, PNG_IOReader);
942 
943 	memset(&chunk, 0x00, sizeof(png_chunk_t));
944 	chunkname = grAb_chunk; // I want to read a grAb chunk
945 
946 	user_chunk_ptr = png_get_user_chunk_ptr(png_ptr);
947 	png_set_read_user_chunk_fn(png_ptr, user_chunk_ptr, PNG_ChunkReader);
948 	png_set_keep_unknown_chunks(png_ptr, 2, chunkname, 1);
949 
950 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
951 	png_set_user_limits(png_ptr, 2048, 2048);
952 #endif
953 
954 	png_read_info(png_ptr, png_info_ptr);
955 	png_get_IHDR(png_ptr, png_info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL);
956 
957 	if (bit_depth == 16)
958 		png_set_strip_16(png_ptr);
959 
960 	palette = NULL;
961 	*use_palette = false;
962 
963 	if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
964 		png_set_gray_to_rgb(png_ptr);
965 	else if (color_type == PNG_COLOR_TYPE_PALETTE)
966 	{
967 		boolean usepal = false;
968 
969 		// Lactozilla: Check if the PNG has a palette, and if its color count
970 		// matches the color count of SRB2's palette: 256 colors.
971 		if (png_get_PLTE(png_ptr, png_info_ptr, &palette, &palette_size))
972 		{
973 			if (palette_size == 256 && pMasterPalette)
974 			{
975 				png_colorp pal = palette;
976 				INT32 i;
977 
978 				usepal = true;
979 
980 				for (i = 0; i < 256; i++)
981 				{
982 					UINT32 rgb = R_PutRgbaRGBA(pal->red, pal->green, pal->blue, 0xFF);
983 					if (rgb != pMasterPalette[i].rgba)
984 					{
985 						usepal = false;
986 						break;
987 					}
988 					pal++;
989 				}
990 			}
991 		}
992 
993 		// If any of the tRNS colors have an alpha lower than 0xFF, and that
994 		// color is present on the image, the palette flag is disabled.
995 		if (usepal)
996 		{
997 			png_get_tRNS(png_ptr, png_info_ptr, &trans, &trans_num, &trans_values);
998 
999 			if (trans && trans_num == 256)
1000 			{
1001 				INT32 i;
1002 				for (i = 0; i < trans_num; i++)
1003 				{
1004 					// libpng will transform this image into RGB even if
1005 					// the transparent index does not exist in the image,
1006 					// and there is no way around that.
1007 					if (trans[i] < 0xFF)
1008 					{
1009 						usepal = false;
1010 						break;
1011 					}
1012 				}
1013 			}
1014 		}
1015 
1016 		if (usepal)
1017 			*use_palette = true;
1018 		else
1019 			png_set_palette_to_rgb(png_ptr);
1020 	}
1021 
1022 	if (png_get_valid(png_ptr, png_info_ptr, PNG_INFO_tRNS))
1023 		png_set_tRNS_to_alpha(png_ptr);
1024 	else if (color_type != PNG_COLOR_TYPE_RGB_ALPHA && color_type != PNG_COLOR_TYPE_GRAY_ALPHA)
1025 	{
1026 #if PNG_LIBPNG_VER < 10207
1027 		png_set_filler(png_ptr, 0xFF, PNG_FILLER_AFTER);
1028 #else
1029 		png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER);
1030 #endif
1031 	}
1032 
1033 	png_read_update_info(png_ptr, png_info_ptr);
1034 
1035 	// Read the image
1036 	row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height);
1037 	for (y = 0; y < height; y++)
1038 		row_pointers[y] = (png_byte*)malloc(png_get_rowbytes(png_ptr, png_info_ptr));
1039 	png_read_image(png_ptr, row_pointers);
1040 
1041 	// Read grAB chunk
1042 	if ((topoffset || leftoffset) && (chunk.data != NULL))
1043 	{
1044 		INT32 *offsets = (INT32 *)chunk.data;
1045 		// read left offset
1046 		if (leftoffset != NULL)
1047 			*leftoffset = (INT16)BIGENDIAN_LONG(*offsets);
1048 		offsets++;
1049 		// read top offset
1050 		if (topoffset != NULL)
1051 			*topoffset = (INT16)BIGENDIAN_LONG(*offsets);
1052 	}
1053 
1054 	png_destroy_read_struct(&png_ptr, &png_info_ptr, NULL);
1055 	if (chunk.data)
1056 		Z_Free(chunk.data);
1057 
1058 	*w = (INT32)width;
1059 	*h = (INT32)height;
1060 
1061 	return row_pointers;
1062 }
1063 
1064 /** Converts a PNG to a picture.
1065   *
1066   * \param png The PNG image.
1067   * \param outformat The output picture's format.
1068   * \param w The output picture's width, as a pointer.
1069   * \param h The output picture's height, as a pointer.
1070   * \param topoffset The output picture's top offset, for sprites, as a pointer.
1071   * \param leftoffset The output picture's left offset, for sprites, as a pointer.
1072   * \param insize The input picture's size.
1073   * \param outsize A pointer to the output picture's size.
1074   * \param flags Input picture flags.
1075   * \return A pointer to the converted picture.
1076   */
Picture_PNGConvert(const UINT8 * png,pictureformat_t outformat,INT32 * w,INT32 * h,INT16 * topoffset,INT16 * leftoffset,size_t insize,size_t * outsize,pictureflags_t flags)1077 void *Picture_PNGConvert(
1078 	const UINT8 *png, pictureformat_t outformat,
1079 	INT32 *w, INT32 *h,
1080 	INT16 *topoffset, INT16 *leftoffset,
1081 	size_t insize, size_t *outsize,
1082 	pictureflags_t flags)
1083 {
1084 	void *flat;
1085 	INT32 outbpp;
1086 	size_t flatsize;
1087 	png_uint_32 x, y;
1088 	png_bytep row;
1089 	boolean palette = false;
1090 	png_bytep *row_pointers = NULL;
1091 	png_uint_32 width, height;
1092 
1093 	INT32 pngwidth, pngheight;
1094 	INT16 loffs = 0, toffs = 0;
1095 
1096 	if (png == NULL)
1097 		I_Error("Picture_PNGConvert: picture was NULL!");
1098 
1099 	if (w == NULL)
1100 		w = &pngwidth;
1101 	if (h == NULL)
1102 		h = &pngheight;
1103 	if (topoffset == NULL)
1104 		topoffset = &toffs;
1105 	if (leftoffset == NULL)
1106 		leftoffset = &loffs;
1107 
1108 	row_pointers = PNG_Read(png, w, h, topoffset, leftoffset, &palette, insize);
1109 	width = *w;
1110 	height = *h;
1111 
1112 	if (row_pointers == NULL)
1113 		I_Error("Picture_PNGConvert: row_pointers was NULL!");
1114 
1115 	// Find the output format's bits per pixel amount
1116 	outbpp = Picture_FormatBPP(outformat);
1117 
1118 	// Hack for patches because you'll want to preserve transparency.
1119 	if (Picture_IsPatchFormat(outformat))
1120 	{
1121 		// Force a higher bit depth
1122 		if (outbpp == PICDEPTH_8BPP)
1123 			outbpp = PICDEPTH_16BPP;
1124 	}
1125 
1126 	// Shouldn't happen.
1127 	if (outbpp == PICDEPTH_NONE)
1128 		I_Error("Picture_PNGConvert: unknown output bits per pixel?!");
1129 
1130 	// Figure out the size
1131 	flatsize = (width * height) * (outbpp / 8);
1132 	if (outsize)
1133 		*outsize = flatsize;
1134 
1135 	// Convert the image
1136 	flat = Z_Calloc(flatsize, PU_STATIC, NULL);
1137 
1138 	// Set transparency
1139 	if (outbpp == PICDEPTH_8BPP)
1140 		memset(flat, TRANSPARENTPIXEL, (width * height));
1141 
1142 #ifdef PICTURE_PNG_USELOOKUP
1143 	if (outbpp != PICDEPTH_32BPP)
1144 		InitColorLUT(&png_colorlookup, pMasterPalette, false);
1145 #endif
1146 
1147 	if (outbpp == PICDEPTH_32BPP)
1148 	{
1149 		RGBA_t out;
1150 		UINT32 *outflat = (UINT32 *)flat;
1151 
1152 		if (palette)
1153 		{
1154 			for (y = 0; y < height; y++)
1155 			{
1156 				row = row_pointers[y];
1157 				for (x = 0; x < width; x++)
1158 				{
1159 					out = V_GetColor(row[x]);
1160 					outflat[((y * width) + x)] = out.rgba;
1161 				}
1162 			}
1163 		}
1164 		else
1165 		{
1166 			for (y = 0; y < height; y++)
1167 			{
1168 				row = row_pointers[y];
1169 				for (x = 0; x < width; x++)
1170 				{
1171 					png_bytep px = &(row[x * 4]);
1172 					if ((UINT8)px[3])
1173 					{
1174 						out.s.red = (UINT8)px[0];
1175 						out.s.green = (UINT8)px[1];
1176 						out.s.blue = (UINT8)px[2];
1177 						out.s.alpha = (UINT8)px[3];
1178 						outflat[((y * width) + x)] = out.rgba;
1179 					}
1180 					else
1181 						outflat[((y * width) + x)] = 0x00000000;
1182 				}
1183 			}
1184 		}
1185 	}
1186 	else if (outbpp == PICDEPTH_16BPP)
1187 	{
1188 		UINT16 *outflat = (UINT16 *)flat;
1189 
1190 		if (palette)
1191 		{
1192 			for (y = 0; y < height; y++)
1193 			{
1194 				row = row_pointers[y];
1195 				for (x = 0; x < width; x++)
1196 					outflat[((y * width) + x)] = (0xFF << 8) | row[x];
1197 			}
1198 		}
1199 		else
1200 		{
1201 			for (y = 0; y < height; y++)
1202 			{
1203 				row = row_pointers[y];
1204 				for (x = 0; x < width; x++)
1205 				{
1206 					png_bytep px = &(row[x * 4]);
1207 					UINT8 red = (UINT8)px[0];
1208 					UINT8 green = (UINT8)px[1];
1209 					UINT8 blue = (UINT8)px[2];
1210 					UINT8 alpha = (UINT8)px[3];
1211 
1212 					if (alpha)
1213 					{
1214 #ifdef PICTURE_PNG_USELOOKUP
1215 						UINT8 palidx = GetColorLUT(&png_colorlookup, red, green, blue);
1216 #else
1217 						UINT8 palidx = NearestColor(red, green, blue);
1218 #endif
1219 						outflat[((y * width) + x)] = (0xFF << 8) | palidx;
1220 					}
1221 					else
1222 						outflat[((y * width) + x)] = 0x0000;
1223 				}
1224 			}
1225 		}
1226 	}
1227 	else // 8bpp
1228 	{
1229 		UINT8 *outflat = (UINT8 *)flat;
1230 
1231 		if (palette)
1232 		{
1233 			for (y = 0; y < height; y++)
1234 			{
1235 				row = row_pointers[y];
1236 				for (x = 0; x < width; x++)
1237 					outflat[((y * width) + x)] = row[x];
1238 			}
1239 		}
1240 		else
1241 		{
1242 			for (y = 0; y < height; y++)
1243 			{
1244 				row = row_pointers[y];
1245 				for (x = 0; x < width; x++)
1246 				{
1247 					png_bytep px = &(row[x * 4]);
1248 					UINT8 red = (UINT8)px[0];
1249 					UINT8 green = (UINT8)px[1];
1250 					UINT8 blue = (UINT8)px[2];
1251 					UINT8 alpha = (UINT8)px[3];
1252 
1253 					if (alpha)
1254 					{
1255 #ifdef PICTURE_PNG_USELOOKUP
1256 						UINT8 palidx = GetColorLUT(&png_colorlookup, red, green, blue);
1257 #else
1258 						UINT8 palidx = NearestColor(red, green, blue);
1259 #endif
1260 						outflat[((y * width) + x)] = palidx;
1261 					}
1262 				}
1263 			}
1264 		}
1265 	}
1266 
1267 	// Free the row pointers that we allocated for libpng.
1268 	for (y = 0; y < height; y++)
1269 		free(row_pointers[y]);
1270 	free(row_pointers);
1271 
1272 	// But wait, there's more!
1273 	if (Picture_IsPatchFormat(outformat))
1274 	{
1275 		void *converted;
1276 		pictureformat_t informat = PICFMT_NONE;
1277 
1278 		// Figure out the format of the flat, from the bit depth of the output format
1279 		switch (outbpp)
1280 		{
1281 			case 32:
1282 				informat = PICFMT_FLAT32;
1283 				break;
1284 			case 16:
1285 				informat = PICFMT_FLAT16;
1286 				break;
1287 			default:
1288 				informat = PICFMT_FLAT;
1289 				break;
1290 		}
1291 
1292 		// Now, convert it!
1293 		converted = Picture_PatchConvert(informat, flat, outformat, insize, outsize, (INT16)width, (INT16)height, *leftoffset, *topoffset, flags);
1294 		Z_Free(flat);
1295 		return converted;
1296 	}
1297 
1298 	// Return the converted flat!
1299 	return flat;
1300 }
1301 
1302 /** Returns the dimensions of a PNG image, but doesn't perform any conversions.
1303   *
1304   * \param png The PNG image.
1305   * \param width A pointer to the input picture's width.
1306   * \param height A pointer to the input picture's height.
1307   * \param topoffset A pointer to the input picture's vertical offset.
1308   * \param leftoffset A pointer to the input picture's horizontal offset.
1309   * \param size The input picture's size.
1310   * \return True if reading the file succeeded, false if it failed.
1311   */
Picture_PNGDimensions(UINT8 * png,INT32 * width,INT32 * height,INT16 * topoffset,INT16 * leftoffset,size_t size)1312 boolean Picture_PNGDimensions(UINT8 *png, INT32 *width, INT32 *height, INT16 *topoffset, INT16 *leftoffset, size_t size)
1313 {
1314 	png_structp png_ptr;
1315 	png_infop png_info_ptr;
1316 	png_uint_32 w, h;
1317 	int bit_depth, color_type;
1318 #ifdef PNG_SETJMP_SUPPORTED
1319 #ifdef USE_FAR_KEYWORD
1320 	jmp_buf jmpbuf;
1321 #endif
1322 #endif
1323 
1324 	png_io_t png_io;
1325 	png_voidp *user_chunk_ptr;
1326 
1327 	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, PNG_error, PNG_warn);
1328 	if (!png_ptr)
1329 		I_Error("Picture_PNGDimensions: Couldn't initialize libpng!");
1330 
1331 	png_info_ptr = png_create_info_struct(png_ptr);
1332 	if (!png_info_ptr)
1333 	{
1334 		png_destroy_read_struct(&png_ptr, NULL, NULL);
1335 		I_Error("Picture_PNGDimensions: libpng couldn't allocate memory!");
1336 	}
1337 
1338 #ifdef USE_FAR_KEYWORD
1339 	if (setjmp(jmpbuf))
1340 #else
1341 	if (setjmp(png_jmpbuf(png_ptr)))
1342 #endif
1343 	{
1344 		png_destroy_read_struct(&png_ptr, &png_info_ptr, NULL);
1345 		I_Error("Picture_PNGDimensions: libpng load error!");
1346 	}
1347 #ifdef USE_FAR_KEYWORD
1348 	png_memcpy(png_jmpbuf(png_ptr), jmpbuf, sizeof jmp_buf);
1349 #endif
1350 
1351 	png_io.buffer = png;
1352 	png_io.size = size;
1353 	png_io.position = 0;
1354 	png_set_read_fn(png_ptr, &png_io, PNG_IOReader);
1355 
1356 	memset(&chunk, 0x00, sizeof(png_chunk_t));
1357 	chunkname = grAb_chunk; // I want to read a grAb chunk
1358 
1359 	user_chunk_ptr = png_get_user_chunk_ptr(png_ptr);
1360 	png_set_read_user_chunk_fn(png_ptr, user_chunk_ptr, PNG_ChunkReader);
1361 	png_set_keep_unknown_chunks(png_ptr, 2, chunkname, 1);
1362 
1363 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
1364 	png_set_user_limits(png_ptr, 2048, 2048);
1365 #endif
1366 
1367 	png_read_info(png_ptr, png_info_ptr);
1368 	png_get_IHDR(png_ptr, png_info_ptr, &w, &h, &bit_depth, &color_type, NULL, NULL, NULL);
1369 
1370 	// Read grAB chunk
1371 	if ((topoffset || leftoffset) && (chunk.data != NULL))
1372 	{
1373 		INT32 *offsets = (INT32 *)chunk.data;
1374 		// read left offset
1375 		if (leftoffset != NULL)
1376 			*leftoffset = (INT16)BIGENDIAN_LONG(*offsets);
1377 		offsets++;
1378 		// read top offset
1379 		if (topoffset != NULL)
1380 			*topoffset = (INT16)BIGENDIAN_LONG(*offsets);
1381 	}
1382 
1383 	png_destroy_read_struct(&png_ptr, &png_info_ptr, NULL);
1384 	if (chunk.data)
1385 		Z_Free(chunk.data);
1386 
1387 	*width = (INT32)w;
1388 	*height = (INT32)h;
1389 	return true;
1390 }
1391 #endif
1392 #endif
1393 
1394 //
1395 // R_ParseSpriteInfoFrame
1396 //
1397 // Parse a SPRTINFO frame.
1398 //
R_ParseSpriteInfoFrame(spriteinfo_t * info)1399 static void R_ParseSpriteInfoFrame(spriteinfo_t *info)
1400 {
1401 	char *sprinfoToken;
1402 	size_t sprinfoTokenLength;
1403 	char *frameChar = NULL;
1404 	UINT8 frameFrame = 0xFF;
1405 	INT16 frameXPivot = 0;
1406 	INT16 frameYPivot = 0;
1407 	rotaxis_t frameRotAxis = 0;
1408 
1409 	// Sprite identifier
1410 	sprinfoToken = M_GetToken(NULL);
1411 	if (sprinfoToken == NULL)
1412 	{
1413 		I_Error("Error parsing SPRTINFO lump: Unexpected end of file where sprite frame should be");
1414 	}
1415 	sprinfoTokenLength = strlen(sprinfoToken);
1416 	if (sprinfoTokenLength != 1)
1417 	{
1418 		I_Error("Error parsing SPRTINFO lump: Invalid frame \"%s\"",sprinfoToken);
1419 	}
1420 	else
1421 		frameChar = sprinfoToken;
1422 
1423 	frameFrame = R_Char2Frame(frameChar[0]);
1424 	Z_Free(sprinfoToken);
1425 
1426 	// Left Curly Brace
1427 	sprinfoToken = M_GetToken(NULL);
1428 	if (sprinfoToken == NULL)
1429 		I_Error("Error parsing SPRTINFO lump: Missing sprite info");
1430 	else
1431 	{
1432 		if (strcmp(sprinfoToken,"{")==0)
1433 		{
1434 			Z_Free(sprinfoToken);
1435 			sprinfoToken = M_GetToken(NULL);
1436 			if (sprinfoToken == NULL)
1437 			{
1438 				I_Error("Error parsing SPRTINFO lump: Unexpected end of file where sprite info should be");
1439 			}
1440 			while (strcmp(sprinfoToken,"}")!=0)
1441 			{
1442 				if (stricmp(sprinfoToken, "XPIVOT")==0)
1443 				{
1444 					Z_Free(sprinfoToken);
1445 					sprinfoToken = M_GetToken(NULL);
1446 					frameXPivot = atoi(sprinfoToken);
1447 				}
1448 				else if (stricmp(sprinfoToken, "YPIVOT")==0)
1449 				{
1450 					Z_Free(sprinfoToken);
1451 					sprinfoToken = M_GetToken(NULL);
1452 					frameYPivot = atoi(sprinfoToken);
1453 				}
1454 				else if (stricmp(sprinfoToken, "ROTAXIS")==0)
1455 				{
1456 					Z_Free(sprinfoToken);
1457 					sprinfoToken = M_GetToken(NULL);
1458 					if ((stricmp(sprinfoToken, "X")==0) || (stricmp(sprinfoToken, "XAXIS")==0) || (stricmp(sprinfoToken, "ROLL")==0))
1459 						frameRotAxis = ROTAXIS_X;
1460 					else if ((stricmp(sprinfoToken, "Y")==0) || (stricmp(sprinfoToken, "YAXIS")==0) || (stricmp(sprinfoToken, "PITCH")==0))
1461 						frameRotAxis = ROTAXIS_Y;
1462 					else if ((stricmp(sprinfoToken, "Z")==0) || (stricmp(sprinfoToken, "ZAXIS")==0) || (stricmp(sprinfoToken, "YAW")==0))
1463 						frameRotAxis = ROTAXIS_Z;
1464 				}
1465 				Z_Free(sprinfoToken);
1466 
1467 				sprinfoToken = M_GetToken(NULL);
1468 				if (sprinfoToken == NULL)
1469 				{
1470 					I_Error("Error parsing SPRTINFO lump: Unexpected end of file where sprite info or right curly brace should be");
1471 				}
1472 			}
1473 		}
1474 		Z_Free(sprinfoToken);
1475 	}
1476 
1477 	// set fields
1478 	info->pivot[frameFrame].x = frameXPivot;
1479 	info->pivot[frameFrame].y = frameYPivot;
1480 	info->pivot[frameFrame].rotaxis = frameRotAxis;
1481 }
1482 
1483 //
1484 // R_ParseSpriteInfo
1485 //
1486 // Parse a SPRTINFO lump.
1487 //
R_ParseSpriteInfo(boolean spr2)1488 static void R_ParseSpriteInfo(boolean spr2)
1489 {
1490 	spriteinfo_t *info;
1491 	char *sprinfoToken;
1492 	size_t sprinfoTokenLength;
1493 	char newSpriteName[5]; // no longer dynamically allocated
1494 	spritenum_t sprnum = NUMSPRITES;
1495 	playersprite_t spr2num = NUMPLAYERSPRITES;
1496 	INT32 i;
1497 	INT32 skinnumbers[MAXSKINS];
1498 	INT32 foundskins = 0;
1499 
1500 	// Sprite name
1501 	sprinfoToken = M_GetToken(NULL);
1502 	if (sprinfoToken == NULL)
1503 	{
1504 		I_Error("Error parsing SPRTINFO lump: Unexpected end of file where sprite name should be");
1505 	}
1506 	sprinfoTokenLength = strlen(sprinfoToken);
1507 	if (sprinfoTokenLength != 4)
1508 	{
1509 		I_Error("Error parsing SPRTINFO lump: Sprite name \"%s\" isn't 4 characters long",sprinfoToken);
1510 	}
1511 	else
1512 	{
1513 		memset(&newSpriteName, 0, 5);
1514 		M_Memcpy(newSpriteName, sprinfoToken, sprinfoTokenLength);
1515 		// ^^ we've confirmed that the token is == 4 characters so it will never overflow a 5 byte char buffer
1516 		strupr(newSpriteName); // Just do this now so we don't have to worry about it
1517 	}
1518 	Z_Free(sprinfoToken);
1519 
1520 	if (!spr2)
1521 	{
1522 		for (i = 0; i <= NUMSPRITES; i++)
1523 		{
1524 			if (i == NUMSPRITES)
1525 				I_Error("Error parsing SPRTINFO lump: Unknown sprite name \"%s\"", newSpriteName);
1526 			if (!memcmp(newSpriteName,sprnames[i],4))
1527 			{
1528 				sprnum = i;
1529 				break;
1530 			}
1531 		}
1532 	}
1533 	else
1534 	{
1535 		for (i = 0; i <= NUMPLAYERSPRITES; i++)
1536 		{
1537 			if (i == NUMPLAYERSPRITES)
1538 				I_Error("Error parsing SPRTINFO lump: Unknown sprite2 name \"%s\"", newSpriteName);
1539 			if (!memcmp(newSpriteName,spr2names[i],4))
1540 			{
1541 				spr2num = i;
1542 				break;
1543 			}
1544 		}
1545 	}
1546 
1547 	// allocate a spriteinfo
1548 	info = Z_Calloc(sizeof(spriteinfo_t), PU_STATIC, NULL);
1549 	info->available = true;
1550 
1551 	// Left Curly Brace
1552 	sprinfoToken = M_GetToken(NULL);
1553 	if (sprinfoToken == NULL)
1554 	{
1555 		I_Error("Error parsing SPRTINFO lump: Unexpected end of file where open curly brace for sprite \"%s\" should be",newSpriteName);
1556 	}
1557 	if (strcmp(sprinfoToken,"{")==0)
1558 	{
1559 		Z_Free(sprinfoToken);
1560 		sprinfoToken = M_GetToken(NULL);
1561 		if (sprinfoToken == NULL)
1562 		{
1563 			I_Error("Error parsing SPRTINFO lump: Unexpected end of file where definition for sprite \"%s\" should be",newSpriteName);
1564 		}
1565 		while (strcmp(sprinfoToken,"}")!=0)
1566 		{
1567 			if (stricmp(sprinfoToken, "SKIN")==0)
1568 			{
1569 				INT32 skinnum;
1570 				char *skinName = NULL;
1571 				if (!spr2)
1572 					I_Error("Error parsing SPRTINFO lump: \"SKIN\" token found outside of a sprite2 definition");
1573 
1574 				Z_Free(sprinfoToken);
1575 
1576 				// Skin name
1577 				sprinfoToken = M_GetToken(NULL);
1578 				if (sprinfoToken == NULL)
1579 				{
1580 					I_Error("Error parsing SPRTINFO lump: Unexpected end of file where skin frame should be");
1581 				}
1582 
1583 				// copy skin name yada yada
1584 				sprinfoTokenLength = strlen(sprinfoToken);
1585 				skinName = (char *)Z_Malloc((sprinfoTokenLength+1)*sizeof(char),PU_STATIC,NULL);
1586 				M_Memcpy(skinName,sprinfoToken,sprinfoTokenLength*sizeof(char));
1587 				skinName[sprinfoTokenLength] = '\0';
1588 				strlwr(skinName);
1589 				Z_Free(sprinfoToken);
1590 
1591 				skinnum = R_SkinAvailable(skinName);
1592 				if (skinnum == -1)
1593 					I_Error("Error parsing SPRTINFO lump: Unknown skin \"%s\"", skinName);
1594 
1595 				skinnumbers[foundskins] = skinnum;
1596 				foundskins++;
1597 			}
1598 			else if (stricmp(sprinfoToken, "FRAME")==0)
1599 			{
1600 				R_ParseSpriteInfoFrame(info);
1601 				Z_Free(sprinfoToken);
1602 				if (spr2)
1603 				{
1604 					if (!foundskins)
1605 						I_Error("Error parsing SPRTINFO lump: No skins specified in this sprite2 definition");
1606 					for (i = 0; i < foundskins; i++)
1607 					{
1608 						size_t skinnum = skinnumbers[i];
1609 						skin_t *skin = &skins[skinnum];
1610 						spriteinfo_t *sprinfo = skin->sprinfo;
1611 						M_Memcpy(&sprinfo[spr2num], info, sizeof(spriteinfo_t));
1612 					}
1613 				}
1614 				else
1615 					M_Memcpy(&spriteinfo[sprnum], info, sizeof(spriteinfo_t));
1616 			}
1617 			else
1618 			{
1619 				I_Error("Error parsing SPRTINFO lump: Unknown keyword \"%s\" in sprite %s",sprinfoToken,newSpriteName);
1620 			}
1621 
1622 			sprinfoToken = M_GetToken(NULL);
1623 			if (sprinfoToken == NULL)
1624 			{
1625 				I_Error("Error parsing SPRTINFO lump: Unexpected end of file where sprite info or right curly brace for sprite \"%s\" should be",newSpriteName);
1626 			}
1627 		}
1628 	}
1629 	else
1630 	{
1631 		I_Error("Error parsing SPRTINFO lump: Expected \"{\" for sprite \"%s\", got \"%s\"",newSpriteName,sprinfoToken);
1632 	}
1633 	Z_Free(sprinfoToken);
1634 	Z_Free(info);
1635 }
1636 
1637 //
1638 // R_ParseSPRTINFOLump
1639 //
1640 // Read a SPRTINFO lump.
1641 //
R_ParseSPRTINFOLump(UINT16 wadNum,UINT16 lumpNum)1642 void R_ParseSPRTINFOLump(UINT16 wadNum, UINT16 lumpNum)
1643 {
1644 	char *sprinfoLump;
1645 	size_t sprinfoLumpLength;
1646 	char *sprinfoText;
1647 	char *sprinfoToken;
1648 
1649 	// Since lumps AREN'T \0-terminated like I'd assumed they should be, I'll
1650 	// need to make a space of memory where I can ensure that it will terminate
1651 	// correctly. Start by loading the relevant data from the WAD.
1652 	sprinfoLump = (char *)W_CacheLumpNumPwad(wadNum, lumpNum, PU_STATIC);
1653 	// If that didn't exist, we have nothing to do here.
1654 	if (sprinfoLump == NULL) return;
1655 	// If we're still here, then it DOES exist; figure out how long it is, and allot memory accordingly.
1656 	sprinfoLumpLength = W_LumpLengthPwad(wadNum, lumpNum);
1657 	sprinfoText = (char *)Z_Malloc((sprinfoLumpLength+1)*sizeof(char),PU_STATIC,NULL);
1658 	// Now move the contents of the lump into this new location.
1659 	memmove(sprinfoText,sprinfoLump,sprinfoLumpLength);
1660 	// Make damn well sure the last character in our new memory location is \0.
1661 	sprinfoText[sprinfoLumpLength] = '\0';
1662 	// Finally, free up the memory from the first data load, because we really
1663 	// don't need it.
1664 	Z_Free(sprinfoLump);
1665 
1666 	sprinfoToken = M_GetToken(sprinfoText);
1667 	while (sprinfoToken != NULL)
1668 	{
1669 		if (!stricmp(sprinfoToken, "SPRITE"))
1670 			R_ParseSpriteInfo(false);
1671 		else if (!stricmp(sprinfoToken, "SPRITE2"))
1672 			R_ParseSpriteInfo(true);
1673 		else
1674 			I_Error("Error parsing SPRTINFO lump: Unknown keyword \"%s\"", sprinfoToken);
1675 		Z_Free(sprinfoToken);
1676 		sprinfoToken = M_GetToken(NULL);
1677 	}
1678 	Z_Free((void *)sprinfoText);
1679 }
1680 
1681 //
1682 // R_LoadSpriteInfoLumps
1683 //
1684 // Load and read every SPRTINFO lump from the specified file.
1685 //
R_LoadSpriteInfoLumps(UINT16 wadnum,UINT16 numlumps)1686 void R_LoadSpriteInfoLumps(UINT16 wadnum, UINT16 numlumps)
1687 {
1688 	lumpinfo_t *lumpinfo = wadfiles[wadnum]->lumpinfo;
1689 	UINT16 i;
1690 	char *name;
1691 
1692 	for (i = 0; i < numlumps; i++, lumpinfo++)
1693 	{
1694 		name = lumpinfo->name;
1695 		// Load SPRTINFO and SPR_ lumps as SpriteInfo
1696 		if (!memcmp(name, "SPRTINFO", 8) || !memcmp(name, "SPR_", 4))
1697 			R_ParseSPRTINFOLump(wadnum, i);
1698 	}
1699 }
1700