1218 lines
34 KiB
C++
Executable File
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;
|
|
}
|