From bf912f84f47855d40f4aea113a8ef286d7ad4354 Mon Sep 17 00:00:00 2001 From: g-cont Date: Thu, 20 Dec 2007 00:00:00 +0300 Subject: [PATCH] 20 Dec 2007 --- changelog.log | 13 + launch/common/filesystem.c | 89 +- launch/common/image.h | 3 - launch/common/imglib.c | 490 ++++---- launch/common/imglib.c.old | 2252 ++++++++++++++++++++++++++++++++++++ public/stdref.h | 18 +- 6 files changed, 2529 insertions(+), 336 deletions(-) create mode 100644 launch/common/imglib.c.old diff --git a/changelog.log b/changelog.log index b1722e9e..1099d881 100644 --- a/changelog.log +++ b/changelog.log @@ -10,6 +10,19 @@ SV_Move SV_Trace SV_ClipMoveToEntity CM_BoxTrace SV_ClipToLinks SV_ClipMoveToEntities +case TYPE_Q1MIP: + if(FS_Seek(w->file, w->lumps[i].filepos - w->lumps[i].size, SEEK_SET)) + { + MsgDev(D_ERROR, "FS_OpenWad3File: corrupt WAD3 file\n"); + return NULL; + } +default: +if(FS_Seek(w->file, w->lumps[i].filepos, SEEK_SET)) +{ + MsgDev(D_ERROR, "FS_OpenWad3File: corrupt WAD3 file\n"); + return NULL; +} + studioframeadvance studiorewindframe diff --git a/launch/common/filesystem.c b/launch/common/filesystem.c index 398d15f7..1bf290b3 100644 --- a/launch/common/filesystem.c +++ b/launch/common/filesystem.c @@ -75,6 +75,7 @@ typedef struct wadfile_s { char name[64]; int numlumps; + int type; // contains ident file_t *file; dlumpinfo_t *lumps; } wadfile_t; @@ -981,6 +982,7 @@ static bool FS_AddWad3File( const char *filename ) int infotableofs; file_t *file; int i, numlumps; + char type[16]; if(!fs_searchwads) // start from scratch fs_searchwads = Mem_CreateArray( fs_mempool, sizeof(wadfile_t), 16 ); @@ -1005,12 +1007,20 @@ static bool FS_AddWad3File( const char *filename ) FS_Close( file ); return false; } - if( header.ident != IDWAD2HEADER && header.ident != IDWAD3HEADER ) + + switch( header.ident ) { + case IDWAD2HEADER: + com_strncpy( type, "Quake1", 16 ); + break; + case IDWAD3HEADER: + com_strncpy( type, "Half-Life", 16 ); + break; + default: MsgDev(D_ERROR, "FS_AddWad3File: %s have invalid ident\n", filename ); FS_Close( file ); return false; - } + } numlumps = LittleLong( header.numlumps ); if( numlumps > MAX_FILES_IN_PACK ) @@ -1031,6 +1041,7 @@ static bool FS_AddWad3File( const char *filename ) w = Mem_AllocElement( fs_searchwads ); com_strncpy( w->name, filename, sizeof(w->name)); w->file = file; + w->type = header.ident; w->numlumps = numlumps; w->lumps = Mem_Alloc( fs_mempool, w->numlumps * sizeof(dlumpinfo_t)); @@ -1054,7 +1065,7 @@ static bool FS_AddWad3File( const char *filename ) com_strnlwr(w->lumps[i].name, w->lumps[i].name, sizeof(w->lumps[i].name)); } // and leaves the file open - MsgDev(D_INFO, "Adding wadfile: %s (%i lumps)\n", filename, numlumps ); + MsgDev(D_INFO, "Adding %s wadfile: %s (%i lumps)\n", type, filename, numlumps ); return true; } @@ -1766,7 +1777,7 @@ FS_OpenWad3File Look for a file in the loaded wadfiles and returns buffer with image lump =========== */ -static byte *FS_OpenWad3File( const char *name, fs_offset_t *filesizeptr, int matchtype ) +static byte *FS_OpenWadFile( const char *name, fs_offset_t *filesizeptr, int matchtype ) { uint i, k; wadfile_t *w; @@ -1797,49 +1808,22 @@ static byte *FS_OpenWad3File( const char *name, fs_offset_t *filesizeptr, int ma { if(!com_strcmp(texname, w->lumps[i].name)) { - mip_t *tex; - lmp_t *lmp; - qpic_t *pic; - qfont_t *fnt; - fs_offset_t size; byte *buf; + size_t size; - if(matchtype != (int)w->lumps[i].type) break; // try next wad + if(matchtype != (int)w->lumps[i].type) return NULL; // try next type if(FS_Seek(w->file, w->lumps[i].filepos, SEEK_SET)) { - MsgDev(D_ERROR, "FS_OpenWad3File: corrupt WAD3 file\n"); + MsgDev(D_ERROR, "FS_OpenWadFile: %s probably corrupted\n", texname ); return NULL; } - switch((int)w->lumps[i].type) - { - case TYPE_QPIC: - pic = (qpic_t *)Mem_Alloc( fs_mempool, w->lumps[i].disksize ); - size = FS_Read(w->file, pic, w->lumps[i].size ); - buf = (byte *)((qpic_t *)pic); - break; - case TYPE_QFONT: - fnt = (qfont_t *)Mem_Alloc( fs_mempool, w->lumps[i].disksize ); - size = FS_Read(w->file, fnt, w->lumps[i].disksize ); // some gfx.wad requires this - buf = (byte *)((qfont_t *)fnt); - break; - case TYPE_Q1MIP: - if(!com_stricmp( w->lumps[i].name, "conchars" )) - { - // great hack from id software - FS_Seek(w->file, w->lumps[i].filepos - w->lumps[i].size, SEEK_SET); - } - lmp = (lmp_t *)Mem_Alloc( fs_mempool, w->lumps[i].disksize ); - size = FS_Read(w->file, lmp, w->lumps[i].size ); - buf = (byte *)((lmp_t *)lmp); - case TYPE_HLMIP: - tex = (mip_t *)Mem_Alloc( fs_mempool, w->lumps[i].disksize ); - size = FS_Read(w->file, tex, w->lumps[i].size ); - buf = (byte *)((mip_t *)tex); - break; - } + buf = (byte *)Mem_Alloc( fs_mempool, w->lumps[i].disksize ); + size = FS_Read(w->file, buf, w->lumps[i].disksize ); if( size < w->lumps[i].disksize ) - MsgDev(D_WARN, "FS_OpenWad3File: lump %s is corrupt\n", texname ); - + { + MsgDev(D_WARN, "FS_OpenWadFile: lump %s is corrupt\n", texname ); + return NULL; + } if(filesizeptr) *filesizeptr = w->lumps[i].disksize; return buf; } @@ -2377,27 +2361,28 @@ byte *FS_LoadFile (const char *path, fs_offset_t *filesizeptr ) FS_Read (file, buf, filesize); FS_Close (file); } - else if(!com_stricmp("wad3", ext )) + else if(!com_stricmp("mip", ext )) { - // probably it's half-life texture - buf = FS_OpenWad3File( path, &filesize, TYPE_HLMIP ); + // mip-texture form quake1 or half-life + buf = FS_OpenWadFile( path, &filesize, TYPE_MIPTEX ); + if(!buf) buf = FS_OpenWadFile( path, &filesize, TYPE_MIPTEX2 ); } else if(!com_stricmp("lmp", ext )) { - // probably it's quake1 texture - buf = FS_OpenWad3File( path, &filesize, TYPE_Q1MIP ); + // qpic from quake1 or half-life + buf = FS_OpenWadFile( path, &filesize, TYPE_QPIC ); } - else if(!com_stricmp("qpic", ext )) + else if(!com_stricmp("fnt", ext )) { - // probably it's gfx image (q1 or hl) - buf = FS_OpenWad3File( path, &filesize, TYPE_QPIC ); + // half-life qfont lump + buf = FS_OpenWadFile( path, &filesize, TYPE_QFONT ); } - else if(!com_stricmp("qfnt", ext )) + else if(!com_stricmp("pal", ext )) { - // probably it's image from wad-file - buf = FS_OpenWad3File( path, &filesize, TYPE_QFONT ); + // quake1 palette lump + buf = FS_OpenWadFile( path, &filesize, TYPE_QPAL ); } - + if(filesizeptr) *filesizeptr = filesize; return buf; } diff --git a/launch/common/image.h b/launch/common/image.h index 29e1267c..54e04716 100644 --- a/launch/common/image.h +++ b/launch/common/image.h @@ -98,9 +98,6 @@ extern uint image_ptr; // common moveable pointer // image lib utilites extern uint *d_currentpal; -void Image_GetQ1Palette( void ); -void Image_GetQ2Palette( void ); -void Image_GetPalettePCX( byte *pal ); bool Image_Copy8bitRGBA(const byte *in, byte *out, int pixels); void Image_RoundDimensions(int *scaled_width, int *scaled_height); byte *Image_Resample(uint *in, int inwidth, int inheight, int outwidth, int outheight, int in_type); diff --git a/launch/common/imglib.c b/launch/common/imglib.c index 51fddbf5..2bf154ff 100644 --- a/launch/common/imglib.c +++ b/launch/common/imglib.c @@ -60,13 +60,13 @@ bool Image_ValidSize( char *name ) { if(image_width > 4096 || image_height > 4096 || image_width <= 0 || image_height <= 0) { - MsgWarn( "Image_ValidSize: (%s) image size out of range [%dx%d]\n", name, image_width, image_height ); + MsgWarn( "Image_ValidSize: (%s) dimensions out of range [%dx%d]\n", name, image_width, image_height ); return false; } return true; } -void Image_GetPalette( byte *pal, uint *d_table ) +void Image_SetPalette( byte *pal, uint *d_table ) { int i; byte rgba[4]; @@ -117,39 +117,39 @@ void Image_GetPalette( byte *pal, uint *d_table ) } } -void Image_GetD1Palette( void ) +void Image_GetPaletteD1( void ) { d_rendermode = LUMP_NORMAL; if(!d1palette_init) { - Image_GetPalette( palette_d1, d_8toD1table ); + Image_SetPalette( palette_d1, d_8toD1table ); d_8toD1table[255] = 247; // 247 is transparent d1palette_init = true; } d_currentpal = d_8toD1table; } -void Image_GetQ1Palette( void ) +void Image_GetPaletteQ1( void ) { d_rendermode = LUMP_NORMAL; if(!q1palette_init) { - Image_GetPalette( palette_q1, d_8toQ1table ); + Image_SetPalette( palette_q1, d_8toQ1table ); d_8toQ1table[255] = 0; // 255 is transparent q1palette_init = true; } d_currentpal = d_8toQ1table; } -void Image_GetQ2Palette( void ) +void Image_GetPaletteQ2( void ) { d_rendermode = LUMP_NORMAL; if(!q2palette_init) { - Image_GetPalette( palette_q2, d_8toQ2table ); + Image_SetPalette( palette_q2, d_8toQ2table ); d_8toQ2table[255] &= LittleLong(0xffffff); q2palette_init = true; } @@ -162,39 +162,38 @@ void Image_GetPalettePCX( byte *pal ) if(pal) { - Image_GetPalette( pal, d_8to24table ); + Image_SetPalette( pal, d_8to24table ); d_8to24table[255] &= LittleLong(0xffffff); d_currentpal = d_8to24table; } - else Image_GetQ2Palette(); + else Image_GetPaletteQ2(); } -void Image_GetPaletteWAD( byte *pal, int rendermode ) +void Image_GetPaletteLMP( byte *pal, int rendermode ) { d_rendermode = rendermode; if( pal ) { - Image_GetPalette( pal, d_8to24table ); + Image_SetPalette( pal, d_8to24table ); d_8to24table[255] &= LittleLong(0xffffff); d_currentpal = d_8to24table; } else if(rendermode == LUMP_QFONT) { // quake1 base palette and font palette have some diferences - Image_GetPalette( palette_q1, d_8to24table ); + Image_SetPalette( palette_q1, d_8to24table ); d_8to24table[0] = 0; d_currentpal = d_8to24table; } - else Image_GetQ1Palette(); + else Image_GetPaletteQ1(); // default quake palette } /* ============ Image_Copy8bitRGBA -NOTE: must call Image_GetQ2Palette or Image_GetQ1Palette -before used +NOTE: must call Image_GetPaletteXXX before used ============ */ bool Image_Copy8bitRGBA(const byte *in, byte *out, int pixels) @@ -206,6 +205,11 @@ bool Image_Copy8bitRGBA(const byte *in, byte *out, int pixels) MsgDev(D_ERROR,"Image_Copy8bitRGBA: no palette set\n"); return false; } + if(!in) + { + MsgDev(D_ERROR,"Image_Copy8bitRGBA: no input image\n"); + return false; + } while (pixels >= 8) { @@ -237,7 +241,7 @@ bool Image_Copy8bitRGBA(const byte *in, byte *out, int pixels) in += 2; iout += 2; } - if (pixels & 1) + if (pixels & 1) // last byte iout[0] = d_currentpal[in[0]]; return true; @@ -346,211 +350,24 @@ bool Image_Processing( const char *name, rgbdata_t **pix ) return false; } -/* -============== -LoadWAL -============== -*/ -bool LoadWAL( char *name, char *buffer, int filesize ) -{ - wal_t wal; - int pixels, ofs[4], mipsize;// FIXME, adding four mips ? - int i, flags, value, contents; // wal additional parms - - if (filesize < (int)sizeof(wal)) - { - MsgWarn("LoadWAL: file (%s) have invalid size\n", name ); - return false; - } - Mem_Copy(&wal, buffer, sizeof(wal)); - - flags = LittleLong(wal.flags); - value = LittleLong(wal.value); - contents = LittleLong(wal.contents); - image_width = LittleLong(wal.width); - image_height = LittleLong(wal.height); - for(i = 0; i < 4; i++) ofs[i] = LittleLong(wal.offsets[i]); - if(!Image_ValidSize( name )) return false; - - pixels = image_width * image_height; - mipsize = (int)sizeof(wal) + ofs[0] + pixels; - if( pixels > 256 && filesize < mipsize ) - { - // note: wal's with dimensions < 32 have invalid sizes. - MsgWarn("LoadWAL: file (%s) have invalid size\n", name ); - return false; - } - - image_num_layers = 1; - image_type = PF_RGBA_32; - - Image_GetQ2Palette(); // hardcoded - return FS_AddMipmapToPack( buffer + ofs[0], image_width, image_height ); -} - /* ============ -LoadLMP +LoadPAL ============ */ -bool LoadLMP( char *name, char *buffer, int filesize ) +bool LoadPAL( char *name, char *buffer, int filesize ) { - lmp_t lmp; - byte *fin, *pal; - int i, pixels; - int rendermode; - - if (filesize < (int)sizeof(lmp)) + if( filesize != 768 ) { - MsgWarn("LoadLMP: file (%s) have invalid size\n", name ); + MsgWarn("LoadPAL: file (%s) have invalid size\n", name ); return false; } - fin = buffer; - - // great hack from id software - if(!com_stricmp( name, "conchars" )) - { - image_width = image_height = 128; - pixels = image_width * image_height; - image_flags |= IMAGE_HAS_ALPHA; - rendermode = LUMP_QFONT; - } - else - { - Mem_Copy(&lmp, fin, sizeof(lmp)); - image_width = LittleLong(lmp.width); - image_height = LittleLong(lmp.height); - fin += sizeof(lmp); - pixels = image_width * image_height; - - if (filesize < (int)sizeof(lmp) + pixels) - { - MsgWarn("LoadLMP: file (%s) have invalid size %d\n", name, filesize ); - return false; - } - rendermode = LUMP_NORMAL; - } - - if(!Image_ValidSize( name )) return false; - image_size = pixels * 4; - image_num_mips = 1; - - image_rgba = (byte *)Mem_Alloc(Sys.imagepool, image_size ); - image_num_layers = 1; - image_type = PF_RGBA_32; - - // try to find transparent pixels - for(i = 0; i < pixels; i++) - { - if(fin[i] != 255) continue; - image_flags |= IMAGE_HAS_ALPHA; - rendermode = LUMP_TRANSPARENT; - break; // found - } - - // half-life 1.0.0.1 lmp version with palette - if( filesize == (int)sizeof(lmp) + pixels + sizeof(short) + 768) - { - int numcolors; - pal = fin + pixels; - numcolors = BuffLittleShort( pal ), pal += 2; - if( numcolors != 256 ) pal = NULL; // corrupted lump ? - } - else pal = NULL; - - Image_GetPaletteWAD( pal, rendermode ); - Image_Copy8bitRGBA( fin, image_rgba, pixels ); + // not a real image, just palette lump + Image_GetPaletteLMP( buffer, LUMP_NORMAL ); return true; } -/* -============ -LoadMIP -============ -*/ -bool LoadMIP( char *name, char *buffer, int filesize ) -{ - mip_t mip; - int i, ofs[4], mipsize; - - if (filesize < (int)sizeof(mip)) - { - MsgWarn("LoadMIP: file (%s) have invalid size\n", name ); - return false; - } - - Mem_Copy(&mip, buffer, sizeof(mip)); - image_width = LittleLong(mip.width); - image_height = LittleLong(mip.height); - for(i = 0; i < 4; i++) ofs[i] = LittleLong(mip.offsets[i]); - - if(!Image_ValidSize( name )) return false; - mipsize = image_width * image_height; - - if (filesize < (int)sizeof(mip) + mipsize) - { - MsgWarn("LoadMIP: file (%s) have invalid size\n", name ); - return false; - } - image_num_layers = 1; - image_type = PF_RGBA_32; - - Image_GetQ1Palette();// hardcoded - return FS_AddMipmapToPack( buffer + ofs[0], image_width, image_height ); -} - -/* -============ -LoadPIC -============ -*/ -bool LoadPIC( char *name, char *buffer, int filesize ) -{ - qpic_t pic; - byte *pal, *fin; - int numcolors; - int pixels; - - if(filesize < (int)sizeof(pic)) - { - MsgWarn("LoadPIC: file (%s) have invalid size\n", name ); - return false; - } - - Mem_Copy(&pic, buffer, sizeof(pic)); - image_width = LittleShort(pic.width); - image_height = LittleShort(pic.height); - if(!Image_ValidSize( name )) return false; - pixels = image_width * image_height; - fin = buffer + sizeof(pic) - 4; - image_num_layers = 1; - image_type = PF_RGBA_32; - - // half-life qlmpy version with palette - if( filesize == (int)sizeof(pic) - 2 + pixels + sizeof(short) + 768) - { - pal = fin + pixels; - numcolors = BuffLittleShort(pal); - if(numcolors != 256) - { - MsgWarn("LoadPIC: file (%s) have invalid palette size %d\n", name, numcolors ); - return false; - } - pal += 2; // skip colorsize - } - else pal = NULL; // quake1 pic - if(fin[0] == 255) image_flags |= IMAGE_HAS_ALPHA; - - Image_GetPaletteWAD( pal, LUMP_NORMAL ); - return FS_AddMipmapToPack( fin, image_width, image_height ); -} - -bool LoadTEST( char *name, char *buffer, int filesize ) -{ - return false; -} - /* ============ LoadFNT @@ -586,50 +403,159 @@ bool LoadFNT( char *name, char *buffer, int filesize ) if( fullsize != filesize ) { - // probably it's "creditsfont" or "conchars" - MsgWarn("LoadFNT: (%s) it's not a qfont_t structure\n", name ); - return LoadTEST( name, buffer, filesize ); + // oldstyle font: "conchars" or "creditsfont" + image_width = 256; // hardcoded + image_height = font.height; + } + else + { + // Half-Life 1.1.0.0 font style (qfont_t) + image_width = font.width * QCHAR_WIDTH; + image_height = font.height; } - // setup font dimensions - image_width = font.width * QCHAR_WIDTH; - image_height = font.height; image_num_layers = 1; image_type = PF_RGBA_32; - if(!Image_ValidSize( name )) return false; + fin = buffer + sizeof(font) - 4; pixels = ( image_width * image_height ); - fin = buffer + sizeof(font) - 4, pal = fin + pixels; - numcolors = BuffLittleShort( pal ), pal += 2; + pal = fin + pixels; + numcolors = BuffLittleShort( pal ), pal += sizeof(short); image_flags |= IMAGE_HAS_ALPHA; // fonts always have transparency - if(numcolors != 768) + if( numcolors == 768 ) + { + // newstyle font + Image_GetPaletteLMP( pal, LUMP_QFONT ); + } + else if( numcolors == 256 ) + { + // oldstyle font + Image_GetPaletteLMP( pal, LUMP_TRANSPARENT ); + } + else { MsgWarn("LoadFNT: file (%s) have invalid palette size %d\n", name, numcolors ); return false; } // FIXME: convert this to quake-style conchars ? - Image_GetPaletteWAD( pal, LUMP_QFONT ); return FS_AddMipmapToPack( fin, image_width, image_height ); } +/* +============== +LoadWAL +============== +*/ +bool LoadWAL( char *name, char *buffer, int filesize ) +{ + wal_t wal; + int pixels, ofs[4], mipsize; + int i, flags, value, contents; // wal additional parms + + if (filesize < (int)sizeof(wal)) + { + MsgWarn("LoadWAL: file (%s) have invalid size\n", name ); + return false; + } + Mem_Copy(&wal, buffer, sizeof(wal)); + + flags = LittleLong(wal.flags); + value = LittleLong(wal.value); + contents = LittleLong(wal.contents); + image_width = LittleLong(wal.width); + image_height = LittleLong(wal.height); + for(i = 0; i < 4; i++) ofs[i] = LittleLong(wal.offsets[i]); + if(!Image_ValidSize( name )) return false; + + pixels = image_width * image_height; + mipsize = (int)sizeof(wal) + ofs[0] + pixels; + if( pixels > 256 && filesize < mipsize ) + { + // note: wal's with dimensions < 32 have invalid sizes. + MsgWarn("LoadWAL: file (%s) have invalid size\n", name ); + return false; + } + + image_num_layers = 1; + image_type = PF_RGBA_32; + + Image_GetPaletteQ2(); // hardcoded + return FS_AddMipmapToPack( buffer + ofs[0], image_width, image_height ); +} + /* ============ -LoadWAD +LoadLMP ============ */ -bool LoadWAD( char *name, char *buffer, int filesize ) +bool LoadLMP( char *name, char *buffer, int filesize ) +{ + lmp_t lmp; + byte *fin, *pal; + int pixels; + + if (filesize < (int)sizeof(lmp)) + { + MsgWarn("LoadLMP: file (%s) have invalid size\n", name ); + return false; + } + fin = buffer; + Mem_Copy(&lmp, fin, sizeof(lmp)); + image_width = LittleLong(lmp.width); + image_height = LittleLong(lmp.height); + fin += sizeof(lmp); + pixels = image_width * image_height; + + if (filesize < (int)sizeof(lmp) + pixels) + { + MsgWarn("LoadLMP: file (%s) have invalid size %d\n", name, filesize ); + return false; + } + + if(!Image_ValidSize( name )) return false; + image_size = pixels * 4; + image_num_mips = 1; + + image_rgba = (byte *)Mem_Alloc(Sys.imagepool, image_size ); + image_num_layers = 1; + image_type = PF_RGBA_32; + + // half-life 1.0.0.1 lmp version with palette + if( filesize > (int)sizeof(lmp) + pixels ) + { + int numcolors; + pal = fin + pixels; + numcolors = BuffLittleShort( pal ); + if( numcolors != 256 ) pal = NULL; // corrupted lump ? + else pal += sizeof(short); + } + else pal = NULL; + if(fin[0] == 255) image_flags |= IMAGE_HAS_ALPHA; + + Image_GetPaletteLMP( pal, LUMP_NORMAL ); + Image_Copy8bitRGBA( fin, image_rgba, pixels ); + + return true; +} + +/* +============ +LoadMIP +============ +*/ +bool LoadMIP( char *name, char *buffer, int filesize ) { mip_t mip; - byte *pal; + byte *fin, *pal; int ofs[4], rendermode; - int i, numcolors; + int i, pixels, numcolors; if (filesize < (int)sizeof(mip)) { - MsgWarn("LoadWAD_3: file (%s) have invalid size\n", name ); + MsgWarn("LoadMIP: file (%s) have invalid size\n", name ); return false; } @@ -637,39 +563,61 @@ bool LoadWAD( char *name, char *buffer, int filesize ) image_width = LittleLong(mip.width); image_height = LittleLong(mip.height); for(i = 0; i < 4; i++) ofs[i] = LittleLong(mip.offsets[i]); - if(!Image_ValidSize( name )) return false; - - pal = buffer + mip.offsets[0] + (((image_width * image_height) * 85)>>6); - numcolors = BuffLittleShort( pal ), pal += 2; // skip colorsize - - if(numcolors != 256) - { - MsgWarn("LoadWAD_3: file (%s) have invalid palette size %d\n", name, numcolors ); - return false; - } - + pixels = image_width * image_height; image_num_layers = 1; - image_type = PF_RGBA_32; // always exctracted to 32-bit buffer + image_type = PF_RGBA_32; - // detect rendermode - if( name[0] == '{' ) + if(!com_stricmp( name, "conchars" )) { - // note: i trying determine transparent miptex by last color in palette - // e.g. valve used in their textures blue color (0,0,255) - // other cases for red (255,0,0) ang green (0,255,0) colors, - // otherwise - it will use decal palette with ugly results. ughgrrr.. - if(pal[255*3+0] == 0 && pal[255*3+1] == 0 && pal[255*3+2] == 255 && pal[255*3+3] == 0) - rendermode = LUMP_TRANSPARENT; - else if(pal[255*3+0] == 0 && pal[255*3+1] == 255 && pal[255*3+2] == 0 && pal[255*3+3] == 0) - rendermode = LUMP_TRANSPARENT; - else if(pal[255*3+0] == 255 && pal[255*3+1] == 0 && pal[255*3+2] == 0 && pal[255*3+3] == 0) - rendermode = LUMP_TRANSPARENT; - else rendermode = LUMP_DECAL; + // greatest hack from id software + image_width = image_height = 128; image_flags |= IMAGE_HAS_ALPHA; + rendermode = LUMP_QFONT; + pal = NULL; // clear palette + fin = buffer; } - else rendermode = LUMP_NORMAL; - Image_GetPaletteWAD( pal, rendermode ); - return FS_AddMipmapToPack( buffer + mip.offsets[0], image_width, image_height ); + else if(filesize >= (int)sizeof(mip) + ((pixels * 85)>>6) + sizeof(short) + 768) + { + // half-life 1.0.0.1 mip version with palette + fin = buffer + mip.offsets[0]; + pal = buffer + mip.offsets[0] + (((image_width * image_height) * 85)>>6); + numcolors = BuffLittleShort( pal ); + if(numcolors != 256) pal = NULL; // corrupted mip ? + else pal += sizeof(short); // skip colorsize + // detect rendermode + if( name[0] == '{' ) + { + // note: i trying determine transparent miptex by last color in palette + // e.g. valve used in their textures blue color (0,0,255) + // other cases for red (255,0,0) ang green (0,255,0) colors, + // otherwise - it will use decal palette with ugly results. ughgrrr.. + if(pal[255*3+0] == 0 && pal[255*3+1] == 0 && pal[255*3+2] == 255 && pal[255*3+3] == 0) + rendermode = LUMP_TRANSPARENT; + else if(pal[255*3+0] == 0 && pal[255*3+1] == 255 && pal[255*3+2] == 0 && pal[255*3+3] == 0) + rendermode = LUMP_TRANSPARENT; + else if(pal[255*3+0] == 255 && pal[255*3+1] == 0 && pal[255*3+2] == 0 && pal[255*3+3] == 0) + rendermode = LUMP_TRANSPARENT; + else rendermode = LUMP_DECAL; + image_flags |= IMAGE_HAS_ALPHA; + } + else rendermode = LUMP_NORMAL; + } + else if(filesize >= (int)sizeof(mip) + ((pixels * 85)>>6)) + { + // quake1 1.01 mip version without palette + pal = NULL; // clear palette + rendermode = LUMP_NORMAL; + fin = buffer + mip.offsets[0]; + } + else + { + MsgWarn("LoadMIP: lump (%s) is corrupted\n", name ); + return false; + } + + if(!Image_ValidSize( name )) return false; + Image_GetPaletteLMP( pal, rendermode ); + return FS_AddMipmapToPack( fin, image_width, image_height ); } /* @@ -1889,23 +1837,21 @@ loadformat_t load_formats[] = {"textures/%s%s.%s", "dds", LoadDDS}, {"textures/%s%s.%s", "tga", LoadTGA}, {"textures/%s%s.%s", "jpg", LoadJPG}, - {"textures/%s%s.%s", "wad3",LoadWAD}, - {"textures/%s%s.%s", "qpic",LoadPIC}, - {"textures/%s%s.%s", "pcx", LoadPCX}, - {"textures/%s%s.%s", "wal", LoadWAL}, - {"textures/%s%s.%s", "lmp", LoadLMP}, {"textures/%s%s.%s", "mip", LoadMIP}, - {"textures/%s%s.%s", "qfnt",LoadFNT}, + {"textures/%s%s.%s", "wal", LoadWAL}, + {"textures/%s%s.%s", "pcx", LoadPCX}, + {"textures/%s%s.%s", "lmp", LoadLMP}, + {"textures/%s%s.%s", "fnt", LoadFNT}, + {"textures/%s%s.%s", "pal", LoadPAL}, {"%s%s.%s", "dds", LoadDDS}, {"%s%s.%s", "tga", LoadTGA}, {"%s%s.%s", "jpg", LoadJPG}, - {"%s%s.%s", "wad3",LoadWAD}, - {"%s%s.%s", "qpic",LoadPIC}, - {"%s%s.%s", "pcx", LoadPCX}, - {"%s%s.%s", "wal", LoadWAL}, - {"%s%s.%s", "lmp", LoadLMP}, {"%s%s.%s", "mip", LoadMIP}, - {"%s%s.%s", "qfnt",LoadFNT}, + {"%s%s.%s", "wal", LoadWAL}, + {"%s%s.%s", "pcx", LoadPCX}, + {"%s%s.%s", "lmp", LoadLMP}, + {"%s%s.%s", "fnt", LoadFNT}, + {"%s%s.%s", "pal", LoadPAL}, {NULL, NULL} }; @@ -2089,12 +2035,12 @@ rgbdata_t *FS_LoadImage(const char *filename, char *buffer, int buffsize ) // this name will be used only for tell user about problems FS_FileBase( loadname, texname ); if( format->loadfunc(texname, buffer, buffsize )) - return ImagePack();// loaded + return ImagePack(); // loaded } } } - MsgDev(D_WARN, "couldn't load %s\n", texname ); + MsgDev(D_WARN, "FS_LoadImage: couldn't load \"%s\"\n", texname ); return NULL; } @@ -2140,10 +2086,14 @@ bool SaveTGA( const char *filename, byte *data, int width, int height, bool alph const byte *bufend, *in; byte *buffer, *out; const char *comment = "Generated by Xash ImageLib\0"; + char mergedname[MAX_SYSPATH]; if(alpha) outsize = width * height * 4 + 18 + com_strlen(comment); else outsize = width * height * 3 + 18 + com_strlen(comment); + com_strncpy(mergedname, filename, MAX_SYSPATH ); + if(mergedname[0] == '*') mergedname[0] = '!'; // quake1 issues + buffer = (byte *)Malloc( outsize ); memset (buffer, 0, 18); @@ -2199,8 +2149,8 @@ bool SaveTGA( const char *filename, byte *data, int width, int height, bool alph } } - MsgDev(D_NOTE, "Writing %s[%d]\n", filename, alpha ? 32 : 24 ); - FS_WriteFile (filename, buffer, outsize ); + MsgDev(D_NOTE, "Writing %s[%d]\n", mergedname, alpha ? 32 : 24 ); + FS_WriteFile( mergedname, buffer, outsize ); Mem_Free( buffer ); return true; diff --git a/launch/common/imglib.c.old b/launch/common/imglib.c.old new file mode 100644 index 00000000..1b895332 --- /dev/null +++ b/launch/common/imglib.c.old @@ -0,0 +1,2252 @@ +//======================================================================= +// Copyright XashXT Group 2007 © +// image.c - loading textures +//======================================================================= + +#include "launch.h" +#include "image.h" +#include "mathlib.h" + +// global image variables +int image_width, image_height; +byte image_num_layers = 1; // num layers in +byte image_num_mips = 0; // build mipmaps +uint image_type; // main type switcher +uint image_flags; // additional image flags +byte image_bits_count; // bits per RGBA +size_t image_size; // image rgba size +uint image_ptr; + +byte *image_palette; // palette pointer +byte *image_rgba; // image pointer (see image_type for details) +jpg_t jpg_file; // jpeg read struct + +// cubemap variables +int cubemap_width, cubemap_height; +int cubemap_num_sides; // how mach sides is loaded +byte *image_cubemap; // cubemap pack +uint cubemap_image_type; // shared image type +char *suf[6] = {"ft", "bk", "rt", "lf", "up", "dn"}; + +#define LUMP_NORMAL 0 +#define LUMP_TRANSPARENT 1 +#define LUMP_DECAL 2 +#define LUMP_QFONT 3 + +uint d_8toD1table[256]; +uint d_8toQ1table[256]; +uint d_8toQ2table[256]; +uint d_8to24table[256]; +uint *d_currentpal; +bool d1palette_init = false; +bool q1palette_init = false; +bool q2palette_init = false; +int d_rendermode = LUMP_NORMAL; + +//======================================================================= +// IMGLIB COMMON TOOLS +//======================================================================= +void Image_Init( void ) +{ + Sys.imagepool = Mem_AllocPool( "ImageLib Pool" ); +} + +void Image_Shutdown( void ) +{ + Mem_FreePool( &Sys.imagepool ); +} + +bool Image_ValidSize( char *name ) +{ + if(image_width > 4096 || image_height > 4096 || image_width <= 0 || image_height <= 0) + { + MsgWarn( "Image_ValidSize: (%s) image size out of range [%dx%d]\n", name, image_width, image_height ); + return false; + } + return true; +} + +void Image_GetPalette( byte *pal, uint *d_table ) +{ + int i; + byte rgba[4]; + + // setup palette + switch( d_rendermode ) + { + case LUMP_DECAL: + for(i = 0; i < 256; i++) + { + rgba[3] = pal[765]; + rgba[2] = pal[766]; + rgba[1] = pal[767]; + rgba[0] = i; + d_table[i] = BuffBigLong( rgba ); + } + break; + case LUMP_TRANSPARENT: + for (i = 0; i < 256; i++) + { + rgba[3] = pal[i*3+0]; + rgba[2] = pal[i*3+1]; + rgba[1] = pal[i*3+2]; + rgba[0] = pal[i] == 255 ? pal[i] : 0xFF; + d_table[i] = BuffBigLong( rgba ); + } + break; + case LUMP_QFONT: + for (i = 1; i < 256; i++) + { + rgba[3] = pal[i*3+0]; + rgba[2] = pal[i*3+1]; + rgba[1] = pal[i*3+2]; + rgba[0] = 0xFF; + d_table[i] = BuffBigLong( rgba ); + } + break; + case LUMP_NORMAL: + for (i = 0; i < 256; i++) + { + rgba[3] = pal[i*3+0]; + rgba[2] = pal[i*3+1]; + rgba[1] = pal[i*3+2]; + rgba[0] = 0xFF; + d_table[i] = BuffBigLong( rgba ); + } + break; + } +} + +void Image_GetD1Palette( void ) +{ + d_rendermode = LUMP_NORMAL; + + if(!d1palette_init) + { + Image_GetPalette( palette_d1, d_8toD1table ); + d_8toD1table[255] = 247; // 247 is transparent + d1palette_init = true; + } + d_currentpal = d_8toD1table; +} + +void Image_GetQ1Palette( void ) +{ + d_rendermode = LUMP_NORMAL; + + if(!q1palette_init) + { + Image_GetPalette( palette_q1, d_8toQ1table ); + d_8toQ1table[255] = 0; // 255 is transparent + q1palette_init = true; + } + d_currentpal = d_8toQ1table; +} + +void Image_GetQ2Palette( void ) +{ + d_rendermode = LUMP_NORMAL; + + if(!q2palette_init) + { + Image_GetPalette( palette_q2, d_8toQ2table ); + d_8toQ2table[255] &= LittleLong(0xffffff); + q2palette_init = true; + } + d_currentpal = d_8toQ2table; +} + +void Image_GetPalettePCX( byte *pal ) +{ + d_rendermode = LUMP_NORMAL; + + if(pal) + { + Image_GetPalette( pal, d_8to24table ); + d_8to24table[255] &= LittleLong(0xffffff); + d_currentpal = d_8to24table; + } + else Image_GetQ2Palette(); +} + +void Image_GetPaletteWAD( byte *pal, int rendermode ) +{ + d_rendermode = rendermode; + + if( pal ) + { + Image_GetPalette( pal, d_8to24table ); + d_8to24table[255] &= LittleLong(0xffffff); + d_currentpal = d_8to24table; + } + else if(rendermode == LUMP_QFONT) + { + // quake1 base palette and font palette have some diferences + Image_GetPalette( palette_q1, d_8to24table ); + d_8to24table[0] = 0; + d_currentpal = d_8to24table; + } + else Image_GetQ1Palette(); +} + +/* +============ +Image_Copy8bitRGBA + +NOTE: must call Image_GetQ2Palette or Image_GetQ1Palette +before used +============ +*/ +bool Image_Copy8bitRGBA(const byte *in, byte *out, int pixels) +{ + int *iout = (int *)out; + + if(!d_currentpal) + { + MsgDev(D_ERROR,"Image_Copy8bitRGBA: no palette set\n"); + return false; + } + + while (pixels >= 8) + { + iout[0] = d_currentpal[in[0]]; + iout[1] = d_currentpal[in[1]]; + iout[2] = d_currentpal[in[2]]; + iout[3] = d_currentpal[in[3]]; + iout[4] = d_currentpal[in[4]]; + iout[5] = d_currentpal[in[5]]; + iout[6] = d_currentpal[in[6]]; + iout[7] = d_currentpal[in[7]]; + in += 8; + iout += 8; + pixels -= 8; + } + if (pixels & 4) + { + iout[0] = d_currentpal[in[0]]; + iout[1] = d_currentpal[in[1]]; + iout[2] = d_currentpal[in[2]]; + iout[3] = d_currentpal[in[3]]; + in += 4; + iout += 4; + } + if (pixels & 2) + { + iout[0] = d_currentpal[in[0]]; + iout[1] = d_currentpal[in[1]]; + in += 2; + iout += 2; + } + if (pixels & 1) + iout[0] = d_currentpal[in[0]]; + + return true; +} + + +void Image_RoundDimensions(int *scaled_width, int *scaled_height) +{ + int width, height; + + for( width = 1; width < *scaled_width; width <<= 1 ); + for( height = 1; height < *scaled_height; height <<= 1 ); + + *scaled_width = bound(1, width, 4096 ); + *scaled_height = bound(1, height, 4096 ); +} + +byte *Image_Resample(uint *in, int inwidth, int inheight, int outwidth, int outheight, int in_type ) +{ + int i, j; + uint frac, fracstep; + uint *inrow1, *inrow2; + byte *pix1, *pix2, *pix3, *pix4; + uint *out, *buf, p1[4096], p2[4096]; + + // check for buffers + if(!in) return NULL; + + // nothing to resample ? + if (inwidth == outwidth && inheight == outheight) + return (byte *)in; + + // can't resample compressed formats + if(in_type != PF_RGBA_32) return NULL; + + // malloc new buffer + out = buf = (uint *)Mem_Alloc( Sys.imagepool, outwidth * outheight * 4 ); + + fracstep = inwidth * 0x10000 / outwidth; + frac = fracstep>>2; + + for( i = 0; i < outwidth; i++) + { + p1[i] = 4 * (frac>>16); + frac += fracstep; + } + frac = 3 * (fracstep>>2); + + for( i = 0; i < outwidth; i++) + { + p2[i] = 4 * (frac>>16); + frac += fracstep; + } + + for (i = 0; i < outheight; i++,buf += outwidth) + { + inrow1 = in + inwidth * (int)((i + 0.25) * inheight / outheight); + inrow2 = in + inwidth * (int)((i + 0.75) * inheight / outheight); + frac = fracstep>>1; + + for (j = 0; j < outwidth; j++) + { + pix1 = (byte *)inrow1 + p1[j]; + pix2 = (byte *)inrow1 + p2[j]; + pix3 = (byte *)inrow2 + p1[j]; + pix4 = (byte *)inrow2 + p2[j]; + ((byte *)(buf+j))[0] = (pix1[0] + pix2[0] + pix3[0] + pix4[0])>>2; + ((byte *)(buf+j))[1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1])>>2; + ((byte *)(buf+j))[2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2])>>2; + ((byte *)(buf+j))[3] = (pix1[3] + pix2[3] + pix3[3] + pix4[3])>>2; + } + } + return (byte *)out; +} + +bool Image_Processing( const char *name, rgbdata_t **pix ) +{ + int w, h; + rgbdata_t *image = *pix; + byte *out; + char width[4], height[4]; + + // check for buffers + if(!image || !image->buffer) return false; + + w = image->width; + h = image->height; + + if(FS_GetParmFromCmdLine("-w", width ) && FS_GetParmFromCmdLine("-h", height )) + { + // custom size + w = bound(4, atoi(width), 1024 ); // maxwidth 1024 + h = bound(4, atoi(height), 1024); // maxheight 1024 + } + else Image_RoundDimensions( &w, &h ); // auto detect new size + + out = Image_Resample((uint *)image->buffer, image->width, image->height, w, h, image->type ); + if(out != image->buffer) + { + MsgDev(D_INFO,"Resampling %s from[%d x %d] to[%d x %d]\n",name, image->width, image->height, w, h ); + Mem_Move( Sys.imagepool, &image->buffer, out, w * h * 4 );// update image->buffer + image->width = w,image->height = h; + *pix = image; + return true; + } + return false; +} + +/* +============== +LoadWAL +============== +*/ +bool LoadWAL( char *name, char *buffer, int filesize ) +{ + wal_t wal; + int pixels, ofs[4], mipsize;// FIXME, adding four mips ? + int i, flags, value, contents; // wal additional parms + + if (filesize < (int)sizeof(wal)) + { + MsgWarn("LoadWAL: file (%s) have invalid size\n", name ); + return false; + } + Mem_Copy(&wal, buffer, sizeof(wal)); + + flags = LittleLong(wal.flags); + value = LittleLong(wal.value); + contents = LittleLong(wal.contents); + image_width = LittleLong(wal.width); + image_height = LittleLong(wal.height); + for(i = 0; i < 4; i++) ofs[i] = LittleLong(wal.offsets[i]); + if(!Image_ValidSize( name )) return false; + + pixels = image_width * image_height; + mipsize = (int)sizeof(wal) + ofs[0] + pixels; + if( pixels > 256 && filesize < mipsize ) + { + // note: wal's with dimensions < 32 have invalid sizes. + MsgWarn("LoadWAL: file (%s) have invalid size\n", name ); + return false; + } + + image_num_layers = 1; + image_type = PF_RGBA_32; + + Image_GetQ2Palette(); // hardcoded + return FS_AddMipmapToPack( buffer + ofs[0], image_width, image_height ); +} + +/* +============ +LoadLMP +============ +*/ +bool LoadLMP( char *name, char *buffer, int filesize ) +{ + lmp_t lmp; + byte *fin, *pal; + int i, pixels; + int rendermode; + + if (filesize < (int)sizeof(lmp)) + { + MsgWarn("LoadLMP: file (%s) have invalid size\n", name ); + return false; + } + fin = buffer; + + // great hack from id software + if(!com_stricmp( name, "conchars" )) + { + image_width = image_height = 128; + pixels = image_width * image_height; + image_flags |= IMAGE_HAS_ALPHA; + rendermode = LUMP_QFONT; + } + else + { + Mem_Copy(&lmp, fin, sizeof(lmp)); + image_width = LittleLong(lmp.width); + image_height = LittleLong(lmp.height); + fin += sizeof(lmp); + pixels = image_width * image_height; + + if (filesize < (int)sizeof(lmp) + pixels) + { + MsgWarn("LoadLMP: file (%s) have invalid size %d\n", name, filesize ); + return false; + } + rendermode = LUMP_NORMAL; + } + + if(!Image_ValidSize( name )) return false; + image_size = pixels * 4; + image_num_mips = 1; + + image_rgba = (byte *)Mem_Alloc(Sys.imagepool, image_size ); + image_num_layers = 1; + image_type = PF_RGBA_32; + + // try to find transparent pixels + for(i = 0; i < pixels; i++) + { + if(fin[i] != 255) continue; + image_flags |= IMAGE_HAS_ALPHA; + rendermode = LUMP_TRANSPARENT; + break; // found + } + + // half-life 1.0.0.1 lmp version with palette + if( filesize == (int)sizeof(lmp) + pixels + sizeof(short) + 768) + { + int numcolors; + pal = fin + pixels; + numcolors = BuffLittleShort( pal ), pal += 2; + if( numcolors != 256 ) pal = NULL; // corrupted lump ? + } + else pal = NULL; + + Image_GetPaletteWAD( pal, rendermode ); + Image_Copy8bitRGBA( fin, image_rgba, pixels ); + + return true; +} + +/* +============ +LoadMIP +============ +*/ +bool LoadMIP( char *name, char *buffer, int filesize ) +{ + mip_t mip; + int i, ofs[4], mipsize; + + if (filesize < (int)sizeof(mip)) + { + MsgWarn("LoadMIP: file (%s) have invalid size\n", name ); + return false; + } + + Mem_Copy(&mip, buffer, sizeof(mip)); + image_width = LittleLong(mip.width); + image_height = LittleLong(mip.height); + for(i = 0; i < 4; i++) ofs[i] = LittleLong(mip.offsets[i]); + + if(!Image_ValidSize( name )) return false; + mipsize = image_width * image_height; + + if (filesize < (int)sizeof(mip) + mipsize) + { + MsgWarn("LoadMIP: file (%s) have invalid size\n", name ); + return false; + } + image_num_layers = 1; + image_type = PF_RGBA_32; + + Image_GetQ1Palette();// hardcoded + return FS_AddMipmapToPack( buffer + ofs[0], image_width, image_height ); +} + +/* +============ +LoadPIC +============ +*/ +bool LoadPIC( char *name, char *buffer, int filesize ) +{ + qpic_t pic; + byte *pal, *fin; + int numcolors; + int pixels; + + if(filesize < (int)sizeof(pic)) + { + MsgWarn("LoadPIC: file (%s) have invalid size\n", name ); + return false; + } + + Mem_Copy(&pic, buffer, sizeof(pic)); + image_width = LittleShort(pic.width); + image_height = LittleShort(pic.height); + if(!Image_ValidSize( name )) return false; + pixels = image_width * image_height; + fin = buffer + sizeof(pic) - 4; + image_num_layers = 1; + image_type = PF_RGBA_32; + + // half-life qlmpy version with palette + if( filesize == (int)sizeof(pic) - 2 + pixels + sizeof(short) + 768) + { + pal = fin + pixels; + numcolors = BuffLittleShort(pal); + if(numcolors != 256) + { + MsgWarn("LoadPIC: file (%s) have invalid palette size %d\n", name, numcolors ); + return false; + } + pal += 2; // skip colorsize + } + else pal = NULL; // quake1 pic + if(fin[0] == 255) image_flags |= IMAGE_HAS_ALPHA; + + Image_GetPaletteWAD( pal, LUMP_NORMAL ); + return FS_AddMipmapToPack( fin, image_width, image_height ); +} + +bool LoadTEST( char *name, char *buffer, int filesize ) +{ + return false; +} + +/* +============ +LoadFNT +============ +*/ +bool LoadFNT( char *name, char *buffer, int filesize ) +{ + qfont_t font; + byte *pal, *fin; + int i, pixels, fullsize; + int numcolors; + + if(filesize < (int)sizeof(font)) + { + MsgWarn("LoadFNT: file (%s) have invalid size\n", name ); + return false; + } + Mem_Copy(&font, buffer, sizeof(font)); + + // swap lumps + font.width = LittleShort(font.width); + font.height = LittleShort(font.height); + font.rowcount = LittleShort(font.rowcount); + font.rowheight = LittleShort(font.rowheight); + for(i = 0; i < 256; i++) + { + font.fontinfo[i].startoffset = LittleShort( font.fontinfo[i].startoffset ); + font.fontinfo[i].charwidth = LittleShort( font.fontinfo[i].charwidth ); + } + + // last sixty four bytes - what the hell ???? + fullsize = sizeof(qfont_t)-4+(128 * font.width * QCHAR_WIDTH) + sizeof(short) + 768 + 64; + + Msg("width %d, height %d\n", font.width, font.height ); + if( fullsize != filesize ) + { + // probably it's "creditsfont" or "conchars" + MsgWarn("LoadFNT: (%s) it's not a qfont_t structure\n", name ); + return LoadTEST( name, buffer, filesize ); + } + + // setup font dimensions + image_width = font.width * QCHAR_WIDTH; + image_height = font.height; + image_num_layers = 1; + image_type = PF_RGBA_32; + + if(!Image_ValidSize( name )) return false; + + pixels = ( image_width * image_height ); + fin = buffer + sizeof(font) - 4, pal = fin + pixels; + numcolors = BuffLittleShort( pal ), pal += 2; + image_flags |= IMAGE_HAS_ALPHA; // fonts always have transparency + + if(numcolors != 768) + { + MsgWarn("LoadFNT: file (%s) have invalid palette size %d\n", name, numcolors ); + return false; + } + + // FIXME: convert this to quake-style conchars ? + Image_GetPaletteWAD( pal, LUMP_QFONT ); + return FS_AddMipmapToPack( fin, image_width, image_height ); +} + +/* +============ +LoadWAD +============ +*/ +bool LoadWAD( char *name, char *buffer, int filesize ) +{ + mip_t mip; + byte *pal; + int ofs[4], rendermode; + int i, numcolors; + + if (filesize < (int)sizeof(mip)) + { + MsgWarn("LoadWAD_3: file (%s) have invalid size\n", name ); + return false; + } + + Mem_Copy(&mip, buffer, sizeof(mip)); + image_width = LittleLong(mip.width); + image_height = LittleLong(mip.height); + for(i = 0; i < 4; i++) ofs[i] = LittleLong(mip.offsets[i]); + if(!Image_ValidSize( name )) return false; + + pal = buffer + mip.offsets[0] + (((image_width * image_height) * 85)>>6); + numcolors = BuffLittleShort( pal ), pal += 2; // skip colorsize + + if(numcolors != 256) + { + MsgWarn("LoadWAD_3: file (%s) have invalid palette size %d\n", name, numcolors ); + return false; + } + + image_num_layers = 1; + image_type = PF_RGBA_32; // always exctracted to 32-bit buffer + + // detect rendermode + if( name[0] == '{' ) + { + // note: i trying determine transparent miptex by last color in palette + // e.g. valve used in their textures blue color (0,0,255) + // other cases for red (255,0,0) ang green (0,255,0) colors, + // otherwise - it will use decal palette with ugly results. ughgrrr.. + if(pal[255*3+0] == 0 && pal[255*3+1] == 0 && pal[255*3+2] == 255 && pal[255*3+3] == 0) + rendermode = LUMP_TRANSPARENT; + else if(pal[255*3+0] == 0 && pal[255*3+1] == 255 && pal[255*3+2] == 0 && pal[255*3+3] == 0) + rendermode = LUMP_TRANSPARENT; + else if(pal[255*3+0] == 255 && pal[255*3+1] == 0 && pal[255*3+2] == 0 && pal[255*3+3] == 0) + rendermode = LUMP_TRANSPARENT; + else rendermode = LUMP_DECAL; + image_flags |= IMAGE_HAS_ALPHA; + } + else rendermode = LUMP_NORMAL; + Image_GetPaletteWAD( pal, rendermode ); + return FS_AddMipmapToPack( buffer + mip.offsets[0], image_width, image_height ); +} + +/* +============ +LoadPCX +============ +*/ +bool LoadPCX( char *name, char *buffer, int filesize ) +{ + pcx_t pcx; + uint *trans; + int s, i, p, x, y, x2, dataByte; + byte *pix, *pbuf, *palette, *fin, *enddata; + + fin = buffer; + Mem_Copy(&pcx, fin, sizeof(pcx)); + fin += sizeof(pcx); + + // probably it's not pcx file + if (pcx.manufacturer != 0x0a || pcx.version != 5 || pcx.encoding != 1 ) return false; + if (filesize < (int)sizeof(pcx) + 768) + { + MsgWarn("LoadPCX: file (%s) have invalid size\n", name ); + return false; + } + + pcx.xmax = LittleShort (pcx.xmax); + pcx.xmin = LittleShort (pcx.xmin); + pcx.ymax = LittleShort (pcx.ymax); + pcx.ymin = LittleShort (pcx.ymin); + pcx.hres = LittleShort (pcx.hres); + pcx.vres = LittleShort (pcx.vres); + pcx.bytes_per_line = LittleShort (pcx.bytes_per_line); + pcx.palette_type = LittleShort (pcx.palette_type); + + image_width = pcx.xmax + 1 - pcx.xmin; + image_height = pcx.ymax + 1 - pcx.ymin; + + if( pcx.bits_per_pixel != 8 || pcx.manufacturer != 0x0a || pcx.version != 5 || pcx.encoding != 1) + { + MsgWarn("LoadPCX: (%s) have illegal pixel size '%d'\n", name, pcx.bits_per_pixel ); + return false; + } + if(!Image_ValidSize( name )) return false; + palette = buffer + filesize - 768; + + image_num_layers = 1; + image_num_mips = 1; + image_type = PF_RGBA_32; + + if(palette) + { + image_palette = Mem_Alloc( Sys.imagepool, 768 ); + Mem_Copy( image_palette, palette, 768 ); + } + + s = image_width * image_height; + image_size = image_width * image_height * 4; + pbuf = (byte *)Mem_Alloc( Sys.imagepool, s ); + image_rgba = (byte *)Mem_Alloc( Sys.imagepool, image_size ); + trans = (uint *)image_rgba; + enddata = palette; + + for (y = 0; y < image_height && fin < enddata; y++) + { + pix = pbuf + y * image_width; + for (x = 0; x < image_width && fin < enddata;) + { + dataByte = *fin++; + if(dataByte >= 0xC0) + { + if (fin >= enddata) break; + x2 = x + (dataByte & 0x3F); + dataByte = *fin++; + if (x2 > image_width) x2 = image_width; // technically an error + while(x < x2) pix[x++] = dataByte; + } + else pix[x++] = dataByte; + } + // the number of bytes per line is always forced to an even number + fin += pcx.bytes_per_line - image_width; + while(x < image_width) pix[x++] = 0; + } + + Image_GetPalettePCX( palette ); + + // convert to rgba + for (i = 0; i < s; i++) + { + p = pbuf[i]; + if (p == 255) + { + image_flags |= IMAGE_HAS_ALPHA; // found alpha channel + ((byte *)&trans[i])[0] = ((byte *)&d_currentpal[0])[0]; + ((byte *)&trans[i])[1] = ((byte *)&d_currentpal[0])[1]; + ((byte *)&trans[i])[2] = ((byte *)&d_currentpal[0])[2]; + ((byte *)&trans[i])[3] = ((byte *)&d_currentpal[p])[3]; + } + else trans[i] = d_currentpal[p]; + } + + Mem_Free( pbuf ); // free compressed image + return true; +} + +/* +============= +LoadTGA +============= +*/ +bool LoadTGA( char *name, char *buffer, int filesize ) +{ + int x, y, pix_inc, row_inc; + int red, green, blue, alpha; + int runlen, alphabits; + + byte *pixbuf, *p; + const byte *fin, *enddata; + tga_t targa_header; + byte palette[256*4]; + + if (filesize < 19) return false; + + fin = buffer; + enddata = fin + filesize; + + targa_header.id_length = *fin++; + targa_header.colormap_type = *fin++; + targa_header.image_type = *fin++; + + targa_header.colormap_index = BuffLittleShort( fin ); fin += 2; + targa_header.colormap_length = BuffLittleShort( fin ); fin += 2; + targa_header.colormap_size = *fin++; + targa_header.x_origin = BuffLittleShort( fin ); fin += 2; + targa_header.y_origin = BuffLittleShort( fin ); fin += 2; + targa_header.width = image_width = BuffLittleShort( fin ); fin += 2; + targa_header.height = image_height = BuffLittleShort( fin );fin += 2; + + if(!Image_ValidSize( name )) return false; + + image_num_layers = 1; + image_num_mips = 1; + image_type = PF_RGBA_32; //always exctracted to 32-bit buffer + + targa_header.pixel_size = *fin++; + targa_header.attributes = *fin++; + // end of header + + // skip TARGA image comment (usually 0 bytes) + fin += targa_header.id_length; + + // read/skip the colormap if present (note: according to the TARGA spec it + // can be present even on truecolor or greyscale images, just not used by + // the image data) + if (targa_header.colormap_type) + { + if (targa_header.colormap_length > 256) + { + MsgWarn("LoadTGA: (%s) have unsupported colormap type ( more than 256 bytes)\n", name ); + return false; + } + if (targa_header.colormap_index) + { + MsgWarn("LoadTGA: (%s) have unspported indexed colormap\n", name ); + return false; + } + image_palette = Mem_Alloc(Sys.imagepool, 256*4 ); + + if (targa_header.colormap_size == 24) + { + for (x = 0;x < targa_header.colormap_length;x++) + { + palette[x*4+2] = *fin++; + palette[x*4+1] = *fin++; + palette[x*4+0] = *fin++; + palette[x*4+3] = 0xff; + } + } + else if (targa_header.colormap_size == 32) + { + + for (x = 0;x < targa_header.colormap_length;x++) + { + palette[x*4+2] = *fin++; + palette[x*4+1] = *fin++; + palette[x*4+0] = *fin++; + palette[x*4+3] = *fin++; + } + } + else + { + MsgWarn("LoadTGA: (%s) have unsupported colormap size (valid is 32 or 24 bit)\n", name ); + return false; + } + Mem_Copy(image_palette, palette, 256 * 4 ); //copy palette + } + + // check our pixel_size restrictions according to image_type + switch (targa_header.image_type & ~8) + { + case 2: + if (targa_header.pixel_size != 24 && targa_header.pixel_size != 32) + { + MsgWarn("LoadTGA: (%s) have unsupported pixel size '%d', for type '%d'\n", name, targa_header.pixel_size, targa_header.image_type ); + return false; + } + break; + case 3: + // set up a palette to make the loader easier + for (x = 0; x < 256; x++) + { + palette[x*4+2] = x; + palette[x*4+1] = x; + palette[x*4+0] = x; + palette[x*4+3] = 255; + } + // fall through to colormap case + case 1: + if (targa_header.pixel_size != 8) + { + MsgWarn("LoadTGA: (%s) have unsupported pixel size '%d', for type '%d'\n", name, targa_header.pixel_size, targa_header.image_type ); + return false; + } + break; + default: + MsgWarn("LoadTGA: (%s) is unsupported image type '%i'\n", name, targa_header.image_type); + return false; + } + + if (targa_header.attributes & 0x10) + { + MsgWarn("LoadTGA: (%s): top right and bottom right origin are not supported\n", name ); + return false; + } + + // number of attribute bits per pixel, we only support 0 or 8 + alphabits = targa_header.attributes & 0x0F; + if (alphabits != 8 && alphabits != 0) + { + MsgWarn("LoadTGA: (%s) have invalid attributes '%i'\n", name, alphabits ); + return false; + } + + image_flags |= alphabits ? IMAGE_HAS_ALPHA : 0; + image_size = image_width * image_height * 4; + image_rgba = Mem_Alloc( Sys.imagepool, image_size ); + + // If bit 5 of attributes isn't set, the image has been stored from bottom to top + if ((targa_header.attributes & 0x20) == 0) + { + pixbuf = image_rgba + (image_height - 1)*image_width*4; + row_inc = -image_width*4*2; + } + else + { + pixbuf = image_rgba; + row_inc = 0; + } + + x = y = 0; + red = green = blue = alpha = 255; + pix_inc = 1; + if ((targa_header.image_type & ~8) == 2) pix_inc = targa_header.pixel_size / 8; + + switch (targa_header.image_type) + { + case 1: // colormapped, uncompressed + case 3: // greyscale, uncompressed + if (fin + image_width * image_height * pix_inc > enddata) + break; + for (y = 0;y < image_height;y++, pixbuf += row_inc) + { + for (x = 0;x < image_width;x++) + { + p = palette + *fin++ * 4; + *pixbuf++ = p[0]; + *pixbuf++ = p[1]; + *pixbuf++ = p[2]; + *pixbuf++ = p[3]; + } + } + break; + case 2: + // BGR or BGRA, uncompressed + if (fin + image_width * image_height * pix_inc > enddata) + break; + if (targa_header.pixel_size == 32 && alphabits) + { + for (y = 0;y < image_height;y++, pixbuf += row_inc) + { + for (x = 0;x < image_width;x++, fin += pix_inc) + { + *pixbuf++ = fin[2]; + *pixbuf++ = fin[1]; + *pixbuf++ = fin[0]; + *pixbuf++ = fin[3]; + } + } + } + else //24 bits + { + for (y = 0;y < image_height; y++, pixbuf += row_inc) + { + for (x = 0;x < image_width; x++, fin += pix_inc) + { + *pixbuf++ = fin[2]; + *pixbuf++ = fin[1]; + *pixbuf++ = fin[0]; + *pixbuf++ = 255; + } + } + } + break; + case 9: // colormapped, RLE + case 11: // greyscale, RLE + for (y = 0; y < image_height; y++, pixbuf += row_inc) + { + for (x = 0;x < image_width;) + { + if (fin >= enddata) + break; // error - truncated file + runlen = *fin++; + if (runlen & 0x80) + { + // RLE - all pixels the same color + runlen += 1 - 0x80; + if (fin + pix_inc > enddata) break; // error - truncated file + if (x + runlen > image_width) break; // error - line exceeds width + p = palette + *fin++ * 4; + red = p[0]; + green = p[1]; + blue = p[2]; + alpha = p[3]; + for (;runlen--; x++) + { + *pixbuf++ = red; + *pixbuf++ = green; + *pixbuf++ = blue; + *pixbuf++ = alpha; + } + } + else + { + // uncompressed - all pixels different color + runlen++; + if (fin + pix_inc * runlen > enddata) break; // error - truncated file + if (x + runlen > image_width) break; // error - line exceeds width + for (;runlen--; x++) + { + p = palette + *fin++ * 4; + *pixbuf++ = p[0]; + *pixbuf++ = p[1]; + *pixbuf++ = p[2]; + *pixbuf++ = p[3]; + } + } + } + } + break; + case 10: + // BGR or BGRA, RLE + if (targa_header.pixel_size == 32 && alphabits) + { + for (y = 0;y < image_height;y++, pixbuf += row_inc) + { + for (x = 0;x < image_width;) + { + if (fin >= enddata) + break; // error - truncated file + runlen = *fin++; + if (runlen & 0x80) + { + // RLE - all pixels the same color + runlen += 1 - 0x80; + if (fin + pix_inc > enddata) + break; // error - truncated file + if (x + runlen > image_width) + break; // error - line exceeds width + red = fin[2]; + green = fin[1]; + blue = fin[0]; + alpha = fin[3]; + fin += pix_inc; + for (;runlen--;x++) + { + *pixbuf++ = red; + *pixbuf++ = green; + *pixbuf++ = blue; + *pixbuf++ = alpha; + } + } + else + { + // uncompressed - all pixels different color + runlen++; + if (fin + pix_inc * runlen > enddata) + break; // error - truncated file + if (x + runlen > image_width) + break; // error - line exceeds width + for (;runlen--;x++, fin += pix_inc) + { + *pixbuf++ = fin[2]; + *pixbuf++ = fin[1]; + *pixbuf++ = fin[0]; + *pixbuf++ = fin[3]; + } + } + } + } + } + else + { + for (y = 0;y < image_height;y++, pixbuf += row_inc) + { + for (x = 0;x < image_width;) + { + if (fin >= enddata) + break; // error - truncated file + runlen = *fin++; + if (runlen & 0x80) + { + // RLE - all pixels the same color + runlen += 1 - 0x80; + if (fin + pix_inc > enddata) + break; // error - truncated file + if (x + runlen > image_width) + break; // error - line exceeds width + red = fin[2]; + green = fin[1]; + blue = fin[0]; + alpha = 255; + fin += pix_inc; + for (;runlen--;x++) + { + *pixbuf++ = red; + *pixbuf++ = green; + *pixbuf++ = blue; + *pixbuf++ = alpha; + } + } + else + { + // uncompressed - all pixels different color + runlen++; + if (fin + pix_inc * runlen > enddata) + break; // error - truncated file + if (x + runlen > image_width) + break; // error - line exceeds width + for (;runlen--;x++, fin += pix_inc) + { + *pixbuf++ = fin[2]; + *pixbuf++ = fin[1]; + *pixbuf++ = fin[0]; + *pixbuf++ = 255; + } + } + } + } + } + break; + default: break; // unknown image_type + } + return true; +} + +/* +============= +LoadDDS +============= +*/ +uint dds_get_linear_size( int width, int height, int depth, int rgbcount ) +{ + uint i, BlockSize = 0; + int block, bpp; + + // right calcualte blocksize + for(i = 0; i < PF_TOTALCOUNT; i++) + { + if(image_type == PFDesc[i].format) + { + block = PFDesc[i].block; + bpp = PFDesc[i].bpp; + break; + } + } + + if(i != PF_TOTALCOUNT) //make sure what match found + { + if(block == 0) BlockSize = width * height * bpp; + else if(block > 0) BlockSize = ((width + 3)/4) * ((height + 3)/4) * depth * block; + else if(block < 0 && rgbcount > 0) BlockSize = width * height * depth * rgbcount; + else BlockSize = width * height * abs(block); + } + return BlockSize; +} + +void dds_get_pixelformat( dds_t *hdr ) +{ + uint bits = hdr->dsPixelFormat.dwRGBBitCount; + + // All volume textures I've seem so far didn't have the DDS_COMPLEX flag set, + // even though this is normally required. But because noone does set it, + // also read images without it (TODO: check file size for 3d texture?) + if (!(hdr->dsCaps.dwCaps2 & DDS_VOLUME)) hdr->dwDepth = 1; + + if(hdr->dsPixelFormat.dwFlags & DDS_ALPHA) + image_flags |= IMAGE_HAS_ALPHA; + + if (hdr->dsPixelFormat.dwFlags & DDS_FOURCC) + { + switch (hdr->dsPixelFormat.dwFourCC) + { + case TYPE_DXT1: image_type = PF_DXT1; break; + case TYPE_DXT2: image_type = PF_DXT2; break; + case TYPE_DXT3: image_type = PF_DXT3; break; + case TYPE_DXT4: image_type = PF_DXT4; break; + case TYPE_DXT5: image_type = PF_DXT5; break; + case TYPE_ATI1: image_type = PF_ATI1N; break; + case TYPE_ATI2: image_type = PF_ATI2N; break; + case TYPE_RXGB: image_type = PF_RXGB; break; + case TYPE_$: image_type = PF_ABGR_64; break; + default: image_type = PF_UNKNOWN; break; + } + } + else + { + // This dds texture isn't compressed so write out ARGB or luminance format + if (hdr->dsPixelFormat.dwFlags & DDS_LUMINANCE) + { + if (hdr->dsPixelFormat.dwFlags & DDS_ALPHAPIXELS) + image_type = PF_LUMINANCE_ALPHA; + else if(hdr->dsPixelFormat.dwRGBBitCount == 16 && hdr->dsPixelFormat.dwRBitMask == 0xFFFF) + image_type = PF_LUMINANCE_16; + else image_type = PF_LUMINANCE; + } + else + { + if( bits == 32) image_type = PF_ABGR_64; + else image_type = PF_ARGB_32; + } + } + + // setup additional flags + if( hdr->dsCaps.dwCaps1 & DDS_COMPLEX && hdr->dsCaps.dwCaps2 & DDS_CUBEMAP) + { + image_flags |= IMAGE_CUBEMAP | IMAGE_CUBEMAP_FLIP; + } + + if(hdr->dsPixelFormat.dwFlags & DDS_ALPHAPIXELS) + { + image_flags |= IMAGE_HAS_ALPHA; + } + + if(image_type == TYPE_DXT2 || image_type == TYPE_DXT4) + image_flags |= IMAGE_PREMULT; + + if(hdr->dwFlags & DDS_MIPMAPCOUNT) + image_num_mips = hdr->dwMipMapCount; + else image_num_mips = 1; + + if(image_type == PF_ARGB_32 || image_type == PF_LUMINANCE || image_type == PF_LUMINANCE_16 || image_type == PF_LUMINANCE_ALPHA) + { + //store RGBA mask into one block, and get palette pointer + byte *tmp = image_palette = Mem_Alloc( Sys.imagepool, sizeof(uint) * 4 ); + Mem_Copy( tmp, &hdr->dsPixelFormat.dwRBitMask, sizeof(uint)); tmp += 4; + Mem_Copy( tmp, &hdr->dsPixelFormat.dwGBitMask, sizeof(uint)); tmp += 4; + Mem_Copy( tmp, &hdr->dsPixelFormat.dwBBitMask, sizeof(uint)); tmp += 4; + Mem_Copy( tmp, &hdr->dsPixelFormat.dwABitMask, sizeof(uint)); tmp += 4; + } +} + +void dds_addjust_volume_texture( dds_t *hdr ) +{ + uint bits; + + if (hdr->dwDepth <= 1) return; + bits = hdr->dsPixelFormat.dwRGBBitCount / 8; + hdr->dwFlags |= DDS_LINEARSIZE; + hdr->dwLinearSize = dds_get_linear_size( hdr->dwWidth, hdr->dwHeight, hdr->dwDepth, bits ); +} + +uint dds_calc_mipmap_size( dds_t *hdr ) +{ + uint buffsize = 0; + int w = hdr->dwWidth; + int h = hdr->dwHeight; + int d = hdr->dwDepth; + int i, mipsize = 0; + int bits = hdr->dsPixelFormat.dwRGBBitCount / 8; + + // now correct buffer size + for( i = 0; i < image_num_mips; i++, buffsize += mipsize ) + { + mipsize = dds_get_linear_size( w, h, d, bits ); + w = (w+1)>>1, h = (h+1)>>1, d = (d+1)>>1; + } + return buffsize; +} + +uint dds_calc_size( char *name, dds_t *hdr, uint filesize ) +{ + uint buffsize = 0; + int w = image_width; + int h = image_height; + int d = image_num_layers; + int bits = hdr->dsPixelFormat.dwRGBBitCount / 8; + + if(hdr->dsCaps.dwCaps2 & DDS_CUBEMAP) + { + // cubemap w*h always match for all sides + buffsize = dds_calc_mipmap_size( hdr ) * 6; + } + else if(hdr->dwFlags & DDS_MIPMAPCOUNT) + { + // if mipcount > 1 + buffsize = dds_calc_mipmap_size( hdr ); + } + else if(hdr->dwFlags & (DDS_LINEARSIZE | DDS_PITCH)) + { + // just in case (no need, really) + buffsize = hdr->dwLinearSize; + } + else + { + // pretty solution for microsoft bug + buffsize = dds_calc_mipmap_size( hdr ); + } + + if(filesize != buffsize) // main check + { + MsgWarn("LoadDDS: (%s) probably corrupted(%i should be %i)\n", name, buffsize, filesize ); + return false; + } + return buffsize; +} + +bool LoadDDS( char *name, char *buffer, int filesize ) +{ + dds_t header; + byte *fin; + uint i; + + fin = buffer; + + // swap header + header.dwIdent = BuffLittleLong(fin); fin += 4; + header.dwSize = BuffLittleLong(fin); fin += 4; + header.dwFlags = BuffLittleLong(fin); fin += 4; + header.dwHeight = BuffLittleLong(fin); fin += 4; + header.dwWidth = BuffLittleLong(fin); fin += 4; + header.dwLinearSize = BuffLittleLong(fin); fin += 4; + header.dwDepth = BuffLittleLong(fin); fin += 4; + header.dwMipMapCount = BuffLittleLong(fin); fin += 4; + header.dwAlphaBitDepth = BuffLittleLong(fin); fin += 4; + + // skip unused stuff + for (i = 0; i < 10; i++) + { + header.dwReserved1[i] = BuffLittleLong(fin); + fin += 4; + } + + // pixel format + header.dsPixelFormat.dwSize = BuffLittleLong(fin); fin += 4; + header.dsPixelFormat.dwFlags = BuffLittleLong(fin); fin += 4; + header.dsPixelFormat.dwFourCC = BuffLittleLong(fin); fin += 4; + header.dsPixelFormat.dwRGBBitCount = BuffLittleLong(fin); fin += 4; + header.dsPixelFormat.dwRBitMask = BuffLittleLong(fin); fin += 4; + header.dsPixelFormat.dwGBitMask = BuffLittleLong(fin); fin += 4; + header.dsPixelFormat.dwBBitMask = BuffLittleLong(fin); fin += 4; + header.dsPixelFormat.dwABitMask = BuffLittleLong(fin); fin += 4; + + // caps + header.dsCaps.dwCaps1 = BuffLittleLong(fin); fin += 4; + header.dsCaps.dwCaps2 = BuffLittleLong(fin); fin += 4; + header.dsCaps.dwCaps3 = BuffLittleLong(fin); fin += 4; + header.dsCaps.dwCaps4 = BuffLittleLong(fin); fin += 4; + header.dwTextureStage = BuffLittleLong(fin); fin += 4; + + if(header.dwIdent != DDSHEADER) return false; // it's not a dds file, just skip it + if(header.dwSize != sizeof(dds_t) - 4 ) // size of the structure (minus MagicNum) + { + MsgWarn("LoadDDS: (%s) have corrupt header\n", name ); + return false; + } + if(header.dsPixelFormat.dwSize != sizeof(dds_pixf_t)) // size of the structure + { + MsgWarn("LoadDDS: (%s) have corrupt pixelformat header\n", name ); + return false; + } + + image_width = header.dwWidth; + image_height = header.dwHeight; + image_bits_count = header.dsPixelFormat.dwRGBBitCount; + if(header.dwFlags & DDS_DEPTH) image_num_layers = header.dwDepth; + if(!Image_ValidSize( name )) return false; + + dds_get_pixelformat( &header );// and image type too :) + dds_addjust_volume_texture( &header ); + + if (image_type == PF_UNKNOWN) + { + MsgWarn("LoadDDS: (%s) have unsupported compression type\n", name ); + return false; //unknown type + } + + image_size = dds_calc_size( name, &header, filesize - 128 ); + if(image_size == 0) return false; // just in case + + // dds files will be uncompressed on a render. requires minimal of info for set this + image_rgba = Mem_Alloc( Sys.imagepool, image_size ); + Mem_Copy( image_rgba, fin, image_size ); + + return true; +} + +/* +============= +LoadJPG +============= +*/ +int jpeg_read_byte( void ) +{ + // read byte + jpg_file.curbyte = *jpg_file.buffer++; + jpg_file.curbit = 0; + return jpg_file.curbyte; +} + +int jpeg_read_word( void ) +{ + // read word + word i = BuffLittleShort( jpg_file.buffer); + i = ((i << 8) & 0xFF00) + ((i >> 8) & 0x00FF); + jpg_file.buffer += 2; + + return i; +} + +int jpeg_read_bit( void ) +{ + // read bit + register int i; + if(jpg_file.curbit == 0) + { + jpeg_read_byte(); + if(jpg_file.curbyte == 0xFF) + { + while(jpg_file.curbyte == 0xFF) jpeg_read_byte(); + if(jpg_file.curbyte >= 0xD0 && jpg_file.curbyte <= 0xD7) + memset(jpg_file.dc, 0, sizeof(int) * 3); + if(jpg_file.curbyte == 0) jpg_file.curbyte = 0xFF; + else jpeg_read_byte(); + } + } + + i = (jpg_file.curbyte >> (7 - jpg_file.curbit++)) & 0x01; + if(jpg_file.curbit == 8) jpg_file.curbit = 0; + + return i; +} + +int jpeg_read_bits( int num ) +{ + // read num bit + register int i, j; + + for(i = 0, j = 0; i < num; i++) + { + j <<= 1; + j |= jpeg_read_bit(); + } + return j; +} + +int jpeg_bit2int( int bit, int i ) +{ + // convert bit code to int + if((i & (1 << (bit - 1))) > 0) return i; + return -(i ^ ((1 << bit) - 1)); +} + +int jpeg_huffmancode( huffman_table_t *table ) +{ + // get Huffman code + register int i,size,code; + for(size = 1, code = 0, i = 0; size < 17; size++) + { + code <<= 1; + code |= jpeg_read_bit(); + while(table->size[i] <= size) + { + if(table->code[i] == code) + return table->hval[i]; + i++; + } + } + return code; +} + +void jpeg_idct( float *data ) +{ + // aa&n algorithm inverse DCT + float t0, t1, t2, t3, t4, t5, t6, t7; + float t10, t11, t12, t13; + float z5, z10, z11, z12, z13; + float *dataptr; + int i; + + dataptr = data; + + for(i = 0; i < 8; i++) + { + t0 = dataptr[8 * 0]; + t1 = dataptr[8 * 2]; + t2 = dataptr[8 * 4]; + t3 = dataptr[8 * 6]; + + t10 = t0 + t2; + t11 = t0 - t2; + t13 = t1 + t3; + t12 = - t13 + (t1 - t3) * 1.414213562;//?? + + t0 = t10 + t13; + t3 = t10 - t13; + t1 = t11 + t12; + t2 = t11 - t12; + t4 = dataptr[8 * 1]; + t5 = dataptr[8 * 3]; + t6 = dataptr[8 * 5]; + t7 = dataptr[8 * 7]; + + z13 = t6 + t5; + z10 = t6 - t5; + z11 = t4 + t7; + z12 = t4 - t7; + + t7 = z11 + z13; + t11 = (z11 - z13) * 1.414213562; + z5 = (z10 + z12) * 1.847759065; + t10 = - z5 + z12 * 1.082392200; + t12 = z5 - z10 * 2.613125930; + t6 = t12 - t7; + t5 = t11 - t6; + t4 = t10 + t5; + + dataptr[8 * 0] = t0 + t7; + dataptr[8 * 7] = t0 - t7; + dataptr[8 * 1] = t1 + t6; + dataptr[8 * 6] = t1 - t6; + dataptr[8 * 2] = t2 + t5; + dataptr[8 * 5] = t2 - t5; + dataptr[8 * 4] = t3 + t4; + dataptr[8 * 3] = t3 - t4; + dataptr++; + } + + dataptr = data; + + for(i = 0; i < 8; i++) + { + t10 = dataptr[0] + dataptr[4]; + t11 = dataptr[0] - dataptr[4]; + t13 = dataptr[2] + dataptr[6]; + t12 = - t13 + (dataptr[2] - dataptr[6]) * 1.414213562;//?? + + t0 = t10 + t13; + t3 = t10 - t13; + t1 = t11 + t12; + t2 = t11 - t12; + + z13 = dataptr[5] + dataptr[3]; + z10 = dataptr[5] - dataptr[3]; + z11 = dataptr[1] + dataptr[7]; + z12 = dataptr[1] - dataptr[7]; + + t7 = z11 + z13; + t11 = (z11 - z13) * 1.414213562; + z5 = (z10 + z12) * 1.847759065; + t10 = - z5 + z12 * 1.082392200; + t12 = z5 - z10 * 2.613125930; + + t6 = t12 - t7; + t5 = t11 - t6; + t4 = t10 + t5; + + dataptr[0] = t0 + t7; + dataptr[7] = t0 - t7; + dataptr[1] = t1 + t6; + dataptr[6] = t1 - t6; + dataptr[2] = t2 + t5; + dataptr[5] = t2 - t5; + dataptr[4] = t3 + t4; + dataptr[3] = t3 - t4; + dataptr += 8;//move ptr + } +} + +int jpeg_readmarkers( void ) +{ + // read jpeg markers + int marker, length, i, j, k, l, m; + huffman_table_t *hptr; + + while( 1 ) + { + marker = jpeg_read_byte(); + if(marker != 0xFF) return 0; + + marker = jpeg_read_byte(); + if(marker != 0xD8) + { + length = jpeg_read_word(); + length -= 2; + + switch(marker) + { + case 0xC0: // Baseline + jpg_file.data_precision = jpeg_read_byte(); + jpg_file.height = jpeg_read_word(); + jpg_file.width = jpeg_read_word(); + jpg_file.num_components = jpeg_read_byte(); + if(length - 6 != jpg_file.num_components * 3) return 0; + + for(i = 0; i < jpg_file.num_components; i++) + { + jpg_file.component_info[i].id = jpeg_read_byte(); + j = jpeg_read_byte(); + jpg_file.component_info[i].h = (j >> 4) & 0x0F; + jpg_file.component_info[i].v = j & 0x0F; + jpg_file.component_info[i].t = jpeg_read_byte(); + } + break; + case 0xC1: // Extended sequetial, Huffman + case 0xC2: // Progressive, Huffman + case 0xC3: // Lossless, Huffman + case 0xC5: // Differential sequential, Huffman + case 0xC6: // Differential progressive, Huffman + case 0xC7: // Differential lossless, Huffman + case 0xC8: // Reserved for JPEG extensions + case 0xC9: // Extended sequential, arithmetic + case 0xCA: // Progressive, arithmetic + case 0xCB: // Lossless, arithmetic + case 0xCD: // Differential sequential, arithmetic + case 0xCE: // Differential progressive, arithmetic + case 0xCF: // Differential lossless, arithmetic + return 0; + case 0xC4: // Huffman table + while(length > 0) + { + k = jpeg_read_byte(); + if(k & 0x10) hptr = &jpg_file.hac[k & 0x0F]; + else hptr = &jpg_file.hdc[k & 0x0F]; + for(i = 0, j = 0; i < 16; i++) + { + hptr->bits[i] = jpeg_read_byte(); + j += hptr->bits[i]; + } + length -= 17; + for(i = 0; i < j; i++) hptr->hval[i] = jpeg_read_byte(); + length -= j; + + for(i = 0, k = 0, l = 0; i < 16; i++) + { + for(j = 0; j < hptr->bits[i]; j++, k++) + { + hptr->size[k] = i + 1; + hptr->code[k] = l++; + } + l <<= 1; + } + } + break; + case 0xDB: // Quantization table + while(length > 0) + { + j = jpeg_read_byte(); + k = (j >> 4) & 0x0F; + for(i = 0; i < 64; i++) + { + if( k )jpg_file.qtable[j][i] = jpeg_read_word(); + else jpg_file.qtable[j][i] = jpeg_read_byte(); + } + length -= 65; + if( k )length -= 64; + } + break; + case 0xD9: // End of image (EOI) + return 0; + case 0xDA: // Start of scan (SOS) + j = jpeg_read_byte(); + for(i = 0; i < j; i++) + { + k = jpeg_read_byte(); + m = jpeg_read_byte(); + for(l = 0; l < jpg_file.num_components; l++) + { + if(jpg_file.component_info[l].id == k) + { + jpg_file.component_info[l].td = (m >> 4) & 0x0F; + jpg_file.component_info[l].ta = m & 0x0F; + } + } + } + jpg_file.scan.ss = jpeg_read_byte(); + jpg_file.scan.se = jpeg_read_byte(); + k = jpeg_read_byte(); + jpg_file.scan.ah = (k >> 4) & 0x0F; + jpg_file.scan.al = k & 0x0F; + return 1; + case 0xDD: // Restart interval + jpg_file.restart_interval = jpeg_read_word(); + break; + default: + jpg_file.buffer += length; //move ptr + break; + } + } + } +} + + +void jpeg_decompress( void ) +{ + // decompress jpeg file (Baseline algorithm) + register int x, y, i, j, k, l, c; + int X, Y, H, V, plane, scaleh[3], scalev[3]; + static float vector[64], dct[64]; + + static const int jpeg_zigzag[64] = + { + 0, 1, 5, 6, 14, 15, 27, 28, + 2, 4, 7, 13, 16, 26, 29, 42, + 3, 8, 12, 17, 25, 30, 41, 43, + 9, 11, 18, 24, 31, 40, 44, 53, + 10, 19, 23, 32, 39, 45, 52, 54, + 20, 22, 33, 38, 46, 51, 55, 60, + 21, 34, 37, 47, 50, 56, 59, 61, + 35, 36, 48, 49, 57, 58, 62, 63 + }; + + // 1.0, k = 0; cos(k * PI / 16) * sqrt(2), k = 1...7 + static const float aanscale[8] = + { + 1.0, 1.387039845, 1.306562965, 1.175875602, + 1.0, 0.785694958, 0.541196100, 0.275899379 + }; + + scaleh[0] = 1; + scalev[0] = 1; + + if(jpg_file.num_components == 3) + { + scaleh[1] = jpg_file.component_info[0].h / jpg_file.component_info[1].h; + scalev[1] = jpg_file.component_info[0].v / jpg_file.component_info[1].v; + scaleh[2] = jpg_file.component_info[0].h / jpg_file.component_info[2].h; + scalev[2] = jpg_file.component_info[0].v / jpg_file.component_info[2].v; + } + memset(jpg_file.dc,0,sizeof(int) * 3); + + for(Y = 0; Y < jpg_file.height; Y += jpg_file.component_info[0].v << 3) + { + if(jpg_file.restart_interval > 0) jpg_file.curbit = 0; + for(X = 0; X < jpg_file.width; X += jpg_file.component_info[0].h << 3) + { + for(plane = 0; plane < jpg_file.num_components; plane++) + { + for(V = 0; V < jpg_file.component_info[plane].v; V++) + { + for(H = 0; H < jpg_file.component_info[plane].h; H++) + { + i = jpeg_huffmancode(&jpg_file.hdc[jpg_file.component_info[plane].td]); + i &= 0x0F; + vector[0] = jpg_file.dc[plane] + jpeg_bit2int(i,jpeg_read_bits(i)); + jpg_file.dc[plane] = vector[0]; + i = 1; + + while(i < 64) + { + j = jpeg_huffmancode(&jpg_file.hac[jpg_file.component_info[plane].ta]); + if(j == 0) while(i < 64) vector[i++] = 0; + else + { + k = i + ((j >> 4) & 0x0F); + while(i < k) vector[i++] = 0; + j &= 0x0F; + vector[i++] = jpeg_bit2int(j,jpeg_read_bits(j)); + } + } + + k = jpg_file.component_info[plane].t; + for(y = 0, i = 0; y < 8; y++) + { + for(x = 0; x < 8; x++, i++) + { + j = jpeg_zigzag[i]; + dct[i] = vector[j] * jpg_file.qtable[k][j] * aanscale[x] * aanscale[y]; + } + } + + jpeg_idct(dct); + for(y = 0; y < 8; y++) + { + for(x = 0; x < 8; x++) + { + c = ((int)dct[(y << 3) + x] >> 3) + 128; + if(c < 0) c = 0; + else if(c > 255) c = 255; + + if(scaleh[plane] == 1 && scalev[plane] == 1) + { + i = X + x + (H << 3); + j = Y + y + (V << 3); + if(i < jpg_file.width && j < jpg_file.height) + jpg_file.data[((j * jpg_file.width + i) << 2) + plane] = c; + } + else for(l = 0; l < scalev[plane]; l++)//else for, heh... + { + for(k = 0; k < scaleh[plane]; k++) + { + i = X + (x + (H << 3)) * scaleh[plane] + k; + j = Y + (y + (V << 3)) * scalev[plane] + l; + if(i < jpg_file.width && j < jpg_file.height) + jpg_file.data[((j * jpg_file.width + i) << 2) + plane] = c; + } + } + } + } + } + } + } + } + } +} + +void jpeg_ycbcr2rgba( void ) +{ + int i, Y, Cb, Cr, R, G, B; + + // convert YCbCr image to RGBA + for(i = 0; i < jpg_file.width * jpg_file.height << 2; i += 4) + { + Y = jpg_file.data[i]; + Cb = jpg_file.data[i + 1] - 128; + Cr = jpg_file.data[i + 2] - 128; + + R = Y + 1.40200 * Cr; + G = Y - 0.34414 * Cb - 0.71414 * Cr; + B = Y + 1.77200 * Cb; + + // bound colors + if(R < 0) R = 0; + else if(R > 255) R = 255; + if(G < 0) G = 0; + else if(G > 255) G = 255; + if(B < 0) B = 0; + else if(B > 255) B = 255; + + jpg_file.data[i + 0] = R; + jpg_file.data[i + 1] = G; + jpg_file.data[i + 2] = B; + jpg_file.data[i + 3] = 0xff;//alpha channel + } +} + +void jpeg_gray2rgba( void ) +{ + int i, j; + + // grayscale image to RGBA + for(i = 0; i < jpg_file.width * jpg_file.height << 2; i += 4) + { + j = jpg_file.data[i]; + jpg_file.data[i + 0] = j; + jpg_file.data[i + 1] = j; + jpg_file.data[i + 2] = j; + jpg_file.data[i + 3] = 0xff; + } +} + +bool LoadJPG(char *name, char *buffer, int filesize ) +{ + memset(&jpg_file, 0, sizeof(jpg_file)); + jpg_file.buffer = buffer; + + if(!jpeg_readmarkers()) + return false; // it's not a jpg file, just skip it + + image_width = jpg_file.width; + image_height = jpg_file.height; + image_type = PF_RGBA_32; + if(!Image_ValidSize( name )) return false; + + image_size = jpg_file.width * jpg_file.height * 4; + jpg_file.data = Mem_Alloc( Sys.imagepool, image_size ); + + jpeg_decompress(); + if(jpg_file.num_components == 1) jpeg_gray2rgba(); + if(jpg_file.num_components == 3) jpeg_ycbcr2rgba(); + + image_num_layers = 1; + image_num_mips = 1; + image_rgba = jpg_file.data; + + return true; +} + +typedef struct loadformat_s +{ + char *formatstring; + char *ext; + bool (*loadfunc)(char *name, char *buffer, int filesize); +} loadformat_t; + +loadformat_t load_formats[] = +{ + {"textures/%s%s.%s", "dds", LoadDDS}, + {"textures/%s%s.%s", "tga", LoadTGA}, + {"textures/%s%s.%s", "jpg", LoadJPG}, + {"textures/%s%s.%s", "wad3",LoadWAD}, + {"textures/%s%s.%s", "qpic",LoadPIC}, + {"textures/%s%s.%s", "pcx", LoadPCX}, + {"textures/%s%s.%s", "wal", LoadWAL}, + {"textures/%s%s.%s", "lmp", LoadLMP}, + {"textures/%s%s.%s", "mip", LoadMIP}, + {"textures/%s%s.%s", "qfnt",LoadFNT}, + {"%s%s.%s", "dds", LoadDDS}, + {"%s%s.%s", "tga", LoadTGA}, + {"%s%s.%s", "jpg", LoadJPG}, + {"%s%s.%s", "wad3",LoadWAD}, + {"%s%s.%s", "qpic",LoadPIC}, + {"%s%s.%s", "pcx", LoadPCX}, + {"%s%s.%s", "wal", LoadWAL}, + {"%s%s.%s", "lmp", LoadLMP}, + {"%s%s.%s", "mip", LoadMIP}, + {"%s%s.%s", "qfnt",LoadFNT}, + {NULL, NULL} +}; + +rgbdata_t *ImagePack( void ) +{ + rgbdata_t *pack = Mem_Alloc( Sys.imagepool, sizeof(rgbdata_t)); + + if(image_cubemap && cubemap_num_sides != 6) + { + // this neved be happens, just in case + MsgWarn("ImagePack: inconsistent cubemap pack %d\n", cubemap_num_sides ); + FS_FreeImage( pack ); + return NULL; + } + + if(image_cubemap) + { + image_flags |= IMAGE_CUBEMAP; + pack->buffer = image_cubemap; + pack->width = cubemap_width; + pack->height = cubemap_height; + pack->type = cubemap_image_type; + pack->size = image_size * cubemap_num_sides; + } + else + { + pack->buffer = image_rgba; + pack->width = image_width; + pack->height = image_height; + pack->type = image_type; + pack->size = image_size; + } + + pack->numLayers = image_num_layers; + pack->numMips = image_num_mips; + pack->bitsCount = image_bits_count; + pack->flags = image_flags; + pack->palette = image_palette; + return pack; +} + +bool FS_AddImageToPack( const char *name ) +{ + byte *resampled; + + // first image have suffix "ft" and set average size for all cubemap sides! + if(!image_cubemap) + { + cubemap_width = image_width; + cubemap_height = image_height; + cubemap_image_type = image_type; + } + image_size = cubemap_width * cubemap_height * 4; // keep constant size, render.dll expecting it + + // mixing dds format with any existing ? + if(image_type != cubemap_image_type) return false; + + // resampling image if needed + resampled = Image_Resample((uint *)image_rgba, image_width, image_height, cubemap_width, cubemap_height, cubemap_image_type ); + if(!resampled) return false; // try to reasmple dxt? + + if(resampled != image_rgba) + { + MsgDev(D_NOTE, "FS_AddImageToPack: resample %s from [%dx%d] to [%dx%d]\n", name, image_width, image_height, cubemap_width, cubemap_height ); + Mem_Move( Sys.imagepool, &image_rgba, resampled, image_size );// update buffer + } + + image_cubemap = Mem_Realloc( Sys.imagepool, image_cubemap, image_ptr + image_size ); + Mem_Copy(image_cubemap + image_ptr, image_rgba, image_size ); + + Mem_Free( image_rgba ); // memmove aren't help us + image_ptr += image_size; // move to next + cubemap_num_sides++; // sides counter + + return true; +} + +bool FS_AddMipmapToPack( const byte *in, int width, int height ) +{ + int mipsize = width * height; + int outsize = width * height * 4; + + // reallocate image buffer + image_rgba = Mem_Realloc( Sys.imagepool, image_rgba, image_size + outsize ); + if(!Image_Copy8bitRGBA( in, image_rgba + image_size, mipsize )) + return false; // pallette not installed + image_size += outsize; + image_num_mips++; + + return true; +} + +/* +================ +FS_LoadImage + +loading and unpack to rgba any known image +================ +*/ +rgbdata_t *FS_LoadImage(const char *filename, char *buffer, int buffsize ) +{ + loadformat_t *format; + const char *ext = FS_FileExtension( filename ); + char path[128], loadname[128], texname[128]; + bool anyformat = !stricmp(ext, "") ? true : false; + int i, filesize = 0; + byte *f; + +#if 0 // don't try to be very clever + if(!buffer || !buffsize) buffer = (char *)florr1_2_jpg, buffsize = sizeof(florr1_2_jpg); +#endif + com_strncpy( loadname, filename, sizeof(loadname)-1); + FS_StripExtension( loadname ); //remove extension if needed + + // developer warning + if(!anyformat) MsgDev(D_NOTE, "Note: %s will be loading only with ext .%s\n", loadname, ext ); + + // now try all the formats in the selected list + for (format = load_formats; format->formatstring; format++) + { + if(anyformat || !stricmp(ext, format->ext )) + { + com_sprintf (path, format->formatstring, loadname, "", format->ext ); + f = FS_LoadFile( path, &filesize ); + if(f && filesize > 0) + { + // this name will be used only for tell user about problems + FS_FileBase( path, texname ); + if( format->loadfunc(texname, f, filesize )) + return ImagePack(); //loaded + } + } + } + + // maybe it skybox or cubemap ? + for(i = 0; i < 6; i++) + { + for (format = load_formats; format->formatstring; format++) + { + if(anyformat || !stricmp(ext, format->ext )) + { + com_sprintf (path, format->formatstring, loadname, suf[i], format->ext ); + f = FS_LoadFile( path, &filesize ); + if(f && filesize > 0) + { + // this name will be used only for tell user about problems + FS_FileBase( path, texname ); + if( format->loadfunc(texname, f, filesize )) + { + if(FS_AddImageToPack(va("%s%s.%s", loadname, suf[i], format->ext))) + break; // loaded + } + } + } + } + if(cubemap_num_sides != i + 1) //check side + { + // first side not found, probably it's not cubemap + // it contain info about image_type and dimensions, don't generate black cubemaps + if(!image_cubemap) break; + MsgDev(D_ERROR, "FS_LoadImage: couldn't load (%s%s.%s), create balck image\n",loadname,suf[i],ext ); + + // Mem_Alloc already filled memblock with 0x00, no need to do it again + image_cubemap = Mem_Realloc( Sys.imagepool, image_cubemap, image_ptr + image_size ); + image_ptr += image_size; // move to next + cubemap_num_sides++; // merge counter + } + } + + if(image_cubemap) return ImagePack(); // now it's cubemap pack + + // try to load image from const buffer (e.g. const byte blank_frame ) + com_strncpy( texname, filename, sizeof(texname) - 1); + + for (format = load_formats; format->formatstring; format++) + { + if(anyformat || !stricmp(ext, format->ext )) + { + if(buffer && buffsize > 0) + { + // this name will be used only for tell user about problems + FS_FileBase( loadname, texname ); + if( format->loadfunc(texname, buffer, buffsize )) + return ImagePack();// loaded + } + } + } + + MsgDev(D_WARN, "couldn't load %s\n", texname ); + return NULL; +} + +/* +================ +FS_FreeImage + +free RGBA buffer +================ +*/ +void FS_FreeImage( rgbdata_t *pack ) +{ + if( pack ) + { + if( pack->buffer ) Mem_Free( pack->buffer ); + if( pack->palette ) Mem_Free( pack->palette ); + Mem_Free( pack ); + } + + // reset global variables + image_width = image_height = 0; + cubemap_width = cubemap_height = 0; + image_bits_count = image_flags = 0; + cubemap_num_sides = 0; + image_num_layers = 1; + image_num_mips = 0; + image_type = PF_UNKNOWN; + image_palette = NULL; + image_rgba = NULL; + image_cubemap = NULL; + image_ptr = 0; + image_size = 0; +} + +/* +============= +SaveTGA +============= +*/ +bool SaveTGA( const char *filename, byte *data, int width, int height, bool alpha, int imagetype ) +{ + int y, outsize, pixel_size; + const byte *bufend, *in; + byte *buffer, *out; + const char *comment = "Generated by Xash ImageLib\0"; + + if(alpha) outsize = width * height * 4 + 18 + com_strlen(comment); + else outsize = width * height * 3 + 18 + com_strlen(comment); + + buffer = (byte *)Malloc( outsize ); + memset (buffer, 0, 18); + + // prepare header + buffer[0] = com_strlen(comment); // tga comment length + buffer[2] = 2; // uncompressed type + buffer[12] = (width >> 0) & 0xFF; + buffer[13] = (width >> 8) & 0xFF; + buffer[14] = (height >> 0) & 0xFF; + buffer[15] = (height >> 8) & 0xFF; + buffer[16] = alpha ? 32 : 24; + buffer[17] = alpha ? 8 : 0; // 8 bits of alpha + com_strncpy(buffer + 18, comment, com_strlen(comment)); + out = buffer + 18 + com_strlen(comment); + + // get image description + switch( imagetype ) + { + case PF_RGB_24_FLIP: + case PF_RGB_24: pixel_size = 3; break; + case PF_RGBA_32: pixel_size = 4; break; + default: + MsgWarn("SaveTGA: unsupported image type %s\n", PFDesc[image_type].name ); + return false; + } + + // flip buffer + switch( imagetype ) + { + case PF_RGB_24_FLIP: + // glReadPixels rotating image at 180 degrees, flip it + for (in = data; in < data + width * height * pixel_size; in += pixel_size) + { + *out++ = in[2]; + *out++ = in[1]; + *out++ = in[0]; + } + break; + case PF_RGB_24: + case PF_RGBA_32: + // swap rgba to bgra and flip upside down + for (y = height - 1; y >= 0; y--) + { + in = data + y * width * pixel_size; + bufend = in + width * pixel_size; + for ( ;in < bufend; in += pixel_size) + { + *out++ = in[2]; + *out++ = in[1]; + *out++ = in[0]; + if(alpha) *out++ = in[3]; + } + } + } + + MsgDev(D_NOTE, "Writing %s[%d]\n", filename, alpha ? 32 : 24 ); + FS_WriteFile (filename, buffer, outsize ); + + Mem_Free( buffer ); + return true; +} + +typedef struct saveformat_s +{ + char *formatstring; + char *ext; + bool (*savefunc)(char *filename, byte *data, int width, int height, bool alpha, int imagetype ); +} saveformat_t; + +saveformat_t save_formats[] = +{ + {"%s%s.%s", "tga", SaveTGA}, + {NULL, NULL} +}; + +/* +================ +FS_SaveImage + +writes image as tga RGBA format +================ +*/ +void FS_SaveImage(const char *filename, rgbdata_t *pix ) +{ + bool has_alpha = false; + int i, numsides = 1; + byte *data; + char savename[256]; + + if(!pix || !pix->buffer) return; + + data = pix->buffer; + FS_StripExtension( (char *)filename ); + if(pix->flags & IMAGE_HAS_ALPHA) has_alpha = true; + if(pix->flags & IMAGE_CUBEMAP) numsides = 6; + + for(i = 0; i < numsides; i++) + { + if(numsides > 1) com_sprintf(savename, "%s%s.tga", filename, suf[i] ); + else com_sprintf(savename, "%s.tga", filename ); + + SaveTGA( savename, data, pix->width, pix->height, has_alpha, pix->type ); + data += pix->width * pix->height * PFDesc[pix->type].bpp; + } +} \ No newline at end of file diff --git a/public/stdref.h b/public/stdref.h index 749f7ab9..4e09c733 100644 --- a/public/stdref.h +++ b/public/stdref.h @@ -1577,9 +1577,10 @@ typedef struct mip_s #define IDWAD3HEADER (('3'<<24)+('D'<<16)+('A'<<8)+'W') // little-endian "WAD3" half-life wads #define WAD3_NAMELEN 16 -#define TYPE_QPIC 66 // quake1 and hl pic (qpic_t) -#define TYPE_HLMIP 67 // half-life textures (mip_t) TYP_SOUND for Quake1, but never used for it -#define TYPE_Q1MIP 68 // quake1 textures (mip_t) +#define TYPE_QPAL 64 +#define TYPE_QPIC 66 // quake1 and hl pic (lmp_t) +#define TYPE_MIPTEX2 67 // half-life (mip_t) previous TYP_SOUND but never used in quake1 +#define TYPE_MIPTEX 68 // quake1 (mip_t) #define TYPE_QFONT 70 // half-life font (qfont_t) #define QCHAR_WIDTH 16 @@ -1588,11 +1589,12 @@ typedef struct mip_s typedef struct { - int ident; // should be WAD3 + int ident; // should be IWAD or WAD2 or WAD3 int numlumps; int infotableofs; } dwadinfo_t; +// quake1 and half-life lump header typedef struct { int filepos; @@ -1602,15 +1604,9 @@ typedef struct char compression; // probably not used char pad1; char pad2; - char name[WAD3_NAMELEN]; // must be null terminated + char name[16]; // must be null terminated } dlumpinfo_t; -typedef struct -{ - int width, height; - byte data[4]; // variably sized -} qpic_t; - typedef struct { short startoffset;