diff options
Diffstat (limited to 'engines/sci/engine')
-rw-r--r-- | engines/sci/engine/kernel.cpp | 19 | ||||
-rw-r--r-- | engines/sci/engine/kernel.h | 7 | ||||
-rw-r--r-- | engines/sci/engine/kernel_tables.h | 95 | ||||
-rw-r--r-- | engines/sci/engine/kfile.cpp | 29 | ||||
-rw-r--r-- | engines/sci/engine/kgraphics.cpp | 372 | ||||
-rw-r--r-- | engines/sci/engine/klists.cpp | 10 | ||||
-rw-r--r-- | engines/sci/engine/kmath.cpp | 8 | ||||
-rw-r--r-- | engines/sci/engine/kmisc.cpp | 38 | ||||
-rw-r--r-- | engines/sci/engine/kmovement.cpp | 110 | ||||
-rw-r--r-- | engines/sci/engine/kstring.cpp | 57 | ||||
-rw-r--r-- | engines/sci/engine/kvideo.cpp | 94 | ||||
-rw-r--r-- | engines/sci/engine/object.cpp | 7 | ||||
-rw-r--r-- | engines/sci/engine/savegame.cpp | 15 | ||||
-rw-r--r-- | engines/sci/engine/script_patches.cpp | 18 | ||||
-rw-r--r-- | engines/sci/engine/scriptdebug.cpp | 5 | ||||
-rw-r--r-- | engines/sci/engine/seg_manager.cpp | 6 | ||||
-rw-r--r-- | engines/sci/engine/selector.cpp | 1 | ||||
-rw-r--r-- | engines/sci/engine/selector.h | 1 | ||||
-rw-r--r-- | engines/sci/engine/static_selectors.cpp | 53 | ||||
-rw-r--r-- | engines/sci/engine/vm.cpp | 12 | ||||
-rw-r--r-- | engines/sci/engine/workarounds.cpp | 12 |
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 |