//----------------------------------------------------------------------------- // 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 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 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 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 &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); /// Finds a resource object with given expressions, seperated by " " ResourceObject* findMatchMultiExprs(const char *multiExpression, 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 &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 inline void Resource::unlock() { if (obj) { ResourceManager->unlock( obj ); obj=NULL; } } template inline void Resource::purge() { if (obj) { ResourceManager->unlock( obj ); if (obj->lockCount == 0) ResourceManager->purge(obj); obj = NULL; } } template inline void Resource::_lock() { if (obj) obj->lockCount++; } template inline void Resource::_unlock() { if (obj) ResourceManager->unlock( obj ); } #endif //_RESMANAGER_H_