aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/graphics/palette32.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sci/graphics/palette32.cpp')
-rw-r--r--engines/sci/graphics/palette32.cpp792
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