diff options
author | Neeraj Kumar | 2010-06-08 17:24:29 +0000 |
---|---|---|
committer | Neeraj Kumar | 2010-06-08 17:24:29 +0000 |
commit | 207a5e0779de9f0002b0b1984bde90ce6597e1f2 (patch) | |
tree | 98883ef89261afd4288f6dadbffe436d5d966dfc /engines | |
parent | e00e94ae18aeb1ed460476f822e20b5bdfe171a4 (diff) | |
parent | 356728dab7f2c4cedf73684d7fe3b968be7396fd (diff) | |
download | scummvm-rg350-207a5e0779de9f0002b0b1984bde90ce6597e1f2.tar.gz scummvm-rg350-207a5e0779de9f0002b0b1984bde90ce6597e1f2.tar.bz2 scummvm-rg350-207a5e0779de9f0002b0b1984bde90ce6597e1f2.zip |
updated my outdate copy of trunk, added couple of more tests in gfxtests
svn-id: r49510
Diffstat (limited to 'engines')
154 files changed, 5663 insertions, 11899 deletions
diff --git a/engines/dialogs.cpp b/engines/dialogs.cpp index 73ba591b4b..954bc81470 100644 --- a/engines/dialogs.cpp +++ b/engines/dialogs.cpp @@ -35,8 +35,9 @@ #include "gui/GuiManager.h" #include "gui/launcher.h" #include "gui/ListWidget.h" -#include "gui/ThemeEval.h" +#include "gui/options.h" #include "gui/saveload.h" +#include "gui/ThemeEval.h" #include "engines/dialogs.h" #include "engines/engine.h" @@ -49,16 +50,17 @@ using GUI::CommandSender; using GUI::StaticTextWidget; -enum { - kSaveCmd = 'SAVE', - kLoadCmd = 'LOAD', - kPlayCmd = 'PLAY', - kOptionsCmd = 'OPTN', - kHelpCmd = 'HELP', - kAboutCmd = 'ABOU', - kQuitCmd = 'QUIT', - kRTLCmd = 'RTL ', - kChooseCmd = 'CHOS' +class ConfigDialog : public GUI::OptionsDialog { +protected: +#ifdef SMALL_SCREEN_DEVICE + GUI::Dialog *_keysDialog; +#endif + +public: + ConfigDialog(bool subtitleControls); + ~ConfigDialog(); + + virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); }; MainMenuDialog::MainMenuDialog(Engine *engine) @@ -95,6 +97,12 @@ MainMenuDialog::MainMenuDialog(Engine *engine) new GUI::ButtonWidget(this, "GlobalMenu.Options", "Options", kOptionsCmd, 'O'); + // The help button is disabled by default. + // To enable "Help", an engine needs to use a subclass of MainMenuDialog + // (at least for now, we might change how this works in the future). + _helpButton = new GUI::ButtonWidget(this, "GlobalMenu.Help", "Help", kHelpCmd, 'H'); + _helpButton->setEnabled(false); + new GUI::ButtonWidget(this, "GlobalMenu.About", "About", kAboutCmd, 'A'); _rtlButton = new GUI::ButtonWidget(this, "GlobalMenu.RTL", "Return to Launcher", kRTLCmd, 'R'); @@ -135,6 +143,9 @@ void MainMenuDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat case kAboutCmd: _aboutDialog->runModal(); break; + case kHelpCmd: + // Not handled here -- needs to be handled by a subclass (for now) + break; case kRTLCmd: { Common::Event eventRTL; eventRTL.type = Common::EVENT_RTL; @@ -263,13 +274,13 @@ enum { // "" as value for the domain, and in fact provide a somewhat better user // experience at the same time. ConfigDialog::ConfigDialog(bool subtitleControls) - : GUI::OptionsDialog("", "ScummConfig") { + : GUI::OptionsDialog("", "GlobalConfig") { // // Sound controllers // - addVolumeControls(this, "ScummConfig."); + addVolumeControls(this, "GlobalConfig."); setVolumeSettingsState(true); // could disable controls by GUI options // @@ -278,7 +289,7 @@ ConfigDialog::ConfigDialog(bool subtitleControls) if (subtitleControls) { // Global talkspeed range of 0-255 - addSubtitleControls(this, "ScummConfig.", 255); + addSubtitleControls(this, "GlobalConfig.", 255); setSubtitleSettingsState(true); // could disable controls by GUI options } @@ -286,11 +297,11 @@ ConfigDialog::ConfigDialog(bool subtitleControls) // Add the buttons // - new GUI::ButtonWidget(this, "ScummConfig.Ok", "OK", GUI::kOKCmd, 'O'); - new GUI::ButtonWidget(this, "ScummConfig.Cancel", "Cancel", GUI::kCloseCmd, 'C'); + new GUI::ButtonWidget(this, "GlobalConfig.Ok", "OK", GUI::kOKCmd, 'O'); + new GUI::ButtonWidget(this, "GlobalConfig.Cancel", "Cancel", GUI::kCloseCmd, 'C'); #ifdef SMALL_SCREEN_DEVICE - new GUI::ButtonWidget(this, "ScummConfig.Keys", "Keys", kKeysCmd, 'K'); + new GUI::ButtonWidget(this, "GlobalConfig.Keys", "Keys", kKeysCmd, 'K'); _keysDialog = NULL; #endif } diff --git a/engines/dialogs.h b/engines/dialogs.h index 6bee7c5fb1..6e5338b317 100644 --- a/engines/dialogs.h +++ b/engines/dialogs.h @@ -27,7 +27,6 @@ #include "common/str.h" #include "gui/dialog.h" -#include "gui/options.h" class Engine; @@ -39,6 +38,19 @@ namespace GUI { class MainMenuDialog : public GUI::Dialog { public: + enum { + kSaveCmd = 'SAVE', + kLoadCmd = 'LOAD', + kPlayCmd = 'PLAY', + kOptionsCmd = 'OPTN', + kHelpCmd = 'HELP', + kAboutCmd = 'ABOU', + kQuitCmd = 'QUIT', + kRTLCmd = 'RTL ', + kChooseCmd = 'CHOS' + }; + +public: MainMenuDialog(Engine *engine); ~MainMenuDialog(); @@ -51,29 +63,20 @@ protected: void load(); protected: - Engine *_engine; + Engine *_engine; - GUI::GraphicsWidget *_logo; - GUI::ButtonWidget *_rtlButton; - GUI::ButtonWidget *_loadButton; - GUI::ButtonWidget *_saveButton; - GUI::Dialog *_aboutDialog; - GUI::Dialog *_optionsDialog; - GUI::SaveLoadChooser *_loadDialog; - GUI::SaveLoadChooser *_saveDialog; -}; + GUI::GraphicsWidget *_logo; -class ConfigDialog : public GUI::OptionsDialog { -protected: -#ifdef SMALL_SCREEN_DEVICE - GUI::Dialog *_keysDialog; -#endif + GUI::ButtonWidget *_rtlButton; + GUI::ButtonWidget *_loadButton; + GUI::ButtonWidget *_saveButton; + GUI::ButtonWidget *_helpButton; -public: - ConfigDialog(bool subtitleControls); - ~ConfigDialog(); + GUI::Dialog *_aboutDialog; + GUI::Dialog *_optionsDialog; - virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); + GUI::SaveLoadChooser *_loadDialog; + GUI::SaveLoadChooser *_saveDialog; }; #endif diff --git a/engines/drascula/animation.cpp b/engines/drascula/animation.cpp index 4a0b82d746..e4bd844d75 100644 --- a/engines/drascula/animation.cpp +++ b/engines/drascula/animation.cpp @@ -1602,7 +1602,7 @@ void DrasculaEngine::animation_9_6() { // We set the room number to -1 for the same purpose. // Also check animation_2_1(), where the same hack was used // by the original - roomNumber = -1; + roomNumber = -2; loadPic("nota2.alg", bgSurface, HALF_PAL); black(); trackProtagonist = 1; diff --git a/engines/drascula/drascula.cpp b/engines/drascula/drascula.cpp index 276554a24c..2c3ca63600 100644 --- a/engines/drascula/drascula.cpp +++ b/engines/drascula/drascula.cpp @@ -87,6 +87,7 @@ DrasculaEngine::DrasculaEngine(OSystem *syst, const DrasculaGameDescription *gam _textverbs = 0; _textmisc = 0; _textd1 = 0; + _talkSequences = 0; _color = 0; blinking = 0; diff --git a/engines/drascula/objects.cpp b/engines/drascula/objects.cpp index c4dc3df1f6..13c8a742ca 100644 --- a/engines/drascula/objects.cpp +++ b/engines/drascula/objects.cpp @@ -89,7 +89,8 @@ void DrasculaEngine::gotoObject(int pointX, int pointY) { updateRoom(); updateScreen(); - if (cursorVisible) + // roomNumber -2 is end credits. Do not show cursor there + if (cursorVisible && roomNumber != -2) showCursor(); } diff --git a/engines/m4/animation.cpp b/engines/m4/animation.cpp index fe46e121f0..1142ba48d1 100644 --- a/engines/m4/animation.cpp +++ b/engines/m4/animation.cpp @@ -26,181 +26,463 @@ #include "m4/assets.h" #include "m4/animation.h" #include "m4/compression.h" +#include "m4/mads_scene.h" namespace M4 { // TODO: this code needs cleanup -Animation::Animation(MadsM4Engine *vm) { - _vm = vm; - _playing = false; +MadsAnimation::MadsAnimation(MadsM4Engine *vm, MadsView *view): Animation(vm), _view(view) { + _font = NULL; + _resetFlag = false; + _freeFlag = false; + _skipLoad = false; + _unkIndex = -1; + _messageCtr= 0; + _field12 = 0; + + _currentFrame = 0; + _oldFrameEntry = 0; + _nextFrameTimer = _madsVm->_currentTimer; } -void Animation::loadFullScreen(const char *filename) { - _vm->_palette->deleteAllRanges(); - load(filename); +MadsAnimation::~MadsAnimation() { + for (uint i = 0; i < _messages.size(); ++i) { + if (_messages[i].kernelMsgIndex >= 0) + _view->_kernelMessages.remove(_messages[i].kernelMsgIndex); + } + + // Further deletion logic + if (_field12) { + _view->_spriteSlots.deleteSprites(_spriteListIndexes[_spriteListIndex]); + } + + delete _font; } -void Animation::load(const char *filename) { - MadsPack anim(filename, _vm); +/** + * Initialises and loads the data of an animation + */ +void MadsAnimation::initialise(const Common::String &filename, uint16 flags, M4Surface *interfaceSurface, M4Surface *sceneSurface) { + MadsPack anim(filename.c_str(), _vm); + bool madsRes = filename[0] == '*'; char buffer[20]; + int streamIndex = 1; // Chunk 1: header // header - // TODO: there are some unknown fields here, plus we don't read - // the entire chunk + Common::SeekableReadStream *animStream = anim.getItemStream(0); - Common::SeekableReadStream *spriteSeriesStream; - //printf("Chunk 0, size %i\n", animStream->size()); - _seriesCount = animStream->readUint16LE(); - _frameCount = animStream->readUint16LE(); - _frameEntryCount = animStream->readUint16LE(); - // Unknown - for (int i = 0; i < 43; i++) - animStream->readByte(); + int spriteListCount = animStream->readUint16LE(); + int miscEntriesCount = animStream->readUint16LE(); + int frameEntryCount = animStream->readUint16LE(); + int messagesCount = animStream->readUint16LE(); + animStream->skip(1); + _flags = animStream->readByte(); + + animStream->skip(2); + _animMode = animStream->readUint16LE(); + _roomNumber = animStream->readUint16LE(); + animStream->skip(2); + _field12 = animStream->readUint16LE() != 0; + _spriteListIndex = animStream->readUint16LE(); + _scrollX = animStream->readUint16LE(); + _scrollY = animStream->readSint16LE(); + animStream->skip(10); + + animStream->read(buffer, 13); + _interfaceFile = Common::String(buffer, 13); + + for (int i = 0; i < 10; ++i) { + animStream->read(buffer, 13); + _spriteSetNames[i] = Common::String(buffer, 13); + } - _spriteSeriesNames = new Common::String[_seriesCount]; - printf("%i sprite series\n", _seriesCount); + animStream->skip(81); + animStream->read(buffer, 13); + _lbmFilename = Common::String(buffer, 13); + animStream->read(buffer, 13); + _spritesFilename = Common::String(buffer, 13); + animStream->skip(48); + animStream->read(buffer, 13); + _soundName = Common::String(buffer, 13); + animStream->skip(26); + animStream->read(buffer, 13); + Common::String fontResource(buffer, 13); + + if (_animMode == 4) + flags |= 0x4000; + if (flags & 0x100) + loadInterface(interfaceSurface, sceneSurface); + + // Initialise the reference list + for (int i = 0; i < spriteListCount; ++i) + _spriteListIndexes.push_back(-1); - // TODO: for now, we only load the first sprite series - if (_seriesCount > 1) - printf("TODO: Anim has %i sprite series, for now, we only load the first one\n", _seriesCount); - _seriesCount = 1; // TODO + delete animStream; - for (int i = 0; i < _seriesCount; i++) { - animStream->read(buffer, 13); - _spriteSeriesNames[i] = Common::String(buffer); - //printf("%03d: %s\n", i, _spriteSeriesNames[i].c_str()); - - spriteSeriesStream = _vm->res()->get(_spriteSeriesNames[i].c_str()); - _spriteSeries = new SpriteAsset(_vm, spriteSeriesStream, - spriteSeriesStream->size(), _spriteSeriesNames[i].c_str()); - _vm->res()->toss(_spriteSeriesNames[i].c_str()); - - // Adjust the palette of the sprites in the sprite series - // so that they can be displayed on screen correctly - RGBList *palData = new RGBList(_spriteSeries->getColorCount(), _spriteSeries->getPalette(), true); - _vm->_palette->addRange(palData); - - for (int k = 0; k < _spriteSeries->getCount(); k++) { - M4Sprite *spr = _spriteSeries->getFrame(k); - spr->translate(palData); // sprite pixel translation + if (messagesCount > 0) { + // Chunk 2 + // Following is a list of any messages for the animation + + animStream = anim.getItemStream(streamIndex++); + + for (int i = 0; i < messagesCount; ++i) { + AnimMessage rec; + animStream->read(rec.msg, 70); + rec.pos.x = animStream->readUint16LE(); + rec.pos.y = animStream->readUint16LE(); + animStream->readUint16LE(); + rec.rgb1.r = animStream->readByte(); + rec.rgb1.g = animStream->readByte(); + rec.rgb1.b = animStream->readByte(); + rec.rgb2.r = animStream->readByte(); + rec.rgb2.g = animStream->readByte(); + rec.rgb2.b = animStream->readByte(); + rec.kernelMsgIndex = animStream->readUint16LE(); + animStream->skip(6); + rec.startFrame = animStream->readUint16LE(); + rec.endFrame = animStream->readUint16LE(); + animStream->readUint16LE(); + + _messages.push_back(rec); } + + delete animStream; } - //printf("End pos: %i\n", animStream->pos()); + if (frameEntryCount > 0) { + // Chunk 3: animation frame info + animStream = anim.getItemStream(streamIndex++); + + for (int i = 0; i < frameEntryCount; i++) { + AnimFrameEntry rec; + rec.frameNumber = animStream->readUint16LE(); + rec.seqIndex = animStream->readByte(); + rec.spriteSlot.spriteListIndex = animStream->readByte(); + rec.spriteSlot.frameNumber = animStream->readUint16LE(); + rec.spriteSlot.xp = animStream->readUint16LE(); + rec.spriteSlot.yp = animStream->readUint16LE(); + rec.spriteSlot.depth = animStream->readByte(); + rec.spriteSlot.scale = (int8)animStream->readByte(); + + _frameEntries.push_back(rec); + } - delete animStream; + delete animStream; + } - // ------------------ - - // Chunk 2: anim info - AnimationFrame frame; - animStream = anim.getItemStream(1); - //printf("Chunk 1, size %i\n", animStream->size()); - - _frameEntries = new AnimationFrame[_frameEntryCount]; - - for (int i = 0; i < _frameEntryCount; i++) { - - frame.animFrameIndex = animStream->readUint16LE(); - frame.u = animStream->readByte(); - frame.seriesIndex = animStream->readByte(); - frame.seriesFrameIndex = animStream->readUint16LE(); - frame.x = animStream->readUint16LE(); - frame.y = animStream->readUint16LE(); - frame.v = animStream->readByte(); - frame.w = animStream->readByte(); - - _frameEntries[i] = frame; - - /* - printf( - "animFrameIndex = %4d, " - "u = %3d, " - "seriesIndex = %3d, " - "seriesFrameIndex = %6d, " - "x = %3d, " - "y = %3d, " - "v = %3d, " - "w = %3d\n", - - frame.animFrameIndex, - frame.u, - frame.seriesIndex, - frame.seriesFrameIndex, - frame.x, - frame.y, - frame.v, - frame.w - ); - */ - } - //printf("End pos: %i\n", animStream->pos()); + if (miscEntriesCount > 0) { + // Chunk 4: Misc Data + animStream = anim.getItemStream(streamIndex); - delete animStream; + for (int i = 0; i < miscEntriesCount; ++i) { + AnimMiscEntry rec; + rec.soundNum = animStream->readByte(); + animStream->skip(1); + rec.numTicks = animStream->readUint16LE(); + rec.posAdjust.x = animStream->readUint16LE(); + rec.posAdjust.y = animStream->readUint16LE(); + animStream->readUint16LE(); - // Chunk 3: unknown (seems to be sound data?) - // TODO -} + _miscEntries.push_back(rec); + } + + delete animStream; + } + + // If the animation specifies a font, then load it for access + if (_flags & ANIM_CUSTOM_FONT) { + Common::String fontName; + if (madsRes) + fontName += "*"; + fontName += fontResource; -Animation::~Animation() { - //delete[] _spriteSeriesNames; - //delete[] _spriteSeries; - //delete[] _frameEntries; + _font = _vm->_font->getFont(fontName); + } + + // Load all the sprite sets for the animation + for (int i = 0; i < spriteListCount; ++i) { + if (_field12 && (i == _spriteListIndex)) + // Skip over field, since it's manually loaded + continue; + + _spriteListIndexes[i] = _view->_spriteSlots.addSprites(_spriteSetNames[i].c_str()); + } + + + if (_field12) { + Common::String resName; + if (madsRes) + resName += "*"; + resName += _spriteSetNames[_spriteListIndex]; + + _spriteListIndexes[_spriteListIndex] = _view->_spriteSlots.addSprites(resName.c_str()); + } + + // TODO: Unknown section about handling sprite set list combined with messages size + + // TODO: The original has two separate loops for the loop below based on _animMode == 4. Is it + // perhaps that in that mode the sprite frames has a different format..? + + // Remap the sprite list index fields from the initial value to the indexes of the loaded + // sprite sets for the animation + for (uint i = 0; i < _frameEntries.size(); ++i) { + int idx = _frameEntries[i].spriteSlot.spriteListIndex; + _frameEntries[i].spriteSlot.spriteListIndex = _spriteListIndexes[idx]; + } } -void Animation::start() { - _curFrame = 0; - _curFrameEntry = 0; - //for (int i = 0; i < _seriesCount; i++) { - //_spriteSeries[i] = new SpriteSeries((char*)_spriteSeriesNames[i].c_str()); - //} - _playing = true; - updateAnim(); +/** + * Loads an animation file for display + */ +void MadsAnimation::load(const Common::String &filename, int abortTimers) { + initialise(filename, 0, NULL, NULL); + _messageCtr = 0; + _skipLoad = true; + +/* TODO: figure out extra stuff in this routine + if (_field12) { + _unkIndex = -1; + int listIndex = _spriteListIndexes[_spriteListIndex]; + SpriteAsset &spriteSet = _view->_spriteSlots.getSprite(listIndex); + ..?.. + } +*/ + + // Initialise miscellaneous fields + _currentFrame = 0; + _oldFrameEntry = 0; + _nextFrameTimer = _madsVm->_currentTimer; + _abortTimers = abortTimers; + _abortMode = _madsVm->scene()->_abortTimersMode2; + + for (int i = 0; i < 3; ++i) + _actionNouns[i] = _madsVm->scene()->actionNouns[i]; + + // Initialise kernel message list + for (uint i = 0; i < _messages.size(); ++i) + _messages[i].kernelMsgIndex = -1; } -bool Animation::updateAnim() { - if (!_playing) - return true; +void MadsAnimation::update() { + if (_field12) { + int spriteListIndex = _spriteListIndexes[_spriteListIndex]; + int newIndex = -1; + + for (uint idx = _oldFrameEntry; idx < _frameEntries.size(); ++idx) { + if (_frameEntries[idx].frameNumber > _currentFrame) + break; + if (_frameEntries[idx].spriteSlot.spriteListIndex == spriteListIndex) + newIndex = _frameEntries[idx].spriteSlot.frameNumber; + } + + if (newIndex >= 0) + load1(newIndex); + } + + // If it's not time for the next frame, then exit + if (_madsVm->_currentTimer < _nextFrameTimer) + return; - // Get the scene background surface - M4Surface *bg = _vm->_scene->getBackgroundSurface(); + // Loop checks for any prior animation sprite slots to be expired + for (int slotIndex = 0; slotIndex < _view->_spriteSlots.startIndex; ++slotIndex) { + if (_view->_spriteSlots[slotIndex].seqIndex >= 0x80) { + // Flag the frame as animation sprite slot + _view->_spriteSlots[slotIndex].spriteType = EXPIRED_SPRITE; + } + } - while (_frameEntries[_curFrameEntry].animFrameIndex == _curFrame) { - AnimationFrame *frame = &_frameEntries[_curFrameEntry]; - int seriesFrameIndex = (frame->seriesFrameIndex & 0x7FFF) - 1; + // Validate the current frame + if (_currentFrame >= (int)_miscEntries.size()) { + // Is the animation allowed to be repeated? + if (_resetFlag) { + _currentFrame = 0; + _oldFrameEntry = 0; + } else { + _freeFlag = true; + return; + } + } - // Write the sprite onto the screen - M4Sprite *spr = _spriteSeries->getFrame(seriesFrameIndex); + // Handle starting any sound for this frame + AnimMiscEntry &misc = _miscEntries[_currentFrame]; + if (misc.soundNum) + _vm->_sound->playSound(misc.soundNum); - // FIXME: correct x, y - spr->copyTo(bg, frame->x, frame->y, (int)spr->getTransparentColor()); + bool screenChanged = false; - // HACK: wait a bit - g_system->delayMillis(100); + // Handle any scrolling of the screen surface + if ((_scrollX != 0) || (_scrollY != 0)) { + _view->_bgSurface->scrollX(_scrollX); + _view->_bgSurface->scrollY(_scrollY); - //printf("_curFrameEntry = %d\n", _curFrameEntry); - _curFrameEntry++; + screenChanged = true; } - //printf("_curFrame = %d\n", _curFrame); + // Handle any offset adjustment for sprites as of this frame + if (_view->_posAdjust.x != misc.posAdjust.x) { + misc.posAdjust.x = _view->_posAdjust.x; + screenChanged = true; + } + if (_view->_posAdjust.y != misc.posAdjust.y) { + misc.posAdjust.y = _view->_posAdjust.y; + screenChanged = true; + } + if (screenChanged) { + // Signal the entire screen needs refreshing + _view->_spriteSlots.fullRefresh(); + } - _curFrame++; - if (_curFrame >= _frameCount) // anim done - stop(); + int spriteSlotsMax = _view->_spriteSlots.startIndex; + + // Main frame animation loop - frames get animated by being placed, as necessary, into the + // main sprite slot array + while ((uint)_oldFrameEntry < _frameEntries.size()) { + if (_frameEntries[_oldFrameEntry].frameNumber > _currentFrame) + break; + else if (_frameEntries[_oldFrameEntry].frameNumber == _currentFrame) { + // Found the correct frame + int spriteSlotIndex = 0; + int index = 0; + + for (;;) { + if ((spriteSlotIndex == 0) && (index < spriteSlotsMax)) { + int seqIndex = _frameEntries[_oldFrameEntry].seqIndex - _view->_spriteSlots[index].seqIndex; + if (seqIndex == 0x80) { + if (_view->_spriteSlots[index] == _frameEntries[_oldFrameEntry].spriteSlot) { + _view->_spriteSlots[index].spriteType = SPRITE_ZERO; + spriteSlotIndex = -1; + } + } + ++index; + continue; + } + + if (spriteSlotIndex == 0) { + int slotIndex = _view->_spriteSlots.getIndex(); + MadsSpriteSlot &slot = _view->_spriteSlots[slotIndex]; + slot.copy(_frameEntries[_oldFrameEntry].spriteSlot); + slot.seqIndex = _frameEntries[_oldFrameEntry].seqIndex + 0x80; + + SpriteAsset &spriteSet = _view->_spriteSlots.getSprite( + _view->_spriteSlots[slotIndex].spriteListIndex); + slot.spriteType = spriteSet.isBackground() ? BACKGROUND_SPRITE : FOREGROUND_SPRITE; + } + break; + } + } + + ++_oldFrameEntry; + } + + // Handle the display of any messages + for (uint idx = 0; idx < _messages.size(); ++idx) { + if (_messages[idx].kernelMsgIndex >= 0) { + // Handle currently active message + if ((_currentFrame < _messages[idx].startFrame) || (_currentFrame > _messages[idx].endFrame)) { + _view->_kernelMessages.remove(_messages[idx].kernelMsgIndex); + _messages[idx].kernelMsgIndex = -1; + --_messageCtr; + } + } else if ((_currentFrame >= _messages[idx].startFrame) && (_currentFrame <= _messages[idx].endFrame)) { + // Start displaying the message + AnimMessage &me = _messages[idx]; + + // The colour index to use is dependant on how many messages are currently on-screen + uint8 colIndex; + switch (_messageCtr) { + case 1: + colIndex = 252; + break; + case 2: + colIndex = 16; + break; + default: + colIndex = 250; + break; + } + + _vm->_palette->setEntry(colIndex, me.rgb1.r, me.rgb1.g, me.rgb1.b); + _vm->_palette->setEntry(colIndex + 1, me.rgb2.r, me.rgb2.g, me.rgb2.b); + + // Add a kernel message to display the given text + me.kernelMsgIndex = _view->_kernelMessages.add(me.pos, colIndex * 101, 0, 0, INDEFINITE_TIMEOUT, me.msg); + ++_messageCtr; + } + } + + // Move to the next frame + _currentFrame++; + if (_currentFrame >= (int)_miscEntries.size()) { + // Animation is complete + if (_abortTimers != 0) { + _view->_abortTimers = _abortTimers; + _view->_abortTimersMode = _abortMode; + + if (_abortMode != ABORTMODE_1) { + // Copy the noun list + for (int i = 0; i < 3; ++i) + _madsVm->scene()->actionNouns[i] = _actionNouns[i]; + } + } + } - return _curFrame >= _frameCount; + int frameNum = MIN(_currentFrame, (int)_miscEntries.size() - 1); + _nextFrameTimer = _madsVm->_currentTimer + _miscEntries[frameNum].numTicks; } -void Animation::stop() { - _playing = false; +void MadsAnimation::setCurrentFrame(int frameNumber) { + _currentFrame = frameNumber; + _oldFrameEntry = 0; + _freeFlag = false; +} + +void MadsAnimation::load1(int frameNumber) { + if (_skipLoad) + return; + + Common::Point pt; + int listIndex = _spriteListIndexes[_spriteListIndex]; + SpriteAsset &spriteSet = _view->_spriteSlots.getSprite(listIndex); + + if (_unkIndex < 0) { + M4Surface *frame = spriteSet.getFrame(0); + pt.x = frame->bounds().left; + pt.y = frame->bounds().top; + } else { + pt.x = _unkList[_unkIndex].x; + pt.y = _unkList[_unkIndex].y; + _unkIndex = 1 - _unkIndex; + } + + if (proc1(spriteSet, pt, frameNumber)) + error("proc1 failure"); +} + +bool MadsAnimation::proc1(SpriteAsset &spriteSet, const Common::Point &pt, int frameNumber) { + return 0; +} - for (int i = 0; i < _seriesCount; i++) { - // TODO: cleanup - //delete _spriteSeries[i]; - //_spriteSeries[i] = NULL; +void MadsAnimation::loadInterface(M4Surface *&interfaceSurface, M4Surface *&depthSurface) { + if (_animMode <= 2) { + MadsSceneResources sceneResources; + sceneResources.load(_roomNumber, _interfaceFile.c_str(), 0, depthSurface, interfaceSurface); + + } else if (_animMode == 4) { + // Load a scene interface + interfaceSurface->madsLoadInterface(_interfaceFile); + } else { + // This mode allocates two large surfaces for the animation + // TODO: Are these ever properly freed? +error("Anim mode %d - need to check free logic", _animMode); + assert(!interfaceSurface); + assert(!depthSurface); + depthSurface = new M4Surface(MADS_SURFACE_WIDTH, MADS_SCREEN_HEIGHT); + interfaceSurface = new M4Surface(MADS_SURFACE_WIDTH, MADS_SCREEN_HEIGHT); + depthSurface->clear(); + interfaceSurface->clear(); } } diff --git a/engines/m4/animation.h b/engines/m4/animation.h index c8be7f5cb3..5c7227a256 100644 --- a/engines/m4/animation.h +++ b/engines/m4/animation.h @@ -29,39 +29,92 @@ #include "m4/m4.h" #include "m4/graphics.h" #include "m4/assets.h" +#include "m4/mads_views.h" +#include "common/array.h" namespace M4 { -struct AnimationFrame { - uint16 animFrameIndex; - byte u; - byte seriesIndex; - uint16 seriesFrameIndex; - uint16 x, y; - byte v, w; +class MadsView; +class SpriteSlotSubset; + +class AnimMessage { +public: + char msg[70]; + Common::Point pos; + RGB8 rgb1, rgb2; + int kernelMsgIndex; + + int startFrame, endFrame; +}; + +class AnimFrameEntry { +public: + int frameNumber; + int seqIndex; + SpriteSlotSubset spriteSlot; }; -class Animation { - public: - Animation(MadsM4Engine *vm); - ~Animation(); - - void load(const char *filename); - void loadFullScreen(const char *filename); - void start(); - bool updateAnim(); - void stop(); - - private: - bool _playing; - MadsM4Engine *_vm; - int _seriesCount; - int _frameCount; - int _frameEntryCount; - AnimationFrame *_frameEntries; - Common::String *_spriteSeriesNames; - SpriteAsset *_spriteSeries; - int _curFrame, _curFrameEntry; +class AnimMiscEntry { +public: + int soundNum; + int numTicks; + Common::Point posAdjust; +}; + +#define ANIM_SPRITE_SET_SIZE 50 + +enum MadsAnimationFlags {ANIM_CUSTOM_FONT = 0x20, ANIM_HAS_SOUND = 0x8000}; + +class MadsAnimation: public Animation { +private: + MadsView *_view; + + int _spriteListCount; + Common::Array<AnimMessage> _messages; + Common::Array<AnimFrameEntry> _frameEntries; + Common::Array<AnimMiscEntry> _miscEntries; + Font *_font; + + uint8 _flags; + int _animMode; + int _roomNumber; + bool _field12; + int _spriteListIndex; + int _scrollX; + int _scrollY; + Common::String _interfaceFile; + Common::String _spriteSetNames[10]; + Common::String _lbmFilename; + Common::String _spritesFilename; + Common::String _soundName; + Common::Array<int> _spriteListIndexes; + + int _currentFrame, _oldFrameEntry; + bool _resetFlag; + bool _freeFlag; + bool _skipLoad; + int _unkIndex; + Common::Point _unkList[2]; + uint32 _nextFrameTimer; + int _messageCtr; + int _abortTimers; + AbortTimerMode _abortMode; + uint16 _actionNouns[3]; + + void load1(int frameNumber); + bool proc1(SpriteAsset &spriteSet, const Common::Point &pt, int frameNumber); + void loadInterface(M4Surface *&interfaceSurface, M4Surface *&depthSurface); +public: + MadsAnimation(MadsM4Engine *vm, MadsView *view); + virtual ~MadsAnimation(); + + virtual void initialise(const Common::String &filename, uint16 flags, M4Surface *interfaceSurface, M4Surface *sceneSurface); + virtual void load(const Common::String &filename, int abortTimers); + virtual void update(); + virtual void setCurrentFrame(int frameNumber); + + bool freeFlag() const { return _freeFlag; } + int roomNumber() const { return _roomNumber; } }; } // End of namespace M4 diff --git a/engines/m4/assets.cpp b/engines/m4/assets.cpp index 14857e6f2b..1f3cf278ae 100644 --- a/engines/m4/assets.cpp +++ b/engines/m4/assets.cpp @@ -30,13 +30,13 @@ namespace M4 { -BaseAsset::BaseAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : _vm(vm) { +BaseAsset::BaseAsset(MadsM4Engine *vm) : _vm(vm) { } BaseAsset::~BaseAsset() { } -MachineAsset::MachineAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm, stream, size, name) { +MachineAsset::MachineAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm) { uint32 stateCount = stream->readUint32LE(); for (uint32 curState = 0; curState < stateCount; curState++) { uint32 stateOffset = stream->readUint32LE(); @@ -61,7 +61,7 @@ uint32 MachineAsset::getStateOffset(uint32 state) { return _stateTable[state]; } -SequenceAsset::SequenceAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm, stream, size, name) { +SequenceAsset::SequenceAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm) { _localVarCount = stream->readUint32LE(); _codeSize = size - 4; _code = new byte[_codeSize]; @@ -78,7 +78,7 @@ void SequenceAsset::getCode(byte *&code, uint32 &codeSize) { } -DataAsset::DataAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm, stream, size, name) { +DataAsset::DataAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm) { _recCount = stream->readUint32LE(); _recSize = stream->readUint32LE(); @@ -98,7 +98,8 @@ long *DataAsset::getRow(int index) { return &_data[_recSize * index]; } -SpriteAsset::SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name, bool asStream) : BaseAsset(vm, stream, size, name) { +SpriteAsset::SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name, bool asStream) : + BaseAsset(vm) { _stream = stream; _palInterface = NULL; _paletteData = NULL; @@ -110,6 +111,20 @@ SpriteAsset::SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, i } } +SpriteAsset::SpriteAsset(MadsM4Engine *vm, const char *name): BaseAsset(vm) { + _stream = vm->res()->get(name); + _palInterface = NULL; + _paletteData = NULL; + + if (_vm->isM4()) { + loadM4SpriteAsset(vm, _stream, true); + } else { + loadMadsSpriteAsset(vm, _stream); + } + + vm->res()->toss(name); +} + SpriteAsset::~SpriteAsset() { if (_palInterface) { // Internally stored palette translation data, so release it @@ -195,11 +210,12 @@ void SpriteAsset::loadMadsSpriteAsset(MadsM4Engine *vm, Common::SeekableReadStre _maxHeight = 0; Common::SeekableReadStream *spriteStream = sprite.getItemStream(0); - - _assetType = spriteStream->readUint16LE(); - for (int i = 0; i < 18; i++) { - spriteStream->readUint16LE(); - } + _mode = spriteStream->readByte(); + spriteStream->skip(1); + int type1 = spriteStream->readUint16LE(); + int type2 = spriteStream->readUint16LE(); + _isBackground = (type1 != 0) && (type2 < 4); + spriteStream->skip(32); _frameCount = spriteStream->readUint16LE(); // we skip the rest of the data delete spriteStream; diff --git a/engines/m4/assets.h b/engines/m4/assets.h index cd0ae6ba78..e5beffbcae 100644 --- a/engines/m4/assets.h +++ b/engines/m4/assets.h @@ -49,7 +49,7 @@ class Palette; class BaseAsset { public: - BaseAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name); + BaseAsset(MadsM4Engine *vm); ~BaseAsset(); const Common::String getName() const { return _name; } protected: @@ -103,6 +103,7 @@ struct SpriteAssetFrame { class SpriteAsset : public BaseAsset { public: SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name, bool asStream = false); + SpriteAsset(MadsM4Engine *vm, const char *name); ~SpriteAsset(); void loadM4SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, bool asStream); void loadMadsSpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream); @@ -113,7 +114,7 @@ public: int32 getFrameHeight(int index); int32 getMaxFrameWidth() const { return _maxWidth; } int32 getMaxFrameHeight() const { return _maxHeight; } - uint16 getAssetType() const { return _assetType; } + bool isBackground() const { return _isBackground; } M4Sprite *getFrame(int frameIndex); void loadStreamingFrame(M4Sprite *frame, int frameIndex, int destX, int destY); RGB8* getPalette() { return _palette; } @@ -136,7 +137,8 @@ protected: uint32 _frameStartOffset; // MADS sprite set fields - uint16 _assetType; + uint8 _mode; + bool _isBackground; int32 parseSprite(bool isBigEndian = false); void loadFrameHeader(SpriteAssetFrame &frameHeader, bool isBigEndian = false); diff --git a/engines/m4/compression.h b/engines/m4/compression.h index 74fed357ff..00e3d1f927 100644 --- a/engines/m4/compression.h +++ b/engines/m4/compression.h @@ -66,8 +66,8 @@ public: class FabDecompressor { private: - int _bitsLeft; - uint32 _bitBuffer; + int _bitsLeft; + uint32 _bitBuffer; const byte *_srcData, *_srcP; int _srcSize; diff --git a/engines/m4/console.cpp b/engines/m4/console.cpp index 0c2e80df0e..4e14afdfaf 100644 --- a/engines/m4/console.cpp +++ b/engines/m4/console.cpp @@ -47,7 +47,6 @@ Console::Console(MadsM4Engine *vm) : GUI::Debugger() { DCmd_Register("start_conv", WRAP_METHOD(Console, cmdStartConversation)); DCmd_Register("textview", WRAP_METHOD(Console, cmdShowTextview)); DCmd_Register("animview", WRAP_METHOD(Console, cmdShowAnimview)); - DCmd_Register("anim", WRAP_METHOD(Console, cmdPlayAnimation)); } Console::~Console() { @@ -247,33 +246,6 @@ bool Console::cmdShowAnimview(int argc, const char **argv) { return false; } -bool Console::cmdPlayAnimation(int argc, const char **argv) { - View *view = _vm->_viewManager->getView(VIEWID_SCENE); - if (view == NULL) { - DebugPrintf("The scene view isn't currently active\n"); - } else if (argc != 2 && argc != 3) { - DebugPrintf("Usage: %s <anim resource (*.aa)> <fullscreen>\n", argv[0]); - DebugPrintf("If fullscreen is 1, the screen palette is replaced with the palette of the animation\n"); - } else { - char resourceName[20]; - strncpy(resourceName, argv[1], 15); - resourceName[15] = '\0'; - if (!strchr(resourceName, '.')) - strcat(resourceName, ".AA"); - - _vm->_viewManager->moveToFront(view); - if (argc == 3 && atoi(argv[2]) == 1) - _vm->_animation->loadFullScreen(resourceName); - else - _vm->_animation->load(resourceName); - _vm->_animation->start(); - view->restore(0, 0, view->width(), view->height()); - return false; - } - - return true; -} - /*--------------------------------------------------------------------------*/ MadsConsole::MadsConsole(MadsEngine *vm): Console(vm) { @@ -282,6 +254,7 @@ MadsConsole::MadsConsole(MadsEngine *vm): Console(vm) { DCmd_Register("object", WRAP_METHOD(MadsConsole, cmdObject)); DCmd_Register("message", WRAP_METHOD(MadsConsole, cmdMessage)); DCmd_Register("scene_info", WRAP_METHOD(MadsConsole, cmdSceneInfo)); + DCmd_Register("anim", WRAP_METHOD(MadsConsole, cmdPlayAnimation)); } bool MadsConsole::cmdObject(int argc, const char **argv) { @@ -386,6 +359,33 @@ bool MadsConsole::cmdSceneInfo(int argc, const char **argv) { return true; } +bool MadsConsole::cmdPlayAnimation(int argc, const char **argv) { + View *view = _vm->_viewManager->getView(VIEWID_SCENE); + if (view == NULL) { + DebugPrintf("The scene view isn't currently active\n"); + } else if (argc != 2 && argc != 3) { + DebugPrintf("Usage: %s <anim resource (*.aa)> <fullscreen>\n", argv[0]); + DebugPrintf("If fullscreen is 1, the screen palette is replaced with the palette of the animation\n"); + } else { + char resourceName[20]; + strncpy(resourceName, argv[1], 15); + resourceName[15] = '\0'; + if (!strchr(resourceName, '.')) + strcat(resourceName, ".AA"); + + _vm->_viewManager->moveToFront(view); + if (argc == 3 && atoi(argv[2]) == 1) + _madsVm->_palette->deleteAllRanges(); + + _madsVm->scene()->_sceneAnimation->load(resourceName, 0); + + view->restore(0, 0, view->width(), view->height()); + return false; + } + + return true; +} + /*--------------------------------------------------------------------------*/ M4Console::M4Console(M4Engine *vm): Console(vm) { diff --git a/engines/m4/console.h b/engines/m4/console.h index b592f041cf..53a47dada9 100644 --- a/engines/m4/console.h +++ b/engines/m4/console.h @@ -50,7 +50,6 @@ private: bool cmdStartConversation(int argc, const char **argv); bool cmdShowTextview(int argc, const char **argv); bool cmdShowAnimview(int argc, const char **argv); - bool cmdPlayAnimation(int argc, const char **argv); public: Console(MadsM4Engine *vm); @@ -64,6 +63,8 @@ private: bool cmdObject(int argc, const char **argv); bool cmdMessage(int argc, const char **argv); bool cmdSceneInfo(int argc, const char **argv); + bool cmdPlayAnimation(int argc, const char **argv); + public: MadsConsole(MadsEngine *vm); virtual ~MadsConsole() {} diff --git a/engines/m4/converse.cpp b/engines/m4/converse.cpp index 746ced5d11..11bc165811 100644 --- a/engines/m4/converse.cpp +++ b/engines/m4/converse.cpp @@ -96,7 +96,7 @@ void ConversationView::setNode(int32 nodeIndex) { _vm->_font->setFont(FONT_CONVERSATION); // TODO: Conversation styles and colors - _vm->_font->setColors(2, 1, 3); + _vm->_font->current()->setColours(2, 1, 3); _currentNodeIndex = nodeIndex; @@ -124,7 +124,7 @@ void ConversationView::setNode(int32 nodeIndex) { } // Figure out the longest string to determine where option highlighting ends - int tempX = _vm->_font->getWidth(node->entries[i]->text, 0) + + int tempX = _vm->_font->current()->getWidth(node->entries[i]->text, 0) + CONV_ENTRIES_X_OFFSET + 10; _xEnd = MAX(_xEnd, tempX); } @@ -163,10 +163,10 @@ void ConversationView::onRefresh(RectList *rects, M4Surface *destSurface) { if (i > CONV_MAX_SHOWN_ENTRIES - 1) break; - _vm->_font->setColor((_highlightedIndex == i) ? CONVERSATION_ENTRY_HIGHLIGHTED : + _vm->_font->current()->setColour((_highlightedIndex == i) ? CONVERSATION_ENTRY_HIGHLIGHTED : CONVERSATION_ENTRY_NORMAL); - _vm->_font->writeString(this, _activeItems[i]->text, CONV_ENTRIES_X_OFFSET, + _vm->_font->current()->writeString(this, _activeItems[i]->text, CONV_ENTRIES_X_OFFSET, CONV_ENTRIES_Y_OFFSET + CONV_ENTRIES_HEIGHT * i, 0, 0); } } diff --git a/engines/m4/dialogs.cpp b/engines/m4/dialogs.cpp index 3af94af262..a7104537f5 100644 --- a/engines/m4/dialogs.cpp +++ b/engines/m4/dialogs.cpp @@ -127,7 +127,7 @@ void Dialog::writeChars(const char *srcLine) { strcat(line, wordStr); lineLen = strlen(line); - lineWidth = _vm->_font->getWidth(line, DIALOG_SPACING); + lineWidth = _vm->_font->current()->getWidth(line, DIALOG_SPACING); if (((_lineX + lineLen) > _widthChars) || ((_widthX + lineWidth) > _dialogWidth)) { incLine(); @@ -146,7 +146,7 @@ void Dialog::writeChars(const char *srcLine) { */ void Dialog::appendText(const char *line) { _lineX += strlen(line); - _widthX += _vm->_font->getWidth(line, DIALOG_SPACING); + _widthX += _vm->_font->current()->getWidth(line, DIALOG_SPACING); strcat(_lines[_lines.size() - 1].data, line); } @@ -158,7 +158,7 @@ void Dialog::addLine(const char *line, bool underlineP) { if ((_widthX > 0) || (_lineX > 0)) incLine(); - int lineWidth = _vm->_font->getWidth(line, DIALOG_SPACING); + int lineWidth = _vm->_font->current()->getWidth(line, DIALOG_SPACING); int lineLen = strlen(line); if ((lineWidth > _dialogWidth) || (lineLen >= _widthChars)) @@ -383,7 +383,7 @@ Dialog::Dialog(MadsM4Engine *vm, const char *msgData, const char *title): View(v if (id > 0) { // Suffix provided - specifies the dialog width in number of chars _widthChars = id * 2; - _dialogWidth = id * (_vm->_font->getMaxWidth() + DIALOG_SPACING) + 10; + _dialogWidth = id * (_vm->_font->current()->getMaxWidth() + DIALOG_SPACING) + 10; } } else if (matchCommand(cmdText, "UNDER")) { @@ -416,7 +416,7 @@ Dialog::Dialog(MadsM4Engine *vm, const char *msgData, const char *title): View(v Dialog::Dialog(MadsM4Engine *vm, int widthChars): View(vm, Common::Rect(0, 0, 0, 0)) { _vm->_font->setFont(FONT_INTERFACE_MADS); _widthChars = widthChars * 2; - _dialogWidth = widthChars * (_vm->_font->getMaxWidth() + DIALOG_SPACING) + 10; + _dialogWidth = widthChars * (_vm->_font->current()->getMaxWidth() + DIALOG_SPACING) + 10; _screenType = LAYER_DIALOG; _lineX = 0; _widthX = 0; @@ -439,7 +439,7 @@ void Dialog::draw() { // Calculate bounds int dlgWidth = _dialogWidth; - int dlgHeight = _lines.size() * (_vm->_font->getHeight() + 1) + 10; + int dlgHeight = _lines.size() * (_vm->_font->current()->getHeight() + 1) + 10; int dialogX = (_vm->_screen->width() - dlgWidth) / 2; int dialogY = (_vm->_screen->height() - dlgHeight) / 2; @@ -480,26 +480,26 @@ void Dialog::draw() { } // Handle drawing the text contents - _vm->_font->setColours(7, 7, 7); + _vm->_font->current()->setColours(7, 7, 7); setColour(7); - for (uint lineCtr = 0, yp = 5; lineCtr < _lines.size(); ++lineCtr, yp += _vm->_font->getHeight() + 1) { + for (uint lineCtr = 0, yp = 5; lineCtr < _lines.size(); ++lineCtr, yp += _vm->_font->current()->getHeight() + 1) { if (_lines[lineCtr].barLine) { // Bar separation line - hLine(5, width() - 6, ((_vm->_font->getHeight() + 1) >> 1) + yp); + hLine(5, width() - 6, ((_vm->_font->current()->getHeight() + 1) >> 1) + yp); } else { // Standard line Common::Point pt(_lines[lineCtr].xp + 5, yp); if (_lines[lineCtr].xp & 0x40) ++pt.y; - _vm->_font->writeString(this, _lines[lineCtr].data, pt.x, pt.y, 0, DIALOG_SPACING); + _vm->_font->current()->writeString(this, _lines[lineCtr].data, pt.x, pt.y, 0, DIALOG_SPACING); if (_lines[lineCtr].underline) // Underline needed - hLine(pt.x, pt.x + _vm->_font->getWidth(_lines[lineCtr].data, DIALOG_SPACING), - pt.y + _vm->_font->getHeight()); + hLine(pt.x, pt.x + _vm->_font->current()->getWidth(_lines[lineCtr].data, DIALOG_SPACING), + pt.y + _vm->_font->current()->getHeight()); } } @@ -528,7 +528,7 @@ void Dialog::display(MadsM4Engine *vm, int widthChars, const char **descEntries) dlg->incLine(); dlg->writeChars(*descEntries); - int lineWidth = vm->_font->getWidth(*descEntries, DIALOG_SPACING); + int lineWidth = vm->_font->current()->getWidth(*descEntries, DIALOG_SPACING); dlg->_lines[dlg->_lines.size() - 1].xp = (dlg->_dialogWidth - 10 - lineWidth) / 2; ++descEntries; } diff --git a/engines/m4/font.cpp b/engines/m4/font.cpp index f8dec65412..4afa158976 100644 --- a/engines/m4/font.cpp +++ b/engines/m4/font.cpp @@ -29,28 +29,46 @@ namespace M4 { -Font::Font(MadsM4Engine *vm) : _vm(vm) { +FontManager::~FontManager() { + for (uint i = 0; i < _entries.size(); ++i) + delete _entries[i]; + _entries.clear(); +} + +Font *FontManager::getFont(const Common::String &filename) { + // Check if the font is already loaded + for (uint i = 0; i < _entries.size(); ++i) + { + if (_entries[i]->_filename.equals(filename)) + return _entries[i]; + } + + Font *f = new Font(_vm, filename); + _entries.push_back(f); + return f; +} + +void FontManager::setFont(const Common::String &filename) { + _currentFont = getFont(filename); +} + +//-------------------------------------------------------------------------- + +Font::Font(MadsM4Engine *vm, const Common::String &filename) : _vm(vm), _filename(filename) { _sysFont = true; - _filename = NULL; + //TODO: System font _fontColors[0] = _vm->_palette->BLACK; _fontColors[1] = _vm->_palette->WHITE; _fontColors[2] = _vm->_palette->BLACK; _fontColors[3] = _vm->_palette->DARK_GRAY; -} - -void Font::setFont(const char *filename) { - if ((_filename != NULL) && (strcmp(filename, _filename) == 0)) - // Already using specified font, so don't bother reloading - return; _sysFont = false; - _filename = filename; if (_vm->isM4()) - setFontM4(filename); + setFontM4(filename.c_str()); else - setFontMads(filename); + setFontMads(filename.c_str()); } void Font::setFontM4(const char *filename) { @@ -134,20 +152,21 @@ Font::~Font() { } } -void Font::setColor(uint8 color) { +void Font::setColour(uint8 colour) { if (_sysFont) - _fontColors[1] = color; + _fontColors[1] = colour; else - _fontColors[3] = color; + _fontColors[3] = colour; } -void Font::setColors(uint8 alt1, uint8 alt2, uint8 foreground) { +void Font::setColours(uint8 col1, uint8 col2, uint8 col3) { if (_sysFont) - _fontColors[1] = foreground; + _fontColors[1] = col3; else { - _fontColors[1] = alt1; - _fontColors[2] = alt2; - _fontColors[3] = foreground; + _fontColors[0] = 0xFF; + _fontColors[1] = col1; + _fontColors[2] = col2; + _fontColors[3] = col3; } } diff --git a/engines/m4/font.h b/engines/m4/font.h index e64f80b70d..ca47848c61 100644 --- a/engines/m4/font.h +++ b/engines/m4/font.h @@ -59,19 +59,11 @@ namespace M4 { class Font { public: - Font(MadsM4Engine *vm); + Font(MadsM4Engine *vm, const Common::String &filename); ~Font(); - Font *getFont(const char *filename) { - // TODO: Proper separation of font instances - setFont(filename); - return this; - } - void setFont(const char *filename); - void setColor(uint8 color); - void setColors(uint8 alt1, uint8 alt2, uint8 foreground); - void setColour(uint8 colour) { setColor(colour); } - void setColours(uint8 alt1, uint8 alt2, uint8 foreground) { setColors(alt1, alt2, foreground); } + void setColour(uint8 colour); + void setColours(uint8 col1, uint8 col2, uint8 col3); int32 getWidth(const char *text, int spaceWidth = -1); int32 getHeight() const { return _maxHeight; } @@ -80,7 +72,8 @@ public: int32 writeString(M4Surface *surface, const char *text, int x, int y, int width = 0, int spaceWidth = -1) { return write(surface, text, x, y, width, spaceWidth, _fontColors); } - +public: + const Common::String _filename; private: void setFontM4(const char *filename); void setFontMads(const char *filename); @@ -91,10 +84,39 @@ private: uint16 *_charOffs; uint8 *_charData; bool _sysFont; - const char *_filename; uint8 _fontColors[4]; }; +class FontEntry { +public: + Font *_font; + + FontEntry() { + _font = NULL; + } + ~FontEntry() { + delete _font; + } +}; + +class FontManager { +private: + MadsM4Engine *_vm; + Common::Array<Font *> _entries; + Font *_currentFont; +public: + FontManager(MadsM4Engine *vm): _vm(vm) { _currentFont = NULL; } + ~FontManager(); + + Font *getFont(const Common::String &filename); + void setFont(const Common::String &filename); + + Font *current() { + assert(_currentFont); + return _currentFont; + } +}; + } // End of namespace M4 #endif diff --git a/engines/m4/graphics.cpp b/engines/m4/graphics.cpp index fa0cd7ccd3..8624f18da1 100644 --- a/engines/m4/graphics.cpp +++ b/engines/m4/graphics.cpp @@ -69,6 +69,13 @@ void RGBList::setRange(int start, int count, const RGB8 *src) { #define VGA_COLOR_TRANS(x) (x == 0x3f ? 255 : x << 2) +M4Surface::~M4Surface() { + if (_rgbList) { + _madsVm->_palette->deleteRange(_rgbList); + delete _rgbList; + } +} + void M4Surface::loadCodesM4(Common::SeekableReadStream *source) { if (!source) { free(); @@ -472,9 +479,18 @@ void M4Surface::loadBackground(int sceneNumber, RGBList **palData) { if (_vm->getGameType() == GType_RexNebular) { // Load Rex Nebular screen + bool hasPalette = palData != NULL; + if (!hasPalette) + palData = &_rgbList; + sprintf(resourceName, "rm%d.art", sceneNumber); stream = _vm->_resourceManager->get(resourceName); rexLoadBackground(stream, palData); + + if (!hasPalette) { + _vm->_palette->addRange(_rgbList); + this->translate(_rgbList); + } } else { // Loads M4 game scene if (palData) @@ -617,16 +633,6 @@ void M4Surface::rexLoadBackground(Common::SeekableReadStream *source, RGBList ** int sceneWidth = sourceUnc->readUint16LE(); int sceneHeight = sourceUnc->readUint16LE(); int sceneSize = sceneWidth * sceneHeight; - if (sceneWidth > this->width()) { - warning("Background width is %i, too large to fit in screen. Setting it to %i", sceneWidth, this->width()); - sceneWidth = this->width(); - sceneSize = sceneWidth * sceneHeight; - } - if (sceneHeight > this->height()) { - warning("Background height is %i, too large to fit in screen.Setting it to %i", sceneHeight, this->height()); - sceneHeight = this->height(); - sceneSize = sceneWidth * sceneHeight; - } // Set palette if (!palData) { @@ -642,6 +648,7 @@ void M4Surface::rexLoadBackground(Common::SeekableReadStream *source, RGBList ** sourceUnc = packData.getItemStream(1); assert((int)sourceUnc->size() >= sceneSize); + create(sceneWidth, sceneHeight, 1); byte *pData = (byte *)pixels; sourceUnc->read(pData, sceneSize); @@ -711,10 +718,8 @@ void M4Surface::m4LoadBackground(Common::SeekableReadStream *source) { delete tileBuffer; } -void M4Surface::madsloadInterface(int index, RGBList **palData) { - char resourceName[20]; - sprintf(resourceName, "i%d.int", index); - MadsPack intFile(resourceName, _vm); +void M4Surface::madsLoadInterface(const Common::String &filename) { + MadsPack intFile(filename.c_str(), _vm); RGB8 *palette = new RGB8[16]; // Chunk 0, palette @@ -728,7 +733,7 @@ void M4Surface::madsloadInterface(int index, RGBList **palData) { intStream->readByte(); intStream->readByte(); } - *palData = new RGBList(16, palette, true); + _rgbList = new RGBList(16, palette, true); delete intStream; // Chunk 1, data @@ -736,8 +741,77 @@ void M4Surface::madsloadInterface(int index, RGBList **palData) { create(320, 44, 1); intStream->read(pixels, 320 * 44); delete intStream; + + // Translate the interface palette + _vm->_palette->addRange(_rgbList); + this->translate(_rgbList); } +void M4Surface::scrollX(int xAmount) { + if (xAmount == 0) + return; + + byte buffer[80]; + int direction = (xAmount > 0) ? 1 : -1; + int xSize = ABS(xAmount); + assert(xSize <= 80); + + byte *srcP = (byte *)getBasePtr(0, 0); + + for (int y = 0; y < height(); ++y, srcP += pitch) { + if (direction < 0) { + // Copy area to be overwritten + Common::copy(srcP, srcP + xSize, &buffer[0]); + // Shift the remainder of the line over the given area + Common::copy(srcP + xSize, srcP + width(), srcP); + // Move buffered area to the end of the line + Common::copy(&buffer[0], &buffer[xSize], srcP + width() - xSize); + } else { + // Copy area to be overwritten + Common::copy_backward(srcP + width() - xSize, srcP + width(), &buffer[80]); + // Shift the remainder of the line over the given area + Common::copy_backward(srcP, srcP + width() - xSize, srcP + width()); + // Move buffered area to the start of the line + Common::copy_backward(&buffer[80 - xSize], &buffer[80], srcP + xSize); + } + } +} + +void M4Surface::scrollY(int yAmount) { + if (yAmount == 0) + return; + + int direction = (yAmount > 0) ? 1 : -1; + int ySize = ABS(yAmount); + assert(ySize < (height() / 2)); + assert(width() == pitch); + + int blockSize = ySize * width(); + byte *tempData = (byte *)malloc(blockSize); + byte *pixelsP = (byte *)getBasePtr(0, 0); + + if (direction > 0) { + // Buffer the lines to be overwritten + byte *srcP = (byte *)getBasePtr(0, height() - ySize); + Common::copy(srcP, srcP + (pitch * ySize), tempData); + // Vertically shift all the lines + Common::copy_backward(pixelsP, pixelsP + (pitch * (height() - ySize)), + pixelsP + (pitch * height())); + // Transfer the buffered lines top the top of the screen + Common::copy(tempData, tempData + blockSize, pixelsP); + } else { + // Buffer the lines to be overwritten + Common::copy(pixelsP, pixelsP + (pitch * ySize), tempData); + // Vertically shift all the lines + Common::copy(pixelsP + (pitch * ySize), pixelsP + (pitch * height()), pixelsP); + // Transfer the buffered lines to the bottom of the screen + Common::copy(tempData, tempData + blockSize, pixelsP + (pitch * (height() - ySize))); + } + + ::free(tempData); +} + + void M4Surface::translate(RGBList *list, bool isTransparent) { byte *p = getBasePtr(0, 0); byte *palIndexes = list->palIndexes(); diff --git a/engines/m4/graphics.h b/engines/m4/graphics.h index 6d0a82ad25..8c4b9ac072 100644 --- a/engines/m4/graphics.h +++ b/engines/m4/graphics.h @@ -35,6 +35,12 @@ namespace M4 { +#define MADS_SURFACE_WIDTH 320 +#define MADS_SURFACE_HEIGHT 156 +#define MADS_SCREEN_HEIGHT 200 +#define MADS_Y_OFFSET ((MADS_SCREEN_HEIGHT - MADS_SURFACE_HEIGHT) / 2) + + struct BGR8 { uint8 b, g, r; }; @@ -89,19 +95,23 @@ class M4Surface : protected Graphics::Surface { private: byte _color; bool _isScreen; + RGBList *_rgbList; void rexLoadBackground(Common::SeekableReadStream *source, RGBList **palData = NULL); void madsLoadBackground(int roomNumber, RGBList **palData = NULL); void m4LoadBackground(Common::SeekableReadStream *source); public: M4Surface(bool isScreen = false) { - create(g_system->getWidth(), g_system->getHeight(), 1); + create(g_system->getWidth(), isScreen ? g_system->getHeight() : MADS_SURFACE_HEIGHT, 1); _isScreen = isScreen; + _rgbList = NULL; } M4Surface(int width_, int height_) { create(width_, height_, 1); _isScreen = false; + _rgbList = NULL; } + virtual ~M4Surface(); // loads a .COD file into the M4Surface // TODO: maybe move this to the rail system? check where it makes sense @@ -112,7 +122,8 @@ public: // loads the specified background void loadBackground(int sceneNumber, RGBList **palData = NULL); void loadBackgroundRiddle(const char *sceneName); - void madsloadInterface(int index, RGBList **palData); + void madsLoadInterface(int index, RGBList **palData = NULL); + void madsLoadInterface(const Common::String &filename); void setColor(byte value) { _color = value; } void setColour(byte value) { _color = value; } @@ -173,7 +184,8 @@ public: dest->copyFrom(this, destX, destY, depth, depthsSurface, scale, transparentColour); } - + void scrollX(int xAmount); + void scrollY(int yAmount); void translate(RGBList *list, bool isTransparent = false); }; diff --git a/engines/m4/gui.cpp b/engines/m4/gui.cpp index 8f949de9c5..8665b4e767 100644 --- a/engines/m4/gui.cpp +++ b/engines/m4/gui.cpp @@ -290,26 +290,26 @@ void MenuButton::onRefresh() { case OBJTYPE_SL_TEXT: switch (_objectState) { case OS_MOUSEOVER: - _vm->_font->setColors(TEXT_COLOR_MOUSEOVER_SHADOW, TEXT_COLOR_MOUSEOVER_FOREGROUND, + _vm->_font->current()->setColours(TEXT_COLOR_MOUSEOVER_SHADOW, TEXT_COLOR_MOUSEOVER_FOREGROUND, TEXT_COLOR_MOUSEOVER_HILIGHT); sprite = sprites[SL_LINE_MOUSEOVER]; break; case OS_PRESSED: - _vm->_font->setColors(TEXT_COLOR_PRESSED_SHADOW, TEXT_COLOR_PRESSED_FOREGROUND, + _vm->_font->current()->setColours(TEXT_COLOR_PRESSED_SHADOW, TEXT_COLOR_PRESSED_FOREGROUND, TEXT_COLOR_PRESSED_HILIGHT); sprite = sprites[SL_LINE_PRESSED]; break; case OS_GREYED: - _vm->_font->setColors(TEXT_COLOR_GREYED_SHADOW, TEXT_COLOR_GREYED_FOREGROUND, + _vm->_font->current()->setColours(TEXT_COLOR_GREYED_SHADOW, TEXT_COLOR_GREYED_FOREGROUND, TEXT_COLOR_GREYED_HILIGHT); sprite = sprites[SL_LINE_NORMAL]; break; default: case OS_NORMAL: - _vm->_font->setColors(TEXT_COLOR_NORMAL_SHADOW, TEXT_COLOR_NORMAL_FOREGROUND, + _vm->_font->current()->setColours(TEXT_COLOR_NORMAL_SHADOW, TEXT_COLOR_NORMAL_FOREGROUND, TEXT_COLOR_NORMAL_HILIGHT); sprite = sprites[SL_LINE_NORMAL]; break; @@ -849,11 +849,11 @@ void MenuSaveLoadText::onRefresh() { if (_displayValue != 0) { char tempBuffer[5]; sprintf(tempBuffer, "%02d", _displayValue); - _vm->_font->writeString(_parent, tempBuffer, xp, _bounds.top + 1, 0, -1); + _vm->_font->current()->writeString(_parent, tempBuffer, xp, _bounds.top + 1, 0, -1); xp = _bounds.left + 26; } - _vm->_font->writeString(_parent, _displayText, xp, _bounds.top + 1, 0, -1); + _vm->_font->current()->writeString(_parent, _displayText, xp, _bounds.top + 1, 0, -1); } } @@ -955,18 +955,18 @@ void MenuTextField::onRefresh() { // Draw the text _vm->_font->setFont(FONT_MENU); - _vm->_font->setColors(TEXT_COLOR_NORMAL_SHADOW, TEXT_COLOR_NORMAL_FOREGROUND, + _vm->_font->current()->setColours(TEXT_COLOR_NORMAL_SHADOW, TEXT_COLOR_NORMAL_FOREGROUND, TEXT_COLOR_NORMAL_HILIGHT); int xp = _bounds.left + 4; if (_displayValue != 0) { char tempBuffer[5]; sprintf(tempBuffer, "%02d", _displayValue); - _vm->_font->writeString(_parent, tempBuffer, xp, _bounds.top + 1, 0, -1); + _vm->_font->current()->writeString(_parent, tempBuffer, xp, _bounds.top + 1, 0, -1); xp = _bounds.left + 26; } - _vm->_font->writeString(_parent, _displayText, xp, _bounds.top + 1, 0, -1); + _vm->_font->current()->writeString(_parent, _displayText, xp, _bounds.top + 1, 0, -1); if (focused) { // Draw in the cursor @@ -975,7 +975,7 @@ void MenuTextField::onRefresh() { // Get the width of the string up to the cursor position char tempCh = *_cursor; *_cursor = '\0'; - int stringWidth = _vm->_font->getWidth(_displayText); + int stringWidth = _vm->_font->current()->getWidth(_displayText); *_cursor = tempCh; parent()->setColor(TEXT_COLOR_MOUSEOVER_FOREGROUND); @@ -1015,10 +1015,10 @@ bool MenuTextField::onEvent(M4EventType event, int32 param, int x, int y, MenuOb tempP = &tempStr[tempLen]; _vm->_font->setFont(FONT_MENU); - tempLen = _vm->_font->getWidth(tempStr); + tempLen = _vm->_font->current()->getWidth(tempStr); while ((tempP != &tempStr[0]) && (tempLen > x - _bounds.left - 26)) { *--tempP = '\0'; - tempLen = _vm->_font->getWidth(tempStr); + tempLen = _vm->_font->current()->getWidth(tempStr); } _cursor = &_displayText[tempP - &tempStr[0]]; @@ -1098,7 +1098,7 @@ bool MenuTextField::onEvent(M4EventType event, int32 param, int x, int y, MenuOb parent()->_deleteSaveDesc = false; _vm->_font->setFont(FONT_MENU); - tempLen = _vm->_font->getWidth(_displayText); + tempLen = _vm->_font->current()->getWidth(_displayText); if ((strlen(_displayText) < MAX_SAVEGAME_NAME - 1) && (tempLen < _pixelWidth - 12) && (param >= 32) && (param <= 127)) { @@ -1140,9 +1140,9 @@ GUITextField::GUITextField(View *owner, const Common::Rect &bounds): GUIRect(own void GUITextField::onRefresh() { _parent->fillRect(_bounds, _vm->_palette->BLACK); - _vm->_font->setColors(3, 3, 3); + _vm->_font->current()->setColours(3, 3, 3); _vm->_font->setFont(FONT_INTERFACE); - _vm->_font->writeString(_parent, _text.c_str(), _bounds.left, _bounds.top, 0, 1); + _vm->_font->current()->writeString(_parent, _text.c_str(), _bounds.left, _bounds.top, 0, 1); } //-------------------------------------------------------------------------- diff --git a/engines/m4/m4.cpp b/engines/m4/m4.cpp index 897fb468cd..a5db6660d8 100644 --- a/engines/m4/m4.cpp +++ b/engines/m4/m4.cpp @@ -145,8 +145,9 @@ MadsM4Engine::~MadsM4Engine() { delete _script; delete _ws; delete _random; - delete _animation; delete _palette; + delete _globals; + delete _resourceManager; } Common::Error MadsM4Engine::run() { @@ -170,7 +171,7 @@ Common::Error MadsM4Engine::run() { _events = new Events(this); _kernel = new Kernel(this); _player = new Player(this); - _font = new Font(this); + _font = new FontManager(this); if (getGameType() == GType_Burger) { _actor = new Actor(this); _conversationView = new ConversationView(this); @@ -184,7 +185,6 @@ Common::Error MadsM4Engine::run() { _sound = new Sound(this, _mixer, 255); _script = new ScriptInterpreter(this); _ws = new WoodScript(this); - _animation = new Animation(this); //_callbacks = new Callbacks(this); _random = new Common::RandomSource(); g_eventRec.registerRandomSource(*_random, "m4"); @@ -305,8 +305,6 @@ M4Engine::M4Engine(OSystem *syst, const M4GameDescription *gameDesc): MadsM4Engi } M4Engine::~M4Engine() { - delete _resourceManager; - delete _globals; delete _converse; } @@ -502,8 +500,6 @@ MadsEngine::MadsEngine(OSystem *syst, const M4GameDescription *gameDesc): MadsM4 } MadsEngine::~MadsEngine() { - delete _globals; - delete _resourceManager; } Common::Error MadsEngine::run() { @@ -554,9 +550,9 @@ Common::Error MadsEngine::run() { _scene->show(); _font->setFont(FONT_MAIN_MADS); - _font->setColors(2, 1, 3); - _font->writeString(_scene->getBackgroundSurface(), "Testing the M4/MADS ScummVM engine", 5, 160, 310, 2); - _font->writeString(_scene->getBackgroundSurface(), "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 5, 180, 310, 2); + _font->current()->setColours(2, 1, 3); + _font->current()->writeString(_scene->getBackgroundSurface(), "Testing the M4/MADS ScummVM engine", 5, 160, 310, 2); + _font->current()->writeString(_scene->getBackgroundSurface(), "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 5, 180, 310, 2); if (getGameType() == GType_DragonSphere) { //_scene->showMADSV2TextBox("Test", 10, 10, NULL); @@ -581,8 +577,6 @@ Common::Error MadsEngine::run() { while (!_events->quitFlag) { eventHandler(); - _animation->updateAnim(); - if (g_system->getMillis() >= nextFrame) { nextFrame = g_system->getMillis() + GAME_FRAME_DELAY; ++_currentTimer; diff --git a/engines/m4/m4.h b/engines/m4/m4.h index 1f34bd3685..9937107668 100644 --- a/engines/m4/m4.h +++ b/engines/m4/m4.h @@ -189,7 +189,7 @@ public: Player *_player; Mouse *_mouse; Events *_events; - Font *_font; + FontManager *_font; Actor *_actor; Scene *_scene; Dialogs *_dialogs; @@ -200,7 +200,6 @@ public: Rails *_rails; ScriptInterpreter *_script; WoodScript *_ws; - Animation *_animation; Common::RandomSource *_random; Scene *scene() { return _scene; } diff --git a/engines/m4/m4_views.cpp b/engines/m4/m4_views.cpp index 3d633cef0d..f4345787df 100644 --- a/engines/m4/m4_views.cpp +++ b/engines/m4/m4_views.cpp @@ -34,7 +34,7 @@ namespace M4 { GUIInventory::GUIInventory(View *owner, MadsM4Engine *vm, const Common::Rect &bounds, int horizCells, int vertCells, int cellWidth, int cellHeight, int tag): GUIRect(owner, bounds, tag) { - _vm = vm; + _vm = vm; _cellCount.x = horizCells; _cellCount.y = vertCells; _cellSize.x = cellWidth; diff --git a/engines/m4/mads_anim.cpp b/engines/m4/mads_anim.cpp index 954916700c..e1dbbaf106 100644 --- a/engines/m4/mads_anim.cpp +++ b/engines/m4/mads_anim.cpp @@ -37,7 +37,7 @@ namespace M4 { TextviewView::TextviewView(MadsM4Engine *vm): View(vm, Common::Rect(0, 0, vm->_screen->width(), vm->_screen->height())), _bgSurface(vm->_screen->width(), MADS_SURFACE_HEIGHT), - _textSurface(vm->_screen->width(), MADS_SURFACE_HEIGHT + vm->_font->getHeight() + + _textSurface(vm->_screen->width(), MADS_SURFACE_HEIGHT + vm->_font->current()->getHeight() + TEXTVIEW_LINE_SPACING) { _screenType = VIEWID_TEXTVIEW; @@ -60,7 +60,7 @@ TextviewView::TextviewView(MadsM4Engine *vm): _vm->_palette->setPalette(&palData[0], 4, 3); _vm->_palette->blockRange(4, 3); - _vm->_font->setColors(5, 6, 4); + _vm->_font->current()->setColours(5, 6, 4); clear(); _bgSurface.clear(); @@ -222,7 +222,7 @@ void TextviewView::updateState() { } } else { // Handling a text row - if (++_lineY == (_vm->_font->getHeight() + TEXTVIEW_LINE_SPACING)) + if (++_lineY == (_vm->_font->current()->getHeight() + TEXTVIEW_LINE_SPACING)) processLines(); } @@ -404,7 +404,7 @@ void TextviewView::processText() { if (!strcmp(_currentLine, "***")) { // Special signifier for end of script - _scrollCount = _vm->_font->getHeight() * 13; + _scrollCount = _vm->_font->current()->getHeight() * 13; _lineY = -1; return; } @@ -416,7 +416,7 @@ void TextviewView::processText() { char *centerP = strchr(_currentLine, '@'); if (centerP) { *centerP = '\0'; - xStart = (width() / 2) - _vm->_font->getWidth(_currentLine); + xStart = (width() / 2) - _vm->_font->current()->getWidth(_currentLine); // Delete the @ character and shift back the remainder of the string char *p = centerP + 1; @@ -424,16 +424,16 @@ void TextviewView::processText() { strcpy(centerP, p); } else { - lineWidth = _vm->_font->getWidth(_currentLine); + lineWidth = _vm->_font->current()->getWidth(_currentLine); xStart = (width() - lineWidth) / 2; } // Copy the text line onto the bottom of the textSurface surface, which will allow it // to gradually scroll onto the screen - int yp = _textSurface.height() - _vm->_font->getHeight() - TEXTVIEW_LINE_SPACING; + int yp = _textSurface.height() - _vm->_font->current()->getHeight() - TEXTVIEW_LINE_SPACING; _textSurface.fillRect(Common::Rect(0, yp, _textSurface.width(), _textSurface.height()), _vm->_palette->BLACK); - _vm->_font->writeString(&_textSurface, _currentLine, xStart, yp); + _vm->_font->current()->writeString(&_textSurface, _currentLine, xStart, yp); } @@ -441,7 +441,12 @@ void TextviewView::processText() { AnimviewView::AnimviewView(MadsM4Engine *vm): View(vm, Common::Rect(0, 0, vm->_screen->width(), vm->_screen->height())), - _bgSurface(vm->_screen->width(), MADS_SURFACE_HEIGHT) { + MadsView(this), _backgroundSurface(MADS_SURFACE_WIDTH, MADS_SURFACE_HEIGHT), + _codeSurface(MADS_SURFACE_WIDTH, MADS_SURFACE_HEIGHT) { + + MadsView::_bgSurface = &_backgroundSurface; + MadsView::_depthSurface = &_codeSurface; + MadsView::_yOffset = MADS_Y_OFFSET; _screenType = VIEWID_ANIMVIEW; _screenFlags.layer = LAYER_BACKGROUND; @@ -452,27 +457,28 @@ AnimviewView::AnimviewView(MadsM4Engine *vm): _palData = NULL; _previousUpdate = 0; _transition = kTransitionNone; + _activeAnimation = NULL; reset(); // Set up system palette colors _vm->_palette->setMadsSystemPalette(); clear(); - _bgSurface.clear(); + _backgroundSurface.clear(); - int y = (height() - MADS_SURFACE_HEIGHT) / 2; setColor(2); - hLine(0, width() - 1, y - 2); - hLine(0, width() - 1, height() - y + 1); + hLine(0, width() - 1, MADS_Y_OFFSET - 2); + hLine(0, width() - 1, MADS_Y_OFFSET + MADS_SURFACE_HEIGHT + 2); } AnimviewView::~AnimviewView() { if (_script) _vm->res()->toss(_resourceName); + delete _activeAnimation; } void AnimviewView::reset() { - _bgSurface.clear(); + _backgroundSurface.clear(); _soundDriverLoaded = false; } @@ -507,27 +513,26 @@ void AnimviewView::updateState() { if (!_script) return; - // Only update state if wait period has expired - if (_previousUpdate > 0) { - if (g_system->getMillis() - _previousUpdate < 100) - return; - - _previousUpdate = g_system->getMillis(); + if (!_activeAnimation) { + readNextCommand(); + assert(_activeAnimation); } - // Check if we're ready for the next command - bool animRunning = false; - if (!animRunning) { + // Update the current animation + _activeAnimation->update(); + if (_activeAnimation->freeFlag()) { + delete _activeAnimation; + _activeAnimation = NULL; + if (_script->eos() || _script->err()) { scriptDone(); return; } readNextCommand(); - - // FIXME: Replace flag with proper animation end check - animRunning = true; } + + refresh(); } void AnimviewView::readNextCommand() { @@ -562,46 +567,15 @@ void AnimviewView::readNextCommand() { if (strchr(_currentLine, '.') == NULL) strcat(_currentLine, ".aa"); - AAFile aaFile(_currentLine, _vm); - - // Initial validation - if (aaFile.flags & AA_HAS_FONT) { - assert(_vm->_resourceManager->resourceExists(aaFile.fontResource.c_str())); - } - - for (int seriesCtr = 0; seriesCtr < aaFile.seriesCount; ++seriesCtr) - assert(_vm->_resourceManager->resourceExists(aaFile.filenames[seriesCtr].c_str())); - - // Start sound - if (aaFile.flags & AA_HAS_SOUND) { - char buffer[100]; - strcpy(buffer, aaFile.soundName.c_str()); - buffer[0] = 'A'; // A for AdLib resource - - /*Common::SeekableReadStream *stream = */_vm->_resourceManager->get(buffer); - - _vm->_resourceManager->toss(buffer); - } - - - char artFile[80]; - sprintf(artFile, "rm%d.art", aaFile.roomNumber); - - // Not all scenes have a background. If there is one, refresh it - if (_vm->_resourceManager->resourceExists(artFile)) { - if (_palData) { - _vm->_palette->deleteRange(_palData); - delete _palData; - } - _bgSurface.loadBackground(aaFile.roomNumber, &_palData); - _vm->_palette->addRange(_palData); - _bgSurface.translate(_palData); - } + _activeAnimation = new MadsAnimation(_vm, this); + _activeAnimation->load(_currentLine, 0); - // Grab what the final palete will be - RGB8 destPalette[256]; - _vm->_palette->grabPalette(destPalette, 0, 256); + _backgroundSurface.loadBackground(_activeAnimation->roomNumber()); + _codeSurface.setSize(_backgroundSurface.width(), _backgroundSurface.height()); + _codeSurface.fillRect(_codeSurface.bounds(), 0xff); + _spriteSlots.fullRefresh(); +/* // Handle scene transition switch (_transition) { case kTransitionNone: @@ -631,16 +605,14 @@ void AnimviewView::readNextCommand() { // nothing to do break; } - - // Refresh the view - int yp = (height() - _bgSurface.height()) / 2; - _bgSurface.copyTo(this, 0, yp); +*/ _vm->_resourceManager->toss(_currentLine); } void AnimviewView::scriptDone() { +return; AnimviewCallback fn = _callback; MadsM4Engine *vm = _vm; @@ -714,45 +686,4 @@ void AnimviewView::processCommand() { } } -AAFile::AAFile(const char *resourceName, MadsM4Engine* vm): MadsPack(resourceName, vm) { - Common::MemoryReadStream stream1(*getItemStream(1)); - Common::MemoryReadStream stream2(*getItemStream(2)); - - Common::MemoryReadStream stream(*getItemStream(0)); - - seriesCount = stream.readUint16LE(); - frameCount = stream.readUint16LE(); - frameEntryCount = stream.readUint16LE(); - stream.skip(3); - flags = stream.readByte(); - stream.skip(4); - roomNumber = stream.readUint16LE(); - stream.skip(10); - frameTicks = stream.readUint16LE(); - - stream.skip(21); - for (int i = 0; i < 10; ++i) { - char filename[13]; - stream.read(filename, 13); - filenames.push_back(Common::String(filename, 13)); - } - - stream.skip(81); - char name[100]; - stream.read(name, 13); - lbmFilename = Common::String(name, 13); - - stream.skip(365); - stream.read(name, 13); - spritesFilename = Common::String(name, 13); - - stream.skip(48); - stream.read(name, 13); - soundName = Common::String(name, 13); - - stream.skip(26); - stream.read(name, 14); - fontResource = Common::String(name, 14); -} - } diff --git a/engines/m4/mads_anim.h b/engines/m4/mads_anim.h index 680c5ff901..8c4a5e6fb7 100644 --- a/engines/m4/mads_anim.h +++ b/engines/m4/mads_anim.h @@ -28,24 +28,12 @@ #include "m4/viewmgr.h" #include "m4/compression.h" +#include "m4/animation.h" #include "common/str-array.h" namespace M4 { -enum SceneTransition { - kTransitionNone = 0, - kTransitionFadeIn = 1, - kTransitionFadeIn2 = 2, - kTransitionBoxInBottomLeft = 3, - kTransitionBoxInBottomRight = 4, - kTransitionBoxInTopLeft = 5, - kTransitionBoxInTopRight = 6, - kTransitionPanLeftToRight = 7, - kTransitionPanRightToLeft = 8, - kTransitionCircleIn = 9 -}; - typedef void (*TextviewCallback)(MadsM4Engine *vm); class TextviewView : public View { @@ -89,36 +77,19 @@ public: typedef void (*AnimviewCallback)(MadsM4Engine *vm); -class AAFile : public MadsPack { -public: - AAFile(const char *resourceName, MadsM4Engine* vm); - - uint16 seriesCount; - uint16 frameCount; - uint16 frameEntryCount; - uint8 flags; - uint16 roomNumber; - uint16 frameTicks; - Common::StringArray filenames; - Common::String lbmFilename; - Common::String spritesFilename; - Common::String soundName; - Common::String fontResource; -}; - -enum AAFlags {AA_HAS_FONT = 0x20, AA_HAS_SOUND = 0x8000}; - -class AnimviewView : public View { +class AnimviewView : public View, MadsView { private: char _resourceName[80]; Common::SeekableReadStream *_script; uint32 _previousUpdate; char _currentLine[80]; - M4Surface _bgSurface; + M4Surface _backgroundSurface; + M4Surface _codeSurface; AnimviewCallback _callback; bool _soundDriverLoaded; RGBList *_palData; int _transition; + MadsAnimation *_activeAnimation; void reset(); void readNextCommand(); diff --git a/engines/m4/mads_logic.cpp b/engines/m4/mads_logic.cpp index 1fe5f4beb3..9cb053a876 100644 --- a/engines/m4/mads_logic.cpp +++ b/engines/m4/mads_logic.cpp @@ -59,8 +59,8 @@ void MadsSceneLogic::getSceneSpriteSet() { // if ((_sceneNumber == 105) ((_sceneNumber == 109) && (word_84800 != 0))) // _madsVm->globals()->playerSpriteChanged = true; -// _vm->_palette->setEntry(16, 0x38, 0xFF, 0xFF); -// _vm->_palette->setEntry(17, 0x38, 0xb4, 0xb4); + _vm->_palette->setEntry(16, 0x38, 0xFF, 0xFF); + _vm->_palette->setEntry(17, 0x38, 0xb4, 0xb4); } void MadsSceneLogic::getAnimName() { @@ -83,7 +83,7 @@ uint16 MadsSceneLogic::startReversibleSpriteSequence(uint16 srcSpriteIdx, int v0 spriteFrame->y + (spriteFrame->height() / 2))); return _madsVm->scene()->_sequenceList.add(srcSpriteIdx, v0, 1, triggerCountdown, timeoutTicks, extraTicks, numTicks, 0, 0, - -1, 100, depth - 1, 1, ANIMTYPE_REVERSIBLE, 0, 0); + true, 100, depth - 1, 1, ANIMTYPE_REVERSIBLE, 0, 0); } uint16 MadsSceneLogic::startCycledSpriteSequence(uint16 srcSpriteIdx, int v0, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks) { @@ -92,7 +92,7 @@ uint16 MadsSceneLogic::startCycledSpriteSequence(uint16 srcSpriteIdx, int v0, in spriteFrame->y + (spriteFrame->height() / 2))); return _madsVm->scene()->_sequenceList.add(srcSpriteIdx, v0, 1, triggerCountdown, timeoutTicks, extraTicks, numTicks, 0, 0, - -1, 100, depth - 1, 1, ANIMTYPE_CYCLED, 0, 0); + true, 100, depth - 1, 1, ANIMTYPE_CYCLED, 0, 0); } uint16 MadsSceneLogic::startSpriteSequence3(uint16 srcSpriteIdx, int v0, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks) { @@ -101,7 +101,7 @@ uint16 MadsSceneLogic::startSpriteSequence3(uint16 srcSpriteIdx, int v0, int num spriteFrame->y + (spriteFrame->height() / 2))); return _madsVm->scene()->_sequenceList.add(srcSpriteIdx, v0, 1, triggerCountdown, timeoutTicks, extraTicks, numTicks, 0, 0, - -1, 100, depth - 1, -1, ANIMTYPE_CYCLED, 0, 0); + true, 100, depth - 1, -1, ANIMTYPE_CYCLED, 0, 0); } void MadsSceneLogic::activateHotspot(int idx, bool active) { @@ -146,6 +146,7 @@ void MadsSceneLogic::lowRoomsEntrySound() { } } + /*--------------------------------------------------------------------------*/ /** @@ -169,7 +170,9 @@ void MadsSceneLogic::setupScene() { // sub_1e754(animName, 3); - getSceneSpriteSet(); + if ((_sceneNumber >= 101) && (_sceneNumber <= 112)) + getSceneSpriteSet(); + getAnimName(); } @@ -231,7 +234,8 @@ void MadsSceneLogic::enterScene() { _madsVm->globals()->loadQuoteSet(0x31, 0x32, 0x37, 0x38, 0x39, -1); if (_madsVm->globals()->_globals[10]) { - // TODO: Load scene animation + const char *animName = MADSResourceManager::getResourceName('S', 'e', EXTTYPE_AA, NULL, -1); + _madsVm->scene()->loadAnimation(animName, 0x47); _madsVm->scene()->getSceneResources().playerPos = Common::Point(68, 140); _madsVm->scene()->getSceneResources().playerDir = 4; @@ -245,4 +249,15 @@ void MadsSceneLogic::doAction() { } +void MadsSceneLogic::sceneStep() { + // FIXME: Temporary code to display a message on-screen + static bool tempBool = false; + if (!tempBool) { + tempBool = true; + + _madsVm->scene()->_kernelMessages.add(Common::Point(63, 100), 0x1110, 0, 0, 240, + _madsVm->globals()->getQuote(49)); + } +} + } diff --git a/engines/m4/mads_logic.h b/engines/m4/mads_logic.h index a589556a21..8c3f41d08b 100644 --- a/engines/m4/mads_logic.h +++ b/engines/m4/mads_logic.h @@ -29,6 +29,8 @@ #ifndef M4_MADS_LOGIC_H #define M4_MADS_LOGIC_H +#include "m4/mads_views.h" + namespace M4 { class MadsSceneLogic { @@ -54,6 +56,7 @@ public: void setupScene(); void enterScene(); void doAction(); + void sceneStep(); }; } diff --git a/engines/m4/mads_menus.cpp b/engines/m4/mads_menus.cpp index da8ac4230e..94894e78be 100644 --- a/engines/m4/mads_menus.cpp +++ b/engines/m4/mads_menus.cpp @@ -49,7 +49,7 @@ RexMainMenuView::RexMainMenuView(MadsM4Engine *vm): _skipFlag = false; // Load the background for the Rex Nebular game - _bgSurface = new M4Surface(width(), MADS_SURFACE_HEIGHT); + _bgSurface = new M4Surface(); _bgSurface->loadBackground(REX_MENUSCREEN, &_bgPalData); _vm->_palette->addRange(_bgPalData); _bgSurface->translate(_bgPalData); @@ -619,7 +619,7 @@ void RexDialogView::initialiseLines() { // Set up a default sprite slot entry for a full screen refresh _spriteSlots.startIndex = 1; _spriteSlots[0].spriteType = FULL_SCREEN_REFRESH; - _spriteSlots[0].timerIndex = -1; + _spriteSlots[0].seqIndex = -1; } void RexDialogView::initialiseGraphics() { @@ -796,7 +796,7 @@ bool RexDialogView::onEvent(M4EventType eventType, int32 param1, int x, int y, b void RexDialogView::setFrame(int frameNumber, int depth) { int slotIndex = _spriteSlots.getIndex(); _spriteSlots[slotIndex].spriteType = FOREGROUND_SPRITE; - _spriteSlots[slotIndex].timerIndex = 1; + _spriteSlots[slotIndex].seqIndex = 1; _spriteSlots[slotIndex].spriteListIndex = 0; //_menuSpritesIndex; _spriteSlots[slotIndex].frameNumber = frameNumber; @@ -985,15 +985,15 @@ RexGameMenuDialog::RexGameMenuDialog(): RexDialogView() { void RexGameMenuDialog::addLines() { // Add the title - int top = MADS_Y_OFFSET - 2 - ((((_vm->_font->getHeight() + 2) * 6) >> 1) - 78); + int top = MADS_Y_OFFSET - 2 - ((((_vm->_font->current()->getHeight() + 2) * 6) >> 1) - 78); - addQuote(_vm->_font, ALIGN_CENTER, 0, top, 10); + addQuote(_vm->_font->current(), ALIGN_CENTER, 0, top, 10); // Loop for adding the option lines of the dialog top += 6; for (int idx = 0; idx < 5; ++idx) { - top += _vm->_font->getHeight() + 1; - addQuote(_vm->_font, ALIGN_CENTER, 0, top, 11 + idx); + top += _vm->_font->current()->getHeight() + 1; + addQuote(_vm->_font->current(), ALIGN_CENTER, 0, top, 11 + idx); } } @@ -1069,42 +1069,42 @@ void RexOptionsDialog::reload() { void RexOptionsDialog::addLines() { // Add the title - int top = MADS_Y_OFFSET - 2 - ((((_vm->_font->getHeight() + 1) * 9 + 12) >> 1) - 78); + int top = MADS_Y_OFFSET - 2 - ((((_vm->_font->current()->getHeight() + 1) * 9 + 12) >> 1) - 78); - addQuote(_vm->_font, ALIGN_CENTER, 0, top, 16); + addQuote(_vm->_font->current(), ALIGN_CENTER, 0, top, 16); // Music state line - top += _vm->_font->getHeight() + 1 + 6; - addQuote(_vm->_font, ALIGN_CHAR_CENTER, 0, top, 17, _tempConfig.musicFlag ? 24 : 25); + top += _vm->_font->current()->getHeight() + 1 + 6; + addQuote(_vm->_font->current(), ALIGN_CHAR_CENTER, 0, top, 17, _tempConfig.musicFlag ? 24 : 25); // Sound state line - top += _vm->_font->getHeight() + 1; - addQuote(_vm->_font, ALIGN_CHAR_CENTER, 0, top, 18, _tempConfig.soundFlag ? 26 : 27); + top += _vm->_font->current()->getHeight() + 1; + addQuote(_vm->_font->current(), ALIGN_CHAR_CENTER, 0, top, 18, _tempConfig.soundFlag ? 26 : 27); // Interface easy state line - top += _vm->_font->getHeight() + 1; - addQuote(_vm->_font, ALIGN_CHAR_CENTER, 0, top, 19, _tempConfig.easyMouse ? 29 : 28); + top += _vm->_font->current()->getHeight() + 1; + addQuote(_vm->_font->current(), ALIGN_CHAR_CENTER, 0, top, 19, _tempConfig.easyMouse ? 29 : 28); // Inventory sppinng state line - top += _vm->_font->getHeight() + 1; - addQuote(_vm->_font, ALIGN_CHAR_CENTER, 0, top, 20, _tempConfig.invObjectsStill ? 31 : 30); + top += _vm->_font->current()->getHeight() + 1; + addQuote(_vm->_font->current(), ALIGN_CHAR_CENTER, 0, top, 20, _tempConfig.invObjectsStill ? 31 : 30); // Text window state line - top += _vm->_font->getHeight() + 1; - addQuote(_vm->_font, ALIGN_CHAR_CENTER, 0, top, 21, _tempConfig.textWindowStill ? 33 : 32); + top += _vm->_font->current()->getHeight() + 1; + addQuote(_vm->_font->current(), ALIGN_CHAR_CENTER, 0, top, 21, _tempConfig.textWindowStill ? 33 : 32); // Screen fade state line - top += _vm->_font->getHeight() + 1; - addQuote(_vm->_font, ALIGN_CHAR_CENTER, 0, top, 22, _tempConfig.screenFades + 34); + top += _vm->_font->current()->getHeight() + 1; + addQuote(_vm->_font->current(), ALIGN_CHAR_CENTER, 0, top, 22, _tempConfig.screenFades + 34); // Storyline mode line - top += _vm->_font->getHeight() + 1; - addQuote(_vm->_font, ALIGN_CHAR_CENTER, 0, top, 23, (_tempConfig.storyMode == 1) ? 37 : 38); + top += _vm->_font->current()->getHeight() + 1; + addQuote(_vm->_font->current(), ALIGN_CHAR_CENTER, 0, top, 23, (_tempConfig.storyMode == 1) ? 37 : 38); // Add Done and Cancel button texts - top += _vm->_font->getHeight() + 1 + 6; - addQuote(_vm->_font, ALIGN_CENTER, -54, top, 1, 0); - addQuote(_vm->_font, ALIGN_CENTER, 54, top, 2, 0); + top += _vm->_font->current()->getHeight() + 1 + 6; + addQuote(_vm->_font->current(), ALIGN_CENTER, -54, top, 1, 0); + addQuote(_vm->_font->current(), ALIGN_CENTER, 54, top, 2, 0); } bool RexOptionsDialog::onEvent(M4EventType eventType, int32 param1, int x, int y, bool &captureEvents) { diff --git a/engines/m4/mads_scene.cpp b/engines/m4/mads_scene.cpp index 4f28cdc6da..a65224c722 100644 --- a/engines/m4/mads_scene.cpp +++ b/engines/m4/mads_scene.cpp @@ -37,11 +37,20 @@ #include "m4/mads_views.h" #include "m4/compression.h" #include "m4/staticres.h" +#include "m4/animation.h" namespace M4 { +static const int INV_ANIM_FRAME_SPEED = 2; +static const int INVENTORY_X = 160; +static const int INVENTORY_Y = 159; +static const int SCROLLER_DELAY = 200; + +//-------------------------------------------------------------------------- + MadsScene::MadsScene(MadsEngine *vm): _sceneResources(), Scene(vm, &_sceneResources), MadsView(this) { _vm = vm; + _activeAnimation = NULL; MadsView::_bgSurface = Scene::_backgroundSurface; MadsView::_depthSurface = Scene::_walkSurface; @@ -51,6 +60,8 @@ MadsScene::MadsScene(MadsEngine *vm): _sceneResources(), Scene(vm, &_sceneResour } MadsScene::~MadsScene() { + delete _activeAnimation; + _activeAnimation = NULL; leaveScene(); _vm->_viewManager->deleteView(_interfaceSurface); } @@ -66,10 +77,17 @@ void MadsScene::loadScene2(const char *aaName) { _kernelMessages.clear(); // Load up the properties for the scene - _sceneResources.load(_currentScene); + _sceneResources.load(_currentScene, NULL, 0/*word_83546*/, _walkSurface, _backgroundSurface); // Load scene walk paths loadSceneCodes(_currentScene); + + // Initialise the scene animation + uint16 flags = 0x4100; + if (_madsVm->globals()->_config.textWindowStill) + flags |= 0x200; + + _sceneAnimation->initialise(aaName, flags, _interfaceSurface, NULL); } /** @@ -78,27 +96,12 @@ void MadsScene::loadScene2(const char *aaName) { void MadsScene::loadSceneTemporary() { /* Existing code that eventually needs to be replaced with the proper MADS code */ // Set system palette entries - _vm->_palette->blockRange(0, 7); + _vm->_palette->blockRange(0, 18); RGB8 sysColors[3] = { {0x1f<<2, 0x2d<<2, 0x31<<2, 0}, {0x24<<2, 0x37<<2, 0x3a<<2, 0}, {0x00<<2, 0x10<<2, 0x16<<2, 0}}; _vm->_palette->setPalette(&sysColors[0], 4, 3); - _backgroundSurface->loadBackground(_currentScene, &_palData); - _vm->_palette->addRange(_palData); - _backgroundSurface->translate(_palData); - - if (_currentScene < 900) { - _interfaceSurface->madsloadInterface(0, &_interfacePal); - _vm->_palette->addRange(_interfacePal); - _interfaceSurface->translate(_interfacePal); - _backgroundSurface->copyFrom(_interfaceSurface, Common::Rect(0, 0, 320, 44), 0, 200 - 44); - - _interfaceSurface->initialise(); - } - - // Don't load other screen resources for system screens - if (_currentScene >= 900) - return; + _interfaceSurface->initialise(); loadSceneHotspots(_currentScene); @@ -167,6 +170,11 @@ void MadsScene::leaveScene() { delete _sceneResources.props; delete _walkSurface; + if (_activeAnimation) { + delete _activeAnimation; + _activeAnimation = NULL; + } + Scene::leaveScene(); } @@ -185,15 +193,6 @@ void MadsScene::loadSceneCodes(int sceneNumber, int index) { sceneS = walkData.getItemStream(0); _walkSurface->loadCodesMads(sceneS); _vm->res()->toss(filename); - } else if (_vm->getGameType() == GType_RexNebular) { - // For Rex Nebular, the walk areas are part of the scene info - byte *destP = _walkSurface->getBasePtr(0, 0); - const byte *srcP = _sceneResources.walkData; - byte runLength; - while ((runLength = *srcP++) != 0) { - Common::set_to(destP, destP + runLength, *srcP++); - destP += runLength; - } } } @@ -289,20 +288,28 @@ void MadsScene::update() { if (sStatusText[0]) { // Text colors are inverted in Dragonsphere if (_vm->getGameType() == GType_DragonSphere) - _vm->_font->setColors(_vm->_palette->BLACK, _vm->_palette->WHITE, _vm->_palette->BLACK); + _vm->_font->current()->setColours(_vm->_palette->BLACK, _vm->_palette->WHITE, _vm->_palette->BLACK); else - _vm->_font->setColors(_vm->_palette->WHITE, _vm->_palette->BLACK, _vm->_palette->BLACK); + _vm->_font->current()->setColours(_vm->_palette->WHITE, _vm->_palette->BLACK, _vm->_palette->BLACK); _vm->_font->setFont(FONT_MAIN_MADS); - _vm->_font->writeString(this, sStatusText, (width() - _vm->_font->getWidth(sStatusText)) / 2, 142, 0); + _vm->_font->current()->writeString(this, sStatusText, (width() - _vm->_font->current()->getWidth(sStatusText)) / 2, 142, 0); } - - //***DEBUG*** - _spriteSlots.getSprite(0).getFrame(1)->copyTo(this, 120, 90, 0); } void MadsScene::updateState() { + _sceneLogic.sceneStep(); _sequenceList.tick(); + + if ((_activeAnimation) && !_abortTimers) { + _activeAnimation->update(); + if (((MadsAnimation *) _activeAnimation)->freeFlag()) { + delete _activeAnimation; + _activeAnimation = NULL; + } + } + + _kernelMessages.update(); } int MadsScene::loadSceneSpriteSet(const char *setName) { @@ -435,6 +442,15 @@ void MadsScene::showMADSV2TextBox(char *text, int x, int y, char *faceName) { boxSprites->getFrame(bottomRight)->copyTo(_backgroundSurface, curX, curY + 1); } +void MadsScene::loadAnimation(const Common::String &animName, int v0) { + if (_activeAnimation) + error("Multiple active animations are not allowed"); + + MadsAnimation *anim = new MadsAnimation(_vm, this); + anim->load(animName.c_str(), 0); + _activeAnimation = anim; +} + /*--------------------------------------------------------------------------*/ MadsAction::MadsAction() { @@ -612,97 +628,531 @@ void MadsAction::set() { /*--------------------------------------------------------------------------*/ -void MadsSceneResources::load(int sId) { - const char *sceneInfoStr = MADSResourceManager::getResourceName(RESPREFIX_RM, sId, ".DAT"); - Common::SeekableReadStream *rawStream = _vm->_resourceManager->get(sceneInfoStr); +void MadsSceneResources::load(int sceneNumber, const char *resName, int v0, M4Surface *depthSurface, M4Surface *surface) { + char buffer1[80]; + const char *sceneName; + + // TODO: Initialise spriteSet / xp_list + + if (sceneNumber > 0) { + sceneName = MADSResourceManager::getResourceName(RESPREFIX_RM, sceneNumber, ".DAT"); + } else { + strcat(buffer1, "*"); + strcat(buffer1, resName); + sceneName = buffer1; // TODO: Check whether this needs to be converted to 'HAG form' + } + + Common::SeekableReadStream *rawStream = _vm->_resourceManager->get(sceneName); MadsPack sceneInfo(rawStream); + // Chunk 0: // Basic scene info Common::SeekableReadStream *stream = sceneInfo.getItemStream(0); int resSceneId = stream->readUint16LE(); - assert(resSceneId == sId); - + assert(resSceneId == sceneNumber); artFileNum = stream->readUint16LE(); - field_4 = stream->readUint16LE(); + drawStyle = stream->readUint16LE(); width = stream->readUint16LE(); height = stream->readUint16LE(); assert((width == 320) && (height == 156)); stream->skip(24); - objectCount = stream->readUint16LE(); + int objectCount = stream->readUint16LE(); stream->skip(40); + // Load in any scene objects for (int i = 0; i < objectCount; ++i) { - objects[i].load(stream); + MadsObject rec; + rec.load(stream); + objects.push_back(rec); + } + for (int i = 0; i < 20 - objectCount; ++i) + stream->skip(48); + + int setCount = stream->readUint16LE(); + stream->readUint16LE(); + for (int i = 0; i < setCount; ++i) { + char buffer2[64]; + Common::String s(buffer2, 64); + setNames.push_back(s); + } + + // Initialise a copy of the surfaces if they weren't provided + bool dsFlag = false, ssFlag = false; + int gfxSize = width * height; + if (!surface) { + surface = new M4Surface(width, height); + ssFlag = true; + } + int walkSize = gfxSize; + if (drawStyle == 2) { + width >>= 2; + walkSize = width * height; + } + if (!depthSurface) { + depthSurface = new M4Surface(width, height); + dsFlag = true; } // For Rex Nebular, read in the scene's compressed walk surface information if (_vm->getGameType() == GType_RexNebular) { - delete walkData; - + assert(depthSurface); stream = sceneInfo.getItemStream(1); - walkData = (byte *)malloc(stream->size()); + byte *walkData = (byte *)malloc(stream->size()); stream->read(walkData, stream->size()); + + // For Rex Nebular, the walk areas are part of the scene info + byte *destP = depthSurface->getBasePtr(0, 0); + const byte *srcP = walkData; + byte runLength; + while ((runLength = *srcP++) != 0) { + Common::set_to(destP, destP + runLength, *srcP++); + destP += runLength; + } + + delete walkData; + delete stream; } - _vm->_resourceManager->toss(sceneInfoStr); + _vm->_resourceManager->toss(sceneName); + + // Load the surface artwork + surface->loadBackground(sceneNumber); + + // Final cleanup + if (ssFlag) + delete surface; + if (dsFlag) + delete depthSurface; } + /*--------------------------------------------------------------------------*/ -/** - * Adds a new entry to the timed on-screen text display list +/*-------------------------------------------------------------------------- + * MadsInterfaceView handles the user interface section at the bottom of + * game screens in MADS games + *-------------------------------------------------------------------------- */ -/* -void MadsScreenText::draw(M4Surface *surface) { + +MadsInterfaceView::MadsInterfaceView(MadsM4Engine *vm): GameInterfaceView(vm, + Common::Rect(0, MADS_SURFACE_HEIGHT, vm->_screen->width(), vm->_screen->height())) { + _screenType = VIEWID_INTERFACE; + _highlightedElement = -1; + _topIndex = 0; + _selectedObject = -1; + _cheatKeyCtr = 0; + + _objectSprites = NULL; + _objectPalData = NULL; + + /* Set up the rect list for screen elements */ + // Actions + for (int i = 0; i < 10; ++i) + _screenObjects.addRect((i / 5) * 32 + 1, (i % 5) * 8 + MADS_SURFACE_HEIGHT + 2, + ((i / 5) + 1) * 32 + 3, ((i % 5) + 1) * 8 + MADS_SURFACE_HEIGHT + 2); + + // Scroller elements (up arrow, scroller, down arrow) + _screenObjects.addRect(73, 160, 82, 167); + _screenObjects.addRect(73, 168, 82, 190); + _screenObjects.addRect(73, 191, 82, 198); + + // Inventory object names + for (int i = 0; i < 5; ++i) + _screenObjects.addRect(89, 158 + i * 8, 160, 166 + i * 8); + + // Full rectangle area for all vocab actions + for (int i = 0; i < 5; ++i) + _screenObjects.addRect(239, 158 + i * 8, 320, 166 + i * 8); +} + +MadsInterfaceView::~MadsInterfaceView() { + delete _objectSprites; } -void MadsScreenText::timedDisplay() { - for (int idx = 0; !_abortTimedText && (idx < OLD_TEXT_DISPLAY_SIZE); ++idx) { - if (((_timedText[idx].flags & TEXTFLAG_ACTIVE) != 0) && - (_timedText[idx].frameTimer <= g_system->getMillis())) - // Add the specified entry - addTimedText(&_timedText[idx]); +void MadsInterfaceView::setFontMode(InterfaceFontMode newMode) { + switch (newMode) { + case ITEM_NORMAL: + _vm->_font->current()->setColours(4, 4, 0xff); + break; + case ITEM_HIGHLIGHTED: + _vm->_font->current()->setColours(5, 5, 0xff); + break; + case ITEM_SELECTED: + _vm->_font->current()->setColours(6, 6, 0xff); + break; } } -void MadsScreenText::addTimedText(TimedText *entry) { - if ((entry->flags & TEXTFLAG_40) != 0) { - this->setActive2(entry->textDisplayIndex); - entry->flags &= 0x7F; +void MadsInterfaceView::initialise() { + // Build up the inventory list + _inventoryList.clear(); + + for (uint i = 0; i < _madsVm->globals()->getObjectsSize(); ++i) { + MadsObject *obj = _madsVm->globals()->getObject(i); + if (obj->roomNumber == PLAYER_INVENTORY) + _inventoryList.push_back(i); + } + + // If the inventory has at least one object, select it + if (_inventoryList.size() > 0) + setSelectedObject(_inventoryList[0]); +} + +void MadsInterfaceView::setSelectedObject(int objectNumber) { + char resName[80]; + + // Load inventory resource + if (_objectSprites) { + _vm->_palette->deleteRange(_objectPalData); + delete _objectSprites; + } + + // Check to make sure the object is in the inventory, and also visible on-screen + int idx = _inventoryList.indexOf(objectNumber); + if (idx == -1) { + // Object wasn't found, so return + _selectedObject = -1; return; } - if ((entry->flags & TEXTFLAG_8) == 0) - // FIXME: Adjust timeouts for ScumVM's milli counter - entry->timeout -= 3; + // Found the object + if (idx < _topIndex) + _topIndex = idx; + else if (idx >= (_topIndex + 5)) + _topIndex = MAX(0, idx - 4); - if ((entry->flags & TEXTFLAG_4) != 0) { - Text4A &rec = _text4A[entry->unk4AIndex]; - if ((rec.field25 != 0) || (rec.active == 0)) - entry->timeout = 0; + _selectedObject = objectNumber; + sprintf(resName, "*OB%.3dI.SS", objectNumber); + + Common::SeekableReadStream *data = _vm->res()->get(resName); + _objectSprites = new SpriteAsset(_vm, data, data->size(), resName); + _vm->res()->toss(resName); + + // Slot it into available palette space + _objectPalData = _objectSprites->getRgbList(); + _vm->_palette->addRange(_objectPalData); + _objectSprites->translate(_objectPalData, true); + + _objectFrameNumber = 0; +} + +void MadsInterfaceView::addObjectToInventory(int objectNumber) { + if (_inventoryList.indexOf(objectNumber) == -1) { + _madsVm->globals()->getObject(objectNumber)->roomNumber = PLAYER_INVENTORY; + _inventoryList.push_back(objectNumber); } - if ((entry->timeout == 0) && !_abortTimedText) { - entry->flags |= TEXTFLAG_40; + setSelectedObject(objectNumber); +} - if (entry->field_1C) { - _abortTimedText = entry->field_1C; - //word_84208 = entry->field_1D; - - if (entry->field_1D != 1) { - // Restore the action list - for (int i = 0; i < 3; ++i) - _madsVm->scene()->actionNouns[i] = entry->actionNouns[i]; +void MadsInterfaceView::onRefresh(RectList *rects, M4Surface *destSurface) { + _vm->_font->setFont(FONT_INTERFACE_MADS); + char buffer[100]; + + // Check to see if any dialog is currently active + bool dialogVisible = _vm->_viewManager->getView(LAYER_DIALOG) != NULL; + + // Highlighting logic for action list + int actionIndex = 0; + for (int x = 0; x < 2; ++x) { + for (int y = 0; y < 5; ++y, ++actionIndex) { + // Determine the font colour depending on whether an item is selected. Note that the first action, + // 'Look', is always 'selected', even when another action is clicked on + setFontMode((_highlightedElement == actionIndex) ? ITEM_HIGHLIGHTED : + ((actionIndex == 0) ? ITEM_SELECTED : ITEM_NORMAL)); + + // Get the verb action and capitalise it + const char *verbStr = _madsVm->globals()->getVocab(kVerbLook + actionIndex); + strcpy(buffer, verbStr); + if ((buffer[0] >= 'a') && (buffer[0] <= 'z')) buffer[0] -= 'a' - 'A'; + + // Display the verb + const Common::Rect r(_screenObjects[actionIndex]); + _vm->_font->current()->writeString(destSurface, buffer, r.left, r.top, r.width(), 0); + } + } + + // Check for highlighting of the scrollbar controls + if ((_highlightedElement == SCROLL_UP) || (_highlightedElement == SCROLL_SCROLLER) || (_highlightedElement == SCROLL_DOWN)) { + // Highlight the control's borders + const Common::Rect r(_screenObjects[_highlightedElement]); + destSurface->frameRect(r, 5); + } + + // Draw the horizontal line in the scroller representing the current top selected + const Common::Rect scroller(_screenObjects[SCROLL_SCROLLER]); + int yP = (_inventoryList.size() < 2) ? 0 : (scroller.height() - 5) * _topIndex / (_inventoryList.size() - 1); + destSurface->setColor(4); + destSurface->hLine(scroller.left + 2, scroller.right - 3, scroller.top + 2 + yP); + + // List inventory items + for (uint i = 0; i < 5; ++i) { + if ((_topIndex + i) >= _inventoryList.size()) + break; + + const char *descStr = _madsVm->globals()->getVocab(_madsVm->globals()->getObject( + _inventoryList[_topIndex + i])->descId); + strcpy(buffer, descStr); + if ((buffer[0] >= 'a') && (buffer[0] <= 'z')) buffer[0] -= 'a' - 'A'; + + const Common::Rect r(_screenObjects[INVLIST_START + i]); + + // Set the highlighting of the inventory item + if (_highlightedElement == (int)(INVLIST_START + i)) setFontMode(ITEM_HIGHLIGHTED); + else if (_selectedObject == _inventoryList[_topIndex + i]) setFontMode(ITEM_SELECTED); + else setFontMode(ITEM_NORMAL); + + // Write out it's description + _vm->_font->current()->writeString(destSurface, buffer, r.left, r.top, r.width(), 0); + } + + // Handle the display of any currently selected object + if (_objectSprites) { + // Display object sprite. Note that the frame number isn't used directly, because it would result + // in too fast an animation + M4Sprite *spr = _objectSprites->getFrame(_objectFrameNumber / INV_ANIM_FRAME_SPEED); + spr->copyTo(destSurface, INVENTORY_X, INVENTORY_Y, 0); + + if (!_madsVm->globals()->_config.invObjectsStill && !dialogVisible) { + // If objects need to be animated, move to the next frame + if (++_objectFrameNumber >= (_objectSprites->getCount() * INV_ANIM_FRAME_SPEED)) + _objectFrameNumber = 0; + } + + // List the vocab actions for the currently selected object + MadsObject *obj = _madsVm->globals()->getObject(_selectedObject); + int yIndex = MIN(_highlightedElement - VOCAB_START, obj->vocabCount - 1); + + for (int i = 0; i < obj->vocabCount; ++i) { + const Common::Rect r(_screenObjects[VOCAB_START + i]); + + // Get the vocab description and capitalise it + const char *descStr = _madsVm->globals()->getVocab(obj->vocabList[i].vocabId); + strcpy(buffer, descStr); + if ((buffer[0] >= 'a') && (buffer[0] <= 'z')) buffer[0] -= 'a' - 'A'; + + // Set the highlighting and display the entry + setFontMode((i == yIndex) ? ITEM_HIGHLIGHTED : ITEM_NORMAL); + _vm->_font->current()->writeString(destSurface, buffer, r.left, r.top, r.width(), 0); + } + } +} + +bool MadsInterfaceView::onEvent(M4EventType eventType, int32 param1, int x, int y, bool &captureEvents) { + MadsAction &act = _madsVm->scene()->getAction(); + + // If the mouse isn't being held down, then reset the repeated scroll timer + if (eventType != MEVENT_LEFT_HOLD) + _nextScrollerTicks = 0; + + // Handle various event types + switch (eventType) { + case MEVENT_MOVE: + // If the cursor isn't in "wait mode", don't do any processing + if (_vm->_mouse->getCursorNum() == CURSOR_WAIT) + return true; + + // Ensure the cursor is the standard arrow + _vm->_mouse->setCursorNum(CURSOR_ARROW); + + // Check if any interface element is currently highlighted + _highlightedElement = _screenObjects.find(Common::Point(x, y)); + + return true; + + case MEVENT_LEFT_CLICK: + // Left mouse click + { + // Check if an inventory object was selected + if ((_highlightedElement >= INVLIST_START) && (_highlightedElement < (INVLIST_START + 5))) { + // Ensure there is an inventory item listed in that cell + uint idx = _highlightedElement - INVLIST_START; + if ((_topIndex + idx) < _inventoryList.size()) { + // Set the selected object + setSelectedObject(_inventoryList[_topIndex + idx]); + } + } else if ((_highlightedElement >= ACTIONS_START) && (_highlightedElement < (ACTIONS_START + 10))) { + // A standard action was selected + int verbId = kVerbLook + (_highlightedElement - ACTIONS_START); + warning("Selected action #%d", verbId); + + } else if ((_highlightedElement >= VOCAB_START) && (_highlightedElement < (VOCAB_START + 5))) { + // A vocab action was selected + MadsObject *obj = _madsVm->globals()->getObject(_selectedObject); + int vocabIndex = MIN(_highlightedElement - VOCAB_START, obj->vocabCount - 1); + if (vocabIndex >= 0) { + act._actionMode = ACTMODE_OBJECT; + act._actionMode2 = ACTMODE2_2; + act._flags1 = obj->vocabList[1].flags1; + act._flags2 = obj->vocabList[1].flags2; + + act._currentHotspot = _selectedObject; + act._articleNumber = act._flags2; + } + } + } + return true; + + case MEVENT_LEFT_HOLD: + // Left mouse hold + // Handle the scroller - the up/down buttons allow for multiple actions whilst the mouse is held down + if ((_highlightedElement == SCROLL_UP) || (_highlightedElement == SCROLL_DOWN)) { + if ((_nextScrollerTicks == 0) || (g_system->getMillis() >= _nextScrollerTicks)) { + // Handle scroll up/down action + _nextScrollerTicks = g_system->getMillis() + SCROLLER_DELAY; + + if ((_highlightedElement == SCROLL_UP) && (_topIndex > 0)) + --_topIndex; + if ((_highlightedElement == SCROLL_DOWN) && (_topIndex < (int)(_inventoryList.size() - 1))) + ++_topIndex; } } + return true; + + case MEVENT_LEFT_DRAG: + // Left mouse drag + // Handle the the the scroller area that can be dragged to adjust the top displayed index + if (_highlightedElement == SCROLL_SCROLLER) { + // Calculate the new top index based on the Y position + const Common::Rect r(_screenObjects[SCROLL_SCROLLER]); + _topIndex = CLIP((int)(_inventoryList.size() - 1) * (y - r.top - 2) / (r.height() - 5), + 0, (int)_inventoryList.size() - 1); + } + return true; + + case KEVENT_KEY: + if (_cheatKeyCtr == CHEAT_SEQUENCE_MAX) + handleCheatKey(param1); + handleKeypress(param1); + return true; + + default: + break; } - // TODO: code from 'loc_244ec' onwards + return false; +} + +bool MadsInterfaceView::handleCheatKey(int32 keycode) { + switch (keycode) { + case Common::KEYCODE_SPACE: + // TODO: Move player to current destination + return true; + + case Common::KEYCODE_t | (Common::KEYCODE_LALT): + case Common::KEYCODE_t | (Common::KEYCODE_RALT): + { + // Teleport to room + //Scene *sceneView = (Scene *)vm->_viewManager->getView(VIEWID_SCENE); + + + return true; + } + + default: + break; + } + + return false; +} + +const char *CHEAT_SEQUENCE = "widepipe"; + +bool MadsInterfaceView::handleKeypress(int32 keycode) { + int flags = keycode >> 24; + int kc = keycode & 0xffff; + + // Capitalise the letter if necessary + if (_cheatKeyCtr < CHEAT_SEQUENCE_MAX) { + if ((flags & Common::KBD_CTRL) && (kc == CHEAT_SEQUENCE[_cheatKeyCtr])) { + ++_cheatKeyCtr; + if (_cheatKeyCtr == CHEAT_SEQUENCE_MAX) + Dialog::display(_vm, 22, cheatingEnabledDesc); + return true; + } else { + _cheatKeyCtr = 0; + } + } + + // Handle the various keys + if ((keycode == Common::KEYCODE_ESCAPE) || (keycode == Common::KEYCODE_F1)) { + // Game menu + _madsVm->globals()->dialogType = DIALOG_GAME_MENU; + leaveScene(); + return false; + } else if (flags & Common::KBD_CTRL) { + // Handling of the different control key combinations + switch (kc) { + case Common::KEYCODE_i: + // Mouse to inventory + warning("TODO: Mouse to inventory"); + break; + + case Common::KEYCODE_k: + // Toggle hotspots + warning("TODO: Toggle hotspots"); + break; + + case Common::KEYCODE_p: + // Player stats + warning("TODO: Player stats"); + break; + + case Common::KEYCODE_q: + // Quit game + break; + + case Common::KEYCODE_s: + // Activate sound + warning("TODO: Activate sound"); + break; + + case Common::KEYCODE_u: + // Rotate player + warning("TODO: Rotate player"); + break; + + case Common::KEYCODE_v: { + // Release version + Dialog *dlg = new Dialog(_vm, GameReleaseInfoStr, GameReleaseTitleStr); + _vm->_viewManager->addView(dlg); + _vm->_viewManager->moveToFront(dlg); + return false; + } + + default: + break; + } + } else if ((flags & Common::KBD_ALT) && (kc == Common::KEYCODE_q)) { + // Quit Game + + } else { + // Standard keypresses + switch (kc) { + case Common::KEYCODE_F2: + // Save game + _madsVm->globals()->dialogType = DIALOG_SAVE; + leaveScene(); + break; + case Common::KEYCODE_F3: + // Restore game + _madsVm->globals()->dialogType = DIALOG_RESTORE; + leaveScene(); + break; + } + } +//DIALOG_OPTIONS + return false; +} + +void MadsInterfaceView::leaveScene() { + // Close the scene + View *view = _madsVm->_viewManager->getView(VIEWID_SCENE); + _madsVm->_viewManager->deleteView(view); } -*/ } // End of namespace M4 diff --git a/engines/m4/mads_scene.h b/engines/m4/mads_scene.h index c8a0da3aea..0269de75c8 100644 --- a/engines/m4/mads_scene.h +++ b/engines/m4/mads_scene.h @@ -33,27 +33,24 @@ namespace M4 { #define INTERFACE_HEIGHT 106 - +class MadsInterfaceView; class MadsSceneResources: public SceneResources { public: int sceneId; int artFileNum; - int field_4; + int drawStyle; int width; int height; - - int objectCount; - MadsObject objects[32]; + Common::Array<MadsObject> objects; + Common::Array<Common::String> setNames; - int walkSize; - byte *walkData; Common::Point playerPos; int playerDir; - MadsSceneResources() { walkSize = 0; walkData = NULL; playerDir = 0; } - ~MadsSceneResources() { delete walkData; } - void load(int sceneId); + MadsSceneResources() { playerDir = 0; } + ~MadsSceneResources() {} + void load(int sceneId, const char *resName, int v0, M4Surface *depthSurface, M4Surface *surface); }; enum MadsActionMode {ACTMODE_NONE = 0, ACTMODE_VERB = 1, ACTMODE_OBJECT = 3, ACTMODE_TALK = 6}; @@ -96,6 +93,7 @@ private: MadsEngine *_vm; MadsSceneResources _sceneResources; MadsAction _action; + Animation *_activeAnimation; MadsSceneLogic _sceneLogic; SpriteAsset *_playerSprites; @@ -130,6 +128,7 @@ public: int loadSceneSpriteSet(const char *setName); void loadPlayerSprites(const char *prefix); void showMADSV2TextBox(char *text, int x, int y, char *faceName); + void loadAnimation(const Common::String &animName, int v0); MadsInterfaceView *getInterface() { return (MadsInterfaceView *)_interfaceSurface; } MadsSceneResources &getSceneResources() { return _sceneResources; } @@ -137,6 +136,56 @@ public: void setStatusText(const char *text) {}//***DEPRECATED*** }; +#define CHEAT_SEQUENCE_MAX 8 + +class IntegerList : public Common::Array<int> { +public: + int indexOf(int v) { + for (uint i = 0; i < size(); ++i) + if (operator [](i) == v) + return i; + return -1; + } +}; + +enum InterfaceFontMode {ITEM_NORMAL, ITEM_HIGHLIGHTED, ITEM_SELECTED}; + +enum InterfaceObjects {ACTIONS_START = 0, SCROLL_UP = 10, SCROLL_SCROLLER = 11, SCROLL_DOWN = 12, + INVLIST_START = 13, VOCAB_START = 18}; + +class MadsInterfaceView : public GameInterfaceView { +private: + IntegerList _inventoryList; + RectList _screenObjects; + int _highlightedElement; + int _topIndex; + uint32 _nextScrollerTicks; + int _cheatKeyCtr; + + // Object display fields + int _selectedObject; + SpriteAsset *_objectSprites; + RGBList *_objectPalData; + int _objectFrameNumber; + + void setFontMode(InterfaceFontMode newMode); + bool handleCheatKey(int32 keycode); + bool handleKeypress(int32 keycode); + void leaveScene(); +public: + MadsInterfaceView(MadsM4Engine *vm); + ~MadsInterfaceView(); + + virtual void initialise(); + virtual void setSelectedObject(int objectNumber); + virtual void addObjectToInventory(int objectNumber); + int getSelectedObject() { return _selectedObject; } + int getInventoryObject(int objectIndex) { return _inventoryList[objectIndex]; } + + void onRefresh(RectList *rects, M4Surface *destSurface); + bool onEvent(M4EventType eventType, int32 param1, int x, int y, bool &captureEvents); +}; + } // End of namespace M4 #endif diff --git a/engines/m4/mads_views.cpp b/engines/m4/mads_views.cpp index e82a9976a5..c7b4f76a00 100644 --- a/engines/m4/mads_views.cpp +++ b/engines/m4/mads_views.cpp @@ -24,6 +24,7 @@ */ #include "m4/m4_views.h" +#include "m4/animation.h" #include "m4/dialogs.h" #include "m4/events.h" #include "m4/font.h" @@ -36,10 +37,19 @@ namespace M4 { -static const int INV_ANIM_FRAME_SPEED = 2; -static const int INVENTORY_X = 160; -static const int INVENTORY_Y = 159; -static const int SCROLLER_DELAY = 200; +bool MadsSpriteSlot::operator==(const SpriteSlotSubset &other) const { + return (spriteListIndex == other.spriteListIndex) && (frameNumber == other.frameNumber) && + (xp == other.xp) && (yp == other.yp) && (depth == other.depth) && (scale == other.scale); +} + +void MadsSpriteSlot::copy(const SpriteSlotSubset &other) { + spriteListIndex = other.spriteListIndex; + frameNumber = other.frameNumber; + xp = other.xp; + yp = other.yp; + depth = other.depth; + scale = other.scale; +} //-------------------------------------------------------------------------- @@ -52,14 +62,21 @@ MadsSpriteSlots::MadsSpriteSlots(MadsView &owner): _owner(owner) { startIndex = 0; } +MadsSpriteSlots::~MadsSpriteSlots() { + for (uint i = 0; i < _sprites.size(); ++i) + delete _sprites[i]; +} + void MadsSpriteSlots::clear() { _owner._textDisplay.clear(); + for (uint i = 0; i < _sprites.size(); ++i) + delete _sprites[i]; _sprites.clear(); // Reset the sprite slots list back to a single entry for a full screen refresh startIndex = 1; _entries[0].spriteType = FULL_SCREEN_REFRESH; - _entries[0].timerIndex = -1; + _entries[0].seqIndex = -1; } int MadsSpriteSlots::getIndex() { @@ -74,20 +91,31 @@ int MadsSpriteSlots::addSprites(const char *resName) { Common::SeekableReadStream *data = _vm->res()->get(resName); SpriteAsset *spriteSet = new SpriteAsset(_vm, data, data->size(), resName); spriteSet->translate(_madsVm->_palette); + assert(spriteSet != NULL); - _sprites.push_back(SpriteList::value_type(spriteSet)); + _sprites.push_back(spriteSet); _vm->res()->toss(resName); return _sprites.size() - 1; } +void MadsSpriteSlots::deleteSprites(int listIndex) { + if (listIndex < 0) + return; + + delete _sprites[listIndex]; + _sprites[listIndex] = NULL; + if (listIndex == ((int)_sprites.size() - 1)) + _sprites.remove_at(listIndex); +} + /* * Deletes the sprite slot with the given timer entry */ -void MadsSpriteSlots::deleteTimer(int timerIndex) { +void MadsSpriteSlots::deleteTimer(int seqIndex) { for (int idx = 0; idx < startIndex; ++idx) { - if (_entries[idx].timerIndex == timerIndex) - _entries[idx].spriteType = -1; + if (_entries[idx].seqIndex == seqIndex) + _entries[idx].spriteType = EXPIRED_SPRITE; } } @@ -128,10 +156,10 @@ void MadsSpriteSlots::drawBackground() { if (_entries[i].depth <= 1) { // No depth, so simply copy the frame onto the background - frame->copyTo(_owner._bgSurface, xp, yp); + frame->copyTo(_owner._bgSurface, xp, yp, 0); } else { // Depth was specified, so draw frame using scene's depth information - frame->copyTo(_owner._bgSurface, xp, yp, _entries[i].depth, _owner._depthSurface, 100); + frame->copyTo(_owner._bgSurface, xp, yp, _entries[i].depth, _owner._depthSurface, 100, 0); } } } @@ -142,7 +170,7 @@ void MadsSpriteSlots::drawBackground() { _owner._dirtyAreas[i].active = false; } -void MadsSpriteSlots::drawForeground(View *view) { +void MadsSpriteSlots::drawForeground(View *view, int yOffset) { DepthList depthList; // Get a list of sprite object depths for active objects @@ -162,13 +190,13 @@ void MadsSpriteSlots::drawForeground(View *view) { DepthEntry &de = *i; MadsSpriteSlot &slot = _entries[de.index]; assert(slot.spriteListIndex < (int)_sprites.size()); - SpriteAsset &spriteSet = *_sprites[slot.spriteListIndex].get(); + SpriteAsset &spriteSet = *_sprites[slot.spriteListIndex]; if (slot.scale < 100) { // Minimalised drawing assert(slot.spriteListIndex < (int)_sprites.size()); - M4Sprite *spr = spriteSet.getFrame(slot.frameNumber - 1); - spr->copyTo(view, slot.xp, slot.yp, slot.depth, _owner._depthSurface, slot.scale, 0); + M4Sprite *spr = spriteSet.getFrame((slot.frameNumber & 0x7fff) - 1); + spr->copyTo(view, slot.xp, slot.yp + yOffset, slot.depth, _owner._depthSurface, slot.scale, 0); } else { int xp, yp; M4Sprite *spr = spriteSet.getFrame(slot.frameNumber - 1); @@ -183,10 +211,10 @@ void MadsSpriteSlots::drawForeground(View *view) { if (slot.depth > 1) { // Draw the frame with depth processing - spr->copyTo(view, xp, yp, slot.depth, _owner._depthSurface, 100, 0); + spr->copyTo(view, xp, yp + yOffset, slot.depth, _owner._depthSurface, 100, 0); } else { // No depth, so simply draw the image - spr->copyTo(view, xp, yp, 0); + spr->copyTo(view, xp, yp + yOffset, 0); } } } @@ -204,6 +232,16 @@ void MadsSpriteSlots::setDirtyAreas() { } /** + * Flags the entire screen to be redrawn during the next drawing cycle + */ +void MadsSpriteSlots::fullRefresh() { + int idx = getIndex(); + + _entries[idx].spriteType = FULL_SCREEN_REFRESH; + _entries[idx].seqIndex = -1; +} + +/** * Removes any sprite slots that are no longer needed */ void MadsSpriteSlots::cleanUp() { @@ -288,13 +326,12 @@ void MadsTextDisplay::setDirtyAreas2() { } } -void MadsTextDisplay::draw(View *view) { +void MadsTextDisplay::draw(View *view, int yOffset) { for (uint idx = 0; idx < _entries.size(); ++idx) { if (_entries[idx].active && (_entries[idx].expire >= 0)) { - _entries[idx].font->setColours(_entries[idx].colour1, - (_entries[idx].colour2 == 0) ? _entries[idx].colour1 : _entries[idx].colour2, 0xff); + _entries[idx].font->setColours(_entries[idx].colour1, _entries[idx].colour2, 0); _entries[idx].font->writeString(view, _entries[idx].msg, - _entries[idx].bounds.left, _entries[idx].bounds.top, _entries[idx].bounds.width(), + _entries[idx].bounds.left, _entries[idx].bounds.top + yOffset, _entries[idx].bounds.width(), _entries[idx].spacing); } } @@ -324,12 +361,13 @@ void MadsTextDisplay::cleanUp() { MadsKernelMessageList::MadsKernelMessageList(MadsView &owner): _owner(owner) { for (int i = 0; i < TIMED_TEXT_SIZE; ++i) { - MadsKernelMessageListEntry rec; + MadsKernelMessageEntry rec; _entries.push_back(rec); } _owner._textSpacing = -1; _talkFont = _vm->_font->getFont(FONT_CONVERSATION_MADS); + word_8469E = 0; } void MadsKernelMessageList::clear() { @@ -340,20 +378,20 @@ void MadsKernelMessageList::clear() { _talkFont = _vm->_font->getFont(FONT_CONVERSATION_MADS); } -int MadsKernelMessageList::add(const Common::Point &pt, uint fontColour, uint8 flags, uint8 v2, uint32 timeout, const char *msg) { +int MadsKernelMessageList::add(const Common::Point &pt, uint fontColour, uint8 flags, uint8 abortTimers, uint32 timeout, const char *msg) { // Find a free slot uint idx = 0; while ((idx < _entries.size()) && ((_entries[idx].flags & KMSG_ACTIVE) != 0)) ++idx; if (idx == _entries.size()) { - if (v2 == 0) + if (abortTimers == 0) return -1; error("MadsKernelList overflow"); } - MadsKernelMessageListEntry &rec = _entries[idx]; - rec.msg = msg; + MadsKernelMessageEntry &rec = _entries[idx]; + strcpy(rec.msg, msg); rec.flags = flags | KMSG_ACTIVE; rec.colour1 = fontColour & 0xff; rec.colour2 = fontColour >> 8; @@ -361,37 +399,37 @@ int MadsKernelMessageList::add(const Common::Point &pt, uint fontColour, uint8 f rec.textDisplayIndex = -1; rec.timeout = timeout; rec.frameTimer = _madsVm->_currentTimer; - rec.field_1C = v2; + rec.abortTimers = abortTimers; rec.abortMode = _owner._abortTimersMode2; for (int i = 0; i < 3; ++i) rec.actionNouns[i] = _madsVm->scene()->actionNouns[i]; - if (flags & KMSG_2) + if (flags & KMSG_OWNER_TIMEOUT) rec.frameTimer = _owner._ticksAmount + _owner._newTimeout; return idx; } -int MadsKernelMessageList::addQuote(int quoteId, int v2, uint32 timeout) { +int MadsKernelMessageList::addQuote(int quoteId, int abortTimers, uint32 timeout) { const char *quoteStr = _madsVm->globals()->getQuote(quoteId); - return add(Common::Point(0, 0), 0x1110, KMSG_2 | KMSG_20, v2, timeout, quoteStr); + return add(Common::Point(0, 0), 0x1110, KMSG_OWNER_TIMEOUT | KMSG_CENTER_ALIGN, abortTimers, timeout, quoteStr); } -void MadsKernelMessageList::unk1(int msgIndex, int v1, int v2) { +void MadsKernelMessageList::scrollMessage(int msgIndex, int numTicks, bool quoted) { if (msgIndex < 0) return; - _entries[msgIndex].flags |= (v2 == 0) ? KMSG_8 : (KMSG_8 | KMSG_1); + _entries[msgIndex].flags |= quoted ? (KMSG_SCROLL | KMSG_QUOTED) : KMSG_SCROLL; _entries[msgIndex].msgOffset = 0; - _entries[msgIndex].field_E = v1; + _entries[msgIndex].numTicks = numTicks; _entries[msgIndex].frameTimer2 = _madsVm->_currentTimer; const char *msgP = _entries[msgIndex].msg; _entries[msgIndex].asciiChar = *msgP; _entries[msgIndex].asciiChar2 = *(msgP + 1); - if (_entries[msgIndex].flags & KMSG_2) + if (_entries[msgIndex].flags & KMSG_OWNER_TIMEOUT) _entries[msgIndex].frameTimer2 = _owner._ticksAmount + _owner._newTimeout; _entries[msgIndex].frameTimer = _entries[msgIndex].frameTimer2; @@ -399,18 +437,18 @@ void MadsKernelMessageList::unk1(int msgIndex, int v1, int v2) { void MadsKernelMessageList::setSeqIndex(int msgIndex, int seqIndex) { if (msgIndex >= 0) { - _entries[msgIndex].flags |= KMSG_4; + _entries[msgIndex].flags |= KMSG_SEQ_ENTRY; _entries[msgIndex].sequenceIndex = seqIndex; } } void MadsKernelMessageList::remove(int msgIndex) { - MadsKernelMessageListEntry &rec = _entries[msgIndex]; + MadsKernelMessageEntry &rec = _entries[msgIndex]; if (rec.flags & KMSG_ACTIVE) { - if (rec.flags & KMSG_8) { - //*(rec.msg + rec.msgOffset) = rec.asciiChar; - //*(rec.msg + rec.msgOffset + 1) = rec.asciiChar2; + if (rec.flags & KMSG_SCROLL) { + *(rec.msg + rec.msgOffset) = rec.asciiChar; + *(rec.msg + rec.msgOffset + 1) = rec.asciiChar2; } if (rec.textDisplayIndex >= 0) @@ -427,6 +465,136 @@ void MadsKernelMessageList::reset() { // sub_20454 } +void MadsKernelMessageList::update() { + uint32 currentTimer = _madsVm->_currentTimer; + + for (uint i = 0; i < _entries.size(); ++i) { + if (((_entries[i].flags & KMSG_ACTIVE) != 0) && (currentTimer >= _entries[i].frameTimer)) + processText(i); + } +} + +void MadsKernelMessageList::processText(int msgIndex) { + MadsKernelMessageEntry &msg = _entries[msgIndex]; + uint32 currentTimer = _madsVm->_currentTimer; + bool flag = false; + + if ((msg.flags & KMSG_EXPIRE) != 0) { + _owner._textDisplay.expire(msg.textDisplayIndex); + msg.flags &= !KMSG_ACTIVE; + return; + } + + if ((msg.flags & KMSG_SCROLL) == 0) { + msg.timeout -= 3; + } + + if (msg.flags & KMSG_SEQ_ENTRY) { + MadsSequenceEntry &seqEntry = _owner._sequenceList[msg.sequenceIndex]; + if (seqEntry.doneFlag || !seqEntry.active) + msg.timeout = 0; + } + + if ((msg.timeout <= 0) && (_owner._abortTimers == 0)) { + msg.flags |= KMSG_EXPIRE; + if (msg.abortTimers != 0) { + _owner._abortTimers = msg.abortTimers; + _owner._abortTimersMode = msg.abortMode; + + if (_owner._abortTimersMode != ABORTMODE_1) { + for (int i = 0; i < 3; ++i) + _madsVm->scene()->actionNouns[i] = msg.actionNouns[i]; + } + } + } + + msg.frameTimer = currentTimer + 3; + int x1 = 0, y1 = 0; + + if (msg.flags & KMSG_SEQ_ENTRY) { + MadsSequenceEntry &seqEntry = _owner._sequenceList[msg.sequenceIndex]; + if (!seqEntry.nonFixed) { + SpriteAsset &spriteSet = _owner._spriteSlots.getSprite(seqEntry.spriteListIndex); + M4Sprite *frame = spriteSet.getFrame(seqEntry.frameIndex - 1); + x1 = frame->bounds().left; + y1 = frame->bounds().top; + } else { + x1 = seqEntry.msgPos.x; + y1 = seqEntry.msgPos.y; + } + } + + if (msg.flags & KMSG_OWNER_TIMEOUT) { + if (word_8469E != 0) { + // TODO: Figure out various flags + } else { + x1 = 160; + y1 = 78; + } + } + + x1 += msg.position.x; + y1 += msg.position.y; + + if ((msg.flags & KMSG_SCROLL) && (msg.frameTimer >= currentTimer)) { + msg.msg[msg.msgOffset] = msg.asciiChar; + char *msgP = &msg.msg[++msg.msgOffset]; + *msgP = msg.asciiChar2; + + msg.asciiChar = *msgP; + msg.asciiChar2 = *(msgP + 1); + + if (!msg.asciiChar) { + // End of message + *msgP = '\0'; + msg.flags &= ~KMSG_SCROLL; + } else if (msg.flags & KMSG_QUOTED) { + *msgP = '"'; + *(msgP + 1) = '\0'; + } + + msg.frameTimer = msg.frameTimer2 = currentTimer + msg.numTicks; + flag = true; + } + + int strWidth = _talkFont->getWidth(msg.msg, _owner._textSpacing); + + if (msg.flags & (KMSG_RIGHT_ALIGN | KMSG_CENTER_ALIGN)) { + x1 -= (msg.flags & KMSG_CENTER_ALIGN) ? strWidth / 2 : strWidth; + } + + // Make sure text appears entirely on-screen + int x2 = x1 + strWidth; + if (x2 > MADS_SURFACE_WIDTH) + x1 -= x2 - MADS_SURFACE_WIDTH; + if (x1 > (MADS_SURFACE_WIDTH - 1)) + x1 = MADS_SURFACE_WIDTH - 1; + if (x1 < 0) + x1 = 0; + + if (y1 > (MADS_SURFACE_HEIGHT - 1)) + y1 = MADS_SURFACE_HEIGHT - 1; + if (y1 < 0) + y1 = 0; + + if (msg.textDisplayIndex >= 0) { + MadsTextDisplayEntry textEntry = _owner._textDisplay[msg.textDisplayIndex]; + + if (flag || (textEntry.bounds.left != x1) || (textEntry.bounds.top != y1)) { + // Mark the associated text entry as deleted, so it can be re-created + _owner._textDisplay.expire(msg.textDisplayIndex); + msg.textDisplayIndex = -1; + } + } + + if (msg.textDisplayIndex < 0) { + // Need to create a new text display entry for this message + int idx = _owner._textDisplay.add(x1, y1, msg.colour1 | (msg.colour2 << 8), _owner._textSpacing, msg.msg, _talkFont); + if (idx >= 0) + msg.textDisplayIndex = idx; + } +} + //-------------------------------------------------------------------------- /** @@ -687,10 +855,10 @@ void MadsDirtyAreas::mergeAreas(int idx1, int idx2) { da1.textActive = true; } -void MadsDirtyAreas::copy(M4Surface *dest, M4Surface *src) { +void MadsDirtyAreas::copy(M4Surface *dest, M4Surface *src, int yOffset) { for (uint i = 0; i < _entries.size(); ++i) { if (_entries[i].active && _entries[i].bounds.isValidRect()) - src->copyTo(dest, _entries[i].bounds, _entries[i].bounds.left, _entries[i].bounds.top); + src->copyTo(dest, _entries[i].bounds, _entries[i].bounds.left, _entries[i].bounds.top + yOffset); } } @@ -725,14 +893,14 @@ bool MadsSequenceList::addSubEntry(int index, SequenceSubEntryMode mode, int fra } int MadsSequenceList::add(int spriteListIndex, int v0, int frameIndex, int triggerCountdown, int delayTicks, int extraTicks, int numTicks, - int height, int width, char field_12, char scale, uint8 depth, int frameInc, SpriteAnimType animType, int numSprites, + int msgX, int msgY, bool nonFixed, char scale, uint8 depth, int frameInc, SpriteAnimType animType, int numSprites, int frameStart) { // Find a free slot - uint timerIndex = 0; - while ((timerIndex < _entries.size()) && (_entries[timerIndex].active)) - ++timerIndex; - if (timerIndex == _entries.size()) + uint seqIndex = 0; + while ((seqIndex < _entries.size()) && (_entries[seqIndex].active)) + ++seqIndex; + if (seqIndex == _entries.size()) error("TimerList full"); if (frameStart <= 0) @@ -743,76 +911,76 @@ int MadsSequenceList::add(int spriteListIndex, int v0, int frameIndex, int trigg frameInc = 0; // Set the list entry fields - _entries[timerIndex].active = true; - _entries[timerIndex].spriteListIndex = spriteListIndex; - _entries[timerIndex].field_2 = v0; - _entries[timerIndex].frameIndex = frameIndex; - _entries[timerIndex].frameStart = frameStart; - _entries[timerIndex].numSprites = numSprites; - _entries[timerIndex].animType = animType; - _entries[timerIndex].frameInc = frameInc; - _entries[timerIndex].depth = depth; - _entries[timerIndex].scale = scale; - _entries[timerIndex].field_12 = field_12; - _entries[timerIndex].width = width; - _entries[timerIndex].height = height; - _entries[timerIndex].numTicks = numTicks; - _entries[timerIndex].extraTicks = extraTicks; - - _entries[timerIndex].timeout = _madsVm->_currentTimer + delayTicks; - - _entries[timerIndex].triggerCountdown = triggerCountdown; - _entries[timerIndex].doneFlag = false; - _entries[timerIndex].field_13 = 0; - _entries[timerIndex].dynamicHotspotIndex = -1; - _entries[timerIndex].entries.count = 0; - _entries[timerIndex].abortMode = _owner._abortTimersMode2; + _entries[seqIndex].active = true; + _entries[seqIndex].spriteListIndex = spriteListIndex; + _entries[seqIndex].field_2 = v0; + _entries[seqIndex].frameIndex = frameIndex; + _entries[seqIndex].frameStart = frameStart; + _entries[seqIndex].numSprites = numSprites; + _entries[seqIndex].animType = animType; + _entries[seqIndex].frameInc = frameInc; + _entries[seqIndex].depth = depth; + _entries[seqIndex].scale = scale; + _entries[seqIndex].nonFixed = nonFixed; + _entries[seqIndex].msgPos.x = msgX; + _entries[seqIndex].msgPos.y = msgY; + _entries[seqIndex].numTicks = numTicks; + _entries[seqIndex].extraTicks = extraTicks; + + _entries[seqIndex].timeout = _madsVm->_currentTimer + delayTicks; + + _entries[seqIndex].triggerCountdown = triggerCountdown; + _entries[seqIndex].doneFlag = false; + _entries[seqIndex].field_13 = 0; + _entries[seqIndex].dynamicHotspotIndex = -1; + _entries[seqIndex].entries.count = 0; + _entries[seqIndex].abortMode = _owner._abortTimersMode2; for (int i = 0; i < 3; ++i) - _entries[timerIndex].actionNouns[i] = _madsVm->scene()->actionNouns[i]; + _entries[seqIndex].actionNouns[i] = _madsVm->scene()->actionNouns[i]; - return timerIndex; + return seqIndex; } -void MadsSequenceList::remove(int timerIndex) { - if (_entries[timerIndex].active) { - if (_entries[timerIndex].dynamicHotspotIndex >= 0) - _owner._dynamicHotspots.remove(_entries[timerIndex].dynamicHotspotIndex); +void MadsSequenceList::remove(int seqIndex) { + if (_entries[seqIndex].active) { + if (_entries[seqIndex].dynamicHotspotIndex >= 0) + _owner._dynamicHotspots.remove(_entries[seqIndex].dynamicHotspotIndex); } - _entries[timerIndex].active = false; - _owner._spriteSlots.deleteTimer(timerIndex); + _entries[seqIndex].active = false; + _owner._spriteSlots.deleteTimer(seqIndex); } -void MadsSequenceList::setSpriteSlot(int timerIndex, MadsSpriteSlot &spriteSlot) { - MadsSequenceEntry &timerEntry = _entries[timerIndex]; - SpriteAsset &sprite = _owner._spriteSlots.getSprite(timerEntry.spriteListIndex); +void MadsSequenceList::setSpriteSlot(int seqIndex, MadsSpriteSlot &spriteSlot) { + MadsSequenceEntry &timerEntry = _entries[seqIndex]; + SpriteAsset &spriteSet = _owner._spriteSlots.getSprite(timerEntry.spriteListIndex); - spriteSlot.spriteType = sprite.getAssetType() == 1 ? BACKGROUND_SPRITE : FOREGROUND_SPRITE; - spriteSlot.timerIndex = timerIndex; + spriteSlot.spriteType = spriteSet.isBackground() ? BACKGROUND_SPRITE : FOREGROUND_SPRITE; + spriteSlot.seqIndex = seqIndex; spriteSlot.spriteListIndex = timerEntry.spriteListIndex; spriteSlot.frameNumber = ((timerEntry.field_2 == 1) ? 0x8000 : 0) | timerEntry.frameIndex; spriteSlot.depth = timerEntry.depth; spriteSlot.scale = timerEntry.scale; - if (timerEntry.field_12 == 0) { - spriteSlot.xp = timerEntry.width; - spriteSlot.yp = timerEntry.height; + if (!timerEntry.nonFixed) { + spriteSlot.xp = timerEntry.msgPos.x; + spriteSlot.yp = timerEntry.msgPos.y; } else { - spriteSlot.xp = sprite.getFrame(timerEntry.frameIndex - 1)->x; - spriteSlot.yp = sprite.getFrame(timerEntry.frameIndex - 1)->y; + spriteSlot.xp = spriteSet.getFrame(timerEntry.frameIndex - 1)->x; + spriteSlot.yp = spriteSet.getFrame(timerEntry.frameIndex - 1)->y; } } -bool MadsSequenceList::loadSprites(int timerIndex) { - MadsSequenceEntry &seqEntry = _entries[timerIndex]; +bool MadsSequenceList::loadSprites(int seqIndex) { + MadsSequenceEntry &seqEntry = _entries[seqIndex]; int slotIndex; bool result = false; int idx = -1; - _owner._spriteSlots.deleteTimer(timerIndex); + _owner._spriteSlots.deleteTimer(seqIndex); if (seqEntry.doneFlag) { - remove(timerIndex); + remove(seqIndex); return false; } @@ -821,7 +989,7 @@ bool MadsSequenceList::loadSprites(int timerIndex) { seqEntry.doneFlag = true; } else if ((slotIndex = _owner._spriteSlots.getIndex()) >= 0) { MadsSpriteSlot &spriteSlot = _owner._spriteSlots[slotIndex]; - setSpriteSlot(timerIndex, spriteSlot); + setSpriteSlot(seqIndex, spriteSlot); int x2 = 0, y2 = 0; @@ -949,8 +1117,8 @@ void MadsSequenceList::delay(uint32 v1, uint32 v2) { } } -void MadsSequenceList::setAnimRange(int timerIndex, int startVal, int endVal) { - MadsSequenceEntry &seqEntry = _entries[timerIndex]; +void MadsSequenceList::setAnimRange(int seqIndex, int startVal, int endVal) { + MadsSequenceEntry &seqEntry = _entries[seqIndex]; SpriteAsset &spriteSet = _owner._spriteSlots.getSprite(seqEntry.spriteListIndex); int numSprites = spriteSet.getCount(); int tempStart = startVal, tempEnd = endVal; @@ -983,10 +1151,28 @@ void MadsSequenceList::setAnimRange(int timerIndex, int startVal, int endVal) { seqEntry.frameIndex = (seqEntry.frameInc < 0) ? tempStart : tempEnd; } +void MadsSequenceList::scan() { + for (uint i = 0; i < _entries.size(); ++i) { + if (!_entries[i].active && (_entries[i].spriteListIndex != -1)) { + int idx = _owner._spriteSlots.getIndex(); + setSpriteSlot(i, _owner._spriteSlots[idx]); + } + } +} + +//-------------------------------------------------------------------------- + +Animation::Animation(MadsM4Engine *vm): _vm(vm) { +} + +Animation::~Animation() { +} + //-------------------------------------------------------------------------- MadsView::MadsView(View *view): _view(view), _dynamicHotspots(*this), _sequenceList(*this), _kernelMessages(*this), _spriteSlots(*this), _dirtyAreas(*this), _textDisplay(*this) { + _textSpacing = -1; _ticksAmount = 3; _newTimeout = 0; @@ -994,9 +1180,15 @@ MadsView::MadsView(View *view): _view(view), _dynamicHotspots(*this), _sequenceL _abortTimers2 = 0; _abortTimersMode = ABORTMODE_0; _abortTimersMode2 = ABORTMODE_0; - + + _yOffset = 0; _depthSurface = NULL; _bgSurface = NULL; + _sceneAnimation = new MadsAnimation(_vm, this); +} + +MadsView::~MadsView() { + delete _sceneAnimation; } void MadsView::refresh() { @@ -1010,7 +1202,7 @@ void MadsView::refresh() { _dirtyAreas.merge(1, DIRTY_AREAS_SIZE); // Copy dirty areas to the main display surface - _dirtyAreas.copy(_view, _bgSurface); + _dirtyAreas.copy(_view, _bgSurface, _yOffset); // Handle dirty areas for foreground objects _spriteSlots.setDirtyAreas(); @@ -1018,10 +1210,10 @@ void MadsView::refresh() { _dirtyAreas.merge(1, DIRTY_AREAS_SIZE); // Draw foreground sprites - _spriteSlots.drawForeground(_view); + _spriteSlots.drawForeground(_view, _yOffset); // Draw text elements onto the view - _textDisplay.draw(_view); + _textDisplay.draw(_view, _yOffset); // Remove any sprite slots that are no longer needed _spriteSlots.cleanUp(); @@ -1030,427 +1222,4 @@ void MadsView::refresh() { _textDisplay.cleanUp(); } -/*-------------------------------------------------------------------------- - * MadsInterfaceView handles the user interface section at the bottom of - * game screens in MADS games - *-------------------------------------------------------------------------- - */ - -MadsInterfaceView::MadsInterfaceView(MadsM4Engine *vm): GameInterfaceView(vm, - Common::Rect(0, MADS_SURFACE_HEIGHT, vm->_screen->width(), vm->_screen->height())) { - _screenType = VIEWID_INTERFACE; - _highlightedElement = -1; - _topIndex = 0; - _selectedObject = -1; - _cheatKeyCtr = 0; - - _objectSprites = NULL; - _objectPalData = NULL; - - /* Set up the rect list for screen elements */ - // Actions - for (int i = 0; i < 10; ++i) - _screenObjects.addRect((i / 5) * 32 + 1, (i % 5) * 8 + MADS_SURFACE_HEIGHT + 2, - ((i / 5) + 1) * 32 + 3, ((i % 5) + 1) * 8 + MADS_SURFACE_HEIGHT + 2); - - // Scroller elements (up arrow, scroller, down arrow) - _screenObjects.addRect(73, 160, 82, 167); - _screenObjects.addRect(73, 168, 82, 190); - _screenObjects.addRect(73, 191, 82, 198); - - // Inventory object names - for (int i = 0; i < 5; ++i) - _screenObjects.addRect(89, 158 + i * 8, 160, 166 + i * 8); - - // Full rectangle area for all vocab actions - for (int i = 0; i < 5; ++i) - _screenObjects.addRect(239, 158 + i * 8, 320, 166 + i * 8); -} - -MadsInterfaceView::~MadsInterfaceView() { - delete _objectSprites; -} - -void MadsInterfaceView::setFontMode(InterfaceFontMode newMode) { - switch (newMode) { - case ITEM_NORMAL: - _vm->_font->setColors(4, 4, 0xff); - break; - case ITEM_HIGHLIGHTED: - _vm->_font->setColors(5, 5, 0xff); - break; - case ITEM_SELECTED: - _vm->_font->setColors(6, 6, 0xff); - break; - } -} - -void MadsInterfaceView::initialise() { - // Build up the inventory list - _inventoryList.clear(); - - for (uint i = 0; i < _madsVm->globals()->getObjectsSize(); ++i) { - MadsObject *obj = _madsVm->globals()->getObject(i); - if (obj->roomNumber == PLAYER_INVENTORY) - _inventoryList.push_back(i); - } - - // If the inventory has at least one object, select it - if (_inventoryList.size() > 0) - setSelectedObject(_inventoryList[0]); -} - -void MadsInterfaceView::setSelectedObject(int objectNumber) { - char resName[80]; - - // Load inventory resource - if (_objectSprites) { - _vm->_palette->deleteRange(_objectPalData); - delete _objectSprites; - } - - // Check to make sure the object is in the inventory, and also visible on-screen - int idx = _inventoryList.indexOf(objectNumber); - if (idx == -1) { - // Object wasn't found, so return - _selectedObject = -1; - return; - } - - // Found the object - if (idx < _topIndex) - _topIndex = idx; - else if (idx >= (_topIndex + 5)) - _topIndex = MAX(0, idx - 4); - - _selectedObject = objectNumber; - sprintf(resName, "*OB%.3dI.SS", objectNumber); - - Common::SeekableReadStream *data = _vm->res()->get(resName); - _objectSprites = new SpriteAsset(_vm, data, data->size(), resName); - _vm->res()->toss(resName); - - // Slot it into available palette space - _objectPalData = _objectSprites->getRgbList(); - _vm->_palette->addRange(_objectPalData); - _objectSprites->translate(_objectPalData, true); - - _objectFrameNumber = 0; -} - -void MadsInterfaceView::addObjectToInventory(int objectNumber) { - if (_inventoryList.indexOf(objectNumber) == -1) { - _madsVm->globals()->getObject(objectNumber)->roomNumber = PLAYER_INVENTORY; - _inventoryList.push_back(objectNumber); - } - - setSelectedObject(objectNumber); -} - -void MadsInterfaceView::onRefresh(RectList *rects, M4Surface *destSurface) { - _vm->_font->setFont(FONT_INTERFACE_MADS); - char buffer[100]; - - // Check to see if any dialog is currently active - bool dialogVisible = _vm->_viewManager->getView(LAYER_DIALOG) != NULL; - - // Highlighting logic for action list - int actionIndex = 0; - for (int x = 0; x < 2; ++x) { - for (int y = 0; y < 5; ++y, ++actionIndex) { - // Determine the font colour depending on whether an item is selected. Note that the first action, - // 'Look', is always 'selected', even when another action is clicked on - setFontMode((_highlightedElement == actionIndex) ? ITEM_HIGHLIGHTED : - ((actionIndex == 0) ? ITEM_SELECTED : ITEM_NORMAL)); - - // Get the verb action and capitalise it - const char *verbStr = _madsVm->globals()->getVocab(kVerbLook + actionIndex); - strcpy(buffer, verbStr); - if ((buffer[0] >= 'a') && (buffer[0] <= 'z')) buffer[0] -= 'a' - 'A'; - - // Display the verb - const Common::Rect r(_screenObjects[actionIndex]); - _vm->_font->writeString(destSurface, buffer, r.left, r.top, r.width(), 0); - } - } - - // Check for highlighting of the scrollbar controls - if ((_highlightedElement == SCROLL_UP) || (_highlightedElement == SCROLL_SCROLLER) || (_highlightedElement == SCROLL_DOWN)) { - // Highlight the control's borders - const Common::Rect r(_screenObjects[_highlightedElement]); - destSurface->frameRect(r, 5); - } - - // Draw the horizontal line in the scroller representing the current top selected - const Common::Rect scroller(_screenObjects[SCROLL_SCROLLER]); - int yP = (_inventoryList.size() < 2) ? 0 : (scroller.height() - 5) * _topIndex / (_inventoryList.size() - 1); - destSurface->setColor(4); - destSurface->hLine(scroller.left + 2, scroller.right - 3, scroller.top + 2 + yP); - - // List inventory items - for (uint i = 0; i < 5; ++i) { - if ((_topIndex + i) >= _inventoryList.size()) - break; - - const char *descStr = _madsVm->globals()->getVocab(_madsVm->globals()->getObject( - _inventoryList[_topIndex + i])->descId); - strcpy(buffer, descStr); - if ((buffer[0] >= 'a') && (buffer[0] <= 'z')) buffer[0] -= 'a' - 'A'; - - const Common::Rect r(_screenObjects[INVLIST_START + i]); - - // Set the highlighting of the inventory item - if (_highlightedElement == (int)(INVLIST_START + i)) setFontMode(ITEM_HIGHLIGHTED); - else if (_selectedObject == _inventoryList[_topIndex + i]) setFontMode(ITEM_SELECTED); - else setFontMode(ITEM_NORMAL); - - // Write out it's description - _vm->_font->writeString(destSurface, buffer, r.left, r.top, r.width(), 0); - } - - // Handle the display of any currently selected object - if (_objectSprites) { - // Display object sprite. Note that the frame number isn't used directly, because it would result - // in too fast an animation - M4Sprite *spr = _objectSprites->getFrame(_objectFrameNumber / INV_ANIM_FRAME_SPEED); - spr->copyTo(destSurface, INVENTORY_X, INVENTORY_Y, 0); - - if (!_madsVm->globals()->_config.invObjectsStill && !dialogVisible) { - // If objects need to be animated, move to the next frame - if (++_objectFrameNumber >= (_objectSprites->getCount() * INV_ANIM_FRAME_SPEED)) - _objectFrameNumber = 0; - } - - // List the vocab actions for the currently selected object - MadsObject *obj = _madsVm->globals()->getObject(_selectedObject); - int yIndex = MIN(_highlightedElement - VOCAB_START, obj->vocabCount - 1); - - for (int i = 0; i < obj->vocabCount; ++i) { - const Common::Rect r(_screenObjects[VOCAB_START + i]); - - // Get the vocab description and capitalise it - const char *descStr = _madsVm->globals()->getVocab(obj->vocabList[i].vocabId); - strcpy(buffer, descStr); - if ((buffer[0] >= 'a') && (buffer[0] <= 'z')) buffer[0] -= 'a' - 'A'; - - // Set the highlighting and display the entry - setFontMode((i == yIndex) ? ITEM_HIGHLIGHTED : ITEM_NORMAL); - _vm->_font->writeString(destSurface, buffer, r.left, r.top, r.width(), 0); - } - } -} - -bool MadsInterfaceView::onEvent(M4EventType eventType, int32 param1, int x, int y, bool &captureEvents) { - MadsAction &act = _madsVm->scene()->getAction(); - - // If the mouse isn't being held down, then reset the repeated scroll timer - if (eventType != MEVENT_LEFT_HOLD) - _nextScrollerTicks = 0; - - // Handle various event types - switch (eventType) { - case MEVENT_MOVE: - // If the cursor isn't in "wait mode", don't do any processing - if (_vm->_mouse->getCursorNum() == CURSOR_WAIT) - return true; - - // Ensure the cursor is the standard arrow - _vm->_mouse->setCursorNum(CURSOR_ARROW); - - // Check if any interface element is currently highlighted - _highlightedElement = _screenObjects.find(Common::Point(x, y)); - - return true; - - case MEVENT_LEFT_CLICK: - // Left mouse click - { - // Check if an inventory object was selected - if ((_highlightedElement >= INVLIST_START) && (_highlightedElement < (INVLIST_START + 5))) { - // Ensure there is an inventory item listed in that cell - uint idx = _highlightedElement - INVLIST_START; - if ((_topIndex + idx) < _inventoryList.size()) { - // Set the selected object - setSelectedObject(_inventoryList[_topIndex + idx]); - } - } else if ((_highlightedElement >= ACTIONS_START) && (_highlightedElement < (ACTIONS_START + 10))) { - // A standard action was selected - int verbId = kVerbLook + (_highlightedElement - ACTIONS_START); - warning("Selected action #%d", verbId); - - } else if ((_highlightedElement >= VOCAB_START) && (_highlightedElement < (VOCAB_START + 5))) { - // A vocab action was selected - MadsObject *obj = _madsVm->globals()->getObject(_selectedObject); - int vocabIndex = MIN(_highlightedElement - VOCAB_START, obj->vocabCount - 1); - if (vocabIndex >= 0) { - act._actionMode = ACTMODE_OBJECT; - act._actionMode2 = ACTMODE2_2; - act._flags1 = obj->vocabList[1].flags1; - act._flags2 = obj->vocabList[1].flags2; - - act._currentHotspot = _selectedObject; - act._articleNumber = act._flags2; - } - } - } - return true; - - case MEVENT_LEFT_HOLD: - // Left mouse hold - // Handle the scroller - the up/down buttons allow for multiple actions whilst the mouse is held down - if ((_highlightedElement == SCROLL_UP) || (_highlightedElement == SCROLL_DOWN)) { - if ((_nextScrollerTicks == 0) || (g_system->getMillis() >= _nextScrollerTicks)) { - // Handle scroll up/down action - _nextScrollerTicks = g_system->getMillis() + SCROLLER_DELAY; - - if ((_highlightedElement == SCROLL_UP) && (_topIndex > 0)) - --_topIndex; - if ((_highlightedElement == SCROLL_DOWN) && (_topIndex < (int)(_inventoryList.size() - 1))) - ++_topIndex; - } - } - return true; - - case MEVENT_LEFT_DRAG: - // Left mouse drag - // Handle the the the scroller area that can be dragged to adjust the top displayed index - if (_highlightedElement == SCROLL_SCROLLER) { - // Calculate the new top index based on the Y position - const Common::Rect r(_screenObjects[SCROLL_SCROLLER]); - _topIndex = CLIP((int)(_inventoryList.size() - 1) * (y - r.top - 2) / (r.height() - 5), - 0, (int)_inventoryList.size() - 1); - } - return true; - - case KEVENT_KEY: - if (_cheatKeyCtr == CHEAT_SEQUENCE_MAX) - handleCheatKey(param1); - handleKeypress(param1); - return true; - - default: - break; - } - - return false; -} - -bool MadsInterfaceView::handleCheatKey(int32 keycode) { - switch (keycode) { - case Common::KEYCODE_SPACE: - // TODO: Move player to current destination - return true; - - case Common::KEYCODE_t | (Common::KEYCODE_LALT): - case Common::KEYCODE_t | (Common::KEYCODE_RALT): - { - // Teleport to room - //Scene *sceneView = (Scene *)vm->_viewManager->getView(VIEWID_SCENE); - - - return true; - } - - default: - break; - } - - return false; -} - -const char *CHEAT_SEQUENCE = "widepipe"; - -bool MadsInterfaceView::handleKeypress(int32 keycode) { - int flags = keycode >> 24; - int kc = keycode & 0xffff; - - // Capitalise the letter if necessary - if (_cheatKeyCtr < CHEAT_SEQUENCE_MAX) { - if ((flags & Common::KBD_CTRL) && (kc == CHEAT_SEQUENCE[_cheatKeyCtr])) { - ++_cheatKeyCtr; - if (_cheatKeyCtr == CHEAT_SEQUENCE_MAX) - Dialog::display(_vm, 22, cheatingEnabledDesc); - return true; - } else { - _cheatKeyCtr = 0; - } - } - - // Handle the various keys - if ((keycode == Common::KEYCODE_ESCAPE) || (keycode == Common::KEYCODE_F1)) { - // Game menu - _madsVm->globals()->dialogType = DIALOG_GAME_MENU; - leaveScene(); - return false; - } else if (flags & Common::KBD_CTRL) { - // Handling of the different control key combinations - switch (kc) { - case Common::KEYCODE_i: - // Mouse to inventory - warning("TODO: Mouse to inventory"); - break; - - case Common::KEYCODE_k: - // Toggle hotspots - warning("TODO: Toggle hotspots"); - break; - - case Common::KEYCODE_p: - // Player stats - warning("TODO: Player stats"); - break; - - case Common::KEYCODE_q: - // Quit game - break; - - case Common::KEYCODE_s: - // Activate sound - warning("TODO: Activate sound"); - break; - - case Common::KEYCODE_u: - // Rotate player - warning("TODO: Rotate player"); - break; - - case Common::KEYCODE_v: { - // Release version - Dialog *dlg = new Dialog(_vm, GameReleaseInfoStr, GameReleaseTitleStr); - _vm->_viewManager->addView(dlg); - _vm->_viewManager->moveToFront(dlg); - return false; - } - - default: - break; - } - } else if ((flags & Common::KBD_ALT) && (kc == Common::KEYCODE_q)) { - // Quit Game - - } else { - // Standard keypresses - switch (kc) { - case Common::KEYCODE_F2: - // Save game - _madsVm->globals()->dialogType = DIALOG_SAVE; - leaveScene(); - break; - case Common::KEYCODE_F3: - // Restore game - _madsVm->globals()->dialogType = DIALOG_RESTORE; - leaveScene(); - break; - } - } -//DIALOG_OPTIONS - return false; -} - -void MadsInterfaceView::leaveScene() { - // Close the scene - View *view = _madsVm->_viewManager->getView(VIEWID_SCENE); - _madsVm->_viewManager->deleteView(view); -} - } // End of namespace M4 diff --git a/engines/m4/mads_views.h b/engines/m4/mads_views.h index 3b64cc77f2..29adb7048a 100644 --- a/engines/m4/mads_views.h +++ b/engines/m4/mads_views.h @@ -34,19 +34,24 @@ namespace M4 { -#define MADS_SURFACE_WIDTH 320 -#define MADS_SURFACE_HEIGHT 156 -#define MADS_SCREEN_HEIGHT 200 -#define MADS_Y_OFFSET ((MADS_SCREEN_HEIGHT - MADS_SURFACE_HEIGHT) / 2) - class MadsView; enum AbortTimerMode {ABORTMODE_0 = 0, ABORTMODE_1 = 1, ABORTMODE_2 = 2}; +class SpriteSlotSubset { +public: + int spriteListIndex; + int frameNumber; + int xp; + int yp; + int depth; + int scale; +}; + class MadsSpriteSlot { public: int spriteType; - int timerIndex; + int seqIndex; int spriteListIndex; int frameNumber; int xp; @@ -55,25 +60,27 @@ public: int scale; MadsSpriteSlot() { } + + bool operator==(const SpriteSlotSubset &other) const; + void copy(const SpriteSlotSubset &other); }; #define SPRITE_SLOTS_SIZE 50 enum SpriteIdSpecial { - BACKGROUND_SPRITE = -4, FULL_SCREEN_REFRESH = -2, FOREGROUND_SPRITE = 1 + BACKGROUND_SPRITE = -4, FULL_SCREEN_REFRESH = -2, EXPIRED_SPRITE = -1, SPRITE_ZERO = 0, FOREGROUND_SPRITE = 1 }; -typedef Common::Array<Common::SharedPtr<SpriteAsset> > SpriteList; - class MadsSpriteSlots { private: MadsView &_owner; Common::Array<MadsSpriteSlot> _entries; - SpriteList _sprites; + Common::Array<SpriteAsset *> _sprites; public: int startIndex; MadsSpriteSlots(MadsView &owner); + ~MadsSpriteSlots(); MadsSpriteSlot &operator[](int idx) { assert(idx < SPRITE_SLOTS_SIZE); @@ -81,17 +88,19 @@ public: } SpriteAsset &getSprite(int idx) { assert(idx < (int)_sprites.size()); - return *_sprites[idx].get(); + return *_sprites[idx]; } int getIndex(); int addSprites(const char *resName); + void deleteSprites(int listIndex); void clear(); - void deleteTimer(int timerIndex); + void deleteTimer(int seqIndex); void drawBackground(); - void drawForeground(View *view); + void drawForeground(View *view, int yOffset); void setDirtyAreas(); + void fullRefresh(); void cleanUp(); }; @@ -130,18 +139,19 @@ public: int add(int xp, int yp, uint fontColour, int charSpacing, const char *msg, Font *font); void clear(); - void draw(View *view); + void draw(View *view, int yOffset); void setDirtyAreas(); void setDirtyAreas2(); void cleanUp(); }; #define TIMED_TEXT_SIZE 10 -#define TEXT_4A_SIZE 30 +#define INDEFINITE_TIMEOUT 9999999 -enum KernelMessageFlags {KMSG_1 = 1, KMSG_2 = 2, KMSG_4 = 4, KMSG_8 = 8, KMSG_20 = 0x20, KMSG_40 = 0x40, KMSG_ACTIVE = 0x80}; +enum KernelMessageFlags {KMSG_QUOTED = 1, KMSG_OWNER_TIMEOUT = 2, KMSG_SEQ_ENTRY = 4, KMSG_SCROLL = 8, KMSG_RIGHT_ALIGN = 0x10, + KMSG_CENTER_ALIGN = 0x20, KMSG_EXPIRE = 0x40, KMSG_ACTIVE = 0x80}; -class MadsKernelMessageListEntry { +class MadsKernelMessageEntry { public: uint8 flags; int sequenceIndex; @@ -152,31 +162,35 @@ public: Common::Point position; int textDisplayIndex; int msgOffset; - int field_E; + int numTicks; uint32 frameTimer2; uint32 frameTimer; uint32 timeout; - bool field_1C; + int abortTimers; AbortTimerMode abortMode; uint16 actionNouns[3]; - const char *msg; + char msg[100]; }; class MadsKernelMessageList { private: MadsView &_owner; - Common::Array<MadsKernelMessageListEntry> _entries; + Common::Array<MadsKernelMessageEntry> _entries; Font *_talkFont; public: + int word_8469E; +public: MadsKernelMessageList(MadsView &owner); void clear(); - int add(const Common::Point &pt, uint fontColour, uint8 flags, uint8 v2, uint32 timeout, const char *msg); - int addQuote(int quoteId, int v2, uint32 timeout); - void unk1(int msgIndex, int v1, int v2); + int add(const Common::Point &pt, uint fontColour, uint8 flags, uint8 abortTimers, uint32 timeout, const char *msg); + int addQuote(int quoteId, int abortTimers, uint32 timeout); + void scrollMessage(int msgIndex, int numTicks, bool quoted); void setSeqIndex(int msgIndex, int seqIndex); void remove(int msgIndex); void reset(); + void update(); + void processText(int msgIndex); }; class ScreenObjectEntry { @@ -275,7 +289,7 @@ public: void merge(int startIndex, int count); bool intersects(int idx1, int idx2); void mergeAreas(int idx1, int idx2); - void copy(M4Surface *dest, M4Surface *src); + void copy(M4Surface *dest, M4Surface *src, int yOffset); }; enum SpriteAnimType {ANIMTYPE_CYCLED = 1, ANIMTYPE_REVERSIBLE = 2}; @@ -308,12 +322,10 @@ struct MadsSequenceEntry { int scale; int dynamicHotspotIndex; - int field_12; + bool nonFixed; int field_13; - int width; - int height; - + Common::Point msgPos; int triggerCountdown; bool doneFlag; MadsSequenceSubEntries entries; @@ -338,20 +350,35 @@ public: void clear(); bool addSubEntry(int index, SequenceSubEntryMode mode, int frameIndex, int abortVal); int add(int spriteListIndex, int v0, int v1, int triggerCountdown, int delayTicks, int extraTicks, int numTicks, - int height, int width, char field_12, char scale, uint8 depth, int frameInc, SpriteAnimType animType, + int msgX, int msgY, bool nonFixed, char scale, uint8 depth, int frameInc, SpriteAnimType animType, int numSprites, int frameStart); - void remove(int timerIndex); - void setSpriteSlot(int timerIndex, MadsSpriteSlot &spriteSlot); - bool loadSprites(int timerIndex); + void remove(int seqIndex); + void setSpriteSlot(int seqIndex, MadsSpriteSlot &spriteSlot); + bool loadSprites(int seqIndex); void tick(); void delay(uint32 v1, uint32 v2); - void setAnimRange(int timerIndex, int startVal, int endVal); + void setAnimRange(int seqIndex, int startVal, int endVal); + void scan(); }; +class Animation { +protected: + MadsM4Engine *_vm; +public: + Animation(MadsM4Engine *vm); + virtual ~Animation(); + virtual void initialise(const Common::String &filename, uint16 flags, M4Surface *walkSurface, M4Surface *sceneSurface) = 0; + virtual void load(const Common::String &filename, int v0) = 0; + virtual void update() = 0; + virtual void setCurrentFrame(int frameNumber) = 0; +}; + + class MadsView { private: View *_view; public: + Animation *_sceneAnimation; MadsSpriteSlots _spriteSlots; MadsTextDisplay _textDisplay; MadsKernelMessageList _kernelMessages; @@ -371,62 +398,14 @@ public: M4Surface *_depthSurface; M4Surface *_bgSurface; + int _yOffset; public: MadsView(View *view); + ~MadsView(); void refresh(); }; -#define CHEAT_SEQUENCE_MAX 8 - -class IntegerList : public Common::Array<int> { -public: - int indexOf(int v) { - for (uint i = 0; i < size(); ++i) - if (operator [](i) == v) - return i; - return -1; - } -}; - -enum InterfaceFontMode {ITEM_NORMAL, ITEM_HIGHLIGHTED, ITEM_SELECTED}; - -enum InterfaceObjects {ACTIONS_START = 0, SCROLL_UP = 10, SCROLL_SCROLLER = 11, SCROLL_DOWN = 12, - INVLIST_START = 13, VOCAB_START = 18}; - -class MadsInterfaceView : public GameInterfaceView { -private: - IntegerList _inventoryList; - RectList _screenObjects; - int _highlightedElement; - int _topIndex; - uint32 _nextScrollerTicks; - int _cheatKeyCtr; - - // Object display fields - int _selectedObject; - SpriteAsset *_objectSprites; - RGBList *_objectPalData; - int _objectFrameNumber; - - void setFontMode(InterfaceFontMode newMode); - bool handleCheatKey(int32 keycode); - bool handleKeypress(int32 keycode); - void leaveScene(); -public: - MadsInterfaceView(MadsM4Engine *vm); - ~MadsInterfaceView(); - - virtual void initialise(); - virtual void setSelectedObject(int objectNumber); - virtual void addObjectToInventory(int objectNumber); - int getSelectedObject() { return _selectedObject; } - int getInventoryObject(int objectIndex) { return _inventoryList[objectIndex]; } - - void onRefresh(RectList *rects, M4Surface *destSurface); - bool onEvent(M4EventType eventType, int32 param1, int x, int y, bool &captureEvents); -}; - } #endif diff --git a/engines/m4/viewmgr.h b/engines/m4/viewmgr.h index 16c3d6ecc3..211e6087f4 100644 --- a/engines/m4/viewmgr.h +++ b/engines/m4/viewmgr.h @@ -42,6 +42,19 @@ namespace M4 { class View; class ViewManager; +enum SceneTransition { + kTransitionNone = 0, + kTransitionFadeIn = 1, + kTransitionFadeIn2 = 2, + kTransitionBoxInBottomLeft = 3, + kTransitionBoxInBottomRight = 4, + kTransitionBoxInTopLeft = 5, + kTransitionBoxInTopRight = 6, + kTransitionPanLeftToRight = 7, + kTransitionPanRightToLeft = 8, + kTransitionCircleIn = 9 +}; + enum {SCREEN_DIALOG, SCREEN_BUFFER, SCREEN_TEXT, SCREEN_TRANSPARENT}; enum ScreenEventType {SCREVENT_NONE = 0, SCREVENT_KEY = 1, SCREVENT_MOUSE = 2, SCREVENT_ALL = 3}; enum ScreenLayers { diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp index 389665db39..149b6b6eba 100644 --- a/engines/mohawk/console.cpp +++ b/engines/mohawk/console.cpp @@ -30,7 +30,7 @@ #include "mohawk/riven.h" #include "mohawk/livingbooks.h" #include "mohawk/sound.h" -#include "mohawk/video/video.h" +#include "mohawk/video.h" namespace Mohawk { diff --git a/engines/mohawk/detection.cpp b/engines/mohawk/detection.cpp index a7b1fe7fae..7f2e0cb312 100644 --- a/engines/mohawk/detection.cpp +++ b/engines/mohawk/detection.cpp @@ -341,6 +341,24 @@ static const MohawkGameDescription gameDescriptions[] = { 0, }, + // Myst Masterpiece Edition + // French Windows + // From gamin (Included in "Myst: La Trilogie") + { + { + "myst", + "Masterpiece Edition", + AD_ENTRY1("MYST.DAT", "aea81633b2d2ae498f09072fb87263b6"), + Common::FR_FRA, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + Common::GUIO_NONE + }, + GType_MYST, + GF_ME, + 0, + }, + // Riven: The Sequel to Myst // Version 1.0 (5CD) // From clone2727 @@ -432,6 +450,24 @@ static const MohawkGameDescription gameDescriptions[] = { }, // Riven: The Sequel to Myst + // Version ? (DVD, From "Myst: La Trilogie") + // From gamin + { + { + "riven", + "", + AD_ENTRY1("a_Data.MHK", "aff2a384aaa9a0e0ec51010f708c5c04"), + Common::FR_FRA, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + Common::GUIO_NONE + }, + GType_RIVEN, + GF_DVD, + 0, + }, + + // Riven: The Sequel to Myst // Version ? (Demo, From "Prince of Persia Collector's Edition") // From Clone2727 { diff --git a/engines/mohawk/graphics.cpp b/engines/mohawk/graphics.cpp index 35691c36aa..2ddfb47575 100644 --- a/engines/mohawk/graphics.cpp +++ b/engines/mohawk/graphics.cpp @@ -78,9 +78,8 @@ MystGraphics::MystGraphics(MohawkEngine_Myst* vm) : _vm(vm) { error("Myst requires greater than 256 colors to run"); if (_vm->getFeatures() & GF_ME) { - // We want to delete our own JPEG surfaces, so don't free after use. - _jpegDecoder = new JPEGDecoder(false); - _pictDecoder = new MystPICT(_jpegDecoder); + _jpegDecoder = new Graphics::JPEGDecoder(); + _pictDecoder = new Graphics::PictDecoder(_pixelFormat); } else { _jpegDecoder = NULL; _pictDecoder = NULL; @@ -152,9 +151,10 @@ void MystGraphics::copyImageSectionToScreen(uint16 image, Common::Rect src, Comm if (_vm->getFeatures() & GF_ME && _vm->getPlatform() == Common::kPlatformMacintosh && _pictureFile.picFile.isOpen()) { for (uint32 i = 0; i < _pictureFile.pictureCount; i++) if (_pictureFile.entries[i].id == image) { - if (_pictureFile.entries[i].type == 0) - surface = _jpegDecoder->decodeImage(new Common::SeekableSubReadStream(&_pictureFile.picFile, _pictureFile.entries[i].offset, _pictureFile.entries[i].offset + _pictureFile.entries[i].size)); - else if (_pictureFile.entries[i].type == 1) + if (_pictureFile.entries[i].type == 0) { + Graphics::Surface *jpegSurface = _jpegDecoder->decodeImage(new Common::SeekableSubReadStream(&_pictureFile.picFile, _pictureFile.entries[i].offset, _pictureFile.entries[i].offset + _pictureFile.entries[i].size)); + surface->copyFrom(*jpegSurface); + } else if (_pictureFile.entries[i].type == 1) surface = _pictDecoder->decodeImage(new Common::SeekableSubReadStream(&_pictureFile.picFile, _pictureFile.entries[i].offset, _pictureFile.entries[i].offset + _pictureFile.entries[i].size)); else error ("Unknown Picture File type %d", _pictureFile.entries[i].type); diff --git a/engines/mohawk/graphics.h b/engines/mohawk/graphics.h index 8d28e1ff4b..dd1764e6d6 100644 --- a/engines/mohawk/graphics.h +++ b/engines/mohawk/graphics.h @@ -27,11 +27,11 @@ #define MOHAWK_GRAPHICS_H #include "mohawk/bitmap.h" -#include "mohawk/jpeg.h" #include "mohawk/livingbooks.h" -#include "mohawk/myst_pict.h" #include "common/file.h" +#include "graphics/pict.h" +#include "graphics/video/codecs/mjpeg.h" namespace Mohawk { @@ -96,16 +96,16 @@ public: void loadExternalPictureFile(uint16 stack); void copyImageSectionToScreen(uint16 image, Common::Rect src, Common::Rect dest); void copyImageToScreen(uint16 image, Common::Rect dest); - void showCursor(void); - void hideCursor(void); + void showCursor(); + void hideCursor(); void changeCursor(uint16); void drawRect(Common::Rect rect, bool active); private: MohawkEngine_Myst *_vm; MystBitmap *_bmpDecoder; - MystPICT *_pictDecoder; - JPEGDecoder *_jpegDecoder; + Graphics::PictDecoder *_pictDecoder; + Graphics::JPEGDecoder *_jpegDecoder; Graphics::PixelFormat _pixelFormat; struct PictureFile { diff --git a/engines/mohawk/jpeg.cpp b/engines/mohawk/jpeg.cpp deleted file mode 100644 index 07ec54dfea..0000000000 --- a/engines/mohawk/jpeg.cpp +++ /dev/null @@ -1,87 +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. - * - * $URL$ - * $Id$ - * - */ - -#include "common/system.h" -#include "graphics/conversion.h" // For YUV2RGB - -#include "mohawk/jpeg.h" - -namespace Mohawk { - -JPEGDecoder::JPEGDecoder(bool freeSurfaceAfterUse) : Graphics::Codec(), _freeSurfaceAfterUse(freeSurfaceAfterUse) { - _jpeg = new Graphics::JPEG(); - _pixelFormat = g_system->getScreenFormat(); - _surface = NULL; -} - -JPEGDecoder::~JPEGDecoder() { - delete _jpeg; - - if (_surface) { - _surface->free(); - delete _surface; - } -} - -Graphics::Surface *JPEGDecoder::decodeImage(Common::SeekableReadStream* stream) { - _jpeg->read(stream); - Graphics::Surface *ySurface = _jpeg->getComponent(1); - Graphics::Surface *uSurface = _jpeg->getComponent(2); - Graphics::Surface *vSurface = _jpeg->getComponent(3); - - Graphics::Surface *destSurface = NULL; - - // If we should free the surface after use, use the internal _surface storage - // (this should be used when using as a Codec, as the Codecs should free their - // surfaces when deleting the Codec object). Otherwise, create a new Surface - // as the destination. - if (_freeSurfaceAfterUse) { - if (!_surface) { - _surface = new Graphics::Surface(); - _surface->create(ySurface->w, ySurface->h, _pixelFormat.bytesPerPixel); - } - destSurface = _surface; - } else { - destSurface = new Graphics::Surface(); - destSurface->create(ySurface->w, ySurface->h, _pixelFormat.bytesPerPixel); - } - - assert(destSurface); - - for (uint16 i = 0; i < destSurface->h; i++) { - for (uint16 j = 0; j < destSurface->w; j++) { - byte r = 0, g = 0, b = 0; - Graphics::YUV2RGB(*((byte *)ySurface->getBasePtr(j, i)), *((byte *)uSurface->getBasePtr(j, i)), *((byte *)vSurface->getBasePtr(j, i)), r, g, b); - if (_pixelFormat.bytesPerPixel == 2) - *((uint16 *)destSurface->getBasePtr(j, i)) = _pixelFormat.RGBToColor(r, g, b); - else - *((uint32 *)destSurface->getBasePtr(j, i)) = _pixelFormat.RGBToColor(r, g, b); - } - } - - return destSurface; -} - -} // End of namespace Mohawk diff --git a/engines/mohawk/jpeg.h b/engines/mohawk/jpeg.h deleted file mode 100644 index ec87b1e7af..0000000000 --- a/engines/mohawk/jpeg.h +++ /dev/null @@ -1,59 +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. - * - * $URL$ - * $Id$ - * - */ - -#ifndef MOHAWK_JPEG_H -#define MOHAWK_JPEG_H - -#include "common/scummsys.h" -#include "common/stream.h" - -#include "graphics/video/codecs/codec.h" -#include "graphics/jpeg.h" -#include "graphics/pixelformat.h" - -namespace Mohawk { - -// Mohawk's JPEG Decoder -// Basically a wrapper around JPEG which converts to RGB and also functions -// as a Codec. - -class JPEGDecoder : public Graphics::Codec { -public: - JPEGDecoder(bool freeSurfaceAfterUse); - ~JPEGDecoder(); - - Graphics::Surface *decodeImage(Common::SeekableReadStream *stream); - Graphics::PixelFormat getPixelFormat() const { return _pixelFormat; } - -private: - Graphics::PixelFormat _pixelFormat; - Graphics::JPEG *_jpeg; - Graphics::Surface *_surface; - bool _freeSurfaceAfterUse; -}; - -} // End of namespace Mohawk - -#endif diff --git a/engines/mohawk/module.mk b/engines/mohawk/module.mk index b224a8b143..bb79d4abac 100644 --- a/engines/mohawk/module.mk +++ b/engines/mohawk/module.mk @@ -6,11 +6,9 @@ MODULE_OBJS = \ detection.o \ dialogs.o \ graphics.o \ - jpeg.o \ livingbooks.o \ mohawk.o \ myst.o \ - myst_pict.o \ myst_vars.o \ myst_saveload.o \ myst_scripts.o \ @@ -22,13 +20,7 @@ MODULE_OBJS = \ riven_scripts.o \ riven_vars.o \ sound.o \ - video/cinepak.o \ - video/qdm2.o \ - video/qtrle.o \ - video/qt_player.o \ - video/rpza.o \ - video/smc.o \ - video/video.o + video.o # This module can be built as a plugin diff --git a/engines/mohawk/mohawk.cpp b/engines/mohawk/mohawk.cpp index 5bde6bbeec..6b4d8bb2d2 100644 --- a/engines/mohawk/mohawk.cpp +++ b/engines/mohawk/mohawk.cpp @@ -35,7 +35,7 @@ #include "mohawk/mohawk.h" #include "mohawk/dialogs.h" #include "mohawk/sound.h" -#include "mohawk/video/video.h" +#include "mohawk/video.h" #include "sound/mixer.h" diff --git a/engines/mohawk/myst.cpp b/engines/mohawk/myst.cpp index d1ef3b2137..9ff301c129 100644 --- a/engines/mohawk/myst.cpp +++ b/engines/mohawk/myst.cpp @@ -33,7 +33,7 @@ #include "mohawk/dialogs.h" #include "mohawk/resource.h" #include "mohawk/resource_cache.h" -#include "mohawk/video/video.h" +#include "mohawk/video.h" namespace Mohawk { diff --git a/engines/mohawk/myst_pict.cpp b/engines/mohawk/myst_pict.cpp deleted file mode 100644 index 794ec9b87f..0000000000 --- a/engines/mohawk/myst_pict.cpp +++ /dev/null @@ -1,272 +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. - * - * $URL$ - * $Id$ - * - */ - -#include "common/system.h" - -#include "mohawk/myst_pict.h" - -namespace Mohawk { - -// The PICT code is based off of the QuickDraw specs: -// http://developer.apple.com/documentation/mac/QuickDraw/QuickDraw-461.html - -MystPICT::MystPICT(JPEGDecoder *jpegDecoder) { - _jpegDecoder = jpegDecoder; - _pixelFormat = g_system->getScreenFormat(); -} - -Graphics::Surface *MystPICT::decodeImage(Common::SeekableReadStream *stream) { - // Skip initial 512 bytes (all 0's) - stream->seek(512, SEEK_CUR); - - // Read in the first part of the header - /* uint16 fileSize = */ stream->readUint16BE(); - - _imageRect.top = stream->readUint16BE(); - _imageRect.left = stream->readUint16BE(); - _imageRect.bottom = stream->readUint16BE(); - _imageRect.right = stream->readUint16BE(); - _imageRect.debugPrint(0, "PICT Rect:"); - - Graphics::Surface *image = new Graphics::Surface(); - image->create(_imageRect.width(), _imageRect.height(), _pixelFormat.bytesPerPixel); - - // NOTE: This is only a subset of the full PICT format. - // - Only V2 Images Supported - // - JPEG Chunks are Supported - // - DirectBitsRect Chunks are Supported - for (uint32 opNum = 0; !stream->eos() && !stream->err() && stream->pos() < stream->size(); opNum++) { - uint16 opcode = stream->readUint16BE(); - debug(2, "Found PICT opcode %04x", opcode); - - if (opNum == 0 && opcode != 0x0011) - error ("Cannot find PICT version opcode"); - else if (opNum == 1 && opcode != 0x0C00) - error ("Cannot find PICT header opcode"); - - if (opcode == 0x0000) { // Nop - stream->readUint16BE(); // Unknown - } else if (opcode == 0x0001) { // Clip - // Ignore - uint16 clipSize = stream->readUint16BE(); - stream->seek(clipSize - 2, SEEK_CUR); - } else if (opcode == 0x0007) { // PnSize - // Ignore - stream->readUint16BE(); - stream->readUint16BE(); - } else if (opcode == 0x0011) { // VersionOp - uint16 version = stream->readUint16BE(); - if (version != 0x02FF) - error ("Unknown PICT version"); - } else if (opcode == 0x001E) { // DefHilite - // Ignore, Contains no Data - } else if (opcode == 0x009A) { // DirectBitsRect - decodeDirectBitsRect(stream, image); - } else if (opcode == 0x00A1) { // LongComment - stream->readUint16BE(); - uint16 dataSize = stream->readUint16BE(); - stream->seek(dataSize, SEEK_CUR); - } else if (opcode == 0x00FF) { // OpEndPic - stream->readUint16BE(); - break; - } else if (opcode == 0x0C00) { // HeaderOp - /* uint16 version = */ stream->readUint16BE(); - stream->readUint16BE(); // Reserved - /* uint32 hRes = */ stream->readUint32BE(); - /* uint32 vRes = */ stream->readUint32BE(); - Common::Rect origResRect; - origResRect.top = stream->readUint16BE(); - origResRect.left = stream->readUint16BE(); - origResRect.bottom = stream->readUint16BE(); - origResRect.right = stream->readUint16BE(); - stream->readUint32BE(); // Reserved - } else if (opcode == 0x8200) { // CompressedQuickTime - decodeCompressedQuickTime(stream, image); - break; - } else { - error ("Unknown PICT opcode %04x", opcode); - } - } - - return image; -} - -struct DirectBitsRectData { - // PixMap - struct { - uint32 baseAddr; - uint16 rowBytes; - Common::Rect bounds; - uint16 pmVersion; - uint16 packType; - uint32 packSize; - uint32 hRes; - uint32 vRes; - uint16 pixelType; - uint16 pixelSize; - uint16 cmpCount; - uint16 cmpSize; - uint32 planeBytes; - uint32 pmTable; - uint32 pmReserved; - } pixMap; - Common::Rect srcRect; - Common::Rect dstRect; - uint16 mode; -}; - -void MystPICT::decodeDirectBitsRect(Common::SeekableReadStream *stream, Graphics::Surface *image) { - static const Graphics::PixelFormat directBitsFormat16 = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0); - - DirectBitsRectData directBitsData; - directBitsData.pixMap.baseAddr = stream->readUint32BE(); - directBitsData.pixMap.rowBytes = stream->readUint16BE() & 0x3fff; - directBitsData.pixMap.bounds.top = stream->readUint16BE(); - directBitsData.pixMap.bounds.left = stream->readUint16BE(); - directBitsData.pixMap.bounds.bottom = stream->readUint16BE(); - directBitsData.pixMap.bounds.right = stream->readUint16BE(); - directBitsData.pixMap.pmVersion = stream->readUint16BE(); - directBitsData.pixMap.packType = stream->readUint16BE(); - directBitsData.pixMap.packSize = stream->readUint32BE(); - directBitsData.pixMap.hRes = stream->readUint32BE(); - directBitsData.pixMap.vRes = stream->readUint32BE(); - directBitsData.pixMap.pixelType = stream->readUint16BE(); - directBitsData.pixMap.pixelSize = stream->readUint16BE(); - directBitsData.pixMap.cmpCount = stream->readUint16BE(); - directBitsData.pixMap.cmpSize = stream->readUint16BE(); - directBitsData.pixMap.planeBytes = stream->readUint32BE(); - directBitsData.pixMap.pmTable = stream->readUint32BE(); - directBitsData.pixMap.pmReserved = stream->readUint32BE(); - directBitsData.srcRect.top = stream->readUint16BE(); - directBitsData.srcRect.left = stream->readUint16BE(); - directBitsData.srcRect.bottom = stream->readUint16BE(); - directBitsData.srcRect.right = stream->readUint16BE(); - directBitsData.dstRect.top = stream->readUint16BE(); - directBitsData.dstRect.left = stream->readUint16BE(); - directBitsData.dstRect.bottom = stream->readUint16BE(); - directBitsData.dstRect.right = stream->readUint16BE(); - directBitsData.mode = stream->readUint16BE(); - - if (directBitsData.pixMap.pixelSize != 16 && directBitsData.pixMap.pixelSize != 32) - error("Unhandled directBitsRect bitsPerPixel %d", directBitsData.pixMap.pixelSize); - - byte bytesPerPixel = (directBitsData.pixMap.pixelSize == 16) ? 2 : 3; - byte *buffer = new byte[image->w * image->h * bytesPerPixel]; - - // Read in amount of data per row - for (uint16 i = 0; i < directBitsData.pixMap.bounds.height(); i++) { - if (directBitsData.pixMap.packType == 1 || directBitsData.pixMap.rowBytes < 8) { // Unpacked, Pad-Byte - error("Pack Type = %d, Row Bytes = %d", directBitsData.pixMap.packType, directBitsData.pixMap.rowBytes); - // TODO - } else if (directBitsData.pixMap.packType == 2) { // Unpacked, No Pad-Byte - error("Pack Type = 2"); - // TODO - } else if (directBitsData.pixMap.packType > 2) { // Packed - uint16 byteCount = (directBitsData.pixMap.rowBytes > 250) ? stream->readUint16BE() : stream->readByte(); - decodeDirectBitsLine(buffer + i * image->w * bytesPerPixel, directBitsData.pixMap.rowBytes, stream->readStream(byteCount), bytesPerPixel); - } - } - - if (bytesPerPixel == 2) { - // Convert from 16-bit to whatever surface we need - for (uint16 y = 0; y < image->h; y++) { - for (uint16 x = 0; x < image->w; x++) { - byte r = 0, g = 0, b = 0; - uint32 color = READ_BE_UINT16(buffer + (y * image->w + x) * bytesPerPixel); - directBitsFormat16.colorToRGB(color, r, g, b); - *((uint16 *)image->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b); - } - } - } else { - // Convert from 24-bit (planar!) to whatever surface we need - for (uint16 y = 0; y < image->h; y++) { - for (uint16 x = 0; x < image->w; x++) { - byte r = *(buffer + y * image->w * 3 + x); - byte g = *(buffer + y * image->w * 3 + image->w + x); - byte b = *(buffer + y * image->w * 3 + image->w * 2 + x); - *((uint16 *)image->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b); - } - } - } - - delete[] buffer; -} - -void MystPICT::decodeDirectBitsLine(byte *out, uint32 length, Common::SeekableReadStream *data, byte bytesPerPixel) { - uint32 dataDecoded = 0; - byte bytesPerDecode = (bytesPerPixel == 2) ? 2 : 1; - - while (data->pos() < data->size() && dataDecoded < length) { - byte op = data->readByte(); - - if (op & 0x80) { - uint32 runSize = (op ^ 255) + 2; - uint16 value = (bytesPerDecode == 2) ? data->readUint16BE() : data->readByte(); - - for (uint32 i = 0; i < runSize; i++) { - if (bytesPerDecode == 2) { - WRITE_BE_UINT16(out, value); - out += 2; - } else - *out++ = value; - } - dataDecoded += runSize * bytesPerDecode; - } else { - uint32 runSize = (op + 1) * bytesPerDecode; - for (uint32 i = 0; i < runSize; i++) - *out++ = data->readByte(); - dataDecoded += runSize; - } - } - - // HACK: rowBytes is in 32-bit, but the data is 24-bit... - if (bytesPerPixel == 3) - dataDecoded += length / 4; - - if (length != dataDecoded) - warning("Mismatched DirectBits read (%d/%d)", dataDecoded, length); - - delete data; -} - -// Compressed QuickTime details can be found here: -// http://developer.apple.com/documentation/QuickTime/Rm/CompressDecompress/ImageComprMgr/B-Chapter/2TheImageCompression.html -// http://developer.apple.com/documentation/QuickTime/Rm/CompressDecompress/ImageComprMgr/F-Chapter/6WorkingwiththeImage.html -// I'm just ignoring that because Myst ME uses none of that extra stuff. The offset is always the same. - -void MystPICT::decodeCompressedQuickTime(Common::SeekableReadStream *stream, Graphics::Surface *image) { - uint32 dataSize = stream->readUint32BE(); - uint32 startPos = stream->pos(); - - Graphics::Surface *jpegImage = _jpegDecoder->decodeImage(new Common::SeekableSubReadStream(stream, stream->pos() + 156, stream->pos() + dataSize)); - stream->seek(startPos + dataSize); - - image->copyFrom(*jpegImage); - - jpegImage->free(); - delete jpegImage; -} - -} // End of namespace Mohawk diff --git a/engines/mohawk/myst_pict.h b/engines/mohawk/myst_pict.h deleted file mode 100644 index 0684e3352a..0000000000 --- a/engines/mohawk/myst_pict.h +++ /dev/null @@ -1,57 +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. - * - * $URL$ - * $Id$ - * - */ - -#ifndef MYST_PICT_H -#define MYST_PICT_H - -#include "common/rect.h" -#include "common/scummsys.h" -#include "common/stream.h" -#include "graphics/pixelformat.h" -#include "graphics/surface.h" - -#include "mohawk/jpeg.h" - -namespace Mohawk { - -class MystPICT { -public: - MystPICT(JPEGDecoder *jpegDecoder); - ~MystPICT() {} - Graphics::Surface *decodeImage(Common::SeekableReadStream *stream); - -private: - JPEGDecoder *_jpegDecoder; - Common::Rect _imageRect; - Graphics::PixelFormat _pixelFormat; - - void decodeDirectBitsRect(Common::SeekableReadStream *stream, Graphics::Surface *image); - void decodeDirectBitsLine(byte *out, uint32 length, Common::SeekableReadStream *data, byte bytesPerPixel); - void decodeCompressedQuickTime(Common::SeekableReadStream *stream, Graphics::Surface *image); -}; - -} // End of namespace Mohawk - -#endif diff --git a/engines/mohawk/myst_scripts.cpp b/engines/mohawk/myst_scripts.cpp index a453bb0985..2f6d178da8 100644 --- a/engines/mohawk/myst_scripts.cpp +++ b/engines/mohawk/myst_scripts.cpp @@ -27,7 +27,7 @@ #include "mohawk/graphics.h" #include "mohawk/myst_scripts.h" #include "mohawk/sound.h" -#include "mohawk/video/video.h" +#include "mohawk/video.h" #include "gui/message.h" diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp index 0412144034..c646855bc7 100644 --- a/engines/mohawk/riven.cpp +++ b/engines/mohawk/riven.cpp @@ -33,7 +33,7 @@ #include "mohawk/riven_external.h" #include "mohawk/riven_saveload.h" #include "mohawk/dialogs.h" -#include "mohawk/video/video.h" +#include "mohawk/video.h" namespace Mohawk { @@ -47,6 +47,7 @@ MohawkEngine_Riven::MohawkEngine_Riven(OSystem *syst, const MohawkGameDescriptio _cardData.hasData = false; _gameOver = false; _activatedSLST = false; + _ignoreNextMouseUp = false; _extrasFile = NULL; // Attempt to let game run from the CDs @@ -147,10 +148,15 @@ Common::Error MohawkEngine_Riven::run() { runHotspotScript(_curHotspot, kMouseDownScript); break; case Common::EVENT_LBUTTONUP: - if (_curHotspot >= 0) - runHotspotScript(_curHotspot, kMouseUpScript); - else - checkInventoryClick(); + // See RivenScript::switchCard() for more information on why we sometimes + // disable the next up event. + if (!_ignoreNextMouseUp) { + if (_curHotspot >= 0) + runHotspotScript(_curHotspot, kMouseUpScript); + else + checkInventoryClick(); + } + _ignoreNextMouseUp = false; break; case Common::EVENT_KEYDOWN: switch (event.kbd.keycode) { @@ -233,6 +239,7 @@ void MohawkEngine_Riven::changeToStack(uint16 n) { // Stop any videos playing _video->stopVideos(); + _video->clearMLST(); // Clear the old stack files out for (uint32 i = 0; i < _mhk.size(); i++) @@ -310,7 +317,6 @@ void MohawkEngine_Riven::refreshCard() { _gfx->clearWaterEffects(); _gfx->_activatedPLSTs.clear(); _video->stopVideos(); - _video->_mlstRecords.clear(); _gfx->drawPLST(1); _activatedSLST = false; diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h index f014b76fb8..11c3a4c0cb 100644 --- a/engines/mohawk/riven.h +++ b/engines/mohawk/riven.h @@ -113,7 +113,6 @@ public: Common::RandomSource *_rnd; Card _cardData; - bool _gameOver; GUI::Debugger *getDebugger(); @@ -147,6 +146,10 @@ private: uint32 *_vars; uint32 _varCount; + // Miscellaneous + bool _gameOver; + bool _ignoreNextMouseUp; + public: Common::SeekableReadStream *getExtrasResource(uint32 tag, uint16 id); bool _activatedSLST; @@ -180,6 +183,9 @@ public: uint32 *getLocalVar(uint32 index); uint32 *matchVarToString(Common::String varName); uint32 *matchVarToString(const char *varName); + + void setGameOver() { _gameOver = true; } + void ignoreNextMouseUp() { _ignoreNextMouseUp = true; } }; } // End of namespace Mohawk diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp index 99afacc5ce..4e6bba1c2a 100644 --- a/engines/mohawk/riven_external.cpp +++ b/engines/mohawk/riven_external.cpp @@ -27,7 +27,7 @@ #include "mohawk/riven.h" #include "mohawk/riven_external.h" #include "mohawk/sound.h" -#include "mohawk/video/video.h" +#include "mohawk/video.h" #include "gui/message.h" #include "common/events.h" @@ -210,7 +210,28 @@ void RivenExternal::runEndGame(uint16 video) { _vm->_video->playMovieBlocking(video); // TODO: Play until the last frame and then run the credits - _vm->_gameOver = true; + _vm->setGameOver(); +} + +void RivenExternal::runDomeButtonMovie() { + // This command just plays the video of the button moving down and up. + _vm->_video->playMovieBlocking(2); +} + +void RivenExternal::runDomeCheck() { + // Check if we clicked while the golden frame was showing + + VideoHandle video = _vm->_video->findVideoHandle(1); + assert(video != NULL_VID_HANDLE); + + int32 curFrame = _vm->_video->getCurFrame(video); + int32 frameCount = _vm->_video->getFrameCount(video); + + // The final frame of the video is the 'golden' frame (double meaning: the + // frame that is the magic one is the one with the golden symbol) but we + // give a 3 frame leeway in either direction. + if (frameCount - curFrame < 3 || curFrame < 3) + *_vm->matchVarToString("domecheck") = 1; } // ------------------------------------------------------------------------------------ @@ -218,11 +239,13 @@ void RivenExternal::runEndGame(uint16 video) { // ------------------------------------------------------------------------------------ void RivenExternal::xastartupbtnhide(uint16 argc, uint16 *argv) { - // The original game hides the start/setup buttons depending on an ini entry. It's safe to ignore this command. + // The original game hides the start/setup buttons depending on an ini entry. + // It's safe to ignore this command. } void RivenExternal::xasetupcomplete(uint16 argc, uint16 *argv) { - // The original game sets an ini entry to disable the setup button and use the start button only. It's safe to ignore this part of the command. + // The original game sets an ini entry to disable the setup button and use the + // start button only. It's safe to ignore this part of the command. _vm->_sound->stopSound(); _vm->changeToCard(1); } @@ -514,13 +537,14 @@ void RivenExternal::xbupdateboiler(uint16 argc, uint16 *argv) { if (heat) { if (platform == 0) { _vm->_video->activateMLST(7, _vm->getCurCard()); - // TODO: Play video (non-blocking) + _vm->_video->playMovie(7); } else { _vm->_video->activateMLST(8, _vm->getCurCard()); - // TODO: Play video (non-blocking) + _vm->_video->playMovie(8); } } else { - // TODO: Stop MLST's 7 and 8 + _vm->_video->stopMovie(7); + _vm->_video->stopMovie(8); } _vm->refreshCard(); @@ -627,11 +651,11 @@ void RivenExternal::xbisland190_slidermw(uint16 argc, uint16 *argv) { } void RivenExternal::xbscpbtn(uint16 argc, uint16 *argv) { - // TODO: Dome related + runDomeButtonMovie(); } void RivenExternal::xbisland_domecheck(uint16 argc, uint16 *argv) { - // TODO: Dome related + runDomeCheck(); } void RivenExternal::xvalvecontrol(uint16 argc, uint16 *argv) { @@ -723,11 +747,11 @@ void RivenExternal::xgisland25_slidermw(uint16 argc, uint16 *argv) { } void RivenExternal::xgscpbtn(uint16 argc, uint16 *argv) { - // TODO: Dome related + runDomeButtonMovie(); } void RivenExternal::xgisland1490_domecheck(uint16 argc, uint16 *argv) { - // TODO: Dome related + runDomeCheck(); } void RivenExternal::xgplateau3160_dopools(uint16 argc, uint16 *argv) { @@ -978,11 +1002,11 @@ void RivenExternal::xjdome25_slidermw(uint16 argc, uint16 *argv) { } void RivenExternal::xjscpbtn(uint16 argc, uint16 *argv) { - // TODO: Dome related + runDomeButtonMovie(); } void RivenExternal::xjisland3500_domecheck(uint16 argc, uint16 *argv) { - // TODO: Dome related + runDomeCheck(); } int RivenExternal::jspitElevatorLoop() { @@ -1258,11 +1282,11 @@ void RivenExternal::xpisland990_elevcombo(uint16 argc, uint16 *argv) { } void RivenExternal::xpscpbtn(uint16 argc, uint16 *argv) { - // TODO: Dome related + runDomeButtonMovie(); } void RivenExternal::xpisland290_domecheck(uint16 argc, uint16 *argv) { - // TODO: Dome related + runDomeCheck(); } void RivenExternal::xpisland25_opencard(uint16 argc, uint16 *argv) { @@ -1457,11 +1481,11 @@ void RivenExternal::xtakeit(uint16 argc, uint16 *argv) { } void RivenExternal::xtscpbtn(uint16 argc, uint16 *argv) { - // TODO: Dome related + runDomeButtonMovie(); } void RivenExternal::xtisland4990_domecheck(uint16 argc, uint16 *argv) { - // TODO: Dome related + runDomeCheck(); } void RivenExternal::xtisland5056_opencard(uint16 argc, uint16 *argv) { diff --git a/engines/mohawk/riven_external.h b/engines/mohawk/riven_external.h index 8270a00854..14bb51340c 100644 --- a/engines/mohawk/riven_external.h +++ b/engines/mohawk/riven_external.h @@ -57,6 +57,8 @@ private: int jspitElevatorLoop(); void runDemoBoundaryDialog(); void runEndGame(uint16 video); + void runDomeCheck(); + void runDomeButtonMovie(); // ----------------------------------------------------- // aspit (Main Menu, Books, Setup) external commands diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp index e809ad9642..d574a455c6 100644 --- a/engines/mohawk/riven_scripts.cpp +++ b/engines/mohawk/riven_scripts.cpp @@ -28,7 +28,7 @@ #include "mohawk/riven_external.h" #include "mohawk/riven_scripts.h" #include "mohawk/sound.h" -#include "mohawk/video/video.h" +#include "mohawk/video.h" #include "common/stream.h" #include "graphics/cursorman.h" @@ -298,13 +298,10 @@ void RivenScript::processCommands(bool runCommands) { // Command 1: draw tBMP resource (tbmp_id, left, top, right, bottom, u0, u1, u2, u3) void RivenScript::drawBitmap(uint16 op, uint16 argc, uint16 *argv) { - if (argc < 5) { - // Copy the image to the whole screen, ignoring the rest of the parameters + if (argc < 5) // Copy the image to the whole screen, ignoring the rest of the parameters _vm->_gfx->copyImageToScreen(argv[0], 0, 0, 608, 392); - } else { - // Copy the image to a certain part of the screen + else // Copy the image to a certain part of the screen _vm->_gfx->copyImageToScreen(argv[0], argv[1], argv[2], argv[3], argv[4]); - } // Now, update the screen _vm->_gfx->updateScreen(); @@ -313,6 +310,12 @@ void RivenScript::drawBitmap(uint16 op, uint16 argc, uint16 *argv) { // Command 2: go to card (card id) void RivenScript::switchCard(uint16 op, uint16 argc, uint16 *argv) { _vm->changeToCard(argv[0]); + + // WORKAROUND: If we changed card on a mouse down event, + // we want to ignore the next mouse up event so we don't + // change card when lifting the mouse on the next card. + if (_scriptType == kMouseDownScript) + _vm->ignoreNextMouseUp(); } // Command 3: play an SLST from the script @@ -547,9 +550,8 @@ void RivenScript::activateSLST(uint16 op, uint16 argc, uint16 *argv) { // Command 41: activate MLST record and play void RivenScript::activateMLSTAndPlay(uint16 op, uint16 argc, uint16 *argv) { - _vm->_video->enableMovie(argv[0] - 1); _vm->_video->activateMLST(argv[0], _vm->getCurCard()); - // TODO: Play movie (blocking?) + _vm->_video->playMovie(argv[0]); } // Command 43: activate BLST record (card hotspot enabling lists) diff --git a/engines/mohawk/video/video.cpp b/engines/mohawk/video.cpp index 86ecd4dedf..a45a4294c8 100644 --- a/engines/mohawk/video/video.cpp +++ b/engines/mohawk/video.cpp @@ -24,10 +24,10 @@ */ #include "mohawk/resource.h" -#include "mohawk/video/video.h" -#include "mohawk/video/qt_player.h" +#include "mohawk/video.h" #include "common/events.h" +#include "graphics/video/qt_decoder.h" namespace Mohawk { @@ -35,18 +35,19 @@ VideoManager::VideoManager(MohawkEngine* vm) : _vm(vm) { } VideoManager::~VideoManager() { - _mlstRecords.clear(); stopVideos(); } void VideoManager::pauseVideos() { for (uint16 i = 0; i < _videoStreams.size(); i++) - _videoStreams[i]->pauseVideo(true); + if (_videoStreams[i].video) + _videoStreams[i]->pauseVideo(true); } void VideoManager::resumeVideos() { for (uint16 i = 0; i < _videoStreams.size(); i++) - _videoStreams[i]->pauseVideo(false); + if (_videoStreams[i].video) + _videoStreams[i]->pauseVideo(false); } void VideoManager::stopVideos() { @@ -89,7 +90,7 @@ void VideoManager::playMovieCentered(Common::String filename, bool clearScreen) void VideoManager::waitUntilMovieEnds(VideoHandle videoHandle) { bool continuePlaying = true; - while (!_videoStreams[videoHandle]->endOfVideo() && !_vm->shouldQuit() && continuePlaying) { + while (_videoStreams[videoHandle].video && !_videoStreams[videoHandle]->endOfVideo() && !_vm->shouldQuit() && continuePlaying) { if (updateBackgroundMovies()) _vm->_system->updateScreen(); @@ -120,8 +121,10 @@ void VideoManager::waitUntilMovieEnds(VideoHandle videoHandle) { _vm->_system->delayMillis(10); } - _videoStreams[videoHandle]->close(); - _videoStreams.clear(); + delete _videoStreams[videoHandle].video; + _videoStreams[videoHandle].video = 0; + _videoStreams[videoHandle].id = 0; + _videoStreams[videoHandle].filename.clear(); } void VideoManager::playBackgroundMovie(Common::String filename, int16 x, int16 y, bool loop) { @@ -152,8 +155,9 @@ bool VideoManager::updateBackgroundMovies() { _videoStreams[i]->rewind(); } else { delete _videoStreams[i].video; - memset(&_videoStreams[i], 0, sizeof(VideoEntry)); - _videoStreams[i].video = NULL; + _videoStreams[i].video = 0; + _videoStreams[i].id = 0; + _videoStreams[i].filename.clear(); continue; } } @@ -240,7 +244,15 @@ void VideoManager::activateMLST(uint16 mlstId, uint16 card) { if (mlstRecord.u1 != 1) warning("mlstRecord.u1 not 1"); + // We've found a match, add it if (mlstRecord.index == mlstId) { + // Make sure we don't have any duplicates + for (uint32 j = 0; j < _mlstRecords.size(); j++) + if (_mlstRecords[j].index == mlstRecord.index || _mlstRecords[j].code == mlstRecord.code) { + _mlstRecords.remove_at(j); + j--; + } + _mlstRecords.push_back(mlstRecord); break; } @@ -249,6 +261,10 @@ void VideoManager::activateMLST(uint16 mlstId, uint16 card) { delete mlstStream; } +void VideoManager::clearMLST() { + _mlstRecords.clear(); +} + void VideoManager::playMovie(uint16 id) { for (uint16 i = 0; i < _mlstRecords.size(); i++) if (_mlstRecords[i].code == id) { @@ -274,8 +290,10 @@ void VideoManager::stopMovie(uint16 id) { if (_mlstRecords[i].code == id) for (uint16 j = 0; j < _videoStreams.size(); j++) if (_mlstRecords[i].movieID == _videoStreams[j].id) { - delete _videoStreams[i].video; - memset(&_videoStreams[i].video, 0, sizeof(VideoEntry)); + delete _videoStreams[j].video; + _videoStreams[j].video = 0; + _videoStreams[j].id = 0; + _videoStreams[j].filename.clear(); return; } } @@ -316,7 +334,7 @@ VideoHandle VideoManager::createVideoHandle(uint16 id, uint16 x, uint16 y, bool // Otherwise, create a new entry VideoEntry entry; - entry.video = new QTPlayer(); + entry.video = new Graphics::QuickTimeDecoder(); entry.x = x; entry.y = y; entry.filename = ""; @@ -346,7 +364,7 @@ VideoHandle VideoManager::createVideoHandle(Common::String filename, uint16 x, u // Otherwise, create a new entry VideoEntry entry; - entry.video = new QTPlayer(); + entry.video = new Graphics::QuickTimeDecoder(); entry.x = x; entry.y = y; entry.filename = filename; @@ -374,4 +392,24 @@ VideoHandle VideoManager::createVideoHandle(Common::String filename, uint16 x, u return _videoStreams.size() - 1; } +VideoHandle VideoManager::findVideoHandle(uint16 id) { + for (uint16 i = 0; i < _mlstRecords.size(); i++) + if (_mlstRecords[i].code == id) + for (uint16 j = 0; j < _videoStreams.size(); j++) + if (_videoStreams[j].video && _mlstRecords[i].movieID == _videoStreams[j].id) + return j; + + return NULL_VID_HANDLE; +} + +int32 VideoManager::getCurFrame(const VideoHandle &handle) { + assert(handle != NULL_VID_HANDLE); + return _videoStreams[handle]->getCurFrame(); +} + +uint32 VideoManager::getFrameCount(const VideoHandle &handle) { + assert(handle != NULL_VID_HANDLE); + return _videoStreams[handle]->getFrameCount(); +} + } // End of namespace Mohawk diff --git a/engines/mohawk/video/video.h b/engines/mohawk/video.h index a5d2bde65d..6aa553e26b 100644 --- a/engines/mohawk/video/video.h +++ b/engines/mohawk/video.h @@ -28,6 +28,10 @@ #include "graphics/pixelformat.h" +namespace Graphics { + class QuickTimeDecoder; +} + namespace Mohawk { class MohawkEngine; @@ -44,10 +48,8 @@ struct MLSTRecord { uint16 u1; }; -class QTPlayer; - struct VideoEntry { - QTPlayer *video; + Graphics::QuickTimeDecoder *video; uint16 x; uint16 y; bool loop; @@ -55,7 +57,7 @@ struct VideoEntry { uint16 id; // Riven only bool enabled; - QTPlayer *operator->() const { assert(video); return video; } + Graphics::QuickTimeDecoder *operator->() const { assert(video); return video; } }; typedef int32 VideoHandle; @@ -80,6 +82,7 @@ public: // Riven-related functions void activateMLST(uint16 mlstId, uint16 card); + void clearMLST(); void enableMovie(uint16 id); void disableMovie(uint16 id); void disableAllMovies(); @@ -87,19 +90,23 @@ public: void stopMovie(uint16 id); void playMovieBlocking(uint16 id); - // Riven-related variables - Common::Array<MLSTRecord> _mlstRecords; + // Handle functions + VideoHandle findVideoHandle(uint16 id); + int32 getCurFrame(const VideoHandle &handle); + uint32 getFrameCount(const VideoHandle &handle); private: MohawkEngine *_vm; - void waitUntilMovieEnds(VideoHandle videoHandle); + // Riven-related variables + Common::Array<MLSTRecord> _mlstRecords; // Keep tabs on any videos playing Common::Array<VideoEntry> _videoStreams; VideoHandle createVideoHandle(uint16 id, uint16 x, uint16 y, bool loop); VideoHandle createVideoHandle(Common::String filename, uint16 x, uint16 y, bool loop); + void waitUntilMovieEnds(VideoHandle videoHandle); }; } // End of namespace Mohawk diff --git a/engines/mohawk/video/cinepak.cpp b/engines/mohawk/video/cinepak.cpp deleted file mode 100644 index 2ffe6869ae..0000000000 --- a/engines/mohawk/video/cinepak.cpp +++ /dev/null @@ -1,286 +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. - * - * $URL$ - * $Id$ - * - */ - -#include "mohawk/video/cinepak.h" - -#include "common/system.h" -#include "graphics/conversion.h" // For YUV2RGB - -// Code here partially based off of ffmpeg ;) - -namespace Mohawk { - -#define PUT_PIXEL(offset, lum, u, v) \ - Graphics::CPYUV2RGB(lum, u, v, r, g, b); \ - if (_pixelFormat.bytesPerPixel == 2) \ - *((uint16 *)_curFrame.surface->pixels + offset) = _pixelFormat.RGBToColor(r, g, b); \ - else \ - *((uint32 *)_curFrame.surface->pixels + offset) = _pixelFormat.RGBToColor(r, g, b) - -CinepakDecoder::CinepakDecoder() : Graphics::Codec() { - _curFrame.surface = NULL; - _curFrame.strips = NULL; - _y = 0; - _pixelFormat = g_system->getScreenFormat(); - - // We're going to have to dither if we're running in 8bpp. - // We'll take RGBA8888 for best color performance in this case. - if (_pixelFormat.bytesPerPixel == 1) - _pixelFormat = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0); -} - -CinepakDecoder::~CinepakDecoder() { - if (_curFrame.surface) - _curFrame.surface->free(); - delete[] _curFrame.strips; -} - -Graphics::Surface *CinepakDecoder::decodeImage(Common::SeekableReadStream *stream) { - _curFrame.flags = stream->readByte(); - _curFrame.length = (stream->readByte() << 16) + stream->readUint16BE(); - _curFrame.width = stream->readUint16BE(); - _curFrame.height = stream->readUint16BE(); - _curFrame.stripCount = stream->readUint16BE(); - - if (_curFrame.strips == NULL) - _curFrame.strips = new CinepakStrip[_curFrame.stripCount]; - - debug (4, "Cinepak Frame: Width = %d, Height = %d, Strip Count = %d", _curFrame.width, _curFrame.height, _curFrame.stripCount); - -#if 0 - // Borrowed from FFMPEG. This should cut out the extra data Cinepak for Sega has (which is useless). - // The theory behind this is that this is here to confuse standard Cinepak decoders. But, we won't let that happen! ;) - if (_curFrame.length != (uint32)stream->size()) { - if (stream->readUint16BE() == 0xFE00) - stream->readUint32BE(); - } -#endif - - if (!_curFrame.surface) { - _curFrame.surface = new Graphics::Surface(); - _curFrame.surface->create(_curFrame.width, _curFrame.height, _pixelFormat.bytesPerPixel); - } - - // Reset the y variable. - _y = 0; - - for (uint16 i = 0; i < _curFrame.stripCount; i++) { - if (i > 0 && !(_curFrame.flags & 1)) { // Use codebooks from last strip - for (uint16 j = 0; j < 256; j++) { - _curFrame.strips[i].v1_codebook[j] = _curFrame.strips[i - 1].v1_codebook[j]; - _curFrame.strips[i].v4_codebook[j] = _curFrame.strips[i - 1].v4_codebook[j]; - } - } - - _curFrame.strips[i].id = stream->readUint16BE(); - _curFrame.strips[i].length = stream->readUint16BE() - 12; // Subtract the 12 byte header - _curFrame.strips[i].rect.top = _y; stream->readUint16BE(); // Ignore, substitute with our own. - _curFrame.strips[i].rect.left = 0; stream->readUint16BE(); // Ignore, substitute with our own - _curFrame.strips[i].rect.bottom = _y + stream->readUint16BE(); - _curFrame.strips[i].rect.right = _curFrame.width; stream->readUint16BE(); // Ignore, substitute with our own - - //printf ("Left = %d, Top = %d, Right = %d, Bottom = %d\n", _curFrame.strips[i].rect.left, _curFrame.strips[i].rect.top, _curFrame.strips[i].rect.right, _curFrame.strips[i].rect.bottom); - - // Sanity check. Because Cinepak is based on 4x4 blocks, the width and height of each strip needs to be divisible by 4. - assert(!(_curFrame.strips[i].rect.width() % 4) && !(_curFrame.strips[i].rect.height() % 4)); - - uint32 pos = stream->pos(); - - while ((uint32)stream->pos() < (pos + _curFrame.strips[i].length) && !stream->eos()) { - byte chunkID = stream->readByte(); - - if (stream->eos()) - break; - - // Chunk Size is 24-bit, ignore the first 4 bytes - uint32 chunkSize = stream->readByte() << 16; - chunkSize += stream->readUint16BE() - 4; - - int32 startPos = stream->pos(); - - switch (chunkID) { - case 0x20: - case 0x21: - case 0x24: - case 0x25: - loadCodebook(stream, i, 4, chunkID, chunkSize); - break; - case 0x22: - case 0x23: - case 0x26: - case 0x27: - loadCodebook(stream, i, 1, chunkID, chunkSize); - break; - case 0x30: - case 0x31: - case 0x32: - decodeVectors(stream, i, chunkID, chunkSize); - break; - default: - warning("Unknown Cinepak chunk ID %02x", chunkID); - return _curFrame.surface; - } - - if (stream->pos() != startPos + (int32)chunkSize) - stream->seek(startPos + chunkSize); - } - - _y = _curFrame.strips[i].rect.bottom; - } - - return _curFrame.surface; -} - -void CinepakDecoder::loadCodebook(Common::SeekableReadStream *stream, uint16 strip, byte codebookType, byte chunkID, uint32 chunkSize) { - CinepakCodebook *codebook = (codebookType == 1) ? _curFrame.strips[strip].v1_codebook : _curFrame.strips[strip].v4_codebook; - - int32 startPos = stream->pos(); - uint32 flag = 0, mask = 0; - - for (uint16 i = 0; i < 256; i++) { - if ((chunkID & 0x01) && !(mask >>= 1)) { - if ((stream->pos() - startPos + 4) > (int32)chunkSize) - break; - - flag = stream->readUint32BE(); - mask = 0x80000000; - } - - if (!(chunkID & 0x01) || (flag & mask)) { - byte n = (chunkID & 0x04) ? 4 : 6; - if ((stream->pos() - startPos + n) > (int32)chunkSize) - break; - - for (byte j = 0; j < 4; j++) - codebook[i].y[j] = stream->readByte(); - - if (n == 6) { - codebook[i].u = stream->readByte() + 128; - codebook[i].v = stream->readByte() + 128; - } else { - /* this codebook type indicates either greyscale or - * palettized video; if palettized, U & V components will - * not be used so it is safe to set them to 128 for the - * benefit of greyscale rendering in YUV420P */ - codebook[i].u = 128; - codebook[i].v = 128; - } - } - } -} - -void CinepakDecoder::decodeVectors(Common::SeekableReadStream *stream, uint16 strip, byte chunkID, uint32 chunkSize) { - uint32 flag = 0, mask = 0; - uint32 iy[4]; - int32 startPos = stream->pos(); - byte r = 0, g = 0, b = 0; - - for (uint16 y = _curFrame.strips[strip].rect.top; y < _curFrame.strips[strip].rect.bottom; y += 4) { - iy[0] = _curFrame.strips[strip].rect.left + y * _curFrame.width; - iy[1] = iy[0] + _curFrame.width; - iy[2] = iy[1] + _curFrame.width; - iy[3] = iy[2] + _curFrame.width; - - for (uint16 x = _curFrame.strips[strip].rect.left; x < _curFrame.strips[strip].rect.right; x += 4) { - if ((chunkID & 0x01) && !(mask >>= 1)) { - if ((stream->pos() - startPos + 4) > (int32)chunkSize) - return; - - flag = stream->readUint32BE(); - mask = 0x80000000; - } - - if (!(chunkID & 0x01) || (flag & mask)) { - if (!(chunkID & 0x02) && !(mask >>= 1)) { - if ((stream->pos() - startPos + 4) > (int32)chunkSize) - return; - - flag = stream->readUint32BE(); - mask = 0x80000000; - } - - if ((chunkID & 0x02) || (~flag & mask)) { - if ((stream->pos() - startPos + 1) > (int32)chunkSize) - return; - - // Get the codebook - CinepakCodebook codebook = _curFrame.strips[strip].v1_codebook[stream->readByte()]; - - PUT_PIXEL(iy[0] + 0, codebook.y[0], codebook.u, codebook.v); - PUT_PIXEL(iy[0] + 1, codebook.y[0], codebook.u, codebook.v); - PUT_PIXEL(iy[1] + 0, codebook.y[0], codebook.u, codebook.v); - PUT_PIXEL(iy[1] + 1, codebook.y[0], codebook.u, codebook.v); - - PUT_PIXEL(iy[0] + 2, codebook.y[1], codebook.u, codebook.v); - PUT_PIXEL(iy[0] + 3, codebook.y[1], codebook.u, codebook.v); - PUT_PIXEL(iy[1] + 2, codebook.y[1], codebook.u, codebook.v); - PUT_PIXEL(iy[1] + 3, codebook.y[1], codebook.u, codebook.v); - - PUT_PIXEL(iy[2] + 0, codebook.y[2], codebook.u, codebook.v); - PUT_PIXEL(iy[2] + 1, codebook.y[2], codebook.u, codebook.v); - PUT_PIXEL(iy[3] + 0, codebook.y[2], codebook.u, codebook.v); - PUT_PIXEL(iy[3] + 1, codebook.y[2], codebook.u, codebook.v); - - PUT_PIXEL(iy[2] + 2, codebook.y[3], codebook.u, codebook.v); - PUT_PIXEL(iy[2] + 3, codebook.y[3], codebook.u, codebook.v); - PUT_PIXEL(iy[3] + 2, codebook.y[3], codebook.u, codebook.v); - PUT_PIXEL(iy[3] + 3, codebook.y[3], codebook.u, codebook.v); - } else if (flag & mask) { - if ((stream->pos() - startPos + 4) > (int32)chunkSize) - return; - - CinepakCodebook codebook = _curFrame.strips[strip].v4_codebook[stream->readByte()]; - PUT_PIXEL(iy[0] + 0, codebook.y[0], codebook.u, codebook.v); - PUT_PIXEL(iy[0] + 1, codebook.y[1], codebook.u, codebook.v); - PUT_PIXEL(iy[1] + 0, codebook.y[2], codebook.u, codebook.v); - PUT_PIXEL(iy[1] + 1, codebook.y[3], codebook.u, codebook.v); - - codebook = _curFrame.strips[strip].v4_codebook[stream->readByte()]; - PUT_PIXEL(iy[0] + 2, codebook.y[0], codebook.u, codebook.v); - PUT_PIXEL(iy[0] + 3, codebook.y[1], codebook.u, codebook.v); - PUT_PIXEL(iy[1] + 2, codebook.y[2], codebook.u, codebook.v); - PUT_PIXEL(iy[1] + 3, codebook.y[3], codebook.u, codebook.v); - - codebook = _curFrame.strips[strip].v4_codebook[stream->readByte()]; - PUT_PIXEL(iy[2] + 0, codebook.y[0], codebook.u, codebook.v); - PUT_PIXEL(iy[2] + 1, codebook.y[1], codebook.u, codebook.v); - PUT_PIXEL(iy[3] + 0, codebook.y[2], codebook.u, codebook.v); - PUT_PIXEL(iy[3] + 1, codebook.y[3], codebook.u, codebook.v); - - codebook = _curFrame.strips[strip].v4_codebook[stream->readByte()]; - PUT_PIXEL(iy[2] + 2, codebook.y[0], codebook.u, codebook.v); - PUT_PIXEL(iy[2] + 3, codebook.y[1], codebook.u, codebook.v); - PUT_PIXEL(iy[3] + 2, codebook.y[2], codebook.u, codebook.v); - PUT_PIXEL(iy[3] + 3, codebook.y[3], codebook.u, codebook.v); - } - } - - for (byte i = 0; i < 4; i++) - iy[i] += 4; - } - } -} - -} // End of namespace Mohawk diff --git a/engines/mohawk/video/cinepak.h b/engines/mohawk/video/cinepak.h deleted file mode 100644 index 3f4cbba17c..0000000000 --- a/engines/mohawk/video/cinepak.h +++ /dev/null @@ -1,81 +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. - * - * $URL$ - * $Id$ - * - */ - -#ifndef CINEPAK_H -#define CINEPAK_H - -#include "common/scummsys.h" -#include "common/stream.h" -#include "common/rect.h" -#include "graphics/surface.h" -#include "graphics/pixelformat.h" - -#include "graphics/video/codecs/codec.h" - -namespace Mohawk { - -struct CinepakCodebook { - byte y[4]; - byte u, v; -}; - -struct CinepakStrip { - uint16 id; - uint16 length; - Common::Rect rect; - CinepakCodebook v1_codebook[256], v4_codebook[256]; -}; - -struct CinepakFrame { - byte flags; - uint32 length; - uint16 width; - uint16 height; - uint16 stripCount; - CinepakStrip *strips; - - Graphics::Surface *surface; -}; - -class CinepakDecoder : public Graphics::Codec { -public: - CinepakDecoder(); - ~CinepakDecoder(); - - Graphics::Surface *decodeImage(Common::SeekableReadStream *stream); - Graphics::PixelFormat getPixelFormat() const { return _pixelFormat; } - -private: - CinepakFrame _curFrame; - int32 _y; - Graphics::PixelFormat _pixelFormat; - - void loadCodebook(Common::SeekableReadStream *stream, uint16 strip, byte codebookType, byte chunkID, uint32 chunkSize); - void decodeVectors(Common::SeekableReadStream *stream, uint16 strip, byte chunkID, uint32 chunkSize); -}; - -} - -#endif diff --git a/engines/mohawk/video/qdm2.cpp b/engines/mohawk/video/qdm2.cpp deleted file mode 100644 index b91440f00d..0000000000 --- a/engines/mohawk/video/qdm2.cpp +++ /dev/null @@ -1,3063 +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. - * - * $URL$ - * $Id$ - * - */ - -// Based off ffmpeg's QDM2 decoder - -#include "mohawk/video/qdm2.h" -#include "mohawk/video/qdm2data.h" - -#include "common/system.h" - -namespace Mohawk { - -// Fix compilation for non C99-compliant compilers, like MSVC -#ifndef int64_t -typedef signed long long int int64_t; -#endif - -// Integer log2 function. This is much faster than invoking -// double precision C99 log2 math functions or equivalent, since -// this is only used to determine maximum number of bits needed -// i.e. only non-fractional part is needed. Also, the double -// version is incorrect for exact cases due to floating point -// rounding errors. -static inline int scummvm_log2(int n) { - int ret = -1; - while(n != 0) { - n /= 2; - ret++; - } - return ret; -} - -#define QDM2_LIST_ADD(list, size, packet) \ - do { \ - if (size > 0) \ - list[size - 1].next = &list[size]; \ - list[size].packet = packet; \ - list[size].next = NULL; \ - size++; \ - } while(0) - -// Result is 8, 16 or 30 -#define QDM2_SB_USED(subSampling) (((subSampling) >= 2) ? 30 : 8 << (subSampling)) - -#define FIX_NOISE_IDX(noiseIdx) \ - if ((noiseIdx) >= 3840) \ - (noiseIdx) -= 3840 \ - -#define SB_DITHERING_NOISE(sb, noiseIdx) (_noiseTable[(noiseIdx)++] * sb_noise_attenuation[(sb)]) - -static inline void initGetBits(GetBitContext *s, const uint8 *buffer, int bitSize) { - int bufferSize = (bitSize + 7) >> 3; - - debug(1, "void initGetBits(GetBitContext *s, const uint8 *buffer, int bitSize)"); - - if (bufferSize < 0 || bitSize < 0) { - bufferSize = bitSize = 0; - buffer = NULL; - } - - s->buffer = buffer; - s->sizeInBits = bitSize; - s->bufferEnd = buffer + bufferSize; - s->index = 0; -} - -static inline int getBitsCount(GetBitContext *s) { - debug(1, "int getBitsCount(GetBitContext *s)"); - return s->index; -} - -static inline unsigned int getBits1(GetBitContext *s) { - int index; - uint8 result; - - debug(1, "unsigned int getBits1(GetBitContext *s)"); - - index = s->index; - result = s->buffer[index >> 3]; - - debug(1, "index : %d", index); - - result >>= (index & 0x07); - result &= 1; - index++; - s->index = index; - - return result; -} - -static inline unsigned int getBits(GetBitContext *s, int n) { - int tmp, reCache, reIndex; - - debug(1, "unsigned int getBits(GetBitContext *s, int n)"); - - reIndex = s->index; - - debug(1, "reIndex : %d", reIndex); - - reCache = READ_LE_UINT32((const uint8 *)s->buffer + (reIndex >> 3)) >> (reIndex & 0x07); - - tmp = (reCache) & ((uint32)0xffffffff >> (32 - n)); - - s->index = reIndex + n; - - return tmp; -} - -static inline void skipBits(GetBitContext *s, int n) { - int reIndex, reCache; - - debug(1, "void skipBits(GetBitContext *s, int n)"); - - reIndex = s->index; - reCache = 0; - - debug(1, "reIndex : %d", reIndex); - - reCache = READ_LE_UINT32((const uint8 *)s->buffer + (reIndex >> 3)) >> (reIndex & 0x07); - s->index = reIndex + n; -} - -#define BITS_LEFT(length, gb) ((length) - getBitsCount((gb))) - -static int splitRadixPermutation(int i, int n, int inverse) { - if (n <= 2) - return i & 1; - - int m = n >> 1; - - if(!(i & m)) - return splitRadixPermutation(i, m, inverse) * 2; - - m >>= 1; - - if (inverse == !(i & m)) - return splitRadixPermutation(i, m, inverse) * 4 + 1; - - return splitRadixPermutation(i, m, inverse) * 4 - 1; -} - -// sin(2*pi*x/n) for 0<=x<n/4, followed by n/2<=x<3n/4 -float ff_sin_16[8]; -float ff_sin_32[16]; -float ff_sin_64[32]; -float ff_sin_128[64]; -float ff_sin_256[128]; -float ff_sin_512[256]; -float ff_sin_1024[512]; -float ff_sin_2048[1024]; -float ff_sin_4096[2048]; -float ff_sin_8192[4096]; -float ff_sin_16384[8192]; -float ff_sin_32768[16384]; -float ff_sin_65536[32768]; - -float *ff_sin_tabs[] = { - NULL, NULL, NULL, NULL, - ff_sin_16, ff_sin_32, ff_sin_64, ff_sin_128, ff_sin_256, ff_sin_512, ff_sin_1024, - ff_sin_2048, ff_sin_4096, ff_sin_8192, ff_sin_16384, ff_sin_32768, ff_sin_65536, -}; - -// cos(2*pi*x/n) for 0<=x<=n/4, followed by its reverse -float ff_cos_16[8]; -float ff_cos_32[16]; -float ff_cos_64[32]; -float ff_cos_128[64]; -float ff_cos_256[128]; -float ff_cos_512[256]; -float ff_cos_1024[512]; -float ff_cos_2048[1024]; -float ff_cos_4096[2048]; -float ff_cos_8192[4096]; -float ff_cos_16384[8192]; -float ff_cos_32768[16384]; -float ff_cos_65536[32768]; - -float *ff_cos_tabs[] = { - NULL, NULL, NULL, NULL, - ff_cos_16, ff_cos_32, ff_cos_64, ff_cos_128, ff_cos_256, ff_cos_512, ff_cos_1024, - ff_cos_2048, ff_cos_4096, ff_cos_8192, ff_cos_16384, ff_cos_32768, ff_cos_65536, -}; - -void initCosineTables(int index) { - int m = 1 << index; - double freq = 2 * PI / m; - float *tab = ff_cos_tabs[index]; - - for (int i = 0; i <= m / 4; i++) - tab[i] = cos(i * freq); - - for (int i = 1; i < m / 4; i++) - tab[m / 2 - i] = tab[i]; -} - -void fftPermute(FFTContext *s, FFTComplex *z) { - const uint16 *revtab = s->revtab; - int np = 1 << s->nbits; - - if (s->tmpBuf) { - // TODO: handle split-radix permute in a more optimal way, probably in-place - for (int j = 0; j < np; j++) - s->tmpBuf[revtab[j]] = z[j]; - memcpy(z, s->tmpBuf, np * sizeof(FFTComplex)); - return; - } - - // reverse - for (int j = 0; j < np; j++) { - int k = revtab[j]; - if (k < j) { - FFTComplex tmp = z[k]; - z[k] = z[j]; - z[j] = tmp; - } - } -} - -#define DECL_FFT(n,n2,n4) \ -static void fft##n(FFTComplex *z) { \ - fft##n2(z); \ - fft##n4(z + n4 * 2); \ - fft##n4(z + n4 * 3); \ - pass(z, ff_cos_##n, n4 / 2); \ -} - -#ifndef M_SQRT1_2 -#define M_SQRT1_2 7.0710678118654752440E-1 -#endif - -#define sqrthalf (float)M_SQRT1_2 - -#define BF(x,y,a,b) { \ - x = a - b; \ - y = a + b; \ -} - -#define BUTTERFLIES(a0, a1, a2, a3) { \ - BF(t3, t5, t5, t1); \ - BF(a2.re, a0.re, a0.re, t5); \ - BF(a3.im, a1.im, a1.im, t3); \ - BF(t4, t6, t2, t6); \ - BF(a3.re, a1.re, a1.re, t4); \ - BF(a2.im, a0.im, a0.im, t6); \ -} - -// force loading all the inputs before storing any. -// this is slightly slower for small data, but avoids store->load aliasing -// for addresses separated by large powers of 2. -#define BUTTERFLIES_BIG(a0, a1, a2, a3) { \ - float r0 = a0.re, i0 = a0.im, r1 = a1.re, i1 = a1.im; \ - BF(t3, t5, t5, t1); \ - BF(a2.re, a0.re, r0, t5); \ - BF(a3.im, a1.im, i1, t3); \ - BF(t4, t6, t2, t6); \ - BF(a3.re, a1.re, r1, t4); \ - BF(a2.im, a0.im, i0, t6); \ -} - -#define TRANSFORM(a0, a1, a2, a3, wre, wim) { \ - t1 = a2.re * wre + a2.im * wim; \ - t2 = a2.im * wre - a2.re * wim; \ - t5 = a3.re * wre - a3.im * wim; \ - t6 = a3.im * wre + a3.re * wim; \ - BUTTERFLIES(a0, a1, a2, a3) \ -} - -#define TRANSFORM_ZERO(a0, a1, a2, a3) { \ - t1 = a2.re; \ - t2 = a2.im; \ - t5 = a3.re; \ - t6 = a3.im; \ - BUTTERFLIES(a0, a1, a2, a3) \ -} - -// z[0...8n-1], w[1...2n-1] -#define PASS(name) \ -static void name(FFTComplex *z, const float *wre, unsigned int n) { \ - float t1, t2, t3, t4, t5, t6; \ - int o1 = 2 * n; \ - int o2 = 4 * n; \ - int o3 = 6 * n; \ - const float *wim = wre + o1; \ - n--; \ - \ - TRANSFORM_ZERO(z[0], z[o1], z[o2], z[o3]); \ - TRANSFORM(z[1], z[o1 + 1], z[o2 + 1], z[o3 + 1], wre[1], wim[-1]); \ - \ - do { \ - z += 2; \ - wre += 2; \ - wim -= 2; \ - TRANSFORM(z[0], z[o1], z[o2], z[o3], wre[0], wim[0]); \ - TRANSFORM(z[1], z[o1 + 1],z[o2 + 1], z[o3 + 1], wre[1], wim[-1]); \ - } while(--n); \ -} - -PASS(pass) -#undef BUTTERFLIES -#define BUTTERFLIES BUTTERFLIES_BIG -PASS(pass_big) - -static void fft4(FFTComplex *z) { - float t1, t2, t3, t4, t5, t6, t7, t8; - - BF(t3, t1, z[0].re, z[1].re); - BF(t8, t6, z[3].re, z[2].re); - BF(z[2].re, z[0].re, t1, t6); - BF(t4, t2, z[0].im, z[1].im); - BF(t7, t5, z[2].im, z[3].im); - BF(z[3].im, z[1].im, t4, t8); - BF(z[3].re, z[1].re, t3, t7); - BF(z[2].im, z[0].im, t2, t5); -} - -static void fft8(FFTComplex *z) { - float t1, t2, t3, t4, t5, t6, t7, t8; - - fft4(z); - - BF(t1, z[5].re, z[4].re, -z[5].re); - BF(t2, z[5].im, z[4].im, -z[5].im); - BF(t3, z[7].re, z[6].re, -z[7].re); - BF(t4, z[7].im, z[6].im, -z[7].im); - BF(t8, t1, t3, t1); - BF(t7, t2, t2, t4); - BF(z[4].re, z[0].re, z[0].re, t1); - BF(z[4].im, z[0].im, z[0].im, t2); - BF(z[6].re, z[2].re, z[2].re, t7); - BF(z[6].im, z[2].im, z[2].im, t8); - - TRANSFORM(z[1], z[3], z[5], z[7], sqrthalf, sqrthalf); -} - -#undef BF - -DECL_FFT(16,8,4) -DECL_FFT(32,16,8) -DECL_FFT(64,32,16) -DECL_FFT(128,64,32) -DECL_FFT(256,128,64) -DECL_FFT(512,256,128) -#define pass pass_big -DECL_FFT(1024,512,256) -DECL_FFT(2048,1024,512) -DECL_FFT(4096,2048,1024) -DECL_FFT(8192,4096,2048) -DECL_FFT(16384,8192,4096) -DECL_FFT(32768,16384,8192) -DECL_FFT(65536,32768,16384) - -void fftCalc(FFTContext *s, FFTComplex *z) { - static void (* const fftDispatch[])(FFTComplex*) = { - fft4, fft8, fft16, fft32, fft64, fft128, fft256, fft512, fft1024, - fft2048, fft4096, fft8192, fft16384, fft32768, fft65536, - }; - - fftDispatch[s->nbits - 2](z); -} - -// complex multiplication: p = a * b -#define CMUL(pre, pim, are, aim, bre, bim) \ -{\ - float _are = (are); \ - float _aim = (aim); \ - float _bre = (bre); \ - float _bim = (bim); \ - (pre) = _are * _bre - _aim * _bim; \ - (pim) = _are * _bim + _aim * _bre; \ -} - -/** - * Compute the middle half of the inverse MDCT of size N = 2^nbits, - * thus excluding the parts that can be derived by symmetry - * @param output N/2 samples - * @param input N/2 samples - */ -void imdctHalfC(FFTContext *s, float *output, const float *input) { - const uint16 *revtab = s->revtab; - const float *tcos = s->tcos; - const float *tsin = s->tsin; - FFTComplex *z = (FFTComplex *)output; - - int n = 1 << s->mdctBits; - int n2 = n >> 1; - int n4 = n >> 2; - int n8 = n >> 3; - - // pre rotation - const float *in1 = input; - const float *in2 = input + n2 - 1; - for (int k = 0; k < n4; k++) { - int j = revtab[k]; - CMUL(z[j].re, z[j].im, *in2, *in1, tcos[k], tsin[k]); - in1 += 2; - in2 -= 2; - } - - fftCalc(s, z); - - // post rotation + reordering - for (int k = 0; k < n8; k++) { - float r0, i0, r1, i1; - CMUL(r0, i1, z[n8 - k - 1].im, z[n8 - k - 1].re, tsin[n8 - k - 1], tcos[n8 - k - 1]); - CMUL(r1, i0, z[n8 + k].im, z[n8 + k].re, tsin[n8 + k], tcos[n8 + k]); - z[n8 - k - 1].re = r0; - z[n8 - k - 1].im = i0; - z[n8 + k].re = r1; - z[n8 + k].im = i1; - } -} - -/** - * Compute inverse MDCT of size N = 2^nbits - * @param output N samples - * @param input N/2 samples - */ -void imdctCalcC(FFTContext *s, float *output, const float *input) { - int n = 1 << s->mdctBits; - int n2 = n >> 1; - int n4 = n >> 2; - - imdctHalfC(s, output + n4, input); - - for (int k = 0; k < n4; k++) { - output[k] = -output[n2 - k - 1]; - output[n - k - 1] = output[n2 + k]; - } -} - -/** - * Compute MDCT of size N = 2^nbits - * @param input N samples - * @param out N/2 samples - */ -void mdctCalcC(FFTContext *s, float *out, const float *input) { - const uint16 *revtab = s->revtab; - const float *tcos = s->tcos; - const float *tsin = s->tsin; - FFTComplex *x = (FFTComplex *)out; - - int n = 1 << s->mdctBits; - int n2 = n >> 1; - int n4 = n >> 2; - int n8 = n >> 3; - int n3 = 3 * n4; - - // pre rotation - for (int i = 0; i < n8; i++) { - float re = -input[2 * i + 3 * n4] - input[n3 - 1 - 2 * i]; - float im = -input[n4 + 2 * i] + input[n4 - 1 - 2 * i]; - int j = revtab[i]; - CMUL(x[j].re, x[j].im, re, im, -tcos[i], tsin[i]); - - re = input[2 * i] - input[n2 - 1 - 2 * i]; - im = -(input[n2 + 2 * i] + input[n - 1 - 2 * i]); - j = revtab[n8 + i]; - CMUL(x[j].re, x[j].im, re, im, -tcos[n8 + i], tsin[n8 + i]); - } - - fftCalc(s, x); - - // post rotation - for (int i = 0; i < n8; i++) { - float r0, i0, r1, i1; - CMUL(i1, r0, x[n8 - i - 1].re, x[n8 - i - 1].im, -tsin[n8 - i - 1], -tcos[n8 - i - 1]); - CMUL(i0, r1, x[n8 + i].re, x[n8 + i].im, -tsin[n8 + i], -tcos[n8 + i]); - x[n8 - i - 1].re = r0; - x[n8 - i - 1].im = i0; - x[n8 + i].re = r1; - x[n8 + i].im = i1; - } -} - -int fftInit(FFTContext *s, int nbits, int inverse) { - int i, j, m, n; - float alpha, c1, s1, s2; - - if (nbits < 2 || nbits > 16) - goto fail; - - s->nbits = nbits; - n = 1 << nbits; - s->tmpBuf = NULL; - - s->exptab = (FFTComplex *)malloc((n / 2) * sizeof(FFTComplex)); - if (!s->exptab) - goto fail; - - s->revtab = (uint16 *)malloc(n * sizeof(uint16)); - if (!s->revtab) - goto fail; - s->inverse = inverse; - - s2 = inverse ? 1.0 : -1.0; - - s->fftPermute = fftPermute; - s->fftCalc = fftCalc; - s->imdctCalc = imdctCalcC; - s->imdctHalf = imdctHalfC; - s->mdctCalc = mdctCalcC; - s->splitRadix = 1; - - if (s->splitRadix) { - for (j = 4; j <= nbits; j++) - initCosineTables(j); - - for (i = 0; i < n; i++) - s->revtab[-splitRadixPermutation(i, n, s->inverse) & (n - 1)] = i; - - s->tmpBuf = (FFTComplex *)malloc(n * sizeof(FFTComplex)); - } else { - for (i = 0; i < n / 2; i++) { - alpha = 2 * PI * (float)i / (float)n; - c1 = cos(alpha); - s1 = sin(alpha) * s2; - s->exptab[i].re = c1; - s->exptab[i].im = s1; - } - - //int np = 1 << nbits; - //int nblocks = np >> 3; - //int np2 = np >> 1; - - // compute bit reverse table - for (i = 0; i < n; i++) { - m = 0; - - for (j = 0; j < nbits; j++) - m |= ((i >> j) & 1) << (nbits - j - 1); - - s->revtab[i] = m; - } - } - - return 0; - - fail: - free(&s->revtab); - free(&s->exptab); - free(&s->tmpBuf); - return -1; -} - -/** - * Sets up a real FFT. - * @param nbits log2 of the length of the input array - * @param trans the type of transform - */ -int rdftInit(RDFTContext *s, int nbits, RDFTransformType trans) { - int n = 1 << nbits; - const double theta = (trans == RDFT || trans == IRIDFT ? -1 : 1) * 2 * PI / n; - - s->nbits = nbits; - s->inverse = trans == IRDFT || trans == IRIDFT; - s->signConvention = trans == RIDFT || trans == IRIDFT ? 1 : -1; - - if (nbits < 4 || nbits > 16) - return -1; - - if (fftInit(&s->fft, nbits - 1, trans == IRDFT || trans == RIDFT) < 0) - return -1; - - initCosineTables(nbits); - s->tcos = ff_cos_tabs[nbits]; - s->tsin = ff_sin_tabs[nbits] + (trans == RDFT || trans == IRIDFT) * (n >> 2); - - for (int i = 0; i < n >> 2; i++) - s->tsin[i] = sin(i*theta); - - return 0; -} - -/** Map one real FFT into two parallel real even and odd FFTs. Then interleave - * the two real FFTs into one complex FFT. Unmangle the results. - * ref: http://www.engineeringproductivitytools.com/stuff/T0001/PT10.HTM - */ -void rdftCalc(RDFTContext *s, float *data) { - FFTComplex ev, od; - - const int n = 1 << s->nbits; - const float k1 = 0.5; - const float k2 = 0.5 - s->inverse; - const float *tcos = s->tcos; - const float *tsin = s->tsin; - - if (!s->inverse) { - fftPermute(&s->fft, (FFTComplex *)data); - fftCalc(&s->fft, (FFTComplex *)data); - } - - // i=0 is a special case because of packing, the DC term is real, so we - // are going to throw the N/2 term (also real) in with it. - ev.re = data[0]; - data[0] = ev.re + data[1]; - data[1] = ev.re - data[1]; - - int i; - - for (i = 1; i < n >> 2; i++) { - int i1 = i * 2; - int i2 = n - i1; - - // Separate even and odd FFTs - ev.re = k1 * (data[i1] + data[i2]); - od.im = -k2 * (data[i1] - data[i2]); - ev.im = k1 * (data[i1 + 1] - data[i2 + 1]); - od.re = k2 * (data[i1 + 1] + data[i2 + 1]); - - // Apply twiddle factors to the odd FFT and add to the even FFT - data[i1] = ev.re + od.re * tcos[i] - od.im * tsin[i]; - data[i1 + 1] = ev.im + od.im * tcos[i] + od.re * tsin[i]; - data[i2] = ev.re - od.re * tcos[i] + od.im * tsin[i]; - data[i2 + 1] = -ev.im + od.im * tcos[i] + od.re * tsin[i]; - } - - data[i * 2 + 1] = s->signConvention * data[i * 2 + 1]; - if (s->inverse) { - data[0] *= k1; - data[1] *= k1; - fftPermute(&s->fft, (FFTComplex*)data); - fftCalc(&s->fft, (FFTComplex*)data); - } -} - -// half mpeg encoding window (full precision) -const int32 ff_mpa_enwindow[257] = { - 0, -1, -1, -1, -1, -1, -1, -2, - -2, -2, -2, -3, -3, -4, -4, -5, - -5, -6, -7, -7, -8, -9, -10, -11, - -13, -14, -16, -17, -19, -21, -24, -26, - -29, -31, -35, -38, -41, -45, -49, -53, - -58, -63, -68, -73, -79, -85, -91, -97, - -104, -111, -117, -125, -132, -139, -147, -154, - -161, -169, -176, -183, -190, -196, -202, -208, - 213, 218, 222, 225, 227, 228, 228, 227, - 224, 221, 215, 208, 200, 189, 177, 163, - 146, 127, 106, 83, 57, 29, -2, -36, - -72, -111, -153, -197, -244, -294, -347, -401, - -459, -519, -581, -645, -711, -779, -848, -919, - -991, -1064, -1137, -1210, -1283, -1356, -1428, -1498, - -1567, -1634, -1698, -1759, -1817, -1870, -1919, -1962, - -2001, -2032, -2057, -2075, -2085, -2087, -2080, -2063, - 2037, 2000, 1952, 1893, 1822, 1739, 1644, 1535, - 1414, 1280, 1131, 970, 794, 605, 402, 185, - -45, -288, -545, -814, -1095, -1388, -1692, -2006, - -2330, -2663, -3004, -3351, -3705, -4063, -4425, -4788, - -5153, -5517, -5879, -6237, -6589, -6935, -7271, -7597, - -7910, -8209, -8491, -8755, -8998, -9219, -9416, -9585, - -9727, -9838, -9916, -9959, -9966, -9935, -9863, -9750, - -9592, -9389, -9139, -8840, -8492, -8092, -7640, -7134, - 6574, 5959, 5288, 4561, 3776, 2935, 2037, 1082, - 70, -998, -2122, -3300, -4533, -5818, -7154, -8540, - -9975,-11455,-12980,-14548,-16155,-17799,-19478,-21189, --22929,-24694,-26482,-28289,-30112,-31947,-33791,-35640, --37489,-39336,-41176,-43006,-44821,-46617,-48390,-50137, --51853,-53534,-55178,-56778,-58333,-59838,-61289,-62684, --64019,-65290,-66494,-67629,-68692,-69679,-70590,-71420, --72169,-72835,-73415,-73908,-74313,-74630,-74856,-74992, - 75038 -}; - -void ff_mpa_synth_init(int16 *window) { - int i; - int32 v; - - // max = 18760, max sum over all 16 coefs : 44736 - for(i = 0; i < 257; i++) { - v = ff_mpa_enwindow[i]; - v = (v + 2) >> 2; - window[i] = v; - - if ((i & 63) != 0) - v = -v; - - if (i != 0) - window[512 - i] = v; - } -} - -static inline uint16 round_sample(int *sum) { - int sum1; - sum1 = (*sum) >> 14; - *sum &= (1 << 14)-1; - if (sum1 < (-0x7fff - 1)) - sum1 = (-0x7fff - 1); - if (sum1 > 0x7fff) - sum1 = 0x7fff; - return sum1; -} - -static inline int MULH(int a, int b) { - return ((int64_t)(a) * (int64_t)(b))>>32; -} - -// signed 16x16 -> 32 multiply add accumulate -#define MACS(rt, ra, rb) rt += (ra) * (rb) - -#define MLSS(rt, ra, rb) ((rt) -= (ra) * (rb)) - -#define SUM8(op, sum, w, p)\ -{\ - op(sum, (w)[0 * 64], (p)[0 * 64]);\ - op(sum, (w)[1 * 64], (p)[1 * 64]);\ - op(sum, (w)[2 * 64], (p)[2 * 64]);\ - op(sum, (w)[3 * 64], (p)[3 * 64]);\ - op(sum, (w)[4 * 64], (p)[4 * 64]);\ - op(sum, (w)[5 * 64], (p)[5 * 64]);\ - op(sum, (w)[6 * 64], (p)[6 * 64]);\ - op(sum, (w)[7 * 64], (p)[7 * 64]);\ -} - -#define SUM8P2(sum1, op1, sum2, op2, w1, w2, p) \ -{\ - tmp_s = p[0 * 64];\ - op1(sum1, (w1)[0 * 64], tmp_s);\ - op2(sum2, (w2)[0 * 64], tmp_s);\ - tmp_s = p[1 * 64];\ - op1(sum1, (w1)[1 * 64], tmp_s);\ - op2(sum2, (w2)[1 * 64], tmp_s);\ - tmp_s = p[2 * 64];\ - op1(sum1, (w1)[2 * 64], tmp_s);\ - op2(sum2, (w2)[2 * 64], tmp_s);\ - tmp_s = p[3 * 64];\ - op1(sum1, (w1)[3 * 64], tmp_s);\ - op2(sum2, (w2)[3 * 64], tmp_s);\ - tmp_s = p[4 * 64];\ - op1(sum1, (w1)[4 * 64], tmp_s);\ - op2(sum2, (w2)[4 * 64], tmp_s);\ - tmp_s = p[5 * 64];\ - op1(sum1, (w1)[5 * 64], tmp_s);\ - op2(sum2, (w2)[5 * 64], tmp_s);\ - tmp_s = p[6 * 64];\ - op1(sum1, (w1)[6 * 64], tmp_s);\ - op2(sum2, (w2)[6 * 64], tmp_s);\ - tmp_s = p[7 * 64];\ - op1(sum1, (w1)[7 * 64], tmp_s);\ - op2(sum2, (w2)[7 * 64], tmp_s);\ -} - -#define FIXHR(a) ((int)((a) * (1LL<<32) + 0.5)) - -// tab[i][j] = 1.0 / (2.0 * cos(pi*(2*k+1) / 2^(6 - j))) - -// cos(i*pi/64) - -#define COS0_0 FIXHR(0.50060299823519630134/2) -#define COS0_1 FIXHR(0.50547095989754365998/2) -#define COS0_2 FIXHR(0.51544730992262454697/2) -#define COS0_3 FIXHR(0.53104259108978417447/2) -#define COS0_4 FIXHR(0.55310389603444452782/2) -#define COS0_5 FIXHR(0.58293496820613387367/2) -#define COS0_6 FIXHR(0.62250412303566481615/2) -#define COS0_7 FIXHR(0.67480834145500574602/2) -#define COS0_8 FIXHR(0.74453627100229844977/2) -#define COS0_9 FIXHR(0.83934964541552703873/2) -#define COS0_10 FIXHR(0.97256823786196069369/2) -#define COS0_11 FIXHR(1.16943993343288495515/4) -#define COS0_12 FIXHR(1.48416461631416627724/4) -#define COS0_13 FIXHR(2.05778100995341155085/8) -#define COS0_14 FIXHR(3.40760841846871878570/8) -#define COS0_15 FIXHR(10.19000812354805681150/32) - -#define COS1_0 FIXHR(0.50241928618815570551/2) -#define COS1_1 FIXHR(0.52249861493968888062/2) -#define COS1_2 FIXHR(0.56694403481635770368/2) -#define COS1_3 FIXHR(0.64682178335999012954/2) -#define COS1_4 FIXHR(0.78815462345125022473/2) -#define COS1_5 FIXHR(1.06067768599034747134/4) -#define COS1_6 FIXHR(1.72244709823833392782/4) -#define COS1_7 FIXHR(5.10114861868916385802/16) - -#define COS2_0 FIXHR(0.50979557910415916894/2) -#define COS2_1 FIXHR(0.60134488693504528054/2) -#define COS2_2 FIXHR(0.89997622313641570463/2) -#define COS2_3 FIXHR(2.56291544774150617881/8) - -#define COS3_0 FIXHR(0.54119610014619698439/2) -#define COS3_1 FIXHR(1.30656296487637652785/4) - -#define COS4_0 FIXHR(0.70710678118654752439/2) - -/* butterfly operator */ -#define BF(a, b, c, s)\ -{\ - tmp0 = tab[a] + tab[b];\ - tmp1 = tab[a] - tab[b];\ - tab[a] = tmp0;\ - tab[b] = MULH(tmp1<<(s), c);\ -} - -#define BF1(a, b, c, d)\ -{\ - BF(a, b, COS4_0, 1);\ - BF(c, d,-COS4_0, 1);\ - tab[c] += tab[d];\ -} - -#define BF2(a, b, c, d)\ -{\ - BF(a, b, COS4_0, 1);\ - BF(c, d,-COS4_0, 1);\ - tab[c] += tab[d];\ - tab[a] += tab[c];\ - tab[c] += tab[b];\ - tab[b] += tab[d];\ -} - -#define ADD(a, b) tab[a] += tab[b] - -// DCT32 without 1/sqrt(2) coef zero scaling. -static void dct32(int32 *out, int32 *tab) { - int tmp0, tmp1; - - // pass 1 - BF( 0, 31, COS0_0 , 1); - BF(15, 16, COS0_15, 5); - // pass 2 - BF( 0, 15, COS1_0 , 1); - BF(16, 31,-COS1_0 , 1); - // pass 1 - BF( 7, 24, COS0_7 , 1); - BF( 8, 23, COS0_8 , 1); - // pass 2 - BF( 7, 8, COS1_7 , 4); - BF(23, 24,-COS1_7 , 4); - // pass 3 - BF( 0, 7, COS2_0 , 1); - BF( 8, 15,-COS2_0 , 1); - BF(16, 23, COS2_0 , 1); - BF(24, 31,-COS2_0 , 1); - // pass 1 - BF( 3, 28, COS0_3 , 1); - BF(12, 19, COS0_12, 2); - // pass 2 - BF( 3, 12, COS1_3 , 1); - BF(19, 28,-COS1_3 , 1); - // pass 1 - BF( 4, 27, COS0_4 , 1); - BF(11, 20, COS0_11, 2); - // pass 2 - BF( 4, 11, COS1_4 , 1); - BF(20, 27,-COS1_4 , 1); - // pass 3 - BF( 3, 4, COS2_3 , 3); - BF(11, 12,-COS2_3 , 3); - BF(19, 20, COS2_3 , 3); - BF(27, 28,-COS2_3 , 3); - // pass 4 - BF( 0, 3, COS3_0 , 1); - BF( 4, 7,-COS3_0 , 1); - BF( 8, 11, COS3_0 , 1); - BF(12, 15,-COS3_0 , 1); - BF(16, 19, COS3_0 , 1); - BF(20, 23,-COS3_0 , 1); - BF(24, 27, COS3_0 , 1); - BF(28, 31,-COS3_0 , 1); - - // pass 1 - BF( 1, 30, COS0_1 , 1); - BF(14, 17, COS0_14, 3); - // pass 2 - BF( 1, 14, COS1_1 , 1); - BF(17, 30,-COS1_1 , 1); - // pass 1 - BF( 6, 25, COS0_6 , 1); - BF( 9, 22, COS0_9 , 1); - // pass 2 - BF( 6, 9, COS1_6 , 2); - BF(22, 25,-COS1_6 , 2); - // pass 3 - BF( 1, 6, COS2_1 , 1); - BF( 9, 14,-COS2_1 , 1); - BF(17, 22, COS2_1 , 1); - BF(25, 30,-COS2_1 , 1); - - // pass 1 - BF( 2, 29, COS0_2 , 1); - BF(13, 18, COS0_13, 3); - // pass 2 - BF( 2, 13, COS1_2 , 1); - BF(18, 29,-COS1_2 , 1); - // pass 1 - BF( 5, 26, COS0_5 , 1); - BF(10, 21, COS0_10, 1); - // pass 2 - BF( 5, 10, COS1_5 , 2); - BF(21, 26,-COS1_5 , 2); - // pass 3 - BF( 2, 5, COS2_2 , 1); - BF(10, 13,-COS2_2 , 1); - BF(18, 21, COS2_2 , 1); - BF(26, 29,-COS2_2 , 1); - // pass 4 - BF( 1, 2, COS3_1 , 2); - BF( 5, 6,-COS3_1 , 2); - BF( 9, 10, COS3_1 , 2); - BF(13, 14,-COS3_1 , 2); - BF(17, 18, COS3_1 , 2); - BF(21, 22,-COS3_1 , 2); - BF(25, 26, COS3_1 , 2); - BF(29, 30,-COS3_1 , 2); - - // pass 5 - BF1( 0, 1, 2, 3); - BF2( 4, 5, 6, 7); - BF1( 8, 9, 10, 11); - BF2(12, 13, 14, 15); - BF1(16, 17, 18, 19); - BF2(20, 21, 22, 23); - BF1(24, 25, 26, 27); - BF2(28, 29, 30, 31); - - // pass 6 - ADD( 8, 12); - ADD(12, 10); - ADD(10, 14); - ADD(14, 9); - ADD( 9, 13); - ADD(13, 11); - ADD(11, 15); - - out[ 0] = tab[0]; - out[16] = tab[1]; - out[ 8] = tab[2]; - out[24] = tab[3]; - out[ 4] = tab[4]; - out[20] = tab[5]; - out[12] = tab[6]; - out[28] = tab[7]; - out[ 2] = tab[8]; - out[18] = tab[9]; - out[10] = tab[10]; - out[26] = tab[11]; - out[ 6] = tab[12]; - out[22] = tab[13]; - out[14] = tab[14]; - out[30] = tab[15]; - - ADD(24, 28); - ADD(28, 26); - ADD(26, 30); - ADD(30, 25); - ADD(25, 29); - ADD(29, 27); - ADD(27, 31); - - out[ 1] = tab[16] + tab[24]; - out[17] = tab[17] + tab[25]; - out[ 9] = tab[18] + tab[26]; - out[25] = tab[19] + tab[27]; - out[ 5] = tab[20] + tab[28]; - out[21] = tab[21] + tab[29]; - out[13] = tab[22] + tab[30]; - out[29] = tab[23] + tab[31]; - out[ 3] = tab[24] + tab[20]; - out[19] = tab[25] + tab[21]; - out[11] = tab[26] + tab[22]; - out[27] = tab[27] + tab[23]; - out[ 7] = tab[28] + tab[18]; - out[23] = tab[29] + tab[19]; - out[15] = tab[30] + tab[17]; - out[31] = tab[31]; -} - -// 32 sub band synthesis filter. Input: 32 sub band samples, Output: -// 32 samples. -// XXX: optimize by avoiding ring buffer usage -void ff_mpa_synth_filter(int16 *synth_buf_ptr, int *synth_buf_offset, - int16 *window, int *dither_state, - int16 *samples, int incr, - int32 sb_samples[32]) -{ - int16 *synth_buf; - const int16 *w, *w2, *p; - int j, offset; - int16 *samples2; - int32 tmp[32]; - int sum, sum2; - int tmp_s; - - offset = *synth_buf_offset; - synth_buf = synth_buf_ptr + offset; - - dct32(tmp, sb_samples); - for(j = 0; j < 32; j++) { - // NOTE: can cause a loss in precision if very high amplitude sound - if (tmp[j] < (-0x7fff - 1)) - synth_buf[j] = (-0x7fff - 1); - else if (tmp[j] > 0x7fff) - synth_buf[j] = 0x7fff; - else - synth_buf[j] = tmp[j]; - } - - // copy to avoid wrap - memcpy(synth_buf + 512, synth_buf, 32 * sizeof(int16)); - - samples2 = samples + 31 * incr; - w = window; - w2 = window + 31; - - sum = *dither_state; - p = synth_buf + 16; - SUM8(MACS, sum, w, p); - p = synth_buf + 48; - SUM8(MLSS, sum, w + 32, p); - *samples = round_sample(&sum); - samples += incr; - w++; - - // we calculate two samples at the same time to avoid one memory - // access per two sample - for(j = 1; j < 16; j++) { - sum2 = 0; - p = synth_buf + 16 + j; - SUM8P2(sum, MACS, sum2, MLSS, w, w2, p); - p = synth_buf + 48 - j; - SUM8P2(sum, MLSS, sum2, MLSS, w + 32, w2 + 32, p); - - *samples = round_sample(&sum); - samples += incr; - sum += sum2; - *samples2 = round_sample(&sum); - samples2 -= incr; - w++; - w2--; - } - - p = synth_buf + 32; - SUM8(MLSS, sum, w + 32, p); - *samples = round_sample(&sum); - *dither_state= sum; - - offset = (offset - 32) & 511; - *synth_buf_offset = offset; -} - -/** - * parses a vlc code, faster then get_vlc() - * @param bits is the number of bits which will be read at once, must be - * identical to nb_bits in init_vlc() - * @param max_depth is the number of times bits bits must be read to completely - * read the longest vlc code - * = (max_vlc_length + bits - 1) / bits - */ -static int getVlc2(GetBitContext *s, int16 (*table)[2], int bits, int maxDepth) { - int reIndex; - int reCache; - int index; - int code; - int n; - - debug(1, "int getVlc2(GetBitContext *s, int16 (*table)[2], int bits, int maxDepth)"); - - reIndex = s->index; - reCache = READ_LE_UINT32(s->buffer + (reIndex >> 3)) >> (reIndex & 0x07); - index = reCache & (0xffffffff >> (32 - bits)); - code = table[index][0]; - n = table[index][1]; - - debug(1, "reIndex : %d", reIndex); - debug(1, "reCache : %d", reCache); - debug(1, "index : %d", index); - debug(1, "code : %d", code); - debug(1, "n : %d", n); - - if (maxDepth > 1 && n < 0){ - reIndex += bits; - reCache = READ_LE_UINT32(s->buffer + (reIndex >> 3)) >> (reIndex & 0x07); - - int nbBits = -n; - - index = (reCache & (0xffffffff >> (32 - nbBits))) + code; - code = table[index][0]; - n = table[index][1]; - - if(maxDepth > 2 && n < 0) { - reIndex += nbBits; - reCache = READ_LE_UINT32(s->buffer + (reIndex >> 3)) >> (reIndex & 0x07); - - nbBits = -n; - - index = (reCache & (0xffffffff >> (32 - nbBits))) + code; - code = table[index][0]; - n = table[index][1]; - } - } - - reCache >>= n; - s->index = reIndex + n; - return code; -} - -static int allocTable(VLC *vlc, int size, int use_static) { - int index; - index = vlc->table_size; - vlc->table_size += size; - if (vlc->table_size > vlc->table_allocated) { - if(use_static) - error("QDM2 cant do anything, init_vlc() is used with too little memory"); - vlc->table_allocated += (1 << vlc->bits); - vlc->table = (int16 (*)[2])realloc(vlc->table, sizeof(int16 *) * 2 * vlc->table_allocated); - if (!vlc->table) - return -1; - } - return index; -} - -#define GET_DATA(v, table, i, wrap, size)\ -{\ - const uint8 *ptr = (const uint8 *)table + i * wrap;\ - switch(size) {\ - case 1:\ - v = *(const uint8 *)ptr;\ - break;\ - case 2:\ - v = *(const uint16 *)ptr;\ - break;\ - default:\ - v = *(const uint32 *)ptr;\ - break;\ - }\ -} - -static int build_table(VLC *vlc, int table_nb_bits, - int nb_codes, - const void *bits, int bits_wrap, int bits_size, - const void *codes, int codes_wrap, int codes_size, - const void *symbols, int symbols_wrap, int symbols_size, - int code_prefix, int n_prefix, int flags) -{ - int i, j, k, n, table_size, table_index, nb, n1, index, code_prefix2, symbol; - uint32 code; - int16 (*table)[2]; - - table_size = 1 << table_nb_bits; - table_index = allocTable(vlc, table_size, flags & 4); - debug(2, "QDM2 new table index=%d size=%d code_prefix=%x n=%d", table_index, table_size, code_prefix, n_prefix); - if (table_index < 0) - return -1; - table = &vlc->table[table_index]; - - for(i = 0; i < table_size; i++) { - table[i][1] = 0; //bits - table[i][0] = -1; //codes - } - - // first pass: map codes and compute auxillary table sizes - for(i = 0; i < nb_codes; i++) { - GET_DATA(n, bits, i, bits_wrap, bits_size); - GET_DATA(code, codes, i, codes_wrap, codes_size); - // we accept tables with holes - if (n <= 0) - continue; - if (!symbols) - symbol = i; - else - GET_DATA(symbol, symbols, i, symbols_wrap, symbols_size); - debug(2, "QDM2 i=%d n=%d code=0x%x", i, n, code); - // if code matches the prefix, it is in the table - n -= n_prefix; - if(flags & 2) - code_prefix2= code & (n_prefix>=32 ? 0xffffffff : (1 << n_prefix)-1); - else - code_prefix2= code >> n; - if (n > 0 && code_prefix2 == code_prefix) { - if (n <= table_nb_bits) { - // no need to add another table - j = (code << (table_nb_bits - n)) & (table_size - 1); - nb = 1 << (table_nb_bits - n); - for(k = 0; k < nb; k++) { - if(flags & 2) - j = (code >> n_prefix) + (k<<n); - debug(2, "QDM2 %4x: code=%d n=%d",j, i, n); - if (table[j][1] /*bits*/ != 0) { - error("QDM2 incorrect codes"); - return -1; - } - table[j][1] = n; //bits - table[j][0] = symbol; - j++; - } - } else { - n -= table_nb_bits; - j = (code >> ((flags & 2) ? n_prefix : n)) & ((1 << table_nb_bits) - 1); - debug(2, "QDM2 %4x: n=%d (subtable)", j, n); - // compute table size - n1 = -table[j][1]; //bits - if (n > n1) - n1 = n; - table[j][1] = -n1; //bits - } - } - } - - // second pass : fill auxillary tables recursively - for(i = 0;i < table_size; i++) { - n = table[i][1]; //bits - if (n < 0) { - n = -n; - if (n > table_nb_bits) { - n = table_nb_bits; - table[i][1] = -n; //bits - } - index = build_table(vlc, n, nb_codes, - bits, bits_wrap, bits_size, - codes, codes_wrap, codes_size, - symbols, symbols_wrap, symbols_size, - (flags & 2) ? (code_prefix | (i << n_prefix)) : ((code_prefix << table_nb_bits) | i), - n_prefix + table_nb_bits, flags); - if (index < 0) - return -1; - // note: realloc has been done, so reload tables - table = &vlc->table[table_index]; - table[i][0] = index; //code - } - } - return table_index; -} - -/* Build VLC decoding tables suitable for use with get_vlc(). - - 'nb_bits' set thee decoding table size (2^nb_bits) entries. The - bigger it is, the faster is the decoding. But it should not be too - big to save memory and L1 cache. '9' is a good compromise. - - 'nb_codes' : number of vlcs codes - - 'bits' : table which gives the size (in bits) of each vlc code. - - 'codes' : table which gives the bit pattern of of each vlc code. - - 'symbols' : table which gives the values to be returned from get_vlc(). - - 'xxx_wrap' : give the number of bytes between each entry of the - 'bits' or 'codes' tables. - - 'xxx_size' : gives the number of bytes of each entry of the 'bits' - or 'codes' tables. - - 'wrap' and 'size' allows to use any memory configuration and types - (byte/word/long) to store the 'bits', 'codes', and 'symbols' tables. - - 'use_static' should be set to 1 for tables, which should be freed - with av_free_static(), 0 if free_vlc() will be used. -*/ -void initVlcSparse(VLC *vlc, int nb_bits, int nb_codes, - const void *bits, int bits_wrap, int bits_size, - const void *codes, int codes_wrap, int codes_size, - const void *symbols, int symbols_wrap, int symbols_size) { - vlc->bits = nb_bits; - - if(vlc->table_size && vlc->table_size == vlc->table_allocated) { - return; - } else if(vlc->table_size) { - error("called on a partially initialized table"); - } - - debug(2, "QDM2 build table nb_codes=%d", nb_codes); - - if (build_table(vlc, nb_bits, nb_codes, - bits, bits_wrap, bits_size, - codes, codes_wrap, codes_size, - symbols, symbols_wrap, symbols_size, - 0, 0, 4 | 2) < 0) { - free(&vlc->table); - return; // Error - } - - if(vlc->table_size != vlc->table_allocated) - error("QDM2 needed %d had %d", vlc->table_size, vlc->table_allocated); -} - -void QDM2Stream::softclipTableInit(void) { - uint16 i; - double dfl = SOFTCLIP_THRESHOLD - 32767; - float delta = 1.0 / -dfl; - - for (i = 0; i < ARRAYSIZE(_softclipTable); i++) - _softclipTable[i] = SOFTCLIP_THRESHOLD - ((int)(sin((float)i * delta) * dfl) & 0x0000FFFF); -} - -// random generated table -void QDM2Stream::rndTableInit(void) { - uint16 i; - uint16 j; - uint32 ldw, hdw; - // TODO: Replace Code with uint64 less version... - int64_t tmp64_1; - int64_t random_seed = 0; - float delta = 1.0 / 16384.0; - - for(i = 0; i < ARRAYSIZE(_noiseTable); i++) { - random_seed = random_seed * 214013 + 2531011; - _noiseTable[i] = (delta * (float)(((int32)random_seed >> 16) & 0x00007FFF)- 1.0) * 1.3; - } - - for (i = 0; i < 256; i++) { - random_seed = 81; - ldw = i; - for (j = 0; j < 5; j++) { - _randomDequantIndex[i][j] = (uint8)((ldw / random_seed) & 0xFF); - ldw = (uint32)ldw % (uint32)random_seed; - tmp64_1 = (random_seed * 0x55555556); - hdw = (uint32)(tmp64_1 >> 32); - random_seed = (int64_t)(hdw + (ldw >> 31)); - } - } - - for (i = 0; i < 128; i++) { - random_seed = 25; - ldw = i; - for (j = 0; j < 3; j++) { - _randomDequantType24[i][j] = (uint8)((ldw / random_seed) & 0xFF); - ldw = (uint32)ldw % (uint32)random_seed; - tmp64_1 = (random_seed * 0x66666667); - hdw = (uint32)(tmp64_1 >> 33); - random_seed = hdw + (ldw >> 31); - } - } -} - -void QDM2Stream::initNoiseSamples(void) { - uint16 i; - uint32 random_seed = 0; - float delta = 1.0 / 16384.0; - - for (i = 0; i < ARRAYSIZE(_noiseSamples); i++) { - random_seed = random_seed * 214013 + 2531011; - _noiseSamples[i] = (delta * (float)((random_seed >> 16) & 0x00007fff) - 1.0); - } -} - -static const uint16 qdm2_vlc_offs[18] = { - 0, 260, 566, 598, 894, 1166, 1230, 1294, 1678, 1950, 2214, 2278, 2310, 2570, 2834, 3124, 3448, 3838 -}; - -void QDM2Stream::initVlc(void) { - static int16 qdm2_table[3838][2]; - - if (!_vlcsInitialized) { - _vlcTabLevel.table = &qdm2_table[qdm2_vlc_offs[0]]; - _vlcTabLevel.table_allocated = qdm2_vlc_offs[1] - qdm2_vlc_offs[0]; - _vlcTabLevel.table_size = 0; - initVlcSparse(&_vlcTabLevel, 8, 24, - vlc_tab_level_huffbits, 1, 1, - vlc_tab_level_huffcodes, 2, 2, NULL, 0, 0); - - _vlcTabDiff.table = &qdm2_table[qdm2_vlc_offs[1]]; - _vlcTabDiff.table_allocated = qdm2_vlc_offs[2] - qdm2_vlc_offs[1]; - _vlcTabDiff.table_size = 0; - initVlcSparse(&_vlcTabDiff, 8, 37, - vlc_tab_diff_huffbits, 1, 1, - vlc_tab_diff_huffcodes, 2, 2, NULL, 0, 0); - - _vlcTabRun.table = &qdm2_table[qdm2_vlc_offs[2]]; - _vlcTabRun.table_allocated = qdm2_vlc_offs[3] - qdm2_vlc_offs[2]; - _vlcTabRun.table_size = 0; - initVlcSparse(&_vlcTabRun, 5, 6, - vlc_tab_run_huffbits, 1, 1, - vlc_tab_run_huffcodes, 1, 1, NULL, 0, 0); - - _fftLevelExpAltVlc.table = &qdm2_table[qdm2_vlc_offs[3]]; - _fftLevelExpAltVlc.table_allocated = qdm2_vlc_offs[4] - qdm2_vlc_offs[3]; - _fftLevelExpAltVlc.table_size = 0; - initVlcSparse(&_fftLevelExpAltVlc, 8, 28, - fft_level_exp_alt_huffbits, 1, 1, - fft_level_exp_alt_huffcodes, 2, 2, NULL, 0, 0); - - _fftLevelExpVlc.table = &qdm2_table[qdm2_vlc_offs[4]]; - _fftLevelExpVlc.table_allocated = qdm2_vlc_offs[5] - qdm2_vlc_offs[4]; - _fftLevelExpVlc.table_size = 0; - initVlcSparse(&_fftLevelExpVlc, 8, 20, - fft_level_exp_huffbits, 1, 1, - fft_level_exp_huffcodes, 2, 2, NULL, 0, 0); - - _fftStereoExpVlc.table = &qdm2_table[qdm2_vlc_offs[5]]; - _fftStereoExpVlc.table_allocated = qdm2_vlc_offs[6] - qdm2_vlc_offs[5]; - _fftStereoExpVlc.table_size = 0; - initVlcSparse(&_fftStereoExpVlc, 6, 7, - fft_stereo_exp_huffbits, 1, 1, - fft_stereo_exp_huffcodes, 1, 1, NULL, 0, 0); - - _fftStereoPhaseVlc.table = &qdm2_table[qdm2_vlc_offs[6]]; - _fftStereoPhaseVlc.table_allocated = qdm2_vlc_offs[7] - qdm2_vlc_offs[6]; - _fftStereoPhaseVlc.table_size = 0; - initVlcSparse(&_fftStereoPhaseVlc, 6, 9, - fft_stereo_phase_huffbits, 1, 1, - fft_stereo_phase_huffcodes, 1, 1, NULL, 0, 0); - - _vlcTabToneLevelIdxHi1.table = &qdm2_table[qdm2_vlc_offs[7]]; - _vlcTabToneLevelIdxHi1.table_allocated = qdm2_vlc_offs[8] - qdm2_vlc_offs[7]; - _vlcTabToneLevelIdxHi1.table_size = 0; - initVlcSparse(&_vlcTabToneLevelIdxHi1, 8, 20, - vlc_tab_tone_level_idx_hi1_huffbits, 1, 1, - vlc_tab_tone_level_idx_hi1_huffcodes, 2, 2, NULL, 0, 0); - - _vlcTabToneLevelIdxMid.table = &qdm2_table[qdm2_vlc_offs[8]]; - _vlcTabToneLevelIdxMid.table_allocated = qdm2_vlc_offs[9] - qdm2_vlc_offs[8]; - _vlcTabToneLevelIdxMid.table_size = 0; - initVlcSparse(&_vlcTabToneLevelIdxMid, 8, 24, - vlc_tab_tone_level_idx_mid_huffbits, 1, 1, - vlc_tab_tone_level_idx_mid_huffcodes, 2, 2, NULL, 0, 0); - - _vlcTabToneLevelIdxHi2.table = &qdm2_table[qdm2_vlc_offs[9]]; - _vlcTabToneLevelIdxHi2.table_allocated = qdm2_vlc_offs[10] - qdm2_vlc_offs[9]; - _vlcTabToneLevelIdxHi2.table_size = 0; - initVlcSparse(&_vlcTabToneLevelIdxHi2, 8, 24, - vlc_tab_tone_level_idx_hi2_huffbits, 1, 1, - vlc_tab_tone_level_idx_hi2_huffcodes, 2, 2, NULL, 0, 0); - - _vlcTabType30.table = &qdm2_table[qdm2_vlc_offs[10]]; - _vlcTabType30.table_allocated = qdm2_vlc_offs[11] - qdm2_vlc_offs[10]; - _vlcTabType30.table_size = 0; - initVlcSparse(&_vlcTabType30, 6, 9, - vlc_tab_type30_huffbits, 1, 1, - vlc_tab_type30_huffcodes, 1, 1, NULL, 0, 0); - - _vlcTabType34.table = &qdm2_table[qdm2_vlc_offs[11]]; - _vlcTabType34.table_allocated = qdm2_vlc_offs[12] - qdm2_vlc_offs[11]; - _vlcTabType34.table_size = 0; - initVlcSparse(&_vlcTabType34, 5, 10, - vlc_tab_type34_huffbits, 1, 1, - vlc_tab_type34_huffcodes, 1, 1, NULL, 0, 0); - - _vlcTabFftToneOffset[0].table = &qdm2_table[qdm2_vlc_offs[12]]; - _vlcTabFftToneOffset[0].table_allocated = qdm2_vlc_offs[13] - qdm2_vlc_offs[12]; - _vlcTabFftToneOffset[0].table_size = 0; - initVlcSparse(&_vlcTabFftToneOffset[0], 8, 23, - vlc_tab_fft_tone_offset_0_huffbits, 1, 1, - vlc_tab_fft_tone_offset_0_huffcodes, 2, 2, NULL, 0, 0); - - _vlcTabFftToneOffset[1].table = &qdm2_table[qdm2_vlc_offs[13]]; - _vlcTabFftToneOffset[1].table_allocated = qdm2_vlc_offs[14] - qdm2_vlc_offs[13]; - _vlcTabFftToneOffset[1].table_size = 0; - initVlcSparse(&_vlcTabFftToneOffset[1], 8, 28, - vlc_tab_fft_tone_offset_1_huffbits, 1, 1, - vlc_tab_fft_tone_offset_1_huffcodes, 2, 2, NULL, 0, 0); - - _vlcTabFftToneOffset[2].table = &qdm2_table[qdm2_vlc_offs[14]]; - _vlcTabFftToneOffset[2].table_allocated = qdm2_vlc_offs[15] - qdm2_vlc_offs[14]; - _vlcTabFftToneOffset[2].table_size = 0; - initVlcSparse(&_vlcTabFftToneOffset[2], 8, 32, - vlc_tab_fft_tone_offset_2_huffbits, 1, 1, - vlc_tab_fft_tone_offset_2_huffcodes, 2, 2, NULL, 0, 0); - - _vlcTabFftToneOffset[3].table = &qdm2_table[qdm2_vlc_offs[15]]; - _vlcTabFftToneOffset[3].table_allocated = qdm2_vlc_offs[16] - qdm2_vlc_offs[15]; - _vlcTabFftToneOffset[3].table_size = 0; - initVlcSparse(&_vlcTabFftToneOffset[3], 8, 35, - vlc_tab_fft_tone_offset_3_huffbits, 1, 1, - vlc_tab_fft_tone_offset_3_huffcodes, 2, 2, NULL, 0, 0); - - _vlcTabFftToneOffset[4].table = &qdm2_table[qdm2_vlc_offs[16]]; - _vlcTabFftToneOffset[4].table_allocated = qdm2_vlc_offs[17] - qdm2_vlc_offs[16]; - _vlcTabFftToneOffset[4].table_size = 0; - initVlcSparse(&_vlcTabFftToneOffset[4], 8, 38, - vlc_tab_fft_tone_offset_4_huffbits, 1, 1, - vlc_tab_fft_tone_offset_4_huffcodes, 2, 2, NULL, 0, 0); - - _vlcsInitialized = true; - } -} - -QDM2Stream::QDM2Stream(Common::SeekableReadStream *stream, Common::SeekableReadStream *extraData) { - uint32 tmp; - int32 tmp_s; - int tmp_val; - int i; - - debug(1, "QDM2Stream::QDM2Stream() Call"); - - _stream = stream; - _compressedData = NULL; - _subPacket = 0; - memset(_quantizedCoeffs, 0, sizeof(_quantizedCoeffs)); - memset(_fftLevelExp, 0, sizeof(_fftLevelExp)); - _noiseIdx = 0; - memset(_fftCoefsMinIndex, 0, sizeof(_fftCoefsMinIndex)); - memset(_fftCoefsMaxIndex, 0, sizeof(_fftCoefsMaxIndex)); - _fftToneStart = 0; - _fftToneEnd = 0; - for(i = 0; i < ARRAYSIZE(_subPacketListA); i++) { - _subPacketListA[i].packet = NULL; - _subPacketListA[i].next = NULL; - } - _subPacketsB = 0; - for(i = 0; i < ARRAYSIZE(_subPacketListB); i++) { - _subPacketListB[i].packet = NULL; - _subPacketListB[i].next = NULL; - } - for(i = 0; i < ARRAYSIZE(_subPacketListC); i++) { - _subPacketListC[i].packet = NULL; - _subPacketListC[i].next = NULL; - } - for(i = 0; i < ARRAYSIZE(_subPacketListD); i++) { - _subPacketListD[i].packet = NULL; - _subPacketListD[i].next = NULL; - } - memset(_synthBuf, 0, sizeof(_synthBuf)); - memset(_synthBufOffset, 0, sizeof(_synthBufOffset)); - memset(_sbSamples, 0, sizeof(_sbSamples)); - memset(_outputBuffer, 0, sizeof(_outputBuffer)); - _vlcsInitialized = false; - _superblocktype_2_3 = 0; - _hasErrors = false; - - // Rewind extraData stream from any previous calls... - extraData->seek(0, SEEK_SET); - - tmp_s = extraData->readSint32BE(); - debug(1, "QDM2Stream::QDM2Stream() extraSize: %d", tmp_s); - if ((extraData->size() - extraData->pos()) / 4 + 1 != tmp_s) - warning("QDM2Stream::QDM2Stream() extraSize mismatch - Expected %d", (extraData->size() - extraData->pos()) / 4 + 1); - if (tmp_s < 12) - error("QDM2Stream::QDM2Stream() Insufficient extraData"); - - tmp = extraData->readUint32BE(); - debug(1, "QDM2Stream::QDM2Stream() extraTag: %d", tmp); - if (tmp != MKID_BE('frma')) - warning("QDM2Stream::QDM2Stream() extraTag mismatch"); - - tmp = extraData->readUint32BE(); - debug(1, "QDM2Stream::QDM2Stream() extraType: %d", tmp); - if (tmp == MKID_BE('QDMC')) - warning("QDM2Stream::QDM2Stream() QDMC stream type not supported."); - else if (tmp != MKID_BE('QDM2')) - error("QDM2Stream::QDM2Stream() Unsupported stream type"); - - tmp_s = extraData->readSint32BE(); - debug(1, "QDM2Stream::QDM2Stream() extraSize2: %d", tmp_s); - if ((extraData->size() - extraData->pos()) + 4 != tmp_s) - warning("QDM2Stream::QDM2Stream() extraSize2 mismatch - Expected %d", (extraData->size() - extraData->pos()) + 4); - - tmp = extraData->readUint32BE(); - debug(1, "QDM2Stream::QDM2Stream() extraTag2: %d", tmp); - if (tmp != MKID_BE('QDCA')) - warning("QDM2Stream::QDM2Stream() extraTag2 mismatch"); - - if (extraData->readUint32BE() != 1) - warning("QDM2Stream::QDM2Stream() u0 field not 1"); - - _channels = extraData->readUint32BE(); - debug(1, "QDM2Stream::QDM2Stream() channels: %d", _channels); - - _sampleRate = extraData->readUint32BE(); - debug(1, "QDM2Stream::QDM2Stream() sampleRate: %d", _sampleRate); - - _bitRate = extraData->readUint32BE(); - debug(1, "QDM2Stream::QDM2Stream() bitRate: %d", _bitRate); - - _blockSize = extraData->readUint32BE(); - debug(1, "QDM2Stream::QDM2Stream() blockSize: %d", _blockSize); - - _frameSize = extraData->readUint32BE(); - debug(1, "QDM2Stream::QDM2Stream() frameSize: %d", _frameSize); - - _packetSize = extraData->readUint32BE(); - debug(1, "QDM2Stream::QDM2Stream() packetSize: %d", _packetSize); - - if (extraData->size() - extraData->pos() != 0) { - tmp_s = extraData->readSint32BE(); - debug(1, "QDM2Stream::QDM2Stream() extraSize3: %d", tmp_s); - if (extraData->size() + 4 != tmp_s) - warning("QDM2Stream::QDM2Stream() extraSize3 mismatch - Expected %d", extraData->size() + 4); - - tmp = extraData->readUint32BE(); - debug(1, "QDM2Stream::QDM2Stream() extraTag3: %d", tmp); - if (tmp != MKID_BE('QDCP')) - warning("QDM2Stream::QDM2Stream() extraTag3 mismatch"); - - if ((float)extraData->readUint32BE() != 1.0) - warning("QDM2Stream::QDM2Stream() uf0 field not 1.0"); - - if (extraData->readUint32BE() != 0) - warning("QDM2Stream::QDM2Stream() u1 field not 0"); - - if ((float)extraData->readUint32BE() != 1.0) - warning("QDM2Stream::QDM2Stream() uf1 field not 1.0"); - - if ((float)extraData->readUint32BE() != 1.0) - warning("QDM2Stream::QDM2Stream() uf2 field not 1.0"); - - if (extraData->readUint32BE() != 27) - warning("QDM2Stream::QDM2Stream() u2 field not 27"); - - if (extraData->readUint32BE() != 8) - warning("QDM2Stream::QDM2Stream() u3 field not 8"); - - if (extraData->readUint32BE() != 0) - warning("QDM2Stream::QDM2Stream() u4 field not 0"); - } - - _fftOrder = scummvm_log2(_frameSize) + 1; - _fftFrameSize = 2 * _frameSize; // complex has two floats - - // something like max decodable tones - _groupOrder = scummvm_log2(_blockSize) + 1; - _sFrameSize = _blockSize / 16; // 16 iterations per super block - - _subSampling = _fftOrder - 7; - _frequencyRange = 255 / (1 << (2 - _subSampling)); - - switch ((_subSampling * 2 + _channels - 1)) { - case 0: - tmp = 40; - break; - case 1: - tmp = 48; - break; - case 2: - tmp = 56; - break; - case 3: - tmp = 72; - break; - case 4: - tmp = 80; - break; - case 5: - tmp = 100; - break; - default: - tmp = _subSampling; - break; - } - - tmp_val = 0; - if ((tmp * 1000) < _bitRate) tmp_val = 1; - if ((tmp * 1440) < _bitRate) tmp_val = 2; - if ((tmp * 1760) < _bitRate) tmp_val = 3; - if ((tmp * 2240) < _bitRate) tmp_val = 4; - _cmTableSelect = tmp_val; - - if (_subSampling == 0) - tmp = 7999; - else - tmp = ((-(_subSampling -1)) & 8000) + 20000; - - if (tmp < 8000) - _coeffPerSbSelect = 0; - else if (tmp <= 16000) - _coeffPerSbSelect = 1; - else - _coeffPerSbSelect = 2; - - if (_fftOrder < 7 || _fftOrder > 9) - error("QDM2Stream::QDM2Stream() Unsupported fft_order: %d", _fftOrder); - - rdftInit(&_rdftCtx, _fftOrder, IRDFT); - - initVlc(); - ff_mpa_synth_init(ff_mpa_synth_window); - softclipTableInit(); - rndTableInit(); - initNoiseSamples(); - - _compressedData = new uint8[_packetSize]; -} - -QDM2Stream::~QDM2Stream() { - delete[] _compressedData; - delete _stream; -} - -static int qdm2_get_vlc(GetBitContext *gb, VLC *vlc, int flag, int depth) { - int value = getVlc2(gb, vlc->table, vlc->bits, depth); - - // stage-2, 3 bits exponent escape sequence - if (value-- == 0) - value = getBits(gb, getBits (gb, 3) + 1); - - // stage-3, optional - if (flag) { - int tmp = vlc_stage3_values[value]; - - if ((value & ~3) > 0) - tmp += getBits(gb, (value >> 2)); - value = tmp; - } - - return value; -} - -static int qdm2_get_se_vlc(VLC *vlc, GetBitContext *gb, int depth) -{ - int value = qdm2_get_vlc(gb, vlc, 0, depth); - - return (value & 1) ? ((value + 1) >> 1) : -(value >> 1); -} - -/** - * QDM2 checksum - * - * @param data pointer to data to be checksum'ed - * @param length data length - * @param value checksum value - * - * @return 0 if checksum is OK - */ -static uint16 qdm2_packet_checksum(const uint8 *data, int length, int value) { - int i; - - for (i = 0; i < length; i++) - value -= data[i]; - - return (uint16)(value & 0xffff); -} - -/** - * Fills a QDM2SubPacket structure with packet type, size, and data pointer. - * - * @param gb bitreader context - * @param sub_packet packet under analysis - */ -static void qdm2_decode_sub_packet_header(GetBitContext *gb, QDM2SubPacket *sub_packet) -{ - sub_packet->type = getBits (gb, 8); - - if (sub_packet->type == 0) { - sub_packet->size = 0; - sub_packet->data = NULL; - } else { - sub_packet->size = getBits (gb, 8); - - if (sub_packet->type & 0x80) { - sub_packet->size <<= 8; - sub_packet->size |= getBits (gb, 8); - sub_packet->type &= 0x7f; - } - - if (sub_packet->type == 0x7f) - sub_packet->type |= (getBits (gb, 8) << 8); - - sub_packet->data = &gb->buffer[getBitsCount(gb) / 8]; // FIXME: this depends on bitreader internal data - } - - debug(1, "QDM2 Subpacket: type=%d size=%d start_offs=%x", sub_packet->type, sub_packet->size, getBitsCount(gb) / 8); -} - -/** - * Return node pointer to first packet of requested type in list. - * - * @param list list of subpackets to be scanned - * @param type type of searched subpacket - * @return node pointer for subpacket if found, else NULL - */ -static QDM2SubPNode* qdm2_search_subpacket_type_in_list(QDM2SubPNode *list, int type) -{ - while (list != NULL && list->packet != NULL) { - if (list->packet->type == type) - return list; - list = list->next; - } - return NULL; -} - -/** - * Replaces 8 elements with their average value. - * Called by qdm2_decode_superblock before starting subblock decoding. - */ -void QDM2Stream::average_quantized_coeffs(void) { - int i, j, n, ch, sum; - - n = coeff_per_sb_for_avg[_coeffPerSbSelect][QDM2_SB_USED(_subSampling) - 1] + 1; - - for (ch = 0; ch < _channels; ch++) { - for (i = 0; i < n; i++) { - sum = 0; - - for (j = 0; j < 8; j++) - sum += _quantizedCoeffs[ch][i][j]; - - sum /= 8; - if (sum > 0) - sum--; - - for (j = 0; j < 8; j++) - _quantizedCoeffs[ch][i][j] = sum; - } - } -} - -/** - * Build subband samples with noise weighted by q->tone_level. - * Called by synthfilt_build_sb_samples. - * - * @param sb subband index - */ -void QDM2Stream::build_sb_samples_from_noise(int sb) { - int ch, j; - - FIX_NOISE_IDX(_noiseIdx); - - if (!_channels) - return; - - for (ch = 0; ch < _channels; ch++) { - for (j = 0; j < 64; j++) { - _sbSamples[ch][j * 2][sb] = (int32)(SB_DITHERING_NOISE(sb, _noiseIdx) * _toneLevel[ch][sb][j] + .5); - _sbSamples[ch][j * 2 + 1][sb] = (int32)(SB_DITHERING_NOISE(sb, _noiseIdx) * _toneLevel[ch][sb][j] + .5); - } - } -} - -/** - * Called while processing data from subpackets 11 and 12. - * Used after making changes to coding_method array. - * - * @param sb subband index - * @param channels number of channels - * @param coding_method q->coding_method[0][0][0] - */ -void QDM2Stream::fix_coding_method_array(int sb, int channels, sb_int8_array coding_method) -{ - int j, k; - int ch; - int run, case_val; - int switchtable[23] = {0,5,1,5,5,5,5,5,2,5,5,5,5,5,5,5,3,5,5,5,5,5,4}; - - for (ch = 0; ch < channels; ch++) { - for (j = 0; j < 64; ) { - if((coding_method[ch][sb][j] - 8) > 22) { - run = 1; - case_val = 8; - } else { - switch (switchtable[coding_method[ch][sb][j]-8]) { - case 0: run = 10; case_val = 10; break; - case 1: run = 1; case_val = 16; break; - case 2: run = 5; case_val = 24; break; - case 3: run = 3; case_val = 30; break; - case 4: run = 1; case_val = 30; break; - case 5: run = 1; case_val = 8; break; - default: run = 1; case_val = 8; break; - } - } - for (k = 0; k < run; k++) - if (j + k < 128) - if (coding_method[ch][sb + (j + k) / 64][(j + k) % 64] > coding_method[ch][sb][j]) - if (k > 0) { - warning("QDM2 Untested Code: not debugged, almost never used"); - memset(&coding_method[ch][sb][j + k], case_val, k * sizeof(int8)); - memset(&coding_method[ch][sb][j + k], case_val, 3 * sizeof(int8)); - } - j += run; - } - } -} - -/** - * Related to synthesis filter - * Called by process_subpacket_10 - * - * @param flag 1 if called after getting data from subpacket 10, 0 if no subpacket 10 - */ -void QDM2Stream::fill_tone_level_array(int flag) { - int i, sb, ch, sb_used; - int tmp, tab; - - // This should never happen - if (_channels <= 0) - return; - - for (ch = 0; ch < _channels; ch++) { - for (sb = 0; sb < 30; sb++) { - for (i = 0; i < 8; i++) { - if ((tab=coeff_per_sb_for_dequant[_coeffPerSbSelect][sb]) < (last_coeff[_coeffPerSbSelect] - 1)) - tmp = _quantizedCoeffs[ch][tab + 1][i] * dequant_table[_coeffPerSbSelect][tab + 1][sb]+ - _quantizedCoeffs[ch][tab][i] * dequant_table[_coeffPerSbSelect][tab][sb]; - else - tmp = _quantizedCoeffs[ch][tab][i] * dequant_table[_coeffPerSbSelect][tab][sb]; - if(tmp < 0) - tmp += 0xff; - _toneLevelIdxBase[ch][sb][i] = (tmp / 256) & 0xff; - } - } - } - - sb_used = QDM2_SB_USED(_subSampling); - - if ((_superblocktype_2_3 != 0) && !flag) { - for (sb = 0; sb < sb_used; sb++) { - for (ch = 0; ch < _channels; ch++) { - for (i = 0; i < 64; i++) { - _toneLevelIdx[ch][sb][i] = _toneLevelIdxBase[ch][sb][i / 8]; - if (_toneLevelIdx[ch][sb][i] < 0) - _toneLevel[ch][sb][i] = 0; - else - _toneLevel[ch][sb][i] = fft_tone_level_table[0][_toneLevelIdx[ch][sb][i] & 0x3f]; - } - } - } - } else { - tab = _superblocktype_2_3 ? 0 : 1; - for (sb = 0; sb < sb_used; sb++) { - if ((sb >= 4) && (sb <= 23)) { - for (ch = 0; ch < _channels; ch++) { - for (i = 0; i < 64; i++) { - tmp = _toneLevelIdxBase[ch][sb][i / 8] - - _toneLevelIdxHi1[ch][sb / 8][i / 8][i % 8] - - _toneLevelIdxMid[ch][sb - 4][i / 8] - - _toneLevelIdxHi2[ch][sb - 4]; - _toneLevelIdx[ch][sb][i] = tmp & 0xff; - if ((tmp < 0) || (!_superblocktype_2_3 && !tmp)) - _toneLevel[ch][sb][i] = 0; - else - _toneLevel[ch][sb][i] = fft_tone_level_table[tab][tmp & 0x3f]; - } - } - } else { - if (sb > 4) { - for (ch = 0; ch < _channels; ch++) { - for (i = 0; i < 64; i++) { - tmp = _toneLevelIdxBase[ch][sb][i / 8] - - _toneLevelIdxHi1[ch][2][i / 8][i % 8] - - _toneLevelIdxHi2[ch][sb - 4]; - _toneLevelIdx[ch][sb][i] = tmp & 0xff; - if ((tmp < 0) || (!_superblocktype_2_3 && !tmp)) - _toneLevel[ch][sb][i] = 0; - else - _toneLevel[ch][sb][i] = fft_tone_level_table[tab][tmp & 0x3f]; - } - } - } else { - for (ch = 0; ch < _channels; ch++) { - for (i = 0; i < 64; i++) { - tmp = _toneLevelIdx[ch][sb][i] = _toneLevelIdxBase[ch][sb][i / 8]; - if ((tmp < 0) || (!_superblocktype_2_3 && !tmp)) - _toneLevel[ch][sb][i] = 0; - else - _toneLevel[ch][sb][i] = fft_tone_level_table[tab][tmp & 0x3f]; - } - } - } - } - } - } -} - -/** - * Related to synthesis filter - * Called by process_subpacket_11 - * c is built with data from subpacket 11 - * Most of this function is used only if superblock_type_2_3 == 0, never seen it in samples - * - * @param tone_level_idx - * @param tone_level_idx_temp - * @param coding_method q->coding_method[0][0][0] - * @param nb_channels number of channels - * @param c coming from subpacket 11, passed as 8*c - * @param superblocktype_2_3 flag based on superblock packet type - * @param cm_table_select q->cm_table_select - */ -void QDM2Stream::fill_coding_method_array(sb_int8_array tone_level_idx, sb_int8_array tone_level_idx_temp, - sb_int8_array coding_method, int nb_channels, - int c, int superblocktype_2_3, int cm_table_select) { - int ch, sb, j; - int tmp, acc, esp_40, comp; - int add1, add2, add3, add4; - // TODO : Remove multres 64 bit variable necessity... - int64_t multres; - - // This should never happen - if (nb_channels <= 0) - return; - if (!superblocktype_2_3) { - warning("QDM2 This case is untested, no samples available"); - for (ch = 0; ch < nb_channels; ch++) - for (sb = 0; sb < 30; sb++) { - for (j = 1; j < 63; j++) { // The loop only iterates to 63 so the code doesn't overflow the buffer - add1 = tone_level_idx[ch][sb][j] - 10; - if (add1 < 0) - add1 = 0; - add2 = add3 = add4 = 0; - if (sb > 1) { - add2 = tone_level_idx[ch][sb - 2][j] + tone_level_idx_offset_table[sb][0] - 6; - if (add2 < 0) - add2 = 0; - } - if (sb > 0) { - add3 = tone_level_idx[ch][sb - 1][j] + tone_level_idx_offset_table[sb][1] - 6; - if (add3 < 0) - add3 = 0; - } - if (sb < 29) { - add4 = tone_level_idx[ch][sb + 1][j] + tone_level_idx_offset_table[sb][3] - 6; - if (add4 < 0) - add4 = 0; - } - tmp = tone_level_idx[ch][sb][j + 1] * 2 - add4 - add3 - add2 - add1; - if (tmp < 0) - tmp = 0; - tone_level_idx_temp[ch][sb][j + 1] = tmp & 0xff; - } - tone_level_idx_temp[ch][sb][0] = tone_level_idx_temp[ch][sb][1]; - } - acc = 0; - for (ch = 0; ch < nb_channels; ch++) - for (sb = 0; sb < 30; sb++) - for (j = 0; j < 64; j++) - acc += tone_level_idx_temp[ch][sb][j]; - - multres = 0x66666667 * (acc * 10); - esp_40 = (multres >> 32) / 8 + ((multres & 0xffffffff) >> 31); - for (ch = 0; ch < nb_channels; ch++) - for (sb = 0; sb < 30; sb++) - for (j = 0; j < 64; j++) { - comp = tone_level_idx_temp[ch][sb][j]* esp_40 * 10; - if (comp < 0) - comp += 0xff; - comp /= 256; // signed shift - switch(sb) { - case 0: - if (comp < 30) - comp = 30; - comp += 15; - break; - case 1: - if (comp < 24) - comp = 24; - comp += 10; - break; - case 2: - case 3: - case 4: - if (comp < 16) - comp = 16; - } - if (comp <= 5) - tmp = 0; - else if (comp <= 10) - tmp = 10; - else if (comp <= 16) - tmp = 16; - else if (comp <= 24) - tmp = -1; - else - tmp = 0; - coding_method[ch][sb][j] = ((tmp & 0xfffa) + 30 )& 0xff; - } - for (sb = 0; sb < 30; sb++) - fix_coding_method_array(sb, nb_channels, coding_method); - for (ch = 0; ch < nb_channels; ch++) - for (sb = 0; sb < 30; sb++) - for (j = 0; j < 64; j++) - if (sb >= 10) { - if (coding_method[ch][sb][j] < 10) - coding_method[ch][sb][j] = 10; - } else { - if (sb >= 2) { - if (coding_method[ch][sb][j] < 16) - coding_method[ch][sb][j] = 16; - } else { - if (coding_method[ch][sb][j] < 30) - coding_method[ch][sb][j] = 30; - } - } - } else { // superblocktype_2_3 != 0 - for (ch = 0; ch < nb_channels; ch++) - for (sb = 0; sb < 30; sb++) - for (j = 0; j < 64; j++) - coding_method[ch][sb][j] = coding_method_table[cm_table_select][sb]; - } -} - -/** - * - * Called by process_subpacket_11 to process more data from subpacket 11 with sb 0-8 - * Called by process_subpacket_12 to process data from subpacket 12 with sb 8-sb_used - * - * @param gb bitreader context - * @param length packet length in bits - * @param sb_min lower subband processed (sb_min included) - * @param sb_max higher subband processed (sb_max excluded) - */ -void QDM2Stream::synthfilt_build_sb_samples(GetBitContext *gb, int length, int sb_min, int sb_max) { - int sb, j, k, n, ch, run, channels; - int joined_stereo, zero_encoding, chs; - int type34_first; - float type34_div = 0; - float type34_predictor; - float samples[10], sign_bits[16]; - - if (length == 0) { - // If no data use noise - for (sb = sb_min; sb < sb_max; sb++) - build_sb_samples_from_noise(sb); - - return; - } - - for (sb = sb_min; sb < sb_max; sb++) { - FIX_NOISE_IDX(_noiseIdx); - - channels = _channels; - - if (_channels <= 1 || sb < 12) - joined_stereo = 0; - else if (sb >= 24) - joined_stereo = 1; - else - joined_stereo = (BITS_LEFT(length,gb) >= 1) ? getBits1 (gb) : 0; - - if (joined_stereo) { - if (BITS_LEFT(length,gb) >= 16) - for (j = 0; j < 16; j++) - sign_bits[j] = getBits1(gb); - - for (j = 0; j < 64; j++) - if (_codingMethod[1][sb][j] > _codingMethod[0][sb][j]) - _codingMethod[0][sb][j] = _codingMethod[1][sb][j]; - - fix_coding_method_array(sb, _channels, _codingMethod); - channels = 1; - } - - for (ch = 0; ch < channels; ch++) { - zero_encoding = (BITS_LEFT(length,gb) >= 1) ? getBits1(gb) : 0; - type34_predictor = 0.0; - type34_first = 1; - - for (j = 0; j < 128; ) { - switch (_codingMethod[ch][sb][j / 2]) { - case 8: - if (BITS_LEFT(length,gb) >= 10) { - if (zero_encoding) { - for (k = 0; k < 5; k++) { - if ((j + 2 * k) >= 128) - break; - samples[2 * k] = getBits1(gb) ? dequant_1bit[joined_stereo][2 * getBits1(gb)] : 0; - } - } else { - n = getBits(gb, 8); - for (k = 0; k < 5; k++) - samples[2 * k] = dequant_1bit[joined_stereo][_randomDequantIndex[n][k]]; - } - for (k = 0; k < 5; k++) - samples[2 * k + 1] = SB_DITHERING_NOISE(sb, _noiseIdx); - } else { - for (k = 0; k < 10; k++) - samples[k] = SB_DITHERING_NOISE(sb, _noiseIdx); - } - run = 10; - break; - - case 10: - if (BITS_LEFT(length,gb) >= 1) { - double f = 0.81; - - if (getBits1(gb)) - f = -f; - f -= _noiseSamples[((sb + 1) * (j +5 * ch + 1)) & 127] * 9.0 / 40.0; - samples[0] = f; - } else { - samples[0] = SB_DITHERING_NOISE(sb, _noiseIdx); - } - run = 1; - break; - - case 16: - if (BITS_LEFT(length,gb) >= 10) { - if (zero_encoding) { - for (k = 0; k < 5; k++) { - if ((j + k) >= 128) - break; - samples[k] = (getBits1(gb) == 0) ? 0 : dequant_1bit[joined_stereo][2 * getBits1(gb)]; - } - } else { - n = getBits (gb, 8); - for (k = 0; k < 5; k++) - samples[k] = dequant_1bit[joined_stereo][_randomDequantIndex[n][k]]; - } - } else { - for (k = 0; k < 5; k++) - samples[k] = SB_DITHERING_NOISE(sb, _noiseIdx); - } - run = 5; - break; - - case 24: - if (BITS_LEFT(length,gb) >= 7) { - n = getBits(gb, 7); - for (k = 0; k < 3; k++) - samples[k] = (_randomDequantType24[n][k] - 2.0) * 0.5; - } else { - for (k = 0; k < 3; k++) - samples[k] = SB_DITHERING_NOISE(sb, _noiseIdx); - } - run = 3; - break; - - case 30: - if (BITS_LEFT(length,gb) >= 4) - samples[0] = type30_dequant[qdm2_get_vlc(gb, &_vlcTabType30, 0, 1)]; - else - samples[0] = SB_DITHERING_NOISE(sb, _noiseIdx); - - run = 1; - break; - - case 34: - if (BITS_LEFT(length,gb) >= 7) { - if (type34_first) { - type34_div = (float)(1 << getBits(gb, 2)); - samples[0] = ((float)getBits(gb, 5) - 16.0) / 15.0; - type34_predictor = samples[0]; - type34_first = 0; - } else { - samples[0] = type34_delta[qdm2_get_vlc(gb, &_vlcTabType34, 0, 1)] / type34_div + type34_predictor; - type34_predictor = samples[0]; - } - } else { - samples[0] = SB_DITHERING_NOISE(sb, _noiseIdx); - } - run = 1; - break; - - default: - samples[0] = SB_DITHERING_NOISE(sb, _noiseIdx); - run = 1; - break; - } - - if (joined_stereo) { - float tmp[10][MPA_MAX_CHANNELS]; - - for (k = 0; k < run; k++) { - tmp[k][0] = samples[k]; - tmp[k][1] = (sign_bits[(j + k) / 8]) ? -samples[k] : samples[k]; - } - for (chs = 0; chs < _channels; chs++) - for (k = 0; k < run; k++) - if ((j + k) < 128) - _sbSamples[chs][j + k][sb] = (int32)(_toneLevel[chs][sb][((j + k)/2)] * tmp[k][chs] + .5); - } else { - for (k = 0; k < run; k++) - if ((j + k) < 128) - _sbSamples[ch][j + k][sb] = (int32)(_toneLevel[ch][sb][(j + k)/2] * samples[k] + .5); - } - - j += run; - } // j loop - } // channel loop - } // subband loop -} - -/** - * Init the first element of a channel in quantized_coeffs with data from packet 10 (quantized_coeffs[ch][0]). - * This is similar to process_subpacket_9, but for a single channel and for element [0] - * same VLC tables as process_subpacket_9 are used. - * - * @param quantized_coeffs pointer to quantized_coeffs[ch][0] - * @param gb bitreader context - * @param length packet length in bits - */ -void QDM2Stream::init_quantized_coeffs_elem0(int8 *quantized_coeffs, GetBitContext *gb, int length) { - int i, k, run, level, diff; - - if (BITS_LEFT(length,gb) < 16) - return; - level = qdm2_get_vlc(gb, &_vlcTabLevel, 0, 2); - - quantized_coeffs[0] = level; - - for (i = 0; i < 7; ) { - if (BITS_LEFT(length,gb) < 16) - break; - run = qdm2_get_vlc(gb, &_vlcTabRun, 0, 1) + 1; - - if (BITS_LEFT(length,gb) < 16) - break; - diff = qdm2_get_se_vlc(&_vlcTabDiff, gb, 2); - - for (k = 1; k <= run; k++) - quantized_coeffs[i + k] = (level + ((k * diff) / run)); - - level += diff; - i += run; - } -} - -/** - * Related to synthesis filter, process data from packet 10 - * Init part of quantized_coeffs via function init_quantized_coeffs_elem0 - * Init tone_level_idx_hi1, tone_level_idx_hi2, tone_level_idx_mid with data from packet 10 - * - * @param gb bitreader context - * @param length packet length in bits - */ -void QDM2Stream::init_tone_level_dequantization(GetBitContext *gb, int length) { - int sb, j, k, n, ch; - - for (ch = 0; ch < _channels; ch++) { - init_quantized_coeffs_elem0(_quantizedCoeffs[ch][0], gb, length); - - if (BITS_LEFT(length,gb) < 16) { - memset(_quantizedCoeffs[ch][0], 0, 8); - break; - } - } - - n = _subSampling + 1; - - for (sb = 0; sb < n; sb++) - for (ch = 0; ch < _channels; ch++) - for (j = 0; j < 8; j++) { - if (BITS_LEFT(length,gb) < 1) - break; - if (getBits1(gb)) { - for (k=0; k < 8; k++) { - if (BITS_LEFT(length,gb) < 16) - break; - _toneLevelIdxHi1[ch][sb][j][k] = qdm2_get_vlc(gb, &_vlcTabToneLevelIdxHi1, 0, 2); - } - } else { - for (k=0; k < 8; k++) - _toneLevelIdxHi1[ch][sb][j][k] = 0; - } - } - - n = QDM2_SB_USED(_subSampling) - 4; - - for (sb = 0; sb < n; sb++) - for (ch = 0; ch < _channels; ch++) { - if (BITS_LEFT(length,gb) < 16) - break; - _toneLevelIdxHi2[ch][sb] = qdm2_get_vlc(gb, &_vlcTabToneLevelIdxHi2, 0, 2); - if (sb > 19) - _toneLevelIdxHi2[ch][sb] -= 16; - else - for (j = 0; j < 8; j++) - _toneLevelIdxMid[ch][sb][j] = -16; - } - - n = QDM2_SB_USED(_subSampling) - 5; - - for (sb = 0; sb < n; sb++) { - for (ch = 0; ch < _channels; ch++) { - for (j = 0; j < 8; j++) { - if (BITS_LEFT(length,gb) < 16) - break; - _toneLevelIdxMid[ch][sb][j] = qdm2_get_vlc(gb, &_vlcTabToneLevelIdxMid, 0, 2) - 32; - } - } - } -} - -/** - * Process subpacket 9, init quantized_coeffs with data from it - * - * @param node pointer to node with packet - */ -void QDM2Stream::process_subpacket_9(QDM2SubPNode *node) { - GetBitContext gb; - int i, j, k, n, ch, run, level, diff; - - initGetBits(&gb, node->packet->data, node->packet->size*8); - - n = coeff_per_sb_for_avg[_coeffPerSbSelect][QDM2_SB_USED(_subSampling) - 1] + 1; // same as averagesomething function - - for (i = 1; i < n; i++) - for (ch = 0; ch < _channels; ch++) { - level = qdm2_get_vlc(&gb, &_vlcTabLevel, 0, 2); - _quantizedCoeffs[ch][i][0] = level; - - for (j = 0; j < (8 - 1); ) { - run = qdm2_get_vlc(&gb, &_vlcTabRun, 0, 1) + 1; - diff = qdm2_get_se_vlc(&_vlcTabDiff, &gb, 2); - - for (k = 1; k <= run; k++) - _quantizedCoeffs[ch][i][j + k] = (level + ((k*diff) / run)); - - level += diff; - j += run; - } - } - - for (ch = 0; ch < _channels; ch++) - for (i = 0; i < 8; i++) - _quantizedCoeffs[ch][0][i] = 0; -} - -/** - * Process subpacket 10 if not null, else - * - * @param node pointer to node with packet - * @param length packet length in bits - */ -void QDM2Stream::process_subpacket_10(QDM2SubPNode *node, int length) { - GetBitContext gb; - - initGetBits(&gb, ((node == NULL) ? _emptyBuffer : node->packet->data), ((node == NULL) ? 0 : node->packet->size*8)); - - if (length != 0) { - init_tone_level_dequantization(&gb, length); - fill_tone_level_array(1); - } else { - fill_tone_level_array(0); - } -} - -/** - * Process subpacket 11 - * - * @param node pointer to node with packet - * @param length packet length in bit - */ -void QDM2Stream::process_subpacket_11(QDM2SubPNode *node, int length) { - GetBitContext gb; - - initGetBits(&gb, ((node == NULL) ? _emptyBuffer : node->packet->data), ((node == NULL) ? 0 : node->packet->size*8)); - if (length >= 32) { - int c = getBits (&gb, 13); - - if (c > 3) - fill_coding_method_array(_toneLevelIdx, _toneLevelIdxTemp, _codingMethod, - _channels, 8*c, _superblocktype_2_3, _cmTableSelect); - } - - synthfilt_build_sb_samples(&gb, length, 0, 8); -} - -/** - * Process subpacket 12 - * - * @param node pointer to node with packet - * @param length packet length in bits - */ -void QDM2Stream::process_subpacket_12(QDM2SubPNode *node, int length) { - GetBitContext gb; - - initGetBits(&gb, ((node == NULL) ? _emptyBuffer : node->packet->data), ((node == NULL) ? 0 : node->packet->size*8)); - synthfilt_build_sb_samples(&gb, length, 8, QDM2_SB_USED(_subSampling)); -} - -/* - * Process new subpackets for synthesis filter - * - * @param list list with synthesis filter packets (list D) - */ -void QDM2Stream::process_synthesis_subpackets(QDM2SubPNode *list) { - struct QDM2SubPNode *nodes[4]; - - nodes[0] = qdm2_search_subpacket_type_in_list(list, 9); - if (nodes[0] != NULL) - process_subpacket_9(nodes[0]); - - nodes[1] = qdm2_search_subpacket_type_in_list(list, 10); - if (nodes[1] != NULL) - process_subpacket_10(nodes[1], nodes[1]->packet->size << 3); - else - process_subpacket_10(NULL, 0); - - nodes[2] = qdm2_search_subpacket_type_in_list(list, 11); - if (nodes[0] != NULL && nodes[1] != NULL && nodes[2] != NULL) - process_subpacket_11(nodes[2], (nodes[2]->packet->size << 3)); - else - process_subpacket_11(NULL, 0); - - nodes[3] = qdm2_search_subpacket_type_in_list(list, 12); - if (nodes[0] != NULL && nodes[1] != NULL && nodes[3] != NULL) - process_subpacket_12(nodes[3], (nodes[3]->packet->size << 3)); - else - process_subpacket_12(NULL, 0); -} - -/* - * Decode superblock, fill packet lists. - * - */ -void QDM2Stream::qdm2_decode_super_block(void) { - GetBitContext gb; - struct QDM2SubPacket header, *packet; - int i, packet_bytes, sub_packet_size, subPacketsD; - unsigned int next_index = 0; - - memset(_toneLevelIdxHi1, 0, sizeof(_toneLevelIdxHi1)); - memset(_toneLevelIdxMid, 0, sizeof(_toneLevelIdxMid)); - memset(_toneLevelIdxHi2, 0, sizeof(_toneLevelIdxHi2)); - - _subPacketsB = 0; - subPacketsD = 0; - - average_quantized_coeffs(); // average elements in quantized_coeffs[max_ch][10][8] - - initGetBits(&gb, _compressedData, _packetSize*8); - qdm2_decode_sub_packet_header(&gb, &header); - - if (header.type < 2 || header.type >= 8) { - _hasErrors = true; - error("QDM2 : bad superblock type"); - return; - } - - _superblocktype_2_3 = (header.type == 2 || header.type == 3); - packet_bytes = (_packetSize - getBitsCount(&gb) / 8); - - initGetBits(&gb, header.data, header.size*8); - - if (header.type == 2 || header.type == 4 || header.type == 5) { - int csum = 257 * getBits(&gb, 8) + 2 * getBits(&gb, 8); - - csum = qdm2_packet_checksum(_compressedData, _packetSize, csum); - - if (csum != 0) { - _hasErrors = true; - error("QDM2 : bad packet checksum"); - return; - } - } - - _subPacketListB[0].packet = NULL; - _subPacketListD[0].packet = NULL; - - for (i = 0; i < 6; i++) - if (--_fftLevelExp[i] < 0) - _fftLevelExp[i] = 0; - - for (i = 0; packet_bytes > 0; i++) { - int j; - - _subPacketListA[i].next = NULL; - - if (i > 0) { - _subPacketListA[i - 1].next = &_subPacketListA[i]; - - // seek to next block - initGetBits(&gb, header.data, header.size*8); - skipBits(&gb, next_index*8); - - if (next_index >= header.size) - break; - } - - // decode subpacket - packet = &_subPackets[i]; - qdm2_decode_sub_packet_header(&gb, packet); - next_index = packet->size + getBitsCount(&gb) / 8; - sub_packet_size = ((packet->size > 0xff) ? 1 : 0) + packet->size + 2; - - if (packet->type == 0) - break; - - if (sub_packet_size > packet_bytes) { - if (packet->type != 10 && packet->type != 11 && packet->type != 12) - break; - packet->size += packet_bytes - sub_packet_size; - } - - packet_bytes -= sub_packet_size; - - // add subpacket to 'all subpackets' list - _subPacketListA[i].packet = packet; - - // add subpacket to related list - if (packet->type == 8) { - error("Unsupported packet type 8"); - return; - } else if (packet->type >= 9 && packet->type <= 12) { - // packets for MPEG Audio like Synthesis Filter - QDM2_LIST_ADD(_subPacketListD, subPacketsD, packet); - } else if (packet->type == 13) { - for (j = 0; j < 6; j++) - _fftLevelExp[j] = getBits(&gb, 6); - } else if (packet->type == 14) { - for (j = 0; j < 6; j++) - _fftLevelExp[j] = qdm2_get_vlc(&gb, &_fftLevelExpVlc, 0, 2); - } else if (packet->type == 15) { - error("Unsupported packet type 15"); - return; - } else if (packet->type >= 16 && packet->type < 48 && !fft_subpackets[packet->type - 16]) { - // packets for FFT - QDM2_LIST_ADD(_subPacketListB, _subPacketsB, packet); - } - } // Packet bytes loop - -// **************************************************************** - if (_subPacketListD[0].packet != NULL) { - process_synthesis_subpackets(_subPacketListD); - _doSynthFilter = 1; - } else if (_doSynthFilter) { - process_subpacket_10(NULL, 0); - process_subpacket_11(NULL, 0); - process_subpacket_12(NULL, 0); - } -// **************************************************************** -} - -void QDM2Stream::qdm2_fft_init_coefficient(int sub_packet, int offset, int duration, - int channel, int exp, int phase) { - if (_fftCoefsMinIndex[duration] < 0) - _fftCoefsMinIndex[duration] = _fftCoefsIndex; - - _fftCoefs[_fftCoefsIndex].sub_packet = ((sub_packet >= 16) ? (sub_packet - 16) : sub_packet); - _fftCoefs[_fftCoefsIndex].channel = channel; - _fftCoefs[_fftCoefsIndex].offset = offset; - _fftCoefs[_fftCoefsIndex].exp = exp; - _fftCoefs[_fftCoefsIndex].phase = phase; - _fftCoefsIndex++; -} - -void QDM2Stream::qdm2_fft_decode_tones(int duration, GetBitContext *gb, int b) { - debug(1, "QDM2Stream::qdm2_fft_decode_tones() duration: %d b:%d", duration, b); - int channel, stereo, phase, exp; - int local_int_4, local_int_8, stereo_phase, local_int_10; - int local_int_14, stereo_exp, local_int_20, local_int_28; - int n, offset; - - local_int_4 = 0; - local_int_28 = 0; - local_int_20 = 2; - local_int_8 = (4 - duration); - local_int_10 = 1 << (_groupOrder - duration - 1); - offset = 1; - - while (1) { - if (_superblocktype_2_3) { - debug(1, "QDM2Stream::qdm2_fft_decode_tones() local_int_8: %d", local_int_8); - while ((n = qdm2_get_vlc(gb, &_vlcTabFftToneOffset[local_int_8], 1, 2)) < 2) { - debug(1, "QDM2Stream::qdm2_fft_decode_tones() local_int_8: %d", local_int_8); - offset = 1; - if (n == 0) { - local_int_4 += local_int_10; - local_int_28 += (1 << local_int_8); - } else { - local_int_4 += 8*local_int_10; - local_int_28 += (8 << local_int_8); - } - } - offset += (n - 2); - } else { - offset += qdm2_get_vlc(gb, &_vlcTabFftToneOffset[local_int_8], 1, 2); - while (offset >= (local_int_10 - 1)) { - offset += (1 - (local_int_10 - 1)); - local_int_4 += local_int_10; - local_int_28 += (1 << local_int_8); - } - } - - if (local_int_4 >= _blockSize) - return; - - local_int_14 = (offset >> local_int_8); - - if (_channels > 1) { - channel = getBits1(gb); - stereo = getBits1(gb); - } else { - channel = 0; - stereo = 0; - } - - exp = qdm2_get_vlc(gb, (b ? &_fftLevelExpVlc : &_fftLevelExpAltVlc), 0, 2); - exp += _fftLevelExp[fft_level_index_table[local_int_14]]; - exp = (exp < 0) ? 0 : exp; - - phase = getBits(gb, 3); - stereo_exp = 0; - stereo_phase = 0; - - if (stereo) { - stereo_exp = (exp - qdm2_get_vlc(gb, &_fftStereoExpVlc, 0, 1)); - stereo_phase = (phase - qdm2_get_vlc(gb, &_fftStereoPhaseVlc, 0, 1)); - if (stereo_phase < 0) - stereo_phase += 8; - } - - if (_frequencyRange > (local_int_14 + 1)) { - int sub_packet = (local_int_20 + local_int_28); - - qdm2_fft_init_coefficient(sub_packet, offset, duration, channel, exp, phase); - if (stereo) - qdm2_fft_init_coefficient(sub_packet, offset, duration, (1 - channel), stereo_exp, stereo_phase); - } - - offset++; - } -} - -void QDM2Stream::qdm2_decode_fft_packets(void) { - debug(1, "QDM2Stream::qdm2_decode_fft_packets()"); - int i, j, min, max, value, type, unknown_flag; - GetBitContext gb; - - if (_subPacketListB[0].packet == NULL) - return; - - // reset minimum indexes for FFT coefficients - _fftCoefsIndex = 0; - for (i=0; i < 5; i++) - _fftCoefsMinIndex[i] = -1; - - // process subpackets ordered by type, largest type first - for (i = 0, max = 256; i < _subPacketsB; i++) { - QDM2SubPacket *packet= NULL; - - // find subpacket with largest type less than max - for (j = 0, min = 0; j < _subPacketsB; j++) { - value = _subPacketListB[j].packet->type; - if (value > min && value < max) { - min = value; - packet = _subPacketListB[j].packet; - } - } - - max = min; - - // check for errors (?) - if (!packet) - return; - - if (i == 0 && (packet->type < 16 || packet->type >= 48 || fft_subpackets[packet->type - 16])) - return; - - // decode FFT tones - debug(1, "QDM2Stream::qdm2_decode_fft_packets initGetBits() packet->size*8: %d", packet->size*8); - initGetBits(&gb, packet->data, packet->size*8); - - if (packet->type >= 32 && packet->type < 48 && !fft_subpackets[packet->type - 16]) - unknown_flag = 1; - else - unknown_flag = 0; - - type = packet->type; - - if ((type >= 17 && type < 24) || (type >= 33 && type < 40)) { - int duration = _subSampling + 5 - (type & 15); - - if (duration >= 0 && duration < 4) { // TODO: Should be <= 4? - debug(1, "QDM2Stream::qdm2_decode_fft_packets qdm2_fft_decode_tones() #1"); - qdm2_fft_decode_tones(duration, &gb, unknown_flag); - } - } else if (type == 31) { - for (j=0; j < 4; j++) { - debug(1, "QDM2Stream::qdm2_decode_fft_packets qdm2_fft_decode_tones() #2"); - qdm2_fft_decode_tones(j, &gb, unknown_flag); - } - } else if (type == 46) { - for (j=0; j < 6; j++) - _fftLevelExp[j] = getBits(&gb, 6); - for (j=0; j < 4; j++) { - debug(1, "QDM2Stream::qdm2_decode_fft_packets qdm2_fft_decode_tones() #3"); - qdm2_fft_decode_tones(j, &gb, unknown_flag); - } - } - } // Loop on B packets - - // calculate maximum indexes for FFT coefficients - for (i = 0, j = -1; i < 5; i++) - if (_fftCoefsMinIndex[i] >= 0) { - if (j >= 0) - _fftCoefsMaxIndex[j] = _fftCoefsMinIndex[i]; - j = i; - } - if (j >= 0) - _fftCoefsMaxIndex[j] = _fftCoefsIndex; -} - -void QDM2Stream::qdm2_fft_generate_tone(FFTTone *tone) -{ - float level, f[6]; - int i; - QDM2Complex c; - const double iscale = 2.0 * PI / 512.0; - - tone->phase += tone->phase_shift; - - // calculate current level (maximum amplitude) of tone - level = fft_tone_envelope_table[tone->duration][tone->time_index] * tone->level; - c.im = level * sin(tone->phase*iscale); - c.re = level * cos(tone->phase*iscale); - - // generate FFT coefficients for tone - if (tone->duration >= 3 || tone->cutoff >= 3) { - tone->complex[0].im += c.im; - tone->complex[0].re += c.re; - tone->complex[1].im -= c.im; - tone->complex[1].re -= c.re; - } else { - f[1] = -tone->table[4]; - f[0] = tone->table[3] - tone->table[0]; - f[2] = 1.0 - tone->table[2] - tone->table[3]; - f[3] = tone->table[1] + tone->table[4] - 1.0; - f[4] = tone->table[0] - tone->table[1]; - f[5] = tone->table[2]; - for (i = 0; i < 2; i++) { - tone->complex[fft_cutoff_index_table[tone->cutoff][i]].re += c.re * f[i]; - tone->complex[fft_cutoff_index_table[tone->cutoff][i]].im += c.im *((tone->cutoff <= i) ? -f[i] : f[i]); - } - for (i = 0; i < 4; i++) { - tone->complex[i].re += c.re * f[i+2]; - tone->complex[i].im += c.im * f[i+2]; - } - } - - // copy the tone if it has not yet died out - if (++tone->time_index < ((1 << (5 - tone->duration)) - 1)) { - memcpy(&_fftTones[_fftToneEnd], tone, sizeof(FFTTone)); - _fftToneEnd = (_fftToneEnd + 1) % 1000; - } -} - -void QDM2Stream::qdm2_fft_tone_synthesizer(uint8 sub_packet) { - int i, j, ch; - const double iscale = 0.25 * PI; - - for (ch = 0; ch < _channels; ch++) { - memset(_fft.complex[ch], 0, _frameSize * sizeof(QDM2Complex)); - } - - // apply FFT tones with duration 4 (1 FFT period) - if (_fftCoefsMinIndex[4] >= 0) - for (i = _fftCoefsMinIndex[4]; i < _fftCoefsMaxIndex[4]; i++) { - float level; - QDM2Complex c; - - if (_fftCoefs[i].sub_packet != sub_packet) - break; - - ch = (_channels == 1) ? 0 : _fftCoefs[i].channel; - level = (_fftCoefs[i].exp < 0) ? 0.0 : fft_tone_level_table[_superblocktype_2_3 ? 0 : 1][_fftCoefs[i].exp & 63]; - - c.re = level * cos(_fftCoefs[i].phase * iscale); - c.im = level * sin(_fftCoefs[i].phase * iscale); - _fft.complex[ch][_fftCoefs[i].offset + 0].re += c.re; - _fft.complex[ch][_fftCoefs[i].offset + 0].im += c.im; - _fft.complex[ch][_fftCoefs[i].offset + 1].re -= c.re; - _fft.complex[ch][_fftCoefs[i].offset + 1].im -= c.im; - } - - // generate existing FFT tones - for (i = _fftToneEnd; i != _fftToneStart; ) { - qdm2_fft_generate_tone(&_fftTones[_fftToneStart]); - _fftToneStart = (_fftToneStart + 1) % 1000; - } - - // create and generate new FFT tones with duration 0 (long) to 3 (short) - for (i = 0; i < 4; i++) - if (_fftCoefsMinIndex[i] >= 0) { - for (j = _fftCoefsMinIndex[i]; j < _fftCoefsMaxIndex[i]; j++) { - int offset, four_i; - FFTTone tone; - - if (_fftCoefs[j].sub_packet != sub_packet) - break; - - four_i = (4 - i); - offset = _fftCoefs[j].offset >> four_i; - ch = (_channels == 1) ? 0 : _fftCoefs[j].channel; - - if (offset < _frequencyRange) { - if (offset < 2) - tone.cutoff = offset; - else - tone.cutoff = (offset >= 60) ? 3 : 2; - - tone.level = (_fftCoefs[j].exp < 0) ? 0.0 : fft_tone_level_table[_superblocktype_2_3 ? 0 : 1][_fftCoefs[j].exp & 63]; - tone.complex = &_fft.complex[ch][offset]; - tone.table = fft_tone_sample_table[i][_fftCoefs[j].offset - (offset << four_i)]; - tone.phase = 64 * _fftCoefs[j].phase - (offset << 8) - 128; - tone.phase_shift = (2 * _fftCoefs[j].offset + 1) << (7 - four_i); - tone.duration = i; - tone.time_index = 0; - - qdm2_fft_generate_tone(&tone); - } - } - _fftCoefsMinIndex[i] = j; - } -} - -void QDM2Stream::qdm2_calculate_fft(int channel) { - debug(1, "QDM2Stream::qdm2_calculate_fft channel: %d", channel); - const float gain = (_channels == 1 && _channels == 2) ? 0.5f : 1.0f; - int i; - - _fft.complex[channel][0].re *= 2.0f; - _fft.complex[channel][0].im = 0.0f; - - //debug(1, "QDM2Stream::qdm2_calculate_fft _fft.complex[channel][0].re: %lf", _fft.complex[channel][0].re); - //debug(1, "QDM2Stream::qdm2_calculate_fft _fft.complex[channel][0].im: %lf", _fft.complex[channel][0].im); - - rdftCalc(&_rdftCtx, (float *)_fft.complex[channel]); - - // add samples to output buffer - for (i = 0; i < ((_fftFrameSize + 15) & ~15); i++) - _outputBuffer[_channels * i + channel] += ((float *) _fft.complex[channel])[i] * gain; -} - -/** - * @param index subpacket number - */ -void QDM2Stream::qdm2_synthesis_filter(uint8 index) -{ - int16 samples[MPA_MAX_CHANNELS * MPA_FRAME_SIZE]; - int i, k, ch, sb_used, sub_sampling, dither_state = 0; - - // copy sb_samples - sb_used = QDM2_SB_USED(_subSampling); - - for (ch = 0; ch < _channels; ch++) - for (i = 0; i < 8; i++) - for (k = sb_used; k < 32; k++) - _sbSamples[ch][(8 * index) + i][k] = 0; - - for (ch = 0; ch < _channels; ch++) { - int16 *samples_ptr = samples + ch; - - for (i = 0; i < 8; i++) { - ff_mpa_synth_filter(_synthBuf[ch], &(_synthBufOffset[ch]), - ff_mpa_synth_window, &dither_state, - samples_ptr, _channels, - _sbSamples[ch][(8 * index) + i]); - samples_ptr += 32 * _channels; - } - } - - // add samples to output buffer - sub_sampling = (4 >> _subSampling); - - for (ch = 0; ch < _channels; ch++) - for (i = 0; i < _sFrameSize; i++) - _outputBuffer[_channels * i + ch] += (float)(samples[_channels * sub_sampling * i + ch] >> (sizeof(int16)*8-16)); -} - -int QDM2Stream::qdm2_decodeFrame(Common::SeekableReadStream *in) { - debug(1, "QDM2Stream::qdm2_decodeFrame in->pos(): %d in->size(): %d", in->pos(), in->size()); - int ch, i; - const int frame_size = (_sFrameSize * _channels); - - // select input buffer - if(in->eos() || in->size() == in->pos()) { - debug(1, "QDM2Stream::qdm2_decodeFrame End of Input Stream"); - return 0; - } - if((in->size() - in->pos()) < _packetSize) { - debug(1, "QDM2Stream::qdm2_decodeFrame Insufficient Packet Data in Input Stream Found: %d Need: %d", in->size() - in->pos(), _packetSize); - return 0; - } - - in->read(_compressedData, _packetSize); - debug(1, "QDM2Stream::qdm2_decodeFrame constructed input data"); - - // copy old block, clear new block of output samples - memmove(_outputBuffer, &_outputBuffer[frame_size], frame_size * sizeof(float)); - memset(&_outputBuffer[frame_size], 0, frame_size * sizeof(float)); - debug(1, "QDM2Stream::qdm2_decodeFrame cleared outputBuffer"); - - // decode block of QDM2 compressed data - debug(1, "QDM2Stream::qdm2_decodeFrame decode block of QDM2 compressed data"); - if (_subPacket == 0) { - _hasErrors = false; // reset it for a new super block - debug(1, "QDM2 : Superblock follows"); - qdm2_decode_super_block(); - } - - // parse subpackets - debug(1, "QDM2Stream::qdm2_decodeFrame parse subpackets"); - if (!_hasErrors) { - if (_subPacket == 2) { - debug(1, "QDM2Stream::qdm2_decodeFrame qdm2_decode_fft_packets()"); - qdm2_decode_fft_packets(); - } - - debug(1, "QDM2Stream::qdm2_decodeFrame qdm2_fft_tone_synthesizer(%d)", _subPacket); - qdm2_fft_tone_synthesizer(_subPacket); - } - - // sound synthesis stage 1 (FFT) - debug(1, "QDM2Stream::qdm2_decodeFrame sound synthesis stage 1 (FFT)"); - for (ch = 0; ch < _channels; ch++) { - qdm2_calculate_fft(ch); - - if (!_hasErrors && _subPacketListC[0].packet != NULL) { - error("QDM2 : has errors, and C list is not empty"); - return 0; - } - } - - // sound synthesis stage 2 (MPEG audio like synthesis filter) - debug(1, "QDM2Stream::qdm2_decodeFrame sound synthesis stage 2 (MPEG audio like synthesis filter)"); - if (!_hasErrors && _doSynthFilter) - qdm2_synthesis_filter(_subPacket); - - _subPacket = (_subPacket + 1) % 16; - - if(_hasErrors) - warning("QDM2 Packet error..."); - - // clip and convert output float[] to 16bit signed samples - debug(1, "QDM2Stream::qdm2_decodeFrame clip and convert output float[] to 16bit signed samples"); - -/* - debugN(1, "Input Data Packet:"); - for(i = 0; i < _packetSize; i++) { - debugN(1, " %d", _compressedData[i]); - } - debugN(1, " Output Data Packet:"); - for(i = 0; i < frame_size; i++) { - debugN(1, " %d", (int)_outputBuffer[i]); - } - debug(1, ""); -*/ - - for (i = 0; i < frame_size; i++) { - //debug(1, "QDM2Stream::qdm2_decodeFrame i: %d", i); - int value = (int)_outputBuffer[i]; - - if (value > SOFTCLIP_THRESHOLD) - value = (value > HARDCLIP_THRESHOLD) ? 32767 : _softclipTable[ value - SOFTCLIP_THRESHOLD]; - else if (value < -SOFTCLIP_THRESHOLD) - value = (value < -HARDCLIP_THRESHOLD) ? -32767 : -_softclipTable[-value - SOFTCLIP_THRESHOLD]; - - _outputSamples.push_back(value); - } - return frame_size; -} - -int QDM2Stream::readBuffer(int16 *buffer, const int numSamples) { - debug(1, "QDM2Stream::readBuffer numSamples: %d", numSamples); - int32 decodedSamples = _outputSamples.size(); - int32 i; - - //while((int)_outputSamples.size() < numSamples) { - while(!_stream->eos() && _stream->pos() != _stream->size()) { - i = qdm2_decodeFrame(_stream); - if(i == 0) - break; // Out Of Decode Frames... - decodedSamples += i; - } - if(decodedSamples > numSamples) - decodedSamples = numSamples; - - for(i = 0; i < decodedSamples; i++) - buffer[i] = _outputSamples.remove_at(0); - - return decodedSamples; -} - -} // End of namespace Mohawk diff --git a/engines/mohawk/video/qdm2.h b/engines/mohawk/video/qdm2.h deleted file mode 100644 index ffa5f77030..0000000000 --- a/engines/mohawk/video/qdm2.h +++ /dev/null @@ -1,289 +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. - * - * $URL$ - * $Id$ - * - */ - -#ifndef MOHAWK_VIDEO_QDM2_H -#define MOHAWK_VIDEO_QDM2_H - -#include "sound/audiostream.h" -#include "common/array.h" -#include "common/stream.h" - -namespace Mohawk { - -enum { - SOFTCLIP_THRESHOLD = 27600, - HARDCLIP_THRESHOLD = 35716, - MPA_MAX_CHANNELS = 2, - MPA_FRAME_SIZE = 1152, - FF_INPUT_BUFFER_PADDING_SIZE = 8 -}; - -typedef int8 sb_int8_array[2][30][64]; - -/* bit input */ -/* buffer, buffer_end and size_in_bits must be present and used by every reader */ -struct GetBitContext { - const uint8 *buffer, *bufferEnd; - int index; - int sizeInBits; -}; - -struct QDM2SubPacket { - int type; - unsigned int size; - const uint8 *data; // pointer to subpacket data (points to input data buffer, it's not a private copy) -}; - -struct QDM2SubPNode { - QDM2SubPacket *packet; - struct QDM2SubPNode *next; // pointer to next packet in the list, NULL if leaf node -}; - -struct QDM2Complex { - float re; - float im; -}; - -struct FFTTone { - float level; - QDM2Complex *complex; - const float *table; - int phase; - int phase_shift; - int duration; - short time_index; - short cutoff; -}; - -struct FFTCoefficient { - int16 sub_packet; - uint8 channel; - int16 offset; - int16 exp; - uint8 phase; -}; - -struct VLC { - int32 bits; - int16 (*table)[2]; // code, bits - int32 table_size; - int32 table_allocated; -}; - -#include "common/pack-start.h" -struct QDM2FFT { - QDM2Complex complex[MPA_MAX_CHANNELS][256]; -} PACKED_STRUCT; -#include "common/pack-end.h" - -enum RDFTransformType { - RDFT, - IRDFT, - RIDFT, - IRIDFT -}; - -struct FFTComplex { - float re, im; -}; - -struct FFTContext { - int nbits; - int inverse; - uint16 *revtab; - FFTComplex *exptab; - FFTComplex *tmpBuf; - int mdctSize; // size of MDCT (i.e. number of input data * 2) - int mdctBits; // n = 2^nbits - // pre/post rotation tables - float *tcos; - float *tsin; - void (*fftPermute)(struct FFTContext *s, FFTComplex *z); - void (*fftCalc)(struct FFTContext *s, FFTComplex *z); - void (*imdctCalc)(struct FFTContext *s, float *output, const float *input); - void (*imdctHalf)(struct FFTContext *s, float *output, const float *input); - void (*mdctCalc)(struct FFTContext *s, float *output, const float *input); - int splitRadix; - int permutation; -}; - -enum { - FF_MDCT_PERM_NONE = 0, - FF_MDCT_PERM_INTERLEAVE = 1 -}; - -struct RDFTContext { - int nbits; - int inverse; - int signConvention; - - // pre/post rotation tables - float *tcos; - float *tsin; - FFTContext fft; -}; - -class QDM2Stream : public Audio::AudioStream { -public: - QDM2Stream(Common::SeekableReadStream *stream, Common::SeekableReadStream *extraData); - ~QDM2Stream(); - - bool isStereo() const { return _channels == 2; } - bool endOfData() const { return ((_stream->pos() == _stream->size()) && (_outputSamples.size() == 0)); } - int getRate() const { return _sampleRate; } - int readBuffer(int16 *buffer, const int numSamples); - -private: - Common::SeekableReadStream *_stream; - - // Parameters from codec header, do not change during playback - uint8 _channels; - uint16 _sampleRate; - uint16 _bitRate; - uint16 _blockSize; // Group - uint16 _frameSize; // FFT - uint16 _packetSize; // Checksum - - // Parameters built from header parameters, do not change during playback - int _groupOrder; // order of frame group - int _fftOrder; // order of FFT (actually fft order+1) - int _fftFrameSize; // size of fft frame, in components (1 comples = re + im) - int _sFrameSize; // size of data frame - int _frequencyRange; - int _subSampling; // subsampling: 0=25%, 1=50%, 2=100% */ - int _coeffPerSbSelect; // selector for "num. of coeffs. per subband" tables. Can be 0, 1, 2 - int _cmTableSelect; // selector for "coding method" tables. Can be 0, 1 (from init: 0-4) - - // Packets and packet lists - QDM2SubPacket _subPackets[16]; // the packets themselves - QDM2SubPNode _subPacketListA[16]; // list of all packets - QDM2SubPNode _subPacketListB[16]; // FFT packets B are on list - int _subPacketsB; // number of packets on 'B' list - QDM2SubPNode _subPacketListC[16]; // packets with errors? - QDM2SubPNode _subPacketListD[16]; // DCT packets - - // FFT and tones - FFTTone _fftTones[1000]; - int _fftToneStart; - int _fftToneEnd; - FFTCoefficient _fftCoefs[1000]; - int _fftCoefsIndex; - int _fftCoefsMinIndex[5]; - int _fftCoefsMaxIndex[5]; - int _fftLevelExp[6]; - //RDFTContext _rdftCtx; - QDM2FFT _fft; - - // I/O data - uint8 *_compressedData; - float _outputBuffer[1024]; - Common::Array<int16> _outputSamples; - - // Synthesis filter - int16 ff_mpa_synth_window[512]; - int16 _synthBuf[MPA_MAX_CHANNELS][512*2]; - int _synthBufOffset[MPA_MAX_CHANNELS]; - int32 _sbSamples[MPA_MAX_CHANNELS][128][32]; - - // Mixed temporary data used in decoding - float _toneLevel[MPA_MAX_CHANNELS][30][64]; - int8 _codingMethod[MPA_MAX_CHANNELS][30][64]; - int8 _quantizedCoeffs[MPA_MAX_CHANNELS][10][8]; - int8 _toneLevelIdxBase[MPA_MAX_CHANNELS][30][8]; - int8 _toneLevelIdxHi1[MPA_MAX_CHANNELS][3][8][8]; - int8 _toneLevelIdxMid[MPA_MAX_CHANNELS][26][8]; - int8 _toneLevelIdxHi2[MPA_MAX_CHANNELS][26]; - int8 _toneLevelIdx[MPA_MAX_CHANNELS][30][64]; - int8 _toneLevelIdxTemp[MPA_MAX_CHANNELS][30][64]; - - // Flags - bool _hasErrors; // packet has errors - int _superblocktype_2_3; // select fft tables and some algorithm based on superblock type - int _doSynthFilter; // used to perform or skip synthesis filter - - uint8 _subPacket; // 0 to 15 - int _noiseIdx; // index for dithering noise table - - byte _emptyBuffer[FF_INPUT_BUFFER_PADDING_SIZE]; - - VLC _vlcTabLevel; - VLC _vlcTabDiff; - VLC _vlcTabRun; - VLC _fftLevelExpAltVlc; - VLC _fftLevelExpVlc; - VLC _fftStereoExpVlc; - VLC _fftStereoPhaseVlc; - VLC _vlcTabToneLevelIdxHi1; - VLC _vlcTabToneLevelIdxMid; - VLC _vlcTabToneLevelIdxHi2; - VLC _vlcTabType30; - VLC _vlcTabType34; - VLC _vlcTabFftToneOffset[5]; - bool _vlcsInitialized; - void initVlc(void); - - uint16 _softclipTable[HARDCLIP_THRESHOLD - SOFTCLIP_THRESHOLD + 1]; - void softclipTableInit(void); - - float _noiseTable[4096]; - byte _randomDequantIndex[256][5]; - byte _randomDequantType24[128][3]; - void rndTableInit(void); - - float _noiseSamples[128]; - void initNoiseSamples(void); - - RDFTContext _rdftCtx; - - void average_quantized_coeffs(void); - void build_sb_samples_from_noise(int sb); - void fix_coding_method_array(int sb, int channels, sb_int8_array coding_method); - void fill_tone_level_array(int flag); - void fill_coding_method_array(sb_int8_array tone_level_idx, sb_int8_array tone_level_idx_temp, - sb_int8_array coding_method, int nb_channels, - int c, int superblocktype_2_3, int cm_table_select); - void synthfilt_build_sb_samples(GetBitContext *gb, int length, int sb_min, int sb_max); - void init_quantized_coeffs_elem0(int8 *quantized_coeffs, GetBitContext *gb, int length); - void init_tone_level_dequantization(GetBitContext *gb, int length); - void process_subpacket_9(QDM2SubPNode *node); - void process_subpacket_10(QDM2SubPNode *node, int length); - void process_subpacket_11(QDM2SubPNode *node, int length); - void process_subpacket_12(QDM2SubPNode *node, int length); - void process_synthesis_subpackets(QDM2SubPNode *list); - void qdm2_decode_super_block(void); - void qdm2_fft_init_coefficient(int sub_packet, int offset, int duration, - int channel, int exp, int phase); - void qdm2_fft_decode_tones(int duration, GetBitContext *gb, int b); - void qdm2_decode_fft_packets(void); - void qdm2_fft_generate_tone(FFTTone *tone); - void qdm2_fft_tone_synthesizer(uint8 sub_packet); - void qdm2_calculate_fft(int channel); - void qdm2_synthesis_filter(uint8 index); - int qdm2_decodeFrame(Common::SeekableReadStream *in); -}; - -} // End of namespace Mohawk - -#endif diff --git a/engines/mohawk/video/qdm2data.h b/engines/mohawk/video/qdm2data.h deleted file mode 100644 index f1c18db41c..0000000000 --- a/engines/mohawk/video/qdm2data.h +++ /dev/null @@ -1,531 +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. - * - * $URL$ - * $Id$ - * - */ - -#ifndef MOHAWK_VIDEO_QDM2DATA_H -#define MOHAWK_VIDEO_QDM2DATA_H - -#include "common/scummsys.h" - -namespace Mohawk { - -/// VLC TABLES - -// values in this table range from -1..23; adjust retrieved value by -1 -static const uint16 vlc_tab_level_huffcodes[24] = { - 0x037c, 0x0004, 0x003c, 0x004c, 0x003a, 0x002c, 0x001c, 0x001a, - 0x0024, 0x0014, 0x0001, 0x0002, 0x0000, 0x0003, 0x0007, 0x0005, - 0x0006, 0x0008, 0x0009, 0x000a, 0x000c, 0x00fc, 0x007c, 0x017c -}; - -static const byte vlc_tab_level_huffbits[24] = { - 10, 6, 7, 7, 6, 6, 6, 6, 6, 5, 4, 4, 4, 3, 3, 3, 3, 4, 4, 5, 7, 8, 9, 10 -}; - -// values in this table range from -1..36; adjust retrieved value by -1 -static const uint16 vlc_tab_diff_huffcodes[37] = { - 0x1c57, 0x0004, 0x0000, 0x0001, 0x0003, 0x0002, 0x000f, 0x000e, - 0x0007, 0x0016, 0x0037, 0x0027, 0x0026, 0x0066, 0x0006, 0x0097, - 0x0046, 0x01c6, 0x0017, 0x0786, 0x0086, 0x0257, 0x00d7, 0x0357, - 0x00c6, 0x0386, 0x0186, 0x0000, 0x0157, 0x0c57, 0x0057, 0x0000, - 0x0b86, 0x0000, 0x1457, 0x0000, 0x0457 -}; - -static const byte vlc_tab_diff_huffbits[37] = { - 13, 3, 3, 2, 3, 3, 4, 4, 6, 5, 6, 6, 7, 7, 8, 8, - 8, 9, 8, 11, 9, 10, 8, 10, 9, 12, 10, 0, 10, 13, 11, 0, - 12, 0, 13, 0, 13 -}; - -// values in this table range from -1..5; adjust retrieved value by -1 -static const byte vlc_tab_run_huffcodes[6] = { - 0x1f, 0x00, 0x01, 0x03, 0x07, 0x0f -}; - -static const byte vlc_tab_run_huffbits[6] = { - 5, 1, 2, 3, 4, 5 -}; - -// values in this table range from -1..19; adjust retrieved value by -1 -static const uint16 vlc_tab_tone_level_idx_hi1_huffcodes[20] = { - 0x5714, 0x000c, 0x0002, 0x0001, 0x0000, 0x0004, 0x0034, 0x0054, - 0x0094, 0x0014, 0x0114, 0x0214, 0x0314, 0x0614, 0x0e14, 0x0f14, - 0x2714, 0x0714, 0x1714, 0x3714 -}; - -static const byte vlc_tab_tone_level_idx_hi1_huffbits[20] = { - 15, 4, 2, 1, 3, 5, 6, 7, 8, 10, 10, 11, 11, 12, 12, 12, 14, 14, 15, 14 -}; - -// values in this table range from -1..23; adjust retrieved value by -1 -static const uint16 vlc_tab_tone_level_idx_mid_huffcodes[24] = { - 0x0fea, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x03ea, 0x00ea, 0x002a, 0x001a, - 0x0006, 0x0001, 0x0000, 0x0002, 0x000a, 0x006a, 0x01ea, 0x07ea -}; - -static const byte vlc_tab_tone_level_idx_mid_huffbits[24] = { - 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 9, 7, 5, 3, 1, 2, 4, 6, 8, 10, 12 -}; - -// values in this table range from -1..23; adjust retrieved value by -1 -static const uint16 vlc_tab_tone_level_idx_hi2_huffcodes[24] = { - 0x0664, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0064, 0x00e4, - 0x00a4, 0x0068, 0x0004, 0x0008, 0x0014, 0x0018, 0x0000, 0x0001, - 0x0002, 0x0003, 0x000c, 0x0028, 0x0024, 0x0164, 0x0000, 0x0264 -}; - -static const byte vlc_tab_tone_level_idx_hi2_huffbits[24] = { - 11, 0, 0, 0, 0, 0, 10, 8, 8, 7, 6, 6, 5, 5, 4, 2, 2, 2, 4, 7, 8, 9, 0, 11 -}; - -// values in this table range from -1..8; adjust retrieved value by -1 -static const byte vlc_tab_type30_huffcodes[9] = { - 0x3c, 0x06, 0x00, 0x01, 0x03, 0x02, 0x04, 0x0c, 0x1c -}; - -static const byte vlc_tab_type30_huffbits[9] = { - 6, 3, 3, 2, 2, 3, 4, 5, 6 -}; - -// values in this table range from -1..9; adjust retrieved value by -1 -static const byte vlc_tab_type34_huffcodes[10] = { - 0x18, 0x00, 0x01, 0x04, 0x05, 0x07, 0x03, 0x02, 0x06, 0x08 -}; - -static const byte vlc_tab_type34_huffbits[10] = { - 5, 4, 3, 3, 3, 3, 3, 3, 3, 5 -}; - -// values in this table range from -1..22; adjust retrieved value by -1 -static const uint16 vlc_tab_fft_tone_offset_0_huffcodes[23] = { - 0x038e, 0x0001, 0x0000, 0x0022, 0x000a, 0x0006, 0x0012, 0x0002, - 0x001e, 0x003e, 0x0056, 0x0016, 0x000e, 0x0032, 0x0072, 0x0042, - 0x008e, 0x004e, 0x00f2, 0x002e, 0x0036, 0x00c2, 0x018e -}; - -static const byte vlc_tab_fft_tone_offset_0_huffbits[23] = { - 10, 1, 2, 6, 4, 5, 6, 7, 6, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 6, 6, 8, 10 -}; - -// values in this table range from -1..27; adjust retrieved value by -1 -static const uint16 vlc_tab_fft_tone_offset_1_huffcodes[28] = { - 0x07a4, 0x0001, 0x0020, 0x0012, 0x001c, 0x0008, 0x0006, 0x0010, - 0x0000, 0x0014, 0x0004, 0x0032, 0x0070, 0x000c, 0x0002, 0x003a, - 0x001a, 0x002c, 0x002a, 0x0022, 0x0024, 0x000a, 0x0064, 0x0030, - 0x0062, 0x00a4, 0x01a4, 0x03a4 -}; - -static const byte vlc_tab_fft_tone_offset_1_huffbits[28] = { - 11, 1, 6, 6, 5, 4, 3, 6, 6, 5, 6, 6, 7, 6, 6, 6, - 6, 6, 6, 7, 8, 6, 7, 7, 7, 9, 10, 11 -}; - -// values in this table range from -1..31; adjust retrieved value by -1 -static const uint16 vlc_tab_fft_tone_offset_2_huffcodes[32] = { - 0x1760, 0x0001, 0x0000, 0x0082, 0x000c, 0x0006, 0x0003, 0x0007, - 0x0008, 0x0004, 0x0010, 0x0012, 0x0022, 0x001a, 0x0000, 0x0020, - 0x000a, 0x0040, 0x004a, 0x006a, 0x002a, 0x0042, 0x0002, 0x0060, - 0x00aa, 0x00e0, 0x00c2, 0x01c2, 0x0160, 0x0360, 0x0760, 0x0f60 -}; - -static const byte vlc_tab_fft_tone_offset_2_huffbits[32] = { - 13, 2, 0, 8, 4, 3, 3, 3, 4, 4, 5, 5, 6, 5, 7, 7, - 7, 7, 7, 7, 8, 8, 8, 9, 8, 8, 9, 9, 10, 11, 13, 12 -}; - -// values in this table range from -1..34; adjust retrieved value by -1 -static const uint16 vlc_tab_fft_tone_offset_3_huffcodes[35] = { - 0x33ea, 0x0005, 0x0000, 0x000c, 0x0000, 0x0006, 0x0003, 0x0008, - 0x0002, 0x0001, 0x0004, 0x0007, 0x001a, 0x000f, 0x001c, 0x002c, - 0x000a, 0x001d, 0x002d, 0x002a, 0x000d, 0x004c, 0x008c, 0x006a, - 0x00cd, 0x004d, 0x00ea, 0x020c, 0x030c, 0x010c, 0x01ea, 0x07ea, - 0x0bea, 0x03ea, 0x13ea -}; - -static const byte vlc_tab_fft_tone_offset_3_huffbits[35] = { - 14, 4, 0, 10, 4, 3, 3, 4, 4, 3, 4, 4, 5, 4, 5, 6, - 6, 5, 6, 7, 7, 7, 8, 8, 8, 8, 9, 10, 10, 10, 10, 11, - 12, 13, 14 -}; - -// values in this table range from -1..37; adjust retrieved value by -1 -static const uint16 vlc_tab_fft_tone_offset_4_huffcodes[38] = { - 0x5282, 0x0016, 0x0000, 0x0136, 0x0004, 0x0000, 0x0007, 0x000a, - 0x000e, 0x0003, 0x0001, 0x000d, 0x0006, 0x0009, 0x0012, 0x0005, - 0x0025, 0x0022, 0x0015, 0x0002, 0x0076, 0x0035, 0x0042, 0x00c2, - 0x0182, 0x00b6, 0x0036, 0x03c2, 0x0482, 0x01c2, 0x0682, 0x0882, - 0x0a82, 0x0082, 0x0282, 0x1282, 0x3282, 0x2282 -}; - -static const byte vlc_tab_fft_tone_offset_4_huffbits[38] = { - 15, 6, 0, 9, 3, 3, 3, 4, 4, 3, 4, 4, 5, 4, 5, 6, - 6, 6, 6, 8, 7, 6, 8, 9, 9, 8, 9, 10, 11, 10, 11, 12, - 12, 12, 14, 15, 14, 14 -}; - -/// FFT TABLES - -// values in this table range from -1..27; adjust retrieved value by -1 -static const uint16 fft_level_exp_alt_huffcodes[28] = { - 0x1ec6, 0x0006, 0x00c2, 0x0142, 0x0242, 0x0246, 0x00c6, 0x0046, - 0x0042, 0x0146, 0x00a2, 0x0062, 0x0026, 0x0016, 0x000e, 0x0005, - 0x0004, 0x0003, 0x0000, 0x0001, 0x000a, 0x0012, 0x0002, 0x0022, - 0x01c6, 0x02c6, 0x06c6, 0x0ec6 -}; - -static const byte fft_level_exp_alt_huffbits[28] = { - 13, 7, 8, 9, 10, 10, 10, 10, 10, 9, 8, 7, 6, 5, 4, 3, - 3, 2, 3, 3, 4, 5, 7, 8, 9, 11, 12, 13 -}; - -// values in this table range from -1..19; adjust retrieved value by -1 -static const uint16 fft_level_exp_huffcodes[20] = { - 0x0f24, 0x0001, 0x0002, 0x0000, 0x0006, 0x0005, 0x0007, 0x000c, - 0x000b, 0x0014, 0x0013, 0x0004, 0x0003, 0x0023, 0x0064, 0x00a4, - 0x0024, 0x0124, 0x0324, 0x0724 -}; - -static const byte fft_level_exp_huffbits[20] = { - 12, 3, 3, 3, 3, 3, 3, 4, 4, 5, 5, 6, 6, 6, 7, 8, 9, 10, 11, 12 -}; - -// values in this table range from -1..6; adjust retrieved value by -1 -static const byte fft_stereo_exp_huffcodes[7] = { - 0x3e, 0x01, 0x00, 0x02, 0x06, 0x0e, 0x1e -}; - -static const byte fft_stereo_exp_huffbits[7] = { - 6, 1, 2, 3, 4, 5, 6 -}; - -// values in this table range from -1..8; adjust retrieved value by -1 -static const byte fft_stereo_phase_huffcodes[9] = { - 0x35, 0x02, 0x00, 0x01, 0x0d, 0x15, 0x05, 0x09, 0x03 -}; - -static const byte fft_stereo_phase_huffbits[9] = { - 6, 2, 2, 4, 4, 6, 5, 4, 2 -}; - -static const int fft_cutoff_index_table[4][2] = { - { 1, 2 }, {-1, 0 }, {-1,-2 }, { 0, 0 } -}; - -static const int16 fft_level_index_table[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, -}; - -static const byte last_coeff[3] = { - 4, 7, 10 -}; - -static const byte coeff_per_sb_for_avg[3][30] = { - { 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, - { 0, 1, 2, 2, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, - { 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9 } -}; - -static const uint32 dequant_table[3][10][30] = { - { { 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 256, 256, 205, 154, 102, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 51, 102, 154, 205, 256, 238, 219, 201, 183, 165, 146, 128, 110, 91, 73, 55, 37, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 18, 37, 55, 73, 91, 110, 128, 146, 165, 183, 201, 219, 238, 256, 228, 199, 171, 142, 114, 85, 57, 28 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, - { { 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 256, 171, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 85, 171, 256, 171, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 85, 171, 256, 219, 183, 146, 110, 73, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 73, 110, 146, 183, 219, 256, 228, 199, 171, 142, 114, 85, 57, 28, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 57, 85, 114, 142, 171, 199, 228, 256, 213, 171, 128, 85, 43 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, - { { 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 256, 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 256, 171, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 85, 171, 256, 192, 128, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 128, 192, 256, 205, 154, 102, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 102, 154, 205, 256, 213, 171, 128, 85, 43, 0, 0, 0, 0, 0, 0 }, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 85, 128, 171, 213, 256, 213, 171, 128, 85, 43 } } -}; - -static const byte coeff_per_sb_for_dequant[3][30] = { - { 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, - { 0, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6 }, - { 0, 1, 2, 3, 4, 4, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9 } -}; - -// first index is subband, 2nd index is 0, 1 or 3 (2 is unused) -static const int8 tone_level_idx_offset_table[30][4] = { - { -50, -50, 0, -50 }, - { -50, -50, 0, -50 }, - { -50, -9, 0, -19 }, - { -16, -6, 0, -12 }, - { -11, -4, 0, -8 }, - { -8, -3, 0, -6 }, - { -7, -3, 0, -5 }, - { -6, -2, 0, -4 }, - { -5, -2, 0, -3 }, - { -4, -1, 0, -3 }, - { -4, -1, 0, -2 }, - { -3, -1, 0, -2 }, - { -3, -1, 0, -2 }, - { -3, -1, 0, -2 }, - { -2, -1, 0, -1 }, - { -2, -1, 0, -1 }, - { -2, -1, 0, -1 }, - { -2, 0, 0, -1 }, - { -2, 0, 0, -1 }, - { -1, 0, 0, -1 }, - { -1, 0, 0, -1 }, - { -1, 0, 0, -1 }, - { -1, 0, 0, -1 }, - { -1, 0, 0, -1 }, - { -1, 0, 0, -1 }, - { -1, 0, 0, -1 }, - { -1, 0, 0, 0 }, - { -1, 0, 0, 0 }, - { -1, 0, 0, 0 }, - { -1, 0, 0, 0 } -}; - -/* all my samples have 1st index 0 or 1 */ -/* second index is subband, only indexes 0-29 seem to be used */ -static const int8 coding_method_table[5][30] = { - { 34, 30, 24, 24, 16, 16, 16, 16, 10, 10, 10, 10, 10, 10, 10, - 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 - }, - { 34, 30, 24, 24, 16, 16, 16, 16, 10, 10, 10, 10, 10, 10, 10, - 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 - }, - { 34, 30, 30, 30, 24, 24, 16, 16, 16, 16, 16, 16, 10, 10, 10, - 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 - }, - { 34, 34, 30, 30, 24, 24, 24, 24, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 10, 10, 10, 10, 10, 10, 10, 10 - }, - { 34, 34, 30, 30, 30, 30, 30, 30, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16 - }, -}; - -static const int vlc_stage3_values[60] = { - 0, 1, 2, 3, 4, 6, 8, 10, 12, 16, 20, 24, - 28, 36, 44, 52, 60, 76, 92, 108, 124, 156, 188, 220, - 252, 316, 380, 444, 508, 636, 764, 892, 1020, 1276, 1532, 1788, - 2044, 2556, 3068, 3580, 4092, 5116, 6140, 7164, 8188, 10236, 12284, 14332, - 16380, 20476, 24572, 28668, 32764, 40956, 49148, 57340, 65532, 81916, 98300,114684 -}; - -static const float fft_tone_sample_table[4][16][5] = { - { { .0100000000f,-.0037037037f,-.0020000000f,-.0069444444f,-.0018416207f }, - { .0416666667f, .0000000000f, .0000000000f,-.0208333333f,-.0123456791f }, - { .1250000000f, .0558035709f, .0330687836f,-.0164473690f,-.0097465888f }, - { .1562500000f, .0625000000f, .0370370370f,-.0062500000f,-.0037037037f }, - { .1996007860f, .0781250000f, .0462962948f, .0022727272f, .0013468013f }, - { .2000000000f, .0625000000f, .0370370373f, .0208333333f, .0074074073f }, - { .2127659619f, .0555555556f, .0329218097f, .0208333333f, .0123456791f }, - { .2173913121f, .0473484844f, .0280583613f, .0347222239f, .0205761325f }, - { .2173913121f, .0347222239f, .0205761325f, .0473484844f, .0280583613f }, - { .2127659619f, .0208333333f, .0123456791f, .0555555556f, .0329218097f }, - { .2000000000f, .0208333333f, .0074074073f, .0625000000f, .0370370370f }, - { .1996007860f, .0022727272f, .0013468013f, .0781250000f, .0462962948f }, - { .1562500000f,-.0062500000f,-.0037037037f, .0625000000f, .0370370370f }, - { .1250000000f,-.0164473690f,-.0097465888f, .0558035709f, .0330687836f }, - { .0416666667f,-.0208333333f,-.0123456791f, .0000000000f, .0000000000f }, - { .0100000000f,-.0069444444f,-.0018416207f,-.0037037037f,-.0020000000f } }, - - { { .0050000000f,-.0200000000f, .0125000000f,-.3030303030f, .0020000000f }, - { .1041666642f, .0400000000f,-.0250000000f, .0333333333f,-.0200000000f }, - { .1250000000f, .0100000000f, .0142857144f,-.0500000007f,-.0200000000f }, - { .1562500000f,-.0006250000f,-.00049382716f,-.000625000f,-.00049382716f }, - { .1562500000f,-.0006250000f,-.00049382716f,-.000625000f,-.00049382716f }, - { .1250000000f,-.0500000000f,-.0200000000f, .0100000000f, .0142857144f }, - { .1041666667f, .0333333333f,-.0200000000f, .0400000000f,-.0250000000f }, - { .0050000000f,-.3030303030f, .0020000001f,-.0200000000f, .0125000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f } }, - - { { .1428571492f, .1250000000f,-.0285714287f,-.0357142873f, .0208333333f }, - { .1818181818f, .0588235296f, .0333333333f, .0212765951f, .0100000000f }, - { .1818181818f, .0212765951f, .0100000000f, .0588235296f, .0333333333f }, - { .1428571492f,-.0357142873f, .0208333333f, .1250000000f,-.0285714287f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f } }, - - { { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, - { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f } } -}; - -static const float fft_tone_level_table[2][64] = { { -// pow ~ (i > 46) ? 0 : (((((i & 1) ? 431 : 304) << (i >> 1))) / 1024.0); - 0.17677669f, 0.42677650f, 0.60355347f, 0.85355347f, - 1.20710683f, 1.68359375f, 2.37500000f, 3.36718750f, - 4.75000000f, 6.73437500f, 9.50000000f, 13.4687500f, - 19.0000000f, 26.9375000f, 38.0000000f, 53.8750000f, - 76.0000000f, 107.750000f, 152.000000f, 215.500000f, - 304.000000f, 431.000000f, 608.000000f, 862.000000f, - 1216.00000f, 1724.00000f, 2432.00000f, 3448.00000f, - 4864.00000f, 6896.00000f, 9728.00000f, 13792.0000f, - 19456.0000f, 27584.0000f, 38912.0000f, 55168.0000f, - 77824.0000f, 110336.000f, 155648.000f, 220672.000f, - 311296.000f, 441344.000f, 622592.000f, 882688.000f, - 1245184.00f, 1765376.00f, 2490368.00f, 0.00000000f, - 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, - 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, - 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, - 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, - }, { -// pow = (i > 45) ? 0 : ((((i & 1) ? 431 : 304) << (i >> 1)) / 512.0); - 0.59375000f, 0.84179688f, 1.18750000f, 1.68359375f, - 2.37500000f, 3.36718750f, 4.75000000f, 6.73437500f, - 9.50000000f, 13.4687500f, 19.0000000f, 26.9375000f, - 38.0000000f, 53.8750000f, 76.0000000f, 107.750000f, - 152.000000f, 215.500000f, 304.000000f, 431.000000f, - 608.000000f, 862.000000f, 1216.00000f, 1724.00000f, - 2432.00000f, 3448.00000f, 4864.00000f, 6896.00000f, - 9728.00000f, 13792.0000f, 19456.0000f, 27584.0000f, - 38912.0000f, 55168.0000f, 77824.0000f, 110336.000f, - 155648.000f, 220672.000f, 311296.000f, 441344.000f, - 622592.000f, 882688.000f, 1245184.00f, 1765376.00f, - 2490368.00f, 3530752.00f, 0.00000000f, 0.00000000f, - 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, - 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, - 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, - 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f -} }; - -static const float fft_tone_envelope_table[4][31] = { - { .009607375f, .038060248f, .084265202f, .146446645f, .222214907f, .308658302f, - .402454883f, .500000060f, .597545207f, .691341758f, .777785182f, .853553414f, - .915734828f, .961939812f, .990392685f, 1.00000000f, .990392625f, .961939752f, - .915734768f, .853553295f, .777785063f, .691341639f, .597545087f, .500000000f, - .402454853f, .308658272f, .222214878f, .146446615f, .084265172f, .038060218f, - .009607345f }, - { .038060248f, .146446645f, .308658302f, .500000060f, .691341758f, .853553414f, - .961939812f, 1.00000000f, .961939752f, .853553295f, .691341639f, .500000000f, - .308658272f, .146446615f, .038060218f, .000000000f, .000000000f, .000000000f, - .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, - .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, - .000000000f }, - { .146446645f, .500000060f, .853553414f, 1.00000000f, .853553295f, .500000000f, - .146446615f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, - .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, - .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, - .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, - .000000000f }, - { .500000060f, 1.00000000f, .500000000f, .000000000f, .000000000f, .000000000f, - .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, - .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, - .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, - .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, - .000000000f } -}; - -static const float sb_noise_attenuation[32] = { - 0.0f, 0.0f, 0.3f, 0.4f, 0.5f, 0.7f, 1.0f, 1.0f, - 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, - 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, - 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -}; - -static const byte fft_subpackets[32] = { - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0 -}; - -// first index is joined_stereo, second index is 0 or 2 (1 is unused) -static const float dequant_1bit[2][3] = { - {-0.920000f, 0.000000f, 0.920000f }, - {-0.890000f, 0.000000f, 0.890000f } -}; - -static const float type30_dequant[8] = { - -1.0f,-0.625f,-0.291666656732559f,0.0f, - 0.25f,0.5f,0.75f,1.0f, -}; - -static const float type34_delta[10] = { // FIXME: covers 8 entries.. - -1.0f,-0.60947573184967f,-0.333333343267441f,-0.138071194291115f,0.0f, - 0.138071194291115f,0.333333343267441f,0.60947573184967f,1.0f,0.0f, -}; - -} // End of namespace Mohawk - -#endif diff --git a/engines/mohawk/video/qt_player.cpp b/engines/mohawk/video/qt_player.cpp deleted file mode 100644 index 0ed05bb84d..0000000000 --- a/engines/mohawk/video/qt_player.cpp +++ /dev/null @@ -1,1272 +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. - * - * $URL$ - * $Id$ - * - */ - -// -// Heavily based on ffmpeg code. -// -// Copyright (c) 2001 Fabrice Bellard. -// First version by Francois Revol revol@free.fr -// Seek function by Gael Chardon gael.dev@4now.net -// - -#include "mohawk/video/qt_player.h" - -#include "common/debug.h" -#include "common/endian.h" -#include "common/util.h" -#include "common/zlib.h" - -// Audio codecs -#include "sound/decoders/adpcm.h" -#include "sound/decoders/raw.h" -#include "mohawk/video/qdm2.h" - -// Video codecs -#include "mohawk/jpeg.h" -#include "mohawk/video/cinepak.h" -#include "mohawk/video/qtrle.h" -#include "mohawk/video/rpza.h" -#include "mohawk/video/smc.h" - -namespace Mohawk { - -//////////////////////////////////////////// -// QTPlayer -//////////////////////////////////////////// - -QTPlayer::QTPlayer() : Graphics::VideoDecoder() { - _audStream = NULL; - _beginOffset = 0; - _videoCodec = NULL; - _curFrame = -1; - _startTime = _nextFrameStartTime = 0; - _audHandle = Audio::SoundHandle(); - _numStreams = 0; - _fd = 0; - _scaledSurface = 0; - _dirtyPalette = false; -} - -QTPlayer::~QTPlayer() { - close(); -} - -uint16 QTPlayer::getWidth() const { - if (_videoStreamIndex < 0) - return 0; - - return _streams[_videoStreamIndex]->width / getScaleMode(); -} - -uint16 QTPlayer::getHeight() const { - if (_videoStreamIndex < 0) - return 0; - - return _streams[_videoStreamIndex]->height / getScaleMode(); -} - -uint32 QTPlayer::getFrameCount() const { - if (_videoStreamIndex < 0) - return 0; - - return _streams[_videoStreamIndex]->nb_frames; -} - -byte QTPlayer::getBitsPerPixel() { - if (_videoStreamIndex < 0) - return 0; - - return _streams[_videoStreamIndex]->bits_per_sample & 0x1F; -} - -uint32 QTPlayer::getCodecTag() { - if (_videoStreamIndex < 0) - return 0; - - return _streams[_videoStreamIndex]->codec_tag; -} - -ScaleMode QTPlayer::getScaleMode() const { - if (_videoStreamIndex < 0) - return kScaleNormal; - - return (ScaleMode)(_scaleMode * _streams[_videoStreamIndex]->scaleMode); -} - -uint32 QTPlayer::getFrameDuration() { - if (_videoStreamIndex < 0) - return 0; - - uint32 curFrameIndex = 0; - for (int32 i = 0; i < _streams[_videoStreamIndex]->stts_count; i++) { - curFrameIndex += _streams[_videoStreamIndex]->stts_data[i].count; - if ((uint32)_curFrame < curFrameIndex) { - // Ok, now we have what duration this frame has. - return _streams[_videoStreamIndex]->stts_data[i].duration; - } - } - - // This should never occur - error ("Cannot find duration for frame %d", _curFrame); - return 0; -} - -Graphics::PixelFormat QTPlayer::getPixelFormat() const { - if (!_videoCodec) - return Graphics::PixelFormat::createFormatCLUT8(); - - return _videoCodec->getPixelFormat(); -} - -void QTPlayer::rewind() { - delete _videoCodec; _videoCodec = NULL; - _curFrame = -1; - _startTime = _nextFrameStartTime = 0; - - // Restart the audio too - stopAudio(); - if (_audioStreamIndex >= 0) { - _curAudioChunk = 0; - _audStream = Audio::makeQueuingAudioStream(_streams[_audioStreamIndex]->sample_rate, _streams[_audioStreamIndex]->channels == 2); - } - startAudio(); -} - -Graphics::Codec *QTPlayer::createCodec(uint32 codecTag, byte bitsPerPixel) { - if (codecTag == MKID_BE('cvid')) { - // Cinepak: As used by most Myst and all Riven videos as well as some Myst ME videos. "The Chief" videos also use this. - return new CinepakDecoder(); - } else if (codecTag == MKID_BE('rpza')) { - // Apple Video ("Road Pizza"): Used by some Myst videos. - return new RPZADecoder(getWidth(), getHeight()); - } else if (codecTag == MKID_BE('rle ')) { - // QuickTime RLE: Used by some Myst ME videos. - return new QTRLEDecoder(getWidth(), getHeight(), bitsPerPixel); - } else if (codecTag == MKID_BE('smc ')) { - // Apple SMC: Used by some Myst videos. - return new SMCDecoder(getWidth(), getHeight()); - } else if (codecTag == MKID_BE('SVQ1')) { - // Sorenson Video 1: Used by some Myst ME videos. - warning ("Sorenson Video 1 not yet supported"); - } else if (codecTag == MKID_BE('SVQ3')) { - // Sorenson Video 3: Used by some Myst ME videos. - warning ("Sorenson Video 3 not yet supported"); - } else if (codecTag == MKID_BE('jpeg')) { - // Motion JPEG: Used by some Myst ME 10th Anniversary videos. - return new JPEGDecoder(true); - } else if (codecTag == MKID_BE('QkBk')) { - // CDToons: Used by most of the Broderbund games. This is an unknown format so far. - warning ("CDToons not yet supported"); - } else { - warning ("Unsupported codec \'%s\'", tag2str(codecTag)); - } - - return NULL; -} - -void QTPlayer::startAudio() { - if (_audStream) // No audio/audio not supported - g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_audHandle, _audStream); -} - -void QTPlayer::stopAudio() { - if (_audStream) { - g_system->getMixer()->stopHandle(_audHandle); - _audStream = NULL; // the mixer automatically frees the stream - } -} - -void QTPlayer::pauseVideoIntern(bool pause) { - if (_audStream) - g_system->getMixer()->pauseHandle(_audHandle, pause); -} - -Graphics::Surface *QTPlayer::decodeNextFrame() { - if (!_videoCodec || _curFrame >= (int32)getFrameCount() - 1) - return NULL; - - if (_startTime == 0) - _startTime = g_system->getMillis(); - - _curFrame++; - _nextFrameStartTime += getFrameDuration(); - - Common::SeekableReadStream *frameData = getNextFramePacket(); - - if (frameData) { - Graphics::Surface *frame = _videoCodec->decodeImage(frameData); - delete frameData; - return scaleSurface(frame); - } - - return NULL; -} - -Graphics::Surface *QTPlayer::scaleSurface(Graphics::Surface *frame) { - if (getScaleMode() == kScaleNormal) - return frame; - - assert(_scaledSurface); - - for (uint32 j = 0; j < _scaledSurface->h; j++) - for (uint32 k = 0; k < _scaledSurface->w; k++) - memcpy(_scaledSurface->getBasePtr(k, j), frame->getBasePtr(k * getScaleMode(), j * getScaleMode()), frame->bytesPerPixel); - - return _scaledSurface; -} - -bool QTPlayer::endOfVideo() const { - return (!_audStream || _audStream->endOfData()) && (!_videoCodec || _curFrame >= (int32)getFrameCount() - 1); -} - -bool QTPlayer::needsUpdate() const { - return !endOfVideo() && getTimeToNextFrame() == 0; -} - -uint32 QTPlayer::getElapsedTime() const { - if (_audStream) - return g_system->getMixer()->getSoundElapsedTime(_audHandle); - - return g_system->getMillis() - _startTime; -} - -uint32 QTPlayer::getTimeToNextFrame() const { - if (endOfVideo() || _curFrame < 0) - return 0; - - // Convert from the Sega FILM base to 1000 - uint32 nextFrameStartTime = _nextFrameStartTime * 1000 / _streams[_videoStreamIndex]->time_scale; - uint32 elapsedTime = getElapsedTime(); - - if (nextFrameStartTime <= elapsedTime) - return 0; - - return nextFrameStartTime - elapsedTime; -} - -bool QTPlayer::load(Common::SeekableReadStream &stream) { - _fd = &stream; - _foundMOOV = _foundMDAT = false; - _numStreams = 0; - _partial = 0; - _videoStreamIndex = _audioStreamIndex = -1; - _startTime = 0; - - initParseTable(); - - MOVatom atom = { 0, 0, 0xffffffff }; - - if (readDefault(atom) < 0 || (!_foundMOOV && !_foundMDAT)) - return false; - - debug(0, "on_parse_exit_offset=%d", _fd->pos()); - - // some cleanup : make sure we are on the mdat atom - if((uint32)_fd->pos() != _mdatOffset) - _fd->seek(_mdatOffset, SEEK_SET); - - _next_chunk_offset = _mdatOffset; // initialise reading - - for (uint32 i = 0; i < _numStreams;) { - if (_streams[i]->codec_type == CODEC_TYPE_MOV_OTHER) {// not audio, not video, delete - delete _streams[i]; - for (uint32 j = i + 1; j < _numStreams; j++) - _streams[j - 1] = _streams[j]; - _numStreams--; - } else - i++; - } - - for (uint32 i = 0; i < _numStreams; i++) { - MOVStreamContext *sc = _streams[i]; - - if(!sc->time_rate) - sc->time_rate = 1; - - if(!sc->time_scale) - sc->time_scale = _timeScale; - - //av_set_pts_info(s->streams[i], 64, sc->time_rate, sc->time_scale); - - sc->duration /= sc->time_rate; - - sc->ffindex = i; - sc->is_ff_stream = 1; - - if (sc->codec_type == CODEC_TYPE_VIDEO && _videoStreamIndex < 0) - _videoStreamIndex = i; - else if (sc->codec_type == CODEC_TYPE_AUDIO && _audioStreamIndex < 0) - _audioStreamIndex = i; - } - - if (_audioStreamIndex >= 0 && checkAudioCodecSupport(_streams[_audioStreamIndex]->codec_tag)) { - _audStream = Audio::makeQueuingAudioStream(_streams[_audioStreamIndex]->sample_rate, _streams[_audioStreamIndex]->channels == 2); - _curAudioChunk = 0; - - // Make sure the bits per sample transfers to the sample size - if (_streams[_audioStreamIndex]->codec_tag == MKID_BE('raw ') || _streams[_audioStreamIndex]->codec_tag == MKID_BE('twos')) - _streams[_audioStreamIndex]->sample_size = (_streams[_audioStreamIndex]->bits_per_sample / 8) * _streams[_audioStreamIndex]->channels; - - startAudio(); - } - - if (_videoStreamIndex >= 0) { - _videoCodec = createCodec(getCodecTag(), getBitsPerPixel()); - - if (getScaleMode() != kScaleNormal) { - // We have to initialize the scaled surface - _scaledSurface = new Graphics::Surface(); - _scaledSurface->create(getWidth(), getHeight(), getPixelFormat().bytesPerPixel); - } - } - - return true; -} - -void QTPlayer::initParseTable() { - static const ParseTable p[] = { - { MKID_BE('dinf'), &QTPlayer::readDefault }, - { MKID_BE('dref'), &QTPlayer::readLeaf }, - { MKID_BE('edts'), &QTPlayer::readDefault }, - { MKID_BE('elst'), &QTPlayer::readELST }, - { MKID_BE('hdlr'), &QTPlayer::readHDLR }, - { MKID_BE('mdat'), &QTPlayer::readMDAT }, - { MKID_BE('mdhd'), &QTPlayer::readMDHD }, - { MKID_BE('mdia'), &QTPlayer::readDefault }, - { MKID_BE('minf'), &QTPlayer::readDefault }, - { MKID_BE('moov'), &QTPlayer::readMOOV }, - { MKID_BE('mvhd'), &QTPlayer::readMVHD }, - { MKID_BE('smhd'), &QTPlayer::readLeaf }, - { MKID_BE('stbl'), &QTPlayer::readDefault }, - { MKID_BE('stco'), &QTPlayer::readSTCO }, - { MKID_BE('stsc'), &QTPlayer::readSTSC }, - { MKID_BE('stsd'), &QTPlayer::readSTSD }, - { MKID_BE('stss'), &QTPlayer::readSTSS }, - { MKID_BE('stsz'), &QTPlayer::readSTSZ }, - { MKID_BE('stts'), &QTPlayer::readSTTS }, - { MKID_BE('tkhd'), &QTPlayer::readTKHD }, - { MKID_BE('trak'), &QTPlayer::readTRAK }, - { MKID_BE('udta'), &QTPlayer::readLeaf }, - { MKID_BE('vmhd'), &QTPlayer::readLeaf }, - { MKID_BE('cmov'), &QTPlayer::readCMOV }, - { MKID_BE('wave'), &QTPlayer::readWAVE }, - { 0, 0 } - }; - - _parseTable = p; -} - -int QTPlayer::readDefault(MOVatom atom) { - uint32 total_size = 0; - MOVatom a; - int err = 0; - - a.offset = atom.offset; - - while(((total_size + 8) < atom.size) && !_fd->eos() && !err) { - a.size = atom.size; - a.type = 0; - - if (atom.size >= 8) { - a.size = _fd->readUint32BE(); - a.type = _fd->readUint32BE(); - } - - total_size += 8; - a.offset += 8; - debug(4, "type: %08x %.4s sz: %x %x %x", a.type, tag2str(a.type), a.size, atom.size, total_size); - - if (a.size == 1) { // 64 bit extended size - warning("64 bit extended size is not supported in QuickTime"); - return -1; - } - - if (a.size == 0) { - a.size = atom.size - total_size; - if (a.size <= 8) - break; - } - - uint32 i = 0; - - for (; _parseTable[i].type != 0 && _parseTable[i].type != a.type; i++) - // empty; - - if (a.size < 8) - break; - - a.size -= 8; - - if (_parseTable[i].type == 0) { // skip leaf atoms data - debug(0, ">>> Skipped [%s]", tag2str(a.type)); - - _fd->seek(a.size, SEEK_CUR); - } else { - uint32 start_pos = _fd->pos(); - err = (this->*_parseTable[i].func)(a); - - uint32 left = a.size - _fd->pos() + start_pos; - - if (left > 0) // skip garbage at atom end - _fd->seek(left, SEEK_CUR); - } - - a.offset += a.size; - total_size += a.size; - } - - if (!err && total_size < atom.size) - _fd->seek(atom.size - total_size, SEEK_SET); - - return err; -} - -int QTPlayer::readLeaf(MOVatom atom) { - if (atom.size > 1) - _fd->seek(atom.size, SEEK_SET); - - return 0; -} - -int QTPlayer::readMOOV(MOVatom atom) { - if (readDefault(atom) < 0) - return -1; - - // we parsed the 'moov' atom, we can terminate the parsing as soon as we find the 'mdat' - // so we don't parse the whole file if over a network - _foundMOOV = true; - - if(_foundMDAT) - return 1; // found both, just go - - return 0; // now go for mdat -} - -int QTPlayer::readCMOV(MOVatom atom) { -#ifdef USE_ZLIB - // Read in the dcom atom - _fd->readUint32BE(); - if (_fd->readUint32BE() != MKID_BE('dcom')) - return -1; - if (_fd->readUint32BE() != MKID_BE('zlib')) { - warning("Unknown cmov compression type"); - return -1; - } - - // Read in the cmvd atom - uint32 compressedSize = _fd->readUint32BE() - 12; - if (_fd->readUint32BE() != MKID_BE('cmvd')) - return -1; - uint32 uncompressedSize = _fd->readUint32BE(); - - // Read in data - byte *compressedData = (byte *)malloc(compressedSize); - _fd->read(compressedData, compressedSize); - - // Create uncompressed stream - byte *uncompressedData = (byte *)malloc(uncompressedSize); - - // Uncompress the data - unsigned long dstLen = uncompressedSize; - if (!Common::uncompress(uncompressedData, &dstLen, compressedData, compressedSize)) { - warning ("Could not uncompress cmov chunk"); - return -1; - } - - // Load data into a new MemoryReadStream and assign _fd to be that - Common::SeekableReadStream *oldStream = _fd; - _fd = new Common::MemoryReadStream(uncompressedData, uncompressedSize, DisposeAfterUse::YES); - - // Read the contents of the uncompressed data - MOVatom a = { MKID_BE('moov'), 0, uncompressedSize }; - int err = readDefault(a); - - // Assign the file handle back to the original handle - free(compressedData); - delete _fd; - _fd = oldStream; - - return err; -#else - warning ("zlib not found, cannot read QuickTime cmov atom"); - return -1; -#endif -} - -int QTPlayer::readMVHD(MOVatom atom) { - byte version = _fd->readByte(); // version - _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags - - if (version == 1) { - warning("QuickTime version 1"); - _fd->readUint32BE(); _fd->readUint32BE(); - _fd->readUint32BE(); _fd->readUint32BE(); - } else { - _fd->readUint32BE(); // creation time - _fd->readUint32BE(); // modification time - } - - _timeScale = _fd->readUint32BE(); // time scale - debug(0, "time scale = %i\n", _timeScale); - - // duration - _duration = (version == 1) ? (_fd->readUint32BE(), _fd->readUint32BE()) : _fd->readUint32BE(); - _fd->readUint32BE(); // preferred scale - - _fd->readUint16BE(); // preferred volume - - _fd->seek(10, SEEK_CUR); // reserved - - // We only need two values from the movie display matrix. Most of the values are just - // skipped. xMod and yMod are 16:16 fixed point numbers, the last part of the 3x3 matrix - // is 2:30. - uint32 xMod = _fd->readUint32BE(); - _fd->skip(12); - uint32 yMod = _fd->readUint32BE(); - _fd->skip(16); - - if (xMod != yMod) - error("X and Y resolution modifiers differ"); - - if (xMod == 0x8000) - _scaleMode = kScaleHalf; - else if (xMod == 0x4000) - _scaleMode = kScaleQuarter; - else - _scaleMode = kScaleNormal; - - debug(1, "readMVHD(): scaleMode = %d", (int)_scaleMode); - - _fd->readUint32BE(); // preview time - _fd->readUint32BE(); // preview duration - _fd->readUint32BE(); // poster time - _fd->readUint32BE(); // selection time - _fd->readUint32BE(); // selection duration - _fd->readUint32BE(); // current time - _fd->readUint32BE(); // next track ID - - return 0; -} - -int QTPlayer::readTRAK(MOVatom atom) { - MOVStreamContext *sc = new MOVStreamContext(); - - if (!sc) - return -1; - - sc->sample_to_chunk_index = -1; - sc->codec_type = CODEC_TYPE_MOV_OTHER; - sc->start_time = 0; // XXX: check - _streams[_numStreams++] = sc; - - return readDefault(atom); -} - -// this atom contains actual media data -int QTPlayer::readMDAT(MOVatom atom) { - if (atom.size == 0) // wrong one (MP4) - return 0; - - _foundMDAT = true; - - _mdatOffset = atom.offset; - _mdatSize = atom.size; - - if (_foundMOOV) - return 1; // found both, just go - - _fd->seek(atom.size, SEEK_CUR); - - return 0; // now go for moov -} - -int QTPlayer::readTKHD(MOVatom atom) { - MOVStreamContext *st = _streams[_numStreams - 1]; - byte version = _fd->readByte(); - - _fd->readByte(); _fd->readByte(); - _fd->readByte(); // flags - // - //MOV_TRACK_ENABLED 0x0001 - //MOV_TRACK_IN_MOVIE 0x0002 - //MOV_TRACK_IN_PREVIEW 0x0004 - //MOV_TRACK_IN_POSTER 0x0008 - // - - if (version == 1) { - _fd->readUint32BE(); _fd->readUint32BE(); - _fd->readUint32BE(); _fd->readUint32BE(); - } else { - _fd->readUint32BE(); // creation time - _fd->readUint32BE(); // modification time - } - - /* st->id = */_fd->readUint32BE(); // track id (NOT 0 !) - _fd->readUint32BE(); // reserved - //st->start_time = 0; // check - (version == 1) ? (_fd->readUint32BE(), _fd->readUint32BE()) : _fd->readUint32BE(); // highlevel (considering edits) duration in movie timebase - _fd->readUint32BE(); // reserved - _fd->readUint32BE(); // reserved - - _fd->readUint16BE(); // layer - _fd->readUint16BE(); // alternate group - _fd->readUint16BE(); // volume - _fd->readUint16BE(); // reserved - - // We only need the two values from the displacement matrix for a track. - // See readMVHD() for more information. - uint32 xMod = _fd->readUint32BE(); - _fd->skip(12); - uint32 yMod = _fd->readUint32BE(); - _fd->skip(16); - - if (xMod != yMod) - error("X and Y resolution modifiers differ"); - - if (xMod == 0x8000) - st->scaleMode = kScaleHalf; - else if (xMod == 0x4000) - st->scaleMode = kScaleQuarter; - else - st->scaleMode = kScaleNormal; - - debug(1, "readTKHD(): scaleMode = %d", (int)_scaleMode); - - // these are fixed-point, 16:16 - // uint32 tkWidth = _fd->readUint32BE() >> 16; // track width - // uint32 tkHeight = _fd->readUint32BE() >> 16; // track height - - return 0; -} - -// edit list atom -int QTPlayer::readELST(MOVatom atom) { - _fd->readByte(); // version - _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags - uint32 editCount = _streams[_numStreams - 1]->edit_count = _fd->readUint32BE(); // entries - - for (uint32 i = 0; i < editCount; i++){ - _fd->readUint32BE(); // Track duration - _fd->readUint32BE(); // Media time - _fd->readUint32BE(); // Media rate - } - - debug(0, "track[%i].edit_count = %i", _numStreams - 1, _streams[_numStreams - 1]->edit_count); - - if (editCount != 1) - warning("Multiple edit list entries. Things may go awry"); - - return 0; -} - -int QTPlayer::readHDLR(MOVatom atom) { - MOVStreamContext *st = _streams[_numStreams - 1]; - - _fd->readByte(); // version - _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags - - // component type - uint32 ctype = _fd->readUint32LE(); - uint32 type = _fd->readUint32BE(); // component subtype - - debug(0, "ctype= %s (0x%08lx)", tag2str(ctype), (long)ctype); - debug(0, "stype= %s", tag2str(type)); - - if(ctype == MKID_BE('mhlr')) // MOV - debug(0, "MOV detected"); - else if(ctype == 0) { - warning("MP4 streams are not supported"); - return -1; - } - - if (type == MKID_BE('vide')) - st->codec_type = CODEC_TYPE_VIDEO; - else if (type == MKID_BE('soun')) - st->codec_type = CODEC_TYPE_AUDIO; - - _fd->readUint32BE(); // component manufacture - _fd->readUint32BE(); // component flags - _fd->readUint32BE(); // component flags mask - - if (atom.size <= 24) - return 0; // nothing left to read - - // .mov: PASCAL string - byte len = _fd->readByte(); - _fd->seek(len, SEEK_CUR); - - _fd->seek(atom.size - (_fd->pos() - atom.offset), SEEK_CUR); - - return 0; -} - -int QTPlayer::readMDHD(MOVatom atom) { - MOVStreamContext *st = _streams[_numStreams - 1]; - byte version = _fd->readByte(); - - if (version > 1) - return 1; // unsupported - - _fd->readByte(); _fd->readByte(); - _fd->readByte(); // flags - - if (version == 1) { - _fd->readUint32BE(); _fd->readUint32BE(); - _fd->readUint32BE(); _fd->readUint32BE(); - } else { - _fd->readUint32BE(); // creation time - _fd->readUint32BE(); // modification time - } - - st->time_scale = _fd->readUint32BE(); - st->duration = (version == 1) ? (_fd->readUint32BE(), _fd->readUint32BE()) : _fd->readUint32BE(); // duration - - _fd->readUint16BE(); // language - _fd->readUint16BE(); // quality - - return 0; -} - -int QTPlayer::readSTSD(MOVatom atom) { - MOVStreamContext *st = _streams[_numStreams - 1]; - - _fd->readByte(); // version - _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags - - uint32 entries = _fd->readUint32BE(); - - while (entries--) { //Parsing Sample description table - MOVatom a = { 0, 0, 0 }; - uint32 start_pos = _fd->pos(); - int size = _fd->readUint32BE(); // size - uint32 format = _fd->readUint32BE(); // data format - - _fd->readUint32BE(); // reserved - _fd->readUint16BE(); // reserved - _fd->readUint16BE(); // index - - debug(0, "size=%d 4CC= %s codec_type=%d", size, tag2str(format), st->codec_type); - st->codec_tag = format; - - if (st->codec_type == CODEC_TYPE_VIDEO) { - debug(0, "Video Codec FourCC: \'%s\'", tag2str(format)); - - _fd->readUint16BE(); // version - _fd->readUint16BE(); // revision level - _fd->readUint32BE(); // vendor - _fd->readUint32BE(); // temporal quality - _fd->readUint32BE(); // spacial quality - - st->width = _fd->readUint16BE(); // width - st->height = _fd->readUint16BE(); // height - - _fd->readUint32BE(); // horiz resolution - _fd->readUint32BE(); // vert resolution - _fd->readUint32BE(); // data size, always 0 - uint16 frames_per_sample = _fd->readUint16BE(); // frames per samples - - debug(0, "frames/samples = %d", frames_per_sample); - - byte codec_name[32]; - _fd->read(codec_name, 32); // codec name, pascal string (FIXME: true for mp4?) - if (codec_name[0] <= 31) { - memcpy(st->codec_name, &codec_name[1], codec_name[0]); - st->codec_name[codec_name[0]] = 0; - } - - st->bits_per_sample = _fd->readUint16BE(); // depth - st->color_table_id = _fd->readUint16BE(); // colortable id - -// These are set in mov_read_stts and might already be set! -// st->codec->time_base.den = 25; -// st->codec->time_base.num = 1; - - - // figure out the palette situation - byte colorDepth = st->bits_per_sample & 0x1F; - bool colorGreyscale = (st->bits_per_sample & 0x20) != 0; - - debug(0, "color depth: %d", colorDepth); - - // if the depth is 2, 4, or 8 bpp, file is palettized - if (colorDepth == 2 || colorDepth == 4 || colorDepth == 8) { - _dirtyPalette = true; - - if (colorGreyscale) { - debug(0, "Greyscale palette"); - - // compute the greyscale palette - uint16 colorCount = 1 << colorDepth; - int16 colorIndex = 255; - byte colorDec = 256 / (colorCount - 1); - for (byte j = 0; j < colorCount; j++) { - _palette[j * 3] = _palette[j * 3 + 1] = _palette[j * 3 + 2] = colorIndex; - colorIndex -= colorDec; - if (colorIndex < 0) - colorIndex = 0; - } - } else if (st->color_table_id & 0x08) { - // if flag bit 3 is set, use the default palette - //uint16 colorCount = 1 << colorDepth; - - warning("Predefined palette! %dbpp", colorDepth); -#if 0 - byte *color_table; - byte r, g, b; - - if (colorDepth == 2) - color_table = ff_qt_default_palette_4; - else if (colorDepth == 4) - color_table = ff_qt_default_palette_16; - else - color_table = ff_qt_default_palette_256; - - for (byte j = 0; j < color_count; j++) { - r = color_table[j * 4 + 0]; - g = color_table[j * 4 + 1]; - b = color_table[j * 4 + 2]; - _palette_control.palette[j] = (r << 16) | (g << 8) | (b); - } -#endif - - } else { - debug(0, "Palette from file"); - - // load the palette from the file - uint32 colorStart = _fd->readUint32BE(); - /* uint16 colorCount = */ _fd->readUint16BE(); - uint16 colorEnd = _fd->readUint16BE(); - for (uint32 j = colorStart; j <= colorEnd; j++) { - // each R, G, or B component is 16 bits; - // only use the top 8 bits; skip alpha bytes - // up front - _fd->readByte(); - _fd->readByte(); - _palette[j * 3] = _fd->readByte(); - _fd->readByte(); - _palette[j * 3 + 1] = _fd->readByte(); - _fd->readByte(); - _palette[j * 3 + 2] = _fd->readByte(); - _fd->readByte(); - } - } - st->palettized = true; - } else - st->palettized = false; - } else if (st->codec_type == CODEC_TYPE_AUDIO) { - debug(0, "Audio Codec FourCC: \'%s\'", tag2str(format)); - - st->stsd_version = _fd->readUint16BE(); - _fd->readUint16BE(); // revision level - _fd->readUint32BE(); // vendor - - st->channels = _fd->readUint16BE(); // channel count - st->bits_per_sample = _fd->readUint16BE(); // sample size - // do we need to force to 16 for AMR ? - - // handle specific s8 codec - _fd->readUint16BE(); // compression id = 0 - _fd->readUint16BE(); // packet size = 0 - - st->sample_rate = (_fd->readUint32BE() >> 16); - - debug(0, "stsd version =%d", st->stsd_version); - if (st->stsd_version == 0) { - // Not used, except in special cases. See below. - st->samples_per_frame = st->bytes_per_frame = 0; - } else if (st->stsd_version == 1) { - // Read QT version 1 fields. In version 0 these dont exist. - st->samples_per_frame = _fd->readUint32BE(); - debug(0, "stsd samples_per_frame =%d", st->samples_per_frame); - _fd->readUint32BE(); // bytes per packet - st->bytes_per_frame = _fd->readUint32BE(); - debug(0, "stsd bytes_per_frame =%d", st->bytes_per_frame); - _fd->readUint32BE(); // bytes per sample - } else { - warning("Unsupported QuickTime STSD audio version %d", st->stsd_version); - return 1; - } - - // Version 0 videos (such as the Riven ones) don't have this set, - // but we need it later on. Add it in here. - if (format == MKID_BE('ima4')) { - st->samples_per_frame = 64; - st->bytes_per_frame = 34 * st->channels; - } - } else { - // other codec type, just skip (rtp, mp4s, tmcd ...) - _fd->seek(size - (_fd->pos() - start_pos), SEEK_CUR); - } - - // this will read extra atoms at the end (wave, alac, damr, avcC, SMI ...) - a.size = size - (_fd->pos() - start_pos); - if (a.size > 8) - readDefault(a); - else if (a.size > 0) - _fd->seek(a.size, SEEK_CUR); - } - - if (st->codec_type == CODEC_TYPE_AUDIO && st->sample_rate == 0 && st->time_scale > 1) - st->sample_rate= st->time_scale; - - return 0; -} - -int QTPlayer::readSTSC(MOVatom atom) { - MOVStreamContext *st = _streams[_numStreams - 1]; - - _fd->readByte(); // version - _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags - - st->sample_to_chunk_sz = _fd->readUint32BE(); - - debug(0, "track[%i].stsc.entries = %i", _numStreams - 1, st->sample_to_chunk_sz); - - st->sample_to_chunk = new MOVstsc[st->sample_to_chunk_sz]; - - if (!st->sample_to_chunk) - return -1; - - for (uint32 i = 0; i < st->sample_to_chunk_sz; i++) { - st->sample_to_chunk[i].first = _fd->readUint32BE(); - st->sample_to_chunk[i].count = _fd->readUint32BE(); - st->sample_to_chunk[i].id = _fd->readUint32BE(); - //printf ("Sample to Chunk[%d]: First = %d, Count = %d\n", i, st->sample_to_chunk[i].first, st->sample_to_chunk[i].count); - } - - return 0; -} - -int QTPlayer::readSTSS(MOVatom atom) { - MOVStreamContext *st = _streams[_numStreams - 1]; - - _fd->readByte(); // version - _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags - - st->keyframe_count = _fd->readUint32BE(); - - debug(0, "keyframe_count = %d", st->keyframe_count); - - st->keyframes = new uint32[st->keyframe_count]; - - if (!st->keyframes) - return -1; - - for (uint32 i = 0; i < st->keyframe_count; i++) { - st->keyframes[i] = _fd->readUint32BE(); - debug(6, "keyframes[%d] = %d", i, st->keyframes[i]); - - } - return 0; -} - -int QTPlayer::readSTSZ(MOVatom atom) { - MOVStreamContext *st = _streams[_numStreams - 1]; - - _fd->readByte(); // version - _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags - - st->sample_size = _fd->readUint32BE(); - st->sample_count = _fd->readUint32BE(); - - debug(5, "sample_size = %d sample_count = %d", st->sample_size, st->sample_count); - - if (st->sample_size) - return 0; // there isn't any table following - - st->sample_sizes = new uint32[st->sample_count]; - - if (!st->sample_sizes) - return -1; - - for(uint32 i = 0; i < st->sample_count; i++) { - st->sample_sizes[i] = _fd->readUint32BE(); - debug(6, "sample_sizes[%d] = %d", i, st->sample_sizes[i]); - } - - return 0; -} - -static uint32 ff_gcd(uint32 a, uint32 b) { - if(b) return ff_gcd(b, a%b); - else return a; -} - -int QTPlayer::readSTTS(MOVatom atom) { - MOVStreamContext *st = _streams[_numStreams - 1]; - uint32 duration = 0; - uint32 total_sample_count = 0; - - _fd->readByte(); // version - _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags - - st->stts_count = _fd->readUint32BE(); - st->stts_data = new MOVstts[st->stts_count]; - - debug(0, "track[%i].stts.entries = %i", _numStreams - 1, st->stts_count); - - st->time_rate = 0; - - for (int32 i = 0; i < st->stts_count; i++) { - int sample_duration; - int sample_count; - - sample_count = _fd->readUint32BE(); - sample_duration = _fd->readUint32BE(); - st->stts_data[i].count = sample_count; - st->stts_data[i].duration = sample_duration; - - st->time_rate = ff_gcd(st->time_rate, sample_duration); - - debug(0, "sample_count=%d, sample_duration=%d", sample_count, sample_duration); - - duration += sample_duration * sample_count; - total_sample_count += sample_count; - } - - st->nb_frames = total_sample_count; - - if (duration) - st->duration = duration; - - return 0; -} - -int QTPlayer::readSTCO(MOVatom atom) { - MOVStreamContext *st = _streams[_numStreams - 1]; - - _fd->readByte(); // version - _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags - - st->chunk_count = _fd->readUint32BE(); - st->chunk_offsets = new uint32[st->chunk_count]; - - if (!st->chunk_offsets) - return -1; - - for (uint32 i = 0; i < st->chunk_count; i++) { - // WORKAROUND/HACK: The offsets in Riven videos (ones inside the Mohawk archives themselves) - // have offsets relative to the archive and not the video. This is quite nasty. We subtract - // the initial offset of the stream to get the correct value inside of the stream. - st->chunk_offsets[i] = _fd->readUint32BE() - _beginOffset; - } - - for (uint32 i = 0; i < _numStreams; i++) { - MOVStreamContext *sc2 = _streams[i]; - - if(sc2 && sc2->chunk_offsets){ - uint32 first = sc2->chunk_offsets[0]; - uint32 last = sc2->chunk_offsets[sc2->chunk_count - 1]; - - if(first >= st->chunk_offsets[st->chunk_count - 1] || last <= st->chunk_offsets[0]) - _ni = 1; - } - } - - return 0; -} - -int QTPlayer::readWAVE(MOVatom atom) { - if (_numStreams < 1) - return 0; - - MOVStreamContext *st = _streams[_numStreams - 1]; - - if (atom.size > (1 << 30)) - return -1; - - if (st->codec_tag == MKID_BE('QDM2')) // Read extradata for QDM2 - st->extradata = _fd->readStream(atom.size - 8); - else if (atom.size > 8) - return readDefault(atom); - else - _fd->skip(atom.size); - - return 0; -} - -void QTPlayer::close() { - stopAudio(); - - delete _videoCodec; _videoCodec = 0; - - for (uint32 i = 0; i < _numStreams; i++) - delete _streams[i]; - - delete _fd; - - if (_scaledSurface) { - _scaledSurface->free(); - delete _scaledSurface; - _scaledSurface = 0; - } - - // The audio stream is deleted automatically - _audStream = NULL; - - Graphics::VideoDecoder::reset(); -} - -Common::SeekableReadStream *QTPlayer::getNextFramePacket() { - if (_videoStreamIndex < 0) - return NULL; - - // First, we have to track down which chunk holds the sample and which sample in the chunk contains the frame we are looking for. - int32 totalSampleCount = 0; - int32 sampleInChunk = 0; - int32 actualChunk = -1; - - for (uint32 i = 0; i < _streams[_videoStreamIndex]->chunk_count; i++) { - int32 sampleToChunkIndex = -1; - - for (uint32 j = 0; j < _streams[_videoStreamIndex]->sample_to_chunk_sz; j++) - if (i >= _streams[_videoStreamIndex]->sample_to_chunk[j].first - 1) - sampleToChunkIndex = j; - - if (sampleToChunkIndex < 0) - error("This chunk (%d) is imaginary", sampleToChunkIndex); - - totalSampleCount += _streams[_videoStreamIndex]->sample_to_chunk[sampleToChunkIndex].count; - - if (totalSampleCount > getCurFrame()) { - actualChunk = i; - sampleInChunk = _streams[_videoStreamIndex]->sample_to_chunk[sampleToChunkIndex].count - totalSampleCount + getCurFrame(); - break; - } - } - - if (actualChunk < 0) { - warning ("Could not find data for frame %d", getCurFrame()); - return NULL; - } - - // Next seek to that frame - _fd->seek(_streams[_videoStreamIndex]->chunk_offsets[actualChunk]); - - // Then, if the chunk holds more than one frame, seek to where the frame we want is located - for (int32 i = getCurFrame() - sampleInChunk; i < getCurFrame(); i++) { - if (_streams[_videoStreamIndex]->sample_size != 0) - _fd->skip(_streams[_videoStreamIndex]->sample_size); - else - _fd->skip(_streams[_videoStreamIndex]->sample_sizes[i]); - } - - // Finally, read in the raw data for the frame - //printf ("Frame Data[%d]: Offset = %d, Size = %d\n", getCurFrame(), _fd->pos(), _streams[_videoStreamIndex]->sample_sizes[getCurFrame()]); - - if (_streams[_videoStreamIndex]->sample_size != 0) - return _fd->readStream(_streams[_videoStreamIndex]->sample_size); - - return _fd->readStream(_streams[_videoStreamIndex]->sample_sizes[getCurFrame()]); -} - -bool QTPlayer::checkAudioCodecSupport(uint32 tag) { - // Check if the codec is a supported codec - if (tag == MKID_BE('twos') || tag == MKID_BE('raw ') || tag == MKID_BE('ima4') || tag == MKID_BE('QDM2')) - return true; - - warning("Audio Codec Not Supported: \'%s\'", tag2str(tag)); - - return false; -} - -Audio::AudioStream *QTPlayer::createAudioStream(Common::SeekableReadStream *stream) { - if (!stream || _audioStreamIndex < 0) - return NULL; - - if (_streams[_audioStreamIndex]->codec_tag == MKID_BE('twos') || _streams[_audioStreamIndex]->codec_tag == MKID_BE('raw ')) { - // Fortunately, most of the audio used in Myst videos is raw... - uint16 flags = 0; - if (_streams[_audioStreamIndex]->codec_tag == MKID_BE('raw ')) - flags |= Audio::FLAG_UNSIGNED; - if (_streams[_audioStreamIndex]->channels == 2) - flags |= Audio::FLAG_STEREO; - if (_streams[_audioStreamIndex]->bits_per_sample == 16) - flags |= Audio::FLAG_16BITS; - uint32 dataSize = stream->size(); - byte *data = (byte *)malloc(dataSize); - stream->read(data, dataSize); - delete stream; - return Audio::makeRawStream(data, dataSize, _streams[_audioStreamIndex]->sample_rate, flags); - } else if (_streams[_audioStreamIndex]->codec_tag == MKID_BE('ima4')) { - // Riven uses this codec (as do some Myst ME videos) - return Audio::makeADPCMStream(stream, DisposeAfterUse::YES, stream->size(), Audio::kADPCMApple, _streams[_audioStreamIndex]->sample_rate, _streams[_audioStreamIndex]->channels, 34); - } else if (_streams[_audioStreamIndex]->codec_tag == MKID_BE('QDM2')) { - // Several Myst ME videos use this codec - return new QDM2Stream(stream, _streams[_audioStreamIndex]->extradata); - } - - error("Unsupported audio codec"); - - return NULL; -} - -void QTPlayer::updateAudioBuffer() { - if (!_audStream) - return; - - // Keep two streams in buffer so that when the first ends, it goes right into the next - for (; _audStream->numQueuedStreams() < 2 && _curAudioChunk < _streams[_audioStreamIndex]->chunk_count; _curAudioChunk++) { - Common::MemoryWriteStreamDynamic *wStream = new Common::MemoryWriteStreamDynamic(); - - _fd->seek(_streams[_audioStreamIndex]->chunk_offsets[_curAudioChunk]); - - // First, we have to get the sample count - uint32 sampleCount = 0; - for (uint32 j = 0; j < _streams[_audioStreamIndex]->sample_to_chunk_sz; j++) - if (_curAudioChunk >= (_streams[_audioStreamIndex]->sample_to_chunk[j].first - 1)) - sampleCount = _streams[_audioStreamIndex]->sample_to_chunk[j].count; - assert(sampleCount); - - // Then calculate the right sizes - while (sampleCount > 0) { - uint32 samples = 0, size = 0; - - if (_streams[_audioStreamIndex]->samples_per_frame >= 160) { - samples = _streams[_audioStreamIndex]->samples_per_frame; - size = _streams[_audioStreamIndex]->bytes_per_frame; - } else if (_streams[_audioStreamIndex]->samples_per_frame > 1) { - samples = MIN<uint32>((1024 / _streams[_audioStreamIndex]->samples_per_frame) * _streams[_audioStreamIndex]->samples_per_frame, sampleCount); - size = (samples / _streams[_audioStreamIndex]->samples_per_frame) * _streams[_audioStreamIndex]->bytes_per_frame; - } else { - samples = MIN<uint32>(1024, sampleCount); - size = samples * _streams[_audioStreamIndex]->sample_size; - } - - // Now, we read in the data for this data and output it - byte *data = (byte *)malloc(size); - _fd->read(data, size); - wStream->write(data, size); - free(data); - sampleCount -= samples; - } - - // Now queue the buffer - _audStream->queueAudioStream(createAudioStream(new Common::MemoryReadStream(wStream->getData(), wStream->size(), DisposeAfterUse::YES))); - delete wStream; - } -} - -} // End of namespace Mohawk diff --git a/engines/mohawk/video/qt_player.h b/engines/mohawk/video/qt_player.h deleted file mode 100644 index 6657d3edba..0000000000 --- a/engines/mohawk/video/qt_player.h +++ /dev/null @@ -1,282 +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. - * - * $URL$ - * $Id$ - * - */ - -// -// Heavily based on ffmpeg code. -// -// Copyright (c) 2001 Fabrice Bellard. -// First version by Francois Revol revol@free.fr -// Seek function by Gael Chardon gael.dev@4now.net -// - -#ifndef MOHAWK_QT_PLAYER_H -#define MOHAWK_QT_PLAYER_H - -#include "common/scummsys.h" -#include "common/queue.h" - -#include "graphics/video/video_decoder.h" -#include "graphics/video/codecs/codec.h" - -#include "sound/audiostream.h" -#include "sound/mixer.h" - -namespace Common { - class File; -} - -namespace Mohawk { - -enum ScaleMode { - kScaleNormal = 1, - kScaleHalf = 2, - kScaleQuarter = 4 -}; - -class QTPlayer : public Graphics::RewindableVideoDecoder { -public: - QTPlayer(); - virtual ~QTPlayer(); - - /** - * Returns the width of the video - * @return the width of the video - */ - uint16 getWidth() const; - - /** - * Returns the height of the video - * @return the height of the video - */ - uint16 getHeight() const; - - /** - * Returns the amount of frames in the video - * @return the amount of frames in the video - */ - uint32 getFrameCount() const; - - /** - * Load a QuickTime video file from a SeekableReadStream - * @param stream the stream to load - */ - bool load(Common::SeekableReadStream &stream); - - /** - * Close a QuickTime encoded video file - */ - void close(); - - /** - * Returns the palette of the video - * @return the palette of the video - */ - byte *getPalette() { _dirtyPalette = false; return _palette; } - bool hasDirtyPalette() const { return _dirtyPalette; } - - /** - * Set the beginning offset of the video so we can modify the offsets in the stco - * atom of videos inside the Mohawk archives - * @param the beginning offset of the video - */ - void setChunkBeginOffset(uint32 offset) { _beginOffset = offset; } - - bool isVideoLoaded() const { return _fd != 0; } - Graphics::Surface *decodeNextFrame(); - bool needsUpdate() const; - bool endOfVideo() const; - uint32 getElapsedTime() const; - uint32 getTimeToNextFrame() const; - Graphics::PixelFormat getPixelFormat() const; - - // RewindableVideoDecoder API - void rewind(); - - // TODO: This audio function need to be removed from the public and/or added to - // the VideoDecoder API directly. I plan on replacing this function with something - // that can figure out how much audio is needed instead of constantly keeping two - // chunks in memory. - void updateAudioBuffer(); - -protected: - // This is the file handle from which data is read from. It can be the actual file handle or a decompressed stream. - Common::SeekableReadStream *_fd; - - struct MOVatom { - uint32 type; - uint32 offset; - uint32 size; - }; - - struct ParseTable { - uint32 type; - int (QTPlayer::*func)(MOVatom atom); - }; - - struct MOVstts { - int count; - int duration; - }; - - struct MOVstsc { - uint32 first; - uint32 count; - uint32 id; - }; - - enum CodecType { - CODEC_TYPE_MOV_OTHER, - CODEC_TYPE_VIDEO, - CODEC_TYPE_AUDIO - }; - - struct MOVStreamContext { - MOVStreamContext() { memset(this, 0, sizeof(MOVStreamContext)); } - ~MOVStreamContext() { - delete[] chunk_offsets; - delete[] stts_data; - delete[] ctts_data; - delete[] sample_to_chunk; - delete[] sample_sizes; - delete[] keyframes; - delete extradata; - } - - int ffindex; /* the ffmpeg stream id */ - int is_ff_stream; /* Is this stream presented to ffmpeg ? i.e. is this an audio or video stream ? */ - uint32 next_chunk; - uint32 chunk_count; - uint32 *chunk_offsets; - int stts_count; - MOVstts *stts_data; - int ctts_count; - MOVstts *ctts_data; - int edit_count; /* number of 'edit' (elst atom) */ - uint32 sample_to_chunk_sz; - MOVstsc *sample_to_chunk; - int32 sample_to_chunk_index; - int sample_to_time_index; - uint32 sample_to_time_sample; - uint32 sample_to_time_time; - int sample_to_ctime_index; - int sample_to_ctime_sample; - uint32 sample_size; - uint32 sample_count; - uint32 *sample_sizes; - uint32 keyframe_count; - uint32 *keyframes; - int32 time_scale; - int time_rate; - uint32 current_sample; - uint32 left_in_chunk; /* how many samples before next chunk */ - - uint16 width; - uint16 height; - int codec_type; - uint32 codec_tag; - char codec_name[32]; - uint16 bits_per_sample; - uint16 color_table_id; - bool palettized; - Common::SeekableReadStream *extradata; - - uint16 stsd_version; - uint16 channels; - uint16 sample_rate; - uint32 samples_per_frame; - uint32 bytes_per_frame; - - uint32 nb_frames; - uint32 duration; - uint32 start_time; - ScaleMode scaleMode; - }; - - const ParseTable *_parseTable; - bool _foundMOOV; - bool _foundMDAT; - uint32 _timeScale; - uint32 _duration; - uint32 _mdatOffset; - uint32 _mdatSize; - uint32 _next_chunk_offset; - MOVStreamContext *_partial; - uint32 _numStreams; - int _ni; - ScaleMode _scaleMode; - MOVStreamContext *_streams[20]; - byte _palette[256 * 3]; - bool _dirtyPalette; - uint32 _beginOffset; - - void initParseTable(); - Audio::AudioStream *createAudioStream(Common::SeekableReadStream *stream); - bool checkAudioCodecSupport(uint32 tag); - Common::SeekableReadStream *getNextFramePacket(); - uint32 getFrameDuration(); - uint32 getCodecTag(); - byte getBitsPerPixel(); - - Audio::QueuingAudioStream *_audStream; - void startAudio(); - void stopAudio(); - int8 _audioStreamIndex; - uint _curAudioChunk; - Audio::SoundHandle _audHandle; - - Graphics::Codec *createCodec(uint32 codecTag, byte bitsPerPixel); - Graphics::Codec *_videoCodec; - uint32 _nextFrameStartTime; - int8 _videoStreamIndex; - - Graphics::Surface *_scaledSurface; - Graphics::Surface *scaleSurface(Graphics::Surface *frame); - ScaleMode getScaleMode() const; - - void pauseVideoIntern(bool pause); - - int readDefault(MOVatom atom); - int readLeaf(MOVatom atom); - int readELST(MOVatom atom); - int readHDLR(MOVatom atom); - int readMDAT(MOVatom atom); - int readMDHD(MOVatom atom); - int readMOOV(MOVatom atom); - int readMVHD(MOVatom atom); - int readTKHD(MOVatom atom); - int readTRAK(MOVatom atom); - int readSTCO(MOVatom atom); - int readSTSC(MOVatom atom); - int readSTSD(MOVatom atom); - int readSTSS(MOVatom atom); - int readSTSZ(MOVatom atom); - int readSTTS(MOVatom atom); - int readCMOV(MOVatom atom); - int readWAVE(MOVatom atom); -}; - -} // End of namespace Mohawk - -#endif diff --git a/engines/mohawk/video/qtrle.cpp b/engines/mohawk/video/qtrle.cpp deleted file mode 100644 index c06dbefcb3..0000000000 --- a/engines/mohawk/video/qtrle.cpp +++ /dev/null @@ -1,420 +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. - * - * $URL$ - * $Id$ - * - */ - -// QuickTime RLE Decoder -// Based off ffmpeg's QuickTime RLE decoder (written by Mike Melanson) - -#include "mohawk/video/qtrle.h" - -#include "common/scummsys.h" -#include "common/stream.h" -#include "common/system.h" -#include "graphics/colormasks.h" -#include "graphics/surface.h" - -namespace Mohawk { - -QTRLEDecoder::QTRLEDecoder(uint16 width, uint16 height, byte bitsPerPixel) : Graphics::Codec() { - _bitsPerPixel = bitsPerPixel; - _pixelFormat = g_system->getScreenFormat(); - - // We need to increase the surface size to a multiple of 4 - uint16 wMod = width % 4; - if(wMod != 0) - width += 4 - wMod; - - debug(2, "QTRLE corrected width: %d", width); - - _surface = new Graphics::Surface(); - _surface->create(width, height, _bitsPerPixel <= 8 ? 1 : _pixelFormat.bytesPerPixel); -} - -#define CHECK_STREAM_PTR(n) \ - if ((stream->pos() + n) > stream->size()) { \ - warning ("Problem: stream out of bounds (%d >= %d)", stream->pos() + n, stream->size()); \ - return; \ - } - -#define CHECK_PIXEL_PTR(n) \ - if ((int32)pixelPtr + n > _surface->w * _surface->h) { \ - warning ("Problem: pixel ptr = %d, pixel limit = %d", pixelPtr + n, _surface->w * _surface->h); \ - return; \ - } \ - -void QTRLEDecoder::decode1(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange) { - uint32 pixelPtr = 0; - byte *rgb = (byte *)_surface->pixels; - - while (linesToChange) { - CHECK_STREAM_PTR(2); - byte skip = stream->readByte(); - int8 rleCode = stream->readSByte(); - - if (rleCode == 0) - break; - - if (skip & 0x80) { - linesToChange--; - rowPtr += _surface->w; - pixelPtr = rowPtr + 2 * (skip & 0x7f); - } else - pixelPtr += 2 * skip; - - if (rleCode < 0) { - // decode the run length code - rleCode = -rleCode; - // get the next 2 bytes from the stream, treat them as groups of 8 pixels, and output them rleCode times */ - CHECK_STREAM_PTR(2); - byte pi0 = stream->readByte(); - byte pi1 = stream->readByte(); - CHECK_PIXEL_PTR(rleCode * 2); - - while (rleCode--) { - rgb[pixelPtr++] = pi0; - rgb[pixelPtr++] = pi1; - } - } else { - // copy the same pixel directly to output 2 times - rleCode *= 2; - CHECK_STREAM_PTR(rleCode); - CHECK_PIXEL_PTR(rleCode); - - while (rleCode--) - rgb[pixelPtr++] = stream->readByte(); - } - } -} - -void QTRLEDecoder::decode2_4(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange, byte bpp) { - uint32 pixelPtr = 0; - byte *rgb = (byte *)_surface->pixels; - byte numPixels = (bpp == 4) ? 8 : 16; - - while (linesToChange--) { - CHECK_STREAM_PTR(2); - pixelPtr = rowPtr + (numPixels * (stream->readByte() - 1)); - - for (int8 rleCode = stream->readSByte(); rleCode != -1; rleCode = stream->readSByte()) { - if (rleCode == 0) { - // there's another skip code in the stream - CHECK_STREAM_PTR(1); - pixelPtr += (numPixels * (stream->readByte() - 1)); - } else if (rleCode < 0) { - // decode the run length code - rleCode = -rleCode; - - // get the next 4 bytes from the stream, treat them as palette indices, and output them rleCode times */ - CHECK_STREAM_PTR(4); - - byte pi[16]; // 16 palette indices - - for (int8 i = numPixels - 1; i >= 0; i--) { - pi[numPixels - 1 - i] = (stream->readByte() >> ((i * bpp) & 0x07)) & ((1 << bpp) - 1); - - // FIXME: Is this right? - //stream_ptr += ((i & ((num_pixels>>2)-1)) == 0); - if ((i & ((numPixels >> 2) - 1)) == 0) - stream->readByte(); - } - - CHECK_PIXEL_PTR(rleCode * numPixels); - - while (rleCode--) - for (byte i = 0; i < numPixels; i++) - rgb[pixelPtr++] = pi[i]; - } else { - // copy the same pixel directly to output 4 times - rleCode *= 4; - CHECK_STREAM_PTR(rleCode); - CHECK_PIXEL_PTR(rleCode * (numPixels >> 2)); - - while (rleCode--) { - byte temp = stream->readByte(); - if (bpp == 4) { - rgb[pixelPtr++] = (temp >> 4) & 0x0f; - rgb[pixelPtr++] = temp & 0x0f; - } else { - rgb[pixelPtr++] = (temp >> 6) & 0x03; - rgb[pixelPtr++] = (temp >> 4) & 0x03; - rgb[pixelPtr++] = (temp >> 2) & 0x03; - rgb[pixelPtr++] = temp & 0x03; - } - } - } - } - - rowPtr += _surface->w; - } -} - -void QTRLEDecoder::decode8(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange) { - uint32 pixelPtr = 0; - byte *rgb = (byte *)_surface->pixels; - - while (linesToChange--) { - CHECK_STREAM_PTR(2); - pixelPtr = rowPtr + 4 * (stream->readByte() - 1); - - for (int8 rleCode = stream->readSByte(); rleCode != -1; rleCode = stream->readSByte()) { - if (rleCode == 0) { - // there's another skip code in the stream - CHECK_STREAM_PTR(1); - pixelPtr += 4 * (stream->readByte() - 1); - } else if (rleCode < 0) { - // decode the run length code - rleCode = -rleCode; - - // get the next 4 bytes from the stream, treat them as palette indices, and output them rleCode times - CHECK_STREAM_PTR(4); - - byte pi[4]; // 4 palette indexes - - for (byte i = 0; i < 4; i++) - pi[i] = stream->readByte(); - - CHECK_PIXEL_PTR(rleCode * 4); - - while (rleCode--) - for (byte i = 0; i < 4; i++) - rgb[pixelPtr++] = pi[i]; - } else { - // copy the same pixel directly to output 4 times - rleCode *= 4; - CHECK_STREAM_PTR(rleCode); - CHECK_PIXEL_PTR(rleCode); - - while (rleCode--) - rgb[pixelPtr++] = stream->readByte(); - } - } - - rowPtr += _surface->w; - } -} - -void QTRLEDecoder::decode16(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange) { - uint32 pixelPtr = 0; - OverlayColor *rgb = (OverlayColor *)_surface->pixels; - - while (linesToChange--) { - CHECK_STREAM_PTR(2); - pixelPtr = rowPtr + stream->readByte() - 1; - - for (int8 rleCode = stream->readSByte(); rleCode != -1; rleCode = stream->readSByte()) { - if (rleCode == 0) { - // there's another skip code in the stream - CHECK_STREAM_PTR(1); - pixelPtr += stream->readByte() - 1; - } else if (rleCode < 0) { - // decode the run length code - rleCode = -rleCode; - CHECK_STREAM_PTR(2); - - uint16 rgb16 = stream->readUint16BE(); - - CHECK_PIXEL_PTR(rleCode); - - while (rleCode--) { - // Convert from RGB555 to the format specified by the Overlay - byte r = 0, g = 0, b = 0; - Graphics::colorToRGB<Graphics::ColorMasks<555> >(rgb16, r, g, b); - rgb[pixelPtr++] = _pixelFormat.RGBToColor(r, g, b); - } - } else { - CHECK_STREAM_PTR(rleCode * 2); - CHECK_PIXEL_PTR(rleCode); - - // copy pixels directly to output - while (rleCode--) { - uint16 rgb16 = stream->readUint16BE(); - - // Convert from RGB555 to the format specified by the Overlay - byte r = 0, g = 0, b = 0; - Graphics::colorToRGB<Graphics::ColorMasks<555> >(rgb16, r, g, b); - rgb[pixelPtr++] = _pixelFormat.RGBToColor(r, g, b); - } - } - } - - rowPtr += _surface->w; - } -} - -void QTRLEDecoder::decode24(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange) { - uint32 pixelPtr = 0; - OverlayColor *rgb = (OverlayColor *)_surface->pixels; - - while (linesToChange--) { - CHECK_STREAM_PTR(2); - pixelPtr = rowPtr + stream->readByte() - 1; - - for (int8 rleCode = stream->readSByte(); rleCode != -1; rleCode = stream->readSByte()) { - if (rleCode == 0) { - // there's another skip code in the stream - CHECK_STREAM_PTR(1); - pixelPtr += stream->readByte() - 1; - } else if (rleCode < 0) { - // decode the run length code - rleCode = -rleCode; - - CHECK_STREAM_PTR(3); - - byte r = stream->readByte(); - byte g = stream->readByte(); - byte b = stream->readByte(); - - CHECK_PIXEL_PTR(rleCode); - - while (rleCode--) - rgb[pixelPtr++] = _pixelFormat.RGBToColor(r, g, b); - } else { - CHECK_STREAM_PTR(rleCode * 3); - CHECK_PIXEL_PTR(rleCode); - - // copy pixels directly to output - while (rleCode--) { - byte r = stream->readByte(); - byte g = stream->readByte(); - byte b = stream->readByte(); - rgb[pixelPtr++] = _pixelFormat.RGBToColor(r, g, b); - } - } - } - - rowPtr += _surface->w; - } -} - -void QTRLEDecoder::decode32(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange) { - uint32 pixelPtr = 0; - OverlayColor *rgb = (OverlayColor *)_surface->pixels; - - while (linesToChange--) { - CHECK_STREAM_PTR(2); - pixelPtr = rowPtr + stream->readByte() - 1; - - for (int8 rleCode = stream->readSByte(); rleCode != -1; rleCode = stream->readSByte()) { - if (rleCode == 0) { - // there's another skip code in the stream - CHECK_STREAM_PTR(1); - pixelPtr += stream->readByte() - 1; - } else if (rleCode < 0) { - // decode the run length code - rleCode = -rleCode; - - CHECK_STREAM_PTR(4); - - byte a = stream->readByte(); - byte r = stream->readByte(); - byte g = stream->readByte(); - byte b = stream->readByte(); - - CHECK_PIXEL_PTR(rleCode); - - while (rleCode--) - rgb[pixelPtr++] = _pixelFormat.ARGBToColor(a, r, g, b); - } else { - CHECK_STREAM_PTR(rleCode * 4); - CHECK_PIXEL_PTR(rleCode); - - // copy pixels directly to output - while (rleCode--) { - byte a = stream->readByte(); - byte r = stream->readByte(); - byte g = stream->readByte(); - byte b = stream->readByte(); - rgb[pixelPtr++] = _pixelFormat.ARGBToColor(a, r, g, b); - } - } - } - - rowPtr += _surface->w; - } -} - -Graphics::Surface *QTRLEDecoder::decodeImage(Common::SeekableReadStream *stream) { - uint16 start_line = 0; - uint16 height = _surface->h; - - // check if this frame is even supposed to change - if (stream->size() < 8) - return _surface; - - // start after the chunk size - stream->readUint32BE(); - - // fetch the header - uint16 header = stream->readUint16BE(); - - // if a header is present, fetch additional decoding parameters - if (header & 8) { - if(stream->size() < 14) - return _surface; - start_line = stream->readUint16BE(); - stream->readUint16BE(); // Unknown - height = stream->readUint16BE(); - stream->readUint16BE(); // Unknown - } - - uint32 row_ptr = _surface->w * start_line; - - switch (_bitsPerPixel) { - case 1: - case 33: - decode1(stream, row_ptr, height); - break; - case 2: - case 34: - decode2_4(stream, row_ptr, height, 2); - break; - case 4: - case 36: - decode2_4(stream, row_ptr, height, 4); - break; - case 8: - case 40: - decode8(stream, row_ptr, height); - break; - case 16: - decode16(stream, row_ptr, height); - break; - case 24: - decode24(stream, row_ptr, height); - break; - case 32: - decode32(stream, row_ptr, height); - break; - default: - error ("Unsupported bits per pixel %d", _bitsPerPixel); - } - - return _surface; -} - -QTRLEDecoder::~QTRLEDecoder() { - _surface->free(); -} - -} // End of namespace Mohawk diff --git a/engines/mohawk/video/qtrle.h b/engines/mohawk/video/qtrle.h deleted file mode 100644 index 2832bd6b24..0000000000 --- a/engines/mohawk/video/qtrle.h +++ /dev/null @@ -1,58 +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. - * - * $URL$ - * $Id$ - * - */ - -#ifndef MOHAWK_QTRLE_H -#define MOHAWK_QTRLE_H - -#include "graphics/pixelformat.h" -#include "graphics/video/codecs/codec.h" - -namespace Mohawk { - -class QTRLEDecoder : public Graphics::Codec { -public: - QTRLEDecoder(uint16 width, uint16 height, byte bitsPerPixel); - ~QTRLEDecoder(); - - Graphics::Surface *decodeImage(Common::SeekableReadStream *stream); - Graphics::PixelFormat getPixelFormat() const { return _pixelFormat; } - -private: - byte _bitsPerPixel; - - Graphics::Surface *_surface; - Graphics::PixelFormat _pixelFormat; - - void decode1(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange); - void decode2_4(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange, byte bpp); - void decode8(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange); - void decode16(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange); - void decode24(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange); - void decode32(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange); -}; - -} // End of namespace Mohawk - -#endif diff --git a/engines/mohawk/video/rpza.cpp b/engines/mohawk/video/rpza.cpp deleted file mode 100644 index f48c055ae2..0000000000 --- a/engines/mohawk/video/rpza.cpp +++ /dev/null @@ -1,208 +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. - * - * $URL$ - * $Id$ - * - */ - - // Based off ffmpeg's RPZA decoder - -#include "mohawk/video/rpza.h" - -#include "common/system.h" -#include "graphics/colormasks.h" - -namespace Mohawk { - -RPZADecoder::RPZADecoder(uint16 width, uint16 height) : Graphics::Codec() { - _pixelFormat = g_system->getScreenFormat(); - - // We need to increase the surface size to a multiple of 4 - uint16 wMod = width % 4; - if(wMod != 0) - width += 4 - wMod; - - debug(2, "RPZA corrected width: %d", width); - - _surface = new Graphics::Surface(); - _surface->create(width, height, _pixelFormat.bytesPerPixel); -} - -#define ADVANCE_BLOCK() \ - pixelPtr += 4; \ - if (pixelPtr >= _surface->w) { \ - pixelPtr = 0; \ - rowPtr += _surface->w * 4; \ - } \ - totalBlocks--; \ - if (totalBlocks < 0) \ - error("block counter just went negative (this should not happen)") \ - -// Convert from RGB555 to the format specified by the screen -#define PUT_PIXEL(color) \ - if ((int32)blockPtr < _surface->w * _surface->h) { \ - byte r = 0, g = 0, b = 0; \ - Graphics::colorToRGB<Graphics::ColorMasks<555> >(color, r, g, b); \ - if (_pixelFormat.bytesPerPixel == 2) \ - *((uint16 *)_surface->pixels + blockPtr) = _pixelFormat.RGBToColor(r, g, b); \ - else \ - *((uint32 *)_surface->pixels + blockPtr) = _pixelFormat.RGBToColor(r, g, b); \ - } \ - blockPtr++ - -Graphics::Surface *RPZADecoder::decodeImage(Common::SeekableReadStream *stream) { - uint16 colorA = 0, colorB = 0; - uint16 color4[4]; - - uint32 rowPtr = 0; - uint32 pixelPtr = 0; - uint32 blockPtr = 0; - uint32 rowInc = _surface->w - 4; - uint16 ta; - uint16 tb; - - // First byte is always 0xe1. Warn if it's different - byte firstByte = stream->readByte(); - if (firstByte != 0xe1) - warning("First RPZA chunk byte is 0x%02x instead of 0xe1", firstByte); - - // Get chunk size, ingnoring first byte - uint32 chunkSize = stream->readUint16BE() << 8; - chunkSize += stream->readByte(); - - // If length mismatch use size from MOV file and try to decode anyway - if (chunkSize != (uint32)stream->size()) { - warning("MOV chunk size != encoded chunk size; using MOV chunk size"); - chunkSize = stream->size(); - } - - // Number of 4x4 blocks in frame - int32 totalBlocks = ((_surface->w + 3) / 4) * ((_surface->h + 3) / 4); - - // Process chunk data - while ((uint32)stream->pos() < chunkSize) { - byte opcode = stream->readByte(); // Get opcode - byte numBlocks = (opcode & 0x1f) + 1; // Extract block counter from opcode - - // If opcode MSbit is 0, we need more data to decide what to do - if ((opcode & 0x80) == 0) { - colorA = (opcode << 8) | stream->readByte(); - opcode = 0; - if (stream->readByte() & 0x80) { - // Must behave as opcode 110xxxxx, using colorA computed - // above. Use fake opcode 0x20 to enter switch block at - // the right place - opcode = 0x20; - numBlocks = 1; - } - stream->seek(-1, SEEK_CUR); - } - - switch (opcode & 0xe0) { - case 0x80: // Skip blocks - while (numBlocks--) { - ADVANCE_BLOCK(); - } - break; - case 0xa0: // Fill blocks with one color - colorA = stream->readUint16BE(); - while (numBlocks--) { - blockPtr = rowPtr + pixelPtr; - for (byte pixel_y = 0; pixel_y < 4; pixel_y++) { - for (byte pixel_x = 0; pixel_x < 4; pixel_x++) { - PUT_PIXEL(colorA); - } - blockPtr += rowInc; - } - ADVANCE_BLOCK(); - } - break; - - // Fill blocks with 4 colors - case 0xc0: - colorA = stream->readUint16BE(); - case 0x20: - colorB = stream->readUint16BE(); - - // Sort out the colors - color4[0] = colorB; - color4[1] = 0; - color4[2] = 0; - color4[3] = colorA; - - // Red components - ta = (colorA >> 10) & 0x1F; - tb = (colorB >> 10) & 0x1F; - color4[1] |= ((11 * ta + 21 * tb) >> 5) << 10; - color4[2] |= ((21 * ta + 11 * tb) >> 5) << 10; - - // Green components - ta = (colorA >> 5) & 0x1F; - tb = (colorB >> 5) & 0x1F; - color4[1] |= ((11 * ta + 21 * tb) >> 5) << 5; - color4[2] |= ((21 * ta + 11 * tb) >> 5) << 5; - - // Blue components - ta = colorA & 0x1F; - tb = colorB & 0x1F; - color4[1] |= ((11 * ta + 21 * tb) >> 5); - color4[2] |= ((21 * ta + 11 * tb) >> 5); - - while (numBlocks--) { - blockPtr = rowPtr + pixelPtr; - for (byte pixel_y = 0; pixel_y < 4; pixel_y++) { - byte index = stream->readByte(); - for (byte pixel_x = 0; pixel_x < 4; pixel_x++){ - byte idx = (index >> (2 * (3 - pixel_x))) & 0x03; - PUT_PIXEL(color4[idx]); - } - blockPtr += rowInc; - } - ADVANCE_BLOCK(); - } - break; - - // Fill block with 16 colors - case 0x00: - blockPtr = rowPtr + pixelPtr; - for (byte pixel_y = 0; pixel_y < 4; pixel_y++) { - for (byte pixel_x = 0; pixel_x < 4; pixel_x++){ - // We already have color of upper left pixel - if (pixel_y != 0 || pixel_x != 0) - colorA = stream->readUint16BE(); - - PUT_PIXEL(colorA); - } - blockPtr += rowInc; - } - ADVANCE_BLOCK(); - break; - - // Unknown opcode - default: - error("Unknown opcode %02x in rpza chunk", opcode); - } - } - - return _surface; -} - -} // End of namespace Mohawk diff --git a/engines/mohawk/video/smc.cpp b/engines/mohawk/video/smc.cpp deleted file mode 100644 index 4a0d16dfcc..0000000000 --- a/engines/mohawk/video/smc.cpp +++ /dev/null @@ -1,385 +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. - * - * $URL$ - * $Id$ - * - */ - -// Based off ffmpeg's SMC decoder - -#include "mohawk/video/smc.h" - -namespace Mohawk { - -#define GET_BLOCK_COUNT() \ - (opcode & 0x10) ? (1 + stream->readByte()) : 1 + (opcode & 0x0F); - -#define ADVANCE_BLOCK() \ -{ \ - pixelPtr += 4; \ - if (pixelPtr >= _surface->w) { \ - pixelPtr = 0; \ - rowPtr += _surface->w * 4; \ - } \ - totalBlocks--; \ - if (totalBlocks < 0) { \ - warning("block counter just went negative (this should not happen)"); \ - return _surface; \ - } \ -} - -SMCDecoder::SMCDecoder(uint16 width, uint16 height) { - _surface = new Graphics::Surface(); - _surface->create(width, height, 1); -} - -Graphics::Surface *SMCDecoder::decodeImage(Common::SeekableReadStream *stream) { - byte *pixels = (byte *)_surface->pixels; - - uint32 numBlocks = 0; - uint32 colorFlags = 0; - uint32 colorFlagsA = 0; - uint32 colorFlagsB = 0; - - const uint16 rowInc = _surface->w - 4; - int32 rowPtr = 0; - int32 pixelPtr = 0; - uint32 blockPtr = 0; - uint32 prevBlockPtr = 0; - uint32 prevBlockPtr1 = 0, prevBlockPtr2 = 0; - byte prevBlockFlag = false; - byte pixel = 0; - - uint32 colorPairIndex = 0; - uint32 colorQuadIndex = 0; - uint32 colorOctetIndex = 0; - uint32 colorTableIndex = 0; // indices to color pair, quad, or octet tables - - int32 chunkSize = stream->readUint32BE() & 0x00FFFFFF; - if (chunkSize != stream->size()) - warning("MOV chunk size != SMC chunk size (%d != %d); ignoring SMC chunk size", chunkSize, stream->size()); - - int32 totalBlocks = ((_surface->w + 3) / 4) * ((_surface->h + 3) / 4); - - // traverse through the blocks - while (totalBlocks != 0) { - // sanity checks - - // make sure stream ptr hasn't gone out of bounds - if (stream->pos() > stream->size()) { - warning("SMC decoder just went out of bounds (stream ptr = %d, chunk size = %d)", stream->pos(), stream->size()); - return _surface; - } - - // make sure the row pointer hasn't gone wild - if (rowPtr >= _surface->w * _surface->h) { - warning("SMC decoder just went out of bounds (row ptr = %d, size = %d)", rowPtr, _surface->w * _surface->h); - return _surface; - } - - byte opcode = stream->readByte(); - - switch (opcode & 0xF0) { - // skip n blocks - case 0x00: - case 0x10: - numBlocks = GET_BLOCK_COUNT(); - while (numBlocks--) { - ADVANCE_BLOCK(); - } - break; - - // repeat last block n times - case 0x20: - case 0x30: - numBlocks = GET_BLOCK_COUNT(); - - // sanity check - if (rowPtr == 0 && pixelPtr == 0) { - warning("encountered repeat block opcode (%02X) but no blocks rendered yet", opcode & 0xF0); - break; - } - - // figure out where the previous block started - if (pixelPtr == 0) - prevBlockPtr1 = (rowPtr - _surface->w * 4) + _surface->w - 4; - else - prevBlockPtr1 = rowPtr + pixelPtr - 4; - - while (numBlocks--) { - blockPtr = rowPtr + pixelPtr; - prevBlockPtr = prevBlockPtr1; - for (byte y = 0; y < 4; y++) { - for (byte x = 0; x < 4; x++) - pixels[blockPtr++] = pixels[prevBlockPtr++]; - blockPtr += rowInc; - prevBlockPtr += rowInc; - } - ADVANCE_BLOCK(); - } - break; - - // repeat previous pair of blocks n times - case 0x40: - case 0x50: - numBlocks = GET_BLOCK_COUNT(); - numBlocks *= 2; - - // sanity check - if (rowPtr == 0 && pixelPtr < 2 * 4) { - warning("encountered repeat block opcode (%02X) but not enough blocks rendered yet", opcode & 0xF0); - break; - } - - // figure out where the previous 2 blocks started - if (pixelPtr == 0) - prevBlockPtr1 = (rowPtr - _surface->w * 4) + _surface->w - 4 * 2; - else if (pixelPtr == 4) - prevBlockPtr1 = (rowPtr - _surface->w * 4) + rowInc; - else - prevBlockPtr1 = rowPtr + pixelPtr - 4 * 2; - - if (pixelPtr == 0) - prevBlockPtr2 = (rowPtr - _surface->w * 4) + rowInc; - else - prevBlockPtr2 = rowPtr + pixelPtr - 4; - - prevBlockFlag = 0; - while (numBlocks--) { - blockPtr = rowPtr + pixelPtr; - - if (prevBlockFlag) - prevBlockPtr = prevBlockPtr2; - else - prevBlockPtr = prevBlockPtr1; - - prevBlockFlag = !prevBlockFlag; - - for (byte y = 0; y < 4; y++) { - for (byte x = 0; x < 4; x++) - pixels[blockPtr++] = pixels[prevBlockPtr++]; - - blockPtr += rowInc; - prevBlockPtr += rowInc; - } - ADVANCE_BLOCK(); - } - break; - - // 1-color block encoding - case 0x60: - case 0x70: - numBlocks = GET_BLOCK_COUNT(); - pixel = stream->readByte(); - - while (numBlocks--) { - blockPtr = rowPtr + pixelPtr; - for (byte y = 0; y < 4; y++) { - for (byte x = 0; x < 4; x++) - pixels[blockPtr++] = pixel; - - blockPtr += rowInc; - } - ADVANCE_BLOCK(); - } - break; - - // 2-color block encoding - case 0x80: - case 0x90: - numBlocks = (opcode & 0x0F) + 1; - - // figure out which color pair to use to paint the 2-color block - if ((opcode & 0xF0) == 0x80) { - // fetch the next 2 colors from bytestream and store in next - // available entry in the color pair table - for (byte i = 0; i < CPAIR; i++) { - pixel = stream->readByte(); - colorTableIndex = CPAIR * colorPairIndex + i; - _colorPairs[colorTableIndex] = pixel; - } - - // this is the base index to use for this block - colorTableIndex = CPAIR * colorPairIndex; - colorPairIndex++; - - // wraparound - if (colorPairIndex == COLORS_PER_TABLE) - colorPairIndex = 0; - } else - colorTableIndex = CPAIR * stream->readByte(); - - while (numBlocks--) { - colorFlags = stream->readUint16BE(); - uint16 flagMask = 0x8000; - blockPtr = rowPtr + pixelPtr; - for (byte y = 0; y < 4; y++) { - for (byte x = 0; x < 4; x++) { - if (colorFlags & flagMask) - pixel = colorTableIndex + 1; - else - pixel = colorTableIndex; - - flagMask >>= 1; - pixels[blockPtr++] = _colorPairs[pixel]; - } - - blockPtr += rowInc; - } - ADVANCE_BLOCK(); - } - break; - - // 4-color block encoding - case 0xA0: - case 0xB0: - numBlocks = (opcode & 0x0F) + 1; - - // figure out which color quad to use to paint the 4-color block - if ((opcode & 0xF0) == 0xA0) { - // fetch the next 4 colors from bytestream and store in next - // available entry in the color quad table - for (byte i = 0; i < CQUAD; i++) { - pixel = stream->readByte(); - colorTableIndex = CQUAD * colorQuadIndex + i; - _colorQuads[colorTableIndex] = pixel; - } - - // this is the base index to use for this block - colorTableIndex = CQUAD * colorQuadIndex; - colorQuadIndex++; - - // wraparound - if (colorQuadIndex == COLORS_PER_TABLE) - colorQuadIndex = 0; - } else - colorTableIndex = CQUAD * stream->readByte(); - - while (numBlocks--) { - colorFlags = stream->readUint32BE(); - - // flag mask actually acts as a bit shift count here - byte flagMask = 30; - blockPtr = rowPtr + pixelPtr; - - for (byte y = 0; y < 4; y++) { - for (byte x = 0; x < 4; x++) { - pixel = colorTableIndex + ((colorFlags >> flagMask) & 0x03); - flagMask -= 2; - pixels[blockPtr++] = _colorQuads[pixel]; - } - blockPtr += rowInc; - } - ADVANCE_BLOCK(); - } - break; - - // 8-color block encoding - case 0xC0: - case 0xD0: - numBlocks = (opcode & 0x0F) + 1; - - // figure out which color octet to use to paint the 8-color block - if ((opcode & 0xF0) == 0xC0) { - // fetch the next 8 colors from bytestream and store in next - // available entry in the color octet table - for (byte i = 0; i < COCTET; i++) { - pixel = stream->readByte(); - colorTableIndex = COCTET * colorOctetIndex + i; - _colorOctets[colorTableIndex] = pixel; - } - - // this is the base index to use for this block - colorTableIndex = COCTET * colorOctetIndex; - colorOctetIndex++; - - // wraparound - if (colorOctetIndex == COLORS_PER_TABLE) - colorOctetIndex = 0; - } else - colorTableIndex = COCTET * stream->readByte(); - - while (numBlocks--) { - /* - For this input of 6 hex bytes: - 01 23 45 67 89 AB - Mangle it to this output: - flags_a = xx012456, flags_b = xx89A37B - */ - - // build the color flags - byte flagData[6]; - stream->read(flagData, 6); - - colorFlagsA = ((READ_BE_UINT16(flagData) & 0xFFF0) << 8) | (READ_BE_UINT16(flagData + 2) >> 4); - colorFlagsB = ((READ_BE_UINT16(flagData + 4) & 0xFFF0) << 8) | ((flagData[1] & 0xF) << 8) | - ((flagData[3] & 0xF) << 4) | (flagData[5] & 0xf); - - colorFlags = colorFlagsA; - - // flag mask actually acts as a bit shift count here - byte flagMask = 21; - blockPtr = rowPtr + pixelPtr; - for (byte y = 0; y < 4; y++) { - // reload flags at third row (iteration y == 2) - if (y == 2) { - colorFlags = colorFlagsB; - flagMask = 21; - } - - for (byte x = 0; x < 4; x++) { - pixel = colorTableIndex + ((colorFlags >> flagMask) & 0x07); - flagMask -= 3; - pixels[blockPtr++] = _colorOctets[pixel]; - } - - blockPtr += rowInc; - } - ADVANCE_BLOCK(); - } - break; - - // 16-color block encoding (every pixel is a different color) - case 0xE0: - numBlocks = (opcode & 0x0F) + 1; - - while (numBlocks--) { - blockPtr = rowPtr + pixelPtr; - for (byte y = 0; y < 4; y++) { - for (byte x = 0; x < 4; x++) - pixels[blockPtr++] = stream->readByte(); - - blockPtr += rowInc; - } - ADVANCE_BLOCK(); - } - break; - - case 0xF0: - warning("0xF0 opcode seen in SMC chunk (contact the developers)"); - break; - } - } - - return _surface; -} - -} // End of namespace Mohawk diff --git a/engines/mohawk/video/smc.h b/engines/mohawk/video/smc.h deleted file mode 100644 index c52226100e..0000000000 --- a/engines/mohawk/video/smc.h +++ /dev/null @@ -1,59 +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. - * - * $URL$ - * $Id$ - * - */ - -#ifndef MOHAWK_VIDEO_SMC_H -#define MOHAWK_VIDEO_SMC_H - -#include "graphics/video/codecs/codec.h" - -namespace Mohawk { - -enum { - CPAIR = 2, - CQUAD = 4, - COCTET = 8, - COLORS_PER_TABLE = 256 -}; - -class SMCDecoder : public Graphics::Codec { -public: - SMCDecoder(uint16 width, uint16 height); - ~SMCDecoder() { delete _surface; } - - Graphics::Surface *decodeImage(Common::SeekableReadStream *stream); - Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); } - -private: - Graphics::Surface *_surface; - - // SMC color tables - byte _colorPairs[COLORS_PER_TABLE * CPAIR]; - byte _colorQuads[COLORS_PER_TABLE * CQUAD]; - byte _colorOctets[COLORS_PER_TABLE * COCTET]; -}; - -} // End of namespace Mohawk - -#endif diff --git a/engines/parallaction/input.cpp b/engines/parallaction/input.cpp index 17254cf424..d6dd9feb19 100644 --- a/engines/parallaction/input.cpp +++ b/engines/parallaction/input.cpp @@ -329,7 +329,7 @@ bool Input::translateGameInput() { bool noWalk = z->_flags & kFlagsNoWalk; // check the explicit no-walk flag if (_gameType == GType_BRA) { // action performed on object marked for self-use do not need walk in BRA - noWalk |= z->_flags & kFlagsYourself; + noWalk |= ((z->_flags & kFlagsYourself) != 0); } if (noWalk) { diff --git a/engines/parallaction/parser_ns.cpp b/engines/parallaction/parser_ns.cpp index be72cf73a1..ff24a06ceb 100644 --- a/engines/parallaction/parser_ns.cpp +++ b/engines/parallaction/parser_ns.cpp @@ -286,6 +286,7 @@ void LocationParser_ns::parseAnimation(AnimationList &list, char *name) { debugC(5, kDebugParser, "parseAnimation(name: %s)", name); if (_vm->_location.findAnimation(name)) { + _zoneProg++; _script->skip("endanimation"); return; } @@ -1305,6 +1306,7 @@ void LocationParser_ns::parseZone(ZoneList &list, char *name) { debugC(5, kDebugParser, "parseZone(name: %s)", name); if (_vm->_location.findZone(name)) { + _zoneProg++; _script->skip("endzone"); return; } diff --git a/engines/saga/saga.cpp b/engines/saga/saga.cpp index 2911d9c451..ed8a9055ba 100644 --- a/engines/saga/saga.cpp +++ b/engines/saga/saga.cpp @@ -330,20 +330,6 @@ Common::Error SagaEngine::run() { syncSoundSettings(); - -#if 0 - // FIXME: Disabled this code for now. We want to get rid of OSystem::kFeatureAutoComputeDirtyRects - // and this is the last place to make use of it. We need to find out whether doing - // so causes any regressions. If it does, we can reenable it, if not, we can remove - // this code in 0.13.0. - - // FIXME: This is the ugly way of reducing redraw overhead. It works - // well for 320x200 but it's unclear how well it will work for - // 640x480. - if (getGameId() == GID_ITE) - _system->setFeatureState(OSystem::kFeatureAutoComputeDirtyRects, true); -#endif - int msec = 0; _previousTicks = _system->getMillis(); diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp index 540b7c84d8..2549be9403 100644 --- a/engines/sci/console.cpp +++ b/engines/sci/console.cpp @@ -47,6 +47,7 @@ #include "sci/graphics/cursor.h" #include "sci/graphics/screen.h" #include "sci/graphics/paint.h" +#include "sci/graphics/paint16.h" #include "sci/graphics/palette.h" #include "sci/parser/vocabulary.h" @@ -74,10 +75,10 @@ Console::Console(SciEngine *engine) : GUI::Debugger() { // Variables DVar_Register("sleeptime_factor", &g_debug_sleeptime_factor, DVAR_INT, 0); - DVar_Register("gc_interval", &script_gc_interval, DVAR_INT, 0); + DVar_Register("gc_interval", &engine->_gamestate->script_gc_interval, DVAR_INT, 0); DVar_Register("simulated_key", &g_debug_simulated_key, DVAR_INT, 0); DVar_Register("track_mouse_clicks", &g_debug_track_mouse_clicks, DVAR_BOOL, 0); - DVar_Register("script_abort_flag", &script_abort_flag, DVAR_INT, 0); + DVar_Register("script_abort_flag", &_engine->_gamestate->script_abort_flag, DVAR_INT, 0); // General DCmd_Register("help", WRAP_METHOD(Console, cmdHelp)); @@ -104,6 +105,7 @@ Console::Console(SciEngine *engine) : GUI::Debugger() { DCmd_Register("resource_types", WRAP_METHOD(Console, cmdResourceTypes)); DCmd_Register("list", WRAP_METHOD(Console, cmdList)); DCmd_Register("hexgrep", WRAP_METHOD(Console, cmdHexgrep)); + DCmd_Register("verify_scripts", WRAP_METHOD(Console, cmdVerifyScripts)); // Game DCmd_Register("save_game", WRAP_METHOD(Console, cmdSaveGame)); DCmd_Register("restore_game", WRAP_METHOD(Console, cmdRestoreGame)); @@ -276,8 +278,8 @@ void Console::postEnter() { #if 0 // Unused #define LOOKUP_SPECIES(species) (\ - (species >= 1000) ? species : *(s->_classtable[species].scriptposp) \ - + s->_classtable[species].class_offset) + (species >= 1000) ? species : *(s->_classTable[species].scriptposp) \ + + s->_classTable[species].class_offset) #endif bool Console::cmdHelp(int argc, const char **argv) { @@ -323,6 +325,7 @@ bool Console::cmdHelp(int argc, const char **argv) { DebugPrintf(" resource_types - Shows the valid resource types\n"); DebugPrintf(" list - Lists all the resources of a given type\n"); DebugPrintf(" hexgrep - Searches some resources for a particular sequence of bytes, represented as hexadecimal numbers\n"); + DebugPrintf(" verify_scripts - Performs sanity checks on SCI1.1-SCI2.1 game scripts (e.g. if they're up to 64KB in total)\n"); DebugPrintf("\n"); DebugPrintf("Game:\n"); DebugPrintf(" save_game - Saves the current game state to the hard disk\n"); @@ -421,10 +424,9 @@ const char *selector_name(EngineState *s, int selector) { bool Console::cmdGetVersion(int argc, const char **argv) { const char *viewTypeDesc[] = { "Unknown", "EGA", "VGA", "VGA SCI1.1", "Amiga" }; - EngineState *s = _engine->_gamestate; bool hasVocab997 = g_sci->getResMan()->testResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SELECTORS)) ? true : false; - DebugPrintf("Game ID: %s\n", s->_gameId.c_str()); + DebugPrintf("Game ID: %s\n", _engine->getGameID()); DebugPrintf("Emulated interpreter version: %s\n", getSciVersionDesc(getSciVersion())); DebugPrintf("\n"); DebugPrintf("Detected features:\n"); @@ -593,14 +595,14 @@ bool Console::cmdSetParseNodes(int argc, const char **argv) { } bool Console::cmdRegisters(int argc, const char **argv) { + EngineState *s = _engine->_gamestate; DebugPrintf("Current register values:\n"); - DebugPrintf("acc=%04x:%04x prev=%04x:%04x &rest=%x\n", PRINT_REG(_engine->_gamestate->r_acc), PRINT_REG(_engine->_gamestate->r_prev), scriptState.restAdjust); + DebugPrintf("acc=%04x:%04x prev=%04x:%04x &rest=%x\n", PRINT_REG(s->r_acc), PRINT_REG(s->r_prev), s->restAdjust); - if (!_engine->_gamestate->_executionStack.empty()) { - EngineState *s = _engine->_gamestate; // for PRINT_STK + if (!s->_executionStack.empty()) { DebugPrintf("pc=%04x:%04x obj=%04x:%04x fp=ST:%04x sp=ST:%04x\n", - PRINT_REG(scriptState.xs->addr.pc), PRINT_REG(scriptState.xs->objp), - (unsigned)(scriptState.xs->fp - s->stack_base), (unsigned)(scriptState.xs->sp - s->stack_base)); + PRINT_REG(s->xs->addr.pc), PRINT_REG(s->xs->objp), + (unsigned)(s->xs->fp - s->stack_base), (unsigned)(s->xs->sp - s->stack_base)); } else DebugPrintf("<no execution stack: pc,obj,fp omitted>\n"); @@ -811,6 +813,40 @@ bool Console::cmdHexgrep(int argc, const char **argv) { return true; } +bool Console::cmdVerifyScripts(int argc, const char **argv) { + if (getSciVersion() < SCI_VERSION_1_1) { + DebugPrintf("This script check is only meant for SCI1.1-SCI2.1 games\n"); + return true; + } + + Common::List<ResourceId> *resources = _engine->getResMan()->listResources(kResourceTypeScript); + sort(resources->begin(), resources->end(), ResourceIdLess()); + Common::List<ResourceId>::iterator itr = resources->begin(); + + DebugPrintf("%d SCI1.1-SCI2.1 scripts found, performing sanity checks...\n", resources->size()); + + Resource *script, *heap; + while (itr != resources->end()) { + script = _engine->getResMan()->findResource(*itr, false); + if (!script) + DebugPrintf("Error: script %d couldn't be loaded\n", itr->number); + + heap = _engine->getResMan()->findResource(*itr, false); + if (!heap) + DebugPrintf("Error: script %d doesn't have a corresponding heap\n", itr->number); + + if (script && heap && (script->size + heap->size > 65535)) + DebugPrintf("Error: script and heap %d together are larger than 64KB (%d bytes)\n", + itr->number, script->size + heap->size); + + ++itr; + } + + DebugPrintf("SCI1.1-SCI2.1 script check finished\n"); + + return true; +} + bool Console::cmdList(int argc, const char **argv) { if (argc < 2) { DebugPrintf("Lists all the resources of a given type\n"); @@ -921,35 +957,19 @@ bool Console::cmdRestoreGame(int argc, const char **argv) { } bool Console::cmdRestartGame(int argc, const char **argv) { - if (argc != 2) { - DebugPrintf("Restarts the game. There are two ways to restart a SCI game:\n"); - DebugPrintf("%s play - calls the game object's play() method\n", argv[0]); - DebugPrintf("%s replay - calls the replay() methody\n", argv[0]); - return true; - } - - if (!scumm_stricmp(argv[1], "play")) { - _engine->_gamestate->restarting_flags |= SCI_GAME_WAS_RESTARTED_AT_LEAST_ONCE; - } else if (!scumm_stricmp(argv[1], "replay")) { - _engine->_gamestate->restarting_flags &= ~SCI_GAME_WAS_RESTARTED_AT_LEAST_ONCE; - } else { - DebugPrintf("Invalid usage of %s\n", argv[0]); - return true; - } - _engine->_gamestate->restarting_flags |= SCI_GAME_IS_RESTARTING_NOW; - script_abort_flag = 1; + _engine->_gamestate->script_abort_flag = 1; return false; } bool Console::cmdClassTable(int argc, const char **argv) { DebugPrintf("Available classes:\n"); - for (uint i = 0; i < _engine->_gamestate->_segMan->_classtable.size(); i++) { - if (_engine->_gamestate->_segMan->_classtable[i].reg.segment) { + for (uint i = 0; i < _engine->_gamestate->_segMan->classTableSize(); i++) { + if (_engine->_gamestate->_segMan->_classTable[i].reg.segment) { DebugPrintf(" Class 0x%x at %04x:%04x (script 0x%x)\n", i, - PRINT_REG(_engine->_gamestate->_segMan->_classtable[i].reg), - _engine->_gamestate->_segMan->_classtable[i].script); + PRINT_REG(_engine->_gamestate->_segMan->_classTable[i].reg), + _engine->_gamestate->_segMan->_classTable[i].script); } } @@ -1129,7 +1149,11 @@ bool Console::cmdUndither(int argc, const char **argv) { bool flag = atoi(argv[1]) ? true : false; _engine->_gfxScreen->debugUnditherSetState(flag); - return false; + if (flag) + DebugPrintf("undithering ENABLED\n"); + else + DebugPrintf("undithering DISABLED\n"); + return true; } bool Console::cmdPicVisualize(int argc, const char **argv) { @@ -1141,7 +1165,16 @@ bool Console::cmdPicVisualize(int argc, const char **argv) { bool state = atoi(argv[1]) ? true : false; - return _engine->_gui->debugEGAdrawingVisualize(state); + if (_engine->_resMan->getViewType() == kViewEga) { + _engine->_gfxPaint16->debugSetEGAdrawingVisualize(state); + if (state) + DebugPrintf("picture visualization ENABLED\n"); + else + DebugPrintf("picture visualization DISABLED\n"); + } else { + DebugPrintf("picture visualization only available for EGA games\n"); + } + return true; } bool Console::cmdPlayVideo(int argc, const char **argv) { @@ -1248,13 +1281,13 @@ bool Console::segmentInfo(int nr) { case SEG_TYPE_SCRIPT: { Script *scr = (Script *)mobj; - DebugPrintf("script.%03d locked by %d, bufsize=%d (%x)\n", scr->_nr, scr->getLockers(), (uint)scr->_bufSize, (uint)scr->_bufSize); - if (scr->_exportTable) - DebugPrintf(" Exports: %4d at %d\n", scr->_numExports, (int)(((byte *)scr->_exportTable) - ((byte *)scr->_buf))); + DebugPrintf("script.%03d locked by %d, bufsize=%d (%x)\n", scr->_nr, scr->getLockers(), (uint)scr->getBufSize(), (uint)scr->getBufSize()); + if (scr->getExportTable()) + DebugPrintf(" Exports: %4d at %d\n", scr->getExportsNr(), (int)(((const byte *)scr->getExportTable()) - ((const byte *)scr->_buf))); else DebugPrintf(" Exports: none\n"); - DebugPrintf(" Synonyms: %4d\n", scr->_numSynonyms); + DebugPrintf(" Synonyms: %4d\n", scr->getSynonymsNr()); if (scr->_localsBlock) DebugPrintf(" Locals : %4d in segment 0x%x\n", scr->_localsBlock->_locals.size(), scr->_localsSegment); @@ -1268,7 +1301,7 @@ bool Console::segmentInfo(int nr) { for (it = scr->_objects.begin(); it != end; ++it) { DebugPrintf(" "); // Object header - Object *obj = _engine->_gamestate->_segMan->getObject(it->_value.getPos()); + const Object *obj = _engine->_gamestate->_segMan->getObject(it->_value.getPos()); if (obj) DebugPrintf("[%04x:%04x] %s : %3d vars, %3d methods\n", PRINT_REG(it->_value.getPos()), _engine->_gamestate->_segMan->getObjectName(it->_value.getPos()), @@ -1315,7 +1348,7 @@ bool Console::segmentInfo(int nr) { objpos.segment = nr; DebugPrintf(" [%04x] %s; copy of ", i, _engine->_gamestate->_segMan->getObjectName(objpos)); // Object header - Object *obj = _engine->_gamestate->_segMan->getObject(ct->_table[i].getPos()); + const Object *obj = _engine->_gamestate->_segMan->getObject(ct->_table[i].getPos()); if (obj) DebugPrintf("[%04x:%04x] %s : %3d vars, %3d methods\n", PRINT_REG(ct->_table[i].getPos()), _engine->_gamestate->_segMan->getObjectName(ct->_table[i].getPos()), @@ -1523,12 +1556,12 @@ bool Console::cmdToggleSound(int argc, const char **argv) { int handle = id.segment << 16 | id.offset; // frobnicate handle if (id.segment) { - SegManager *segMan = _engine->_gamestate->_segMan; // for PUT_SEL32V + SegManager *segMan = _engine->_gamestate->_segMan; // for writeSelectorValue _engine->_gamestate->_sound.sfx_song_set_status(handle, SOUND_STATUS_STOPPED); _engine->_gamestate->_sound.sfx_remove_song(handle); - PUT_SEL32V(segMan, id, SELECTOR(signal), SIGNAL_OFFSET); - PUT_SEL32V(segMan, id, SELECTOR(nodePtr), 0); - PUT_SEL32V(segMan, id, SELECTOR(handle), 0); + writeSelectorValue(segMan, id, SELECTOR(signal), SIGNAL_OFFSET); + writeSelectorValue(segMan, id, SELECTOR(nodePtr), 0); + writeSelectorValue(segMan, id, SELECTOR(handle), 0); } #else @@ -1733,14 +1766,15 @@ bool Console::cmdGCNormalize(int argc, const char **argv) { } bool Console::cmdVMVarlist(int argc, const char **argv) { + EngineState *s = _engine->_gamestate; const char *varnames[] = {"global", "local", "temp", "param"}; DebugPrintf("Addresses of variables in the VM:\n"); for (int i = 0; i < 4; i++) { - DebugPrintf("%s vars at %04x:%04x ", varnames[i], PRINT_REG(make_reg(scriptState.variables_seg[i], scriptState.variables[i] - scriptState.variables_base[i]))); - if (scriptState.variables_max) - DebugPrintf(" total %d", scriptState.variables_max[i]); + DebugPrintf("%s vars at %04x:%04x ", varnames[i], PRINT_REG(make_reg(s->variables_seg[i], s->variables[i] - s->variables_base[i]))); + if (s->variables_max) + DebugPrintf(" total %d", s->variables_max[i]); DebugPrintf("\n"); } @@ -1758,6 +1792,7 @@ bool Console::cmdVMVars(int argc, const char **argv) { return true; } + EngineState *s = _engine->_gamestate; const char *varnames[] = {"global", "local", "temp", "param"}; const char *varabbrev = "gltp"; const char *vartype_pre = strchr(varabbrev, *argv[1]); @@ -1796,17 +1831,17 @@ bool Console::cmdVMVars(int argc, const char **argv) { return true; } - if ((scriptState.variables_max) && (scriptState.variables_max[vartype] <= idx)) { - DebugPrintf("Max. index is %d (0x%x)\n", scriptState.variables_max[vartype], scriptState.variables_max[vartype]); + if ((s->variables_max) && (s->variables_max[vartype] <= idx)) { + DebugPrintf("Max. index is %d (0x%x)\n", s->variables_max[vartype], s->variables_max[vartype]); return true; } switch (argc) { case 3: - DebugPrintf("%s var %d == %04x:%04x\n", varnames[vartype], idx, PRINT_REG(scriptState.variables[vartype][idx])); + DebugPrintf("%s var %d == %04x:%04x\n", varnames[vartype], idx, PRINT_REG(s->variables[vartype][idx])); break; case 4: - if (parse_reg_t(_engine->_gamestate, argv[3], &scriptState.variables[vartype][idx], true)) { + if (parse_reg_t(_engine->_gamestate, argv[3], &s->variables[vartype][idx], true)) { DebugPrintf("Invalid value/address passed.\n"); DebugPrintf("Check the \"addresses\" command on how to use addresses\n"); DebugPrintf("Or pass a decimal or hexadecimal value directly (e.g. 12, 1Ah)\n"); @@ -2039,7 +2074,7 @@ bool Console::cmdViewObject(int argc, const char **argv) { bool Console::cmdViewActiveObject(int argc, const char **argv) { DebugPrintf("Information on the currently active object or class:\n"); - printObject(scriptState.xs->objp); + printObject(_engine->_gamestate->xs->objp); return true; } @@ -2052,7 +2087,7 @@ bool Console::cmdViewAccumulatorObject(int argc, const char **argv) { } bool Console::cmdScriptSteps(int argc, const char **argv) { - DebugPrintf("Number of executed SCI operations: %d\n", script_step_counter); + DebugPrintf("Number of executed SCI operations: %d\n", _engine->_gamestate->script_step_counter); return true; } @@ -2229,8 +2264,8 @@ bool Console::cmdDisassemble(int argc, const char **argv) { return true; } - Object *obj = _engine->_gamestate->_segMan->getObject(objAddr); - int selector_id = _engine->getKernel()->findSelector(argv[2]); + const Object *obj = _engine->_gamestate->_segMan->getObject(objAddr); + int selectorId = _engine->getKernel()->findSelector(argv[2]); reg_t addr; if (!obj) { @@ -2238,12 +2273,12 @@ bool Console::cmdDisassemble(int argc, const char **argv) { return true; } - if (selector_id < 0) { + if (selectorId < 0) { DebugPrintf("Not a valid selector name."); return true; } - if (lookup_selector(_engine->_gamestate->_segMan, objAddr, selector_id, NULL, &addr) != kSelectorMethod) { + if (lookupSelector(_engine->_gamestate->_segMan, objAddr, selectorId, NULL, &addr) != kSelectorMethod) { DebugPrintf("Not a method."); return true; } @@ -2323,20 +2358,20 @@ bool Console::cmdSend(int argc, const char **argv) { } const char *selector_name = argv[2]; - int selector_id = _engine->getKernel()->findSelector(selector_name); + int selectorId = _engine->getKernel()->findSelector(selector_name); - if (selector_id < 0) { + if (selectorId < 0) { DebugPrintf("Unknown selector: \"%s\"\n", selector_name); return true; } - Object *o = _engine->_gamestate->_segMan->getObject(object); + const Object *o = _engine->_gamestate->_segMan->getObject(object); if (o == NULL) { DebugPrintf("Address \"%04x:%04x\" is not an object\n", PRINT_REG(object)); return true; } - SelectorType selector_type = lookup_selector(_engine->_gamestate->_segMan, object, selector_id, 0, 0); + SelectorType selector_type = lookupSelector(_engine->_gamestate->_segMan, object, selectorId, 0, 0); if (selector_type == kSelectorNone) { DebugPrintf("Object does not support selector: \"%s\"\n", selector_name); @@ -2349,7 +2384,7 @@ bool Console::cmdSend(int argc, const char **argv) { // Create the data block for send_selecor() at the top of the stack: // [selector_number][argument_counter][arguments...] StackPtr stackframe = _engine->_gamestate->_executionStack.back().sp; - stackframe[0] = make_reg(0, selector_id); + stackframe[0] = make_reg(0, selectorId); stackframe[1] = make_reg(0, send_argc); for (int i = 0; i < send_argc; i++) { if (parse_reg_t(_engine->_gamestate, argv[3+i], &stackframe[2+i], false)) { @@ -2696,7 +2731,7 @@ bool Console::cmdQuit(int argc, const char **argv) { if (!scumm_stricmp(argv[1], "game")) { // Quit gracefully - script_abort_flag = 1; // Terminate VM + _engine->_gamestate->script_abort_flag = 1; // Terminate VM g_debugState.seeking = kDebugSeekNothing; g_debugState.runningStep = 0; @@ -3021,8 +3056,8 @@ int Console::printNode(reg_t addr) { int Console::printObject(reg_t pos) { EngineState *s = _engine->_gamestate; // for the several defines in this function - Object *obj = s->_segMan->getObject(pos); - Object *var_container = obj; + const Object *obj = s->_segMan->getObject(pos); + const Object *var_container = obj; uint i; if (!obj) { @@ -3034,7 +3069,7 @@ int Console::printObject(reg_t pos) { DebugPrintf("[%04x:%04x] %s : %3d vars, %3d methods\n", PRINT_REG(pos), s->_segMan->getObjectName(pos), obj->getVarCount(), obj->getMethodCount()); - if (!(obj->getInfoSelector().offset & SCRIPT_INFO_CLASS)) + if (!obj->isClass()) var_container = s->_segMan->getObject(obj->getSuperClassSelector()); DebugPrintf(" -- member variables:\n"); for (i = 0; (uint)i < obj->getVarCount(); i++) { @@ -3051,7 +3086,7 @@ int Console::printObject(reg_t pos) { if (!val.segment) DebugPrintf(" (%d)", val.offset); - Object *ref = s->_segMan->getObject(val); + const Object *ref = s->_segMan->getObject(val); if (ref) DebugPrintf(" (%s)", s->_segMan->getObjectName(val)); @@ -3063,7 +3098,7 @@ int Console::printObject(reg_t pos) { DebugPrintf(" [%03x] %s = %04x:%04x\n", obj->getFuncSelector(i), selector_name(s, obj->getFuncSelector(i)), PRINT_REG(fptr)); } if (s->_segMan->_heap[pos.segment]->getType() == SEG_TYPE_SCRIPT) - DebugPrintf("\nOwner script:\t%d\n", s->_segMan->getScript(pos.segment)->_nr); + DebugPrintf("\nOwner script: %d\n", s->_segMan->getScript(pos.segment)->_nr); return 0; } diff --git a/engines/sci/console.h b/engines/sci/console.h index c88795ae26..2b13e03ef6 100644 --- a/engines/sci/console.h +++ b/engines/sci/console.h @@ -73,6 +73,7 @@ private: bool cmdResourceTypes(int argc, const char **argv); bool cmdList(int argc, const char **argv); bool cmdHexgrep(int argc, const char **argv); + bool cmdVerifyScripts(int argc, const char **argv); // Game bool cmdSaveGame(int argc, const char **argv); bool cmdRestoreGame(int argc, const char **argv); diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp index beea025aea..1ccfc6bf02 100644 --- a/engines/sci/detection.cpp +++ b/engines/sci/detection.cpp @@ -120,6 +120,154 @@ static const PlainGameDescriptor SciGameTitles[] = { {0, 0} }; +struct OldNewIdTableEntry { + const char *oldId; + const char *newId; + SciVersion version; +}; + +static const OldNewIdTableEntry s_oldNewTable[] = { + { "arthur", "camelot", SCI_VERSION_NONE }, + { "brain", "castlebrain", SCI_VERSION_1_MIDDLE }, // Amiga + { "brain", "castlebrain", SCI_VERSION_1_LATE }, + { "demo", "christmas1988", SCI_VERSION_NONE }, + { "card", "christmas1990", SCI_VERSION_1_EARLY, }, + { "card", "christmas1992", SCI_VERSION_1_1 }, + { "RH Budget", "cnick-longbow", SCI_VERSION_NONE }, + // iceman is the same + { "icedemo", "iceman", SCI_VERSION_NONE }, + // longbow is the same + { "eco", "ecoquest", SCI_VERSION_NONE }, + { "eco2", "ecoquest2", SCI_VERSION_NONE }, // EcoQuest 2 demo + { "rain", "ecoquest2", SCI_VERSION_NONE }, // EcoQuest 2 full + { "tales", "fairytales", SCI_VERSION_NONE }, + { "fp", "freddypharkas", SCI_VERSION_NONE }, + { "emc", "funseeker", SCI_VERSION_NONE }, + { "gk", "gk1", SCI_VERSION_NONE }, + // gk2 is the same + { "hoyledemo", "hoyle1", SCI_VERSION_NONE }, + { "cardgames", "hoyle1", SCI_VERSION_NONE }, + { "solitare", "hoyle2", SCI_VERSION_NONE }, + // hoyle3 is the same + // hoyle4 is the same + { "brain", "islandbrain", SCI_VERSION_1_1 }, + { "demo000", "kq1sci", SCI_VERSION_NONE }, + { "kq1", "kq1sci", SCI_VERSION_NONE }, + { "kq4", "kq4sci", SCI_VERSION_NONE }, + // kq5 is the same + // kq6 is the same + // kq7 is the same + { "mm1", "laurabow", SCI_VERSION_NONE }, + { "cb1", "laurabow", SCI_VERSION_NONE }, + { "lb2", "laurabow2", SCI_VERSION_NONE }, + { "rh", "longbow", SCI_VERSION_NONE }, + { "ll1", "lsl1sci", SCI_VERSION_NONE }, + { "lsl1", "lsl1sci", SCI_VERSION_NONE }, + // lsl2 is the same + { "lsl3", "lsl3", SCI_VERSION_NONE }, + { "ll5", "lsl5", SCI_VERSION_NONE }, + // lsl5 is the same + // lsl6 is the same + { "mg", "mothergoose", SCI_VERSION_NONE }, + { "twisty", "pepper", SCI_VERSION_NONE }, + { "scary", "phantasmagoria", SCI_VERSION_NONE }, + // TODO: distinguish the full version of Phantasmagoria from the demo + { "pq1", "pq1sci", SCI_VERSION_NONE }, + { "pq", "pq2", SCI_VERSION_NONE }, + // pq3 is the same + // pq4 is the same + { "hq", "qfg1", SCI_VERSION_NONE }, // QFG1 SCI0/EGA + { "glory", "qfg1", SCI_VERSION_0_LATE }, // QFG1 SCI0/EGA + { "trial", "qfg2", SCI_VERSION_NONE }, + { "hq2demo", "qfg2", SCI_VERSION_NONE }, + // rama is the same + // TODO: distinguish the full version of rama from the demo + { "thegame", "slater", SCI_VERSION_NONE }, + { "sq1demo", "sq1sci", SCI_VERSION_NONE }, + { "sq1", "sq1sci", SCI_VERSION_NONE }, + // sq3 is the same + // sq4 is the same + // sq5 is the same + // sq6 is the same + // TODO: distinguish the full version of SQ6 from the demo + // torin is the same + + + // TODO: SCI3 IDs + + { "", "", SCI_VERSION_NONE } +}; + +Common::String convertSierraGameId(Common::String sierraId, uint32 *gameFlags, ResourceManager *resMan) { + // Convert the id to lower case, so that we match all upper/lower case variants. + sierraId.toLowercase(); + + // If the game has less than the expected scripts, it's a demo + uint32 demoThreshold = 100; + // ...but there are some exceptions + if (sierraId == "brain" || sierraId == "lsl1" || + sierraId == "mg" || sierraId == "pq" || + sierraId == "jones" || + sierraId == "cardgames" || sierraId == "solitare" || + sierraId == "hoyle3" || sierraId == "hoyle4") + demoThreshold = 40; + if (sierraId == "fp" || sierraId == "gk" || sierraId == "pq4") + demoThreshold = 150; + + Common::List<ResourceId> *resources = resMan->listResources(kResourceTypeScript, -1); + if (resources->size() < demoThreshold) { + *gameFlags |= ADGF_DEMO; + + // Crazy Nick's Picks + if (sierraId == "lsl1" && resources->size() == 34) + return "cnick-lsl"; + if (sierraId == "sq4" && resources->size() == 34) + return "cnick-sq"; + + // TODO: cnick-kq, cnick-laurabow and cnick-longbow (their resources can't be read) + + // Handle Astrochicken 1 (SQ3) and 2 (SQ4) + if (sierraId == "sq3" && resources->size() == 20) + return "astrochicken"; + if (sierraId == "sq4") + return "msastrochicken"; + } + + if (sierraId == "torin" && resources->size() == 226) // Torin's Passage demo + *gameFlags |= ADGF_DEMO; + + for (const OldNewIdTableEntry *cur = s_oldNewTable; cur->oldId[0]; ++cur) { + if (sierraId == cur->oldId) { + // Distinguish same IDs from the SCI version + if (cur->version != SCI_VERSION_NONE && cur->version != getSciVersion()) + continue; + + return cur->newId; + } + } + + if (sierraId == "glory") { + // This could either be qfg1 VGA, qfg3 or qfg4 demo (all SCI1.1), + // or qfg4 full (SCI2) + // qfg1 VGA doesn't have view 1 + if (!resMan->testResource(ResourceId(kResourceTypeView, 1))) + return "qfg1"; + + // qfg4 full is SCI2 + if (getSciVersion() == SCI_VERSION_2) + return "qfg4"; + + // qfg4 demo has less than 50 scripts + if (resources->size() < 50) + return "qfg4"; + + // Otherwise it's qfg3 + return "qfg3"; + } + + return sierraId; +} + #include "sci/detection_tables.h" /** @@ -205,42 +353,6 @@ Common::Language charToScummVMLanguage(const char c) { } } -#define READ_UINT16(ptr) (!resMan->isSci11Mac() ? READ_LE_UINT16(ptr) : READ_BE_UINT16(ptr)) - -// Finds the internal ID of the current game from script 0 -Common::String getSierraGameId(ResourceManager *resMan) { - Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, 0), false); - // In SCI0-SCI1, the heap is embedded in the script. In SCI1.1+, it's separated - Resource *heap = 0; - byte *seeker = 0; - - // Seek to the name selector of the first export - if (getSciVersion() < SCI_VERSION_1_1) { - const int nameSelector = 3; - int extraSci0EarlyBytes = (getSciVersion() == SCI_VERSION_0_EARLY) ? 2 : 0; - byte *exportPtr = script->data + extraSci0EarlyBytes + 4 + 2; - seeker = script->data + READ_UINT16(script->data + READ_UINT16(exportPtr) + nameSelector * 2); - } else { - const int nameSelector = 5 + 3; - heap = resMan->findResource(ResourceId(kResourceTypeHeap, 0), false); - byte *exportPtr = script->data + 4 + 2 + 2; - seeker = heap->data + READ_UINT16(heap->data + READ_UINT16(exportPtr) + nameSelector * 2); - } - - char sierraId[20]; - int i = 0; - byte curChar = 0; - - do { - curChar = *(seeker + i); - sierraId[i++] = curChar; - } while (curChar != 0); - - return sierraId; -} - -#undef READ_UINT16 - const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fslist) const { bool foundResMap = false; bool foundRes000 = false; @@ -261,19 +373,6 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl filename.toLowercase(); if (filename.contains("resource.map") || filename.contains("resmap.00") || filename.contains("Data1")) { - // HACK: resource.map is located in the same directory as the other resource files, - // therefore add the directory here, so that the game files can be opened later on - // We now add the parent directory temporary to our SearchMan so the engine code - // used in the detection can access all files via Common::File without any problems. - // In all branches returning from this function, we need to have a call to - // SearchMan.remove to remove it from the default directory pool again. - // - // A proper solution to remove this hack would be to have the code, which is needed - // for detection, to operate on Stream objects, so they can be easily called from - // the detection code. This might be easily to achieve through refactoring the - // code needed for detection. - assert(!SearchMan.hasArchive("SCI_detection")); - SearchMan.addDirectory("SCI_detection", file->getParent()); foundResMap = true; } @@ -317,7 +416,6 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl // If these files aren't found, it can't be SCI if (!foundResMap && !foundRes000) { - SearchMan.remove("SCI_detection"); return 0; } @@ -325,11 +423,10 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl ViewType gameViews = resMan->getViewType(); // Have we identified the game views? If not, stop here + // Can't be SCI (or unsupported SCI views). Pinball Creep by sierra also uses resource.map/resource.000 files + // but doesnt share sci format at all, if we dont return 0 here we will detect this game as SCI if (gameViews == kViewUnknown) { - SearchMan.remove("SCI_detection"); delete resMan; - // Can't be SCI (or unsupported SCI views). Pinball Creep by sierra also uses resource.map/resource.000 files - // but doesnt share sci format at all, if we dont return 0 here we will detect this game as SCI return 0; } @@ -337,7 +434,6 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl // Is SCI32 compiled in? If not, and this is a SCI32 game, // stop here if (getSciVersion() >= SCI_VERSION_2) { - SearchMan.remove("SCI_detection"); delete resMan; return (const ADGameDescription *)&s_fallbackDesc; } @@ -352,7 +448,15 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl s_fallbackDesc.platform = Common::kPlatformAmiga; // Determine the game id - Common::String gameId = convertSierraGameId(getSierraGameId(resMan).c_str(), &s_fallbackDesc.flags, resMan); + Common::String sierraGameId = resMan->findSierraGameId(); + + // If we don't have a game id, the game is not SCI + if (sierraGameId.empty()) { + delete resMan; + return 0; + } + + Common::String gameId = convertSierraGameId(sierraGameId, &s_fallbackDesc.flags, resMan); strncpy(s_fallbackGameIdBuf, gameId.c_str(), sizeof(s_fallbackGameIdBuf) - 1); s_fallbackGameIdBuf[sizeof(s_fallbackGameIdBuf) - 1] = 0; // Make sure string is NULL terminated s_fallbackDesc.gameid = s_fallbackGameIdBuf; @@ -386,7 +490,6 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl } } - delete resMan; // Fill in extras field if (!strcmp(s_fallbackDesc.gameid, "lsl1sci") || @@ -394,14 +497,14 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl !strcmp(s_fallbackDesc.gameid, "sq1sci")) s_fallbackDesc.extra = "VGA Remake"; - if (!strcmp(s_fallbackDesc.gameid, "qfg1") && !Common::File::exists("resource.001")) + if (!strcmp(s_fallbackDesc.gameid, "qfg1") && getSciVersion() == SCI_VERSION_1_1) s_fallbackDesc.extra = "VGA Remake"; // Add "demo" to the description for demos if (s_fallbackDesc.flags & ADGF_DEMO) s_fallbackDesc.extra = "demo"; - SearchMan.remove("SCI_detection"); + delete resMan; return (const ADGameDescription *)&s_fallbackDesc; } @@ -427,8 +530,8 @@ bool SciMetaEngine::hasFeature(MetaEngineFeature f) const { bool SciEngine::hasFeature(EngineFeature f) const { return //(f == kSupportsRTL) || - (f == kSupportsLoadingDuringRuntime); - //(f == kSupportsSavingDuringRuntime); + (f == kSupportsLoadingDuringRuntime) || + (f == kSupportsSavingDuringRuntime); } SaveStateList SciMetaEngine::listSaves(const char *target) const { diff --git a/engines/sci/engine/features.cpp b/engines/sci/engine/features.cpp index e51867332a..48f7c2d64f 100644 --- a/engines/sci/engine/features.cpp +++ b/engines/sci/engine/features.cpp @@ -56,7 +56,7 @@ reg_t GameFeatures::getDetectionAddr(const Common::String &objName, Selector slc } if (methodNum == -1) { - if (lookup_selector(_segMan, objAddr, slc, NULL, &addr) != kSelectorMethod) { + if (lookupSelector(_segMan, objAddr, slc, NULL, &addr) != kSelectorMethod) { warning("getDetectionAddr: target selector is not a method of object %s", objName.c_str()); return NULL_REG; } @@ -87,7 +87,7 @@ bool GameFeatures::autoDetectSoundType() { opcode = extOpcode >> 1; // Check for end of script - if (opcode == op_ret || offset >= script->_bufSize) + if (opcode == op_ret || offset >= script->getBufSize()) break; // The play method of the Sound object pushes the DoSound command @@ -189,7 +189,7 @@ SciVersion GameFeatures::detectSetCursorType() { } // Now we check what the number variable holds in the handCursor object. - uint16 number = GET_SEL32V(_segMan, objAddr, SELECTOR(number)); + uint16 number = readSelectorValue(_segMan, objAddr, SELECTOR(number)); // If the number is 0, it uses views and therefore the SCI1.1 kSetCursor semantics, // otherwise it uses the SCI0 early kSetCursor semantics. @@ -223,7 +223,7 @@ bool GameFeatures::autoDetectLofsType(int methodNum) { opcode = extOpcode >> 1; // Check for end of script - if (opcode == op_ret || offset >= script->_bufSize) + if (opcode == op_ret || offset >= script->getBufSize()) break; if (opcode == op_lofsa || opcode == op_lofss) { @@ -231,13 +231,13 @@ bool GameFeatures::autoDetectLofsType(int methodNum) { uint16 lofs = opparams[0]; // Check for going out of bounds when interpreting as abs/rel - if (lofs >= script->_bufSize) + if (lofs >= script->getBufSize()) _lofsType = SCI_VERSION_0_EARLY; if ((signed)offset + (int16)lofs < 0) _lofsType = SCI_VERSION_1_MIDDLE; - if ((signed)offset + (int16)lofs >= (signed)script->_bufSize) + if ((signed)offset + (int16)lofs >= (signed)script->getBufSize()) _lofsType = SCI_VERSION_1_MIDDLE; if (_lofsType != SCI_VERSION_NONE) @@ -266,7 +266,7 @@ SciVersion GameFeatures::detectLofsType() { // Find a function of the game object which invokes lofsa/lofss reg_t gameClass = _segMan->findObjectByName("Game"); - Object *obj = _segMan->getObject(gameClass); + const Object *obj = _segMan->getObject(gameClass); bool found = false; for (uint m = 0; m < obj->getMethodCount(); m++) { @@ -309,7 +309,7 @@ bool GameFeatures::autoDetectGfxFunctionsType(int methodNum) { opcode = extOpcode >> 1; // Check for end of script - if (opcode == op_ret || offset >= script->_bufSize) + if (opcode == op_ret || offset >= script->getBufSize()) break; if (opcode == op_callk) { @@ -332,18 +332,47 @@ bool GameFeatures::autoDetectGfxFunctionsType(int methodNum) { SciVersion GameFeatures::detectGfxFunctionsType() { if (_gfxFunctionsType == SCI_VERSION_NONE) { - // This detection only works (and is only needed) for SCI0 games - if (getSciVersion() >= SCI_VERSION_01) { + if (getSciVersion() == SCI_VERSION_0_EARLY) { + // Old SCI0 games always used old graphics functions + _gfxFunctionsType = SCI_VERSION_0_EARLY; + } else if (getSciVersion() >= SCI_VERSION_01) { + // SCI01 and newer games always used new graphics functions _gfxFunctionsType = SCI_VERSION_0_LATE; - } else if (getSciVersion() > SCI_VERSION_0_EARLY) { + } else { // SCI0 late // Check if the game is using an overlay - bool found = false; + bool searchRoomObj = false; + reg_t rmObjAddr = _segMan->findObjectByName("Rm"); + + if (_kernel->_selectorCache.overlay != -1) { + // The game has an overlay selector, check how it calls kDrawPicto determine + // the graphics functions type used + if (lookupSelector(_segMan, rmObjAddr, _kernel->_selectorCache.overlay, NULL, NULL) == kSelectorMethod) { + if (!autoDetectGfxFunctionsType()) { + warning("Graphics functions detection failed, taking an educated guess"); + + // Try detecting the graphics function types from the existence of the motionCue + // selector (which is a bit of a hack) + if (_kernel->findSelector("motionCue") != -1) + _gfxFunctionsType = SCI_VERSION_0_LATE; + else + _gfxFunctionsType = SCI_VERSION_0_EARLY; + } + } else { + // The game has an overlay selector, but it's not a method of the Rm object + // (like in Hoyle 1 and 2), so search for other methods + searchRoomObj = true; + } + } else { + // The game doesn't have an overlay selector, so search for it manually + searchRoomObj = true; + } - if (_kernel->_selectorCache.overlay == -1) { - // No overlay selector found, check if any method of the Rm object - // is calling kDrawPic, as the overlay selector might be missing in demos + if (searchRoomObj) { + // If requested, check if any method of the Rm object is calling kDrawPic, + // as the overlay selector might be missing in demos + bool found = false; - Object *obj = _segMan->getObject(_segMan->findObjectByName("Rm")); + const Object *obj = _segMan->getObject(rmObjAddr); for (uint m = 0; m < obj->getMethodCount(); m++) { found = autoDetectGfxFunctionsType(m); if (found) @@ -351,30 +380,11 @@ SciVersion GameFeatures::detectGfxFunctionsType() { } if (!found) { - // No overlay selector found, therefore the game is definitely - // using old graphics functions + // No method of the Rm object is calling kDrawPic, thus the game + // doesn't have overlays and is using older graphics functions _gfxFunctionsType = SCI_VERSION_0_EARLY; } - } else { // _kernel->_selectorCache.overlay != -1 - // An in-between case: The game does not have a shiftParser - // selector, but it does have an overlay selector, so it uses an - // overlay. Therefore, check it to see how it calls kDrawPic to - // determine the graphics functions type used - - if (!autoDetectGfxFunctionsType()) { - warning("Graphics functions detection failed, taking an educated guess"); - - // Try detecting the graphics function types from the existence of the motionCue - // selector (which is a bit of a hack) - if (_kernel->findSelector("motionCue") != -1) - _gfxFunctionsType = SCI_VERSION_0_LATE; - else - _gfxFunctionsType = SCI_VERSION_0_EARLY; - } } - } else { // (getSciVersion() == SCI_VERSION_0_EARLY) - // Old SCI0 games always used old graphics functions - _gfxFunctionsType = SCI_VERSION_0_EARLY; } debugC(1, kDebugLevelVM, "Detected graphics functions type: %s", getSciVersionDesc(_gfxFunctionsType)); @@ -402,7 +412,7 @@ bool GameFeatures::autoDetectSci21KernelType() { opcode = extOpcode >> 1; // Check for end of script - if (opcode == op_ret || offset >= script->_bufSize) + if (opcode == op_ret || offset >= script->getBufSize()) break; if (opcode == op_callk) { @@ -455,7 +465,7 @@ bool GameFeatures::autoDetectMoveCountType() { opcode = extOpcode >> 1; // Check for end of script - if (opcode == op_ret || offset >= script->_bufSize) + if (opcode == op_ret || offset >= script->getBufSize()) break; if (opcode == op_callk) { diff --git a/engines/sci/engine/game.cpp b/engines/sci/engine/game.cpp index 4ac2a22531..bc10099e52 100644 --- a/engines/sci/engine/game.cpp +++ b/engines/sci/engine/game.cpp @@ -41,141 +41,6 @@ namespace Sci { -struct OldNewIdTableEntry { - const char *oldId; - const char *newId; - SciVersion version; -}; - -static const OldNewIdTableEntry s_oldNewTable[] = { - { "arthur", "camelot", SCI_VERSION_NONE }, - { "brain", "castlebrain", SCI_VERSION_1_MIDDLE }, // Amiga - { "brain", "castlebrain", SCI_VERSION_1_LATE }, - { "demo", "christmas1988", SCI_VERSION_NONE }, - { "card", "christmas1990", SCI_VERSION_1_EARLY, }, - { "card", "christmas1992", SCI_VERSION_1_1 }, - { "RH Budget", "cnick-longbow", SCI_VERSION_NONE }, - // iceman is the same - { "icedemo", "iceman", SCI_VERSION_NONE }, - // longbow is the same - { "eco", "ecoquest", SCI_VERSION_NONE }, - { "eco2", "ecoquest2", SCI_VERSION_NONE }, // EcoQuest 2 demo - { "rain", "ecoquest2", SCI_VERSION_NONE }, // EcoQuest 2 full - { "fp", "freddypharkas", SCI_VERSION_NONE }, - { "emc", "funseeker", SCI_VERSION_NONE }, - { "gk", "gk1", SCI_VERSION_NONE }, - { "hoyledemo", "hoyle1", SCI_VERSION_NONE }, - { "cardgames", "hoyle1", SCI_VERSION_NONE }, - { "solitare", "hoyle2", SCI_VERSION_NONE }, - // hoyle3 is the same - // hoyle4 is the same - { "brain", "islandbrain", SCI_VERSION_1_1 }, - { "demo000", "kq1sci", SCI_VERSION_NONE }, - { "kq1", "kq1sci", SCI_VERSION_NONE }, - { "kq4", "kq4sci", SCI_VERSION_NONE }, - { "mm1", "laurabow", SCI_VERSION_NONE }, - { "cb1", "laurabow", SCI_VERSION_NONE }, - { "lb2", "laurabow2", SCI_VERSION_NONE }, - { "rh", "longbow", SCI_VERSION_NONE }, - { "ll1", "lsl1sci", SCI_VERSION_NONE }, - { "lsl1", "lsl1sci", SCI_VERSION_NONE }, - // lsl2 is the same - { "lsl3", "lsl3", SCI_VERSION_NONE }, - { "ll5", "lsl5", SCI_VERSION_NONE }, - // lsl5 is the same - // lsl6 is the same - { "mg", "mothergoose", SCI_VERSION_NONE }, - { "twisty", "pepper", SCI_VERSION_NONE }, - { "pq1", "pq1sci", SCI_VERSION_NONE }, - { "pq", "pq2", SCI_VERSION_NONE }, - // pq3 is the same - // pq4 is the same - { "tales", "fairytales", SCI_VERSION_NONE }, - { "hq", "qfg1", SCI_VERSION_NONE }, // QFG1 SCI0/EGA - { "glory", "qfg1", SCI_VERSION_0_LATE }, // QFG1 SCI0/EGA - { "trial", "qfg2", SCI_VERSION_NONE }, - { "hq2demo", "qfg2", SCI_VERSION_NONE }, - { "thegame", "slater", SCI_VERSION_NONE }, - { "sq1demo", "sq1sci", SCI_VERSION_NONE }, - { "sq1", "sq1sci", SCI_VERSION_NONE }, - // sq3 is the same - // sq4 is the same - // sq5 is the same - // torin is the same - - // TODO: SCI2.1, SCI3 IDs - - { "", "", SCI_VERSION_NONE } -}; - -Common::String convertSierraGameId(const char *gameId, uint32 *gameFlags, ResourceManager *resMan) { - // Convert the id to lower case, so that we match all upper/lower case variants. - Common::String sierraId = gameId; - sierraId.toLowercase(); - - // If the game has less than the expected scripts, it's a demo - uint32 demoThreshold = 100; - // ...but there are some exceptions - if (sierraId == "brain" || sierraId == "lsl1" || - sierraId == "mg" || sierraId == "pq" || - sierraId == "jones" || - sierraId == "cardgames" || sierraId == "solitare" || - sierraId == "hoyle3" || sierraId == "hoyle4") - demoThreshold = 40; - if (sierraId == "fp" || sierraId == "gk" || sierraId == "pq4") - demoThreshold = 150; - - Common::List<ResourceId> *resources = resMan->listResources(kResourceTypeScript, -1); - if (resources->size() < demoThreshold) { - *gameFlags |= ADGF_DEMO; - - // Crazy Nick's Picks - if (sierraId == "lsl1" && resources->size() == 34) - return "cnick-lsl"; - if (sierraId == "sq4" && resources->size() == 34) - return "cnick-sq"; - - // TODO: cnick-kq, cnick-laurabow and cnick-longbow (their resources can't be read) - - // Handle Astrochicken 1 (SQ3) and 2 (SQ4) - if (sierraId == "sq3" && resources->size() == 20) - return "astrochicken"; - if (sierraId == "sq4") - return "msastrochicken"; - } - - for (const OldNewIdTableEntry *cur = s_oldNewTable; cur->oldId[0]; ++cur) { - if (sierraId == cur->oldId) { - // Distinguish same IDs from the SCI version - if (cur->version != SCI_VERSION_NONE && cur->version != getSciVersion()) - continue; - - return cur->newId; - } - } - - if (sierraId == "glory") { - // This could either be qfg1 VGA, qfg3 or qfg4 demo (all SCI1.1), - // or qfg4 full (SCI2) - // qfg1 VGA doesn't have view 1 - if (!resMan->testResource(ResourceId(kResourceTypeView, 1))) - return "qfg1"; - - // qfg4 full is SCI2 - if (getSciVersion() == SCI_VERSION_2) - return "qfg4"; - - // qfg4 demo has less than 50 scripts - if (resources->size() < 50) - return "qfg4"; - - // Otherwise it's qfg3 - return "qfg3"; - } - - return sierraId; -} - #ifdef USE_OLD_MUSIC_FUNCTIONS int game_init_sound(EngineState *s, int sound_flags, SciVersion soundVersion) { if (getSciVersion() > SCI_VERSION_0_LATE) @@ -202,13 +67,7 @@ int script_init_engine(EngineState *s) { s->script_000 = s->_segMan->getScript(script_000_segment); - s->sys_strings = s->_segMan->allocateSysStrings(&s->sys_strings_segment); - - // Allocate static buffer for savegame and CWD directories - SystemString *str = &s->sys_strings->_strings[SYS_STRING_SAVEDIR]; - str->_name = "savedir"; - str->_maxSize = MAX_SAVE_DIR_SIZE; - str->_value = (char *)calloc(MAX_SAVE_DIR_SIZE, sizeof(char)); + s->_segMan->initSysStrings(); s->r_acc = s->r_prev = NULL_REG; s->restAdjust = 0; @@ -240,35 +99,26 @@ int game_init(EngineState *s) { return 1; } - if (s->_voc) { - s->_voc->parserIsValid = false; // Invalidate parser - s->_voc->parser_event = NULL_REG; // Invalidate parser event - s->_voc->parser_base = make_reg(s->sys_strings_segment, SYS_STRING_PARSER_BASE); + // Reset parser + Vocabulary *voc = g_sci->getVocabulary(); + if (voc) { + voc->parserIsValid = false; // Invalidate parser + voc->parser_event = NULL_REG; // Invalidate parser event + voc->parser_base = make_reg(s->_segMan->getSysStringsSegment(), SYS_STRING_PARSER_BASE); } // Initialize menu TODO: Actually this should be another init() if (g_sci->_gfxMenu) g_sci->_gfxMenu->reset(); - s->successor = NULL; // No successor - - SystemString *str = &s->sys_strings->_strings[SYS_STRING_PARSER_BASE]; - str->_name = "parser-base"; - str->_maxSize = MAX_PARSER_BASE; - str->_value = (char *)calloc(MAX_PARSER_BASE, sizeof(char)); + s->restoring = false; s->game_start_time = g_system->getMillis(); s->last_wait_time = s->game_start_time; srand(g_system->getMillis()); // Initialize random number generator -// script_dissect(0, s->_selectorNames); - // The first entry in the export table of script 0 points to the game object - s->_gameObj = s->_segMan->lookupScriptExport(0, 0); - uint32 gameFlags = 0; // unused - s->_gameId = convertSierraGameId(s->_segMan->getObjectName(s->_gameObj), &gameFlags, g_sci->getResMan()); - - debug(2, " \"%s\" at %04x:%04x", s->_gameId.c_str(), PRINT_REG(s->_gameObj)); + s->_gameObj = g_sci->getResMan()->findGameObject(); #ifdef USE_OLD_MUSIC_FUNCTIONS if (s->sfx_init_flags & SFX_STATE_FLAG_NOSOUND) @@ -284,9 +134,8 @@ int game_init(EngineState *s) { } int game_exit(EngineState *s) { - s->_executionStack.clear(); - - if (!s->successor) { + if (!s->restoring) { + s->_executionStack.clear(); #ifdef USE_OLD_MUSIC_FUNCTIONS s->_sound.sfx_exit(); // Reinit because some other code depends on having a valid state diff --git a/engines/sci/engine/kernel.cpp b/engines/sci/engine/kernel.cpp index 49266f3a18..6ebee2dfbd 100644 --- a/engines/sci/engine/kernel.cpp +++ b/engines/sci/engine/kernel.cpp @@ -332,11 +332,12 @@ SciKernelFunction kfunct_mappers[] = { DEFUN("DoSync", kDoSync, ".*"), DEFUN("MemorySegment", kMemorySegment, "iri*"), DEFUN("Intersections", kIntersections, "iiiiriiiri"), + DEFUN("MergePoly", kMergePoly, "rli"), DEFUN("ResCheck", kResCheck, "iii*"), DEFUN("SetQuitStr", kSetQuitStr, "r"), DEFUN("ShowMovie", kShowMovie, ".*"), DEFUN("SetVideoMode", kSetVideoMode, "i"), - DEFUN("Platform", kPlatform, "i.*"), + DEFUN("Platform", kPlatform, ".*"), DEFUN("TextColors", kTextColors, ".*"), DEFUN("TextFonts", kTextFonts, ".*"), DEFUN("Portrait", kPortrait, ".*"), @@ -362,7 +363,7 @@ SciKernelFunction kfunct_mappers[] = { DEFUN("ListIndexOf", kListIndexOf, "lZo"), DEFUN("OnMe", kOnMe, "iio.*"), DEFUN("InPolygon", kInPolygon, "iio"), - DEFUN("CreateTextBitmap", kCreateTextBitmap, "iiio"), + DEFUN("CreateTextBitmap", kCreateTextBitmap, "i.*"), // SCI2.1 Kernel Functions DEFUN("Save", kSave, ".*"), @@ -382,7 +383,6 @@ SciKernelFunction kfunct_mappers[] = { DEFUN("ShiftScreen", kShiftScreen, ".*"), DEFUN("ListOps", kListOps, ".*"), DEFUN("ATan", kATan, ".*"), - DEFUN("MergePoly", kMergePoly, ".*"), DEFUN("Record", kRecord, ".*"), DEFUN("PlayBack", kPlayBack, ".*"), DEFUN("DbugStr", kDbugStr, ".*"), @@ -628,7 +628,7 @@ int Kernel::findRegType(reg_t reg) { switch (mobj->getType()) { case SEG_TYPE_SCRIPT: - if (reg.offset <= (*(Script *)mobj)._bufSize && + if (reg.offset <= (*(Script *)mobj).getBufSize() && reg.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET && RAW_IS_OBJECT((*(Script *)mobj)._buf + reg.offset)) { return ((Script *)mobj)->getObject(reg.offset) ? KSIG_OBJECT : KSIG_REF; diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h index 7717743e19..8f8f34f74e 100644 --- a/engines/sci/engine/kernel.h +++ b/engines/sci/engine/kernel.h @@ -414,6 +414,7 @@ reg_t kDoAudio(EngineState *s, int argc, reg_t *argv); reg_t kDoSync(EngineState *s, int argc, reg_t *argv); reg_t kMemorySegment(EngineState *s, int argc, reg_t *argv); reg_t kIntersections(EngineState *s, int argc, reg_t *argv); +reg_t kMergePoly(EngineState *s, int argc, reg_t *argv); reg_t kResCheck(EngineState *s, int argc, reg_t *argv); reg_t kSetQuitStr(EngineState *s, int argc, reg_t *argv); reg_t kShowMovie(EngineState *s, int argc, reg_t *argv); diff --git a/engines/sci/engine/kernel32.cpp b/engines/sci/engine/kernel32.cpp index 465e0e92df..0afdc3f2eb 100644 --- a/engines/sci/engine/kernel32.cpp +++ b/engines/sci/engine/kernel32.cpp @@ -501,7 +501,7 @@ reg_t kArray(EngineState *s, int argc, reg_t *argv) { if (!s->_segMan->isHeapObject(argv[1])) return argv[1]; - return GET_SEL32(s->_segMan, argv[1], SELECTOR(data)); + return readSelector(s->_segMan, argv[1], SELECTOR(data)); default: error("Unknown kArray subop %d", argv[0].toUint16()); } @@ -524,8 +524,12 @@ reg_t kString(EngineState *s, int argc, reg_t *argv) { } case 1: // Size return make_reg(0, s->_segMan->getString(argv[1]).size()); - case 2: // At (return value at an index) + case 2: { // At (return value at an index) + if (argv[1].segment == s->_segMan->getStringSegmentId()) + return make_reg(0, s->_segMan->lookupString(argv[1])->getRawData()[argv[2].toUint16()]); + return make_reg(0, s->_segMan->getString(argv[1])[argv[2].toUint16()]); + } case 3: { // Atput (put value at an index) SciString *string = s->_segMan->lookupString(argv[1]); @@ -563,28 +567,40 @@ reg_t kString(EngineState *s, int argc, reg_t *argv) { return argv[1]; } case 6: { // Cpy - Common::String string2 = s->_segMan->getString(argv[3]); + const char *string2 = 0; + uint32 string2Size = 0; + + if (argv[3].segment == s->_segMan->getStringSegmentId()) { + SciString *string = s->_segMan->lookupString(argv[3]); + string2 = string->getRawData(); + string2Size = string->getSize(); + } else { + Common::String string = s->_segMan->getString(argv[3]); + string2 = string.c_str(); + string2Size = string.size() + 1; + } + uint32 index1 = argv[2].toUint16(); uint32 index2 = argv[4].toUint16(); // The original engine ignores bad copies too - if (index2 > string2.size()) + if (index2 > string2Size) break; // A count of -1 means fill the rest of the array - uint32 count = argv[5].toSint16() == -1 ? string2.size() - index2 + 1 : argv[5].toUint16(); + uint32 count = argv[5].toSint16() == -1 ? string2Size - index2 + 1 : argv[5].toUint16(); // We have a special case here for argv[1] being a system string - if (argv[1].segment == s->sys_strings_segment) { + if (argv[1].segment == s->_segMan->getSysStringsSegment()) { // Resize if necessary - if ((uint32)s->sys_strings->_strings[argv[1].toUint16()]._maxSize < index1 + count) { - delete[] s->sys_strings->_strings[argv[1].toUint16()]._value; - s->sys_strings->_strings[argv[1].toUint16()]._maxSize = index1 + count; - s->sys_strings->_strings[argv[1].toUint16()]._value = new char[index1 + count]; - memset(s->sys_strings->_strings[argv[1].toUint16()]._value, 0, index1 + count); + const uint16 sysStringId = argv[1].toUint16(); + if ((uint32)s->_segMan->sysStrings->_strings[sysStringId]._maxSize < index1 + count) { + free(s->_segMan->sysStrings->_strings[sysStringId]._value); + s->_segMan->sysStrings->_strings[sysStringId]._maxSize = index1 + count; + s->_segMan->sysStrings->_strings[sysStringId]._value = (char *)calloc(index1 + count, sizeof(char)); } - strncpy(s->sys_strings->_strings[argv[1].toUint16()]._value + index1, string2.c_str() + index2, count); + strncpy(s->_segMan->sysStrings->_strings[sysStringId]._value + index1, string2 + index2, count); } else { SciString *string1 = s->_segMan->lookupString(argv[1]); @@ -594,7 +610,7 @@ reg_t kString(EngineState *s, int argc, reg_t *argv) { // Note: We're accessing from c_str() here because the string's size ignores // the trailing 0 and therefore triggers an assert when doing string2[i + index2]. for (uint16 i = 0; i < count; i++) - string1->setValue(i + index1, string2.c_str()[i + index2]); + string1->setValue(i + index1, string2[i + index2]); } } return argv[1]; @@ -608,15 +624,25 @@ reg_t kString(EngineState *s, int argc, reg_t *argv) { return make_reg(0, strcmp(string1.c_str(), string2.c_str())); } case 8: { // Dup - Common::String string = s->_segMan->getString(argv[1]); + const char *rawString = 0; + uint32 size = 0; + + if (argv[1].segment == s->_segMan->getStringSegmentId()) { + SciString *string = s->_segMan->lookupString(argv[1]); + rawString = string->getRawData(); + size = string->getSize(); + } else { + Common::String string = s->_segMan->getString(argv[1]); + rawString = string.c_str(); + size = string.size() + 1; + } + reg_t stringHandle; SciString *dupString = s->_segMan->allocateString(&stringHandle); - dupString->setSize(string.size() + 1); + dupString->setSize(size); - for (uint32 i = 0; i < string.size(); i++) - dupString->setValue(i, string.c_str()[i]); - - dupString->setValue(dupString->getSize() - 1, 0); + for (uint32 i = 0; i < size; i++) + dupString->setValue(i, rawString[i]); return stringHandle; } @@ -624,7 +650,7 @@ reg_t kString(EngineState *s, int argc, reg_t *argv) { if (!s->_segMan->isHeapObject(argv[1])) return argv[1]; - return GET_SEL32(s->_segMan, argv[1], SELECTOR(data)); + return readSelector(s->_segMan, argv[1], SELECTOR(data)); case 10: // Stringlen return make_reg(0, s->_segMan->strlen(argv[1])); case 11: { // Printf @@ -689,12 +715,12 @@ reg_t kDeleteScreenItem(EngineState *s, int argc, reg_t *argv) { /* reg_t viewObj = argv[0]; - uint16 viewId = GET_SEL32V(s->_segMan, viewObj, SELECTOR(view)); - int16 loopNo = GET_SEL32V(s->_segMan, viewObj, SELECTOR(loop)); - int16 celNo = GET_SEL32V(s->_segMan, viewObj, SELECTOR(cel)); + uint16 viewId = readSelectorValue(s->_segMan, viewObj, SELECTOR(view)); + int16 loopNo = readSelectorValue(s->_segMan, viewObj, SELECTOR(loop)); + int16 celNo = readSelectorValue(s->_segMan, viewObj, SELECTOR(cel)); //int16 leftPos = 0; //int16 topPos = 0; - int16 priority = GET_SEL32V(s->_segMan, viewObj, SELECTOR(priority)); + int16 priority = readSelectorValue(s->_segMan, viewObj, SELECTOR(priority)); //int16 control = 0; */ @@ -761,10 +787,10 @@ reg_t kOnMe(EngineState *s, int argc, reg_t *argv) { Common::Rect nsRect; // Get the bounding rectangle of the object - nsRect.left = GET_SEL32V(s->_segMan, targetObject, SELECTOR(nsLeft)); - nsRect.top = GET_SEL32V(s->_segMan, targetObject, SELECTOR(nsTop)); - nsRect.right = GET_SEL32V(s->_segMan, targetObject, SELECTOR(nsRight)); - nsRect.bottom = GET_SEL32V(s->_segMan, targetObject, SELECTOR(nsBottom)); + nsRect.left = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsLeft)); + nsRect.top = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsTop)); + nsRect.right = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsRight)); + nsRect.bottom = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsBottom)); //warning("kOnMe: (%d, %d) on object %04x:%04x, parameter %d", argv[0].toUint16(), argv[1].toUint16(), PRINT_REG(argv[2]), argv[3].toUint16()); @@ -778,9 +804,16 @@ reg_t kInPolygon(EngineState *s, int argc, reg_t *argv) { reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv) { // TODO: argument 0 is usually 0, and arguments 1 and 2 are usually 1 - reg_t object = argv[3]; - Common::String text = s->_segMan->getString(GET_SEL32(s->_segMan, object, SELECTOR(text))); - debug("kCreateTextBitmap: %s", text.c_str()); + switch (argv[0].toUint16()) { + case 0: + if (argc != 4) { + warning("kCreateTextBitmap(0): expected 4 arguments, got %i", argc); + return NULL_REG; + } + reg_t object = argv[3]; + Common::String text = s->_segMan->getString(readSelector(s->_segMan, object, SELECTOR(text))); + debug("kCreateTextBitmap: %s", text.c_str()); + } return NULL_REG; } diff --git a/engines/sci/engine/kevent.cpp b/engines/sci/engine/kevent.cpp index 156035b30d..fd7711f196 100644 --- a/engines/sci/engine/kevent.cpp +++ b/engines/sci/engine/kevent.cpp @@ -49,16 +49,18 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) { SegManager *segMan = s->_segMan; Common::Point mousePos; + // Limit the mouse cursor position, if necessary + g_sci->_gfxCursor->refreshPosition(); mousePos = g_sci->_gfxCursor->getPosition(); // If there's a simkey pending, and the game wants a keyboard event, use the // simkey instead of a normal event if (g_debug_simulated_key && (mask & SCI_EVENT_KEYBOARD)) { - PUT_SEL32V(segMan, obj, SELECTOR(type), SCI_EVENT_KEYBOARD); // Keyboard event - PUT_SEL32V(segMan, obj, SELECTOR(message), g_debug_simulated_key); - PUT_SEL32V(segMan, obj, SELECTOR(modifiers), SCI_KEYMOD_NUMLOCK); // Numlock on - PUT_SEL32V(segMan, obj, SELECTOR(x), mousePos.x); - PUT_SEL32V(segMan, obj, SELECTOR(y), mousePos.y); + writeSelectorValue(segMan, obj, SELECTOR(type), SCI_EVENT_KEYBOARD); // Keyboard event + writeSelectorValue(segMan, obj, SELECTOR(message), g_debug_simulated_key); + writeSelectorValue(segMan, obj, SELECTOR(modifiers), SCI_KEYMOD_NUMLOCK); // Numlock on + writeSelectorValue(segMan, obj, SELECTOR(x), mousePos.x); + writeSelectorValue(segMan, obj, SELECTOR(y), mousePos.y); g_debug_simulated_key = 0; return make_reg(0, 1); } @@ -67,26 +69,26 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) { oldy = mousePos.y; curEvent = s->_event->get(mask); - if (s->_voc) - s->_voc->parser_event = NULL_REG; // Invalidate parser event + if (g_sci->getVocabulary()) + g_sci->getVocabulary()->parser_event = NULL_REG; // Invalidate parser event - PUT_SEL32V(segMan, obj, SELECTOR(x), mousePos.x); - PUT_SEL32V(segMan, obj, SELECTOR(y), mousePos.y); + writeSelectorValue(segMan, obj, SELECTOR(x), mousePos.x); + writeSelectorValue(segMan, obj, SELECTOR(y), mousePos.y); //s->_gui->moveCursor(s->gfx_state->pointer_pos.x, s->gfx_state->pointer_pos.y); switch (curEvent.type) { case SCI_EVENT_QUIT: - quit_vm(); + quit_vm(s); break; case SCI_EVENT_KEYBOARD: - PUT_SEL32V(segMan, obj, SELECTOR(type), SCI_EVENT_KEYBOARD); // Keyboard event + writeSelectorValue(segMan, obj, SELECTOR(type), SCI_EVENT_KEYBOARD); // Keyboard event s->r_acc = make_reg(0, 1); - PUT_SEL32V(segMan, obj, SELECTOR(message), curEvent.character); + writeSelectorValue(segMan, obj, SELECTOR(message), curEvent.character); // We only care about the translated character - PUT_SEL32V(segMan, obj, SELECTOR(modifiers), curEvent.modifiers & modifier_mask); + writeSelectorValue(segMan, obj, SELECTOR(modifiers), curEvent.modifiers & modifier_mask); break; case SCI_EVENT_MOUSE_RELEASE: @@ -111,9 +113,9 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) { break; } - PUT_SEL32V(segMan, obj, SELECTOR(type), curEvent.type); - PUT_SEL32V(segMan, obj, SELECTOR(message), 0); - PUT_SEL32V(segMan, obj, SELECTOR(modifiers), (curEvent.modifiers | extra_bits) & modifier_mask); + writeSelectorValue(segMan, obj, SELECTOR(type), curEvent.type); + writeSelectorValue(segMan, obj, SELECTOR(message), 0); + writeSelectorValue(segMan, obj, SELECTOR(modifiers), (curEvent.modifiers | extra_bits) & modifier_mask); s->r_acc = make_reg(0, 1); } break; @@ -165,9 +167,9 @@ reg_t kMapKeyToDir(EngineState *s, int argc, reg_t *argv) { reg_t obj = argv[0]; SegManager *segMan = s->_segMan; - if (GET_SEL32V(segMan, obj, SELECTOR(type)) == SCI_EVENT_KEYBOARD) { // Keyboard + if (readSelectorValue(segMan, obj, SELECTOR(type)) == SCI_EVENT_KEYBOARD) { // Keyboard int mover = -1; - switch (GET_SEL32V(segMan, obj, SELECTOR(message))) { + switch (readSelectorValue(segMan, obj, SELECTOR(message))) { case SCI_KEY_HOME: mover = 8; break; @@ -201,8 +203,8 @@ reg_t kMapKeyToDir(EngineState *s, int argc, reg_t *argv) { } if (mover >= 0) { - PUT_SEL32V(segMan, obj, SELECTOR(type), SCI_EVENT_JOYSTICK); - PUT_SEL32V(segMan, obj, SELECTOR(message), mover); + writeSelectorValue(segMan, obj, SELECTOR(type), SCI_EVENT_JOYSTICK); + writeSelectorValue(segMan, obj, SELECTOR(message), mover); return make_reg(0, 1); } else return NULL_REG; @@ -217,13 +219,13 @@ reg_t kGlobalToLocal(EngineState *s, int argc, reg_t *argv) { SegManager *segMan = s->_segMan; if (obj.segment) { - int16 x = GET_SEL32V(segMan, obj, SELECTOR(x)); - int16 y = GET_SEL32V(segMan, obj, SELECTOR(y)); + int16 x = readSelectorValue(segMan, obj, SELECTOR(x)); + int16 y = readSelectorValue(segMan, obj, SELECTOR(y)); g_sci->_gfxCoordAdjuster->kernelGlobalToLocal(x, y, planeObject); - PUT_SEL32V(segMan, obj, SELECTOR(x), x); - PUT_SEL32V(segMan, obj, SELECTOR(y), y); + writeSelectorValue(segMan, obj, SELECTOR(x), x); + writeSelectorValue(segMan, obj, SELECTOR(y), y); } return s->r_acc; @@ -236,13 +238,13 @@ reg_t kLocalToGlobal(EngineState *s, int argc, reg_t *argv) { SegManager *segMan = s->_segMan; if (obj.segment) { - int16 x = GET_SEL32V(segMan, obj, SELECTOR(x)); - int16 y = GET_SEL32V(segMan, obj, SELECTOR(y)); + int16 x = readSelectorValue(segMan, obj, SELECTOR(x)); + int16 y = readSelectorValue(segMan, obj, SELECTOR(y)); g_sci->_gfxCoordAdjuster->kernelLocalToGlobal(x, y, planeObject); - PUT_SEL32V(segMan, obj, SELECTOR(x), x); - PUT_SEL32V(segMan, obj, SELECTOR(y), y); + writeSelectorValue(segMan, obj, SELECTOR(x), x); + writeSelectorValue(segMan, obj, SELECTOR(y), y); } return s->r_acc; diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp index e6b9a5388c..3e0ecd1a28 100644 --- a/engines/sci/engine/kfile.cpp +++ b/engines/sci/engine/kfile.cpp @@ -44,6 +44,8 @@ struct SavegameDesc { int id; int date; int time; + int version; + char name[SCI_MAX_SAVENAME_LENGTH]; }; /* @@ -245,13 +247,10 @@ static void fgets_wrapper(EngineState *s, char *dest, int maxsize, int handle) { debugC(2, kDebugLevelFile, "FGets'ed \"%s\"", dest); } -static int _savegame_index_struct_compare(const void *a, const void *b) { - const SavegameDesc *A = (const SavegameDesc *)a; - const SavegameDesc *B = (const SavegameDesc *)b; - - if (B->date != A->date) - return B->date - A->date; - return B->time - A->time; +static bool _savegame_index_struct_compare(const SavegameDesc &l, const SavegameDesc &r) { + if (l.date != r.date) + return (l.date > r.date); + return (l.time > r.time); } void listSavegames(Common::Array<SavegameDesc> &saves) { @@ -265,7 +264,7 @@ void listSavegames(Common::Array<SavegameDesc> &saves) { Common::SeekableReadStream *in; if ((in = saveFileMan->openForLoading(filename))) { SavegameMetadata meta; - if (!get_savegame_metadata(in, &meta)) { + if (!get_savegame_metadata(in, &meta) || meta.savegame_name.empty()) { // invalid delete in; continue; @@ -278,6 +277,13 @@ void listSavegames(Common::Array<SavegameDesc> &saves) { // We need to fix date in here, because we save DDMMYYYY instead of YYYYMMDD, so sorting wouldnt work desc.date = ((desc.date & 0xFFFF) << 16) | ((desc.date & 0xFF0000) >> 8) | ((desc.date & 0xFF000000) >> 24); desc.time = meta.savegame_time; + desc.version = meta.savegame_version; + + if (meta.savegame_name.lastChar() == '\n') + meta.savegame_name.deleteLastChar(); + + Common::strlcpy(desc.name, meta.savegame_name.c_str(), SCI_MAX_SAVENAME_LENGTH); + debug(3, "Savegame in file %s ok, id %d", filename.c_str(), desc.id); saves.push_back(desc); @@ -285,35 +291,18 @@ void listSavegames(Common::Array<SavegameDesc> &saves) { } // Sort the list by creation date of the saves - qsort(saves.begin(), saves.size(), sizeof(SavegameDesc), _savegame_index_struct_compare); + Common::sort(saves.begin(), saves.end(), _savegame_index_struct_compare); } bool Console::cmdListSaves(int argc, const char **argv) { Common::Array<SavegameDesc> saves; listSavegames(saves); - Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager(); - for (uint i = 0; i < saves.size(); i++) { Common::String filename = g_sci->getSavegameName(saves[i].id); - Common::SeekableReadStream *in; - if ((in = saveFileMan->openForLoading(filename))) { - SavegameMetadata meta; - if (!get_savegame_metadata(in, &meta)) { - // invalid - delete in; - continue; - } - - if (!meta.savegame_name.empty()) { - if (meta.savegame_name.lastChar() == '\n') - meta.savegame_name.deleteLastChar(); - - DebugPrintf("%s: '%s'\n", filename.c_str(), meta.savegame_name.c_str()); - } - delete in; - } + DebugPrintf("%s: '%s'\n", filename.c_str(), saves[i].name); } + return true; } @@ -428,7 +417,7 @@ reg_t kGetSaveDir(EngineState *s, int argc, reg_t *argv) { warning("kGetSaveDir called with %d parameter(s): %04x:%04x", argc, PRINT_REG(argv[0])); #endif - return make_reg(s->sys_strings_segment, SYS_STRING_SAVEDIR); + return make_reg(s->_segMan->getSysStringsSegment(), SYS_STRING_SAVEDIR); } reg_t kCheckFreeSpace(EngineState *s, int argc, reg_t *argv) { @@ -449,90 +438,59 @@ reg_t kCheckFreeSpace(EngineState *s, int argc, reg_t *argv) { reg_t kCheckSaveGame(EngineState *s, int argc, reg_t *argv) { Common::String game_id = s->_segMan->getString(argv[0]); - int savedir_nr = argv[1].toUint16(); + uint16 savedir_nr = argv[1].toUint16(); debug(3, "kCheckSaveGame(%s, %d)", game_id.c_str(), savedir_nr); Common::Array<SavegameDesc> saves; listSavegames(saves); - savedir_nr = saves[savedir_nr].id; - - if (savedir_nr > MAX_SAVEGAME_NR - 1) { + // Check for savegame slot being out of range + if (savedir_nr >= saves.size()) return NULL_REG; - } - Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager(); - Common::String filename = g_sci->getSavegameName(savedir_nr); - Common::SeekableReadStream *in; - if ((in = saveFileMan->openForLoading(filename))) { - // found a savegame file - - SavegameMetadata meta; - if (!get_savegame_metadata(in, &meta)) { - // invalid - s->r_acc = make_reg(0, 0); - } else { - s->r_acc = make_reg(0, 1); - } - delete in; - } else { - s->r_acc = make_reg(0, 1); - } + // Check for compatible savegame version + int ver = saves[savedir_nr].version; + if (ver < MINIMUM_SAVEGAME_VERSION || ver > CURRENT_SAVEGAME_VERSION) + return NULL_REG; - return s->r_acc; + // Otherwise we assume the savegame is OK + return make_reg(0, 1); } reg_t kGetSaveFiles(EngineState *s, int argc, reg_t *argv) { Common::String game_id = s->_segMan->getString(argv[0]); - reg_t nametarget = argv[1]; - reg_t *nameoffsets = s->_segMan->derefRegPtr(argv[2], 0); debug(3, "kGetSaveFiles(%s)", game_id.c_str()); Common::Array<SavegameDesc> saves; listSavegames(saves); - s->r_acc = NULL_REG; - Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager(); + uint totalSaves = MIN<uint>(saves.size(), MAX_SAVEGAME_NR); - for (uint i = 0; i < saves.size(); i++) { - Common::String filename = g_sci->getSavegameName(saves[i].id); - Common::SeekableReadStream *in; - if ((in = saveFileMan->openForLoading(filename))) { - // found a savegame file + reg_t *slot = s->_segMan->derefRegPtr(argv[2], totalSaves); - SavegameMetadata meta; - if (!get_savegame_metadata(in, &meta)) { - // invalid - delete in; - continue; - } - - if (!meta.savegame_name.empty()) { - if (meta.savegame_name.lastChar() == '\n') - meta.savegame_name.deleteLastChar(); - - *nameoffsets = s->r_acc; // Store savegame ID - ++s->r_acc.offset; // Increase number of files found + if (!slot) { + warning("kGetSaveFiles: %04X:%04X invalid or too small to hold slot data", PRINT_REG(argv[2])); + totalSaves = 0; + } - nameoffsets++; // Make sure the next ID string address is written to the next pointer - Common::String name = meta.savegame_name; - if (name.size() > SCI_MAX_SAVENAME_LENGTH-1) - name = Common::String(meta.savegame_name.c_str(), SCI_MAX_SAVENAME_LENGTH-1); - s->_segMan->strcpy(nametarget, name.c_str()); + const uint bufSize = (totalSaves * SCI_MAX_SAVENAME_LENGTH) + 1; + char *saveNames = new char[bufSize]; + char *saveNamePtr = saveNames; - // Increase name offset pointer accordingly - nametarget.offset += SCI_MAX_SAVENAME_LENGTH; - } - delete in; - } + for (uint i = 0; i < totalSaves; i++) { + *slot++ = make_reg(0, i); // Store slot + strcpy(saveNamePtr, saves[i].name); + saveNamePtr += SCI_MAX_SAVENAME_LENGTH; } - //free(gfname); - s->_segMan->strcpy(nametarget, ""); // Terminate list + *saveNamePtr = 0; // Terminate list - return s->r_acc; + s->_segMan->memcpy(argv[1], (byte *)saveNames, bufSize); + delete[] saveNames; + + return make_reg(0, totalSaves); } reg_t kSaveGame(EngineState *s, int argc, reg_t *argv) { diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp index d587790b6c..abe55455de 100644 --- a/engines/sci/engine/kgraphics.cpp +++ b/engines/sci/engine/kgraphics.cpp @@ -23,8 +23,10 @@ * */ +#include "engines/util.h" #include "graphics/cursorman.h" #include "graphics/video/avi_decoder.h" +#include "graphics/video/qt_decoder.h" #include "graphics/surface.h" #include "sci/sci.h" @@ -51,8 +53,8 @@ namespace Sci { void _k_dirloop(reg_t object, uint16 angle, EngineState *s, int argc, reg_t *argv) { - GuiResourceId viewId = GET_SEL32V(s->_segMan, object, SELECTOR(view)); - uint16 signal = GET_SEL32V(s->_segMan, object, SELECTOR(signal)); + GuiResourceId viewId = readSelectorValue(s->_segMan, object, SELECTOR(view)); + uint16 signal = readSelectorValue(s->_segMan, object, SELECTOR(signal)); int16 loopNo; int16 maxLoops; bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); @@ -91,7 +93,7 @@ void _k_dirloop(reg_t object, uint16 angle, EngineState *s, int argc, reg_t *arg if ((loopNo > 1) && (maxLoops < 4)) return; - PUT_SEL32V(s->_segMan, object, SELECTOR(loop), loopNo); + writeSelectorValue(s->_segMan, object, SELECTOR(loop), loopNo); } static reg_t kSetCursorSci0(EngineState *s, int argc, reg_t *argv) { @@ -146,6 +148,10 @@ static reg_t kSetCursorSci11(EngineState *s, int argc, reg_t *argv) { int16 bottom = argv[2].toSint16(); int16 right = argv[3].toSint16(); + // In SCI32, the right parameter seems to be divided by 2 + if (getSciVersion() >= SCI_VERSION_2) + right *= 2; + if ((right >= left) && (bottom >= top)) { Common::Rect rect = Common::Rect(left, top, right, bottom); g_sci->_gfxCursor->kernelSetMoveZone(rect); @@ -437,7 +443,7 @@ reg_t kCelWide(EngineState *s, int argc, reg_t *argv) { reg_t kNumLoops(EngineState *s, int argc, reg_t *argv) { reg_t object = argv[0]; - GuiResourceId viewId = GET_SEL32V(s->_segMan, object, SELECTOR(view)); + GuiResourceId viewId = readSelectorValue(s->_segMan, object, SELECTOR(view)); int16 loopCount; loopCount = g_sci->_gfxCache->kernelViewGetLoopCount(viewId); @@ -449,8 +455,8 @@ reg_t kNumLoops(EngineState *s, int argc, reg_t *argv) { reg_t kNumCels(EngineState *s, int argc, reg_t *argv) { reg_t object = argv[0]; - GuiResourceId viewId = GET_SEL32V(s->_segMan, object, SELECTOR(view)); - int16 loopNo = GET_SEL32V(s->_segMan, object, SELECTOR(loop)); + GuiResourceId viewId = readSelectorValue(s->_segMan, object, SELECTOR(view)); + int16 loopNo = readSelectorValue(s->_segMan, object, SELECTOR(loop)); int16 celCount; celCount = g_sci->_gfxCache->kernelViewGetCelCount(viewId, loopNo); @@ -525,9 +531,9 @@ reg_t kBaseSetter(EngineState *s, int argc, reg_t *argv) { // WORKAROUND for a problem in LSL1VGA. This allows the casino door to be opened, // till the actual problem is found - if (s->_gameId == "lsl1sci" && s->currentRoomNumber() == 300) { - int top = GET_SEL32V(s->_segMan, object, SELECTOR(brTop)); - PUT_SEL32V(s->_segMan, object, SELECTOR(brTop), top + 2); + if (!strcmp(g_sci->getGameID(), "lsl1sci") && s->currentRoomNumber() == 300) { + int top = readSelectorValue(s->_segMan, object, SELECTOR(brTop)); + writeSelectorValue(s->_segMan, object, SELECTOR(brTop), top + 2); } return s->r_acc; @@ -741,12 +747,12 @@ Common::Rect kControlCreateRect(int16 x, int16 y, int16 x1, int16 y1) { } void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) { - int16 type = GET_SEL32V(s->_segMan, controlObject, SELECTOR(type)); - int16 style = GET_SEL32V(s->_segMan, controlObject, SELECTOR(state)); - int16 x = GET_SEL32V(s->_segMan, controlObject, SELECTOR(nsLeft)); - int16 y = GET_SEL32V(s->_segMan, controlObject, SELECTOR(nsTop)); - GuiResourceId fontId = GET_SEL32V(s->_segMan, controlObject, SELECTOR(font)); - reg_t textReference = GET_SEL32(s->_segMan, controlObject, SELECTOR(text)); + int16 type = readSelectorValue(s->_segMan, controlObject, SELECTOR(type)); + int16 style = readSelectorValue(s->_segMan, controlObject, SELECTOR(state)); + int16 x = readSelectorValue(s->_segMan, controlObject, SELECTOR(nsLeft)); + int16 y = readSelectorValue(s->_segMan, controlObject, SELECTOR(nsTop)); + GuiResourceId fontId = readSelectorValue(s->_segMan, controlObject, SELECTOR(font)); + reg_t textReference = readSelector(s->_segMan, controlObject, SELECTOR(text)); Common::String text; Common::Rect rect; TextAlignment alignment; @@ -762,8 +768,8 @@ void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) { bool isAlias = false; rect = kControlCreateRect(x, y, - GET_SEL32V(s->_segMan, controlObject, SELECTOR(nsRight)), - GET_SEL32V(s->_segMan, controlObject, SELECTOR(nsBottom))); + readSelectorValue(s->_segMan, controlObject, SELECTOR(nsRight)), + readSelectorValue(s->_segMan, controlObject, SELECTOR(nsBottom))); if (!textReference.isNull()) text = s->_segMan->getString(textReference); @@ -775,32 +781,32 @@ void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) { return; case SCI_CONTROLS_TYPE_TEXT: - alignment = GET_SEL32V(s->_segMan, controlObject, SELECTOR(mode)); + alignment = readSelectorValue(s->_segMan, controlObject, SELECTOR(mode)); debugC(2, kDebugLevelGraphics, "drawing text %04x:%04x ('%s') to %d,%d, mode=%d", PRINT_REG(controlObject), text.c_str(), x, y, alignment); g_sci->_gfxControls->kernelDrawText(rect, controlObject, g_sci->strSplit(text.c_str()).c_str(), fontId, alignment, style, hilite); return; case SCI_CONTROLS_TYPE_TEXTEDIT: - mode = GET_SEL32V(s->_segMan, controlObject, SELECTOR(mode)); - maxChars = GET_SEL32V(s->_segMan, controlObject, SELECTOR(max)); - cursorPos = GET_SEL32V(s->_segMan, controlObject, SELECTOR(cursor)); + mode = readSelectorValue(s->_segMan, controlObject, SELECTOR(mode)); + maxChars = readSelectorValue(s->_segMan, controlObject, SELECTOR(max)); + cursorPos = readSelectorValue(s->_segMan, controlObject, SELECTOR(cursor)); debugC(2, kDebugLevelGraphics, "drawing edit control %04x:%04x (text %04x:%04x, '%s') to %d,%d", PRINT_REG(controlObject), PRINT_REG(textReference), text.c_str(), x, y); g_sci->_gfxControls->kernelDrawTextEdit(rect, controlObject, g_sci->strSplit(text.c_str(), NULL).c_str(), fontId, mode, style, cursorPos, maxChars, hilite); return; case SCI_CONTROLS_TYPE_ICON: - viewId = GET_SEL32V(s->_segMan, controlObject, SELECTOR(view)); + viewId = readSelectorValue(s->_segMan, controlObject, SELECTOR(view)); { - int l = GET_SEL32V(s->_segMan, controlObject, SELECTOR(loop)); + int l = readSelectorValue(s->_segMan, controlObject, SELECTOR(loop)); loopNo = (l & 0x80) ? l - 256 : l; - int c = GET_SEL32V(s->_segMan, controlObject, SELECTOR(cel)); + int c = readSelectorValue(s->_segMan, controlObject, SELECTOR(cel)); celNo = (c & 0x80) ? c - 256 : c; // Game-specific: *ONLY* the jones EGA/VGA sierra interpreter contain code using priority selector // ALL other games use a hardcoded -1 (madness!) // We are detecting jones/talkie as "jones" as well, but the sierra interpreter of talkie doesnt have this // "hack". Hopefully it wont cause regressions (the code causes regressions if used against kq5/floppy) - if (s->_gameId == "jones") - priority = GET_SEL32V(s->_segMan, controlObject, SELECTOR(priority)); + if (!strcmp(g_sci->getGameID(), "jones")) + priority = readSelectorValue(s->_segMan, controlObject, SELECTOR(priority)); else priority = -1; } @@ -813,17 +819,17 @@ void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) { if (type == SCI_CONTROLS_TYPE_LIST_ALIAS) isAlias = true; - maxChars = GET_SEL32V(s->_segMan, controlObject, SELECTOR(x)); // max chars per entry - cursorOffset = GET_SEL32V(s->_segMan, controlObject, SELECTOR(cursor)); + maxChars = readSelectorValue(s->_segMan, controlObject, SELECTOR(x)); // max chars per entry + cursorOffset = readSelectorValue(s->_segMan, controlObject, SELECTOR(cursor)); if (g_sci->getKernel()->_selectorCache.topString != -1) { // Games from early SCI1 onwards use topString - upperOffset = GET_SEL32V(s->_segMan, controlObject, SELECTOR(topString)); + upperOffset = readSelectorValue(s->_segMan, controlObject, SELECTOR(topString)); } else { // Earlier games use lsTop or brTop - if (lookup_selector(s->_segMan, controlObject, g_sci->getKernel()->_selectorCache.brTop, NULL, NULL) == kSelectorVariable) - upperOffset = GET_SEL32V(s->_segMan, controlObject, SELECTOR(brTop)); + if (lookupSelector(s->_segMan, controlObject, g_sci->getKernel()->_selectorCache.brTop, NULL, NULL) == kSelectorVariable) + upperOffset = readSelectorValue(s->_segMan, controlObject, SELECTOR(brTop)); else - upperOffset = GET_SEL32V(s->_segMan, controlObject, SELECTOR(lsTop)); + upperOffset = readSelectorValue(s->_segMan, controlObject, SELECTOR(lsTop)); } // Count string entries in NULL terminated string list @@ -874,8 +880,8 @@ reg_t kDrawControl(EngineState *s, int argc, reg_t *argv) { // Disable the "Change Directory" button, as we don't allow the game engine to // change the directory where saved games are placed if (objName == "changeDirI") { - int state = GET_SEL32V(s->_segMan, controlObject, SELECTOR(state)); - PUT_SEL32V(s->_segMan, controlObject, SELECTOR(state), (state | SCI_CONTROLS_STYLE_DISABLED) & ~SCI_CONTROLS_STYLE_ENABLED); + int state = readSelectorValue(s->_segMan, controlObject, SELECTOR(state)); + writeSelectorValue(s->_segMan, controlObject, SELECTOR(state), (state | SCI_CONTROLS_STYLE_DISABLED) & ~SCI_CONTROLS_STYLE_ENABLED); } _k_GenericDrawControl(s, controlObject, false); @@ -894,7 +900,7 @@ reg_t kEditControl(EngineState *s, int argc, reg_t *argv) { reg_t eventObject = argv[1]; if (!controlObject.isNull()) { - int16 controlType = GET_SEL32V(s->_segMan, controlObject, SELECTOR(type)); + int16 controlType = readSelectorValue(s->_segMan, controlObject, SELECTOR(type)); switch (controlType) { case SCI_CONTROLS_TYPE_TEXTEDIT: @@ -983,7 +989,7 @@ reg_t kDrawCel(EngineState *s, int argc, reg_t *argv) { bool hiresMode = (argc > 7) ? true : false; reg_t upscaledHiresHandle = (argc > 7) ? argv[7] : NULL_REG; - if ((s->_gameId == "freddypharkas") || (s->_gameId == "freddypharkas-demo")) { + if (!strcmp(g_sci->getGameID(), "freddypharkas") || !strcmp(g_sci->getGameID(), "freddypharkas-demo")) { // WORKAROUND // Script 24 contains code that draws the game menu on screen. It uses a temp variable for setting priority that // is not set. in Sierra sci this happens to be 8250h. In our sci temporary variables are initialized thus we would @@ -994,7 +1000,7 @@ reg_t kDrawCel(EngineState *s, int argc, reg_t *argv) { priority = 15; } - if (s->_gameId == "laurabow2") { + if (!strcmp(g_sci->getGameID(), "laurabow2")) { // WORKAROUND // see the one above if ((viewId == 995) && (priority == 0)) @@ -1080,11 +1086,12 @@ reg_t kDisplay(EngineState *s, int argc, reg_t *argv) { reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { // Hide the cursor if it's showing and then show it again if it was // previously visible. - bool reshowCursor; - - reshowCursor = g_sci->_gfxCursor->isVisible(); + bool reshowCursor = g_sci->_gfxCursor->isVisible(); if (reshowCursor) g_sci->_gfxCursor->kernelHide(); + + uint16 screenWidth = g_system->getWidth(); + uint16 screenHeight = g_system->getHeight(); Graphics::VideoDecoder *videoDecoder = 0; @@ -1094,8 +1101,18 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { if (g_sci->getPlatform() == Common::kPlatformMacintosh) { // Mac QuickTime // The only argument is the string for the video - warning("TODO: Play QuickTime movie '%s'", filename.c_str()); - return s->r_acc; + + // HACK: Switch to 16bpp graphics for Cinepak. + initGraphics(screenWidth, screenHeight, screenWidth > 320, NULL); + + if (g_system->getScreenFormat().bytesPerPixel == 1) { + warning("This video requires >8bpp color to be displayed, but could not switch to RGB color mode."); + return NULL_REG; + } + + videoDecoder = new Graphics::QuickTimeDecoder(); + if (!videoDecoder->loadFile(filename)) + error("Could not open '%s'", filename.c_str()); } else { // DOS SEQ // SEQ's are called with no subops, just the string and delay @@ -1110,7 +1127,7 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { } } } else { - // Windows AVI (Macintosh QuickTime? Need to check KQ6 Macintosh) + // Windows AVI // TODO: This appears to be some sort of subop. case 0 contains the string // for the video, so we'll just play it from there for now. @@ -1142,10 +1159,10 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { } if (videoDecoder) { - uint16 x = (g_system->getWidth() - videoDecoder->getWidth()) / 2; - uint16 y = (g_system->getHeight() - videoDecoder->getHeight()) / 2; + uint16 x = (screenWidth - videoDecoder->getWidth()) / 2; + uint16 y = (screenHeight - videoDecoder->getHeight()) / 2; - while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo()) { + while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo()) { if (videoDecoder->needsUpdate()) { Graphics::Surface *frame = videoDecoder->decodeNextFrame(); if (frame) { @@ -1164,9 +1181,15 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { g_system->delayMillis(10); } + + // HACK: Switch back to 8bpp if we played a QuickTime video. + // We also won't be copying the screen to the SCI screen... + if (g_system->getScreenFormat().bytesPerPixel != 1) + initGraphics(screenWidth, screenHeight, screenWidth > 320); + else + g_sci->_gfxScreen->kernelSyncWithFramebuffer(); delete videoDecoder; - g_sci->_gfxScreen->kernelSyncWithFramebuffer(); } if (reshowCursor) diff --git a/engines/sci/engine/klists.cpp b/engines/sci/engine/klists.cpp index c04454ca3d..f06f3eec77 100644 --- a/engines/sci/engine/klists.cpp +++ b/engines/sci/engine/klists.cpp @@ -155,28 +155,10 @@ reg_t kDisposeList(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } -static reg_t _k_new_node(EngineState *s, reg_t value, reg_t key) { - reg_t nodebase; - Node *n = s->_segMan->allocateNode(&nodebase); - - if (!n) { - error("[Kernel] Out of memory while creating a node"); - return NULL_REG; - } - - n->pred = n->succ = NULL_REG; - n->key = key; - n->value = value; - - return nodebase; -} - reg_t kNewNode(EngineState *s, int argc, reg_t *argv) { - - if (argc == 1) - s->r_acc = _k_new_node(s, argv[0], argv[0]); - else - s->r_acc = _k_new_node(s, argv[0], argv[1]); + reg_t nodeValue = argv[0]; + reg_t nodeKey = (argc == 2) ? argv[1] : NULL_REG; + s->r_acc = s->_segMan->newNode(nodeValue, nodeKey); debugC(2, kDebugLevelNodes, "New nodebase at %04x:%04x", PRINT_REG(s->r_acc)); @@ -415,11 +397,11 @@ reg_t kSort(EngineState *s, int argc, reg_t *argv) { reg_t dest = argv[1]; reg_t order_func = argv[2]; - int input_size = (int16)GET_SEL32V(segMan, source, SELECTOR(size)); + int input_size = (int16)readSelectorValue(segMan, source, SELECTOR(size)); int i; - reg_t input_data = GET_SEL32(segMan, source, SELECTOR(elements)); - reg_t output_data = GET_SEL32(segMan, dest, SELECTOR(elements)); + reg_t input_data = readSelector(segMan, source, SELECTOR(elements)); + reg_t output_data = readSelector(segMan, dest, SELECTOR(elements)); List *list; Node *node; @@ -430,10 +412,10 @@ reg_t kSort(EngineState *s, int argc, reg_t *argv) { if (output_data.isNull()) { list = s->_segMan->allocateList(&output_data); list->first = list->last = NULL_REG; - PUT_SEL32(segMan, dest, SELECTOR(elements), output_data); + writeSelector(segMan, dest, SELECTOR(elements), output_data); } - PUT_SEL32V(segMan, dest, SELECTOR(size), input_size); + writeSelectorValue(segMan, dest, SELECTOR(size), input_size); list = s->_segMan->lookupList(input_data); node = s->_segMan->lookupNode(list->first); @@ -442,7 +424,7 @@ reg_t kSort(EngineState *s, int argc, reg_t *argv) { i = 0; while (node) { - invoke_selector(INV_SEL(s, order_func, doit, kStopOnInvalidSelector), 1, node->value); + invokeSelector(INV_SEL(s, order_func, doit, kStopOnInvalidSelector), 1, node->value); temp_array[i].key = node->key; temp_array[i].value = node->value; temp_array[i].order = s->r_acc; @@ -453,7 +435,7 @@ reg_t kSort(EngineState *s, int argc, reg_t *argv) { qsort(temp_array, input_size, sizeof(sort_temp_t), sort_temp_cmp); for (i = 0;i < input_size;i++) { - reg_t lNode = _k_new_node(s, temp_array[i].key, temp_array[i].value); + reg_t lNode = s->_segMan->newNode(temp_array[i].value, temp_array[i].key); _k_add_to_end(s, output_data, lNode); } @@ -533,15 +515,15 @@ reg_t kListEachElementDo(EngineState *s, int argc, reg_t *argv) { curObject = curNode->value; // First, check if the target selector is a variable - if (lookup_selector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) { + if (lookupSelector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) { // This can only happen with 3 params (list, target selector, variable) if (argc != 3) { warning("kListEachElementDo: Attempted to modify a variable selector with %d params", argc); } else { - write_selector(s->_segMan, curObject, slc, argv[2]); + writeSelector(s->_segMan, curObject, slc, argv[2]); } } else { - invoke_selector_argv(s, curObject, slc, kContinueOnInvalidSelector, argc, argv, argc - 2, argv + 2); + invokeSelectorArgv(s, curObject, slc, kContinueOnInvalidSelector, argc, argv, argc - 2, argv + 2); } curNode = s->_segMan->lookupNode(nextNode); @@ -566,11 +548,11 @@ reg_t kListFirstTrue(EngineState *s, int argc, reg_t *argv) { curObject = curNode->value; // First, check if the target selector is a variable - if (lookup_selector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) { + if (lookupSelector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) { // Can this happen with variable selectors? warning("kListFirstTrue: Attempted to access a variable selector"); } else { - invoke_selector_argv(s, curObject, slc, kContinueOnInvalidSelector, argc, argv, argc - 2, argv + 2); + invokeSelectorArgv(s, curObject, slc, kContinueOnInvalidSelector, argc, argv, argc - 2, argv + 2); // Check if the result is true if (!s->r_acc.isNull()) @@ -600,11 +582,11 @@ reg_t kListAllTrue(EngineState *s, int argc, reg_t *argv) { curObject = curNode->value; // First, check if the target selector is a variable - if (lookup_selector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) { + if (lookupSelector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) { // Can this happen with variable selectors? warning("kListAllTrue: Attempted to access a variable selector"); } else { - invoke_selector_argv(s, curObject, slc, kContinueOnInvalidSelector, argc, argv, argc - 2, argv + 2); + invokeSelectorArgv(s, curObject, slc, kContinueOnInvalidSelector, argc, argv, argc - 2, argv + 2); // Check if the result isn't true if (s->r_acc.isNull()) diff --git a/engines/sci/engine/kmisc.cpp b/engines/sci/engine/kmisc.cpp index 450dca3770..f91ba0fd82 100644 --- a/engines/sci/engine/kmisc.cpp +++ b/engines/sci/engine/kmisc.cpp @@ -32,16 +32,16 @@ #include "sci/engine/kernel.h" #include "sci/engine/gc.h" #include "sci/graphics/gui.h" +#include "sci/graphics/maciconbar.h" namespace Sci { reg_t kRestartGame(EngineState *s, int argc, reg_t *argv) { s->restarting_flags |= SCI_GAME_IS_RESTARTING_NOW; - s->restarting_flags &= ~SCI_GAME_WAS_RESTARTED_AT_LEAST_ONCE; // This appears to help - shrink_execution_stack(s, s->execution_stack_base + 1); + s->shrinkStackToBase(); - script_abort_flag = 1; // Force vm to abort ASAP + s->script_abort_flag = 1; // Force vm to abort ASAP return NULL_REG; } @@ -62,9 +62,9 @@ reg_t kGameIsRestarting(EngineState *s, int argc, reg_t *argv) { // LSL3 calculates a machinespeed variable during game startup (right after the filthy questions) // This one would go through w/o throttling resulting in having to do 1000 pushups or something // Another way of handling this would be delaying incrementing of "machineSpeed" selector - if (s->_gameId == "lsl3" && s->currentRoomNumber() == 290) + if (!strcmp(g_sci->getGameID(), "lsl3") && s->currentRoomNumber() == 290) s->_throttleTrigger = true; - if (s->_gameId == "iceman" && s->currentRoomNumber() == 27) { + if (!strcmp(g_sci->getGameID(), "iceman") && s->currentRoomNumber() == 27) { s->_throttleTrigger = true; neededSleep = 60; } @@ -252,10 +252,15 @@ reg_t kMemory(EngineState *s, int argc, reg_t *argv) { break; } case K_MEMORY_PEEK : { + if (!argv[1].segment) { + // This occurs in KQ5CD when interacting with certain objects + warning("Attempt to peek invalid memory at %04x:%04x", PRINT_REG(argv[1])); + return s->r_acc; + } + SegmentRef ref = s->_segMan->dereference(argv[1]); if (!ref.isValid() || ref.maxSize < 2) { - // This occurs in KQ5CD when interacting with certain objects warning("Attempt to peek invalid memory at %04x:%04x", PRINT_REG(argv[1])); return s->r_acc; } @@ -298,9 +303,12 @@ reg_t kMemory(EngineState *s, int argc, reg_t *argv) { reg_t kIconBar(EngineState *s, int argc, reg_t *argv) { // TODO... - if (argv[0].toUint16() == 4 && argv[1].toUint16() == 0) + if (argv[0].toUint16() == 4 && argv[1].toUint16() == 0) { for (int i = 0; i < argv[2].toUint16(); i++) - warning("kIconBar: Icon Object %d = %04x:%04x", i, PRINT_REG(argv[i + 3])); + g_sci->_gfxMacIconBar->addIcon(argv[i + 3]); + + g_sci->_gfxMacIconBar->drawIcons(); + } // Other calls seem to handle selecting/deselecting them diff --git a/engines/sci/engine/kmovement.cpp b/engines/sci/engine/kmovement.cpp index fcaf0d7ea0..499aeabcc6 100644 --- a/engines/sci/engine/kmovement.cpp +++ b/engines/sci/engine/kmovement.cpp @@ -158,8 +158,8 @@ reg_t kSetJump(EngineState *s, int argc, reg_t *argv) { debugC(2, kDebugLevelBresen, "SetJump for object at %04x:%04x", PRINT_REG(object)); debugC(2, kDebugLevelBresen, "xStep: %d, yStep: %d", vx, vy); - PUT_SEL32V(segMan, object, SELECTOR(xStep), vx); - PUT_SEL32V(segMan, object, SELECTOR(yStep), vy); + writeSelectorValue(segMan, object, SELECTOR(xStep), vx); + writeSelectorValue(segMan, object, SELECTOR(yStep), vy); return s->r_acc; } @@ -168,9 +168,9 @@ reg_t kSetJump(EngineState *s, int argc, reg_t *argv) { #define _K_BRESEN_AXIS_Y 1 static void initialize_bresen(SegManager *segMan, int argc, reg_t *argv, reg_t mover, int step_factor, int deltax, int deltay) { - reg_t client = GET_SEL32(segMan, mover, SELECTOR(client)); - int stepx = (int16)GET_SEL32V(segMan, client, SELECTOR(xStep)) * step_factor; - int stepy = (int16)GET_SEL32V(segMan, client, SELECTOR(yStep)) * step_factor; + reg_t client = readSelector(segMan, mover, SELECTOR(client)); + int stepx = (int16)readSelectorValue(segMan, client, SELECTOR(xStep)) * step_factor; + int stepy = (int16)readSelectorValue(segMan, client, SELECTOR(yStep)) * step_factor; int numsteps_x = stepx ? (abs(deltax) + stepx - 1) / stepx : 0; int numsteps_y = stepy ? (abs(deltay) + stepy - 1) / stepy : 0; int bdi, i1; @@ -191,15 +191,15 @@ static void initialize_bresen(SegManager *segMan, int argc, reg_t *argv, reg_t m /* if (abs(deltax) > abs(deltay)) {*/ // Bresenham on y if (numsteps_y < numsteps_x) { - PUT_SEL32V(segMan, mover, SELECTOR(b_xAxis), _K_BRESEN_AXIS_Y); - PUT_SEL32V(segMan, mover, SELECTOR(b_incr), (deltay < 0) ? -1 : 1); + writeSelectorValue(segMan, mover, SELECTOR(b_xAxis), _K_BRESEN_AXIS_Y); + writeSelectorValue(segMan, mover, SELECTOR(b_incr), (deltay < 0) ? -1 : 1); //i1 = 2 * (abs(deltay) - abs(deltay_step * numsteps)) * abs(deltax_step); //bdi = -abs(deltax); i1 = 2 * (abs(deltay) - abs(deltay_step * (numsteps - 1))) * abs(deltax_step); bdi = -abs(deltax); } else { // Bresenham on x - PUT_SEL32V(segMan, mover, SELECTOR(b_xAxis), _K_BRESEN_AXIS_X); - PUT_SEL32V(segMan, mover, SELECTOR(b_incr), (deltax < 0) ? -1 : 1); + writeSelectorValue(segMan, mover, SELECTOR(b_xAxis), _K_BRESEN_AXIS_X); + writeSelectorValue(segMan, mover, SELECTOR(b_incr), (deltax < 0) ? -1 : 1); //i1= 2 * (abs(deltax) - abs(deltax_step * numsteps)) * abs(deltay_step); //bdi = -abs(deltay); i1 = 2 * (abs(deltax) - abs(deltax_step * (numsteps - 1))) * abs(deltay_step); @@ -207,26 +207,26 @@ static void initialize_bresen(SegManager *segMan, int argc, reg_t *argv, reg_t m } - PUT_SEL32V(segMan, mover, SELECTOR(dx), deltax_step); - PUT_SEL32V(segMan, mover, SELECTOR(dy), deltay_step); + writeSelectorValue(segMan, mover, SELECTOR(dx), deltax_step); + writeSelectorValue(segMan, mover, SELECTOR(dy), deltay_step); debugC(2, kDebugLevelBresen, "Init bresen for mover %04x:%04x: d=(%d,%d)", PRINT_REG(mover), deltax, deltay); debugC(2, kDebugLevelBresen, " steps=%d, mv=(%d, %d), i1= %d, i2=%d", numsteps, deltax_step, deltay_step, i1, bdi*2); - //PUT_SEL32V(segMan, mover, SELECTOR(b_movCnt), numsteps); // Needed for HQ1/Ogre? - PUT_SEL32V(segMan, mover, SELECTOR(b_di), bdi); - PUT_SEL32V(segMan, mover, SELECTOR(b_i1), i1); - PUT_SEL32V(segMan, mover, SELECTOR(b_i2), bdi * 2); + //writeSelectorValue(segMan, mover, SELECTOR(b_movCnt), numsteps); // Needed for HQ1/Ogre? + writeSelectorValue(segMan, mover, SELECTOR(b_di), bdi); + writeSelectorValue(segMan, mover, SELECTOR(b_i1), i1); + writeSelectorValue(segMan, mover, SELECTOR(b_i2), bdi * 2); } reg_t kInitBresen(EngineState *s, int argc, reg_t *argv) { SegManager *segMan = s->_segMan; reg_t mover = argv[0]; - reg_t client = GET_SEL32(segMan, mover, SELECTOR(client)); + reg_t client = readSelector(segMan, mover, SELECTOR(client)); - int deltax = (int16)GET_SEL32V(segMan, mover, SELECTOR(x)) - (int16)GET_SEL32V(segMan, client, SELECTOR(x)); - int deltay = (int16)GET_SEL32V(segMan, mover, SELECTOR(y)) - (int16)GET_SEL32V(segMan, client, SELECTOR(y)); + int deltax = (int16)readSelectorValue(segMan, mover, SELECTOR(x)) - (int16)readSelectorValue(segMan, client, SELECTOR(x)); + int deltay = (int16)readSelectorValue(segMan, mover, SELECTOR(y)) - (int16)readSelectorValue(segMan, client, SELECTOR(y)); int step_factor = (argc < 1) ? argv[1].toUint16() : 1; initialize_bresen(s->_segMan, argc, argv, mover, step_factor, deltax, deltay); @@ -240,42 +240,42 @@ reg_t kInitBresen(EngineState *s, int argc, reg_t *argv) { reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) { SegManager *segMan = s->_segMan; reg_t mover = argv[0]; - reg_t client = GET_SEL32(segMan, mover, SELECTOR(client)); + reg_t client = readSelector(segMan, mover, SELECTOR(client)); - int x = (int16)GET_SEL32V(segMan, client, SELECTOR(x)); - int y = (int16)GET_SEL32V(segMan, client, SELECTOR(y)); + int x = (int16)readSelectorValue(segMan, client, SELECTOR(x)); + int y = (int16)readSelectorValue(segMan, client, SELECTOR(y)); int oldx, oldy, destx, desty, dx, dy, bdi, bi1, bi2, movcnt, bdelta, axis; - uint16 signal = GET_SEL32V(segMan, client, SELECTOR(signal)); + uint16 signal = readSelectorValue(segMan, client, SELECTOR(signal)); int completed = 0; - int max_movcnt = GET_SEL32V(segMan, client, SELECTOR(moveSpeed)); + int max_movcnt = readSelectorValue(segMan, client, SELECTOR(moveSpeed)); if (getSciVersion() > SCI_VERSION_01) signal &= ~kSignalHitObstacle; - PUT_SEL32(segMan, client, SELECTOR(signal), make_reg(0, signal)); // This is a NOP for SCI0 + writeSelector(segMan, client, SELECTOR(signal), make_reg(0, signal)); // This is a NOP for SCI0 oldx = x; oldy = y; - destx = (int16)GET_SEL32V(segMan, mover, SELECTOR(x)); - desty = (int16)GET_SEL32V(segMan, mover, SELECTOR(y)); - dx = (int16)GET_SEL32V(segMan, mover, SELECTOR(dx)); - dy = (int16)GET_SEL32V(segMan, mover, SELECTOR(dy)); - bdi = (int16)GET_SEL32V(segMan, mover, SELECTOR(b_di)); - bi1 = (int16)GET_SEL32V(segMan, mover, SELECTOR(b_i1)); - bi2 = (int16)GET_SEL32V(segMan, mover, SELECTOR(b_i2)); - movcnt = GET_SEL32V(segMan, mover, SELECTOR(b_movCnt)); - bdelta = (int16)GET_SEL32V(segMan, mover, SELECTOR(b_incr)); - axis = (int16)GET_SEL32V(segMan, mover, SELECTOR(b_xAxis)); + destx = (int16)readSelectorValue(segMan, mover, SELECTOR(x)); + desty = (int16)readSelectorValue(segMan, mover, SELECTOR(y)); + dx = (int16)readSelectorValue(segMan, mover, SELECTOR(dx)); + dy = (int16)readSelectorValue(segMan, mover, SELECTOR(dy)); + bdi = (int16)readSelectorValue(segMan, mover, SELECTOR(b_di)); + bi1 = (int16)readSelectorValue(segMan, mover, SELECTOR(b_i1)); + bi2 = (int16)readSelectorValue(segMan, mover, SELECTOR(b_i2)); + movcnt = readSelectorValue(segMan, mover, SELECTOR(b_movCnt)); + bdelta = (int16)readSelectorValue(segMan, mover, SELECTOR(b_incr)); + axis = (int16)readSelectorValue(segMan, mover, SELECTOR(b_xAxis)); //printf("movecnt %d, move speed %d\n", movcnt, max_movcnt); if (g_sci->_features->handleMoveCount()) { if (max_movcnt > movcnt) { ++movcnt; - PUT_SEL32V(segMan, mover, SELECTOR(b_movCnt), movcnt); // Needed for HQ1/Ogre? + writeSelectorValue(segMan, mover, SELECTOR(b_movCnt), movcnt); // Needed for HQ1/Ogre? return NULL_REG; } else { movcnt = 0; - PUT_SEL32V(segMan, mover, SELECTOR(b_movCnt), movcnt); // Needed for HQ1/Ogre? + writeSelectorValue(segMan, mover, SELECTOR(b_movCnt), movcnt); // Needed for HQ1/Ogre? } } @@ -288,7 +288,7 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) { dy += bdelta; } - PUT_SEL32V(segMan, mover, SELECTOR(b_di), bdi); + writeSelectorValue(segMan, mover, SELECTOR(b_di), bdi); x += dx; y += dy; @@ -310,33 +310,32 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) { debugC(2, kDebugLevelBresen, "Finished mover %04x:%04x", PRINT_REG(mover)); } - PUT_SEL32V(segMan, client, SELECTOR(x), x); - PUT_SEL32V(segMan, client, SELECTOR(y), y); + writeSelectorValue(segMan, client, SELECTOR(x), x); + writeSelectorValue(segMan, client, SELECTOR(y), y); debugC(2, kDebugLevelBresen, "New data: (x,y)=(%d,%d), di=%d", x, y, bdi); if (g_sci->getKernel()->_selectorCache.cantBeHere != -1) { - invoke_selector(INV_SEL(s, client, cantBeHere, kStopOnInvalidSelector), 0); + invokeSelector(INV_SEL(s, client, cantBeHere, kStopOnInvalidSelector), 0); s->r_acc = make_reg(0, !s->r_acc.offset); } else { - invoke_selector(INV_SEL(s, client, canBeHere, kStopOnInvalidSelector), 0); + invokeSelector(INV_SEL(s, client, canBeHere, kStopOnInvalidSelector), 0); } if (!s->r_acc.offset) { // Contains the return value - signal = GET_SEL32V(segMan, client, SELECTOR(signal)); + signal = readSelectorValue(segMan, client, SELECTOR(signal)); - PUT_SEL32V(segMan, client, SELECTOR(x), oldx); - PUT_SEL32V(segMan, client, SELECTOR(y), oldy); - PUT_SEL32V(segMan, client, SELECTOR(signal), (signal | kSignalHitObstacle)); + writeSelectorValue(segMan, client, SELECTOR(x), oldx); + writeSelectorValue(segMan, client, SELECTOR(y), oldy); + writeSelectorValue(segMan, client, SELECTOR(signal), (signal | kSignalHitObstacle)); debugC(2, kDebugLevelBresen, "Finished mover %04x:%04x by collision", PRINT_REG(mover)); completed = 1; } - // FIXME: find out why iceman needs this and we ask for version > SCI01 - if ((getSciVersion() > SCI_VERSION_01) || (s->_gameId == "iceman")) + if ((getSciVersion() >= SCI_VERSION_1_EGA)) if (completed) - invoke_selector(INV_SEL(s, mover, moveDone, kStopOnInvalidSelector), 0); + invokeSelector(INV_SEL(s, mover, moveDone, kStopOnInvalidSelector), 0); return make_reg(0, completed); } @@ -378,15 +377,15 @@ reg_t kDoAvoider(EngineState *s, int argc, reg_t *argv) { return NULL_REG; } - client = GET_SEL32(segMan, avoider, SELECTOR(client)); + client = readSelector(segMan, avoider, SELECTOR(client)); if (!s->_segMan->isHeapObject(client)) { warning("DoAvoider() where client %04x:%04x is not an object", PRINT_REG(client)); return NULL_REG; } - looper = GET_SEL32(segMan, client, SELECTOR(looper)); - mover = GET_SEL32(segMan, client, SELECTOR(mover)); + looper = readSelector(segMan, client, SELECTOR(looper)); + mover = readSelector(segMan, client, SELECTOR(mover)); if (!s->_segMan->isHeapObject(mover)) { if (mover.segment) { @@ -395,38 +394,38 @@ reg_t kDoAvoider(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } - destx = GET_SEL32V(segMan, mover, SELECTOR(x)); - desty = GET_SEL32V(segMan, mover, SELECTOR(y)); + destx = readSelectorValue(segMan, mover, SELECTOR(x)); + desty = readSelectorValue(segMan, mover, SELECTOR(y)); debugC(2, kDebugLevelBresen, "Doing avoider %04x:%04x (dest=%d,%d)", PRINT_REG(avoider), destx, desty); - if (invoke_selector(INV_SEL(s, mover, doit, kContinueOnInvalidSelector) , 0)) { + if (invokeSelector(INV_SEL(s, mover, doit, kContinueOnInvalidSelector) , 0)) { error("Mover %04x:%04x of avoider %04x:%04x doesn't have a doit() funcselector", PRINT_REG(mover), PRINT_REG(avoider)); return NULL_REG; } - mover = GET_SEL32(segMan, client, SELECTOR(mover)); + mover = readSelector(segMan, client, SELECTOR(mover)); if (!mover.segment) // Mover has been disposed? return s->r_acc; // Return gracefully. - if (invoke_selector(INV_SEL(s, client, isBlocked, kContinueOnInvalidSelector) , 0)) { + if (invokeSelector(INV_SEL(s, client, isBlocked, kContinueOnInvalidSelector) , 0)) { error("Client %04x:%04x of avoider %04x:%04x doesn't" " have an isBlocked() funcselector", PRINT_REG(client), PRINT_REG(avoider)); return NULL_REG; } - dx = destx - GET_SEL32V(segMan, client, SELECTOR(x)); - dy = desty - GET_SEL32V(segMan, client, SELECTOR(y)); + dx = destx - readSelectorValue(segMan, client, SELECTOR(x)); + dy = desty - readSelectorValue(segMan, client, SELECTOR(y)); angle = getAngle(dx, dy); debugC(2, kDebugLevelBresen, "Movement (%d,%d), angle %d is %sblocked", dx, dy, angle, (s->r_acc.offset) ? " " : "not "); if (s->r_acc.offset) { // isBlocked() returned non-zero int rotation = (rand() & 1) ? 45 : (360 - 45); // Clockwise/counterclockwise - int oldx = GET_SEL32V(segMan, client, SELECTOR(x)); - int oldy = GET_SEL32V(segMan, client, SELECTOR(y)); - int xstep = GET_SEL32V(segMan, client, SELECTOR(xStep)); - int ystep = GET_SEL32V(segMan, client, SELECTOR(yStep)); + int oldx = readSelectorValue(segMan, client, SELECTOR(x)); + int oldy = readSelectorValue(segMan, client, SELECTOR(y)); + int xstep = readSelectorValue(segMan, client, SELECTOR(xStep)); + int ystep = readSelectorValue(segMan, client, SELECTOR(yStep)); int moves; debugC(2, kDebugLevelBresen, " avoider %04x:%04x", PRINT_REG(avoider)); @@ -435,23 +434,23 @@ reg_t kDoAvoider(EngineState *s, int argc, reg_t *argv) { int move_x = (int)(sin(angle * PI / 180.0) * (xstep)); int move_y = (int)(-cos(angle * PI / 180.0) * (ystep)); - PUT_SEL32V(segMan, client, SELECTOR(x), oldx + move_x); - PUT_SEL32V(segMan, client, SELECTOR(y), oldy + move_y); + writeSelectorValue(segMan, client, SELECTOR(x), oldx + move_x); + writeSelectorValue(segMan, client, SELECTOR(y), oldy + move_y); debugC(2, kDebugLevelBresen, "Pos (%d,%d): Trying angle %d; delta=(%d,%d)", oldx, oldy, angle, move_x, move_y); - if (invoke_selector(INV_SEL(s, client, canBeHere, kContinueOnInvalidSelector) , 0)) { + if (invokeSelector(INV_SEL(s, client, canBeHere, kContinueOnInvalidSelector) , 0)) { error("Client %04x:%04x of avoider %04x:%04x doesn't" " have a canBeHere() funcselector", PRINT_REG(client), PRINT_REG(avoider)); return NULL_REG; } - PUT_SEL32V(segMan, client, SELECTOR(x), oldx); - PUT_SEL32V(segMan, client, SELECTOR(y), oldy); + writeSelectorValue(segMan, client, SELECTOR(x), oldx); + writeSelectorValue(segMan, client, SELECTOR(y), oldy); if (s->r_acc.offset) { // We can be here debugC(2, kDebugLevelBresen, "Success"); - PUT_SEL32V(segMan, client, SELECTOR(heading), angle); + writeSelectorValue(segMan, client, SELECTOR(heading), angle); return make_reg(0, angle); } @@ -464,17 +463,17 @@ reg_t kDoAvoider(EngineState *s, int argc, reg_t *argv) { warning("DoAvoider failed for avoider %04x:%04x", PRINT_REG(avoider)); } else { - int heading = GET_SEL32V(segMan, client, SELECTOR(heading)); + int heading = readSelectorValue(segMan, client, SELECTOR(heading)); if (heading == -1) return s->r_acc; // No change - PUT_SEL32V(segMan, client, SELECTOR(heading), angle); + writeSelectorValue(segMan, client, SELECTOR(heading), angle); s->r_acc = make_reg(0, angle); if (looper.segment) { - if (invoke_selector(INV_SEL(s, looper, doit, kContinueOnInvalidSelector), 2, angle, client)) { + if (invokeSelector(INV_SEL(s, looper, doit, kContinueOnInvalidSelector), 2, angle, client)) { error("Looper %04x:%04x of avoider %04x:%04x doesn't" " have a doit() funcselector", PRINT_REG(looper), PRINT_REG(avoider)); } else diff --git a/engines/sci/engine/kparse.cpp b/engines/sci/engine/kparse.cpp index 0254d21642..785ff39d22 100644 --- a/engines/sci/engine/kparse.cpp +++ b/engines/sci/engine/kparse.cpp @@ -42,6 +42,7 @@ reg_t kSaid(EngineState *s, int argc, reg_t *argv) { reg_t heap_said_block = argv[0]; byte *said_block; int new_lastmatch; + Vocabulary *voc = g_sci->getVocabulary(); #ifdef DEBUG_PARSER const int debug_parser = 1; #else @@ -63,7 +64,7 @@ reg_t kSaid(EngineState *s, int argc, reg_t *argv) { s->_voc->decipherSaidBlock(said_block); #endif - if (s->_voc->parser_event.isNull() || (GET_SEL32V(s->_segMan, s->_voc->parser_event, SELECTOR(claimed)))) { + if (voc->parser_event.isNull() || (readSelectorValue(s->_segMan, voc->parser_event, SELECTOR(claimed)))) { return NULL_REG; } @@ -77,7 +78,7 @@ reg_t kSaid(EngineState *s, int argc, reg_t *argv) { s->r_acc = make_reg(0, 1); if (new_lastmatch != SAID_PARTIAL_MATCH) - PUT_SEL32V(s->_segMan, s->_voc->parser_event, SELECTOR(claimed), 1); + writeSelectorValue(s->_segMan, voc->parser_event, SELECTOR(claimed), 1); } else { return NULL_REG; @@ -92,15 +93,15 @@ reg_t kParse(EngineState *s, int argc, reg_t *argv) { char *error; ResultWordList words; reg_t event = argv[1]; - Vocabulary *voc = s->_voc; + Vocabulary *voc = g_sci->getVocabulary(); - s->_voc->parser_event = event; + voc->parser_event = event; bool res = voc->tokenizeString(words, string.c_str(), &error); - s->_voc->parserIsValid = false; /* not valid */ + voc->parserIsValid = false; /* not valid */ if (res && !words.empty()) { - s->_voc->synonymizeTokens(words); + voc->synonymizeTokens(words); s->r_acc = make_reg(0, 1); @@ -115,32 +116,32 @@ reg_t kParse(EngineState *s, int argc, reg_t *argv) { if (syntax_fail) { s->r_acc = make_reg(0, 1); - PUT_SEL32V(segMan, event, SELECTOR(claimed), 1); + writeSelectorValue(segMan, event, SELECTOR(claimed), 1); - invoke_selector(INV_SEL(s, s->_gameObj, syntaxFail, kStopOnInvalidSelector), 2, s->_voc->parser_base, stringpos); + invokeSelector(INV_SEL(s, s->_gameObj, syntaxFail, kStopOnInvalidSelector), 2, voc->parser_base, stringpos); /* Issue warning */ debugC(2, kDebugLevelParser, "Tree building failed"); } else { - s->_voc->parserIsValid = true; - PUT_SEL32V(segMan, event, SELECTOR(claimed), 0); + voc->parserIsValid = true; + writeSelectorValue(segMan, event, SELECTOR(claimed), 0); #ifdef DEBUG_PARSER - s->_voc->dumpParseTree(); + voc->dumpParseTree(); #endif } } else { s->r_acc = make_reg(0, 0); - PUT_SEL32V(segMan, event, SELECTOR(claimed), 1); + writeSelectorValue(segMan, event, SELECTOR(claimed), 1); if (error) { - s->_segMan->strcpy(s->_voc->parser_base, error); + s->_segMan->strcpy(voc->parser_base, error); debugC(2, kDebugLevelParser, "Word unknown: %s", error); /* Issue warning: */ - invoke_selector(INV_SEL(s, s->_gameObj, wordFail, kStopOnInvalidSelector), 2, s->_voc->parser_base, stringpos); + invokeSelector(INV_SEL(s, s->_gameObj, wordFail, kStopOnInvalidSelector), 2, voc->parser_base, stringpos); free(error); return make_reg(0, 1); /* Tell them that it didn't work */ } @@ -156,28 +157,29 @@ reg_t kSetSynonyms(EngineState *s, int argc, reg_t *argv) { Node *node; int script; int numSynonyms = 0; + Vocabulary *voc = g_sci->getVocabulary(); // Only SCI0-SCI1 EGA games had a parser. In newer versions, this is a stub if (getSciVersion() > SCI_VERSION_1_EGA) return s->r_acc; - s->_voc->clearSynonyms(); + voc->clearSynonyms(); - list = s->_segMan->lookupList(GET_SEL32(segMan, object, SELECTOR(elements))); + list = s->_segMan->lookupList(readSelector(segMan, object, SELECTOR(elements))); node = s->_segMan->lookupNode(list->first); while (node) { reg_t objpos = node->value; int seg; - script = GET_SEL32V(segMan, objpos, SELECTOR(number)); + script = readSelectorValue(segMan, objpos, SELECTOR(number)); seg = s->_segMan->getScriptSegment(script); if (seg > 0) numSynonyms = s->_segMan->getScript(seg)->getSynonymsNr(); if (numSynonyms) { - byte *synonyms = s->_segMan->getScript(seg)->getSynonyms(); + const byte *synonyms = s->_segMan->getScript(seg)->getSynonyms(); if (synonyms) { debugC(2, kDebugLevelParser, "Setting %d synonyms for script.%d", @@ -193,7 +195,7 @@ reg_t kSetSynonyms(EngineState *s, int argc, reg_t *argv) { synonym_t tmp; tmp.replaceant = (int16)READ_LE_UINT16(synonyms + i * 4); tmp.replacement = (int16)READ_LE_UINT16(synonyms + i * 4 + 2); - s->_voc->addSynonym(tmp); + voc->addSynonym(tmp); } } else warning("Synonyms of script.%03d were requested, but script is not available", script); diff --git a/engines/sci/engine/kpathing.cpp b/engines/sci/engine/kpathing.cpp index 25d967c247..857ccc2a08 100644 --- a/engines/sci/engine/kpathing.cpp +++ b/engines/sci/engine/kpathing.cpp @@ -337,15 +337,15 @@ static void draw_point(EngineState *s, Common::Point p, int start, int width, in static void draw_polygon(EngineState *s, reg_t polygon, int width, int height) { SegManager *segMan = s->_segMan; - reg_t points = GET_SEL32(segMan, polygon, SELECTOR(points)); + reg_t points = readSelector(segMan, polygon, SELECTOR(points)); #ifdef ENABLE_SCI32 if (segMan->isHeapObject(points)) - points = GET_SEL32(segMan, points, SELECTOR(data)); + points = readSelector(segMan, points, SELECTOR(data)); #endif - int size = GET_SEL32V(segMan, polygon, SELECTOR(size)); - int type = GET_SEL32V(segMan, polygon, SELECTOR(type)); + int size = readSelectorValue(segMan, polygon, SELECTOR(size)); + int type = readSelectorValue(segMan, polygon, SELECTOR(type)); Common::Point first, prev; int i; @@ -386,15 +386,15 @@ static void draw_input(EngineState *s, reg_t poly_list, Common::Point start, Com } static void print_polygon(SegManager *segMan, reg_t polygon) { - reg_t points = GET_SEL32(segMan, polygon, SELECTOR(points)); + reg_t points = readSelector(segMan, polygon, SELECTOR(points)); #ifdef ENABLE_SCI32 if (segMan->isHeapObject(points)) - points = GET_SEL32(segMan, points, SELECTOR(data)); + points = readSelector(segMan, points, SELECTOR(data)); #endif - int size = GET_SEL32V(segMan, polygon, SELECTOR(size)); - int type = GET_SEL32V(segMan, polygon, SELECTOR(type)); + int size = readSelectorValue(segMan, polygon, SELECTOR(size)); + int type = readSelectorValue(segMan, polygon, SELECTOR(type)); int i; Common::Point point; @@ -1036,13 +1036,13 @@ static Polygon *convert_polygon(EngineState *s, reg_t polygon) { // Returns : (Polygon *) The converted polygon, or NULL on error SegManager *segMan = s->_segMan; int i; - reg_t points = GET_SEL32(segMan, polygon, SELECTOR(points)); - int size = GET_SEL32V(segMan, polygon, SELECTOR(size)); + reg_t points = readSelector(segMan, polygon, SELECTOR(points)); + int size = readSelectorValue(segMan, polygon, SELECTOR(size)); #ifdef ENABLE_SCI32 // SCI32 stores the actual points in the data property of points (in a new array) if (segMan->isHeapObject(points)) - points = GET_SEL32(segMan, points, SELECTOR(data)); + points = readSelector(segMan, points, SELECTOR(data)); #endif if (size == 0) { @@ -1050,13 +1050,13 @@ static Polygon *convert_polygon(EngineState *s, reg_t polygon) { return NULL; } - Polygon *poly = new Polygon(GET_SEL32V(segMan, polygon, SELECTOR(type))); + Polygon *poly = new Polygon(readSelectorValue(segMan, polygon, SELECTOR(type))); int skip = 0; // WORKAROUND: broken polygon in lsl1sci, room 350, after opening elevator // Polygon has 17 points but size is set to 19 - if ((size == 19) && (s->_gameId == "lsl1sci")) { + if ((size == 19) && !strcmp(g_sci->getGameID(), "lsl1sci")) { if ((s->currentRoomNumber() == 350) && (read_point(segMan, points, 18) == Common::Point(108, 137))) { debug(1, "Applying fix for broken polygon in lsl1sci, room 350"); @@ -1121,7 +1121,7 @@ static PathfindingState *convert_polygon_set(EngineState *s, reg_t poly_list, Co if (polygon) { pf_s->polygons.push_back(polygon); - count += GET_SEL32V(segMan, node->value, SELECTOR(size)); + count += readSelectorValue(segMan, node->value, SELECTOR(size)); } node = s->_segMan->lookupNode(node->succ); @@ -1174,7 +1174,7 @@ static PathfindingState *convert_polygon_set(EngineState *s, reg_t poly_list, Co // WORKAROUND LSL5 room 660. Priority glitch due to us choosing a different path // than SSCI. Happens when Patti walks to the control room. - if ((s->_gameId == "lsl5") && (s->currentRoomNumber() == 660) && (Common::Point(67, 131) == *new_start) && (Common::Point(229, 101) == *new_end)) { + if (!strcmp(g_sci->getGameID(), "lsl5") && (s->currentRoomNumber() == 660) && (Common::Point(67, 131) == *new_start) && (Common::Point(229, 101) == *new_end)) { debug(1, "[avoidpath] Applying fix for priority problem in LSL5, room 660"); pf_s->_prependPoint = new_start; new_start = new Common::Point(77, 107); @@ -1394,7 +1394,7 @@ reg_t kAvoidPath(EngineState *s, int argc, reg_t *argv) { if (argc < 7) error("[avoidpath] Not enough arguments"); - poly_list = (!argv[4].isNull() ? GET_SEL32(s->_segMan, argv[4], SELECTOR(elements)) : NULL_REG); + poly_list = (!argv[4].isNull() ? readSelector(s->_segMan, argv[4], SELECTOR(elements)) : NULL_REG); width = argv[5].toUint16(); height = argv[6].toUint16(); if (argc > 7) @@ -1694,4 +1694,38 @@ reg_t kIntersections(EngineState *s, int argc, reg_t *argv) { } } +// This is a quite rare kernel function. An example of when it's called +// is in QFG1VGA, after killing any monster. +reg_t kMergePoly(EngineState *s, int argc, reg_t *argv) { + // 3 parameters: raw polygon data, polygon list, list size + reg_t polygonData = argv[0]; + + // TODO: actually merge the polygon + // In QFG1VGA, there are no immediately visible side-effects + // of this being a stub. + +#if 0 + List *list = s->_segMan->lookupList(argv[1]); + Node *node = s->_segMan->lookupNode(list->first); + // List size is not needed + + Polygon *polygon; + int count = 0; + + while (node) { + polygon = convert_polygon(s, node->value); + + if (polygon) { + count += readSelectorValue(s->_segMan, node->value, SELECTOR(size)); + } + + node = s->_segMan->lookupNode(node->succ); + } +#endif + + warning("Stub: kMergePoly"); + + return polygonData; +} + } // End of namespace Sci diff --git a/engines/sci/engine/kscripts.cpp b/engines/sci/engine/kscripts.cpp index ba29f64966..722d0175d1 100644 --- a/engines/sci/engine/kscripts.cpp +++ b/engines/sci/engine/kscripts.cpp @@ -111,7 +111,7 @@ reg_t kResCheck(EngineState *s, int argc, reg_t *argv) { reg_t kClone(EngineState *s, int argc, reg_t *argv) { reg_t parent_addr = argv[0]; - Object *parent_obj = s->_segMan->getObject(parent_addr); + const Object *parent_obj = s->_segMan->getObject(parent_addr); reg_t clone_addr; Clone *clone_obj; // same as Object* @@ -132,7 +132,7 @@ reg_t kClone(EngineState *s, int argc, reg_t *argv) { *clone_obj = *parent_obj; // Mark as clone - clone_obj->setInfoSelector(make_reg(0, SCRIPT_INFO_CLONE)); + clone_obj->markAsClone(); clone_obj->setSpeciesSelector(clone_obj->getPos()); if (parent_obj->isClass()) clone_obj->setSuperClassSelector(parent_obj->getPos()); @@ -154,14 +154,14 @@ reg_t kDisposeClone(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } - if (victim_obj->getInfoSelector().offset != SCRIPT_INFO_CLONE) { + if (!victim_obj->isClone()) { //warning("Attempt to dispose something other than a clone at %04x", offset); // SCI silently ignores this behaviour; some games actually depend on it return s->r_acc; } // QFG3 clears clones with underbits set - //if (GET_SEL32V(victim_addr, underBits)) + //if (readSelectorValue(victim_addr, underBits)) // warning("Clone %04x:%04x was cleared with underBits set", PRINT_REG(victim_addr)); #if 0 @@ -181,7 +181,7 @@ reg_t kDisposeClone(EngineState *s, int argc, reg_t *argv) { // Returns script dispatch address index in the supplied script reg_t kScriptID(EngineState *s, int argc, reg_t *argv) { int script = argv[0].toUint16(); - int index = (argc > 1) ? argv[1].toUint16() : 0; + uint16 index = (argc > 1) ? argv[1].toUint16() : 0; if (argv[0].segment) return argv[0]; @@ -193,18 +193,30 @@ reg_t kScriptID(EngineState *s, int argc, reg_t *argv) { Script *scr = s->_segMan->getScript(scriptSeg); - if (!scr->_numExports) { - // FIXME: Is this fatal? This occurs in SQ4CD - warning("Script 0x%x does not have a dispatch table", script); + if (!scr->getExportsNr()) { + // This is normal. Some scripts don't have a dispatch (exports) table, + // and this call is probably used to load them in memory, ignoring + // the return value. If only one argument is passed, this call is done + // only to load the script in memory. Thus, don't show any warning, + // as no return value is expected + if (argc == 2) + warning("Script 0x%x does not have a dispatch table and export %d " + "was requested from it", script, index); return NULL_REG; } - if (index > scr->_numExports) { - error("Dispatch index too big: %d > %d", index, scr->_numExports); + if (index > scr->getExportsNr()) { + error("Dispatch index too big: %d > %d", index, scr->getExportsNr()); return NULL_REG; } - return make_reg(scriptSeg, scr->validateExportFunc(index)); + uint16 address = scr->validateExportFunc(index); + + // Point to the heap for SCI1.1+ games + if (getSciVersion() >= SCI_VERSION_1_1) + address += scr->getScriptSize(); + + return make_reg(scriptSeg, address); } reg_t kDisposeScript(EngineState *s, int argc, reg_t *argv) { @@ -244,7 +256,7 @@ reg_t kRespondsTo(EngineState *s, int argc, reg_t *argv) { reg_t obj = argv[0]; int selector = argv[1].toUint16(); - return make_reg(0, s->_segMan->isHeapObject(obj) && lookup_selector(s->_segMan, obj, selector, NULL, NULL) != kSelectorNone); + return make_reg(0, s->_segMan->isHeapObject(obj) && lookupSelector(s->_segMan, obj, selector, NULL, NULL) != kSelectorNone); } } // End of namespace Sci diff --git a/engines/sci/engine/kstring.cpp b/engines/sci/engine/kstring.cpp index 426c682e11..2681b612e9 100644 --- a/engines/sci/engine/kstring.cpp +++ b/engines/sci/engine/kstring.cpp @@ -138,10 +138,36 @@ reg_t kReadNumber(EngineState *s, int argc, reg_t *argv) { while (isspace((unsigned char)*source)) source++; /* Skip whitespace */ - if (*source == '$') /* SCI uses this for hex numbers */ - return make_reg(0, (int16)strtol(source + 1, NULL, 16)); /* Hex */ - else - return make_reg(0, (int16)strtol(source, NULL, 10)); /* Force decimal */ + int16 result = 0; + + if (*source == '$') { + // hexadecimal input + result = (int16)strtol(source + 1, NULL, 16); + } else { + // decimal input, we can not use strtol/atoi in here, because sierra used atoi BUT it was a non standard compliant + // atoi, that didnt do clipping. In SQ4 we get the door code in here and that's even larger than uint32! + if (*source == '-') { + result = -1; + source++; + } + while (*source) { + if ((*source < '0') || (*source > '9')) { + // Sierras atoi stopped processing at anything different than number + // Sometimes the input has a trailing space, that's fine (example: lsl3) + if (*source != ' ') { + // TODO: this happens in lsl5 right in the intro -> we get '1' '3' 0xCD 0xCD 0xCD 0xCD 0xCD + // find out why this happens and fix it + warning("Invalid character in kReadNumber input"); + } + break; + } + result *= 10; + result += *source - 0x30; + source++; + } + } + + return make_reg(0, result); } @@ -241,7 +267,7 @@ reg_t kFormat(EngineState *s, int argc, reg_t *argv) { #ifdef ENABLE_SCI32 // If the string is a string object, get to the actual string in the data selector if (s->_segMan->isObject(reg)) - reg = GET_SEL32(s->_segMan, reg, SELECTOR(data)); + reg = readSelector(s->_segMan, reg, SELECTOR(data)); #endif Common::String tempsource = (reg == NULL_REG) ? "" : g_sci->getKernel()->lookupText(reg, diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp index fef2b9a19e..9bf23dedf5 100644 --- a/engines/sci/engine/savegame.cpp +++ b/engines/sci/engine/savegame.cpp @@ -383,7 +383,7 @@ void EngineState::saveLoadWithSerializer(Common::Serializer &s) { sync_SegManagerPtr(s, _segMan); - syncArray<Class>(s, _segMan->_classtable); + syncArray<Class>(s, _segMan->_classTable); #ifdef USE_OLD_MUSIC_FUNCTIONS sync_songlib(s, _sound._songlib); @@ -541,8 +541,8 @@ void Script::saveLoadWithSerializer(Common::Serializer &s) { } } - s.syncAsSint32LE(_numExports); - s.syncAsSint32LE(_numSynonyms); + s.skip(4, VER(9), VER(19)); // OBSOLETE: Used to be _numExports + s.skip(4, VER(9), VER(19)); // OBSOLETE: Used to be _numSynonyms s.syncAsSint32LE(_lockers); // Sync _objects. This is a hashmap, and we use the following on disk format: @@ -615,7 +615,7 @@ void DynMem::saveLoadWithSerializer(Common::Serializer &s) { void DataStack::saveLoadWithSerializer(Common::Serializer &s) { s.syncAsUint32LE(_capacity); if (s.isLoading()) { - //free(entries); + free(_entries); _entries = (reg_t *)calloc(_capacity, sizeof(reg_t)); } } @@ -731,15 +731,6 @@ int gamestate_save(EngineState *s, Common::WriteStream *fh, const char* savename return 1; } -/* - if (s->sound_server) { - if ((s->sound_server->save)(s, dirname)) { - warning("Saving failed for the sound subsystem"); - //chdir(".."); - return 1; - } - } -*/ Common::Serializer ser(0, fh); sync_SavegameMetadata(ser, meta); Graphics::saveThumbnail(*fh); @@ -748,26 +739,6 @@ int gamestate_save(EngineState *s, Common::WriteStream *fh, const char* savename return 0; } -static byte *find_unique_script_block(EngineState *s, byte *buf, int type) { - bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); - - if (oldScriptHeader) - buf += 2; - - do { - int seeker_type = READ_LE_UINT16(buf); - - if (seeker_type == 0) break; - if (seeker_type == type) return buf; - - int seeker_size = READ_LE_UINT16(buf + 2); - assert(seeker_size > 0); - buf += seeker_size; - } while (1); - - return NULL; -} - // TODO: This should probably be turned into an EngineState or DataStack method. static void reconstruct_stack(EngineState *retval) { SegmentId stack_seg = retval->_segMan->findSegmentByType(SEG_TYPE_STACK); @@ -777,99 +748,37 @@ static void reconstruct_stack(EngineState *retval) { retval->stack_top = stack->_entries + stack->_capacity; } -static void load_script(EngineState *s, Script *scr) { - scr->_buf = (byte *)malloc(scr->_bufSize); - assert(scr->_buf); - - Resource *script = g_sci->getResMan()->findResource(ResourceId(kResourceTypeScript, scr->_nr), 0); - assert(script != 0); - - assert(scr->_bufSize >= script->size); - memcpy(scr->_buf, script->data, script->size); - - if (getSciVersion() >= SCI_VERSION_1_1) { - Resource *heap = g_sci->getResMan()->findResource(ResourceId(kResourceTypeHeap, scr->_nr), 0); - assert(heap != 0); - - scr->_heapStart = scr->_buf + scr->_scriptSize; - - assert(scr->_bufSize - scr->_scriptSize <= heap->size); - memcpy(scr->_heapStart, heap->data, heap->size); - } -} - // TODO: Move thie function to a more appropriate place, such as vm.cpp or script.cpp void SegManager::reconstructScripts(EngineState *s) { uint i; - SegmentObj *mobj; for (i = 0; i < _heap.size(); i++) { - mobj = _heap[i]; - if (!mobj || mobj->getType() != SEG_TYPE_SCRIPT) + if (!_heap[i] || _heap[i]->getType() != SEG_TYPE_SCRIPT) continue; - Script *scr = (Script *)mobj; - - // FIXME: Unify this code with script_instantiate_* ? - load_script(s, scr); + Script *scr = (Script *)_heap[i]; + scr->load(g_sci->getResMan()); scr->_localsBlock = (scr->_localsSegment == 0) ? NULL : (LocalVariables *)(_heap[scr->_localsSegment]); - if (getSciVersion() >= SCI_VERSION_1_1) { - scr->_exportTable = 0; - scr->_synonyms = 0; - if (READ_LE_UINT16(scr->_buf + 6) > 0) { - scr->setExportTableOffset(6); - s->_segMan->scriptRelocateExportsSci11(i); - } - } else { - scr->_exportTable = (uint16 *) find_unique_script_block(s, scr->_buf, SCI_OBJ_EXPORTS); - scr->_synonyms = find_unique_script_block(s, scr->_buf, SCI_OBJ_SYNONYMS); - scr->_exportTable += 3; - } - scr->_codeBlocks.clear(); - ObjMap::iterator it; - const ObjMap::iterator end = scr->_objects.end(); - for (it = scr->_objects.begin(); it != end; ++it) { - byte *data = scr->_buf + it->_value.getPos().offset; - it->_value._baseObj = data; - } + for (ObjMap::iterator it = scr->_objects.begin(); it != scr->_objects.end(); ++it) + it->_value._baseObj = scr->_buf + it->_value.getPos().offset; } for (i = 0; i < _heap.size(); i++) { - mobj = _heap[i]; - if (!mobj || mobj->getType() != SEG_TYPE_SCRIPT) + if (!_heap[i] || _heap[i]->getType() != SEG_TYPE_SCRIPT) continue; - Script *scr = (Script *)mobj; + Script *scr = (Script *)_heap[i]; - // FIXME: Unify this code with Script::scriptObjInit ? - ObjMap::iterator it; - const ObjMap::iterator end = scr->_objects.end(); - for (it = scr->_objects.begin(); it != end; ++it) { - byte *data = scr->_buf + it->_value.getPos().offset; - - if (getSciVersion() >= SCI_VERSION_1_1) { - uint16 *funct_area = (uint16 *)(scr->_buf + READ_LE_UINT16( data + 6 )); - uint16 *prop_area = (uint16 *)(scr->_buf + READ_LE_UINT16( data + 4 )); - - it->_value._baseMethod = funct_area; - it->_value._baseVars = prop_area; - } else { - int funct_area = READ_LE_UINT16(data + SCRIPT_FUNCTAREAPTR_OFFSET); - Object *_baseObj; - - _baseObj = s->_segMan->getObject(it->_value.getSpeciesSelector()); - - if (!_baseObj) { - warning("Object without a base class: Script %d, index %d (reg address %04x:%04x", - scr->_nr, i, PRINT_REG(it->_value.getSpeciesSelector())); - continue; - } - it->_value.setVarCount(_baseObj->getVarCount()); - it->_value._baseObj = _baseObj->_baseObj; + for (ObjMap::iterator it = scr->_objects.begin(); it != scr->_objects.end(); ++it) { + reg_t addr = it->_value.getPos(); + Object *obj = scr->scriptObjInit(addr, false); - it->_value._baseMethod = (uint16 *)(data + funct_area); - it->_value._baseVars = (uint16 *)(data + it->_value.getVarCount() * 2 + SCRIPT_SELECTOR_OFFSET); + if (getSciVersion() < SCI_VERSION_1_1) { + if (!obj->initBaseObject(this, addr, false)) { + warning("Failed to locate base object for object at %04X:%04X; skipping", PRINT_REG(addr)); + scr->scriptObjRemove(addr); + } } } } @@ -912,7 +821,6 @@ static void reconstruct_sounds(EngineState *s) { #endif void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) { - EngineState *retval; #ifdef USE_OLD_MUSIC_FUNCTIONS SongLibrary temp; #endif @@ -947,86 +855,68 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) { thumbnail = 0; } - // Create a new EngineState object - retval = new EngineState(s->_voc, s->_segMan); - retval->_event = s->_event; - - // Copy some old data - retval->_soundCmd = s->_soundCmd; - - // Copy memory segment - retval->_memorySegmentSize = s->_memorySegmentSize; - memcpy(retval->_memorySegment, s->_memorySegment, s->_memorySegmentSize); - - retval->saveLoadWithSerializer(ser); // FIXME: Error handling? + s->reset(true); + s->saveLoadWithSerializer(ser); // FIXME: Error handling? #ifdef USE_OLD_MUSIC_FUNCTIONS s->_sound.sfx_exit(); #endif // Set exec stack base to zero - retval->execution_stack_base = 0; + s->execution_stack_base = 0; // Now copy all current state information #ifdef USE_OLD_MUSIC_FUNCTIONS - temp = retval->_sound._songlib; - retval->_sound.sfx_init(g_sci->getResMan(), s->sfx_init_flags, g_sci->_features->detectDoSoundType()); - retval->sfx_init_flags = s->sfx_init_flags; - retval->_sound._songlib.freeSounds(); - retval->_sound._songlib = temp; - retval->_soundCmd->updateSfxState(&retval->_sound); + temp = s->_sound._songlib; + s->_sound.sfx_init(g_sci->getResMan(), s->sfx_init_flags, g_sci->_features->detectDoSoundType()); + s->sfx_init_flags = s->sfx_init_flags; + s->_sound._songlib.freeSounds(); + s->_sound._songlib = temp; + s->_soundCmd->updateSfxState(&retval->_sound); #endif - reconstruct_stack(retval); - retval->_segMan->reconstructScripts(retval); - retval->_segMan->reconstructClones(); - retval->_gameObj = s->_gameObj; - retval->script_000 = retval->_segMan->getScript(retval->_segMan->getScriptSegment(0, SCRIPT_GET_DONT_LOAD)); - retval->gc_countdown = GC_INTERVAL - 1; - retval->sys_strings_segment = retval->_segMan->findSegmentByType(SEG_TYPE_SYS_STRINGS); - retval->sys_strings = (SystemStrings *)(retval->_segMan->_heap[retval->sys_strings_segment]); + reconstruct_stack(s); + s->_segMan->reconstructScripts(s); + s->_segMan->reconstructClones(); + s->_gameObj = s->_gameObj; + s->script_000 = s->_segMan->getScript(s->_segMan->getScriptSegment(0, SCRIPT_GET_DONT_LOAD)); + s->gc_countdown = GC_INTERVAL - 1; // Time state: - retval->last_wait_time = g_system->getMillis(); - retval->game_start_time = g_system->getMillis(); - - // static parser information: - - if (retval->_voc) - retval->_voc->parser_base = make_reg(s->sys_strings_segment, SYS_STRING_PARSER_BASE); + s->last_wait_time = g_system->getMillis(); + s->game_start_time = g_system->getMillis(); - retval->successor = NULL; - retval->_gameId = s->_gameId; + s->restoring = false; #ifdef USE_OLD_MUSIC_FUNCTIONS - retval->_sound._it = NULL; - retval->_sound._flags = s->_sound._flags; - retval->_sound._song = NULL; - retval->_sound._suspended = s->_sound._suspended; - reconstruct_sounds(retval); + s->_sound._it = NULL; + s->_sound._flags = s->_sound._flags; + s->_sound._song = NULL; + s->_sound._suspended = s->_sound._suspended; + reconstruct_sounds(s); #else - retval->_soundCmd->reconstructPlayList(meta.savegame_version); + s->_soundCmd->reconstructPlayList(meta.savegame_version); #endif // Message state: - retval->_msgState = new MessageState(retval->_segMan); + s->_msgState = new MessageState(s->_segMan); #ifdef ENABLE_SCI32 if (g_sci->_gui32) { g_sci->_gui32->init(); } else { #endif - g_sci->_gui->resetEngineState(retval); + g_sci->_gui->resetEngineState(s); g_sci->_gui->init(g_sci->_features->usesOldGfxFunctions()); #ifdef ENABLE_SCI32 } #endif - s->successor = retval; // Set successor - script_abort_flag = 2; // Abort current game with replay - shrink_execution_stack(s, s->execution_stack_base + 1); + s->restoring = true; + s->script_abort_flag = 2; // Abort current game with replay + s->shrinkStackToBase(); } bool get_savegame_metadata(Common::SeekableReadStream *stream, SavegameMetadata *meta) { diff --git a/engines/sci/engine/savegame.h b/engines/sci/engine/savegame.h index bad79fca27..7be05381da 100644 --- a/engines/sci/engine/savegame.h +++ b/engines/sci/engine/savegame.h @@ -36,7 +36,7 @@ namespace Sci { struct EngineState; enum { - CURRENT_SAVEGAME_VERSION = 19, + CURRENT_SAVEGAME_VERSION = 20, MINIMUM_SAVEGAME_VERSION = 9 }; diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp index e9b1ce3f28..1f32e50b67 100644 --- a/engines/sci/engine/script.cpp +++ b/engines/sci/engine/script.cpp @@ -123,13 +123,13 @@ void SegManager::createClassTable() { error("SegManager: failed to open vocab 996"); int totalClasses = vocab996->size >> 2; - _classtable.resize(totalClasses); + _classTable.resize(totalClasses); for (uint16 classNr = 0; classNr < totalClasses; classNr++) { uint16 scriptNr = READ_SCI11ENDIAN_UINT16(vocab996->data + classNr * 4 + 2); - _classtable[classNr].reg = NULL_REG; - _classtable[classNr].script = scriptNr; + _classTable[classNr].reg = NULL_REG; + _classTable[classNr].script = scriptNr; } _resMan->unlockResource(vocab996); @@ -139,11 +139,11 @@ reg_t SegManager::getClassAddress(int classnr, ScriptLoadType lock, reg_t caller if (classnr == 0xffff) return NULL_REG; - if (classnr < 0 || (int)_classtable.size() <= classnr || _classtable[classnr].script < 0) { - error("[VM] Attempt to dereference class %x, which doesn't exist (max %x)", classnr, _classtable.size()); + if (classnr < 0 || (int)_classTable.size() <= classnr || _classTable[classnr].script < 0) { + error("[VM] Attempt to dereference class %x, which doesn't exist (max %x)", classnr, _classTable.size()); return NULL_REG; } else { - Class *the_class = &_classtable[classnr]; + Class *the_class = &_classTable[classnr]; if (!the_class->reg.segment) { getScriptSegment(the_class->script, lock); @@ -175,7 +175,7 @@ void SegManager::scriptInitialiseLocals(reg_t location) { Script *scr = getScript(location.segment); unsigned int count; - VERIFY(location.offset + 1 < (uint16)scr->_bufSize, "Locals beyond end of script\n"); + VERIFY(location.offset + 1 < (uint16)scr->getBufSize(), "Locals beyond end of script\n"); if (getSciVersion() >= SCI_VERSION_1_1) count = READ_SCI11ENDIAN_UINT16(scr->_buf + location.offset - 2); @@ -185,55 +185,38 @@ void SegManager::scriptInitialiseLocals(reg_t location) { scr->_localsOffset = location.offset; - if (!(location.offset + count * 2 + 1 < scr->_bufSize)) { - warning("Locals extend beyond end of script: offset %04x, count %x vs size %x", location.offset, count, (uint)scr->_bufSize); - count = (scr->_bufSize - location.offset) >> 1; + if (!(location.offset + count * 2 + 1 < scr->getBufSize())) { + warning("Locals extend beyond end of script: offset %04x, count %x vs size %x", location.offset, count, (uint)scr->getBufSize()); + count = (scr->getBufSize() - location.offset) >> 1; } LocalVariables *locals = allocLocalsSegment(scr, count); if (locals) { uint i; - byte *base = (byte *)(scr->_buf + location.offset); + const byte *base = (const byte *)(scr->_buf + location.offset); for (i = 0; i < count; i++) locals->_locals[i] = make_reg(0, READ_SCI11ENDIAN_UINT16(base + i * 2)); } } -void SegManager::scriptRelocateExportsSci11(SegmentId seg) { - Script *scr = getScript(seg); - for (int i = 0; i < scr->_numExports; i++) { - /* We are forced to use an ugly heuristic here to distinguish function - exports from object/class exports. The former kind points into the - script resource, the latter into the heap resource. */ - uint16 location = READ_SCI11ENDIAN_UINT16((byte *)(scr->_exportTable + i)); - - if ((location < scr->_heapSize - 1) && (READ_SCI11ENDIAN_UINT16(scr->_heapStart + location) == SCRIPT_OBJECT_MAGIC_NUMBER)) { - WRITE_SCI11ENDIAN_UINT16((byte *)(scr->_exportTable + i), location + scr->_heapStart - scr->_buf); - } else { - // Otherwise it's probably a function export, - // and we don't need to do anything. - } - } -} - void SegManager::scriptInitialiseObjectsSci11(SegmentId seg) { Script *scr = getScript(seg); - byte *seeker = scr->_heapStart + 4 + READ_SCI11ENDIAN_UINT16(scr->_heapStart + 2) * 2; + const byte *seeker = scr->_heapStart + 4 + READ_SCI11ENDIAN_UINT16(scr->_heapStart + 2) * 2; while (READ_SCI11ENDIAN_UINT16(seeker) == SCRIPT_OBJECT_MAGIC_NUMBER) { - if (READ_SCI11ENDIAN_UINT16(seeker + 14) & SCRIPT_INFO_CLASS) { + if (READ_SCI11ENDIAN_UINT16(seeker + 14) & kInfoFlagClass) { // -info- selector int classpos = seeker - scr->_buf; int species = READ_SCI11ENDIAN_UINT16(seeker + 10); - if (species < 0 || species >= (int)_classtable.size()) { + if (species < 0 || species >= (int)_classTable.size()) { error("Invalid species %d(0x%x) not in interval [0,%d) while instantiating script %d", - species, species, _classtable.size(), scr->_nr); + species, species, _classTable.size(), scr->_nr); return; } - _classtable[species].reg.segment = seg; - _classtable[species].reg.offset = classpos; + _classTable[species].reg.segment = seg; + _classTable[species].reg.offset = classpos; } seeker += READ_SCI11ENDIAN_UINT16(seeker + 2) * 2; } @@ -243,19 +226,20 @@ void SegManager::scriptInitialiseObjectsSci11(SegmentId seg) { reg_t reg = make_reg(seg, seeker - scr->_buf); Object *obj = scr->scriptObjInit(reg); -#if 0 - if (obj->_variables[5].offset != 0xffff) { - obj->_variables[5] = INST_LOOKUP_CLASS(obj->_variables[5].offset); - baseObj = getObject(obj->_variables[5]); - obj->variable_names_nr = baseObj->variables_nr; - obj->_baseObj = baseObj->_baseObj; - } -#endif - // Copy base from species class, as we need its selector IDs obj->setSuperClassSelector( getClassAddress(obj->getSuperClassSelector().offset, SCRIPT_GET_LOCK, NULL_REG)); + // If object is instance, get -propDict- from class and set it for this object + // This is needed for ::isMemberOf() to work. + // Example testcase - room 381 of sq4cd - if isMemberOf() doesn't work, talk-clicks on the robot will act like + // clicking on ego + if (!obj->isClass()) { + reg_t classObject = obj->getSuperClassSelector(); + Object *classObj = getObject(classObject); + obj->setPropDictSelector(classObj->getPropDictSelector()); + } + // Set the -classScript- selector to the script number. // FIXME: As this selector is filled in at run-time, it is likely // that it is supposed to hold a pointer. The Obj::isKindOf method @@ -268,86 +252,24 @@ void SegManager::scriptInitialiseObjectsSci11(SegmentId seg) { } } - - -int script_instantiate_common(ResourceManager *resMan, SegManager *segMan, int script_nr, Resource **script, Resource **heap, int *was_new) { - *was_new = 1; - - *script = resMan->findResource(ResourceId(kResourceTypeScript, script_nr), 0); - if (getSciVersion() >= SCI_VERSION_1_1) - *heap = resMan->findResource(ResourceId(kResourceTypeHeap, script_nr), 0); - - if (!*script || (getSciVersion() >= SCI_VERSION_1_1 && !heap)) { - warning("Script 0x%x requested but not found", script_nr); - if (getSciVersion() >= SCI_VERSION_1_1) { - if (*heap) - warning("Inconsistency: heap resource WAS found"); - else if (*script) - warning("Inconsistency: script resource WAS found"); - } - return 0; - } - - SegmentId seg_id = segMan->getScriptSegment(script_nr); - Script *scr = segMan->getScriptIfLoaded(seg_id); - if (scr) { - if (!scr->isMarkedAsDeleted()) { - scr->incrementLockers(); - return seg_id; - } else { - scr->freeScript(); - } - } else { - scr = segMan->allocateScript(script_nr, &seg_id); - if (!scr) { // ALL YOUR SCRIPT BASE ARE BELONG TO US - error("Not enough heap space for script size 0x%x of script 0x%x (Should this happen?)", (*script)->size, script_nr); - return 0; - } - } - - scr->init(script_nr, resMan); - - // Set heap position (beyond the size word) - scr->setLockers(1); - scr->setExportTableOffset(0); - scr->setSynonymsOffset(0); - scr->setSynonymsNr(0); - - *was_new = 0; - - return seg_id; -} - -#define INST_LOOKUP_CLASS(id) ((id == 0xffff)? NULL_REG : segMan->getClassAddress(id, SCRIPT_GET_LOCK, addr)) - -int script_instantiate_sci0(ResourceManager *resMan, SegManager *segMan, int script_nr) { +void script_instantiate_sci0(Script *scr, int segmentId, SegManager *segMan) { int objType; uint32 objLength = 0; - int relocation = -1; - Resource *script; - int was_new; bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); - const int seg_id = script_instantiate_common(resMan, segMan, script_nr, &script, NULL, &was_new); uint16 curOffset = oldScriptHeader ? 2 : 0; - if (was_new) - return seg_id; - - Script *scr = segMan->getScript(seg_id); - scr->mcpyInOut(0, script->data, script->size); - if (oldScriptHeader) { // Old script block // There won't be a localvar block in this case // Instead, the script starts with a 16 bit int specifying the // number of locals we need; these are then allocated and zeroed. - int locals_nr = READ_LE_UINT16(script->data); - if (locals_nr) - segMan->scriptInitialiseLocalsZero(seg_id, locals_nr); + int localsCount = READ_LE_UINT16(scr->_buf); + if (localsCount) + segMan->scriptInitialiseLocalsZero(segmentId, localsCount); } // Now do a first pass through the script objects to find the - // export table and local variable block + // local variable blocks do { objType = scr->getHeap(curOffset); @@ -355,48 +277,30 @@ int script_instantiate_sci0(ResourceManager *resMan, SegManager *segMan, int scr break; objLength = scr->getHeap(curOffset + 2); - - // This happens in some demos (e.g. the EcoQuest 1 demo). Not sure what is the - // actual cause of it, but the scripts of these demos can't be loaded properly - // and we're stuck forever in this loop, as objLength never changes - if (!objLength) { - warning("script_instantiate_sci0: objLength is 0, unable to parse script"); - return 0; - } - curOffset += 4; // skip header switch (objType) { - case SCI_OBJ_EXPORTS: - scr->setExportTableOffset(curOffset); - break; - case SCI_OBJ_SYNONYMS: - scr->setSynonymsOffset(curOffset); - scr->setSynonymsNr((objLength) / 4); - break; case SCI_OBJ_LOCALVARS: - segMan->scriptInitialiseLocals(make_reg(seg_id, curOffset)); + segMan->scriptInitialiseLocals(make_reg(segmentId, curOffset)); break; - case SCI_OBJ_CLASS: { int classpos = curOffset - SCRIPT_OBJECT_MAGIC_OFFSET; int species = scr->getHeap(curOffset - SCRIPT_OBJECT_MAGIC_OFFSET + SCRIPT_SPECIES_OFFSET); - if (species < 0 || species >= (int)segMan->_classtable.size()) { - if (species == (int)segMan->_classtable.size()) { + if (species < 0 || species >= (int)segMan->classTableSize()) { + if (species == (int)segMan->classTableSize()) { // Happens in the LSL2 demo warning("Applying workaround for an off-by-one invalid species access"); - segMan->_classtable.resize(segMan->_classtable.size() + 1); + segMan->resizeClassTable(segMan->classTableSize() + 1); } else { - warning("Invalid species %d(0x%x) not in interval " - "[0,%d) while instantiating script %d\n", - species, species, segMan->_classtable.size(), - script_nr); - return 0; + error("Invalid species %d(0x%x) not in interval " + "[0,%d) while instantiating script at segment %d\n", + species, species, segMan->classTableSize(), + segmentId); + return; } } - segMan->_classtable[species].reg.segment = seg_id; - segMan->_classtable[species].reg.offset = classpos; + segMan->setClassOffset(species, make_reg(segmentId, classpos)); // Set technical class position-- into the block allocated for it } break; @@ -406,7 +310,7 @@ int script_instantiate_sci0(ResourceManager *resMan, SegManager *segMan, int scr } curOffset += objLength - 4; - } while (objType != 0 && curOffset < script->size - 2); + } while (objType != 0 && curOffset < scr->getScriptSize() - 2); // And now a second pass to adjust objects and class pointers, and the general pointers objLength = 0; @@ -420,7 +324,7 @@ int script_instantiate_sci0(ResourceManager *resMan, SegManager *segMan, int scr objLength = scr->getHeap(curOffset + 2); curOffset += 4; // skip header - reg_t addr = make_reg(seg_id, curOffset); + reg_t addr = make_reg(segmentId, curOffset); switch (objType) { case SCI_OBJ_CODE: @@ -429,77 +333,52 @@ int script_instantiate_sci0(ResourceManager *resMan, SegManager *segMan, int scr case SCI_OBJ_OBJECT: case SCI_OBJ_CLASS: { // object or class? Object *obj = scr->scriptObjInit(addr); + obj->initSpecies(segMan, addr); - // Instantiate the superclass, if neccessary - obj->setSpeciesSelector(INST_LOOKUP_CLASS(obj->getSpeciesSelector().offset)); - - Object *baseObj = segMan->getObject(obj->getSpeciesSelector()); - - if (baseObj) { - obj->setVarCount(baseObj->getVarCount()); - // Copy base from species class, as we need its selector IDs - obj->_baseObj = baseObj->_baseObj; - - obj->setSuperClassSelector(INST_LOOKUP_CLASS(obj->getSuperClassSelector().offset)); - } else { + if (!obj->initBaseObject(segMan, addr)) { warning("Failed to locate base object for object at %04X:%04X; skipping", PRINT_REG(addr)); - scr->scriptObjRemove(addr); } } // if object or class break; - case SCI_OBJ_POINTERS: // A relocation table - relocation = addr.offset; - break; - default: break; } curOffset += objLength - 4; - } while (objType != 0 && curOffset < script->size - 2); - - if (relocation >= 0) - scr->scriptRelocate(make_reg(seg_id, relocation)); - - return seg_id; // instantiation successful + } while (objType != 0 && curOffset < scr->getScriptSize() - 2); } -int script_instantiate_sci11(ResourceManager *resMan, SegManager *segMan, int script_nr) { - Resource *script, *heap; - int was_new; - const int seg_id = script_instantiate_common(resMan, segMan, script_nr, &script, &heap, &was_new); - - if (was_new) - return seg_id; - - Script *scr = segMan->getScript(seg_id); - int _heapStart = script->size; - - if (script->size & 2) - _heapStart++; - - scr->mcpyInOut(0, script->data, script->size); - scr->mcpyInOut(_heapStart, heap->data, heap->size); - - if (READ_SCI11ENDIAN_UINT16(script->data + 6) > 0) - scr->setExportTableOffset(6); - - segMan->scriptInitialiseLocals(make_reg(seg_id, _heapStart + 4)); - - segMan->scriptRelocateExportsSci11(seg_id); - segMan->scriptInitialiseObjectsSci11(seg_id); +int script_instantiate(ResourceManager *resMan, SegManager *segMan, int scriptNum) { + SegmentId segmentId = segMan->getScriptSegment(scriptNum); + Script *scr = segMan->getScriptIfLoaded(segmentId); + if (scr) { + if (!scr->isMarkedAsDeleted()) { + scr->incrementLockers(); + return segmentId; + } else { + scr->freeScript(); + } + } else { + scr = segMan->allocateScript(scriptNum, &segmentId); + } - scr->heapRelocate(make_reg(seg_id, READ_SCI11ENDIAN_UINT16(heap->data))); + scr->init(scriptNum, resMan); + scr->load(resMan); - return seg_id; -} + if (getSciVersion() >= SCI_VERSION_1_1) { + int heapStart = scr->getScriptSize(); + segMan->scriptInitialiseLocals(make_reg(segmentId, heapStart + 4)); + segMan->scriptInitialiseObjectsSci11(segmentId); + scr->relocate(make_reg(segmentId, READ_SCI11ENDIAN_UINT16(scr->_heapStart))); + } else { + script_instantiate_sci0(scr, segmentId, segMan); + byte *relocationBlock = scr->findBlock(SCI_OBJ_POINTERS); + if (relocationBlock) + scr->relocate(make_reg(segmentId, relocationBlock - scr->_buf + 4)); + } -int script_instantiate(ResourceManager *resMan, SegManager *segMan, int script_nr) { - if (getSciVersion() >= SCI_VERSION_1_1) - return script_instantiate_sci11(resMan, segMan, script_nr); - else - return script_instantiate_sci0(resMan, segMan, script_nr); + return segmentId; } void script_uninstantiate_sci0(SegManager *segMan, int script_nr, SegmentId seg) { @@ -528,7 +407,7 @@ void script_uninstantiate_sci0(SegManager *segMan, int script_nr, SegmentId seg) superclass = scr->getHeap(reg.offset + SCRIPT_SUPERCLASS_OFFSET); // Get superclass... if (superclass >= 0) { - int superclass_script = segMan->_classtable[superclass].script; + int superclass_script = segMan->getClass(superclass).script; if (superclass_script == script_nr) { if (scr->getLockers()) @@ -562,9 +441,9 @@ void script_uninstantiate(SegManager *segMan, int script_nr) { return; // Free all classtable references to this script - for (uint i = 0; i < segMan->_classtable.size(); i++) - if (segMan->_classtable[i].reg.segment == segment) - segMan->_classtable[i].reg = NULL_REG; + for (uint i = 0; i < segMan->classTableSize(); i++) + if (segMan->getClass(i).reg.segment == segment) + segMan->setClassOffset(i, NULL_REG); if (getSciVersion() < SCI_VERSION_1_1) script_uninstantiate_sci0(segMan, script_nr, segment); diff --git a/engines/sci/engine/scriptdebug.cpp b/engines/sci/engine/scriptdebug.cpp index 4b60626b2e..159c278e8c 100644 --- a/engines/sci/engine/scriptdebug.cpp +++ b/engines/sci/engine/scriptdebug.cpp @@ -67,37 +67,6 @@ extern const char *selector_name(EngineState *s, int selector); DebugState g_debugState; -int propertyOffsetToId(SegManager *segMan, int prop_ofs, reg_t objp) { - Object *obj = segMan->getObject(objp); - byte *selectoroffset; - int selectors; - - if (!obj) { - warning("Applied propertyOffsetToId on non-object at %04x:%04x", PRINT_REG(objp)); - return -1; - } - - selectors = obj->getVarCount(); - - if (getSciVersion() < SCI_VERSION_1_1) - selectoroffset = ((byte *)(obj->_baseObj)) + SCRIPT_SELECTOR_OFFSET + selectors * 2; - else { - if (!(obj->getInfoSelector().offset & SCRIPT_INFO_CLASS)) { - obj = segMan->getObject(obj->getSuperClassSelector()); - selectoroffset = (byte *)obj->_baseVars; - } else - selectoroffset = (byte *)obj->_baseVars; - } - - if (prop_ofs < 0 || (prop_ofs >> 1) >= selectors) { - warning("Applied propertyOffsetToId to invalid property offset %x (property #%d not in [0..%d]) on object at %04x:%04x", - prop_ofs, prop_ofs >> 1, selectors - 1, PRINT_REG(objp)); - return -1; - } - - return READ_SCI11ENDIAN_UINT16(selectoroffset + prop_ofs); -} - // Disassembles one command from the heap, returns address of next command or 0 if a ret was encountered. reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecode) { SegmentObj *mobj = s->_segMan->getSegment(pos.segment, SEG_TYPE_SCRIPT); @@ -116,7 +85,7 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod script_entity = (Script *)mobj; scr = script_entity->_buf; - scr_size = script_entity->_bufSize; + scr_size = script_entity->getBufSize(); if (pos.offset >= scr_size) { warning("Trying to disassemble beyond end of script"); @@ -221,51 +190,52 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod } } - if (pos == scriptState.xs->addr.pc) { // Extra information if debugging the current opcode + if (pos == s->xs->addr.pc) { // Extra information if debugging the current opcode if ((opcode == op_pTos) || (opcode == op_sTop) || (opcode == op_pToa) || (opcode == op_aTop) || (opcode == op_dpToa) || (opcode == op_ipToa) || (opcode == op_dpTos) || (opcode == op_ipTos)) { - int prop_ofs = scr[pos.offset + 1]; - int prop_id = propertyOffsetToId(s->_segMan, prop_ofs, scriptState.xs->objp); - - printf(" (%s)", selector_name(s, prop_id)); + const Object *obj = s->_segMan->getObject(s->xs->objp); + if (!obj) + warning("Attempted to reference on non-object at %04x:%04x", PRINT_REG(s->xs->objp)); + else + printf(" (%s)", selector_name(s, obj->propertyOffsetToId(s->_segMan, scr[pos.offset + 1]))); } } printf("\n"); - if (pos == scriptState.xs->addr.pc) { // Extra information if debugging the current opcode + if (pos == s->xs->addr.pc) { // Extra information if debugging the current opcode if (opcode == op_callk) { - int stackframe = (scr[pos.offset + 2] >> 1) + (scriptState.restAdjust); - int argc = ((scriptState.xs->sp)[- stackframe - 1]).offset; + int stackframe = (scr[pos.offset + 2] >> 1) + (s->restAdjust); + int argc = ((s->xs->sp)[- stackframe - 1]).offset; bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); if (!oldScriptHeader) - argc += (scriptState.restAdjust); + argc += (s->restAdjust); printf(" Kernel params: ("); for (int j = 0; j < argc; j++) { - printf("%04x:%04x", PRINT_REG((scriptState.xs->sp)[j - stackframe])); + printf("%04x:%04x", PRINT_REG((s->xs->sp)[j - stackframe])); if (j + 1 < argc) printf(", "); } printf(")\n"); } else if ((opcode == op_send) || (opcode == op_self)) { - int restmod = scriptState.restAdjust; + int restmod = s->restAdjust; int stackframe = (scr[pos.offset + 1] >> 1) + restmod; - reg_t *sb = scriptState.xs->sp; + reg_t *sb = s->xs->sp; uint16 selector; reg_t fun_ref; while (stackframe > 0) { int argc = sb[- stackframe + 1].offset; const char *name = NULL; - reg_t called_obj_addr = scriptState.xs->objp; + reg_t called_obj_addr = s->xs->objp; if (opcode == op_send) called_obj_addr = s->r_acc; else if (opcode == op_self) - called_obj_addr = scriptState.xs->objp; + called_obj_addr = s->xs->objp; selector = sb[- stackframe].offset; @@ -276,7 +246,7 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod printf(" %s::%s[", name, (selector > kernel->getSelectorNamesSize()) ? "<invalid>" : selector_name(s, selector)); - switch (lookup_selector(s->_segMan, called_obj_addr, selector, 0, &fun_ref)) { + switch (lookupSelector(s->_segMan, called_obj_addr, selector, 0, &fun_ref)) { case kSelectorMethod: printf("FUNCT"); argc += restmod; @@ -315,10 +285,10 @@ void script_debug(EngineState *s) { #if 0 if (sci_debug_flags & _DEBUG_FLAG_LOGGING) { printf("%d: acc=%04x:%04x ", script_step_counter, PRINT_REG(s->r_acc)); - disassemble(s, scriptState.xs->addr.pc, 0, 1); - if (scriptState.seeking == kDebugSeekGlobal) - printf("Global %d (0x%x) = %04x:%04x\n", scriptState.seekSpecial, - scriptState.seekSpecial, PRINT_REG(s->script_000->_localsBlock->_locals[scriptState.seekSpecial])); + disassemble(s, s->xs->addr.pc, 0, 1); + if (s->seeking == kDebugSeekGlobal) + printf("Global %d (0x%x) = %04x:%04x\n", s->seekSpecial, + s->seekSpecial, PRINT_REG(s->script_000->_localsBlock->_locals[s->seekSpecial])); } #endif @@ -328,16 +298,16 @@ void script_debug(EngineState *s) { #endif if (g_debugState.seeking && !g_debugState.breakpointWasHit) { // Are we looking for something special? - SegmentObj *mobj = s->_segMan->getSegment(scriptState.xs->addr.pc.segment, SEG_TYPE_SCRIPT); + SegmentObj *mobj = s->_segMan->getSegment(s->xs->addr.pc.segment, SEG_TYPE_SCRIPT); if (mobj) { Script *scr = (Script *)mobj; byte *code_buf = scr->_buf; - int code_buf_size = scr->_bufSize; - int opcode = scriptState.xs->addr.pc.offset >= code_buf_size ? 0 : code_buf[scriptState.xs->addr.pc.offset]; + int code_buf_size = scr->getBufSize(); + int opcode = s->xs->addr.pc.offset >= code_buf_size ? 0 : code_buf[s->xs->addr.pc.offset]; int op = opcode >> 1; - int paramb1 = scriptState.xs->addr.pc.offset + 1 >= code_buf_size ? 0 : code_buf[scriptState.xs->addr.pc.offset + 1]; - int paramf1 = (opcode & 1) ? paramb1 : (scriptState.xs->addr.pc.offset + 2 >= code_buf_size ? 0 : (int16)READ_SCI11ENDIAN_UINT16(code_buf + scriptState.xs->addr.pc.offset + 1)); + int paramb1 = s->xs->addr.pc.offset + 1 >= code_buf_size ? 0 : code_buf[s->xs->addr.pc.offset + 1]; + int paramf1 = (opcode & 1) ? paramb1 : (s->xs->addr.pc.offset + 2 >= code_buf_size ? 0 : (int16)READ_SCI11ENDIAN_UINT16(code_buf + s->xs->addr.pc.offset + 1)); switch (g_debugState.seeking) { case kDebugSeekSpecialCallk: @@ -381,8 +351,8 @@ void script_debug(EngineState *s) { } } - printf("Step #%d\n", script_step_counter); - disassemble(s, scriptState.xs->addr.pc, 0, 1); + printf("Step #%d\n", s->script_step_counter); + disassemble(s, s->xs->addr.pc, 0, 1); if (g_debugState.runningStep) { g_debugState.runningStep--; diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp index b18d76e1a7..4d3e6f754e 100644 --- a/engines/sci/engine/seg_manager.cpp +++ b/engines/sci/engine/seg_manager.cpp @@ -54,7 +54,6 @@ SegManager::SegManager(ResourceManager *resMan) { createClassTable(); } -// Destroy the object, free the memorys if allocated before SegManager::~SegManager() { resetSegMan(); } @@ -77,10 +76,29 @@ void SegManager::resetSegMan() { Hunks_seg_id = 0; // Reinitialize class table - _classtable.clear(); + _classTable.clear(); createClassTable(); } +void SegManager::initSysStrings() { + sysStrings = (SystemStrings *)allocSegment(new SystemStrings(), &sysStringsSegment); + + // Allocate static buffer for savegame and CWD directories + SystemString *strSaveDir = &sysStrings->_strings[SYS_STRING_SAVEDIR]; + strSaveDir->_name = "savedir"; + strSaveDir->_maxSize = MAX_SAVE_DIR_SIZE; + strSaveDir->_value = (char *)calloc(MAX_SAVE_DIR_SIZE, sizeof(char)); + // Set the savegame dir (actually, we set it to a fake value, + // since we cannot let the game control where saves are stored) + ::strcpy(strSaveDir->_value, ""); + + // Allocate static buffer for the parser base + SystemString *strParserBase = &sysStrings->_strings[SYS_STRING_PARSER_BASE]; + strParserBase->_name = "parser-base"; + strParserBase->_maxSize = MAX_PARSER_BASE; + strParserBase->_value = (char *)calloc(MAX_PARSER_BASE, sizeof(char)); +} + SegmentId SegManager::findFreeSegment() const { // FIXME: This is a very crude approach: We find a free segment id by scanning // from the start. This can be slow if the number of segments becomes large. @@ -156,7 +174,7 @@ int SegManager::deallocate(SegmentId seg, bool recursive) { } bool SegManager::isHeapObject(reg_t pos) { - Object *obj = getObject(pos); + const Object *obj = getObject(pos); if (obj == NULL || (obj && obj->isFreed())) return false; Script *scr = getScriptIfLoaded(pos.segment); @@ -223,7 +241,7 @@ Object *SegManager::getObject(reg_t pos) { warning("getObject(): Trying to get an invalid object"); } else if (mobj->getType() == SEG_TYPE_SCRIPT) { Script *scr = (Script *)mobj; - if (pos.offset <= scr->_bufSize && pos.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET + if (pos.offset <= scr->getBufSize() && pos.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET && RAW_IS_OBJECT(scr->_buf + pos.offset)) { obj = scr->getObject(pos.offset); } @@ -234,7 +252,7 @@ Object *SegManager::getObject(reg_t pos) { } const char *SegManager::getObjectName(reg_t pos) { - Object *obj = getObject(pos); + const Object *obj = getObject(pos); if (!obj) return "<no such object>"; @@ -275,7 +293,7 @@ reg_t SegManager::findObjectByName(const Common::String &name, int index) { // It's a script or a clone table, scan all objects in it for (; idx < max_index; ++idx) { - Object *obj = NULL; + const Object *obj = NULL; reg_t objpos; objpos.offset = 0; objpos.segment = i; @@ -393,10 +411,6 @@ DataStack *SegManager::allocateStack(int size, SegmentId *segid) { return retval; } -SystemStrings *SegManager::allocateSysStrings(SegmentId *segid) { - return (SystemStrings *)allocSegment(new SystemStrings(), segid); -} - void SegManager::freeHunkEntry(reg_t addr) { if (addr.isNull()) { warning("Attempt to free a Hunk from a null address"); @@ -485,7 +499,7 @@ void SegManager::reconstructClones() { continue; CloneTable::Entry &seeker = ct->_table[j]; - Object *baseObj = getObject(seeker.getSpeciesSelector()); + const Object *baseObj = getObject(seeker.getSpeciesSelector()); seeker.cloneFromObject(baseObj); if (!baseObj) warning("Clone entry without a base class: %d", j); @@ -523,6 +537,16 @@ Node *SegManager::allocateNode(reg_t *addr) { return &(table->_table[offset]); } +reg_t SegManager::newNode(reg_t value, reg_t key) { + reg_t nodebase; + Node *n = allocateNode(&nodebase); + n->pred = n->succ = NULL_REG; + n->key = key; + n->value = value; + + return nodebase; +} + List *SegManager::lookupList(reg_t addr) { if (getSegmentType(addr.segment) != SEG_TYPE_LISTS) { warning("Attempt to use non-list %04x:%04x as list", PRINT_REG(addr)); diff --git a/engines/sci/engine/seg_manager.h b/engines/sci/engine/seg_manager.h index e8bbdbdb3f..9312f51f9d 100644 --- a/engines/sci/engine/seg_manager.h +++ b/engines/sci/engine/seg_manager.h @@ -112,12 +112,6 @@ public: SegmentId getScriptSegment(int script_nr, ScriptLoadType load); // TODO: document this - reg_t lookupScriptExport(int script_nr, int export_index) { - SegmentId seg = getScriptSegment(script_nr, SCRIPT_GET_DONT_LOAD); - return make_reg(seg, getScript(seg)->validateExportFunc(export_index)); - } - - // TODO: document this reg_t getClassAddress(int classnr, ScriptLoadType lock, reg_t caller); /** @@ -188,15 +182,10 @@ public: // 5. System Strings /** - * Allocates a system string table - * See also sys_string_acquire(); - * @param[in] segid Segment ID of the stack - * @returns The physical stack + * Initializes the system string table. */ - SystemStrings *allocateSysStrings(SegmentId *segid); - + void initSysStrings(); - // 5. System Strings // 6, 7. Lists and Nodes @@ -215,6 +204,14 @@ public: Node *allocateNode(reg_t *addr); /** + * Allocate and initialize a new list node. + * @param[in] value The value to set the node to + * @param[in] key The key to set + * @return Pointer to the newly initialized list node + */ + reg_t newNode(reg_t value, reg_t key); + + /** * Resolves a list pointer to a list. * @param addr The address to resolve * @return The list referenced, or NULL on error @@ -432,12 +429,22 @@ public: */ reg_t findObjectByName(const Common::String &name, int index = -1); - void scriptRelocateExportsSci11(SegmentId seg); void scriptInitialiseObjectsSci11(SegmentId seg); + uint32 classTableSize() { return _classTable.size(); } + Class getClass(int index) { return _classTable[index]; } + void setClassOffset(int index, reg_t offset) { _classTable[index].reg = offset; } + void resizeClassTable(uint32 size) { _classTable.resize(size); } + + /** + * Obtains the system strings segment ID + */ + SegmentId getSysStringsSegment() { return sysStringsSegment; } + public: // TODO: make private Common::Array<SegmentObj *> _heap; - Common::Array<Class> _classtable; /**< Table of all classes */ + // Only accessible from saveLoadWithSerializer() + Common::Array<Class> _classTable; /**< Table of all classes */ #ifdef ENABLE_SCI32 SciArray<reg_t> *allocateArray(reg_t *addr); @@ -460,6 +467,13 @@ private: SegmentId Nodes_seg_id; ///< ID of the (a) node segment SegmentId Hunks_seg_id; ///< ID of the (a) hunk segment + /* System strings */ + SegmentId sysStringsSegment; +public: // TODO: make private. Only kString() needs direct access + SystemStrings *sysStrings; + +private: + #ifdef ENABLE_SCI32 SegmentId Arrays_seg_id; SegmentId String_seg_id; diff --git a/engines/sci/engine/segment.cpp b/engines/sci/engine/segment.cpp index ab1a68d165..0e0a759d4b 100644 --- a/engines/sci/engine/segment.cpp +++ b/engines/sci/engine/segment.cpp @@ -27,6 +27,7 @@ #include "sci/sci.h" #include "sci/engine/features.h" +#include "sci/engine/script.h" // for SCI_OBJ_EXPORTS and SCI_OBJ_SYNONYMS #include "sci/engine/segment.h" #include "sci/engine/seg_manager.h" #include "sci/engine/state.h" @@ -100,8 +101,7 @@ Script::Script() : SegmentObj(SEG_TYPE_SCRIPT) { _localsSegment = 0; _localsBlock = NULL; - _relocated = false; - _markedAsDeleted = 0; + _markedAsDeleted = false; } Script::~Script() { @@ -117,54 +117,39 @@ void Script::freeScript() { _codeBlocks.clear(); } -bool Script::init(int script_nr, ResourceManager *resMan) { - setScriptSize(script_nr, resMan); - - _buf = (byte *)malloc(_bufSize); - - if (!_buf) { - freeScript(); - warning("Not enough memory space for script size"); - _bufSize = 0; - return false; - } +void Script::init(int script_nr, ResourceManager *resMan) { + Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, script_nr), 0); _localsOffset = 0; _localsBlock = NULL; _codeBlocks.clear(); - _relocated = false; _markedAsDeleted = false; _nr = script_nr; - - if (getSciVersion() >= SCI_VERSION_1_1) - _heapStart = _buf + _scriptSize; - else - _heapStart = _buf; - - return true; -} - -void Script::setScriptSize(int script_nr, ResourceManager *resMan) { - Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, script_nr), 0); - Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, script_nr), 0); - bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); + _buf = 0; + _heapStart = 0; _scriptSize = script->size; - _heapSize = 0; // Set later + _bufSize = script->size; + _heapSize = 0; - if (!script || (getSciVersion() >= SCI_VERSION_1_1 && !heap)) { - error("SegManager::setScriptSize: failed to load %s", !script ? "script" : "heap"); - } - if (oldScriptHeader) { - _bufSize = script->size + READ_LE_UINT16(script->data) * 2; - //locals_size = READ_LE_UINT16(script->data) * 2; - } else if (getSciVersion() < SCI_VERSION_1_1) { - _bufSize = script->size; - } else { - _bufSize = script->size + heap->size; + _lockers = 1; + + if (getSciVersion() == SCI_VERSION_0_EARLY) { + _bufSize += READ_LE_UINT16(script->data) * 2; + } else if (getSciVersion() >= SCI_VERSION_1_1) { + /** + * In SCI11, the heap was in a separate space from the script. + * We append it to the end of the script, and adjust addressing accordingly. + * However, since we address the heap with a 16-bit pointer, the combined + * size of the stack and the heap must be 64KB. So far this has worked + * for SCI11, SCI2 and SCI21 games. SCI3 games use a different script format, + * and theoretically they can exceed the 64KB boundary using relocation. + */ + Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, script_nr), 0); + _bufSize += heap->size; _heapSize = heap->size; // Ensure that the start of the heap resource can be word-aligned. @@ -173,12 +158,56 @@ void Script::setScriptSize(int script_nr, ResourceManager *resMan) { _scriptSize++; } - if (_bufSize > 65535) { - error("Script and heap sizes combined exceed 64K." - "This means a fundamental design bug was made in SCI\n" - "regarding SCI1.1 games.\nPlease report this so it can be" - "fixed in the next major version"); - return; + // As mentioned above, the script and the heap together should not exceed 64KB + if (_bufSize > 65535) + error("Script and heap sizes combined exceed 64K. This means a fundamental " + "design bug was made regarding SCI1.1 and newer games.\nPlease " + "report this error to the ScummVM team"); + } +} + +void Script::load(ResourceManager *resMan) { + Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, _nr), 0); + assert(script != 0); + + _buf = (byte *)malloc(_bufSize); + assert(_buf); + + assert(_bufSize >= script->size); + memcpy(_buf, script->data, script->size); + + if (getSciVersion() >= SCI_VERSION_1_1) { + Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, _nr), 0); + assert(heap != 0); + + _heapStart = _buf + _scriptSize; + + assert(_bufSize - _scriptSize <= heap->size); + memcpy(_heapStart, heap->data, heap->size); + } + + _codeBlocks.clear(); + + _exportTable = 0; + _numExports = 0; + _synonyms = 0; + _numSynonyms = 0; + + if (getSciVersion() >= SCI_VERSION_1_1) { + if (READ_LE_UINT16(_buf + 1 + 5) > 0) { + _exportTable = (const uint16 *)(_buf + 1 + 5 + 2); + _numExports = READ_SCI11ENDIAN_UINT16(_exportTable - 1); + } + } else { + _exportTable = (const uint16 *)findBlock(SCI_OBJ_EXPORTS); + if (_exportTable) { + _numExports = READ_SCI11ENDIAN_UINT16(_exportTable + 1); + _exportTable += 3; // skip header plus 2 bytes (_exportTable is a uint16 pointer) + } + _synonyms = findBlock(SCI_OBJ_SYNONYMS); + if (_synonyms) { + _numSynonyms = READ_SCI11ENDIAN_UINT16(_synonyms + 2) / 4; + _synonyms += 4; // skip header } } } @@ -194,19 +223,26 @@ Object *Script::getObject(uint16 offset) { return 0; } -Object *Script::scriptObjInit(reg_t obj_pos) { +const Object *Script::getObject(uint16 offset) const { + if (_objects.contains(offset)) + return &_objects[offset]; + else + return 0; +} + +Object *Script::scriptObjInit(reg_t obj_pos, bool fullObjectInit) { Object *obj; - if (getSciVersion() < SCI_VERSION_1_1) + if (getSciVersion() < SCI_VERSION_1_1 && fullObjectInit) obj_pos.offset += 8; // magic offset (SCRIPT_OBJECT_MAGIC_OFFSET) VERIFY(obj_pos.offset < _bufSize, "Attempt to initialize object beyond end of script\n"); obj = allocateObject(obj_pos.offset); - VERIFY(obj_pos.offset + SCRIPT_FUNCTAREAPTR_OFFSET < (int)_bufSize, "Function area pointer stored beyond end of script\n"); + VERIFY(obj_pos.offset + kOffsetFunctionArea < (int)_bufSize, "Function area pointer stored beyond end of script\n"); - obj->init(_buf, obj_pos); + obj->init(_buf, obj_pos, fullObjectInit); return obj; } @@ -218,37 +254,34 @@ void Script::scriptObjRemove(reg_t obj_pos) { _objects.erase(obj_pos.toUint16()); } -int Script::relocateBlock(Common::Array<reg_t> &block, int block_location, SegmentId segment, int location) { +// This helper function is used by Script::relocateLocal and Object::relocate +static bool relocateBlock(Common::Array<reg_t> &block, int block_location, SegmentId segment, int location, size_t scriptSize) { int rel = location - block_location; if (rel < 0) - return 0; + return false; uint idx = rel >> 1; if (idx >= block.size()) - return 0; + return false; if (rel & 1) { warning("Attempt to relocate odd variable #%d.5e (relative to %04x)\n", idx, block_location); - return 0; + return false; } block[idx].segment = segment; // Perform relocation if (getSciVersion() >= SCI_VERSION_1_1) - block[idx].offset += _scriptSize; + block[idx].offset += scriptSize; - return 1; + return true; } -int Script::relocateLocal(SegmentId segment, int location) { +bool Script::relocateLocal(SegmentId segment, int location) { if (_localsBlock) - return relocateBlock(_localsBlock->_locals, _localsOffset, segment, location); + return relocateBlock(_localsBlock->_locals, _localsOffset, segment, location, _scriptSize); else - return 0; // No hands, no cookies -} - -int Script::relocateObject(Object &obj, SegmentId segment, int location) { - return relocateBlock(obj._variables, obj.getPos().offset, segment, location); + return false; } void Script::scriptAddCodeBlock(reg_t location) { @@ -258,61 +291,35 @@ void Script::scriptAddCodeBlock(reg_t location) { _codeBlocks.push_back(cb); } -void Script::scriptRelocate(reg_t block) { - VERIFY(block.offset < (uint16)_bufSize && READ_SCI11ENDIAN_UINT16(_buf + block.offset) * 2 + block.offset < (uint16)_bufSize, - "Relocation block outside of script\n"); - - int count = READ_SCI11ENDIAN_UINT16(_buf + block.offset); - - for (int i = 0; i <= count; i++) { - int pos = READ_SCI11ENDIAN_UINT16(_buf + block.offset + 2 + (i * 2)); - if (!pos) - continue; // FIXME: A hack pending investigation - - if (!relocateLocal(block.segment, pos)) { - bool done = false; - uint k; - - ObjMap::iterator it; - const ObjMap::iterator end = _objects.end(); - for (it = _objects.begin(); !done && it != end; ++it) { - if (relocateObject(it->_value, block.segment, pos)) - done = true; - } - - for (k = 0; !done && k < _codeBlocks.size(); k++) { - if (pos >= _codeBlocks[k].pos.offset && - pos < _codeBlocks[k].pos.offset + _codeBlocks[k].size) - done = true; - } +void Script::relocate(reg_t block) { + byte *heap = _buf; + uint16 heapSize = (uint16)_bufSize; + uint16 heapOffset = 0; - if (!done) { - printf("While processing relocation block %04x:%04x:\n", PRINT_REG(block)); - printf("Relocation failed for index %04x (%d/%d)\n", pos, i + 1, count); - if (_localsBlock) - printf("- locals: %d at %04x\n", _localsBlock->_locals.size(), _localsOffset); - else - printf("- No locals\n"); - for (it = _objects.begin(), k = 0; it != end; ++it, ++k) - printf("- obj#%d at %04x w/ %d vars\n", k, it->_value.getPos().offset, it->_value.getVarCount()); - // SQ3 script 71 has broken relocation entries. - printf("Trying to continue anyway...\n"); - } - } + if (getSciVersion() >= SCI_VERSION_1_1) { + heap = _heapStart; + heapSize = (uint16)_heapSize; + heapOffset = _scriptSize; } -} -void Script::heapRelocate(reg_t block) { - VERIFY(block.offset < (uint16)_heapSize && READ_SCI11ENDIAN_UINT16(_heapStart + block.offset) * 2 + block.offset < (uint16)_bufSize, + VERIFY(block.offset < (uint16)heapSize && READ_SCI11ENDIAN_UINT16(heap + block.offset) * 2 + block.offset < (uint16)heapSize, "Relocation block outside of script\n"); - if (_relocated) - return; - _relocated = true; - int count = READ_SCI11ENDIAN_UINT16(_heapStart + block.offset); + int count = READ_SCI11ENDIAN_UINT16(heap + block.offset); + int exportIndex = 0; for (int i = 0; i < count; i++) { - int pos = READ_SCI11ENDIAN_UINT16(_heapStart + block.offset + 2 + (i * 2)) + _scriptSize; + int pos = READ_SCI11ENDIAN_UINT16(heap + block.offset + 2 + (exportIndex * 2)) + heapOffset; + // This occurs in SCI01/SCI1 games where every usually one export + // value is zero. It seems that in this situation, we should skip + // the export and move to the next one, though the total count + // of valid exports remains the same + if (!pos) { + exportIndex++; + pos = READ_SCI11ENDIAN_UINT16(heap + block.offset + 2 + (exportIndex * 2)) + heapOffset; + if (!pos) + error("Script::relocate(): Consecutive zero exports found"); + } if (!relocateLocal(block.segment, pos)) { bool done = false; @@ -321,22 +328,33 @@ void Script::heapRelocate(reg_t block) { ObjMap::iterator it; const ObjMap::iterator end = _objects.end(); for (it = _objects.begin(); !done && it != end; ++it) { - if (relocateObject(it->_value, block.segment, pos)) + if (it->_value.relocate(block.segment, pos, _scriptSize)) done = true; } + // Sanity check for SCI0-SCI1 + if (getSciVersion() < SCI_VERSION_1_1) { + for (k = 0; !done && k < _codeBlocks.size(); k++) { + if (pos >= _codeBlocks[k].pos.offset && + pos < _codeBlocks[k].pos.offset + _codeBlocks[k].size) + done = true; + } + } + if (!done) { - printf("While processing relocation block %04x:%04x:\n", PRINT_REG(block)); - printf("Relocation failed for index %04x (%d/%d)\n", pos, i + 1, count); + debug("While processing relocation block %04x:%04x:\n", PRINT_REG(block)); + debug("Relocation failed for index %04x (%d/%d)\n", pos, exportIndex + 1, count); if (_localsBlock) - printf("- locals: %d at %04x\n", _localsBlock->_locals.size(), _localsOffset); + debug("- locals: %d at %04x\n", _localsBlock->_locals.size(), _localsOffset); else - printf("- No locals\n"); + debug("- No locals\n"); for (it = _objects.begin(), k = 0; it != end; ++it, ++k) - printf("- obj#%d at %04x w/ %d vars\n", k, it->_value.getPos().offset, it->_value.getVarCount()); - error("Breakpoint in %s, line %d", __FILE__, __LINE__); + debug("- obj#%d at %04x w/ %d vars\n", k, it->_value.getPos().offset, it->_value.getVarCount()); + debug("Trying to continue anyway...\n"); } } + + exportIndex++; } } @@ -357,16 +375,6 @@ void Script::setLockers(int lockers) { _lockers = lockers; } -void Script::setExportTableOffset(int offset) { - if (offset) { - _exportTable = (uint16 *)(_buf + offset + 2); - _numExports = READ_SCI11ENDIAN_UINT16((byte *)(_exportTable - 1)); - } else { - _exportTable = NULL; - _numExports = 0; - } -} - uint16 Script::validateExportFunc(int pubfunct) { bool exportsAreWide = (g_sci->_features->detectLofsType() == SCI_VERSION_1_MIDDLE); @@ -377,28 +385,36 @@ uint16 Script::validateExportFunc(int pubfunct) { if (exportsAreWide) pubfunct *= 2; - uint16 offset = READ_SCI11ENDIAN_UINT16((byte *)(_exportTable + pubfunct)); + uint16 offset = READ_SCI11ENDIAN_UINT16(_exportTable + pubfunct); VERIFY(offset < _bufSize, "invalid export function pointer"); return offset; } -void Script::setSynonymsOffset(int offset) { - _synonyms = _buf + offset; -} +byte *Script::findBlock(int type) { + byte *buf = _buf; + bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); -byte *Script::getSynonyms() const { - return _synonyms; -} + if (oldScriptHeader) + buf += 2; -void Script::setSynonymsNr(int n) { - _numSynonyms = n; -} + do { + int seekerType = READ_LE_UINT16(buf); + + if (seekerType == 0) + break; + if (seekerType == type) + return buf; -int Script::getSynonymsNr() const { - return _numSynonyms; + int seekerSize = READ_LE_UINT16(buf + 2); + assert(seekerSize > 0); + buf += seekerSize; + } while (1); + + return NULL; } + // memory operations void Script::mcpyInOut(int dst, const void *src, size_t n) { @@ -511,7 +527,7 @@ SegmentRef SystemStrings::dereference(reg_t pointer) { //-------------------- script -------------------- -reg_t Script::findCanonicAddress(SegManager *segMan, reg_t addr) { +reg_t Script::findCanonicAddress(SegManager *segMan, reg_t addr) const { addr.offset = 0; return addr; } @@ -527,13 +543,13 @@ void Script::freeAtAddress(SegManager *segMan, reg_t addr) { segMan->deallocateScript(_nr); } -void Script::listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) { +void Script::listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) const { (*note)(param, make_reg(segId, 0)); } -void Script::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) { +void Script::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const { if (addr.offset <= _bufSize && addr.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET && RAW_IS_OBJECT(_buf + addr.offset)) { - Object *obj = getObject(addr.offset); + const Object *obj = getObject(addr.offset); if (obj) { // Note all local variables, if we have a local variable environment if (_localsSegment) @@ -553,16 +569,14 @@ void Script::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback not //-------------------- clones -------------------- -void CloneTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) { - Clone *clone; - +void CloneTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const { // assert(addr.segment == _segId); if (!isValidEntry(addr.offset)) { error("Unexpected request for outgoing references from clone at %04x:%04x", PRINT_REG(addr)); } - clone = &(_table[addr.offset]); + const Clone *clone = &(_table[addr.offset]); // Emit all member variables (including references to the 'super' delegate) for (uint i = 0; i < clone->getVarCount(); i++) @@ -597,7 +611,7 @@ void CloneTable::freeAtAddress(SegManager *segMan, reg_t addr) { //-------------------- locals -------------------- -reg_t LocalVariables::findCanonicAddress(SegManager *segMan, reg_t addr) { +reg_t LocalVariables::findCanonicAddress(SegManager *segMan, reg_t addr) const { // Reference the owning script SegmentId owner_seg = segMan->getScriptSegment(script_id); @@ -606,7 +620,7 @@ reg_t LocalVariables::findCanonicAddress(SegManager *segMan, reg_t addr) { return make_reg(owner_seg, 0); } -void LocalVariables::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) { +void LocalVariables::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const { // assert(addr.segment == _segId); for (uint i = 0; i < _locals.size(); i++) @@ -615,12 +629,12 @@ void LocalVariables::listAllOutgoingReferences(reg_t addr, void *param, NoteCall //-------------------- stack -------------------- -reg_t DataStack::findCanonicAddress(SegManager *segMan, reg_t addr) { +reg_t DataStack::findCanonicAddress(SegManager *segMan, reg_t addr) const { addr.offset = 0; return addr; } -void DataStack::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) { +void DataStack::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const { fprintf(stderr, "Emitting %d stack entries\n", _capacity); for (int i = 0; i < _capacity; i++) (*note)(param, _entries[i]); @@ -633,13 +647,13 @@ void ListTable::freeAtAddress(SegManager *segMan, reg_t sub_addr) { freeEntry(sub_addr.offset); } -void ListTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) { +void ListTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const { if (!isValidEntry(addr.offset)) { warning("Invalid list referenced for outgoing references: %04x:%04x", PRINT_REG(addr)); return; } - List *list = &(_table[addr.offset]); + const List *list = &(_table[addr.offset]); note(param, list->first); note(param, list->last); @@ -653,12 +667,12 @@ void NodeTable::freeAtAddress(SegManager *segMan, reg_t sub_addr) { freeEntry(sub_addr.offset); } -void NodeTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) { +void NodeTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const { if (!isValidEntry(addr.offset)) { warning("Invalid node referenced for outgoing references: %04x:%04x", PRINT_REG(addr)); return; } - Node *node = &(_table[addr.offset]); + const Node *node = &(_table[addr.offset]); // We need all four here. Can't just stick with 'pred' OR 'succ' because node operations allow us // to walk around from any given node @@ -673,20 +687,43 @@ void NodeTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback //-------------------- object ---------------------------- -Object *Object::getClass(SegManager *segMan) { +void Object::init(byte *buf, reg_t obj_pos, bool initVariables) { + byte *data = buf + obj_pos.offset; + _baseObj = data; + _pos = obj_pos; + + if (getSciVersion() < SCI_VERSION_1_1) { + _variables.resize(READ_LE_UINT16(data + kOffsetSelectorCounter)); + _baseVars = (const uint16 *)(_baseObj + _variables.size() * 2); + _baseMethod = (const uint16 *)(data + READ_LE_UINT16(data + kOffsetFunctionArea)); + _methodCount = READ_LE_UINT16(_baseMethod - 1); + } else { + _variables.resize(READ_SCI11ENDIAN_UINT16(data + 2)); + _baseVars = (const uint16 *)(buf + READ_SCI11ENDIAN_UINT16(data + 4)); + _baseMethod = (const uint16 *)(buf + READ_SCI11ENDIAN_UINT16(data + 6)); + _methodCount = READ_SCI11ENDIAN_UINT16(_baseMethod); + } + + if (initVariables) { + for (uint i = 0; i < _variables.size(); i++) + _variables[i] = make_reg(0, READ_SCI11ENDIAN_UINT16(data + (i * 2))); + } +} + +const Object *Object::getClass(SegManager *segMan) const { return isClass() ? this : segMan->getObject(getSuperClassSelector()); } -int Object::locateVarSelector(SegManager *segMan, Selector slc) { - byte *buf; +int Object::locateVarSelector(SegManager *segMan, Selector slc) const { + const byte *buf; uint varnum; if (getSciVersion() < SCI_VERSION_1_1) { varnum = getVarCount(); - int selector_name_offset = varnum * 2 + SCRIPT_SELECTOR_OFFSET; + int selector_name_offset = varnum * 2 + kOffsetSelectorSegment; buf = _baseObj + selector_name_offset; } else { - Object *obj = getClass(segMan); + const Object *obj = getClass(segMan); varnum = obj->getVariable(1).toUint16(); buf = (byte *)obj->_baseVars; } @@ -698,14 +735,72 @@ int Object::locateVarSelector(SegManager *segMan, Selector slc) { return -1; // Failed } +bool Object::relocate(SegmentId segment, int location, size_t scriptSize) { + return relocateBlock(_variables, getPos().offset, segment, location, scriptSize); +} + +int Object::propertyOffsetToId(SegManager *segMan, int propertyOffset) const { + int selectors = getVarCount(); + + if (propertyOffset < 0 || (propertyOffset >> 1) >= selectors) { + warning("Applied propertyOffsetToId to invalid property offset %x (property #%d not in [0..%d])", + propertyOffset, propertyOffset >> 1, selectors - 1); + return -1; + } + + if (getSciVersion() < SCI_VERSION_1_1) { + const byte *selectoroffset = ((const byte *)(_baseObj)) + kOffsetSelectorSegment + selectors * 2; + return READ_SCI11ENDIAN_UINT16(selectoroffset + propertyOffset); + } else { + const Object *obj = this; + if (!isClass()) + obj = segMan->getObject(getSuperClassSelector()); + + return READ_SCI11ENDIAN_UINT16((const byte *)obj->_baseVars + propertyOffset); + } +} + +void Object::initSpecies(SegManager *segMan, reg_t addr) { + uint16 speciesOffset = getSpeciesSelector().offset; + + if (speciesOffset == 0xffff) // -1 + setSpeciesSelector(NULL_REG); // no species + else + setSpeciesSelector(segMan->getClassAddress(speciesOffset, SCRIPT_GET_LOCK, addr)); +} + +void Object::initSuperClass(SegManager *segMan, reg_t addr) { + uint16 superClassOffset = getSuperClassSelector().offset; + + if (superClassOffset == 0xffff) // -1 + setSuperClassSelector(NULL_REG); // no superclass + else + setSuperClassSelector(segMan->getClassAddress(superClassOffset, SCRIPT_GET_LOCK, addr)); +} + +bool Object::initBaseObject(SegManager *segMan, reg_t addr, bool doInitSuperClass) { + const Object *baseObj = segMan->getObject(getSpeciesSelector()); + + if (baseObj) { + _variables.resize(baseObj->getVarCount()); + // Copy base from species class, as we need its selector IDs + _baseObj = baseObj->_baseObj; + if (doInitSuperClass) + initSuperClass(segMan, addr); + return true; + } + + return false; +} + //-------------------- dynamic memory -------------------- -reg_t DynMem::findCanonicAddress(SegManager *segMan, reg_t addr) { +reg_t DynMem::findCanonicAddress(SegManager *segMan, reg_t addr) const { addr.offset = 0; return addr; } -void DynMem::listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) { +void DynMem::listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) const { (*note)(param, make_reg(segId, 0)); } @@ -724,13 +819,13 @@ void ArrayTable::freeAtAddress(SegManager *segMan, reg_t sub_addr) { freeEntry(sub_addr.offset); } -void ArrayTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) { +void ArrayTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const { if (!isValidEntry(addr.offset)) { warning("Invalid array referenced for outgoing references: %04x:%04x", PRINT_REG(addr)); return; } - SciArray<reg_t> *array = &(_table[addr.offset]); + const SciArray<reg_t> *array = &(_table[addr.offset]); for (uint32 i = 0; i < array->getSize(); i++) { reg_t value = array->getValue(i); @@ -739,7 +834,7 @@ void ArrayTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback } } -Common::String SciString::toString() { +Common::String SciString::toString() const { if (_type != 3) error("SciString::toString(): Array is not a string"); @@ -750,7 +845,7 @@ Common::String SciString::toString() { return string; } -void SciString::fromString(Common::String string) { +void SciString::fromString(const Common::String &string) { if (_type != 3) error("SciString::fromString(): Array is not a string"); diff --git a/engines/sci/engine/segment.h b/engines/sci/engine/segment.h index 1089ada475..f1b6dccaa2 100644 --- a/engines/sci/engine/segment.h +++ b/engines/sci/engine/segment.h @@ -112,7 +112,7 @@ public: * * @param sub_addr base address whose canonic address is to be found */ - virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) { return sub_addr; } + virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const { return sub_addr; } /** * Deallocates all memory associated with the specified address. @@ -125,7 +125,7 @@ public: * @param note Invoked for each address on which free_at_address() makes sense * @param param parameter passed to 'note' */ - virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) {} + virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) const {} /** * Iterates over all references reachable from the specified object. @@ -134,7 +134,7 @@ public: * @param note Invoked for each outgoing reference within the object * Note: This function may also choose to report numbers (segment 0) as adresses */ - virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) {} + virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const {} }; @@ -194,8 +194,8 @@ public: virtual bool isValidOffset(uint16 offset) const; virtual SegmentRef dereference(reg_t pointer); - virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr); - virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note); + virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const; + virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const; virtual void saveLoadWithSerializer(Common::Serializer &ser); }; @@ -205,6 +205,22 @@ enum { OBJECT_FLAG_FREED = (1 << 0) }; +enum infoSelectorFlags { + kInfoFlagClone = 0x0001, + kInfoFlagClass = 0x8000 +}; + +enum ObjectOffsets { + kOffsetLocalVariables = -6, + kOffsetFunctionArea = -4, + kOffsetSelectorCounter = -2, + kOffsetSelectorSegment = 0, + kOffsetInfoSelectorSci0 = 4, + kOffsetNamePointerSci0 = 6, + kOffsetInfoSelectorSci11 = 14, + kOffsetNamePointerSci11 = 16 +}; + class Object { public: Object() { @@ -214,31 +230,34 @@ public: ~Object() { } - reg_t getSpeciesSelector() { return _variables[_offset]; } + reg_t getSpeciesSelector() const { return _variables[_offset]; } void setSpeciesSelector(reg_t value) { _variables[_offset] = value; } - reg_t getSuperClassSelector() { return _variables[_offset + 1]; } + reg_t getSuperClassSelector() const { return _variables[_offset + 1]; } void setSuperClassSelector(reg_t value) { _variables[_offset + 1] = value; } - reg_t getInfoSelector() { return _variables[_offset + 2]; } - void setInfoSelector(reg_t value) { _variables[_offset + 2] = value; } + reg_t getInfoSelector() const { return _variables[_offset + 2]; } + void setInfoSelector(reg_t value) { _variables[_offset + 2] = value; } - reg_t getNameSelector() { return _variables[_offset + 3]; } - void setNameSelector(reg_t value) { _variables[_offset + 3] = value; } + reg_t getNameSelector() const { return _variables[_offset + 3]; } + void setNameSelector(reg_t value) { _variables[_offset + 3] = value; } - reg_t getClassScriptSelector() { return _variables[4]; } + reg_t getPropDictSelector() const { return _variables[2]; } + void setPropDictSelector(reg_t value) { _variables[2] = value; } + + reg_t getClassScriptSelector() const { return _variables[4]; } void setClassScriptSelector(reg_t value) { _variables[4] = value; } - Selector getVarSelector(uint16 i) { return READ_SCI11ENDIAN_UINT16(_baseVars + i); } + Selector getVarSelector(uint16 i) const { return READ_SCI11ENDIAN_UINT16(_baseVars + i); } - reg_t getFunction(uint16 i) { + reg_t getFunction(uint16 i) const { uint16 offset = (getSciVersion() < SCI_VERSION_1_1) ? _methodCount + 1 + i : i * 2 + 2; - return make_reg(_pos.segment, READ_SCI11ENDIAN_UINT16((byte *) (_baseMethod + offset))); + return make_reg(_pos.segment, READ_SCI11ENDIAN_UINT16(_baseMethod + offset)); } - Selector getFuncSelector(uint16 i) { + Selector getFuncSelector(uint16 i) const { uint16 offset = (getSciVersion() < SCI_VERSION_1_1) ? i : i * 2 + 1; - return READ_SCI11ENDIAN_UINT16((byte *) (_baseMethod + offset)); + return READ_SCI11ENDIAN_UINT16(_baseMethod + offset); } /** @@ -247,7 +266,7 @@ public: * superclasses, i.e. failure may be returned even if one of the * superclasses defines the funcselector */ - int funcSelectorPosition(Selector sel) { + int funcSelectorPosition(Selector sel) const { for (uint i = 0; i < _methodCount; i++) if (getFuncSelector(i) == sel) return i; @@ -256,61 +275,56 @@ public: } /** - * Determines if the object explicitly defines slc as a varselector - * Returns -1 if not found + * Determines if the object explicitly defines slc as a varselector. + * Returns -1 if not found. */ - int locateVarSelector(SegManager *segMan, Selector slc); - - bool isClass() { return (getInfoSelector().offset & SCRIPT_INFO_CLASS); } - Object *getClass(SegManager *segMan); + int locateVarSelector(SegManager *segMan, Selector slc) const; - void markAsFreed() { _flags |= OBJECT_FLAG_FREED; } - bool isFreed() { return _flags & OBJECT_FLAG_FREED; } + bool isClass() const { return (getInfoSelector().offset & kInfoFlagClass); } + const Object *getClass(SegManager *segMan) const; - void setVarCount(uint size) { _variables.resize(size); } - uint getVarCount() { return _variables.size(); } + void markAsClone() { setInfoSelector(make_reg(0, kInfoFlagClone)); } + bool isClone() const { return (getInfoSelector().offset & kInfoFlagClone); } - void init(byte *buf, reg_t obj_pos) { - byte *data = (byte *)(buf + obj_pos.offset); - _baseObj = data; - _pos = obj_pos; + void markAsFreed() { _flags |= OBJECT_FLAG_FREED; } + bool isFreed() const { return _flags & OBJECT_FLAG_FREED; } - if (getSciVersion() < SCI_VERSION_1_1) { - _variables.resize(READ_LE_UINT16(data + SCRIPT_SELECTORCTR_OFFSET)); - _baseVars = (uint16 *)(_baseObj + _variables.size() * 2); - _baseMethod = (uint16 *)(data + READ_LE_UINT16(data + SCRIPT_FUNCTAREAPTR_OFFSET)); - _methodCount = READ_LE_UINT16(_baseMethod - 1); - } else { - _variables.resize(READ_SCI11ENDIAN_UINT16(data + 2)); - _baseVars = (uint16 *)(buf + READ_SCI11ENDIAN_UINT16(data + 4)); - _baseMethod = (uint16 *)(buf + READ_SCI11ENDIAN_UINT16(data + 6)); - _methodCount = READ_SCI11ENDIAN_UINT16(_baseMethod); - } + uint getVarCount() const { return _variables.size(); } - for (uint i = 0; i < _variables.size(); i++) - _variables[i] = make_reg(0, READ_SCI11ENDIAN_UINT16(data + (i * 2))); - } + void init(byte *buf, reg_t obj_pos, bool initVariables = true); - reg_t getVariable(uint var) { return _variables[var]; } + reg_t getVariable(uint var) const { return _variables[var]; } + reg_t &getVariableRef(uint var) { return _variables[var]; } - uint16 getMethodCount() { return _methodCount; } - reg_t getPos() { return _pos; } + uint16 getMethodCount() const { return _methodCount; } + reg_t getPos() const { return _pos; } void saveLoadWithSerializer(Common::Serializer &ser); - void cloneFromObject(Object *obj) { + void cloneFromObject(const Object *obj) { _baseObj = obj ? obj->_baseObj : NULL; _baseMethod = obj ? obj->_baseMethod : NULL; _baseVars = obj ? obj->_baseVars : NULL; } + bool relocate(SegmentId segment, int location, size_t scriptSize); + + int propertyOffsetToId(SegManager *segMan, int propertyOffset) const; + + void initSpecies(SegManager *segMan, reg_t addr); + void initSuperClass(SegManager *segMan, reg_t addr); + bool initBaseObject(SegManager *segMan, reg_t addr, bool doInitSuperClass = true); + // TODO: make private - Common::Array<reg_t> _variables; - byte *_baseObj; /**< base + object offset within base */ - uint16 *_baseVars; /**< Pointer to the varselector area for this object */ - uint16 *_baseMethod; /**< Pointer to the method selector area for this object */ + // Only SegManager::reconstructScripts() is left needing direct access to these +public: + const byte *_baseObj; /**< base + object offset within base */ private: + const uint16 *_baseVars; /**< Pointer to the varselector area for this object */ + const uint16 *_baseMethod; /**< Pointer to the method selector area for this object */ + + Common::Array<reg_t> _variables; uint16 _methodCount; int _flags; uint16 _offset; @@ -328,21 +342,28 @@ class Script : public SegmentObj { public: int _nr; /**< Script number */ byte *_buf; /**< Static data buffer, or NULL if not used */ - size_t _bufSize; - size_t _scriptSize; - size_t _heapSize; - byte *_heapStart; /**< Start of heap if SCI1.1, NULL otherwise */ - uint16 *_exportTable; /**< Abs. offset of the export table or 0 if not present */ - int _numExports; /**< Number of entries in the exports table */ - - byte *_synonyms; /**< Synonyms block or 0 if not present*/ - int _numSynonyms; /**< Number of entries in the synonyms block */ + uint32 getScriptSize() { return _scriptSize; } + uint32 getHeapSize() { return _heapSize; } + uint32 getBufSize() { return _bufSize; } protected: int _lockers; /**< Number of classes and objects that require this script */ +private: + size_t _scriptSize; + size_t _heapSize; + size_t _bufSize; + + const uint16 *_exportTable; /**< Abs. offset of the export table or 0 if not present */ + uint16 _numExports; /**< Number of entries in the exports table */ + + const byte *_synonyms; /**< Synonyms block or 0 if not present*/ + uint16 _numSynonyms; /**< Number of entries in the synonyms block */ + + Common::Array<CodeBlock> _codeBlocks; + public: /** * Table for objects, contains property variables. @@ -354,8 +375,6 @@ public: SegmentId _localsSegment; /**< The local variable segment */ LocalVariables *_localsBlock; - Common::Array<CodeBlock> _codeBlocks; - bool _relocated; bool _markedAsDeleted; public: @@ -363,19 +382,21 @@ public: ~Script(); void freeScript(); - bool init(int script_nr, ResourceManager *resMan); + void init(int script_nr, ResourceManager *resMan); + void load(ResourceManager *resMan); virtual bool isValidOffset(uint16 offset) const; virtual SegmentRef dereference(reg_t pointer); - virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr); + virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const; virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr); - virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note); - virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note); + virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) const; + virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const; virtual void saveLoadWithSerializer(Common::Serializer &ser); Object *allocateObject(uint16 offset); Object *getObject(uint16 offset); + const Object *getObject(uint16 offset) const; /** * Informs the segment manager that a code block must be relocated @@ -392,7 +413,7 @@ public: * @returns A newly created Object describing the object, * stored within the relevant script */ - Object *scriptObjInit(reg_t obj_pos); + Object *scriptObjInit(reg_t obj_pos, bool fullObjectInit = true); /** * Removes a script object @@ -407,14 +428,10 @@ public: * @param obj_pos Location (segment, offset) of the block * @return Location of the relocation block */ - void scriptRelocate(reg_t block); - - void heapRelocate(reg_t block); + void relocate(reg_t block); private: - int relocateLocal(SegmentId segment, int location); - int relocateBlock(Common::Array<reg_t> &block, int block_location, SegmentId segment, int location); - int relocateObject(Object &obj, SegmentId segment, int location); + bool relocateLocal(SegmentId segment, int location); public: // script lock operations @@ -435,22 +452,28 @@ public: void setLockers(int lockers); /** + * Retrieves a pointer to the exports of this script + * @return pointer to the exports. + */ + const uint16 *getExportTable() const { return _exportTable; } + + /** + * Retrieves the number of exports of script. + * @return the number of exports of this script + */ + uint16 getExportsNr() const { return _numExports; } + + /** * Retrieves a pointer to the synonyms associated with this script * @return pointer to the synonyms, in non-parsed format. */ - byte *getSynonyms() const; + const byte *getSynonyms() const { return _synonyms; } /** * Retrieves the number of synonyms associated with this script. * @return the number of synonyms associated with this script */ - int getSynonymsNr() const; - - /** - * Sets the script-relative offset of the exports table. - * @param offset script-relative exports table offset - */ - void setExportTableOffset(int offset); + uint16 getSynonymsNr() const { return _numSynonyms; } /** * Validate whether the specified public function is exported by @@ -462,19 +485,6 @@ public: uint16 validateExportFunc(int pubfunct); /** - * Sets the script-relative offset of the synonyms associated with this script. - * @param offset script-relative offset of the synonyms block - */ - void setSynonymsOffset(int offset); - - /** - * Sets the number of synonyms associated with this script, - * @param nr number of synonyms, as to be stored within the script - */ - void setSynonymsNr(int nr); - - - /** * Marks the script as deleted. * This will not actually delete the script. If references remain present on the * heap or the stack, the script will stay in memory in a quasi-deleted state until @@ -508,8 +518,10 @@ public: */ int16 getHeap(uint16 offset) const; -private: - void setScriptSize(int script_nr, ResourceManager *resMan); + /** + * Finds the pointer where a block of a specific type starts from + */ + byte *findBlock(int type); }; /** Data stack */ @@ -529,8 +541,8 @@ public: virtual bool isValidOffset(uint16 offset) const; virtual SegmentRef dereference(reg_t pointer); - virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr); - virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note); + virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const; + virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const; virtual void saveLoadWithSerializer(Common::Serializer &ser); }; @@ -618,7 +630,7 @@ public: entries_used--; } - virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) { + virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) const { for (uint i = 0; i < _table.size(); i++) if (isValidEntry(i)) (*note)(param, make_reg(segId, i)); @@ -631,7 +643,7 @@ struct CloneTable : public Table<Clone> { CloneTable() : Table<Clone>(SEG_TYPE_CLONES) {} virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr); - virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note); + virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const; virtual void saveLoadWithSerializer(Common::Serializer &ser); }; @@ -642,7 +654,7 @@ struct NodeTable : public Table<Node> { NodeTable() : Table<Node>(SEG_TYPE_NODES) {} virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr); - virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note); + virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const; virtual void saveLoadWithSerializer(Common::Serializer &ser); }; @@ -653,7 +665,7 @@ struct ListTable : public Table<List> { ListTable() : Table<List>(SEG_TYPE_LISTS) {} virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr); - virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note); + virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const; virtual void saveLoadWithSerializer(Common::Serializer &ser); }; @@ -688,8 +700,8 @@ public: virtual bool isValidOffset(uint16 offset) const; virtual SegmentRef dereference(reg_t pointer); - virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr); - virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note); + virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const; + virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) const; virtual void saveLoadWithSerializer(Common::Serializer &ser); }; @@ -782,7 +794,7 @@ public: _size = _actualSize = size; } - T getValue(uint16 index) { + T getValue(uint16 index) const { if (index >= _size) error("SciArray::getValue(): %d is out of bounds (%d)", index, _size); @@ -796,8 +808,8 @@ public: _data[index] = value; } - byte getType() { return _type; } - uint32 getSize() { return _size; } + byte getType() const { return _type; } + uint32 getSize() const { return _size; } T *getRawData() { return _data; } protected: @@ -814,15 +826,15 @@ public: // We overload destroy to ensure the string type is 3 after destroying void destroy() { SciArray<char>::destroy(); _type = 3; } - Common::String toString(); - void fromString(Common::String string); + Common::String toString() const; + void fromString(const Common::String &string); }; struct ArrayTable : public Table<SciArray<reg_t> > { ArrayTable() : Table<SciArray<reg_t> >(SEG_TYPE_ARRAY) {} virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr); - virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note); + virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const; void saveLoadWithSerializer(Common::Serializer &ser); SegmentRef dereference(reg_t pointer); diff --git a/engines/sci/engine/selector.cpp b/engines/sci/engine/selector.cpp index e226c4b574..04a1b8fbba 100644 --- a/engines/sci/engine/selector.cpp +++ b/engines/sci/engine/selector.cpp @@ -153,11 +153,11 @@ void Kernel::mapSelectors() { FIND_SELECTOR(subtitleLang); FIND_SELECTOR(parseLang); FIND_SELECTOR(overlay); - FIND_SELECTOR(setCursor); FIND_SELECTOR(topString); FIND_SELECTOR(scaleSignal); FIND_SELECTOR(scaleX); FIND_SELECTOR(scaleY); + FIND_SELECTOR(iconIndex); #ifdef ENABLE_SCI32 FIND_SELECTOR(data); @@ -175,32 +175,32 @@ void Kernel::mapSelectors() { #endif } -reg_t read_selector(SegManager *segMan, reg_t object, Selector selector_id) { +reg_t readSelector(SegManager *segMan, reg_t object, Selector selectorId) { ObjVarRef address; - if (lookup_selector(segMan, object, selector_id, &address, NULL) != kSelectorVariable) + if (lookupSelector(segMan, object, selectorId, &address, NULL) != kSelectorVariable) return NULL_REG; else return *address.getPointer(segMan); } -void write_selector(SegManager *segMan, reg_t object, Selector selector_id, reg_t value) { +void writeSelector(SegManager *segMan, reg_t object, Selector selectorId, reg_t value) { ObjVarRef address; - if ((selector_id < 0) || (selector_id > (int)g_sci->getKernel()->getSelectorNamesSize())) { + if ((selectorId < 0) || (selectorId > (int)g_sci->getKernel()->getSelectorNamesSize())) { warning("Attempt to write to invalid selector %d of" - " object at %04x:%04x.", selector_id, PRINT_REG(object)); + " object at %04x:%04x.", selectorId, PRINT_REG(object)); return; } - if (lookup_selector(segMan, object, selector_id, &address, NULL) != kSelectorVariable) + if (lookupSelector(segMan, object, selectorId, &address, NULL) != kSelectorVariable) warning("Selector '%s' of object at %04x:%04x could not be" - " written to", g_sci->getKernel()->getSelectorName(selector_id).c_str(), PRINT_REG(object)); + " written to", g_sci->getKernel()->getSelectorName(selectorId).c_str(), PRINT_REG(object)); else *address.getPointer(segMan) = value; } -int invoke_selector_argv(EngineState *s, reg_t object, int selector_id, SelectorInvocation noinvalid, +int invokeSelectorArgv(EngineState *s, reg_t object, int selectorId, SelectorInvocation noinvalid, int k_argc, StackPtr k_argp, int argc, const reg_t *argv) { int i; int framesize = 2 + 1 * argc; @@ -208,21 +208,21 @@ int invoke_selector_argv(EngineState *s, reg_t object, int selector_id, Selector int slc_type; StackPtr stackframe = k_argp + k_argc; - stackframe[0] = make_reg(0, selector_id); // The selector we want to call + stackframe[0] = make_reg(0, selectorId); // The selector we want to call stackframe[1] = make_reg(0, argc); // Argument count - slc_type = lookup_selector(s->_segMan, object, selector_id, NULL, &address); + slc_type = lookupSelector(s->_segMan, object, selectorId, NULL, &address); if (slc_type == kSelectorNone) { warning("Selector '%s' of object at %04x:%04x could not be invoked", - g_sci->getKernel()->getSelectorName(selector_id).c_str(), PRINT_REG(object)); + g_sci->getKernel()->getSelectorName(selectorId).c_str(), PRINT_REG(object)); if (noinvalid == kStopOnInvalidSelector) error("[Kernel] Not recoverable: VM was halted"); return 1; } if (slc_type == kSelectorVariable) { warning("Attempting to invoke variable selector %s of object %04x:%04x", - g_sci->getKernel()->getSelectorName(selector_id).c_str(), PRINT_REG(object)); + g_sci->getKernel()->getSelectorName(selectorId).c_str(), PRINT_REG(object)); return 0; } @@ -242,7 +242,7 @@ int invoke_selector_argv(EngineState *s, reg_t object, int selector_id, Selector return 0; } -int invoke_selector(EngineState *s, reg_t object, int selector_id, SelectorInvocation noinvalid, +int invokeSelector(EngineState *s, reg_t object, int selectorId, SelectorInvocation noinvalid, int k_argc, StackPtr k_argp, int argc, ...) { va_list argp; reg_t *args = new reg_t[argc]; @@ -252,28 +252,28 @@ int invoke_selector(EngineState *s, reg_t object, int selector_id, SelectorInvoc args[i] = va_arg(argp, reg_t); va_end(argp); - int retval = invoke_selector_argv(s, object, selector_id, noinvalid, k_argc, k_argp, argc, args); + int retval = invokeSelectorArgv(s, object, selectorId, noinvalid, k_argc, k_argp, argc, args); delete[] args; return retval; } -SelectorType lookup_selector(SegManager *segMan, reg_t obj_location, Selector selector_id, ObjVarRef *varp, reg_t *fptr) { - Object *obj = segMan->getObject(obj_location); +SelectorType lookupSelector(SegManager *segMan, reg_t obj_location, Selector selectorId, ObjVarRef *varp, reg_t *fptr) { + const Object *obj = segMan->getObject(obj_location); int index; bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); // Early SCI versions used the LSB in the selector ID as a read/write // toggle, meaning that we must remove it for selector lookup. if (oldScriptHeader) - selector_id &= ~1; + selectorId &= ~1; if (!obj) { - error("lookup_selector(): Attempt to send to non-object or invalid script. Address was %04x:%04x", + error("lookupSelector(): Attempt to send to non-object or invalid script. Address was %04x:%04x", PRINT_REG(obj_location)); } - index = obj->locateVarSelector(segMan, selector_id); + index = obj->locateVarSelector(segMan, selectorId); if (index >= 0) { // Found it as a variable @@ -285,7 +285,7 @@ SelectorType lookup_selector(SegManager *segMan, reg_t obj_location, Selector se } else { // Check if it's a method, with recursive lookup in superclasses while (obj) { - index = obj->funcSelectorPosition(selector_id); + index = obj->funcSelectorPosition(selectorId); if (index >= 0) { if (fptr) *fptr = obj->getFunction(index); @@ -300,7 +300,7 @@ SelectorType lookup_selector(SegManager *segMan, reg_t obj_location, Selector se } -// return _lookup_selector_function(segMan, obj, selector_id, fptr); +// return _lookupSelector_function(segMan, obj, selectorId, fptr); } } // End of namespace Sci diff --git a/engines/sci/engine/selector.h b/engines/sci/engine/selector.h index 70eeb34d93..f50b9ab1b3 100644 --- a/engines/sci/engine/selector.h +++ b/engines/sci/engine/selector.h @@ -34,20 +34,15 @@ namespace Sci { - -/******************** Selector functionality ********************/ - enum SelectorInvocation { kStopOnInvalidSelector = 0, kContinueOnInvalidSelector = 1 }; - /** * Map a selector name to a selector id. Shortcut for accessing the selector cache. */ #define SELECTOR(_slc_) (g_sci->getKernel()->_selectorCache._slc_) -//#define SELECTOR(_slc_) _slc_ /** * Retrieves a selector from an object. @@ -58,8 +53,8 @@ enum SelectorInvocation { * This macro halts on error. 'selector' must be a selector name registered in vm.h's * SelectorCache and mapped in script.cpp. */ -#define GET_SEL32(segMan, _obj_, _slc_) read_selector(segMan, _obj_, _slc_) -#define GET_SEL32V(segMan, _obj_, _slc_) (GET_SEL32(segMan, _obj_, _slc_).offset) +reg_t readSelector(SegManager *segMan, reg_t object, Selector selectorId); +#define readSelectorValue(segMan, _obj_, _slc_) (readSelector(segMan, _obj_, _slc_).offset) /** * Writes a selector value to an object. @@ -70,27 +65,25 @@ enum SelectorInvocation { * This macro halts on error. 'selector' must be a selector name registered in vm.h's * SelectorCache and mapped in script.cpp. */ -#define PUT_SEL32(segMan, _obj_, _slc_, _val_) write_selector(segMan, _obj_, _slc_, _val_) -#define PUT_SEL32V(segMan, _obj_, _slc_, _val_) PUT_SEL32(segMan, _obj_, _slc_, make_reg(0, _val_)) +void writeSelector(SegManager *segMan, reg_t object, Selector selectorId, reg_t value); +#define writeSelectorValue(segMan, _obj_, _slc_, _val_) writeSelector(segMan, _obj_, _slc_, make_reg(0, _val_)) +/** + * Invokes a selector from an object. + */ +int invokeSelector(EngineState *s, reg_t object, int selectorId, SelectorInvocation noinvalid, + int k_argc, StackPtr k_argp, int argc, ...); +int invokeSelectorArgv(EngineState *s, reg_t object, int selectorId, SelectorInvocation noinvalid, + int k_argc, StackPtr k_argp, int argc, const reg_t *argv); /** - * Kludge for use with invoke_selector(). Used for compatibility with compilers + * Kludge for use with invokeSelector(). Used for compatibility with compilers * that cannot handle vararg macros. */ #define INV_SEL(s, _object_, _selector_, _noinvalid_) \ s, _object_, g_sci->getKernel()->_selectorCache._selector_, _noinvalid_, argc, argv -reg_t read_selector(SegManager *segMan, reg_t object, Selector selector_id); -void write_selector(SegManager *segMan, reg_t object, Selector selector_id, reg_t value); -int invoke_selector(EngineState *s, reg_t object, int selector_id, SelectorInvocation noinvalid, - int k_argc, StackPtr k_argp, int argc, ...); -int invoke_selector_argv(EngineState *s, reg_t object, int selector_id, SelectorInvocation noinvalid, - int k_argc, StackPtr k_argp, int argc, const reg_t *argv); - - - } // End of namespace Sci #endif // SCI_ENGINE_KERNEL_H diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp index bd78639c77..b4a04f8826 100644 --- a/engines/sci/engine/state.cpp +++ b/engines/sci/engine/state.cpp @@ -69,51 +69,56 @@ static const uint16 s_halfWidthSJISMap[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -EngineState::EngineState(Vocabulary *voc, SegManager *segMan) -: _voc(voc), _segMan(segMan), _dirseeker() { +EngineState::EngineState(SegManager *segMan) +: _segMan(segMan), _dirseeker() { + reset(false); +} + +EngineState::~EngineState() { + delete _msgState; +} + +void EngineState::reset(bool isRestoring) { #ifdef USE_OLD_MUSIC_FUNCTIONS sfx_init_flags = 0; #endif - restarting_flags = 0; - - last_wait_time = 0; + if (!isRestoring) { + script_000 = 0; + _gameObj = NULL_REG; - _fileHandles.resize(5); + _memorySegmentSize = 0; + _soundCmd = 0; - execution_stack_base = 0; - _executionStackPosChanged = false; + restarting_flags = 0; - r_acc = NULL_REG; - restAdjust = 0; - r_prev = NULL_REG; + execution_stack_base = 0; + _executionStackPosChanged = false; - stack_base = 0; - stack_top = 0; + _fileHandles.resize(5); - script_000 = 0; + r_acc = NULL_REG; + restAdjust = 0; + r_prev = NULL_REG; - sys_strings_segment = 0; - sys_strings = 0; + stack_base = 0; + stack_top = 0; + } - _gameObj = NULL_REG; + last_wait_time = 0; gc_countdown = 0; - successor = 0; - _throttleCounter = 0; _throttleLastTime = 0; _throttleTrigger = false; - _memorySegmentSize = 0; - - _soundCmd = 0; -} + script_abort_flag = 0; + script_step_counter = 0; + script_gc_interval = GC_INTERVAL; -EngineState::~EngineState() { - delete _msgState; + restoring = false; } void EngineState::wait(int16 ticks) { @@ -135,6 +140,15 @@ void EngineState::setRoomNumber(uint16 roomNumber) { script_000->_localsBlock->_locals[13] = make_reg(0, roomNumber); } +void EngineState::shrinkStackToBase() { + uint size = execution_stack_base + 1; + assert(_executionStack.size() >= size); + Common::List<ExecStack>::iterator iter = _executionStack.begin(); + for (uint i = 0; i < size; ++i) + ++iter; + _executionStack.erase(iter, _executionStack.end()); +} + static kLanguage charToLanguage(const char c) { switch (c) { case 'F': @@ -218,7 +232,7 @@ kLanguage SciEngine::getSciLanguage() { lang = K_LANG_ENGLISH; if (_kernel->_selectorCache.printLang != -1) { - lang = (kLanguage)GET_SEL32V(_gamestate->_segMan, _gamestate->_gameObj, SELECTOR(printLang)); + lang = (kLanguage)readSelectorValue(_gamestate->_segMan, _gamestate->_gameObj, SELECTOR(printLang)); if ((getSciVersion() >= SCI_VERSION_1_1) || (lang == K_LANG_NONE)) { // If language is set to none, we use the language from the game detector. @@ -253,7 +267,7 @@ kLanguage SciEngine::getSciLanguage() { } // Store language in printLang selector - PUT_SEL32V(_gamestate->_segMan, _gamestate->_gameObj, SELECTOR(printLang), lang); + writeSelectorValue(_gamestate->_segMan, _gamestate->_gameObj, SELECTOR(printLang), lang); } } @@ -265,7 +279,7 @@ Common::String SciEngine::strSplit(const char *str, const char *sep) { kLanguage subLang = K_LANG_NONE; if (_kernel->_selectorCache.subtitleLang != -1) { - subLang = (kLanguage)GET_SEL32V(_gamestate->_segMan, _gamestate->_gameObj, SELECTOR(subtitleLang)); + subLang = (kLanguage)readSelectorValue(_gamestate->_segMan, _gamestate->_gameObj, SELECTOR(subtitleLang)); } kLanguage secondLang; diff --git a/engines/sci/engine/state.h b/engines/sci/engine/state.h index c4b995806f..68e6a5516a 100644 --- a/engines/sci/engine/state.h +++ b/engines/sci/engine/state.h @@ -76,8 +76,7 @@ enum { enum { SCI_GAME_IS_NOT_RESTARTING = 0, SCI_GAME_WAS_RESTARTED = 1, - SCI_GAME_IS_RESTARTING_NOW = 2, - SCI_GAME_WAS_RESTARTED_AT_LEAST_ONCE = 4 + SCI_GAME_IS_RESTARTING_NOW = 2 }; class FileHandle { @@ -96,16 +95,13 @@ public: struct EngineState : public Common::Serializable { public: - EngineState(Vocabulary *voc, SegManager *segMan); + EngineState(SegManager *segMan); virtual ~EngineState(); virtual void saveLoadWithSerializer(Common::Serializer &ser); public: SegManager *_segMan; /**< The segment manager */ - Vocabulary *_voc; - - Common::String _gameId; /**< Designation of the primary object (which inherits from Game) */ /* Non-VM information */ @@ -152,14 +148,34 @@ public: StackPtr stack_base; /**< Pointer to the least stack element */ StackPtr stack_top; /**< First invalid stack element */ + // Script state + ExecStack *xs; + reg_t *variables[4]; ///< global, local, temp, param, as immediate pointers + reg_t *variables_base[4]; ///< Used for referencing VM ops + SegmentId variables_seg[4]; ///< Same as above, contains segment IDs + int variables_max[4]; ///< Max. values for all variables + Script *script_000; /**< script 000, e.g. for globals */ + int loadFromLauncher; + + /** + * Set this to 1 to abort script execution immediately. Aborting will + * leave the debug exec stack intact. + * Set it to 2 to force a replay afterwards. + */ + int script_abort_flag; // Set to 1 to abort execution. Set to 2 to force a replay afterwards + int script_step_counter; // Counts the number of steps executed + int script_gc_interval; // Number of steps in between gcs + uint16 currentRoomNumber() const; void setRoomNumber(uint16 roomNumber); - /* System strings */ - SegmentId sys_strings_segment; - SystemStrings *sys_strings; + /** + * Shrink execution stack to size. + * Contains an assert it is not already smaller. + */ + void shrinkStackToBase(); reg_t _gameObj; /**< Pointer to the game object */ @@ -176,7 +192,12 @@ public: uint _memorySegmentSize; byte _memorySegment[kMemorySegmentMax]; - EngineState *successor; /**< Successor of this state: Used for restoring */ + /** + * Resets the engine state. + */ + void reset(bool isRestoring); + + bool restoring; /**< A flag to indicate if a game is being restored */ }; } // End of namespace Sci diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp index e2ee2e1971..b04cc473b5 100644 --- a/engines/sci/engine/vm.cpp +++ b/engines/sci/engine/vm.cpp @@ -46,13 +46,6 @@ const reg_t SIGNAL_REG = {0, SIGNAL_OFFSET}; //#define VM_DEBUG_SEND -ScriptState scriptState; // FIXME: Avoid non-const global vars -int g_loadFromLauncher; // FIXME: Avoid non-const global vars - -int script_abort_flag = 0; // Set to 1 to abort execution. Set to 2 to force a replay afterwards // FIXME: Avoid non-const global vars -int script_step_counter = 0; // Counts the number of steps executed // FIXME: Avoid non-const global vars -int script_gc_interval = GC_INTERVAL; // Number of steps in between gcs // FIXME: Avoid non-const global vars - #define SCI_XS_CALLEE_LOCALS ((SegmentId)-1) /** @@ -120,7 +113,7 @@ static reg_t &validate_property(Object *obj, int index) { return dummyReg; } - return obj->_variables[index]; + return obj->getVariableRef(index); } static StackPtr validate_stack_addr(EngineState *s, StackPtr sp) { @@ -212,7 +205,7 @@ static void validate_write_var(reg_t *r, reg_t *stack_base, int type, int max, i if (!stopGroopPos.isNull()) { // does the game have a stopGroop object? // Find the "client" member variable of the stopGroop object, and update it ObjVarRef varp; - if (lookup_selector(segMan, stopGroopPos, kernel->_selectorCache.client, &varp, NULL) == kSelectorVariable) { + if (lookupSelector(segMan, stopGroopPos, kernel->_selectorCache.client, &varp, NULL) == kSelectorVariable) { reg_t *clientVar = varp.getPointer(segMan); *clientVar = value; } @@ -236,8 +229,8 @@ static void validate_write_var(reg_t *r, reg_t *stack_base, int type, int max, i #endif -#define READ_VAR(type, index, def) validate_read_var(scriptState.variables[type], s->stack_base, type, scriptState.variables_max[type], index, __LINE__, def) -#define WRITE_VAR(type, index, value) validate_write_var(scriptState.variables[type], s->stack_base, type, scriptState.variables_max[type], index, __LINE__, value, s->_segMan, g_sci->getKernel()) +#define READ_VAR(type, index, def) validate_read_var(s->variables[type], s->stack_base, type, s->variables_max[type], index, __LINE__, def) +#define WRITE_VAR(type, index, value) validate_write_var(s->variables[type], s->stack_base, type, s->variables_max[type], index, __LINE__, value, s->_segMan, g_sci->getKernel()) #define WRITE_VAR16(type, index, value) WRITE_VAR(type, index, make_reg(0, value)); #define ACC_ARITHMETIC_L(op) make_reg(0, (op validate_arithmetic(s->r_acc))) @@ -251,8 +244,8 @@ static void validate_write_var(reg_t *r, reg_t *stack_base, int type, int max, i #define PUSH(v) PUSH32(make_reg(0, v)) #define POP() (validate_arithmetic(POP32())) // 32 bit: -#define PUSH32(a) (*(validate_stack_addr(s, (scriptState.xs->sp)++)) = (a)) -#define POP32() (*(validate_stack_addr(s, --(scriptState.xs->sp)))) +#define PUSH32(a) (*(validate_stack_addr(s, (s->xs->sp)++)) = (a)) +#define POP32() (*(validate_stack_addr(s, --(s->xs->sp)))) ExecStack *execute_method(EngineState *s, uint16 script, uint16 pubfunct, StackPtr sp, reg_t calling_obj, uint16 argc, StackPtr argp) { int seg = s->_segMan->getScriptSegment(script); @@ -341,7 +334,7 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt int selector; int argc; int origin = s->_executionStack.size()-1; // Origin: Used for debugging - int print_send_action = 0; + bool printSendActions = false; // We return a pointer to the new active ExecStack // The selector calls we catch are stored below: @@ -370,7 +363,7 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt if (bp->type == BREAK_SELECTOR && !strncmp(bp->name.c_str(), method_name, cmplen)) { Console *con = g_sci->getSciDebugger(); con->DebugPrintf("Break on %s (in [%04x:%04x])\n", method_name, PRINT_REG(send_obj)); - print_send_action = 1; + printSendActions = true; g_debugState.debugging = true; g_debugState.breakpointWasHit = true; break; @@ -383,7 +376,7 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt #endif // VM_DEBUG_SEND ObjVarRef varp; - switch (lookup_selector(s->_segMan, send_obj, selector, &varp, &funcp)) { + switch (lookupSelector(s->_segMan, send_obj, selector, &varp, &funcp)) { case kSelectorNone: error("Send to invalid selector 0x%x of object at %04x:%04x", 0xffff & selector, PRINT_REG(send_obj)); break; @@ -398,22 +391,34 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt #endif // VM_DEBUG_SEND // argc == 0: read selector - // argc == 1: write selector - // argc > 1: write selector? - if (print_send_action && argc == 0) { // read selector - printf("[read selector]\n"); - print_send_action = 0; + // argc != 0: write selector + if (printSendActions && !argc) { // read selector + debug("[read selector]\n"); + printSendActions = false; } - if (print_send_action && argc > 0) { + if (printSendActions && argc) { reg_t oldReg = *varp.getPointer(s->_segMan); reg_t newReg = argp[1]; - printf("[write to selector: change %04x:%04x to %04x:%04x]\n", PRINT_REG(oldReg), PRINT_REG(newReg)); - print_send_action = 0; + debug("[write to selector: change %04x:%04x to %04x:%04x]\n", PRINT_REG(oldReg), PRINT_REG(newReg)); + printSendActions = false; } - if (argc > 1) - warning("send_selector(): more than 1 parameter (%d) while modifying a variable selector", argc); + if (argc > 1) { + // argc can indeed be bigger than 1 in some cases, and it seems correct + // (i.e. we should skip that many bytes later on)... question is, why + // does this occur? Could such calls be used to point to data after X + // bytes in the heap? What are the skipped bytes in this case? + // In SQ4CD, this occurs with the returnVal selector of object + // Sq4GlobalNarrator when the game starts, and right after the narrator + // is heard (e.g. after he talks when examining something) + reg_t oldReg = *varp.getPointer(s->_segMan); + reg_t newReg = argp[1]; + warning("send_selector(): argc = %d while modifying variable selector " + "%x (%s) of object %04x:%04x (%s) from %04x:%04x to %04x:%04x", + argc, selector, g_sci->getKernel()->getSelectorName(selector).c_str(), PRINT_REG(send_obj), + s->_segMan->getObjectName(send_obj), PRINT_REG(oldReg), PRINT_REG(newReg)); + } { CallsStruct call; @@ -438,9 +443,9 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt } printf(") at %04x:%04x\n", PRINT_REG(funcp)); #endif // VM_DEBUG_SEND - if (print_send_action) { - printf("[invoke selector]\n"); - print_send_action = 0; + if (printSendActions) { + debug("[invoke selector]\n"); + printSendActions = false; } { @@ -456,7 +461,7 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt } break; - } // switch (lookup_selector()) + } // switch (lookupSelector()) framesize -= (2 + argc); argp += argc + 1; @@ -477,9 +482,7 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt _exec_varselectors(s); - if (s->_executionStack.empty()) - return NULL; - return &(s->_executionStack.back()); + return s->_executionStack.empty() ? NULL : &(s->_executionStack.back()); } static ExecStack *add_exec_stack_varselector(Common::List<ExecStack> &execStack, reg_t objp, int argc, StackPtr argp, Selector selector, const ObjVarRef& address, int origin) { @@ -565,11 +568,11 @@ static void callKernelFunc(EngineState *s, int kernelFuncNum, int argc) { const KernelFuncWithSignature &kernelFunc = g_sci->getKernel()->_kernelFuncs[kernelFuncNum]; if (kernelFunc.signature - && !g_sci->getKernel()->signatureMatch(kernelFunc.signature, argc, scriptState.xs->sp + 1)) { + && !g_sci->getKernel()->signatureMatch(kernelFunc.signature, argc, s->xs->sp + 1)) { error("[VM] Invalid arguments to kernel call %x", kernelFuncNum); } - reg_t *argv = scriptState.xs->sp + 1; + reg_t *argv = s->xs->sp + 1; if (!kernelFunc.isDummy) { // Add stack frame to indicate we're executing a callk. @@ -583,19 +586,26 @@ static void callKernelFunc(EngineState *s, int kernelFuncNum, int argc) { //warning("callk %s", kernelFunc.orig_name.c_str()); - // TODO: SCI2/SCI2.1+ equivalent, once saving/loading works in SCI2/SCI2.1+ - if (g_loadFromLauncher >= 0 && kernelFuncNum == 0x8) { - // A game is being loaded from the launcher, and kDisplay is called, all initialization has taken - // place (i.e. menus have been constructed etc). Therefore, inject a kRestoreGame call - // here, instead of the requested function. - int saveSlot = g_loadFromLauncher; - g_loadFromLauncher = -1; // invalidate slot, so that we don't load again - - if (saveSlot < 0) - error("Requested to load invalid save slot"); // should never happen, really - - reg_t restoreArgv[2] = { NULL_REG, make_reg(0, saveSlot) }; // special call (argv[0] is NULL) - kRestoreGame(s, 2, restoreArgv); + // TODO: SCI2.1 equivalent + if (s->loadFromLauncher >= 0 && ( + (kernelFuncNum == 0x8 && getSciVersion() <= SCI_VERSION_1_1) || // DrawPic + (kernelFuncNum == 0x3d && getSciVersion() == SCI_VERSION_2) // GetSaveDir + //(kernelFuncNum == 0x28 && getSciVersion() == SCI_VERSION_2_1) // AddPlane + )) { + + // A game is being loaded from the launcher, and the game is about to draw something on + // screen, hence all initialization has taken place (i.e. menus have been constructed etc). + // Therefore, inject a kRestoreGame call here, instead of the requested function. + // The restore call is injected here mainly for games which have a menu, as the menu is + // constructed when the game starts and is not reconstructed when a saved game is loaded. + int saveSlot = s->loadFromLauncher; + s->loadFromLauncher = -1; // invalidate slot, so that we don't load again + + if (saveSlot < 0) + error("Requested to load invalid save slot"); // should never happen, really + + reg_t restoreArgv[2] = { NULL_REG, make_reg(0, saveSlot) }; // special call (argv[0] is NULL) + kRestoreGame(s, 2, restoreArgv); } else { // Call kernel function s->r_acc = kernelFunc.fun(s, argc, argv); @@ -620,7 +630,7 @@ static void callKernelFunc(EngineState *s, int kernelFuncNum, int argc) { static void gc_countdown(EngineState *s) { if (s->gc_countdown-- <= 0) { - s->gc_countdown = script_gc_interval; + s->gc_countdown = s->script_gc_interval; run_gc(s); } } @@ -708,13 +718,13 @@ void run_vm(EngineState *s, bool restoring) { StackPtr s_temp; // Temporary stack pointer int16 opparams[4]; // opcode parameters - scriptState.restAdjust = s->restAdjust; + s->restAdjust = s->restAdjust; // &rest adjusts the parameter count by this value // Current execution data: - scriptState.xs = &(s->_executionStack.back()); + s->xs = &(s->_executionStack.back()); ExecStack *xs_new = NULL; - Object *obj = s->_segMan->getObject(scriptState.xs->objp); - Script *local_script = s->_segMan->getScriptIfLoaded(scriptState.xs->local_segment); + Object *obj = s->_segMan->getObject(s->xs->objp); + Script *local_script = s->_segMan->getScriptIfLoaded(s->xs->local_segment); int old_execution_stack_base = s->execution_stack_base; // Used to detect the stack bottom, for "physical" returns const byte *code_buf = NULL; // (Avoid spurious warning) @@ -724,25 +734,25 @@ void run_vm(EngineState *s, bool restoring) { } if (!restoring) - s->execution_stack_base = s->_executionStack.size()-1; + s->execution_stack_base = s->_executionStack.size() - 1; #ifndef DISABLE_VALIDATIONS // Initialize maximum variable count if (s->script_000->_localsBlock) - scriptState.variables_max[VAR_GLOBAL] = s->script_000->_localsBlock->_locals.size(); + s->variables_max[VAR_GLOBAL] = s->script_000->_localsBlock->_locals.size(); else - scriptState.variables_max[VAR_GLOBAL] = 0; + s->variables_max[VAR_GLOBAL] = 0; #endif - scriptState.variables_seg[VAR_GLOBAL] = s->script_000->_localsSegment; - scriptState.variables_seg[VAR_TEMP] = scriptState.variables_seg[VAR_PARAM] = s->_segMan->findSegmentByType(SEG_TYPE_STACK); - scriptState.variables_base[VAR_TEMP] = scriptState.variables_base[VAR_PARAM] = s->stack_base; + s->variables_seg[VAR_GLOBAL] = s->script_000->_localsSegment; + s->variables_seg[VAR_TEMP] = s->variables_seg[VAR_PARAM] = s->_segMan->findSegmentByType(SEG_TYPE_STACK); + s->variables_base[VAR_TEMP] = s->variables_base[VAR_PARAM] = s->stack_base; // SCI code reads the zeroth argument to determine argc if (s->script_000->_localsBlock) - scriptState.variables_base[VAR_GLOBAL] = scriptState.variables[VAR_GLOBAL] = s->script_000->_localsBlock->_locals.begin(); + s->variables_base[VAR_GLOBAL] = s->variables[VAR_GLOBAL] = s->script_000->_localsBlock->_locals.begin(); else - scriptState.variables_base[VAR_GLOBAL] = scriptState.variables[VAR_GLOBAL] = NULL; + s->variables_base[VAR_GLOBAL] = s->variables[VAR_GLOBAL] = NULL; s->_executionStackPosChanged = true; // Force initialization @@ -750,63 +760,63 @@ void run_vm(EngineState *s, bool restoring) { int var_type; // See description below int var_number; - g_debugState.old_pc_offset = scriptState.xs->addr.pc.offset; - g_debugState.old_sp = scriptState.xs->sp; + g_debugState.old_pc_offset = s->xs->addr.pc.offset; + g_debugState.old_sp = s->xs->sp; if (s->_executionStackPosChanged) { Script *scr; - scriptState.xs = &(s->_executionStack.back()); + s->xs = &(s->_executionStack.back()); s->_executionStackPosChanged = false; - scr = s->_segMan->getScriptIfLoaded(scriptState.xs->addr.pc.segment); + scr = s->_segMan->getScriptIfLoaded(s->xs->addr.pc.segment); if (!scr) { // No script? Implicit return via fake instruction buffer - warning("Running on non-existant script in segment %x", scriptState.xs->addr.pc.segment); + warning("Running on non-existant script in segment %x", s->xs->addr.pc.segment); code_buf = _fake_return_buffer; #ifndef DISABLE_VALIDATIONS code_buf_size = 2; #endif - scriptState.xs->addr.pc.offset = 1; + s->xs->addr.pc.offset = 1; scr = NULL; obj = NULL; } else { - obj = s->_segMan->getObject(scriptState.xs->objp); + obj = s->_segMan->getObject(s->xs->objp); code_buf = scr->_buf; #ifndef DISABLE_VALIDATIONS - code_buf_size = scr->_bufSize; + code_buf_size = scr->getBufSize(); #endif - local_script = s->_segMan->getScriptIfLoaded(scriptState.xs->local_segment); + local_script = s->_segMan->getScriptIfLoaded(s->xs->local_segment); if (!local_script) { - warning("Could not find local script from segment %x", scriptState.xs->local_segment); + warning("Could not find local script from segment %x", s->xs->local_segment); local_script = NULL; - scriptState.variables_base[VAR_LOCAL] = scriptState.variables[VAR_LOCAL] = NULL; + s->variables_base[VAR_LOCAL] = s->variables[VAR_LOCAL] = NULL; #ifndef DISABLE_VALIDATIONS - scriptState.variables_max[VAR_LOCAL] = 0; + s->variables_max[VAR_LOCAL] = 0; #endif } else { - scriptState.variables_seg[VAR_LOCAL] = local_script->_localsSegment; + s->variables_seg[VAR_LOCAL] = local_script->_localsSegment; if (local_script->_localsBlock) - scriptState.variables_base[VAR_LOCAL] = scriptState.variables[VAR_LOCAL] = local_script->_localsBlock->_locals.begin(); + s->variables_base[VAR_LOCAL] = s->variables[VAR_LOCAL] = local_script->_localsBlock->_locals.begin(); else - scriptState.variables_base[VAR_LOCAL] = scriptState.variables[VAR_LOCAL] = NULL; + s->variables_base[VAR_LOCAL] = s->variables[VAR_LOCAL] = NULL; #ifndef DISABLE_VALIDATIONS if (local_script->_localsBlock) - scriptState.variables_max[VAR_LOCAL] = local_script->_localsBlock->_locals.size(); + s->variables_max[VAR_LOCAL] = local_script->_localsBlock->_locals.size(); else - scriptState.variables_max[VAR_LOCAL] = 0; - scriptState.variables_max[VAR_TEMP] = scriptState.xs->sp - scriptState.xs->fp; - scriptState.variables_max[VAR_PARAM] = scriptState.xs->argc + 1; + s->variables_max[VAR_LOCAL] = 0; + s->variables_max[VAR_TEMP] = s->xs->sp - s->xs->fp; + s->variables_max[VAR_PARAM] = s->xs->argc + 1; #endif } - scriptState.variables[VAR_TEMP] = scriptState.xs->fp; - scriptState.variables[VAR_PARAM] = scriptState.xs->variables_argp; + s->variables[VAR_TEMP] = s->xs->fp; + s->variables[VAR_PARAM] = s->xs->variables_argp; } } - if (script_abort_flag || g_engine->shouldQuit()) + if (s->script_abort_flag || g_engine->shouldQuit()) return; // Emergency // Debug if this has been requested: @@ -821,18 +831,20 @@ void run_vm(EngineState *s, bool restoring) { } #ifndef DISABLE_VALIDATIONS - if (scriptState.xs->sp < scriptState.xs->fp) - error("run_vm(): stack underflow"); + if (s->xs->sp < s->xs->fp) + error("run_vm(): stack underflow, sp: %04x:%04x, fp: %04x:%04x", + PRINT_REG(*s->xs->sp), PRINT_REG(*s->xs->fp)); - scriptState.variables_max[VAR_TEMP] = scriptState.xs->sp - scriptState.xs->fp; + s->variables_max[VAR_TEMP] = s->xs->sp - s->xs->fp; - if (scriptState.xs->addr.pc.offset >= code_buf_size) - error("run_vm(): program counter gone astray"); + if (s->xs->addr.pc.offset >= code_buf_size) + error("run_vm(): program counter gone astray, addr: %d, code buffer size: %d", + s->xs->addr.pc.offset, code_buf_size); #endif // Get opcode byte extOpcode; - scriptState.xs->addr.pc.offset += readPMachineInstruction(code_buf + scriptState.xs->addr.pc.offset, extOpcode, opparams); + s->xs->addr.pc.offset += readPMachineInstruction(code_buf + s->xs->addr.pc.offset, extOpcode, opparams); const byte opcode = extOpcode >> 1; switch (opcode) { @@ -1063,16 +1075,16 @@ void run_vm(EngineState *s, bool restoring) { case op_bt: // 0x17 (23) if (s->r_acc.offset || s->r_acc.segment) - scriptState.xs->addr.pc.offset += opparams[0]; + s->xs->addr.pc.offset += opparams[0]; break; case op_bnt: // 0x18 (24) if (!(s->r_acc.offset || s->r_acc.segment)) - scriptState.xs->addr.pc.offset += opparams[0]; + s->xs->addr.pc.offset += opparams[0]; break; case op_jmp: // 0x19 (25) - scriptState.xs->addr.pc.offset += opparams[0]; + s->xs->addr.pc.offset += opparams[0]; break; case op_ldi: // 0x1a (26) @@ -1088,34 +1100,34 @@ void run_vm(EngineState *s, bool restoring) { break; case op_toss: // 0x1d (29) - scriptState.xs->sp--; + s->xs->sp--; break; case op_dup: // 0x1e (30) - r_temp = scriptState.xs->sp[-1]; + r_temp = s->xs->sp[-1]; PUSH32(r_temp); break; case op_link: // 0x1f (31) for (int i = 0; i < opparams[0]; i++) - scriptState.xs->sp[i] = NULL_REG; - scriptState.xs->sp += opparams[0]; + s->xs->sp[i] = NULL_REG; + s->xs->sp += opparams[0]; break; case op_call: { // 0x20 (32) int argc = (opparams[1] >> 1) // Given as offset, but we need count - + 1 + scriptState.restAdjust; - StackPtr call_base = scriptState.xs->sp - argc; - scriptState.xs->sp[1].offset += scriptState.restAdjust; - - xs_new = add_exec_stack_entry(s->_executionStack, make_reg(scriptState.xs->addr.pc.segment, - scriptState.xs->addr.pc.offset + opparams[0]), - scriptState.xs->sp, scriptState.xs->objp, - (validate_arithmetic(*call_base)) + scriptState.restAdjust, - call_base, NULL_SELECTOR, scriptState.xs->objp, - s->_executionStack.size()-1, scriptState.xs->local_segment); - scriptState.restAdjust = 0; // Used up the &rest adjustment - scriptState.xs->sp = call_base; + + 1 + s->restAdjust; + StackPtr call_base = s->xs->sp - argc; + s->xs->sp[1].offset += s->restAdjust; + + xs_new = add_exec_stack_entry(s->_executionStack, make_reg(s->xs->addr.pc.segment, + s->xs->addr.pc.offset + opparams[0]), + s->xs->sp, s->xs->objp, + (validate_arithmetic(*call_base)) + s->restAdjust, + call_base, NULL_SELECTOR, s->xs->objp, + s->_executionStack.size()-1, s->xs->local_segment); + s->restAdjust = 0; // Used up the &rest adjustment + s->xs->sp = call_base; s->_executionStackPosChanged = true; break; @@ -1124,23 +1136,23 @@ void run_vm(EngineState *s, bool restoring) { case op_callk: { // 0x21 (33) gc_countdown(s); - scriptState.xs->sp -= (opparams[1] >> 1) + 1; + s->xs->sp -= (opparams[1] >> 1) + 1; bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); if (!oldScriptHeader) { - scriptState.xs->sp -= scriptState.restAdjust; - s->restAdjust = 0; // We just used up the scriptState.restAdjust, remember? + s->xs->sp -= s->restAdjust; + s->restAdjust = 0; // We just used up the s->restAdjust, remember? } - int argc = validate_arithmetic(scriptState.xs->sp[0]); + int argc = validate_arithmetic(s->xs->sp[0]); if (!oldScriptHeader) - argc += scriptState.restAdjust; + argc += s->restAdjust; callKernelFunc(s, opparams[0], argc); if (!oldScriptHeader) - scriptState.restAdjust = s->restAdjust; + s->restAdjust = s->restAdjust; // Calculate xs again: The kernel function might // have spawned a new VM @@ -1151,27 +1163,27 @@ void run_vm(EngineState *s, bool restoring) { } case op_callb: // 0x22 (34) - temp = ((opparams[1] >> 1) + scriptState.restAdjust + 1); - s_temp = scriptState.xs->sp; - scriptState.xs->sp -= temp; - - scriptState.xs->sp[0].offset += scriptState.restAdjust; - xs_new = execute_method(s, 0, opparams[0], s_temp, scriptState.xs->objp, - scriptState.xs->sp[0].offset, scriptState.xs->sp); - scriptState.restAdjust = 0; // Used up the &rest adjustment + temp = ((opparams[1] >> 1) + s->restAdjust + 1); + s_temp = s->xs->sp; + s->xs->sp -= temp; + + s->xs->sp[0].offset += s->restAdjust; + xs_new = execute_method(s, 0, opparams[0], s_temp, s->xs->objp, + s->xs->sp[0].offset, s->xs->sp); + s->restAdjust = 0; // Used up the &rest adjustment if (xs_new) // in case of error, keep old stack s->_executionStackPosChanged = true; break; case op_calle: // 0x23 (35) - temp = ((opparams[2] >> 1) + scriptState.restAdjust + 1); - s_temp = scriptState.xs->sp; - scriptState.xs->sp -= temp; + temp = ((opparams[2] >> 1) + s->restAdjust + 1); + s_temp = s->xs->sp; + s->xs->sp -= temp; - scriptState.xs->sp[0].offset += scriptState.restAdjust; - xs_new = execute_method(s, opparams[0], opparams[1], s_temp, scriptState.xs->objp, - scriptState.xs->sp[0].offset, scriptState.xs->sp); - scriptState.restAdjust = 0; // Used up the &rest adjustment + s->xs->sp[0].offset += s->restAdjust; + xs_new = execute_method(s, opparams[0], opparams[1], s_temp, s->xs->objp, + s->xs->sp[0].offset, s->xs->sp); + s->restAdjust = 0; // Used up the &rest adjustment if (xs_new) // in case of error, keep old stack s->_executionStackPosChanged = true; @@ -1179,17 +1191,17 @@ void run_vm(EngineState *s, bool restoring) { case op_ret: // 0x24 (36) do { - StackPtr old_sp2 = scriptState.xs->sp; - StackPtr old_fp = scriptState.xs->fp; + StackPtr old_sp2 = s->xs->sp; + StackPtr old_fp = s->xs->fp; ExecStack *old_xs = &(s->_executionStack.back()); - if ((int)s->_executionStack.size()-1 == s->execution_stack_base) { // Have we reached the base? + if ((int)s->_executionStack.size() - 1 == s->execution_stack_base) { // Have we reached the base? s->execution_stack_base = old_execution_stack_base; // Restore stack base s->_executionStack.pop_back(); s->_executionStackPosChanged = true; - s->restAdjust = scriptState.restAdjust; // Update &rest + s->restAdjust = s->restAdjust; // Update &rest return; // "Hard" return } @@ -1205,33 +1217,33 @@ void run_vm(EngineState *s, bool restoring) { // Not reached the base, so let's do a soft return s->_executionStack.pop_back(); s->_executionStackPosChanged = true; - scriptState.xs = &(s->_executionStack.back()); + s->xs = &(s->_executionStack.back()); - if (scriptState.xs->sp == CALL_SP_CARRY // Used in sends to 'carry' the stack pointer - || scriptState.xs->type != EXEC_STACK_TYPE_CALL) { - scriptState.xs->sp = old_sp2; - scriptState.xs->fp = old_fp; + if (s->xs->sp == CALL_SP_CARRY // Used in sends to 'carry' the stack pointer + || s->xs->type != EXEC_STACK_TYPE_CALL) { + s->xs->sp = old_sp2; + s->xs->fp = old_fp; } - } while (scriptState.xs->type == EXEC_STACK_TYPE_VARSELECTOR); + } while (s->xs->type == EXEC_STACK_TYPE_VARSELECTOR); // Iterate over all varselector accesses s->_executionStackPosChanged = true; - xs_new = scriptState.xs; + xs_new = s->xs; break; case op_send: // 0x25 (37) - s_temp = scriptState.xs->sp; - scriptState.xs->sp -= ((opparams[0] >> 1) + scriptState.restAdjust); // Adjust stack + s_temp = s->xs->sp; + s->xs->sp -= ((opparams[0] >> 1) + s->restAdjust); // Adjust stack - scriptState.xs->sp[1].offset += scriptState.restAdjust; + s->xs->sp[1].offset += s->restAdjust; xs_new = send_selector(s, s->r_acc, s->r_acc, s_temp, - (int)(opparams[0] >> 1) + (uint16)scriptState.restAdjust, scriptState.xs->sp); + (int)(opparams[0] >> 1) + (uint16)s->restAdjust, s->xs->sp); - if (xs_new && xs_new != scriptState.xs) + if (xs_new && xs_new != s->xs) s->_executionStackPosChanged = true; - scriptState.restAdjust = 0; + s->restAdjust = 0; break; @@ -1242,7 +1254,7 @@ void run_vm(EngineState *s, bool restoring) { case op_class: // 0x28 (40) s->r_acc = s->_segMan->getClassAddress((unsigned)opparams[0], SCRIPT_GET_LOCK, - scriptState.xs->addr.pc); + s->xs->addr.pc); break; case 0x29: // (41) @@ -1250,48 +1262,48 @@ void run_vm(EngineState *s, bool restoring) { break; case op_self: // 0x2a (42) - s_temp = scriptState.xs->sp; - scriptState.xs->sp -= ((opparams[0] >> 1) + scriptState.restAdjust); // Adjust stack + s_temp = s->xs->sp; + s->xs->sp -= ((opparams[0] >> 1) + s->restAdjust); // Adjust stack - scriptState.xs->sp[1].offset += scriptState.restAdjust; - xs_new = send_selector(s, scriptState.xs->objp, scriptState.xs->objp, - s_temp, (int)(opparams[0] >> 1) + (uint16)scriptState.restAdjust, - scriptState.xs->sp); + s->xs->sp[1].offset += s->restAdjust; + xs_new = send_selector(s, s->xs->objp, s->xs->objp, + s_temp, (int)(opparams[0] >> 1) + (uint16)s->restAdjust, + s->xs->sp); - if (xs_new && xs_new != scriptState.xs) + if (xs_new && xs_new != s->xs) s->_executionStackPosChanged = true; - scriptState.restAdjust = 0; + s->restAdjust = 0; break; case op_super: // 0x2b (43) - r_temp = s->_segMan->getClassAddress(opparams[0], SCRIPT_GET_LOAD, scriptState.xs->addr.pc); + r_temp = s->_segMan->getClassAddress(opparams[0], SCRIPT_GET_LOAD, s->xs->addr.pc); if (!r_temp.segment) error("[VM]: Invalid superclass in object"); else { - s_temp = scriptState.xs->sp; - scriptState.xs->sp -= ((opparams[1] >> 1) + scriptState.restAdjust); // Adjust stack + s_temp = s->xs->sp; + s->xs->sp -= ((opparams[1] >> 1) + s->restAdjust); // Adjust stack - scriptState.xs->sp[1].offset += scriptState.restAdjust; - xs_new = send_selector(s, r_temp, scriptState.xs->objp, s_temp, - (int)(opparams[1] >> 1) + (uint16)scriptState.restAdjust, - scriptState.xs->sp); + s->xs->sp[1].offset += s->restAdjust; + xs_new = send_selector(s, r_temp, s->xs->objp, s_temp, + (int)(opparams[1] >> 1) + (uint16)s->restAdjust, + s->xs->sp); - if (xs_new && xs_new != scriptState.xs) + if (xs_new && xs_new != s->xs) s->_executionStackPosChanged = true; - scriptState.restAdjust = 0; + s->restAdjust = 0; } break; case op_rest: // 0x2c (44) temp = (uint16) opparams[0]; // First argument - scriptState.restAdjust = MAX<int16>(scriptState.xs->argc - temp + 1, 0); // +1 because temp counts the paramcount while argc doesn't + s->restAdjust = MAX<int16>(s->xs->argc - temp + 1, 0); // +1 because temp counts the paramcount while argc doesn't - for (; temp <= scriptState.xs->argc; temp++) - PUSH32(scriptState.xs->variables_argp[temp]); + for (; temp <= s->xs->argc; temp++) + PUSH32(s->xs->variables_argp[temp]); break; @@ -1300,8 +1312,8 @@ void run_vm(EngineState *s, bool restoring) { var_number = temp & 0x03; // Get variable type // Get variable block offset - r_temp.segment = scriptState.variables_seg[var_number]; - r_temp.offset = scriptState.variables[var_number] - scriptState.variables_base[var_number]; + r_temp.segment = s->variables_seg[var_number]; + r_temp.offset = s->variables[var_number] - s->variables_base[var_number]; if (temp & 0x08) // Add accumulator offset if requested r_temp.offset += signed_validate_arithmetic(s->r_acc); @@ -1314,7 +1326,7 @@ void run_vm(EngineState *s, bool restoring) { case op_selfID: // 0x2e (46) - s->r_acc = scriptState.xs->objp; + s->r_acc = s->xs->objp; break; case 0x2f: // (47) @@ -1382,17 +1394,17 @@ void run_vm(EngineState *s, bool restoring) { break; case op_lofsa: // 0x39 (57) - s->r_acc.segment = scriptState.xs->addr.pc.segment; + s->r_acc.segment = s->xs->addr.pc.segment; switch (g_sci->_features->detectLofsType()) { case SCI_VERSION_1_1: - s->r_acc.offset = opparams[0] + local_script->_scriptSize; + s->r_acc.offset = opparams[0] + local_script->getScriptSize(); break; case SCI_VERSION_1_MIDDLE: s->r_acc.offset = opparams[0]; break; default: - s->r_acc.offset = scriptState.xs->addr.pc.offset + opparams[0]; + s->r_acc.offset = s->xs->addr.pc.offset + opparams[0]; } #ifndef DISABLE_VALIDATIONS @@ -1404,17 +1416,17 @@ void run_vm(EngineState *s, bool restoring) { break; case op_lofss: // 0x3a (58) - r_temp.segment = scriptState.xs->addr.pc.segment; + r_temp.segment = s->xs->addr.pc.segment; switch (g_sci->_features->detectLofsType()) { case SCI_VERSION_1_1: - r_temp.offset = opparams[0] + local_script->_scriptSize; + r_temp.offset = opparams[0] + local_script->getScriptSize(); break; case SCI_VERSION_1_MIDDLE: r_temp.offset = opparams[0]; break; default: - r_temp.offset = scriptState.xs->addr.pc.offset + opparams[0]; + r_temp.offset = s->xs->addr.pc.offset + opparams[0]; } #ifndef DISABLE_VALIDATIONS @@ -1440,10 +1452,10 @@ void run_vm(EngineState *s, bool restoring) { case op_pushSelf: // 0x3e (62) if (!(extOpcode & 1)) { - PUSH32(scriptState.xs->objp); + PUSH32(s->xs->objp); } else { // Debug opcode op_file, skip null-terminated string (file name) - while (code_buf[scriptState.xs->addr.pc.offset++]) ; + while (code_buf[s->xs->addr.pc.offset++]) ; } break; @@ -1657,16 +1669,16 @@ void run_vm(EngineState *s, bool restoring) { } // switch (opcode) if (s->_executionStackPosChanged) // Force initialization - scriptState.xs = xs_new; + s->xs = xs_new; //#ifndef DISABLE_VALIDATIONS - if (scriptState.xs != &(s->_executionStack.back())) { + if (s->xs != &(s->_executionStack.back())) { warning("xs is stale (%p vs %p); last command was %02x", - (void *)scriptState.xs, (void *)&(s->_executionStack.back()), + (void *)s->xs, (void *)&(s->_executionStack.back()), opcode); } //#endif - ++script_step_counter; + ++s->script_step_counter; } } @@ -1676,17 +1688,16 @@ static void _init_stack_base_with_selector(EngineState *s, Selector selector) { } static EngineState *_game_run(EngineState *&s) { - EngineState *successor = NULL; - int game_is_finished = 0; + bool restoring = false; if (DebugMan.isDebugChannelEnabled(kDebugLevelOnStartup)) g_sci->getSciDebugger()->attach(); do { s->_executionStackPosChanged = false; - run_vm(s, successor ? true : false); + run_vm(s, restoring); if (s->restarting_flags & SCI_GAME_IS_RESTARTING_NOW) { // Restart was requested? - successor = NULL; + restoring = false; s->_executionStack.clear(); s->_executionStackPosChanged = false; @@ -1700,17 +1711,15 @@ static EngineState *_game_run(EngineState *&s) { send_selector(s, s->_gameObj, s->_gameObj, s->stack_base, 2, s->stack_base); - script_abort_flag = 0; - s->restarting_flags = SCI_GAME_WAS_RESTARTED | SCI_GAME_WAS_RESTARTED_AT_LEAST_ONCE; + s->script_abort_flag = 0; + s->restarting_flags = SCI_GAME_WAS_RESTARTED; } else { - successor = s->successor; - if (successor) { + restoring = s->restoring; + if (restoring) { game_exit(s); - delete s; - s = successor; - - if (script_abort_flag == 2) { + s->restoring = false; + if (s->script_abort_flag == 2) { debugC(2, kDebugLevelVM, "Restarting with replay()"); s->_executionStack.clear(); // Restart with replay @@ -1719,12 +1728,12 @@ static EngineState *_game_run(EngineState *&s) { send_selector(s, s->_gameObj, s->_gameObj, s->stack_base, 2, s->stack_base); } - script_abort_flag = 0; + s->script_abort_flag = 0; } else - game_is_finished = 1; + break; // exit loop } - } while (!game_is_finished); + } while (true); return s; } @@ -1732,7 +1741,7 @@ static EngineState *_game_run(EngineState *&s) { int game_run(EngineState **_s) { EngineState *s = *_s; - debugC(2, kDebugLevelVM, "Calling %s::play()", s->_gameId.c_str()); + debugC(2, kDebugLevelVM, "Calling %s::play()", g_sci->getGameID()); _init_stack_base_with_selector(s, g_sci->getKernel()->_selectorCache.play); // Call the play selector // Now: Register the first element on the execution stack- @@ -1750,28 +1759,18 @@ int game_run(EngineState **_s) { return 0; } -void quit_vm() { - script_abort_flag = 1; // Terminate VM +void quit_vm(EngineState *s) { + s->script_abort_flag = 1; // Terminate VM g_debugState.seeking = kDebugSeekNothing; g_debugState.runningStep = 0; } -void shrink_execution_stack(EngineState *s, uint size) { - assert(s->_executionStack.size() >= size); - Common::List<ExecStack>::iterator iter; - iter = s->_executionStack.begin(); - for (uint i = 0; i < size; ++i) - ++iter; - s->_executionStack.erase(iter, s->_executionStack.end()); -} - -reg_t* ObjVarRef::getPointer(SegManager *segMan) const { +reg_t *ObjVarRef::getPointer(SegManager *segMan) const { Object *o = segMan->getObject(obj); - if (!o) return 0; - return &(o->_variables[varindex]); + return o ? &o->getVariableRef(varindex) : 0; } -reg_t* ExecStack::getVarPointer(SegManager *segMan) const { +reg_t *ExecStack::getVarPointer(SegManager *segMan) const { assert(type == EXEC_STACK_TYPE_VARSELECTOR); return addr.varp.getPointer(segMan); } diff --git a/engines/sci/engine/vm.h b/engines/sci/engine/vm.h index 8e40fed818..67a6bd0dc3 100644 --- a/engines/sci/engine/vm.h +++ b/engines/sci/engine/vm.h @@ -43,44 +43,9 @@ class ResourceManager; /** Number of bytes to be allocated for the stack */ #define VM_STACK_SIZE 0x1000 -/** Maximum number of calls residing on the stack */ -#define SCRIPT_MAX_EXEC_STACK 256 -/** Maximum number of entries in the class table */ -#define SCRIPT_MAX_CLASSTABLE_SIZE 256 -/** Maximum number of cloned objects on the heap */ -#define SCRIPT_MAX_CLONES 256 - - -/** Object-relative offset of the selector area inside a script */ -#define SCRIPT_SELECTOR_OFFSET 8 -8 - -/** Object-relative offset of the pointer to the underlying script's local variables */ -#define SCRIPT_LOCALVARPTR_OFFSET 2 -8 - -/** Object-relative offset of the selector counter */ -#define SCRIPT_SELECTORCTR_OFFSET 6 -8 - -/** Object-relative offset of the offset of the function area */ -#define SCRIPT_FUNCTAREAPTR_OFFSET 4 -8 - -/** Offset that has to be added to the function area pointer */ -#define SCRIPT_FUNCTAREAPTR_MAGIC 8 -8 - -/** Offset of the name pointer */ -#define SCRIPT_NAME_OFFSET (getSciVersion() < SCI_VERSION_1_1 ? 14 -8 : 16) - -/** Object-relative offset of the -info- selector */ -#define SCRIPT_INFO_OFFSET (getSciVersion() < SCI_VERSION_1_1 ? 12 -8 : 14) - -/** Flag fo the -info- selector */ -#define SCRIPT_INFO_CLONE 0x0001 - -/** Flag for the -info- selector */ -#define SCRIPT_INFO_CLASS 0x8000 - - /** Magical object identifier */ #define SCRIPT_OBJECT_MAGIC_NUMBER 0x1234 + /** Offset of this identifier */ #define SCRIPT_OBJECT_MAGIC_OFFSET (getSciVersion() < SCI_VERSION_1_1 ? -8 : 0) @@ -89,13 +54,10 @@ class ResourceManager; #define SCRIPT_SUPERCLASS_OFFSET (getSciVersion() < SCI_VERSION_1_1 ? 10 -8 : 12) -/** Magic adjustment value for lofsa and lofss */ -#define SCRIPT_LOFS_MAGIC 3 - /** Stack pointer value: Use predecessor's value */ #define CALL_SP_CARRY NULL -/** Types of selectors as returned by lookup_selector() below. */ +/** Types of selectors as returned by lookupSelector() below. */ enum SelectorType { kSelectorNone = 0, kSelectorVariable, @@ -195,7 +157,9 @@ struct SelectorCache { // Used for auto detection purposes Selector overlay; ///< Used to determine if a game is using old gfx functions or not - Selector setCursor; ///< For cursor semantics autodetection + + // SCI1.1 Mac icon bar selectors + Selector iconIndex; ///< Used to index icon bar objects #ifdef ENABLE_SCI32 Selector data; // Used by Array()/String() @@ -260,41 +224,11 @@ enum { VAR_PARAM = 3 }; -/** - * Structure for storing the current internal state of the VM. - */ -struct ScriptState { - ExecStack *xs; - int16 restAdjust; - reg_t *variables[4]; ///< global, local, temp, param, as immediate pointers - reg_t *variables_base[4]; ///< Used for referencing VM ops - SegmentId variables_seg[4]; ///< Same as above, contains segment IDs - int variables_max[4]; ///< Max. values for all variables -}; - -/** - * The current internal state of the VM. - */ -extern ScriptState scriptState; - -/** - * Set this to 1 to abort script execution immediately. Aborting will - * leave the debug exec stack intact. - * Set it to 2 to force a replay afterwards. - */ -extern int script_abort_flag; - /** Number of kernel calls in between gcs; should be < 50000 */ enum { GC_INTERVAL = 32768 }; -/** Initially GC_DELAY, can be set at runtime */ -extern int script_gc_interval; - -/** Number of steps executed */ -extern int script_step_counter; - /** * Executes function pubfunct of the specified script. @@ -376,7 +310,7 @@ int script_init_engine(EngineState *); * kSelectorMethod if the selector represents a * method */ -SelectorType lookup_selector(SegManager *segMan, reg_t obj, Selector selectorid, +SelectorType lookupSelector(SegManager *segMan, reg_t obj, Selector selectorid, ObjVarRef *varp, reg_t *fptr); /** @@ -466,13 +400,7 @@ int game_exit(EngineState *s); /** * Instructs the virtual machine to abort */ -void quit_vm(); - -/** - * Shrink execution stack to size. - * Contains an assert it is not already smaller. - */ -void shrink_execution_stack(EngineState *s, uint size); +void quit_vm(EngineState *s); /** * Read a PMachine instruction from a memory buffer and return its length. diff --git a/engines/sci/event.cpp b/engines/sci/event.cpp index 53f4675f56..c3be22b143 100644 --- a/engines/sci/event.cpp +++ b/engines/sci/event.cpp @@ -319,8 +319,6 @@ sciEvent SciEvent::get(unsigned int mask) { //sci_event_t error_event = { SCI_EVT_ERROR, 0, 0, 0 }; sciEvent event = { 0, 0, 0, 0 }; - // TODO: we need to call Cursor::refreshPosition() before each screen update to limit the mouse cursor position - // Update the screen here, since it's called very often g_system->updateScreen(); @@ -389,7 +387,6 @@ void SciEvent::sleep(uint32 msecs) { while (true) { // let backend process events and update the screen get(SCI_EVENT_PEEK); - // TODO: we need to call Cursor::refreshPosition() before each screen update to limit the mouse cursor position time = g_system->getMillis(); if (time + 10 < wakeup_time) { g_system->delayMillis(10); diff --git a/engines/sci/graphics/animate.cpp b/engines/sci/graphics/animate.cpp index 71c7b7dd7f..c201b2cfb7 100644 --- a/engines/sci/graphics/animate.cpp +++ b/engines/sci/graphics/animate.cpp @@ -92,10 +92,10 @@ bool GfxAnimate::invoke(List *list, int argc, reg_t *argv) { } } - signal = GET_SEL32V(_s->_segMan, curObject, SELECTOR(signal)); + signal = readSelectorValue(_s->_segMan, curObject, SELECTOR(signal)); if (!(signal & kSignalFrozen)) { // Call .doit method of that object - invoke_selector(_s, curObject, g_sci->getKernel()->_selectorCache.doit, kContinueOnInvalidSelector, argc, argv, 0); + invokeSelector(_s, curObject, g_sci->getKernel()->_selectorCache.doit, kContinueOnInvalidSelector, argc, argv, 0); // Lookup node again, since the nodetable it was in may have been reallocated curNode = _s->_segMan->lookupNode(curAddress); } @@ -109,7 +109,15 @@ bool GfxAnimate::invoke(List *list, int argc, reg_t *argv) { } bool sortHelper(const AnimateEntry* entry1, const AnimateEntry* entry2) { - return (entry1->y == entry2->y) ? (entry1->z < entry2->z) : (entry1->y < entry2->y); + if (entry1->y == entry2->y) { + // if both y and z are the same, use the order we were given originally + // this is needed for special cases like iceman room 35 + if (entry1->z == entry2->z) + return entry1->givenOrderNo < entry2->givenOrderNo; + else + return entry1->z < entry2->z; + } + return entry1->y < entry2->y; } void GfxAnimate::makeSortedList(List *list) { @@ -156,21 +164,22 @@ void GfxAnimate::makeSortedList(List *list) { listEntry->object = curObject; // Get data from current object - listEntry->viewId = GET_SEL32V(_s->_segMan, curObject, SELECTOR(view)); - listEntry->loopNo = GET_SEL32V(_s->_segMan, curObject, SELECTOR(loop)); - listEntry->celNo = GET_SEL32V(_s->_segMan, curObject, SELECTOR(cel)); - listEntry->paletteNo = GET_SEL32V(_s->_segMan, curObject, SELECTOR(palette)); - listEntry->x = GET_SEL32V(_s->_segMan, curObject, SELECTOR(x)); - listEntry->y = GET_SEL32V(_s->_segMan, curObject, SELECTOR(y)); - listEntry->z = GET_SEL32V(_s->_segMan, curObject, SELECTOR(z)); - listEntry->priority = GET_SEL32V(_s->_segMan, curObject, SELECTOR(priority)); - listEntry->signal = GET_SEL32V(_s->_segMan, curObject, SELECTOR(signal)); + listEntry->givenOrderNo = listNr; + listEntry->viewId = readSelectorValue(_s->_segMan, curObject, SELECTOR(view)); + listEntry->loopNo = readSelectorValue(_s->_segMan, curObject, SELECTOR(loop)); + listEntry->celNo = readSelectorValue(_s->_segMan, curObject, SELECTOR(cel)); + listEntry->paletteNo = readSelectorValue(_s->_segMan, curObject, SELECTOR(palette)); + listEntry->x = readSelectorValue(_s->_segMan, curObject, SELECTOR(x)); + listEntry->y = readSelectorValue(_s->_segMan, curObject, SELECTOR(y)); + listEntry->z = readSelectorValue(_s->_segMan, curObject, SELECTOR(z)); + listEntry->priority = readSelectorValue(_s->_segMan, curObject, SELECTOR(priority)); + listEntry->signal = readSelectorValue(_s->_segMan, curObject, SELECTOR(signal)); if (getSciVersion() >= SCI_VERSION_1_1) { // Cel scaling - listEntry->scaleSignal = GET_SEL32V(_s->_segMan, curObject, SELECTOR(scaleSignal)); + listEntry->scaleSignal = readSelectorValue(_s->_segMan, curObject, SELECTOR(scaleSignal)); if (listEntry->scaleSignal & kScaleSignalDoScaling) { - listEntry->scaleX = GET_SEL32V(_s->_segMan, curObject, SELECTOR(scaleX)); - listEntry->scaleY = GET_SEL32V(_s->_segMan, curObject, SELECTOR(scaleY)); + listEntry->scaleX = readSelectorValue(_s->_segMan, curObject, SELECTOR(scaleX)); + listEntry->scaleY = readSelectorValue(_s->_segMan, curObject, SELECTOR(scaleY)); } else { listEntry->scaleX = 128; listEntry->scaleY = 128; @@ -219,11 +228,11 @@ void GfxAnimate::fill(byte &old_picNotValid) { // adjust loop and cel, if any of those is invalid if (listEntry->loopNo >= view->getLoopCount()) { listEntry->loopNo = 0; - PUT_SEL32V(_s->_segMan, curObject, SELECTOR(loop), listEntry->loopNo); + writeSelectorValue(_s->_segMan, curObject, SELECTOR(loop), listEntry->loopNo); } if (listEntry->celNo >= view->getCelCount(listEntry->loopNo)) { listEntry->celNo = 0; - PUT_SEL32V(_s->_segMan, curObject, SELECTOR(cel), listEntry->celNo); + writeSelectorValue(_s->_segMan, curObject, SELECTOR(cel), listEntry->celNo); } // Create rect according to coordinates and given cel @@ -232,17 +241,17 @@ void GfxAnimate::fill(byte &old_picNotValid) { } else { view->getCelRect(listEntry->loopNo, listEntry->celNo, listEntry->x, listEntry->y, listEntry->z, &listEntry->celRect); } - PUT_SEL32V(_s->_segMan, curObject, SELECTOR(nsLeft), listEntry->celRect.left); - PUT_SEL32V(_s->_segMan, curObject, SELECTOR(nsTop), listEntry->celRect.top); - PUT_SEL32V(_s->_segMan, curObject, SELECTOR(nsRight), listEntry->celRect.right); - PUT_SEL32V(_s->_segMan, curObject, SELECTOR(nsBottom), listEntry->celRect.bottom); + writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsLeft), listEntry->celRect.left); + writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsTop), listEntry->celRect.top); + writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsRight), listEntry->celRect.right); + writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsBottom), listEntry->celRect.bottom); signal = listEntry->signal; // Calculate current priority according to y-coordinate if (!(signal & kSignalFixedPriority)) { listEntry->priority = _ports->kernelCoordinateToPriority(listEntry->y); - PUT_SEL32V(_s->_segMan, curObject, SELECTOR(priority), listEntry->priority); + writeSelectorValue(_s->_segMan, curObject, SELECTOR(priority), listEntry->priority); } if (signal & kSignalNoUpdate) { @@ -282,14 +291,14 @@ void GfxAnimate::update() { if (signal & kSignalNoUpdate) { if (!(signal & kSignalRemoveView)) { - bitsHandle = GET_SEL32(_s->_segMan, curObject, SELECTOR(underBits)); + bitsHandle = readSelector(_s->_segMan, curObject, SELECTOR(underBits)); if (_screen->_picNotValid != 1) { _paint16->bitsRestore(bitsHandle); listEntry->showBitsFlag = true; } else { _paint16->bitsFree(bitsHandle); } - PUT_SEL32V(_s->_segMan, curObject, SELECTOR(underBits), 0); + writeSelectorValue(_s->_segMan, curObject, SELECTOR(underBits), 0); } signal &= 0xFFFF ^ kSignalForceUpdate; signal &= signal & kSignalViewUpdated ? 0xFFFF ^ (kSignalViewUpdated | kSignalNoUpdate) : 0xFFFF; @@ -339,7 +348,7 @@ void GfxAnimate::update() { bitsHandle = _paint16->bitsSave(listEntry->celRect, GFX_SCREEN_MASK_VISUAL|GFX_SCREEN_MASK_PRIORITY); else bitsHandle = _paint16->bitsSave(listEntry->celRect, GFX_SCREEN_MASK_ALL); - PUT_SEL32(_s->_segMan, curObject, SELECTOR(underBits), bitsHandle); + writeSelector(_s->_segMan, curObject, SELECTOR(underBits), bitsHandle); } listEntry->signal = signal; } @@ -387,7 +396,7 @@ void GfxAnimate::drawCels() { if (!(signal & (kSignalNoUpdate | kSignalHidden | kSignalAlwaysUpdate))) { // Save background bitsHandle = _paint16->bitsSave(listEntry->celRect, GFX_SCREEN_MASK_ALL); - PUT_SEL32(_s->_segMan, curObject, SELECTOR(underBits), bitsHandle); + writeSelector(_s->_segMan, curObject, SELECTOR(underBits), bitsHandle); // draw corresponding cel _paint16->drawCel(listEntry->viewId, listEntry->loopNo, listEntry->celNo, listEntry->celRect, listEntry->priority, listEntry->paletteNo, listEntry->scaleX, listEntry->scaleY); @@ -423,10 +432,10 @@ void GfxAnimate::updateScreen(byte oldPicNotValid) { if (listEntry->showBitsFlag || !(signal & (kSignalRemoveView | kSignalNoUpdate) || (!(signal & kSignalRemoveView) && (signal & kSignalNoUpdate) && oldPicNotValid))) { - lsRect.left = GET_SEL32V(_s->_segMan, curObject, SELECTOR(lsLeft)); - lsRect.top = GET_SEL32V(_s->_segMan, curObject, SELECTOR(lsTop)); - lsRect.right = GET_SEL32V(_s->_segMan, curObject, SELECTOR(lsRight)); - lsRect.bottom = GET_SEL32V(_s->_segMan, curObject, SELECTOR(lsBottom)); + lsRect.left = readSelectorValue(_s->_segMan, curObject, SELECTOR(lsLeft)); + lsRect.top = readSelectorValue(_s->_segMan, curObject, SELECTOR(lsTop)); + lsRect.right = readSelectorValue(_s->_segMan, curObject, SELECTOR(lsRight)); + lsRect.bottom = readSelectorValue(_s->_segMan, curObject, SELECTOR(lsBottom)); workerRect = lsRect; workerRect.clip(listEntry->celRect); @@ -438,10 +447,10 @@ void GfxAnimate::updateScreen(byte oldPicNotValid) { _paint16->bitsShow(lsRect); workerRect = listEntry->celRect; } - PUT_SEL32V(_s->_segMan, curObject, SELECTOR(lsLeft), workerRect.left); - PUT_SEL32V(_s->_segMan, curObject, SELECTOR(lsTop), workerRect.top); - PUT_SEL32V(_s->_segMan, curObject, SELECTOR(lsRight), workerRect.right); - PUT_SEL32V(_s->_segMan, curObject, SELECTOR(lsBottom), workerRect.bottom); + writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsLeft), workerRect.left); + writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsTop), workerRect.top); + writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsRight), workerRect.right); + writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsBottom), workerRect.bottom); _paint16->bitsShow(workerRect); if (signal & kSignalHidden) { @@ -472,7 +481,7 @@ void GfxAnimate::restoreAndDelete(int argc, reg_t *argv) { signal = listEntry->signal; // Finally update signal - PUT_SEL32V(_s->_segMan, curObject, SELECTOR(signal), signal); + writeSelectorValue(_s->_segMan, curObject, SELECTOR(signal), signal); listIterator++; } @@ -481,16 +490,16 @@ void GfxAnimate::restoreAndDelete(int argc, reg_t *argv) { listEntry = *listIterator; curObject = listEntry->object; // We read out signal here again, this is not by accident but to ensure that we got an up-to-date signal - signal = GET_SEL32V(_s->_segMan, curObject, SELECTOR(signal)); + signal = readSelectorValue(_s->_segMan, curObject, SELECTOR(signal)); if ((signal & (kSignalNoUpdate | kSignalRemoveView)) == 0) { - _paint16->bitsRestore(GET_SEL32(_s->_segMan, curObject, SELECTOR(underBits))); - PUT_SEL32V(_s->_segMan, curObject, SELECTOR(underBits), 0); + _paint16->bitsRestore(readSelector(_s->_segMan, curObject, SELECTOR(underBits))); + writeSelectorValue(_s->_segMan, curObject, SELECTOR(underBits), 0); } if (signal & kSignalDisposeMe) { // Call .delete_ method of that object - invoke_selector(_s, curObject, g_sci->getKernel()->_selectorCache.delete_, kContinueOnInvalidSelector, argc, argv, 0); + invokeSelector(_s, curObject, g_sci->getKernel()->_selectorCache.delete_, kContinueOnInvalidSelector, argc, argv, 0); } listIterator--; } diff --git a/engines/sci/graphics/animate.h b/engines/sci/graphics/animate.h index 2cc59b1767..706b7182cf 100644 --- a/engines/sci/graphics/animate.h +++ b/engines/sci/graphics/animate.h @@ -40,7 +40,7 @@ enum ViewSignals { kSignalAlwaysUpdate = 0x0020, kSignalForceUpdate = 0x0040, kSignalRemoveView = 0x0080, - kSignalFrozen = 0x0100, + kSignalFrozen = 0x0100, // I got frozen today!! kSignalExtraActor = 0x0200, // unused by us, defines all actors that may be included into the background if speed is too slow kSignalHitObstacle = 0x0400, // used in the actor movement code by kDoBresen() kSignalDoesntTurn = 0x0800, // used by _k_dirloop() to determine if an actor can turn or not @@ -57,6 +57,7 @@ enum ViewScaleSignals { }; struct AnimateEntry { + int16 givenOrderNo; reg_t object; GuiResourceId viewId; int16 loopNo; diff --git a/engines/sci/graphics/compare.cpp b/engines/sci/graphics/compare.cpp index 36dd2d4aed..3102edc2fa 100644 --- a/engines/sci/graphics/compare.cpp +++ b/engines/sci/graphics/compare.cpp @@ -79,12 +79,12 @@ bool GfxCompare::canBeHereCheckRectList(reg_t checkObject, const Common::Rect &c while (curNode) { curObject = curNode->value; if (curObject != checkObject) { - signal = GET_SEL32V(_segMan, curObject, SELECTOR(signal)); + signal = readSelectorValue(_segMan, curObject, SELECTOR(signal)); if ((signal & (kSignalIgnoreActor | kSignalRemoveView | kSignalNoUpdate)) == 0) { - curRect.left = GET_SEL32V(_segMan, curObject, SELECTOR(brLeft)); - curRect.top = GET_SEL32V(_segMan, curObject, SELECTOR(brTop)); - curRect.right = GET_SEL32V(_segMan, curObject, SELECTOR(brRight)); - curRect.bottom = GET_SEL32V(_segMan, curObject, SELECTOR(brBottom)); + curRect.left = readSelectorValue(_segMan, curObject, SELECTOR(brLeft)); + curRect.top = readSelectorValue(_segMan, curObject, SELECTOR(brTop)); + curRect.right = readSelectorValue(_segMan, curObject, SELECTOR(brRight)); + curRect.bottom = readSelectorValue(_segMan, curObject, SELECTOR(brBottom)); // Check if curRect is within checkRect // TODO: This check is slightly odd, because it means that a rect is not contained // in itself. It may very well be that the original SCI engine did it just @@ -115,29 +115,29 @@ uint16 GfxCompare::kernelOnControl(byte screenMask, const Common::Rect &rect) { void GfxCompare::kernelSetNowSeen(reg_t objectReference) { GfxView *view = NULL; Common::Rect celRect(0, 0); - GuiResourceId viewId = (GuiResourceId)GET_SEL32V(_segMan, objectReference, SELECTOR(view)); + GuiResourceId viewId = (GuiResourceId)readSelectorValue(_segMan, objectReference, SELECTOR(view)); // HACK: Ignore invalid views for now (perhaps unimplemented text views?) if (viewId == 0xFFFF) // invalid view return; - int16 loopNo = GET_SEL32V(_segMan, objectReference, SELECTOR(loop)); - int16 celNo = GET_SEL32V(_segMan, objectReference, SELECTOR(cel)); - int16 x = (int16)GET_SEL32V(_segMan, objectReference, SELECTOR(x)); - int16 y = (int16)GET_SEL32V(_segMan, objectReference, SELECTOR(y)); + int16 loopNo = readSelectorValue(_segMan, objectReference, SELECTOR(loop)); + int16 celNo = readSelectorValue(_segMan, objectReference, SELECTOR(cel)); + int16 x = (int16)readSelectorValue(_segMan, objectReference, SELECTOR(x)); + int16 y = (int16)readSelectorValue(_segMan, objectReference, SELECTOR(y)); int16 z = 0; if (_kernel->_selectorCache.z > -1) - z = (int16)GET_SEL32V(_segMan, objectReference, SELECTOR(z)); + z = (int16)readSelectorValue(_segMan, objectReference, SELECTOR(z)); // now get cel rectangle view = _cache->getView(viewId); view->getCelRect(loopNo, celNo, x, y, z, &celRect); - if (lookup_selector(_segMan, objectReference, _kernel->_selectorCache.nsTop, NULL, NULL) == kSelectorVariable) { - PUT_SEL32V(_segMan, objectReference, SELECTOR(nsLeft), celRect.left); - PUT_SEL32V(_segMan, objectReference, SELECTOR(nsRight), celRect.right); - PUT_SEL32V(_segMan, objectReference, SELECTOR(nsTop), celRect.top); - PUT_SEL32V(_segMan, objectReference, SELECTOR(nsBottom), celRect.bottom); + if (lookupSelector(_segMan, objectReference, _kernel->_selectorCache.nsTop, NULL, NULL) == kSelectorVariable) { + writeSelectorValue(_segMan, objectReference, SELECTOR(nsLeft), celRect.left); + writeSelectorValue(_segMan, objectReference, SELECTOR(nsRight), celRect.right); + writeSelectorValue(_segMan, objectReference, SELECTOR(nsTop), celRect.top); + writeSelectorValue(_segMan, objectReference, SELECTOR(nsBottom), celRect.bottom); } } @@ -147,10 +147,10 @@ bool GfxCompare::kernelCanBeHere(reg_t curObject, reg_t listReference) { uint16 signal, controlMask; bool result; - checkRect.left = GET_SEL32V(_segMan, curObject, SELECTOR(brLeft)); - checkRect.top = GET_SEL32V(_segMan, curObject, SELECTOR(brTop)); - checkRect.right = GET_SEL32V(_segMan, curObject, SELECTOR(brRight)); - checkRect.bottom = GET_SEL32V(_segMan, curObject, SELECTOR(brBottom)); + checkRect.left = readSelectorValue(_segMan, curObject, SELECTOR(brLeft)); + checkRect.top = readSelectorValue(_segMan, curObject, SELECTOR(brTop)); + checkRect.right = readSelectorValue(_segMan, curObject, SELECTOR(brRight)); + checkRect.bottom = readSelectorValue(_segMan, curObject, SELECTOR(brBottom)); if (!checkRect.isValidRect()) { // can occur in Iceman - HACK? TODO: is this really occuring in sierra sci? check this warning("kCan(t)BeHere - invalid rect %d, %d -> %d, %d", checkRect.left, checkRect.top, checkRect.right, checkRect.bottom); @@ -159,8 +159,8 @@ bool GfxCompare::kernelCanBeHere(reg_t curObject, reg_t listReference) { adjustedRect = _coordAdjuster->onControl(checkRect); - signal = GET_SEL32V(_segMan, curObject, SELECTOR(signal)); - controlMask = GET_SEL32V(_segMan, curObject, SELECTOR(illegalBits)); + signal = readSelectorValue(_segMan, curObject, SELECTOR(signal)); + controlMask = readSelectorValue(_segMan, curObject, SELECTOR(illegalBits)); result = (isOnControl(GFX_SCREEN_MASK_CONTROL, adjustedRect) & controlMask) ? false : true; if ((result) && (signal & (kSignalIgnoreActor | kSignalRemoveView)) == 0) { List *list = _segMan->lookupList(listReference); @@ -183,14 +183,14 @@ bool GfxCompare::kernelIsItSkip(GuiResourceId viewId, int16 loopNo, int16 celNo, } void GfxCompare::kernelBaseSetter(reg_t object) { - if (lookup_selector(_segMan, object, _kernel->_selectorCache.brLeft, NULL, NULL) == kSelectorVariable) { - int16 x = GET_SEL32V(_segMan, object, SELECTOR(x)); - int16 y = GET_SEL32V(_segMan, object, SELECTOR(y)); - int16 z = (_kernel->_selectorCache.z > -1) ? GET_SEL32V(_segMan, object, SELECTOR(z)) : 0; - int16 yStep = GET_SEL32V(_segMan, object, SELECTOR(yStep)); - GuiResourceId viewId = GET_SEL32V(_segMan, object, SELECTOR(view)); - int16 loopNo = GET_SEL32V(_segMan, object, SELECTOR(loop)); - int16 celNo = GET_SEL32V(_segMan, object, SELECTOR(cel)); + if (lookupSelector(_segMan, object, _kernel->_selectorCache.brLeft, NULL, NULL) == kSelectorVariable) { + int16 x = readSelectorValue(_segMan, object, SELECTOR(x)); + int16 y = readSelectorValue(_segMan, object, SELECTOR(y)); + int16 z = (_kernel->_selectorCache.z > -1) ? readSelectorValue(_segMan, object, SELECTOR(z)) : 0; + int16 yStep = readSelectorValue(_segMan, object, SELECTOR(yStep)); + GuiResourceId viewId = readSelectorValue(_segMan, object, SELECTOR(view)); + int16 loopNo = readSelectorValue(_segMan, object, SELECTOR(loop)); + int16 celNo = readSelectorValue(_segMan, object, SELECTOR(cel)); // HACK: Ignore invalid views for now (perhaps unimplemented text views?) if (viewId == 0xFFFF) // invalid view @@ -203,10 +203,10 @@ void GfxCompare::kernelBaseSetter(reg_t object) { celRect.bottom = y + 1; celRect.top = celRect.bottom - yStep; - PUT_SEL32V(_segMan, object, SELECTOR(brLeft), celRect.left); - PUT_SEL32V(_segMan, object, SELECTOR(brRight), celRect.right); - PUT_SEL32V(_segMan, object, SELECTOR(brTop), celRect.top); - PUT_SEL32V(_segMan, object, SELECTOR(brBottom), celRect.bottom); + writeSelectorValue(_segMan, object, SELECTOR(brLeft), celRect.left); + writeSelectorValue(_segMan, object, SELECTOR(brRight), celRect.right); + writeSelectorValue(_segMan, object, SELECTOR(brTop), celRect.top); + writeSelectorValue(_segMan, object, SELECTOR(brBottom), celRect.bottom); } } diff --git a/engines/sci/graphics/controls.cpp b/engines/sci/graphics/controls.cpp index f744d6e7f1..26af9741c2 100644 --- a/engines/sci/graphics/controls.cpp +++ b/engines/sci/graphics/controls.cpp @@ -143,9 +143,9 @@ void GfxControls::texteditSetBlinkTime() { } void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) { - uint16 cursorPos = GET_SEL32V(_segMan, controlObject, SELECTOR(cursor)); - uint16 maxChars = GET_SEL32V(_segMan, controlObject, SELECTOR(max)); - reg_t textReference = GET_SEL32(_segMan, controlObject, SELECTOR(text)); + uint16 cursorPos = readSelectorValue(_segMan, controlObject, SELECTOR(cursor)); + uint16 maxChars = readSelectorValue(_segMan, controlObject, SELECTOR(max)); + reg_t textReference = readSelector(_segMan, controlObject, SELECTOR(text)); Common::String text; uint16 textSize, eventType, eventKey = 0; bool textChanged = false; @@ -158,14 +158,14 @@ void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) { if (!eventObject.isNull()) { textSize = text.size(); - eventType = GET_SEL32V(_segMan, eventObject, SELECTOR(type)); + eventType = readSelectorValue(_segMan, eventObject, SELECTOR(type)); switch (eventType) { case SCI_EVENT_MOUSE_PRESS: // TODO: Implement mouse support for cursor change break; case SCI_EVENT_KEYBOARD: - eventKey = GET_SEL32V(_segMan, eventObject, SELECTOR(message)); + eventKey = readSelectorValue(_segMan, eventObject, SELECTOR(message)); switch (eventKey) { case SCI_KEY_BACKSPACE: if (cursorPos > 0) { @@ -207,9 +207,9 @@ void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) { if (textChanged) { GuiResourceId oldFontId = _text16->GetFontId(); - GuiResourceId fontId = GET_SEL32V(_segMan, controlObject, SELECTOR(font)); - rect = Common::Rect(GET_SEL32V(_segMan, controlObject, SELECTOR(nsLeft)), GET_SEL32V(_segMan, controlObject, SELECTOR(nsTop)), - GET_SEL32V(_segMan, controlObject, SELECTOR(nsRight)), GET_SEL32V(_segMan, controlObject, SELECTOR(nsBottom))); + GuiResourceId fontId = readSelectorValue(_segMan, controlObject, SELECTOR(font)); + rect = Common::Rect(readSelectorValue(_segMan, controlObject, SELECTOR(nsLeft)), readSelectorValue(_segMan, controlObject, SELECTOR(nsTop)), + readSelectorValue(_segMan, controlObject, SELECTOR(nsRight)), readSelectorValue(_segMan, controlObject, SELECTOR(nsBottom))); _text16->SetFont(fontId); if (textAddChar) { // We check, if we are really able to add the new char @@ -241,7 +241,7 @@ void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) { } } - PUT_SEL32V(_segMan, controlObject, SELECTOR(cursor), cursorPos); + writeSelectorValue(_segMan, controlObject, SELECTOR(cursor), cursorPos); } int GfxControls::getPicNotValid() { diff --git a/engines/sci/graphics/coordadjuster.cpp b/engines/sci/graphics/coordadjuster.cpp index 40ef655be7..422df52f27 100644 --- a/engines/sci/graphics/coordadjuster.cpp +++ b/engines/sci/graphics/coordadjuster.cpp @@ -93,18 +93,18 @@ GfxCoordAdjuster32::~GfxCoordAdjuster32() { } void GfxCoordAdjuster32::kernelGlobalToLocal(int16 &x, int16 &y, reg_t planeObject) { - //int16 resY = GET_SEL32V(_s->_segMan, planeObj, SELECTOR(resY)); - //int16 resX = GET_SEL32V(_s->_segMan, planeObj, SELECTOR(resX)); + //int16 resY = readSelectorValue(_s->_segMan, planeObj, SELECTOR(resY)); + //int16 resX = readSelectorValue(_s->_segMan, planeObj, SELECTOR(resX)); //*x = ( *x * _screen->getWidth()) / resX; //*y = ( *y * _screen->getHeight()) / resY; - x -= GET_SEL32V(_segMan, planeObject, SELECTOR(left)); - y -= GET_SEL32V(_segMan, planeObject, SELECTOR(top)); + x -= readSelectorValue(_segMan, planeObject, SELECTOR(left)); + y -= readSelectorValue(_segMan, planeObject, SELECTOR(top)); } void GfxCoordAdjuster32::kernelLocalToGlobal(int16 &x, int16 &y, reg_t planeObject) { - //int16 resY = GET_SEL32V(_s->_segMan, planeObj, SELECTOR(resY)); - //int16 resX = GET_SEL32V(_s->_segMan, planeObj, SELECTOR(resX)); - x += GET_SEL32V(_segMan, planeObject, SELECTOR(left)); - y += GET_SEL32V(_segMan, planeObject, SELECTOR(top)); + //int16 resY = readSelectorValue(_s->_segMan, planeObj, SELECTOR(resY)); + //int16 resX = readSelectorValue(_s->_segMan, planeObj, SELECTOR(resX)); + x += readSelectorValue(_segMan, planeObject, SELECTOR(left)); + y += readSelectorValue(_segMan, planeObject, SELECTOR(top)); //*x = ( *x * resX) / _screen->getWidth(); //*y = ( *y * resY) / _screen->getHeight(); } diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp index 78253bd913..3cc5ca5447 100644 --- a/engines/sci/graphics/frameout.cpp +++ b/engines/sci/graphics/frameout.cpp @@ -54,7 +54,7 @@ GfxFrameout::~GfxFrameout() { void GfxFrameout::kernelAddPlane(reg_t object) { _planes.push_back(object); - int16 planePri = GET_SEL32V(_segMan, object, SELECTOR(priority)) & 0xFFFF; + int16 planePri = readSelectorValue(_segMan, object, SELECTOR(priority)) & 0xFFFF; if (planePri > _highPlanePri) _highPlanePri = planePri; } @@ -74,7 +74,7 @@ void GfxFrameout::kernelDeletePlane(reg_t object) { _highPlanePri = 0; for (uint32 planeNr = 0; planeNr < _planes.size(); planeNr++) { - int16 planePri = GET_SEL32V(_segMan, _planes[planeNr], SELECTOR(priority)) & 0xFFFF; + int16 planePri = readSelectorValue(_segMan, _planes[planeNr], SELECTOR(priority)) & 0xFFFF; if (planePri > _highPlanePri) _highPlanePri = planePri; } @@ -126,29 +126,29 @@ void GfxFrameout::kernelFrameout() { for (uint32 planeNr = 0; planeNr < _planes.size(); planeNr++) { planeObject = _planes[planeNr]; - planePriority = GET_SEL32V(_segMan, planeObject, SELECTOR(priority)); + planePriority = readSelectorValue(_segMan, planeObject, SELECTOR(priority)); if (planePriority == -1) // Plane currently not meant to be shown continue; - planeRect.top = GET_SEL32V(_segMan, planeObject, SELECTOR(top)); - planeRect.left = GET_SEL32V(_segMan, planeObject, SELECTOR(left)); - planeRect.bottom = GET_SEL32V(_segMan, planeObject, SELECTOR(bottom)); - planeRect.right = GET_SEL32V(_segMan, planeObject, SELECTOR(right)); - planeResY = GET_SEL32V(_segMan, planeObject, SELECTOR(resY)); - planeResX = GET_SEL32V(_segMan, planeObject, SELECTOR(resX)); + planeRect.top = readSelectorValue(_segMan, planeObject, SELECTOR(top)); + planeRect.left = readSelectorValue(_segMan, planeObject, SELECTOR(left)); + planeRect.bottom = readSelectorValue(_segMan, planeObject, SELECTOR(bottom)); + planeRect.right = readSelectorValue(_segMan, planeObject, SELECTOR(right)); + planeResY = readSelectorValue(_segMan, planeObject, SELECTOR(resY)); + planeResX = readSelectorValue(_segMan, planeObject, SELECTOR(resX)); planeRect.top = (planeRect.top * _screen->getHeight()) / planeResY; planeRect.left = (planeRect.left * _screen->getWidth()) / planeResX; planeRect.bottom = (planeRect.bottom * _screen->getHeight()) / planeResY; planeRect.right = (planeRect.right * _screen->getWidth()) / planeResX; - planeBack = GET_SEL32V(_segMan, planeObject, SELECTOR(back)); + planeBack = readSelectorValue(_segMan, planeObject, SELECTOR(back)); if (planeBack) { _paint32->fillRect(planeRect, planeBack); } - planePictureNr = GET_SEL32V(_segMan, planeObject, SELECTOR(picture)); + planePictureNr = readSelectorValue(_segMan, planeObject, SELECTOR(picture)); if ((planePictureNr != 0xFFFF) && (planePictureNr != 0xFFFE)) { planePicture = new GfxPicture(_resMan, _coordAdjuster, 0, _screen, _palette, planePictureNr, false); planePictureCels = planePicture->getSci32celCount(); @@ -161,19 +161,19 @@ void GfxFrameout::kernelFrameout() { itemEntry = itemData; for (uint32 itemNr = 0; itemNr < _screenItems.size(); itemNr++) { itemObject = _screenItems[itemNr]; - itemPlane = GET_SEL32(_segMan, itemObject, SELECTOR(plane)); + itemPlane = readSelector(_segMan, itemObject, SELECTOR(plane)); if (planeObject == itemPlane) { // Found an item on current plane - itemEntry->viewId = GET_SEL32V(_segMan, itemObject, SELECTOR(view)); - itemEntry->loopNo = GET_SEL32V(_segMan, itemObject, SELECTOR(loop)); - itemEntry->celNo = GET_SEL32V(_segMan, itemObject, SELECTOR(cel)); - itemEntry->x = GET_SEL32V(_segMan, itemObject, SELECTOR(x)); - itemEntry->y = GET_SEL32V(_segMan, itemObject, SELECTOR(y)); - itemEntry->z = GET_SEL32V(_segMan, itemObject, SELECTOR(z)); - itemEntry->priority = GET_SEL32V(_segMan, itemObject, SELECTOR(priority)); - itemEntry->signal = GET_SEL32V(_segMan, itemObject, SELECTOR(signal)); - itemEntry->scaleX = GET_SEL32V(_segMan, itemObject, SELECTOR(scaleX)); - itemEntry->scaleY = GET_SEL32V(_segMan, itemObject, SELECTOR(scaleY)); + itemEntry->viewId = readSelectorValue(_segMan, itemObject, SELECTOR(view)); + itemEntry->loopNo = readSelectorValue(_segMan, itemObject, SELECTOR(loop)); + itemEntry->celNo = readSelectorValue(_segMan, itemObject, SELECTOR(cel)); + itemEntry->x = readSelectorValue(_segMan, itemObject, SELECTOR(x)); + itemEntry->y = readSelectorValue(_segMan, itemObject, SELECTOR(y)); + itemEntry->z = readSelectorValue(_segMan, itemObject, SELECTOR(z)); + itemEntry->priority = readSelectorValue(_segMan, itemObject, SELECTOR(priority)); + itemEntry->signal = readSelectorValue(_segMan, itemObject, SELECTOR(signal)); + itemEntry->scaleX = readSelectorValue(_segMan, itemObject, SELECTOR(scaleX)); + itemEntry->scaleY = readSelectorValue(_segMan, itemObject, SELECTOR(scaleY)); itemEntry->object = itemObject; itemEntry->y = ((itemEntry->y * _screen->getHeight()) / planeResY); @@ -240,12 +240,12 @@ void GfxFrameout::kernelFrameout() { // This doesn't work for SCI2.1 games... if (getSciVersion() == SCI_VERSION_2) { Kernel *kernel = g_sci->getKernel(); - if (lookup_selector(_segMan, itemEntry->object, kernel->_selectorCache.text, NULL, NULL) == kSelectorVariable) { - Common::String text = _segMan->getString(GET_SEL32(_segMan, itemEntry->object, SELECTOR(text))); - int16 fontRes = GET_SEL32V(_segMan, itemEntry->object, SELECTOR(font)); + if (lookupSelector(_segMan, itemEntry->object, kernel->_selectorCache.text, NULL, NULL) == kSelectorVariable) { + Common::String text = _segMan->getString(readSelector(_segMan, itemEntry->object, SELECTOR(text))); + int16 fontRes = readSelectorValue(_segMan, itemEntry->object, SELECTOR(font)); GfxFont *font = new GfxFontFromResource(_resMan, _screen, fontRes); - bool dimmed = GET_SEL32V(_segMan, itemEntry->object, SELECTOR(dimmed)); - uint16 foreColor = GET_SEL32V(_segMan, itemEntry->object, SELECTOR(fore)); + bool dimmed = readSelectorValue(_segMan, itemEntry->object, SELECTOR(dimmed)); + uint16 foreColor = readSelectorValue(_segMan, itemEntry->object, SELECTOR(fore)); uint16 curX = itemEntry->x; uint16 curY = itemEntry->y; for (uint32 i = 0; i < text.size(); i++) { diff --git a/engines/sci/graphics/gui.cpp b/engines/sci/graphics/gui.cpp index 46f7fcd689..e427edd732 100644 --- a/engines/sci/graphics/gui.cpp +++ b/engines/sci/graphics/gui.cpp @@ -92,7 +92,7 @@ void SciGui::resetEngineState(EngineState *s) { } void SciGui::init(bool usesOldGfxFunctions) { - _ports->init(usesOldGfxFunctions, this, _paint16, _text16, _s->_gameId); + _ports->init(usesOldGfxFunctions, this, _paint16, _text16); _paint16->init(_animate, _text16); } @@ -136,9 +136,4 @@ void SciGui::portraitShow(Common::String resourceName, Common::Point position, u void SciGui::portraitUnload(uint16 portraitId) { } -bool SciGui::debugEGAdrawingVisualize(bool state) { - _paint16->setEGAdrawingVisualize(state); - return false; -} - } // End of namespace Sci diff --git a/engines/sci/graphics/gui.h b/engines/sci/graphics/gui.h index 732e195026..7663036117 100644 --- a/engines/sci/graphics/gui.h +++ b/engines/sci/graphics/gui.h @@ -61,8 +61,6 @@ public: virtual void portraitShow(Common::String resourceName, Common::Point position, uint16 resourceNum, uint16 noun, uint16 verb, uint16 cond, uint16 seq); virtual void portraitUnload(uint16 portraitId); - virtual bool debugEGAdrawingVisualize(bool state); - // FIXME: Don't store EngineState virtual void resetEngineState(EngineState *s); diff --git a/engines/sci/graphics/maciconbar.cpp b/engines/sci/graphics/maciconbar.cpp new file mode 100644 index 0000000000..a06e98ccbf --- /dev/null +++ b/engines/sci/graphics/maciconbar.cpp @@ -0,0 +1,91 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#include "sci/sci.h" +#include "sci/engine/selector.h" +#include "sci/engine/state.h" +#include "sci/graphics/maciconbar.h" +#include "sci/graphics/palette.h" + +#include "common/stream.h" +#include "common/system.h" +#include "graphics/pict.h" +#include "graphics/surface.h" + +namespace Sci { + +void GfxMacIconBar::addIcon(reg_t obj) { + _iconBarObjects.push_back(obj); +} + +void GfxMacIconBar::drawIcons() { + // Draw the icons to the bottom of the screen + + byte *pal = new byte[256 * 4]; + Graphics::PictDecoder *pict = new Graphics::PictDecoder(Graphics::PixelFormat::createFormatCLUT8()); + uint32 lastX = 0; + + for (uint32 i = 0; i < _iconBarObjects.size(); i++) { + uint32 iconIndex = readSelectorValue(g_sci->getEngineState()->_segMan, _iconBarObjects[i], SELECTOR(iconIndex)); + Resource *res = g_sci->getResMan()->findResource(ResourceId(kResourceTypeMacIconBarPictN, iconIndex + 1), false); + if (!res) + continue; + + Common::MemoryReadStream *stream = new Common::MemoryReadStream(res->data, res->size); + Graphics::Surface *surf = pict->decodeImage(stream, pal); + remapColors(surf, pal); + + g_system->copyRectToScreen((byte *)surf->pixels, surf->pitch, lastX, 200, MIN<uint32>(surf->w, 320 - lastX), surf->h); + + lastX += surf->w; + surf->free(); + delete surf; + delete stream; + } + + delete pict; + delete[] pal; +} + +void GfxMacIconBar::remapColors(Graphics::Surface *surf, byte *palette) { + byte *pixels = (byte *)surf->pixels; + + // Remap to the screen palette + for (uint16 i = 0; i < surf->w * surf->h; i++) { + byte color = *pixels; + + byte r = palette[color * 4]; + byte g = palette[color * 4 + 1]; + byte b = palette[color * 4 + 2]; + + // For black, make sure the index is 0 + if (r == 0 && g == 0 && b == 0) + *pixels++ = 0; + else + *pixels++ = g_sci->_gfxPalette->kernelFindColor(r, g, b); + } +} + +} // End of namespace Sci diff --git a/engines/mohawk/video/rpza.h b/engines/sci/graphics/maciconbar.h index c6d0ada6f5..71e65fcb40 100644 --- a/engines/mohawk/video/rpza.h +++ b/engines/sci/graphics/maciconbar.h @@ -23,27 +23,33 @@ * */ -#ifndef MOHAWK_RPZA_H -#define MOHAWK_RPZA_H +#ifndef SCI_GRAPHICS_MACICONBAR_H +#define SCI_GRAPHICS_MACICONBAR_H -#include "graphics/pixelformat.h" -#include "graphics/video/codecs/codec.h" +#include "common/array.h" -namespace Mohawk { +#include "sci/engine/vm.h" -class RPZADecoder : public Graphics::Codec { +namespace Graphics { + struct Surface; +} + +namespace Sci { + +class GfxMacIconBar { public: - RPZADecoder(uint16 width, uint16 height); - ~RPZADecoder() { delete _surface; } + GfxMacIconBar() {} + ~GfxMacIconBar() {} - Graphics::Surface *decodeImage(Common::SeekableReadStream *stream); - Graphics::PixelFormat getPixelFormat() const { return _pixelFormat; } + void addIcon(reg_t obj); + void drawIcons(); private: - Graphics::Surface *_surface; - Graphics::PixelFormat _pixelFormat; + Common::Array<reg_t> _iconBarObjects; + + void remapColors(Graphics::Surface *surf, byte *palette); }; -} // End of namespace Mohawk +} // End of namespace Sci #endif diff --git a/engines/sci/graphics/menu.cpp b/engines/sci/graphics/menu.cpp index 5e3b419fe3..880e1aba12 100644 --- a/engines/sci/graphics/menu.cpp +++ b/engines/sci/graphics/menu.cpp @@ -378,7 +378,7 @@ void GfxMenu::calculateMenuAndItemWidth() { } reg_t GfxMenu::kernelSelect(reg_t eventObject) { - int16 eventType = GET_SEL32V(_segMan, eventObject, SELECTOR(type)); + int16 eventType = readSelectorValue(_segMan, eventObject, SELECTOR(type)); int16 keyPress, keyModifier; Common::Point mousePosition; GuiMenuItemList::iterator itemIterator = _itemList.begin(); @@ -390,8 +390,8 @@ reg_t GfxMenu::kernelSelect(reg_t eventObject) { switch (eventType) { case SCI_EVENT_KEYBOARD: - keyPress = GET_SEL32V(_segMan, eventObject, SELECTOR(message)); - keyModifier = GET_SEL32V(_segMan, eventObject, SELECTOR(modifiers)); + keyPress = readSelectorValue(_segMan, eventObject, SELECTOR(message)); + keyModifier = readSelectorValue(_segMan, eventObject, SELECTOR(modifiers)); // If tab got pressed, handle it here as if it was Ctrl-I - at least sci0 also did it that way if (keyPress == SCI_KEY_TAB) { keyModifier = SCI_KEYMOD_CTRL; @@ -465,7 +465,7 @@ reg_t GfxMenu::kernelSelect(reg_t eventObject) { _ports->setPort(_oldPort); if ((itemEntry) || (forceClaimed)) - PUT_SEL32(_segMan, eventObject, SELECTOR(claimed), make_reg(0, 1)); + writeSelector(_segMan, eventObject, SELECTOR(claimed), make_reg(0, 1)); if (itemEntry) return make_reg(0, (itemEntry->menuId << 8) | (itemEntry->id)); return NULL_REG; diff --git a/engines/sci/graphics/paint16.cpp b/engines/sci/graphics/paint16.cpp index d0975f3d3d..ff4f3bec52 100644 --- a/engines/sci/graphics/paint16.cpp +++ b/engines/sci/graphics/paint16.cpp @@ -61,7 +61,7 @@ void GfxPaint16::init(GfxAnimate *animate, GfxText16 *text16) { _EGAdrawingVisualize = false; } -void GfxPaint16::setEGAdrawingVisualize(bool state) { +void GfxPaint16::debugSetEGAdrawingVisualize(bool state) { _EGAdrawingVisualize = state; } @@ -354,7 +354,8 @@ void GfxPaint16::bitsRestore(reg_t memoryHandle) { } void GfxPaint16::bitsFree(reg_t memoryHandle) { - _segMan->freeHunkEntry(memoryHandle); + if (!memoryHandle.isNull()) // happens in KQ5CD + _segMan->freeHunkEntry(memoryHandle); } void GfxPaint16::kernelDrawPicture(GuiResourceId pictureId, int16 animationNr, bool animationBlackoutFlag, bool mirroredFlag, bool addToFlag, int16 EGApaletteNo) { diff --git a/engines/sci/graphics/paint16.h b/engines/sci/graphics/paint16.h index b18c879387..65f9dd0d9c 100644 --- a/engines/sci/graphics/paint16.h +++ b/engines/sci/graphics/paint16.h @@ -50,7 +50,7 @@ public: void init(GfxAnimate *animate, GfxText16 *text16); - void setEGAdrawingVisualize(bool state); + void debugSetEGAdrawingVisualize(bool state); void drawPicture(GuiResourceId pictureId, int16 animationNr, bool mirroredFlag, bool addToFlag, GuiResourceId paletteId); void drawCelAndShow(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, byte priority, uint16 paletteNo, uint16 scaleX = 128, uint16 scaleY = 128); diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp index 96bdb42007..3c4cf7e964 100644 --- a/engines/sci/graphics/palette.cpp +++ b/engines/sci/graphics/palette.cpp @@ -30,8 +30,9 @@ #include "sci/sci.h" #include "sci/engine/state.h" -#include "sci/graphics/screen.h" +#include "sci/graphics/maciconbar.h" #include "sci/graphics/palette.h" +#include "sci/graphics/screen.h" namespace Sci { @@ -55,6 +56,7 @@ GfxPalette::GfxPalette(ResourceManager *resMan, GfxScreen *screen, bool autoSetP _sysPalette.colors[255].g = 255; _sysPalette.colors[255].b = 255; + _sysPaletteChanged = false; if (autoSetPalette) { if (_resMan->getViewType() == kViewEga) setEGA(); @@ -63,7 +65,6 @@ GfxPalette::GfxPalette(ResourceManager *resMan, GfxScreen *screen, bool autoSetP else kernelSetFromResource(999, true); } - _sysPaletteChanged = false; } GfxPalette::~GfxPalette() { @@ -311,6 +312,10 @@ void GfxPalette::setOnScreen() { if (_resMan->isAmiga32color()) return; _screen->setPalette(&_sysPalette); + + // Redraw the Mac SCI1.1 Icon bar every palette change + if (g_sci->_gfxMacIconBar) + g_sci->_gfxMacIconBar->drawIcons(); } bool GfxPalette::kernelSetFromResource(GuiResourceId resourceId, bool force) { diff --git a/engines/sci/graphics/picture.cpp b/engines/sci/graphics/picture.cpp index 74f651a88a..a59153f116 100644 --- a/engines/sci/graphics/picture.cpp +++ b/engines/sci/graphics/picture.cpp @@ -69,15 +69,20 @@ void GfxPicture::draw(int16 animationNr, bool mirroredFlag, bool addToFlag, int1 headerSize = READ_LE_UINT16(_resource->data); switch (headerSize) { case 0x26: // SCI 1.1 VGA picture + _resourceType = SCI_PICTURE_TYPE_SCI11; + if (_addToFlag) + _priority = 15; drawSci11Vga(); break; #ifdef ENABLE_SCI32 case 0x0e: // SCI32 VGA picture + _resourceType = SCI_PICTURE_TYPE_SCI32; drawSci32Vga(); break; #endif default: // VGA, EGA or Amiga vector data + _resourceType = SCI_PICTURE_TYPE_REGULAR; drawVectorData(_resource->data, _resource->size); } } @@ -108,9 +113,8 @@ void GfxPicture::drawSci11Vga() { _palette->set(&palette, true); // display Cel-data - if (has_cel) { - drawCelData(inbuffer, size, cel_headerPos, cel_RlePos, cel_LiteralPos, 0, 0, false); - } + if (has_cel) + drawCelData(inbuffer, size, cel_headerPos, cel_RlePos, cel_LiteralPos, 0, 0); // process vector data drawVectorData(inbuffer + vector_dataPos, vector_size); @@ -139,6 +143,7 @@ void GfxPicture::drawSci32Vga(int16 celNo) { // HACK _mirroredFlag = false; _addToFlag = false; + _resourceType = SCI_PICTURE_TYPE_SCI32; if ((celNo == -1) || (celNo == 0)) { // Create palette and set it @@ -155,14 +160,14 @@ void GfxPicture::drawSci32Vga(int16 celNo) { cel_LiteralPos = READ_LE_UINT32(inbuffer + cel_headerPos + 28); cel_relXpos = READ_LE_UINT16(inbuffer + cel_headerPos + 38); cel_relYpos = READ_LE_UINT16(inbuffer + cel_headerPos + 40); - drawCelData(inbuffer, size, cel_headerPos, cel_RlePos, cel_LiteralPos, cel_relXpos, cel_relYpos, true); + drawCelData(inbuffer, size, cel_headerPos, cel_RlePos, cel_LiteralPos, cel_relXpos, cel_relYpos); cel_headerPos += 42; celCount--; } } #endif -void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos, int literalPos, int16 callerX, int16 callerY, bool hasSci32Header) { +void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos, int literalPos, int16 callerX, int16 callerY) { byte *celBitmap = NULL; byte *ptr = NULL; byte *headerPtr = inbuffer + headerPos; @@ -179,11 +184,16 @@ void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos int pixelNr, pixelCount; #ifdef ENABLE_SCI32 - if (!hasSci32Header) { + if (_resourceType != SCI_PICTURE_TYPE_SCI32) { #endif displaceX = (signed char)headerPtr[4]; displaceY = (unsigned char)headerPtr[5]; - clearColor = headerPtr[6]; + if (_resourceType == SCI_PICTURE_TYPE_SCI11) { + // SCI1.1 uses hardcoded clearcolor for pictures, even if cel header specifies otherwise + clearColor = _screen->getColorWhite(); + } else { + clearColor = headerPtr[6]; + } #ifdef ENABLE_SCI32 } else { displaceX = READ_LE_UINT16(headerPtr + 4); // probably signed?!? @@ -518,10 +528,29 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) { } break; + // Pattern opcodes are handled in sierra sci1.1+ as actual NOPs and normally they definitely should not occur + // inside picture data for such games case PIC_OP_SET_PATTERN: + if (_resourceType >= SCI_PICTURE_TYPE_SCI11) { + if (strcmp(g_sci->getGameID(), "sq4") == 0) { + // WORKAROUND: For SQ4 / for some pictures handle this like a terminator + // This picture includes garbage data, first a set pattern w/o parameter and then short pattern + // I guess that garbage is a left over from the sq4-floppy (sci1) to sq4-cd (sci1.1) conversion + switch (_resourceId) { + case 381: + case 376: + return; + default: + break; + } + } + error("pic-operation set pattern inside sci1.1+ vector data"); + } pattern_Code = data[curPos++]; break; case PIC_OP_SHORT_PATTERNS: + if (_resourceType >= SCI_PICTURE_TYPE_SCI11) + error("pic-operation short pattern inside sci1.1+ vector data"); vectorGetPatternTexture(data, curPos, pattern_Code, pattern_Texture); vectorGetAbsCoords(data, curPos, x, y); vectorPattern(x, y, pic_color, pic_priority, pic_control, pattern_Code, pattern_Texture); @@ -532,6 +561,8 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) { } break; case PIC_OP_MEDIUM_PATTERNS: + if (_resourceType >= SCI_PICTURE_TYPE_SCI11) + error("pic-operation medium pattern inside sci1.1+ vector data"); vectorGetPatternTexture(data, curPos, pattern_Code, pattern_Texture); vectorGetAbsCoords(data, curPos, x, y); vectorPattern(x, y, pic_color, pic_priority, pic_control, pattern_Code, pattern_Texture); @@ -542,6 +573,8 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) { } break; case PIC_OP_ABSOLUTE_PATTERN: + if (_resourceType >= SCI_PICTURE_TYPE_SCI11) + error("pic-operation absolute pattern inside sci1.1+ vector data"); while (vectorIsNonOpcode(data[curPos])) { vectorGetPatternTexture(data, curPos, pattern_Code, pattern_Texture); vectorGetAbsCoords(data, curPos, x, y); @@ -585,7 +618,7 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) { vectorGetAbsCoordsNoMirror(data, curPos, x, y); size = READ_LE_UINT16(data + curPos); curPos += 2; _priority = pic_priority; // set global priority so the cel gets drawn using current priority as well - drawCelData(data, _resource->size, curPos, curPos + 8, 0, x, y, false); + drawCelData(data, _resource->size, curPos, curPos + 8, 0, x, y); curPos += size; break; case PIC_OPX_EGA_SET_PRIORITY_TABLE: @@ -627,7 +660,7 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) { _priority = pic_priority; // set global priority so the cel gets drawn using current priority as well if (pic_priority == 255) _priority = 0; // if priority not set, use priority 0 - drawCelData(data, _resource->size, curPos, curPos + 8, 0, x, y, false); + drawCelData(data, _resource->size, curPos, curPos + 8, 0, x, y); curPos += size; break; case PIC_OPX_VGA_PRIORITY_TABLE_EQDIST: diff --git a/engines/sci/graphics/picture.h b/engines/sci/graphics/picture.h index 3374c33b52..5a86539b37 100644 --- a/engines/sci/graphics/picture.h +++ b/engines/sci/graphics/picture.h @@ -32,6 +32,12 @@ namespace Sci { #define SCI_PATTERN_CODE_USE_TEXTURE 0x20 #define SCI_PATTERN_CODE_PENSIZE 0x07 +enum { + SCI_PICTURE_TYPE_REGULAR = 0, + SCI_PICTURE_TYPE_SCI11 = 1, + SCI_PICTURE_TYPE_SCI32 = 2 +}; + class GfxPorts; class GfxScreen; class GfxPalette; @@ -57,7 +63,7 @@ private: void initData(GuiResourceId resourceId); void reset(); void drawSci11Vga(); - void drawCelData(byte *inbuffer, int size, int headerPos, int rlePos, int literalPos, int16 callerX, int16 callerY, bool hasSci32Header); + void drawCelData(byte *inbuffer, int size, int headerPos, int rlePos, int literalPos, int16 callerX, int16 callerY); void drawVectorData(byte *data, int size); bool vectorIsNonOpcode(byte pixel); void vectorGetAbsCoords(byte *data, int &curPos, int16 &x, int16 &y); @@ -80,6 +86,7 @@ private: int16 _resourceId; Resource *_resource; + int _resourceType; int16 _animationNr; bool _mirroredFlag; diff --git a/engines/sci/graphics/ports.cpp b/engines/sci/graphics/ports.cpp index ab3291dd79..cdb6fe4ae1 100644 --- a/engines/sci/graphics/ports.cpp +++ b/engines/sci/graphics/ports.cpp @@ -54,7 +54,7 @@ GfxPorts::~GfxPorts() { delete _menuPort; } -void GfxPorts::init(bool usesOldGfxFunctions, SciGui *gui, GfxPaint16 *paint16, GfxText16 *text16, Common::String gameId) { +void GfxPorts::init(bool usesOldGfxFunctions, SciGui *gui, GfxPaint16 *paint16, GfxText16 *text16) { int16 offTop = 10; _usesOldGfxFunctions = usesOldGfxFunctions; @@ -88,6 +88,7 @@ void GfxPorts::init(bool usesOldGfxFunctions, SciGui *gui, GfxPaint16 *paint16, // Jones, Slater and Hoyle 3 were called with parameter -Nw 0 0 200 320. // Mother Goose (SCI1) uses -Nw 0 0 159 262. The game will later use SetPort so we don't need to set the other fields. // This actually meant not skipping the first 10 pixellines in windowMgrPort + Common::String gameId = g_sci->getGameID(); if (gameId == "jones" || gameId == "slater" || gameId == "hoyle3" || (gameId == "mothergoose" && getSciVersion() == SCI_VERSION_1_EARLY)) offTop = 0; diff --git a/engines/sci/graphics/ports.h b/engines/sci/graphics/ports.h index 0876d9e442..c8ce6b3470 100644 --- a/engines/sci/graphics/ports.h +++ b/engines/sci/graphics/ports.h @@ -45,7 +45,7 @@ public: GfxPorts(SegManager *segMan, GfxScreen *screen); ~GfxPorts(); - void init(bool usesOldGfxFunctions, SciGui *gui, GfxPaint16 *paint16, GfxText16 *text16, Common::String gameId); + void init(bool usesOldGfxFunctions, SciGui *gui, GfxPaint16 *paint16, GfxText16 *text16); void kernelSetActive(uint16 portId); Common::Rect kernelGetPicWindow(int16 &picTop, int16 &picLeft); diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp index 7ca9e33509..0e054d5a76 100644 --- a/engines/sci/graphics/screen.cpp +++ b/engines/sci/graphics/screen.cpp @@ -93,7 +93,18 @@ GfxScreen::GfxScreen(ResourceManager *resMan, int16 width, int16 height, int ups } // Initialize the actual screen - initGraphics(_displayWidth, _displayHeight, _displayWidth > 320); + + if (_resMan->isSci11Mac() && getSciVersion() == SCI_VERSION_1_1) { + // For SCI1.1 Mac, we need to expand the screen to accommodate for + // the icon bar. Of course, both KQ6 and QFG1 VGA differ in size. + if (!scumm_stricmp(g_sci->getGameID(), "kq6")) + initGraphics(_displayWidth, _displayHeight + 26, _displayWidth > 320); + else if (!scumm_stricmp(g_sci->getGameID(), "qfg1")) + initGraphics(_displayWidth, _displayHeight + 20, _displayWidth > 320); + else + error("Unknown SCI1.1 Mac game"); + } else + initGraphics(_displayWidth, _displayHeight, _displayWidth > 320); } GfxScreen::~GfxScreen() { diff --git a/engines/sci/module.mk b/engines/sci/module.mk index 8c4d666ba7..a2cfd38f95 100644 --- a/engines/sci/module.mk +++ b/engines/sci/module.mk @@ -6,6 +6,7 @@ MODULE_OBJS := \ detection.o \ event.o \ resource.o \ + resource_audio.o \ sci.o \ util.o \ engine/features.o \ @@ -44,6 +45,7 @@ MODULE_OBJS := \ graphics/font.o \ graphics/fontsjis.o \ graphics/gui.o \ + graphics/maciconbar.o \ graphics/menu.o \ graphics/paint.o \ graphics/paint16.o \ diff --git a/engines/sci/parser/grammar.cpp b/engines/sci/parser/grammar.cpp index 1cfe84076f..070e6767cf 100644 --- a/engines/sci/parser/grammar.cpp +++ b/engines/sci/parser/grammar.cpp @@ -258,6 +258,11 @@ void Vocabulary::freeRuleList(ParseRuleList *list) { static ParseRuleList *_vocab_add_rule(ParseRuleList *list, ParseRule *rule) { if (!rule) return list; + if (!rule->_data.size()) { + // Special case for qfg2 demo + warning("no rule contents on _vocab_add_rule()"); + return list; + } ParseRuleList *new_elem = new ParseRuleList(rule); diff --git a/engines/sci/parser/said.cpp b/engines/sci/parser/said.cpp index 5cd1310ad3..f49704372a 100644 --- a/engines/sci/parser/said.cpp +++ b/engines/sci/parser/said.cpp @@ -2443,13 +2443,14 @@ static int augment_parse_nodes(parse_tree_node_t *parset, parse_tree_node_t *sai int said(EngineState *s, byte *spec, bool verbose) { int retval; + Vocabulary *voc = g_sci->getVocabulary(); - parse_tree_node_t *parse_tree_ptr = s->_voc->_parserNodes; + parse_tree_node_t *parse_tree_ptr = voc->_parserNodes; - if (s->_voc->parserIsValid) { + if (voc->parserIsValid) { if (said_parse_spec(spec)) { printf("Offending spec was: "); - s->_voc->decipherSaidBlock(spec); + voc->decipherSaidBlock(spec); return SAID_NO_MATCH; } diff --git a/engines/sci/parser/said.y b/engines/sci/parser/said.y index 27486c5794..cbb2ff3e62 100644 --- a/engines/sci/parser/said.y +++ b/engines/sci/parser/said.y @@ -799,13 +799,14 @@ static int augment_parse_nodes(parse_tree_node_t *parset, parse_tree_node_t *sai int said(EngineState *s, byte *spec, bool verbose) { int retval; + Vocabulary *voc = g_sci->getVocabulary(); - parse_tree_node_t *parse_tree_ptr = s->_voc->_parserNodes; + parse_tree_node_t *parse_tree_ptr = voc->_parserNodes; - if (s->_voc->parserIsValid) { + if (voc->parserIsValid) { if (said_parse_spec(spec)) { printf("Offending spec was: "); - s->_voc->decipherSaidBlock(spec); + voc->decipherSaidBlock(spec); return SAID_NO_MATCH; } diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp index 4888dbd4cb..4818428663 100644 --- a/engines/sci/resource.cpp +++ b/engines/sci/resource.cpp @@ -26,7 +26,6 @@ // Resource library #include "common/file.h" -#include "common/macresman.h" #include "sci/resource.h" #include "sci/util.h" @@ -45,18 +44,6 @@ struct resource_index_t { uint16 wSize; }; -struct ResourceSource { - ResSourceType source_type; - bool scanned; - Common::String location_name; // FIXME: Replace by FSNode ? - const Common::FSNode *resourceFile; - int volume_number; - ResourceSource *associated_map; - uint32 audioCompressionType; - int32 *audioCompressionOffsetMapping; - Common::MacResManager macResMan; -}; - ////////////////////////////////////////////////////////////////////// static SciVersion s_sciVersion = SCI_VERSION_NONE; // FIXME: Move this inside a suitable class, e.g. SciEngine @@ -196,7 +183,7 @@ ResourceSource *ResourceManager::addExternalMap(const char *file_name, int volum return newsrc; } -ResourceSource *ResourceManager::addExternalMap(const Common::FSNode *mapFile) { +ResourceSource *ResourceManager::addExternalMap(const Common::FSNode *mapFile, int volume_nr) { ResourceSource *newsrc = new ResourceSource(); newsrc->source_type = kSourceExtMap; @@ -204,7 +191,7 @@ ResourceSource *ResourceManager::addExternalMap(const Common::FSNode *mapFile) { newsrc->resourceFile = mapFile; newsrc->scanned = false; newsrc->associated_map = NULL; - newsrc->volume_number = 0; + newsrc->volume_number = volume_nr; _sources.push_back(newsrc); return newsrc; @@ -250,6 +237,7 @@ ResourceSource *ResourceManager::addPatchDir(const char *dirname) { ResourceSource *newsrc = new ResourceSource(); newsrc->source_type = kSourceDirectory; + newsrc->resourceFile = 0; newsrc->scanned = false; newsrc->location_name = dirname; @@ -270,37 +258,7 @@ ResourceSource *ResourceManager::getVolume(ResourceSource *map, int volume_nr) { // Resource manager constructors and operations -void ResourceManager::checkIfAudioVolumeIsCompressed(ResourceSource *source) { - Common::File *file = getVolumeFile(source->location_name.c_str()); - if (!file) { - warning("Failed to open %s", source->location_name.c_str()); - return; - } - file->seek(0, SEEK_SET); - uint32 compressionType = file->readUint32BE(); - switch (compressionType) { - case MKID_BE('MP3 '): - case MKID_BE('OGG '): - case MKID_BE('FLAC'): - // Detected a compressed audio volume - source->audioCompressionType = compressionType; - // Now read the whole offset mapping table for later usage - int32 recordCount = file->readUint32LE(); - if (!recordCount) - error("compressed audio volume doesn't contain any entries!"); - int32 *offsetMapping = new int32[(recordCount + 1) * 2]; - source->audioCompressionOffsetMapping = offsetMapping; - for (int recordNo = 0; recordNo < recordCount; recordNo++) { - *offsetMapping++ = file->readUint32LE(); - *offsetMapping++ = file->readUint32LE(); - } - // Put ending zero - *offsetMapping++ = 0; - *offsetMapping++ = file->size(); - } -} - -bool ResourceManager::loadPatch(Resource *res, Common::File &file) { +bool ResourceManager::loadPatch(Resource *res, Common::SeekableReadStream *file) { // We assume that the resource type matches res->type // We also assume that the current file position is right at the actual data (behind resourceid/headersize byte) @@ -315,12 +273,12 @@ bool ResourceManager::loadPatch(Resource *res, Common::File &file) { unsigned int really_read; if (res->_headerSize > 0) { - really_read = file.read(res->_header, res->_headerSize); + really_read = file->read(res->_header, res->_headerSize); if (really_read != res->_headerSize) error("Read %d bytes from %s but expected %d", really_read, res->_id.toString().c_str(), res->_headerSize); } - really_read = file.read(res->data, res->size); + really_read = file->read(res->data, res->size); if (really_read != res->size) error("Read %d bytes from %s but expected %d", really_read, res->_id.toString().c_str(), res->size); @@ -338,77 +296,18 @@ bool ResourceManager::loadFromPatchFile(Resource *res) { } // Skip resourceid and header size byte file.seek(2, SEEK_SET); - return loadPatch(res, file); -} - -bool ResourceManager::loadFromWaveFile(Resource *res, Common::File &file) { - res->data = new byte[res->size]; - - uint32 really_read = file.read(res->data, res->size); - if (really_read != res->size) - error("Read %d bytes from %s but expected %d", really_read, res->_id.toString().c_str(), res->size); - - res->_status = kResStatusAllocated; - return true; -} - -bool ResourceManager::loadFromAudioVolumeSCI11(Resource *res, Common::File &file) { - // Check for WAVE files here - uint32 riffTag = file.readUint32BE(); - if (riffTag == MKID_BE('RIFF')) { - res->_headerSize = 0; - res->size = file.readUint32LE(); - file.seek(-8, SEEK_CUR); - return loadFromWaveFile(res, file); - } - file.seek(-4, SEEK_CUR); - - ResourceType type = (ResourceType)(file.readByte() & 0x7f); - if (((res->_id.type == kResourceTypeAudio || res->_id.type == kResourceTypeAudio36) && (type != kResourceTypeAudio)) - || ((res->_id.type == kResourceTypeSync || res->_id.type == kResourceTypeSync36) && (type != kResourceTypeSync))) { - warning("Resource type mismatch loading %s from %s", res->_id.toString().c_str(), file.getName()); - res->unalloc(); - return false; - } - - res->_headerSize = file.readByte(); - - if (type == kResourceTypeAudio) { - if (res->_headerSize != 11 && res->_headerSize != 12) { - warning("Unsupported audio header"); - res->unalloc(); - return false; - } - - // Load sample size - file.seek(7, SEEK_CUR); - res->size = file.readUint32LE(); - // Adjust offset to point at the header data again - file.seek(-11, SEEK_CUR); - } - - return loadPatch(res, file); -} - -bool ResourceManager::loadFromAudioVolumeSCI1(Resource *res, Common::File &file) { - res->data = new byte[res->size]; - - if (res->data == NULL) { - error("Can't allocate %d bytes needed for loading %s", res->size, res->_id.toString().c_str()); - } - - unsigned int really_read = file.read(res->data, res->size); - if (really_read != res->size) - warning("Read %d bytes from %s but expected %d", really_read, res->_id.toString().c_str(), res->size); - - res->_status = kResStatusAllocated; - return true; + return loadPatch(res, &file); } -Common::File *ResourceManager::getVolumeFile(const char *filename) { +Common::SeekableReadStream *ResourceManager::getVolumeFile(ResourceSource *source) { Common::List<Common::File *>::iterator it = _volumeFiles.begin(); Common::File *file; + if (source->resourceFile) + return source->resourceFile->createReadStream(); + + const char *filename = source->location_name.c_str(); + // check if file is already opened while (it != _volumeFiles.end()) { file = *it; @@ -445,11 +344,10 @@ void ResourceManager::loadResource(Resource *res) { return; if (res->_source->source_type == kSourceMacResourceFork) { - //error("ResourceManager::loadResource(): TODO: Mac resource fork ;)"); Common::SeekableReadStream *stream = res->_source->macResMan.getResource(resTypeToMacTag(res->_id.type), res->_id.number); if (!stream) - error("Could not get Mac resource fork resource"); + error("Could not get Mac resource fork resource: %d %d", res->_id.type, res->_id.number); int error = decompress(res, stream); if (error) { @@ -460,10 +358,9 @@ void ResourceManager::loadResource(Resource *res) { return; } - Common::File *file; - // Either loading from volume or patch loading failed - file = getVolumeFile(res->_source->location_name.c_str()); - if (!file) { + Common::SeekableReadStream *fileStream = getVolumeFile(res->_source); + + if (!fileStream) { warning("Failed to open %s", res->_source->location_name.c_str()); res->unalloc(); return; @@ -471,8 +368,10 @@ void ResourceManager::loadResource(Resource *res) { switch(res->_source->source_type) { case kSourceWave: - file->seek(res->_fileOffset, SEEK_SET); - loadFromWaveFile(res, *file); + fileStream->seek(res->_fileOffset, SEEK_SET); + loadFromWaveFile(res, fileStream); + if (res->_source->resourceFile) + delete fileStream; return; case kSourceAudioVolume: @@ -503,30 +402,39 @@ void ResourceManager::loadResource(Resource *res) { if (!compressedOffset) error("could not translate offset to compressed offset in audio volume"); - file->seek(compressedOffset, SEEK_SET); + fileStream->seek(compressedOffset, SEEK_SET); switch (res->_id.type) { case kResourceTypeAudio: case kResourceTypeAudio36: // Directly read the stream, compressed audio wont have resource type id and header size for SCI1.1 - loadFromAudioVolumeSCI1(res, *file); + loadFromAudioVolumeSCI1(res, fileStream); + if (res->_source->resourceFile) + delete fileStream; return; default: break; } } else { // original file, directly seek to given offset and get SCI1/SCI1.1 audio resource - file->seek(res->_fileOffset, SEEK_SET); + fileStream->seek(res->_fileOffset, SEEK_SET); } if (getSciVersion() < SCI_VERSION_1_1) - loadFromAudioVolumeSCI1(res, *file); + loadFromAudioVolumeSCI1(res, fileStream); else - loadFromAudioVolumeSCI11(res, *file); + loadFromAudioVolumeSCI11(res, fileStream); + + if (res->_source->resourceFile) + delete fileStream; return; default: - file->seek(res->_fileOffset, SEEK_SET); - int error = decompress(res, file); + fileStream->seek(res->_fileOffset, SEEK_SET); + int error = decompress(res, fileStream); + + if (res->_source->resourceFile) + delete fileStream; + if (error) { warning("Error %d occured while reading %s from resource file: %s", error, res->_id.toString().c_str(), sci_error_types[error]); @@ -539,27 +447,14 @@ Resource *ResourceManager::testResource(ResourceId id) { return _resMap.getVal(id, NULL); } -int sci0_get_compression_method(Common::ReadStream &stream) { - uint16 compressionMethod; - - stream.readUint16LE(); - stream.readUint16LE(); - stream.readUint16LE(); - compressionMethod = stream.readUint16LE(); - if (stream.err()) - return SCI_ERROR_IO_ERROR; - - return compressionMethod; -} - int ResourceManager::addAppropriateSources() { Common::ArchiveMemberList files; - if (Common::File::exists("RESOURCE.MAP")) { + if (Common::File::exists("resource.map")) { // SCI0-SCI2 file naming scheme - ResourceSource *map = addExternalMap("RESOURCE.MAP"); + ResourceSource *map = addExternalMap("resource.map"); - SearchMan.listMatchingMembers(files, "RESOURCE.0??"); + SearchMan.listMatchingMembers(files, "resource.0??"); for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) { const Common::String name = (*x)->getName(); @@ -570,12 +465,12 @@ int ResourceManager::addAppropriateSources() { } #ifdef ENABLE_SCI32 // GK1CD hires content - if (Common::File::exists("ALT.MAP") && Common::File::exists("RESOURCE.ALT")) - addSource(addExternalMap("ALT.MAP", 10), kSourceVolume, "RESOURCE.ALT", 10); + if (Common::File::exists("alt.map") && Common::File::exists("resource.alt")) + addSource(addExternalMap("alt.map", 10), kSourceVolume, "resource.alt", 10); #endif } else if (Common::File::exists("Data1")) { // Mac SCI1.1+ file naming scheme - SearchMan.listMatchingMembers(files, "Data?"); + SearchMan.listMatchingMembers(files, "Data?*"); for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) { Common::String filename = (*x)->getName(); @@ -594,8 +489,8 @@ int ResourceManager::addAppropriateSources() { } else { // SCI2.1-SCI3 file naming scheme Common::ArchiveMemberList mapFiles; - SearchMan.listMatchingMembers(mapFiles, "RESMAP.0??"); - SearchMan.listMatchingMembers(files, "RESSCI.0??"); + SearchMan.listMatchingMembers(mapFiles, "resmap.0??"); + SearchMan.listMatchingMembers(files, "ressci.0??"); // We need to have the same number of maps as resource archives if (mapFiles.empty() || files.empty() || mapFiles.size() != files.size()) @@ -617,9 +512,9 @@ int ResourceManager::addAppropriateSources() { } // SCI2.1 resource patches - if (Common::File::exists("RESMAP.PAT") && Common::File::exists("RESSCI.PAT")) { + if (Common::File::exists("resmap.pat") && Common::File::exists("ressci.pat")) { // We add this resource with a map which surely won't exist - addSource(addExternalMap("RESMAP.PAT", 100), kSourceVolume, "RESSCI.PAT", 100); + addSource(addExternalMap("resmap.pat", 100), kSourceVolume, "ressci.pat", 100); } } #else @@ -628,14 +523,18 @@ int ResourceManager::addAppropriateSources() { #endif addPatchDir("."); - if (Common::File::exists("MESSAGE.MAP")) - addSource(addExternalMap("MESSAGE.MAP"), kSourceVolume, "RESOURCE.MSG", 0); + if (Common::File::exists("message.map")) + addSource(addExternalMap("message.map"), kSourceVolume, "resource.msg", 0); return 1; } int ResourceManager::addAppropriateSources(const Common::FSList &fslist) { ResourceSource *map = 0; +#ifdef ENABLE_SCI32 + ResourceSource *sci21PatchMap = 0; + const Common::FSNode *sci21PatchRes = 0; +#endif // First, find resource.map for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) { @@ -645,17 +544,33 @@ int ResourceManager::addAppropriateSources(const Common::FSList &fslist) { Common::String filename = file->getName(); filename.toLowercase(); - // TODO: Load the SCI2.1+ maps (resmap.*) in concurrence with the volumes to - // get the proper volume numbers from the maps. - if (filename.contains("resource.map") || filename.contains("resmap.000")) { + if (filename.contains("resource.map")) map = addExternalMap(file); - break; + + if (filename.contains("resmap.0")) { + const char *dot = strrchr(file->getName().c_str(), '.'); + int number = atoi(dot + 1); + map = addExternalMap(file, number); } + +#ifdef ENABLE_SCI32 + // SCI2.1 resource patches + if (filename.contains("resmap.pat")) + sci21PatchMap = addExternalMap(file, 100); + + if (filename.contains("ressci.pat")) + sci21PatchRes = file; +#endif } if (!map) return 0; +#ifdef ENABLE_SCI32 + if (sci21PatchMap && sci21PatchRes) + addSource(sci21PatchMap, kSourceVolume, sci21PatchRes, 100); +#endif + // Now find all the resource.0?? files for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) { if (file->isDirectory()) @@ -696,36 +611,6 @@ int ResourceManager::addInternalSources() { return 1; } -void ResourceManager::addNewGMPatch(const Common::String &gameId) { - Common::String gmPatchFile; - - if (gameId == "ecoquest") - gmPatchFile = "ECO1GM.PAT"; - else if (gameId == "hoyle3") - gmPatchFile = "HOY3GM.PAT"; - else if (gameId == "hoyle3") - gmPatchFile = "HOY3GM.PAT"; - else if (gameId == "lsl1sci") - gmPatchFile = "LL1_GM.PAT"; - else if (gameId == "lsl5") - gmPatchFile = "LL5_GM.PAT"; - else if (gameId == "longbow") - gmPatchFile = "ROBNGM.PAT"; - else if (gameId == "sq1sci") - gmPatchFile = "SQ1_GM.PAT"; - else if (gameId == "sq4") - gmPatchFile = "SQ4_GM.PAT"; - else if (gameId == "fairytales") - gmPatchFile = "TALEGM.PAT"; - - if (!gmPatchFile.empty() && Common::File::exists(gmPatchFile)) { - ResourceSource *psrcPatch = new ResourceSource; - psrcPatch->source_type = kSourcePatch; - psrcPatch->location_name = gmPatchFile; - processPatch(psrcPatch, kResourceTypePatch, 4); - } -} - void ResourceManager::scanNewSources() { for (Common::List<ResourceSource *>::iterator it = _sources.begin(); it != _sources.end(); ++it) { ResourceSource *source = *it; @@ -735,6 +620,9 @@ void ResourceManager::scanNewSources() { switch (source->source_type) { case kSourceDirectory: readResourcePatches(source); +#ifdef ENABLE_SCI32 + readResourcePatchesBase36(source); +#endif readWaveAudioPatches(); break; case kSourceExtMap: @@ -990,7 +878,6 @@ const char *ResourceManager::versionDescription(ResVersion version) const { ResourceManager::ResVersion ResourceManager::detectMapVersion() { Common::SeekableReadStream *fileStream = 0; - Common::File *file = 0; byte buff[6]; ResourceSource *rsrc= 0; @@ -1001,7 +888,7 @@ ResourceManager::ResVersion ResourceManager::detectMapVersion() { if (rsrc->resourceFile) { fileStream = rsrc->resourceFile->createReadStream(); } else { - file = new Common::File(); + Common::File *file = new Common::File(); file->open(rsrc->location_name); if (file->isOpen()) fileStream = file; @@ -1081,7 +968,6 @@ ResourceManager::ResVersion ResourceManager::detectMapVersion() { ResourceManager::ResVersion ResourceManager::detectVolVersion() { Common::SeekableReadStream *fileStream = 0; - Common::File *file = 0; ResourceSource *rsrc; for (Common::List<ResourceSource *>::iterator it = _sources.begin(); it != _sources.end(); ++it) { @@ -1091,7 +977,7 @@ ResourceManager::ResVersion ResourceManager::detectVolVersion() { if (rsrc->resourceFile) { fileStream = rsrc->resourceFile->createReadStream(); } else { - file = new Common::File(); + Common::File *file = new Common::File(); file->open(rsrc->location_name); if (file->isOpen()) fileStream = file; @@ -1180,27 +1066,36 @@ ResourceManager::ResVersion ResourceManager::detectVolVersion() { } // version-agnostic patch application -void ResourceManager::processPatch(ResourceSource *source, ResourceType restype, int resnumber) { - Common::File file; +void ResourceManager::processPatch(ResourceSource *source, ResourceType restype, uint16 resnumber, uint32 tuple) { + Common::SeekableReadStream *fileStream = 0; Resource *newrsc; - ResourceId resId = ResourceId(restype, resnumber); + ResourceId resId = ResourceId(restype, resnumber, tuple); byte patchtype, patch_data_offset; int fsize; - if (resnumber == -1) - return; - if (!file.open(source->location_name)) { - warning("ResourceManager::processPatch(): failed to open %s", source->location_name.c_str()); + if (resnumber == 0xFFFF) return; + + if (source->resourceFile) { + fileStream = source->resourceFile->createReadStream(); + } else { + Common::File *file = new Common::File(); + if (!file->open(source->location_name)) { + warning("ResourceManager::processPatch(): failed to open %s", source->location_name.c_str()); + return; + } + fileStream = file; } - fsize = file.size(); + fsize = fileStream->size(); if (fsize < 3) { debug("Patching %s failed - file too small", source->location_name.c_str()); return; } - patchtype = file.readByte() & 0x7F; - patch_data_offset = file.readByte(); + patchtype = fileStream->readByte() & 0x7F; + patch_data_offset = fileStream->readByte(); + + delete fileStream; if (patchtype != restype) { debug("Patching %s failed - resource type mismatch", source->location_name.c_str()); @@ -1215,8 +1110,12 @@ void ResourceManager::processPatch(ResourceSource *source, ResourceType restype, case 1: patch_data_offset = 2; break; + case 4: + patch_data_offset = 8; + break; default: - warning("Resource patch unsupported special case %X", patch_data_offset); + warning("Resource patch unsupported special case %X", patch_data_offset & 0x7F); + return; } } @@ -1241,11 +1140,70 @@ void ResourceManager::processPatch(ResourceSource *source, ResourceType restype, debugC(1, kDebugLevelResMan, "Patching %s - OK", source->location_name.c_str()); } +#ifdef ENABLE_SCI32 + +void ResourceManager::readResourcePatchesBase36(ResourceSource *source) { + // The base36 encoded audio36 and sync36 resources use a different naming scheme, because they + // cannot be described with a single resource number, but are a result of a + // <number, noun, verb, cond, seq> tuple. Please don't be confused with the normal audio patches + // (*.aud) and normal sync patches (*.syn). audio36 patches can be seen for example in the AUD + // folder of GK1CD, and are like this file: @0CS0M00.0X1. GK1CD is the first game where these + // have been observed. The actual audio36 and sync36 resources exist in SCI1.1 as well, but the + // first game where external patch files for them have been found is GK1CD. The names of these + // files are base36 encoded, and we handle their decoding here. audio36 files start with a '@', + // whereas sync36 start with a '#'. Mac versions begin with 'A' (probably meaning AIFF). Torin + // has several that begin with 'B'. + + Common::String name, inputName; + Common::ArchiveMemberList files; + //ResourceSource *psrcPatch; + + for (int i = kResourceTypeAudio36; i <= kResourceTypeSync36; ++i) { + // audio36 resources start with a @, A, or B + // sync36 resources start with a # + if (i == kResourceTypeAudio36) { + SearchMan.listMatchingMembers(files, "@???????.???"); + SearchMan.listMatchingMembers(files, "A???????.???"); + SearchMan.listMatchingMembers(files, "B???????.???"); + } else + SearchMan.listMatchingMembers(files, "#???????.???"); + + for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) { + name = (*x)->getName(); + inputName = (*x)->getName(); + inputName.toUppercase(); + inputName.deleteChar(0); // delete the first character (type) + inputName.deleteChar(7); // delete the dot + + // The base36 encoded resource contains the following: + // uint16 number, byte noun, byte verb, byte cond, byte seq + // TODO: this is still not right (especially the tuple part, seems to be overflowing?) + uint16 number = strtol(Common::String(inputName.c_str(), 2).c_str(), 0, 36); + uint32 tuple = strtol(inputName.c_str() + 2, 0, 36); + ResourceId resource36((ResourceType)i, number, tuple); + + if (i == kResourceTypeAudio36) + debug("audio36 patch: %s => %s. tuple:%d, %s\n", name.c_str(), inputName.c_str(), tuple, resource36.toString().c_str()); + else + debug("sync36 patch: %s => %s. tuple:%d, %s\n", name.c_str(), inputName.c_str(), tuple, resource36.toString().c_str()); + + /* + psrcPatch = new ResourceSource; + psrcPatch->source_type = kSourcePatch; + psrcPatch->location_name = name; + psrcPatch->resourceFile = 0; + processPatch(psrcPatch, (ResourceType)i, number, tuple); + */ + } + } +} + +#endif void ResourceManager::readResourcePatches(ResourceSource *source) { -// Note: since some SCI1 games(KQ5 floppy, SQ4) might use SCI0 naming scheme for patch files -// this function tries to read patch file with any supported naming scheme, -// regardless of s_sciVersion value + // Note: since some SCI1 games(KQ5 floppy, SQ4) might use SCI0 naming scheme for patch files + // this function tries to read patch file with any supported naming scheme, + // regardless of s_sciVersion value Common::String mask, name; Common::ArchiveMemberList files; @@ -1253,11 +1211,7 @@ void ResourceManager::readResourcePatches(ResourceSource *source) { const char *szResType; ResourceSource *psrcPatch; - for (int i = kResourceTypeView; i <= kResourceTypeRobot; ++i) { - // TODO: add support for audio36 and sync36 files - if (i == kResourceTypeAudio36 || i == kResourceTypeSync36) - continue; - + for (int i = kResourceTypeView; i <= kResourceTypeHeap; ++i) { files.clear(); szResType = getResourceTypeName((ResourceType)i); // SCI0 naming - type.nnn @@ -1268,6 +1222,7 @@ void ResourceManager::readResourcePatches(ResourceSource *source) { mask = "*."; mask += resourceTypeSuffixes[i]; SearchMan.listMatchingMembers(files, mask); + for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) { bool bAdd = false; name = (*x)->getName(); @@ -1289,75 +1244,42 @@ void ResourceManager::readResourcePatches(ResourceSource *source) { psrcPatch = new ResourceSource; psrcPatch->source_type = kSourcePatch; psrcPatch->location_name = name; + psrcPatch->resourceFile = 0; processPatch(psrcPatch, (ResourceType)i, number); } } } } -void ResourceManager::readWaveAudioPatches() { - // Here we do check for SCI1.1+ so we can patch wav files in as audio resources - Common::ArchiveMemberList files; - SearchMan.listMatchingMembers(files, "*.wav"); - - for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) { - Common::String name = (*x)->getName(); - - if (isdigit(name[0])) { - int number = atoi(name.c_str()); - ResourceSource *psrcPatch = new ResourceSource; - psrcPatch->source_type = kSourceWave; - psrcPatch->location_name = name; - psrcPatch->volume_number = 0; - psrcPatch->audioCompressionType = 0; - - ResourceId resId = ResourceId(kResourceTypeAudio, number); - - Resource *newrsc = NULL; - - // Prepare destination, if neccessary - if (_resMap.contains(resId) == false) { - newrsc = new Resource; - _resMap.setVal(resId, newrsc); - } else - newrsc = _resMap.getVal(resId); - - // Get the size of the file - Common::SeekableReadStream *stream = (*x)->createReadStream(); - uint32 fileSize = stream->size(); - delete stream; - - // Overwrite everything, because we're patching - newrsc->_id = resId; - newrsc->_status = kResStatusNoMalloc; - newrsc->_source = psrcPatch; - newrsc->size = fileSize; - newrsc->_headerSize = 0; - debugC(1, kDebugLevelResMan, "Patching %s - OK", psrcPatch->location_name.c_str()); - } - } -} - int ResourceManager::readResourceMapSCI0(ResourceSource *map) { - Common::File file; + Common::SeekableReadStream *fileStream = 0; Resource *res; ResourceType type; uint16 number, id; uint32 offset; - if (!file.open(map->location_name)) - return SCI_ERROR_RESMAP_NOT_FOUND; + if (map->resourceFile) { + fileStream = map->resourceFile->createReadStream(); + if (!fileStream) + return SCI_ERROR_RESMAP_NOT_FOUND; + } else { + Common::File *file = new Common::File(); + if (!file->open(map->location_name)) + return SCI_ERROR_RESMAP_NOT_FOUND; + fileStream = file; + } - file.seek(0, SEEK_SET); + fileStream->seek(0, SEEK_SET); byte bMask = (_mapVersion == kResVersionSci1Middle) ? 0xF0 : 0xFC; byte bShift = (_mapVersion == kResVersionSci1Middle) ? 28 : 26; do { - id = file.readUint16LE(); - offset = file.readUint32LE(); + id = fileStream->readUint16LE(); + offset = fileStream->readUint32LE(); - if (file.eos() || file.err()) { + if (fileStream->eos() || fileStream->err()) { + delete fileStream; warning("Error while reading %s", map->location_name.c_str()); return SCI_ERROR_RESMAP_NOT_FOUND; } @@ -1386,15 +1308,26 @@ int ResourceManager::readResourceMapSCI0(ResourceSource *map) { res->_id = resId; _resMap.setVal(resId, res); } - } while (!file.eos()); + } while (!fileStream->eos()); + + delete fileStream; return 0; } int ResourceManager::readResourceMapSCI1(ResourceSource *map) { - Common::File file; + Common::SeekableReadStream *fileStream = 0; Resource *res; - if (!file.open(map->location_name)) - return SCI_ERROR_RESMAP_NOT_FOUND; + + if (map->resourceFile) { + fileStream = map->resourceFile->createReadStream(); + if (!fileStream) + return SCI_ERROR_RESMAP_NOT_FOUND; + } else { + Common::File *file = new Common::File(); + if (!file->open(map->location_name)) + return SCI_ERROR_RESMAP_NOT_FOUND; + fileStream = file; + } resource_index_t resMap[32]; memset(resMap, 0, sizeof(resource_index_t) * 32); @@ -1405,8 +1338,8 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) { // Read resource type and offsets to resource offsets block from .MAP file // The last entry has type=0xFF (0x1F) and offset equals to map file length do { - type = file.readByte() & 0x1F; - resMap[type].wOffset = file.readUint16LE(); + type = fileStream->readByte() & 0x1F; + resMap[type].wOffset = fileStream->readUint16LE(); resMap[prevtype].wSize = (resMap[type].wOffset - resMap[prevtype].wOffset) / nEntrySize; prevtype = type; @@ -1417,18 +1350,18 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) { for (type = 0; type < 32; type++) { if (resMap[type].wOffset == 0) // this resource does not exist in map continue; - file.seek(resMap[type].wOffset); + fileStream->seek(resMap[type].wOffset); for (int i = 0; i < resMap[type].wSize; i++) { - uint16 number = file.readUint16LE(); + uint16 number = fileStream->readUint16LE(); int volume_nr = 0; if (_mapVersion == kResVersionSci11) { // offset stored in 3 bytes - off = file.readUint16LE(); - off |= file.readByte() << 16; + off = fileStream->readUint16LE(); + off |= fileStream->readByte() << 16; off <<= 1; } else { // offset/volume stored in 4 bytes - off = file.readUint32LE(); + off = fileStream->readUint32LE(); if (_mapVersion < kResVersionSci11) { volume_nr = off >> 28; // most significant 4 bits off &= 0x0FFFFFFF; // least significant 28 bits @@ -1436,7 +1369,8 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) { // in SCI32 it's a plain offset } } - if (file.eos() || file.err()) { + if (fileStream->eos() || fileStream->err()) { + delete fileStream; warning("Error while reading %s", map->location_name.c_str()); return SCI_ERROR_RESMAP_NOT_FOUND; } @@ -1448,7 +1382,7 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) { res->_id = resId; // NOTE: We add the map's volume number here to the specified volume number - // for SCI2.1 and SCI3 maps that are not RESMAP.000. The RESMAP.* files' numbers + // for SCI2.1 and SCI3 maps that are not resmap.000. The resmap.* files' numbers // need to be used in concurrence with the volume specified in the map to get // the actual resource file. res->_source = getVolume(map, volume_nr + map->volume_number); @@ -1456,6 +1390,8 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) { } } } + + delete fileStream; return 0; } @@ -1476,7 +1412,11 @@ struct { { MKID_BE('PAL '), kResourceTypePalette }, { MKID_BE('snd '), kResourceTypeAudio }, { MKID_BE('MSG '), kResourceTypeMessage }, - { MKID_BE('HEP '), kResourceTypeHeap } + { MKID_BE('HEP '), kResourceTypeHeap }, + { MKID_BE('IBIN'), kResourceTypeMacIconBarPictN }, + { MKID_BE('IBIS'), kResourceTypeMacIconBarPictS }, + { MKID_BE('PICT'), kResourceTypeMacPict }, + { MKID_BE('SYN '), kResourceTypeSync } }; static uint32 resTypeToMacTag(ResourceType type) { @@ -1509,6 +1449,16 @@ int ResourceManager::readMacResourceFork(ResourceSource *source) { Common::MacResIDArray idArray = source->macResMan.getResIDArray(tagArray[i]); for (uint32 j = 0; j < idArray.size(); j++) { + // Get the size of the file + Common::SeekableReadStream *stream = source->macResMan.getResource(tagArray[i], idArray[j]); + + // Some IBIS resources have a size of 0, so we skip them + if (!stream) + continue; + + uint32 fileSize = stream->size(); + delete stream; + ResourceId resId = ResourceId(type, idArray[j]); Resource *newrsc = NULL; @@ -1520,11 +1470,6 @@ int ResourceManager::readMacResourceFork(ResourceSource *source) { } else newrsc = _resMap.getVal(resId); - // Get the size of the file - Common::SeekableReadStream *stream = source->macResMan.getResource(tagArray[i], idArray[j]); - uint32 fileSize = stream->size(); - delete stream; - // Overwrite everything newrsc->_id = resId; newrsc->_status = kResStatusNoMalloc; @@ -1549,261 +1494,6 @@ void ResourceManager::addResource(ResourceId resId, ResourceSource *src, uint32 } } -void ResourceManager::removeAudioResource(ResourceId resId) { - // Remove resource, unless it was loaded from a patch - if (_resMap.contains(resId)) { - Resource *res = _resMap.getVal(resId); - - if (res->_source->source_type == kSourceAudioVolume) { - if (res->_status == kResStatusLocked) { - warning("Failed to remove resource %s (still in use)", resId.toString().c_str()); - } else { - if (res->_status == kResStatusEnqueued) - removeFromLRU(res); - - _resMap.erase(resId); - delete res; - } - } - } -} - -// Early SCI1.1 65535.MAP structure (uses RESOURCE.AUD): -// ========= -// 6-byte entries: -// w nEntry -// dw offset - -// Late SCI1.1 65535.MAP structure (uses RESOURCE.SFX): -// ========= -// 5-byte entries: -// w nEntry -// tb offset (cumulative) - -// Early SCI1.1 MAP structure: -// =============== -// 10-byte entries: -// b noun -// b verb -// b cond -// b seq -// dw offset -// w syncSize + syncAscSize - -// Late SCI1.1 MAP structure: -// =============== -// Header: -// dw baseOffset -// Followed by 7 or 11-byte entries: -// b noun -// b verb -// b cond -// b seq -// tb cOffset (cumulative offset) -// w syncSize (iff seq has bit 7 set) -// w syncAscSize (iff seq has bit 6 set) - -int ResourceManager::readAudioMapSCI11(ResourceSource *map) { - bool isEarly = true; - uint32 offset = 0; - Resource *mapRes = findResource(ResourceId(kResourceTypeMap, map->volume_number), false); - - if (!mapRes) { - warning("Failed to open %i.MAP", map->volume_number); - return SCI_ERROR_RESMAP_NOT_FOUND; - } - - ResourceSource *src = getVolume(map, 0); - - if (!src) - return SCI_ERROR_NO_RESOURCE_FILES_FOUND; - - byte *ptr = mapRes->data; - - if (map->volume_number == 65535) { - // Heuristic to detect late SCI1.1 map format - if ((mapRes->size >= 6) && (ptr[mapRes->size - 6] != 0xff)) - isEarly = false; - - while (ptr < mapRes->data + mapRes->size) { - uint16 n = READ_LE_UINT16(ptr); - ptr += 2; - - if (n == 0xffff) - break; - - if (isEarly) { - offset = READ_LE_UINT32(ptr); - ptr += 4; - } else { - offset += READ_LE_UINT24(ptr); - ptr += 3; - } - - addResource(ResourceId(kResourceTypeAudio, n), src, offset); - } - } else { - // Heuristic to detect late SCI1.1 map format - if ((mapRes->size >= 11) && (ptr[mapRes->size - 11] == 0xff)) - isEarly = false; - - if (!isEarly) { - offset = READ_LE_UINT32(ptr); - ptr += 4; - } - - while (ptr < mapRes->data + mapRes->size) { - uint32 n = READ_BE_UINT32(ptr); - int syncSize = 0; - ptr += 4; - - if (n == 0xffffffff) - break; - - if (isEarly) { - offset = READ_LE_UINT32(ptr); - ptr += 4; - } else { - offset += READ_LE_UINT24(ptr); - ptr += 3; - } - - if (isEarly || (n & 0x80)) { - syncSize = READ_LE_UINT16(ptr); - ptr += 2; - - if (syncSize > 0) - addResource(ResourceId(kResourceTypeSync36, map->volume_number, n & 0xffffff3f), src, offset, syncSize); - } - - if (n & 0x40) { - syncSize += READ_LE_UINT16(ptr); - ptr += 2; - } - - addResource(ResourceId(kResourceTypeAudio36, map->volume_number, n & 0xffffff3f), src, offset + syncSize); - } - } - - return 0; -} - -// AUDIOnnn.MAP contains 10-byte entries: -// Early format: -// w 5 bits resource type and 11 bits resource number -// dw 7 bits volume number and 25 bits offset -// dw size -// Later format: -// w nEntry -// dw offset+volume (as in resource.map) -// dw size -// ending with 10 0xFFs -int ResourceManager::readAudioMapSCI1(ResourceSource *map, bool unload) { - Common::File file; - - if (!file.open(map->location_name)) - return SCI_ERROR_RESMAP_NOT_FOUND; - - bool oldFormat = (file.readUint16LE() >> 11) == kResourceTypeAudio; - file.seek(0); - - while (1) { - uint16 n = file.readUint16LE(); - uint32 offset = file.readUint32LE(); - uint32 size = file.readUint32LE(); - - if (file.eos() || file.err()) { - warning("Error while reading %s", map->location_name.c_str()); - return SCI_ERROR_RESMAP_NOT_FOUND; - } - - if (n == 0xffff) - break; - - byte volume_nr; - - if (oldFormat) { - n &= 0x07ff; // Mask out resource type - volume_nr = offset >> 25; // most significant 7 bits - offset &= 0x01ffffff; // least significant 25 bits - } else { - volume_nr = offset >> 28; // most significant 4 bits - offset &= 0x0fffffff; // least significant 28 bits - } - - ResourceSource *src = getVolume(map, volume_nr); - - if (src) { - if (unload) - removeAudioResource(ResourceId(kResourceTypeAudio, n)); - else - addResource(ResourceId(kResourceTypeAudio, n), src, offset, size); - } else { - warning("Failed to find audio volume %i", volume_nr); - } - } - - return 0; -} - -void ResourceManager::setAudioLanguage(int language) { - if (_audioMapSCI1) { - if (_audioMapSCI1->volume_number == language) { - // This language is already loaded - return; - } - - // We already have a map loaded, so we unload it first - readAudioMapSCI1(_audioMapSCI1, true); - - // Remove all volumes that use this map from the source list - Common::List<ResourceSource *>::iterator it = _sources.begin(); - while (it != _sources.end()) { - ResourceSource *src = *it; - if (src->associated_map == _audioMapSCI1) { - it = _sources.erase(it); - delete src; - } else { - ++it; - } - } - - // Remove the map itself from the source list - _sources.remove(_audioMapSCI1); - delete _audioMapSCI1; - - _audioMapSCI1 = NULL; - } - - char filename[9]; - snprintf(filename, 9, "AUDIO%03d", language); - - Common::String fullname = Common::String(filename) + ".MAP"; - if (!Common::File::exists(fullname)) { - warning("No audio map found for language %i", language); - return; - } - - _audioMapSCI1 = addSource(NULL, kSourceExtAudioMap, fullname.c_str(), language); - - // Search for audio volumes for this language and add them to the source list - Common::ArchiveMemberList files; - SearchMan.listMatchingMembers(files, Common::String(filename) + ".0??"); - for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) { - const Common::String name = (*x)->getName(); - const char *dot = strrchr(name.c_str(), '.'); - int number = atoi(dot + 1); - - addSource(_audioMapSCI1, kSourceAudioVolume, name.c_str(), number); - } - - scanNewSources(); -} - -int ResourceManager::getAudioLanguage() const { - return (_audioMapSCI1 ? _audioMapSCI1->volume_number : 0); -} - int ResourceManager::readResourceInfo(Resource *res, Common::SeekableReadStream *file, uint32&szPacked, ResourceCompression &compression) { // SCI0 volume format: {wResId wPacked+4 wUnpacked wCompression} = 8 bytes @@ -1954,7 +1644,7 @@ ResourceCompression ResourceManager::getViewCompression() { // Test 10 views to see if any are compressed for (int i = 0; i < 1000; i++) { - Common::File *file; + Common::SeekableReadStream *fileStream = 0; Resource *res = testResource(ResourceId(kResourceTypeView, i)); if (!res) @@ -1963,16 +1653,23 @@ ResourceCompression ResourceManager::getViewCompression() { if (res->_source->source_type != kSourceVolume) continue; - file = getVolumeFile(res->_source->location_name.c_str()); - if (!file) + fileStream = getVolumeFile(res->_source); + + if (!fileStream) continue; - file->seek(res->_fileOffset, SEEK_SET); + fileStream->seek(res->_fileOffset, SEEK_SET); uint32 szPacked; ResourceCompression compression; - if (readResourceInfo(res, file, szPacked, compression)) + if (readResourceInfo(res, fileStream, szPacked, compression)) { + if (res->_source->resourceFile) + delete fileStream; continue; + } + + if (res->_source->resourceFile) + delete fileStream; if (compression != kCompNone) return compression; @@ -2329,248 +2026,98 @@ bool ResourceManager::hasSci1Voc900() { return offset == res->size; } -SoundResource::SoundResource(uint32 resNumber, ResourceManager *resMan, SciVersion soundVersion) : _resMan(resMan), _soundVersion(soundVersion) { - Resource *resource = _resMan->findResource(ResourceId(kResourceTypeSound, resNumber), true); - int trackNr, channelNr; - if (!resource) - return; - - _innerResource = resource; - - byte *data, *data2; - byte *dataEnd; - Channel *channel, *sampleChannel; - - switch (_soundVersion) { - case SCI_VERSION_0_EARLY: - case SCI_VERSION_0_LATE: - // SCI0 only has a header of 0x11/0x21 byte length and the actual midi track follows afterwards - _trackCount = 1; - _tracks = new Track[_trackCount]; - _tracks->digitalChannelNr = -1; - _tracks->type = 0; // Not used for SCI0 - _tracks->channelCount = 1; - // Digital sample data included? -> Add an additional channel - if (resource->data[0] == 2) - _tracks->channelCount++; - _tracks->channels = new Channel[_tracks->channelCount]; - memset(_tracks->channels, 0, sizeof(Channel) * _tracks->channelCount); - channel = &_tracks->channels[0]; - if (_soundVersion == SCI_VERSION_0_EARLY) { - channel->data = resource->data + 0x11; - channel->size = resource->size - 0x11; - } else { - channel->data = resource->data + 0x21; - channel->size = resource->size - 0x21; - } - if (_tracks->channelCount == 2) { - // Digital sample data included - _tracks->digitalChannelNr = 1; - sampleChannel = &_tracks->channels[1]; - // we need to find 0xFC (channel terminator) within the data - data = channel->data; - dataEnd = channel->data + channel->size; - while ((data < dataEnd) && (*data != 0xfc)) - data++; - // Skip any following 0xFCs as well - while ((data < dataEnd) && (*data == 0xfc)) - data++; - // Now adjust channels accordingly - sampleChannel->data = data; - sampleChannel->size = channel->size - (data - channel->data); - channel->size = data - channel->data; - // Read sample header information - //Offset 14 in the header contains the frequency as a short integer. Offset 32 contains the sample length, also as a short integer. - _tracks->digitalSampleRate = READ_LE_UINT16(sampleChannel->data + 14); - _tracks->digitalSampleSize = READ_LE_UINT16(sampleChannel->data + 32); - _tracks->digitalSampleStart = 0; - _tracks->digitalSampleEnd = 0; - sampleChannel->data += 44; // Skip over header - sampleChannel->size -= 44; - } - break; - - case SCI_VERSION_1_EARLY: - case SCI_VERSION_1_LATE: - data = resource->data; - // Count # of tracks - _trackCount = 0; - while ((*data++) != 0xFF) { - _trackCount++; - while (*data != 0xFF) - data += 6; - data++; - } - _tracks = new Track[_trackCount]; - data = resource->data; - for (trackNr = 0; trackNr < _trackCount; trackNr++) { - // Track info starts with track type:BYTE - // Then the channel information gets appended Unknown:WORD, ChannelOffset:WORD, ChannelSize:WORD - // 0xFF:BYTE as terminator to end that track and begin with another track type - // Track type 0xFF is the marker signifying the end of the tracks - - _tracks[trackNr].type = *data++; - // Counting # of channels used - data2 = data; - _tracks[trackNr].channelCount = 0; - while (*data2 != 0xFF) { - data2 += 6; - _tracks[trackNr].channelCount++; - } - _tracks[trackNr].channels = new Channel[_tracks[trackNr].channelCount]; - _tracks[trackNr].digitalChannelNr = -1; // No digital sound associated - _tracks[trackNr].digitalSampleRate = 0; - _tracks[trackNr].digitalSampleSize = 0; - _tracks[trackNr].digitalSampleStart = 0; - _tracks[trackNr].digitalSampleEnd = 0; - if (_tracks[trackNr].type != 0xF0) { // Digital track marker - not supported currently - for (channelNr = 0; channelNr < _tracks[trackNr].channelCount; channelNr++) { - channel = &_tracks[trackNr].channels[channelNr]; - channel->prio = READ_LE_UINT16(data); - channel->data = resource->data + READ_LE_UINT16(data + 2) + 2; - channel->size = READ_LE_UINT16(data + 4) - 2; // Not counting channel header - channel->number = *(channel->data - 2); - channel->poly = *(channel->data - 1); - channel->time = channel->prev = 0; - if (channel->number == 0xFE) { // Digital channel - _tracks[trackNr].digitalChannelNr = channelNr; - _tracks[trackNr].digitalSampleRate = READ_LE_UINT16(channel->data); - _tracks[trackNr].digitalSampleSize = READ_LE_UINT16(channel->data + 2); - _tracks[trackNr].digitalSampleStart = READ_LE_UINT16(channel->data + 4); - _tracks[trackNr].digitalSampleEnd = READ_LE_UINT16(channel->data + 6); - channel->data += 8; // Skip over header - channel->size -= 8; - } - data += 6; - } - } else { - // Skip over digital track - data += 6; - } - data++; // Skipping 0xFF that closes channels list - } - break; - - default: - error("SoundResource: SCI version %d is unsupported", _soundVersion); - } -} - -SoundResource::~SoundResource() { - for (int trackNr = 0; trackNr < _trackCount; trackNr++) - delete[] _tracks[trackNr].channels; - delete[] _tracks; +// Same function as Script::findBlock(). Slight code +// duplication here, but this has been done to keep the resource +// manager independent from the rest of the engine +static byte *findSci0ExportsBlock(byte *buffer) { + byte *buf = buffer; + bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); - _resMan->unlockResource(_innerResource); -} + if (oldScriptHeader) + buf += 2; -#if 0 -SoundResource::Track* SoundResource::getTrackByNumber(uint16 number) { - if (_soundVersion <= SCI_VERSION_0_LATE) - return &_tracks[0]; - - if (/*number >= 0 &&*/number < _trackCount) - return &_tracks[number]; - return NULL; -} -#endif + do { + int seekerType = READ_LE_UINT16(buf); -SoundResource::Track *SoundResource::getTrackByType(byte type) { - if (_soundVersion <= SCI_VERSION_0_LATE) - return &_tracks[0]; + if (seekerType == 0) + break; + if (seekerType == 7) // exports + return buf; - for (int trackNr = 0; trackNr < _trackCount; trackNr++) { - if (_tracks[trackNr].type == type) - return &_tracks[trackNr]; - } - return NULL; -} + int seekerSize = READ_LE_UINT16(buf + 2); + assert(seekerSize > 0); + buf += seekerSize; + } while (1); -SoundResource::Track *SoundResource::getDigitalTrack() { - for (int trackNr = 0; trackNr < _trackCount; trackNr++) { - if (_tracks[trackNr].digitalChannelNr != -1) - return &_tracks[trackNr]; - } return NULL; } -// Gets the filter mask for SCI0 sound resources -int SoundResource::getChannelFilterMask(int hardwareMask, bool wantsRhythm) { - byte *data = _innerResource->data; - int channelMask = 0; - - if (_soundVersion > SCI_VERSION_0_LATE) - return 0; +reg_t ResourceManager::findGameObject(bool addSci11ScriptOffset) { + Resource *script = findResource(ResourceId(kResourceTypeScript, 0), false); - data++; // Skip over digital sample flag + if (!script) + return NULL_REG; - for (int channelNr = 0; channelNr < 16; channelNr++) { - channelMask = channelMask >> 1; + byte *offsetPtr = 0; - byte flags; + if (getSciVersion() < SCI_VERSION_1_1) { + byte *buf = (getSciVersion() == SCI_VERSION_0_EARLY) ? script->data + 2 : script->data; - if (_soundVersion == SCI_VERSION_0_EARLY) { - // Each channel is specified by a single byte - // Upper 4 bits of the byte is a voices count - // Lower 4 bits -> bit 0 set: use for AdLib - // bit 1 set: use for PCjr - // bit 2 set: use for PC speaker - // bit 3 set and bit 0 clear: control channel (15) - // bit 3 set and bit 0 set: rhythm channel (9) - // Note: control channel is dynamically assigned inside the drivers, - // but seems to be fixed at 15 in the song data. - flags = *data++; - - // Get device bits - flags &= 0x7; + // Check if the first block is the exports block (in most cases, it is) + bool exportsIsFirst = (READ_LE_UINT16(buf + 4) == 7); + if (exportsIsFirst) { + offsetPtr = buf + 4 + 2; } else { - // Each channel is specified by 2 bytes - // 1st byte is voices count - // 2nd byte is play mask, which specifies if the channel is supposed to be played - // by the corresponding hardware - - // Skip voice count - data++; - - flags = *data++; + offsetPtr = findSci0ExportsBlock(script->data); + if (!offsetPtr) + error("Unable to find exports block from script 0"); + offsetPtr += 4 + 2; } + } else { + offsetPtr = script->data + 4 + 2 + 2; + } + + int16 offset = !isSci11Mac() ? READ_LE_UINT16(offsetPtr) : READ_BE_UINT16(offsetPtr); - bool play; - switch (channelNr) { - case 15: - // Always play control channel - play = true; - break; - case 9: - // Play rhythm channel when requested - play = wantsRhythm; - break; - default: - // Otherwise check for flag - play = flags & hardwareMask; - } + // In SCI1.1 and newer, the heap is appended at the end of the script, + // so adjust the offset accordingly + if (getSciVersion() >= SCI_VERSION_1_1 && addSci11ScriptOffset) { + offset += script->size; - if (play) { - // This Channel is supposed to be played by the hardware - channelMask |= 0x8000; - } + // Ensure that the start of the heap is word-aligned - same as in Script::init() + if (script->size & 2) + offset++; } - return channelMask; + return make_reg(1, offset); } -byte SoundResource::getInitialVoiceCount(byte channel) { - byte *data = _innerResource->data; +Common::String ResourceManager::findSierraGameId() { + // In SCI0-SCI1, the heap is embedded in the script. In SCI1.1+, it's separated + Resource *heap = 0; + int nameSelector = 3; - if (_soundVersion > SCI_VERSION_0_LATE) - return 0; // TODO + if (getSciVersion() < SCI_VERSION_1_1) { + heap = findResource(ResourceId(kResourceTypeScript, 0), false); + } else { + heap = findResource(ResourceId(kResourceTypeHeap, 0), false); + nameSelector += 5; + } - data++; // Skip over digital sample flag + if (!heap) + return ""; - if (_soundVersion == SCI_VERSION_0_EARLY) - return data[channel] >> 4; - else - return data[channel * 2]; + int16 gameObjectOffset = findGameObject(false).offset; + + if (!gameObjectOffset) + return ""; + + // Seek to the name selector of the first export + byte *seeker = heap->data + READ_UINT16(heap->data + gameObjectOffset + nameSelector * 2); + Common::String sierraId; + sierraId += (const char *)seeker; + + return sierraId; } } // End of namespace Sci diff --git a/engines/sci/resource.h b/engines/sci/resource.h index 48b5f095b1..43e61eaadb 100644 --- a/engines/sci/resource.h +++ b/engines/sci/resource.h @@ -26,8 +26,9 @@ #ifndef SCI_SCICORE_RESOURCE_H #define SCI_SCICORE_RESOURCE_H -#include "common/str.h" #include "common/fs.h" +#include "common/macresman.h" +#include "common/str.h" #include "sci/graphics/helpers.h" // for ViewType #include "sci/decompressor.h" @@ -108,14 +109,31 @@ enum ResourceType { kResourceTypeUnknown1, // Translation, currently unsupported kResourceTypeUnknown2, kResourceTypeRobot, - kResourceTypeInvalid + kResourceTypeInvalid, + + // Mac-only resources, these resource types are self-defined + // Numbers subject to change + kResourceTypeMacIconBarPictN = -1, // IBIN resources (icon bar, not selected) + kResourceTypeMacIconBarPictS = -2, // IBIS resources (icon bar, selected) + kResourceTypeMacPict = -3 // PICT resources (inventory) }; const char *getResourceTypeName(ResourceType restype); class ResourceManager; -struct ResourceSource; + +struct ResourceSource { + ResSourceType source_type; + bool scanned; + Common::String location_name; // FIXME: Replace by FSNode ? + const Common::FSNode *resourceFile; + int volume_number; + ResourceSource *associated_map; + uint32 audioCompressionType; + int32 *audioCompressionOffsetMapping; + Common::MacResManager macResMan; +}; class ResourceId { public: @@ -127,7 +145,7 @@ public: ResourceId(ResourceType type_, uint16 number_, uint32 tuple_ = 0) : type(type_), number(number_), tuple(tuple_) { - if ((type < kResourceTypeView) || (type > kResourceTypeInvalid)) + if (type < kResourceTypeMacPict || type > kResourceTypeInvalid) type = kResourceTypeInvalid; } @@ -273,6 +291,19 @@ public: // Detects, if standard font of current game includes extended characters (>0x80) bool detectFontExtended(); + /** + * Finds the internal Sierra ID of the current game from script 0 + */ + Common::String findSierraGameId(); + + /** + * Finds the location of the game object from script 0 + * @param addSci11ScriptOffset: Adjust the return value for SCI1.1 and newer + * games. Needs to be false when the heap is accessed directly inside + * findSierraGameId(). + */ + reg_t findGameObject(bool addSci11ScriptOffset = true); + protected: // Maximum number of bytes to allow being allocated for resources // Note: maxMemory will not be interpreted as a hard limit, only as a restriction @@ -290,8 +321,8 @@ protected: ResourceMap _resMap; Common::List<Common::File *> _volumeFiles; ///< list of opened volume files ResourceSource *_audioMapSCI1; ///< Currently loaded audio map for SCI1 - ResVersion _volVersion; ///< RESOURCE.0xx version - ResVersion _mapVersion; ///< RESOURCE.MAP version + ResVersion _volVersion; ///< resource.0xx version + ResVersion _mapVersion; ///< resource.map version /** * Initializes the resource manager @@ -327,7 +358,7 @@ protected: */ ResourceSource *addExternalMap(const char *file_name, int volume_nr = 0); - ResourceSource *addExternalMap(const Common::FSNode *mapFile); + ResourceSource *addExternalMap(const Common::FSNode *mapFile, int volume_nr = 0); /** * Add an internal (i.e., resource) map to the resource manager's list of sources. @@ -362,13 +393,13 @@ protected: */ const char *versionDescription(ResVersion version) const; - Common::File *getVolumeFile(const char *filename); + Common::SeekableReadStream *getVolumeFile(ResourceSource *source); void loadResource(Resource *res); - bool loadPatch(Resource *res, Common::File &file); + bool loadPatch(Resource *res, Common::SeekableReadStream *file); bool loadFromPatchFile(Resource *res); - bool loadFromWaveFile(Resource *res, Common::File &file); - bool loadFromAudioVolumeSCI1(Resource *res, Common::File &file); - bool loadFromAudioVolumeSCI11(Resource *res, Common::File &file); + bool loadFromWaveFile(Resource *res, Common::SeekableReadStream *file); + bool loadFromAudioVolumeSCI1(Resource *res, Common::SeekableReadStream *file); + bool loadFromAudioVolumeSCI11(Resource *res, Common::SeekableReadStream *file); void freeOldResources(); int decompress(Resource *res, Common::SeekableReadStream *file); int readResourceInfo(Resource *res, Common::SeekableReadStream *file, uint32&szPacked, ResourceCompression &compression); @@ -421,7 +452,10 @@ protected: * Reads patch files from a local directory. */ void readResourcePatches(ResourceSource *source); - void processPatch(ResourceSource *source, ResourceType restype, int resnumber); +#ifdef ENABLE_SCI32 + void readResourcePatchesBase36(ResourceSource *source); +#endif + void processPatch(ResourceSource *source, ResourceType restype, uint16 resnumber, uint32 tuple = 0); /** * Process wave files as patches for Audio resources @@ -481,6 +515,7 @@ public: Track *getDigitalTrack(); int getChannelFilterMask(int hardwareMask, bool wantsRhythm); byte getInitialVoiceCount(byte channel); + bool isChannelUsed(byte channel) const { return _channelsUsed & (1 << channel); } private: SciVersion _soundVersion; @@ -488,6 +523,9 @@ private: Track *_tracks; Resource *_innerResource; ResourceManager *_resMan; + uint16 _channelsUsed; + + void setChannelUsed(byte channel) { _channelsUsed |= (1 << channel); } }; } // End of namespace Sci diff --git a/engines/sci/resource_audio.cpp b/engines/sci/resource_audio.cpp new file mode 100644 index 0000000000..57efbdcb38 --- /dev/null +++ b/engines/sci/resource_audio.cpp @@ -0,0 +1,711 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +// Resource library + +#include "common/file.h" + +#include "sci/resource.h" +#include "sci/util.h" + +namespace Sci { + +void ResourceManager::checkIfAudioVolumeIsCompressed(ResourceSource *source) { + Common::SeekableReadStream *fileStream = getVolumeFile(source); + + if (!fileStream) { + warning("Failed to open %s", source->location_name.c_str()); + return; + } + + fileStream->seek(0, SEEK_SET); + uint32 compressionType = fileStream->readUint32BE(); + switch (compressionType) { + case MKID_BE('MP3 '): + case MKID_BE('OGG '): + case MKID_BE('FLAC'): + // Detected a compressed audio volume + source->audioCompressionType = compressionType; + // Now read the whole offset mapping table for later usage + int32 recordCount = fileStream->readUint32LE(); + if (!recordCount) + error("compressed audio volume doesn't contain any entries!"); + int32 *offsetMapping = new int32[(recordCount + 1) * 2]; + source->audioCompressionOffsetMapping = offsetMapping; + for (int recordNo = 0; recordNo < recordCount; recordNo++) { + *offsetMapping++ = fileStream->readUint32LE(); + *offsetMapping++ = fileStream->readUint32LE(); + } + // Put ending zero + *offsetMapping++ = 0; + *offsetMapping++ = fileStream->size(); + } + + if (source->resourceFile) + delete fileStream; +} + +bool ResourceManager::loadFromWaveFile(Resource *res, Common::SeekableReadStream *file) { + res->data = new byte[res->size]; + + uint32 really_read = file->read(res->data, res->size); + if (really_read != res->size) + error("Read %d bytes from %s but expected %d", really_read, res->_id.toString().c_str(), res->size); + + res->_status = kResStatusAllocated; + return true; +} + +bool ResourceManager::loadFromAudioVolumeSCI11(Resource *res, Common::SeekableReadStream *file) { + // Check for WAVE files here + uint32 riffTag = file->readUint32BE(); + if (riffTag == MKID_BE('RIFF')) { + res->_headerSize = 0; + res->size = file->readUint32LE(); + file->seek(-8, SEEK_CUR); + return loadFromWaveFile(res, file); + } + file->seek(-4, SEEK_CUR); + + ResourceType type = (ResourceType)(file->readByte() & 0x7f); + if (((res->_id.type == kResourceTypeAudio || res->_id.type == kResourceTypeAudio36) && (type != kResourceTypeAudio)) + || ((res->_id.type == kResourceTypeSync || res->_id.type == kResourceTypeSync36) && (type != kResourceTypeSync))) { + warning("Resource type mismatch loading %s", res->_id.toString().c_str()); + res->unalloc(); + return false; + } + + res->_headerSize = file->readByte(); + + if (type == kResourceTypeAudio) { + if (res->_headerSize != 11 && res->_headerSize != 12) { + warning("Unsupported audio header"); + res->unalloc(); + return false; + } + + // Load sample size + file->seek(7, SEEK_CUR); + res->size = file->readUint32LE(); + // Adjust offset to point at the header data again + file->seek(-11, SEEK_CUR); + } + + return loadPatch(res, file); +} + +bool ResourceManager::loadFromAudioVolumeSCI1(Resource *res, Common::SeekableReadStream *file) { + res->data = new byte[res->size]; + + if (res->data == NULL) { + error("Can't allocate %d bytes needed for loading %s", res->size, res->_id.toString().c_str()); + } + + unsigned int really_read = file->read(res->data, res->size); + if (really_read != res->size) + warning("Read %d bytes from %s but expected %d", really_read, res->_id.toString().c_str(), res->size); + + res->_status = kResStatusAllocated; + return true; +} + +void ResourceManager::addNewGMPatch(const Common::String &gameId) { + Common::String gmPatchFile; + + if (gameId == "ecoquest") + gmPatchFile = "ECO1GM.PAT"; + else if (gameId == "hoyle3") + gmPatchFile = "HOY3GM.PAT"; + else if (gameId == "hoyle3") + gmPatchFile = "HOY3GM.PAT"; + else if (gameId == "lsl1sci") + gmPatchFile = "LL1_GM.PAT"; + else if (gameId == "lsl5") + gmPatchFile = "LL5_GM.PAT"; + else if (gameId == "longbow") + gmPatchFile = "ROBNGM.PAT"; + else if (gameId == "sq1sci") + gmPatchFile = "SQ1_GM.PAT"; + else if (gameId == "sq4") + gmPatchFile = "SQ4_GM.PAT"; + else if (gameId == "fairytales") + gmPatchFile = "TALEGM.PAT"; + + if (!gmPatchFile.empty() && Common::File::exists(gmPatchFile)) { + ResourceSource *psrcPatch = new ResourceSource; + psrcPatch->source_type = kSourcePatch; + psrcPatch->resourceFile = 0; + psrcPatch->location_name = gmPatchFile; + processPatch(psrcPatch, kResourceTypePatch, 4); + } +} + +void ResourceManager::readWaveAudioPatches() { + // Here we do check for SCI1.1+ so we can patch wav files in as audio resources + Common::ArchiveMemberList files; + SearchMan.listMatchingMembers(files, "*.wav"); + + for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) { + Common::String name = (*x)->getName(); + + if (isdigit(name[0])) { + int number = atoi(name.c_str()); + ResourceSource *psrcPatch = new ResourceSource; + psrcPatch->source_type = kSourceWave; + psrcPatch->resourceFile = 0; + psrcPatch->location_name = name; + psrcPatch->volume_number = 0; + psrcPatch->audioCompressionType = 0; + + ResourceId resId = ResourceId(kResourceTypeAudio, number); + + Resource *newrsc = NULL; + + // Prepare destination, if neccessary + if (_resMap.contains(resId) == false) { + newrsc = new Resource; + _resMap.setVal(resId, newrsc); + } else + newrsc = _resMap.getVal(resId); + + // Get the size of the file + Common::SeekableReadStream *stream = (*x)->createReadStream(); + uint32 fileSize = stream->size(); + delete stream; + + // Overwrite everything, because we're patching + newrsc->_id = resId; + newrsc->_status = kResStatusNoMalloc; + newrsc->_source = psrcPatch; + newrsc->size = fileSize; + newrsc->_headerSize = 0; + debugC(1, kDebugLevelResMan, "Patching %s - OK", psrcPatch->location_name.c_str()); + } + } +} + +void ResourceManager::removeAudioResource(ResourceId resId) { + // Remove resource, unless it was loaded from a patch + if (_resMap.contains(resId)) { + Resource *res = _resMap.getVal(resId); + + if (res->_source->source_type == kSourceAudioVolume) { + if (res->_status == kResStatusLocked) { + warning("Failed to remove resource %s (still in use)", resId.toString().c_str()); + } else { + if (res->_status == kResStatusEnqueued) + removeFromLRU(res); + + _resMap.erase(resId); + delete res; + } + } + } +} + +// Early SCI1.1 65535.MAP structure (uses RESOURCE.AUD): +// ========= +// 6-byte entries: +// w nEntry +// dw offset + +// Late SCI1.1 65535.MAP structure (uses RESOURCE.SFX): +// ========= +// 5-byte entries: +// w nEntry +// tb offset (cumulative) + +// Early SCI1.1 MAP structure: +// =============== +// 10-byte entries: +// b noun +// b verb +// b cond +// b seq +// dw offset +// w syncSize + syncAscSize + +// Late SCI1.1 MAP structure: +// =============== +// Header: +// dw baseOffset +// Followed by 7 or 11-byte entries: +// b noun +// b verb +// b cond +// b seq +// tb cOffset (cumulative offset) +// w syncSize (iff seq has bit 7 set) +// w syncAscSize (iff seq has bit 6 set) + +int ResourceManager::readAudioMapSCI11(ResourceSource *map) { + bool isEarly = true; + uint32 offset = 0; + Resource *mapRes = findResource(ResourceId(kResourceTypeMap, map->volume_number), false); + + if (!mapRes) { + warning("Failed to open %i.MAP", map->volume_number); + return SCI_ERROR_RESMAP_NOT_FOUND; + } + + ResourceSource *src = getVolume(map, 0); + + if (!src) + return SCI_ERROR_NO_RESOURCE_FILES_FOUND; + + byte *ptr = mapRes->data; + + if (map->volume_number == 65535) { + // Heuristic to detect late SCI1.1 map format + if ((mapRes->size >= 6) && (ptr[mapRes->size - 6] != 0xff)) + isEarly = false; + + while (ptr < mapRes->data + mapRes->size) { + uint16 n = READ_LE_UINT16(ptr); + ptr += 2; + + if (n == 0xffff) + break; + + if (isEarly) { + offset = READ_LE_UINT32(ptr); + ptr += 4; + } else { + offset += READ_LE_UINT24(ptr); + ptr += 3; + } + + addResource(ResourceId(kResourceTypeAudio, n), src, offset); + } + } else { + // Heuristic to detect late SCI1.1 map format + if ((mapRes->size >= 11) && (ptr[mapRes->size - 11] == 0xff)) + isEarly = false; + + if (!isEarly) { + offset = READ_LE_UINT32(ptr); + ptr += 4; + } + + while (ptr < mapRes->data + mapRes->size) { + uint32 n = READ_BE_UINT32(ptr); + int syncSize = 0; + ptr += 4; + + if (n == 0xffffffff) + break; + + if (isEarly) { + offset = READ_LE_UINT32(ptr); + ptr += 4; + } else { + offset += READ_LE_UINT24(ptr); + ptr += 3; + } + + if (isEarly || (n & 0x80)) { + syncSize = READ_LE_UINT16(ptr); + ptr += 2; + + if (syncSize > 0) + addResource(ResourceId(kResourceTypeSync36, map->volume_number, n & 0xffffff3f), src, offset, syncSize); + } + + if (n & 0x40) { + syncSize += READ_LE_UINT16(ptr); + ptr += 2; + } + + addResource(ResourceId(kResourceTypeAudio36, map->volume_number, n & 0xffffff3f), src, offset + syncSize); + } + } + + return 0; +} + +// AUDIOnnn.MAP contains 10-byte entries: +// Early format: +// w 5 bits resource type and 11 bits resource number +// dw 7 bits volume number and 25 bits offset +// dw size +// Later format: +// w nEntry +// dw offset+volume (as in resource.map) +// dw size +// ending with 10 0xFFs +int ResourceManager::readAudioMapSCI1(ResourceSource *map, bool unload) { + Common::File file; + + if (!file.open(map->location_name)) + return SCI_ERROR_RESMAP_NOT_FOUND; + + bool oldFormat = (file.readUint16LE() >> 11) == kResourceTypeAudio; + file.seek(0); + + while (1) { + uint16 n = file.readUint16LE(); + uint32 offset = file.readUint32LE(); + uint32 size = file.readUint32LE(); + + if (file.eos() || file.err()) { + warning("Error while reading %s", map->location_name.c_str()); + return SCI_ERROR_RESMAP_NOT_FOUND; + } + + if (n == 0xffff) + break; + + byte volume_nr; + + if (oldFormat) { + n &= 0x07ff; // Mask out resource type + volume_nr = offset >> 25; // most significant 7 bits + offset &= 0x01ffffff; // least significant 25 bits + } else { + volume_nr = offset >> 28; // most significant 4 bits + offset &= 0x0fffffff; // least significant 28 bits + } + + ResourceSource *src = getVolume(map, volume_nr); + + if (src) { + if (unload) + removeAudioResource(ResourceId(kResourceTypeAudio, n)); + else + addResource(ResourceId(kResourceTypeAudio, n), src, offset, size); + } else { + warning("Failed to find audio volume %i", volume_nr); + } + } + + return 0; +} + +void ResourceManager::setAudioLanguage(int language) { + if (_audioMapSCI1) { + if (_audioMapSCI1->volume_number == language) { + // This language is already loaded + return; + } + + // We already have a map loaded, so we unload it first + readAudioMapSCI1(_audioMapSCI1, true); + + // Remove all volumes that use this map from the source list + Common::List<ResourceSource *>::iterator it = _sources.begin(); + while (it != _sources.end()) { + ResourceSource *src = *it; + if (src->associated_map == _audioMapSCI1) { + it = _sources.erase(it); + delete src; + } else { + ++it; + } + } + + // Remove the map itself from the source list + _sources.remove(_audioMapSCI1); + delete _audioMapSCI1; + + _audioMapSCI1 = NULL; + } + + char filename[9]; + snprintf(filename, 9, "AUDIO%03d", language); + + Common::String fullname = Common::String(filename) + ".MAP"; + if (!Common::File::exists(fullname)) { + warning("No audio map found for language %i", language); + return; + } + + _audioMapSCI1 = addSource(NULL, kSourceExtAudioMap, fullname.c_str(), language); + + // Search for audio volumes for this language and add them to the source list + Common::ArchiveMemberList files; + SearchMan.listMatchingMembers(files, Common::String(filename) + ".0??"); + for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) { + const Common::String name = (*x)->getName(); + const char *dot = strrchr(name.c_str(), '.'); + int number = atoi(dot + 1); + + addSource(_audioMapSCI1, kSourceAudioVolume, name.c_str(), number); + } + + scanNewSources(); +} + +int ResourceManager::getAudioLanguage() const { + return (_audioMapSCI1 ? _audioMapSCI1->volume_number : 0); +} + +SoundResource::SoundResource(uint32 resNumber, ResourceManager *resMan, SciVersion soundVersion) : _resMan(resMan), _soundVersion(soundVersion) { + Resource *resource = _resMan->findResource(ResourceId(kResourceTypeSound, resNumber), true); + int trackNr, channelNr; + if (!resource) + return; + + _innerResource = resource; + + byte *data, *data2; + byte *dataEnd; + Channel *channel, *sampleChannel; + + _channelsUsed = 0; + + switch (_soundVersion) { + case SCI_VERSION_0_EARLY: + case SCI_VERSION_0_LATE: + // SCI0 only has a header of 0x11/0x21 byte length and the actual midi track follows afterwards + _trackCount = 1; + _tracks = new Track[_trackCount]; + _tracks->digitalChannelNr = -1; + _tracks->type = 0; // Not used for SCI0 + _tracks->channelCount = 1; + // Digital sample data included? -> Add an additional channel + if (resource->data[0] == 2) + _tracks->channelCount++; + _tracks->channels = new Channel[_tracks->channelCount]; + memset(_tracks->channels, 0, sizeof(Channel) * _tracks->channelCount); + channel = &_tracks->channels[0]; + if (_soundVersion == SCI_VERSION_0_EARLY) { + channel->data = resource->data + 0x11; + channel->size = resource->size - 0x11; + } else { + channel->data = resource->data + 0x21; + channel->size = resource->size - 0x21; + } + if (_tracks->channelCount == 2) { + // Digital sample data included + _tracks->digitalChannelNr = 1; + sampleChannel = &_tracks->channels[1]; + // we need to find 0xFC (channel terminator) within the data + data = channel->data; + dataEnd = channel->data + channel->size; + while ((data < dataEnd) && (*data != 0xfc)) + data++; + // Skip any following 0xFCs as well + while ((data < dataEnd) && (*data == 0xfc)) + data++; + // Now adjust channels accordingly + sampleChannel->data = data; + sampleChannel->size = channel->size - (data - channel->data); + channel->size = data - channel->data; + // Read sample header information + //Offset 14 in the header contains the frequency as a short integer. Offset 32 contains the sample length, also as a short integer. + _tracks->digitalSampleRate = READ_LE_UINT16(sampleChannel->data + 14); + _tracks->digitalSampleSize = READ_LE_UINT16(sampleChannel->data + 32); + _tracks->digitalSampleStart = 0; + _tracks->digitalSampleEnd = 0; + sampleChannel->data += 44; // Skip over header + sampleChannel->size -= 44; + } + break; + + case SCI_VERSION_1_EARLY: + case SCI_VERSION_1_LATE: + data = resource->data; + // Count # of tracks + _trackCount = 0; + while ((*data++) != 0xFF) { + _trackCount++; + while (*data != 0xFF) + data += 6; + data++; + } + _tracks = new Track[_trackCount]; + data = resource->data; + for (trackNr = 0; trackNr < _trackCount; trackNr++) { + // Track info starts with track type:BYTE + // Then the channel information gets appended Unknown:WORD, ChannelOffset:WORD, ChannelSize:WORD + // 0xFF:BYTE as terminator to end that track and begin with another track type + // Track type 0xFF is the marker signifying the end of the tracks + + _tracks[trackNr].type = *data++; + // Counting # of channels used + data2 = data; + _tracks[trackNr].channelCount = 0; + while (*data2 != 0xFF) { + data2 += 6; + _tracks[trackNr].channelCount++; + } + _tracks[trackNr].channels = new Channel[_tracks[trackNr].channelCount]; + _tracks[trackNr].digitalChannelNr = -1; // No digital sound associated + _tracks[trackNr].digitalSampleRate = 0; + _tracks[trackNr].digitalSampleSize = 0; + _tracks[trackNr].digitalSampleStart = 0; + _tracks[trackNr].digitalSampleEnd = 0; + if (_tracks[trackNr].type != 0xF0) { // Digital track marker - not supported currently + for (channelNr = 0; channelNr < _tracks[trackNr].channelCount; channelNr++) { + channel = &_tracks[trackNr].channels[channelNr]; + channel->prio = READ_LE_UINT16(data); + channel->data = resource->data + READ_LE_UINT16(data + 2) + 2; + channel->size = READ_LE_UINT16(data + 4) - 2; // Not counting channel header + channel->number = *(channel->data - 2); + setChannelUsed(channel->number); + channel->poly = *(channel->data - 1); + channel->time = channel->prev = 0; + if (channel->number == 0xFE) { // Digital channel + _tracks[trackNr].digitalChannelNr = channelNr; + _tracks[trackNr].digitalSampleRate = READ_LE_UINT16(channel->data); + _tracks[trackNr].digitalSampleSize = READ_LE_UINT16(channel->data + 2); + _tracks[trackNr].digitalSampleStart = READ_LE_UINT16(channel->data + 4); + _tracks[trackNr].digitalSampleEnd = READ_LE_UINT16(channel->data + 6); + channel->data += 8; // Skip over header + channel->size -= 8; + } + data += 6; + } + } else { + // Skip over digital track + data += 6; + } + data++; // Skipping 0xFF that closes channels list + } + break; + + default: + error("SoundResource: SCI version %d is unsupported", _soundVersion); + } +} + +SoundResource::~SoundResource() { + for (int trackNr = 0; trackNr < _trackCount; trackNr++) + delete[] _tracks[trackNr].channels; + delete[] _tracks; + + _resMan->unlockResource(_innerResource); +} + +#if 0 +SoundResource::Track* SoundResource::getTrackByNumber(uint16 number) { + if (_soundVersion <= SCI_VERSION_0_LATE) + return &_tracks[0]; + + if (/*number >= 0 &&*/number < _trackCount) + return &_tracks[number]; + return NULL; +} +#endif + +SoundResource::Track *SoundResource::getTrackByType(byte type) { + if (_soundVersion <= SCI_VERSION_0_LATE) + return &_tracks[0]; + + for (int trackNr = 0; trackNr < _trackCount; trackNr++) { + if (_tracks[trackNr].type == type) + return &_tracks[trackNr]; + } + return NULL; +} + +SoundResource::Track *SoundResource::getDigitalTrack() { + for (int trackNr = 0; trackNr < _trackCount; trackNr++) { + if (_tracks[trackNr].digitalChannelNr != -1) + return &_tracks[trackNr]; + } + return NULL; +} + +// Gets the filter mask for SCI0 sound resources +int SoundResource::getChannelFilterMask(int hardwareMask, bool wantsRhythm) { + byte *data = _innerResource->data; + int channelMask = 0; + + if (_soundVersion > SCI_VERSION_0_LATE) + return 0; + + data++; // Skip over digital sample flag + + for (int channelNr = 0; channelNr < 16; channelNr++) { + channelMask = channelMask >> 1; + + byte flags; + + if (_soundVersion == SCI_VERSION_0_EARLY) { + // Each channel is specified by a single byte + // Upper 4 bits of the byte is a voices count + // Lower 4 bits -> bit 0 set: use for AdLib + // bit 1 set: use for PCjr + // bit 2 set: use for PC speaker + // bit 3 set and bit 0 clear: control channel (15) + // bit 3 set and bit 0 set: rhythm channel (9) + // Note: control channel is dynamically assigned inside the drivers, + // but seems to be fixed at 15 in the song data. + flags = *data++; + + // Get device bits + flags &= 0x7; + } else { + // Each channel is specified by 2 bytes + // 1st byte is voices count + // 2nd byte is play mask, which specifies if the channel is supposed to be played + // by the corresponding hardware + + // Skip voice count + data++; + + flags = *data++; + } + + bool play; + switch (channelNr) { + case 15: + // Always play control channel + play = true; + break; + case 9: + // Play rhythm channel when requested + play = wantsRhythm; + break; + default: + // Otherwise check for flag + play = flags & hardwareMask; + } + + if (play) { + // This Channel is supposed to be played by the hardware + channelMask |= 0x8000; + } + } + + return channelMask; +} + +byte SoundResource::getInitialVoiceCount(byte channel) { + byte *data = _innerResource->data; + + if (_soundVersion > SCI_VERSION_0_LATE) + return 0; // TODO + + data++; // Skip over digital sample flag + + if (_soundVersion == SCI_VERSION_0_EARLY) + return data[channel] >> 4; + else + return data[channel * 2]; +} + +} // End of namespace Sci diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp index 4862d0579a..929bdf3307 100644 --- a/engines/sci/sci.cpp +++ b/engines/sci/sci.cpp @@ -43,6 +43,7 @@ #include "sci/sound/audio.h" #include "sci/sound/soundcmd.h" #include "sci/graphics/gui.h" +#include "sci/graphics/maciconbar.h" #include "sci/graphics/ports.h" #include "sci/graphics/palette.h" #include "sci/graphics/cursor.h" @@ -55,8 +56,6 @@ namespace Sci { -extern int g_loadFromLauncher; - SciEngine *g_sci = 0; @@ -97,6 +96,7 @@ SciEngine::SciEngine(OSystem *syst, const ADGameDescription *desc) DebugMan.addDebugChannel(kDebugLevelOnStartup, "OnStartup", "Enter debugger at start of game"); _gamestate = 0; + _gfxMacIconBar = 0; const Common::FSNode gameDataDir(ConfMan.get("path")); @@ -124,6 +124,7 @@ SciEngine::~SciEngine() { delete _console; delete _resMan; delete _features; + delete _gfxMacIconBar; g_sci = 0; } @@ -170,6 +171,9 @@ Common::Error SciEngine::run() { else screen = new GfxScreen(_resMan, 320, 200, upscaledHires); + if (_resMan->isSci11Mac() && getSciVersion() == SCI_VERSION_1_1) + _gfxMacIconBar = new GfxMacIconBar(); + GfxPalette *palette = new GfxPalette(_resMan, screen); GfxCache *cache = new GfxCache(_resMan, screen, palette); GfxCursor *cursor = new GfxCursor(_resMan, palette, screen); @@ -184,7 +188,7 @@ Common::Error SciEngine::run() { _features = new GameFeatures(segMan, _kernel); - _gamestate = new EngineState(_vocabulary, segMan); + _gamestate = new EngineState(segMan); _gamestate->_event = new SciEvent(_resMan); @@ -222,16 +226,11 @@ Common::Error SciEngine::run() { } // Add the after market GM patches for the specified game, if they exist - _resMan->addNewGMPatch(_gamestate->_gameId); + _resMan->addNewGMPatch(getGameID()); script_adjust_opcode_formats(_gamestate); _kernel->loadKernelNames(getGameID()); - // Set the savegame dir (actually, we set it to a fake value, - // since we cannot let the game control where saves are stored) - assert(_gamestate->sys_strings->_strings[SYS_STRING_SAVEDIR]._value != 0); - strcpy(_gamestate->sys_strings->_strings[SYS_STRING_SAVEDIR]._value, ""); - SciVersion soundVersion = _features->detectDoSoundType(); _gamestate->_soundCmd = new SoundCommandParser(_resMan, segMan, _kernel, _audio, soundVersion); @@ -258,9 +257,9 @@ Common::Error SciEngine::run() { // Check whether loading a savestate was requested if (ConfMan.hasKey("save_slot")) { - g_loadFromLauncher = ConfMan.getInt("save_slot"); + _gamestate->loadFromLauncher = ConfMan.getInt("save_slot"); } else { - g_loadFromLauncher = -1; + _gamestate->loadFromLauncher = -1; } game_run(&_gamestate); // Run the game diff --git a/engines/sci/sci.h b/engines/sci/sci.h index fdd10bcd04..685f05e685 100644 --- a/engines/sci/sci.h +++ b/engines/sci/sci.h @@ -65,7 +65,7 @@ class GfxPalette; class GfxPorts; class GfxScreen; class SciGui; - +class GfxMacIconBar; #ifdef ENABLE_SCI32 class SciGui32; @@ -206,6 +206,7 @@ public: GfxPorts *_gfxPorts; // Port managment for 16-bit gfx GfxScreen *_gfxScreen; SciGui *_gui; /* Currently active Gui */ + GfxMacIconBar *_gfxMacIconBar; // Mac Icon Bar manager #ifdef ENABLE_SCI32 SciGui32 *_gui32; // GUI for SCI32 games diff --git a/engines/sci/sound/audio.cpp b/engines/sci/sound/audio.cpp index 331561eea4..7748c0505b 100644 --- a/engines/sci/sound/audio.cpp +++ b/engines/sci/sound/audio.cpp @@ -235,6 +235,7 @@ Audio::RewindableAudioStream *AudioPlayer::getAudioStream(uint32 number, uint32 uint32 audioCompressionType = audioRes->getAudioCompressionType(); if (audioCompressionType) { +#if (defined(USE_MAD) || defined(USE_VORBIS) || defined(USE_FLAC)) // Compressed audio made by our tool byte *compressedData = (byte *)malloc(audioRes->size); assert(compressedData); @@ -261,6 +262,9 @@ Audio::RewindableAudioStream *AudioPlayer::getAudioStream(uint32 number, uint32 #endif break; } +#else + error("Compressed audio file encountered, but no appropriate decoder is compiled in"); +#endif } else { // Original source file if (audioRes->_headerSize > 0) { @@ -332,11 +336,11 @@ void AudioPlayer::setSoundSync(ResourceId id, reg_t syncObjAddr, SegManager *seg _syncOffset = 0; if (_syncResource) { - PUT_SEL32V(segMan, syncObjAddr, SELECTOR(syncCue), 0); + writeSelectorValue(segMan, syncObjAddr, SELECTOR(syncCue), 0); } else { warning("setSoundSync: failed to find resource %s", id.toString().c_str()); // Notify the scripts to stop sound sync - PUT_SEL32V(segMan, syncObjAddr, SELECTOR(syncCue), SIGNAL_OFFSET); + writeSelectorValue(segMan, syncObjAddr, SELECTOR(syncCue), SIGNAL_OFFSET); } } @@ -352,8 +356,8 @@ void AudioPlayer::doSoundSync(reg_t syncObjAddr, SegManager *segMan) { _syncOffset += 2; } - PUT_SEL32V(segMan, syncObjAddr, SELECTOR(syncTime), syncTime); - PUT_SEL32V(segMan, syncObjAddr, SELECTOR(syncCue), syncCue); + writeSelectorValue(segMan, syncObjAddr, SELECTOR(syncTime), syncTime); + writeSelectorValue(segMan, syncObjAddr, SELECTOR(syncCue), syncCue); } } diff --git a/engines/sci/sound/midiparser_sci.cpp b/engines/sci/sound/midiparser_sci.cpp index 2068ea9a33..3ee8a3a83d 100644 --- a/engines/sci/sound/midiparser_sci.cpp +++ b/engines/sci/sound/midiparser_sci.cpp @@ -60,6 +60,9 @@ MidiParser_SCI::MidiParser_SCI(SciVersion soundVersion) : _dataincToAdd = 0; _resetOnPause = false; _channelsUsed = 0; + + for (int i = 0; i < 16; i++) + _channelRemap[i] = i; } MidiParser_SCI::~MidiParser_SCI() { @@ -85,7 +88,6 @@ bool MidiParser_SCI::loadMusic(SoundResource::Track *track, MusicEntry *psnd, in _tracks[0] = _mixedData; setTrack(0); _loopTick = 0; - _channelsUsed = 0; if (_soundVersion <= SCI_VERSION_0_LATE) { // Set initial voice count @@ -120,17 +122,20 @@ void MidiParser_SCI::unloadMusic() { // Center the pitch wheels and hold pedal in preparation for the next piece of music if (_driver) { for (int i = 0; i < 16; ++i) { - if (_channelsUsed & (1 << i)) { + if (isChannelUsed(i)) { _driver->send(0xE0 | i, 0, 0x40); // Reset pitch wheel _driver->send(0xB0 | i, 0x40, 0); // Reset hold pedal } } } + + for (int i = 0; i < 16; i++) + _channelRemap[i] = i; } void MidiParser_SCI::parseNextEvent(EventInfo &info) { // Monitor which channels are used by this song - _channelsUsed |= (1 << info.channel()); + setChannelUsed(info.channel()); // Set signal AFTER waiting for delta, otherwise we would set signal too soon resulting in all sorts of bugs if (_dataincAdd) { @@ -322,7 +327,7 @@ byte MidiParser_SCI::midiGetNextChannel(long ticker) { for (int i = 0; i < _track->channelCount; i++) { if (_track->channels[i].time == -1) // channel ended continue; - next = *_track->channels[i].data; // when the next event shoudl occur + next = *_track->channels[i].data; // when the next event should occur if (next == 0xF8) // 0xF8 means 240 ticks delay next = 240; next += _track->channels[i].time; @@ -389,9 +394,18 @@ byte *MidiParser_SCI::midiMixChannels() { channel->time = -1; // FIXME break; default: // MIDI command - if (command & 0x80) + if (command & 0x80) { par1 = *channel->data++; - else {// running status + + // TODO: Fix remapping + +#if 0 + // Remap channel. Keep the upper 4 bits (command code) and change + // the lower 4 bits (channel) + byte remappedChannel = _channelRemap[par1 & 0xF]; + par1 = (par1 & 0xF0) | (remappedChannel & 0xF); +#endif + } else {// running status par1 = command; command = channel->prev; } diff --git a/engines/sci/sound/midiparser_sci.h b/engines/sci/sound/midiparser_sci.h index f95c71ce2f..9d4b5a39da 100644 --- a/engines/sci/sound/midiparser_sci.h +++ b/engines/sci/sound/midiparser_sci.h @@ -71,7 +71,18 @@ public: jumpToTick(0); } + void remapChannel(byte channel, byte newChannel) { + assert(channel < 0xF); // don't touch special SCI channel 15 + assert(newChannel < 0xF); // don't touch special SCI channel 15 + _channelRemap[channel] = newChannel; + } + + void clearUsedChannels() { _channelsUsed = 0; } + protected: + bool isChannelUsed(byte channel) const { return _channelsUsed & (1 << channel); } + void setChannelUsed(byte channel) { _channelsUsed |= (1 << channel); } + void parseNextEvent(EventInfo &info); byte *midiMixChannels(); byte *midiFilterChannels(int channelMask); @@ -93,6 +104,8 @@ protected: // A 16-bit mask, containing the channels used // by the currently parsed song uint16 _channelsUsed; + + byte _channelRemap[16]; }; } // End of namespace Sci diff --git a/engines/sci/sound/music.cpp b/engines/sci/sound/music.cpp index 66f5ce9710..fa5716e7cc 100644 --- a/engines/sci/sound/music.cpp +++ b/engines/sci/sound/music.cpp @@ -37,9 +37,6 @@ namespace Sci { -// When defined, volume fading immediately sets the final sound volume -#define DISABLE_VOLUME_FADING - SciMusic::SciMusic(SciVersion soundVersion) : _soundVersion(soundVersion), _soundOn(true), _masterVolume(0) { @@ -115,8 +112,6 @@ void SciMusic::clearPlayList() { } void SciMusic::pauseAll(bool pause) { - Common::StackLock lock(_mutex); - const MusicList::iterator end = _playList.end(); for (MusicList::iterator i = _playList.begin(); i != end; ++i) { soundToggle(*i, pause); @@ -170,14 +165,29 @@ void SciMusic::setReverb(byte reverb) { _pMidiDrv->setReverb(reverb); } -static int f_compare(const void *arg1, const void *arg2) { - return ((const MusicEntry *)arg2)->priority - ((const MusicEntry *)arg1)->priority; +static bool musicEntryCompare(const MusicEntry *l, const MusicEntry *r) { + return (l->priority > r->priority); } void SciMusic::sortPlayList() { - MusicEntry ** pData = _playList.begin(); - qsort(pData, _playList.size(), sizeof(MusicEntry *), &f_compare); + // Sort the play list in descending priority order + Common::sort(_playList.begin(), _playList.end(), musicEntryCompare); } + +void SciMusic::findUsedChannels() { + // Reset list + for (int k = 0; k < 16; k++) + _usedChannels[k] = false; + + const MusicList::const_iterator end = _playList.end(); + for (MusicList::const_iterator i = _playList.begin(); i != end; ++i) { + for (int channel = 0; channel < 16; channel++) { + if ((*i)->soundRes && (*i)->soundRes->isChannelUsed(channel)) + _usedChannels[channel] = true; + } + } +} + void SciMusic::soundInitSnd(MusicEntry *pSnd) { int channelFilterMask = 0; SoundResource::Track *track = pSnd->soundRes->getTrackByType(_pMidiDrv->getPlayId()); @@ -221,6 +231,27 @@ void SciMusic::soundInitSnd(MusicEntry *pSnd) { pSnd->pauseCounter = 0; + // TODO: Fix channel remapping. This doesn't quite work... (e.g. no difference in LSL1VGA) +#if 0 + // Remap channels + findUsedChannels(); + + pSnd->pMidiParser->clearUsedChannels(); + + for (int i = 0; i < 16; i++) { + if (_usedChannels[i] && pSnd->soundRes->isChannelUsed(i)) { + int16 newChannel = getNextUnusedChannel(); + if (newChannel >= 0) { + _usedChannels[newChannel] = true; + debug("Remapping channel %d to %d\n", i, newChannel); + pSnd->pMidiParser->remapChannel(i, newChannel); + } else { + warning("Attempt to remap channel %d, but no unused channels exist", i); + } + } + } +#endif + // Find out what channels to filter for SCI0 channelFilterMask = pSnd->soundRes->getChannelFilterMask(_pMidiDrv->getPlayId(), _pMidiDrv->hasRhythmChannel()); pSnd->pMidiParser->loadMusic(track, pSnd, channelFilterMask, _soundVersion); @@ -243,14 +274,14 @@ void SciMusic::soundPlay(MusicEntry *pSnd) { uint playListCount = _playList.size(); uint playListNo = playListCount; - bool alreadyPlaying = false; + MusicEntry *alreadyPlaying = NULL; // searching if sound is already in _playList for (uint i = 0; i < playListCount; i++) { if (_playList[i] == pSnd) playListNo = i; if ((_playList[i]->status == kSoundPlaying) && (_playList[i]->pMidiParser)) - alreadyPlaying = true; + alreadyPlaying = _playList[i]; } if (playListNo == playListCount) { // not found _playList.push_back(pSnd); @@ -261,13 +292,20 @@ void SciMusic::soundPlay(MusicEntry *pSnd) { if (pSnd->pMidiParser) { if ((_soundVersion <= SCI_VERSION_0_LATE) && (alreadyPlaying)) { - // if any music is already playing, SCI0 queues music and plays it after the current music has finished - // done by SoundCommandParser::updateSci0Cues() - // Example of such case: iceman room 14 - // FIXME: this code is supposed to also take a look at priority and pause currently playing sound accordingly - pSnd->isQueued = true; - pSnd->status = kSoundPaused; - return; + // Music already playing in SCI0? + if (pSnd->priority > alreadyPlaying->priority) { + // And new priority higher? pause previous music and play new one immediately + // Example of such case: lsl3, when getting points (jingle is played then) + soundPause(alreadyPlaying); + alreadyPlaying->isQueued = true; + } else { + // And new priority equal or lower? queue up music and play it afterwards done by + // SoundCommandParser::updateSci0Cues() + // Example of such case: iceman room 14 + pSnd->isQueued = true; + pSnd->status = kSoundPaused; + return; + } } } @@ -298,6 +336,8 @@ void SciMusic::soundPlay(MusicEntry *pSnd) { void SciMusic::soundStop(MusicEntry *pSnd) { pSnd->status = kSoundStopped; + if (_soundVersion <= SCI_VERSION_0_LATE) + pSnd->isQueued = false; if (pSnd->pStreamAud) _pMixer->stopHandle(pSnd->hCurrentAud); @@ -380,7 +420,12 @@ void SciMusic::soundResume(MusicEntry *pSnd) { return; if (pSnd->status != kSoundPaused) return; - soundPlay(pSnd); + if (pSnd->pStreamAud) { + _pMixer->pauseHandle(pSnd->hCurrentAud, false); + pSnd->status = kSoundPlaying; + } else { + soundPlay(pSnd); + } } void SciMusic::soundToggle(MusicEntry *pSnd, bool pause) { @@ -522,15 +567,17 @@ void MusicEntry::doFade() { fadeStep = 0; fadeCompleted = true; } - - // Only process MIDI streams in this thread, not digital sound effects - if (pMidiParser) { -#ifdef DISABLE_VOLUME_FADING - // Signal fading to stop... +#ifdef ENABLE_SCI32 + // Disable fading for SCI32 - sound drivers have issues when fading in (gabriel knight 1 sierra title) + if (getSciVersion() >= SCI_VERSION_2) { volume = fadeTo; fadeStep = 0; fadeCompleted = true; + } #endif + + // Only process MIDI streams in this thread, not digital sound effects + if (pMidiParser) { pMidiParser->setVolume(volume); } diff --git a/engines/sci/sound/music.h b/engines/sci/sound/music.h index 8f08065b99..83cd59e89b 100644 --- a/engines/sci/sound/music.h +++ b/engines/sci/sound/music.h @@ -197,7 +197,6 @@ public: Common::Mutex _mutex; protected: - byte findAudEntry(uint16 nAud, byte&oVolume, uint32& oOffset, uint32&oSize); void sortPlayList(); SciVersion _soundVersion; @@ -211,10 +210,20 @@ protected: bool _bMultiMidi; private: static void miditimerCallback(void *p); + void findUsedChannels(); + int16 getNextUnusedChannel() const { + for (int i = 0; i < 16; i++) { + if (!_usedChannels[i]) + return i; + } + + return -1; + } MusicList _playList; bool _soundOn; byte _masterVolume; + bool _usedChannels[16]; }; } // End of namespace Sci diff --git a/engines/sci/sound/soundcmd.cpp b/engines/sci/sound/soundcmd.cpp index 925f3b2e1a..ece4c1430c 100644 --- a/engines/sci/sound/soundcmd.cpp +++ b/engines/sci/sound/soundcmd.cpp @@ -50,9 +50,9 @@ namespace Sci { #ifdef USE_OLD_MUSIC_FUNCTIONS static void script_set_priority(ResourceManager *resMan, SegManager *segMan, SfxState *state, reg_t obj, int priority) { - int song_nr = GET_SEL32V(segMan, obj, SELECTOR(number)); + int song_nr = readSelectorValue(segMan, obj, SELECTOR(number)); Resource *song = resMan->findResource(ResourceId(kResourceTypeSound, song_nr), 0); - int flags = GET_SEL32V(segMan, obj, SELECTOR(flags)); + int flags = readSelectorValue(segMan, obj, SELECTOR(flags)); if (priority == -1) { if (song->data[0] == 0xf0) @@ -64,7 +64,7 @@ static void script_set_priority(ResourceManager *resMan, SegManager *segMan, Sfx } else flags |= SCI1_SOUND_FLAG_SCRIPTED_PRI; state->sfx_song_renice(FROBNICATE_HANDLE(obj), priority); - PUT_SEL32V(segMan, obj, SELECTOR(flags), flags); + writeSelectorValue(segMan, obj, SELECTOR(flags), flags); } SongIterator *build_iterator(ResourceManager *resMan, int song_nr, SongIteratorType type, songit_id_t id) { @@ -98,27 +98,27 @@ void process_sound_events(EngineState *s) { /* Get all sound events, apply their case SI_LOOP: debugC(2, kDebugLevelSound, "[process-sound] Song %04x:%04x looped (to %d)", PRINT_REG(obj), cue); - /* PUT_SEL32V(segMan, obj, SELECTOR(loops), GET_SEL32V(segMan, obj, SELECTOR(loop));; - 1);*/ - PUT_SEL32V(segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); + /* writeSelectorValue(segMan, obj, SELECTOR(loops), readSelectorValue(segMan, obj, SELECTOR(loop));; - 1);*/ + writeSelectorValue(segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); break; case SI_RELATIVE_CUE: debugC(2, kDebugLevelSound, "[process-sound] Song %04x:%04x received relative cue %d", PRINT_REG(obj), cue); - PUT_SEL32V(segMan, obj, SELECTOR(signal), cue + 0x7f); + writeSelectorValue(segMan, obj, SELECTOR(signal), cue + 0x7f); break; case SI_ABSOLUTE_CUE: debugC(2, kDebugLevelSound, "[process-sound] Song %04x:%04x received absolute cue %d", PRINT_REG(obj), cue); - PUT_SEL32V(segMan, obj, SELECTOR(signal), cue); + writeSelectorValue(segMan, obj, SELECTOR(signal), cue); break; case SI_FINISHED: debugC(2, kDebugLevelSound, "[process-sound] Song %04x:%04x finished", PRINT_REG(obj)); - PUT_SEL32V(segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); - PUT_SEL32V(segMan, obj, SELECTOR(state), kSoundStopped); + writeSelectorValue(segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); + writeSelectorValue(segMan, obj, SELECTOR(state), kSoundStopped); break; default: @@ -253,7 +253,7 @@ void SoundCommandParser::cmdInitSound(reg_t obj, int16 value) { if (!obj.segment) return; - int resourceId = GET_SEL32V(_segMan, obj, SELECTOR(number)); + int resourceId = readSelectorValue(_segMan, obj, SELECTOR(number)); #ifdef USE_OLD_MUSIC_FUNCTIONS @@ -267,7 +267,7 @@ void SoundCommandParser::cmdInitSound(reg_t obj, int16 value) { SongIteratorType type = (_soundVersion <= SCI_VERSION_0_LATE) ? SCI_SONG_ITERATOR_TYPE_SCI0 : SCI_SONG_ITERATOR_TYPE_SCI1; if (_soundVersion <= SCI_VERSION_0_LATE) { - if (GET_SEL32V(_segMan, obj, SELECTOR(nodePtr))) { + if (readSelectorValue(_segMan, obj, SELECTOR(nodePtr))) { _state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED); _state->sfx_remove_song(handle); } @@ -281,11 +281,11 @@ void SoundCommandParser::cmdInitSound(reg_t obj, int16 value) { // Notify the engine if (_soundVersion <= SCI_VERSION_0_LATE) - PUT_SEL32V(_segMan, obj, SELECTOR(state), kSoundInitialized); + writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundInitialized); else - PUT_SEL32(_segMan, obj, SELECTOR(nodePtr), obj); + writeSelector(_segMan, obj, SELECTOR(nodePtr), obj); - PUT_SEL32(_segMan, obj, SELECTOR(handle), obj); + writeSelector(_segMan, obj, SELECTOR(handle), obj); #else @@ -302,10 +302,10 @@ void SoundCommandParser::cmdInitSound(reg_t obj, int16 value) { newSound->soundRes = 0; newSound->soundObj = obj; - newSound->loop = GET_SEL32V(_segMan, obj, SELECTOR(loop)); - newSound->priority = GET_SEL32V(_segMan, obj, SELECTOR(pri)) & 0xFF; - if (_soundVersion >= SCI_VERSION_1_LATE) - newSound->volume = CLIP<int>(GET_SEL32V(_segMan, obj, SELECTOR(vol)), 0, MUSIC_VOLUME_MAX); + newSound->loop = readSelectorValue(_segMan, obj, SELECTOR(loop)); + newSound->priority = readSelectorValue(_segMan, obj, SELECTOR(pri)) & 0xFF; + if (_soundVersion >= SCI_VERSION_1_EARLY) + newSound->volume = CLIP<int>(readSelectorValue(_segMan, obj, SELECTOR(vol)), 0, MUSIC_VOLUME_MAX); // In SCI1.1 games, sound effects are started from here. If we can find // a relevant audio resource, play it, otherwise switch to synthesized @@ -327,11 +327,11 @@ void SoundCommandParser::cmdInitSound(reg_t obj, int16 value) { if (newSound->soundRes || newSound->pStreamAud) { // Notify the engine if (_soundVersion <= SCI_VERSION_0_LATE) - PUT_SEL32V(_segMan, obj, SELECTOR(state), kSoundInitialized); + writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundInitialized); else - PUT_SEL32(_segMan, obj, SELECTOR(nodePtr), obj); + writeSelector(_segMan, obj, SELECTOR(nodePtr), obj); - PUT_SEL32(_segMan, obj, SELECTOR(handle), obj); + writeSelector(_segMan, obj, SELECTOR(handle), obj); } #endif @@ -346,30 +346,30 @@ void SoundCommandParser::cmdPlaySound(reg_t obj, int16 value) { if (_soundVersion <= SCI_VERSION_0_LATE) { _state->sfx_song_set_status(handle, SOUND_STATUS_PLAYING); - _state->sfx_song_set_loops(handle, GET_SEL32V(_segMan, obj, SELECTOR(loop))); - PUT_SEL32V(_segMan, obj, SELECTOR(state), kSoundPlaying); + _state->sfx_song_set_loops(handle, readSelectorValue(_segMan, obj, SELECTOR(loop))); + writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundPlaying); } else if (_soundVersion == SCI_VERSION_1_EARLY) { _state->sfx_song_set_status(handle, SOUND_STATUS_PLAYING); - _state->sfx_song_set_loops(handle, GET_SEL32V(_segMan, obj, SELECTOR(loop))); - _state->sfx_song_renice(handle, GET_SEL32V(_segMan, obj, SELECTOR(pri))); + _state->sfx_song_set_loops(handle, readSelectorValue(_segMan, obj, SELECTOR(loop))); + _state->sfx_song_renice(handle, readSelectorValue(_segMan, obj, SELECTOR(pri))); RESTORE_BEHAVIOR rb = (RESTORE_BEHAVIOR) value; /* Too lazy to look up a default value for this */ _state->_songlib.setSongRestoreBehavior(handle, rb); - PUT_SEL32V(_segMan, obj, SELECTOR(signal), 0); + writeSelectorValue(_segMan, obj, SELECTOR(signal), 0); } else if (_soundVersion == SCI_VERSION_1_LATE) { - int looping = GET_SEL32V(_segMan, obj, SELECTOR(loop)); - //int vol = GET_SEL32V(_segMan, obj, SELECTOR(vol)); - int pri = GET_SEL32V(_segMan, obj, SELECTOR(pri)); + int looping = readSelectorValue(_segMan, obj, SELECTOR(loop)); + //int vol = readSelectorValue(_segMan, obj, SELECTOR(vol)); + int pri = readSelectorValue(_segMan, obj, SELECTOR(pri)); int sampleLen = 0; Song *song = _state->_songlib.findSong(handle); - int songNumber = GET_SEL32V(_segMan, obj, SELECTOR(number)); + int songNumber = readSelectorValue(_segMan, obj, SELECTOR(number)); - if (GET_SEL32V(_segMan, obj, SELECTOR(nodePtr)) && (song && songNumber != song->_resourceNum)) { + if (readSelectorValue(_segMan, obj, SELECTOR(nodePtr)) && (song && songNumber != song->_resourceNum)) { _state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED); _state->sfx_remove_song(handle); - PUT_SEL32(_segMan, obj, SELECTOR(nodePtr), NULL_REG); + writeSelector(_segMan, obj, SELECTOR(nodePtr), NULL_REG); } - if (!GET_SEL32V(_segMan, obj, SELECTOR(nodePtr)) && obj.segment) { + if (!readSelectorValue(_segMan, obj, SELECTOR(nodePtr)) && obj.segment) { // In SCI1.1 games, sound effects are started from here. If we can find // a relevant audio resource, play it, otherwise switch to synthesized // effects. If the resource exists, play it using map 65535 (sound @@ -387,7 +387,7 @@ void SoundCommandParser::cmdPlaySound(reg_t obj, int16 value) { warning("Could not open song number %d", songNumber); // Send a "stop handle" event so that the engine won't wait forever here _state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED); - PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); + writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); return; } debugC(2, kDebugLevelSound, "Initializing song number %d", songNumber); @@ -395,15 +395,15 @@ void SoundCommandParser::cmdPlaySound(reg_t obj, int16 value) { handle), 0, handle, songNumber); } - PUT_SEL32(_segMan, obj, SELECTOR(nodePtr), obj); - PUT_SEL32(_segMan, obj, SELECTOR(handle), obj); + writeSelector(_segMan, obj, SELECTOR(nodePtr), obj); + writeSelector(_segMan, obj, SELECTOR(handle), obj); } if (obj.segment) { _state->sfx_song_set_status(handle, SOUND_STATUS_PLAYING); _state->sfx_song_set_loops(handle, looping); _state->sfx_song_renice(handle, pri); - PUT_SEL32V(_segMan, obj, SELECTOR(signal), 0); + writeSelectorValue(_segMan, obj, SELECTOR(signal), 0); } } @@ -415,7 +415,7 @@ void SoundCommandParser::cmdPlaySound(reg_t obj, int16 value) { return; } - int resourceId = obj.segment ? GET_SEL32V(_segMan, obj, SELECTOR(number)) : -1; + int resourceId = obj.segment ? readSelectorValue(_segMan, obj, SELECTOR(number)) : -1; if (musicSlot->resourceId != resourceId) { // another sound loaded into struct cmdDisposeSound(obj, value); @@ -423,25 +423,25 @@ void SoundCommandParser::cmdPlaySound(reg_t obj, int16 value) { // Find slot again :) musicSlot = _music->getSlot(obj); } - int16 loop = GET_SEL32V(_segMan, obj, SELECTOR(loop)); + int16 loop = readSelectorValue(_segMan, obj, SELECTOR(loop)); debugC(2, kDebugLevelSound, "cmdPlaySound: resource number %d, loop %d", resourceId, loop); - PUT_SEL32(_segMan, obj, SELECTOR(handle), obj); + writeSelector(_segMan, obj, SELECTOR(handle), obj); if (_soundVersion >= SCI_VERSION_1_EARLY) { - PUT_SEL32(_segMan, obj, SELECTOR(nodePtr), obj); - PUT_SEL32V(_segMan, obj, SELECTOR(min), 0); - PUT_SEL32V(_segMan, obj, SELECTOR(sec), 0); - PUT_SEL32V(_segMan, obj, SELECTOR(frame), 0); - PUT_SEL32V(_segMan, obj, SELECTOR(signal), 0); + writeSelector(_segMan, obj, SELECTOR(nodePtr), obj); + writeSelectorValue(_segMan, obj, SELECTOR(min), 0); + writeSelectorValue(_segMan, obj, SELECTOR(sec), 0); + writeSelectorValue(_segMan, obj, SELECTOR(frame), 0); + writeSelectorValue(_segMan, obj, SELECTOR(signal), 0); } else { - PUT_SEL32V(_segMan, obj, SELECTOR(state), kSoundPlaying); + writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundPlaying); } - musicSlot->loop = GET_SEL32V(_segMan, obj, SELECTOR(loop)); - musicSlot->priority = GET_SEL32V(_segMan, obj, SELECTOR(priority)); - if (_soundVersion >= SCI_VERSION_1_LATE) - musicSlot->volume = GET_SEL32V(_segMan, obj, SELECTOR(vol)); + musicSlot->loop = readSelectorValue(_segMan, obj, SELECTOR(loop)); + musicSlot->priority = readSelectorValue(_segMan, obj, SELECTOR(priority)); + if (_soundVersion >= SCI_VERSION_1_EARLY) + musicSlot->volume = readSelectorValue(_segMan, obj, SELECTOR(vol)); _music->soundPlay(musicSlot); #endif @@ -458,7 +458,7 @@ void SoundCommandParser::changeSoundStatus(reg_t obj, int newStatus) { if (obj.segment) { _state->sfx_song_set_status(handle, newStatus); if (_soundVersion <= SCI_VERSION_0_LATE) - PUT_SEL32V(_segMan, obj, SELECTOR(state), newStatus); + writeSelectorValue(_segMan, obj, SELECTOR(state), newStatus); } } #endif @@ -475,7 +475,7 @@ void SoundCommandParser::cmdDisposeSound(reg_t obj, int16 value) { _state->sfx_remove_song(handle); if (_soundVersion <= SCI_VERSION_0_LATE) - PUT_SEL32V(_segMan, obj, SELECTOR(handle), 0x0000); + writeSelectorValue(_segMan, obj, SELECTOR(handle), 0x0000); } #else @@ -489,11 +489,11 @@ void SoundCommandParser::cmdDisposeSound(reg_t obj, int16 value) { cmdStopSound(obj, value); _music->soundKill(musicSlot); - PUT_SEL32V(_segMan, obj, SELECTOR(handle), 0); + writeSelectorValue(_segMan, obj, SELECTOR(handle), 0); if (_soundVersion >= SCI_VERSION_1_EARLY) - PUT_SEL32(_segMan, obj, SELECTOR(nodePtr), NULL_REG); + writeSelector(_segMan, obj, SELECTOR(nodePtr), NULL_REG); else - PUT_SEL32V(_segMan, obj, SELECTOR(state), kSoundStopped); + writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundStopped); #endif } @@ -509,7 +509,7 @@ void SoundCommandParser::processStopSound(reg_t obj, int16 value, bool sampleFin changeSoundStatus(obj, SOUND_STATUS_STOPPED); if (_soundVersion >= SCI_VERSION_1_EARLY) - PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); + writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); #else MusicEntry *musicSlot = _music->getSlot(obj); if (!musicSlot) { @@ -518,9 +518,9 @@ void SoundCommandParser::processStopSound(reg_t obj, int16 value, bool sampleFin } if (_soundVersion <= SCI_VERSION_0_LATE) { - PUT_SEL32V(_segMan, obj, SELECTOR(state), kSoundStopped); + writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundStopped); } else { - PUT_SEL32V(_segMan, obj, SELECTOR(handle), 0); + writeSelectorValue(_segMan, obj, SELECTOR(handle), 0); } // Set signal selector in sound SCI0 games only, when the sample has finished playing @@ -530,7 +530,7 @@ void SoundCommandParser::processStopSound(reg_t obj, int16 value, bool sampleFin // sfx drivers included // We need to set signal in sound SCI1+ games all the time if ((_soundVersion > SCI_VERSION_0_LATE) || sampleFinishedPlaying) - PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); + writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); musicSlot->dataInc = 0; musicSlot->signal = 0; @@ -565,7 +565,7 @@ void SoundCommandParser::cmdPauseSound(reg_t obj, int16 value) { if (_soundVersion <= SCI_VERSION_0_LATE) { // Always pause the sound in SCI0 games. It's resumed in cmdResumeSound() - PUT_SEL32V(_segMan, musicSlot->soundObj, SELECTOR(state), kSoundPaused); + writeSelectorValue(_segMan, musicSlot->soundObj, SELECTOR(state), kSoundPaused); _music->soundPause(musicSlot); } else { _music->soundToggle(musicSlot, value); @@ -590,7 +590,7 @@ void SoundCommandParser::cmdResumeSound(reg_t obj, int16 value) { return; } - PUT_SEL32V(_segMan, musicSlot->soundObj, SELECTOR(state), kSoundPlaying); + writeSelectorValue(_segMan, musicSlot->soundObj, SELECTOR(state), kSoundPlaying); _music->soundResume(musicSlot); #endif } @@ -630,13 +630,12 @@ void SoundCommandParser::cmdFadeSound(reg_t obj, int16 value) { #ifdef USE_OLD_MUSIC_FUNCTIONS SongHandle handle = FROBNICATE_HANDLE(obj); if (_soundVersion != SCI_VERSION_1_LATE) { - /*s->sound_server->command(s, SOUND_COMMAND_FADE_HANDLE, obj, 120);*/ /* Fade out in 2 secs */ /* FIXME: The next couple of lines actually STOP the handle, rather ** than fading it! */ _state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED); if (_soundVersion <= SCI_VERSION_0_LATE) - PUT_SEL32V(_segMan, obj, SELECTOR(state), SOUND_STATUS_STOPPED); - PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); + writeSelectorValue(_segMan, obj, SELECTOR(state), SOUND_STATUS_STOPPED); + writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); } else { fade_params_t fade; fade.final_volume = _argv[2].toUint16(); @@ -651,11 +650,11 @@ void SoundCommandParser::cmdFadeSound(reg_t obj, int16 value) { /* FIXME: The next couple of lines actually STOP the handle, rather ** than fading it! */ if (_argv[5].toUint16()) { - PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); + writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); _state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED); } else { // FIXME: Support fade-and-continue. For now, send signal right away. - PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); + writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); } } #else @@ -692,7 +691,7 @@ void SoundCommandParser::cmdFadeSound(reg_t obj, int16 value) { // If sound is not playing currently, set signal directly if (musicSlot->status != kSoundPlaying) { warning("cmdFadeSound: fading requested, but sound is currently not playing"); - PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); + writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); } debugC(2, kDebugLevelSound, "cmdFadeSound: to %d, step %d, ticker %d", musicSlot->fadeTo, musicSlot->fadeStep, musicSlot->fadeTickerStep); @@ -714,8 +713,8 @@ void SoundCommandParser::cmdUpdateSound(reg_t obj, int16 value) { #ifdef USE_OLD_MUSIC_FUNCTIONS SongHandle handle = FROBNICATE_HANDLE(obj); if (_soundVersion <= SCI_VERSION_0_LATE && obj.segment) { - _state->sfx_song_set_loops(handle, GET_SEL32V(_segMan, obj, SELECTOR(loop))); - script_set_priority(_resMan, _segMan, _state, obj, GET_SEL32V(_segMan, obj, SELECTOR(pri))); + _state->sfx_song_set_loops(handle, readSelectorValue(_segMan, obj, SELECTOR(loop))); + script_set_priority(_resMan, _segMan, _state, obj, readSelectorValue(_segMan, obj, SELECTOR(pri))); } #else MusicEntry *musicSlot = _music->getSlot(obj); @@ -724,11 +723,11 @@ void SoundCommandParser::cmdUpdateSound(reg_t obj, int16 value) { return; } - musicSlot->loop = GET_SEL32V(_segMan, obj, SELECTOR(loop)); - int16 objVol = CLIP<int>(GET_SEL32V(_segMan, obj, SELECTOR(vol)), 0, 255); + musicSlot->loop = readSelectorValue(_segMan, obj, SELECTOR(loop)); + int16 objVol = CLIP<int>(readSelectorValue(_segMan, obj, SELECTOR(vol)), 0, 255); if (objVol != musicSlot->volume) _music->soundSetVolume(musicSlot, objVol); - uint32 objPrio = GET_SEL32V(_segMan, obj, SELECTOR(pri)); + uint32 objPrio = readSelectorValue(_segMan, obj, SELECTOR(pri)); if (objPrio != musicSlot->priority) _music->soundSetPriority(musicSlot, objPrio); @@ -755,7 +754,7 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) { debugC(2, kDebugLevelSound, "--- [CUE] %04x:%04x Absolute Cue: %d", PRINT_REG(obj), signal); debugC(2, kDebugLevelSound, "abs-signal %04X", signal); - PUT_SEL32V(_segMan, obj, SELECTOR(signal), signal); + writeSelectorValue(_segMan, obj, SELECTOR(signal), signal); break; case SI_RELATIVE_CUE: @@ -765,17 +764,17 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) { /* FIXME to match commented-out semantics * below, with proper storage of dataInc and * signal in the iterator code. */ - PUT_SEL32V(_segMan, obj, SELECTOR(dataInc), signal); + writeSelectorValue(_segMan, obj, SELECTOR(dataInc), signal); debugC(2, kDebugLevelSound, "rel-signal %04X", signal); if (_soundVersion == SCI_VERSION_1_EARLY) - PUT_SEL32V(_segMan, obj, SELECTOR(signal), signal); + writeSelectorValue(_segMan, obj, SELECTOR(signal), signal); else - PUT_SEL32V(_segMan, obj, SELECTOR(signal), signal + 127); + writeSelectorValue(_segMan, obj, SELECTOR(signal), signal + 127); break; case SI_FINISHED: debugC(2, kDebugLevelSound, "--- [FINISHED] %04x:%04x", PRINT_REG(obj)); - PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); + writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); break; case SI_LOOP: @@ -784,30 +783,30 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) { //switch (signal) { //case 0x00: - // if (dataInc != GET_SEL32V(segMan, obj, SELECTOR(dataInc))) { - // PUT_SEL32V(segMan, obj, SELECTOR(dataInc), dataInc); - // PUT_SEL32V(segMan, obj, SELECTOR(signal), dataInc+0x7f); + // if (dataInc != readSelectorValue(segMan, obj, SELECTOR(dataInc))) { + // writeSelectorValue(segMan, obj, SELECTOR(dataInc), dataInc); + // writeSelectorValue(segMan, obj, SELECTOR(signal), dataInc+0x7f); // } else { - // PUT_SEL32V(segMan, obj, SELECTOR(signal), signal); + // writeSelectorValue(segMan, obj, SELECTOR(signal), signal); // } // break; //case 0xFF: // May be unnecessary // s->_sound.sfx_song_set_status(handle, SOUND_STATUS_STOPPED); // break; //default : - // if (dataInc != GET_SEL32V(segMan, obj, SELECTOR(dataInc))) { - // PUT_SEL32V(segMan, obj, SELECTOR(dataInc), dataInc); - // PUT_SEL32V(segMan, obj, SELECTOR(signal), dataInc + 0x7f); + // if (dataInc != readSelectorValue(segMan, obj, SELECTOR(dataInc))) { + // writeSelectorValue(segMan, obj, SELECTOR(dataInc), dataInc); + // writeSelectorValue(segMan, obj, SELECTOR(signal), dataInc + 0x7f); // } else { - // PUT_SEL32V(segMan, obj, SELECTOR(signal), signal); + // writeSelectorValue(segMan, obj, SELECTOR(signal), signal); // } // break; //} if (_soundVersion == SCI_VERSION_1_EARLY) { - PUT_SEL32V(_segMan, obj, SELECTOR(min), min); - PUT_SEL32V(_segMan, obj, SELECTOR(sec), sec); - PUT_SEL32V(_segMan, obj, SELECTOR(frame), frame); + writeSelectorValue(_segMan, obj, SELECTOR(min), min); + writeSelectorValue(_segMan, obj, SELECTOR(sec), sec); + writeSelectorValue(_segMan, obj, SELECTOR(frame), frame); } #else MusicEntry *musicSlot = _music->getSlot(obj); @@ -828,7 +827,7 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) { musicSlot->loop -= currentLoopCounter - musicSlot->sampleLoopCounter; musicSlot->sampleLoopCounter = currentLoopCounter; } - if (!_music->soundIsActive(musicSlot)) { + if ((!_music->soundIsActive(musicSlot)) && (musicSlot->status != kSoundPaused)) { processStopSound(obj, 0, true); } else { _music->updateAudioStreamTicker(musicSlot); @@ -841,14 +840,14 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) { } else if (musicSlot->pMidiParser) { // Update MIDI slots if (musicSlot->signal == 0) { - if (musicSlot->dataInc != GET_SEL32V(_segMan, obj, SELECTOR(dataInc))) { + if (musicSlot->dataInc != readSelectorValue(_segMan, obj, SELECTOR(dataInc))) { if (_kernel->_selectorCache.dataInc > -1) - PUT_SEL32V(_segMan, obj, SELECTOR(dataInc), musicSlot->dataInc); - PUT_SEL32V(_segMan, obj, SELECTOR(signal), musicSlot->dataInc + 127); + writeSelectorValue(_segMan, obj, SELECTOR(dataInc), musicSlot->dataInc); + writeSelectorValue(_segMan, obj, SELECTOR(signal), musicSlot->dataInc + 127); } } else { // Sync the signal of the sound object - PUT_SEL32V(_segMan, obj, SELECTOR(signal), musicSlot->signal); + writeSelectorValue(_segMan, obj, SELECTOR(signal), musicSlot->signal); // We need to do this especially because state selector needs to get updated if (musicSlot->signal == SIGNAL_OFFSET) cmdStopSound(obj, 0); @@ -856,14 +855,14 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) { } else { // Slot actually has no data (which would mean that a sound-resource w/ unsupported data is used // (example lsl5 - sound resource 744 - it's roland exclusive - PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); + writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); // If we don't set signal here, at least the switch to the mud wrestling room in lsl5 will not work } if (musicSlot->fadeCompleted) { musicSlot->fadeCompleted = false; // We need signal for sci0 at least in iceman as well (room 14, fireworks) - PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); + writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); if (_soundVersion <= SCI_VERSION_0_LATE) { cmdStopSound(obj, 0); } else { @@ -874,14 +873,14 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) { // Sync loop selector for SCI0 if (_soundVersion <= SCI_VERSION_0_LATE) - PUT_SEL32V(_segMan, obj, SELECTOR(loop), musicSlot->loop); + writeSelectorValue(_segMan, obj, SELECTOR(loop), musicSlot->loop); musicSlot->signal = 0; if (_soundVersion >= SCI_VERSION_1_EARLY) { - PUT_SEL32V(_segMan, obj, SELECTOR(min), musicSlot->ticker / 3600); - PUT_SEL32V(_segMan, obj, SELECTOR(sec), musicSlot->ticker % 3600 / 60); - PUT_SEL32V(_segMan, obj, SELECTOR(frame), musicSlot->ticker); + writeSelectorValue(_segMan, obj, SELECTOR(min), musicSlot->ticker / 3600); + writeSelectorValue(_segMan, obj, SELECTOR(sec), musicSlot->ticker % 3600 / 60); + writeSelectorValue(_segMan, obj, SELECTOR(frame), musicSlot->ticker); } #endif @@ -927,18 +926,13 @@ void SoundCommandParser::cmdStopAllSounds(reg_t obj, int16 value) { #ifndef USE_OLD_MUSIC_FUNCTIONS Common::StackLock(_music->_mutex); - // FIXME: this can't be right, it's called in iceman (room 14) when the door sound has done playing - // stopping sounds can't be right, because music is starting afterwards in ssci. can't be resume queued - // song(s), because music is playing even when this call is nuked inside ssci. - return; - const MusicList::iterator end = _music->getPlayListEnd(); for (MusicList::iterator i = _music->getPlayListStart(); i != end; ++i) { if (_soundVersion <= SCI_VERSION_0_LATE) { - PUT_SEL32V(_segMan, (*i)->soundObj, SELECTOR(state), kSoundStopped); + writeSelectorValue(_segMan, (*i)->soundObj, SELECTOR(state), kSoundStopped); } else { - PUT_SEL32V(_segMan, obj, SELECTOR(handle), 0); - PUT_SEL32V(_segMan, (*i)->soundObj, SELECTOR(signal), SIGNAL_OFFSET); + writeSelectorValue(_segMan, obj, SELECTOR(handle), 0); + writeSelectorValue(_segMan, (*i)->soundObj, SELECTOR(signal), SIGNAL_OFFSET); } (*i)->dataInc = 0; @@ -969,7 +963,7 @@ void SoundCommandParser::cmdSetSoundVolume(reg_t obj, int16 value) { if (musicSlot->volume != value) { musicSlot->volume = value; _music->soundSetVolume(musicSlot, value); - PUT_SEL32V(_segMan, obj, SELECTOR(vol), value); + writeSelectorValue(_segMan, obj, SELECTOR(vol), value); } #endif } @@ -996,12 +990,12 @@ void SoundCommandParser::cmdSetSoundPriority(reg_t obj, int16 value) { warning("cmdSetSoundPriority: Attempt to unset song priority when there is no built-in value"); //pSnd->prio=0;field_15B=0 - PUT_SEL32V(_segMan, obj, SELECTOR(flags), GET_SEL32V(_segMan, obj, SELECTOR(flags)) & 0xFD); + writeSelectorValue(_segMan, obj, SELECTOR(flags), readSelectorValue(_segMan, obj, SELECTOR(flags)) & 0xFD); } else { // Scripted priority //pSnd->field_15B=1; - PUT_SEL32V(_segMan, obj, SELECTOR(flags), GET_SEL32V(_segMan, obj, SELECTOR(flags)) | 2); + writeSelectorValue(_segMan, obj, SELECTOR(flags), readSelectorValue(_segMan, obj, SELECTOR(flags)) | 2); //DoSOund(0xF,hobj,w) } #endif @@ -1012,7 +1006,7 @@ void SoundCommandParser::cmdSetSoundLoop(reg_t obj, int16 value) { return; #ifdef USE_OLD_MUSIC_FUNCTIONS - if (!GET_SEL32(_segMan, obj, SELECTOR(nodePtr)).isNull()) { + if (!readSelector(_segMan, obj, SELECTOR(nodePtr)).isNull()) { SongHandle handle = FROBNICATE_HANDLE(obj); _state->sfx_song_set_loops(handle, value); } @@ -1037,7 +1031,7 @@ void SoundCommandParser::cmdSetSoundLoop(reg_t obj, int16 value) { musicSlot->loop = 1; // actually plays the music once } - PUT_SEL32V(_segMan, obj, SELECTOR(loop), musicSlot->loop); + writeSelectorValue(_segMan, obj, SELECTOR(loop), musicSlot->loop); #endif } @@ -1058,8 +1052,11 @@ void SoundCommandParser::updateSci0Cues() { for (MusicList::iterator i = _music->getPlayListStart(); i != end; ++i) { // Is the sound stopped, and the sound object updated too? If yes, skip // this sound, as SCI0 only allows one active song - if (((*i)->isQueued) && (!pWaitingForPlay)) { + if ((*i)->isQueued) { pWaitingForPlay = (*i); + // FIXME (?) - in iceman 2 songs are queued when playing the door sound - if we use the first song for resuming + // then it's the wrong one. Both songs have same priority. Maybe the new sound function in sci0 + // is somehow responsible continue; } if ((*i)->signal == 0 && (*i)->status != kSoundPlaying) @@ -1105,11 +1102,11 @@ void SoundCommandParser::reconstructPlayList(int savegame_version) { } if ((*i)->status == kSoundPlaying) { if (savegame_version < 14) { - (*i)->dataInc = GET_SEL32V(_segMan, (*i)->soundObj, SELECTOR(dataInc)); - (*i)->signal = GET_SEL32V(_segMan, (*i)->soundObj, SELECTOR(signal)); + (*i)->dataInc = readSelectorValue(_segMan, (*i)->soundObj, SELECTOR(dataInc)); + (*i)->signal = readSelectorValue(_segMan, (*i)->soundObj, SELECTOR(signal)); if (_soundVersion >= SCI_VERSION_1_LATE) - (*i)->volume = GET_SEL32V(_segMan, (*i)->soundObj, SELECTOR(vol)); + (*i)->volume = readSelectorValue(_segMan, (*i)->soundObj, SELECTOR(vol)); } cmdPlaySound((*i)->soundObj, 0); @@ -1145,7 +1142,7 @@ void SoundCommandParser::startNewSound(int number) { MusicEntry *song = *_music->getPlayListStart(); reg_t soundObj = song->soundObj; cmdDisposeSound(soundObj, 0); - PUT_SEL32V(_segMan, soundObj, SELECTOR(number), number); + writeSelectorValue(_segMan, soundObj, SELECTOR(number), number); cmdInitSound(soundObj, 0); cmdPlaySound(soundObj, 0); #endif diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp index 757171b24c..0e0c0e129e 100644 --- a/engines/scumm/charset.cpp +++ b/engines/scumm/charset.cpp @@ -845,7 +845,7 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) { offsX = offsY = 0; } else { uint32 charOffs = READ_LE_UINT32(_fontPtr + chr * 4 + 4); - assert(charOffs < 0x10000); + assert(charOffs < 0x14000); if (!charOffs) return; charPtr = _fontPtr + charOffs; diff --git a/engines/scumm/debugger.cpp b/engines/scumm/debugger.cpp index a0975839d6..ea29e25a1f 100644 --- a/engines/scumm/debugger.cpp +++ b/engines/scumm/debugger.cpp @@ -870,7 +870,7 @@ bool ScummDebugger::Cmd_Passcode(int argc, const char **argv) { _detach_now = true; } else { - DebugPrintf("Use 'passcode <SEGA CD Passcode>'\n"); + DebugPrintf("Current Passcode is %d \nUse 'passcode <SEGA CD Passcode>'\n",_vm->_scummVars[411]); return true; } return false; diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp index 1f153094c1..d9c24ddca2 100644 --- a/engines/scumm/dialogs.cpp +++ b/engines/scumm/dialogs.cpp @@ -233,19 +233,6 @@ protected: #endif -class ConfigDialog : public GUI::OptionsDialog { -protected: -#ifdef SMALL_SCREEN_DEVICE - GUI::Dialog *_keysDialog; -#endif - -public: - ConfigDialog(); - ~ConfigDialog(); - - virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); -}; - #pragma mark - ScummDialog::ScummDialog(int x, int y, int w, int h) : GUI::Dialog(x, y, w, h) { @@ -258,223 +245,31 @@ ScummDialog::ScummDialog(String name) : GUI::Dialog(name) { #pragma mark - -enum { - kSaveCmd = 'SAVE', - kLoadCmd = 'LOAD', - kPlayCmd = 'PLAY', - kOptionsCmd = 'OPTN', - kHelpCmd = 'HELP', - kAboutCmd = 'ABOU', - kQuitCmd = 'QUIT', - kChooseCmd = 'CHOS' -}; - -ScummMenuDialog::ScummMenuDialog(ScummEngine *scumm) - : ScummDialog("ScummMain"), _vm(scumm) { - - new GUI::ButtonWidget(this, "ScummMain.Resume", "Resume", kPlayCmd, 'P'); - - _loadButton = new GUI::ButtonWidget(this, "ScummMain.Load", "Load", kLoadCmd, 'L'); - _saveButton = new GUI::ButtonWidget(this, "ScummMain.Save", "Save", kSaveCmd, 'S'); - - new GUI::ButtonWidget(this, "ScummMain.Options", "Options", kOptionsCmd, 'O'); #ifndef DISABLE_HELP - new GUI::ButtonWidget(this, "ScummMain.Help", "Help", kHelpCmd, 'H'); -#endif - new GUI::ButtonWidget(this, "ScummMain.About", "About", kAboutCmd, 'A'); - - new GUI::ButtonWidget(this, "ScummMain.Quit", "Quit", kQuitCmd, 'Q'); - // - // Create the sub dialog(s) - // - _aboutDialog = new GUI::AboutDialog(); - _optionsDialog = new ConfigDialog(); -#ifndef DISABLE_HELP +ScummMenuDialog::ScummMenuDialog(ScummEngine *scumm) + : MainMenuDialog(scumm) { _helpDialog = new HelpDialog(scumm->_game); -#endif - _saveDialog = new GUI::SaveLoadChooser("Save game:", "Save"); - _saveDialog->setSaveMode(true); - _loadDialog = new GUI::SaveLoadChooser("Load game:", "Load"); - _loadDialog->setSaveMode(false); + _helpButton->setEnabled(true); } ScummMenuDialog::~ScummMenuDialog() { - delete _aboutDialog; - delete _optionsDialog; -#ifndef DISABLE_HELP delete _helpDialog; -#endif - delete _saveDialog; - delete _loadDialog; -} - -int ScummMenuDialog::runModal() { - _loadButton->setEnabled(_vm->canLoadGameStateCurrently()); - _saveButton->setEnabled(_vm->canSaveGameStateCurrently()); - return ScummDialog::runModal(); -} - -void ScummMenuDialog::reflowLayout() { - _loadButton->setEnabled(_vm->canLoadGameStateCurrently()); - _saveButton->setEnabled(_vm->canSaveGameStateCurrently()); - Dialog::reflowLayout(); } void ScummMenuDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { switch (cmd) { - case kSaveCmd: - save(); - break; - case kLoadCmd: - load(); - break; - case kPlayCmd: - close(); - break; - case kOptionsCmd: - _optionsDialog->runModal(); - break; - case kAboutCmd: - _aboutDialog->runModal(); - break; -#ifndef DISABLE_HELP case kHelpCmd: _helpDialog->runModal(); break; -#endif - case kQuitCmd: - _vm->quitGame(); - close(); - break; default: - ScummDialog::handleCommand(sender, cmd, data); - } -} - -void ScummMenuDialog::save() { - Common::String gameId = ConfMan.get("gameid"); - - const EnginePlugin *plugin = 0; - EngineMan.findGame(gameId, &plugin); - - int idx = _saveDialog->runModal(plugin, ConfMan.getActiveDomainName()); - if (idx >= 0) { - String result(_saveDialog->getResultString()); - char buffer[20]; - const char *str; - if (result.empty()) { - // If the user was lazy and entered no save name, come up with a default name. - sprintf(buffer, "Save %d", idx); - str = buffer; - } else - str = result.c_str(); - _vm->requestSave(idx, str); - close(); - } -} - -void ScummMenuDialog::load() { - Common::String gameId = ConfMan.get("gameid"); - - const EnginePlugin *plugin = 0; - EngineMan.findGame(gameId, &plugin); - - int idx = _loadDialog->runModal(plugin, ConfMan.getActiveDomainName()); - if (idx >= 0) { - _vm->requestLoad(idx); - close(); + MainMenuDialog::handleCommand(sender, cmd, data); } } #pragma mark - enum { - kKeysCmd = 'KEYS' -}; - -// FIXME: We use the empty string as domain name here. This tells the -// ConfigManager to use the 'default' domain for all its actions. We do that -// to get as close as possible to editing the 'active' settings. -// -// However, that requires bad & evil hacks in the ConfigManager code, -// and even then still doesn't work quite correctly. -// For example, if the transient domain contains 'false' for the 'fullscreen' -// flag, but the user used a hotkey to switch to windowed mode, then the dialog -// will display the wrong value anyway. -// -// Proposed solution consisting of multiple steps: -// 1) Add special code to the open() code that reads out everything stored -// in the transient domain that is controlled by this dialog, and updates -// the dialog accordingly. -// 2) Even more code is added to query the backend for current settings, like -// the fullscreen mode flag etc., and also updates the dialog accordingly. -// 3) The domain being edited is set to the active game domain. -// 4) If the dialog is closed with the "OK" button, then we remove everything -// stored in the transient domain (or at least everything corresponding to -// switches in this dialog. -// If OTOH the dialog is closed with "Cancel" we do no such thing. -// -// These changes will achieve two things at once: Allow us to get rid of using -// "" as value for the domain, and in fact provide a somewhat better user -// experience at the same time. -ConfigDialog::ConfigDialog() - : GUI::OptionsDialog("", "ScummConfig") { - - // - // Sound controllers - // - - addVolumeControls(this, "ScummConfig."); - - // - // Some misc options - // - - // SCUMM has a talkspeed range of 0-9 - addSubtitleControls(this, "ScummConfig.", 9); - - // - // Add the buttons - // - - new GUI::ButtonWidget(this, "ScummConfig.Ok", "OK", GUI::kOKCmd, 'O'); - new GUI::ButtonWidget(this, "ScummConfig.Cancel", "Cancel", GUI::kCloseCmd, 'C'); -#ifdef SMALL_SCREEN_DEVICE - new GUI::ButtonWidget(this, "ScummConfig.Keys", "Keys", kKeysCmd, 'K'); - _keysDialog = NULL; -#endif -} - -ConfigDialog::~ConfigDialog() { -#ifdef SMALL_SCREEN_DEVICE - delete _keysDialog; -#endif -} - -void ConfigDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { - switch (cmd) { - case kKeysCmd: -#ifdef SMALL_SCREEN_DEVICE - // - // Create the sub dialog(s) - // - _keysDialog = new GUI::KeysDialog(); - _keysDialog->runModal(); - delete _keysDialog; - _keysDialog = NULL; -#endif - break; - default: - GUI::OptionsDialog::handleCommand (sender, cmd, data); - } -} - -#ifndef DISABLE_HELP - -#pragma mark - - -enum { kNextCmd = 'NEXT', kPrevCmd = 'PREV' }; diff --git a/engines/scumm/dialogs.h b/engines/scumm/dialogs.h index 7889027dcf..41a8ec83c1 100644 --- a/engines/scumm/dialogs.h +++ b/engines/scumm/dialogs.h @@ -27,9 +27,8 @@ #include "common/str.h" #include "gui/dialog.h" -#include "gui/options.h" #include "gui/widget.h" -#include "gui/saveload.h" +#include "engines/dialogs.h" #include "scumm/detection.h" @@ -52,32 +51,17 @@ protected: typedef Common::String String; }; -class ScummMenuDialog : public ScummDialog { +#ifndef DISABLE_HELP +class ScummMenuDialog : public MainMenuDialog { public: ScummMenuDialog(ScummEngine *scumm); ~ScummMenuDialog(); virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); - virtual void reflowLayout(); - - int runModal(); protected: - ScummEngine *_vm; - - GUI::Dialog *_aboutDialog; - GUI::Dialog *_optionsDialog; -#ifndef DISABLE_HELP GUI::Dialog *_helpDialog; -#endif - GUI::SaveLoadChooser *_saveDialog; - GUI::SaveLoadChooser *_loadDialog; - - GUI::ButtonWidget *_loadButton; - GUI::ButtonWidget *_saveButton; - - void save(); - void load(); }; +#endif /** * A dialog which displays an arbitrary message to the user and returns diff --git a/engines/scumm/he/resource_he.cpp b/engines/scumm/he/resource_he.cpp index 886ee99e57..c259c3ffd2 100644 --- a/engines/scumm/he/resource_he.cpp +++ b/engines/scumm/he/resource_he.cpp @@ -633,8 +633,10 @@ Win32ResExtractor::WinResource *Win32ResExtractor::list_pe_resources(WinLibrary wr[c].children = fi->first_resource + (FROM_LE_32(dirent[c].offset_to_data) & ~IMAGE_RESOURCE_DATA_IS_DIRECTORY); /* fill in wr->id, wr->numeric_id */ - if (!decode_pe_resource_id(fi, wr + c, FROM_LE_32(dirent[c].name))) + if (!decode_pe_resource_id(fi, wr + c, FROM_LE_32(dirent[c].name))) { + free(wr); return NULL; + } } return wr; diff --git a/engines/scumm/input.cpp b/engines/scumm/input.cpp index 8a9570f534..dc3a5d26b3 100644 --- a/engines/scumm/input.cpp +++ b/engines/scumm/input.cpp @@ -508,7 +508,7 @@ void ScummEngine::processKeyboard(Common::KeyState lastKeyHit) { if (VAR_SAVELOAD_SCRIPT != 0xFF && _currentRoom != 0) runScript(VAR(VAR_SAVELOAD_SCRIPT), 0, 0, 0); - scummMenuDialog(); // Display GUI + openMainMenuDialog(); // Display global main menu if (VAR_SAVELOAD_SCRIPT != 0xFF && _currentRoom != 0) runScript(VAR(VAR_SAVELOAD_SCRIPT2), 0, 0, 0); diff --git a/engines/scumm/scumm-md5.h b/engines/scumm/scumm-md5.h index 9b7e0798eb..cc382d9621 100644 --- a/engines/scumm/scumm-md5.h +++ b/engines/scumm/scumm-md5.h @@ -1,5 +1,5 @@ /* - This file was generated by the md5table tool on Sun May 9 20:53:55 2010 + This file was generated by the md5table tool on Mon May 24 13:24:24 2010 DO NOT EDIT MANUALLY! */ @@ -170,11 +170,13 @@ static const MD5Table md5table[] = { { "3af61c5edf8e15b43dbafd285b2e9777", "puttcircus", "", "Demo", -1, Common::HE_ISR, Common::kPlatformWindows }, { "3b301b7892f883ce42ab4be6a274fea6", "samnmax", "Floppy", "Floppy", -1, Common::EN_ANY, Common::kPlatformPC }, { "3b832f4a90740bf22e9b8ed42ca0128c", "freddi4", "HE 99", "", -1, Common::EN_GRB, Common::kPlatformWindows }, + { "3c4c471342bd95505a42334367d8f127", "puttmoon", "HE 70", "", 12161, Common::RU_RUS, Common::kPlatformWindows }, { "3cce1913a3bc586b51a75c3892ff18dd", "indy3", "VGA", "VGA", -1, Common::RU_RUS, Common::kPlatformPC }, { "3d219e7546039543307b55a91282bf18", "funpack", "", "", -1, Common::EN_ANY, Common::kPlatformPC }, { "3de99ef0523f8ca7958faa3afccd035a", "spyfox", "HE 100", "Updated", -1, Common::EN_USA, Common::kPlatformUnknown }, { "3df6ead57930488bc61e6e41901d0e97", "fbear", "HE 62", "", -1, Common::EN_ANY, Common::kPlatformMacintosh }, { "3e48298920fab9b7aec5a971e1bd1fab", "pajama3", "", "Demo", -1, Common::EN_GRB, Common::kPlatformWindows }, + { "3e861421f494711bc6f619d4aba60285", "airport", "", "", 93231, Common::RU_RUS, Common::kPlatformWindows }, { "40564ec47da48a67787d1f9bd043902a", "maniac", "V2 Demo", "V2 Demo", 1988, Common::EN_ANY, Common::kPlatformPC }, { "4167a92a1d46baa4f4127d918d561f88", "tentacle", "", "CD", 7932, Common::EN_ANY, Common::kPlatformUnknown }, { "41958e24d03181ff9a381a66d048a581", "ft", "", "", -1, Common::PT_BRA, Common::kPlatformUnknown }, @@ -243,6 +245,7 @@ static const MD5Table md5table[] = { { "5bd335265a61caa3d78956ad9f88ba23", "football", "", "Demo", -1, Common::EN_ANY, Common::kPlatformUnknown }, { "5c21fc49aee8f46e58fef21579e614a1", "thinker1", "", "", -1, Common::EN_USA, Common::kPlatformUnknown }, { "5d88b9d6a88e6f8e90cded9d01b7f082", "loom", "VGA", "VGA", 8307, Common::EN_ANY, Common::kPlatformPC }, + { "5dda73606533d66a4c3f4f9ea6e842af", "farm", "", "", 87061, Common::RU_RUS, Common::kPlatformWindows }, { "5e8fb66971a60e523e5afbc4c129c0e8", "socks", "HE 85", "", -1, Common::EN_USA, Common::kPlatformUnknown }, { "5ebb57234b2fe5c5dff641e00184ad81", "freddi", "HE 73", "", -1, Common::FR_FRA, Common::kPlatformWindows }, { "5fbe557049892eb4b709d90916ec97ca", "indy3", "EGA", "EGA", 5361, Common::EN_ANY, Common::kPlatformPC }, diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index 2359d4a04f..bb50ce7bb2 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -108,7 +108,7 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr) _language(dr.language), _debugger(0), _currentScript(0xFF), // Let debug() work on init stage - _messageDialog(0), _pauseDialog(0), _scummMenuDialog(0), _versionDialog(0) { + _messageDialog(0), _pauseDialog(0), _versionDialog(0) { if (_game.platform == Common::kPlatformNES) { _gdi = new GdiNES(this); @@ -140,7 +140,6 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr) _fileHandle = 0; - // Init all vars _v0ObjectIndex = false; _v0ObjectInInventory = false; @@ -152,7 +151,6 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr) _sound = NULL; memset(&vm, 0, sizeof(vm)); _pauseDialog = NULL; - _scummMenuDialog = NULL; _versionDialog = NULL; _fastMode = 0; _actors = NULL; @@ -552,6 +550,12 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr) for (int i = 0; i < ARRAYSIZE(debugChannels); ++i) DebugMan.addDebugChannel(debugChannels[i].flag, debugChannels[i].channel, debugChannels[i].desc); +#ifndef DISABLE_HELP + // Create custom GMM dialog providing a help subdialog + assert(!_mainMenuDialog); + _mainMenuDialog = new ScummMenuDialog(this); +#endif + g_eventRec.registerRandomSource(_rnd, "scumm"); } @@ -572,7 +576,6 @@ ScummEngine::~ScummEngine() { delete _charset; delete _messageDialog; delete _pauseDialog; - delete _scummMenuDialog; delete _versionDialog; delete _fileHandle; @@ -2437,13 +2440,6 @@ void ScummEngine::versionDialog() { runDialog(*_versionDialog); } -void ScummEngine::scummMenuDialog() { - if (!_scummMenuDialog) - _scummMenuDialog = new ScummMenuDialog(this); - runDialog(*_scummMenuDialog); - syncSoundSettings(); -} - void ScummEngine::confirmExitDialog() { ConfirmDialog d(this, 6); diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h index 885ab790de..42322ba5a2 100644 --- a/engines/scumm/scumm.h +++ b/engines/scumm/scumm.h @@ -530,7 +530,6 @@ protected: Dialog *_pauseDialog; Dialog *_messageDialog; Dialog *_versionDialog; - Dialog *_scummMenuDialog; virtual int runDialog(Dialog &dialog); void confirmExitDialog(); @@ -538,7 +537,6 @@ protected: void pauseDialog(); void messageDialog(const char *message); void versionDialog(); - void scummMenuDialog(); char displayMessage(const char *altButton, const char *message, ...) GCC_PRINTF(3, 4); diff --git a/engines/sword1/animation.cpp b/engines/sword1/animation.cpp index c0e7be7758..441e622184 100644 --- a/engines/sword1/animation.cpp +++ b/engines/sword1/animation.cpp @@ -23,7 +23,6 @@ * */ - #include "common/file.h" #include "sword1/sword1.h" #include "sword1/animation.h" @@ -72,6 +71,9 @@ MoviePlayer::MoviePlayer(SwordEngine *vm, Text *textMan, Audio::Mixer *snd, OSys _bgSoundStream = NULL; _decoderType = decoderType; _decoder = decoder; + + _white = 255; + _black = 0; } MoviePlayer::~MoviePlayer() { @@ -254,9 +256,35 @@ bool MoviePlayer::playVideo() { if (frame) _vm->_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h); - if (_decoder->hasDirtyPalette()) + if (_decoder->hasDirtyPalette()) { _decoder->setSystemPalette(); + uint32 maxWeight = 0; + uint32 minWeight = 0xFFFFFFFF; + uint32 weight; + byte r, g, b; + + byte *palette = _decoder->getPalette(); + + for (int i = 0; i < 256; i++) { + r = *palette++; + g = *palette++; + b = *palette++; + + weight = 3 * r * r + 6 * g * g + 2 * b * b; + + if (weight >= maxWeight) { + maxWeight = weight; + _white = i; + } + + if (weight <= minWeight) { + minWeight = weight; + _black = i; + } + } + } + Graphics::Surface *screen = _vm->_system->lockScreen(); performPostProcessing((byte *)screen->pixels); _vm->_system->unlockScreen(); @@ -267,17 +295,19 @@ bool MoviePlayer::playVideo() { while (_vm->_system->getEventManager()->pollEvent(event)) if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP) return false; + + _vm->_system->delayMillis(10); } return !_vm->shouldQuit(); } byte MoviePlayer::findBlackPalIndex() { - return 0; + return _black; } byte MoviePlayer::findWhitePalIndex() { - return 0xff; + return _white; } DXADecoderWithSound::DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle) diff --git a/engines/sword1/animation.h b/engines/sword1/animation.h index 82343f2800..193d5cf7ca 100644 --- a/engines/sword1/animation.h +++ b/engines/sword1/animation.h @@ -85,6 +85,7 @@ protected: OSystem *_system; Common::Array<MovieText *> _movieTexts; int _textX, _textY, _textWidth, _textHeight; + byte _white, _black; DecoderType _decoderType; Graphics::VideoDecoder *_decoder; diff --git a/engines/sword2/animation.cpp b/engines/sword2/animation.cpp index c3f3e796b2..10895b2ec1 100644 --- a/engines/sword2/animation.cpp +++ b/engines/sword2/animation.cpp @@ -51,6 +51,9 @@ MoviePlayer::MoviePlayer(Sword2Engine *vm, Audio::Mixer *snd, OSystem *system, A _bgSoundStream = NULL; _decoderType = decoderType; _decoder = decoder; + + _white = 255; + _black = 0; } MoviePlayer:: ~MoviePlayer() { @@ -280,9 +283,35 @@ bool MoviePlayer::playVideo() { if (frame) _vm->_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h); - if (_decoder->hasDirtyPalette()) + if (_decoder->hasDirtyPalette()) { _decoder->setSystemPalette(); + uint32 maxWeight = 0; + uint32 minWeight = 0xFFFFFFFF; + uint32 weight; + byte r, g, b; + + byte *palette = _decoder->getPalette(); + + for (int i = 0; i < 256; i++) { + r = *palette++; + g = *palette++; + b = *palette++; + + weight = 3 * r * r + 6 * g * g + 2 * b * b; + + if (weight >= maxWeight) { + maxWeight = weight; + _white = i; + } + + if (weight <= minWeight) { + minWeight = weight; + _black = i; + } + } + } + Graphics::Surface *screen = _vm->_system->lockScreen(); performPostProcessing((byte *)screen->pixels); _vm->_system->unlockScreen(); @@ -293,17 +322,19 @@ bool MoviePlayer::playVideo() { while (_vm->_system->getEventManager()->pollEvent(event)) if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP) return false; + + _vm->_system->delayMillis(10); } return !_vm->shouldQuit(); } byte MoviePlayer::findBlackPalIndex() { - return 0; + return _black; } byte MoviePlayer::findWhitePalIndex() { - return 0xff; + return _white; } DXADecoderWithSound::DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle) diff --git a/engines/sword2/animation.h b/engines/sword2/animation.h index bbf83e264c..ee32b1d5f2 100644 --- a/engines/sword2/animation.h +++ b/engines/sword2/animation.h @@ -87,6 +87,7 @@ protected: uint32 _currentMovieText; byte *_textSurface; int _textX, _textY; + byte _white, _black; DecoderType _decoderType; Graphics::VideoDecoder *_decoder; diff --git a/engines/testbed/gfxtests.cpp b/engines/testbed/gfxtests.cpp index 092e56ab39..2eeb7125b2 100644 --- a/engines/testbed/gfxtests.cpp +++ b/engines/testbed/gfxtests.cpp @@ -1,8 +1,10 @@ #include "testbed/gfxtests.h" +#include "testbed/graphics.h" #include "testbed/testsuite.h" #include "graphics/fontman.h" #include "graphics/surface.h" +#include "graphics/cursorman.h" namespace Testbed { @@ -13,13 +15,13 @@ bool testFullScreenMode() { Common::Point pt(0,100); Common::Rect rect = Testsuite::writeOnScreen("Testing fullscreen mode", pt); - g_system->delayMillis(1000); bool isFeaturePresent; bool isFeatureEnabled; isFeaturePresent = g_system->hasFeature(OSystem::kFeatureFullscreenMode); isFeatureEnabled = g_system->getFeatureState(OSystem::kFeatureFullscreenMode); + g_system->delayMillis(1000); if (isFeaturePresent) { //Toggle @@ -43,7 +45,96 @@ bool testFullScreenMode() { } bool testAspectRatio() { + Testsuite::displayMessage("Testing Aspect Ratio Correction. \n \ + With this feature enabled games running at 320x200 should be scaled upto 320x240 pixels"); + + Common::Point pt(0,100); + Common::Rect rect = Testsuite::writeOnScreen("Testing Aspect ratio correction", pt); + + bool isFeaturePresent; + bool isFeatureEnabled; + + isFeaturePresent = g_system->hasFeature(OSystem::kFeatureAspectRatioCorrection); + isFeatureEnabled = g_system->getFeatureState(OSystem::kFeatureAspectRatioCorrection); + g_system->delayMillis(1000); + + if (isFeaturePresent) { + //Toggle + + g_system->beginGFXTransaction(); + g_system->setFeatureState(OSystem::kFeatureAspectRatioCorrection, !isFeatureEnabled); + g_system->endGFXTransaction(); + + g_system->delayMillis(1000); + + g_system->beginGFXTransaction(); + g_system->setFeatureState(OSystem::kFeatureAspectRatioCorrection, isFeatureEnabled); + g_system->endGFXTransaction(); + } + else { + Testsuite::displayMessage("feature not supported"); + } + + Testsuite::clearScreen(rect); return true; } +bool testPalettizedCursors() { + Testsuite::displayMessage("Testing Cursors. You should expect to see a red colored cursor.\n"); + + Common::Point pt(0,100); + Common::Rect rect = Testsuite::writeOnScreen("Testing Palettized Cursors", pt); + + bool isFeaturePresent; + bool isFeatureEnabled; + + isFeaturePresent = g_system->hasFeature(OSystem::kFeatureCursorHasPalette); + isFeatureEnabled = g_system->getFeatureState(OSystem::kFeatureCursorHasPalette); + g_system->delayMillis(1000); + + if (isFeaturePresent) { + byte palette[3 * 4]; // Black, white and yellow + palette[0] = palette[1] = palette[2] = 0; + palette[4] = palette[5] = palette[6] = 255; + palette[8] = palette[9] = 255; + palette[10] = 0; + + byte buffer[10 * 10]; + memset(buffer, 2, 10 * 10); + + CursorMan.pushCursorPalette(palette, 0, 3); + CursorMan.pushCursor(buffer, 10, 10, 40, 40, 2, 1); + CursorMan.showMouse(true); + g_system->updateScreen(); + } + else { + Testsuite::displayMessage("feature not supported"); + } + Testsuite::clearScreen(rect); + return true; +} + +bool testCopyRectToScreen() { + Testsuite::displayMessage("Testing Blitting a Bitmap to screen. \n\ + You should expect to see a 20x40 yellow horizontal rectangle centred at the screen."); + + GFXTestSuite::setCustomColor(255, 255, 0); + byte buffer[20 * 40]; + memset(buffer, 2, 20 * 40); + + uint x = g_system->getWidth() / 2 - 20; + uint y = g_system->getHeight() / 2 - 10; + + g_system->copyRectToScreen(buffer, 40, x, y, 40, 20); + g_system->updateScreen(); + g_system->delayMillis(1000); + + Common::Rect rect(x, y, x+40, y+20); + Testsuite::clearScreen(rect); + + return true; + +} + + } diff --git a/engines/testbed/gfxtests.h b/engines/testbed/gfxtests.h index 7f5ae7d8af..bd219411e9 100644 --- a/engines/testbed/gfxtests.h +++ b/engines/testbed/gfxtests.h @@ -6,6 +6,8 @@ namespace Testbed { // will contain function declarations for GFX tests bool testFullScreenMode(); bool testAspectRatio(); +bool testPalettizedCursors(); +bool testCopyRectToScreen(); // add more here } diff --git a/engines/testbed/graphics.cpp b/engines/testbed/graphics.cpp index a5658a1213..ea1c1b1130 100644 --- a/engines/testbed/graphics.cpp +++ b/engines/testbed/graphics.cpp @@ -3,19 +3,20 @@ namespace Testbed { +byte GFXTestSuite::_palette[3 * 4] = {0, 0, 0, 0, 255, 255, 255, 0, 255, 255, 255, 0}; + GFXTestSuite::GFXTestSuite() { // Initialize color palettes // Te fourth field is for alpha channel which is unused // Assuming 8bpp as of now - _palette[0] =_palette[1] =_palette[2] = 0; - _palette[4] =_palette[5] =_palette[6] = 255; - _palette[8] =_palette[9] =_palette[10] = 255; g_system->setPalette(_palette, 0, 3); g_system->grabPalette(_palette, 0, 3); // Add tests here - addTest("FullScreenMode", &testFullScreenMode); - addTest("AspectRatio", &testAspectRatio); +// addTest("FullScreenMode", &testFullScreenMode); +// addTest("AspectRatio", &testAspectRatio); + addTest("PalettizedCursors", &testPalettizedCursors); +// addTest("BlitBitmaps", &testCopyRectToScreen); } const char *GFXTestSuite::getName() { diff --git a/engines/testbed/graphics.h b/engines/testbed/graphics.h index 5dcd02abac..eb84310db7 100644 --- a/engines/testbed/graphics.h +++ b/engines/testbed/graphics.h @@ -19,7 +19,7 @@ public: ~GFXTestSuite(){} void execute(); const char *getName(); - void setCustomColor(uint r, uint g, uint b); + static void setCustomColor(uint r, uint g, uint b); private: /** @@ -29,7 +29,7 @@ private: * 1 (R:255, G:255, B:255) White (kColorWhite) * 2 (R:255, G:255, B:255) your customized color (by default white) (kColorCustom) */ - byte _palette[3 * 4]; + static byte _palette[3 * 4]; }; } // End of namespace Testbed diff --git a/engines/tinsel/handle.cpp b/engines/tinsel/handle.cpp index de573feee2..60eb08a2dd 100644 --- a/engines/tinsel/handle.cpp +++ b/engines/tinsel/handle.cpp @@ -288,7 +288,8 @@ void LoadFile(MEMHANDLE *pH) { } // extract and zero terminate the filename - Common::strlcpy(szFilename, pH->szName, sizeof(pH->szName)); + memcpy(szFilename, pH->szName, sizeof(pH->szName)); + szFilename[sizeof(pH->szName)] = 0; if (f.open(szFilename)) { // read the data diff --git a/engines/tinsel/saveload.cpp b/engines/tinsel/saveload.cpp index b90ace4613..b010ad1fcb 100644 --- a/engines/tinsel/saveload.cpp +++ b/engines/tinsel/saveload.cpp @@ -180,7 +180,8 @@ static void syncSavedMover(Common::Serializer &s, SAVED_MOVER &sm) { static void syncSavedActor(Common::Serializer &s, SAVED_ACTOR &sa) { s.syncAsUint16LE(sa.actorID); s.syncAsUint16LE(sa.zFactor); - s.syncAsUint32LE(sa.bAlive); + s.syncAsUint16LE(sa.bAlive); + s.syncAsUint16LE(sa.bHidden); s.syncAsUint32LE(sa.presFilm); s.syncAsUint16LE(sa.presRnum); s.syncAsUint16LE(sa.presPlayX); diff --git a/engines/tucker/sequences.cpp b/engines/tucker/sequences.cpp index 68f5301a80..633ed2790d 100644 --- a/engines/tucker/sequences.cpp +++ b/engines/tucker/sequences.cpp @@ -491,7 +491,6 @@ AnimationSequencePlayer::AnimationSequencePlayer(OSystem *system, Audio::Mixer * _offscreenBuffer = (uint8 *)malloc(kScreenWidth * kScreenHeight); _updateScreenWidth = 0; _updateScreenPicture = false; - _updateScreenOffset = 0; _picBufPtr = _pic2BufPtr = 0; } @@ -537,9 +536,9 @@ void AnimationSequencePlayer::mainLoop() { } // budttle2.flc is shorter in french version ; start the background music // earlier and skip any sounds effects - if (_seqNum == 19 && _flicPlayer[0].getFrameCount() == 127) { + if (_seqNum == 19 && _flicPlayer[0].getFrameCount() == 126) { _soundSeqDataIndex = 6; - _frameCounter = 79; + _frameCounter = 80; } } (this->*(_updateFunc[_updateFuncIndex].play))(); @@ -765,10 +764,10 @@ void AnimationSequencePlayer::openAnimation(int index, const char *fileName) { } } -bool AnimationSequencePlayer::decodeNextAnimationFrame(int index) { +bool AnimationSequencePlayer::decodeNextAnimationFrame(int index, bool copyDirtyRects) { ::Graphics::Surface *surface = _flicPlayer[index].decodeNextFrame(); - if (_seqNum == 19) { + if (!copyDirtyRects) { for (uint16 y = 0; (y < surface->h) && (y < kScreenHeight); y++) memcpy(_offscreenBuffer + y * kScreenWidth, (byte *)surface->pixels + y * surface->pitch, surface->w); } else { @@ -807,13 +806,13 @@ void AnimationSequencePlayer::playIntroSeq19_20() { // cogs, and is being replayed when an intro credit appears ::Graphics::Surface *surface = 0; - if (_flicPlayer[0].getCurFrame() >= 117) { + if (_flicPlayer[0].getCurFrame() >= 115) { surface = _flicPlayer[1].decodeNextFrame(); if (_flicPlayer[1].endOfVideo()) _flicPlayer[1].reset(); } - bool framesLeft = decodeNextAnimationFrame(0); + bool framesLeft = decodeNextAnimationFrame(0, false); if (surface) for (int i = 0; i < kScreenWidth * kScreenHeight; ++i) @@ -841,19 +840,28 @@ void AnimationSequencePlayer::displayLoadingScreen() { void AnimationSequencePlayer::initPicPart4() { _updateScreenWidth = 320; _updateScreenPicture = true; - _updateScreenOffset = 0; + _updateScreenCounter = 0; + _updateScreenIndex = -1; } void AnimationSequencePlayer::drawPicPart4() { - static const uint8 offsetsTable[77] = { - 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, - 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 3, 3, 3, - 3, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1 - }; - _updateScreenWidth = _updateScreenWidth - offsetsTable[_updateScreenOffset]; - ++_updateScreenOffset; + static const uint8 offsets[] = { 1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1 }; + if (_updateScreenIndex == -1) { + for (int i = 0; i < 256; ++i) { + if (memcmp(_animationPalette + i * 4, _picBufPtr + 32 + i * 3, 3) != 0) { + memcpy(_animationPalette + i * 4, _picBufPtr + 32 + i * 3, 3); + _animationPalette[i * 4 + 3] = 0; + } + } + } + if (_updateScreenCounter == 0) { + static const uint8 counter[] = { 1, 2, 3, 4, 5, 35, 5, 4, 3, 2, 1 }; + ++_updateScreenIndex; + assert(_updateScreenIndex < ARRAYSIZE(counter)); + _updateScreenCounter = counter[_updateScreenIndex]; + } + --_updateScreenCounter; + _updateScreenWidth -= offsets[_updateScreenIndex]; for (int y = 0; y < 200; ++y) { memcpy(_offscreenBuffer + y * 320, _picBufPtr + 800 + y * 640 + _updateScreenWidth, 320); } @@ -875,7 +883,7 @@ void AnimationSequencePlayer::loadIntroSeq3_4() { void AnimationSequencePlayer::playIntroSeq3_4() { if (!_updateScreenPicture) { bool framesLeft = decodeNextAnimationFrame(0); - if (_flicPlayer[0].getCurFrame() == 707) { + if (_flicPlayer[0].getCurFrame() == 705) { initPicPart4(); } if (!framesLeft) { @@ -914,17 +922,10 @@ void AnimationSequencePlayer::drawPic2Part10() { } void AnimationSequencePlayer::drawPic1Part10() { - ::Graphics::Surface *surface = _flicPlayer[0].decodeNextFrame(); - _flicPlayer[0].copyDirtyRectsToBuffer(_offscreenBuffer, kScreenWidth); - ++_frameCounter; - - if (_flicPlayer[0].hasDirtyPalette()) - getRGBPalette(0); - int offset = 0; for (int y = 0; y < kScreenHeight; ++y) { for (int x = 0; x < kScreenWidth; ++x) { - byte color = *((byte *)surface->pixels + offset); + byte color = _offscreenBuffer[offset]; if (color == 0) color = _picBufPtr[800 + y * 640 + _updateScreenWidth + x]; @@ -943,22 +944,24 @@ void AnimationSequencePlayer::loadIntroSeq9_10() { } void AnimationSequencePlayer::playIntroSeq9_10() { - if (_flicPlayer[0].getCurFrame() >= 265 && _flicPlayer[0].getCurFrame() <= 296) { + const int nextFrame = _flicPlayer[0].getCurFrame() + 1; + if (nextFrame >= 263 && nextFrame <= 294) { + decodeNextAnimationFrame(0, false); drawPic1Part10(); _updateScreenWidth += 6; - } else if (_flicPlayer[0].getCurFrame() == 985) { + } else if (nextFrame == 983) { decodeNextAnimationFrame(0); drawPic2Part10(); - } else if (_flicPlayer[0].getCurFrame() >= 989 && _flicPlayer[0].getCurFrame() <= 997) { + } else if (nextFrame >= 987 && nextFrame <= 995) { + decodeNextAnimationFrame(0, false); drawPic1Part10(); _updateScreenWidth -= 25; if (_updateScreenWidth < 0) { _updateScreenWidth = 0; } - } - - if (_flicPlayer[0].endOfVideo()) + } else if (!decodeNextAnimationFrame(0)) { _changeToNextSequence = true; + } } void AnimationSequencePlayer::loadIntroSeq21_22() { diff --git a/engines/tucker/tucker.h b/engines/tucker/tucker.h index d9810c7929..86f5843e77 100644 --- a/engines/tucker/tucker.h +++ b/engines/tucker/tucker.h @@ -934,7 +934,7 @@ private: void unloadAnimation(); uint8 *loadPicture(const char *fileName); void openAnimation(int index, const char *fileName); - bool decodeNextAnimationFrame(int index); + bool decodeNextAnimationFrame(int index, bool copyDirtyRects = true); void loadIntroSeq17_18(); void playIntroSeq17_18(); void loadIntroSeq19_20(); @@ -975,7 +975,8 @@ private: uint8 *_offscreenBuffer; int _updateScreenWidth; int _updateScreenPicture; - int _updateScreenOffset; + int _updateScreenCounter; + int _updateScreenIndex; int _frameCounter; int _frameTime; uint32 _lastFrameTime; |