Ticket #8904: kyra_resource_v1.patch

File kyra_resource_v1.patch, 78.6 KB (added by lordhoto, 11 years ago)

Patch against r34397

  • module.mk

     
    2323        kyra_mr.o \
    2424        lol.o \
    2525        resource.o \
     26        resource_intern.o \
    2627        saveload.o \
    2728        saveload_lok.o \
    2829        saveload_hof.o \
  • resource.cpp

     
    3131#include "common/func.h"
    3232
    3333#include "kyra/resource.h"
     34#include "kyra/resource_intern.h"
    3435
    3536namespace Kyra {
    3637
    37 Resource::Resource(KyraEngine_v1 *vm) : _loaders(), _map(), _vm(vm) {
     38Resource::Resource(KyraEngine_v1 *vm) : _archiveCache(), _files(), _archiveFiles(new Common::SearchSet()), _protectedFiles(new Common::SearchSet()), _loaders(), _vm(vm) {
    3839        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);
    3949}
    4050
    4151Resource::~Resource() {
    42         _map.clear();
    4352        _loaders.clear();
    44 
    45         clearCompFileList();
    46         _compLoaders.clear();
    4753}
    4854
    4955bool Resource::reset() {
    50         clearCompFileList();
    5156        unloadAllPakFiles();
    5257
    5358        Common::FilesystemNode dir(ConfMan.get("path"));
     
    7176                        loadPakFile("CHAPTER1.VRM");
    7277        } else if (_vm->game() == GI_KYRA2) {
    7378                if (_vm->gameFlags().useInstallerPackage)
    74                         tryLoadCompFiles();
     79                        _files.add("installer", loadInstallerArchive("WESTWOOD", "%03d", 6), 2);
    7580
    7681                // mouse pointer, fonts, etc. required for initializing
    7782                if (_vm->gameFlags().isDemo && !_vm->gameFlags().isTalkie) {
    7883                        loadPakFile("GENERAL.PAK");
    7984                } else {
    80                         if (_vm->gameFlags().isTalkie) {
    81                                 // Add default file directories
    82                                 Common::File::addDefaultDirectory(ConfMan.get("path") + "hof_cd");
    83                                 Common::File::addDefaultDirectory(ConfMan.get("path") + "HOF_CD");
    84                         }
    85 
    8685                        loadPakFile("INTROGEN.PAK");
    8786                        loadPakFile("OTHER.PAK");
    8887                }
     
    9493                                error("couldn't load file: 'WESTWOOD.001'");
    9594                }
    9695
    97                 // Add default file directories
    98                 Common::File::addDefaultDirectory(ConfMan.get("path") + "malcolm");
    99                 Common::File::addDefaultDirectory(ConfMan.get("path") + "MALCOLM");
    100 
    10196                if (!loadFileList("FILEDATA.FDT"))
    10297                        error("couldn't load file: 'FILEDATA.FDT'");
    10398
    10499                return true;
    105100        } else if (_vm->game() == GI_LOL) {
    106101                if (_vm->gameFlags().useInstallerPackage)
    107                         tryLoadCompFiles();
     102                        _files.add("installer", loadInstallerArchive("WESTWOOD", "%d", 0), 2);
    108103
    109104                return true;
    110105        }
     
    120115                        "CAVE.APK", "DRAGON1.APK", "DRAGON2.APK", "LAGOON.APK"
    121116                };
    122117
    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);
    129121                }
    130122        } else {
    131123                for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
     
    149141        return true;
    150142}
    151143
    152 bool Resource::loadPakFile(const Common::String &filename) {
    153         if (!isAccessible(filename))
    154                 return false;
     144bool Resource::loadPakFile(Common::String filename) {
     145        filename.toUppercase();
    155146
    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))
    162148                return true;
    163         }
    164149
    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)
    168152                return false;
    169         }
    170153
    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);
    176155
    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 to
    188                         // allow overwriting files inside archives with plain files inside the
    189                         // game directory
    190                         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 old
    208                                         // 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 the
    213                                 // old file entry, could be afterall that the preload flag of the new archive was
    214                                 // 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 putting
    220                 // them in the gamepath.
    221         }
    222 
    223         detectFileTypes();
    224156        return true;
    225157}
    226158
     
    240172                buffer[12] = 0;
    241173                f.seek(offset + 16, SEEK_SET);
    242174
    243                 Common::String filename = Common::String((char*)buffer);
     175                Common::String filename = Common::String((char *)buffer);
    244176                filename.toUppercase();
    245177
    246178                if (filename.hasSuffix(".PAK")) {
    247                         if (!isAccessible(filename) && _vm->gameFlags().isDemo) {
     179                        if (!exists(filename.c_str()) && _vm->gameFlags().isDemo) {
    248180                                // the demo version supplied with Kyra3 does not
    249181                                // contain all pak files listed in filedata.fdt
    250182                                // so we don't do anything here if they are non
     
    273205        return true;
    274206}
    275207
    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         }
     208void Resource::unloadPakFile(Common::String filename) {
     209        filename.toUppercase();
     210        _archiveFiles->remove(filename);
     211        _protectedFiles->remove(filename);
    282212}
    283213
    284 void Resource::clearCompFileList() {
    285         for (CompFileMap::iterator i = _compFiles.begin(); i != _compFiles.end(); ++i)
    286                 delete[] i->_value.data;
    287 
    288         _compFiles.clear();
     214bool Resource::isInPakList(Common::String filename) {
     215        filename.toUppercase();
     216        return (_archiveFiles->hasArchive(filename) || _protectedFiles->hasArchive(filename));
    289217}
    290218
    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 
    300219void Resource::unloadAllPakFiles() {
    301         // remove all entries
    302         _map.clear();
     220        _archiveFiles->clear();
     221        _protectedFiles->clear();
    303222}
    304223
    305224uint8 *Resource::fileData(const char *file, uint32 *size) {
     
    318237}
    319238
    320239bool Resource::exists(const char *file, bool errorOutOnFail) {
    321         if (Common::File::exists(file))
     240        if (_files.hasFile(file))
    322241                return true;
    323         else if (isAccessible(file))
    324                 return true;
    325242        else if (errorOutOnFail)
    326243                error("File '%s' can't be found", file);
    327244        return false;
    328245}
    329246
    330247uint32 Resource::getFileSize(const char *file) {
    331         CompFileMap::iterator compEntry;
     248        Common::SeekableReadStream *stream = getFileStream(file);
     249        if (!stream)
     250                return 0;
    332251
    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;
    346255}
    347256
    348257bool Resource::loadFileToBuf(const char *file, void *buf, uint32 maxSize) {
     
    357266}
    358267
    359268Common::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);
    392270}
    393271
    394 bool Resource::isAccessible(const Common::String &file) {
    395         checkFile(file);
     272Common::ArchivePtr Resource::loadArchive(const Common::String &file) {
     273        ArchiveMap::iterator cachedArchive = _archiveCache.find(file);
     274        if (cachedArchive != _archiveCache.end())
     275                return cachedArchive->_value;
    396276
    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();
    403280
    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));
    510287                                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 - ResFileLodaer
    535 #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 filelist
    569                 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 string
    583                 if (file.empty()) {
    584                         if (firstFile)
    585                                 return false;
    586                         else
    587                                 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 namespace
    613 
    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 filelist
    630                 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 string
    647                 if (file.empty()) {
    648                         if (firstFile) {
    649                                 warning("PAK file '%s' is corrupted", filename.c_str());
    650                                 return false;
    651288                        } else {
    652                                 break;
     289                                stream->seek(0, SEEK_SET);
    653290                        }
    654291                }
    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;
    680292        }
    681293
    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;
    685295
    686                 uint32 magic = stream.readUint32BE();
     296        if (!archive)
     297                return Common::ArchivePtr();
    687298
    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;
    710301}
    711302
    712 Common::SeekableReadStream *ResLoaderPak::loadFileFromArchive(const Common::String &file, Common::SeekableReadStream *archive, const ResFileEntry entry) const {
    713         assert(archive);
     303Common::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;
    714307
    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();
    720311
    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;
    738314}
    739315
    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 table
    761         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 CRLF
    771                         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 
    890316#pragma mark -
    891 #pragma mark - CompFileLoader
    892 #pragma mark -
    893317
    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 &para);
    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, 0x0F
    1033         };
    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 &para) {
    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                                         else
    1473                                                 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 files
    1541                                                 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                                                 else
    1551                                                         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 
    1580318void Resource::initializeLoaders() {
    1581319        _loaders.push_back(LoaderList::value_type(new ResLoaderPak()));
    1582320        _loaders.push_back(LoaderList::value_type(new ResLoaderInsMalcolm()));
    1583321        _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()));
    1587322}
    1588323
    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 
    1597324} // end of namespace Kyra
    1598325
    1599326
  • resource.h

     
    3535#include "common/hashmap.h"
    3636#include "common/stream.h"
    3737#include "common/ptr.h"
     38#include "common/archive.h"
    3839
    3940#include "kyra/kyra_v1.h"
    4041#include "kyra/kyra_hof.h"
    4142
    4243namespace Kyra {
    4344
    44 struct ResFileEntry {
    45         Common::String parent;
    46         mutable ResFileEntry *parentEntry;      // Cache to avoid lookup by string in the map
    47                                                                                 // No smart invalidation is needed because the map is cleared globally
    48                                                                                 // or expanded but no element is ever removed
    49         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                 kAutoDetect
    61         };
    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;
    7345class Resource;
    7446
    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) {}
     47class ResArchiveLoader;
    8048
    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 caller
    96         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 
    11049class Resource {
    11150public:
    11251        Resource(KyraEngine_v1 *vm);
     
    11453
    11554        bool reset();
    11655
    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);
    12059
    12160        bool loadFileList(const Common::String &filedata);
    12261        bool loadFileList(const char * const *filelist, uint32 numFiles);
     
    13069
    13170        bool loadFileToBuf(const char *file, void *buf, uint32 maxSize);
    13271protected:
    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;
    13674
    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;
    13978
     79        Common::ArchivePtr loadArchive(const Common::String &file);
     80        Common::ArchivePtr loadInstallerArchive(const Common::String &file, const Common::String &ext, const uint8 offset);
     81
    14082        void initializeLoaders();
    141         const ResArchiveLoader *getLoader(ResFileEntry::kType type) const;
     83
    14284        typedef Common::List<Common::SharedPtr<ResArchiveLoader> > LoaderList;
    143         typedef LoaderList::iterator LoaderIterator;
    144         typedef LoaderList::const_iterator CLoaderIterator;
    14585        LoaderList _loaders;
    146         ResFileMap _map;
    14786
    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 
    16087        KyraEngine_v1 *_vm;
    16188};
    16289
  • 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
     32namespace Kyra {
     33
     34// Implementation of various Archive subclasses
     35
     36// -> PlainArchive implementation
     37
     38PlainArchive::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
     50bool PlainArchive::hasFile(const Common::String &name) {
     51        return (_files.find(name) != _files.end());
     52}
     53
     54int 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
     65Common::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
     79CachedArchive::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
     91CachedArchive::~CachedArchive() {
     92        for (FileMap::iterator i = _files.begin(); i != _files.end(); ++i)
     93                delete[] i->_value.data;
     94        _files.clear();
     95}
     96
     97bool CachedArchive::hasFile(const Common::String &name) {
     98        return (_files.find(name) != _files.end());
     99}
     100
     101int 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
     112Common::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
     124bool 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
     129bool 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
     175namespace {
     176
     177Common::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
     187struct 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
     198Common::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
     295bool ResLoaderInsMalcolm::checkFilename(Common::String filename) const {
     296        filename.toUppercase();
     297        if (!filename.hasSuffix(".001"))
     298                return false;
     299        return true;
     300}
     301
     302bool 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
     316Common::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
     359bool ResLoaderTlk::checkFilename(Common::String filename) const {
     360        filename.toUppercase();
     361        return (filename.hasSuffix(".TLK"));
     362}
     363
     364bool 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
     384Common::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
     413class FileExpanderSource {
     414public:
     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
     429private:
     430        const uint8 *_dataPtr;
     431        const uint8 *_endofBuffer;
     432        uint16 _key;
     433        int8 _bitsLeft;
     434        uint8 _index;
     435};
     436
     437void FileExpanderSource::advSrcBitsBy1() {
     438        _key >>= 1;             
     439        if (!--_bitsLeft) {
     440                if (_dataPtr < _endofBuffer)
     441                        _key = ((*_dataPtr++) << 8 ) | (_key & 0xff);
     442                _bitsLeft = 8;
     443        }
     444}
     445
     446void 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
     459uint16 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
     479void 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
     492uint16 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
     502void FileExpanderSource::advSrcRefresh() {
     503        _key = READ_LE_UINT16(_dataPtr);
     504        if (_dataPtr < _endofBuffer - 1)
     505                _dataPtr += 2;         
     506        _bitsLeft = 8;
     507}
     508
     509class FileExpander {
     510public:
     511        FileExpander();
     512        ~FileExpander();
     513
     514        bool process(uint8 *dst, const uint8 *src, uint32 outsize, uint32 insize);
     515
     516private:
     517        void generateTables(uint8 srcIndex, uint8 dstIndex, uint8 dstIndex2, int cnt);
     518        uint8 calcCmdAndIndex(const uint8 *tbl, int16 &para);
     519
     520        FileExpanderSource *_src;
     521        uint8 *_tables[9];
     522        uint16 *_tables16[3];
     523};
     524
     525FileExpander::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
     543FileExpander::~FileExpander() {
     544        delete _src;
     545        delete[] _tables[0];
     546}
     547
     548bool 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
     698void 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
     815uint8 FileExpander::calcCmdAndIndex(const uint8 *tbl, int16 &para) {
     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
     830namespace {
     831
     832struct 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
     843Common::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
     35namespace Kyra {
     36
     37class Resource;
     38
     39class PlainArchive : public Common::Archive {
     40public:
     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);
     55private:
     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
     68class CachedArchive : public Common::Archive {
     69public:
     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);
     85private:
     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
     96class ResArchiveLoader {
     97public:
     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
     103class ResLoaderPak : public ResArchiveLoader {
     104public:
     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
     110class ResLoaderInsMalcolm : public ResArchiveLoader {
     111public:
     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
     117class ResLoaderTlk : public ResArchiveLoader {
     118public:
     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
     124class InstallerLoader {
     125public:
     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