[Scummvm-cvs-logs] scummvm master -> 693d5e662595f60eed41feb5254a28bbd318094e

m-kiewitz m_kiewitz at users.sourceforge.net
Wed Dec 11 08:25:53 CET 2013


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
693d5e6625 SCI: rave support (KQ6 hires portrait lip sync)


Commit: 693d5e662595f60eed41feb5254a28bbd318094e
    https://github.com/scummvm/scummvm/commit/693d5e662595f60eed41feb5254a28bbd318094e
Author: Martin Kiewitz (m_kiewitz at users.sourceforge.net)
Date: 2013-12-10T23:25:23-08:00

Commit Message:
SCI: rave support (KQ6 hires portrait lip sync)

Thanks to wjp and [md5] for helping

Changed paths:
    engines/sci/graphics/portrait.cpp
    engines/sci/graphics/portrait.h
    engines/sci/resource_audio.cpp



diff --git a/engines/sci/graphics/portrait.cpp b/engines/sci/graphics/portrait.cpp
index a39bcfc..7217af5 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 75baa9a..de0dbff 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 8c39edc..268180b 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:






More information about the Scummvm-git-logs mailing list