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