Ticket #8904: kyra_resource_v1.patch
File kyra_resource_v1.patch, 78.6 KB (added by , 16 years ago) |
---|
-
module.mk
23 23 kyra_mr.o \ 24 24 lol.o \ 25 25 resource.o \ 26 resource_intern.o \ 26 27 saveload.o \ 27 28 saveload_lok.o \ 28 29 saveload_hof.o \ -
resource.cpp
31 31 #include "common/func.h" 32 32 33 33 #include "kyra/resource.h" 34 #include "kyra/resource_intern.h" 34 35 35 36 namespace Kyra { 36 37 37 Resource::Resource(KyraEngine_v1 *vm) : _ loaders(), _map(), _vm(vm) {38 Resource::Resource(KyraEngine_v1 *vm) : _archiveCache(), _files(), _archiveFiles(new Common::SearchSet()), _protectedFiles(new Common::SearchSet()), _loaders(), _vm(vm) { 38 39 initializeLoaders(); 40 41 Common::SharedPtr<Common::Archive> path(new Common::FSDirectory(ConfMan.get("path"))); 42 Common::SharedPtr<Common::Archive> extrapath(new Common::FSDirectory(ConfMan.get("extrapath"))); 43 _files.add("path", path, 3); 44 _files.add("extrapath", extrapath, 3); 45 // compressed installer archives are added at level '2', 46 // but that's done in Resource::reset not here 47 _files.add("protected", _protectedFiles, 1); 48 _files.add("archives", _archiveFiles, 0); 39 49 } 40 50 41 51 Resource::~Resource() { 42 _map.clear();43 52 _loaders.clear(); 44 45 clearCompFileList();46 _compLoaders.clear();47 53 } 48 54 49 55 bool Resource::reset() { 50 clearCompFileList();51 56 unloadAllPakFiles(); 52 57 53 58 Common::FilesystemNode dir(ConfMan.get("path")); … … 71 76 loadPakFile("CHAPTER1.VRM"); 72 77 } else if (_vm->game() == GI_KYRA2) { 73 78 if (_vm->gameFlags().useInstallerPackage) 74 tryLoadCompFiles();79 _files.add("installer", loadInstallerArchive("WESTWOOD", "%03d", 6), 2); 75 80 76 81 // mouse pointer, fonts, etc. required for initializing 77 82 if (_vm->gameFlags().isDemo && !_vm->gameFlags().isTalkie) { 78 83 loadPakFile("GENERAL.PAK"); 79 84 } else { 80 if (_vm->gameFlags().isTalkie) {81 // Add default file directories82 Common::File::addDefaultDirectory(ConfMan.get("path") + "hof_cd");83 Common::File::addDefaultDirectory(ConfMan.get("path") + "HOF_CD");84 }85 86 85 loadPakFile("INTROGEN.PAK"); 87 86 loadPakFile("OTHER.PAK"); 88 87 } … … 94 93 error("couldn't load file: 'WESTWOOD.001'"); 95 94 } 96 95 97 // Add default file directories98 Common::File::addDefaultDirectory(ConfMan.get("path") + "malcolm");99 Common::File::addDefaultDirectory(ConfMan.get("path") + "MALCOLM");100 101 96 if (!loadFileList("FILEDATA.FDT")) 102 97 error("couldn't load file: 'FILEDATA.FDT'"); 103 98 104 99 return true; 105 100 } else if (_vm->game() == GI_LOL) { 106 101 if (_vm->gameFlags().useInstallerPackage) 107 tryLoadCompFiles();102 _files.add("installer", loadInstallerArchive("WESTWOOD", "%d", 0), 2); 108 103 109 104 return true; 110 105 } … … 120 115 "CAVE.APK", "DRAGON1.APK", "DRAGON2.APK", "LAGOON.APK" 121 116 }; 122 117 123 Common::for_each(list, list + ARRAYSIZE(list), Common::bind1st(Common::mem_fun(&Resource::loadPakFile), this)); 124 125 for (int i = 0; i < ARRAYSIZE(list); ++i) { 126 ResFileMap::iterator iterator = _map.find(list[i]); 127 if (iterator != _map.end()) 128 iterator->_value.prot = true; 118 for (uint i = 0; i < ARRAYSIZE(list); ++i) { 119 Common::ArchivePtr archive = loadArchive(list[i]); 120 _protectedFiles->add(list[i], archive, 0); 129 121 } 130 122 } else { 131 123 for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) { … … 149 141 return true; 150 142 } 151 143 152 bool Resource::loadPakFile(const Common::String &filename) { 153 if (!isAccessible(filename)) 154 return false; 144 bool Resource::loadPakFile(Common::String filename) { 145 filename.toUppercase(); 155 146 156 ResFileMap::iterator iter = _map.find(filename); 157 if (iter == _map.end()) 158 return false; 159 160 if (iter->_value.preload) { 161 iter->_value.mounted = true; 147 if (_archiveFiles->hasArchive(filename) || _protectedFiles->hasArchive(filename)) 162 148 return true; 163 }164 149 165 const ResArchiveLoader *loader = getLoader(iter->_value.type); 166 if (!loader) { 167 error("no archive loader for file '%s' found which is of type %d", filename.c_str(), iter->_value.type); 150 Common::ArchivePtr archive = loadArchive(filename); 151 if (!archive) 168 152 return false; 169 }170 153 171 Common::SeekableReadStream *stream = getFileStream(filename); 172 if (!stream) { 173 error("archive file '%s' not found", filename.c_str()); 174 return false; 175 } 154 _archiveFiles->add(filename, archive, 0); 176 155 177 iter->_value.mounted = true;178 iter->_value.preload = true;179 ResArchiveLoader::FileList files;180 loader->loadFile(filename, *stream, files);181 delete stream;182 stream = 0;183 184 for (ResArchiveLoader::FileList::iterator i = files.begin(); i != files.end(); ++i) {185 iter = _map.find(i->filename);186 if (iter == _map.end()) {187 // We do an internal check for a file in gamepath with same filename to188 // allow overwriting files inside archives with plain files inside the189 // game directory190 checkFile(i->filename);191 192 // A new file entry, so we just insert it into the file map.193 if (_map.find(i->filename) == _map.end())194 _map[i->filename] = i->entry;195 } else if (!iter->_value.parent.empty()) {196 if (!iter->_value.parent.equalsIgnoreCase(filename)) {197 ResFileMap::iterator oldParent = _map.find(iter->_value.parent);198 if (oldParent != _map.end()) {199 // Protected files and their embedded file entries do not get overwritten.200 if (!oldParent->_value.prot) {201 // If the old parent is not protected we mark it as not preload anymore,202 // since now no longer all of its embedded files are in the filemap.203 oldParent->_value.preload = false;204 iter->_value = i->entry;205 }206 } else {207 // Old parent not found? That's strange... But we just overwrite the old208 // entry.209 iter->_value = i->entry;210 }211 } else {212 // The old parent has the same filenames as the new archive, we are sure and overwrite the213 // old file entry, could be afterall that the preload flag of the new archive was214 // just unflagged.215 iter->_value = i->entry;216 }217 }218 // 'else' case would mean here overwriting an existing file entry in the map without parent.219 // We don't support that though, so one can overwrite files from archives by putting220 // them in the gamepath.221 }222 223 detectFileTypes();224 156 return true; 225 157 } 226 158 … … 240 172 buffer[12] = 0; 241 173 f.seek(offset + 16, SEEK_SET); 242 174 243 Common::String filename = Common::String((char *)buffer);175 Common::String filename = Common::String((char *)buffer); 244 176 filename.toUppercase(); 245 177 246 178 if (filename.hasSuffix(".PAK")) { 247 if (! isAccessible(filename) && _vm->gameFlags().isDemo) {179 if (!exists(filename.c_str()) && _vm->gameFlags().isDemo) { 248 180 // the demo version supplied with Kyra3 does not 249 181 // contain all pak files listed in filedata.fdt 250 182 // so we don't do anything here if they are non … … 273 205 return true; 274 206 } 275 207 276 void Resource::unloadPakFile(const Common::String &filename) { 277 ResFileMap::iterator iter = _map.find(filename); 278 if (iter != _map.end()) { 279 if (!iter->_value.prot) 280 iter->_value.mounted = false; 281 } 208 void Resource::unloadPakFile(Common::String filename) { 209 filename.toUppercase(); 210 _archiveFiles->remove(filename); 211 _protectedFiles->remove(filename); 282 212 } 283 213 284 void Resource::clearCompFileList() { 285 for (CompFileMap::iterator i = _compFiles.begin(); i != _compFiles.end(); ++i) 286 delete[] i->_value.data; 287 288 _compFiles.clear(); 214 bool Resource::isInPakList(Common::String filename) { 215 filename.toUppercase(); 216 return (_archiveFiles->hasArchive(filename) || _protectedFiles->hasArchive(filename)); 289 217 } 290 218 291 bool Resource::isInPakList(const Common::String &filename) {292 if (!isAccessible(filename))293 return false;294 ResFileMap::iterator iter = _map.find(filename);295 if (iter == _map.end())296 return false;297 return (iter->_value.type != ResFileEntry::kRaw);298 }299 300 219 void Resource::unloadAllPakFiles() { 301 // remove all entries302 _ map.clear();220 _archiveFiles->clear(); 221 _protectedFiles->clear(); 303 222 } 304 223 305 224 uint8 *Resource::fileData(const char *file, uint32 *size) { … … 318 237 } 319 238 320 239 bool Resource::exists(const char *file, bool errorOutOnFail) { 321 if ( Common::File::exists(file))240 if (_files.hasFile(file)) 322 241 return true; 323 else if (isAccessible(file))324 return true;325 242 else if (errorOutOnFail) 326 243 error("File '%s' can't be found", file); 327 244 return false; 328 245 } 329 246 330 247 uint32 Resource::getFileSize(const char *file) { 331 CompFileMap::iterator compEntry; 248 Common::SeekableReadStream *stream = getFileStream(file); 249 if (!stream) 250 return 0; 332 251 333 if (Common::File::exists(file)) { 334 Common::File f; 335 if (f.open(file)) 336 return f.size(); 337 } else { 338 if (!isAccessible(file)) 339 return 0; 340 341 ResFileMap::const_iterator iter = _map.find(file); 342 if (iter != _map.end()) 343 return iter->_value.size; 344 } 345 return 0; 252 uint32 size = stream->size(); 253 delete stream; 254 return size; 346 255 } 347 256 348 257 bool Resource::loadFileToBuf(const char *file, void *buf, uint32 maxSize) { … … 357 266 } 358 267 359 268 Common::SeekableReadStream *Resource::getFileStream(const Common::String &file) { 360 CompFileMap::iterator compEntry; 361 362 if ((compEntry = _compFiles.find(file)) != _compFiles.end()) 363 return new Common::MemoryReadStream(compEntry->_value.data, compEntry->_value.size, false); 364 365 if (!isAccessible(file)) 366 return 0; 367 368 ResFileMap::const_iterator iter = _map.find(file); 369 if (iter == _map.end()) 370 return 0; 371 372 if (iter->_value.parent.empty()) { 373 Common::File *stream = new Common::File(); 374 if (!stream->open(file)) { 375 delete stream; 376 stream = 0; 377 error("Couldn't open file '%s'", file.c_str()); 378 } 379 return stream; 380 } else { 381 Common::SeekableReadStream *parent = getFileStream(iter->_value.parent); 382 assert(parent); 383 384 ResFileEntry* parentEntry = getParentEntry(&iter->_value); 385 const ResArchiveLoader *loader = getLoader(parentEntry->type); 386 assert(loader); 387 388 return loader->loadFileFromArchive(file, parent, iter->_value); 389 } 390 391 return 0; 269 return _files.openFile(file); 392 270 } 393 271 394 bool Resource::isAccessible(const Common::String &file) { 395 checkFile(file); 272 Common::ArchivePtr Resource::loadArchive(const Common::String &file) { 273 ArchiveMap::iterator cachedArchive = _archiveCache.find(file); 274 if (cachedArchive != _archiveCache.end()) 275 return cachedArchive->_value; 396 276 397 ResFileMap::const_iterator iter = _map.find(file); 398 if (iter == _map.end()) 399 return false; 400 401 return isAccessible(&iter->_value); 402 } 277 Common::SeekableReadStream *stream = getFileStream(file); 278 if (!stream) 279 return Common::ArchivePtr(); 403 280 404 bool Resource::isAccessible(const ResFileEntry *fileEntry) { 405 assert(fileEntry); 406 407 const ResFileEntry* currentEntry = fileEntry; 408 while (!currentEntry->parent.empty()) { 409 if (currentEntry->parentEntry) { 410 currentEntry = currentEntry->parentEntry; 411 } else { 412 ResFileMap::iterator it = _map.find(currentEntry->parent); 413 if (it == _map.end()) 414 return false; 415 else 416 currentEntry->parentEntry = &it->_value; 417 } 418 // parent can never be a non archive file 419 if (currentEntry->type == ResFileEntry::kRaw) 420 return false; 421 // not mounted parent means not accessable 422 else if (!currentEntry->mounted) 423 return false; 424 } 425 426 return true; 427 } 428 429 ResFileEntry *Resource::getParentEntry(const ResFileEntry *entry) const { 430 assert(entry); 431 if (entry->parent.empty()) { 432 return 0; 433 } else if (entry->parentEntry) { 434 assert(_map.find(entry->parent) != _map.end()); // If some day the hash map implementations changes and moves nodes around, 435 // this assumption would fail and the whole system would need a refactoring 436 assert(entry->parentEntry == &_map.find(entry->parent)->_value); 437 return entry->parentEntry; 438 } else { 439 ResFileMap::iterator it = _map.find(entry->parent); 440 if (it == _map.end()) 441 return 0; // If it happens often, the structure maybe deserves a flag to avoid rechecking the map 442 else { 443 entry->parentEntry = &it->_value; 444 return entry->parentEntry; 445 } 446 } 447 } 448 449 ResFileEntry *Resource::getParentEntry(const Common::String &filename) const { 450 ResFileMap::iterator it = _map.find(filename); 451 assert(it != _map.end()); 452 return getParentEntry(&it->_value); 453 } 454 455 void Resource::checkFile(const Common::String &file) { 456 if (_map.find(file) == _map.end()) { 457 CompFileMap::const_iterator iter; 458 459 if ((iter = _compFiles.find(file)) != _compFiles.end()) { 460 ResFileEntry& entry = _map[file]; 461 entry.parent = ""; 462 entry.parentEntry = 0; 463 entry.size = iter->_value.size; 464 entry.mounted = false; 465 entry.preload = false; 466 entry.prot = false; 467 entry.type = ResFileEntry::kAutoDetect; 468 entry.offset = 0; 469 470 detectFileType(file, &entry); 471 } else if (Common::File::exists(file)) { 472 Common::File temp; 473 if (temp.open(file)) { 474 ResFileEntry& entry = _map[file]; 475 entry.parent = ""; 476 entry.parentEntry = 0; 477 entry.size = temp.size(); 478 entry.mounted = file.compareToIgnoreCase(StaticResource::staticDataFilename()) != 0; 479 entry.preload = false; 480 entry.prot = false; 481 entry.type = ResFileEntry::kAutoDetect; 482 entry.offset = 0; 483 temp.close(); 484 485 detectFileType(file, &entry); 486 } 487 } 488 } 489 } 490 491 void Resource::detectFileType(const Common::String &filename, ResFileEntry *fileEntry) { 492 assert(fileEntry); 493 494 if (!isAccessible(fileEntry)) 495 return; 496 497 if (fileEntry->type == ResFileEntry::kAutoDetect) { 498 Common::SeekableReadStream *stream = 0; 499 for (LoaderIterator l = _loaders.begin(); l != _loaders.end(); ++l) { 500 if (!(*l)->checkFilename(filename)) 501 continue; 502 503 if (!stream) 504 stream = getFileStream(filename); 505 506 if ((*l)->isLoadable(filename, *stream)) { 507 fileEntry->type = (*l)->getType(); 508 fileEntry->mounted = false; 509 fileEntry->preload = false; 281 Common::ArchivePtr archive; 282 for (LoaderList::const_iterator i = _loaders.begin(); i != _loaders.end(); ++i) { 283 if ((*i)->checkFilename(file)) { 284 if ((*i)->isLoadable(file, *stream)) { 285 stream->seek(0, SEEK_SET); 286 archive = Common::ArchivePtr((*i)->load(this, file, *stream)); 510 287 break; 511 }512 }513 delete stream;514 stream = 0;515 516 if (fileEntry->type == ResFileEntry::kAutoDetect)517 fileEntry->type = ResFileEntry::kRaw;518 }519 }520 521 void Resource::detectFileTypes() {522 for (ResFileMap::iterator i = _map.begin(); i != _map.end(); ++i)523 detectFileType(i->_key, &i->_value);524 }525 526 void Resource::tryLoadCompFiles() {527 for (CCompLoaderIterator i = _compLoaders.begin(); i != _compLoaders.end(); ++i) {528 if ((*i)->checkForFiles())529 (*i)->loadFile(_compFiles);530 }531 }532 533 #pragma mark -534 #pragma mark - ResFileLodaer535 #pragma mark -536 537 class ResLoaderPak : public ResArchiveLoader {538 public:539 bool checkFilename(Common::String filename) const;540 bool isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const;541 bool loadFile(const Common::String &filename, Common::SeekableReadStream &stream, FileList &files) const;542 Common::SeekableReadStream *loadFileFromArchive(const Common::String &file, Common::SeekableReadStream *archive, const ResFileEntry entry) const;543 544 ResFileEntry::kType getType() const {545 return ResFileEntry::kPak;546 }547 };548 549 bool ResLoaderPak::checkFilename(Common::String filename) const {550 filename.toUppercase();551 return (filename.hasSuffix(".PAK") || filename.hasSuffix(".APK") || filename.hasSuffix(".VRM") || filename.hasSuffix(".TLK") || filename.equalsIgnoreCase(StaticResource::staticDataFilename()));552 }553 554 bool ResLoaderPak::isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const {555 uint32 filesize = stream.size();556 uint32 offset = 0;557 bool switchEndian = false;558 bool firstFile = true;559 560 offset = stream.readUint32LE();561 if (offset > filesize) {562 switchEndian = true;563 offset = SWAP_BYTES_32(offset);564 }565 566 Common::String file = "";567 while (!stream.eos()) {568 // The start offset of a file should never be in the filelist569 if (offset < stream.pos() || offset > filesize)570 return false;571 572 byte c = 0;573 574 file = "";575 576 while (!stream.eos() && (c = stream.readByte()) != 0)577 file += c;578 579 if (stream.eos())580 return false;581 582 // Quit now if we encounter an empty string583 if (file.empty()) {584 if (firstFile)585 return false;586 else587 break;588 }589 590 firstFile = false;591 offset = switchEndian ? stream.readUint32BE() : stream.readUint32LE();592 593 if (!offset || offset == filesize)594 break;595 }596 597 return true;598 }599 600 namespace {601 602 Common::String readString(Common::SeekableReadStream &stream) {603 Common::String result;604 char c = 0;605 606 while ((c = stream.readByte()) != 0)607 result += c;608 609 return result;610 }611 612 } // end of anonymous namespace613 614 bool ResLoaderPak::loadFile(const Common::String &filename, Common::SeekableReadStream &stream, FileList &files) const {615 uint32 filesize = stream.size();616 617 uint32 startoffset = 0, endoffset = 0;618 bool switchEndian = false;619 bool firstFile = true;620 621 startoffset = stream.readUint32LE();622 if (startoffset > filesize) {623 switchEndian = true;624 startoffset = SWAP_BYTES_32(startoffset);625 }626 627 Common::String file = "";628 while (!stream.eos()) {629 // The start offset of a file should never be in the filelist630 if (startoffset < stream.pos() || startoffset > filesize) {631 warning("PAK file '%s' is corrupted", filename.c_str());632 return false;633 }634 635 file = "";636 byte c = 0;637 638 while (!stream.eos() && (c = stream.readByte()) != 0)639 file += c;640 641 if (stream.eos()) {642 warning("PAK file '%s' is corrupted", filename.c_str());643 return false;644 }645 646 // Quit now if we encounter an empty string647 if (file.empty()) {648 if (firstFile) {649 warning("PAK file '%s' is corrupted", filename.c_str());650 return false;651 288 } else { 652 break;289 stream->seek(0, SEEK_SET); 653 290 } 654 291 } 655 656 firstFile = false;657 endoffset = switchEndian ? stream.readUint32BE() : stream.readUint32LE();658 659 if (!endoffset)660 endoffset = filesize;661 662 if (startoffset != endoffset) {663 ResFileEntry entry;664 entry.size = endoffset - startoffset;665 entry.offset = startoffset;666 entry.parent = filename;667 entry.parentEntry = 0;668 entry.type = ResFileEntry::kAutoDetect;669 entry.mounted = false;670 entry.prot = false;671 entry.preload = false;672 673 files.push_back(File(file, entry));674 }675 676 if (endoffset == filesize)677 break;678 679 startoffset = endoffset;680 292 } 681 293 682 FileList::const_iterator iter = Common::find(files.begin(), files.end(), Common::String("LINKLIST")); 683 if (iter != files.end()) { 684 stream.seek(iter->entry.offset, SEEK_SET); 294 delete stream; 685 295 686 uint32 magic = stream.readUint32BE(); 296 if (!archive) 297 return Common::ArchivePtr(); 687 298 688 if (magic != MKID_BE('SCVM')) 689 error("LINKLIST file does not contain 'SCVM' header"); 690 691 uint32 links = stream.readUint32BE(); 692 for (uint i = 0; i < links; ++i) { 693 Common::String linksTo = readString(stream); 694 uint32 sources = stream.readUint32BE(); 695 696 iter = Common::find(files.begin(), files.end(), linksTo); 697 if (iter == files.end()) 698 error("PAK file link destination '%s' not found", linksTo.c_str()); 699 700 for (uint j = 0; j < sources; ++j) { 701 Common::String dest = readString(stream); 702 files.push_back(File(dest, iter->entry)); 703 // Better safe than sorry, we update the 'iter' value, in case push_back invalidated it 704 iter = Common::find(files.begin(), files.end(), linksTo); 705 } 706 } 707 } 708 709 return true; 299 _archiveCache[file] = archive; 300 return archive; 710 301 } 711 302 712 Common::SeekableReadStream *ResLoaderPak::loadFileFromArchive(const Common::String &file, Common::SeekableReadStream *archive, const ResFileEntry entry) const { 713 assert(archive); 303 Common::ArchivePtr Resource::loadInstallerArchive(const Common::String &file, const Common::String &ext, const uint8 offset) { 304 ArchiveMap::iterator cachedArchive = _archiveCache.find(file); 305 if (cachedArchive != _archiveCache.end()) 306 return cachedArchive->_value; 714 307 715 archive->seek(entry.offset, SEEK_SET); 716 Common::SeekableSubReadStream *stream = new Common::SeekableSubReadStream(archive, entry.offset, entry.offset + entry.size, true); 717 assert(stream); 718 return stream; 719 } 308 Common::ArchivePtr archive(InstallerLoader::load(this, file, ext, offset)); 309 if (!archive) 310 return Common::ArchivePtr(); 720 311 721 class ResLoaderInsMalcolm : public ResArchiveLoader { 722 public: 723 bool checkFilename(Common::String filename) const; 724 bool isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const; 725 bool loadFile(const Common::String &filename, Common::SeekableReadStream &stream, FileList &files) const; 726 Common::SeekableReadStream *loadFileFromArchive(const Common::String &file, Common::SeekableReadStream *archive, const ResFileEntry entry) const; 727 728 ResFileEntry::kType getType() const { 729 return ResFileEntry::kInsMal; 730 } 731 }; 732 733 bool ResLoaderInsMalcolm::checkFilename(Common::String filename) const { 734 filename.toUppercase(); 735 if (!filename.hasSuffix(".001")) 736 return false; 737 return true; 312 _archiveCache[file] = archive; 313 return archive; 738 314 } 739 315 740 bool ResLoaderInsMalcolm::isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const {741 stream.seek(3);742 uint32 size = stream.readUint32LE();743 744 if (size+7 > stream.size())745 return false;746 747 stream.seek(size+5, SEEK_SET);748 uint8 buffer[2];749 stream.read(&buffer, 2);750 751 return (buffer[0] == 0x0D && buffer[1] == 0x0A);752 }753 754 bool ResLoaderInsMalcolm::loadFile(const Common::String &filename, Common::SeekableReadStream &stream, FileList &files) const {755 Common::List<Common::String> filenames;756 757 // thanks to eriktorbjorn for this code (a bit modified though)758 stream.seek(3, SEEK_SET);759 760 // first file is the index table761 uint32 size = stream.readUint32LE();762 Common::String temp = "";763 764 for (uint32 i = 0; i < size; ++i) {765 byte c = stream.readByte();766 767 if (c == '\\') {768 temp = "";769 } else if (c == 0x0D) {770 // line endings are CRLF771 c = stream.readByte();772 assert(c == 0x0A);773 ++i;774 775 filenames.push_back(temp);776 } else {777 temp += (char)c;778 }779 }780 781 stream.seek(3, SEEK_SET);782 783 for (Common::List<Common::String>::iterator file = filenames.begin(); file != filenames.end(); ++file) {784 ResFileEntry entry;785 entry.parent = filename;786 entry.parentEntry = 0;787 entry.type = ResFileEntry::kAutoDetect;788 entry.mounted = false;789 entry.preload = false;790 entry.prot = false;791 entry.size = stream.readUint32LE();792 entry.offset = stream.pos();793 stream.seek(entry.size, SEEK_CUR);794 files.push_back(File(*file, entry));795 }796 797 return true;798 }799 800 Common::SeekableReadStream *ResLoaderInsMalcolm::loadFileFromArchive(const Common::String &file, Common::SeekableReadStream *archive, const ResFileEntry entry) const {801 assert(archive);802 803 archive->seek(entry.offset, SEEK_SET);804 Common::SeekableSubReadStream *stream = new Common::SeekableSubReadStream(archive, entry.offset, entry.offset + entry.size, true);805 assert(stream);806 return stream;807 }808 809 class ResLoaderTlk : public ResArchiveLoader {810 public:811 bool checkFilename(Common::String filename) const;812 bool isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const;813 bool loadFile(const Common::String &filename, Common::SeekableReadStream &stream, FileList &files) const;814 Common::SeekableReadStream *loadFileFromArchive(const Common::String &file, Common::SeekableReadStream *archive, const ResFileEntry entry) const;815 816 ResFileEntry::kType getType() const {817 return ResFileEntry::kTlk;818 }819 820 private:821 static bool sortTlkFileList(const File &l, const File &r);822 static FileList::const_iterator nextFile(const FileList &list, FileList::const_iterator iter);823 };824 825 bool ResLoaderTlk::checkFilename(Common::String filename) const {826 filename.toUppercase();827 return (filename.hasSuffix(".TLK"));828 }829 830 bool ResLoaderTlk::isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const {831 uint16 entries = stream.readUint16LE();832 uint32 entryTableSize = (entries * 8);833 834 if (entryTableSize + 2 > stream.size())835 return false;836 837 uint32 offset = 0;838 839 for (uint i = 0; i < entries; ++i) {840 stream.readUint32LE();841 offset = stream.readUint32LE();842 843 if (offset > stream.size())844 return false;845 }846 847 return true;848 }849 850 bool ResLoaderTlk::loadFile(const Common::String &filename, Common::SeekableReadStream &stream, FileList &files) const {851 uint16 entries = stream.readUint16LE();852 853 for (uint i = 0; i < entries; ++i) {854 ResFileEntry entry;855 entry.parent = filename;856 entry.parentEntry = 0;857 entry.type = ResFileEntry::kAutoDetect;858 entry.mounted = false;859 entry.preload = false;860 entry.prot = false;861 862 uint32 resFilename = stream.readUint32LE();863 uint32 resOffset = stream.readUint32LE();864 865 entry.offset = resOffset+4;866 867 char realFilename[20];868 snprintf(realFilename, 20, "%.08u.AUD", resFilename);869 870 uint32 curOffset = stream.pos();871 stream.seek(resOffset, SEEK_SET);872 entry.size = stream.readUint32LE();873 stream.seek(curOffset, SEEK_SET);874 875 files.push_back(FileList::value_type(realFilename, entry));876 }877 878 return true;879 }880 881 Common::SeekableReadStream *ResLoaderTlk::loadFileFromArchive(const Common::String &file, Common::SeekableReadStream *archive, const ResFileEntry entry) const {882 assert(archive);883 884 archive->seek(entry.offset, SEEK_SET);885 Common::SeekableSubReadStream *stream = new Common::SeekableSubReadStream(archive, entry.offset, entry.offset + entry.size, true);886 assert(stream);887 return stream;888 }889 890 316 #pragma mark - 891 #pragma mark - CompFileLoader892 #pragma mark -893 317 894 class FileExpanderSource {895 public:896 FileExpanderSource(const uint8 *data, int dataSize) : _dataPtr(data), _endofBuffer(data + dataSize), _bitsLeft(8), _key(0), _index(0) {}897 ~FileExpanderSource() {}898 899 void advSrcRefresh();900 void advSrcBitsBy1();901 void advSrcBitsByIndex(uint8 newIndex);902 903 uint8 getKeyLower() { return _key & 0xff; }904 void setIndex(uint8 index) { _index = index; }905 uint16 getKeyMasked(uint8 newIndex);906 uint16 keyMaskedAlign(uint16 val);907 908 void copyBytes(uint8 *& dst);909 910 private:911 const uint8 *_dataPtr;912 const uint8 *_endofBuffer;913 uint16 _key;914 int8 _bitsLeft;915 uint8 _index;916 };917 918 void FileExpanderSource::advSrcBitsBy1() {919 _key >>= 1;920 if (!--_bitsLeft) {921 if (_dataPtr < _endofBuffer)922 _key = ((*_dataPtr++) << 8 ) | (_key & 0xff);923 _bitsLeft = 8;924 }925 }926 927 void FileExpanderSource::advSrcBitsByIndex(uint8 newIndex) {928 _index = newIndex;929 _bitsLeft -= _index;930 if (_bitsLeft <= 0) {931 _key >>= (_index + _bitsLeft);932 _index = -_bitsLeft;933 _bitsLeft = 8 - _index;934 if (_dataPtr < _endofBuffer)935 _key = (*_dataPtr++ << 8) | (_key & 0xff);936 }937 _key >>= _index;938 }939 940 uint16 FileExpanderSource::getKeyMasked(uint8 newIndex) {941 static const uint8 mskTable[] = { 0x0F, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF };942 _index = newIndex;943 uint16 res = 0;944 945 if (_index > 8) {946 newIndex = _index - 8;947 res = (_key & 0xff) & mskTable[8];948 advSrcBitsByIndex(8);949 _index = newIndex;950 res |= (((_key & 0xff) & mskTable[_index]) << 8);951 advSrcBitsByIndex(_index);952 } else {953 res = (_key & 0xff) & mskTable[_index];954 advSrcBitsByIndex(_index);955 }956 957 return res;958 }959 960 void FileExpanderSource::copyBytes(uint8 *& dst) {961 advSrcBitsByIndex(_bitsLeft);962 uint16 r = (READ_LE_UINT16(_dataPtr) ^ _key) + 1;963 _dataPtr += 2;964 965 if (r)966 error("decompression failure");967 968 memcpy(dst, _dataPtr, _key);969 _dataPtr += _key;970 dst += _key;971 }972 973 uint16 FileExpanderSource::keyMaskedAlign(uint16 val) {974 val -= 0x101;975 _index = (val & 0xff) >> 2;976 int16 b = ((_bitsLeft << 8) | _index) - 1;977 _bitsLeft = b >> 8;978 _index = b & 0xff;979 uint16 res = (((val & 3) + 4) << _index) + 0x101;980 return res + getKeyMasked(_index);981 }982 983 void FileExpanderSource::advSrcRefresh() {984 _key = READ_LE_UINT16(_dataPtr);985 if (_dataPtr < _endofBuffer - 1)986 _dataPtr += 2;987 _bitsLeft = 8;988 }989 990 class FileExpander {991 public:992 FileExpander();993 ~FileExpander();994 995 bool process(uint8 *dst, const uint8 *src, uint32 outsize, uint32 insize);996 997 private:998 void generateTables(uint8 srcIndex, uint8 dstIndex, uint8 dstIndex2, int cnt);999 uint8 calcCmdAndIndex(const uint8 *tbl, int16 ¶);1000 1001 FileExpanderSource *_src;1002 uint8 *_tables[9];1003 uint16 *_tables16[3];1004 };1005 1006 FileExpander::FileExpander() : _src(0) {1007 _tables[0] = new uint8[3914];1008 assert(_tables[0]);1009 1010 _tables[1] = _tables[0] + 320;1011 _tables[2] = _tables[0] + 352;1012 _tables[3] = _tables[0] + 864;1013 _tables[4] = _tables[0] + 2016;1014 _tables[5] = _tables[0] + 2528;1015 _tables[6] = _tables[0] + 2656;1016 _tables[7] = _tables[0] + 2736;1017 _tables[8] = _tables[0] + 2756;1018 1019 _tables16[0] = (uint16 *)(_tables[0] + 3268);1020 _tables16[1] = (uint16 *)(_tables[0] + 3302);1021 _tables16[2] = (uint16 *)(_tables[0] + 3338);1022 }1023 1024 FileExpander::~FileExpander() {1025 delete _src;1026 delete[] _tables[0];1027 }1028 1029 bool FileExpander::process(uint8 *dst, const uint8 *src, uint32 outsize, uint32 compressedSize) {1030 static const uint8 indexTable[] = {1031 0x10, 0x11, 0x12, 0x00, 0x08, 0x07, 0x09, 0x06, 0x0A,1032 0x05, 0x0B, 0x04, 0x0C, 0x03, 0x0D, 0x02, 0x0E, 0x01, 0x0F1033 };1034 1035 memset(_tables[0], 0, 3914);1036 1037 uint8 *d = dst;1038 uint16 tableSize0 = 0;1039 uint16 tableSize1 = 0;1040 bool needrefresh = true;1041 bool postprocess = false;1042 1043 _src = new FileExpanderSource(src, compressedSize);1044 1045 while (d < dst + outsize) {1046 1047 if (needrefresh) {1048 needrefresh = false;1049 _src->advSrcRefresh();1050 }1051 1052 _src->advSrcBitsBy1();1053 1054 int mode = _src->getKeyMasked(2) - 1;1055 if (mode == 1) {1056 tableSize0 = _src->getKeyMasked(5) + 257;1057 tableSize1 = _src->getKeyMasked(5) + 1;1058 memset(_tables[7], 0, 19);1059 1060 const uint8 *itbl = indexTable;1061 int numbytes = _src->getKeyMasked(4) + 4;1062 1063 while (numbytes--)1064 _tables[7][*itbl++] = _src->getKeyMasked(3);1065 1066 generateTables(7, 8, 255, 19);1067 1068 int cnt = tableSize0 + tableSize1;1069 uint8 *tmp = _tables[0];1070 1071 while (cnt) {1072 uint16 cmd = _src->getKeyLower();1073 cmd = READ_LE_UINT16(&_tables[8][cmd << 1]);1074 _src->advSrcBitsByIndex(_tables[7][cmd]);1075 1076 if (cmd < 16) {1077 *tmp++ = cmd;1078 cnt--;1079 } else {1080 uint8 tmpI = 0;1081 if (cmd == 16) {1082 cmd = _src->getKeyMasked(2) + 3;1083 tmpI = *(tmp - 1);1084 } else if (cmd == 17) {1085 cmd = _src->getKeyMasked(3) + 3;1086 } else {1087 cmd = _src->getKeyMasked(7) + 11;1088 }1089 _src->setIndex(tmpI);1090 memset(tmp, tmpI, cmd);1091 tmp += cmd;1092 1093 cnt -= cmd;1094 if (cnt < 0)1095 error("decompression failure");1096 }1097 }1098 1099 memcpy(_tables[1], _tables[0] + tableSize0, tableSize1);1100 generateTables(0, 2, 3, tableSize0);1101 generateTables(1, 4, 5, tableSize1);1102 postprocess = true;1103 } else if (mode < 0) {1104 _src->copyBytes(d);1105 postprocess = false;1106 needrefresh = true;1107 } else if (mode == 0){1108 uint8 *d2 = _tables[0];1109 memset(d2, 8, 144);1110 memset(d2 + 144, 9, 112);1111 memset(d2 + 256, 7, 24);1112 memset(d2 + 280, 8, 8);1113 d2 = _tables[1];1114 memset(d2, 5, 32);1115 tableSize0 = 288;1116 tableSize1 = 32;1117 1118 generateTables(0, 2, 3, tableSize0);1119 generateTables(1, 4, 5, tableSize1);1120 postprocess = true;1121 } else {1122 error("decompression failure");1123 }1124 1125 if (!postprocess)1126 continue;1127 1128 int16 cmd = 0;1129 1130 do {1131 cmd = ((int16*) _tables[2])[_src->getKeyLower()];1132 _src->advSrcBitsByIndex(cmd < 0 ? calcCmdAndIndex(_tables[3], cmd) : _tables[0][cmd]);1133 1134 if (cmd == 0x11d) {1135 cmd = 0x200;1136 } else if (cmd > 0x108) {1137 cmd = _src->keyMaskedAlign(cmd);1138 }1139 1140 if (!(cmd >> 8)) {1141 *d++ = cmd & 0xff;1142 } else if (cmd != 0x100) {1143 cmd -= 0xfe;1144 int16 offset = ((int16*) _tables[4])[_src->getKeyLower()];1145 _src->advSrcBitsByIndex(offset < 0 ? calcCmdAndIndex(_tables[5], offset) : _tables[1][offset]);1146 if ((offset & 0xff) >= 4) {1147 uint8 newIndex = ((offset & 0xff) >> 1) - 1;1148 offset = (((offset & 1) + 2) << newIndex);1149 offset += _src->getKeyMasked(newIndex);1150 }1151 1152 uint8 *s2 = d - 1 - offset;1153 if (s2 >= dst) {1154 while (cmd--)1155 *d++ = *s2++;1156 } else {1157 uint32 pos = dst - s2;1158 s2 += (d - dst);1159 1160 if (pos < (uint32) cmd) {1161 cmd -= pos;1162 while (pos--)1163 *d++ = *s2++;1164 s2 = dst;1165 }1166 while (cmd--)1167 *d++ = *s2++;1168 }1169 }1170 } while (cmd != 0x100);1171 }1172 1173 delete _src;1174 _src = 0;1175 1176 return true;1177 }1178 1179 void FileExpander::generateTables(uint8 srcIndex, uint8 dstIndex, uint8 dstIndex2, int cnt) {1180 const uint8 *tbl1 = _tables[srcIndex];1181 uint8 *tbl2 = _tables[dstIndex];1182 const uint8 *tbl3 = dstIndex2 == 0xff ? 0 : _tables[dstIndex2];1183 1184 if (!cnt)1185 return;1186 1187 const uint8 *s = tbl1;1188 memset(_tables16[0], 0, 32);1189 1190 for (int i = 0; i < cnt; i++)1191 _tables16[0][(*s++)]++;1192 1193 _tables16[1][1] = 0;1194 1195 for (uint16 i = 1, r = 0; i < 16; i++) {1196 r = (r + _tables16[0][i]) << 1;1197 _tables16[1][i + 1] = r;1198 }1199 1200 if (_tables16[1][16]) {1201 uint16 r = 0;1202 for (uint16 i = 1; i < 16; i++)1203 r += _tables16[0][i];1204 if (r > 1)1205 error("decompression failure");1206 }1207 1208 s = tbl1;1209 uint16 *d = _tables16[2];1210 for (int i = 0; i < cnt; i++) {1211 uint16 t = *s++;1212 if (t) {1213 _tables16[1][t]++;1214 t = _tables16[1][t] - 1;1215 }1216 *d++ = t;1217 }1218 1219 s = tbl1;1220 d = _tables16[2];1221 for (int i = 0; i < cnt; i++) {1222 int8 t = ((int8)(*s++)) - 1;1223 if (t > 0) {1224 uint16 v1 = *d;1225 uint16 v2 = 0;1226 1227 do {1228 v2 = (v2 << 1) | (v1 & 1);1229 v1 >>= 1;1230 } while (--t && v1);1231 1232 t++;1233 uint8 c1 = (v1 & 1);1234 while (t--) {1235 uint8 c2 = v2 >> 15;1236 v2 = (v2 << 1) | c1;1237 c1 = c2;1238 };1239 1240 *d++ = v2;1241 } else {1242 d++;1243 }1244 }1245 1246 memset(tbl2, 0, 512);1247 1248 cnt--;1249 s = tbl1 + cnt;1250 d = &_tables16[2][cnt];1251 uint16 * bt = (uint16*) tbl3;1252 uint16 inc = 0;1253 uint16 cnt2 = 0;1254 1255 do {1256 uint8 t = *s--;1257 uint16 *s2 = (uint16*) tbl2;1258 1259 if (t && t < 9) {1260 inc = 1 << t;1261 uint16 o = *d;1262 1263 do {1264 s2[o] = cnt;1265 o += inc;1266 } while (!(o & 0xf00));1267 1268 } else if (t > 8) {1269 if (!bt)1270 error("decompression failure");1271 1272 t -= 8;1273 uint8 shiftCnt = 1;1274 uint8 v = (*d) >> 8;1275 s2 = &((uint16*) tbl2)[*d & 0xff];1276 1277 do {1278 if (!*s2) {1279 *s2 = (uint16)(~cnt2);1280 *(uint32*)&bt[cnt2] = 0;1281 cnt2 += 2;1282 }1283 1284 s2 = &bt[(uint16)(~*s2)];1285 if (v & shiftCnt)1286 s2++;1287 1288 shiftCnt <<= 1;1289 } while (--t);1290 *s2 = cnt;1291 }1292 d--;1293 } while (--cnt >= 0);1294 }1295 1296 uint8 FileExpander::calcCmdAndIndex(const uint8 *tbl, int16 ¶) {1297 const uint16 *t = (const uint16*)tbl;1298 _src->advSrcBitsByIndex(8);1299 uint8 newIndex = 0;1300 uint16 v = _src->getKeyLower();1301 1302 do {1303 newIndex++;1304 para = t[((~para) & 0xfffe) | (v & 1)];1305 v >>= 1;1306 } while (para < 0);1307 1308 return newIndex;1309 }1310 1311 class CompLoaderInsHof : public CompArchiveLoader {1312 public:1313 CompLoaderInsHof() {1314 _fileExtP = "%03d";1315 _checkFile1 = "WESTWOOD.001";1316 _checkFile2 = "WESTWOOD.002";1317 _containerOffset = 6;1318 }1319 1320 virtual bool checkForFiles() const;1321 virtual bool loadFile(CompFileMap &loadTo) const;1322 1323 protected:1324 struct Archive {1325 Common::String filename;1326 uint32 firstFile;1327 uint32 startOffset;1328 uint32 lastFile;1329 uint32 endOffset;1330 uint32 totalSize;1331 };1332 1333 const char *_fileExtP;1334 const char *_checkFile1;1335 const char *_checkFile2;1336 uint8 _containerOffset;1337 };1338 1339 class CompLoaderInsLol : public CompLoaderInsHof {1340 public:1341 CompLoaderInsLol() {1342 _fileExtP = "%d";1343 _checkFile1 = "WESTWOOD.1";1344 _checkFile2 = "WESTWOOD.2";1345 _containerOffset = 0;1346 }1347 };1348 1349 bool CompLoaderInsHof::checkForFiles() const {1350 return (Common::File::exists(_checkFile1) && Common::File::exists(_checkFile2));1351 }1352 1353 bool CompLoaderInsHof::loadFile(CompFileMap &loadTo) const {1354 Common::File tmpFile;1355 1356 uint32 pos = 0;1357 uint32 bytesleft = 0;1358 bool startFile = true;1359 1360 Common::String filenameBase = "WESTWOOD.";1361 Common::String filenameTemp;1362 char filenameExt[4];1363 1364 while (filenameBase.lastChar() != '.')1365 filenameBase.deleteLastChar();1366 1367 Archive newArchive;1368 1369 Common::List<Archive> archives;1370 1371 for (int8 currentFile = 1; currentFile; currentFile++) {1372 sprintf(filenameExt, _fileExtP, currentFile);1373 filenameTemp = filenameBase + Common::String(filenameExt);1374 1375 if (!tmpFile.open(filenameTemp)) {1376 debug(3, "couldn't open file '%s'\n", filenameTemp.c_str());1377 break;1378 }1379 1380 tmpFile.seek(pos);1381 uint8 fileId = tmpFile.readByte();1382 pos++;1383 1384 uint32 size = tmpFile.size() - 1;1385 if (startFile) {1386 size -= 4;1387 if (fileId == currentFile) {1388 size -= _containerOffset;1389 pos += _containerOffset;1390 tmpFile.seek(_containerOffset, SEEK_CUR);1391 } else {1392 size = size + 1 - pos;1393 }1394 newArchive.filename = filenameBase;1395 bytesleft = newArchive.totalSize = tmpFile.readUint32LE();1396 pos += 4;1397 newArchive.firstFile = currentFile;1398 newArchive.startOffset = pos;1399 startFile = false;1400 }1401 1402 uint32 cs = MIN(size, bytesleft);1403 bytesleft -= cs;1404 1405 tmpFile.close();1406 1407 pos += cs;1408 if (cs == size) {1409 if (!bytesleft) {1410 newArchive.lastFile = currentFile;1411 newArchive.endOffset = --pos;1412 archives.push_back(newArchive);1413 currentFile = -1;1414 } else {1415 pos = 0;1416 }1417 } else {1418 startFile = true;1419 bytesleft = size - cs;1420 newArchive.lastFile = currentFile--;1421 newArchive.endOffset = --pos;1422 archives.push_back(newArchive);1423 }1424 }1425 1426 FileExpander exp;1427 CompFileEntry newEntry;1428 uint32 insize = 0;1429 uint32 outsize = 0;1430 uint8 *inbuffer = 0;1431 uint8 *outbuffer = 0;1432 uint32 inPart1 = 0;1433 uint32 inPart2 = 0;1434 uint8 compressionType = 0;1435 Common::String entryStr;1436 1437 pos = 0;1438 1439 const uint32 kExecSize = 0x0bba;1440 const uint32 kHeaderSize = 30;1441 const uint32 kHeaderSize2 = 46;1442 1443 for (Common::List<Archive>::iterator a = archives.begin(); a != archives.end(); ++a) {1444 startFile = true;1445 for (uint32 i = a->firstFile; i != (a->lastFile + 1); i++) {1446 sprintf(filenameExt, _fileExtP, i);1447 filenameTemp = a->filename + Common::String(filenameExt);1448 1449 if (!tmpFile.open(filenameTemp)) {1450 debug(3, "couldn't open file '%s'\n", filenameTemp.c_str());1451 break;1452 }1453 1454 uint32 size = (i == a->lastFile) ? a->endOffset : tmpFile.size();1455 1456 if (startFile) {1457 startFile = false;1458 pos = a->startOffset + kExecSize;1459 if (pos > size) {1460 pos -= size;1461 tmpFile.close();1462 continue;1463 }1464 } else {1465 if (inPart2) {1466 tmpFile.seek(1);1467 tmpFile.read(inbuffer + inPart1, inPart2);1468 inPart2 = 0;1469 1470 if (compressionType > 0)1471 exp.process(outbuffer, inbuffer, outsize, insize);1472 else1473 memcpy(outbuffer, inbuffer, outsize);1474 1475 delete[] inbuffer;1476 inbuffer = 0;1477 newEntry.data = outbuffer;1478 newEntry.size = outsize;1479 loadTo[entryStr] = newEntry;1480 }1481 pos++;1482 }1483 1484 while (pos < size) {1485 uint8 hdr[43];1486 uint32 m = 0;1487 tmpFile.seek(pos);1488 1489 if (pos + 42 > size) {1490 m = size - pos;1491 uint32 b = 42 - m;1492 1493 if (m >= 4) {1494 uint32 id = tmpFile.readUint32LE();1495 if (id == 0x06054B50) {1496 startFile = true;1497 break;1498 } else {1499 tmpFile.seek(pos);1500 }1501 }1502 1503 sprintf(filenameExt, _fileExtP, i + 1);1504 filenameTemp = a->filename + Common::String(filenameExt);1505 1506 Common::File tmpFile2;1507 tmpFile2.open(filenameTemp);1508 tmpFile.read(hdr, m);1509 tmpFile2.read(hdr + m, b);1510 tmpFile2.close();1511 1512 } else {1513 tmpFile.read(hdr, 42);1514 }1515 1516 uint32 id = READ_LE_UINT32(hdr);1517 1518 if (id == 0x04034B50) {1519 compressionType = hdr[8];1520 insize = READ_LE_UINT32(hdr + 18);1521 outsize = READ_LE_UINT32(hdr + 22);1522 1523 uint16 filestrlen = READ_LE_UINT16(hdr + 26);1524 *(hdr + 30 + filestrlen) = 0;1525 entryStr = Common::String((const char *)(hdr + 30));1526 pos += (kHeaderSize + filestrlen - m);1527 tmpFile.seek(pos);1528 1529 outbuffer = new uint8[outsize];1530 if (!outbuffer)1531 error("Out of memory: Can't uncompress installer files");1532 1533 if (!inbuffer) {1534 inbuffer = new uint8[insize];1535 if (!inbuffer)1536 error("Out of memory: Can't uncompress installer files");1537 }1538 1539 if ((pos + insize) > size) {1540 // this is for files that are split between two archive files1541 inPart1 = size - pos;1542 inPart2 = insize - inPart1;1543 tmpFile.read(inbuffer, inPart1);1544 } else {1545 tmpFile.read(inbuffer, insize);1546 inPart2 = 0;1547 1548 if (compressionType > 0)1549 exp.process(outbuffer, inbuffer, outsize, insize);1550 else1551 memcpy(outbuffer, inbuffer, outsize);1552 1553 delete[] inbuffer;1554 inbuffer = 0;1555 newEntry.data = outbuffer;1556 newEntry.size = outsize;1557 loadTo[entryStr] = newEntry;1558 }1559 1560 pos += insize;1561 if (pos > size) {1562 pos -= size;1563 break;1564 }1565 } else {1566 uint32 filestrlen = READ_LE_UINT32(hdr + 28);1567 pos += (kHeaderSize2 + filestrlen - m);1568 }1569 }1570 tmpFile.close();1571 }1572 }1573 1574 archives.clear();1575 return true;1576 }1577 1578 #pragma mark -1579 1580 318 void Resource::initializeLoaders() { 1581 319 _loaders.push_back(LoaderList::value_type(new ResLoaderPak())); 1582 320 _loaders.push_back(LoaderList::value_type(new ResLoaderInsMalcolm())); 1583 321 _loaders.push_back(LoaderList::value_type(new ResLoaderTlk())); 1584 1585 _compLoaders.push_back(CompLoaderList::value_type(new CompLoaderInsHof()));1586 _compLoaders.push_back(CompLoaderList::value_type(new CompLoaderInsLol()));1587 322 } 1588 323 1589 const ResArchiveLoader *Resource::getLoader(ResFileEntry::kType type) const {1590 for (CLoaderIterator i = _loaders.begin(); i != _loaders.end(); ++i) {1591 if ((*i)->getType() == type)1592 return (*i).get();1593 }1594 return 0;1595 }1596 1597 324 } // end of namespace Kyra 1598 325 1599 326 -
resource.h
35 35 #include "common/hashmap.h" 36 36 #include "common/stream.h" 37 37 #include "common/ptr.h" 38 #include "common/archive.h" 38 39 39 40 #include "kyra/kyra_v1.h" 40 41 #include "kyra/kyra_hof.h" 41 42 42 43 namespace Kyra { 43 44 44 struct ResFileEntry {45 Common::String parent;46 mutable ResFileEntry *parentEntry; // Cache to avoid lookup by string in the map47 // No smart invalidation is needed because the map is cleared globally48 // or expanded but no element is ever removed49 uint32 size;50 51 bool preload;52 bool mounted;53 bool prot;54 55 enum kType {56 kRaw = 0,57 kPak = 1,58 kInsMal = 2,59 kTlk = 3,60 kAutoDetect61 };62 kType type;63 uint32 offset;64 };65 66 struct CompFileEntry {67 uint32 size;68 uint8 *data;69 };70 71 typedef Common::HashMap<Common::String, ResFileEntry, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> ResFileMap;72 typedef Common::HashMap<Common::String, CompFileEntry, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> CompFileMap;73 45 class Resource; 74 46 75 class ResArchiveLoader { 76 public: 77 struct File { 78 File() : filename(), entry() {} 79 File(const Common::String &f, const ResFileEntry &e) : filename(f), entry(e) {} 47 class ResArchiveLoader; 80 48 81 bool operator ==(const Common::String &r) const {82 return filename.equalsIgnoreCase(r);83 }84 85 Common::String filename;86 ResFileEntry entry;87 };88 typedef Common::List<File> FileList;89 90 virtual ~ResArchiveLoader() {}91 92 virtual bool checkFilename(Common::String filename) const = 0;93 virtual bool isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const = 0;94 virtual bool loadFile(const Common::String &filename, Common::SeekableReadStream &stream, FileList &files) const = 0;95 // parameter 'archive' can be deleted by this method and it may not be deleted from the caller96 virtual Common::SeekableReadStream *loadFileFromArchive(const Common::String &file, Common::SeekableReadStream *archive, const ResFileEntry entry) const = 0;97 98 virtual ResFileEntry::kType getType() const = 0;99 protected:100 };101 102 class CompArchiveLoader {103 public:104 virtual ~CompArchiveLoader() {}105 106 virtual bool checkForFiles() const = 0;107 virtual bool loadFile(CompFileMap &loadTo) const = 0;108 };109 110 49 class Resource { 111 50 public: 112 51 Resource(KyraEngine_v1 *vm); … … 114 53 115 54 bool reset(); 116 55 117 bool loadPakFile( const Common::String &filename);118 void unloadPakFile( const Common::String &filename);119 bool isInPakList( const Common::String &filename);56 bool loadPakFile(Common::String filename); 57 void unloadPakFile(Common::String filename); 58 bool isInPakList(Common::String filename); 120 59 121 60 bool loadFileList(const Common::String &filedata); 122 61 bool loadFileList(const char * const *filelist, uint32 numFiles); … … 130 69 131 70 bool loadFileToBuf(const char *file, void *buf, uint32 maxSize); 132 71 protected: 133 void checkFile(const Common::String &file); 134 bool isAccessible(const Common::String &file); 135 bool isAccessible(const ResFileEntry *fileEntry); 72 typedef Common::HashMap<Common::String, Common::ArchivePtr, Common::CaseSensitiveString_Hash, Common::CaseSensitiveString_EqualTo> ArchiveMap; 73 ArchiveMap _archiveCache; 136 74 137 void detectFileTypes(); 138 void detectFileType(const Common::String &filename, ResFileEntry *fileEntry); 75 Common::SearchSet _files; 76 Common::SharedPtr<Common::SearchSet> _archiveFiles; 77 Common::SharedPtr<Common::SearchSet> _protectedFiles; 139 78 79 Common::ArchivePtr loadArchive(const Common::String &file); 80 Common::ArchivePtr loadInstallerArchive(const Common::String &file, const Common::String &ext, const uint8 offset); 81 140 82 void initializeLoaders(); 141 const ResArchiveLoader *getLoader(ResFileEntry::kType type) const; 83 142 84 typedef Common::List<Common::SharedPtr<ResArchiveLoader> > LoaderList; 143 typedef LoaderList::iterator LoaderIterator;144 typedef LoaderList::const_iterator CLoaderIterator;145 85 LoaderList _loaders; 146 ResFileMap _map;147 86 148 ResFileEntry *getParentEntry(const ResFileEntry *entry) const;149 ResFileEntry *getParentEntry(const Common::String &filename) const;150 151 typedef Common::List<Common::SharedPtr<CompArchiveLoader> > CompLoaderList;152 typedef CompLoaderList::iterator CompLoaderIterator;153 typedef CompLoaderList::const_iterator CCompLoaderIterator;154 CompLoaderList _compLoaders;155 CompFileMap _compFiles;156 157 void tryLoadCompFiles();158 void clearCompFileList();159 160 87 KyraEngine_v1 *_vm; 161 88 }; 162 89 -
resource_intern.cpp
1 /* ScummVM - Graphic Adventure Engine 2 * 3 * ScummVM is the legal property of its developers, whose names 4 * are too numerous to list here. Please refer to the COPYRIGHT 5 * file distributed with this source distribution. 6 * 7 * This program 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 2 10 * of the License, or (at your option) any later version. 11 12 * This program 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 this program; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 * 21 * $URL$ 22 * $Id$ 23 * 24 */ 25 26 #include "kyra/resource_intern.h" 27 #include "kyra/resource.h" 28 29 #include "common/stream.h" 30 #include "common/endian.h" 31 32 namespace Kyra { 33 34 // Implementation of various Archive subclasses 35 36 // -> PlainArchive implementation 37 38 PlainArchive::PlainArchive(Resource *owner, const Common::String &filename, const FileInputList &files) 39 : _owner(owner), _filename(filename), _files() { 40 for (FileInputList::iterator i = files.begin(); i != files.end(); ++i) { 41 Entry entry; 42 43 entry.offset = i->offset; 44 entry.size = i->size; 45 46 _files[i->name] = entry; 47 } 48 } 49 50 bool PlainArchive::hasFile(const Common::String &name) { 51 return (_files.find(name) != _files.end()); 52 } 53 54 int PlainArchive::getAllNames(Common::StringList &list) { 55 int count = 0; 56 57 for (FileMap::const_iterator i = _files.begin(); i != _files.end(); ++i) { 58 list.push_back(i->_key); 59 ++count; 60 } 61 62 return count; 63 } 64 65 Common::SeekableReadStream *PlainArchive::openFile(const Common::String &name) { 66 FileMap::const_iterator fDesc = _files.find(name); 67 if (fDesc == _files.end()) 68 return 0; 69 70 Common::SeekableReadStream *parent = _owner->getFileStream(_filename); 71 if (!parent) 72 return 0; 73 74 return new Common::SeekableSubReadStream(parent, fDesc->_value.offset, fDesc->_value.offset + fDesc->_value.size, true); 75 } 76 77 // -> CachedArchive implementation 78 79 CachedArchive::CachedArchive(const FileInputList &files) 80 : _files() { 81 for (FileInputList::iterator i = files.begin(); i != files.end(); ++i) { 82 Entry entry; 83 84 entry.data = i->data; 85 entry.size = i->size; 86 87 _files[i->name] = entry; 88 } 89 } 90 91 CachedArchive::~CachedArchive() { 92 for (FileMap::iterator i = _files.begin(); i != _files.end(); ++i) 93 delete[] i->_value.data; 94 _files.clear(); 95 } 96 97 bool CachedArchive::hasFile(const Common::String &name) { 98 return (_files.find(name) != _files.end()); 99 } 100 101 int CachedArchive::getAllNames(Common::StringList &list) { 102 int count = 0; 103 104 for (FileMap::const_iterator i = _files.begin(); i != _files.end(); ++i) { 105 list.push_back(i->_key); 106 ++count; 107 } 108 109 return count; 110 } 111 112 Common::SeekableReadStream *CachedArchive::openFile(const Common::String &name) { 113 FileMap::const_iterator fDesc = _files.find(name); 114 if (fDesc == _files.end()) 115 return 0; 116 117 return new Common::MemoryReadStream(fDesc->_value.data, fDesc->_value.size, false); 118 } 119 120 // ResFileLoader implementations 121 122 // -> ResLoaderPak implementation 123 124 bool ResLoaderPak::checkFilename(Common::String filename) const { 125 filename.toUppercase(); 126 return (filename.hasSuffix(".PAK") || filename.hasSuffix(".APK") || filename.hasSuffix(".VRM") || filename.hasSuffix(".TLK") || filename.equalsIgnoreCase(StaticResource::staticDataFilename())); 127 } 128 129 bool ResLoaderPak::isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const { 130 uint32 filesize = stream.size(); 131 uint32 offset = 0; 132 bool switchEndian = false; 133 bool firstFile = true; 134 135 offset = stream.readUint32LE(); 136 if (offset > filesize) { 137 switchEndian = true; 138 offset = SWAP_BYTES_32(offset); 139 } 140 141 Common::String file = ""; 142 while (!stream.eos()) { 143 // The start offset of a file should never be in the filelist 144 if (offset < stream.pos() || offset > filesize) 145 return false; 146 147 byte c = 0; 148 149 file = ""; 150 151 while (!stream.eos() && (c = stream.readByte()) != 0) 152 file += c; 153 154 if (stream.eos()) 155 return false; 156 157 // Quit now if we encounter an empty string 158 if (file.empty()) { 159 if (firstFile) 160 return false; 161 else 162 break; 163 } 164 165 firstFile = false; 166 offset = switchEndian ? stream.readUint32BE() : stream.readUint32LE(); 167 168 if (!offset || offset == filesize) 169 break; 170 } 171 172 return true; 173 } 174 175 namespace { 176 177 Common::String readString(Common::SeekableReadStream &stream) { 178 Common::String result; 179 char c = 0; 180 181 while ((c = stream.readByte()) != 0) 182 result += c; 183 184 return result; 185 } 186 187 struct PlainArchiveListSearch { 188 PlainArchiveListSearch(const Common::String &search) : _search(search) {} 189 190 bool operator()(const PlainArchive::InputEntry &entry) { 191 return _search.equalsIgnoreCase(entry.name); 192 } 193 Common::String _search; 194 }; 195 196 } // end of anonymous namespace 197 198 Common::Archive *ResLoaderPak::load(Resource *owner, const Common::String &filename, Common::SeekableReadStream &stream) const { 199 uint32 filesize = stream.size(); 200 201 uint32 startoffset = 0, endoffset = 0; 202 bool switchEndian = false; 203 bool firstFile = true; 204 205 startoffset = stream.readUint32LE(); 206 if (startoffset > filesize) { 207 switchEndian = true; 208 startoffset = SWAP_BYTES_32(startoffset); 209 } 210 211 PlainArchive::FileInputList files; 212 213 Common::String file = ""; 214 while (!stream.eos()) { 215 // The start offset of a file should never be in the filelist 216 if (startoffset < stream.pos() || startoffset > filesize) { 217 warning("PAK file '%s' is corrupted", filename.c_str()); 218 return false; 219 } 220 221 file = ""; 222 byte c = 0; 223 224 while (!stream.eos() && (c = stream.readByte()) != 0) 225 file += c; 226 227 if (stream.eos()) { 228 warning("PAK file '%s' is corrupted", filename.c_str()); 229 return false; 230 } 231 232 // Quit now if we encounter an empty string 233 if (file.empty()) { 234 if (firstFile) { 235 warning("PAK file '%s' is corrupted", filename.c_str()); 236 return false; 237 } else { 238 break; 239 } 240 } 241 242 firstFile = false; 243 endoffset = switchEndian ? stream.readUint32BE() : stream.readUint32LE(); 244 245 if (!endoffset) 246 endoffset = filesize; 247 248 if (startoffset != endoffset) { 249 PlainArchive::InputEntry entry; 250 entry.size = endoffset - startoffset; 251 entry.offset = startoffset; 252 entry.name = file; 253 254 files.push_back(entry); 255 } 256 257 if (endoffset == filesize) 258 break; 259 260 startoffset = endoffset; 261 } 262 263 PlainArchive::FileInputList::const_iterator iter = Common::find_if(files.begin(), files.end(), PlainArchiveListSearch("LINKLIST")); 264 if (iter != files.end()) { 265 stream.seek(iter->offset, SEEK_SET); 266 267 uint32 magic = stream.readUint32BE(); 268 269 if (magic != MKID_BE('SCVM')) 270 error("LINKLIST file does not contain 'SCVM' header"); 271 272 uint32 links = stream.readUint32BE(); 273 for (uint i = 0; i < links; ++i) { 274 Common::String linksTo = readString(stream); 275 uint32 sources = stream.readUint32BE(); 276 277 iter = Common::find_if(files.begin(), files.end(), PlainArchiveListSearch(linksTo)); 278 if (iter == files.end()) 279 error("PAK file link destination '%s' not found", linksTo.c_str()); 280 281 for (uint j = 0; j < sources; ++j) { 282 Common::String dest = readString(stream); 283 files.push_back(*iter); 284 // Better safe than sorry, we update the 'iter' value, in case push_back invalidated it 285 iter = Common::find_if(files.begin(), files.end(), PlainArchiveListSearch(linksTo)); 286 } 287 } 288 } 289 290 return new PlainArchive(owner, filename, files); 291 } 292 293 // -> ResLoaderInsMalcolm implementation 294 295 bool ResLoaderInsMalcolm::checkFilename(Common::String filename) const { 296 filename.toUppercase(); 297 if (!filename.hasSuffix(".001")) 298 return false; 299 return true; 300 } 301 302 bool ResLoaderInsMalcolm::isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const { 303 stream.seek(3, SEEK_SET); 304 uint32 size = stream.readUint32LE(); 305 306 if (size+7 > stream.size()) 307 return false; 308 309 stream.seek(size+5, SEEK_SET); 310 uint8 buffer[2]; 311 stream.read(&buffer, 2); 312 313 return (buffer[0] == 0x0D && buffer[1] == 0x0A); 314 } 315 316 Common::Archive *ResLoaderInsMalcolm::load(Resource *owner, const Common::String &filename, Common::SeekableReadStream &stream) const { 317 Common::List<Common::String> filenames; 318 PlainArchive::FileInputList files; 319 320 // thanks to eriktorbjorn for this code (a bit modified though) 321 stream.seek(3, SEEK_SET); 322 323 // first file is the index table 324 uint32 size = stream.readUint32LE(); 325 Common::String temp = ""; 326 327 for (uint32 i = 0; i < size; ++i) { 328 byte c = stream.readByte(); 329 330 if (c == '\\') { 331 temp = ""; 332 } else if (c == 0x0D) { 333 // line endings are CRLF 334 c = stream.readByte(); 335 assert(c == 0x0A); 336 ++i; 337 338 filenames.push_back(temp); 339 } else { 340 temp += (char)c; 341 } 342 } 343 344 stream.seek(3, SEEK_SET); 345 346 for (Common::List<Common::String>::iterator file = filenames.begin(); file != filenames.end(); ++file) { 347 PlainArchive::InputEntry entry; 348 entry.size = stream.readUint32LE(); 349 entry.offset = stream.pos(); 350 entry.name = *file; 351 stream.seek(entry.size, SEEK_CUR); 352 353 files.push_back(entry); 354 } 355 356 return new PlainArchive(owner, filename, files); 357 } 358 359 bool ResLoaderTlk::checkFilename(Common::String filename) const { 360 filename.toUppercase(); 361 return (filename.hasSuffix(".TLK")); 362 } 363 364 bool ResLoaderTlk::isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const { 365 uint16 entries = stream.readUint16LE(); 366 uint32 entryTableSize = (entries * 8); 367 368 if (entryTableSize + 2 > stream.size()) 369 return false; 370 371 uint32 offset = 0; 372 373 for (uint i = 0; i < entries; ++i) { 374 stream.readUint32LE(); 375 offset = stream.readUint32LE(); 376 377 if (offset > stream.size()) 378 return false; 379 } 380 381 return true; 382 } 383 384 Common::Archive *ResLoaderTlk::load(Resource *owner, const Common::String &filename, Common::SeekableReadStream &stream) const { 385 uint16 entries = stream.readUint16LE(); 386 PlainArchive::FileInputList files; 387 388 for (uint i = 0; i < entries; ++i) { 389 PlainArchive::InputEntry entry; 390 391 uint32 resFilename = stream.readUint32LE(); 392 uint32 resOffset = stream.readUint32LE(); 393 394 entry.offset = resOffset+4; 395 396 char realFilename[20]; 397 snprintf(realFilename, 20, "%.08u.AUD", resFilename); 398 entry.name = realFilename; 399 400 uint32 curOffset = stream.pos(); 401 stream.seek(resOffset, SEEK_SET); 402 entry.size = stream.readUint32LE(); 403 stream.seek(curOffset, SEEK_SET); 404 405 files.push_back(entry); 406 } 407 408 return new PlainArchive(owner, filename, files); 409 } 410 411 // InstallerLoader implementation 412 413 class FileExpanderSource { 414 public: 415 FileExpanderSource(const uint8 *data, int dataSize) : _dataPtr(data), _endofBuffer(data + dataSize), _bitsLeft(8), _key(0), _index(0) {} 416 ~FileExpanderSource() {} 417 418 void advSrcRefresh(); 419 void advSrcBitsBy1(); 420 void advSrcBitsByIndex(uint8 newIndex); 421 422 uint8 getKeyLower() { return _key & 0xff; } 423 void setIndex(uint8 index) { _index = index; } 424 uint16 getKeyMasked(uint8 newIndex); 425 uint16 keyMaskedAlign(uint16 val); 426 427 void copyBytes(uint8 *& dst); 428 429 private: 430 const uint8 *_dataPtr; 431 const uint8 *_endofBuffer; 432 uint16 _key; 433 int8 _bitsLeft; 434 uint8 _index; 435 }; 436 437 void FileExpanderSource::advSrcBitsBy1() { 438 _key >>= 1; 439 if (!--_bitsLeft) { 440 if (_dataPtr < _endofBuffer) 441 _key = ((*_dataPtr++) << 8 ) | (_key & 0xff); 442 _bitsLeft = 8; 443 } 444 } 445 446 void FileExpanderSource::advSrcBitsByIndex(uint8 newIndex) { 447 _index = newIndex; 448 _bitsLeft -= _index; 449 if (_bitsLeft <= 0) { 450 _key >>= (_index + _bitsLeft); 451 _index = -_bitsLeft; 452 _bitsLeft = 8 - _index; 453 if (_dataPtr < _endofBuffer) 454 _key = (*_dataPtr++ << 8) | (_key & 0xff); 455 } 456 _key >>= _index; 457 } 458 459 uint16 FileExpanderSource::getKeyMasked(uint8 newIndex) { 460 static const uint8 mskTable[] = { 0x0F, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF }; 461 _index = newIndex; 462 uint16 res = 0; 463 464 if (_index > 8) { 465 newIndex = _index - 8; 466 res = (_key & 0xff) & mskTable[8]; 467 advSrcBitsByIndex(8); 468 _index = newIndex; 469 res |= (((_key & 0xff) & mskTable[_index]) << 8); 470 advSrcBitsByIndex(_index); 471 } else { 472 res = (_key & 0xff) & mskTable[_index]; 473 advSrcBitsByIndex(_index); 474 } 475 476 return res; 477 } 478 479 void FileExpanderSource::copyBytes(uint8 *& dst) { 480 advSrcBitsByIndex(_bitsLeft); 481 uint16 r = (READ_LE_UINT16(_dataPtr) ^ _key) + 1; 482 _dataPtr += 2; 483 484 if (r) 485 error("decompression failure"); 486 487 memcpy(dst, _dataPtr, _key); 488 _dataPtr += _key; 489 dst += _key; 490 } 491 492 uint16 FileExpanderSource::keyMaskedAlign(uint16 val) { 493 val -= 0x101; 494 _index = (val & 0xff) >> 2; 495 int16 b = ((_bitsLeft << 8) | _index) - 1; 496 _bitsLeft = b >> 8; 497 _index = b & 0xff; 498 uint16 res = (((val & 3) + 4) << _index) + 0x101; 499 return res + getKeyMasked(_index); 500 } 501 502 void FileExpanderSource::advSrcRefresh() { 503 _key = READ_LE_UINT16(_dataPtr); 504 if (_dataPtr < _endofBuffer - 1) 505 _dataPtr += 2; 506 _bitsLeft = 8; 507 } 508 509 class FileExpander { 510 public: 511 FileExpander(); 512 ~FileExpander(); 513 514 bool process(uint8 *dst, const uint8 *src, uint32 outsize, uint32 insize); 515 516 private: 517 void generateTables(uint8 srcIndex, uint8 dstIndex, uint8 dstIndex2, int cnt); 518 uint8 calcCmdAndIndex(const uint8 *tbl, int16 ¶); 519 520 FileExpanderSource *_src; 521 uint8 *_tables[9]; 522 uint16 *_tables16[3]; 523 }; 524 525 FileExpander::FileExpander() : _src(0) { 526 _tables[0] = new uint8[3914]; 527 assert(_tables[0]); 528 529 _tables[1] = _tables[0] + 320; 530 _tables[2] = _tables[0] + 352; 531 _tables[3] = _tables[0] + 864; 532 _tables[4] = _tables[0] + 2016; 533 _tables[5] = _tables[0] + 2528; 534 _tables[6] = _tables[0] + 2656; 535 _tables[7] = _tables[0] + 2736; 536 _tables[8] = _tables[0] + 2756; 537 538 _tables16[0] = (uint16 *)(_tables[0] + 3268); 539 _tables16[1] = (uint16 *)(_tables[0] + 3302); 540 _tables16[2] = (uint16 *)(_tables[0] + 3338); 541 } 542 543 FileExpander::~FileExpander() { 544 delete _src; 545 delete[] _tables[0]; 546 } 547 548 bool FileExpander::process(uint8 *dst, const uint8 *src, uint32 outsize, uint32 compressedSize) { 549 static const uint8 indexTable[] = { 550 0x10, 0x11, 0x12, 0x00, 0x08, 0x07, 0x09, 0x06, 0x0A, 551 0x05, 0x0B, 0x04, 0x0C, 0x03, 0x0D, 0x02, 0x0E, 0x01, 0x0F 552 }; 553 554 memset(_tables[0], 0, 3914); 555 556 uint8 *d = dst; 557 uint16 tableSize0 = 0; 558 uint16 tableSize1 = 0; 559 bool needrefresh = true; 560 bool postprocess = false; 561 562 _src = new FileExpanderSource(src, compressedSize); 563 564 while (d < dst + outsize) { 565 566 if (needrefresh) { 567 needrefresh = false; 568 _src->advSrcRefresh(); 569 } 570 571 _src->advSrcBitsBy1(); 572 573 int mode = _src->getKeyMasked(2) - 1; 574 if (mode == 1) { 575 tableSize0 = _src->getKeyMasked(5) + 257; 576 tableSize1 = _src->getKeyMasked(5) + 1; 577 memset(_tables[7], 0, 19); 578 579 const uint8 *itbl = indexTable; 580 int numbytes = _src->getKeyMasked(4) + 4; 581 582 while (numbytes--) 583 _tables[7][*itbl++] = _src->getKeyMasked(3); 584 585 generateTables(7, 8, 255, 19); 586 587 int cnt = tableSize0 + tableSize1; 588 uint8 *tmp = _tables[0]; 589 590 while (cnt) { 591 uint16 cmd = _src->getKeyLower(); 592 cmd = READ_LE_UINT16(&_tables[8][cmd << 1]); 593 _src->advSrcBitsByIndex(_tables[7][cmd]); 594 595 if (cmd < 16) { 596 *tmp++ = cmd; 597 cnt--; 598 } else { 599 uint8 tmpI = 0; 600 if (cmd == 16) { 601 cmd = _src->getKeyMasked(2) + 3; 602 tmpI = *(tmp - 1); 603 } else if (cmd == 17) { 604 cmd = _src->getKeyMasked(3) + 3; 605 } else { 606 cmd = _src->getKeyMasked(7) + 11; 607 } 608 _src->setIndex(tmpI); 609 memset(tmp, tmpI, cmd); 610 tmp += cmd; 611 612 cnt -= cmd; 613 if (cnt < 0) 614 error("decompression failure"); 615 } 616 } 617 618 memcpy(_tables[1], _tables[0] + tableSize0, tableSize1); 619 generateTables(0, 2, 3, tableSize0); 620 generateTables(1, 4, 5, tableSize1); 621 postprocess = true; 622 } else if (mode < 0) { 623 _src->copyBytes(d); 624 postprocess = false; 625 needrefresh = true; 626 } else if (mode == 0){ 627 uint8 *d2 = _tables[0]; 628 memset(d2, 8, 144); 629 memset(d2 + 144, 9, 112); 630 memset(d2 + 256, 7, 24); 631 memset(d2 + 280, 8, 8); 632 d2 = _tables[1]; 633 memset(d2, 5, 32); 634 tableSize0 = 288; 635 tableSize1 = 32; 636 637 generateTables(0, 2, 3, tableSize0); 638 generateTables(1, 4, 5, tableSize1); 639 postprocess = true; 640 } else { 641 error("decompression failure"); 642 } 643 644 if (!postprocess) 645 continue; 646 647 int16 cmd = 0; 648 649 do { 650 cmd = ((int16*) _tables[2])[_src->getKeyLower()]; 651 _src->advSrcBitsByIndex(cmd < 0 ? calcCmdAndIndex(_tables[3], cmd) : _tables[0][cmd]); 652 653 if (cmd == 0x11d) { 654 cmd = 0x200; 655 } else if (cmd > 0x108) { 656 cmd = _src->keyMaskedAlign(cmd); 657 } 658 659 if (!(cmd >> 8)) { 660 *d++ = cmd & 0xff; 661 } else if (cmd != 0x100) { 662 cmd -= 0xfe; 663 int16 offset = ((int16*) _tables[4])[_src->getKeyLower()]; 664 _src->advSrcBitsByIndex(offset < 0 ? calcCmdAndIndex(_tables[5], offset) : _tables[1][offset]); 665 if ((offset & 0xff) >= 4) { 666 uint8 newIndex = ((offset & 0xff) >> 1) - 1; 667 offset = (((offset & 1) + 2) << newIndex); 668 offset += _src->getKeyMasked(newIndex); 669 } 670 671 uint8 *s2 = d - 1 - offset; 672 if (s2 >= dst) { 673 while (cmd--) 674 *d++ = *s2++; 675 } else { 676 uint32 pos = dst - s2; 677 s2 += (d - dst); 678 679 if (pos < (uint32) cmd) { 680 cmd -= pos; 681 while (pos--) 682 *d++ = *s2++; 683 s2 = dst; 684 } 685 while (cmd--) 686 *d++ = *s2++; 687 } 688 } 689 } while (cmd != 0x100); 690 } 691 692 delete _src; 693 _src = 0; 694 695 return true; 696 } 697 698 void FileExpander::generateTables(uint8 srcIndex, uint8 dstIndex, uint8 dstIndex2, int cnt) { 699 const uint8 *tbl1 = _tables[srcIndex]; 700 uint8 *tbl2 = _tables[dstIndex]; 701 const uint8 *tbl3 = dstIndex2 == 0xff ? 0 : _tables[dstIndex2]; 702 703 if (!cnt) 704 return; 705 706 const uint8 *s = tbl1; 707 memset(_tables16[0], 0, 32); 708 709 for (int i = 0; i < cnt; i++) 710 _tables16[0][(*s++)]++; 711 712 _tables16[1][1] = 0; 713 714 for (uint16 i = 1, r = 0; i < 16; i++) { 715 r = (r + _tables16[0][i]) << 1; 716 _tables16[1][i + 1] = r; 717 } 718 719 if (_tables16[1][16]) { 720 uint16 r = 0; 721 for (uint16 i = 1; i < 16; i++) 722 r += _tables16[0][i]; 723 if (r > 1) 724 error("decompression failure"); 725 } 726 727 s = tbl1; 728 uint16 *d = _tables16[2]; 729 for (int i = 0; i < cnt; i++) { 730 uint16 t = *s++; 731 if (t) { 732 _tables16[1][t]++; 733 t = _tables16[1][t] - 1; 734 } 735 *d++ = t; 736 } 737 738 s = tbl1; 739 d = _tables16[2]; 740 for (int i = 0; i < cnt; i++) { 741 int8 t = ((int8)(*s++)) - 1; 742 if (t > 0) { 743 uint16 v1 = *d; 744 uint16 v2 = 0; 745 746 do { 747 v2 = (v2 << 1) | (v1 & 1); 748 v1 >>= 1; 749 } while (--t && v1); 750 751 t++; 752 uint8 c1 = (v1 & 1); 753 while (t--) { 754 uint8 c2 = v2 >> 15; 755 v2 = (v2 << 1) | c1; 756 c1 = c2; 757 }; 758 759 *d++ = v2; 760 } else { 761 d++; 762 } 763 } 764 765 memset(tbl2, 0, 512); 766 767 cnt--; 768 s = tbl1 + cnt; 769 d = &_tables16[2][cnt]; 770 uint16 * bt = (uint16*) tbl3; 771 uint16 inc = 0; 772 uint16 cnt2 = 0; 773 774 do { 775 uint8 t = *s--; 776 uint16 *s2 = (uint16*) tbl2; 777 778 if (t && t < 9) { 779 inc = 1 << t; 780 uint16 o = *d; 781 782 do { 783 s2[o] = cnt; 784 o += inc; 785 } while (!(o & 0xf00)); 786 787 } else if (t > 8) { 788 if (!bt) 789 error("decompression failure"); 790 791 t -= 8; 792 uint8 shiftCnt = 1; 793 uint8 v = (*d) >> 8; 794 s2 = &((uint16*) tbl2)[*d & 0xff]; 795 796 do { 797 if (!*s2) { 798 *s2 = (uint16)(~cnt2); 799 *(uint32*)&bt[cnt2] = 0; 800 cnt2 += 2; 801 } 802 803 s2 = &bt[(uint16)(~*s2)]; 804 if (v & shiftCnt) 805 s2++; 806 807 shiftCnt <<= 1; 808 } while (--t); 809 *s2 = cnt; 810 } 811 d--; 812 } while (--cnt >= 0); 813 } 814 815 uint8 FileExpander::calcCmdAndIndex(const uint8 *tbl, int16 ¶) { 816 const uint16 *t = (const uint16*)tbl; 817 _src->advSrcBitsByIndex(8); 818 uint8 newIndex = 0; 819 uint16 v = _src->getKeyLower(); 820 821 do { 822 newIndex++; 823 para = t[((~para) & 0xfffe) | (v & 1)]; 824 v >>= 1; 825 } while (para < 0); 826 827 return newIndex; 828 } 829 830 namespace { 831 832 struct InsArchive { 833 Common::String filename; 834 uint32 firstFile; 835 uint32 startOffset; 836 uint32 lastFile; 837 uint32 endOffset; 838 uint32 totalSize; 839 }; 840 841 } // end of anonymouse namespace 842 843 Common::Archive *InstallerLoader::load(Resource *owner, const Common::String &filename, const Common::String &extension, const uint8 containerOffset) { 844 uint32 pos = 0; 845 uint32 bytesleft = 0; 846 bool startFile = true; 847 848 Common::String filenameBase =filename; 849 Common::String filenameTemp; 850 char filenameExt[4]; 851 852 if (filenameBase.lastChar() != '.') 853 filenameBase += '.'; 854 855 InsArchive newArchive; 856 Common::List<InsArchive> archives; 857 858 Common::SeekableReadStream *tmpFile = 0; 859 860 for (int8 currentFile = 1; currentFile; currentFile++) { 861 sprintf(filenameExt, extension.c_str(), currentFile); 862 filenameTemp = filenameBase + Common::String(filenameExt); 863 864 if (!(tmpFile = owner->getFileStream(filenameTemp))) { 865 debug(3, "couldn't open file '%s'\n", filenameTemp.c_str()); 866 break; 867 } 868 869 tmpFile->seek(pos, SEEK_SET); 870 uint8 fileId = tmpFile->readByte(); 871 pos++; 872 873 uint32 size = tmpFile->size() - 1; 874 if (startFile) { 875 size -= 4; 876 if (fileId == currentFile) { 877 size -= containerOffset; 878 pos += containerOffset; 879 tmpFile->seek(containerOffset, SEEK_CUR); 880 } else { 881 size = size + 1 - pos; 882 } 883 newArchive.filename = filenameBase; 884 bytesleft = newArchive.totalSize = tmpFile->readUint32LE(); 885 pos += 4; 886 newArchive.firstFile = currentFile; 887 newArchive.startOffset = pos; 888 startFile = false; 889 } 890 891 uint32 cs = MIN(size, bytesleft); 892 bytesleft -= cs; 893 894 delete tmpFile; 895 tmpFile = 0; 896 897 pos += cs; 898 if (cs == size) { 899 if (!bytesleft) { 900 newArchive.lastFile = currentFile; 901 newArchive.endOffset = --pos; 902 archives.push_back(newArchive); 903 currentFile = -1; 904 } else { 905 pos = 0; 906 } 907 } else { 908 startFile = true; 909 bytesleft = size - cs; 910 newArchive.lastFile = currentFile--; 911 newArchive.endOffset = --pos; 912 archives.push_back(newArchive); 913 } 914 } 915 916 FileExpander exp; 917 CachedArchive::InputEntry newEntry; 918 uint32 insize = 0; 919 uint32 outsize = 0; 920 uint8 *inbuffer = 0; 921 uint8 *outbuffer = 0; 922 uint32 inPart1 = 0; 923 uint32 inPart2 = 0; 924 uint8 compressionType = 0; 925 Common::String entryStr; 926 927 CachedArchive::FileInputList fileList; 928 929 pos = 0; 930 931 const uint32 kExecSize = 0x0bba; 932 const uint32 kHeaderSize = 30; 933 const uint32 kHeaderSize2 = 46; 934 935 for (Common::List<InsArchive>::iterator a = archives.begin(); a != archives.end(); ++a) { 936 startFile = true; 937 for (uint32 i = a->firstFile; i != (a->lastFile + 1); i++) { 938 sprintf(filenameExt, extension.c_str(), i); 939 filenameTemp = a->filename + Common::String(filenameExt); 940 941 if (!(tmpFile = owner->getFileStream(filenameTemp))) { 942 debug(3, "couldn't open file '%s'\n", filenameTemp.c_str()); 943 break; 944 } 945 946 uint32 size = (i == a->lastFile) ? a->endOffset : tmpFile->size(); 947 948 if (startFile) { 949 startFile = false; 950 pos = a->startOffset + kExecSize; 951 if (pos > size) { 952 pos -= size; 953 delete tmpFile; 954 tmpFile = 0; 955 continue; 956 } 957 } else { 958 if (inPart2) { 959 tmpFile->seek(1, SEEK_SET); 960 tmpFile->read(inbuffer + inPart1, inPart2); 961 inPart2 = 0; 962 963 if (compressionType > 0) 964 exp.process(outbuffer, inbuffer, outsize, insize); 965 else 966 memcpy(outbuffer, inbuffer, outsize); 967 968 delete[] inbuffer; 969 inbuffer = 0; 970 newEntry.data = outbuffer; 971 newEntry.size = outsize; 972 newEntry.name = entryStr; 973 fileList.push_back(newEntry); 974 } 975 pos++; 976 } 977 978 while (pos < size) { 979 uint8 hdr[43]; 980 uint32 m = 0; 981 tmpFile->seek(pos, SEEK_SET); 982 983 if (pos + 42 > size) { 984 m = size - pos; 985 uint32 b = 42 - m; 986 987 if (m >= 4) { 988 uint32 id = tmpFile->readUint32LE(); 989 if (id == 0x06054B50) { 990 startFile = true; 991 break; 992 } else { 993 tmpFile->seek(pos, SEEK_SET); 994 } 995 } 996 997 sprintf(filenameExt, extension.c_str(), i + 1); 998 filenameTemp = a->filename + Common::String(filenameExt); 999 1000 Common::SeekableReadStream *tmpFile2 = owner->getFileStream(filenameTemp); 1001 tmpFile->read(hdr, m); 1002 tmpFile2->read(hdr + m, b); 1003 delete tmpFile2; 1004 } else { 1005 tmpFile->read(hdr, 42); 1006 } 1007 1008 uint32 id = READ_LE_UINT32(hdr); 1009 1010 if (id == 0x04034B50) { 1011 compressionType = hdr[8]; 1012 insize = READ_LE_UINT32(hdr + 18); 1013 outsize = READ_LE_UINT32(hdr + 22); 1014 1015 uint16 filestrlen = READ_LE_UINT16(hdr + 26); 1016 *(hdr + 30 + filestrlen) = 0; 1017 entryStr = Common::String((const char *)(hdr + 30)); 1018 pos += (kHeaderSize + filestrlen - m); 1019 tmpFile->seek(pos, SEEK_SET); 1020 1021 outbuffer = new uint8[outsize]; 1022 if (!outbuffer) 1023 error("Out of memory: Can't uncompress installer files"); 1024 1025 if (!inbuffer) { 1026 inbuffer = new uint8[insize]; 1027 if (!inbuffer) 1028 error("Out of memory: Can't uncompress installer files"); 1029 } 1030 1031 if ((pos + insize) > size) { 1032 // this is for files that are split between two archive files 1033 inPart1 = size - pos; 1034 inPart2 = insize - inPart1; 1035 tmpFile->read(inbuffer, inPart1); 1036 } else { 1037 tmpFile->read(inbuffer, insize); 1038 inPart2 = 0; 1039 1040 if (compressionType > 0) 1041 exp.process(outbuffer, inbuffer, outsize, insize); 1042 else 1043 memcpy(outbuffer, inbuffer, outsize); 1044 1045 delete[] inbuffer; 1046 inbuffer = 0; 1047 newEntry.data = outbuffer; 1048 newEntry.size = outsize; 1049 newEntry.name = entryStr; 1050 fileList.push_back(newEntry); 1051 } 1052 1053 pos += insize; 1054 if (pos > size) { 1055 pos -= size; 1056 break; 1057 } 1058 } else { 1059 uint32 filestrlen = READ_LE_UINT32(hdr + 28); 1060 pos += (kHeaderSize2 + filestrlen - m); 1061 } 1062 } 1063 delete tmpFile; 1064 tmpFile = 0; 1065 } 1066 } 1067 1068 archives.clear(); 1069 return new CachedArchive(fileList); 1070 } 1071 1072 } // end of namespace Kyra -
resource_intern.h
1 /* ScummVM - Graphic Adventure Engine 2 * 3 * ScummVM is the legal property of its developers, whose names 4 * are too numerous to list here. Please refer to the COPYRIGHT 5 * file distributed with this source distribution. 6 * 7 * This program 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 2 10 * of the License, or (at your option) any later version. 11 12 * This program 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 this program; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 * 21 * $URL$ 22 * $Id$ 23 * 24 */ 25 26 #ifndef KYRA_RESOURCE_INTERN_H 27 #define KYRA_RESOURCE_INTERN_H 28 29 #include "common/archive.h" 30 #include "common/hash-str.h" 31 #include "common/hashmap.h" 32 #include "common/str.h" 33 #include "common/list.h" 34 35 namespace Kyra { 36 37 class Resource; 38 39 class PlainArchive : public Common::Archive { 40 public: 41 struct InputEntry { 42 Common::String name; 43 44 uint32 offset; 45 uint32 size; 46 }; 47 48 typedef Common::List<InputEntry> FileInputList; 49 50 PlainArchive(Resource *owner, const Common::String &filename, const FileInputList &files); 51 52 bool hasFile(const Common::String &name); 53 int getAllNames(Common::StringList &list); 54 Common::SeekableReadStream *openFile(const Common::String &name); 55 private: 56 struct Entry { 57 uint32 offset; 58 uint32 size; 59 }; 60 61 typedef Common::HashMap<Common::String, Entry, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FileMap; 62 63 Resource *_owner; 64 Common::String _filename; 65 FileMap _files; 66 }; 67 68 class CachedArchive : public Common::Archive { 69 public: 70 struct InputEntry { 71 Common::String name; 72 73 byte *data; 74 uint32 size; 75 }; 76 77 typedef Common::List<InputEntry> FileInputList; 78 79 CachedArchive(const FileInputList &files); 80 ~CachedArchive(); 81 82 bool hasFile(const Common::String &name); 83 int getAllNames(Common::StringList &list); 84 Common::SeekableReadStream *openFile(const Common::String &name); 85 private: 86 struct Entry { 87 byte *data; 88 uint32 size; 89 }; 90 91 typedef Common::HashMap<Common::String, Entry, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FileMap; 92 FileMap _files; 93 }; 94 95 96 class ResArchiveLoader { 97 public: 98 virtual bool checkFilename(Common::String filename) const = 0; 99 virtual bool isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const = 0; 100 virtual Common::Archive *load(Resource *owner, const Common::String &filename, Common::SeekableReadStream &stream) const = 0; 101 }; 102 103 class ResLoaderPak : public ResArchiveLoader { 104 public: 105 bool checkFilename(Common::String filename) const; 106 bool isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const; 107 Common::Archive *load(Resource *owner, const Common::String &filename, Common::SeekableReadStream &stream) const; 108 }; 109 110 class ResLoaderInsMalcolm : public ResArchiveLoader { 111 public: 112 bool checkFilename(Common::String filename) const; 113 bool isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const; 114 Common::Archive *load(Resource *owner, const Common::String &filename, Common::SeekableReadStream &stream) const; 115 }; 116 117 class ResLoaderTlk : public ResArchiveLoader { 118 public: 119 bool checkFilename(Common::String filename) const; 120 bool isLoadable(const Common::String &filename, Common::SeekableReadStream &stream) const; 121 Common::Archive *load(Resource *owner, const Common::String &filename, Common::SeekableReadStream &stream) const; 122 }; 123 124 class InstallerLoader { 125 public: 126 static Common::Archive *load(Resource *owner, const Common::String &filename, const Common::String &extension, const uint8 offset); 127 }; 128 129 } // end of namespace Kyra 130 131 #endif