// Filename:- R_Image.cpp // // a pile of block-paste code from other codebases for basic image file stuff #include "stdafx.h" #include "includes.h" #include "R_Common.h" #include "jpeg_interface.h" #include "png/png.h" // #include "R_Image.h" static image_t* hashTable[FILE_HASH_SIZE]; /* ======================================================================== PCX files are used for 8 bit images ======================================================================== */ typedef struct { char manufacturer; char version; char encoding; char bits_per_pixel; unsigned short xmin,ymin,xmax,ymax; unsigned short hres,vres; unsigned char palette[48]; char reserved; char color_planes; unsigned short bytes_per_line; unsigned short palette_type; char filler[58]; unsigned char data; // unbounded } pcx_t; /* ======================================================================== TGA files are used for 24/32 bit images ======================================================================== */ typedef struct _TargaHeader { unsigned char id_length, colormap_type, image_type; unsigned short colormap_index, colormap_length; unsigned char colormap_size; unsigned short x_origin, y_origin, width, height; unsigned char pixel_size, attributes; } TargaHeader; /* ========================================================= BMP LOADING ========================================================= */ typedef struct { char id[2]; unsigned long fileSize; unsigned long reserved0; unsigned long bitmapDataOffset; unsigned long bitmapHeaderSize; unsigned long width; unsigned long height; unsigned short planes; unsigned short bitsPerPixel; unsigned long compression; unsigned long bitmapDataSize; unsigned long hRes; unsigned long vRes; unsigned long colors; unsigned long importantColors; unsigned char palette[256][4]; } BMPHeader_t; static void LoadBMP( const char *name, byte **pic, int *width, int *height ) { int columns, rows, numPixels; byte *pixbuf; int row, column; byte *buf_p; byte *buffer; int length; BMPHeader_t bmpHeader; byte *bmpRGBA; *pic = NULL; // // load the file // length = ri.FS_ReadFile( ( char * ) name, (void **)&buffer); if (!buffer) { return; } buf_p = buffer; bmpHeader.id[0] = *buf_p++; bmpHeader.id[1] = *buf_p++; bmpHeader.fileSize = LittleLong( * ( long * ) buf_p ); buf_p += 4; bmpHeader.reserved0 = LittleLong( * ( long * ) buf_p ); buf_p += 4; bmpHeader.bitmapDataOffset = LittleLong( * ( long * ) buf_p ); buf_p += 4; bmpHeader.bitmapHeaderSize = LittleLong( * ( long * ) buf_p ); buf_p += 4; bmpHeader.width = LittleLong( * ( long * ) buf_p ); buf_p += 4; bmpHeader.height = LittleLong( * ( long * ) buf_p ); buf_p += 4; bmpHeader.planes = LittleShort( * ( short * ) buf_p ); buf_p += 2; bmpHeader.bitsPerPixel = LittleShort( * ( short * ) buf_p ); buf_p += 2; bmpHeader.compression = LittleLong( * ( long * ) buf_p ); buf_p += 4; bmpHeader.bitmapDataSize = LittleLong( * ( long * ) buf_p ); buf_p += 4; bmpHeader.hRes = LittleLong( * ( long * ) buf_p ); buf_p += 4; bmpHeader.vRes = LittleLong( * ( long * ) buf_p ); buf_p += 4; bmpHeader.colors = LittleLong( * ( long * ) buf_p ); buf_p += 4; bmpHeader.importantColors = LittleLong( * ( long * ) buf_p ); buf_p += 4; memcpy( bmpHeader.palette, buf_p, sizeof( bmpHeader.palette ) ); if ( bmpHeader.bitsPerPixel == 8 ) buf_p += 1024; if ( bmpHeader.id[0] != 'B' && bmpHeader.id[1] != 'M' ) { ri.Error( ERR_DROP, "LoadBMP: only Windows-style BMP files supported (%s)\n", name ); } if ( bmpHeader.fileSize != length ) { ri.Error( ERR_DROP, "LoadBMP: header size does not match file size (%d vs. %d) (%s)\n", bmpHeader.fileSize, length, name ); } if ( bmpHeader.compression != 0 ) { ri.Error( ERR_DROP, "LoadBMP: only uncompressed BMP files supported (%s)\n", name ); } if ( bmpHeader.bitsPerPixel < 8 ) { ri.Error( ERR_DROP, "LoadBMP: monochrome and 4-bit BMP files not supported (%s)\n", name ); } columns = bmpHeader.width; rows = bmpHeader.height; if ( rows < 0 ) rows = -rows; numPixels = columns * rows; if ( width ) *width = columns; if ( height ) *height = rows; bmpRGBA = (unsigned char *)ri.Malloc( numPixels * 4 ); *pic = bmpRGBA; for ( row = rows-1; row >= 0; row-- ) { pixbuf = bmpRGBA + row*columns*4; for ( column = 0; column < columns; column++ ) { unsigned char red, green, blue, alpha; int palIndex; unsigned short shortPixel; switch ( bmpHeader.bitsPerPixel ) { case 8: palIndex = *buf_p++; *pixbuf++ = bmpHeader.palette[palIndex][2]; *pixbuf++ = bmpHeader.palette[palIndex][1]; *pixbuf++ = bmpHeader.palette[palIndex][0]; *pixbuf++ = 0xff; break; case 16: shortPixel = * ( unsigned short * ) pixbuf; pixbuf += 2; *pixbuf++ = ( shortPixel & ( 31 << 10 ) ) >> 7; *pixbuf++ = ( shortPixel & ( 31 << 5 ) ) >> 2; *pixbuf++ = ( shortPixel & ( 31 ) ) << 3; *pixbuf++ = 0xff; break; case 24: blue = *buf_p++; green = *buf_p++; red = *buf_p++; *pixbuf++ = red; *pixbuf++ = green; *pixbuf++ = blue; *pixbuf++ = 255; break; case 32: blue = *buf_p++; green = *buf_p++; red = *buf_p++; alpha = *buf_p++; *pixbuf++ = red; *pixbuf++ = green; *pixbuf++ = blue; *pixbuf++ = alpha; break; default: ri.Error( ERR_DROP, "LoadBMP: illegal pixel_size '%d' in file '%s'\n", bmpHeader.bitsPerPixel, name ); break; } } } ri.FS_FreeFile( buffer ); } /* typedef struct _TargaHeader { unsigned char id_length, colormap_type, image_type; unsigned short colormap_index, colormap_length; unsigned char colormap_size; unsigned short x_origin, y_origin, width, height; unsigned char pixel_size, attributes; } TargaHeader; correct 000000 00 00 02 00 00 00 00 18 00 00 00 00 76 02 43 01 ............v.C. 000010 18 20 ED D9 C9 EC D9 C8 EC D9 C7 EA D7 C7 EA D7 . íÙÉìÙÈìÙÇê×Çê× bad 000000 00 00 02 00 00 00 00 00 00 00 00 00 76 02 43 01 ............v.C. 000010 20 00 ED D9 C9 FF EC D9 C8 FF EC D9 C7 FF EA D7 .íÙÉÿìÙÈÿìÙÇÿê× #pragma pack(push,1) typedef struct { byte byIDFieldLength; // must be 0 byte byColourmapType; // 0 = truecolour, 1 = paletted, else bad byte byImageType; // 1 = colour mapped (palette), uncompressed, 2 = truecolour, uncompressed, else bad word w1stColourMapEntry; // must be 0 word wColourMapLength; // 256 for 8-bit palettes, else 0 for true-colour byte byColourMapEntrySize; // 24 for 8-bit palettes, else 0 for true-colour word wImageXOrigin; // ignored word wImageYOrigin; // ignored word wImageWidth; // in pixels word wImageHeight; // in pixels byte byImagePlanes; // bits per pixel (8 for paletted, else 24 for true-colour) byte byScanLineOrder; // Image descriptor bytes // bits 0-3 = # attr bits (alpha chan) // bits 4-5 = pixel order/dir // bits 6-7 scan line interleave (00b=none,01b=2way interleave,10b=4way) } TGAHEADER, *LPTGAHEADER; #pragma pack(pop) */ bool TGA_Write(LPCSTR psFullPathedFilename, byte *pPixels, int iWidth, int iHeight, int iPlanes) { byte *pbBuffer = NULL; int iPixelBytes = (iPlanes == 24) ? 3:4; int iTotalPixelBytes = iWidth * iHeight * iPixelBytes; int iTotalBufferBytes = iTotalPixelBytes + 18; assert(iPlanes == 24 || iPlanes == 32); pbBuffer = (byte *) ri.Hunk_AllocateTempMemory(iTotalBufferBytes); memset (pbBuffer, 0, 18); pbBuffer[2] = 2; // uncompressed type pbBuffer[12] = iWidth & 255; pbBuffer[13] = iWidth >> 8; pbBuffer[14] = iHeight & 255; pbBuffer[15] = iHeight >> 8; pbBuffer[16] = iPlanes; // pixel size pbBuffer[17] = 0x00; // scan line order, tells TGA-legal packages that it's upside down. ID code ignore it. memcpy(pbBuffer+18, pPixels, iTotalPixelBytes ); // turn it upside down to match TGA default, because ID loader ignores scanline attr flags... (sigh) // int iScanLineBytes = iWidth * iPixelBytes; byte *pbScanLine = (byte *) ri.Hunk_AllocateTempMemory(iScanLineBytes); byte *pbSrc = &pbBuffer[18]; byte *pbDst = &pbBuffer[(18+iTotalPixelBytes)-iScanLineBytes]; for (int iYFlip=0; iYFlip < iHeight/2; iYFlip++) { memcpy(pbScanLine,pbSrc,iScanLineBytes); memcpy(pbSrc,pbDst,iScanLineBytes); memcpy(pbDst,pbScanLine,iScanLineBytes); pbSrc+=iScanLineBytes; pbDst-=iScanLineBytes; } ri.Hunk_FreeTempMemory( pbScanLine ); // swap rgb to bgr... // for (int i=18 ; i=0; row--) { pixbuf = pRGBA + row*columns*4; for(column=0; column=0; row--) { pixbuf = pRGBA + row*columns*4; for(column=0; column0) row--; else goto breakOut; pixbuf = pRGBA + row*columns*4; } } } else { // non run-length packet for(j=0;j0) row--; else goto breakOut; pixbuf = pRGBA + row*columns*4; } } } } breakOut:; } } ri.FS_FreeFile (buffer); } */ // My TGA loader... // //--------------------------------------------------- #pragma pack(push,1) typedef struct { byte byIDFieldLength; // must be 0 byte byColourmapType; // 0 = truecolour, 1 = paletted, else bad byte byImageType; // 1 = colour mapped (palette), uncompressed, 2 = truecolour, uncompressed, else bad word w1stColourMapEntry; // must be 0 word wColourMapLength; // 256 for 8-bit palettes, else 0 for true-colour byte byColourMapEntrySize; // 24 for 8-bit palettes, else 0 for true-colour word wImageXOrigin; // ignored word wImageYOrigin; // ignored word wImageWidth; // in pixels word wImageHeight; // in pixels byte byImagePlanes; // bits per pixel (8 for paletted, else 24 for true-colour) byte byScanLineOrder; // Image descriptor bytes // bits 0-3 = # attr bits (alpha chan) // bits 4-5 = pixel order/dir // bits 6-7 scan line interleave (00b=none,01b=2way interleave,10b=4way) } TGAHeader_t; #pragma pack(pop) // *pic == pic, else NULL for failed. // // returns false if found but had a format error, else true for either OK or not-found (there's a reason for this) // void LoadTGA ( const char *name, byte **pic, int *width, int *height) { char sErrorString[1024]; bool bFormatErrors = false; // these don't need to be declared or initialised until later, but the compiler whines that 'goto' skips them. // byte *pRGBA = NULL; byte *pOut = NULL; byte *pIn = NULL; *pic = NULL; #define TGA_FORMAT_ERROR(blah) {sprintf(sErrorString,blah); bFormatErrors = true; goto TGADone;} //#define TGA_FORMAT_ERROR(blah) ri.Error( ERR_DROP, blah ); // // load the file // byte *pTempLoadedBuffer = 0; ri.FS_ReadFile ( ( char * ) name, (void **)&pTempLoadedBuffer); if (!pTempLoadedBuffer) { return; } TGAHeader_t *pHeader = (TGAHeader_t *) pTempLoadedBuffer; if (pHeader->byColourmapType!=0) { TGA_FORMAT_ERROR("LoadTGA: colourmaps not supported\n" ); } if (pHeader->byImageType != 2 && pHeader->byImageType != 3 && pHeader->byImageType != 10) { TGA_FORMAT_ERROR("LoadTGA: Only type 2 (RGB), 3 (gray), and 10 (RLE-RGB) images supported\n"); } if (pHeader->w1stColourMapEntry != 0) { TGA_FORMAT_ERROR("LoadTGA: colourmaps not supported\n" ); } if (pHeader->wColourMapLength !=0 && pHeader->wColourMapLength != 256) { TGA_FORMAT_ERROR("LoadTGA: ColourMapLength must be either 0 or 256\n" ); } if (pHeader->byColourMapEntrySize != 0 && pHeader->byColourMapEntrySize != 24) { TGA_FORMAT_ERROR("LoadTGA: ColourMapEntrySize must be either 0 or 24\n" ); } if ( ( pHeader->byImagePlanes != 24 && pHeader->byImagePlanes != 32) && (pHeader->byImagePlanes != 8 && pHeader->byImageType != 3)) { TGA_FORMAT_ERROR("LoadTGA: Only type 2 (RGB), 3 (gray), and 10 (RGB) TGA images supported\n"); } if ((pHeader->byScanLineOrder&0x30)!=0x00 && (pHeader->byScanLineOrder&0x30)!=0x10 && (pHeader->byScanLineOrder&0x30)!=0x20 && (pHeader->byScanLineOrder&0x30)!=0x30 ) { TGA_FORMAT_ERROR("LoadTGA: ScanLineOrder must be either 0x00,0x10,0x20, or 0x30\n"); } // these last checks are so i can use ID's RLE-code. I don't dare fiddle with it or it'll probably break... // if ( pHeader->byImageType == 10) { if ((pHeader->byScanLineOrder & 0x30) != 0x00) { TGA_FORMAT_ERROR("LoadTGA: RLE-RGB Images (type 10) must be in bottom-to-top format\n"); } if (pHeader->byImagePlanes != 24 && pHeader->byImagePlanes != 32) // probably won't happen, but avoids compressed greyscales? { TGA_FORMAT_ERROR("LoadTGA: RLE-RGB Images (type 10) must be 24 or 32 bit\n"); } } // now read the actual bitmap in... // // Image descriptor bytes // bits 0-3 = # attr bits (alpha chan) // bits 4-5 = pixel order/dir // bits 6-7 scan line interleave (00b=none,01b=2way interleave,10b=4way) // int iYStart,iXStart,iYStep,iXStep; switch(pHeader->byScanLineOrder & 0x30) { case 0x00: // left to right, bottom to top iXStart = 0; iXStep = 1; iYStart = pHeader->wImageHeight-1; iYStep = -1; break; case 0x10: // right to left, bottom to top iXStart = pHeader->wImageWidth-1; iXStep = -1; iYStart = pHeader->wImageHeight-1; iYStep = -1; break; case 0x20: // left to right, top to bottom iXStart = 0; iXStep = 1; iYStart = 0; iYStep = 1; break; case 0x30: // right to left, top to bottom iXStart = pHeader->wImageWidth-1; iXStep = -1; iYStart = 0; iYStep = 1; break; } // feed back the results... // if (width) *width = pHeader->wImageWidth; if (height) *height = pHeader->wImageHeight; pRGBA = (byte *) ri.Malloc (pHeader->wImageWidth * pHeader->wImageHeight * 4); *pic = pRGBA; pOut = pRGBA; pIn = pTempLoadedBuffer + sizeof(*pHeader); // I don't know if this ID-thing here is right, since comments that I've seen are at the end of the file, // with a zero in this field. However, may as well... // if (pHeader->byIDFieldLength != 0) pIn += pHeader->byIDFieldLength; // skip TARGA image comment byte red,green,blue,alpha; if ( pHeader->byImageType == 2 || pHeader->byImageType == 3 ) // RGB or greyscale { for (int y=iYStart, iYCount=0; iYCountwImageHeight; y+=iYStep, iYCount++) { pOut = pRGBA + y * pHeader->wImageWidth *4; for (int x=iXStart, iXCount=0; iXCountwImageWidth; x+=iXStep, iXCount++) { switch (pHeader->byImagePlanes) { case 8: blue = *pIn++; green = blue; red = blue; *pOut++ = red; *pOut++ = green; *pOut++ = blue; *pOut++ = 255; break; case 24: blue = *pIn++; green = *pIn++; red = *pIn++; *pOut++ = red; *pOut++ = green; *pOut++ = blue; *pOut++ = 255; break; case 32: blue = *pIn++; green = *pIn++; red = *pIn++; alpha = *pIn++; *pOut++ = red; *pOut++ = green; *pOut++ = blue; *pOut++ = alpha; break; default: assert(0); // if we ever hit this, someone deleted a header check higher up TGA_FORMAT_ERROR("LoadTGA: Image can only have 8, 24 or 32 planes for RGB/greyscale\n"); break; } } } } else if (pHeader->byImageType == 10) // RLE-RGB { // I've no idea if this stuff works, I normally reject RLE targas, but this is from ID's code // so maybe I should try and support it... // byte packetHeader, packetSize, j; for (int y = pHeader->wImageHeight-1; y >= 0; y--) { pOut = pRGBA + y * pHeader->wImageWidth *4; for (int x=0; xwImageWidth;) { packetHeader = *pIn++; packetSize = 1 + (packetHeader & 0x7f); if (packetHeader & 0x80) // run-length packet { switch (pHeader->byImagePlanes) { case 24: blue = *pIn++; green = *pIn++; red = *pIn++; alpha = 255; break; case 32: blue = *pIn++; green = *pIn++; red = *pIn++; alpha = *pIn++; break; default: assert(0); // if we ever hit this, someone deleted a header check higher up TGA_FORMAT_ERROR("LoadTGA: RLE-RGB can only have 24 or 32 planes\n"); break; } for (j=0; jwImageWidth) // run spans across rows { x = 0; if (y > 0) y--; else goto breakOut; pOut = pRGBA + y * pHeader->wImageWidth * 4; } } } else { // non run-length packet for (j=0; jbyImagePlanes) { case 24: blue = *pIn++; green = *pIn++; red = *pIn++; *pOut++ = red; *pOut++ = green; *pOut++ = blue; *pOut++ = 255; break; case 32: blue = *pIn++; green = *pIn++; red = *pIn++; alpha = *pIn++; *pOut++ = red; *pOut++ = green; *pOut++ = blue; *pOut++ = alpha; break; default: assert(0); // if we ever hit this, someone deleted a header check higher up TGA_FORMAT_ERROR("LoadTGA: RLE-RGB can only have 24 or 32 planes\n"); break; } x++; if (x == pHeader->wImageWidth) // pixel packet run spans across rows { x = 0; if (y > 0) y--; else goto breakOut; pOut = pRGBA + y * pHeader->wImageWidth * 4; } } } } breakOut:; } } TGADone: ri.FS_FreeFile (pTempLoadedBuffer); if (bFormatErrors) { ErrorBox(va("Error parsing file \"%s\"!\n\n",name, sErrorString)); } } /* ============== LoadPCX ============== */ static void LoadPCX ( const char *filename, byte **pic, byte **palette, int *width, int *height) { byte *raw; pcx_t *pcx; int x, y; int len; int dataByte, runLength; byte *out, *pix; int xmax, ymax; *pic = NULL; *palette = NULL; // // load the file // len = ri.FS_ReadFile( ( char * ) filename, (void **)&raw); if (!raw) { return; } // // parse the PCX file // pcx = (pcx_t *)raw; raw = &pcx->data; xmax = LittleShort(pcx->xmax); ymax = LittleShort(pcx->ymax); if (pcx->manufacturer != 0x0a || pcx->version != 5 || pcx->encoding != 1 || pcx->bits_per_pixel != 8 || xmax >= 1024 || ymax >= 1024) { ri.Printf (PRINT_ALL, "Bad pcx file %s (%i x %i) (%i x %i)\n", filename, xmax+1, ymax+1, pcx->xmax, pcx->ymax); return; } out = (unsigned char *)ri.Malloc ( (ymax+1) * (xmax+1) ); *pic = out; pix = out; if (palette) { *palette = (unsigned char *)ri.Malloc(768); memcpy (*palette, (byte *)pcx + len - 768, 768); } if (width) *width = xmax+1; if (height) *height = ymax+1; // FIXME: use bytes_per_line here? for (y=0 ; y<=ymax ; y++, pix += xmax+1) { for (x=0 ; x<=xmax ; ) { dataByte = *raw++; if((dataByte & 0xC0) == 0xC0) { runLength = dataByte & 0x3F; dataByte = *raw++; } else runLength = 1; while(runLength-- > 0) pix[x++] = dataByte; } } if ( raw - (byte *)pcx > len) { ri.Printf (PRINT_DEVELOPER, "PCX file %s was malformed", filename); ri.Free (*pic); *pic = NULL; } ri.FS_FreeFile (pcx); } /* ============== LoadPCX32 ============== */ static void LoadPCX32 ( const char *filename, byte **pic, int *width, int *height) { byte *palette; byte *pic8; int i, c, p; byte *pic32; LoadPCX (filename, &pic8, &palette, width, height); if (!pic8) { *pic = NULL; return; } c = (*width) * (*height); pic32 = *pic = (unsigned char *)ri.Malloc(4 * c ); for (i = 0 ; i < c ; i++) { p = pic8[i]; pic32[0] = palette[p*3]; pic32[1] = palette[p*3 + 1]; pic32[2] = palette[p*3 + 2]; pic32[3] = 255; pic32 += 4; } ri.Free (pic8); ri.Free (palette); } /* ================= R_LoadImage Loads any of the supported image types into a cannonical 32 bit format. ================= */ char g_sImageExtension[MAX_QPATH]; void R_LoadImage2( const char *name, byte **pic, int *width, int *height ) { int len; *pic = NULL; *width = 0; *height = 0; len = strlen(name); if (len<5) { return; } char *psExt = strstr(name,"."); strcpy(g_sImageExtension,psExt?psExt:""); if ( !Q_stricmp( name+len-4, ".tga" ) ) { LoadTGA( name, pic, width, height ); // try tga first if (!*pic) { // char altname[MAX_QPATH]; // try jpg in place of tga strcpy( altname, name ); len = strlen( altname ); altname[len-3] = 'j'; altname[len-2] = 'p'; altname[len-1] = 'g'; LoadJPG( altname, pic, width, height ); strcpy(g_sImageExtension,".jpg"); } } else if ( !Q_stricmp(name+len-4, ".pcx") ) { LoadPCX32( name, pic, width, height ); } else if ( !Q_stricmp( name+len-4, ".bmp" ) ) { LoadBMP( name, pic, width, height ); } else if ( !Q_stricmp( name+len-4, ".jpg" ) ) { LoadJPG( name, pic, width, height ); } else if ( !Q_stricmp( name+len-4, ".png" ) ) { LoadPNG32(name, pic , width, height, NULL); } } // feeder version of above because of John's not having extensions on anymore (but it might do... sigh) // bool g_bReportImageLoadErrors = false; void R_LoadImage1( const char *name, byte **pic, int *width, int *height ) { int len; char work[MAX_QPATH]; *pic = NULL; *width = 0; *height = 0; len = strlen(name); if(len >= MAX_QPATH) { return; } if (len < 5) { return; } if (name[1] == ':') { if (g_bReportImageLoadErrors) { g_bReportImageLoadErrors = GetYesNo(va("Filename \"%s\" has a full-path in it, paths should be local only!\n\nContinue reporting image-load errors?",name)); } return; } // if the file has an extension then try it first... // if (name[strlen(name)-4] == '.') { R_LoadImage2( name, pic, width, height ); if (*pic) return; } // else if not found, or no extension specified, then force some on in SOF2 order... // COM_StripExtension(name, work); COM_DefaultExtension( work, sizeof( work ), ".png" ); LoadPNG32( work, pic, width, height, NULL ); // try png first if(*pic) { strcpy(g_sImageExtension,".png"); return; } COM_StripExtension(name, work); COM_DefaultExtension( work, sizeof( work ), ".tga" ); LoadTGA( work, pic, width, height); // try tga next if(*pic) { strcpy(g_sImageExtension,".tga"); return; } COM_StripExtension(name, work); COM_DefaultExtension( work, sizeof( work ), ".jpg" ); LoadJPG( work, pic, width, height ); // try jpg last if(*pic) { strcpy(g_sImageExtension,".jpg"); return; } // Image loading failed... // if (g_bReportImageLoadErrors) { g_bReportImageLoadErrors = GetYesNo(va("R_LoadImage(): Couldn't read image:\n\n\"%s\"\n\n... continue receiving this message for this model?", name)); } } bool gbInImageLoader = false; void R_LoadImage( const char *name, byte **pic, int *width, int *height ) { // only attempt load if not the special case... // if (stricmp(name,"[NoMaterial]")) { // thanks, Jake... // char sTempName[1024]={0}; if (bXMenPathHack) strcpy(sTempName,"textures/"); strcat(sTempName,name); gbInImageLoader = true; { R_LoadImage1( sTempName, pic, width, height ); } gbInImageLoader = false; } else { // special case, so just zap whatever the last extension found was, else it gets strcat'd on return... // g_sImageExtension[0] = '\0'; } } ///////////////////////// eof //////////////////////////