xoreos  0.0.5
resman.cpp
Go to the documentation of this file.
1 /* xoreos - A reimplementation of BioWare's Aurora engine
2  *
3  * xoreos is the legal property of its developers, whose names
4  * can be found in the AUTHORS file distributed with this source
5  * distribution.
6  *
7  * xoreos is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 3
10  * of the License, or (at your option) any later version.
11  *
12  * xoreos is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with xoreos. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
25 #include <cassert>
26 
27 #include <boost/scope_exit.hpp>
28 
29 #include "src/common/util.h"
30 #include "src/common/scopedptr.h"
31 #include "src/common/error.h"
32 #include "src/common/readstream.h"
33 #include "src/common/filepath.h"
34 #include "src/common/readfile.h"
35 #include "src/common/writefile.h"
36 
37 #include "src/aurora/resman.h"
38 #include "src/aurora/util.h"
39 
40 #include "src/aurora/keyfile.h"
41 #include "src/aurora/keydatafile.h"
42 #include "src/aurora/biffile.h"
43 #include "src/aurora/bzffile.h"
44 #include "src/aurora/erffile.h"
45 #include "src/aurora/rimfile.h"
46 #include "src/aurora/ndsrom.h"
47 #include "src/aurora/zipfile.h"
48 #include "src/aurora/pefile.h"
49 #include "src/aurora/herffile.h"
50 #include "src/aurora/nsbtxfile.h"
51 #include "src/aurora/smallfile.h"
52 
53 // Check for hash collisions (if possible)
54 #define CHECK_HASH_COLLISION 1
55 
57 
58 namespace Aurora {
59 
61  type(kArchiveMAX), resource(0), opened(0) {
62 
63 }
64 
66  name(n), type(t), resource(&r), opened(0) {
67 
68 }
69 
70 
71 ResourceManager::OpenedArchive::OpenedArchive() : archive(0), known(0), parent(0) {
72 }
73 
75  archive = &a;
76  known = &kA;
77 
78  if (known->opened)
79  throw Common::Exception("Archive \"%s\" already opened", known->name.c_str());
80 
81  known->opened = this;
82 
83  /* If the resource itself is found in an archive, double-link these two
84  * archives, so that we can catch attempts of closing archives with
85  * child-archives still open.
86  */
87 
88  assert(known->resource);
89  if (known->resource->source == kSourceArchive) {
90  assert(known->resource->archive);
91 
92  parent = known->resource->archive;
93  parent->children.push_back(this);
94  }
95 }
96 
97 
98 ResourceManager::Resource::Resource() : type(kFileTypeNone), isSmall(false), priority(0),
99  source(kSourceNone), archive(0), archiveIndex(0xFFFFFFFF) {
100 
101  selfArchive.first = 0;
102 }
103 
105  return priority < right.priority;
106 }
107 
108 
111 
112  // These file types are archives
113 
115 
118 
124 
127 
129 
131 
133 
135 
137 
138  // These files types are specific resource types
139 
150 
157 
161 
166 
171 }
172 
174  clearResources();
175 }
176 
178  _typeAliases.clear();
179 
180  _hasSmall = false;
182 
183  setRIMsAreERFs(false);
184  clearResources();
185 }
186 
188  _cursorRemap.clear();
189 
190  _baseDir.clear();
192 
193  for (size_t i = 0; i < kArchiveMAX; i++)
194  _knownArchives[i].clear();
195 
196  for (OpenedArchives::iterator a = _openedArchives.begin(); a != _openedArchives.end(); ++a)
197  delete a->archive;
198  _openedArchives.clear();
199 
200  _resources.clear();
201 
202  _changes.clear();
203 }
204 
205 void ResourceManager::setRIMsAreERFs(bool rimsAreERFs) {
206  // Treat RIM and RIMP as either RIM or ERF
207 
212 
213  if (rimsAreERFs) {
216  } else {
219  }
220 }
221 
222 void ResourceManager::setHasSmall(bool hasSmall) {
223  _hasSmall = hasSmall;
224 }
225 
227  if ((algo != _hashAlgo) && !_resources.empty())
228  throw Common::Exception("ResourceManager::setHashAlgo(): We already have resources!");
229 
230  _hashAlgo = algo;
231 }
232 
233 void ResourceManager::setCursorRemap(const std::vector<Common::UString> &remap) {
234  _cursorRemap = remap;
235 }
236 
238  clearResources();
239 
241 
242  if (Common::FilePath::isDirectory(base)) {
243 
244  _baseDir = base;
245 
246  indexResourceDir("", 0, 0, 1);
247 
248  } else if (Common::FilePath::isRegularFile(base)) {
249 
250  _baseArchive = base;
251 
254 
255  } else
256  throw Common::Exception("No such file or directory \"%s\"", path.c_str());
257 
258 }
259 
261  if (!_baseArchive.empty())
262  return _baseArchive;
263 
264  return _baseDir;
265 }
266 
268  ArchiveType archiveType = getArchiveType(file);
269  if (((size_t) archiveType) >= kArchiveMAX)
270  return 0;
271 
272  return findArchive(file, _knownArchives[archiveType]);
273 }
274 
276  file = (Common::FilePath::isAbsolute(file) ? "" : "/") + file;
277  file = Common::FilePath::normalize(file, false).toLower();
278 
279  for (KnownArchives::iterator a = archives.begin(); a != archives.end(); ++a) {
280  if (a->name.toLower().endsWith(file))
281  return &*a;
282 
283  // BZF archives are still indexed as BIF in the KEY file
285  if (a->name.toLower().endsWith(Common::FilePath::changeExtension(file, ".bzf")))
286  return &*a;
287  }
288 
289  return 0;
290 }
291 
293  return findArchive(file) != 0;
294 }
295 
297  if (!archive.resource)
298  throw Common::Exception("Archive without resource reference");
299 
300  return getResource(*archive.resource, true);
301 }
302 
304  const std::vector<byte> &password, Common::ChangeID *changeID) {
305 
306  KnownArchive *knownArchive = findArchive(file);
307  if (!knownArchive)
308  throw Common::Exception("No such archive file \"%s\"", file.c_str());
309 
310  if (knownArchive->type == kArchiveBIF)
311  throw Common::Exception("Attempted to index a lone BIF");
312 
313  Change *change = 0;
314  if (changeID)
315  change = newChangeSet(*changeID);
316 
317  Common::SeekableReadStream *archiveStream = openArchiveStream(*knownArchive);
318 
320  switch (knownArchive->type) {
321  case kArchiveKEY:
322  indexKEY(archiveStream, priority, change);
323  break;
324 
325  case kArchiveNDS:
326  archive.reset(new NDSFile(archiveStream));
327  break;
328 
329  case kArchiveHERF:
330  archive.reset(new HERFFile(archiveStream));
331  break;
332 
333  case kArchiveERF:
334  archive.reset(new ERFFile(archiveStream, password));
335  break;
336 
337  case kArchiveRIM:
338  archive.reset(new RIMFile(archiveStream));
339  break;
340 
341  case kArchiveZIP:
342  archive.reset(new ZIPFile(archiveStream));
343  break;
344 
345  case kArchiveEXE:
346  archive.reset(new PEFile(archiveStream, _cursorRemap));
347  break;
348 
349  case kArchiveNSBTX:
350  archive.reset(new NSBTXFile(archiveStream));
351  break;
352 
353  default:
354  throw Common::Exception("Invalid archive type %d", knownArchive->type);
355  }
356 
357  if (archive)
358  indexArchive(*knownArchive, archive.release(), priority, change);
359 }
360 
362  std::vector<byte> password;
363 
364  indexArchive(file, priority, password, changeID);
365 }
366 
368  std::vector<KnownArchive *> &archives,
369  std::vector<KEYDataFile *> &keyData) {
370 
371  bool success = false;
372  BOOST_SCOPE_EXIT( (&success) (&archives) (&keyData) ) {
373  if (!success) {
374  for (std::vector<KEYDataFile *>::iterator b = keyData.begin(); b != keyData.end(); ++b)
375  delete *b;
376 
377  keyData.clear();
378  archives.clear();
379  }
380  } BOOST_SCOPE_EXIT_END
381 
383  KEYFile key(*keyStream);
384 
385  const KEYFile::BIFList &keyBIFs = key.getBIFs();
386  archives.resize(keyBIFs.size(), 0);
387  keyData.resize(keyBIFs.size(), 0);
388 
389  for (uint32 i = 0; i < keyBIFs.size(); i++) {
390  archives[i] = findArchive(keyBIFs[i], _knownArchives[kArchiveBIF]);
391  if (!archives[i])
392  throw Common::Exception("BIF \"%s\" not found", keyBIFs[i].c_str());
393 
394  if (Common::FilePath::getExtension(archives[i]->name).equalsIgnoreCase(".bzf"))
395  keyData[i] = new BZFFile(openArchiveStream(*archives[i]));
396  else
397  keyData[i] = new BIFFile(openArchiveStream(*archives[i]));
398 
399  keyData[i]->mergeKEY(key, i);
400  }
401 
402  success = true;
403  return archives.size();
404 }
405 
407  std::vector<KnownArchive *> archives;
408  std::vector<KEYDataFile *> keyData;
409 
410  const uint32 count = openKEYBIFs(stream, archives, keyData);
411 
412  for (uint32 i = 0; i < count; i++)
413  indexArchive(*archives[i], keyData[i], priority, change);
414 }
415 
417  uint32 priority, Change *change) {
418 
419  const Common::HashAlgo hashAlgo = archive->getNameHashAlgo();
420  if ((hashAlgo != Common::kHashNone) && (hashAlgo != _hashAlgo))
421  throw Common::Exception("ResourceManager::indexArchive(): Archive uses a different name hashing "
422  "algorithm than we do (%d vs. %d)", (int) hashAlgo, (int) _hashAlgo);
423 
424  bool couldSet = false;
425  _openedArchives.push_back(OpenedArchive());
426 
427  BOOST_SCOPE_EXIT( (&couldSet) (&_openedArchives) (&archive) ) {
428  if (!couldSet) {
429  _openedArchives.pop_back();
430  delete archive;
431  }
432  } BOOST_SCOPE_EXIT_END
433 
434  _openedArchives.back().set(knownArchive, *archive);
435  couldSet = true;
436 
437  // Add the information of the new archive to the change set
438  if (change)
439  change->_change->openedArchives.push_back(--_openedArchives.end());
440 
441  const Archive::ResourceList &resources = archive->getResources();
442  for (Archive::ResourceList::const_iterator resource = resources.begin(); resource != resources.end(); ++resource) {
443  // Build the resource record
444  Resource res;
445  res.priority = priority;
446  res.source = kSourceArchive;
447  res.archive = &_openedArchives.back();
448  res.archiveIndex = resource->index;
449  res.name = resource->name;
450  res.type = resource->type;
451 
452  // Get the hash or calculate if we have to
453  uint64 hash = (hashAlgo == Common::kHashNone) ? getHash(res.name, res.type) : resource->hash;
454 
455  // Normalize the file types if we can and recalculate the hash
456  if ((res.name != "") && (res.type != kFileTypeNone))
457  if (normalizeType(res))
458  hash = getHash(res.name, res.type);
459 
460  // Handle "small" files
461  if (_hasSmall && (res.type == kFileTypeSMALL)) {
462  res.isSmall = true;
463 
464  res.name = Common::FilePath::getStem(resource->name);
465  res.type = TypeMan.getFileType(resource->name);
466  }
467 
468  // And add it to our list
469  addResource(res, hash, change);
470  }
471 }
472 
474  if (_baseDir.empty())
475  return false;
476 
477  return !Common::FilePath::findSubDirectory(_baseDir, dir, true).empty();
478 }
479 
481  Common::ChangeID *changeID) {
482 
483  Common::UString path;
484  path = _baseDir.empty() ? file : (_baseDir + "/" + file);
485  path = Common::FilePath::normalize(path, false);
486 
488  throw Common::Exception("No such file \"%s\"", file.c_str());
489 
490  Change *change = 0;
491  if (changeID)
492  change = newChangeSet(*changeID);
493 
494  addResource(path, change, priority);
495 }
496 
497 void ResourceManager::indexResourceDir(const Common::UString &dir, const char *glob, int depth,
498  uint32 priority, Common::ChangeID *changeID) {
499  if (_baseDir.empty())
500  throw Common::Exception("No base data directory set");
501 
502  // Find the directory
504  if (directory.empty())
505  throw Common::Exception("No such directory \"%s\"", dir.c_str());
506 
507  // Find files
508  Common::FileList files;
509  files.addDirectory(directory, depth);
510 
511  Change *change = 0;
512  if (changeID)
513  change = newChangeSet(*changeID);
514 
515  if (!glob) {
516  // Add the files
517  addResources(files, change, priority);
518  return;
519  }
520 
521  // Find files matching the glob pattern
522  Common::FileList globFiles;
523  files.getSubListGlob(glob, true, globFiles);
524 
525  // Add the files
526  addResources(globFiles, change, priority);
527 }
528 
530  Change *change = dynamic_cast<Change *>(changeID.getContent());
531  if (!change || (change->_change == _changes.end()))
532  return;
533 
534  // Removing all changes in the opened archives list
535  for (OpenedArchiveChanges::iterator oaChange = change->_change->openedArchives.begin();
536  oaChange != change->_change->openedArchives.end(); ++oaChange) {
537 
538  if (!(*oaChange)->children.empty())
539  throw Common::Exception("Attempted to deindex an archive that has opened children archives");
540 
541  assert((*oaChange)->known);
542 
543  // Remove us from the KnownArchive
544  (*oaChange)->known->opened = 0;
545 
546  // Remove us from a parent's children list
547  if ((*oaChange)->parent) {
548  std::list<OpenedArchive *> &pChildren = (*oaChange)->parent->children;
549 
550  bool found = false;
551  for (std::list<OpenedArchive *>::iterator c = pChildren.begin(); c != pChildren.end(); ++c) {
552  if (*c == &**oaChange) {
553  found = true;
554  pChildren.erase(c);
555  break;
556  }
557  }
558 
559  if (!found)
560  throw Common::Exception("Couldn't find archive in the parent's children list");
561  }
562 
563  delete (*oaChange)->archive;
564  _openedArchives.erase(*oaChange);
565  }
566 
567  // Removing all changes in the known archives list
568  for (KnownArchiveChanges::iterator kaChange = change->_change->knownArchives.begin();
569  kaChange != change->_change->knownArchives.end(); ++kaChange) {
570 
571  if (kaChange->second->opened)
572  throw Common::Exception("Attempted to deindex an archive that's still opened");
573 
574  // Remove us from the resource
575  assert(kaChange->second->resource);
576  kaChange->second->resource->selfArchive.first = 0;
577 
578  kaChange->first->erase(kaChange->second);
579  }
580 
581  // Go through all changes in the resource map
582  for (ResourceChanges::iterator resChange = change->_change->resources.begin();
583  resChange != change->_change->resources.end(); ++resChange) {
584 
585  // If the resource still has an archive attached, it was added by a
586  // declareResources() call and needs to be removed manually
587  if (resChange->resIt->selfArchive.first) {
588  if (resChange->resIt->selfArchive.second->opened)
589  throw Common::Exception("Attempted to deindex an archive resource that's still opened");
590 
591  resChange->resIt->selfArchive.first->erase(resChange->resIt->selfArchive.second);
592  }
593 
594  // Remove the resource, and the name list too if it's empty
595  resChange->hashIt->second.erase(resChange->resIt);
596 
597  if (resChange->hashIt->second.empty())
598  _resources.erase(resChange->hashIt);
599  }
600 
601  // Now we can remove the change set from our list of change sets
602  _changes.erase(change->_change);
603 
604  // And finally set the change ID to a defined empty state
605  changeID.clear();
606 }
607 
609  _typeAliases[alias] = realType;
610 }
611 
613  ResourceMap::iterator resList = _resources.find(getHash(name, type));
614  if (resList == _resources.end())
615  return;
616 
617  for (ResourceList::iterator res = resList->second.begin(); res != resList->second.end(); ++res)
618  res->priority = 0;
619 }
620 
622  bool isSmall = false;
623 
624  ResourceMap::iterator resList = _resources.find(getHash(name, type));
625  if (resList == _resources.end()) {
626  if (_hasSmall) {
627  Common::UString smallName = TypeMan.addFileType(TypeMan.setFileType(name, type), kFileTypeSMALL);
628 
629  resList = _resources.find(getHash(smallName));
630  isSmall = true;
631  }
632 
633  if (resList == _resources.end())
634  return;
635  }
636 
637  for (ResourceList::iterator r = resList->second.begin(); r != resList->second.end(); ++r) {
638  r->name = name;
639  r->type = type;
640  r->isSmall = isSmall;
641 
642  checkResourceIsArchive(*r, 0);
643  }
644 }
645 
647  declareResource(TypeMan.setFileType(name, kFileTypeNone), TypeMan.getFileType(name));
648 }
649 
651  std::vector<FileType> types;
652 
653  types.push_back(type);
654 
655  return hasResource(name, types);
656 }
657 
659  assert((type >= 0) && (type < kResourceMAX));
660 
661  return hasResource(name, _resourceTypeTypes[type]);
662 }
663 
665  return hasResource(TypeMan.setFileType(name, kFileTypeNone), TypeMan.getFileType(name));
666 }
667 
668 bool ResourceManager::hasResource(const Common::UString &name, const std::vector<FileType> &types) const {
669  return getRes(name, types) != 0;
670 }
671 
673  return getRes(hash) != 0;
674 }
675 
677  std::vector<FileType> types;
678 
679  types.push_back(type);
680 
681  return findResourceFile(name, types);
682 }
683 
685  assert((type >= 0) && (type < kResourceMAX));
686 
687  return findResourceFile(name, _resourceTypeTypes[type]);
688 }
689 
691  return findResourceFile(TypeMan.setFileType(name, kFileTypeNone), TypeMan.getFileType(name));
692 }
693 
695  const std::vector<FileType> &types) const {
696  const Resource *res = getRes(name, types);
697  if (res && (res->source == kSourceFile))
698  return res->path;
699 
700  return "";
701 }
702 
704  if (res.source == kSourceArchive) {
705  if ((res.archive == 0) || (res.archive->archive == 0) || (res.archiveIndex == 0xFFFFFFFF))
706  return 0xFFFFFFFF;
707 
708  return res.archive->archive->getResourceSize(res.archiveIndex);
709  }
710 
711  if (res.source == kSourceFile)
713 
714  return 0xFFFFFFFF;
715 }
716 
718  if ((res.archive == 0) || (res.archive->archive == 0) || (res.archiveIndex == 0xFFFFFFFF))
719  throw Common::Exception("Archive resource has no archive");
720 
721  return res.archive->archive->getResource(res.archiveIndex, tryNoCopy);
722 }
723 
725  std::vector<FileType> types;
726 
727  types.push_back(type);
728 
729  return getResource(name, types);
730 }
731 
733  return getResource(TypeMan.setFileType(name, kFileTypeNone), TypeMan.getFileType(name));
734 }
735 
737  const std::vector<FileType> &types, FileType *foundType) const {
738 
739  const Resource *res = getRes(name, types);
740  if (!res)
741  return 0;
742 
743  // Return the actually found type
744  if (foundType)
745  *foundType = res->type;
746 
747  return getResource(*res);
748 }
749 
751  const Resource *res = getRes(hash);
752  if (!res)
753  return 0;
754 
755  // Return the actually found type
756  if (type)
757  *type = res->type;
758 
759  return getResource(*res);
760 }
761 
763  Common::SeekableReadStream *stream = 0;
764 
765  switch (res.source) {
766  case kSourceFile:
767  stream = new Common::ReadFile(res.path);
768  break;
769 
770  case kSourceArchive:
771  stream = getArchiveResource(res, tryNoCopy);
772  break;
773 
774  default:
775  throw Common::Exception("Invalid source for resource \"%s\": (%d)",
776  TypeMan.setFileType(res.name, res.type).c_str(), res.source);
777  }
778 
779  // Transparently decompress "small" files
780  if (res.isSmall)
781  stream = Small::decompress(stream);
782 
783  return stream;
784 }
785 
787  const Common::UString &name, FileType *foundType) const {
788 
789  assert((resType >= 0) && (resType < kResourceMAX));
790 
791  // Try every known file type for that resource type
793  if ((res = getResource(name, _resourceTypeTypes[resType], foundType)))
794  return res;
795 
796  // No such resource
797  return 0;
798 }
799 
801  std::list<ResourceID> &list) const {
802 
803  for (ResourceMap::const_iterator r = _resources.begin(); r != _resources.end(); ++r) {
804  if (!r->second.empty() && (r->second.front().type == type)) {
805  list.push_back(ResourceID());
806 
807  list.back().name = r->second.front().name;
808  list.back().type = r->second.front().type;
809  list.back().hash = r->first;
810  }
811  }
812 }
813 
814 void ResourceManager::getAvailableResources(const std::vector<FileType> &types,
815  std::list<ResourceID> &list) const {
816 
817  for (ResourceMap::const_iterator r = _resources.begin(); r != _resources.end(); ++r) {
818  for (std::vector<FileType>::const_iterator t = types.begin(); t != types.end(); ++t) {
819  if (!r->second.empty() && (r->second.front().type == *t)) {
820  list.push_back(ResourceID());
821 
822  list.back().name = r->second.front().name;
823  list.back().type = r->second.front().type;
824  list.back().hash = r->first;
825  }
826  }
827 
828  }
829 }
830 
832  std::list<ResourceID> &list) const {
833 
835 }
836 
838  for (size_t i = 0; i < kArchiveMAX; i++)
839  if (_archiveTypeTypes[i].find(type) != _archiveTypeTypes[i].end())
840  return (ArchiveType) i;
841 
842  return kArchiveMAX;
843 }
844 
846  return getArchiveType(TypeMan.getFileType(name));
847 }
848 
850  switch (resource.source) {
851  case kSourceFile:
852  return resource.path;
853 
854  case kSourceArchive:
855  return "/" + TypeMan.addFileType(resource.name, resource.type);
856 
857  default:
858  break;
859  }
860 
861  throw Common::Exception("Invalid source for resource \"%s\": (%d)",
862  TypeMan.addFileType(resource.name, resource.type).c_str(),
863  resource.source);
864 }
865 
867  // Resolve the type aliases
868  std::map<FileType, FileType>::const_iterator alias = _typeAliases.find(resource.type);
869  if (alias != _typeAliases.end()) {
870  resource.type = alias->second;
871  return true;
872  }
873 
874  // Normalize resource type *sigh*
875  if (resource.type == kFileTypeQST2)
876  resource.type = kFileTypeQST;
877  else if (resource.type == kFileTypeMDX2)
878  resource.type = kFileTypeMDX;
879  else if (resource.type == kFileTypeTXB2)
880  resource.type = kFileTypeTXB;
881  else if (resource.type == kFileTypeMDB2)
882  resource.type = kFileTypeMDB;
883  else if (resource.type == kFileTypeMDA2)
884  resource.type = kFileTypeMDA;
885  else if (resource.type == kFileTypeSPT2)
886  resource.type = kFileTypeSPT;
887  else if (resource.type == kFileTypeJPG2)
888  resource.type = kFileTypeJPG;
889  else
890  return false;
891 
892  return true;
893 }
894 
895 inline uint64 ResourceManager::getHash(const Common::UString &name, FileType type) const {
896  return getHash(TypeMan.setFileType(name, type));
897 }
898 
900  return Common::hashString(name.toLower(), _hashAlgo);
901 }
902 
903 void ResourceManager::checkHashCollision(const Resource &resource, ResourceMap::const_iterator resList) {
904  if (resource.name.empty() || resList->second.empty())
905  return;
906 
907  Common::UString newName = TypeMan.setFileType(resource.name, resource.type).toLower();
908 
909  for (ResourceList::const_iterator r = resList->second.begin(); r != resList->second.end(); ++r) {
910  if (r->name.empty())
911  continue;
912 
913  Common::UString oldName = TypeMan.setFileType(r->name, r->type).toLower();
914  if (oldName != newName) {
915  warning("ResourceManager: Found hash collision: %s (\"%s\" and \"%s\")",
916  Common::formatHash(getHash(oldName)).c_str(), oldName.c_str(), newName.c_str());
917  return;
918  }
919  }
920 }
921 
923  if ((resource.source == kSourceNone) || resource.name.empty())
924  return false;
925 
926  ArchiveType type = getArchiveType(resource.type);
927  if (type == kArchiveMAX)
928  return false;
929 
930  Common::UString name = getArchiveName(resource);
931  if (name.empty())
932  return false;
933 
934  if (resource.selfArchive.first) {
935  if ((resource.selfArchive.second->type != type) || (resource.selfArchive.second->name != name))
936  throw Common::Exception("Tried to reclassify a resource archive (\"%s\")", name.c_str());
937 
938  return false;
939  }
940 
941  KnownArchives &archives = _knownArchives[type];
942 
943  archives.push_back(KnownArchive(type, name, resource));
944 
945  resource.selfArchive = std::make_pair(&archives, --archives.end());
946 
947  if (change)
948  change->_change->knownArchives.push_back(resource.selfArchive);
949 
950  return true;
951 }
952 
953 void ResourceManager::addResource(Resource &resource, uint64 hash, Change *change) {
954  ResourceMap::iterator resList = _resources.find(hash);
955  if (resList == _resources.end()) {
956  // We don't have a resource with this name yet, create a new resource list for it
957 
958  std::pair<ResourceMap::iterator, bool> result;
959 
960  result = _resources.insert(std::make_pair(hash, ResourceList()));
961 
962  resList = result.first;
963  }
964 
965 #ifdef CHECK_HASH_COLLISION
966  checkHashCollision(resource, resList);
967 #endif
968 
969  // Add the resource to the list
970  resList->second.push_back(resource);
971  Resource *res = &resList->second.back();
972 
973  checkResourceIsArchive(*res, change);
974 
975  // Remember the resource in the change set
976  if (change) {
977  change->_change->resources.push_back(ResourceChange());
978  change->_change->resources.back().hashIt = resList;
979  change->_change->resources.back().resIt = --resList->second.end();
980  }
981 
982  // Resort the list by priority
983  resList->second.sort();
984 }
985 
986 void ResourceManager::addResource(const Common::UString &path, Change *change, uint32 priority) {
987  Resource res;
988  res.priority = priority;
989  res.source = kSourceFile;
990  res.path = path;
991  res.name = Common::FilePath::getStem(path);
992  res.type = TypeMan.getFileType(path);
993 
994  // Handle "small" files
995  if (_hasSmall && (res.type == kFileTypeSMALL)) {
996  const Common::UString name = res.name;
997 
998  res.isSmall = true;
999 
1000  res.name = Common::FilePath::getStem(name);
1001  res.type = TypeMan.getFileType(name);
1002  }
1003 
1004  uint64 hash = getHash(res.name, res.type);
1005  if (normalizeType(res))
1006  hash = getHash(res.name, res.type);
1007 
1008  addResource(res, hash, change);
1009 }
1010 
1011 void ResourceManager::addResources(const Common::FileList &files, Change *change, uint32 priority) {
1012  for (Common::FileList::const_iterator file = files.begin(); file != files.end(); ++file)
1013  addResource(*file, change, priority);
1014 }
1015 
1017  ResourceMap::const_iterator r = _resources.find(hash);
1018  if ((r == _resources.end()) || r->second.empty() || (r->second.back().priority == 0))
1019  return 0;
1020 
1021  return &r->second.back();
1022 }
1023 
1025  const std::vector<FileType> &types) const {
1026 
1027  const Resource *result = 0;
1028  for (std::vector<FileType>::const_iterator type = types.begin(); type != types.end(); ++type) {
1029  const Resource *res = getRes(getHash(name, *type));
1030  if (res && (!result || *result < *res))
1031  result = res;
1032  }
1033  if (!result && _hasSmall) {
1034  for (std::vector<FileType>::const_iterator type = types.begin(); type != types.end(); ++type) {
1035  Common::UString smallName = TypeMan.addFileType(TypeMan.setFileType(name, *type), kFileTypeSMALL);
1036 
1037  const Resource *res = getRes(getHash(smallName));
1038  if (res && (!result || *result < *res))
1039  result = res;
1040  }
1041  }
1042 
1043  return result;
1044 }
1045 
1047  std::vector<FileType> types(1, type);
1048 
1049  return getRes(name, types);
1050 }
1051 
1053  Common::WriteFile file;
1054 
1055  if (!file.open(fileName))
1057 
1058  file.writeString(" Name | Hash | Size \n");
1059  file.writeString("-------------------------------------|--------------------|-------------\n");
1060 
1061  for (ResourceMap::const_iterator r = _resources.begin(); r != _resources.end(); ++r) {
1062  if (r->second.empty())
1063  continue;
1064 
1065  const Resource &res = r->second.back();
1066 
1067  const Common::UString &name = res.name;
1068  const Common::UString ext = TypeMan.setFileType("", res.type);
1069  const uint64 hash = r->first;
1070  const uint32 size = getResourceSize(res);
1071 
1072  const Common::UString line =
1073  Common::UString::format("%32s%4s | %s | %12d\n", name.c_str(), ext.c_str(),
1074  Common::formatHash(hash).c_str(), size);
1075 
1076  file.writeString(line);
1077  }
1078 
1079  file.flush();
1080  file.close();
1081 }
1082 
1084  // Does this change ID already have a change set attached? If so, use that
1085  Change *change = dynamic_cast<Change *>(changeID.getContent());
1086  if (change && (change->_change != _changes.end()))
1087  return change;
1088 
1089  // Otherwise, generate a new one
1090 
1091  _changes.push_back(ChangeSet());
1092 
1093  change = new Change(--_changes.end());
1094  changeID.setContent(change);
1095 
1096  return change;
1097 }
1098 
1099 } // End of namespace Aurora
ResourceType
Definition: types.h:407
uint32 getResourceSize(const Resource &res) const
Definition: resman.cpp:703
ChangeSetList::iterator _change
Definition: resman.h:458
Compressed file, Nintendo LZSS.
Definition: types.h:257
FileTypeList _resourceTypeTypes[kResourceMAX]
All valid resource type file types.
Definition: resman.h:494
bool hasResourceDir(const Common::UString &dir)
Does a specific directory, relative to the base directory, exist?
Definition: resman.cpp:473
void addResources(const Common::FileList &files, Change *change, uint32 priority)
Definition: resman.cpp:1011
Packed layer texture.
Definition: types.h:63
Source source
Where can the resource be found?
Definition: resman.h:410
#define TypeMan
Shortcut for accessing the file type manager.
Definition: util.h:85
Definition: 2dafile.h:39
Class to hold resource data information of a BIF file.
Definition: biffile.h:71
EXE archive.
Definition: types.h:422
Video, Windows media.
Definition: types.h:69
std::vector< Common::UString > BIFList
Definition: keyfile.h:82
A class holding an UTF-8 string.
Definition: ustring.h:48
static void decompress(Common::ReadStream &small, Common::WriteStream &out)
Definition: smallfile.cpp:255
64bit Fowler-Noll-Vo hash by Glenn Fowler, Landon Curt Noll and Phong Vo.
Definition: hash.h:41
void writeString(const UString &str)
Write the given string to the stream, encoded as UTF-8.
Definition: writestream.cpp:94
void reset(PointerType o=0)
Resets the pointer with the new value.
Definition: scopedptr.h:87
Game resource data.
Definition: types.h:208
static UString getExtension(const UString &p)
Return a file name&#39;s extension.
Definition: filepath.cpp:93
void setHasSmall(bool hasSmall)
Do we have "small" files (compressed with Nintendo DS&#39;s LZSS algorithm)?
Definition: resman.cpp:222
PointerType release()
Returns the plain pointer value and releases ScopedPtr.
Definition: scopedptr.h:103
FileType type
The resource&#39;s type.
Definition: resman.h:398
Handling BioWare&#39;s RIMs (resource archives).
uint64_t uint64
Definition: types.h:206
Common::SeekableReadStream * openArchiveStream(const KnownArchive &archive) const
Definition: resman.cpp:296
uint32 archiveIndex
Index into the archive.
Definition: resman.h:417
const Resource * getRes(uint64 hash) const
Definition: resman.cpp:1016
static bool isDirectory(const UString &p)
Does specified path exist and is it a directory?
Definition: filepath.cpp:56
void indexResourceDir(const Common::UString &dir, const char *glob, int depth, uint32 priority, Common::ChangeID *changeID=0)
Add a directory&#39;s contents to the resource manager.
Definition: resman.cpp:497
bool equalsIgnoreCase(const UString &str) const
Definition: ustring.cpp:218
Game resource index.
Definition: types.h:209
A simple streaming file reading class.
Definition: readfile.h:40
Module resources, RIM.
Definition: types.h:178
Treat Nintendo NSBTX files, which contain multiple textures as an archive of intermediate textures...
A set of changes produced by a manager operation.
Definition: resman.h:446
Utility functions to handle files used in BioWare&#39;s Aurora engine.
Class to hold resource data of an HERF archive file.
Definition: herffile.h:53
Image, JPEG.
Definition: types.h:142
std::list< UString >::const_iterator const_iterator
Definition: filelist.h:37
virtual const ResourceList & getResources() const =0
Return the list of resources.
A ZIP archive.
Texture.
Definition: types.h:182
Geometry, model mesh data.
Definition: types.h:184
std::vector< Common::UString > _cursorRemap
Cursor ID -> cursor name.
Definition: resman.h:477
A change produced by indexing archive resources.
Definition: resman.h:436
KnownArchives _knownArchives[kArchiveMAX]
List of all known archives.
Definition: resman.h:484
ChangeContent * getContent() const
Definition: changeid.cpp:56
void clear()
Clear all resource information.
Definition: resman.cpp:177
void dumpResourcesList(const Common::UString &fileName) const
Dump a list of all resources into a file.
Definition: resman.cpp:1052
Class to hold resource index information of a KEY file.
Definition: keyfile.h:70
std::list< Resource > ResourceList
List of resources, sorted by priority.
Definition: resman.h:425
Quest, GFF.
Definition: types.h:188
ChangeSetList _changes
Changes produced by indexing the currently known resources.
Definition: resman.h:491
Face bone definitions, FaceFX Actor.
Definition: types.h:223
Module resources.
Definition: types.h:207
Exception that provides a stack of explanations.
Definition: error.h:36
Archive * archive
The actual archive.
Definition: resman.h:366
ZIP archive.
Definition: types.h:421
Class to hold resource data information of a BZF file.
Definition: bzffile.h:55
Cursor, Windows cursor.
Definition: types.h:286
A simple scoped smart pointer template.
Image, Portable Network Graphics.
Definition: types.h:175
Video, MPEG.
Definition: types.h:66
BIF archive.
Definition: types.h:418
static UString formatHash(uint64 hash)
Definition: hash.h:261
void set(KnownArchive &kA, Archive &a)
Definition: resman.cpp:74
bool isSmall
Is this a "small" (compressed Nintendo DS) file?
Definition: resman.h:401
void setCursorRemap(const std::vector< Common::UString > &remap)
Set the array used to map cursor ID to cursor names.
Definition: resman.cpp:233
void indexKEY(Common::SeekableReadStream *stream, uint32 priority, Change *change)
Definition: resman.cpp:406
KEY archive.
Definition: types.h:417
Handling BioWare&#39;s KEYs (resource index files).
Basic exceptions to throw.
Font, character bitmap data.
Definition: types.h:335
bool normalizeType(Resource &resource)
Definition: resman.cpp:866
const char * c_str() const
Return the (utf8 encoded) string data.
Definition: ustring.cpp:249
Geometry, model mesh data.
Definition: types.h:191
bool open(const UString &fileName)
Try to open the file with the given fileName.
Definition: writefile.cpp:50
virtual uint32 getResourceSize(uint32 index) const
Return the size of a resource.
Definition: archive.cpp:40
Handling BioWare&#39;s BIFs (resource data files).
static UString format(const char *s,...) GCC_PRINTF(1
Print formatted data into an UString object, similar to sprintf().
Definition: ustring.cpp:718
Video, Actimagine.
Definition: types.h:280
ArchiveType
Definition: types.h:416
static bool isRegularFile(const UString &p)
Does specified path exist and is it a regular file?
Definition: filepath.cpp:52
Common::HashAlgo _hashAlgo
With which hash algorithm are/should the names be hashed?
Definition: resman.h:474
void addResource(Resource &resource, uint64 hash, Change *change)
Definition: resman.cpp:953
Utility templates and functions.
Handling BioWare&#39;s BZFs (resource data files), used in the iOS version of Knights of the Old Republic...
void registerDataBase(const Common::UString &path)
Register a path to be the data base.
Definition: resman.cpp:237
#define DECLARE_SINGLETON(T)
Note that you need to use this macro from the global namespace.
Definition: singleton.h:122
FileTypeSet _archiveTypeTypes[kArchiveMAX]
All valid archive types file types.
Definition: resman.h:493
An image resource.
Definition: types.h:408
std::list< Resource > ResourceList
Definition: archive.h:57
void blacklist(const Common::UString &name, FileType type)
Blacklist a specific resource.
Definition: resman.cpp:612
Common::UString findResourceFile(const Common::UString &name, FileType type) const
Find and return the absolute filesystem file behind a resource.
Definition: resman.cpp:676
bool _hasSmall
Do we have "small" files?
Definition: resman.h:471
Image, Windows bitmap.
Definition: types.h:59
std::map< FileType, FileType > _typeAliases
The current type aliases, changing one type to another.
Definition: resman.h:488
bool hasResource(uint64 hash) const
Does a specific resource exist?
Definition: resman.cpp:672
BioWare&#39;s HERF (hashed ERF) file parsing.
Handling BioWare&#39;s ERFs (encapsulated resource file).
Audio, Ogg Vorbis.
Definition: types.h:144
Game resource data, LZMA-compressed BIF.
Definition: types.h:368
static UString canonicalize(const UString &p, bool resolveSymLinks=true)
Return the canonical, absolutized and normalized path.
Definition: filepath.cpp:230
virtual Common::HashAlgo getNameHashAlgo() const
Return with which algorithm the name is hashed.
Definition: archive.cpp:44
bool empty() const
Is the string empty?
Definition: ustring.cpp:245
ArchiveType getArchiveType(FileType type) const
Definition: resman.cpp:837
HashAlgo
The algorithm used for hashing.
Definition: hash.h:37
void indexArchive(const Common::UString &file, uint32 priority, Common::ChangeID *changeID=0)
Add all the resources of an archive to the resource manager.
Definition: resman.cpp:361
Geometry, BioWare model.
Definition: types.h:136
Archive, Nintendo DS ROM file.
Definition: types.h:254
Common::UString getArchiveName(const Resource &resource) const
Definition: resman.cpp:849
void setContent(ChangeContent *content)
Definition: changeid.cpp:60
StackException Exception
Definition: error.h:59
static uint64 hashString(const UString &string, HashAlgo algo)
Hash the string with the given algorithm, as a series of UTF-8 characters.
Definition: hash.h:218
Texture.
Definition: types.h:183
A scoped plain pointer, allowing pointer-y access and normal deletion.
Definition: scopedptr.h:120
Common::UString name
The resource&#39;s name.
Definition: resman.h:397
Cursor, Mac CURS format.
Definition: types.h:325
void warning(const char *s,...)
Definition: util.cpp:33
Intermediate texture.
Definition: types.h:390
uint64 getHash(const Common::UString &name, FileType type) const
Definition: resman.cpp:895
Basic reading stream interfaces.
Resource hak pak, ERF.
Definition: types.h:127
Quest, GFF.
Definition: types.h:150
ResourceMap _resources
All currently known resources.
Definition: resman.h:490
std::list< KnownArchive > KnownArchives
List of all known archive files.
Definition: resman.h:382
bool hasArchive(const Common::UString &file)
Does a specific archive exist?
Definition: resman.cpp:292
void flush()
Commit any buffered data to the underlying channel or storage medium; unbuffered streams can use the ...
Definition: writefile.cpp:83
Video, QuickTime/MPEG-4.
Definition: types.h:324
Nintendo DS ROM parsing.
Windows PE EXE file.
Definition: types.h:217
Implementing the stream reading interfaces for files.
const BIFList & getBIFs() const
Return a list of all managed bifs.
Definition: keyfile.cpp:141
Video, RAD Game Tools Bink.
Definition: types.h:129
A video resource.
Definition: types.h:409
KnownArchive * findArchive(const Common::UString &file)
Definition: resman.cpp:267
A sound resource.
Definition: types.h:410
void close()
Close the file, if open.
Definition: writefile.cpp:69
Common::UString path
The file&#39;s path.
Definition: resman.h:413
void indexResourceFile(const Common::UString &file, uint32 priority, Common::ChangeID *changeID=0)
Add a single file to the resource manager.
Definition: resman.cpp:480
static UString getStem(const UString &p)
Return a file name&#39;s stem.
Definition: filepath.cpp:87
UString toLower() const
Return a lowercased copy of the string.
Definition: ustring.cpp:481
Neverwinter Nights original campaign module, ERF.
Definition: types.h:128
RIM archive.
Definition: types.h:420
An abstract KEY data file (BIF or BZF).
Common::SeekableReadStream * getResource(uint64 hash, FileType *type=0) const
Return a resource.
Definition: resman.cpp:750
Decompressing "small" files, Nintendo DS LZSS (types 0x00 and 0x10), found in Sonic.
uint32 openKEYBIFs(Common::SeekableReadStream *keyStream, std::vector< KnownArchive *> &archives, std::vector< KEYDataFile *> &keyData)
Definition: resman.cpp:367
A class representing an undoable change.
Definition: changeid.h:35
static bool isAbsolute(const UString &p)
Is the given string an absolute path?
Definition: filepath.cpp:113
NSBTX texture archives.
Definition: types.h:425
Class to hold resource data of an ERF archive file.
Definition: erffile.h:105
A portable executable archive.
uint32_t uint32
Definition: types.h:204
A list of files.
Definition: filelist.h:35
Module, ERF.
Definition: types.h:80
HERF archive.
Definition: types.h:424
static size_t getFileSize(const UString &p)
Return a file&#39;s size.
Definition: filepath.cpp:60
Audio, Windows media.
Definition: types.h:68
static UString normalize(const UString &p, bool resolveSymLinks=true)
Normalize a path.
Definition: filepath.cpp:154
bool operator<(const Resource &right) const
Definition: resman.cpp:104
virtual Common::SeekableReadStream * getResource(uint32 index, bool tryNoCopy=false) const =0
Return a stream of the resource&#39;s contents.
FileType
Various file types used by the Aurora engine and found in archives.
Definition: types.h:56
Image, Truevision TARGA image.
Definition: types.h:61
Implementing the stream writing interfaces for files.
uint32 priority
The resource&#39;s priority over others with the same name and type.
Definition: resman.h:404
A music resource.
Definition: types.h:411
Common::UString _baseArchive
The data base archive (if any), the archive the current game is in.
Definition: resman.h:482
const_iterator begin() const
Return a const_iterator pointing to the beginning of the list.
Definition: filelist.cpp:94
bool addDirectory(const UString &directory, int recurseDepth=0)
Add a directory to the list.
Definition: filelist.cpp:102
Class to hold resource data of a RIM archive file.
Definition: rimfile.h:61
Video, Xbox.
Definition: types.h:70
Resource * resource
The resource this archive is.
Definition: resman.h:355
A class encapsulating Nintendo DS ROM access.
Definition: ndsrom.h:45
Nintendo DS ROM.
Definition: types.h:423
An abstract file archive.
Definition: archive.h:45
Archive, hashed ERF.
Definition: types.h:255
const Common::UString & getDataBase() const
Return the path of the currently registered base data directory or archive.
Definition: resman.cpp:260
ERF archive.
Definition: types.h:419
No hashing at all.
Definition: hash.h:38
A resource manager holding information about and handling all request for all resources usable by the...
Definition: resman.h:55
const Exception kOpenError("Can't open file")
Exception when a file couldn&#39;t be opened.
Definition: error.h:61
std::pair< KnownArchives *, KnownArchives::iterator > selfArchive
The archive this resource itself is.
Definition: resman.h:407
void undo(Common::ChangeID &changeID)
Undo the changes done in the specified change ID.
Definition: resman.cpp:529
void setRIMsAreERFs(bool rimsAreERFs)
Are .rim/.rimp files actually ERF files?
Definition: resman.cpp:205
A simple streaming file writing class.
Definition: writefile.h:40
void addTypeAlias(FileType alias, FileType realType)
Add an alias for one file type to another.
Definition: resman.cpp:608
Common::UString _baseDir
The data base directory (if any), the directory the current game is in.
Definition: resman.h:480
Common::SeekableReadStream * getArchiveResource(const Resource &res, bool tryNoCopy=false) const
Definition: resman.cpp:717
void clear()
Clear the string&#39;s contents.
Definition: ustring.cpp:236
bool getSubListGlob(const UString &glob, bool caseInsensitive, FileList &subList) const
Add files matching the given regex into another FileList.
Definition: filelist.cpp:167
OpenedArchives _openedArchives
List of currently used archives.
Definition: resman.h:485
ArchiveType type
The archive&#39;s type.
Definition: resman.h:352
Interface for a seekable & readable data stream.
Definition: readstream.h:265
void getAvailableResources(FileType type, std::list< ResourceID > &list) const
Return a list of all available resources of the specified type.
Definition: resman.cpp:800
void declareResource(const Common::UString &name, FileType type)
Declare the name of a specific resource.
Definition: resman.cpp:621
void setHashAlgo(Common::HashAlgo algo)
With which hash algorithm are/should the names be hashed?
Definition: resman.cpp:226
Texture, DirectDraw Surface.
Definition: types.h:98
A cursor resource.
Definition: types.h:412
Audio, Waveform.
Definition: types.h:62
bool checkResourceIsArchive(Resource &resource, Change *change)
Definition: resman.cpp:922
The global resource manager for Aurora resources.
Utility class for manipulating file paths.
Change * newChangeSet(Common::ChangeID &changeID)
Definition: resman.cpp:1083
Tree data SpeedTree.
Definition: types.h:145
static UString changeExtension(const UString &p, const UString &ext="")
Change a file name&#39;s extension.
Definition: filepath.cpp:99
A class encapsulating PE exe&#39;s for resource archive access.
Definition: pefile.h:45
const_iterator end() const
Return a const_iterator pointing past the end of the list.
Definition: filelist.cpp:98
void checkHashCollision(const Resource &resource, ResourceMap::const_iterator resList)
Definition: resman.cpp:903
static UString findSubDirectory(const UString &directory, const UString &subDirectory, bool caseInsensitive=false)
Find a directory&#39;s subdirectory.
Definition: filepath.cpp:318
OpenedArchive * archive
Pointer to the opened archive.
Definition: resman.h:416
A class encapsulating ZIP files for resource archive access.
Definition: zipfile.h:42
Audio, MP3 with extra header.
Definition: types.h:65