resourceManager.cc 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372
  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. #include "platform/platform.h"
  23. #include "console/console.h"
  24. #include "collection/vector.h"
  25. #include "io/stream.h"
  26. #include "io/fileStream.h"
  27. #include "io/resizeStream.h"
  28. #include "memory/frameAllocator.h"
  29. #include "io/zip/zipArchive.h"
  30. #include "io/resource/resourceManager.h"
  31. #include "string/findMatch.h"
  32. #include "console/console.h"
  33. #include "console/consoleTypes.h"
  34. #include "memory/safeDelete.h"
  35. ResManager *ResourceManager = NULL;
  36. const char *ResManager::smExcludedDirectories = ".svn;CVS";
  37. //------------------------------------------------------------------------------
  38. ResourceObject::ResourceObject ()
  39. {
  40. next = NULL;
  41. prev = NULL;
  42. lockCount = 0;
  43. mInstance = NULL;
  44. mZipArchive = NULL;
  45. mCentralDir = NULL;
  46. }
  47. void ResourceObject::destruct ()
  48. {
  49. // If the resource was not loaded because of an error, the resource
  50. // pointer will be NULL
  51. SAFE_DELETE(mInstance);
  52. // Free if it is ResourceObject::File and is NOT ResourceObject::VolumeBlock
  53. if((flags & ResourceObject::File) && !(flags & ResourceObject::VolumeBlock) )
  54. {
  55. // [tom, 10/26/2006] We don't want to delete if it's a volume block since
  56. // the archive will be freed when the zip file resource object is freed.
  57. SAFE_DELETE(mZipArchive);
  58. }
  59. }
  60. //------------------------------------------------------------------------------
  61. //------------------------------------------------------------------------------
  62. ResManager::ResManager ()
  63. {
  64. echoFileNames = false;
  65. primaryPath[0] = 0;
  66. writeablePath[0] = 0;
  67. pathList = NULL;
  68. resourceList.nextResource = NULL;
  69. resourceList.next = NULL;
  70. resourceList.prev = NULL;
  71. timeoutList.nextResource = NULL;
  72. timeoutList.next = NULL;
  73. timeoutList.prev = NULL;
  74. registeredList = NULL;
  75. mLoggingMissingFiles = false;
  76. usingVFS = false;
  77. }
  78. void ResManager::fileIsMissing(const char *fileName)
  79. {
  80. if(mLoggingMissingFiles)
  81. {
  82. char *name = dStrdup(fileName);
  83. mMissingFileList.push_back(name);
  84. }
  85. }
  86. void ResManager::setMissingFileLogging(bool logging)
  87. {
  88. mLoggingMissingFiles = logging;
  89. if(!mLoggingMissingFiles)
  90. clearMissingFileList();
  91. }
  92. void ResManager::clearMissingFileList()
  93. {
  94. while(mMissingFileList.size())
  95. {
  96. dFree(mMissingFileList[0]);
  97. mMissingFileList.pop_front();
  98. }
  99. mMissingFileList.clear();
  100. }
  101. bool ResManager::getMissingFileList(Vector<char *> &list)
  102. {
  103. if(!mMissingFileList.size())
  104. return false;
  105. for(U32 i = 0; i < (U32)mMissingFileList.size();i ++)
  106. {
  107. for(U32 j = 0; j < (U32)list.size(); j++)
  108. {
  109. if(!dStrcmp(list[j], mMissingFileList[i]))
  110. {
  111. dFree(mMissingFileList[i]);
  112. mMissingFileList[i] = NULL;
  113. break;
  114. }
  115. }
  116. if(mMissingFileList[i])
  117. list.push_back(mMissingFileList[i]);
  118. }
  119. mMissingFileList.clear();
  120. return true;
  121. }
  122. void ResourceObject::getFileTimes (FileTime * createTime, FileTime * modifyTime)
  123. {
  124. if( !path || !name )
  125. {
  126. createTime = modifyTime = NULL;
  127. return;
  128. }
  129. char buffer[1024];
  130. Platform::makeFullPathName(path, buffer, sizeof(buffer));
  131. U32 len = dStrlen(buffer);
  132. dSprintf (buffer + len, sizeof (buffer) - len, "/%s", name);
  133. Platform::getFileTimes (buffer, createTime, modifyTime);
  134. }
  135. //------------------------------------------------------------------------------
  136. ResManager::~ResManager ()
  137. {
  138. purge ();
  139. // volume list should be gone.
  140. if (pathList)
  141. dFree (pathList);
  142. for (ResourceObject * walk = resourceList.nextResource; walk;
  143. walk = walk->nextResource)
  144. walk->destruct ();
  145. while (resourceList.nextResource)
  146. freeResource (resourceList.nextResource);
  147. while (registeredList)
  148. {
  149. RegisteredExtension *temp = registeredList->next;
  150. delete registeredList;
  151. registeredList = temp;
  152. }
  153. }
  154. #ifdef TORQUE_DEBUG
  155. void ResManager::dumpResources (const bool onlyLoaded)
  156. {
  157. ResourceObject *walk = resourceList.nextResource;
  158. while (walk != NULL)
  159. {
  160. if ( !onlyLoaded || walk->mInstance != NULL)
  161. {
  162. Con::errorf ("Resource: %s/%s (%d)", walk->path, walk->name,
  163. walk->lockCount);
  164. }
  165. walk = walk->nextResource;
  166. }
  167. }
  168. ConsoleFunction(dumpResources, void, 2, 2, "(onlyLoaded?) Use the dumpLoadedResources function to dump a listing of the currently in-use resources to the console. This will include such things as sound files, font files, etc.\n"
  169. "For this to work, the engine must have been compiled with TORQUE_DEBUG defined.\n"
  170. "@return No return value.\n"
  171. "@sa purgeResources")
  172. {
  173. const bool onlyLoaded = argc == 2 ? dAtob(argv[1]) : true;
  174. ResourceManager->dumpResources(onlyLoaded);
  175. }
  176. #endif
  177. //------------------------------------------------------------------------------
  178. void ResManager::create ()
  179. {
  180. AssertFatal (ResourceManager == NULL,
  181. "ResourceManager::create: manager already exists.");
  182. ResourceManager = new ResManager;
  183. Con::addVariable("Pref::ResourceManager::excludedDirectories", TypeString, &smExcludedDirectories);
  184. }
  185. //------------------------------------------------------------------------------
  186. void ResManager::destroy ()
  187. {
  188. AssertFatal (ResourceManager != NULL,
  189. "ResourceManager::destroy: manager does not exist.");
  190. delete ResourceManager;
  191. ResourceManager = NULL;
  192. }
  193. //------------------------------------------------------------------------------
  194. void ResManager::setFileNameEcho (bool on)
  195. {
  196. echoFileNames = on;
  197. }
  198. //------------------------------------------------------------------------------
  199. bool ResManager::isValidWriteFileName (const char *fn)
  200. {
  201. if(isUsingVFS())
  202. return false;
  203. return true;
  204. if (!writeablePath[0])
  205. return true;
  206. // get the path to the file
  207. const char * path = dStrrchr (fn, '/');
  208. if (!path)
  209. path = fn;
  210. else
  211. {
  212. if (!dStrchr (path, '.'))
  213. return false;
  214. }
  215. // now loop through the writeable path.
  216. const char * start = writeablePath;
  217. for (;;)
  218. {
  219. const char * end = dStrchr (writeablePath, ';');
  220. if (!end)
  221. end = writeablePath + dStrlen (writeablePath);
  222. //if (end - start == pathLen && !dStrnicmp (start, path, pathLen))
  223. if(dStrnicmp(start, fn, end - start) == 0)
  224. return true;
  225. if (end[0])
  226. start = end + 1;
  227. else
  228. break;
  229. }
  230. // now check prefs folder
  231. const char * prefsPath = Platform::getPrefsPath();
  232. if (dStrnicmp(prefsPath, fn, dStrlen(prefsPath)) == 0)
  233. return true;
  234. // now check user data folder
  235. const char * dataPath = Platform::getUserDataDirectory();
  236. if (dStrnicmp(dataPath, fn, dStrlen(dataPath)) == 0)
  237. return true;
  238. return false;
  239. }
  240. void ResManager::setWriteablePath (const char *path)
  241. {
  242. dStrcpy (writeablePath, path);
  243. }
  244. //------------------------------------------------------------------------------
  245. static const char * buildPath (StringTableEntry path, StringTableEntry file)
  246. {
  247. static char buf[1024];
  248. if (path)
  249. Platform::makeFullPathName(file, buf, sizeof(buf), path);
  250. else
  251. dStrcpy (buf, file);
  252. return buf;
  253. }
  254. //------------------------------------------------------------------------------
  255. static void getPaths (const char *fullPath, StringTableEntry & path,
  256. StringTableEntry & fileName)
  257. {
  258. static char buf[1024];
  259. char *ptr = (char *) dStrrchr (fullPath, '/');
  260. if (!ptr)
  261. {
  262. path = NULL;
  263. fileName = StringTable->insert (fullPath);
  264. }
  265. else
  266. {
  267. S32 len = ptr - fullPath;
  268. dStrncpy (buf, fullPath, len);
  269. buf[len] = 0;
  270. fileName = StringTable->insert (ptr + 1);
  271. path = StringTable->insert (buf);
  272. }
  273. }
  274. //------------------------------------------------------------------------------
  275. bool ResManager::addVFSRoot(Zip::ZipArchive *vfs)
  276. {
  277. ResourceObject *ro = createResource (StringTable->EmptyString, StringTable->EmptyString);
  278. dictionary.pushBehind (ro, ResourceObject::File);
  279. // [tom, 10/28/2006] Using VolumeBlock here so that destruct() doesnt try and
  280. // delete the archive.
  281. ro->flags = ResourceObject::VolumeBlock;
  282. ro->fileOffset = 0;
  283. ro->fileSize = 0;
  284. ro->compressedFileSize = 0;
  285. ro->mZipArchive = vfs;
  286. ro->zipPath = StringTable->EmptyString;
  287. ro->zipName = StringTable->EmptyString;
  288. usingVFS = true;
  289. return scanZip(ro);
  290. }
  291. bool ResManager::scanZip (ResourceObject * zipObject)
  292. {
  293. const char *zipPath = buildPath(zipObject->zipPath, zipObject->zipName);
  294. if(zipObject->mZipArchive == NULL)
  295. {
  296. zipObject->mZipArchive = new Zip::ZipArchive;
  297. if(! zipObject->mZipArchive->openArchive(zipPath))
  298. {
  299. SAFE_DELETE(zipObject->mZipArchive);
  300. return false;
  301. }
  302. }
  303. for(U32 i = 0;i < zipObject->mZipArchive->numEntries();++i)
  304. {
  305. const Zip::CentralDir &dir = (*zipObject->mZipArchive)[i];
  306. // FIXME [tom, 10/26/2006] This is pretty lame
  307. char buf[1024];
  308. dStrncpy(buf, dir.mFilename, sizeof(buf));
  309. buf[sizeof(buf)-1] = 0;
  310. // Iterate through the string and change any
  311. // characters with \\ to /
  312. char* scan = buf;
  313. while (*scan != '\0')
  314. {
  315. if (*scan == '\\')
  316. *scan = '/';
  317. scan++;
  318. }
  319. const char *zipFN = zipObject->mZipArchive->getFilename() ? zipObject->mZipArchive->getFilename() : "";
  320. FrameTemp<char> zipPath(dStrlen(zipFN) + dStrlen(buf) + 2);
  321. dStrcpy(zipPath, zipFN);
  322. char* dot = dStrrchr(zipPath, '.');
  323. if(dot)
  324. {
  325. dot -= 2;
  326. dot[2] = '\0';
  327. dStrcat(zipPath, "/");
  328. }
  329. dStrcat(zipPath, buf);
  330. // Create file base name
  331. char* pPathEnd = dStrrchr(zipPath, '/');
  332. if(pPathEnd == NULL)
  333. continue;
  334. pPathEnd[0] = '\0';
  335. const char * path = StringTable->insert(zipPath);
  336. const char * file = StringTable->insert(pPathEnd + 1);
  337. ResourceObject *ro = createZipResource(path, file, zipObject->zipPath, zipObject->zipName);
  338. ro->flags = ResourceObject::VolumeBlock;
  339. ro->fileSize = dir.mUncompressedSize;
  340. ro->compressedFileSize = dir.mCompressedSize;
  341. ro->fileOffset = dir.mLocalHeadOffset;
  342. ro->mZipArchive = zipObject->mZipArchive;
  343. ro->mCentralDir = &dir;
  344. dictionary.pushBehind (ro, ResourceObject::File);
  345. }
  346. return true;
  347. }
  348. //------------------------------------------------------------------------------
  349. void ResManager::searchPath (const char *path, bool noDups /* = false */, bool ignoreZips /* = false */ )
  350. {
  351. AssertFatal (path != NULL, "No path to dump?");
  352. // Set up exclusions.
  353. initExcludedDirectories();
  354. Vector < Platform::FileInfo > fileInfoVec;
  355. Platform::dumpPath (path, fileInfoVec);
  356. for (U32 i = 0; i < (U32)fileInfoVec.size (); i++)
  357. {
  358. Platform::FileInfo & rInfo = fileInfoVec[i];
  359. // Create a resource for this file...
  360. //
  361. if(noDups && dictionary.find(rInfo.pFullPath, rInfo.pFileName) != NULL)
  362. continue;
  363. ResourceObject *ro = createResource (rInfo.pFullPath, rInfo.pFileName);
  364. dictionary.pushBehind (ro, ResourceObject::File);
  365. //Con::printf("> ResourceManager: Found file '%s' in path '%s'.", rInfo.pFileName, rInfo.pFullPath );
  366. ro->flags = ResourceObject::File;
  367. ro->fileOffset = 0;
  368. ro->fileSize = rInfo.fileSize;
  369. ro->compressedFileSize = rInfo.fileSize;
  370. // see if it's a zip
  371. const char *extension = dStrrchr (ro->name, '.');
  372. if (extension && !dStricmp (extension, ".zip") && !ignoreZips )
  373. {
  374. // Copy the path and files names to the zips resource object
  375. ro->zipName = rInfo.pFileName;
  376. ro->zipPath = rInfo.pFullPath;
  377. scanZip(ro);
  378. }
  379. }
  380. // Clear Exclusion list
  381. Platform::clearExcludedDirectories();
  382. }
  383. //------------------------------------------------------------------------------
  384. bool ResManager::setModZip(const char* path)
  385. {
  386. // Get the path and add .zip to the end of the dir
  387. const char* ext = ".zip";
  388. char* modPath = new char[dStrlen(path) + dStrlen(ext) + 1]; // make enough room.
  389. dStrcpy(modPath, path);
  390. dStrcat(modPath, ext);
  391. // Now we have to go through the root and look for our zipped up mod
  392. // this is unfortunately necessary because there is no means to get
  393. // a individual files properties -- we can only do it in one
  394. // big dump
  395. const char *basePath = Platform::getCurrentDirectory();
  396. Vector < Platform::FileInfo > pathInfo;
  397. Platform::dumpPath (basePath, pathInfo);
  398. for(U32 i = 0; i < (U32)pathInfo.size(); i++)
  399. {
  400. Platform::FileInfo &file = pathInfo[i];
  401. if(!dStricmp(file.pFileName, modPath))
  402. {
  403. // Setup the resource to the zip file itself
  404. ResourceObject *zip = createResource(basePath, file.pFileName);
  405. dictionary.pushBehind(zip, ResourceObject::File);
  406. zip->flags = ResourceObject::File;
  407. zip->fileOffset = 0;
  408. zip->fileSize = file.fileSize;
  409. zip->compressedFileSize = file.fileSize;
  410. zip->zipName = file.pFileName;
  411. zip->zipPath = basePath;
  412. // Setup the resource for the zip contents
  413. // ..now open the volume and add all its resources to the dictionary
  414. scanZip(zip);
  415. // Break from the loop since we got our one file
  416. delete [] modPath;
  417. return true;
  418. }
  419. }
  420. delete [] modPath;
  421. return false;
  422. }
  423. //------------------------------------------------------------------------------
  424. void ResManager::initExcludedDirectories()
  425. {
  426. // Set up our excluded directories.
  427. Platform::clearExcludedDirectories();
  428. // ignored is a semi-colon delimited list of names.
  429. char *working = dStrdup(smExcludedDirectories);
  430. char* temp = dStrtok( working, ";" );
  431. while ( temp )
  432. {
  433. Platform::addExcludedDirectory(temp);
  434. temp = dStrtok( NULL, ";" );
  435. }
  436. dFree(working);
  437. }
  438. void ResManager::addPath(const char *path, bool ignoreZips )
  439. {
  440. searchPath(path, true, ignoreZips );
  441. }
  442. ConsoleFunction(addResPath, void, 2, 3, "(path, [ignoreZips=false]) Add a path to the resource manager")
  443. {
  444. if( argc > 2 )
  445. ResourceManager->addPath(argv[1], dAtob(argv[2]));
  446. else
  447. ResourceManager->addPath(argv[1]);
  448. }
  449. void ResManager::removePath(const char *path)
  450. {
  451. ResourceObject *rwalk = resourceList.nextResource, *rtemp;
  452. while (rwalk != NULL)
  453. {
  454. const char *fname = buildPath(rwalk->path, rwalk->name);
  455. if(!rwalk->mInstance && FindMatch::isMatch(path, fname, false))
  456. {
  457. rwalk->unlink ();
  458. dictionary.remove (rwalk);
  459. rtemp = rwalk->nextResource;
  460. freeResource (rwalk);
  461. rwalk = rtemp;
  462. }
  463. else
  464. rwalk = rwalk->nextResource;
  465. }
  466. }
  467. ConsoleFunction(removeResPath, void, 2, 2, "(pathExpression) Remove a path from the resource manager. Path is an expression as in findFirstFile()")
  468. {
  469. ResourceManager->removePath(argv[1]);
  470. }
  471. void ResManager::setModPaths (U32 numPaths, const char **paths)
  472. {
  473. // [tom, 10/28/2006] If we're using a VFS, we don't want to do this
  474. // since it'll remove all the stuff we've already added.
  475. if(usingVFS)
  476. return;
  477. // detach all the files.
  478. for(ResourceObject * pwalk = resourceList.nextResource; pwalk; pwalk = pwalk->nextResource)
  479. pwalk->flags |= ResourceObject::Added;
  480. U32 pathLen = 0;
  481. // Set up exclusions.
  482. initExcludedDirectories();
  483. // Make sure invalid paths are not processed
  484. Vector<const char*> validPaths;
  485. // Determine if the mod paths are valid
  486. for (U32 i = 0; i < numPaths; i++)
  487. {
  488. pathLen += (dStrlen (paths[i]) + 1);
  489. // Load zip first so that local files override
  490. setModZip(paths[i]);
  491. searchPath (paths[i]);
  492. // Copy this path to the validPaths list
  493. validPaths.push_back(paths[i]);
  494. }
  495. Platform::clearExcludedDirectories();
  496. if (!pathLen)
  497. return;
  498. // Build the internal path list string
  499. pathList = (char *) dRealloc (pathList, pathLen);
  500. dStrcpy (pathList, validPaths[0]);
  501. U32 strlen;
  502. for (U32 i = 1; i < (U32)validPaths.size(); i++)
  503. {
  504. strlen = dStrlen (pathList);
  505. dSprintf (pathList + strlen, pathLen - strlen, ";%s", validPaths[i]);
  506. }
  507. // Unlink all 'added' that aren't loaded.
  508. ResourceObject *rwalk = resourceList.nextResource, *rtemp;
  509. while (rwalk != NULL)
  510. {
  511. if ((rwalk->flags & ResourceObject::Added) && !rwalk->mInstance)
  512. {
  513. rwalk->unlink ();
  514. dictionary.remove (rwalk);
  515. rtemp = rwalk->nextResource;
  516. freeResource (rwalk);
  517. rwalk = rtemp;
  518. }
  519. else
  520. rwalk = rwalk->nextResource;
  521. }
  522. }
  523. const char * ResManager::getModPaths ()
  524. {
  525. return ((const char *) pathList);
  526. }
  527. //------------------------------------------------------------------------------
  528. // Mod paths aren't used in tools applications.
  529. // See : addResPath/removeResPath console functions
  530. ConsoleFunction( setModPaths, void, 2, 2, "( path ) Use the setModPaths function to set the current mod path to the value specified in path.\n"
  531. "@param path A string containing a semi-colon (;) separated list of game and mod paths.\n"
  532. "@return No return value.\n"
  533. "@sa getModPaths")
  534. {
  535. char buf[512];
  536. dStrncpy(buf, argv[1], sizeof(buf) - 1);
  537. buf[511] = '\0';
  538. Vector<char *> paths;
  539. char* temp = dStrtok( buf, ";" );
  540. while ( temp )
  541. {
  542. if ( temp[0] )
  543. paths.push_back(temp);
  544. temp = dStrtok( NULL, ";" );
  545. }
  546. ResourceManager->setModPaths( paths.size(), (const char**) paths.address() );
  547. }
  548. ConsoleFunction( getModPaths, const char*, 1, 1, "() Use the getModPaths function to get the current mod path information.\n"
  549. "@return Returns a string equivalent to the complete current mod path, that is all pads that are visible to the file manager.\n"
  550. "@sa setModPaths")
  551. {
  552. return( ResourceManager->getModPaths() );
  553. }
  554. //------------------------------------------------------------------------------
  555. S32 ResManager::getSize (const char *fileName)
  556. {
  557. ResourceObject * ro = find (fileName);
  558. if (!ro)
  559. return 0;
  560. else
  561. return ro->fileSize;
  562. }
  563. //------------------------------------------------------------------------------
  564. const char * ResManager::getFullPath (const char *fileName, char *path, U32 pathlen)
  565. {
  566. AssertFatal (fileName, "ResourceManager::getFullPath: fileName is NULL");
  567. AssertFatal (path, "ResourceManager::getFullPath: path is NULL");
  568. ResourceObject *obj = find (fileName);
  569. if (!obj)
  570. dStrcpy (path, fileName);
  571. else
  572. Platform::makeFullPathName(obj->name, path, pathlen, obj->path);
  573. return path;
  574. }
  575. //------------------------------------------------------------------------------
  576. const char *ResManager::getPathOf (const char *fileName)
  577. {
  578. AssertFatal (fileName, "ResourceManager::getPathOf: fileName is NULL");
  579. ResourceObject *obj = find (fileName);
  580. if (!obj)
  581. return NULL;
  582. else
  583. return obj->path;
  584. }
  585. //------------------------------------------------------------------------------
  586. const char * ResManager::getModPathOf (const char *fileName)
  587. {
  588. AssertFatal (fileName, "ResourceManager::getModPathOf: fileName is NULL");
  589. if (!pathList)
  590. return NULL;
  591. ResourceObject *obj = find (fileName);
  592. if (!obj)
  593. return NULL;
  594. char buffer[256];
  595. char *base;
  596. const char *list = pathList;
  597. do
  598. {
  599. base = buffer;
  600. *base = 0;
  601. while (*list && *list != ';')
  602. {
  603. *base++ = *list++;
  604. }
  605. if (*list == ';')
  606. ++list;
  607. *base = 0;
  608. if (dStrncmp (buffer, obj->path, (base - buffer)) == 0)
  609. return StringTable->insert (buffer);
  610. }
  611. while (*list);
  612. return NULL;
  613. }
  614. //------------------------------------------------------------------------------
  615. const char *ResManager::getBasePath ()
  616. {
  617. if (!pathList)
  618. return NULL;
  619. const char *base = dStrrchr (pathList, ';');
  620. return base ? (base + 1) : pathList;
  621. }
  622. //------------------------------------------------------------------------------
  623. void ResManager::registerExtension (const char *name, RESOURCE_CREATE_FN create_fn)
  624. {
  625. AssertFatal (!getCreateFunction (name),
  626. "ResourceManager::registerExtension: file extension already registered.");
  627. const char *extension = dStrrchr (name, '.');
  628. AssertFatal (extension,
  629. "ResourceManager::registerExtension: file has no extension.");
  630. RegisteredExtension *add = new RegisteredExtension;
  631. add->mExtension = StringTable->insert (extension);
  632. add->mCreateFn = create_fn;
  633. add->next = registeredList;
  634. registeredList = add;
  635. }
  636. //------------------------------------------------------------------------------
  637. RESOURCE_CREATE_FN ResManager::getCreateFunction (const char *name)
  638. {
  639. const char * s = dStrrchr (name, '.');
  640. if (!s)
  641. return (NULL);
  642. RegisteredExtension * itr = registeredList;
  643. while (itr)
  644. {
  645. if (dStricmp (s, itr->mExtension) == 0)
  646. return (itr->mCreateFn);
  647. itr = itr->next;
  648. }
  649. return (NULL);
  650. }
  651. //------------------------------------------------------------------------------
  652. void ResManager::unlock (ResourceObject * obj)
  653. {
  654. if (!obj)
  655. return;
  656. AssertFatal (obj->lockCount > 0,
  657. "ResourceManager::unlock: lock count is zero.");
  658. //set the timeout to the max requested
  659. if (--obj->lockCount == 0)
  660. obj->linkAfter (&timeoutList);
  661. }
  662. //------------------------------------------------------------------------------
  663. // gets the crc of the file, ignores the stream type
  664. bool ResManager::getCrc (const char *fileName, U32 & crcVal,
  665. const U32 crcInitialVal)
  666. {
  667. ResourceObject *obj = find (fileName);
  668. if (!obj)
  669. return (false);
  670. // check if in a volume
  671. if (obj->flags & (ResourceObject::VolumeBlock | ResourceObject::File))
  672. {
  673. // can't crc locked resources...
  674. if (obj->lockCount)
  675. return false;
  676. // get rid of the resource
  677. // have to make sure user can't have it sitting around in the resource cache
  678. obj->unlink ();
  679. obj->destruct ();
  680. Stream *stream = openStream (obj);
  681. U32 waterMark = 0xFFFFFFFF;
  682. U8 *buffer;
  683. U32 maxSize = FrameAllocator::getHighWaterMark () - FrameAllocator::getWaterMark ();
  684. if (maxSize < (U32)obj->fileSize)
  685. buffer = new U8[obj->fileSize];
  686. else
  687. {
  688. waterMark = FrameAllocator::getWaterMark ();
  689. buffer = (U8 *) FrameAllocator::alloc (obj->fileSize);
  690. }
  691. stream->read (obj->fileSize, buffer);
  692. // get the crc value
  693. crcVal = calculateCRC (buffer, obj->fileSize, crcInitialVal);
  694. if (waterMark == 0xFFFFFFFF)
  695. delete[]buffer;
  696. else
  697. FrameAllocator::setWaterMark (waterMark);
  698. closeStream (stream);
  699. return (true);
  700. }
  701. return (false);
  702. }
  703. //------------------------------------------------------------------------------
  704. ResourceObject *ResManager::load (const char *fileName, bool computeCRC)
  705. {
  706. // if filename is not known, exit now
  707. ResourceObject *obj = find (fileName);
  708. if (!obj)
  709. return NULL;
  710. // if no one has a lock on this, but it's loaded and it needs to
  711. // be CRC'd, delete it and reload it.
  712. if (!obj->lockCount && computeCRC && obj->mInstance)
  713. obj->destruct ();
  714. obj->lockCount++;
  715. obj->unlink (); // remove from purge list
  716. if (!obj->mInstance)
  717. {
  718. obj->mInstance = loadInstance (obj, computeCRC);
  719. if (!obj->mInstance)
  720. {
  721. obj->lockCount--;
  722. return NULL;
  723. }
  724. }
  725. return obj;
  726. }
  727. //------------------------------------------------------------------------------
  728. ResourceInstance * ResManager::loadInstance (const char *fileName, bool computeCRC)
  729. {
  730. // if filename is not known, exit now
  731. ResourceObject *obj = find (fileName);
  732. if (!obj)
  733. return NULL;
  734. return loadInstance (obj, computeCRC);
  735. }
  736. //------------------------------------------------------------------------------
  737. static const char *alwaysCRCList = ".ter.dif.dts";
  738. ResourceInstance * ResManager::loadInstance (ResourceObject * obj, bool computeCRC)
  739. {
  740. Stream *stream = openStream (obj);
  741. if (!stream)
  742. return NULL;
  743. if (!computeCRC)
  744. {
  745. const char *x = dStrrchr (obj->name, '.');
  746. if (x && dStrstr (alwaysCRCList, x))
  747. computeCRC = true;
  748. }
  749. if (computeCRC)
  750. obj->crc = calculateCRCStream (stream, InvalidCRC);
  751. else
  752. obj->crc = InvalidCRC;
  753. RESOURCE_CREATE_FN createFunction = ResourceManager->getCreateFunction (obj->name);
  754. if(!createFunction)
  755. {
  756. AssertWarn( false, "ResourceObject::construct: NULL resource create function.");
  757. Con::errorf("ResourceObject::construct: NULL resource create function for '%s'.", obj->name);
  758. return NULL;
  759. }
  760. ResourceInstance *ret = createFunction (*stream);
  761. if(ret)
  762. ret->mSourceResource = obj;
  763. closeStream (stream);
  764. return ret;
  765. }
  766. //------------------------------------------------------------------------------
  767. Stream * ResManager::openStream (const char *fileName)
  768. {
  769. ResourceObject *obj = find (fileName);
  770. if (!obj)
  771. return NULL;
  772. return openStream (obj);
  773. }
  774. //------------------------------------------------------------------------------
  775. Stream * ResManager::openStream (ResourceObject * obj)
  776. {
  777. // if filename is not known, exit now
  778. if (!obj)
  779. return NULL;
  780. if (echoFileNames)
  781. Con::printf ("FILE ACCESS: %s/%s", obj->path, obj->name);
  782. // used for openStream stream access
  783. FileStream *diskStream = NULL;
  784. // if disk file
  785. if (obj->flags & (ResourceObject::File))
  786. {
  787. diskStream = new FileStream;
  788. if( !diskStream->open (buildPath (obj->path, obj->name), FileStream::Read) )
  789. {
  790. delete diskStream;
  791. return NULL;
  792. }
  793. obj->fileSize = diskStream->getStreamSize ();
  794. return diskStream;
  795. }
  796. // if zip file
  797. if (obj->flags & ResourceObject::VolumeBlock)
  798. {
  799. AssertFatal(obj->mZipArchive, "mZipArchive is NULL");
  800. AssertFatal(obj->mCentralDir, "mCentralDir is NULL");
  801. return obj->mZipArchive->openFileForRead(obj->mCentralDir);
  802. }
  803. // unknown type
  804. return NULL;
  805. }
  806. //------------------------------------------------------------------------------
  807. void ResManager::closeStream(Stream* stream)
  808. {
  809. // FIXME [tom, 10/26/2006] Note that this should really hand off to ZipArchive if it's
  810. // a zip stream, but there's currently no way to get the ZipArchive pointer from
  811. // here so we just repeat the relevant code. This is pretty lame.
  812. FilterStream *currentStream, *nextStream;
  813. // Try to cast the stream to a FilterStream
  814. nextStream = dynamic_cast<FilterStream*>(stream);
  815. bool isFilter = nextStream != NULL;
  816. // While the nextStream is valid (meaning it was successfully cast to a FilterStream)
  817. while (nextStream)
  818. {
  819. // Point currentStream to nextStream
  820. currentStream = nextStream;
  821. // Point stream to the Stream contained within the current FilterStream
  822. stream = currentStream->getStream();
  823. // Detach the current FilterStream from the Stream contained within it
  824. currentStream->detachStream();
  825. // Try to cast the stream (which was already contained within a FilterStream) to a FilterStream
  826. nextStream = dynamic_cast<FilterStream*>(stream);
  827. // Delete the FilterStream that was wrapping stream
  828. delete currentStream;
  829. }
  830. if(! isFilter)
  831. delete stream;
  832. }
  833. //------------------------------------------------------------------------------
  834. ResourceObject *ResManager::find (const char *fileName)
  835. {
  836. if (!fileName)
  837. return NULL;
  838. StringTableEntry path, file;
  839. getPaths (fileName, path, file);
  840. ResourceObject *ret = dictionary.find (path, file);
  841. if(!ret)
  842. {
  843. // If we couldn't find the file in the resource list (generated
  844. // by setting the modPaths) then try to load it directly
  845. if (Platform::isFile(fileName))
  846. {
  847. ret = createResource (path, file);
  848. dictionary.pushBehind (ret, ResourceObject::File);
  849. ret->flags = ResourceObject::File;
  850. ret->fileOffset = 0;
  851. S32 fileSize = Platform::getFileSize(fileName);
  852. ret->fileSize = fileSize;
  853. ret->compressedFileSize = fileSize;
  854. return ret;
  855. }
  856. fileIsMissing(fileName);
  857. }
  858. return ret;
  859. }
  860. //------------------------------------------------------------------------------
  861. ResourceObject *ResManager::find (const char *fileName, U32 flags)
  862. {
  863. if (!fileName)
  864. return NULL;
  865. StringTableEntry path, file;
  866. getPaths (fileName, path, file);
  867. return dictionary.find (path, file, flags);
  868. }
  869. //------------------------------------------------------------------------------
  870. // Add resource constructed outside the manager
  871. bool ResManager::add (const char *name, ResourceInstance * addInstance,
  872. bool extraLock)
  873. {
  874. StringTableEntry path, file;
  875. getPaths (name, path, file);
  876. ResourceObject *obj = dictionary.find (path, file);
  877. if (obj && obj->mInstance)
  878. // Resource already exists?
  879. return false;
  880. if (!obj)
  881. obj = createResource (path, file);
  882. dictionary.pushBehind (obj,
  883. ResourceObject::File | ResourceObject::VolumeBlock);
  884. obj->mInstance = addInstance;
  885. addInstance->mSourceResource = obj;
  886. obj->lockCount = extraLock ? 2 : 1;
  887. unlock (obj);
  888. return true;
  889. }
  890. //------------------------------------------------------------------------------
  891. void ResManager::purge ()
  892. {
  893. bool found;
  894. do
  895. {
  896. ResourceObject *obj = timeoutList.getNext ();
  897. found = false;
  898. while (obj)
  899. {
  900. ResourceObject *temp = obj;
  901. obj = obj->next;
  902. temp->unlink ();
  903. temp->destruct ();
  904. found = true;
  905. if (temp->flags & ResourceObject::Added)
  906. freeResource (temp);
  907. }
  908. }
  909. while (found);
  910. }
  911. ConsoleFunction( purgeResources, void, 1, 1, "() Use the purgeResources function to purge all game resources.\n"
  912. "@return No return value.\n"
  913. "@sa clearTextureHolds, dumpResourceStats, dumpTextureStats, flushTextureCache")
  914. {
  915. ResourceManager->purge();
  916. }
  917. //------------------------------------------------------------------------------
  918. void ResManager::purge (ResourceObject * obj)
  919. {
  920. AssertFatal (obj->lockCount == 0,
  921. "ResourceManager::purge: handle lock count is not ZERO.") obj->
  922. unlink ();
  923. obj->destruct ();
  924. }
  925. //------------------------------------------------------------------------------
  926. // serialize sorts a list of files by .zip and position within the zip
  927. // it allows an aggregate (material list, etc) to find the preferred
  928. // loading order for a set of files.
  929. //------------------------------------------------------------------------------
  930. struct ResourceObjectIndex
  931. {
  932. ResourceObject *ro;
  933. const char *fileName;
  934. static S32 QSORT_CALLBACK compare (const void *s1, const void *s2)
  935. {
  936. const ResourceObjectIndex *r1 = (ResourceObjectIndex *) s1;
  937. const ResourceObjectIndex *r2 = (ResourceObjectIndex *) s2;
  938. if (r1->ro->path != r2->ro->path)
  939. return r1->ro->path - r2->ro->path;
  940. if (r1->ro->name != r2->ro->name)
  941. return r1->ro->name - r2->ro->name;
  942. return r1->ro->fileOffset - r2->ro->fileOffset;
  943. }
  944. };
  945. //------------------------------------------------------------------------------
  946. void ResManager::serialize (VectorPtr < const char *>&filenames)
  947. {
  948. Vector < ResourceObjectIndex > sortVector;
  949. sortVector.reserve (filenames.size ());
  950. U32 i;
  951. for (i = 0; i < (U32)filenames.size (); i++)
  952. {
  953. ResourceObjectIndex roi;
  954. roi.ro = find (filenames[i]);
  955. roi.fileName = filenames[i];
  956. sortVector.push_back (roi);
  957. }
  958. dQsort ((void *)sortVector.address(), sortVector.size (),
  959. sizeof (ResourceObjectIndex), ResourceObjectIndex::compare);
  960. for (i = 0; i < (U32)filenames.size (); i++)
  961. filenames[i] = sortVector[i].fileName;
  962. }
  963. //------------------------------------------------------------------------------
  964. ResourceObject * ResManager::findMatch (const char *expression, const char **fn,
  965. ResourceObject * start)
  966. {
  967. if (!start)
  968. start = resourceList.nextResource;
  969. else
  970. start = start->nextResource;
  971. while (start)
  972. {
  973. const char *fname = buildPath (start->path, start->name);
  974. if (FindMatch::isMatch (expression, fname, false))
  975. {
  976. *fn = fname;
  977. return start;
  978. }
  979. start = start->nextResource;
  980. }
  981. return NULL;
  982. }
  983. ResourceObject * ResManager::findMatchMultiExprs (const char *multiExpression, const char **fn,
  984. ResourceObject * start)
  985. {
  986. if (!start)
  987. start = resourceList.nextResource;
  988. else
  989. start = start->nextResource;
  990. while (start)
  991. {
  992. const char *fname = buildPath (start->path, start->name);
  993. if (FindMatch::isMatchMultipleExprs(multiExpression, fname, false))
  994. {
  995. *fn = fname;
  996. return start;
  997. }
  998. start = start->nextResource;
  999. }
  1000. return NULL;
  1001. }
  1002. S32 ResManager::findMatches (FindMatch * pFM)
  1003. {
  1004. static char buffer[16384];
  1005. S32 bufl = 0;
  1006. ResourceObject * walk;
  1007. for (walk = resourceList.nextResource; walk && !pFM->isFull (); walk = walk->nextResource)
  1008. {
  1009. const char * fpath =
  1010. buildPath (walk->path, walk->name);
  1011. if (bufl + dStrlen (fpath) >= 16380)
  1012. return pFM->numMatches ();
  1013. dStrcpy (buffer + bufl, fpath);
  1014. if (pFM->findMatch (buffer + bufl))
  1015. bufl += dStrlen (fpath) + 1;
  1016. }
  1017. return (pFM->numMatches ());
  1018. }
  1019. //------------------------------------------------------------------------------
  1020. bool ResManager::findFile (const char *name)
  1021. {
  1022. return (bool) find (name);
  1023. }
  1024. //------------------------------------------------------------------------------
  1025. ResourceObject * ResManager::createResource (StringTableEntry path, StringTableEntry file)
  1026. {
  1027. ResourceObject *newRO = dictionary.find (path, file);
  1028. if (newRO)
  1029. return newRO;
  1030. newRO = new ResourceObject;
  1031. newRO->path = path;
  1032. newRO->name = file;
  1033. newRO->lockCount = 0;
  1034. newRO->mInstance = NULL;
  1035. newRO->flags = ResourceObject::Added;
  1036. newRO->next = newRO->prev = NULL;
  1037. newRO->nextResource = resourceList.nextResource;
  1038. resourceList.nextResource = newRO;
  1039. newRO->prevResource = &resourceList;
  1040. if (newRO->nextResource)
  1041. newRO->nextResource->prevResource = newRO;
  1042. dictionary.insert (newRO, path, file);
  1043. newRO->fileSize = newRO->fileOffset = newRO->compressedFileSize = 0;
  1044. newRO->zipPath = NULL;
  1045. newRO->zipName = NULL;
  1046. newRO->crc = InvalidCRC;
  1047. return newRO;
  1048. }
  1049. //------------------------------------------------------------------------------
  1050. ResourceObject * ResManager::createZipResource (StringTableEntry path, StringTableEntry file,
  1051. StringTableEntry zipPath,
  1052. StringTableEntry zipName)
  1053. {
  1054. ResourceObject *newRO = dictionary.find (path, file, zipPath, zipName);
  1055. if (newRO)
  1056. return newRO;
  1057. newRO = new ResourceObject;
  1058. newRO->path = path;
  1059. newRO->name = file;
  1060. newRO->lockCount = 0;
  1061. newRO->mInstance = NULL;
  1062. newRO->flags = ResourceObject::Added;
  1063. newRO->next = newRO->prev = NULL;
  1064. newRO->nextResource = resourceList.nextResource;
  1065. resourceList.nextResource = newRO;
  1066. newRO->prevResource = &resourceList;
  1067. if (newRO->nextResource)
  1068. newRO->nextResource->prevResource = newRO;
  1069. dictionary.insert (newRO, path, file);
  1070. newRO->fileSize = newRO->fileOffset = newRO->compressedFileSize = 0;
  1071. newRO->zipPath = zipPath;
  1072. newRO->zipName = zipName;
  1073. newRO->crc = InvalidCRC;
  1074. newRO->mZipArchive = NULL;
  1075. newRO->mCentralDir = NULL;
  1076. return newRO;
  1077. }
  1078. //------------------------------------------------------------------------------
  1079. void ResManager::freeResource (ResourceObject * ro)
  1080. {
  1081. ro->destruct ();
  1082. ro->unlink ();
  1083. // if((ro->flags & ResourceObject::File) && ro->lockedData)
  1084. // delete[] ro->lockedData;
  1085. if (ro->prevResource)
  1086. ro->prevResource->nextResource = ro->nextResource;
  1087. if (ro->nextResource)
  1088. ro->nextResource->prevResource = ro->prevResource;
  1089. dictionary.remove (ro);
  1090. delete ro;
  1091. }
  1092. //------------------------------------------------------------------------------
  1093. bool ResManager::openFileForWrite (FileStream & stream, const char *fileName, U32 accessMode)
  1094. {
  1095. //if (!isValidWriteFileName (fileName))
  1096. //return false;
  1097. // tag it on to the first directory
  1098. char path[1024];
  1099. dStrcpy (path, fileName);
  1100. char *file = dStrrchr (path, '/');
  1101. if (!file)
  1102. return false; // don't allow storing files in root
  1103. *file++ = 0;
  1104. if (!Platform::createPath (fileName)) // create directory tree
  1105. return false;
  1106. if (!stream.open (fileName, (FileStream::AccessMode) accessMode))
  1107. return false;
  1108. // create a resource for the file.
  1109. ResourceObject *ro = createResource (StringTable->insert (path), StringTable->insert (file));
  1110. ro->flags = ResourceObject::File;
  1111. ro->fileOffset = 0;
  1112. ro->fileSize = 0;
  1113. ro->compressedFileSize = 0;
  1114. return true;
  1115. }
  1116. ConsoleFunction(isUsingVFS, bool, 1, 1, "()\n"
  1117. "@return Returns true if using Virtual File System")
  1118. {
  1119. return ResourceManager->isUsingVFS();
  1120. }