aboutsummaryrefslogtreecommitdiff
path: root/engines/sci
diff options
context:
space:
mode:
authorFilippos Karapetis2016-01-15 01:33:24 +0200
committerFilippos Karapetis2016-01-15 01:33:24 +0200
commitd44c150d16b2d1d3b1aec4181fe6328db36cd8fd (patch)
treed97024e5ae5e04c17939769b9e7dd6d91682d0b0 /engines/sci
parent6269075ad67de618722e1c9e1e712950a749002b (diff)
parent40f6d74d97618adc80d10244a77c444bf7e613eb (diff)
downloadscummvm-rg350-d44c150d16b2d1d3b1aec4181fe6328db36cd8fd.tar.gz
scummvm-rg350-d44c150d16b2d1d3b1aec4181fe6328db36cd8fd.tar.bz2
scummvm-rg350-d44c150d16b2d1d3b1aec4181fe6328db36cd8fd.zip
Merge pull request #649 from csnover/sci32-kPalette
SCI32: kPalVary, kPalette fade support, kPalette findColor support, improvements to game time management
Diffstat (limited to 'engines/sci')
-rw-r--r--engines/sci/detection.cpp6
-rw-r--r--engines/sci/engine/kernel.h13
-rw-r--r--engines/sci/engine/kernel_tables.h55
-rw-r--r--engines/sci/engine/kgraphics32.cpp91
-rw-r--r--engines/sci/engine/kmisc.cpp3
-rw-r--r--engines/sci/engine/savegame.cpp99
-rw-r--r--engines/sci/engine/savegame.h3
-rw-r--r--engines/sci/graphics/frameout.cpp11
-rw-r--r--engines/sci/graphics/frameout.h3
-rw-r--r--engines/sci/graphics/helpers.h24
-rw-r--r--engines/sci/graphics/palette.cpp6
-rw-r--r--engines/sci/graphics/palette.h12
-rw-r--r--engines/sci/graphics/palette32.cpp592
-rw-r--r--engines/sci/graphics/palette32.h166
-rw-r--r--engines/sci/sci.cpp7
-rw-r--r--engines/sci/sci.h1
16 files changed, 951 insertions, 141 deletions
diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp
index bac9b3467a..4179bae1d9 100644
--- a/engines/sci/detection.cpp
+++ b/engines/sci/detection.cpp
@@ -773,7 +773,11 @@ SaveStateDescriptor SciMetaEngine::querySaveMetaInfos(const char *target, int sl
desc.setSaveTime(hour, minutes);
- desc.setPlayTime(meta.playTime * 1000);
+ if (meta.version >= 34) {
+ desc.setPlayTime(meta.playTime * 1000 / 60);
+ } else {
+ desc.setPlayTime(meta.playTime * 1000);
+ }
delete in;
diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h
index a2563b5a32..f62c43840f 100644
--- a/engines/sci/engine/kernel.h
+++ b/engines/sci/engine/kernel.h
@@ -474,10 +474,19 @@ reg_t kMakeSaveCatName(EngineState *s, int argc, reg_t *argv);
reg_t kMakeSaveFileName(EngineState *s, int argc, reg_t *argv);
reg_t kSetScroll(EngineState *s, int argc, reg_t *argv);
reg_t kPalCycle(EngineState *s, int argc, reg_t *argv);
-reg_t kPalVaryUnknown(EngineState *s, int argc, reg_t *argv);
-reg_t kPalVaryUnknown2(EngineState *s, int argc, reg_t *argv);
+reg_t kPaletteSetFade(EngineState *s, int argc, reg_t *argv);
+reg_t kPalVarySetVary(EngineState *s, int argc, reg_t *argv);
+reg_t kPalVarySetPercent(EngineState *s, int argc, reg_t *argv);
+reg_t kPalVaryGetPercent(EngineState *s, int argc, reg_t *argv);
+reg_t kPalVaryOff(EngineState *s, int argc, reg_t *argv);
+reg_t kPalVaryMergeTarget(EngineState *s, int argc, reg_t *argv);
+reg_t kPalVarySetTime(EngineState *s, int argc, reg_t *argv);
+reg_t kPalVarySetTarget(EngineState *s, int argc, reg_t *argv);
+reg_t kPalVarySetStart(EngineState *s, int argc, reg_t *argv);
+reg_t kPalVaryMergeStart(EngineState *s, int argc, reg_t *argv);
// SCI2.1 Kernel Functions
+reg_t kMorphOn(EngineState *s, int argc, reg_t *argv);
reg_t kText(EngineState *s, int argc, reg_t *argv);
reg_t kSave(EngineState *s, int argc, reg_t *argv);
reg_t kAutoSave(EngineState *s, int argc, reg_t *argv);
diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h
index 3189709109..b1df235cb0 100644
--- a/engines/sci/engine/kernel_tables.h
+++ b/engines/sci/engine/kernel_tables.h
@@ -201,31 +201,42 @@ static const SciKernelMapSubEntry kGraph_subops[] = {
// version, subId, function-mapping, signature, workarounds
static const SciKernelMapSubEntry kPalVary_subops[] = {
- { SIG_SINCE_SCI21, 0, MAP_CALL(PalVaryInit), "ii(i)(i)(i)", NULL },
- { SIG_SCIALL, 0, MAP_CALL(PalVaryInit), "ii(i)(i)", NULL },
- { SIG_SCIALL, 1, MAP_CALL(PalVaryReverse), "(i)(i)(i)", NULL },
- { SIG_SCIALL, 2, MAP_CALL(PalVaryGetCurrentStep), "", NULL },
- { SIG_SCIALL, 3, MAP_CALL(PalVaryDeinit), "", NULL },
- { SIG_SCIALL, 4, MAP_CALL(PalVaryChangeTarget), "i", NULL },
- { SIG_SCIALL, 5, MAP_CALL(PalVaryChangeTicks), "i", NULL },
- { SIG_SCIALL, 6, MAP_CALL(PalVaryPauseResume), "i", NULL },
+ { SIG_SCI16, 0, MAP_CALL(PalVaryInit), "ii(i)(i)", NULL },
+ { SIG_SCI16, 1, MAP_CALL(PalVaryReverse), "(i)(i)(i)", NULL },
+ { SIG_SCI16, 2, MAP_CALL(PalVaryGetCurrentStep), "", NULL },
+ { SIG_SCI16, 3, MAP_CALL(PalVaryDeinit), "", NULL },
+ { SIG_SCI16, 4, MAP_CALL(PalVaryChangeTarget), "i", NULL },
+ { SIG_SCI16, 5, MAP_CALL(PalVaryChangeTicks), "i", NULL },
+ { SIG_SCI16, 6, MAP_CALL(PalVaryPauseResume), "i", NULL },
#ifdef ENABLE_SCI32
- { SIG_SCI32, 8, MAP_CALL(PalVaryUnknown), "i", NULL },
- { SIG_SCI32, 9, MAP_CALL(PalVaryUnknown2), "i", NULL },
+ { SIG_SCI32, 0, MAP_CALL(PalVarySetVary), "i(i)(i)(ii)", NULL },
+ { SIG_SCI32, 1, MAP_CALL(PalVarySetPercent), "(i)(i)", NULL },
+ { SIG_SCI32, 2, MAP_CALL(PalVaryGetPercent), "", NULL },
+ { SIG_SCI32, 3, MAP_CALL(PalVaryOff), "", NULL },
+ { SIG_SCI32, 4, MAP_CALL(PalVaryMergeTarget), "i", NULL },
+ { SIG_SCI32, 5, MAP_CALL(PalVarySetTime), "i", NULL },
+ { SIG_SCI32, 6, MAP_CALL(PalVaryPauseResume), "i", NULL },
+ { SIG_SCI32, 7, MAP_CALL(PalVarySetTarget), "i", NULL },
+ { SIG_SCI32, 8, MAP_CALL(PalVarySetStart), "i", NULL },
+ { SIG_SCI32, 9, MAP_CALL(PalVaryMergeStart), "i", NULL },
#endif
SCI_SUBOPENTRY_TERMINATOR
};
// version, subId, function-mapping, signature, workarounds
static const SciKernelMapSubEntry kPalette_subops[] = {
- { SIG_SCIALL, 1, MAP_CALL(PaletteSetFromResource), "i(i)", NULL },
- { SIG_SCIALL, 2, MAP_CALL(PaletteSetFlag), "iii", NULL },
- { SIG_SCIALL, 3, MAP_CALL(PaletteUnsetFlag), "iii", kPaletteUnsetFlag_workarounds },
- { SIG_SCIALL, 4, MAP_CALL(PaletteSetIntensity), "iii(i)", NULL },
- { SIG_SCIALL, 5, MAP_CALL(PaletteFindColor), "iii", NULL },
- { SIG_SCIALL, 6, MAP_CALL(PaletteAnimate), "i*", NULL },
- { SIG_SCIALL, 7, MAP_CALL(PaletteSave), "", NULL },
- { SIG_SCIALL, 8, MAP_CALL(PaletteRestore), "[r0]", NULL },
+ { SIG_SCIALL, 1, MAP_CALL(PaletteSetFromResource), "i(i)", NULL },
+ { SIG_SCI16, 2, MAP_CALL(PaletteSetFlag), "iii", NULL },
+ { SIG_SCI16, 3, MAP_CALL(PaletteUnsetFlag), "iii", kPaletteUnsetFlag_workarounds },
+#ifdef ENABLE_SCI32
+ { SIG_SCI32, 2, MAP_CALL(PaletteSetFade), "iii", NULL },
+ { SIG_SCI32, 3, MAP_CALL(PaletteFindColor), "iii", NULL },
+#endif
+ { SIG_SCI16, 4, MAP_CALL(PaletteSetIntensity), "iii(i)", NULL },
+ { SIG_SCI16, 5, MAP_CALL(PaletteFindColor), "iii", NULL },
+ { SIG_SCI16, 6, MAP_CALL(PaletteAnimate), "i*", NULL },
+ { SIG_SCI16, 7, MAP_CALL(PaletteSave), "", NULL },
+ { SIG_SCI16, 8, MAP_CALL(PaletteRestore), "[r0]", NULL },
SCI_SUBOPENTRY_TERMINATOR
};
@@ -695,15 +706,15 @@ static SciKernelMapEntry s_kernelMap[] = {
// MovePlaneItems - used by SQ6 to scroll through the inventory via the up/down buttons
// SetPalStyleRange - 2 integer parameters, start and end. All styles from start-end
// (inclusive) are set to 0
- { MAP_CALL(SetPalStyleRange), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(SetPalStyleRange), SIG_EVERYWHERE, "ii", NULL, NULL },
- // MorphOn - used by SQ6, script 900, the datacorder reprogramming puzzle (from room 270)
+ { MAP_CALL(MorphOn), SIG_EVERYWHERE, "", NULL, NULL },
// SCI3 Kernel Functions
- { MAP_CALL(PlayDuck), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(PlayDuck), SIG_EVERYWHERE, "(.*)", NULL, NULL },
#endif
- { NULL, NULL, SIG_EVERYWHERE, NULL, NULL, NULL }
+ { NULL, NULL, SIG_EVERYWHERE, NULL, NULL, NULL }
};
/** Default kernel name table. */
diff --git a/engines/sci/engine/kgraphics32.cpp b/engines/sci/engine/kgraphics32.cpp
index a765fe842e..e87849201e 100644
--- a/engines/sci/engine/kgraphics32.cpp
+++ b/engines/sci/engine/kgraphics32.cpp
@@ -728,20 +728,87 @@ reg_t kSetScroll(EngineState *s, int argc, reg_t *argv) {
return kStub(s, argc, argv);
}
-reg_t kPalVaryUnknown(EngineState *s, int argc, reg_t *argv) {
- // TODO: Unknown (seems to be SCI32 exclusive)
- return kStub(s, argc, argv);
+// Used by SQ6, script 900, the datacorder reprogramming puzzle (from room 270)
+reg_t kMorphOn(EngineState *s, int argc, reg_t *argv) {
+ // TODO: g_sci->_gfxManager->palMorphIsOn = true
+ // This function sets the palMorphIsOn flag which causes kFrameOut to use
+ // an alternative FrameOut function (GraphicsMgr::PalMorphFrameOut instead
+ // of GraphicsMgr::FrameOut). At the end of the frame, kFrameOut sets the
+ // palMorphIsOn flag back to false.
+ kStub(s, argc, argv);
+ return NULL_REG;
}
-reg_t kPalVaryUnknown2(EngineState *s, int argc, reg_t *argv) {
- // TODO: Unknown (seems to be SCI32 exclusive)
- // It seems to be related to the day/night palette effects in QFG4, and
- // accepts a palette resource ID. It is triggered right when the night
- // effect is initially applied (when exiting the caves).
- // In QFG4, there are two scene palettes: 790 for night, and 791 for day.
- // Initially, the game starts at night time, but this is called with the
- // ID of the day time palette (i.e. 791).
- return kStub(s, argc, argv);
+reg_t kPaletteSetFade(EngineState *s, int argc, reg_t *argv) {
+ uint16 fromColor = argv[0].toUint16();
+ uint16 toColor = argv[1].toUint16();
+ uint16 percent = argv[2].toUint16();
+ g_sci->_gfxPalette32->setFade(percent, fromColor, toColor);
+ return NULL_REG;
+}
+
+reg_t kPalVarySetVary(EngineState *s, int argc, reg_t *argv) {
+ GuiResourceId paletteId = argv[0].toUint16();
+ int time = argc > 1 ? argv[1].toSint16() * 60 : 0;
+ int16 percent = argc > 2 ? argv[2].toSint16() : 100;
+ int16 fromColor;
+ int16 toColor;
+
+ if (argc > 4) {
+ fromColor = argv[3].toSint16();
+ toColor = argv[4].toSint16();
+ } else {
+ fromColor = toColor = -1;
+ }
+
+ g_sci->_gfxPalette32->kernelPalVarySet(paletteId, percent, time, fromColor, toColor);
+ return NULL_REG;
+}
+
+reg_t kPalVarySetPercent(EngineState *s, int argc, reg_t *argv) {
+ int time = argc > 1 ? argv[1].toSint16() * 60 : 0;
+ int16 percent = argc > 2 ? argv[2].toSint16() : 0;
+ g_sci->_gfxPalette32->setVaryPercent(percent, time, -1, -1);
+ return NULL_REG;
+}
+
+reg_t kPalVaryGetPercent(EngineState *s, int argc, reg_t *argv) {
+ return make_reg(0, g_sci->_gfxPalette32->getVaryPercent());
+}
+
+reg_t kPalVaryOff(EngineState *s, int argc, reg_t *argv) {
+ g_sci->_gfxPalette32->varyOff();
+ return NULL_REG;
+}
+
+reg_t kPalVaryMergeTarget(EngineState *s, int argc, reg_t *argv) {
+ GuiResourceId paletteId = argv[1].toUint16();
+ g_sci->_gfxPalette32->kernelPalVaryMergeTarget(paletteId);
+ return make_reg(0, g_sci->_gfxPalette32->getVaryPercent());
+}
+
+reg_t kPalVarySetTime(EngineState *s, int argc, reg_t *argv) {
+ int time = argv[1].toSint16() * 60;
+ g_sci->_gfxPalette32->setVaryTime(time);
+ return NULL_REG;
+}
+
+reg_t kPalVarySetTarget(EngineState *s, int argc, reg_t *argv) {
+ GuiResourceId paletteId = argv[1].toUint16();
+ g_sci->_gfxPalette32->kernelPalVarySetTarget(paletteId);
+ return make_reg(0, g_sci->_gfxPalette32->getVaryPercent());
+}
+
+reg_t kPalVarySetStart(EngineState *s, int argc, reg_t *argv) {
+ GuiResourceId paletteId = argv[1].toUint16();
+ g_sci->_gfxPalette32->kernelPalVarySetStart(paletteId);
+ return make_reg(0, g_sci->_gfxPalette32->getVaryPercent());
+}
+
+reg_t kPalVaryMergeStart(EngineState *s, int argc, reg_t *argv) {
+ GuiResourceId paletteId = argv[1].toUint16();
+ g_sci->_gfxPalette32->kernelPalVaryMergeStart(paletteId);
+ return make_reg(0, g_sci->_gfxPalette32->getVaryPercent());
}
enum {
diff --git a/engines/sci/engine/kmisc.cpp b/engines/sci/engine/kmisc.cpp
index 1d9dae69b7..084315e011 100644
--- a/engines/sci/engine/kmisc.cpp
+++ b/engines/sci/engine/kmisc.cpp
@@ -218,7 +218,6 @@ enum {
reg_t kGetTime(EngineState *s, int argc, reg_t *argv) {
TimeDate loc_time;
- uint32 elapsedTime = g_engine->getTotalPlayTime();
int retval = 0; // Avoid spurious warning
g_system->getTimeAndDate(loc_time);
@@ -232,7 +231,7 @@ reg_t kGetTime(EngineState *s, int argc, reg_t *argv) {
switch (mode) {
case KGETTIME_TICKS :
- retval = elapsedTime * 60 / 1000;
+ retval = g_sci->getTickCount();
debugC(kDebugLevelTime, "GetTime(elapsed) returns %d", retval);
break;
case KGETTIME_TIME_12HOUR :
diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp
index 09ccff39ac..481ae75907 100644
--- a/engines/sci/engine/savegame.cpp
+++ b/engines/sci/engine/savegame.cpp
@@ -47,6 +47,7 @@
#include "sci/sound/music.h"
#ifdef ENABLE_SCI32
+#include "sci/graphics/palette32.h"
#include "sci/graphics/frameout.h"
#endif
@@ -272,7 +273,11 @@ static void sync_SavegameMetadata(Common::Serializer &s, SavegameMetadata &obj)
if (s.getVersion() >= 26)
s.syncAsUint32LE(obj.playTime);
} else {
- obj.playTime = g_engine->getTotalPlayTime() / 1000;
+ if (s.getVersion() >= 34) {
+ obj.playTime = g_sci->getTickCount();
+ } else {
+ obj.playTime = g_engine->getTotalPlayTime() / 1000;
+ }
s.syncAsUint32LE(obj.playTime);
}
}
@@ -304,6 +309,7 @@ void EngineState::saveLoadWithSerializer(Common::Serializer &s) {
_segMan->saveLoadWithSerializer(s);
g_sci->_soundCmd->syncPlayList(s);
+ // NOTE: This will be GfxPalette32 for SCI32 engine games
g_sci->_gfxPalette16->saveLoadWithSerializer(s);
}
@@ -721,6 +727,91 @@ void GfxPalette::saveLoadWithSerializer(Common::Serializer &s) {
}
}
+#ifdef ENABLE_SCI32
+void saveLoadPalette32(Common::Serializer &s, Palette *const palette) {
+ s.syncAsUint32LE(palette->timestamp);
+ for (int i = 0; i < ARRAYSIZE(palette->colors); ++i) {
+ s.syncAsByte(palette->colors[i].used);
+ s.syncAsByte(palette->colors[i].r);
+ s.syncAsByte(palette->colors[i].g);
+ s.syncAsByte(palette->colors[i].b);
+ }
+}
+
+void saveLoadOptionalPalette32(Common::Serializer &s, Palette **const palette) {
+ bool hasPalette;
+ if (s.isSaving()) {
+ hasPalette = (*palette != nullptr);
+ }
+ s.syncAsByte(hasPalette);
+ if (hasPalette) {
+ if (s.isLoading()) {
+ *palette = new Palette;
+ }
+ saveLoadPalette32(s, *palette);
+ }
+}
+
+void GfxPalette32::saveLoadWithSerializer(Common::Serializer &s) {
+ if (s.getVersion() < 34) {
+ return;
+ }
+
+ if (s.isLoading()) {
+ ++_version;
+ }
+
+ s.syncAsSint16LE(_varyDirection);
+ s.syncAsSint16LE(_varyPercent);
+ s.syncAsSint16LE(_varyTargetPercent);
+ s.syncAsSint16LE(_varyFromColor);
+ s.syncAsSint16LE(_varyToColor);
+ s.syncAsUint16LE(_varyNumTimesPaused);
+ s.syncAsByte(_versionUpdated);
+ s.syncAsSint32LE(_varyTime);
+ s.syncAsUint32LE(_varyLastTick);
+
+ for (int i = 0; i < ARRAYSIZE(_fadeTable); ++i) {
+ s.syncAsByte(_fadeTable[i]);
+ }
+ for (int i = 0; i < ARRAYSIZE(_cycleMap); ++i) {
+ s.syncAsByte(_cycleMap[i]);
+ }
+
+ saveLoadOptionalPalette32(s, &_varyTargetPalette);
+ saveLoadOptionalPalette32(s, &_varyStartPalette);
+ // NOTE: _sourcePalette and _nextPalette are not saved
+ // by SCI engine
+
+ for (int i = 0; i < ARRAYSIZE(_cyclers); ++i) {
+ PalCycler *cycler;
+
+ bool hasCycler;
+ if (s.isSaving()) {
+ cycler = _cyclers[i];
+ hasCycler = (cycler != nullptr);
+ }
+ s.syncAsByte(hasCycler);
+
+ if (hasCycler) {
+ if (s.isLoading()) {
+ _cyclers[i] = cycler = new PalCycler;
+ }
+
+ s.syncAsByte(cycler->fromColor);
+ s.syncAsUint16LE(cycler->numColorsToCycle);
+ s.syncAsByte(cycler->currentCycle);
+ s.syncAsByte(cycler->direction);
+ s.syncAsUint32LE(cycler->lastUpdateTick);
+ s.syncAsSint16LE(cycler->delay);
+ s.syncAsUint16LE(cycler->numTimesPaused);
+ }
+ }
+
+ // TODO: _clutTable
+}
+#endif
+
void GfxPorts::saveLoadWithSerializer(Common::Serializer &s) {
// reset() is called directly way earlier in gamestate_restore()
if (s.getVersion() >= 27) {
@@ -944,7 +1035,11 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
// Time state:
s->lastWaitTime = g_system->getMillis();
s->_screenUpdateTime = g_system->getMillis();
- g_engine->setTotalPlayTime(meta.playTime * 1000);
+ if (meta.version >= 34) {
+ g_sci->setTickCount(meta.playTime);
+ } else {
+ g_engine->setTotalPlayTime(meta.playTime * 1000);
+ }
if (g_sci->_gfxPorts)
g_sci->_gfxPorts->saveLoadWithSerializer(ser);
diff --git a/engines/sci/engine/savegame.h b/engines/sci/engine/savegame.h
index 5512b90fa8..bb555434c9 100644
--- a/engines/sci/engine/savegame.h
+++ b/engines/sci/engine/savegame.h
@@ -37,6 +37,7 @@ struct EngineState;
*
* Version - new/changed feature
* =============================
+ * 34 - SCI32 palettes, and store play time in ticks
* 33 - new overridePriority flag in MusicEntry
* 32 - new playBed flag in MusicEntry
* 31 - priority for sound effects/music is now a signed int16, instead of a byte
@@ -58,7 +59,7 @@ struct EngineState;
*/
enum {
- CURRENT_SAVEGAME_VERSION = 33,
+ CURRENT_SAVEGAME_VERSION = 34,
MINIMUM_SAVEGAME_VERSION = 14
};
diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp
index 9512ec2ed1..90bd798a1b 100644
--- a/engines/sci/graphics/frameout.cpp
+++ b/engines/sci/graphics/frameout.cpp
@@ -748,12 +748,11 @@ void GfxFrameout::kernelFrameout() {
return;
}
- _palette->palVaryUpdate();
- _palette->applyCycles();
- _palette->applyFade();
- // TODO: This should probably not require screen pic invalidation
- _screen->_picNotValid = 1;
- _palette->setOnScreen();
+ _palette->updateForFrame();
+
+ // TODO: Tons of drawing stuff should be here, see commented out implementation above
+
+ _palette->updateHardware();
for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); it++) {
reg_t planeObject = it->object;
diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h
index a422572ef4..0017fb19e8 100644
--- a/engines/sci/graphics/frameout.h
+++ b/engines/sci/graphics/frameout.h
@@ -110,7 +110,8 @@ class GfxPalette;
class GfxScreen;
/**
- * Frameout class, kFrameout and relevant functions for SCI32 games
+ * Frameout class, kFrameout and relevant functions for SCI32 games.
+ * Roughly equivalent to GraphicsMgr in the actual SCI engine.
*/
class GfxFrameout {
public:
diff --git a/engines/sci/graphics/helpers.h b/engines/sci/graphics/helpers.h
index 4889f12bd2..e5b9f2aaed 100644
--- a/engines/sci/graphics/helpers.h
+++ b/engines/sci/graphics/helpers.h
@@ -121,6 +121,15 @@ struct Window : public Port, public Common::Serializable {
struct Color {
byte used;
byte r, g, b;
+
+#ifdef ENABLE_SCI32
+ bool operator==(const Color &other) const {
+ return used == other.used && r == other.r && g == other.g && b == other.b;
+ }
+ inline bool operator!=(const Color &other) const {
+ return !(*this == other);
+ }
+#endif
};
struct Palette {
@@ -128,6 +137,21 @@ struct Palette {
uint32 timestamp;
Color colors[256];
byte intensity[256];
+
+#ifdef ENABLE_SCI32
+ bool operator==(const Palette &other) const {
+ for (int i = 0; i < ARRAYSIZE(colors); ++i) {
+ if (colors[i] != other.colors[i]) {
+ return false;
+ }
+ }
+
+ return false;
+ }
+ inline bool operator!=(const Palette &other) const {
+ return !(*this == other);
+ }
+#endif
};
struct PalSchedule {
diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp
index 3742a8d2d8..106924ecd3 100644
--- a/engines/sci/graphics/palette.cpp
+++ b/engines/sci/graphics/palette.cpp
@@ -136,7 +136,7 @@ void GfxPalette::setDefault() {
#define SCI_PAL_FORMAT_CONSTANT 1
#define SCI_PAL_FORMAT_VARIABLE 0
-void GfxPalette::createFromData(byte *data, int bytesLeft, Palette *paletteOut) {
+void GfxPalette::createFromData(byte *data, int bytesLeft, Palette *paletteOut) const {
int palFormat = 0;
int palOffset = 0;
int palColorStart = 0;
@@ -650,7 +650,7 @@ bool GfxPalette::kernelAnimate(byte fromColor, byte toColor, int speed) {
Color col;
//byte colorNr;
int16 colorCount;
- uint32 now = g_system->getMillis() * 60 / 1000;
+ uint32 now = g_sci->getTickCount();
// search for sheduled animations with the same 'from' value
// schedule animation...
@@ -811,8 +811,6 @@ bool GfxPalette::palVaryLoadTargetPalette(GuiResourceId resourceId) {
return false;
}
-// TODO: This code should not set up an independent timer in SCI32. SCI32 engine palette varies happen in
-// Palette::UpdateForFrame which is called by GraphicsMgr::FrameOut.
void GfxPalette::palVaryInstallTimer() {
// Remove any possible leftover palVary timer callbacks.
// This happens for example in QFG1VGA, when sleeping at Erana's place
diff --git a/engines/sci/graphics/palette.h b/engines/sci/graphics/palette.h
index 82fd20a432..61a8d36cb9 100644
--- a/engines/sci/graphics/palette.h
+++ b/engines/sci/graphics/palette.h
@@ -53,11 +53,11 @@ public:
bool isUsing16bitColorMatch();
void setDefault();
- void createFromData(byte *data, int bytesLeft, Palette *paletteOut);
+ void createFromData(byte *data, int bytesLeft, Palette *paletteOut) const;
bool setAmiga();
void modifyAmigaPalette(byte *data);
void setEGA();
- void set(Palette *sciPal, bool force, bool forceRealMerge = false);
+ virtual void set(Palette *sciPal, bool force, bool forceRealMerge = false);
bool insert(Palette *newPalette, Palette *destPalette);
bool merge(Palette *pFrom, bool force, bool forceRealMerge);
uint16 matchColor(byte r, byte g, byte b);
@@ -78,11 +78,11 @@ public:
void drewPicture(GuiResourceId pictureId);
- bool kernelSetFromResource(GuiResourceId resourceId, bool force);
+ virtual bool kernelSetFromResource(GuiResourceId resourceId, bool force);
void kernelSetFlag(uint16 fromColor, uint16 toColor, uint16 flag);
void kernelUnsetFlag(uint16 fromColor, uint16 toColor, uint16 flag);
void kernelSetIntensity(uint16 fromColor, uint16 toColor, uint16 intensity, bool setPalette);
- int16 kernelFindColor(uint16 r, uint16 g, uint16 b);
+ virtual int16 kernelFindColor(uint16 r, uint16 g, uint16 b);
bool kernelAnimate(byte fromColor, byte toColor, int speed);
void kernelAnimateSet();
reg_t kernelSave();
@@ -96,7 +96,7 @@ public:
int16 kernelPalVaryGetCurrentStep();
int16 kernelPalVaryChangeTarget(GuiResourceId resourceId);
void kernelPalVaryChangeTicks(uint16 ticks);
- void kernelPalVaryPause(bool pause);
+ virtual void kernelPalVaryPause(bool pause);
void kernelPalVaryDeinit();
void palVaryUpdate();
void palVaryPrepareForTransition();
@@ -110,7 +110,7 @@ public:
byte findMacIconBarColor(byte r, byte g, byte b);
bool colorIsFromMacClut(byte index);
-private:
+protected:
void palVaryInit();
void palVaryInstallTimer();
void palVaryRemoveTimer();
diff --git a/engines/sci/graphics/palette32.cpp b/engines/sci/graphics/palette32.cpp
index 95c4f99974..8ce8b85678 100644
--- a/engines/sci/graphics/palette32.cpp
+++ b/engines/sci/graphics/palette32.cpp
@@ -21,20 +21,235 @@
*/
#include "common/file.h"
+#include "common/system.h"
+#include "graphics/palette.h"
#include "sci/sci.h"
+#include "sci/event.h"
+#include "sci/resource.h"
#include "sci/graphics/palette32.h"
+#include "sci/graphics/screen.h"
namespace Sci {
GfxPalette32::GfxPalette32(ResourceManager *resMan, GfxScreen *screen)
- : GfxPalette(resMan, screen), _clutTable(0), _cyclers(), _cycleMap() {}
+ : GfxPalette(resMan, screen),
+ _clutTable(nullptr),
+ // Palette cycling
+ _cyclers(), _cycleMap(),
+ // Palette varying
+ _sourcePalette(_sysPalette), _nextPalette(_sysPalette),
+ _varyTime(0), _varyDirection(0), _varyTargetPercent(0),
+ _varyTargetPalette(nullptr), _varyStartPalette(nullptr),
+ _varyFromColor(0), _varyToColor(255), _varyNumTimesPaused(0),
+ _varyPercent(_varyTargetPercent), _varyLastTick(0),
+ // Palette versioning
+ _version(1), _versionUpdated(false) {
+ 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
+ }
GfxPalette32::~GfxPalette32() {
unloadClut();
+ 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];
+ }
+ }
+}
+
+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;
+}
+
+override 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;
+
+ if (createPaletteFromResourceInternal(resourceId, &palette)) {
+ submit(palette);
+ return true;
+ }
+
+ return false;
+}
+
+// In SCI32 engine this method is SOLPalette::Match(Rgb24 *)
+// and is called as PaletteMgr.Current().Match(color)
+override 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 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;
+ 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;
+ }
+
+ 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`.
+override 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;
+
+ // SQ6 DOS really does check only the first 236 entries
+ for (int i = 0, channelDifference; i < 236; ++i) {
+ if (matchTable[i] == 0) {
+ continue;
+ }
+
+ 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;
+ }
+
+ // NOTE: This value is only valid if the last index to
+ // perform a difference calculation was the best index
+ lastCalculatedDifference = difference;
+ return bestIndex;
+}
+
+void GfxPalette32::updateForFrame() {
+ applyAll();
+ _versionUpdated = false;
+ // TODO: Implement remapping
+ // g_sci->_gfxFrameout->remapAllTables(_nextPalette != _sysPalette);
+}
+
+void GfxPalette32::updateHardware() {
+ if (_sysPalette == _nextPalette) {
+ // TODO: This condition has never been encountered yet
+ debug("Skipping hardware update because palettes are identical");
+ return;
+ }
+
+ byte bpal[3 * 256];
+
+ for (int i = 0; i < ARRAYSIZE(_sysPalette.colors); ++i) {
+ _sysPalette.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;
+ }
+ }
+
+ g_system->getPaletteManager()->setPalette(bpal, 0, 256);
+ g_sci->getEventManager()->updateScreen();
+}
+
+void GfxPalette32::applyAll() {
+ applyVary();
+ applyCycles();
+ applyFade();
+}
+
+//
+// Clut
+//
+
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.
@@ -82,9 +297,255 @@ byte GfxPalette32::matchClutColor(uint16 color) {
void GfxPalette32::unloadClut() {
// This will only unload the actual table, but not reset any palette
delete[] _clutTable;
- _clutTable = 0;
+ _clutTable = nullptr;
+}
+
+//
+// Palette vary
+//
+
+inline bool GfxPalette32::createPaletteFromResourceInternal(const GuiResourceId paletteId, Palette *const out) const {
+ Resource *palResource = _resMan->findResource(ResourceId(kResourceTypePalette, paletteId), false);
+
+ if (!palResource) {
+ return false;
+ }
+
+ createFromData(palResource->data, palResource->size, out);
+ return true;
+}
+
+inline Palette GfxPalette32::getPaletteFromResourceInternal(const GuiResourceId paletteId) const {
+ Palette palette;
+ if (!createPaletteFromResourceInternal(paletteId, &palette)) {
+ error("Could not load vary target %d", paletteId);
+ }
+ return palette;
+}
+
+inline void GfxPalette32::setVaryTimeInternal(const int16 percent, const int time) {
+ _varyLastTick = g_sci->getTickCount();
+ if (!time || _varyPercent == percent) {
+ _varyDirection = 0;
+ _varyTargetPercent = _varyPercent = percent;
+ } else {
+ _varyTime = time / (percent - _varyPercent);
+ _varyTargetPercent = percent;
+
+ if (_varyTime > 0) {
+ _varyDirection = 1;
+ } else if (_varyTime < 0) {
+ _varyDirection = -1;
+ _varyTime = -_varyTime;
+ } else {
+ _varyDirection = 0;
+ _varyTargetPercent = _varyPercent = percent;
+ }
+ }
+}
+
+// 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);
+}
+
+override 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;
+ _varyFromColor = 0;
+ _varyToColor = 255;
+ _varyDirection = 0;
+
+ if (_varyTargetPalette != nullptr) {
+ delete _varyTargetPalette;
+ _varyTargetPalette = nullptr;
+ }
+
+ if (_varyStartPalette != nullptr) {
+ delete _varyStartPalette;
+ _varyStartPalette = nullptr;
+ }
+}
+
+void GfxPalette32::mergeTarget(const Palette *const palette) {
+ if (_varyTargetPalette != nullptr) {
+ mergePaletteInternal(_varyTargetPalette, palette);
+ } else {
+ _varyTargetPalette = new Palette(*palette);
+ }
+}
+
+void GfxPalette32::varyPause() {
+ _varyDirection = 0;
+ ++_varyNumTimesPaused;
+}
+
+void GfxPalette32::varyOn() {
+ if (_varyNumTimesPaused > 0) {
+ --_varyNumTimesPaused;
+ }
+
+ if (_varyTargetPalette != nullptr && _varyNumTimesPaused == 0 && _varyPercent != _varyTargetPercent) {
+ if (_varyTime == 0) {
+ _varyPercent = _varyTargetPercent;
+ } else if (_varyTargetPercent < _varyPercent) {
+ _varyDirection = -1;
+ } else {
+ _varyDirection = 1;
+ }
+ }
+}
+
+void GfxPalette32::setVaryTime(const int time) {
+ if (_varyTargetPalette == nullptr) {
+ return;
+ }
+
+ setVaryTimeInternal(_varyTargetPercent, time);
+}
+
+void GfxPalette32::setTarget(const Palette *const palette) {
+ if (_varyTargetPalette != nullptr) {
+ delete _varyTargetPalette;
+ }
+
+ _varyTargetPalette = new Palette(*palette);
+}
+
+void GfxPalette32::setStart(const Palette *const palette) {
+ if (_varyStartPalette != nullptr) {
+ delete _varyStartPalette;
+ }
+
+ _varyStartPalette = new Palette(*palette);
+}
+
+void GfxPalette32::mergeStart(const Palette *const palette) {
+ if (_varyStartPalette != nullptr) {
+ mergePaletteInternal(_varyStartPalette, palette);
+ } else {
+ _varyStartPalette = new Palette(*palette);
+ }
+}
+
+void GfxPalette32::applyVary() {
+ while (g_sci->getTickCount() - _varyLastTick > _varyTime && _varyDirection != 0) {
+ _varyLastTick += _varyTime;
+
+ if (_varyPercent == _varyTargetPercent) {
+ _varyDirection = 0;
+ }
+
+ _varyPercent += _varyDirection;
+ }
+
+ if (_varyPercent == 0 || _varyTargetPalette == nullptr) {
+ for (int i = 0, len = ARRAYSIZE(_nextPalette.colors); i < len; ++i) {
+ if (_varyStartPalette != nullptr && i >= _varyFromColor && i <= _varyToColor) {
+ _nextPalette.colors[i] = _varyStartPalette->colors[i];
+ } else {
+ _nextPalette.colors[i] = _sourcePalette.colors[i];
+ }
+ }
+ } else {
+ for (int i = 0, len = ARRAYSIZE(_nextPalette.colors); i < len; ++i) {
+ if (i >= _varyFromColor && i <= _varyToColor) {
+ Color targetColor = _varyTargetPalette->colors[i];
+ Color sourceColor;
+
+ if (_varyStartPalette != nullptr) {
+ sourceColor = _varyStartPalette->colors[i];
+ } else {
+ sourceColor = _sourcePalette.colors[i];
+ }
+
+ Color computedColor;
+
+ int color;
+ color = targetColor.r - sourceColor.r;
+ computedColor.r = ((color * _varyPercent) / 100) + sourceColor.r;
+ color = targetColor.g - sourceColor.g;
+ computedColor.g = ((color * _varyPercent) / 100) + sourceColor.g;
+ color = targetColor.b - sourceColor.b;
+ computedColor.b = ((color * _varyPercent) / 100) + sourceColor.b;
+ computedColor.used = sourceColor.used;
+
+ _nextPalette.colors[i] = computedColor;
+ }
+ else {
+ _nextPalette.colors[i] = _sourcePalette.colors[i];
+ }
+ }
+ }
}
+//
+// Palette cycling
+//
+
inline void GfxPalette32::clearCycleMap(const uint16 fromColor, const uint16 numColorsToClear) {
bool *mapEntry = _cycleMap + fromColor;
const bool *lastEntry = _cycleMap + numColorsToClear;
@@ -130,47 +591,7 @@ inline void doCycleInternal(PalCycler *cycler, const int16 speed) {
cycler->currentCycle = (uint8) (currentCycle % numColorsToCycle);
}
-void GfxPalette32::applyAllCycles() {
- Color paletteCopy[256];
- memcpy(paletteCopy, _sysPalette.colors, sizeof(Color) * 256);
-
- for (int cyclerIndex = 0, numCyclers = ARRAYSIZE(_cyclers); cyclerIndex < numCyclers; ++cyclerIndex) {
- PalCycler *cycler = _cyclers[cyclerIndex];
- 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
- for (int j = 0; j < cycler->numColorsToCycle; j++) {
- _sysPalette.colors[cycler->fromColor + j] = paletteCopy[cycler->fromColor + (cycler->currentCycle + j) % cycler->numColorsToCycle];
- }
- }
- }
-}
-
-void GfxPalette32::applyCycles() {
- Color paletteCopy[256];
- memcpy(paletteCopy, _sysPalette.colors, sizeof(Color) * 256);
-
- for (int i = 0, len = ARRAYSIZE(_cyclers); i < len; ++i) {
- PalCycler *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);
- cycler->lastUpdateTick += cycler->delay;
- }
- }
-
- for (int j = 0; j < cycler->numColorsToCycle; j++) {
- _sysPalette.colors[cycler->fromColor + j] = paletteCopy[cycler->fromColor + (cycler->currentCycle + j) % cycler->numColorsToCycle];
- }
- }
-}
-
-int16 GfxPalette32::setCycle(const uint16 fromColor, const uint16 toColor, const int16 direction, const int16 delay) {
+void GfxPalette32::setCycle(const uint8 fromColor, const uint8 toColor, const int16 direction, const int16 delay) {
assert(fromColor < toColor);
int cyclerIndex;
@@ -179,7 +600,6 @@ int16 GfxPalette32::setCycle(const uint16 fromColor, const uint16 toColor, const
PalCycler *cycler = getCycler(fromColor);
if (cycler != nullptr) {
- //debug("Resetting existing cycler");
clearCycleMap(fromColor, cycler->numColorsToCycle);
} else {
for (cyclerIndex = 0; cyclerIndex < numCyclers; ++cyclerIndex) {
@@ -223,13 +643,9 @@ int16 GfxPalette32::setCycle(const uint16 fromColor, const uint16 toColor, const
cycler->numTimesPaused = 0;
setCycleMap(fromColor, numColorsToCycle);
-
- // TODO: Validate that this is the correct return value according
- // to disassembly
- return 0;
}
-void GfxPalette32::doCycle(const uint16 fromColor, const int16 speed) {
+void GfxPalette32::doCycle(const uint8 fromColor, const int16 speed) {
PalCycler *cycler = getCycler(fromColor);
if (cycler != nullptr) {
cycler->lastUpdateTick = g_sci->getTickCount();
@@ -237,14 +653,14 @@ void GfxPalette32::doCycle(const uint16 fromColor, const int16 speed) {
}
}
-void GfxPalette32::cycleOn(const uint16 fromColor) {
+void GfxPalette32::cycleOn(const uint8 fromColor) {
PalCycler *cycler = getCycler(fromColor);
if (cycler != nullptr && cycler->numTimesPaused > 0) {
--cycler->numTimesPaused;
}
}
-void GfxPalette32::cyclePause(const uint16 fromColor) {
+void GfxPalette32::cyclePause(const uint8 fromColor) {
PalCycler *cycler = getCycler(fromColor);
if (cycler != nullptr) {
++cycler->numTimesPaused;
@@ -289,7 +705,7 @@ void GfxPalette32::cycleAllPause() {
}
}
-void GfxPalette32::cycleOff(const uint16 fromColor) {
+void GfxPalette32::cycleOff(const uint8 fromColor) {
for (int i = 0, len = ARRAYSIZE(_cyclers); i < len; ++i) {
PalCycler *cycler = _cyclers[i];
if (cycler != nullptr && cycler->fromColor == fromColor) {
@@ -312,27 +728,75 @@ void GfxPalette32::cycleAllOff() {
}
}
-void GfxPalette32::applyFade() {
- for (int i = 0; i < 256; ++i) {
- if (_fadeTable[i] == 100)
+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];
+ 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
+ for (int j = 0; j < cycler->numColorsToCycle; j++) {
+ _nextPalette.colors[cycler->fromColor + j] = paletteCopy[cycler->fromColor + (cycler->currentCycle + j) % cycler->numColorsToCycle];
+ }
+ }
+ }
+}
+
+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];
+ if (cycler == nullptr) {
continue;
+ }
+
+ if (cycler->delay != 0 && cycler->numTimesPaused == 0) {
+ while ((cycler->delay + cycler->lastUpdateTick) < g_sci->getTickCount()) {
+ doCycleInternal(cycler, 1);
+ cycler->lastUpdateTick += cycler->delay;
+ }
+ }
- // TODO: Create and update a _nextPalette, not a single _sysPalette, to
- // conform to the way the actual SCI32 engine works (writes to a
- // next-frame-palette and then copies to the current palette on frameout)
- _sysPalette.colors[i].r = (_sysPalette.colors[i].r * _fadeTable[i]) / 100;
- _sysPalette.colors[i].g = (_sysPalette.colors[i].g * _fadeTable[i]) / 100;
- _sysPalette.colors[i].b = (_sysPalette.colors[i].b * _fadeTable[i]) / 100;
+ for (int j = 0; j < cycler->numColorsToCycle; j++) {
+ _nextPalette.colors[cycler->fromColor + j] = paletteCopy[cycler->fromColor + (cycler->currentCycle + j) % cycler->numColorsToCycle];
+ }
}
}
-void GfxPalette32::setFade(uint8 percent, uint16 fromColor, uint16 toColor) {
- for (int i = fromColor; i <= toColor; i++)
+//
+// Palette fading
+//
+
+void GfxPalette32::setFade(uint8 percent, uint8 fromColor, uint16 numColorsToFade) {
+ if (fromColor > numColorsToFade) {
+ return;
+ }
+
+ assert(numColorsToFade <= ARRAYSIZE(_fadeTable));
+
+ for (int i = fromColor; i < numColorsToFade; i++)
_fadeTable[i] = percent;
}
void GfxPalette32::fadeOff() {
- setFade(100, 0, 255);
+ setFade(100, 0, 256);
}
+void GfxPalette32::applyFade() {
+ for (int i = 0; i < ARRAYSIZE(_fadeTable); ++i) {
+ 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;
+ }
+}
}
diff --git a/engines/sci/graphics/palette32.h b/engines/sci/graphics/palette32.h
index 3432e4bfd2..2e3f6274fa 100644
--- a/engines/sci/graphics/palette32.h
+++ b/engines/sci/graphics/palette32.h
@@ -77,12 +77,42 @@ namespace Sci {
GfxPalette32(ResourceManager *resMan, GfxScreen *screen);
~GfxPalette32();
- private:
- // SCI2 (SQ6) defines 10 cyclers
+ protected:
+ /**
+ * The palette revision version. Increments once per game
+ * loop that changes the source palette. TODO: Possibly
+ * other areas also change _version, double-check once it
+ * is all implemented.
+ */
+ uint32 _version;
+
+ /**
+ * Whether or not the palette manager version was updated
+ * during this loop.
+ */
+ bool _versionUpdated;
+
+ /**
+ * The unmodified source palette loaded by kPalette. Additional
+ * palette entries may be mixed into the source palette by
+ * CelObj objects, which contain their own palettes.
+ */
+ Palette _sourcePalette;
+
+ /**
+ * The palette to be used when the hardware is next updated.
+ * On update, _nextPalette is transferred to _sysPalette.
+ */
+ Palette _nextPalette;
+
+ // SQ6 defines 10 cyclers
PalCycler *_cyclers[10];
+
/**
- * The cycle map is used to detect overlapping cyclers. According to SCI engine code, when two cyclers overlap,
- * a fatal error has occurred and the engine will display an error and then exit.
+ * The cycle map is used to detect overlapping cyclers.
+ * According to SCI engine code, when two cyclers overlap,
+ * a fatal error has occurred and the engine will display
+ * an error and then exit.
*/
bool _cycleMap[256];
inline void clearCycleMap(uint16 fromColor, uint16 numColorsToClear);
@@ -90,32 +120,138 @@ namespace Sci {
inline PalCycler *getCycler(uint16 fromColor);
/**
- * The fade table records the expected intensity level of each pixel in the palette that will be displayed on
- * the next frame.
+ * The fade table records the expected intensity level of each pixel
+ * in the palette that will be displayed on the next frame.
*/
byte _fadeTable[256];
+
+ /**
+ * An optional lookup table used to remap RGB565 colors to a palette
+ * index. Used by Phantasmagoria 2 in 8-bit color environments.
+ */
byte *_clutTable;
+ /**
+ * An optional palette used to describe the source colors used
+ * in a palette vary operation. If this palette is not specified,
+ * sourcePalette is used instead.
+ */
+ Palette *_varyStartPalette;
+
+ /**
+ * An optional palette used to describe the target colors used
+ * in a palette vary operation.
+ */
+ Palette *_varyTargetPalette;
+
+ /**
+ * The minimum palette index that has been varied from the
+ * source palette. 0–255
+ */
+ uint8 _varyFromColor;
+
+ /**
+ * The maximum palette index that is has been varied from the
+ * source palette. 0-255
+ */
+ uint8 _varyToColor;
+
+ /**
+ * The tick at the last time the palette vary was updated.
+ */
+ uint32 _varyLastTick;
+
+ /**
+ * TODO: Document
+ * The velocity of change in percent?
+ */
+ int _varyTime;
+
+ /**
+ * TODO: Better documentation
+ * The direction of change, -1, 0, or 1.
+ */
+ int16 _varyDirection;
+
+ /**
+ * The amount, in percent, that the vary color is currently
+ * blended into the source color.
+ */
+ int16 _varyPercent;
+
+ /**
+ * The target amount that a vary color will be blended into
+ * the source color.
+ */
+ int16 _varyTargetPercent;
+
+ /**
+ * The number of time palette varying has been paused.
+ */
+ uint16 _varyNumTimesPaused;
+
+ /**
+ * Submits a palette to display. Entries marked as “used” in the
+ * submitted palette are merged into the existing entries of
+ * _sourcePalette.
+ */
+ void submit(Palette &palette);
+
public:
- void applyAllCycles();
- void applyCycles();
- void applyFade();
+ override virtual void saveLoadWithSerializer(Common::Serializer &s);
+
+ override virtual bool kernelSetFromResource(GuiResourceId resourceId, bool force);
+ override virtual int16 kernelFindColor(const uint16 r, const uint16 g, const uint16 b);
+ override virtual void set(Palette *newPalette, bool force, bool forceRealMerge = false);
+ int16 matchColor(const byte matchRed, const byte matchGreen, const byte matchBlue, const int defaultDifference, int &lastCalculatedDifference, const bool *const matchTable);
+
+ void updateForFrame();
+ void updateHardware();
+ void applyAll();
bool loadClut(uint16 clutId);
byte matchClutColor(uint16 color);
void unloadClut();
- int16 setCycle(uint16 fromColor, uint16 toColor, int16 direction, int16 delay);
- void doCycle(uint16 fromColor, int16 speed);
- void cycleOn(uint16 fromColor);
- void cyclePause(uint16 fromColor);
+ void kernelPalVarySet(const GuiResourceId paletteId, const int16 percent, const int time, const int16 fromColor, const int16 toColor);
+ void kernelPalVaryMergeTarget(const GuiResourceId paletteId);
+ void kernelPalVarySetTarget(const GuiResourceId paletteId);
+ void kernelPalVarySetStart(const GuiResourceId paletteId);
+ void kernelPalVaryMergeStart(const GuiResourceId paletteId);
+ override virtual void kernelPalVaryPause(const bool pause);
+
+ void setVary(const Palette *const targetPalette, const int16 percent, const int time, const int16 fromColor, const int16 toColor);
+ void setVaryPercent(const int16 percent, const int time, const int16 fromColor, const int16 fromColorAlternate);
+ int16 getVaryPercent() const;
+ void varyOff();
+ void mergeTarget(const Palette *const palette);
+ void varyPause();
+ void varyOn();
+ void setVaryTime(const int time);
+ void setTarget(const Palette *const palette);
+ void setStart(const Palette *const palette);
+ void mergeStart(const Palette *const palette);
+ private:
+ bool createPaletteFromResourceInternal(const GuiResourceId paletteId, Palette *const out) const;
+ Palette getPaletteFromResourceInternal(const GuiResourceId paletteId) const;
+ void setVaryTimeInternal(const int16 percent, const int time);
+ public:
+ void applyVary();
+
+ void setCycle(const uint8 fromColor, const uint8 toColor, const int16 direction, const int16 delay);
+ void doCycle(const uint8 fromColor, const int16 speed);
+ void cycleOn(const uint8 fromColor);
+ void cyclePause(const uint8 fromColor);
void cycleAllOn();
void cycleAllPause();
- void cycleOff(uint16 fromColor);
+ void cycleOff(const uint8 fromColor);
void cycleAllOff();
+ void applyAllCycles();
+ void applyCycles();
- void setFade(uint8 percent, uint16 fromColor, uint16 toColor);
+ void setFade(const uint8 percent, const uint8 fromColor, const uint16 toColor);
void fadeOff();
+ void applyFade();
};
}
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index e568562820..9f3858e7b8 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -1037,9 +1037,10 @@ void SciEngine::loadMacExecutable() {
}
}
-// Note that SCI engine also has a corresponding TimeMgr::SetTickCount method
-// which is used by TimeMgr::SaveRestore when loading a save game.
uint32 SciEngine::getTickCount() {
- return (uint32) ((g_system->getMillis() * 60) / 1000);
+ return g_engine->getTotalPlayTime() * 60 / 1000;
+}
+void SciEngine::setTickCount(const uint32 ticks) {
+ return g_engine->setTotalPlayTime(ticks * 1000 / 60);
}
} // End of namespace Sci
diff --git a/engines/sci/sci.h b/engines/sci/sci.h
index e49345c0d4..8b47cf8876 100644
--- a/engines/sci/sci.h
+++ b/engines/sci/sci.h
@@ -237,6 +237,7 @@ public:
bool canSaveGameStateCurrently();
void syncSoundSettings();
uint32 getTickCount();
+ void setTickCount(const uint32 ticks);
/**
* Syncs the audio options of the ScummVM launcher (speech, subtitles or