1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 
5 This file is part of Quake III Arena source code.
6 
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11 
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 ===========================================================================
21 */
22 
23 #include "tr_local.h"
24 
25 /*
26 ========================================================================
27 
28 TGA files are used for 24/32 bit images
29 
30 ========================================================================
31 */
32 
33 typedef struct _TargaHeader {
34 	unsigned char 	id_length, colormap_type, image_type;
35 	unsigned short	colormap_index, colormap_length;
36 	unsigned char	colormap_size;
37 	unsigned short	x_origin, y_origin, width, height;
38 	unsigned char	pixel_size, attributes;
39 } TargaHeader;
40 
R_LoadTGA(const char * name,byte ** pic,int * width,int * height)41 void R_LoadTGA ( const char *name, byte **pic, int *width, int *height)
42 {
43 	unsigned	columns, rows, numPixels;
44 	byte	*pixbuf;
45 	int		row, column;
46 	byte	*buf_p;
47 	byte	*end;
48 	union {
49 		byte *b;
50 		void *v;
51 	} buffer;
52 	TargaHeader	targa_header;
53 	byte		*targa_rgba;
54 	int length;
55 
56 	*pic = NULL;
57 
58 	if(width)
59 		*width = 0;
60 	if(height)
61 		*height = 0;
62 
63 	//
64 	// load the file
65 	//
66 	length = ri.FS_ReadFile ( ( char * ) name, &buffer.v);
67 	if (!buffer.b || length < 0) {
68 		return;
69 	}
70 
71 	if(length < 18)
72 	{
73 		ri.Error( ERR_DROP, "LoadTGA: header too short (%s)", name );
74 	}
75 
76 	buf_p = buffer.b;
77 	end = buffer.b + length;
78 
79 	targa_header.id_length = buf_p[0];
80 	targa_header.colormap_type = buf_p[1];
81 	targa_header.image_type = buf_p[2];
82 
83 	memcpy(&targa_header.colormap_index, &buf_p[3], 2);
84 	memcpy(&targa_header.colormap_length, &buf_p[5], 2);
85 	targa_header.colormap_size = buf_p[7];
86 	memcpy(&targa_header.x_origin, &buf_p[8], 2);
87 	memcpy(&targa_header.y_origin, &buf_p[10], 2);
88 	memcpy(&targa_header.width, &buf_p[12], 2);
89 	memcpy(&targa_header.height, &buf_p[14], 2);
90 	targa_header.pixel_size = buf_p[16];
91 	targa_header.attributes = buf_p[17];
92 
93 	targa_header.colormap_index = LittleShort(targa_header.colormap_index);
94 	targa_header.colormap_length = LittleShort(targa_header.colormap_length);
95 	targa_header.x_origin = LittleShort(targa_header.x_origin);
96 	targa_header.y_origin = LittleShort(targa_header.y_origin);
97 	targa_header.width = LittleShort(targa_header.width);
98 	targa_header.height = LittleShort(targa_header.height);
99 
100 	buf_p += 18;
101 
102 	if (targa_header.image_type!=2
103 		&& targa_header.image_type!=10
104 		&& targa_header.image_type != 3 )
105 	{
106 		ri.Error (ERR_DROP, "LoadTGA: Only type 2 (RGB), 3 (gray), and 10 (RGB) TGA images supported");
107 	}
108 
109 	if ( targa_header.colormap_type != 0 )
110 	{
111 		ri.Error( ERR_DROP, "LoadTGA: colormaps not supported" );
112 	}
113 
114 	if ( ( targa_header.pixel_size != 32 && targa_header.pixel_size != 24 ) && targa_header.image_type != 3 )
115 	{
116 		ri.Error (ERR_DROP, "LoadTGA: Only 32 or 24 bit images supported (no colormaps)");
117 	}
118 
119 	columns = targa_header.width;
120 	rows = targa_header.height;
121 	numPixels = columns * rows * 4;
122 
123 	if(!columns || !rows || numPixels > 0x7FFFFFFF || numPixels / columns / 4 != rows)
124 	{
125 		ri.Error (ERR_DROP, "LoadTGA: %s has an invalid image size", name);
126 	}
127 
128 
129 	targa_rgba = ri.Z_Malloc (numPixels);
130 
131 	if (targa_header.id_length != 0)
132 	{
133 		if (buf_p + targa_header.id_length > end)
134 			ri.Error( ERR_DROP, "LoadTGA: header too short (%s)", name );
135 
136 		buf_p += targa_header.id_length;  // skip TARGA image comment
137 	}
138 
139 	if ( targa_header.image_type==2 || targa_header.image_type == 3 )
140 	{
141 		if(buf_p + columns*rows*targa_header.pixel_size/8 > end)
142 		{
143 			ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name);
144 		}
145 
146 		// Uncompressed RGB or gray scale image
147 		for(row=rows-1; row>=0; row--)
148 		{
149 			pixbuf = targa_rgba + row*columns*4;
150 			for(column=0; column<columns; column++)
151 			{
152 				unsigned char red,green,blue,alphabyte;
153 				switch (targa_header.pixel_size)
154 				{
155 
156 				case 8:
157 					blue = *buf_p++;
158 					green = blue;
159 					red = blue;
160 					*pixbuf++ = red;
161 					*pixbuf++ = green;
162 					*pixbuf++ = blue;
163 					*pixbuf++ = 255;
164 					break;
165 
166 				case 24:
167 					blue = *buf_p++;
168 					green = *buf_p++;
169 					red = *buf_p++;
170 					*pixbuf++ = red;
171 					*pixbuf++ = green;
172 					*pixbuf++ = blue;
173 					*pixbuf++ = 255;
174 					break;
175 				case 32:
176 					blue = *buf_p++;
177 					green = *buf_p++;
178 					red = *buf_p++;
179 					alphabyte = *buf_p++;
180 					*pixbuf++ = red;
181 					*pixbuf++ = green;
182 					*pixbuf++ = blue;
183 					*pixbuf++ = alphabyte;
184 					break;
185 				default:
186 					ri.Error( ERR_DROP, "LoadTGA: illegal pixel_size '%d' in file '%s'", targa_header.pixel_size, name );
187 					break;
188 				}
189 			}
190 		}
191 	}
192 	else if (targa_header.image_type==10) {   // Runlength encoded RGB images
193 		unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j;
194 
195 		for(row=rows-1; row>=0; row--) {
196 			pixbuf = targa_rgba + row*columns*4;
197 			for(column=0; column<columns; ) {
198 				if(buf_p + 1 > end)
199 					ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name);
200 				packetHeader= *buf_p++;
201 				packetSize = 1 + (packetHeader & 0x7f);
202 				if (packetHeader & 0x80) {        // run-length packet
203 					if(buf_p + targa_header.pixel_size/8 > end)
204 						ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name);
205 					switch (targa_header.pixel_size) {
206 						case 24:
207 								blue = *buf_p++;
208 								green = *buf_p++;
209 								red = *buf_p++;
210 								alphabyte = 255;
211 								break;
212 						case 32:
213 								blue = *buf_p++;
214 								green = *buf_p++;
215 								red = *buf_p++;
216 								alphabyte = *buf_p++;
217 								break;
218 						default:
219 							ri.Error( ERR_DROP, "LoadTGA: illegal pixel_size '%d' in file '%s'", targa_header.pixel_size, name );
220 							break;
221 					}
222 
223 					for(j=0;j<packetSize;j++) {
224 						*pixbuf++=red;
225 						*pixbuf++=green;
226 						*pixbuf++=blue;
227 						*pixbuf++=alphabyte;
228 						column++;
229 						if (column==columns) { // run spans across rows
230 							column=0;
231 							if (row>0)
232 								row--;
233 							else
234 								goto breakOut;
235 							pixbuf = targa_rgba + row*columns*4;
236 						}
237 					}
238 				}
239 				else {                            // non run-length packet
240 
241 					if(buf_p + targa_header.pixel_size/8*packetSize > end)
242 						ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name);
243 					for(j=0;j<packetSize;j++) {
244 						switch (targa_header.pixel_size) {
245 							case 24:
246 									blue = *buf_p++;
247 									green = *buf_p++;
248 									red = *buf_p++;
249 									*pixbuf++ = red;
250 									*pixbuf++ = green;
251 									*pixbuf++ = blue;
252 									*pixbuf++ = 255;
253 									break;
254 							case 32:
255 									blue = *buf_p++;
256 									green = *buf_p++;
257 									red = *buf_p++;
258 									alphabyte = *buf_p++;
259 									*pixbuf++ = red;
260 									*pixbuf++ = green;
261 									*pixbuf++ = blue;
262 									*pixbuf++ = alphabyte;
263 									break;
264 							default:
265 								ri.Error( ERR_DROP, "LoadTGA: illegal pixel_size '%d' in file '%s'", targa_header.pixel_size, name );
266 								break;
267 						}
268 						column++;
269 						if (column==columns) { // pixel packet run spans across rows
270 							column=0;
271 							if (row>0)
272 								row--;
273 							else
274 								goto breakOut;
275 							pixbuf = targa_rgba + row*columns*4;
276 						}
277 					}
278 				}
279 			}
280 			breakOut:;
281 		}
282 	}
283 
284 #if 0
285   // TTimo: this is the chunk of code to ensure a behavior that meets TGA specs
286   // bit 5 set => top-down
287   if (targa_header.attributes & 0x20) {
288     unsigned char *flip = (unsigned char*)malloc (columns*4);
289     unsigned char *src, *dst;
290 
291     for (row = 0; row < rows/2; row++) {
292       src = targa_rgba + row * 4 * columns;
293       dst = targa_rgba + (rows - row - 1) * 4 * columns;
294 
295       memcpy (flip, src, columns*4);
296       memcpy (src, dst, columns*4);
297       memcpy (dst, flip, columns*4);
298     }
299     free (flip);
300   }
301 #endif
302   // instead we just print a warning
303   if (targa_header.attributes & 0x20) {
304     ri.Printf( PRINT_DEVELOPER, "WARNING: '%s' TGA file header declares top-down image, ignoring\n", name);
305   }
306 
307   if (width)
308 	  *width = columns;
309   if (height)
310 	  *height = rows;
311 
312   *pic = targa_rgba;
313 
314   ri.FS_FreeFile (buffer.v);
315 }
316