diff options
Diffstat (limited to 'engines/sci/graphics')
28 files changed, 1257 insertions, 683 deletions
diff --git a/engines/sci/graphics/animate.cpp b/engines/sci/graphics/animate.cpp index 2e5e94c52a..d8f3fa09b0 100644 --- a/engines/sci/graphics/animate.cpp +++ b/engines/sci/graphics/animate.cpp @@ -27,6 +27,7 @@ #include "common/stack.h" #include "graphics/primitives.h" +#include "sci/console.h" #include "sci/sci.h" #include "sci/event.h" #include "sci/engine/kernel.h" @@ -191,11 +192,88 @@ void GfxAnimate::makeSortedList(List *list) { Common::sort(_list.begin(), _list.end(), sortHelper); } -void GfxAnimate::applyGlobalScaling(AnimateList::iterator entry, GfxView *view) { - reg_t curObject = entry->object; +void GfxAnimate::fill(byte &old_picNotValid) { + GfxView *view = NULL; + AnimateList::iterator it; + const AnimateList::iterator end = _list.end(); + + for (it = _list.begin(); it != end; ++it) { + // Get the corresponding view + view = _cache->getView(it->viewId); + + adjustInvalidCels(view, it); + processViewScaling(view, it); + setNsRect(view, it); + + //warning("%s view %d, loop %d, cel %d, signal %x", _s->_segMan->getObjectName(curObject), it->viewId, it->loopNo, it->celNo, it->signal); + + // Calculate current priority according to y-coordinate + if (!(it->signal & kSignalFixedPriority)) { + it->priority = _ports->kernelCoordinateToPriority(it->y); + writeSelectorValue(_s->_segMan, it->object, SELECTOR(priority), it->priority); + } + + if (it->signal & kSignalNoUpdate) { + if ((it->signal & (kSignalForceUpdate | kSignalViewUpdated)) + || (it->signal & kSignalHidden && !(it->signal & kSignalRemoveView)) + || (!(it->signal & kSignalHidden) && it->signal & kSignalRemoveView) + || (it->signal & kSignalAlwaysUpdate)) + old_picNotValid++; + it->signal &= ~kSignalStopUpdate; + } else { + if ((it->signal & kSignalStopUpdate) || (it->signal & kSignalAlwaysUpdate)) + old_picNotValid++; + it->signal &= ~kSignalForceUpdate; + } + } +} + +void GfxAnimate::adjustInvalidCels(GfxView *view, AnimateList::iterator it) { + // adjust loop and cel, if any of those is invalid + // this seems to be completely crazy code + // sierra sci checked signed int16 to be above or equal the counts and reseted to 0 in those cases + // later during view processing those are compared unsigned again and then set to maximum count - 1 + // Games rely on this behaviour. For example laura bow 1 has a knight standing around in room 37 + // which has cel set to 3. This cel does not exist and the actual knight is 0 + // In kq5 on the other hand during the intro, when the trunk is opened, cel is set to some real + // high number, which is negative when considered signed. This actually requires to get fixed to + // maximum cel, otherwise the trunk would be closed. + int16 viewLoopCount = view->getLoopCount(); + if (it->loopNo >= viewLoopCount) { + it->loopNo = 0; + writeSelectorValue(_s->_segMan, it->object, SELECTOR(loop), it->loopNo); + } else if (it->loopNo < 0) { + it->loopNo = viewLoopCount - 1; + // not setting selector is right, sierra sci didn't do it during view processing as well + } + int16 viewCelCount = view->getCelCount(it->loopNo); + if (it->celNo >= viewCelCount) { + it->celNo = 0; + writeSelectorValue(_s->_segMan, it->object, SELECTOR(cel), it->celNo); + } else if (it->celNo < 0) { + it->celNo = viewCelCount - 1; + } +} + +void GfxAnimate::processViewScaling(GfxView *view, AnimateList::iterator it) { + if (!view->isScaleable()) { + // Laura Bow 2 (especially floppy) depends on this, some views are not supposed to be scaleable + // this "feature" was removed in later versions of SCI1.1 + it->scaleSignal = 0; + it->scaleY = it->scaleX = 128; + } else { + // Process global scaling, if needed + if (it->scaleSignal & kScaleSignalDoScaling) { + if (it->scaleSignal & kScaleSignalGlobalScaling) { + applyGlobalScaling(it, view); + } + } + } +} +void GfxAnimate::applyGlobalScaling(AnimateList::iterator entry, GfxView *view) { // Global scaling uses global var 2 and some other stuff to calculate scaleX/scaleY - int16 maxScale = readSelectorValue(_s->_segMan, curObject, SELECTOR(maxScale)); + int16 maxScale = readSelectorValue(_s->_segMan, entry->object, SELECTOR(maxScale)); int16 celHeight = view->getHeight(entry->loopNo, entry->celNo); int16 maxCelHeight = (maxScale * celHeight) >> 7; reg_t globalVar2 = _s->variables[VAR_GLOBAL][2]; // current room object @@ -215,120 +293,43 @@ void GfxAnimate::applyGlobalScaling(AnimateList::iterator entry, GfxView *view) entry->scaleX = entry->scaleY; // and set objects scale selectors - writeSelectorValue(_s->_segMan, curObject, SELECTOR(scaleX), entry->scaleX); - writeSelectorValue(_s->_segMan, curObject, SELECTOR(scaleY), entry->scaleY); + writeSelectorValue(_s->_segMan, entry->object, SELECTOR(scaleX), entry->scaleX); + writeSelectorValue(_s->_segMan, entry->object, SELECTOR(scaleY), entry->scaleY); } -void GfxAnimate::fill(byte &old_picNotValid) { - reg_t curObject; - uint16 signal; - GfxView *view = NULL; - AnimateList::iterator it; - const AnimateList::iterator end = _list.end(); - - for (it = _list.begin(); it != end; ++it) { - curObject = it->object; - signal = it->signal; - - // Get the corresponding view - view = _cache->getView(it->viewId); - - // adjust loop and cel, if any of those is invalid - // this seems to be completely crazy code - // sierra sci checked signed int16 to be above or equal the counts and reseted to 0 in those cases - // later during view processing those are compared unsigned again and then set to maximum count - 1 - // Games rely on this behaviour. For example laura bow 1 has a knight standing around in room 37 - // which has cel set to 3. This cel does not exist and the actual knight is 0 - // In kq5 on the other hand during the intro, when the trunk is opened, cel is set to some real - // high number, which is negative when considered signed. This actually requires to get fixed to - // maximum cel, otherwise the trunk would be closed. - int16 viewLoopCount = view->getLoopCount(); - if (it->loopNo >= viewLoopCount) { - it->loopNo = 0; - writeSelectorValue(_s->_segMan, curObject, SELECTOR(loop), it->loopNo); - } else if (it->loopNo < 0) { - it->loopNo = viewLoopCount - 1; - // not setting selector is right, sierra sci didn't do it during view processing as well - } - int16 viewCelCount = view->getCelCount(it->loopNo); - if (it->celNo >= viewCelCount) { - it->celNo = 0; - writeSelectorValue(_s->_segMan, curObject, SELECTOR(cel), it->celNo); - } else if (it->celNo < 0) { - it->celNo = viewCelCount - 1; - } - - if (!view->isScaleable()) { - // Laura Bow 2 (especially floppy) depends on this, some views are not supposed to be scaleable - // this "feature" was removed in later versions of SCI1.1 - it->scaleSignal = 0; - it->scaleY = it->scaleX = 128; - } else { - // Process global scaling, if needed - if (it->scaleSignal & kScaleSignalDoScaling) { - if (it->scaleSignal & kScaleSignalGlobalScaling) { - applyGlobalScaling(it, view); - } - } - } - - //warning("%s view %d, loop %d, cel %d, signal %x", _s->_segMan->getObjectName(curObject), it->viewId, it->loopNo, it->celNo, it->signal); +void GfxAnimate::setNsRect(GfxView *view, AnimateList::iterator it) { + bool shouldSetNsRect = true; - bool setNsRect = true; - - // Create rect according to coordinates and given cel - if (it->scaleSignal & kScaleSignalDoScaling) { - view->getCelScaledRect(it->loopNo, it->celNo, it->x, it->y, it->z, it->scaleX, it->scaleY, it->celRect); - // when being scaled, only set nsRect, if object will get drawn - if ((signal & kSignalHidden) && !(signal & kSignalAlwaysUpdate)) - setNsRect = false; + // Create rect according to coordinates and given cel + if (it->scaleSignal & kScaleSignalDoScaling) { + view->getCelScaledRect(it->loopNo, it->celNo, it->x, it->y, it->z, it->scaleX, it->scaleY, it->celRect); + // when being scaled, only set nsRect, if object will get drawn + if ((it->signal & kSignalHidden) && !(it->signal & kSignalAlwaysUpdate)) + shouldSetNsRect = false; + } else { + // This special handling is not included in the other SCI1.1 interpreters and MUST NOT be + // checked in those cases, otherwise we will break games (e.g. EcoQuest 2, room 200) + if ((g_sci->getGameId() == GID_HOYLE4) && (it->scaleSignal & kScaleSignalHoyle4SpecialHandling)) { + it->celRect.left = readSelectorValue(_s->_segMan, it->object, SELECTOR(nsLeft)); + it->celRect.top = readSelectorValue(_s->_segMan, it->object, SELECTOR(nsTop)); + it->celRect.right = readSelectorValue(_s->_segMan, it->object, SELECTOR(nsRight)); + it->celRect.bottom = readSelectorValue(_s->_segMan, it->object, SELECTOR(nsBottom)); + view->getCelSpecialHoyle4Rect(it->loopNo, it->celNo, it->x, it->y, it->z, it->celRect); + shouldSetNsRect = false; } else { - // This special handling is not included in the other SCI1.1 interpreters and MUST NOT be - // checked in those cases, otherwise we will break games (e.g. EcoQuest 2, room 200) - if ((g_sci->getGameId() == GID_HOYLE4) && (it->scaleSignal & kScaleSignalHoyle4SpecialHandling)) { - it->celRect.left = readSelectorValue(_s->_segMan, curObject, SELECTOR(nsLeft)); - it->celRect.top = readSelectorValue(_s->_segMan, curObject, SELECTOR(nsTop)); - it->celRect.right = readSelectorValue(_s->_segMan, curObject, SELECTOR(nsRight)); - it->celRect.bottom = readSelectorValue(_s->_segMan, curObject, SELECTOR(nsBottom)); - view->getCelSpecialHoyle4Rect(it->loopNo, it->celNo, it->x, it->y, it->z, it->celRect); - setNsRect = false; - } else { - view->getCelRect(it->loopNo, it->celNo, it->x, it->y, it->z, it->celRect); - } - } - - if (setNsRect) { - writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsLeft), it->celRect.left); - writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsTop), it->celRect.top); - writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsRight), it->celRect.right); - writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsBottom), it->celRect.bottom); - } - - // Calculate current priority according to y-coordinate - if (!(signal & kSignalFixedPriority)) { - it->priority = _ports->kernelCoordinateToPriority(it->y); - writeSelectorValue(_s->_segMan, curObject, SELECTOR(priority), it->priority); + view->getCelRect(it->loopNo, it->celNo, it->x, it->y, it->z, it->celRect); } + } - if (signal & kSignalNoUpdate) { - if (signal & (kSignalForceUpdate | kSignalViewUpdated) - || (signal & kSignalHidden && !(signal & kSignalRemoveView)) - || (!(signal & kSignalHidden) && signal & kSignalRemoveView) - || (signal & kSignalAlwaysUpdate)) - old_picNotValid++; - signal &= ~kSignalStopUpdate; - } else { - if (signal & kSignalStopUpdate || signal & kSignalAlwaysUpdate) - old_picNotValid++; - signal &= ~kSignalForceUpdate; - } - it->signal = signal; + if (shouldSetNsRect) { + writeSelectorValue(_s->_segMan, it->object, SELECTOR(nsLeft), it->celRect.left); + writeSelectorValue(_s->_segMan, it->object, SELECTOR(nsTop), it->celRect.top); + writeSelectorValue(_s->_segMan, it->object, SELECTOR(nsRight), it->celRect.right); + writeSelectorValue(_s->_segMan, it->object, SELECTOR(nsBottom), it->celRect.bottom); } } void GfxAnimate::update() { - reg_t curObject; - uint16 signal; reg_t bitsHandle; Common::Rect rect; AnimateList::iterator it; @@ -336,81 +337,66 @@ void GfxAnimate::update() { // Remove all no-update cels, if requested for (it = _list.reverse_begin(); it != end; --it) { - curObject = it->object; - signal = it->signal; - - if (signal & kSignalNoUpdate) { - if (!(signal & kSignalRemoveView)) { - bitsHandle = readSelector(_s->_segMan, curObject, SELECTOR(underBits)); + if (it->signal & kSignalNoUpdate) { + if (!(it->signal & kSignalRemoveView)) { + bitsHandle = readSelector(_s->_segMan, it->object, SELECTOR(underBits)); if (_screen->_picNotValid != 1) { _paint16->bitsRestore(bitsHandle); it->showBitsFlag = true; } else { _paint16->bitsFree(bitsHandle); } - writeSelectorValue(_s->_segMan, curObject, SELECTOR(underBits), 0); + writeSelectorValue(_s->_segMan, it->object, SELECTOR(underBits), 0); } - signal &= ~kSignalForceUpdate; - if (signal & kSignalViewUpdated) - signal &= ~(kSignalViewUpdated | kSignalNoUpdate); - } else if (signal & kSignalStopUpdate) { - signal &= ~kSignalStopUpdate; - signal |= kSignalNoUpdate; + it->signal &= ~kSignalForceUpdate; + if (it->signal & kSignalViewUpdated) + it->signal &= ~(kSignalViewUpdated | kSignalNoUpdate); + } else if (it->signal & kSignalStopUpdate) { + it->signal &= ~kSignalStopUpdate; + it->signal |= kSignalNoUpdate; } - it->signal = signal; } // Draw always-update cels for (it = _list.begin(); it != end; ++it) { - curObject = it->object; - signal = it->signal; - - if (signal & kSignalAlwaysUpdate) { + if (it->signal & kSignalAlwaysUpdate) { // draw corresponding cel _paint16->drawCel(it->viewId, it->loopNo, it->celNo, it->celRect, it->priority, it->paletteNo, it->scaleX, it->scaleY); it->showBitsFlag = true; - signal &= ~(kSignalStopUpdate | kSignalViewUpdated | kSignalNoUpdate | kSignalForceUpdate); - if ((signal & kSignalIgnoreActor) == 0) { + it->signal &= ~(kSignalStopUpdate | kSignalViewUpdated | kSignalNoUpdate | kSignalForceUpdate); + if (!(it->signal & kSignalIgnoreActor)) { rect = it->celRect; rect.top = CLIP<int16>(_ports->kernelPriorityToCoordinate(it->priority) - 1, rect.top, rect.bottom - 1); _paint16->fillRect(rect, GFX_SCREEN_MASK_CONTROL, 0, 0, 15); } - it->signal = signal; } } // Saving background for all NoUpdate-cels for (it = _list.begin(); it != end; ++it) { - curObject = it->object; - signal = it->signal; - - if (signal & kSignalNoUpdate) { - if (signal & kSignalHidden) { - signal |= kSignalRemoveView; + if (it->signal & kSignalNoUpdate) { + if (it->signal & kSignalHidden) { + it->signal |= kSignalRemoveView; } else { - signal &= ~kSignalRemoveView; - if (signal & kSignalIgnoreActor) + it->signal &= ~kSignalRemoveView; + if (it->signal & kSignalIgnoreActor) bitsHandle = _paint16->bitsSave(it->celRect, GFX_SCREEN_MASK_VISUAL|GFX_SCREEN_MASK_PRIORITY); else bitsHandle = _paint16->bitsSave(it->celRect, GFX_SCREEN_MASK_ALL); - writeSelector(_s->_segMan, curObject, SELECTOR(underBits), bitsHandle); + writeSelector(_s->_segMan, it->object, SELECTOR(underBits), bitsHandle); } - it->signal = signal; } } // Draw NoUpdate cels for (it = _list.begin(); it != end; ++it) { - curObject = it->object; - signal = it->signal; - - if (signal & kSignalNoUpdate && !(signal & kSignalHidden)) { + if (it->signal & kSignalNoUpdate && !(it->signal & kSignalHidden)) { // draw corresponding cel _paint16->drawCel(it->viewId, it->loopNo, it->celNo, it->celRect, it->priority, it->paletteNo, it->scaleX, it->scaleY); it->showBitsFlag = true; - if (!(signal & kSignalIgnoreActor)) { + if (!(it->signal & kSignalIgnoreActor)) { rect = it->celRect; rect.top = CLIP<int16>(_ports->kernelPriorityToCoordinate(it->priority) - 1, rect.top, rect.bottom - 1); _paint16->fillRect(rect, GFX_SCREEN_MASK_CONTROL, 0, 0, 15); @@ -420,30 +406,23 @@ void GfxAnimate::update() { } void GfxAnimate::drawCels() { - reg_t curObject; - uint16 signal; reg_t bitsHandle; AnimateList::iterator it; const AnimateList::iterator end = _list.end(); _lastCastData.clear(); for (it = _list.begin(); it != end; ++it) { - curObject = it->object; - signal = it->signal; - - if (!(signal & (kSignalNoUpdate | kSignalHidden | kSignalAlwaysUpdate))) { + if (!(it->signal & (kSignalNoUpdate | kSignalHidden | kSignalAlwaysUpdate))) { // Save background bitsHandle = _paint16->bitsSave(it->celRect, GFX_SCREEN_MASK_ALL); - writeSelector(_s->_segMan, curObject, SELECTOR(underBits), bitsHandle); + writeSelector(_s->_segMan, it->object, SELECTOR(underBits), bitsHandle); // draw corresponding cel _paint16->drawCel(it->viewId, it->loopNo, it->celNo, it->celRect, it->priority, it->paletteNo, it->scaleX, it->scaleY); it->showBitsFlag = true; - if (signal & kSignalRemoveView) { - signal &= ~kSignalRemoveView; - } - it->signal = signal; + if (it->signal & kSignalRemoveView) + it->signal &= ~kSignalRemoveView; // Remember that entry in lastCast _lastCastData.push_back(*it); @@ -452,23 +431,18 @@ void GfxAnimate::drawCels() { } void GfxAnimate::updateScreen(byte oldPicNotValid) { - reg_t curObject; - uint16 signal; AnimateList::iterator it; const AnimateList::iterator end = _list.end(); Common::Rect lsRect; Common::Rect workerRect; for (it = _list.begin(); it != end; ++it) { - curObject = it->object; - signal = it->signal; - - if (it->showBitsFlag || !(signal & (kSignalRemoveView | kSignalNoUpdate) || - (!(signal & kSignalRemoveView) && (signal & kSignalNoUpdate) && oldPicNotValid))) { - 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)); + if (it->showBitsFlag || !(it->signal & (kSignalRemoveView | kSignalNoUpdate) || + (!(it->signal & kSignalRemoveView) && (it->signal & kSignalNoUpdate) && oldPicNotValid))) { + lsRect.left = readSelectorValue(_s->_segMan, it->object, SELECTOR(lsLeft)); + lsRect.top = readSelectorValue(_s->_segMan, it->object, SELECTOR(lsTop)); + lsRect.right = readSelectorValue(_s->_segMan, it->object, SELECTOR(lsRight)); + lsRect.bottom = readSelectorValue(_s->_segMan, it->object, SELECTOR(lsBottom)); workerRect = lsRect; workerRect.clip(it->celRect); @@ -480,17 +454,16 @@ void GfxAnimate::updateScreen(byte oldPicNotValid) { _paint16->bitsShow(lsRect); workerRect = it->celRect; } - writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsLeft), it->celRect.left); - writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsTop), it->celRect.top); - writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsRight), it->celRect.right); - writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsBottom), it->celRect.bottom); + writeSelectorValue(_s->_segMan, it->object, SELECTOR(lsLeft), it->celRect.left); + writeSelectorValue(_s->_segMan, it->object, SELECTOR(lsTop), it->celRect.top); + writeSelectorValue(_s->_segMan, it->object, SELECTOR(lsRight), it->celRect.right); + writeSelectorValue(_s->_segMan, it->object, SELECTOR(lsBottom), it->celRect.bottom); // may get used for debugging //_paint16->frameRect(workerRect); _paint16->bitsShow(workerRect); - if (signal & kSignalHidden) { + if (it->signal & kSignalHidden) it->signal |= kSignalRemoveView; - } } } // use this for debug purposes @@ -498,37 +471,30 @@ void GfxAnimate::updateScreen(byte oldPicNotValid) { } void GfxAnimate::restoreAndDelete(int argc, reg_t *argv) { - reg_t curObject; - uint16 signal; AnimateList::iterator it; const AnimateList::iterator end = _list.end(); - // This has to be done in a separate loop. At least in sq1 some .dispose // modifies FIXEDLOOP flag in signal for another object. In that case we // would overwrite the new signal with our version of the old signal. for (it = _list.begin(); it != end; ++it) { - curObject = it->object; - signal = it->signal; - // Finally update signal - writeSelectorValue(_s->_segMan, curObject, SELECTOR(signal), signal); + writeSelectorValue(_s->_segMan, it->object, SELECTOR(signal), it->signal); } for (it = _list.reverse_begin(); it != end; --it) { - curObject = it->object; // We read out signal here again, this is not by accident but to ensure // that we got an up-to-date signal - signal = readSelectorValue(_s->_segMan, curObject, SELECTOR(signal)); + it->signal = readSelectorValue(_s->_segMan, it->object, SELECTOR(signal)); - if ((signal & (kSignalNoUpdate | kSignalRemoveView)) == 0) { - _paint16->bitsRestore(readSelector(_s->_segMan, curObject, SELECTOR(underBits))); - writeSelectorValue(_s->_segMan, curObject, SELECTOR(underBits), 0); + if ((it->signal & (kSignalNoUpdate | kSignalRemoveView)) == 0) { + _paint16->bitsRestore(readSelector(_s->_segMan, it->object, SELECTOR(underBits))); + writeSelectorValue(_s->_segMan, it->object, SELECTOR(underBits), 0); } - if (signal & kSignalDisposeMe) { + if (it->signal & kSignalDisposeMe) { // Call .delete_ method of that object - invokeSelector(_s, curObject, SELECTOR(delete_), argc, argv, 0); + invokeSelector(_s, it->object, SELECTOR(delete_), argc, argv, 0); } } } @@ -591,7 +557,7 @@ void GfxAnimate::addToPicDrawCels() { // draw corresponding cel _paint16->drawCel(view, it->loopNo, it->celNo, it->celRect, it->priority, it->paletteNo, it->scaleX, it->scaleY); - if ((it->signal & kSignalIgnoreActor) == 0) { + if (!(it->signal & kSignalIgnoreActor)) { it->celRect.top = CLIP<int16>(_ports->kernelPriorityToCoordinate(it->priority) - 1, it->celRect.top, it->celRect.bottom - 1); _paint16->fillRect(it->celRect, GFX_SCREEN_MASK_CONTROL, 0, 0, 15); } @@ -665,10 +631,10 @@ void GfxAnimate::kernelAnimate(reg_t listReference, bool cycle, int argc, reg_t // beginUpdate()/endUpdate() were introduced SCI1. // Calling those for SCI0 will work most of the time but breaks minor // stuff like percentage bar of qfg1ega at the character skill screen. - if (getSciVersion() >= SCI_VERSION_1_EGA) + if (getSciVersion() >= SCI_VERSION_1_EGA_ONLY) _ports->beginUpdate(_ports->_picWind); update(); - if (getSciVersion() >= SCI_VERSION_1_EGA) + if (getSciVersion() >= SCI_VERSION_1_EGA_ONLY) _ports->endUpdate(_ports->_picWind); } @@ -687,6 +653,10 @@ void GfxAnimate::kernelAnimate(reg_t listReference, bool cycle, int argc, reg_t _ports->setPort(oldPort); // Now trigger speed throttler + throttleSpeed(); +} + +void GfxAnimate::throttleSpeed() { switch (_lastCastData.size()) { case 0: // No entries drawn -> no speed throttler triggering @@ -759,4 +729,21 @@ void GfxAnimate::kernelAddToPicView(GuiResourceId viewId, int16 loopNo, int16 ce addToPicSetPicNotValid(); } +void GfxAnimate::printAnimateList(Console *con) { + AnimateList::iterator it; + const AnimateList::iterator end = _list.end(); + + for (it = _list.begin(); it != end; ++it) { + Script *scr = _s->_segMan->getScriptIfLoaded(it->object.segment); + int16 scriptNo = scr ? scr->getScriptNumber() : -1; + + con->DebugPrintf("%04x:%04x (%s), script %d, view %d (%d, %d), pal %d, " + "at %d, %d, scale %d, %d / %d (z: %d, prio: %d, shown: %d, signal: %d)\n", + PRINT_REG(it->object), _s->_segMan->getObjectName(it->object), + scriptNo, it->viewId, it->loopNo, it->celNo, it->paletteNo, + it->x, it->y, it->scaleX, it->scaleY, it->scaleSignal, + it->z, it->priority, it->showBitsFlag, it->signal); + } +} + } // End of namespace Sci diff --git a/engines/sci/graphics/animate.h b/engines/sci/graphics/animate.h index 44ffdd53af..b2aadcbead 100644 --- a/engines/sci/graphics/animate.h +++ b/engines/sci/graphics/animate.h @@ -77,6 +77,7 @@ struct AnimateEntry { typedef Common::List<AnimateEntry> AnimateList; typedef Common::Array<AnimateEntry> AnimateArray; +class Console; class GfxCache; class GfxCursor; class GfxPorts; @@ -105,6 +106,7 @@ public: void reAnimate(Common::Rect rect); void addToPicDrawCels(); void addToPicDrawView(GuiResourceId viewId, int16 loopNo, int16 celNo, int16 leftPos, int16 topPos, int16 priority, int16 control); + void printAnimateList(Console *con); virtual void kernelAnimate(reg_t listReference, bool cycle, int argc, reg_t *argv); virtual void kernelAddToPicList(reg_t listReference, int argc, reg_t *argv); @@ -115,6 +117,10 @@ private: void addToPicSetPicNotValid(); void animateShowPic(); + void throttleSpeed(); + void adjustInvalidCels(GfxView *view, AnimateList::iterator it); + void processViewScaling(GfxView *view, AnimateList::iterator it); + void setNsRect(GfxView *view, AnimateList::iterator it); EngineState *_s; GfxCache *_cache; diff --git a/engines/sci/graphics/compare.cpp b/engines/sci/graphics/compare.cpp index 5f6aef0153..db926f2962 100644 --- a/engines/sci/graphics/compare.cpp +++ b/engines/sci/graphics/compare.cpp @@ -81,23 +81,14 @@ reg_t GfxCompare::canBeHereCheckRectList(reg_t checkObject, const Common::Rect & curObject = curNode->value; if (curObject != checkObject) { signal = readSelectorValue(_segMan, curObject, SELECTOR(signal)); - if ((signal & (kSignalIgnoreActor | kSignalRemoveView | kSignalNoUpdate)) == 0) { + if (!(signal & (kSignalIgnoreActor | kSignalRemoveView | kSignalNoUpdate))) { 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 - // this way, so it should not be changed lightly. However, somebody should - // confirm whether the original engine really did it this way. Then, update - // this comment accordingly, and, if necessary, fix the code. - if (curRect.right > checkRect.left && - curRect.left < checkRect.right && - curRect.bottom > checkRect.top && - curRect.top < checkRect.bottom) { + if (checkRect.contains(curRect)) return curObject; - } } } curAddress = curNode->succ; @@ -134,7 +125,7 @@ void GfxCompare::kernelSetNowSeen(reg_t objectReference) { #ifdef ENABLE_SCI32 if (view->isSci2Hires()) - _screen->adjustToUpscaledCoordinates(y, x); + view->adjustToUpscaledCoordinates(y, x); else if (getSciVersion() == SCI_VERSION_2_1) _coordAdjuster->fromScriptToDisplay(y, x); #endif @@ -143,8 +134,8 @@ void GfxCompare::kernelSetNowSeen(reg_t objectReference) { #ifdef ENABLE_SCI32 if (view->isSci2Hires()) { - _screen->adjustBackUpscaledCoordinates(celRect.top, celRect.left); - _screen->adjustBackUpscaledCoordinates(celRect.bottom, celRect.right); + view->adjustBackUpscaledCoordinates(celRect.top, celRect.left); + view->adjustBackUpscaledCoordinates(celRect.bottom, celRect.right); } else if (getSciVersion() == SCI_VERSION_2_1) { _coordAdjuster->fromDisplayToScript(celRect.top, celRect.left); _coordAdjuster->fromDisplayToScript(celRect.bottom, celRect.right); @@ -232,13 +223,13 @@ void GfxCompare::kernelBaseSetter(reg_t object) { celRect.bottom = readSelectorValue(_segMan, object, SELECTOR(nsBottom)); } else { if (tmpView->isSci2Hires()) - _screen->adjustToUpscaledCoordinates(y, x); + tmpView->adjustToUpscaledCoordinates(y, x); tmpView->getCelRect(loopNo, celNo, x, y, z, celRect); if (tmpView->isSci2Hires()) { - _screen->adjustBackUpscaledCoordinates(celRect.top, celRect.left); - _screen->adjustBackUpscaledCoordinates(celRect.bottom, celRect.right); + tmpView->adjustBackUpscaledCoordinates(celRect.top, celRect.left); + tmpView->adjustBackUpscaledCoordinates(celRect.bottom, celRect.right); } } diff --git a/engines/sci/graphics/cursor.cpp b/engines/sci/graphics/cursor.cpp index e78d3e67cb..3b95a5c955 100644 --- a/engines/sci/graphics/cursor.cpp +++ b/engines/sci/graphics/cursor.cpp @@ -26,6 +26,7 @@ #include "common/config-manager.h" #include "common/events.h" #include "common/macresman.h" +#include "common/memstream.h" #include "common/system.h" #include "common/util.h" #include "graphics/cursorman.h" @@ -38,6 +39,7 @@ #include "sci/graphics/coordadjuster.h" #include "sci/graphics/view.h" #include "sci/graphics/cursor.h" +#include "sci/graphics/maciconbar.h" namespace Sci { @@ -434,9 +436,17 @@ void GfxCursor::kernelSetMacCursor(GuiResourceId viewNum, int loopNum, int celNu if (_macCursorRemap.empty()) { // QFG1/Freddy/Hoyle4 use a straight viewNum->cursor ID mapping - // KQ6 seems to use this mapping for its cursors - if (g_sci->getGameId() == GID_KQ6) - viewNum = loopNum * 1000 + celNum; + // KQ6 uses this mapping for its cursors + if (g_sci->getGameId() == GID_KQ6) { + if (viewNum == 990) // Inventory Cursors + viewNum = loopNum * 16 + celNum + 2000; + else if (viewNum == 998) // Regular Cursors + viewNum = celNum + 1000; + else // Unknown cursor, ignored + return; + } + if (g_sci->hasMacIconBar()) + g_sci->_gfxMacIconBar->setInventoryIcon(viewNum); } else { // If we do have the list, we'll be using a remap based on what the // scripts have given us. @@ -485,10 +495,10 @@ void GfxCursor::kernelSetMacCursor(GuiResourceId viewNum, int loopNum, int celNu cursorBitmap[i * 8 + b] = 0; // Doesn't matter, just is transparent } - uint16 hotspotX = READ_BE_UINT16(data); - uint16 hotspotY = READ_BE_UINT16(data + 2); + uint16 hotspotY = READ_BE_UINT16(data); + uint16 hotspotX = READ_BE_UINT16(data + 2); - static const byte cursorPalette[] = { 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00 }; + static const byte cursorPalette[] = { 0x00, 0x00, 0x00, 0xff, 0xff, 0xff }; CursorMan.replaceCursor(cursorBitmap, 16, 16, hotspotX, hotspotY, 0); CursorMan.replaceCursorPalette(cursorPalette, 1, 2); @@ -498,11 +508,12 @@ void GfxCursor::kernelSetMacCursor(GuiResourceId viewNum, int loopNum, int celNu // Mac crsr cursor byte *cursorBitmap, *palette; int width, height, hotspotX, hotspotY, palSize, keycolor; - Common::MacResManager::convertCrsrCursor(resource->data, resource->size, &cursorBitmap, &width, &height, &hotspotX, &hotspotY, &keycolor, true, &palette, &palSize); + Common::MemoryReadStream resStream(resource->data, resource->size); + Common::MacResManager::convertCrsrCursor(&resStream, &cursorBitmap, width, height, hotspotX, hotspotY, keycolor, true, &palette, palSize); CursorMan.replaceCursor(cursorBitmap, width, height, hotspotX, hotspotY, keycolor); CursorMan.replaceCursorPalette(palette, 0, palSize); - free(cursorBitmap); - free(palette); + delete[] cursorBitmap; + delete[] palette; } kernelShow(); diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp index 5b690f289a..dd55b3b060 100644 --- a/engines/sci/graphics/frameout.cpp +++ b/engines/sci/graphics/frameout.cpp @@ -58,6 +58,12 @@ GfxFrameout::GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAd GfxFrameout::~GfxFrameout() { } +void GfxFrameout::clear() { + _screenItems.clear(); + _planes.clear(); + _planePictures.clear(); +} + void GfxFrameout::kernelAddPlane(reg_t object) { PlaneEntry newPlane; @@ -77,10 +83,12 @@ void GfxFrameout::kernelAddPlane(reg_t object) { } newPlane.object = object; - newPlane.pictureId = 0xFFFF; newPlane.priority = readSelectorValue(_segMan, object, SELECTOR(priority)); newPlane.lastPriority = 0xFFFF; // hidden newPlane.planeOffsetX = 0; + newPlane.pictureId = 0xFFFF; + newPlane.planePictureMirrored = false; + newPlane.planeBack = 0; _planes.push_back(newPlane); kernelUpdatePlane(object); @@ -117,7 +125,10 @@ void GfxFrameout::kernelUpdatePlane(reg_t object) { if (it->planeRect.left < 0) { it->planeOffsetX = -it->planeRect.left; it->planeRect.left = 0; + } else { + it->planeOffsetX = 0; } + if (it->planeRect.top < 0) it->planeRect.top = 0; // We get bad plane-bottom in sq6 @@ -331,6 +342,8 @@ static int16 GetLongest(const char *text, int16 maxWidth, GfxFont *font) { maxChars = curCharCount; // return count up to (but not including) breaking space break; } + if (width + font->getCharWidth(curChar) > maxWidth) + break; width += font->getCharWidth(curChar); curCharCount++; } @@ -479,8 +492,8 @@ void GfxFrameout::kernelFrameout() { if (view->isSci2Hires()) { int16 dummyX = 0; - _screen->adjustToUpscaledCoordinates(itemEntry->y, itemEntry->x); - _screen->adjustToUpscaledCoordinates(itemEntry->z, dummyX); + view->adjustToUpscaledCoordinates(itemEntry->y, itemEntry->x); + view->adjustToUpscaledCoordinates(itemEntry->z, dummyX); } else if (getSciVersion() == SCI_VERSION_2_1) { itemEntry->y = (itemEntry->y * _screen->getHeight()) / scriptsRunningHeight; itemEntry->x = (itemEntry->x * _screen->getWidth()) / scriptsRunningWidth; @@ -497,8 +510,8 @@ void GfxFrameout::kernelFrameout() { itemEntry->celRect.bottom = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inBottom)) + 1; itemEntry->celRect.right = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inRight)) + 1; if (view->isSci2Hires()) { - _screen->adjustToUpscaledCoordinates(itemEntry->celRect.top, itemEntry->celRect.left); - _screen->adjustToUpscaledCoordinates(itemEntry->celRect.bottom, itemEntry->celRect.right); + view->adjustToUpscaledCoordinates(itemEntry->celRect.top, itemEntry->celRect.left); + view->adjustToUpscaledCoordinates(itemEntry->celRect.bottom, itemEntry->celRect.right); } itemEntry->celRect.translate(itemEntry->x, itemEntry->y); // TODO: maybe we should clip the cels rect with this, i'm not sure @@ -514,8 +527,8 @@ void GfxFrameout::kernelFrameout() { nsRect.translate(it->planeOffsetX, 0); if (view->isSci2Hires()) { - _screen->adjustBackUpscaledCoordinates(nsRect.top, nsRect.left); - _screen->adjustBackUpscaledCoordinates(nsRect.bottom, nsRect.right); + view->adjustBackUpscaledCoordinates(nsRect.top, nsRect.left); + view->adjustBackUpscaledCoordinates(nsRect.bottom, nsRect.right); } else if (getSciVersion() == SCI_VERSION_2_1) { nsRect.top = (nsRect.top * scriptsRunningHeight) / _screen->getHeight(); nsRect.left = (nsRect.left * scriptsRunningWidth) / _screen->getWidth(); @@ -584,6 +597,11 @@ void GfxFrameout::kernelFrameout() { uint16 startX = itemEntry->x + it->planeRect.left; uint16 curY = itemEntry->y + it->planeRect.top; const char *txt = text.c_str(); + // HACK. The plane sometimes doesn't contain the correct width. This + // hack breaks the dialog options when speaking with Grace, but it's + // the best we got up to now. This happens because of the unimplemented + // kTextWidth function in SCI32. + // TODO: Remove this once kTextWidth has been implemented. uint16 w = it->planeRect.width() >= 20 ? it->planeRect.width() : _screen->getWidth() - 10; int16 charCount; diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h index 93d61ba22e..347ecb9424 100644 --- a/engines/sci/graphics/frameout.h +++ b/engines/sci/graphics/frameout.h @@ -28,6 +28,8 @@ namespace Sci { +class GfxPicture; + struct PlaneEntry { reg_t object; uint16 priority; @@ -99,6 +101,7 @@ public: void addPlanePicture(reg_t object, GuiResourceId pictureId, uint16 startX); void deletePlanePictures(reg_t object); + void clear(); private: SegManager *_segMan; diff --git a/engines/sci/graphics/helpers.h b/engines/sci/graphics/helpers.h index f6cb214a2b..343f3c7e6e 100644 --- a/engines/sci/graphics/helpers.h +++ b/engines/sci/graphics/helpers.h @@ -45,6 +45,10 @@ typedef int GuiResourceId; // is a resource-number and -1 means no parameter giv typedef int16 TextAlignment; +#define PORTS_FIRSTWINDOWID 2 +#define PORTS_FIRSTSCRIPTWINDOWID 3 + + struct Port { uint16 id; int16 top, left; @@ -62,6 +66,8 @@ struct Port { fontHeight(0), fontId(0), greyedOutput(false), penClr(0), backClr(0xFF), penMode(0), counterTillFree(0) { } + + bool isWindow() const { return id >= PORTS_FIRSTWINDOWID && id != 0xFFFF; } }; struct Window : public Port, public Common::Serializable { @@ -132,12 +138,14 @@ struct PalSchedule { uint32 schedule; }; +// Game view types, sorted by the number of colors enum ViewType { - kViewUnknown, - kViewEga, - kViewVga, - kViewVga11, - kViewAmiga + kViewUnknown, // uninitialized, or non-SCI + kViewEga, // EGA SCI0/SCI1 and Amiga SCI0/SCI1 ECS 16 colors + kViewAmiga, // Amiga SCI1 ECS 32 colors + kViewAmiga64, // Amiga SCI1 AGA 64 colors (i.e. Longbow) + kViewVga, // VGA SCI1 256 colors + kViewVga11 // VGA SCI1.1 and newer 256 colors }; } // End of namespace Sci diff --git a/engines/sci/graphics/maciconbar.cpp b/engines/sci/graphics/maciconbar.cpp index 2ce17ab531..6cf4f269a7 100644 --- a/engines/sci/graphics/maciconbar.cpp +++ b/engines/sci/graphics/maciconbar.cpp @@ -27,8 +27,10 @@ #include "sci/engine/kernel.h" #include "sci/engine/selector.h" #include "sci/engine/state.h" +#include "sci/event.h" #include "sci/graphics/maciconbar.h" #include "sci/graphics/palette.h" +#include "sci/graphics/screen.h" #include "common/memstream.h" #include "common/system.h" @@ -37,37 +39,189 @@ namespace Sci { +GfxMacIconBar::GfxMacIconBar() { + _lastX = 0; + + if (g_sci->getGameId() == GID_FREDDYPHARKAS) + _inventoryIndex = 5; + else + _inventoryIndex = 4; + + _inventoryIcon = 0; + _allDisabled = true; +} + +GfxMacIconBar::~GfxMacIconBar() { + if (_inventoryIcon) { + _inventoryIcon->free(); + delete _inventoryIcon; + } + + for (uint32 i = 0; i < _iconBarItems.size(); i++) { + if (_iconBarItems[i].nonSelectedImage) { + _iconBarItems[i].nonSelectedImage->free(); + delete _iconBarItems[i].nonSelectedImage; + } + + if (_iconBarItems[i].selectedImage) { + _iconBarItems[i].selectedImage->free(); + delete _iconBarItems[i].selectedImage; + } + } +} + void GfxMacIconBar::addIcon(reg_t obj) { - _iconBarObjects.push_back(obj); + IconBarItem item; + uint32 iconIndex = readSelectorValue(g_sci->getEngineState()->_segMan, obj, SELECTOR(iconIndex)); + + item.object = obj; + item.nonSelectedImage = createImage(iconIndex, false); + + if (iconIndex != _inventoryIndex) + item.selectedImage = createImage(iconIndex, true); + else + item.selectedImage = 0; + + item.enabled = true; + + // Start after the main viewing window and add a two pixel buffer + uint16 y = g_sci->_gfxScreen->getHeight() + 2; + + if (item.nonSelectedImage) + item.rect = Common::Rect(_lastX, y, MIN<uint32>(_lastX + item.nonSelectedImage->w, 320), y + item.nonSelectedImage->h); + else + error("Could not find a non-selected image for icon %d", iconIndex); + + _lastX += item.rect.width(); + + _iconBarItems.push_back(item); } 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 < _iconBarItems.size(); i++) + drawIcon(i, false); +} + +void GfxMacIconBar::drawIcon(uint16 iconIndex, bool selected) { + if (iconIndex >= _iconBarItems.size()) + return; + + Common::Rect rect = _iconBarItems[iconIndex].rect; + + if (isIconEnabled(iconIndex)) { + if (selected) + drawEnabledImage(_iconBarItems[iconIndex].selectedImage, rect); + else + drawEnabledImage(_iconBarItems[iconIndex].nonSelectedImage, rect); + } else + drawDisabledImage(_iconBarItems[iconIndex].nonSelectedImage, rect); + + if ((iconIndex == _inventoryIndex) && _inventoryIcon) { + Common::Rect invRect = Common::Rect(0, 0, _inventoryIcon->w, _inventoryIcon->h); + invRect.moveTo(rect.left, rect.top); + invRect.translate((rect.width() - invRect.width()) / 2, (rect.height() - invRect.height()) / 2); + + if (isIconEnabled(iconIndex)) + drawEnabledImage(_inventoryIcon, invRect); + else + drawDisabledImage(_inventoryIcon, invRect); + } +} + +void GfxMacIconBar::drawEnabledImage(Graphics::Surface *surface, const Common::Rect &rect) { + if (surface) + g_system->copyRectToScreen((byte *)surface->pixels, surface->pitch, rect.left, rect.top, rect.width(), rect.height()); +} + +void GfxMacIconBar::drawDisabledImage(Graphics::Surface *surface, const Common::Rect &rect) { + if (!surface) + return; + + // Add a black checkboard pattern to the image before copying it to the screen + + Graphics::Surface newSurf; + newSurf.copyFrom(*surface); + + for (int i = 0; i < newSurf.h; i++) { + // Start at the next four byte boundary + int startX = 3 - ((rect.left + 3) & 3); + + // Start odd rows at two bytes past that (also properly aligned) + if ((i + rect.top) & 1) + startX = (startX + 2) & 3; + + for (int j = startX; j < newSurf.w; j += 4) + *((byte *)newSurf.getBasePtr(j, i)) = 0; + } + + g_system->copyRectToScreen((byte *)newSurf.pixels, newSurf.pitch, rect.left, rect.top, rect.width(), rect.height()); + newSurf.free(); +} + +void GfxMacIconBar::drawSelectedImage(uint16 iconIndex) { + assert(iconIndex <= _iconBarItems.size()); + + drawEnabledImage(_iconBarItems[iconIndex].selectedImage, _iconBarItems[iconIndex].rect); +} - 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; +bool GfxMacIconBar::isIconEnabled(uint16 iconIndex) const { + if (iconIndex >= _iconBarItems.size()) + return false; + + return !_allDisabled && _iconBarItems[iconIndex].enabled; +} + +void GfxMacIconBar::setIconEnabled(int16 iconIndex, bool enabled) { + if (iconIndex < 0) + _allDisabled = !enabled; + else if (iconIndex < (int)_iconBarItems.size()) { + _iconBarItems[iconIndex].enabled = enabled; + } +} - Common::SeekableReadStream *stream = new Common::MemoryReadStream(res->data, res->size); - Graphics::Surface *surf = pict->decodeImage(stream, pal); - remapColors(surf, pal); +void GfxMacIconBar::setInventoryIcon(int16 icon) { + Graphics::Surface *surface = 0; - g_system->copyRectToScreen((byte *)surf->pixels, surf->pitch, lastX, 200, MIN<uint32>(surf->w, 320 - lastX), surf->h); + if (icon >= 0) + surface = loadPict(ResourceId(kResourceTypeMacPict, icon)); - lastX += surf->w; - surf->free(); - delete surf; - delete stream; + if (_inventoryIcon) { + // Free old inventory icon if we're removing the inventory icon + // or setting a new one. + if ((icon < 0) || surface) { + _inventoryIcon->free(); + delete _inventoryIcon; + _inventoryIcon = 0; + } } - delete pict; - delete[] pal; + if (surface) + _inventoryIcon = surface; + + drawIcon(_inventoryIndex, false); +} + +Graphics::Surface *GfxMacIconBar::loadPict(ResourceId id) { + Graphics::PictDecoder pictDecoder(Graphics::PixelFormat::createFormatCLUT8()); + Resource *res = g_sci->getResMan()->findResource(id, false); + + if (!res || res->size == 0) + return 0; + + byte palette[256 * 3]; + Common::SeekableReadStream *stream = new Common::MemoryReadStream(res->data, res->size); + Graphics::Surface *surface = pictDecoder.decodeImage(stream, palette); + remapColors(surface, palette); + + delete stream; + return surface; +} + +Graphics::Surface *GfxMacIconBar::createImage(uint32 iconIndex, bool isSelected) { + ResourceType type = isSelected ? kResourceTypeMacIconBarPictS : kResourceTypeMacIconBarPictN; + return loadPict(ResourceId(type, iconIndex + 1)); } void GfxMacIconBar::remapColors(Graphics::Surface *surf, byte *palette) { @@ -77,16 +231,67 @@ void GfxMacIconBar::remapColors(Graphics::Surface *surf, byte *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]; + byte r = palette[color * 3]; + byte g = palette[color * 3 + 1]; + byte b = palette[color * 3 + 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); + *pixels++ = g_sci->_gfxPalette->findMacIconBarColor(r, g, b); + } +} + +bool GfxMacIconBar::pointOnIcon(uint32 iconIndex, Common::Point point) { + return _iconBarItems[iconIndex].rect.contains(point); +} + +reg_t GfxMacIconBar::handleEvents() { + // Peek event queue for a mouse button press + EventManager *evtMgr = g_sci->getEventManager(); + SciEvent evt = evtMgr->getSciEvent(SCI_EVENT_MOUSE_PRESS | SCI_EVENT_PEEK); + + // No mouse press found + if (evt.type == SCI_EVENT_NONE) + return NULL_REG; + + // If the mouse is not over the icon bar, return + if (evt.mousePos.y < g_sci->_gfxScreen->getHeight()) + return NULL_REG; + + // Remove event from queue + evtMgr->getSciEvent(SCI_EVENT_MOUSE_PRESS); + + // Mouse press on the icon bar, check the icon rectangles + uint iconNr; + for (iconNr = 0; iconNr < _iconBarItems.size(); iconNr++) { + if (pointOnIcon(iconNr, evt.mousePos) && isIconEnabled(iconNr)) + break; } + + // Mouse press not on an icon + if (iconNr == _iconBarItems.size()) + return NULL_REG; + + drawIcon(iconNr, true); + bool isSelected = true; + + // Wait for mouse release + while (evt.type != SCI_EVENT_MOUSE_RELEASE) { + // Mimic behavior of SSCI when moving mouse with button held down + if (isSelected != pointOnIcon(iconNr, evt.mousePos)) { + isSelected = !isSelected; + drawIcon(iconNr, isSelected); + } + + evt = evtMgr->getSciEvent(SCI_EVENT_MOUSE_RELEASE); + g_system->delayMillis(10); + } + + drawIcon(iconNr, false); + + // If user moved away from the icon, we do nothing + if (pointOnIcon(iconNr, evt.mousePos)) + return _iconBarItems[iconNr].object; + + return NULL_REG; } } // End of namespace Sci diff --git a/engines/sci/graphics/maciconbar.h b/engines/sci/graphics/maciconbar.h index 71e65fcb40..3ac5475147 100644 --- a/engines/sci/graphics/maciconbar.h +++ b/engines/sci/graphics/maciconbar.h @@ -38,16 +38,40 @@ namespace Sci { class GfxMacIconBar { public: - GfxMacIconBar() {} - ~GfxMacIconBar() {} + GfxMacIconBar(); + ~GfxMacIconBar(); void addIcon(reg_t obj); void drawIcons(); + void setIconEnabled(int16 index, bool enabled); + void setInventoryIcon(int16 icon); + reg_t handleEvents(); private: - Common::Array<reg_t> _iconBarObjects; + struct IconBarItem { + reg_t object; + Graphics::Surface *nonSelectedImage; + Graphics::Surface *selectedImage; + Common::Rect rect; + bool enabled; + }; + Common::Array<IconBarItem> _iconBarItems; + uint32 _lastX; + uint16 _inventoryIndex; + Graphics::Surface *_inventoryIcon; + bool _allDisabled; + + Graphics::Surface *loadPict(ResourceId id); + Graphics::Surface *createImage(uint32 iconIndex, bool isSelected); void remapColors(Graphics::Surface *surf, byte *palette); + + void drawIcon(uint16 index, bool selected); + void drawSelectedImage(uint16 index); + bool isIconEnabled(uint16 index) const; + void drawEnabledImage(Graphics::Surface *surface, const Common::Rect &rect); + void drawDisabledImage(Graphics::Surface *surface, const Common::Rect &rect); + bool pointOnIcon(uint32 iconIndex, Common::Point point); }; } // End of namespace Sci diff --git a/engines/sci/graphics/menu.cpp b/engines/sci/graphics/menu.cpp index 3b9119c52f..50ba77e832 100644 --- a/engines/sci/graphics/menu.cpp +++ b/engines/sci/graphics/menu.cpp @@ -399,12 +399,10 @@ void GfxMenu::calculateMenuAndItemWidth() { reg_t GfxMenu::kernelSelect(reg_t eventObject, bool pauseSound) { int16 eventType = readSelectorValue(_segMan, eventObject, SELECTOR(type)); int16 keyPress, keyModifier; - Common::Point mousePosition; GuiMenuItemList::iterator itemIterator = _itemList.begin(); GuiMenuItemList::iterator itemEnd = _itemList.end(); GuiMenuItemEntry *itemEntry = NULL; bool forceClaimed = false; - EngineState *s; switch (eventType) { case SCI_EVENT_KEYBOARD: @@ -438,8 +436,6 @@ reg_t GfxMenu::kernelSelect(reg_t eventObject, bool pauseSound) { break; case SCI_EVENT_SAID: - // HACK: should be removed as soon as said() is cleaned up - s = g_sci->getEngineState(); while (itemIterator != itemEnd) { itemEntry = *itemIterator; @@ -451,7 +447,7 @@ reg_t GfxMenu::kernelSelect(reg_t eventObject, bool pauseSound) { continue; } - if (said(s, saidSpec, 0) != SAID_NO_MATCH) + if (said(saidSpec, 0) != SAID_NO_MATCH) break; } itemIterator++; @@ -460,15 +456,17 @@ reg_t GfxMenu::kernelSelect(reg_t eventObject, bool pauseSound) { itemEntry = NULL; break; - case SCI_EVENT_MOUSE_PRESS: - mousePosition = _cursor->getPosition(); + case SCI_EVENT_MOUSE_PRESS: { + Common::Point mousePosition; + mousePosition.x = readSelectorValue(_segMan, eventObject, SELECTOR(x)); + mousePosition.y = readSelectorValue(_segMan, eventObject, SELECTOR(y)); if (mousePosition.y < 10) { interactiveStart(pauseSound); itemEntry = interactiveWithMouse(); interactiveEnd(pauseSound); forceClaimed = true; } - break; + } break; } if (!_menuSaveHandle.isNull()) { @@ -718,7 +716,6 @@ GuiMenuItemEntry *GfxMenu::interactiveWithKeyboard() { uint16 newItemId = _curItemId; GuiMenuItemEntry *curItemEntry = findItem(_curMenuId, _curItemId); GuiMenuItemEntry *newItemEntry = curItemEntry; - Common::Point mousePosition; // We don't 100% follow Sierra here: we select last item instead of // selecting first item of first menu every time. Also sierra sci didn't @@ -796,9 +793,9 @@ GuiMenuItemEntry *GfxMenu::interactiveWithKeyboard() { } break; - case SCI_EVENT_MOUSE_PRESS: - mousePosition = _cursor->getPosition(); - if (_cursor->getPosition().y < 10) { + case SCI_EVENT_MOUSE_PRESS: { + Common::Point mousePosition = curEvent.mousePos; + if (mousePosition.y < 10) { // Somewhere on the menubar newMenuId = mouseFindMenuSelection(mousePosition); if (newMenuId) { @@ -827,7 +824,8 @@ GuiMenuItemEntry *GfxMenu::interactiveWithKeyboard() { } newItemId = curItemEntry->id; } - break; + } break; + case SCI_EVENT_NONE: g_sci->sleep(2500 / 1000); break; @@ -843,7 +841,6 @@ GuiMenuItemEntry *GfxMenu::interactiveWithMouse() { SciEvent curEvent; uint16 newMenuId = 0, newItemId = 0; uint16 curMenuId = 0, curItemId = 0; - Common::Point mousePosition = _cursor->getPosition(); bool firstMenuChange = true; GuiMenuItemEntry *curItemEntry = NULL; @@ -874,7 +871,7 @@ GuiMenuItemEntry *GfxMenu::interactiveWithMouse() { } // Find out where mouse is currently pointing to - mousePosition = _cursor->getPosition(); + Common::Point mousePosition = curEvent.mousePos; if (mousePosition.y < 10) { // Somewhere on the menubar newMenuId = mouseFindMenuSelection(mousePosition); @@ -914,6 +911,13 @@ void GfxMenu::kernelDrawStatus(const char *text, int16 colorPen, int16 colorBack _ports->moveTo(0, 1); _text16->DrawStatus(text); _paint16->bitsShow(_ports->_menuBarRect); + // Also draw the line under the status bar. Normally, this is never drawn, + // but we need it to be drawn because Dr. Brain 1 Mac draws over it when + // it displays the icon bar. SSCI used negative rectangles to erase the + // area after drawing the icon bar, but this is a much cleaner way of + // achieving the same effect. + _paint16->fillRect(_ports->_menuLine, 1, 0); + _paint16->bitsShow(_ports->_menuLine); _ports->setPort(oldPort); } diff --git a/engines/sci/graphics/paint.cpp b/engines/sci/graphics/paint.cpp index 50b0534ba7..c347da3c0f 100644 --- a/engines/sci/graphics/paint.cpp +++ b/engines/sci/graphics/paint.cpp @@ -43,9 +43,6 @@ GfxPaint::~GfxPaint() { void GfxPaint::kernelDrawPicture(GuiResourceId pictureId, int16 animationNr, bool animationBlackoutFlag, bool mirroredFlag, bool addToFlag, int16 EGApaletteNo) { } -void GfxPaint::kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, bool hiresMode, reg_t upscaledHiresHandle) { -} - void GfxPaint::kernelGraphDrawLine(Common::Point startPoint, Common::Point endPoint, int16 color, int16 priority, int16 control) { } diff --git a/engines/sci/graphics/paint.h b/engines/sci/graphics/paint.h index 994bc4e5e9..a79e8993c2 100644 --- a/engines/sci/graphics/paint.h +++ b/engines/sci/graphics/paint.h @@ -36,7 +36,6 @@ public: virtual ~GfxPaint(); virtual void kernelDrawPicture(GuiResourceId pictureId, int16 animationNr, bool animationBlackoutFlag, bool mirroredFlag, bool addToFlag, int16 EGApaletteNo); - virtual void kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, bool hiresMode, reg_t upscaledHiresHandle); virtual void kernelGraphDrawLine(Common::Point startPoint, Common::Point endPoint, int16 color, int16 priority, int16 control); }; diff --git a/engines/sci/graphics/paint16.cpp b/engines/sci/graphics/paint16.cpp index 0bb3fc85c5..ff738fc3b9 100644 --- a/engines/sci/graphics/paint16.cpp +++ b/engines/sci/graphics/paint16.cpp @@ -121,11 +121,10 @@ void GfxPaint16::drawCel(GfxView *view, int16 loopNo, int16 celNo, const Common: Common::Rect clipRectTranslated = clipRect; _ports->offsetRect(clipRectTranslated); - if (scaleX == 128 && scaleY == 128) { + if (scaleX == 128 && scaleY == 128) view->draw(celRect, clipRect, clipRectTranslated, loopNo, celNo, priority, paletteNo, false); - } else { + else view->drawScaled(celRect, clipRect, clipRectTranslated, loopNo, celNo, priority, scaleX, scaleY); - } } // This is used as replacement for drawCelAndShow() when hires-cels are drawn to @@ -160,8 +159,8 @@ void GfxPaint16::drawHiresCelAndShow(GuiResourceId viewId, int16 loopNo, int16 c // adjust curPort to upscaled hires clipRect = celRect; curPortRect = _ports->_curPort->rect; - _screen->adjustToUpscaledCoordinates(curPortRect.top, curPortRect.left); - _screen->adjustToUpscaledCoordinates(curPortRect.bottom, curPortRect.right); + view->adjustToUpscaledCoordinates(curPortRect.top, curPortRect.left); + view->adjustToUpscaledCoordinates(curPortRect.bottom, curPortRect.right); curPortRect.bottom++; curPortRect.right++; clipRect.clip(curPortRect); @@ -171,7 +170,7 @@ void GfxPaint16::drawHiresCelAndShow(GuiResourceId viewId, int16 loopNo, int16 c clipRectTranslated = clipRect; if (!upscaledHiresHack) { curPortPos.x = _ports->_curPort->left; curPortPos.y = _ports->_curPort->top; - _screen->adjustToUpscaledCoordinates(curPortPos.y, curPortPos.x); + view->adjustToUpscaledCoordinates(curPortPos.y, curPortPos.x); clipRectTranslated.top += curPortPos.y; clipRectTranslated.bottom += curPortPos.y; clipRectTranslated.left += curPortPos.x; clipRectTranslated.right += curPortPos.x; } @@ -278,9 +277,8 @@ void GfxPaint16::fillRect(const Common::Rect &rect, int16 drawFlags, byte color, } void GfxPaint16::frameRect(const Common::Rect &rect) { - Common::Rect r; + Common::Rect r = rect; // left - r = rect; r.right = rect.left + 1; paintRect(r); // right @@ -362,7 +360,7 @@ void GfxPaint16::bitsRestore(reg_t memoryHandle) { if (memoryPtr) { _screen->bitsRestore(memoryPtr); - _segMan->freeHunkEntry(memoryHandle); + bitsFree(memoryHandle); } } } @@ -534,20 +532,7 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) { case SCI_DISPLAY_RESTOREUNDER: bitsGetRect(argv[0], &rect); rect.translate(-_ports->getPort()->left, -_ports->getPort()->top); - if (g_sci->getGameId() == GID_PQ3 && g_sci->getEngineState()->currentRoomNumber() == 29) { - // WORKAROUND: PQ3 calls this without calling the associated - // kDisplay(SCI_DISPLAY_SAVEUNDER) call before. Theoretically, - // this would result in no rect getting restored. However, we - // still maintain a pointer from the previous room, resulting - // in invalidated content being restored on screen, and causing - // graphics glitches. Thus, we simply don't restore a rect in - // that room. The correct fix for this would be to erase hunk - // pointers when changing rooms, but this will suffice for now, - // as restoring from a totally invalid pointer is very rare. - // Fixes bug #3037945. - } else { - bitsRestore(argv[0]); - } + bitsRestore(argv[0]); kernelGraphRedrawBox(rect); // finishing loop argc = 0; @@ -583,7 +568,10 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) { // now drawing the text _text16->Size(rect, text, -1, width); rect.moveTo(_ports->getPort()->curLeft, _ports->getPort()->curTop); - if (getSciVersion() >= SCI_VERSION_1_LATE) { + // Note: This code has been found in SCI1 middle and newer games. It was + // previously only for SCI1 late and newer, but the LSL1 interpreter contains + // this code. + if (getSciVersion() >= SCI_VERSION_1_MIDDLE) { int16 leftPos = rect.right <= _screen->getWidth() ? 0 : _screen->getWidth() - rect.right; int16 topPos = rect.bottom <= _screen->getHeight() ? 0 : _screen->getHeight() - rect.bottom; _ports->move(leftPos, topPos); diff --git a/engines/sci/graphics/paint16.h b/engines/sci/graphics/paint16.h index 4f709fd7c6..69ddf09ea6 100644 --- a/engines/sci/graphics/paint16.h +++ b/engines/sci/graphics/paint16.h @@ -36,7 +36,6 @@ class GfxPorts; class GfxScreen; class GfxPalette; class Font; -class SciGuiPicture; class GfxView; /** diff --git a/engines/sci/graphics/paint32.cpp b/engines/sci/graphics/paint32.cpp index aa3bf8dfb3..69a278eb8b 100644 --- a/engines/sci/graphics/paint32.cpp +++ b/engines/sci/graphics/paint32.cpp @@ -65,17 +65,6 @@ void GfxPaint32::kernelDrawPicture(GuiResourceId pictureId, int16 animationNr, b delete picture; } -// This is "hacked" together, because its only used by debug command -void GfxPaint32::kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, bool hiresMode, reg_t upscaledHiresHandle) { - GfxView *view = _cache->getView(viewId); - Common::Rect celRect(50, 50, 50, 50); - Common::Rect translatedRect; - celRect.bottom += view->getHeight(loopNo, celNo); - celRect.right += view->getWidth(loopNo, celNo); - view->draw(celRect, celRect, celRect, loopNo, celNo, 255, 0, false); - _screen->copyRectToScreen(celRect); -} - void GfxPaint32::kernelGraphDrawLine(Common::Point startPoint, Common::Point endPoint, int16 color, int16 priority, int16 control) { _screen->drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y, color, priority, control); } diff --git a/engines/sci/graphics/paint32.h b/engines/sci/graphics/paint32.h index 3fa9d11d9b..e412bdf1c4 100644 --- a/engines/sci/graphics/paint32.h +++ b/engines/sci/graphics/paint32.h @@ -45,7 +45,6 @@ public: void fillRect(Common::Rect rect, byte color); void kernelDrawPicture(GuiResourceId pictureId, int16 animationNr, bool animationBlackoutFlag, bool mirroredFlag, bool addToFlag, int16 EGApaletteNo); - void kernelDrawCel(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, int16 priority, uint16 paletteNo, bool hiresMode, reg_t upscaledHiresHandle); void kernelGraphDrawLine(Common::Point startPoint, Common::Point endPoint, int16 color, int16 priority, int16 control); private: diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp index b27b5f35a7..0433479a09 100644 --- a/engines/sci/graphics/palette.cpp +++ b/engines/sci/graphics/palette.cpp @@ -69,16 +69,39 @@ GfxPalette::GfxPalette(ResourceManager *resMan, GfxScreen *screen, bool useMergi _useMerging = useMerging; palVaryInit(); + + _macClut = 0; + loadMacIconBarPalette(); #ifdef ENABLE_SCI32 _clutTable = 0; #endif + + switch (_resMan->getViewType()) { + case kViewEga: + _totalScreenColors = 16; + break; + case kViewAmiga: + _totalScreenColors = 32; + break; + case kViewAmiga64: + _totalScreenColors = 64; + break; + case kViewVga: + case kViewVga11: + _totalScreenColors = 256; + break; + default: + error("GfxPalette: Unknown view type"); + } } GfxPalette::~GfxPalette() { if (_palVaryResourceId != -1) palVaryRemoveTimer(); + delete[] _macClut; + #ifdef ENABLE_SCI32 unloadClut(); #endif @@ -92,7 +115,7 @@ bool GfxPalette::isMerging() { void GfxPalette::setDefault() { if (_resMan->getViewType() == kViewEga) setEGA(); - else if (_resMan->isAmiga32color()) + else if (_resMan->getViewType() == kViewAmiga) setAmiga(); else kernelSetFromResource(999, true); @@ -171,39 +194,42 @@ void GfxPalette::createFromData(byte *data, int bytesLeft, Palette *paletteOut) // Will try to set amiga palette by using "spal" file. If not found, we return false bool GfxPalette::setAmiga() { Common::File file; - int curColor, byte1, byte2; if (file.open("spal")) { - for (curColor = 0; curColor < 32; curColor++) { - byte1 = file.readByte(); - byte2 = file.readByte(); - if ((byte1 == EOF) || (byte2 == EOF)) + for (int curColor = 0; curColor < 32; curColor++) { + byte byte1 = file.readByte(); + byte byte2 = file.readByte(); + + if (file.eos()) error("Amiga palette file ends prematurely"); + _sysPalette.colors[curColor].used = 1; _sysPalette.colors[curColor].r = (byte1 & 0x0F) * 0x11; _sysPalette.colors[curColor].g = ((byte2 & 0xF0) >> 4) * 0x11; _sysPalette.colors[curColor].b = (byte2 & 0x0F) * 0x11; } - file.close(); + // Directly set the palette, because setOnScreen() wont do a thing for amiga - _screen->setPalette(&_sysPalette); + copySysPaletteToScreen(); return true; } + return false; } // Called from picture class, some amiga sci1 games set half of the palette void GfxPalette::modifyAmigaPalette(byte *data) { - int16 curColor, curPos = 0; - byte byte1, byte2; - for (curColor = 0; curColor < 16; curColor++) { - byte1 = data[curPos++]; - byte2 = data[curPos++]; + int16 curPos = 0; + + for (int curColor = 0; curColor < 16; curColor++) { + byte byte1 = data[curPos++]; + byte byte2 = data[curPos++]; _sysPalette.colors[curColor].r = (byte1 & 0x0F) * 0x11; _sysPalette.colors[curColor].g = ((byte2 & 0xF0) >> 4) * 0x11; _sysPalette.colors[curColor].b = (byte2 & 0x0F) * 0x11; } - _screen->setPalette(&_sysPalette); + + copySysPaletteToScreen(); } static byte blendColors(byte c1, byte c2) { @@ -297,19 +323,20 @@ bool GfxPalette::insert(Palette *newPalette, Palette *destPalette) { newPalette->mapping[i] = i; } } + // We don't update the timestamp for SCI1.1, it's only updated on kDrawPic calls return paletteChanged; } bool GfxPalette::merge(Palette *newPalette, bool force, bool forceRealMerge) { uint16 res; - int i,j; bool paletteChanged = false; - // colors 0 (black) and 255 (white) are not affected by merging - for (i = 1; i < 255; i++) { - if (!newPalette->colors[i].used)// color is not used - so skip it + for (int i = 1; i < 255; i++) { + // skip unused colors + if (!newPalette->colors[i].used) continue; + // forced palette merging or dest color is not used yet if (force || (!_sysPalette.colors[i].used)) { _sysPalette.colors[i].used = newPalette->colors[i].used; @@ -322,6 +349,7 @@ bool GfxPalette::merge(Palette *newPalette, bool force, bool forceRealMerge) { newPalette->mapping[i] = i; continue; } + // is the same color already at the same position? -> match it directly w/o lookup // this fixes games like lsl1demo/sq5 where the same rgb color exists multiple times and where we would // otherwise match the wrong one (which would result into the pixels affected (or not) by palette changes) @@ -329,14 +357,18 @@ bool GfxPalette::merge(Palette *newPalette, bool force, bool forceRealMerge) { newPalette->mapping[i] = i; continue; } + // check if exact color could be matched res = matchColor(newPalette->colors[i].r, newPalette->colors[i].g, newPalette->colors[i].b); if (res & 0x8000) { // exact match was found newPalette->mapping[i] = res & 0xFF; continue; } + + int j = 1; + // no exact match - see if there is an unused color - for (j = 1; j < 256; j++) + for (; j < 256; j++) { if (!_sysPalette.colors[j].used) { _sysPalette.colors[j].used = newPalette->colors[i].used; _sysPalette.colors[j].r = newPalette->colors[i].r; @@ -346,6 +378,8 @@ bool GfxPalette::merge(Palette *newPalette, bool force, bool forceRealMerge) { paletteChanged = true; break; } + } + // if still no luck - set an approximate color if (j == 256) { newPalette->mapping[i] = res & 0xFF; @@ -355,6 +389,7 @@ bool GfxPalette::merge(Palette *newPalette, bool force, bool forceRealMerge) { if (!forceRealMerge) _sysPalette.timestamp = g_system->getMillis() * 60 / 1000; + return paletteChanged; } @@ -401,14 +436,35 @@ void GfxPalette::getSys(Palette *pal) { } void GfxPalette::setOnScreen() { - // We dont change palette at all times for amiga - if (_resMan->isAmiga32color()) - return; - _screen->setPalette(&_sysPalette); + copySysPaletteToScreen(); +} + +static byte convertMacGammaToSCIGamma(int comp) { + return (byte)sqrt(comp * 255.0f); +} - // Redraw the Mac SCI1.1 Icon bar every palette change - if (g_sci->_gfxMacIconBar) - g_sci->_gfxMacIconBar->drawIcons(); +void GfxPalette::copySysPaletteToScreen() { + // just copy palette to system + byte bpal[3 * 256]; + + // Get current palette, update it and put back + g_system->getPaletteManager()->grabPalette(bpal, 0, 256); + + for (int16 i = 0; i < 256; i++) { + if (colorIsFromMacClut(i)) { + // If we've got a Mac CLUT, override the SCI palette with its non-black colors + bpal[i * 3 ] = convertMacGammaToSCIGamma(_macClut[i * 3 ]); + bpal[i * 3 + 1] = convertMacGammaToSCIGamma(_macClut[i * 3 + 1]); + bpal[i * 3 + 2] = convertMacGammaToSCIGamma(_macClut[i * 3 + 2]); + } else if (_sysPalette.colors[i].used != 0) { + // Otherwise, copy to the screen + bpal[i * 3 ] = CLIP(_sysPalette.colors[i].r * _sysPalette.intensity[i] / 100, 0, 255); + bpal[i * 3 + 1] = CLIP(_sysPalette.colors[i].g * _sysPalette.intensity[i] / 100, 0, 255); + bpal[i * 3 + 2] = CLIP(_sysPalette.colors[i].b * _sysPalette.intensity[i] / 100, 0, 255); + } + } + + g_system->getPaletteManager()->setPalette(bpal, 0, 256); } bool GfxPalette::kernelSetFromResource(GuiResourceId resourceId, bool force) { @@ -420,6 +476,7 @@ bool GfxPalette::kernelSetFromResource(GuiResourceId resourceId, bool force) { set(&palette, force); return true; } + return false; } @@ -565,7 +622,16 @@ void GfxPalette::kernelAssertPalette(GuiResourceId resourceId) { } void GfxPalette::kernelSyncScreenPalette() { - _screen->getPalette(&_sysPalette); + // just copy palette to system + byte bpal[3 * 256]; + + // Get current palette, update it and put back + g_system->getPaletteManager()->grabPalette(bpal, 0, 256); + for (int16 i = 1; i < 255; i++) { + _sysPalette.colors[i].r = bpal[i * 3]; + _sysPalette.colors[i].g = bpal[i * 3 + 1]; + _sysPalette.colors[i].b = bpal[i * 3 + 2]; + } } // palVary @@ -795,6 +861,87 @@ void GfxPalette::palVaryProcess(int signal, bool setPalette) { } } +static inline uint getMacColorDiff(byte r1, byte g1, byte b1, byte r2, byte g2, byte b2) { + // Use the difference of the top 4 bits and add together the differences + return ABS((r2 & 0xf0) - (r1 & 0xf0)) + ABS((g2 & 0xf0) - (g1 & 0xf0)) + ABS((b2 & 0xf0) - (b1 & 0xf0)); +} + +byte GfxPalette::findMacIconBarColor(byte r, byte g, byte b) { + // Find the best color for use with the Mac icon bar + // Check white, then the palette colors, and then black + + // Try white first + byte found = 0xff; + uint diff = getMacColorDiff(r, g, b, 0xff, 0xff, 0xff); + + if (diff == 0) + return found; + + // Go through the main colors of the CLUT + for (uint16 i = 1; i < 255; i++) { + if (!colorIsFromMacClut(i)) + continue; + + uint cdiff = getMacColorDiff(r, g, b, _macClut[i * 3], _macClut[i * 3 + 1], _macClut[i * 3 + 2]); + + if (cdiff == 0) + return i; + else if (cdiff < diff) { + found = i; + diff = cdiff; + } + } + + // Also check black here + if (getMacColorDiff(r, g, b, 0, 0, 0) < diff) + return 0; + + return found; +} + +void GfxPalette::loadMacIconBarPalette() { + if (!g_sci->hasMacIconBar()) + return; + + Common::SeekableReadStream *clutStream = g_sci->getMacExecutable()->getResource(MKTAG('c','l','u','t'), 150); + + if (!clutStream) + error("Could not find clut 150 for the Mac icon bar"); + + clutStream->readUint32BE(); // seed + clutStream->readUint16BE(); // flags + uint16 colorCount = clutStream->readUint16BE() + 1; + assert(colorCount == 256); + + _macClut = new byte[256 * 3]; + + for (uint16 i = 0; i < colorCount; i++) { + clutStream->readUint16BE(); + _macClut[i * 3 ] = clutStream->readUint16BE() >> 8; + _macClut[i * 3 + 1] = clutStream->readUint16BE() >> 8; + _macClut[i * 3 + 2] = clutStream->readUint16BE() >> 8; + } + + // Adjust bounds on the KQ6 palette + // We don't use all of it for the icon bar + if (g_sci->getGameId() == GID_KQ6) + memset(_macClut + 32 * 3, 0, (256 - 32) * 3); + + // Force black/white + _macClut[0x00 * 3 ] = 0; + _macClut[0x00 * 3 + 1] = 0; + _macClut[0x00 * 3 + 2] = 0; + _macClut[0xff * 3 ] = 0xff; + _macClut[0xff * 3 + 1] = 0xff; + _macClut[0xff * 3 + 2] = 0xff; + + delete clutStream; +} + +bool GfxPalette::colorIsFromMacClut(byte index) { + return index != 0 && _macClut && (_macClut[index * 3] != 0 || _macClut[index * 3 + 1] != 0 || _macClut[index * 3 + 1] != 0); +} + #ifdef ENABLE_SCI32 bool GfxPalette::loadClut(uint16 clutId) { diff --git a/engines/sci/graphics/palette.h b/engines/sci/graphics/palette.h index 84334a4a61..d2e5151d6a 100644 --- a/engines/sci/graphics/palette.h +++ b/engines/sci/graphics/palette.h @@ -54,8 +54,10 @@ public: bool merge(Palette *pFrom, bool force, bool forceRealMerge); uint16 matchColor(byte r, byte g, byte b); void getSys(Palette *pal); + uint16 getTotalColorCount() const { return _totalScreenColors; } void setOnScreen(); + void copySysPaletteToScreen(); void drewPicture(GuiResourceId pictureId); @@ -88,6 +90,9 @@ public: virtual void saveLoadWithSerializer(Common::Serializer &s); void palVarySaveLoadPalette(Common::Serializer &s, Palette *palette); + byte findMacIconBarColor(byte r, byte g, byte b); + bool colorIsFromMacClut(byte index); + #ifdef ENABLE_SCI32 bool loadClut(uint16 clutId); byte matchClutColor(uint16 color); @@ -119,6 +124,10 @@ private: uint16 _palVaryTicks; int _palVaryPaused; int _palVarySignal; + uint16 _totalScreenColors; + + void loadMacIconBarPalette(); + byte *_macClut; #ifdef ENABLE_SCI32 byte *_clutTable; diff --git a/engines/sci/graphics/picture.cpp b/engines/sci/graphics/picture.cpp index 7e71c1a258..82aae5399f 100644 --- a/engines/sci/graphics/picture.cpp +++ b/engines/sci/graphics/picture.cpp @@ -36,6 +36,8 @@ namespace Sci { +//#define DEBUG_PICTURE_DRAW + GfxPicture::GfxPicture(ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, GfxPorts *ports, GfxScreen *screen, GfxPalette *palette, GuiResourceId resourceId, bool EGAdrawingVisualize) : _resMan(resMan), _coordAdjuster(coordAdjuster), _ports(ports), _screen(screen), _palette(palette), _resourceId(resourceId), _EGAdrawingVisualize(EGAdrawingVisualize) { assert(resourceId != -1); @@ -222,19 +224,20 @@ void GfxPicture::drawSci32Vga(int16 celNo, int16 drawX, int16 drawY, int16 pictu } #endif +extern void unpackCelData(byte *inBuffer, byte *celBitmap, byte clearColor, int pixelCount, int rlePos, int literalPos, ViewType viewType, uint16 width, bool isMacSci11ViewData); + void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos, int literalPos, int16 drawX, int16 drawY, int16 pictureX) { byte *celBitmap = NULL; byte *ptr = NULL; byte *headerPtr = inbuffer + headerPos; byte *rlePtr = inbuffer + rlePos; - byte *literalPtr = inbuffer + literalPos; int16 displaceX, displaceY; byte priority = _addToFlag ? _priority : 0; byte clearColor; bool compression = true; - byte curByte, runLength; + byte curByte; int16 y, lastY, x, leftX, rightX; - int pixelNr, pixelCount; + int pixelCount; uint16 width, height; #ifdef ENABLE_SCI32 @@ -245,12 +248,11 @@ void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos height = READ_LE_UINT16(headerPtr + 2); displaceX = (signed char)headerPtr[4]; displaceY = (unsigned char)headerPtr[5]; - if (_resourceType == SCI_PICTURE_TYPE_SCI11) { + if (_resourceType == SCI_PICTURE_TYPE_SCI11) // SCI1.1 uses hardcoded clearcolor for pictures, even if cel header specifies otherwise clearColor = _screen->getColorWhite(); - } else { + else clearColor = headerPtr[6]; - } #ifdef ENABLE_SCI32 } else { width = READ_SCI11ENDIAN_UINT16(headerPtr + 0); @@ -266,90 +268,37 @@ void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos if (displaceX || displaceY) error("unsupported embedded cel-data in picture"); + // We will unpack cel-data into a temporary buffer and then plot it to screen + // That needs to be done cause a mirrored picture may be requested pixelCount = width * height; celBitmap = new byte[pixelCount]; if (!celBitmap) error("Unable to allocate temporary memory for picture drawing"); - if (compression) { - // We will unpack cel-data into a temporary buffer and then plot it to screen - // That needs to be done cause a mirrored picture may be requested - memset(celBitmap, clearColor, pixelCount); - pixelNr = 0; - ptr = celBitmap; - if (literalPos == 0) { - // decompression for data that has only one stream (vecor embedded view data) - switch (_resMan->getViewType()) { - case kViewEga: - while (pixelNr < pixelCount) { - curByte = *rlePtr++; - runLength = curByte >> 4; - memset(ptr + pixelNr, curByte & 0x0F, MIN<uint16>(runLength, pixelCount - pixelNr)); - pixelNr += runLength; - } - break; - case kViewVga: - case kViewVga11: - while (pixelNr < pixelCount) { - curByte = *rlePtr++; - runLength = curByte & 0x3F; - switch (curByte & 0xC0) { - case 0: // copy bytes as-is - while (runLength-- && pixelNr < pixelCount) - ptr[pixelNr++] = *rlePtr++; - break; - case 0x80: // fill with color - memset(ptr + pixelNr, *rlePtr++, MIN<uint16>(runLength, pixelCount - pixelNr)); - pixelNr += runLength; - break; - case 0xC0: // fill with transparent - pixelNr += runLength; - break; - } - } - break; - case kViewAmiga: - while (pixelNr < pixelCount) { - curByte = *rlePtr++; - if (curByte & 0x07) { // fill with color - runLength = curByte & 0x07; - curByte = curByte >> 3; - while (runLength-- && pixelNr < pixelCount) { - ptr[pixelNr++] = curByte; - } - } else { // fill with transparent - runLength = curByte >> 3; - pixelNr += runLength; - } - } - break; + if (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_2) { + // See GfxView::unpackCel() for why this black/white swap is done + // This picture swap is only needed in SCI32, not SCI1.1 + if (clearColor == 0) + clearColor = 0xff; + else if (clearColor == 0xff) + clearColor = 0; + } - default: - error("Unsupported picture viewtype"); - } - } else { - // decompression for data that has two separate streams (probably SCI 1.1 picture) - while (pixelNr < pixelCount) { - curByte = *rlePtr++; - runLength = curByte & 0x3F; - switch (curByte & 0xC0) { - case 0: // copy bytes as-is - while (runLength-- && pixelNr < pixelCount) - ptr[pixelNr++] = *literalPtr++; - break; - case 0x80: // fill with color - memset(ptr + pixelNr, *literalPtr++, MIN<uint16>(runLength, pixelCount - pixelNr)); - pixelNr += runLength; - break; - case 0xC0: // fill with transparent - pixelNr += runLength; - break; - } - } - } - } else { + if (compression) + unpackCelData(inbuffer, celBitmap, clearColor, pixelCount, rlePos, literalPos, _resMan->getViewType(), width, false); + else // No compression (some SCI32 pictures) memcpy(celBitmap, rlePtr, pixelCount); + + if (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_2) { + // See GfxView::unpackCel() for why this black/white swap is done + // This picture swap is only needed in SCI32, not SCI1.1 + for (int i = 0; i < pixelCount; i++) { + if (celBitmap[i] == 0) + celBitmap[i] = 0xff; + else if (celBitmap[i] == 0xff) + celBitmap[i] = 0; + } } Common::Rect displayArea = _coordAdjuster->pictureGetDisplayArea(); @@ -377,10 +326,11 @@ void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos sourcePixelSkipPerRow = width - (rightX - leftX); // Change clearcolor to white, if we dont add to an existing picture. That way we will paint everything on screen - // but white and that wont matter because the screen is supposed to be already white. It seems that most (if not all) - // SCI1.1 games use color 0 as transparency and SCI1 games use color 255 as transparency. Sierra SCI seems to paint - // the whole data to screen and wont skip over transparent pixels. So this will actually make it work like Sierra - if (!_addToFlag) + // but white and that won't matter because the screen is supposed to be already white. It seems that most (if not all) + // SCI1.1 games use color 0 as transparency and SCI1 games use color 255 as transparency. Sierra SCI seems to paint + // the whole data to screen and wont skip over transparent pixels. So this will actually make it work like Sierra. + // SCI32 doesn't use _addToFlag at all. + if (!_addToFlag && _resourceType != SCI_PICTURE_TYPE_SCI32) clearColor = _screen->getColorWhite(); byte drawMask = priority == 255 ? GFX_SCREEN_MASK_VISUAL : GFX_SCREEN_MASK_VISUAL | GFX_SCREEN_MASK_PRIORITY; @@ -443,6 +393,7 @@ enum { PIC_OP_OPX = 0xfe, PIC_OP_TERMINATE = 0xff }; + #define PIC_OP_FIRST PIC_OP_SET_COLOR enum { @@ -465,6 +416,47 @@ enum { PIC_OPX_VGA_PRIORITY_TABLE_EXPLICIT = 4 }; +#ifdef DEBUG_PICTURE_DRAW +const char *picOpcodeNames[] = { + "Set color", + "Disable visual", + "Set priority", + "Disable priority", + "Short patterns", + "Medium lines", + "Long lines", + "Short lines", + "Fill", + "Set pattern", + "Absolute pattern", + "Set control", + "Disable control", + "Medium patterns", + "Extended opcode", + "Terminate" +}; + +const char *picExOpcodeNamesEGA[] = { + "Set palette entries", + "Set palette", + "Mono0", + "Mono1", + "Mono2", + "Mono3", + "Mono4", + "Embedded view", + "Set priority table" +}; + +const char *picExOpcodeNamesVGA[] = { + "Set palette entries", + "Embedded view", + "Set palette", + "Set priority table (eqdist)", + "Set priority table (explicit)" +}; +#endif + #define PIC_EGAPALETTE_COUNT 4 #define PIC_EGAPALETTE_SIZE 40 #define PIC_EGAPALETTE_TOTALSIZE PIC_EGAPALETTE_COUNT*PIC_EGAPALETTE_SIZE @@ -543,7 +535,9 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) { // Drawing while (curPos < dataSize) { - //warning("%X at %d", data[curPos], curPos); +#ifdef DEBUG_PICTURE_DRAW + debug("Picture op: %X (%s) at %d", data[curPos], picOpcodeNames[data[curPos] - 0xF0], curPos); +#endif switch (pic_op = data[curPos++]) { case PIC_OP_SET_COLOR: pic_color = data[curPos++]; @@ -681,6 +675,9 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) { case PIC_OP_OPX: // Extended functions if (isEGA) { +#ifdef DEBUG_PICTURE_DRAW + debug("* Picture ex op: %X (%s) at %d", data[curPos], picExOpcodeNamesEGA[data[curPos]], curPos); +#endif switch (pic_op = data[curPos++]) { case PIC_OPX_EGA_SET_PALETTE_ENTRIES: while (vectorIsNonOpcode(data[curPos])) { @@ -726,6 +723,9 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) { error("Unsupported sci1 extended pic-operation %X", pic_op); } } else { +#ifdef DEBUG_PICTURE_DRAW + debug("* Picture ex op: %X (%s) at %d", data[curPos], picExOpcodeNamesVGA[data[curPos]], curPos); +#endif switch (pic_op = data[curPos++]) { case PIC_OPX_VGA_SET_PALETTE_ENTRIES: while (vectorIsNonOpcode(data[curPos])) { @@ -733,7 +733,7 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) { } break; case PIC_OPX_VGA_SET_PALETTE: - if (_resMan->isAmiga32color()) { + if (_resMan->getViewType() == kViewAmiga || _resMan->getViewType() == kViewAmiga64) { if ((data[curPos] == 0x00) && (data[curPos + 1] == 0x01) && ((data[curPos + 32] & 0xF0) != 0xF0)) { // Left-Over VGA palette, we simply ignore it curPos += 256 + 4 + 1024; @@ -780,7 +780,7 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) { case GID_SQ3: switch (_resourceId) { case 154: // SQ3: intro, ship gets sucked in - _screen->ditherForceMemorial(0xD0); + _screen->ditherForceDitheredColor(0xD0); break; default: break; @@ -866,6 +866,8 @@ void GfxPicture::vectorFloodFill(int16 x, int16 y, byte color, byte priority, by byte matchedMask, matchMask; int16 w, e, a_set, b_set; + bool isEGA = (_resMan->getViewType() == kViewEga); + p.x = x + curPort->left; p.y = y + curPort->top; stack.push(p); @@ -874,6 +876,18 @@ void GfxPicture::vectorFloodFill(int16 x, int16 y, byte color, byte priority, by byte searchPriority = _screen->getPriority(p.x, p.y); byte searchControl = _screen->getControl(p.x, p.y); + if (isEGA) { + // In EGA games a pixel in the framebuffer is only 4 bits. We store + // a full byte per pixel to allow undithering, but when comparing + // pixels for flood-fill purposes, we should only compare the + // visible color of a pixel. + + if ((x ^ y) & 1) + searchColor = (searchColor ^ (searchColor >> 4)) & 0x0F; + else + searchColor = searchColor & 0x0F; + } + // This logic was taken directly from sierra sci, floodfill will get aborted on various occations if (screenMask & GFX_SCREEN_MASK_VISUAL) { if ((color == _screen->getColorWhite()) || (searchColor != _screen->getColorWhite())) @@ -913,20 +927,20 @@ void GfxPicture::vectorFloodFill(int16 x, int16 y, byte color, byte priority, by int b = curPort->rect.bottom + curPort->top - 1; while (stack.size()) { p = stack.pop(); - if ((matchedMask = _screen->isFillMatch(p.x, p.y, matchMask, searchColor, searchPriority, searchControl)) == 0) // already filled + if ((matchedMask = _screen->isFillMatch(p.x, p.y, matchMask, searchColor, searchPriority, searchControl, isEGA)) == 0) // already filled continue; _screen->putPixel(p.x, p.y, screenMask, color, priority, control); w = p.x; e = p.x; // moving west and east pointers as long as there is a matching color to fill - while (w > l && (matchedMask = _screen->isFillMatch(w - 1, p.y, matchMask, searchColor, searchPriority, searchControl))) + while (w > l && (matchedMask = _screen->isFillMatch(w - 1, p.y, matchMask, searchColor, searchPriority, searchControl, isEGA))) _screen->putPixel(--w, p.y, screenMask, color, priority, control); - while (e < r && (matchedMask = _screen->isFillMatch(e + 1, p.y, matchMask, searchColor, searchPriority, searchControl))) + while (e < r && (matchedMask = _screen->isFillMatch(e + 1, p.y, matchMask, searchColor, searchPriority, searchControl, isEGA))) _screen->putPixel(++e, p.y, screenMask, color, priority, control); // checking lines above and below for possible flood targets a_set = b_set = 0; while (w <= e) { - if (p.y > t && (matchedMask = _screen->isFillMatch(w, p.y - 1, matchMask, searchColor, searchPriority, searchControl))) { // one line above + if (p.y > t && (matchedMask = _screen->isFillMatch(w, p.y - 1, matchMask, searchColor, searchPriority, searchControl, isEGA))) { // one line above if (a_set == 0) { p1.x = w; p1.y = p.y - 1; @@ -936,7 +950,7 @@ void GfxPicture::vectorFloodFill(int16 x, int16 y, byte color, byte priority, by } else a_set = 0; - if (p.y < b && (matchedMask = _screen->isFillMatch(w, p.y + 1, matchMask, searchColor, searchPriority, searchControl))) { // one line below + if (p.y < b && (matchedMask = _screen->isFillMatch(w, p.y + 1, matchMask, searchColor, searchPriority, searchControl, isEGA))) { // one line below if (b_set == 0) { p1.x = w; p1.y = p.y + 1; diff --git a/engines/sci/graphics/ports.cpp b/engines/sci/graphics/ports.cpp index 87f0c3d6a2..cc206bd5b9 100644 --- a/engines/sci/graphics/ports.cpp +++ b/engines/sci/graphics/ports.cpp @@ -25,8 +25,10 @@ #include "common/util.h" +#include "sci/console.h" #include "sci/sci.h" #include "sci/engine/features.h" +#include "sci/engine/gc.h" #include "sci/engine/kernel.h" #include "sci/engine/state.h" #include "sci/engine/selector.h" @@ -106,7 +108,10 @@ void GfxPorts::init(bool usesOldGfxFunctions, GfxPaint16 *paint16, GfxText16 *te offTop = 26; break; default: - offTop = 10; + // For Mac games running with a height of 190, we do not have a menu bar + // so the top offset should be 0. + if (_screen->getHeight() == 190) + offTop = 0; break; } @@ -243,8 +248,10 @@ void GfxPorts::beginUpdate(Window *wnd) { PortList::iterator it = _windowList.reverse_begin(); const PortList::iterator end = Common::find(_windowList.begin(), _windowList.end(), wnd); while (it != end) { - // FIXME: We also store Port objects in the window list. - // We should add a check that we really only pass windows here... + // We also store Port objects in the window list, but they + // shouldn't be encountered during this iteration. + assert((*it)->isWindow()); + updateWindow((Window *)*it); --it; } @@ -260,11 +267,16 @@ void GfxPorts::endUpdate(Window *wnd) { assert(it != end); while (++it != end) { - // FIXME: We also store Port objects in the window list. - // We should add a check that we really only pass windows here... + // We also store Port objects in the window list, but they + // shouldn't be encountered during this iteration. + assert((*it)->isWindow()); + updateWindow((Window *)*it); } + if (getSciVersion() < SCI_VERSION_1_EGA_ONLY) + g_sci->_gfxPaint16->kernelGraphRedrawBox(_curPort->rect); + setPort(oldPort); } @@ -297,7 +309,14 @@ Window *GfxPorts::addWindow(const Common::Rect &dims, const Common::Rect *restor } _windowsById[id] = pwnd; - if (style & SCI_WINDOWMGR_STYLE_TOPMOST) + + // KQ1sci, KQ4, iceman, QfG2 always add windows to the back of the list. + // KQ5CD checks style. + // Hoyle3-demo also always adds to the back (#3036763). + bool forceToBack = (getSciVersion() <= SCI_VERSION_1_EGA_ONLY) || + (g_sci->getGameId() == GID_HOYLE3 && g_sci->isDemo()); + + if (!forceToBack && (style & SCI_WINDOWMGR_STYLE_TOPMOST)) _windowList.push_front(pwnd); else _windowList.push_back(pwnd); @@ -308,7 +327,7 @@ Window *GfxPorts::addWindow(const Common::Rect &dims, const Common::Rect *restor // bit of the left dimension in their interpreter. It seems Sierra did it // for EGA byte alignment (EGA uses 1 byte for 2 pixels) and left it in // their interpreter even in the newer VGA games. - r.left = r.left & 0x7FFE; + r.left = r.left & 0xFFFE; if (r.width() > _screen->getWidth()) { // We get invalid dimensions at least at the end of sq3 (script bug!). @@ -344,25 +363,50 @@ Window *GfxPorts::addWindow(const Common::Rect &dims, const Common::Rect *restor } pwnd->dims = r; - const Common::Rect *wmprect = &_wmgrPort->rect; + + // Clip window, if needed + Common::Rect wmprect = _wmgrPort->rect; + // Handle a special case for Dr. Brain 1 Mac. When hovering the mouse cursor + // over the status line on top, the game scripts try to draw the game's icon + // bar above the current port, by specifying a negative window top, so that + // the end result will be drawn 10 pixels above the current port. This is a + // hack by Sierra, and is only limited to user style windows. Normally, we + // should not clip, same as what Sierra does. However, this will result in + // having invalid rectangles with negative coordinates. For this reason, we + // adjust the containing rectangle instead. + if (pwnd->dims.top < 0 && g_sci->getPlatform() == Common::kPlatformMacintosh && + (style & SCI_WINDOWMGR_STYLE_USER) && _wmgrPort->top + pwnd->dims.top >= 0) { + // Offset the final rect top by the requested pixels + wmprect.top += pwnd->dims.top; + } + int16 oldtop = pwnd->dims.top; int16 oldleft = pwnd->dims.left; - if (wmprect->top > pwnd->dims.top) - pwnd->dims.moveTo(pwnd->dims.left, wmprect->top); - if (wmprect->bottom < pwnd->dims.bottom) - pwnd->dims.moveTo(pwnd->dims.left, wmprect->bottom - pwnd->dims.bottom + pwnd->dims.top); + if (wmprect.top > pwnd->dims.top) + pwnd->dims.moveTo(pwnd->dims.left, wmprect.top); + + if (wmprect.bottom < pwnd->dims.bottom) + pwnd->dims.moveTo(pwnd->dims.left, wmprect.bottom - pwnd->dims.bottom + pwnd->dims.top); - if (wmprect->right < pwnd->dims.right) - pwnd->dims.moveTo(wmprect->right + pwnd->dims.left - pwnd->dims.right, pwnd->dims.top); + if (wmprect.right < pwnd->dims.right) + pwnd->dims.moveTo(wmprect.right + pwnd->dims.left - pwnd->dims.right, pwnd->dims.top); - if (wmprect->left > pwnd->dims.left) - pwnd->dims.moveTo(wmprect->left, pwnd->dims.top); + if (wmprect.left > pwnd->dims.left) + pwnd->dims.moveTo(wmprect.left, pwnd->dims.top); pwnd->rect.moveTo(pwnd->rect.left + pwnd->dims.left - oldleft, pwnd->rect.top + pwnd->dims.top - oldtop); + if (restoreRect == 0) pwnd->restoreRect = pwnd->dims; + if (pwnd->restoreRect.top < 0 && g_sci->getPlatform() == Common::kPlatformMacintosh && + (style & SCI_WINDOWMGR_STYLE_USER) && _wmgrPort->top + pwnd->restoreRect.top >= 0) { + // Special case for Dr. Brain 1 Mac (check above), applied to the + // restore rectangle. + pwnd->restoreRect.moveTo(pwnd->restoreRect.left, wmprect.top); + } + if (draw) drawWindow(pwnd); setPort((Port *)pwnd); @@ -575,10 +619,6 @@ void GfxPorts::priorityBandsInit(int16 bandCount, int16 top, int16 bottom) { int16 y; int32 bandSize; - // This code is for 320x200 games only - if (_screen->getHeight() != 200) - return; - if (bandCount != -1) _priorityBandCount = bandCount; @@ -669,4 +709,26 @@ int16 GfxPorts::kernelPriorityToCoordinate(byte priority) { return _priorityBottom; } +void GfxPorts::processEngineHunkList(WorklistManager &wm) { + for (PortList::const_iterator it = _windowList.begin(); it != _windowList.end(); ++it) { + if ((*it)->isWindow()) { + Window *wnd = ((Window *)*it); + wm.push(wnd->hSaved1); + wm.push(wnd->hSaved2); + } + } +} + +void GfxPorts::printWindowList(Console *con) { + for (PortList::const_iterator it = _windowList.begin(); it != _windowList.end(); ++it) { + if ((*it)->isWindow()) { + Window *wnd = ((Window *)*it); + con->DebugPrintf("%d: '%s' at %d, %d, (%d, %d, %d, %d), drawn: %d, style: %d\n", + wnd->id, wnd->title.c_str(), wnd->left, wnd->top, + wnd->rect.left, wnd->rect.top, wnd->rect.right, wnd->rect.bottom, + wnd->bDrawn, wnd->wndStyle); + } + } +} + } // End of namespace Sci diff --git a/engines/sci/graphics/ports.h b/engines/sci/graphics/ports.h index 21c6d31ebd..31ed671daf 100644 --- a/engines/sci/graphics/ports.h +++ b/engines/sci/graphics/ports.h @@ -32,13 +32,10 @@ namespace Sci { -class SciGui; class GfxPaint16; class GfxScreen; class GfxText16; - -#define PORTS_FIRSTWINDOWID 2 -#define PORTS_FIRSTSCRIPTWINDOWID 3 +struct WorklistManager; // window styles enum { @@ -49,6 +46,9 @@ enum { SCI_WINDOWMGR_STYLE_USER = (1 << 7) }; +typedef Common::List<Port *> PortList; +typedef Common::Array<Port *> PortArray; + /** * Ports class, includes all port managment for SCI0->SCI1.1 games. Ports are some sort of windows in SCI * this class also handles adjusting coordinates to a specific port @@ -103,6 +103,8 @@ public: void kernelGraphAdjustPriority(int top, int bottom); byte kernelCoordinateToPriority(int16 y); int16 kernelPriorityToCoordinate(byte priority); + void processEngineHunkList(WorklistManager &wm); + void printWindowList(Console *con); Port *_wmgrPort; Window *_picWind; @@ -116,7 +118,11 @@ public: virtual void saveLoadWithSerializer(Common::Serializer &ser); private: - typedef Common::List<Port *> PortList; + /** The list of open 'windows' (and ports), in visual order. */ + PortList _windowList; + + /** The list of all open 'windows' (and ports), ordered by their id. */ + PortArray _windowsById; SegManager *_segMan; GfxPaint16 *_paint16; @@ -130,12 +136,6 @@ private: // counts windows that got disposed but are not freed yet uint16 _freeCounter; - /** The list of open 'windows' (and ports), in visual order. */ - PortList _windowList; - - /** The list of all open 'windows' (and ports), ordered by their id. */ - Common::Array<Port *> _windowsById; - Common::Rect _bounds; // Priority Bands related variables diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp index 896ac0db22..32f54c7e23 100644 --- a/engines/sci/graphics/screen.cpp +++ b/engines/sci/graphics/screen.cpp @@ -32,6 +32,7 @@ #include "sci/sci.h" #include "sci/engine/state.h" #include "sci/graphics/screen.h" +#include "sci/graphics/view.h" namespace Sci { @@ -64,7 +65,7 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) { _height = 480; } else { _width = 320; - _height = 200; + _height = getLowResScreenHeight(); } // Japanese versions of games use hi-res font on upscaled version of the game. @@ -113,7 +114,7 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) { _unditherState = true; _fontIsUpscaled = false; - if (_resMan->isVGA() || (_resMan->isAmiga32color())) { + if (_resMan->getViewType() != kViewEga) { // It is not 100% accurate to set white to be 255 for Amiga 32-color // games. But 255 is defined as white in our SCI at all times, so it // doesn't matter. @@ -132,10 +133,12 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) { if (g_sci->hasMacIconBar()) { // For SCI1.1 Mac games with the custom icon bar, we need to expand the screen // to accommodate for the icon bar. Of course, both KQ6 and QFG1 VGA differ in size. + // We add 2 to the height of the icon bar to add a buffer between the screen and the + // icon bar (as did the original interpreter). if (g_sci->getGameId() == GID_KQ6) - initGraphics(_displayWidth, _displayHeight + 26, _displayWidth > 320); + initGraphics(_displayWidth, _displayHeight + 26 + 2, _displayWidth > 320); else if (g_sci->getGameId() == GID_FREDDYPHARKAS) - initGraphics(_displayWidth, _displayHeight + 28, _displayWidth > 320); + initGraphics(_displayWidth, _displayHeight + 28 + 2, _displayWidth > 320); else error("Unknown SCI1.1 Mac game"); } else @@ -154,12 +157,14 @@ void GfxScreen::copyToScreen() { } void GfxScreen::copyFromScreen(byte *buffer) { + // TODO this ignores the pitch Graphics::Surface *screen = g_system->lockScreen(); memcpy(buffer, screen->pixels, _displayPixels); g_system->unlockScreen(); } void GfxScreen::kernelSyncWithFramebuffer() { + // TODO this ignores the pitch Graphics::Surface *screen = g_system->lockScreen(); memcpy(_displayScreen, screen->pixels, _displayPixels); g_system->unlockScreen(); @@ -351,12 +356,30 @@ byte GfxScreen::getControl(int x, int y) { return _controlScreen[y * _width + x]; } -byte GfxScreen::isFillMatch(int16 x, int16 y, byte screenMask, byte t_color, byte t_pri, byte t_con) { +byte GfxScreen::isFillMatch(int16 x, int16 y, byte screenMask, byte t_color, byte t_pri, byte t_con, bool isEGA) { int offset = y * _width + x; byte match = 0; - if ((screenMask & GFX_SCREEN_MASK_VISUAL) && *(_visualScreen + offset) == t_color) - match |= GFX_SCREEN_MASK_VISUAL; + // FIXME: + if (screenMask & GFX_SCREEN_MASK_VISUAL) { + if (!isEGA) { + if (*(_visualScreen + offset) == t_color) + match |= GFX_SCREEN_MASK_VISUAL; + } else { + // In EGA games a pixel in the framebuffer is only 4 bits. We store + // a full byte per pixel to allow undithering, but when comparing + // pixels for flood-fill purposes, we should only compare the + // visible color of a pixel. + + byte c = *(_visualScreen + offset); + if ((x ^ y) & 1) + c = (c ^ (c >> 4)) & 0x0F; + else + c = c & 0x0F; + if (c == t_color) + match |= GFX_SCREEN_MASK_VISUAL; + } + } if ((screenMask & GFX_SCREEN_MASK_PRIORITY) && *(_priorityScreen + offset) == t_pri) match |= GFX_SCREEN_MASK_PRIORITY; if ((screenMask & GFX_SCREEN_MASK_CONTROL) && *(_controlScreen + offset) == t_con) @@ -509,34 +532,6 @@ void GfxScreen::bitsRestoreDisplayScreen(Common::Rect rect, byte *&memoryPtr) { } } -void GfxScreen::getPalette(Palette *pal) { - // just copy palette to system - byte bpal[4 * 256]; - // Get current palette, update it and put back - g_system->getPaletteManager()->grabPalette(bpal, 0, 256); - for (int16 i = 1; i < 255; i++) { - pal->colors[i].r = bpal[i * 4]; - pal->colors[i].g = bpal[i * 4 + 1]; - pal->colors[i].b = bpal[i * 4 + 2]; - } -} - -void GfxScreen::setPalette(Palette *pal) { - // just copy palette to system - byte bpal[4 * 256]; - // Get current palette, update it and put back - g_system->getPaletteManager()->grabPalette(bpal, 0, 256); - for (int16 i = 0; i < 256; i++) { - if (!pal->colors[i].used) - continue; - bpal[i * 4] = CLIP(pal->colors[i].r * pal->intensity[i] / 100, 0, 255); - bpal[i * 4 + 1] = CLIP(pal->colors[i].g * pal->intensity[i] / 100, 0, 255); - bpal[i * 4 + 2] = CLIP(pal->colors[i].b * pal->intensity[i] / 100, 0, 255); - bpal[i * 4 + 3] = 100; - } - g_system->getPaletteManager()->setPalette(bpal, 0, 256); -} - void GfxScreen::setVerticalShakePos(uint16 shakePos) { if (!_upscaledHires) g_system->setShakePos(shakePos); @@ -591,7 +586,7 @@ void GfxScreen::dither(bool addToFlag) { } } else { if (!addToFlag) - memset(&_unditherMemorial, 0, sizeof(_unditherMemorial)); + memset(&_ditheredPicColors, 0, sizeof(_ditheredPicColors)); // Do dithering on visual screen and put decoded but undithered byte onto display-screen for (y = 0; y < _height; y++) { for (x = 0; x < _width; x++) { @@ -599,7 +594,7 @@ void GfxScreen::dither(bool addToFlag) { if (color & 0xF0) { color ^= color << 4; // remember dither combination for cel-undithering - _unditherMemorial[color]++; + _ditheredPicColors[color]++; // if decoded color wants do dither with black on left side, we turn it around // otherwise the normal ega color would get used for display if (color & 0xF0) { @@ -626,18 +621,17 @@ void GfxScreen::dither(bool addToFlag) { } } -// Force a color combination into memorial -void GfxScreen::ditherForceMemorial(byte color) { - _unditherMemorial[color] = 256; +void GfxScreen::ditherForceDitheredColor(byte color) { + _ditheredPicColors[color] = 256; } void GfxScreen::debugUnditherSetState(bool flag) { _unditherState = flag; } -int16 *GfxScreen::unditherGetMemorial() { +int16 *GfxScreen::unditherGetDitheredBgColors() { if (_unditherState) - return (int16 *)&_unditherMemorial; + return (int16 *)&_ditheredPicColors; else return NULL; } @@ -702,12 +696,39 @@ void GfxScreen::scale2x(const byte *src, byte *dst, int16 srcWidth, int16 srcHei } } -void GfxScreen::adjustToUpscaledCoordinates(int16 &y, int16 &x) { +struct UpScaledAdjust { + GfxScreenUpscaledMode gameHiresMode; + Sci32ViewNativeResolution viewNativeRes; + int numerator; + int denominator; +}; + +static const UpScaledAdjust s_upscaledAdjustTable[] = { + { GFX_SCREEN_UPSCALED_640x480, SCI_VIEW_NATIVERES_640x400, 5, 6 } +}; + +void GfxScreen::adjustToUpscaledCoordinates(int16 &y, int16 &x, Sci32ViewNativeResolution viewNativeRes) { x *= 2; y = _upscaledMapping[y]; + + for (int i = 0; i < ARRAYSIZE(s_upscaledAdjustTable); i++) { + if (s_upscaledAdjustTable[i].gameHiresMode == _upscaledHires && + s_upscaledAdjustTable[i].viewNativeRes == viewNativeRes) { + y = (y * s_upscaledAdjustTable[i].numerator) / s_upscaledAdjustTable[i].denominator; + break; + } + } } -void GfxScreen::adjustBackUpscaledCoordinates(int16 &y, int16 &x) { +void GfxScreen::adjustBackUpscaledCoordinates(int16 &y, int16 &x, Sci32ViewNativeResolution viewNativeRes) { + for (int i = 0; i < ARRAYSIZE(s_upscaledAdjustTable); i++) { + if (s_upscaledAdjustTable[i].gameHiresMode == _upscaledHires && + s_upscaledAdjustTable[i].viewNativeRes == viewNativeRes) { + y = (y * s_upscaledAdjustTable[i].denominator) / s_upscaledAdjustTable[i].numerator; + break; + } + } + switch (_upscaledHires) { case GFX_SCREEN_UPSCALED_640x400: x /= 2; @@ -743,4 +764,23 @@ int16 GfxScreen::kernelPicNotValid(int16 newPicNotValid) { return oldPicNotValid; } +uint16 GfxScreen::getLowResScreenHeight() { + // Some Mac SCI1/1.1 games only take up 190 rows and do not + // have the menu bar. + if (g_sci->getPlatform() == Common::kPlatformMacintosh) { + switch (g_sci->getGameId()) { + case GID_FREDDYPHARKAS: + case GID_KQ5: + case GID_KQ6: + case GID_SQ1: + return 190; + default: + break; + } + } + + // Everything else is 200 + return 200; +} + } // End of namespace Sci diff --git a/engines/sci/graphics/screen.h b/engines/sci/graphics/screen.h index b6898b905a..89ad52e0ac 100644 --- a/engines/sci/graphics/screen.h +++ b/engines/sci/graphics/screen.h @@ -28,6 +28,7 @@ #include "sci/sci.h" #include "sci/graphics/helpers.h" +#include "sci/graphics/view.h" #include "graphics/sjis.h" @@ -51,7 +52,7 @@ enum GfxScreenMasks { }; enum { - SCI_SCREEN_UNDITHERMEMORIAL_SIZE = 256 + DITHERED_BG_COLORS_SIZE = 256 }; /** @@ -89,35 +90,37 @@ public: void drawLine(int16 left, int16 top, int16 right, int16 bottom, byte color, byte prio, byte control) { drawLine(Common::Point(left, top), Common::Point(right, bottom), color, prio, control); } - int getUpscaledHires() const { + + GfxScreenUpscaledMode getUpscaledHires() const { return _upscaledHires; } + bool getUnditherState() const { return _unditherState; } + void putKanjiChar(Graphics::FontSJIS *commonFont, int16 x, int16 y, uint16 chr, byte color); byte getVisual(int x, int y); byte getPriority(int x, int y); byte getControl(int x, int y); - byte isFillMatch(int16 x, int16 y, byte drawMask, byte t_color, byte t_pri, byte t_con); + byte isFillMatch(int16 x, int16 y, byte drawMask, byte t_color, byte t_pri, byte t_con, bool isEGA); int bitsGetDataSize(Common::Rect rect, byte mask); void bitsSave(Common::Rect rect, byte mask, byte *memoryPtr); void bitsGetRect(byte *memoryPtr, Common::Rect *destRect); void bitsRestore(byte *memoryPtr); - void getPalette(Palette *pal); - void setPalette(Palette *pal); - void scale2x(const byte *src, byte *dst, int16 srcWidth, int16 srcHeight, byte bytesPerPixel = 1); - void adjustToUpscaledCoordinates(int16 &y, int16 &x); - void adjustBackUpscaledCoordinates(int16 &y, int16 &x); + void adjustToUpscaledCoordinates(int16 &y, int16 &x, Sci32ViewNativeResolution viewScalingType = SCI_VIEW_NATIVERES_NONE); + void adjustBackUpscaledCoordinates(int16 &y, int16 &x, Sci32ViewNativeResolution viewScalingType = SCI_VIEW_NATIVERES_NONE); void dither(bool addToFlag); - void ditherForceMemorial(byte color); + + // Force a color combination as a dithered color + void ditherForceDitheredColor(byte color); void debugUnditherSetState(bool flag); - int16 *unditherGetMemorial(); + int16 *unditherGetDitheredBgColors(); void debugShowMap(int mapNo); @@ -149,7 +152,7 @@ private: void setVerticalShakePos(uint16 shakePos); bool _unditherState; - int16 _unditherMemorial[SCI_SCREEN_UNDITHERMEMORIAL_SIZE]; + int16 _ditheredPicColors[DITHERED_BG_COLORS_SIZE]; // These screens have the real resolution of the game engine (320x200 for // SCI0/SCI1/SCI11 games, 640x480 for SCI2 games). SCI0 games will be @@ -175,7 +178,7 @@ private: // This variable defines, if upscaled hires is active and what upscaled mode // is used. - int _upscaledHires; + GfxScreenUpscaledMode _upscaledHires; // This here holds a translation for vertical coordinates between native // (visual) and actual (display) screen. @@ -184,6 +187,8 @@ private: // This defines whether or not the font we're drawing is already scaled // to the screen size (and we therefore should not upscale it ourselves). bool _fontIsUpscaled; + + uint16 getLowResScreenHeight(); }; } // End of namespace Sci diff --git a/engines/sci/graphics/text16.cpp b/engines/sci/graphics/text16.cpp index 21605ccb8e..6269a58492 100644 --- a/engines/sci/graphics/text16.cpp +++ b/engines/sci/graphics/text16.cpp @@ -204,19 +204,24 @@ int16 GfxText16::GetLongest(const char *text, int16 maxWidth, GuiResourceId orgF maxChars = curCharCount; // return count up to (but not including) breaking space break; } + // Sometimes this can go off the screen, like for example bug #3040161. + // However, we only perform this for non-Japanese games, as these require + // special handling, done after this loop. + if (width + _font->getCharWidth(curChar) > maxWidth && g_sci->getLanguage() != Common::JA_JPN) + break; width += _font->getCharWidth(curChar); curCharCount++; } + + // Text without spaces, probably Kanji/Japanese if (maxChars == 0) { - // Text w/o space, supposingly kanji maxChars = curCharCount; uint16 nextChar; // We remove the last char only, if maxWidth was actually equal width // before adding the last char. Otherwise we won't get the same cutting - // as in sierra pc98 sci. Note: changing the while() instead will NOT - // WORK. it would break all sorts of regular sci games. + // as in sierra pc98 sci. if (maxWidth == (width - _font->getCharWidth(curChar))) { maxChars--; if (curChar > 0xFF) diff --git a/engines/sci/graphics/transitions.cpp b/engines/sci/graphics/transitions.cpp index b7b2bfb38e..fb124055d6 100644 --- a/engines/sci/graphics/transitions.cpp +++ b/engines/sci/graphics/transitions.cpp @@ -39,8 +39,8 @@ namespace Sci { //#define DISABLE_TRANSITIONS // uncomment to disable room transitions (for development only! helps in testing games quickly) -GfxTransitions::GfxTransitions(GfxScreen *screen, GfxPalette *palette, bool isVGA) - : _screen(screen), _palette(palette), _isVGA(isVGA) { +GfxTransitions::GfxTransitions(GfxScreen *screen, GfxPalette *palette) + : _screen(screen), _palette(palette) { init(); } @@ -124,6 +124,7 @@ void GfxTransitions::setup(int16 number, bool blackoutFlag) { _number = SCI_TRANSITIONS_NONE; #endif _blackoutFlag = blackoutFlag; + debugC(kDebugLevelGraphics, "Transition %d, blackout %d", number, blackoutFlag); } } @@ -272,8 +273,7 @@ void GfxTransitions::doTransition(int16 number, bool blackoutFlag) { void GfxTransitions::setNewPalette(bool blackoutFlag) { if (!blackoutFlag) - if (_isVGA) - _palette->setOnScreen(); + _palette->setOnScreen(); } void GfxTransitions::setNewScreen(bool blackoutFlag) { @@ -303,7 +303,7 @@ void GfxTransitions::copyRectToScreen(const Common::Rect rect, bool blackoutFlag // Note: don't do too many steps in here, otherwise cpu will crap out because of // the load void GfxTransitions::fadeOut() { - byte oldPalette[4 * 256], workPalette[4 * 256]; + byte oldPalette[3 * 256], workPalette[3 * 256]; int16 stepNr, colorNr; // Sierra did not fade in/out color 255 for sci1.1, but they used it in // several pictures (e.g. qfg3 demo/intro), so the fading looked weird @@ -312,12 +312,18 @@ void GfxTransitions::fadeOut() { g_system->getPaletteManager()->grabPalette(oldPalette, 0, 256); for (stepNr = 100; stepNr >= 0; stepNr -= 10) { - for (colorNr = 1; colorNr < tillColorNr; colorNr++){ - workPalette[colorNr * 4 + 0] = oldPalette[colorNr * 4] * stepNr / 100; - workPalette[colorNr * 4 + 1] = oldPalette[colorNr * 4 + 1] * stepNr / 100; - workPalette[colorNr * 4 + 2] = oldPalette[colorNr * 4 + 2] * stepNr / 100; + for (colorNr = 1; colorNr < tillColorNr; colorNr++) { + if (_palette->colorIsFromMacClut(colorNr)) { + workPalette[colorNr * 3 + 0] = oldPalette[colorNr * 3]; + workPalette[colorNr * 3 + 1] = oldPalette[colorNr * 3 + 1]; + workPalette[colorNr * 3 + 2] = oldPalette[colorNr * 3 + 2]; + } else { + workPalette[colorNr * 3 + 0] = oldPalette[colorNr * 3] * stepNr / 100; + workPalette[colorNr * 3 + 1] = oldPalette[colorNr * 3 + 1] * stepNr / 100; + workPalette[colorNr * 3 + 2] = oldPalette[colorNr * 3 + 2] * stepNr / 100; + } } - g_system->getPaletteManager()->setPalette(workPalette + 4, 1, 254); + g_system->getPaletteManager()->setPalette(workPalette + 3, 1, 254); g_sci->getEngineState()->wait(2); } } @@ -457,7 +463,6 @@ void GfxTransitions::scrollCopyOldToScreen(Common::Rect screenRect, int16 x, int // Scroll old screen (up/down/left/right) and insert new screen that way - works // on _picRect area only. void GfxTransitions::scroll(int16 number) { - int16 screenWidth, screenHeight; int16 stepNr = 0; Common::Rect oldMoveRect = _picRect; Common::Rect oldScreenRect = _picRect; @@ -466,7 +471,6 @@ void GfxTransitions::scroll(int16 number) { uint32 msecCount = 0; _screen->copyFromScreen(_oldScreen); - screenWidth = _screen->getDisplayWidth(); screenHeight = _screen->getDisplayHeight(); switch (number) { case SCI_TRANSITIONS_SCROLL_LEFT: @@ -510,7 +514,7 @@ void GfxTransitions::scroll(int16 number) { newScreenRect.bottom = newScreenRect.top; newMoveRect.top = newMoveRect.bottom; while (oldMoveRect.top < oldMoveRect.bottom) { - oldMoveRect.top++; oldScreenRect.top++; + oldMoveRect.top++; oldScreenRect.top++; newScreenRect.bottom++; newMoveRect.top--; msecCount += 5; diff --git a/engines/sci/graphics/transitions.h b/engines/sci/graphics/transitions.h index 674b7a8173..a8f0ca6f07 100644 --- a/engines/sci/graphics/transitions.h +++ b/engines/sci/graphics/transitions.h @@ -65,7 +65,7 @@ class Screen; */ class GfxTransitions { public: - GfxTransitions(GfxScreen *screen, GfxPalette *palette, bool isVGA); + GfxTransitions(GfxScreen *screen, GfxPalette *palette); ~GfxTransitions(); void setup(int16 number, bool blackoutFlag); @@ -97,7 +97,6 @@ private: GfxScreen *_screen; GfxPalette *_palette; - bool _isVGA; const GfxTransitionTranslateEntry *_translationTable; int16 _number; bool _blackoutFlag; diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp index a8fc8e7f15..f31cbacb22 100644 --- a/engines/sci/graphics/view.cpp +++ b/engines/sci/graphics/view.cpp @@ -83,7 +83,7 @@ void GfxView::initData(GuiResourceId resourceId) { _loopCount = 0; _embeddedPal = false; _EGAmapping = NULL; - _isSci2Hires = false; + _sci2ScaleRes = SCI_VIEW_NATIVERES_NONE; _isScaleable = true; // we adjust inside getCelRect for SCI0EARLY (that version didn't have the +1 when calculating bottom) @@ -104,9 +104,10 @@ void GfxView::initData(GuiResourceId resourceId) { } switch (curViewType) { - case kViewEga: // View-format SCI0 (and Amiga 16 colors) + case kViewEga: // SCI0 (and Amiga 16 colors) isEGA = true; - case kViewAmiga: // View-format Amiga (32 colors) + case kViewAmiga: // Amiga ECS (32 colors) + case kViewAmiga64: // Amiga AGA (64 colors) case kViewVga: // View-format SCI1 // LoopCount:WORD MirrorMask:WORD Version:WORD PaletteOffset:WORD LoopOffset0:WORD LoopOffset1:WORD... @@ -132,7 +133,7 @@ void GfxView::initData(GuiResourceId resourceId) { // SCI1 VGA conversion games (which will get detected as SCI1EARLY/MIDDLE/LATE) have some views // with broken mapping tables. I guess those games won't use the mapping, so I rather disable it // for them - if (getSciVersion() == SCI_VERSION_1_EGA) { + if (getSciVersion() == SCI_VERSION_1_EGA_ONLY) { _EGAmapping = &_resourceData[palOffset]; for (EGAmapNr = 0; EGAmapNr < SCI_VIEW_EGAMAPPING_COUNT; EGAmapNr++) { if (memcmp(_EGAmapping, EGAmappingStraight, SCI_VIEW_EGAMAPPING_SIZE) != 0) @@ -201,8 +202,15 @@ void GfxView::initData(GuiResourceId resourceId) { assert(headerSize >= 16); _loopCount = _resourceData[2]; assert(_loopCount); - _isSci2Hires = _resourceData[5] == 1 ? true : false; palOffset = READ_SCI11ENDIAN_UINT32(_resourceData + 8); + + // For SCI32, this is a scale flag + if (getSciVersion() >= SCI_VERSION_2) { + _sci2ScaleRes = (Sci32ViewNativeResolution)_resourceData[5]; + if (_screen->getUpscaledHires() == GFX_SCREEN_UPSCALED_DISABLED) + _sci2ScaleRes = SCI_VIEW_NATIVERES_NONE; + } + // flags is actually a bit-mask // it seems it was only used for some early sci1.1 games (or even just laura bow 2) // later interpreters dont support it at all anymore @@ -282,10 +290,10 @@ void GfxView::initData(GuiResourceId resourceId) { } #ifdef ENABLE_SCI32 // adjust width/height returned to scripts - if (_isSci2Hires) { + if (_sci2ScaleRes != SCI_VIEW_NATIVERES_NONE) { for (loopNo = 0; loopNo < _loopCount; loopNo++) for (celNo = 0; celNo < _loop[loopNo].celCount; celNo++) - _screen->adjustBackUpscaledCoordinates(_loop[loopNo].cel[celNo].scriptWidth, _loop[loopNo].cel[celNo].scriptHeight); + _screen->adjustBackUpscaledCoordinates(_loop[loopNo].cel[celNo].scriptWidth, _loop[loopNo].cel[celNo].scriptHeight, _sci2ScaleRes); } else if (getSciVersion() == SCI_VERSION_2_1) { for (loopNo = 0; loopNo < _loopCount; loopNo++) for (celNo = 0; celNo < _loop[loopNo].celCount; celNo++) @@ -329,7 +337,7 @@ Palette *GfxView::getPalette() { } bool GfxView::isSci2Hires() { - return _isSci2Hires; + return _sci2ScaleRes > SCI_VIEW_NATIVERES_320x200; } bool GfxView::isScaleable() { @@ -346,12 +354,9 @@ void GfxView::getCelRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, C void GfxView::getCelSpecialHoyle4Rect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, Common::Rect &outRect) const { const CelInfo *celInfo = getCelInfo(loopNo, celNo); - int16 adjustY = y - celInfo->height + celInfo->displaceY + 1; - int16 adjustX = x - ((celInfo->width - 1) >> 1) + celInfo->displaceX; - outRect.top += adjustY; - outRect.bottom += adjustY; - outRect.left += adjustX; - outRect.right += adjustX; + int16 adjustY = y + celInfo->displaceY - celInfo->height + 1; + int16 adjustX = x + celInfo->displaceX - ((celInfo->width - 1) >> 1); + outRect.translate(adjustX, adjustY); } void GfxView::getCelScaledRect(int16 loopNo, int16 celNo, int16 x, int16 y, int16 z, int16 scaleX, int16 scaleY, Common::Rect &outRect) const { @@ -373,22 +378,157 @@ void GfxView::getCelScaledRect(int16 loopNo, int16 celNo, int16 x, int16 y, int1 outRect.top = outRect.bottom - scaledHeight; } +void unpackCelData(byte *inBuffer, byte *celBitmap, byte clearColor, int pixelCount, int rlePos, int literalPos, ViewType viewType, uint16 width, bool isMacSci11ViewData) { + byte *outPtr = celBitmap; + byte curByte, runLength; + byte *rlePtr = inBuffer + rlePos; + // The existence of a literal position pointer signifies data with two + // separate streams, most likely a SCI1.1 view + byte *literalPtr = inBuffer + literalPos; + int pixelNr = 0; + + memset(celBitmap, clearColor, pixelCount); + + // View unpacking: + // + // EGA: + // Each byte is like XXXXYYYY (XXXX: 0 - 15, YYYY: 0 - 15) + // Set the next XXXX pixels to YYYY + // + // Amiga: + // Each byte is like XXXXXYYY (XXXXX: 0 - 31, YYY: 0 - 7) + // - Case A: YYY != 0 + // Set the next YYY pixels to XXXXX + // - Case B: YYY == 0 + // Skip the next XXXXX pixels (i.e. transparency) + // + // Amiga 64: + // Each byte is like XXYYYYYY (XX: 0 - 3, YYYYYY: 0 - 63) + // - Case A: XX != 0 + // Set the next XX pixels to YYYYYY + // - Case B: XX == 0 + // Skip the next YYYYYY pixels (i.e. transparency) + // + // VGA: + // Each byte is like XXYYYYYY (YYYYY: 0 - 63) + // - Case A: XX == 00 (binary) + // Copy next YYYYYY bytes as-is + // - Case B: XX == 01 (binary) + // Same as above, copy YYYYYY + 64 bytes as-is + // - Case C: XX == 10 (binary) + // Set the next YYYYY pixels to the next byte value + // - Case D: XX == 11 (binary) + // Skip the next YYYYY pixels (i.e. transparency) + + if (literalPos && isMacSci11ViewData) { + // KQ6/Freddy Pharkas use byte lengths, all others use uint16 + // The SCI devs must have realized that a max of 255 pixels wide + // was not very good for 320 or 640 width games. + bool hasByteLengths = (g_sci->getGameId() == GID_KQ6 || g_sci->getGameId() == GID_FREDDYPHARKAS); + + // compression for SCI1.1+ Mac + while (pixelNr < pixelCount) { + uint32 pixelLine = pixelNr; + + if (hasByteLengths) { + pixelNr += *rlePtr++; + runLength = *rlePtr++; + } else { + pixelNr += READ_BE_UINT16(rlePtr); + runLength = READ_BE_UINT16(rlePtr + 2); + rlePtr += 4; + } + + while (runLength-- && pixelNr < pixelCount) + outPtr[pixelNr++] = *literalPtr++; + + pixelNr = pixelLine + width; + } + return; + } + + switch (viewType) { + case kViewEga: + while (pixelNr < pixelCount) { + curByte = *rlePtr++; + runLength = curByte >> 4; + memset(outPtr + pixelNr, curByte & 0x0F, MIN<uint16>(runLength, pixelCount - pixelNr)); + pixelNr += runLength; + } + break; + case kViewAmiga: + while (pixelNr < pixelCount) { + curByte = *rlePtr++; + if (curByte & 0x07) { // fill with color + runLength = curByte & 0x07; + curByte = curByte >> 3; + memset(outPtr + pixelNr, curByte, MIN<uint16>(runLength, pixelCount - pixelNr)); + } else { // skip the next pixels (transparency) + runLength = curByte >> 3; + } + pixelNr += runLength; + } + break; + case kViewAmiga64: + while (pixelNr < pixelCount) { + curByte = *rlePtr++; + if (curByte & 0xC0) { // fill with color + runLength = curByte >> 6; + memset(outPtr + pixelNr, curByte & 0x3F, MIN<uint16>(runLength, pixelCount - pixelNr)); + } else { // skip the next pixels (transparency) + runLength = curByte & 0x3F; + } + pixelNr += runLength; + } + break; + case kViewVga: + case kViewVga11: + // If we have no RLE data, the image is just uncompressed + if (rlePos == 0) { + memcpy(outPtr, literalPtr, pixelCount); + break; + } + + while (pixelNr < pixelCount) { + curByte = *rlePtr++; + runLength = curByte & 0x3F; + + switch (curByte & 0xC0) { + case 0x40: // copy bytes as is (In copy case, runLength can go up to 127 i.e. pixel & 0x40). Fixes bug #3135872. + runLength += 64; + case 0x00: // copy bytes as-is + if (!literalPos) { + memcpy(outPtr + pixelNr, rlePtr, MIN<uint16>(runLength, pixelCount - pixelNr)); + rlePtr += runLength; + } else { + memcpy(outPtr + pixelNr, literalPtr, MIN<uint16>(runLength, pixelCount - pixelNr)); + literalPtr += runLength; + } + break; + case 0x80: // fill with color + if (!literalPos) + memset(outPtr + pixelNr, *rlePtr++, MIN<uint16>(runLength, pixelCount - pixelNr)); + else + memset(outPtr + pixelNr, *literalPtr++, MIN<uint16>(runLength, pixelCount - pixelNr)); + break; + case 0xC0: // skip the next pixels (transparency) + break; + } + + pixelNr += runLength; + } + break; + default: + error("Unsupported picture viewtype"); + } +} + void GfxView::unpackCel(int16 loopNo, int16 celNo, byte *outPtr, uint32 pixelCount) { const CelInfo *celInfo = getCelInfo(loopNo, celNo); - byte *rlePtr; - byte *literalPtr; - uint32 pixelNo = 0, runLength; - byte pixel; if (celInfo->offsetEGA) { // decompression for EGA views - literalPtr = _resourceData + _loop[loopNo].cel[celNo].offsetEGA; - while (pixelNo < pixelCount) { - pixel = *literalPtr++; - runLength = pixel >> 4; - memset(outPtr + pixelNo, pixel & 0x0F, MIN<uint32>(runLength, pixelCount - pixelNo)); - pixelNo += runLength; - } + unpackCelData(_resourceData, outPtr, 0, pixelCount, celInfo->offsetEGA, 0, _resMan->getViewType(), celInfo->width, false); } else { // We fill the buffer with transparent pixels, so that we can later skip // over pixels to automatically have them transparent @@ -411,100 +551,8 @@ void GfxView::unpackCel(int16 loopNo, int16 celNo, byte *outPtr, uint32 pixelCou clearColor = 0; } - memset(outPtr, clearColor, pixelCount); - - rlePtr = _resourceData + celInfo->offsetRLE; - if (!celInfo->offsetLiteral) { // no additional literal data - if (_resMan->isAmiga32color()) { - // decompression for amiga views - while (pixelNo < pixelCount) { - pixel = *rlePtr++; - if (pixel & 0x07) { // fill with color - runLength = pixel & 0x07; - pixel = pixel >> 3; - while (runLength-- && pixelNo < pixelCount) { - outPtr[pixelNo++] = pixel; - } - } else { // fill with transparent - runLength = pixel >> 3; - pixelNo += runLength; - } - } - } else { - // decompression for data that has just one combined stream - while (pixelNo < pixelCount) { - pixel = *rlePtr++; - runLength = pixel & 0x3F; - switch (pixel & 0xC0) { - case 0x40: // copy bytes as is (In copy case, runLength can go upto 127 i.e. pixel & 0x40) - runLength += 64; - case 0x00: // copy bytes as-is - while (runLength-- && pixelNo < pixelCount) - outPtr[pixelNo++] = *rlePtr++; - break; - case 0x80: // fill with color - memset(outPtr + pixelNo, *rlePtr++, MIN<uint32>(runLength, pixelCount - pixelNo)); - pixelNo += runLength; - break; - case 0xC0: // fill with transparent - pixelNo += runLength; - break; - } - } - } - } else { - literalPtr = _resourceData + celInfo->offsetLiteral; - if (celInfo->offsetRLE) { - if (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() == SCI_VERSION_1_1) { - // KQ6/Freddy Pharkas use byte lengths, all others use uint16 - // The SCI devs must have realized that a max of 255 pixels wide - // was not very good for 320 or 640 width games. - bool hasByteLengths = (g_sci->getGameId() == GID_KQ6 || g_sci->getGameId() == GID_FREDDYPHARKAS); - - // compression for SCI1.1+ Mac - while (pixelNo < pixelCount) { - uint32 pixelLine = pixelNo; - - if (hasByteLengths) { - pixelNo += *rlePtr++; - runLength = *rlePtr++; - } else { - pixelNo += READ_BE_UINT16(rlePtr); - runLength = READ_BE_UINT16(rlePtr + 2); - rlePtr += 4; - } - - while (runLength-- && pixelNo < pixelCount) - outPtr[pixelNo++] = *literalPtr++; - - pixelNo = pixelLine + celInfo->width; - } - } else { - // decompression for data that has separate rle and literal streams - while (pixelNo < pixelCount) { - pixel = *rlePtr++; - runLength = pixel & 0x3F; - switch (pixel & 0xC0) { - case 0: // copy bytes as-is - while (runLength-- && pixelNo < pixelCount) - outPtr[pixelNo++] = *literalPtr++; - break; - case 0x80: // fill with color - memset(outPtr + pixelNo, *literalPtr++, MIN<uint32>(runLength, pixelCount - pixelNo)); - pixelNo += runLength; - break; - case 0xC0: // fill with transparent - pixelNo += runLength; - break; - } - } - } - } else { - // literal stream only, so no compression - memcpy(outPtr, literalPtr, pixelCount); - pixelNo = pixelCount; - } - } + bool isMacSci11ViewData = g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() == SCI_VERSION_1_1; + unpackCelData(_resourceData, outPtr, clearColor, pixelCount, celInfo->offsetRLE, celInfo->offsetLiteral, _resMan->getViewType(), celInfo->width, isMacSci11ViewData); // Swap 0 and 0xff pixels for Mac SCI1.1+ games (see above) if (g_sci->getPlatform() == Common::kPlatformMacintosh && getSciVersion() >= SCI_VERSION_1_1) { @@ -534,9 +582,8 @@ const byte *GfxView::getBitmap(int16 loopNo, int16 celNo) { // unpack the actual cel bitmap data unpackCel(loopNo, celNo, pBitmap, pixelCount); - if (!_resMan->isVGA()) { + if (_resMan->getViewType() == kViewEga) unditherBitmap(pBitmap, width, height, _loop[loopNo].cel[celNo].clearKey); - } // mirroring the cel if needed if (_loop[loopNo].mirrorFlag) { @@ -552,19 +599,15 @@ const byte *GfxView::getBitmap(int16 loopNo, int16 celNo) { * cel if the dithering in here matches dithering used by the current picture. */ void GfxView::unditherBitmap(byte *bitmapPtr, int16 width, int16 height, byte clearKey) { - int16 *unditherMemorial = _screen->unditherGetMemorial(); + int16 *ditheredPicColors = _screen->unditherGetDitheredBgColors(); - // It makes no sense to go further, if no memorial data from current picture - // is available - if (!unditherMemorial) + // It makes no sense to go further, if there isn't any dithered color data + // available for the current picture + if (!ditheredPicColors) return; - // Makes no sense to process bitmaps that are 3 pixels wide or less - if (width <= 3) - return; - - // We need at least 2 pixel lines - if (height < 2) + // We need at least a 4x2 bitmap for this algorithm to work + if (width < 4 || height < 2) return; // If EGA mapping is used for this view, dont do undithering as well @@ -572,13 +615,13 @@ void GfxView::unditherBitmap(byte *bitmapPtr, int16 width, int16 height, byte cl return; // Walk through the bitmap and remember all combinations of colors - int16 bitmapMemorial[SCI_SCREEN_UNDITHERMEMORIAL_SIZE]; + int16 ditheredBitmapColors[DITHERED_BG_COLORS_SIZE]; byte *curPtr; byte color1, color2; byte nextColor1, nextColor2; int16 y, x; - memset(&bitmapMemorial, 0, sizeof(bitmapMemorial)); + memset(&ditheredBitmapColors, 0, sizeof(ditheredBitmapColors)); // Count all seemingly dithered pixel-combinations as soon as at least 4 // pixels are adjacent and check pixels in the following line as well to @@ -597,17 +640,17 @@ void GfxView::unditherBitmap(byte *bitmapPtr, int16 width, int16 height, byte cl nextColor1 = (nextColor1 >> 4) | (nextColor2 << 4); nextColor2 = (nextColor2 >> 4) | *nextPtr++ << 4; if ((color1 == color2) && (color1 == nextColor1) && (color1 == nextColor2)) - bitmapMemorial[color1]++; + ditheredBitmapColors[color1]++; } } - // Now compare both memorial tables to find out matching - // dithering-combinations - bool unditherTable[SCI_SCREEN_UNDITHERMEMORIAL_SIZE]; + // Now compare both dither color tables to find out matching dithered color + // combinations + bool unditherTable[DITHERED_BG_COLORS_SIZE]; byte color, unditherCount = 0; memset(&unditherTable, false, sizeof(unditherTable)); for (color = 0; color < 255; color++) { - if ((bitmapMemorial[color] > 5) && (unditherMemorial[color] > 200)) { + if ((ditheredBitmapColors[color] > 5) && (ditheredPicColors[color] > 200)) { // match found, check if colorKey is contained -> if so, we ignore // of course color1 = color & 0x0F; color2 = color >> 4; @@ -655,10 +698,9 @@ void GfxView::draw(const Common::Rect &rect, const Common::Rect &clipRect, const const byte drawMask = (priority == 255) ? GFX_SCREEN_MASK_VISUAL : GFX_SCREEN_MASK_VISUAL|GFX_SCREEN_MASK_PRIORITY; int x, y; - if (_embeddedPal) { + if (_embeddedPal) // Merge view palette in... _palette->set(&_viewPalette, false); - } const int16 width = MIN(clipRect.width(), celWidth); const int16 height = MIN(clipRect.height(), celHeight); @@ -679,7 +721,9 @@ void GfxView::draw(const Common::Rect &rect, const Common::Rect &clipRect, const // UpscaledHires means view is hires and is supposed to // get drawn onto lowres screen. // FIXME(?): we can't read priority directly with the - // hires coordinates. may not be needed at all in kq6 + // hires coordinates. May not be needed at all in kq6 + // FIXME: Handle proper aspect ratio. Some GK1 hires images + // are in 640x400 instead of 640x480 _screen->putPixelOnDisplay(x2, y2, palette->mapping[color]); } } @@ -718,10 +762,9 @@ void GfxView::drawScaled(const Common::Rect &rect, const Common::Rect &clipRect, int16 scaledWidth, scaledHeight; int pixelNo, scaledPixel, scaledPixelNo, prevScaledPixelNo; - if (_embeddedPal) { + if (_embeddedPal) // Merge view palette in... _palette->set(&_viewPalette, false); - } scaledWidth = (celInfo->width * scaleX) >> 7; scaledHeight = (celInfo->height * scaleY) >> 7; @@ -788,4 +831,12 @@ void GfxView::drawScaled(const Common::Rect &rect, const Common::Rect &clipRect, } } +void GfxView::adjustToUpscaledCoordinates(int16 &y, int16 &x) { + _screen->adjustToUpscaledCoordinates(y, x, _sci2ScaleRes); +} + +void GfxView::adjustBackUpscaledCoordinates(int16 &y, int16 &x) { + _screen->adjustBackUpscaledCoordinates(y, x, _sci2ScaleRes); +} + } // End of namespace Sci diff --git a/engines/sci/graphics/view.h b/engines/sci/graphics/view.h index f785e3c475..36914f916c 100644 --- a/engines/sci/graphics/view.h +++ b/engines/sci/graphics/view.h @@ -28,6 +28,13 @@ namespace Sci { +enum Sci32ViewNativeResolution { + SCI_VIEW_NATIVERES_NONE = -1, + SCI_VIEW_NATIVERES_320x200 = 0, + SCI_VIEW_NATIVERES_640x480 = 1, + SCI_VIEW_NATIVERES_640x400 = 2 +}; + struct CelInfo { int16 width, height; int16 scriptWidth, scriptHeight; @@ -78,6 +85,9 @@ public: bool isScaleable(); bool isSci2Hires(); + void adjustToUpscaledCoordinates(int16 &y, int16 &x); + void adjustBackUpscaledCoordinates(int16 &y, int16 &x); + private: void initData(GuiResourceId resourceId); void unpackCel(int16 loopNo, int16 celNo, byte *outPtr, uint32 pixelCount); @@ -98,8 +108,8 @@ private: bool _embeddedPal; Palette _viewPalette; - // set for SCI2 views in gk1/windows, means that views are hires and should be handled accordingly - bool _isSci2Hires; + // specifies scaling resolution for SCI2 views (see gk1/windows, Wolfgang in room 720) + Sci32ViewNativeResolution _sci2ScaleRes; byte *_EGAmapping; |