r1132 - in trunk: . code/qcommon code/renderer
DONOTREPLY at icculus.org
DONOTREPLY at icculus.org
Thu Aug 23 13:23:16 EDT 2007
Author: tma
Date: 2007-08-23 13:23:15 -0400 (Thu, 23 Aug 2007)
New Revision: 1132
Added:
trunk/code/qcommon/puff.c
trunk/code/qcommon/puff.h
Modified:
trunk/Makefile
trunk/README
trunk/code/renderer/tr_image.c
Log:
* PNG support from Joerg Dietrich <dietrich_joerg at t-online.de>
* Cleanup of tabulation in R_LoadImage
Modified: trunk/Makefile
===================================================================
--- trunk/Makefile 2007-08-23 15:23:43 UTC (rev 1131)
+++ trunk/Makefile 2007-08-23 17:23:15 UTC (rev 1132)
@@ -945,6 +945,7 @@
$(B)/client/q_shared.o \
\
$(B)/client/unzip.o \
+ $(B)/client/puff.o \
$(B)/client/vm.o \
$(B)/client/vm_interpreted.o \
\
Modified: trunk/README
===================================================================
--- trunk/README 2007-08-23 15:23:43 UTC (rev 1131)
+++ trunk/README 2007-08-23 17:23:15 UTC (rev 1132)
@@ -6,7 +6,7 @@
| |_| |
| |
`---------- http://ioquake3.org --------'
-
+
The intent of this project is to provide a baseline Quake 3 which may be used
for further development. Some of the major features currently implemented are:
@@ -27,6 +27,7 @@
* HTTP/FTP download redirection (using cURL)
* Multiuser support on Windows systems (user specific game data
is stored in "%APPDATA%\Quake3")
+ * PNG support
* Many, many bug fixes
The map editor and associated compiling tools are not included. We suggest you
@@ -62,7 +63,7 @@
XCode 2.2 and newer.
2. Change to the directory containing this README file.
3. Run './make-macosx-ub.sh'
- 4. Copy the resulting ioquake3.app in /build/release-darwin-ub to your
+ 4. Copy the resulting ioquake3.app in /build/release-darwin-ub to your
/Applications/ioquake3 folder.
Installation, for *nix
@@ -154,7 +155,7 @@
cl_guidServerUniq - makes cl_guid unique for each server
cl_cURLLib - filename of cURL library to load
sv_dlURL - the base of the HTTP or FTP site that
- holds custom pk3 files for your server
+ holds custom pk3 files for your server
New commands
video [filename] - start video capture (use with demo command)
@@ -215,7 +216,7 @@
If cl_guidServerUniq is non-zero (the default), then this value is also
pseudo-unique for each server a client connects to (based on IP:PORT of
- the server).
+ the server).
The purpose of cl_guid is to add an identifier for each player on
a server. This value can be reset by the client at any time so it's not
@@ -223,7 +224,7 @@
your mod's game code:
1) improve logging to allow statistical tools to index players by more
than just name
- 2) granting some weak admin rights to players without requiring passwords
+ 2) granting some weak admin rights to players without requiring passwords
Using HTTP/FTP Download Support (Server)
You can enable redirected downloads on your server even if it's not
@@ -248,7 +249,7 @@
Server operators who are concerned about potential "leeching" from their
HTTP servers from other ioquake3 servers can make use of the HTTP_REFERER
that ioquake3 sets which is "ioQ3://{SERVER_IP}:{SERVER_PORT}". For,
- example, Apache's mod_rewrite can restrict access based on HTTP_REFERER.
+ example, Apache's mod_rewrite can restrict access based on HTTP_REFERER.
Using HTTP/FTP Download Support (Client)
Simply setting cl_allowDownload to 1 will enable HTTP/FTP downloads
@@ -261,7 +262,7 @@
When ioquake3 is built with USE_CURL_DLOPEN=1 (default on some platforms),
it will use the value of the cvar cl_cURLLib as the filename of the cURL
- library to dynamically load.
+ library to dynamically load.
Multiuser Support on Windows systems
On Windows, all user specific files such as autogenerated configuration,
@@ -271,19 +272,19 @@
On NT-based such as Windows XP, this is usually a directory named:
"C:\Documents and Settings\%USERNAME%\Application Data\Quake3\"
- Windows 95, Windows 98, and Windows ME will use a directory like:
+ Windows 95, Windows 98, and Windows ME will use a directory like:
"C:\Windows\Application Data\Quake3"
in single-user mode, or:
"C:\Windows\Profiles\%USERNAME%\Application Data\Quake3"
- if multiple logins have been enabled.
+ if multiple logins have been enabled.
In order to access this directory more easily, the installer may create a
Shortcut which has its target set to:
"%APPDATA%\Quake3\"
This Shortcut would work for all users on the system regardless of the
- locale settings. Unfortunately, this environment variable is only
+ locale settings. Unfortunately, this environment variable is only
present on Windows NT based systems.
-
+
You can revert to the old single-user behaviour by setting the fs_homepath
cvar to the directory where ioquake3 is installed. For example:
ioquake3.exe +set fs_homepath "c:\ioquake3"
@@ -294,7 +295,7 @@
keyboard behaviour than the original Quake3 clients.
* "Caps Lock" and "Num Lock" can not be used as normal binds since they
- do not send a KEYUP event until the key is pressed again.
+ do not send a KEYUP event until the key is pressed again.
* SDL > 1.2.9 does not support disabling "Dead Key" recognition.
In order to send "Dead Key" characters (e.g. ~, ', `, and ^), you
@@ -311,6 +312,12 @@
annoying to use on many non-US keyboards. In response, an additional
toggleConsole bind has been added on the key combination Shift-Esc.
+PNG support
+ ioquake3 supports the use of PNG (Portable Network Graphic) images as
+ textures. It should be noted that the use of such images in a maps will
+ result in missing placeholder textures where the map is used with the id
+ Quake 3 client or earlier versions of ioquake3.
+
------------------------------------------------------------- Contributing -----
Please send all patches to bugzilla (https://bugzilla.icculus.org), or join the
Added: trunk/code/qcommon/puff.c
===================================================================
--- trunk/code/qcommon/puff.c (rev 0)
+++ trunk/code/qcommon/puff.c 2007-08-23 17:23:15 UTC (rev 1132)
@@ -0,0 +1,758 @@
+/*
+ * This is a modified version of Mark Adlers work,
+ * see below for the original copyright.
+ * 2006 - Joerg Dietrich <dietrich_joerg at gmx.de>
+ */
+
+/*
+ * puff.c
+ * Copyright (C) 2002-2004 Mark Adler
+ * For conditions of distribution and use, see copyright notice in puff.h
+ * version 1.8, 9 Jan 2004
+ *
+ * puff.c is a simple inflate written to be an unambiguous way to specify the
+ * deflate format. It is not written for speed but rather simplicity. As a
+ * side benefit, this code might actually be useful when small code is more
+ * important than speed, such as bootstrap applications. For typical deflate
+ * data, zlib's inflate() is about four times as fast as puff(). zlib's
+ * inflate compiles to around 20K on my machine, whereas puff.c compiles to
+ * around 4K on my machine (a PowerPC using GNU cc). If the faster decode()
+ * function here is used, then puff() is only twice as slow as zlib's
+ * inflate().
+ *
+ * All dynamically allocated memory comes from the stack. The stack required
+ * is less than 2K bytes. This code is compatible with 16-bit int's and
+ * assumes that long's are at least 32 bits. puff.c uses the short data type,
+ * assumed to be 16 bits, for arrays in order to to conserve memory. The code
+ * works whether integers are stored big endian or little endian.
+ *
+ * In the comments below are "Format notes" that describe the inflate process
+ * and document some of the less obvious aspects of the format. This source
+ * code is meant to supplement RFC 1951, which formally describes the deflate
+ * format:
+ *
+ * http://www.zlib.org/rfc-deflate.html
+ */
+
+/*
+ * Change history:
+ *
+ * 1.0 10 Feb 2002 - First version
+ * 1.1 17 Feb 2002 - Clarifications of some comments and notes
+ * - Update puff() dest and source pointers on negative
+ * errors to facilitate debugging deflators
+ * - Remove longest from struct huffman -- not needed
+ * - Simplify offs[] index in construct()
+ * - Add input size and checking, using longjmp() to
+ * maintain easy readability
+ * - Use short data type for large arrays
+ * - Use pointers instead of long to specify source and
+ * destination sizes to avoid arbitrary 4 GB limits
+ * 1.2 17 Mar 2002 - Add faster version of decode(), doubles speed (!),
+ * but leave simple version for readabilty
+ * - Make sure invalid distances detected if pointers
+ * are 16 bits
+ * - Fix fixed codes table error
+ * - Provide a scanning mode for determining size of
+ * uncompressed data
+ * 1.3 20 Mar 2002 - Go back to lengths for puff() parameters [Jean-loup]
+ * - Add a puff.h file for the interface
+ * - Add braces in puff() for else do [Jean-loup]
+ * - Use indexes instead of pointers for readability
+ * 1.4 31 Mar 2002 - Simplify construct() code set check
+ * - Fix some comments
+ * - Add FIXLCODES #define
+ * 1.5 6 Apr 2002 - Minor comment fixes
+ * 1.6 7 Aug 2002 - Minor format changes
+ * 1.7 3 Mar 2003 - Added test code for distribution
+ * - Added zlib-like license
+ * 1.8 9 Jan 2004 - Added some comments on no distance codes case
+ */
+
+#include <setjmp.h> /* for setjmp(), longjmp(), and jmp_buf */
+#include "puff.h" /* prototype for puff() */
+
+#define local static /* for local function definitions */
+
+/*
+ * Maximums for allocations and loops. It is not useful to change these --
+ * they are fixed by the deflate format.
+ */
+#define MAXBITS 15 /* maximum bits in a code */
+#define MAXLCODES 286 /* maximum number of literal/length codes */
+#define MAXDCODES 30 /* maximum number of distance codes */
+#define MAXCODES (MAXLCODES+MAXDCODES) /* maximum codes lengths to read */
+#define FIXLCODES 288 /* number of fixed literal/length codes */
+
+/* input and output state */
+struct state {
+ /* output state */
+ uint8_t *out; /* output buffer */
+ uint32_t outlen; /* available space at out */
+ uint32_t outcnt; /* bytes written to out so far */
+
+ /* input state */
+ uint8_t *in; /* input buffer */
+ uint32_t inlen; /* available input at in */
+ uint32_t incnt; /* bytes read so far */
+ int32_t bitbuf; /* bit buffer */
+ int32_t bitcnt; /* number of bits in bit buffer */
+
+ /* input limit error return state for bits() and decode() */
+ jmp_buf env;
+};
+
+/*
+ * Return need bits from the input stream. This always leaves less than
+ * eight bits in the buffer. bits() works properly for need == 0.
+ *
+ * Format notes:
+ *
+ * - Bits are stored in bytes from the least significant bit to the most
+ * significant bit. Therefore bits are dropped from the bottom of the bit
+ * buffer, using shift right, and new bytes are appended to the top of the
+ * bit buffer, using shift left.
+ */
+local int32_t bits(struct state *s, int32_t need)
+{
+ int32_t val; /* bit accumulator (can use up to 20 bits) */
+
+ /* load at least need bits into val */
+ val = s->bitbuf;
+ while (s->bitcnt < need) {
+ if (s->incnt == s->inlen) longjmp(s->env, 1); /* out of input */
+ val |= (int32_t)(s->in[s->incnt++]) << s->bitcnt; /* load eight bits */
+ s->bitcnt += 8;
+ }
+
+ /* drop need bits and update buffer, always zero to seven bits left */
+ s->bitbuf = (int32_t)(val >> need);
+ s->bitcnt -= need;
+
+ /* return need bits, zeroing the bits above that */
+ return (int32_t)(val & ((1L << need) - 1));
+}
+
+/*
+ * Process a stored block.
+ *
+ * Format notes:
+ *
+ * - After the two-bit stored block type (00), the stored block length and
+ * stored bytes are byte-aligned for fast copying. Therefore any leftover
+ * bits in the byte that has the last bit of the type, as many as seven, are
+ * discarded. The value of the discarded bits are not defined and should not
+ * be checked against any expectation.
+ *
+ * - The second inverted copy of the stored block length does not have to be
+ * checked, but it's probably a good idea to do so anyway.
+ *
+ * - A stored block can have zero length. This is sometimes used to byte-align
+ * subsets of the compressed data for random access or partial recovery.
+ */
+local int32_t stored(struct state *s)
+{
+ uint32_t len; /* length of stored block */
+
+ /* discard leftover bits from current byte (assumes s->bitcnt < 8) */
+ s->bitbuf = 0;
+ s->bitcnt = 0;
+
+ /* get length and check against its one's complement */
+ if (s->incnt + 4 > s->inlen) return 2; /* not enough input */
+ len = s->in[s->incnt++];
+ len |= s->in[s->incnt++] << 8;
+ if (s->in[s->incnt++] != (~len & 0xff) ||
+ s->in[s->incnt++] != ((~len >> 8) & 0xff))
+ return -2; /* didn't match complement! */
+
+ /* copy len bytes from in to out */
+ if (s->incnt + len > s->inlen) return 2; /* not enough input */
+ if (s->out != NULL) {
+ if (s->outcnt + len > s->outlen)
+ return 1; /* not enough output space */
+ while (len--)
+ s->out[s->outcnt++] = s->in[s->incnt++];
+ }
+ else { /* just scanning */
+ s->outcnt += len;
+ s->incnt += len;
+ }
+
+ /* done with a valid stored block */
+ return 0;
+}
+
+/*
+ * Huffman code decoding tables. count[1..MAXBITS] is the number of symbols of
+ * each length, which for a canonical code are stepped through in order.
+ * symbol[] are the symbol values in canonical order, where the number of
+ * entries is the sum of the counts in count[]. The decoding process can be
+ * seen in the function decode() below.
+ */
+struct huffman {
+ int16_t *count; /* number of symbols of each length */
+ int16_t *symbol; /* canonically ordered symbols */
+};
+
+/*
+ * Decode a code from the stream s using huffman table h. Return the symbol or
+ * a negative value if there is an error. If all of the lengths are zero, i.e.
+ * an empty code, or if the code is incomplete and an invalid code is received,
+ * then -9 is returned after reading MAXBITS bits.
+ *
+ * Format notes:
+ *
+ * - The codes as stored in the compressed data are bit-reversed relative to
+ * a simple integer ordering of codes of the same lengths. Hence below the
+ * bits are pulled from the compressed data one at a time and used to
+ * build the code value reversed from what is in the stream in order to
+ * permit simple integer comparisons for decoding. A table-based decoding
+ * scheme (as used in zlib) does not need to do this reversal.
+ *
+ * - The first code for the shortest length is all zeros. Subsequent codes of
+ * the same length are simply integer increments of the previous code. When
+ * moving up a length, a zero bit is appended to the code. For a complete
+ * code, the last code of the longest length will be all ones.
+ *
+ * - Incomplete codes are handled by this decoder, since they are permitted
+ * in the deflate format. See the format notes for fixed() and dynamic().
+ */
+local int32_t decode(struct state *s, struct huffman *h)
+{
+ int32_t len; /* current number of bits in code */
+ int32_t code; /* len bits being decoded */
+ int32_t first; /* first code of length len */
+ int32_t count; /* number of codes of length len */
+ int32_t index; /* index of first code of length len in symbol table */
+ int32_t bitbuf; /* bits from stream */
+ int32_t left; /* bits left in next or left to process */
+ int16_t *next; /* next number of codes */
+
+ bitbuf = s->bitbuf;
+ left = s->bitcnt;
+ code = first = index = 0;
+ len = 1;
+ next = h->count + 1;
+ while (1) {
+ while (left--) {
+ code |= bitbuf & 1;
+ bitbuf >>= 1;
+ count = *next++;
+ if (code < first + count) { /* if length len, return symbol */
+ s->bitbuf = bitbuf;
+ s->bitcnt = (s->bitcnt - len) & 7;
+ return h->symbol[index + (code - first)];
+ }
+ index += count; /* else update for next length */
+ first += count;
+ first <<= 1;
+ code <<= 1;
+ len++;
+ }
+ left = (MAXBITS+1) - len;
+ if (left == 0) break;
+ if (s->incnt == s->inlen) longjmp(s->env, 1); /* out of input */
+ bitbuf = s->in[s->incnt++];
+ if (left > 8) left = 8;
+ }
+ return -9; /* ran out of codes */
+}
+
+/*
+ * Given the list of code lengths length[0..n-1] representing a canonical
+ * Huffman code for n symbols, construct the tables required to decode those
+ * codes. Those tables are the number of codes of each length, and the symbols
+ * sorted by length, retaining their original order within each length. The
+ * return value is zero for a complete code set, negative for an over-
+ * subscribed code set, and positive for an incomplete code set. The tables
+ * can be used if the return value is zero or positive, but they cannot be used
+ * if the return value is negative. If the return value is zero, it is not
+ * possible for decode() using that table to return an error--any stream of
+ * enough bits will resolve to a symbol. If the return value is positive, then
+ * it is possible for decode() using that table to return an error for received
+ * codes past the end of the incomplete lengths.
+ *
+ * Not used by decode(), but used for error checking, h->count[0] is the number
+ * of the n symbols not in the code. So n - h->count[0] is the number of
+ * codes. This is useful for checking for incomplete codes that have more than
+ * one symbol, which is an error in a dynamic block.
+ *
+ * Assumption: for all i in 0..n-1, 0 <= length[i] <= MAXBITS
+ * This is assured by the construction of the length arrays in dynamic() and
+ * fixed() and is not verified by construct().
+ *
+ * Format notes:
+ *
+ * - Permitted and expected examples of incomplete codes are one of the fixed
+ * codes and any code with a single symbol which in deflate is coded as one
+ * bit instead of zero bits. See the format notes for fixed() and dynamic().
+ *
+ * - Within a given code length, the symbols are kept in ascending order for
+ * the code bits definition.
+ */
+local int32_t construct(struct huffman *h, int16_t *length, int32_t n)
+{
+ int32_t symbol; /* current symbol when stepping through length[] */
+ int32_t len; /* current length when stepping through h->count[] */
+ int32_t left; /* number of possible codes left of current length */
+ int16_t offs[MAXBITS+1]; /* offsets in symbol table for each length */
+
+ /* count number of codes of each length */
+ for (len = 0; len <= MAXBITS; len++)
+ h->count[len] = 0;
+ for (symbol = 0; symbol < n; symbol++)
+ (h->count[length[symbol]])++; /* assumes lengths are within bounds */
+ if (h->count[0] == n) /* no codes! */
+ return 0; /* complete, but decode() will fail */
+
+ /* check for an over-subscribed or incomplete set of lengths */
+ left = 1; /* one possible code of zero length */
+ for (len = 1; len <= MAXBITS; len++) {
+ left <<= 1; /* one more bit, double codes left */
+ left -= h->count[len]; /* deduct count from possible codes */
+ if (left < 0) return left; /* over-subscribed--return negative */
+ } /* left > 0 means incomplete */
+
+ /* generate offsets into symbol table for each length for sorting */
+ offs[1] = 0;
+ for (len = 1; len < MAXBITS; len++)
+ offs[len + 1] = offs[len] + h->count[len];
+
+ /*
+ * put symbols in table sorted by length, by symbol order within each
+ * length
+ */
+ for (symbol = 0; symbol < n; symbol++)
+ if (length[symbol] != 0)
+ h->symbol[offs[length[symbol]]++] = symbol;
+
+ /* return zero for complete set, positive for incomplete set */
+ return left;
+}
+
+/*
+ * Decode literal/length and distance codes until an end-of-block code.
+ *
+ * Format notes:
+ *
+ * - Compressed data that is after the block type if fixed or after the code
+ * description if dynamic is a combination of literals and length/distance
+ * pairs terminated by and end-of-block code. Literals are simply Huffman
+ * coded bytes. A length/distance pair is a coded length followed by a
+ * coded distance to represent a string that occurs earlier in the
+ * uncompressed data that occurs again at the current location.
+ *
+ * - Literals, lengths, and the end-of-block code are combined into a single
+ * code of up to 286 symbols. They are 256 literals (0..255), 29 length
+ * symbols (257..285), and the end-of-block symbol (256).
+ *
+ * - There are 256 possible lengths (3..258), and so 29 symbols are not enough
+ * to represent all of those. Lengths 3..10 and 258 are in fact represented
+ * by just a length symbol. Lengths 11..257 are represented as a symbol and
+ * some number of extra bits that are added as an integer to the base length
+ * of the length symbol. The number of extra bits is determined by the base
+ * length symbol. These are in the static arrays below, lens[] for the base
+ * lengths and lext[] for the corresponding number of extra bits.
+ *
+ * - The reason that 258 gets its own symbol is that the longest length is used
+ * often in highly redundant files. Note that 258 can also be coded as the
+ * base value 227 plus the maximum extra value of 31. While a good deflate
+ * should never do this, it is not an error, and should be decoded properly.
+ *
+ * - If a length is decoded, including its extra bits if any, then it is
+ * followed a distance code. There are up to 30 distance symbols. Again
+ * there are many more possible distances (1..32768), so extra bits are added
+ * to a base value represented by the symbol. The distances 1..4 get their
+ * own symbol, but the rest require extra bits. The base distances and
+ * corresponding number of extra bits are below in the static arrays dist[]
+ * and dext[].
+ *
+ * - Literal bytes are simply written to the output. A length/distance pair is
+ * an instruction to copy previously uncompressed bytes to the output. The
+ * copy is from distance bytes back in the output stream, copying for length
+ * bytes.
+ *
+ * - Distances pointing before the beginning of the output data are not
+ * permitted.
+ *
+ * - Overlapped copies, where the length is greater than the distance, are
+ * allowed and common. For example, a distance of one and a length of 258
+ * simply copies the last byte 258 times. A distance of four and a length of
+ * twelve copies the last four bytes three times. A simple forward copy
+ * ignoring whether the length is greater than the distance or not implements
+ * this correctly. You should not use memcpy() since its behavior is not
+ * defined for overlapped arrays. You should not use memmove() or bcopy()
+ * since though their behavior -is- defined for overlapping arrays, it is
+ * defined to do the wrong thing in this case.
+ */
+local int32_t codes(struct state *s,
+ struct huffman *lencode,
+ struct huffman *distcode)
+{
+ int32_t symbol; /* decoded symbol */
+ int32_t len; /* length for copy */
+ uint32_t dist; /* distance for copy */
+ static const int16_t lens[29] = { /* Size base for length codes 257..285 */
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258};
+ static const int16_t lext[29] = { /* Extra bits for length codes 257..285 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
+ 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0};
+ static const int16_t dists[30] = { /* Offset base for distance codes 0..29 */
+ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+ 8193, 12289, 16385, 24577};
+ static const int16_t dext[30] = { /* Extra bits for distance codes 0..29 */
+ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
+ 7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
+ 12, 12, 13, 13};
+
+ /* decode literals and length/distance pairs */
+ do {
+ symbol = decode(s, lencode);
+ if (symbol < 0) return symbol; /* invalid symbol */
+ if (symbol < 256) { /* literal: symbol is the byte */
+ /* write out the literal */
+ if (s->out != NULL) {
+ if (s->outcnt == s->outlen) return 1;
+ s->out[s->outcnt] = symbol;
+ }
+ s->outcnt++;
+ }
+ else if (symbol > 256) { /* length */
+ /* get and compute length */
+ symbol -= 257;
+ if (symbol >= 29) return -9; /* invalid fixed code */
+ len = lens[symbol] + bits(s, lext[symbol]);
+
+ /* get and check distance */
+ symbol = decode(s, distcode);
+ if (symbol < 0) return symbol; /* invalid symbol */
+ dist = dists[symbol] + bits(s, dext[symbol]);
+ if (dist > s->outcnt)
+ return -10; /* distance too far back */
+
+ /* copy length bytes from distance bytes back */
+ if (s->out != NULL) {
+ if (s->outcnt + len > s->outlen) return 1;
+ while (len--) {
+ s->out[s->outcnt] = s->out[s->outcnt - dist];
+ s->outcnt++;
+ }
+ }
+ else
+ s->outcnt += len;
+ }
+ } while (symbol != 256); /* end of block symbol */
+
+ /* done with a valid fixed or dynamic block */
+ return 0;
+}
+
+/*
+ * Process a fixed codes block.
+ *
+ * Format notes:
+ *
+ * - This block type can be useful for compressing small amounts of data for
+ * which the size of the code descriptions in a dynamic block exceeds the
+ * benefit of custom codes for that block. For fixed codes, no bits are
+ * spent on code descriptions. Instead the code lengths for literal/length
+ * codes and distance codes are fixed. The specific lengths for each symbol
+ * can be seen in the "for" loops below.
+ *
+ * - The literal/length code is complete, but has two symbols that are invalid
+ * and should result in an error if received. This cannot be implemented
+ * simply as an incomplete code since those two symbols are in the "middle"
+ * of the code. They are eight bits long and the longest literal/length\
+ * code is nine bits. Therefore the code must be constructed with those
+ * symbols, and the invalid symbols must be detected after decoding.
+ *
+ * - The fixed distance codes also have two invalid symbols that should result
+ * in an error if received. Since all of the distance codes are the same
+ * length, this can be implemented as an incomplete code. Then the invalid
+ * codes are detected while decoding.
+ */
+local int32_t fixed(struct state *s)
+{
+ static int32_t virgin = 1;
+ static int16_t lencnt[MAXBITS+1], lensym[FIXLCODES];
+ static int16_t distcnt[MAXBITS+1], distsym[MAXDCODES];
+ static struct huffman lencode = {lencnt, lensym};
+ static struct huffman distcode = {distcnt, distsym};
+
+ /* build fixed huffman tables if first call (may not be thread safe) */
+ if (virgin) {
+ int32_t symbol;
+ int16_t lengths[FIXLCODES];
+
+ /* literal/length table */
+ for (symbol = 0; symbol < 144; symbol++)
+ lengths[symbol] = 8;
+ for (; symbol < 256; symbol++)
+ lengths[symbol] = 9;
+ for (; symbol < 280; symbol++)
+ lengths[symbol] = 7;
+ for (; symbol < FIXLCODES; symbol++)
+ lengths[symbol] = 8;
+ construct(&lencode, lengths, FIXLCODES);
+
+ /* distance table */
+ for (symbol = 0; symbol < MAXDCODES; symbol++)
+ lengths[symbol] = 5;
+ construct(&distcode, lengths, MAXDCODES);
+
+ /* do this just once */
+ virgin = 0;
+ }
+
+ /* decode data until end-of-block code */
+ return codes(s, &lencode, &distcode);
+}
+
+/*
+ * Process a dynamic codes block.
+ *
+ * Format notes:
+ *
+ * - A dynamic block starts with a description of the literal/length and
+ * distance codes for that block. New dynamic blocks allow the compressor to
+ * rapidly adapt to changing data with new codes optimized for that data.
+ *
+ * - The codes used by the deflate format are "canonical", which means that
+ * the actual bits of the codes are generated in an unambiguous way simply
+ * from the number of bits in each code. Therefore the code descriptions
+ * are simply a list of code lengths for each symbol.
+ *
+ * - The code lengths are stored in order for the symbols, so lengths are
+ * provided for each of the literal/length symbols, and for each of the
+ * distance symbols.
+ *
+ * - If a symbol is not used in the block, this is represented by a zero as
+ * as the code length. This does not mean a zero-length code, but rather
+ * that no code should be created for this symbol. There is no way in the
+ * deflate format to represent a zero-length code.
+ *
+ * - The maximum number of bits in a code is 15, so the possible lengths for
+ * any code are 1..15.
+ *
+ * - The fact that a length of zero is not permitted for a code has an
+ * interesting consequence. Normally if only one symbol is used for a given
+ * code, then in fact that code could be represented with zero bits. However
+ * in deflate, that code has to be at least one bit. So for example, if
+ * only a single distance base symbol appears in a block, then it will be
+ * represented by a single code of length one, in particular one 0 bit. This
+ * is an incomplete code, since if a 1 bit is received, it has no meaning,
+ * and should result in an error. So incomplete distance codes of one symbol
+ * should be permitted, and the receipt of invalid codes should be handled.
+ *
+ * - It is also possible to have a single literal/length code, but that code
+ * must be the end-of-block code, since every dynamic block has one. This
+ * is not the most efficient way to create an empty block (an empty fixed
+ * block is fewer bits), but it is allowed by the format. So incomplete
+ * literal/length codes of one symbol should also be permitted.
+ *
+ * - If there are only literal codes and no lengths, then there are no distance
+ * codes. This is represented by one distance code with zero bits.
+ *
+ * - The list of up to 286 length/literal lengths and up to 30 distance lengths
+ * are themselves compressed using Huffman codes and run-length encoding. In
+ * the list of code lengths, a 0 symbol means no code, a 1..15 symbol means
+ * that length, and the symbols 16, 17, and 18 are run-length instructions.
+ * Each of 16, 17, and 18 are follwed by extra bits to define the length of
+ * the run. 16 copies the last length 3 to 6 times. 17 represents 3 to 10
+ * zero lengths, and 18 represents 11 to 138 zero lengths. Unused symbols
+ * are common, hence the special coding for zero lengths.
+ *
+ * - The symbols for 0..18 are Huffman coded, and so that code must be
+ * described first. This is simply a sequence of up to 19 three-bit values
+ * representing no code (0) or the code length for that symbol (1..7).
+ *
+ * - A dynamic block starts with three fixed-size counts from which is computed
+ * the number of literal/length code lengths, the number of distance code
+ * lengths, and the number of code length code lengths (ok, you come up with
+ * a better name!) in the code descriptions. For the literal/length and
+ * distance codes, lengths after those provided are considered zero, i.e. no
+ * code. The code length code lengths are received in a permuted order (see
+ * the order[] array below) to make a short code length code length list more
+ * likely. As it turns out, very short and very long codes are less likely
+ * to be seen in a dynamic code description, hence what may appear initially
+ * to be a peculiar ordering.
+ *
+ * - Given the number of literal/length code lengths (nlen) and distance code
+ * lengths (ndist), then they are treated as one long list of nlen + ndist
+ * code lengths. Therefore run-length coding can and often does cross the
+ * boundary between the two sets of lengths.
+ *
+ * - So to summarize, the code description at the start of a dynamic block is
+ * three counts for the number of code lengths for the literal/length codes,
+ * the distance codes, and the code length codes. This is followed by the
+ * code length code lengths, three bits each. This is used to construct the
+ * code length code which is used to read the remainder of the lengths. Then
+ * the literal/length code lengths and distance lengths are read as a single
+ * set of lengths using the code length codes. Codes are constructed from
+ * the resulting two sets of lengths, and then finally you can start
+ * decoding actual compressed data in the block.
+ *
+ * - For reference, a "typical" size for the code description in a dynamic
+ * block is around 80 bytes.
+ */
+local int32_t dynamic(struct state *s)
+{
+ int32_t nlen, ndist, ncode; /* number of lengths in descriptor */
+ int32_t index; /* index of lengths[] */
+ int32_t err; /* construct() return value */
+ int16_t lengths[MAXCODES]; /* descriptor code lengths */
+ int16_t lencnt[MAXBITS+1], lensym[MAXLCODES]; /* lencode memory */
+ int16_t distcnt[MAXBITS+1], distsym[MAXDCODES]; /* distcode memory */
+ struct huffman lencode = {lencnt, lensym}; /* length code */
+ struct huffman distcode = {distcnt, distsym}; /* distance code */
+ static const int16_t order[19] = /* permutation of code length codes */
+ {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+ /* get number of lengths in each table, check lengths */
+ nlen = bits(s, 5) + 257;
+ ndist = bits(s, 5) + 1;
+ ncode = bits(s, 4) + 4;
+ if (nlen > MAXLCODES || ndist > MAXDCODES)
+ return -3; /* bad counts */
+
+ /* read code length code lengths (really), missing lengths are zero */
+ for (index = 0; index < ncode; index++)
+ lengths[order[index]] = bits(s, 3);
+ for (; index < 19; index++)
+ lengths[order[index]] = 0;
+
+ /* build huffman table for code lengths codes (use lencode temporarily) */
+ err = construct(&lencode, lengths, 19);
+ if (err != 0) return -4; /* require complete code set here */
+
+ /* read length/literal and distance code length tables */
+ index = 0;
+ while (index < nlen + ndist) {
+ int32_t symbol; /* decoded value */
+ int32_t len; /* last length to repeat */
+
+ symbol = decode(s, &lencode);
+ if (symbol < 16) /* length in 0..15 */
+ lengths[index++] = symbol;
+ else { /* repeat instruction */
+ len = 0; /* assume repeating zeros */
+ if (symbol == 16) { /* repeat last length 3..6 times */
+ if (index == 0) return -5; /* no last length! */
+ len = lengths[index - 1]; /* last length */
+ symbol = 3 + bits(s, 2);
+ }
+ else if (symbol == 17) /* repeat zero 3..10 times */
+ symbol = 3 + bits(s, 3);
+ else /* == 18, repeat zero 11..138 times */
+ symbol = 11 + bits(s, 7);
+ if (index + symbol > nlen + ndist)
+ return -6; /* too many lengths! */
+ while (symbol--) /* repeat last or zero symbol times */
+ lengths[index++] = len;
+ }
+ }
+
+ /* build huffman table for literal/length codes */
+ err = construct(&lencode, lengths, nlen);
+ if (err < 0 || (err > 0 && nlen - lencode.count[0] != 1))
+ return -7; /* only allow incomplete codes if just one code */
+
+ /* build huffman table for distance codes */
+ err = construct(&distcode, lengths + nlen, ndist);
+ if (err < 0 || (err > 0 && ndist - distcode.count[0] != 1))
+ return -8; /* only allow incomplete codes if just one code */
+
+ /* decode data until end-of-block code */
+ return codes(s, &lencode, &distcode);
+}
+
+/*
+ * Inflate source to dest. On return, destlen and sourcelen are updated to the
+ * size of the uncompressed data and the size of the deflate data respectively.
+ * On success, the return value of puff() is zero. If there is an error in the
+ * source data, i.e. it is not in the deflate format, then a negative value is
+ * returned. If there is not enough input available or there is not enough
+ * output space, then a positive error is returned. In that case, destlen and
+ * sourcelen are not updated to facilitate retrying from the beginning with the
+ * provision of more input data or more output space. In the case of invalid
+ * inflate data (a negative error), the dest and source pointers are updated to
+ * facilitate the debugging of deflators.
+ *
+ * puff() also has a mode to determine the size of the uncompressed output with
+ * no output written. For this dest must be (uint8_t *)0. In this case,
+ * the input value of *destlen is ignored, and on return *destlen is set to the
+ * size of the uncompressed output.
+ *
+ * The return codes are:
+ *
+ * 2: available inflate data did not terminate
+ * 1: output space exhausted before completing inflate
+ * 0: successful inflate
+ * -1: invalid block type (type == 3)
+ * -2: stored block length did not match one's complement
+ * -3: dynamic block code description: too many length or distance codes
+ * -4: dynamic block code description: code lengths codes incomplete
+ * -5: dynamic block code description: repeat lengths with no first length
+ * -6: dynamic block code description: repeat more than specified lengths
+ * -7: dynamic block code description: invalid literal/length code lengths
+ * -8: dynamic block code description: invalid distance code lengths
+ * -9: invalid literal/length or distance code in fixed or dynamic block
+ * -10: distance is too far back in fixed or dynamic block
+ *
+ * Format notes:
+ *
+ * - Three bits are read for each block to determine the kind of block and
+ * whether or not it is the last block. Then the block is decoded and the
+ * process repeated if it was not the last block.
+ *
+ * - The leftover bits in the last byte of the deflate data after the last
+ * block (if it was a fixed or dynamic block) are undefined and have no
+ * expected values to check.
+ */
+int32_t puff(uint8_t *dest, /* pointer to destination pointer */
+ uint32_t *destlen, /* amount of output space */
+ uint8_t *source, /* pointer to source data pointer */
+ uint32_t *sourcelen) /* amount of input available */
+{
+ struct state s; /* input/output state */
+ int32_t last, type; /* block information */
+ int32_t err; /* return value */
+
+ /* initialize output state */
+ s.out = dest;
+ s.outlen = *destlen; /* ignored if dest is NULL */
+ s.outcnt = 0;
+
+ /* initialize input state */
+ s.in = source;
+ s.inlen = *sourcelen;
+ s.incnt = 0;
+ s.bitbuf = 0;
+ s.bitcnt = 0;
+
+ /* return if bits() or decode() tries to read past available input */
+ if (setjmp(s.env) != 0) /* if came back here via longjmp() */
+ err = 2; /* then skip do-loop, return error */
+ else {
+ /* process blocks until last block or error */
+ do {
+ last = bits(&s, 1); /* one if last block */
+ type = bits(&s, 2); /* block type 0..3 */
+ err = type == 0 ? stored(&s) :
+ (type == 1 ? fixed(&s) :
+ (type == 2 ? dynamic(&s) :
+ -1)); /* type == 3, invalid */
+ if (err != 0) break; /* return with error */
+ } while (!last);
+ }
+
+ /* update the lengths and return */
+ if (err <= 0) {
+ *destlen = s.outcnt;
+ *sourcelen = s.incnt;
+ }
+ return err;
+}
Added: trunk/code/qcommon/puff.h
===================================================================
--- trunk/code/qcommon/puff.h (rev 0)
+++ trunk/code/qcommon/puff.h 2007-08-23 17:23:15 UTC (rev 1132)
@@ -0,0 +1,43 @@
+/*
+ * This is a modified version of Mark Adlers work,
+ * see below for the original copyright.
+ * 2006 - Joerg Dietrich <dietrich_joerg at gmx.de>
+ */
+
+/* puff.h
+ Copyright (C) 2002, 2003 Mark Adler, all rights reserved
+ version 1.7, 3 Mar 2002
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the author be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Mark Adler madler at alumni.caltech.edu
+ */
+
+#ifndef __PUFF_H
+#define __PUFF_H
+
+#include "q_shared.h" /* for definitions of the <stdint.h> types */
+
+/*
+ * See puff.c for purpose and usage.
+ */
+int32_t puff(uint8_t *dest, /* pointer to destination pointer */
+ uint32_t *destlen, /* amount of output space */
+ uint8_t *source, /* pointer to source data pointer */
+ uint32_t *sourcelen); /* amount of input available */
+
+#endif // __PUFF_H
Modified: trunk/code/renderer/tr_image.c
===================================================================
--- trunk/code/renderer/tr_image.c 2007-08-23 15:23:43 UTC (rev 1131)
+++ trunk/code/renderer/tr_image.c 2007-08-23 17:23:15 UTC (rev 1132)
@@ -33,10 +33,13 @@
#define JPEG_INTERNALS
#include "../jpeg-6/jpeglib.h"
+#include "../qcommon/puff.h"
+
static void LoadBMP( const char *name, byte **pic, int *width, int *height );
static void LoadTGA( const char *name, byte **pic, int *width, int *height );
static void LoadJPG( const char *name, byte **pic, int *width, int *height );
+static void LoadPNG( const char *name, byte **pic, int *width, int *height );
static byte s_intensitytable[256];
static unsigned char s_gammatable[256];
@@ -1931,6 +1934,2450 @@
/*
=================
+PNG LOADING
+=================
+*/
+
+/*
+ * Quake 3 image format : RGBA
+ */
+
+#define Q3IMAGE_BYTESPERPIXEL (4)
+
+/*
+ * PNG specifications
+ */
+
+/*
+ * The first 8 Bytes of every PNG-File are a fixed signature
+ * to identify the file as a PNG.
+ */
+
+#define PNG_Signature "\x89\x50\x4E\x47\xD\xA\x1A\xA"
+#define PNG_Signature_Size (8)
+
+/*
+ * After the signature diverse chunks follow.
+ * A chunk consists of a header and if Length
+ * is bigger than 0 a body and a CRC of the body follow.
+ */
+
+struct PNG_ChunkHeader
+{
+ uint32_t Length;
+ uint32_t Type;
+};
+
+#define PNG_ChunkHeader_Size (8)
+
+typedef uint32_t PNG_ChunkCRC;
+
+#define PNG_ChunkCRC_Size (4)
+
+/*
+ * We use the following ChunkTypes.
+ * All others are ignored.
+ */
+
+#define MAKE_CHUNKTYPE(a,b,c,d) (((a) << 24) | ((b) << 16) | ((c) << 8) | ((d)))
+
+#define PNG_ChunkType_IHDR MAKE_CHUNKTYPE('I', 'H', 'D', 'R')
+#define PNG_ChunkType_PLTE MAKE_CHUNKTYPE('P', 'L', 'T', 'E')
+#define PNG_ChunkType_IDAT MAKE_CHUNKTYPE('I', 'D', 'A', 'T')
+#define PNG_ChunkType_IEND MAKE_CHUNKTYPE('I', 'E', 'N', 'D')
+#define PNG_ChunkType_tRNS MAKE_CHUNKTYPE('t', 'R', 'N', 'S')
+
+/*
+ * Per specification the first chunk after the signature SHALL be IHDR.
+ */
+
+struct PNG_Chunk_IHDR
+{
+ uint32_t Width;
+ uint32_t Height;
+ uint8_t BitDepth;
+ uint8_t ColourType;
+ uint8_t CompressionMethod;
+ uint8_t FilterMethod;
+ uint8_t InterlaceMethod;
+};
+
+#define PNG_Chunk_IHDR_Size (13)
+
+/*
+ * ColourTypes
+ */
+
+#define PNG_ColourType_Grey (0)
+#define PNG_ColourType_True (2)
+#define PNG_ColourType_Indexed (3)
+#define PNG_ColourType_GreyAlpha (4)
+#define PNG_ColourType_TrueAlpha (6)
+
+/*
+ * number of colour components
+ *
+ * Grey : 1 grey
+ * True : 1 R, 1 G, 1 B
+ * Indexed : 1 index
+ * GreyAlpha : 1 grey, 1 alpha
+ * TrueAlpha : 1 R, 1 G, 1 B, 1 alpha
+ */
+
+#define PNG_NumColourComponents_Grey (1)
+#define PNG_NumColourComponents_True (3)
+#define PNG_NumColourComponents_Indexed (1)
+#define PNG_NumColourComponents_GreyAlpha (2)
+#define PNG_NumColourComponents_TrueAlpha (4)
+
+/*
+ * For the different ColourTypes
+ * different BitDepths are specified.
+ */
+
+#define PNG_BitDepth_1 ( 1)
+#define PNG_BitDepth_2 ( 2)
+#define PNG_BitDepth_4 ( 4)
+#define PNG_BitDepth_8 ( 8)
+#define PNG_BitDepth_16 (16)
+
+/*
+ * Only one valid CompressionMethod is standardized.
+ */
+
+#define PNG_CompressionMethod_0 (0)
+
+/*
+ * Only one valid FilterMethod is currently standardized.
+ */
+
+#define PNG_FilterMethod_0 (0)
+
+/*
+ * This FilterMethod defines 5 FilterTypes
+ */
+
+#define PNG_FilterType_None (0)
+#define PNG_FilterType_Sub (1)
+#define PNG_FilterType_Up (2)
+#define PNG_FilterType_Average (3)
+#define PNG_FilterType_Paeth (4)
+
+/*
+ * Two InterlaceMethods are standardized :
+ * 0 - NonInterlaced
+ * 1 - Interlaced
+ */
+
+#define PNG_InterlaceMethod_NonInterlaced (0)
+#define PNG_InterlaceMethod_Interlaced (1)
+
+/*
+ * The Adam7 interlace method uses 7 passes.
+ */
+
+#define PNG_Adam7_NumPasses (7)
+
+/*
+ * The compressed data starts with a header ...
+ */
+
+struct PNG_ZlibHeader
+{
+ uint8_t CompressionMethod;
+ uint8_t Flags;
+};
+
+#define PNG_ZlibHeader_Size (2)
+
+/*
+ * ... and is followed by a check value
+ */
+
+#define PNG_ZlibCheckValue_Size (4)
+
+/*
+ * Some support functions for buffered files follow.
+ */
+
+/*
+ * buffered file representation
+ */
+
+struct BufferedFile
+{
+ byte *Buffer;
+ int Length;
+ byte *Ptr;
+ int BytesLeft;
+};
+
+/*
+ * Read a file into a buffer.
+ */
+
+static struct BufferedFile *ReadBufferedFile(const char *name)
+{
+ struct BufferedFile *BF;
+
+ /*
+ * input verification
+ */
+
+ if(!name)
+ {
+ return(NULL);
+ }
+
+ /*
+ * Allocate control struct.
+ */
+
+ BF = ri.Malloc(sizeof(struct BufferedFile));
+ if(!BF)
+ {
+ return(NULL);
+ }
+
+ /*
+ * Initialize the structs components.
+ */
+
+ BF->Length = 0;
+ BF->Buffer = NULL;
+ BF->Ptr = NULL;
+ BF->BytesLeft = 0;
+
+ /*
+ * Read the file.
+ */
+
+ BF->Length = ri.FS_ReadFile((char *) name, (void **) &BF->Buffer);
+
+ /*
+ * Did we get it? Is it big enough?
+ */
+
+ if(!(BF->Buffer && (BF->Length > 0)))
+ {
+ ri.Free(BF);
+
+ return(NULL);
+ }
+
+ /*
+ * Set the pointers and counters.
+ */
+
+ BF->Ptr = BF->Buffer;
+ BF->BytesLeft = BF->Length;
+
+ return(BF);
+}
+
+/*
+ * Close a buffered file.
+ */
+
+static void CloseBufferedFile(struct BufferedFile *BF)
+{
+ if(BF)
+ {
+ if(BF->Buffer)
+ {
+ ri.FS_FreeFile(BF->Buffer);
+ }
+
+ ri.Free(BF);
+ }
+}
+
+/*
+ * Get a pointer to the requested bytes.
+ */
+
+static void *BufferedFileRead(struct BufferedFile *BF, int Length)
+{
+ void *RetVal;
+
+ /*
+ * input verification
+ */
+
+ if(!(BF && Length))
+ {
+ return(NULL);
+ }
+
+ /*
+ * not enough bytes left
+ */
+
+ if(Length > BF->BytesLeft)
+ {
+ return(NULL);
+ }
+
+ /*
+ * the pointer to the requested data
+ */
+
+ RetVal = BF->Ptr;
+
+ /*
+ * Raise the pointer and counter.
+ */
+
+ BF->Ptr += Length;
+ BF->BytesLeft -= Length;
+
+ return(RetVal);
+}
+
+/*
+ * Rewind the buffer.
+ */
+
+static qboolean BufferedFileRewind(struct BufferedFile *BF, int Offset)
+{
+ int BytesRead;
+
+ /*
+ * input verification
+ */
+
+ if(!BF)
+ {
+ return(qfalse);
+ }
+
+ /*
+ * special trick to rewind to the beginning of the buffer
+ */
+
+ if(Offset == -1)
+ {
+ BF->Ptr = BF->Buffer;
+ BF->BytesLeft = BF->Length;
+
+ return(qtrue);
+ }
+
+ /*
+ * How many bytes do we have already read?
+ */
+
+ BytesRead = BF->Ptr - BF->Buffer;
+
+ /*
+ * We can only rewind to the beginning of the BufferedFile.
+ */
+
+ if(Offset > BytesRead)
+ {
+ return(qfalse);
+ }
+
+ /*
+ * lower the pointer and counter.
+ */
+
+ BF->Ptr -= Offset;
+ BF->BytesLeft += Offset;
+
+ return(qtrue);
+}
+
+/*
+ * Skip some bytes.
+ */
+
+static qboolean BufferedFileSkip(struct BufferedFile *BF, int Offset)
+{
+ /*
+ * input verification
+ */
+
+ if(!BF)
+ {
+ return(qfalse);
+ }
+
+ /*
+ * We can only skip to the end of the BufferedFile.
+ */
+
+ if(Offset > BF->BytesLeft)
+ {
+ return(qfalse);
+ }
+
+ /*
+ * lower the pointer and counter.
+ */
+
+ BF->Ptr += Offset;
+ BF->BytesLeft -= Offset;
+
+ return(qtrue);
+}
+
+/*
+ * Find a chunk
+ */
+
+static qboolean FindChunk(struct BufferedFile *BF, uint32_t ChunkType)
+{
+ struct PNG_ChunkHeader *CH;
+
+ uint32_t Length;
+ uint32_t Type;
+
+ /*
+ * input verification
+ */
+
+ if(!BF)
+ {
+ return(qfalse);
+ }
+
+ /*
+ * cycle trough the chunks
+ */
+
+ while(qtrue)
+ {
+ /*
+ * Read the chunk-header.
+ */
+
+ CH = BufferedFileRead(BF, PNG_ChunkHeader_Size);
+ if(!CH)
+ {
+ return(qfalse);
+ }
+
+ /*
+ * Do not swap the original types
+ * they might be needed later.
+ */
+
+ Length = BigLong(CH->Length);
+ Type = BigLong(CH->Type);
+
+ /*
+ * We found it!
+ */
+
+ if(Type == ChunkType)
+ {
+ /*
+ * Rewind to the start of the chunk.
+ */
+
+ BufferedFileRewind(BF, PNG_ChunkHeader_Size);
+
+ break;
+ }
+ else
+ {
+ /*
+ * Skip the rest of the chunk.
+ */
+
+ if(Length)
+ {
+ if(!BufferedFileSkip(BF, Length + PNG_ChunkCRC_Size))
+ {
+ return(qfalse);
+ }
+ }
+ }
+ }
+
+ return(qtrue);
+}
+
+/*
+ * Decompress all IDATs
+ */
+
+static uint32_t DecompressIDATs(struct BufferedFile *BF, uint8_t **Buffer)
+{
+ uint8_t *DecompressedData;
+ uint32_t DecompressedDataLength;
+
+ uint8_t *CompressedData;
+ uint8_t *CompressedDataPtr;
+ uint32_t CompressedDataLength;
+
+ struct PNG_ChunkHeader *CH;
+
+ uint32_t Length;
+ uint32_t Type;
+
+ int BytesToRewind;
+
+ int32_t puffResult;
+ uint8_t *puffDest;
+ uint32_t puffDestLen;
+ uint8_t *puffSrc;
+ uint32_t puffSrcLen;
+
+ /*
+ * input verification
+ */
+
+ if(!(BF && Buffer))
+ {
+ return(-1);
+ }
+
+ /*
+ * some zeroing
+ */
+
+ DecompressedData = NULL;
+ DecompressedDataLength = 0;
+ *Buffer = DecompressedData;
+
+ CompressedData = NULL;
+ CompressedDataLength = 0;
+
+ BytesToRewind = 0;
+
+ /*
+ * Find the first IDAT chunk.
+ */
+
+ if(!FindChunk(BF, PNG_ChunkType_IDAT))
+ {
+ return(-1);
+ }
+
+ /*
+ * Count the size of the uncompressed data
+ */
+
+ while(qtrue)
+ {
+ /*
+ * Read chunk header
+ */
+
+ CH = BufferedFileRead(BF, PNG_ChunkHeader_Size);
+ if(!CH)
+ {
+ /*
+ * Rewind to the start of this adventure
+ * and return unsuccessfull
+ */
+
+ BufferedFileRewind(BF, BytesToRewind);
+
+ return(-1);
+ }
+
+ /*
+ * Length and Type of chunk
+ */
+
+ Length = BigLong(CH->Length);
+ Type = BigLong(CH->Type);
+
+ /*
+ * We have reached the end of the IDAT chunks
+ */
+
+ if(!(Type == PNG_ChunkType_IDAT))
+ {
+ BufferedFileRewind(BF, PNG_ChunkHeader_Size);
+
+ break;
+ }
+
+ /*
+ * Add chunk header to count.
+ */
+
+ BytesToRewind += PNG_ChunkHeader_Size;
+
+ /*
+ * Skip to next chunk
+ */
+
+ if(Length)
+ {
+ if(!BufferedFileSkip(BF, Length + PNG_ChunkCRC_Size))
+ {
+ BufferedFileRewind(BF, BytesToRewind);
+
+ return(-1);
+ }
+
+ BytesToRewind += Length + PNG_ChunkCRC_Size;
+ CompressedDataLength += Length;
+ }
+ }
+
+ BufferedFileRewind(BF, BytesToRewind);
+
+ CompressedData = ri.Malloc(CompressedDataLength);
+ if(!CompressedData)
+ {
+ return(-1);
+ }
+
+ CompressedDataPtr = CompressedData;
+
+ /*
+ * Collect the compressed Data
+ */
+
+ while(qtrue)
+ {
+ /*
+ * Read chunk header
+ */
+
+ CH = BufferedFileRead(BF, PNG_ChunkHeader_Size);
+ if(!CH)
+ {
+ ri.Free(CompressedData);
+
+ return(-1);
+ }
+
+ /*
+ * Length and Type of chunk
+ */
+
+ Length = BigLong(CH->Length);
+ Type = BigLong(CH->Type);
+
+ /*
+ * We have reached the end of the IDAT chunks
+ */
+
+ if(!(Type == PNG_ChunkType_IDAT))
+ {
+ BufferedFileRewind(BF, PNG_ChunkHeader_Size);
+
+ break;
+ }
+
+ /*
+ * Copy the Data
+ */
+
+ if(Length)
+ {
+ uint8_t *OrigCompressedData;
+
+ OrigCompressedData = BufferedFileRead(BF, Length);
+ if(!OrigCompressedData)
+ {
+ ri.Free(CompressedData);
+
+ return(-1);
+ }
+
+ if(!BufferedFileSkip(BF, PNG_ChunkCRC_Size))
+ {
+ ri.Free(CompressedData);
+
+ return(-1);
+ }
+
+ memcpy(CompressedDataPtr, OrigCompressedData, Length);
+ CompressedDataPtr += Length;
+ }
+ }
+
+ /*
+ * Let puff() calculate the decompressed data length.
+ */
+
+ puffDest = NULL;
+ puffDestLen = 0;
+
+ /*
+ * The zlib header and checkvalue don't belong to the compressed data.
+ */
+
+ puffSrc = CompressedData + PNG_ZlibHeader_Size;
+ puffSrcLen = CompressedDataLength - PNG_ZlibHeader_Size - PNG_ZlibCheckValue_Size;
+
+ /*
+ * first puff() to calculate the size of the uncompressed data
+ */
+
+ puffResult = puff(puffDest, &puffDestLen, puffSrc, &puffSrcLen);
+ if(!((puffResult == 0) && (puffDestLen > 0)))
+ {
+ ri.Free(CompressedData);
+
+ return(-1);
+ }
+
+ /*
+ * Allocate the buffer for the uncompressed data.
+ */
+
+ DecompressedData = ri.Malloc(puffDestLen);
+ if(!DecompressedData)
+ {
+ ri.Free(CompressedData);
+
+ return(-1);
+ }
+
+ /*
+ * Set the input again in case something was changed by the last puff() .
+ */
+
+ puffDest = DecompressedData;
+ puffSrc = CompressedData + PNG_ZlibHeader_Size;
+ puffSrcLen = CompressedDataLength - PNG_ZlibHeader_Size - PNG_ZlibCheckValue_Size;
+
+ /*
+ * decompression puff()
+ */
+
+ puffResult = puff(puffDest, &puffDestLen, puffSrc, &puffSrcLen);
+
+ /*
+ * The compressed data is not needed anymore.
+ */
+
+ ri.Free(CompressedData);
+
+ /*
+ * Check if the last puff() was successfull.
+ */
+
+ if(!((puffResult == 0) && (puffDestLen > 0)))
+ {
+ ri.Free(DecompressedData);
+
+ return(-1);
+ }
+
+ /*
+ * Set the output of this function.
+ */
+
+ DecompressedDataLength = puffDestLen;
+ *Buffer = DecompressedData;
+
+ return(DecompressedDataLength);
+}
+
+/*
+ * the Paeth predictor
+ */
+
+static uint8_t PredictPaeth(uint8_t a, uint8_t b, uint8_t c)
+{
+ /*
+ * a == Left
+ * b == Up
+ * c == UpLeft
+ */
+
+ uint8_t Pr;
+ int p;
+ int pa, pb, pc;
+
+ Pr = 0;
+
+ p = ((int) a) + ((int) b) - ((int) c);
+ pa = abs(p - ((int) a));
+ pb = abs(p - ((int) b));
+ pc = abs(p - ((int) c));
+
+ if((pa <= pb) && (pa <= pc))
+ {
+ Pr = a;
+ }
+ else if(pb <= pc)
+ {
+ Pr = b;
+ }
+ else
+ {
+ Pr = c;
+ }
+
+ return(Pr);
+
+}
+
+/*
+ * Reverse the filters.
+ */
+
+static qboolean UnfilterImage(uint8_t *DecompressedData,
+ uint32_t ImageHeight,
+ uint32_t BytesPerScanline,
+ uint32_t BytesPerPixel)
+{
+ uint8_t *DecompPtr;
+ uint8_t FilterType;
+ uint8_t *PixelLeft, *PixelUp, *PixelUpLeft;
+ uint32_t w, h, p;
+
+ /*
+ * some zeros for the filters
+ */
+
+ uint8_t Zeros[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+
+ /*
+ * input verification
+ *
+ * ImageHeight and BytesPerScanline are not checked,
+ * because these can be zero in some interlace passes.
+ */
+
+ if(!(DecompressedData && BytesPerPixel))
+ {
+ return(qfalse);
+ }
+
+
+ /*
+ * Set the pointer to the start of the decompressed Data.
+ */
+
+ DecompPtr = DecompressedData;
+
+ /*
+ * Un-filtering is done in place.
+ */
+
+ /*
+ * Go trough all scanlines.
+ */
+
+ for(h = 0; h < ImageHeight; h++)
+ {
+ /*
+ * Every scanline starts with a FilterType byte.
+ */
+
+ FilterType = *DecompPtr;
+ DecompPtr++;
+
+ /*
+ * Left pixel of the first byte in a scanline is zero.
+ */
+
+ PixelLeft = Zeros;
+
+ /*
+ * Set PixelUp to previous line only if we are on the second line or above.
+ *
+ * Plus one byte for the FilterType
+ */
+
+ if(h > 0)
+ {
+ PixelUp = DecompPtr - (BytesPerScanline + 1);
+ }
+ else
+ {
+ PixelUp = Zeros;
+ }
+
+ /*
+ * The pixel left to the first pixel of the previous scanline is zero too.
+ */
+
+ PixelUpLeft = Zeros;
+
+ /*
+ * Cycle trough all pixels of the scanline.
+ */
+
+ for(w = 0; w < (BytesPerScanline / BytesPerPixel); w++)
+ {
+ /*
+ * Cycle trough the bytes of the pixel.
+ */
+
+ for(p = 0; p < BytesPerPixel; p++)
+ {
+ switch(FilterType)
+ {
+ case PNG_FilterType_None :
+ {
+ /*
+ * The byte is unfiltered.
+ */
+
+ break;
+ }
+
+ case PNG_FilterType_Sub :
+ {
+ DecompPtr[p] += PixelLeft[p];
+
+ break;
+ }
+
+ case PNG_FilterType_Up :
+ {
+ DecompPtr[p] += PixelUp[p];
+
+ break;
+ }
+
+ case PNG_FilterType_Average :
+ {
+ DecompPtr[p] += ((uint8_t) ((((uint16_t) PixelLeft[p]) + ((uint16_t) PixelUp[p])) / 2));
+
+ break;
+ }
+
+ case PNG_FilterType_Paeth :
+ {
+ DecompPtr[p] += PredictPaeth(PixelLeft[p], PixelUp[p], PixelUpLeft[p]);
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+ }
+
+ PixelLeft = DecompPtr;
+
+ /*
+ * We only have a upleft pixel if we are on the second line or above.
+ */
+
+ if(h > 0)
+ {
+ PixelUpLeft = DecompPtr - (BytesPerScanline + 1);
+ }
+
+ /*
+ * Skip to the next pixel.
+ */
+
+ DecompPtr += BytesPerPixel;
+
+ /*
+ * We only have a previous line if we are on the second line and above.
+ */
+
+ if(h > 0)
+ {
+ PixelUp = DecompPtr - (BytesPerScanline + 1);
+ }
+ }
+ }
+
+ return(qtrue);
+}
+
+/*
+ * Convert a raw input pixel to Quake 3 RGA format.
+ */
+
+static qboolean ConvertPixel(struct PNG_Chunk_IHDR *IHDR,
+ byte *OutPtr,
+ uint8_t *DecompPtr,
+ qboolean HasTransparentColour,
+ uint8_t *TransparentColour,
+ uint8_t *OutPal)
+{
+ /*
+ * input verification
+ */
+
+ if(!(IHDR && OutPtr && DecompPtr && TransparentColour && OutPal))
+ {
+ return(qfalse);
+ }
+
+ switch(IHDR->ColourType)
+ {
+ case PNG_ColourType_Grey :
+ {
+ switch(IHDR->BitDepth)
+ {
+ case PNG_BitDepth_1 :
+ case PNG_BitDepth_2 :
+ case PNG_BitDepth_4 :
+ {
+ uint8_t Step;
+ uint8_t GreyValue;
+
+ Step = 0xFF / ((1 << IHDR->BitDepth) - 1);
+
+ GreyValue = DecompPtr[0] * Step;
+
+ OutPtr[0] = GreyValue;
+ OutPtr[1] = GreyValue;
+ OutPtr[2] = GreyValue;
+ OutPtr[3] = 0xFF;
+
+ /*
+ * Grey supports full transparency for one specified colour
+ */
+
+ if(HasTransparentColour)
+ {
+ if(TransparentColour[1] == DecompPtr[0])
+ {
+ OutPtr[3] = 0x00;
+ }
+ }
+
+
+ break;
+ }
+
+ case PNG_BitDepth_8 :
+ case PNG_BitDepth_16 :
+ {
+ OutPtr[0] = DecompPtr[0];
+ OutPtr[1] = DecompPtr[0];
+ OutPtr[2] = DecompPtr[0];
+ OutPtr[3] = 0xFF;
+
+ /*
+ * Grey supports full transparency for one specified colour
+ */
+
+ if(HasTransparentColour)
+ {
+ if(IHDR->BitDepth == PNG_BitDepth_8)
+ {
+ if(TransparentColour[1] == DecompPtr[0])
+ {
+ OutPtr[3] = 0x00;
+ }
+ }
+ else
+ {
+ if((TransparentColour[0] == DecompPtr[0]) && (TransparentColour[1] == DecompPtr[1]))
+ {
+ OutPtr[3] = 0x00;
+ }
+ }
+ }
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ break;
+ }
+
+ case PNG_ColourType_True :
+ {
+ switch(IHDR->BitDepth)
+ {
+ case PNG_BitDepth_8 :
+ {
+ OutPtr[0] = DecompPtr[0];
+ OutPtr[1] = DecompPtr[1];
+ OutPtr[2] = DecompPtr[2];
+ OutPtr[3] = 0xFF;
+
+ /*
+ * True supports full transparency for one specified colour
+ */
+
+ if(HasTransparentColour)
+ {
+ if((TransparentColour[1] == DecompPtr[0]) &&
+ (TransparentColour[3] == DecompPtr[1]) &&
+ (TransparentColour[5] == DecompPtr[3]))
+ {
+ OutPtr[3] = 0x00;
+ }
+ }
+
+ break;
+ }
+
+ case PNG_BitDepth_16 :
+ {
+ /*
+ * We use only the upper byte.
+ */
+
+ OutPtr[0] = DecompPtr[0];
+ OutPtr[1] = DecompPtr[2];
+ OutPtr[2] = DecompPtr[4];
+ OutPtr[3] = 0xFF;
+
+ /*
+ * True supports full transparency for one specified colour
+ */
+
+ if(HasTransparentColour)
+ {
+ if((TransparentColour[0] == DecompPtr[0]) && (TransparentColour[1] == DecompPtr[1]) &&
+ (TransparentColour[2] == DecompPtr[2]) && (TransparentColour[3] == DecompPtr[3]) &&
+ (TransparentColour[4] == DecompPtr[4]) && (TransparentColour[5] == DecompPtr[5]))
+ {
+ OutPtr[3] = 0x00;
+ }
+ }
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ break;
+ }
+
+ case PNG_ColourType_Indexed :
+ {
+ OutPtr[0] = OutPal[DecompPtr[0] * Q3IMAGE_BYTESPERPIXEL + 0];
+ OutPtr[1] = OutPal[DecompPtr[0] * Q3IMAGE_BYTESPERPIXEL + 1];
+ OutPtr[2] = OutPal[DecompPtr[0] * Q3IMAGE_BYTESPERPIXEL + 2];
+ OutPtr[3] = OutPal[DecompPtr[0] * Q3IMAGE_BYTESPERPIXEL + 3];
+
+ break;
+ }
+
+ case PNG_ColourType_GreyAlpha :
+ {
+ switch(IHDR->BitDepth)
+ {
+ case PNG_BitDepth_8 :
+ {
+ OutPtr[0] = DecompPtr[0];
+ OutPtr[1] = DecompPtr[0];
+ OutPtr[2] = DecompPtr[0];
+ OutPtr[3] = DecompPtr[1];
+
+ break;
+ }
+
+ case PNG_BitDepth_16 :
+ {
+ /*
+ * We use only the upper byte.
+ */
+
+ OutPtr[0] = DecompPtr[0];
+ OutPtr[1] = DecompPtr[0];
+ OutPtr[2] = DecompPtr[0];
+ OutPtr[3] = DecompPtr[2];
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ break;
+ }
+
+ case PNG_ColourType_TrueAlpha :
+ {
+ switch(IHDR->BitDepth)
+ {
+ case PNG_BitDepth_8 :
+ {
+ OutPtr[0] = DecompPtr[0];
+ OutPtr[1] = DecompPtr[1];
+ OutPtr[2] = DecompPtr[2];
+ OutPtr[3] = DecompPtr[3];
+
+ break;
+ }
+
+ case PNG_BitDepth_16 :
+ {
+ /*
+ * We use only the upper byte.
+ */
+
+ OutPtr[0] = DecompPtr[0];
+ OutPtr[1] = DecompPtr[2];
+ OutPtr[2] = DecompPtr[4];
+ OutPtr[3] = DecompPtr[6];
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ return(qtrue);
+}
+
+
+/*
+ * Decode a non-interlaced image.
+ */
+
+static qboolean DecodeImageNonInterlaced(struct PNG_Chunk_IHDR *IHDR,
+ byte *OutBuffer,
+ uint8_t *DecompressedData,
+ uint32_t DecompressedDataLength,
+ qboolean HasTransparentColour,
+ uint8_t *TransparentColour,
+ uint8_t *OutPal)
+{
+ uint32_t IHDR_Width;
+ uint32_t IHDR_Height;
+ uint32_t BytesPerScanline, BytesPerPixel, PixelsPerByte;
+ uint32_t w, h, p;
+ byte *OutPtr;
+ uint8_t *DecompPtr;
+
+ /*
+ * input verification
+ */
+
+ if(!(IHDR && OutBuffer && DecompressedData && DecompressedDataLength && TransparentColour && OutPal))
+ {
+ return(qfalse);
+ }
+
+ /*
+ * byte swapping
+ */
+
+ IHDR_Width = BigLong(IHDR->Width);
+ IHDR_Height = BigLong(IHDR->Height);
+
+ /*
+ * information for un-filtering
+ */
+
+ switch(IHDR->ColourType)
+ {
+ case PNG_ColourType_Grey :
+ {
+ switch(IHDR->BitDepth)
+ {
+ case PNG_BitDepth_1 :
+ case PNG_BitDepth_2 :
+ case PNG_BitDepth_4 :
+ {
+ BytesPerPixel = 1;
+ PixelsPerByte = 8 / IHDR->BitDepth;
+
+ break;
+ }
+
+ case PNG_BitDepth_8 :
+ case PNG_BitDepth_16 :
+ {
+ BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_Grey;
+ PixelsPerByte = 1;
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ break;
+ }
+
+ case PNG_ColourType_True :
+ {
+ switch(IHDR->BitDepth)
+ {
+ case PNG_BitDepth_8 :
+ case PNG_BitDepth_16 :
+ {
+ BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_True;
+ PixelsPerByte = 1;
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ break;
+ }
+
+ case PNG_ColourType_Indexed :
+ {
+ switch(IHDR->BitDepth)
+ {
+ case PNG_BitDepth_1 :
+ case PNG_BitDepth_2 :
+ case PNG_BitDepth_4 :
+ {
+ BytesPerPixel = 1;
+ PixelsPerByte = 8 / IHDR->BitDepth;
+
+ break;
+ }
+
+ case PNG_BitDepth_8 :
+ {
+ BytesPerPixel = PNG_NumColourComponents_Indexed;
+ PixelsPerByte = 1;
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ break;
+ }
+
+ case PNG_ColourType_GreyAlpha :
+ {
+ switch(IHDR->BitDepth)
+ {
+ case PNG_BitDepth_8 :
+ case PNG_BitDepth_16 :
+ {
+ BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_GreyAlpha;
+ PixelsPerByte = 1;
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ break;
+ }
+
+ case PNG_ColourType_TrueAlpha :
+ {
+ switch(IHDR->BitDepth)
+ {
+ case PNG_BitDepth_8 :
+ case PNG_BitDepth_16 :
+ {
+ BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_TrueAlpha;
+ PixelsPerByte = 1;
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ /*
+ * Calculate the size of one scanline
+ */
+
+ BytesPerScanline = (IHDR_Width * BytesPerPixel + (PixelsPerByte - 1)) / PixelsPerByte;
+
+ /*
+ * Check if we have enough data for the whole image.
+ */
+
+ if(!(DecompressedDataLength == ((BytesPerScanline + 1) * IHDR_Height)))
+ {
+ return(qfalse);
+ }
+
+ /*
+ * Unfilter the image.
+ */
+
+ if(!UnfilterImage(DecompressedData, IHDR_Height, BytesPerScanline, BytesPerPixel))
+ {
+ return(qfalse);
+ }
+
+ /*
+ * Set the working pointers to the beginning of the buffers.
+ */
+
+ OutPtr = OutBuffer;
+ DecompPtr = DecompressedData;
+
+ /*
+ * Create the output image.
+ */
+
+ for(h = 0; h < IHDR_Height; h++)
+ {
+ /*
+ * Count the pixels on the scanline for those multipixel bytes
+ */
+
+ uint32_t CurrPixel;
+
+ /*
+ * skip FilterType
+ */
+
+ DecompPtr++;
+
+ /*
+ * Reset the pixel count.
+ */
+
+ CurrPixel = 0;
+
+ for(w = 0; w < (BytesPerScanline / BytesPerPixel); w++)
+ {
+ if(PixelsPerByte > 1)
+ {
+ uint8_t Mask;
+ uint32_t Shift;
+ uint8_t SinglePixel;
+
+ for(p = 0; p < PixelsPerByte; p++)
+ {
+ if(CurrPixel < IHDR_Width)
+ {
+ Mask = (1 << IHDR->BitDepth) - 1;
+ Shift = (PixelsPerByte - 1 - p) * IHDR->BitDepth;
+
+ SinglePixel = ((DecompPtr[0] & (Mask << Shift)) >> Shift);
+
+ if(!ConvertPixel(IHDR, OutPtr, &SinglePixel, HasTransparentColour, TransparentColour, OutPal))
+ {
+ return(qfalse);
+ }
+
+ OutPtr += Q3IMAGE_BYTESPERPIXEL;
+ CurrPixel++;
+ }
+ }
+
+ }
+ else
+ {
+ if(!ConvertPixel(IHDR, OutPtr, DecompPtr, HasTransparentColour, TransparentColour, OutPal))
+ {
+ return(qfalse);
+ }
+
+
+ OutPtr += Q3IMAGE_BYTESPERPIXEL;
+ }
+
+ DecompPtr += BytesPerPixel;
+ }
+ }
+
+ return(qtrue);
+}
+
+/*
+ * Decode an interlaced image.
+ */
+
+static qboolean DecodeImageInterlaced(struct PNG_Chunk_IHDR *IHDR,
+ byte *OutBuffer,
+ uint8_t *DecompressedData,
+ uint32_t DecompressedDataLength,
+ qboolean HasTransparentColour,
+ uint8_t *TransparentColour,
+ uint8_t *OutPal)
+{
+ uint32_t IHDR_Width;
+ uint32_t IHDR_Height;
+ uint32_t BytesPerScanline[PNG_Adam7_NumPasses], BytesPerPixel, PixelsPerByte;
+ uint32_t PassWidth[PNG_Adam7_NumPasses], PassHeight[PNG_Adam7_NumPasses];
+ uint32_t WSkip[PNG_Adam7_NumPasses], WOffset[PNG_Adam7_NumPasses], HSkip[PNG_Adam7_NumPasses], HOffset[PNG_Adam7_NumPasses];
+ uint32_t w, h, p, a;
+ byte *OutPtr;
+ uint8_t *DecompPtr;
+ uint32_t TargetLength;
+
+ /*
+ * input verification
+ */
+
+ if(!(IHDR && OutBuffer && DecompressedData && DecompressedDataLength && TransparentColour && OutPal))
+ {
+ return(qfalse);
+ }
+
+ /*
+ * byte swapping
+ */
+
+ IHDR_Width = BigLong(IHDR->Width);
+ IHDR_Height = BigLong(IHDR->Height);
+
+ /*
+ * Skip and Offset for the passes.
+ */
+
+ WSkip[0] = 8;
+ WOffset[0] = 0;
+ HSkip[0] = 8;
+ HOffset[0] = 0;
+
+ WSkip[1] = 8;
+ WOffset[1] = 4;
+ HSkip[1] = 8;
+ HOffset[1] = 0;
+
+ WSkip[2] = 4;
+ WOffset[2] = 0;
+ HSkip[2] = 8;
+ HOffset[2] = 4;
+
+ WSkip[3] = 4;
+ WOffset[3] = 2;
+ HSkip[3] = 4;
+ HOffset[3] = 0;
+
+ WSkip[4] = 2;
+ WOffset[4] = 0;
+ HSkip[4] = 4;
+ HOffset[4] = 2;
+
+ WSkip[5] = 2;
+ WOffset[5] = 1;
+ HSkip[5] = 2;
+ HOffset[5] = 0;
+
+ WSkip[6] = 1;
+ WOffset[6] = 0;
+ HSkip[6] = 2;
+ HOffset[6] = 1;
+
+ /*
+ * Calculate the sizes of the passes.
+ */
+
+ PassWidth[0] = (IHDR_Width + 7) / 8;
+ PassHeight[0] = (IHDR_Height + 7) / 8;
+
+ PassWidth[1] = (IHDR_Width + 3) / 8;
+ PassHeight[1] = (IHDR_Height + 7) / 8;
+
+ PassWidth[2] = (IHDR_Width + 3) / 4;
+ PassHeight[2] = (IHDR_Height + 3) / 8;
+
+ PassWidth[3] = (IHDR_Width + 1) / 4;
+ PassHeight[3] = (IHDR_Height + 3) / 4;
+
+ PassWidth[4] = (IHDR_Width + 1) / 2;
+ PassHeight[4] = (IHDR_Height + 1) / 4;
+
+ PassWidth[5] = (IHDR_Width + 0) / 2;
+ PassHeight[5] = (IHDR_Height + 1) / 2;
+
+ PassWidth[6] = (IHDR_Width + 0) / 1;
+ PassHeight[6] = (IHDR_Height + 0) / 2;
+
+ /*
+ * information for un-filtering
+ */
+
+ switch(IHDR->ColourType)
+ {
+ case PNG_ColourType_Grey :
+ {
+ switch(IHDR->BitDepth)
+ {
+ case PNG_BitDepth_1 :
+ case PNG_BitDepth_2 :
+ case PNG_BitDepth_4 :
+ {
+ BytesPerPixel = 1;
+ PixelsPerByte = 8 / IHDR->BitDepth;
+
+ break;
+ }
+
+ case PNG_BitDepth_8 :
+ case PNG_BitDepth_16 :
+ {
+ BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_Grey;
+ PixelsPerByte = 1;
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ break;
+ }
+
+ case PNG_ColourType_True :
+ {
+ switch(IHDR->BitDepth)
+ {
+ case PNG_BitDepth_8 :
+ case PNG_BitDepth_16 :
+ {
+ BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_True;
+ PixelsPerByte = 1;
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ break;
+ }
+
+ case PNG_ColourType_Indexed :
+ {
+ switch(IHDR->BitDepth)
+ {
+ case PNG_BitDepth_1 :
+ case PNG_BitDepth_2 :
+ case PNG_BitDepth_4 :
+ {
+ BytesPerPixel = 1;
+ PixelsPerByte = 8 / IHDR->BitDepth;
+
+ break;
+ }
+
+ case PNG_BitDepth_8 :
+ {
+ BytesPerPixel = PNG_NumColourComponents_Indexed;
+ PixelsPerByte = 1;
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ break;
+ }
+
+ case PNG_ColourType_GreyAlpha :
+ {
+ switch(IHDR->BitDepth)
+ {
+ case PNG_BitDepth_8 :
+ case PNG_BitDepth_16 :
+ {
+ BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_GreyAlpha;
+ PixelsPerByte = 1;
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ break;
+ }
+
+ case PNG_ColourType_TrueAlpha :
+ {
+ switch(IHDR->BitDepth)
+ {
+ case PNG_BitDepth_8 :
+ case PNG_BitDepth_16 :
+ {
+ BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_TrueAlpha;
+ PixelsPerByte = 1;
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ /*
+ * Calculate the size of the scanlines per pass
+ */
+
+ for(a = 0; a < PNG_Adam7_NumPasses; a++)
+ {
+ BytesPerScanline[a] = (PassWidth[a] * BytesPerPixel + (PixelsPerByte - 1)) / PixelsPerByte;
+ }
+
+ /*
+ * Calculate the size of all passes
+ */
+
+ TargetLength = 0;
+
+ for(a = 0; a < PNG_Adam7_NumPasses; a++)
+ {
+ TargetLength += ((BytesPerScanline[a] + (BytesPerScanline[a] ? 1 : 0)) * PassHeight[a]);
+ }
+
+ /*
+ * Check if we have enough data for the whole image.
+ */
+
+ if(!(DecompressedDataLength == TargetLength))
+ {
+ return(qfalse);
+ }
+
+ /*
+ * Unfilter the image.
+ */
+
+ DecompPtr = DecompressedData;
+
+ for(a = 0; a < PNG_Adam7_NumPasses; a++)
+ {
+ if(!UnfilterImage(DecompPtr, PassHeight[a], BytesPerScanline[a], BytesPerPixel))
+ {
+ return(qfalse);
+ }
+
+ DecompPtr += ((BytesPerScanline[a] + (BytesPerScanline[a] ? 1 : 0)) * PassHeight[a]);
+ }
+
+ /*
+ * Set the working pointers to the beginning of the buffers.
+ */
+
+ DecompPtr = DecompressedData;
+
+ /*
+ * Create the output image.
+ */
+
+ for(a = 0; a < PNG_Adam7_NumPasses; a++)
+ {
+ for(h = 0; h < PassHeight[a]; h++)
+ {
+ /*
+ * Count the pixels on the scanline for those multipixel bytes
+ */
+
+ uint32_t CurrPixel;
+
+ /*
+ * skip FilterType
+ */
+
+ DecompPtr++;
+
+ /*
+ * Reset the pixel count.
+ */
+
+ CurrPixel = 0;
+
+ for(w = 0; w < (BytesPerScanline[a] / BytesPerPixel); w++)
+ {
+ if(PixelsPerByte > 1)
+ {
+ uint8_t Mask;
+ uint32_t Shift;
+ uint8_t SinglePixel;
+
+ for(p = 0; p < PixelsPerByte; p++)
+ {
+ if(CurrPixel < PassWidth[a])
+ {
+ Mask = (1 << IHDR->BitDepth) - 1;
+ Shift = (PixelsPerByte - 1 - p) * IHDR->BitDepth;
+
+ SinglePixel = ((DecompPtr[0] & (Mask << Shift)) >> Shift);
+
+ OutPtr = OutBuffer + (((((h * HSkip[a]) + HOffset[a]) * IHDR_Width) + ((CurrPixel * WSkip[a]) + WOffset[a])) * Q3IMAGE_BYTESPERPIXEL);
+
+ if(!ConvertPixel(IHDR, OutPtr, &SinglePixel, HasTransparentColour, TransparentColour, OutPal))
+ {
+ return(qfalse);
+ }
+
+ CurrPixel++;
+ }
+ }
+
+ }
+ else
+ {
+ OutPtr = OutBuffer + (((((h * HSkip[a]) + HOffset[a]) * IHDR_Width) + ((w * WSkip[a]) + WOffset[a])) * Q3IMAGE_BYTESPERPIXEL);
+
+ if(!ConvertPixel(IHDR, OutPtr, DecompPtr, HasTransparentColour, TransparentColour, OutPal))
+ {
+ return(qfalse);
+ }
+ }
+
+ DecompPtr += BytesPerPixel;
+ }
+ }
+ }
+
+ return(qtrue);
+}
+
+/*
+ * The PNG loader
+ */
+
+static void LoadPNG(const char *name, byte **pic, int *width, int *height)
+{
+ struct BufferedFile *ThePNG;
+ byte *OutBuffer;
+ uint8_t *Signature;
+ struct PNG_ChunkHeader *CH;
+ uint32_t ChunkHeaderLength;
+ uint32_t ChunkHeaderType;
+ struct PNG_Chunk_IHDR *IHDR;
+ uint32_t IHDR_Width;
+ uint32_t IHDR_Height;
+ PNG_ChunkCRC *CRC;
+ uint8_t *InPal;
+ uint8_t *DecompressedData;
+ uint32_t DecompressedDataLength;
+ uint32_t i;
+
+ /*
+ * palette with 256 RGBA entries
+ */
+
+ uint8_t OutPal[1024];
+
+ /*
+ * transparent colour from the tRNS chunk
+ */
+
+ qboolean HasTransparentColour = qfalse;
+ uint8_t TransparentColour[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+ /*
+ * input verification
+ */
+
+ if(!(name && pic))
+ {
+ return;
+ }
+
+ /*
+ * Zero out return values.
+ */
+
+ *pic = NULL;
+
+ if(width)
+ {
+ *width = 0;
+ }
+
+ if(height)
+ {
+ *height = 0;
+ }
+
+ /*
+ * Read the file.
+ */
+
+ ThePNG = ReadBufferedFile(name);
+ if(!ThePNG)
+ {
+ return;
+ }
+
+ /*
+ * Read the siganture of the file.
+ */
+
+ Signature = BufferedFileRead(ThePNG, PNG_Signature_Size);
+ if(!Signature)
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Is it a PNG?
+ */
+
+ if(memcmp(Signature, PNG_Signature, PNG_Signature_Size))
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Read the first chunk-header.
+ */
+
+ CH = BufferedFileRead(ThePNG, PNG_ChunkHeader_Size);
+ if(!CH)
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * PNG multi-byte types are in Big Endian
+ */
+
+ ChunkHeaderLength = BigLong(CH->Length);
+ ChunkHeaderType = BigLong(CH->Type);
+
+ /*
+ * Check if the first chunk is an IHDR.
+ */
+
+ if(!((ChunkHeaderType == PNG_ChunkType_IHDR) && (ChunkHeaderLength == PNG_Chunk_IHDR_Size)))
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Read the IHDR.
+ */
+
+ IHDR = BufferedFileRead(ThePNG, PNG_Chunk_IHDR_Size);
+ if(!IHDR)
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Read the CRC for IHDR
+ */
+
+ CRC = BufferedFileRead(ThePNG, PNG_ChunkCRC_Size);
+ if(!CRC)
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Here we could check the CRC if we wanted to.
+ */
+
+ /*
+ * multi-byte type swapping
+ */
+
+ IHDR_Width = BigLong(IHDR->Width);
+ IHDR_Height = BigLong(IHDR->Height);
+
+ /*
+ * Check if Width and Height are valid.
+ */
+
+ if(!((IHDR_Width > 0) && (IHDR_Height > 0)))
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Do we need to check if the dimensions of the image are valid for Quake3?
+ */
+
+ /*
+ * Check if CompressionMethod and FilterMethod are valid.
+ */
+
+ if(!((IHDR->CompressionMethod == PNG_CompressionMethod_0) && (IHDR->FilterMethod == PNG_FilterMethod_0)))
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Check if InterlaceMethod is valid.
+ */
+
+ if(!((IHDR->InterlaceMethod == PNG_InterlaceMethod_NonInterlaced) || (IHDR->InterlaceMethod == PNG_InterlaceMethod_Interlaced)))
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Read palette for an indexed image.
+ */
+
+ if(IHDR->ColourType == PNG_ColourType_Indexed)
+ {
+ /*
+ * We need the palette first.
+ */
+
+ if(!FindChunk(ThePNG, PNG_ChunkType_PLTE))
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Read the chunk-header.
+ */
+
+ CH = BufferedFileRead(ThePNG, PNG_ChunkHeader_Size);
+ if(!CH)
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * PNG multi-byte types are in Big Endian
+ */
+
+ ChunkHeaderLength = BigLong(CH->Length);
+ ChunkHeaderType = BigLong(CH->Type);
+
+ /*
+ * Check if the chunk is an PLTE.
+ */
+
+ if(!(ChunkHeaderType == PNG_ChunkType_PLTE))
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Check if Length is divisible by 3
+ */
+
+ if(ChunkHeaderLength % 3)
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Read the raw palette data
+ */
+
+ InPal = BufferedFileRead(ThePNG, ChunkHeaderLength);
+ if(!InPal)
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Read the CRC for the palette
+ */
+
+ CRC = BufferedFileRead(ThePNG, PNG_ChunkCRC_Size);
+ if(!CRC)
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Set some default values.
+ */
+
+ for(i = 0; i < 256; i++)
+ {
+ OutPal[i * Q3IMAGE_BYTESPERPIXEL + 0] = 0x00;
+ OutPal[i * Q3IMAGE_BYTESPERPIXEL + 1] = 0x00;
+ OutPal[i * Q3IMAGE_BYTESPERPIXEL + 2] = 0x00;
+ OutPal[i * Q3IMAGE_BYTESPERPIXEL + 3] = 0xFF;
+ }
+
+ /*
+ * Convert to the Quake3 RGBA-format.
+ */
+
+ for(i = 0; i < (ChunkHeaderLength / 3); i++)
+ {
+ OutPal[i * Q3IMAGE_BYTESPERPIXEL + 0] = InPal[i*3+0];
+ OutPal[i * Q3IMAGE_BYTESPERPIXEL + 1] = InPal[i*3+1];
+ OutPal[i * Q3IMAGE_BYTESPERPIXEL + 2] = InPal[i*3+2];
+ OutPal[i * Q3IMAGE_BYTESPERPIXEL + 3] = 0xFF;
+ }
+ }
+
+ /*
+ * transparency information is sometimes stored in an tRNS chunk
+ */
+
+ /*
+ * Let's see if there is a tRNS chunk
+ */
+
+ if(FindChunk(ThePNG, PNG_ChunkType_tRNS))
+ {
+ uint8_t *Trans;
+
+ /*
+ * Read the chunk-header.
+ */
+
+ CH = BufferedFileRead(ThePNG, PNG_ChunkHeader_Size);
+ if(!CH)
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * PNG multi-byte types are in Big Endian
+ */
+
+ ChunkHeaderLength = BigLong(CH->Length);
+ ChunkHeaderType = BigLong(CH->Type);
+
+ /*
+ * Check if the chunk is an tRNS.
+ */
+
+ if(!(ChunkHeaderType == PNG_ChunkType_tRNS))
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Read the transparency information.
+ */
+
+ Trans = BufferedFileRead(ThePNG, ChunkHeaderLength);
+ if(!Trans)
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Read the CRC.
+ */
+
+ CRC = BufferedFileRead(ThePNG, PNG_ChunkCRC_Size);
+ if(!CRC)
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Only for Grey, True and Indexed ColourType should tRNS exist.
+ */
+
+ switch(IHDR->ColourType)
+ {
+ case PNG_ColourType_Grey :
+ {
+ if(!ChunkHeaderLength == 2)
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ HasTransparentColour = qtrue;
+
+ /*
+ * Grey can have one colour which is completely transparent.
+ * This colour is always stored in 16 bits.
+ */
+
+ TransparentColour[0] = Trans[0];
+ TransparentColour[1] = Trans[1];
+
+ break;
+ }
+
+ case PNG_ColourType_True :
+ {
+ if(!ChunkHeaderLength == 6)
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ HasTransparentColour = qtrue;
+
+ /*
+ * True can have one colour which is completely transparent.
+ * This colour is always stored in 16 bits.
+ */
+
+ TransparentColour[0] = Trans[0];
+ TransparentColour[1] = Trans[1];
+ TransparentColour[2] = Trans[2];
+ TransparentColour[3] = Trans[3];
+ TransparentColour[4] = Trans[4];
+ TransparentColour[5] = Trans[5];
+
+ break;
+ }
+
+ case PNG_ColourType_Indexed :
+ {
+ /*
+ * Maximum of 256 one byte transparency entries.
+ */
+
+ if(ChunkHeaderLength > 256)
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ HasTransparentColour = qtrue;
+
+ /*
+ * alpha values for palette entries
+ */
+
+ for(i = 0; i < ChunkHeaderLength; i++)
+ {
+ OutPal[i * Q3IMAGE_BYTESPERPIXEL + 3] = Trans[i];
+ }
+
+ break;
+ }
+
+ /*
+ * All other ColourTypes should not have tRNS chunks
+ */
+
+ default :
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+ }
+ }
+
+ /*
+ * Rewind to the start of the file.
+ */
+
+ if(!BufferedFileRewind(ThePNG, -1))
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Skip the signature
+ */
+
+ if(!BufferedFileSkip(ThePNG, PNG_Signature_Size))
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Decompress all IDAT chunks
+ */
+
+ DecompressedDataLength = DecompressIDATs(ThePNG, &DecompressedData);
+ if(!(DecompressedDataLength && DecompressedData))
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Allocate output buffer.
+ */
+
+ OutBuffer = ri.Malloc(IHDR_Width * IHDR_Height * Q3IMAGE_BYTESPERPIXEL);
+ if(!OutBuffer)
+ {
+ ri.Free(DecompressedData);
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Interlaced and Non-interlaced images need to be handled differently.
+ */
+
+ switch(IHDR->InterlaceMethod)
+ {
+ case PNG_InterlaceMethod_NonInterlaced :
+ {
+ if(!DecodeImageNonInterlaced(IHDR, OutBuffer, DecompressedData, DecompressedDataLength, HasTransparentColour, TransparentColour, OutPal))
+ {
+ ri.Free(OutBuffer);
+ ri.Free(DecompressedData);
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ break;
+ }
+
+ case PNG_InterlaceMethod_Interlaced :
+ {
+ if(!DecodeImageInterlaced(IHDR, OutBuffer, DecompressedData, DecompressedDataLength, HasTransparentColour, TransparentColour, OutPal))
+ {
+ ri.Free(OutBuffer);
+ ri.Free(DecompressedData);
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ break;
+ }
+
+ default :
+ {
+ ri.Free(OutBuffer);
+ ri.Free(DecompressedData);
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+ }
+
+ /*
+ * update the pointer to the image data
+ */
+
+ *pic = OutBuffer;
+
+ /*
+ * Fill width and height.
+ */
+
+ if(width)
+ {
+ *width = IHDR_Width;
+ }
+
+ if(height)
+ {
+ *height = IHDR_Height;
+ }
+
+ /*
+ * DecompressedData is not needed anymore.
+ */
+
+ ri.Free(DecompressedData);
+
+ /*
+ * We have all data, so close the file.
+ */
+
+ CloseBufferedFile(ThePNG);
+}
+
+//===================================================================
+
+/*
+=================
R_LoadImage
Loads any of the supported image types into a cannonical
@@ -1950,23 +4397,38 @@
}
if ( !Q_stricmp( name+len-4, ".tga" ) ) {
- LoadTGA( name, pic, width, height ); // try tga first
- if (!*pic) { //
- char altname[MAX_QPATH]; // try jpg in place of tga
- strcpy( altname, name );
- len = strlen( altname );
- altname[len-3] = 'j';
- altname[len-2] = 'p';
- altname[len-1] = 'g';
+ LoadTGA( name, pic, width, height );
+
+ // This is a hack to get around the fact that some
+ // baseq3 shaders refer to tga files where the images
+ // are actually jpgs
+ if (!*pic) {
+ // try jpg in place of tga
+ char altname[MAX_QPATH];
+ strcpy( altname, name );
+ len = strlen( altname );
+ altname[len-3] = 'j';
+ altname[len-2] = 'p';
+ altname[len-1] = 'g';
LoadJPG( altname, pic, width, height );
}
- } else if ( !Q_stricmp(name+len-4, ".pcx") ) {
- LoadPCX32( name, pic, width, height );
- } else if ( !Q_stricmp( name+len-4, ".bmp" ) ) {
+ }
+ else if ( !Q_stricmp(name+len-4, ".pcx") )
+ {
+ LoadPCX32( name, pic, width, height );
+ }
+ else if ( !Q_stricmp( name+len-4, ".bmp" ) )
+ {
LoadBMP( name, pic, width, height );
- } else if ( !Q_stricmp( name+len-4, ".jpg" ) ) {
+ }
+ else if ( !Q_stricmp( name+len-4, ".jpg" ) )
+ {
LoadJPG( name, pic, width, height );
}
+ else if ( !Q_stricmp( name+len-4, ".png" ) )
+ {
+ LoadPNG( name, pic, width, height );
+ }
}
@@ -2023,7 +4485,7 @@
altname[len-3] = toupper(altname[len-3]); // and try upper case extension for unix systems
altname[len-2] = toupper(altname[len-2]); //
altname[len-1] = toupper(altname[len-1]); //
- ri.Printf( PRINT_ALL, "trying %s...\n", altname ); //
+ ri.Printf( PRINT_DEVELOPER, "trying %s...\n", altname ); //
R_LoadImage( altname, &pic, &width, &height ); //
if (pic == NULL) { // if that fails
return NULL; // bail
More information about the quake3-commits
mailing list