diff options
Diffstat (limited to 'engines/sci/resource.cpp')
-rw-r--r-- | engines/sci/resource.cpp | 293 |
1 files changed, 207 insertions, 86 deletions
diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp index f9ae45b6cd..24b4d9beed 100644 --- a/engines/sci/resource.cpp +++ b/engines/sci/resource.cpp @@ -63,7 +63,7 @@ enum SolFlags { kSolFlagIsSigned = 1 << 3 }; -const char *sci_error_types[] = { +static const char *sci_error_types[] = { "No error", "I/O error", "Resource is empty (size 0)", @@ -78,7 +78,7 @@ const char *sci_error_types[] = { }; // These are the 20 resource types supported by SCI1.1 -const char *resourceTypeNames[] = { +static const char *resourceTypeNames[] = { "view", "pic", "script", "text", "sound", "memory", "vocab", "font", "cursor", "patch", "bitmap", "palette", "cdaudio", @@ -86,7 +86,7 @@ const char *resourceTypeNames[] = { "audio36", "sync36" }; -const char *resourceTypeSuffixes[] = { +static const char *resourceTypeSuffixes[] = { "v56", "p56", "scr", "tex", "snd", " ", "voc", "fon", "cur", "pat", "bit", "pal", "cda", "aud", "syn", @@ -97,13 +97,6 @@ const char *getResourceTypeName(ResourceType restype) { return resourceTypeNames[restype]; } -const char *getResourceTypeSuffix(ResourceType restype) { - return resourceTypeSuffixes[restype]; -} - -typedef int decomp_funct(Resource *result, Common::ReadStream &stream, int sci_version); -typedef void patch_sprintf_funct(char *string, Resource *res); - //-- Resource main functions -- Resource::Resource() { data = NULL; @@ -290,7 +283,7 @@ int sci0_get_compression_method(Common::ReadStream &stream) { return compressionMethod; } -int sci_test_view_type(ResourceManager *mgr) { +int ResourceManager::guessSciVersion() { Common::File file; char filename[MAXPATHLEN]; int compression; @@ -298,7 +291,7 @@ int sci_test_view_type(ResourceManager *mgr) { int i; for (i = 0; i < 1000; i++) { - res = mgr->testResource(kResourceTypeView, i); + res = testResource(kResourceTypeView, i); if (!res) continue; @@ -322,7 +315,7 @@ int sci_test_view_type(ResourceManager *mgr) { // Try the same thing with pics for (i = 0; i < 1000; i++) { - res = mgr->testResource(kResourceTypePic, i); + res = testResource(kResourceTypePic, i); if (!res) continue; @@ -442,14 +435,14 @@ ResourceManager::ResourceManager(int version, int maxMemory) { switch (_mapVersion) { case SCI_VERSION_0: if (testResource(kResourceTypeVocab, VOCAB_RESOURCE_SCI0_MAIN_VOCAB)) { - version = sci_test_view_type(this) ? SCI_VERSION_01_VGA : SCI_VERSION_0; + version = guessSciVersion() ? SCI_VERSION_01_VGA : SCI_VERSION_0; } else if (testResource(kResourceTypeVocab, VOCAB_RESOURCE_SCI1_MAIN_VOCAB)) { - version = sci_test_view_type(this); + version = guessSciVersion(); if (version != SCI_VERSION_01_VGA) { version = testResource(kResourceTypeVocab, 912) ? SCI_VERSION_0 : SCI_VERSION_01; } } else { - version = sci_test_view_type(this) ? SCI_VERSION_01_VGA : SCI_VERSION_0; + version = guessSciVersion() ? SCI_VERSION_01_VGA : SCI_VERSION_0; } break; case SCI_VERSION_01_VGA_ODD: @@ -586,7 +579,7 @@ void ResourceManager::freeOldResources(int last_invulnerable) { } } -Resource *ResourceManager::findResource(ResourceType type, int number, int lock) { +Resource *ResourceManager::findResource(ResourceType type, int number, bool lock) { Resource *retval; if (number >= sci_max_resource_nr[_sciVersion]) { @@ -863,7 +856,7 @@ void ResourceManager::readResourcePatches(ResourceSource *source) { SearchMan.listMatchingMembers(files, mask); // SCI1 and later naming - nnn.typ mask = "*."; - mask += getResourceTypeSuffix((ResourceType)i); + mask += resourceTypeSuffixes[i]; SearchMan.listMatchingMembers(files, mask); for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); x++) { bAdd = false; @@ -1178,7 +1171,7 @@ void ResourceSync::stopSync() { AudioResource::AudioResource(ResourceManager *resMgr, int sciVersion) { _resMgr = resMgr; _sciVersion = sciVersion; - _audioRate = 0; + _audioRate = 11025; _lang = 0; _audioMapSCI1 = 0; _audioMapSCI11 = 0; @@ -1238,7 +1231,7 @@ bool AudioResource::findAudEntrySCI1(uint16 audioNumber, byte &volume, uint32 &o return false; byte *ptr = _audioMapSCI1; - while ((n = READ_UINT16(ptr)) != 0xFFFF) { + while ((n = READ_LE_UINT16(ptr)) != 0xFFFF) { if (n == audioNumber) { off = READ_LE_UINT32(ptr + 2); size = READ_LE_UINT32(ptr + 6); @@ -1252,14 +1245,8 @@ bool AudioResource::findAudEntrySCI1(uint16 audioNumber, byte &volume, uint32 &o return false; } -bool AudioResource::findAudEntrySCI11(uint32 audioNumber, uint32 volume, uint32 &offset, bool getSync, uint32 *size) { - // 65535.MAP structure: - // ========= - // 6 byte entries: - // w nEntry - // dw offset - - // Other map files: +bool AudioResource::findAudEntrySCI11Late(uint32 audioNumber, uint32 &offset, bool getSync, uint32 *size) { + // Map structure: // =============== // Header: // dw baseOffset @@ -1269,8 +1256,116 @@ bool AudioResource::findAudEntrySCI11(uint32 audioNumber, uint32 volume, uint32 // b cond // b seq // tb cOffset (cumulative offset) - // w syncSize (iff seq has bits 7 and 6 set) - // w syncAscSize (iff seq has bits 7 and 6 set) + // w syncSize (iff seq has bit 7 set) + // w syncAscSize (iff seq has bit 6 set) + + uint32 n; + offset = 0; + + byte *ptr = _audioMapSCI11->data; + + offset = READ_LE_UINT32(ptr); + ptr += 4; + + while (ptr < _audioMapSCI11->data + _audioMapSCI11->size) { + n = READ_BE_UINT32(ptr); + ptr += 4; + + if (n == 0xffffffff) + break; + + offset += READ_LE_UINT24(ptr); + ptr += 3; + + int syncSkip = 0; + + if (n & 0x80) { + n ^= 0x80; + + if (getSync) { + if (size) + *size = READ_LE_UINT16(ptr); + } else { + syncSkip = READ_LE_UINT16(ptr); + } + + ptr += 2; + + if (n & 0x40) { + n ^= 0x40; + + if (!getSync) + syncSkip += READ_LE_UINT16(ptr); + + ptr += 2; + } + + offset += syncSkip; + + if (n == audioNumber) + return true; + + } else { + if (n == audioNumber) + return !getSync; + } + + offset -= syncSkip; + } + + return false; +} + +bool AudioResource::findAudEntrySCI11Early(uint32 audioNumber, uint32 &offset, bool getSync, uint32 *size) { + // Map structure: + // =============== + // 10-byte entries: + // b noun + // b verb + // b cond + // b seq + // dw offset + // w syncSize + syncAscSize + + uint32 n; + offset = 0; + + byte *ptr = _audioMapSCI11->data; + + while (ptr < _audioMapSCI11->data + _audioMapSCI11->size) { + n = READ_BE_UINT32(ptr); + ptr += 4; + + if (n == 0xffffffff) + break; + + offset = READ_LE_UINT32(ptr); + ptr += 4; + + int syncSize = READ_LE_UINT16(ptr); + ptr += 2; + + if (n == audioNumber) { + if (getSync) { + if (size) + *size = syncSize; + return true; + } else { + offset += syncSize; + return true; + } + } + } + + return false; +} + +bool AudioResource::findAudEntrySCI11(uint32 audioNumber, uint32 volume, uint32 &offset, bool getSync, uint32 *size) { + // 65535.MAP structure: + // ========= + // 6 byte entries: + // w nEntry + // dw offset uint32 n; offset = 0; @@ -1288,65 +1383,93 @@ bool AudioResource::findAudEntrySCI11(uint32 audioNumber, uint32 volume, uint32 if (volume == 65535) { while (ptr < _audioMapSCI11->data + _audioMapSCI11->size) { - n = READ_UINT16(ptr); + n = READ_LE_UINT16(ptr); ptr += 2; if (n == 0xffff) break; - offset = READ_UINT32(ptr); + offset = READ_LE_UINT32(ptr); ptr += 4; if (n == audioNumber) return true; } } else { - offset = READ_UINT32(ptr); - ptr += 4; + // In early SCI1.1 the map is terminated with 10x 0xff, in late SCI1.1 + // with 11x 0xff. If we look at the 11th last byte in an early SCI1.1 + // map, this will be the high byte of the Sync length of the last entry. + // As Sync resources are relative small, we should never encounter a + // Sync with a size of 0xffnn. As such, the following heuristic should be + // sufficient to tell these map formats apart. + if (_audioMapSCI11->size >= 11 && (ptr[_audioMapSCI11->size - 11] == 0xff)) + return findAudEntrySCI11Late(audioNumber, offset, getSync, size); + else { + return findAudEntrySCI11Early(audioNumber, offset, getSync, size); + } + } - while (ptr < _audioMapSCI11->data + _audioMapSCI11->size) { - n = READ_BE_UINT32(ptr); - ptr += 4; + return false; +} - if (n == 0xffffffff) - break; +// FIXME: Move this to sound/adpcm.cpp? +// Note that the 16-bit version is also used in coktelvideo.cpp +static const uint16 tableDPCM16[128] = { + 0x0000, 0x0008, 0x0010, 0x0020, 0x0030, 0x0040, 0x0050, 0x0060, 0x0070, 0x0080, + 0x0090, 0x00A0, 0x00B0, 0x00C0, 0x00D0, 0x00E0, 0x00F0, 0x0100, 0x0110, 0x0120, + 0x0130, 0x0140, 0x0150, 0x0160, 0x0170, 0x0180, 0x0190, 0x01A0, 0x01B0, 0x01C0, + 0x01D0, 0x01E0, 0x01F0, 0x0200, 0x0208, 0x0210, 0x0218, 0x0220, 0x0228, 0x0230, + 0x0238, 0x0240, 0x0248, 0x0250, 0x0258, 0x0260, 0x0268, 0x0270, 0x0278, 0x0280, + 0x0288, 0x0290, 0x0298, 0x02A0, 0x02A8, 0x02B0, 0x02B8, 0x02C0, 0x02C8, 0x02D0, + 0x02D8, 0x02E0, 0x02E8, 0x02F0, 0x02F8, 0x0300, 0x0308, 0x0310, 0x0318, 0x0320, + 0x0328, 0x0330, 0x0338, 0x0340, 0x0348, 0x0350, 0x0358, 0x0360, 0x0368, 0x0370, + 0x0378, 0x0380, 0x0388, 0x0390, 0x0398, 0x03A0, 0x03A8, 0x03B0, 0x03B8, 0x03C0, + 0x03C8, 0x03D0, 0x03D8, 0x03E0, 0x03E8, 0x03F0, 0x03F8, 0x0400, 0x0440, 0x0480, + 0x04C0, 0x0500, 0x0540, 0x0580, 0x05C0, 0x0600, 0x0640, 0x0680, 0x06C0, 0x0700, + 0x0740, 0x0780, 0x07C0, 0x0800, 0x0900, 0x0A00, 0x0B00, 0x0C00, 0x0D00, 0x0E00, + 0x0F00, 0x1000, 0x1400, 0x1800, 0x1C00, 0x2000, 0x3000, 0x4000 +}; - offset += (READ_UINT16(ptr) | (ptr[2] << 16)); - ptr += 3; +static const byte tableDPCM8[8] = {0, 1, 2, 3, 6, 10, 15, 21}; - int syncSkip = 0; +static void deDPCM16(byte *soundBuf, Common::SeekableReadStream &audioStream, uint32 n) { + int16 *out = (int16 *) soundBuf; - if ((n & 0xc0) == 0xc0) { - n ^= 0xc0; + int32 s = 0; + for (uint32 i = 0; i < n; i++) { + byte b = audioStream.readByte(); + if (b & 0x80) + s -= tableDPCM16[b & 0x7f]; + else + s += tableDPCM16[b]; - if (getSync) { - if (size) - *size = READ_UINT16(ptr); - } else { - syncSkip = READ_UINT16(ptr) + READ_UINT16(ptr + 2); - offset += syncSkip; - } + s = CLIP<int32>(s, -32768, 32767); + *out++ = TO_BE_16(s); + } +} - if (n == audioNumber) - return true; +static void deDPCM8Nibble(byte *soundBuf, int32 &s, byte b) { + if (b & 8) + s -= tableDPCM8[7 - (b & 7)]; + else + s += tableDPCM8[b & 7]; + s = CLIP<int32>(s, 0, 255); + *soundBuf = s; +} - ptr += 4; - } else { - if (n == audioNumber) - return !getSync; - } +static void deDPCM8(byte *soundBuf, Common::SeekableReadStream &audioStream, uint32 n) { + int32 s = 0x80; - offset -= syncSkip; - } - } + for (uint i = 0; i < n; i++) { + byte b = audioStream.readByte(); - return false; + deDPCM8Nibble(soundBuf++, s, b >> 4); + deDPCM8Nibble(soundBuf++, s, b & 0xf); + } } // Sierra SOL audio file reader // Check here for more info: http://wiki.multimedia.cx/index.php?title=Sierra_Audio -// TODO: improve this for later versions of the format, as this currently only reads -// raw PCM encoded files (not ADPCM compressed ones) byte* readSOLAudio(Common::SeekableReadStream *audioStream, uint32 *size, uint16 *audioRate, byte *flags) { byte audioFlags; byte type = audioStream->readByte(); @@ -1357,6 +1480,12 @@ byte* readSOLAudio(Common::SeekableReadStream *audioStream, uint32 *size, uint16 } int headerSize = audioStream->readByte(); + + if (headerSize != 11 && headerSize != 12) { + warning("SOL audio header of size %i not supported", headerSize); + return NULL; + } + audioStream->readUint32LE(); // skip "SOL" + 0 (4 bytes) *audioRate = audioStream->readUint16LE(); audioFlags = audioStream->readByte(); @@ -1365,36 +1494,25 @@ byte* readSOLAudio(Common::SeekableReadStream *audioStream, uint32 *size, uint16 *flags = 0; if (audioFlags & kSolFlag16Bit) *flags |= Audio::Mixer::FLAG_16BITS; - if ((audioFlags & kSolFlagCompressed) || !(audioFlags & kSolFlagIsSigned)) + if (!(audioFlags & kSolFlagIsSigned)) *flags |= Audio::Mixer::FLAG_UNSIGNED; - *size = audioStream->readUint16LE(); + *size = audioStream->readUint32LE(); - if (headerSize == 12) - *size |= audioStream->readByte() << 16; + if (headerSize == 12) { + // Unknown byte + audioStream->readByte(); + } byte *buffer; if (audioFlags & kSolFlagCompressed) { - if (audioFlags & kSolFlag16Bit) { - warning("Unsupported DPCM mode"); - return NULL; - } - buffer = new byte[*size * 2]; - byte sample = 0x80; - - for (uint i = 0; i < *size; i++) { - const int delta[] = {0, 1, 2, 3, 6, 10, 15, 21, -21, -15, -10, -6, -3, -2, -1, -0}; - - byte b = audioStream->readByte(); - - sample += delta[b >> 4]; - buffer[i * 2] = sample; - sample += delta[b & 0xf]; - buffer[i * 2 + 1] = sample; - } + if (audioFlags & kSolFlag16Bit) + deDPCM16(buffer, *audioStream, *size); + else + deDPCM8(buffer, *audioStream, *size); *size *= 2; } else { @@ -1498,7 +1616,10 @@ Audio::AudioStream* AudioResource::getAudioStream(uint32 audioNumber, uint32 vol } } - *sampleLen = size * 60 / _audioRate; + *sampleLen = (flags & Audio::Mixer::FLAG_16BITS ? size >> 1 : size) * 60 / _audioRate; + } else { + warning("Failed to find audio entry (%i, %i, %i, %i, %i)", volume, (audioNumber >> 24) & 0xff, + (audioNumber >> 16) & 0xff, (audioNumber >> 8) & 0xff, audioNumber & 0xff); } return audioStream; |