tge/engine/core/resManager.cc
2025-02-17 23:17:30 -06:00

1218 lines
34 KiB
C++
Executable File

//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "core/tVector.h"
#include "core/stream.h"
#include "core/fileStream.h"
#include "core/zipSubStream.h"
#include "core/zipAggregate.h"
#include "core/zipHeaders.h"
#include "core/resizeStream.h"
#include "core/frameAllocator.h"
#include "core/resManager.h"
#include "core/findMatch.h"
#include "console/console.h"
#include "console/consoleTypes.h"
#include "util/safeDelete.h"
ResManager *ResourceManager = NULL;
char *ResManager::smExcludedDirectories = ".svn;CVS";
//------------------------------------------------------------------------------
ResourceObject::ResourceObject ()
{
next = NULL;
prev = NULL;
lockCount = 0;
mInstance = NULL;
}
void ResourceObject::destruct ()
{
// If the resource was not loaded because of an error, the resource
// pointer will be NULL
SAFE_DELETE(mInstance);
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
ResManager::ResManager ()
{
echoFileNames = 0;
primaryPath[0] = 0;
writeablePath[0] = 0;
pathList = NULL;
resourceList.nextResource = NULL;
resourceList.next = NULL;
resourceList.prev = NULL;
timeoutList.nextResource = NULL;
timeoutList.next = NULL;
timeoutList.prev = NULL;
registeredList = NULL;
mLoggingMissingFiles = false;
}
void ResManager::fileIsMissing(const char *fileName)
{
if(mLoggingMissingFiles)
{
char *name = dStrdup(fileName);
// Con::printf("> Missing file: %s", fileName);
mMissingFileList.push_back(name);
}
}
void ResManager::setMissingFileLogging(bool logging)
{
mLoggingMissingFiles = logging;
if(!mLoggingMissingFiles)
clearMissingFileList();
}
void ResManager::clearMissingFileList()
{
while(mMissingFileList.size())
{
dFree(mMissingFileList[0]);
mMissingFileList.pop_front();
}
mMissingFileList.clear();
}
bool ResManager::getMissingFileList(Vector<char *> &list)
{
if(!mMissingFileList.size())
return false;
for(U32 i = 0; i < mMissingFileList.size();i ++)
{
for(U32 j = 0; j < list.size(); j++)
{
if(!dStrcmp(list[j], mMissingFileList[i]))
{
dFree(mMissingFileList[i]);
mMissingFileList[i] = NULL;
break;
}
}
if(mMissingFileList[i])
list.push_back(mMissingFileList[i]);
}
mMissingFileList.clear();
return true;
}
void ResourceObject::getFileTimes (FileTime * createTime, FileTime * modifyTime)
{
char buffer[1024];
dSprintf (buffer, sizeof (buffer), "%s/%s/%s",
Platform::getWorkingDirectory (), path, name);
Platform::getFileTimes (buffer, createTime, modifyTime);
}
//------------------------------------------------------------------------------
ResManager::~ResManager ()
{
purge ();
// volume list should be gone.
if (pathList)
dFree (pathList);
for (ResourceObject * walk = resourceList.nextResource; walk;
walk = walk->nextResource)
walk->destruct ();
while (resourceList.nextResource)
freeResource (resourceList.nextResource);
while (registeredList)
{
RegisteredExtension *temp = registeredList->next;
delete registeredList;
registeredList = temp;
}
}
#ifdef TORQUE_DEBUG
void ResManager::dumpLoadedResources ()
{
ResourceObject *walk = resourceList.nextResource;
while (walk != NULL)
{
if (walk->mInstance != NULL)
{
Con::errorf ("LoadedRes: %s/%s (%d)", walk->path, walk->name,
walk->lockCount);
}
walk = walk->nextResource;
}
}
ConsoleFunction(dumpResourceStats, void, 1, 1, "Dump information about resources. Debug only!")
{
ResourceManager->dumpLoadedResources();
}
#endif
//------------------------------------------------------------------------------
void ResManager::create ()
{
AssertFatal (ResourceManager == NULL,
"ResourceManager::create: manager already exists.");
ResourceManager = new ResManager;
Con::addVariable("Pref::ResourceManager::excludedDirectories", TypeString, &smExcludedDirectories);
}
//------------------------------------------------------------------------------
void ResManager::destroy ()
{
AssertFatal (ResourceManager != NULL,
"ResourceManager::destroy: manager does not exist.");
delete ResourceManager;
ResourceManager = NULL;
}
//------------------------------------------------------------------------------
void ResManager::setFileNameEcho (bool on)
{
echoFileNames = on;
}
//------------------------------------------------------------------------------
bool ResManager::isValidWriteFileName (const char *fn)
{
// all files must be based off the VFS
if (fn[0] == '/' || dStrchr (fn, ':'))
return false;
if (!writeablePath[0])
return true;
// get the path to the file
const char * path = dStrrchr (fn, '/');
if (!path)
path = fn;
else
{
if (!dStrchr (path, '.'))
return false;
}
// now loop through the writeable path.
const char * start = writeablePath;
S32 pathLen = path - fn;
for (;;)
{
const char * end = dStrchr (writeablePath, ';');
if (!end)
end = writeablePath + dStrlen (writeablePath);
if (end - start == pathLen && !dStrnicmp (start, path, pathLen))
return true;
if (end[0])
start = end + 1;
else
break;
}
return false;
}
void ResManager::setWriteablePath (const char *path)
{
dStrcpy (writeablePath, path);
}
//------------------------------------------------------------------------------
static const char * buildPath (StringTableEntry path, StringTableEntry file)
{
static char buf[1024];
if (path)
dSprintf (buf, sizeof (buf), "%s/%s", path, file);
else
dStrcpy (buf, file);
return buf;
}
//------------------------------------------------------------------------------
static void getPaths (const char *fullPath, StringTableEntry & path,
StringTableEntry & fileName)
{
static char buf[1024];
char *ptr = (char *) dStrrchr (fullPath, '/');
if (!ptr)
{
path = NULL;
fileName = StringTable->insert (fullPath);
}
else
{
S32 len = ptr - fullPath;
dStrncpy (buf, fullPath, len);
buf[len] = 0;
fileName = StringTable->insert (ptr + 1);
path = StringTable->insert (buf);
}
}
//------------------------------------------------------------------------------
bool ResManager::scanZip (ResourceObject * zipObject)
{
// now open the volume and add all its resources to the dictionary
ZipAggregate zipAggregate;
if (zipAggregate.
openAggregate (buildPath (zipObject->zipPath, zipObject->zipName)) ==
false)
{
Con::errorf ("Error opening zip (%s/%s), need to handle this better...",
zipObject->zipPath, zipObject->zipName);
return false;
}
ZipAggregate::iterator itr;
for (itr = zipAggregate.begin (); itr != zipAggregate.end (); itr++)
{
const ZipAggregate::FileEntry & rEntry = * itr;
ResourceObject * ro =
createZipResource (rEntry.pPath, rEntry.pFileName,
zipObject->zipPath,
zipObject->zipName);
ro->flags = ResourceObject::VolumeBlock;
ro->fileSize = rEntry.fileSize;
ro->compressedFileSize = rEntry.compressedFileSize;
ro->fileOffset = rEntry.fileOffset;
dictionary.pushBehind (ro, ResourceObject::File);
}
zipAggregate.closeAggregate ();
return true;
}
//------------------------------------------------------------------------------
void ResManager::searchPath (const char *path)
{
AssertFatal (path != NULL, "No path to dump?");
Vector < Platform::FileInfo > fileInfoVec;
Platform::dumpPath (path, fileInfoVec);
for (U32 i = 0; i < fileInfoVec.size (); i++)
{
Platform::FileInfo & rInfo = fileInfoVec[i];
// Create a resource for this file...
//
ResourceObject *ro = createResource (rInfo.pFullPath, rInfo.pFileName);
dictionary.pushBehind (ro, ResourceObject::File);
ro->flags = ResourceObject::File;
ro->fileOffset = 0;
ro->fileSize = rInfo.fileSize;
ro->compressedFileSize = rInfo.fileSize;
// see if it's a zip
const char *extension = dStrrchr (ro->name, '.');
if (extension && !dStricmp (extension, ".zip"))
{
// Copy the path and files names to the zips resource object
ro->zipName = rInfo.pFileName;
ro->zipPath = rInfo.pFullPath;
scanZip(ro);
}
}
}
//------------------------------------------------------------------------------
bool ResManager::setModZip(const char* path)
{
// Get the path and add .zip to the end of the dir
const char* ext = ".zip";
char* modPath = new char[dStrlen(path) + dStrlen(ext) + 1]; // make enough room.
dStrcpy(modPath, path);
dStrcat(modPath, ext);
// Now we have to go through the root and look for our zipped up mod
// this is unfortunately necessary because there is no means to get
// a individual files properties -- we can only do it in one
// big dump
Vector < Platform::FileInfo > pathInfo;
Platform::dumpPath (Platform::getWorkingDirectory(), pathInfo);
for(U32 i = 0; i < pathInfo.size(); i++)
{
Platform::FileInfo &file = pathInfo[i];
if(!dStricmp(file.pFileName, modPath))
{
// Setup the resource to the zip file itself
ResourceObject *zip = createResource(NULL, file.pFileName);
dictionary.pushBehind(zip, ResourceObject::File);
zip->flags = ResourceObject::File;
zip->fileOffset = 0;
zip->fileSize = file.fileSize;
zip->compressedFileSize = file.fileSize;
zip->zipName = file.pFileName;
zip->zipPath = NULL;
// Setup the resource for the zip contents
// ..now open the volume and add all its resources to the dictionary
ZipAggregate zipAggregate;
if (zipAggregate.openAggregate(zip->zipName) == false)
{
delete [] modPath;
return false;
}
ZipAggregate::iterator itr;
for (itr = zipAggregate.begin (); itr != zipAggregate.end (); itr++)
{
const ZipAggregate::FileEntry &rEntry = *itr;
ResourceObject *ro = createZipResource(rEntry.pPath, rEntry.pFileName, zip->zipPath, zip->zipName);
ro->flags = ResourceObject::VolumeBlock;
ro->fileSize = rEntry.fileSize;
ro->compressedFileSize = rEntry.compressedFileSize;
ro->fileOffset = rEntry.fileOffset;
dictionary.pushBehind (ro, ResourceObject::File);
}
zipAggregate.closeAggregate ();
// Break from the loop since we got our one file
delete [] modPath;
return true;
}
}
delete [] modPath;
return false;
}
//------------------------------------------------------------------------------
void ResManager::initExcludedDirectories()
{
// Set up our excluded directories.
Platform::clearExcludedDirectories();
// ignored is a semi-colon delimited list of names.
char *working = dStrdup(smExcludedDirectories);
char* temp = dStrtok( working, ";" );
while ( temp )
{
Platform::addExcludedDirectory(temp);
temp = dStrtok( NULL, ";" );
}
dFree(working);
}
void ResManager::setModPaths (U32 numPaths, const char **paths)
{
// detach all the files.
for (ResourceObject * pwalk = resourceList.nextResource; pwalk;
pwalk = pwalk->nextResource)
pwalk->flags = ResourceObject::Added;
U32 pathLen = 0;
// Set up exclusions.
initExcludedDirectories();
// Make sure invalid paths are not processed
Vector<const char*> validPaths;
// Determine if the mod paths are valid
for (U32 i = 0; i < numPaths; i++)
{
if (!Platform::isSubDirectory (Platform::getWorkingDirectory (), paths[i]) || Platform::isExcludedDirectory(paths[i]))
{
if (!setModZip(paths[i]))
{
Con::errorf ("setModPaths: invalid mod path directory name: '%s'", paths[i]);
continue;
}
}
pathLen += (dStrlen (paths[i]) + 1);
// Load zip first so that local files override
setModZip(paths[i]);
searchPath (paths[i]);
// Copy this path to the validPaths list
validPaths.push_back(paths[i]);
}
Platform::clearExcludedDirectories();
if (!pathLen)
return;
// Build the internal path list string
pathList = (char *) dRealloc (pathList, pathLen);
dStrcpy (pathList, validPaths[0]);
U32 strlen;
for (U32 i = 1; i < validPaths.size(); i++)
{
strlen = dStrlen (pathList);
dSprintf (pathList + strlen, pathLen - strlen, ";%s", validPaths[i]);
}
// Unlink all 'added' that aren't loaded.
ResourceObject *rwalk = resourceList.nextResource, *rtemp;
while (rwalk != NULL)
{
if ((rwalk->flags & ResourceObject::Added) && !rwalk->mInstance)
{
rwalk->unlink ();
dictionary.remove (rwalk);
rtemp = rwalk->nextResource;
freeResource (rwalk);
rwalk = rtemp;
}
else
rwalk = rwalk->nextResource;
}
}
ConsoleFunction( setModPaths, void, 2, 2, "(string paths)"
"Set the mod paths the resource manager is using. These are semicolon delimited.")
{
char buf[512];
dStrncpy(buf, argv[1], sizeof(buf) - 1);
buf[511] = '\0';
Vector<char*> paths;
char* temp = dStrtok( buf, ";" );
while ( temp )
{
if ( temp[0] )
paths.push_back( temp );
temp = dStrtok( NULL, ";" );
}
ResourceManager->setModPaths( paths.size(), (const char**) paths.address() );
}
//------------------------------------------------------------------------------
const char * ResManager::getModPaths ()
{
return ((const char *) pathList);
}
ConsoleFunction( getModPaths, const char*, 1, 1, "Return the mod paths the resource manager is using.")
{
return( ResourceManager->getModPaths() );
}
//------------------------------------------------------------------------------
S32 ResManager::getSize (const char *fileName)
{
ResourceObject * ro = find (fileName);
if (!ro)
return 0;
else
return ro->fileSize;
}
//------------------------------------------------------------------------------
const char * ResManager::getFullPath (const char *fileName, char *path, U32 pathlen)
{
AssertFatal (fileName, "ResourceManager::getFullPath: fileName is NULL");
AssertFatal (path, "ResourceManager::getFullPath: path is NULL");
ResourceObject *obj = find (fileName);
if (!obj)
dStrcpy (path, fileName);
else
dSprintf (path, pathlen, "%s/%s", obj->path, obj->name);
return path;
}
//------------------------------------------------------------------------------
const char *ResManager::getPathOf (const char *fileName)
{
AssertFatal (fileName, "ResourceManager::getPathOf: fileName is NULL");
ResourceObject *obj = find (fileName);
if (!obj)
return NULL;
else
return obj->path;
}
//------------------------------------------------------------------------------
const char * ResManager::getModPathOf (const char *fileName)
{
AssertFatal (fileName, "ResourceManager::getModPathOf: fileName is NULL");
if (!pathList)
return NULL;
ResourceObject *obj = find (fileName);
if (!obj)
return NULL;
char buffer[256];
char *base;
const char *list = pathList;
do
{
base = buffer;
*base = 0;
while (*list && *list != ';')
{
*base++ = *list++;
}
if (*list == ';')
++list;
*base = 0;
if (dStrncmp (buffer, obj->path, (base - buffer)) == 0)
return StringTable->insert (buffer);
}
while (*list);
return NULL;
}
//------------------------------------------------------------------------------
const char *ResManager::getBasePath ()
{
if (!pathList)
return NULL;
const char *base = dStrrchr (pathList, ';');
return base ? (base + 1) : pathList;
}
//------------------------------------------------------------------------------
void ResManager::registerExtension (const char *name, RESOURCE_CREATE_FN create_fn)
{
AssertFatal (!getCreateFunction (name),
"ResourceManager::registerExtension: file extension already registered.");
const char *extension = dStrrchr (name, '.');
AssertFatal (extension,
"ResourceManager::registerExtension: file has no extension.");
RegisteredExtension *add = new RegisteredExtension;
add->mExtension = StringTable->insert (extension);
add->mCreateFn = create_fn;
add->next = registeredList;
registeredList = add;
}
//------------------------------------------------------------------------------
RESOURCE_CREATE_FN ResManager::getCreateFunction (const char *name)
{
const char * s = dStrrchr (name, '.');
if (!s)
return (NULL);
RegisteredExtension * itr = registeredList;
while (itr)
{
if (dStricmp (s, itr->mExtension) == 0)
return (itr->mCreateFn);
itr = itr->next;
}
return (NULL);
}
//------------------------------------------------------------------------------
void ResManager::unlock (ResourceObject * obj)
{
if (!obj)
return;
AssertFatal (obj->lockCount > 0,
"ResourceManager::unlock: lock count is zero.");
//set the timeout to the max requested
if (--obj->lockCount == 0)
obj->linkAfter (&timeoutList);
}
//------------------------------------------------------------------------------
// gets the crc of the file, ignores the stream type
bool ResManager::getCrc (const char *fileName, U32 & crcVal,
const U32 crcInitialVal)
{
ResourceObject *obj = find (fileName);
if (!obj)
return (false);
// check if in a volume
if (obj->flags & (ResourceObject::VolumeBlock | ResourceObject::File))
{
// can't crc locked resources...
if (obj->lockCount)
return false;
// get rid of the resource
// have to make sure user can't have it sitting around in the resource cache
obj->unlink ();
obj->destruct ();
Stream *stream = openStream (obj);
U32 waterMark = 0xFFFFFFFF;
U8 *buffer;
U32 maxSize = FrameAllocator::getHighWaterMark () - FrameAllocator::getWaterMark ();
if (maxSize < obj->fileSize)
buffer = new U8[obj->fileSize];
else
{
waterMark = FrameAllocator::getWaterMark ();
buffer = (U8 *) FrameAllocator::alloc (obj->fileSize);
}
stream->read (obj->fileSize, buffer);
// get the crc value
crcVal = calculateCRC (buffer, obj->fileSize, crcInitialVal);
if (waterMark == 0xFFFFFFFF)
delete[]buffer;
else
FrameAllocator::setWaterMark (waterMark);
closeStream (stream);
return (true);
}
return (false);
}
//------------------------------------------------------------------------------
ResourceObject *ResManager::load (const char *fileName, bool computeCRC)
{
// if filename is not known, exit now
ResourceObject *obj = find (fileName);
if (!obj)
return NULL;
// if no one has a lock on this, but it's loaded and it needs to
// be CRC'd, delete it and reload it.
if (!obj->lockCount && computeCRC && obj->mInstance)
obj->destruct ();
obj->lockCount++;
obj->unlink (); // remove from purge list
if (!obj->mInstance)
{
obj->mInstance = loadInstance (obj, computeCRC);
if (!obj->mInstance)
{
obj->lockCount--;
return NULL;
}
}
return obj;
}
//------------------------------------------------------------------------------
ResourceInstance * ResManager::loadInstance (const char *fileName, bool computeCRC)
{
// if filename is not known, exit now
ResourceObject *obj = find (fileName);
if (!obj)
return NULL;
return loadInstance (obj, computeCRC);
}
//------------------------------------------------------------------------------
static const char *alwaysCRCList = ".ter.dif.dts";
ResourceInstance * ResManager::loadInstance (ResourceObject * obj, bool computeCRC)
{
Stream *stream = openStream (obj);
if (!stream)
return NULL;
if (!computeCRC)
{
const char *x = dStrrchr (obj->name, '.');
if (x && dStrstr (alwaysCRCList, x))
computeCRC = true;
}
if (computeCRC)
obj->crc = calculateCRCStream (stream, InvalidCRC);
else
obj->crc = InvalidCRC;
RESOURCE_CREATE_FN createFunction = ResourceManager->getCreateFunction (obj->name);
if(!createFunction)
{
AssertWarn( false, "ResourceObject::construct: NULL resource create function.");
Con::errorf("ResourceObject::construct: NULL resource create function for '%s'.", obj->name);
return NULL;
}
ResourceInstance *ret = createFunction (*stream);
if(ret)
ret->mSourceResource = obj;
closeStream (stream);
return ret;
}
//------------------------------------------------------------------------------
Stream * ResManager::openStream (const char *fileName)
{
ResourceObject *obj = find (fileName);
if (!obj)
return NULL;
return openStream (obj);
}
//------------------------------------------------------------------------------
Stream * ResManager::openStream (ResourceObject * obj)
{
// if filename is not known, exit now
if (!obj)
return NULL;
if (echoFileNames)
Con::printf ("FILE ACCESS: %s/%s", obj->path, obj->name);
// used for openStream stream access
FileStream *diskStream = NULL;
// if disk file
if (obj->flags & (ResourceObject::File))
{
diskStream = new FileStream;
diskStream->open (buildPath (obj->path, obj->name), FileStream::Read);
obj->fileSize = diskStream->getStreamSize ();
return diskStream;
}
// if zip file
if (obj->flags & ResourceObject::VolumeBlock)
{
diskStream = new FileStream;
diskStream->open (buildPath (obj->zipPath, obj->zipName),
FileStream::Read);
diskStream->setPosition (obj->fileOffset);
ZipLocalFileHeader zlfHeader;
if (zlfHeader.readFromStream (*diskStream) == false)
{
Con::errorf("ResourceManager::loadStream: '%s' Not in the zip! (%s/%s)",
obj->name, obj->zipPath, obj->zipName);
diskStream->close ();
return NULL;
}
if (zlfHeader.m_header.compressionMethod == ZipLocalFileHeader::Stored
|| obj->fileSize == 0)
{
// Just read straight from the stream...
ResizeFilterStream *strm = new ResizeFilterStream;
strm->attachStream (diskStream);
strm->setStreamOffset (diskStream->getPosition (), obj->fileSize);
return strm;
}
else
{
if (zlfHeader.m_header.compressionMethod ==
ZipLocalFileHeader::Deflated)
{
ZipSubRStream *zipStream = new ZipSubRStream;
zipStream->attachStream (diskStream);
zipStream->setUncompressedSize (obj->fileSize);
return zipStream;
}
else
{
AssertFatal (false,avar("ResourceManager::loadStream: '%s' Compressed inappropriately in the zip! (%s/%s)",
obj->name, obj->zipPath, obj->zipName));
diskStream->close ();
return NULL;
}
}
}
// unknown type
return NULL;
}
//------------------------------------------------------------------------------
void ResManager::closeStream (Stream * stream)
{
FilterStream *subStream = dynamic_cast < FilterStream * >(stream);
if (subStream)
{
stream = subStream->getStream ();
subStream->detachStream ();
delete subStream;
}
delete stream;
}
//------------------------------------------------------------------------------
ResourceObject *ResManager::find (const char *fileName)
{
if (!fileName)
return NULL;
StringTableEntry path, file;
getPaths (fileName, path, file);
ResourceObject *ret = dictionary.find (path, file);
if(!ret)
{
// Potentially dangerous behavior to have in shipping version but *very* useful
// in a production environment
#ifndef TORQUE_SHIPPING
// If we couldn't find the file in the resource list (generated
// by setting the modPaths) then try to load it directly
if (Platform::isFile(fileName))
{
ret = createResource (path, file);
dictionary.pushBehind (ret, ResourceObject::File);
ret->flags = ResourceObject::File;
ret->fileOffset = 0;
S32 fileSize = Platform::getFileSize(fileName);
ret->fileSize = fileSize;
ret->compressedFileSize = fileSize;
return ret;
}
#endif
fileIsMissing(fileName);
}
return ret;
}
//------------------------------------------------------------------------------
ResourceObject *ResManager::find (const char *fileName, U32 flags)
{
if (!fileName)
return NULL;
StringTableEntry path, file;
getPaths (fileName, path, file);
return dictionary.find (path, file, flags);
}
//------------------------------------------------------------------------------
// Add resource constructed outside the manager
bool ResManager::add (const char *name, ResourceInstance * addInstance,
bool extraLock)
{
StringTableEntry path, file;
getPaths (name, path, file);
ResourceObject *obj = dictionary.find (path, file);
if (obj && obj->mInstance)
// Resource already exists?
return false;
if (!obj)
obj = createResource (path, file);
dictionary.pushBehind (obj,
ResourceObject::File | ResourceObject::VolumeBlock);
obj->mInstance = addInstance;
addInstance->mSourceResource = obj;
obj->lockCount = extraLock ? 2 : 1;
unlock (obj);
return true;
}
//------------------------------------------------------------------------------
void ResManager::purge ()
{
bool found;
do
{
ResourceObject *obj = timeoutList.getNext ();
found = false;
while (obj)
{
ResourceObject *temp = obj;
obj = obj->next;
temp->unlink ();
temp->destruct ();
found = true;
if (temp->flags & ResourceObject::Added)
freeResource (temp);
}
}
while (found);
}
ConsoleFunction( purgeResources, void, 1, 1, "Purge resources from the resource manager.")
{
ResourceManager->purge();
}
//------------------------------------------------------------------------------
void ResManager::purge (ResourceObject * obj)
{
AssertFatal (obj->lockCount == 0,
"ResourceManager::purge: handle lock count is not ZERO.") obj->
unlink ();
obj->destruct ();
}
//------------------------------------------------------------------------------
// serialize sorts a list of files by .zip and position within the zip
// it allows an aggregate (material list, etc) to find the preferred
// loading order for a set of files.
//------------------------------------------------------------------------------
struct ResourceObjectIndex
{
ResourceObject *ro;
const char *fileName;
static S32 QSORT_CALLBACK compare (const void *s1, const void *s2)
{
const ResourceObjectIndex *r1 = (ResourceObjectIndex *) s1;
const ResourceObjectIndex *r2 = (ResourceObjectIndex *) s2;
if (r1->ro->path != r2->ro->path)
return r1->ro->path - r2->ro->path;
if (r1->ro->name != r2->ro->name)
return r1->ro->name - r2->ro->name;
return r1->ro->fileOffset - r2->ro->fileOffset;
}
};
//------------------------------------------------------------------------------
void ResManager::serialize (VectorPtr < const char *>&filenames)
{
Vector < ResourceObjectIndex > sortVector;
sortVector.reserve (filenames.size ());
U32 i;
for (i = 0; i < filenames.size (); i++)
{
ResourceObjectIndex roi;
roi.ro = find (filenames[i]);
roi.fileName = filenames[i];
sortVector.push_back (roi);
}
dQsort ((void *) &sortVector[0], sortVector.size (),
sizeof (ResourceObjectIndex), ResourceObjectIndex::compare);
for (i = 0; i < filenames.size (); i++)
filenames[i] = sortVector[i].fileName;
}
//------------------------------------------------------------------------------
ResourceObject * ResManager::findMatch (const char *expression, const char **fn,
ResourceObject * start)
{
if (!start)
start = resourceList.nextResource;
else
start = start->nextResource;
while (start)
{
const char *fname = buildPath (start->path, start->name);
if (FindMatch::isMatch (expression, fname, false))
{
*fn = fname;
return start;
}
start = start->nextResource;
}
return NULL;
}
S32 ResManager::findMatches (FindMatch * pFM)
{
static char buffer[16384];
S32 bufl = 0;
ResourceObject * walk;
for (walk = resourceList.nextResource; walk && !pFM->isFull (); walk = walk->nextResource)
{
const char * fpath =
buildPath (walk->path, walk->name);
if (bufl + dStrlen (fpath) >= 16380)
return pFM->numMatches ();
dStrcpy (buffer + bufl, fpath);
if (pFM->findMatch (buffer + bufl))
bufl += dStrlen (fpath) + 1;
}
return (pFM->numMatches ());
}
//------------------------------------------------------------------------------
bool ResManager::findFile (const char *name)
{
return (bool) find (name);
}
//------------------------------------------------------------------------------
ResourceObject * ResManager::createResource (StringTableEntry path, StringTableEntry file)
{
ResourceObject *newRO = dictionary.find (path, file);
if (newRO)
return newRO;
newRO = new ResourceObject;
newRO->path = path;
newRO->name = file;
newRO->lockCount = 0;
newRO->mInstance = NULL;
newRO->flags = ResourceObject::Added;
newRO->next = newRO->prev = NULL;
newRO->nextResource = resourceList.nextResource;
resourceList.nextResource = newRO;
newRO->prevResource = &resourceList;
if (newRO->nextResource)
newRO->nextResource->prevResource = newRO;
dictionary.insert (newRO, path, file);
newRO->fileSize = newRO->fileOffset = newRO->compressedFileSize = 0;
newRO->zipPath = NULL;
newRO->zipName = NULL;
newRO->crc = InvalidCRC;
return newRO;
}
//------------------------------------------------------------------------------
ResourceObject * ResManager::createZipResource (StringTableEntry path, StringTableEntry file,
StringTableEntry zipPath,
StringTableEntry zipName)
{
ResourceObject *newRO = dictionary.find (path, file, zipPath, zipName);
if (newRO)
return newRO;
newRO = new ResourceObject;
newRO->path = path;
newRO->name = file;
newRO->lockCount = 0;
newRO->mInstance = NULL;
newRO->flags = ResourceObject::Added;
newRO->next = newRO->prev = NULL;
newRO->nextResource = resourceList.nextResource;
resourceList.nextResource = newRO;
newRO->prevResource = &resourceList;
if (newRO->nextResource)
newRO->nextResource->prevResource = newRO;
dictionary.insert (newRO, path, file);
newRO->fileSize = newRO->fileOffset = newRO->compressedFileSize = 0;
newRO->zipPath = zipPath;
newRO->zipName = zipName;
newRO->crc = InvalidCRC;
return newRO;
}
//------------------------------------------------------------------------------
void ResManager::freeResource (ResourceObject * ro)
{
ro->destruct ();
ro->unlink ();
// if((ro->flags & ResourceObject::File) && ro->lockedData)
// delete[] ro->lockedData;
if (ro->prevResource)
ro->prevResource->nextResource = ro->nextResource;
if (ro->nextResource)
ro->nextResource->prevResource = ro->prevResource;
dictionary.remove (ro);
delete ro;
}
//------------------------------------------------------------------------------
bool ResManager::openFileForWrite (FileStream & stream, const char *fileName, U32 accessMode)
{
if (!isValidWriteFileName (fileName))
return false;
// tag it on to the first directory
char path[1024];
dStrcpy (path, fileName);
char *file = dStrrchr (path, '/');
if (!file)
return false; // don't allow storing files in root
*file++ = 0;
if (!Platform::createPath (fileName)) // create directory tree
return false;
if (!stream.open (fileName, (FileStream::AccessMode) accessMode))
return false;
// create a resource for the file.
ResourceObject *ro = createResource (StringTable->insert (path), StringTable->insert (file));
ro->flags = ResourceObject::File;
ro->fileOffset = 0;
ro->fileSize = 0;
ro->compressedFileSize = 0;
return true;
}