Initial commit
This commit is contained in:
486
engine/core/resManager.h
Executable file
486
engine/core/resManager.h
Executable file
@@ -0,0 +1,486 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _RESMANAGER_H_
|
||||
#define _RESMANAGER_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/tVector.h"
|
||||
#endif
|
||||
#ifndef _STRINGTABLE_H_
|
||||
#include "core/stringTable.h"
|
||||
#endif
|
||||
|
||||
#ifndef _FILESTREAM_H_
|
||||
#include "core/fileStream.h"
|
||||
#endif
|
||||
#ifndef _ZIPSUBSTREAM_H_
|
||||
#include "core/zipSubStream.h"
|
||||
#endif
|
||||
#ifndef _ZIPAGGREGATE_H_
|
||||
#include "core/zipAggregate.h"
|
||||
#endif
|
||||
#ifndef _ZIPHEADERS_H_
|
||||
#include "core/zipHeaders.h"
|
||||
#endif
|
||||
#ifndef _CRC_H_
|
||||
#include "core/crc.h"
|
||||
#endif
|
||||
|
||||
class Stream;
|
||||
class FileStream;
|
||||
class ZipSubRStream;
|
||||
class ResManager;
|
||||
class FindMatch;
|
||||
|
||||
extern ResManager *ResourceManager;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
class ResourceObject;
|
||||
|
||||
/// The base class for all resources.
|
||||
///
|
||||
/// This must be subclassed to implement a resource that can be managed.
|
||||
///
|
||||
/// Creating a new resource type is very simple. First, you need a function
|
||||
/// which can create a new instance of the resource given a stream:
|
||||
///
|
||||
/// @code
|
||||
/// ResourceInstance* constructBitmapBMP(Stream &stream)
|
||||
/// {
|
||||
/// GBitmap *bmp = new GBitmap;
|
||||
/// if(bmp->readMSBmp(stream))
|
||||
/// return bmp;
|
||||
/// else
|
||||
/// {
|
||||
/// delete bmp;
|
||||
/// return NULL;
|
||||
/// }
|
||||
/// }
|
||||
/// @endcode
|
||||
///
|
||||
/// Then you need to register the extension of your resource type with the resource manager:
|
||||
///
|
||||
/// @code
|
||||
/// ResourceManager->registerExtension(".bmp", constructBitmapBMP);
|
||||
/// @endcode
|
||||
///
|
||||
/// And, of course, you need to provide a subclass of ResourceInstance:
|
||||
///
|
||||
/// @code
|
||||
/// class GBitmap : ResourceInstance {
|
||||
/// ... whatever you need for your resource goes in here ...
|
||||
/// }
|
||||
/// @endcode
|
||||
///
|
||||
/// ResourceInstance imposes very few requirements on you as the resource type
|
||||
/// implementor. All you "have" to provide is a destructor to deallocate whatever
|
||||
/// storage your resource might allocate. The ResourceManager will ensure that
|
||||
/// there is only one instance of the ResourceInstance for each file, and that it
|
||||
/// is only destroyed when no more references to it remain.
|
||||
///
|
||||
/// @see TerrainFile, GBitmap, AudioBuffer for more examples.
|
||||
class ResourceInstance
|
||||
{
|
||||
private:
|
||||
public:
|
||||
/// Pointer to the ResourceObject that stores all our book-keeping data.
|
||||
ResourceObject *mSourceResource;
|
||||
|
||||
ResourceInstance() { mSourceResource = NULL; }
|
||||
virtual ~ResourceInstance() {}
|
||||
};
|
||||
|
||||
|
||||
typedef ResourceInstance* (*RESOURCE_CREATE_FN)(Stream &stream);
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#define InvalidCRC 0xFFFFFFFF
|
||||
|
||||
/// Wrapper around a ResourceInstance.
|
||||
///
|
||||
/// This contains all the book-keeping data used by ResDictionary and ResManager.
|
||||
///
|
||||
/// @see ResManager
|
||||
class ResourceObject
|
||||
{
|
||||
friend class ResDictionary;
|
||||
friend class ResManager;
|
||||
|
||||
/// @name List Book-keeping
|
||||
/// @{
|
||||
|
||||
///
|
||||
ResourceObject *prev, *next;
|
||||
|
||||
ResourceObject *nextEntry; ///< This is used by ResDictionary for its hash table.
|
||||
|
||||
ResourceObject *nextResource;
|
||||
ResourceObject *prevResource;
|
||||
/// @}
|
||||
|
||||
public:
|
||||
enum Flags
|
||||
{
|
||||
VolumeBlock = BIT(0),
|
||||
File = BIT(1),
|
||||
Added = BIT(2),
|
||||
};
|
||||
S32 flags; ///< Set from Flags.
|
||||
|
||||
StringTableEntry path; ///< Resource path.
|
||||
StringTableEntry name; ///< Resource name.
|
||||
|
||||
/// @name ZIP Archive
|
||||
/// If the resource is stored in a zip file, these members are populated.
|
||||
/// @{
|
||||
|
||||
///
|
||||
StringTableEntry zipPath; ///< Path of zip file.
|
||||
StringTableEntry zipName; ///< Name of zip file.
|
||||
|
||||
S32 fileOffset; ///< Offset of data in zip file.
|
||||
S32 fileSize; ///< Size on disk of resource block.
|
||||
S32 compressedFileSize; ///< Actual size of resource data.
|
||||
/// @}
|
||||
|
||||
ResourceInstance *mInstance; ///< Pointer to actual object instance. If the object is not loaded,
|
||||
/// this may be NULL or garbage.
|
||||
S32 lockCount; ///< Lock count; used to control load/unload of resource from memory.
|
||||
U32 crc; ///< CRC of resource.
|
||||
|
||||
ResourceObject();
|
||||
~ResourceObject() { unlink(); }
|
||||
|
||||
void destruct();
|
||||
|
||||
/// @name List Management
|
||||
/// @{
|
||||
|
||||
///
|
||||
ResourceObject* getNext() const { return next; }
|
||||
void unlink();
|
||||
void linkAfter(ResourceObject* res);
|
||||
/// @}
|
||||
|
||||
/// Get some information about file times.
|
||||
///
|
||||
/// @param createTime Pointer to a FileTime structure to fill with information about when this object was
|
||||
/// created.
|
||||
/// @param modifyTime Pointer to a FileTime structure to fill with information about when this object was
|
||||
/// modified.
|
||||
void getFileTimes(FileTime *createTime, FileTime *modifyTime);
|
||||
};
|
||||
|
||||
|
||||
inline void ResourceObject::unlink()
|
||||
{
|
||||
if (next)
|
||||
next->prev = prev;
|
||||
if (prev)
|
||||
prev->next = next;
|
||||
next = prev = 0;
|
||||
}
|
||||
|
||||
inline void ResourceObject::linkAfter(ResourceObject* res)
|
||||
{
|
||||
unlink();
|
||||
prev = res;
|
||||
if ((next = res->next) != 0)
|
||||
next->prev = this;
|
||||
res->next = this;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// Wrapper class around a ResourceInstance subclass.
|
||||
///
|
||||
/// @code
|
||||
/// // Loading a resource...
|
||||
/// Resource<TerrainFile> terrRes;
|
||||
///
|
||||
/// terrRes = ResourceManager->load(fileName);
|
||||
/// if(!bool(terrRes))
|
||||
/// Con::errorf(ConsoleLogEntry::General, "Terraformer::terrainFile - invalid terrain file '%s'.", fileName);
|
||||
/// @endcode
|
||||
///
|
||||
/// When the Resource<> is destroyed, it frees the lock on the resource.
|
||||
///
|
||||
/// @see ResManager
|
||||
template <class T> class Resource
|
||||
{
|
||||
private:
|
||||
ResourceObject *obj; ///< Actual resource object
|
||||
|
||||
// ***WARNING***
|
||||
// Using a faster lock that bypasses the resource manger.
|
||||
// void _lock() { if (obj) obj->rm->lockResource( obj ); }
|
||||
void _lock(); ///< Increments the lock count on this object
|
||||
void _unlock(); ///< Decrements the lock count on this object
|
||||
|
||||
public:
|
||||
/// If assigned a ResourceObject, it's assumed to already have
|
||||
/// been locked, lock count is incremented only for copies or
|
||||
/// assignment from another Resource.
|
||||
Resource() : obj(NULL) { ; }
|
||||
Resource(ResourceObject *p) : obj(p) { ; }
|
||||
Resource(const Resource &res) : obj(res.obj) { _lock(); }
|
||||
~Resource() { unlock(); } ///< Decrements the lock count on this object, and if the lock count is 0 afterwards,
|
||||
///< adds the object to the timeoutList for deletion on execution of purge().
|
||||
|
||||
const char *getFilePath() const { return (obj ? obj->path : NULL); } ///< Returns the path of the file (without the actual name)
|
||||
const char *getFileName() const { return (obj ? obj->name : NULL); } ///< Returns the actual file name (without the path)
|
||||
|
||||
Resource& operator= (ResourceObject *p) { _unlock(); obj = p; return *this; }
|
||||
Resource& operator= (const Resource &r) { _unlock(); obj = r.obj; _lock(); return *this; }
|
||||
|
||||
U32 getCRC() { return (obj ? obj->crc : 0); }
|
||||
bool isNull() const { return ((obj == NULL) || (obj->mInstance == NULL)); }
|
||||
operator bool() const { return ((obj != NULL) && (obj->mInstance != NULL)); }
|
||||
T* operator->() { return (T*)obj->mInstance; }
|
||||
T& operator*() { return *((T*)obj->mInstance); }
|
||||
operator T*() const { return (obj) ? (T*)obj->mInstance : (T*)NULL; }
|
||||
const T* operator->() const { return (const T*)obj->mInstance; }
|
||||
const T& operator*() const { return *((const T*)obj->mInstance); }
|
||||
operator const T*() const { return (obj) ? (const T*)obj->mInstance : (const T*)NULL; }
|
||||
void unlock();
|
||||
void purge();
|
||||
};
|
||||
|
||||
#define INVALID_ID ((U32)(~0))
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// Resource Dictionary.
|
||||
///
|
||||
/// Maps of names and object IDs to objects.
|
||||
///
|
||||
/// Provides fast lookup for name->object, id->object and
|
||||
/// for fast removal of an object given a pointer to it.
|
||||
///
|
||||
/// @see ResManager
|
||||
class ResDictionary
|
||||
{
|
||||
/// @name Hash Table
|
||||
/// @{
|
||||
|
||||
enum { DefaultTableSize = 1029 };
|
||||
|
||||
ResourceObject **hashTable;
|
||||
S32 entryCount;
|
||||
S32 hashTableSize;
|
||||
DataChunker memPool;
|
||||
S32 hash(StringTableEntry path, StringTableEntry name);
|
||||
S32 hash(ResourceObject *obj) { return hash(obj->path, obj->name); }
|
||||
/// @}
|
||||
|
||||
public:
|
||||
ResDictionary();
|
||||
~ResDictionary();
|
||||
|
||||
/// Add a ResourceObject to the dictionary.
|
||||
void insert(ResourceObject *obj, StringTableEntry path, StringTableEntry file);
|
||||
|
||||
/// @name Find
|
||||
/// These functions search the hash table for an individual resource. If the resource has
|
||||
/// already been loaded, it will find the resource and return its object. If not,
|
||||
/// it will return NULL.
|
||||
/// @{
|
||||
|
||||
ResourceObject* find(StringTableEntry path, StringTableEntry file);
|
||||
ResourceObject* find(StringTableEntry path, StringTableEntry file, StringTableEntry filePath, StringTableEntry fileName);
|
||||
ResourceObject* find(StringTableEntry path, StringTableEntry file, U32 flags);
|
||||
/// @}
|
||||
|
||||
/// Move a previously added resource object to be in the list after everything that matches the specified mask.
|
||||
void pushBehind(ResourceObject *obj, S32 mask);
|
||||
|
||||
/// Remove a resource object from the dictionary.
|
||||
void remove(ResourceObject *obj);
|
||||
};
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// A virtual file system for the storage and retrieval of ResourceObjects.
|
||||
///
|
||||
/// Basic resource manager behavior:
|
||||
/// - Set the mod path.
|
||||
/// - ResManager scans directory tree under listed base directories
|
||||
/// - Any volume (.zip) file in the root directory of a mod is scanned
|
||||
/// for resources.
|
||||
/// - Any files currently in the resource manager become memory resources.
|
||||
/// - They can be "reattached" to the first file that matches the file name.
|
||||
///
|
||||
/// All classes which wish to be handled by the resource manager need:
|
||||
/// -# To be derived from ResourceInstance.
|
||||
/// -# To register a creation function and file extension with the manager.
|
||||
///
|
||||
/// @nosubgrouping
|
||||
class ResManager
|
||||
{
|
||||
private:
|
||||
/// Path to which we will write data.
|
||||
///
|
||||
/// This is used when, for instance, we download files from a server.
|
||||
char writeablePath[1024];
|
||||
|
||||
/// Primary path from which we load data.
|
||||
char primaryPath[1024];
|
||||
|
||||
/// List of secondary paths to search.
|
||||
char* pathList;
|
||||
|
||||
ResourceObject timeoutList;
|
||||
ResourceObject resourceList;
|
||||
|
||||
ResDictionary dictionary;
|
||||
|
||||
bool echoFileNames;
|
||||
|
||||
bool isIgnoredSubdirectoryName(const char *name) const;
|
||||
|
||||
/// Scan a zip file for resources.
|
||||
bool scanZip(ResourceObject *zipObject);
|
||||
|
||||
/// Create a ResourceObject from the given file.
|
||||
ResourceObject* createResource(StringTableEntry path, StringTableEntry file);
|
||||
|
||||
/// Create a ResourceObject from the given file in a zip file.
|
||||
ResourceObject* createZipResource(StringTableEntry path, StringTableEntry file, StringTableEntry zipPath, StringTableEntry zipFle);
|
||||
|
||||
void searchPath(const char *pathStart);
|
||||
bool setModZip(const char* path);
|
||||
|
||||
struct RegisteredExtension
|
||||
{
|
||||
StringTableEntry mExtension;
|
||||
RESOURCE_CREATE_FN mCreateFn;
|
||||
RegisteredExtension *next;
|
||||
};
|
||||
|
||||
Vector<char *> mMissingFileList; ///< List of missing files.
|
||||
bool mLoggingMissingFiles; ///< Are there any missing files?
|
||||
void fileIsMissing(const char *fileName); ///< Called when a file is missing.
|
||||
|
||||
RegisteredExtension *registeredList;
|
||||
|
||||
static char *smExcludedDirectories;
|
||||
ResManager();
|
||||
public:
|
||||
RESOURCE_CREATE_FN getCreateFunction( const char *name );
|
||||
|
||||
~ResManager();
|
||||
/// @name Global Control
|
||||
/// These are called to initialize/destroy the resource manager at runtime.
|
||||
/// @{
|
||||
|
||||
static void create();
|
||||
static void destroy();
|
||||
|
||||
/// Load the excluded directories from the resource manager pref and
|
||||
/// stuff it into the platform layer.
|
||||
static void initExcludedDirectories();
|
||||
|
||||
///@}
|
||||
|
||||
void setFileNameEcho(bool on); ///< Sets whether or not to echo filenames that have been accessed by means of openStream
|
||||
void setModPaths(U32 numPaths, const char **dirs); ///< Sets the path for the current game mod
|
||||
const char* getModPaths(); ///< Gets the path for the current game mod
|
||||
|
||||
void setMissingFileLogging(bool log); ///< Should we log missing files?
|
||||
bool getMissingFileList(Vector<char *> &list); ///< Gets which files are missing
|
||||
void clearMissingFileList(); ///< Clears the missing file list
|
||||
|
||||
/// Tells the resource manager what to do with a resource that it loads
|
||||
void registerExtension(const char *extension, RESOURCE_CREATE_FN create_fn);
|
||||
|
||||
S32 getSize(const char* filename); ///< Gets the size of the file
|
||||
const char* getFullPath(const char * filename, char * path, U32 pathLen); ///< Gets the full path of the file
|
||||
const char* getModPathOf(const char* fileName); ///< Gets the path of the file local to the mod
|
||||
const char* getPathOf(const char * filename); ///< Gets the path of the file from the base directory
|
||||
const char* getBasePath(); ///< Gets the base path
|
||||
|
||||
ResourceObject* load(const char * fileName, bool computeCRC = false); ///< loads an instance of an object
|
||||
Stream* openStream(const char * fileName); ///< Opens a stream for an object
|
||||
Stream* openStream(ResourceObject *object); ///< Opens a stream for an object
|
||||
void closeStream(Stream *stream); ///< Closes the stream
|
||||
|
||||
/// Decrements the lock count of an object. If the lock count is zero post-decrement,
|
||||
/// the object is added to the timeoutList for deletion upon call of flush.
|
||||
void unlock( ResourceObject* );
|
||||
|
||||
/// Add a new resource instance
|
||||
bool add(const char* name, ResourceInstance *addInstance, bool extraLock = false);
|
||||
|
||||
/// Searches the hash list for the filename and returns it's object if found, otherwise NULL
|
||||
ResourceObject* find(const char * fileName);
|
||||
/// Loads a new instance of an object by means of a filename
|
||||
ResourceInstance* loadInstance(const char *fileName, bool computeCRC = false);
|
||||
/// Loads a new instance of an object by means of a resource object
|
||||
ResourceInstance* loadInstance(ResourceObject *object, bool computeCRC = false);
|
||||
|
||||
/// Searches the hash list for the filename and returns it's object if found, otherwise NULL
|
||||
ResourceObject* find(const char * fileName, U32 flags);
|
||||
|
||||
/// Finds a resource object with given expression
|
||||
ResourceObject* findMatch(const char *expression, const char **fn, ResourceObject *start = NULL);
|
||||
|
||||
void purge(); ///< Goes through the timeoutList and deletes it all. BURN!!!
|
||||
void purge( ResourceObject *obj ); ///< Deletes one resource object.
|
||||
void freeResource(ResourceObject *resObject); ///< Frees a resource!
|
||||
void serialize(VectorPtr<const char *> &filenames);///< Sorts the resource objects
|
||||
|
||||
S32 findMatches( FindMatch *pFM ); ///< Finds multiple matches to an expression.
|
||||
bool findFile( const char *name ); ///< Checks to see if a file exists.
|
||||
|
||||
/// Computes the CRC of a file.
|
||||
///
|
||||
/// By passing a different crcInitialVal, you can take the CRC of multiple files.
|
||||
bool getCrc(const char * fileName, U32 & crcVal, const U32 crcInitialVal = INITIAL_CRC_VALUE );
|
||||
|
||||
void setWriteablePath(const char *path); ///< Sets the writable path for a file to the one given.
|
||||
bool isValidWriteFileName(const char *fn); ///< Checks to see if the given path is valid for writing.
|
||||
|
||||
/// Opens a file for writing!
|
||||
bool openFileForWrite(FileStream &fs, const char *fileName, U32 accessMode = 1);
|
||||
|
||||
#ifdef TORQUE_DEBUG
|
||||
void dumpLoadedResources(); ///< Dumps all loaded resources to the console.
|
||||
#endif
|
||||
};
|
||||
|
||||
template<class T> inline void Resource<T>::unlock()
|
||||
{
|
||||
if (obj) {
|
||||
ResourceManager->unlock( obj );
|
||||
obj=NULL;
|
||||
}
|
||||
}
|
||||
|
||||
template<class T> inline void Resource<T>::purge()
|
||||
{
|
||||
if (obj) {
|
||||
ResourceManager->unlock( obj );
|
||||
if (obj->lockCount == 0)
|
||||
ResourceManager->purge(obj);
|
||||
obj = NULL;
|
||||
}
|
||||
}
|
||||
template <class T> inline void Resource<T>::_lock()
|
||||
{
|
||||
if (obj)
|
||||
obj->lockCount++;
|
||||
}
|
||||
|
||||
template <class T> inline void Resource<T>::_unlock()
|
||||
{
|
||||
if (obj)
|
||||
ResourceManager->unlock( obj );
|
||||
}
|
||||
|
||||
#endif //_RESMANAGER_H_
|
||||
Reference in New Issue
Block a user