diff options
author | Matthew Hoops | 2011-12-12 15:25:28 -0500 |
---|---|---|
committer | Matthew Hoops | 2011-12-12 15:25:28 -0500 |
commit | 00279659b22cbd5db739d5351e83a9fc2a2ae408 (patch) | |
tree | 497f06f46820043cbdf1725652b8f0073223e24a /engines/sci/engine | |
parent | d932df79bed5aac97e17c0920a5e75cb5ce733ee (diff) | |
parent | d1628feb761acc9f4607f64de3eb620fea53bcc9 (diff) | |
download | scummvm-rg350-00279659b22cbd5db739d5351e83a9fc2a2ae408.tar.gz scummvm-rg350-00279659b22cbd5db739d5351e83a9fc2a2ae408.tar.bz2 scummvm-rg350-00279659b22cbd5db739d5351e83a9fc2a2ae408.zip |
Merge remote branch 'upstream/master' into pegasus
Conflicts:
video/qt_decoder.cpp
Diffstat (limited to 'engines/sci/engine')
26 files changed, 582 insertions, 445 deletions
diff --git a/engines/sci/engine/kernel.cpp b/engines/sci/engine/kernel.cpp index a83a026762..c99bc4fe47 100644 --- a/engines/sci/engine/kernel.cpp +++ b/engines/sci/engine/kernel.cpp @@ -828,7 +828,8 @@ void Kernel::setDefaultKernelNames(GameFeatures *features) { enum { kKernelEntriesSci2 = 0x8b, kKernelEntriesGk2Demo = 0xa0, - kKernelEntriesSci21 = 0x9d + kKernelEntriesSci21 = 0x9d, + kKernelEntriesSci3 = 0xa1 }; void Kernel::setKernelNamesSci2() { @@ -856,8 +857,11 @@ void Kernel::setKernelNamesSci21(GameFeatures *features) { // OnMe is IsOnMe here, but they should be compatible _kernelNames[0x23] = "Robot"; // Graph in SCI2 _kernelNames[0x2e] = "Priority"; // DisposeTextBitmap in SCI2 - } else + } else if (getSciVersion() != SCI_VERSION_3) { _kernelNames = Common::StringArray(sci21_default_knames, kKernelEntriesSci21); + } else if (getSciVersion() == SCI_VERSION_3) { + _kernelNames = Common::StringArray(sci21_default_knames, kKernelEntriesSci3); + } } #endif @@ -910,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 b605908dc1..e549c1f8ae 100644 --- a/engines/sci/engine/kernel.h +++ b/engines/sci/engine/kernel.h @@ -442,11 +442,11 @@ reg_t kUpdateScreenItem(EngineState *s, int argc, reg_t *argv); reg_t kDeleteScreenItem(EngineState *s, int argc, reg_t *argv); // Text reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv); +reg_t kDisposeTextBitmap(EngineState *s, int argc, reg_t *argv); // "Planes" in SCI32 are pictures reg_t kAddPlane(EngineState *s, int argc, reg_t *argv); reg_t kDeletePlane(EngineState *s, int argc, reg_t *argv); reg_t kUpdatePlane(EngineState *s, int argc, reg_t *argv); -reg_t kRepaintPlane(EngineState *s, int argc, reg_t *argv); reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv); reg_t kGetHighPlanePri(EngineState *s, int argc, reg_t *argv); reg_t kFrameOut(EngineState *s, int argc, reg_t *argv); @@ -456,6 +456,8 @@ reg_t kListEachElementDo(EngineState *s, int argc, reg_t *argv); reg_t kListFirstTrue(EngineState *s, int argc, reg_t *argv); reg_t kListAllTrue(EngineState *s, int argc, reg_t *argv); reg_t kInPolygon(EngineState *s, int argc, reg_t *argv); +reg_t kObjectIntersect(EngineState *s, int argc, reg_t *argv); +reg_t kEditText(EngineState *s, int argc, reg_t *argv); // SCI2.1 Kernel Functions reg_t kText(EngineState *s, int argc, reg_t *argv); @@ -477,6 +479,9 @@ reg_t kScrollWindow(EngineState *s, int argc, reg_t *argv); reg_t kSetFontRes(EngineState *s, int argc, reg_t *argv); reg_t kFont(EngineState *s, int argc, reg_t *argv); reg_t kBitmap(EngineState *s, int argc, reg_t *argv); + +// SCI3 Kernel functions +reg_t kPlayDuck(EngineState *s, int argc, reg_t *argv); #endif reg_t kDoSoundInit(EngineState *s, int argc, reg_t *argv); diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h index d3adcaccbf..fe0c9fc2ef 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 { @@ -477,6 +477,7 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(CreateTextBitmap), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, { MAP_CALL(DeletePlane), SIG_EVERYWHERE, "o", NULL, NULL }, { MAP_CALL(DeleteScreenItem), SIG_EVERYWHERE, "o", NULL, NULL }, + { MAP_CALL(DisposeTextBitmap), SIG_EVERYWHERE, "r", NULL, NULL }, { MAP_CALL(FrameOut), SIG_EVERYWHERE, "", NULL, NULL }, { MAP_CALL(GetHighPlanePri), SIG_EVERYWHERE, "", NULL, NULL }, { MAP_CALL(InPolygon), SIG_EVERYWHERE, "iio", NULL, NULL }, @@ -493,25 +494,46 @@ static SciKernelMapEntry s_kernelMap[] = { // our own memory manager and garbage collector, thus we simply call FlushResources, which in turn invokes // our garbage collector (i.e. the SCI0-SCI1.1 semantics). { "Purge", kFlushResources, SIG_EVERYWHERE, "i", NULL, NULL }, - { MAP_CALL(RepaintPlane), SIG_EVERYWHERE, "o", NULL, NULL }, { MAP_CALL(SetShowStyle), SIG_EVERYWHERE, "ioiiiii([ri])(i)", NULL, NULL }, { MAP_CALL(String), SIG_EVERYWHERE, "(.*)", NULL, NULL }, { MAP_CALL(UpdatePlane), SIG_EVERYWHERE, "o", NULL, NULL }, { MAP_CALL(UpdateScreenItem), SIG_EVERYWHERE, "o", NULL, NULL }, + { MAP_CALL(ObjectIntersect), SIG_EVERYWHERE, "oo", NULL, NULL }, + { MAP_CALL(EditText), SIG_EVERYWHERE, "o", NULL, NULL }, // SCI2 unmapped functions - TODO! - // SetScroll - // AddMagnify // most probably similar to the SCI1.1 functions. We need a test case - // DeleteMagnify - // EditText - // DisposeTextBitmap - // VibrateMouse - used in QFG4 floppy - // PalCycle - // ObjectIntersect - used in QFG4 floppy - // MakeSaveCatName - used in the Save/Load dialog of GK1CD (SRDialog, script 64990) - // MakeSaveFileName - used in the Save/Load dialog of GK1CD (SRDialog, script 64990) + + // SetScroll - called by script 64909, Styler::doit() + // PalCycle - called by Game::newRoom. Related to RemapColors. + // VibrateMouse - used in QFG4 + + // SCI2 Empty functions + + // Debug function used to track resources + { MAP_EMPTY(ResourceTrack), SIG_EVERYWHERE, "(.*)", NULL, NULL }, + + // SCI2 functions that are used in the original save/load menus. Marked as dummy, so + // that the engine errors out on purpose. TODO: Implement once the original save/load + // menus are implemented. + + // Creates the name of the save catalogue/directory to save into. + // TODO: Implement once the original save/load menus are implemented. + { MAP_DUMMY(MakeSaveCatName), SIG_EVERYWHERE, "(.*)", NULL, NULL }, + + // Creates the name of the save file to save into + // TODO: Implement once the original save/load menus are implemented. + { MAP_DUMMY(MakeSaveFileName), SIG_EVERYWHERE, "(.*)", NULL, NULL }, // Unused / debug SCI2 unused functions, always mapped to kDummy + + // AddMagnify/DeleteMagnify are both called by script 64979 (the Magnifier + // object) in GK1 only. There is also an associated empty magnifier view + // (view 1), however, it doesn't seem to be used anywhere, as all the + // magnifier closeups (e.g. in scene 470) are normal views. Thus, these + // are marked as dummy, so if they're ever used the engine will error out. + { MAP_DUMMY(AddMagnify), SIG_EVERYWHERE, "(.*)", NULL, NULL }, + { MAP_DUMMY(DeleteMagnify), SIG_EVERYWHERE, "(.*)", NULL, NULL }, + { MAP_DUMMY(RepaintPlane), SIG_EVERYWHERE, "o", NULL, NULL }, { MAP_DUMMY(InspectObject), SIG_EVERYWHERE, "(.*)", NULL, NULL }, // Profiler (same as SCI0-SCI1.1) // Record (same as SCI0-SCI1.1) @@ -590,7 +612,6 @@ static SciKernelMapEntry s_kernelMap[] = { // SCI2.1 unmapped functions - TODO! - // Bitmap // MovePlaneItems - used by SQ6 to scroll through the inventory via the up/down buttons // AddLine - used by Torin's Passage to highlight the chapter buttons // DeleteLine - used by Torin's Passage to delete the highlight from the chapter buttons @@ -599,6 +620,9 @@ static SciKernelMapEntry s_kernelMap[] = { // (inclusive) are set to 0 // MorphOn - used by SQ6, script 900, the datacorder reprogramming puzzle (from room 270) // SetHotRectangles - used by Phantasmagoria 1 + + // SCI3 Kernel Functions + { MAP_CALL(PlayDuck), SIG_EVERYWHERE, "(.*)", NULL, NULL }, #endif { NULL, NULL, SIG_EVERYWHERE, NULL, NULL, NULL } @@ -771,7 +795,7 @@ static const char *const sci2_default_knames[] = { /*0x0d*/ "CelWide", /*0x0e*/ "CelHigh", /*0x0f*/ "GetHighPlanePri", - /*0x10*/ "GetHighItemPri", + /*0x10*/ "GetHighItemPri", // unused function /*0x11*/ "ShakeScreen", /*0x12*/ "OnMe", /*0x13*/ "ShowMovie", @@ -783,9 +807,9 @@ static const char *const sci2_default_knames[] = { /*0x19*/ "AddPlane", /*0x1a*/ "DeletePlane", /*0x1b*/ "UpdatePlane", - /*0x1c*/ "RepaintPlane", + /*0x1c*/ "RepaintPlane", // unused function /*0x1d*/ "SetShowStyle", - /*0x1e*/ "ShowStylePercent", + /*0x1e*/ "ShowStylePercent", // unused function /*0x1f*/ "SetScroll", /*0x20*/ "AddMagnify", /*0x21*/ "DeleteMagnify", @@ -799,7 +823,7 @@ static const char *const sci2_default_knames[] = { /*0x29*/ "Dummy", /*0x2a*/ "SetQuitStr", /*0x2b*/ "EditText", - /*0x2c*/ "InputText", + /*0x2c*/ "InputText", // unused function /*0x2d*/ "CreateTextBitmap", /*0x2e*/ "DisposeTextBitmap", /*0x2f*/ "GetEvent", @@ -944,8 +968,8 @@ static const char *const sci21_default_knames[] = { /*0x15*/ "NumLoops", /*0x16*/ "NumCels", /*0x17*/ "IsOnMe", - /*0x18*/ "AddMagnify", - /*0x19*/ "DeleteMagnify", + /*0x18*/ "AddMagnify", // dummy in SCI3 + /*0x19*/ "DeleteMagnify", // dummy in SCI3 /*0x1a*/ "CelRect", /*0x1b*/ "BaseLineSpan", /*0x1c*/ "CelWide", @@ -965,10 +989,10 @@ static const char *const sci21_default_knames[] = { /*0x2a*/ "UpdatePlane", /*0x2b*/ "RepaintPlane", /*0x2c*/ "GetHighPlanePri", - /*0x2d*/ "GetHighItemPri", + /*0x2d*/ "GetHighItemPri", // unused function /*0x2e*/ "SetShowStyle", - /*0x2f*/ "ShowStylePercent", - /*0x30*/ "SetScroll", + /*0x2f*/ "ShowStylePercent", // unused function + /*0x30*/ "SetScroll", // dummy in SCI3 /*0x31*/ "MovePlaneItems", /*0x32*/ "ShakeScreen", /*0x33*/ "Dummy", @@ -977,7 +1001,7 @@ static const char *const sci21_default_knames[] = { /*0x36*/ "Dummy", /*0x37*/ "IsHiRes", /*0x38*/ "SetVideoMode", - /*0x39*/ "ShowMovie", + /*0x39*/ "ShowMovie", // dummy in SCI3 /*0x3a*/ "Robot", /*0x3b*/ "CreateTextBitmap", /*0x3c*/ "Random", @@ -995,7 +1019,7 @@ static const char *const sci21_default_knames[] = { /*0x48*/ "Message", /*0x49*/ "Font", /*0x4a*/ "EditText", - /*0x4b*/ "InputText", + /*0x4b*/ "InputText", // unused function /*0x4c*/ "ScrollWindow", // Dummy in SCI3 /*0x4d*/ "Dummy", /*0x4e*/ "Dummy", @@ -1020,9 +1044,9 @@ static const char *const sci21_default_knames[] = { /*0x61*/ "InitBresen", /*0x62*/ "DoBresen", /*0x63*/ "SetJump", - /*0x64*/ "AvoidPath", + /*0x64*/ "AvoidPath", // dummy in SCI3 /*0x65*/ "InPolygon", - /*0x66*/ "MergePoly", + /*0x66*/ "MergePoly", // dummy in SCI3 /*0x67*/ "ObjectIntersect", /*0x68*/ "Dummy", /*0x69*/ "MemoryInfo", @@ -1051,7 +1075,7 @@ static const char *const sci21_default_knames[] = { /*0x80*/ "Dummy", /*0x81*/ "Dummy", // called when changing rooms in most SCI2.1 games (e.g. KQ7, GK2, MUMG deluxe, Phant1) /*0x82*/ "Dummy", - /*0x83*/ "PrintDebug", // debug function, used by Shivers 2 (demo and full) + /*0x83*/ "PrintDebug", // debug function, used by Shivers (demo and full) /*0x84*/ "Dummy", /*0x85*/ "Dummy", /*0x86*/ "Dummy", @@ -1086,7 +1110,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 0c73125bdb..ce903626e7 100644 --- a/engines/sci/engine/kfile.cpp +++ b/engines/sci/engine/kfile.cpp @@ -22,6 +22,7 @@ #include "common/archive.h" #include "common/config-manager.h" +#include "common/debug-channels.h" #include "common/file.h" #include "common/str.h" #include "common/savefile.h" @@ -52,9 +53,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 +117,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) { @@ -365,9 +353,12 @@ reg_t kDeviceInfo(EngineState *s, int argc, reg_t *argv) { reg_t kGetSaveDir(EngineState *s, int argc, reg_t *argv) { #ifdef ENABLE_SCI32 - // TODO: SCI32 uses a parameter here. - if (argc > 0) - warning("kGetSaveDir called with %d parameter(s): %04x:%04x", argc, PRINT_REG(argv[0])); + // SCI32 uses a parameter here. It is used to modify a string, stored in a + // global variable, so that game scripts store the save directory. We + // don't really set a save game directory, thus not setting the string to + // anything is the correct thing to do here. + //if (argc > 0) + // warning("kGetSaveDir called with %d parameter(s): %04x:%04x", argc, PRINT_REG(argv[0])); #endif return s->_segMan->getSaveDirPtr(); } @@ -1057,6 +1048,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 @@ -1164,8 +1167,17 @@ reg_t kSave(EngineState *s, int argc, reg_t *argv) { return kRestoreGame(s, argc - 1,argv + 1); case 2: return kGetSaveDir(s, argc - 1, argv + 1); + case 3: + return kCheckSaveGame(s, argc - 1, argv + 1); case 5: return kGetSaveFiles(s, argc - 1, argv + 1); + case 6: + // This is used in Shivers to delete saved games, however it + // always passes the same file name (SHIVER), so it doesn't + // actually delete anything... + // TODO: Check why this happens + // argv[1] is a string (most likely the save game directory) + return kFileIOUnlink(s, argc - 2, argv + 2); case 8: // TODO // This is a timer callback, with 1 parameter: the timer object @@ -1176,10 +1188,9 @@ reg_t kSave(EngineState *s, int argc, reg_t *argv) { // This function has to return something other than 0 to proceed return s->r_acc; default: - warning("Unknown/unhandled kSave subop %d", argv[0].toUint16()); + kStub(s, argc, argv); + return NULL_REG; } - - return NULL_REG; } #endif diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp index 9f309aeab7..76c6778f0a 100644 --- a/engines/sci/engine/kgraphics.cpp +++ b/engines/sci/engine/kgraphics.cpp @@ -39,7 +39,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,9 +49,10 @@ #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" -#include "sci/video/robot_decoder.h" #endif namespace Sci { @@ -354,11 +355,9 @@ reg_t kTextSize(EngineState *s, int argc, reg_t *argv) { textWidth = dest[3].toUint16(); textHeight = dest[2].toUint16(); #ifdef ENABLE_SCI32 - if (!g_sci->_gfxText16) { - // TODO: Implement this - textWidth = 0; textHeight = 0; - warning("TODO: implement kTextSize for SCI32"); - } else + if (g_sci->_gfxText32) + g_sci->_gfxText32->kernelTextSize(g_sci->strSplit(text.c_str(), sep).c_str(), font_nr, maxwidth, &textWidth, &textHeight); + else #endif g_sci->_gfxText16->kernelTextSize(g_sci->strSplit(text.c_str(), sep).c_str(), font_nr, maxwidth, &textWidth, &textHeight); @@ -381,8 +380,13 @@ reg_t kTextSize(EngineState *s, int argc, reg_t *argv) { } debugC(kDebugLevelStrings, "GetTextSize '%s' -> %dx%d", text.c_str(), textWidth, textHeight); - dest[2] = make_reg(0, textHeight); - dest[3] = make_reg(0, textWidth); + if (getSciVersion() <= SCI_VERSION_1_1) { + dest[2] = make_reg(0, textHeight); + dest[3] = make_reg(0, textWidth); + } else { + dest[2] = make_reg(0, textWidth); + dest[3] = make_reg(0, textHeight); + } return s->r_acc; } @@ -807,13 +811,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; @@ -827,7 +831,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: @@ -844,7 +848,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: @@ -892,7 +896,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; @@ -972,7 +976,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; @@ -1285,7 +1292,10 @@ reg_t kCantBeHere32(EngineState *s, int argc, reg_t *argv) { } reg_t kAddScreenItem(EngineState *s, int argc, reg_t *argv) { - g_sci->_gfxFrameout->kernelAddScreenItem(argv[0]); + if (g_sci->_gfxFrameout->findScreenItem(argv[0]) == NULL) + g_sci->_gfxFrameout->kernelAddScreenItem(argv[0]); + else + g_sci->_gfxFrameout->kernelUpdateScreenItem(argv[0]); return s->r_acc; } @@ -1314,11 +1324,6 @@ reg_t kUpdatePlane(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } -reg_t kRepaintPlane(EngineState *s, int argc, reg_t *argv) { - g_sci->_gfxFrameout->kernelRepaintPlane(argv[0]); - return s->r_acc; -} - reg_t kAddPicAt(EngineState *s, int argc, reg_t *argv) { reg_t planeObj = argv[0]; GuiResourceId pictureId = argv[1].toUint16(); @@ -1338,30 +1343,23 @@ reg_t kFrameOut(EngineState *s, int argc, reg_t *argv) { return NULL_REG; } +reg_t kObjectIntersect(EngineState *s, int argc, reg_t *argv) { + Common::Rect objRect1 = g_sci->_gfxCompare->getNSRect(argv[0]); + Common::Rect objRect2 = g_sci->_gfxCompare->getNSRect(argv[1]); + return make_reg(0, objRect1.intersects(objRect2)); +} + // Tests if the coordinate is on the passed object reg_t kIsOnMe(EngineState *s, int argc, reg_t *argv) { uint16 x = argv[0].toUint16(); uint16 y = argv[1].toUint16(); reg_t targetObject = argv[2]; uint16 illegalBits = argv[3].offset; - Common::Rect nsRect; + Common::Rect nsRect = g_sci->_gfxCompare->getNSRect(targetObject, true); // we assume that x, y are local coordinates - // Get the bounding rectangle of the object - nsRect.left = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsLeft)); - nsRect.top = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsTop)); - nsRect.right = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsRight)); - nsRect.bottom = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsBottom)); - - // nsRect top/left may be negative, adjust accordingly - Common::Rect checkRect = nsRect; - if (checkRect.top < 0) - checkRect.top = 0; - if (checkRect.left < 0) - checkRect.left = 0; - - bool contained = checkRect.contains(x, y); + bool contained = nsRect.contains(x, y); if (contained && illegalBits) { // If illegalbits are set, we check the color of the pixel that got clicked on // for now, we return false if the pixel is transparent @@ -1387,70 +1385,29 @@ reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv) { debugC(kDebugLevelStrings, "kCreateTextBitmap case 0 (%04x:%04x, %04x:%04x, %04x:%04x)", PRINT_REG(argv[1]), PRINT_REG(argv[2]), PRINT_REG(argv[3])); debugC(kDebugLevelStrings, "%s", text.c_str()); - // TODO: arguments 1 and 2 - g_sci->_gfxText32->createTextBitmap(object); - break; + uint16 maxWidth = argv[1].toUint16(); // nsRight - nsLeft + 1 + uint16 maxHeight = argv[2].toUint16(); // nsBottom - nsTop + 1 + return g_sci->_gfxText32->createTextBitmap(object, maxWidth, maxHeight); } case 1: { if (argc != 2) { - warning("kCreateTextBitmap(0): expected 2 arguments, got %i", argc); + warning("kCreateTextBitmap(1): expected 2 arguments, got %i", argc); return NULL_REG; } reg_t object = argv[1]; Common::String text = s->_segMan->getString(readSelector(s->_segMan, object, SELECTOR(text))); debugC(kDebugLevelStrings, "kCreateTextBitmap case 1 (%04x:%04x)", PRINT_REG(argv[1])); debugC(kDebugLevelStrings, "%s", text.c_str()); - g_sci->_gfxText32->createTextBitmap(object); - break; + return g_sci->_gfxText32->createTextBitmap(object); } default: warning("CreateTextBitmap(%d)", argv[0].toUint16()); + return NULL_REG; } - - return NULL_REG; } -reg_t kRobot(EngineState *s, int argc, reg_t *argv) { - - int16 subop = argv[0].toUint16(); - - switch (subop) { - case 0: { // init - int id = argv[1].toUint16(); - reg_t obj = argv[2]; - int16 flag = argv[3].toSint16(); - int16 x = argv[4].toUint16(); - int16 y = argv[5].toUint16(); - warning("kRobot(init), id %d, obj %04x:%04x, flag %d, x=%d, y=%d", id, PRINT_REG(obj), flag, x, y); - g_sci->_robotDecoder->load(id); - g_sci->_robotDecoder->setPos(x, y); - } - break; - case 1: // LSL6 hires (startup) - // TODO - return NULL_REG; // an integer is expected - case 4: { // start - we don't really have a use for this one - //int id = argv[1].toUint16(); - //warning("kRobot(start), id %d", id); - } - break; - case 7: // unknown, called e.g. by Phantasmagoria - warning("kRobot(%d)", subop); - break; - case 8: // sync - if ((uint32)g_sci->_robotDecoder->getCurFrame() != g_sci->_robotDecoder->getFrameCount() - 1) { - writeSelector(s->_segMan, argv[1], SELECTOR(signal), NULL_REG); - } else { - g_sci->_robotDecoder->close(); - // Signal the engine scripts that the video is done - writeSelector(s->_segMan, argv[1], SELECTOR(signal), SIGNAL_REG); - } - break; - default: - warning("kRobot(%d)", subop); - break; - } - +reg_t kDisposeTextBitmap(EngineState *s, int argc, reg_t *argv) { + g_sci->_gfxText32->disposeTextBitmap(argv[0]); return s->r_acc; } @@ -1484,22 +1441,30 @@ reg_t kWinHelp(EngineState *s, int argc, reg_t *argv) { } /** - * Used to programmatically mass set properties of the target plane + * Used for scene transitions, replacing (but reusing parts of) the old + * transition code. */ reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) { - // TODO: This is all a stub/skeleton, thus we're invoking kStub() for now - kStub(s, argc, argv); - // Can be called with 7 or 8 parameters - // showStyle matches the style selector of the associated plane object + // The style defines which transition to perform. Related to the transition + // tables inside graphics/transitions.cpp uint16 showStyle = argv[0].toUint16(); // 0 - 15 - reg_t planeObj = argv[1]; - //argv[2] // seconds - //argv[3] // back + reg_t planeObj = argv[1]; // the affected plane + //argv[2] // seconds that the transition lasts + //argv[3] // back color to be used(?) //int16 priority = argv[4].toSint16(); - //argv[5] // animate + //argv[5] // boolean, animate or not while the transition lasts //argv[6] // refFrame - //int16 unk7 = (argc >= 8) ? argv[7].toSint16() : 0; // divisions + + // If the game has the pFadeArray selector, another parameter is used here, + // before the optional last parameter + /*bool hasFadeArray = g_sci->getKernel()->findSelector("pFadeArray") > 0; + if (hasFadeArray) { + // argv[7] + //int16 unk7 = (argc >= 9) ? argv[8].toSint16() : 0; // divisions (transition steps?) + } else { + //int16 unk7 = (argc >= 8) ? argv[7].toSint16() : 0; // divisions (transition steps?) + }*/ if (showStyle > 15) { warning("kSetShowStyle: Illegal style %d for plane %04x:%04x", showStyle, PRINT_REG(planeObj)); @@ -1508,36 +1473,36 @@ reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) { // TODO: Check if the plane is in the list of planes to draw - return s->r_acc; -} - -reg_t kCelInfo(EngineState *s, int argc, reg_t *argv) { // TODO: This is all a stub/skeleton, thus we're invoking kStub() for now kStub(s, argc, argv); - // Used by Shivers 1, room 23601 - - // 6 arguments, all integers: - // argv[0] - subop (0 - 4). It's constantly called with 4 in Shivers 1 - // argv[1] - view (used with view 23602 in Shivers 1) - // argv[2] - loop - // argv[3] - cel - // argv[4] - unknown (row?) - // argv[5] - unknown (column?) - - // Subops: - // 0 - return the view - // 1 - return the loop - // 2, 3 - nop - // 4 - returns some kind of hash (?) based on the view and the two last params - - // This seems to be a debug function, but it could be used to check if - // the jigsaw pieces "stick" together (they currently don't, unless I'm missing - // something) - return s->r_acc; } +reg_t kCelInfo(EngineState *s, int argc, reg_t *argv) { + // Used by Shivers 1, room 23601 to determine what blocks on the red door puzzle board + // are occupied by pieces already + + switch (argv[0].toUint16()) { // subops 0 - 4 + // 0 - return the view + // 1 - return the loop + // 2, 3 - nop + case 4: { + GuiResourceId viewId = argv[1].toSint16(); + int16 loopNo = argv[2].toSint16(); + int16 celNo = argv[3].toSint16(); + int16 x = argv[4].toUint16(); + int16 y = argv[5].toUint16(); + byte color = g_sci->_gfxCache->kernelViewGetColorAtCoordinate(viewId, loopNo, celNo, x, y); + return make_reg(0, color); + } + default: { + kStub(s, argc, argv); + return s->r_acc; + } + } +} + reg_t kScrollWindow(EngineState *s, int argc, reg_t *argv) { // Used by Phantasmagoria 1 and SQ6. In SQ6, it is used for the messages // shown in the scroll window at the bottom of the screen. @@ -1670,6 +1635,11 @@ reg_t kFont(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } +// TODO: Eventually, all of the kBitmap operations should be put +// in a separate class + +#define BITMAP_HEADER_SIZE 46 + reg_t kBitmap(EngineState *s, int argc, reg_t *argv) { // Used for bitmap operations in SCI2.1 and SCI3. // This is the SCI2.1 version, the functionality seems to have changed in SCI3. @@ -1681,21 +1651,28 @@ reg_t kBitmap(EngineState *s, int argc, reg_t *argv) { // script 64890 and TransView::init() in script 64884 uint16 width = argv[1].toUint16(); uint16 height = argv[2].toUint16(); - uint16 skip = argv[3].toUint16(); - uint16 back = argv[4].toUint16(); - uint16 width2 = (argc >= 6) ? argv[5].toUint16() : 0; - uint16 height2 = (argc >= 7) ? argv[6].toUint16() : 0; - uint16 transparent = (argc >= 8) ? argv[7].toUint16() : 0; - warning("kBitmap(0): width %d, height %d, skip %d, back %d, width2 %d, height2 %d, transparent %d", - width, height, skip, back, width2, height2, transparent); - // returns a pointer to a bitmap + //uint16 skip = argv[3].toUint16(); + uint16 back = argv[4].toUint16(); // usually equals skip + //uint16 width2 = (argc >= 6) ? argv[5].toUint16() : 0; + //uint16 height2 = (argc >= 7) ? argv[6].toUint16() : 0; + //uint16 transparentFlag = (argc >= 8) ? argv[7].toUint16() : 0; + + // TODO: skip, width2, height2, transparentFlag + // (used for transparent bitmaps) + int entrySize = width * height + BITMAP_HEADER_SIZE; + reg_t memoryId = s->_segMan->allocateHunkEntry("Bitmap()", entrySize); + byte *memoryPtr = s->_segMan->getHunkPointer(memoryId); + memset(memoryPtr, 0, BITMAP_HEADER_SIZE); // zero out the bitmap header + memset(memoryPtr + BITMAP_HEADER_SIZE, back, width * height); + // Save totalWidth, totalHeight + // TODO: Save the whole bitmap header, like SSCI does + WRITE_LE_UINT16(memoryPtr, width); + WRITE_LE_UINT16(memoryPtr + 2, height); + return memoryId; } break; - case 1: // dispose bitmap surface - // 1 param, bitmap pointer, called e.g. from MenuItem::dispose - // in Torin's Passage, script 64893 - warning("kBitmap(1), bitmap ptr %04x:%04x", PRINT_REG(argv[1])); - break; + case 1: // dispose text bitmap surface + return kDisposeTextBitmap(s, argc - 1, argv + 1); case 2: // dispose bitmap surface, with extra param // 2 params, called e.g. from MenuItem::dispose in Torin's Passage, // script 64893 @@ -1705,50 +1682,113 @@ reg_t kBitmap(EngineState *s, int argc, reg_t *argv) { { // 6 params, called e.g. from TiledBitmap::resize() in Torin's Passage, // script 64869 - reg_t bitmapPtr = argv[1]; // obtained from kBitmap(0) + reg_t hunkId = argv[1]; // obtained from kBitmap(0) // The tiled view seems to always have 2 loops. // These loops need to have 1 cel in loop 0 and 8 cels in loop 1. - uint16 view = argv[2].toUint16(); // vTiles selector + uint16 viewNum = argv[2].toUint16(); // vTiles selector uint16 loop = argv[3].toUint16(); uint16 cel = argv[4].toUint16(); uint16 x = argv[5].toUint16(); uint16 y = argv[6].toUint16(); - warning("kBitmap(3): bitmap ptr %04x:%04x, view %d, loop %d, cel %d, x %d, y %d", - PRINT_REG(bitmapPtr), view, loop, cel, x, y); + + byte *memoryPtr = s->_segMan->getHunkPointer(hunkId); + // Get totalWidth, totalHeight + 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); + uint16 tileWidth = view->getWidth(loop, cel); + uint16 tileHeight = view->getHeight(loop, cel); + const byte *tileBitmap = view->getBitmap(loop, cel); + uint16 width = MIN<uint16>(totalWidth - x, tileWidth); + uint16 height = MIN<uint16>(totalHeight - y, tileHeight); + + for (uint16 curY = 0; curY < height; curY++) { + for (uint16 curX = 0; curX < width; curX++) { + bitmap[(curY + y) * totalWidth + (curX + x)] = tileBitmap[curY * tileWidth + curX]; + } + } + } break; - case 4: // process text + case 4: // add text to bitmap { // 13 params, called e.g. from TextButton::createBitmap() in Torin's Passage, // script 64894 - reg_t bitmapPtr = argv[1]; // obtained from kBitmap(0) + reg_t hunkId = argv[1]; // obtained from kBitmap(0) Common::String text = s->_segMan->getString(argv[2]); - // unk3 - // unk4 - // unk5 - // unk6 - // skip? - // back? - uint16 font = argv[9].toUint16(); - uint16 mode = argv[10].toUint16(); - // unk + uint16 textX = argv[3].toUint16(); + uint16 textY = argv[4].toUint16(); + //reg_t unk5 = argv[5]; + //reg_t unk6 = argv[6]; + //reg_t unk7 = argv[7]; // skip? + //reg_t unk8 = argv[8]; // back? + //reg_t unk9 = argv[9]; + uint16 fontId = argv[10].toUint16(); + //uint16 mode = argv[11].toUint16(); uint16 dimmed = argv[12].toUint16(); - warning("kBitmap(4): bitmap ptr %04x:%04x, font %d, mode %d, dimmed %d - text: \"%s\"", - PRINT_REG(bitmapPtr), font, mode, dimmed, text.c_str()); + //warning("kBitmap(4): bitmap ptr %04x:%04x, font %d, mode %d, dimmed %d - text: \"%s\"", + // PRINT_REG(bitmapPtr), font, mode, dimmed, text.c_str()); + uint16 foreColor = 255; // TODO + + byte *memoryPtr = s->_segMan->getHunkPointer(hunkId); + // Get totalWidth, totalHeight + uint16 totalWidth = READ_LE_UINT16(memoryPtr); + uint16 totalHeight = READ_LE_UINT16(memoryPtr + 2); + byte *bitmap = memoryPtr + BITMAP_HEADER_SIZE; + + GfxFont *font = g_sci->_gfxCache->getFont(fontId); + + int16 charCount = 0; + uint16 curX = textX, curY = textY; + const char *txt = text.c_str(); + + while (*txt) { + charCount = g_sci->_gfxText32->GetLongest(txt, totalWidth, font); + if (charCount == 0) + break; + + for (int i = 0; i < charCount; i++) { + unsigned char curChar = txt[i]; + font->drawToBuffer(curChar, curY, curX, foreColor, dimmed, bitmap, totalWidth, totalHeight); + curX += font->getCharWidth(curChar); + } + + curX = textX; + curY += font->getHeight(); + txt += charCount; + while (*txt == ' ') + txt++; // skip over breaking spaces + } + } break; - case 5: + case 5: // fill with color { // 6 params, called e.g. from TextView::init() and TextView::draw() // in Torin's Passage, script 64890 - reg_t bitmapPtr = argv[1]; // obtained from kBitmap(0) - uint16 unk1 = argv[2].toUint16(); // unknown, usually 0, judging from scripts? - uint16 unk2 = argv[3].toUint16(); // unknown, usually 0, judging from scripts? - uint16 width = argv[4].toUint16(); // width - 1 - uint16 height = argv[5].toUint16(); // height - 1 + reg_t hunkId = argv[1]; // obtained from kBitmap(0) + uint16 x = argv[2].toUint16(); + uint16 y = argv[3].toUint16(); + uint16 fillWidth = argv[4].toUint16(); // width - 1 + uint16 fillHeight = argv[5].toUint16(); // height - 1 uint16 back = argv[6].toUint16(); - warning("kBitmap(5): bitmap ptr %04x:%04x, unk1 %d, unk2 %d, width %d, height %d, back %d", - PRINT_REG(bitmapPtr), unk1, unk2, width, height, back); + + byte *memoryPtr = s->_segMan->getHunkPointer(hunkId); + // Get totalWidth, totalHeight + 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; + + for (uint16 curY = 0; curY < height; curY++) { + for (uint16 curX = 0; curX < width; curX++) { + bitmap[(curY + y) * totalWidth + (curX + x)] = back; + } + } + } break; default: @@ -1759,6 +1799,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 68469f5c9a..83e59c9c20 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)) @@ -691,6 +691,16 @@ reg_t kArray(EngineState *s, int argc, reg_t *argv) { } case 2: { // At (return value at an index) SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]); + if (g_sci->getGameId() == GID_PHANTASMAGORIA2) { + // HACK: Phantasmagoria 2 keeps trying to access past the end of an + // array when it starts. I'm assuming it's trying to see where the + // array ends, or tries to resize it. Adjust the array size + // accordingly, and return NULL for now. + if (array->getSize() == argv[2].toUint16()) { + array->setSize(argv[2].toUint16()); + return NULL_REG; + } + } return array->getValue(argv[2].toUint16()); } case 3: { // Atput (put value at an index) diff --git a/engines/sci/engine/kmovement.cpp b/engines/sci/engine/kmovement.cpp index 14f7db47a0..649a1428a0 100644 --- a/engines/sci/engine/kmovement.cpp +++ b/engines/sci/engine/kmovement.cpp @@ -505,116 +505,6 @@ reg_t kDoAvoider(EngineState *s, int argc, reg_t *argv) { } writeSelectorValue(segMan, avoider, SELECTOR(heading), avoiderHeading); return s->r_acc; - -#if 0 - reg_t client, looper, mover; - int angle; - int dx, dy; - int destx, desty; - - s->r_acc = SIGNAL_REG; - - if (!s->_segMan->isHeapObject(avoider)) { - error("DoAvoider() where avoider %04x:%04x is not an object", PRINT_REG(avoider)); - return NULL_REG; - } - - client = readSelector(segMan, avoider, SELECTOR(client)); - - if (!s->_segMan->isHeapObject(client)) { - error("DoAvoider() where client %04x:%04x is not an object", PRINT_REG(client)); - return NULL_REG; - } - - looper = readSelector(segMan, client, SELECTOR(looper)); - mover = readSelector(segMan, client, SELECTOR(mover)); - - if (!s->_segMan->isHeapObject(mover)) { - if (mover.segment) { - error("DoAvoider() where mover %04x:%04x is not an object", PRINT_REG(mover)); - } - return s->r_acc; - } - - destx = readSelectorValue(segMan, mover, SELECTOR(x)); - desty = readSelectorValue(segMan, mover, SELECTOR(y)); - - debugC(kDebugLevelBresen, "Doing avoider %04x:%04x (dest=%d,%d)", PRINT_REG(avoider), destx, desty); - - invokeSelector(s, mover, SELECTOR(doit), argc, argv); - - mover = readSelector(segMan, client, SELECTOR(mover)); - if (!mover.segment) // Mover has been disposed? - return s->r_acc; // Return gracefully. - - invokeSelector(s, client, SELECTOR(isBlocked), argc, argv); - - dx = destx - readSelectorValue(segMan, client, SELECTOR(x)); - dy = desty - readSelectorValue(segMan, client, SELECTOR(y)); - angle = getAngle(dx, dy); - - debugC(kDebugLevelBresen, "Movement (%d,%d), angle %d is %sblocked", dx, dy, angle, (s->r_acc.offset) ? " " : "not "); - - if (s->r_acc.offset) { // isBlocked() returned non-zero - int rotation = (g_sci->getRNG().getRandomBit() == 1) ? 45 : (360 - 45); // Clockwise/counterclockwise - int oldx = readSelectorValue(segMan, client, SELECTOR(x)); - int oldy = readSelectorValue(segMan, client, SELECTOR(y)); - int xstep = readSelectorValue(segMan, client, SELECTOR(xStep)); - int ystep = readSelectorValue(segMan, client, SELECTOR(yStep)); - int moves; - - debugC(kDebugLevelBresen, " avoider %04x:%04x", PRINT_REG(avoider)); - - for (moves = 0; moves < 8; moves++) { - int move_x = (int)(sin(angle * M_PI / 180.0) * (xstep)); - int move_y = (int)(-cos(angle * M_PI / 180.0) * (ystep)); - - writeSelectorValue(segMan, client, SELECTOR(x), oldx + move_x); - writeSelectorValue(segMan, client, SELECTOR(y), oldy + move_y); - - debugC(kDebugLevelBresen, "Pos (%d,%d): Trying angle %d; delta=(%d,%d)", oldx, oldy, angle, move_x, move_y); - - invokeSelector(s, client, SELECTOR(canBeHere), argc, argv); - - writeSelectorValue(segMan, client, SELECTOR(x), oldx); - writeSelectorValue(segMan, client, SELECTOR(y), oldy); - - if (s->r_acc.offset) { // We can be here - debugC(kDebugLevelBresen, "Success"); - writeSelectorValue(segMan, client, SELECTOR(heading), angle); - - return make_reg(0, angle); - } - - angle += rotation; - - if (angle > 360) - angle -= 360; - } - - error("DoAvoider failed for avoider %04x:%04x", PRINT_REG(avoider)); - } else { - int heading = readSelectorValue(segMan, client, SELECTOR(heading)); - - if (heading == -1) - return s->r_acc; // No change - - writeSelectorValue(segMan, client, SELECTOR(heading), angle); - - s->r_acc = make_reg(0, angle); - - if (looper.segment) { - reg_t params[2] = { make_reg(0, angle), client }; - invokeSelector(s, looper, SELECTOR(doit), argc, argv, 2, params); - return s->r_acc; - } else { - // No looper? Fall back to DirLoop - kDirLoopWorker(client, (uint16)angle, s, argc, argv); - } - } - - return s->r_acc; -#endif } } // End of namespace Sci diff --git a/engines/sci/engine/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..5ae18c1367 100644 --- a/engines/sci/engine/kstring.cpp +++ b/engines/sci/engine/kstring.cpp @@ -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; } diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp index 6d810d516c..c9cf652013 100644 --- a/engines/sci/engine/kvideo.cpp +++ b/engines/sci/engine/kvideo.cpp @@ -21,6 +21,7 @@ */ #include "engines/util.h" +#include "sci/engine/kernel.h" #include "sci/engine/state.h" #include "sci/graphics/helpers.h" #include "sci/graphics/cursor.h" @@ -39,6 +40,7 @@ #include "sci/video/seq_decoder.h" #ifdef ENABLE_SCI32 #include "video/coktel_decoder.h" +#include "sci/video/robot_decoder.h" #endif namespace Sci { @@ -230,6 +232,49 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { #ifdef ENABLE_SCI32 +reg_t kRobot(EngineState *s, int argc, reg_t *argv) { + int16 subop = argv[0].toUint16(); + + switch (subop) { + case 0: { // init + int id = argv[1].toUint16(); + reg_t obj = argv[2]; + int16 flag = argv[3].toSint16(); + int16 x = argv[4].toUint16(); + int16 y = argv[5].toUint16(); + warning("kRobot(init), id %d, obj %04x:%04x, flag %d, x=%d, y=%d", id, PRINT_REG(obj), flag, x, y); + g_sci->_robotDecoder->load(id); + g_sci->_robotDecoder->setPos(x, y); + } + break; + case 1: // LSL6 hires (startup) + // TODO + return NULL_REG; // an integer is expected + case 4: { // start - we don't really have a use for this one + //int id = argv[1].toUint16(); + //warning("kRobot(start), id %d", id); + } + break; + case 7: // unknown, called e.g. by Phantasmagoria + warning("kRobot(%d)", subop); + break; + case 8: // sync + if ((uint32)g_sci->_robotDecoder->getCurFrame() != g_sci->_robotDecoder->getFrameCount() - 1) { + writeSelector(s->_segMan, argv[1], SELECTOR(signal), NULL_REG); + } else { + g_sci->_robotDecoder->close(); + // Signal the engine scripts that the video is done + writeSelector(s->_segMan, argv[1], SELECTOR(signal), SIGNAL_REG); + } + break; + default: + warning("kRobot(%d)", subop); + break; + } + + return s->r_acc; +} + reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv) { uint16 operation = argv[0].toUint16(); Video::VideoDecoder *videoDecoder = 0; @@ -331,6 +376,55 @@ reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } +reg_t kPlayDuck(EngineState *s, int argc, reg_t *argv) { + uint16 operation = argv[0].toUint16(); + Video::VideoDecoder *videoDecoder = 0; + bool reshowCursor = g_sci->_gfxCursor->isVisible(); + + switch (operation) { + case 1: // Play + // 6 params + s->_videoState.reset(); + s->_videoState.fileName = Common::String::format("%d.duk", argv[1].toUint16()); + + videoDecoder = new Video::AviDecoder(g_system->getMixer()); + + if (!videoDecoder->loadFile(s->_videoState.fileName)) { + warning("Could not open Duck %s", s->_videoState.fileName.c_str()); + break; + } + + if (reshowCursor) + g_sci->_gfxCursor->kernelHide(); + + { + // Duck videos are 16bpp, so we need to change the active pixel format + int oldWidth = g_system->getWidth(); + int oldHeight = g_system->getHeight(); + Common::List<Graphics::PixelFormat> formats; + formats.push_back(videoDecoder->getPixelFormat()); + initGraphics(640, 480, true, formats); + + if (g_system->getScreenFormat().bytesPerPixel != videoDecoder->getPixelFormat().bytesPerPixel) + error("Could not switch screen format for the duck video"); + + playVideo(videoDecoder, s->_videoState); + + // Switch back to 8bpp + initGraphics(oldWidth, oldHeight, oldWidth > 320); + } + + if (reshowCursor) + g_sci->_gfxCursor->kernelShow(); + break; + default: + kStub(s, argc, argv); + break; + } + + return s->r_acc; +} + #endif } // End of namespace Sci diff --git a/engines/sci/engine/object.cpp b/engines/sci/engine/object.cpp index a1854a2723..78e216cdb5 100644 --- a/engines/sci/engine/object.cpp +++ b/engines/sci/engine/object.cpp @@ -202,9 +202,10 @@ bool Object::initBaseObject(SegManager *segMan, reg_t addr, bool doInitSuperClas name = "<invalid name>"; } - warning("Object %04x:%04x (name %s, script %d) varnum doesn't " - "match baseObj's: obj %d, base %d", PRINT_REG(_pos), - name, objScript, originalVarCount, baseObj->getVarCount()); + debugC(kDebugLevelVM, "Object %04x:%04x (name %s, script %d) " + "varnum doesn't match baseObj's: obj %d, base %d", + PRINT_REG(_pos), name, objScript, + originalVarCount, baseObj->getVarCount()); #if 0 // We enumerate the methods selectors which could be hidden here diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp index c30518ab42..f1c7133d01 100644 --- a/engines/sci/engine/savegame.cpp +++ b/engines/sci/engine/savegame.cpp @@ -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()); } } } @@ -545,8 +547,6 @@ void DataStack::saveLoadWithSerializer(Common::Serializer &s) { void SciMusic::saveLoadWithSerializer(Common::Serializer &s) { // Sync song lib data. When loading, the actual song lib will be initialized // afterwards in gamestate_restore() - Common::StackLock lock(_mutex); - int songcount = 0; byte masterVolume = soundGetMasterVolume(); byte reverb = _pMidiDrv->getReverb(); @@ -576,9 +576,12 @@ void SciMusic::saveLoadWithSerializer(Common::Serializer &s) { songcount = _playList.size(); s.syncAsUint32LE(songcount); - if (s.isLoading()) { + if (s.isLoading()) clearPlayList(); + Common::StackLock lock(_mutex); + + if (s.isLoading()) { for (int i = 0; i < songcount; i++) { MusicEntry *curSong = new MusicEntry(); curSong->saveLoadWithSerializer(s); 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/scriptdebug.cpp b/engines/sci/engine/scriptdebug.cpp index ad3f4fb788..554a6b6a2c 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; diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp index 1510af8508..04c61f7b7c 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; 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..73d81baf3a 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 diff --git a/engines/sci/engine/selector.cpp b/engines/sci/engine/selector.cpp index c2f857f319..a8b1cf7ec2 100644 --- a/engines/sci/engine/selector.cpp +++ b/engines/sci/engine/selector.cpp @@ -167,6 +167,7 @@ void Kernel::mapSelectors() { #ifdef ENABLE_SCI32 FIND_SELECTOR(data); FIND_SELECTOR(picture); + FIND_SELECTOR(bitmap); FIND_SELECTOR(plane); FIND_SELECTOR(top); FIND_SELECTOR(left); @@ -177,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 085dd6e832..4b913a866a 100644 --- a/engines/sci/engine/selector.h +++ b/engines/sci/engine/selector.h @@ -132,6 +132,7 @@ struct SelectorCache { #ifdef ENABLE_SCI32 Selector data; // Used by Array()/String() Selector picture; // Used to hold the picture ID for SCI32 pictures + Selector bitmap; // Used to hold the text bitmap for SCI32 texts Selector plane; Selector top; @@ -143,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/static_selectors.cpp b/engines/sci/engine/static_selectors.cpp index 8f3337743d..74d2851024 100644 --- a/engines/sci/engine/static_selectors.cpp +++ b/engines/sci/engine/static_selectors.cpp @@ -192,9 +192,6 @@ Common::StringArray Kernel::checkStaticSelectorNames() { for (int i = count + countSci1; i < count + countSci1 + countSci11; i++) names[i] = sci11Selectors[i - count - countSci1]; } - - findSpecificSelectors(names); - #ifdef ENABLE_SCI32 } else { // SCI2+ @@ -203,6 +200,8 @@ Common::StringArray Kernel::checkStaticSelectorNames() { #endif } + findSpecificSelectors(names); + for (const SelectorRemap *selectorRemap = sciSelectorRemap; selectorRemap->slot; ++selectorRemap) { if (getSciVersion() >= selectorRemap->minVersion && getSciVersion() <= selectorRemap->maxVersion) { const uint32 slot = selectorRemap->slot; @@ -223,13 +222,16 @@ void Kernel::findSpecificSelectors(Common::StringArray &selectorNames) { // We need to initialize script 0 here, to make sure that it's always // located at segment 1. _segMan->instantiateScript(0); + uint16 sci2Offset = (getSciVersion() >= SCI_VERSION_2) ? 64000 : 0; // The Actor class contains the init, xLast and yLast selectors, which // we reference directly. It's always in script 998, so we need to // explicitly load it here. if ((getSciVersion() >= SCI_VERSION_1_EGA_ONLY)) { - if (_resMan->testResource(ResourceId(kResourceTypeScript, 998))) { - _segMan->instantiateScript(998); + uint16 actorScript = 998; + + if (_resMan->testResource(ResourceId(kResourceTypeScript, actorScript + sci2Offset))) { + _segMan->instantiateScript(actorScript + sci2Offset); const Object *actorClass = _segMan->getObject(_segMan->findObjectByName("Actor")); @@ -237,9 +239,10 @@ void Kernel::findSpecificSelectors(Common::StringArray &selectorNames) { // Find the xLast and yLast selectors, used in kDoBresen const int offset = (getSciVersion() < SCI_VERSION_1_1) ? 3 : 0; + const int offset2 = (getSciVersion() >= SCI_VERSION_2) ? 12 : 0; // xLast and yLast always come between illegalBits and xStep - int illegalBitsSelectorPos = actorClass->locateVarSelector(_segMan, 15 + offset); // illegalBits - int xStepSelectorPos = actorClass->locateVarSelector(_segMan, 51 + offset); // xStep + int illegalBitsSelectorPos = actorClass->locateVarSelector(_segMan, 15 + offset + offset2); // illegalBits + int xStepSelectorPos = actorClass->locateVarSelector(_segMan, 51 + offset + offset2); // xStep if (xStepSelectorPos - illegalBitsSelectorPos != 3) { error("illegalBits and xStep selectors aren't found in " "known locations. illegalBits = %d, xStep = %d", @@ -263,10 +266,10 @@ void Kernel::findSpecificSelectors(Common::StringArray &selectorNames) { // Find selectors from specific classes for (int i = 0; i < ARRAYSIZE(classReferences); i++) { - if (!_resMan->testResource(ResourceId(kResourceTypeScript, classReferences[i].script))) + if (!_resMan->testResource(ResourceId(kResourceTypeScript, classReferences[i].script + sci2Offset))) continue; - _segMan->instantiateScript(classReferences[i].script); + _segMan->instantiateScript(classReferences[i].script + sci2Offset); const Object *targetClass = _segMan->getObject(_segMan->findObjectByName(classReferences[i].className)); int targetSelectorPos = 0; diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp index 7c22b48ece..cbe4736ba2 100644 --- a/engines/sci/engine/vm.cpp +++ b/engines/sci/engine/vm.cpp @@ -232,11 +232,10 @@ ExecStack *execute_method(EngineState *s, uint16 script, uint16 pubfunct, StackP if (!temp) { #ifdef ENABLE_SCI32 - // HACK: Temporarily switch to a warning in SCI32 games until we can figure out why Torin has - // an invalid exported function. - if (getSciVersion() >= SCI_VERSION_2) - warning("Request for invalid exported function 0x%x of script %d", pubfunct, script); - else + if (g_sci->getGameId() == GID_TORIN && script == 64036) { + // Script 64036 in Torin's Passage is empty and contains an invalid + // (empty) export + } else #endif error("Request for invalid exported function 0x%x of script %d", pubfunct, script); return NULL; @@ -463,10 +462,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++]; @@ -594,15 +593,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; } @@ -771,16 +764,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 ac8d5fa262..f68b74e1e0 100644 --- a/engines/sci/engine/workarounds.cpp +++ b/engines/sci/engine/workarounds.cpp @@ -124,6 +124,7 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = { { GID_MOTHERGOOSEHIRES,-1,64950, 1, "View", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // see above { GID_PEPPER, -1, 894, 0, "Package", "doVerb", -1, 3, { WORKAROUND_FAKE, 0 } }, // using the hand on the book in the inventory - bug #3040012 { GID_PEPPER, 150, 928, 0, "Narrator", "startText", -1, 0, { WORKAROUND_FAKE, 0 } }, // happens during the non-interactive demo of Pepper + { GID_PQ4, -1, 25, 0, "iconToggle", "select", -1, 1, { WORKAROUND_FAKE, 0 } }, // when toggling the icon bar to auto-hide or not { GID_PQSWAT, -1, 64950, 0, "View", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // Using the menu in the beginning { GID_QFG1, -1, 210, 0, "Encounter", "init", 0xbd0, 0, { WORKAROUND_FAKE, 0 } }, // hq1: going to the brigands hideout { GID_QFG1, -1, 210, 0, "Encounter", "init", 0xbe4, 0, { WORKAROUND_FAKE, 0 } }, // qfg1: going to the brigands hideout @@ -154,6 +155,9 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = { { GID_SHIVERS, -1, 952, 0, "SoundManager", "stop", -1, 2, { WORKAROUND_FAKE, 0 } }, // Just after Sierra logo { GID_SHIVERS, -1, 64950, 0, "Feature", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // When clicking on the locked door at the beginning { GID_SHIVERS, -1, 64950, 0, "View", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // When clicking on the gargoyle eye at the beginning + { GID_SHIVERS, 20311, 64964, 0, "DPath", "init", -1, 1, { WORKAROUND_FAKE, 0 } }, // Just after door puzzle is solved and the metal balls start to roll + { GID_SHIVERS, 29260, 29260, 0, "spMars", "handleEvent", -1, 4, { WORKAROUND_FAKE, 0 } }, // When clicking mars after seeing fortune to align earth etc... + { GID_SHIVERS, 29260, 29260, 0, "spVenus", "handleEvent", -1, 4, { WORKAROUND_FAKE, 0 } }, // When clicking venus after seeing fortune to align earth etc... { GID_SQ1, 103, 103, 0, "hand", "internalEvent", -1, -1, { WORKAROUND_FAKE, 0 } }, // Spanish (and maybe early versions?) only: when moving cursor over input pad, temps 1 and 2 { GID_SQ1, -1, 703, 0, "", "export 1", -1, 0, { WORKAROUND_FAKE, 0 } }, // sub that's called from several objects while on sarien battle cruiser { GID_SQ1, -1, 703, 0, "firePulsar", "changeState", 0x18a, 0, { WORKAROUND_FAKE, 0 } }, // export 1, but called locally (when shooting at aliens) @@ -383,7 +387,7 @@ const SciWorkaroundEntry kStrLen_workarounds[] = { const SciWorkaroundEntry kUnLoad_workarounds[] = { { GID_ECOQUEST, 380, 61, 0, "gotIt", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // CD version: after talking to the dolphin the first time, a 3rd parameter is passed by accident { GID_ECOQUEST, 380, 69, 0, "lookAtBlackBoard", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // German version, when closing the blackboard closeup in the dolphin room, a 3rd parameter is passed by accident - bug #3098353 - { GID_LAURABOW2, -1, 1, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during the intro, a 3rd parameter is passed by accident - bug #3034902 + { GID_LAURABOW2, -1, -1, 0, "sCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: during the intro, a 3rd parameter is passed by accident - bug #3034902 { GID_LSL6, 130, 130, 0, "recruitLarryScr", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during intro, a 3rd parameter is passed by accident { GID_LSL6, 740, 740, 0, "showCartoon", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during ending, 4 additional parameters are passed by accident { GID_LSL6HIRES, 130, 130, 0, "recruitLarryScr", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during intro, a 3rd parameter is passed by accident |