/* Copyright (C) 1999-2007 id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GtkRadiant is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ // lbmlib.c // by Hydra - hydra@hydras-world.com // // This module is based on the image module, but just more stripped down. // it still currently supports TGA file loading, even though this is not // required for HalfLife support (unless MD2 files use TGA's) // // use the #defines in imagehl.h to enable/disable the various formats. // // HLW = Half-Life-WAD, I don't know if the actual in data in the WAD files // has it's own name, so I'm just calling the individal textures .HLW files :) // // Thanks to the guys that made Wally for releasing an example WAD loader. // without it this would not have been possible. #include #include #include #include "imagehl.h" #include "lbmlib.h" #include #define Sys_Printf g_FuncTable.m_pfnSysPrintf #define Sys_FPrintf g_FuncTable.m_pfnSysFPrintf #ifdef USE_IDSP /* ============================================================================ IDSP IMAGE (.spr files) Some code copied straight from the Q1 source, also used the HalfLife SDK as a reference. ============================================================================ */ typedef enum {ST_SYNC=0, ST_RAND } synctype_t; typedef enum { SPR_SINGLE=0, SPR_GROUP } spriteframetype_t; typedef struct dspriteheader_s { int ident; int version; } dspriteheader_t; // Quake1 typedef struct { int type; float boundingradius; int width; int height; int numframes; float beamlength; synctype_t synctype; } dspritev1_t; // Halflife typedef struct { int type; int texFormat; float boundingradius; int width; int height; int numframes; float beamlength; synctype_t synctype; } dspritev2_t; typedef struct { int origin[2]; int width; int height; } dspriteframe_t; typedef struct { short type; } dspriteframetype_t; /* typedef struct { byte rgb[256][3]; } dpalette_t; */ #define IDSPRITEHEADER (('P'<<24)+('S'<<16)+('D'<<8)+'I') // little-endian "IDSP" /* ============= LoadIDSP ============= */ static void LoadIDSP (const char *name, byte ** pic, int *width, int *height) { byte *buffer; byte *buf_p; unsigned int length; int columns, rows, numPixels; byte *pixbuf; int row, column; byte *bmpRGBA; byte *palette; unsigned char red, green, blue, alphabyte; dspriteheader_t *header; dspritev1_t *pinv1; dspritev2_t *pinv2; dspriteframetype_t *pframetype; int version; int numframes; int size; spriteframetype_t frametype; dspriteframe_t *spriteframe; *pic = NULL; // // load the file // length = vfsLoadFile ((char *) name, (void **) &buffer); if (length == (unsigned int) -1) return; header = (dspriteheader_t *)buffer; if (header->ident != IDSPRITEHEADER) { Sys_Printf ("WARNING: %s has wrong header\n"); vfsFreeFile (buffer); return; } version = header->version; if (version != 1 && version != 2 ) { Sys_Printf ("WARNING: %s has wrong version number " "(%i should be 1 or 2)\n", name, version); vfsFreeFile (buffer); return; } // initialise variables depending on the sprite version. switch (version) { case 1: pinv1 = (dspritev1_t *)(header+1); numframes = pinv1->numframes; columns = pinv1->width; rows = pinv1->height; pframetype = (dspriteframetype_t *)(pinv1 + 1); break; case 2: pinv2 = (dspritev2_t *)(header+1); numframes = pinv2->numframes; columns = pinv2->width; rows = pinv2->height; pframetype = (dspriteframetype_t *)(pinv2 + 1); break; } if (numframes > 1) Sys_Printf ("WARNING: %s has multiple frames, only the first frame will be used.\n", name); // palette = buffer+mipdatasize+2; // buf_p = buffer+lpMip->offsets[0]; numPixels = columns * rows; if (width) *width = columns; if (height) *height = rows; bmpRGBA = reinterpret_cast < unsigned char *>(g_malloc (numPixels * 4)); *pic = bmpRGBA; #ifdef DEBUG frametype = spriteframetype_t(LittleLong(pframetype->type)); if (frametype == SPR_SINGLE) { Sys_Printf("Single Frame\n"); } else if (frametype == SPR_GROUP) { Sys_Printf("Group of Frames\n"); } else { Sys_Printf("Bleh!\n"); // <-- we always get this, wtf! } #endif palette = (byte *)(pframetype+1); spriteframe = (dspriteframe_t *)(palette + (256*3) + 4); // what are those 4 extra bytes ? what's missing ? buf_p = (byte *)(spriteframe + 1); int temp; temp = buf_p - buffer; for (row = 0; row < rows; row++) { pixbuf = bmpRGBA + row * columns * 4; for (column = 0; column < columns; column++) { int palIndex; palIndex = *buf_p++; red = *(palette+(palIndex*3)); green = *(palette+(palIndex*3)+1); blue = *(palette+(palIndex*3)+2); // HalfLife engine makes pixels that are BLUE transparent. (RGB = 0x0000FF) // So show them that way in the editor. if (blue == 0xff && red == 0x00 && green == 0x00) { alphabyte = 0xff; //FIXME: backwards? (so sprite models to render correctly) blue = 0x00; // don't set the resulting pixel to blue } else { alphabyte = 0x00; //FIXME: backwards? (so sprite models to render correctly) } *pixbuf++ = red; *pixbuf++ = green; *pixbuf++ = blue; *pixbuf++ = alphabyte; } } vfsFreeFile (buffer); } #endif #ifdef USE_HLW /* ============================================================================ HLW IMAGE HalfLife WAD files contain files that look like this: Mip section First mip Mip header First mip (width * height) Second mip (width * height / 4) Third mip (width * height / 16) Fourth mip (width * height / 64) Palette size (WORD) Palette (Palette size * 3) Padding (WORD) ============================================================================ */ #define GET_MIP_DATA_SIZE(WIDTH, HEIGHT) (sizeof(WAD3_MIP) + (WIDTH * HEIGHT) + (WIDTH * HEIGHT / 4) + (WIDTH * HEIGHT / 16) + (WIDTH * HEIGHT / 64)) typedef struct { char name[16]; DWORD width, height; DWORD offsets[4]; // four mip maps stored } WAD3_MIP, *LPWAD3_MIP; /* ========================================================= HLW LOADING Hydra: this code isn't bullet proof and probably won't like corrupt WAD files, but it works for now. TODO: make it more robust. ========================================================= */ /* ============= LoadHLW ============= */ static void LoadHLW (const char *name, byte ** pic, int *width, int *height) { byte *buffer; byte *buf_p; unsigned int length; unsigned long mipdatasize; int columns, rows, numPixels; byte *pixbuf; int row, column; byte *bmpRGBA; byte *palette; LPWAD3_MIP lpMip; unsigned char red, green, blue, alphabyte; *pic = NULL; // // load the file // length = vfsLoadFile ((char *) name, (void **) &buffer); if (length == (unsigned int) -1) return; lpMip = (LPWAD3_MIP)buffer; mipdatasize = GET_MIP_DATA_SIZE(lpMip->width,lpMip->height); palette = buffer+mipdatasize+2; buf_p = buffer+lpMip->offsets[0]; columns = lpMip->width; rows = lpMip->height; numPixels = columns * rows; if (width) *width = columns; if (height) *height = rows; bmpRGBA = reinterpret_cast < unsigned char *>(g_malloc (numPixels * 4)); *pic = bmpRGBA; for (row = 0; row < rows; row++) { pixbuf = bmpRGBA + row * columns * 4; for (column = 0; column < columns; column++) { int palIndex; palIndex = *buf_p++; red = *(palette+(palIndex*3)); green = *(palette+(palIndex*3)+1); blue = *(palette+(palIndex*3)+2); // HalfLife engine makes pixels that are BLUE transparent. // So show them that way in the editor. if (blue == 0xff && red == 0x00 && green == 0x00) { alphabyte = 0x00; blue = 0x00; // don't set the resulting pixel to blue } else { alphabyte = 0xff; } *pixbuf++ = red; *pixbuf++ = green; *pixbuf++ = blue; *pixbuf++ = alphabyte; } } vfsFreeFile (buffer); } #endif #ifdef USE_MIP /* ============================================================================ MIP IMAGE Quake WAD files contain miptex files that look like this: Mip section First mip Mip header First mip (width * height) Second mip (width * height / 4) Third mip (width * height / 16) Fourth mip (width * height / 64) ============================================================================ */ /* ========================================================= MIP LOADING LordHavoc: this code is based on the HLW code above. ========================================================= */ static const byte quakepalette[768] = { 0x00,0x00,0x00, 0x0f,0x0f,0x0f, 0x1f,0x1f,0x1f, 0x2f,0x2f,0x2f, 0x3f,0x3f,0x3f, 0x4b,0x4b,0x4b, 0x5b,0x5b,0x5b, 0x6b,0x6b,0x6b, 0x7b,0x7b,0x7b, 0x8b,0x8b,0x8b, 0x9b,0x9b,0x9b, 0xab,0xab,0xab, 0xbb,0xbb,0xbb, 0xcb,0xcb,0xcb, 0xdb,0xdb,0xdb, 0xeb,0xeb,0xeb, 0x0f,0x0b,0x07, 0x17,0x0f,0x0b, 0x1f,0x17,0x0b, 0x27,0x1b,0x0f, 0x2f,0x23,0x13, 0x37,0x2b,0x17, 0x3f,0x2f,0x17, 0x4b,0x37,0x1b, 0x53,0x3b,0x1b, 0x5b,0x43,0x1f, 0x63,0x4b,0x1f, 0x6b,0x53,0x1f, 0x73,0x57,0x1f, 0x7b,0x5f,0x23, 0x83,0x67,0x23, 0x8f,0x6f,0x23, 0x0b,0x0b,0x0f, 0x13,0x13,0x1b, 0x1b,0x1b,0x27, 0x27,0x27,0x33, 0x2f,0x2f,0x3f, 0x37,0x37,0x4b, 0x3f,0x3f,0x57, 0x47,0x47,0x67, 0x4f,0x4f,0x73, 0x5b,0x5b,0x7f, 0x63,0x63,0x8b, 0x6b,0x6b,0x97, 0x73,0x73,0xa3, 0x7b,0x7b,0xaf, 0x83,0x83,0xbb, 0x8b,0x8b,0xcb, 0x00,0x00,0x00, 0x07,0x07,0x00, 0x0b,0x0b,0x00, 0x13,0x13,0x00, 0x1b,0x1b,0x00, 0x23,0x23,0x00, 0x2b,0x2b,0x07, 0x2f,0x2f,0x07, 0x37,0x37,0x07, 0x3f,0x3f,0x07, 0x47,0x47,0x07, 0x4b,0x4b,0x0b, 0x53,0x53,0x0b, 0x5b,0x5b,0x0b, 0x63,0x63,0x0b, 0x6b,0x6b,0x0f, 0x07,0x00,0x00, 0x0f,0x00,0x00, 0x17,0x00,0x00, 0x1f,0x00,0x00, 0x27,0x00,0x00, 0x2f,0x00,0x00, 0x37,0x00,0x00, 0x3f,0x00,0x00, 0x47,0x00,0x00, 0x4f,0x00,0x00, 0x57,0x00,0x00, 0x5f,0x00,0x00, 0x67,0x00,0x00, 0x6f,0x00,0x00, 0x77,0x00,0x00, 0x7f,0x00,0x00, 0x13,0x13,0x00, 0x1b,0x1b,0x00, 0x23,0x23,0x00, 0x2f,0x2b,0x00, 0x37,0x2f,0x00, 0x43,0x37,0x00, 0x4b,0x3b,0x07, 0x57,0x43,0x07, 0x5f,0x47,0x07, 0x6b,0x4b,0x0b, 0x77,0x53,0x0f, 0x83,0x57,0x13, 0x8b,0x5b,0x13, 0x97,0x5f,0x1b, 0xa3,0x63,0x1f, 0xaf,0x67,0x23, 0x23,0x13,0x07, 0x2f,0x17,0x0b, 0x3b,0x1f,0x0f, 0x4b,0x23,0x13, 0x57,0x2b,0x17, 0x63,0x2f,0x1f, 0x73,0x37,0x23, 0x7f,0x3b,0x2b, 0x8f,0x43,0x33, 0x9f,0x4f,0x33, 0xaf,0x63,0x2f, 0xbf,0x77,0x2f, 0xcf,0x8f,0x2b, 0xdf,0xab,0x27, 0xef,0xcb,0x1f, 0xff,0xf3,0x1b, 0x0b,0x07,0x00, 0x1b,0x13,0x00, 0x2b,0x23,0x0f, 0x37,0x2b,0x13, 0x47,0x33,0x1b, 0x53,0x37,0x23, 0x63,0x3f,0x2b, 0x6f,0x47,0x33, 0x7f,0x53,0x3f, 0x8b,0x5f,0x47, 0x9b,0x6b,0x53, 0xa7,0x7b,0x5f, 0xb7,0x87,0x6b, 0xc3,0x93,0x7b, 0xd3,0xa3,0x8b, 0xe3,0xb3,0x97, 0xab,0x8b,0xa3, 0x9f,0x7f,0x97, 0x93,0x73,0x87, 0x8b,0x67,0x7b, 0x7f,0x5b,0x6f, 0x77,0x53,0x63, 0x6b,0x4b,0x57, 0x5f,0x3f,0x4b, 0x57,0x37,0x43, 0x4b,0x2f,0x37, 0x43,0x27,0x2f, 0x37,0x1f,0x23, 0x2b,0x17,0x1b, 0x23,0x13,0x13, 0x17,0x0b,0x0b, 0x0f,0x07,0x07, 0xbb,0x73,0x9f, 0xaf,0x6b,0x8f, 0xa3,0x5f,0x83, 0x97,0x57,0x77, 0x8b,0x4f,0x6b, 0x7f,0x4b,0x5f, 0x73,0x43,0x53, 0x6b,0x3b,0x4b, 0x5f,0x33,0x3f, 0x53,0x2b,0x37, 0x47,0x23,0x2b, 0x3b,0x1f,0x23, 0x2f,0x17,0x1b, 0x23,0x13,0x13, 0x17,0x0b,0x0b, 0x0f,0x07,0x07, 0xdb,0xc3,0xbb, 0xcb,0xb3,0xa7, 0xbf,0xa3,0x9b, 0xaf,0x97,0x8b, 0xa3,0x87,0x7b, 0x97,0x7b,0x6f, 0x87,0x6f,0x5f, 0x7b,0x63,0x53, 0x6b,0x57,0x47, 0x5f,0x4b,0x3b, 0x53,0x3f,0x33, 0x43,0x33,0x27, 0x37,0x2b,0x1f, 0x27,0x1f,0x17, 0x1b,0x13,0x0f, 0x0f,0x0b,0x07, 0x6f,0x83,0x7b, 0x67,0x7b,0x6f, 0x5f,0x73,0x67, 0x57,0x6b,0x5f, 0x4f,0x63,0x57, 0x47,0x5b,0x4f, 0x3f,0x53,0x47, 0x37,0x4b,0x3f, 0x2f,0x43,0x37, 0x2b,0x3b,0x2f, 0x23,0x33,0x27, 0x1f,0x2b,0x1f, 0x17,0x23,0x17, 0x0f,0x1b,0x13, 0x0b,0x13,0x0b, 0x07,0x0b,0x07, 0xff,0xf3,0x1b, 0xef,0xdf,0x17, 0xdb,0xcb,0x13, 0xcb,0xb7,0x0f, 0xbb,0xa7,0x0f, 0xab,0x97,0x0b, 0x9b,0x83,0x07, 0x8b,0x73,0x07, 0x7b,0x63,0x07, 0x6b,0x53,0x00, 0x5b,0x47,0x00, 0x4b,0x37,0x00, 0x3b,0x2b,0x00, 0x2b,0x1f,0x00, 0x1b,0x0f,0x00, 0x0b,0x07,0x00, 0x00,0x00,0xff, 0x0b,0x0b,0xef, 0x13,0x13,0xdf, 0x1b,0x1b,0xcf, 0x23,0x23,0xbf, 0x2b,0x2b,0xaf, 0x2f,0x2f,0x9f, 0x2f,0x2f,0x8f, 0x2f,0x2f,0x7f, 0x2f,0x2f,0x6f, 0x2f,0x2f,0x5f, 0x2b,0x2b,0x4f, 0x23,0x23,0x3f, 0x1b,0x1b,0x2f, 0x13,0x13,0x1f, 0x0b,0x0b,0x0f, 0x2b,0x00,0x00, 0x3b,0x00,0x00, 0x4b,0x07,0x00, 0x5f,0x07,0x00, 0x6f,0x0f,0x00, 0x7f,0x17,0x07, 0x93,0x1f,0x07, 0xa3,0x27,0x0b, 0xb7,0x33,0x0f, 0xc3,0x4b,0x1b, 0xcf,0x63,0x2b, 0xdb,0x7f,0x3b, 0xe3,0x97,0x4f, 0xe7,0xab,0x5f, 0xef,0xbf,0x77, 0xf7,0xd3,0x8b, 0xa7,0x7b,0x3b, 0xb7,0x9b,0x37, 0xc7,0xc3,0x37, 0xe7,0xe3,0x57, 0x7f,0xbf,0xff, 0xab,0xe7,0xff, 0xd7,0xff,0xff, 0x67,0x00,0x00, 0x8b,0x00,0x00, 0xb3,0x00,0x00, 0xd7,0x00,0x00, 0xff,0x00,0x00, 0xff,0xf3,0x93, 0xff,0xf7,0xc7, 0xff,0xff,0xff, 0x9f,0x5b,0x53 }; /* ============= LoadMIP ============= */ static void LoadMIP (const char *name, byte ** pic, int *width, int *height) { byte *buffer; byte *buf_p; unsigned int length, palettelength; unsigned long mipdatasize; int columns, rows, numPixels; byte *pixbuf; int i; byte *bmpRGBA; byte *loadedpalette; const byte *palette; LPWAD3_MIP lpMip; *pic = NULL; loadedpalette = NULL; // // load the file // length = vfsLoadFile ((char *) name, (void **) &buffer); if (length == (unsigned int) -1) return; lpMip = (LPWAD3_MIP)buffer; mipdatasize = GET_MIP_DATA_SIZE(lpMip->width,lpMip->height); palettelength = vfsLoadFile ("textures/palette.lmp", (void **) &loadedpalette); if (palettelength == 768) palette = loadedpalette; else { loadedpalette = NULL; palette = quakepalette; } buf_p = buffer+lpMip->offsets[0]; columns = lpMip->width; rows = lpMip->height; numPixels = columns * rows; if (width) *width = columns; if (height) *height = rows; //Sys_Printf("lpMip->width = %i, lpMip->height = %i, lpMip->offsets[0] = %i, lpMip->offsets[1] = %i, lpMip->offsets[2] = %i, lpMip->offsets[3] = %i, numPixels = %i\n", lpMip->width, lpMip->height, lpMip->offsets[0], lpMip->offsets[1], lpMip->offsets[2], lpMip->offsets[3], numPixels); //for (i = 0; i < sizeof(*lpMip); i++) // Sys_Printf("%02x", (int) ((unsigned char *)lpMip)[i]); bmpRGBA = reinterpret_cast < unsigned char *>(g_malloc (numPixels * 4)); *pic = bmpRGBA; pixbuf = bmpRGBA; for (i = 0; i < numPixels; i++) { int palIndex = *buf_p++; *pixbuf++ = palette[palIndex*3]; *pixbuf++ = palette[palIndex*3+1]; *pixbuf++ = palette[palIndex*3+2]; *pixbuf++ = 0xff; } vfsFreeFile (buffer); if (loadedpalette != NULL) vfsFreeFile (loadedpalette); } #endif /* ================= LoadImage Loads any of the supported image types into a cannonical 32 bit format. ================= */ void LoadImage (const char *name, byte ** pic, int *width, int *height) { int len; *pic = NULL; *width = 0; *height = 0; len = strlen (name); if (len < 5) { return; } #ifdef USE_HLW if (*pic == NULL && !g_strcasecmp (name + len - 4, ".hlw")) { LoadHLW (name, pic, width, height); } #endif #ifdef USE_MIP if (*pic == NULL && !g_strcasecmp (name + len - 4, ".mip")) { LoadMIP (name, pic, width, height); } #endif #ifdef USE_IDSP if (*pic == NULL && !g_strcasecmp (name + len - 4, ".spr")) { LoadIDSP (name, pic, width, height); } #endif }