1066 lines
27 KiB
C++
1066 lines
27 KiB
C++
//-----------------------------------------------------------------------------
|
|
// Torque Game Engine
|
|
//
|
|
// Copyright (c) 2001 GarageGames.Com
|
|
// Portions Copyright (c) 2001 by Sierra Online, 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 "sim/frameAllocator.h"
|
|
|
|
#include "core/resManager.h"
|
|
#include "core/findMatch.h"
|
|
|
|
#include "console/console.h"
|
|
|
|
ResManager *ResourceManager = NULL;
|
|
|
|
//------------------------------------------------------------------------------
|
|
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
|
|
if (mInstance)
|
|
{
|
|
delete mInstance;
|
|
mInstance = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
//------------------------------------------------------------------------------
|
|
|
|
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;
|
|
}
|
|
|
|
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 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;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//------------------------------------------------------------------------------
|
|
void
|
|
ResManager::create ()
|
|
{
|
|
AssertFatal (ResourceManager == NULL,
|
|
"ResourceManager::create: manager already exists.");
|
|
ResourceManager = new ResManager;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
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"))
|
|
{
|
|
// scanZip(ro);
|
|
// TODO: repair zip scanning
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
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;
|
|
|
|
// determine if the mod paths are valid
|
|
for (U32 i = 0; i < numPaths; i++)
|
|
{
|
|
if (!Platform::
|
|
isSubDirectory (Platform::getWorkingDirectory (), paths[i]))
|
|
{
|
|
Con::errorf ("setModPaths: invalid mod path directory name: '%s'",
|
|
paths[i]);
|
|
continue;
|
|
}
|
|
|
|
pathLen += (dStrlen (paths[i]) + 1);
|
|
searchPath (paths[i]);
|
|
}
|
|
if(!pathLen)
|
|
return;
|
|
|
|
// build the interal path list string
|
|
pathList = (char *) dRealloc (pathList, pathLen);
|
|
dStrcpy (pathList, paths[0]);
|
|
U32 strlen;
|
|
for (U32 i = 1; i < numPaths; i++)
|
|
{
|
|
strlen = dStrlen (pathList);
|
|
dSprintf (pathList + strlen, pathLen - strlen, ";%s", paths[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;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
const char *
|
|
ResManager::getModPaths ()
|
|
{
|
|
return ((const char *) pathList);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
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 noone 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);
|
|
AssertFatal (createFunction,
|
|
"ResourceObject::construct: NULL resource create function.");
|
|
ResourceInstance *ret = createFunction (*stream);
|
|
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);
|
|
return dictionary.find (path, file);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
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;
|
|
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);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
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 preffered
|
|
// 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;
|
|
}
|
|
|
|
///Added for DTS Gui files
|
|
ResourceObject *
|
|
ResManager::createResource (const char * fileName)
|
|
{
|
|
|
|
StringTableEntry path, file;
|
|
getPaths (fileName, path, 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;
|
|
}
|