diff options
Diffstat (limited to 'engines')
81 files changed, 1009 insertions, 1643 deletions
diff --git a/engines/agi/detection.cpp b/engines/agi/detection.cpp index 805fe7d366..5f7780bfe3 100644 --- a/engines/agi/detection.cpp +++ b/engines/agi/detection.cpp @@ -20,9 +20,6 @@ * */ -// FIXME: Avoid using printf -#define FORBIDDEN_SYMBOL_EXCEPTION_printf - #include "base/plugins.h" #include "engines/advancedDetector.h" @@ -491,10 +488,14 @@ const ADGameDescription *AgiMetaEngine::fallbackDetect(const FileMap &allFilesXX g_fallbackDesc.desc.gameid = _gameid.c_str(); g_fallbackDesc.desc.extra = _extra.c_str(); - printf("Your game version has been detected using fallback matching as a\n"); - printf("variant of %s (%s).\n", g_fallbackDesc.desc.gameid, g_fallbackDesc.desc.extra); - printf("If this is an original and unmodified version or new made Fanmade game,\n"); - printf("please report any, information previously printed by ScummVM to the team.\n"); + Common::String fallbackWarning; + + fallbackWarning = "Your game version has been detected using fallback matching as a\n"; + fallbackWarning += Common::String::format("variant of %s (%s).\n", g_fallbackDesc.desc.gameid, g_fallbackDesc.desc.extra); + fallbackWarning += "If this is an original and unmodified version or new made Fanmade game,\n"; + fallbackWarning += "please report any, information previously printed by ScummVM to the team.\n"; + + g_system->logMessage(LogMessageType::kWarning, fallbackWarning.c_str()); return (const ADGameDescription *)&g_fallbackDesc; } diff --git a/engines/agos/animation.cpp b/engines/agos/animation.cpp index 10c01741ae..9176412e0e 100644 --- a/engines/agos/animation.cpp +++ b/engines/agos/animation.cpp @@ -260,9 +260,6 @@ bool MoviePlayerDXA::load() { debug(0, "Playing video %s", videoName.c_str()); CursorMan.showMouse(false); - - _firstFrameOffset = _fileStream->pos(); - return true; } @@ -271,6 +268,10 @@ void MoviePlayerDXA::copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch) { uint w = getWidth(); const Graphics::Surface *surface = decodeNextFrame(); + + if (!surface) + return; + byte *src = (byte *)surface->pixels; dst += y * pitch + x; @@ -281,7 +282,7 @@ void MoviePlayerDXA::copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch) { } while (--h); if (hasDirtyPalette()) - setSystemPalette(); + g_system->getPaletteManager()->setPalette(getPalette(), 0, 256); } void MoviePlayerDXA::playVideo() { @@ -302,34 +303,7 @@ void MoviePlayerDXA::stopVideo() { } void MoviePlayerDXA::startSound() { - uint32 offset, size; - - if (getSoundTag() == MKTAG('W','A','V','E')) { - size = _fileStream->readUint32BE(); - - if (_sequenceNum) { - Common::File in; - - _fileStream->seek(size, SEEK_CUR); - - in.open("audio.wav"); - if (!in.isOpen()) { - error("Can't read offset file 'audio.wav'"); - } - - in.seek(_sequenceNum * 8, SEEK_SET); - offset = in.readUint32LE(); - size = in.readUint32LE(); - - in.seek(offset, SEEK_SET); - _bgSoundStream = Audio::makeWAVStream(in.readStream(size), DisposeAfterUse::YES); - in.close(); - } else { - _bgSoundStream = Audio::makeWAVStream(_fileStream->readStream(size), DisposeAfterUse::YES); - } - } else { - _bgSoundStream = Audio::SeekableAudioStream::openStreamFile(baseName); - } + start(); if (_bgSoundStream != NULL) { _vm->_mixer->stopHandle(_bgSound); @@ -344,8 +318,7 @@ void MoviePlayerDXA::nextFrame() { } if (_vm->_interactiveVideo == TYPE_LOOPING && endOfVideo()) { - _fileStream->seek(_firstFrameOffset); - _curFrame = -1; + rewind(); startSound(); } @@ -374,13 +347,15 @@ bool MoviePlayerDXA::processFrame() { copyFrameToBuffer((byte *)screen->pixels, (_vm->_screenWidth - getWidth()) / 2, (_vm->_screenHeight - getHeight()) / 2, screen->pitch); _vm->_system->unlockScreen(); - Common::Rational soundTime(_mixer->getSoundElapsedTime(_bgSound), 1000); - if ((_bgSoundStream == NULL) || ((soundTime * getFrameRate()).toInt() / 1000 < getCurFrame() + 1)) { + uint32 soundTime = _mixer->getSoundElapsedTime(_bgSound); + uint32 nextFrameStartTime = ((Video::VideoDecoder::VideoTrack *)getTrack(0))->getNextFrameStartTime(); + + if ((_bgSoundStream == NULL) || soundTime < nextFrameStartTime) { if (_bgSoundStream && _mixer->isSoundHandleActive(_bgSound)) { - while (_mixer->isSoundHandleActive(_bgSound) && (soundTime * getFrameRate()).toInt() < getCurFrame()) { + while (_mixer->isSoundHandleActive(_bgSound) && soundTime < nextFrameStartTime) { _vm->_system->delayMillis(10); - soundTime = Common::Rational(_mixer->getSoundElapsedTime(_bgSound), 1000); + soundTime = _mixer->getSoundElapsedTime(_bgSound); } // In case the background sound ends prematurely, update // _ticks so that we can still fall back on the no-sound @@ -399,14 +374,35 @@ bool MoviePlayerDXA::processFrame() { return false; } -void MoviePlayerDXA::updateVolume() { - if (g_system->getMixer()->isSoundHandleActive(_bgSound)) - g_system->getMixer()->setChannelVolume(_bgSound, getVolume()); -} +void MoviePlayerDXA::readSoundData(Common::SeekableReadStream *stream) { + uint32 tag = stream->readUint32BE(); + + if (tag == MKTAG('W','A','V','E')) { + uint32 size = stream->readUint32BE(); + + if (_sequenceNum) { + Common::File in; + + stream->skip(size); + + in.open("audio.wav"); + if (!in.isOpen()) { + error("Can't read offset file 'audio.wav'"); + } + + in.seek(_sequenceNum * 8, SEEK_SET); + uint32 offset = in.readUint32LE(); + size = in.readUint32LE(); -void MoviePlayerDXA::updateBalance() { - if (g_system->getMixer()->isSoundHandleActive(_bgSound)) - g_system->getMixer()->setChannelBalance(_bgSound, getBalance()); + in.seek(offset, SEEK_SET); + _bgSoundStream = Audio::makeWAVStream(in.readStream(size), DisposeAfterUse::YES); + in.close(); + } else { + _bgSoundStream = Audio::makeWAVStream(stream->readStream(size), DisposeAfterUse::YES); + } + } else { + _bgSoundStream = Audio::SeekableAudioStream::openStreamFile(baseName); + } } /////////////////////////////////////////////////////////////////////////////// @@ -415,7 +411,7 @@ void MoviePlayerDXA::updateBalance() { MoviePlayerSMK::MoviePlayerSMK(AGOSEngine_Feeble *vm, const char *name) - : MoviePlayer(vm), SmackerDecoder(vm->_mixer) { + : MoviePlayer(vm), SmackerDecoder() { debug(0, "Creating SMK cutscene player"); memset(baseName, 0, sizeof(baseName)); @@ -435,8 +431,6 @@ bool MoviePlayerSMK::load() { CursorMan.showMouse(false); - _firstFrameOffset = _fileStream->pos(); - return true; } @@ -445,6 +439,10 @@ void MoviePlayerSMK::copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch) { uint w = getWidth(); const Graphics::Surface *surface = decodeNextFrame(); + + if (!surface) + return; + byte *src = (byte *)surface->pixels; dst += y * pitch + x; @@ -455,7 +453,7 @@ void MoviePlayerSMK::copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch) { } while (--h); if (hasDirtyPalette()) - setSystemPalette(); + g_system->getPaletteManager()->setPalette(getPalette(), 0, 256); } void MoviePlayerSMK::playVideo() { @@ -468,6 +466,7 @@ void MoviePlayerSMK::stopVideo() { } void MoviePlayerSMK::startSound() { + start(); } void MoviePlayerSMK::handleNextFrame() { @@ -477,10 +476,8 @@ void MoviePlayerSMK::handleNextFrame() { } void MoviePlayerSMK::nextFrame() { - if (_vm->_interactiveVideo == TYPE_LOOPING && endOfVideo()) { - _fileStream->seek(_firstFrameOffset); - _curFrame = -1; - } + if (_vm->_interactiveVideo == TYPE_LOOPING && endOfVideo()) + rewind(); if (!endOfVideo()) { decodeNextFrame(); @@ -503,7 +500,7 @@ bool MoviePlayerSMK::processFrame() { uint32 waitTime = getTimeToNextFrame(); - if (!waitTime) { + if (!waitTime && !endOfVideoTracks()) { warning("dropped frame %i", getCurFrame()); return false; } diff --git a/engines/agos/animation.h b/engines/agos/animation.h index d1ff074b03..9e31fced6d 100644 --- a/engines/agos/animation.h +++ b/engines/agos/animation.h @@ -67,9 +67,6 @@ protected: virtual void handleNextFrame(); virtual bool processFrame() = 0; virtual void startSound() {} - -protected: - uint32 _firstFrameOffset; }; class MoviePlayerDXA : public MoviePlayer, Video::DXADecoder { @@ -84,9 +81,7 @@ public: virtual void stopVideo(); protected: - // VideoDecoder API - void updateVolume(); - void updateBalance(); + void readSoundData(Common::SeekableReadStream *stream); private: void handleNextFrame(); diff --git a/engines/cine/anim.cpp b/engines/cine/anim.cpp index 410fcca1f3..60168831a1 100644 --- a/engines/cine/anim.cpp +++ b/engines/cine/anim.cpp @@ -682,9 +682,10 @@ void convert8BBP2(byte *dest, byte *source, int16 width, int16 height) { * Load image set * @param resourceName Image set filename * @param idx Target index in animDataTable (-1 if any empty space will do) + * @param frameIndex frame of animation to load (-1 for all frames) * @return The number of the animDataTable entry after the loaded image set (-1 if error) */ -int loadSet(const char *resourceName, int16 idx) { +int loadSet(const char *resourceName, int16 idx, int16 frameIndex =-1 ) { AnimHeader2Struct header2; uint16 numSpriteInAnim; int16 foundFileIdx = findFileInBundle(resourceName); @@ -708,7 +709,17 @@ int loadSet(const char *resourceName, int16 idx) { entry = idx < 0 ? emptyAnimSpace() : idx; assert(entry >= 0); - for (int16 i = 0; i < numSpriteInAnim; i++, entry++) { + int16 startFrame = 0; + int16 endFrame = numSpriteInAnim; + + if(frameIndex>=0) + { + startFrame = frameIndex; + endFrame = frameIndex+1; + ptr += 0x10 * frameIndex; + } + + for (int16 i = startFrame; i < endFrame; i++, entry++) { Common::MemoryReadStream readS(ptr, 0x10); header2.field_0 = readS.readUint32BE(); @@ -767,7 +778,7 @@ int loadSeq(const char *resourceName, int16 idx) { * @return The number of the animDataTable entry after the loaded resource (-1 if error) * @todo Implement loading of all resource types */ -int loadResource(const char *resourceName, int16 idx) { +int loadResource(const char *resourceName, int16 idx, int16 frameIndex) { int result = -1; // Return an error by default if (strstr(resourceName, ".SPL")) { result = loadSpl(resourceName, idx); @@ -778,7 +789,7 @@ int loadResource(const char *resourceName, int16 idx) { } else if (strstr(resourceName, ".ANM")) { result = loadAni(resourceName, idx); } else if (strstr(resourceName, ".SET")) { - result = loadSet(resourceName, idx); + result = loadSet(resourceName, idx, frameIndex); } else if (strstr(resourceName, ".SEQ")) { result = loadSeq(resourceName, idx); } else if (strstr(resourceName, ".H32")) { diff --git a/engines/cine/anim.h b/engines/cine/anim.h index 9c06c260ce..c5130aab82 100644 --- a/engines/cine/anim.h +++ b/engines/cine/anim.h @@ -98,7 +98,7 @@ public: void freeAnimDataTable(); void freeAnimDataRange(byte startIdx, byte numIdx); -int loadResource(const char *resourceName, int16 idx = -1); +int loadResource(const char *resourceName, int16 idx = -1, int16 frameIndex = -1); void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGameFormat saveGameFormat); void generateMask(const byte *sprite, byte *mask, uint16 size, byte transparency); diff --git a/engines/cine/bg_list.cpp b/engines/cine/bg_list.cpp index 693fea3294..36ecf53dea 100644 --- a/engines/cine/bg_list.cpp +++ b/engines/cine/bg_list.cpp @@ -39,9 +39,9 @@ uint32 var8; * @param objIdx Sprite description */ void addToBGList(int16 objIdx) { - renderer->incrustSprite(g_cine->_objectTable[objIdx]); - createBgIncrustListElement(objIdx, 0); + + renderer->incrustSprite(g_cine->_bgIncrustList.back()); } /** @@ -49,9 +49,9 @@ void addToBGList(int16 objIdx) { * @param objIdx Sprite description */ void addSpriteFilledToBGList(int16 objIdx) { - renderer->incrustMask(g_cine->_objectTable[objIdx]); - createBgIncrustListElement(objIdx, 1); + + renderer->incrustMask(g_cine->_bgIncrustList.back()); } /** @@ -103,9 +103,9 @@ void loadBgIncrustFromSave(Common::SeekableReadStream &fHandle) { g_cine->_bgIncrustList.push_back(tmp); if (tmp.param == 0) { - renderer->incrustSprite(g_cine->_objectTable[tmp.objIdx]); + renderer->incrustSprite(tmp); } else { - renderer->incrustMask(g_cine->_objectTable[tmp.objIdx]); + renderer->incrustMask(tmp); } } } diff --git a/engines/cine/cine.cpp b/engines/cine/cine.cpp index 6b94c33c31..bbe2cd4896 100644 --- a/engines/cine/cine.cpp +++ b/engines/cine/cine.cpp @@ -189,6 +189,8 @@ void CineEngine::initialize() { g_cine->_messageTable.clear(); resetObjectTable(); + disableSystemMenu = 1; + var8 = 0; var2 = var3 = var4 = var5 = 0; diff --git a/engines/cine/gfx.cpp b/engines/cine/gfx.cpp index d448f134ff..7a988227f6 100644 --- a/engines/cine/gfx.cpp +++ b/engines/cine/gfx.cpp @@ -113,7 +113,7 @@ FWRenderer::FWRenderer() : _background(NULL), _backupPal(), _cmd(""), assert(_backBuffer); memset(_backBuffer, 0, _screenSize); - memset(_bgName, 0, sizeof(_bgName)); + memset(_bgName, 0, sizeof (_bgName)); } @@ -174,7 +174,8 @@ void FWRenderer::fillSprite(const ObjectStruct &obj, uint8 color) { * @param obj Object info * @param fillColor Sprite color */ -void FWRenderer::incrustMask(const ObjectStruct &obj, uint8 color) { +void FWRenderer::incrustMask(const BGIncrust &incrust, uint8 color) { + const ObjectStruct &obj = g_cine->_objectTable[incrust.objIdx]; const byte *data = g_cine->_animDataTable[obj.frame].data(); int x, y, width, height; @@ -218,7 +219,9 @@ void FWRenderer::drawSprite(const ObjectStruct &obj) { * Draw color sprite on background * @param obj Object info */ -void FWRenderer::incrustSprite(const ObjectStruct &obj) { +void FWRenderer::incrustSprite(const BGIncrust &incrust) { + const ObjectStruct &obj = g_cine->_objectTable[incrust.objIdx]; + const byte *data = g_cine->_animDataTable[obj.frame].data(); const byte *mask = g_cine->_animDataTable[obj.frame].mask(); int x, y, width, height; @@ -246,14 +249,16 @@ void FWRenderer::drawCommand() { unsigned int i; int x = 10, y = _cmdY; - drawPlainBox(x, y, 301, 11, 0); - drawBorder(x - 1, y - 1, 302, 12, 2); + if(disableSystemMenu == 0) { + drawPlainBox(x, y, 301, 11, 0); + drawBorder(x - 1, y - 1, 302, 12, 2); - x += 2; - y += 2; + x += 2; + y += 2; - for (i = 0; i < _cmd.size(); i++) { - x = drawChar(_cmd[i], x, y); + for (i = 0; i < _cmd.size(); i++) { + x = drawChar(_cmd[i], x, y); + } } } @@ -298,10 +303,11 @@ void FWRenderer::drawMessage(const char *str, int x, int y, int width, int color for (i = 0; str[i]; i++, line--) { // Fit line of text into textbox if (!line) { - while (str[i] == ' ') i++; + while (str[i] == ' ') + i++; line = fitLine(str + i, tw, words, cw); - if (str[i + line] != '\0' && str[i + line] != 0x7C && words) { + if ( str[i + line] != '\0' && str[i + line] != 0x7C && words) { space = (tw - cw) / words; extraSpace = (tw - cw) % words; } else { @@ -1119,7 +1125,8 @@ void OSRenderer::clear() { * @param obj Object info * @param fillColor Sprite color */ -void OSRenderer::incrustMask(const ObjectStruct &obj, uint8 color) { +void OSRenderer::incrustMask(const BGIncrust &incrust, uint8 color) { + const ObjectStruct &obj = g_cine->_objectTable[incrust.objIdx]; const byte *data = g_cine->_animDataTable[obj.frame].data(); int x, y, width, height; @@ -1154,15 +1161,16 @@ void OSRenderer::drawSprite(const ObjectStruct &obj) { * Draw color sprite * @param obj Object info */ -void OSRenderer::incrustSprite(const ObjectStruct &obj) { - const byte *data = g_cine->_animDataTable[obj.frame].data(); +void OSRenderer::incrustSprite(const BGIncrust &incrust) { + const ObjectStruct &obj = g_cine->_objectTable[incrust.objIdx]; + const byte *data = g_cine->_animDataTable[incrust.frame].data(); int x, y, width, height, transColor; - x = obj.x; - y = obj.y; + x = incrust.x; + y = incrust.y; transColor = obj.part; - width = g_cine->_animDataTable[obj.frame]._realWidth; - height = g_cine->_animDataTable[obj.frame]._height; + width = g_cine->_animDataTable[incrust.frame]._realWidth; + height = g_cine->_animDataTable[incrust.frame]._height; if (_bgTable[_currentBg].bg) { drawSpriteRaw2(data, transColor, width, height, _bgTable[_currentBg].bg, x, y); @@ -1416,7 +1424,7 @@ void OSRenderer::selectBg(unsigned int idx) { if (_bgTable[idx].bg) { assert(_bgTable[idx].pal.isValid() && !(_bgTable[idx].pal.empty())); - _currentBg = idx; + _currentBg = idx; } else warning("OSRenderer::selectBg(%d) - attempt to select null background", idx); reloadPalette(); @@ -1742,23 +1750,23 @@ void OSRenderer::drawSprite(overlay *overlayPtr, const byte *spritePtr, int16 wi // draw the mask based on next objects in the list Common::List<overlay>::iterator it; - for (it = g_cine->_overlayList.begin(); it != g_cine->_overlayList.end(); ++it) { - if (&(*it) == overlayPtr) { + for (it = g_cine->_overlayList.begin(); it != g_cine->_overlayList.end(); ++it) { + if(&(*it) == overlayPtr) { break; } } - while (it != g_cine->_overlayList.end()) { + while(it != g_cine->_overlayList.end()) { overlay *pCurrentOverlay = &(*it); if ((pCurrentOverlay->type == 5) || ((pCurrentOverlay->type == 21) && (pCurrentOverlay->x == overlayPtr->objIdx))) { AnimData *sprite = &g_cine->_animDataTable[g_cine->_objectTable[it->objIdx].frame]; if (pMask == NULL) { - pMask = new byte[width * height]; + pMask = new byte[width*height]; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { - byte spriteColor = spritePtr[width * i + j]; + byte spriteColor= spritePtr[width * i + j]; pMask[width * i + j] = spriteColor; } } @@ -1769,7 +1777,7 @@ void OSRenderer::drawSprite(overlay *overlayPtr, const byte *spritePtr, int16 wi int inMaskX = (g_cine->_objectTable[it->objIdx].x + i) - x; int inMaskY = (g_cine->_objectTable[it->objIdx].y + j) - y; - if (inMaskX >= 0 && inMaskX < width) { + if (inMaskX >=0 && inMaskX < width) { if (inMaskY >= 0 && inMaskY < height) { if (sprite->_bpp == 1) { if (!sprite->getColor(i, j)) { @@ -1780,23 +1788,27 @@ void OSRenderer::drawSprite(overlay *overlayPtr, const byte *spritePtr, int16 wi } } } - - } it++; } // now, draw with the mask we created - if (pMask) { + if(pMask) { spritePtr = pMask; } + + // ignore transparent color in 1bpp + if (bpp == 1) { + transparentColor = 1; + } + { for (int i = 0; i < height; i++) { byte *destPtr = page + x + y * 320; destPtr += i * 320; for (int j = 0; j < width; j++) { - byte color = *(spritePtr++); + byte color= *(spritePtr++); if ((transparentColor != color) && x + j >= 0 && x + j < 320 && i + y >= 0 && i + y < 200) { *(destPtr++) = color; } else { diff --git a/engines/cine/gfx.h b/engines/cine/gfx.h index 6ff5b08b77..3434cf9fc2 100644 --- a/engines/cine/gfx.h +++ b/engines/cine/gfx.h @@ -27,6 +27,7 @@ #include "common/rect.h" #include "common/stack.h" #include "cine/object.h" +#include "cine/bg_list.h" namespace Cine { @@ -177,8 +178,8 @@ public: void drawFrame(); void setCommand(Common::String cmd); - virtual void incrustMask(const ObjectStruct &obj, uint8 color = 0); - virtual void incrustSprite(const ObjectStruct &obj); + virtual void incrustMask(const BGIncrust &incrust, uint8 color = 0); + virtual void incrustSprite(const BGIncrust &incrust); virtual void loadBg16(const byte *bg, const char *name, unsigned int idx = 0); virtual void loadCt16(const byte *ct, const char *name); @@ -239,8 +240,8 @@ public: void clear(); - void incrustMask(const ObjectStruct &obj, uint8 color = 0); - void incrustSprite(const ObjectStruct &obj); + void incrustMask(const BGIncrust &incrust, uint8 color = 0); + void incrustSprite(const BGIncrust &incrust); void loadBg16(const byte *bg, const char *name, unsigned int idx = 0); void loadCt16(const byte *ct, const char *name); diff --git a/engines/cine/main_loop.cpp b/engines/cine/main_loop.cpp index 971830ce8f..f13f38a45e 100644 --- a/engines/cine/main_loop.cpp +++ b/engines/cine/main_loop.cpp @@ -56,6 +56,12 @@ static void processEvent(Common::Event &event) { case Common::EVENT_RBUTTONDOWN: mouseRight = 1; break; + case Common::EVENT_LBUTTONUP: + mouseLeft = 0; + break; + case Common::EVENT_RBUTTONUP: + mouseRight = 0; + break; case Common::EVENT_MOUSEMOVE: break; case Common::EVENT_KEYDOWN: @@ -115,7 +121,7 @@ static void processEvent(Common::Event &event) { } break; case Common::KEYCODE_F10: - if (!disableSystemMenu && !inMenu) { + if (!inMenu) { g_cine->makeSystemMenu(); } break; @@ -214,8 +220,6 @@ void manageEvents() { g_sound->update(); mouseData.left = mouseLeft; mouseData.right = mouseRight; - mouseLeft = 0; - mouseRight = 0; } void getMouseData(uint16 param, uint16 *pButton, uint16 *pX, uint16 *pY) { @@ -311,6 +315,7 @@ void CineEngine::mainLoop(int bootScriptIdx) { // HACK: Force amount of oxygen left to maximum during Operation Stealth's first arcade sequence. // This makes it possible to pass the arcade sequence for now. // FIXME: Remove the hack and make the first arcade sequence normally playable. + /* if (g_cine->getGameType() == Cine::GType_OS) { Common::String bgName(renderer->getBgName()); // Check if the background is one of the three backgrounds @@ -320,7 +325,7 @@ void CineEngine::mainLoop(int bootScriptIdx) { // Force the amount of oxygen left to the maximum. g_cine->_objectTable[oxygenObjNum].x = maxOxygen; } - } + }*/ // HACK: In Operation Stealth after the first arcade sequence jump player's position to avoid getting stuck. // After the first arcade sequence the player comes up stairs from @@ -379,8 +384,8 @@ void CineEngine::mainLoop(int bootScriptIdx) { playerAction = false; _messageLen <<= 3; - if (_messageLen < 0x800) - _messageLen = 0x800; + if (_messageLen < 800) + _messageLen = 800; do { manageEvents(); diff --git a/engines/cine/saveload.cpp b/engines/cine/saveload.cpp index 223099a587..20952eea52 100644 --- a/engines/cine/saveload.cpp +++ b/engines/cine/saveload.cpp @@ -991,7 +991,7 @@ void CineEngine::makeSave(char *saveFileName) { * at a time. */ void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGameFormat saveGameFormat) { - int16 currentAnim, foundFileIdx; + int16 foundFileIdx; char *animName, part[256], name[10]; strcpy(part, currentPartName); @@ -1001,10 +1001,10 @@ void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGam const int entrySize = ((saveGameFormat == ANIMSIZE_23) ? 23 : 30); const int fileStartPos = fHandle.pos(); - currentAnim = 0; - while (currentAnim < NUM_MAX_ANIMDATA) { + + for(int resourceIndex=0; resourceIndex<NUM_MAX_ANIMDATA; resourceIndex++) { // Seek to the start of the current animation's entry - fHandle.seek(fileStartPos + currentAnim * entrySize); + fHandle.seek(fileStartPos + resourceIndex * entrySize); // Read in the current animation entry fHandle.readUint16BE(); // width fHandle.readUint16BE(); @@ -1019,7 +1019,7 @@ void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGam } foundFileIdx = fHandle.readSint16BE(); - fHandle.readSint16BE(); // frame + int16 frameIndex = fHandle.readSint16BE(); // frame fHandle.read(name, 10); // Handle variables only present in animation entries of size 23 @@ -1029,7 +1029,7 @@ void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGam // Don't try to load invalid entries. if (foundFileIdx < 0 || !validPtr) { - currentAnim++; // Jump over the invalid entry + //resourceIndex++; // Jump over the invalid entry continue; } @@ -1041,9 +1041,7 @@ void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGam animName = g_cine->_partBuffer[foundFileIdx].partName; loadRelatedPalette(animName); // Is this for Future Wars only? - const int16 prevAnim = currentAnim; - currentAnim = loadResource(animName, currentAnim); - assert(currentAnim > prevAnim); // Make sure we advance forward + loadResource(animName, resourceIndex, frameIndex); } loadPart(part); diff --git a/engines/cine/script_fw.cpp b/engines/cine/script_fw.cpp index 66150cc5b2..9cbe3c3fab 100644 --- a/engines/cine/script_fw.cpp +++ b/engines/cine/script_fw.cpp @@ -533,7 +533,6 @@ void RawScript::setData(const FWScriptInfo &info, const byte *data) { * @return Precalculated script labels */ const ScriptVars &RawScript::labels() const { - assert(_data); return _labels; } @@ -687,7 +686,7 @@ const char *FWScript::getNextString() { * @param pos Restored script position */ void FWScript::load(const ScriptVars &labels, const ScriptVars &local, uint16 compare, uint16 pos) { - assert(pos < _script._size); + assert(pos <= _script._size); _labels = labels; _localVars = local; _compare = compare; @@ -705,13 +704,15 @@ void FWScript::load(const ScriptVars &labels, const ScriptVars &local, uint16 co int FWScript::execute() { int ret = 0; - while (!ret) { - _line = _pos; - byte opcode = getNextByte(); - OpFunc handler = _info->opcodeHandler(opcode); + if(_script._size) { + while (!ret) { + _line = _pos; + byte opcode = getNextByte(); + OpFunc handler = _info->opcodeHandler(opcode); - if (handler) { - ret = (this->*handler)(); + if (handler) { + ret = (this->*handler)(); + } } } @@ -1861,7 +1862,7 @@ int FWScript::o1_disableSystemMenu() { byte param = getNextByte(); debugC(5, kCineDebugScript, "Line: %d: disableSystemMenu(%d)", _line, param); - disableSystemMenu = (param != 0); + disableSystemMenu = param; return 0; } diff --git a/engines/cine/various.cpp b/engines/cine/various.cpp index 9b73ae1101..eccd71cf05 100644 --- a/engines/cine/various.cpp +++ b/engines/cine/various.cpp @@ -36,7 +36,7 @@ namespace Cine { -bool disableSystemMenu = false; +int16 disableSystemMenu = 0; bool inMenu; int16 commandVar3[4]; @@ -341,7 +341,7 @@ void CineEngine::makeSystemMenu() { int16 mouseX, mouseY, mouseButton; int16 selectedSave; - if (!disableSystemMenu) { + if (disableSystemMenu != 1) { inMenu = true; do { @@ -544,14 +544,16 @@ int16 buildObjectListCommand(int16 param) { int16 selectSubObject(int16 x, int16 y, int16 param) { int16 listSize = buildObjectListCommand(param); - int16 selectedObject; + int16 selectedObject = -1; bool osExtras = g_cine->getGameType() == Cine::GType_OS; if (!listSize) { return -2; } - selectedObject = makeMenuChoice(objectListCommand, listSize, x, y, 140, osExtras); + if (disableSystemMenu == 0) { + selectedObject = makeMenuChoice(objectListCommand, listSize, x, y, 140, osExtras); + } if (selectedObject == -1) return -1; @@ -691,9 +693,6 @@ int16 makeMenuChoice(const CommandeType commandList[], uint16 height, uint16 X, int16 var_4; SelectionMenu *menu; - if (disableSystemMenu) - return -1; - paramY = (height * 9) + 10; if (X + width > 319) { @@ -810,14 +809,18 @@ void makeActionMenu() { getMouseData(mouseUpdateStatus, &mouseButton, &mouseX, &mouseY); if (g_cine->getGameType() == Cine::GType_OS) { - playerCommand = makeMenuChoice(defaultActionCommand, 6, mouseX, mouseY, 70, true); + if(disableSystemMenu == 0) { + playerCommand = makeMenuChoice(defaultActionCommand, 6, mouseX, mouseY, 70, true); + } if (playerCommand >= 8000) { playerCommand -= 8000; canUseOnObject = canUseOnItemTable[playerCommand]; } } else { - playerCommand = makeMenuChoice(defaultActionCommand, 6, mouseX, mouseY, 70); + if(disableSystemMenu == 0) { + playerCommand = makeMenuChoice(defaultActionCommand, 6, mouseX, mouseY, 70); + } } inMenu = false; diff --git a/engines/cine/various.h b/engines/cine/various.h index 0c1883c323..813619816d 100644 --- a/engines/cine/various.h +++ b/engines/cine/various.h @@ -41,7 +41,7 @@ void makeActionMenu(); void waitPlayerInput(); void setTextWindow(uint16 param1, uint16 param2, uint16 param3, uint16 param4); -extern bool disableSystemMenu; +extern int16 disableSystemMenu; extern bool inMenu; extern CommandeType currentSaveName[10]; diff --git a/engines/lastexpress/data/background.cpp b/engines/lastexpress/data/background.cpp index 3d866c26f9..60379251a3 100644 --- a/engines/lastexpress/data/background.cpp +++ b/engines/lastexpress/data/background.cpp @@ -107,6 +107,7 @@ byte *Background::decodeComponent(Common::SeekableReadStream *in, uint32 inSize, return NULL; // Initialize the decoding + memset(out, 0, outSize * sizeof(byte)); uint32 inPos = 0; uint32 outPos = 0; diff --git a/engines/lastexpress/data/subtitle.cpp b/engines/lastexpress/data/subtitle.cpp index a9a8284588..4d19c02aa7 100644 --- a/engines/lastexpress/data/subtitle.cpp +++ b/engines/lastexpress/data/subtitle.cpp @@ -210,10 +210,10 @@ void SubtitleManager::setTime(uint16 time) { _currentIndex = -1; // Find the appropriate line to show - for (int16 i = 0; i < (int16)_subtitles.size(); i++) { + for (uint i = 0; i < _subtitles.size(); i++) { if ((time >= _subtitles[i]->getTimeStart()) && (time <= _subtitles[i]->getTimeStop())) { // Keep the index of the line to show - _currentIndex = i; + _currentIndex = (int16)i; return; } } @@ -237,7 +237,7 @@ Common::Rect SubtitleManager::draw(Graphics::Surface *surface) { // Draw the current line assert(_currentIndex >= 0 && _currentIndex < (int16)_subtitles.size()); - return _subtitles[_currentIndex]->draw(surface, _font); + return _subtitles[(uint16)_currentIndex]->draw(surface, _font); } } // End of namespace LastExpress diff --git a/engines/lastexpress/debug.cpp b/engines/lastexpress/debug.cpp index f89ad8b80d..db3a3e3962 100644 --- a/engines/lastexpress/debug.cpp +++ b/engines/lastexpress/debug.cpp @@ -85,7 +85,6 @@ Debugger::Debugger(LastExpressEngine *engine) : _engine(engine), _command(NULL), DCmd_Register("entity", WRAP_METHOD(Debugger, cmdEntity)); // Misc - DCmd_Register("loadgame", WRAP_METHOD(Debugger, cmdLoadGame)); DCmd_Register("chapter", WRAP_METHOD(Debugger, cmdSwitchChapter)); DCmd_Register("clear", WRAP_METHOD(Debugger, cmdClear)); @@ -139,6 +138,9 @@ void Debugger::copyCommand(int argc, const char **argv) { for (int i = 0; i < _numParams; i++) { _commandParams[i] = (char *)malloc(strlen(argv[i]) + 1); + if (_commandParams[i] == NULL) + error("[Debugger::copyCommand] Cannot allocate memory for command parameters"); + memset(_commandParams[i], 0, strlen(argv[i]) + 1); strcpy(_commandParams[i], argv[i]); } @@ -152,9 +154,18 @@ void Debugger::callCommand() { (*_command)(_numParams, const_cast<const char **>(_commandParams)); } -void Debugger::loadArchive(ArchiveIndex index) const { - _engine->getResourceManager()->loadArchive(index); - getScenes()->loadSceneDataFile(index); +bool Debugger::loadArchive(int index) { + if (index < 1 || index > 3) { + DebugPrintf("Invalid cd number (was: %d, valid: [1-3])\n", index); + return false; + } + + if (!_engine->getResourceManager()->loadArchive((ArchiveIndex)index)) + return false; + + getScenes()->loadSceneDataFile((ArchiveIndex)index); + + return true; } // Restore loaded archive @@ -233,8 +244,10 @@ bool Debugger::cmdListFiles(int argc, const char **argv) { Common::String filter(const_cast<char *>(argv[1])); // Load the proper archive - if (argc == 3) - loadArchive((ArchiveIndex)getNumber(argv[2])); + if (argc == 3) { + if (!loadArchive(getNumber(argv[2]))) + return true; + } Common::ArchiveMemberList list; int count = _engine->getResourceManager()->listMatchingMembers(list, filter); @@ -317,8 +330,10 @@ bool Debugger::cmdShowFrame(int argc, const char **argv) { Common::String filename(const_cast<char *>(argv[1])); filename += ".seq"; - if (argc == 4) - loadArchive((ArchiveIndex)getNumber(argv[3])); + if (argc == 4) { + if (!loadArchive(getNumber(argv[3]))) + return true; + } if (!_engine->getResourceManager()->hasFile(filename)) { DebugPrintf("Cannot find file: %s\n", filename.c_str()); @@ -377,8 +392,10 @@ bool Debugger::cmdShowBg(int argc, const char **argv) { if (argc == 2 || argc == 3) { Common::String filename(const_cast<char *>(argv[1])); - if (argc == 3) - loadArchive((ArchiveIndex)getNumber(argv[2])); + if (argc == 3) { + if (!loadArchive(getNumber(argv[2]))) + return true; + } if (!_engine->getResourceManager()->hasFile(filename + ".BG")) { DebugPrintf("Cannot find file: %s\n", (filename + ".BG").c_str()); @@ -430,8 +447,10 @@ bool Debugger::cmdPlaySeq(int argc, const char **argv) { Common::String filename(const_cast<char *>(argv[1])); filename += ".seq"; - if (argc == 3) - loadArchive((ArchiveIndex)getNumber(argv[2])); + if (argc == 3) { + if (!loadArchive(getNumber(argv[2]))) + return true; + } if (!_engine->getResourceManager()->hasFile(filename)) { DebugPrintf("Cannot find file: %s\n", filename.c_str()); @@ -505,8 +524,10 @@ bool Debugger::cmdPlaySeq(int argc, const char **argv) { bool Debugger::cmdPlaySnd(int argc, const char **argv) { if (argc == 2 || argc == 3) { - if (argc == 3) - loadArchive((ArchiveIndex)getNumber(argv[2])); + if (argc == 3) { + if (!loadArchive(getNumber(argv[2]))) + return true; + } // Add .SND at the end of the filename if needed Common::String name(const_cast<char *>(argv[1])); @@ -520,7 +541,7 @@ bool Debugger::cmdPlaySnd(int argc, const char **argv) { _engine->_system->getMixer()->stopAll(); - _soundStream->load(getArchive(name)); + _soundStream->load(getArchive(name), 16); if (argc == 3) restoreArchive(); @@ -542,8 +563,10 @@ bool Debugger::cmdPlaySbe(int argc, const char **argv) { if (argc == 2 || argc == 3) { Common::String filename(const_cast<char *>(argv[1])); - if (argc == 3) - loadArchive((ArchiveIndex)getNumber(argv[2])); + if (argc == 3) { + if (!loadArchive(getNumber(argv[2]))) + return true; + } filename += ".sbe"; @@ -605,8 +628,10 @@ bool Debugger::cmdPlayNis(int argc, const char **argv) { if (argc == 2 || argc == 3) { Common::String name(const_cast<char *>(argv[1])); - if (argc == 3) - loadArchive((ArchiveIndex)getNumber(argv[2])); + if (argc == 3) { + if (!loadArchive(getNumber(argv[2]))) + return true; + } // If we got a nis filename, check that the file exists if (name.contains('.') && !_engine->getResourceManager()->hasFile(name)) { @@ -662,8 +687,10 @@ bool Debugger::cmdLoadScene(int argc, const char **argv) { SceneIndex index = (SceneIndex)getNumber(argv[1]); // Check args - if (argc == 3) - loadArchive((ArchiveIndex)getNumber(argv[2])); + if (argc == 3) { + if (!loadArchive(getNumber(argv[2]))) + return true; + } if (index > 2500) { DebugPrintf("Error: invalid index value (0-2500)"); @@ -1091,30 +1118,6 @@ label_error: } /** - * Command: loads a game - * - * @param argc The argument count. - * @param argv The values. - * - * @return true if it was handled, false otherwise - */ -bool Debugger::cmdLoadGame(int argc, const char **argv) { - if (argc == 2) { - int id = getNumber(argv[1]); - - if (id == 0 || id > 6) - goto error; - - getSaveLoad()->loadGame((GameId)(id - 1)); - } else { -error: - DebugPrintf("Syntax: loadgame <id> (id=1-6)\n"); - } - - return true; -} - -/** * Command: switch to a specific chapter * * @param argc The argument count. diff --git a/engines/lastexpress/debug.h b/engines/lastexpress/debug.h index d9ba6f47a1..532cb83717 100644 --- a/engines/lastexpress/debug.h +++ b/engines/lastexpress/debug.h @@ -79,7 +79,6 @@ private: bool cmdShow(int argc, const char **argv); bool cmdEntity(int argc, const char **argv); - bool cmdLoadGame(int argc, const char **argv); bool cmdSwitchChapter(int argc, const char **argv); bool cmdClear(int argc, const char **argv); @@ -87,7 +86,7 @@ private: void copyCommand(int argc, const char **argv); int getNumber(const char *arg) const; - void loadArchive(ArchiveIndex index) const; + bool loadArchive(int index); void restoreArchive() const; Debuglet *_command; diff --git a/engines/lastexpress/entities/abbot.cpp b/engines/lastexpress/entities/abbot.cpp index e0fe429520..406b017d3a 100644 --- a/engines/lastexpress/entities/abbot.cpp +++ b/engines/lastexpress/entities/abbot.cpp @@ -58,10 +58,10 @@ Abbot::Abbot(LastExpressEngine *engine) : Entity(engine, kEntityAbbot) { ADD_CALLBACK_FUNCTION(Abbot, chapter2); ADD_CALLBACK_FUNCTION(Abbot, chapter3); ADD_CALLBACK_FUNCTION(Abbot, chapter3Handler); - ADD_CALLBACK_FUNCTION(Abbot, function19); - ADD_CALLBACK_FUNCTION(Abbot, function20); - ADD_CALLBACK_FUNCTION(Abbot, function21); - ADD_CALLBACK_FUNCTION(Abbot, function22); + ADD_CALLBACK_FUNCTION(Abbot, conversationWithBoutarel); + ADD_CALLBACK_FUNCTION(Abbot, readPaper); + ADD_CALLBACK_FUNCTION(Abbot, goToLunch); + ADD_CALLBACK_FUNCTION(Abbot, haveLunch); ADD_CALLBACK_FUNCTION(Abbot, function23); ADD_CALLBACK_FUNCTION(Abbot, function24); ADD_CALLBACK_FUNCTION(Abbot, function25); @@ -259,7 +259,7 @@ IMPLEMENT_FUNCTION(18, Abbot, chapter3Handler) getData()->entityPosition = kPosition_6470; getData()->location = kLocationInsideCompartment; - setup_function19(); + setup_conversationWithBoutarel(); break; } break; @@ -272,7 +272,7 @@ IMPLEMENT_FUNCTION(18, Abbot, chapter3Handler) IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION(19, Abbot, function19) +IMPLEMENT_FUNCTION(19, Abbot, conversationWithBoutarel) switch (savepoint.action) { default: break; @@ -311,21 +311,21 @@ IMPLEMENT_FUNCTION(19, Abbot, function19) case 3: getSavePoints()->push(kEntityAbbot, kEntityBoutarel, kAction122288808); - setup_function20(); + setup_readPaper(); break; } } IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION(20, Abbot, function20) +IMPLEMENT_FUNCTION(20, Abbot, readPaper) switch (savepoint.action) { default: break; case kActionNone: if (getState()->time > kTime1966500 && getEntities()->isInRestaurant(kEntityBoutarel)) - setup_function21(); + setup_goToLunch(); break; case kActionDefault: @@ -335,7 +335,7 @@ IMPLEMENT_FUNCTION(20, Abbot, function20) IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION(21, Abbot, function21) +IMPLEMENT_FUNCTION(21, Abbot, goToLunch) switch (savepoint.action) { default: break; @@ -393,7 +393,7 @@ IMPLEMENT_FUNCTION(21, Abbot, function21) break; case 7: - setup_function22(); + setup_haveLunch(); break; } break; @@ -409,7 +409,7 @@ IMPLEMENT_FUNCTION(21, Abbot, function21) IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION(22, Abbot, function22) +IMPLEMENT_FUNCTION(22, Abbot, haveLunch) switch (savepoint.action) { default: break; @@ -1547,7 +1547,7 @@ IMPLEMENT_FUNCTION(45, Abbot, function45) getData()->car = kCarRedSleeping; getData()->location = kLocationOutsideCompartment; - RESET_ENTITY_STATE(kEntityVerges, Verges, setup_function38); + RESET_ENTITY_STATE(kEntityVerges, Verges, setup_resetState); getEntities()->drawSequenceLeft(kEntityAbbot, "617Ec"); getEntities()->enterCompartment(kEntityAbbot, kObjectCompartmentC, true); diff --git a/engines/lastexpress/entities/abbot.h b/engines/lastexpress/entities/abbot.h index ce52bb68ce..dc3e86db54 100644 --- a/engines/lastexpress/entities/abbot.h +++ b/engines/lastexpress/entities/abbot.h @@ -156,10 +156,10 @@ public: * Handle Chapter 3 events */ DECLARE_FUNCTION(chapter3Handler) - DECLARE_FUNCTION(function19) - DECLARE_FUNCTION(function20) - DECLARE_FUNCTION(function21) - DECLARE_FUNCTION(function22) + DECLARE_FUNCTION(conversationWithBoutarel) + DECLARE_FUNCTION(readPaper) + DECLARE_FUNCTION(goToLunch) + DECLARE_FUNCTION(haveLunch) DECLARE_FUNCTION(function23) DECLARE_FUNCTION(function24) DECLARE_FUNCTION(function25) diff --git a/engines/lastexpress/entities/august.cpp b/engines/lastexpress/entities/august.cpp index 67d810fde2..dbae7bad20 100644 --- a/engines/lastexpress/entities/august.cpp +++ b/engines/lastexpress/entities/august.cpp @@ -3530,7 +3530,7 @@ IMPLEMENT_FUNCTION(69, August, unhookCars) getScenes()->loadSceneFromPosition(kCarRestaurant, 85, 1); getSavePoints()->pushAll(kEntityAugust, kActionProceedChapter5); - RESET_ENTITY_STATE(kEntityVerges, Verges, setup_function42) + RESET_ENTITY_STATE(kEntityVerges, Verges, setup_end) } break; } diff --git a/engines/lastexpress/entities/chapters.cpp b/engines/lastexpress/entities/chapters.cpp index a2f3a3d871..d373432710 100644 --- a/engines/lastexpress/entities/chapters.cpp +++ b/engines/lastexpress/entities/chapters.cpp @@ -1851,7 +1851,7 @@ void Chapters::enterExitHelper(bool isEnteringStation) { callbackAction(); } -void Chapters::playSteam() { +void Chapters::playSteam() const { getSoundQueue()->resetState(); getSound()->playSteam((CityIndex)ENTITY_PARAM(0, 4)); ENTITY_PARAM(0, 2) = 0; diff --git a/engines/lastexpress/entities/chapters.h b/engines/lastexpress/entities/chapters.h index ddb3de3bea..fb52ea3ee4 100644 --- a/engines/lastexpress/entities/chapters.h +++ b/engines/lastexpress/entities/chapters.h @@ -157,7 +157,7 @@ private: bool timeCheckExitStation(TimeValue timeValue, uint ¶meter, byte callback, const char *sequence); void enterExitStation(const SavePoint &savepoint, bool isEnteringStation); void enterExitHelper(bool isEnteringStation); - void playSteam(); + void playSteam() const; }; } // End of namespace LastExpress diff --git a/engines/lastexpress/entities/entity.cpp b/engines/lastexpress/entities/entity.cpp index 2deca291f6..dad5e67392 100644 --- a/engines/lastexpress/entities/entity.cpp +++ b/engines/lastexpress/entities/entity.cpp @@ -26,10 +26,15 @@ #include "lastexpress/game/action.h" #include "lastexpress/game/entities.h" +#include "lastexpress/game/logic.h" #include "lastexpress/game/object.h" #include "lastexpress/game/savegame.h" +#include "lastexpress/game/savepoint.h" +#include "lastexpress/game/state.h" #include "lastexpress/game/scenes.h" +#include "lastexpress/lastexpress.h" + namespace LastExpress { ////////////////////////////////////////////////////////////////////////// @@ -45,12 +50,14 @@ EntityData::EntityCallData::~EntityCallData() { SAFE_DELETE(sequence3); } -void EntityData::EntityCallData::syncString(Common::Serializer &s, Common::String &string, int length) { +void EntityData::EntityCallData::syncString(Common::Serializer &s, Common::String &string, uint length) const { char seqName[13]; memset(&seqName, 0, length); - if (s.isSaving()) strcpy((char *)&seqName, string.c_str()); - s.syncBytes((byte *)&seqName, length); + if (s.isSaving()) + strcpy((char *)&seqName, string.c_str()); + + s.syncBytes((byte *)&seqName, length); if (s.isLoading()) string = seqName; @@ -110,7 +117,7 @@ EntityData::EntityParameters *EntityData::getParameters(uint callback, byte inde return _parameters[callback].parameters[index]; } -int EntityData::getCallback(uint callback) const { +byte EntityData::getCallback(uint callback) const { if (callback >= 16) error("[EntityData::getCallback] Invalid callback value (was: %d, max: 16)", callback); @@ -818,7 +825,7 @@ void Entity::setupIISS(const char *name, uint index, uint param1, uint param2, c // Helper functions ////////////////////////////////////////////////////////////////////////// -bool Entity::updateParameter(uint ¶meter, uint timeType, uint delta) { +bool Entity::updateParameter(uint ¶meter, uint timeType, uint delta) const { if (!parameter) parameter = (uint)(timeType + delta); @@ -830,7 +837,7 @@ bool Entity::updateParameter(uint ¶meter, uint timeType, uint delta) { return true; } -bool Entity::updateParameterTime(TimeValue timeValue, bool check, uint ¶meter, uint delta) { +bool Entity::updateParameterTime(TimeValue timeValue, bool check, uint ¶meter, uint delta) const { if (getState()->time <= timeValue) { if (check || !parameter) parameter = (uint)(getState()->time + delta); @@ -844,7 +851,7 @@ bool Entity::updateParameterTime(TimeValue timeValue, bool check, uint ¶mete return true; } -bool Entity::updateParameterCheck(uint ¶meter, uint timeType, uint delta) { +bool Entity::updateParameterCheck(uint ¶meter, uint timeType, uint delta) const { if (parameter && parameter >= timeType) return false; @@ -854,7 +861,7 @@ bool Entity::updateParameterCheck(uint ¶meter, uint timeType, uint delta) { return true; } -bool Entity::timeCheck(TimeValue timeValue, uint ¶meter, Common::Functor0<void> *function) { +bool Entity::timeCheck(TimeValue timeValue, uint ¶meter, Common::Functor0<void> *function) const { if (getState()->time > timeValue && !parameter) { parameter = 1; (*function)(); @@ -929,14 +936,14 @@ bool Entity::timeCheckCar(TimeValue timeValue, uint ¶meter, byte callback, C return false; } -void Entity::timeCheckSavepoint(TimeValue timeValue, uint ¶meter, EntityIndex entity1, EntityIndex entity2, ActionIndex action) { +void Entity::timeCheckSavepoint(TimeValue timeValue, uint ¶meter, EntityIndex entity1, EntityIndex entity2, ActionIndex action) const { if (getState()->time > timeValue && !parameter) { parameter = 1; getSavePoints()->push(entity1, entity2, action); } } -void Entity::timeCheckObject(TimeValue timeValue, uint ¶meter, ObjectIndex object, ObjectLocation location) { +void Entity::timeCheckObject(TimeValue timeValue, uint ¶meter, ObjectIndex object, ObjectLocation location) const { if (getState()->time > timeValue && !parameter) { parameter = 1; getObjects()->updateLocation2(object, location); diff --git a/engines/lastexpress/entities/entity.h b/engines/lastexpress/entities/entity.h index 3601f34f6f..c67d13db9e 100644 --- a/engines/lastexpress/entities/entity.h +++ b/engines/lastexpress/entities/entity.h @@ -25,13 +25,8 @@ #include "lastexpress/shared.h" -#include "lastexpress/game/logic.h" -#include "lastexpress/game/savepoint.h" -#include "lastexpress/game/state.h" - #include "lastexpress/sound/sound.h" -#include "lastexpress/lastexpress.h" #include "lastexpress/helpers.h" #include "common/array.h" @@ -827,7 +822,7 @@ public: * @param string The string. * @param length Length of the string. */ - void syncString(Common::Serializer &s, Common::String &string, int length); + void syncString(Common::Serializer &s, Common::String &string, uint length) const; // Serializable void saveLoadWithSerializer(Common::Serializer &s); @@ -850,8 +845,8 @@ public: EntityParameters *getCurrentParameters(byte index = 0) { return getParameters(_data.currentCall, index); } EntityCallParameters *getCurrentCallParameters() { return &_parameters[_data.currentCall]; } - int getCallback(uint callback) const; - int getCurrentCallback() { return getCallback(_data.currentCall); } + byte getCallback(uint callback) const; + byte getCurrentCallback() { return getCallback(_data.currentCall); } void setCallback(uint callback, byte index); void setCurrentCallback(uint index) { setCallback(_data.currentCall, index); } @@ -1089,18 +1084,18 @@ protected: // Helper functions ////////////////////////////////////////////////////////////////////////// - bool updateParameter(uint ¶meter, uint timeType, uint delta); - bool updateParameterCheck(uint ¶meter, uint timeType, uint delta); - bool updateParameterTime(TimeValue timeValue, bool check, uint ¶meter, uint delta); + bool updateParameter(uint ¶meter, uint timeType, uint delta) const; + bool updateParameterCheck(uint ¶meter, uint timeType, uint delta) const; + bool updateParameterTime(TimeValue timeValue, bool check, uint ¶meter, uint delta) const; - bool timeCheck(TimeValue timeValue, uint ¶meter, Common::Functor0<void> *function); + bool timeCheck(TimeValue timeValue, uint ¶meter, Common::Functor0<void> *function) const; bool timeCheckCallback(TimeValue timeValue, uint ¶meter, byte callback, Common::Functor0<void> *function); bool timeCheckCallback(TimeValue timeValue, uint ¶meter, byte callback, const char *str, Common::Functor1<const char *, void> *function); bool timeCheckCallback(TimeValue timeValue, uint ¶meter, byte callback, bool check, Common::Functor1<bool, void> *function); bool timeCheckCallbackInventory(TimeValue timeValue, uint ¶meter, byte callback, Common::Functor0<void> *function); bool timeCheckCar(TimeValue timeValue, uint ¶meter, byte callback, Common::Functor0<void> *function); - void timeCheckSavepoint(TimeValue timeValue, uint ¶meter, EntityIndex entity1, EntityIndex entity2, ActionIndex action); - void timeCheckObject(TimeValue timeValue, uint ¶meter, ObjectIndex index, ObjectLocation location); + void timeCheckSavepoint(TimeValue timeValue, uint ¶meter, EntityIndex entity1, EntityIndex entity2, ActionIndex action) const; + void timeCheckObject(TimeValue timeValue, uint ¶meter, ObjectIndex index, ObjectLocation location) const; bool timeCheckCallbackAction(TimeValue timeValue, uint ¶meter); bool timeCheckPlaySoundUpdatePosition(TimeValue timeValue, uint ¶meter, byte callback, const char* sound, EntityPosition position); diff --git a/engines/lastexpress/entities/verges.cpp b/engines/lastexpress/entities/verges.cpp index 867f122d8f..d9ddb0a4d1 100644 --- a/engines/lastexpress/entities/verges.cpp +++ b/engines/lastexpress/entities/verges.cpp @@ -46,40 +46,40 @@ Verges::Verges(LastExpressEngine *engine) : Entity(engine, kEntityVerges) { ADD_CALLBACK_FUNCTION(Verges, callbackActionRestaurantOrSalon); ADD_CALLBACK_FUNCTION(Verges, savegame); ADD_CALLBACK_FUNCTION(Verges, updateEntity); - ADD_CALLBACK_FUNCTION(Verges, function9); - ADD_CALLBACK_FUNCTION(Verges, function10); + ADD_CALLBACK_FUNCTION(Verges, walkBetweenCars); + ADD_CALLBACK_FUNCTION(Verges, makeAnnouncement); ADD_CALLBACK_FUNCTION(Verges, function11); ADD_CALLBACK_FUNCTION(Verges, function12); - ADD_CALLBACK_FUNCTION(Verges, function13); + ADD_CALLBACK_FUNCTION(Verges, baggageCar); ADD_CALLBACK_FUNCTION(Verges, updateFromTime); - ADD_CALLBACK_FUNCTION(Verges, function15); - ADD_CALLBACK_FUNCTION(Verges, function16); - ADD_CALLBACK_FUNCTION(Verges, function17); + ADD_CALLBACK_FUNCTION(Verges, dialog); + ADD_CALLBACK_FUNCTION(Verges, dialog2); + ADD_CALLBACK_FUNCTION(Verges, talkAboutPassengerList); ADD_CALLBACK_FUNCTION(Verges, chapter1); ADD_CALLBACK_FUNCTION(Verges, talkHarem); ADD_CALLBACK_FUNCTION(Verges, talkPassengerList); ADD_CALLBACK_FUNCTION(Verges, talkGendarmes); - ADD_CALLBACK_FUNCTION(Verges, function22); + ADD_CALLBACK_FUNCTION(Verges, askMertensToRelayAugustInvitation); ADD_CALLBACK_FUNCTION(Verges, function23); ADD_CALLBACK_FUNCTION(Verges, policeGettingOffTrain); - ADD_CALLBACK_FUNCTION(Verges, function25); + ADD_CALLBACK_FUNCTION(Verges, policeSearch); ADD_CALLBACK_FUNCTION(Verges, chapter1Handler); ADD_CALLBACK_FUNCTION(Verges, chapter2); ADD_CALLBACK_FUNCTION(Verges, chapter2Handler); ADD_CALLBACK_FUNCTION(Verges, chapter3); ADD_CALLBACK_FUNCTION(Verges, function30); - ADD_CALLBACK_FUNCTION(Verges, function31); + ADD_CALLBACK_FUNCTION(Verges, talkAboutMax); ADD_CALLBACK_FUNCTION(Verges, function32); ADD_CALLBACK_FUNCTION(Verges, function33); ADD_CALLBACK_FUNCTION(Verges, function34); - ADD_CALLBACK_FUNCTION(Verges, function35); + ADD_CALLBACK_FUNCTION(Verges, organizeConcertInvitations); ADD_CALLBACK_FUNCTION(Verges, chapter4); ADD_CALLBACK_FUNCTION(Verges, chapter4Handler); - ADD_CALLBACK_FUNCTION(Verges, function38); + ADD_CALLBACK_FUNCTION(Verges, resetState); ADD_CALLBACK_FUNCTION(Verges, chapter5); ADD_CALLBACK_FUNCTION(Verges, chapter5Handler); - ADD_CALLBACK_FUNCTION(Verges, function41); - ADD_CALLBACK_FUNCTION(Verges, function42); + ADD_CALLBACK_FUNCTION(Verges, askPassengersToStayInCompartments); + ADD_CALLBACK_FUNCTION(Verges, end); } ////////////////////////////////////////////////////////////////////////// @@ -149,7 +149,7 @@ IMPLEMENT_FUNCTION_II(8, Verges, updateEntity, CarIndex, EntityPosition) IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION_S(9, Verges, function9) +IMPLEMENT_FUNCTION_S(9, Verges, walkBetweenCars) switch (savepoint.action) { default: break; @@ -201,7 +201,7 @@ switch (savepoint.action) { case 3: setCallback(4); - setup_function10(kCarGreenSleeping, kPosition_540, (char *)¶ms->seq1); + setup_makeAnnouncement(kCarGreenSleeping, kPosition_540, (char *)¶ms->seq1); break; case 4: @@ -225,7 +225,7 @@ switch (savepoint.action) { IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION_IIS(10, Verges, function10, CarIndex, EntityPosition) +IMPLEMENT_FUNCTION_IIS(10, Verges, makeAnnouncement, CarIndex, EntityPosition) switch (savepoint.action) { default: break; @@ -404,7 +404,7 @@ IMPLEMENT_FUNCTION(12, Verges, function12) IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION_I(13, Verges, function13, bool) +IMPLEMENT_FUNCTION_I(13, Verges, baggageCar, bool) switch (savepoint.action) { default: break; @@ -449,7 +449,7 @@ IMPLEMENT_FUNCTION_I(14, Verges, updateFromTime, uint32) IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION_IS(15, Verges, function15, EntityIndex) +IMPLEMENT_FUNCTION_IS(15, Verges, dialog, EntityIndex) switch (savepoint.action) { default: break; @@ -486,7 +486,7 @@ IMPLEMENT_FUNCTION_IS(15, Verges, function15, EntityIndex) IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION_ISS(16, Verges, function16, EntityIndex) +IMPLEMENT_FUNCTION_ISS(16, Verges, dialog2, EntityIndex) switch (savepoint.action) { default: break; @@ -526,7 +526,7 @@ IMPLEMENT_FUNCTION_ISS(16, Verges, function16, EntityIndex) IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION(17, Verges, function17) +IMPLEMENT_FUNCTION(17, Verges, talkAboutPassengerList) switch (savepoint.action) { default: break; @@ -548,7 +548,7 @@ IMPLEMENT_FUNCTION(17, Verges, function17) case 2: setCallback(3); - setup_function15(kEntityMertens, "TRA1291"); + setup_dialog(kEntityMertens, "TRA1291"); break; case 3: @@ -611,7 +611,7 @@ IMPLEMENT_FUNCTION(21, Verges, talkGendarmes) IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION(22, Verges, function22) +IMPLEMENT_FUNCTION(22, Verges, askMertensToRelayAugustInvitation) switch (savepoint.action) { default: break; @@ -634,10 +634,10 @@ IMPLEMENT_FUNCTION(22, Verges, function22) case 2: if (getEvent(kEventMertensAskTylerCompartment) || getEvent(kEventMertensAskTylerCompartmentD) || getEvent(kEventMertensAugustWaiting)) { setCallback(3); - setup_function16(kEntityMertens, "TRA1200", "TRA1201"); + setup_dialog2(kEntityMertens, "TRA1200", "TRA1201"); } else { setCallback(4); - setup_function16(kEntityMertens, "TRA1200A", "TRA1201"); + setup_dialog2(kEntityMertens, "TRA1200A", "TRA1201"); } break; @@ -714,7 +714,7 @@ IMPLEMENT_FUNCTION(24, Verges, policeGettingOffTrain) IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION(25, Verges, function25) +IMPLEMENT_FUNCTION(25, Verges, policeSearch) switch (savepoint.action) { default: break; @@ -774,10 +774,10 @@ IMPLEMENT_FUNCTION(25, Verges, function25) if (getData()->car == kCarRedSleeping) { setCallback(6); - setup_function10(kCarGreenSleeping, kPosition_540, "TRA1005"); + setup_makeAnnouncement(kCarGreenSleeping, kPosition_540, "TRA1005"); } else { setCallback(7); - setup_function10(kCarRedSleeping, kPosition_9460, "TRA1006"); + setup_makeAnnouncement(kCarRedSleeping, kPosition_9460, "TRA1006"); } break; } @@ -805,7 +805,7 @@ IMPLEMENT_FUNCTION(25, Verges, function25) getSavePoints()->push(kEntityVerges, kEntityCoudert, kAction168254872); setCallback(4); - setup_function10(kCarRedSleeping, kPosition_9460, "TRA1006"); + setup_makeAnnouncement(kCarRedSleeping, kPosition_9460, "TRA1006"); break; case 4: @@ -838,7 +838,7 @@ IMPLEMENT_FUNCTION(25, Verges, function25) getSavePoints()->push(kEntityVerges, kEntityCoudert, kAction168254872); setCallback(10); - setup_function10(kCarGreenSleeping, kPosition_540, "TRA1006"); + setup_makeAnnouncement(kCarGreenSleeping, kPosition_540, "TRA1006"); break; case 10: @@ -892,14 +892,14 @@ IMPLEMENT_FUNCTION(26, Verges, chapter1Handler) label_callback1: if (getEntities()->isInBaggageCarEntrance(kEntityPlayer)) { setCallback(2); - setup_function13(false); + setup_baggageCar(false); break; } label_callback2: if (ENTITY_PARAM(0, 7)) { setCallback(3); - setup_function25(); + setup_policeSearch(); break; } @@ -907,7 +907,7 @@ label_callback3: if (params->param6) goto label_callback12; - if (Entity::timeCheckCallback(kTimeChapter1, params->param7, 4, "TRA1001", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + if (Entity::timeCheckCallback(kTimeChapter1, params->param7, 4, "TRA1001", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) break; label_callback4: @@ -923,19 +923,19 @@ label_callback4: } label_callback8: - if (Entity::timeCheckCallback(kTime1107000, CURRENT_PARAM(1, 1), 9, "TRA1001A", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + if (Entity::timeCheckCallback(kTime1107000, CURRENT_PARAM(1, 1), 9, "TRA1001A", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) break; label_callback9: - if (Entity::timeCheckCallback(kTime1134000, CURRENT_PARAM(1, 2), 10, "TRA1002", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + if (Entity::timeCheckCallback(kTime1134000, CURRENT_PARAM(1, 2), 10, "TRA1002", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) break; label_callback10: - if (Entity::timeCheckCallback(kTime1165500, CURRENT_PARAM(1, 3), 11, "TRA1003", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + if (Entity::timeCheckCallback(kTime1165500, CURRENT_PARAM(1, 3), 11, "TRA1003", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) break; label_callback11: - if (Entity::timeCheckCallback(kTime1225800, CURRENT_PARAM(1, 4), 12, "TRA1004", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + if (Entity::timeCheckCallback(kTime1225800, CURRENT_PARAM(1, 4), 12, "TRA1004", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) break; label_callback12: @@ -955,7 +955,7 @@ label_callback13: label_callback14: if (ENTITY_PARAM(0, 3) && !params->param4 && (getState()->time < kTime1134000 || getState()->time > kTime1156500)) { setCallback(15); - setup_function17(); + setup_talkAboutPassengerList(); break; } @@ -963,14 +963,14 @@ label_callback15: if (ENTITY_PARAM(0, 1) && !params->param5) { if (getState()->time < kTime1134000 || getState()->time > kTime1156500) { setCallback(16); - setup_function22(); + setup_askMertensToRelayAugustInvitation(); } } break; case kActionOpenDoor: setCallback(17); - setup_function13(savepoint.param.intValue < 106 ? true : false); + setup_baggageCar(savepoint.param.intValue < 106 ? true : false); break; case kActionDefault: @@ -1006,7 +1006,7 @@ label_callback15: case 6: setCallback(7); - setup_function15(kEntityMertens, "TRA1202"); + setup_dialog(kEntityMertens, "TRA1202"); break; case 7: @@ -1084,11 +1084,11 @@ IMPLEMENT_FUNCTION(28, Verges, chapter2Handler) case kActionNone: if (getEntities()->isInBaggageCarEntrance(kEntityPlayer)) { setCallback(1); - setup_function13(false); + setup_baggageCar(false); } label_callback_1: - if (Entity::timeCheckCallback(kTime1818900, params->param1, 2, "Tra2177", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + if (Entity::timeCheckCallback(kTime1818900, params->param1, 2, "Tra2177", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) break; label_callback_2: @@ -1117,7 +1117,7 @@ label_callback_6: if (ENTITY_PARAM(0, 3)) { setCallback(7); - setup_function17(); + setup_talkAboutPassengerList(); } break; @@ -1130,7 +1130,7 @@ label_callback_6: case kActionOpenDoor: setCallback(8); - setup_function13(savepoint.param.intValue < 106); + setup_baggageCar(savepoint.param.intValue < 106); break; case kActionDefault: @@ -1155,7 +1155,7 @@ label_callback_6: case 4: setCallback(5); - setup_function15(kEntityCoudert, "TRA2100"); + setup_dialog(kEntityCoudert, "TRA2100"); break; case 5: @@ -1177,7 +1177,7 @@ IMPLEMENT_FUNCTION(29, Verges, chapter3) break; case kActionNone: - setup_function23(); + setup_function33(); break; case kActionDefault: @@ -1221,7 +1221,7 @@ IMPLEMENT_FUNCTION_S(30, Verges, function30) case 2: setCallback(3); - setup_function15(kEntityCoudert, (char *)¶ms->seq1); + setup_dialog(kEntityCoudert, (char *)¶ms->seq1); break; case 3: @@ -1238,7 +1238,7 @@ IMPLEMENT_FUNCTION_S(30, Verges, function30) IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION(31, Verges, function31) +IMPLEMENT_FUNCTION(31, Verges, talkAboutMax) switch (savepoint.action) { default: break; @@ -1260,7 +1260,7 @@ IMPLEMENT_FUNCTION(31, Verges, function31) case 2: setCallback(3); - setup_function15(kEntityCoudert, "TRA3015"); + setup_dialog(kEntityCoudert, "TRA3015"); break; case 3: @@ -1289,7 +1289,7 @@ IMPLEMENT_FUNCTION(32, Verges, function32) if (getState()->time > kTime2263500 && !params->param1) { params->param1 = 1; setCallback(5); - setup_function10(kCarRedSleeping, kPosition_9460, "TRA3006"); + setup_makeAnnouncement(kCarRedSleeping, kPosition_9460, "TRA3006"); break; } break; @@ -1341,7 +1341,7 @@ IMPLEMENT_FUNCTION(32, Verges, function32) case 3: setCallback(4); - setup_function10(kCarGreenSleeping, kPosition_540, "TRA3004"); + setup_makeAnnouncement(kCarGreenSleeping, kPosition_540, "TRA3004"); break; case 4: @@ -1412,7 +1412,7 @@ IMPLEMENT_FUNCTION(33, Verges, function33) getSavePoints()->push(kEntityVerges, kEntityAbbot, kAction192054567); setCallback(6); - setup_function9("Tra3010"); + setup_walkBetweenCars("Tra3010"); break; case 6: @@ -1432,42 +1432,42 @@ IMPLEMENT_FUNCTION(34, Verges, function34) case kActionNone: if (getEntities()->isInBaggageCarEntrance(kEntityPlayer)) { setCallback(1); - setup_function13(false); + setup_baggageCar(false); break; } label_callback_1: if (ENTITY_PARAM(0, 4)) { setCallback(2); - setup_function31(); + setup_talkAboutMax(); break; } label_callback_2: if (ENTITY_PARAM(0, 3)) { setCallback(3); - setup_function17(); + setup_talkAboutPassengerList(); break; } label_callback_3: - if (Entity::timeCheckCallback(kTime1971000, params->param1, 4, "Tra3001", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + if (Entity::timeCheckCallback(kTime1971000, params->param1, 4, "Tra3001", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) break; label_callback_4: - if (Entity::timeCheckCallback(kTime1998000, params->param2, 5, "Tra3010a", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + if (Entity::timeCheckCallback(kTime1998000, params->param2, 5, "Tra3010a", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) break; label_callback_5: - if (Entity::timeCheckCallback(kTime2016000, params->param3, 6, WRAP_SETUP_FUNCTION(Verges, setup_function35))) + if (Entity::timeCheckCallback(kTime2016000, params->param3, 6, WRAP_SETUP_FUNCTION(Verges, setup_organizeConcertInvitations))) break; label_callback_6: - if (Entity::timeCheckCallback(kTime2070000, params->param4, 7, "Tra3002", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + if (Entity::timeCheckCallback(kTime2070000, params->param4, 7, "Tra3002", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) break; label_callback_7: - if (Entity::timeCheckCallback(kTime2142000, params->param5, 8, "Tra3003", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + if (Entity::timeCheckCallback(kTime2142000, params->param5, 8, "Tra3003", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) break; label_callback_8: @@ -1480,7 +1480,7 @@ label_callback_9: case kActionOpenDoor: setCallback(11); - setup_function13(savepoint.param.intValue < 106); + setup_baggageCar(savepoint.param.intValue < 106); break; case kActionCallback: @@ -1520,7 +1520,7 @@ label_callback_9: IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION(35, Verges, function35) +IMPLEMENT_FUNCTION(35, Verges, organizeConcertInvitations) switch (savepoint.action) { default: break; @@ -1542,7 +1542,7 @@ IMPLEMENT_FUNCTION(35, Verges, function35) case 2: setCallback(3); - setup_function15(kEntityMertens, "Tra3011A"); + setup_dialog(kEntityMertens, "Tra3011A"); break; case 3: @@ -1554,7 +1554,7 @@ IMPLEMENT_FUNCTION(35, Verges, function35) case 4: setCallback(5); - setup_function15(kEntityMertens, "Tra3011"); + setup_dialog(kEntityMertens, "Tra3011"); break; case 5: @@ -1609,7 +1609,7 @@ IMPLEMENT_FUNCTION(37, Verges, chapter4Handler) case kActionNone: if (getEntities()->isInBaggageCarEntrance(kEntityPlayer)) { setCallback(1); - setup_function13(false); + setup_baggageCar(false); break; } @@ -1617,42 +1617,42 @@ label_callback_1: if (ENTITY_PARAM(0, 6)) { if (ENTITY_PARAM(0, 3)) { setCallback(2); - setup_function17(); + setup_talkAboutPassengerList(); break; } label_callback_2: - if (Entity::timeCheckCallback(kTime2349000, params->param1, 3, "Tra1001", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + if (Entity::timeCheckCallback(kTime2349000, params->param1, 3, "Tra1001", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) break; label_callback_3: - if (Entity::timeCheckCallback(kTime2378700, params->param2, 4, "Tra4001", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + if (Entity::timeCheckCallback(kTime2378700, params->param2, 4, "Tra4001", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) break; label_callback_4: - if (Entity::timeCheckCallback(kTime2403000, params->param3, 5, "Tra1001A", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + if (Entity::timeCheckCallback(kTime2403000, params->param3, 5, "Tra1001A", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) break; label_callback_5: - if (Entity::timeCheckCallback(kTime2414700, params->param4, 6, "Tra4002", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + if (Entity::timeCheckCallback(kTime2414700, params->param4, 6, "Tra4002", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) break; label_callback_6: - if (Entity::timeCheckCallback(kTime2484000, params->param5, 7, "Tra4003", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + if (Entity::timeCheckCallback(kTime2484000, params->param5, 7, "Tra4003", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) break; label_callback_7: - if (Entity::timeCheckCallback(kTime2511000, params->param6, 8, "Tra4004", WRAP_SETUP_FUNCTION_S(Verges, setup_function9))) + if (Entity::timeCheckCallback(kTime2511000, params->param6, 8, "Tra4004", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars))) break; } label_callback_8: - Entity::timeCheckCallback(kTime2538000, params->param7, 9, "Tra4005", WRAP_SETUP_FUNCTION_S(Verges, setup_function9)); + Entity::timeCheckCallback(kTime2538000, params->param7, 9, "Tra4005", WRAP_SETUP_FUNCTION_S(Verges, setup_walkBetweenCars)); break; case kActionOpenDoor: setCallback(10); - setup_function13(savepoint.param.intValue < 106); + setup_baggageCar(savepoint.param.intValue < 106); break; case kActionDefault: @@ -1697,7 +1697,7 @@ label_callback_8: IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION(38, Verges, function38) +IMPLEMENT_FUNCTION(38, Verges, resetState) switch (savepoint.action) { default: break; @@ -1803,14 +1803,14 @@ IMPLEMENT_FUNCTION(40, Verges, chapter5Handler) getAction()->playAnimation(kEventCathFreePassengers); getSavePoints()->pushAll(kEntityVerges, kActionProceedChapter5); getScenes()->loadSceneFromPosition(kCarRedSleeping, 40); - setup_function41(); + setup_askPassengersToStayInCompartments(); } break; } IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION(41, Verges, function41) +IMPLEMENT_FUNCTION(41, Verges, askPassengersToStayInCompartments) switch (savepoint.action) { default: break; @@ -1822,7 +1822,7 @@ IMPLEMENT_FUNCTION(41, Verges, function41) getData()->location = kLocationInsideCompartment; setCallback(1); - setup_function10(kCarRedSleeping, kPosition_2000, "Tra5001"); + setup_makeAnnouncement(kCarRedSleeping, kPosition_2000, "Tra5001"); break; case kActionCallback: @@ -1852,7 +1852,7 @@ IMPLEMENT_FUNCTION(41, Verges, function41) break; case 4: - setup_function42(); + setup_end(); break; } break; @@ -1860,7 +1860,7 @@ IMPLEMENT_FUNCTION(41, Verges, function41) IMPLEMENT_FUNCTION_END ////////////////////////////////////////////////////////////////////////// -IMPLEMENT_FUNCTION(42, Verges, function42) +IMPLEMENT_FUNCTION(42, Verges, end) if (savepoint.action == kActionDefault) getEntities()->clearSequences(kEntityVerges); IMPLEMENT_FUNCTION_END @@ -1891,7 +1891,7 @@ void Verges::talk(const SavePoint &savepoint, const char *sound1, const char *so case 2: setCallback(3); - setup_function15(kEntityCoudert, sound1); + setup_dialog(kEntityCoudert, sound1); break; case 3: @@ -1901,7 +1901,7 @@ void Verges::talk(const SavePoint &savepoint, const char *sound1, const char *so case 4: setCallback(5); - setup_function15(kEntityMertens, sound2); + setup_dialog(kEntityMertens, sound2); break; case 5: diff --git a/engines/lastexpress/entities/verges.h b/engines/lastexpress/entities/verges.h index 82381043d3..93cc190d1e 100644 --- a/engines/lastexpress/entities/verges.h +++ b/engines/lastexpress/entities/verges.h @@ -87,11 +87,11 @@ public: */ DECLARE_FUNCTION_2(updateEntity, CarIndex car, EntityPosition entityPosition) - DECLARE_FUNCTION_1(function9, const char *soundName) - DECLARE_FUNCTION_3(function10, CarIndex car, EntityPosition entityPosition, const char *soundName) + DECLARE_FUNCTION_1(walkBetweenCars, const char *soundName) + DECLARE_FUNCTION_3(makeAnnouncement, CarIndex car, EntityPosition entityPosition, const char *soundName) DECLARE_FUNCTION(function11) DECLARE_FUNCTION(function12) - DECLARE_FUNCTION_1(function13, bool) + DECLARE_FUNCTION_1(baggageCar, bool) /** * Updates parameter 2 using time value @@ -100,9 +100,9 @@ public: */ DECLARE_FUNCTION_1(updateFromTime, uint32 time) - DECLARE_FUNCTION_2(function15, EntityIndex entity, const char *soundName) - DECLARE_FUNCTION_3(function16, EntityIndex entityIndex, const char *soundName1, const char *soundName2) - DECLARE_FUNCTION(function17) + DECLARE_FUNCTION_2(dialog, EntityIndex entity, const char *soundName) + DECLARE_FUNCTION_3(dialog2, EntityIndex entityIndex, const char *soundName1, const char *soundName2) + DECLARE_FUNCTION(talkAboutPassengerList) /** * Setup Chapter 1 @@ -112,10 +112,10 @@ public: DECLARE_FUNCTION_NOSETUP(talkHarem) DECLARE_FUNCTION(talkPassengerList) DECLARE_FUNCTION(talkGendarmes) - DECLARE_FUNCTION(function22) + DECLARE_FUNCTION(askMertensToRelayAugustInvitation) DECLARE_FUNCTION(function23) DECLARE_FUNCTION(policeGettingOffTrain) - DECLARE_FUNCTION(function25) + DECLARE_FUNCTION(policeSearch) /** * Handle Chapter 1 events @@ -138,11 +138,11 @@ public: DECLARE_FUNCTION(chapter3) DECLARE_FUNCTION_1(function30, const char *soundName) - DECLARE_FUNCTION(function31) + DECLARE_FUNCTION(talkAboutMax) DECLARE_FUNCTION(function32) DECLARE_FUNCTION(function33) DECLARE_FUNCTION(function34) - DECLARE_FUNCTION(function35) + DECLARE_FUNCTION(organizeConcertInvitations) /** * Setup Chapter 4 @@ -154,7 +154,7 @@ public: */ DECLARE_FUNCTION(chapter4Handler) - DECLARE_FUNCTION(function38) + DECLARE_FUNCTION(resetState) /** * Setup Chapter 5 @@ -166,8 +166,8 @@ public: */ DECLARE_FUNCTION(chapter5Handler) - DECLARE_FUNCTION(function41) - DECLARE_FUNCTION(function42) + DECLARE_FUNCTION(askPassengersToStayInCompartments) + DECLARE_FUNCTION(end) private: void talk(const SavePoint &savepoint, const char *sound1, const char *sound2); diff --git a/engines/lastexpress/fight/fight.cpp b/engines/lastexpress/fight/fight.cpp index b00c1732e7..49a9b85657 100644 --- a/engines/lastexpress/fight/fight.cpp +++ b/engines/lastexpress/fight/fight.cpp @@ -39,10 +39,8 @@ #include "lastexpress/game/state.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/graphics.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" diff --git a/engines/lastexpress/game/action.cpp b/engines/lastexpress/game/action.cpp index 60a309518a..796abf2ce7 100644 --- a/engines/lastexpress/game/action.cpp +++ b/engines/lastexpress/game/action.cpp @@ -394,7 +394,7 @@ Action::Action(LastExpressEngine *engine) : _engine(engine) { } Action::~Action() { - for (int i = 0; i < (int)_actions.size(); i++) + for (uint i = 0; i < _actions.size(); i++) SAFE_DELETE(_actions[i]); _actions.clear(); @@ -421,8 +421,6 @@ SceneIndex Action::processHotspot(const SceneHotspot &hotspot) { // Action 0 IMPLEMENT_ACTION(dummy) error("[Action::action_dummy] Dummy action function called (hotspot action: %d)", hotspot.action); - - return kSceneInvalid; } ////////////////////////////////////////////////////////////////////////// diff --git a/engines/lastexpress/game/beetle.cpp b/engines/lastexpress/game/beetle.cpp index 2a72459697..d7a369ba40 100644 --- a/engines/lastexpress/game/beetle.cpp +++ b/engines/lastexpress/game/beetle.cpp @@ -94,7 +94,7 @@ void Beetle::load() { // Check that all sequences are loaded properly _data->isLoaded = true; - for (int i = 0; i < (int)_data->sequences.size(); i++) { + for (uint i = 0; i < _data->sequences.size(); i++) { if (!_data->sequences[i]->isLoaded()) { _data->isLoaded = false; break; @@ -351,6 +351,9 @@ void Beetle::drawUpdate() { } void Beetle::invertDirection() { + if (!_data) + error("[Beetle::invertDirection] Sequences have not been loaded"); + switch (_data->indexes[_data->offset]) { default: break; diff --git a/engines/lastexpress/game/entities.cpp b/engines/lastexpress/game/entities.cpp index 51db635bed..fafbd7cb64 100644 --- a/engines/lastexpress/game/entities.cpp +++ b/engines/lastexpress/game/entities.cpp @@ -181,7 +181,7 @@ Entities::Entities(LastExpressEngine *engine) : _engine(engine) { Entities::~Entities() { SAFE_DELETE(_header); - for (int i = 0; i < (int)_entities.size(); i++) + for (uint i = 0; i < _entities.size(); i++) SAFE_DELETE(_entities[i]); _entities.clear(); @@ -669,7 +669,7 @@ void Entities::executeCallbacks() { ////////////////////////////////////////////////////////////////////////// // Processing ////////////////////////////////////////////////////////////////////////// -void Entities::incrementDirectionCounter(EntityData::EntityCallData *data) { +void Entities::incrementDirectionCounter(EntityData::EntityCallData *data) const { data->doProcessEntity = false; if (data->direction == kDirectionRight || (data->direction == kDirectionSwitch && data->directionSwitch == kDirectionRight)) diff --git a/engines/lastexpress/game/entities.h b/engines/lastexpress/game/entities.h index a9de7931f0..81aed627aa 100644 --- a/engines/lastexpress/game/entities.h +++ b/engines/lastexpress/game/entities.h @@ -344,7 +344,7 @@ private: uint _positions[_positionsCount]; void executeCallbacks(); - void incrementDirectionCounter(EntityData::EntityCallData *data); + void incrementDirectionCounter(EntityData::EntityCallData *data) const; void processEntity(EntityIndex entity); void drawSequence(EntityIndex entity, const char *sequence, EntityDirection direction) const; diff --git a/engines/lastexpress/game/inventory.cpp b/engines/lastexpress/game/inventory.cpp index 52c00ece31..11e7369ee1 100644 --- a/engines/lastexpress/game/inventory.cpp +++ b/engines/lastexpress/game/inventory.cpp @@ -35,10 +35,8 @@ #include "lastexpress/menu/menu.h" #include "lastexpress/sound/queue.h" -#include "lastexpress/sound/sound.h" #include "lastexpress/graphics.h" -#include "lastexpress/helpers.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" @@ -621,7 +619,7 @@ void Inventory::drawEgg() const { // Blinking egg: we need to blink the egg for delta time, with the blinking getting faster until it's always lit. void Inventory::drawBlinkingEgg(uint ticks) { - uint globalTimer = getGlobalTimer(); + uint globalTimer = (uint)getGlobalTimer(); uint timerValue = (getProgress().jacket == kJacketGreen) ? 450 : 225; if (globalTimer == timerValue || globalTimer == 900) { @@ -655,7 +653,7 @@ void Inventory::drawBlinkingEgg(uint ticks) { } void Inventory::blinkEgg() { - drawItem((CursorStyle)(getMenu()->getGameId() + 39), 608, 448, (_blinkingBrightness == 0) ? -1 : _blinkingBrightness); + drawItem((CursorStyle)(getMenu()->getGameId() + 39), 608, 448, (_blinkingBrightness == 0) ? -1 : (int16)_blinkingBrightness); askForRedraw(); diff --git a/engines/lastexpress/game/logic.cpp b/engines/lastexpress/game/logic.cpp index 1696f100ff..09104d1bf9 100644 --- a/engines/lastexpress/game/logic.cpp +++ b/engines/lastexpress/game/logic.cpp @@ -48,7 +48,6 @@ #include "lastexpress/sound/queue.h" -#include "lastexpress/graphics.h" #include "lastexpress/lastexpress.h" #include "lastexpress/resource.h" diff --git a/engines/lastexpress/game/savegame.cpp b/engines/lastexpress/game/savegame.cpp index 360e99146a..021dc40bb9 100644 --- a/engines/lastexpress/game/savegame.cpp +++ b/engines/lastexpress/game/savegame.cpp @@ -78,7 +78,7 @@ uint32 SavegameStream::read(void *dataPtr, uint32 dataSize) { uint32 SavegameStream::readUncompressed(void *dataPtr, uint32 dataSize) { if ((int32)dataSize > size() - pos()) { - dataSize = size() - pos(); + dataSize = (uint32)(size() - pos()); _eos = true; } memcpy(dataPtr, getData() + pos(), dataSize); @@ -230,7 +230,7 @@ uint32 SavegameStream::writeCompressed(const void *dataPtr, uint32 dataSize) { if (*data != _previousValue || _repeatCount >= 255) { if (_previousValue) { writeBuffer(0xFF, true); - writeBuffer(_repeatCount, true); + writeBuffer((uint8)_repeatCount, true); writeBuffer(_previousValue, true); _previousValue = *data++; @@ -255,7 +255,7 @@ uint32 SavegameStream::writeCompressed(const void *dataPtr, uint32 dataSize) { } writeBuffer(0xFD, true); - writeBuffer(_repeatCount, true); + writeBuffer((uint8)_repeatCount, true); _previousValue = *data++; _valueCount = 1; @@ -348,11 +348,12 @@ uint32 SavegameStream::readCompressed(void *dataPtr, uint32 dataSize) { // Constructors ////////////////////////////////////////////////////////////////////////// -SaveLoad::SaveLoad(LastExpressEngine *engine) : _engine(engine), _savegame(NULL), _gameTicksLastSavegame(0) { +SaveLoad::SaveLoad(LastExpressEngine *engine) : _engine(engine), _savegame(NULL), _gameTicksLastSavegame(0), _entity(kEntityPlayer) { } SaveLoad::~SaveLoad() { clear(true); + _savegame = NULL; // Zero passed pointers _engine = NULL; @@ -481,10 +482,10 @@ void SaveLoad::clear(bool clearStream) { // Save & Load ////////////////////////////////////////////////////////////////////////// -// Load game -void SaveLoad::loadGame(GameId id) { +// Load last saved game +void SaveLoad::loadLastGame() { if (!_savegame) - error("[SaveLoad::loadGame] No savegame stream present"); + error("[SaveLoad::loadLastGame] No savegame stream present"); // Rewind current savegame _savegame->seek(0); @@ -521,7 +522,29 @@ void SaveLoad::loadGame(GameId id) { } // Load a specific game entry -void SaveLoad::loadGame(GameId id, uint32 index) { +void SaveLoad::loadGame(uint32 index) { + if (!_savegame) + error("[SaveLoad::loadLastGame] No savegame stream present"); + + // Rewind current savegame + _savegame->seek(0); + + // Write main header (with selected index) + SavegameMainHeader header; + header.count = index; + header.brightness = getState()->brightness; + header.volume = getState()->volume; + + Common::Serializer ser(NULL, _savegame); + header.saveLoadWithSerializer(ser); + + // TODO + // Go to the entry + // Load the entry + // Get offset (main and entry) + // Write main header again with correct entry offset + // Setup game and start + error("[SaveLoad::loadGame] Not implemented! (only loading the last entry is working for now)"); } @@ -550,7 +573,7 @@ void SaveLoad::saveGame(SavegameType type, EntityIndex entity, uint32 value) { entry.saveLoadWithSerializer(ser); if (!entry.isValid()) { - error("[SaveLoad::saveGame] Invalid entry. This savegame might be corrupted"); + warning("[SaveLoad::saveGame] Invalid entry. This savegame might be corrupted"); _savegame->seek(header.offset); } else if (getState()->time < entry.time || (type == kSavegameTypeTickInterval && getState()->time == entry.time)) { // Not ready to save a game, skipping! @@ -634,6 +657,9 @@ bool SaveLoad::loadMainHeader(Common::InSaveFile *stream, SavegameMainHeader *he // Entries ////////////////////////////////////////////////////////////////////////// uint32 SaveLoad::writeValue(Common::Serializer &ser, const char *name, Common::Functor1<Common::Serializer &, void> *function, uint size) { + if (!_savegame) + error("[SaveLoad::writeValue] Stream not initialized properly"); + debugC(kLastExpressDebugSavegame, "Savegame: Writing %s: %u bytes", name, size); uint32 prevPosition = (uint32)_savegame->pos(); @@ -652,6 +678,9 @@ uint32 SaveLoad::writeValue(Common::Serializer &ser, const char *name, Common::F } uint32 SaveLoad::readValue(Common::Serializer &ser, const char *name, Common::Functor1<Common::Serializer &, void> *function, uint size) { + if (!_savegame) + error("[SaveLoad::readValue] Stream not initialized properly"); + debugC(kLastExpressDebugSavegame, "Savegame: Reading %s: %u bytes", name, size); uint32 prevPosition = (uint32)_savegame->pos(); diff --git a/engines/lastexpress/game/savegame.h b/engines/lastexpress/game/savegame.h index 8656b2ee86..361957227e 100644 --- a/engines/lastexpress/game/savegame.h +++ b/engines/lastexpress/game/savegame.h @@ -153,8 +153,8 @@ public: uint32 init(GameId id, bool resetHeaders); // Save & Load - void loadGame(GameId id); - void loadGame(GameId id, uint32 index); + void loadLastGame(); + void loadGame(uint32 index); void saveGame(SavegameType type, EntityIndex entity, uint32 value); void loadVolumeBrightness(); diff --git a/engines/lastexpress/game/savepoint.cpp b/engines/lastexpress/game/savepoint.cpp index 6b2dfc5930..8d14ec386b 100644 --- a/engines/lastexpress/game/savepoint.cpp +++ b/engines/lastexpress/game/savepoint.cpp @@ -202,7 +202,7 @@ void SavePoints::callAndProcess() { // Misc ////////////////////////////////////////////////////////////////////////// bool SavePoints::updateEntityFromData(const SavePoint &savepoint) { - for (int i = 0; i < (int)_data.size(); i++) { + for (uint i = 0; i < _data.size(); i++) { // Not a data savepoint! if (!_data[i].entity1) @@ -210,7 +210,7 @@ bool SavePoints::updateEntityFromData(const SavePoint &savepoint) { // Found our data! if (_data[i].entity1 == savepoint.entity1 && _data[i].action == savepoint.action) { - debugC(8, kLastExpressDebugLogic, "Update entity from data: entity1=%s, action=%s, param=%d", ENTITY_NAME(_data[i].entity1), ACTION_NAME(_data[i].action), _data[i].param); + debugC(8, kLastExpressDebugLogic, "Update entity from data: entity1=%s, action=%s, param=%u", ENTITY_NAME(_data[i].entity1), ACTION_NAME(_data[i].action), _data[i].param); // the SavePoint param value is the index of the entity call parameter to update getEntities()->get(_data[i].entity1)->getParamData()->updateParameters(_data[i].param); diff --git a/engines/lastexpress/game/scenes.cpp b/engines/lastexpress/game/scenes.cpp index 3cda900757..a2c7226b93 100644 --- a/engines/lastexpress/game/scenes.cpp +++ b/engines/lastexpress/game/scenes.cpp @@ -739,7 +739,7 @@ void SceneManager::resetQueue() { _queue.clear(); } -void SceneManager::setCoordinates(Common::Rect rect) { +void SceneManager::setCoordinates(const Common::Rect &rect) { _flagCoordinates = true; if (_coords.right > rect.right) diff --git a/engines/lastexpress/game/scenes.h b/engines/lastexpress/game/scenes.h index a866c65111..1c7ae85f98 100644 --- a/engines/lastexpress/game/scenes.h +++ b/engines/lastexpress/game/scenes.h @@ -79,7 +79,7 @@ public: void removeAndRedraw(SequenceFrame **frame, bool doRedraw); void resetQueue(); void setCoordinates(SequenceFrame *frame); - void setCoordinates(Common::Rect rect); + void setCoordinates(const Common::Rect &rect); // Helpers SceneIndex getSceneIndexFromPosition(CarIndex car, Position position, int param3 = -1); diff --git a/engines/lastexpress/menu/menu.cpp b/engines/lastexpress/menu/menu.cpp index 6a453aee99..c48e55bb55 100644 --- a/engines/lastexpress/menu/menu.cpp +++ b/engines/lastexpress/menu/menu.cpp @@ -916,13 +916,13 @@ void Menu::startGame() { if (_lastIndex == _index) { setGlobalTimer(0); if (_index) { - getSaveLoad()->loadGame(_gameId); + getSaveLoad()->loadLastGame(); } else { getLogic()->resetState(); getEntities()->setup(true, kEntityPlayer); } } else { - getSaveLoad()->loadGame(_gameId, _index); + getSaveLoad()->loadGame(_index); } } diff --git a/engines/lastexpress/sound/entry.cpp b/engines/lastexpress/sound/entry.cpp index f2a063e45f..3d22657124 100644 --- a/engines/lastexpress/sound/entry.cpp +++ b/engines/lastexpress/sound/entry.cpp @@ -116,10 +116,8 @@ void SoundEntry::close() { } void SoundEntry::play() { - if (!_stream) { + if (!_stream) error("[SoundEntry::play] stream has been disposed"); - return; - } // Prepare sound stream if (!_soundStream) diff --git a/engines/lastexpress/sound/queue.cpp b/engines/lastexpress/sound/queue.cpp index d72acfd8a0..8904b48930 100644 --- a/engines/lastexpress/sound/queue.cpp +++ b/engines/lastexpress/sound/queue.cpp @@ -67,6 +67,8 @@ void SoundQueue::handleTimer() { for (Common::List<SoundEntry *>::iterator i = _soundList.begin(); i != _soundList.end(); ++i) { SoundEntry *entry = (*i); + if (entry == NULL) + error("[SoundQueue::handleTimer] Invalid entry found in sound queue"); // When the entry has stopped playing, we remove his buffer if (entry->isFinished()) { @@ -123,6 +125,8 @@ void SoundQueue::updateQueue() { for (Common::List<SoundEntry *>::iterator it = _soundList.begin(); it != _soundList.end(); ++it) { SoundEntry *entry = *it; + if (entry == NULL) + error("[SoundQueue::updateQueue] Invalid entry found in sound queue"); // Original removes the entry data from the cache and sets the archive as not loaded // and if the sound data buffer is not full, loads a new entry to be played based on @@ -179,6 +183,8 @@ void SoundQueue::clearQueue() { for (Common::List<SoundEntry *>::iterator i = _soundList.begin(); i != _soundList.end(); ++i) { SoundEntry *entry = (*i); + if (entry == NULL) + error("[SoundQueue::clearQueue] Invalid entry found in sound queue"); // Delete entry entry->close(); diff --git a/engines/mohawk/myst_stacks/dni.cpp b/engines/mohawk/myst_stacks/dni.cpp index cae165ccf0..d103105c2d 100644 --- a/engines/mohawk/myst_stacks/dni.cpp +++ b/engines/mohawk/myst_stacks/dni.cpp @@ -109,7 +109,7 @@ void Dni::o_handPage(uint16 op, uint16 var, uint16 argc, uint16 *argv) { _vm->setMainCursor(kDefaultMystCursor); // Play movie end (atrus leaving) - _vm->_video->setVideoBounds(atrus, Audio::Timestamp(0, 14813, 600), Audio::Timestamp(0xFFFFFFFF)); + _vm->_video->setVideoBounds(atrus, Audio::Timestamp(0, 14813, 600), _vm->_video->getDuration(atrus)); _vm->_video->setVideoLooping(atrus, false); _atrusLeft = true; diff --git a/engines/mohawk/video.cpp b/engines/mohawk/video.cpp index 18d609c513..0ed4f38b53 100644 --- a/engines/mohawk/video.cpp +++ b/engines/mohawk/video.cpp @@ -29,6 +29,7 @@ #include "common/textconsole.h" #include "common/system.h" +#include "graphics/palette.h" #include "graphics/surface.h" #include "video/qt_decoder.h" @@ -43,13 +44,12 @@ void VideoEntry::clear() { loop = false; enabled = false; start = Audio::Timestamp(0, 1); - end = Audio::Timestamp(0xFFFFFFFF, 1); // Largest possible, there is an endOfVideo() check anyway filename.clear(); id = -1; } bool VideoEntry::endOfVideo() { - return !video || video->endOfVideo() || video->getTime() >= (uint)end.msecs(); + return !video || video->endOfVideo(); } VideoManager::VideoManager(MohawkEngine* vm) : _vm(vm) { @@ -207,7 +207,7 @@ bool VideoManager::updateMovies() { // Remove any videos that are over if (_videoStreams[i].endOfVideo()) { if (_videoStreams[i].loop) { - _videoStreams[i]->seekToTime(_videoStreams[i].start); + _videoStreams[i]->seek(_videoStreams[i].start); } else { // Check the video time one last time before deleting it _vm->doVideoTimer(i, true); @@ -239,7 +239,7 @@ bool VideoManager::updateMovies() { frame = convertedFrame; } else if (pixelFormat.bytesPerPixel == 1 && _videoStreams[i]->hasDirtyPalette()) { // Set the palette when running in 8bpp mode only - _videoStreams[i]->setSystemPalette(); + _vm->_system->getPaletteManager()->setPalette(_videoStreams[i]->getPalette(), 0, 256); } // Clip the width/height to make sure we stay on the screen (Myst does this a few times) @@ -394,6 +394,8 @@ VideoHandle VideoManager::createVideoHandle(uint16 id, uint16 x, uint16 y, bool entry.loop = loop; entry.enabled = true; + entry->start(); + // Search for any deleted videos so we can take a formerly used slot for (uint32 i = 0; i < _videoStreams.size(); i++) if (!_videoStreams[i].video) { @@ -430,6 +432,7 @@ VideoHandle VideoManager::createVideoHandle(const Common::String &filename, uint entry->loadStream(file); entry->setVolume(volume); + entry->start(); // Search for any deleted videos so we can take a formerly used slot for (uint32 i = 0; i < _videoStreams.size(); i++) @@ -492,7 +495,7 @@ uint32 VideoManager::getTime(VideoHandle handle) { uint32 VideoManager::getDuration(VideoHandle handle) { assert(handle != NULL_VID_HANDLE); - return _videoStreams[handle]->getDuration(); + return _videoStreams[handle]->getDuration().msecs(); } bool VideoManager::endOfVideo(VideoHandle handle) { @@ -511,14 +514,13 @@ bool VideoManager::isVideoPlaying() { void VideoManager::setVideoBounds(VideoHandle handle, Audio::Timestamp start, Audio::Timestamp end) { assert(handle != NULL_VID_HANDLE); _videoStreams[handle].start = start; - _videoStreams[handle].end = end; - _videoStreams[handle]->seekToTime(start); + _videoStreams[handle]->setEndTime(end); + _videoStreams[handle]->seek(start); } void VideoManager::drawVideoFrame(VideoHandle handle, Audio::Timestamp time) { assert(handle != NULL_VID_HANDLE); - _videoStreams[handle].end = Audio::Timestamp(0xffffffff, 1); - _videoStreams[handle]->seekToTime(time); + _videoStreams[handle]->seek(time); updateMovies(); delete _videoStreams[handle].video; _videoStreams[handle].clear(); @@ -526,7 +528,7 @@ void VideoManager::drawVideoFrame(VideoHandle handle, Audio::Timestamp time) { void VideoManager::seekToTime(VideoHandle handle, Audio::Timestamp time) { assert(handle != NULL_VID_HANDLE); - _videoStreams[handle]->seekToTime(time); + _videoStreams[handle]->seek(time); } void VideoManager::setVideoLooping(VideoHandle handle, bool loop) { diff --git a/engines/mohawk/video.h b/engines/mohawk/video.h index 98bcadfb53..9dddcde09b 100644 --- a/engines/mohawk/video.h +++ b/engines/mohawk/video.h @@ -45,19 +45,19 @@ struct MLSTRecord { struct VideoEntry { // Playback variables - Video::SeekableVideoDecoder *video; + Video::VideoDecoder *video; uint16 x; uint16 y; bool loop; bool enabled; - Audio::Timestamp start, end; + Audio::Timestamp start; // Identification Common::String filename; // External video files int id; // Internal Mohawk files // Helper functions - Video::SeekableVideoDecoder *operator->() const { assert(video); return video; } // TODO: Remove this eventually + Video::VideoDecoder *operator->() const { assert(video); return video; } // TODO: Remove this eventually void clear(); bool endOfVideo(); }; diff --git a/engines/saga/introproc_saga2.cpp b/engines/saga/introproc_saga2.cpp index b6470370af..260eca98e6 100644 --- a/engines/saga/introproc_saga2.cpp +++ b/engines/saga/introproc_saga2.cpp @@ -32,6 +32,7 @@ #include "common/keyboard.h" #include "common/system.h" #include "common/textconsole.h" +#include "graphics/palette.h" #include "graphics/surface.h" #include "video/smk_decoder.h" @@ -92,7 +93,7 @@ int Scene::FTA2EndProc(FTA2Endings whichEnding) { } void Scene::playMovie(const char *filename) { - Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder(_vm->_mixer); + Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder(); if (!smkDecoder->loadFile(filename)) return; @@ -101,6 +102,8 @@ void Scene::playMovie(const char *filename) { uint16 y = (g_system->getHeight() - smkDecoder->getHeight()) / 2; bool skipVideo = false; + smkDecoder->start(); + while (!_vm->shouldQuit() && !smkDecoder->endOfVideo() && !skipVideo) { if (smkDecoder->needsUpdate()) { const Graphics::Surface *frame = smkDecoder->decodeNextFrame(); @@ -108,7 +111,7 @@ void Scene::playMovie(const char *filename) { _vm->_system->copyRectToScreen(frame->pixels, frame->pitch, x, y, frame->w, frame->h); if (smkDecoder->hasDirtyPalette()) - smkDecoder->setSystemPalette(); + _vm->_system->getPaletteManager()->setPalette(smkDecoder->getPalette(), 0, 256); _vm->_system->updateScreen(); } diff --git a/engines/saga/shorten.cpp b/engines/saga/shorten.cpp index 5efc8d1f67..69c27b6a6b 100644 --- a/engines/saga/shorten.cpp +++ b/engines/saga/shorten.cpp @@ -519,9 +519,6 @@ byte *loadShortenFromStream(Common::ReadStream &stream, int &size, int &rate, by if (maxLPC > 0) free(lpc); - if (size > 0) - free(unpackedBuffer); - delete gReader; return unpackedBuffer; } diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp index 564bbbbd79..1889d53480 100644 --- a/engines/sci/console.cpp +++ b/engines/sci/console.cpp @@ -250,20 +250,18 @@ void Console::postEnter() { #endif if (_videoFile.hasSuffix(".seq")) { - SeqDecoder *seqDecoder = new SeqDecoder(); - seqDecoder->setFrameDelay(_videoFrameDelay); - videoDecoder = seqDecoder; + videoDecoder = new SEQDecoder(_videoFrameDelay); #ifdef ENABLE_SCI32 } else if (_videoFile.hasSuffix(".vmd")) { - videoDecoder = new Video::VMDDecoder(g_system->getMixer()); + videoDecoder = new Video::AdvancedVMDDecoder(); } else if (_videoFile.hasSuffix(".rbt")) { - videoDecoder = new RobotDecoder(g_system->getMixer(), _engine->getPlatform() == Common::kPlatformMacintosh); + videoDecoder = new RobotDecoder(_engine->getPlatform() == Common::kPlatformMacintosh); } else if (_videoFile.hasSuffix(".duk")) { duckMode = true; - videoDecoder = new Video::AviDecoder(g_system->getMixer()); + videoDecoder = new Video::AVIDecoder(); #endif } else if (_videoFile.hasSuffix(".avi")) { - videoDecoder = new Video::AviDecoder(g_system->getMixer()); + videoDecoder = new Video::AVIDecoder(); } else { warning("Unrecognized video type"); } diff --git a/engines/sci/engine/kgraphics32.cpp b/engines/sci/engine/kgraphics32.cpp index 685b3c0bd3..8b3afeef99 100644 --- a/engines/sci/engine/kgraphics32.cpp +++ b/engines/sci/engine/kgraphics32.cpp @@ -771,20 +771,23 @@ reg_t kRemapColors32(EngineState *s, int argc, reg_t *argv) { } break; case 3: { // remap to gray - // Example call: QFG4 room 490 (Baba Yaga's hut) - params are color 253, 75% and 0 + // Example call: QFG4 room 490 (Baba Yaga's hut) - params are color 253, 75% and 0. + // In this room, it's used for the cloud before Baba Yaga appears. int16 color = argv[1].toSint16(); int16 percent = argv[2].toSint16(); // 0 - 100 - uint16 unk3 = (argc >= 4) ? argv[3].toUint16() : 0; - warning("kRemapColors: RemapToGray color %d by %d percent (unk3 = %d)", color, percent, unk3); - // TODO + if (argc >= 4) + warning("RemapToGray called with 4 parameters, unknown parameter is %d", argv[3].toUint16()); + g_sci->_gfxPalette->setRemappingPercentGray(color, percent); } break; case 4: { // remap to percent gray - //int16 unk1 = argv[1].toSint16(); - //uint16 unk2 = argv[2].toUint16(); - //uint16 unk3 = argv[3].toUint16(); - //uint16 unk4 = (argc >= 5) ? argv[4].toUint16() : 0; - kStub(s, argc, argv); + // Example call: QFG4 rooms 530/535 (swamp) - params are 253, 100%, 200 + int16 color = argv[1].toSint16(); + int16 percent = argv[2].toSint16(); // 0 - 100 + // argv[3] is unknown (a number, e.g. 200) - start color, perhaps? + if (argc >= 5) + warning("RemapToGrayPercent called with 5 parameters, unknown parameter is %d", argv[4].toUint16()); + g_sci->_gfxPalette->setRemappingPercentGray(color, percent); } break; case 5: { // don't map to range diff --git a/engines/sci/engine/kmath.cpp b/engines/sci/engine/kmath.cpp index a643fbe37a..4b8fadbb84 100644 --- a/engines/sci/engine/kmath.cpp +++ b/engines/sci/engine/kmath.cpp @@ -84,27 +84,10 @@ reg_t kSqrt(EngineState *s, int argc, reg_t *argv) { * accurate. */ uint16 kGetAngleWorker(int16 x1, int16 y1, int16 x2, int16 y2) { - // TODO: This has been implemented based on behavior observed with a test - // program created with SCI Studio. However, the return values have subtle - // differences from the original, which uses custom implementation of atan(). - // The differences in the return values are the cause of bug #3540976 - // and perhaps bug #3037267 as well. - // The results of this function match the expected results of SCI0, but not - // SCI1 (hence the bug in Longbow). We need to find the point in history - // when this function was changed. - - // HACK: Return the expected value for Longbow, scene 150 (bug #3540976). - // This is a temporary solution, till the function returns the expected - // results. - if (g_sci->getGameId() == GID_LONGBOW && g_sci->getEngineState()->currentRoomNumber() == 150) { - if (x1 == 207 && y1 == 88 && x2 == 107 && y2 == 184) - return 226; - } - -#if 0 - // A simpler atan2-based implementation - return (int16)(360 - atan2((double)(x1 - x2), (double)(y1 - y2)) * 57.2958) % 360; -#endif + // SCI1 games (QFG2 and newer) use a simple atan implementation. SCI0 games + // use a somewhat less accurate calculation (below). + if (getSciVersion() >= SCI_VERSION_1_EGA_ONLY) + return (int16)(360 - atan2((double)(x1 - x2), (double)(y1 - y2)) * 57.2958) % 360; int16 xRel = x2 - x1; int16 yRel = y1 - y2; // y-axis is mirrored. diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp index cb2a763da9..6bf9aff2fe 100644 --- a/engines/sci/engine/kvideo.cpp +++ b/engines/sci/engine/kvideo.cpp @@ -50,6 +50,8 @@ void playVideo(Video::VideoDecoder *videoDecoder, VideoState videoState) { if (!videoDecoder) return; + videoDecoder->start(); + byte *scaleBuffer = 0; byte bytesPerPixel = videoDecoder->getPixelFormat().bytesPerPixel; uint16 width = videoDecoder->getWidth(); @@ -162,9 +164,8 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { } else { // DOS SEQ // SEQ's are called with no subops, just the string and delay - SeqDecoder *seqDecoder = new SeqDecoder(); - seqDecoder->setFrameDelay(argv[1].toUint16()); // Time between frames in ticks - videoDecoder = seqDecoder; + // Time is specified as ticks + videoDecoder = new SEQDecoder(argv[1].toUint16()); if (!videoDecoder->loadFile(filename)) { warning("Failed to open movie file %s", filename.c_str()); @@ -190,7 +191,7 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { switch (argv[0].toUint16()) { case 0: { Common::String filename = s->_segMan->getString(argv[1]); - videoDecoder = new Video::AviDecoder(g_system->getMixer()); + videoDecoder = new Video::AVIDecoder(); if (filename.equalsIgnoreCase("gk2a.avi")) { // HACK: Switch to 16bpp graphics for Indeo3. @@ -252,6 +253,7 @@ reg_t kRobot(EngineState *s, int argc, reg_t *argv) { int16 y = argv[5].toUint16(); warning("kRobot(init), id %d, obj %04x:%04x, flag %d, x=%d, y=%d", id, PRINT_REG(obj), flag, x, y); g_sci->_robotDecoder->load(id); + g_sci->_robotDecoder->start(); g_sci->_robotDecoder->setPos(x, y); } break; @@ -267,13 +269,13 @@ reg_t kRobot(EngineState *s, int argc, reg_t *argv) { warning("kRobot(%d)", subop); break; case 8: // sync - //if (false) { // debug: automatically skip all robot videos - if ((uint32)g_sci->_robotDecoder->getCurFrame() != g_sci->_robotDecoder->getFrameCount() - 1) { - writeSelector(s->_segMan, argv[1], SELECTOR(signal), NULL_REG); - } else { + //if (true) { // debug: automatically skip all robot videos + if (g_sci->_robotDecoder->endOfVideo()) { g_sci->_robotDecoder->close(); // Signal the engine scripts that the video is done writeSelector(s->_segMan, argv[1], SELECTOR(signal), SIGNAL_REG); + } else { + writeSelector(s->_segMan, argv[1], SELECTOR(signal), NULL_REG); } break; default: @@ -348,7 +350,7 @@ reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv) { break; } case 6: // Play - videoDecoder = new Video::VMDDecoder(g_system->getMixer()); + videoDecoder = new Video::AdvancedVMDDecoder(); if (s->_videoState.fileName.empty()) { // Happens in Lighthouse @@ -406,7 +408,7 @@ reg_t kPlayDuck(EngineState *s, int argc, reg_t *argv) { s->_videoState.reset(); s->_videoState.fileName = Common::String::format("%d.duk", argv[1].toUint16()); - videoDecoder = new Video::AviDecoder(g_system->getMixer()); + videoDecoder = new Video::AVIDecoder(); if (!videoDecoder->loadFile(s->_videoState.fileName)) { warning("Could not open Duck %s", s->_videoState.fileName.c_str()); diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp index a4c2355e8f..9fa0368784 100644 --- a/engines/sci/engine/workarounds.cpp +++ b/engines/sci/engine/workarounds.cpp @@ -399,7 +399,7 @@ const SciWorkaroundEntry kUnLoad_workarounds[] = { { GID_LSL6, 740, 740, 0, "showCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during ending, 4 additional parameters are passed by accident { GID_LSL6HIRES, 130, 130, 0, "recruitLarryScr", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during intro, a 3rd parameter is passed by accident { GID_SQ1, 43, 303, 0, "slotGuy", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when leaving ulence flats bar, parameter 1 is not passed - script error - { GID_QFG4, 770, 110, 0, "dreamer", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during the dream sequence, a 3rd parameter is passed by accident + { GID_QFG4, -1, 110, 0, "dreamer", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during the dream sequence, a 3rd parameter is passed by accident SCI_WORKAROUNDENTRY_TERMINATOR }; diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp index 0098728e5d..968014c032 100644 --- a/engines/sci/graphics/frameout.cpp +++ b/engines/sci/graphics/frameout.cpp @@ -28,6 +28,7 @@ #include "common/system.h" #include "common/textconsole.h" #include "engines/engine.h" +#include "graphics/palette.h" #include "graphics/surface.h" #include "sci/sci.h" @@ -488,7 +489,7 @@ void GfxFrameout::showVideo() { uint16 y = videoDecoder->getPos().y; if (videoDecoder->hasDirtyPalette()) - videoDecoder->setSystemPalette(); + g_system->getPaletteManager()->setPalette(videoDecoder->getPalette(), 0, 256); while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) { if (videoDecoder->needsUpdate()) { @@ -497,7 +498,7 @@ void GfxFrameout::showVideo() { g_system->copyRectToScreen(frame->pixels, frame->pitch, x, y, frame->w, frame->h); if (videoDecoder->hasDirtyPalette()) - videoDecoder->setSystemPalette(); + g_system->getPaletteManager()->setPalette(videoDecoder->getPalette(), 0, 256); g_system->updateScreen(); } @@ -728,7 +729,9 @@ void GfxFrameout::kernelFrameout() { g_sci->_gfxCompare->setNSRect(itemEntry->object, nsRect); } - // FIXME: When does this happen, and why? + // Don't attempt to draw sprites that are outside the visible + // screen area. An example is the random people walking in + // Jackson Square in GK1. if (itemEntry->celRect.bottom < 0 || itemEntry->celRect.top >= _screen->getDisplayHeight() || itemEntry->celRect.right < 0 || itemEntry->celRect.left >= _screen->getDisplayWidth()) continue; diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp index 68104b0ac8..53d69cdcca 100644 --- a/engines/sci/graphics/palette.cpp +++ b/engines/sci/graphics/palette.cpp @@ -375,6 +375,27 @@ void GfxPalette::setRemappingPercent(byte color, byte percent) { _remappingType[color] = kRemappingByPercent; } +void GfxPalette::setRemappingPercentGray(byte color, byte percent) { + _remapOn = true; + + // We need to defer the setup of the remapping table every time the screen + // palette is changed, so that kernelFindColor() can find the correct + // colors. Set it once here, in case the palette stays the same and update + // it on each palette change by copySysPaletteToScreen(). + _remappingPercentToSet = percent; + + // Note: This is not what the original does, but the results are the same visually + for (int i = 0; i < 256; i++) { + byte rComponent = _sysPalette.colors[i].r * _remappingPercentToSet * 0.30 / 100; + byte gComponent = _sysPalette.colors[i].g * _remappingPercentToSet * 0.59 / 100; + byte bComponent = _sysPalette.colors[i].b * _remappingPercentToSet * 0.11 / 100; + byte luminosity = rComponent + gComponent + bComponent; + _remappingByPercent[i] = kernelFindColor(luminosity, luminosity, luminosity); + } + + _remappingType[color] = kRemappingByPercent; +} + void GfxPalette::setRemappingRange(byte color, byte from, byte to, byte base) { _remapOn = true; diff --git a/engines/sci/graphics/palette.h b/engines/sci/graphics/palette.h index 9898315897..e974781d49 100644 --- a/engines/sci/graphics/palette.h +++ b/engines/sci/graphics/palette.h @@ -61,6 +61,7 @@ public: void resetRemapping(); void setRemappingPercent(byte color, byte percent); + void setRemappingPercentGray(byte color, byte percent); void setRemappingRange(byte color, byte from, byte to, byte base); bool isRemapped(byte color) const { return _remapOn && (_remappingType[color] != kRemappingNone); diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp index d43a9d06fc..42ae00b525 100644 --- a/engines/sci/sci.cpp +++ b/engines/sci/sci.cpp @@ -632,7 +632,7 @@ void SciEngine::initGraphics() { _gfxPaint = _gfxPaint32; _gfxText32 = new GfxText32(_gamestate->_segMan, _gfxCache, _gfxScreen); _gfxControls32 = new GfxControls32(_gamestate->_segMan, _gfxCache, _gfxScreen, _gfxText32); - _robotDecoder = new RobotDecoder(g_system->getMixer(), getPlatform() == Common::kPlatformMacintosh); + _robotDecoder = new RobotDecoder(getPlatform() == Common::kPlatformMacintosh); _gfxFrameout = new GfxFrameout(_gamestate->_segMan, _resMan, _gfxCoordAdjuster, _gfxCache, _gfxScreen, _gfxPalette, _gfxPaint32); } else { #endif diff --git a/engines/sci/sound/audio.cpp b/engines/sci/sound/audio.cpp index 123dd21894..528bb51393 100644 --- a/engines/sci/sound/audio.cpp +++ b/engines/sci/sound/audio.cpp @@ -67,7 +67,8 @@ int AudioPlayer::startAudio(uint16 module, uint32 number) { if (audioStream) { _wPlayFlag = false; - _mixer->playStream(Audio::Mixer::kSpeechSoundType, &_audioHandle, audioStream); + Audio::Mixer::SoundType soundType = (module == 65535) ? Audio::Mixer::kSFXSoundType : Audio::Mixer::kSpeechSoundType; + _mixer->playStream(soundType, &_audioHandle, audioStream); return sampleLen; } else { // Don't throw a warning in this case. getAudioStream() already has. Some games diff --git a/engines/sci/sound/soundcmd.cpp b/engines/sci/sound/soundcmd.cpp index cbb5cab4fe..5d32f40f18 100644 --- a/engines/sci/sound/soundcmd.cpp +++ b/engines/sci/sound/soundcmd.cpp @@ -96,7 +96,7 @@ void SoundCommandParser::initSoundResource(MusicEntry *newSound) { if (_useDigitalSFX || !newSound->soundRes) { int sampleLen; newSound->pStreamAud = _audio->getAudioStream(newSound->resourceId, 65535, &sampleLen); - newSound->soundType = Audio::Mixer::kSpeechSoundType; + newSound->soundType = Audio::Mixer::kSFXSoundType; } } @@ -367,29 +367,36 @@ reg_t SoundCommandParser::kDoSoundFade(int argc, reg_t *argv, reg_t acc) { case 4: // SCI01+ case 5: // SCI1+ (SCI1 late sound scheme), with fade and continue - musicSlot->fadeTo = CLIP<uint16>(argv[1].toUint16(), 0, MUSIC_VOLUME_MAX); - // Check if the song is already at the requested volume. If it is, don't - // perform any fading. Happens for example during the intro of Longbow. - if (musicSlot->fadeTo == musicSlot->volume) - return acc; - - // sometimes we get objects in that position, fix it up (ffs. workarounds) - if (!argv[1].getSegment()) - musicSlot->fadeStep = volume > musicSlot->fadeTo ? -argv[3].toUint16() : argv[3].toUint16(); - else - musicSlot->fadeStep = volume > musicSlot->fadeTo ? -5 : 5; - musicSlot->fadeTickerStep = argv[2].toUint16() * 16667 / _music->soundGetTempo(); - musicSlot->fadeTicker = 0; - if (argc == 5) { // TODO: We currently treat this argument as a boolean, but may // have to handle different non-zero values differently. (e.g., - // some KQ6 scripts pass 3 here) - musicSlot->stopAfterFading = (argv[4].toUint16() != 0); + // some KQ6 scripts pass 3 here). + // There is a script bug in KQ6, room 460 (the room with the flying + // books). An object is passed here, which should not be treated as + // a true flag. Fixes bugs #3555404 and #3291115. + musicSlot->stopAfterFading = (argv[4].isNumber() && argv[4].toUint16() != 0); } else { musicSlot->stopAfterFading = false; } + musicSlot->fadeTo = CLIP<uint16>(argv[1].toUint16(), 0, MUSIC_VOLUME_MAX); + // Check if the song is already at the requested volume. If it is, don't + // perform any fading. Happens for example during the intro of Longbow. + if (musicSlot->fadeTo != musicSlot->volume) { + // sometimes we get objects in that position, fix it up (ffs. workarounds) + if (!argv[1].getSegment()) + musicSlot->fadeStep = volume > musicSlot->fadeTo ? -argv[3].toUint16() : argv[3].toUint16(); + else + musicSlot->fadeStep = volume > musicSlot->fadeTo ? -5 : 5; + musicSlot->fadeTickerStep = argv[2].toUint16() * 16667 / _music->soundGetTempo(); + } else { + // Stop the music, if requested. Fixes bug #3555404. + if (musicSlot->stopAfterFading) + processStopSound(obj, false); + } + + musicSlot->fadeTicker = 0; + // WORKAROUND/HACK: In the labyrinth in KQ6, when falling in the pit and // lighting the lantern, the game scripts perform a fade in of the game // music, but set it to stop after fading. Remove that flag here. This is diff --git a/engines/sci/video/robot_decoder.cpp b/engines/sci/video/robot_decoder.cpp index ebcfac6054..608c77136f 100644 --- a/engines/sci/video/robot_decoder.cpp +++ b/engines/sci/video/robot_decoder.cpp @@ -22,11 +22,13 @@ #include "common/archive.h" #include "common/stream.h" +#include "common/substream.h" #include "common/system.h" #include "common/textconsole.h" #include "common/util.h" #include "graphics/surface.h" +#include "audio/audiostream.h" #include "audio/decoders/raw.h" #include "sci/resource.h" @@ -63,57 +65,26 @@ namespace Sci { // our graphics engine, it looks just like a part of the room. A RBT can move // around the screen and go behind other objects. (...) -#ifdef ENABLE_SCI32 - -enum robotPalTypes { +enum RobotPalTypes { kRobotPalVariable = 0, kRobotPalConstant = 1 }; -RobotDecoder::RobotDecoder(Audio::Mixer *mixer, bool isBigEndian) { - _surface = 0; - _width = 0; - _height = 0; +RobotDecoder::RobotDecoder(bool isBigEndian) { _fileStream = 0; - _audioStream = 0; - _dirtyPalette = false; _pos = Common::Point(0, 0); - _mixer = mixer; _isBigEndian = isBigEndian; + _frameTotalSize = 0; } RobotDecoder::~RobotDecoder() { close(); } -bool RobotDecoder::load(GuiResourceId id) { - // TODO: RAMA's robot 1003 cannot be played (shown at the menu screen) - - // its drawn at odd coordinates. SV can't play it either (along with some - // others), so it must be some new functionality added in RAMA's robot - // videos. Skip it for now. - if (g_sci->getGameId() == GID_RAMA && id == 1003) - return false; - - // TODO: The robot video in the Lighthouse demo gets stuck - if (g_sci->getGameId() == GID_LIGHTHOUSE && id == 16) - return false; - - Common::String fileName = Common::String::format("%d.rbt", id); - Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(fileName); - - if (!stream) { - warning("Unable to open robot file %s", fileName.c_str()); - return false; - } - - return loadStream(stream); -} - bool RobotDecoder::loadStream(Common::SeekableReadStream *stream) { close(); _fileStream = new Common::SeekableSubReadStreamEndian(stream, 0, stream->size(), _isBigEndian, DisposeAfterUse::YES); - _surface = new Graphics::Surface(); readHeaderChunk(); @@ -125,131 +96,60 @@ bool RobotDecoder::loadStream(Common::SeekableReadStream *stream) { if (_header.version < 4 || _header.version > 6) error("Unknown robot version: %d", _header.version); - if (_header.hasSound) { - _audioStream = Audio::makeQueuingAudioStream(11025, false); - _mixer->playStream(Audio::Mixer::kMusicSoundType, &_audioHandle, _audioStream, -1, getVolume(), getBalance()); - } + RobotVideoTrack *videoTrack = new RobotVideoTrack(_header.frameCount); + addTrack(videoTrack); - readPaletteChunk(_header.paletteDataSize); - readFrameSizesChunk(); - calculateVideoDimensions(); - _surface->create(_width, _height, Graphics::PixelFormat::createFormatCLUT8()); + if (_header.hasSound) + addTrack(new RobotAudioTrack()); + videoTrack->readPaletteChunk(_fileStream, _header.paletteDataSize); + readFrameSizesChunk(); + videoTrack->calculateVideoDimensions(_fileStream, _frameTotalSize); return true; } -void RobotDecoder::readHeaderChunk() { - // Header (60 bytes) - _fileStream->skip(6); - _header.version = _fileStream->readUint16(); - _header.audioChunkSize = _fileStream->readUint16(); - _header.audioSilenceSize = _fileStream->readUint16(); - _fileStream->skip(2); - _header.frameCount = _fileStream->readUint16(); - _header.paletteDataSize = _fileStream->readUint16(); - _header.unkChunkDataSize = _fileStream->readUint16(); - _fileStream->skip(5); - _header.hasSound = _fileStream->readByte(); - _fileStream->skip(34); - - // Some videos (e.g. robot 1305 in Phantasmagoria and - // robot 184 in Lighthouse) have an unknown chunk before - // the palette chunk (probably used for sound preloading). - // Skip it here. - if (_header.unkChunkDataSize) - _fileStream->skip(_header.unkChunkDataSize); -} - -void RobotDecoder::readPaletteChunk(uint16 chunkSize) { - byte *paletteData = new byte[chunkSize]; - _fileStream->read(paletteData, chunkSize); - - // SCI1.1 palette - byte palFormat = paletteData[32]; - uint16 palColorStart = paletteData[25]; - uint16 palColorCount = READ_SCI11ENDIAN_UINT16(paletteData + 29); +bool RobotDecoder::load(GuiResourceId id) { + // TODO: RAMA's robot 1003 cannot be played (shown at the menu screen) - + // its drawn at odd coordinates. SV can't play it either (along with some + // others), so it must be some new functionality added in RAMA's robot + // videos. Skip it for now. + if (g_sci->getGameId() == GID_RAMA && id == 1003) + return false; + + // TODO: The robot video in the Lighthouse demo gets stuck + if (g_sci->getGameId() == GID_LIGHTHOUSE && id == 16) + return false; - int palOffset = 37; - memset(_palette, 0, 256 * 3); + Common::String fileName = Common::String::format("%d.rbt", id); + Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(fileName); - for (uint16 colorNo = palColorStart; colorNo < palColorStart + palColorCount; colorNo++) { - if (palFormat == kRobotPalVariable) - palOffset++; - _palette[colorNo * 3 + 0] = paletteData[palOffset++]; - _palette[colorNo * 3 + 1] = paletteData[palOffset++]; - _palette[colorNo * 3 + 2] = paletteData[palOffset++]; + if (!stream) { + warning("Unable to open robot file %s", fileName.c_str()); + return false; } - _dirtyPalette = true; - delete[] paletteData; + return loadStream(stream); } +void RobotDecoder::close() { + VideoDecoder::close(); -void RobotDecoder::readFrameSizesChunk() { - // The robot video file contains 2 tables, with one entry for each frame: - // - A table containing the size of the image in each video frame - // - A table containing the total size of each video frame. - // In v5 robots, the tables contain 16-bit integers, whereas in v6 robots, - // they contain 32-bit integers. - - _frameTotalSize = new uint32[_header.frameCount]; - - // TODO: The table reading code can probably be removed once the - // audio chunk size is figured out (check the TODO inside processNextFrame()) -#if 0 - // We don't need any of the two tables to play the video, so we ignore - // both of them. - uint16 wordSize = _header.version == 6 ? 4 : 2; - _fileStream->skip(_header.frameCount * wordSize * 2); -#else - switch (_header.version) { - case 4: - case 5: // sizes are 16-bit integers - // Skip table with frame image sizes, as we don't need it - _fileStream->skip(_header.frameCount * 2); - for (int i = 0; i < _header.frameCount; ++i) - _frameTotalSize[i] = _fileStream->readUint16(); - break; - case 6: // sizes are 32-bit integers - // Skip table with frame image sizes, as we don't need it - _fileStream->skip(_header.frameCount * 4); - for (int i = 0; i < _header.frameCount; ++i) - _frameTotalSize[i] = _fileStream->readUint32(); - break; - default: - error("Can't yet handle index table for robot version %d", _header.version); - } -#endif - - // 2 more unknown tables - _fileStream->skip(1024 + 512); + delete _fileStream; + _fileStream = 0; - // Pad to nearest 2 kilobytes - uint32 curPos = _fileStream->pos(); - if (curPos & 0x7ff) - _fileStream->seek((curPos & ~0x7ff) + 2048); + delete[] _frameTotalSize; + _frameTotalSize = 0; } -void RobotDecoder::calculateVideoDimensions() { - // This is an O(n) operation, as each frame has a different size. - // We need to know the actual frame size to have a constant video size. - uint32 pos = _fileStream->pos(); - - for (uint32 curFrame = 0; curFrame < _header.frameCount; curFrame++) { - _fileStream->skip(4); - uint16 frameWidth = _fileStream->readUint16(); - uint16 frameHeight = _fileStream->readUint16(); - if (frameWidth > _width) - _width = frameWidth; - if (frameHeight > _height) - _height = frameHeight; - _fileStream->skip(_frameTotalSize[curFrame] - 8); - } +void RobotDecoder::readNextPacket() { + // Get our track + RobotVideoTrack *videoTrack = (RobotVideoTrack *)getTrack(0); + videoTrack->increaseCurFrame(); + Graphics::Surface *surface = videoTrack->getSurface(); - _fileStream->seek(pos); -} + if (videoTrack->endOfTrack()) + return; -const Graphics::Surface *RobotDecoder::decodeNextFrame() { // Read frame image header (24 bytes) _fileStream->skip(3); byte frameScale = _fileStream->readByte(); @@ -258,23 +158,28 @@ const Graphics::Surface *RobotDecoder::decodeNextFrame() { _fileStream->skip(4); // unknown, almost always 0 uint16 frameX = _fileStream->readUint16(); uint16 frameY = _fileStream->readUint16(); + // TODO: In v4 robot files, frameX and frameY have a different meaning. // Set them both to 0 for v4 for now, so that robots in PQ:SWAT show up // correctly. if (_header.version == 4) frameX = frameY = 0; + uint16 compressedSize = _fileStream->readUint16(); uint16 frameFragments = _fileStream->readUint16(); _fileStream->skip(4); // unknown uint32 decompressedSize = frameWidth * frameHeight * frameScale / 100; + // FIXME: A frame's height + position can go off limits... why? With the // following, we cut the contents to fit the frame - uint16 scaledHeight = CLIP<uint16>(decompressedSize / frameWidth, 0, _height - frameY); + uint16 scaledHeight = CLIP<uint16>(decompressedSize / frameWidth, 0, surface->h - frameY); + // FIXME: Same goes for the frame's width + position. In this case, we // modify the position to fit the contents on screen. - if (frameWidth + frameX > _width) - frameX = _width - frameWidth; - assert (frameWidth + frameX <= _width && scaledHeight + frameY <= _height); + if (frameWidth + frameX > surface->w) + frameX = surface->w - frameWidth; + + assert(frameWidth + frameX <= surface->w && scaledHeight + frameY <= surface->h); DecompressorLZS lzs; byte *decompressedFrame = new byte[decompressedSize]; @@ -305,24 +210,23 @@ const Graphics::Surface *RobotDecoder::decodeNextFrame() { // Copy over the decompressed frame byte *inFrame = decompressedFrame; - byte *outFrame = (byte *)_surface->pixels; + byte *outFrame = (byte *)surface->pixels; // Black out the surface - memset(outFrame, 0, _width * _height); + memset(outFrame, 0, surface->w * surface->h); // Move to the correct y coordinate - outFrame += _width * frameY; + outFrame += surface->w * frameY; for (uint16 y = 0; y < scaledHeight; y++) { memcpy(outFrame + frameX, inFrame, frameWidth); inFrame += frameWidth; - outFrame += _width; + outFrame += surface->w; } delete[] decompressedFrame; - // +1 because we start with frame number -1 - uint32 audioChunkSize = _frameTotalSize[_curFrame + 1] - (24 + compressedSize); + uint32 audioChunkSize = _frameTotalSize[videoTrack->getCurFrame()] - (24 + compressedSize); // TODO: The audio chunk size below is usually correct, but there are some // exceptions (e.g. robot 4902 in Phantasmagoria, towards its end) @@ -337,51 +241,166 @@ const Graphics::Surface *RobotDecoder::decodeNextFrame() { // Queue the next audio frame // FIXME: For some reason, there are audio hiccups/gaps if (_header.hasSound) { - _fileStream->skip(8); // header - _audioStream->queueBuffer(g_sci->_audio->getDecodedRobotAudioFrame(_fileStream, audioChunkSize - 8), - (audioChunkSize - 8) * 2, DisposeAfterUse::NO, - Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN); + RobotAudioTrack *audioTrack = (RobotAudioTrack *)getTrack(1); + _fileStream->skip(8); // header + audioChunkSize -= 8; + audioTrack->queueBuffer(g_sci->_audio->getDecodedRobotAudioFrame(_fileStream, audioChunkSize), audioChunkSize * 2); } else { _fileStream->skip(audioChunkSize); - } - - if (_curFrame == -1) - _startTime = g_system->getMillis(); + } +} - _curFrame++; +void RobotDecoder::readHeaderChunk() { + // Header (60 bytes) + _fileStream->skip(6); + _header.version = _fileStream->readUint16(); + _header.audioChunkSize = _fileStream->readUint16(); + _header.audioSilenceSize = _fileStream->readUint16(); + _fileStream->skip(2); + _header.frameCount = _fileStream->readUint16(); + _header.paletteDataSize = _fileStream->readUint16(); + _header.unkChunkDataSize = _fileStream->readUint16(); + _fileStream->skip(5); + _header.hasSound = _fileStream->readByte(); + _fileStream->skip(34); - return _surface; + // Some videos (e.g. robot 1305 in Phantasmagoria and + // robot 184 in Lighthouse) have an unknown chunk before + // the palette chunk (probably used for sound preloading). + // Skip it here. + if (_header.unkChunkDataSize) + _fileStream->skip(_header.unkChunkDataSize); } -void RobotDecoder::close() { - if (!_fileStream) - return; +void RobotDecoder::readFrameSizesChunk() { + // The robot video file contains 2 tables, with one entry for each frame: + // - A table containing the size of the image in each video frame + // - A table containing the total size of each video frame. + // In v5 robots, the tables contain 16-bit integers, whereas in v6 robots, + // they contain 32-bit integers. - delete _fileStream; - _fileStream = 0; + _frameTotalSize = new uint32[_header.frameCount]; + // TODO: The table reading code can probably be removed once the + // audio chunk size is figured out (check the TODO inside processNextFrame()) +#if 0 + // We don't need any of the two tables to play the video, so we ignore + // both of them. + uint16 wordSize = _header.version == 6 ? 4 : 2; + _fileStream->skip(_header.frameCount * wordSize * 2); +#else + switch (_header.version) { + case 4: + case 5: // sizes are 16-bit integers + // Skip table with frame image sizes, as we don't need it + _fileStream->skip(_header.frameCount * 2); + for (int i = 0; i < _header.frameCount; ++i) + _frameTotalSize[i] = _fileStream->readUint16(); + break; + case 6: // sizes are 32-bit integers + // Skip table with frame image sizes, as we don't need it + _fileStream->skip(_header.frameCount * 4); + for (int i = 0; i < _header.frameCount; ++i) + _frameTotalSize[i] = _fileStream->readUint32(); + break; + default: + error("Can't yet handle index table for robot version %d", _header.version); + } +#endif + + // 2 more unknown tables + _fileStream->skip(1024 + 512); + + // Pad to nearest 2 kilobytes + uint32 curPos = _fileStream->pos(); + if (curPos & 0x7ff) + _fileStream->seek((curPos & ~0x7ff) + 2048); +} + +RobotDecoder::RobotVideoTrack::RobotVideoTrack(int frameCount) : _frameCount(frameCount) { + _surface = new Graphics::Surface(); + _curFrame = -1; + _dirtyPalette = false; +} + +RobotDecoder::RobotVideoTrack::~RobotVideoTrack() { _surface->free(); delete _surface; - _surface = 0; +} - if (_header.hasSound) { - _mixer->stopHandle(_audioHandle); - //delete _audioStream; _audioStream = 0; +uint16 RobotDecoder::RobotVideoTrack::getWidth() const { + return _surface->w; +} + +uint16 RobotDecoder::RobotVideoTrack::getHeight() const { + return _surface->h; +} + +Graphics::PixelFormat RobotDecoder::RobotVideoTrack::getPixelFormat() const { + return _surface->format; +} + +void RobotDecoder::RobotVideoTrack::readPaletteChunk(Common::SeekableSubReadStreamEndian *stream, uint16 chunkSize) { + byte *paletteData = new byte[chunkSize]; + stream->read(paletteData, chunkSize); + + // SCI1.1 palette + byte palFormat = paletteData[32]; + uint16 palColorStart = paletteData[25]; + uint16 palColorCount = READ_SCI11ENDIAN_UINT16(paletteData + 29); + + int palOffset = 37; + memset(_palette, 0, 256 * 3); + + for (uint16 colorNo = palColorStart; colorNo < palColorStart + palColorCount; colorNo++) { + if (palFormat == kRobotPalVariable) + palOffset++; + _palette[colorNo * 3 + 0] = paletteData[palOffset++]; + _palette[colorNo * 3 + 1] = paletteData[palOffset++]; + _palette[colorNo * 3 + 2] = paletteData[palOffset++]; } - reset(); + _dirtyPalette = true; + delete[] paletteData; } -void RobotDecoder::updateVolume() { - if (g_system->getMixer()->isSoundHandleActive(_audioHandle)) - g_system->getMixer()->setChannelVolume(_audioHandle, getVolume()); +void RobotDecoder::RobotVideoTrack::calculateVideoDimensions(Common::SeekableSubReadStreamEndian *stream, uint32 *frameSizes) { + // This is an O(n) operation, as each frame has a different size. + // We need to know the actual frame size to have a constant video size. + uint32 pos = stream->pos(); + + uint16 width = 0, height = 0; + + for (int curFrame = 0; curFrame < _frameCount; curFrame++) { + stream->skip(4); + uint16 frameWidth = stream->readUint16(); + uint16 frameHeight = stream->readUint16(); + if (frameWidth > width) + width = frameWidth; + if (frameHeight > height) + height = frameHeight; + stream->skip(frameSizes[curFrame] - 8); + } + + stream->seek(pos); + + _surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8()); } -void RobotDecoder::updateBalance() { - if (g_system->getMixer()->isSoundHandleActive(_audioHandle)) - g_system->getMixer()->setChannelBalance(_audioHandle, getBalance()); +RobotDecoder::RobotAudioTrack::RobotAudioTrack() { + _audioStream = Audio::makeQueuingAudioStream(11025, false); } -#endif +RobotDecoder::RobotAudioTrack::~RobotAudioTrack() { + delete _audioStream; +} + +void RobotDecoder::RobotAudioTrack::queueBuffer(byte *buffer, int size) { + _audioStream->queueBuffer(buffer, size, DisposeAfterUse::YES, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN); +} + +Audio::AudioStream *RobotDecoder::RobotAudioTrack::getAudioStream() const { + return _audioStream; +} } // End of namespace Sci diff --git a/engines/sci/video/robot_decoder.h b/engines/sci/video/robot_decoder.h index e9cefe7d91..ebc3262939 100644 --- a/engines/sci/video/robot_decoder.h +++ b/engines/sci/video/robot_decoder.h @@ -25,84 +25,103 @@ #include "common/rational.h" #include "common/rect.h" -#include "common/stream.h" -#include "common/substream.h" -#include "audio/audiostream.h" -#include "audio/mixer.h" -#include "graphics/pixelformat.h" #include "video/video_decoder.h" -namespace Sci { +namespace Audio { +class QueuingAudioStream; +} -#ifdef ENABLE_SCI32 - -struct RobotHeader { - // 6 bytes, identifier bytes - uint16 version; - uint16 audioChunkSize; - uint16 audioSilenceSize; - // 2 bytes, unknown - uint16 frameCount; - uint16 paletteDataSize; - uint16 unkChunkDataSize; - // 5 bytes, unknown - byte hasSound; - // 34 bytes, unknown -}; +namespace Common { +class SeekableSubReadStreamEndian; +} + +namespace Sci { -class RobotDecoder : public Video::FixedRateVideoDecoder { +class RobotDecoder : public Video::VideoDecoder { public: - RobotDecoder(Audio::Mixer *mixer, bool isBigEndian); + RobotDecoder(bool isBigEndian); virtual ~RobotDecoder(); bool loadStream(Common::SeekableReadStream *stream); bool load(GuiResourceId id); void close(); - - bool isVideoLoaded() const { return _fileStream != 0; } - uint16 getWidth() const { return _width; } - uint16 getHeight() const { return _height; } - uint32 getFrameCount() const { return _header.frameCount; } - const Graphics::Surface *decodeNextFrame(); - Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); } - const byte *getPalette() { _dirtyPalette = false; return _palette; } - bool hasDirtyPalette() const { return _dirtyPalette; } + void setPos(uint16 x, uint16 y) { _pos = Common::Point(x, y); } Common::Point getPos() const { return _pos; } protected: - // VideoDecoder API - void updateVolume(); - void updateBalance(); - - // FixedRateVideoDecoder API - Common::Rational getFrameRate() const { return Common::Rational(60, 10); } - + void readNextPacket(); + private: + class RobotVideoTrack : public FixedRateVideoTrack { + public: + RobotVideoTrack(int frameCount); + ~RobotVideoTrack(); + + uint16 getWidth() const; + uint16 getHeight() const; + Graphics::PixelFormat getPixelFormat() const; + int getCurFrame() const { return _curFrame; } + int getFrameCount() const { return _frameCount; } + const Graphics::Surface *decodeNextFrame() { return _surface; } + const byte *getPalette() const { _dirtyPalette = false; return _palette; } + bool hasDirtyPalette() const { return _dirtyPalette; } + + void readPaletteChunk(Common::SeekableSubReadStreamEndian *stream, uint16 chunkSize); + void calculateVideoDimensions(Common::SeekableSubReadStreamEndian *stream, uint32 *frameSizes); + Graphics::Surface *getSurface() { return _surface; } + void increaseCurFrame() { _curFrame++; } + + protected: + Common::Rational getFrameRate() const { return Common::Rational(60, 10); } + + private: + int _frameCount; + int _curFrame; + byte _palette[256 * 3]; + mutable bool _dirtyPalette; + Graphics::Surface *_surface; + }; + + class RobotAudioTrack : public AudioTrack { + public: + RobotAudioTrack(); + ~RobotAudioTrack(); + + Audio::Mixer::SoundType getSoundType() const { return Audio::Mixer::kMusicSoundType; } + + void queueBuffer(byte *buffer, int size); + + protected: + Audio::AudioStream *getAudioStream() const; + + private: + Audio::QueuingAudioStream *_audioStream; + }; + + struct RobotHeader { + // 6 bytes, identifier bytes + uint16 version; + uint16 audioChunkSize; + uint16 audioSilenceSize; + // 2 bytes, unknown + uint16 frameCount; + uint16 paletteDataSize; + uint16 unkChunkDataSize; + // 5 bytes, unknown + byte hasSound; + // 34 bytes, unknown + } _header; + void readHeaderChunk(); - void readPaletteChunk(uint16 chunkSize); void readFrameSizesChunk(); - void calculateVideoDimensions(); - - void freeData(); - RobotHeader _header; Common::Point _pos; bool _isBigEndian; + uint32 *_frameTotalSize; Common::SeekableSubReadStreamEndian *_fileStream; - - uint16 _width; - uint16 _height; - uint32 *_frameTotalSize; - byte _palette[256 * 3]; - bool _dirtyPalette; - Graphics::Surface *_surface; - Audio::QueuingAudioStream *_audioStream; - Audio::SoundHandle _audioHandle; - Audio::Mixer *_mixer; }; -#endif } // End of namespace Sci diff --git a/engines/sci/video/seq_decoder.cpp b/engines/sci/video/seq_decoder.cpp index abd64911a7..a7b6346eca 100644 --- a/engines/sci/video/seq_decoder.cpp +++ b/engines/sci/video/seq_decoder.cpp @@ -41,33 +41,44 @@ enum seqFrameTypes { kSeqFrameDiff = 1 }; -SeqDecoder::SeqDecoder() { - _fileStream = 0; - _surface = 0; - _dirtyPalette = false; +SEQDecoder::SEQDecoder(uint frameDelay) : _frameDelay(frameDelay) { } -SeqDecoder::~SeqDecoder() { +SEQDecoder::~SEQDecoder() { close(); } -bool SeqDecoder::loadStream(Common::SeekableReadStream *stream) { +bool SEQDecoder::loadStream(Common::SeekableReadStream *stream) { close(); + addTrack(new SEQVideoTrack(stream, _frameDelay)); + + return true; +} + +SEQDecoder::SEQVideoTrack::SEQVideoTrack(Common::SeekableReadStream *stream, uint frameDelay) { + assert(stream); + assert(frameDelay != 0); _fileStream = stream; + _frameDelay = frameDelay; + _curFrame = -1; + _surface = new Graphics::Surface(); _surface->create(SEQ_SCREEN_WIDTH, SEQ_SCREEN_HEIGHT, Graphics::PixelFormat::createFormatCLUT8()); _frameCount = _fileStream->readUint16LE(); - // Set palette - int paletteChunkSize = _fileStream->readUint32LE(); - readPaletteChunk(paletteChunkSize); + // Set initial palette + readPaletteChunk(_fileStream->readUint32LE()); +} - return true; +SEQDecoder::SEQVideoTrack::~SEQVideoTrack() { + delete _fileStream; + _surface->free(); + delete _surface; } -void SeqDecoder::readPaletteChunk(uint16 chunkSize) { +void SEQDecoder::SEQVideoTrack::readPaletteChunk(uint16 chunkSize) { byte *paletteData = new byte[chunkSize]; _fileStream->read(paletteData, chunkSize); @@ -91,23 +102,7 @@ void SeqDecoder::readPaletteChunk(uint16 chunkSize) { delete[] paletteData; } -void SeqDecoder::close() { - if (!_fileStream) - return; - - _frameDelay = 0; - - delete _fileStream; - _fileStream = 0; - - _surface->free(); - delete _surface; - _surface = 0; - - reset(); -} - -const Graphics::Surface *SeqDecoder::decodeNextFrame() { +const Graphics::Surface *SEQDecoder::SEQVideoTrack::decodeNextFrame() { int16 frameWidth = _fileStream->readUint16LE(); int16 frameHeight = _fileStream->readUint16LE(); int16 frameLeft = _fileStream->readUint16LE(); @@ -142,9 +137,6 @@ const Graphics::Surface *SeqDecoder::decodeNextFrame() { delete[] buf; } - if (_curFrame == -1) - _startTime = g_system->getMillis(); - _curFrame++; return _surface; } @@ -159,7 +151,7 @@ const Graphics::Surface *SeqDecoder::decodeNextFrame() { } \ memcpy(dest + writeRow * SEQ_SCREEN_WIDTH + writeCol, litData + litPos, n); -bool SeqDecoder::decodeFrame(byte *rleData, int rleSize, byte *litData, int litSize, byte *dest, int left, int width, int height, int colorKey) { +bool SEQDecoder::SEQVideoTrack::decodeFrame(byte *rleData, int rleSize, byte *litData, int litSize, byte *dest, int left, int width, int height, int colorKey) { int writeRow = 0; int writeCol = left; int litPos = 0; @@ -237,4 +229,9 @@ bool SeqDecoder::decodeFrame(byte *rleData, int rleSize, byte *litData, int litS return true; } +const byte *SEQDecoder::SEQVideoTrack::getPalette() const { + _dirtyPalette = false; + return _palette; +} + } // End of namespace Sci diff --git a/engines/sci/video/seq_decoder.h b/engines/sci/video/seq_decoder.h index 800a3c9024..890f349feb 100644 --- a/engines/sci/video/seq_decoder.h +++ b/engines/sci/video/seq_decoder.h @@ -40,44 +40,49 @@ namespace Sci { /** * Implementation of the Sierra SEQ decoder, used in KQ6 DOS floppy/CD and GK1 DOS */ -class SeqDecoder : public Video::FixedRateVideoDecoder { +class SEQDecoder : public Video::VideoDecoder { public: - SeqDecoder(); - virtual ~SeqDecoder(); + SEQDecoder(uint frameDelay); + virtual ~SEQDecoder(); bool loadStream(Common::SeekableReadStream *stream); - void close(); - - void setFrameDelay(int frameDelay) { _frameDelay = frameDelay; } - - bool isVideoLoaded() const { return _fileStream != 0; } - uint16 getWidth() const { return SEQ_SCREEN_WIDTH; } - uint16 getHeight() const { return SEQ_SCREEN_HEIGHT; } - uint32 getFrameCount() const { return _frameCount; } - const Graphics::Surface *decodeNextFrame(); - Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); } - const byte *getPalette() { _dirtyPalette = false; return _palette; } - bool hasDirtyPalette() const { return _dirtyPalette; } - -protected: - Common::Rational getFrameRate() const { assert(_frameDelay); return Common::Rational(60, _frameDelay); } private: - enum { - SEQ_SCREEN_WIDTH = 320, - SEQ_SCREEN_HEIGHT = 200 + class SEQVideoTrack : public FixedRateVideoTrack { + public: + SEQVideoTrack(Common::SeekableReadStream *stream, uint frameDelay); + ~SEQVideoTrack(); + + uint16 getWidth() const { return SEQ_SCREEN_WIDTH; } + uint16 getHeight() const { return SEQ_SCREEN_HEIGHT; } + Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); } + int getCurFrame() const { return _curFrame; } + int getFrameCount() const { return _frameCount; } + const Graphics::Surface *decodeNextFrame(); + const byte *getPalette() const; + bool hasDirtyPalette() const { return _dirtyPalette; } + + protected: + Common::Rational getFrameRate() const { return Common::Rational(60, _frameDelay); } + + private: + enum { + SEQ_SCREEN_WIDTH = 320, + SEQ_SCREEN_HEIGHT = 200 + }; + + void readPaletteChunk(uint16 chunkSize); + bool decodeFrame(byte *rleData, int rleSize, byte *litData, int litSize, byte *dest, int left, int width, int height, int colorKey); + + Common::SeekableReadStream *_fileStream; + int _curFrame, _frameCount; + byte _palette[256 * 3]; + mutable bool _dirtyPalette; + Graphics::Surface *_surface; + uint _frameDelay; }; - void readPaletteChunk(uint16 chunkSize); - bool decodeFrame(byte *rleData, int rleSize, byte *litData, int litSize, byte *dest, int left, int width, int height, int colorKey); - - uint16 _width, _height; - uint16 _frameDelay; - Common::SeekableReadStream *_fileStream; - byte _palette[256 * 3]; - bool _dirtyPalette; - uint32 _frameCount; - Graphics::Surface *_surface; + uint _frameDelay; }; } // End of namespace Sci diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp index 87305921c9..5404c7f8b1 100644 --- a/engines/scumm/detection.cpp +++ b/engines/scumm/detection.cpp @@ -20,9 +20,6 @@ * */ -// FIXME: Avoid using printf -#define FORBIDDEN_SYMBOL_EXCEPTION_printf - #include "base/plugins.h" #include "common/archive.h" @@ -1066,15 +1063,19 @@ Common::Error ScummMetaEngine::createInstance(OSystem *syst, Engine **engine) co // unknown MD5, or with a medium debug level in case of a known MD5 (for // debugging purposes). if (!findInMD5Table(res.md5.c_str())) { - printf("Your game version appears to be unknown. If this is *NOT* a fan-modified\n"); - printf("version (in particular, not a fan-made translation), please, report the\n"); - printf("following data to the ScummVM team along with name of the game you tried\n"); - printf("to add and its version/language/etc.:\n"); + Common::String md5Warning; + + md5Warning = "Your game version appears to be unknown. If this is *NOT* a fan-modified\n"; + md5Warning += "version (in particular, not a fan-made translation), please, report the\n"; + md5Warning += "following data to the ScummVM team along with name of the game you tried\n"; + md5Warning += "to add and its version/language/etc.:\n"; - printf(" SCUMM gameid '%s', file '%s', MD5 '%s'\n\n", + md5Warning += Common::String::format(" SCUMM gameid '%s', file '%s', MD5 '%s'\n\n", res.game.gameid, generateFilenameForDetection(res.fp.pattern, res.fp.genMethod).c_str(), res.md5.c_str()); + + g_system->logMessage(LogMessageType::kWarning, md5Warning.c_str()); } else { debug(1, "Using MD5 '%s'", res.md5.c_str()); } diff --git a/engines/scumm/he/animation_he.cpp b/engines/scumm/he/animation_he.cpp index 40e99c26a8..be17a3b305 100644 --- a/engines/scumm/he/animation_he.cpp +++ b/engines/scumm/he/animation_he.cpp @@ -40,7 +40,7 @@ MoviePlayer::MoviePlayer(ScummEngine_v90he *vm, Audio::Mixer *mixer) : _vm(vm) { _video = new Video::BinkDecoder(); else #endif - _video = new Video::SmackerDecoder(mixer); + _video = new Video::SmackerDecoder(); _flags = 0; _wizResNum = 0; @@ -61,11 +61,16 @@ int MoviePlayer::load(const char *filename, int flags, int image) { if (_video->isVideoLoaded()) _video->close(); + // Ensure that Bink will use our PixelFormat + _video->setDefaultHighColorFormat(g_system->getScreenFormat()); + if (!_video->loadFile(filename)) { warning("Failed to load video file %s", filename); return -1; } + _video->start(); + debug(1, "Playing video %s", filename); if (flags & 2) diff --git a/engines/sword1/animation.cpp b/engines/sword1/animation.cpp index ddafd964eb..f7add4eed2 100644 --- a/engines/sword1/animation.cpp +++ b/engines/sword1/animation.cpp @@ -37,6 +37,7 @@ #include "gui/message.h" +#include "video/dxa_decoder.h" #include "video/psx_decoder.h" #include "video/smk_decoder.h" @@ -96,9 +97,8 @@ static const char *const sequenceListPSX[20] = { // Basic movie player /////////////////////////////////////////////////////////////////////////////// -MoviePlayer::MoviePlayer(SwordEngine *vm, Text *textMan, ResMan *resMan, Audio::Mixer *snd, OSystem *system, Audio::SoundHandle *bgSoundHandle, Video::VideoDecoder *decoder, DecoderType decoderType) - : _vm(vm), _textMan(textMan), _resMan(resMan), _snd(snd), _bgSoundHandle(bgSoundHandle), _system(system) { - _bgSoundStream = NULL; +MoviePlayer::MoviePlayer(SwordEngine *vm, Text *textMan, ResMan *resMan, OSystem *system, Video::VideoDecoder *decoder, DecoderType decoderType) + : _vm(vm), _textMan(textMan), _resMan(resMan), _system(system) { _decoderType = decoderType; _decoder = decoder; @@ -107,7 +107,6 @@ MoviePlayer::MoviePlayer(SwordEngine *vm, Text *textMan, ResMan *resMan, Audio:: } MoviePlayer::~MoviePlayer() { - delete _bgSoundHandle; delete _decoder; } @@ -116,16 +115,12 @@ MoviePlayer::~MoviePlayer() { * @param id the id of the file */ bool MoviePlayer::load(uint32 id) { - Common::File f; Common::String filename; - if (_decoderType == kVideoDecoderDXA) - _bgSoundStream = Audio::SeekableAudioStream::openStreamFile(sequenceList[id]); - else - _bgSoundStream = NULL; - if (SwordEngine::_systemVars.showText) { + Common::File f; filename = Common::String::format("%s.txt", sequenceList[id]); + if (f.open(filename)) { Common::String line; int lineNo = 0; @@ -169,7 +164,6 @@ bool MoviePlayer::load(uint32 id) { _movieTexts.push_back(MovieText(startFrame, endFrame, ptr, color)); lastEnd = endFrame; } - f.close(); } } @@ -189,6 +183,7 @@ bool MoviePlayer::load(uint32 id) { // Need to load here in case it fails in which case we'd need // to go back to paletted mode if (_decoder->loadFile(filename)) { + _decoder->start(); return true; } else { initGraphics(g_system->getWidth(), g_system->getHeight(), true); @@ -197,30 +192,27 @@ bool MoviePlayer::load(uint32 id) { break; } - return _decoder->loadFile(filename.c_str()); -} + if (!_decoder->loadFile(filename)) + return false; -void MoviePlayer::play() { - if (_bgSoundStream) - _snd->playStream(Audio::Mixer::kSFXSoundType, _bgSoundHandle, _bgSoundStream); + // For DXA, also add the external sound file + if (_decoderType == kVideoDecoderDXA) + _decoder->addStreamFileTrack(sequenceList[id]); - bool terminated = false; + _decoder->start(); + return true; +} +void MoviePlayer::play() { _textX = 0; _textY = 0; - terminated = !playVideo(); - - if (terminated) - _snd->stopHandle(*_bgSoundHandle); + playVideo(); _textMan->releaseText(2, false); _movieTexts.clear(); - while (_snd->isSoundHandleActive(*_bgSoundHandle)) - _system->delayMillis(100); - // It's tempting to call _screen->fullRefresh() here to restore the old // palette. However, that causes glitches with DXA movies, where the // previous location would be momentarily drawn, before switching to @@ -320,7 +312,7 @@ bool MoviePlayer::playVideo() { } if (_decoder->hasDirtyPalette()) { - _decoder->setSystemPalette(); + _vm->_system->getPaletteManager()->setPalette(_decoder->getPalette(), 0, 256); if (!_movieTexts.empty()) { // Look for the best color indexes to use to display the subtitles @@ -506,24 +498,12 @@ void MoviePlayer::drawFramePSX(const Graphics::Surface *frame) { scaledFrame.free(); } -DXADecoderWithSound::DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle) - : _mixer(mixer), _bgSoundHandle(bgSoundHandle) { -} - -uint32 DXADecoderWithSound::getTime() const { - if (_mixer->isSoundHandleActive(*_bgSoundHandle)) - return _mixer->getSoundElapsedTime(*_bgSoundHandle); - - return DXADecoder::getTime(); -} - /////////////////////////////////////////////////////////////////////////////// // Factory function for creating the appropriate cutscene player /////////////////////////////////////////////////////////////////////////////// -MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan *resMan, Audio::Mixer *snd, OSystem *system) { +MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan *resMan, OSystem *system) { Common::String filename; - Audio::SoundHandle *bgSoundHandle = new Audio::SoundHandle; // For the PSX version, we'll try the PlayStation stream files if (vm->isPsx()) { @@ -534,7 +514,7 @@ MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan * #ifdef USE_RGB_COLOR // All BS1 PSX videos run the videos at 2x speed Video::VideoDecoder *psxDecoder = new Video::PSXStreamDecoder(Video::PSXStreamDecoder::kCD2x); - return new MoviePlayer(vm, textMan, resMan, snd, system, bgSoundHandle, psxDecoder, kVideoDecoderPSX); + return new MoviePlayer(vm, textMan, resMan, system, psxDecoder, kVideoDecoderPSX); #else GUI::MessageDialog dialog(Common::String::format(_("PSX stream cutscene '%s' cannot be played in paletted mode"), filename.c_str()), _("OK")); dialog.runModal(); @@ -546,20 +526,20 @@ MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan * filename = Common::String::format("%s.smk", sequenceList[id]); if (Common::File::exists(filename)) { - Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder(snd); - return new MoviePlayer(vm, textMan, resMan, snd, system, bgSoundHandle, smkDecoder, kVideoDecoderSMK); + Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder(); + return new MoviePlayer(vm, textMan, resMan, system, smkDecoder, kVideoDecoderSMK); } filename = Common::String::format("%s.dxa", sequenceList[id]); if (Common::File::exists(filename)) { #ifdef USE_ZLIB - DXADecoderWithSound *dxaDecoder = new DXADecoderWithSound(snd, bgSoundHandle); - return new MoviePlayer(vm, textMan, resMan, snd, system, bgSoundHandle, dxaDecoder, kVideoDecoderDXA); + Video::VideoDecoder *dxaDecoder = new Video::DXADecoder(); + return new MoviePlayer(vm, textMan, resMan, system, dxaDecoder, kVideoDecoderDXA); #else GUI::MessageDialog dialog(_("DXA cutscenes found but ScummVM has been built without zlib support"), _("OK")); dialog.runModal(); - return NULL; + return 0; #endif } @@ -569,7 +549,7 @@ MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan * if (Common::File::exists(filename)) { GUI::MessageDialog dialog(_("MPEG2 cutscenes are no longer supported"), _("OK")); dialog.runModal(); - return NULL; + return 0; } if (!vm->isPsx() || scumm_stricmp(sequenceList[id], "enddemo") != 0) { @@ -578,7 +558,7 @@ MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan * dialog.runModal(); } - return NULL; + return 0; } } // End of namespace Sword1 diff --git a/engines/sword1/animation.h b/engines/sword1/animation.h index c2ed86a1a3..d0c61f5eb3 100644 --- a/engines/sword1/animation.h +++ b/engines/sword1/animation.h @@ -23,16 +23,19 @@ #ifndef SWORD1_ANIMATION_H #define SWORD1_ANIMATION_H -#include "video/dxa_decoder.h" -#include "video/video_decoder.h" - #include "common/list.h" -#include "audio/audiostream.h" - #include "sword1/screen.h" #include "sword1/sound.h" +namespace Graphics { +struct Surface; +} + +namespace Video { +class VideoDecoder; +} + namespace Sword1 { enum DecoderType { @@ -55,21 +58,9 @@ public: } }; -class DXADecoderWithSound : public Video::DXADecoder { -public: - DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle); - ~DXADecoderWithSound() {} - - uint32 getTime() const; - -private: - Audio::Mixer *_mixer; - Audio::SoundHandle *_bgSoundHandle; -}; - class MoviePlayer { public: - MoviePlayer(SwordEngine *vm, Text *textMan, ResMan *resMan, Audio::Mixer *snd, OSystem *system, Audio::SoundHandle *bgSoundHandle, Video::VideoDecoder *decoder, DecoderType decoderType); + MoviePlayer(SwordEngine *vm, Text *textMan, ResMan *resMan, OSystem *system, Video::VideoDecoder *decoder, DecoderType decoderType); virtual ~MoviePlayer(); bool load(uint32 id); void play(); @@ -78,7 +69,6 @@ protected: SwordEngine *_vm; Text *_textMan; ResMan *_resMan; - Audio::Mixer *_snd; OSystem *_system; Common::List<MovieText> _movieTexts; int _textX, _textY, _textWidth, _textHeight; @@ -88,8 +78,6 @@ protected: DecoderType _decoderType; Video::VideoDecoder *_decoder; - Audio::SoundHandle *_bgSoundHandle; - Audio::AudioStream *_bgSoundStream; bool playVideo(); void performPostProcessing(byte *screen); @@ -100,7 +88,7 @@ protected: void convertColor(byte r, byte g, byte b, float &h, float &s, float &v); }; -MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan *resMan, Audio::Mixer *snd, OSystem *system); +MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan *resMan, OSystem *system); } // End of namespace Sword1 diff --git a/engines/sword1/logic.cpp b/engines/sword1/logic.cpp index 8e04861edf..757d768780 100644 --- a/engines/sword1/logic.cpp +++ b/engines/sword1/logic.cpp @@ -959,7 +959,7 @@ int Logic::fnPlaySequence(Object *cpt, int32 id, int32 sequenceId, int32 d, int3 // meantime, we don't want any looping sound effects still playing. _sound->quitScreen(); - MoviePlayer *player = makeMoviePlayer(sequenceId, _vm, _textMan, _resMan, _mixer, _system); + MoviePlayer *player = makeMoviePlayer(sequenceId, _vm, _textMan, _resMan, _system); if (player) { _screen->clearScreen(); if (player->load(sequenceId)) diff --git a/engines/sword2/animation.cpp b/engines/sword2/animation.cpp index 5e3f8929e9..00260f789a 100644 --- a/engines/sword2/animation.cpp +++ b/engines/sword2/animation.cpp @@ -38,8 +38,11 @@ #include "sword2/screen.h" #include "sword2/animation.h" +#include "graphics/palette.h" + #include "gui/message.h" +#include "video/dxa_decoder.h" #include "video/smk_decoder.h" #include "video/psx_decoder.h" @@ -51,9 +54,8 @@ namespace Sword2 { // Basic movie player /////////////////////////////////////////////////////////////////////////////// -MoviePlayer::MoviePlayer(Sword2Engine *vm, Audio::Mixer *snd, OSystem *system, Audio::SoundHandle *bgSoundHandle, Video::VideoDecoder *decoder, DecoderType decoderType) - : _vm(vm), _snd(snd), _bgSoundHandle(bgSoundHandle), _system(system) { - _bgSoundStream = NULL; +MoviePlayer::MoviePlayer(Sword2Engine *vm, OSystem *system, Video::VideoDecoder *decoder, DecoderType decoderType) + : _vm(vm), _system(system) { _decoderType = decoderType; _decoder = decoder; @@ -62,7 +64,6 @@ MoviePlayer::MoviePlayer(Sword2Engine *vm, Audio::Mixer *snd, OSystem *system, A } MoviePlayer::~MoviePlayer() { - delete _bgSoundHandle; delete _decoder; } @@ -75,11 +76,6 @@ bool MoviePlayer::load(const char *name) { if (_vm->shouldQuit()) return false; - if (_decoderType == kVideoDecoderDXA) - _bgSoundStream = Audio::SeekableAudioStream::openStreamFile(name); - else - _bgSoundStream = NULL; - _textSurface = NULL; Common::String filename; @@ -99,6 +95,7 @@ bool MoviePlayer::load(const char *name) { // Need to load here in case it fails in which case we'd need // to go back to paletted mode if (_decoder->loadFile(filename)) { + _decoder->start(); return true; } else { initGraphics(640, 480, true); @@ -106,7 +103,15 @@ bool MoviePlayer::load(const char *name) { } } - return _decoder->loadFile(filename.c_str()); + if (!_decoder->loadFile(filename)) + return false; + + // For DXA, also add the external sound file + if (_decoderType == kVideoDecoderDXA) + _decoder->addStreamFileTrack(name); + + _decoder->start(); + return true; } void MoviePlayer::play(MovieText *movieTexts, uint32 numMovieTexts, uint32 leadIn, uint32 leadOut) { @@ -122,24 +127,15 @@ void MoviePlayer::play(MovieText *movieTexts, uint32 numMovieTexts, uint32 leadI if (leadIn) _vm->_sound->playMovieSound(leadIn, kLeadInSound); - if (_bgSoundStream) - _snd->playStream(Audio::Mixer::kSFXSoundType, _bgSoundHandle, _bgSoundStream); - - bool terminated = false; - - terminated = !playVideo(); + bool terminated = !playVideo(); closeTextObject(_currentMovieText, NULL, 0); if (terminated) { - _snd->stopHandle(*_bgSoundHandle); _vm->_sound->stopMovieSounds(); _vm->_sound->stopSpeech(); } - while (_snd->isSoundHandleActive(*_bgSoundHandle)) - _system->delayMillis(100); - if (_decoderType == kVideoDecoderPSX) { // Need to jump back to paletted color initGraphics(640, 480, true); @@ -336,7 +332,7 @@ bool MoviePlayer::playVideo() { } if (_decoder->hasDirtyPalette()) { - _decoder->setSystemPalette(); + _vm->_system->getPaletteManager()->setPalette(_decoder->getPalette(), 0, 256); uint32 maxWeight = 0; uint32 minWeight = 0xFFFFFFFF; @@ -406,31 +402,19 @@ void MoviePlayer::drawFramePSX(const Graphics::Surface *frame) { scaledFrame.free(); } -DXADecoderWithSound::DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle) - : _mixer(mixer), _bgSoundHandle(bgSoundHandle) { -} - -uint32 DXADecoderWithSound::getTime() const { - if (_mixer->isSoundHandleActive(*_bgSoundHandle)) - return _mixer->getSoundElapsedTime(*_bgSoundHandle); - - return DXADecoder::getTime(); -} - /////////////////////////////////////////////////////////////////////////////// // Factory function for creating the appropriate cutscene player /////////////////////////////////////////////////////////////////////////////// -MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, Audio::Mixer *snd, OSystem *system, uint32 frameCount) { +MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, OSystem *system, uint32 frameCount) { Common::String filename; - Audio::SoundHandle *bgSoundHandle = new Audio::SoundHandle; filename = Common::String::format("%s.str", name); if (Common::File::exists(filename)) { #ifdef USE_RGB_COLOR Video::VideoDecoder *psxDecoder = new Video::PSXStreamDecoder(Video::PSXStreamDecoder::kCD2x, frameCount); - return new MoviePlayer(vm, snd, system, bgSoundHandle, psxDecoder, kVideoDecoderPSX); + return new MoviePlayer(vm, system, psxDecoder, kVideoDecoderPSX); #else GUI::MessageDialog dialog(_("PSX cutscenes found but ScummVM has been built without RGB color support"), _("OK")); dialog.runModal(); @@ -441,16 +425,16 @@ MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, Audio::Mixer *s filename = Common::String::format("%s.smk", name); if (Common::File::exists(filename)) { - Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder(snd); - return new MoviePlayer(vm, snd, system, bgSoundHandle, smkDecoder, kVideoDecoderSMK); + Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder(); + return new MoviePlayer(vm, system, smkDecoder, kVideoDecoderSMK); } filename = Common::String::format("%s.dxa", name); if (Common::File::exists(filename)) { #ifdef USE_ZLIB - DXADecoderWithSound *dxaDecoder = new DXADecoderWithSound(snd, bgSoundHandle); - return new MoviePlayer(vm, snd, system, bgSoundHandle, dxaDecoder, kVideoDecoderDXA); + Video::DXADecoder *dxaDecoder = new Video::DXADecoder(); + return new MoviePlayer(vm, system, dxaDecoder, kVideoDecoderDXA); #else GUI::MessageDialog dialog(_("DXA cutscenes found but ScummVM has been built without zlib support"), _("OK")); dialog.runModal(); diff --git a/engines/sword2/animation.h b/engines/sword2/animation.h index 3d5c42b7f7..b2a243b2ca 100644 --- a/engines/sword2/animation.h +++ b/engines/sword2/animation.h @@ -25,12 +25,16 @@ #ifndef SWORD2_ANIMATION_H #define SWORD2_ANIMATION_H -#include "video/dxa_decoder.h" -#include "video/video_decoder.h" -#include "audio/mixer.h" - #include "sword2/screen.h" +namespace Graphics { +struct Surface; +} + +namespace Video { +class VideoDecoder; +} + namespace Sword2 { enum DecoderType { @@ -55,20 +59,9 @@ struct MovieText { } }; -class DXADecoderWithSound : public Video::DXADecoder { -public: - DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle); - ~DXADecoderWithSound() {} - - uint32 getTime() const; -private: - Audio::Mixer *_mixer; - Audio::SoundHandle *_bgSoundHandle; -}; - class MoviePlayer { public: - MoviePlayer(Sword2Engine *vm, Audio::Mixer *snd, OSystem *system, Audio::SoundHandle *bgSoundHandle, Video::VideoDecoder *decoder, DecoderType decoderType); + MoviePlayer(Sword2Engine *vm, OSystem *system, Video::VideoDecoder *decoder, DecoderType decoderType); virtual ~MoviePlayer(); bool load(const char *name); @@ -76,7 +69,6 @@ public: protected: Sword2Engine *_vm; - Audio::Mixer *_snd; OSystem *_system; MovieText *_movieTexts; uint32 _numMovieTexts; @@ -87,8 +79,6 @@ protected: DecoderType _decoderType; Video::VideoDecoder *_decoder; - Audio::SoundHandle *_bgSoundHandle; - Audio::AudioStream *_bgSoundStream; uint32 _leadOut; int _leadOutFrame; @@ -105,7 +95,7 @@ protected: uint32 getWhiteColor(); }; -MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, Audio::Mixer *snd, OSystem *system, uint32 frameCount); +MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, OSystem *system, uint32 frameCount); } // End of namespace Sword2 diff --git a/engines/sword2/function.cpp b/engines/sword2/function.cpp index 836b252d6c..07fcaa094b 100644 --- a/engines/sword2/function.cpp +++ b/engines/sword2/function.cpp @@ -2139,7 +2139,7 @@ int32 Logic::fnPlaySequence(int32 *params) { uint32 frameCount = Sword2Engine::isPsx() ? params[1] : 0; - _moviePlayer = makeMoviePlayer(filename, _vm, _vm->_mixer, _vm->_system, frameCount); + _moviePlayer = makeMoviePlayer(filename, _vm, _vm->_system, frameCount); if (_moviePlayer && _moviePlayer->load(filename)) { _moviePlayer->play(_sequenceTextList, _sequenceTextLines, _smackerLeadIn, _smackerLeadOut); diff --git a/engines/sword25/fmv/movieplayer.cpp b/engines/sword25/fmv/movieplayer.cpp index 9ee13b4b6d..a95532ec65 100644 --- a/engines/sword25/fmv/movieplayer.cpp +++ b/engines/sword25/fmv/movieplayer.cpp @@ -61,6 +61,7 @@ bool MoviePlayer::loadMovie(const Common::String &filename, uint z) { // Get the file and load it into the decoder Common::SeekableReadStream *in = Kernel::getInstance()->getPackage()->getStream(filename); _decoder.loadStream(in); + _decoder.start(); GraphicEngine *pGfx = Kernel::getInstance()->getGfx(); diff --git a/engines/sword25/fmv/movieplayer.h b/engines/sword25/fmv/movieplayer.h index 1d256e56ba..2f5614b505 100644 --- a/engines/sword25/fmv/movieplayer.h +++ b/engines/sword25/fmv/movieplayer.h @@ -39,7 +39,7 @@ #include "sword25/gfx/bitmap.h" #ifdef USE_THEORADEC -#include "sword25/fmv/theora_decoder.h" +#include "video/theora_decoder.h" #endif #define THEORA_INDIRECT_RENDERING @@ -141,7 +141,7 @@ private: #ifdef USE_THEORADEC - TheoraDecoder _decoder; + Video::TheoraDecoder _decoder; Graphics::Surface *_backSurface; int _outX, _outY; diff --git a/engines/sword25/fmv/theora_decoder.cpp b/engines/sword25/fmv/theora_decoder.cpp deleted file mode 100644 index d38f5a26cf..0000000000 --- a/engines/sword25/fmv/theora_decoder.cpp +++ /dev/null @@ -1,565 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * 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. - * - */ - -/* - * Source is based on the player example from libvorbis package, - * available at: http://svn.xiph.org/trunk/theora/examples/player_example.c - * - * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. - * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS - * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE - * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. - * - * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 - * by the Xiph.Org Foundation and contributors http://www.xiph.org/ - * - */ - -#include "sword25/fmv/theora_decoder.h" - -#ifdef USE_THEORADEC -#include "common/system.h" -#include "common/textconsole.h" -#include "common/util.h" -#include "graphics/yuv_to_rgb.h" -#include "audio/decoders/raw.h" -#include "sword25/kernel/common.h" - -namespace Sword25 { - -#define AUDIOFD_FRAGSIZE 10240 - -static double rint(double v) { - return floor(v + 0.5); -} - -TheoraDecoder::TheoraDecoder(Audio::Mixer::SoundType soundType) { - _fileStream = 0; - - _theoraPacket = 0; - _vorbisPacket = 0; - _theoraDecode = 0; - _theoraSetup = 0; - _nextFrameStartTime = 0.0; - - _soundType = soundType; - _audStream = 0; - _audHandle = new Audio::SoundHandle(); - - ogg_sync_init(&_oggSync); - - _curFrame = -1; - _audiobuf = (ogg_int16_t *)malloc(AUDIOFD_FRAGSIZE * sizeof(ogg_int16_t)); - - reset(); -} - -TheoraDecoder::~TheoraDecoder() { - close(); - delete _fileStream; - delete _audHandle; - free(_audiobuf); -} - -void TheoraDecoder::queuePage(ogg_page *page) { - if (_theoraPacket) - ogg_stream_pagein(&_theoraOut, page); - - if (_vorbisPacket) - ogg_stream_pagein(&_vorbisOut, page); -} - -int TheoraDecoder::bufferData() { - char *buffer = ogg_sync_buffer(&_oggSync, 4096); - int bytes = _fileStream->read(buffer, 4096); - - ogg_sync_wrote(&_oggSync, bytes); - - return bytes; -} - -bool TheoraDecoder::loadStream(Common::SeekableReadStream *stream) { - close(); - - _endOfAudio = false; - _endOfVideo = false; - _fileStream = stream; - - // start up Ogg stream synchronization layer - ogg_sync_init(&_oggSync); - - // init supporting Vorbis structures needed in header parsing - vorbis_info_init(&_vorbisInfo); - vorbis_comment_init(&_vorbisComment); - - // init supporting Theora structures needed in header parsing - th_comment_init(&_theoraComment); - th_info_init(&_theoraInfo); - - // Ogg file open; parse the headers - // Only interested in Vorbis/Theora streams - bool foundHeader = false; - while (!foundHeader) { - int ret = bufferData(); - - if (ret == 0) - break; - - while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) { - ogg_stream_state test; - - // is this a mandated initial header? If not, stop parsing - if (!ogg_page_bos(&_oggPage)) { - // don't leak the page; get it into the appropriate stream - queuePage(&_oggPage); - foundHeader = true; - break; - } - - ogg_stream_init(&test, ogg_page_serialno(&_oggPage)); - ogg_stream_pagein(&test, &_oggPage); - ogg_stream_packetout(&test, &_oggPacket); - - // identify the codec: try theora - if (!_theoraPacket && th_decode_headerin(&_theoraInfo, &_theoraComment, &_theoraSetup, &_oggPacket) >= 0) { - // it is theora - memcpy(&_theoraOut, &test, sizeof(test)); - _theoraPacket = 1; - } else if (!_vorbisPacket && vorbis_synthesis_headerin(&_vorbisInfo, &_vorbisComment, &_oggPacket) >= 0) { - // it is vorbis - memcpy(&_vorbisOut, &test, sizeof(test)); - _vorbisPacket = 1; - } else { - // whatever it is, we don't care about it - ogg_stream_clear(&test); - } - } - // fall through to non-bos page parsing - } - - // we're expecting more header packets. - while ((_theoraPacket && _theoraPacket < 3) || (_vorbisPacket && _vorbisPacket < 3)) { - int ret; - - // look for further theora headers - while (_theoraPacket && (_theoraPacket < 3) && (ret = ogg_stream_packetout(&_theoraOut, &_oggPacket))) { - if (ret < 0) - error("Error parsing Theora stream headers; corrupt stream?"); - - if (!th_decode_headerin(&_theoraInfo, &_theoraComment, &_theoraSetup, &_oggPacket)) - error("Error parsing Theora stream headers; corrupt stream?"); - - _theoraPacket++; - } - - // look for more vorbis header packets - while (_vorbisPacket && (_vorbisPacket < 3) && (ret = ogg_stream_packetout(&_vorbisOut, &_oggPacket))) { - if (ret < 0) - error("Error parsing Vorbis stream headers; corrupt stream?"); - - if (vorbis_synthesis_headerin(&_vorbisInfo, &_vorbisComment, &_oggPacket)) - error("Error parsing Vorbis stream headers; corrupt stream?"); - - _vorbisPacket++; - - if (_vorbisPacket == 3) - break; - } - - // The header pages/packets will arrive before anything else we - // care about, or the stream is not obeying spec - - if (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) { - queuePage(&_oggPage); // demux into the appropriate stream - } else { - ret = bufferData(); // someone needs more data - - if (ret == 0) - error("End of file while searching for codec headers."); - } - } - - // and now we have it all. initialize decoders - if (_theoraPacket) { - _theoraDecode = th_decode_alloc(&_theoraInfo, _theoraSetup); - debugN(1, "Ogg logical stream %lx is Theora %dx%d %.02f fps", - _theoraOut.serialno, _theoraInfo.pic_width, _theoraInfo.pic_height, - (double)_theoraInfo.fps_numerator / _theoraInfo.fps_denominator); - - switch (_theoraInfo.pixel_fmt) { - case TH_PF_420: - debug(1, " 4:2:0 video"); - break; - case TH_PF_422: - debug(1, " 4:2:2 video"); - break; - case TH_PF_444: - debug(1, " 4:4:4 video"); - break; - case TH_PF_RSVD: - default: - debug(1, " video\n (UNKNOWN Chroma sampling!)"); - break; - } - - if (_theoraInfo.pic_width != _theoraInfo.frame_width || _theoraInfo.pic_height != _theoraInfo.frame_height) - debug(1, " Frame content is %dx%d with offset (%d,%d).", - _theoraInfo.frame_width, _theoraInfo.frame_height, _theoraInfo.pic_x, _theoraInfo.pic_y); - - switch (_theoraInfo.colorspace){ - case TH_CS_UNSPECIFIED: - /* nothing to report */ - break; - case TH_CS_ITU_REC_470M: - debug(1, " encoder specified ITU Rec 470M (NTSC) color."); - break; - case TH_CS_ITU_REC_470BG: - debug(1, " encoder specified ITU Rec 470BG (PAL) color."); - break; - default: - debug(1, "warning: encoder specified unknown colorspace (%d).", _theoraInfo.colorspace); - break; - } - - debug(1, "Encoded by %s", _theoraComment.vendor); - if (_theoraComment.comments) { - debug(1, "theora comment header:"); - for (int i = 0; i < _theoraComment.comments; i++) { - if (_theoraComment.user_comments[i]) { - int len = _theoraComment.comment_lengths[i]; - char *value = (char *)malloc(len + 1); - if (value) { - memcpy(value, _theoraComment.user_comments[i], len); - value[len] = '\0'; - debug(1, "\t%s", value); - free(value); - } - } - } - } - - th_decode_ctl(_theoraDecode, TH_DECCTL_GET_PPLEVEL_MAX, &_ppLevelMax, sizeof(_ppLevelMax)); - _ppLevel = _ppLevelMax; - th_decode_ctl(_theoraDecode, TH_DECCTL_SET_PPLEVEL, &_ppLevel, sizeof(_ppLevel)); - _ppInc = 0; - } else { - // tear down the partial theora setup - th_info_clear(&_theoraInfo); - th_comment_clear(&_theoraComment); - } - - th_setup_free(_theoraSetup); - _theoraSetup = 0; - - if (_vorbisPacket) { - vorbis_synthesis_init(&_vorbisDSP, &_vorbisInfo); - vorbis_block_init(&_vorbisDSP, &_vorbisBlock); - debug(3, "Ogg logical stream %lx is Vorbis %d channel %ld Hz audio.", - _vorbisOut.serialno, _vorbisInfo.channels, _vorbisInfo.rate); - - _audStream = Audio::makeQueuingAudioStream(_vorbisInfo.rate, _vorbisInfo.channels); - - // Get enough audio data to start us off - while (_audStream->numQueuedStreams() == 0) { - // Queue more data - bufferData(); - while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) - queuePage(&_oggPage); - - queueAudio(); - } - - if (_audStream) - g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, _audHandle, _audStream, -1, getVolume(), getBalance()); - } else { - // tear down the partial vorbis setup - vorbis_info_clear(&_vorbisInfo); - vorbis_comment_clear(&_vorbisComment); - _endOfAudio = true; - } - - _surface.create(_theoraInfo.frame_width, _theoraInfo.frame_height, g_system->getScreenFormat()); - - // Set up a display surface - _displaySurface.pixels = _surface.getBasePtr(_theoraInfo.pic_x, _theoraInfo.pic_y); - _displaySurface.w = _theoraInfo.pic_width; - _displaySurface.h = _theoraInfo.pic_height; - _displaySurface.format = _surface.format; - _displaySurface.pitch = _surface.pitch; - - // Set the frame rate - _frameRate = Common::Rational(_theoraInfo.fps_numerator, _theoraInfo.fps_denominator); - - return true; -} - -void TheoraDecoder::close() { - if (_vorbisPacket) { - ogg_stream_clear(&_vorbisOut); - vorbis_block_clear(&_vorbisBlock); - vorbis_dsp_clear(&_vorbisDSP); - vorbis_comment_clear(&_vorbisComment); - vorbis_info_clear(&_vorbisInfo); - - g_system->getMixer()->stopHandle(*_audHandle); - - _audStream = 0; - _vorbisPacket = false; - } - if (_theoraPacket) { - ogg_stream_clear(&_theoraOut); - th_decode_free(_theoraDecode); - th_comment_clear(&_theoraComment); - th_info_clear(&_theoraInfo); - _theoraDecode = 0; - _theoraPacket = false; - } - - if (!_fileStream) - return; - - ogg_sync_clear(&_oggSync); - - delete _fileStream; - _fileStream = 0; - - _surface.free(); - _displaySurface.pixels = 0; - _displaySurface.free(); - - reset(); -} - -const Graphics::Surface *TheoraDecoder::decodeNextFrame() { - // First, let's get our frame - while (_theoraPacket) { - // theora is one in, one out... - if (ogg_stream_packetout(&_theoraOut, &_oggPacket) > 0) { - - if (_ppInc) { - _ppLevel += _ppInc; - th_decode_ctl(_theoraDecode, TH_DECCTL_SET_PPLEVEL, &_ppLevel, sizeof(_ppLevel)); - _ppInc = 0; - } - - if (th_decode_packetin(_theoraDecode, &_oggPacket, NULL) == 0) { - _curFrame++; - - // Convert YUV data to RGB data - th_ycbcr_buffer yuv; - th_decode_ycbcr_out(_theoraDecode, yuv); - translateYUVtoRGBA(yuv); - - if (_curFrame == 0) - _startTime = g_system->getMillis(); - - double time = th_granule_time(_theoraDecode, _oggPacket.granulepos); - - // We need to calculate when the next frame should be shown - // This is all in floating point because that's what the Ogg code gives us - // Ogg is a lossy container format, so it doesn't always list the time to the - // next frame. In such cases, we need to calculate it ourselves. - if (time == -1.0) - _nextFrameStartTime += _frameRate.getInverse().toDouble(); - else - _nextFrameStartTime = time; - - // break out - break; - } - } else { - // If we can't get any more frames, we're done. - if (_theoraOut.e_o_s || _fileStream->eos()) { - _endOfVideo = true; - break; - } - - // Queue more data - bufferData(); - while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) - queuePage(&_oggPage); - } - - // Update audio if we can - queueAudio(); - } - - // Force at least some audio to be buffered - // TODO: 5 is very arbitrary. We probably should do something like QuickTime does. - while (!_endOfAudio && _audStream->numQueuedStreams() < 5) { - bufferData(); - while (ogg_sync_pageout(&_oggSync, &_oggPage) > 0) - queuePage(&_oggPage); - - bool queuedAudio = queueAudio(); - if ((_vorbisOut.e_o_s || _fileStream->eos()) && !queuedAudio) { - _endOfAudio = true; - break; - } - } - - return &_displaySurface; -} - -bool TheoraDecoder::queueAudio() { - if (!_audStream) - return false; - - // An audio buffer should have been allocated (either in the constructor or after queuing the current buffer) - if (!_audiobuf) { - warning("[TheoraDecoder::queueAudio] Invalid audio buffer"); - return false; - } - - bool queuedAudio = false; - - for (;;) { - float **pcm; - - // if there's pending, decoded audio, grab it - int ret = vorbis_synthesis_pcmout(&_vorbisDSP, &pcm); - if (ret > 0) { - int count = _audiobufFill / 2; - int maxsamples = ((AUDIOFD_FRAGSIZE - _audiobufFill) / _vorbisInfo.channels) >> 1; - int i; - for (i = 0; i < ret && i < maxsamples; i++) - for (int j = 0; j < _vorbisInfo.channels; j++) { - int val = CLIP((int)rint(pcm[j][i] * 32767.f), -32768, 32767); - _audiobuf[count++] = val; - } - - vorbis_synthesis_read(&_vorbisDSP, i); - _audiobufFill += (i * _vorbisInfo.channels) << 1; - - if (_audiobufFill == AUDIOFD_FRAGSIZE) { - byte flags = Audio::FLAG_16BITS | Audio::FLAG_STEREO; -#ifdef SCUMM_LITTLE_ENDIAN - flags |= Audio::FLAG_LITTLE_ENDIAN; -#endif - _audStream->queueBuffer((byte *)_audiobuf, AUDIOFD_FRAGSIZE, DisposeAfterUse::NO, flags); - - // The audio mixer is now responsible for the old audio buffer. - // We need to create a new one. - _audiobuf = (ogg_int16_t *)malloc(AUDIOFD_FRAGSIZE * sizeof(ogg_int16_t)); - if (!_audiobuf) { - warning("[TheoraDecoder::queueAudio] Cannot allocate memory for audio buffer"); - return false; - } - - _audiobufFill = 0; - queuedAudio = true; - } - } else { - // no pending audio; is there a pending packet to decode? - if (ogg_stream_packetout(&_vorbisOut, &_oggPacket) > 0) { - if (vorbis_synthesis(&_vorbisBlock, &_oggPacket) == 0) // test for success! - vorbis_synthesis_blockin(&_vorbisDSP, &_vorbisBlock); - } else // we've buffered all we have, break out for now - return queuedAudio; - } - } - - // Unreachable - return false; -} - -void TheoraDecoder::reset() { - VideoDecoder::reset(); - - // FIXME: This does a rewind() instead of a reset()! - - if (_fileStream) - _fileStream->seek(0); - - _audiobufFill = 0; - _audiobufReady = false; - - _curFrame = -1; - - _theoraPacket = 0; - _vorbisPacket = 0; -} - -bool TheoraDecoder::endOfVideo() const { - return !isVideoLoaded() || (_endOfVideo && (!_audStream || (_audStream->endOfData() && _endOfAudio))); -} - -uint32 TheoraDecoder::getTimeToNextFrame() const { - if (endOfVideo() || _curFrame < 0) - return 0; - - uint32 elapsedTime = getTime(); - uint32 nextFrameStartTime = (uint32)(_nextFrameStartTime * 1000); - - if (nextFrameStartTime <= elapsedTime) - return 0; - - return nextFrameStartTime - elapsedTime; -} - -uint32 TheoraDecoder::getTime() const { - if (_audStream) - return g_system->getMixer()->getSoundElapsedTime(*_audHandle); - - return VideoDecoder::getTime(); -} - -void TheoraDecoder::pauseVideoIntern(bool pause) { - if (_audStream) - g_system->getMixer()->pauseHandle(*_audHandle, pause); -} - -enum TheoraYUVBuffers { - kBufferY = 0, - kBufferU = 1, - kBufferV = 2 -}; - -void TheoraDecoder::translateYUVtoRGBA(th_ycbcr_buffer &YUVBuffer) { - // Width and height of all buffers have to be divisible by 2. - assert((YUVBuffer[kBufferY].width & 1) == 0); - assert((YUVBuffer[kBufferY].height & 1) == 0); - assert((YUVBuffer[kBufferU].width & 1) == 0); - assert((YUVBuffer[kBufferV].width & 1) == 0); - - // UV images have to have a quarter of the Y image resolution - assert(YUVBuffer[kBufferU].width == YUVBuffer[kBufferY].width >> 1); - assert(YUVBuffer[kBufferV].width == YUVBuffer[kBufferY].width >> 1); - assert(YUVBuffer[kBufferU].height == YUVBuffer[kBufferY].height >> 1); - assert(YUVBuffer[kBufferV].height == YUVBuffer[kBufferY].height >> 1); - - Graphics::convertYUV420ToRGB(&_surface, YUVBuffer[kBufferY].data, YUVBuffer[kBufferU].data, YUVBuffer[kBufferV].data, YUVBuffer[kBufferY].width, YUVBuffer[kBufferY].height, YUVBuffer[kBufferY].stride, YUVBuffer[kBufferU].stride); -} - -void TheoraDecoder::updateVolume() { - if (g_system->getMixer()->isSoundHandleActive(*_audHandle)) - g_system->getMixer()->setChannelVolume(*_audHandle, getVolume()); -} - -void TheoraDecoder::updateBalance() { - if (g_system->getMixer()->isSoundHandleActive(*_audHandle)) - g_system->getMixer()->setChannelBalance(*_audHandle, getBalance()); -} - -} // End of namespace Sword25 - -#endif diff --git a/engines/sword25/fmv/theora_decoder.h b/engines/sword25/fmv/theora_decoder.h deleted file mode 100644 index 739040024f..0000000000 --- a/engines/sword25/fmv/theora_decoder.h +++ /dev/null @@ -1,144 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * 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. - * - */ - -#ifndef SWORD25_THEORADECODER_H -#define SWORD25_THEORADECODER_H - -#include "common/scummsys.h" // for USE_THEORADEC - -#ifdef USE_THEORADEC - -#include "common/rational.h" -#include "video/video_decoder.h" -#include "audio/audiostream.h" -#include "audio/mixer.h" -#include "graphics/pixelformat.h" -#include "graphics/surface.h" - -#include <theora/theoradec.h> -#include <vorbis/codec.h> - -namespace Common { -class SeekableReadStream; -} - -namespace Sword25 { - -/** - * - * Decoder for Theora videos. - * Video decoder used in engines: - * - sword25 - */ -class TheoraDecoder : public Video::VideoDecoder { -public: - TheoraDecoder(Audio::Mixer::SoundType soundType = Audio::Mixer::kMusicSoundType); - virtual ~TheoraDecoder(); - - /** - * Load a video file - * @param stream the stream to load - */ - bool loadStream(Common::SeekableReadStream *stream); - void close(); - void reset(); - - /** - * Decode the next frame and return the frame's surface - * @note the return surface should *not* be freed - * @note this may return 0, in which case the last frame should be kept on screen - */ - const Graphics::Surface *decodeNextFrame(); - - bool isVideoLoaded() const { return _fileStream != 0; } - uint16 getWidth() const { return _displaySurface.w; } - uint16 getHeight() const { return _displaySurface.h; } - - uint32 getFrameCount() const { - // It is not possible to get frame count easily - // I.e. seeking is required - assert(0); - return 0; - } - - Graphics::PixelFormat getPixelFormat() const { return _displaySurface.format; } - uint32 getTime() const; - uint32 getTimeToNextFrame() const; - - bool endOfVideo() const; - -protected: - // VideoDecoder API - void updateVolume(); - void updateBalance(); - void pauseVideoIntern(bool pause); - -private: - void queuePage(ogg_page *page); - bool queueAudio(); - int bufferData(); - void translateYUVtoRGBA(th_ycbcr_buffer &YUVBuffer); - - Common::SeekableReadStream *_fileStream; - Graphics::Surface _surface; - Graphics::Surface _displaySurface; - Common::Rational _frameRate; - double _nextFrameStartTime; - bool _endOfVideo; - bool _endOfAudio; - - Audio::Mixer::SoundType _soundType; - Audio::SoundHandle *_audHandle; - Audio::QueuingAudioStream *_audStream; - - ogg_sync_state _oggSync; - ogg_page _oggPage; - ogg_packet _oggPacket; - ogg_stream_state _vorbisOut; - ogg_stream_state _theoraOut; - th_info _theoraInfo; - th_comment _theoraComment; - th_dec_ctx *_theoraDecode; - th_setup_info *_theoraSetup; - vorbis_info _vorbisInfo; - vorbis_dsp_state _vorbisDSP; - vorbis_block _vorbisBlock; - vorbis_comment _vorbisComment; - - int _theoraPacket; - int _vorbisPacket; - - int _ppLevelMax; - int _ppLevel; - int _ppInc; - - // single audio fragment audio buffering - int _audiobufFill; - bool _audiobufReady; - ogg_int16_t *_audiobuf; -}; - -} // End of namespace Sword25 - -#endif - -#endif diff --git a/engines/sword25/module.mk b/engines/sword25/module.mk index 302120c500..e24a221244 100644 --- a/engines/sword25/module.mk +++ b/engines/sword25/module.mk @@ -85,11 +85,6 @@ MODULE_OBJS := \ util/pluto/pluto.o \ util/pluto/plzio.o -ifdef USE_THEORADEC -MODULE_OBJS += \ - fmv/theora_decoder.o -endif - # This module can be built as a plugin ifeq ($(ENABLE_SWORD25), DYNAMIC_PLUGIN) PLUGIN := 1 diff --git a/engines/sword25/sfx/soundengine.cpp b/engines/sword25/sfx/soundengine.cpp index 78b2db19eb..69fae3dc4e 100644 --- a/engines/sword25/sfx/soundengine.cpp +++ b/engines/sword25/sfx/soundengine.cpp @@ -239,16 +239,20 @@ void SoundEngine::setSoundVolume(uint handle, float volume) { debugC(1, kDebugSound, "SoundEngine::setSoundVolume(%d, %f)", handle, volume); SndHandle* sndHandle = findHandle(handle); - if (sndHandle != NULL) + if (sndHandle != NULL) { + sndHandle->volume = volume; _mixer->setChannelVolume(sndHandle->handle, (byte)(volume * 255)); + } } void SoundEngine::setSoundPanning(uint handle, float pan) { debugC(1, kDebugSound, "SoundEngine::setSoundPanning(%d, %f)", handle, pan); SndHandle* sndHandle = findHandle(handle); - if (sndHandle != NULL) + if (sndHandle != NULL) { + sndHandle->pan = pan; _mixer->setChannelBalance(sndHandle->handle, (int8)(pan * 127)); + } } void SoundEngine::pauseSound(uint handle) { @@ -324,13 +328,16 @@ bool SoundEngine::canLoadResource(const Common::String &fileName) { return fname.hasSuffix(".ogg"); } - - bool SoundEngine::persist(OutputPersistenceBlock &writer) { +bool SoundEngine::persist(OutputPersistenceBlock &writer) { writer.write(_maxHandleId); for (uint i = 0; i < SOUND_HANDLES; i++) { writer.write(_handles[i].id); + // Don't restart sounds which already finished playing. + if (_handles[i].type != kFreeHandle && !_mixer->isSoundHandleActive(_handles[i].handle)) + _handles[i].type = kFreeHandle; + writer.writeString(_handles[i].fileName); writer.write((int)_handles[i].sndType); writer.write(_handles[i].volume); @@ -374,7 +381,8 @@ bool SoundEngine::unpersist(InputPersistenceBlock &reader) { reader.read(layer); if (reader.isGood()) { - playSoundEx(fileName, (SOUND_TYPES)sndType, volume, pan, loop, loopStart, loopEnd, layer, i); + if (sndType != kFreeHandle) + playSoundEx(fileName, (SOUND_TYPES)sndType, volume, pan, loop, loopStart, loopEnd, layer, i); } else return false; } diff --git a/engines/sword25/util/pluto/pluto.cpp b/engines/sword25/util/pluto/pluto.cpp index d645e5ed2a..b7f5e30340 100644 --- a/engines/sword25/util/pluto/pluto.cpp +++ b/engines/sword25/util/pluto/pluto.cpp @@ -895,10 +895,10 @@ static void unpersistnumber(UnpersistInfo *upi) static void unpersiststring(UnpersistInfo *upi) { /* perms reftbl sptbl ref */ - int length; + size_t length; char* string; lua_checkstack(upi->L, 1); - verify(LIF(Z,read)(&upi->zio, &length, sizeof(int)) == 0); + verify(LIF(Z,read)(&upi->zio, &length, sizeof(size_t)) == 0); string = pdep_newvector(upi->L, length, char); verify(LIF(Z,read)(&upi->zio, string, length) == 0); lua_pushlstring(upi->L, string, length); diff --git a/engines/toon/movie.cpp b/engines/toon/movie.cpp index 93e41adf57..8c85e20f7c 100644 --- a/engines/toon/movie.cpp +++ b/engines/toon/movie.cpp @@ -25,6 +25,7 @@ #include "common/keyboard.h" #include "common/stream.h" #include "common/system.h" +#include "graphics/palette.h" #include "graphics/surface.h" #include "toon/audio.h" @@ -33,6 +34,10 @@ namespace Toon { +ToonstruckSmackerDecoder::ToonstruckSmackerDecoder() : Video::SmackerDecoder() { + _lowRes = false; +} + void ToonstruckSmackerDecoder::handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize) { debugC(6, kDebugMovie, "handleAudioTrack(%d, %d, %d)", track, chunkSize, unpackedSize); @@ -40,33 +45,21 @@ void ToonstruckSmackerDecoder::handleAudioTrack(byte track, uint32 chunkSize, ui /* uint16 width = */ _fileStream->readUint16LE(); uint16 height = _fileStream->readUint16LE(); _lowRes = (height == getHeight() / 2); - } else + } else { Video::SmackerDecoder::handleAudioTrack(track, chunkSize, unpackedSize); + } } -bool ToonstruckSmackerDecoder::loadFile(const Common::String &filename) { - debugC(1, kDebugMovie, "loadFile(%s)", filename.c_str()); +bool ToonstruckSmackerDecoder::loadStream(Common::SeekableReadStream *stream) { + if (!Video::SmackerDecoder::loadStream(stream)) + return false; _lowRes = false; - - if (Video::SmackerDecoder::loadFile(filename)) { - if (_surface->h == 200) { - if (_surface) { - _surface->free(); - delete _surface; - } - _surface = new Graphics::Surface(); - _surface->create(640, 400, Graphics::PixelFormat::createFormatCLUT8()); - _header.flags = 4; - } - - return true; - } - return false; + return true; } -ToonstruckSmackerDecoder::ToonstruckSmackerDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) : Video::SmackerDecoder(mixer, soundType) { - _lowRes = false; +Video::SmackerDecoder::SmackerVideoTrack *ToonstruckSmackerDecoder::createVideoTrack(uint32 width, uint32 height, uint32 frameCount, const Common::Rational &frameRate, uint32 flags, uint32 signature) const { + return Video::SmackerDecoder::createVideoTrack(width, height, frameCount, frameRate, (height == 200) ? 4 : flags, signature); } // decoder is deallocated with Movie destruction i.e. new ToonstruckSmackerDecoder is needed @@ -103,6 +96,9 @@ void Movie::play(const Common::String &video, int32 flags) { bool Movie::playVideo(bool isFirstIntroVideo) { debugC(1, kDebugMovie, "playVideo(isFirstIntroVideo: %d)", isFirstIntroVideo); + + _decoder->start(); + while (!_vm->shouldQuit() && !_decoder->endOfVideo()) { if (_decoder->needsUpdate()) { const Graphics::Surface *frame = _decoder->decodeNextFrame(); @@ -131,7 +127,7 @@ bool Movie::playVideo(bool isFirstIntroVideo) { } } } - _decoder->setSystemPalette(); + _vm->_system->getPaletteManager()->setPalette(_decoder->getPalette(), 0, 256); _vm->_system->updateScreen(); } diff --git a/engines/toon/movie.h b/engines/toon/movie.h index 2cd33302f2..e795182cba 100644 --- a/engines/toon/movie.h +++ b/engines/toon/movie.h @@ -30,13 +30,17 @@ namespace Toon { class ToonstruckSmackerDecoder : public Video::SmackerDecoder { public: - ToonstruckSmackerDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType = Audio::Mixer::kSFXSoundType); - virtual ~ToonstruckSmackerDecoder() {} - void handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize); - bool loadFile(const Common::String &filename); + ToonstruckSmackerDecoder(); + + bool loadStream(Common::SeekableReadStream *stream); bool isLowRes() { return _lowRes; } + protected: - bool _lowRes; + void handleAudioTrack(byte track, uint32 chunkSize, uint32 unpackedSize); + SmackerVideoTrack *createVideoTrack(uint32 width, uint32 height, uint32 frameCount, const Common::Rational &frameRate, uint32 flags, uint32 signature) const; + +private: + bool _lowRes; }; class Movie { diff --git a/engines/toon/toon.cpp b/engines/toon/toon.cpp index ee427652d8..9fd8415676 100644 --- a/engines/toon/toon.cpp +++ b/engines/toon/toon.cpp @@ -51,7 +51,7 @@ void ToonEngine::init() { _currentScriptRegion = 0; _resources = new Resources(this); _animationManager = new AnimationManager(this); - _moviePlayer = new Movie(this, new ToonstruckSmackerDecoder(_mixer)); + _moviePlayer = new Movie(this, new ToonstruckSmackerDecoder()); _hotspots = new Hotspots(this); _mainSurface = new Graphics::Surface(); diff --git a/engines/tucker/sequences.cpp b/engines/tucker/sequences.cpp index 775fd6f1a0..16c4f4f6f0 100644 --- a/engines/tucker/sequences.cpp +++ b/engines/tucker/sequences.cpp @@ -28,6 +28,7 @@ #include "audio/decoders/wave.h" #include "graphics/palette.h" +#include "graphics/surface.h" #include "tucker/tucker.h" #include "tucker/graphics.h" @@ -749,6 +750,7 @@ void AnimationSequencePlayer::openAnimation(int index, const char *fileName) { _seqNum = 1; return; } + _flicPlayer[index].start(); _flicPlayer[index].decodeNextFrame(); if (index == 0) { getRGBPalette(index); @@ -801,7 +803,7 @@ void AnimationSequencePlayer::playIntroSeq19_20() { if (_flicPlayer[0].getCurFrame() >= 115) { surface = _flicPlayer[1].decodeNextFrame(); if (_flicPlayer[1].endOfVideo()) - _flicPlayer[1].reset(); + _flicPlayer[1].rewind(); } bool framesLeft = decodeNextAnimationFrame(0, false); |