resourceManager.h 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2013 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #ifndef _RESMANAGER_H_
  23. #define _RESMANAGER_H_
  24. #ifndef _PLATFORM_H_
  25. #include "platform/platform.h"
  26. #endif
  27. #ifndef _VECTOR_H_
  28. #include "collection/vector.h"
  29. #endif
  30. #ifndef _STRINGTABLE_H_
  31. #include "string/stringTable.h"
  32. #endif
  33. #ifndef _FILESTREAM_H_
  34. #include "io/fileStream.h"
  35. #endif
  36. #ifndef _CRC_H_
  37. #include "algorithm/crc.h"
  38. #endif
  39. class Stream;
  40. class FileStream;
  41. class ZipSubRStream;
  42. class ResManager;
  43. class FindMatch;
  44. namespace Zip
  45. {
  46. class ZipArchive;
  47. class CentralDir;
  48. }
  49. extern ResManager *ResourceManager;
  50. //------------------------------------------------------------------------------
  51. class ResourceObject;
  52. /// The base class for all resources.
  53. ///
  54. /// This must be subclassed to implement a resource that can be managed.
  55. ///
  56. /// Creating a new resource type is very simple. First, you need a function
  57. /// which can create a new instance of the resource given a stream:
  58. ///
  59. /// @code
  60. /// ResourceInstance* constructBitmapPNG(Stream &stream)
  61. /// {
  62. /// GBitmap *bmp = new GBitmap;
  63. /// if(bmp->readPNG(stream))
  64. /// return bmp;
  65. /// else
  66. /// {
  67. /// delete bmp;
  68. /// return NULL;
  69. /// }
  70. /// }
  71. /// @endcode
  72. ///
  73. /// Then you need to register the extension of your resource type with the resource manager:
  74. ///
  75. /// @code
  76. /// ResourceManager->registerExtension(".png", constructBitmapPNG);
  77. /// @endcode
  78. ///
  79. /// And, of course, you need to provide a subclass of ResourceInstance:
  80. ///
  81. /// @code
  82. /// class GBitmap : ResourceInstance {
  83. /// ... whatever you need for your resource goes in here ...
  84. /// }
  85. /// @endcode
  86. ///
  87. /// ResourceInstance imposes very few requirements on you as the resource type
  88. /// implementor. All you "have" to provide is a destructor to deallocate whatever
  89. /// storage your resource might allocate. The ResourceManager will ensure that
  90. /// there is only one instance of the ResourceInstance for each file, and that it
  91. /// is only destroyed when no more references to it remain.
  92. ///
  93. /// @see TerrainFile, GBitmap, AudioBuffer for more examples.
  94. class ResourceInstance
  95. {
  96. private:
  97. public:
  98. /// Pointer to the ResourceObject that stores all our book-keeping data.
  99. ResourceObject *mSourceResource;
  100. ResourceInstance() { mSourceResource = NULL; }
  101. virtual ~ResourceInstance() {}
  102. };
  103. typedef ResourceInstance* (*RESOURCE_CREATE_FN)(Stream &stream);
  104. //------------------------------------------------------------------------------
  105. #define InvalidCRC 0xFFFFFFFF
  106. /// Wrapper around a ResourceInstance.
  107. ///
  108. /// This contains all the book-keeping data used by ResDictionary and ResManager.
  109. ///
  110. /// @see ResManager
  111. class ResourceObject
  112. {
  113. friend class ResDictionary;
  114. friend class ResManager;
  115. /// @name List Book-keeping
  116. /// @{
  117. ///
  118. ResourceObject *prev, *next;
  119. ResourceObject *nextEntry; ///< This is used by ResDictionary for its hash table.
  120. ResourceObject *nextResource;
  121. ResourceObject *prevResource;
  122. /// @}
  123. public:
  124. enum Flags
  125. {
  126. VolumeBlock = BIT(0),
  127. File = BIT(1),
  128. Added = BIT(2),
  129. };
  130. S32 flags; ///< Set from Flags.
  131. StringTableEntry path; ///< Resource path.
  132. StringTableEntry name; ///< Resource name.
  133. /// @name ZIP Archive
  134. /// If the resource is stored in a zip file, these members are populated.
  135. /// @{
  136. ///
  137. StringTableEntry zipPath; ///< Path of zip file.
  138. StringTableEntry zipName; ///< Name of zip file.
  139. S32 fileOffset; ///< Offset of data in zip file.
  140. S32 fileSize; ///< Size on disk of resource block.
  141. S32 compressedFileSize; ///< Actual size of resource data.
  142. /// @}
  143. ResourceInstance *mInstance; ///< Pointer to actual object instance. If the object is not loaded,
  144. /// this may be NULL or garbage.
  145. S32 lockCount; ///< Lock count; used to control load/unload of resource from memory.
  146. U32 crc; ///< CRC of resource.
  147. Zip::ZipArchive *mZipArchive; ///< The zip archive for reading from zips
  148. const Zip::CentralDir *mCentralDir; ///< The central directory for this file in the zip
  149. ResourceObject();
  150. ~ResourceObject() { unlink(); }
  151. void destruct();
  152. /// @name List Management
  153. /// @{
  154. ///
  155. ResourceObject* getNext() const { return next; }
  156. void unlink();
  157. void linkAfter(ResourceObject* res);
  158. /// @}
  159. /// Get some information about file times.
  160. ///
  161. /// @param createTime Pointer to a FileTime structure to fill with information about when this object was
  162. /// created.
  163. /// @param modifyTime Pointer to a FileTime structure to fill with information about when this object was
  164. /// modified.
  165. void getFileTimes(FileTime *createTime, FileTime *modifyTime);
  166. };
  167. inline void ResourceObject::unlink()
  168. {
  169. if (next)
  170. next->prev = prev;
  171. if (prev)
  172. prev->next = next;
  173. next = prev = 0;
  174. }
  175. inline void ResourceObject::linkAfter(ResourceObject* res)
  176. {
  177. unlink();
  178. prev = res;
  179. if ((next = res->next) != 0)
  180. next->prev = this;
  181. res->next = this;
  182. }
  183. //------------------------------------------------------------------------------
  184. /// Wrapper class around a ResourceInstance subclass.
  185. ///
  186. /// @code
  187. /// // Loading a resource...
  188. /// Resource<TerrainFile> terrRes;
  189. ///
  190. /// terrRes = ResourceManager->load(fileName);
  191. /// if(!bool(terrRes))
  192. /// Con::errorf(ConsoleLogEntry::General, "Terraformer::terrainFile - invalid terrain file '%s'.", fileName);
  193. /// @endcode
  194. ///
  195. /// When the Resource<> is destroyed, it frees the lock on the resource.
  196. ///
  197. /// @see ResManager
  198. template <class T> class Resource
  199. {
  200. private:
  201. ResourceObject *obj; ///< Actual resource object
  202. // ***WARNING***
  203. // Using a faster lock that bypasses the resource manger.
  204. // void _lock() { if (obj) obj->rm->lockResource( obj ); }
  205. void _lock(); ///< Increments the lock count on this object
  206. void _unlock(); ///< Decrements the lock count on this object
  207. public:
  208. /// If assigned a ResourceObject, it's assumed to already have
  209. /// been locked, lock count is incremented only for copies or
  210. /// assignment from another Resource.
  211. Resource() : obj(NULL) { ; }
  212. Resource(ResourceObject *p) : obj(p) { ; }
  213. Resource(const Resource &res) : obj(res.obj) { _lock(); }
  214. ~Resource() { unlock(); } ///< Decrements the lock count on this object, and if the lock count is 0 afterwards,
  215. ///< adds the object to the timeoutList for deletion on execution of purge().
  216. const char *getFilePath() const { return (obj ? obj->path : NULL); } ///< Returns the path of the file (without the actual name)
  217. const char *getFileName() const { return (obj ? obj->name : NULL); } ///< Returns the actual file name (without the path)
  218. Resource& operator= (ResourceObject *p) { _unlock(); obj = p; return *this; }
  219. Resource& operator= (const Resource &r) { _unlock(); obj = r.obj; _lock(); return *this; }
  220. U32 getCRC() { return (obj ? obj->crc : 0); }
  221. bool isNull() const { return ((obj == NULL) || (obj->mInstance == NULL)); }
  222. operator bool() const { return ((obj != NULL) && (obj->mInstance != NULL)); }
  223. T* operator->() { return (T*)obj->mInstance; }
  224. T& operator*() { return *((T*)obj->mInstance); }
  225. operator T*() const { return (obj) ? (T*)obj->mInstance : (T*)NULL; }
  226. const T* operator->() const { return (const T*)obj->mInstance; }
  227. const T& operator*() const { return *((const T*)obj->mInstance); }
  228. operator const T*() const { return (obj) ? (const T*)obj->mInstance : (const T*)NULL; }
  229. void unlock();
  230. void purge();
  231. };
  232. #define INVALID_ID ((U32)(~0))
  233. //----------------------------------------------------------------------------
  234. /// Resource Dictionary.
  235. ///
  236. /// Maps of names and object IDs to objects.
  237. ///
  238. /// Provides fast lookup for name->object, id->object and
  239. /// for fast removal of an object given a pointer to it.
  240. ///
  241. /// @see ResManager
  242. class ResDictionary
  243. {
  244. /// @name Hash Table
  245. /// @{
  246. enum { DefaultTableSize = 1029 };
  247. ResourceObject **hashTable;
  248. S32 entryCount;
  249. S32 hashTableSize;
  250. DataChunker memPool;
  251. S32 hash(StringTableEntry path, StringTableEntry name);
  252. S32 hash(ResourceObject *obj) { return hash(obj->path, obj->name); }
  253. /// @}
  254. public:
  255. ResDictionary();
  256. ~ResDictionary();
  257. /// Add a ResourceObject to the dictionary.
  258. void insert(ResourceObject *obj, StringTableEntry path, StringTableEntry file);
  259. /// @name Find
  260. /// These functions search the hash table for an individual resource. If the resource has
  261. /// already been loaded, it will find the resource and return its object. If not,
  262. /// it will return NULL.
  263. /// @{
  264. ResourceObject* find(StringTableEntry path, StringTableEntry file);
  265. ResourceObject* find(StringTableEntry path, StringTableEntry file, StringTableEntry filePath, StringTableEntry fileName);
  266. ResourceObject* find(StringTableEntry path, StringTableEntry file, U32 flags);
  267. /// @}
  268. /// Move a previously added resource object to be in the list after everything that matches the specified mask.
  269. void pushBehind(ResourceObject *obj, S32 mask);
  270. /// Remove a resource object from the dictionary.
  271. void remove(ResourceObject *obj);
  272. };
  273. //------------------------------------------------------------------------------
  274. /// A virtual file system for the storage and retrieval of ResourceObjects.
  275. ///
  276. /// Basic resource manager behavior:
  277. /// - Set the mod path.
  278. /// - ResManager scans directory tree under listed base directories
  279. /// - Any volume (.zip) file in the root directory of a mod is scanned
  280. /// for resources.
  281. /// - Any files currently in the resource manager become memory resources.
  282. /// - They can be "reattached" to the first file that matches the file name.
  283. ///
  284. /// All classes which wish to be handled by the resource manager need:
  285. /// -# To be derived from ResourceInstance.
  286. /// -# To register a creation function and file extension with the manager.
  287. ///
  288. /// @nosubgrouping
  289. class ResManager
  290. {
  291. private:
  292. /// Path to which we will write data.
  293. ///
  294. /// This is used when, for instance, we download files from a server.
  295. char writeablePath[1024];
  296. /// Primary path from which we load data.
  297. char primaryPath[1024];
  298. /// List of secondary paths to search.
  299. char* pathList;
  300. ResourceObject timeoutList;
  301. ResourceObject resourceList;
  302. ResDictionary dictionary;
  303. bool echoFileNames;
  304. bool usingVFS;
  305. bool isIgnoredSubdirectoryName(const char *name) const;
  306. /// Scan a zip file for resources.
  307. bool scanZip(ResourceObject *zipObject);
  308. /// Create a ResourceObject from the given file.
  309. ResourceObject* createResource(StringTableEntry path, StringTableEntry file);
  310. /// Create a ResourceObject from the given file in a zip file.
  311. ResourceObject* createZipResource(StringTableEntry path, StringTableEntry file, StringTableEntry zipPath, StringTableEntry zipFle);
  312. void searchPath(const char *pathStart, bool noDups = false, bool ignoreZips = false);
  313. bool setModZip(const char* path);
  314. struct RegisteredExtension
  315. {
  316. StringTableEntry mExtension;
  317. RESOURCE_CREATE_FN mCreateFn;
  318. RegisteredExtension *next;
  319. };
  320. Vector<char *> mMissingFileList; ///< List of missing files.
  321. bool mLoggingMissingFiles; ///< Are there any missing files?
  322. void fileIsMissing(const char *fileName); ///< Called when a file is missing.
  323. RegisteredExtension *registeredList;
  324. static const char *smExcludedDirectories;
  325. ResManager();
  326. public:
  327. RESOURCE_CREATE_FN getCreateFunction( const char *name );
  328. ~ResManager();
  329. /// @name Global Control
  330. /// These are called to initialize/destroy the resource manager at runtime.
  331. /// @{
  332. static void create();
  333. static void destroy();
  334. /// Load the excluded directories from the resource manager pref and
  335. /// stuff it into the platform layer.
  336. static void initExcludedDirectories();
  337. ///@}
  338. void setFileNameEcho(bool on); ///< Sets whether or not to echo filenames that have been accessed by means of openStream
  339. void setModPaths(U32 numPaths, const char **dirs); ///< Sets the path for the current game mod
  340. const char* getModPaths(); ///< Gets the path for the current game mod
  341. void addPath(const char *path, bool ignoreZips=false);///< Add a path
  342. void removePath(const char *path); ///< Remove a path. Only removes resources that are not loaded.
  343. void setMissingFileLogging(bool log); ///< Should we log missing files?
  344. bool getMissingFileList(Vector<char *> &list); ///< Gets which files are missing
  345. void clearMissingFileList(); ///< Clears the missing file list
  346. /// Tells the resource manager what to do with a resource that it loads
  347. void registerExtension(const char *extension, RESOURCE_CREATE_FN create_fn);
  348. S32 getSize(const char* filename); ///< Gets the size of the file
  349. const char* getFullPath(const char * filename, char * path, U32 pathLen); ///< Gets the full path of the file
  350. const char* getModPathOf(const char* fileName); ///< Gets the path of the file local to the mod
  351. const char* getPathOf(const char * filename); ///< Gets the path of the file from the base directory
  352. const char* getBasePath(); ///< Gets the base path
  353. ResourceObject* load(const char * fileName, bool computeCRC = false); ///< loads an instance of an object
  354. Stream* openStream(const char * fileName); ///< Opens a stream for an object
  355. Stream* openStream(ResourceObject *object); ///< Opens a stream for an object
  356. void closeStream(Stream *stream); ///< Closes the stream
  357. /// Decrements the lock count of an object. If the lock count is zero post-decrement,
  358. /// the object is added to the timeoutList for deletion upon call of flush.
  359. void unlock( ResourceObject* );
  360. /// Add a new resource instance
  361. bool add(const char* name, ResourceInstance *addInstance, bool extraLock = false);
  362. /// Searches the hash list for the filename and returns it's object if found, otherwise NULL
  363. ResourceObject* find(const char * fileName);
  364. /// Loads a new instance of an object by means of a filename
  365. ResourceInstance* loadInstance(const char *fileName, bool computeCRC = false);
  366. /// Loads a new instance of an object by means of a resource object
  367. ResourceInstance* loadInstance(ResourceObject *object, bool computeCRC = false);
  368. /// Searches the hash list for the filename and returns it's object if found, otherwise NULL
  369. ResourceObject* find(const char * fileName, U32 flags);
  370. /// Finds a resource object with given expression
  371. ResourceObject* findMatch(const char *expression, const char **fn, ResourceObject *start = NULL);
  372. /// Finds a resource object with given expressions, seperated by " "
  373. ResourceObject* findMatchMultiExprs(const char *multiExpression, const char **fn, ResourceObject *start = NULL);
  374. void purge(); ///< Goes through the timeoutList and deletes it all. BURN!!!
  375. void purge( ResourceObject *obj ); ///< Deletes one resource object.
  376. void freeResource(ResourceObject *resObject); ///< Frees a resource!
  377. void serialize(VectorPtr<const char *> &filenames);///< Sorts the resource objects
  378. S32 findMatches( FindMatch *pFM ); ///< Finds multiple matches to an expression.
  379. bool findFile( const char *name ); ///< Checks to see if a file exists.
  380. bool addVFSRoot(Zip::ZipArchive *vfs);
  381. bool isUsingVFS() { return usingVFS; }
  382. /// Computes the CRC of a file.
  383. ///
  384. /// By passing a different crcInitialVal, you can take the CRC of multiple files.
  385. bool getCrc(const char * fileName, U32 & crcVal, const U32 crcInitialVal = INITIAL_CRC_VALUE );
  386. void setWriteablePath(const char *path); ///< Sets the writable path for a file to the one given.
  387. bool isValidWriteFileName(const char *fn); ///< Checks to see if the given path is valid for writing.
  388. /// Opens a file for writing!
  389. bool openFileForWrite(FileStream &fs, const char *fileName, U32 accessMode = File::Write);
  390. #ifdef TORQUE_DEBUG
  391. void dumpResources(const bool onlyLoaded = true); ///< Dumps all loaded resources to the console.
  392. #endif
  393. };
  394. template<class T> inline void Resource<T>::unlock()
  395. {
  396. if (obj) {
  397. ResourceManager->unlock( obj );
  398. obj=NULL;
  399. }
  400. }
  401. template<class T> inline void Resource<T>::purge()
  402. {
  403. if (obj) {
  404. ResourceManager->unlock( obj );
  405. if (obj->lockCount == 0)
  406. ResourceManager->purge(obj);
  407. obj = NULL;
  408. }
  409. }
  410. template <class T> inline void Resource<T>::_lock()
  411. {
  412. if (obj)
  413. obj->lockCount++;
  414. }
  415. template <class T> inline void Resource<T>::_unlock()
  416. {
  417. if (obj)
  418. ResourceManager->unlock( obj );
  419. }
  420. #endif //_RESMANAGER_H_