DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

Image_files.cpp (20948B)


      1 /*
      2 ===========================================================================
      3 
      4 Doom 3 BFG Edition GPL Source Code
      5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 
      6 
      7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").  
      8 
      9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
     10 it under the terms of the GNU General Public License as published by
     11 the Free Software Foundation, either version 3 of the License, or
     12 (at your option) any later version.
     13 
     14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
     15 but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 GNU General Public License for more details.
     18 
     19 You should have received a copy of the GNU General Public License
     20 along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.
     23 
     24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
     25 
     26 ===========================================================================
     27 */
     28 
     29 #pragma hdrstop
     30 #include "../idlib/precompiled.h"
     31 
     32 
     33 #include "tr_local.h"
     34 
     35 /*
     36 
     37 This file only has a single entry point:
     38 
     39 void R_LoadImage( const char *name, byte **pic, int *width, int *height, bool makePowerOf2 );
     40 
     41 */
     42 
     43 /*
     44  * Include file for users of JPEG library.
     45  * You will need to have included system headers that define at least
     46  * the typedefs FILE and size_t before you can include jpeglib.h.
     47  * (stdio.h is sufficient on ANSI-conforming systems.)
     48  * You may also wish to include "jerror.h".
     49  */
     50 
     51 #include "jpeg-6/jpeglib.h"
     52 
     53 // hooks from jpeg lib to our system
     54 
     55 void jpg_Error( const char *fmt, ... ) {
     56 	va_list		argptr;
     57 	char		msg[2048];
     58 
     59 	va_start (argptr,fmt);
     60 	vsprintf (msg,fmt,argptr);
     61 	va_end (argptr);
     62 
     63 	common->FatalError( "%s", msg );
     64 }
     65 
     66 void jpg_Printf( const char *fmt, ... ) {
     67 	va_list		argptr;
     68 	char		msg[2048];
     69 
     70 	va_start (argptr,fmt);
     71 	vsprintf (msg,fmt,argptr);
     72 	va_end (argptr);
     73 
     74 	common->Printf( "%s", msg );
     75 }
     76 
     77 
     78 
     79 /*
     80 ================
     81 R_WriteTGA
     82 ================
     83 */
     84 void R_WriteTGA( const char *filename, const byte *data, int width, int height, bool flipVertical, const char * basePath ) {
     85 	byte	*buffer;
     86 	int		i;
     87 	int		bufferSize = width*height*4 + 18;
     88 	int     imgStart = 18;
     89 
     90 	idTempArray<byte> buf( bufferSize );
     91 	buffer = (byte *)buf.Ptr();
     92 	memset( buffer, 0, 18 );
     93 	buffer[2] = 2;		// uncompressed type
     94 	buffer[12] = width&255;
     95 	buffer[13] = width>>8;
     96 	buffer[14] = height&255;
     97 	buffer[15] = height>>8;
     98 	buffer[16] = 32;	// pixel size
     99 	if ( !flipVertical ) {
    100 		buffer[17] = (1<<5);	// flip bit, for normal top to bottom raster order
    101 	}
    102 
    103 	// swap rgb to bgr
    104 	for ( i=imgStart ; i<bufferSize ; i+=4 ) {
    105 		buffer[i] = data[i-imgStart+2];		// blue
    106 		buffer[i+1] = data[i-imgStart+1];		// green
    107 		buffer[i+2] = data[i-imgStart+0];		// red
    108 		buffer[i+3] = data[i-imgStart+3];		// alpha
    109 	}
    110 
    111 	fileSystem->WriteFile( filename, buffer, bufferSize, basePath );
    112 }
    113 
    114 static void LoadTGA( const char *name, byte **pic, int *width, int *height, ID_TIME_T *timestamp );
    115 static void LoadJPG( const char *name, byte **pic, int *width, int *height, ID_TIME_T *timestamp );
    116 
    117 /*
    118 ========================================================================
    119 
    120 TGA files are used for 24/32 bit images
    121 
    122 ========================================================================
    123 */
    124 
    125 typedef struct _TargaHeader {
    126 	unsigned char 	id_length, colormap_type, image_type;
    127 	unsigned short	colormap_index, colormap_length;
    128 	unsigned char	colormap_size;
    129 	unsigned short	x_origin, y_origin, width, height;
    130 	unsigned char	pixel_size, attributes;
    131 } TargaHeader;
    132 
    133 
    134 /*
    135 =========================================================
    136 
    137 TARGA LOADING
    138 
    139 =========================================================
    140 */
    141 
    142 /*
    143 =============
    144 LoadTGA
    145 =============
    146 */
    147 static void LoadTGA( const char *name, byte **pic, int *width, int *height, ID_TIME_T *timestamp ) {
    148 	int		columns, rows, numPixels, fileSize, numBytes;
    149 	byte	*pixbuf;
    150 	int		row, column;
    151 	byte	*buf_p;
    152 	byte	*buffer;
    153 	TargaHeader	targa_header;
    154 	byte		*targa_rgba;
    155 
    156 	if ( !pic ) {
    157 		fileSystem->ReadFile( name, NULL, timestamp );
    158 		return;	// just getting timestamp
    159 	}
    160 
    161 	*pic = NULL;
    162 
    163 	//
    164 	// load the file
    165 	//
    166 	fileSize = fileSystem->ReadFile( name, (void **)&buffer, timestamp );
    167 	if ( !buffer ) {
    168 		return;
    169 	}
    170 
    171 	buf_p = buffer;
    172 
    173 	targa_header.id_length = *buf_p++;
    174 	targa_header.colormap_type = *buf_p++;
    175 	targa_header.image_type = *buf_p++;
    176 	
    177 	targa_header.colormap_index = LittleShort ( *(short *)buf_p );
    178 	buf_p += 2;
    179 	targa_header.colormap_length = LittleShort ( *(short *)buf_p );
    180 	buf_p += 2;
    181 	targa_header.colormap_size = *buf_p++;
    182 	targa_header.x_origin = LittleShort ( *(short *)buf_p );
    183 	buf_p += 2;
    184 	targa_header.y_origin = LittleShort ( *(short *)buf_p );
    185 	buf_p += 2;
    186 	targa_header.width = LittleShort ( *(short *)buf_p );
    187 	buf_p += 2;
    188 	targa_header.height = LittleShort ( *(short *)buf_p );
    189 	buf_p += 2;
    190 	targa_header.pixel_size = *buf_p++;
    191 	targa_header.attributes = *buf_p++;
    192 
    193 	if ( targa_header.image_type != 2 && targa_header.image_type != 10 && targa_header.image_type != 3 ) {
    194 		common->Error( "LoadTGA( %s ): Only type 2 (RGB), 3 (gray), and 10 (RGB) TGA images supported\n", name );
    195 	}
    196 
    197 	if ( targa_header.colormap_type != 0 ) {
    198 		common->Error( "LoadTGA( %s ): colormaps not supported\n", name );
    199 	}
    200 
    201 	if ( ( targa_header.pixel_size != 32 && targa_header.pixel_size != 24 ) && targa_header.image_type != 3 ) {
    202 		common->Error( "LoadTGA( %s ): Only 32 or 24 bit images supported (no colormaps)\n", name );
    203 	}
    204 
    205 	if ( targa_header.image_type == 2 || targa_header.image_type == 3 ) {
    206 		numBytes = targa_header.width * targa_header.height * ( targa_header.pixel_size >> 3 );
    207 		if ( numBytes > fileSize - 18 - targa_header.id_length ) {
    208 			common->Error( "LoadTGA( %s ): incomplete file\n", name );
    209 		}
    210 	}
    211 
    212 	columns = targa_header.width;
    213 	rows = targa_header.height;
    214 	numPixels = columns * rows;
    215 
    216 	if ( width ) {
    217 		*width = columns;
    218 	}
    219 	if ( height ) {
    220 		*height = rows;
    221 	}
    222 
    223 	targa_rgba = (byte *)R_StaticAlloc(numPixels*4, TAG_IMAGE);
    224 	*pic = targa_rgba;
    225 
    226 	if ( targa_header.id_length != 0 ) {
    227 		buf_p += targa_header.id_length;  // skip TARGA image comment
    228 	}
    229 	
    230 	if ( targa_header.image_type == 2 || targa_header.image_type == 3 )
    231 	{ 
    232 		// Uncompressed RGB or gray scale image
    233 		for( row = rows - 1; row >= 0; row-- )
    234 		{
    235 			pixbuf = targa_rgba + row*columns*4;
    236 			for( column = 0; column < columns; column++)
    237 			{
    238 				unsigned char red,green,blue,alphabyte;
    239 				switch( targa_header.pixel_size )
    240 				{
    241 					
    242 				case 8:
    243 					blue = *buf_p++;
    244 					green = blue;
    245 					red = blue;
    246 					*pixbuf++ = red;
    247 					*pixbuf++ = green;
    248 					*pixbuf++ = blue;
    249 					*pixbuf++ = 255;
    250 					break;
    251 
    252 				case 24:
    253 					blue = *buf_p++;
    254 					green = *buf_p++;
    255 					red = *buf_p++;
    256 					*pixbuf++ = red;
    257 					*pixbuf++ = green;
    258 					*pixbuf++ = blue;
    259 					*pixbuf++ = 255;
    260 					break;
    261 				case 32:
    262 					blue = *buf_p++;
    263 					green = *buf_p++;
    264 					red = *buf_p++;
    265 					alphabyte = *buf_p++;
    266 					*pixbuf++ = red;
    267 					*pixbuf++ = green;
    268 					*pixbuf++ = blue;
    269 					*pixbuf++ = alphabyte;
    270 					break;
    271 				default:
    272 					common->Error( "LoadTGA( %s ): illegal pixel_size '%d'\n", name, targa_header.pixel_size );
    273 					break;
    274 				}
    275 			}
    276 		}
    277 	}
    278 	else if ( targa_header.image_type == 10 ) {   // Runlength encoded RGB images
    279 		unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j;
    280 
    281 		red = 0;
    282 		green = 0;
    283 		blue = 0;
    284 		alphabyte = 0xff;
    285 
    286 		for( row = rows - 1; row >= 0; row-- ) {
    287 			pixbuf = targa_rgba + row*columns*4;
    288 			for( column = 0; column < columns; ) {
    289 				packetHeader= *buf_p++;
    290 				packetSize = 1 + (packetHeader & 0x7f);
    291 				if ( packetHeader & 0x80 ) {        // run-length packet
    292 					switch( targa_header.pixel_size ) {
    293 						case 24:
    294 								blue = *buf_p++;
    295 								green = *buf_p++;
    296 								red = *buf_p++;
    297 								alphabyte = 255;
    298 								break;
    299 						case 32:
    300 								blue = *buf_p++;
    301 								green = *buf_p++;
    302 								red = *buf_p++;
    303 								alphabyte = *buf_p++;
    304 								break;
    305 						default:
    306 							common->Error( "LoadTGA( %s ): illegal pixel_size '%d'\n", name, targa_header.pixel_size );
    307 							break;
    308 					}
    309 	
    310 					for( j = 0; j < packetSize; j++ ) {
    311 						*pixbuf++=red;
    312 						*pixbuf++=green;
    313 						*pixbuf++=blue;
    314 						*pixbuf++=alphabyte;
    315 						column++;
    316 						if ( column == columns ) { // run spans across rows
    317 							column = 0;
    318 							if ( row > 0) {
    319 								row--;
    320 							}
    321 							else {
    322 								goto breakOut;
    323 							}
    324 							pixbuf = targa_rgba + row*columns*4;
    325 						}
    326 					}
    327 				}
    328 				else {                            // non run-length packet
    329 					for( j = 0; j < packetSize; j++ ) {
    330 						switch( targa_header.pixel_size ) {
    331 							case 24:
    332 									blue = *buf_p++;
    333 									green = *buf_p++;
    334 									red = *buf_p++;
    335 									*pixbuf++ = red;
    336 									*pixbuf++ = green;
    337 									*pixbuf++ = blue;
    338 									*pixbuf++ = 255;
    339 									break;
    340 							case 32:
    341 									blue = *buf_p++;
    342 									green = *buf_p++;
    343 									red = *buf_p++;
    344 									alphabyte = *buf_p++;
    345 									*pixbuf++ = red;
    346 									*pixbuf++ = green;
    347 									*pixbuf++ = blue;
    348 									*pixbuf++ = alphabyte;
    349 									break;
    350 							default:
    351 								common->Error( "LoadTGA( %s ): illegal pixel_size '%d'\n", name, targa_header.pixel_size );
    352 								break;
    353 						}
    354 						column++;
    355 						if ( column == columns ) { // pixel packet run spans across rows
    356 							column = 0;
    357 							if ( row > 0 ) {
    358 								row--;
    359 							}
    360 							else {
    361 								goto breakOut;
    362 							}
    363 							pixbuf = targa_rgba + row*columns*4;
    364 						}						
    365 					}
    366 				}
    367 			}
    368 			breakOut: ;
    369 		}
    370 	}
    371 
    372 	if ( (targa_header.attributes & (1<<5)) ) {			// image flp bit
    373 		if ( width != NULL && height != NULL ) {
    374 			R_VerticalFlip( *pic, *width, *height );
    375 		}
    376 	}
    377 
    378 	fileSystem->FreeFile( buffer );
    379 }
    380 
    381 /*
    382 =========================================================
    383 
    384 JPG LOADING
    385 
    386 Interfaces with the huge libjpeg
    387 =========================================================
    388 */
    389 
    390 /*
    391 =============
    392 LoadJPG
    393 =============
    394 */
    395 static void LoadJPG( const char *filename, unsigned char **pic, int *width, int *height, ID_TIME_T *timestamp ) {
    396   /* This struct contains the JPEG decompression parameters and pointers to
    397    * working space (which is allocated as needed by the JPEG library).
    398    */
    399   struct jpeg_decompress_struct cinfo;
    400   /* We use our private extension JPEG error handler.
    401    * Note that this struct must live as long as the main JPEG parameter
    402    * struct, to avoid dangling-pointer problems.
    403    */
    404   /* This struct represents a JPEG error handler.  It is declared separately
    405    * because applications often want to supply a specialized error handler
    406    * (see the second half of this file for an example).  But here we just
    407    * take the easy way out and use the standard error handler, which will
    408    * print a message on stderr and call exit() if compression fails.
    409    * Note that this struct must live as long as the main JPEG parameter
    410    * struct, to avoid dangling-pointer problems.
    411    */
    412   struct jpeg_error_mgr jerr;
    413   /* More stuff */
    414   JSAMPARRAY buffer;		/* Output row buffer */
    415   int row_stride;		/* physical row width in output buffer */
    416   unsigned char *out;
    417   byte	*fbuffer;
    418   byte  *bbuf;
    419 
    420   /* In this example we want to open the input file before doing anything else,
    421    * so that the setjmp() error recovery below can assume the file is open.
    422    * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
    423    * requires it in order to read binary files.
    424    */
    425 
    426 	// JDC: because fill_input_buffer() blindly copies INPUT_BUF_SIZE bytes,
    427 	// we need to make sure the file buffer is padded or it may crash
    428   if ( pic ) {
    429 	*pic = NULL;		// until proven otherwise
    430   }
    431   {
    432 		int		len;
    433 		idFile *f;
    434 
    435 		f = fileSystem->OpenFileRead( filename );
    436 		if ( !f ) {
    437 			return;
    438 		}
    439 		len = f->Length();
    440 		if ( timestamp ) {
    441 			*timestamp = f->Timestamp();
    442 		}
    443 		if ( !pic ) {
    444 			fileSystem->CloseFile( f );
    445 			return;	// just getting timestamp
    446 		}
    447 		fbuffer = (byte *)Mem_ClearedAlloc( len + 4096, TAG_JPG );
    448 		f->Read( fbuffer, len );
    449 		fileSystem->CloseFile( f );
    450   }
    451 
    452 
    453   /* Step 1: allocate and initialize JPEG decompression object */
    454 
    455   /* We have to set up the error handler first, in case the initialization
    456    * step fails.  (Unlikely, but it could happen if you are out of memory.)
    457    * This routine fills in the contents of struct jerr, and returns jerr's
    458    * address which we place into the link field in cinfo.
    459    */
    460   cinfo.err = jpeg_std_error(&jerr);
    461 
    462   /* Now we can initialize the JPEG decompression object. */
    463   jpeg_create_decompress(&cinfo);
    464 
    465   /* Step 2: specify data source (eg, a file) */
    466 
    467   jpeg_stdio_src(&cinfo, fbuffer);
    468 
    469   /* Step 3: read file parameters with jpeg_read_header() */
    470 
    471   (void) jpeg_read_header(&cinfo, true );
    472   /* We can ignore the return value from jpeg_read_header since
    473    *   (a) suspension is not possible with the stdio data source, and
    474    *   (b) we passed TRUE to reject a tables-only JPEG file as an error.
    475    * See libjpeg.doc for more info.
    476    */
    477 
    478   /* Step 4: set parameters for decompression */
    479 
    480   /* In this example, we don't need to change any of the defaults set by
    481    * jpeg_read_header(), so we do nothing here.
    482    */
    483 
    484   /* Step 5: Start decompressor */
    485 
    486   (void) jpeg_start_decompress(&cinfo);
    487   /* We can ignore the return value since suspension is not possible
    488    * with the stdio data source.
    489    */
    490 
    491   /* We may need to do some setup of our own at this point before reading
    492    * the data.  After jpeg_start_decompress() we have the correct scaled
    493    * output image dimensions available, as well as the output colormap
    494    * if we asked for color quantization.
    495    * In this example, we need to make an output work buffer of the right size.
    496    */ 
    497   /* JSAMPLEs per row in output buffer */
    498   row_stride = cinfo.output_width * cinfo.output_components;
    499 
    500   if (cinfo.output_components!=4) {
    501 		common->DWarning( "JPG %s is unsupported color depth (%d)", 
    502 			filename, cinfo.output_components);
    503   }
    504   out = (byte *)R_StaticAlloc(cinfo.output_width*cinfo.output_height*4, TAG_IMAGE);
    505 
    506   *pic = out;
    507   *width = cinfo.output_width;
    508   *height = cinfo.output_height;
    509 
    510   /* Step 6: while (scan lines remain to be read) */
    511   /*           jpeg_read_scanlines(...); */
    512 
    513   /* Here we use the library's state variable cinfo.output_scanline as the
    514    * loop counter, so that we don't have to keep track ourselves.
    515    */
    516   while (cinfo.output_scanline < cinfo.output_height) {
    517     /* jpeg_read_scanlines expects an array of pointers to scanlines.
    518      * Here the array is only one element long, but you could ask for
    519      * more than one scanline at a time if that's more convenient.
    520      */
    521 	bbuf = ((out+(row_stride*cinfo.output_scanline)));
    522 	buffer = &bbuf;
    523     (void) jpeg_read_scanlines(&cinfo, buffer, 1);
    524   }
    525 
    526   // clear all the alphas to 255
    527   {
    528 	  int	i, j;
    529 		byte	*buf;
    530 
    531 		buf = *pic;
    532 
    533 	  j = cinfo.output_width * cinfo.output_height * 4;
    534 	  for ( i = 3 ; i < j ; i+=4 ) {
    535 		  buf[i] = 255;
    536 	  }
    537   }
    538 
    539   /* Step 7: Finish decompression */
    540 
    541   (void) jpeg_finish_decompress(&cinfo);
    542   /* We can ignore the return value since suspension is not possible
    543    * with the stdio data source.
    544    */
    545 
    546   /* Step 8: Release JPEG decompression object */
    547 
    548   /* This is an important step since it will release a good deal of memory. */
    549   jpeg_destroy_decompress(&cinfo);
    550 
    551   /* After finish_decompress, we can close the input file.
    552    * Here we postpone it until after no more JPEG errors are possible,
    553    * so as to simplify the setjmp error logic above.  (Actually, I don't
    554    * think that jpeg_destroy can do an error exit, but why assume anything...)
    555    */
    556   Mem_Free( fbuffer );
    557 
    558   /* At this point you may want to check to see whether any corrupt-data
    559    * warnings occurred (test whether jerr.pub.num_warnings is nonzero).
    560    */
    561 
    562   /* And we're done! */
    563 }
    564 
    565 //===================================================================
    566 
    567 /*
    568 =================
    569 R_LoadImage
    570 
    571 Loads any of the supported image types into a cannonical
    572 32 bit format.
    573 
    574 Automatically attempts to load .jpg files if .tga files fail to load.
    575 
    576 *pic will be NULL if the load failed.
    577 
    578 Anything that is going to make this into a texture would use
    579 makePowerOf2 = true, but something loading an image as a lookup
    580 table of some sort would leave it in identity form.
    581 
    582 It is important to do this at image load time instead of texture load
    583 time for bump maps.
    584 
    585 Timestamp may be NULL if the value is going to be ignored
    586 
    587 If pic is NULL, the image won't actually be loaded, it will just find the
    588 timestamp.
    589 =================
    590 */
    591 void R_LoadImage( const char *cname, byte **pic, int *width, int *height, ID_TIME_T *timestamp, bool makePowerOf2 ) {
    592 	idStr name = cname;
    593 
    594 	if ( pic ) {
    595 		*pic = NULL;
    596 	}
    597 	if ( timestamp ) {
    598 		*timestamp = FILE_NOT_FOUND_TIMESTAMP;
    599 	}
    600 	if ( width ) {
    601 		*width = 0;
    602 	}
    603 	if ( height ) {
    604 		*height = 0;
    605 	}
    606 
    607 	name.DefaultFileExtension( ".tga" );
    608 
    609 	if (name.Length()<5) {
    610 		return;
    611 	}
    612 
    613 	name.ToLower();
    614 	idStr ext;
    615 	name.ExtractFileExtension( ext );
    616 
    617 	if ( ext == "tga" ) {
    618 		LoadTGA( name.c_str(), pic, width, height, timestamp );            // try tga first
    619 		if ( ( pic && *pic == 0 ) || ( timestamp && *timestamp == -1 ) ) { //-V595
    620 			name.StripFileExtension();
    621 			name.DefaultFileExtension( ".jpg" );
    622 			LoadJPG( name.c_str(), pic, width, height, timestamp );
    623 		}
    624 	} else if ( ext == "jpg" ) {
    625 		LoadJPG( name.c_str(), pic, width, height, timestamp );
    626 	}
    627 
    628 	if ( ( width && *width < 1 ) || ( height && *height < 1 ) ) {
    629 		if ( pic && *pic ) {
    630 			R_StaticFree( *pic );
    631 			*pic = 0;
    632 		}
    633 	}
    634 
    635 	//
    636 	// convert to exact power of 2 sizes
    637 	//
    638 	/*
    639 	if ( pic && *pic && makePowerOf2 ) {
    640 		int		w, h;
    641 		int		scaled_width, scaled_height;
    642 		byte	*resampledBuffer;
    643 
    644 		w = *width;
    645 		h = *height;
    646 
    647 		for (scaled_width = 1 ; scaled_width < w ; scaled_width<<=1)
    648 			;
    649 		for (scaled_height = 1 ; scaled_height < h ; scaled_height<<=1)
    650 			;
    651 
    652 		if ( scaled_width != w || scaled_height != h ) {
    653 			resampledBuffer = R_ResampleTexture( *pic, w, h, scaled_width, scaled_height );
    654 			R_StaticFree( *pic );
    655 			*pic = resampledBuffer;
    656 			*width = scaled_width;
    657 			*height = scaled_height;
    658 		}
    659 	}
    660 	*/
    661 }
    662 
    663 
    664 /*
    665 =======================
    666 R_LoadCubeImages
    667 
    668 Loads six files with proper extensions
    669 =======================
    670 */
    671 bool R_LoadCubeImages( const char *imgName, cubeFiles_t extensions, byte *pics[6], int *outSize, ID_TIME_T *timestamp ) {
    672 	int		i, j;
    673 	char	*cameraSides[6] =  { "_forward.tga", "_back.tga", "_left.tga", "_right.tga", 
    674 		"_up.tga", "_down.tga" };
    675 	char	*axisSides[6] =  { "_px.tga", "_nx.tga", "_py.tga", "_ny.tga", 
    676 		"_pz.tga", "_nz.tga" };
    677 	char	**sides;
    678 	char	fullName[MAX_IMAGE_NAME];
    679 	int		width, height, size = 0;
    680 
    681 	if ( extensions == CF_CAMERA ) {
    682 		sides = cameraSides;
    683 	} else {
    684 		sides = axisSides;
    685 	}
    686 
    687 	// FIXME: precompressed cube map files
    688 	if ( pics ) {
    689 		memset( pics, 0, 6*sizeof(pics[0]) );
    690 	}
    691 	if ( timestamp ) {
    692 		*timestamp = 0;
    693 	}
    694 
    695 	for ( i = 0 ; i < 6 ; i++ ) {
    696 		idStr::snPrintf( fullName, sizeof( fullName ), "%s%s", imgName, sides[i] );
    697 
    698 		ID_TIME_T thisTime;
    699 		if ( !pics ) {
    700 			// just checking timestamps
    701 			R_LoadImageProgram( fullName, NULL, &width, &height, &thisTime );
    702 		} else {
    703 			R_LoadImageProgram( fullName, &pics[i], &width, &height, &thisTime );
    704 		}
    705 		if ( thisTime == FILE_NOT_FOUND_TIMESTAMP ) {
    706 			break;
    707 		}
    708 		if ( i == 0 ) {
    709 			size = width;
    710 		}
    711 		if ( width != size || height != size ) {
    712 			common->Warning( "Mismatched sizes on cube map '%s'", imgName );
    713 			break;
    714 		}
    715 		if ( timestamp ) {
    716 			if ( thisTime > *timestamp ) {
    717 				*timestamp = thisTime;
    718 			}
    719 		}
    720 		if ( pics && extensions == CF_CAMERA ) {
    721 			// convert from "camera" images to native cube map images
    722 			switch( i ) {
    723 			case 0:	// forward
    724 				R_RotatePic( pics[i], width);
    725 				break;
    726 			case 1:	// back
    727 				R_RotatePic( pics[i], width);
    728 				R_HorizontalFlip( pics[i], width, height );
    729 				R_VerticalFlip( pics[i], width, height );
    730 				break;
    731 			case 2:	// left
    732 				R_VerticalFlip( pics[i], width, height );
    733 				break;
    734 			case 3:	// right
    735 				R_HorizontalFlip( pics[i], width, height );
    736 				break;
    737 			case 4:	// up
    738 				R_RotatePic( pics[i], width);
    739 				break;
    740 			case 5: // down
    741 				R_RotatePic( pics[i], width);
    742 				break;
    743 			}
    744 		}
    745 	}
    746 
    747 	if ( i != 6 ) {
    748 		// we had an error, so free everything
    749 		if ( pics ) {
    750 			for ( j = 0 ; j < i ; j++ ) {
    751 				R_StaticFree( pics[j] );
    752 			}
    753 		}
    754 
    755 		if ( timestamp ) {
    756 			*timestamp = 0;
    757 		}
    758 		return false;
    759 	}
    760 
    761 	if ( outSize ) {
    762 		*outSize = size;
    763 	}
    764 	return true;
    765 }