int ResourceManager::readAudioMapSCI11(IntMapResourceSource *map) { #ifndef ENABLE_SCI32 // SCI32 support is not built in. Check if this is a SCI32 game // and if it is abort here. if (_volVersion >= kResVersionSci2) return SCI_ERROR_RESMAP_NOT_FOUND; #endif uint32 offset = 0; Resource *mapRes = findResource(ResourceId(kResourceTypeMap, map->_mapNumber), false); if (!mapRes) { warning("Failed to open %i.MAP", map->_mapNumber); return SCI_ERROR_RESMAP_NOT_FOUND; } ResourceSource *src = findVolume(map, map->_volumeNumber); if (!src) { warning("Failed to find volume for %i.MAP", map->_mapNumber); return SCI_ERROR_NO_RESOURCE_FILES_FOUND; } byte *ptr = mapRes->data; // Heuristic to detect entry size uint32 entrySize = 0; for (int i = mapRes->size - 1; i >= 0; --i) { if (ptr[i] == 0xff) entrySize++; else break; } if (map->_mapNumber == 65535) { while (ptr < mapRes->data + mapRes->size) { uint16 n = READ_LE_UINT16(ptr); ptr += 2; if (n == 0xffff) break; if (entrySize == 6) { offset = READ_LE_UINT32(ptr); ptr += 4; } else { offset += READ_LE_UINT24(ptr); ptr += 3; } addResource(ResourceId(kResourceTypeAudio, n), src, offset); } } else if (map->_mapNumber == 0 && entrySize == 10 && ptr[3] == 0) { // QFG3 demo format // ptr[3] would be 'seq' in the normal format and cannot possibly be 0 while (ptr < mapRes->data + mapRes->size) { uint16 n = READ_BE_UINT16(ptr); ptr += 2; if (n == 0xffff) break; offset = READ_LE_UINT32(ptr); ptr += 4; uint32 size = READ_LE_UINT32(ptr); ptr += 4; addResource(ResourceId(kResourceTypeAudio, n), src, offset, size); } } else if (map->_mapNumber == 0 && entrySize == 8 && READ_LE_UINT16(ptr + 2) == 0xffff) { // LB2 Floppy/Mother Goose SCI1.1 format Common::SeekableReadStream *stream = getVolumeFile(src); while (ptr < mapRes->data + mapRes->size) { uint16 n = READ_LE_UINT16(ptr); ptr += 4; if (n == 0xffff) break; offset = READ_LE_UINT32(ptr); ptr += 4; // The size is not stored in the map and the entries have no order. // We need to dig into the audio resource in the volume to get the size. stream->seek(offset + 1); byte headerSize = stream->readByte(); assert(headerSize == 11 || headerSize == 12); stream->skip(5); uint32 size = stream->readUint32LE() + headerSize + 2; addResource(ResourceId(kResourceTypeAudio, n), src, offset, size); } } else { bool isEarly = (entrySize != 11); if (!isEarly) { offset = READ_LE_UINT32(ptr); ptr += 4; } while (ptr < mapRes->data + mapRes->size) { uint32 n = READ_BE_UINT32(ptr); int syncSize = 0; ptr += 4; if (n == 0xffffffff) break; if (isEarly) { offset = READ_LE_UINT32(ptr); ptr += 4; } else { offset += READ_LE_UINT24(ptr); ptr += 3; } if (isEarly || (n & 0x80)) { syncSize = READ_LE_UINT16(ptr); ptr += 2; // FIXME: The sync36 resource seems to be two bytes too big in KQ6CD // (bytes taken from the RAVE resource right after it) if (syncSize > 0) addResource(ResourceId(kResourceTypeSync36, map->_mapNumber, n & 0xffffff3f), src, offset, syncSize); } if (n & 0x40) { // This seems to define the size of raw lipsync data (at least // in KQ6 CD Windows). int kq6HiresSyncSize = READ_LE_UINT16(ptr); ptr += 2; if (kq6HiresSyncSize > 0) { addResource(ResourceId(kResourceTypeRave, map->_mapNumber, n & 0xffffff3f), src, offset + syncSize, kq6HiresSyncSize); syncSize += kq6HiresSyncSize; } } addResource(ResourceId(kResourceTypeAudio36, map->_mapNumber, n & 0xffffff3f), src, offset + syncSize); } } return 0; }
int ResourceManager::readAudioMapSCI11(IntMapResourceSource *map) { #ifndef ENABLE_SCI32 // SCI32 support is not built in. Check if this is a SCI32 game // and if it is abort here. if (_volVersion >= kResVersionSci2) return SCI_ERROR_RESMAP_NOT_FOUND; #endif uint32 offset = 0; const ResourceId mapResId(kResourceTypeMap, map->_mapNumber); Resource *mapRes = _resMap.getVal(mapResId, nullptr); if (!mapRes) { warning("Failed to open %s", mapResId.toString().c_str()); return SCI_ERROR_RESMAP_NOT_FOUND; } // Here, we allocate audio maps ourselves instead of using findResource to // do this for us. This is in order to prevent the map resources from // getting into the LRU cache. These resources must be read and then // deallocated in games with multi-disc audio in order to read the audio // maps from every CD, and LRU eviction freaks out if an unallocated // resource ends up in the LRU list. It is also not necessary for these // resources to be cached in the LRU at all, since they are only used upon // game startup to populate _resMap. assert(mapRes->_status == kResStatusNoMalloc); loadResource(mapRes); if (!mapRes->data()) { warning("Failed to read data for %s", mapResId.toString().c_str()); return SCI_ERROR_RESMAP_NOT_FOUND; } ResourceSource *src = findVolume(map, map->_volumeNumber); if (!src) { warning("Failed to find volume for %s", mapResId.toString().c_str()); return SCI_ERROR_NO_RESOURCE_FILES_FOUND; } Common::SeekableReadStream *fileStream = getVolumeFile(src); if (!fileStream) { warning("Failed to open file stream for %s", src->getLocationName().c_str()); return SCI_ERROR_NO_RESOURCE_FILES_FOUND; } const uint32 srcSize = fileStream->size(); disposeVolumeFileStream(fileStream, src); SciSpan<const byte>::const_iterator ptr = mapRes->cbegin(); uint32 entrySize = 0; if (_volVersion >= kResVersionSci2) { // The heuristic size detection is incompatible with at least Torin RU, // which is fine because it is not needed for SCI32 entrySize = 11; } else { // Heuristic to detect entry size for (int i = mapRes->size() - 1; i >= 0; --i) { if (ptr[i] == 0xff) entrySize++; else break; } } if (map->_mapNumber == 65535) { while (ptr != mapRes->cend()) { uint16 n = ptr.getUint16LE(); ptr += 2; if (n == 0xffff) break; if (entrySize == 6) { offset = ptr.getUint32LE(); ptr += 4; } else { offset += ptr.getUint24LE(); ptr += 3; } addResource(ResourceId(kResourceTypeAudio, n), src, offset, 0, map->getLocationName()); } } else if (map->_mapNumber == 0 && entrySize == 10 && ptr[3] == 0) { // QFG3 demo format // ptr[3] would be 'seq' in the normal format and cannot possibly be 0 while (ptr != mapRes->cend()) { uint16 n = ptr.getUint16BE(); ptr += 2; if (n == 0xffff) break; offset = ptr.getUint32LE(); ptr += 4; uint32 size = ptr.getUint32LE(); ptr += 4; addResource(ResourceId(kResourceTypeAudio, n), src, offset, size, map->getLocationName()); } } else if (map->_mapNumber == 0 && entrySize == 8 && (ptr + 2).getUint16LE() == 0xffff) { // LB2 Floppy/Mother Goose SCI1.1 format Common::SeekableReadStream *stream = getVolumeFile(src); while (ptr != mapRes->cend()) { uint16 n = ptr.getUint16LE(); ptr += 4; if (n == 0xffff) break; const ResourceId audioResId(kResourceTypeAudio, n); offset = ptr.getUint32LE(); ptr += 4; uint32 size; if (src->getAudioCompressionType() == 0) { // The size is not stored in the map and the entries have no order. // We need to dig into the audio resource in the volume to get the size. stream->seek(offset + 1); byte headerSize = stream->readByte(); if (headerSize != 11 && headerSize != 12) { error("Unexpected header size in %s: should be 11 or 12, got %d", audioResId.toString().c_str(), headerSize); } stream->skip(7); size = stream->readUint32LE() + headerSize + 2; } else { size = 0; } addResource(audioResId, src, offset, size, map->getLocationName()); } disposeVolumeFileStream(stream, src); } else { // EQ1CD & SQ4CD are "early" games; KQ6CD and all SCI32 are "late" games const bool isEarly = (entrySize != 11); if (!isEarly) { offset = ptr.getUint32LE(); ptr += 4; } enum { kRaveFlag = 0x40, kSyncFlag = 0x80, kEndOfMapFlag = 0xFF }; while (ptr != mapRes->cend()) { uint32 n = ptr.getUint32BE(); uint32 syncSize = 0; ptr += 4; // Checking the entire tuple breaks Torin RU and is not how SSCI // works if ((n & kEndOfMapFlag) == kEndOfMapFlag) { const uint32 bytesLeft = mapRes->cend() - ptr; if (bytesLeft >= entrySize) { warning("End of %s reached, but %u entries remain", mapResId.toString().c_str(), bytesLeft / entrySize); } break; } // GK1CD has a message whose audio36 resource has the wrong tuple and never plays. // The message tuple is 420 2 32 0 1 but the audio36 tuple is 420 2 32 3 1. bug #10819 if (g_sci->getGameId() == GID_GK1 && g_sci->isCD() && map->_mapNumber == 420 && n == 0x02200301) { n = 0x02200001; } // QFG4CD has a message whose audio36 resource has the wrong tuple and never plays. // The message tuple is 510 23 1 0 1 but the audio36 tuple is 510 199 1 0 1. bug #10848 if (g_sci->getGameId() == GID_QFG4 && g_sci->isCD() && map->_mapNumber == 510 && n == 0xc7010001) { n = 0x17010001; } // QFG4CD has an orphaned audio36 resource that additionally has the wrong tuple. // The audio36 tuple is 520 2 59 0 3. The message would be 520 2 59 0 2. bug #10849 // We restore the missing message in message.cpp. if (g_sci->getGameId() == GID_QFG4 && g_sci->isCD() && map->_mapNumber == 520 && n == 0x023b0003) { n = 0x023b0002; } if (isEarly) { offset = ptr.getUint32LE(); ptr += 4; } else { offset += ptr.getUint24LE(); ptr += 3; } if (isEarly || (n & kSyncFlag)) { syncSize = ptr.getUint16LE(); ptr += 2; // FIXME: The sync36 resource seems to be two bytes too big in KQ6CD // (bytes taken from the RAVE resource right after it) if (syncSize > 0) { // TODO: Add a mechanism to handle cases with missing resources like the ones below // // LB2CD is missing the sync resource for message 1885 1 6 30 2 but it's a duplicate // of 1885 1 6 16 2 which does have a sync resource so use that for both. bug #9956 if (g_sci->getGameId() == GID_LAURABOW2 && map->_mapNumber == 1885 && n == 0x01061002) { addResource(ResourceId(kResourceTypeSync36, map->_mapNumber, 0x01061e02), src, offset, syncSize, map->getLocationName()); } addResource(ResourceId(kResourceTypeSync36, map->_mapNumber, n & 0xffffff3f), src, offset, syncSize, map->getLocationName()); } } // Checking for this 0x40 flag breaks at least Laura Bow 2 CD 1.1 // map 448 if (g_sci->getGameId() == GID_KQ6 && (n & kRaveFlag)) { // This seems to define the size of raw lipsync data (at least // in KQ6 CD Windows). uint32 kq6HiresSyncSize = ptr.getUint16LE(); ptr += 2; if (kq6HiresSyncSize > 0) { // Rave resources do not have separate entries in the audio // map (their data was just appended to sync resources), so // we have to use the sync resource offset first and then // adjust the offset & size later, otherwise offset // validation will fail for compressed volumes (since the // relocation table in a compressed volume only contains // offsets that existed in the original audio map) Resource *res = addResource(ResourceId(kResourceTypeRave, map->_mapNumber, n & 0xffffff3f), src, offset, syncSize + kq6HiresSyncSize, map->getLocationName()); res->_fileOffset += syncSize; res->_size -= syncSize; syncSize += kq6HiresSyncSize; } } const ResourceId id(kResourceTypeAudio36, map->_mapNumber, n & 0xffffff3f); // Map 405 on CD 1 of the US release of PQ:SWAT 1.000 is broken // and points to garbage in the RESOURCE.AUD. The affected audio36 // assets seem to be able to load successfully from one of the later // CDs, so just ignore the map on this disc if (g_sci->getGameId() == GID_PQSWAT && g_sci->getLanguage() == Common::EN_ANY && map->_volumeNumber == 1 && map->_mapNumber == 405) { continue; } if (g_sci->getGameId() == GID_GK2) { // At least version 1.00 of the US release, and the German // release, of GK2 have multiple invalid audio36 map entries on // CD 6 if (map->_volumeNumber == 6 && offset + syncSize >= srcSize) { bool skip; switch (g_sci->getLanguage()) { case Common::EN_ANY: skip = (map->_mapNumber == 22 || map->_mapNumber == 160); break; case Common::DE_DEU: skip = (map->_mapNumber == 22); break; default: skip = false; } if (skip) { continue; } } // Map 2020 on CD 1 of the German release of GK2 is invalid. // This content does not appear to ever be used by the game (it // does not even exist in the US release), and there is a // correct copy of it on CD 6, so just ignore the bad copy on // CD 1 if (g_sci->getLanguage() == Common::DE_DEU && map->_volumeNumber == 1 && map->_mapNumber == 2020) { continue; } } // Map 800 and 4176 contain content that was cut from the game. The // French version of the game includes map files from the US // release, but the audio resources are French so the maps don't // match. Since the content was never used, just ignore these maps // everywhere if (g_sci->getGameId() == GID_PHANTASMAGORIA2 && (map->_mapNumber == 800 || map->_mapNumber == 4176)) { continue; } addResource(id, src, offset + syncSize, 0, map->getLocationName()); } } mapRes->unalloc(); return 0; }