diff options
Diffstat (limited to 'engines/sci/engine')
-rw-r--r-- | engines/sci/engine/gc.cpp | 10 | ||||
-rw-r--r-- | engines/sci/engine/kernel.h | 48 | ||||
-rw-r--r-- | engines/sci/engine/kernel_tables.h | 188 | ||||
-rw-r--r-- | engines/sci/engine/kfile.cpp | 50 | ||||
-rw-r--r-- | engines/sci/engine/kgraphics.cpp | 2 | ||||
-rw-r--r-- | engines/sci/engine/kgraphics32.cpp | 474 | ||||
-rw-r--r-- | engines/sci/engine/kpathing.cpp | 2 | ||||
-rw-r--r-- | engines/sci/engine/ksound.cpp | 238 | ||||
-rw-r--r-- | engines/sci/engine/savegame.cpp | 30 | ||||
-rw-r--r-- | engines/sci/engine/savegame.h | 3 | ||||
-rw-r--r-- | engines/sci/engine/script_patches.cpp | 215 | ||||
-rw-r--r-- | engines/sci/engine/workarounds.cpp | 14 | ||||
-rw-r--r-- | engines/sci/engine/workarounds.h | 2 |
13 files changed, 907 insertions, 369 deletions
diff --git a/engines/sci/engine/gc.cpp b/engines/sci/engine/gc.cpp index 70c8c52bf0..b229490570 100644 --- a/engines/sci/engine/gc.cpp +++ b/engines/sci/engine/gc.cpp @@ -24,6 +24,10 @@ #include "common/array.h" #include "sci/graphics/ports.h" +#ifdef ENABLE_SCI32 +#include "sci/graphics/controls32.h" +#endif + namespace Sci { //#define GC_DEBUG_CODE @@ -150,6 +154,12 @@ AddrSet *findAllActiveReferences(EngineState *s) { } } +#ifdef ENABLE_SCI32 + // Init: ScrollWindows + if (g_sci->_gfxControls32) + wm.pushArray(g_sci->_gfxControls32->listObjectReferences()); +#endif + debugC(kDebugLevelGC, "[GC] -- Finished explicitly loaded scripts, done with root set"); processWorkList(s->_segMan, wm, heap); diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h index 62566a74b2..1202982986 100644 --- a/engines/sci/engine/kernel.h +++ b/engines/sci/engine/kernel.h @@ -412,7 +412,7 @@ reg_t kPlatform(EngineState *s, int argc, reg_t *argv); reg_t kTextColors(EngineState *s, int argc, reg_t *argv); reg_t kTextFonts(EngineState *s, int argc, reg_t *argv); reg_t kShow(EngineState *s, int argc, reg_t *argv); -reg_t kRemapColors16(EngineState *s, int argc, reg_t *argv); +reg_t kRemapColors(EngineState *s, int argc, reg_t *argv); reg_t kDummy(EngineState *s, int argc, reg_t *argv); reg_t kEmpty(EngineState *s, int argc, reg_t *argv); reg_t kStub(EngineState *s, int argc, reg_t *argv); @@ -420,6 +420,26 @@ reg_t kStubNull(EngineState *s, int argc, reg_t *argv); #ifdef ENABLE_SCI32 // SCI2 Kernel Functions +reg_t kDoAudio32(EngineState *s, int argc, reg_t *argv); +reg_t kDoAudioInit(EngineState *s, int argc, reg_t *argv); +reg_t kDoAudioWaitForPlay(EngineState *s, int argc, reg_t *argv); +reg_t kDoAudioPlay(EngineState *s, int argc, reg_t *argv); +reg_t kDoAudioStop(EngineState *s, int argc, reg_t *argv); +reg_t kDoAudioPause(EngineState *s, int argc, reg_t *argv); +reg_t kDoAudioResume(EngineState *s, int argc, reg_t *argv); +reg_t kDoAudioPosition(EngineState *s, int argc, reg_t *argv); +reg_t kDoAudioRate(EngineState *s, int argc, reg_t *argv); +reg_t kDoAudioVolume(EngineState *s, int argc, reg_t *argv); +reg_t kDoAudioGetCapability(EngineState *s, int argc, reg_t *argv); +reg_t kDoAudioBitDepth(EngineState *s, int argc, reg_t *argv); +reg_t kDoAudioDistort(EngineState *s, int argc, reg_t *argv); +reg_t kDoAudioMixing(EngineState *s, int argc, reg_t *argv); +reg_t kDoAudioChannels(EngineState *s, int argc, reg_t *argv); +reg_t kDoAudioPreload(EngineState *s, int argc, reg_t *argv); +reg_t kDoAudioFade(EngineState *s, int argc, reg_t *argv); +reg_t kDoAudioHasSignal(EngineState *s, int argc, reg_t *argv); +reg_t kDoAudioSetLoop(EngineState *s, int argc, reg_t *argv); + reg_t kIsHiRes(EngineState *s, int argc, reg_t *argv); reg_t kArray(EngineState *s, int argc, reg_t *argv); reg_t kListAt(EngineState *s, int argc, reg_t *argv); @@ -447,19 +467,28 @@ reg_t kStringTrnExclude(EngineState *s, int argc, reg_t *argv); reg_t kScrollWindowCreate(EngineState *s, int argc, reg_t *argv); reg_t kScrollWindowAdd(EngineState *s, int argc, reg_t *argv); +reg_t kScrollWindowPageUp(EngineState *s, int argc, reg_t *argv); +reg_t kScrollWindowPageDown(EngineState *s, int argc, reg_t *argv); +reg_t kScrollWindowUpArrow(EngineState *s, int argc, reg_t *argv); +reg_t kScrollWindowDownArrow(EngineState *s, int argc, reg_t *argv); +reg_t kScrollWindowHome(EngineState *s, int argc, reg_t *argv); +reg_t kScrollWindowEnd(EngineState *s, int argc, reg_t *argv); reg_t kScrollWindowWhere(EngineState *s, int argc, reg_t *argv); +reg_t kScrollWindowGo(EngineState *s, int argc, reg_t *argv); +reg_t kScrollWindowModify(EngineState *s, int argc, reg_t *argv); +reg_t kScrollWindowHide(EngineState *s, int argc, reg_t *argv); reg_t kScrollWindowShow(EngineState *s, int argc, reg_t *argv); reg_t kScrollWindowDestroy(EngineState *s, int argc, reg_t *argv); reg_t kMulDiv(EngineState *s, int argc, reg_t *argv); -reg_t kRemapColors(EngineState *s, int argc, reg_t *argv); -reg_t kRemapOff(EngineState *s, int argc, reg_t *argv); -reg_t kRemapByRange(EngineState *s, int argc, reg_t *argv); -reg_t kRemapByPercent(EngineState *s, int argc, reg_t *argv); -reg_t kRemapToGray(EngineState *s, int argc, reg_t *argv); -reg_t kRemapToPercentGray(EngineState *s, int argc, reg_t *argv); -reg_t kRemapSetNoMatchRange(EngineState *s, int argc, reg_t *argv); +reg_t kRemapColors32(EngineState *s, int argc, reg_t *argv); +reg_t kRemapColorsOff(EngineState *s, int argc, reg_t *argv); +reg_t kRemapColorsByRange(EngineState *s, int argc, reg_t *argv); +reg_t kRemapColorsByPercent(EngineState *s, int argc, reg_t *argv); +reg_t kRemapColorsToGray(EngineState *s, int argc, reg_t *argv); +reg_t kRemapColorsToPercentGray(EngineState *s, int argc, reg_t *argv); +reg_t kRemapColorsBlockRange(EngineState *s, int argc, reg_t *argv); reg_t kAddScreenItem(EngineState *s, int argc, reg_t *argv); reg_t kUpdateScreenItem(EngineState *s, int argc, reg_t *argv); @@ -537,6 +566,7 @@ reg_t kMoveToFront(EngineState *s, int argc, reg_t *argv); reg_t kMoveToEnd(EngineState *s, int argc, reg_t *argv); reg_t kGetWindowsOption(EngineState *s, int argc, reg_t *argv); reg_t kWinHelp(EngineState *s, int argc, reg_t *argv); +reg_t kMessageBox(EngineState *s, int argc, reg_t *argv); reg_t kGetConfig(EngineState *s, int argc, reg_t *argv); reg_t kGetSierraProfileInt(EngineState *s, int argc, reg_t *argv); reg_t kCelInfo(EngineState *s, int argc, reg_t *argv); @@ -558,7 +588,6 @@ reg_t kPlayDuck(EngineState *s, int argc, reg_t *argv); reg_t kDoSoundInit(EngineState *s, int argc, reg_t *argv); reg_t kDoSoundPlay(EngineState *s, int argc, reg_t *argv); -reg_t kDoSoundRestore(EngineState *s, int argc, reg_t *argv); reg_t kDoSoundDispose(EngineState *s, int argc, reg_t *argv); reg_t kDoSoundMute(EngineState *s, int argc, reg_t *argv); reg_t kDoSoundStop(EngineState *s, int argc, reg_t *argv); @@ -573,7 +602,6 @@ reg_t kDoSoundUpdateCues(EngineState *s, int argc, reg_t *argv); reg_t kDoSoundSendMidi(EngineState *s, int argc, reg_t *argv); reg_t kDoSoundGlobalReverb(EngineState *s, int argc, reg_t *argv); reg_t kDoSoundSetHold(EngineState *s, int argc, reg_t *argv); -reg_t kDoSoundDummy(EngineState *s, int argc, reg_t *argv); reg_t kDoSoundGetAudioCapability(EngineState *s, int argc, reg_t *argv); reg_t kDoSoundSuspend(EngineState *s, int argc, reg_t *argv); reg_t kDoSoundSetVolume(EngineState *s, int argc, reg_t *argv); diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h index 0ede307e6b..dacaafe757 100644 --- a/engines/sci/engine/kernel_tables.h +++ b/engines/sci/engine/kernel_tables.h @@ -102,7 +102,7 @@ struct SciKernelMapSubEntry { static const SciKernelMapSubEntry kDoSound_subops[] = { { SIG_SOUNDSCI0, 0, MAP_CALL(DoSoundInit), "o", NULL }, { SIG_SOUNDSCI0, 1, MAP_CALL(DoSoundPlay), "o", NULL }, - { SIG_SOUNDSCI0, 2, MAP_CALL(DoSoundRestore), "(o)", NULL }, + { SIG_SOUNDSCI0, 2, MAP_EMPTY(DoSoundRestore), "(o)", NULL }, { SIG_SOUNDSCI0, 3, MAP_CALL(DoSoundDispose), "o", NULL }, { SIG_SOUNDSCI0, 4, MAP_CALL(DoSoundMute), "(i)", NULL }, { SIG_SOUNDSCI0, 5, MAP_CALL(DoSoundStop), "o", NULL }, @@ -115,7 +115,7 @@ static const SciKernelMapSubEntry kDoSound_subops[] = { { SIG_SOUNDSCI0, 12, MAP_CALL(DoSoundStopAll), "", NULL }, { SIG_SOUNDSCI1EARLY, 0, MAP_CALL(DoSoundMasterVolume), NULL, NULL }, { SIG_SOUNDSCI1EARLY, 1, MAP_CALL(DoSoundMute), NULL, NULL }, - { SIG_SOUNDSCI1EARLY, 2, MAP_CALL(DoSoundRestore), NULL, NULL }, + { SIG_SOUNDSCI1EARLY, 2, MAP_EMPTY(DoSoundRestore), NULL, NULL }, { SIG_SOUNDSCI1EARLY, 3, MAP_CALL(DoSoundGetPolyphony), NULL, NULL }, { SIG_SOUNDSCI1EARLY, 4, MAP_CALL(DoSoundUpdate), NULL, NULL }, { SIG_SOUNDSCI1EARLY, 5, MAP_CALL(DoSoundInit), NULL, NULL }, @@ -128,11 +128,11 @@ static const SciKernelMapSubEntry kDoSound_subops[] = { { SIG_SOUNDSCI1EARLY, 12, MAP_CALL(DoSoundSendMidi), "oiii", NULL }, { SIG_SOUNDSCI1EARLY, 13, MAP_CALL(DoSoundGlobalReverb), "(i)", NULL }, { SIG_SOUNDSCI1EARLY, 14, MAP_CALL(DoSoundSetHold), "oi", NULL }, - { SIG_SOUNDSCI1EARLY, 15, MAP_CALL(DoSoundDummy), "", NULL }, + { SIG_SOUNDSCI1EARLY, 15, MAP_EMPTY(DoSoundDummy), "", NULL }, // ^^ Longbow demo { SIG_SOUNDSCI1LATE, 0, MAP_CALL(DoSoundMasterVolume), NULL, NULL }, { SIG_SOUNDSCI1LATE, 1, MAP_CALL(DoSoundMute), NULL, NULL }, - { SIG_SOUNDSCI1LATE, 2, MAP_CALL(DoSoundRestore), "", NULL }, + { SIG_SOUNDSCI1LATE, 2, MAP_EMPTY(DoSoundRestore), "", NULL }, { SIG_SOUNDSCI1LATE, 3, MAP_CALL(DoSoundGetPolyphony), NULL, NULL }, { SIG_SOUNDSCI1LATE, 4, MAP_CALL(DoSoundGetAudioCapability), "", NULL }, { SIG_SOUNDSCI1LATE, 5, MAP_CALL(DoSoundSuspend), "i", NULL }, @@ -143,7 +143,7 @@ static const SciKernelMapSubEntry kDoSound_subops[] = { { SIG_SOUNDSCI1LATE, 10, MAP_CALL(DoSoundPause), NULL, NULL }, { SIG_SOUNDSCI1LATE, 11, MAP_CALL(DoSoundFade), "oiiii(i)", kDoSoundFade_workarounds }, { SIG_SOUNDSCI1LATE, 12, MAP_CALL(DoSoundSetHold), NULL, NULL }, - { SIG_SOUNDSCI1LATE, 13, MAP_CALL(DoSoundDummy), NULL, NULL }, + { SIG_SOUNDSCI1LATE, 13, MAP_EMPTY(DoSoundDummy), NULL, NULL }, { SIG_SOUNDSCI1LATE, 14, MAP_CALL(DoSoundSetVolume), "oi", NULL }, { SIG_SOUNDSCI1LATE, 15, MAP_CALL(DoSoundSetPriority), "oi", NULL }, { SIG_SOUNDSCI1LATE, 16, MAP_CALL(DoSoundSetLoop), "oi", NULL }, @@ -152,36 +152,97 @@ static const SciKernelMapSubEntry kDoSound_subops[] = { { SIG_SOUNDSCI1LATE, 19, MAP_CALL(DoSoundGlobalReverb), NULL, NULL }, { SIG_SOUNDSCI1LATE, 20, MAP_CALL(DoSoundUpdate), NULL, NULL }, #ifdef ENABLE_SCI32 - { SIG_SOUNDSCI21, 0, MAP_CALL(DoSoundMasterVolume), NULL, NULL }, - { SIG_SOUNDSCI21, 1, MAP_CALL(DoSoundMute), NULL, NULL }, - { SIG_SOUNDSCI21, 2, MAP_CALL(DoSoundRestore), NULL, NULL }, - { SIG_SOUNDSCI21, 3, MAP_CALL(DoSoundGetPolyphony), NULL, NULL }, - { SIG_SOUNDSCI21, 4, MAP_CALL(DoSoundGetAudioCapability), NULL, NULL }, - { SIG_SOUNDSCI21, 5, MAP_CALL(DoSoundSuspend), NULL, NULL }, - { SIG_SOUNDSCI21, 6, MAP_CALL(DoSoundInit), NULL, NULL }, - { SIG_SOUNDSCI21, 7, MAP_CALL(DoSoundDispose), NULL, NULL }, - { SIG_SOUNDSCI21, 8, MAP_CALL(DoSoundPlay), "o(i)", NULL }, + { SIG_SOUNDSCI21, 0, MAP_CALL(DoSoundMasterVolume), "(i)", NULL }, + { SIG_SOUNDSCI21, 1, MAP_CALL(DoSoundMute), "(i)", NULL }, + { SIG_SOUNDSCI21, 2, MAP_EMPTY(DoSoundRestore), NULL, NULL }, + { SIG_SOUNDSCI21, 3, MAP_CALL(DoSoundGetPolyphony), "", NULL }, + { SIG_SOUNDSCI21, 4, MAP_CALL(DoSoundGetAudioCapability), "", NULL }, + { SIG_SOUNDSCI21, 5, MAP_CALL(DoSoundSuspend), "i", NULL }, + { SIG_SOUNDSCI21, 6, MAP_CALL(DoSoundInit), "o", NULL }, + { SIG_SOUNDSCI21, 7, MAP_CALL(DoSoundDispose), "o", NULL }, + { SIG_SOUNDSCI21, 8, MAP_CALL(DoSoundPlay), "o", kDoSoundPlay_workarounds }, // ^^ TODO: if this is really the only change between SCI1LATE AND SCI21, we could rename the // SIG_SOUNDSCI1LATE #define to SIG_SINCE_SOUNDSCI1LATE and make it being SCI1LATE+. Although // I guess there are many more changes somewhere // TODO: Quest for Glory 4 (SCI2.1) uses the old scheme, we need to detect it accordingly // signature for SCI21 should be "o" - { SIG_SOUNDSCI21, 9, MAP_CALL(DoSoundStop), NULL, NULL }, - { SIG_SOUNDSCI21, 10, MAP_CALL(DoSoundPause), NULL, NULL }, - { SIG_SOUNDSCI21, 11, MAP_CALL(DoSoundFade), NULL, kDoSoundFade_workarounds }, - { SIG_SOUNDSCI21, 12, MAP_CALL(DoSoundSetHold), NULL, NULL }, - { SIG_SOUNDSCI21, 13, MAP_CALL(DoSoundDummy), NULL, NULL }, - { SIG_SOUNDSCI21, 14, MAP_CALL(DoSoundSetVolume), NULL, NULL }, - { SIG_SOUNDSCI21, 15, MAP_CALL(DoSoundSetPriority), NULL, NULL }, - { SIG_SOUNDSCI21, 16, MAP_CALL(DoSoundSetLoop), NULL, NULL }, - { SIG_SOUNDSCI21, 17, MAP_CALL(DoSoundUpdateCues), NULL, NULL }, - { SIG_SOUNDSCI21, 18, MAP_CALL(DoSoundSendMidi), NULL, NULL }, - { SIG_SOUNDSCI21, 19, MAP_CALL(DoSoundGlobalReverb), NULL, NULL }, - { SIG_SOUNDSCI21, 20, MAP_CALL(DoSoundUpdate), NULL, NULL }, + { SIG_SOUNDSCI21, 9, MAP_CALL(DoSoundStop), "o", NULL }, + { SIG_SOUNDSCI21, 10, MAP_CALL(DoSoundPause), "[o0]i", NULL }, + { SIG_SOUNDSCI21, 11, MAP_CALL(DoSoundFade), "oiiii", kDoSoundFade_workarounds }, + { SIG_SOUNDSCI21, 12, MAP_CALL(DoSoundSetHold), "oi", NULL }, + { SIG_SOUNDSCI21, 13, MAP_EMPTY(DoSoundDummy), NULL, NULL }, + { SIG_SOUNDSCI21, 14, MAP_CALL(DoSoundSetVolume), "oi", NULL }, + { SIG_SOUNDSCI21, 15, MAP_CALL(DoSoundSetPriority), "oi", NULL }, + { SIG_SOUNDSCI21, 16, MAP_CALL(DoSoundSetLoop), "oi", NULL }, + { SIG_SOUNDSCI21, 17, MAP_CALL(DoSoundUpdateCues), "o", NULL }, + { SIG_SOUNDSCI21, 18, MAP_CALL(DoSoundSendMidi), "oiiii", NULL }, + { SIG_SOUNDSCI21, 19, MAP_CALL(DoSoundGlobalReverb), "(i)", NULL }, + { SIG_SOUNDSCI21, 20, MAP_CALL(DoSoundUpdate), "o", NULL }, #endif SCI_SUBOPENTRY_TERMINATOR }; +#ifdef ENABLE_SCI32 +// NOTE: In SSCI, some 'unused' kDoAudio subops are actually +// called indirectly by kDoSound: +// +// kDoSoundGetAudioCapability -> kDoAudioGetCapability +// kDoSoundPlay -> kDoAudioPlay, kDoAudioStop +// kDoSoundPause -> kDoAudioPause, kDoAudioResume +// kDoSoundFade -> kDoAudioFade +// kDoSoundSetVolume -> kDoAudioVolume +// kDoSoundSetLoop -> kDoAudioSetLoop +// kDoSoundUpdateCues -> kDoAudioPosition +// +// In ScummVM, logic inside these kernel functions has been +// moved to methods of Audio32, and direct calls to Audio32 +// are made from kDoSound instead. +// +// Some kDoAudio methods are esoteric and appear to be used +// only by one or two games: +// +// kDoAudioMixing: Phantasmagoria (other games call this +// function, but only to disable the feature) +// kDoAudioHasSignal: SQ6 TalkRandCycle +// kDoAudioPan: Rama RegionSFX::pan method +// +// Finally, there is a split in SCI2.1mid audio code. +// QFG4CD & SQ6 do not have opcodes 18 and 19, but they +// exist in GK2, KQ7 2.00b, Phantasmagoria 1, PQ:SWAT, and +// Torin. (It is unknown if they exist in MUMG Deluxe or +// Shivers 1; they are not used in either of these games.) + +// version, subId, function-mapping, signature, workarounds +static const SciKernelMapSubEntry kDoAudio_subops[] = { + { SIG_SCI32, 0, MAP_CALL(DoAudioInit), "", NULL }, + // SCI2 includes a Sync script that would call + // kDoAudioWaitForPlay, but SSCI has no opcode 1 until + // SCI2.1early + { SIG_SINCE_SCI21, 1, MAP_CALL(DoAudioWaitForPlay), "(i)(i)(i)(i)(i)(i)(i)", NULL }, + { SIG_SCI32, 2, MAP_CALL(DoAudioPlay), "(i)(i)(i)(i)(i)(i)(i)", NULL }, + { SIG_SCI32, 3, MAP_CALL(DoAudioStop), "(i)(i)(i)(i)(i)", NULL }, + { SIG_SCI32, 4, MAP_CALL(DoAudioPause), "(i)(i)(i)(i)(i)", NULL }, + { SIG_SCI32, 5, MAP_CALL(DoAudioResume), "(i)(i)(i)(i)(i)", NULL }, + { SIG_SCI32, 6, MAP_CALL(DoAudioPosition), "(i)(i)(i)(i)(i)", NULL }, + { SIG_SCI32, 7, MAP_CALL(DoAudioRate), "(i)", NULL }, + { SIG_SCI32, 8, MAP_CALL(DoAudioVolume), "(i)(i)(i)(i)(i)(i)", NULL }, + { SIG_SCI32, 9, MAP_CALL(DoAudioGetCapability), "", NULL }, + { SIG_SCI32, 10, MAP_CALL(DoAudioBitDepth), "(i)", NULL }, + { SIG_SCI32, 11, MAP_DUMMY(DoAudioDistort), "(i)", NULL }, + { SIG_SCI32, 12, MAP_CALL(DoAudioMixing), "(i)", NULL }, + { SIG_SCI32, 13, MAP_CALL(DoAudioChannels), "(i)", NULL }, + { SIG_SCI32, 14, MAP_CALL(DoAudioPreload), "(i)", NULL }, + { SIG_SINCE_SCI21MID, 15, MAP_CALL(DoAudioFade), "(iiii)(i)(i)", NULL }, + { SIG_SINCE_SCI21MID, 16, MAP_DUMMY(DoAudioFade36), "iiiii(iii)(i)", NULL }, + { SIG_SINCE_SCI21MID, 17, MAP_CALL(DoAudioHasSignal), "", NULL }, + { SIG_SINCE_SCI21MID, 18, MAP_EMPTY(DoAudioCritical), "", NULL }, + { SIG_SINCE_SCI21MID, 19, MAP_CALL(DoAudioSetLoop), "iii(o)", NULL }, + { SIG_SCI3, 20, MAP_DUMMY(DoAudioPan), "", NULL }, + { SIG_SCI3, 21, MAP_DUMMY(DoAudioPanOff), "", NULL }, + SCI_SUBOPENTRY_TERMINATOR +}; +#endif + // version, subId, function-mapping, signature, workarounds static const SciKernelMapSubEntry kGraph_subops[] = { { SIG_SCI32, 1, MAP_CALL(StubNull), "", NULL }, // called by gk1 sci32 right at the start @@ -353,12 +414,12 @@ static const SciKernelMapSubEntry kList_subops[] = { // version, subId, function-mapping, signature, workarounds static const SciKernelMapSubEntry kRemapColors_subops[] = { - { SIG_SCI32, 0, MAP_CALL(RemapOff), "(i)", NULL }, - { SIG_SCI32, 1, MAP_CALL(RemapByRange), "iiii(i)", NULL }, - { SIG_SCI32, 2, MAP_CALL(RemapByPercent), "ii(i)", NULL }, - { SIG_SCI32, 3, MAP_CALL(RemapToGray), "ii(i)", NULL }, - { SIG_SCI32, 4, MAP_CALL(RemapToPercentGray), "iii(i)", NULL }, - { SIG_SCI32, 5, MAP_CALL(RemapSetNoMatchRange), "ii", NULL }, + { SIG_SCI32, 0, MAP_CALL(RemapColorsOff), "(i)", NULL }, + { SIG_SCI32, 1, MAP_CALL(RemapColorsByRange), "iiii(i)", NULL }, + { SIG_SCI32, 2, MAP_CALL(RemapColorsByPercent), "ii(i)", NULL }, + { SIG_SCI32, 3, MAP_CALL(RemapColorsToGray), "ii(i)", NULL }, + { SIG_SCI32, 4, MAP_CALL(RemapColorsToPercentGray), "iii(i)", NULL }, + { SIG_SCI32, 5, MAP_CALL(RemapColorsBlockRange), "ii", NULL }, SCI_SUBOPENTRY_TERMINATOR }; @@ -407,25 +468,30 @@ static const SciKernelMapSubEntry kString_subops[] = { // version, subId, function-mapping, signature, workarounds static const SciKernelMapSubEntry kScrollWindow_subops[] = { { SIG_SCI32, 0, MAP_CALL(ScrollWindowCreate), "oi", NULL }, - { SIG_SCI32, 1, MAP_CALL(ScrollWindowAdd), "o.ii.(.)", NULL }, - { SIG_SCI32, 2, MAP_DUMMY(ScrollWindowClear), "o", NULL }, - { SIG_SCI32, 3, MAP_DUMMY(ScrollWindowPageUp), "o", NULL }, - { SIG_SCI32, 4, MAP_DUMMY(ScrollWindowPageDown), "o", NULL }, - { SIG_SCI32, 5, MAP_DUMMY(ScrollWindowUpArrow), "o", NULL }, - { SIG_SCI32, 6, MAP_DUMMY(ScrollWindowDownArrow), "o", NULL }, - { SIG_SCI32, 7, MAP_DUMMY(ScrollWindowHome), "o", NULL }, - { SIG_SCI32, 8, MAP_DUMMY(ScrollWindowEnd), "o", NULL }, - { SIG_SCI32, 9, MAP_DUMMY(ScrollWindowResize), "o.", NULL }, - { SIG_SCI32, 10, MAP_CALL(ScrollWindowWhere), "oi", NULL }, - { SIG_SCI32, 11, MAP_DUMMY(ScrollWindowGo), "o..", NULL }, - { SIG_SCI32, 12, MAP_DUMMY(ScrollWindowInsert), "o.....", NULL }, - { SIG_SCI32, 13, MAP_DUMMY(ScrollWindowDelete), "o.", NULL }, - { SIG_SCI32, 14, MAP_DUMMY(ScrollWindowModify), "o.....(.)", NULL }, - { SIG_SCI32, 15, MAP_DUMMY(ScrollWindowHide), "o", NULL }, - { SIG_SCI32, 16, MAP_CALL(ScrollWindowShow), "o", NULL }, - { SIG_SCI32, 17, MAP_CALL(ScrollWindowDestroy), "o", NULL }, - { SIG_SCI32, 18, MAP_DUMMY(ScrollWindowText), "o", NULL }, - { SIG_SCI32, 19, MAP_DUMMY(ScrollWindowReconstruct), "o.", NULL }, + { SIG_SCI32, 1, MAP_CALL(ScrollWindowAdd), "iriii(i)", kScrollWindowAdd_workarounds }, + { SIG_SCI32, 2, MAP_DUMMY(ScrollWindowClear), "i", NULL }, + { SIG_SCI32, 3, MAP_CALL(ScrollWindowPageUp), "i", NULL }, + { SIG_SCI32, 4, MAP_CALL(ScrollWindowPageDown), "i", NULL }, + { SIG_SCI32, 5, MAP_CALL(ScrollWindowUpArrow), "i", NULL }, + { SIG_SCI32, 6, MAP_CALL(ScrollWindowDownArrow), "i", NULL }, + { SIG_SCI32, 7, MAP_CALL(ScrollWindowHome), "i", NULL }, + { SIG_SCI32, 8, MAP_CALL(ScrollWindowEnd), "i", NULL }, + { SIG_SCI32, 9, MAP_DUMMY(ScrollWindowResize), "i.", NULL }, + { SIG_SCI32, 10, MAP_CALL(ScrollWindowWhere), "ii", NULL }, + { SIG_SCI32, 11, MAP_CALL(ScrollWindowGo), "i..", NULL }, + { SIG_SCI32, 12, MAP_DUMMY(ScrollWindowInsert), "i.....", NULL }, + { SIG_SCI32, 13, MAP_DUMMY(ScrollWindowDelete), "i.", NULL }, + { SIG_SCI32, 14, MAP_CALL(ScrollWindowModify), "iiriii(i)", NULL }, + { SIG_SCI32, 15, MAP_CALL(ScrollWindowHide), "i", NULL }, + { SIG_SCI32, 16, MAP_CALL(ScrollWindowShow), "i", NULL }, + { SIG_SCI32, 17, MAP_CALL(ScrollWindowDestroy), "i", NULL }, + // LSL6hires uses kScrollWindowText and kScrollWindowReconstruct to try to save + // and restore the content of the game's subtitle window, but this feature did not + // use the normal save/load functionality of the engine and was actually broken + // (all text formatting was missing on restore). Since there is no real reason to + // save the subtitle scrollback anyway, we just ignore calls to these two functions. + { SIG_SCI32, 18, MAP_EMPTY(ScrollWindowText), "i", NULL }, + { SIG_SCI32, 19, MAP_EMPTY(ScrollWindowReconstruct), "i.", NULL }, SCI_SUBOPENTRY_TERMINATOR }; @@ -484,7 +550,10 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(DisposeList), SIG_EVERYWHERE, "l", NULL, NULL }, { MAP_CALL(DisposeScript), SIG_EVERYWHERE, "i(i*)", NULL, kDisposeScript_workarounds }, { MAP_CALL(DisposeWindow), SIG_EVERYWHERE, "i(i)", NULL, NULL }, - { MAP_CALL(DoAudio), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop + { MAP_CALL(DoAudio), SCI_VERSION_NONE, SCI_VERSION_2, SIGFOR_ALL, "i(.*)", NULL, NULL }, // subop +#ifdef ENABLE_SCI32 + { "DoAudio", kDoAudio32, SIG_SINCE_SCI21, SIGFOR_ALL, "(.*)", kDoAudio_subops, NULL }, +#endif { MAP_CALL(DoAvoider), SIG_EVERYWHERE, "o(i)", NULL, NULL }, { MAP_CALL(DoBresen), SIG_EVERYWHERE, "o", NULL, NULL }, { MAP_CALL(DoSound), SIG_EVERYWHERE, "i(.*)", kDoSound_subops, NULL }, @@ -565,9 +634,9 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(PriCoord), SIG_EVERYWHERE, "i", NULL, NULL }, { MAP_CALL(Random), SIG_EVERYWHERE, "i(i)(i)", NULL, NULL }, { MAP_CALL(ReadNumber), SIG_EVERYWHERE, "r", NULL, kReadNumber_workarounds }, - { "RemapColors", kRemapColors16, SIG_SCI11, SIGFOR_ALL, "i(i)(i)(i)(i)", NULL, NULL }, + { MAP_CALL(RemapColors), SIG_SCI11, SIGFOR_ALL, "i(i)(i)(i)(i)", NULL, NULL }, #ifdef ENABLE_SCI32 - { MAP_CALL(RemapColors), SIG_SCI32, SIGFOR_ALL, "i(i)(i)(i)(i)(i)", kRemapColors_subops, NULL }, + { "RemapColors", kRemapColors32, SIG_SCI32, SIGFOR_ALL, "i(i)(i)(i)(i)(i)", kRemapColors_subops, NULL }, #endif { MAP_CALL(ResCheck), SIG_EVERYWHERE, "ii(iiii)", NULL, NULL }, { MAP_CALL(RespondsTo), SIG_EVERYWHERE, ".i", NULL, NULL }, @@ -659,6 +728,8 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(ListEachElementDo), SIG_EVERYWHERE, "li(.*)", NULL, NULL }, { MAP_CALL(ListFirstTrue), SIG_EVERYWHERE, "li(.*)", NULL, NULL }, { MAP_CALL(ListIndexOf), SIG_EVERYWHERE, "l[o0]", NULL, NULL }, + // kMessageBox is used only by KQ7 1.51 + { MAP_CALL(MessageBox), SIG_SCI32, SIGFOR_ALL, "rri", NULL, NULL }, { "OnMe", kIsOnMe, SIG_EVERYWHERE, "iioi", NULL, NULL }, // Purge is used by the memory manager in SSCI to ensure that X number of bytes (the so called "unmovable // memory") are available when the current room changes. This is similar to the SCI0-SCI1.1 FlushResources @@ -736,9 +807,12 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(SetFontRes), SIG_SCI21EARLY, SIGFOR_ALL, "ii", NULL, NULL }, { MAP_CALL(Font), SIG_SINCE_SCI21MID, SIGFOR_ALL, "i(.*)", kFont_subops, NULL }, { MAP_CALL(Bitmap), SIG_EVERYWHERE, "(.*)", kBitmap_subops, NULL }, - { MAP_CALL(AddLine), SIG_EVERYWHERE, "oiiiiiiiii", NULL, NULL }, - { MAP_CALL(UpdateLine), SIG_EVERYWHERE, "[r0]oiiiiiiiii", NULL, NULL }, - { MAP_CALL(DeleteLine), SIG_EVERYWHERE, "[r0]o", NULL, NULL }, + { MAP_CALL(AddLine), SIG_EVERYWHERE, "oiiii(iiiii)", NULL, NULL }, + // The first argument is a ScreenItem instance ID that is created by the + // engine, not the VM; as a result, in ScummVM, this argument looks like + // an integer and not an object, although it is an object reference. + { MAP_CALL(UpdateLine), SIG_EVERYWHERE, "ioiiii(iiiii)", NULL, NULL }, + { MAP_CALL(DeleteLine), SIG_EVERYWHERE, "io", NULL, NULL }, // SCI2.1 Empty Functions diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp index 335763a35f..d604bb85d0 100644 --- a/engines/sci/engine/kfile.cpp +++ b/engines/sci/engine/kfile.cpp @@ -806,25 +806,47 @@ reg_t kSaveGame(EngineState *s, int argc, reg_t *argv) { return NULL_REG; } else if (virtualId < SAVEGAMEID_OFFICIALRANGE_START) { // virtualId is low, we assume that scripts expect us to create new slot - if (g_sci->getGameId() == GID_JONES) { + switch (g_sci->getGameId()) { + case GID_JONES: // Jones has one save slot only savegameId = 0; - } else if (virtualId == s->_lastSaveVirtualId) { - // if last virtual id is the same as this one, we assume that caller wants to overwrite last save - savegameId = s->_lastSaveNewId; - } else { - uint savegameNr; - // savegameId is in lower range, scripts expect us to create a new slot - for (savegameId = SAVEGAMESLOT_FIRST; savegameId <= SAVEGAMESLOT_LAST; savegameId++) { - for (savegameNr = 0; savegameNr < saves.size(); savegameNr++) { - if (savegameId == saves[savegameNr].id) + break; + case GID_FANMADE: { + // Fanmade game, try to identify the game + const char *gameName = g_sci->getGameObjectName(); + + if (strcmp(gameName, "CascadeQuest") == 0) { + // Cascade Quest calls us directly to auto-save and uses slot 99, + // put that save into slot 0 (ScummVM auto-save slot) see bug #7007 + if (virtualId == (SAVEGAMEID_OFFICIALRANGE_START - 1)) { + savegameId = 0; + } + } + break; + } + default: + break; + } + + if (savegameId < 0) { + // savegameId not set yet + if (virtualId == s->_lastSaveVirtualId) { + // if last virtual id is the same as this one, we assume that caller wants to overwrite last save + savegameId = s->_lastSaveNewId; + } else { + uint savegameNr; + // savegameId is in lower range, scripts expect us to create a new slot + for (savegameId = SAVEGAMESLOT_FIRST; savegameId <= SAVEGAMESLOT_LAST; savegameId++) { + for (savegameNr = 0; savegameNr < saves.size(); savegameNr++) { + if (savegameId == saves[savegameNr].id) + break; + } + if (savegameNr == saves.size()) // Slot not found, seems to be good to go break; } - if (savegameNr == saves.size()) // Slot not found, seems to be good to go - break; + if (savegameId > SAVEGAMESLOT_LAST) + error("kSavegame: no more savegame slots available"); } - if (savegameId > SAVEGAMESLOT_LAST) - error("kSavegame: no more savegame slots available"); } } else { error("kSaveGame: invalid savegameId used"); diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp index 73236b98ed..cae5a09789 100644 --- a/engines/sci/engine/kgraphics.cpp +++ b/engines/sci/engine/kgraphics.cpp @@ -1258,7 +1258,7 @@ reg_t kShow(EngineState *s, int argc, reg_t *argv) { } // Early variant of the SCI32 kRemapColors kernel function, used in the demo of QFG4 -reg_t kRemapColors16(EngineState *s, int argc, reg_t *argv) { +reg_t kRemapColors(EngineState *s, int argc, reg_t *argv) { uint16 operation = argv[0].toUint16(); switch (operation) { diff --git a/engines/sci/engine/kgraphics32.cpp b/engines/sci/engine/kgraphics32.cpp index b140062ca0..019a06930c 100644 --- a/engines/sci/engine/kgraphics32.cpp +++ b/engines/sci/engine/kgraphics32.cpp @@ -52,7 +52,9 @@ #include "sci/graphics/controls32.h" #include "sci/graphics/font.h" // TODO: remove once kBitmap is moved in a separate class #include "sci/graphics/frameout.h" +#include "sci/graphics/paint32.h" #include "sci/graphics/palette32.h" +#include "sci/graphics/remap32.h" #include "sci/graphics/text32.h" #endif @@ -253,6 +255,10 @@ reg_t kWinHelp(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } +reg_t kMessageBox(EngineState *s, int argc, reg_t *argv) { + return g_sci->_gfxControls32->kernelMessageBox(s->_segMan->getString(argv[0]), s->_segMan->getString(argv[1]), argv[2].toUint16()); +} + /** * Causes an immediate plane transition with an optional transition * effect @@ -359,140 +365,138 @@ reg_t kScrollWindow(EngineState *s, int argc, reg_t *argv) { } reg_t kScrollWindowCreate(EngineState *s, int argc, reg_t *argv) { - debug("kScrollWindowCreate"); - kStub(s, argc, argv); - return argv[0]; + const reg_t object = argv[0]; + const uint16 maxNumEntries = argv[1].toUint16(); + + SegManager *segMan = s->_segMan; + const int16 borderColor = readSelectorValue(segMan, object, SELECTOR(borderColor)); + const TextAlign alignment = (TextAlign)readSelectorValue(segMan, object, SELECTOR(mode)); + const GuiResourceId fontId = (GuiResourceId)readSelectorValue(segMan, object, SELECTOR(font)); + const int16 backColor = readSelectorValue(segMan, object, SELECTOR(back)); + const int16 foreColor = readSelectorValue(segMan, object, SELECTOR(fore)); + const reg_t plane = readSelector(segMan, object, SELECTOR(plane)); + + Common::Rect rect; + rect.left = readSelectorValue(segMan, object, SELECTOR(nsLeft)); + rect.top = readSelectorValue(segMan, object, SELECTOR(nsTop)); + rect.right = readSelectorValue(segMan, object, SELECTOR(nsRight)) + 1; + rect.bottom = readSelectorValue(segMan, object, SELECTOR(nsBottom)) + 1; + const Common::Point position(rect.left, rect.top); + + return g_sci->_gfxControls32->makeScrollWindow(rect, position, plane, foreColor, backColor, fontId, alignment, borderColor, maxNumEntries); } reg_t kScrollWindowAdd(EngineState *s, int argc, reg_t *argv) { - debug("kScrollWindowAdd"); - return kStubNull(s, argc, argv); + ScrollWindow *scrollWindow = g_sci->_gfxControls32->getScrollWindow(argv[0]); + + const Common::String text = s->_segMan->getString(argv[1]); + const GuiResourceId fontId = argv[2].toSint16(); + const int16 color = argv[3].toSint16(); + const TextAlign alignment = (TextAlign)argv[4].toSint16(); + const bool scrollTo = argc > 5 ? (bool)argv[5].toUint16() : true; + + return scrollWindow->add(text, fontId, color, alignment, scrollTo); } reg_t kScrollWindowWhere(EngineState *s, int argc, reg_t *argv) { - debug("kScrollWindowWhere"); - return kStubNull(s, argc, argv); + ScrollWindow *scrollWindow = g_sci->_gfxControls32->getScrollWindow(argv[0]); + + const uint16 where = (argv[1].toUint16() * scrollWindow->where()).toInt(); + + return make_reg(0, where); +} + +reg_t kScrollWindowGo(EngineState *s, int argc, reg_t *argv) { + ScrollWindow *scrollWindow = g_sci->_gfxControls32->getScrollWindow(argv[0]); + + const Ratio scrollTop(argv[1].toSint16(), argv[2].toSint16()); + scrollWindow->go(scrollTop); + + return s->r_acc; +} + +reg_t kScrollWindowModify(EngineState *s, int argc, reg_t *argv) { + ScrollWindow *scrollWindow = g_sci->_gfxControls32->getScrollWindow(argv[0]); + + const reg_t entryId = argv[1]; + const Common::String newText = s->_segMan->getString(argv[2]); + const GuiResourceId fontId = argv[3].toSint16(); + const int16 color = argv[4].toSint16(); + const TextAlign alignment = (TextAlign)argv[5].toSint16(); + const bool scrollTo = argc > 6 ? (bool)argv[6].toUint16() : true; + + return scrollWindow->modify(entryId, newText, fontId, color, alignment, scrollTo); +} + +reg_t kScrollWindowHide(EngineState *s, int argc, reg_t *argv) { + ScrollWindow *scrollWindow = g_sci->_gfxControls32->getScrollWindow(argv[0]); + + scrollWindow->hide(); + + return s->r_acc; } reg_t kScrollWindowShow(EngineState *s, int argc, reg_t *argv) { - debug("kScrollWindowShow"); - return kStubNull(s, argc, argv); + ScrollWindow *scrollWindow = g_sci->_gfxControls32->getScrollWindow(argv[0]); + + scrollWindow->show(); + + return s->r_acc; } -reg_t kScrollWindowDestroy(EngineState *s, int argc, reg_t *argv) { - debug("kScrollWindowDestroy"); - return kStubNull(s, argc, argv); +reg_t kScrollWindowPageUp(EngineState *s, int argc, reg_t *argv) { + ScrollWindow *scrollWindow = g_sci->_gfxControls32->getScrollWindow(argv[0]); + + scrollWindow->pageUp(); + + return s->r_acc; } -#if 0 -reg_t kScrollWindow(EngineState *s, int argc, reg_t *argv) { - // Used by SQ6 and LSL6 hires for the text area in the bottom of the - // screen. The relevant scripts also exist in Phantasmagoria 1, but they're - // unused. This is always called by scripts 64906 (ScrollerWindow) and - // 64907 (ScrollableWindow). - - reg_t kWindow = argv[1]; - uint16 op = argv[0].toUint16(); - switch (op) { - case 0: // Init - // TODO: Init reads the nsLeft, nsTop, nsRight, nsBottom, - // borderColor, fore, back, mode, font, plane selectors - // from the window in argv[1]. - g_sci->_gfxFrameout->initScrollText(argv[2].toUint16()); // maxItems - g_sci->_gfxFrameout->clearScrollTexts(); - return argv[1]; // kWindow - case 1: // Show message, called by ScrollableWindow::addString - case 14: // Modify message, called by ScrollableWindow::modifyString - // TODO: The parameters in Modify are shifted by one: the first - // argument is the handle of the text to modify. The others - // are as Add. - { - Common::String text = s->_segMan->getString(argv[2]); - uint16 x = 0; - uint16 y = 0; - // TODO: argv[3] is font - // TODO: argv[4] is color - // TODO: argv[5] is alignment (0 = left, 1 = center, 2 = right) - // font,color,alignment may also be -1. (Maybe same as previous?) - // TODO: argv[6] is an optional bool, defaulting to true if not present. - // If true, the old contents are scrolled out of view. - // TODO: Return a handle of the inserted text. (Used for modify/insert) - // This handle looks like it should also be usable by kString. - g_sci->_gfxFrameout->addScrollTextEntry(text, kWindow, x, y, (op == 14)); - } - break; - case 2: // Clear, called by ScrollableWindow::erase - g_sci->_gfxFrameout->clearScrollTexts(); - break; - case 3: // Page up, called by ScrollableWindow::scrollTo - // TODO - kStub(s, argc, argv); - break; - case 4: // Page down, called by ScrollableWindow::scrollTo - // TODO - kStub(s, argc, argv); - break; - case 5: // Up arrow, called by ScrollableWindow::scrollTo - g_sci->_gfxFrameout->prevScrollText(); - break; - case 6: // Down arrow, called by ScrollableWindow::scrollTo - g_sci->_gfxFrameout->nextScrollText(); - break; - case 7: // Home, called by ScrollableWindow::scrollTo - g_sci->_gfxFrameout->firstScrollText(); - break; - case 8: // End, called by ScrollableWindow::scrollTo - g_sci->_gfxFrameout->lastScrollText(); - break; - case 9: // Resize, called by ScrollableWindow::resize and ScrollerWindow::resize - // TODO: This reads the nsLeft, nsTop, nsRight, nsBottom - // selectors from the SCI object passed in argv[2]. - kStub(s, argc, argv); - break; - case 10: // Where, called by ScrollableWindow::where - // TODO: - // Gives the current relative scroll location as a fraction - // with argv[2] as the denominator. (Return value is the numerator.) - // Silenced the warnings because of the high amount of console spam - //kStub(s, argc, argv); - break; - case 11: // Go, called by ScrollableWindow::scrollTo - // TODO: - // Two arguments provide a fraction: argv[2] is num., argv[3] is denom. - // Scrolls to the relative location given by the fraction. - kStub(s, argc, argv); - break; - case 12: // Insert, called by ScrollableWindow::insertString - // 5 extra parameters here: - // handle of insert location (new string takes that position). - // text, font, color, alignment - // TODO - kStub(s, argc, argv); - break; - // case 13 (Delete) is handled below - // case 14 (Modify) is handled above - case 15: // Hide, called by ScrollableWindow::hide - g_sci->_gfxFrameout->toggleScrollText(false); - break; - case 16: // Show, called by ScrollableWindow::show - g_sci->_gfxFrameout->toggleScrollText(true); - break; - case 17: // Destroy, called by ScrollableWindow::dispose - g_sci->_gfxFrameout->clearScrollTexts(); - break; - case 13: // Delete, unused - case 18: // Text, unused - case 19: // Reconstruct, unused - error("kScrollWindow: Unused subop %d invoked", op); - break; - default: - error("kScrollWindow: unknown subop %d", op); - break; - } +reg_t kScrollWindowPageDown(EngineState *s, int argc, reg_t *argv) { + ScrollWindow *scrollWindow = g_sci->_gfxControls32->getScrollWindow(argv[0]); + + scrollWindow->pageDown(); + + return s->r_acc; +} + +reg_t kScrollWindowUpArrow(EngineState *s, int argc, reg_t *argv) { + ScrollWindow *scrollWindow = g_sci->_gfxControls32->getScrollWindow(argv[0]); + + scrollWindow->upArrow(); + + return s->r_acc; +} + +reg_t kScrollWindowDownArrow(EngineState *s, int argc, reg_t *argv) { + ScrollWindow *scrollWindow = g_sci->_gfxControls32->getScrollWindow(argv[0]); + + scrollWindow->downArrow(); + + return s->r_acc; +} + +reg_t kScrollWindowHome(EngineState *s, int argc, reg_t *argv) { + ScrollWindow *scrollWindow = g_sci->_gfxControls32->getScrollWindow(argv[0]); + + scrollWindow->home(); + + return s->r_acc; +} + +reg_t kScrollWindowEnd(EngineState *s, int argc, reg_t *argv) { + ScrollWindow *scrollWindow = g_sci->_gfxControls32->getScrollWindow(argv[0]); + + scrollWindow->end(); + + return s->r_acc; +} + +reg_t kScrollWindowDestroy(EngineState *s, int argc, reg_t *argv) { + g_sci->_gfxControls32->destroyScrollWindow(argv[0]); return s->r_acc; } -#endif reg_t kFont(EngineState *s, int argc, reg_t *argv) { if (!s) @@ -546,46 +550,31 @@ reg_t kBitmapDrawLine(EngineState *s, int argc, reg_t *argv) { } reg_t kBitmapDrawView(EngineState *s, int argc, reg_t *argv) { - // viewId, loopNo, celNo, displace x, displace y, unused, view x, view y + BitmapResource bitmap(argv[0]); + CelObjView view(argv[1].toUint16(), argv[2].toSint16(), argv[3].toSint16()); - // called e.g. from TiledBitmap::resize() in Torin's Passage, script 64869 - // The tiled view seems to always have 2 loops. - // These loops need to have 1 cel in loop 0 and 8 cels in loop 1. + const int16 x = argc > 4 ? argv[4].toSint16() : 0; + const int16 y = argc > 5 ? argv[5].toSint16() : 0; + const int16 alignX = argc > 7 ? argv[7].toSint16() : -1; + const int16 alignY = argc > 8 ? argv[8].toSint16() : -1; - return kStubNull(s, argc + 1, argv - 1); + Common::Point position( + x == -1 ? bitmap.getDisplace().x : x, + y == -1 ? bitmap.getDisplace().y : y + ); -#if 0 - // tiled surface - // 6 params, called e.g. from TiledBitmap::resize() in Torin's Passage, - // script 64869 - reg_t hunkId = argv[1]; // obtained from kBitmap(0) - // The tiled view seems to always have 2 loops. - // These loops need to have 1 cel in loop 0 and 8 cels in loop 1. - uint16 viewNum = argv[2].toUint16(); // vTiles selector - uint16 loop = argv[3].toUint16(); - uint16 cel = argv[4].toUint16(); - uint16 x = argv[5].toUint16(); - uint16 y = argv[6].toUint16(); - - byte *memoryPtr = s->_segMan->getHunkPointer(hunkId); - // Get totalWidth, totalHeight - uint16 totalWidth = READ_LE_UINT16(memoryPtr); - uint16 totalHeight = READ_LE_UINT16(memoryPtr + 2); - byte *bitmap = memoryPtr + BITMAP_HEADER_SIZE; - - GfxView *view = g_sci->_gfxCache->getView(viewNum); - uint16 tileWidth = view->getWidth(loop, cel); - uint16 tileHeight = view->getHeight(loop, cel); - const byte *tileBitmap = view->getBitmap(loop, cel); - uint16 width = MIN<uint16>(totalWidth - x, tileWidth); - uint16 height = MIN<uint16>(totalHeight - y, tileHeight); - - for (uint16 curY = 0; curY < height; curY++) { - for (uint16 curX = 0; curX < width; curX++) { - bitmap[(curY + y) * totalWidth + (curX + x)] = tileBitmap[curY * tileWidth + curX]; - } - } -#endif + position.x -= alignX == -1 ? view._displace.x : alignX; + position.y -= alignY == -1 ? view._displace.y : alignY; + + Common::Rect drawRect( + position.x, + position.y, + position.x + view._width, + position.y + view._height + ); + drawRect.clip(Common::Rect(bitmap.getWidth(), bitmap.getHeight())); + view.draw(bitmap.getBuffer(), drawRect, position, view._mirrorX); + return s->r_acc; } reg_t kBitmapDrawText(EngineState *s, int argc, reg_t *argv) { @@ -616,9 +605,8 @@ reg_t kBitmapDrawText(EngineState *s, int argc, reg_t *argv) { textRect.clip(Common::Rect(bitmap.getWidth(), bitmap.getHeight())); reg_t textBitmapObject = g_sci->_gfxText32->createFontBitmap(textRect.width(), textRect.height(), Common::Rect(textRect.width(), textRect.height()), text, foreColor, backColor, skipColor, fontId, alignment, borderColor, dimmed, false); - Buffer bitmapBuffer(bitmap.getWidth(), bitmap.getHeight(), bitmap.getPixels()); CelObjMem textCel(textBitmapObject); - textCel.draw(bitmapBuffer, textRect, Common::Point(textRect.left, textRect.top), false); + textCel.draw(bitmap.getBuffer(), textRect, Common::Point(textRect.left, textRect.top), false); s->_segMan->freeHunkEntry(textBitmapObject); return s->r_acc; @@ -635,8 +623,7 @@ reg_t kBitmapDrawColor(EngineState *s, int argc, reg_t *argv) { argv[4].toSint16() + 1 ); - Buffer buffer(bitmap.getWidth(), bitmap.getHeight(), bitmap.getPixels()); - buffer.fillRect(fillRect, argv[5].toSint16()); + bitmap.getBuffer().fillRect(fillRect, argv[5].toSint16()); return s->r_acc; } @@ -700,46 +687,77 @@ reg_t kEditText(EngineState *s, int argc, reg_t *argv) { } reg_t kAddLine(EngineState *s, int argc, reg_t *argv) { - return kStubNull(s, argc, argv); // return 0:0 for now, so that follow up calls won't create signature mismatches -#if 0 - reg_t plane = argv[0]; - Common::Point startPoint(argv[1].toUint16(), argv[2].toUint16()); - Common::Point endPoint(argv[3].toUint16(), argv[4].toUint16()); - byte priority = (byte)argv[5].toUint16(); - byte color = (byte)argv[6].toUint16(); - byte style = (byte)argv[7].toUint16(); // 0: solid, 1: dashed, 2: pattern - byte pattern = (byte)argv[8].toUint16(); - byte thickness = (byte)argv[9].toUint16(); -// return g_sci->_gfxFrameout->addPlaneLine(plane, startPoint, endPoint, color, priority, 0); - return s->r_acc; -#endif + const reg_t plane = argv[0]; + const Common::Point startPoint(argv[1].toSint16(), argv[2].toSint16()); + const Common::Point endPoint(argv[3].toSint16(), argv[4].toSint16()); + + int16 priority; + uint8 color; + LineStyle style; + uint16 pattern; + uint8 thickness; + + if (argc == 10) { + priority = argv[5].toSint16(); + color = (uint8)argv[6].toUint16(); + style = (LineStyle)argv[7].toSint16(); + pattern = argv[8].toUint16(); + thickness = (uint8)argv[9].toUint16(); + } else { + priority = 1000; + color = 255; + style = kLineStyleSolid; + pattern = 0; + thickness = 1; + } + + return g_sci->_gfxPaint32->kernelAddLine(plane, startPoint, endPoint, priority, color, style, pattern, thickness); } reg_t kUpdateLine(EngineState *s, int argc, reg_t *argv) { - return kStub(s, argc, argv); + const reg_t screenItemObject = argv[0]; + const reg_t planeObject = argv[1]; + const Common::Point startPoint(argv[2].toSint16(), argv[3].toSint16()); + const Common::Point endPoint(argv[4].toSint16(), argv[5].toSint16()); + + int16 priority; + uint8 color; + LineStyle style; + uint16 pattern; + uint8 thickness; + + Plane *plane = g_sci->_gfxFrameout->getPlanes().findByObject(planeObject); + if (plane == nullptr) { + error("kUpdateLine: Plane %04x:%04x not found", PRINT_REG(planeObject)); + } + + ScreenItem *screenItem = plane->_screenItemList.findByObject(screenItemObject); + if (screenItem == nullptr) { + error("kUpdateLine: Screen item %04x:%04x not found", PRINT_REG(screenItemObject)); + } + + if (argc == 11) { + priority = argv[6].toSint16(); + color = (uint8)argv[7].toUint16(); + style = (LineStyle)argv[8].toSint16(); + pattern = argv[9].toUint16(); + thickness = (uint8)argv[10].toUint16(); + } else { + priority = screenItem->_priority; + color = screenItem->_celInfo.color; + style = kLineStyleSolid; + pattern = 0; + thickness = 1; + } + + g_sci->_gfxPaint32->kernelUpdateLine(screenItem, plane, startPoint, endPoint, priority, color, style, pattern, thickness); -#if 0 - reg_t hunkId = argv[0]; - reg_t plane = argv[1]; - Common::Point startPoint(argv[2].toUint16(), argv[3].toUint16()); - Common::Point endPoint(argv[4].toUint16(), argv[5].toUint16()); - // argv[6] is unknown (a number, usually 200) - byte color = (byte)argv[7].toUint16(); - byte priority = (byte)argv[8].toUint16(); - byte control = (byte)argv[9].toUint16(); - // argv[10] is unknown (usually a small number, 1 or 2). Thickness, perhaps? -// g_sci->_gfxFrameout->updatePlaneLine(plane, hunkId, startPoint, endPoint, color, priority, control); return s->r_acc; -#endif } + reg_t kDeleteLine(EngineState *s, int argc, reg_t *argv) { - return kStub(s, argc, argv); -#if 0 - reg_t hunkId = argv[0]; - reg_t plane = argv[1]; -// g_sci->_gfxFrameout->deletePlaneLine(plane, hunkId); + g_sci->_gfxPaint32->kernelDeleteLine(argv[0], argv[1]); return s->r_acc; -#endif } reg_t kSetScroll(EngineState *s, int argc, reg_t *argv) { @@ -908,57 +926,69 @@ reg_t kPalCycle(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } -reg_t kRemapColors(EngineState *s, int argc, reg_t *argv) { +reg_t kRemapColors32(EngineState *s, int argc, reg_t *argv) { if (!s) return make_reg(0, getSciVersion()); error("not supposed to call this"); } -reg_t kRemapOff(EngineState *s, int argc, reg_t *argv) { - byte color = (argc >= 1) ? argv[0].toUint16() : 0; - g_sci->_gfxRemap32->remapOff(color); +reg_t kRemapColorsOff(EngineState *s, int argc, reg_t *argv) { + if (argc == 0) { + g_sci->_gfxRemap32->remapAllOff(); + } else { + const uint8 color = argv[0].toUint16(); + g_sci->_gfxRemap32->remapOff(color); + } return s->r_acc; } -reg_t kRemapByRange(EngineState *s, int argc, reg_t *argv) { - byte color = argv[0].toUint16(); - byte from = argv[1].toUint16(); - byte to = argv[2].toUint16(); - byte base = argv[3].toUint16(); - // The last parameter, depth, is unused - g_sci->_gfxRemap32->setRemappingRange(color, from, to, base); +reg_t kRemapColorsByRange(EngineState *s, int argc, reg_t *argv) { + const uint8 color = argv[0].toUint16(); + const int16 from = argv[1].toSint16(); + const int16 to = argv[2].toSint16(); + const int16 base = argv[3].toSint16(); + // NOTE: There is an optional last parameter after `base` + // which was only used by the priority map debugger, which + // does not exist in release versions of SSCI + g_sci->_gfxRemap32->remapByRange(color, from, to, base); return s->r_acc; } -reg_t kRemapByPercent(EngineState *s, int argc, reg_t *argv) { - byte color = argv[0].toUint16(); - byte percent = argv[1].toUint16(); - // The last parameter, depth, is unused - g_sci->_gfxRemap32->setRemappingPercent(color, percent); +reg_t kRemapColorsByPercent(EngineState *s, int argc, reg_t *argv) { + const uint8 color = argv[0].toUint16(); + const int16 percent = argv[1].toSint16(); + // NOTE: There is an optional last parameter after `percent` + // which was only used by the priority map debugger, which + // does not exist in release versions of SSCI + g_sci->_gfxRemap32->remapByPercent(color, percent); return s->r_acc; } -reg_t kRemapToGray(EngineState *s, int argc, reg_t *argv) { - byte color = argv[0].toUint16(); - byte gray = argv[1].toUint16(); - // The last parameter, depth, is unused - g_sci->_gfxRemap32->setRemappingToGray(color, gray); +reg_t kRemapColorsToGray(EngineState *s, int argc, reg_t *argv) { + const uint8 color = argv[0].toUint16(); + const int16 gray = argv[1].toSint16(); + // NOTE: There is an optional last parameter after `gray` + // which was only used by the priority map debugger, which + // does not exist in release versions of SSCI + g_sci->_gfxRemap32->remapToGray(color, gray); return s->r_acc; } -reg_t kRemapToPercentGray(EngineState *s, int argc, reg_t *argv) { - byte color = argv[0].toUint16(); - byte gray = argv[1].toUint16(); - byte percent = argv[2].toUint16(); - // The last parameter, depth, is unused - g_sci->_gfxRemap32->setRemappingToPercentGray(color, gray, percent); +reg_t kRemapColorsToPercentGray(EngineState *s, int argc, reg_t *argv) { + const uint8 color = argv[0].toUint16(); + const int16 gray = argv[1].toSint16(); + const int16 percent = argv[2].toSint16(); + // NOTE: There is an optional last parameter after `percent` + // which was only used by the priority map debugger, which + // does not exist in release versions of SSCI + g_sci->_gfxRemap32->remapToPercentGray(color, gray, percent); return s->r_acc; } -reg_t kRemapSetNoMatchRange(EngineState *s, int argc, reg_t *argv) { - byte from = argv[0].toUint16(); - byte count = argv[1].toUint16(); - g_sci->_gfxRemap32->setNoMatchRange(from, count); +reg_t kRemapColorsBlockRange(EngineState *s, int argc, reg_t *argv) { + const uint8 from = argv[0].toUint16(); + const uint8 count = argv[1].toUint16(); + g_sci->_gfxRemap32->blockRange(from, count); return s->r_acc; } diff --git a/engines/sci/engine/kpathing.cpp b/engines/sci/engine/kpathing.cpp index 7ac744f584..06f16299aa 100644 --- a/engines/sci/engine/kpathing.cpp +++ b/engines/sci/engine/kpathing.cpp @@ -326,7 +326,7 @@ static void draw_line(EngineState *s, Common::Point p1, Common::Point p2, int ty p2.y = CLIP<int16>(p2.y, 0, height - 1); assert(type >= 0 && type <= 3); - g_sci->_gfxPaint->kernelGraphDrawLine(p1, p2, poly_colors[type], 255, 255); + g_sci->_gfxPaint16->kernelGraphDrawLine(p1, p2, poly_colors[type], 255, 255); } static void draw_point(EngineState *s, Common::Point p, int start, int width, int height) { diff --git a/engines/sci/engine/ksound.cpp b/engines/sci/engine/ksound.cpp index 398a623286..ed53b8d52f 100644 --- a/engines/sci/engine/ksound.cpp +++ b/engines/sci/engine/ksound.cpp @@ -26,7 +26,11 @@ #include "sci/engine/kernel.h" #include "sci/engine/vm.h" // for Object #include "sci/sound/audio.h" +#ifdef ENABLE_SCI32 +#include "sci/sound/audio32.h" +#endif #include "sci/sound/soundcmd.h" +#include "sci/sound/sync.h" #include "audio/mixer.h" #include "common/system.h" @@ -46,7 +50,6 @@ reg_t kDoSound(EngineState *s, int argc, reg_t *argv) { CREATE_DOSOUND_FORWARD(DoSoundInit) CREATE_DOSOUND_FORWARD(DoSoundPlay) -CREATE_DOSOUND_FORWARD(DoSoundRestore) CREATE_DOSOUND_FORWARD(DoSoundDispose) CREATE_DOSOUND_FORWARD(DoSoundMute) CREATE_DOSOUND_FORWARD(DoSoundStop) @@ -61,13 +64,41 @@ CREATE_DOSOUND_FORWARD(DoSoundUpdateCues) CREATE_DOSOUND_FORWARD(DoSoundSendMidi) CREATE_DOSOUND_FORWARD(DoSoundGlobalReverb) CREATE_DOSOUND_FORWARD(DoSoundSetHold) -CREATE_DOSOUND_FORWARD(DoSoundDummy) CREATE_DOSOUND_FORWARD(DoSoundGetAudioCapability) CREATE_DOSOUND_FORWARD(DoSoundSuspend) CREATE_DOSOUND_FORWARD(DoSoundSetVolume) CREATE_DOSOUND_FORWARD(DoSoundSetPriority) CREATE_DOSOUND_FORWARD(DoSoundSetLoop) +#ifdef ENABLE_SCI32 +reg_t kDoSoundPhantasmagoriaMac(EngineState *s, int argc, reg_t *argv) { + // Phantasmagoria Mac (and seemingly no other game (!)) uses this + // cutdown version of kDoSound. + + switch (argv[0].toUint16()) { + case 0: + return g_sci->_soundCmd->kDoSoundMasterVolume(argc - 1, argv + 1, s->r_acc); + case 2: + return g_sci->_soundCmd->kDoSoundInit(argc - 1, argv + 1, s->r_acc); + case 3: + return g_sci->_soundCmd->kDoSoundDispose(argc - 1, argv + 1, s->r_acc); + case 4: + return g_sci->_soundCmd->kDoSoundPlay(argc - 1, argv + 1, s->r_acc); + case 5: + return g_sci->_soundCmd->kDoSoundStop(argc - 1, argv + 1, s->r_acc); + case 8: + return g_sci->_soundCmd->kDoSoundSetVolume(argc - 1, argv + 1, s->r_acc); + case 9: + return g_sci->_soundCmd->kDoSoundSetLoop(argc - 1, argv + 1, s->r_acc); + case 10: + return g_sci->_soundCmd->kDoSoundUpdateCues(argc - 1, argv + 1, s->r_acc); + } + + error("Unknown kDoSound Phantasmagoria Mac subop %d", argv[0].toUint16()); + return s->r_acc; +} +#endif + reg_t kDoCdAudio(EngineState *s, int argc, reg_t *argv) { switch (argv[0].toUint16()) { case kSciAudioPlay: { @@ -113,7 +144,8 @@ reg_t kDoCdAudio(EngineState *s, int argc, reg_t *argv) { } /** - * Used for speech playback and digital soundtracks in CD games + * Used for speech playback and digital soundtracks in CD games. + * This is the SCI16 version; SCI32 is handled separately. */ reg_t kDoAudio(EngineState *s, int argc, reg_t *argv) { // JonesCD uses different functions based on the cdaudio.map file @@ -184,14 +216,6 @@ reg_t kDoAudio(EngineState *s, int argc, reg_t *argv) { int16 volume = argv[1].toUint16(); volume = CLIP<int16>(volume, 0, AUDIO_VOLUME_MAX); debugC(kDebugLevelSound, "kDoAudio: set volume to %d", volume); -#ifdef ENABLE_SCI32 - if (getSciVersion() >= SCI_VERSION_2_1_EARLY) { - int16 volumePrev = mixer->getVolumeForSoundType(Audio::Mixer::kSpeechSoundType) / 2; - volumePrev = CLIP<int16>(volumePrev, 0, AUDIO_VOLUME_MAX); - mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, volume * 2); - return make_reg(0, volumePrev); - } else -#endif mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, volume * 2); break; } @@ -232,12 +256,6 @@ reg_t kDoAudio(EngineState *s, int argc, reg_t *argv) { if (getSciVersion() <= SCI_VERSION_1_1) { debugC(kDebugLevelSound, "kDoAudio: CD audio subop"); return kDoCdAudio(s, argc - 1, argv + 1); -#ifdef ENABLE_SCI32 - } else { - // TODO: This isn't CD Audio in SCI32 anymore - warning("kDoAudio: Unhandled case 10, %d extra arguments passed", argc - 1); - break; -#endif } // 3 new subops in Pharkas CD (including CD demo). kDoAudio in Pharkas sits at seg026:038C @@ -286,14 +304,12 @@ reg_t kDoAudio(EngineState *s, int argc, reg_t *argv) { } reg_t kDoSync(EngineState *s, int argc, reg_t *argv) { - SegManager *segMan = s->_segMan; switch (argv[0].toUint16()) { case kSciAudioSyncStart: { ResourceId id; - g_sci->_audio->stopSoundSync(); + g_sci->_sync->stop(); - // Load sound sync resource and lock it if (argc == 3) { id = ResourceId(kResourceTypeSync, argv[2].toUint16()); } else if (argc == 7) { @@ -304,14 +320,14 @@ reg_t kDoSync(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } - g_sci->_audio->setSoundSync(id, argv[1], segMan); + g_sci->_sync->start(id, argv[1]); break; } case kSciAudioSyncNext: - g_sci->_audio->doSoundSync(argv[1], segMan); + g_sci->_sync->next(argv[1]); break; case kSciAudioSyncStop: - g_sci->_audio->stopSoundSync(); + g_sci->_sync->stop(); break; default: error("DoSync: Unhandled subfunction %d", argv[0].toUint16()); @@ -321,6 +337,155 @@ reg_t kDoSync(EngineState *s, int argc, reg_t *argv) { } #ifdef ENABLE_SCI32 +reg_t kDoAudio32(EngineState *s, int argc, reg_t *argv) { + if (!s) + return make_reg(0, getSciVersion()); + error("not supposed to call this"); +} + +reg_t kDoAudioInit(EngineState *s, int argc, reg_t *argv) { + return make_reg(0, 0); +} + +reg_t kDoAudioWaitForPlay(EngineState *s, int argc, reg_t *argv) { + return g_sci->_audio32->kernelPlay(false, argc, argv); +} + +reg_t kDoAudioPlay(EngineState *s, int argc, reg_t *argv) { + return g_sci->_audio32->kernelPlay(true, argc, argv); +} + +reg_t kDoAudioStop(EngineState *s, int argc, reg_t *argv) { + const int16 channelIndex = g_sci->_audio32->findChannelByArgs(argc, argv, 0, argc > 1 ? argv[1] : NULL_REG); + return make_reg(0, g_sci->_audio32->stop(channelIndex)); +} + +reg_t kDoAudioPause(EngineState *s, int argc, reg_t *argv) { + const int16 channelIndex = g_sci->_audio32->findChannelByArgs(argc, argv, 0, argc > 1 ? argv[1] : NULL_REG); + return make_reg(0, g_sci->_audio32->pause(channelIndex)); +} + +reg_t kDoAudioResume(EngineState *s, int argc, reg_t *argv) { + const int16 channelIndex = g_sci->_audio32->findChannelByArgs(argc, argv, 0, argc > 1 ? argv[1] : NULL_REG); + return make_reg(0, g_sci->_audio32->resume(channelIndex)); +} + +reg_t kDoAudioPosition(EngineState *s, int argc, reg_t *argv) { + const int16 channelIndex = g_sci->_audio32->findChannelByArgs(argc, argv, 0, argc > 1 ? argv[1] : NULL_REG); + return make_reg(0, g_sci->_audio32->getPosition(channelIndex)); +} + +reg_t kDoAudioRate(EngineState *s, int argc, reg_t *argv) { + // NOTE: In the original engine this would set the hardware + // DSP sampling rate; ScummVM mixer does not need this, so + // we only store the value to satisfy engine compatibility. + + if (argc > 0) { + const uint16 sampleRate = argv[0].toUint16(); + if (sampleRate != 0) { + g_sci->_audio32->setSampleRate(sampleRate); + } + } + + return make_reg(0, g_sci->_audio32->getSampleRate()); +} + +reg_t kDoAudioVolume(EngineState *s, int argc, reg_t *argv) { + const int16 volume = argc > 0 ? argv[0].toSint16() : -1; + const int16 channelIndex = g_sci->_audio32->findChannelByArgs(argc, argv, 1, argc > 2 ? argv[2] : NULL_REG); + + if (volume != -1) { + g_sci->_audio32->setVolume(channelIndex, volume); + } + + return make_reg(0, g_sci->_audio32->getVolume(channelIndex)); +} + +reg_t kDoAudioGetCapability(EngineState *s, int argc, reg_t *argv) { + return make_reg(0, 1); +} + +reg_t kDoAudioBitDepth(EngineState *s, int argc, reg_t *argv) { + // NOTE: In the original engine this would set the hardware + // DSP bit depth; ScummVM mixer does not need this, so + // we only store the value to satisfy engine compatibility. + + if (argc > 0) { + const uint16 bitDepth = argv[0].toUint16(); + if (bitDepth != 0) { + g_sci->_audio32->setBitDepth(bitDepth); + } + } + + return make_reg(0, g_sci->_audio32->getBitDepth()); +} + +reg_t kDoAudioMixing(EngineState *s, int argc, reg_t *argv) { + if (argc > 0) { + g_sci->_audio32->setAttenuatedMixing(argv[0].toUint16()); + } + + return make_reg(0, g_sci->_audio32->getAttenuatedMixing()); +} + +reg_t kDoAudioChannels(EngineState *s, int argc, reg_t *argv) { + // NOTE: In the original engine this would set the hardware + // DSP stereo output; ScummVM mixer does not need this, so + // we only store the value to satisfy engine compatibility. + + if (argc > 0) { + const int16 numChannels = argv[0].toSint16(); + if (numChannels != 0) { + g_sci->_audio32->setNumOutputChannels(numChannels); + } + } + + return make_reg(0, g_sci->_audio32->getNumOutputChannels()); +} + +reg_t kDoAudioPreload(EngineState *s, int argc, reg_t *argv) { + // NOTE: In the original engine this would cause audio + // data for new channels to be preloaded to memory when + // the channel was initialized; we do not need this, so + // we only store the value to satisfy engine compatibility. + + if (argc > 0) { + g_sci->_audio32->setPreload(argv[0].toUint16()); + } + + return make_reg(0, g_sci->_audio32->getPreload()); +} + +reg_t kDoAudioFade(EngineState *s, int argc, reg_t *argv) { + if (argc < 4) { + return make_reg(0, 0); + } + + // NOTE: Sierra did a nightmarish hack here, temporarily replacing + // the argc of the kernel arguments with 2 and then restoring it + // after findChannelByArgs was called. + const int16 channelIndex = g_sci->_audio32->findChannelByArgs(2, argv, 0, argc > 5 ? argv[5] : NULL_REG); + + const int16 volume = argv[1].toSint16(); + const int16 speed = argv[2].toSint16(); + const int16 steps = argv[3].toSint16(); + const bool stopAfterFade = argc > 4 ? (bool)argv[4].toUint16() : false; + + return make_reg(0, g_sci->_audio32->fadeChannel(channelIndex, volume, speed, steps, stopAfterFade)); +} + +reg_t kDoAudioHasSignal(EngineState *s, int argc, reg_t *argv) { + return make_reg(0, g_sci->_audio32->hasSignal()); +} + +reg_t kDoAudioSetLoop(EngineState *s, int argc, reg_t *argv) { + const int16 channelIndex = g_sci->_audio32->findChannelByArgs(argc, argv, 0, argc == 3 ? argv[2] : NULL_REG); + + const bool loop = argv[0].toSint16() != 0 && argv[0].toSint16() != 1; + + g_sci->_audio32->setLoop(channelIndex, loop); + return s->r_acc; +} reg_t kSetLanguage(EngineState *s, int argc, reg_t *argv) { // This is used by script 90 of MUMG Deluxe from the main menu to toggle @@ -335,33 +500,6 @@ reg_t kSetLanguage(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } -reg_t kDoSoundPhantasmagoriaMac(EngineState *s, int argc, reg_t *argv) { - // Phantasmagoria Mac (and seemingly no other game (!)) uses this - // cutdown version of kDoSound. - - switch (argv[0].toUint16()) { - case 0: - return g_sci->_soundCmd->kDoSoundMasterVolume(argc - 1, argv + 1, s->r_acc); - case 2: - return g_sci->_soundCmd->kDoSoundInit(argc - 1, argv + 1, s->r_acc); - case 3: - return g_sci->_soundCmd->kDoSoundDispose(argc - 1, argv + 1, s->r_acc); - case 4: - return g_sci->_soundCmd->kDoSoundPlay(argc - 1, argv + 1, s->r_acc); - case 5: - return g_sci->_soundCmd->kDoSoundStop(argc - 1, argv + 1, s->r_acc); - case 8: - return g_sci->_soundCmd->kDoSoundSetVolume(argc - 1, argv + 1, s->r_acc); - case 9: - return g_sci->_soundCmd->kDoSoundSetLoop(argc - 1, argv + 1, s->r_acc); - case 10: - return g_sci->_soundCmd->kDoSoundUpdateCues(argc - 1, argv + 1, s->r_acc); - } - - error("Unknown kDoSound Phantasmagoria Mac subop %d", argv[0].toUint16()); - return s->r_acc; -} - #endif } // End of namespace Sci diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp index 302f046458..0972aec4a4 100644 --- a/engines/sci/engine/savegame.cpp +++ b/engines/sci/engine/savegame.cpp @@ -48,8 +48,9 @@ #include "sci/sound/music.h" #ifdef ENABLE_SCI32 -#include "sci/graphics/palette32.h" #include "sci/graphics/frameout.h" +#include "sci/graphics/palette32.h" +#include "sci/graphics/remap32.h" #endif namespace Sci { @@ -807,6 +808,33 @@ void GfxPalette32::saveLoadWithSerializer(Common::Serializer &s) { } } } + +void GfxRemap32::saveLoadWithSerializer(Common::Serializer &s) { + if (s.getVersion() < 35) { + return; + } + + s.syncAsByte(_numActiveRemaps); + s.syncAsByte(_blockedRangeStart); + s.syncAsSint16LE(_blockedRangeCount); + + for (uint i = 0; i < _remaps.size(); ++i) { + SingleRemap &singleRemap = _remaps[i]; + s.syncAsByte(singleRemap._type); + if (s.isLoading() && singleRemap._type != kRemapNone) { + singleRemap.reset(); + } + s.syncAsByte(singleRemap._from); + s.syncAsByte(singleRemap._to); + s.syncAsByte(singleRemap._delta); + s.syncAsByte(singleRemap._percent); + s.syncAsByte(singleRemap._gray); + } + + if (s.isLoading()) { + _needsUpdate = true; + } +} #endif void GfxPorts::saveLoadWithSerializer(Common::Serializer &s) { diff --git a/engines/sci/engine/savegame.h b/engines/sci/engine/savegame.h index 459e992e24..43909accf2 100644 --- a/engines/sci/engine/savegame.h +++ b/engines/sci/engine/savegame.h @@ -37,6 +37,7 @@ struct EngineState; * * Version - new/changed feature * ============================= + * 35 - SCI32 remap * 34 - SCI32 palettes, and store play time in ticks * 33 - new overridePriority flag in MusicEntry * 32 - new playBed flag in MusicEntry @@ -59,7 +60,7 @@ struct EngineState; */ enum { - CURRENT_SAVEGAME_VERSION = 34, + CURRENT_SAVEGAME_VERSION = 35, MINIMUM_SAVEGAME_VERSION = 14 }; diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp index 116ffdd5a2..5d7fa49ce5 100644 --- a/engines/sci/engine/script_patches.cpp +++ b/engines/sci/engine/script_patches.cpp @@ -379,12 +379,40 @@ static const SciScriptPatcherEntry ecoquest2Signatures[] = { }; // =========================================================================== +// Fan-made games +// Attention: Try to make script patches as specific as possible + +// CascadeQuest::autosave in script 994 is called various times to auto-save the game. +// The script use a fixed slot "999" for this purpose. This doesn't work in ScummVM, because we do not let +// scripts save directly into specific slots, but instead use virtual slots / detect scripts wanting to +// create a new slot. +// +// For this game we patch the code to use slot 99 instead. kSaveGame also checks for Cascade Quest, +// will then check, if slot 99 is asked for and will then use the actual slot 0, which is the official +// ScummVM auto-save slot. +// +// Responsible method: CascadeQuest::autosave +// Fixes bug: #7007 +static const uint16 fanmadeSignatureCascadeQuestFixAutoSaving[] = { + SIG_MAGICDWORD, + 0x38, SIG_UINT16(0x03e7), // pushi 3E7 (999d) -> save game slot 999 + 0x74, SIG_UINT16(0x06f8), // lofss "AutoSave" + 0x89, 0x1e, // lsg global[1E] + 0x43, 0x2d, 0x08, // callk SaveGame + SIG_END +}; + +static const uint16 fanmadePatchCascadeQuestFixAutoSaving[] = { + 0x38, PATCH_UINT16((SAVEGAMEID_OFFICIALRANGE_START - 1)), // fix slot + PATCH_END +}; + // EventHandler::handleEvent in Demo Quest has a bug, and it jumps to the // wrong address when an incorrect word is typed, therefore leading to an // infinite loop. This script bug was not apparent in SSCI, probably because // event handling was slightly different there, so it was never discovered. // Fixes bug: #5120 -static const uint16 fanmadeSignatureInfiniteLoop[] = { +static const uint16 fanmadeSignatureDemoQuestInfiniteLoop[] = { 0x38, SIG_UINT16(0x004c), // pushi 004c 0x39, 0x00, // pushi 00 0x87, 0x01, // lap 01 @@ -395,15 +423,16 @@ static const uint16 fanmadeSignatureInfiniteLoop[] = { SIG_END }; -static const uint16 fanmadePatchInfiniteLoop[] = { +static const uint16 fanmadePatchDemoQuestInfiniteLoop[] = { PATCH_ADDTOOFFSET(+10), - 0x30, SIG_UINT16(0x0032), // bnt 0032 [06a8] --> pushi 004c + 0x30, PATCH_UINT16(0x0032), // bnt 0032 [06a8] --> pushi 004c PATCH_END }; -// script, description, signature patch +// script, description, signature patch static const SciScriptPatcherEntry fanmadeSignatures[] = { - { true, 999, "infinite loop on typo", 1, fanmadeSignatureInfiniteLoop, fanmadePatchInfiniteLoop }, + { true, 994, "Cascade Quest: fix auto-saving", 1, fanmadeSignatureCascadeQuestFixAutoSaving, fanmadePatchCascadeQuestFixAutoSaving }, + { true, 999, "Demo Quest: infinite loop on typo", 1, fanmadeSignatureDemoQuestInfiniteLoop, fanmadePatchDemoQuestInfiniteLoop }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -1654,9 +1683,160 @@ static const uint16 laurabow1PatchEasterEggViewFix[] = { PATCH_END }; +// When oiling the armor or opening the visor of the armor, the scripts +// first check if Laura/ego is near the armor and if she is not, they will move her +// to the armor. After that further code is executed. +// +// The current location is checked by a ego::inRect() call. +// +// The given rect for the inRect call inside openVisor::changeState was made larger for Atari ST/Amiga versions. +// We change the PC version to use the same rect. +// +// Additionally the coordinate, that Laura is moved to, is 152, 107 and may not be reachable depending on where +// Laura/ego was, when "use oil on helmet of armor" / "open visor of armor" got entered. +// Bad coordinates are for example 82, 110, which then cause collisions and effectively an endless loop. +// Game will effectively "freeze" and the user is only able to restore a previous game. +// This also happened, when using the original interpreter. +// We change the destination coordinate to 152, 110, which seems to be reachable all the time. +// +// The following patch fixes the rect for the PC version of the game. +// +// Applies to at least: English PC Floppy +// Responsible method: openVisor::changeState (script 37) +// Fixes bug: #7119 +static const uint16 laurabow1SignatureArmorOpenVisorFix[] = { + 0x39, 0x04, // pushi 04 + SIG_MAGICDWORD, + 0x39, 0x6a, // pushi 6a (106d) + 0x38, SIG_UINT16(0x96), // pushi 0096 (150d) + 0x39, 0x6c, // pushi 6c (108d) + 0x38, SIG_UINT16(0x98), // pushi 0098 (152d) + SIG_END +}; + +static const uint16 laurabow1PatchArmorOpenVisorFix[] = { + PATCH_ADDTOOFFSET(+2), + 0x39, 0x68, // pushi 68 (104d) (-2) + 0x38, SIG_UINT16(0x94), // pushi 0094 (148d) (-2) + 0x39, 0x6f, // pushi 6f (111d) (+3) + 0x38, SIG_UINT16(0x9a), // pushi 009a (154d) (+2) + PATCH_END +}; + +// This here fixes the destination coordinate (exact details are above). +// +// Applies to at least: English PC Floppy, English Atari ST Floppy, English Amiga Floppy +// Responsible method: openVisor::changeState, oiling::changeState (script 37) +// Fixes bug: #7119 +static const uint16 laurabow1SignatureArmorMoveToFix[] = { + SIG_MAGICDWORD, + 0x36, // push + 0x39, 0x6b, // pushi 6B (107d) + 0x38, SIG_UINT16(0x0098), // pushi 98 (152d) + 0x7c, // pushSelf + 0x81, 0x00, // lag global[0] + SIG_END +}; + +static const uint16 laurabow1PatchArmorMoveToFix[] = { + PATCH_ADDTOOFFSET(+1), + 0x39, 0x6e, // pushi 6E (110d) - adjust x, so that no collision can occur anymore + PATCH_END +}; + +// In some cases like for example when the player oils the arm of the armor, command input stays +// disabled, even when the player exits fast enough, so that Laura doesn't die. +// +// This is caused by the scripts only enabling control (directional movement), but do not enable command input as well. +// +// This bug also happens, when using the original interpreter. +// And it was fixed for the Atari ST + Amiga versions of the game. +// +// Applies to at least: English PC Floppy +// Responsible method: 2nd subroutine in script 37, called by oiling::changeState(7) +// Fixes bug: #7154 +static const uint16 laurabow1SignatureArmorOilingArmFix[] = { + 0x38, SIG_UINT16(0x0089), // pushi 89h + 0x76, // push0 + SIG_MAGICDWORD, + 0x72, SIG_UINT16(0x1a5c), // lofsa "Can" - offsets are not skipped to make sure only the PC version gets patched + 0x4a, 0x04, // send 04 + 0x38, SIG_UINT16(0x0089), // pushi 89h + 0x76, // push0 + 0x72, SIG_UINT16(0x19a1), // lofsa "Visor" + 0x4a, 0x04, // send 04 + 0x38, SIG_UINT16(0x0089), // pushi 89h + 0x76, // push0 + 0x72, SIG_UINT16(0x194a), // lofsa "note" + 0x4a, 0x04, // send 04 + 0x38, SIG_UINT16(0x0089), // pushi 89h + 0x76, // push0 + 0x72, SIG_UINT16(0x18f3), // lofsa "valve" + 0x4a, 0x04, // send 04 + 0x8b, 0x34, // lsl local[34h] + 0x35, 0x02, // ldi 02 + 0x1c, // ne? + 0x30, SIG_UINT16(0x0014), // bnt [to ret] + 0x8b, 0x34, // lsl local[34h] + 0x35, 0x05, // ldi 05 + 0x1c, // ne? + 0x30, SIG_UINT16(0x000c), // bnt [to ret] + 0x8b, 0x34, // lsl local[34h] + 0x35, 0x06, // ldi 06 + 0x1c, // ne? + 0x30, SIG_UINT16(0x0004), // bnt [to ret] + // followed by code to call script 0 export to re-enable controls and call setMotion + SIG_END +}; + +static const uint16 laurabow1PatchArmorOilingArmFix[] = { + PATCH_ADDTOOFFSET(+3), // skip over pushi 89h + 0x3c, // dup + 0x3c, // dup + 0x3c, // dup + // saves a total of 6 bytes + 0x76, // push0 + 0x72, SIG_UINT16(0x1a59), // lofsa "Can" + 0x4a, 0x04, // send 04 + 0x76, // push0 + 0x72, SIG_UINT16(0x19a1), // lofsa "Visor" + 0x4a, 0x04, // send 04 + 0x76, // push0 + 0x72, SIG_UINT16(0x194d), // lofsa "note" + 0x4a, 0x04, // send 04 + 0x76, // push0 + 0x72, SIG_UINT16(0x18f9), // lofsa "valve" 18f3 + 0x4a, 0x04, // send 04 + // new code to enable input as well, needs 9 spare bytes + 0x38, SIG_UINT16(0x00e2), // canInput + 0x78, // push1 + 0x78, // push1 + 0x51, 0x2b, // class User + 0x4a, 0x06, // send 06 -> call User::canInput(1) + // original code, but changed a bit to save some more bytes + 0x8b, 0x34, // lsl local[34h] + 0x35, 0x02, // ldi 02 + 0x04, // sub + 0x31, 0x12, // bnt [to ret] + 0x36, // push + 0x35, 0x03, // ldi 03 + 0x04, // sub + 0x31, 0x0c, // bnt [to ret] + 0x78, // push1 + 0x1a, // eq? + 0x2f, 0x08, // bt [to ret] + // saves 7 bytes, we only need 3, so waste 4 bytes + 0x35, 0x00, // ldi 0 + 0x35, 0x00, // ldi 0 + PATCH_END +}; + // script, description, signature patch static const SciScriptPatcherEntry laurabow1Signatures[] = { - { true, 4, "easter egg view fix", 1, laurabow1SignatureEasterEggViewFix, laurabow1PatchEasterEggViewFix }, + { true, 4, "easter egg view fix", 1, laurabow1SignatureEasterEggViewFix, laurabow1PatchEasterEggViewFix }, + { true, 37, "armor open visor fix", 1, laurabow1SignatureArmorOpenVisorFix, laurabow1PatchArmorOpenVisorFix }, + { true, 37, "armor move to fix", 2, laurabow1SignatureArmorMoveToFix, laurabow1PatchArmorMoveToFix }, + { true, 37, "allowing input, after oiling arm", 1, laurabow1SignatureArmorOilingArmFix, laurabow1PatchArmorOilingArmFix }, SCI_SIGNATUREENTRY_TERMINATOR }; @@ -2761,7 +2941,7 @@ static const uint16 qfg3PatchImportDialog[] = { // Teller::doChild. We jump to this call of hero::solvePuzzle to get that same // behaviour. // Applies to at least: English, German, Italian, French, Spanish Floppy -// Responsible method: unknown +// Responsible method: uhuraTell::doChild // Fixes bug: #5172 static const uint16 qfg3SignatureWooDialog[] = { SIG_MAGICDWORD, @@ -3514,24 +3694,35 @@ static const uint16 sq1vgaSignatureSpiderDroidTiming[] = { 0x30, SIG_UINT16(0x0005), // bnt [further method code] 0x35, 0x00, // ldi 00 0x32, SIG_UINT16(0x0052), // jmp [super-call] - 0x89, 0xa6, // lsg global[a6] + 0x89, 0xa6, // lsg global[a6] <-- flag gets set to 1 when ego went up the skeleton tail, when going down it's set to 2 0x35, 0x01, // ldi 01 0x1a, // eq? - 0x30, SIG_UINT16(0x0012), // bnt [2nd code], in case global A6 <> 1 + 0x30, SIG_UINT16(0x0012), // bnt [PChase set code], in case global A6 <> 1 0x81, 0xb5, // lag global[b5] - 0x30, SIG_UINT16(0x000d), // bnt [2nd code], in case global B5 == 0 + 0x30, SIG_UINT16(0x000d), // bnt [PChase set code], in case global B5 == 0 0x38, SIG_UINT16(0x008c), // pushi 008c 0x78, // push1 0x72, SIG_UINT16(0x1cb6), // lofsa 1CB6 (moveToPath) 0x36, // push 0x54, 0x06, // self 06 0x32, SIG_UINT16(0x0038), // jmp [super-call] + // PChase set call 0x81, 0xb5, // lag global[B5] 0x18, // not 0x30, SIG_UINT16(0x0032), // bnt [super-call], in case global B5 <> 0 + // followed by: + // is spider in current room + // is global A6h == 2? -> set PChase SIG_END }; // 58 bytes) +// Global A6h <> 1 (did NOT went up the skeleton) +// Global B5h = 0 -> set PChase +// Global B5h <> 0 -> do not do anything +// Global A6h = 1 (did went up the skeleton) +// Global B5h = 0 -> set PChase +// Global B5h <> 0 -> set moveToPath + static const uint16 sq1vgaPatchSpiderDroidTiming[] = { 0x63, 0x4e, // pToa script 0x2f, 0x68, // bt [super-call] @@ -3556,8 +3747,8 @@ static const uint16 sq1vgaPatchSpiderDroidTiming[] = { 0x65, 0x4c, // aTop cycleSpeed 0x65, 0x5e, // aTop moveSpeed // new code end - 0x89, 0xb5, // lsg global[B5] - 0x31, 0x13, // bnt [2nd code chunk] + 0x81, 0xb5, // lag global[B5] + 0x31, 0x13, // bnt [PChase code chunk] 0x89, 0xa6, // lsg global[A6] 0x35, 0x01, // ldi 01 0x1a, // eq? diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp index 0cb8ff48d7..073bb93983 100644 --- a/engines/sci/engine/workarounds.cpp +++ b/engines/sci/engine/workarounds.cpp @@ -514,6 +514,14 @@ const SciWorkaroundEntry kDisposeScript_workarounds[] = { }; // gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +const SciWorkaroundEntry kDoSoundPlay_workarounds[] = { + { GID_LSL6HIRES, -1, 64989, 0, NULL, "play", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // always passes an extra null argument + { GID_QFG4, -1, 64989, 0, NULL, "play", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // always passes an extra null argument + { GID_PQ4, -1, 64989, 0, NULL, "play", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // always passes an extra null argument + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kDoSoundFade_workarounds[] = { { GID_KQ5, 213, 989, 0, "globalSound3", "fade", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // english floppy: when bandits leave the secret temple, parameter 4 is an object - bug #5078 { GID_KQ6, 105, 989, 0, "globalSound", "fade", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // floppy: during intro, parameter 4 is an object @@ -745,6 +753,12 @@ const SciWorkaroundEntry kUnLoad_workarounds[] = { SCI_WORKAROUNDENTRY_TERMINATOR }; +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +const SciWorkaroundEntry kScrollWindowAdd_workarounds[] = { + { GID_PHANTASMAGORIA, 45, 64907, 0, "ScrollableWindow", "addString", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // ScrollWindow interface passes the last two parameters twice +}; + + SciWorkaroundSolution trackOriginAndFindWorkaround(int index, const SciWorkaroundEntry *workaroundList, SciTrackOriginReply *trackOrigin) { // HACK for SCI3: Temporarily ignore this if (getSciVersion() == SCI_VERSION_3) { diff --git a/engines/sci/engine/workarounds.h b/engines/sci/engine/workarounds.h index 8f519a8c9c..248d37fc6c 100644 --- a/engines/sci/engine/workarounds.h +++ b/engines/sci/engine/workarounds.h @@ -74,6 +74,7 @@ extern const SciWorkaroundEntry kDeviceInfo_workarounds[]; extern const SciWorkaroundEntry kDisplay_workarounds[]; extern const SciWorkaroundEntry kDirLoop_workarounds[]; extern const SciWorkaroundEntry kDisposeScript_workarounds[]; +extern const SciWorkaroundEntry kDoSoundPlay_workarounds[]; extern const SciWorkaroundEntry kDoSoundFade_workarounds[]; extern const SciWorkaroundEntry kFindKey_workarounds[]; extern const SciWorkaroundEntry kDeleteKey_workarounds[]; @@ -98,6 +99,7 @@ extern const SciWorkaroundEntry kStrAt_workarounds[]; extern const SciWorkaroundEntry kStrCpy_workarounds[]; extern const SciWorkaroundEntry kStrLen_workarounds[]; extern const SciWorkaroundEntry kUnLoad_workarounds[]; +extern const SciWorkaroundEntry kScrollWindowAdd_workarounds[]; extern SciWorkaroundSolution trackOriginAndFindWorkaround(int index, const SciWorkaroundEntry *workaroundList, SciTrackOriginReply *trackOrigin); |