diff options
Diffstat (limited to 'engines/sci/engine')
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 }; |