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 typedef struct
26 {
27 	char id[2];
28 	unsigned fileSize;
29 	unsigned reserved0;
30 	unsigned bitmapDataOffset;
31 	unsigned bitmapHeaderSize;
32 	unsigned width;
33 	unsigned height;
34 	unsigned short planes;
35 	unsigned short bitsPerPixel;
36 	unsigned compression;
37 	unsigned bitmapDataSize;
38 	unsigned hRes;
39 	unsigned vRes;
40 	unsigned colors;
41 	unsigned importantColors;
42 	unsigned char palette[256][4];
43 } BMPHeader_t;
44 
R_LoadBMP(const char * name,byte ** pic,int * width,int * height)45 void R_LoadBMP( const char *name, byte **pic, int *width, int *height )
46 {
47 	int		columns, rows;
48 	unsigned	numPixels;
49 	byte	*pixbuf;
50 	int		row, column;
51 	byte	*buf_p;
52 	byte	*end;
53 	union {
54 		byte *b;
55 		void *v;
56 	} buffer;
57 	int		length;
58 	BMPHeader_t bmpHeader;
59 	byte		*bmpRGBA;
60 
61 	*pic = NULL;
62 
63 	if(width)
64 		*width = 0;
65 
66 	if(height)
67 		*height = 0;
68 
69 	//
70 	// load the file
71 	//
72 	length = ri.FS_ReadFile( ( char * ) name, &buffer.v);
73 	if (!buffer.b || length < 0) {
74 		return;
75 	}
76 
77 	if (length < 54)
78 	{
79 		ri.Error( ERR_DROP, "LoadBMP: header too short (%s)", name );
80 	}
81 
82 	buf_p = buffer.b;
83 	end = buffer.b + length;
84 
85 	bmpHeader.id[0] = *buf_p++;
86 	bmpHeader.id[1] = *buf_p++;
87 	bmpHeader.fileSize = LittleLong( * ( int * ) buf_p );
88 	buf_p += 4;
89 	bmpHeader.reserved0 = LittleLong( * ( int * ) buf_p );
90 	buf_p += 4;
91 	bmpHeader.bitmapDataOffset = LittleLong( * ( int * ) buf_p );
92 	buf_p += 4;
93 	bmpHeader.bitmapHeaderSize = LittleLong( * ( int * ) buf_p );
94 	buf_p += 4;
95 	bmpHeader.width = LittleLong( * ( int * ) buf_p );
96 	buf_p += 4;
97 	bmpHeader.height = LittleLong( * ( int * ) buf_p );
98 	buf_p += 4;
99 	bmpHeader.planes = LittleShort( * ( short * ) buf_p );
100 	buf_p += 2;
101 	bmpHeader.bitsPerPixel = LittleShort( * ( short * ) buf_p );
102 	buf_p += 2;
103 	bmpHeader.compression = LittleLong( * ( int * ) buf_p );
104 	buf_p += 4;
105 	bmpHeader.bitmapDataSize = LittleLong( * ( int * ) buf_p );
106 	buf_p += 4;
107 	bmpHeader.hRes = LittleLong( * ( int * ) buf_p );
108 	buf_p += 4;
109 	bmpHeader.vRes = LittleLong( * ( int * ) buf_p );
110 	buf_p += 4;
111 	bmpHeader.colors = LittleLong( * ( int * ) buf_p );
112 	buf_p += 4;
113 	bmpHeader.importantColors = LittleLong( * ( int * ) buf_p );
114 	buf_p += 4;
115 
116 	if ( bmpHeader.bitsPerPixel == 8 )
117 	{
118 		if (buf_p + sizeof(bmpHeader.palette) > end)
119 			ri.Error( ERR_DROP, "LoadBMP: header too short (%s)", name );
120 
121 		Com_Memcpy( bmpHeader.palette, buf_p, sizeof( bmpHeader.palette ) );
122 	}
123 
124 	if (buffer.b + bmpHeader.bitmapDataOffset > end)
125 	{
126 		ri.Error( ERR_DROP, "LoadBMP: invalid offset value in header (%s)", name );
127 	}
128 
129 	buf_p = buffer.b + bmpHeader.bitmapDataOffset;
130 
131 	if ( bmpHeader.id[0] != 'B' && bmpHeader.id[1] != 'M' )
132 	{
133 		ri.Error( ERR_DROP, "LoadBMP: only Windows-style BMP files supported (%s)", name );
134 	}
135 	if ( bmpHeader.fileSize != length )
136 	{
137 		ri.Error( ERR_DROP, "LoadBMP: header size does not match file size (%u vs. %u) (%s)", bmpHeader.fileSize, length, name );
138 	}
139 	if ( bmpHeader.compression != 0 )
140 	{
141 		ri.Error( ERR_DROP, "LoadBMP: only uncompressed BMP files supported (%s)", name );
142 	}
143 	if ( bmpHeader.bitsPerPixel < 8 )
144 	{
145 		ri.Error( ERR_DROP, "LoadBMP: monochrome and 4-bit BMP files not supported (%s)", name );
146 	}
147 
148 	switch ( bmpHeader.bitsPerPixel )
149 	{
150 		case 8:
151 		case 16:
152 		case 24:
153 		case 32:
154 			break;
155 		default:
156 			ri.Error( ERR_DROP, "LoadBMP: illegal pixel_size '%hu' in file '%s'", bmpHeader.bitsPerPixel, name );
157 			break;
158 	}
159 
160 	columns = bmpHeader.width;
161 	rows = bmpHeader.height;
162 	if ( rows < 0 )
163 		rows = -rows;
164 	numPixels = columns * rows;
165 
166 	if(columns <= 0 || !rows || numPixels > 0x1FFFFFFF // 4*1FFFFFFF == 0x7FFFFFFC < 0x7FFFFFFF
167 	    || ((numPixels * 4) / columns) / 4 != rows)
168 	{
169 	  ri.Error (ERR_DROP, "LoadBMP: %s has an invalid image size", name);
170 	}
171 	if(buf_p + numPixels*bmpHeader.bitsPerPixel/8 > end)
172 	{
173 	  ri.Error (ERR_DROP, "LoadBMP: file truncated (%s)", name);
174 	}
175 
176 	if ( width )
177 		*width = columns;
178 	if ( height )
179 		*height = rows;
180 
181 	bmpRGBA = ri.Z_Malloc( numPixels * 4 );
182 	*pic = bmpRGBA;
183 
184 
185 	for ( row = rows-1; row >= 0; row-- )
186 	{
187 		pixbuf = bmpRGBA + row*columns*4;
188 
189 		for ( column = 0; column < columns; column++ )
190 		{
191 			unsigned char red, green, blue, alpha;
192 			int palIndex;
193 			unsigned short shortPixel;
194 
195 			switch ( bmpHeader.bitsPerPixel )
196 			{
197 			case 8:
198 				palIndex = *buf_p++;
199 				*pixbuf++ = bmpHeader.palette[palIndex][2];
200 				*pixbuf++ = bmpHeader.palette[palIndex][1];
201 				*pixbuf++ = bmpHeader.palette[palIndex][0];
202 				*pixbuf++ = 0xff;
203 				break;
204 			case 16:
205 				shortPixel = * ( unsigned short * ) pixbuf;
206 				pixbuf += 2;
207 				*pixbuf++ = ( shortPixel & ( 31 << 10 ) ) >> 7;
208 				*pixbuf++ = ( shortPixel & ( 31 << 5 ) ) >> 2;
209 				*pixbuf++ = ( shortPixel & ( 31 ) ) << 3;
210 				*pixbuf++ = 0xff;
211 				break;
212 
213 			case 24:
214 				blue = *buf_p++;
215 				green = *buf_p++;
216 				red = *buf_p++;
217 				*pixbuf++ = red;
218 				*pixbuf++ = green;
219 				*pixbuf++ = blue;
220 				*pixbuf++ = 255;
221 				break;
222 			case 32:
223 				blue = *buf_p++;
224 				green = *buf_p++;
225 				red = *buf_p++;
226 				alpha = *buf_p++;
227 				*pixbuf++ = red;
228 				*pixbuf++ = green;
229 				*pixbuf++ = blue;
230 				*pixbuf++ = alpha;
231 				break;
232 			}
233 		}
234 	}
235 
236 	ri.FS_FreeFile( buffer.v );
237 
238 }
239