resourceManager.cc 36 KB

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