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)\n", 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)\n", name );
120
121 Com_Memcpy( bmpHeader.palette, buf_p, sizeof( bmpHeader.palette ) );
122 buf_p += sizeof(bmpHeader.palette);
123 }
124
125 if (buffer.b + bmpHeader.bitmapDataOffset > end)
126 {
127 ri.Error( ERR_DROP, "LoadBMP: invalid offset value in header (%s)\n", name );
128 }
129
130 buf_p = buffer.b + bmpHeader.bitmapDataOffset;
131
132 if ( bmpHeader.id[0] != 'B' && bmpHeader.id[1] != 'M' )
133 {
134 ri.Error( ERR_DROP, "LoadBMP: only Windows-style BMP files supported (%s)\n", name );
135 }
136 if ( bmpHeader.fileSize != length )
137 {
138 ri.Error( ERR_DROP, "LoadBMP: header size does not match file size (%u vs. %u) (%s)\n", bmpHeader.fileSize, length, name );
139 }
140 if ( bmpHeader.compression != 0 )
141 {
142 ri.Error( ERR_DROP, "LoadBMP: only uncompressed BMP files supported (%s)\n", name );
143 }
144 if ( bmpHeader.bitsPerPixel < 8 )
145 {
146 ri.Error( ERR_DROP, "LoadBMP: monochrome and 4-bit BMP files not supported (%s)\n", name );
147 }
148
149 switch ( bmpHeader.bitsPerPixel )
150 {
151 case 8:
152 case 16:
153 case 24:
154 case 32:
155 break;
156 default:
157 ri.Error( ERR_DROP, "LoadBMP: illegal pixel_size '%hu' in file '%s'\n", bmpHeader.bitsPerPixel, name );
158 break;
159 }
160
161 columns = bmpHeader.width;
162 rows = bmpHeader.height;
163 if ( rows < 0 )
164 rows = -rows;
165 numPixels = columns * rows;
166
167 if(columns <= 0 || !rows || numPixels > 0x1FFFFFFF // 4*1FFFFFFF == 0x7FFFFFFC < 0x7FFFFFFF
168 || ((numPixels * 4) / columns) / 4 != rows)
169 {
170 ri.Error (ERR_DROP, "LoadBMP: %s has an invalid image size\n", name);
171 }
172 if(buf_p + numPixels*bmpHeader.bitsPerPixel/8 > end)
173 {
174 ri.Error (ERR_DROP, "LoadBMP: file truncated (%s)\n", name);
175 }
176
177 if ( width )
178 *width = columns;
179 if ( height )
180 *height = rows;
181
182 bmpRGBA = ri.Malloc( numPixels * 4 );
183 *pic = bmpRGBA;
184
185
186 for ( row = rows-1; row >= 0; row-- )
187 {
188 pixbuf = bmpRGBA + row*columns*4;
189
190 for ( column = 0; column < columns; column++ )
191 {
192 unsigned char red, green, blue, alpha;
193 int palIndex;
194 unsigned short shortPixel;
195
196 switch ( bmpHeader.bitsPerPixel )
197 {
198 case 8:
199 palIndex = *buf_p++;
200 *pixbuf++ = bmpHeader.palette[palIndex][2];
201 *pixbuf++ = bmpHeader.palette[palIndex][1];
202 *pixbuf++ = bmpHeader.palette[palIndex][0];
203 *pixbuf++ = 0xff;
204 break;
205 case 16:
206 shortPixel = * ( unsigned short * ) pixbuf;
207 pixbuf += 2;
208 *pixbuf++ = ( shortPixel & ( 31 << 10 ) ) >> 7;
209 *pixbuf++ = ( shortPixel & ( 31 << 5 ) ) >> 2;
210 *pixbuf++ = ( shortPixel & ( 31 ) ) << 3;
211 *pixbuf++ = 0xff;
212 break;
213
214 case 24:
215 blue = *buf_p++;
216 green = *buf_p++;
217 red = *buf_p++;
218 *pixbuf++ = red;
219 *pixbuf++ = green;
220 *pixbuf++ = blue;
221 *pixbuf++ = 255;
222 break;
223 case 32:
224 blue = *buf_p++;
225 green = *buf_p++;
226 red = *buf_p++;
227 alpha = *buf_p++;
228 *pixbuf++ = red;
229 *pixbuf++ = green;
230 *pixbuf++ = blue;
231 *pixbuf++ = alpha;
232 break;
233 }
234 }
235 }
236
237 ri.FS_FreeFile( buffer.v );
238
239 }
240