aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/engine
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sci/engine')
-rw-r--r--engines/sci/engine/features.cpp9
-rw-r--r--engines/sci/engine/kernel.cpp26
-rw-r--r--engines/sci/engine/kernel.h1
-rw-r--r--engines/sci/engine/kernel_tables.h20
-rw-r--r--engines/sci/engine/kfile.cpp45
-rw-r--r--engines/sci/engine/kgraphics.cpp160
-rw-r--r--engines/sci/engine/klists.cpp20
-rw-r--r--engines/sci/engine/kpathing.cpp1
-rw-r--r--engines/sci/engine/kscripts.cpp10
-rw-r--r--engines/sci/engine/ksound.cpp7
-rw-r--r--engines/sci/engine/kstring.cpp34
-rw-r--r--engines/sci/engine/message.cpp2
-rw-r--r--engines/sci/engine/savegame.cpp32
-rw-r--r--engines/sci/engine/script.cpp27
-rw-r--r--engines/sci/engine/script.h18
-rw-r--r--engines/sci/engine/script_patches.cpp62
-rw-r--r--engines/sci/engine/scriptdebug.cpp16
-rw-r--r--engines/sci/engine/seg_manager.cpp34
-rw-r--r--engines/sci/engine/seg_manager.h7
-rw-r--r--engines/sci/engine/segment.cpp13
-rw-r--r--engines/sci/engine/segment.h2
-rw-r--r--engines/sci/engine/selector.cpp1
-rw-r--r--engines/sci/engine/selector.h1
-rw-r--r--engines/sci/engine/state.cpp8
-rw-r--r--engines/sci/engine/vm.cpp31
-rw-r--r--engines/sci/engine/vm.h22
-rw-r--r--engines/sci/engine/vm_types.h20
-rw-r--r--engines/sci/engine/workarounds.cpp2
28 files changed, 401 insertions, 230 deletions
diff --git a/engines/sci/engine/features.cpp b/engines/sci/engine/features.cpp
index a5c1b970f1..b3cfee873c 100644
--- a/engines/sci/engine/features.cpp
+++ b/engines/sci/engine/features.cpp
@@ -430,19 +430,16 @@ SciVersion GameFeatures::detectMessageFunctionType() {
return _messageFunctionType;
}
- Common::List<ResourceId> *resources = g_sci->getResMan()->listResources(kResourceTypeMessage, -1);
-
- if (resources->empty()) {
- delete resources;
+ Common::List<ResourceId> resources = g_sci->getResMan()->listResources(kResourceTypeMessage, -1);
+ if (resources.empty()) {
// No messages found, so this doesn't really matter anyway...
_messageFunctionType = SCI_VERSION_1_1;
return _messageFunctionType;
}
- Resource *res = g_sci->getResMan()->findResource(*resources->begin(), false);
+ Resource *res = g_sci->getResMan()->findResource(*resources.begin(), false);
assert(res);
- delete resources;
// Only v2 Message resources use the kGetMessage kernel function.
// v3-v5 use the kMessage kernel function.
diff --git a/engines/sci/engine/kernel.cpp b/engines/sci/engine/kernel.cpp
index 6f783d79e8..c99bc4fe47 100644
--- a/engines/sci/engine/kernel.cpp
+++ b/engines/sci/engine/kernel.cpp
@@ -914,28 +914,32 @@ Common::String Kernel::lookupText(reg_t address, int index) {
// TODO: script_adjust_opcode_formats should probably be part of the
// constructor (?) of a VirtualMachine or a ScriptManager class.
void script_adjust_opcode_formats() {
+
+ g_sci->_opcode_formats = new opcode_format[128][4];
+ memcpy(g_sci->_opcode_formats, g_base_opcode_formats, 128*4*sizeof(opcode_format));
+
if (g_sci->_features->detectLofsType() != SCI_VERSION_0_EARLY) {
- g_opcode_formats[op_lofsa][0] = Script_Offset;
- g_opcode_formats[op_lofss][0] = Script_Offset;
+ g_sci->_opcode_formats[op_lofsa][0] = Script_Offset;
+ g_sci->_opcode_formats[op_lofss][0] = Script_Offset;
}
#ifdef ENABLE_SCI32
// In SCI32, some arguments are now words instead of bytes
if (getSciVersion() >= SCI_VERSION_2) {
- g_opcode_formats[op_calle][2] = Script_Word;
- g_opcode_formats[op_callk][1] = Script_Word;
- g_opcode_formats[op_super][1] = Script_Word;
- g_opcode_formats[op_send][0] = Script_Word;
- g_opcode_formats[op_self][0] = Script_Word;
- g_opcode_formats[op_call][1] = Script_Word;
- g_opcode_formats[op_callb][1] = Script_Word;
+ g_sci->_opcode_formats[op_calle][2] = Script_Word;
+ g_sci->_opcode_formats[op_callk][1] = Script_Word;
+ g_sci->_opcode_formats[op_super][1] = Script_Word;
+ g_sci->_opcode_formats[op_send][0] = Script_Word;
+ g_sci->_opcode_formats[op_self][0] = Script_Word;
+ g_sci->_opcode_formats[op_call][1] = Script_Word;
+ g_sci->_opcode_formats[op_callb][1] = Script_Word;
}
if (getSciVersion() >= SCI_VERSION_3) {
// TODO: There are also opcodes in
// here to get the superclass, and possibly the species too.
- g_opcode_formats[0x4d/2][0] = Script_None;
- g_opcode_formats[0x4e/2][0] = Script_None;
+ g_sci->_opcode_formats[0x4d/2][0] = Script_None;
+ g_sci->_opcode_formats[0x4e/2][0] = Script_None;
}
#endif
}
diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h
index 8cb294e166..e549c1f8ae 100644
--- a/engines/sci/engine/kernel.h
+++ b/engines/sci/engine/kernel.h
@@ -457,6 +457,7 @@ 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);
+reg_t kEditText(EngineState *s, int argc, reg_t *argv);
// SCI2.1 Kernel Functions
reg_t kText(EngineState *s, int argc, reg_t *argv);
diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h
index afe6b176e5..622511c906 100644
--- a/engines/sci/engine/kernel_tables.h
+++ b/engines/sci/engine/kernel_tables.h
@@ -24,7 +24,7 @@
#define SCI_ENGINE_KERNEL_TABLES_H
#include "sci/engine/workarounds.h"
-#include "sci/engine/vm.h" // for opcode_formats
+#include "sci/engine/vm_types.h" // for opcode_formats
namespace Sci {
@@ -499,6 +499,7 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_CALL(UpdatePlane), SIG_EVERYWHERE, "o", NULL, NULL },
{ MAP_CALL(UpdateScreenItem), SIG_EVERYWHERE, "o", NULL, NULL },
{ MAP_CALL(ObjectIntersect), SIG_EVERYWHERE, "oo", NULL, NULL },
+ { MAP_CALL(EditText), SIG_EVERYWHERE, "o", NULL, NULL },
// SCI2 unmapped functions - TODO!
@@ -523,13 +524,6 @@ static SciKernelMapEntry s_kernelMap[] = {
// 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
@@ -616,6 +610,12 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_DUMMY(DeletePic), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_DUMMY(GetSierraProfileString), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ // Unused / debug functions in the in-between SCI2.1 interpreters
+ { MAP_DUMMY(PreloadResource), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(CheckCDisc), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(GetSaveCDisc), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_DUMMY(TestPoly), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+
// SCI2.1 unmapped functions - TODO!
// MovePlaneItems - used by SQ6 to scroll through the inventory via the up/down buttons
@@ -1116,7 +1116,9 @@ static const char *const sci21_default_knames[] = {
#endif
-opcode_format g_opcode_formats[128][4] = {
+// Base set of opcode formats. They're copied and adjusted slightly in
+// script_adjust_opcode_format depending on SCI version.
+static const opcode_format g_base_opcode_formats[128][4] = {
/*00*/
{Script_None}, {Script_None}, {Script_None}, {Script_None},
/*04*/
diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp
index 9e9441847d..312497720a 100644
--- a/engines/sci/engine/kfile.cpp
+++ b/engines/sci/engine/kfile.cpp
@@ -22,7 +22,9 @@
#include "common/archive.h"
#include "common/config-manager.h"
+#include "common/debug-channels.h"
#include "common/file.h"
+#include "common/macresman.h"
#include "common/str.h"
#include "common/savefile.h"
#include "common/system.h"
@@ -52,9 +54,10 @@ struct SavegameDesc {
* arbitrary data files, simply because many of our target platforms do not
* support this. The only files one can create are savestates. But SCI has an
* opcode to create and write to seemingly 'arbitrary' files. This is mainly
- * used in LSL3 for LARRY3.DRV (which is a game data file, not a driver) and
- * in LSL5 for MEMORY.DRV (which is again a game data file and contains the
- * game's password).
+ * used in LSL3 for LARRY3.DRV (which is a game data file, not a driver, used
+ * for persisting the results of the "age quiz" across restarts) and in LSL5
+ * for MEMORY.DRV (which is again a game data file and contains the game's
+ * password, XOR encrypted).
* To implement that opcode, we combine the SaveFileManager with regular file
* code, similarly to how the SCUMM HE engine does it.
*
@@ -115,20 +118,6 @@ reg_t file_open(EngineState *s, const Common::String &filename, int mode, bool u
if (!inFile)
inFile = SearchMan.createReadStreamForMember(englishName);
- // Special case for LSL3: It tries to create a new dummy file,
- // LARRY3.DRV. Apparently, if the file doesn't exist here, it should be
- // created. The game scripts then go ahead and fill its contents with
- // data. It seems to be a similar case as the dummy MEMORY.DRV file in
- // LSL5, but LSL5 creates the file if it can't find it with a separate
- // call to file_open().
- if (!inFile && englishName == "LARRY3.DRV") {
- outFile = saveFileMan->openForSaving(wrappedName);
- outFile->finalize();
- delete outFile;
- outFile = 0;
- inFile = SearchMan.createReadStreamForMember(wrappedName);
- }
-
if (!inFile)
debugC(kDebugLevelFile, " -> file_open(_K_FILE_MODE_OPEN_OR_FAIL): failed to open file '%s'", englishName.c_str());
} else if (mode == _K_FILE_MODE_CREATE) {
@@ -835,7 +824,7 @@ reg_t kFileIOWriteRaw(EngineState *s, int argc, reg_t *argv) {
int size = argv[2].toUint16();
char *buf = new char[size];
bool success = false;
- s->_segMan->memcpy((byte*)buf, argv[1], size);
+ s->_segMan->memcpy((byte *)buf, argv[1], size);
debugC(kDebugLevelFile, "kFileIO(writeRaw): %d, %d", handle, size);
FileHandle *f = getFileFromHandle(s, handle);
@@ -1060,6 +1049,18 @@ reg_t kFileIOExists(EngineState *s, int argc, reg_t *argv) {
exists = !saveFileMan->listSavefiles(wrappedName).empty();
}
+ // SCI2+ debug mode
+ if (DebugMan.isDebugChannelEnabled(kDebugLevelDebugMode)) {
+ if (!exists && name == "1.scr") // PQ4
+ exists = true;
+ if (!exists && name == "18.scr") // QFG4
+ exists = true;
+ if (!exists && name == "99.scr") // GK1, KQ7
+ exists = true;
+ if (!exists && name == "classes") // GK2, SQ6, LSL7
+ exists = true;
+ }
+
// Special case for non-English versions of LSL5: The English version of
// LSL5 calls kFileIO(), case K_FILEIO_OPEN for reading to check if
// memory.drv exists (which is where the game's password is stored). If
@@ -1080,6 +1081,14 @@ reg_t kFileIOExists(EngineState *s, int argc, reg_t *argv) {
delete outFile;
}
+ // Special case for KQ6 Mac: The game checks for two video files to see
+ // if they exist before it plays them. Since we support multiple naming
+ // schemes for resource fork files, we also need to support that here in
+ // case someone has a "HalfDome.bin" file, etc.
+ if (!exists && g_sci->getGameId() == GID_KQ6 && g_sci->getPlatform() == Common::kPlatformMacintosh &&
+ (name == "HalfDome" || name == "Kq6Movie"))
+ exists = Common::MacResManager::exists(name);
+
debugC(kDebugLevelFile, "kFileIO(fileExists) %s -> %d", name.c_str(), exists);
return make_reg(0, exists);
}
diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp
index ea2b4816fc..caae562d67 100644
--- a/engines/sci/engine/kgraphics.cpp
+++ b/engines/sci/engine/kgraphics.cpp
@@ -25,6 +25,7 @@
#include "engines/util.h"
#include "graphics/cursorman.h"
#include "graphics/surface.h"
+#include "graphics/palette.h" // temporary, for the fadeIn()/fadeOut() functions below
#include "gui/message.h"
@@ -39,7 +40,7 @@
#include "sci/graphics/animate.h"
#include "sci/graphics/cache.h"
#include "sci/graphics/compare.h"
-#include "sci/graphics/controls.h"
+#include "sci/graphics/controls16.h"
#include "sci/graphics/cursor.h"
#include "sci/graphics/palette.h"
#include "sci/graphics/paint16.h"
@@ -49,6 +50,7 @@
#include "sci/graphics/text16.h"
#include "sci/graphics/view.h"
#ifdef ENABLE_SCI32
+#include "sci/graphics/controls32.h"
#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"
@@ -57,11 +59,13 @@
namespace Sci {
static int16 adjustGraphColor(int16 color) {
- // WORKAROUND: SCI1 EGA and Amiga games can set invalid colors (above 0 - 15).
- // Colors above 15 are all white in SCI1 EGA games, which is why this was never
- // observed. We clip them all to (0, 15) instead, as colors above 15 are used
- // for the undithering algorithm in EGA games - bug #3048908.
- if (getSciVersion() >= SCI_VERSION_1_EARLY && g_sci->getResMan()->getViewType() == kViewEga)
+ // WORKAROUND: EGA and Amiga games can set invalid colors (above 0 - 15).
+ // It seems only the lower nibble was used in these games.
+ // bug #3048908, #3486899.
+ // Confirmed in EGA games KQ4(late), QFG1(ega), LB1 that
+ // at least FillBox (only one of the functions using adjustGraphColor)
+ // behaves like this.
+ if (g_sci->getResMan()->getViewType() == kViewEga)
return color & 0x0F; // 0 - 15
else
return color;
@@ -810,13 +814,13 @@ void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) {
switch (type) {
case SCI_CONTROLS_TYPE_BUTTON:
debugC(kDebugLevelGraphics, "drawing button %04x:%04x to %d,%d", PRINT_REG(controlObject), x, y);
- g_sci->_gfxControls->kernelDrawButton(rect, controlObject, g_sci->strSplit(text.c_str(), NULL).c_str(), fontId, style, hilite);
+ g_sci->_gfxControls16->kernelDrawButton(rect, controlObject, g_sci->strSplit(text.c_str(), NULL).c_str(), fontId, style, hilite);
return;
case SCI_CONTROLS_TYPE_TEXT:
alignment = readSelectorValue(s->_segMan, controlObject, SELECTOR(mode));
debugC(kDebugLevelGraphics, "drawing text %04x:%04x ('%s') to %d,%d, mode=%d", PRINT_REG(controlObject), text.c_str(), x, y, alignment);
- g_sci->_gfxControls->kernelDrawText(rect, controlObject, g_sci->strSplit(text.c_str()).c_str(), fontId, alignment, style, hilite);
+ g_sci->_gfxControls16->kernelDrawText(rect, controlObject, g_sci->strSplit(text.c_str()).c_str(), fontId, alignment, style, hilite);
s->r_acc = g_sci->_gfxText16->allocAndFillReferenceRectArray();
return;
@@ -830,7 +834,7 @@ void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) {
writeSelectorValue(s->_segMan, controlObject, SELECTOR(cursor), cursorPos);
}
debugC(kDebugLevelGraphics, "drawing edit control %04x:%04x (text %04x:%04x, '%s') to %d,%d", PRINT_REG(controlObject), PRINT_REG(textReference), text.c_str(), x, y);
- g_sci->_gfxControls->kernelDrawTextEdit(rect, controlObject, g_sci->strSplit(text.c_str(), NULL).c_str(), fontId, mode, style, cursorPos, maxChars, hilite);
+ g_sci->_gfxControls16->kernelDrawTextEdit(rect, controlObject, g_sci->strSplit(text.c_str(), NULL).c_str(), fontId, mode, style, cursorPos, maxChars, hilite);
return;
case SCI_CONTROLS_TYPE_ICON:
@@ -847,7 +851,7 @@ void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) {
priority = -1;
}
debugC(kDebugLevelGraphics, "drawing icon control %04x:%04x to %d,%d", PRINT_REG(controlObject), x, y - 1);
- g_sci->_gfxControls->kernelDrawIcon(rect, controlObject, viewId, loopNo, celNo, priority, style, hilite);
+ g_sci->_gfxControls16->kernelDrawIcon(rect, controlObject, viewId, loopNo, celNo, priority, style, hilite);
return;
case SCI_CONTROLS_TYPE_LIST:
@@ -895,7 +899,7 @@ void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) {
}
debugC(kDebugLevelGraphics, "drawing list control %04x:%04x to %d,%d, diff %d", PRINT_REG(controlObject), x, y, SCI_MAX_SAVENAME_LENGTH);
- g_sci->_gfxControls->kernelDrawList(rect, controlObject, maxChars, listCount, listEntries, fontId, style, upperPos, cursorPos, isAlias, hilite);
+ g_sci->_gfxControls16->kernelDrawList(rect, controlObject, maxChars, listCount, listEntries, fontId, style, upperPos, cursorPos, isAlias, hilite);
free(listEntries);
delete[] listStrings;
return;
@@ -975,7 +979,10 @@ reg_t kEditControl(EngineState *s, int argc, reg_t *argv) {
switch (controlType) {
case SCI_CONTROLS_TYPE_TEXTEDIT:
// Only process textedit controls in here
- g_sci->_gfxControls->kernelTexteditChange(controlObject, eventObject);
+ g_sci->_gfxControls16->kernelTexteditChange(controlObject, eventObject);
+ break;
+ default:
+ break;
}
}
return s->r_acc;
@@ -1208,7 +1215,8 @@ reg_t kRemapColors(EngineState *s, int argc, reg_t *argv) {
switch (operation) {
case 0: { // Set remapping to base. 0 turns remapping off.
int16 base = (argc >= 2) ? argv[1].toSint16() : 0;
- warning("kRemapColors: Set remapping to base %d", base);
+ if (base != 0) // 0 is the default behavior when changing rooms in GK1, thus silencing the warning
+ warning("kRemapColors: Set remapping to base %d", base);
}
break;
case 1: { // unknown
@@ -1436,6 +1444,46 @@ reg_t kWinHelp(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
+// Taken from the SCI16 GfxTransitions class
+static void fadeOut() {
+ byte oldPalette[3 * 256], workPalette[3 * 256];
+ int16 stepNr, colorNr;
+ // Sierra did not fade in/out color 255 for sci1.1, but they used it in
+ // several pictures (e.g. qfg3 demo/intro), so the fading looked weird
+ int16 tillColorNr = getSciVersion() >= SCI_VERSION_1_1 ? 255 : 254;
+
+ g_system->getPaletteManager()->grabPalette(oldPalette, 0, 256);
+
+ for (stepNr = 100; stepNr >= 0; stepNr -= 10) {
+ for (colorNr = 1; colorNr <= tillColorNr; colorNr++) {
+ if (g_sci->_gfxPalette->colorIsFromMacClut(colorNr)) {
+ workPalette[colorNr * 3 + 0] = oldPalette[colorNr * 3];
+ workPalette[colorNr * 3 + 1] = oldPalette[colorNr * 3 + 1];
+ workPalette[colorNr * 3 + 2] = oldPalette[colorNr * 3 + 2];
+ } else {
+ workPalette[colorNr * 3 + 0] = oldPalette[colorNr * 3] * stepNr / 100;
+ workPalette[colorNr * 3 + 1] = oldPalette[colorNr * 3 + 1] * stepNr / 100;
+ workPalette[colorNr * 3 + 2] = oldPalette[colorNr * 3 + 2] * stepNr / 100;
+ }
+ }
+ g_system->getPaletteManager()->setPalette(workPalette + 3, 1, tillColorNr);
+ g_sci->getEngineState()->wait(2);
+ }
+}
+
+// Taken from the SCI16 GfxTransitions class
+static void fadeIn() {
+ int16 stepNr;
+ // Sierra did not fade in/out color 255 for sci1.1, but they used it in
+ // several pictures (e.g. qfg3 demo/intro), so the fading looked weird
+ int16 tillColorNr = getSciVersion() >= SCI_VERSION_1_1 ? 255 : 254;
+
+ for (stepNr = 0; stepNr <= 100; stepNr += 10) {
+ g_sci->_gfxPalette->kernelSetIntensity(1, tillColorNr + 1, stepNr, true);
+ g_sci->getEngineState()->wait(2);
+ }
+}
+
/**
* Used for scene transitions, replacing (but reusing parts of) the old
* transition code.
@@ -1446,31 +1494,65 @@ reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) {
// tables inside graphics/transitions.cpp
uint16 showStyle = argv[0].toUint16(); // 0 - 15
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] // boolean, animate or not while the transition lasts
- //argv[6] // refFrame
+ uint16 seconds = argv[2].toUint16(); // seconds that the transition lasts
+ uint16 backColor = argv[3].toUint16(); // target back color(?). When fading out, it's 0x0000. When fading in, it's 0xffff
+ int16 priority = argv[4].toSint16(); // always 0xc8 (200) when fading in/out
+ uint16 animate = argv[5].toUint16(); // boolean, animate or not while the transition lasts
+ uint16 refFrame = argv[6].toUint16(); // refFrame, always 0 when fading in/out
+ int16 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;
+ bool hasFadeArray = g_sci->getKernel()->findSelector("pFadeArray") > 0;
if (hasFadeArray) {
// argv[7]
- //int16 unk7 = (argc >= 9) ? argv[8].toSint16() : 0; // divisions (transition steps?)
+ divisions = (argc >= 9) ? argv[8].toSint16() : -1; // divisions (transition steps?)
} else {
- //int16 unk7 = (argc >= 8) ? argv[7].toSint16() : 0; // divisions (transition steps?)
- }*/
+ divisions = (argc >= 8) ? argv[7].toSint16() : -1; // divisions (transition steps?)
+ }
if (showStyle > 15) {
warning("kSetShowStyle: Illegal style %d for plane %04x:%04x", showStyle, PRINT_REG(planeObj));
return s->r_acc;
}
+ // TODO: Proper implementation. This is a very basic version. I'm not even
+ // sure if the rest of the styles will work with this mechanism.
+
+ // Check if the passed parameters are the ones we expect
+ if (showStyle == 13 || showStyle == 14) { // fade out / fade in
+ if (seconds != 1)
+ warning("kSetShowStyle(fade): seconds isn't 1, it's %d", seconds);
+ if (backColor != 0 && backColor != 0xFFFF)
+ warning("kSetShowStyle(fade): backColor isn't 0 or 0xFFFF, it's %d", backColor);
+ if (priority != 200)
+ warning("kSetShowStyle(fade): priority isn't 200, it's %d", priority);
+ if (animate != 0)
+ warning("kSetShowStyle(fade): animate isn't 0, it's %d", animate);
+ if (refFrame != 0)
+ warning("kSetShowStyle(fade): refFrame isn't 0, it's %d", refFrame);
+ if (divisions >= 0 && divisions != 20)
+ warning("kSetShowStyle(fade): divisions isn't 20, it's %d", divisions);
+ }
+
// TODO: Check if the plane is in the list of planes to draw
- // TODO: This is all a stub/skeleton, thus we're invoking kStub() for now
- kStub(s, argc, argv);
+ switch (showStyle) {
+ //case 0: // no transition, perhaps? (like in the previous SCI versions)
+ case 13: // fade out
+ // TODO: Temporary implementation, which ignores all additional parameters
+ fadeOut();
+ break;
+ case 14: // fade in
+ // TODO: Temporary implementation, which ignores all additional parameters
+ g_sci->_gfxFrameout->kernelFrameout(); // draw new scene before fading in
+ fadeIn();
+ break;
+ default:
+ // TODO: This is all a stub/skeleton, thus we're invoking kStub() for now
+ kStub(s, argc, argv);
+ break;
+ }
return s->r_acc;
}
@@ -1662,8 +1744,8 @@ reg_t kBitmap(EngineState *s, int argc, reg_t *argv) {
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);
+ WRITE_LE_UINT16(memoryPtr, width);
+ WRITE_LE_UINT16(memoryPtr + 2, height);
return memoryId;
}
break;
@@ -1689,8 +1771,8 @@ reg_t kBitmap(EngineState *s, int argc, reg_t *argv) {
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 totalWidth = READ_LE_UINT16(memoryPtr);
+ uint16 totalHeight = READ_LE_UINT16(memoryPtr + 2);
byte *bitmap = memoryPtr + BITMAP_HEADER_SIZE;
GfxView *view = g_sci->_gfxCache->getView(viewNum);
@@ -1730,8 +1812,8 @@ reg_t kBitmap(EngineState *s, int argc, reg_t *argv) {
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 totalWidth = READ_LE_UINT16(memoryPtr);
+ uint16 totalHeight = READ_LE_UINT16(memoryPtr + 2);
byte *bitmap = memoryPtr + BITMAP_HEADER_SIZE;
GfxFont *font = g_sci->_gfxCache->getFont(fontId);
@@ -1773,8 +1855,8 @@ reg_t kBitmap(EngineState *s, int argc, reg_t *argv) {
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 totalWidth = READ_LE_UINT16(memoryPtr);
+ uint16 totalHeight = READ_LE_UINT16(memoryPtr + 2);
uint16 width = MIN<uint16>(totalWidth - x, fillWidth);
uint16 height = MIN<uint16>(totalHeight - y, fillHeight);
byte *bitmap = memoryPtr + BITMAP_HEADER_SIZE;
@@ -1795,6 +1877,20 @@ reg_t kBitmap(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
+// 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().
+reg_t kEditText(EngineState *s, int argc, reg_t *argv) {
+ reg_t controlObject = argv[0];
+
+ if (!controlObject.isNull()) {
+ g_sci->_gfxControls32->kernelTexteditChange(controlObject);
+ }
+
+ return s->r_acc;
+}
+
#endif
} // End of namespace Sci
diff --git a/engines/sci/engine/klists.cpp b/engines/sci/engine/klists.cpp
index 8500f08211..2a33df26bc 100644
--- a/engines/sci/engine/klists.cpp
+++ b/engines/sci/engine/klists.cpp
@@ -409,7 +409,7 @@ int sort_temp_cmp(const void *p1, const void *p2) {
const sort_temp_t *st1 = (const sort_temp_t *)p1;
const sort_temp_t *st2 = (const sort_temp_t *)p2;
- if (st1->order.segment < st1->order.segment || (st1->order.segment == st1->order.segment && st1->order.offset < st2->order.offset))
+ if (st1->order.segment < st2->order.segment || (st1->order.segment == st2->order.segment && st1->order.offset < st2->order.offset))
return -1;
if (st1->order.segment > st2->order.segment || (st1->order.segment == st2->order.segment && st1->order.offset > st2->order.offset))
@@ -753,7 +753,6 @@ reg_t kArray(EngineState *s, int argc, reg_t *argv) {
reg_t arrayHandle = argv[1];
SciArray<reg_t> *array1 = s->_segMan->lookupArray(argv[1]);
- //SciArray<reg_t> *array1 = !argv[1].isNull() ? s->_segMan->lookupArray(argv[1]) : s->_segMan->allocateArray(&arrayHandle);
SciArray<reg_t> *array2 = s->_segMan->lookupArray(argv[3]);
uint32 index1 = argv[2].toUint16();
uint32 index2 = argv[4].toUint16();
@@ -790,21 +789,8 @@ reg_t kArray(EngineState *s, int argc, reg_t *argv) {
#endif
return NULL_REG;
}
- SegmentType sourceType = s->_segMan->getSegmentObj(argv[1].segment)->getType();
- if (sourceType == SEG_TYPE_SCRIPT) {
- // A technique used in later SCI2.1 and SCI3 games: the contents of a script
- // are loaded in an array (well, actually a string).
- Script *scr = s->_segMan->getScript(argv[1].segment);
- reg_t stringHandle;
-
- SciString *dupString = s->_segMan->allocateString(&stringHandle);
- dupString->setSize(scr->getBufSize());
- dupString->fromString(Common::String((const char *)scr->getBuf()));
-
- return stringHandle;
- } else if (sourceType != SEG_TYPE_ARRAY && sourceType != SEG_TYPE_SCRIPT) {
- error("kArray(Dup): Request to duplicate a segment which isn't an array or a script");
- }
+ if (s->_segMan->getSegmentObj(argv[1].segment)->getType() != SEG_TYPE_ARRAY)
+ error("kArray(Dup): Request to duplicate a segment which isn't an array");
reg_t arrayHandle;
SciArray<reg_t> *dupArray = s->_segMan->allocateArray(&arrayHandle);
diff --git a/engines/sci/engine/kpathing.cpp b/engines/sci/engine/kpathing.cpp
index 375aeaa06b..089b325a7f 100644
--- a/engines/sci/engine/kpathing.cpp
+++ b/engines/sci/engine/kpathing.cpp
@@ -1545,7 +1545,6 @@ reg_t kAvoidPath(EngineState *s, int argc, reg_t *argv) {
default:
warning("Unknown AvoidPath subfunction %d", argc);
return NULL_REG;
- break;
}
}
diff --git a/engines/sci/engine/kscripts.cpp b/engines/sci/engine/kscripts.cpp
index 93c1fffe3c..9b0e490d7e 100644
--- a/engines/sci/engine/kscripts.cpp
+++ b/engines/sci/engine/kscripts.cpp
@@ -74,17 +74,13 @@ reg_t kLock(EngineState *s, int argc, reg_t *argv) {
case 0 :
if (id.getNumber() == 0xFFFF) {
// Unlock all resources of the requested type
- Common::List<ResourceId> *resources = g_sci->getResMan()->listResources(type);
- Common::List<ResourceId>::iterator itr = resources->begin();
-
- while (itr != resources->end()) {
+ Common::List<ResourceId> resources = g_sci->getResMan()->listResources(type);
+ Common::List<ResourceId>::iterator itr;
+ for (itr = resources.begin(); itr != resources.end(); ++itr) {
Resource *res = g_sci->getResMan()->testResource(*itr);
if (res->isLocked())
g_sci->getResMan()->unlockResource(res);
- ++itr;
}
-
- delete resources;
} else {
which = g_sci->getResMan()->findResource(id, 0);
diff --git a/engines/sci/engine/ksound.cpp b/engines/sci/engine/ksound.cpp
index 33bef58e52..c469f775f9 100644
--- a/engines/sci/engine/ksound.cpp
+++ b/engines/sci/engine/ksound.cpp
@@ -195,6 +195,13 @@ reg_t kDoAudio(EngineState *s, int argc, reg_t *argv) {
return make_reg(0, 1);
} else {
int16 language = argv[1].toSint16();
+
+ // athrxx: It seems from disasm that the original KQ5 FM-Towns loads a default language (Japanese) audio map at the beginning
+ // right after loading the video and audio drivers. The -1 language argument in here simply means that the original will stick
+ // with Japanese. Instead of doing that we switch to the language selected in the launcher.
+ if (g_sci->getPlatform() == Common::kPlatformFMTowns && language == -1)
+ language = (g_sci->getLanguage() == Common::JA_JPN) ? K_LANG_JAPANESE : K_LANG_ENGLISH;
+
debugC(kDebugLevelSound, "kDoAudio: set language to %d", language);
if (language != -1)
diff --git a/engines/sci/engine/kstring.cpp b/engines/sci/engine/kstring.cpp
index 1a9359bb26..fe8d631497 100644
--- a/engines/sci/engine/kstring.cpp
+++ b/engines/sci/engine/kstring.cpp
@@ -147,7 +147,7 @@ reg_t kReadNumber(EngineState *s, int argc, reg_t *argv) {
Common::String source_str = s->_segMan->getString(argv[0]);
const char *source = source_str.c_str();
- while (isspace((unsigned char)*source))
+ while (Common::isSpace(*source))
source++; /* Skip whitespace */
int16 result = 0;
@@ -246,14 +246,14 @@ reg_t kFormat(EngineState *s, int argc, reg_t *argv) {
/* int writelength; -- unused atm */
- if (xfer && (isdigit(static_cast<unsigned char>(xfer)) || xfer == '-' || xfer == '=')) {
+ if (xfer && (Common::isDigit(xfer) || xfer == '-' || xfer == '=')) {
char *destp;
if (xfer == '0')
fillchar = '0';
else if (xfer == '=')
align = ALIGN_CENTER;
- else if (isdigit(static_cast<unsigned char>(xfer)) || (xfer == '-'))
+ else if (Common::isDigit(xfer) || (xfer == '-'))
source--; // Go to start of length argument
strLength = strtol(source, &destp, 10);
@@ -697,13 +697,15 @@ reg_t kString(EngineState *s, int argc, reg_t *argv) {
case 6: { // Cpy
const char *string2 = 0;
uint32 string2Size = 0;
+ Common::String string;
if (argv[3].segment == s->_segMan->getStringSegmentId()) {
- SciString *string = s->_segMan->lookupString(argv[3]);
- string2 = string->getRawData();
- string2Size = string->getSize();
+ SciString *sstr;
+ sstr = s->_segMan->lookupString(argv[3]);
+ string2 = sstr->getRawData();
+ string2Size = sstr->getSize();
} else {
- Common::String string = s->_segMan->getString(argv[3]);
+ string = s->_segMan->getString(argv[3]);
string2 = string.c_str();
string2Size = string.size() + 1;
}
@@ -743,28 +745,16 @@ reg_t kString(EngineState *s, int argc, reg_t *argv) {
return make_reg(0, strcmp(string1.c_str(), string2.c_str()));
}
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]);
- rawString = string->getRawData();
- size = string->getSize();
+ *dupString = *s->_segMan->lookupString(argv[1]);
} else {
- Common::String string = s->_segMan->getString(argv[1]);
- rawString = string.c_str();
- size = string.size() + 1;
+ dupString->fromString(s->_segMan->getString(argv[1]));
}
- dupString->setSize(size);
-
- for (uint32 i = 0; i < size; i++)
- dupString->setValue(i, rawString[i]);
-
return stringHandle;
}
case 9: // Getdata
diff --git a/engines/sci/engine/message.cpp b/engines/sci/engine/message.cpp
index f30f4e923e..cddd01e10c 100644
--- a/engines/sci/engine/message.cpp
+++ b/engines/sci/engine/message.cpp
@@ -345,7 +345,7 @@ bool MessageState::stringLit(Common::String &outStr, const Common::String &inStr
}
bool MessageState::stringStage(Common::String &outstr, const Common::String &inStr, uint &index) {
- // Stage directions of the form (n*), where n is anything but a digit or a lowercase character
+ // Stage directions of the form (n *), where n is anything but a digit or a lowercase character
if (inStr[index] != '(')
return false;
diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp
index 030c0f3f54..404bea799d 100644
--- a/engines/sci/engine/savegame.cpp
+++ b/engines/sci/engine/savegame.cpp
@@ -66,7 +66,7 @@ void syncWithSerializer(Common::Serializer &s, T &obj) {
}
// By default, sync using syncWithSerializer, which in turn can easily be overloaded.
-template <typename T>
+template<typename T>
struct DefaultSyncer : Common::BinaryFunction<Common::Serializer, T, void> {
void operator()(Common::Serializer &s, T &obj) const {
//obj.saveLoadWithSerializer(s);
@@ -87,7 +87,7 @@ struct DefaultSyncer : Common::BinaryFunction<Common::Serializer, T, void> {
*
* TODO: Add something like this for lists, queues....
*/
-template <typename T, class Syncer = DefaultSyncer<T> >
+template<typename T, class Syncer = DefaultSyncer<T> >
struct ArraySyncer : Common::BinaryFunction<Common::Serializer, T, void> {
void operator()(Common::Serializer &s, Common::Array<T> &arr) const {
uint len = arr.size();
@@ -113,13 +113,13 @@ void syncArray(Common::Serializer &s, Common::Array<T> &arr) {
}
-template <>
+template<>
void syncWithSerializer(Common::Serializer &s, reg_t &obj) {
s.syncAsUint16LE(obj.segment);
s.syncAsUint16LE(obj.offset);
}
-template <>
+template<>
void syncWithSerializer(Common::Serializer &s, synonym_t &obj) {
s.syncAsUint16LE(obj.replaceant);
s.syncAsUint16LE(obj.replacement);
@@ -203,7 +203,8 @@ void SegManager::saveLoadWithSerializer(Common::Serializer &s) {
// Now, load the script itself
scr->load(g_sci->getResMan());
- for (ObjMap::iterator it = scr->_objects.begin(); it != scr->_objects.end(); ++it)
+ ObjMap objects = scr->getObjectMap();
+ for (ObjMap::iterator it = objects.begin(); it != objects.end(); ++it)
it->_value.syncBaseObject(scr->getBuf(it->_value.getPos().offset));
}
@@ -226,9 +227,10 @@ void SegManager::saveLoadWithSerializer(Common::Serializer &s) {
continue;
Script *scr = (Script *)_heap[i];
- scr->_localsBlock = (scr->_localsSegment == 0) ? NULL : (LocalVariables *)(_heap[scr->_localsSegment]);
+ scr->syncLocalsBlock(this);
- for (ObjMap::iterator it = scr->_objects.begin(); it != scr->_objects.end(); ++it) {
+ ObjMap objects = scr->getObjectMap();
+ for (ObjMap::iterator it = objects.begin(); it != objects.end(); ++it) {
reg_t addr = it->_value.getPos();
Object *obj = scr->scriptObjInit(addr, false);
@@ -237,7 +239,7 @@ void SegManager::saveLoadWithSerializer(Common::Serializer &s) {
// TODO/FIXME: This should not be happening at all. It might indicate a possible issue
// with the garbage collector. It happens for example in LSL5 (German, perhaps English too).
warning("Failed to locate base object for object at %04X:%04X; skipping", PRINT_REG(addr));
- scr->_objects.erase(addr.toUint16());
+ objects.erase(addr.toUint16());
}
}
}
@@ -245,7 +247,7 @@ void SegManager::saveLoadWithSerializer(Common::Serializer &s) {
}
-template <>
+template<>
void syncWithSerializer(Common::Serializer &s, Class &obj) {
s.syncAsSint32LE(obj.script);
syncWithSerializer(s, obj.reg);
@@ -324,14 +326,14 @@ void Object::saveLoadWithSerializer(Common::Serializer &s) {
syncArray<reg_t>(s, _variables);
}
-template <>
+template<>
void syncWithSerializer(Common::Serializer &s, SegmentObjTable<Clone>::Entry &obj) {
s.syncAsSint32LE(obj.next_free);
syncWithSerializer<Object>(s, obj);
}
-template <>
+template<>
void syncWithSerializer(Common::Serializer &s, SegmentObjTable<List>::Entry &obj) {
s.syncAsSint32LE(obj.next_free);
@@ -339,7 +341,7 @@ void syncWithSerializer(Common::Serializer &s, SegmentObjTable<List>::Entry &obj
syncWithSerializer(s, obj.last);
}
-template <>
+template<>
void syncWithSerializer(Common::Serializer &s, SegmentObjTable<Node>::Entry &obj) {
s.syncAsSint32LE(obj.next_free);
@@ -350,7 +352,7 @@ void syncWithSerializer(Common::Serializer &s, SegmentObjTable<Node>::Entry &obj
}
#ifdef ENABLE_SCI32
-template <>
+template<>
void syncWithSerializer(Common::Serializer &s, SegmentObjTable<SciArray<reg_t> >::Entry &obj) {
s.syncAsSint32LE(obj.next_free);
@@ -386,7 +388,7 @@ void syncWithSerializer(Common::Serializer &s, SegmentObjTable<SciArray<reg_t> >
}
}
-template <>
+template<>
void syncWithSerializer(Common::Serializer &s, SegmentObjTable<SciString>::Entry &obj) {
s.syncAsSint32LE(obj.next_free);
@@ -414,7 +416,7 @@ void syncWithSerializer(Common::Serializer &s, SegmentObjTable<SciString>::Entry
}
#endif
-template <typename T>
+template<typename T>
void sync_Table(Common::Serializer &s, T &obj) {
s.syncAsSint32LE(obj.first_free);
s.syncAsSint32LE(obj.entries_used);
diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp
index 01e1afe5ea..8b26969f4a 100644
--- a/engines/sci/engine/script.cpp
+++ b/engines/sci/engine/script.cpp
@@ -492,8 +492,29 @@ SegmentRef Script::dereference(reg_t pointer) {
return ret;
}
+LocalVariables *Script::allocLocalsSegment(SegManager *segMan) {
+ if (!getLocalsCount()) { // No locals
+ return NULL;
+ } else {
+ LocalVariables *locals;
+
+ if (_localsSegment) {
+ locals = (LocalVariables *)segMan->getSegment(_localsSegment, SEG_TYPE_LOCALS);
+ if (!locals || locals->getType() != SEG_TYPE_LOCALS || locals->script_id != getScriptNumber())
+ error("Invalid script locals segment while allocating locals");
+ } else
+ locals = (LocalVariables *)segMan->allocSegment(new LocalVariables(), &_localsSegment);
+
+ _localsBlock = locals;
+ locals->script_id = getScriptNumber();
+ locals->_locals.resize(getLocalsCount());
+
+ return locals;
+ }
+}
+
void Script::initializeLocals(SegManager *segMan) {
- LocalVariables *locals = segMan->allocLocalsSegment(this);
+ LocalVariables *locals = allocLocalsSegment(segMan);
if (locals) {
if (getSciVersion() > SCI_VERSION_0_EARLY) {
const byte *base = (const byte *)(_buf + getLocalsOffset());
@@ -508,6 +529,10 @@ void Script::initializeLocals(SegManager *segMan) {
}
}
+void Script::syncLocalsBlock(SegManager *segMan) {
+ _localsBlock = (_localsSegment == 0) ? NULL : (LocalVariables *)(segMan->getSegment(_localsSegment, SEG_TYPE_LOCALS));
+}
+
void Script::initializeClasses(SegManager *segMan) {
const byte *seeker = 0;
uint16 mult = 0;
diff --git a/engines/sci/engine/script.h b/engines/sci/engine/script.h
index ff061e0e36..1ebae3b7a8 100644
--- a/engines/sci/engine/script.h
+++ b/engines/sci/engine/script.h
@@ -62,23 +62,18 @@ private:
const uint16 *_exportTable; /**< Abs. offset of the export table or 0 if not present */
uint16 _numExports; /**< Number of entries in the exports table */
- const byte *_synonyms; /**< Synonyms block or 0 if not present*/
+ const byte *_synonyms; /**< Synonyms block or 0 if not present */
uint16 _numSynonyms; /**< Number of entries in the synonyms block */
int _localsOffset;
uint16 _localsCount;
bool _markedAsDeleted;
-
-public:
- /**
- * Table for objects, contains property variables.
- * Indexed by the TODO offset.
- */
- ObjMap _objects;
SegmentId _localsSegment; /**< The local variable segment */
LocalVariables *_localsBlock;
+ ObjMap _objects; /**< Table for objects, contains property variables */
+
public:
int getLocalsOffset() const { return _localsOffset; }
uint16 getLocalsCount() const { return _localsCount; }
@@ -89,6 +84,11 @@ public:
const byte *getBuf(uint offset = 0) const { return _buf + offset; }
int getScriptNumber() const { return _nr; }
+ SegmentId getLocalsSegment() const { return _localsSegment; }
+ reg_t *getLocalsBegin() { return _localsBlock ? _localsBlock->_locals.begin() : NULL; }
+ void syncLocalsBlock(SegManager *segMan);
+ ObjMap &getObjectMap() { return _objects; }
+ const ObjMap &getObjectMap() const { return _objects; }
public:
Script();
@@ -295,6 +295,8 @@ private:
* @param segmentId The script's segment id
*/
void initializeObjectsSci3(SegManager *segMan, SegmentId segmentId);
+
+ LocalVariables *allocLocalsSegment(SegManager *segMan);
};
} // End of namespace Sci
diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp
index 7efcb42f4b..187f1ce021 100644
--- a/engines/sci/engine/script_patches.cpp
+++ b/engines/sci/engine/script_patches.cpp
@@ -366,7 +366,7 @@ const uint16 freddypharkasPatchLadderEvent[] = {
// script, description, magic DWORD, adjust
const SciScriptSignature freddypharkasSignatures[] = {
{ 0, "CD: score early disposal", 1, PATCH_MAGICDWORD(0x39, 0x0d, 0x43, 0x75), -3, freddypharkasSignatureScoreDisposal, freddypharkasPatchScoreDisposal },
- { 235, "CD: canister pickup hang", 3, PATCH_MAGICDWORD(0x39, 0x07, 0x39, 0x08), -4, freddypharkasSignatureCanisterHang, freddypharkasPatchCanisterHang },
+ { 235, "CD: canister pickup hang", 3, PATCH_MAGICDWORD(0x39, 0x07, 0x39, 0x08), -4, freddypharkasSignatureCanisterHang, freddypharkasPatchCanisterHang },
{ 320, "ladder event issue", 2, PATCH_MAGICDWORD(0x6d, 0x76, 0x38, 0xf5), -1, freddypharkasSignatureLadderEvent, freddypharkasPatchLadderEvent },
SCI_SIGNATUREENTRY_TERMINATOR
};
@@ -437,8 +437,68 @@ const uint16 gk1PatchDay5PhoneFreeze[] = {
PATCH_END
};
+// Floppy version: Interrogation::dispose() compares an object reference
+// (stored in the view selector) with a number, leading to a crash (this kind
+// of comparison was not used in SCI32). The view selector is used to store
+// both a view number (in some cases), and a view reference (in other cases).
+// In the floppy version, the checks are in the wrong order, so there is a
+// comparison between a number an an object. In the CD version, the checks are
+// in the correct order, thus the comparison is correct, thus we use the code
+// from the CD version in the floppy one.
+const byte gk1SignatureInterrogationBug[] = {
+ 43,
+ 0x65, 0x4c, // aTop 4c
+ 0x67, 0x50, // pTos 50
+ 0x34, 0x10, 0x27, // ldi 2710
+ 0x1e, // gt?
+ 0x31, 0x08, // bnt 08 [05a0]
+ 0x67, 0x50, // pTos 50
+ 0x34, 0x10, 0x27, // ldi 2710
+ 0x04, // sub
+ 0x65, 0x50, // aTop 50
+ 0x63, 0x50, // pToa 50
+ 0x31, 0x15, // bnt 15 [05b9]
+ 0x39, 0x0e, // pushi 0e
+ 0x76, // push0
+ 0x4a, 0x04, 0x00, // send 0004
+ 0xa5, 0x00, // sat 00
+ 0x38, 0x93, 0x00, // pushi 0093
+ 0x76, // push0
+ 0x63, 0x50, // pToa 50
+ 0x4a, 0x04, 0x00, // send 0004
+ 0x85, 0x00, // lat 00
+ 0x65, 0x50, // aTop 50
+ 0
+};
+
+const uint16 gk1PatchInterrogationBug[] = {
+ 0x65, 0x4c, // aTop 4c
+ 0x63, 0x50, // pToa 50
+ 0x31, 0x15, // bnt 15 [05b9]
+ 0x39, 0x0e, // pushi 0e
+ 0x76, // push0
+ 0x4a, 0x04, 0x00, // send 0004
+ 0xa5, 0x00, // sat 00
+ 0x38, 0x93, 0x00, // pushi 0093
+ 0x76, // push0
+ 0x63, 0x50, // pToa 50
+ 0x4a, 0x04, 0x00, // send 0004
+ 0x85, 0x00, // lat 00
+ 0x65, 0x50, // aTop 50
+ 0x67, 0x50, // pTos 50
+ 0x34, 0x10, 0x27, // ldi 2710
+ 0x1e, // gt?
+ 0x31, 0x08, // bnt 08 [05b9]
+ 0x67, 0x50, // pTos 50
+ 0x34, 0x10, 0x27, // ldi 2710
+ 0x04, // sub
+ 0x65, 0x50, // aTop 50
+ PATCH_END
+};
+
// script, description, magic DWORD, adjust
const SciScriptSignature gk1Signatures[] = {
+ { 51, "interrogation bug", 1, PATCH_MAGICDWORD(0x65, 0x4c, 0x67, 0x50), 0, gk1SignatureInterrogationBug, gk1PatchInterrogationBug },
{ 212, "day 5 phone freeze", 1, PATCH_MAGICDWORD(0x35, 0x03, 0x65, 0x1a), 0, gk1SignatureDay5PhoneFreeze, gk1PatchDay5PhoneFreeze },
{ 230, "day 6 police beignet timer issue", 1, PATCH_MAGICDWORD(0x34, 0xdc, 0x00, 0x65), -16, gk1SignatureDay6PoliceBeignet, gk1PatchDay6PoliceBeignet },
{ 230, "day 6 police sleep timer issue", 1, PATCH_MAGICDWORD(0x34, 0xdc, 0x00, 0x65), -5, gk1SignatureDay6PoliceSleep, gk1PatchDay6PoliceSleep },
diff --git a/engines/sci/engine/scriptdebug.cpp b/engines/sci/engine/scriptdebug.cpp
index ad3f4fb788..ef61b2e28a 100644
--- a/engines/sci/engine/scriptdebug.cpp
+++ b/engines/sci/engine/scriptdebug.cpp
@@ -122,8 +122,8 @@ reg_t disassemble(EngineState *s, reg_t pos, bool printBWTag, bool printBytecode
#endif
i = 0;
- while (g_opcode_formats[opcode][i]) {
- switch (g_opcode_formats[opcode][i++]) {
+ while (g_sci->_opcode_formats[opcode][i]) {
+ switch (g_sci->_opcode_formats[opcode][i++]) {
case Script_Invalid:
warning("-Invalid operation-");
break;
@@ -296,7 +296,7 @@ bool isJumpOpcode(EngineState *s, reg_t pos, reg_t& jumpTarget) {
Script *script_entity = (Script *)mobj;
const byte *scr = script_entity->getBuf();
- int scr_size = script_entity->getBufSize();
+ int scr_size = script_entity->getScriptSize();
if (pos.offset >= scr_size)
return false;
@@ -310,7 +310,13 @@ bool isJumpOpcode(EngineState *s, reg_t pos, reg_t& jumpTarget) {
case op_bt:
case op_bnt:
case op_jmp:
- jumpTarget = pos + bytecount + opparams[0];
+ {
+ reg_t jmpTarget = pos + bytecount + opparams[0];
+ // QFG2 has invalid jumps outside the script buffer in script 260
+ if (jmpTarget.offset >= scr_size)
+ return false;
+ jumpTarget = jmpTarget;
+ }
return true;
default:
return false;
@@ -721,7 +727,7 @@ void logKernelCall(const KernelFunction *kernelCall, const KernelSubFunction *ke
switch (mobj->getType()) {
case SEG_TYPE_HUNK:
{
- HunkTable *ht = (HunkTable*)mobj;
+ HunkTable *ht = (HunkTable *)mobj;
int index = argv[parmNr].offset;
if (ht->isValidEntry(index)) {
// NOTE: This ", deleted" isn't as useful as it could
diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp
index 1510af8508..cc127c8dbc 100644
--- a/engines/sci/engine/seg_manager.cpp
+++ b/engines/sci/engine/seg_manager.cpp
@@ -151,8 +151,8 @@ void SegManager::deallocate(SegmentId seg) {
if (mobj->getType() == SEG_TYPE_SCRIPT) {
Script *scr = (Script *)mobj;
_scriptSegMap.erase(scr->getScriptNumber());
- if (scr->_localsSegment)
- deallocate(scr->_localsSegment);
+ if (scr->getLocalsSegment())
+ deallocate(scr->getLocalsSegment());
}
delete mobj;
@@ -270,12 +270,13 @@ reg_t SegManager::findObjectByName(const Common::String &name, int index) {
if (mobj->getType() == SEG_TYPE_SCRIPT) {
// It's a script, scan all objects in it
const Script *scr = (const Script *)mobj;
- for (ObjMap::const_iterator it = scr->_objects.begin(); it != scr->_objects.end(); ++it) {
+ const ObjMap &objects = scr->getObjectMap();
+ for (ObjMap::const_iterator it = objects.begin(); it != objects.end(); ++it) {
objpos.offset = it->_value.getPos().offset;
if (name == getObjectName(objpos))
result.push_back(objpos);
}
- } else if (mobj->getType() == SEG_TYPE_CLONES) {
+ } else if (mobj->getType() == SEG_TYPE_CLONES) {
// It's clone table, scan all objects in it
const CloneTable *ct = (const CloneTable *)mobj;
for (uint idx = 0; idx < ct->_table.size(); ++idx) {
@@ -341,29 +342,6 @@ SegmentId SegManager::getScriptSegment(int script_nr, ScriptLoadType load) {
return segment;
}
-LocalVariables *SegManager::allocLocalsSegment(Script *scr) {
- if (!scr->getLocalsCount()) { // No locals
- scr->_localsSegment = 0;
- scr->_localsBlock = NULL;
- return NULL;
- } else {
- LocalVariables *locals;
-
- if (scr->_localsSegment) {
- locals = (LocalVariables *)_heap[scr->_localsSegment];
- if (!locals || locals->getType() != SEG_TYPE_LOCALS || locals->script_id != scr->getScriptNumber())
- error("Invalid script locals segment while allocating locals");
- } else
- locals = (LocalVariables *)allocSegment(new LocalVariables(), &scr->_localsSegment);
-
- scr->_localsBlock = locals;
- locals->script_id = scr->getScriptNumber();
- locals->_locals.resize(scr->getLocalsCount());
-
- return locals;
- }
-}
-
DataStack *SegManager::allocateStack(int size, SegmentId *segid) {
SegmentObj *mobj = allocSegment(new DataStack(), segid);
DataStack *retval = (DataStack *)mobj;
@@ -713,7 +691,7 @@ void SegManager::memcpy(reg_t dest, const byte* src, size_t n) {
if (dest_r.isRaw) {
// raw -> raw
- ::memcpy((char*)dest_r.raw, src, n);
+ ::memcpy((char *)dest_r.raw, src, n);
} else {
// raw -> non-raw
for (uint i = 0; i < n; i++)
diff --git a/engines/sci/engine/seg_manager.h b/engines/sci/engine/seg_manager.h
index ab5aeacabf..62e711e686 100644
--- a/engines/sci/engine/seg_manager.h
+++ b/engines/sci/engine/seg_manager.h
@@ -463,8 +463,10 @@ private:
SegmentId _stringSegId;
#endif
-private:
+public:
SegmentObj *allocSegment(SegmentObj *mem, SegmentId *segid);
+
+private:
void deallocate(SegmentId seg);
void createClassTable();
@@ -477,9 +479,6 @@ private:
* 'seg' is a valid segment
*/
bool check(SegmentId seg);
-
-public:
- LocalVariables *allocLocalsSegment(Script *scr);
};
} // End of namespace Sci
diff --git a/engines/sci/engine/segment.cpp b/engines/sci/engine/segment.cpp
index 3f11d6ff49..36b7d92c07 100644
--- a/engines/sci/engine/segment.cpp
+++ b/engines/sci/engine/segment.cpp
@@ -143,9 +143,11 @@ SegmentRef LocalVariables::dereference(reg_t pointer) {
if (ret.maxSize > 0) {
ret.reg = &_locals[pointer.offset / 2];
} else {
- if ((g_sci->getEngineState()->currentRoomNumber() == 660 || g_sci->getEngineState()->currentRoomNumber() == 660)
+ if ((g_sci->getEngineState()->currentRoomNumber() == 160 ||
+ g_sci->getEngineState()->currentRoomNumber() == 220)
&& g_sci->getGameId() == GID_LAURABOW2) {
- // Happens in two places during the intro of LB2CD, both from kMemory(peek):
+ // WORKAROUND: Happens in two places during the intro of LB2CD, both
+ // from kMemory(peek):
// - room 160: Heap 160 has 83 local variables (0-82), and the game
// asks for variables at indices 83 - 90 too.
// - room 220: Heap 220 has 114 local variables (0-113), and the
@@ -292,18 +294,19 @@ void SciString::fromString(const Common::String &string) {
if (_type != 3)
error("SciString::fromString(): Array is not a string");
- if (string.size() > _size)
- setSize(string.size());
+ setSize(string.size() + 1);
for (uint32 i = 0; i < string.size(); i++)
_data[i] = string[i];
+
+ _data[string.size()] = 0;
}
SegmentRef StringTable::dereference(reg_t pointer) {
SegmentRef ret;
ret.isRaw = true;
ret.maxSize = _table[pointer.offset].getSize();
- ret.raw = (byte*)_table[pointer.offset].getRawData();
+ ret.raw = (byte *)_table[pointer.offset].getRawData();
return ret;
}
diff --git a/engines/sci/engine/segment.h b/engines/sci/engine/segment.h
index 009a2558ce..54cf7b98af 100644
--- a/engines/sci/engine/segment.h
+++ b/engines/sci/engine/segment.h
@@ -370,7 +370,7 @@ public:
#ifdef ENABLE_SCI32
-template <typename T>
+template<typename T>
class SciArray {
public:
SciArray() : _type(-1), _data(NULL), _size(0), _actualSize(0) { }
diff --git a/engines/sci/engine/selector.cpp b/engines/sci/engine/selector.cpp
index a9aca9e22f..a8b1cf7ec2 100644
--- a/engines/sci/engine/selector.cpp
+++ b/engines/sci/engine/selector.cpp
@@ -178,6 +178,7 @@ void Kernel::mapSelectors() {
FIND_SELECTOR(dimmed);
FIND_SELECTOR(fore);
FIND_SELECTOR(back);
+ FIND_SELECTOR(skip);
FIND_SELECTOR(fixPriority);
FIND_SELECTOR(mirrored);
FIND_SELECTOR(useInsetRect);
diff --git a/engines/sci/engine/selector.h b/engines/sci/engine/selector.h
index bbd86bb03e..4b913a866a 100644
--- a/engines/sci/engine/selector.h
+++ b/engines/sci/engine/selector.h
@@ -144,6 +144,7 @@ struct SelectorCache {
Selector fore;
Selector back;
+ Selector skip;
Selector dimmed;
Selector fixPriority;
diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp
index 4ea9f72054..28818cddef 100644
--- a/engines/sci/engine/state.cpp
+++ b/engines/sci/engine/state.cpp
@@ -145,12 +145,12 @@ void EngineState::wait(int16 ticks) {
void EngineState::initGlobals() {
Script *script_000 = _segMan->getScript(1);
- if (!script_000->_localsBlock)
+ if (script_000->getLocalsCount() == 0)
error("Script 0 has no locals block");
- variablesSegment[VAR_GLOBAL] = script_000->_localsSegment;
- variablesBase[VAR_GLOBAL] = variables[VAR_GLOBAL] = script_000->_localsBlock->_locals.begin();
- variablesMax[VAR_GLOBAL] = script_000->_localsBlock->_locals.size();
+ variablesSegment[VAR_GLOBAL] = script_000->getLocalsSegment();
+ variablesBase[VAR_GLOBAL] = variables[VAR_GLOBAL] = script_000->getLocalsBegin();
+ variablesMax[VAR_GLOBAL] = script_000->getLocalsCount();
}
uint16 EngineState::currentRoomNumber() const {
diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp
index c94fdac034..162dce9fcc 100644
--- a/engines/sci/engine/vm.cpp
+++ b/engines/sci/engine/vm.cpp
@@ -235,6 +235,9 @@ ExecStack *execute_method(EngineState *s, uint16 script, uint16 pubfunct, StackP
if (g_sci->getGameId() == GID_TORIN && script == 64036) {
// Script 64036 in Torin's Passage is empty and contains an invalid
// (empty) export
+ } else if (g_sci->getGameId() == GID_RAMA && script == 64908) {
+ // Script 64908 in the demo of RAMA contains an invalid (empty)
+ // export
} else
#endif
error("Request for invalid exported function 0x%x of script %d", pubfunct, script);
@@ -462,10 +465,10 @@ int readPMachineInstruction(const byte *src, byte &extOpcode, int16 opparams[4])
memset(opparams, 0, 4*sizeof(int16));
- for (int i = 0; g_opcode_formats[opcode][i]; ++i) {
+ for (int i = 0; g_sci->_opcode_formats[opcode][i]; ++i) {
//debugN("Opcode: 0x%x, Opnumber: 0x%x, temp: %d\n", opcode, opcode, temp);
assert(i < 3);
- switch (g_opcode_formats[opcode][i]) {
+ switch (g_sci->_opcode_formats[opcode][i]) {
case Script_Byte:
opparams[i] = src[offset++];
@@ -593,15 +596,9 @@ void run_vm(EngineState *s) {
if (!local_script) {
error("Could not find local script from segment %x", s->xs->local_segment);
} else {
- s->variablesSegment[VAR_LOCAL] = local_script->_localsSegment;
- if (local_script->_localsBlock)
- s->variablesBase[VAR_LOCAL] = s->variables[VAR_LOCAL] = local_script->_localsBlock->_locals.begin();
- else
- s->variablesBase[VAR_LOCAL] = s->variables[VAR_LOCAL] = NULL;
- if (local_script->_localsBlock)
- s->variablesMax[VAR_LOCAL] = local_script->_localsBlock->_locals.size();
- else
- s->variablesMax[VAR_LOCAL] = 0;
+ s->variablesSegment[VAR_LOCAL] = local_script->getLocalsSegment();
+ s->variablesBase[VAR_LOCAL] = s->variables[VAR_LOCAL] = local_script->getLocalsBegin();
+ s->variablesMax[VAR_LOCAL] = local_script->getLocalsCount();
s->variablesMax[VAR_TEMP] = s->xs->sp - s->xs->fp;
s->variablesMax[VAR_PARAM] = s->xs->argc + 1;
}
@@ -770,16 +767,28 @@ void run_vm(EngineState *s) {
// Branch relative if true
if (s->r_acc.offset || s->r_acc.segment)
s->xs->addr.pc.offset += opparams[0];
+
+ if (s->xs->addr.pc.offset >= local_script->getScriptSize())
+ error("[VM] op_bt: request to jump past the end of script %d (offset %d, script is %d bytes)",
+ local_script->getScriptNumber(), s->xs->addr.pc.offset, local_script->getScriptSize());
break;
case op_bnt: // 0x18 (24)
// Branch relative if not true
if (!(s->r_acc.offset || s->r_acc.segment))
s->xs->addr.pc.offset += opparams[0];
+
+ if (s->xs->addr.pc.offset >= local_script->getScriptSize())
+ error("[VM] op_bnt: request to jump past the end of script %d (offset %d, script is %d bytes)",
+ local_script->getScriptNumber(), s->xs->addr.pc.offset, local_script->getScriptSize());
break;
case op_jmp: // 0x19 (25)
s->xs->addr.pc.offset += opparams[0];
+
+ if (s->xs->addr.pc.offset >= local_script->getScriptSize())
+ error("[VM] op_jmp: request to jump past the end of script %d (offset %d, script is %d bytes)",
+ local_script->getScriptNumber(), s->xs->addr.pc.offset, local_script->getScriptSize());
break;
case op_ldi: // 0x1a (26)
diff --git a/engines/sci/engine/vm.h b/engines/sci/engine/vm.h
index 36eadfa1c2..334d224baf 100644
--- a/engines/sci/engine/vm.h
+++ b/engines/sci/engine/vm.h
@@ -139,26 +139,6 @@ enum {
GC_INTERVAL = 0x8000
};
-// Opcode formats
-enum opcode_format {
- Script_Invalid = -1,
- Script_None = 0,
- Script_Byte,
- Script_SByte,
- Script_Word,
- Script_SWord,
- Script_Variable,
- Script_SVariable,
- Script_SRelative,
- Script_Property,
- Script_Global,
- Script_Local,
- Script_Temp,
- Script_Param,
- Script_Offset,
- Script_End
-};
-
enum sci_opcodes {
op_bnot = 0x00, // 000
op_add = 0x01, // 001
@@ -290,8 +270,6 @@ enum sci_opcodes {
op_minusspi = 0x7f // 127
};
-extern opcode_format g_opcode_formats[128][4];
-
void script_adjust_opcode_formats();
/**
diff --git a/engines/sci/engine/vm_types.h b/engines/sci/engine/vm_types.h
index dc87cf758a..7b155a4532 100644
--- a/engines/sci/engine/vm_types.h
+++ b/engines/sci/engine/vm_types.h
@@ -172,6 +172,26 @@ enum {
NULL_SELECTOR = -1
};
+// Opcode formats
+enum opcode_format {
+ Script_Invalid = -1,
+ Script_None = 0,
+ Script_Byte,
+ Script_SByte,
+ Script_Word,
+ Script_SWord,
+ Script_Variable,
+ Script_SVariable,
+ Script_SRelative,
+ Script_Property,
+ Script_Global,
+ Script_Local,
+ Script_Temp,
+ Script_Param,
+ Script_Offset,
+ Script_End
+};
+
} // End of namespace Sci
diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp
index f68b74e1e0..a556134e32 100644
--- a/engines/sci/engine/workarounds.cpp
+++ b/engines/sci/engine/workarounds.cpp
@@ -353,7 +353,7 @@ const SciWorkaroundEntry kNewWindow_workarounds[] = {
// gameID, room,script,lvl, object-name, method-name, call,index, workaround
const SciWorkaroundEntry kPaletteUnsetFlag_workarounds[] = {
- { GID_QFG4, 100, 100, 0, "doMovie", "<noname144>", -1, 0, { WORKAROUND_IGNORE, 0 } }, // after the Sierra logo, no flags are passed, thus the call is meaningless - bug #3034506
+ { GID_QFG4, 100, 100, 0, "doMovie", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // after the Sierra logo, no flags are passed, thus the call is meaningless - bug #3034506
SCI_WORKAROUNDENTRY_TERMINATOR
};