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