aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/engine
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sci/engine')
-rw-r--r--engines/sci/engine/features.cpp40
-rw-r--r--engines/sci/engine/features.h8
-rw-r--r--engines/sci/engine/kernel.cpp762
-rw-r--r--engines/sci/engine/kernel.h61
-rw-r--r--engines/sci/engine/kernel32.cpp932
-rw-r--r--engines/sci/engine/kernel_tables.h1030
-rw-r--r--engines/sci/engine/kevent.cpp18
-rw-r--r--engines/sci/engine/kfile.cpp568
-rw-r--r--engines/sci/engine/kgraphics.cpp589
-rw-r--r--engines/sci/engine/klists.cpp210
-rw-r--r--engines/sci/engine/kmath.cpp25
-rw-r--r--engines/sci/engine/kmisc.cpp35
-rw-r--r--engines/sci/engine/kmovement.cpp28
-rw-r--r--engines/sci/engine/kparse.cpp9
-rw-r--r--engines/sci/engine/kpathing.cpp28
-rw-r--r--engines/sci/engine/kscripts.cpp14
-rw-r--r--engines/sci/engine/ksound.cpp2
-rw-r--r--engines/sci/engine/kstring.cpp191
-rw-r--r--engines/sci/engine/kvideo.cpp300
-rw-r--r--engines/sci/engine/message.cpp9
-rw-r--r--engines/sci/engine/savegame.cpp153
-rw-r--r--engines/sci/engine/savegame.h4
-rw-r--r--engines/sci/engine/script.cpp18
-rw-r--r--engines/sci/engine/scriptdebug.cpp43
-rw-r--r--engines/sci/engine/seg_manager.cpp5
-rw-r--r--engines/sci/engine/seg_manager.h2
-rw-r--r--engines/sci/engine/selector.cpp11
-rw-r--r--engines/sci/engine/selector.h7
-rw-r--r--engines/sci/engine/state.cpp13
-rw-r--r--engines/sci/engine/static_selectors.cpp14
-rw-r--r--engines/sci/engine/vm.cpp616
-rw-r--r--engines/sci/engine/vm.h3
-rw-r--r--engines/sci/engine/workarounds.cpp357
-rw-r--r--engines/sci/engine/workarounds.h100
34 files changed, 3530 insertions, 2675 deletions
diff --git a/engines/sci/engine/features.cpp b/engines/sci/engine/features.cpp
index fee6e69da7..315c86c56c 100644
--- a/engines/sci/engine/features.cpp
+++ b/engines/sci/engine/features.cpp
@@ -38,6 +38,7 @@ GameFeatures::GameFeatures(SegManager *segMan, Kernel *kernel) : _segMan(segMan)
_doSoundType = SCI_VERSION_NONE;
_lofsType = SCI_VERSION_NONE;
_gfxFunctionsType = SCI_VERSION_NONE;
+ _messageFunctionType = SCI_VERSION_NONE;
_moveCountType = kMoveCountUninitialized;
#ifdef ENABLE_SCI32
@@ -140,6 +141,10 @@ SciVersion GameFeatures::detectDoSoundType() {
// This game is using early SCI0 sound code (different headers than
// SCI0 late)
_doSoundType = SCI_VERSION_0_EARLY;
+#ifdef ENABLE_SCI32
+ } else if (getSciVersion() >= SCI_VERSION_2_1) {
+ _doSoundType = SCI_VERSION_2_1;
+#endif
} else if (SELECTOR(nodePtr) == -1) {
// No nodePtr selector, so this game is definitely using newer
// SCI0 sound code (i.e. SCI_VERSION_0_LATE)
@@ -403,6 +408,41 @@ SciVersion GameFeatures::detectGfxFunctionsType() {
return _gfxFunctionsType;
}
+SciVersion GameFeatures::detectMessageFunctionType() {
+ if (_messageFunctionType != SCI_VERSION_NONE)
+ return _messageFunctionType;
+
+ if (getSciVersion() > SCI_VERSION_1_1) {
+ _messageFunctionType = SCI_VERSION_1_1;
+ return _messageFunctionType;
+ } else if (getSciVersion() < SCI_VERSION_1_1) {
+ _messageFunctionType = SCI_VERSION_1_LATE;
+ return _messageFunctionType;
+ }
+
+ Common::List<ResourceId> *resources = g_sci->getResMan()->listResources(kResourceTypeMessage, -1);
+
+ if (resources->empty()) {
+ // No messages found, so this doesn't really matter anyway...
+ _messageFunctionType = SCI_VERSION_1_1;
+ return _messageFunctionType;
+ }
+
+ Resource *res = g_sci->getResMan()->findResource(*resources->begin(), false);
+ assert(res);
+
+ // Only v2 Message resources use the kGetMessage kernel function.
+ // v3-v5 use the kMessage kernel function.
+
+ if (READ_SCI11ENDIAN_UINT32(res->data) / 1000 == 2)
+ _messageFunctionType = SCI_VERSION_1_LATE;
+ else
+ _messageFunctionType = SCI_VERSION_1_1;
+
+ debugC(1, kDebugLevelVM, "Detected message function type: %s", getSciVersionDesc(_messageFunctionType));
+ return _messageFunctionType;
+}
+
#ifdef ENABLE_SCI32
bool GameFeatures::autoDetectSci21KernelType() {
// First, check if the Sound object is loaded
diff --git a/engines/sci/engine/features.h b/engines/sci/engine/features.h
index 5b383746d8..167c207437 100644
--- a/engines/sci/engine/features.h
+++ b/engines/sci/engine/features.h
@@ -66,6 +66,12 @@ public:
* @return Graphics functions type, SCI_VERSION_0_EARLY / SCI_VERSION_0_LATE
*/
SciVersion detectGfxFunctionsType();
+
+ /**
+ * Autodetects the message function used
+ * @return Message function type, SCI_VERSION_1_LATE / SCI_VERSION_1_1
+ */
+ SciVersion detectMessageFunctionType();
#ifdef ENABLE_SCI32
/**
@@ -105,7 +111,7 @@ private:
bool autoDetectSci21KernelType();
#endif
- SciVersion _doSoundType, _setCursorType, _lofsType, _gfxFunctionsType;
+ SciVersion _doSoundType, _setCursorType, _lofsType, _gfxFunctionsType, _messageFunctionType;
#ifdef ENABLE_SCI32
SciVersion _sci21KernelType;
#endif
diff --git a/engines/sci/engine/kernel.cpp b/engines/sci/engine/kernel.cpp
index beb1d3ce35..d76199c794 100644
--- a/engines/sci/engine/kernel.cpp
+++ b/engines/sci/engine/kernel.cpp
@@ -27,619 +27,15 @@
#include "sci/engine/kernel.h"
#include "sci/event.h"
#include "sci/resource.h"
+#include "sci/engine/features.h"
+#include "sci/engine/kernel_tables.h"
#include "sci/engine/state.h"
+#include "sci/engine/workarounds.h"
#include "common/system.h"
namespace Sci {
-// Uncompiled kernel signatures are formed from a string of letters.
-// each corresponding to a type of a parameter (see below).
-// Use small letters to indicate end of sum type.
-// Use capital letters for sum types, e.g.
-// "LNoLr" for a function which takes two arguments:
-// (1) list, node or object
-// (2) list or ref
-#define KSIG_SPEC_LIST 'l'
-#define KSIG_SPEC_NODE 'n'
-#define KSIG_SPEC_OBJECT 'o'
-#define KSIG_SPEC_REF 'r' // Said Specs and strings
-#define KSIG_SPEC_ARITHMETIC 'i'
-#define KSIG_SPEC_NULL 'z'
-#define KSIG_SPEC_ANY '.'
-#define KSIG_SPEC_ELLIPSIS '*' // Arbitrarily more TYPED arguments
-
-#define KSIG_SPEC_SUM_DONE ('a' - 'A')
-
-
-
-/** Default kernel name table. */
-static const char *s_defaultKernelNames[] = {
- /*0x00*/ "Load",
- /*0x01*/ "UnLoad",
- /*0x02*/ "ScriptID",
- /*0x03*/ "DisposeScript",
- /*0x04*/ "Clone",
- /*0x05*/ "DisposeClone",
- /*0x06*/ "IsObject",
- /*0x07*/ "RespondsTo",
- /*0x08*/ "DrawPic",
- /*0x09*/ "Dummy", // Show
- /*0x0a*/ "PicNotValid",
- /*0x0b*/ "Animate",
- /*0x0c*/ "SetNowSeen",
- /*0x0d*/ "NumLoops",
- /*0x0e*/ "NumCels",
- /*0x0f*/ "CelWide",
- /*0x10*/ "CelHigh",
- /*0x11*/ "DrawCel",
- /*0x12*/ "AddToPic",
- /*0x13*/ "NewWindow",
- /*0x14*/ "GetPort",
- /*0x15*/ "SetPort",
- /*0x16*/ "DisposeWindow",
- /*0x17*/ "DrawControl",
- /*0x18*/ "HiliteControl",
- /*0x19*/ "EditControl",
- /*0x1a*/ "TextSize",
- /*0x1b*/ "Display",
- /*0x1c*/ "GetEvent",
- /*0x1d*/ "GlobalToLocal",
- /*0x1e*/ "LocalToGlobal",
- /*0x1f*/ "MapKeyToDir",
- /*0x20*/ "DrawMenuBar",
- /*0x21*/ "MenuSelect",
- /*0x22*/ "AddMenu",
- /*0x23*/ "DrawStatus",
- /*0x24*/ "Parse",
- /*0x25*/ "Said",
- /*0x26*/ "SetSynonyms", // Portrait (KQ6 hires)
- /*0x27*/ "HaveMouse",
- /*0x28*/ "SetCursor",
- // FOpen (SCI0)
- // FPuts (SCI0)
- // FGets (SCI0)
- // FClose (SCI0)
- /*0x29*/ "SaveGame",
- /*0x2a*/ "RestoreGame",
- /*0x2b*/ "RestartGame",
- /*0x2c*/ "GameIsRestarting",
- /*0x2d*/ "DoSound",
- /*0x2e*/ "NewList",
- /*0x2f*/ "DisposeList",
- /*0x30*/ "NewNode",
- /*0x31*/ "FirstNode",
- /*0x32*/ "LastNode",
- /*0x33*/ "EmptyList",
- /*0x34*/ "NextNode",
- /*0x35*/ "PrevNode",
- /*0x36*/ "NodeValue",
- /*0x37*/ "AddAfter",
- /*0x38*/ "AddToFront",
- /*0x39*/ "AddToEnd",
- /*0x3a*/ "FindKey",
- /*0x3b*/ "DeleteKey",
- /*0x3c*/ "Random",
- /*0x3d*/ "Abs",
- /*0x3e*/ "Sqrt",
- /*0x3f*/ "GetAngle",
- /*0x40*/ "GetDistance",
- /*0x41*/ "Wait",
- /*0x42*/ "GetTime",
- /*0x43*/ "StrEnd",
- /*0x44*/ "StrCat",
- /*0x45*/ "StrCmp",
- /*0x46*/ "StrLen",
- /*0x47*/ "StrCpy",
- /*0x48*/ "Format",
- /*0x49*/ "GetFarText",
- /*0x4a*/ "ReadNumber",
- /*0x4b*/ "BaseSetter",
- /*0x4c*/ "DirLoop",
- /*0x4d*/ "CanBeHere", // CantBeHere in newer SCI versions
- /*0x4e*/ "OnControl",
- /*0x4f*/ "InitBresen",
- /*0x50*/ "DoBresen",
- /*0x51*/ "Platform", // DoAvoider (SCI0)
- /*0x52*/ "SetJump",
- /*0x53*/ "SetDebug",
- /*0x54*/ "Dummy", // InspectObj
- /*0x55*/ "Dummy", // ShowSends
- /*0x56*/ "Dummy", // ShowObjs
- /*0x57*/ "Dummy", // ShowFree
- /*0x58*/ "MemoryInfo",
- /*0x59*/ "Dummy", // StackUsage
- /*0x5a*/ "Dummy", // Profiler
- /*0x5b*/ "GetMenu",
- /*0x5c*/ "SetMenu",
- /*0x5d*/ "GetSaveFiles",
- /*0x5e*/ "GetCWD",
- /*0x5f*/ "CheckFreeSpace",
- /*0x60*/ "ValidPath",
- /*0x61*/ "CoordPri",
- /*0x62*/ "StrAt",
- /*0x63*/ "DeviceInfo",
- /*0x64*/ "GetSaveDir",
- /*0x65*/ "CheckSaveGame",
- /*0x66*/ "ShakeScreen",
- /*0x67*/ "FlushResources",
- /*0x68*/ "SinMult",
- /*0x69*/ "CosMult",
- /*0x6a*/ "SinDiv",
- /*0x6b*/ "CosDiv",
- /*0x6c*/ "Graph",
- /*0x6d*/ "Joystick",
- // End of kernel function table for SCI0
- /*0x6e*/ "Dummy", // ShiftScreen
- /*0x6f*/ "Palette",
- /*0x70*/ "MemorySegment",
- /*0x71*/ "Intersections", // MoveCursor (SCI1 late), PalVary (SCI1.1)
- /*0x72*/ "Memory",
- /*0x73*/ "Dummy", // ListOps
- /*0x74*/ "FileIO",
- /*0x75*/ "DoAudio",
- /*0x76*/ "DoSync",
- /*0x77*/ "AvoidPath",
- /*0x78*/ "Sort", // StrSplit (SCI01)
- /*0x79*/ "Dummy", // ATan
- /*0x7a*/ "Lock",
- /*0x7b*/ "StrSplit",
- /*0x7c*/ "GetMessage", // Message (SCI1.1)
- /*0x7d*/ "IsItSkip",
- /*0x7e*/ "MergePoly",
- /*0x7f*/ "ResCheck",
- /*0x80*/ "AssertPalette",
- /*0x81*/ "TextColors",
- /*0x82*/ "TextFonts",
- /*0x83*/ "Dummy", // Record
- /*0x84*/ "Dummy", // PlayBack
- /*0x85*/ "ShowMovie",
- /*0x86*/ "SetVideoMode",
- /*0x87*/ "SetQuitStr",
- /*0x88*/ "Dummy" // DbugStr
-};
-
-reg_t kStub(EngineState *s, int argc, reg_t *argv) {
- Kernel *kernel = g_sci->getKernel();
- int kernelCallNr = -1;
-
- Common::List<ExecStack>::iterator callIterator = s->_executionStack.end();
- if (callIterator != s->_executionStack.begin()) {
- callIterator--;
- ExecStack lastCall = *callIterator;
- kernelCallNr = lastCall.debugSelector;
- }
-
- Common::String warningMsg = "Dummy function k" + kernel->getKernelName(kernelCallNr) +
- Common::String::printf("[%x]", kernelCallNr) +
- " invoked. Params: " +
- Common::String::printf("%d", argc) + " (";
-
- for (int i = 0; i < argc; i++) {
- warningMsg += Common::String::printf("%04x:%04x", PRINT_REG(argv[i]));
- warningMsg += (i == argc - 1 ? ")" : ", ");
- }
-
- warning("%s", warningMsg.c_str());
- return s->r_acc;
-}
-
-reg_t kStubNull(EngineState *s, int argc, reg_t *argv) {
- kStub(s, argc, argv);
- return NULL_REG;
-}
-
-reg_t kDummy(EngineState *s, int argc, reg_t *argv) {
- kStub(s, argc, argv);
- error("Kernel function was called, which was considered to be unused - see log for details");
-}
-
-// [io] -> either integer or object
-// (io) -> optionally integer AND an object
-// (i) -> optional integer
-// . -> any type
-// i* -> optional multiple integers
-// .* -> any parameters afterwards (or none)
-
-// gameID, scriptNr,lvl, object-name, method-name, call, index, replace
-static const SciWorkaroundEntry kAbs_workarounds[] = {
- { GID_HOYLE1, 1, 0, "room1", "doit", -1, 0, { 2, 0x3e9 } }, // crazy eights - called with objects instead of integers
- { GID_HOYLE1, 2, 0, "room2", "doit", -1, 0, { 2, 0x3e9 } }, // old maid - called with objects instead of integers
- { GID_HOYLE1, 3, 0, "room3", "doit", -1, 0, { 2, 0x3e9 } }, // hearts - called with objects instead of integers
- SCI_WORKAROUNDENTRY_TERMINATOR
-};
-
-// gameID, scriptNr,lvl, object-name, method-name, call, index, replace
-static const SciWorkaroundEntry kDisposeScript_workarounds[] = {
- { GID_QFG1, 64, 0, "rm64", "dispose", -1, 0, { 1, 0 } }, // when leaving graveyard, parameter 0 is an object
- SCI_WORKAROUNDENTRY_TERMINATOR
-};
-
-// gameID, scriptNr,lvl, object-name, method-name, call, index, replace
-static const SciWorkaroundEntry kDoSoundFade_workarounds[] = {
- { GID_KQ6, 989, 0, "globalSound", "fade", -1, 0, { 0, 0 } }, // during intro, parameter 4 is an object
- SCI_WORKAROUNDENTRY_TERMINATOR
-};
-
-// gameID, scriptNr,lvl, object-name, method-name, call, index, replace
-static const SciWorkaroundEntry kGraphRestoreBox_workarounds[] = {
- { GID_LSL6, 85, 0, "rScroller", "hide", -1, 0, { 0, 0 } }, // happens when restoring (sometimes), same as the one below
- { GID_LSL6, 85, 0, "lScroller", "hide", -1, 0, { 0, 0 } }, // happens when restoring (sometimes), same as the one below
- { GID_LSL6, 86, 0, "LL6Inv", "show", -1, 0, { 0, 0 } }, // happens when restoring, is called with hunk segment, but hunk is not allocated at that time
- // ^^ TODO: check, if this is really a script error or an issue with our restore code
- { GID_LSL6, 86, 0, "LL6Inv", "hide", -1, 0, { 0, 0 } }, // happens during the game, gets called with 1 extra parameter
- SCI_WORKAROUNDENTRY_TERMINATOR
-};
-
-// gameID, scriptNr,lvl, object-name, method-name, call, index, replace
-static const SciWorkaroundEntry kGraphFillBoxForeground_workarounds[] = {
- { GID_LSL6, 0, 0, "LSL6", "hideControls", -1, 0, { 0, 0 } }, // happens when giving the bungee key to merrily - gets called with additional 5th parameter
- SCI_WORKAROUNDENTRY_TERMINATOR
-};
-
-// gameID, scriptNr,lvl, object-name, method-name, call, index, replace
-static const SciWorkaroundEntry kGraphFillBoxAny_workarounds[] = {
- { GID_SQ4, 818, 0, "iconTextSwitch", "show", -1, 0, { 0, 0 } }, // game menu "text/speech" display - parameter 5 is missing, but the right color number is on the stack
- SCI_WORKAROUNDENTRY_TERMINATOR
-};
-
-// gameID, scriptNr,lvl, object-name, method-name, call, index, replace
-static const SciWorkaroundEntry kUnLoad_workarounds[] = {
- { GID_LSL6, 130, 0, "recruitLarryScr", "changeState", -1, 0, { 1, 0 } }, // during intro, a 3rd parameter is passed by accident
- { GID_LSL6, 740, 0, "showCartoon", "changeState", -1, 0, { 1, 0 } }, // during ending, 4 additional parameters are passed by accident
- { GID_SQ1, 303, 0, "slotGuy", "dispose", -1, 0, { 1, 0 } }, // when leaving ulence flats bar, parameter 1 is not passed - script error
- SCI_WORKAROUNDENTRY_TERMINATOR
-};
-
-struct SciKernelMapSubEntry {
- SciVersion fromVersion;
- SciVersion toVersion;
-
- uint16 id;
-
- const char *name;
- KernelFunctionCall *function;
-
- const char *signature;
- const SciWorkaroundEntry *workarounds;
-};
-
-#define SCI_SUBOPENTRY_TERMINATOR { SCI_VERSION_NONE, SCI_VERSION_NONE, 0, NULL, NULL, NULL, NULL }
-
-
-#define SIG_SCIALL SCI_VERSION_NONE, SCI_VERSION_NONE
-#define SIG_SCI0 SCI_VERSION_NONE, SCI_VERSION_01
-#define SIG_SCI1 SCI_VERSION_1_EGA, SCI_VERSION_1_LATE
-#define SIG_SCI11 SCI_VERSION_1_1, SCI_VERSION_1_1
-#define SIG_SCI16 SCI_VERSION_NONE, SCI_VERSION_1_1
-#define SIG_SCI32 SCI_VERSION_2, SCI_VERSION_NONE
-
-// SCI-Sound-Version
-#define SIG_SOUNDSCI0 SCI_VERSION_0_EARLY, SCI_VERSION_0_LATE
-#define SIG_SOUNDSCI1EARLY SCI_VERSION_1_EARLY, SCI_VERSION_1_EARLY
-#define SIG_SOUNDSCI1LATE SCI_VERSION_1_LATE, SCI_VERSION_1_LATE
-
-#define SIGFOR_ALL 0x3f
-#define SIGFOR_DOS 1 << 0
-#define SIGFOR_PC98 1 << 1
-#define SIGFOR_WIN 1 << 2
-#define SIGFOR_MAC 1 << 3
-#define SIGFOR_AMIGA 1 << 4
-#define SIGFOR_ATARI 1 << 5
-#define SIGFOR_PC SIGFOR_DOS|SIGFOR_WIN
-
-#define SIG_EVERYWHERE SIG_SCIALL, SIGFOR_ALL
-
-#define MAP_CALL(_name_) #_name_, k##_name_
-
-// version, subId, function-mapping, signature, workarounds
-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(DoSoundDummy), "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 },
- { SIG_SOUNDSCI0, 6, MAP_CALL(DoSoundPause), "i", NULL },
- { SIG_SOUNDSCI0, 7, MAP_CALL(DoSoundResume), "", NULL },
- { SIG_SOUNDSCI0, 8, MAP_CALL(DoSoundMasterVolume), "(i)", NULL },
- { SIG_SOUNDSCI0, 9, MAP_CALL(DoSoundUpdate), "o", NULL },
- { SIG_SOUNDSCI0, 10, MAP_CALL(DoSoundFade), "o", NULL },
- { SIG_SOUNDSCI0, 11, MAP_CALL(DoSoundGetPolyphony), "", NULL },
- { 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(DoSoundDummy), 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 },
- { SIG_SOUNDSCI1EARLY, 6, MAP_CALL(DoSoundDispose), NULL, NULL },
- { SIG_SOUNDSCI1EARLY, 7, MAP_CALL(DoSoundPlay), "oi", NULL },
- { SIG_SOUNDSCI1EARLY, 8, MAP_CALL(DoSoundStop), NULL, NULL },
- { SIG_SOUNDSCI1EARLY, 9, MAP_CALL(DoSoundPause), "[o0]i", NULL },
- { SIG_SOUNDSCI1EARLY, 10, MAP_CALL(DoSoundFade), "oiiii", NULL },
- { SIG_SOUNDSCI1EARLY, 11, MAP_CALL(DoSoundUpdateCues), "o", NULL },
- { SIG_SOUNDSCI1EARLY, 12, MAP_CALL(DoSoundSendMidi), "oiiii", NULL },
- { SIG_SOUNDSCI1EARLY, 13, MAP_CALL(DoSoundReverb), "oi", NULL },
- { SIG_SOUNDSCI1EARLY, 14, MAP_CALL(DoSoundSetHold), "oi", NULL },
- { SIG_SOUNDSCI1EARLY, 15, MAP_CALL(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(DoSoundDummy), "", NULL },
- { SIG_SOUNDSCI1LATE, 3, MAP_CALL(DoSoundGetPolyphony), NULL, NULL },
- { SIG_SOUNDSCI1LATE, 4, MAP_CALL(DoSoundGetAudioCapability), "", NULL },
- { SIG_SOUNDSCI1LATE, 5, MAP_CALL(DoSoundSuspend), "i", NULL },
- { SIG_SOUNDSCI1LATE, 6, MAP_CALL(DoSoundInit), NULL, NULL },
- { SIG_SOUNDSCI1LATE, 7, MAP_CALL(DoSoundDispose), NULL, NULL },
- { SIG_SOUNDSCI1LATE, 8, MAP_CALL(DoSoundPlay), NULL, NULL },
- { SIG_SOUNDSCI1LATE, 9, MAP_CALL(DoSoundStop), NULL, NULL },
- { 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, 14, MAP_CALL(DoSoundSetVolume), "oi", NULL },
- { SIG_SOUNDSCI1LATE, 15, MAP_CALL(DoSoundSetPriority), "oi", NULL },
- { SIG_SOUNDSCI1LATE, 16, MAP_CALL(DoSoundSetLoop), "oi", NULL },
- { SIG_SOUNDSCI1LATE, 17, MAP_CALL(DoSoundUpdateCues), NULL, NULL },
- { SIG_SOUNDSCI1LATE, 18, MAP_CALL(DoSoundSendMidi), NULL, NULL },
- { SIG_SOUNDSCI1LATE, 19, MAP_CALL(DoSoundReverb), NULL, NULL },
- { SIG_SOUNDSCI1LATE, 20, MAP_CALL(DoSoundUpdate), NULL, NULL },
- SCI_SUBOPENTRY_TERMINATOR
-};
-
-// 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
- { SIG_SCIALL, 2, MAP_CALL(GraphGetColorCount), "", NULL },
- // 3 - set palette via resource
- { SIG_SCIALL, 4, MAP_CALL(GraphDrawLine), "iiiii(i)(i)", NULL },
- // 5 - nop
- // 6 - draw pattern
- { SIG_SCIALL, 7, MAP_CALL(GraphSaveBox), "iiiii", NULL },
- { SIG_SCIALL, 8, MAP_CALL(GraphRestoreBox), "[r0!]", kGraphRestoreBox_workarounds },
- // ^ this may get called with invalid references, we check them within restoreBits() and sierra sci behaves the same
- { SIG_SCIALL, 9, MAP_CALL(GraphFillBoxBackground), "iiii", NULL },
- { SIG_SCIALL, 10, MAP_CALL(GraphFillBoxForeground), "iiii", kGraphFillBoxForeground_workarounds },
- { SIG_SCIALL, 11, MAP_CALL(GraphFillBoxAny), "iiiiii(i)(i)", kGraphFillBoxAny_workarounds },
- { SIG_SCI11, 12, MAP_CALL(GraphUpdateBox), "iiii(i)(r0)", NULL }, // kq6 hires
- { SIG_SCIALL, 12, MAP_CALL(GraphUpdateBox), "iiii(i)", NULL },
- { SIG_SCIALL, 13, MAP_CALL(GraphRedrawBox), "iiii", NULL },
- { SIG_SCIALL, 14, MAP_CALL(GraphAdjustPriority), "ii", NULL },
- { SIG_SCI11, 15, MAP_CALL(GraphSaveUpscaledHiresBox), "iiii", NULL }, // kq6 hires
- SCI_SUBOPENTRY_TERMINATOR
-};
-
-// version, subId, function-mapping, signature, workarounds
-static const SciKernelMapSubEntry kPalVary_subops[] = {
- { 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_SCI32, 8, MAP_CALL(PalVaryUnknown), "", NULL },
- 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", NULL },
- { 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), "i", NULL },
- SCI_SUBOPENTRY_TERMINATOR
-};
-
-struct SciKernelMapEntry {
- const char *name;
- KernelFunctionCall *function;
-
- SciVersion fromVersion;
- SciVersion toVersion;
- byte forPlatform;
-
- const char *signature;
- const SciKernelMapSubEntry *subFunctions;
- const SciWorkaroundEntry *workarounds;
-};
-
-// name, version/platform, signature, sub-signatures, workarounds
-static SciKernelMapEntry s_kernelMap[] = {
- { MAP_CALL(Abs), SIG_EVERYWHERE, "i", NULL, kAbs_workarounds },
- { MAP_CALL(AddAfter), SIG_EVERYWHERE, "lnn", NULL, NULL },
- { MAP_CALL(AddMenu), SIG_EVERYWHERE, "rr", NULL, NULL },
- { MAP_CALL(AddToEnd), SIG_EVERYWHERE, "ln", NULL, NULL },
- { MAP_CALL(AddToFront), SIG_EVERYWHERE, "ln", NULL, NULL },
- { MAP_CALL(AddToPic), SIG_EVERYWHERE, "[il](iiiiii)", NULL, NULL },
- { MAP_CALL(Animate), SIG_EVERYWHERE, "(l0)(i)", NULL, NULL },
- { MAP_CALL(AssertPalette), SIG_EVERYWHERE, "i", NULL, NULL },
- { MAP_CALL(AvoidPath), SIG_EVERYWHERE, "ii(.*)", NULL, NULL },
- { MAP_CALL(BaseSetter), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(CanBeHere), SIG_EVERYWHERE, "o(l)", NULL, NULL },
- { MAP_CALL(CantBeHere), SIG_EVERYWHERE, "o(l)", NULL, NULL },
- { MAP_CALL(CelHigh), SIG_EVERYWHERE, "ii(i)", NULL, NULL },
- { MAP_CALL(CelWide), SIG_EVERYWHERE, "ii(i)", NULL, NULL },
- { MAP_CALL(CheckFreeSpace), SIG_SCI32, SIGFOR_ALL, "r.*", NULL, NULL },
- { MAP_CALL(CheckFreeSpace), SIG_EVERYWHERE, "r", NULL, NULL },
- { MAP_CALL(CheckSaveGame), SIG_EVERYWHERE, ".*", NULL, NULL },
- { MAP_CALL(Clone), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(CoordPri), SIG_EVERYWHERE, "i(i)", NULL, NULL },
- { MAP_CALL(CosDiv), SIG_EVERYWHERE, "ii", NULL, NULL },
- { MAP_CALL(DeleteKey), SIG_EVERYWHERE, "l.", NULL, NULL },
- { MAP_CALL(DeviceInfo), SIG_EVERYWHERE, "i(r)(r)(i)", NULL, NULL }, // subop
- { MAP_CALL(Display), SIG_EVERYWHERE, "[ir]([ir!]*)", NULL, NULL },
- // ^ we allow invalid references here, because kDisplay gets called with those in e.g. pq3 during intro
- // restoreBits() checks and skips invalid handles, so that's fine. Sierra SCI behaved the same
- { MAP_CALL(DirLoop), SIG_EVERYWHERE, "oi", NULL, NULL },
- { MAP_CALL(DisposeClone), SIG_EVERYWHERE, "o", NULL, NULL },
- { 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(DoAvoider), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(DoBresen), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(DoSound), SIG_EVERYWHERE, "i([io])(i)(ii[io])(i)", kDoSound_subops, NULL },
- { MAP_CALL(DoSync), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
- { MAP_CALL(DrawCel), SIG_SCI11, SIGFOR_PC, "iiiii(i)(i)(r0)", NULL, NULL }, // for kq6 hires
- { MAP_CALL(DrawCel), SIG_EVERYWHERE, "iiiii(i)(i)", NULL, NULL },
- { MAP_CALL(DrawControl), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(DrawMenuBar), SIG_EVERYWHERE, "i", NULL, NULL },
- { MAP_CALL(DrawPic), SIG_EVERYWHERE, "i(i)(i)(i)", NULL, NULL },
- { MAP_CALL(DrawStatus), SIG_EVERYWHERE, "[r0](i)(i)", NULL, NULL },
- { MAP_CALL(EditControl), SIG_EVERYWHERE, "[o0][o0]", NULL, NULL },
- { MAP_CALL(Empty), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_CALL(EmptyList), SIG_EVERYWHERE, "l", NULL, NULL },
- { MAP_CALL(FClose), SIG_EVERYWHERE, "i", NULL, NULL },
- { MAP_CALL(FGets), SIG_EVERYWHERE, "rii", NULL, NULL },
- { MAP_CALL(FOpen), SIG_EVERYWHERE, "ri", NULL, NULL },
- { MAP_CALL(FPuts), SIG_EVERYWHERE, "ir", NULL, NULL },
- { MAP_CALL(FileIO), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
- { MAP_CALL(FindKey), SIG_EVERYWHERE, "l.", NULL, NULL },
- { MAP_CALL(FirstNode), SIG_EVERYWHERE, "[l0]", NULL, NULL },
- { MAP_CALL(FlushResources), SIG_EVERYWHERE, "i", NULL, NULL },
- { MAP_CALL(Format), SIG_EVERYWHERE, "r(.*)", NULL, NULL },
- { MAP_CALL(GameIsRestarting), SIG_EVERYWHERE, "(i)", NULL, NULL },
- { MAP_CALL(GetAngle), SIG_EVERYWHERE, "iiii", NULL, NULL },
- // ^^ FIXME - occasionally KQ6 passes a 5th argument by mistake
- { MAP_CALL(GetCWD), SIG_EVERYWHERE, "r", NULL, NULL },
- { MAP_CALL(GetDistance), SIG_EVERYWHERE, "ii(i)(i)(i)(i)", NULL, NULL },
- { MAP_CALL(GetEvent), SIG_SCIALL, SIGFOR_MAC, "io(i*)", NULL, NULL },
- { MAP_CALL(GetEvent), SIG_EVERYWHERE, "io", NULL, NULL },
- { MAP_CALL(GetFarText), SIG_EVERYWHERE, "ii[r0]", NULL, NULL },
- { MAP_CALL(GetMenu), SIG_EVERYWHERE, "i.", NULL, NULL },
- { MAP_CALL(GetMessage), SIG_EVERYWHERE, "iiir", NULL, NULL },
- { MAP_CALL(GetPort), SIG_EVERYWHERE, "", NULL, NULL },
- { MAP_CALL(GetSaveDir), SIG_SCI32, SIGFOR_ALL, "(r*)", NULL, NULL },
- { MAP_CALL(GetSaveDir), SIG_EVERYWHERE, "", NULL, NULL },
- { MAP_CALL(GetSaveFiles), SIG_EVERYWHERE, "rrr", NULL, NULL },
- { MAP_CALL(GetTime), SIG_EVERYWHERE, "(i)", NULL, NULL },
- { MAP_CALL(GlobalToLocal), SIG_SCI32, SIGFOR_ALL, "oo", NULL, NULL },
- { MAP_CALL(GlobalToLocal), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(Graph), SIG_EVERYWHERE, NULL, kGraph_subops, NULL },
- { MAP_CALL(HaveMouse), SIG_EVERYWHERE, "", NULL, NULL },
- { MAP_CALL(HiliteControl), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(InitBresen), SIG_EVERYWHERE, "o(i)", NULL, NULL },
- { MAP_CALL(Intersections), SIG_EVERYWHERE, "iiiiriiiri", NULL, NULL },
- { MAP_CALL(IsItSkip), SIG_EVERYWHERE, "iiiii", NULL, NULL },
- { MAP_CALL(IsObject), SIG_EVERYWHERE, ".", NULL, NULL },
- { MAP_CALL(Joystick), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
- { MAP_CALL(LastNode), SIG_EVERYWHERE, "l", NULL, NULL },
- { MAP_CALL(Load), SIG_EVERYWHERE, "ii(i*)", NULL, NULL },
- { MAP_CALL(LocalToGlobal), SIG_SCI32, SIGFOR_ALL, "oo", NULL, NULL },
- { MAP_CALL(LocalToGlobal), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(Lock), SIG_EVERYWHERE, "ii(i)", NULL, NULL },
- { MAP_CALL(MapKeyToDir), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(Memory), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
- { MAP_CALL(MemoryInfo), SIG_EVERYWHERE, "i", NULL, NULL },
- { MAP_CALL(MemorySegment), SIG_EVERYWHERE, "ir(i)", NULL, NULL }, // subop
- { MAP_CALL(MenuSelect), SIG_EVERYWHERE, "o(i)", NULL, NULL },
- { MAP_CALL(MergePoly), SIG_EVERYWHERE, "rli", NULL, NULL },
- { MAP_CALL(Message), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
- { MAP_CALL(MoveCursor), SIG_EVERYWHERE, "ii", NULL, NULL },
- { MAP_CALL(NewList), SIG_EVERYWHERE, "", NULL, NULL },
- { MAP_CALL(NewNode), SIG_EVERYWHERE, "..", NULL, NULL },
- { MAP_CALL(NewWindow), SIG_SCIALL, SIGFOR_MAC, ".*", NULL, NULL },
- { MAP_CALL(NewWindow), SIG_SCI0, SIGFOR_ALL, "iiii[r0]i(i)(i)(i)", NULL, NULL },
- { MAP_CALL(NewWindow), SIG_SCI1, SIGFOR_ALL, "iiii[ir]i(i)(i)([ir])(i)(i)(i)(i)", NULL, NULL },
- { MAP_CALL(NewWindow), SIG_SCI11, SIGFOR_ALL, "iiiiiiii[r0]i(i)(i)(i)", NULL, NULL },
- { MAP_CALL(NextNode), SIG_EVERYWHERE, "n", NULL, NULL },
- { MAP_CALL(NodeValue), SIG_EVERYWHERE, "[n0]", NULL, NULL },
- { MAP_CALL(NumCels), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(NumLoops), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(OnControl), SIG_EVERYWHERE, "ii(i)(i)(i)", NULL, NULL },
- { MAP_CALL(PalVary), SIG_EVERYWHERE, "i(i*)", kPalVary_subops, NULL },
- { MAP_CALL(Palette), SIG_EVERYWHERE, "i(.*)", kPalette_subops, NULL },
- { MAP_CALL(Parse), SIG_EVERYWHERE, "ro", NULL, NULL },
- { MAP_CALL(PicNotValid), SIG_EVERYWHERE, "(i)", NULL, NULL },
- { MAP_CALL(Platform), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_CALL(Portrait), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
- { MAP_CALL(PrevNode), SIG_EVERYWHERE, "n", NULL, NULL },
- { MAP_CALL(PriCoord), SIG_EVERYWHERE, "i", NULL, NULL },
- { MAP_CALL(Random), SIG_EVERYWHERE, "ii", NULL, NULL },
- { MAP_CALL(ReadNumber), SIG_EVERYWHERE, "r", NULL, NULL },
- { MAP_CALL(ResCheck), SIG_EVERYWHERE, "ii(iiii)", NULL, NULL },
- { MAP_CALL(RespondsTo), SIG_EVERYWHERE, ".i", NULL, NULL },
- { MAP_CALL(RestartGame), SIG_EVERYWHERE, "", NULL, NULL },
- { MAP_CALL(RestoreGame), SIG_EVERYWHERE, "rir", NULL, NULL },
- { MAP_CALL(Said), SIG_EVERYWHERE, "[r0]", NULL, NULL },
- { MAP_CALL(SaveGame), SIG_EVERYWHERE, "rir(r)", NULL, NULL },
- { MAP_CALL(ScriptID), SIG_EVERYWHERE, "[io](i)", NULL, NULL },
- { MAP_CALL(SetCursor), SIG_EVERYWHERE, "i(i*)", NULL, NULL },
- { MAP_CALL(SetDebug), SIG_EVERYWHERE, "(i*)", NULL, NULL },
- { MAP_CALL(SetJump), SIG_EVERYWHERE, "oiii", NULL, NULL },
- { MAP_CALL(SetMenu), SIG_EVERYWHERE, "i(.*)", NULL, NULL },
- { MAP_CALL(SetNowSeen), SIG_EVERYWHERE, "o(i)", NULL, NULL },
- { MAP_CALL(SetPort), SIG_EVERYWHERE, "i(iii)(i)(i)(i)", NULL, NULL },
- { MAP_CALL(SetQuitStr), SIG_EVERYWHERE, "r", NULL, NULL },
- { MAP_CALL(SetSynonyms), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(SetVideoMode), SIG_EVERYWHERE, "i", NULL, NULL },
- { MAP_CALL(ShakeScreen), SIG_EVERYWHERE, "(i)(i)", NULL, NULL },
- { MAP_CALL(ShowMovie), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_CALL(SinDiv), SIG_EVERYWHERE, "ii", NULL, NULL },
- { MAP_CALL(Sort), SIG_EVERYWHERE, "ooo", NULL, NULL },
- { MAP_CALL(Sqrt), SIG_EVERYWHERE, "i", NULL, NULL },
- { MAP_CALL(StrAt), SIG_EVERYWHERE, "ri(i)", NULL, NULL },
- { MAP_CALL(StrCat), SIG_EVERYWHERE, "rr", NULL, NULL },
- { MAP_CALL(StrCmp), SIG_EVERYWHERE, "rr(i)", NULL, NULL },
- { MAP_CALL(StrCpy), SIG_EVERYWHERE, "[r0]r(i)", NULL, NULL },
- { MAP_CALL(StrEnd), SIG_EVERYWHERE, "r", NULL, NULL },
- { MAP_CALL(StrLen), SIG_EVERYWHERE, "[r0]", NULL, NULL },
- { MAP_CALL(StrSplit), SIG_EVERYWHERE, "rr[r0]", NULL, NULL },
- { MAP_CALL(TextColors), SIG_EVERYWHERE, "(i*)", NULL, NULL },
- { MAP_CALL(TextFonts), SIG_EVERYWHERE, "(i*)", NULL, NULL },
- { MAP_CALL(TextSize), SIG_EVERYWHERE, "r[r0]i(i)(r0)", NULL, NULL },
- { MAP_CALL(TimesCos), SIG_EVERYWHERE, "ii", NULL, NULL },
- { "CosMult", kTimesCos, SIG_EVERYWHERE, "ii", NULL, NULL },
- { MAP_CALL(TimesCot), SIG_EVERYWHERE, "ii", NULL, NULL },
- { MAP_CALL(TimesSin), SIG_EVERYWHERE, "ii", NULL, NULL },
- { "SinMult", kTimesSin, SIG_EVERYWHERE, "ii", NULL, NULL },
- { MAP_CALL(TimesTan), SIG_EVERYWHERE, "ii", NULL, NULL },
- { MAP_CALL(UnLoad), SIG_EVERYWHERE, "i[ri]", NULL, kUnLoad_workarounds },
- { MAP_CALL(ValidPath), SIG_EVERYWHERE, "r", NULL, NULL },
- { MAP_CALL(Wait), SIG_EVERYWHERE, "i", NULL, NULL },
-
-#ifdef ENABLE_SCI32
- // SCI2 Kernel Functions
- { MAP_CALL(AddPlane), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(AddScreenItem), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(Array), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_CALL(CreateTextBitmap), SIG_EVERYWHERE, "i(.*)", NULL, NULL },
- { MAP_CALL(DeletePlane), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(DeleteScreenItem), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(FrameOut), SIG_EVERYWHERE, "", NULL, NULL },
- { MAP_CALL(GetHighPlanePri), SIG_EVERYWHERE, "", NULL, NULL },
- { MAP_CALL(InPolygon), SIG_EVERYWHERE, "iio", NULL, NULL },
- { MAP_CALL(IsHiRes), SIG_EVERYWHERE, "", NULL, NULL },
- { MAP_CALL(ListAllTrue), SIG_EVERYWHERE, "li(.*)", NULL, NULL },
- { MAP_CALL(ListAt), SIG_EVERYWHERE, "li", NULL, NULL },
- { 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 },
- { MAP_CALL(OnMe), SIG_EVERYWHERE, "iio(.*)", NULL, NULL },
- { MAP_CALL(RepaintPlane), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(String), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_CALL(UpdatePlane), SIG_EVERYWHERE, "o", NULL, NULL },
- { MAP_CALL(UpdateScreenItem), SIG_EVERYWHERE, "o", NULL, NULL },
-
- // SCI2.1 Kernel Functions
- { MAP_CALL(CD), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_CALL(IsOnMe), SIG_EVERYWHERE, "iio(.*)", NULL, NULL },
- { MAP_CALL(List), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_CALL(MulDiv), SIG_EVERYWHERE, "iii", NULL, NULL },
- { MAP_CALL(PlayVMD), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_CALL(Robot), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_CALL(Save), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_CALL(Text), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { NULL, NULL, SIG_EVERYWHERE, NULL, NULL, NULL }
-#endif
-};
-
Kernel::Kernel(ResourceManager *resMan, SegManager *segMan)
: _resMan(resMan), _segMan(segMan), _invalid("<invalid>") {
loadSelectorNames();
@@ -648,16 +44,24 @@ Kernel::Kernel(ResourceManager *resMan, SegManager *segMan)
Kernel::~Kernel() {
for (KernelFunctionArray::iterator i = _kernelFuncs.begin(); i != _kernelFuncs.end(); ++i)
- free(i->signature);
+ delete[] i->signature;
}
uint Kernel::getSelectorNamesSize() const {
return _selectorNames.size();
}
-const Common::String &Kernel::getSelectorName(uint selector) const {
- if (selector >= _selectorNames.size())
- return _invalid;
+const Common::String &Kernel::getSelectorName(uint selector) {
+ if (selector >= _selectorNames.size()) {
+ // This should only occur in games w/o a selector-table
+ // We need this for proper workaround tables
+ // TODO: maybe check, if there is a fixed selector-table and error() out in that case
+ for (uint loopSelector = _selectorNames.size(); loopSelector <= selector; loopSelector++) {
+ Common::String newSelectorName;
+ newSelectorName = newSelectorName.printf("<noname %d>", loopSelector);
+ _selectorNames.push_back(newSelectorName);
+ }
+ }
return _selectorNames[selector];
}
@@ -1140,7 +544,7 @@ void Kernel::mapFunctions() {
_kernelFuncs[id].workarounds = NULL;
_kernelFuncs[id].subFunctions = NULL;
_kernelFuncs[id].subFunctionCount = 0;
- _kernelFuncs[id].debugCalls = false;
+ _kernelFuncs[id].debugLogging = false;
if (kernelName.empty()) {
// No name was given -> must be an unknown opcode
warning("Kernel function %x unknown", id);
@@ -1246,12 +650,77 @@ void Kernel::mapFunctions() {
return;
}
-void Kernel::setDefaultKernelNames() {
+bool Kernel::debugSetFunctionLogging(const char *kernelName, bool logging) {
+ if (strcmp(kernelName, "*")) {
+ for (uint id = 0; id < _kernelFuncs.size(); id++) {
+ if (_kernelFuncs[id].name) {
+ if (strcmp(kernelName, _kernelFuncs[id].name) == 0) {
+ if (_kernelFuncs[id].subFunctions) {
+ // sub-functions available and main name matched, in that case set logging of all sub-functions
+ KernelSubFunction *kernelSubCall = _kernelFuncs[id].subFunctions;
+ uint kernelSubCallCount = _kernelFuncs[id].subFunctionCount;
+ for (uint subId = 0; subId < kernelSubCallCount; subId++) {
+ if (kernelSubCall->function)
+ kernelSubCall->debugLogging = logging;
+ kernelSubCall++;
+ }
+ return true;
+ }
+ // function name matched, set for this one and exit
+ _kernelFuncs[id].debugLogging = logging;
+ return true;
+ } else {
+ // main name was not matched
+ if (_kernelFuncs[id].subFunctions) {
+ // Sub-Functions available
+ KernelSubFunction *kernelSubCall = _kernelFuncs[id].subFunctions;
+ uint kernelSubCallCount = _kernelFuncs[id].subFunctionCount;
+ for (uint subId = 0; subId < kernelSubCallCount; subId++) {
+ if (kernelSubCall->function) {
+ if (strcmp(kernelName, kernelSubCall->name) == 0) {
+ // sub-function name matched, set for this one and exit
+ kernelSubCall->debugLogging = logging;
+ return true;
+ }
+ }
+ kernelSubCall++;
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+ // Set debugLogging for all calls
+ for (uint id = 0; id < _kernelFuncs.size(); id++) {
+ if (_kernelFuncs[id].name) {
+ if (!_kernelFuncs[id].subFunctions) {
+ // No sub-functions, enable actual kernel function
+ _kernelFuncs[id].debugLogging = logging;
+ } else {
+ // Sub-Functions available, enable those too
+ KernelSubFunction *kernelSubCall = _kernelFuncs[id].subFunctions;
+ uint kernelSubCallCount = _kernelFuncs[id].subFunctionCount;
+ for (uint subId = 0; subId < kernelSubCallCount; subId++) {
+ if (kernelSubCall->function)
+ kernelSubCall->debugLogging = logging;
+ kernelSubCall++;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+void Kernel::setDefaultKernelNames(GameFeatures *features) {
_kernelNames = Common::StringArray(s_defaultKernelNames, ARRAYSIZE(s_defaultKernelNames));
// Some (later) SCI versions replaced CanBeHere by CantBeHere
- if (_selectorCache.cantBeHere != -1)
- _kernelNames[0x4d] = "CantBeHere";
+ if (_selectorCache.cantBeHere != -1) {
+ // hoyle 3 has cantBeHere selector but is assuming to call kCanBeHere
+ if (g_sci->getGameId() != GID_HOYLE3)
+ _kernelNames[0x4d] = "CantBeHere";
+ }
switch (getSciVersion()) {
case SCI_VERSION_0_EARLY:
@@ -1298,7 +767,11 @@ void Kernel::setDefaultKernelNames() {
}
_kernelNames[0x71] = "PalVary";
- _kernelNames[0x7c] = "Message";
+
+ // At least EcoQuest 1 demo uses kGetMessage instead of kMessage.
+ // Detect which function to use.
+ if (features->detectMessageFunctionType() == SCI_VERSION_1_1)
+ _kernelNames[0x7c] = "Message";
break;
default:
@@ -1307,6 +780,39 @@ void Kernel::setDefaultKernelNames() {
}
}
+#ifdef ENABLE_SCI32
+
+enum {
+ kKernelEntriesSci2 = 0x8b,
+ kKernelEntriesGk2Demo = 0xa0,
+ kKernelEntriesSci21 = 0x9d
+};
+
+void Kernel::setKernelNamesSci2() {
+ _kernelNames = Common::StringArray(sci2_default_knames, kKernelEntriesSci2);
+}
+
+void Kernel::setKernelNamesSci21(GameFeatures *features) {
+ // Some SCI games use a modified SCI2 kernel table instead of the
+ // SCI2.1 kernel table. The GK2 demo does this as well as at least
+ // one version of KQ7 (1.4). We detect which version to use based on
+ // how kDoSound is called from Sound::play().
+
+ // This is interesting because they all have the same interpreter
+ // version (2.100.002), yet they would not be compatible with other
+ // games of the same interpreter.
+
+ if (features->detectSci21KernelType() == SCI_VERSION_2) {
+ _kernelNames = Common::StringArray(sci2_default_knames, kKernelEntriesGk2Demo);
+ // OnMe is IsOnMe here, but they should be compatible
+ _kernelNames[0x23] = "Robot"; // Graph in SCI2
+ _kernelNames[0x2e] = "Priority"; // DisposeTextBitmap in SCI2
+ } else
+ _kernelNames = Common::StringArray(sci21_default_knames, kKernelEntriesSci21);
+}
+
+#endif
+
void Kernel::loadKernelNames(GameFeatures *features) {
_kernelNames.clear();
@@ -1317,7 +823,7 @@ void Kernel::loadKernelNames(GameFeatures *features) {
setKernelNamesSci2();
else
#endif
- setDefaultKernelNames();
+ setDefaultKernelNames(features);
mapFunctions();
}
@@ -1352,4 +858,26 @@ Common::String Kernel::lookupText(reg_t address, int index) {
return NULL;
}
+// TODO: script_adjust_opcode_formats should probably be part of the
+// constructor (?) of a VirtualMachine or a ScriptManager class.
+void script_adjust_opcode_formats() {
+ if (g_sci->_features->detectLofsType() != SCI_VERSION_0_EARLY) {
+ g_opcode_formats[op_lofsa][0] = Script_Offset;
+ g_opcode_formats[op_lofss][0] = Script_Offset;
+ }
+
+#ifdef ENABLE_SCI32
+ // In SCI32, some arguments are now words instead of bytes
+ if (getSciVersion() >= SCI_VERSION_2) {
+ g_opcode_formats[op_calle][2] = Script_Word;
+ g_opcode_formats[op_callk][1] = Script_Word;
+ g_opcode_formats[op_super][1] = Script_Word;
+ g_opcode_formats[op_send][0] = Script_Word;
+ g_opcode_formats[op_self][0] = Script_Word;
+ g_opcode_formats[op_call][1] = Script_Word;
+ g_opcode_formats[op_callb][1] = Script_Word;
+ }
+#endif
+}
+
} // End of namespace Sci
diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h
index fa206e8053..285e746349 100644
--- a/engines/sci/engine/kernel.h
+++ b/engines/sci/engine/kernel.h
@@ -40,6 +40,7 @@ namespace Sci {
struct Node; // from segment.h
struct List; // from segment.h
struct SelectorCache; // from selector.h
+struct SciWorkaroundEntry; // from workarounds.h
/**
* @defgroup VocabularyResources Vocabulary resources in SCI
@@ -100,12 +101,12 @@ struct SelectorCache; // from selector.h
enum {
SIG_TYPE_NULL = 0x01, // may be 0:0 [0]
SIG_TYPE_INTEGER = 0x02, // may be 0:* [i], automatically also allows null
- SIG_TYPE_UNINITIALIZED = 0x04, // may be FFFF:* -> not allowable, only used for comparsion
+ SIG_TYPE_UNINITIALIZED = 0x04, // may be FFFF:* -> not allowable, only used for comparison
SIG_TYPE_OBJECT = 0x08, // may be object [o]
SIG_TYPE_REFERENCE = 0x10, // may be reference [r]
SIG_TYPE_LIST = 0x20, // may be list [l]
SIG_TYPE_NODE = 0x40, // may be node [n]
- SIG_TYPE_ERROR = 0x80, // happens, when there is a identification error - only used for comparsion
+ SIG_TYPE_ERROR = 0x80, // happens, when there is a identification error - only used for comparison
SIG_IS_INVALID = 0x100, // ptr is invalid [!] -> invalid offset
SIG_IS_OPTIONAL = 0x200, // is optional
SIG_NEEDS_MORE = 0x400, // needs at least one additional parameter following
@@ -120,24 +121,12 @@ enum {
/* Generic description: */
typedef reg_t KernelFunctionCall(EngineState *s, int argc, reg_t *argv);
-struct SciWorkaroundEntry {
- SciGameId gameId;
- int scriptNr;
- int16 inheritanceLevel;
- const char *objectName;
- const char *methodName;
- int localCallOffset;
- int index;
- reg_t newValue;
-};
-
-#define SCI_WORKAROUNDENTRY_TERMINATOR { (SciGameId)0, -1, 0, NULL, NULL, -1, 0, { 0, 0 } }
-
struct KernelSubFunction {
KernelFunctionCall *function;
const char *name;
uint16 *signature;
const SciWorkaroundEntry *workarounds;
+ bool debugLogging;
};
struct KernelFunction {
@@ -145,9 +134,9 @@ struct KernelFunction {
const char *name;
uint16 *signature;
const SciWorkaroundEntry *workarounds;
- const KernelSubFunction *subFunctions;
+ KernelSubFunction *subFunctions;
uint16 subFunctionCount;
- bool debugCalls;
+ bool debugLogging;
};
class Kernel {
@@ -159,7 +148,7 @@ public:
~Kernel();
uint getSelectorNamesSize() const;
- const Common::String &getSelectorName(uint selector) const;
+ const Common::String &getSelectorName(uint selector);
uint getKernelNamesSize() const;
const Common::String &getKernelName(uint number) const;
@@ -228,11 +217,16 @@ public:
*/
void loadKernelNames(GameFeatures *features);
+ /**
+ * Sets debugCalls flag for a kernel function
+ */
+ bool debugSetFunctionLogging(const char *kernelName, bool debugCalls);
+
private:
/**
* Sets the default kernel function names, based on the SCI version used.
*/
- void setDefaultKernelNames();
+ void setDefaultKernelNames(GameFeatures *features);
#ifdef ENABLE_SCI32
/**
@@ -423,7 +417,10 @@ reg_t kStrSplit(EngineState *s, int argc, reg_t *argv);
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 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);
+reg_t kStubNull(EngineState *s, int argc, reg_t *argv);
#ifdef ENABLE_SCI32
// SCI2 Kernel Functions
@@ -432,6 +429,7 @@ reg_t kArray(EngineState *s, int argc, reg_t *argv);
reg_t kListAt(EngineState *s, int argc, reg_t *argv);
reg_t kString(EngineState *s, int argc, reg_t *argv);
reg_t kMulDiv(EngineState *s, int argc, reg_t *argv);
+reg_t kCantBeHere32(EngineState *s, int argc, reg_t *argv);
// "Screen items" in SCI32 are views
reg_t kAddScreenItem(EngineState *s, int argc, reg_t *argv);
reg_t kUpdateScreenItem(EngineState *s, int argc, reg_t *argv);
@@ -460,7 +458,11 @@ reg_t kRobot(EngineState *s, int argc, reg_t *argv);
reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv);
reg_t kIsOnMe(EngineState *s, int argc, reg_t *argv);
reg_t kCD(EngineState *s, int argc, reg_t *argv);
+reg_t kAddPicAt(EngineState *s, int argc, reg_t *argv);
+reg_t kAddBefore(EngineState *s, int argc, reg_t *argv);
+reg_t kMoveToFront(EngineState *s, int argc, reg_t *argv);
+reg_t kMoveToEnd(EngineState *s, int argc, reg_t *argv);
#endif
reg_t kDoSoundInit(EngineState *s, int argc, reg_t *argv);
@@ -471,7 +473,7 @@ reg_t kDoSoundMute(EngineState *s, int argc, reg_t *argv);
reg_t kDoSoundStop(EngineState *s, int argc, reg_t *argv);
reg_t kDoSoundStopAll(EngineState *s, int argc, reg_t *argv);
reg_t kDoSoundPause(EngineState *s, int argc, reg_t *argv);
-reg_t kDoSoundResume(EngineState *s, int argc, reg_t *argv);
+reg_t kDoSoundResumeAfterRestore(EngineState *s, int argc, reg_t *argv);
reg_t kDoSoundMasterVolume(EngineState *s, int argc, reg_t *argv);
reg_t kDoSoundUpdate(EngineState *s, int argc, reg_t *argv);
reg_t kDoSoundFade(EngineState *s, int argc, reg_t *argv);
@@ -516,6 +518,25 @@ reg_t kPaletteAnimate(EngineState *s, int argc, reg_t *argv);
reg_t kPaletteSave(EngineState *s, int argc, reg_t *argv);
reg_t kPaletteRestore(EngineState *s, int argc, reg_t *argv);
+reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv);
+reg_t kFileIOClose(EngineState *s, int argc, reg_t *argv);
+reg_t kFileIOReadRaw(EngineState *s, int argc, reg_t *argv);
+reg_t kFileIOWriteRaw(EngineState *s, int argc, reg_t *argv);
+reg_t kFileIOUnlink(EngineState *s, int argc, reg_t *argv);
+reg_t kFileIOReadString(EngineState *s, int argc, reg_t *argv);
+reg_t kFileIOWriteString(EngineState *s, int argc, reg_t *argv);
+reg_t kFileIOSeek(EngineState *s, int argc, reg_t *argv);
+reg_t kFileIOFindFirst(EngineState *s, int argc, reg_t *argv);
+reg_t kFileIOFindNext(EngineState *s, int argc, reg_t *argv);
+reg_t kFileIOExists(EngineState *s, int argc, reg_t *argv);
+reg_t kFileIORename(EngineState *s, int argc, reg_t *argv);
+#ifdef ENABLE_SCI32
+reg_t kFileIOReadByte(EngineState *s, int argc, reg_t *argv);
+reg_t kFileIOWriteByte(EngineState *s, int argc, reg_t *argv);
+reg_t kFileIOReadWord(EngineState *s, int argc, reg_t *argv);
+reg_t kFileIOWriteWord(EngineState *s, int argc, reg_t *argv);
+#endif
+
//@}
} // End of namespace Sci
diff --git a/engines/sci/engine/kernel32.cpp b/engines/sci/engine/kernel32.cpp
deleted file mode 100644
index eab1b90139..0000000000
--- a/engines/sci/engine/kernel32.cpp
+++ /dev/null
@@ -1,932 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
-
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * $URL$
- * $Id$
- *
- */
-
-#ifdef ENABLE_SCI32
-
-#include "sci/engine/features.h"
-#include "sci/engine/kernel.h"
-#include "sci/engine/segment.h"
-#include "sci/engine/state.h"
-#include "sci/engine/selector.h"
-#include "sci/graphics/frameout.h"
-#include "sci/graphics/screen.h"
-
-#include "common/system.h"
-
-namespace Sci {
-
-// NOTE: 0x72-0x79, 0x85-0x86, 0x88 are from the GK2 demo (which has debug support) and are
-// just Dummy in other SCI2 games.
-static const char *sci2_default_knames[] = {
- /*0x00*/ "Load",
- /*0x01*/ "UnLoad",
- /*0x02*/ "ScriptID",
- /*0x03*/ "DisposeScript",
- /*0x04*/ "Lock",
- /*0x05*/ "ResCheck",
- /*0x06*/ "Purge",
- /*0x07*/ "Clone",
- /*0x08*/ "DisposeClone",
- /*0x09*/ "RespondsTo",
- /*0x0a*/ "SetNowSeen",
- /*0x0b*/ "NumLoops",
- /*0x0c*/ "NumCels",
- /*0x0d*/ "CelWide",
- /*0x0e*/ "CelHigh",
- /*0x0f*/ "GetHighPlanePri",
- /*0x10*/ "GetHighItemPri",
- /*0x11*/ "ShakeScreen",
- /*0x12*/ "OnMe",
- /*0x13*/ "ShowMovie",
- /*0x14*/ "SetVideoMode",
- /*0x15*/ "AddScreenItem",
- /*0x16*/ "DeleteScreenItem",
- /*0x17*/ "UpdateScreenItem",
- /*0x18*/ "FrameOut",
- /*0x19*/ "AddPlane",
- /*0x1a*/ "DeletePlane",
- /*0x1b*/ "UpdatePlane",
- /*0x1c*/ "RepaintPlane",
- /*0x1d*/ "SetShowStyle",
- /*0x1e*/ "ShowStylePercent",
- /*0x1f*/ "SetScroll",
- /*0x20*/ "AddMagnify",
- /*0x21*/ "DeleteMagnify",
- /*0x22*/ "IsHiRes",
- /*0x23*/ "Graph",
- /*0x24*/ "InvertRect",
- /*0x25*/ "TextSize",
- /*0x26*/ "Message",
- /*0x27*/ "TextColors",
- /*0x28*/ "TextFonts",
- /*0x29*/ "Dummy",
- /*0x2a*/ "SetQuitStr",
- /*0x2b*/ "EditText",
- /*0x2c*/ "InputText",
- /*0x2d*/ "CreateTextBitmap",
- /*0x2e*/ "DisposeTextBitmap",
- /*0x2f*/ "GetEvent",
- /*0x30*/ "GlobalToLocal",
- /*0x31*/ "LocalToGlobal",
- /*0x32*/ "MapKeyToDir",
- /*0x33*/ "HaveMouse",
- /*0x34*/ "SetCursor",
- /*0x35*/ "VibrateMouse",
- /*0x36*/ "SaveGame",
- /*0x37*/ "RestoreGame",
- /*0x38*/ "RestartGame",
- /*0x39*/ "GameIsRestarting",
- /*0x3a*/ "MakeSaveCatName",
- /*0x3b*/ "MakeSaveFileName",
- /*0x3c*/ "GetSaveFiles",
- /*0x3d*/ "GetSaveDir",
- /*0x3e*/ "CheckSaveGame",
- /*0x3f*/ "CheckFreeSpace",
- /*0x40*/ "DoSound",
- /*0x41*/ "DoAudio",
- /*0x42*/ "DoSync",
- /*0x43*/ "NewList",
- /*0x44*/ "DisposeList",
- /*0x45*/ "NewNode",
- /*0x46*/ "FirstNode",
- /*0x47*/ "LastNode",
- /*0x48*/ "EmptyList",
- /*0x49*/ "NextNode",
- /*0x4a*/ "PrevNode",
- /*0x4b*/ "NodeValue",
- /*0x4c*/ "AddAfter",
- /*0x4d*/ "AddToFront",
- /*0x4e*/ "AddToEnd",
- /*0x4f*/ "Dummy",
- /*0x50*/ "Dummy",
- /*0x51*/ "FindKey",
- /*0x52*/ "Dummy",
- /*0x53*/ "Dummy",
- /*0x54*/ "Dummy",
- /*0x55*/ "DeleteKey",
- /*0x56*/ "Dummy",
- /*0x57*/ "Dummy",
- /*0x58*/ "ListAt",
- /*0x59*/ "ListIndexOf",
- /*0x5a*/ "ListEachElementDo",
- /*0x5b*/ "ListFirstTrue",
- /*0x5c*/ "ListAllTrue",
- /*0x5d*/ "Random",
- /*0x5e*/ "Abs",
- /*0x5f*/ "Sqrt",
- /*0x60*/ "GetAngle",
- /*0x61*/ "GetDistance",
- /*0x62*/ "ATan",
- /*0x63*/ "SinMult",
- /*0x64*/ "CosMult",
- /*0x65*/ "SinDiv",
- /*0x66*/ "CosDiv",
- /*0x67*/ "GetTime",
- /*0x68*/ "Platform",
- /*0x69*/ "BaseSetter",
- /*0x6a*/ "DirLoop",
- /*0x6b*/ "CantBeHere",
- /*0x6c*/ "InitBresen",
- /*0x6d*/ "DoBresen",
- /*0x6e*/ "SetJump",
- /*0x6f*/ "AvoidPath",
- /*0x70*/ "InPolygon",
- /*0x71*/ "MergePoly",
- /*0x72*/ "SetDebug",
- /*0x73*/ "InspectObject",
- /*0x74*/ "MemoryInfo",
- /*0x75*/ "Profiler",
- /*0x76*/ "Record",
- /*0x77*/ "PlayBack",
- /*0x78*/ "MonoOut",
- /*0x79*/ "SetFatalStr",
- /*0x7a*/ "GetCWD",
- /*0x7b*/ "ValidPath",
- /*0x7c*/ "FileIO",
- /*0x7d*/ "Dummy",
- /*0x7e*/ "DeviceInfo",
- /*0x7f*/ "Palette",
- /*0x80*/ "PalVary",
- /*0x81*/ "PalCycle",
- /*0x82*/ "Array",
- /*0x83*/ "String",
- /*0x84*/ "RemapColors",
- /*0x85*/ "IntegrityChecking",
- /*0x86*/ "CheckIntegrity",
- /*0x87*/ "ObjectIntersect",
- /*0x88*/ "MarkMemory",
- /*0x89*/ "TextWidth",
- /*0x8a*/ "PointSize",
-
- // GK2 Demo only kernel functions
- /*0x8b*/ "AddLine",
- /*0x8c*/ "DeleteLine",
- /*0x8d*/ "UpdateLine",
- /*0x8e*/ "AddPolygon",
- /*0x8f*/ "DeletePolygon",
- /*0x90*/ "UpdatePolygon",
- /*0x91*/ "Bitmap",
- /*0x92*/ "ScrollWindow",
- /*0x93*/ "SetFontRes",
- /*0x94*/ "MovePlaneItems",
- /*0x95*/ "PreloadResource",
- /*0x96*/ "Dummy",
- /*0x97*/ "ResourceTrack",
- /*0x98*/ "CheckCDisc",
- /*0x99*/ "GetSaveCDisc",
- /*0x9a*/ "TestPoly",
- /*0x9b*/ "WinHelp",
- /*0x9c*/ "LoadChunk",
- /*0x9d*/ "SetPalStyleRange",
- /*0x9e*/ "AddPicAt",
- /*0x9f*/ "MessageBox"
-};
-
-static const char *sci21_default_knames[] = {
- /*0x00*/ "Load",
- /*0x01*/ "UnLoad",
- /*0x02*/ "ScriptID",
- /*0x03*/ "DisposeScript",
- /*0x04*/ "Lock",
- /*0x05*/ "ResCheck",
- /*0x06*/ "Purge",
- /*0x07*/ "SetLanguage",
- /*0x08*/ "Dummy",
- /*0x09*/ "Dummy",
- /*0x0a*/ "Clone",
- /*0x0b*/ "DisposeClone",
- /*0x0c*/ "RespondsTo",
- /*0x0d*/ "FindSelector",
- /*0x0e*/ "FindClass",
- /*0x0f*/ "Dummy",
- /*0x10*/ "Dummy",
- /*0x11*/ "Dummy",
- /*0x12*/ "Dummy",
- /*0x13*/ "Dummy",
- /*0x14*/ "SetNowSeen",
- /*0x15*/ "NumLoops",
- /*0x16*/ "NumCels",
- /*0x17*/ "IsOnMe",
- /*0x18*/ "AddMagnify",
- /*0x19*/ "DeleteMagnify",
- /*0x1a*/ "CelRect",
- /*0x1b*/ "BaseLineSpan",
- /*0x1c*/ "CelWide",
- /*0x1d*/ "CelHigh",
- /*0x1e*/ "AddScreenItem",
- /*0x1f*/ "DeleteScreenItem",
- /*0x20*/ "UpdateScreenItem",
- /*0x21*/ "FrameOut",
- /*0x22*/ "CelInfo",
- /*0x23*/ "Bitmap",
- /*0x24*/ "CelLink",
- /*0x25*/ "Dummy",
- /*0x26*/ "Dummy",
- /*0x27*/ "Dummy",
- /*0x28*/ "AddPlane",
- /*0x29*/ "DeletePlane",
- /*0x2a*/ "UpdatePlane",
- /*0x2b*/ "RepaintPlane",
- /*0x2c*/ "GetHighPlanePri",
- /*0x2d*/ "GetHighItemPri",
- /*0x2e*/ "SetShowStyle",
- /*0x2f*/ "ShowStylePercent",
- /*0x30*/ "SetScroll",
- /*0x31*/ "MovePlaneItems",
- /*0x32*/ "ShakeScreen",
- /*0x33*/ "Dummy",
- /*0x34*/ "Dummy",
- /*0x35*/ "Dummy",
- /*0x36*/ "Dummy",
- /*0x37*/ "IsHiRes",
- /*0x38*/ "SetVideoMode",
- /*0x39*/ "ShowMovie",
- /*0x3a*/ "Robot",
- /*0x3b*/ "CreateTextBitmap",
- /*0x3c*/ "Random",
- /*0x3d*/ "Abs",
- /*0x3e*/ "Sqrt",
- /*0x3f*/ "GetAngle",
- /*0x40*/ "GetDistance",
- /*0x41*/ "ATan",
- /*0x42*/ "SinMult",
- /*0x43*/ "CosMult",
- /*0x44*/ "SinDiv",
- /*0x45*/ "CosDiv",
- /*0x46*/ "Text",
- /*0x47*/ "Dummy",
- /*0x48*/ "Message",
- /*0x49*/ "Font",
- /*0x4a*/ "EditText",
- /*0x4b*/ "InputText",
- /*0x4c*/ "ScrollWindow",
- /*0x4d*/ "Dummy",
- /*0x4e*/ "Dummy",
- /*0x4f*/ "Dummy",
- /*0x50*/ "GetEvent",
- /*0x51*/ "GlobalToLocal",
- /*0x52*/ "LocalToGlobal",
- /*0x53*/ "MapKeyToDir",
- /*0x54*/ "HaveMouse",
- /*0x55*/ "SetCursor",
- /*0x56*/ "VibrateMouse", // NOTE: Not in SCI3, instead replaced by Dummy.
- /*0x57*/ "Dummy",
- /*0x58*/ "Dummy",
- /*0x59*/ "Dummy",
- /*0x5a*/ "List",
- /*0x5b*/ "Array",
- /*0x5c*/ "String",
- /*0x5d*/ "FileIO",
- /*0x5e*/ "BaseSetter",
- /*0x5f*/ "DirLoop",
- /*0x60*/ "CantBeHere",
- /*0x61*/ "InitBresen",
- /*0x62*/ "DoBresen",
- /*0x63*/ "SetJump",
- /*0x64*/ "AvoidPath",
- /*0x65*/ "InPolygon",
- /*0x66*/ "MergePoly",
- /*0x67*/ "ObjectIntersect",
- /*0x68*/ "Dummy",
- /*0x69*/ "MemoryInfo",
- /*0x6a*/ "DeviceInfo",
- /*0x6b*/ "Palette",
- /*0x6c*/ "PalVary",
- /*0x6d*/ "PalCycle",
- /*0x6e*/ "RemapColors",
- /*0x6f*/ "AddLine",
- /*0x70*/ "DeleteLine",
- /*0x71*/ "UpdateLine",
- /*0x72*/ "AddPolygon",
- /*0x73*/ "DeletePolygon",
- /*0x74*/ "UpdatePolygon",
- /*0x75*/ "DoSound",
- /*0x76*/ "DoAudio",
- /*0x77*/ "DoSync",
- /*0x78*/ "Save",
- /*0x79*/ "GetTime",
- /*0x7a*/ "Platform",
- /*0x7b*/ "CD",
- /*0x7c*/ "SetQuitStr",
- /*0x7d*/ "GetConfig",
- /*0x7e*/ "Table",
- /*0x7f*/ "WinHelp", // Windows only
- /*0x80*/ "Dummy",
- /*0x81*/ "Dummy",
- /*0x82*/ "Dummy",
- /*0x83*/ "PrintDebug", // used by the Shivers 2 demo
- /*0x84*/ "Dummy",
- /*0x85*/ "Dummy",
- /*0x86*/ "Dummy",
- /*0x87*/ "Dummy",
- /*0x88*/ "Dummy",
- /*0x89*/ "Dummy",
- /*0x8a*/ "LoadChunk",
- /*0x8b*/ "SetPalStyleRange",
- /*0x8c*/ "AddPicAt",
- /*0x8d*/ "Dummy",
- /*0x8e*/ "NewRoom",
- /*0x8f*/ "Dummy",
- /*0x90*/ "Priority",
- /*0x91*/ "MorphOn",
- /*0x92*/ "PlayVMD",
- /*0x93*/ "SetHotRectangles",
- /*0x94*/ "MulDiv",
- /*0x95*/ "GetSierraProfileInt", // Windows only
- /*0x96*/ "GetSierraProfileString", // Windows only
- /*0x97*/ "SetWindowsOption", // Windows only
- /*0x98*/ "GetWindowsOption", // Windows only
- /*0x99*/ "WinDLL", // Windows only
-
- // SCI3
- /*0x9a*/ "Dummy",
- /*0x9b*/ "Dummy",
- /*0x9c*/ "DeletePic"
-};
-
-enum {
- kKernelEntriesSci2 = 0x8b,
- kKernelEntriesGk2Demo = 0xa0,
- kKernelEntriesSci21 = 0x9a,
- kKernelEntriesSci3 = 0x9d
-};
-
-void Kernel::setKernelNamesSci2() {
- _kernelNames = Common::StringArray(sci2_default_knames, kKernelEntriesSci2);
-}
-
-void Kernel::setKernelNamesSci21(GameFeatures *features) {
- // Some SCI games use a modified SCI2 kernel table instead of the
- // SCI2.1/SCI3 kernel table. The GK2 demo does this as well as at
- // least one version of KQ7. We detect which version to use based on
- // where kDoSound is called from Sound::play().
-
- // This is interesting because they all have the same interpreter
- // version (2.100.002), yet they would not be compatible with other
- // games of the same interpreter.
-
- if (features->detectSci21KernelType() == SCI_VERSION_2) {
- _kernelNames = Common::StringArray(sci2_default_knames, kKernelEntriesGk2Demo);
- // OnMe is IsOnMe here, but they should be compatible
- _kernelNames[0x23] = "Robot"; // Graph in SCI2
- _kernelNames[0x2e] = "Priority"; // DisposeTextBitmap in SCI2
- } else {
- // TODO: Differentiate between SCI2.1/3
- _kernelNames = Common::StringArray(sci21_default_knames, kKernelEntriesSci3);
- }
-}
-
-// SCI2 Kernel Functions
-
-reg_t kIsHiRes(EngineState *s, int argc, reg_t *argv) {
- // Returns 0 if the screen width or height is less than 640 or 400,
- // respectively.
- if (g_system->getWidth() < 640 || g_system->getHeight() < 400)
- return make_reg(0, 0);
-
- return make_reg(0, 1);
-}
-
-reg_t kArray(EngineState *s, int argc, reg_t *argv) {
- switch (argv[0].toUint16()) {
- case 0: { // New
- reg_t arrayHandle;
- SciArray<reg_t> *array = s->_segMan->allocateArray(&arrayHandle);
- array->setType(argv[2].toUint16());
- array->setSize(argv[1].toUint16());
- return arrayHandle;
- }
- case 1: { // Size
- SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]);
- return make_reg(0, array->getSize());
- }
- case 2: { // At (return value at an index)
- SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]);
- return array->getValue(argv[2].toUint16());
- }
- case 3: { // Atput (put value at an index)
- SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]);
-
- uint32 index = argv[2].toUint16();
- uint32 count = argc - 3;
-
- if (index + count > 65535)
- break;
-
- if (array->getSize() < index + count)
- array->setSize(index + count);
-
- for (uint16 i = 0; i < count; i++)
- array->setValue(i + index, argv[i + 3]);
-
- return argv[1]; // We also have to return the handle
- }
- case 4: // Free
- // Freeing of arrays is handled by the garbage collector
- return s->r_acc;
- case 5: { // Fill
- SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]);
- uint16 index = argv[2].toUint16();
-
- // A count of -1 means fill the rest of the array
- uint16 count = argv[3].toSint16() == -1 ? array->getSize() - index : argv[3].toUint16();
- uint16 arraySize = array->getSize();
-
- if (arraySize < index + count)
- array->setSize(index + count);
-
- for (uint16 i = 0; i < count; i++)
- array->setValue(i + index, argv[4]);
-
- return argv[1];
- }
- case 6: { // Cpy
- if (s->_segMan->getSegmentObj(argv[1].segment)->getType() != SEG_TYPE_ARRAY ||
- s->_segMan->getSegmentObj(argv[3].segment)->getType() != SEG_TYPE_ARRAY) {
- // Happens in the RAMA demo
- warning("kArray(Cpy): Request to copy a segment which isn't an array, ignoring");
- return NULL_REG;
- }
-
- SciArray<reg_t> *array1 = s->_segMan->lookupArray(argv[1]);
- SciArray<reg_t> *array2 = s->_segMan->lookupArray(argv[3]);
- uint32 index1 = argv[2].toUint16();
- uint32 index2 = argv[4].toUint16();
-
- // The original engine ignores bad copies too
- if (index2 > array2->getSize())
- break;
-
- // A count of -1 means fill the rest of the array
- uint32 count = argv[5].toSint16() == -1 ? array2->getSize() - index2 : argv[5].toUint16();
-
- if (array1->getSize() < index1 + count)
- array1->setSize(index1 + count);
-
- for (uint16 i = 0; i < count; i++)
- array1->setValue(i + index1, array2->getValue(i + index2));
-
- return argv[1];
- }
- case 7: // Cmp
- // Not implemented in SSCI
- return s->r_acc;
- case 8: { // Dup
- SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]);
- reg_t arrayHandle;
- SciArray<reg_t> *dupArray = s->_segMan->allocateArray(&arrayHandle);
-
- dupArray->setType(array->getType());
- dupArray->setSize(array->getSize());
-
- for (uint32 i = 0; i < array->getSize(); i++)
- dupArray->setValue(i, array->getValue(i));
-
- return arrayHandle;
- }
- case 9: // Getdata
- if (!s->_segMan->isHeapObject(argv[1]))
- return argv[1];
-
- return readSelector(s->_segMan, argv[1], SELECTOR(data));
- default:
- error("Unknown kArray subop %d", argv[0].toUint16());
- }
-
- return NULL_REG;
-}
-
-reg_t kText(EngineState *s, int argc, reg_t *argv) {
- switch (argv[0].toUint16()) {
- case 0:
- return kTextSize(s, argc - 1, argv + 1);
- default:
- // TODO: Other subops here too, perhaps kTextColors and kTextFonts
- warning("kText(%d)", argv[0].toUint16());
- break;
- }
-
- return s->r_acc;
-}
-
-reg_t kString(EngineState *s, int argc, reg_t *argv) {
- switch (argv[0].toUint16()) {
- case 0: { // New
- reg_t stringHandle;
- SciString *string = s->_segMan->allocateString(&stringHandle);
- string->setSize(argv[1].toUint16());
-
- // Make sure the first character is a null character
- if (string->getSize() > 0)
- string->setValue(0, 0);
-
- return stringHandle;
- }
- case 1: // Size
- return make_reg(0, s->_segMan->getString(argv[1]).size());
- case 2: { // At (return value at an index)
- if (argv[1].segment == s->_segMan->getStringSegmentId())
- return make_reg(0, s->_segMan->lookupString(argv[1])->getRawData()[argv[2].toUint16()]);
-
- return make_reg(0, s->_segMan->getString(argv[1])[argv[2].toUint16()]);
- }
- case 3: { // Atput (put value at an index)
- SciString *string = s->_segMan->lookupString(argv[1]);
-
- uint32 index = argv[2].toUint16();
- uint32 count = argc - 3;
-
- if (index + count > 65535)
- break;
-
- if (string->getSize() < index + count)
- string->setSize(index + count);
-
- for (uint16 i = 0; i < count; i++)
- string->setValue(i + index, argv[i + 3].toUint16());
-
- return argv[1]; // We also have to return the handle
- }
- case 4: // Free
- // Freeing of strings is handled by the garbage collector
- return s->r_acc;
- case 5: { // Fill
- SciString *string = s->_segMan->lookupString(argv[1]);
- uint16 index = argv[2].toUint16();
-
- // A count of -1 means fill the rest of the array
- uint16 count = argv[3].toSint16() == -1 ? string->getSize() - index : argv[3].toUint16();
- uint16 stringSize = string->getSize();
-
- if (stringSize < index + count)
- string->setSize(index + count);
-
- for (uint16 i = 0; i < count; i++)
- string->setValue(i + index, argv[4].toUint16());
-
- return argv[1];
- }
- case 6: { // Cpy
- const char *string2 = 0;
- uint32 string2Size = 0;
-
- if (argv[3].segment == s->_segMan->getStringSegmentId()) {
- SciString *string = s->_segMan->lookupString(argv[3]);
- string2 = string->getRawData();
- string2Size = string->getSize();
- } else {
- Common::String string = s->_segMan->getString(argv[3]);
- string2 = string.c_str();
- string2Size = string.size() + 1;
- }
-
- uint32 index1 = argv[2].toUint16();
- uint32 index2 = argv[4].toUint16();
-
- // The original engine ignores bad copies too
- if (index2 > string2Size)
- break;
-
- // A count of -1 means fill the rest of the array
- uint32 count = argv[5].toSint16() == -1 ? string2Size - index2 + 1 : argv[5].toUint16();
-
- // We have a special case here for argv[1] being a system string
- if (argv[1].segment == s->_segMan->getSysStringsSegment()) {
- // Resize if necessary
- const uint16 sysStringId = argv[1].toUint16();
- SystemString *sysString = s->_segMan->getSystemString(sysStringId);
- assert(sysString);
- if ((uint32)sysString->_maxSize < index1 + count) {
- free(sysString->_value);
- sysString->_maxSize = index1 + count;
- sysString->_value = (char *)calloc(index1 + count, sizeof(char));
- }
-
- strncpy(sysString->_value + index1, string2 + index2, count);
- } else {
- SciString *string1 = s->_segMan->lookupString(argv[1]);
-
- if (string1->getSize() < index1 + count)
- string1->setSize(index1 + count);
-
- // Note: We're accessing from c_str() here because the
- // string's size ignores the trailing 0 and therefore
- // triggers an assert when doing string2[i + index2].
- for (uint16 i = 0; i < count; i++)
- string1->setValue(i + index1, string2[i + index2]);
- }
-
- } return argv[1];
- case 7: { // Cmp
- Common::String string1 = argv[1].isNull() ? "" : s->_segMan->getString(argv[1]);
- Common::String string2 = argv[2].isNull() ? "" : s->_segMan->getString(argv[2]);
-
- if (argc == 4) // Strncmp
- return make_reg(0, strncmp(string1.c_str(), string2.c_str(), argv[3].toUint16()));
- else // Strcmp
- return make_reg(0, strcmp(string1.c_str(), string2.c_str()));
- }
- case 8: { // Dup
- const char *rawString = 0;
- uint32 size = 0;
-
- if (argv[1].segment == s->_segMan->getStringSegmentId()) {
- SciString *string = s->_segMan->lookupString(argv[1]);
- rawString = string->getRawData();
- size = string->getSize();
- } else {
- Common::String string = s->_segMan->getString(argv[1]);
- rawString = string.c_str();
- size = string.size() + 1;
- }
-
- reg_t stringHandle;
- SciString *dupString = s->_segMan->allocateString(&stringHandle);
- dupString->setSize(size);
-
- for (uint32 i = 0; i < size; i++)
- dupString->setValue(i, rawString[i]);
-
- return stringHandle;
- }
- case 9: // Getdata
- if (!s->_segMan->isHeapObject(argv[1]))
- return argv[1];
-
- return readSelector(s->_segMan, argv[1], SELECTOR(data));
- case 10: // Stringlen
- return make_reg(0, s->_segMan->strlen(argv[1]));
- case 11: { // Printf
- reg_t stringHandle;
- s->_segMan->allocateString(&stringHandle);
-
- reg_t *adjustedArgs = new reg_t[argc];
- adjustedArgs[0] = stringHandle;
- memcpy(&adjustedArgs[1], argv + 1, (argc - 1) * sizeof(reg_t));
-
- kFormat(s, argc, adjustedArgs);
- delete[] adjustedArgs;
- return stringHandle;
- }
- case 12: // Printf Buf
- return kFormat(s, argc - 1, argv + 1);
- case 13: { // atoi
- Common::String string = s->_segMan->getString(argv[1]);
- return make_reg(0, (uint16)atoi(string.c_str()));
- }
- default:
- error("Unknown kString subop %d", argv[0].toUint16());
- }
-
- return NULL_REG;
-}
-
-reg_t kSave(EngineState *s, int argc, reg_t *argv) {
- switch (argv[0].toUint16()) {
- case 2: // GetSaveDir
- // Yay! Reusing the old kernel function!
- return kGetSaveDir(s, argc - 1, argv + 1);
- case 8:
- // TODO
- // This function has to return something other than 0 to proceed
- return s->r_acc;
- default:
- warning("Unknown/unhandled kSave subop %d", argv[0].toUint16());
- }
-
- return NULL_REG;
-}
-
-reg_t kAddScreenItem(EngineState *s, int argc, reg_t *argv) {
- reg_t viewObj = argv[0];
-
- g_sci->_gfxFrameout->kernelAddScreenItem(viewObj);
- return NULL_REG;
-}
-
-reg_t kUpdateScreenItem(EngineState *s, int argc, reg_t *argv) {
- //reg_t viewObj = argv[0];
-
- //warning("kUpdateScreenItem, object %04x:%04x, view %d, loop %d, cel %d, pri %d", PRINT_REG(viewObj), viewId, loopNo, celNo, priority);
- return NULL_REG;
-}
-
-reg_t kDeleteScreenItem(EngineState *s, int argc, reg_t *argv) {
- reg_t viewObj = argv[0];
-
- g_sci->_gfxFrameout->kernelDeleteScreenItem(viewObj);
-
- /*
- reg_t viewObj = argv[0];
- uint16 viewId = readSelectorValue(s->_segMan, viewObj, SELECTOR(view));
- int16 loopNo = readSelectorValue(s->_segMan, viewObj, SELECTOR(loop));
- int16 celNo = readSelectorValue(s->_segMan, viewObj, SELECTOR(cel));
- //int16 leftPos = 0;
- //int16 topPos = 0;
- int16 priority = readSelectorValue(s->_segMan, viewObj, SELECTOR(priority));
- //int16 control = 0;
- */
-
- // TODO
-
- //warning("kDeleteScreenItem, view %d, loop %d, cel %d, pri %d", viewId, loopNo, celNo, priority);
- return NULL_REG;
-}
-
-reg_t kAddPlane(EngineState *s, int argc, reg_t *argv) {
- reg_t planeObj = argv[0];
-
- g_sci->_gfxFrameout->kernelAddPlane(planeObj);
- warning("kAddPlane object %04x:%04x", PRINT_REG(planeObj));
- return NULL_REG;
-}
-
-reg_t kDeletePlane(EngineState *s, int argc, reg_t *argv) {
- reg_t planeObj = argv[0];
-
- g_sci->_gfxFrameout->kernelDeletePlane(planeObj);
- warning("kDeletePlane object %04x:%04x", PRINT_REG(planeObj));
- return NULL_REG;
-}
-
-reg_t kUpdatePlane(EngineState *s, int argc, reg_t *argv) {
- reg_t planeObj = argv[0];
-
- g_sci->_gfxFrameout->kernelUpdatePlane(planeObj);
- return s->r_acc;
-}
-
-reg_t kRepaintPlane(EngineState *s, int argc, reg_t *argv) {
- reg_t picObj = argv[0];
-
- // TODO
-
- warning("kRepaintPlane object %04x:%04x", PRINT_REG(picObj));
- return NULL_REG;
-}
-
-reg_t kGetHighPlanePri(EngineState *s, int argc, reg_t *argv) {
- warning("kGetHighPlanePri: %d", g_sci->_gfxFrameout->kernelGetHighPlanePri());
- return make_reg(0, g_sci->_gfxFrameout->kernelGetHighPlanePri());
-}
-
-reg_t kFrameOut(EngineState *s, int argc, reg_t *argv) {
- // This kernel call likely seems to be doing the screen updates,
- // as its called right after a view is updated
-
- // TODO
- g_sci->_gfxFrameout->kernelFrameout();
-
- return NULL_REG;
-}
-
-reg_t kOnMe(EngineState *s, int argc, reg_t *argv) {
- // Tests if the cursor is on the passed object
-
- uint16 x = argv[0].toUint16();
- uint16 y = argv[1].toUint16();
- reg_t targetObject = argv[2];
- // TODO: argv[3] - it's usually 0
- Common::Rect nsRect;
-
- // Get the bounding rectangle of the object
- nsRect.left = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsLeft));
- nsRect.top = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsTop));
- nsRect.right = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsRight));
- nsRect.bottom = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsBottom));
- uint16 itemX = readSelectorValue(s->_segMan, targetObject, SELECTOR(x));
- uint16 itemY = readSelectorValue(s->_segMan, targetObject, SELECTOR(y));
-
- // If top and left are negative, we need to adjust coordinates by
- // the item's x and y (e.g. happens in GK1, day 1, with detective
- // Mosely's hotspot in his office)
-
- if (nsRect.left < 0)
- nsRect.translate(itemX, 0);
-
- if (nsRect.top < 0)
- nsRect.translate(0, itemY);
-
- // HACK: nsLeft and nsTop can be invalid, so try and fix them here
- // using x and y (e.g. with the inventory screen in GK1)
- if (nsRect.left == itemY && nsRect.top == itemX) {
- // Swap the values, as they're inversed (eh???)
- nsRect.left = itemX;
- nsRect.top = itemY;
- }
-
- /*
- warning("kOnMe: (%d, %d) on object %04x:%04x (%s), rect (%d, %d, %d, %d), parameter %d",
- argv[0].toUint16(), argv[1].toUint16(), PRINT_REG(argv[2]), s->_segMan->getObjectName(argv[2]),
- nsRect.left, nsRect.top, nsRect.right, nsRect.bottom,
- argv[3].toUint16());
- */
-
- return make_reg(0, nsRect.contains(x, y));
-}
-
-reg_t kIsOnMe(EngineState *s, int argc, reg_t *argv) {
- // Tests if the cursor is on the passed object, after adjusting the
- // coordinates of the object according to the object's plane
-
- uint16 x = argv[0].toUint16();
- uint16 y = argv[1].toUint16();
- reg_t targetObject = argv[2];
- // TODO: argv[3] - it's usually 0
- Common::Rect nsRect;
-
- // Get the bounding rectangle of the object
- nsRect.left = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsLeft));
- nsRect.top = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsTop));
- nsRect.right = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsRight));
- nsRect.bottom = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsBottom));
-
- // Get the object's plane
- reg_t planeObject = readSelector(s->_segMan, targetObject, SELECTOR(plane));
- if (!planeObject.isNull()) {
- uint16 itemX = readSelectorValue(s->_segMan, targetObject, SELECTOR(x));
- uint16 itemY = readSelectorValue(s->_segMan, targetObject, SELECTOR(y));
- uint16 planeResY = readSelectorValue(s->_segMan, planeObject, SELECTOR(resY));
- uint16 planeResX = readSelectorValue(s->_segMan, planeObject, SELECTOR(resX));
- uint16 planeTop = readSelectorValue(s->_segMan, planeObject, SELECTOR(top));
- uint16 planeLeft = readSelectorValue(s->_segMan, planeObject, SELECTOR(left));
- planeTop = (planeTop * g_sci->_gfxScreen->getHeight()) / planeResY;
- planeLeft = (planeLeft * g_sci->_gfxScreen->getWidth()) / planeResX;
-
- // Adjust the bounding rectangle of the object by the object's
- // actual X, Y coordinates
- itemY = ((itemY * g_sci->_gfxScreen->getHeight()) / planeResY);
- itemX = ((itemX * g_sci->_gfxScreen->getWidth()) / planeResX);
- itemY += planeTop;
- itemX += planeLeft;
-
- nsRect.translate(itemX, itemY);
- }
-
- //warning("kIsOnMe: (%d, %d) on object %04x:%04x, parameter %d", argv[0].toUint16(), argv[1].toUint16(), PRINT_REG(argv[2]), argv[3].toUint16());
-
- return make_reg(0, nsRect.contains(x, y));
-}
-
-reg_t kInPolygon(EngineState *s, int argc, reg_t *argv) {
- // kAvoidPath already implements this
- return kAvoidPath(s, argc, argv);
-}
-
-reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv) {
- // TODO: argument 0 is usually 0, and arguments 1 and 2 are usually 1
- switch (argv[0].toUint16()) {
- case 0: {
- if (argc != 4) {
- warning("kCreateTextBitmap(0): expected 4 arguments, got %i", argc);
- return NULL_REG;
- }
- reg_t object = argv[3];
- Common::String text = s->_segMan->getString(readSelector(s->_segMan, object, SELECTOR(text)));
- debug("kCreateTextBitmap: %s", text.c_str());
- }
- default:
- warning("CreateTextBitmap(%d)", argv[0].toUint16());
- }
-
- return NULL_REG;
-}
-
-reg_t kCD(EngineState *s, int argc, reg_t *argv) {
- // TODO: Stub
- switch (argv[0].toUint16()) {
- case 0:
- // Return whether the contents of disc argv[1] is available.
- return TRUE_REG;
- default:
- warning("CD(%d)", argv[0].toUint16());
- }
-
- return NULL_REG;
-}
-
-} // End of namespace Sci
-
-#endif // ENABLE_SCI32
diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h
new file mode 100644
index 0000000000..886e918fd8
--- /dev/null
+++ b/engines/sci/engine/kernel_tables.h
@@ -0,0 +1,1030 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SCI_ENGINE_KERNEL_TABLES_H
+#define SCI_ENGINE_KERNEL_TABLES_H
+
+#include "sci/engine/workarounds.h"
+
+namespace Sci {
+
+// [io] -> either integer or object
+// (io) -> optionally integer AND an object
+// (i) -> optional integer
+// . -> any type
+// i* -> optional multiple integers
+// .* -> any parameters afterwards (or none)
+
+struct SciKernelMapSubEntry {
+ SciVersion fromVersion;
+ SciVersion toVersion;
+
+ uint16 id;
+
+ const char *name;
+ KernelFunctionCall *function;
+
+ const char *signature;
+ const SciWorkaroundEntry *workarounds;
+};
+
+#define SCI_SUBOPENTRY_TERMINATOR { SCI_VERSION_NONE, SCI_VERSION_NONE, 0, NULL, NULL, NULL, NULL }
+
+
+#define SIG_SCIALL SCI_VERSION_NONE, SCI_VERSION_NONE
+#define SIG_SCI0 SCI_VERSION_NONE, SCI_VERSION_01
+#define SIG_SCI1 SCI_VERSION_1_EGA, SCI_VERSION_1_LATE
+#define SIG_SCI11 SCI_VERSION_1_1, SCI_VERSION_1_1
+#define SIG_SINCE_SCI11 SCI_VERSION_1_1, SCI_VERSION_NONE
+#define SIG_SCI21 SCI_VERSION_2_1, SCI_VERSION_2_1
+
+#define SIG_SCI16 SCI_VERSION_NONE, SCI_VERSION_1_1
+#define SIG_SCI32 SCI_VERSION_2, SCI_VERSION_NONE
+
+// SCI-Sound-Version
+#define SIG_SOUNDSCI0 SCI_VERSION_0_EARLY, SCI_VERSION_0_LATE
+#define SIG_SOUNDSCI1EARLY SCI_VERSION_1_EARLY, SCI_VERSION_1_EARLY
+#define SIG_SOUNDSCI1LATE SCI_VERSION_1_LATE, SCI_VERSION_1_LATE
+#define SIG_SOUNDSCI21 SCI_VERSION_2_1, SCI_VERSION_2_1
+
+#define SIGFOR_ALL 0x3f
+#define SIGFOR_DOS 1 << 0
+#define SIGFOR_PC98 1 << 1
+#define SIGFOR_WIN 1 << 2
+#define SIGFOR_MAC 1 << 3
+#define SIGFOR_AMIGA 1 << 4
+#define SIGFOR_ATARI 1 << 5
+#define SIGFOR_PC SIGFOR_DOS|SIGFOR_WIN
+
+#define SIG_EVERYWHERE SIG_SCIALL, SIGFOR_ALL
+
+#define MAP_CALL(_name_) #_name_, k##_name_
+
+// version, subId, function-mapping, signature, workarounds
+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(DoSoundDummy), "(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 },
+ { SIG_SOUNDSCI0, 6, MAP_CALL(DoSoundPause), "i", NULL },
+ { SIG_SOUNDSCI0, 7, MAP_CALL(DoSoundResumeAfterRestore), "", NULL },
+ { SIG_SOUNDSCI0, 8, MAP_CALL(DoSoundMasterVolume), "(i)", NULL },
+ { SIG_SOUNDSCI0, 9, MAP_CALL(DoSoundUpdate), "o", NULL },
+ { SIG_SOUNDSCI0, 10, MAP_CALL(DoSoundFade), "o", kDoSoundFade_workarounds },
+ { SIG_SOUNDSCI0, 11, MAP_CALL(DoSoundGetPolyphony), "", NULL },
+ { 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(DoSoundDummy), 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 },
+ { SIG_SOUNDSCI1EARLY, 6, MAP_CALL(DoSoundDispose), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 7, MAP_CALL(DoSoundPlay), "oi", NULL },
+ // ^^ TODO: In SCI1-SCI1.1 DoSound (play) is called by 2 methods of the Sound object: play and
+ // playBed. The methods are the same, apart from the second integer parameter: it's 0 in
+ // play and 1 in playBed, to distinguish the caller. It's passed on, we should find out what
+ // it actually does internally
+ { SIG_SOUNDSCI1EARLY, 8, MAP_CALL(DoSoundStop), NULL, NULL },
+ { SIG_SOUNDSCI1EARLY, 9, MAP_CALL(DoSoundPause), "[o0]i", NULL },
+ { SIG_SOUNDSCI1EARLY, 10, MAP_CALL(DoSoundFade), "oiiii", NULL },
+ { SIG_SOUNDSCI1EARLY, 11, MAP_CALL(DoSoundUpdateCues), "o", NULL },
+ { SIG_SOUNDSCI1EARLY, 12, MAP_CALL(DoSoundSendMidi), "oiii", NULL },
+ { SIG_SOUNDSCI1EARLY, 13, MAP_CALL(DoSoundReverb), "i", NULL },
+ { SIG_SOUNDSCI1EARLY, 14, MAP_CALL(DoSoundSetHold), "oi", NULL },
+ { SIG_SOUNDSCI1EARLY, 15, MAP_CALL(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(DoSoundDummy), "", NULL },
+ { SIG_SOUNDSCI1LATE, 3, MAP_CALL(DoSoundGetPolyphony), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 4, MAP_CALL(DoSoundGetAudioCapability), "", NULL },
+ { SIG_SOUNDSCI1LATE, 5, MAP_CALL(DoSoundSuspend), "i", NULL },
+ { SIG_SOUNDSCI1LATE, 6, MAP_CALL(DoSoundInit), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 7, MAP_CALL(DoSoundDispose), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 8, MAP_CALL(DoSoundPlay), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 9, MAP_CALL(DoSoundStop), NULL, NULL },
+ { 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, 14, MAP_CALL(DoSoundSetVolume), "oi", NULL },
+ { SIG_SOUNDSCI1LATE, 15, MAP_CALL(DoSoundSetPriority), "oi", NULL },
+ { SIG_SOUNDSCI1LATE, 16, MAP_CALL(DoSoundSetLoop), "oi", NULL },
+ { SIG_SOUNDSCI1LATE, 17, MAP_CALL(DoSoundUpdateCues), NULL, NULL },
+ { SIG_SOUNDSCI1LATE, 18, MAP_CALL(DoSoundSendMidi), "oiii(i)", NULL },
+ { SIG_SOUNDSCI1LATE, 19, MAP_CALL(DoSoundReverb), 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(DoSoundDummy), 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 },
+ // ^^ 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, NULL },
+ { 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(DoSoundReverb), NULL, NULL },
+ { SIG_SOUNDSCI21, 20, MAP_CALL(DoSoundUpdate), NULL, NULL },
+#endif
+ SCI_SUBOPENTRY_TERMINATOR
+};
+
+// 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
+ { SIG_SCIALL, 2, MAP_CALL(GraphGetColorCount), "", NULL },
+ // 3 - set palette via resource
+ { SIG_SCIALL, 4, MAP_CALL(GraphDrawLine), "iiiii(i)(i)", kGraphDrawLine_workarounds },
+ // 5 - nop
+ // 6 - draw pattern
+ { SIG_SCIALL, 7, MAP_CALL(GraphSaveBox), "iiiii", kGraphSaveBox_workarounds },
+ { SIG_SCIALL, 8, MAP_CALL(GraphRestoreBox), "[r0!]", kGraphRestoreBox_workarounds },
+ // ^ this may get called with invalid references, we check them within restoreBits() and sierra sci behaves the same
+ { SIG_SCIALL, 9, MAP_CALL(GraphFillBoxBackground), "iiii", NULL },
+ { SIG_SCIALL, 10, MAP_CALL(GraphFillBoxForeground), "iiii", kGraphFillBoxForeground_workarounds },
+ { SIG_SCIALL, 11, MAP_CALL(GraphFillBoxAny), "iiiiii(i)(i)", kGraphFillBoxAny_workarounds },
+ { SIG_SCI11, 12, MAP_CALL(GraphUpdateBox), "iiii(i)(r0)", NULL }, // kq6 hires
+ { SIG_SCIALL, 12, MAP_CALL(GraphUpdateBox), "iiii(i)", NULL },
+ { SIG_SCIALL, 13, MAP_CALL(GraphRedrawBox), "iiii", kGraphRedrawBox_workarounds },
+ { SIG_SCIALL, 14, MAP_CALL(GraphAdjustPriority), "ii", NULL },
+ { SIG_SCI11, 15, MAP_CALL(GraphSaveUpscaledHiresBox), "iiii", NULL }, // kq6 hires
+ SCI_SUBOPENTRY_TERMINATOR
+};
+
+// version, subId, function-mapping, signature, workarounds
+static const SciKernelMapSubEntry kPalVary_subops[] = {
+ { SIG_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_SCI32, 8, MAP_CALL(PalVaryUnknown), "i", NULL },
+ 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 },
+ SCI_SUBOPENTRY_TERMINATOR
+};
+
+static const SciKernelMapSubEntry kFileIO_subops[] = {
+ { SIG_SCI32, 0, MAP_CALL(FileIOOpen), "r(i)", NULL },
+ { SIG_SCIALL, 0, MAP_CALL(FileIOOpen), "ri", NULL },
+ { SIG_SCIALL, 1, MAP_CALL(FileIOClose), "i", NULL },
+ { SIG_SCIALL, 2, MAP_CALL(FileIOReadRaw), "iri", NULL },
+ { SIG_SCIALL, 3, MAP_CALL(FileIOWriteRaw), "iri", NULL },
+ { SIG_SCIALL, 4, MAP_CALL(FileIOUnlink), "r", NULL },
+ { SIG_SCIALL, 5, MAP_CALL(FileIOReadString), "rii", NULL },
+ { SIG_SCIALL, 6, MAP_CALL(FileIOWriteString), "ir", NULL },
+ { SIG_SCIALL, 7, MAP_CALL(FileIOSeek), "iii", NULL },
+ { SIG_SCIALL, 8, MAP_CALL(FileIOFindFirst), "rri", NULL },
+ { SIG_SCIALL, 9, MAP_CALL(FileIOFindNext), "r", NULL },
+ { SIG_SCIALL, 10, MAP_CALL(FileIOExists), "r", NULL },
+ { SIG_SINCE_SCI11, 11, MAP_CALL(FileIORename), "rr", NULL },
+#ifdef ENABLE_SCI32
+ { SIG_SCI32, 13, MAP_CALL(FileIOReadByte), "i", NULL },
+ { SIG_SCI32, 14, MAP_CALL(FileIOWriteByte), "ii", NULL },
+ { SIG_SCI32, 15, MAP_CALL(FileIOReadWord), "i", NULL },
+ { SIG_SCI32, 16, MAP_CALL(FileIOWriteWord), "ii", NULL },
+ { SIG_SCI32, 19, MAP_CALL(Stub), "r", NULL }, // for Torin / Torin demo
+#endif
+ SCI_SUBOPENTRY_TERMINATOR
+};
+
+#ifdef ENABLE_SCI32
+// version, subId, function-mapping, signature, workarounds
+static const SciKernelMapSubEntry kList_subops[] = {
+ { SIG_SCI21, 0, MAP_CALL(NewList), "", NULL },
+ { SIG_SCI21, 1, MAP_CALL(DisposeList), "l", NULL },
+ { SIG_SCI21, 2, MAP_CALL(NewNode), ".", NULL },
+ { SIG_SCI21, 3, MAP_CALL(FirstNode), "[l0]", NULL },
+ { SIG_SCI21, 4, MAP_CALL(LastNode), "l", NULL },
+ { SIG_SCI21, 5, MAP_CALL(EmptyList), "l", NULL },
+ { SIG_SCI21, 6, MAP_CALL(NextNode), "n", NULL },
+ { SIG_SCI21, 7, MAP_CALL(PrevNode), "n", NULL },
+ { SIG_SCI21, 8, MAP_CALL(NodeValue), "[n0]", NULL },
+ { SIG_SCI21, 9, MAP_CALL(AddAfter), "lnn.", NULL },
+ { SIG_SCI21, 10, MAP_CALL(AddToFront), "ln.", NULL },
+ { SIG_SCI21, 11, MAP_CALL(AddToEnd), "ln.", NULL },
+ { SIG_SCI21, 12, MAP_CALL(AddBefore), "ln.", NULL },
+ { SIG_SCI21, 13, MAP_CALL(MoveToFront), "ln", NULL },
+ { SIG_SCI21, 14, MAP_CALL(MoveToEnd), "ln", NULL },
+ { SIG_SCI21, 15, MAP_CALL(FindKey), "l.", NULL },
+ { SIG_SCI21, 16, MAP_CALL(DeleteKey), "l.", NULL },
+ { SIG_SCI21, 17, MAP_CALL(ListAt), "li", NULL },
+ // FIXME: This doesn't seem to be ListIndexOf. In Torin demo, an index is
+ // passed as a second parameter instead of an object. Thus, it seems to
+ // be something like ListAt instead... If we swap the two subops though,
+ // Torin demo crashes complaining that it tried to send to a non-object,
+ // therefore the semantics might be different here (signature was l[o0])
+ // In SQ6 object is passed right when skipping the intro
+ { SIG_SCI21, 18, MAP_CALL(StubNull), "l[io]", NULL },
+ { SIG_SCI21, 19, MAP_CALL(ListEachElementDo), "li(.*)", NULL },
+ { SIG_SCI21, 20, MAP_CALL(ListFirstTrue), "li(.*)", NULL },
+ { SIG_SCI21, 21, MAP_CALL(ListAllTrue), "li(.*)", NULL },
+ { SIG_SCI21, 22, MAP_CALL(Sort), "ooo", NULL },
+ SCI_SUBOPENTRY_TERMINATOR
+};
+#endif
+
+struct SciKernelMapEntry {
+ const char *name;
+ KernelFunctionCall *function;
+
+ SciVersion fromVersion;
+ SciVersion toVersion;
+ byte forPlatform;
+
+ const char *signature;
+ const SciKernelMapSubEntry *subFunctions;
+ const SciWorkaroundEntry *workarounds;
+};
+
+// name, version/platform, signature, sub-signatures, workarounds
+static SciKernelMapEntry s_kernelMap[] = {
+ { MAP_CALL(Abs), SIG_EVERYWHERE, "i", NULL, kAbs_workarounds },
+ { MAP_CALL(AddAfter), SIG_EVERYWHERE, "lnn", NULL, NULL },
+ { MAP_CALL(AddMenu), SIG_EVERYWHERE, "rr", NULL, NULL },
+ { MAP_CALL(AddToEnd), SIG_EVERYWHERE, "ln", NULL, NULL },
+ { MAP_CALL(AddToFront), SIG_EVERYWHERE, "ln", NULL, NULL },
+ { MAP_CALL(AddToPic), SIG_EVERYWHERE, "[il](iiiiii)", NULL, NULL },
+ { MAP_CALL(Animate), SIG_EVERYWHERE, "(l0)(i)", NULL, NULL },
+ { MAP_CALL(AssertPalette), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(AvoidPath), SIG_EVERYWHERE, "ii(.*)", NULL, NULL },
+ { MAP_CALL(BaseSetter), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(CanBeHere), SIG_EVERYWHERE, "o(l)", NULL, NULL },
+#ifdef ENABLE_SCI32
+ { "CantBeHere", kCantBeHere32, SIG_SCI32, SIGFOR_ALL, "ol", NULL, NULL },
+#endif
+ { MAP_CALL(CantBeHere), SIG_EVERYWHERE, "o(l)", NULL, NULL },
+ { MAP_CALL(CelHigh), SIG_EVERYWHERE, "ii(i)", NULL, kCelHigh_workarounds },
+ { MAP_CALL(CelWide), SIG_EVERYWHERE, "ii(i)", NULL, kCelWide_workarounds },
+ { MAP_CALL(CheckFreeSpace), SIG_SCI32, SIGFOR_ALL, "r.*", NULL, NULL },
+ { MAP_CALL(CheckFreeSpace), SIG_SCI11, SIGFOR_ALL, "r(i)", NULL, NULL },
+ { MAP_CALL(CheckFreeSpace), SIG_EVERYWHERE, "r", NULL, NULL },
+ { MAP_CALL(CheckSaveGame), SIG_EVERYWHERE, ".*", NULL, NULL },
+ { MAP_CALL(Clone), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(CoordPri), SIG_EVERYWHERE, "i(i)", NULL, NULL },
+ { MAP_CALL(CosDiv), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(DeleteKey), SIG_EVERYWHERE, "l.", NULL, NULL },
+ { MAP_CALL(DeviceInfo), SIG_EVERYWHERE, "i(r)(r)(i)", NULL, NULL }, // subop
+ { MAP_CALL(Display), SIG_EVERYWHERE, "[ir]([ir!]*)", NULL, NULL },
+ // ^ we allow invalid references here, because kDisplay gets called with those in e.g. pq3 during intro
+ // restoreBits() checks and skips invalid handles, so that's fine. Sierra SCI behaved the same
+ { MAP_CALL(DirLoop), SIG_EVERYWHERE, "oi", NULL, NULL },
+ { MAP_CALL(DisposeClone), SIG_EVERYWHERE, "o", NULL, NULL },
+ { 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(DoAvoider), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(DoBresen), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(DoSound), SIG_EVERYWHERE, "i(.*)", kDoSound_subops, NULL },
+ { MAP_CALL(DoSync), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
+ { MAP_CALL(DrawCel), SIG_SCI11, SIGFOR_PC, "iiiii(i)(i)([ri])", NULL, NULL }, // reference for kq6 hires
+ { MAP_CALL(DrawCel), SIG_EVERYWHERE, "iiiii(i)(i)", NULL, NULL },
+ { MAP_CALL(DrawControl), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(DrawMenuBar), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(DrawPic), SIG_EVERYWHERE, "i(i)(i)(i)", NULL, NULL },
+ { MAP_CALL(DrawStatus), SIG_EVERYWHERE, "[r0](i)(i)", NULL, NULL },
+ { MAP_CALL(EditControl), SIG_EVERYWHERE, "[o0][o0]", NULL, NULL },
+ { MAP_CALL(Empty), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(EmptyList), SIG_EVERYWHERE, "l", NULL, NULL },
+ { MAP_CALL(FClose), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(FGets), SIG_EVERYWHERE, "rii", NULL, NULL },
+ { MAP_CALL(FOpen), SIG_EVERYWHERE, "ri", NULL, NULL },
+ { MAP_CALL(FPuts), SIG_EVERYWHERE, "ir", NULL, NULL },
+ { MAP_CALL(FileIO), SIG_EVERYWHERE, "i(.*)", kFileIO_subops, NULL },
+ { MAP_CALL(FindKey), SIG_EVERYWHERE, "l.", NULL, kFindKey_workarounds },
+ { MAP_CALL(FirstNode), SIG_EVERYWHERE, "[l0]", NULL, NULL },
+ { MAP_CALL(FlushResources), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(Format), SIG_EVERYWHERE, "r(.*)", NULL, NULL },
+ { MAP_CALL(GameIsRestarting), SIG_EVERYWHERE, "(i)", NULL, NULL },
+ { MAP_CALL(GetAngle), SIG_EVERYWHERE, "iiii", NULL, kGetAngle_workarounds },
+ { MAP_CALL(GetCWD), SIG_EVERYWHERE, "r", NULL, NULL },
+ { MAP_CALL(GetDistance), SIG_EVERYWHERE, "ii(i)(i)(i)(i)", NULL, NULL },
+ { MAP_CALL(GetEvent), SIG_SCIALL, SIGFOR_MAC, "io(i*)", NULL, NULL },
+ { MAP_CALL(GetEvent), SIG_EVERYWHERE, "io", NULL, NULL },
+ { MAP_CALL(GetFarText), SIG_EVERYWHERE, "ii[r0]", NULL, NULL },
+ { MAP_CALL(GetMenu), SIG_EVERYWHERE, "i.", NULL, NULL },
+ { MAP_CALL(GetMessage), SIG_EVERYWHERE, "iiir", NULL, NULL },
+ { MAP_CALL(GetPort), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(GetSaveDir), SIG_SCI32, SIGFOR_ALL, "(r*)", NULL, NULL },
+ { MAP_CALL(GetSaveDir), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(GetSaveFiles), SIG_EVERYWHERE, "rrr", NULL, NULL },
+ { MAP_CALL(GetTime), SIG_EVERYWHERE, "(i)", NULL, NULL },
+ { MAP_CALL(GlobalToLocal), SIG_SCI32, SIGFOR_ALL, "oo", NULL, NULL },
+ { MAP_CALL(GlobalToLocal), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(Graph), SIG_EVERYWHERE, NULL, kGraph_subops, NULL },
+ { MAP_CALL(HaveMouse), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(HiliteControl), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(InitBresen), SIG_EVERYWHERE, "o(i)", NULL, NULL },
+ { MAP_CALL(Intersections), SIG_EVERYWHERE, "iiiiriiiri", NULL, NULL },
+ { MAP_CALL(IsItSkip), SIG_EVERYWHERE, "iiiii", NULL, NULL },
+ { MAP_CALL(IsObject), SIG_EVERYWHERE, ".", NULL, kIsObject_workarounds },
+ { MAP_CALL(Joystick), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
+ { MAP_CALL(LastNode), SIG_EVERYWHERE, "l", NULL, NULL },
+ { MAP_CALL(Load), SIG_EVERYWHERE, "ii(i*)", NULL, NULL },
+ { MAP_CALL(LocalToGlobal), SIG_SCI32, SIGFOR_ALL, "oo", NULL, NULL },
+ { MAP_CALL(LocalToGlobal), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(Lock), SIG_EVERYWHERE, "ii(i)", NULL, NULL },
+ { MAP_CALL(MapKeyToDir), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(Memory), SIG_EVERYWHERE, "i(.*)", NULL, kMemory_workarounds }, // subop
+ { MAP_CALL(MemoryInfo), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(MemorySegment), SIG_EVERYWHERE, "ir(i)", NULL, NULL }, // subop
+ { MAP_CALL(MenuSelect), SIG_EVERYWHERE, "o(i)", NULL, NULL },
+ { MAP_CALL(MergePoly), SIG_EVERYWHERE, "rli", NULL, NULL },
+ { MAP_CALL(Message), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
+ { MAP_CALL(MoveCursor), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(NewList), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(NewNode), SIG_EVERYWHERE, "..", NULL, NULL },
+ { MAP_CALL(NewWindow), SIG_SCIALL, SIGFOR_MAC, ".*", NULL, NULL },
+ { MAP_CALL(NewWindow), SIG_SCI0, SIGFOR_ALL, "iiii[r0]i(i)(i)(i)", NULL, NULL },
+ { MAP_CALL(NewWindow), SIG_SCI1, SIGFOR_ALL, "iiii[ir]i(i)(i)([ir])(i)(i)(i)(i)", NULL, NULL },
+ { MAP_CALL(NewWindow), SIG_SCI11, SIGFOR_ALL, "iiiiiiii[r0]i(i)(i)(i)", NULL, kNewWindow_workarounds },
+ { MAP_CALL(NextNode), SIG_EVERYWHERE, "n", NULL, NULL },
+ { MAP_CALL(NodeValue), SIG_EVERYWHERE, "[n0]", NULL, NULL },
+ { MAP_CALL(NumCels), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(NumLoops), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(OnControl), SIG_EVERYWHERE, "ii(i)(i)(i)", NULL, NULL },
+ { MAP_CALL(PalVary), SIG_EVERYWHERE, "i(i*)", kPalVary_subops, NULL },
+ { MAP_CALL(Palette), SIG_EVERYWHERE, "i(.*)", kPalette_subops, NULL },
+ { MAP_CALL(Parse), SIG_EVERYWHERE, "ro", NULL, NULL },
+ { MAP_CALL(PicNotValid), SIG_EVERYWHERE, "(i)", NULL, NULL },
+ { MAP_CALL(Platform), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(Portrait), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop
+ { MAP_CALL(PrevNode), SIG_EVERYWHERE, "n", NULL, NULL },
+ { 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, NULL },
+ { MAP_CALL(ResCheck), SIG_EVERYWHERE, "ii(iiii)", NULL, NULL },
+ { MAP_CALL(RespondsTo), SIG_EVERYWHERE, ".i", NULL, NULL },
+ { MAP_CALL(RestartGame), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(RestoreGame), SIG_EVERYWHERE, "rir", NULL, NULL },
+ { MAP_CALL(Said), SIG_EVERYWHERE, "[r0]", NULL, NULL },
+ { MAP_CALL(SaveGame), SIG_EVERYWHERE, "rir(r)", NULL, NULL },
+ { MAP_CALL(ScriptID), SIG_EVERYWHERE, "[io](i)", NULL, NULL },
+ { MAP_CALL(SetCursor), SIG_SCI21, SIGFOR_ALL, "i(i)([io])(i*)", NULL, NULL },
+ // TODO: SCI2.1 may supply an object optionally (mother goose sci21 right on startup) - find out why
+ { MAP_CALL(SetCursor), SIG_EVERYWHERE, "i(i*)", NULL, NULL },
+ { MAP_CALL(SetDebug), SIG_EVERYWHERE, "(i*)", NULL, NULL },
+ { MAP_CALL(SetJump), SIG_EVERYWHERE, "oiii", NULL, NULL },
+ { MAP_CALL(SetMenu), SIG_EVERYWHERE, "i(.*)", NULL, NULL },
+ { MAP_CALL(SetNowSeen), SIG_EVERYWHERE, "o(i)", NULL, NULL },
+ { MAP_CALL(SetPort), SIG_EVERYWHERE, "i(iiiii)(i)", NULL, kSetPort_workarounds },
+ { MAP_CALL(SetQuitStr), SIG_EVERYWHERE, "r", NULL, NULL },
+ { MAP_CALL(SetSynonyms), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(SetVideoMode), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(ShakeScreen), SIG_EVERYWHERE, "(i)(i)", NULL, NULL },
+ { MAP_CALL(ShowMovie), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(SinDiv), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(Sort), SIG_EVERYWHERE, "ooo", NULL, NULL },
+ { MAP_CALL(Sqrt), SIG_EVERYWHERE, "i", NULL, NULL },
+ { MAP_CALL(StrAt), SIG_EVERYWHERE, "ri(i)", NULL, NULL },
+ { MAP_CALL(StrCat), SIG_EVERYWHERE, "rr", NULL, NULL },
+ { MAP_CALL(StrCmp), SIG_EVERYWHERE, "rr(i)", NULL, NULL },
+ { MAP_CALL(StrCpy), SIG_EVERYWHERE, "r[r0](i)", NULL, NULL },
+ { MAP_CALL(StrEnd), SIG_EVERYWHERE, "r", NULL, NULL },
+ { MAP_CALL(StrLen), SIG_EVERYWHERE, "[r0]", NULL, NULL },
+ { MAP_CALL(StrSplit), SIG_EVERYWHERE, "rr[r0]", NULL, NULL },
+ { MAP_CALL(TextColors), SIG_EVERYWHERE, "(i*)", NULL, NULL },
+ { MAP_CALL(TextFonts), SIG_EVERYWHERE, "(i*)", NULL, NULL },
+ { MAP_CALL(TextSize), SIG_SCIALL, SIGFOR_MAC, "r[r0]i(i)(r0)(i)", NULL, NULL },
+ { MAP_CALL(TextSize), SIG_EVERYWHERE, "r[r0]i(i)(r0)", NULL, NULL },
+ { MAP_CALL(TimesCos), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { "CosMult", kTimesCos, SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(TimesCot), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(TimesSin), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { "SinMult", kTimesSin, SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(TimesTan), SIG_EVERYWHERE, "ii", NULL, NULL },
+ { MAP_CALL(UnLoad), SIG_EVERYWHERE, "i[ri]", NULL, kUnLoad_workarounds },
+ { MAP_CALL(ValidPath), SIG_EVERYWHERE, "r", NULL, NULL },
+ { MAP_CALL(Wait), SIG_EVERYWHERE, "i", NULL, NULL },
+
+#ifdef ENABLE_SCI32
+ // SCI2 Kernel Functions
+ { MAP_CALL(AddPlane), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(AddScreenItem), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(Array), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(CreateTextBitmap), SIG_EVERYWHERE, "i(.*)", NULL, NULL },
+ { MAP_CALL(DeletePlane), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(DeleteScreenItem), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(FrameOut), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(GetHighPlanePri), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(InPolygon), SIG_EVERYWHERE, "iio", NULL, NULL },
+ { MAP_CALL(IsHiRes), SIG_EVERYWHERE, "", NULL, NULL },
+ { MAP_CALL(ListAllTrue), SIG_EVERYWHERE, "li(.*)", NULL, NULL },
+ { MAP_CALL(ListAt), SIG_EVERYWHERE, "li", NULL, NULL },
+ { 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 },
+ { MAP_CALL(OnMe), SIG_EVERYWHERE, "iio(.*)", NULL, NULL },
+ { MAP_CALL(RepaintPlane), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(String), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(UpdatePlane), SIG_EVERYWHERE, "o", NULL, NULL },
+ { MAP_CALL(UpdateScreenItem), SIG_EVERYWHERE, "o", NULL, NULL },
+
+ // SCI2.1 Kernel Functions
+ { MAP_CALL(CD), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(IsOnMe), SIG_EVERYWHERE, "iio(.*)", NULL, NULL },
+ { MAP_CALL(List), SIG_SCI21, SIGFOR_ALL, "(.*)", kList_subops, NULL },
+ { MAP_CALL(MulDiv), SIG_EVERYWHERE, "iii", NULL, NULL },
+ { MAP_CALL(PlayVMD), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(Robot), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(Save), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(Text), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(AddPicAt), SIG_EVERYWHERE, "oiii", NULL, NULL },
+ { NULL, NULL, SIG_EVERYWHERE, NULL, NULL, NULL }
+#endif
+};
+
+/** Default kernel name table. */
+static const char *s_defaultKernelNames[] = {
+ /*0x00*/ "Load",
+ /*0x01*/ "UnLoad",
+ /*0x02*/ "ScriptID",
+ /*0x03*/ "DisposeScript",
+ /*0x04*/ "Clone",
+ /*0x05*/ "DisposeClone",
+ /*0x06*/ "IsObject",
+ /*0x07*/ "RespondsTo",
+ /*0x08*/ "DrawPic",
+ /*0x09*/ "Dummy", // Show
+ /*0x0a*/ "PicNotValid",
+ /*0x0b*/ "Animate",
+ /*0x0c*/ "SetNowSeen",
+ /*0x0d*/ "NumLoops",
+ /*0x0e*/ "NumCels",
+ /*0x0f*/ "CelWide",
+ /*0x10*/ "CelHigh",
+ /*0x11*/ "DrawCel",
+ /*0x12*/ "AddToPic",
+ /*0x13*/ "NewWindow",
+ /*0x14*/ "GetPort",
+ /*0x15*/ "SetPort",
+ /*0x16*/ "DisposeWindow",
+ /*0x17*/ "DrawControl",
+ /*0x18*/ "HiliteControl",
+ /*0x19*/ "EditControl",
+ /*0x1a*/ "TextSize",
+ /*0x1b*/ "Display",
+ /*0x1c*/ "GetEvent",
+ /*0x1d*/ "GlobalToLocal",
+ /*0x1e*/ "LocalToGlobal",
+ /*0x1f*/ "MapKeyToDir",
+ /*0x20*/ "DrawMenuBar",
+ /*0x21*/ "MenuSelect",
+ /*0x22*/ "AddMenu",
+ /*0x23*/ "DrawStatus",
+ /*0x24*/ "Parse",
+ /*0x25*/ "Said",
+ /*0x26*/ "SetSynonyms", // Portrait (KQ6 hires)
+ /*0x27*/ "HaveMouse",
+ /*0x28*/ "SetCursor",
+ // FOpen (SCI0)
+ // FPuts (SCI0)
+ // FGets (SCI0)
+ // FClose (SCI0)
+ /*0x29*/ "SaveGame",
+ /*0x2a*/ "RestoreGame",
+ /*0x2b*/ "RestartGame",
+ /*0x2c*/ "GameIsRestarting",
+ /*0x2d*/ "DoSound",
+ /*0x2e*/ "NewList",
+ /*0x2f*/ "DisposeList",
+ /*0x30*/ "NewNode",
+ /*0x31*/ "FirstNode",
+ /*0x32*/ "LastNode",
+ /*0x33*/ "EmptyList",
+ /*0x34*/ "NextNode",
+ /*0x35*/ "PrevNode",
+ /*0x36*/ "NodeValue",
+ /*0x37*/ "AddAfter",
+ /*0x38*/ "AddToFront",
+ /*0x39*/ "AddToEnd",
+ /*0x3a*/ "FindKey",
+ /*0x3b*/ "DeleteKey",
+ /*0x3c*/ "Random",
+ /*0x3d*/ "Abs",
+ /*0x3e*/ "Sqrt",
+ /*0x3f*/ "GetAngle",
+ /*0x40*/ "GetDistance",
+ /*0x41*/ "Wait",
+ /*0x42*/ "GetTime",
+ /*0x43*/ "StrEnd",
+ /*0x44*/ "StrCat",
+ /*0x45*/ "StrCmp",
+ /*0x46*/ "StrLen",
+ /*0x47*/ "StrCpy",
+ /*0x48*/ "Format",
+ /*0x49*/ "GetFarText",
+ /*0x4a*/ "ReadNumber",
+ /*0x4b*/ "BaseSetter",
+ /*0x4c*/ "DirLoop",
+ /*0x4d*/ "CanBeHere", // CantBeHere in newer SCI versions
+ /*0x4e*/ "OnControl",
+ /*0x4f*/ "InitBresen",
+ /*0x50*/ "DoBresen",
+ /*0x51*/ "Platform", // DoAvoider (SCI0)
+ /*0x52*/ "SetJump",
+ /*0x53*/ "SetDebug",
+ /*0x54*/ "Dummy", // InspectObj
+ /*0x55*/ "Dummy", // ShowSends
+ /*0x56*/ "Dummy", // ShowObjs
+ /*0x57*/ "Dummy", // ShowFree
+ /*0x58*/ "MemoryInfo",
+ /*0x59*/ "Dummy", // StackUsage
+ /*0x5a*/ "Dummy", // Profiler
+ /*0x5b*/ "GetMenu",
+ /*0x5c*/ "SetMenu",
+ /*0x5d*/ "GetSaveFiles",
+ /*0x5e*/ "GetCWD",
+ /*0x5f*/ "CheckFreeSpace",
+ /*0x60*/ "ValidPath",
+ /*0x61*/ "CoordPri",
+ /*0x62*/ "StrAt",
+ /*0x63*/ "DeviceInfo",
+ /*0x64*/ "GetSaveDir",
+ /*0x65*/ "CheckSaveGame",
+ /*0x66*/ "ShakeScreen",
+ /*0x67*/ "FlushResources",
+ /*0x68*/ "SinMult",
+ /*0x69*/ "CosMult",
+ /*0x6a*/ "SinDiv",
+ /*0x6b*/ "CosDiv",
+ /*0x6c*/ "Graph",
+ /*0x6d*/ "Joystick",
+ // End of kernel function table for SCI0
+ /*0x6e*/ "Dummy", // ShiftScreen
+ /*0x6f*/ "Palette",
+ /*0x70*/ "MemorySegment",
+ /*0x71*/ "Intersections", // MoveCursor (SCI1 late), PalVary (SCI1.1)
+ /*0x72*/ "Memory",
+ /*0x73*/ "Dummy", // ListOps
+ /*0x74*/ "FileIO",
+ /*0x75*/ "DoAudio",
+ /*0x76*/ "DoSync",
+ /*0x77*/ "AvoidPath",
+ /*0x78*/ "Sort", // StrSplit (SCI01)
+ /*0x79*/ "Dummy", // ATan
+ /*0x7a*/ "Lock",
+ /*0x7b*/ "StrSplit",
+ /*0x7c*/ "GetMessage", // Message (SCI1.1)
+ /*0x7d*/ "IsItSkip",
+ /*0x7e*/ "MergePoly",
+ /*0x7f*/ "ResCheck",
+ /*0x80*/ "AssertPalette",
+ /*0x81*/ "TextColors",
+ /*0x82*/ "TextFonts",
+ /*0x83*/ "Dummy", // Record
+ /*0x84*/ "Dummy", // PlayBack
+ /*0x85*/ "ShowMovie",
+ /*0x86*/ "SetVideoMode",
+ /*0x87*/ "SetQuitStr",
+ /*0x88*/ "Dummy" // DbugStr
+};
+
+#ifdef ENABLE_SCI32
+
+// NOTE: 0x72-0x79, 0x85-0x86, 0x88 are from the GK2 demo (which has debug support) and are
+// just Dummy in other SCI2 games.
+static const char *sci2_default_knames[] = {
+ /*0x00*/ "Load",
+ /*0x01*/ "UnLoad",
+ /*0x02*/ "ScriptID",
+ /*0x03*/ "DisposeScript",
+ /*0x04*/ "Lock",
+ /*0x05*/ "ResCheck",
+ /*0x06*/ "Purge",
+ /*0x07*/ "Clone",
+ /*0x08*/ "DisposeClone",
+ /*0x09*/ "RespondsTo",
+ /*0x0a*/ "SetNowSeen",
+ /*0x0b*/ "NumLoops",
+ /*0x0c*/ "NumCels",
+ /*0x0d*/ "CelWide",
+ /*0x0e*/ "CelHigh",
+ /*0x0f*/ "GetHighPlanePri",
+ /*0x10*/ "GetHighItemPri",
+ /*0x11*/ "ShakeScreen",
+ /*0x12*/ "OnMe",
+ /*0x13*/ "ShowMovie",
+ /*0x14*/ "SetVideoMode",
+ /*0x15*/ "AddScreenItem",
+ /*0x16*/ "DeleteScreenItem",
+ /*0x17*/ "UpdateScreenItem",
+ /*0x18*/ "FrameOut",
+ /*0x19*/ "AddPlane",
+ /*0x1a*/ "DeletePlane",
+ /*0x1b*/ "UpdatePlane",
+ /*0x1c*/ "RepaintPlane",
+ /*0x1d*/ "SetShowStyle",
+ /*0x1e*/ "ShowStylePercent",
+ /*0x1f*/ "SetScroll",
+ /*0x20*/ "AddMagnify",
+ /*0x21*/ "DeleteMagnify",
+ /*0x22*/ "IsHiRes",
+ /*0x23*/ "Graph",
+ /*0x24*/ "InvertRect",
+ /*0x25*/ "TextSize",
+ /*0x26*/ "Message",
+ /*0x27*/ "TextColors",
+ /*0x28*/ "TextFonts",
+ /*0x29*/ "Dummy",
+ /*0x2a*/ "SetQuitStr",
+ /*0x2b*/ "EditText",
+ /*0x2c*/ "InputText",
+ /*0x2d*/ "CreateTextBitmap",
+ /*0x2e*/ "DisposeTextBitmap",
+ /*0x2f*/ "GetEvent",
+ /*0x30*/ "GlobalToLocal",
+ /*0x31*/ "LocalToGlobal",
+ /*0x32*/ "MapKeyToDir",
+ /*0x33*/ "HaveMouse",
+ /*0x34*/ "SetCursor",
+ /*0x35*/ "VibrateMouse",
+ /*0x36*/ "SaveGame",
+ /*0x37*/ "RestoreGame",
+ /*0x38*/ "RestartGame",
+ /*0x39*/ "GameIsRestarting",
+ /*0x3a*/ "MakeSaveCatName",
+ /*0x3b*/ "MakeSaveFileName",
+ /*0x3c*/ "GetSaveFiles",
+ /*0x3d*/ "GetSaveDir",
+ /*0x3e*/ "CheckSaveGame",
+ /*0x3f*/ "CheckFreeSpace",
+ /*0x40*/ "DoSound",
+ /*0x41*/ "DoAudio",
+ /*0x42*/ "DoSync",
+ /*0x43*/ "NewList",
+ /*0x44*/ "DisposeList",
+ /*0x45*/ "NewNode",
+ /*0x46*/ "FirstNode",
+ /*0x47*/ "LastNode",
+ /*0x48*/ "EmptyList",
+ /*0x49*/ "NextNode",
+ /*0x4a*/ "PrevNode",
+ /*0x4b*/ "NodeValue",
+ /*0x4c*/ "AddAfter",
+ /*0x4d*/ "AddToFront",
+ /*0x4e*/ "AddToEnd",
+ /*0x4f*/ "Dummy",
+ /*0x50*/ "Dummy",
+ /*0x51*/ "FindKey",
+ /*0x52*/ "Dummy",
+ /*0x53*/ "Dummy",
+ /*0x54*/ "Dummy",
+ /*0x55*/ "DeleteKey",
+ /*0x56*/ "Dummy",
+ /*0x57*/ "Dummy",
+ /*0x58*/ "ListAt",
+ /*0x59*/ "ListIndexOf",
+ /*0x5a*/ "ListEachElementDo",
+ /*0x5b*/ "ListFirstTrue",
+ /*0x5c*/ "ListAllTrue",
+ /*0x5d*/ "Random",
+ /*0x5e*/ "Abs",
+ /*0x5f*/ "Sqrt",
+ /*0x60*/ "GetAngle",
+ /*0x61*/ "GetDistance",
+ /*0x62*/ "ATan",
+ /*0x63*/ "SinMult",
+ /*0x64*/ "CosMult",
+ /*0x65*/ "SinDiv",
+ /*0x66*/ "CosDiv",
+ /*0x67*/ "GetTime",
+ /*0x68*/ "Platform",
+ /*0x69*/ "BaseSetter",
+ /*0x6a*/ "DirLoop",
+ /*0x6b*/ "CantBeHere",
+ /*0x6c*/ "InitBresen",
+ /*0x6d*/ "DoBresen",
+ /*0x6e*/ "SetJump",
+ /*0x6f*/ "AvoidPath",
+ /*0x70*/ "InPolygon",
+ /*0x71*/ "MergePoly",
+ /*0x72*/ "SetDebug",
+ /*0x73*/ "InspectObject",
+ /*0x74*/ "MemoryInfo",
+ /*0x75*/ "Profiler",
+ /*0x76*/ "Record",
+ /*0x77*/ "PlayBack",
+ /*0x78*/ "MonoOut",
+ /*0x79*/ "SetFatalStr",
+ /*0x7a*/ "GetCWD",
+ /*0x7b*/ "ValidPath",
+ /*0x7c*/ "FileIO",
+ /*0x7d*/ "Dummy",
+ /*0x7e*/ "DeviceInfo",
+ /*0x7f*/ "Palette",
+ /*0x80*/ "PalVary",
+ /*0x81*/ "PalCycle",
+ /*0x82*/ "Array",
+ /*0x83*/ "String",
+ /*0x84*/ "RemapColors",
+ /*0x85*/ "IntegrityChecking",
+ /*0x86*/ "CheckIntegrity",
+ /*0x87*/ "ObjectIntersect",
+ /*0x88*/ "MarkMemory",
+ /*0x89*/ "TextWidth",
+ /*0x8a*/ "PointSize",
+
+ // GK2 Demo (and similar) only kernel functions
+ /*0x8b*/ "AddLine",
+ /*0x8c*/ "DeleteLine",
+ /*0x8d*/ "UpdateLine",
+ /*0x8e*/ "AddPolygon",
+ /*0x8f*/ "DeletePolygon",
+ /*0x90*/ "UpdatePolygon",
+ /*0x91*/ "Bitmap",
+ /*0x92*/ "ScrollWindow",
+ /*0x93*/ "SetFontRes",
+ /*0x94*/ "MovePlaneItems",
+ /*0x95*/ "PreloadResource",
+ /*0x96*/ "Dummy",
+ /*0x97*/ "ResourceTrack",
+ /*0x98*/ "CheckCDisc",
+ /*0x99*/ "GetSaveCDisc",
+ /*0x9a*/ "TestPoly",
+ /*0x9b*/ "WinHelp",
+ /*0x9c*/ "LoadChunk",
+ /*0x9d*/ "SetPalStyleRange",
+ /*0x9e*/ "AddPicAt",
+ /*0x9f*/ "MessageBox"
+};
+
+static const char *sci21_default_knames[] = {
+ /*0x00*/ "Load",
+ /*0x01*/ "UnLoad",
+ /*0x02*/ "ScriptID",
+ /*0x03*/ "DisposeScript",
+ /*0x04*/ "Lock",
+ /*0x05*/ "ResCheck",
+ /*0x06*/ "Purge",
+ /*0x07*/ "SetLanguage",
+ /*0x08*/ "Dummy",
+ /*0x09*/ "Dummy",
+ /*0x0a*/ "Clone",
+ /*0x0b*/ "DisposeClone",
+ /*0x0c*/ "RespondsTo",
+ /*0x0d*/ "FindSelector",
+ /*0x0e*/ "FindClass",
+ /*0x0f*/ "Dummy",
+ /*0x10*/ "Dummy",
+ /*0x11*/ "Dummy",
+ /*0x12*/ "Dummy",
+ /*0x13*/ "Dummy",
+ /*0x14*/ "SetNowSeen",
+ /*0x15*/ "NumLoops",
+ /*0x16*/ "NumCels",
+ /*0x17*/ "IsOnMe",
+ /*0x18*/ "AddMagnify",
+ /*0x19*/ "DeleteMagnify",
+ /*0x1a*/ "CelRect",
+ /*0x1b*/ "BaseLineSpan",
+ /*0x1c*/ "CelWide",
+ /*0x1d*/ "CelHigh",
+ /*0x1e*/ "AddScreenItem",
+ /*0x1f*/ "DeleteScreenItem",
+ /*0x20*/ "UpdateScreenItem",
+ /*0x21*/ "FrameOut",
+ /*0x22*/ "CelInfo",
+ /*0x23*/ "Bitmap",
+ /*0x24*/ "CelLink",
+ /*0x25*/ "Dummy",
+ /*0x26*/ "Dummy",
+ /*0x27*/ "Dummy",
+ /*0x28*/ "AddPlane",
+ /*0x29*/ "DeletePlane",
+ /*0x2a*/ "UpdatePlane",
+ /*0x2b*/ "RepaintPlane",
+ /*0x2c*/ "GetHighPlanePri",
+ /*0x2d*/ "GetHighItemPri",
+ /*0x2e*/ "SetShowStyle",
+ /*0x2f*/ "ShowStylePercent",
+ /*0x30*/ "SetScroll",
+ /*0x31*/ "MovePlaneItems",
+ /*0x32*/ "ShakeScreen",
+ /*0x33*/ "Dummy",
+ /*0x34*/ "Dummy",
+ /*0x35*/ "Dummy",
+ /*0x36*/ "Dummy",
+ /*0x37*/ "IsHiRes",
+ /*0x38*/ "SetVideoMode",
+ /*0x39*/ "ShowMovie",
+ /*0x3a*/ "Robot",
+ /*0x3b*/ "CreateTextBitmap",
+ /*0x3c*/ "Random",
+ /*0x3d*/ "Abs",
+ /*0x3e*/ "Sqrt",
+ /*0x3f*/ "GetAngle",
+ /*0x40*/ "GetDistance",
+ /*0x41*/ "ATan",
+ /*0x42*/ "SinMult",
+ /*0x43*/ "CosMult",
+ /*0x44*/ "SinDiv",
+ /*0x45*/ "CosDiv",
+ /*0x46*/ "Text",
+ /*0x47*/ "Dummy",
+ /*0x48*/ "Message",
+ /*0x49*/ "Font",
+ /*0x4a*/ "EditText",
+ /*0x4b*/ "InputText",
+ /*0x4c*/ "ScrollWindow",
+ /*0x4d*/ "Dummy",
+ /*0x4e*/ "Dummy",
+ /*0x4f*/ "Dummy",
+ /*0x50*/ "GetEvent",
+ /*0x51*/ "GlobalToLocal",
+ /*0x52*/ "LocalToGlobal",
+ /*0x53*/ "MapKeyToDir",
+ /*0x54*/ "HaveMouse",
+ /*0x55*/ "SetCursor",
+ /*0x56*/ "VibrateMouse",
+ /*0x57*/ "Dummy",
+ /*0x58*/ "Dummy",
+ /*0x59*/ "Dummy",
+ /*0x5a*/ "List",
+ /*0x5b*/ "Array",
+ /*0x5c*/ "String",
+ /*0x5d*/ "FileIO",
+ /*0x5e*/ "BaseSetter",
+ /*0x5f*/ "DirLoop",
+ /*0x60*/ "CantBeHere",
+ /*0x61*/ "InitBresen",
+ /*0x62*/ "DoBresen",
+ /*0x63*/ "SetJump",
+ /*0x64*/ "AvoidPath",
+ /*0x65*/ "InPolygon",
+ /*0x66*/ "MergePoly",
+ /*0x67*/ "ObjectIntersect",
+ /*0x68*/ "Dummy",
+ /*0x69*/ "MemoryInfo",
+ /*0x6a*/ "DeviceInfo",
+ /*0x6b*/ "Palette",
+ /*0x6c*/ "PalVary",
+ /*0x6d*/ "PalCycle",
+ /*0x6e*/ "RemapColors",
+ /*0x6f*/ "AddLine",
+ /*0x70*/ "DeleteLine",
+ /*0x71*/ "UpdateLine",
+ /*0x72*/ "AddPolygon",
+ /*0x73*/ "DeletePolygon",
+ /*0x74*/ "UpdatePolygon",
+ /*0x75*/ "DoSound",
+ /*0x76*/ "DoAudio",
+ /*0x77*/ "DoSync",
+ /*0x78*/ "Save",
+ /*0x79*/ "GetTime",
+ /*0x7a*/ "Platform",
+ /*0x7b*/ "CD",
+ /*0x7c*/ "SetQuitStr",
+ /*0x7d*/ "GetConfig",
+ /*0x7e*/ "Table",
+ /*0x7f*/ "WinHelp", // Windows only
+ /*0x80*/ "Dummy",
+ /*0x81*/ "Dummy",
+ /*0x82*/ "Dummy",
+ /*0x83*/ "PrintDebug", // used by Shivers 2 (demo and full)
+ /*0x84*/ "Dummy",
+ /*0x85*/ "Dummy",
+ /*0x86*/ "Dummy",
+ /*0x87*/ "Dummy",
+ /*0x88*/ "Dummy",
+ /*0x89*/ "Dummy",
+ /*0x8a*/ "LoadChunk",
+ /*0x8b*/ "SetPalStyleRange",
+ /*0x8c*/ "AddPicAt",
+ /*0x8d*/ "Dummy",
+ /*0x8e*/ "NewRoom",
+ /*0x8f*/ "Dummy",
+ /*0x90*/ "Priority",
+ /*0x91*/ "MorphOn",
+ /*0x92*/ "PlayVMD",
+ /*0x93*/ "SetHotRectangles",
+ /*0x94*/ "MulDiv",
+ /*0x95*/ "GetSierraProfileInt", // Windows only
+ /*0x96*/ "GetSierraProfileString", // Windows only
+ /*0x97*/ "SetWindowsOption", // Windows only
+ /*0x98*/ "GetWindowsOption", // Windows only
+ /*0x99*/ "WinDLL", // Windows only
+ /*0x9a*/ "Dummy",
+ /*0x9b*/ "Dummy",
+ /*0x9c*/ "DeletePic"
+};
+
+#endif
+
+#define END Script_None
+
+opcode_format g_opcode_formats[128][4] = {
+ /*00*/
+ {Script_None}, {Script_None}, {Script_None}, {Script_None},
+ /*04*/
+ {Script_None}, {Script_None}, {Script_None}, {Script_None},
+ /*08*/
+ {Script_None}, {Script_None}, {Script_None}, {Script_None},
+ /*0C*/
+ {Script_None}, {Script_None}, {Script_None}, {Script_None},
+ /*10*/
+ {Script_None}, {Script_None}, {Script_None}, {Script_None},
+ /*14*/
+ {Script_None}, {Script_None}, {Script_None}, {Script_SRelative, END},
+ /*18*/
+ {Script_SRelative, END}, {Script_SRelative, END}, {Script_SVariable, END}, {Script_None},
+ /*1C*/
+ {Script_SVariable, END}, {Script_None}, {Script_None}, {Script_Variable, END},
+ /*20*/
+ {Script_SRelative, Script_Byte, END}, {Script_Variable, Script_Byte, END}, {Script_Variable, Script_Byte, END}, {Script_Variable, Script_SVariable, Script_Byte, END},
+ /*24 (24=ret)*/
+ {Script_End}, {Script_Byte, END}, {Script_Invalid}, {Script_Invalid},
+ /*28*/
+ {Script_Variable, END}, {Script_Invalid}, {Script_Byte, END}, {Script_Variable, Script_Byte, END},
+ /*2C*/
+ {Script_SVariable, END}, {Script_SVariable, Script_Variable, END}, {Script_None}, {Script_Invalid},
+ /*30*/
+ {Script_None}, {Script_Property, END}, {Script_Property, END}, {Script_Property, END},
+ /*34*/
+ {Script_Property, END}, {Script_Property, END}, {Script_Property, END}, {Script_Property, END},
+ /*38*/
+ {Script_Property, END}, {Script_SRelative, END}, {Script_SRelative, END}, {Script_None},
+ /*3C*/
+ {Script_None}, {Script_None}, {Script_None}, {Script_Word},
+ /*40-4F*/
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ /*50-5F*/
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ /*60-6F*/
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ /*70-7F*/
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
+ {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}
+};
+#undef END
+
+} // End of namespace Sci
+
+#endif // SCI_ENGINE_KERNEL_TABLES_H
diff --git a/engines/sci/engine/kevent.cpp b/engines/sci/engine/kevent.cpp
index 7547ad5ab6..3395811700 100644
--- a/engines/sci/engine/kevent.cpp
+++ b/engines/sci/engine/kevent.cpp
@@ -50,6 +50,10 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) {
// Limit the mouse cursor position, if necessary
g_sci->_gfxCursor->refreshPosition();
mousePos = g_sci->_gfxCursor->getPosition();
+#ifdef ENABLE_SCI32
+ if (getSciVersion() >= SCI_VERSION_2_1)
+ g_sci->_gfxCoordAdjuster->fromDisplayToScript(mousePos.y, mousePos.x);
+#endif
// If there's a simkey pending, and the game wants a keyboard event, use the
// simkey instead of a normal event
@@ -150,11 +154,11 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) {
}
if (g_sci->_features->detectDoSoundType() <= SCI_VERSION_0_LATE) {
- // If we're running a SCI0 game, update the sound cues, to compensate
- // for the fact that SCI0 does not poll to update the sound cues itself,
- // like SCI01 and later do with cmdUpdateSoundCues. kGetEvent is called
- // quite often, so emulate the SCI01 behavior of cmdUpdateSoundCues with
- // this call
+ // If we're running a sound-SCI0 game, update the sound cues, to
+ // compensate for the fact that sound-SCI0 does not poll to update
+ // the sound cues itself, like sound-SCI1 and later do with
+ // cmdUpdateSoundCues. kGetEvent is called quite often, so emulate
+ // the sound-SCI1 behavior of cmdUpdateSoundCues with this call
g_sci->_soundCmd->updateSci0Cues();
}
@@ -215,7 +219,7 @@ reg_t kMapKeyToDir(EngineState *s, int argc, reg_t *argv) {
}
reg_t kGlobalToLocal(EngineState *s, int argc, reg_t *argv) {
- reg_t obj = argc ? argv[0] : NULL_REG; // Can this really happen? Lars
+ reg_t obj = argv[0];
reg_t planeObject = argc > 1 ? argv[1] : NULL_REG; // SCI32
SegManager *segMan = s->_segMan;
@@ -234,7 +238,7 @@ reg_t kGlobalToLocal(EngineState *s, int argc, reg_t *argv) {
}
reg_t kLocalToGlobal(EngineState *s, int argc, reg_t *argv) {
- reg_t obj = argc ? argv[0] : NULL_REG; // Can this really happen? Lars
+ reg_t obj = argv[0];
reg_t planeObject = argc > 1 ? argv[1] : NULL_REG; // SCI32
SegManager *segMan = s->_segMan;
diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp
index 807edc63a5..d4ba467b25 100644
--- a/engines/sci/engine/kfile.cpp
+++ b/engines/sci/engine/kfile.cpp
@@ -98,7 +98,7 @@ enum {
-void file_open(EngineState *s, const char *filename, int mode) {
+reg_t file_open(EngineState *s, const char *filename, int mode) {
// QfG3 character import prepends /\ to the filenames.
if (filename[0] == '/' && filename[1] == '\\')
filename += 2;
@@ -132,17 +132,17 @@ void file_open(EngineState *s, const char *filename, int mode) {
}
if (!inFile)
- warning(" -> file_open(_K_FILE_MODE_OPEN_OR_FAIL): failed to open file '%s'", englishName.c_str());
+ debugC(2, kDebugLevelFile, " -> file_open(_K_FILE_MODE_OPEN_OR_FAIL): failed to open file '%s'", englishName.c_str());
} else if (mode == _K_FILE_MODE_CREATE) {
// Create the file, destroying any content it might have had
outFile = saveFileMan->openForSaving(wrappedName);
if (!outFile)
- warning(" -> file_open(_K_FILE_MODE_CREATE): failed to create file '%s'", englishName.c_str());
+ debugC(2, kDebugLevelFile, " -> file_open(_K_FILE_MODE_CREATE): failed to create file '%s'", englishName.c_str());
} else if (mode == _K_FILE_MODE_OPEN_OR_CREATE) {
// Try to open file, create it if it doesn't exist
outFile = saveFileMan->openForSaving(wrappedName);
if (!outFile)
- warning(" -> file_open(_K_FILE_MODE_CREATE): failed to create file '%s'", englishName.c_str());
+ debugC(2, kDebugLevelFile, " -> file_open(_K_FILE_MODE_CREATE): failed to create file '%s'", englishName.c_str());
// QfG1 opens the character export file with _K_FILE_MODE_CREATE first,
// closes it immediately and opens it again with this here. Perhaps
// other games use this for read access as well. I guess changing this
@@ -154,8 +154,7 @@ void file_open(EngineState *s, const char *filename, int mode) {
if (!inFile && !outFile) { // Failed
debugC(2, kDebugLevelFile, " -> file_open() failed");
- s->r_acc = SIGNAL_REG;
- return;
+ return SIGNAL_REG;
}
// Find a free file handle
@@ -172,9 +171,8 @@ void file_open(EngineState *s, const char *filename, int mode) {
s->_fileHandles[handle]._out = outFile;
s->_fileHandles[handle]._name = englishName;
- s->r_acc = make_reg(0, handle);
-
debugC(2, kDebugLevelFile, " -> opened file '%s' with handle %d", englishName.c_str(), handle);
+ return make_reg(0, handle);
}
reg_t kFOpen(EngineState *s, int argc, reg_t *argv) {
@@ -182,8 +180,7 @@ reg_t kFOpen(EngineState *s, int argc, reg_t *argv) {
int mode = argv[1].toUint16();
debugC(2, kDebugLevelFile, "kFOpen(%s,0x%x)", name.c_str(), mode);
- file_open(s, name.c_str(), mode);
- return s->r_acc;
+ return file_open(s, name.c_str(), mode);
}
static FileHandle *getFileFromHandle(EngineState *s, uint handle) {
@@ -368,11 +365,25 @@ reg_t kGetSaveDir(EngineState *s, int argc, reg_t *argv) {
}
reg_t kCheckFreeSpace(EngineState *s, int argc, reg_t *argv) {
-#ifdef ENABLE_SCI32
- // TODO: SCI32 uses a parameter here.
- if (argc > 1)
- warning("kCheckFreeSpace called with %d parameter(s): %04x:%04x", argc, PRINT_REG(argv[1]));
-#endif
+ if (argc > 1) {
+ // SCI1.1/SCI32
+ // TODO: don't know if those are right for SCI32 as well
+ // Please note that sierra sci supported both calls either w/ or w/o opcode in SCI1.1
+ switch (argv[1].toUint16()) {
+ case 0: // return saved game size
+ return make_reg(0, 0); // we return 0
+
+ case 1: // return free harddisc space (shifted right somehow)
+ return make_reg(0, 0x7fff); // we return maximum
+
+ case 2: // same as call w/o opcode
+ break;
+ return make_reg(0, 1);
+
+ default:
+ error("kCheckFreeSpace: called with unknown sub-op %d", argv[1].toUint16());
+ }
+ }
Common::String path = s->_segMan->getString(argv[0]);
@@ -534,6 +545,12 @@ reg_t kSaveGame(EngineState *s, int argc, reg_t *argv) {
debug(3, "kSaveGame(%s,%d,%s,%s)", game_id.c_str(), virtualId, game_description.c_str(), version.c_str());
+ // We check here, we don't want to delete a users save in case we are within a kernel function
+ if (s->executionStackBase) {
+ warning("kSaveGame - won't save from within kernel function");
+ return NULL_REG;
+ }
+
Common::Array<SavegameDesc> saves;
listSavegames(saves);
@@ -541,7 +558,7 @@ reg_t kSaveGame(EngineState *s, int argc, reg_t *argv) {
if ((virtualId >= SAVEGAMEID_OFFICIALRANGE_START) && (virtualId <= SAVEGAMEID_OFFICIALRANGE_END)) {
// savegameId is an actual Id, so search for it just to make sure
savegameId = virtualId - SAVEGAMEID_OFFICIALRANGE_START;
- if (findSavegame(saves, savegameId) != -1)
+ if (findSavegame(saves, savegameId) == -1)
return NULL_REG;
} else if (virtualId < SAVEGAMEID_OFFICIALRANGE_START) {
// virtualId is low, we assume that scripts expect us to create new slot
@@ -603,11 +620,15 @@ reg_t kRestoreGame(EngineState *s, int argc, reg_t *argv) {
debug(3, "kRestoreGame(%s,%d)", game_id.c_str(), savegameId);
- if ((savegameId < 1000) || (savegameId > 1999)) {
- warning("Savegame ID %d is not allowed", savegameId);
- return TRUE_REG;
+ if (argv[0].isNull()) {
+ // Loading from the launcher, don't adjust the ID of the saved game
+ } else {
+ if ((savegameId < 1000) || (savegameId > 1999)) {
+ warning("Savegame ID %d is not allowed", savegameId);
+ return TRUE_REG;
+ }
+ savegameId -= 1000;
}
- savegameId -= 1000;
Common::Array<SavegameDesc> saves;
listSavegames(saves);
@@ -643,28 +664,6 @@ reg_t kValidPath(EngineState *s, int argc, reg_t *argv) {
return make_reg(0, 1);
}
-enum {
- K_FILEIO_OPEN = 0,
- K_FILEIO_CLOSE = 1,
- K_FILEIO_READ_RAW = 2,
- K_FILEIO_WRITE_RAW = 3,
- K_FILEIO_UNLINK = 4,
- K_FILEIO_READ_STRING = 5,
- K_FILEIO_WRITE_STRING = 6,
- K_FILEIO_SEEK = 7,
- K_FILEIO_FIND_FIRST = 8,
- K_FILEIO_FIND_NEXT = 9,
- K_FILEIO_FILE_EXISTS = 10,
- // SCI1.1
- K_FILEIO_RENAME = 11,
- // SCI32
- // 12?
- K_FILEIO_READ_BYTE = 13,
- K_FILEIO_WRITE_BYTE = 14,
- K_FILEIO_READ_WORD = 15,
- K_FILEIO_WRITE_WORD = 16
-};
-
reg_t DirSeeker::firstFile(const Common::String &mask, reg_t buffer, SegManager *segMan) {
// Verify that we are given a valid buffer
if (!buffer.segment) {
@@ -705,263 +704,298 @@ reg_t DirSeeker::nextFile(SegManager *segMan) {
}
reg_t kFileIO(EngineState *s, int argc, reg_t *argv) {
- int func_nr = argv[0].toUint16();
+ if (!s)
+ return make_reg(0, getSciVersion());
+ error("not supposed to call this");
+}
- switch (func_nr) {
- case K_FILEIO_OPEN : {
- Common::String name = s->_segMan->getString(argv[1]);
+reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) {
+ Common::String name = s->_segMan->getString(argv[0]);
- // SCI32 can call K_FILEIO_OPEN with only two arguments. It seems to
- // just be checking if it exists.
- int mode = (argc < 3) ? (int)_K_FILE_MODE_OPEN_OR_FAIL : argv[2].toUint16();
+ // SCI32 can call K_FILEIO_OPEN with only one argument. It seems to
+ // just be checking if it exists.
+ int mode = (argc < 2) ? (int)_K_FILE_MODE_OPEN_OR_FAIL : argv[1].toUint16();
- // SQ4 floppy prepends /\ to the filenames
- if (name.hasPrefix("/\\")) {
- name.deleteChar(0);
- name.deleteChar(0);
- }
+ // SQ4 floppy prepends /\ to the filenames
+ if (name.hasPrefix("/\\")) {
+ name.deleteChar(0);
+ name.deleteChar(0);
+ }
- // SQ4 floppy attempts to update the savegame index file sq4sg.dir when
- // deleting saved games. We don't use an index file for saving or
- // loading, so just stop the game from modifying the file here in order
- // to avoid having it saved in the ScummVM save directory.
- if (name == "sq4sg.dir") {
- debugC(2, kDebugLevelFile, "Not opening unused file sq4sg.dir");
- return SIGNAL_REG;
- }
+ // SQ4 floppy attempts to update the savegame index file sq4sg.dir when
+ // deleting saved games. We don't use an index file for saving or
+ // loading, so just stop the game from modifying the file here in order
+ // to avoid having it saved in the ScummVM save directory.
+ if (name == "sq4sg.dir") {
+ debugC(2, kDebugLevelFile, "Not opening unused file sq4sg.dir");
+ return SIGNAL_REG;
+ }
- if (name.empty()) {
- warning("Attempted to open a file with an empty filename");
- return SIGNAL_REG;
- }
- debugC(2, kDebugLevelFile, "kFileIO(open): %s, 0x%x", name.c_str(), mode);
- file_open(s, name.c_str(), mode);
- break;
+ if (name.empty()) {
+ warning("Attempted to open a file with an empty filename");
+ return SIGNAL_REG;
}
- case K_FILEIO_CLOSE : {
- debugC(2, kDebugLevelFile, "kFileIO(close): %d", argv[1].toUint16());
+ debugC(2, kDebugLevelFile, "kFileIO(open): %s, 0x%x", name.c_str(), mode);
+ return file_open(s, name.c_str(), mode);
+}
- FileHandle *f = getFileFromHandle(s, argv[1].toUint16());
- if (f)
- f->close();
- break;
+reg_t kFileIOClose(EngineState *s, int argc, reg_t *argv) {
+ debugC(2, kDebugLevelFile, "kFileIO(close): %d", argv[0].toUint16());
+
+ FileHandle *f = getFileFromHandle(s, argv[0].toUint16());
+ if (f) {
+ f->close();
+ return SIGNAL_REG;
}
- case K_FILEIO_READ_RAW : {
- int handle = argv[1].toUint16();
- int size = argv[3].toUint16();
- char *buf = new char[size];
- debugC(2, kDebugLevelFile, "kFileIO(readRaw): %d, %d", handle, size);
+ return NULL_REG;
+}
+
+reg_t kFileIOReadRaw(EngineState *s, int argc, reg_t *argv) {
+ int handle = argv[0].toUint16();
+ int size = argv[2].toUint16();
+ int bytesRead = 0;
+ char *buf = new char[size];
+ debugC(2, kDebugLevelFile, "kFileIO(readRaw): %d, %d", handle, size);
- FileHandle *f = getFileFromHandle(s, handle);
- if (f) {
- s->r_acc = make_reg(0, f->_in->read(buf, size));
- s->_segMan->memcpy(argv[2], (const byte*)buf, size);
- }
+ FileHandle *f = getFileFromHandle(s, handle);
+ if (f) {
+ bytesRead = f->_in->read(buf, size);
+ s->_segMan->memcpy(argv[1], (const byte*)buf, size);
+ }
- delete[] buf;
- break;
+ delete[] buf;
+ return make_reg(0, bytesRead);
+}
+
+reg_t kFileIOWriteRaw(EngineState *s, int argc, reg_t *argv) {
+ int handle = argv[0].toUint16();
+ int size = argv[2].toUint16();
+ char *buf = new char[size];
+ bool success = false;
+ s->_segMan->memcpy((byte*)buf, argv[1], size);
+ debugC(2, kDebugLevelFile, "kFileIO(writeRaw): %d, %d", handle, size);
+
+ FileHandle *f = getFileFromHandle(s, handle);
+ if (f) {
+ f->_out->write(buf, size);
+ success = true;
}
- case K_FILEIO_WRITE_RAW : {
- int handle = argv[1].toUint16();
- int size = argv[3].toUint16();
- char *buf = new char[size];
- s->_segMan->memcpy((byte*)buf, argv[2], size);
- debugC(2, kDebugLevelFile, "kFileIO(writeRaw): %d, %d", handle, size);
- FileHandle *f = getFileFromHandle(s, handle);
- if (f)
- f->_out->write(buf, size);
+ delete[] buf;
+ if (success)
+ return NULL_REG;
+ return make_reg(0, 6); // DOS - invalid handle
+}
- delete[] buf;
- break;
+reg_t kFileIOUnlink(EngineState *s, int argc, reg_t *argv) {
+ Common::String name = s->_segMan->getString(argv[0]);
+ Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
+ bool result;
+
+ // SQ4 floppy prepends /\ to the filenames
+ if (name.hasPrefix("/\\")) {
+ name.deleteChar(0);
+ name.deleteChar(0);
+ }
+
+ // Special case for SQ4 floppy: This game has hardcoded names for all of
+ // its savegames, and they are all named "sq4sg.xxx", where xxx is the
+ // slot. We just take the slot number here, and delete the appropriate
+ // save game.
+ if (name.hasPrefix("sq4sg.")) {
+ // Special handling for SQ4... get the slot number and construct the
+ // save game name.
+ int slotNum = atoi(name.c_str() + name.size() - 3);
+ Common::Array<SavegameDesc> saves;
+ listSavegames(saves);
+ int savedir_nr = saves[slotNum].id;
+ name = g_sci->getSavegameName(savedir_nr);
+ result = saveFileMan->removeSavefile(name);
+ } else {
+ const Common::String wrappedName = g_sci->wrapFilename(name);
+ result = saveFileMan->removeSavefile(wrappedName);
}
- case K_FILEIO_UNLINK : {
- Common::String name = s->_segMan->getString(argv[1]);
- Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
- // SQ4 floppy prepends /\ to the filenames
- if (name.hasPrefix("/\\")) {
- name.deleteChar(0);
- name.deleteChar(0);
- }
- // Special case for SQ4 floppy: This game has hardcoded names for all of
- // its savegames, and they are all named "sq4sg.xxx", where xxx is the
- // slot. We just take the slot number here, and delete the appropriate
- // save game.
- if (name.hasPrefix("sq4sg.")) {
- // Special handling for SQ4... get the slot number and construct the
- // save game name.
- int slotNum = atoi(name.c_str() + name.size() - 3);
- Common::Array<SavegameDesc> saves;
- listSavegames(saves);
- int savedir_nr = saves[slotNum].id;
- name = g_sci->getSavegameName(savedir_nr);
- saveFileMan->removeSavefile(name);
- } else {
- const Common::String wrappedName = g_sci->wrapFilename(name);
- saveFileMan->removeSavefile(wrappedName);
- }
+ debugC(2, kDebugLevelFile, "kFileIO(unlink): %s", name.c_str());
+ if (result)
+ return NULL_REG;
+ return make_reg(0, 2); // DOS - file not found error code
+}
- debugC(2, kDebugLevelFile, "kFileIO(unlink): %s", name.c_str());
+reg_t kFileIOReadString(EngineState *s, int argc, reg_t *argv) {
+ int size = argv[1].toUint16();
+ char *buf = new char[size];
+ int handle = argv[2].toUint16();
+ debugC(2, kDebugLevelFile, "kFileIO(readString): %d, %d", handle, size);
- // TODO/FIXME: Should we return something (like, a bool indicating
- // whether deleting the save succeeded or failed)?
- break;
- }
- case K_FILEIO_READ_STRING : {
- int size = argv[2].toUint16();
- char *buf = new char[size];
- int handle = argv[3].toUint16();
- debugC(2, kDebugLevelFile, "kFileIO(readString): %d, %d", handle, size);
+ fgets_wrapper(s, buf, size, handle);
+ s->_segMan->memcpy(argv[0], (const byte*)buf, size);
+ delete[] buf;
+ return argv[0];
+}
- fgets_wrapper(s, buf, size, handle);
- s->_segMan->memcpy(argv[1], (const byte*)buf, size);
- delete[] buf;
- return argv[1];
- }
- case K_FILEIO_WRITE_STRING : {
- int handle = argv[1].toUint16();
- int size = argv[3].toUint16();
- Common::String str = s->_segMan->getString(argv[2]);
- debugC(2, kDebugLevelFile, "kFileIO(writeString): %d, %d", handle, size);
-
- // CHECKME: Is the size parameter used at all?
- // In the LSL5 password protection it is zero, and we should
- // then write a full string. (Not sure if it should write the
- // terminating zero.)
-
- FileHandle *f = getFileFromHandle(s, handle);
- if (f)
- f->_out->write(str.c_str(), str.size());
- break;
- }
- case K_FILEIO_SEEK : {
- int handle = argv[1].toUint16();
- int offset = argv[2].toUint16();
- int whence = argv[3].toUint16();
- debugC(2, kDebugLevelFile, "kFileIO(seek): %d, %d, %d", handle, offset, whence);
+reg_t kFileIOWriteString(EngineState *s, int argc, reg_t *argv) {
+ int handle = argv[0].toUint16();
+ Common::String str = s->_segMan->getString(argv[1]);
+ debugC(2, kDebugLevelFile, "kFileIO(writeString): %d", handle);
+
+ FileHandle *f = getFileFromHandle(s, handle);
+ if (f)
+ f->_out->write(str.c_str(), str.size());
+ return NULL_REG;
+ return make_reg(0, 6); // DOS - invalid handle
+}
+
+reg_t kFileIOSeek(EngineState *s, int argc, reg_t *argv) {
+ int handle = argv[0].toUint16();
+ int offset = argv[1].toUint16();
+ int whence = argv[2].toUint16();
+ debugC(2, kDebugLevelFile, "kFileIO(seek): %d, %d, %d", handle, offset, whence);
- FileHandle *f = getFileFromHandle(s, handle);
- if (f)
- s->r_acc = make_reg(0, f->_in->seek(offset, whence));
- break;
- }
- case K_FILEIO_FIND_FIRST : {
- Common::String mask = s->_segMan->getString(argv[1]);
- reg_t buf = argv[2];
- int attr = argv[3].toUint16(); // We won't use this, Win32 might, though...
- debugC(2, kDebugLevelFile, "kFileIO(findFirst): %s, 0x%x", mask.c_str(), attr);
-
- // We remove ".*". mask will get prefixed, so we will return all additional files for that gameid
- if (mask == "*.*")
- mask = "*";
-
- // QfG3 uses this mask for the character import
- if (mask == "/\\*.*")
- mask = "*";
-//#ifndef WIN32
-// if (mask == "*.*")
-// mask = "*"; // For UNIX
-//#endif
- s->r_acc = s->_dirseeker.firstFile(mask, buf, s->_segMan);
+ FileHandle *f = getFileFromHandle(s, handle);
+ if (f)
+ s->r_acc = make_reg(0, f->_in->seek(offset, whence));
+ return SIGNAL_REG;
+}
- break;
- }
- case K_FILEIO_FIND_NEXT : {
- debugC(2, kDebugLevelFile, "kFileIO(findNext)");
- s->r_acc = s->_dirseeker.nextFile(s->_segMan);
- break;
+reg_t kFileIOFindFirst(EngineState *s, int argc, reg_t *argv) {
+ Common::String mask = s->_segMan->getString(argv[0]);
+ reg_t buf = argv[1];
+ int attr = argv[2].toUint16(); // We won't use this, Win32 might, though...
+ debugC(2, kDebugLevelFile, "kFileIO(findFirst): %s, 0x%x", mask.c_str(), attr);
+
+ // QfG3 uses "/\*.*" for the character import, QfG4 uses "/\*"
+ if (mask.hasPrefix("/\\")) {
+ mask.deleteChar(0);
+ mask.deleteChar(0);
}
- case K_FILEIO_FILE_EXISTS : {
- Common::String name = s->_segMan->getString(argv[1]);
- // Check for regular file
- bool exists = Common::File::exists(name);
- Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
- const Common::String wrappedName = g_sci->wrapFilename(name);
+ // We remove ".*". mask will get prefixed, so we will return all additional files for that gameid
+ if (mask == "*.*")
+ mask = "*";
+ return s->_dirseeker.firstFile(mask, buf, s->_segMan);
+}
- if (!exists)
- exists = !saveFileMan->listSavefiles(name).empty();
+reg_t kFileIOFindNext(EngineState *s, int argc, reg_t *argv) {
+ debugC(2, kDebugLevelFile, "kFileIO(findNext)");
+ return s->_dirseeker.nextFile(s->_segMan);
+}
- if (!exists) {
- // Try searching for the file prepending target-
- Common::SeekableReadStream *inFile = saveFileMan->openForLoading(wrappedName);
- exists = (inFile != 0);
- delete inFile;
- }
+reg_t kFileIOExists(EngineState *s, int argc, reg_t *argv) {
+ Common::String name = s->_segMan->getString(argv[0]);
- // Special case for non-English versions of LSL5: The English version of
- // LSL5 calls kFileIO(), case K_FILEIO_OPEN for reading to check if
- // memory.drv exists (which is where the game's password is stored). If
- // it's not found, it calls kFileIO() again, case K_FILEIO_OPEN for
- // writing and creates a new file. Non-English versions call kFileIO(),
- // case K_FILEIO_FILE_EXISTS instead, and fail if memory.drv can't be
- // found. We create a default memory.drv file with no password, so that
- // the game can continue.
- if (!exists && name == "memory.drv") {
- // Create a new file, and write the bytes for the empty password
- // string inside
- byte defaultContent[] = { 0xE9, 0xE9, 0xEB, 0xE1, 0x0D, 0x0A, 0x31, 0x30, 0x30, 0x30 };
- Common::WriteStream *outFile = saveFileMan->openForSaving(wrappedName);
- for (int i = 0; i < 10; i++)
- outFile->writeByte(defaultContent[i]);
- outFile->finalize();
- delete outFile;
- exists = true;
- }
+ // Check for regular file
+ bool exists = Common::File::exists(name);
+ Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager();
+ const Common::String wrappedName = g_sci->wrapFilename(name);
+
+ if (!exists)
+ exists = !saveFileMan->listSavefiles(name).empty();
+
+ if (!exists) {
+ // Try searching for the file prepending target-
+ Common::SeekableReadStream *inFile = saveFileMan->openForLoading(wrappedName);
+ exists = (inFile != 0);
+ delete inFile;
+ }
+
+ // Special case for non-English versions of LSL5: The English version of
+ // LSL5 calls kFileIO(), case K_FILEIO_OPEN for reading to check if
+ // memory.drv exists (which is where the game's password is stored). If
+ // it's not found, it calls kFileIO() again, case K_FILEIO_OPEN for
+ // writing and creates a new file. Non-English versions call kFileIO(),
+ // case K_FILEIO_FILE_EXISTS instead, and fail if memory.drv can't be
+ // found. We create a default memory.drv file with no password, so that
+ // the game can continue.
+ if (!exists && name == "memory.drv") {
+ // Create a new file, and write the bytes for the empty password
+ // string inside
+ byte defaultContent[] = { 0xE9, 0xE9, 0xEB, 0xE1, 0x0D, 0x0A, 0x31, 0x30, 0x30, 0x30 };
+ Common::WriteStream *outFile = saveFileMan->openForSaving(wrappedName);
+ for (int i = 0; i < 10; i++)
+ outFile->writeByte(defaultContent[i]);
+ outFile->finalize();
+ delete outFile;
+ exists = true;
+ }
+
+ debugC(2, kDebugLevelFile, "kFileIO(fileExists) %s -> %d", name.c_str(), exists);
+ return make_reg(0, exists);
+}
- debugC(2, kDebugLevelFile, "kFileIO(fileExists) %s -> %d", name.c_str(), exists);
- return make_reg(0, exists);
- }
- case K_FILEIO_RENAME: {
- Common::String oldName = s->_segMan->getString(argv[1]);
- Common::String newName = s->_segMan->getString(argv[2]);
+reg_t kFileIORename(EngineState *s, int argc, reg_t *argv) {
+ Common::String oldName = s->_segMan->getString(argv[0]);
+ Common::String newName = s->_segMan->getString(argv[1]);
+
+ // SCI1.1 returns 0 on success and a DOS error code on fail. SCI32
+ // returns -1 on fail. We just return -1 for all versions.
+ if (g_engine->getSaveFileManager()->renameSavefile(oldName, newName))
+ return NULL_REG;
+ else
+ return SIGNAL_REG;
+}
- // SCI1.1 returns 0 on success and a DOS error code on fail. SCI32
- // returns -1 on fail. We just return -1 for all versions.
- if (g_engine->getSaveFileManager()->renameSavefile(oldName, newName))
- return NULL_REG;
- else
- return SIGNAL_REG;
- }
#ifdef ENABLE_SCI32
- case K_FILEIO_READ_BYTE: {
- // Read the byte into the low byte of the accumulator
- FileHandle *f = getFileFromHandle(s, argv[1].toUint16());
- if (!f)
- return NULL_REG;
-
- return make_reg(0, (s->r_acc.toUint16() & 0xff00) | f->_in->readByte());
- }
- case K_FILEIO_WRITE_BYTE: {
- FileHandle *f = getFileFromHandle(s, argv[1].toUint16());
- if (f)
- f->_out->writeByte(argv[2].toUint16() & 0xff);
- break;
- }
- case K_FILEIO_READ_WORD: {
- FileHandle *f = getFileFromHandle(s, argv[1].toUint16());
- if (!f)
- return NULL_REG;
-
- return make_reg(0, f->_in->readUint16LE());
- }
- case K_FILEIO_WRITE_WORD: {
- FileHandle *f = getFileFromHandle(s, argv[1].toUint16());
- if (f)
- f->_out->writeUint16LE(argv[2].toUint16());
- break;
+reg_t kFileIOReadByte(EngineState *s, int argc, reg_t *argv) {
+ // Read the byte into the low byte of the accumulator
+ FileHandle *f = getFileFromHandle(s, argv[0].toUint16());
+ if (!f)
+ return NULL_REG;
+ return make_reg(0, (s->r_acc.toUint16() & 0xff00) | f->_in->readByte());
+}
+
+reg_t kFileIOWriteByte(EngineState *s, int argc, reg_t *argv) {
+ FileHandle *f = getFileFromHandle(s, argv[0].toUint16());
+ if (f)
+ f->_out->writeByte(argv[1].toUint16() & 0xff);
+ return s->r_acc; // FIXME: does this really doesn't return anything?
+}
+
+reg_t kFileIOReadWord(EngineState *s, int argc, reg_t *argv) {
+ FileHandle *f = getFileFromHandle(s, argv[0].toUint16());
+ if (!f)
+ return NULL_REG;
+ return make_reg(0, f->_in->readUint16LE());
+}
+
+reg_t kFileIOWriteWord(EngineState *s, int argc, reg_t *argv) {
+ FileHandle *f = getFileFromHandle(s, argv[0].toUint16());
+ if (f)
+ f->_out->writeUint16LE(argv[1].toUint16());
+ return s->r_acc; // FIXME: does this really doesn't return anything?
+}
+
+reg_t kCD(EngineState *s, int argc, reg_t *argv) {
+ // TODO: Stub
+ switch (argv[0].toUint16()) {
+ case 0:
+ // Return whether the contents of disc argv[1] is available.
+ return TRUE_REG;
+ default:
+ warning("CD(%d)", argv[0].toUint16());
}
- case 19:
- // TODO: Torin's Passage uses this early on in the Sierra logo
- warning("kFileIO(19)");
- break;
-#endif
+
+ return NULL_REG;
+}
+
+reg_t kSave(EngineState *s, int argc, reg_t *argv) {
+ switch (argv[0].toUint16()) {
+ case 0: // Called by kq7 when starting chapters
+ return SIGNAL_REG;
+ case 2: // GetSaveDir
+ // Yay! Reusing the old kernel function!
+ return kGetSaveDir(s, argc - 1, argv + 1);
+ case 8:
+ // TODO
+ // This function has to return something other than 0 to proceed
+ return s->r_acc;
default:
- error("kFileIO(): unknown sub-command: %d", func_nr);
+ warning("Unknown/unhandled kSave subop %d", argv[0].toUint16());
}
- return s->r_acc;
+ return NULL_REG;
}
+#endif
+
} // End of namespace Sci
diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp
index 29f7565ef9..56518f10bf 100644
--- a/engines/sci/engine/kgraphics.cpp
+++ b/engines/sci/engine/kgraphics.cpp
@@ -23,16 +23,15 @@
*
*/
+#include "common/system.h"
+
#include "engines/util.h"
#include "graphics/cursorman.h"
-#include "graphics/video/avi_decoder.h"
-#include "graphics/video/qt_decoder.h"
#include "graphics/surface.h"
#include "sci/sci.h"
#include "sci/debug.h" // for g_debug_sleeptime_factor
#include "sci/resource.h"
-#include "sci/video/seq_decoder.h"
#include "sci/engine/features.h"
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
@@ -44,12 +43,14 @@
#include "sci/graphics/cursor.h"
#include "sci/graphics/palette.h"
#include "sci/graphics/paint16.h"
+#include "sci/graphics/picture.h"
#include "sci/graphics/ports.h"
+#include "sci/graphics/robot.h"
#include "sci/graphics/screen.h"
#include "sci/graphics/text16.h"
#include "sci/graphics/view.h"
#ifdef ENABLE_SCI32
-#include "sci/video/vmd_decoder.h"
+#include "sci/graphics/frameout.h"
#endif
namespace Sci {
@@ -129,9 +130,10 @@ static reg_t kSetCursorSci11(EngineState *s, int argc, reg_t *argv) {
break;
case -1:
// TODO: Special case at least in kq6, check disassembly
+ // Does something with magCursor, which is set on argc = 10, which we don't support
break;
case -2:
- // TODO: Special case at least in kq6, check disassembly
+ g_sci->_gfxCursor->kernelResetMoveZone();
break;
default:
g_sci->_gfxCursor->kernelShow();
@@ -145,14 +147,22 @@ static reg_t kSetCursorSci11(EngineState *s, int argc, reg_t *argv) {
g_sci->_gfxCursor->kernelSetPos(pos);
break;
case 4: {
- int16 top = argv[0].toSint16();
- int16 left = argv[1].toSint16();
- int16 bottom = argv[2].toSint16();
- int16 right = argv[3].toSint16();
+ int16 top, left, bottom, right;
- // In SCI32, the right parameter seems to be divided by 2
- if (getSciVersion() >= SCI_VERSION_2)
- right *= 2;
+ if (getSciVersion() >= SCI_VERSION_2) {
+ top = argv[1].toSint16();
+ left = argv[0].toSint16();
+ bottom = argv[3].toSint16();
+ right = argv[2].toSint16();
+ } else {
+ top = argv[0].toSint16();
+ left = argv[1].toSint16();
+ bottom = argv[2].toSint16();
+ right = argv[3].toSint16();
+ }
+ // bottom/right needs to be included into our movezone, because we compare it like any regular Common::Rect
+ bottom++;
+ right++;
if ((right >= left) && (bottom >= top)) {
Common::Rect rect = Common::Rect(left, top, right, bottom);
@@ -250,7 +260,7 @@ reg_t kGraphDrawLine(EngineState *s, int argc, reg_t *argv) {
reg_t kGraphSaveBox(EngineState *s, int argc, reg_t *argv) {
Common::Rect rect = getGraphRect(argv);
- uint16 screenMask = (argc > 4) ? argv[4].toUint16() : 0;
+ uint16 screenMask = argv[4].toUint16() & GFX_SCREEN_MASK_ALL;
return g_sci->_gfxPaint16->kernelGraphSaveBox(rect, screenMask);
}
@@ -382,17 +392,16 @@ reg_t kCanBeHere(EngineState *s, int argc, reg_t *argv) {
reg_t curObject = argv[0];
reg_t listReference = (argc > 1) ? argv[1] : NULL_REG;
- bool canBeHere = g_sci->_gfxCompare->kernelCanBeHere(curObject, listReference);
- return make_reg(0, canBeHere);
+ reg_t canBeHere = g_sci->_gfxCompare->kernelCanBeHere(curObject, listReference);
+ return make_reg(0, canBeHere.isNull() ? 1 : 0);
}
-// kCantBeHere does the same thing as kCanBeHere, except that it returns the opposite result.
reg_t kCantBeHere(EngineState *s, int argc, reg_t *argv) {
reg_t curObject = argv[0];
reg_t listReference = (argc > 1) ? argv[1] : NULL_REG;
- bool canBeHere = g_sci->_gfxCompare->kernelCanBeHere(curObject, listReference);
- return make_reg(0, !canBeHere);
+ reg_t canBeHere = g_sci->_gfxCompare->kernelCanBeHere(curObject, listReference);
+ return canBeHere;
}
reg_t kIsItSkip(EngineState *s, int argc, reg_t *argv) {
@@ -518,14 +527,6 @@ reg_t kBaseSetter(EngineState *s, int argc, reg_t *argv) {
reg_t object = argv[0];
g_sci->_gfxCompare->kernelBaseSetter(object);
-
- // WORKAROUND for a problem in LSL1VGA. This allows the casino door to be opened,
- // till the actual problem is found
- if (s->currentRoomNumber() == 300 && g_sci->getGameId() == GID_LSL1) {
- int top = readSelectorValue(s->_segMan, object, SELECTOR(brTop));
- writeSelectorValue(s->_segMan, object, SELECTOR(brTop), top + 2);
- }
-
return s->r_acc;
}
@@ -615,16 +616,16 @@ reg_t kPaletteAnimate(EngineState *s, int argc, reg_t *argv) {
reg_t kPaletteSave(EngineState *s, int argc, reg_t *argv) {
if (g_sci->getResMan()->isVGA()) {
- warning("kPalette(7), save palette to heap STUB");
+ return g_sci->_gfxPalette->kernelSave();
}
return NULL_REG;
}
reg_t kPaletteRestore(EngineState *s, int argc, reg_t *argv) {
if (g_sci->getResMan()->isVGA()) {
- warning("kPalette(8), restore palette from heap STUB");
+ g_sci->_gfxPalette->kernelRestore(argv[0]);
}
- return s->r_acc;
+ return argv[0];
}
reg_t kPalVary(EngineState *s, int argc, reg_t *argv) {
@@ -889,8 +890,8 @@ reg_t kDrawControl(EngineState *s, int argc, reg_t *argv) {
reg_t textReference = readSelector(s->_segMan, controlObject, SELECTOR(text));
if (!textReference.isNull()) {
Common::String text = s->_segMan->getString(textReference);
- if (text == "a:hq1_hero.sav") {
- // Remove "a:" from hero quest export default filename
+ if ((text == "a:hq1_hero.sav") || (text == "a:glory1.sav") || (text == "a:glory2.sav") || (text == "a:glory3.sav")) {
+ // Remove "a:" from hero quest / quest for glory export default filenames
text.deleteChar(0);
text.deleteChar(0);
s->_segMan->strcpy(textReference, text.c_str());
@@ -974,14 +975,13 @@ reg_t kSetPort(EngineState *s, int argc, reg_t *argv) {
case 7:
initPriorityBandsFlag = true;
- case 4:
case 6:
picRect.top = argv[0].toSint16();
picRect.left = argv[1].toSint16();
picRect.bottom = argv[2].toSint16();
picRect.right = argv[3].toSint16();
- picTop = (argc >= 6) ? argv[4].toSint16() : 0;
- picLeft = (argc >= 6) ? argv[5].toSint16() : 0;
+ picTop = argv[4].toSint16();
+ picLeft = argv[5].toSint16();
g_sci->_gfxPorts->kernelSetPicWindow(picRect, picTop, picLeft, initPriorityBandsFlag);
break;
@@ -1000,10 +1000,26 @@ reg_t kDrawCel(EngineState *s, int argc, reg_t *argv) {
uint16 y = argv[4].toUint16();
int16 priority = (argc > 5) ? argv[5].toSint16() : -1;
uint16 paletteNo = (argc > 6) ? argv[6].toUint16() : 0;
- bool hiresMode = (argc > 7) ? true : false;
- reg_t upscaledHiresHandle = (argc > 7) ? argv[7] : NULL_REG;
+ bool hiresMode = false;
+ reg_t upscaledHiresHandle = NULL_REG;
+ uint16 scaleX = 128;
+ uint16 scaleY = 128;
+
+ if (argc > 7) {
+ // this is either kq6 hires or scaling
+ if (paletteNo > 0) {
+ // it's scaling
+ scaleX = argv[6].toUint16();
+ scaleY = argv[7].toUint16();
+ paletteNo = 0;
+ } else {
+ // KQ6 hires
+ hiresMode = true;
+ upscaledHiresHandle = argv[7];
+ }
+ }
- g_sci->_gfxPaint16->kernelDrawCel(viewId, loopNo, celNo, x, y, priority, paletteNo, hiresMode, upscaledHiresHandle);
+ g_sci->_gfxPaint16->kernelDrawCel(viewId, loopNo, celNo, x, y, priority, paletteNo, scaleX, scaleY, hiresMode, upscaledHiresHandle);
return s->r_acc;
}
@@ -1075,153 +1091,264 @@ reg_t kDisplay(EngineState *s, int argc, reg_t *argv) {
return g_sci->_gfxPaint16->kernelDisplay(g_sci->strSplit(text.c_str()).c_str(), argc, argv);
}
-void playVideo(Graphics::VideoDecoder *videoDecoder) {
- if (!videoDecoder)
- return;
+reg_t kSetVideoMode(EngineState *s, int argc, reg_t *argv) {
+ // This call is used for KQ6's intro. It has one parameter, which is 1 when
+ // the intro begins, and 0 when it ends. It is suspected that this is
+ // actually a flag to enable video planar memory access, as the video
+ // decoder in KQ6 is specifically written for the planar memory model.
+ // Planar memory mode access was used for VGA "Mode X" (320x240 resolution,
+ // although the intro in KQ6 is 320x200).
+ // Refer to http://en.wikipedia.org/wiki/Mode_X
- byte *scaleBuffer = 0;
- uint16 width = videoDecoder->getWidth();
- uint16 height = videoDecoder->getHeight();
- uint16 screenWidth = g_system->getWidth();
- uint16 screenHeight = g_system->getHeight();
-
- if (screenWidth == 640 && width <= 320 && height <= 240) {
- assert(videoDecoder->getPixelFormat().bytesPerPixel == 1);
- width *= 2;
- height *= 2;
- scaleBuffer = new byte[width * height];
- }
+ //warning("STUB: SetVideoMode %d", argv[0].toUint16());
+ return s->r_acc;
+}
- uint16 x = (screenWidth - width) / 2;
- uint16 y = (screenHeight - height) / 2;
- bool skipVideo = false;
-
- while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) {
- if (videoDecoder->needsUpdate()) {
- Graphics::Surface *frame = videoDecoder->decodeNextFrame();
- if (frame) {
- if (scaleBuffer) {
- // TODO: Probably should do aspect ratio correction in e.g. GK1 Windows
- g_sci->_gfxScreen->scale2x((byte *)frame->pixels, scaleBuffer, videoDecoder->getWidth(), videoDecoder->getHeight());
- g_system->copyRectToScreen(scaleBuffer, width, x, y, width, height);
- } else
- g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, width, height);
-
- if (videoDecoder->hasDirtyPalette())
- videoDecoder->setSystemPalette();
-
- g_system->updateScreen();
- }
- }
+// New calls for SCI11. Using those is only needed when using text-codes so that
+// one is able to change font and/or color multiple times during kDisplay and
+// kDrawControl
+reg_t kTextFonts(EngineState *s, int argc, reg_t *argv) {
+ g_sci->_gfxText16->kernelTextFonts(argc, argv);
+ return s->r_acc;
+}
- Common::Event event;
- while (g_system->getEventManager()->pollEvent(event)) {
- if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP)
- skipVideo = true;
- }
+reg_t kTextColors(EngineState *s, int argc, reg_t *argv) {
+ g_sci->_gfxText16->kernelTextColors(argc, argv);
+ return s->r_acc;
+}
- g_system->delayMillis(10);
- }
+#ifdef ENABLE_SCI32
- delete[] scaleBuffer;
- delete videoDecoder;
+reg_t kIsHiRes(EngineState *s, int argc, reg_t *argv) {
+ // Returns 0 if the screen width or height is less than 640 or 400,
+ // respectively.
+ if (g_system->getWidth() < 640 || g_system->getHeight() < 400)
+ return make_reg(0, 0);
+
+ return make_reg(0, 1);
}
-reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {
- // Hide the cursor if it's showing and then show it again if it was
- // previously visible.
- bool reshowCursor = g_sci->_gfxCursor->isVisible();
- if (reshowCursor)
- g_sci->_gfxCursor->kernelHide();
+// SCI32 variant, can't work like sci16 variants
+reg_t kCantBeHere32(EngineState *s, int argc, reg_t *argv) {
+// reg_t curObject = argv[0];
+// reg_t listReference = (argc > 1) ? argv[1] : NULL_REG;
+
+ return NULL_REG;
+}
- uint16 screenWidth = g_system->getWidth();
- uint16 screenHeight = g_system->getHeight();
-
- Graphics::VideoDecoder *videoDecoder = 0;
+reg_t kAddScreenItem(EngineState *s, int argc, reg_t *argv) {
+ reg_t viewObj = argv[0];
- if (argv[0].segment != 0) {
- Common::String filename = s->_segMan->getString(argv[0]);
+ g_sci->_gfxFrameout->kernelAddScreenItem(viewObj);
+ return NULL_REG;
+}
- if (g_sci->getPlatform() == Common::kPlatformMacintosh) {
- // Mac QuickTime
- // The only argument is the string for the video
+reg_t kUpdateScreenItem(EngineState *s, int argc, reg_t *argv) {
+ //reg_t viewObj = argv[0];
- // HACK: Switch to 16bpp graphics for Cinepak.
- initGraphics(screenWidth, screenHeight, screenWidth > 320, NULL);
+ //warning("kUpdateScreenItem, object %04x:%04x, view %d, loop %d, cel %d, pri %d", PRINT_REG(viewObj), viewId, loopNo, celNo, priority);
+ return NULL_REG;
+}
- if (g_system->getScreenFormat().bytesPerPixel == 1) {
- error("This video requires >8bpp color to be displayed, but could not switch to RGB color mode.");
- return NULL_REG;
- }
+reg_t kDeleteScreenItem(EngineState *s, int argc, reg_t *argv) {
+ reg_t viewObj = argv[0];
- videoDecoder = new Graphics::QuickTimeDecoder();
- if (!videoDecoder->loadFile(filename))
- error("Could not open '%s'", filename.c_str());
- } else {
- // DOS SEQ
- // SEQ's are called with no subops, just the string and delay
- SeqDecoder *seqDecoder = new SeqDecoder();
- seqDecoder->setFrameDelay(argv[1].toUint16()); // Time between frames in ticks
- videoDecoder = seqDecoder;
-
- if (!videoDecoder->loadFile(filename)) {
- warning("Failed to open movie file %s", filename.c_str());
- delete videoDecoder;
- videoDecoder = 0;
- }
- }
- } else {
- // Windows AVI
- // TODO: This appears to be some sort of subop. case 0 contains the string
- // for the video, so we'll just play it from there for now.
+ g_sci->_gfxFrameout->kernelDeleteScreenItem(viewObj);
-#ifdef ENABLE_SCI32
- if (getSciVersion() >= SCI_VERSION_2_1) {
- // SCI2.1 always has argv[0] as 1, the rest of the arguments seem to
- // follow SCI1.1/2.
- if (argv[0].toUint16() != 1)
- error("SCI2.1 kShowMovie argv[0] not 1");
- argv++;
- argc--;
- }
-#endif
- switch (argv[0].toUint16()) {
- case 0: {
- Common::String filename = s->_segMan->getString(argv[1]);
- videoDecoder = new Graphics::AviDecoder(g_system->getMixer());
-
- if (!videoDecoder->loadFile(filename.c_str())) {
- warning("Failed to open movie file %s", filename.c_str());
- delete videoDecoder;
- videoDecoder = 0;
- }
- break;
- }
- default:
- warning("Unhandled SCI kShowMovie subop %d", argv[1].toUint16());
- }
+ /*
+ reg_t viewObj = argv[0];
+ uint16 viewId = readSelectorValue(s->_segMan, viewObj, SELECTOR(view));
+ int16 loopNo = readSelectorValue(s->_segMan, viewObj, SELECTOR(loop));
+ int16 celNo = readSelectorValue(s->_segMan, viewObj, SELECTOR(cel));
+ //int16 leftPos = 0;
+ //int16 topPos = 0;
+ int16 priority = readSelectorValue(s->_segMan, viewObj, SELECTOR(priority));
+ //int16 control = 0;
+ */
+
+ // TODO
+
+ //warning("kDeleteScreenItem, view %d, loop %d, cel %d, pri %d", viewId, loopNo, celNo, priority);
+ return NULL_REG;
+}
+
+reg_t kAddPlane(EngineState *s, int argc, reg_t *argv) {
+ reg_t planeObj = argv[0];
+
+ g_sci->_gfxFrameout->kernelAddPlane(planeObj);
+ return NULL_REG;
+}
+
+reg_t kDeletePlane(EngineState *s, int argc, reg_t *argv) {
+ reg_t planeObj = argv[0];
+
+ g_sci->_gfxFrameout->kernelDeletePlane(planeObj);
+ return NULL_REG;
+}
+
+reg_t kUpdatePlane(EngineState *s, int argc, reg_t *argv) {
+ reg_t planeObj = argv[0];
+
+ g_sci->_gfxFrameout->kernelUpdatePlane(planeObj);
+ return s->r_acc;
+}
+
+reg_t kRepaintPlane(EngineState *s, int argc, reg_t *argv) {
+ reg_t picObj = argv[0];
+
+ // TODO
+
+ warning("kRepaintPlane object %04x:%04x", PRINT_REG(picObj));
+ return NULL_REG;
+}
+
+reg_t kAddPicAt(EngineState *s, int argc, reg_t *argv) {
+ reg_t planeObj = argv[0];
+ GuiResourceId pictureId = argv[1].toUint16();
+ int16 forWidth = argv[2].toSint16();
+ // argv[3] seems to be 0 most of the time
+
+ g_sci->_gfxFrameout->kernelAddPicAt(planeObj, forWidth, pictureId);
+ return s->r_acc;
+}
+
+reg_t kGetHighPlanePri(EngineState *s, int argc, reg_t *argv) {
+ return make_reg(0, g_sci->_gfxFrameout->kernelGetHighPlanePri());
+}
+
+reg_t kFrameOut(EngineState *s, int argc, reg_t *argv) {
+ // This kernel call likely seems to be doing the screen updates,
+ // as its called right after a view is updated
+
+ // TODO
+ g_sci->_gfxFrameout->kernelFrameout();
+
+ return NULL_REG;
+}
+
+reg_t kOnMe(EngineState *s, int argc, reg_t *argv) {
+ // Tests if the cursor is on the passed object
+
+ uint16 x = argv[0].toUint16();
+ uint16 y = argv[1].toUint16();
+ reg_t targetObject = argv[2];
+ uint16 illegalBits = argv[3].offset;
+ Common::Rect nsRect;
+
+ // we assume that x, y are local coordinates
+
+ // Get the bounding rectangle of the object
+ nsRect.left = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsLeft));
+ nsRect.top = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsTop));
+ nsRect.right = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsRight));
+ nsRect.bottom = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsBottom));
+
+ // nsRect top/left may be negative, adjust accordingly
+ Common::Rect checkRect = nsRect;
+ if (checkRect.top < 0)
+ checkRect.top = 0;
+ if (checkRect.left < 0)
+ checkRect.left = 0;
+
+ bool contained = checkRect.contains(x, y);
+ if (contained && illegalBits) {
+ // If illegalbits are set, we check the color of the pixel that got clicked on
+ // for now, we return false if the pixel is transparent
+ // although illegalBits may get differently set, don't know yet how this really works out
+ uint16 viewId = readSelectorValue(s->_segMan, targetObject, SELECTOR(view));
+ int16 loopNo = readSelectorValue(s->_segMan, targetObject, SELECTOR(loop));
+ int16 celNo = readSelectorValue(s->_segMan, targetObject, SELECTOR(cel));
+ if (g_sci->_gfxCompare->kernelIsItSkip(viewId, loopNo, celNo, Common::Point(x - nsRect.left, y - nsRect.top)))
+ contained = false;
+ }
+// these hacks shouldn't be needed anymore
+// uint16 itemX = readSelectorValue(s->_segMan, targetObject, SELECTOR(x));
+// uint16 itemY = readSelectorValue(s->_segMan, targetObject, SELECTOR(y));
+
+ // If top and left are negative, we need to adjust coordinates by
+ // the item's x and y (e.g. happens in GK1, day 1, with detective
+ // Mosely's hotspot in his office)
+
+// if (nsRect.left < 0)
+// nsRect.translate(itemX, 0);
+//
+// if (nsRect.top < 0)
+// nsRect.translate(0, itemY);
+
+// // HACK: nsLeft and nsTop can be invalid, so try and fix them here
+// // using x and y (e.g. with the inventory screen in GK1)
+// if (nsRect.left == itemY && nsRect.top == itemX) {
+// // Swap the values, as they're inversed (eh???)
+// nsRect.left = itemX;
+// nsRect.top = itemY;
+// }
+
+ return make_reg(0, contained);
+}
+
+reg_t kIsOnMe(EngineState *s, int argc, reg_t *argv) {
+ // Tests if the cursor is on the passed object, after adjusting the
+ // coordinates of the object according to the object's plane
+
+ uint16 x = argv[0].toUint16();
+ uint16 y = argv[1].toUint16();
+ reg_t targetObject = argv[2];
+ // TODO: argv[3] - it's usually 0
+ Common::Rect nsRect;
+
+ // Get the bounding rectangle of the object
+ nsRect.left = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsLeft));
+ nsRect.top = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsTop));
+ nsRect.right = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsRight));
+ nsRect.bottom = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsBottom));
+
+ // Get the object's plane
+#if 0
+ reg_t planeObject = readSelector(s->_segMan, targetObject, SELECTOR(plane));
+ if (!planeObject.isNull()) {
+ //uint16 itemX = readSelectorValue(s->_segMan, targetObject, SELECTOR(x));
+ //uint16 itemY = readSelectorValue(s->_segMan, targetObject, SELECTOR(y));
+ uint16 planeResY = readSelectorValue(s->_segMan, planeObject, SELECTOR(resY));
+ uint16 planeResX = readSelectorValue(s->_segMan, planeObject, SELECTOR(resX));
+ uint16 planeTop = readSelectorValue(s->_segMan, planeObject, SELECTOR(top));
+ uint16 planeLeft = readSelectorValue(s->_segMan, planeObject, SELECTOR(left));
+ planeTop = (planeTop * g_sci->_gfxScreen->getHeight()) / planeResY;
+ planeLeft = (planeLeft * g_sci->_gfxScreen->getWidth()) / planeResX;
+
+ // Adjust the bounding rectangle of the object by the object's
+ // actual X, Y coordinates
+ nsRect.top = ((nsRect.top * g_sci->_gfxScreen->getHeight()) / planeResY);
+ nsRect.left = ((nsRect.left * g_sci->_gfxScreen->getWidth()) / planeResX);
+ nsRect.bottom = ((nsRect.bottom * g_sci->_gfxScreen->getHeight()) / planeResY);
+ nsRect.right = ((nsRect.right * g_sci->_gfxScreen->getWidth()) / planeResX);
+
+ nsRect.translate(planeLeft, planeTop);
}
+#endif
+ //warning("kIsOnMe: (%d, %d) on object %04x:%04x, parameter %d", argv[0].toUint16(), argv[1].toUint16(), PRINT_REG(argv[2]), argv[3].toUint16());
- if (videoDecoder) {
- playVideo(videoDecoder);
+ return make_reg(0, nsRect.contains(x, y));
+}
- // HACK: Switch back to 8bpp if we played a QuickTime video.
- // We also won't be copying the screen to the SCI screen...
- if (g_system->getScreenFormat().bytesPerPixel != 1)
- initGraphics(screenWidth, screenHeight, screenWidth > 320);
- else {
- g_sci->_gfxScreen->kernelSyncWithFramebuffer();
- g_sci->_gfxPalette->kernelSyncScreenPalette();
+reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv) {
+ // TODO: argument 0 is usually 0, and arguments 1 and 2 are usually 1
+ switch (argv[0].toUint16()) {
+ case 0: {
+ if (argc != 4) {
+ warning("kCreateTextBitmap(0): expected 4 arguments, got %i", argc);
+ return NULL_REG;
}
+ reg_t object = argv[3];
+ Common::String text = s->_segMan->getString(readSelector(s->_segMan, object, SELECTOR(text)));
+ break;
+ }
+ default:
+ warning("CreateTextBitmap(%d)", argv[0].toUint16());
}
- if (reshowCursor)
- g_sci->_gfxCursor->kernelShow();
-
- return s->r_acc;
+ return NULL_REG;
}
-#ifdef ENABLE_SCI32
reg_t kRobot(EngineState *s, int argc, reg_t *argv) {
int16 subop = argv[0].toUint16();
@@ -1234,6 +1361,10 @@ reg_t kRobot(EngineState *s, int argc, reg_t *argv) {
int16 x = argv[4].toUint16();
int16 y = argv[5].toUint16();
warning("kRobot(init), id %d, obj %04x:%04x, flag %d, x=%d, y=%d", id, PRINT_REG(obj), flag, x, y);
+ GfxRobot *test = new GfxRobot(g_sci->getResMan(), g_sci->_gfxScreen, id);
+ test->draw();
+ delete test;
+
}
break;
case 1: // LSL6 hires (startup)
@@ -1255,140 +1386,6 @@ reg_t kRobot(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
-reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv) {
- uint16 operation = argv[0].toUint16();
- Graphics::VideoDecoder *videoDecoder = 0;
- bool reshowCursor = g_sci->_gfxCursor->isVisible();
- Common::String fileName, warningMsg;
-
- switch (operation) {
- case 0: // init
- // This is actually meant to init the video file, but we play it instead
- fileName = s->_segMan->derefString(argv[1]);
- // TODO: argv[2] (usually null). When it exists, it points to an "Event" object,
- // that holds no data initially (e.g. in the intro of Phantasmagoria 1 demo).
- // Perhaps it's meant for syncing
- if (argv[2] != NULL_REG)
- warning("kPlayVMD: third parameter isn't 0 (it's %04x:%04x - %s)", PRINT_REG(argv[2]), s->_segMan->getObjectName(argv[2]));
-
- videoDecoder = new VMDDecoder(g_system->getMixer());
-
- if (reshowCursor)
- g_sci->_gfxCursor->kernelHide();
-
- if (videoDecoder && videoDecoder->loadFile(fileName))
- playVideo(videoDecoder);
-
- if (reshowCursor)
- g_sci->_gfxCursor->kernelShow();
- break;
- case 1:
- {
- // Set VMD parameters. Called with a maximum of 6 parameters:
- //
- // x, y, flags, gammaBoost, gammaFirst, gammaLast
- //
- // Flags are as follows:
- // bit 0 doubled
- // bit 1 "drop frames"?
- // bit 2 insert black lines
- // bit 3 unknown
- // bit 4 gamma correction
- // bit 5 hold black frame
- // bit 6 hold last frame
- // bit 7 unknown
- // bit 8 stretch
-
- // gammaBoost boosts palette colors in the range gammaFirst to
- // gammaLast, but only if bit 4 in flags is set. Percent value such that
- // 0% = no amplification These three parameters are optional if bit 4 is
- // clear. Also note that the x, y parameters play subtle games if used
- // with subfx 21. The subtleness has to do with creation of temporary
- // planes and positioning relative to such planes.
-
- int flags = argv[3].offset;
- Common::String flagspec;
-
- if (argc > 3) {
- if (flags & 1)
- flagspec += "doubled ";
- if (flags & 2)
- flagspec += "dropframes ";
- if (flags & 4)
- flagspec += "blacklines ";
- if (flags & 8)
- flagspec += "bit3 ";
- if (flags & 16)
- flagspec += "gammaboost ";
- if (flags & 32)
- flagspec += "holdblack ";
- if (flags & 64)
- flagspec += "holdlast ";
- if (flags & 128)
- flagspec += "bit7 ";
- if (flags & 256)
- flagspec += "stretch";
-
- warning("VMDFlags: %s", flagspec.c_str());
- }
-
- warning("x, y: %d, %d", argv[1].offset, argv[2].offset);
-
- if (argc > 4 && flags & 16)
- warning("gammaBoost: %d%% between palette entries %d and %d", argv[4].offset, argv[5].offset, argv[6].offset);
- break;
- }
- case 6:
- // Play, perhaps? Or stop? This is the last call made, and takes no extra parameters
- case 14:
- // Takes an additional integer parameter (e.g. 3)
- case 16:
- // Takes an additional parameter, usually 0
- case 21:
- // Looks to be setting the video size and position. Called with 4 extra integer
- // parameters (e.g. 86, 41, 235, 106)
- default:
- warningMsg = "PlayVMD - unsupported subop. Params: " +
- Common::String::printf("%d", argc) + " (";
-
- for (int i = 0; i < argc; i++) {
- warningMsg += Common::String::printf("%04x:%04x", PRINT_REG(argv[i]));
- warningMsg += (i == argc - 1 ? ")" : ", ");
- }
-
- warning("%s", warningMsg.c_str());
- break;
- }
-
- return s->r_acc;
-}
-
#endif
-reg_t kSetVideoMode(EngineState *s, int argc, reg_t *argv) {
- // This call is used for KQ6's intro. It has one parameter, which is 1 when
- // the intro begins, and 0 when it ends. It is suspected that this is
- // actually a flag to enable video planar memory access, as the video
- // decoder in KQ6 is specifically written for the planar memory model.
- // Planar memory mode access was used for VGA "Mode X" (320x240 resolution,
- // although the intro in KQ6 is 320x200).
- // Refer to http://en.wikipedia.org/wiki/Mode_X
-
- //warning("STUB: SetVideoMode %d", argv[0].toUint16());
- return s->r_acc;
-}
-
-// New calls for SCI11. Using those is only needed when using text-codes so that
-// one is able to change font and/or color multiple times during kDisplay and
-// kDrawControl
-reg_t kTextFonts(EngineState *s, int argc, reg_t *argv) {
- g_sci->_gfxText16->kernelTextFonts(argc, argv);
- return s->r_acc;
-}
-
-reg_t kTextColors(EngineState *s, int argc, reg_t *argv) {
- g_sci->_gfxText16->kernelTextColors(argc, argv);
- return s->r_acc;
-}
-
} // End of namespace Sci
diff --git a/engines/sci/engine/klists.cpp b/engines/sci/engine/klists.cpp
index 0701883a9b..93e95099f5 100644
--- a/engines/sci/engine/klists.cpp
+++ b/engines/sci/engine/klists.cpp
@@ -34,10 +34,15 @@ static bool isSaneNodePointer(SegManager *segMan, reg_t addr) {
reg_t prev = addr;
do {
- Node *node = segMan->lookupNode(addr);
+ Node *node = segMan->lookupNode(addr, false);
if (!node) {
- error("isSaneNodePointer: Node at %04x:%04x wasn't found", PRINT_REG(addr));
+ if ((g_sci->getGameId() == GID_ICEMAN) && (g_sci->getEngineState()->currentRoomNumber() == 40)) {
+ // ICEMAN: when plotting course, unDrawLast is called by startPlot::changeState
+ // there is no previous entry so we get 0 in here
+ } else {
+ error("isSaneNodePointer: Node at %04x:%04x wasn't found", PRINT_REG(addr));
+ }
return false;
}
@@ -70,8 +75,8 @@ static void checkListPointer(SegManager *segMan, reg_t addr) {
// Empty list is fine
} else if (!list->first.isNull() && !list->last.isNull()) {
// Normal list
- Node *node_a = segMan->lookupNode(list->first);
- Node *node_z = segMan->lookupNode(list->last);
+ Node *node_a = segMan->lookupNode(list->first, false);
+ Node *node_z = segMan->lookupNode(list->last, false);
if (!node_a) {
error("checkListPointer (list %04x:%04x): missing first node", PRINT_REG(addr));
@@ -251,6 +256,19 @@ reg_t kNodeValue(EngineState *s, int argc, reg_t *argv) {
reg_t kAddToFront(EngineState *s, int argc, reg_t *argv) {
addToFront(s, argv[0], argv[1]);
+
+ if (argc == 3)
+ s->_segMan->lookupNode(argv[1])->key = argv[2];
+
+ return s->r_acc;
+}
+
+reg_t kAddToEnd(EngineState *s, int argc, reg_t *argv) {
+ addToEnd(s, argv[0], argv[1]);
+
+ if (argc == 3)
+ s->_segMan->lookupNode(argv[1])->key = argv[2];
+
return s->r_acc;
}
@@ -294,11 +312,6 @@ reg_t kAddAfter(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
-reg_t kAddToEnd(EngineState *s, int argc, reg_t *argv) {
- addToEnd(s, argv[0], argv[1]);
- return s->r_acc;
-}
-
reg_t kFindKey(EngineState *s, int argc, reg_t *argv) {
reg_t node_pos;
reg_t key = argv[1];
@@ -580,65 +593,134 @@ reg_t kListAllTrue(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
-// In SCI2.1, all the list functions were merged in one
reg_t kList(EngineState *s, int argc, reg_t *argv) {
+ if (!s)
+ return make_reg(0, getSciVersion());
+ error("not supposed to call this");
+}
+
+reg_t kAddBefore(EngineState *s, int argc, reg_t *argv) {
+ error("Unimplemented function kAddBefore called");
+ return s->r_acc;
+}
+
+reg_t kMoveToFront(EngineState *s, int argc, reg_t *argv) {
+ error("Unimplemented function kMoveToFront called");
+ return s->r_acc;
+}
+
+reg_t kMoveToEnd(EngineState *s, int argc, reg_t *argv) {
+ error("Unimplemented function kMoveToEnd called");
+ return s->r_acc;
+}
+
+reg_t kArray(EngineState *s, int argc, reg_t *argv) {
switch (argv[0].toUint16()) {
- case 0:
- return kNewList(s, argc - 1, argv + 1);
- case 1:
- return kDisposeList(s, argc - 1, argv + 1);
- case 2:
- return kNewNode(s, argc - 1, argv + 1);
- case 3:
- return kFirstNode(s, argc - 1, argv + 1);
- case 4:
- return kLastNode(s, argc - 1, argv + 1);
- case 5:
- return kEmptyList(s, argc - 1, argv + 1);
- case 6:
- return kNextNode(s, argc - 1, argv + 1);
- case 7:
- return kPrevNode(s, argc - 1, argv + 1);
- case 8:
- return kNodeValue(s, argc - 1, argv + 1);
- case 9:
- return kAddAfter(s, argc - 1, argv + 1);
- case 10:
- return kAddToFront(s, argc - 1, argv + 1);
- case 11:
- return kAddToEnd(s, argc - 1, argv + 1);
- case 12:
- error("kList: unimplemented subfunction kAddBefore");
- //return kAddBefore(s, argc - 1, argv + 1);
- return NULL_REG;
- case 13:
- error("kList: unimplemented subfunction kMoveToFront");
- //return kMoveToFront(s, argc - 1, argv + 1);
- return NULL_REG;
- case 14:
- error("kList: unimplemented subfunction kMoveToEnd");
- //return kMoveToEnd(s, argc - 1, argv + 1);
- return NULL_REG;
- case 15:
- return kFindKey(s, argc - 1, argv + 1);
- case 16:
- return kDeleteKey(s, argc - 1, argv + 1);
- case 17:
- return kListAt(s, argc - 1, argv + 1);
- case 18:
- return kListIndexOf(s, argc - 1, argv + 1);
- case 19:
- return kListEachElementDo(s, argc - 1, argv + 1);
- case 20:
- return kListFirstTrue(s, argc - 1, argv + 1);
- case 21:
- return kListAllTrue(s, argc - 1, argv + 1);
- case 22:
- return kSort(s, argc - 1, argv + 1);
+ case 0: { // New
+ reg_t arrayHandle;
+ SciArray<reg_t> *array = s->_segMan->allocateArray(&arrayHandle);
+ array->setType(argv[2].toUint16());
+ array->setSize(argv[1].toUint16());
+ return arrayHandle;
+ }
+ case 1: { // Size
+ SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]);
+ return make_reg(0, array->getSize());
+ }
+ case 2: { // At (return value at an index)
+ SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]);
+ return array->getValue(argv[2].toUint16());
+ }
+ case 3: { // Atput (put value at an index)
+ SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]);
+
+ uint32 index = argv[2].toUint16();
+ uint32 count = argc - 3;
+
+ if (index + count > 65535)
+ break;
+
+ if (array->getSize() < index + count)
+ array->setSize(index + count);
+
+ for (uint16 i = 0; i < count; i++)
+ array->setValue(i + index, argv[i + 3]);
+
+ return argv[1]; // We also have to return the handle
+ }
+ case 4: // Free
+ // Freeing of arrays is handled by the garbage collector
+ return s->r_acc;
+ case 5: { // Fill
+ SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]);
+ uint16 index = argv[2].toUint16();
+
+ // A count of -1 means fill the rest of the array
+ uint16 count = argv[3].toSint16() == -1 ? array->getSize() - index : argv[3].toUint16();
+ uint16 arraySize = array->getSize();
+
+ if (arraySize < index + count)
+ array->setSize(index + count);
+
+ for (uint16 i = 0; i < count; i++)
+ array->setValue(i + index, argv[4]);
+
+ return argv[1];
+ }
+ case 6: { // Cpy
+ if (s->_segMan->getSegmentObj(argv[1].segment)->getType() != SEG_TYPE_ARRAY ||
+ s->_segMan->getSegmentObj(argv[3].segment)->getType() != SEG_TYPE_ARRAY) {
+ // Happens in the RAMA demo
+ warning("kArray(Cpy): Request to copy a segment which isn't an array, ignoring");
+ return NULL_REG;
+ }
+
+ SciArray<reg_t> *array1 = s->_segMan->lookupArray(argv[1]);
+ SciArray<reg_t> *array2 = s->_segMan->lookupArray(argv[3]);
+ uint32 index1 = argv[2].toUint16();
+ uint32 index2 = argv[4].toUint16();
+
+ // The original engine ignores bad copies too
+ if (index2 > array2->getSize())
+ break;
+
+ // A count of -1 means fill the rest of the array
+ uint32 count = argv[5].toSint16() == -1 ? array2->getSize() - index2 : argv[5].toUint16();
+
+ if (array1->getSize() < index1 + count)
+ array1->setSize(index1 + count);
+
+ for (uint16 i = 0; i < count; i++)
+ array1->setValue(i + index1, array2->getValue(i + index2));
+
+ return argv[1];
+ }
+ case 7: // Cmp
+ // Not implemented in SSCI
+ return s->r_acc;
+ case 8: { // Dup
+ SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]);
+ reg_t arrayHandle;
+ SciArray<reg_t> *dupArray = s->_segMan->allocateArray(&arrayHandle);
+
+ dupArray->setType(array->getType());
+ dupArray->setSize(array->getSize());
+
+ for (uint32 i = 0; i < array->getSize(); i++)
+ dupArray->setValue(i, array->getValue(i));
+
+ return arrayHandle;
+ }
+ case 9: // Getdata
+ if (!s->_segMan->isHeapObject(argv[1]))
+ return argv[1];
+
+ return readSelector(s->_segMan, argv[1], SELECTOR(data));
default:
- error("kList: Unhandled case %d", argv[0].toUint16());
- return NULL_REG;
+ error("Unknown kArray subop %d", argv[0].toUint16());
}
+
+ return NULL_REG;
}
#endif
diff --git a/engines/sci/engine/kmath.cpp b/engines/sci/engine/kmath.cpp
index eab964d624..bdc705cae3 100644
--- a/engines/sci/engine/kmath.cpp
+++ b/engines/sci/engine/kmath.cpp
@@ -29,10 +29,27 @@
namespace Sci {
reg_t kRandom(EngineState *s, int argc, reg_t *argv) {
- int fromNumber = argv[0].toUint16();
- int toNumber = argv[1].toUint16();
- double randomNumber = fromNumber + ((toNumber + 1.0 - fromNumber) * (rand() / (RAND_MAX + 1.0)));
- return make_reg(0, (int)randomNumber);
+ switch (argc) {
+ case 1: // set seed to argv[0]
+ // SCI0/SCI01 just reset the seed to 0 instead of using argv[0] at all
+ return NULL_REG;
+
+ case 2: { // get random number
+ int fromNumber = argv[0].toUint16();
+ int toNumber = argv[1].toUint16();
+ double randomNumber = fromNumber + ((toNumber + 1.0 - fromNumber) * (rand() / (RAND_MAX + 1.0)));
+ return make_reg(0, (int)randomNumber);
+ }
+
+ case 3: // get seed
+ // SCI0/01 did not support this at all
+ // Actually we would have to return the previous seed
+ error("kRandom: scripts asked for previous seed");
+ break;
+
+ default:
+ error("kRandom: unsupported argc");
+ }
}
reg_t kAbs(EngineState *s, int argc, reg_t *argv) {
diff --git a/engines/sci/engine/kmisc.cpp b/engines/sci/engine/kmisc.cpp
index b8c62210f9..305e202ae9 100644
--- a/engines/sci/engine/kmisc.cpp
+++ b/engines/sci/engine/kmisc.cpp
@@ -368,4 +368,39 @@ reg_t kEmpty(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
+reg_t kStub(EngineState *s, int argc, reg_t *argv) {
+ Kernel *kernel = g_sci->getKernel();
+ int kernelCallNr = -1;
+
+ Common::List<ExecStack>::iterator callIterator = s->_executionStack.end();
+ if (callIterator != s->_executionStack.begin()) {
+ callIterator--;
+ ExecStack lastCall = *callIterator;
+ kernelCallNr = lastCall.debugSelector;
+ }
+
+ Common::String warningMsg = "Dummy function k" + kernel->getKernelName(kernelCallNr) +
+ Common::String::printf("[%x]", kernelCallNr) +
+ " invoked. Params: " +
+ Common::String::printf("%d", argc) + " (";
+
+ for (int i = 0; i < argc; i++) {
+ warningMsg += Common::String::printf("%04x:%04x", PRINT_REG(argv[i]));
+ warningMsg += (i == argc - 1 ? ")" : ", ");
+ }
+
+ warning("%s", warningMsg.c_str());
+ return s->r_acc;
+}
+
+reg_t kStubNull(EngineState *s, int argc, reg_t *argv) {
+ kStub(s, argc, argv);
+ return NULL_REG;
+}
+
+reg_t kDummy(EngineState *s, int argc, reg_t *argv) {
+ kStub(s, argc, argv);
+ error("Kernel function was called, which was considered to be unused - see log for details");
+}
+
} // End of namespace Sci
diff --git a/engines/sci/engine/kmovement.cpp b/engines/sci/engine/kmovement.cpp
index ccef3d862a..114b6eb755 100644
--- a/engines/sci/engine/kmovement.cpp
+++ b/engines/sci/engine/kmovement.cpp
@@ -267,6 +267,15 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) {
bdelta = (int16)readSelectorValue(segMan, mover, SELECTOR(b_incr));
axis = (int16)readSelectorValue(segMan, mover, SELECTOR(b_xAxis));
+ if ((getSciVersion() >= SCI_VERSION_1_LATE)) {
+ // Mixed-Up Fairy Tales has no xLast/yLast selectors
+ if (SELECTOR(xLast) != -1) {
+ // save last position into mover
+ writeSelectorValue(segMan, mover, SELECTOR(xLast), x);
+ writeSelectorValue(segMan, mover, SELECTOR(yLast), y);
+ }
+ }
+
//printf("movecnt %d, move speed %d\n", movcnt, max_movcnt);
if (g_sci->_features->handleMoveCount()) {
@@ -316,14 +325,24 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) {
debugC(2, kDebugLevelBresen, "New data: (x,y)=(%d,%d), di=%d", x, y, bdi);
+ bool collision = false;
+ reg_t cantBeHere = NULL_REG;
+
if (SELECTOR(cantBeHere) != -1) {
+ // adding this here for hoyle 3 to get happy. CantBeHere is a dummy in hoyle 3 and acc is != 0 so we would
+ // get a collision otherwise
+ s->r_acc = NULL_REG;
invokeSelector(s, client, SELECTOR(cantBeHere), argc, argv);
- s->r_acc = make_reg(0, !s->r_acc.offset);
+ if (!s->r_acc.isNull())
+ collision = true;
+ cantBeHere = s->r_acc;
} else {
invokeSelector(s, client, SELECTOR(canBeHere), argc, argv);
+ if (s->r_acc.isNull())
+ collision = true;
}
- if (!s->r_acc.offset) { // Contains the return value
+ if (collision) {
signal = readSelectorValue(segMan, client, SELECTOR(signal));
writeSelectorValue(segMan, client, SELECTOR(x), oldx);
@@ -331,13 +350,16 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) {
writeSelectorValue(segMan, client, SELECTOR(signal), (signal | kSignalHitObstacle));
debugC(2, kDebugLevelBresen, "Finished mover %04x:%04x by collision", PRINT_REG(mover));
- completed = 1;
+ // We shall not set completed in this case, sierra sci also doesn't do it
+ // if we set call .moveDone in those cases qfg1 vga gate at the castle and lsl1 casino door will not work
}
if ((getSciVersion() >= SCI_VERSION_1_EGA))
if (completed)
invokeSelector(s, mover, SELECTOR(moveDone), argc, argv);
+ if (SELECTOR(cantBeHere) != -1)
+ return cantBeHere;
return make_reg(0, completed);
}
diff --git a/engines/sci/engine/kparse.cpp b/engines/sci/engine/kparse.cpp
index 45493a95d2..552e425906 100644
--- a/engines/sci/engine/kparse.cpp
+++ b/engines/sci/engine/kparse.cpp
@@ -31,6 +31,8 @@
#include "sci/engine/message.h"
#include "sci/engine/kernel.h"
+//#define DEBUG_PARSER
+
namespace Sci {
/*************************************************************/
@@ -60,8 +62,8 @@ reg_t kSaid(EngineState *s, int argc, reg_t *argv) {
}
#ifdef DEBUG_PARSER
- debugC(2, kDebugLevelParser, "Said block:", 0);
- s->_voc->decipherSaidBlock(said_block);
+ printf("Said block: ");
+ g_sci->getVocabulary()->debugDecipherSaidBlock(said_block);
#endif
if (voc->parser_event.isNull() || (readSelectorValue(s->_segMan, voc->parser_event, SELECTOR(claimed)))) {
@@ -93,6 +95,7 @@ reg_t kParse(EngineState *s, int argc, reg_t *argv) {
char *error;
ResultWordList words;
reg_t event = argv[1];
+ g_sci->checkVocabularySwitch();
Vocabulary *voc = g_sci->getVocabulary();
voc->parser_event = event;
reg_t params[2] = { voc->parser_base, stringpos };
@@ -106,7 +109,7 @@ reg_t kParse(EngineState *s, int argc, reg_t *argv) {
s->r_acc = make_reg(0, 1);
#ifdef DEBUG_PARSER
- debugC(2, kDebugLevelParser, "Parsed to the following blocks:", 0);
+ debugC(2, kDebugLevelParser, "Parsed to the following blocks:");
for (ResultWordList::const_iterator i = words.begin(); i != words.end(); ++i)
debugC(2, kDebugLevelParser, " Type[%04x] Group[%04x]", i->_class, i->_group);
diff --git a/engines/sci/engine/kpathing.cpp b/engines/sci/engine/kpathing.cpp
index fdaae3e121..fdebc0599c 100644
--- a/engines/sci/engine/kpathing.cpp
+++ b/engines/sci/engine/kpathing.cpp
@@ -933,9 +933,11 @@ static Common::Point *fixup_start_point(PathfindingState *s, const Common::Point
case POLY_NEAREST_ACCESS:
if (cont == CONT_INSIDE) {
if (s->_prependPoint != NULL) {
- // We shouldn't get here twice
+ // We shouldn't get here twice.
+ // We need to break in this case, otherwise we'll end in an infinite
+ // loop.
warning("AvoidPath: start point is contained in multiple polygons");
- continue;
+ break;
}
if (s->findNearPoint(start, (*it), new_start) != PF_OK) {
@@ -944,7 +946,7 @@ static Common::Point *fixup_start_point(PathfindingState *s, const Common::Point
}
if ((type == POLY_BARRED_ACCESS) || (type == POLY_CONTAINED_ACCESS))
- warning("AvoidPath: start position at unreachable location");
+ debugC(2, kDebugLevelAvoidPath, "AvoidPath: start position at unreachable location");
// The original start position is in an invalid location, so we
// use the moved point and add the original one to the final path
@@ -987,9 +989,14 @@ static Common::Point *fixup_end_point(PathfindingState *s, const Common::Point &
case POLY_NEAREST_ACCESS:
if (cont != CONT_OUTSIDE) {
if (s->_appendPoint != NULL) {
- // We shouldn't get here twice
+ // We shouldn't get here twice.
+ // Happens in LB2CD, inside the speakeasy when walking from the
+ // speakeasy (room 310) into the bathroom (room 320), after having
+ // consulted the notebook (bug #3036299).
+ // We need to break in this case, otherwise we'll end in an infinite
+ // loop.
warning("AvoidPath: end point is contained in multiple polygons");
- continue;
+ break;
}
// The original end position is in an invalid location, so we move the point
@@ -1315,7 +1322,7 @@ static void AStar(PathfindingState *s) {
}
if (openSet.empty())
- warning("[avoidpath] End point (%i, %i) is unreachable", s->vertex_end->v.x, s->vertex_end->v.y);
+ debugC(2, kDebugLevelAvoidPath, "AvoidPath: End point (%i, %i) is unreachable", s->vertex_end->v.x, s->vertex_end->v.y);
}
static reg_t allocateOutputArray(SegManager *segMan, int size) {
@@ -1770,4 +1777,13 @@ reg_t kMergePoly(EngineState *s, int argc, reg_t *argv) {
return output;
}
+#ifdef ENABLE_SCI32
+
+reg_t kInPolygon(EngineState *s, int argc, reg_t *argv) {
+ // kAvoidPath already implements this
+ return kAvoidPath(s, argc, argv);
+}
+
+#endif
+
} // End of namespace Sci
diff --git a/engines/sci/engine/kscripts.cpp b/engines/sci/engine/kscripts.cpp
index 029943b070..e211867ef9 100644
--- a/engines/sci/engine/kscripts.cpp
+++ b/engines/sci/engine/kscripts.cpp
@@ -38,7 +38,7 @@ namespace Sci {
// Loads arbitrary resources of type 'restype' with resource numbers 'resnrs'
// This implementation ignores all resource numbers except the first one.
reg_t kLoad(EngineState *s, int argc, reg_t *argv) {
- ResourceType restype = (ResourceType)(argv[0].toUint16() & 0x7f);
+ ResourceType restype = g_sci->getResMan()->convertResType(argv[0].toUint16());
int resnr = argv[1].toUint16();
// Request to dynamically allocate hunk memory for later use
@@ -53,7 +53,7 @@ reg_t kLoad(EngineState *s, int argc, reg_t *argv) {
// 1 or 3+ parameters is not right according to sierra sci
reg_t kUnLoad(EngineState *s, int argc, reg_t *argv) {
if (argc >= 2) {
- ResourceType restype = (ResourceType)(argv[0].toUint16() & 0x7f);
+ ResourceType restype = g_sci->getResMan()->convertResType(argv[0].toUint16());
reg_t resnr = argv[1];
// WORKAROUND for a broken script in room 320 in Castle of Dr. Brain.
@@ -73,7 +73,7 @@ reg_t kUnLoad(EngineState *s, int argc, reg_t *argv) {
reg_t kLock(EngineState *s, int argc, reg_t *argv) {
int state = argc > 2 ? argv[2].toUint16() : 1;
- ResourceType type = (ResourceType)(argv[0].toUint16() & 0x7f);
+ ResourceType type = g_sci->getResMan()->convertResType(argv[0].toUint16());
ResourceId id = ResourceId(type, argv[1].toUint16());
Resource *which;
@@ -104,8 +104,10 @@ reg_t kLock(EngineState *s, int argc, reg_t *argv) {
if (id.getType() == kResourceTypeInvalid)
warning("[resMan] Attempt to unlock resource %i of invalid type %i", id.getNumber(), type);
else
- // Happens in CD games (e.g. LSL6CD) with the message resource
- warning("[resMan] Attempt to unlock non-existant resource %s", id.toString().c_str());
+ // Happens in CD games (e.g. LSL6CD) with the message
+ // resource. It isn't fatal, and it's usually caused
+ // by leftover scripts.
+ debugC(2, kDebugLevelResMan, "[resMan] Attempt to unlock non-existant resource %s", id.toString().c_str());
}
}
break;
@@ -115,7 +117,7 @@ reg_t kLock(EngineState *s, int argc, reg_t *argv) {
reg_t kResCheck(EngineState *s, int argc, reg_t *argv) {
Resource *res = NULL;
- ResourceType restype = (ResourceType)(argv[0].toUint16() & 0x7f);
+ ResourceType restype = g_sci->getResMan()->convertResType(argv[0].toUint16());
if (restype == kResourceTypeVMD) {
char fileName[10];
diff --git a/engines/sci/engine/ksound.cpp b/engines/sci/engine/ksound.cpp
index 69ae68674b..4e5ddc5e96 100644
--- a/engines/sci/engine/ksound.cpp
+++ b/engines/sci/engine/ksound.cpp
@@ -54,7 +54,7 @@ CREATE_DOSOUND_FORWARD(DoSoundMute)
CREATE_DOSOUND_FORWARD(DoSoundStop)
CREATE_DOSOUND_FORWARD(DoSoundStopAll)
CREATE_DOSOUND_FORWARD(DoSoundPause)
-CREATE_DOSOUND_FORWARD(DoSoundResume)
+CREATE_DOSOUND_FORWARD(DoSoundResumeAfterRestore)
CREATE_DOSOUND_FORWARD(DoSoundMasterVolume)
CREATE_DOSOUND_FORWARD(DoSoundUpdate)
CREATE_DOSOUND_FORWARD(DoSoundFade)
diff --git a/engines/sci/engine/kstring.cpp b/engines/sci/engine/kstring.cpp
index f2f9543ad2..9254bce9c1 100644
--- a/engines/sci/engine/kstring.cpp
+++ b/engines/sci/engine/kstring.cpp
@@ -566,7 +566,7 @@ reg_t kMessage(EngineState *s, int argc, reg_t *argv) {
}
reg_t kSetQuitStr(EngineState *s, int argc, reg_t *argv) {
- Common::String quitStr = s->_segMan->getString(argv[0]);
+ //Common::String quitStr = s->_segMan->getString(argv[0]);
//debug("Setting quit string to '%s'", quitStr.c_str());
return s->r_acc;
}
@@ -592,4 +592,193 @@ reg_t kStrSplit(EngineState *s, int argc, reg_t *argv) {
return argv[0];
}
+#ifdef ENABLE_SCI32
+
+reg_t kText(EngineState *s, int argc, reg_t *argv) {
+ switch (argv[0].toUint16()) {
+ case 0:
+ return kTextSize(s, argc - 1, argv + 1);
+ default:
+ // TODO: Other subops here too, perhaps kTextColors and kTextFonts
+ warning("kText(%d)", argv[0].toUint16());
+ break;
+ }
+
+ return s->r_acc;
+}
+
+reg_t kString(EngineState *s, int argc, reg_t *argv) {
+ switch (argv[0].toUint16()) {
+ case 0: { // New
+ reg_t stringHandle;
+ SciString *string = s->_segMan->allocateString(&stringHandle);
+ string->setSize(argv[1].toUint16());
+
+ // Make sure the first character is a null character
+ if (string->getSize() > 0)
+ string->setValue(0, 0);
+
+ return stringHandle;
+ }
+ case 1: // Size
+ return make_reg(0, s->_segMan->getString(argv[1]).size());
+ case 2: { // At (return value at an index)
+ if (argv[1].segment == s->_segMan->getStringSegmentId())
+ return make_reg(0, s->_segMan->lookupString(argv[1])->getRawData()[argv[2].toUint16()]);
+
+ return make_reg(0, s->_segMan->getString(argv[1])[argv[2].toUint16()]);
+ }
+ case 3: { // Atput (put value at an index)
+ SciString *string = s->_segMan->lookupString(argv[1]);
+
+ uint32 index = argv[2].toUint16();
+ uint32 count = argc - 3;
+
+ if (index + count > 65535)
+ break;
+
+ if (string->getSize() < index + count)
+ string->setSize(index + count);
+
+ for (uint16 i = 0; i < count; i++)
+ string->setValue(i + index, argv[i + 3].toUint16());
+
+ return argv[1]; // We also have to return the handle
+ }
+ case 4: // Free
+ // Freeing of strings is handled by the garbage collector
+ return s->r_acc;
+ case 5: { // Fill
+ SciString *string = s->_segMan->lookupString(argv[1]);
+ uint16 index = argv[2].toUint16();
+
+ // A count of -1 means fill the rest of the array
+ uint16 count = argv[3].toSint16() == -1 ? string->getSize() - index : argv[3].toUint16();
+ uint16 stringSize = string->getSize();
+
+ if (stringSize < index + count)
+ string->setSize(index + count);
+
+ for (uint16 i = 0; i < count; i++)
+ string->setValue(i + index, argv[4].toUint16());
+
+ return argv[1];
+ }
+ case 6: { // Cpy
+ const char *string2 = 0;
+ uint32 string2Size = 0;
+
+ if (argv[3].segment == s->_segMan->getStringSegmentId()) {
+ SciString *string = s->_segMan->lookupString(argv[3]);
+ string2 = string->getRawData();
+ string2Size = string->getSize();
+ } else {
+ Common::String string = s->_segMan->getString(argv[3]);
+ string2 = string.c_str();
+ string2Size = string.size() + 1;
+ }
+
+ uint32 index1 = argv[2].toUint16();
+ uint32 index2 = argv[4].toUint16();
+
+ // The original engine ignores bad copies too
+ if (index2 > string2Size)
+ break;
+
+ // A count of -1 means fill the rest of the array
+ uint32 count = argv[5].toSint16() == -1 ? string2Size - index2 + 1 : argv[5].toUint16();
+
+ // We have a special case here for argv[1] being a system string
+ if (argv[1].segment == s->_segMan->getSysStringsSegment()) {
+ // Resize if necessary
+ const uint16 sysStringId = argv[1].toUint16();
+ SystemString *sysString = s->_segMan->getSystemString(sysStringId);
+ assert(sysString);
+ if ((uint32)sysString->_maxSize < index1 + count) {
+ free(sysString->_value);
+ sysString->_maxSize = index1 + count;
+ sysString->_value = (char *)calloc(index1 + count, sizeof(char));
+ }
+
+ strncpy(sysString->_value + index1, string2 + index2, count);
+ } else {
+ SciString *string1 = s->_segMan->lookupString(argv[1]);
+
+ if (string1->getSize() < index1 + count)
+ string1->setSize(index1 + count);
+
+ // Note: We're accessing from c_str() here because the
+ // string's size ignores the trailing 0 and therefore
+ // triggers an assert when doing string2[i + index2].
+ for (uint16 i = 0; i < count; i++)
+ string1->setValue(i + index1, string2[i + index2]);
+ }
+
+ } return argv[1];
+ case 7: { // Cmp
+ Common::String string1 = argv[1].isNull() ? "" : s->_segMan->getString(argv[1]);
+ Common::String string2 = argv[2].isNull() ? "" : s->_segMan->getString(argv[2]);
+
+ if (argc == 4) // Strncmp
+ return make_reg(0, strncmp(string1.c_str(), string2.c_str(), argv[3].toUint16()));
+ else // Strcmp
+ return make_reg(0, strcmp(string1.c_str(), string2.c_str()));
+ }
+ case 8: { // Dup
+ const char *rawString = 0;
+ uint32 size = 0;
+
+ if (argv[1].segment == s->_segMan->getStringSegmentId()) {
+ SciString *string = s->_segMan->lookupString(argv[1]);
+ rawString = string->getRawData();
+ size = string->getSize();
+ } else {
+ Common::String string = s->_segMan->getString(argv[1]);
+ rawString = string.c_str();
+ size = string.size() + 1;
+ }
+
+ reg_t stringHandle;
+ SciString *dupString = s->_segMan->allocateString(&stringHandle);
+ dupString->setSize(size);
+
+ for (uint32 i = 0; i < size; i++)
+ dupString->setValue(i, rawString[i]);
+
+ return stringHandle;
+ }
+ case 9: // Getdata
+ if (!s->_segMan->isHeapObject(argv[1]))
+ return argv[1];
+
+ return readSelector(s->_segMan, argv[1], SELECTOR(data));
+ case 10: // Stringlen
+ return make_reg(0, s->_segMan->strlen(argv[1]));
+ case 11: { // Printf
+ reg_t stringHandle;
+ s->_segMan->allocateString(&stringHandle);
+
+ reg_t *adjustedArgs = new reg_t[argc];
+ adjustedArgs[0] = stringHandle;
+ memcpy(&adjustedArgs[1], argv + 1, (argc - 1) * sizeof(reg_t));
+
+ kFormat(s, argc, adjustedArgs);
+ delete[] adjustedArgs;
+ return stringHandle;
+ }
+ case 12: // Printf Buf
+ return kFormat(s, argc - 1, argv + 1);
+ case 13: { // atoi
+ Common::String string = s->_segMan->getString(argv[1]);
+ return make_reg(0, (uint16)atoi(string.c_str()));
+ }
+ default:
+ error("Unknown kString subop %d", argv[0].toUint16());
+ }
+
+ return NULL_REG;
+}
+
+#endif
+
} // End of namespace Sci
diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp
new file mode 100644
index 0000000000..cd103dade7
--- /dev/null
+++ b/engines/sci/engine/kvideo.cpp
@@ -0,0 +1,300 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "engines/util.h"
+#include "sci/engine/state.h"
+#include "sci/graphics/helpers.h"
+#include "sci/graphics/cursor.h"
+#include "sci/graphics/palette.h"
+#include "sci/graphics/screen.h"
+#include "graphics/cursorman.h"
+#include "graphics/video/avi_decoder.h"
+#include "graphics/video/qt_decoder.h"
+#include "sci/video/seq_decoder.h"
+#ifdef ENABLE_SCI32
+#include "sci/video/vmd_decoder.h"
+#endif
+
+namespace Sci {
+
+void playVideo(Graphics::VideoDecoder *videoDecoder) {
+ if (!videoDecoder)
+ return;
+
+ byte *scaleBuffer = 0;
+ uint16 width = videoDecoder->getWidth();
+ uint16 height = videoDecoder->getHeight();
+ uint16 screenWidth = g_system->getWidth();
+ uint16 screenHeight = g_system->getHeight();
+
+ if (screenWidth == 640 && width <= 320 && height <= 240) {
+ assert(videoDecoder->getPixelFormat().bytesPerPixel == 1);
+ width *= 2;
+ height *= 2;
+ scaleBuffer = new byte[width * height];
+ }
+
+ uint16 x = (screenWidth - width) / 2;
+ uint16 y = (screenHeight - height) / 2;
+ bool skipVideo = false;
+
+ while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) {
+ if (videoDecoder->needsUpdate()) {
+ Graphics::Surface *frame = videoDecoder->decodeNextFrame();
+ if (frame) {
+ if (scaleBuffer) {
+ // TODO: Probably should do aspect ratio correction in e.g. GK1 Windows
+ g_sci->_gfxScreen->scale2x((byte *)frame->pixels, scaleBuffer, videoDecoder->getWidth(), videoDecoder->getHeight());
+ g_system->copyRectToScreen(scaleBuffer, width, x, y, width, height);
+ } else
+ g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, width, height);
+
+ if (videoDecoder->hasDirtyPalette())
+ videoDecoder->setSystemPalette();
+
+ g_system->updateScreen();
+ }
+ }
+
+ Common::Event event;
+ while (g_system->getEventManager()->pollEvent(event)) {
+ if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP)
+ skipVideo = true;
+ }
+
+ g_system->delayMillis(10);
+ }
+
+ delete[] scaleBuffer;
+ delete videoDecoder;
+}
+
+reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {
+ // Hide the cursor if it's showing and then show it again if it was
+ // previously visible.
+ bool reshowCursor = g_sci->_gfxCursor->isVisible();
+ if (reshowCursor)
+ g_sci->_gfxCursor->kernelHide();
+
+ uint16 screenWidth = g_system->getWidth();
+ uint16 screenHeight = g_system->getHeight();
+
+ Graphics::VideoDecoder *videoDecoder = 0;
+
+ if (argv[0].segment != 0) {
+ Common::String filename = s->_segMan->getString(argv[0]);
+
+ if (g_sci->getPlatform() == Common::kPlatformMacintosh) {
+ // Mac QuickTime
+ // The only argument is the string for the video
+
+ // HACK: Switch to 16bpp graphics for Cinepak.
+ initGraphics(screenWidth, screenHeight, screenWidth > 320, NULL);
+
+ if (g_system->getScreenFormat().bytesPerPixel == 1) {
+ error("This video requires >8bpp color to be displayed, but could not switch to RGB color mode.");
+ return NULL_REG;
+ }
+
+ videoDecoder = new Graphics::QuickTimeDecoder();
+ if (!videoDecoder->loadFile(filename))
+ error("Could not open '%s'", filename.c_str());
+ } else {
+ // DOS SEQ
+ // SEQ's are called with no subops, just the string and delay
+ SeqDecoder *seqDecoder = new SeqDecoder();
+ seqDecoder->setFrameDelay(argv[1].toUint16()); // Time between frames in ticks
+ videoDecoder = seqDecoder;
+
+ if (!videoDecoder->loadFile(filename)) {
+ warning("Failed to open movie file %s", filename.c_str());
+ delete videoDecoder;
+ videoDecoder = 0;
+ }
+ }
+ } else {
+ // Windows AVI
+ // TODO: This appears to be some sort of subop. case 0 contains the string
+ // for the video, so we'll just play it from there for now.
+
+#ifdef ENABLE_SCI32
+ if (getSciVersion() >= SCI_VERSION_2_1) {
+ // SCI2.1 always has argv[0] as 1, the rest of the arguments seem to
+ // follow SCI1.1/2.
+ if (argv[0].toUint16() != 1)
+ error("SCI2.1 kShowMovie argv[0] not 1");
+ argv++;
+ argc--;
+ }
+#endif
+ switch (argv[0].toUint16()) {
+ case 0: {
+ Common::String filename = s->_segMan->getString(argv[1]);
+ videoDecoder = new Graphics::AviDecoder(g_system->getMixer());
+
+ if (!videoDecoder->loadFile(filename.c_str())) {
+ warning("Failed to open movie file %s", filename.c_str());
+ delete videoDecoder;
+ videoDecoder = 0;
+ }
+ break;
+ }
+ default:
+ warning("Unhandled SCI kShowMovie subop %d", argv[1].toUint16());
+ }
+ }
+
+ if (videoDecoder) {
+ playVideo(videoDecoder);
+
+ // HACK: Switch back to 8bpp if we played a QuickTime video.
+ // We also won't be copying the screen to the SCI screen...
+ if (g_system->getScreenFormat().bytesPerPixel != 1)
+ initGraphics(screenWidth, screenHeight, screenWidth > 320);
+ else {
+ g_sci->_gfxScreen->kernelSyncWithFramebuffer();
+ g_sci->_gfxPalette->kernelSyncScreenPalette();
+ }
+ }
+
+ if (reshowCursor)
+ g_sci->_gfxCursor->kernelShow();
+
+ return s->r_acc;
+}
+
+#ifdef ENABLE_SCI32
+
+reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv) {
+ uint16 operation = argv[0].toUint16();
+ Graphics::VideoDecoder *videoDecoder = 0;
+ bool reshowCursor = g_sci->_gfxCursor->isVisible();
+ Common::String fileName, warningMsg;
+
+ switch (operation) {
+ case 0: // init
+ // This is actually meant to init the video file, but we play it instead
+ fileName = s->_segMan->derefString(argv[1]);
+ // TODO: argv[2] (usually null). When it exists, it points to an "Event" object,
+ // that holds no data initially (e.g. in the intro of Phantasmagoria 1 demo).
+ // Perhaps it's meant for syncing
+ if (argv[2] != NULL_REG)
+ warning("kPlayVMD: third parameter isn't 0 (it's %04x:%04x - %s)", PRINT_REG(argv[2]), s->_segMan->getObjectName(argv[2]));
+
+ videoDecoder = new VMDDecoder(g_system->getMixer());
+
+ if (reshowCursor)
+ g_sci->_gfxCursor->kernelHide();
+
+ if (videoDecoder && videoDecoder->loadFile(fileName))
+ playVideo(videoDecoder);
+
+ if (reshowCursor)
+ g_sci->_gfxCursor->kernelShow();
+ break;
+ case 1:
+ {
+ // Set VMD parameters. Called with a maximum of 6 parameters:
+ //
+ // x, y, flags, gammaBoost, gammaFirst, gammaLast
+ //
+ // Flags are as follows:
+ // bit 0 doubled
+ // bit 1 "drop frames"?
+ // bit 2 insert black lines
+ // bit 3 unknown
+ // bit 4 gamma correction
+ // bit 5 hold black frame
+ // bit 6 hold last frame
+ // bit 7 unknown
+ // bit 8 stretch
+
+ // gammaBoost boosts palette colors in the range gammaFirst to
+ // gammaLast, but only if bit 4 in flags is set. Percent value such that
+ // 0% = no amplification These three parameters are optional if bit 4 is
+ // clear. Also note that the x, y parameters play subtle games if used
+ // with subfx 21. The subtleness has to do with creation of temporary
+ // planes and positioning relative to such planes.
+
+ int flags = argv[3].offset;
+ Common::String flagspec;
+
+ if (argc > 3) {
+ if (flags & 1)
+ flagspec += "doubled ";
+ if (flags & 2)
+ flagspec += "dropframes ";
+ if (flags & 4)
+ flagspec += "blacklines ";
+ if (flags & 8)
+ flagspec += "bit3 ";
+ if (flags & 16)
+ flagspec += "gammaboost ";
+ if (flags & 32)
+ flagspec += "holdblack ";
+ if (flags & 64)
+ flagspec += "holdlast ";
+ if (flags & 128)
+ flagspec += "bit7 ";
+ if (flags & 256)
+ flagspec += "stretch";
+
+ warning("VMDFlags: %s", flagspec.c_str());
+ }
+
+ warning("x, y: %d, %d", argv[1].offset, argv[2].offset);
+
+ if (argc > 4 && flags & 16)
+ warning("gammaBoost: %d%% between palette entries %d and %d", argv[4].offset, argv[5].offset, argv[6].offset);
+ break;
+ }
+ case 6:
+ // Play, perhaps? Or stop? This is the last call made, and takes no extra parameters
+ case 14:
+ // Takes an additional integer parameter (e.g. 3)
+ case 16:
+ // Takes an additional parameter, usually 0
+ case 21:
+ // Looks to be setting the video size and position. Called with 4 extra integer
+ // parameters (e.g. 86, 41, 235, 106)
+ default:
+ warningMsg = "PlayVMD - unsupported subop. Params: " +
+ Common::String::printf("%d", argc) + " (";
+
+ for (int i = 0; i < argc; i++) {
+ warningMsg += Common::String::printf("%04x:%04x", PRINT_REG(argv[i]));
+ warningMsg += (i == argc - 1 ? ")" : ", ");
+ }
+
+ warning("%s", warningMsg.c_str());
+ break;
+ }
+
+ return s->r_acc;
+}
+
+#endif
+
+} // End of namespace Sci
diff --git a/engines/sci/engine/message.cpp b/engines/sci/engine/message.cpp
index 18e60eb521..cdecc556e8 100644
--- a/engines/sci/engine/message.cpp
+++ b/engines/sci/engine/message.cpp
@@ -380,7 +380,14 @@ void MessageState::outputString(reg_t buf, const Common::String &str) {
if ((unsigned)buffer_r.maxSize >= str.size() + 1) {
_segMan->strcpy(buf, str.c_str());
} else {
- warning("Message: buffer %04x:%04x invalid or too small to hold the following text of %i bytes: '%s'", PRINT_REG(buf), str.size() + 1, str.c_str());
+ // LSL6 sets an exit text here, but the buffer size allocated
+ // is too small. Don't display a warning in this case, as we
+ // don't use the exit text anyway - bug report #3035533
+ if (g_sci->getGameId() == GID_LSL6 && str.hasPrefix("\r\n(c) 1993 Sierra On-Line, Inc")) {
+ // LSL6 buggy exit text, don't show warning
+ } else {
+ warning("Message: buffer %04x:%04x invalid or too small to hold the following text of %i bytes: '%s'", PRINT_REG(buf), str.size() + 1, str.c_str());
+ }
// Set buffer to empty string if possible
if (buffer_r.maxSize > 0)
diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp
index 0fe5f2088a..806c8893b4 100644
--- a/engines/sci/engine/savegame.cpp
+++ b/engines/sci/engine/savegame.cpp
@@ -40,6 +40,7 @@
#include "sci/engine/selector.h"
#include "sci/engine/vm_types.h"
#include "sci/engine/script.h" // for SCI_OBJ_EXPORTS and SCI_OBJ_SYNONYMS
+#include "sci/graphics/palette.h"
#include "sci/graphics/ports.h"
#include "sci/sound/audio.h"
#include "sci/sound/music.h"
@@ -64,47 +65,20 @@ const uint32 INTMAPPER_MAGIC_KEY = 0xDEADBEEF;
#define DEFROBNICATE_HANDLE(handle) (make_reg((handle >> 16) & 0xffff, handle & 0xffff))
void MusicEntry::saveLoadWithSerializer(Common::Serializer &s) {
- if (s.getVersion() < 14) {
- // Old sound system data. This data is only loaded, never saved (as we're never
- // saving in the older version format)
- uint32 handle = 0;
- s.syncAsSint32LE(handle);
- soundObj = DEFROBNICATE_HANDLE(handle);
- s.syncAsSint32LE(resourceId);
- s.syncAsSint32LE(priority);
- s.syncAsSint32LE(status);
- s.skip(4); // restoreBehavior
- uint32 restoreTime = 0;
- s.syncAsSint32LE(restoreTime);
- ticker = restoreTime * 60 / 1000;
- s.syncAsSint32LE(loop);
- s.syncAsSint32LE(hold);
- // volume and dataInc will be synced from the sound objects
- // when the sound list is reconstructed in gamestate_restore()
- volume = MUSIC_VOLUME_MAX;
- dataInc = 0;
- // No fading info
- fadeTo = 0;
- fadeStep = 0;
- fadeTicker = 0;
- fadeTickerStep = 0;
- } else {
- // A bit more optimized saving
- soundObj.saveLoadWithSerializer(s);
- s.syncAsSint16LE(resourceId);
- s.syncAsSint16LE(dataInc);
- s.syncAsSint16LE(ticker);
- s.syncAsSint16LE(signal, VER(17));
- s.syncAsByte(priority);
- s.syncAsSint16LE(loop, VER(17));
- s.syncAsByte(volume);
- s.syncAsByte(hold, VER(17));
- s.syncAsByte(fadeTo);
- s.syncAsSint16LE(fadeStep);
- s.syncAsSint32LE(fadeTicker);
- s.syncAsSint32LE(fadeTickerStep);
- s.syncAsByte(status);
- }
+ soundObj.saveLoadWithSerializer(s);
+ s.syncAsSint16LE(resourceId);
+ s.syncAsSint16LE(dataInc);
+ s.syncAsSint16LE(ticker);
+ s.syncAsSint16LE(signal, VER(17));
+ s.syncAsByte(priority);
+ s.syncAsSint16LE(loop, VER(17));
+ s.syncAsByte(volume);
+ s.syncAsByte(hold, VER(17));
+ s.syncAsByte(fadeTo);
+ s.syncAsSint16LE(fadeStep);
+ s.syncAsSint32LE(fadeTicker);
+ s.syncAsSint32LE(fadeTickerStep);
+ s.syncAsByte(status);
// pMidiParser and pStreamAud will be initialized when the
// sound list is reconstructed in gamestate_restore()
@@ -181,7 +155,7 @@ void SegManager::saveLoadWithSerializer(Common::Serializer &s) {
if (s.isLoading())
resetSegMan();
- s.skip(4, VER(12), VER(18)); // OBSOLETE: Used to be _exportsAreWide
+ s.skip(4, VER(14), VER(18)); // OBSOLETE: Used to be _exportsAreWide
if (s.isLoading()) {
// Reset _scriptSegMap, to be restored below
@@ -256,40 +230,9 @@ static void sync_SavegameMetadata(Common::Serializer &s, SavegameMetadata &obj)
void EngineState::saveLoadWithSerializer(Common::Serializer &s) {
Common::String tmp;
- s.syncString(tmp, VER(12), VER(23)); // OBSOLETE: Used to be game_version
-
- // OBSOLETE: Saved menus. Skip all of the saved data
- if (s.getVersion() < 14) {
- int totalMenus = 0;
- s.syncAsUint32LE(totalMenus);
-
- // Now iterate through the obsolete saved menu data
- for (int i = 0; i < totalMenus; i++) {
- s.syncString(tmp); // OBSOLETE: Used to be _title
- s.skip(4, VER(12), VER(12)); // OBSOLETE: Used to be _titleWidth
- s.skip(4, VER(12), VER(12)); // OBSOLETE: Used to be _width
-
- int menuLength = 0;
- s.syncAsUint32LE(menuLength);
-
- for (int j = 0; j < menuLength; j++) {
- s.skip(4, VER(12), VER(12)); // OBSOLETE: Used to be _type
- s.syncString(tmp); // OBSOLETE: Used to be _keytext
-
- s.skip(4, VER(12), VER(12)); // OBSOLETE: Used to be _flags
- s.skip(64, VER(12), VER(12)); // OBSOLETE: Used to be MENU_SAID_SPEC_SIZE
- s.skip(4, VER(12), VER(12)); // OBSOLETE: Used to be _saidPos
- s.syncString(tmp); // OBSOLETE: Used to be _text
- s.skip(4, VER(12), VER(12)); // OBSOLETE: Used to be _textPos
- s.skip(4 * 4, VER(12), VER(12)); // OBSOLETE: Used to be _modifiers, _key, _enabled and _tag
- }
- }
- }
+ s.syncString(tmp, VER(14), VER(23)); // OBSOLETE: Used to be game_version
- s.skip(4, VER(12), VER(12)); // obsolete: used to be status_bar_foreground
- s.skip(4, VER(12), VER(12)); // obsolete: used to be status_bar_background
-
- if (s.getVersion() >= 13 && getSciVersion() <= SCI_VERSION_1_1) {
+ if (getSciVersion() <= SCI_VERSION_1_1) {
// Save/Load picPort as well for SCI0-SCI1.1. Necessary for Castle of Dr. Brain,
// as the picPort has been changed when loading during the intro
int16 picPortTop, picPortLeft;
@@ -312,6 +255,7 @@ void EngineState::saveLoadWithSerializer(Common::Serializer &s) {
_segMan->saveLoadWithSerializer(s);
g_sci->_soundCmd->syncPlayList(s);
+ g_sci->_gfxPalette->saveLoadWithSerializer(s);
}
void LocalVariables::saveLoadWithSerializer(Common::Serializer &s) {
@@ -323,7 +267,6 @@ void LocalVariables::saveLoadWithSerializer(Common::Serializer &s) {
void Object::saveLoadWithSerializer(Common::Serializer &s) {
s.syncAsSint32LE(_flags);
_pos.saveLoadWithSerializer(s);
- s.skip(4, VER(12), VER(12)); // OBSOLETE: Used to be variable_names_nr
s.syncAsSint32LE(_methodCount); // that's actually a uint16
syncArray<reg_t>(s, _variables);
@@ -449,12 +392,12 @@ void Script::saveLoadWithSerializer(Common::Serializer &s) {
if (s.isLoading())
init(_nr, g_sci->getResMan());
- s.skip(4, VER(12), VER(22)); // OBSOLETE: Used to be _bufSize
- s.skip(4, VER(12), VER(22)); // OBSOLETE: Used to be _scriptSize
- s.skip(4, VER(12), VER(22)); // OBSOLETE: Used to be _heapSize
+ s.skip(4, VER(14), VER(22)); // OBSOLETE: Used to be _bufSize
+ s.skip(4, VER(14), VER(22)); // OBSOLETE: Used to be _scriptSize
+ s.skip(4, VER(14), VER(22)); // OBSOLETE: Used to be _heapSize
- s.skip(4, VER(12), VER(19)); // OBSOLETE: Used to be _numExports
- s.skip(4, VER(12), VER(19)); // OBSOLETE: Used to be _numSynonyms
+ s.skip(4, VER(14), VER(19)); // OBSOLETE: Used to be _numExports
+ s.skip(4, VER(14), VER(19)); // OBSOLETE: Used to be _numSynonyms
s.syncAsSint32LE(_lockers);
// Sync _objects. This is a hashmap, and we use the following on disk format:
@@ -482,7 +425,7 @@ void Script::saveLoadWithSerializer(Common::Serializer &s) {
}
}
- s.skip(4, VER(12), VER(20)); // OBSOLETE: Used to be _localsOffset
+ s.skip(4, VER(14), VER(20)); // OBSOLETE: Used to be _localsOffset
s.syncAsSint32LE(_localsSegment);
s.syncAsSint32LE(_markedAsDeleted);
@@ -599,14 +542,6 @@ void SoundCommandParser::reconstructPlayList(int savegame_version) {
(*i)->soundRes = 0;
}
if ((*i)->status == kSoundPlaying) {
- if (savegame_version < 14) {
- (*i)->dataInc = readSelectorValue(_segMan, (*i)->soundObj, SELECTOR(dataInc));
- (*i)->signal = readSelectorValue(_segMan, (*i)->soundObj, SELECTOR(signal));
-
- if (_soundVersion >= SCI_VERSION_1_LATE)
- (*i)->volume = readSelectorValue(_segMan, (*i)->soundObj, SELECTOR(vol));
- }
-
processPlaySound((*i)->soundObj);
}
}
@@ -628,6 +563,42 @@ void StringTable::saveLoadWithSerializer(Common::Serializer &ser) {
}
#endif
+void GfxPalette::palVarySaveLoadPalette(Common::Serializer &s, Palette *palette) {
+ s.syncBytes(palette->mapping, 256);
+ s.syncAsUint32LE(palette->timestamp);
+ for (int i = 0; i < 256; 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);
+ }
+ s.syncBytes(palette->intensity, 256);
+}
+
+void GfxPalette::saveLoadWithSerializer(Common::Serializer &s) {
+ if (s.getVersion() < 24)
+ return;
+
+ if (s.isLoading() && _palVaryResourceId != -1)
+ palVaryRemoveTimer();
+
+ s.syncAsSint32LE(_palVaryResourceId);
+ if (_palVaryResourceId != -1) {
+ palVarySaveLoadPalette(s, &_palVaryOriginPalette);
+ palVarySaveLoadPalette(s, &_palVaryTargetPalette);
+ s.syncAsSint16LE(_palVaryStep);
+ s.syncAsSint16LE(_palVaryStepStop);
+ s.syncAsSint16LE(_palVaryDirection);
+ s.syncAsUint16LE(_palVaryTicks);
+ s.syncAsSint32LE(_palVaryPaused);
+ }
+
+ if (s.isLoading() && _palVaryResourceId != -1) {
+ _palVarySignal = 0;
+ palVaryInstallTimer();
+ }
+}
+
void SegManager::reconstructStack(EngineState *s) {
DataStack *stack = (DataStack *)(_heap[findSegmentByType(SEG_TYPE_STACK)]);
s->stack_base = stack->_entries;
@@ -722,6 +693,7 @@ bool gamestate_save(EngineState *s, Common::WriteStream *fh, const char* savenam
meta.script0_size = script0->size;
meta.game_object_offset = g_sci->getGameObject().offset;
+ // Checking here again
if (s->executionStackBase) {
warning("Cannot save from below kernel function");
return false;
@@ -804,7 +776,6 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
s->_msgState = new MessageState(s->_segMan);
s->abortScriptProcessing = kAbortLoadGame;
- s->shrinkStackToBase();
}
bool get_savegame_metadata(Common::SeekableReadStream *stream, SavegameMetadata *meta) {
diff --git a/engines/sci/engine/savegame.h b/engines/sci/engine/savegame.h
index 9a882f2bf7..fc254ba33d 100644
--- a/engines/sci/engine/savegame.h
+++ b/engines/sci/engine/savegame.h
@@ -36,8 +36,8 @@ namespace Sci {
struct EngineState;
enum {
- CURRENT_SAVEGAME_VERSION = 23,
- MINIMUM_SAVEGAME_VERSION = 12
+ CURRENT_SAVEGAME_VERSION = 24,
+ MINIMUM_SAVEGAME_VERSION = 14
};
// Savegame metadata
diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp
index a293f81d2f..645094d9ec 100644
--- a/engines/sci/engine/script.cpp
+++ b/engines/sci/engine/script.cpp
@@ -434,13 +434,19 @@ void Script::initialiseClasses(SegManager *segMan) {
}
if (isClass) {
- // WORKAROUND for an invalid species access in the demo of LSL2
+ // WORKAROUNDs for off-by-one script errors
if (g_sci->getGameId() == GID_LSL2 && g_sci->isDemo() && species == (int)segMan->classTableSize())
segMan->resizeClassTable(segMan->classTableSize() + 1);
+ if (g_sci->getGameId() == GID_LSL3 && !g_sci->isDemo() && _nr == 500 && species == (int)segMan->classTableSize())
+ segMan->resizeClassTable(segMan->classTableSize() + 1);
+ if (g_sci->getGameId() == GID_SQ3 && !g_sci->isDemo() && _nr == 93 && species == (int)segMan->classTableSize())
+ segMan->resizeClassTable(segMan->classTableSize() + 1);
+ if (g_sci->getGameId() == GID_SQ3 && !g_sci->isDemo() && _nr == 99 && species == (int)segMan->classTableSize())
+ segMan->resizeClassTable(segMan->classTableSize() + 1);
if (species < 0 || species >= (int)segMan->classTableSize())
- error("Invalid species %d(0x%x) not in interval [0,%d) while instantiating script %d\n",
- species, species, segMan->classTableSize(), _nr);
+ error("Invalid species %d(0x%x) unknown max %d(0x%x) while instantiating script %d\n",
+ species, species, segMan->classTableSize(), segMan->classTableSize(), _nr);
SegmentId segmentId = segMan->getScriptSegment(_nr);
segMan->setClassOffset(species, make_reg(segmentId, classpos));
@@ -468,8 +474,10 @@ void Script::initialiseObjectsSci0(SegManager *segMan, SegmentId segmentId) {
obj->initSpecies(segMan, addr);
if (!obj->initBaseObject(segMan, addr)) {
- if (_nr == 202 && g_sci->getGameId() == GID_KQ5 && g_sci->getSciLanguage() == K_LANG_FRENCH) {
- // Script 202 of KQ5 French has an invalid object. This is non-fatal.
+ if (_nr == 202 && g_sci->getGameId() == GID_KQ5) {
+ // WORKAROUND: Script 202 of KQ5 French and German
+ // (perhaps Spanish too?) has an invalid object.
+ // This is non-fatal. Refer to bug #3035396.
} else {
error("Failed to locate base object for object at %04X:%04X; skipping", PRINT_REG(addr));
}
diff --git a/engines/sci/engine/scriptdebug.cpp b/engines/sci/engine/scriptdebug.cpp
index 915a6fa994..9c08526fbb 100644
--- a/engines/sci/engine/scriptdebug.cpp
+++ b/engines/sci/engine/scriptdebug.cpp
@@ -488,48 +488,7 @@ void Kernel::dissectScript(int scriptNumber, Vocabulary *vocab) {
Common::hexdump(script->data + seeker, objsize - 4, 16, seeker);
printf("%04x: ", seeker);
- while (seeker < _seeker) {
- unsigned char nextitem = script->data [seeker++];
- if (nextitem == 0xFF)
- printf("\n%04x: ", seeker);
- else if (nextitem >= 0xF0) {
- switch (nextitem) {
- case 0xf0:
- printf(", ");
- break;
- case 0xf1:
- printf("& ");
- break;
- case 0xf2:
- printf("/ ");
- break;
- case 0xf3:
- printf("( ");
- break;
- case 0xf4:
- printf(") ");
- break;
- case 0xf5:
- printf("[ ");
- break;
- case 0xf6:
- printf("] ");
- break;
- case 0xf7:
- printf("# ");
- break;
- case 0xf8:
- printf("< ");
- break;
- case 0xf9:
- printf("> ");
- break;
- }
- } else {
- nextitem = nextitem << 8 | script->data [seeker++];
- printf("%s[%03x] ", vocab->getAnyWordFromGroup(nextitem), nextitem);
- }
- }
+ vocab->debugDecipherSaidBlock(script->data + seeker);
printf("\n");
break;
diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp
index ef2279e492..25cf1d069f 100644
--- a/engines/sci/engine/seg_manager.cpp
+++ b/engines/sci/engine/seg_manager.cpp
@@ -508,7 +508,7 @@ List *SegManager::lookupList(reg_t addr) {
return &(lt->_table[addr.offset]);
}
-Node *SegManager::lookupNode(reg_t addr) {
+Node *SegManager::lookupNode(reg_t addr, bool stopOnDiscarded) {
if (addr.isNull())
return NULL; // Non-error null
@@ -522,6 +522,9 @@ Node *SegManager::lookupNode(reg_t addr) {
NodeTable *nt = (NodeTable *)_heap[addr.segment];
if (!nt->isValidEntry(addr.offset)) {
+ if (!stopOnDiscarded)
+ return NULL;
+
error("Attempt to use invalid or discarded reference %04x:%04x as list node", PRINT_REG(addr));
return NULL;
}
diff --git a/engines/sci/engine/seg_manager.h b/engines/sci/engine/seg_manager.h
index a7f5f8517f..e0808dbb1b 100644
--- a/engines/sci/engine/seg_manager.h
+++ b/engines/sci/engine/seg_manager.h
@@ -232,7 +232,7 @@ public:
* @param addr The address to resolve
* @return The list node referenced, or NULL on error
*/
- Node *lookupNode(reg_t addr);
+ Node *lookupNode(reg_t addr, bool stopOnDiscarded = true);
// 8. Hunk Memory
diff --git a/engines/sci/engine/selector.cpp b/engines/sci/engine/selector.cpp
index 00480743cc..f5eb9eb73a 100644
--- a/engines/sci/engine/selector.cpp
+++ b/engines/sci/engine/selector.cpp
@@ -104,6 +104,8 @@ void Kernel::mapSelectors() {
FIND_SELECTOR2(b_incr, "b-incr");
FIND_SELECTOR(xStep);
FIND_SELECTOR(yStep);
+ FIND_SELECTOR(xLast);
+ FIND_SELECTOR(yLast);
FIND_SELECTOR(moveSpeed);
FIND_SELECTOR(canBeHere); // cantBeHere
FIND_SELECTOR(heading);
@@ -176,6 +178,13 @@ void Kernel::mapSelectors() {
FIND_SELECTOR(dimmed);
FIND_SELECTOR(fore);
FIND_SELECTOR(back);
+ FIND_SELECTOR(fixPriority);
+ FIND_SELECTOR(mirrored);
+ FIND_SELECTOR(useInsetRect);
+ FIND_SELECTOR(inTop);
+ FIND_SELECTOR(inLeft);
+ FIND_SELECTOR(inBottom);
+ FIND_SELECTOR(inRight);
#endif
}
@@ -236,7 +245,7 @@ void invokeSelector(EngineState *s, reg_t object, int selectorId,
xstack->sp += argc + 2;
xstack->fp += argc + 2;
- run_vm(s, false); // Start a new vm
+ run_vm(s); // Start a new vm
}
SelectorType lookupSelector(SegManager *segMan, reg_t obj_location, Selector selectorId, ObjVarRef *varp, reg_t *fptr) {
diff --git a/engines/sci/engine/selector.h b/engines/sci/engine/selector.h
index acb7912d8d..661290f58c 100644
--- a/engines/sci/engine/selector.h
+++ b/engines/sci/engine/selector.h
@@ -69,6 +69,7 @@ struct SelectorCache {
Selector dx, dy; ///< Deltas
Selector b_movCnt, b_i1, b_i2, b_di, b_xAxis, b_incr; ///< Various Bresenham vars
Selector xStep, yStep; ///< BR adjustments
+ Selector xLast, yLast; ///< BR last position of client
Selector moveSpeed; ///< Used for DoBresen
Selector canBeHere; ///< Funcselector: Checks for movement validity in SCI0
Selector heading, mover; ///< Used in DoAvoider
@@ -141,6 +142,12 @@ struct SelectorCache {
Selector fore;
Selector back;
Selector dimmed;
+
+ Selector fixPriority;
+ Selector mirrored;
+
+ Selector useInsetRect;
+ Selector inTop, inLeft, inBottom, inRight;
#endif
};
diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp
index 36b03c0ad9..a069344d61 100644
--- a/engines/sci/engine/state.cpp
+++ b/engines/sci/engine/state.cpp
@@ -324,4 +324,17 @@ Common::String SciEngine::strSplit(const char *str, const char *sep) {
return retval;
}
+void SciEngine::checkVocabularySwitch() {
+ uint16 parserLanguage = 1;
+ if (SELECTOR(parseLang) != -1)
+ parserLanguage = readSelectorValue(_gamestate->_segMan, _gameObj, SELECTOR(parseLang));
+
+ if (parserLanguage != _vocabularyLanguage) {
+ delete _vocabulary;
+ _vocabulary = new Vocabulary(_resMan, parserLanguage > 1 ? true : false);
+ _vocabulary->reset();
+ _vocabularyLanguage = parserLanguage;
+ }
+}
+
} // End of namespace Sci
diff --git a/engines/sci/engine/static_selectors.cpp b/engines/sci/engine/static_selectors.cpp
index 85089e74c8..55e18613e0 100644
--- a/engines/sci/engine/static_selectors.cpp
+++ b/engines/sci/engine/static_selectors.cpp
@@ -168,6 +168,20 @@ Common::StringArray Kernel::checkStaticSelectorNames() {
names[274] = "syncTime";
names[275] = "syncCue";
+ } else if (g_sci->getGameId() == GID_ISLANDBRAIN) {
+ // The demo of Island of Dr. Brain needs the init selector set to match up with the full
+ // game's workaround - bug #3035033
+ if (names.size() < 111)
+ names.resize(111);
+
+ names[110] = "init";
+ } else if (g_sci->getGameId() == GID_LAURABOW2) {
+ // The floppy of version needs the open selector set to match up with the CD version's
+ // workaround - bug #3035694
+ if (names.size() < 190)
+ names.resize(190);
+
+ names[189] = "open";
}
#ifdef ENABLE_SCI32
diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp
index 8108440102..b7f6896a48 100644
--- a/engines/sci/engine/vm.cpp
+++ b/engines/sci/engine/vm.cpp
@@ -38,6 +38,7 @@
#include "sci/engine/seg_manager.h"
#include "sci/engine/selector.h" // for SELECTOR
#include "sci/engine/gc.h"
+#include "sci/engine/workarounds.h"
namespace Sci {
@@ -48,86 +49,6 @@ const reg_t TRUE_REG = {0, 1};
#define SCI_XS_CALLEE_LOCALS ((SegmentId)-1)
-#define END Script_None
-
-opcode_format g_opcode_formats[128][4] = {
- /*00*/
- {Script_None}, {Script_None}, {Script_None}, {Script_None},
- /*04*/
- {Script_None}, {Script_None}, {Script_None}, {Script_None},
- /*08*/
- {Script_None}, {Script_None}, {Script_None}, {Script_None},
- /*0C*/
- {Script_None}, {Script_None}, {Script_None}, {Script_None},
- /*10*/
- {Script_None}, {Script_None}, {Script_None}, {Script_None},
- /*14*/
- {Script_None}, {Script_None}, {Script_None}, {Script_SRelative, END},
- /*18*/
- {Script_SRelative, END}, {Script_SRelative, END}, {Script_SVariable, END}, {Script_None},
- /*1C*/
- {Script_SVariable, END}, {Script_None}, {Script_None}, {Script_Variable, END},
- /*20*/
- {Script_SRelative, Script_Byte, END}, {Script_Variable, Script_Byte, END}, {Script_Variable, Script_Byte, END}, {Script_Variable, Script_SVariable, Script_Byte, END},
- /*24 (24=ret)*/
- {Script_End}, {Script_Byte, END}, {Script_Invalid}, {Script_Invalid},
- /*28*/
- {Script_Variable, END}, {Script_Invalid}, {Script_Byte, END}, {Script_Variable, Script_Byte, END},
- /*2C*/
- {Script_SVariable, END}, {Script_SVariable, Script_Variable, END}, {Script_None}, {Script_Invalid},
- /*30*/
- {Script_None}, {Script_Property, END}, {Script_Property, END}, {Script_Property, END},
- /*34*/
- {Script_Property, END}, {Script_Property, END}, {Script_Property, END}, {Script_Property, END},
- /*38*/
- {Script_Property, END}, {Script_SRelative, END}, {Script_SRelative, END}, {Script_None},
- /*3C*/
- {Script_None}, {Script_None}, {Script_None}, {Script_Word},
- /*40-4F*/
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- /*50-5F*/
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- /*60-6F*/
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- /*70-7F*/
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END},
- {Script_Global, END}, {Script_Local, END}, {Script_Temp, END}, {Script_Param, END}
-};
-#undef END
-
-// TODO: script_adjust_opcode_formats should probably be part of the
-// constructor (?) of a VirtualMachine or a ScriptManager class.
-void script_adjust_opcode_formats() {
- if (g_sci->_features->detectLofsType() != SCI_VERSION_0_EARLY) {
- g_opcode_formats[op_lofsa][0] = Script_Offset;
- g_opcode_formats[op_lofss][0] = Script_Offset;
- }
-
-#ifdef ENABLE_SCI32
- // In SCI32, some arguments are now words instead of bytes
- if (getSciVersion() >= SCI_VERSION_2) {
- g_opcode_formats[op_calle][2] = Script_Word;
- g_opcode_formats[op_callk][1] = Script_Word;
- g_opcode_formats[op_super][1] = Script_Word;
- g_opcode_formats[op_send][0] = Script_Word;
- g_opcode_formats[op_self][0] = Script_Word;
- g_opcode_formats[op_call][1] = Script_Word;
- g_opcode_formats[op_callb][1] = Script_Word;
- }
-#endif
-}
-
/**
* Adds an entry to the top of the execution stack.
*
@@ -248,7 +169,7 @@ static bool validate_variable(reg_t *r, reg_t *stack_base, int type, int max, in
} else {
// WORKAROUND: Mixed-Up Mother Goose tries to use an invalid parameter in Event::new().
// Just skip around it here so we don't error out in validate_arithmetic.
- if (g_sci->getGameId() == GID_MOTHERGOOSE && getSciVersion() <= SCI_VERSION_1_1 && type == VAR_PARAM && index == 1)
+ if (g_sci->getGameId() == GID_MOTHERGOOSE && type == VAR_PARAM && index == 1)
return false;
debugC(2, kDebugLevelVM, "%s", txt.c_str());
@@ -262,109 +183,33 @@ static bool validate_variable(reg_t *r, reg_t *stack_base, int type, int max, in
return true;
}
-struct SciTrackOriginReply {
- int scriptNr;
- Common::String objectName;
- Common::String methodName;
- int localCallOffset;
-};
-
-static reg_t trackOriginAndFindWorkaround(int index, const SciWorkaroundEntry *workaroundList, SciTrackOriginReply *trackOrigin) {
- EngineState *state = g_sci->getEngineState();
- ExecStack *lastCall = state->xs;
- Script *local_script = state->_segMan->getScriptIfLoaded(lastCall->local_segment);
- int curScriptNr = local_script->getScriptNumber();
-
- if (lastCall->debugLocalCallOffset != -1) {
- // if lastcall was actually a local call search back for a real call
- Common::List<ExecStack>::iterator callIterator = state->_executionStack.end();
- while (callIterator != state->_executionStack.begin()) {
- callIterator--;
- ExecStack loopCall = *callIterator;
- if ((loopCall.debugSelector != -1) || (loopCall.debugExportId != -1)) {
- lastCall->debugSelector = loopCall.debugSelector;
- lastCall->debugExportId = loopCall.debugExportId;
- break;
- }
- }
- }
-
- Common::String curObjectName = state->_segMan->getObjectName(lastCall->sendp);
- Common::String curMethodName;
- const SciGameId gameId = g_sci->getGameId();
-
- if (lastCall->type == EXEC_STACK_TYPE_CALL) {
- if (lastCall->debugSelector != -1) {
- curMethodName = g_sci->getKernel()->getSelectorName(lastCall->debugSelector);
- } else if (lastCall->debugExportId != -1) {
- curObjectName = "";
- curMethodName = curMethodName.printf("export %d", lastCall->debugExportId);
- }
- }
-
- if (workaroundList) {
- // Search if there is a workaround for this one
- const SciWorkaroundEntry *workaround;
- int16 inheritanceLevel = 0;
- Common::String searchObjectName = curObjectName;
- reg_t searchObject = lastCall->sendp;
- do {
- workaround = workaroundList;
- while (workaround->objectName) {
- if (workaround->gameId == gameId && workaround->scriptNr == curScriptNr
- && ((workaround->inheritanceLevel == -1) || (workaround->inheritanceLevel == inheritanceLevel))
- && (workaround->objectName == searchObjectName)
- && workaround->methodName == curMethodName && workaround->localCallOffset == lastCall->debugLocalCallOffset && workaround->index == index) {
- // Workaround found
- return workaround->newValue;
- }
- workaround++;
- }
-
- // Go back to the parent
- inheritanceLevel++;
- searchObject = state->_segMan->getObject(searchObject)->getSuperClassSelector();
- if (!searchObject.isNull())
- searchObjectName = state->_segMan->getObjectName(searchObject);
- } while (!searchObject.isNull()); // no parent left?
- }
+static bool validate_unsignedInteger(reg_t reg, uint16 &integer) {
+ if (reg.segment)
+ return false;
+ integer = reg.offset;
+ return true;
+}
- // give caller origin data
- trackOrigin->objectName = curObjectName;
- trackOrigin->methodName = curMethodName;
- trackOrigin->scriptNr = curScriptNr;
- trackOrigin->localCallOffset = lastCall->debugLocalCallOffset;
- return make_reg(0xFFFF, 0xFFFF);
+static bool validate_signedInteger(reg_t reg, int16 &integer) {
+ if (reg.segment)
+ return false;
+ integer = (int16)reg.offset;
+ return true;
}
-// gameID, scriptNr,lvl, object-name, method-name, call, index, replace
-static const SciWorkaroundEntry uninitializedReadWorkarounds[] = {
- { GID_LAURABOW2, 24, 0, "gcWin", "open", -1, 5, { 0, 0xf } }, // is used as priority for game menu
- { GID_FREDDYPHARKAS, 24, 0, "gcWin", "open", -1, 5, { 0, 0xf } }, // is used as priority for game menu
- { GID_FREDDYPHARKAS, 31, 0, "quitWin", "open", -1, 5, { 0, 0xf } }, // is used as priority for game menu
- { GID_GK2, 11, 0, "", "export 10", -1, 3, { 0, 0 } }, // called when the game starts
- { GID_JONES, 232, 0, "weekendText", "draw", 0x3d3, 0, { 0, 0 } }, // jones/cd only - gets called during the game
- { GID_JONES, 255, 0, "", "export 0", -1, 13, { 0, 0 } }, // jones/ega&vga only - called when the game starts
- { GID_JONES, 255, 0, "", "export 0", -1, 14, { 0, 0 } }, // jones/ega&vga only - called when the game starts
- { GID_LSL1, 720, 0, "rm720", "init", -1, 0, { 0, 0 } }, // age check room
- { GID_LSL3, 997, 0, "TheMenuBar", "handleEvent", -1, 1, { 0, 0xf } }, // when setting volume the first time, this temp is used to set volume on entry (normally it would have been initialized to 's')
- { GID_LSL6, 928, -1, "Narrator", "startText", -1, 0, { 0, 0 } }, // used by various objects that are even translated in foreign versions, that's why we use the base-class
- { GID_ISLANDBRAIN, 140, 0, "piece", "init", -1, 3, { 0, 1 } }, // first puzzle right at the start, some initialization variable. bnt is done on it, and it should be non-0
- { GID_ISLANDBRAIN, 268, 0, "anElement", "select", -1, 0, { 0, 0 } }, // elements puzzle, gets used before super TextIcon
- { GID_KQ5, 0, 0, "", "export 29", -1, 3, { 0, 0 } }, // called when playing harp for the harpies, is used for kDoAudio
- { GID_KQ5, 25, 0, "rm025", "doit", -1, 0, { 0, 0 } }, // inside witch forest, where the walking rock is
- { GID_KQ6, 903, 0, "controlWin", "open", -1, 4, { 0, 0 } }, // when opening the controls window (save, load etc)
- { GID_KQ6, 500, 0, "rm500", "init", -1, 0, { 0, 0 } }, // going to island of the beast
- { GID_KQ6, 520, 0, "rm520", "init", -1, 0, { 0, 0 } }, // going to boiling water trap on beast isle
- { GID_KQ6, 30, 0, "rats", "changeState", -1, 0, { 0, 0 } }, // rats in the catacombs
- { GID_LSL6, 85, 0, "washcloth", "doVerb", -1, 0, { 0, 0 } }, // washcloth in inventory
- { GID_SQ1, 703, 0, "", "export 1", -1, 0, { 0, 0 } }, // sub that's called from several objects while on sarien battle cruiser
- { GID_SQ1, 703, 0, "firePulsar", "changeState", 0x18a, 0, { 0, 0 } }, // export 1, but called locally (when shooting at aliens)
- { GID_SQ4, 928, 0, "Narrator", "startText", -1, 1000, { 0, 1 } }, // sq4cd: method returns this to the caller
- { GID_SQ6, 0, 0, "SQ6", "init", -1, 2, { 0, 0 } }, // called when the game starts
- { GID_SQ6, 64950, 0, "View", "handleEvent", -1, 0, { 0, 0 } }, // called when pressing "Start game" in the main menu
- SCI_WORKAROUNDENTRY_TERMINATOR
-};
+extern const char *opcodeNames[]; // from scriptdebug.cpp
+
+static reg_t arithmetic_lookForWorkaround(const byte opcode, const SciWorkaroundEntry *workaroundList, reg_t value1, reg_t value2) {
+ SciTrackOriginReply originReply;
+ SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, workaroundList, &originReply);
+ if (solution.type == WORKAROUND_NONE)
+ error("%s on non-integer (%04x:%04x, %04x:%04x) from method %s::%s (script %d, room %d, localCall %x)",
+ opcodeNames[opcode], PRINT_REG(value1), PRINT_REG(value2), originReply.objectName.c_str(),
+ originReply.methodName.c_str(), originReply.scriptNr, g_sci->getEngineState()->currentRoomNumber(),
+ originReply.localCallOffset);
+ assert(solution.type == WORKAROUND_FAKE);
+ return make_reg(0, solution.value);
+}
static reg_t validate_read_var(reg_t *r, reg_t *stack_base, int type, int max, int index, reg_t default_value) {
if (validate_variable(r, stack_base, type, max, index)) {
@@ -374,9 +219,13 @@ static reg_t validate_read_var(reg_t *r, reg_t *stack_base, int type, int max, i
// Uninitialized read on a temp
// We need to find correct replacements for each situation manually
SciTrackOriginReply originReply;
- r[index] = trackOriginAndFindWorkaround(index, uninitializedReadWorkarounds, &originReply);
- if ((r[index].segment == 0xFFFF) && (r[index].offset == 0xFFFF))
- error("Uninitialized read for temp %d from method %s::%s (script %d, localCall %x)", index, originReply.objectName.c_str(), originReply.methodName.c_str(), originReply.scriptNr, originReply.localCallOffset);
+ SciWorkaroundSolution solution = trackOriginAndFindWorkaround(index, uninitializedReadWorkarounds, &originReply);
+ if (solution.type == WORKAROUND_NONE)
+ error("Uninitialized read for temp %d from method %s::%s (script %d, room %d, localCall %x)",
+ index, originReply.objectName.c_str(), originReply.methodName.c_str(), originReply.scriptNr,
+ g_sci->getEngineState()->currentRoomNumber(), originReply.localCallOffset);
+ assert(solution.type == WORKAROUND_FAKE);
+ r[index] = make_reg(0, solution.value);
break;
}
case VAR_PARAM:
@@ -440,12 +289,9 @@ static void validate_write_var(reg_t *r, reg_t *stack_base, int type, int max, i
#define WRITE_VAR(type, index, value) validate_write_var(s->variables[type], s->stack_base, type, s->variablesMax[type], index, value, s->_segMan, g_sci->getKernel())
#define WRITE_VAR16(type, index, value) WRITE_VAR(type, index, make_reg(0, value));
-#define ACC_ARITHMETIC_L(op) make_reg(0, (op validate_arithmetic(s->r_acc)))
-
// Operating on the stack
// 16 bit:
#define PUSH(v) PUSH32(make_reg(0, v))
-#define POP() (validate_arithmetic(POP32()))
// 32 bit:
#define PUSH32(a) (*(validate_stack_addr(s, (s->xs->sp)++)) = (a))
#define POP32() (*(validate_stack_addr(s, --(s->xs->sp))))
@@ -618,29 +464,17 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt
if (argc > 1) {
// argc can indeed be bigger than 1 in some cases, and it's usually the
- // result of a script bug
+ // result of a script bug. Usually these aren't fatal.
const char *objectName = s->_segMan->getObjectName(send_obj);
- if (!strcmp(objectName, "Sq4GlobalNarrator") && selector == 606) {
- // SQ4 has a script bug in the Sq4GlobalNarrator object when invoking the
- // returnVal selector, which doesn't affect gameplay, thus don't diplay it
- } else if (!strcmp(objectName, "longSong") && selector == 3 && g_sci->getGameId() == GID_QFG1) {
- // QFG1VGA has a script bug in the longSong object when invoking the
- // loop selector, which doesn't affect gameplay, thus don't diplay it
- } else if (!strcmp(objectName, "PuzPiece") && selector == 77 && g_sci->getGameId() == GID_CASTLEBRAIN) {
- // Castle of Dr. Brain has a script bug in the PuzPiece object when invoking
- // the value selector, which doesn't affect gameplay, thus don't display it
- } else {
- // Unknown script bug, show it. Usually these aren't fatal.
- reg_t oldReg = *varp.getPointer(s->_segMan);
- reg_t newReg = argp[1];
- const char *selectorName = g_sci->getKernel()->getSelectorName(selector).c_str();
- warning("send_selector(): argc = %d while modifying variable selector "
- "%x (%s) of object %04x:%04x (%s) from %04x:%04x to %04x:%04x",
- argc, selector, selectorName, PRINT_REG(send_obj),
- objectName, PRINT_REG(oldReg), PRINT_REG(newReg));
- }
+ reg_t oldReg = *varp.getPointer(s->_segMan);
+ reg_t newReg = argp[1];
+ const char *selectorName = g_sci->getKernel()->getSelectorName(selector).c_str();
+ debug(2, "send_selector(): argc = %d while modifying variable selector "
+ "%x (%s) of object %04x:%04x (%s) from %04x:%04x to %04x:%04x",
+ argc, selector, selectorName, PRINT_REG(send_obj),
+ objectName, PRINT_REG(oldReg), PRINT_REG(newReg));
}
{
@@ -813,6 +647,61 @@ static void addKernelCallToExecStack(EngineState *s, int kernelCallNr, int argc,
xstack->type = EXEC_STACK_TYPE_KERNEL;
}
+static void logKernelCall(const KernelFunction *kernelCall, const KernelSubFunction *kernelSubCall, EngineState *s, int argc, reg_t *argv, reg_t result) {
+ Kernel *kernel = g_sci->getKernel();
+ if (!kernelSubCall) {
+ printf("k%s: ", kernelCall->name);
+ } else {
+ int callNameLen = strlen(kernelCall->name);
+ if (strncmp(kernelCall->name, kernelSubCall->name, callNameLen) == 0) {
+ const char *subCallName = kernelSubCall->name + callNameLen;
+ printf("k%s(%s): ", kernelCall->name, subCallName);
+ } else {
+ printf("k%s(%s): ", kernelCall->name, kernelSubCall->name);
+ }
+ }
+ for (int parmNr = 0; parmNr < argc; parmNr++) {
+ if (parmNr)
+ printf(", ");
+ uint16 regType = kernel->findRegType(argv[parmNr]);
+ if (regType & SIG_TYPE_NULL)
+ printf("0");
+ else if (regType & SIG_TYPE_UNINITIALIZED)
+ printf("UNINIT");
+ else if (regType & SIG_IS_INVALID)
+ printf("INVALID");
+ else if (regType & SIG_TYPE_INTEGER)
+ printf("%d", argv[parmNr].offset);
+ else {
+ printf("%04x:%04x", PRINT_REG(argv[parmNr]));
+ switch (regType) {
+ case SIG_TYPE_OBJECT:
+ printf(" (%s)", s->_segMan->getObjectName(argv[parmNr]));
+ break;
+ case SIG_TYPE_REFERENCE:
+ if (kernelCall->function == kSaid) {
+ SegmentRef saidSpec = s->_segMan->dereference(argv[parmNr]);
+ if (saidSpec.isRaw) {
+ printf(" ('");
+ g_sci->getVocabulary()->debugDecipherSaidBlock(saidSpec.raw);
+ printf("')");
+ } else {
+ printf(" (non-raw said-spec)");
+ }
+ } else {
+ printf(" ('%s')", s->_segMan->getString(argv[parmNr]).c_str());
+ }
+ default:
+ break;
+ }
+ }
+ }
+ if (result.segment)
+ printf(" = %04x:%04x\n", PRINT_REG(result));
+ else
+ printf(" = %d\n", result.offset);
+}
+
static void callKernelFunc(EngineState *s, int kernelCallNr, int argc) {
Kernel *kernel = g_sci->getKernel();
@@ -826,17 +715,24 @@ static void callKernelFunc(EngineState *s, int kernelCallNr, int argc) {
&& !kernel->signatureMatch(kernelCall.signature, argc, argv)) {
// signature mismatch, check if a workaround is available
SciTrackOriginReply originReply;
- reg_t workaround;
- workaround = trackOriginAndFindWorkaround(0, kernelCall.workarounds, &originReply);
- if ((workaround.segment == 0xFFFF) && (workaround.offset == 0xFFFF)) {
+ SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, kernelCall.workarounds, &originReply);
+ switch (solution.type) {
+ case WORKAROUND_NONE:
kernel->signatureDebug(kernelCall.signature, argc, argv);
- error("[VM] k%s[%x]: signature mismatch via method %s::%s (script %d, localCall %x)", kernelCall.name, kernelCallNr, originReply.objectName.c_str(), originReply.methodName.c_str(), originReply.scriptNr, originReply.localCallOffset);
- }
- // FIXME: implement some real workaround type logic - ignore call, still do call etc.
- if (workaround.segment == 2)
- s->r_acc = make_reg(0, workaround.offset);
- if (workaround.segment)
+ error("[VM] k%s[%x]: signature mismatch via method %s::%s (script %d, room %d, localCall 0x%x)",
+ kernelCall.name, kernelCallNr, originReply.objectName.c_str(), originReply.methodName.c_str(),
+ originReply.scriptNr, s->currentRoomNumber(), originReply.localCallOffset);
+ break;
+ case WORKAROUND_IGNORE: // don't do kernel call, leave acc alone
+ return;
+ case WORKAROUND_STILLCALL: // call kernel anyway
+ break;
+ case WORKAROUND_FAKE: // don't do kernel call, fake acc
+ s->r_acc = make_reg(0, solution.value);
return;
+ default:
+ error("unknown workaround type");
+ }
}
@@ -844,6 +740,9 @@ static void callKernelFunc(EngineState *s, int kernelCallNr, int argc) {
if (!kernelCall.subFunctionCount) {
addKernelCallToExecStack(s, kernelCallNr, argc, argv);
s->r_acc = kernelCall.function(s, argc, argv);
+
+ if (kernelCall.debugLogging)
+ logKernelCall(&kernelCall, NULL, s, argc, argv, s->r_acc);
} else {
// Sub-functions available, check signature and call that one directly
if (argc < 1)
@@ -860,43 +759,41 @@ static void callKernelFunc(EngineState *s, int kernelCallNr, int argc) {
if (kernelSubCall.signature && !kernel->signatureMatch(kernelSubCall.signature, argc, argv)) {
// Signature mismatch
SciTrackOriginReply originReply;
- reg_t workaround;
- workaround = trackOriginAndFindWorkaround(0, kernelSubCall.workarounds, &originReply);
- if ((workaround.segment == 0xFFFF) && (workaround.offset == 0xFFFF)) {
+ SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, kernelSubCall.workarounds, &originReply);
+ switch (solution.type) {
+ case WORKAROUND_NONE: {
kernel->signatureDebug(kernelSubCall.signature, argc, argv);
int callNameLen = strlen(kernelCall.name);
if (strncmp(kernelCall.name, kernelSubCall.name, callNameLen) == 0) {
const char *subCallName = kernelSubCall.name + callNameLen;
- error("[VM] k%s(%s): signature mismatch via method %s::%s (script %d, localCall %x)", kernelCall.name, subCallName, originReply.objectName.c_str(), originReply.methodName.c_str(), originReply.scriptNr, originReply.localCallOffset);
+ error("[VM] k%s(%s): signature mismatch via method %s::%s (script %d, room %d, localCall %x)",
+ kernelCall.name, subCallName, originReply.objectName.c_str(), originReply.methodName.c_str(),
+ originReply.scriptNr, s->currentRoomNumber(), originReply.localCallOffset);
}
- error("[VM] k%s: signature mismatch via method %s::%s (script %d, localCall %x)", kernelSubCall.name, originReply.objectName.c_str(), originReply.methodName.c_str(), originReply.scriptNr, originReply.localCallOffset);
+ error("[VM] k%s: signature mismatch via method %s::%s (script %d, room %d, localCall %x)",
+ kernelSubCall.name, originReply.objectName.c_str(), originReply.methodName.c_str(),
+ originReply.scriptNr, s->currentRoomNumber(), originReply.localCallOffset);
+ break;
}
- // FIXME: implement some real workaround type logic - ignore call, still do call etc.
- if (workaround.segment == 2)
- s->r_acc = make_reg(0, workaround.offset);
- if (workaround.segment)
+ case WORKAROUND_IGNORE: // don't do kernel call, leave acc alone
+ return;
+ case WORKAROUND_STILLCALL: // call kernel anyway
+ break;
+ case WORKAROUND_FAKE: // don't do kernel call, fake acc
+ s->r_acc = make_reg(0, solution.value);
return;
+ default:
+ error("unknown workaround type");
+ }
}
if (!kernelSubCall.function)
error("[VM] k%s: subfunction-id %d requested, but not available", kernelCall.name, subId);
addKernelCallToExecStack(s, kernelCallNr, argc, argv);
s->r_acc = kernelSubCall.function(s, argc, argv);
- }
-
-#if 0
- // Used for debugging
- Common::String debugMsg = Common::String::printf("%s [0x%x]", kernelCall.name, kernelCallNr) +
- Common::String::printf(", %d params: ", argc) +
- " (";
- for (int i = 0; i < argc; i++) {
- debugMsg += Common::String::printf("%04x:%04x", PRINT_REG(argv[i]));
- debugMsg += (i == argc - 1 ? ")" : ", ");
- }
-
- debugMsg += ", result: " + Common::String::printf("%04x:%04x", PRINT_REG(s->r_acc));
- debug("%s", debugMsg.c_str());
-#endif
+ if (kernelSubCall.debugLogging)
+ logKernelCall(&kernelCall, &kernelSubCall, s, argc, argv, s->r_acc);
+ }
// Remove callk stack frame again, if there's still an execution stack
if (s->_executionStack.begin() != s->_executionStack.end())
@@ -978,7 +875,7 @@ int readPMachineInstruction(const byte *src, byte &extOpcode, int16 opparams[4])
return offset;
}
-void run_vm(EngineState *s, bool restoring) {
+void run_vm(EngineState *s) {
assert(s);
int temp;
@@ -999,8 +896,7 @@ void run_vm(EngineState *s, bool restoring) {
if (!local_script)
error("run_vm(): program counter gone astray (local_script pointer is null)");
- if (!restoring)
- s->executionStackBase = s->_executionStack.size() - 1;
+ s->executionStackBase = s->_executionStack.size() - 1;
s->variablesSegment[VAR_TEMP] = s->variablesSegment[VAR_PARAM] = s->_segMan->findSegmentByType(SEG_TYPE_STACK);
s->variablesBase[VAR_TEMP] = s->variablesBase[VAR_PARAM] = s->stack_base;
@@ -1059,9 +955,7 @@ void run_vm(EngineState *s, bool restoring) {
g_sci->_debugState.breakpointWasHit = false;
}
Console *con = g_sci->getSciDebugger();
- if (con->isAttached()) {
- con->onFrame();
- }
+ con->onFrame();
if (s->xs->sp < s->xs->fp)
error("run_vm(): stack underflow, sp: %04x:%04x, fp: %04x:%04x",
@@ -1080,10 +974,15 @@ void run_vm(EngineState *s, bool restoring) {
switch (opcode) {
- case op_bnot: // 0x00 (00)
+ case op_bnot: { // 0x00 (00)
// Binary not
- s->r_acc = ACC_ARITHMETIC_L(0xffff ^ /*acc*/);
+ int16 value;
+ if (validate_signedInteger(s->r_acc, value))
+ s->r_acc = make_reg(0, 0xffff ^ value);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, s->r_acc, NULL_REG);
break;
+ }
case op_add: // 0x01 (01)
r_temp = POP32();
@@ -1148,45 +1047,96 @@ void run_vm(EngineState *s, bool restoring) {
}
break;
- case op_mul: // 0x03 (03)
- s->r_acc = ACC_ARITHMETIC_L(((int16)POP()) * (int16)/*acc*/);
+ case op_mul: { // 0x03 (03)
+ r_temp = POP32();
+ int16 value1, value2;
+ if (validate_signedInteger(s->r_acc, value1) && validate_signedInteger(r_temp, value2))
+ s->r_acc = make_reg(0, value1 * value2);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, s->r_acc, r_temp);
break;
+ }
case op_div: { // 0x04 (04)
- int16 divisor = signed_validate_arithmetic(s->r_acc);
- s->r_acc = make_reg(0, (divisor != 0 ? ((int16)POP()) / divisor : 0));
+ r_temp = POP32();
+ int16 divisor, dividend;
+ if (validate_signedInteger(s->r_acc, divisor) && validate_signedInteger(r_temp, dividend))
+ s->r_acc = make_reg(0, (divisor != 0 ? dividend / divisor : 0));
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, opcodeDivWorkarounds, s->r_acc, r_temp);
break;
}
+
case op_mod: { // 0x05 (05)
- int16 modulo = signed_validate_arithmetic(s->r_acc);
- s->r_acc = make_reg(0, (modulo != 0 ? ((int16)POP()) % modulo : 0));
+ r_temp = POP32();
+ int16 modulo, value;
+ if (validate_signedInteger(s->r_acc, modulo) && validate_signedInteger(r_temp, value))
+ s->r_acc = make_reg(0, (modulo != 0 ? value % modulo : 0));
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, s->r_acc, r_temp);
break;
}
- case op_shr: // 0x06 (06)
+
+ case op_shr: { // 0x06 (06)
// Shift right logical
- s->r_acc = ACC_ARITHMETIC_L(((uint16)POP()) >> /*acc*/);
+ r_temp = POP32();
+ uint16 value, shiftCount;
+ if (validate_unsignedInteger(r_temp, value) && validate_unsignedInteger(s->r_acc, shiftCount))
+ s->r_acc = make_reg(0, value >> shiftCount);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc);
break;
+ }
- case op_shl: // 0x07 (07)
+ case op_shl: { // 0x07 (07)
// Shift left logical
- s->r_acc = ACC_ARITHMETIC_L(((uint16)POP()) << /*acc*/);
+ r_temp = POP32();
+ uint16 value, shiftCount;
+ if (validate_unsignedInteger(r_temp, value) && validate_unsignedInteger(s->r_acc, shiftCount))
+ s->r_acc = make_reg(0, value << shiftCount);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc);
break;
+ }
- case op_xor: // 0x08 (08)
- s->r_acc = ACC_ARITHMETIC_L(POP() ^ /*acc*/);
+ case op_xor: { // 0x08 (08)
+ r_temp = POP32();
+ uint16 value1, value2;
+ if (validate_unsignedInteger(r_temp, value1) && validate_unsignedInteger(s->r_acc, value2))
+ s->r_acc = make_reg(0, value1 ^ value2);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc);
break;
+ }
- case op_and: // 0x09 (09)
- s->r_acc = ACC_ARITHMETIC_L(POP() & /*acc*/);
+ case op_and: { // 0x09 (09)
+ r_temp = POP32();
+ uint16 value1, value2;
+ if (validate_unsignedInteger(r_temp, value1) && validate_unsignedInteger(s->r_acc, value2))
+ s->r_acc = make_reg(0, value1 & value2);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc);
break;
+ }
- case op_or: // 0x0a (10)
- s->r_acc = ACC_ARITHMETIC_L(POP() | /*acc*/);
+ case op_or: { // 0x0a (10)
+ r_temp = POP32();
+ uint16 value1, value2;
+ if (validate_unsignedInteger(r_temp, value1) && validate_unsignedInteger(s->r_acc, value2))
+ s->r_acc = make_reg(0, value1 | value2);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, opcodeOrWorkarounds, r_temp, s->r_acc);
break;
+ }
- case op_neg: // 0x0b (11)
- s->r_acc = ACC_ARITHMETIC_L(-/*acc*/);
+ case op_neg: { // 0x0b (11)
+ int16 value;
+ if (validate_signedInteger(s->r_acc, value))
+ s->r_acc = make_reg(0, -value);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, s->r_acc, NULL_REG);
break;
+ }
case op_not: // 0x0c (12)
s->r_acc = make_reg(0, !(s->r_acc.offset || s->r_acc.segment));
@@ -1220,12 +1170,17 @@ void run_vm(EngineState *s, bool restoring) {
s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset > s->r_acc.offset);
} else if (r_temp.segment && !s->r_acc.segment) {
if (s->r_acc.offset >= 1000)
- error("[VM] op_gt: comparsion between a pointer and number");
- // Pseudo-WORKAROUND: sierra allows any pointer <-> value comparsion
+ error("[VM] op_gt: comparison between a pointer and number");
+ // Pseudo-WORKAROUND: Sierra allows any pointer <-> value comparison
// Happens in SQ1, room 28, when throwing the water at Orat
s->r_acc = make_reg(0, 1);
- } else
- s->r_acc = ACC_ARITHMETIC_L(signed_validate_arithmetic(r_temp) > (int16)/*acc*/);
+ } else {
+ int16 compare1, compare2;
+ if (validate_signedInteger(r_temp, compare1) && validate_signedInteger(s->r_acc, compare2))
+ s->r_acc = make_reg(0, compare1 > compare2);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc);
+ }
break;
case op_ge_: // 0x10 (16)
@@ -1236,8 +1191,13 @@ void run_vm(EngineState *s, bool restoring) {
if (r_temp.segment != s->r_acc.segment)
warning("[VM] Comparing pointers in different segments (%04x:%04x vs. %04x:%04x)", PRINT_REG(r_temp), PRINT_REG(s->r_acc));
s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset >= s->r_acc.offset);
- } else
- s->r_acc = ACC_ARITHMETIC_L(signed_validate_arithmetic(r_temp) >= (int16)/*acc*/);
+ } else {
+ int16 compare1, compare2;
+ if (validate_signedInteger(r_temp, compare1) && validate_signedInteger(s->r_acc, compare2))
+ s->r_acc = make_reg(0, compare1 >= compare2);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc);
+ }
break;
case op_lt_: // 0x11 (17)
@@ -1250,12 +1210,17 @@ void run_vm(EngineState *s, bool restoring) {
s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset < s->r_acc.offset);
} else if (r_temp.segment && !s->r_acc.segment) {
if (s->r_acc.offset >= 1000)
- error("[VM] op_lt: comparsion between a pointer and number");
- // Pseudo-WORKAROUND: sierra allows any pointer <-> value comparsion
+ error("[VM] op_lt: comparison between a pointer and number");
+ // Pseudo-WORKAROUND: Sierra allows any pointer <-> value comparison
// Happens in SQ1, room 58, when giving id-card to robot
s->r_acc = make_reg(0, 1);
- } else
- s->r_acc = ACC_ARITHMETIC_L(signed_validate_arithmetic(r_temp) < (int16)/*acc*/);
+ } else {
+ int16 compare1, compare2;
+ if (validate_signedInteger(r_temp, compare1) && validate_signedInteger(s->r_acc, compare2))
+ s->r_acc = make_reg(0, compare1 < compare2);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc);
+ }
break;
case op_le_: // 0x12 (18)
@@ -1266,8 +1231,13 @@ void run_vm(EngineState *s, bool restoring) {
if (r_temp.segment != s->r_acc.segment)
warning("[VM] Comparing pointers in different segments (%04x:%04x vs. %04x:%04x)", PRINT_REG(r_temp), PRINT_REG(s->r_acc));
s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset <= s->r_acc.offset);
- } else
- s->r_acc = ACC_ARITHMETIC_L(signed_validate_arithmetic(r_temp) <= (int16)/*acc*/);
+ } else {
+ int16 compare1, compare2;
+ if (validate_signedInteger(r_temp, compare1) && validate_signedInteger(s->r_acc, compare2))
+ s->r_acc = make_reg(0, compare1 <= compare2);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc);
+ }
break;
case op_ugt_: // 0x13 (19)
@@ -1289,8 +1259,13 @@ void run_vm(EngineState *s, bool restoring) {
s->r_acc = make_reg(0, 1);
else if (r_temp.segment && s->r_acc.segment)
s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset > s->r_acc.offset);
- else
- s->r_acc = ACC_ARITHMETIC_L(validate_arithmetic(r_temp) > /*acc*/);
+ else {
+ uint16 compare1, compare2;
+ if (validate_unsignedInteger(r_temp, compare1) && validate_unsignedInteger(s->r_acc, compare2))
+ s->r_acc = make_reg(0, compare1 > compare2);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc);
+ }
break;
case op_uge_: // 0x14 (20)
@@ -1303,8 +1278,13 @@ void run_vm(EngineState *s, bool restoring) {
s->r_acc = make_reg(0, 1);
else if (r_temp.segment && s->r_acc.segment)
s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset >= s->r_acc.offset);
- else
- s->r_acc = ACC_ARITHMETIC_L(validate_arithmetic(r_temp) >= /*acc*/);
+ else {
+ uint16 compare1, compare2;
+ if (validate_unsignedInteger(r_temp, compare1) && validate_unsignedInteger(s->r_acc, compare2))
+ s->r_acc = make_reg(0, compare1 >= compare2);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc);
+ }
break;
case op_ult_: // 0x15 (21)
@@ -1313,12 +1293,18 @@ void run_vm(EngineState *s, bool restoring) {
r_temp = POP32();
// See above
- if (r_temp.segment && (s->r_acc == make_reg(0, 1000)))
+ // PQ2 japanese compares pointers to 2000 to find out if its a pointer or a resourceid
+ if (r_temp.segment && (s->r_acc == make_reg(0, 1000) || (s->r_acc == make_reg(0, 2000))))
s->r_acc = NULL_REG;
else if (r_temp.segment && s->r_acc.segment)
s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset < s->r_acc.offset);
- else
- s->r_acc = ACC_ARITHMETIC_L(validate_arithmetic(r_temp) < /*acc*/);
+ else {
+ uint16 compare1, compare2;
+ if (validate_unsignedInteger(r_temp, compare1) && validate_unsignedInteger(s->r_acc, compare2))
+ s->r_acc = make_reg(0, compare1 < compare2);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc);
+ }
break;
case op_ule_: // 0x16 (22)
@@ -1331,8 +1317,13 @@ void run_vm(EngineState *s, bool restoring) {
s->r_acc = NULL_REG;
else if (r_temp.segment && s->r_acc.segment)
s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset <= s->r_acc.offset);
- else
- s->r_acc = ACC_ARITHMETIC_L(validate_arithmetic(r_temp) <= /*acc*/);
+ else {
+ uint16 compare1, compare2;
+ if (validate_unsignedInteger(r_temp, compare1) && validate_unsignedInteger(s->r_acc, compare2))
+ s->r_acc = make_reg(0, compare1 <= compare2);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc);
+ }
break;
case op_bt: // 0x17 (23)
@@ -1647,32 +1638,55 @@ void run_vm(EngineState *s, bool restoring) {
validate_property(obj, (opparams[0] >> 1)) = POP32();
break;
- case op_ipToa: // 0x35 (53)
- // Incement Property and copy To Accumulator
- s->r_acc = validate_property(obj, (opparams[0] >> 1));
- s->r_acc = validate_property(obj, (opparams[0] >> 1)) = ACC_ARITHMETIC_L(1 + /*acc*/);
+ case op_ipToa: { // 0x35 (53)
+ // Increment Property and copy To Accumulator
+ reg_t &opProperty = validate_property(obj, opparams[0] >> 1);
+ uint16 valueProperty;
+ if (validate_unsignedInteger(opProperty, valueProperty))
+ s->r_acc = make_reg(0, valueProperty + 1);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, opProperty, NULL_REG);
+ opProperty = s->r_acc;
break;
+ }
case op_dpToa: { // 0x36 (54)
// Decrement Property and copy To Accumulator
- s->r_acc = validate_property(obj, (opparams[0] >> 1));
- s->r_acc = validate_property(obj, (opparams[0] >> 1)) = ACC_ARITHMETIC_L(-1 + /*acc*/);
+ reg_t &opProperty = validate_property(obj, opparams[0] >> 1);
+ uint16 valueProperty;
+ if (validate_unsignedInteger(opProperty, valueProperty))
+ s->r_acc = make_reg(0, valueProperty - 1);
+ else
+ s->r_acc = arithmetic_lookForWorkaround(opcode, opcodeDptoaWorkarounds, opProperty, NULL_REG);
+ opProperty = s->r_acc;
break;
}
- case op_ipTos: // 0x37 (55)
+ case op_ipTos: { // 0x37 (55)
// Increment Property and push to Stack
- validate_arithmetic(validate_property(obj, (opparams[0] >> 1)));
- temp = ++validate_property(obj, (opparams[0] >> 1)).offset;
- PUSH(temp);
+ reg_t &opProperty = validate_property(obj, opparams[0] >> 1);
+ uint16 valueProperty;
+ if (validate_unsignedInteger(opProperty, valueProperty))
+ valueProperty++;
+ else
+ valueProperty = arithmetic_lookForWorkaround(opcode, NULL, opProperty, NULL_REG).offset;
+ opProperty = make_reg(0, valueProperty);
+ PUSH(valueProperty);
break;
+ }
- case op_dpTos: // 0x38 (56)
+ case op_dpTos: { // 0x38 (56)
// Decrement Property and push to Stack
- validate_arithmetic(validate_property(obj, (opparams[0] >> 1)));
- temp = --validate_property(obj, (opparams[0] >> 1)).offset;
- PUSH(temp);
+ reg_t &opProperty = validate_property(obj, opparams[0] >> 1);
+ uint16 valueProperty;
+ if (validate_unsignedInteger(opProperty, valueProperty))
+ valueProperty--;
+ else
+ valueProperty = arithmetic_lookForWorkaround(opcode, NULL, opProperty, NULL_REG).offset;
+ opProperty = make_reg(0, valueProperty);
+ PUSH(valueProperty);
break;
+ }
case op_lofsa: // 0x39 (57)
// Load Offset to Accumulator
diff --git a/engines/sci/engine/vm.h b/engines/sci/engine/vm.h
index 81ec4f1c61..ee22e03310 100644
--- a/engines/sci/engine/vm.h
+++ b/engines/sci/engine/vm.h
@@ -315,9 +315,8 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj,
* It executes the code on s->heap[pc] until it hits a 'ret' operation
* while (stack_base == stack_pos). Requires s to be set up correctly.
* @param[in] s The state to use
- * @param[in] restoring true if s has just been restored, false otherwise
*/
-void run_vm(EngineState *s, bool restoring);
+void run_vm(EngineState *s);
/**
* Debugger functionality
diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp
new file mode 100644
index 0000000000..0db73e34d5
--- /dev/null
+++ b/engines/sci/engine/workarounds.cpp
@@ -0,0 +1,357 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "sci/engine/kernel.h"
+#include "sci/engine/state.h"
+#include "sci/engine/vm.h"
+#include "sci/engine/workarounds.h"
+
+#define SCI_WORKAROUNDENTRY_TERMINATOR { (SciGameId)0, -1, -1, 0, NULL, NULL, -1, 0, { WORKAROUND_NONE, 0 } }
+
+namespace Sci {
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry opcodeDivWorkarounds[] = {
+ { GID_QFG1VGA, 301, 928, 0, "Blink", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // when entering inn, gets called with 1 parameter, but 2nd parameter is used for div which happens to be an object
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry opcodeOrWorkarounds[] = {
+ { GID_ECOQUEST2, 100, 0, 0, "Rain", "points", 0xcc6, 0, { WORKAROUND_FAKE, 0 } }, // when giving the papers to the customs officer, gets called against a pointer instead of a number - bug #3034464
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call, index, workaround
+const SciWorkaroundEntry opcodeDptoaWorkarounds[] = {
+ { GID_LSL6, 360, 938, 0, "ROsc", "cycleDone", -1, 0, { WORKAROUND_FAKE, 1 } }, // when looking through tile in the shower room initial cycles get set to an object instead of 2, we fix this by setting 1 after decrease
+ { GID_LSL6HIRES, 360,64938, 0, "ROsc", "cycleDone", -1, 0, { WORKAROUND_FAKE, 1 } }, // when looking through tile in the shower room initial cycles get set to an object instead of 2, we fix this by setting 1 after decrease
+ { GID_SQ5, 200, 939, 0, "Osc", "cycleDone", -1, 0, { WORKAROUND_FAKE, 1 } }, // when going back to bridge the crew is goofing off, we get an object as cycle count
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry uninitializedReadWorkarounds[] = {
+ { GID_CNICK_KQ, 200, 0, 1, "Character", "<noname 446>", -1, 504, { WORKAROUND_FAKE, 0 } }, // checkers, like in hoyle 3
+ { GID_CNICK_KQ, 200, 0, 1, "Character", "<noname 446>", -1, 505, { WORKAROUND_FAKE, 0 } }, // checkers, like in hoyle 3
+ { GID_CNICK_KQ, -1, 700, 0, "gcWindow", "<noname 183>", -1, -1, { WORKAROUND_FAKE, 0 } }, // when entering control menu, like in hoyle 3
+ { GID_CNICK_LONGBOW, 0, 0, 0, "RH Budget", "<noname 110>", -1, 1, { WORKAROUND_FAKE, 0 } }, // when starting the game
+ { GID_ECOQUEST, -1, -1, 0, NULL, "doVerb", -1, 0, { WORKAROUND_FAKE, 0 } }, // almost clicking anywhere triggers this in almost all rooms
+ { GID_FREDDYPHARKAS, -1, 24, 0, "gcWin", "open", -1, 5, { WORKAROUND_FAKE, 0xf } }, // is used as priority for game menu
+ { GID_FREDDYPHARKAS, -1, 31, 0, "quitWin", "open", -1, 5, { WORKAROUND_FAKE, 0xf } }, // is used as priority for game menu
+ { GID_GK1, -1, 64950, 1, "Feature", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // sometimes when walk-clicking
+ { GID_GK2, -1, 11, 0, "", "export 10", -1, 3, { WORKAROUND_FAKE, 0 } }, // called when the game starts
+ { GID_GK2, -1, 11, 0, "", "export 10", -1, 4, { WORKAROUND_FAKE, 0 } }, // called during the game
+ { GID_HOYLE1, 4, 104, 0, "GinRummyCardList", "calcRuns", -1, 4, { WORKAROUND_FAKE, 0 } }, // Gin Rummy / right when the game starts
+ { GID_HOYLE1, 5, 204, 0, "tableau", "checkRuns", -1, 2, { WORKAROUND_FAKE, 0 } }, // Cribbage / during the game
+ { GID_HOYLE3, -1, 0, 1, "Character", "say", -1, 504, { WORKAROUND_FAKE, 0 } }, // when starting checkers or dominoes, first time a character says something
+ { GID_HOYLE3, -1, 0, 1, "Character", "say", -1, 505, { WORKAROUND_FAKE, 0 } }, // when starting checkers or dominoes, first time a character says something
+ { GID_HOYLE3, -1, 700, 0, "gcWindow", "open", -1, -1, { WORKAROUND_FAKE, 0 } }, // when entering control menu
+ { GID_ISLANDBRAIN, 140, 140, 0, "piece", "init", -1, 3, { WORKAROUND_FAKE, 1 } }, // first puzzle right at the start, some initialization variable. bnt is done on it, and it should be non-0
+ { GID_ISLANDBRAIN, 200, 268, 0, "anElement", "select", -1, 0, { WORKAROUND_FAKE, 0 } }, // elements puzzle, gets used before super TextIcon
+ { GID_JONES, 1, 232, 0, "weekendText", "draw", 0x3d3, 0, { WORKAROUND_FAKE, 0 } }, // jones/cd only - gets called during the game
+ { GID_JONES, 1, 255, 0, "", "export 0", -1, 13, { WORKAROUND_FAKE, 0 } }, // jones/cd only - called when a game ends
+ { GID_JONES, 1, 255, 0, "", "export 0", -1, 14, { WORKAROUND_FAKE, 0 } }, // jones/cd only - called when a game ends
+ { GID_JONES, 764, 255, 0, "", "export 0", -1, 13, { WORKAROUND_FAKE, 0 } }, // jones/ega&vga only - called when the game starts
+ { GID_JONES, 764, 255, 0, "", "export 0", -1, 14, { WORKAROUND_FAKE, 0 } }, // jones/ega&vga only - called when the game starts
+ { GID_KQ5, -1, 0, 0, "", "export 29", -1, 3, { WORKAROUND_FAKE, 0 } }, // called when playing harp for the harpies or when aborting dialog in toy shop, is used for kDoAudio - bug #3034700
+ { GID_KQ5, 25, 25, 0, "rm025", "doit", -1, 0, { WORKAROUND_FAKE, 0 } }, // inside witch forest, when going to the room where the walking rock is
+ { GID_KQ6, -1, 30, 0, "rats", "changeState", -1, -1, { WORKAROUND_FAKE, 0 } }, // rats in the catacombs (temps 1 - 5) - bugs #3034597, #3035495, #3035824
+ { GID_KQ6, 210, 210, 0, "rm210", "scriptCheck", -1, 0, { WORKAROUND_FAKE, 1 } }, // using inventory in that room - bug #3034565
+ { GID_KQ6, 500, 500, 0, "rm500", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // going to island of the beast
+ { GID_KQ6, 520, 520, 0, "rm520", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // going to boiling water trap on beast isle
+ { GID_KQ6, -1, 903, 0, "controlWin", "open", -1, 4, { WORKAROUND_FAKE, 0 } }, // when opening the controls window (save, load etc)
+ { GID_KQ7, 30, 64996, 0, "User", "handleEvent", -1, 1, { WORKAROUND_FAKE, 0 } }, // called when pushing a keyboard key
+ { GID_LAURABOW, 44, 967, 0, "myIcon", "cycle", -1, 1, { WORKAROUND_FAKE, 0 } }, // second dialog box after the intro, when talking with Lillian - bug #3034985
+ { GID_LAURABOW2, -1, 24, 0, "gcWin", "open", -1, 5, { WORKAROUND_FAKE, 0xf } }, // is used as priority for game menu
+ { GID_LAURABOW2, -1, 21, 0, "dropCluesCode", "doit", -1, 1, { WORKAROUND_FAKE, 0 } }, // when asking some questions (e.g. the reporter about the burglary, or the policeman about Ziggy) - bugs #3035068, #3036274
+ { GID_LAURABOW2, 240, 240, 0, "sSteveAnimates", "changeState", -1, 0, { WORKAROUND_FAKE, 0 } }, // Steve Dorian's idle animation at the docks - bug #3036291
+ { GID_LONGBOW, -1, 213, 0, "clear", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // When giving an aswer using the druid hand sign code in any room
+ { GID_LONGBOW, -1, 213, 0, "letter", "handleEvent", 0xa8, 1, { WORKAROUND_FAKE, 0 } }, // When using the druid hand sign code in any room - bug #3036601
+ { GID_LSL1, 250, 250, 0, "increase", "handleEvent", -1, 2, { WORKAROUND_FAKE, 0 } }, // casino, playing game, increasing bet
+ { GID_LSL1, 720, 720, 0, "rm720", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // age check room
+ { GID_LSL2, 38, 38, 0, "cloudScript", "changeState", -1, 1, { WORKAROUND_FAKE, 0 } }, // entering the room in the middle deck of the ship - bug #3036483
+ { GID_LSL3, 340, 340, 0, "ComicScript", "changeState", -1, -1, { WORKAROUND_FAKE, 0 } }, // right after entering the 3 ethnic groups inside comedy club (temps 200, 201, 202, 203)
+ { GID_LSL3, -1, 997, 0, "TheMenuBar", "handleEvent", -1, 1, { WORKAROUND_FAKE, 0xf } }, // when setting volume the first time, this temp is used to set volume on entry (normally it would have been initialized to 's')
+ { GID_LSL6, -1, 85, 0, "washcloth", "doVerb", -1, 0, { WORKAROUND_FAKE, 0 } }, // washcloth in inventory
+ { GID_LSL6, -1, 928, -1, "Narrator", "startText", -1, 0, { WORKAROUND_FAKE, 0 } }, // used by various objects that are even translated in foreign versions, that's why we use the base-class
+ { GID_LSL6HIRES, 0, 85, 0, "LL6Inv", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // on startup
+ { GID_LSL6HIRES, -1, 64950, 1, "Feature", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // at least when entering swimming pool area
+ { GID_LSL6HIRES, -1, 64964, 0, "DPath", "init", -1, 1, { WORKAROUND_FAKE, 0 } }, // during the game
+ { GID_MOTHERGOOSE, 18, 992, 0, "AIPath", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // DEMO: Called when walking north from mother goose's house two screens
+ { GID_MOTHERGOOSEHIRES,-1,64950, 1, "Feature", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // right when clicking on a child at the start and probably also later
+ { GID_MOTHERGOOSEHIRES,-1,64950, 1, "View", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // see above
+ { GID_QFG2, -1, 71, 0, "theInvSheet", "doit", -1, 1, { WORKAROUND_FAKE, 0 } }, // accessing the inventory
+ { GID_QFG2, -1, 701, 0, "Alley", "at", -1, 0, { WORKAROUND_FAKE, 0 } }, // when walking inside the alleys in the town - bug #3035835
+ { GID_QFG3, 330, 330, 0, "rajahTeller", "doChild", -1, 0, { WORKAROUND_FAKE, 0 } }, // when talking to King Rajah about "Tarna"
+ { GID_QFG3, 330, 330, 0, "rajahTeller", "doChild", -1, 1, { WORKAROUND_FAKE, 0 } }, // when talking to King Rajah about "Rajah" - bug #3036390
+ { GID_SQ1, 103, 103, 0, "hand", "internalEvent", -1, 1, { WORKAROUND_FAKE, 0 } }, // spanish (and maybe early versions?) only: when moving cursor over input pad
+ { GID_SQ1, 103, 103, 0, "hand", "internalEvent", -1, 2, { WORKAROUND_FAKE, 0 } }, // spanish (and maybe early versions?) only: when moving cursor over input pad
+ { GID_SQ1, -1, 703, 0, "", "export 1", -1, 0, { WORKAROUND_FAKE, 0 } }, // sub that's called from several objects while on sarien battle cruiser
+ { GID_SQ1, -1, 703, 0, "firePulsar", "changeState", 0x18a, 0, { WORKAROUND_FAKE, 0 } }, // export 1, but called locally (when shooting at aliens)
+ { GID_SQ4, -1, 398, 0, "showBox", "changeState", -1, 0, { WORKAROUND_FAKE, 0 } }, // sq4cd: called when rummaging in Software Excess bargain bin
+ { GID_SQ4, -1, 928, 0, "Narrator", "startText", -1, 1000, { WORKAROUND_FAKE, 1 } }, // sq4cd: method returns this to the caller
+ { GID_SQ6, 100, 0, 0, "SQ6", "init", -1, 2, { WORKAROUND_FAKE, 0 } }, // called when the game starts
+ { GID_SQ6, 100, 64950, 0, "View", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // called when pressing "Start game" in the main menu
+ { GID_SQ6, -1, 64964, 0, "DPath", "init", -1, 1, { WORKAROUND_FAKE, 0 } }, // during the game
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kAbs_workarounds[] = {
+ { GID_HOYLE1, 1, 1, 0, "room1", "doit", -1, 0, { WORKAROUND_FAKE, 0x3e9 } }, // crazy eights - called with objects instead of integers
+ { GID_HOYLE1, 2, 2, 0, "room2", "doit", -1, 0, { WORKAROUND_FAKE, 0x3e9 } }, // old maid - called with objects instead of integers
+ { GID_HOYLE1, 3, 3, 0, "room3", "doit", -1, 0, { WORKAROUND_FAKE, 0x3e9 } }, // hearts - called with objects instead of integers
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kCelHigh_workarounds[] = {
+ { GID_SQ1, 1, 255, 0, "DIcon", "setSize", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // DEMO: Called with 2nd/3rd parameters as objects when clicking on the menu
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kCelWide_workarounds[] = {
+ { GID_SQ1, 1, 255, 0, "DIcon", "setSize", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // DEMO: Called with 2nd/3rd parameters as objects when clicking on the menu - bug #3035720
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kDisplay_workarounds[] = {
+ { GID_ISLANDBRAIN, 300, 300, 0, "geneDude", "show", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when looking at the gene explanation chart - a parameter is an object
+ { GID_SQ4, 391, 391, 0, "doCatalog", "mode", 0x84, 0, { WORKAROUND_IGNORE, 0 } }, // clicking on catalog in roboter sale - a parameter is an object
+ { GID_SQ4, 391, 391, 0, "choosePlug", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // ordering connector in roboter sale - a parameter is an object
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kDisposeScript_workarounds[] = {
+ { GID_LAURABOW, 777, 777, 0, "myStab", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: after the will is signed, parameter 0 is an object - bug #3034907
+ { GID_QFG1, -1, 64, 0, "rm64", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when leaving graveyard, parameter 0 is an object
+ { GID_SQ4, 150, 151, 0, "fightScript", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during fight with vohaul, parameter 0 is an object
+ { GID_SQ4, 150, 152, 0, "driveCloseUp", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when choosing "beam download", parameter 0 is an object
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kDoSoundFade_workarounds[] = {
+ { GID_CAMELOT, -1, 989, 0, "rmMusic", "fade", -1, 0, { WORKAROUND_IGNORE, 0 } }, // gets called frequently with a NULL reference (i.e. 0:0) - bug #3035149
+ { GID_KQ1, -1, 989, 0, "gameSound", "fade", -1, 0, { WORKAROUND_IGNORE, 0 } }, // gets called in several scenes (e.g. graham cracker) with 0:0
+ { GID_KQ6, 105, 989, 0, "globalSound", "fade", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // floppy: during intro, parameter 4 is an object
+ { GID_KQ6, 460, 989, 0, "globalSound2", "fade", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // after pulling the black widow's web on the isle of wonder, parameter 4 is an object - bug #3034567
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kGetAngle_workarounds[] = {
+ { GID_KQ6, 740, 752, 0, "throwDazzle", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // after the Genie is exposed in the Palace (short and long ending), it starts shooting lightning bolts around. An extra 5th parameter is passed - bug #3034610
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kFindKey_workarounds[] = {
+ { GID_ECOQUEST2, 100, 999, 0, "myList", "contains", -1, 0, { WORKAROUND_FAKE, 0 } }, // When Noah Greene gives Adam the Ecorder, and just before the game gives a demonstration, a null reference to a list is passed - bug #3035186
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kGraphDrawLine_workarounds[] = {
+ { GID_ISLANDBRAIN, 300, 300, 0, "dudeViewer", "show", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when looking at the gene explanation chart, gets called with 1 extra parameter
+ { GID_SQ1, 43, 43, 0, "someoneDied", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when ordering beer, gets called with 1 extra parameter
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kGraphSaveBox_workarounds[] = {
+ { GID_CASTLEBRAIN, 420, 427, 0, "alienIcon", "select", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when selecting a card during the alien card game, gets called with 1 extra parameter
+ { GID_ISLANDBRAIN, 290, 291, 0, "upElevator", "changeState",0x201f, 0, { WORKAROUND_STILLCALL, 0 } }, // when testing in the elevator puzzle, gets called with 1 argument less - 15 is on stack - bug #3034485
+ { GID_ISLANDBRAIN, 290, 291, 0, "downElevator", "changeState",0x201f, 0, { WORKAROUND_STILLCALL, 0 } }, // see above
+ { GID_ISLANDBRAIN, 290, 291, 0, "correctElevator", "changeState",0x201f, 0, { WORKAROUND_STILLCALL, 0 } }, // see above (when testing the correct solution)
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kGraphRestoreBox_workarounds[] = {
+ { GID_LSL6, -1, 85, 0, "rScroller", "hide", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens when restoring (sometimes), same as the one below
+ { GID_LSL6, -1, 85, 0, "lScroller", "hide", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens when restoring (sometimes), same as the one below
+ { GID_LSL6, -1, 86, 0, "LL6Inv", "show", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens when restoring, is called with hunk segment, but hunk is not allocated at that time
+ // ^^ TODO: check, if this is really a script error or an issue with our restore code
+ { GID_LSL6, -1, 86, 0, "LL6Inv", "hide", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens during the game, gets called with 1 extra parameter
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kGraphFillBoxForeground_workarounds[] = {
+ { GID_LSL6, -1, 0, 0, "LSL6", "hideControls", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens when giving the bungee key to merrily (room 240) and at least in room 650 too - gets called with additional 5th parameter
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kGraphFillBoxAny_workarounds[] = {
+ { GID_SQ4, -1, 818, 0, "iconTextSwitch", "show", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // game menu "text/speech" display - parameter 5 is missing, but the right color number is on the stack
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kGraphRedrawBox_workarounds[] = {
+ { GID_SQ4, 405, 405, 0, "swimAfterEgo", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified
+ { GID_SQ4, 406, 406, 0, "swimAndShoot", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified
+ { GID_SQ4, 410, 410, 0, "swimAfterEgo", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified
+ { GID_SQ4, 411, 411, 0, "swimAndShoot", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // skateOrama when "swimming" in the air - accidental additional parameter specified
+ { GID_SQ4, -1, 704, 0, "shootEgo", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // When shot by Droid in Super Computer Maze (Rooms 500, 505, 510...) - accidental additional parameter specified
+ { GID_KQ5, -1, 981, 0, "myWindow", "dispose", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the floppy version, when closing any dialog box, accidental additional parameter specified - bug #3036331
+ { GID_KQ5, -1, 995, 0, "invW", "doit", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // Happens in the floppy version, when closing the inventory window, accidental additional parameter specified
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kIsObject_workarounds[] = {
+ { GID_GK1, 50, 999, 0, "List", "eachElementDo", -1, 0, { WORKAROUND_FAKE, 0 } }, // GK1 demo, when asking Grace for messages it gets called with an invalid parameter (type "error") - bug #3034519
+ { GID_ISLANDBRAIN, -1, 999, 0, "List", "eachElementDo", -1, 0, { WORKAROUND_FAKE, 0 } }, // when going to the game options, choosing "Info" and selecting anything from the list, gets called with an invalid parameter (type "error") - bug #3035262
+ { GID_QFG3, -1, 999, 0, "List", "eachElementDo", -1, 0, { WORKAROUND_FAKE, 0 } }, // when asking for something, gets called with type error parameter
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kMemory_workarounds[] = {
+ { GID_LAURABOW2, -1, 999, 0, "", "export 6", -1, 0, { WORKAROUND_FAKE, 0 } }, // during the intro, when exiting the train, talking to Mr. Augustini, etc. - bug #3034490
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kNewWindow_workarounds[] = {
+ { GID_ECOQUEST, -1, 981, 0, "SysWindow", "<noname 178>", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // EcoQuest 1 demo uses an in-between interpreter from SCI1 to SCI1.1. It's SCI1.1, but uses the SCI1 semantics for this call - bug #3035057
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kPaletteUnsetFlag_workarounds[] = {
+ { GID_QFG4, 100, 100, 0, "doMovie", "<noname 144>", -1, 0, { WORKAROUND_IGNORE, 0 } }, // after the Sierra logo, no flags are passed, thus the call is meaningless - bug #3034506
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kSetPort_workarounds[] = {
+ { GID_LSL6, 740, 740, 0, "rm740", "drawPic", -1, 0, { WORKAROUND_IGNORE, 0 } }, // ending scene, is called with additional 3 (!) parameters
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+// gameID, room,script,lvl, object-name, method-name, call,index, workaround
+const SciWorkaroundEntry kUnLoad_workarounds[] = {
+ { GID_CAMELOT, 921, 921, 1, "Script", "changeState", 0x36, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: While showing Camelot (and other places), the reference is invalid - bug #3035000
+ { GID_CAMELOT, 921, 921, 1, "Script", "init", 0x36, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: When being attacked by the boar (and other places), the reference is invalid - bug #3035000
+ { GID_CASTLEBRAIN, 320, 377, 0, "SWord", "upDate", -1, 0, { WORKAROUND_IGNORE, 0 } }, // after solving the cross-word-puzzle, trying to unload invalid reference
+ { GID_CASTLEBRAIN, 320, 377, 0, "theWord", "show", -1, 0, { WORKAROUND_IGNORE, 0 } }, // 2nd word puzzle, when exiting before solving, trying to unload invalid reference - bug #3034473
+ { GID_ECOQUEST, 380, 61, 0, "gotIt", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // after talking to the dolphin the first time
+ { GID_LAURABOW2, 1, 1, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during the intro, a 3rd parameter is passed by accident - bug #3034902
+ { GID_LAURABOW2, 2, 2, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during the intro, a 3rd parameter is passed by accident - bug #3034902
+ { GID_LAURABOW2, 4, 4, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: inside the museum, a 3rd parameter is passed by accident - bug #3034902
+ { GID_LAURABOW2, 6, 6, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: after the murder, a 3rd parameter is passed by accident - bug #3034902
+ { GID_LAURABOW2, 7, 7, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: after the logo is shown, a 3rd parameter is passed by accident - bug #3034902
+ { GID_LSL6, 130, 130, 0, "recruitLarryScr", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during intro, a 3rd parameter is passed by accident
+ { GID_LSL6, 740, 740, 0, "showCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during ending, 4 additional parameters are passed by accident
+ { GID_LSL6HIRES, 130, 130, 0, "recruitLarryScr", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during intro, a 3rd parameter is passed by accident
+ { GID_SQ1, 43, 303, 0, "slotGuy", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when leaving ulence flats bar, parameter 1 is not passed - script error
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
+SciWorkaroundSolution trackOriginAndFindWorkaround(int index, const SciWorkaroundEntry *workaroundList, SciTrackOriginReply *trackOrigin) {
+ EngineState *state = g_sci->getEngineState();
+ ExecStack *lastCall = state->xs;
+ Script *local_script = state->_segMan->getScriptIfLoaded(lastCall->local_segment);
+ int curScriptNr = local_script->getScriptNumber();
+
+ if (lastCall->debugLocalCallOffset != -1) {
+ // if lastcall was actually a local call search back for a real call
+ Common::List<ExecStack>::iterator callIterator = state->_executionStack.end();
+ while (callIterator != state->_executionStack.begin()) {
+ callIterator--;
+ ExecStack loopCall = *callIterator;
+ if ((loopCall.debugSelector != -1) || (loopCall.debugExportId != -1)) {
+ lastCall->debugSelector = loopCall.debugSelector;
+ lastCall->debugExportId = loopCall.debugExportId;
+ break;
+ }
+ }
+ }
+
+ Common::String curObjectName = state->_segMan->getObjectName(lastCall->sendp);
+ Common::String curMethodName;
+ const SciGameId gameId = g_sci->getGameId();
+ const int curRoomNumber = state->currentRoomNumber();
+
+ if (lastCall->type == EXEC_STACK_TYPE_CALL) {
+ if (lastCall->debugSelector != -1) {
+ curMethodName = g_sci->getKernel()->getSelectorName(lastCall->debugSelector);
+ } else if (lastCall->debugExportId != -1) {
+ curObjectName = "";
+ curMethodName = curMethodName.printf("export %d", lastCall->debugExportId);
+ }
+ }
+
+ if (workaroundList) {
+ // Search if there is a workaround for this one
+ const SciWorkaroundEntry *workaround;
+ int16 inheritanceLevel = 0;
+ Common::String searchObjectName = curObjectName;
+ reg_t searchObject = lastCall->sendp;
+ do {
+ workaround = workaroundList;
+ while (workaround->methodName) {
+ if (workaround->gameId == gameId
+ && ((workaround->scriptNr == -1) || (workaround->scriptNr == curScriptNr))
+ && ((workaround->roomNr == -1) || (workaround->roomNr == curRoomNumber))
+ && ((workaround->inheritanceLevel == -1) || (workaround->inheritanceLevel == inheritanceLevel))
+ && ((workaround->objectName == NULL) || (workaround->objectName == searchObjectName))
+ && workaround->methodName == curMethodName && workaround->localCallOffset == lastCall->debugLocalCallOffset
+ && ((workaround->index == -1) || (workaround->index == index))) {
+ // Workaround found
+ return workaround->newValue;
+ }
+ workaround++;
+ }
+
+ // Go back to the parent
+ inheritanceLevel++;
+ searchObject = state->_segMan->getObject(searchObject)->getSuperClassSelector();
+ if (!searchObject.isNull())
+ searchObjectName = state->_segMan->getObjectName(searchObject);
+ } while (!searchObject.isNull()); // no parent left?
+ }
+
+ // give caller origin data
+ trackOrigin->objectName = curObjectName;
+ trackOrigin->methodName = curMethodName;
+ trackOrigin->scriptNr = curScriptNr;
+ trackOrigin->localCallOffset = lastCall->debugLocalCallOffset;
+
+ SciWorkaroundSolution noneFound;
+ noneFound.type = WORKAROUND_NONE;
+ noneFound.value = 0;
+ return noneFound;
+}
+
+} // End of namespace Sci
diff --git a/engines/sci/engine/workarounds.h b/engines/sci/engine/workarounds.h
new file mode 100644
index 0000000000..d509d300d7
--- /dev/null
+++ b/engines/sci/engine/workarounds.h
@@ -0,0 +1,100 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SCI_ENGINE_WORKAROUNDS_H
+#define SCI_ENGINE_WORKAROUNDS_H
+
+#include "sci/engine/vm_types.h"
+#include "sci/sci.h"
+
+namespace Sci {
+
+enum SciWorkaroundType {
+ WORKAROUND_NONE, // only used by terminator or when no workaround was found
+ WORKAROUND_IGNORE, // ignore kernel call
+ WORKAROUND_STILLCALL, // still do kernel call
+ WORKAROUND_FAKE // fake kernel call / replace temp value / fake opcode
+};
+
+struct SciTrackOriginReply {
+ int scriptNr;
+ Common::String objectName;
+ Common::String methodName;
+ int localCallOffset;
+};
+
+struct SciWorkaroundSolution {
+ SciWorkaroundType type;
+ uint16 value;
+};
+
+/**
+ * A structure describing a 'workaround' for a SCI script bug.
+ *
+ * Arrays of SciWorkaroundEntry instances are terminated by
+ * a fake entry in which "objectName" is NULL.
+ */
+struct SciWorkaroundEntry {
+ SciGameId gameId;
+ int roomNr;
+ int scriptNr;
+ int16 inheritanceLevel;
+ const char *objectName;
+ const char *methodName;
+ int localCallOffset;
+ int index;
+ SciWorkaroundSolution newValue;
+};
+
+extern const SciWorkaroundEntry opcodeDivWorkarounds[];
+extern const SciWorkaroundEntry opcodeOrWorkarounds[];
+extern const SciWorkaroundEntry opcodeDptoaWorkarounds[];
+extern const SciWorkaroundEntry uninitializedReadWorkarounds[];
+extern const SciWorkaroundEntry kAbs_workarounds[];
+extern const SciWorkaroundEntry kCelHigh_workarounds[];
+extern const SciWorkaroundEntry kCelWide_workarounds[];
+extern const SciWorkaroundEntry kDisplay_workarounds[];
+extern const SciWorkaroundEntry kDisposeScript_workarounds[];
+extern const SciWorkaroundEntry kDoSoundFade_workarounds[];
+extern const SciWorkaroundEntry kFindKey_workarounds[];
+extern const SciWorkaroundEntry kGetAngle_workarounds[];
+extern const SciWorkaroundEntry kGraphDrawLine_workarounds[];
+extern const SciWorkaroundEntry kGraphSaveBox_workarounds[];
+extern const SciWorkaroundEntry kGraphRestoreBox_workarounds[];
+extern const SciWorkaroundEntry kGraphFillBoxForeground_workarounds[];
+extern const SciWorkaroundEntry kGraphFillBoxAny_workarounds[];
+extern const SciWorkaroundEntry kGraphRedrawBox_workarounds[];
+extern const SciWorkaroundEntry kIsObject_workarounds[];
+extern const SciWorkaroundEntry kMemory_workarounds[];
+extern const SciWorkaroundEntry kNewWindow_workarounds[];
+extern const SciWorkaroundEntry kPaletteUnsetFlag_workarounds[];
+extern const SciWorkaroundEntry kSetPort_workarounds[];
+extern const SciWorkaroundEntry kUnLoad_workarounds[];
+
+extern SciWorkaroundSolution trackOriginAndFindWorkaround(int index, const SciWorkaroundEntry *workaroundList, SciTrackOriginReply *trackOrigin);
+
+} // End of namespace Sci
+
+#endif // SCI_ENGINE_WORKAROUNDS_H