diff options
author | Filippos Karapetis | 2016-01-15 01:33:24 +0200 |
---|---|---|
committer | Filippos Karapetis | 2016-01-15 01:33:24 +0200 |
commit | d44c150d16b2d1d3b1aec4181fe6328db36cd8fd (patch) | |
tree | d97024e5ae5e04c17939769b9e7dd6d91682d0b0 /engines | |
parent | 6269075ad67de618722e1c9e1e712950a749002b (diff) | |
parent | 40f6d74d97618adc80d10244a77c444bf7e613eb (diff) | |
download | scummvm-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')
-rw-r--r-- | engines/sci/detection.cpp | 6 | ||||
-rw-r--r-- | engines/sci/engine/kernel.h | 13 | ||||
-rw-r--r-- | engines/sci/engine/kernel_tables.h | 55 | ||||
-rw-r--r-- | engines/sci/engine/kgraphics32.cpp | 91 | ||||
-rw-r--r-- | engines/sci/engine/kmisc.cpp | 3 | ||||
-rw-r--r-- | engines/sci/engine/savegame.cpp | 99 | ||||
-rw-r--r-- | engines/sci/engine/savegame.h | 3 | ||||
-rw-r--r-- | engines/sci/graphics/frameout.cpp | 11 | ||||
-rw-r--r-- | engines/sci/graphics/frameout.h | 3 | ||||
-rw-r--r-- | engines/sci/graphics/helpers.h | 24 | ||||
-rw-r--r-- | engines/sci/graphics/palette.cpp | 6 | ||||
-rw-r--r-- | engines/sci/graphics/palette.h | 12 | ||||
-rw-r--r-- | engines/sci/graphics/palette32.cpp | 592 | ||||
-rw-r--r-- | engines/sci/graphics/palette32.h | 166 | ||||
-rw-r--r-- | engines/sci/sci.cpp | 7 | ||||
-rw-r--r-- | engines/sci/sci.h | 1 |
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 |