diff options
Diffstat (limited to 'engines/sci/resource.cpp')
-rw-r--r-- | engines/sci/resource.cpp | 247 |
1 files changed, 173 insertions, 74 deletions
diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp index 9809f10576..a19e7bc893 100644 --- a/engines/sci/resource.cpp +++ b/engines/sci/resource.cpp @@ -114,16 +114,21 @@ static const char *s_resourceTypeNames[] = { "patch", "bitmap", "palette", "cdaudio", "audio", "sync", "message", "map", "heap", "audio36", "sync36", "xlate", "robot", "vmd", - "chunk", "macibin", "macibis", "macpict" + "chunk", "animation", "etc", "duck", "clut", + "tga", "zzz", "macibin", "macibis", "macpict" }; +// Resource type suffixes. Note that the +// suffic of SCI3 scripts has been changed from +// scr to csc static const char *s_resourceTypeSuffixes[] = { "v56", "p56", "scr", "tex", "snd", "", "voc", "fon", "cur", "pat", "bit", "pal", "cda", "aud", "syn", "msg", "map", "hep", "", "", "trn", "rbt", "vmd", "chk", "", - "", "" + "etc", "duk", "clu", "tga", "zzz", + "", "", "" }; const char *getResourceTypeName(ResourceType restype) { @@ -144,18 +149,19 @@ static const ResourceType s_resTypeMapSci0[] = { // TODO: 12 should be "Wave", but SCI seems to just store it in Audio resources static const ResourceType s_resTypeMapSci21[] = { - kResourceTypeView, kResourceTypePic, kResourceTypeScript, kResourceTypeText, // 0x00-0x03 - kResourceTypeSound, kResourceTypeMemory, kResourceTypeVocab, kResourceTypeFont, // 0x04-0x07 + kResourceTypeView, kResourceTypePic, kResourceTypeScript, kResourceTypeAnimation, // 0x00-0x03 + kResourceTypeSound, kResourceTypeEtc, kResourceTypeVocab, kResourceTypeFont, // 0x04-0x07 kResourceTypeCursor, kResourceTypePatch, kResourceTypeBitmap, kResourceTypePalette, // 0x08-0x0B kResourceTypeInvalid, kResourceTypeAudio, kResourceTypeSync, kResourceTypeMessage, // 0x0C-0x0F kResourceTypeMap, kResourceTypeHeap, kResourceTypeChunk, kResourceTypeAudio36, // 0x10-0x13 - kResourceTypeSync36, kResourceTypeTranslation, kResourceTypeRobot, kResourceTypeVMD // 0x14-0x17 + kResourceTypeSync36, kResourceTypeTranslation, kResourceTypeRobot, kResourceTypeVMD, // 0x14-0x17 + kResourceTypeDuck, kResourceTypeClut, kResourceTypeTGA, kResourceTypeZZZ // 0x18-0x1B }; ResourceType ResourceManager::convertResType(byte type) { type &= 0x7f; - if (_mapVersion != kResVersionSci32) { + if (_mapVersion < kResVersionSci2) { // SCI0 - SCI2 if (type < ARRAYSIZE(s_resTypeMapSci0)) return s_resTypeMapSci0[type]; @@ -587,6 +593,8 @@ int ResourceManager::addAppropriateSources() { int ResourceManager::addAppropriateSources(const Common::FSList &fslist) { ResourceSource *map = 0; + Common::Array<ResourceSource *> sci21Maps; + #ifdef ENABLE_SCI32 ResourceSource *sci21PatchMap = 0; const Common::FSNode *sci21PatchRes = 0; @@ -605,8 +613,13 @@ int ResourceManager::addAppropriateSources(const Common::FSList &fslist) { if (filename.contains("resmap.0")) { const char *dot = strrchr(file->getName().c_str(), '.'); - int number = atoi(dot + 1); - map = addExternalMap(file, number); + uint number = atoi(dot + 1); + + // We need to store each of these maps for use later on + if (number >= sci21Maps.size()) + sci21Maps.resize(number + 1); + + sci21Maps[number] = addExternalMap(file, number); } #ifdef ENABLE_SCI32 @@ -619,7 +632,7 @@ int ResourceManager::addAppropriateSources(const Common::FSList &fslist) { #endif } - if (!map) + if (!map && sci21Maps.empty()) return 0; #ifdef ENABLE_SCI32 @@ -635,11 +648,17 @@ int ResourceManager::addAppropriateSources(const Common::FSList &fslist) { Common::String filename = file->getName(); filename.toLowercase(); - if (filename.contains("resource.0") || filename.contains("ressci.0")) { + if (filename.contains("resource.0")) { const char *dot = strrchr(filename.c_str(), '.'); int number = atoi(dot + 1); addSource(new VolumeResourceSource(file->getName(), map, number, file)); + } else if (filename.contains("ressci.0")) { + const char *dot = strrchr(filename.c_str(), '.'); + int number = atoi(dot + 1); + + // Match this volume to its own map + addSource(new VolumeResourceSource(file->getName(), sci21Maps[number], number, file)); } } @@ -660,11 +679,29 @@ int ResourceManager::addInternalSources() { addSource(new AudioVolumeResourceSource(this, "RESOURCE.SFX", src, 0)); else if (Common::File::exists("RESOURCE.AUD")) addSource(new AudioVolumeResourceSource(this, "RESOURCE.AUD", src, 0)); + else + return 0; ++itr; } delete resources; + +#ifdef ENABLE_SCI32 + if (_mapVersion >= kResVersionSci2) { + // If we have no scripts, but chunk 0 is present, open up the chunk + // to try to get to any scripts in there. The Lighthouse SCI2.1 demo + // does exactly this. + + resources = listResources(kResourceTypeScript); + + if (resources->empty() && testResource(ResourceId(kResourceTypeChunk, 0))) + addResourcesFromChunk(0); + + delete resources; + } +#endif + return 1; } @@ -779,7 +816,7 @@ void ChunkResourceSource::loadResource(ResourceManager *resMan, Resource *res) { } void ResourceManager::addResourcesFromChunk(uint16 id) { - addSource(new ChunkResourceSource(Common::String::printf("Chunk %d", id), id)); + addSource(new ChunkResourceSource(Common::String::format("Chunk %d", id), id)); scanNewSources(); } @@ -795,7 +832,7 @@ void ResourceManager::freeResourceSources() { ResourceManager::ResourceManager() { } -void ResourceManager::init() { +void ResourceManager::init(bool initFromFallbackDetector) { _memoryLocked = 0; _memoryLRU = 0; _LRU.clear(); @@ -806,6 +843,13 @@ void ResourceManager::init() { _mapVersion = detectMapVersion(); _volVersion = detectVolVersion(); + + // TODO/FIXME: Remove once SCI3 resource detection is finished + if ((_mapVersion == kResVersionSci3 || _volVersion == kResVersionSci3) && (_mapVersion != _volVersion)) { + warning("FIXME: Incomplete SCI3 detection: setting map and volume version to SCI3"); + _mapVersion = _volVersion = kResVersionSci3; + } + if ((_volVersion == kResVersionUnknown) && (_mapVersion != kResVersionUnknown)) { warning("Volume version not detected, but map version has been detected. Setting volume version to map version"); _volVersion = _mapVersion; @@ -826,8 +870,13 @@ void ResourceManager::init() { } scanNewSources(); - addInternalSources(); - scanNewSources(); + + if (!initFromFallbackDetector) { + if (!addInternalSources()) + error("Somehow I can't seem to find the sound files I need (RESOURCE.AUD/RESOURCE.SFX), aborting"); + + scanNewSources(); + } detectSciVersion(); @@ -858,21 +907,6 @@ void ResourceManager::init() { } #endif } - -#ifdef ENABLE_SCI32 - if (getSciVersion() >= SCI_VERSION_2_1) { - // If we have no scripts, but chunk 0 is present, open up the chunk - // to try to get to any scripts in there. The Lighthouse SCI2.1 demo - // does exactly this. - - Common::List<ResourceId> *scriptList = listResources(kResourceTypeScript); - - if (scriptList->empty() && testResource(ResourceId(kResourceTypeChunk, 0))) - addResourcesFromChunk(0); - - delete scriptList; - } -#endif } ResourceManager::~ResourceManager() { @@ -940,7 +974,7 @@ void ResourceManager::freeOldResources() { removeFromLRU(goner); goner->unalloc(); #ifdef SCI_VERBOSE_RESMAN - printf("resMan-debug: LRU: Freeing %s.%03d (%d bytes)\n", getResourceTypeName(goner->type), goner->number, goner->size); + debug("resMan-debug: LRU: Freeing %s.%03d (%d bytes)", getResourceTypeName(goner->type), goner->number, goner->size); #endif } } @@ -1024,8 +1058,10 @@ const char *ResourceManager::versionDescription(ResVersion version) const { return "SCI1.1"; case kResVersionSci11Mac: return "Mac SCI1.1+"; - case kResVersionSci32: - return "SCI32"; + case kResVersionSci2: + return "SCI2/2.1"; + case kResVersionSci3: + return "SCI3"; } return "Version not valid"; @@ -1036,6 +1072,8 @@ ResVersion ResourceManager::detectMapVersion() { byte buff[6]; ResourceSource *rsrc= 0; + // TODO: Add SCI3 support + for (Common::List<ResourceSource *>::iterator it = _sources.begin(); it != _sources.end(); ++it) { rsrc = *it; @@ -1084,8 +1122,8 @@ ResVersion ResourceManager::detectMapVersion() { directoryOffset = fileStream->readUint16LE(); // Only SCI32 has directory type < 0x80 - if (directoryType < 0x80 && (mapDetected == kResVersionUnknown || mapDetected == kResVersionSci32)) - mapDetected = kResVersionSci32; + if (directoryType < 0x80 && (mapDetected == kResVersionUnknown || mapDetected == kResVersionSci2)) + mapDetected = kResVersionSci2; else if (directoryType < 0x80 || ((directoryType & 0x7f) > 0x20 && directoryType != 0xFF)) break; @@ -1127,7 +1165,7 @@ ResVersion ResourceManager::detectVolVersion() { for (Common::List<ResourceSource *>::iterator it = _sources.begin(); it != _sources.end(); ++it) { rsrc = *it; - + if (rsrc->getSourceType() == kSourceVolume) { if (rsrc->_resourceFile) { fileStream = rsrc->_resourceFile->createReadStream(); @@ -1163,23 +1201,38 @@ ResVersion ResourceManager::detectVolVersion() { bool failed = false; bool sci11Align = false; - // Check for SCI0, SCI1, SCI1.1 and SCI32 v2 (Gabriel Knight 1 CD) formats + // Check for SCI0, SCI1, SCI1.1, SCI32 v2 (Gabriel Knight 1 CD) and SCI32 v3 (LSL7) formats while (!fileStream->eos() && fileStream->pos() < 0x100000) { if (curVersion > kResVersionSci0Sci1Early) fileStream->readByte(); resId = fileStream->readUint16LE(); - dwPacked = (curVersion < kResVersionSci32) ? fileStream->readUint16LE() : fileStream->readUint32LE(); - dwUnpacked = (curVersion < kResVersionSci32) ? fileStream->readUint16LE() : fileStream->readUint32LE(); + dwPacked = (curVersion < kResVersionSci2) ? fileStream->readUint16LE() : fileStream->readUint32LE(); + dwUnpacked = (curVersion < kResVersionSci2) ? fileStream->readUint16LE() : fileStream->readUint32LE(); + + // The compression field is present, but bogus when + // loading SCI3 volumes, the format is otherwise + // identical to SCI2. We therefore get the compression + // indicator here, but disregard it in the following + // code. wCompression = fileStream->readUint16LE(); + if (fileStream->eos()) { delete fileStream; return curVersion; } - int chk = (curVersion == kResVersionSci0Sci1Early) ? 4 : 20; + int chk; + + if (curVersion == kResVersionSci0Sci1Early) + chk = 4; + else if (curVersion < kResVersionSci2) + chk = 20; + else + chk = 32; // We don't need this, but include it for completeness + int offs = curVersion < kResVersionSci11 ? 4 : 0; - if ((curVersion < kResVersionSci32 && wCompression > chk) - || (curVersion == kResVersionSci32 && wCompression != 0 && wCompression != 32) + if ((curVersion < kResVersionSci2 && wCompression > chk) + || (curVersion == kResVersionSci2 && wCompression != 0 && wCompression != 32) || (wCompression == 0 && dwPacked != dwUnpacked + offs) || (dwUnpacked < dwPacked - offs)) { @@ -1192,7 +1245,9 @@ ResVersion ResourceManager::detectVolVersion() { // Later versions (e.g. QFG1VGA) have resources word-aligned sci11Align = true; } else if (curVersion == kResVersionSci11) { - curVersion = kResVersionSci32; + curVersion = kResVersionSci2; + } else if (curVersion == kResVersionSci2) { + curVersion = kResVersionSci3; } else { // All version checks failed, exit loop failed = true; @@ -1207,7 +1262,7 @@ ResVersion ResourceManager::detectVolVersion() { fileStream->seek(dwPacked - 4, SEEK_CUR); else if (curVersion == kResVersionSci11) fileStream->seek(sci11Align && ((9 + dwPacked) % 2) ? dwPacked + 1 : dwPacked, SEEK_CUR); - else if (curVersion == kResVersionSci32) + else if (curVersion >= kResVersionSci2) fileStream->seek(dwPacked, SEEK_CUR); } @@ -1390,7 +1445,7 @@ void ResourceManager::readResourcePatches() { for (int i = kResourceTypeView; i < kResourceTypeInvalid; ++i) { // Ignore the types that can't be patched (and Robot/VMD is handled externally for now) - if (!s_resourceTypeSuffixes[i] || i == kResourceTypeRobot || i == kResourceTypeVMD) + if (!s_resourceTypeSuffixes[i] || (i >= kResourceTypeRobot && i != kResourceTypeChunk)) continue; files.clear(); @@ -1404,6 +1459,12 @@ void ResourceManager::readResourcePatches() { mask += s_resourceTypeSuffixes[i]; SearchMan.listMatchingMembers(files, mask); + if (i == kResourceTypeScript && files.size() == 0) { + // SCI3 (we can't use getSciVersion() at this point) + mask = "*.csc"; + SearchMan.listMatchingMembers(files, mask); + } + for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) { bool bAdd = false; name = (*x)->getName(); @@ -1557,10 +1618,8 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) { // the actual resource file. int mapVolumeNr = volume_nr + map->_volumeNumber; ResourceSource *source = findVolume(map, mapVolumeNr); - // FIXME: this code has serious issues with multiple RESMAP.* files (like in unmodified gk2) - // adding a resource with source == NULL would crash later on - if (!source) - error("Unable to find volume for map %s volumeNr %d", map->getLocationName().c_str(), mapVolumeNr); + + assert(source); Resource *resource = _resMap.getVal(resId, NULL); if (!resource) { @@ -1583,10 +1642,12 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) { return 0; } -struct { +struct MacResTag { uint32 tag; ResourceType type; -} static const macResTagMap[] = { +}; + +static const MacResTag macResTagMap[] = { { MKID_BE('V56 '), kResourceTypeView }, { MKID_BE('P56 '), kResourceTypePic }, { MKID_BE('SCR '), kResourceTypeScript }, @@ -1731,12 +1792,22 @@ int Resource::readResourceInfo(ResVersion volVersion, Common::SeekableReadStream wCompression = 0; break; #ifdef ENABLE_SCI32 - case kResVersionSci32: + case kResVersionSci2: + case kResVersionSci3: type = _resMan->convertResType(file->readByte()); number = file->readUint16LE(); szPacked = file->readUint32LE(); szUnpacked = file->readUint32LE(); + + // The same comment applies here as in + // detectVolVersion regarding SCI3. We ignore the + // compression field for SCI3 games, but must presume + // it exists in the file. wCompression = file->readUint16LE(); + + if (volVersion == kResVersionSci3) + wCompression = szPacked != szUnpacked ? 32 : 0; + break; #endif default: @@ -1953,7 +2024,7 @@ void ResourceManager::detectSciVersion() { #ifdef ENABLE_SCI32 viewCompression = getViewCompression(); #else - if (_volVersion == kResVersionSci32) { + if (_volVersion >= kResVersionSci2) { // SCI32 support isn't built in, thus view detection will fail viewCompression = kCompUnknown; } else { @@ -1975,7 +2046,7 @@ void ResourceManager::detectSciVersion() { || _volVersion == kResVersionSci11Mac #ifdef ENABLE_SCI32 || viewCompression == kCompSTACpack - || _volVersion == kResVersionSci32 // kq7 + || _volVersion == kResVersionSci2 // kq7 #endif ) { // SCI1.1 VGA views @@ -1985,7 +2056,7 @@ void ResourceManager::detectSciVersion() { // Otherwise we detect it from a view _viewType = detectViewType(); #else - if (_volVersion == kResVersionSci32 && viewCompression == kCompUnknown) { + if (_volVersion == kResVersionSci2 && viewCompression == kCompUnknown) { // A SCI32 game, but SCI32 support is disabled. Force the view type // to kViewVga11, as we can't read from the game's resource files _viewType = kViewVga11; @@ -2009,17 +2080,20 @@ void ResourceManager::detectSciVersion() { } // Handle SCI32 versions here - if (_volVersion == kResVersionSci32) { + if (_volVersion >= kResVersionSci2) { + Common::List<ResourceId> *heaps = listResources(kResourceTypeHeap); // SCI2.1/3 and SCI1 Late resource maps are the same, except that // SCI1 Late resource maps have the resource types or'd with // 0x80. We differentiate between SCI2 and SCI2.1/3 based on that. - // TODO: Differentiate between SCI2.1 and SCI3 if (_mapVersion == kResVersionSci1Late) { s_sciVersion = SCI_VERSION_2; return; - } else { + } else if (!heaps->empty()) { s_sciVersion = SCI_VERSION_2_1; return; + } else { + s_sciVersion = SCI_VERSION_3; + return; } } @@ -2284,7 +2358,7 @@ bool ResourceManager::hasSci1Voc900() { return offset == res->size; } -// Same function as Script::findBlock(). Slight code +// Same function as Script::findBlockSCI0(). Slight code // duplication here, but this has been done to keep the resource // manager independent from the rest of the engine static byte *findSci0ExportsBlock(byte *buffer) { @@ -2310,6 +2384,24 @@ static byte *findSci0ExportsBlock(byte *buffer) { return NULL; } +// This code duplicates Script::relocateOffsetSci3, but we can't use +// that here since we can't instantiate scripts at this point. +static int relocateOffsetSci3(const byte *buf, uint32 offset) { + int relocStart = READ_LE_UINT32(buf + 8); + int relocCount = READ_LE_UINT16(buf + 18); + const byte *seeker = buf + relocStart; + + for (int i = 0; i < relocCount; ++i) { + if (READ_SCI11ENDIAN_UINT32(seeker) == offset) { + // TODO: Find out what UINT16 at (seeker + 8) means + return READ_SCI11ENDIAN_UINT16(buf + offset) + READ_SCI11ENDIAN_UINT32(seeker + 4); + } + seeker += 10; + } + + return -1; +} + reg_t ResourceManager::findGameObject(bool addSci11ScriptOffset) { Resource *script = findResource(ResourceId(kResourceTypeScript, 0), false); @@ -2318,7 +2410,7 @@ reg_t ResourceManager::findGameObject(bool addSci11ScriptOffset) { byte *offsetPtr = 0; - if (getSciVersion() < SCI_VERSION_1_1) { + if (getSciVersion() <= SCI_VERSION_1_LATE) { byte *buf = (getSciVersion() == SCI_VERSION_0_EARLY) ? script->data + 2 : script->data; // Check if the first block is the exports block (in most cases, it is) @@ -2331,35 +2423,42 @@ reg_t ResourceManager::findGameObject(bool addSci11ScriptOffset) { error("Unable to find exports block from script 0"); offsetPtr += 4 + 2; } - } else { + + int16 offset = !isSci11Mac() ? READ_LE_UINT16(offsetPtr) : READ_BE_UINT16(offsetPtr); + return make_reg(1, offset); + } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) { offsetPtr = script->data + 4 + 2 + 2; - } - - int16 offset = !isSci11Mac() ? READ_LE_UINT16(offsetPtr) : READ_BE_UINT16(offsetPtr); - // In SCI1.1 and newer, the heap is appended at the end of the script, - // so adjust the offset accordingly - if (getSciVersion() >= SCI_VERSION_1_1 && addSci11ScriptOffset) { - offset += script->size; + // In SCI1.1 - SCI2.1, the heap is appended at the end of the script, + // so adjust the offset accordingly if requested + int16 offset = !isSci11Mac() ? READ_LE_UINT16(offsetPtr) : READ_BE_UINT16(offsetPtr); + if (addSci11ScriptOffset) { + offset += script->size; - // Ensure that the start of the heap is word-aligned - same as in Script::init() - if (script->size & 2) - offset++; - } + // Ensure that the start of the heap is word-aligned - same as in Script::init() + if (script->size & 2) + offset++; + } - return make_reg(1, offset); + return make_reg(1, offset); + } else { + return make_reg(1, relocateOffsetSci3(script->data, 22)); + } } Common::String ResourceManager::findSierraGameId() { - // In SCI0-SCI1, the heap is embedded in the script. In SCI1.1+, it's separated + // In SCI0-SCI1, the heap is embedded in the script. In SCI1.1 - SCI2.1, + // it's in a separate heap resource Resource *heap = 0; int nameSelector = 3; if (getSciVersion() < SCI_VERSION_1_1) { heap = findResource(ResourceId(kResourceTypeScript, 0), false); - } else { + } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) { heap = findResource(ResourceId(kResourceTypeHeap, 0), false); nameSelector += 5; + } else if (getSciVersion() == SCI_VERSION_3) { + warning("TODO: findSierraGameId(): SCI3 equivalent"); } if (!heap) |