This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/ww3d2/texfcach.cpp

852 lines
No EOL
38 KiB
C++

/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program 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 3 of the License, or
** (at your option) any later version.
**
** This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* $Header: /Commando/Code/ww3d2/texfcach.cpp 5 8/24/01 3:23p Jani_p $ */
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/texfcach.cpp $*
* *
* $Author:: Jani_p $*
* *
* $Modtime:: 8/24/01 11:50a $*
* *
* $Revision:: 5 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* TextureFileCache::TextureFileCache -- Open cache. *
* ~TextureFileCache::TextureFileCache -- Shut down texture cache system. *
* TextureFileCache::Save_Texture -- Save the texture into the cache. *
* TextureFileCache::Load_Texture -- Load texture from cache into surface. *
* *TextureFileCache::_Create_File_Name -- Create a file name from prefix *
* *TextureFileCache::Load_Original_Texture_Surface -- Create the initial *
* *TextureFileCache::Open_Texture_Handle -- Set the TextureHandle and Header.. *
* TextureFileCache::Close_Texture_Handle -- Close the current texture so we can open anoth*
* TextureFileCache::Read_Texture -- Read in the texture into surface buffer. *
* *TextureFileCache::Create_First_Texture_As_Surface -- Load first texture into a surface. *
* *TextureFileCache::Find_Cached_Surface -- Search for a texture already cached. *
* TextureFileCache::Add_Cached_Surface -- Add a new cached texture. *
* *TFC::Get_Surface -- Load a texture reduced N times. *
* TextureFileCache::Reset_File -- virtual function to reset file and write out file. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#ifdef WW3D_DX8
#include "texfcach.h"
#include "lzo.h"
#include "lzo1x.h"
#include "mutex.h"
#include "thread.h"
#include <assert.h>
#include "wwdebug.h"
#include "simplevec.h"
#include "wwstring.h"
#include "textureloader.h"
#include "texture.h"
#include "ffactory.h"
#include <srSurfaceIOManager.hpp>
#include <srExtension.hpp>
#include <srCore.hpp>
#include <srBinIStream.hpp>
#include <srColorSurface.hpp>
#include <srTextureIFace.hpp>
#include <direct.h>
#include <stdlib.h>
#include <stdio.h>
#ifdef _UNIX
#include "osdep.h"
#endif
#define FILE_HEADER_NAME "Texture File Cache Header"
char *TextureFileCache::_FileNamePtr = NULL;
static int Instances=0;
static CriticalSectionClass mutex(0);
static SimpleVecClass<char> compression_buffer;
const char BUFFER_OVERRUN_TEST_VALUE=((char)0x7d);
static char* Get_Compression_Buffer(int size)
{
compression_buffer.Uninitialised_Grow(size+1);
compression_buffer[size]=BUFFER_OVERRUN_TEST_VALUE;
return &(compression_buffer[0]);
}
static void Verify_Compression_Buffer()
{
WWASSERT(compression_buffer[compression_buffer.Length()-1]==BUFFER_OVERRUN_TEST_VALUE);
}
////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////// TextureFileCache /////////////////////////////////////////////
/***********************************************************************************************
* TextureFileCache::TextureFileCache -- Open cache. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 05/07/1999 SKB : Created. *
* 06/27/2000 SKB : added CachedSurfaces *
* 08/14/2000 : Check for revision number. *
*=============================================================================================*/
TextureFileCache::TextureFileCache(const char *fileprefix):
File(_Create_File_Name(fileprefix)),
CurrentTexture(NULL),
TextureHandle(NULL),
Header(),
CachedSurfaces(),
Offsets(NULL),
NumCachedTextures(0)
{
WWASSERT(!Instances);
Instances++;
// This was allocated by _Create_File_Name() and need to go away now.
delete _FileNamePtr;
_FileNamePtr = NULL;
memset(CachedSurfaces, 0, sizeof(CachedSurfaces));
bool reset = false;
TagBlockHandle *handle = File.Open_Tag(FILE_HEADER_NAME);
if (handle) {
FileHeader fileheader;
// Read in header for others to use.
handle->Read(&fileheader, sizeof(fileheader));
if (fileheader.Version != FileHeader::TCF_VERSION) {
reset = true;
}
// Close down handle.
delete handle;
} else {
reset = true;
}
if (reset) {
Reset_File();
}
}
/***********************************************************************************************
* *TextureFileCache::_Create_File_Name -- Create a file name from prefix passed in. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* Caller of this function must free _FileNamePtr when done with it. *
* *
* HISTORY: *
* 05/13/1999 SKB : Created. *
*=============================================================================================*/
char *TextureFileCache::_Create_File_Name(const char *fileprefix)
{
bool addpath = (*fileprefix != '\\' && fileprefix[1] != ':');
assert(!_FileNamePtr);
_FileNamePtr = new char[strlen(fileprefix) + (addpath ? 256 : 6)];
char path[_MAX_PATH];
if (addpath && _getcwd(path, _MAX_PATH )) {
sprintf(_FileNamePtr, "%s\\%s.tfc", path, fileprefix);
} else {
// Create a file name.
strcpy(_FileNamePtr, fileprefix);
strcat(_FileNamePtr, ".tfc");
*_FileNamePtr = 0;
}
return(_FileNamePtr);
}
/***********************************************************************************************
* ~TextureFileCache::TextureFileCache -- Shut down texture cache system. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 05/07/1999 SKB : Created. *
*=============================================================================================*/
TextureFileCache::~TextureFileCache()
{
// Make sure we have shut down everything.
Close_Texture_Handle();
Instances--;
WWASSERT(!Instances);
}
/**************************************************************************
* TextureFileCache::Reset_File -- virtual function to reset file and wri *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 08/14/2000 : Created. *
*========================================================================*/
void TextureFileCache::Reset_File()
{
File.Reset_File();
TagBlockHandle *handle = File.Create_Tag(FILE_HEADER_NAME);
if (handle) {
FileHeader fileheader;
fileheader.Version = FileHeader::TCF_VERSION;
// Read in header for others to use.
handle->Write(&fileheader, sizeof(fileheader));
// Close down handle.
delete handle;
} else {
assert(false);
}
}
/***********************************************************************************************
* TextureFileCache::Save_Texture -- Save the texture into the cache. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 05/12/1999 SKB : Created. *
* 06/27/2000 SKB : added Header.PixelFormat *
* 06/27/2000 SKB : changed cached textures to surfaces *
* 08/14/2000 : save file's datatime stamp. *
*=============================================================================================*/
bool TextureFileCache::Save_Texture(const char *texturename, srTextureIFace::MultiRequest& mreq, srColorSurfaceIFace& origsurface)
{
CriticalSectionClass::LockClass m(mutex);
int idx;
unsigned lod;
// Open up our new texture.
Open_Texture_Handle(texturename);
// Create a new texture now.
TextureHandle = File.Create_Tag(texturename);
if (!TextureHandle) {
Close_Texture_Handle();
return(false);
}
// Setup the Header.
FileClass *asset=_TheFileFactory->Get_File(texturename);
WWASSERT( asset );
asset->Open();
Header.FileTime = asset->Get_Date_Time();
Header.NumMipMaps = (mreq.smallLOD - mreq.largeLOD) + 1;
Header.LargestWidth = mreq.levels[mreq.largeLOD]->getWidth();
Header.LargestHeight = mreq.levels[mreq.largeLOD]->getHeight();
Header.SourceWidth = origsurface.getWidth();
Header.SourceHeight = origsurface.getHeight();
mreq.levels[mreq.largeLOD]->getPixelFormat(Header.PixelFormat);
origsurface.getPixelFormat(Header.SourcePixelFormat);
_TheFileFactory->Return_File(asset);
asset=NULL;
// Write it out.
TextureHandle->Write(&Header, sizeof(Header));
// Setup offset table.
Offsets = new OffsetTableType[Header.NumMipMaps + 1];
// Write for now, but we will need to seek back to write final data.
int tableoffset = TextureHandle->Tell();
TextureHandle->Write(Offsets, sizeof(OffsetTableType) * (Header.NumMipMaps + 1));
// Now write out the textures.
for (idx = 0, lod = mreq.largeLOD; lod <= mreq.smallLOD; idx++, lod++) {
srColorSurface *surface = mreq.levels[lod];
WWASSERT(surface->getDataPtr());
Offsets[idx].Offset = TextureHandle->Tell();
Offsets[idx].Size = surface->getDataSize();
// Save data pointers so we don't need to read them from disk next time.
Add_Cached_Surface(surface);
int compsize,lzocode;
int buf_size=LZO_BUFFER_SIZE(surface->getDataSize());
lzocode = LZOCompressor::Compress( (const lzo_byte *) surface->getDataPtr(),
(lzo_uint) surface->getDataSize(),
(lzo_byte *) Get_Compression_Buffer(buf_size),
(unsigned *) &compsize);
// Lots-o-test to make sure that the compression did what we want.
assert(lzocode == LZO_E_OK);
Verify_Compression_Buffer();
int readin = TextureHandle->Write(Get_Compression_Buffer(compsize), compsize);
assert(readin == compsize);
}
int pos = TextureHandle->Seek(0, SEEK_END);
// Set last one so we can get a compressed size of last texture.
Offsets[idx].Offset = TextureHandle->Tell();
Offsets[idx].Size = 0;
// Now write out header for good.
TextureHandle->Seek(tableoffset, SEEK_SET);
TextureHandle->Write(Offsets, sizeof(OffsetTableType) * (Header.NumMipMaps + 1));
pos = TextureHandle->Seek(0, SEEK_END);
// End write access so that it gets flushed out to disk.
TextureHandle->End_Write_Access();
return (true);
}
/***********************************************************************************************
* *TextureFileCache::Load_Original_Texture_Surface -- Create the initial surface that would *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* Sureface is only needed to get default values. The texel data does not get filled in. *
* *
* HISTORY: *
* 05/13/1999 SKB : Created. *
*=============================================================================================*/
srColorSurfaceIFace *TextureFileCache::Load_Original_Texture_Surface(const char *texturename)
{
CriticalSectionClass::LockClass m(mutex);
if (Open_Texture_Handle(texturename)) {
// Create surface that we wish to return.
srColorSurfaceIFace *surface = new srColorSurface(Header.SourcePixelFormat, Header.SourceWidth, Header.SourceHeight);
return(surface);
}
return(NULL);
}
/***********************************************************************************************
* *TFC::Get_Surface -- Load a texture reduced N times. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/26/2000 SKB : Created. *
*=============================================================================================*/
srColorSurfaceIFace *TextureFileCache::Get_Surface(const char *texturename, unsigned int reduce_factor)
{
CriticalSectionClass::LockClass m(mutex);
// If we can't get a handle, then we can't load the texture.
if (!Open_Texture_Handle(texturename)) {
return(0);
}
if (reduce_factor >= (unsigned int)(Header.NumMipMaps)) {
reduce_factor=Header.NumMipMaps-1;
}
// Figure out the width and height of texture.
int width = Header.LargestWidth >> reduce_factor;
int height = Header.LargestHeight >> reduce_factor;
if (!width) width = 1;
if (!height) height = 1;
// Create surface to return.
srColorSurface *surface = new srColorSurface(Header.PixelFormat, width, height);
int size = Texture_Size(reduce_factor);
assert(size == surface->getDataSize());
// See if this texture is already in memory.
srColorSurface *cached = Find_Cached_Surface(size);
if (cached) {
assert(size == cached->getDataSize());
memcpy(surface->getDataPtr(), cached->getDataPtr(), size);
} else {
// Cache pointer so if we need texture again, we will have a pointer to
// valid texture data instead of reading it from the file.
Add_Cached_Surface(surface);
// Read texture in - note that Textures[lod] has to be set prior to this.
Read_Texture(reduce_factor, surface);
}
return(surface);
}
/***********************************************************************************************
* TextureFileCache::Load_Texture -- Load texture from cache into surface. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 05/12/1999 SKB : Created. *
* 06/02/1999 SKB : deal with texture requests larger then what is in cache. *
* 06/27/2000 SKB : changed cached textures to surfaces *
*=============================================================================================*/
bool TextureFileCache::Load_Texture(const char *texturename, srTextureIFace::MultiRequest& mreq)
{
CriticalSectionClass::LockClass m(mutex);
// If we can't get a handle, then we can't load the texture.
if (!Open_Texture_Handle(texturename)) {
return(false);
}
// NOTE: Mip maps that are saved in the cache are the first mip maps requested and created
// for a given texture name. If a user susequently requests mip maps of different
// sizes that are not in the cache, they will be created by use of surface copy functions.
// When this happens it is unfortunate because it indicates some sort of inefficiency.
// In code below I have put in comments 'A WASTE' to indicate what part of code deels with it.
int idx;
unsigned lod;
// Get largest size of texture in file (idx) and what is required (lod).
int idxsize = Texture_Size(0);
int lodsize = -1;
// Now skip any mip maps that we want but are not in the cache. (A WASTE).
for (lod = mreq.largeLOD; lod <= mreq.smallLOD; lod++) {
lodsize = mreq.levels[lod]->getDataSize();
if (lodsize <= idxsize) {
break;
}
}
// make sure largeLOD <= smallLOD.
assert(lodsize != -1);
// Skip past all mipmap levels that we do not want in the file.
for (idx = 0; idx < Header.NumMipMaps; idx++) {
idxsize = Texture_Size(idx);
if (idxsize <= lodsize) {
break;
}
}
// Save off the first lod and what index it is.
unsigned firstlod = lod;
// Now if we have in the file something that the texture needs, load it. (This is most desired.)
if (idxsize == lodsize) {
// Read in textures that we have and need.
while ((lod <= mreq.smallLOD) && (idx < Header.NumMipMaps)) {
srColorSurface *surface = mreq.levels[lod];
int size = surface->getDataSize(); assert(Texture_Size(idx) == size);
// See if we can find the texture already cached.
srColorSurface *cached = Find_Cached_Surface(size);
// See if texture has already been read in.
if (cached) {
assert(size == cached->getDataSize());
// Copy data over using size of dest surface incase asserts disabled and sizes are not right.
memcpy(surface->getDataPtr(), cached->getDataPtr(), size);
} else {
// Cache pointer so if we need texture again, we will have a pointer to
// valid texture data instead of reading it from the file.
Add_Cached_Surface(surface);
// Read texture in - note that Textures[lod] has to be set prior to this.
Read_Texture(idx, surface);
}
idx++, lod++;
}
}
// Save last lod.
unsigned lastlod = lod - 1;
// largest surface loaded.
srColorSurfaceIFace *surface = NULL;
if (firstlod < lastlod) {
surface = mreq.levels[firstlod];
surface->addReference();
}
// If user requested a texture larger then what we have saved, then
// we will have to scale it up for him.
// NOTE: This is normally not desired. It typically means the user is
// creating a texture larger then the original source - a waste.
if (mreq.largeLOD < firstlod) {
// If there has not been a surface loaded yet, then do so now.
if (!surface) {
surface = Create_First_Texture_As_Surface(mreq.levels[mreq.largeLOD]);
}
// Now scale the largest mip map we had up to what user wants (ONCE AGAIN: A WASTE).
for (lod = mreq.largeLOD; lod <= firstlod; lod++) {
// Do a surface scale.
surface->copy(*mreq.levels[lod]);
}
}
// Are there more smaller lod's that are not in the cache?
if (lastlod < mreq.smallLOD) {
// If there has not been a surface loaded yet, then do so now.
if (!surface) {
surface = Create_First_Texture_As_Surface(mreq.levels[mreq.largeLOD]);
}
// Now scale the largest mip map we had up to what user wants (ONCE AGAIN: A WASTE).
for (lod = lastlod + 1; lod <= mreq.smallLOD; lod++) {
// Do a surface scale.
surface->copy(*mreq.levels[lod]);
}
}
// Done with this one.
if (surface) {
surface->release();
}
return (true);
}
/***********************************************************************************************
* TextureFileCache::Read_Texture -- Read in the texture into surface buffer. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/01/1999 SKB : Created. *
* 06/27/2000 SKB : changed cached textures to surfaces *
*=============================================================================================*/
void TextureFileCache::Read_Texture(int offsetidx, srColorSurface *surface)
{
// Seek to correct spot.
TextureHandle->Seek(Offsets[offsetidx].Offset, SEEK_SET);
// Figure out how much read in and make sure it is correct size.
int compsize = Compressed_Texture_Size(offsetidx);
// Read in texture from cache.
int readin = TextureHandle->Read(Get_Compression_Buffer(compsize), compsize);
assert(readin == compsize);
// Set values so we can assert if overrun.
Verify_Compression_Buffer();
// Decompress into texture pointer.
int lzocode, decompsize;
lzocode = LZOCompressor::Decompress( (const lzo_byte*) Get_Compression_Buffer(compsize),
(lzo_uint) compsize,
(lzo_byte*) surface->getDataPtr(),
(unsigned *) &decompsize);
// Lots-o-test to make sure that the compression did what we want.
assert(lzocode == LZO_E_OK);
assert(decompsize == Texture_Size(offsetidx));
Verify_Compression_Buffer();
}
/***********************************************************************************************
* *TextureFileCache::Create_First_Texture_As_Surface -- Load first texture into a surface. *
* *
* INPUT: *
* srColorSurfaceIFace *surfacetype - only used to create surface type we want. *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/02/1999 SKB : Created. *
*=============================================================================================*/
srColorSurfaceIFace *TextureFileCache::Create_First_Texture_As_Surface(srColorSurfaceIFace *surfacetype)
{
srColorSurfaceIFace::PixelFormat pf;
// Get our pixel format.
surfacetype->getPixelFormat(pf);
// Create a surface we can load the largest texture into it
srColorSurface *surface = new srColorSurface(pf, Header.LargestWidth, Header.LargestHeight);
assert(Texture_Size(0) == surface->getDataSize());
// Read in texture to our surface data..
Read_Texture(0, surface);
return(surface);
}
/***********************************************************************************************
* *TextureFileCache::Open_Texture_Handle -- Set the TextureHandle and Header.. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 05/13/1999 SKB : Created. *
*=============================================================================================*/
bool TextureFileCache::Open_Texture_Handle(const char *fname)
{
if (TextureHandle) {
assert(CurrentTexture);
if (!strcmpi(fname, CurrentTexture)) {
return(true);
}
// Wrong texture, close it down so we can open another.
Close_Texture_Handle();
}
if (!CurrentTexture) {
CurrentTexture = strdup(fname);
}
#if 0
// If this is texture is newer then our texture file, reset file.
// This means that next time the texture cache file gets opened, all the
// previous textures will be loaded.
// SKB 8/14/2000 : Removed to put in better system below..
RawFileClass asset(fname);
asset.Open();
if (asset.Get_Date_Time() > File.Get_Date_Time()) {
Reset_File();
}
#endif
// See if we have the texture in the file cache yet.
TextureHandle = File.Open_Tag(fname);
if (TextureHandle) {
// Read in header for others to use.
TextureHandle->Read(&Header, sizeof(Header));
file_auto_ptr asset(_TheFileFactory, fname);
asset->Open();
// Make sure it is same file.
if (Header.FileTime != asset->Get_Date_Time()) {
delete TextureHandle;
TextureHandle = NULL;
Reset_File();
return(false);
}
// Load up the offset table.
Offsets = new OffsetTableType[Header.NumMipMaps + 1];
TextureHandle->Read(Offsets, sizeof(OffsetTableType) * (Header.NumMipMaps + 1));
return(true);
}
return(false);
}
/***********************************************************************************************
* TextureFileCache::Close_Texture_Handle -- Close the current texture so we can open another. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/01/1999 SKB : Created. *
*=============================================================================================*/
void TextureFileCache::Close_Texture_Handle()
{
if (CurrentTexture) {
free(CurrentTexture);
CurrentTexture = NULL;
if (TextureHandle) {
delete TextureHandle;
TextureHandle = NULL;
}
while (NumCachedTextures--) {
assert(CachedSurfaces[NumCachedTextures]);
CachedSurfaces[NumCachedTextures]->release();
CachedSurfaces[NumCachedTextures] = 0;
}
NumCachedTextures = 0;
if (Offsets) {
delete[] Offsets;
Offsets = NULL;
}
} else {
assert(!CurrentTexture);
}
}
/***********************************************************************************************
* *TextureFileCache::Find_Cached_Surface -- Search for a texture already cached. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/02/1999 SKB : Created. *
*=============================================================================================*/
srColorSurface *TextureFileCache::Find_Cached_Surface(int size)
{
// Search through each allocated record for the right size.
for (int idx = 0; idx < NumCachedTextures; idx++) {
if (CachedSurfaces[idx]->getDataSize() == size) {
return(CachedSurfaces[idx]);
}
}
return(NULL);
}
/***********************************************************************************************
* TextureFileCache::Add_Cached_Surface -- Add a new cached texture. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 06/02/1999 SKB : Created. *
*=============================================================================================*/
void TextureFileCache::Add_Cached_Surface(srColorSurface *surface)
{
// return;
assert(!Find_Cached_Surface(surface->getDataSize()));
// If are cache is full, use the smallest one's space.
int surface_idx = -1;
if (NumCachedTextures == MAX_CACHED_SURFACES) {
// Make sure there are some cached textures.
// Assume first is smallest.
int smallsize = 0xfffffff;
int smallidx = -1;
// Look for any others that might be smaller.
for (int idx = 0; idx < NumCachedTextures; idx++) {
int size = CachedSurfaces[idx]->getDataSize();
if (size < smallsize) {
// Found one.
smallsize = size;
smallidx = idx;
}
}
surface_idx = smallidx;
CachedSurfaces[idx]->release();
CachedSurfaces[idx] = 0;
} else {
// Use next slot in array.
assert(NumCachedTextures < MAX_CACHED_SURFACES);
surface_idx = NumCachedTextures;
NumCachedTextures++;
}
assert(surface_idx >= 0);
assert(!CachedSurfaces[surface_idx]);
CachedSurfaces[surface_idx] = surface;
surface->addReference();
}
bool TextureFileCache::Validate_Texture(const char* FileName)
{
// The functions used in here are all thread safe so this function doesn't have to be mutex guarded
if (!Texture_Exists(FileName)) {
if (!TextureLoader::Texture_File_Exists(FileName)) {
return false;
}
// We need to load the surface from the file first to determine the real size
srColorSurfaceIFace* TempSurfacePtr = ::Load_Surface(FileName);
if (!TempSurfacePtr) return false;
int w=TempSurfacePtr->getWidth();
srColorSurfaceIFace::PixelFormat pf;
TempSurfacePtr->getPixelFormat(pf);
srTextureIFace::MultiRequest mr;
for (int cnt=0;cnt<srTextureIFace::MAX_LOD;++cnt) mr.levels[cnt]=0;
for (cnt=0;cnt<srTextureIFace::MAX_LOD;) {
mr.levels[cnt]=new srColorSurface(pf,w,w);
mr.levels[cnt]->copy(*TempSurfacePtr);
w>>=1;
++cnt;
if (!w) break;
}
mr.smallLOD=cnt-1;
mr.largeLOD=0;
Save_Texture(FileName, mr, *TempSurfacePtr);
TempSurfacePtr->release();
TempSurfacePtr=0;
for (cnt=0;cnt<srTextureIFace::MAX_LOD;++cnt) {
if (mr.levels[cnt]) mr.levels[cnt]->release();
}
}
return true;
}
int TextureFileCache::Texture_Exists(const char *fname)
{
CriticalSectionClass::LockClass m(mutex);
return(File.Does_Tag_Exist(fname));
}
#endif // WW3D_DX8