src/video/SDL_bmp.c
author Petr Písař <ppisar@redhat.com>
Tue, 11 Jun 2019 06:28:12 -0700
branchSDL-1.2
changeset 12831 f1f5878be5db
parent 12722 33940ce0a0ba
child 12960 ad1bbfbca760
permissions -rw-r--r--
CVE-2019-7635: Reject BMP images with pixel colors out the palette
If a 1-, 4-, or 8-bit per pixel BMP image declares less used colors
than the palette offers an SDL_Surface with a palette of the indicated
number of used colors is created. If some of the image's pixel
refer to a color number higher then the maximal used colors, a subsequent
bliting operation on the surface will look up a color past a blit map
(that is based on the palette) memory. I.e. passing such SDL_Surface
to e.g. an SDL_DisplayFormat() function will result in a buffer overread in
a blit function.

This patch fixes it by validing each pixel's color to be less than the
maximal color number in the palette. A validation failure raises an
error from a SDL_LoadBMP_RW() function.

CVE-2019-7635
https://bugzilla.libsdl.org/show_bug.cgi?id=4498

Signed-off-by: Petr Písař <ppisar@redhat.com>
slouken@0
     1
/*
slouken@0
     2
    SDL - Simple DirectMedia Layer
slouken@6137
     3
    Copyright (C) 1997-2012 Sam Lantinga
slouken@0
     4
slouken@0
     5
    This library is free software; you can redistribute it and/or
slouken@1312
     6
    modify it under the terms of the GNU Lesser General Public
slouken@0
     7
    License as published by the Free Software Foundation; either
slouken@1312
     8
    version 2.1 of the License, or (at your option) any later version.
slouken@0
     9
slouken@0
    10
    This library is distributed in the hope that it will be useful,
slouken@0
    11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
slouken@0
    12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
slouken@1312
    13
    Lesser General Public License for more details.
slouken@0
    14
slouken@1312
    15
    You should have received a copy of the GNU Lesser General Public
slouken@1312
    16
    License along with this library; if not, write to the Free Software
slouken@1312
    17
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
slouken@0
    18
slouken@0
    19
    Sam Lantinga
slouken@252
    20
    slouken@libsdl.org
slouken@0
    21
*/
slouken@1402
    22
#include "SDL_config.h"
slouken@0
    23
slouken@0
    24
/* 
slouken@0
    25
   Code to load and save surfaces in Windows BMP format.
slouken@0
    26
slouken@0
    27
   Why support BMP format?  Well, it's a native format for Windows, and
slouken@0
    28
   most image processing programs can read and write it.  It would be nice
slouken@0
    29
   to be able to have at least one image format that we can natively load
slouken@0
    30
   and save, and since PNG is so complex that it would bloat the library,
slouken@0
    31
   BMP is a good alternative. 
slouken@0
    32
slouken@0
    33
   This code currently supports Win32 DIBs in uncompressed 8 and 24 bpp.
slouken@0
    34
*/
slouken@0
    35
slouken@0
    36
#include "SDL_video.h"
slouken@0
    37
#include "SDL_endian.h"
slouken@0
    38
slouken@0
    39
/* Compression encodings for BMP files */
slouken@0
    40
#ifndef BI_RGB
slouken@0
    41
#define BI_RGB		0
slouken@0
    42
#define BI_RLE8		1
slouken@0
    43
#define BI_RLE4		2
slouken@0
    44
#define BI_BITFIELDS	3
slouken@0
    45
#endif
slouken@0
    46
slouken@0
    47
slouken@0
    48
SDL_Surface * SDL_LoadBMP_RW (SDL_RWops *src, int freesrc)
slouken@0
    49
{
slouken@4241
    50
	SDL_bool was_error;
icculus@5988
    51
	long fp_offset = 0;
slouken@0
    52
	int bmpPitch;
slouken@0
    53
	int i, pad;
slouken@0
    54
	SDL_Surface *surface;
slouken@0
    55
	Uint32 Rmask;
slouken@0
    56
	Uint32 Gmask;
slouken@0
    57
	Uint32 Bmask;
slouken@0
    58
	SDL_Palette *palette;
slouken@0
    59
	Uint8 *bits;
slouken@4241
    60
	Uint8 *top, *end;
slouken@4241
    61
	SDL_bool topDown;
slouken@0
    62
	int ExpandBMP;
slouken@0
    63
slouken@0
    64
	/* The Win32 BMP file header (14 bytes) */
slouken@0
    65
	char   magic[2];
slouken@0
    66
	Uint32 bfSize;
slouken@0
    67
	Uint16 bfReserved1;
slouken@0
    68
	Uint16 bfReserved2;
slouken@0
    69
	Uint32 bfOffBits;
slouken@0
    70
slouken@0
    71
	/* The Win32 BITMAPINFOHEADER struct (40 bytes) */
slouken@0
    72
	Uint32 biSize;
slouken@0
    73
	Sint32 biWidth;
slouken@0
    74
	Sint32 biHeight;
slouken@0
    75
	Uint16 biPlanes;
slouken@0
    76
	Uint16 biBitCount;
slouken@0
    77
	Uint32 biCompression;
slouken@0
    78
	Uint32 biSizeImage;
slouken@0
    79
	Sint32 biXPelsPerMeter;
slouken@0
    80
	Sint32 biYPelsPerMeter;
slouken@0
    81
	Uint32 biClrUsed;
slouken@0
    82
	Uint32 biClrImportant;
slouken@0
    83
slouken@0
    84
	/* Make sure we are passed a valid data source */
slouken@0
    85
	surface = NULL;
slouken@4241
    86
	was_error = SDL_FALSE;
slouken@0
    87
	if ( src == NULL ) {
slouken@4241
    88
		was_error = SDL_TRUE;
slouken@0
    89
		goto done;
slouken@0
    90
	}
slouken@0
    91
slouken@0
    92
	/* Read in the BMP file header */
slouken@0
    93
	fp_offset = SDL_RWtell(src);
slouken@0
    94
	SDL_ClearError();
slouken@0
    95
	if ( SDL_RWread(src, magic, 1, 2) != 2 ) {
slouken@0
    96
		SDL_Error(SDL_EFREAD);
slouken@4241
    97
		was_error = SDL_TRUE;
slouken@0
    98
		goto done;
slouken@0
    99
	}
slouken@1336
   100
	if ( SDL_strncmp(magic, "BM", 2) != 0 ) {
slouken@0
   101
		SDL_SetError("File is not a Windows BMP file");
slouken@4241
   102
		was_error = SDL_TRUE;
slouken@0
   103
		goto done;
slouken@0
   104
	}
slouken@0
   105
	bfSize		= SDL_ReadLE32(src);
slouken@0
   106
	bfReserved1	= SDL_ReadLE16(src);
slouken@0
   107
	bfReserved2	= SDL_ReadLE16(src);
slouken@0
   108
	bfOffBits	= SDL_ReadLE32(src);
slouken@0
   109
slouken@0
   110
	/* Read the Win32 BITMAPINFOHEADER */
slouken@0
   111
	biSize		= SDL_ReadLE32(src);
slouken@0
   112
	if ( biSize == 12 ) {
slouken@0
   113
		biWidth		= (Uint32)SDL_ReadLE16(src);
slouken@0
   114
		biHeight	= (Uint32)SDL_ReadLE16(src);
slouken@0
   115
		biPlanes	= SDL_ReadLE16(src);
slouken@0
   116
		biBitCount	= SDL_ReadLE16(src);
slouken@0
   117
		biCompression	= BI_RGB;
slouken@0
   118
		biSizeImage	= 0;
slouken@0
   119
		biXPelsPerMeter	= 0;
slouken@0
   120
		biYPelsPerMeter	= 0;
slouken@0
   121
		biClrUsed	= 0;
slouken@0
   122
		biClrImportant	= 0;
slouken@0
   123
	} else {
slouken@0
   124
		biWidth		= SDL_ReadLE32(src);
slouken@0
   125
		biHeight	= SDL_ReadLE32(src);
slouken@0
   126
		biPlanes	= SDL_ReadLE16(src);
slouken@0
   127
		biBitCount	= SDL_ReadLE16(src);
slouken@0
   128
		biCompression	= SDL_ReadLE32(src);
slouken@0
   129
		biSizeImage	= SDL_ReadLE32(src);
slouken@0
   130
		biXPelsPerMeter	= SDL_ReadLE32(src);
slouken@0
   131
		biYPelsPerMeter	= SDL_ReadLE32(src);
slouken@0
   132
		biClrUsed	= SDL_ReadLE32(src);
slouken@0
   133
		biClrImportant	= SDL_ReadLE32(src);
slouken@0
   134
	}
icculus@6227
   135
icculus@6227
   136
	/* stop some compiler warnings. */
icculus@6227
   137
	(void) bfSize;
icculus@6227
   138
	(void) bfReserved1;
icculus@6227
   139
	(void) bfReserved2;
icculus@6227
   140
	(void) biPlanes;
icculus@6227
   141
	(void) biSizeImage;
icculus@6227
   142
	(void) biXPelsPerMeter;
icculus@6227
   143
	(void) biYPelsPerMeter;
icculus@6227
   144
	(void) biClrImportant;
icculus@6227
   145
slouken@4241
   146
	if (biHeight < 0) {
slouken@4241
   147
		topDown = SDL_TRUE;
slouken@4241
   148
		biHeight = -biHeight;
slouken@4241
   149
	} else {
slouken@4241
   150
		topDown = SDL_FALSE;
slouken@4241
   151
	}
slouken@0
   152
slouken@0
   153
	/* Check for read error */
slouken@1336
   154
	if ( SDL_strcmp(SDL_GetError(), "") != 0 ) {
slouken@4241
   155
		was_error = SDL_TRUE;
slouken@0
   156
		goto done;
slouken@0
   157
	}
slouken@0
   158
slouken@0
   159
	/* Expand 1 and 4 bit bitmaps to 8 bits per pixel */
slouken@0
   160
	switch (biBitCount) {
slouken@0
   161
		case 1:
slouken@0
   162
		case 4:
slouken@0
   163
			ExpandBMP = biBitCount;
slouken@0
   164
			biBitCount = 8;
slouken@0
   165
			break;
slouken@12645
   166
		case 2:
slouken@12645
   167
		case 3:
slouken@12645
   168
		case 5:
slouken@12645
   169
		case 6:
slouken@12645
   170
		case 7:
slouken@12645
   171
			SDL_SetError("%d-bpp BMP images are not supported", biBitCount);
slouken@12645
   172
			was_error = SDL_TRUE;
slouken@12645
   173
			goto done;
slouken@0
   174
		default:
slouken@0
   175
			ExpandBMP = 0;
slouken@0
   176
			break;
slouken@0
   177
	}
slouken@0
   178
slouken@0
   179
	/* We don't support any BMP compression right now */
slouken@0
   180
	Rmask = Gmask = Bmask = 0;
slouken@0
   181
	switch (biCompression) {
slouken@0
   182
		case BI_RGB:
slouken@0
   183
			/* If there are no masks, use the defaults */
slouken@0
   184
			if ( bfOffBits == (14+biSize) ) {
slouken@0
   185
				/* Default values for the BMP format */
slouken@0
   186
				switch (biBitCount) {
slouken@0
   187
					case 15:
slouken@0
   188
					case 16:
slouken@0
   189
						Rmask = 0x7C00;
slouken@0
   190
						Gmask = 0x03E0;
slouken@0
   191
						Bmask = 0x001F;
slouken@0
   192
						break;
slouken@0
   193
					case 24:
slouken@0
   194
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
slouken@0
   195
					        Rmask = 0x000000FF;
slouken@0
   196
					        Gmask = 0x0000FF00;
slouken@0
   197
					        Bmask = 0x00FF0000;
slouken@0
   198
						break;
slouken@0
   199
#endif
slouken@0
   200
					case 32:
slouken@0
   201
						Rmask = 0x00FF0000;
slouken@0
   202
						Gmask = 0x0000FF00;
slouken@0
   203
						Bmask = 0x000000FF;
slouken@0
   204
						break;
slouken@0
   205
					default:
slouken@0
   206
						break;
slouken@0
   207
				}
slouken@0
   208
				break;
slouken@0
   209
			}
slouken@0
   210
			/* Fall through -- read the RGB masks */
slouken@0
   211
slouken@0
   212
		case BI_BITFIELDS:
slouken@0
   213
			switch (biBitCount) {
slouken@0
   214
				case 15:
slouken@0
   215
				case 16:
slouken@0
   216
				case 32:
slouken@0
   217
					Rmask = SDL_ReadLE32(src);
slouken@0
   218
					Gmask = SDL_ReadLE32(src);
slouken@0
   219
					Bmask = SDL_ReadLE32(src);
slouken@0
   220
					break;
slouken@0
   221
				default:
slouken@0
   222
					break;
slouken@0
   223
			}
slouken@0
   224
			break;
slouken@0
   225
		default:
slouken@0
   226
			SDL_SetError("Compressed BMP files not supported");
slouken@4241
   227
			was_error = SDL_TRUE;
slouken@0
   228
			goto done;
slouken@0
   229
	}
slouken@0
   230
slouken@0
   231
	/* Create a compatible surface, note that the colors are RGB ordered */
slouken@0
   232
	surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
slouken@0
   233
			biWidth, biHeight, biBitCount, Rmask, Gmask, Bmask, 0);
slouken@0
   234
	if ( surface == NULL ) {
slouken@4241
   235
		was_error = SDL_TRUE;
slouken@0
   236
		goto done;
slouken@0
   237
	}
slouken@0
   238
slouken@0
   239
	/* Load the palette, if any */
slouken@0
   240
	palette = (surface->format)->palette;
slouken@0
   241
	if ( palette ) {
slouken@0
   242
		if ( biClrUsed == 0 ) {
slouken@0
   243
			biClrUsed = 1 << biBitCount;
slouken@12611
   244
		} else if ( biClrUsed > (1 << biBitCount) ) {
slouken@12611
   245
			SDL_SetError("BMP file has an invalid number of colors");
slouken@12611
   246
			was_error = SDL_TRUE;
slouken@12611
   247
			goto done;
slouken@0
   248
		}
slouken@0
   249
		if ( biSize == 12 ) {
slouken@0
   250
			for ( i = 0; i < (int)biClrUsed; ++i ) {
slouken@0
   251
				SDL_RWread(src, &palette->colors[i].b, 1, 1);
slouken@0
   252
				SDL_RWread(src, &palette->colors[i].g, 1, 1);
slouken@0
   253
				SDL_RWread(src, &palette->colors[i].r, 1, 1);
slouken@0
   254
				palette->colors[i].unused = 0;
slouken@0
   255
			}	
slouken@0
   256
		} else {
slouken@0
   257
			for ( i = 0; i < (int)biClrUsed; ++i ) {
slouken@0
   258
				SDL_RWread(src, &palette->colors[i].b, 1, 1);
slouken@0
   259
				SDL_RWread(src, &palette->colors[i].g, 1, 1);
slouken@0
   260
				SDL_RWread(src, &palette->colors[i].r, 1, 1);
slouken@0
   261
				SDL_RWread(src, &palette->colors[i].unused, 1, 1);
slouken@0
   262
			}	
slouken@0
   263
		}
slouken@0
   264
		palette->ncolors = biClrUsed;
slouken@0
   265
	}
slouken@0
   266
slouken@0
   267
	/* Read the surface pixels.  Note that the bmp image is upside down */
slouken@1330
   268
	if ( SDL_RWseek(src, fp_offset+bfOffBits, RW_SEEK_SET) < 0 ) {
slouken@0
   269
		SDL_Error(SDL_EFSEEK);
slouken@4241
   270
		was_error = SDL_TRUE;
slouken@0
   271
		goto done;
slouken@0
   272
	}
slouken@4241
   273
	top = (Uint8 *)surface->pixels;
slouken@4241
   274
	end = (Uint8 *)surface->pixels+(surface->h*surface->pitch);
slouken@0
   275
	switch (ExpandBMP) {
slouken@0
   276
		case 1:
slouken@0
   277
			bmpPitch = (biWidth + 7) >> 3;
slouken@0
   278
			pad  = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
slouken@0
   279
			break;
slouken@0
   280
		case 4:
slouken@0
   281
			bmpPitch = (biWidth + 1) >> 1;
slouken@0
   282
			pad  = (((bmpPitch)%4) ? (4-((bmpPitch)%4)) : 0);
slouken@0
   283
			break;
slouken@0
   284
		default:
slouken@0
   285
			pad  = ((surface->pitch%4) ?
slouken@0
   286
					(4-(surface->pitch%4)) : 0);
slouken@0
   287
			break;
slouken@0
   288
	}
slouken@4241
   289
	if ( topDown ) {
slouken@4241
   290
		bits = top;
slouken@4241
   291
	} else {
slouken@4241
   292
		bits = end - surface->pitch;
slouken@4241
   293
	}
slouken@4241
   294
	while ( bits >= top && bits < end ) {
slouken@0
   295
		switch (ExpandBMP) {
slouken@0
   296
			case 1:
slouken@0
   297
			case 4: {
slouken@0
   298
			Uint8 pixel = 0;
slouken@0
   299
			int   shift = (8-ExpandBMP);
slouken@0
   300
			for ( i=0; i<surface->w; ++i ) {
slouken@0
   301
				if ( i%(8/ExpandBMP) == 0 ) {
slouken@0
   302
					if ( !SDL_RWread(src, &pixel, 1, 1) ) {
slouken@0
   303
						SDL_SetError(
slouken@0
   304
					"Error reading from BMP");
slouken@4241
   305
						was_error = SDL_TRUE;
slouken@0
   306
						goto done;
slouken@0
   307
					}
slouken@0
   308
				}
slouken@0
   309
				*(bits+i) = (pixel>>shift);
slouken@0
   310
				pixel <<= ExpandBMP;
ppisar@12831
   311
				if ( bits[i] >= biClrUsed ) {
ppisar@12831
   312
					SDL_SetError(
ppisar@12831
   313
						"A BMP image contains a pixel with a color out of the palette");
ppisar@12831
   314
					was_error = SDL_TRUE;
ppisar@12831
   315
					goto done;
ppisar@12831
   316
				}
slouken@0
   317
			} }
slouken@0
   318
			break;
slouken@0
   319
slouken@0
   320
			default:
slouken@0
   321
			if ( SDL_RWread(src, bits, 1, surface->pitch)
slouken@0
   322
							 != surface->pitch ) {
slouken@0
   323
				SDL_Error(SDL_EFREAD);
slouken@4241
   324
				was_error = SDL_TRUE;
slouken@0
   325
				goto done;
slouken@0
   326
			}
ppisar@12831
   327
			if ( 8 == biBitCount && palette && biClrUsed < (1 << biBitCount ) ) {
ppisar@12831
   328
				for ( i=0; i<surface->w; ++i ) {
ppisar@12831
   329
					if ( bits[i] >= biClrUsed ) {
ppisar@12831
   330
						SDL_SetError(
ppisar@12831
   331
							"A BMP image contains a pixel with a color out of the palette");
ppisar@12831
   332
						was_error = SDL_TRUE;
ppisar@12831
   333
						goto done;
ppisar@12831
   334
					}
ppisar@12831
   335
				}
ppisar@12831
   336
			}
slouken@0
   337
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
slouken@0
   338
			/* Byte-swap the pixels if needed. Note that the 24bpp
slouken@0
   339
			   case has already been taken care of above. */
slouken@0
   340
			switch(biBitCount) {
slouken@0
   341
				case 15:
slouken@0
   342
				case 16: {
slouken@0
   343
				        Uint16 *pix = (Uint16 *)bits;
slouken@0
   344
					for(i = 0; i < surface->w; i++)
slouken@0
   345
					        pix[i] = SDL_Swap16(pix[i]);
slouken@0
   346
					break;
slouken@0
   347
				}
slouken@0
   348
slouken@0
   349
				case 32: {
slouken@0
   350
				        Uint32 *pix = (Uint32 *)bits;
slouken@0
   351
					for(i = 0; i < surface->w; i++)
slouken@0
   352
					        pix[i] = SDL_Swap32(pix[i]);
slouken@0
   353
					break;
slouken@0
   354
				}
slouken@0
   355
			}
slouken@0
   356
#endif
slouken@0
   357
			break;
slouken@0
   358
		}
slouken@0
   359
		/* Skip padding bytes, ugh */
slouken@0
   360
		if ( pad ) {
slouken@0
   361
			Uint8 padbyte;
slouken@0
   362
			for ( i=0; i<pad; ++i ) {
slouken@0
   363
				SDL_RWread(src, &padbyte, 1, 1);
slouken@0
   364
			}
slouken@0
   365
		}
slouken@4241
   366
		if ( topDown ) {
slouken@4241
   367
			bits += surface->pitch;
slouken@4241
   368
		} else {
slouken@4241
   369
			bits -= surface->pitch;
slouken@4241
   370
		}
slouken@0
   371
	}
slouken@0
   372
done:
slouken@0
   373
	if ( was_error ) {
slouken@1329
   374
		if ( src ) {
slouken@1330
   375
			SDL_RWseek(src, fp_offset, RW_SEEK_SET);
slouken@1329
   376
		}
slouken@0
   377
		if ( surface ) {
slouken@0
   378
			SDL_FreeSurface(surface);
slouken@0
   379
		}
slouken@0
   380
		surface = NULL;
slouken@0
   381
	}
slouken@0
   382
	if ( freesrc && src ) {
slouken@0
   383
		SDL_RWclose(src);
slouken@0
   384
	}
slouken@0
   385
	return(surface);
slouken@0
   386
}
slouken@0
   387
slouken@0
   388
int SDL_SaveBMP_RW (SDL_Surface *saveme, SDL_RWops *dst, int freedst)
slouken@0
   389
{
slouken@0
   390
	long fp_offset;
slouken@0
   391
	int i, pad;
slouken@0
   392
	SDL_Surface *surface;
slouken@0
   393
	Uint8 *bits;
slouken@0
   394
slouken@0
   395
	/* The Win32 BMP file header (14 bytes) */
slouken@0
   396
	char   magic[2] = { 'B', 'M' };
slouken@0
   397
	Uint32 bfSize;
slouken@0
   398
	Uint16 bfReserved1;
slouken@0
   399
	Uint16 bfReserved2;
slouken@0
   400
	Uint32 bfOffBits;
slouken@0
   401
slouken@0
   402
	/* The Win32 BITMAPINFOHEADER struct (40 bytes) */
slouken@0
   403
	Uint32 biSize;
slouken@0
   404
	Sint32 biWidth;
slouken@0
   405
	Sint32 biHeight;
slouken@0
   406
	Uint16 biPlanes;
slouken@0
   407
	Uint16 biBitCount;
slouken@0
   408
	Uint32 biCompression;
slouken@0
   409
	Uint32 biSizeImage;
slouken@0
   410
	Sint32 biXPelsPerMeter;
slouken@0
   411
	Sint32 biYPelsPerMeter;
slouken@0
   412
	Uint32 biClrUsed;
slouken@0
   413
	Uint32 biClrImportant;
slouken@0
   414
slouken@0
   415
	/* Make sure we have somewhere to save */
slouken@0
   416
	surface = NULL;
slouken@0
   417
	if ( dst ) {
slouken@0
   418
		if ( saveme->format->palette ) {
slouken@0
   419
			if ( saveme->format->BitsPerPixel == 8 ) {
slouken@0
   420
				surface = saveme;
slouken@0
   421
			} else {
slouken@0
   422
				SDL_SetError("%d bpp BMP files not supported",
slouken@0
   423
						saveme->format->BitsPerPixel);
slouken@0
   424
			}
slouken@0
   425
		}
slouken@0
   426
		else if ( (saveme->format->BitsPerPixel == 24) &&
slouken@0
   427
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
slouken@0
   428
				(saveme->format->Rmask == 0x00FF0000) &&
slouken@0
   429
				(saveme->format->Gmask == 0x0000FF00) &&
slouken@0
   430
				(saveme->format->Bmask == 0x000000FF)
slouken@0
   431
#else
slouken@0
   432
				(saveme->format->Rmask == 0x000000FF) &&
slouken@0
   433
				(saveme->format->Gmask == 0x0000FF00) &&
slouken@0
   434
				(saveme->format->Bmask == 0x00FF0000)
slouken@0
   435
#endif
slouken@0
   436
			  ) {
slouken@0
   437
			surface = saveme;
slouken@0
   438
		} else {
slouken@0
   439
			SDL_Rect bounds;
slouken@0
   440
slouken@0
   441
			/* Convert to 24 bits per pixel */
slouken@0
   442
			surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
slouken@0
   443
					saveme->w, saveme->h, 24,
slouken@0
   444
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
slouken@0
   445
					0x00FF0000, 0x0000FF00, 0x000000FF,
slouken@0
   446
#else
slouken@0
   447
					0x000000FF, 0x0000FF00, 0x00FF0000,
slouken@0
   448
#endif
slouken@0
   449
					0);
slouken@0
   450
			if ( surface != NULL ) {
slouken@0
   451
				bounds.x = 0;
slouken@0
   452
				bounds.y = 0;
slouken@0
   453
				bounds.w = saveme->w;
slouken@0
   454
				bounds.h = saveme->h;
slouken@0
   455
				if ( SDL_LowerBlit(saveme, &bounds, surface,
slouken@0
   456
							&bounds) < 0 ) {
slouken@0
   457
					SDL_FreeSurface(surface);
slouken@0
   458
					SDL_SetError(
slouken@0
   459
					"Couldn't convert image to 24 bpp");
slouken@0
   460
					surface = NULL;
slouken@0
   461
				}
slouken@0
   462
			}
slouken@0
   463
		}
slouken@0
   464
	}
slouken@0
   465
slouken@0
   466
	if ( surface && (SDL_LockSurface(surface) == 0) ) {
slouken@1012
   467
		const int bw = surface->w*surface->format->BytesPerPixel;
slouken@1012
   468
slouken@0
   469
		/* Set the BMP file header values */
slouken@0
   470
		bfSize = 0;		 /* We'll write this when we're done */
slouken@0
   471
		bfReserved1 = 0;
slouken@0
   472
		bfReserved2 = 0;
slouken@0
   473
		bfOffBits = 0;		/* We'll write this when we're done */
slouken@0
   474
slouken@0
   475
		/* Write the BMP file header values */
slouken@0
   476
		fp_offset = SDL_RWtell(dst);
slouken@0
   477
		SDL_ClearError();
slouken@0
   478
		SDL_RWwrite(dst, magic, 2, 1);
slouken@0
   479
		SDL_WriteLE32(dst, bfSize);
slouken@0
   480
		SDL_WriteLE16(dst, bfReserved1);
slouken@0
   481
		SDL_WriteLE16(dst, bfReserved2);
slouken@0
   482
		SDL_WriteLE32(dst, bfOffBits);
slouken@0
   483
slouken@0
   484
		/* Set the BMP info values */
slouken@0
   485
		biSize = 40;
slouken@0
   486
		biWidth = surface->w;
slouken@0
   487
		biHeight = surface->h;
slouken@0
   488
		biPlanes = 1;
slouken@0
   489
		biBitCount = surface->format->BitsPerPixel;
slouken@0
   490
		biCompression = BI_RGB;
slouken@0
   491
		biSizeImage = surface->h*surface->pitch;
slouken@0
   492
		biXPelsPerMeter = 0;
slouken@0
   493
		biYPelsPerMeter = 0;
slouken@0
   494
		if ( surface->format->palette ) {
slouken@0
   495
			biClrUsed = surface->format->palette->ncolors;
slouken@0
   496
		} else {
slouken@0
   497
			biClrUsed = 0;
slouken@0
   498
		}
slouken@0
   499
		biClrImportant = 0;
slouken@0
   500
slouken@0
   501
		/* Write the BMP info values */
slouken@0
   502
		SDL_WriteLE32(dst, biSize);
slouken@0
   503
		SDL_WriteLE32(dst, biWidth);
slouken@0
   504
		SDL_WriteLE32(dst, biHeight);
slouken@0
   505
		SDL_WriteLE16(dst, biPlanes);
slouken@0
   506
		SDL_WriteLE16(dst, biBitCount);
slouken@0
   507
		SDL_WriteLE32(dst, biCompression);
slouken@0
   508
		SDL_WriteLE32(dst, biSizeImage);
slouken@0
   509
		SDL_WriteLE32(dst, biXPelsPerMeter);
slouken@0
   510
		SDL_WriteLE32(dst, biYPelsPerMeter);
slouken@0
   511
		SDL_WriteLE32(dst, biClrUsed);
slouken@0
   512
		SDL_WriteLE32(dst, biClrImportant);
slouken@0
   513
slouken@0
   514
		/* Write the palette (in BGR color order) */
slouken@0
   515
		if ( surface->format->palette ) {
slouken@0
   516
			SDL_Color *colors;
slouken@0
   517
			int       ncolors;
slouken@0
   518
slouken@0
   519
			colors = surface->format->palette->colors;
slouken@0
   520
			ncolors = surface->format->palette->ncolors;
slouken@0
   521
			for ( i=0; i<ncolors; ++i ) {
slouken@0
   522
				SDL_RWwrite(dst, &colors[i].b, 1, 1);
slouken@0
   523
				SDL_RWwrite(dst, &colors[i].g, 1, 1);
slouken@0
   524
				SDL_RWwrite(dst, &colors[i].r, 1, 1);
slouken@0
   525
				SDL_RWwrite(dst, &colors[i].unused, 1, 1);
slouken@0
   526
			}
slouken@0
   527
		}
slouken@0
   528
slouken@0
   529
		/* Write the bitmap offset */
slouken@0
   530
		bfOffBits = SDL_RWtell(dst)-fp_offset;
slouken@1330
   531
		if ( SDL_RWseek(dst, fp_offset+10, RW_SEEK_SET) < 0 ) {
slouken@0
   532
			SDL_Error(SDL_EFSEEK);
slouken@0
   533
		}
slouken@0
   534
		SDL_WriteLE32(dst, bfOffBits);
slouken@1330
   535
		if ( SDL_RWseek(dst, fp_offset+bfOffBits, RW_SEEK_SET) < 0 ) {
slouken@0
   536
			SDL_Error(SDL_EFSEEK);
slouken@0
   537
		}
slouken@0
   538
slouken@0
   539
		/* Write the bitmap image upside down */
slouken@0
   540
		bits = (Uint8 *)surface->pixels+(surface->h*surface->pitch);
slouken@1012
   541
		pad  = ((bw%4) ? (4-(bw%4)) : 0);
slouken@0
   542
		while ( bits > (Uint8 *)surface->pixels ) {
slouken@0
   543
			bits -= surface->pitch;
slouken@1012
   544
			if ( SDL_RWwrite(dst, bits, 1, bw) != bw) {
slouken@0
   545
				SDL_Error(SDL_EFWRITE);
slouken@0
   546
				break;
slouken@0
   547
			}
slouken@0
   548
			if ( pad ) {
slouken@0
   549
				const Uint8 padbyte = 0;
slouken@0
   550
				for ( i=0; i<pad; ++i ) {
slouken@0
   551
					SDL_RWwrite(dst, &padbyte, 1, 1);
slouken@0
   552
				}
slouken@0
   553
			}
slouken@0
   554
		}
slouken@0
   555
slouken@0
   556
		/* Write the BMP file size */
slouken@0
   557
		bfSize = SDL_RWtell(dst)-fp_offset;
slouken@1330
   558
		if ( SDL_RWseek(dst, fp_offset+2, RW_SEEK_SET) < 0 ) {
slouken@0
   559
			SDL_Error(SDL_EFSEEK);
slouken@0
   560
		}
slouken@0
   561
		SDL_WriteLE32(dst, bfSize);
slouken@1330
   562
		if ( SDL_RWseek(dst, fp_offset+bfSize, RW_SEEK_SET) < 0 ) {
slouken@0
   563
			SDL_Error(SDL_EFSEEK);
slouken@0
   564
		}
slouken@0
   565
slouken@0
   566
		/* Close it up.. */
slouken@0
   567
		SDL_UnlockSurface(surface);
slouken@0
   568
		if ( surface != saveme ) {
slouken@0
   569
			SDL_FreeSurface(surface);
slouken@0
   570
		}
slouken@0
   571
	}
slouken@0
   572
slouken@0
   573
	if ( freedst && dst ) {
slouken@0
   574
		SDL_RWclose(dst);
slouken@0
   575
	}
slouken@1336
   576
	return((SDL_strcmp(SDL_GetError(), "") == 0) ? 0 : -1);
slouken@0
   577
}