diff options
Diffstat (limited to 'engines/sci/graphics/palette32.cpp')
-rw-r--r-- | engines/sci/graphics/palette32.cpp | 792 |
1 files changed, 369 insertions, 423 deletions
diff --git a/engines/sci/graphics/palette32.cpp b/engines/sci/graphics/palette32.cpp index 9204e4bf96..56e1940224 100644 --- a/engines/sci/graphics/palette32.cpp +++ b/engines/sci/graphics/palette32.cpp @@ -27,20 +27,111 @@ #include "sci/sci.h" #include "sci/event.h" #include "sci/resource.h" +#include "sci/util.h" +#include "sci/engine/features.h" #include "sci/graphics/palette32.h" +#include "sci/graphics/remap32.h" #include "sci/graphics/screen.h" namespace Sci { -GfxPalette32::GfxPalette32(ResourceManager *resMan, GfxScreen *screen) - : GfxPalette(resMan, screen), +#pragma mark HunkPalette + +HunkPalette::HunkPalette(byte *rawPalette) : + _version(0), + // NOTE: The header size in palettes is garbage. In at least KQ7 2.00b and + // Phant1, the 999.pal sets this value to 0. In most other palettes it is + // set to 14, but the *actual* size of the header structure used in SSCI is + // 13, which is reflected by `kHunkPaletteHeaderSize`. + // _headerSize(rawPalette[0]), + _numPalettes(rawPalette[10]), + _data(nullptr) { + assert(_numPalettes == 0 || _numPalettes == 1); + if (_numPalettes) { + _data = rawPalette; + _version = getEntryHeader().version; + } +} + +void HunkPalette::setVersion(const uint32 version) const { + if (_numPalettes != _data[10]) { + error("Invalid HunkPalette"); + } + + if (_numPalettes) { + const EntryHeader header = getEntryHeader(); + if (header.version != _version) { + error("Invalid HunkPalette"); + } + + WRITE_SCI11ENDIAN_UINT32(getPalPointer() + kEntryVersionOffset, version); + _version = version; + } +} + +const HunkPalette::EntryHeader HunkPalette::getEntryHeader() const { + const byte *const data = getPalPointer(); + + EntryHeader header; + header.startColor = data[10]; + header.numColors = READ_SCI11ENDIAN_UINT16(data + 14); + header.used = data[16]; + header.sharedUsed = data[17]; + header.version = READ_SCI11ENDIAN_UINT32(data + kEntryVersionOffset); + + return header; +} + +const Palette HunkPalette::toPalette() const { + Palette outPalette; + + for (int16 i = 0; i < ARRAYSIZE(outPalette.colors); ++i) { + outPalette.colors[i].used = false; + outPalette.colors[i].r = 0; + outPalette.colors[i].g = 0; + outPalette.colors[i].b = 0; + } + + if (_numPalettes) { + const EntryHeader header = getEntryHeader(); + const byte *data = getPalPointer() + kEntryHeaderSize; + + const int16 end = header.startColor + header.numColors; + assert(end <= 256); + + if (header.sharedUsed) { + for (int16 i = header.startColor; i < end; ++i) { + outPalette.colors[i].used = header.used; + outPalette.colors[i].r = *data++; + outPalette.colors[i].g = *data++; + outPalette.colors[i].b = *data++; + } + } else { + for (int16 i = header.startColor; i < end; ++i) { + outPalette.colors[i].used = *data++; + outPalette.colors[i].r = *data++; + outPalette.colors[i].g = *data++; + outPalette.colors[i].b = *data++; + } + } + } + + return outPalette; +} + +#pragma mark - +#pragma mark GfxPalette32 + +GfxPalette32::GfxPalette32(ResourceManager *resMan) + : _resMan(resMan), + // Palette versioning _version(1), - _versionUpdated(false), - _sourcePalette(_sysPalette), - _nextPalette(_sysPalette), - // Clut - _clutTable(nullptr), + _needsUpdate(false), + _currentPalette(), + _sourcePalette(), + _nextPalette(), + // Palette varying _varyStartPalette(nullptr), _varyTargetPalette(nullptr), @@ -51,107 +142,57 @@ GfxPalette32::GfxPalette32(ResourceManager *resMan, GfxScreen *screen) _varyDirection(0), _varyTargetPercent(0), _varyNumTimesPaused(0), + // Palette cycling _cyclers(), _cycleMap() { - _varyPercent = _varyTargetPercent; - memset(_fadeTable, 100, sizeof(_fadeTable)); - // NOTE: In SCI engine, the palette manager constructor loads - // the default palette, but in ScummVM this initialisation - // is performed by SciEngine::run; see r49523 for details + _varyPercent = _varyTargetPercent; + for (int i = 0, len = ARRAYSIZE(_fadeTable); i < len; ++i) { + _fadeTable[i] = 100; + } + + loadPalette(999); } GfxPalette32::~GfxPalette32() { +#ifdef ENABLE_SCI3_GAMES unloadClut(); +#endif varyOff(); cycleAllOff(); } -inline void mergePaletteInternal(Palette *const to, const Palette *const from) { - for (int i = 0; i < ARRAYSIZE(to->colors); ++i) { - if (from->colors[i].used) { - to->colors[i] = from->colors[i]; - } - } -} - -const Palette *GfxPalette32::getNextPalette() const { - return &_nextPalette; -} - -void GfxPalette32::submit(Palette &palette) { - // TODO: The resource manager in SCI32 retains raw data of palettes from - // the ResourceManager (ResourceMgr) through SegManager (MemoryMgr), and - // the version number for submitted palettes is set in the raw palette - // data in memory as an int at an offset - // `rawData + *rawData[0x0a] * 2 + 31`. However, ScummVM does not retain - // resource data like this, so this versioning code, while accurate to - // the original engine, does not do much. - // (Hopefully this was an optimisation mechanism in SCI engine and not a - // clever thing to keep the same palette submitted many times from - // overwriting other palette entries.) - if (palette.timestamp == _version) { - return; - } - - Palette oldSourcePalette(_sourcePalette); - mergePaletteInternal(&_sourcePalette, &palette); - - if (!_versionUpdated && _sourcePalette != oldSourcePalette) { - ++_version; - _versionUpdated = true; - } - - // Technically this information is supposed to be persisted through a - // HunkPalette object; right now it would just be lost once the temporary - // palette was destroyed. - palette.timestamp = _version; -} - -bool GfxPalette32::kernelSetFromResource(GuiResourceId resourceId, bool force) { - // TODO: In SCI32, palettes that come from resources come in as - // HunkPalette objects, not SOLPalette objects. The HunkPalettes - // have some extra persistence stuff associated with them, such that - // when they are passed to GfxPalette32::submit, they would get the - // version number of GfxPalette32 assigned to them. - Palette palette; +bool GfxPalette32::loadPalette(const GuiResourceId resourceId) { + Resource *palResource = _resMan->findResource(ResourceId(kResourceTypePalette, resourceId), false); - if (createPaletteFromResourceInternal(resourceId, &palette)) { - submit(palette); - return true; + if (!palResource) { + return false; } - return false; + const HunkPalette palette(palResource->data); + submit(palette); + return true; } -// In SCI32 engine this method is SOLPalette::Match(Rgb24 *) -// and is called as PaletteMgr.Current().Match(color) -int16 GfxPalette32::kernelFindColor(uint16 r, uint16 g, uint16 b) { - // SQ6 SCI32 engine takes the 16-bit r, g, b arguments from the - // VM and puts them into al, ah, dl. For compatibility, make sure - // to discard any high bits here too - r = r & 0xFF; - g = g & 0xFF; - b = b & 0xFF; +int16 GfxPalette32::matchColor(const uint8 r, const uint8 g, const uint8 b) { int16 bestIndex = 0; int bestDifference = 0xFFFFF; int difference; - // SQ6 DOS really does check only the first 236 entries - for (int i = 0, channelDifference; i < 236; ++i) { - difference = _sysPalette.colors[i].r - r; + for (int i = 0, channelDifference; i < g_sci->_gfxRemap32->getStartColor(); ++i) { + difference = _currentPalette.colors[i].r - r; difference *= difference; if (bestDifference <= difference) { continue; } - channelDifference = _sysPalette.colors[i].g - g; + channelDifference = _currentPalette.colors[i].g - g; difference += channelDifference * channelDifference; if (bestDifference <= difference) { continue; } - channelDifference = _sysPalette.colors[i].b - b; + channelDifference = _currentPalette.colors[i].b - b; difference += channelDifference * channelDifference; if (bestDifference <= difference) { continue; @@ -163,192 +204,158 @@ int16 GfxPalette32::kernelFindColor(uint16 r, uint16 g, uint16 b) { return bestIndex; } -// TODO: set is overridden for the time being to send palettes coming from -// various draw methods like GfxPicture::drawSci32Vga and GfxView::draw to -// _nextPalette instead of _sysPalette. In the SCI32 engine, CelObj palettes -// (which are stored as Hunk palettes) are submitted by GraphicsMgr::FrameOut -// to PaletteMgr::Submit by way of calls to CelObj::SubmitPalette. -// GfxPalette::set is very similar to GfxPalette32::submit, except that SCI32 -// does not do any fancy best-fit merging and so does not accept arguments -// like `force` and `forceRealMerge`. -void GfxPalette32::set(Palette *newPalette, bool force, bool forceRealMerge) { - submit(*newPalette); -} - -// In SCI32 engine this method is SOLPalette::Match(Rgb24 *, int, int *, int *) -// and is used by Remap -// TODO: Anything that calls GfxPalette::matchColor(int, int, int) is going to -// match using an algorithm from SCI16 engine right now. This needs to be -// corrected in the future so either nothing calls -// GfxPalette::matchColor(int, int, int), or it is fixed to match the other -// SCI32 algorithms. -int16 GfxPalette32::matchColor(const byte r, const byte g, const byte b, const int defaultDifference, int &lastCalculatedDifference, const bool *const matchTable) { - int16 bestIndex = -1; - int bestDifference = 0xFFFFF; - int difference = defaultDifference; +void GfxPalette32::submit(const Palette &palette) { + const Palette oldSourcePalette(_sourcePalette); + mergePalette(_sourcePalette, palette); - // SQ6 DOS really does check only the first 236 entries - for (int i = 0, channelDifference; i < 236; ++i) { - if (matchTable[i] == 0) { - continue; - } + if (!_needsUpdate && _sourcePalette != oldSourcePalette) { + ++_version; + _needsUpdate = true; + } +} - difference = _sysPalette.colors[i].r - r; - difference *= difference; - if (bestDifference <= difference) { - continue; - } - channelDifference = _sysPalette.colors[i].g - g; - difference += channelDifference * channelDifference; - if (bestDifference <= difference) { - continue; - } - channelDifference = _sysPalette.colors[i].b - b; - difference += channelDifference * channelDifference; - if (bestDifference <= difference) { - continue; - } - bestDifference = difference; - bestIndex = i; +void GfxPalette32::submit(const HunkPalette &hunkPalette) { + if (hunkPalette.getVersion() == _version) { + return; } - // NOTE: This value is only valid if the last index to - // perform a difference calculation was the best index - lastCalculatedDifference = difference; - return bestIndex; + const Palette oldSourcePalette(_sourcePalette); + const Palette palette = hunkPalette.toPalette(); + mergePalette(_sourcePalette, palette); + + if (!_needsUpdate && oldSourcePalette != _sourcePalette) { + ++_version; + _needsUpdate = true; + } + + hunkPalette.setVersion(_version); } bool GfxPalette32::updateForFrame() { applyAll(); - _versionUpdated = false; - // TODO: Implement remapping - // return g_sci->_gfxFrameout->remapAllTables(_nextPalette != _sysPalette); - return false; + _needsUpdate = false; + return g_sci->_gfxRemap32->remapAllTables(_nextPalette != _currentPalette); } void GfxPalette32::updateFFrame() { for (int i = 0; i < ARRAYSIZE(_nextPalette.colors); ++i) { _nextPalette.colors[i] = _sourcePalette.colors[i]; } - _versionUpdated = false; - // TODO: Implement remapping - // g_sci->_gfxFrameout->remapAllTables(_nextPalette != _sysPalette); + _needsUpdate = false; + g_sci->_gfxRemap32->remapAllTables(_nextPalette != _currentPalette); } -void GfxPalette32::updateHardware() { - if (_sysPalette == _nextPalette) { +void GfxPalette32::updateHardware(const bool updateScreen) { + if (_currentPalette == _nextPalette) { return; } byte bpal[3 * 256]; - for (int i = 0; i < ARRAYSIZE(_sysPalette.colors); ++i) { - _sysPalette.colors[i] = _nextPalette.colors[i]; + for (int i = 0; i < ARRAYSIZE(_currentPalette.colors) - 1; ++i) { + _currentPalette.colors[i] = _nextPalette.colors[i]; // NOTE: If the brightness option in the user configuration file is set, - // SCI engine adjusts palette brightnesses here by mapping RGB values to values - // in some hard-coded brightness tables. There is no reason on modern hardware - // to implement this, unless it is discovered that some game uses a non-standard - // brightness setting by default - if (_sysPalette.colors[i].used) { - bpal[i * 3 ] = _sysPalette.colors[i].r; - bpal[i * 3 + 1] = _sysPalette.colors[i].g; - bpal[i * 3 + 2] = _sysPalette.colors[i].b; - } + // SCI engine adjusts palette brightnesses here by mapping RGB values to + // values in some hard-coded brightness tables. There is no reason on + // modern hardware to implement this, unless it is discovered that some + // game uses a non-standard brightness setting by default + + // All color entries MUST be copied, not just "used" entries, otherwise + // uninitialised memory from bpal makes its way into the system palette. + // This would not normally be a problem, except that games sometimes use + // unused palette entries. e.g. Phant1 title screen references palette + // entries outside its own palette, so will render garbage colors where + // the game expects them to be black + bpal[i * 3 ] = _currentPalette.colors[i].r; + bpal[i * 3 + 1] = _currentPalette.colors[i].g; + bpal[i * 3 + 2] = _currentPalette.colors[i].b; + } + + if (g_sci->getPlatform() != Common::kPlatformMacintosh) { + // The last color must always be white + bpal[255 * 3 ] = 255; + bpal[255 * 3 + 1] = 255; + bpal[255 * 3 + 2] = 255; + } else { + bpal[255 * 3 ] = 0; + bpal[255 * 3 + 1] = 0; + bpal[255 * 3 + 2] = 0; } g_system->getPaletteManager()->setPalette(bpal, 0, 256); - g_sci->getEventManager()->updateScreen(); -} - -void GfxPalette32::applyAll() { - applyVary(); - applyCycles(); - applyFade(); + if (updateScreen) { + g_system->updateScreen(); + } } -#pragma mark - -#pragma mark Colour look-up - -bool GfxPalette32::loadClut(uint16 clutId) { - // loadClut() will load a color lookup table from a clu file and set - // the palette found in the file. This is to be used with Phantasmagoria 2. - - unloadClut(); - - Common::String filename = Common::String::format("%d.clu", clutId); - Common::File clut; - - if (!clut.open(filename) || clut.size() != 0x10000 + 236 * 3) - return false; - - // Read in the lookup table - // It maps each RGB565 color to a palette index - _clutTable = new byte[0x10000]; - clut.read(_clutTable, 0x10000); - - Palette pal; - memset(&pal, 0, sizeof(Palette)); +Palette GfxPalette32::getPaletteFromResource(const GuiResourceId resourceId) const { + Resource *palResource = _resMan->findResource(ResourceId(kResourceTypePalette, resourceId), false); - // Setup 1:1 mapping - for (int i = 0; i < 256; i++) { - pal.mapping[i] = i; - } - - // Now load in the palette - for (int i = 1; i <= 236; i++) { - pal.colors[i].used = 1; - pal.colors[i].r = clut.readByte(); - pal.colors[i].g = clut.readByte(); - pal.colors[i].b = clut.readByte(); + if (!palResource) { + error("Could not load vary palette %d", resourceId); } - set(&pal, true); - setOnScreen(); - return true; + const HunkPalette rawPalette(palResource->data); + return rawPalette.toPalette(); } -byte GfxPalette32::matchClutColor(uint16 color) { - // Match a color in RGB565 format to a palette index based on the loaded CLUT - assert(_clutTable); - return _clutTable[color]; +void GfxPalette32::mergePalette(Palette &to, const Palette &from) { + // The last color is always white in SCI, so it is not copied. (Some + // palettes, particularly in KQ7, try to set the last color, which causes + // unnecessary palette updates since the last color is forced by SSCI to a + // specific value) + for (int i = 0; i < ARRAYSIZE(to.colors) - 1; ++i) { + if (from.colors[i].used) { + to.colors[i] = from.colors[i]; + } + } } -void GfxPalette32::unloadClut() { - // This will only unload the actual table, but not reset any palette - delete[] _clutTable; - _clutTable = nullptr; +void GfxPalette32::applyAll() { + applyVary(); + applyCycles(); + applyFade(); } #pragma mark - #pragma mark Varying -inline bool GfxPalette32::createPaletteFromResourceInternal(const GuiResourceId paletteId, Palette *const out) const { - Resource *palResource = _resMan->findResource(ResourceId(kResourceTypePalette, paletteId), false); +void GfxPalette32::setVary(const Palette &target, const int16 percent, const int32 ticks, const int16 fromColor, const int16 toColor) { + setTarget(target); + setVaryTime(percent, ticks); - if (!palResource) { - return false; + if (fromColor > -1) { + _varyFromColor = fromColor; + } + if (toColor > -1) { + assert(toColor < 256); + _varyToColor = toColor; + } +} + +void GfxPalette32::setVaryPercent(const int16 percent, const int32 ticks) { + if (_varyTargetPalette != nullptr) { + setVaryTime(percent, ticks); } - createFromData(palResource->data, palResource->size, out); - return true; + // NOTE: SSCI had two additional parameters for this function to change the + // `_varyFromColor`, but they were always hardcoded to be ignored } -inline Palette GfxPalette32::getPaletteFromResourceInternal(const GuiResourceId paletteId) const { - Palette palette; - if (!createPaletteFromResourceInternal(paletteId, &palette)) { - error("Could not load vary target %d", paletteId); +void GfxPalette32::setVaryTime(const int32 time) { + if (_varyTargetPalette != nullptr) { + setVaryTime(_varyTargetPercent, time); } - return palette; } -inline void GfxPalette32::setVaryTimeInternal(const int16 percent, const int time) { +void GfxPalette32::setVaryTime(const int16 percent, const int32 ticks) { _varyLastTick = g_sci->getTickCount(); - if (!time || _varyPercent == percent) { + if (!ticks || _varyPercent == percent) { _varyDirection = 0; _varyTargetPercent = _varyPercent = percent; } else { - _varyTime = time / (percent - _varyPercent); + _varyTime = ticks / (percent - _varyPercent); _varyTargetPercent = percent; if (_varyTime > 0) { @@ -363,74 +370,6 @@ inline void GfxPalette32::setVaryTimeInternal(const int16 percent, const int tim } } -// TODO: This gets called *a lot* in at least the first scene -// of SQ6. Optimisation would not be the worst idea in the world. -void GfxPalette32::kernelPalVarySet(const GuiResourceId paletteId, const int16 percent, const int time, const int16 fromColor, const int16 toColor) { - Palette palette = getPaletteFromResourceInternal(paletteId); - setVary(&palette, percent, time, fromColor, toColor); -} - -void GfxPalette32::kernelPalVaryMergeTarget(GuiResourceId paletteId) { - Palette palette = getPaletteFromResourceInternal(paletteId); - mergeTarget(&palette); -} - -void GfxPalette32::kernelPalVarySetTarget(GuiResourceId paletteId) { - Palette palette = getPaletteFromResourceInternal(paletteId); - setTarget(&palette); -} - -void GfxPalette32::kernelPalVarySetStart(GuiResourceId paletteId) { - Palette palette = getPaletteFromResourceInternal(paletteId); - setStart(&palette); -} - -void GfxPalette32::kernelPalVaryMergeStart(GuiResourceId paletteId) { - Palette palette = getPaletteFromResourceInternal(paletteId); - mergeStart(&palette); -} - -void GfxPalette32::kernelPalVaryPause(bool pause) { - if (pause) { - varyPause(); - } else { - varyOn(); - } -} - -void GfxPalette32::setVary(const Palette *const target, const int16 percent, const int time, const int16 fromColor, const int16 toColor) { - setTarget(target); - setVaryTimeInternal(percent, time); - - if (fromColor > -1) { - _varyFromColor = fromColor; - } - if (toColor > -1) { - assert(toColor < 256); - _varyToColor = toColor; - } -} - -void GfxPalette32::setVaryPercent(const int16 percent, const int time, const int16 fromColor, const int16 fromColorAlternate) { - if (_varyTargetPalette != nullptr) { - setVaryTimeInternal(percent, time); - } - - // This looks like a mistake in the actual SCI engine (both SQ6 and Lighthouse); - // the values are always hardcoded to -1 in kPalVary, so this code can never - // actually be executed - if (fromColor > -1) { - _varyFromColor = fromColor; - } - if (fromColorAlternate > -1) { - _varyFromColor = fromColorAlternate; - } -} - -int16 GfxPalette32::getVaryPercent() const { - return ABS(_varyPercent); -} - void GfxPalette32::varyOff() { _varyNumTimesPaused = 0; _varyPercent = _varyTargetPercent = 0; @@ -449,14 +388,6 @@ void GfxPalette32::varyOff() { } } -void GfxPalette32::mergeTarget(const Palette *const palette) { - if (_varyTargetPalette != nullptr) { - mergePaletteInternal(_varyTargetPalette, palette); - } else { - _varyTargetPalette = new Palette(*palette); - } -} - void GfxPalette32::varyPause() { _varyDirection = 0; ++_varyNumTimesPaused; @@ -467,51 +398,44 @@ void GfxPalette32::varyOn() { --_varyNumTimesPaused; } - if (_varyTargetPalette != nullptr && _varyNumTimesPaused == 0 && _varyPercent != _varyTargetPercent) { - if (_varyTime == 0) { - _varyPercent = _varyTargetPercent; - } else if (_varyTargetPercent < _varyPercent) { - _varyDirection = -1; + if (_varyTargetPalette != nullptr && _varyNumTimesPaused == 0) { + if (_varyPercent != _varyTargetPercent && _varyTime != 0) { + _varyDirection = (_varyTargetPercent - _varyPercent > 0) ? 1 : -1; } else { - _varyDirection = 1; + _varyPercent = _varyTargetPercent; } } } -void GfxPalette32::setVaryTime(const int time) { - if (_varyTargetPalette == nullptr) { - return; - } - - setVaryTimeInternal(_varyTargetPercent, time); +void GfxPalette32::setTarget(const Palette &palette) { + delete _varyTargetPalette; + _varyTargetPalette = new Palette(palette); } -void GfxPalette32::setTarget(const Palette *const palette) { - if (_varyTargetPalette != nullptr) { - delete _varyTargetPalette; - } - - _varyTargetPalette = new Palette(*palette); +void GfxPalette32::setStart(const Palette &palette) { + delete _varyStartPalette; + _varyStartPalette = new Palette(palette); } -void GfxPalette32::setStart(const Palette *const palette) { +void GfxPalette32::mergeStart(const Palette &palette) { if (_varyStartPalette != nullptr) { - delete _varyStartPalette; + mergePalette(*_varyStartPalette, palette); + } else { + _varyStartPalette = new Palette(palette); } - - _varyStartPalette = new Palette(*palette); } -void GfxPalette32::mergeStart(const Palette *const palette) { - if (_varyStartPalette != nullptr) { - mergePaletteInternal(_varyStartPalette, palette); +void GfxPalette32::mergeTarget(const Palette &palette) { + if (_varyTargetPalette != nullptr) { + mergePalette(*_varyTargetPalette, palette); } else { - _varyStartPalette = new Palette(*palette); + _varyTargetPalette = new Palette(palette); } } void GfxPalette32::applyVary() { - while (g_sci->getTickCount() - _varyLastTick > (uint32)_varyTime && _varyDirection != 0) { + const uint32 now = g_sci->getTickCount(); + while ((int32)(now - _varyLastTick) > _varyTime && _varyDirection != 0) { _varyLastTick += _varyTime; if (_varyPercent == _varyTargetPercent) { @@ -522,7 +446,7 @@ void GfxPalette32::applyVary() { } if (_varyPercent == 0 || _varyTargetPalette == nullptr) { - for (int i = 0, len = ARRAYSIZE(_nextPalette.colors); i < len; ++i) { + for (int i = 0; i < ARRAYSIZE(_nextPalette.colors); ++i) { if (_varyStartPalette != nullptr && i >= _varyFromColor && i <= _varyToColor) { _nextPalette.colors[i] = _varyStartPalette->colors[i]; } else { @@ -530,7 +454,7 @@ void GfxPalette32::applyVary() { } } } else { - for (int i = 0, len = ARRAYSIZE(_nextPalette.colors); i < len; ++i) { + for (int i = 0; i < ARRAYSIZE(_nextPalette.colors); ++i) { if (i >= _varyFromColor && i <= _varyToColor) { Color targetColor = _varyTargetPalette->colors[i]; Color sourceColor; @@ -561,89 +485,71 @@ void GfxPalette32::applyVary() { } } -#pragma mark - -#pragma mark Cycling - -inline void GfxPalette32::clearCycleMap(const uint16 fromColor, const uint16 numColorsToClear) { - bool *mapEntry = _cycleMap + fromColor; - const bool *lastEntry = _cycleMap + numColorsToClear; - while (mapEntry < lastEntry) { - *mapEntry++ = false; - } +void GfxPalette32::kernelPalVarySet(const GuiResourceId paletteId, const int16 percent, const int32 ticks, const int16 fromColor, const int16 toColor) { + const Palette palette = getPaletteFromResource(paletteId); + setVary(palette, percent, ticks, fromColor, toColor); } -inline void GfxPalette32::setCycleMap(const uint16 fromColor, const uint16 numColorsToSet) { - bool *mapEntry = _cycleMap + fromColor; - const bool *lastEntry = _cycleMap + numColorsToSet; - while (mapEntry < lastEntry) { - if (*mapEntry != false) { - error("Cycles intersect"); - } - *mapEntry++ = true; - } +void GfxPalette32::kernelPalVaryMergeTarget(const GuiResourceId paletteId) { + const Palette palette = getPaletteFromResource(paletteId); + mergeTarget(palette); } -inline PalCycler *GfxPalette32::getCycler(const uint16 fromColor) { - const int numCyclers = ARRAYSIZE(_cyclers); - - for (int cyclerIndex = 0; cyclerIndex < numCyclers; ++cyclerIndex) { - PalCycler *cycler = _cyclers[cyclerIndex]; - if (cycler != nullptr && cycler->fromColor == fromColor) { - return cycler; - } - } +void GfxPalette32::kernelPalVarySetTarget(const GuiResourceId paletteId) { + const Palette palette = getPaletteFromResource(paletteId); + setTarget(palette); +} - return nullptr; +void GfxPalette32::kernelPalVarySetStart(const GuiResourceId paletteId) { + const Palette palette = getPaletteFromResource(paletteId); + setStart(palette); } -inline void doCycleInternal(PalCycler *cycler, const int16 speed) { - int16 currentCycle = cycler->currentCycle; - const uint16 numColorsToCycle = cycler->numColorsToCycle; +void GfxPalette32::kernelPalVaryMergeStart(const GuiResourceId paletteId) { + const Palette palette = getPaletteFromResource(paletteId); + mergeStart(palette); +} - if (cycler->direction == 0) { - currentCycle = (currentCycle - (speed % numColorsToCycle)) + numColorsToCycle; +void GfxPalette32::kernelPalVaryPause(const bool pause) { + if (pause) { + varyPause(); } else { - currentCycle = currentCycle + speed; + varyOn(); } - - cycler->currentCycle = (uint8) (currentCycle % numColorsToCycle); } +#pragma mark - +#pragma mark Cycling + void GfxPalette32::setCycle(const uint8 fromColor, const uint8 toColor, const int16 direction, const int16 delay) { assert(fromColor < toColor); - int cyclerIndex; - const int numCyclers = ARRAYSIZE(_cyclers); - PalCycler *cycler = getCycler(fromColor); if (cycler != nullptr) { clearCycleMap(fromColor, cycler->numColorsToCycle); } else { - for (cyclerIndex = 0; cyclerIndex < numCyclers; ++cyclerIndex) { - if (_cyclers[cyclerIndex] == nullptr) { - cycler = new PalCycler; - _cyclers[cyclerIndex] = cycler; + for (int i = 0; i < kNumCyclers; ++i) { + if (_cyclers[i] == nullptr) { + _cyclers[i] = cycler = new PalCycler; break; } } } - // SCI engine overrides the first oldest cycler that it finds where - // “oldest” is determined by the difference between the tick and now + // If there are no free cycler slots, SCI engine overrides the first oldest + // cycler that it finds, where "oldest" is determined by the difference + // between the tick and now if (cycler == nullptr) { - int maxUpdateDelta = -1; - // Optimization: Unlike actual SCI (SQ6) engine, we call - // getTickCount only once and store it, instead of calling it - // twice on each iteration through the loop const uint32 now = g_sci->getTickCount(); + uint32 minUpdateDelta = 0xFFFFFFFF; - for (cyclerIndex = 0; cyclerIndex < numCyclers; ++cyclerIndex) { - PalCycler *candidate = _cyclers[cyclerIndex]; + for (int i = 0; i < kNumCyclers; ++i) { + PalCycler *const candidate = _cyclers[i]; - const int32 updateDelta = now - candidate->lastUpdateTick; - if (updateDelta >= maxUpdateDelta) { - maxUpdateDelta = updateDelta; + const uint32 updateDelta = now - candidate->lastUpdateTick; + if (updateDelta < minUpdateDelta) { + minUpdateDelta = updateDelta; cycler = candidate; } } @@ -651,11 +557,14 @@ void GfxPalette32::setCycle(const uint8 fromColor, const uint8 toColor, const in clearCycleMap(cycler->fromColor, cycler->numColorsToCycle); } - const uint16 numColorsToCycle = 1 + ((uint8) toColor) - fromColor; - cycler->fromColor = (uint8) fromColor; - cycler->numColorsToCycle = (uint8) numColorsToCycle; - cycler->currentCycle = (uint8) fromColor; - cycler->direction = direction < 0 ? PalCycleBackward : PalCycleForward; + uint16 numColorsToCycle = toColor - fromColor; + if (g_sci->_features->hasNewPaletteCode()) { + numColorsToCycle += 1; + } + cycler->fromColor = fromColor; + cycler->numColorsToCycle = numColorsToCycle; + cycler->currentCycle = fromColor; + cycler->direction = direction < 0 ? kPalCycleBackward : kPalCycleForward; cycler->delay = delay; cycler->lastUpdateTick = g_sci->getTickCount(); cycler->numTimesPaused = 0; @@ -664,30 +573,30 @@ void GfxPalette32::setCycle(const uint8 fromColor, const uint8 toColor, const in } void GfxPalette32::doCycle(const uint8 fromColor, const int16 speed) { - PalCycler *cycler = getCycler(fromColor); + PalCycler *const cycler = getCycler(fromColor); if (cycler != nullptr) { cycler->lastUpdateTick = g_sci->getTickCount(); - doCycleInternal(cycler, speed); + updateCycler(*cycler, speed); } } void GfxPalette32::cycleOn(const uint8 fromColor) { - PalCycler *cycler = getCycler(fromColor); + PalCycler *const cycler = getCycler(fromColor); if (cycler != nullptr && cycler->numTimesPaused > 0) { --cycler->numTimesPaused; } } void GfxPalette32::cyclePause(const uint8 fromColor) { - PalCycler *cycler = getCycler(fromColor); + PalCycler *const cycler = getCycler(fromColor); if (cycler != nullptr) { ++cycler->numTimesPaused; } } void GfxPalette32::cycleAllOn() { - for (int i = 0, len = ARRAYSIZE(_cyclers); i < len; ++i) { - PalCycler *cycler = _cyclers[i]; + for (int i = 0; i < kNumCyclers; ++i) { + PalCycler *const cycler = _cyclers[i]; if (cycler != nullptr && cycler->numTimesPaused > 0) { --cycler->numTimesPaused; } @@ -695,16 +604,10 @@ void GfxPalette32::cycleAllOn() { } void GfxPalette32::cycleAllPause() { - // TODO: The SCI SQ6 cycleAllPause function does not seem to perform - // nullptr checking?? This would definitely cause null pointer - // dereference in SCI code. I have not seen anything actually call - // this function yet, so it is possible it is just unused and broken - // in SCI SQ6. This assert exists so that if this function is called, - // it is noticed and can be rechecked in the actual engine. - // Obviously this code *does* do nullptr checks instead of crashing. :) - assert(0); - for (int i = 0, len = ARRAYSIZE(_cyclers); i < len; ++i) { - PalCycler *cycler = _cyclers[i]; + // NOTE: The original engine did not check for null pointers in the + // palette cyclers pointer array. + for (int i = 0; i < kNumCyclers; ++i) { + PalCycler *const cycler = _cyclers[i]; if (cycler != nullptr) { // This seems odd, because currentCycle is 0..numColorsPerCycle, // but fromColor is 0..255. When applyAllCycles runs, the values @@ -715,8 +618,8 @@ void GfxPalette32::cycleAllPause() { applyAllCycles(); - for (int i = 0, len = ARRAYSIZE(_cyclers); i < len; ++i) { - PalCycler *cycler = _cyclers[i]; + for (int i = 0; i < kNumCyclers; ++i) { + PalCycler *const cycler = _cyclers[i]; if (cycler != nullptr) { ++cycler->numTimesPaused; } @@ -724,8 +627,8 @@ void GfxPalette32::cycleAllPause() { } void GfxPalette32::cycleOff(const uint8 fromColor) { - for (int i = 0, len = ARRAYSIZE(_cyclers); i < len; ++i) { - PalCycler *cycler = _cyclers[i]; + for (int i = 0; i < kNumCyclers; ++i) { + PalCycler *const cycler = _cyclers[i]; if (cycler != nullptr && cycler->fromColor == fromColor) { clearCycleMap(fromColor, cycler->numColorsToCycle); delete cycler; @@ -736,8 +639,8 @@ void GfxPalette32::cycleOff(const uint8 fromColor) { } void GfxPalette32::cycleAllOff() { - for (int i = 0, len = ARRAYSIZE(_cyclers); i < len; ++i) { - PalCycler *cycler = _cyclers[i]; + for (int i = 0; i < kNumCyclers; ++i) { + PalCycler *const cycler = _cyclers[i]; if (cycler != nullptr) { clearCycleMap(cycler->fromColor, cycler->numColorsToCycle); delete cycler; @@ -746,16 +649,57 @@ void GfxPalette32::cycleAllOff() { } } +void GfxPalette32::updateCycler(PalCycler &cycler, const int16 speed) { + int16 currentCycle = cycler.currentCycle; + const uint16 numColorsToCycle = cycler.numColorsToCycle; + + if (cycler.direction == kPalCycleBackward) { + currentCycle = (currentCycle - (speed % numColorsToCycle)) + numColorsToCycle; + } else { + currentCycle = currentCycle + speed; + } + + cycler.currentCycle = currentCycle % numColorsToCycle; +} + +void GfxPalette32::clearCycleMap(const uint16 fromColor, const uint16 numColorsToClear) { + bool *mapEntry = _cycleMap + fromColor; + const bool *const lastEntry = _cycleMap + numColorsToClear; + while (mapEntry < lastEntry) { + *mapEntry++ = false; + } +} + +void GfxPalette32::setCycleMap(const uint16 fromColor, const uint16 numColorsToSet) { + bool *mapEntry = _cycleMap + fromColor; + const bool *const lastEntry = _cycleMap + numColorsToSet; + while (mapEntry < lastEntry) { + if (*mapEntry != false) { + error("Cycles intersect"); + } + *mapEntry++ = true; + } +} + +PalCycler *GfxPalette32::getCycler(const uint16 fromColor) { + for (int cyclerIndex = 0; cyclerIndex < kNumCyclers; ++cyclerIndex) { + PalCycler *cycler = _cyclers[cyclerIndex]; + if (cycler != nullptr && cycler->fromColor == fromColor) { + return cycler; + } + } + + return nullptr; +} + void GfxPalette32::applyAllCycles() { Color paletteCopy[256]; memcpy(paletteCopy, _nextPalette.colors, sizeof(Color) * 256); - for (int cyclerIndex = 0, numCyclers = ARRAYSIZE(_cyclers); cyclerIndex < numCyclers; ++cyclerIndex) { - PalCycler *cycler = _cyclers[cyclerIndex]; + for (int i = 0; i < kNumCyclers; ++i) { + PalCycler *const cycler = _cyclers[i]; if (cycler != nullptr) { - cycler->currentCycle = (uint8) ((((int) cycler->currentCycle) + 1) % cycler->numColorsToCycle); - // Disassembly was not fully evaluated to verify this is exactly the same - // as the code from applyCycles, but it appeared to be at a glance + cycler->currentCycle = (((int) cycler->currentCycle) + 1) % cycler->numColorsToCycle; for (int j = 0; j < cycler->numColorsToCycle; j++) { _nextPalette.colors[cycler->fromColor + j] = paletteCopy[cycler->fromColor + (cycler->currentCycle + j) % cycler->numColorsToCycle]; } @@ -767,15 +711,16 @@ void GfxPalette32::applyCycles() { Color paletteCopy[256]; memcpy(paletteCopy, _nextPalette.colors, sizeof(Color) * 256); - for (int i = 0, len = ARRAYSIZE(_cyclers); i < len; ++i) { - PalCycler *cycler = _cyclers[i]; + const uint32 now = g_sci->getTickCount(); + for (int i = 0; i < kNumCyclers; ++i) { + PalCycler *const cycler = _cyclers[i]; if (cycler == nullptr) { continue; } if (cycler->delay != 0 && cycler->numTimesPaused == 0) { - while ((cycler->delay + cycler->lastUpdateTick) < g_sci->getTickCount()) { - doCycleInternal(cycler, 1); + while ((cycler->delay + cycler->lastUpdateTick) < now) { + updateCycler(*cycler, 1); cycler->lastUpdateTick += cycler->delay; } } @@ -789,37 +734,38 @@ void GfxPalette32::applyCycles() { #pragma mark - #pragma mark Fading -// NOTE: There are some game scripts (like SQ6 Sierra logo and main menu) that call -// setFade with numColorsToFade set to 256, but other parts of the engine like -// processShowStyleNone use 255 instead of 256. It is not clear if this is because -// the last palette entry is intentionally left unmodified, or if this is a bug -// in the engine. It certainly seems confused because all other places that accept -// color ranges typically receive values in the range of 0–255. -void GfxPalette32::setFade(uint8 percent, uint8 fromColor, uint16 numColorsToFade) { - if (fromColor > numColorsToFade) { +void GfxPalette32::setFade(const uint16 percent, const uint8 fromColor, uint16 toColor) { + if (fromColor > toColor) { return; } - assert(numColorsToFade <= ARRAYSIZE(_fadeTable)); + // Some game scripts (like SQ6 Sierra logo and main menu) incorrectly call + // setFade with toColor set to 256 + if (toColor > 255) { + toColor = 255; + } - for (int i = fromColor; i < numColorsToFade; i++) + for (int i = fromColor; i <= toColor; i++) { _fadeTable[i] = percent; + } } void GfxPalette32::fadeOff() { - setFade(100, 0, 256); + setFade(100, 0, 255); } void GfxPalette32::applyFade() { for (int i = 0; i < ARRAYSIZE(_fadeTable); ++i) { - if (_fadeTable[i] == 100) + if (_fadeTable[i] == 100) { continue; + } Color &color = _nextPalette.colors[i]; - color.r = (int16)color.r * _fadeTable[i] / 100; - color.g = (int16)color.g * _fadeTable[i] / 100; - color.b = (int16)color.b * _fadeTable[i] / 100; + color.r = MIN(255, (uint16)color.r * _fadeTable[i] / 100); + color.g = MIN(255, (uint16)color.g * _fadeTable[i] / 100); + color.b = MIN(255, (uint16)color.b * _fadeTable[i] / 100); } } -} + +} // End of namespace Sci |