From dc42a86f7e181b3b3662fd40e94a9afdfde65ffc Mon Sep 17 00:00:00 2001 From: Colin Snover Date: Thu, 14 Jan 2016 10:45:27 -0600 Subject: SCI: Fix bad spacing in kernel tables --- engines/sci/engine/kernel_tables.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'engines') diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h index 3189709109..53447ab907 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. */ -- cgit v1.2.3 From f1cf07eded09f5f66a0915ddb79a890a3a11a362 Mon Sep 17 00:00:00 2001 From: Colin Snover Date: Thu, 14 Jan 2016 12:05:29 -0600 Subject: SCI: Add kPalMorph stub --- engines/sci/engine/kernel.h | 1 + engines/sci/engine/kernel_tables.h | 1 - engines/sci/engine/kgraphics32.cpp | 10 ++++++++++ 3 files changed, 11 insertions(+), 1 deletion(-) (limited to 'engines') diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h index a2563b5a32..d9e42cb3ee 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 53447ab907..b89797f348 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 a765fe842e..ddb6106c8c 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); -- cgit v1.2.3 From fb891e4c081d5f0678cdda3855daa82728dc0ac0 Mon Sep 17 00:00:00 2001 From: Colin Snover Date: Thu, 14 Jan 2016 10:50:41 -0600 Subject: SCI: Implement SCI32 kPalVary and kPalette setFade This also fixes kPalCycle signatures to be more accurate. --- engines/sci/engine/kernel.h | 12 +- engines/sci/engine/kernel_tables.h | 47 ++-- engines/sci/engine/kgraphics32.cpp | 81 +++++- engines/sci/engine/savegame.cpp | 87 +++++++ engines/sci/engine/savegame.h | 3 +- engines/sci/graphics/frameout.cpp | 11 +- engines/sci/graphics/helpers.h | 24 ++ engines/sci/graphics/palette.cpp | 4 +- engines/sci/graphics/palette.h | 10 +- engines/sci/graphics/palette32.cpp | 508 ++++++++++++++++++++++++++++++++----- engines/sci/graphics/palette32.h | 166 ++++++++++-- 11 files changed, 828 insertions(+), 125 deletions(-) (limited to 'engines') diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h index d9e42cb3ee..f62c43840f 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 b89797f348..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 }; diff --git a/engines/sci/engine/kgraphics32.cpp b/engines/sci/engine/kgraphics32.cpp index ddb6106c8c..e87849201e 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 09ccff39ac..3103be1463 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 5512b90fa8..1062433d24 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 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/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..698ba632fe 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 82fd20a432..dfc276fa59 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 95c4f99974..00e502c696 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 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(); }; } -- cgit v1.2.3 From 76bd2eeb2e580a6a5d0b883dfc3e6b3c6e84e6b3 Mon Sep 17 00:00:00 2001 From: Colin Snover Date: Thu, 14 Jan 2016 10:54:27 -0600 Subject: 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. --- engines/sci/detection.cpp | 6 +++++- engines/sci/engine/kmisc.cpp | 3 +-- engines/sci/engine/savegame.cpp | 12 ++++++++++-- engines/sci/engine/savegame.h | 2 +- engines/sci/graphics/palette.cpp | 2 +- engines/sci/sci.cpp | 7 ++++--- engines/sci/sci.h | 1 + 7 files changed, 23 insertions(+), 10 deletions(-) (limited to 'engines') 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/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 3103be1463..481ae75907 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 1062433d24..bb555434c9 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 698ba632fe..106924ecd3 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 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 -- cgit v1.2.3 From 8224d321226793c6d7499c1d1548269ef3912327 Mon Sep 17 00:00:00 2001 From: Colin Snover Date: Thu, 14 Jan 2016 10:56:58 -0600 Subject: SCI: Implement SCI32 kPalette findColor and matchColor It seems that findColor is used only by kPalette, and matchColor is used only by Remap. --- engines/sci/graphics/palette.h | 2 +- engines/sci/graphics/palette32.cpp | 82 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) (limited to 'engines') diff --git a/engines/sci/graphics/palette.h b/engines/sci/graphics/palette.h index dfc276fa59..61a8d36cb9 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 00e502c696..9aa08782a5 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; -- cgit v1.2.3 From 40f6d74d97618adc80d10244a77c444bf7e613eb Mon Sep 17 00:00:00 2001 From: Colin Snover Date: Thu, 14 Jan 2016 10:58:37 -0600 Subject: SCI: Minor clean-ups to comments and null pointer values --- engines/sci/graphics/frameout.h | 3 ++- engines/sci/graphics/palette32.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'engines') 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/palette32.cpp b/engines/sci/graphics/palette32.cpp index 9aa08782a5..8ce8b85678 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; } // -- cgit v1.2.3