[Scummvm-cvs-logs] scummvm master -> d44c150d16b2d1d3b1aec4181fe6328db36cd8fd

bluegr bluegr at gmail.com
Fri Jan 15 00:34:05 CET 2016


This automated email contains information about 7 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
dc42a86f7e SCI: Fix bad spacing in kernel tables
f1cf07eded SCI: Add kPalMorph stub
fb891e4c08 SCI: Implement SCI32 kPalVary and kPalette setFade
76bd2eeb2e SCI: Use tick-based timing more consistently
8224d32122 SCI: Implement SCI32 kPalette findColor and matchColor
40f6d74d97 SCI: Minor clean-ups to comments and null pointer values
d44c150d16 Merge pull request #649 from csnover/sci32-kPalette


Commit: dc42a86f7e181b3b3662fd40e94a9afdfde65ffc
    https://github.com/scummvm/scummvm/commit/dc42a86f7e181b3b3662fd40e94a9afdfde65ffc
Author: Colin Snover (github.com at zetafleet.com)
Date: 2016-01-14T16:13:22-06:00

Commit Message:
SCI: Fix bad spacing in kernel tables

Changed paths:
    engines/sci/engine/kernel_tables.h



diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h
index 3189709..53447ab 100644
--- a/engines/sci/engine/kernel_tables.h
+++ b/engines/sci/engine/kernel_tables.h
@@ -695,15 +695,16 @@ 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. */


Commit: f1cf07eded09f5f66a0915ddb79a890a3a11a362
    https://github.com/scummvm/scummvm/commit/f1cf07eded09f5f66a0915ddb79a890a3a11a362
Author: Colin Snover (github.com at zetafleet.com)
Date: 2016-01-14T16:13:22-06:00

Commit Message:
SCI: Add kPalMorph stub

Changed paths:
    engines/sci/engine/kernel.h
    engines/sci/engine/kernel_tables.h
    engines/sci/engine/kgraphics32.cpp



diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h
index a2563b5..d9e42cb 100644
--- a/engines/sci/engine/kernel.h
+++ b/engines/sci/engine/kernel.h
@@ -478,6 +478,7 @@ reg_t kPalVaryUnknown(EngineState *s, int argc, reg_t *argv);
 reg_t kPalVaryUnknown2(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 53447ab..b89797f 100644
--- a/engines/sci/engine/kernel_tables.h
+++ b/engines/sci/engine/kernel_tables.h
@@ -697,7 +697,6 @@ static SciKernelMapEntry s_kernelMap[] = {
 	//   (inclusive) are set to 0
 	{ 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
diff --git a/engines/sci/engine/kgraphics32.cpp b/engines/sci/engine/kgraphics32.cpp
index a765fe8..ddb6106 100644
--- a/engines/sci/engine/kgraphics32.cpp
+++ b/engines/sci/engine/kgraphics32.cpp
@@ -728,6 +728,16 @@ reg_t kSetScroll(EngineState *s, int argc, reg_t *argv) {
 	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 kPalVaryUnknown(EngineState *s, int argc, reg_t *argv) {
 	// TODO: Unknown (seems to be SCI32 exclusive)
 	return kStub(s, argc, argv);


Commit: fb891e4c081d5f0678cdda3855daa82728dc0ac0
    https://github.com/scummvm/scummvm/commit/fb891e4c081d5f0678cdda3855daa82728dc0ac0
Author: Colin Snover (github.com at zetafleet.com)
Date: 2016-01-14T16:13:22-06:00

Commit Message:
SCI: Implement SCI32 kPalVary and kPalette setFade

This also fixes kPalCycle signatures to be more accurate.

Changed paths:
    engines/sci/engine/kernel.h
    engines/sci/engine/kernel_tables.h
    engines/sci/engine/kgraphics32.cpp
    engines/sci/engine/savegame.cpp
    engines/sci/engine/savegame.h
    engines/sci/graphics/frameout.cpp
    engines/sci/graphics/helpers.h
    engines/sci/graphics/palette.cpp
    engines/sci/graphics/palette.h
    engines/sci/graphics/palette32.cpp
    engines/sci/graphics/palette32.h



diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h
index d9e42cb..f62c438 100644
--- a/engines/sci/engine/kernel.h
+++ b/engines/sci/engine/kernel.h
@@ -474,8 +474,16 @@ 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);
diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h
index b89797f..b1df235 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
 };
 
diff --git a/engines/sci/engine/kgraphics32.cpp b/engines/sci/engine/kgraphics32.cpp
index ddb6106..e878492 100644
--- a/engines/sci/engine/kgraphics32.cpp
+++ b/engines/sci/engine/kgraphics32.cpp
@@ -737,21 +737,78 @@ reg_t kMorphOn(EngineState *s, int argc, reg_t *argv) {
 	// palMorphIsOn flag back to false.
 	kStub(s, argc, argv);
 	return NULL_REG;
+}
 
-reg_t kPalVaryUnknown(EngineState *s, int argc, reg_t *argv) {
-	// TODO: Unknown (seems to be SCI32 exclusive)
-	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 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 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/savegame.cpp b/engines/sci/engine/savegame.cpp
index 09ccff3..3103be1 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
 
@@ -304,6 +305,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 +723,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) {
diff --git a/engines/sci/engine/savegame.h b/engines/sci/engine/savegame.h
index 5512b90..1062433 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
  *      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 9512ec2..90bd798 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/helpers.h b/engines/sci/graphics/helpers.h
index 4889f12..e5b9f2a 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 3742a8d..698ba63 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;
@@ -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 82fd20a..dfc276f 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,7 +78,7 @@ 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);
@@ -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 95c4f99..00e502c 100644
--- a/engines/sci/graphics/palette32.cpp
+++ b/engines/sci/graphics/palette32.cpp
@@ -21,20 +21,153 @@
  */
 
 #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;
+}
+
+// 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);
+}
+
+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.
@@ -85,6 +218,252 @@ void GfxPalette32::unloadClut() {
 	_clutTable = 0;
 }
 
+//
+// 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 +509,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 +518,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 +561,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 +571,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 +623,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 +646,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;
+		}
 
-		// 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;
+		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++) {
+			_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 3432e4b..2e3f627 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();
 	};
 }
 


Commit: 76bd2eeb2e580a6a5d0b883dfc3e6b3c6e84e6b3
    https://github.com/scummvm/scummvm/commit/76bd2eeb2e580a6a5d0b883dfc3e6b3c6e84e6b3
Author: Colin Snover (github.com at zetafleet.com)
Date: 2016-01-14T16:13:22-06:00

Commit Message:
SCI: Use tick-based timing more consistently

This means tick-based times are saved to save games, as in SCI32
engine, instead of seconds, which are not accurate enough. It
also means places in SCI engine that need to access game ticks
should do so through g_sci instead of g_system or g_engine.

Changed paths:
    engines/sci/detection.cpp
    engines/sci/engine/kmisc.cpp
    engines/sci/engine/savegame.cpp
    engines/sci/engine/savegame.h
    engines/sci/graphics/palette.cpp
    engines/sci/sci.cpp
    engines/sci/sci.h



diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp
index bac9b34..4179bae 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/kmisc.cpp b/engines/sci/engine/kmisc.cpp
index 1d9dae6..084315e 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 3103be1..481ae75 100644
--- a/engines/sci/engine/savegame.cpp
+++ b/engines/sci/engine/savegame.cpp
@@ -273,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);
 	}
 }
@@ -1031,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 1062433..bb55543 100644
--- a/engines/sci/engine/savegame.h
+++ b/engines/sci/engine/savegame.h
@@ -37,7 +37,7 @@ struct EngineState;
  *
  * Version - new/changed feature
  * =============================
- *      34 - SCI32 palettes
+ *      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
diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp
index 698ba63..106924e 100644
--- a/engines/sci/graphics/palette.cpp
+++ b/engines/sci/graphics/palette.cpp
@@ -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...
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index e568562..9f3858e 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 e49345c..8b47cf8 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


Commit: 8224d321226793c6d7499c1d1548269ef3912327
    https://github.com/scummvm/scummvm/commit/8224d321226793c6d7499c1d1548269ef3912327
Author: Colin Snover (github.com at zetafleet.com)
Date: 2016-01-14T17:21:05-06:00

Commit Message:
SCI: Implement SCI32 kPalette findColor and matchColor

It seems that findColor is used only by kPalette, and matchColor is
used only by Remap.

Changed paths:
    engines/sci/graphics/palette.h
    engines/sci/graphics/palette32.cpp



diff --git a/engines/sci/graphics/palette.h b/engines/sci/graphics/palette.h
index dfc276f..61a8d36 100644
--- a/engines/sci/graphics/palette.h
+++ b/engines/sci/graphics/palette.h
@@ -82,7 +82,7 @@ public:
 	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();
diff --git a/engines/sci/graphics/palette32.cpp b/engines/sci/graphics/palette32.cpp
index 00e502c..9aa0878 100644
--- a/engines/sci/graphics/palette32.cpp
+++ b/engines/sci/graphics/palette32.cpp
@@ -111,6 +111,45 @@ override bool GfxPalette32::kernelSetFromResource(GuiResourceId resourceId, bool
 	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
@@ -123,6 +162,49 @@ override void GfxPalette32::set(Palette *newPalette, bool force, bool forceRealM
 	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;


Commit: 40f6d74d97618adc80d10244a77c444bf7e613eb
    https://github.com/scummvm/scummvm/commit/40f6d74d97618adc80d10244a77c444bf7e613eb
Author: Colin Snover (github.com at zetafleet.com)
Date: 2016-01-14T17:21:05-06:00

Commit Message:
SCI: Minor clean-ups to comments and null pointer values

Changed paths:
    engines/sci/graphics/frameout.h
    engines/sci/graphics/palette32.cpp



diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h
index a422572..0017fb1 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/palette32.cpp b/engines/sci/graphics/palette32.cpp
index 9aa0878..8ce8b85 100644
--- a/engines/sci/graphics/palette32.cpp
+++ b/engines/sci/graphics/palette32.cpp
@@ -297,7 +297,7 @@ 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;
 }
 
 //


Commit: d44c150d16b2d1d3b1aec4181fe6328db36cd8fd
    https://github.com/scummvm/scummvm/commit/d44c150d16b2d1d3b1aec4181fe6328db36cd8fd
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2016-01-15T01:33:24+02:00

Commit Message:
Merge pull request #649 from csnover/sci32-kPalette

SCI32: kPalVary, kPalette fade support, kPalette findColor support, improvements to game time management

Changed paths:
    engines/sci/detection.cpp
    engines/sci/engine/kernel.h
    engines/sci/engine/kernel_tables.h
    engines/sci/engine/kgraphics32.cpp
    engines/sci/engine/kmisc.cpp
    engines/sci/engine/savegame.cpp
    engines/sci/engine/savegame.h
    engines/sci/graphics/frameout.cpp
    engines/sci/graphics/frameout.h
    engines/sci/graphics/helpers.h
    engines/sci/graphics/palette.cpp
    engines/sci/graphics/palette.h
    engines/sci/graphics/palette32.cpp
    engines/sci/graphics/palette32.h
    engines/sci/sci.cpp
    engines/sci/sci.h









More information about the Scummvm-git-logs mailing list