aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/engine
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sci/engine')
-rw-r--r--engines/sci/engine/kernel.cpp19
-rw-r--r--engines/sci/engine/kernel.h7
-rw-r--r--engines/sci/engine/kernel_tables.h95
-rw-r--r--engines/sci/engine/kfile.cpp29
-rw-r--r--engines/sci/engine/kgraphics.cpp372
-rw-r--r--engines/sci/engine/klists.cpp10
-rw-r--r--engines/sci/engine/kmath.cpp8
-rw-r--r--engines/sci/engine/kmisc.cpp38
-rw-r--r--engines/sci/engine/kmovement.cpp110
-rw-r--r--engines/sci/engine/kstring.cpp57
-rw-r--r--engines/sci/engine/kvideo.cpp94
-rw-r--r--engines/sci/engine/object.cpp7
-rw-r--r--engines/sci/engine/savegame.cpp15
-rw-r--r--engines/sci/engine/script_patches.cpp18
-rw-r--r--engines/sci/engine/scriptdebug.cpp5
-rw-r--r--engines/sci/engine/seg_manager.cpp6
-rw-r--r--engines/sci/engine/selector.cpp1
-rw-r--r--engines/sci/engine/selector.h1
-rw-r--r--engines/sci/engine/static_selectors.cpp53
-rw-r--r--engines/sci/engine/vm.cpp12
-rw-r--r--engines/sci/engine/workarounds.cpp12
21 files changed, 593 insertions, 376 deletions
diff --git a/engines/sci/engine/kernel.cpp b/engines/sci/engine/kernel.cpp
index 8fb6322f55..6f783d79e8 100644
--- a/engines/sci/engine/kernel.cpp
+++ b/engines/sci/engine/kernel.cpp
@@ -488,8 +488,15 @@ bool Kernel::signatureMatch(const uint16 *sig, int argc, const reg_t *argv) {
if ((type & SIG_IS_INVALID) && (!(curSig & SIG_IS_INVALID)))
return false; // pointer is invalid and signature doesn't allow that?
- if (!((type & ~SIG_IS_INVALID) & curSig))
- return false; // type mismatch
+ if (!((type & ~SIG_IS_INVALID) & curSig)) {
+ if ((type & ~SIG_IS_INVALID) == SIG_TYPE_ERROR && (curSig & SIG_IS_INVALID)) {
+ // Type is unknown (error - usually because of a deallocated object or
+ // stale pointer) and the signature allows invalid pointers. In this case,
+ // ignore the invalid pointer.
+ } else {
+ return false; // type mismatch
+ }
+ }
if (!(curSig & SIG_MORE_MAY_FOLLOW)) {
sig++;
@@ -821,7 +828,8 @@ void Kernel::setDefaultKernelNames(GameFeatures *features) {
enum {
kKernelEntriesSci2 = 0x8b,
kKernelEntriesGk2Demo = 0xa0,
- kKernelEntriesSci21 = 0x9d
+ kKernelEntriesSci21 = 0x9d,
+ kKernelEntriesSci3 = 0xa1
};
void Kernel::setKernelNamesSci2() {
@@ -849,8 +857,11 @@ void Kernel::setKernelNamesSci21(GameFeatures *features) {
// OnMe is IsOnMe here, but they should be compatible
_kernelNames[0x23] = "Robot"; // Graph in SCI2
_kernelNames[0x2e] = "Priority"; // DisposeTextBitmap in SCI2
- } else
+ } else if (getSciVersion() != SCI_VERSION_3) {
_kernelNames = Common::StringArray(sci21_default_knames, kKernelEntriesSci21);
+ } else if (getSciVersion() == SCI_VERSION_3) {
+ _kernelNames = Common::StringArray(sci21_default_knames, kKernelEntriesSci3);
+ }
}
#endif
diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h
index ff3c67c84b..8cb294e166 100644
--- a/engines/sci/engine/kernel.h
+++ b/engines/sci/engine/kernel.h
@@ -442,11 +442,11 @@ reg_t kUpdateScreenItem(EngineState *s, int argc, reg_t *argv);
reg_t kDeleteScreenItem(EngineState *s, int argc, reg_t *argv);
// Text
reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv);
+reg_t kDisposeTextBitmap(EngineState *s, int argc, reg_t *argv);
// "Planes" in SCI32 are pictures
reg_t kAddPlane(EngineState *s, int argc, reg_t *argv);
reg_t kDeletePlane(EngineState *s, int argc, reg_t *argv);
reg_t kUpdatePlane(EngineState *s, int argc, reg_t *argv);
-reg_t kRepaintPlane(EngineState *s, int argc, reg_t *argv);
reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv);
reg_t kGetHighPlanePri(EngineState *s, int argc, reg_t *argv);
reg_t kFrameOut(EngineState *s, int argc, reg_t *argv);
@@ -456,6 +456,7 @@ reg_t kListEachElementDo(EngineState *s, int argc, reg_t *argv);
reg_t kListFirstTrue(EngineState *s, int argc, reg_t *argv);
reg_t kListAllTrue(EngineState *s, int argc, reg_t *argv);
reg_t kInPolygon(EngineState *s, int argc, reg_t *argv);
+reg_t kObjectIntersect(EngineState *s, int argc, reg_t *argv);
// SCI2.1 Kernel Functions
reg_t kText(EngineState *s, int argc, reg_t *argv);
@@ -476,6 +477,10 @@ reg_t kSetLanguage(EngineState *s, int argc, reg_t *argv);
reg_t kScrollWindow(EngineState *s, int argc, reg_t *argv);
reg_t kSetFontRes(EngineState *s, int argc, reg_t *argv);
reg_t kFont(EngineState *s, int argc, reg_t *argv);
+reg_t kBitmap(EngineState *s, int argc, reg_t *argv);
+
+// SCI3 Kernel functions
+reg_t kPlayDuck(EngineState *s, int argc, reg_t *argv);
#endif
reg_t kDoSoundInit(EngineState *s, int argc, reg_t *argv);
diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h
index 0c5d4e680d..afe6b176e5 100644
--- a/engines/sci/engine/kernel_tables.h
+++ b/engines/sci/engine/kernel_tables.h
@@ -477,6 +477,7 @@ static SciKernelMapEntry s_kernelMap[] = {
{ 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(DisposeTextBitmap), SIG_EVERYWHERE, "r", NULL, NULL },
{ MAP_CALL(FrameOut), SIG_EVERYWHERE, "", NULL, NULL },
{ MAP_CALL(GetHighPlanePri), SIG_EVERYWHERE, "", NULL, NULL },
{ MAP_CALL(InPolygon), SIG_EVERYWHERE, "iio", NULL, NULL },
@@ -493,25 +494,52 @@ static SciKernelMapEntry s_kernelMap[] = {
// our own memory manager and garbage collector, thus we simply call FlushResources, which in turn invokes
// our garbage collector (i.e. the SCI0-SCI1.1 semantics).
{ "Purge", kFlushResources, SIG_EVERYWHERE, "i", NULL, NULL },
- { MAP_CALL(RepaintPlane), SIG_EVERYWHERE, "o", NULL, NULL },
{ MAP_CALL(SetShowStyle), SIG_EVERYWHERE, "ioiiiii([ri])(i)", 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 },
+ { MAP_CALL(ObjectIntersect), SIG_EVERYWHERE, "oo", NULL, NULL },
// SCI2 unmapped functions - TODO!
- // SetScroll
- // AddMagnify // most probably similar to the SCI1.1 functions. We need a test case
- // DeleteMagnify
- // EditText
- // DisposeTextBitmap
- // VibrateMouse - used in QFG4 floppy
- // PalCycle
- // ObjectIntersect - used in QFG4 floppy
- // MakeSaveCatName - used in the Save/Load dialog of GK1CD (SRDialog, script 64990)
- // MakeSaveFileName - used in the Save/Load dialog of GK1CD (SRDialog, script 64990)
+
+ // SetScroll - called by script 64909, Styler::doit()
+ // PalCycle - called by Game::newRoom. Related to RemapColors.
+ // VibrateMouse - used in QFG4
+
+ // SCI2 Empty functions
+
+ // Debug function used to track resources
+ { MAP_EMPTY(ResourceTrack), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+
+ // SCI2 functions that are used in the original save/load menus. Marked as dummy, so
+ // that the engine errors out on purpose. TODO: Implement once the original save/load
+ // menus are implemented.
+
+ // Creates the name of the save catalogue/directory to save into.
+ // TODO: Implement once the original save/load menus are implemented.
+ { MAP_DUMMY(MakeSaveCatName), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+
+ // Creates the name of the save file to save into
+ // TODO: Implement once the original save/load menus are implemented.
+ { MAP_DUMMY(MakeSaveFileName), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+
+ // Used for edit boxes in save/load dialogs. It's a rewritten version of kEditControl,
+ // but it handles events on its own, using an internal loop, instead of using SCI
+ // scripts for event management like kEditControl does. Called by script 64914,
+ // DEdit::hilite().
+ // TODO: Implement once the original save/load menus are implemented.
+ { MAP_DUMMY(EditText), SIG_EVERYWHERE, "o", NULL, NULL },
// Unused / debug SCI2 unused functions, always mapped to kDummy
+
+ // AddMagnify/DeleteMagnify are both called by script 64979 (the Magnifier
+ // object) in GK1 only. There is also an associated empty magnifier view
+ // (view 1), however, it doesn't seem to be used anywhere, as all the
+ // magnifier closeups (e.g. in scene 470) are normal views. Thus, these
+ // are marked as dummy, so if they're ever used the engine will error out.
+ { MAP_DUMMY(AddMagnify), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(DeleteMagnify), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(RepaintPlane), SIG_EVERYWHERE, "o", NULL, NULL },
{ MAP_DUMMY(InspectObject), SIG_EVERYWHERE, "(.*)", NULL, NULL },
// Profiler (same as SCI0-SCI1.1)
// Record (same as SCI0-SCI1.1)
@@ -546,6 +574,7 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_CALL(ScrollWindow), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_CALL(SetFontRes), SIG_EVERYWHERE, "ii", NULL, NULL },
{ MAP_CALL(Font), SIG_EVERYWHERE, "i(.*)", NULL, NULL },
+ { MAP_CALL(Bitmap), SIG_EVERYWHERE, "(.*)", NULL, NULL },
// SCI2.1 Empty Functions
@@ -564,6 +593,9 @@ static SciKernelMapEntry s_kernelMap[] = {
// just use GetConfig and mark this one as empty, like the DOS version does.
{ MAP_EMPTY(GetSierraProfileInt), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ // Debug function called whenever the current room changes
+ { MAP_EMPTY(NewRoom), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+
// Unused / debug SCI2.1 unused functions, always mapped to kDummy
// The debug functions are called from the inbuilt debugger or polygon
@@ -586,23 +618,24 @@ static SciKernelMapEntry s_kernelMap[] = {
// SCI2.1 unmapped functions - TODO!
- // Bitmap
// MovePlaneItems - used by SQ6 to scroll through the inventory via the up/down buttons
// AddLine - used by Torin's Passage to highlight the chapter buttons
// DeleteLine - used by Torin's Passage to delete the highlight from the chapter buttons
// UpdateLine - used by LSL6
// SetPalStyleRange - 2 integer parameters, start and end. All styles from start-end
// (inclusive) are set to 0
- // NewRoom - 1 integer parameter, the current room number
// MorphOn - used by SQ6, script 900, the datacorder reprogramming puzzle (from room 270)
// SetHotRectangles - used by Phantasmagoria 1
+
+ // SCI3 Kernel Functions
+ { MAP_CALL(PlayDuck), SIG_EVERYWHERE, "(.*)", NULL, NULL },
#endif
{ NULL, NULL, SIG_EVERYWHERE, NULL, NULL, NULL }
};
/** Default kernel name table. */
-static const char *s_defaultKernelNames[] = {
+static const char *const s_defaultKernelNames[] = {
/*0x00*/ "Load",
/*0x01*/ "UnLoad",
/*0x02*/ "ScriptID",
@@ -751,7 +784,7 @@ static const char *s_defaultKernelNames[] = {
// 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[] = {
+static const char *const sci2_default_knames[] = {
/*0x00*/ "Load",
/*0x01*/ "UnLoad",
/*0x02*/ "ScriptID",
@@ -768,7 +801,7 @@ static const char *sci2_default_knames[] = {
/*0x0d*/ "CelWide",
/*0x0e*/ "CelHigh",
/*0x0f*/ "GetHighPlanePri",
- /*0x10*/ "GetHighItemPri",
+ /*0x10*/ "GetHighItemPri", // unused function
/*0x11*/ "ShakeScreen",
/*0x12*/ "OnMe",
/*0x13*/ "ShowMovie",
@@ -780,9 +813,9 @@ static const char *sci2_default_knames[] = {
/*0x19*/ "AddPlane",
/*0x1a*/ "DeletePlane",
/*0x1b*/ "UpdatePlane",
- /*0x1c*/ "RepaintPlane",
+ /*0x1c*/ "RepaintPlane", // unused function
/*0x1d*/ "SetShowStyle",
- /*0x1e*/ "ShowStylePercent",
+ /*0x1e*/ "ShowStylePercent", // unused function
/*0x1f*/ "SetScroll",
/*0x20*/ "AddMagnify",
/*0x21*/ "DeleteMagnify",
@@ -796,7 +829,7 @@ static const char *sci2_default_knames[] = {
/*0x29*/ "Dummy",
/*0x2a*/ "SetQuitStr",
/*0x2b*/ "EditText",
- /*0x2c*/ "InputText",
+ /*0x2c*/ "InputText", // unused function
/*0x2d*/ "CreateTextBitmap",
/*0x2e*/ "DisposeTextBitmap",
/*0x2f*/ "GetEvent",
@@ -916,7 +949,7 @@ static const char *sci2_default_knames[] = {
/*0x9f*/ "MessageBox"
};
-static const char *sci21_default_knames[] = {
+static const char *const sci21_default_knames[] = {
/*0x00*/ "Load",
/*0x01*/ "UnLoad",
/*0x02*/ "ScriptID",
@@ -941,8 +974,8 @@ static const char *sci21_default_knames[] = {
/*0x15*/ "NumLoops",
/*0x16*/ "NumCels",
/*0x17*/ "IsOnMe",
- /*0x18*/ "AddMagnify",
- /*0x19*/ "DeleteMagnify",
+ /*0x18*/ "AddMagnify", // dummy in SCI3
+ /*0x19*/ "DeleteMagnify", // dummy in SCI3
/*0x1a*/ "CelRect",
/*0x1b*/ "BaseLineSpan",
/*0x1c*/ "CelWide",
@@ -962,10 +995,10 @@ static const char *sci21_default_knames[] = {
/*0x2a*/ "UpdatePlane",
/*0x2b*/ "RepaintPlane",
/*0x2c*/ "GetHighPlanePri",
- /*0x2d*/ "GetHighItemPri",
+ /*0x2d*/ "GetHighItemPri", // unused function
/*0x2e*/ "SetShowStyle",
- /*0x2f*/ "ShowStylePercent",
- /*0x30*/ "SetScroll",
+ /*0x2f*/ "ShowStylePercent", // unused function
+ /*0x30*/ "SetScroll", // dummy in SCI3
/*0x31*/ "MovePlaneItems",
/*0x32*/ "ShakeScreen",
/*0x33*/ "Dummy",
@@ -974,7 +1007,7 @@ static const char *sci21_default_knames[] = {
/*0x36*/ "Dummy",
/*0x37*/ "IsHiRes",
/*0x38*/ "SetVideoMode",
- /*0x39*/ "ShowMovie",
+ /*0x39*/ "ShowMovie", // dummy in SCI3
/*0x3a*/ "Robot",
/*0x3b*/ "CreateTextBitmap",
/*0x3c*/ "Random",
@@ -992,7 +1025,7 @@ static const char *sci21_default_knames[] = {
/*0x48*/ "Message",
/*0x49*/ "Font",
/*0x4a*/ "EditText",
- /*0x4b*/ "InputText",
+ /*0x4b*/ "InputText", // unused function
/*0x4c*/ "ScrollWindow", // Dummy in SCI3
/*0x4d*/ "Dummy",
/*0x4e*/ "Dummy",
@@ -1017,9 +1050,9 @@ static const char *sci21_default_knames[] = {
/*0x61*/ "InitBresen",
/*0x62*/ "DoBresen",
/*0x63*/ "SetJump",
- /*0x64*/ "AvoidPath",
+ /*0x64*/ "AvoidPath", // dummy in SCI3
/*0x65*/ "InPolygon",
- /*0x66*/ "MergePoly",
+ /*0x66*/ "MergePoly", // dummy in SCI3
/*0x67*/ "ObjectIntersect",
/*0x68*/ "Dummy",
/*0x69*/ "MemoryInfo",
@@ -1048,7 +1081,7 @@ static const char *sci21_default_knames[] = {
/*0x80*/ "Dummy",
/*0x81*/ "Dummy", // called when changing rooms in most SCI2.1 games (e.g. KQ7, GK2, MUMG deluxe, Phant1)
/*0x82*/ "Dummy",
- /*0x83*/ "PrintDebug", // debug function, used by Shivers 2 (demo and full)
+ /*0x83*/ "PrintDebug", // debug function, used by Shivers (demo and full)
/*0x84*/ "Dummy",
/*0x85*/ "Dummy",
/*0x86*/ "Dummy",
@@ -1059,7 +1092,7 @@ static const char *sci21_default_knames[] = {
/*0x8b*/ "SetPalStyleRange",
/*0x8c*/ "AddPicAt",
/*0x8d*/ "MessageBox", // SCI3, was Dummy in SCI2.1
- /*0x8e*/ "NewRoom",
+ /*0x8e*/ "NewRoom", // debug function
/*0x8f*/ "Dummy",
/*0x90*/ "Priority",
/*0x91*/ "MorphOn",
diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp
index 1bd6754ca5..9e9441847d 100644
--- a/engines/sci/engine/kfile.cpp
+++ b/engines/sci/engine/kfile.cpp
@@ -365,9 +365,12 @@ reg_t kDeviceInfo(EngineState *s, int argc, reg_t *argv) {
reg_t kGetSaveDir(EngineState *s, int argc, reg_t *argv) {
#ifdef ENABLE_SCI32
- // TODO: SCI32 uses a parameter here.
- if (argc > 0)
- warning("kGetSaveDir called with %d parameter(s): %04x:%04x", argc, PRINT_REG(argv[0]));
+ // SCI32 uses a parameter here. It is used to modify a string, stored in a
+ // global variable, so that game scripts store the save directory. We
+ // don't really set a save game directory, thus not setting the string to
+ // anything is the correct thing to do here.
+ //if (argc > 0)
+ // warning("kGetSaveDir called with %d parameter(s): %04x:%04x", argc, PRINT_REG(argv[0]));
#endif
return s->_segMan->getSaveDirPtr();
}
@@ -578,11 +581,15 @@ reg_t kSaveGame(EngineState *s, int argc, reg_t *argv) {
game_description = dialog->getResultString();
if (game_description.empty()) {
// create our own description for the saved game, the user didnt enter it
+ #if defined(USE_SAVEGAME_TIMESTAMP)
TimeDate curTime;
g_system->getTimeAndDate(curTime);
curTime.tm_year += 1900; // fixup year
curTime.tm_mon++; // fixup month
- game_description = Common::String::format("%02d.%02d.%04d / %02d:%02d:%02d", curTime.tm_mday, curTime.tm_mon, curTime.tm_year, curTime.tm_hour, curTime.tm_min, curTime.tm_sec);
+ game_description = Common::String::format("%04d.%02d.%02d / %02d:%02d:%02d", curTime.tm_year, curTime.tm_mon, curTime.tm_mday, curTime.tm_hour, curTime.tm_min, curTime.tm_sec);
+ #else
+ game_description = Common::String::format("Save %d", savegameId + 1);
+ #endif
}
delete dialog;
g_sci->_soundCmd->pauseAll(false); // unpause music ( we can't have it paused during save)
@@ -1160,8 +1167,17 @@ reg_t kSave(EngineState *s, int argc, reg_t *argv) {
return kRestoreGame(s, argc - 1,argv + 1);
case 2:
return kGetSaveDir(s, argc - 1, argv + 1);
+ case 3:
+ return kCheckSaveGame(s, argc - 1, argv + 1);
case 5:
return kGetSaveFiles(s, argc - 1, argv + 1);
+ case 6:
+ // This is used in Shivers to delete saved games, however it
+ // always passes the same file name (SHIVER), so it doesn't
+ // actually delete anything...
+ // TODO: Check why this happens
+ // argv[1] is a string (most likely the save game directory)
+ return kFileIOUnlink(s, argc - 2, argv + 2);
case 8:
// TODO
// This is a timer callback, with 1 parameter: the timer object
@@ -1172,10 +1188,9 @@ reg_t kSave(EngineState *s, int argc, reg_t *argv) {
// 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());
+ kStub(s, argc, argv);
+ return NULL_REG;
}
-
- return NULL_REG;
}
#endif
diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp
index 36de767464..ea2b4816fc 100644
--- a/engines/sci/engine/kgraphics.cpp
+++ b/engines/sci/engine/kgraphics.cpp
@@ -49,8 +49,9 @@
#include "sci/graphics/text16.h"
#include "sci/graphics/view.h"
#ifdef ENABLE_SCI32
+#include "sci/graphics/font.h" // TODO: remove once kBitmap is moved in a separate class
+#include "sci/graphics/text32.h"
#include "sci/graphics/frameout.h"
-#include "sci/video/robot_decoder.h"
#endif
namespace Sci {
@@ -353,11 +354,9 @@ reg_t kTextSize(EngineState *s, int argc, reg_t *argv) {
textWidth = dest[3].toUint16(); textHeight = dest[2].toUint16();
#ifdef ENABLE_SCI32
- if (!g_sci->_gfxText16) {
- // TODO: Implement this
- textWidth = 0; textHeight = 0;
- warning("TODO: implement kTextSize for SCI32");
- } else
+ if (g_sci->_gfxText32)
+ g_sci->_gfxText32->kernelTextSize(g_sci->strSplit(text.c_str(), sep).c_str(), font_nr, maxwidth, &textWidth, &textHeight);
+ else
#endif
g_sci->_gfxText16->kernelTextSize(g_sci->strSplit(text.c_str(), sep).c_str(), font_nr, maxwidth, &textWidth, &textHeight);
@@ -380,8 +379,13 @@ reg_t kTextSize(EngineState *s, int argc, reg_t *argv) {
}
debugC(kDebugLevelStrings, "GetTextSize '%s' -> %dx%d", text.c_str(), textWidth, textHeight);
- dest[2] = make_reg(0, textHeight);
- dest[3] = make_reg(0, textWidth);
+ if (getSciVersion() <= SCI_VERSION_1_1) {
+ dest[2] = make_reg(0, textHeight);
+ dest[3] = make_reg(0, textWidth);
+ } else {
+ dest[2] = make_reg(0, textWidth);
+ dest[3] = make_reg(0, textHeight);
+ }
return s->r_acc;
}
@@ -1284,7 +1288,10 @@ reg_t kCantBeHere32(EngineState *s, int argc, reg_t *argv) {
}
reg_t kAddScreenItem(EngineState *s, int argc, reg_t *argv) {
- g_sci->_gfxFrameout->kernelAddScreenItem(argv[0]);
+ if (g_sci->_gfxFrameout->findScreenItem(argv[0]) == NULL)
+ g_sci->_gfxFrameout->kernelAddScreenItem(argv[0]);
+ else
+ g_sci->_gfxFrameout->kernelUpdateScreenItem(argv[0]);
return s->r_acc;
}
@@ -1313,18 +1320,13 @@ reg_t kUpdatePlane(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
-reg_t kRepaintPlane(EngineState *s, int argc, reg_t *argv) {
- g_sci->_gfxFrameout->kernelRepaintPlane(argv[0]);
- return s->r_acc;
-}
-
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
+ int16 pictureX = argv[2].toSint16();
+ int16 pictureY = argv[3].toSint16();
- g_sci->_gfxFrameout->kernelAddPicAt(planeObj, forWidth, pictureId);
+ g_sci->_gfxFrameout->kernelAddPicAt(planeObj, pictureId, pictureX, pictureY);
return s->r_acc;
}
@@ -1333,39 +1335,27 @@ reg_t kGetHighPlanePri(EngineState *s, int argc, reg_t *argv) {
}
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 kObjectIntersect(EngineState *s, int argc, reg_t *argv) {
+ Common::Rect objRect1 = g_sci->_gfxCompare->getNSRect(argv[0]);
+ Common::Rect objRect2 = g_sci->_gfxCompare->getNSRect(argv[1]);
+ return make_reg(0, objRect1.intersects(objRect2));
+}
+
// Tests if the coordinate is on the passed object
reg_t kIsOnMe(EngineState *s, int argc, reg_t *argv) {
uint16 x = argv[0].toUint16();
uint16 y = argv[1].toUint16();
reg_t targetObject = argv[2];
uint16 illegalBits = argv[3].offset;
- Common::Rect nsRect;
+ Common::Rect nsRect = g_sci->_gfxCompare->getNSRect(targetObject, true);
// 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);
+ bool contained = nsRect.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
@@ -1380,74 +1370,40 @@ reg_t kIsOnMe(EngineState *s, int argc, reg_t *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)));
- break;
+ reg_t object = argv[3];
+ Common::String text = s->_segMan->getString(readSelector(s->_segMan, object, SELECTOR(text)));
+ debugC(kDebugLevelStrings, "kCreateTextBitmap case 0 (%04x:%04x, %04x:%04x, %04x:%04x)",
+ PRINT_REG(argv[1]), PRINT_REG(argv[2]), PRINT_REG(argv[3]));
+ debugC(kDebugLevelStrings, "%s", text.c_str());
+ uint16 maxWidth = argv[1].toUint16(); // nsRight - nsLeft + 1
+ uint16 maxHeight = argv[2].toUint16(); // nsBottom - nsTop + 1
+ return g_sci->_gfxText32->createTextBitmap(object, maxWidth, maxHeight);
}
case 1: {
if (argc != 2) {
- warning("kCreateTextBitmap(0): expected 2 arguments, got %i", argc);
+ warning("kCreateTextBitmap(1): expected 2 arguments, got %i", argc);
return NULL_REG;
}
- //reg_t object = argv[1];
- //Common::String text = s->_segMan->getString(readSelector(s->_segMan, object, SELECTOR(text)));
- break;
+ reg_t object = argv[1];
+ Common::String text = s->_segMan->getString(readSelector(s->_segMan, object, SELECTOR(text)));
+ debugC(kDebugLevelStrings, "kCreateTextBitmap case 1 (%04x:%04x)", PRINT_REG(argv[1]));
+ debugC(kDebugLevelStrings, "%s", text.c_str());
+ return g_sci->_gfxText32->createTextBitmap(object);
}
default:
warning("CreateTextBitmap(%d)", argv[0].toUint16());
+ return NULL_REG;
}
-
- return NULL_REG;
}
-reg_t kRobot(EngineState *s, int argc, reg_t *argv) {
-
- int16 subop = argv[0].toUint16();
-
- switch (subop) {
- case 0: { // init
- int id = argv[1].toUint16();
- reg_t obj = argv[2];
- int16 flag = argv[3].toSint16();
- 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);
- g_sci->_robotDecoder->load(id);
- g_sci->_robotDecoder->setPos(x, y);
- }
- break;
- case 1: // LSL6 hires (startup)
- // TODO
- return NULL_REG; // an integer is expected
- case 4: { // start - we don't really have a use for this one
- //int id = argv[1].toUint16();
- //warning("kRobot(start), id %d", id);
- }
- break;
- case 7: // unknown, called e.g. by Phantasmagoria
- warning("kRobot(%d)", subop);
- break;
- case 8: // sync
- if ((uint32)g_sci->_robotDecoder->getCurFrame() != g_sci->_robotDecoder->getFrameCount() - 1) {
- writeSelector(s->_segMan, argv[1], SELECTOR(signal), NULL_REG);
- } else {
- g_sci->_robotDecoder->close();
- // Signal the engine scripts that the video is done
- writeSelector(s->_segMan, argv[1], SELECTOR(signal), SIGNAL_REG);
- }
- break;
- default:
- warning("kRobot(%d)", subop);
- break;
- }
-
+reg_t kDisposeTextBitmap(EngineState *s, int argc, reg_t *argv) {
+ g_sci->_gfxText32->disposeTextBitmap(argv[0]);
return s->r_acc;
}
@@ -1481,22 +1437,30 @@ reg_t kWinHelp(EngineState *s, int argc, reg_t *argv) {
}
/**
- * Used to programmatically mass set properties of the target plane
+ * Used for scene transitions, replacing (but reusing parts of) the old
+ * transition code.
*/
reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) {
- // TODO: This is all a stub/skeleton, thus we're invoking kStub() for now
- kStub(s, argc, argv);
-
// Can be called with 7 or 8 parameters
- // showStyle matches the style selector of the associated plane object
+ // The style defines which transition to perform. Related to the transition
+ // tables inside graphics/transitions.cpp
uint16 showStyle = argv[0].toUint16(); // 0 - 15
- reg_t planeObj = argv[1];
- //argv[2] // seconds
- //argv[3] // back
+ reg_t planeObj = argv[1]; // the affected plane
+ //argv[2] // seconds that the transition lasts
+ //argv[3] // back color to be used(?)
//int16 priority = argv[4].toSint16();
- //argv[5] // animate
+ //argv[5] // boolean, animate or not while the transition lasts
//argv[6] // refFrame
- //int16 unk7 = (argc >= 8) ? argv[7].toSint16() : 0; // divisions
+
+ // If the game has the pFadeArray selector, another parameter is used here,
+ // before the optional last parameter
+ /*bool hasFadeArray = g_sci->getKernel()->findSelector("pFadeArray") > 0;
+ if (hasFadeArray) {
+ // argv[7]
+ //int16 unk7 = (argc >= 9) ? argv[8].toSint16() : 0; // divisions (transition steps?)
+ } else {
+ //int16 unk7 = (argc >= 8) ? argv[7].toSint16() : 0; // divisions (transition steps?)
+ }*/
if (showStyle > 15) {
warning("kSetShowStyle: Illegal style %d for plane %04x:%04x", showStyle, PRINT_REG(planeObj));
@@ -1505,36 +1469,36 @@ reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) {
// TODO: Check if the plane is in the list of planes to draw
- return s->r_acc;
-}
-
-reg_t kCelInfo(EngineState *s, int argc, reg_t *argv) {
// TODO: This is all a stub/skeleton, thus we're invoking kStub() for now
kStub(s, argc, argv);
- // Used by Shivers 1, room 23601
-
- // 6 arguments, all integers:
- // argv[0] - subop (0 - 4). It's constantly called with 4 in Shivers 1
- // argv[1] - view (used with view 23602 in Shivers 1)
- // argv[2] - loop
- // argv[3] - cel
- // argv[4] - unknown (row?)
- // argv[5] - unknown (column?)
-
- // Subops:
- // 0 - return the view
- // 1 - return the loop
- // 2, 3 - nop
- // 4 - returns some kind of hash (?) based on the view and the two last params
-
- // This seems to be a debug function, but it could be used to check if
- // the jigsaw pieces "stick" together (they currently don't, unless I'm missing
- // something)
-
return s->r_acc;
}
+reg_t kCelInfo(EngineState *s, int argc, reg_t *argv) {
+ // Used by Shivers 1, room 23601 to determine what blocks on the red door puzzle board
+ // are occupied by pieces already
+
+ switch (argv[0].toUint16()) { // subops 0 - 4
+ // 0 - return the view
+ // 1 - return the loop
+ // 2, 3 - nop
+ case 4: {
+ GuiResourceId viewId = argv[1].toSint16();
+ int16 loopNo = argv[2].toSint16();
+ int16 celNo = argv[3].toSint16();
+ int16 x = argv[4].toUint16();
+ int16 y = argv[5].toUint16();
+ byte color = g_sci->_gfxCache->kernelViewGetColorAtCoordinate(viewId, loopNo, celNo, x, y);
+ return make_reg(0, color);
+ }
+ default: {
+ kStub(s, argc, argv);
+ return s->r_acc;
+ }
+ }
+}
+
reg_t kScrollWindow(EngineState *s, int argc, reg_t *argv) {
// Used by Phantasmagoria 1 and SQ6. In SQ6, it is used for the messages
// shown in the scroll window at the bottom of the screen.
@@ -1667,6 +1631,170 @@ reg_t kFont(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
+// TODO: Eventually, all of the kBitmap operations should be put
+// in a separate class
+
+#define BITMAP_HEADER_SIZE 46
+
+reg_t kBitmap(EngineState *s, int argc, reg_t *argv) {
+ // Used for bitmap operations in SCI2.1 and SCI3.
+ // This is the SCI2.1 version, the functionality seems to have changed in SCI3.
+
+ switch (argv[0].toUint16()) {
+ case 0: // init bitmap surface
+ {
+ // 6 params, called e.g. from TextView::init() in Torin's Passage,
+ // script 64890 and TransView::init() in script 64884
+ uint16 width = argv[1].toUint16();
+ uint16 height = argv[2].toUint16();
+ //uint16 skip = argv[3].toUint16();
+ uint16 back = argv[4].toUint16(); // usually equals skip
+ //uint16 width2 = (argc >= 6) ? argv[5].toUint16() : 0;
+ //uint16 height2 = (argc >= 7) ? argv[6].toUint16() : 0;
+ //uint16 transparentFlag = (argc >= 8) ? argv[7].toUint16() : 0;
+
+ // TODO: skip, width2, height2, transparentFlag
+ // (used for transparent bitmaps)
+ int entrySize = width * height + BITMAP_HEADER_SIZE;
+ reg_t memoryId = s->_segMan->allocateHunkEntry("Bitmap()", entrySize);
+ byte *memoryPtr = s->_segMan->getHunkPointer(memoryId);
+ memset(memoryPtr, 0, BITMAP_HEADER_SIZE); // zero out the bitmap header
+ memset(memoryPtr + BITMAP_HEADER_SIZE, back, width * height);
+ // Save totalWidth, totalHeight
+ // TODO: Save the whole bitmap header, like SSCI does
+ WRITE_LE_UINT16((void *)memoryPtr, width);
+ WRITE_LE_UINT16((void *)(memoryPtr + 2), height);
+ return memoryId;
+ }
+ break;
+ case 1: // dispose text bitmap surface
+ return kDisposeTextBitmap(s, argc - 1, argv + 1);
+ case 2: // dispose bitmap surface, with extra param
+ // 2 params, called e.g. from MenuItem::dispose in Torin's Passage,
+ // script 64893
+ warning("kBitmap(2), unk1 %d, bitmap ptr %04x:%04x", argv[1].toUint16(), PRINT_REG(argv[2]));
+ break;
+ case 3: // tiled surface
+ {
+ // 6 params, called e.g. from TiledBitmap::resize() in Torin's Passage,
+ // script 64869
+ reg_t hunkId = argv[1]; // obtained from kBitmap(0)
+ // The tiled view seems to always have 2 loops.
+ // These loops need to have 1 cel in loop 0 and 8 cels in loop 1.
+ uint16 viewNum = argv[2].toUint16(); // vTiles selector
+ uint16 loop = argv[3].toUint16();
+ uint16 cel = argv[4].toUint16();
+ uint16 x = argv[5].toUint16();
+ uint16 y = argv[6].toUint16();
+
+ byte *memoryPtr = s->_segMan->getHunkPointer(hunkId);
+ // Get totalWidth, totalHeight
+ uint16 totalWidth = READ_LE_UINT16((void *)memoryPtr);
+ uint16 totalHeight = READ_LE_UINT16((void *)(memoryPtr + 2));
+ byte *bitmap = memoryPtr + BITMAP_HEADER_SIZE;
+
+ GfxView *view = g_sci->_gfxCache->getView(viewNum);
+ uint16 tileWidth = view->getWidth(loop, cel);
+ uint16 tileHeight = view->getHeight(loop, cel);
+ const byte *tileBitmap = view->getBitmap(loop, cel);
+ uint16 width = MIN<uint16>(totalWidth - x, tileWidth);
+ uint16 height = MIN<uint16>(totalHeight - y, tileHeight);
+
+ for (uint16 curY = 0; curY < height; curY++) {
+ for (uint16 curX = 0; curX < width; curX++) {
+ bitmap[(curY + y) * totalWidth + (curX + x)] = tileBitmap[curY * tileWidth + curX];
+ }
+ }
+
+ }
+ break;
+ case 4: // add text to bitmap
+ {
+ // 13 params, called e.g. from TextButton::createBitmap() in Torin's Passage,
+ // script 64894
+ reg_t hunkId = argv[1]; // obtained from kBitmap(0)
+ Common::String text = s->_segMan->getString(argv[2]);
+ uint16 textX = argv[3].toUint16();
+ uint16 textY = argv[4].toUint16();
+ //reg_t unk5 = argv[5];
+ //reg_t unk6 = argv[6];
+ //reg_t unk7 = argv[7]; // skip?
+ //reg_t unk8 = argv[8]; // back?
+ //reg_t unk9 = argv[9];
+ uint16 fontId = argv[10].toUint16();
+ //uint16 mode = argv[11].toUint16();
+ uint16 dimmed = argv[12].toUint16();
+ //warning("kBitmap(4): bitmap ptr %04x:%04x, font %d, mode %d, dimmed %d - text: \"%s\"",
+ // PRINT_REG(bitmapPtr), font, mode, dimmed, text.c_str());
+ uint16 foreColor = 255; // TODO
+
+ byte *memoryPtr = s->_segMan->getHunkPointer(hunkId);
+ // Get totalWidth, totalHeight
+ uint16 totalWidth = READ_LE_UINT16((void *)memoryPtr);
+ uint16 totalHeight = READ_LE_UINT16((void *)(memoryPtr + 2));
+ byte *bitmap = memoryPtr + BITMAP_HEADER_SIZE;
+
+ GfxFont *font = g_sci->_gfxCache->getFont(fontId);
+
+ int16 charCount = 0;
+ uint16 curX = textX, curY = textY;
+ const char *txt = text.c_str();
+
+ while (*txt) {
+ charCount = g_sci->_gfxText32->GetLongest(txt, totalWidth, font);
+ if (charCount == 0)
+ break;
+
+ for (int i = 0; i < charCount; i++) {
+ unsigned char curChar = txt[i];
+ font->drawToBuffer(curChar, curY, curX, foreColor, dimmed, bitmap, totalWidth, totalHeight);
+ curX += font->getCharWidth(curChar);
+ }
+
+ curX = textX;
+ curY += font->getHeight();
+ txt += charCount;
+ while (*txt == ' ')
+ txt++; // skip over breaking spaces
+ }
+
+ }
+ break;
+ case 5: // fill with color
+ {
+ // 6 params, called e.g. from TextView::init() and TextView::draw()
+ // in Torin's Passage, script 64890
+ reg_t hunkId = argv[1]; // obtained from kBitmap(0)
+ uint16 x = argv[2].toUint16();
+ uint16 y = argv[3].toUint16();
+ uint16 fillWidth = argv[4].toUint16(); // width - 1
+ uint16 fillHeight = argv[5].toUint16(); // height - 1
+ uint16 back = argv[6].toUint16();
+
+ byte *memoryPtr = s->_segMan->getHunkPointer(hunkId);
+ // Get totalWidth, totalHeight
+ uint16 totalWidth = READ_LE_UINT16((void *)memoryPtr);
+ uint16 totalHeight = READ_LE_UINT16((void *)(memoryPtr + 2));
+ uint16 width = MIN<uint16>(totalWidth - x, fillWidth);
+ uint16 height = MIN<uint16>(totalHeight - y, fillHeight);
+ byte *bitmap = memoryPtr + BITMAP_HEADER_SIZE;
+
+ for (uint16 curY = 0; curY < height; curY++) {
+ for (uint16 curX = 0; curX < width; curX++) {
+ bitmap[(curY + y) * totalWidth + (curX + x)] = back;
+ }
+ }
+
+ }
+ break;
+ default:
+ kStub(s, argc, argv);
+ break;
+ }
+
+ return s->r_acc;
+}
+
#endif
} // End of namespace Sci
diff --git a/engines/sci/engine/klists.cpp b/engines/sci/engine/klists.cpp
index 68469f5c9a..8500f08211 100644
--- a/engines/sci/engine/klists.cpp
+++ b/engines/sci/engine/klists.cpp
@@ -691,6 +691,16 @@ reg_t kArray(EngineState *s, int argc, reg_t *argv) {
}
case 2: { // At (return value at an index)
SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]);
+ if (g_sci->getGameId() == GID_PHANTASMAGORIA2) {
+ // HACK: Phantasmagoria 2 keeps trying to access past the end of an
+ // array when it starts. I'm assuming it's trying to see where the
+ // array ends, or tries to resize it. Adjust the array size
+ // accordingly, and return NULL for now.
+ if (array->getSize() == argv[2].toUint16()) {
+ array->setSize(argv[2].toUint16());
+ return NULL_REG;
+ }
+ }
return array->getValue(argv[2].toUint16());
}
case 3: { // Atput (put value at an index)
diff --git a/engines/sci/engine/kmath.cpp b/engines/sci/engine/kmath.cpp
index ef795d7e2f..7570856dff 100644
--- a/engines/sci/engine/kmath.cpp
+++ b/engines/sci/engine/kmath.cpp
@@ -37,6 +37,14 @@ reg_t kRandom(EngineState *s, int argc, reg_t *argv) {
// some codes in sq4 are also random and 5 digit (if i remember correctly)
const uint16 fromNumber = argv[0].toUint16();
const uint16 toNumber = argv[1].toUint16();
+ // Some scripts may request a range in the reverse order (from largest
+ // to smallest). An example can be found in Longbow, room 710, where a
+ // random number is requested from 119 to 83. In this case, we're
+ // supposed to return toNumber (determined by the KQ5CD disasm).
+ // Fixes bug #3413020.
+ if (fromNumber > toNumber)
+ return make_reg(0, toNumber);
+
uint16 range = toNumber - fromNumber + 1;
// calculating range is exactly how sierra sci did it and is required for hoyle 4
// where we get called with kRandom(0, -1) and we are supposed to give back values from 0 to 0
diff --git a/engines/sci/engine/kmisc.cpp b/engines/sci/engine/kmisc.cpp
index e6837242e4..a32480c168 100644
--- a/engines/sci/engine/kmisc.cpp
+++ b/engines/sci/engine/kmisc.cpp
@@ -54,8 +54,33 @@ reg_t kGameIsRestarting(EngineState *s, int argc, reg_t *argv) {
uint32 neededSleep = 30;
- // WORKAROUNDS:
+ // WORKAROUNDS for scripts that are polling too quickly in scenes that
+ // are not animating much
switch (g_sci->getGameId()) {
+ case GID_CASTLEBRAIN:
+ // In Castle of Dr. Brain, memory color matching puzzle in the first
+ // room (room 100), the game scripts constantly poll the state of each
+ // stone when the user clicks on one. Since the scene is not animating
+ // much, this results in activating and deactivating each stone very
+ // quickly (together with its associated tone sound), depending on how
+ // low it is in the animate list. This worked somewhat in older PCs, but
+ // not in modern computers. We throttle the scene in order to allow the
+ // stones to display, otherwise the game scripts reset them too soon.
+ // Fixes bug #3127824.
+ if (s->currentRoomNumber() == 100) {
+ s->_throttleTrigger = true;
+ neededSleep = 60;
+ }
+ break;
+ case GID_ICEMAN:
+ // In ICEMAN the submarine control room is not animating much, so it
+ // runs way too fast. We calm it down even more, otherwise fighting
+ // against other submarines is almost impossible.
+ if (s->currentRoomNumber() == 27) {
+ s->_throttleTrigger = true;
+ neededSleep = 60;
+ }
+ break;
case GID_LSL3:
// LSL3 calculates a machinespeed variable during game startup
// (right after the filthy questions). This one would go through w/o
@@ -65,21 +90,12 @@ reg_t kGameIsRestarting(EngineState *s, int argc, reg_t *argv) {
if (s->currentRoomNumber() == 290)
s->_throttleTrigger = true;
break;
- case GID_ICEMAN:
- // In ICEMAN the submarine control room is not animating much, so it runs way too fast
- // we calm it down even more otherwise especially fighting against other submarines
- // is almost impossible
- if (s->currentRoomNumber() == 27) {
- s->_throttleTrigger = true;
- neededSleep = 60;
- }
- break;
case GID_SQ4:
// In SQ4 (floppy and CD) the sequel police appear way too quickly in
// the Skate-o-rama rooms, resulting in all sorts of timer issues, like
// #3109139 (which occurs because a police officer instantly teleports
// just before Roger exits and shoots him). We throttle these scenes a
- // bit more, in order to prevent timer bugs related to the sequel police
+ // bit more, in order to prevent timer bugs related to the sequel police.
if (s->currentRoomNumber() == 405 || s->currentRoomNumber() == 406 ||
s->currentRoomNumber() == 410 || s->currentRoomNumber() == 411) {
s->_throttleTrigger = true;
diff --git a/engines/sci/engine/kmovement.cpp b/engines/sci/engine/kmovement.cpp
index 14f7db47a0..649a1428a0 100644
--- a/engines/sci/engine/kmovement.cpp
+++ b/engines/sci/engine/kmovement.cpp
@@ -505,116 +505,6 @@ reg_t kDoAvoider(EngineState *s, int argc, reg_t *argv) {
}
writeSelectorValue(segMan, avoider, SELECTOR(heading), avoiderHeading);
return s->r_acc;
-
-#if 0
- reg_t client, looper, mover;
- int angle;
- int dx, dy;
- int destx, desty;
-
- s->r_acc = SIGNAL_REG;
-
- if (!s->_segMan->isHeapObject(avoider)) {
- error("DoAvoider() where avoider %04x:%04x is not an object", PRINT_REG(avoider));
- return NULL_REG;
- }
-
- client = readSelector(segMan, avoider, SELECTOR(client));
-
- if (!s->_segMan->isHeapObject(client)) {
- error("DoAvoider() where client %04x:%04x is not an object", PRINT_REG(client));
- return NULL_REG;
- }
-
- looper = readSelector(segMan, client, SELECTOR(looper));
- mover = readSelector(segMan, client, SELECTOR(mover));
-
- if (!s->_segMan->isHeapObject(mover)) {
- if (mover.segment) {
- error("DoAvoider() where mover %04x:%04x is not an object", PRINT_REG(mover));
- }
- return s->r_acc;
- }
-
- destx = readSelectorValue(segMan, mover, SELECTOR(x));
- desty = readSelectorValue(segMan, mover, SELECTOR(y));
-
- debugC(kDebugLevelBresen, "Doing avoider %04x:%04x (dest=%d,%d)", PRINT_REG(avoider), destx, desty);
-
- invokeSelector(s, mover, SELECTOR(doit), argc, argv);
-
- mover = readSelector(segMan, client, SELECTOR(mover));
- if (!mover.segment) // Mover has been disposed?
- return s->r_acc; // Return gracefully.
-
- invokeSelector(s, client, SELECTOR(isBlocked), argc, argv);
-
- dx = destx - readSelectorValue(segMan, client, SELECTOR(x));
- dy = desty - readSelectorValue(segMan, client, SELECTOR(y));
- angle = getAngle(dx, dy);
-
- debugC(kDebugLevelBresen, "Movement (%d,%d), angle %d is %sblocked", dx, dy, angle, (s->r_acc.offset) ? " " : "not ");
-
- if (s->r_acc.offset) { // isBlocked() returned non-zero
- int rotation = (g_sci->getRNG().getRandomBit() == 1) ? 45 : (360 - 45); // Clockwise/counterclockwise
- int oldx = readSelectorValue(segMan, client, SELECTOR(x));
- int oldy = readSelectorValue(segMan, client, SELECTOR(y));
- int xstep = readSelectorValue(segMan, client, SELECTOR(xStep));
- int ystep = readSelectorValue(segMan, client, SELECTOR(yStep));
- int moves;
-
- debugC(kDebugLevelBresen, " avoider %04x:%04x", PRINT_REG(avoider));
-
- for (moves = 0; moves < 8; moves++) {
- int move_x = (int)(sin(angle * M_PI / 180.0) * (xstep));
- int move_y = (int)(-cos(angle * M_PI / 180.0) * (ystep));
-
- writeSelectorValue(segMan, client, SELECTOR(x), oldx + move_x);
- writeSelectorValue(segMan, client, SELECTOR(y), oldy + move_y);
-
- debugC(kDebugLevelBresen, "Pos (%d,%d): Trying angle %d; delta=(%d,%d)", oldx, oldy, angle, move_x, move_y);
-
- invokeSelector(s, client, SELECTOR(canBeHere), argc, argv);
-
- writeSelectorValue(segMan, client, SELECTOR(x), oldx);
- writeSelectorValue(segMan, client, SELECTOR(y), oldy);
-
- if (s->r_acc.offset) { // We can be here
- debugC(kDebugLevelBresen, "Success");
- writeSelectorValue(segMan, client, SELECTOR(heading), angle);
-
- return make_reg(0, angle);
- }
-
- angle += rotation;
-
- if (angle > 360)
- angle -= 360;
- }
-
- error("DoAvoider failed for avoider %04x:%04x", PRINT_REG(avoider));
- } else {
- int heading = readSelectorValue(segMan, client, SELECTOR(heading));
-
- if (heading == -1)
- return s->r_acc; // No change
-
- writeSelectorValue(segMan, client, SELECTOR(heading), angle);
-
- s->r_acc = make_reg(0, angle);
-
- if (looper.segment) {
- reg_t params[2] = { make_reg(0, angle), client };
- invokeSelector(s, looper, SELECTOR(doit), argc, argv, 2, params);
- return s->r_acc;
- } else {
- // No looper? Fall back to DirLoop
- kDirLoopWorker(client, (uint16)angle, s, argc, argv);
- }
- }
-
- return s->r_acc;
-#endif
}
} // End of namespace Sci
diff --git a/engines/sci/engine/kstring.cpp b/engines/sci/engine/kstring.cpp
index 07b87a7cbc..1a9359bb26 100644
--- a/engines/sci/engine/kstring.cpp
+++ b/engines/sci/engine/kstring.cpp
@@ -42,6 +42,14 @@ reg_t kStrCat(EngineState *s, int argc, reg_t *argv) {
Common::String s1 = s->_segMan->getString(argv[0]);
Common::String s2 = s->_segMan->getString(argv[1]);
+ // The Japanese version of PQ2 splits the two strings here
+ // (check bug #3396887).
+ if (g_sci->getGameId() == GID_PQ2 &&
+ g_sci->getLanguage() == Common::JA_JPN) {
+ s1 = g_sci->strSplit(s1.c_str(), NULL);
+ s2 = g_sci->strSplit(s2.c_str(), NULL);
+ }
+
s1 += s2;
s->_segMan->strcpy(argv[0], s1.c_str());
return argv[0];
@@ -193,8 +201,8 @@ reg_t kFormat(EngineState *s, int argc, reg_t *argv) {
char xfer;
int i;
int startarg;
- int str_leng = 0; /* Used for stuff like "%13s" */
- int unsigned_var = 0;
+ int strLength = 0; /* Used for stuff like "%13s" */
+ bool unsignedVar = false;
if (position.segment)
startarg = 2;
@@ -228,7 +236,7 @@ reg_t kFormat(EngineState *s, int argc, reg_t *argv) {
mode = 0;
} else {
mode = 1;
- str_leng = 0;
+ strLength = 0;
}
} else if (mode == 1) { /* xfer != '%' */
char fillchar = ' ';
@@ -238,32 +246,32 @@ reg_t kFormat(EngineState *s, int argc, reg_t *argv) {
/* int writelength; -- unused atm */
- if (xfer && (isdigit(xfer) || xfer == '-' || xfer == '=')) {
+ if (xfer && (isdigit(static_cast<unsigned char>(xfer)) || xfer == '-' || xfer == '=')) {
char *destp;
if (xfer == '0')
fillchar = '0';
else if (xfer == '=')
align = ALIGN_CENTER;
- else if (isdigit(xfer) || (xfer == '-'))
+ else if (isdigit(static_cast<unsigned char>(xfer)) || (xfer == '-'))
source--; // Go to start of length argument
- str_leng = strtol(source, &destp, 10);
+ strLength = strtol(source, &destp, 10);
if (destp > source)
source = destp;
- if (str_leng < 0) {
+ if (strLength < 0) {
align = ALIGN_LEFT;
- str_leng = -str_leng;
+ strLength = -strLength;
} else if (align != ALIGN_CENTER)
align = ALIGN_RIGHT;
xfer = *source++;
} else
- str_leng = 0;
+ strLength = 0;
- assert((target - targetbuf) + str_leng + 1 <= maxsize);
+ assert((target - targetbuf) + strLength + 1 <= maxsize);
switch (xfer) {
case 's': { /* Copy string */
@@ -278,7 +286,7 @@ reg_t kFormat(EngineState *s, int argc, reg_t *argv) {
Common::String tempsource = g_sci->getKernel()->lookupText(reg,
arguments[paramindex + 1]);
int slen = strlen(tempsource.c_str());
- int extralen = str_leng - slen;
+ int extralen = strLength - slen;
assert((target - targetbuf) + extralen <= maxsize);
if (extralen < 0)
extralen = 0;
@@ -334,18 +342,25 @@ reg_t kFormat(EngineState *s, int argc, reg_t *argv) {
case 'c': { /* insert character */
assert((target - targetbuf) + 2 <= maxsize);
if (align >= 0)
- while (str_leng-- > 1)
+ while (strLength-- > 1)
*target++ = ' '; /* Format into the text */
-
- *target++ = arguments[paramindex++];
+ char argchar = arguments[paramindex++];
+ if (argchar)
+ *target++ = argchar;
mode = 0;
}
break;
case 'x':
case 'u':
- unsigned_var = 1;
+ unsignedVar = true;
case 'd': { /* Copy decimal */
+ // In the new SCI2 kString function, %d is used for unsigned
+ // integers. An example is script 962 in Shivers - it uses %d
+ // to create file names.
+ if (getSciVersion() >= SCI_VERSION_2)
+ unsignedVar = true;
+
/* int templen; -- unused atm */
const char *format_string = "%d";
@@ -353,14 +368,14 @@ reg_t kFormat(EngineState *s, int argc, reg_t *argv) {
format_string = "%x";
int val = arguments[paramindex];
- if (!unsigned_var)
+ if (!unsignedVar)
val = (int16)arguments[paramindex];
target += sprintf(target, format_string, val);
paramindex++;
assert((target - targetbuf) <= maxsize);
- unsigned_var = 0;
+ unsignedVar = false;
mode = 0;
}
@@ -375,7 +390,7 @@ reg_t kFormat(EngineState *s, int argc, reg_t *argv) {
if (align) {
int written = target - writestart;
- int padding = str_leng - written;
+ int padding = strLength - written;
if (padding > 0) {
if (align > 0) {
@@ -730,6 +745,10 @@ reg_t kString(EngineState *s, int argc, reg_t *argv) {
case 8: { // Dup
const char *rawString = 0;
uint32 size = 0;
+ reg_t stringHandle;
+ // We allocate the new string first because if the StringTable needs to
+ // grow, our rawString pointer will be invalidated
+ SciString *dupString = s->_segMan->allocateString(&stringHandle);
if (argv[1].segment == s->_segMan->getStringSegmentId()) {
SciString *string = s->_segMan->lookupString(argv[1]);
@@ -741,8 +760,6 @@ reg_t kString(EngineState *s, int argc, reg_t *argv) {
size = string.size() + 1;
}
- reg_t stringHandle;
- SciString *dupString = s->_segMan->allocateString(&stringHandle);
dupString->setSize(size);
for (uint32 i = 0; i < size; i++)
diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp
index 6d810d516c..c9cf652013 100644
--- a/engines/sci/engine/kvideo.cpp
+++ b/engines/sci/engine/kvideo.cpp
@@ -21,6 +21,7 @@
*/
#include "engines/util.h"
+#include "sci/engine/kernel.h"
#include "sci/engine/state.h"
#include "sci/graphics/helpers.h"
#include "sci/graphics/cursor.h"
@@ -39,6 +40,7 @@
#include "sci/video/seq_decoder.h"
#ifdef ENABLE_SCI32
#include "video/coktel_decoder.h"
+#include "sci/video/robot_decoder.h"
#endif
namespace Sci {
@@ -230,6 +232,49 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {
#ifdef ENABLE_SCI32
+reg_t kRobot(EngineState *s, int argc, reg_t *argv) {
+ int16 subop = argv[0].toUint16();
+
+ switch (subop) {
+ case 0: { // init
+ int id = argv[1].toUint16();
+ reg_t obj = argv[2];
+ int16 flag = argv[3].toSint16();
+ 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);
+ g_sci->_robotDecoder->load(id);
+ g_sci->_robotDecoder->setPos(x, y);
+ }
+ break;
+ case 1: // LSL6 hires (startup)
+ // TODO
+ return NULL_REG; // an integer is expected
+ case 4: { // start - we don't really have a use for this one
+ //int id = argv[1].toUint16();
+ //warning("kRobot(start), id %d", id);
+ }
+ break;
+ case 7: // unknown, called e.g. by Phantasmagoria
+ warning("kRobot(%d)", subop);
+ break;
+ case 8: // sync
+ if ((uint32)g_sci->_robotDecoder->getCurFrame() != g_sci->_robotDecoder->getFrameCount() - 1) {
+ writeSelector(s->_segMan, argv[1], SELECTOR(signal), NULL_REG);
+ } else {
+ g_sci->_robotDecoder->close();
+ // Signal the engine scripts that the video is done
+ writeSelector(s->_segMan, argv[1], SELECTOR(signal), SIGNAL_REG);
+ }
+ break;
+ default:
+ warning("kRobot(%d)", subop);
+ break;
+ }
+
+ return s->r_acc;
+}
+
reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv) {
uint16 operation = argv[0].toUint16();
Video::VideoDecoder *videoDecoder = 0;
@@ -331,6 +376,55 @@ reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
+reg_t kPlayDuck(EngineState *s, int argc, reg_t *argv) {
+ uint16 operation = argv[0].toUint16();
+ Video::VideoDecoder *videoDecoder = 0;
+ bool reshowCursor = g_sci->_gfxCursor->isVisible();
+
+ switch (operation) {
+ case 1: // Play
+ // 6 params
+ s->_videoState.reset();
+ s->_videoState.fileName = Common::String::format("%d.duk", argv[1].toUint16());
+
+ videoDecoder = new Video::AviDecoder(g_system->getMixer());
+
+ if (!videoDecoder->loadFile(s->_videoState.fileName)) {
+ warning("Could not open Duck %s", s->_videoState.fileName.c_str());
+ break;
+ }
+
+ if (reshowCursor)
+ g_sci->_gfxCursor->kernelHide();
+
+ {
+ // Duck videos are 16bpp, so we need to change the active pixel format
+ int oldWidth = g_system->getWidth();
+ int oldHeight = g_system->getHeight();
+ Common::List<Graphics::PixelFormat> formats;
+ formats.push_back(videoDecoder->getPixelFormat());
+ initGraphics(640, 480, true, formats);
+
+ if (g_system->getScreenFormat().bytesPerPixel != videoDecoder->getPixelFormat().bytesPerPixel)
+ error("Could not switch screen format for the duck video");
+
+ playVideo(videoDecoder, s->_videoState);
+
+ // Switch back to 8bpp
+ initGraphics(oldWidth, oldHeight, oldWidth > 320);
+ }
+
+ if (reshowCursor)
+ g_sci->_gfxCursor->kernelShow();
+ break;
+ default:
+ kStub(s, argc, argv);
+ break;
+ }
+
+ return s->r_acc;
+}
+
#endif
} // End of namespace Sci
diff --git a/engines/sci/engine/object.cpp b/engines/sci/engine/object.cpp
index a1854a2723..78e216cdb5 100644
--- a/engines/sci/engine/object.cpp
+++ b/engines/sci/engine/object.cpp
@@ -202,9 +202,10 @@ bool Object::initBaseObject(SegManager *segMan, reg_t addr, bool doInitSuperClas
name = "<invalid name>";
}
- warning("Object %04x:%04x (name %s, script %d) varnum doesn't "
- "match baseObj's: obj %d, base %d", PRINT_REG(_pos),
- name, objScript, originalVarCount, baseObj->getVarCount());
+ debugC(kDebugLevelVM, "Object %04x:%04x (name %s, script %d) "
+ "varnum doesn't match baseObj's: obj %d, base %d",
+ PRINT_REG(_pos), name, objScript,
+ originalVarCount, baseObj->getVarCount());
#if 0
// We enumerate the methods selectors which could be hidden here
diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp
index e43c7097ed..030c0f3f54 100644
--- a/engines/sci/engine/savegame.cpp
+++ b/engines/sci/engine/savegame.cpp
@@ -545,8 +545,6 @@ void DataStack::saveLoadWithSerializer(Common::Serializer &s) {
void SciMusic::saveLoadWithSerializer(Common::Serializer &s) {
// Sync song lib data. When loading, the actual song lib will be initialized
// afterwards in gamestate_restore()
- Common::StackLock lock(_mutex);
-
int songcount = 0;
byte masterVolume = soundGetMasterVolume();
byte reverb = _pMidiDrv->getReverb();
@@ -576,9 +574,12 @@ void SciMusic::saveLoadWithSerializer(Common::Serializer &s) {
songcount = _playList.size();
s.syncAsUint32LE(songcount);
- if (s.isLoading()) {
+ if (s.isLoading())
clearPlayList();
+ Common::StackLock lock(_mutex);
+
+ if (s.isLoading()) {
for (int i = 0; i < songcount; i++) {
MusicEntry *curSong = new MusicEntry();
curSong->saveLoadWithSerializer(s);
@@ -626,12 +627,8 @@ void SoundCommandParser::reconstructPlayList() {
const MusicList::iterator end = _music->getPlayListEnd();
for (MusicList::iterator i = _music->getPlayListStart(); i != end; ++i) {
- if ((*i)->resourceId && _resMan->testResource(ResourceId(kResourceTypeSound, (*i)->resourceId))) {
- (*i)->soundRes = new SoundResource((*i)->resourceId, _resMan, _soundVersion);
- _music->soundInitSnd(*i);
- } else {
- (*i)->soundRes = 0;
- }
+ initSoundResource(*i);
+
if ((*i)->status == kSoundPlaying) {
// Sync the sound object's selectors related to playing with the stored
// ones in the playlist, as they may have been invalidated when loading.
diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp
index a714980a35..7efcb42f4b 100644
--- a/engines/sci/engine/script_patches.cpp
+++ b/engines/sci/engine/script_patches.cpp
@@ -855,26 +855,8 @@ const uint16 qfg3PatchImportDialog[] = {
PATCH_END
};
-// Script 23 in QFG3 has a typo/bug which makes it loop endlessly and
-// read garbage. Fixes bug #3040722.
-const byte qfg3DialogCrash[] = {
- 5,
- 0x34, 0xe7, 0x03, // ldi 3e7 (999)
- 0x22, // lt?
- 0x33, // jmp [back] ---> BUG! Infinite loop
- 0
-};
-
-const uint16 qfg3PatchDialogCrash[] = {
- 0x34, 0xe7, 0x03, // ldi 3e7 (999)
- 0x22, // lt?
- 0x31, // bnt [back]
- PATCH_END
-};
-
// script, description, magic DWORD, adjust
const SciScriptSignature qfg3Signatures[] = {
- { 23, "dialog crash", 1, PATCH_MAGICDWORD(0xe7, 0x03, 0x22, 0x33), -1, qfg3DialogCrash, qfg3PatchDialogCrash },
{ 944, "import dialog continuous calls", 1, PATCH_MAGICDWORD(0x2a, 0x31, 0x0b, 0x7a), -1, qfg3SignatureImportDialog, qfg3PatchImportDialog },
SCI_SIGNATUREENTRY_TERMINATOR
};
diff --git a/engines/sci/engine/scriptdebug.cpp b/engines/sci/engine/scriptdebug.cpp
index 3a18fbc68f..ad3f4fb788 100644
--- a/engines/sci/engine/scriptdebug.cpp
+++ b/engines/sci/engine/scriptdebug.cpp
@@ -31,6 +31,8 @@
namespace Sci {
+//#define VM_DEBUG_SEND
+
// This table is only used for debugging. Don't include it for devices
// with not enough available memory (e.g. phones), where REDUCE_MEMORY_USAGE
// is defined
@@ -618,12 +620,13 @@ void debugSelectorCall(reg_t send_obj, Selector selector, int argc, StackPtr arg
#ifdef VM_DEBUG_SEND
debugN("Send to %04x:%04x (%s), selector %04x (%s):", PRINT_REG(send_obj),
- s->_segMan->getObjectName(send_obj), selector,
+ segMan->getObjectName(send_obj), selector,
g_sci->getKernel()->getSelectorName(selector).c_str());
#endif // VM_DEBUG_SEND
switch (selectorType) {
case kSelectorNone:
+ debugN("\n");
break;
case kSelectorVariable:
#ifdef VM_DEBUG_SEND
diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp
index ab67da32db..1510af8508 100644
--- a/engines/sci/engine/seg_manager.cpp
+++ b/engines/sci/engine/seg_manager.cpp
@@ -28,12 +28,6 @@
namespace Sci {
-enum {
- DEFAULT_SCRIPTS = 32,
- DEFAULT_OBJECTS = 8, ///< default number of objects per script
- DEFAULT_OBJECTS_INCREMENT = 4 ///< Number of additional objects to instantiate if we're running out of them
-};
-
SegManager::SegManager(ResourceManager *resMan) {
_heap.push_back(0);
diff --git a/engines/sci/engine/selector.cpp b/engines/sci/engine/selector.cpp
index c2f857f319..a9aca9e22f 100644
--- a/engines/sci/engine/selector.cpp
+++ b/engines/sci/engine/selector.cpp
@@ -167,6 +167,7 @@ void Kernel::mapSelectors() {
#ifdef ENABLE_SCI32
FIND_SELECTOR(data);
FIND_SELECTOR(picture);
+ FIND_SELECTOR(bitmap);
FIND_SELECTOR(plane);
FIND_SELECTOR(top);
FIND_SELECTOR(left);
diff --git a/engines/sci/engine/selector.h b/engines/sci/engine/selector.h
index 085dd6e832..bbd86bb03e 100644
--- a/engines/sci/engine/selector.h
+++ b/engines/sci/engine/selector.h
@@ -132,6 +132,7 @@ struct SelectorCache {
#ifdef ENABLE_SCI32
Selector data; // Used by Array()/String()
Selector picture; // Used to hold the picture ID for SCI32 pictures
+ Selector bitmap; // Used to hold the text bitmap for SCI32 texts
Selector plane;
Selector top;
diff --git a/engines/sci/engine/static_selectors.cpp b/engines/sci/engine/static_selectors.cpp
index cca4c47be8..74d2851024 100644
--- a/engines/sci/engine/static_selectors.cpp
+++ b/engines/sci/engine/static_selectors.cpp
@@ -102,18 +102,28 @@ static const char * const sci2Selectors[] = {
#endif
static const SelectorRemap sciSelectorRemap[] = {
- { SCI_VERSION_0_EARLY, SCI_VERSION_0_LATE, "moveDone", 170 },
- { SCI_VERSION_0_EARLY, SCI_VERSION_0_LATE, "points", 316 },
- { SCI_VERSION_0_EARLY, SCI_VERSION_0_LATE, "flags", 368 },
- { SCI_VERSION_1_EARLY, SCI_VERSION_1_LATE, "nodePtr", 44 },
- { SCI_VERSION_1_LATE, SCI_VERSION_1_LATE, "cantBeHere", 57 },
- { SCI_VERSION_1_EARLY, SCI_VERSION_1_LATE, "topString", 101 },
- { SCI_VERSION_1_EARLY, SCI_VERSION_1_LATE, "flags", 102 },
+ { SCI_VERSION_0_EARLY, SCI_VERSION_0_LATE, "moveDone", 170 },
+ { SCI_VERSION_0_EARLY, SCI_VERSION_0_LATE, "points", 316 },
+ { SCI_VERSION_0_EARLY, SCI_VERSION_0_LATE, "flags", 368 },
+ { SCI_VERSION_1_EARLY, SCI_VERSION_1_LATE, "nodePtr", 44 },
+ { SCI_VERSION_1_LATE, SCI_VERSION_1_LATE, "cantBeHere", 57 },
+ { SCI_VERSION_1_EARLY, SCI_VERSION_1_LATE, "topString", 101 },
+ { SCI_VERSION_1_EARLY, SCI_VERSION_1_LATE, "flags", 102 },
// SCI1.1
- { SCI_VERSION_1_1, SCI_VERSION_1_1, "nodePtr", 41 },
- { SCI_VERSION_1_1, SCI_VERSION_1_1, "cantBeHere", 54 },
- { SCI_VERSION_1_1, SCI_VERSION_2_1, "-info-",4103 },
- { SCI_VERSION_NONE, SCI_VERSION_NONE, 0, 0 }
+ { SCI_VERSION_1_1, SCI_VERSION_1_1, "nodePtr", 41 },
+ { SCI_VERSION_1_1, SCI_VERSION_1_1, "cantBeHere", 54 },
+ // The following are not really needed. They've only been defined to
+ // ease game debugging.
+ { SCI_VERSION_1_1, SCI_VERSION_2_1, "-objID-", 4096 },
+ { SCI_VERSION_1_1, SCI_VERSION_2_1, "-size-", 4097 },
+ { SCI_VERSION_1_1, SCI_VERSION_2_1, "-propDict-", 4098 },
+ { SCI_VERSION_1_1, SCI_VERSION_2_1, "-methDict-", 4099 },
+ { SCI_VERSION_1_1, SCI_VERSION_2_1, "-classScript-", 4100 },
+ { SCI_VERSION_1_1, SCI_VERSION_2_1, "-script-", 4101 },
+ { SCI_VERSION_1_1, SCI_VERSION_2_1, "-super-", 4102 },
+ //
+ { SCI_VERSION_1_1, SCI_VERSION_2_1, "-info-", 4103 },
+ { SCI_VERSION_NONE, SCI_VERSION_NONE, 0, 0 }
};
struct ClassReference {
@@ -182,9 +192,6 @@ Common::StringArray Kernel::checkStaticSelectorNames() {
for (int i = count + countSci1; i < count + countSci1 + countSci11; i++)
names[i] = sci11Selectors[i - count - countSci1];
}
-
- findSpecificSelectors(names);
-
#ifdef ENABLE_SCI32
} else {
// SCI2+
@@ -193,6 +200,8 @@ Common::StringArray Kernel::checkStaticSelectorNames() {
#endif
}
+ findSpecificSelectors(names);
+
for (const SelectorRemap *selectorRemap = sciSelectorRemap; selectorRemap->slot; ++selectorRemap) {
if (getSciVersion() >= selectorRemap->minVersion && getSciVersion() <= selectorRemap->maxVersion) {
const uint32 slot = selectorRemap->slot;
@@ -213,13 +222,16 @@ void Kernel::findSpecificSelectors(Common::StringArray &selectorNames) {
// We need to initialize script 0 here, to make sure that it's always
// located at segment 1.
_segMan->instantiateScript(0);
+ uint16 sci2Offset = (getSciVersion() >= SCI_VERSION_2) ? 64000 : 0;
// The Actor class contains the init, xLast and yLast selectors, which
// we reference directly. It's always in script 998, so we need to
// explicitly load it here.
if ((getSciVersion() >= SCI_VERSION_1_EGA_ONLY)) {
- if (_resMan->testResource(ResourceId(kResourceTypeScript, 998))) {
- _segMan->instantiateScript(998);
+ uint16 actorScript = 998;
+
+ if (_resMan->testResource(ResourceId(kResourceTypeScript, actorScript + sci2Offset))) {
+ _segMan->instantiateScript(actorScript + sci2Offset);
const Object *actorClass = _segMan->getObject(_segMan->findObjectByName("Actor"));
@@ -227,9 +239,10 @@ void Kernel::findSpecificSelectors(Common::StringArray &selectorNames) {
// Find the xLast and yLast selectors, used in kDoBresen
const int offset = (getSciVersion() < SCI_VERSION_1_1) ? 3 : 0;
+ const int offset2 = (getSciVersion() >= SCI_VERSION_2) ? 12 : 0;
// xLast and yLast always come between illegalBits and xStep
- int illegalBitsSelectorPos = actorClass->locateVarSelector(_segMan, 15 + offset); // illegalBits
- int xStepSelectorPos = actorClass->locateVarSelector(_segMan, 51 + offset); // xStep
+ int illegalBitsSelectorPos = actorClass->locateVarSelector(_segMan, 15 + offset + offset2); // illegalBits
+ int xStepSelectorPos = actorClass->locateVarSelector(_segMan, 51 + offset + offset2); // xStep
if (xStepSelectorPos - illegalBitsSelectorPos != 3) {
error("illegalBits and xStep selectors aren't found in "
"known locations. illegalBits = %d, xStep = %d",
@@ -253,10 +266,10 @@ void Kernel::findSpecificSelectors(Common::StringArray &selectorNames) {
// Find selectors from specific classes
for (int i = 0; i < ARRAYSIZE(classReferences); i++) {
- if (!_resMan->testResource(ResourceId(kResourceTypeScript, classReferences[i].script)))
+ if (!_resMan->testResource(ResourceId(kResourceTypeScript, classReferences[i].script + sci2Offset)))
continue;
- _segMan->instantiateScript(classReferences[i].script);
+ _segMan->instantiateScript(classReferences[i].script + sci2Offset);
const Object *targetClass = _segMan->getObject(_segMan->findObjectByName(classReferences[i].className));
int targetSelectorPos = 0;
diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp
index db682de16f..c94fdac034 100644
--- a/engines/sci/engine/vm.cpp
+++ b/engines/sci/engine/vm.cpp
@@ -41,7 +41,6 @@ namespace Sci {
const reg_t NULL_REG = {0, 0};
const reg_t SIGNAL_REG = {0, SIGNAL_OFFSET};
const reg_t TRUE_REG = {0, 1};
-//#define VM_DEBUG_SEND
// Enable the define below to have the VM abort on cases where a conditional
// statement is followed by an unconditional jump (which will most likely lead
// to an infinite loop). Aids in detecting script bugs such as #3040722.
@@ -233,11 +232,10 @@ ExecStack *execute_method(EngineState *s, uint16 script, uint16 pubfunct, StackP
if (!temp) {
#ifdef ENABLE_SCI32
- // HACK: Temporarily switch to a warning in SCI32 games until we can figure out why Torin has
- // an invalid exported function.
- if (getSciVersion() >= SCI_VERSION_2)
- warning("Request for invalid exported function 0x%x of script %d", pubfunct, script);
- else
+ if (g_sci->getGameId() == GID_TORIN && script == 64036) {
+ // Script 64036 in Torin's Passage is empty and contains an invalid
+ // (empty) export
+ } else
#endif
error("Request for invalid exported function 0x%x of script %d", pubfunct, script);
return NULL;
@@ -462,7 +460,7 @@ int readPMachineInstruction(const byte *src, byte &extOpcode, int16 opparams[4])
extOpcode = src[offset++]; // Get "extended" opcode (lower bit has special meaning)
const byte opcode = extOpcode >> 1; // get the actual opcode
- memset(opparams, 0, sizeof(opparams));
+ memset(opparams, 0, 4*sizeof(int16));
for (int i = 0; g_opcode_formats[opcode][i]; ++i) {
//debugN("Opcode: 0x%x, Opnumber: 0x%x, temp: %d\n", opcode, opcode, temp);
diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp
index 768ba28518..f68b74e1e0 100644
--- a/engines/sci/engine/workarounds.cpp
+++ b/engines/sci/engine/workarounds.cpp
@@ -77,6 +77,7 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = {
{ GID_HOYLE4, 700, 700, 1, "BridgeHand", "calcQTS", -1, 3, { WORKAROUND_FAKE, 0 } }, // sometimes when placing a bid in bridge
{ GID_HOYLE4, 300, 300, 0, "", "export 2", 0x1d4d, 0, { WORKAROUND_FAKE, 0 } }, // after passing around cards in hearts
{ GID_HOYLE4, 400, 400, 1, "GinHand", "calcRuns", -1, 4, { WORKAROUND_FAKE, 0 } }, // sometimes while playing Gin Rummy (e.g. when knocking and placing a card) - bug #3292334
+ { GID_HOYLE4, 500, 17, 1, "Character", "say", -1, 504, { WORKAROUND_FAKE, 0 } }, // sometimes while playing Cribbage (e.g. when the opponent says "Last Card") - bug #3292327
{ GID_ISLANDBRAIN, 100, 937, 0, "IconBar", "dispatchEvent", -1, 58, { WORKAROUND_FAKE, 0 } }, // when using ENTER at the startup menu - bug #3045225
{ 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
@@ -123,6 +124,7 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = {
{ GID_MOTHERGOOSEHIRES,-1,64950, 1, "View", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // see above
{ GID_PEPPER, -1, 894, 0, "Package", "doVerb", -1, 3, { WORKAROUND_FAKE, 0 } }, // using the hand on the book in the inventory - bug #3040012
{ GID_PEPPER, 150, 928, 0, "Narrator", "startText", -1, 0, { WORKAROUND_FAKE, 0 } }, // happens during the non-interactive demo of Pepper
+ { GID_PQ4, -1, 25, 0, "iconToggle", "select", -1, 1, { WORKAROUND_FAKE, 0 } }, // when toggling the icon bar to auto-hide or not
{ GID_PQSWAT, -1, 64950, 0, "View", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // Using the menu in the beginning
{ GID_QFG1, -1, 210, 0, "Encounter", "init", 0xbd0, 0, { WORKAROUND_FAKE, 0 } }, // hq1: going to the brigands hideout
{ GID_QFG1, -1, 210, 0, "Encounter", "init", 0xbe4, 0, { WORKAROUND_FAKE, 0 } }, // qfg1: going to the brigands hideout
@@ -153,6 +155,9 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = {
{ GID_SHIVERS, -1, 952, 0, "SoundManager", "stop", -1, 2, { WORKAROUND_FAKE, 0 } }, // Just after Sierra logo
{ GID_SHIVERS, -1, 64950, 0, "Feature", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // When clicking on the locked door at the beginning
{ GID_SHIVERS, -1, 64950, 0, "View", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // When clicking on the gargoyle eye at the beginning
+ { GID_SHIVERS, 20311, 64964, 0, "DPath", "init", -1, 1, { WORKAROUND_FAKE, 0 } }, // Just after door puzzle is solved and the metal balls start to roll
+ { GID_SHIVERS, 29260, 29260, 0, "spMars", "handleEvent", -1, 4, { WORKAROUND_FAKE, 0 } }, // When clicking mars after seeing fortune to align earth etc...
+ { GID_SHIVERS, 29260, 29260, 0, "spVenus", "handleEvent", -1, 4, { WORKAROUND_FAKE, 0 } }, // When clicking venus after seeing fortune to align earth etc...
{ GID_SQ1, 103, 103, 0, "hand", "internalEvent", -1, -1, { WORKAROUND_FAKE, 0 } }, // Spanish (and maybe early versions?) only: when moving cursor over input pad, temps 1 and 2
{ 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)
@@ -211,10 +216,6 @@ const SciWorkaroundEntry kDisplay_workarounds[] = {
{ GID_PQ2, 23, 23, 0, "rm23Script", "elements", 0x4ae, 0, { WORKAROUND_IGNORE, 0 } }, // when looking at the 2nd page of pate's file - 0x75 as id
{ GID_PQ2, 23, 23, 0, "rm23Script", "elements", 0x4c1, 0, { WORKAROUND_IGNORE, 0 } }, // when looking at the 2nd page of pate's file - 0x75 as id (another pq2 version, bug #3043904)
{ GID_QFG1, 11, 11, 0, "battle", "<noname90>", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: When entering battle, 0x75 as id
- { GID_QFG3, -1, 47, 0, "barterWin", "open", 0x1426, 0, { WORKAROUND_IGNORE, 0 } }, // sometimes when talking with a vendor that can be bartered with, the wrong local variable is checked and the variable contents are wrong - bug #3292251
- { GID_QFG3, -1, 47, 0, "barterIcon", "show", 0x135c, 0, { WORKAROUND_IGNORE, 0 } }, // sometimes when talking with a vendor that can be bartered with, the wrong local variable is checked and the variable contents are wrong - bug #3292251
- { GID_SQ1, -1, 700, 0, "arcadaRegion", "doit", -1, 0, { WORKAROUND_IGNORE, 0 } }, // restoring in some rooms of the arcada (right at the start)
- { GID_SQ1, 44, 44, 0, "spinDone", "changeState",0x13b0, 0, { WORKAROUND_IGNORE, 0 } }, // restoring a game at the slot machine in Ulence Flats (bug #3308087)
{ GID_SQ4, 397, 0, 0, "", "export 12", -1, 0, { WORKAROUND_IGNORE, 0 } }, // FLOPPY: when going into the computer store (bug #3044044)
{ GID_SQ4, 391, 391, 0, "doCatalog", "mode", 0x84, 0, { WORKAROUND_IGNORE, 0 } }, // CD: clicking on catalog in roboter sale - a parameter is an object
{ GID_SQ4, 391, 391, 0, "choosePlug", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // CD: ordering connector in roboter sale - a parameter is an object
@@ -283,7 +284,6 @@ const SciWorkaroundEntry kGraphSaveBox_workarounds[] = {
// gameID, room,script,lvl, object-name, method-name, call,index, workaround
const SciWorkaroundEntry kGraphRestoreBox_workarounds[] = {
{ GID_LSL6, -1, 86, 0, "LL6Inv", "hide", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens during the game, gets called with 1 extra parameter
- { GID_SQ5, 850, 850, 0, NULL, "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens while playing Battle Cruiser (invalid segment) - bug #3056811
SCI_WORKAROUNDENTRY_TERMINATOR
};
@@ -387,7 +387,7 @@ const SciWorkaroundEntry kStrLen_workarounds[] = {
const SciWorkaroundEntry kUnLoad_workarounds[] = {
{ GID_ECOQUEST, 380, 61, 0, "gotIt", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // CD version: after talking to the dolphin the first time, a 3rd parameter is passed by accident
{ GID_ECOQUEST, 380, 69, 0, "lookAtBlackBoard", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // German version, when closing the blackboard closeup in the dolphin room, a 3rd parameter is passed by accident - bug #3098353
- { 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, -1, -1, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during the intro, 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