diff options
Diffstat (limited to 'engines/sci/graphics/portrait.cpp')
-rw-r--r-- | engines/sci/graphics/portrait.cpp | 265 |
1 files changed, 257 insertions, 8 deletions
diff --git a/engines/sci/graphics/portrait.cpp b/engines/sci/graphics/portrait.cpp index d5227126e2..488450485d 100644 --- a/engines/sci/graphics/portrait.cpp +++ b/engines/sci/graphics/portrait.cpp @@ -8,12 +8,12 @@ * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. - + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. @@ -21,6 +21,7 @@ */ #include "common/archive.h" +#include "common/file.h" // for DumpFile #include "common/system.h" #include "sci/sci.h" @@ -39,6 +40,7 @@ Portrait::Portrait(ResourceManager *resMan, EventManager *event, GfxScreen *scre } Portrait::~Portrait() { + delete[] _lipSyncDataOffsetTable; delete[] _bitmaps; delete[] _fileData; } @@ -51,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 @@ -81,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); @@ -128,17 +132,103 @@ 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 } +// use this to print out kPortrait debug data +//#define DEBUG_PORTRAIT +// use this to use sync resources instead of rave resources (rave resources are better though) +//#define DEBUG_PORTRAIT_USE_SYNC_RESOURCES + void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint16 verb, uint16 cond, uint16 seq) { _position = position; // Now init audio and sync resource uint32 audioNumber = ((noun & 0xff) << 24) | ((verb & 0xff) << 16) | ((cond & 0xff) << 8) | (seq & 0xff); +#ifndef DEBUG_PORTRAIT_USE_SYNC_RESOURCES + ResourceId raveResourceId = ResourceId(kResourceTypeRave, resourceId, noun, verb, cond, seq); + Resource *raveResource = _resMan->findResource(raveResourceId, true); + uint raveOffset = 0; +#else ResourceId syncResourceId = ResourceId(kResourceTypeSync36, resourceId, noun, verb, cond, seq); Resource *syncResource = _resMan->findResource(syncResourceId, true); uint syncOffset = 0; +#endif + +#ifdef DEBUG_PORTRAIT + // prints out the current lip sync ASCII data + char debugPrint[4000]; + if (raveResource->size < 4000) { + memcpy(debugPrint, raveResource->data, raveResource->size); + debugPrint[raveResource->size] = 0; // set terminating NUL + debug("kPortrait (noun %d, verb %d, cond %d, seq %d)", noun, verb, cond, seq); + debug("kPortrait: %s", debugPrint); + } +#endif + + // TODO: maybe try to create the missing sync resources for low-res KQ6 out of the rave resources + +#ifdef DEBUG_PORTRAIT_USE_SYNC_RESOURCES + // Dump the sync resources to disk + Common::DumpFile *outFile = new Common::DumpFile(); + Common::String outName = syncResourceId.toPatchNameBase36() + ".sync36"; + outFile->open(outName); + syncResource->writeToStream(outFile); + outFile->finalize(); + outFile->close(); + + ResourceId raveResourceId = ResourceId(kResourceTypeRave, resourceId, noun, verb, cond, seq); + Resource *raveResource = _resMan->findResource(raveResourceId, true); + outName = raveResourceId.toPatchNameBase36() + ".rave"; + outFile->open(outName); + raveResource->writeToStream(outFile); + outFile->finalize(); + outFile->close(); + _resMan->unlockResource(raveResource); + + delete outFile; +#endif // Set the portrait palette _palette->set(&_portraitPalette, false, true); @@ -151,6 +241,90 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint _audio->stopAudio(); _audio->startAudio(resourceId, audioNumber); +#ifndef DEBUG_PORTRAIT_USE_SYNC_RESOURCES + 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++; + } + } + } +#else if (!syncResource) { // Getting the book in the book shop calls kPortrait where no sync exists // TODO: find out what to do then @@ -197,15 +371,90 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint } } } - +#endif + + // Reset the portrait bitmap to "closed mouth" state (rave.dll seems to do the same) + drawBitmap(0); + bitsShow(); if (userAbort) { - // Reset the portrait bitmap to "closed mouth" state, when skipping dialogs - drawBitmap(0); - bitsShow(); _audio->stopAudio(); } +#ifndef DEBUG_PORTRAIT_USE_SYNC_RESOURCES + _resMan->unlockResource(raveResource); +#else _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) { |