aboutsummaryrefslogtreecommitdiff
path: root/engines/sci
diff options
context:
space:
mode:
authorMartin Kiewitz2013-12-11 08:25:23 +0100
committerMartin Kiewitz2013-12-11 08:25:23 +0100
commit693d5e662595f60eed41feb5254a28bbd318094e (patch)
tree4d7461880aca44a8f9d517c326b306ddc45cecef /engines/sci
parent298f4a5c065c1dded3c3462ea5bfb97814f370e1 (diff)
downloadscummvm-rg350-693d5e662595f60eed41feb5254a28bbd318094e.tar.gz
scummvm-rg350-693d5e662595f60eed41feb5254a28bbd318094e.tar.bz2
scummvm-rg350-693d5e662595f60eed41feb5254a28bbd318094e.zip
SCI: rave support (KQ6 hires portrait lip sync)
Thanks to wjp and [md5] for helping
Diffstat (limited to 'engines/sci')
-rw-r--r--engines/sci/graphics/portrait.cpp222
-rw-r--r--engines/sci/graphics/portrait.h11
-rw-r--r--engines/sci/resource_audio.cpp43
3 files changed, 252 insertions, 24 deletions
diff --git a/engines/sci/graphics/portrait.cpp b/engines/sci/graphics/portrait.cpp
index a39bcfcd88..7217af51f7 100644
--- a/engines/sci/graphics/portrait.cpp
+++ b/engines/sci/graphics/portrait.cpp
@@ -40,6 +40,7 @@ Portrait::Portrait(ResourceManager *resMan, EventManager *event, GfxScreen *scre
}
Portrait::~Portrait() {
+ delete[] _lipSyncDataOffsetTable;
delete[] _bitmaps;
delete[] _fileData;
}
@@ -52,7 +53,7 @@ void Portrait::init() {
// 2 bytes main height (should be the same as first bitmap header height)
// 2 bytes animation count
// 2 bytes unknown
- // 2 bytes unknown
+ // 2 bytes lip sync ID count
// 4 bytes paletteSize (base 1)
// -> 17 bytes
// paletteSize bytes paletteData
@@ -82,6 +83,8 @@ void Portrait::init() {
_width = READ_LE_UINT16(_fileData + 3);
_height = READ_LE_UINT16(_fileData + 5);
_bitmapCount = READ_LE_UINT16(_fileData + 7);
+ _lipSyncIDCount = READ_LE_UINT16(_fileData + 11);
+
_bitmaps = new PortraitBitmap[_bitmapCount];
uint16 portraitPaletteSize = READ_LE_UINT16(_fileData + 13);
@@ -129,7 +132,48 @@ void Portrait::init() {
}
data += offsetTableSize;
- // raw lip-sync data follows
+ // raw lip-sync ID table follows
+ uint32 lipSyncIDTableSize;
+
+ lipSyncIDTableSize = READ_LE_UINT32(data);
+ data += 4;
+ assert( lipSyncIDTableSize == (_lipSyncIDCount * 4) );
+ _lipSyncIDTable = data;
+ data += lipSyncIDTableSize;
+
+ // raw lip-sync frame table follows
+ uint32 lipSyncDataTableSize;
+ uint32 lipSyncDataTableLastOffset;
+ byte lipSyncData;
+ uint16 lipSyncDataNr;
+ uint16 lipSyncCurOffset;
+
+ lipSyncDataTableSize = READ_LE_UINT32(data);
+ data += 4;
+ assert( lipSyncDataTableSize == 0x220 ); // always this size, just a safety-check
+
+ _lipSyncData = data;
+ lipSyncDataTableLastOffset = lipSyncDataTableSize - 1;
+ _lipSyncDataOffsetTable = new uint16[ _lipSyncIDCount ];
+
+ lipSyncDataNr = 0;
+ lipSyncCurOffset = 0;
+ while ( (lipSyncCurOffset < lipSyncDataTableSize) && (lipSyncDataNr < _lipSyncIDCount) ) {
+ // We are currently at the start of ID-frame data
+ _lipSyncDataOffsetTable[lipSyncDataNr] = lipSyncCurOffset;
+
+ // Look for end of ID-frame data
+ lipSyncData = *data++; lipSyncCurOffset++;
+ while ( (lipSyncData != 0xFF) && (lipSyncCurOffset < lipSyncDataTableLastOffset) ) {
+ // Either terminator (0xFF) or frame-data (1 byte tick count and 1 byte bitmap ID)
+ data++;
+ lipSyncData = *data++;
+ lipSyncCurOffset += 2;
+ }
+ lipSyncDataNr++;
+ }
+ _lipSyncDataOffsetTableEnd = data;
+ // last 4 bytes seem to be garbage
}
void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint16 verb, uint16 cond, uint16 seq) {
@@ -137,10 +181,20 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint
// Now init audio and sync resource
uint32 audioNumber = ((noun & 0xff) << 24) | ((verb & 0xff) << 16) | ((cond & 0xff) << 8) | (seq & 0xff);
- ResourceId syncResourceId = ResourceId(kResourceTypeSync36, resourceId, noun, verb, cond, seq);
- Resource *syncResource = _resMan->findResource(syncResourceId, true);
+ //ResourceId syncResourceId = ResourceId(kResourceTypeSync36, resourceId, noun, verb, cond, seq);
+ //Resource *syncResource = _resMan->findResource(syncResourceId, true);
+ ResourceId raveResourceId = ResourceId(kResourceTypeRave, resourceId, noun, verb, cond, seq);
+ Resource *raveResource = _resMan->findResource(raveResourceId, true);
+
+#if 0
uint syncOffset = 0;
+#endif
+ // TODO: play through the game if this is 100% accurate
+ // TODO: maybe try to create the missing sync resources for low-res KQ6 out of the rave resources
+
+ uint raveOffset = 0;
+
#if 0
// Dump the sync resources to disk
Common::DumpFile *outFile = new Common::DumpFile();
@@ -172,7 +226,92 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint
// Start playing audio...
_audio->stopAudio();
_audio->startAudio(resourceId, audioNumber);
+
+ if (!raveResource) {
+ warning("kPortrait: no rave resource %d %X", resourceId, audioNumber);
+ return;
+ }
+
+ // Do animation depending on rave resource till audio is done playing
+ int16 raveTicks;
+ uint16 raveID;
+ byte *raveLipSyncData;
+ byte raveLipSyncTicks;
+ byte raveLipSyncBitmapNr;
+ int timerPosition = 0;
+ int timerPositionWithin = 0;
+ int curPosition;
+ SciEvent curEvent;
+ bool userAbort = false;
+
+ while ((raveOffset < raveResource->size) && (!userAbort)) {
+ // rave string starts with tick count, followed by lipSyncID, tick count and so on
+ raveTicks = raveGetTicks(raveResource, &raveOffset);
+ if (raveTicks < 0)
+ break;
+
+ // get lipSyncID
+ raveID = raveGetID(raveResource, &raveOffset);
+ if (raveID) {
+ raveLipSyncData = raveGetLipSyncData(raveID);
+ } else {
+ raveLipSyncData = NULL;
+ }
+
+ timerPosition += raveTicks;
+ // Wait till syncTime passed, then show specific animation bitmap
+ if (timerPosition > 0) {
+ do {
+ g_sci->getEngineState()->wait(1);
+ curEvent = _event->getSciEvent(SCI_EVENT_ANY);
+ if (curEvent.type == SCI_EVENT_MOUSE_PRESS ||
+ (curEvent.type == SCI_EVENT_KEYBOARD && curEvent.data == SCI_KEY_ESC) ||
+ g_sci->getEngineState()->abortScriptProcessing == kAbortQuitGame)
+ userAbort = true;
+ curPosition = _audio->getAudioPosition();
+ } while ((curPosition != -1) && (curPosition < timerPosition) && (!userAbort));
+ }
+
+ if (raveLipSyncData) {
+ // lip sync data is
+ // Tick:Byte, Bitmap-Nr:BYTE
+ // Tick = 0xFF is the terminator for the data
+ timerPositionWithin = timerPosition;
+ raveLipSyncTicks = *raveLipSyncData++;
+ while ( (raveLipSyncData < _lipSyncDataOffsetTableEnd) && (raveLipSyncTicks != 0xFF) ) {
+ timerPositionWithin += raveLipSyncTicks;
+
+ do {
+ g_sci->getEngineState()->wait(1);
+ curEvent = _event->getSciEvent(SCI_EVENT_ANY);
+ if (curEvent.type == SCI_EVENT_MOUSE_PRESS ||
+ (curEvent.type == SCI_EVENT_KEYBOARD && curEvent.data == SCI_KEY_ESC) ||
+ g_sci->getEngineState()->abortScriptProcessing == kAbortQuitGame)
+ userAbort = true;
+ curPosition = _audio->getAudioPosition();
+ } while ((curPosition != -1) && (curPosition < timerPositionWithin) && (!userAbort));
+
+ raveLipSyncBitmapNr = *raveLipSyncData++;
+
+ // bitmap nr within sync data is base 1, we need base 0
+ raveLipSyncBitmapNr--;
+
+ if (raveLipSyncBitmapNr < _bitmapCount) {
+ drawBitmap(0);
+ drawBitmap(raveLipSyncBitmapNr);
+ bitsShow();
+ } else {
+ warning("kPortrait: rave lip sync data tried to draw non-existent bitmap %d", raveLipSyncBitmapNr);
+ }
+
+ raveLipSyncTicks = *raveLipSyncData++;
+ }
+ }
+ }
+
+// old sync resource code
+#if 0
if (!syncResource) {
// Getting the book in the book shop calls kPortrait where no sync exists
// TODO: find out what to do then
@@ -219,6 +358,7 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint
}
}
}
+#endif
if (userAbort) {
// Reset the portrait bitmap to "closed mouth" state, when skipping dialogs
@@ -227,7 +367,81 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint
_audio->stopAudio();
}
+ _resMan->unlockResource(raveResource);
+
+#if 0
_resMan->unlockResource(syncResource);
+#endif
+}
+
+// returns ASCII ticks from lip sync string as uint16
+int16 Portrait::raveGetTicks(Resource *resource, uint *offset) {
+ uint curOffset = *offset;
+ byte *curData = resource->data + curOffset;
+ byte curByte;
+ uint16 curValue = 0;
+
+ if (curOffset >= resource->size)
+ return -1;
+
+ while (curOffset < resource->size) {
+ curByte = *curData++; curOffset++;
+ if ( curByte == ' ' )
+ break;
+ if ( (curByte >= '0') && (curByte <= '9') ) {
+ curValue = curValue * 10 + ( curByte - '0' );
+ } else {
+ // no number -> assume there is an ID at current offset
+ return 0;
+ }
+ }
+ *offset = curOffset;
+ return curValue;
+}
+
+// returns ASCII ID from lip sync string as uint16
+uint16 Portrait::raveGetID(Resource *resource, uint *offset) {
+ uint curOffset = *offset;
+ byte *curData = resource->data + curOffset;
+ byte curByte = 0;
+ uint16 curValue = 0;
+
+ while (curOffset < resource->size) {
+ curByte = *curData++; curOffset++;
+ if ( curByte == ' ' )
+ break;
+ if (!curValue) {
+ curValue = curByte << 8;
+ } else {
+ curValue |= curByte;
+ }
+ }
+
+ *offset = curOffset;
+ return curValue;
+}
+
+// Searches for a specific lip sync ID and returns pointer to lip sync data or NULL in case ID was not found
+byte *Portrait::raveGetLipSyncData(uint16 raveID) {
+ uint lipSyncIDNr = 0;
+ byte *lipSyncIDPtr = _lipSyncIDTable;
+ byte lipSyncIDByte1, lipSyncIDByte2;
+ uint16 lipSyncID;
+
+ lipSyncIDPtr++; // skip over first byte
+ while (lipSyncIDNr < _lipSyncIDCount) {
+ lipSyncIDByte1 = *lipSyncIDPtr++;
+ lipSyncIDByte2 = *lipSyncIDPtr++;
+ lipSyncID = ( lipSyncIDByte1 << 8 ) | lipSyncIDByte2;
+
+ if ( lipSyncID == raveID ) {
+ return _lipSyncData + _lipSyncDataOffsetTable[lipSyncIDNr];
+ }
+
+ lipSyncIDNr++;
+ lipSyncIDPtr += 2; // ID is every 4 bytes
+ }
+ return NULL;
}
void Portrait::drawBitmap(uint16 bitmapNr) {
diff --git a/engines/sci/graphics/portrait.h b/engines/sci/graphics/portrait.h
index 75baa9a56b..de0dbffb3f 100644
--- a/engines/sci/graphics/portrait.h
+++ b/engines/sci/graphics/portrait.h
@@ -52,6 +52,10 @@ private:
void drawBitmap(uint16 bitmapNr);
void bitsShow();
+ int16 raveGetTicks(Resource *resource, uint *offset);
+ uint16 raveGetID(Resource *resource, uint *offset);
+ byte *raveGetLipSyncData(uint16 raveID);
+
ResourceManager *_resMan;
EventManager *_event;
GfxPalette *_palette;
@@ -68,6 +72,13 @@ private:
Common::String _resourceName;
byte *_fileData;
+
+ uint32 _lipSyncIDCount;
+ byte *_lipSyncIDTable;
+
+ byte *_lipSyncData;
+ uint16 *_lipSyncDataOffsetTable;
+ byte *_lipSyncDataOffsetTableEnd;
Common::Point _position;
};
diff --git a/engines/sci/resource_audio.cpp b/engines/sci/resource_audio.cpp
index 8c39edc42e..268180b531 100644
--- a/engines/sci/resource_audio.cpp
+++ b/engines/sci/resource_audio.cpp
@@ -101,32 +101,34 @@ bool Resource::loadFromAudioVolumeSCI11(Common::SeekableReadStream *file) {
}
file->seek(-4, SEEK_CUR);
- ResourceType type = _resMan->convertResType(file->readByte());
- if (((getType() == kResourceTypeAudio || getType() == kResourceTypeAudio36) && (type != kResourceTypeAudio))
- || ((getType() == kResourceTypeSync || getType() == kResourceTypeSync36) && (type != kResourceTypeSync))) {
- warning("Resource type mismatch loading %s", _id.toString().c_str());
- unalloc();
- return false;
- }
-
- _headerSize = file->readByte();
-
- if (type == kResourceTypeAudio) {
- if (_headerSize != 7 && _headerSize != 11 && _headerSize != 12) {
- warning("Unsupported audio header");
+ // Rave-resources (King's Quest 6) don't have any header at all
+ if (getType() != kResourceTypeRave) {
+ ResourceType type = _resMan->convertResType(file->readByte());
+ if (((getType() == kResourceTypeAudio || getType() == kResourceTypeAudio36) && (type != kResourceTypeAudio))
+ || ((getType() == kResourceTypeSync || getType() == kResourceTypeSync36) && (type != kResourceTypeSync))) {
+ warning("Resource type mismatch loading %s", _id.toString().c_str());
unalloc();
return false;
}
+
+ _headerSize = file->readByte();
+
+ if (type == kResourceTypeAudio) {
+ if (_headerSize != 7 && _headerSize != 11 && _headerSize != 12) {
+ warning("Unsupported audio header");
+ unalloc();
+ return false;
+ }
- if (_headerSize != 7) { // Size is defined already from the map
- // Load sample size
- file->seek(7, SEEK_CUR);
- size = file->readUint32LE();
- // Adjust offset to point at the header data again
- file->seek(-11, SEEK_CUR);
+ if (_headerSize != 7) { // Size is defined already from the map
+ // Load sample size
+ file->seek(7, SEEK_CUR);
+ size = file->readUint32LE();
+ // Adjust offset to point at the header data again
+ file->seek(-11, SEEK_CUR);
+ }
}
}
-
return loadPatch(file);
}
@@ -863,6 +865,7 @@ void AudioVolumeResourceSource::loadResource(ResourceManager *resMan, Resource *
switch (res->getType()) {
case kResourceTypeSync:
case kResourceTypeSync36:
+ case kResourceTypeRave:
// we should already have a (valid) size
break;
default: