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;
 | |
| }
 | 
