This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/launch/imagelib/img_vtf.c

318 lines
8.3 KiB
C

//=======================================================================
// Copyright XashXT Group 2008 ©
// img_vtf.c - vtf format load & save
//=======================================================================
#include "imagelib.h"
// VTF->DXT supply conversion structures
typedef struct
{
long ofs; // buffer + ofs
size_t size; // map size
} vlayer_t;
typedef struct
{
vlayer_t layers[512]; // max frames or layers
int numlayers;
} vmip_t;
typedef struct
{
vmip_t mips[512]; // max frames or layers
int nummips; // or cubemap counts
} vside_t;
typedef struct
{
vside_t sides[CB_FACECOUNT];// 6 cubemap sides and one envmap
int numSides; // must be equal 1 or 7
} vtex_t;
// NOTE: not-in list VTF formats never used in games based on Source Engine
pixformat_t Image_VTFFormat( vtf_format_t srcFormat )
{
switch( srcFormat )
{
case VTF_DXT1:
case VTF_DXT1_ONEBITALPHA:
return PF_DXT1;
case VTF_DXT3:
return PF_DXT3;
case VTF_DXT5:
return PF_DXT5;
case VTF_RGBA16161616F:
return PF_ABGR_64F;
case VTF_BGR888:
return PF_BGR_24;
case VTF_UVWQ8888:
return PF_UV_32;
case VTF_BGRA8888:
return PF_BGRA_32;
case VTF_UV88:
return PF_UV_16;
case VTF_RGBA8888:
return PF_RGBA_32;
case VTF_RGB888:
return PF_RGB_24;
case VTF_UNKNOWN:
return VTF_UNKNOWN;
default: return PF_UNKNOWN;
}
}
/*
================
Image_VTFCalcLowResSize
lowres image hasn't mip-levels, frames or cubemap sides
typically params: 16x16 DXT1 but can be missing
================
*/
size_t Image_VTFCalcLowResSize( vtf_t *hdr )
{
size_t buffsize = 0;
int format;
format = Image_VTFFormat( hdr->lowResImageFormat );
// missing lowRes image for -1 value
if( format != VTF_UNKNOWN )
{
buffsize = Image_DXTGetLinearSize( format, hdr->lowResImageWidth, hdr->lowResImageHeight, 1, 0 );
}
return buffsize;
}
/*
================
Image_VTFCalcMipmapSize
stupid idea - last miplevel (1x1) put at begin of buffer
or DX8 format requries it, i'm don't know...
================
*/
size_t Image_VTFCalcMipmapSize( vtf_t *hdr, int mipNum )
{
size_t buffsize = 0;
int w, h, mipsize;
w = max( 1, hdr->width >> mipNum );
h = max( 1, hdr->height >> mipNum );
mipsize = Image_DXTGetLinearSize( image.type, w, h, 1, 0 );
return mipsize;
}
/*
================
Image_VTFCalcImageSize
main image size without header and lowres
================
*/
size_t Image_VTFCalcImageSize( vtf_t *hdr, qboolean oldformat )
{
size_t buffsize = 0;
int i, numSides = 1;
if( image.flags & IMAGE_CUBEMAP ) numSides = (oldformat) ? 6 : CB_FACECOUNT;
for( i = 0; i < image.num_mips; i++ )
buffsize += Image_VTFCalcMipmapSize( hdr, i ) * image.depth * numSides;
return buffsize;
}
/*
================
Image_VTFSwapBuffer
VTF: <format>
header: vtf_t
lowResImage[lowResSize]
mipmaps
layers (frames)
sides
DDS: <format>
header: dds_t
sides
mipmaps
layers
================
*/
void Image_VTFSwapBuffer( vtf_t *hdr, const byte *input, size_t input_size, qboolean oldformat )
{
int numSides = (image.flags & IMAGE_CUBEMAP) ? oldformat ? 6 : CB_FACECOUNT : 1;
vtex_t *texture = Mem_Alloc( Sys.imagepool, sizeof( vtex_t ));
qboolean ignore_mips = (image.cmd_flags & IL_IGNORE_MIPS);
uint i, j, k, out_size = 0;
byte *src, *dst;
// output size can't be more than input size
image.tempbuffer = Mem_Realloc( Sys.imagepool, image.tempbuffer, input_size );
src = (byte *)(input + input_size);
texture->numSides = numSides;
dst = image.tempbuffer;
// build image representation table
// NOTE: src = buffer + sizeof(vtf_t) + lowResSize;
for( i = 0; i < image.num_mips; i++ )
{
for( j = 0; j < image.depth; j++ )
{
for( k = 0; k < numSides; k++ )
{
texture->sides[k].mips[i].layers[j].size = Image_VTFCalcMipmapSize( hdr, i );
src -= texture->sides[k].mips[i].layers[j].size;
texture->sides[k].mips[i].layers[j].ofs = src - input;
texture->sides[k].mips[i].numlayers++;
texture->sides[k].nummips++;
}
}
}
// write to DXT buffer
for( i = 0; i < texture->numSides; i++ )
{
// NOTE: we needs to swap sides order
vside_t *side = &texture->sides[texture->numSides-i-1];
if( !oldformat && texture->numSides > 1 && i == 6 )
continue; // skip envmap if present
for( j = 0; j < side->nummips; j++ )
{
vmip_t *mip = &side->mips[j];
if( ignore_mips && j > 0 ) continue;
for( k = 0; k < mip->numlayers; k++ )
{
// NOTE: we needs to swap frames order
vlayer_t *layer = &mip->layers[mip->numlayers-k-1];
Mem_Copy( dst, input + layer->ofs, layer->size );
out_size += layer->size;
dst += layer->size;
}
}
}
// copy swapped buffer back to the input file, because tempbuffer
// too unreliable place for keep this data
Mem_Copy((byte *)input, image.tempbuffer, out_size );
if( ignore_mips ) image.num_mips = 1;
if( texture ) Mem_Free( texture );
image.size = out_size; // merge out size (minus envmap or identical)
}
/*
=============
Image_LoadVTF
=============
*/
qboolean Image_LoadVTF( const char *name, const byte *buffer, size_t filesize )
{
vtf_t vtf;
byte *fin;
string shortname;
qboolean oldformat = false;
uint hdrSize, biasSize, resSize, lowResSize;
int i, flags;
fin = (byte *)buffer;
Mem_Copy( &vtf, fin, sizeof( vtf ));
hdrSize = vtf.hdr_size;
biasSize = 0;
if( vtf.ident != VTFHEADER )
return false; // it's not a vtf file, just skip it
FS_FileBase( name, shortname );
// bounds check
i = vtf.ver_major;
if( i != VTF_VERSION )
{
MsgDev( D_ERROR, "Image_LoadVTF: %s has wrong ver (%i should be %i)\n", shortname, i, VTF_VERSION );
return false;
}
i = vtf.ver_minor;
if( i == VTF_SUBVERSION0 && vtf.hdr_size == 64 )
oldformat = true; // 7.0 hasn't envmap for cubemap images
// all other subversions are valid
image.width = vtf.width;
image.height = vtf.height;
if( !Image_ValidSize( name ))
return false;
// translate VF_flags into IMAGE_flags
flags = vtf.flags;
if(( flags & VF_ONEBITALPHA ) || ( flags & VF_EIGHTBITALPHA ))
image.flags |= IMAGE_HAS_ALPHA;
if( flags & VF_ENVMAP ) image.flags |= IMAGE_CUBEMAP;
image.type = Image_VTFFormat( vtf.imageFormat );
image.depth = vtf.num_frames;
image.num_mips = vtf.numMipLevels;
if( image.type == PF_UNKNOWN )
{
MsgDev( D_ERROR, "Image_LoadVTF: file (%s) has unknown format %i\n", shortname, vtf.imageFormat );
return false;
}
lowResSize = Image_VTFCalcLowResSize( &vtf);
resSize = Image_VTFCalcImageSize( &vtf, (i == 0));
i = hdrSize + lowResSize + resSize;
if( filesize > i )
{
// Valve changed format after subversion 3 and higher...
MsgDev( D_WARN, "Image_LoadVTF: %s have invalid size\n", name );
biasSize = filesize - i;
}
else if( filesize < i ) return false; // corrupted texture or somewhat
fin += hdrSize + biasSize + lowResSize; // go to main image
image.size = resSize; // base size can be merged after swapping
// convert VTF to DXT
Image_VTFSwapBuffer( &vtf, fin, resSize, oldformat );
// all supported VTF formats for now potentially has color
image.flags |= IMAGE_HAS_COLOR;
if( Image_ForceDecompress( ))
{
int offset, numsides = 1;
uint target = 1;
byte *buf = fin;
// if hardware loader is absent or image not power of two
// or user want load current side from cubemap we run software decompressing
if( image.flags & IMAGE_CUBEMAP ) numsides = 6;
Image_SetPixelFormat( image.width, image.height, image.depth ); // setup
image.size = image.ptr = 0;
image.cur_mips = image.num_mips;
image.num_mips = 0; // clear mipcount
for( i = 0, offset = 0; i < numsides; i++, buf += offset )
{
Image_SetPixelFormat( image.curwidth, image.curheight, image.curdepth );
offset = image.SizeOfFile; // move pointer
Image_DecompressDDS( buf, target + i );
}
// now we can change type to RGBA
if( image.hint != IL_HINT_NO ) image.flags &= ~IMAGE_CUBEMAP; // side extracted
image.type = PF_RGBA_32;
}
else
{
// vtf 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;
}