aboutsummaryrefslogtreecommitdiff
path: root/engines/sci
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sci')
-rw-r--r--engines/sci/console.cpp223
-rw-r--r--engines/sci/console.h4
-rw-r--r--engines/sci/detection.cpp89
-rw-r--r--engines/sci/detection_tables.h880
-rw-r--r--engines/sci/engine/features.cpp42
-rw-r--r--engines/sci/engine/features.h7
-rw-r--r--engines/sci/engine/file.cpp464
-rw-r--r--engines/sci/engine/file.h140
-rw-r--r--engines/sci/engine/gc.cpp10
-rw-r--r--engines/sci/engine/gc.h2
-rw-r--r--engines/sci/engine/kernel.cpp153
-rw-r--r--engines/sci/engine/kernel.h35
-rw-r--r--engines/sci/engine/kernel_tables.h152
-rw-r--r--engines/sci/engine/kevent.cpp6
-rw-r--r--engines/sci/engine/kfile.cpp1245
-rw-r--r--engines/sci/engine/kgraphics.cpp723
-rw-r--r--engines/sci/engine/kgraphics32.cpp812
-rw-r--r--engines/sci/engine/klists.cpp42
-rw-r--r--engines/sci/engine/kmath.cpp11
-rw-r--r--engines/sci/engine/kmenu.cpp2
-rw-r--r--engines/sci/engine/kmisc.cpp85
-rw-r--r--engines/sci/engine/kparse.cpp2
-rw-r--r--engines/sci/engine/kpathing.cpp644
-rw-r--r--engines/sci/engine/kscripts.cpp21
-rw-r--r--engines/sci/engine/ksound.cpp15
-rw-r--r--engines/sci/engine/kstring.cpp47
-rw-r--r--engines/sci/engine/kvideo.cpp61
-rw-r--r--engines/sci/engine/message.cpp20
-rw-r--r--engines/sci/engine/object.cpp31
-rw-r--r--engines/sci/engine/object.h8
-rw-r--r--engines/sci/engine/savegame.cpp24
-rw-r--r--engines/sci/engine/script.cpp142
-rw-r--r--engines/sci/engine/script.h12
-rw-r--r--engines/sci/engine/script_patches.cpp69
-rw-r--r--engines/sci/engine/scriptdebug.cpp125
-rw-r--r--engines/sci/engine/seg_manager.cpp172
-rw-r--r--engines/sci/engine/seg_manager.h10
-rw-r--r--engines/sci/engine/segment.cpp50
-rw-r--r--engines/sci/engine/segment.h14
-rw-r--r--engines/sci/engine/selector.cpp1
-rw-r--r--engines/sci/engine/selector.h3
-rw-r--r--engines/sci/engine/state.cpp17
-rw-r--r--engines/sci/engine/state.h51
-rw-r--r--engines/sci/engine/vm.cpp118
-rw-r--r--engines/sci/engine/vm.h14
-rw-r--r--engines/sci/engine/vm_types.cpp14
-rw-r--r--engines/sci/engine/vm_types.h90
-rw-r--r--engines/sci/engine/workarounds.cpp18
-rw-r--r--engines/sci/event.cpp4
-rw-r--r--engines/sci/graphics/animate.cpp2
-rw-r--r--engines/sci/graphics/controls16.cpp2
-rw-r--r--engines/sci/graphics/controls32.cpp6
-rw-r--r--engines/sci/graphics/cursor.cpp12
-rw-r--r--engines/sci/graphics/font.cpp2
-rw-r--r--engines/sci/graphics/frameout.cpp364
-rw-r--r--engines/sci/graphics/frameout.h53
-rw-r--r--engines/sci/graphics/maciconbar.cpp22
-rw-r--r--engines/sci/graphics/maciconbar.h2
-rw-r--r--engines/sci/graphics/menu.cpp12
-rw-r--r--engines/sci/graphics/paint16.cpp16
-rw-r--r--engines/sci/graphics/palette.cpp94
-rw-r--r--engines/sci/graphics/palette.h21
-rw-r--r--engines/sci/graphics/ports.cpp41
-rw-r--r--engines/sci/graphics/screen.cpp48
-rw-r--r--engines/sci/graphics/screen.h1
-rw-r--r--engines/sci/graphics/text32.cpp74
-rw-r--r--engines/sci/graphics/text32.h6
-rw-r--r--engines/sci/graphics/view.cpp30
-rw-r--r--engines/sci/module.mk2
-rw-r--r--engines/sci/resource.cpp17
-rw-r--r--engines/sci/resource.h18
-rw-r--r--engines/sci/sci.cpp19
-rw-r--r--engines/sci/sci.h20
-rw-r--r--engines/sci/sound/audio.cpp3
-rw-r--r--engines/sci/sound/drivers/adlib.cpp3
-rw-r--r--engines/sci/sound/drivers/fmtowns.cpp12
-rw-r--r--engines/sci/sound/drivers/midi.cpp2
-rw-r--r--engines/sci/sound/midiparser_sci.cpp80
-rw-r--r--engines/sci/sound/midiparser_sci.h2
-rw-r--r--engines/sci/sound/music.cpp14
-rw-r--r--engines/sci/sound/soundcmd.cpp59
-rw-r--r--engines/sci/video/robot_decoder.cpp360
-rw-r--r--engines/sci/video/robot_decoder.h120
-rw-r--r--engines/sci/video/seq_decoder.cpp61
-rw-r--r--engines/sci/video/seq_decoder.h67
85 files changed, 5438 insertions, 3153 deletions
diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp
index 9607a8e66d..5ae8245e5a 100644
--- a/engines/sci/console.cpp
+++ b/engines/sci/console.cpp
@@ -53,6 +53,7 @@
#include "video/avi_decoder.h"
#include "sci/video/seq_decoder.h"
#ifdef ENABLE_SCI32
+#include "sci/graphics/frameout.h"
#include "video/coktel_decoder.h"
#include "sci/video/robot_decoder.h"
#endif
@@ -131,6 +132,10 @@ Console::Console(SciEngine *engine) : GUI::Debugger(),
DCmd_Register("al", WRAP_METHOD(Console, cmdAnimateList)); // alias
DCmd_Register("window_list", WRAP_METHOD(Console, cmdWindowList));
DCmd_Register("wl", WRAP_METHOD(Console, cmdWindowList)); // alias
+ DCmd_Register("plane_list", WRAP_METHOD(Console, cmdPlaneList));
+ DCmd_Register("pl", WRAP_METHOD(Console, cmdPlaneList)); // alias
+ DCmd_Register("plane_items", WRAP_METHOD(Console, cmdPlaneItemList));
+ DCmd_Register("pi", WRAP_METHOD(Console, cmdPlaneItemList)); // alias
DCmd_Register("saved_bits", WRAP_METHOD(Console, cmdSavedBits));
DCmd_Register("show_saved_bits", WRAP_METHOD(Console, cmdShowSavedBits));
// Segments
@@ -245,20 +250,18 @@ void Console::postEnter() {
#endif
if (_videoFile.hasSuffix(".seq")) {
- SeqDecoder *seqDecoder = new SeqDecoder();
- seqDecoder->setFrameDelay(_videoFrameDelay);
- videoDecoder = seqDecoder;
+ videoDecoder = new SEQDecoder(_videoFrameDelay);
#ifdef ENABLE_SCI32
} else if (_videoFile.hasSuffix(".vmd")) {
- videoDecoder = new Video::VMDDecoder(g_system->getMixer());
+ videoDecoder = new Video::AdvancedVMDDecoder();
} else if (_videoFile.hasSuffix(".rbt")) {
- videoDecoder = new RobotDecoder(g_system->getMixer(), _engine->getPlatform() == Common::kPlatformMacintosh);
+ videoDecoder = new RobotDecoder(_engine->getPlatform() == Common::kPlatformMacintosh);
} else if (_videoFile.hasSuffix(".duk")) {
duckMode = true;
- videoDecoder = new Video::AviDecoder(g_system->getMixer());
+ videoDecoder = new Video::AVIDecoder();
#endif
} else if (_videoFile.hasSuffix(".avi")) {
- videoDecoder = new Video::AviDecoder(g_system->getMixer());
+ videoDecoder = new Video::AVIDecoder();
} else {
warning("Unrecognized video type");
}
@@ -365,7 +368,10 @@ bool Console::cmdHelp(int argc, const char **argv) {
DebugPrintf(" pic_visualize - Enables visualization of the drawing process of EGA pictures\n");
DebugPrintf(" undither - Enable/disable undithering\n");
DebugPrintf(" play_video - Plays a SEQ, AVI, VMD, RBT or DUK video\n");
- DebugPrintf(" animate_object_list / al - Shows the current list of objects in kAnimate's draw list\n");
+ DebugPrintf(" animate_list / al - Shows the current list of objects in kAnimate's draw list (SCI0 - SCI1.1)\n");
+ DebugPrintf(" window_list / wl - Shows a list of all the windows (ports) in the draw list (SCI0 - SCI1.1)\n");
+ DebugPrintf(" plane_list / pl - Shows a list of all the planes in the draw list (SCI2+)\n");
+ DebugPrintf(" plane_items / pi - Shows a list of all items for a plane (SCI2+)\n");
DebugPrintf(" saved_bits - List saved bits on the hunk\n");
DebugPrintf(" show_saved_bits - Display saved bits\n");
DebugPrintf("\n");
@@ -1209,12 +1215,33 @@ bool Console::cmdRestartGame(int argc, const char **argv) {
return Cmd_Exit(0, 0);
}
+// The scripts get IDs ranging from 100->199, because the scripts require us to assign unique ids THAT EVEN STAY BETWEEN
+// SAVES and the scripts also use "saves-count + 1" to create a new savedgame slot.
+// SCI1.1 actually recycles ids, in that case we will currently get "0".
+// This behavior is required especially for LSL6. In this game, it's possible to quick save. The scripts will use
+// the last-used id for that feature. If we don't assign sticky ids, the feature will overwrite different saves all the
+// time. And sadly we can't just use the actual filename ids directly, because of the creation method for new slots.
+
+extern void listSavegames(Common::Array<SavegameDesc> &saves);
+
+bool Console::cmdListSaves(int argc, const char **argv) {
+ Common::Array<SavegameDesc> saves;
+ listSavegames(saves);
+
+ for (uint i = 0; i < saves.size(); i++) {
+ Common::String filename = g_sci->getSavegameName(saves[i].id);
+ DebugPrintf("%s: '%s'\n", filename.c_str(), saves[i].name);
+ }
+
+ return true;
+}
+
bool Console::cmdClassTable(int argc, const char **argv) {
DebugPrintf("Available classes (parse a parameter to filter the table by a specific class):\n");
for (uint i = 0; i < _engine->_gamestate->_segMan->classTableSize(); i++) {
Class temp = _engine->_gamestate->_segMan->_classTable[i];
- if (temp.reg.segment) {
+ if (temp.reg.getSegment()) {
const char *className = _engine->_gamestate->_segMan->getObjectName(temp.reg);
if (argc == 1 || (argc == 2 && !strcmp(className, argv[1]))) {
DebugPrintf(" Class 0x%x (%s) at %04x:%04x (script %d)\n", i,
@@ -1589,6 +1616,8 @@ bool Console::cmdAnimateList(int argc, const char **argv) {
if (_engine->_gfxAnimate) {
DebugPrintf("Animate list:\n");
_engine->_gfxAnimate->printAnimateList(this);
+ } else {
+ DebugPrintf("This SCI version does not have an animate list\n");
}
return true;
}
@@ -1597,9 +1626,52 @@ bool Console::cmdWindowList(int argc, const char **argv) {
if (_engine->_gfxPorts) {
DebugPrintf("Window list:\n");
_engine->_gfxPorts->printWindowList(this);
+ } else {
+ DebugPrintf("This SCI version does not have a list of ports\n");
}
return true;
+}
+
+bool Console::cmdPlaneList(int argc, const char **argv) {
+#ifdef ENABLE_SCI32
+ if (_engine->_gfxFrameout) {
+ DebugPrintf("Plane list:\n");
+ _engine->_gfxFrameout->printPlaneList(this);
+ } else {
+ DebugPrintf("This SCI version does not have a list of planes\n");
+ }
+#else
+ DebugPrintf("SCI32 isn't included in this compiled executable\n");
+#endif
+ return true;
+}
+bool Console::cmdPlaneItemList(int argc, const char **argv) {
+ if (argc != 2) {
+ DebugPrintf("Shows the list of items for a plane\n");
+ DebugPrintf("Usage: %s <plane address>\n", argv[0]);
+ return true;
+ }
+
+ reg_t planeObject = NULL_REG;
+
+ if (parse_reg_t(_engine->_gamestate, argv[1], &planeObject, false)) {
+ DebugPrintf("Invalid address passed.\n");
+ DebugPrintf("Check the \"addresses\" command on how to use addresses\n");
+ return true;
+ }
+
+#ifdef ENABLE_SCI32
+ if (_engine->_gfxFrameout) {
+ DebugPrintf("Plane item list:\n");
+ _engine->_gfxFrameout->printPlaneItemList(this, planeObject);
+ } else {
+ DebugPrintf("This SCI version does not have a list of plane items\n");
+ }
+#else
+ DebugPrintf("SCI32 isn't included in this compiled executable\n");
+#endif
+ return true;
}
bool Console::cmdSavedBits(int argc, const char **argv) {
@@ -1614,7 +1686,7 @@ bool Console::cmdSavedBits(int argc, const char **argv) {
Common::Array<reg_t> entries = hunks->listAllDeallocatable(id);
for (uint i = 0; i < entries.size(); ++i) {
- uint16 offset = entries[i].offset;
+ uint16 offset = entries[i].getOffset();
const Hunk& h = hunks->_table[offset];
if (strcmp(h.type, "SaveBits()") == 0) {
byte* memoryPtr = (byte *)h.mem;
@@ -1677,12 +1749,12 @@ bool Console::cmdShowSavedBits(int argc, const char **argv) {
return true;
}
- if (memoryHandle.segment != id || !hunks->isValidOffset(memoryHandle.offset)) {
+ if (memoryHandle.getSegment() != id || !hunks->isValidOffset(memoryHandle.getOffset())) {
DebugPrintf("Invalid address.\n");
return true;
}
- const Hunk& h = hunks->_table[memoryHandle.offset];
+ const Hunk& h = hunks->_table[memoryHandle.getOffset()];
if (strcmp(h.type, "SaveBits()") != 0) {
DebugPrintf("Invalid address.\n");
@@ -2192,16 +2264,16 @@ bool Console::cmdGCShowReachable(int argc, const char **argv) {
return true;
}
- SegmentObj *mobj = _engine->_gamestate->_segMan->getSegmentObj(addr.segment);
+ SegmentObj *mobj = _engine->_gamestate->_segMan->getSegmentObj(addr.getSegment());
if (!mobj) {
- DebugPrintf("Unknown segment : %x\n", addr.segment);
+ DebugPrintf("Unknown segment : %x\n", addr.getSegment());
return 1;
}
DebugPrintf("Reachable from %04x:%04x:\n", PRINT_REG(addr));
const Common::Array<reg_t> tmp = mobj->listAllOutgoingReferences(addr);
for (Common::Array<reg_t>::const_iterator it = tmp.begin(); it != tmp.end(); ++it)
- if (it->segment)
+ if (it->getSegment())
g_sci->getSciDebugger()->DebugPrintf(" %04x:%04x\n", PRINT_REG(*it));
return true;
@@ -2224,16 +2296,16 @@ bool Console::cmdGCShowFreeable(int argc, const char **argv) {
return true;
}
- SegmentObj *mobj = _engine->_gamestate->_segMan->getSegmentObj(addr.segment);
+ SegmentObj *mobj = _engine->_gamestate->_segMan->getSegmentObj(addr.getSegment());
if (!mobj) {
- DebugPrintf("Unknown segment : %x\n", addr.segment);
+ DebugPrintf("Unknown segment : %x\n", addr.getSegment());
return true;
}
- DebugPrintf("Freeable in segment %04x:\n", addr.segment);
- const Common::Array<reg_t> tmp = mobj->listAllDeallocatable(addr.segment);
+ DebugPrintf("Freeable in segment %04x:\n", addr.getSegment());
+ const Common::Array<reg_t> tmp = mobj->listAllDeallocatable(addr.getSegment());
for (Common::Array<reg_t>::const_iterator it = tmp.begin(); it != tmp.end(); ++it)
- if (it->segment)
+ if (it->getSegment())
g_sci->getSciDebugger()->DebugPrintf(" %04x:%04x\n", PRINT_REG(*it));
return true;
@@ -2257,9 +2329,9 @@ bool Console::cmdGCNormalize(int argc, const char **argv) {
return true;
}
- SegmentObj *mobj = _engine->_gamestate->_segMan->getSegmentObj(addr.segment);
+ SegmentObj *mobj = _engine->_gamestate->_segMan->getSegmentObj(addr.getSegment());
if (!mobj) {
- DebugPrintf("Unknown segment : %x\n", addr.segment);
+ DebugPrintf("Unknown segment : %x\n", addr.getSegment());
return true;
}
@@ -2498,12 +2570,12 @@ bool Console::cmdViewReference(int argc, const char **argv) {
DebugPrintf("%04x:%04x is of type 0x%x: ", PRINT_REG(reg), type_mask);
- if (reg.segment == 0 && reg.offset == 0) {
+ if (reg.getSegment() == 0 && reg.getOffset() == 0) {
DebugPrintf("Null.\n");
return true;
}
- if (reg_end.segment != reg.segment && reg_end != NULL_REG) {
+ if (reg_end.getSegment() != reg.getSegment() && reg_end != NULL_REG) {
DebugPrintf("Ending segment different from starting segment. Assuming no bound on dump.\n");
reg_end = NULL_REG;
}
@@ -2539,7 +2611,7 @@ bool Console::cmdViewReference(int argc, const char **argv) {
printObject(reg);
break;
case SIG_TYPE_REFERENCE: {
- switch (_engine->_gamestate->_segMan->getSegmentType(reg.segment)) {
+ switch (_engine->_gamestate->_segMan->getSegmentType(reg.getSegment())) {
#ifdef ENABLE_SCI32
case SEG_TYPE_STRING: {
DebugPrintf("SCI32 string\n");
@@ -2555,21 +2627,20 @@ bool Console::cmdViewReference(int argc, const char **argv) {
}
#endif
default: {
- int size;
const SegmentRef block = _engine->_gamestate->_segMan->dereference(reg);
- size = block.maxSize;
+ uint16 size = block.maxSize;
DebugPrintf("raw data\n");
- if (reg_end.segment != 0 && size < reg_end.offset - reg.offset) {
+ if (reg_end.getSegment() != 0 && (size < reg_end.getOffset() - reg.getOffset())) {
DebugPrintf("Block end out of bounds (size %d). Resetting.\n", size);
reg_end = NULL_REG;
}
- if (reg_end.segment != 0 && (size >= reg_end.offset - reg.offset))
- size = reg_end.offset - reg.offset;
+ if (reg_end.getSegment() != 0 && (size >= reg_end.getOffset() - reg.getOffset()))
+ size = reg_end.getOffset() - reg.getOffset();
- if (reg_end.segment != 0)
+ if (reg_end.getSegment() != 0)
DebugPrintf("Block size less than or equal to %d\n", size);
if (block.isRaw)
@@ -2581,7 +2652,7 @@ bool Console::cmdViewReference(int argc, const char **argv) {
break;
}
case SIG_TYPE_INTEGER:
- DebugPrintf("arithmetic value\n %d (%04x)\n", (int16) reg.offset, reg.offset);
+ DebugPrintf("arithmetic value\n %d (%04x)\n", (int16) reg.getOffset(), reg.getOffset());
break;
default:
DebugPrintf("unknown type %d.\n", type);
@@ -2651,7 +2722,7 @@ bool Console::cmdBacktrace(int argc, const char **argv) {
switch (call.type) {
case EXEC_STACK_TYPE_CALL: // Normal function
if (call.type == EXEC_STACK_TYPE_CALL)
- DebugPrintf(" %x: script %d - ", i, (*(Script *)_engine->_gamestate->_segMan->_heap[call.addr.pc.segment]).getScriptNumber());
+ DebugPrintf(" %x: script %d - ", i, (*(Script *)_engine->_gamestate->_segMan->_heap[call.addr.pc.getSegment()]).getScriptNumber());
if (call.debugSelector != -1) {
DebugPrintf("%s::%s(", objname, _engine->getKernel()->getSelectorName(call.debugSelector).c_str());
} else if (call.debugExportId != -1) {
@@ -2811,17 +2882,17 @@ bool Console::cmdDisassemble(int argc, const char **argv) {
reg_t addr = NULL_REG;
if (!obj) {
- DebugPrintf("Not an object.");
+ DebugPrintf("Not an object.\n");
return true;
}
if (selectorId < 0) {
- DebugPrintf("Not a valid selector name.");
+ DebugPrintf("Not a valid selector name.\n");
return true;
}
if (lookupSelector(_engine->_gamestate->_segMan, objAddr, selectorId, NULL, &addr) != kSelectorMethod) {
- DebugPrintf("Not a method.");
+ DebugPrintf("Not a method.\n");
return true;
}
@@ -2840,10 +2911,11 @@ bool Console::cmdDisassemble(int argc, const char **argv) {
if (jumpTarget > farthestTarget)
farthestTarget = jumpTarget;
}
- addr = disassemble(_engine->_gamestate, addr, printBWTag, printBytecode);
+ // TODO: Use a true 32-bit reg_t for the position (addr)
+ addr = disassemble(_engine->_gamestate, make_reg32(addr.getSegment(), addr.getOffset()), printBWTag, printBytecode);
if (addr.isNull() && prevAddr < farthestTarget)
addr = prevAddr + 1; // skip past the ret
- } while (addr.offset > 0);
+ } while (addr.getOffset() > 0);
return true;
}
@@ -2860,10 +2932,10 @@ bool Console::cmdDisassembleAddress(int argc, const char **argv) {
}
reg_t vpc = NULL_REG;
- int opCount = 1;
+ uint opCount = 1;
bool printBWTag = false;
bool printBytes = false;
- int size;
+ uint16 size;
if (parse_reg_t(_engine->_gamestate, argv[1], &vpc, false)) {
DebugPrintf("Invalid address passed.\n");
@@ -2872,7 +2944,7 @@ bool Console::cmdDisassembleAddress(int argc, const char **argv) {
}
SegmentRef ref = _engine->_gamestate->_segMan->dereference(vpc);
- size = ref.maxSize + vpc.offset; // total segment size
+ size = ref.maxSize + vpc.getOffset(); // total segment size
for (int i = 2; i < argc; i++) {
if (!scumm_stricmp(argv[i], "bwt"))
@@ -2887,14 +2959,10 @@ bool Console::cmdDisassembleAddress(int argc, const char **argv) {
}
}
- if (opCount < 0) {
- DebugPrintf("Invalid op_count\n");
- return true;
- }
-
do {
- vpc = disassemble(_engine->_gamestate, vpc, printBWTag, printBytes);
- } while ((vpc.offset > 0) && (vpc.offset + 6 < size) && (--opCount));
+ // TODO: Use a true 32-bit reg_t for the position (vpc)
+ vpc = disassemble(_engine->_gamestate, make_reg32(vpc.getSegment(), vpc.getOffset()), printBWTag, printBytes);
+ } while ((vpc.getOffset() > 0) && (vpc.getOffset() + 6 < size) && (--opCount));
return true;
}
@@ -2917,8 +2985,9 @@ void Console::printKernelCallsFound(int kernelFuncNum, bool showFoundScripts) {
// Ignore specific leftover scripts, which require other non-existing scripts
if ((_engine->getGameId() == GID_HOYLE3 && itr->getNumber() == 995) ||
(_engine->getGameId() == GID_KQ5 && itr->getNumber() == 980) ||
- (_engine->getGameId() == GID_SLATER && itr->getNumber() == 947) ||
- (_engine->getGameId() == GID_MOTHERGOOSE256 && itr->getNumber() == 980)) {
+ (_engine->getGameId() == GID_KQ7 && itr->getNumber() == 111) ||
+ (_engine->getGameId() == GID_MOTHERGOOSE256 && itr->getNumber() == 980) ||
+ (_engine->getGameId() == GID_SLATER && itr->getNumber() == 947)) {
continue;
}
@@ -2937,7 +3006,7 @@ void Console::printKernelCallsFound(int kernelFuncNum, bool showFoundScripts) {
// Now dissassemble each method of the script object
for (uint16 i = 0; i < obj->getMethodCount(); i++) {
reg_t fptr = obj->getFunction(i);
- uint16 offset = fptr.offset;
+ uint16 offset = fptr.getOffset();
int16 opparams[4];
byte extOpcode;
byte opcode;
@@ -3584,10 +3653,14 @@ static int parse_reg_t(EngineState *s, const char *str, reg_t *dest, bool mayBeV
relativeOffset = true;
if (!scumm_strnicmp(str + 1, "PC", 2)) {
- *dest = s->_executionStack.back().addr.pc;
+ // TODO: Handle 32-bit PC addresses
+ reg32_t pc = s->_executionStack.back().addr.pc;
+ *dest = make_reg(pc.getSegment(), (uint16)pc.getOffset());
offsetStr = str + 3;
} else if (!scumm_strnicmp(str + 1, "P", 1)) {
- *dest = s->_executionStack.back().addr.pc;
+ // TODO: Handle 32-bit PC addresses
+ reg32_t pc = s->_executionStack.back().addr.pc;
+ *dest = make_reg(pc.getSegment(), (uint16)pc.getOffset());
offsetStr = str + 2;
} else if (!scumm_strnicmp(str + 1, "PREV", 4)) {
*dest = s->r_prev;
@@ -3625,8 +3698,8 @@ static int parse_reg_t(EngineState *s, const char *str, reg_t *dest, bool mayBeV
return 1;
// Now lookup the script's segment
- dest->segment = s->_segMan->getScriptSegment(script_nr);
- if (!dest->segment) {
+ dest->setSegment(s->_segMan->getScriptSegment(script_nr));
+ if (!dest->getSegment()) {
return 1;
}
@@ -3708,19 +3781,19 @@ static int parse_reg_t(EngineState *s, const char *str, reg_t *dest, bool mayBeV
offsetStr = colon + 1;
Common::String segmentStr(str, colon);
- dest->segment = strtol(segmentStr.c_str(), &endptr, 16);
+ dest->setSegment(strtol(segmentStr.c_str(), &endptr, 16));
if (*endptr)
return 1;
} else {
int val = 0;
- dest->segment = 0;
+ dest->setSegment(0);
if (charsCountNumber == charsCount) {
// Only numbers in input, assume decimal value
val = strtol(str, &endptr, 10);
if (*endptr)
return 1; // strtol failed?
- dest->offset = val;
+ dest->setOffset(val);
return 0;
} else {
// We also got letters, check if there were only hexadecimal letters and '0x' at the start or 'h' at the end
@@ -3728,7 +3801,7 @@ static int parse_reg_t(EngineState *s, const char *str, reg_t *dest, bool mayBeV
val = strtol(str, &endptr, 16);
if ((*endptr != 'h') && (*endptr != 0))
return 1;
- dest->offset = val;
+ dest->setOffset(val);
return 0;
} else {
// Something else was in input, assume object name
@@ -3777,9 +3850,9 @@ static int parse_reg_t(EngineState *s, const char *str, reg_t *dest, bool mayBeV
int val = strtol(offsetStr, &endptr, 16);
if (relativeOffset)
- dest->offset += val;
+ dest->incOffset(val);
else
- dest->offset = val;
+ dest->setOffset(val);
if (*endptr)
return 1;
@@ -3859,15 +3932,15 @@ void Console::printList(List *list) {
while (!pos.isNull()) {
Node *node;
- NodeTable *nt = (NodeTable *)_engine->_gamestate->_segMan->getSegment(pos.segment, SEG_TYPE_NODES);
+ NodeTable *nt = (NodeTable *)_engine->_gamestate->_segMan->getSegment(pos.getSegment(), SEG_TYPE_NODES);
- if (!nt || !nt->isValidEntry(pos.offset)) {
+ if (!nt || !nt->isValidEntry(pos.getOffset())) {
DebugPrintf(" WARNING: %04x:%04x: Doesn't contain list node!\n",
PRINT_REG(pos));
return;
}
- node = &(nt->_table[pos.offset]);
+ node = &(nt->_table[pos.getOffset()]);
DebugPrintf("\t%04x:%04x : %04x:%04x -> %04x:%04x\n", PRINT_REG(pos), PRINT_REG(node->key), PRINT_REG(node->value));
@@ -3886,37 +3959,37 @@ void Console::printList(List *list) {
}
int Console::printNode(reg_t addr) {
- SegmentObj *mobj = _engine->_gamestate->_segMan->getSegment(addr.segment, SEG_TYPE_LISTS);
+ SegmentObj *mobj = _engine->_gamestate->_segMan->getSegment(addr.getSegment(), SEG_TYPE_LISTS);
if (mobj) {
ListTable *lt = (ListTable *)mobj;
List *list;
- if (!lt->isValidEntry(addr.offset)) {
+ if (!lt->isValidEntry(addr.getOffset())) {
DebugPrintf("Address does not contain a list\n");
return 1;
}
- list = &(lt->_table[addr.offset]);
+ list = &(lt->_table[addr.getOffset()]);
DebugPrintf("%04x:%04x : first x last = (%04x:%04x, %04x:%04x)\n", PRINT_REG(addr), PRINT_REG(list->first), PRINT_REG(list->last));
} else {
NodeTable *nt;
Node *node;
- mobj = _engine->_gamestate->_segMan->getSegment(addr.segment, SEG_TYPE_NODES);
+ mobj = _engine->_gamestate->_segMan->getSegment(addr.getSegment(), SEG_TYPE_NODES);
if (!mobj) {
- DebugPrintf("Segment #%04x is not a list or node segment\n", addr.segment);
+ DebugPrintf("Segment #%04x is not a list or node segment\n", addr.getSegment());
return 1;
}
nt = (NodeTable *)mobj;
- if (!nt->isValidEntry(addr.offset)) {
+ if (!nt->isValidEntry(addr.getOffset())) {
DebugPrintf("Address does not contain a node\n");
return 1;
}
- node = &(nt->_table[addr.offset]);
+ node = &(nt->_table[addr.getOffset()]);
DebugPrintf("%04x:%04x : prev x next = (%04x:%04x, %04x:%04x); maps %04x:%04x -> %04x:%04x\n",
PRINT_REG(addr), PRINT_REG(node->pred), PRINT_REG(node->succ), PRINT_REG(node->key), PRINT_REG(node->value));
@@ -3954,8 +4027,8 @@ int Console::printObject(reg_t pos) {
reg_t val = obj->getVariable(i);
DebugPrintf("%04x:%04x", PRINT_REG(val));
- if (!val.segment)
- DebugPrintf(" (%d)", val.offset);
+ if (!val.getSegment())
+ DebugPrintf(" (%d)", val.getOffset());
const Object *ref = s->_segMan->getObject(val);
if (ref)
@@ -3968,8 +4041,8 @@ int Console::printObject(reg_t pos) {
reg_t fptr = obj->getFunction(i);
DebugPrintf(" [%03x] %s = %04x:%04x\n", obj->getFuncSelector(i), _engine->getKernel()->getSelectorName(obj->getFuncSelector(i)).c_str(), PRINT_REG(fptr));
}
- if (s->_segMan->_heap[pos.segment]->getType() == SEG_TYPE_SCRIPT)
- DebugPrintf("\nOwner script: %d\n", s->_segMan->getScript(pos.segment)->getScriptNumber());
+ if (s->_segMan->_heap[pos.getSegment()]->getType() == SEG_TYPE_SCRIPT)
+ DebugPrintf("\nOwner script: %d\n", s->_segMan->getScript(pos.getSegment())->getScriptNumber());
return 0;
}
diff --git a/engines/sci/console.h b/engines/sci/console.h
index d943923ba1..1c54748842 100644
--- a/engines/sci/console.h
+++ b/engines/sci/console.h
@@ -33,7 +33,7 @@ namespace Sci {
class SciEngine;
struct List;
-reg_t disassemble(EngineState *s, reg_t pos, bool printBWTag, bool printBytecode);
+reg_t disassemble(EngineState *s, reg32_t pos, bool printBWTag, bool printBytecode);
bool isJumpOpcode(EngineState *s, reg_t pos, reg_t& jumpOffset);
class Console : public GUI::Debugger {
@@ -94,6 +94,8 @@ private:
bool cmdPlayVideo(int argc, const char **argv);
bool cmdAnimateList(int argc, const char **argv);
bool cmdWindowList(int argc, const char **argv);
+ bool cmdPlaneList(int argc, const char **argv);
+ bool cmdPlaneItemList(int argc, const char **argv);
bool cmdSavedBits(int argc, const char **argv);
bool cmdShowSavedBits(int argc, const char **argv);
// Segments
diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp
index c43849056b..58ac5f1fa6 100644
--- a/engines/sci/detection.cpp
+++ b/engines/sci/detection.cpp
@@ -26,6 +26,7 @@
#include "common/ptr.h"
#include "common/savefile.h"
#include "common/system.h"
+#include "common/translation.h"
#include "graphics/thumbnail.h"
#include "graphics/surface.h"
@@ -362,6 +363,83 @@ Common::String convertSierraGameId(Common::String sierraId, uint32 *gameFlags, R
#include "sci/detection_tables.h"
+static const ADExtraGuiOptionsMap optionsList[] = {
+ {
+ GAMEOPTION_EGA_UNDITHER,
+ {
+ _s("EGA undithering"),
+ _s("Enable undithering in EGA games"),
+ "disable_dithering",
+ false
+ }
+ },
+
+ {
+ GAMEOPTION_PREFER_DIGITAL_SFX,
+ {
+ _s("Prefer digital sound effects"),
+ _s("Prefer digital sound effects instead of synthesized ones"),
+ "prefer_digitalsfx",
+ true
+ }
+ },
+
+ {
+ GAMEOPTION_ORIGINAL_SAVELOAD,
+ {
+ _s("Use original save/load screens"),
+ _s("Use the original save/load screens, instead of the ScummVM ones"),
+ "originalsaveload",
+ false
+ }
+ },
+
+ {
+ GAMEOPTION_FB01_MIDI,
+ {
+ _s("Use IMF/Yamaha FB-01 for MIDI output"),
+ _s("Use an IBM Music Feature card or a Yamaha FB-01 FM synth module for MIDI output"),
+ "native_fb01",
+ false
+ }
+ },
+
+ // Jones in the Fast Lane - CD audio tracks or resource.snd
+ {
+ GAMEOPTION_JONES_CDAUDIO,
+ {
+ _s("Use CD audio"),
+ _s("Use CD audio instead of in-game audio, if available"),
+ "use_cdaudio",
+ true
+ }
+ },
+
+ // KQ6 Windows - windows cursors
+ {
+ GAMEOPTION_KQ6_WINDOWS_CURSORS,
+ {
+ _s("Use Windows cursors"),
+ _s("Use the Windows cursors (smaller and monochrome) instead of the DOS ones"),
+ "windows_cursors",
+ false
+ }
+ },
+
+ // SQ4 CD - silver cursors
+ {
+ GAMEOPTION_SQ4_SILVER_CURSORS,
+ {
+ _s("Use silver cursors"),
+ _s("Use the alternate set of silver cursors, instead of the normal golden ones"),
+ "silver_cursors",
+ false
+ }
+ },
+
+ AD_EXTRA_GUI_OPTIONS_TERMINATOR
+};
+
/**
* The fallback game descriptor used by the SCI engine's fallbackDetector.
* Contents of this struct are overwritten by the fallbackDetector.
@@ -373,14 +451,14 @@ static ADGameDescription s_fallbackDesc = {
Common::UNK_LANG,
Common::kPlatformPC,
ADGF_NO_FLAGS,
- GUIO0()
+ GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI)
};
static char s_fallbackGameIdBuf[256];
class SciMetaEngine : public AdvancedMetaEngine {
public:
- SciMetaEngine() : AdvancedMetaEngine(Sci::SciGameDescriptions, sizeof(ADGameDescription), s_sciGameTitles) {
+ SciMetaEngine() : AdvancedMetaEngine(Sci::SciGameDescriptions, sizeof(ADGameDescription), s_sciGameTitles, optionsList) {
_singleid = "sci";
}
@@ -435,7 +513,7 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles,
s_fallbackDesc.flags = ADGF_NO_FLAGS;
s_fallbackDesc.platform = Common::kPlatformPC; // default to PC platform
s_fallbackDesc.gameid = "sci";
- s_fallbackDesc.guioptions = GUIO0();
+ s_fallbackDesc.guioptions = GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI);
if (allFiles.contains("resource.map") || allFiles.contains("Data1")
|| allFiles.contains("resmap.001") || allFiles.contains("resmap.001")) {
@@ -565,7 +643,7 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles,
const bool isCD = (s_fallbackDesc.flags & ADGF_CD);
if (!isCD)
- s_fallbackDesc.guioptions = GUIO1(GUIO_NOSPEECH);
+ s_fallbackDesc.guioptions = GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI);
if (gameId.hasSuffix("sci")) {
s_fallbackDesc.extra = "SCI";
@@ -684,9 +762,6 @@ SaveStateDescriptor SciMetaEngine::querySaveMetaInfos(const char *target, int sl
Graphics::Surface *const thumbnail = Graphics::loadThumbnail(*in);
desc.setThumbnail(thumbnail);
- desc.setDeletableFlag(true);
- desc.setWriteProtectedFlag(false);
-
int day = (meta.saveDate >> 24) & 0xFF;
int month = (meta.saveDate >> 16) & 0xFF;
int year = meta.saveDate & 0xFFFF;
diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h
index 63eda1c348..b978f40aba 100644
--- a/engines/sci/detection_tables.h
+++ b/engines/sci/detection_tables.h
@@ -22,6 +22,14 @@
namespace Sci {
+#define GAMEOPTION_PREFER_DIGITAL_SFX GUIO_GAMEOPTIONS1
+#define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS2
+#define GAMEOPTION_FB01_MIDI GUIO_GAMEOPTIONS3
+#define GAMEOPTION_JONES_CDAUDIO GUIO_GAMEOPTIONS4
+#define GAMEOPTION_KQ6_WINDOWS_CURSORS GUIO_GAMEOPTIONS5
+#define GAMEOPTION_SQ4_SILVER_CURSORS GUIO_GAMEOPTIONS6
+#define GAMEOPTION_EGA_UNDITHER GUIO_GAMEOPTIONS7
+
// SCI3 games have a different script format (in CSC files) and are currently unsupported
#define ENABLE_SCI3_GAMES
@@ -29,7 +37,7 @@ namespace Sci {
{"sci-fanmade", name, { \
{"resource.map", 0, resMapMd5, resMapSize}, \
{"resource.001", 0, resMd5, resSize}, \
- AD_LISTEND}, lang, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) \
+ AD_LISTEND}, lang, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) \
}
#define FANMADE(name, resMapMd5, resMapSize, resMd5, resSize) FANMADE_L(name, resMapMd5, resMapSize, resMd5, resSize, Common::EN_ANY)
@@ -42,7 +50,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "f3d1be7752d30ba60614533d531e2e98", 474},
{"resource.001", 0, "6fd05926c2199af0af6f72f90d0d7260", 126895},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Castle of Dr. Brain - English Amiga (from www.back2roots.org)
// Executable scanning reports "1.005.000"
@@ -54,7 +62,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "d226d7d3b4f77c4a566913fc310487fc", 792380},
{"resource.003", 0, "d226d7d3b4f77c4a566913fc310487fc", 464348},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Castle of Dr. Brain - German Amiga (from www.back2roots.org, also includes English language)
// Executable scanning reports "1.005.001"
@@ -66,7 +74,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "85e51acb5f9c539d66e3c8fe40e17da5", 826309},
{"resource.003", 0, "85e51acb5f9c539d66e3c8fe40e17da5", 493638},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformAmiga, ADGF_ADDENGLISH, GUIO1(GUIO_NOSPEECH) },
+ Common::DE_DEU, Common::kPlatformAmiga, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Castle of Dr. Brain Macintosh (from omer_mor, bug report #3328251)
{"castlebrain", "", {
@@ -76,7 +84,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "e1a6b6f1060f60be9dcb6d28ad7a2a20", 1168310},
{"resource.003", 0, "6c3d1bb26ad532c94046bc9ac49b5ff4", 891295},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformMacintosh, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformMacintosh, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Castle of Dr. Brain - English DOS Non-Interactive Demo
// SCI interpreter version 1.000.005
@@ -85,7 +93,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "9780f040d58182994e22d2e34fab85b0", 67367},
{"resource.001", 0, "2af49dbd8f2e1db4ab09f9310dc91259", 570553},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Castle of Dr. Brain - English DOS 5.25" Floppy EGA (from omer_mor, bug report #3035349)
{"castlebrain", "EGA", {
@@ -98,7 +106,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "8a5ed3ba96e2eaf18e36fedfaab89419", 297838},
{"resource.006", 0, "dceed92e709cad1bd9582809a235b0a0", 266682},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Castle of Dr. Brain - English DOS 3.5" Floppy EGA (from nozomi77, bug report #3405307)
{"castlebrain", "EGA", {
@@ -108,7 +116,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "de2f182529efaad2c4b510b452ab77ac", 633662},
{"resource.003", 0, "38b4b37febc6b4f5061c461a283df148", 430388},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Castle of Dr. Brain - English DOS Floppy (from jvprat)
// Executable scanning reports "1.000.044", Floppy label reports "1.0, 10.30.91", VERSION file reports "1.000"
@@ -119,7 +127,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "d2f5a1be74ed963fa849a76892be5290", 794832},
{"resource.002", 0, "c0c29c51af66d65cb53f49e785a2d978", 1280907},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Castle of Dr. Brain - English DOS Floppy 1.1
{"castlebrain", "", {
@@ -128,7 +136,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "13e81e1839cd7b216d2bb5615c1ca160", 796776},
{"resource.002", 0, "930e416bec196b9703a331d81b3d66f2", 1283812},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Castle of Dr. Brain - English DOS Floppy 1.000
// Reported by graxer in bug report #3037942
@@ -143,7 +151,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "1d778a0c65cac9ddbab65495e50a94ee", 335281},
{"resource.007", 0, "063bb8ce4157c778cf30d1c912c006f1", 335631},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Castle of Dr. Brain - Spanish DOS (also includes english language)
// SCI interpreter version 1.000.510
@@ -152,7 +160,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "27ec5fa09cd12a7fd16e86d96a2ed245", 1197694},
{"resource.001", 0, "735be4e58957180cfc807d5e18fdffcd", 1433302},
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO1(GUIO_NOSPEECH) },
+ Common::ES_ESP, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Christmas Card 1988 - English DOS
// SCI interpreter version 0.000.294
@@ -160,7 +168,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "39485580d34a72997f3d5b3aba4d24f1", 426},
{"resource.001", 0, "11391434f41c834090d7a1e9488ce936", 129739},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Christmas Card 1990: The Seasoned Professional - English DOS (16 Colors)
// SCI interpreter version 1.000.172
@@ -168,7 +176,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "8f656714a05b94423ac6eb10ee8797d0", 600},
{"resource.001", 0, "acde93e58fca4f7a2a5a220558a94aa8", 272629},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Christmas Card 1990: The Seasoned Professional - English DOS (256 Colors)
// SCI interpreter version 1.000.174
@@ -176,7 +184,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "44b8f45b841b9b5e17e939a35e443988", 600},
{"resource.001", 0, "acde93e58fca4f7a2a5a220558a94aa8", 335362},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Christmas Card 1992 - English DOS
// SCI interpreter version 1.001.055
@@ -184,7 +192,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "f1f8c8a8443f523422af70b4ec85b71c", 318},
{"resource.000", 0, "62fb9256f8e7e6e65a6875efdb7939ac", 203396},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Codename: Iceman - English Amiga (from www.back2roots.org)
// Executable scanning reports "1.002.031"
@@ -198,7 +206,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "8613c45fc771d658e5a505b9a4a54f31", 713382},
{"resource.005", 0, "605b67a9ef199a9bb015745e7c004cf4", 478384},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Codename: Iceman - English DOS Non-Interactive Demo
// Executable scanning reports "0.000.685"
@@ -206,7 +214,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "782974f29d8a824782d2d4aea39964e3", 1056},
{"resource.001", 0, "d4b75e280d1c3a97cfef1b0bebff387c", 573647},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Codename: Iceman - English DOS (from jvprat)
// Executable scanning reports "0.000.685", Floppy label reports "1.033, 6.8.90", VERSION file reports "1.033"
@@ -219,7 +227,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "d97a96f1ab91b41cf46a02cc89b0a04e", 624303},
{"resource.004", 0, "8613c45fc771d658e5a505b9a4a54f31", 670883},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Codename: Iceman - English DOS (from FRG)
// SCI interpreter version 0.000.668
@@ -231,7 +239,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "dc7c5280e7acfaffe6ef2a6c963c5f94", 622118},
{"resource.004", 0, "64f342463f6f35ba71b3509ef696ae3f", 669188},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Codename: Iceman - English DOS (supplied by ssburnout in bug report #3049193)
// 1.022 9x5.25" (label: Int#0.000.668)
@@ -246,7 +254,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "08050329aa113a9f14ed99cbfe3536ec", 232942},
{"resource.007", 0, "64f342463f6f35ba71b3509ef696ae3f", 267811},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Codename: Iceman - English DOS 1.023 (from abevi, bug report #2612718)
{"iceman", "", {
@@ -260,7 +268,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "08050329aa113a9f14ed99cbfe3536ec", 232942},
{"resource.007", 0, "64f342463f6f35ba71b3509ef696ae3f", 267702},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Conquests of Camelot - English Amiga (from www.back2roots.org)
// Executable scanning reports "1.002.030"
@@ -275,7 +283,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "c6e551bdc24f0acc193159038d4ca767", 605882},
{"resource.006", 0, "8f880a536908ab496bbc552f7f5c3738", 585255},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Conquests of Camelot - English DOS Non-Interactive Demo
// SCI interpreter version 0.000.668
@@ -283,7 +291,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "f4cd75c15be75e04cdca3acda2c0b0ea", 468},
{"resource.001", 0, "4930708722f34bfbaa4945fb08f55f61", 232523},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Conquests of Camelot - English DOS (from jvprat)
// Executable scanning reports "0.000.685", Floppy label reports "1.001, 0.000.685", VERSION file reports "1.001.000"
@@ -295,7 +303,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "8e1a3a8c588007404b532b8dfacc1460", 723712},
{"resource.004", 0, "8e1a3a8c588007404b532b8dfacc1460", 729143},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Conquests of Camelot - English DOS
// SCI interpreter version 0.000.685
@@ -309,7 +317,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "8e1a3a8c588007404b532b8dfacc1460", 332446},
{"resource.007", 0, "8e1a3a8c588007404b532b8dfacc1460", 358182},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Conquests of the Longbow - English Amiga (from www.back2roots.org)
// Executable scanning reports "1.005.001"
@@ -324,7 +332,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "1c3804e56b114028c5873a35c2f06d13", 653002},
{"resource.006", 0, "f9487732289a4f4966b4e34eea413325", 842817},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Conquests of the Longbow - English DOS
// SCI interpreter version 1.000.510
@@ -338,7 +346,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "d036df0872f2db19bca34601276be2d7", 1154950},
{"resource.006", 0, "b367a6a59f29ee30dde1d88a5a41152d", 1042966},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Conquests of the Longbow - English DOS Floppy (from jvprat)
// Executable scanning reports "1.000.168", Floppy label reports "1.1, 1.13.92", VERSION file reports "1.1"
@@ -352,7 +360,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "9cfce07e204a329e94fda8b5657621da", 1261462},
{"resource.005", 0, "21ebe6b39b57a73fc449f67f013765aa", 1284720},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Conquests of the Longbow - English DOS
// SCI interpreter version 1.000.510
@@ -365,7 +373,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "9cfce07e204a329e94fda8b5657621da", 1260237},
{"resource.005", 0, "21ebe6b39b57a73fc449f67f013765aa", 1284609},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Conquests of the Longbow EGA - English DOS
// SCI interpreter version 1.000.510
@@ -378,7 +386,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "b7bb35c027bb424ecefcd122768e5e60", 705631},
{"resource.005", 0, "58942b1aa6d6ffeb66e9f8897fd4435f", 469243},
{"resource.006", 0, "8c767b3939add63d11274065e46aad04", 713158},
- AD_LISTEND}, Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ AD_LISTEND}, Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Conquests of the Longbow DOS 1.0 EGA (4 x 5.25" disks)
// Provided by ssburnout in bug report #3046802
@@ -388,7 +396,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "76c729e563809170e6cc8b2f3f6cf0a4", 1196133},
{"resource.002", 0, "8c767b3939add63d11274065e46aad04", 1152478},
{"resource.003", 0, "7025b87e735b1df3f0e9488a621f4333", 1171439},
- AD_LISTEND}, Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ AD_LISTEND}, Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Conquests of the Longbow - English DOS Non-Interactive Demo
// SCI interpreter version 1.000.510
@@ -396,7 +404,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "cbc5cb73341de1bff1b1e20a640af220", 588},
{"resource.001", 0, "f05a20cc07eee85da8e999d0ac0f596b", 869916},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Conquests of the Longbow - German DOS (suplied by markcoolio in bug report #2727681, also includes english language)
// SCI interpreter version 1.000.510
@@ -410,7 +418,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "d036df0872f2db19bca34601276be2d7", 1176914},
{"resource.006", 0, "b367a6a59f29ee30dde1d88a5a41152d", 1123585},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO1(GUIO_NOSPEECH) },
+ Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Eco Quest - English DOS Non-Interactive Demo (from FRG)
// Executable scanning reports "x.yyy.zzz"
@@ -419,7 +427,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "c819e171359b7c95f4c13b846d5c034e", 873},
{"resource.001", 0, "baf9393a9bfa73098adb501e5bc5487b", 657518},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Eco Quest - English DOS CD 1.1
// SCI interpreter version 1.001.064
@@ -427,7 +435,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "a4b73d5d2b55bdb6e44345e99c8fbdd0", 4804},
{"resource.000", 0, "d908dbef56816ac6c60dd145fdeafb2b", 3536046},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO0() },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Eco Quest - English DOS CD 1.1
// SCI interpreter version 1.001.064
@@ -437,7 +445,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "a4b73d5d2b55bdb6e44345e99c8fbdd0", 4804},
{"resource.000", 0, "d908dbef56816ac6c60dd145fdeafb2b", 3536046},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_CD, GUIO1(GUIO_MIDIGM) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_CD, GUIO4(GUIO_MIDIGM, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Eco Quest - English DOS Floppy
// SCI interpreter version 1.000.510
@@ -448,7 +456,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "28fe9b4f0567e71feb198bc9f3a2c605", 1241816},
{"resource.003", 0, "f3146df0ad4297f5ce35aa8c4753bf6c", 586832},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Eco Quest - English DOS Floppy
// SCI interpreter version 1.000.510
@@ -459,7 +467,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "323b3b12f43d53f27d259beb225f0aa7", 1129316},
{"resource.003", 0, "83ac03e4bddb2c1ac2d36d2a587d0536", 1145616},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Eco Quest - German DOS Floppy (supplied by markcoolio in bug report #2723744, also includes english language)
// SCI interpreter version 1.000.510
@@ -470,7 +478,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "02d7d0411f7903aacb3bc8b0f8ca8a9a", 1202581},
{"resource.003", 0, "84dd11b6825255671c703aee5ceff620", 1175835},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO1(GUIO_NOSPEECH) },
+ Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Eco Quest - Spanish DOS Floppy (from jvprat, also includes english language)
// Executable scanning reports "1.ECO.013", VERSION file reports "1.000, 11.12.92"
@@ -482,7 +490,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "2d21a1d2dcbffa551552e3e0725d2284", 1186033},
{"resource.003", 0, "84dd11b6825255671c703aee5ceff620", 1174993},
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO1(GUIO_NOSPEECH) },
+ Common::ES_ESP, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Eco Quest - French DOS Floppy (from Strangerke, also includes english language)
// SCI interpreter version 1.ECO.013
@@ -493,7 +501,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "b836c6ee9de67d814ac5d1b05f5b9858", 1173872},
{"resource.003", 0, "f8f767f9d6351432621c6e54c1b2ba8c", 1141520},
AD_LISTEND},
- Common::FR_FRA, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO1(GUIO_NOSPEECH) },
+ Common::FR_FRA, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Eco Quest 2 - English DOS Non-Interactive Demo
// SCI interpreter version 1.001.055
@@ -501,7 +509,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "607cfa0d8a03b7d348c06ee727e3d939", 1321},
{"resource.000", 0, "dd6f614c43c029f063e93cd243af90a4", 525992},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Eco Quest 2 - English DOS Floppy (supplied by markcoolio in bug report #2723761)
// SCI interpreter version 1.001.065
@@ -509,7 +517,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "28fb7b6abb9fc1cb8882d7c2e701b63f", 5658},
{"resource.000", 0, "cc1d17e5637528dbe4a812699e1cbfc6", 4208192},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Eco Quest 2 - French DOS Floppy (from Strangerke)
// SCI interpreter version 1.001.081
@@ -517,7 +525,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "c22ab8b33c339c138b6b1697b77b9e79", 5588},
{"resource.000", 0, "1c4093f7248240329121fdf8c0d59152", 4231946},
AD_LISTEND},
- Common::FR_FRA, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::FR_FRA, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Eco Quest 2 - Spanish DOS Floppy (supplied by umbrio in bug report #3313962)
{"ecoquest2", "Floppy", {
@@ -525,7 +533,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "1c4093f7248240329121fdf8c0d59152", 4209150},
{"resource.msg", 0, "eff8be1925d42288de55e405983e9314", 117810},
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::ES_ESP, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Freddy Pharkas - English DOS demo (from FRG)
// SCI interpreter version 1.001.069
@@ -533,7 +541,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "97aa9fcfe84c9993a64debd28c32393a", 1909},
{"resource.000", 0, "5ea8e7a3ea10cce6efd5c106dc62fd8c", 867724},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Freddy Pharkas - English CD DOS (from FRG)
// SCI interpreter version 1.001.132
@@ -541,7 +549,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "d46b282f228a67ba13bd4b4009e95f8f", 6058},
{"resource.000", 0, "ee3c64ffff0ba9fb08bea2624631c598", 5490246},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO0() },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Freddy Pharkas - English DOS Floppy (updated information from markcoolio in bug reports #2723773 and #2724720)
// Executable scanning reports "1.cfs.081"
@@ -551,7 +559,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "96b07e9b914dba1c8dc6c78a176326df", 5233230},
{"resource.msg", 0, "554f65315d851184f6e38211489fdd8f", -1},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Freddy Pharkas - Windows (supplied by abevi in bug report #2612718)
// Executable scanning reports "1.cfs.081"
@@ -560,7 +568,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "a32674e7fbf7b213b4a066c8037f16b6", 5816},
{"resource.000", 0, "fed4808fdb72486908ac7ad0044b14d8", 5233230},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformWindows, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Freddy Pharkas - German DOS Floppy (from Tobis87, updated information from markcoolio in bug reports #2723772 and #2724720)
// Executable scanning reports "1.cfs.081"
@@ -570,7 +578,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "96b07e9b914dba1c8dc6c78a176326df", 5233230},
{"resource.msg", 0, "304b5a5781800affd2235152a5794fa8", -1},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::DE_DEU, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Freddy Pharkas - Spanish DOS (from jvprat)
// Executable scanning reports "1.cfs.081", VERSION file reports "1.000, March 30, 1995"
@@ -583,7 +591,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "05acdc256c742e79c50b9fe7ec2cc898", 863310},
{"resource.msg", 0, "45b5bf74933ac3727e4cc844446dc052", 796156},
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformPC, ADGF_CD, GUIO0() },
+ Common::ES_ESP, Common::kPlatformPC, ADGF_CD, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Freddy Pharkas - Spanish DOS (from jvprat)
// Executable scanning reports "1.cfs.081", VERSION file reports "1.000, March 30, 1995"
@@ -593,7 +601,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "96b07e9b914dba1c8dc6c78a176326df", 5233230},
{"resource.msg", 0, "45b5bf74933ac3727e4cc844446dc052", 796156},
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::ES_ESP, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Freddy Pharkas - English DOS CD Demo
// SCI interpreter version 1.001.095
@@ -601,14 +609,14 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "a62a7eae85dd1e6b07f39662b278437e", 1918},
{"resource.000", 0, "4962a3c4dd44e36e78ea4a7a374c2220", 957382},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO0() },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Freddy Pharkas - English Macintosh
{"freddypharkas", "", {
{"Data1", 0, "ef7cbd62727989818f1cfae69c9fd61d", 3038492},
{"Data2", 0, "2424b418f7d52c385cea4701f529c69a", 4721732},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Fun Seeker's Guide - English DOS
// SCI interpreter version 0.000.506
@@ -616,7 +624,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "7ee6859ef74314f6d91938c3595348a9", 282},
{"resource.001", 0, "f1e680095424e31f7fae1255d36bacba", 40692},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Gabriel Knight - English DOS CD Demo
// SCI interpreter version 1.001.092
@@ -624,7 +632,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "39645952ae0ed8072c7e838f31b75464", 2490},
{"resource.000", 0, "eb3ed7477ca4110813fe1fcf35928561", 1718450},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO0() },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#ifdef ENABLE_SCI32
// Gabriel Knight - English DOS Floppy
@@ -633,7 +641,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "372d059f75856afa6d73dd84cbb8913d", 10783},
{"resource.000", 0, "69b7516962510f780d38519cc15fcc7c", 13022630},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Gabriel Knight - English DOS Floppy (supplied my markcoolio in bug report #2723777)
// SCI interpreter version 2.000.000
@@ -641,7 +649,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "65e8c14092e4c9b3b3538b7602c8c5ec", 10783},
{"resource.000", 0, "69b7516962510f780d38519cc15fcc7c", 13022630},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Gabriel Knight - English DOS Floppy
// SCI interpreter version 2.000.000, VERSION file reports "1.0\nGabriel Knight\n11/22/10:33 pm\n\x1A"
@@ -649,7 +657,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "ef41df08cf2c1f680216cdbeed0f8311", 10783},
{"resource.000", 0, "69b7516962510f780d38519cc15fcc7c", 13022630},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Gabriel Knight - German DOS Floppy (supplied my markcoolio in bug report #2723775)
// SCI interpreter version 2.000.000
@@ -657,7 +665,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "ad6508b0296b25c07b1f58828dc33696", 10789},
{"resource.000", 0, "091cf08910780feabc56f8551b09cb36", 13077029},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformPC, ADGF_UNSTABLE, GUIO1(GUIO_NOSPEECH) },
+ Common::DE_DEU, Common::kPlatformPC, ADGF_UNSTABLE, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Gabriel Knight - English DOS CD (from jvprat)
// Executable scanning reports "2.000.000", VERSION file reports "01.100.000"
@@ -665,7 +673,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "372d059f75856afa6d73dd84cbb8913d", 10996},
{"resource.000", 0, "69b7516962510f780d38519cc15fcc7c", 12581736},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_CD | ADGF_UNSTABLE, GUIO0() },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_CD | ADGF_UNSTABLE, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Gabriel Knight - English Windows CD (from jvprat)
// Executable scanning reports "2.000.000", VERSION file reports "01.100.000"
@@ -673,7 +681,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "372d059f75856afa6d73dd84cbb8913d", 10996},
{"resource.000", 0, "69b7516962510f780d38519cc15fcc7c", 12581736},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_CD | ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_CD | ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Gabriel Knight - German DOS CD (from Tobis87)
// SCI interpreter version 2.000.000
@@ -681,7 +689,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "a7d3e55114c65647310373cb390815ba", 11392},
{"resource.000", 0, "091cf08910780feabc56f8551b09cb36", 13400497},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformPC, ADGF_CD | ADGF_UNSTABLE, GUIO0() },
+ Common::DE_DEU, Common::kPlatformPC, ADGF_CD | ADGF_UNSTABLE, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Gabriel Knight - Spanish DOS CD (from jvprat)
// Executable scanning reports "2.000.000", VERSION file reports "1.000.000, April 13, 1995"
@@ -689,7 +697,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "7cb6e9bba15b544ec7a635c45bde9953", 11404},
{"resource.000", 0, "091cf08910780feabc56f8551b09cb36", 13381599},
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformPC, ADGF_CD | ADGF_UNSTABLE, GUIO0() },
+ Common::ES_ESP, Common::kPlatformPC, ADGF_CD | ADGF_UNSTABLE, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Gabriel Knight - French DOS CD (from Hkz)
// VERSION file reports "1.000.000, May 3, 1994"
@@ -697,7 +705,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "55f909ba93a2515042a08d8a2da8414e", 11392},
{"resource.000", 0, "091cf08910780feabc56f8551b09cb36", 13325145},
AD_LISTEND},
- Common::FR_FRA, Common::kPlatformPC, ADGF_CD | ADGF_UNSTABLE, GUIO0() },
+ Common::FR_FRA, Common::kPlatformPC, ADGF_CD | ADGF_UNSTABLE, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Gabriel Knight - German Windows CD (from Tobis87)
// SCI interpreter version 2.000.000
@@ -705,7 +713,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "a7d3e55114c65647310373cb390815ba", 11392},
{"resource.000", 0, "091cf08910780feabc56f8551b09cb36", 13400497},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformWindows, ADGF_CD | ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::DE_DEU, Common::kPlatformWindows, ADGF_CD | ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Gabriel Knight - Spanish Windows CD (from jvprat)
// Executable scanning reports "2.000.000", VERSION file reports "1.000.000, April 13, 1995"
@@ -713,7 +721,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "7cb6e9bba15b544ec7a635c45bde9953", 11404},
{"resource.000", 0, "091cf08910780feabc56f8551b09cb36", 13381599},
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformWindows, ADGF_CD | ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::ES_ESP, Common::kPlatformWindows, ADGF_CD | ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Gabriel Knight - English Macintosh
{"gk1", "", {
@@ -722,7 +730,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"Data3", 0, "f25068b408b09275d8b698866462f578", 3677599},
{"Data4", 0, "1cceebbe411b26c860a74f91c337fdf3", 3230086},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK | ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK | ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Gabriel Knight 2 - English Windows Non-Interactive Demo
// Executable scanning reports "2.100.002"
@@ -730,7 +738,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "e0effce11c4908f4b91838741716c83d", 1351},
{"resource.000", 0, "d04cfc7f04b6f74d13025378be49ec2b", 4640330},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO | ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO | ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Gabriel Knight 2 - English DOS (GOG version) - ressci.* merged in ressci.000
// using Enrico Rolfi's HD/DVD installer: http://gkpatches.vogons.zetafleet.com/
@@ -738,7 +746,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.000", 0, "b996fa1e57389a1e179a00a0049de1f4", 8110},
{"ressci.000", 0, "a19fc3604c6e5407abcf03d59ee87217", 168522221},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Gabriel Knight 2 - English DOS (from jvprat)
// Executable scanning reports "2.100.002", VERSION file reports "1.1"
@@ -756,7 +764,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.006", 0, "ce9359037277b7d7976da185c2fa0aad", 2977},
{"ressci.006", 0, "8e44e03890205a7be12f45aaba9644b4", 60659424},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Gabriel Knight 2 - French DOS (6-CDs Sierra Originals reedition)
// Executable scanning reports "2.100.002", VERSION file reports "1.0"
@@ -774,7 +782,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.006", 0, "11b2e722170b8c93fdaa5428e2c7676f", 3001},
{"ressci.006", 0, "4037d941aec39d2e654e20960429aefc", 60568486},
AD_LISTEND},
- Common::FR_FRA, Common::kPlatformPC, ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::FR_FRA, Common::kPlatformPC, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Gabriel Knight 2 - English Macintosh
// NOTE: This only contains disc 1 files (as well as the persistent file:
@@ -786,7 +794,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"Data4", 0, "8b843c62eb53136a855d6e0087e3cb0d", 5889553},
{"Data5", 0, "f9fcf9ab2eb13b2125c33a1cda03a093", 14349984},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK | ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK | ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#endif // ENABLE_SCI32
@@ -798,7 +806,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "e0dd44069a62a463fd124974b915f10d", 342149},
{"resource.003", 0, "e0dd44069a62a463fd124974b915f10d", 328925},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Hoyle 1 - English DOS (supplied by wibble92 in bug report #2644547)
// SCI interpreter version 0.000.530
@@ -808,7 +816,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "e0dd44069a62a463fd124974b915f10d", 342309},
{"resource.003", 0, "e0dd44069a62a463fd124974b915f10d", 328912},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Hoyle 1 - English DOS (supplied by merkur in bug report #2719227)
// SCI interpreter version 0.000.530
@@ -816,14 +824,14 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "1034a218943d12f1f36e753fa10c95b8", 4386},
{"resource.001", 0, "e0dd44069a62a463fd124974b915f10d", 518308},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Hoyle 1 3.5' - English DOS (supplied by eddydrama in bug report #3052366 and dinnerx in bug report #3090841)
{"hoyle1", "", {
{"resource.map", 0, "0af9a3dcd72a091960de070432e1f524", 4386},
{"resource.001", 0, "e0dd44069a62a463fd124974b915f10d", 518127},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#if 0 // TODO: unknown if these files are corrupt
// Hoyle 1 - English Amiga (from www.back2roots.org)
@@ -833,7 +841,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "e0dd44069a62a463fd124974b915f10d", 218755},
{"resource.002", 0, "e0dd44069a62a463fd124974b915f10d", 439502},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#endif
// Hoyle 2 - English DOS
@@ -843,7 +851,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "8f2dd70abe01112eca464cda818b5eb6", 98138},
{"resource.002", 0, "8f2dd70abe01112eca464cda818b5eb6", 196631},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Hoyle 2 - English DOS (supplied by ssburnout in bug report #3049193)
// 1.000.011 1x3.5" (label:Int#6.21.90)
@@ -851,7 +859,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "db0ba08b953e9904a4960ad99cd29c20", 1356},
{"resource.001", 0, "8f2dd70abe01112eca464cda818b5eb6", 216315},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Hoyle 2 - English Amiga (from www.back2roots.org)
// Executable scanning reports "1.002.032"
@@ -860,7 +868,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "62ed48d20c580e5a98f102f7cd93706a", 1356},
{"resource.001", 0, "8f2dd70abe01112eca464cda818b5eb6", 222704},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Hoyle 2 - English Macintosh
// Executable scanning reports "x.yyy.zzz"
@@ -868,7 +876,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "1af1d3aa3cf564f93477c9f87e53f495", 1728},
{"resource.001", 0, "b73b8131669d69d41a326415e4519138", 482882},
{NULL, 0, NULL, 0}},
- Common::EN_ANY, Common::kPlatformMacintosh, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformMacintosh, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#if 0 // TODO: unknown if these files are corrupt
// Hoyle 3 - English Amiga (from www.back2roots.org)
@@ -879,7 +887,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "595b6039ea1356e7f96a52c58eedcf22", 355791},
{"resource.001", 0, "143df8aef214a2db34c2d48190742012", 632273},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#endif
// Hoyle 3 - English DOS Non-Interactive Demo
@@ -889,7 +897,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "0d06cacc87dc21a08cd017e73036f905", 735},
{"resource.001", 0, "24db2bccda0a3c43ac4a7b5edb116c7e", 797678},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Hoyle 3 - English DOS Floppy (from jvprat)
// Executable scanning reports "x.yyy.zzz", Floppy label reports "1.0, 11.2.91", VERSION file reports "1.000"
@@ -899,7 +907,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "6ef28cac094dcd97fdb461662ead6f92", 541845},
{"resource.001", 0, "0a98a268ee99b92c233a0d7187c1f0fa", 845795},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Hoyle 3 - English DOS Floppy (supplied by eddydrama in bug report #3038837)
{"hoyle3", "", {
@@ -910,7 +918,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "97cfd72633f8f9b2a0b1d4116cf3ee81", 346116},
{"resource.004", 0, "2884fb91b225fabd9ca87ea231293b48", 351218},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Hoyle 3 EGA - English DOS Floppy 1.0 (supplied by abevi in bug report #2612718)
{"hoyle3", "EGA", {
@@ -918,14 +926,14 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "6ef28cac094dcd97fdb461662ead6f92", 319905},
{"resource.001", 0, "0a98a268ee99b92c233a0d7187c1f0fa", 526438},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Hoyle 4 (Hoyle Classic Card Games) - English DOS Demo
{"hoyle4", "Demo", {
{"resource.map", 0, "60f764020a6b788bbbe415dbc2ccb9f3", 931},
{"resource.000", 0, "5fe3670e3ddcd4f85c10013b5453141a", 615522},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Hoyle 4 (Hoyle Classic Card Games) - English DOS Demo
// SCI interpreter version 1.001.200 (just a guess)
@@ -934,7 +942,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "662087cb383e52e3cc4ae7ecb10e20aa", 938},
{"resource.000", 0, "24c10844792c54d476d272213cbac300", 675252},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Hoyle 4 (Hoyle Classic Card Games) - English DOS/Win
// Supplied by abevi in bug report #3039291
@@ -942,7 +950,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "2b577c975cc8d8d43f61b6a756129fe3", 4352},
{"resource.000", 0, "43e2c15ce436aab611a462ad0603e12d", 2000132},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Hoyle 4 (Hoyle Classic Card Games) - English Macintosh Floppy
// VERSION file reports "2.0"
@@ -950,7 +958,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"Data1", 0, "99575fae4579540a314bbedd72d51e8c", 7682887},
{"Data2", 0, "7d4bf5bdf3c02edbf35cb8471c84ec13", 1539134},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Jones in the Fast Lane EGA - English DOS
// SCI interpreter version 1.000.172 (not 100% sure FIXME)
@@ -959,14 +967,14 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "bac3ec6cb3e3920984ab0f32becf5163", 202105},
{"resource.002", 0, "b86daa3ba2784d1502da881eedb80d9b", 341771},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Jones in the Fast Lane EGA - English DOS (supplied by EddyDrama in bug report #3038761)
{"jones", "EGA", {
{"resource.map", 0, "8e92cf319180cc8b5b87b2ce93a4fe22", 1602},
{"resource.001", 0, "bac3ec6cb3e3920984ab0f32becf5163", 511528},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Jones in the Fast Lane VGA - English DOS
// SCI interpreter version 1.000.172
@@ -975,7 +983,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "bac3ec6cb3e3920984ab0f32becf5163", 313476},
{"resource.002", 0, "b86daa3ba2784d1502da881eedb80d9b", 719747},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Jones in the Fast Lane VGA - English DOS (supplied by omer_mor in bug report #3037054)
// VERSION file reports "1.000.060"
@@ -983,14 +991,14 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "db175ab494ab0666f19ab8f2597a8e49", 1602},
{"resource.001", 0, "bac3ec6cb3e3920984ab0f32becf5163", 994487},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Jones in the Fast Lane - English DOS CD
{"jones", "CD", {
{"resource.map", 0, "459f5b04467bc2107aec02f5c4b71b37", 4878},
{"resource.001", 0, "3876da2ce16fb7dea2f5d943d946fa84", 1652150},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO0() },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO1(GAMEOPTION_JONES_CDAUDIO) },
// Jones in the Fast Lane - English DOS CD
// Same entry as the DOS version above. This one is used for the alternate
@@ -999,7 +1007,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "459f5b04467bc2107aec02f5c4b71b37", 4878},
{"resource.001", 0, "3876da2ce16fb7dea2f5d943d946fa84", 1652150},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_CD, GUIO1(GUIO_MIDIGM) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_CD, GUIO4(GUIO_MIDIGM, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_FB01_MIDI, GAMEOPTION_JONES_CDAUDIO) },
// King's Quest 1 SCI Remake - English Amiga (from www.back2roots.org)
// Executable scanning reports "1.003.007"
@@ -1011,7 +1019,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "9ae2a13708d691cd42f9129173c4b39d", 763224},
{"resource.004", 0, "9ae2a13708d691cd42f9129173c4b39d", 820443},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 1 SCI Remake - English DOS Non-Interactive Demo
// Executable scanning reports "S.old.010"
@@ -1019,7 +1027,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "59b13619078bd47011421468959ee5d4", 954},
{"resource.001", 0, "4cfb9040db152868f7cb6a1e8151c910", 296555},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 1 SCI Remake - English DOS (from the King's Quest Collection)
// Executable scanning reports "S.old.010", VERSION file reports "1.000.051"
@@ -1030,7 +1038,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "fed9e0072ffd511d248674e60dee2099", 714062},
{"resource.003", 0, "fed9e0072ffd511d248674e60dee2099", 717478},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 1 SCI Remake - English DOS (supplied by ssburnout in bug report #3049193)
// 1.000.051 9x5.25" (label: INT#9.19.90)
@@ -1044,7 +1052,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "fed9e0072ffd511d248674e60dee2099", 351062},
{"resource.007", 0, "fed9e0072ffd511d248674e60dee2099", 330472},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 4 - English Amiga (from www.back2roots.org)
// Executable scanning reports "1.002.032"
@@ -1057,7 +1065,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "fd16c9c223f7dc5b65f06447615224ff", 683016},
{"resource.004", 0, "3fac034c7d130e055d05bc43a1f8d5f8", 549993},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 4 - English DOS Non-Interactive Demo
// Executable scanning reports "0.000.494"
@@ -1065,7 +1073,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "992ac7cc31d3717fe53818a9bb6d1dae", 594},
{"resource.001", 0, "143e1c14f15ad0fbfc714f648a65f661", 205330},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 4 - English DOS (original boxed release, 3 1/2" disks)
// SCI interpreter version 0.000.247
@@ -1076,7 +1084,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "851a62d00972dc4002f472cc0d84e71d", 683145},
{"resource.004", 0, "851a62d00972dc4002f472cc0d84e71d", 649441},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 4 - English DOS (from the King's Quest Collection)
// Executable scanning reports "0.000.502"
@@ -1088,7 +1096,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "77615c595388acf3d1df8e107bfb6b52", 707591},
{"resource.004", 0, "77615c595388acf3d1df8e107bfb6b52", 479562},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 4 - English DOS (supplied by ssburnout in bug report #3049193)
// 1.006.003 8x5.25" (label: Int.#0.000.502)
@@ -1102,7 +1110,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "6db7de6f93c6ea62dca78abee677f8c0", 324789},
{"resource.007", 0, "6db7de6f93c6ea62dca78abee677f8c0", 334441},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 4 - English DOS
// SCI interpreter version 0.000.274
@@ -1116,7 +1124,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "851a62d00972dc4002f472cc0d84e71d", 333777},
{"resource.007", 0, "851a62d00972dc4002f472cc0d84e71d", 341038},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 4 - English DOS
// SCI interpreter version 0.000.253
@@ -1130,7 +1138,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "0c8566848a76eea19a6d6220914030a7", 337288},
{"resource.007", 0, "0c8566848a76eea19a6d6220914030a7", 343882},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 4 - English Atari ST (double-sided diskettes)
// Game version 1.003.006 (January 12, 1989)
@@ -1143,7 +1151,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "a3cdb4848fb859fdd302976fff56490f", 705074},
{"resource.004", 0, "a3cdb4848fb859fdd302976fff56490f", 478366},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformAtariST, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformAtariST, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 5 - English Amiga (from www.back2roots.org)
// Executable scanning reports "1.004.018"
@@ -1159,7 +1167,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "26c0c25399b6715fec03fc3e12544fe3", 823048},
{"resource.007", 0, "b914b5901e786327213e779725d30dd1", 778772},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 5 - German Amiga (also includes english language)
// Executable scanning reports "1.004.024"
@@ -1175,7 +1183,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "56546b20db11a4836f900efa6d3a3e74", 672099},
{"resource.007", 0, "56546b20db11a4836f900efa6d3a3e74", 794194},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformAmiga, ADGF_ADDENGLISH, GUIO1(GUIO_NOSPEECH) },
+ Common::DE_DEU, Common::kPlatformAmiga, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 5 - Italian Amiga (also includes english language)
// Executable scanning reports "1.004.024"
@@ -1191,7 +1199,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "11cb750f5f816445ad0f4b9f50a4f59a", 672527},
{"resource.007", 0, "11cb750f5f816445ad0f4b9f50a4f59a", 794259},
AD_LISTEND},
- Common::IT_ITA, Common::kPlatformAmiga, ADGF_ADDENGLISH, GUIO1(GUIO_NOSPEECH) },
+ Common::IT_ITA, Common::kPlatformAmiga, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 5 - English DOS CD (from the King's Quest Collection)
// Executable scanning reports "x.yyy.zzz", VERSION file reports "1.000.052"
@@ -1201,7 +1209,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "449471bfd77be52f18a3773c7f7d843d", 571368},
{"resource.001", 0, "b45a581ff8751e052c7e364f58d3617f", 16800210},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO0() },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 5 - English DOS CD (from the King's Quest Collection)
// Executable scanning reports "x.yyy.zzz", VERSION file reports "1.000.052"
@@ -1213,7 +1221,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "449471bfd77be52f18a3773c7f7d843d", 571368},
{"resource.001", 0, "b45a581ff8751e052c7e364f58d3617f", 16800210},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_CD, GUIO1(GUIO_MIDIGM) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_CD, GUIO4(GUIO_MIDIGM, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 5 - English DOS Floppy
// SCI interpreter version 1.000.060
@@ -1228,7 +1236,25 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "672ede1136e9e401658538e51bd5dc22", 1172619},
{"resource.007", 0, "2f48faf27666b58c276dda20f91f4a93", 1240456},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+
+ // King's Quest 5 - English DOS Floppy
+ // VERSION file reports "0.000.051"
+ // Supplied by misterhands in bug report #3536863.
+ // This is the original English version, which has been externally patched to
+ // Polish in the Polish release below.
+ {"kq5", "", {
+ {"resource.map", 0, "70010c20138541f89013bb5e1b30f16a", 7998},
+ {"resource.000", 0, "a591bd4b879fc832b8095c0b3befe9e2", 276398},
+ {"resource.001", 0, "c0f48d4a7ebeaa6aa074fc98d77423e9", 1018560},
+ {"resource.002", 0, "7f188a95acdb60bbe32a8379ba299393", 1307048},
+ {"resource.003", 0, "0860785af59518b94d54718dddcd6907", 1348500},
+ {"resource.004", 0, "c4745dd1e261c22daa6477961d08bf6c", 1239887},
+ {"resource.005", 0, "6556ff8e7c4d1acf6a78aea154daa76c", 1287869},
+ {"resource.006", 0, "da82e4beb744731d0a151f1d4922fafa", 1170456},
+ {"resource.007", 0, "431def14ca29cdb5e6a5e84d3f38f679", 1240176},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 5 - English DOS Floppy (supplied by omer_mor in bug report #3036996)
// VERSION file reports "0.000.051"
@@ -1243,7 +1269,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "06cb3f689836086ebe08b1efc0126592", 921113},
{"resource.007", 0, "252249753c6e850eacceb8af634986d3", 1133608},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 5 EGA (supplied by markcoolio in bug report #2829470)
// SCI interpreter version 1.000.060
@@ -1259,7 +1285,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "698c698570cde9015e4d51eb8d2e9db1", 666527},
{"resource.007", 0, "703d8df30e89541af337d7706540d5c4", 541743},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 5 EGA 1.2M disk version (from LordHoto)
// VERSION file reports "0.000.055"
@@ -1271,7 +1297,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "53206afb4fd73871a484e83acab80f31", 7608},
{"resource.004", 0, "83568edf7fde18b3eed988bc5d22ceb1", 1188053},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 5 EGA (supplied by omer_mor in bug report #3035421)
// VERSION file reports "0.000.062"
@@ -1286,7 +1312,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "698c698570cde9015e4d51eb8d2e9db1", 666541},
{"resource.007", 0, "703d8df30e89541af337d7706540d5c4", 541762},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest V DOS 0.000.062 EGA (5 x 5.25" disks)
// Supplied by ssburnout in bug report #3046780
@@ -1298,7 +1324,22 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "3cca5b2dae8afe94532edfdc98d7edbe", 1092325},
{"resource.004", 0, "8e5c1bc4d738cf7316ff506f59d265e2", 1187803},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+
+ // King's Quest 5 DOS Spanish Floppy 0.000.062 VGA (5 x 3.5" disks)
+ // Supplied by dianiu in bug report #3555646
+ {"kq5", "", {
+ {"resource.map", 0, "c09896a2a30c9b002c5cbbc62f5a5c3a", 8169},
+ {"resource.000", 0, "1f1d03aead44da46362ff40c0074a3ec", 335871},
+ {"resource.001", 0, "d1803ad904127ae091edb274ee8c047f", 1180637},
+ {"resource.002", 0, "d9cd5972016f650cc31fb7c2a2b0953a", 1102207},
+ {"resource.003", 0, "829c8caeff793f3cfcea2cb01aaa4150", 965586},
+ {"resource.004", 0, "0bd9e570ee04b025e43d3075998fae5b", 1117965},
+ {"resource.005", 0, "4aaa2e9a69089b9afbaaccbbf2c4e647", 1202936},
+ {"resource.006", 0, "65b520e60c4217e6a6572d9edf77193b", 1141985},
+ {"resource.007", 0, "f42b0100f0a1c30806814f8648b6bc28", 1145583},
+ AD_LISTEND},
+ Common::ES_ESP, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 5 - German DOS Floppy (supplied by markcoolio in bug report #2727101, also includes english language)
// SCI interpreter version 1.000.060
@@ -1313,7 +1354,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "d1a75fdc01840664d00366cff6919366", 1208972},
{"resource.007", 0, "c07494f0cce7c05210893938786a955b", 1337361},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO1(GUIO_NOSPEECH) },
+ Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 5 - French DOS Floppy (from the King's Quest Collector's Edition 1994, also includes english language)
// Supplied by aroenai in bug report #2812611
@@ -1329,7 +1370,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "f7dc85307632ef657ceb1651204f6f51", 1210081},
{"resource.007", 0, "7db4d0a1d8d547c0019cb7d2a6acbdd4", 1338473},
AD_LISTEND},
- Common::FR_FRA, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO1(GUIO_NOSPEECH) },
+ Common::FR_FRA, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 5 - Italian DOS Floppy (from glorifindel, includes english language)
// SCI interpreter version 1.000.060
@@ -1344,10 +1385,12 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "8eeabd92af71e766e323db2100879102", 1209325},
{"resource.007", 0, "dc10c107e0923b902326a040b9c166b9", 1337859},
AD_LISTEND},
- Common::IT_ITA, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO1(GUIO_NOSPEECH) },
+ Common::IT_ITA, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
- // King's Quest 5 - Polish DOS Floppy (supplied by jacek909 in bug report #2725722, includes english language?!)
+ // King's Quest 5 - Polish DOS Floppy (supplied by jacek909 in bug report #2725722)
// SCI interpreter version 1.000.060
+ // VERSION file reports "0.000.051".
+ // This is actually an English version with external text resource patches (bug #3536863).
{"kq5", "", {
{"resource.map", 0, "70010c20138541f89013bb5e1b30f16a", 7998},
{"resource.000", 0, "a591bd4b879fc832b8095c0b3befe9e2", 276398},
@@ -1358,8 +1401,9 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "6556ff8e7c4d1acf6a78aea154daa76c", 1287869},
{"resource.006", 0, "da82e4beb744731d0a151f1d4922fafa", 1170456},
{"resource.007", 0, "431def14ca29cdb5e6a5e84d3f38f679", 1240176},
+ {"text.000", 0, "601aa35a3ddeb558e1280e0963e955a2", 1517},
AD_LISTEND},
- Common::PL_POL, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO1(GUIO_NOSPEECH) },
+ Common::PL_POL, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 5 - English Macintosh
// VERSION file reports "1.000.055"
@@ -1374,7 +1418,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "3d22904a374c192f51e5665b74364133", 1264079},
{"resource.007", 0, "ffe17e23d5833a79f3695addfc149a56", 1361965},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformMacintosh, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformMacintosh, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 5 - FM-Towns (supplied by abevi in bug report #3038720)
{"kq5", "", {
@@ -1400,7 +1444,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "e114ce8f884601c43308fb5cbbea4874", 1174129},
{"resource.005", 0, "349ad9438172265d00680075c5a988d0", 1019669},
AD_LISTEND},
- Common::JA_JPN, Common::kPlatformPC98, ADGF_ADDENGLISH, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::JA_JPN, Common::kPlatformPC98, ADGF_ADDENGLISH, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 6 - English DOS Non-Interactive Demo
// Executable scanning reports "1.001.055", VERSION file reports "1.000.000"
@@ -1410,7 +1454,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "535b1b920441ec73f42eaa4ccfd47b89", 264116},
{"resource.msg", 0, "54d1fdc936f98c81f9e4c19e04fb1510", 8260},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 6 - English DOS Floppy
// SCI interpreter version 1.001.054
@@ -1419,7 +1463,16 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "f2b7f753992c56a0c7a08d6a5077c895", 7863324},
{"resource.msg", 0, "3cf5de44de36191f109d425b8450efc8", 258590},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+
+ // King's Quest 6 - French DOS Floppy (supplied by misterhands in bug #3503425)
+ // SCI interpreter version ???
+ {"kq6", "", {
+ {"resource.map", 0, "a362063318eebe7d6423b1d9dc6213e1", 8703},
+ {"resource.000", 0, "f2b7f753992c56a0c7a08d6a5077c895", 7863324},
+ {"resource.msg", 0, "adc2aa8adbdcc97507d44a6f492fbd77", 265194},
+ AD_LISTEND},
+ Common::FR_FRA, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 6 - German DOS Floppy (supplied by markcoolio in bug report #2727156)
// SCI interpreter version 1.001.054
@@ -1428,7 +1481,17 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "f2b7f753992c56a0c7a08d6a5077c895", 7863324},
{"resource.msg", 0, "756297b2155db9e43f621c6f6fb763c3", 282822},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::DE_DEU, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+
+ // King's Quest 6 - Spanish DOS Floppy (from jvprat)
+ // Executable scanning reports "1.cfs.158", VERSION file reports "1.000.000, July 5, 1994"
+ // SCI interpreter version 1.001.055
+ {"kq6", "", {
+ {"resource.map", 0, "a73a5ab04b8f60c4b75b946a4dccea5a", 8953},
+ {"resource.000", 0, "4da3ad5868a775549a7cc4f72770a58e", 8537260},
+ {"resource.msg", 0, "41eed2d3893e1ca6c3695deba4e9d2e8", 267102},
+ AD_LISTEND},
+ Common::ES_ESP, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 6 - English DOS CD (from the King's Quest Collection)
// Executable scanning reports "1.cfs.158", VERSION file reports "1.034 9/11/94 - KQ6 version 1.000.00G"
@@ -1437,7 +1500,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "7a550ebfeae2575ca00d47703a6a774c", 9215},
{"resource.000", 0, "233394a5f33b475ae5975e7e9a420865", 8376352},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO0() },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 6 - English Windows CD (from the King's Quest Collection)
// Executable scanning reports "1.cfs.158", VERSION file reports "1.034 9/11/94 - KQ6 version 1.000.00G"
@@ -1446,17 +1509,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "7a550ebfeae2575ca00d47703a6a774c", 9215},
{"resource.000", 0, "233394a5f33b475ae5975e7e9a420865", 8376352},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_CD, GUIO1(GUIO_NOASPECT) },
-
- // King's Quest 6 - Spanish DOS CD (from jvprat)
- // Executable scanning reports "1.cfs.158", VERSION file reports "1.000.000, July 5, 1994"
- // SCI interpreter version 1.001.055
- {"kq6", "CD", {
- {"resource.map", 0, "a73a5ab04b8f60c4b75b946a4dccea5a", 8953},
- {"resource.000", 0, "4da3ad5868a775549a7cc4f72770a58e", 8537260},
- {"resource.msg", 0, "41eed2d3893e1ca6c3695deba4e9d2e8", 267102},
- AD_LISTEND},
- Common::ES_ESP, Common::kPlatformPC, ADGF_CD, GUIO0() },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_CD, GUIO5(GUIO_NOASPECT, GAMEOPTION_KQ6_WINDOWS_CURSORS, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 6 - English Macintosh Floppy
// VERSION file reports "1.0"
@@ -1464,7 +1517,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"Data1", 0, "a183fc0c22fcbd9be4c8800d974b5599", 3892124},
{"Data2", 0, "b3722460dfd3097a1fbaf99a21ad8ea5", 15031272},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#ifdef ENABLE_SCI32
@@ -1475,7 +1528,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "4948e4e1506f1e1c4e1d47abfa06b7f8", 204385195},
{"resource.map", 0, "40ccafb2195301504eba2e4f4f2c7f3d", 18925},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 7 - English Windows (from the King's Quest Collection)
// Executable scanning reports "2.100.002", VERSION file reports "1.4"
@@ -1483,7 +1536,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "2be9ab94429c721af8e05c507e048a15", 18697},
{"resource.000", 0, "eb63ea3a2c2469dc2d777d351c626404", 203882535},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 7 - English DOS (from FRG)
// SCI interpreter version 2.100.002, VERSION file reports "2.00b"
@@ -1491,7 +1544,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "8676b0fbbd7362989a029fe72fea14c6", 18709},
{"resource.000", 0, "51c1ead1163e19a2de8f121c39df7a76", 200764100},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 7 - English Windows (from FRG)
// SCI interpreter version 2.100.002, VERSION file reports "2.00b"
@@ -1499,7 +1552,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "8676b0fbbd7362989a029fe72fea14c6", 18709},
{"resource.000", 0, "51c1ead1163e19a2de8f121c39df7a76", 200764100},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 7 - German Windows (supplied by markcoolio in bug report #2727402)
// SCI interpreter version 2.100.002
@@ -1507,7 +1560,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "838b9ff132bd6962026fee832e8a7ddb", 18697},
{"resource.000", 0, "eb63ea3a2c2469dc2d777d351c626404", 206626576},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformPC, ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::DE_DEU, Common::kPlatformPC, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 7 - Spanish DOS (from jvprat)
// Executable scanning reports "2.100.002", VERSION file reports "2.00"
@@ -1515,7 +1568,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "0b62693cbe87e3aaca3e8655a437f27f", 18709},
{"resource.000", 0, "51c1ead1163e19a2de8f121c39df7a76", 200764100},
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformPC, ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::ES_ESP, Common::kPlatformPC, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// King's Quest 7 - English DOS Non-Interactive Demo
// SCI interpreter version 2.100.002
@@ -1523,7 +1576,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "b44f774108d63faa1d021101221c5a54", 1690},
{"resource.000", 0, "d9659d2cf0c269c6a9dc776707f5bea0", 2433827},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO | ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO | ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#endif // ENABLE_SCI32
@@ -1539,7 +1592,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "aa553977f7e5804081de293800d3bcce", 695067},
{"resource.005", 0, "bfd870d51dc97729f0914095f58e6957", 676881},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Laura Bow - English Atari ST (from jvprat)
// Executable scanning reports "1.002.030", Floppy label reports "1.000.062, 9.23.90"
@@ -1551,7 +1604,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 667365},
{"resource.004", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 683737},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformAtariST, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformAtariST, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Laura Bow - English DOS Non-Interactive Demo
// Executable scanning reports "x.yyy.zzz"
@@ -1559,7 +1612,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "e625726268ff4e123ada11f31f0249f3", 768},
{"resource.001", 0, "0c8912290af0890f8d95faeb4ddb2d68", 333031},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Laura Bow - English DOS 3.5" Floppy (from "The Roberta Williams Anthology"/1996)
// SCI interpreter version 0.000.631
@@ -1570,7 +1623,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 667468},
{"resource.004", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 683807},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Laura Bow - English DOS (from FRG)
// SCI interpreter version 0.000.631
@@ -1584,7 +1637,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 328390},
{"resource.007", 0, "e45c888d9c7c04aec0a20e9f820b79ff", 317687},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Laura Bow 2 - English DOS Non-Interactive Demo (from FRG)
// Executable scanning reports "x.yyy.zzz"
@@ -1593,7 +1646,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "24dffc5db1d88c7999f13e8767ed7346", 855},
{"resource.000", 0, "2b2b1b4f7584f9b38fd13f6ab95634d1", 781912},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Laura Bow 2 - English DOS Floppy
// Executable scanning reports "2.000.274"
@@ -1602,7 +1655,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "610bfd9a852004222f0faaf5fc9e630a", 6489},
{"resource.000", 0, "57084910bc923bff5d6d9bc1b56e9604", 5035964},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Laura Bow 2 - English DOS CD (from "The Roberta Williams Antology"/1996)
// Executable scanning reports "1.001.072", VERSION file reports "1.1" (from jvprat)
@@ -1611,7 +1664,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "a70945e61ba7ac7bfea6b7bd72c6aec5", 7274},
{"resource.000", 0, "82578b8d5a7e09c4c58891ca49fae35b", 5598672},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO0() },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Laura Bow 2 v1.1 - French DOS Floppy (from Hkz)
{"laurabow2", "", {
@@ -1619,7 +1672,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "57084910bc923bff5d6d9bc1b56e9604", 5028766},
{"resource.msg", 0, "0fceedfbdd85a4bc7851fdd9dd2d2f19", 278253},
AD_LISTEND},
- Common::FR_FRA, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::FR_FRA, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Laura Bow 2 v1.1 - German DOS Floppy (from Tobis87, updated info from markcoolio in bug report #2723787, updated info from #2797962))
// Executable scanning reports "2.000.274"
@@ -1628,7 +1681,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "57084910bc923bff5d6d9bc1b56e9604", 5028766},
{"resource.msg", 0, "795c928cd00dfec9fbc62ebcd12e1f65", 303185},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::DE_DEU, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Laura Bow 2 - Spanish DOS CD (from jvprat)
// Executable scanning reports "2.000.274", VERSION file reports "1.000.000, May 10, 1994"
@@ -1637,7 +1690,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "57084910bc923bff5d6d9bc1b56e9604", 5028766},
{"resource.msg", 0, "71f1f0cd9f082da2e750c793a8ed9d84", 286141},
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformPC, ADGF_CD, GUIO0() },
+ Common::ES_ESP, Common::kPlatformPC, ADGF_CD, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 1 EGA Remake - English DOS (from spookypeanut)
// SCI interpreter version 0.000.510 (or 0.000.577?)
@@ -1648,7 +1701,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "24c958bc922b07f91e25e8c93aa01fcf", 491230},
{"resource.003", 0, "685cd6c1e05a695ab1e0db826337ee2a", 553279},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#if 0
// The resource.002 file, contained in disk 3, is broken in this version
@@ -1666,7 +1719,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "5790ac0505f7ca98d4567132b875eb1e", 681041},
{"resource.003", 0, "4a34c3367c2fe7eb380d741374da1989", 572251},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#endif
// Larry 1 VGA Remake - English DOS (from spookypeanut)
@@ -1677,7 +1730,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "ec20246209d7b19f38989261e5c8f5b8", 1111226},
{"resource.002", 0, "85d6935ef77e6b0e16bc307640a0d913", 1088312},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 1 VGA Remake - English DOS (from FRG)
// SCI interpreter version 1.000.510
@@ -1687,7 +1740,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "d34cadb11e1aefbb497cf91bc1d3baa7", 1114688},
{"resource.002", 0, "85b030bb66d5342b0a068f1208c431a8", 1078443},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 1 VGA Remake - English Macintosh (from omer_mor, bug report #3328262)
{"lsl1sci", "SCI", {
@@ -1696,7 +1749,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "aa6f153f70f1e32d1bde465fff08eecf", 1137418},
{"resource.002", 0, "b22c616aa789ebef990290c7ffd86548", 1097477},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformMacintosh, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformMacintosh, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 1 VGA Remake - English DOS Non-Interactive Demo
// SCI interpreter version 1.000.084
@@ -1704,7 +1757,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "434e1f6c39d71647b34f0ee57b2bbd68", 444},
{"resource.001", 0, "0c0768215c562d9dace4a5ca53696cf3", 359913},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 1 VGA Remake - Spanish DOS (from the Leisure Suit Larry Collection, also includes english language)
// Executable scanning reports "1.SQ4.057", VERSION file reports "1.000"
@@ -1717,7 +1770,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "3fe2a3aec0ed53c7d6db1845a67e3aa2", 1095908},
{"resource.003", 0, "ac175df0ea9a2cba57f0248651856d27", 376556},
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO1(GUIO_NOSPEECH) },
+ Common::ES_ESP, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 1 VGA Remake - Russian DOS (also includes english language?!)
// Executable scanning reports "1.000.510", VERSION file reports "2.0"
@@ -1728,7 +1781,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "bc8ca10c807515d959cbd91f9ba47735", 1123759},
{"resource.002", 0, "b7409ab32bc3bee2d6cce887cd33f2b6", 1092160},
AD_LISTEND},
- Common::RU_RUS, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO1(GUIO_NOSPEECH) },
+ Common::RU_RUS, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 1 VGA Remake - Polish DOS (from Polish Leisure Suit Larry Collection, official release)
// SCI interpreter version 1.000.577, VERSION file reports "2.1" (this release does NOT include english text)
@@ -1736,7 +1789,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "58330a85767e42a2487129913283ab5b", 3228},
{"resource.000", 0, "b6097ff35cdc8469f02150fe2f824198", 4781210},
AD_LISTEND},
- Common::PL_POL, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::PL_POL, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 2 - English Amiga (from www.back2roots.org)
// Executable scanning reports "x.yyy.zzz"
@@ -1748,7 +1801,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "a0d4a625311d307257da7fc43d00459d", 570356},
{"resource.004", 0, "a0d4a625311d307257da7fc43d00459d", 717844},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 2 - English DOS Non-Interactive Demo
// Executable scanning reports "x.yyy.zzz"
@@ -1757,7 +1810,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "03dba704bb77da55a91ad27b5a3cac09", 528},
{"resource.001", 0, "9f5520f0297206928df0b0b36493cd33", 127532},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 2 - English DOS
// SCI interpreter version 0.000.409
@@ -1770,7 +1823,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "4a24443a25e2b1492462a52809605dc2", 277732},
{"resource.006", 0, "4a24443a25e2b1492462a52809605dc2", 345683},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 2 - English DOS
// SCI interpreter version 0.000.343
@@ -1785,7 +1838,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
//{"resource.006", 0, "96033f57accfca903750413fd09193c8", 345818},
{"resource.006", 0, "96033f57accfca903750413fd09193c8", -1}, // 345818 or 208739
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 2 - English DOS (supplied by ssburnout in bug report #3049193)
// 1.000.011 3x3.5" (label: Int. #0.000.343)
@@ -1795,7 +1848,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "96033f57accfca903750413fd09193c8", 407014},
{"resource.003", 0, "96033f57accfca903750413fd09193c8", 592834},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 2 - English DOS (supplied by ssburnout in bug report #3049193)
// 1.002.000 3x3.5" (label: INT#0.000.409)
@@ -1805,7 +1858,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "4a24443a25e2b1492462a52809605dc2", 406935},
{"resource.003", 0, "4a24443a25e2b1492462a52809605dc2", 592533},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 3 - English Amiga (from www.back2roots.org)
// Executable scanning reports "1.002.032"
@@ -1819,7 +1872,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "f408e59cbee1457f042e5773b8c53951", 651634},
{"resource.005", 0, "433911eb764089d493aed1f958a5615a", 524259},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 3 - English DOS
// SCI interpreter version 0.000.572
@@ -1830,7 +1883,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "f18441027154292836b973c655fa3175", 506807},
{"resource.004", 0, "f18441027154292836b973c655fa3175", 513651},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 3 - English DOS (supplied by ssburnout in bug report #3049193)
// 1.021 8x5.25" (label: Int#5.15.90)
@@ -1844,7 +1897,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "f18441027154292836b973c655fa3175", 282649},
{"resource.007", 0, "f18441027154292836b973c655fa3175", 257178},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 3 - English DOS
// SCI interpreter version 0.000.572
@@ -1858,7 +1911,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "f18441027154292836b973c655fa3175", 282465},
{"resource.007", 0, "f18441027154292836b973c655fa3175", 257174},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 3 - English DOS Non-Interactive Demo
// SCI interpreter version 0.000.530
@@ -1867,7 +1920,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "f773d79b93dfd4052ec8c1cc64c1e6ab", 76525},
{"resource.002", 0, "f773d79b93dfd4052ec8c1cc64c1e6ab", 268299},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 3 - German DOS (from Tobis87, updated info from markcoolio in bug report #2723832, also includes english language)
// Executable scanning reports "S.old.123"
@@ -1879,7 +1932,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "3827a9b17b926e12dcc336860f50612a", 587036},
{"resource.004", 0, "3827a9b17b926e12dcc336860f50612a", 691932},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 3 - French DOS (provided by richiefs in bug report #2670691, also includes english language)
// Executable scanning reports "S.old.123"
@@ -1891,7 +1944,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "65f1bdaa20f6d0470e9d969f22473873", 586921},
{"resource.004", 0, "65f1bdaa20f6d0470e9d969f22473873", 690826},
AD_LISTEND},
- Common::FR_FRA, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::FR_FRA, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 3 1.050 Fr/En (9 x 5.25" disks)
// Provided by ssburnout in bug report #3046779
@@ -1905,7 +1958,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "65f1bdaa20f6d0470e9d969f22473873", 325292},
{"resource.007", 0, "65f1bdaa20f6d0470e9d969f22473873", 308982},
AD_LISTEND},
- Common::FR_FRA, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO1(GUIO_NOSPEECH) },
+ Common::FR_FRA, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 5 - English Amiga
// Executable scanning reports "1.004.023"
@@ -1920,7 +1973,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "f8b2d1137bb767e5d232056b99dd69eb", 623621},
{"resource.006", 0, "bafc64e3144f115dc58c6aee02de98fb", 715598},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 5 - German Amiga (also includes english language)
// Executable scanning reports "1.004.024"
@@ -1936,7 +1989,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "bafc64e3144f115dc58c6aee02de98fb", 754966},
{"resource.007", 0, "59eba83ad465b08d763b44f86afa86f6", 683135},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformAmiga, ADGF_ADDENGLISH, GUIO1(GUIO_NOSPEECH) },
+ Common::DE_DEU, Common::kPlatformAmiga, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 5 - English DOS Non-Interactive Demo (from FRG)
// SCI interpreter version 1.000.181
@@ -1944,7 +1997,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "efe8d3f45ce4f6bd9a6643e0ac8d2a97", 504},
{"resource.001", 0, "8bd8d9c0b5f455ee1269d63ce86c50dd", 531380},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 5 - English DOS (from spookypeanut)
// SCI interpreter version 1.000.510
@@ -1959,7 +2012,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "dda27ce00682aa76198dac124bbbe334", 1024810},
{"resource.007", 0, "ac443fae1285fb359bf2b2bc6a7301ae", 1030656},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 5 - English Macintosh (from omer_mor, bug report #3328257)
{"lsl5", "", {
@@ -1973,7 +2026,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "dda27ce00682aa76198dac124bbbe334", 1110043},
{"resource.007", 0, "ac443fae1285fb359bf2b2bc6a7301ae", 989801},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformMacintosh, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformMacintosh, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 5 - German DOS (from Tobis87)
// SCI interpreter version T.A00.196
@@ -1988,7 +2041,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "dda27ce00682aa76198dac124bbbe334", 1021774},
{"resource.007", 0, "ac443fae1285fb359bf2b2bc6a7301ae", 993408},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO1(GUIO_NOSPEECH) },
+ Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 5 - French DOS (provided by richiefs in bug report #2670691)
// Executable scanning reports "1.lsl5.019"
@@ -2004,7 +2057,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "dda27ce00682aa76198dac124bbbe334", 946540},
{"resource.007", 0, "ac443fae1285fb359bf2b2bc6a7301ae", 958842},
AD_LISTEND},
- Common::FR_FRA, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO1(GUIO_NOSPEECH) },
+ Common::FR_FRA, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 5 - Spanish DOS (from the Leisure Suit Larry Collection)
// Executable scanning reports "1.ls5.006", VERSION file reports "1.000, 4/21/92"
@@ -2020,7 +2073,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "dda27ce00682aa76198dac124bbbe334", 1015136},
{"resource.007", 0, "ac443fae1285fb359bf2b2bc6a7301ae", 987222},
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO1(GUIO_NOSPEECH) },
+ Common::ES_ESP, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 5 - Italian DOS Floppy (from glorifindel)
// SCI interpreter version 1.000.510 (just a guess)
@@ -2028,7 +2081,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "a99776df795127f387cb35dae872d4e4", 5919},
{"resource.000", 0, "a8989a5a89e7d4f702b26b378c7a357a", 7001981},
AD_LISTEND},
- Common::IT_ITA, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::IT_ITA, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 5 1.0 EGA DOS (8 x 3.5" disks)
// Provided by ssburnout in bug report #3046806
@@ -2043,7 +2096,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "f6046a8445422f17d40b1b10ab21ebf3", 568551},
{"resource.007", 0, "640ee65595d40372ef95462f2c1ae28a", 593429},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 5 EGA
// Supplied by omer_mor in bug report #3049771
@@ -2054,7 +2107,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "5a55af4e40728b1a8103dc47ad2afa8d", 1100539},
{"resource.003", 0, "16f4d8fb1b526125edaca4fc6cbb7530", 1064563},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 6 - English DOS (from spookypeanut)
// SCI interpreter version 1.001.113
@@ -2062,7 +2115,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "bb8a39d9e2a77ba449a1e591109ad9a8", 6973},
{"resource.000", 0, "4462fe48c7452d98fddcec327a3e738d", 5789138},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 6 - English/German/French DOS CD - LOWRES
// SCI interpreter version 1.001.115
@@ -2070,7 +2123,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "0b91234b7112782962cb480b7791b6e2", 7263},
{"resource.000", 0, "57d5fe8bb9e044158514476ea7678eb0", 5754790},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO0() },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 6 - German DOS CD - LOWRES (provided by richiefs in bug report #2670691)
// SCI interpreter version 1.001.115
@@ -2078,7 +2131,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "bafe85f32738854135991d4324ad147e", 7268},
{"resource.000", 0, "f6cbc6da7b90ea135883e0759848ca2c", 5773160},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformPC, ADGF_CD, GUIO0() },
+ Common::DE_DEU, Common::kPlatformPC, ADGF_CD, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 6 - French DOS CD - LOWRES (provided by richiefs in bug report #2670691)
// SCI interpreter version 1.001.115
@@ -2086,7 +2139,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "97797ea775baaf18a1907d357d3c0ea6", 7268},
{"resource.000", 0, "f6cbc6da7b90ea135883e0759848ca2c", 5776092},
AD_LISTEND},
- Common::FR_FRA, Common::kPlatformPC, ADGF_CD, GUIO0() },
+ Common::FR_FRA, Common::kPlatformPC, ADGF_CD, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 6 - Spanish DOS - LOWRES (from the Leisure Suit Larry Collection)
// Executable scanning reports "1.001.113", VERSION file reports "1.000, 11.06.93, FIVE PATCHES ADDED TO DISK 6 ON 11-18-93"
@@ -2094,7 +2147,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "633bf8f42170b6271019917c8009989b", 6943},
{"resource.000", 0, "7884a8db9253e29e6b37a2651fd90ba3", 5733116},
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::ES_ESP, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Crazy Nick's Software Picks: Leisure Suit Larry's Casino - English DOS (from the Leisure Suit Larry Collection)
// Executable scanning reports "1.001.029", VERSION file reports "1.000"
@@ -2102,35 +2155,35 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "194f1578f2624db813c9072359ad1639", 783},
{"resource.001", 0, "3733433b517ec3d14a3331d9ab3842ae", 344830},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Crazy Nick's Software Picks: King Graham's Board Game Challenge
{"cnick-kq", "", {
{"resource.map", 0, "44bc538a5cd24b39ffccc967c0ebf84d", 1137},
{"resource.001", 0, "470e7a4a3504635e70b623c44461e1ac", 451272},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Crazy Nick's Software Picks: Parlor Games with Laura Bow
{"cnick-laurabow", "", {
{"resource.map", 0, "3b826bfe64f8ff1ccf30eef93cd2f727", 999},
{"resource.001", 0, "985ac8db6f636f2b4334c04b0fbb44fb", 336698},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Crazy Nick's Software Picks: Robin Hood's Game of Skill and Chance
{"cnick-longbow", "", {
{"resource.map", 0, "4a5c81f485a2416bde12978506f2fb5f", 897},
{"resource.001", 0, "ef16dc9e867eb8eeb5b13e110b90bd4b", 571466},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Crazy Nick's Software Picks: Roger Wilco's Spaced Out Game Pack
{"cnick-sq", "", {
{"resource.map", 0, "b4d95b02d84e297441bd999d34eaa6b1", 879},
{"resource.001", 0, "82ff2b64a60117886fbcd6a3a8c977c6", 364921},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#ifdef ENABLE_SCI32
// Larry 6 - English/German DOS CD - HIRES
@@ -2139,7 +2192,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "0c0804434ea62278dd15032b1947426c", 8872},
{"resource.000", 0, "9a9f4870504444cda863dd14d077a680", 18520872},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 6 - German DOS CD - HIRES (provided by richiefs in bug report #2670691)
// SCI interpreter version 2.100.002
@@ -2147,7 +2200,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "badfdf446ffed569a310d2c63a249421", 8896},
{"resource.000", 0, "bd944d2b06614a5b39f1586906f0ee88", 18534274},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformPC, ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::DE_DEU, Common::kPlatformPC, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 6 - French DOS CD - HIRES (provided by richiefs in bug report #2670691)
// SCI interpreter version 2.100.002
@@ -2155,7 +2208,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "d184e9aa4f2d4b5670ddb3669db82cda", 8896},
{"resource.000", 0, "bd944d2b06614a5b39f1586906f0ee88", 18538987},
AD_LISTEND},
- Common::FR_FRA, Common::kPlatformPC, ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::FR_FRA, Common::kPlatformPC, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 7 - English DOS Demo (provided by richiefs in bug report #2670691)
// SCI interpreter version 2.100.002
@@ -2163,7 +2216,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"ressci.000", 0, "5cc6159688b2dc03790a67c90ccc67f9", 10195878},
{"resmap.000", 0, "6a2b2811eef82e87cde91cf1de845af8", 2695},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO | ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO | ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#ifdef ENABLE_SCI3_GAMES
// Larry 7 - English DOS CD (from spookypeanut)
@@ -2172,7 +2225,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.000", 0, "eae93e1b1d1ccc58b4691c371281c95d", 8188},
{"ressci.000", 0, "89353723488219e25589165d73ed663e", 66965678},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 7 - German DOS (from Tobis87)
// SCI interpreter version 3.000.000
@@ -2180,7 +2233,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.000", 0, "c11e6bfcfc2f2d05da47e5a7df3e9b1a", 8188},
{"ressci.000", 0, "a8c6817bb94f332ff498a71c8b47f893", 66971724},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformPC, ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::DE_DEU, Common::kPlatformPC, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 7 - French DOS (provided by richiefs in bug report #2670691)
// SCI interpreter version 3.000.000
@@ -2188,7 +2241,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.000", 0, "4407849fd52fe3efb0c30fba60cd5cd4", 8206},
{"ressci.000", 0, "dc37c3055fffbefb494ff22b145d377b", 66964472},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformPC, ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::DE_DEU, Common::kPlatformPC, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 7 - Italian DOS CD (from glorifindel)
// SCI interpreter version 3.000.000
@@ -2196,7 +2249,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.000", 0, "9852a97141f789413f29bf956052acdb", 8212},
{"ressci.000", 0, "440b9fed89590abb4e4386ed6f948ee2", 67140181},
AD_LISTEND},
- Common::IT_ITA, Common::kPlatformPC, ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::IT_ITA, Common::kPlatformPC, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Larry 7 - Spanish DOS (from the Leisure Suit Larry Collection)
// Executable scanning reports "3.000.000", VERSION file reports "1.0s"
@@ -2204,7 +2257,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.000", 0, "8f3d603e1acc834a5d598b30cdfc93f3", 8188},
{"ressci.000", 0, "32792f9bc1bf3633a88b382bb3f6e40d", 67071418},
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformPC, ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::ES_ESP, Common::kPlatformPC, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#endif
// Lighthouse - English Windows Demo (from jvprat)
@@ -2213,7 +2266,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "543124606352bfa5e07696ddf2a669be", 64},
{"resource.000", 0, "5d7714416b612463d750fb9c5690c859", 28952},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO | ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO | ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#ifdef ENABLE_SCI3_GAMES
// Lighthouse - English Windows Demo
@@ -2222,7 +2275,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.000", 0, "3bdee7a16926975a4729f75cf6b80a92", 1525},
{"ressci.000", 0, "3c585827fa4a82f4c04a56a0bc52ccee", 11494351},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO | ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO | ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Lighthouse - English DOS (from jvprat)
// Executable scanning reports "3.000.000", VERSION file reports "1.1"
@@ -2232,7 +2285,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.002", 0, "c68db5333f152fea6ca2dfc75cad8b34", 7573},
{"ressci.002", 0, "175468431a979b9f317c294ce3bc1430", 94628315},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Lighthouse - Spanish DOS (from jvprat)
// Executable scanning reports "3.000.000", VERSION file reports "1.1"
@@ -2242,7 +2295,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.002", 0, "e7dc85884a2417e2eff9de0c63dd65fa", 7630},
{"ressci.002", 0, "3c8d627c555b0e3e4f1d9955bc0f0df4", 94631127},
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformPC, ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::ES_ESP, Common::kPlatformPC, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#endif // ENABLE_SCI3_GAMES
#endif // ENABLE_SCI32
@@ -2253,7 +2306,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "8be56a3a88c065ee00c02c0e29199f3a", 14643},
{"resource.001", 0, "9e33566515b18bee7915db448063bba2", 871853},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Mixed-Up Fairy Tales - English DOS Floppy EGA (from omer_mor, bug report #3035350)
{"fairytales", "EGA", {
@@ -2264,7 +2317,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "509b2467ba779100d5933ed51a9ae32f", 560255},
{"resource.004", 0, "93afc85d5ffa60ea555d6cc336d22c03", 651109},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Mixed-Up Fairy Tales v1.000 - English DOS (supplied by markcoolio in bug report #2723791)
// Executable scanning reports "1.000.145"
@@ -2276,7 +2329,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "b1288e0821ee358d1ffe877e5900c8ec", 1047565},
{"resource.004", 0, "f79daa70390d73746742ffcfc3dc4471", 937580},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Mixed-Up Fairy Tales - English DOS Floppy (from jvprat)
// Executable scanning reports "1.000.145", Floppy label reports "1.0, 11.13.91", VERSION file reports "1.000"
@@ -2287,7 +2340,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "564f516d991032e781492592a4eaa275", 1414142},
{"resource.003", 0, "dd6cef0c592eadb7e6be9a25307c57a2", 1344719},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Mixed-Up Mother Goose - English Amiga (from www.back2roots.org)
// Executable scanning reports "1.003.009"
@@ -2297,7 +2350,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "fb552ae550ca1dac19ed8f6a3767612d", 262885},
{"resource.002", 0, "fb552ae550ca1dac19ed8f6a3767612d", 817191},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Mixed-Up Mother Goose - English DOS Floppy EGA (from omer_mor, bug report #3035354)
{"mothergoose", "EGA", {
@@ -2305,7 +2358,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "d893892d62b3f061357291d66775e360", 239906},
{"resource.002", 0, "d893892d62b3f061357291d66775e360", 719398},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Mixed-Up Mother Goose - English DOS Floppy EGA (supplied by ssburnout in bug report #3049193)
// 1.011 5x5.25" (label: Int#8.2.90)
@@ -2318,7 +2371,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "dbbc22f124533ce308bc386b08956326", 146251},
{"resource.005", 0, "2ba5348e7fad641b9c4c7ff7c7cf4e68", 110979},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Mixed-Up Mother Goose v2.000 - English DOS Floppy (supplied by markcoolio in bug report #2723795)
// Executable scanning reports "1.001.031"
@@ -2326,7 +2379,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "52aae15e493cafd1da7e1c9b657a5bb9", 7026},
{"resource.000", 0, "b7ecd8ae9e254e80310b5a668b276e6e", 2948975},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Mixed-Up Mother Goose - English DOS CD (from jvprat)
// Executable scanning reports "x.yyy.zzz"
@@ -2335,7 +2388,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "1c7f311b0a2c927b2fbe81ae341fb2f6", 5790},
{"resource.001", 0, "5a0ed1d745855148364de1b3be099bac", 4369438},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO0() },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Mixed-Up Mother Goose - English Windows Interactive Demo
// Executable scanning reports "x.yyy.zzz"
@@ -2343,19 +2396,19 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "87f9dc1cafc4d4fa835fb2f00cf3a6ef", 4560},
{"resource.001", 0, "5a0ed1d745855148364de1b3be099bac", 2070072},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Mixed-Up Mother Goose - FM-Towns (supplied by abevi in bug report #3038720)
{"mothergoose256", "", {
{"resource.map", 0, "b11e971ccd2040bebba59dfb409a08ef", 5772},
{"resource.001", 0, "d49625d9b8005ec01c852f8322a82867", 4330713},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformFMTowns, 0, GUIO1(GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformFMTowns, 0, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
{"mothergoose256", "", {
{"resource.map", 0, "b11e971ccd2040bebba59dfb409a08ef", 5772},
{"resource.001", 0, "d49625d9b8005ec01c852f8322a82867", 4330713},
AD_LISTEND},
- Common::JA_JPN, Common::kPlatformFMTowns, 0, GUIO1(GUIO_NOASPECT) },
+ Common::JA_JPN, Common::kPlatformFMTowns, 0, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#ifdef ENABLE_SCI32
// Mixed-Up Mother Goose Deluxe - English Windows/DOS CD (supplied by markcoolio in bug report #2723810)
@@ -2364,7 +2417,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "5159a1578c4306bfe070a3e4d8c2e1d3", 4741},
{"resource.000", 0, "1926925c95d82f0999590e93b02887c5", 15150768},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Mixed-Up Mother Goose Deluxe - Multilingual Windows CD (English/French/German/Spanish)
// Executable scanning reports "2.100.002"
@@ -2372,7 +2425,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.000", 0, "ef611af561898dcfea87846919ebf3eb", 4969},
{"ressci.000", 0, "227685bc59d90821978d330713e44a7a", 17205800},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#endif // ENABLE_SCI32
// Ms. Astro Chicken - English DOS
@@ -2381,7 +2434,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "5b457cbe5042f557e5b610148171f6c0", 1158},
{"resource.001", 0, "453ea81ef66a50cbe33ce06302afe47f", 229737},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#ifdef ENABLE_SCI32
// Phantasmagoria - English DOS (from jvprat)
@@ -2402,7 +2455,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.007", 0, "afbd16ea77869a720afa1c5371de107d", 7972},
//{"ressci.007", 0, "3aae6559aa1df273bc542d5ac6330d75", 25859038},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Phantasmagoria - English DOS Demo
// Executable scanning reports "2.100.002"
@@ -2410,7 +2463,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.001", 0, "416138651ea828219ca454cae18341a3", 11518},
{"ressci.001", 0, "3aae6559aa1df273bc542d5ac6330d75", 65844612},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO | ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO | ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Phantasmagoria - English DOS/Windows (GOG version) - ressci.* merged in ressci.000
// Windows executable scanning reports "2.100.002" - "Sep 19 1995 15:09:43"
@@ -2421,7 +2474,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"ressci.000", 0, "cd5967f9b9586e3380645961c0765be3", 116822037},
{"resmap.000", 0, "3cafc1c6a53945c1f3babbfd6380c64c", 16468},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Phantasmagoria - English Macintosh
// NOTE: This only contains disc 1 files (as well as the two persistent files:
@@ -2437,7 +2490,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
// Data8-12 are empty
{"Data13", 0, "6d2c450fca19a69b5af74ed5b03c0a17", 14923328},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK | ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK | ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#ifdef ENABLE_SCI3_GAMES
// Phantasmagoria 2 - English Windows (from jvprat)
@@ -2454,7 +2507,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.005", 0, "8bd5ceeedcbe16dfe55d1b90dcd4be84", 1942},
{"ressci.005", 0, "05f9fe2bee749659acb3cd2c90252fc5", 67905112},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Phantasmagoria 2 - English DOS (GOG version) - ressci.* merged in ressci.000
// Executable scanning reports "3.000.000" - "Dec 07 1996 09:29:03"
@@ -2464,7 +2517,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"ressci.000", 0, "c54f26d9f43f908151263254b6d97053", 108134481},
{"resmap.000", 0, "de154a223a9ef4ea7358b76adc38ef5b", 2956},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#endif // ENABLE_SCI3_GAMES
#endif // ENABLE_SCI32
@@ -2475,7 +2528,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "72726dc81c1b4c1110c486be77369bc8", 5179},
{"resource.000", 0, "670d0c53622429f4b11275caf7f8d292", 5459574},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Pepper - English DOS Non-Interactive Demo
// Executable scanning reports "1.001.060", VERSION file reports "1.000"
@@ -2483,7 +2536,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "379bb4fb896630b14f2d91ed21e36ba1", 984},
{"resource.000", 0, "118f6c31a93ec7fd9a231c61125229e3", 645494},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Pepper - English DOS/Windows Interactive Demo
// Executable scanning reports "1.001.069", VERSION file reports ".001"
@@ -2491,7 +2544,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "975e8df76106a5c13d12ab674f906a02", 2514},
{"resource.000", 0, "e6a918a2dd7a4bcecd8fb389f43287c2", 1698164},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Pepper - English DOS Interactive Demo
// Executable scanning reports "1.001.072", VERSION file reports "1.000"
@@ -2499,7 +2552,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "9c9b7b900651a370dd3fb38d478b1798", 2524},
{"resource.000", 0, "e6a918a2dd7a4bcecd8fb389f43287c2", 1713544},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Police Quest 1 VGA Remake - English DOS (from the Police Quest Collection)
// Executable scanning reports "1.001.029", VERSION file reports "2.000"
@@ -2507,7 +2560,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "35efa814fb994b1cbdac9611e401da67", 5013},
{"resource.000", 0, "e0d5ddf34eda903a38f0837e2aa7145b", 6401433},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Police Quest 2 - English Amiga (from www.back2roots.org)
// SCI interpreter version 0.000.685 (just a guess)
@@ -2518,7 +2571,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "499737c21a28ac026e11ab817100d610", 511099},
{"resource.003", 0, "e008f5d6e2a7c4d4a0da0173e4fa8f8b", 553970},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Police Quest 2 - English DOS Non-Interactive Demo
// Executable scanning reports "0.000.413"
@@ -2526,7 +2579,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "8b77d0d4650c2052b356cece28294b58", 576},
{"resource.001", 0, "376ef6d6eaaeed66e1424bd219c4b9ab", 215398},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Police Quest 2 - English DOS (provided by richiefs in bug report #2670691)
// SCI interpreter version 0.000.395
@@ -2539,7 +2592,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "77f02def3094af804fd2371db25b7100", 349899},
{"resource.006", 0, "77f02def3094af804fd2371db25b7100", 354991},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Police Quest 2 - English DOS (from the Police Quest Collection)
// Executable scanning reports "0.000.490"
@@ -2549,7 +2602,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "77f02def3094af804fd2371db25b7100", 546000},
{"resource.003", 0, "77f02def3094af804fd2371db25b7100", 591851},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Police Quest 2 - English DOS (from FRG)
// SCI interpreter version 0.000.395
@@ -2559,7 +2612,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "77f02def3094af804fd2371db25b7100", 542897},
{"resource.003", 0, "77f02def3094af804fd2371db25b7100", 586857},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Police Quest 2 English DOS 1.001.006 (supplied by merkur-kun in bug report #3028479)
{"pq2", "", {
@@ -2568,7 +2621,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "77f02def3094af804fd2371db25b7100", 541261},
{"resource.003", 0, "77f02def3094af804fd2371db25b7100", 587511},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Police Quest 2 - Japanese PC-98 (also includes english language)
// SCI interpreter version unknown
@@ -2578,7 +2631,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "05fdee43a228dd6ea4d1a92ccae3f788", 637662},
{"resource.003", 0, "05fdee43a228dd6ea4d1a92ccae3f788", 684395},
AD_LISTEND},
- Common::JA_JPN, Common::kPlatformPC98, ADGF_ADDENGLISH, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::JA_JPN, Common::kPlatformPC98, ADGF_ADDENGLISH, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Police Quest 3 - English Amiga
// Executable scanning reports "1.004.024"
@@ -2591,7 +2644,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "630bfa65beb05f743552704ac2899dae", 759891},
{"resource.004", 0, "7b229fbdf30d670d0728cede3e984a7e", 838663},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Police Quest 3 - German Amiga (also includes english language)
// Executable scanning reports "1.004.024"
@@ -2605,7 +2658,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "6258d5dd85898d8e218eb8113ebc9059", 722738},
{"resource.005", 0, "6258d5dd85898d8e218eb8113ebc9059", 704485},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformAmiga, ADGF_ADDENGLISH, GUIO1(GUIO_NOSPEECH) },
+ Common::DE_DEU, Common::kPlatformAmiga, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Police Quest 3 - English DOS (from the Police Quest Collection)
// Executable scanning reports "T.A00.178", VERSION file reports "1.00"
@@ -2618,7 +2671,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "8791b9eef53edf77c2dac950142221d3", 1159791},
{"resource.004", 0, "1b91e891a3c60a941dac0eecdf83375b", 1143606},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Police Quest 3 - English DOS Non-Interactive Demo
// Executable scanning reports "T.A00.052"
@@ -2628,7 +2681,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "277f97771f7a6d89677141f02da313d6", 65150},
{"resource.001", 0, "5c5a551b6c86cce2ee75becb90e0b586", 624411},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Police Quest 3 - German DOS (supplied by markcoolio in bug report #2723837, also includes english language)
// Executable scanning reports "T.A00.178"
@@ -2641,7 +2694,14 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "4836f460f4cfc8de61e2df4c45775504", 1180956},
{"resource.004", 0, "0c3eb84b9755852d9e795e0d5c9373c7", 1171760},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO1(GUIO_NOSPEECH) },
+ Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+
+ // Police Quest 3 - Spanish DOS v1.000 - Supplied by dianiu in bug report #3555647
+ {"pq3", "", {
+ {"resource.map", 0, "ffa0b4631c4e36d69631256d19ba29e7", 5421},
+ {"resource.000", 0, "5ee460af3d70c06a745cc482b6c783ba", 5410263},
+ AD_LISTEND},
+ Common::ES_ESP, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Police Quest 3 EGA
// Reported by musiclyinspired in bug report #3046573
@@ -2654,7 +2714,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "b96a86ab681769e4cbb439670d967ca6", 449682},
{"resource.005", 0, "9e6c53a0e7eef53694d260fade8b1fc7", 724000},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Police Quest 4 - English DOS Non-Interactive Demo (from FRG)
// SCI interpreter version 1.001.096
@@ -2662,7 +2722,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "be56f87a1c4a13062a30a362df860c2f", 1472},
{"resource.000", 0, "527d5684016e6816157cd15d9071b11b", 1121310},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#ifdef ENABLE_SCI32
// Police Quest 4 - English DOS CD (from the Police Quest Collection)
@@ -2671,7 +2731,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "379dfe80ed6bd16c47e4b950c4722eac", 11374},
{"resource.000", 0, "fd316a09b628b7032248139003369022", 18841068},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_CD | ADGF_UNSTABLE, GUIO0() },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_CD | ADGF_UNSTABLE, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Police Quest 4 - German DOS CD (German text, English speech)
// Supplied by markcoolio in bug report #3392955
@@ -2679,7 +2739,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "a398076371ed0e1e706c8f9fb9fc7ac5", 11386},
{"resource.000", 0, "6ff21954e0a2c5992279e7eb787c8d56", 18918747},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformPC, ADGF_CD | ADGF_UNSTABLE, GUIO0() },
+ Common::DE_DEU, Common::kPlatformPC, ADGF_CD | ADGF_UNSTABLE, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Police Quest 4 - English DOS
// SCI interpreter version 2.000.000 (a guess?)
@@ -2687,7 +2747,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "aed9643158ccf01b71f359db33137f82", 9895},
{"resource.000", 0, "da383857b3be1e4514daeba2524359e0", 15141432},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Police Quest 4 - French DOS (supplied by abevi in bug report #2612718)
// SCI interpreter version 2.000.000
@@ -2695,7 +2755,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "008030846edcc7c5c7a812c7f4ae4ceb", 9256},
{"resource.000", 0, "6ba98bd2e436739d87ecd2a9b99cabb4", 14730153},
AD_LISTEND},
- Common::FR_FRA, Common::kPlatformPC, ADGF_UNSTABLE, GUIO1(GUIO_NOSPEECH) },
+ Common::FR_FRA, Common::kPlatformPC, ADGF_UNSTABLE, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Police Quest 4 - German DOS (supplied by markcoolio in bug report #2723840)
// SCI interpreter version 2.000.000 (a guess?)
@@ -2703,7 +2763,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "2393ee728ab930b2762cb5889f9b5aff", 9256},
{"resource.000", 0, "6ba98bd2e436739d87ecd2a9b99cabb4", 14730155},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformPC, ADGF_UNSTABLE, GUIO1(GUIO_NOSPEECH) },
+ Common::DE_DEU, Common::kPlatformPC, ADGF_UNSTABLE, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Police Quest: SWAT - English DOS/Windows Demo (from jvprat)
// Executable scanning reports "2.100.002", VERSION file reports "0.001.200"
@@ -2711,7 +2771,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "8c96733ef94c21526792f7ca4e3f2120", 1648},
{"resource.000", 0, "d8892f1b8c56c8f7704325460f49b300", 3676175},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO | ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO | ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Police Quest: SWAT - English DOS (from GOG.com)
// Executable scanning reports "2.100.002", VERSION file reports "1.0c"
@@ -2719,7 +2779,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.000", 0, "1c2563fee189885e29d9348f37306d94", 12175},
{"ressci.000", 0, "b2e1826ca81ce2e7e764587f5a14eee9", 127149181},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Police Quest: SWAT - English Windows (from the Police Quest Collection)
// Executable scanning reports "2.100.002", VERSION file reports "1.0c"
@@ -2734,7 +2794,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.004", 0, "4228038906f041623e65789500b22285", 6835},
{"ressci.004", 0, "b7e619e6ecf62fe65d5116a3a422e5f0", 46223872},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#endif // ENABLE_SCI32
// Quest for Glory 1 / Hero's Quest - English DOS 3.5" Floppy (supplied by merkur in bug report #2718784)
@@ -2747,7 +2807,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "7ab2bf8e224b57f75e0cd6e4ba790761", 642203},
{"resource.004", 0, "7ab2bf8e224b57f75e0cd6e4ba790761", 641688},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Quest for Glory 1 / Hero's Quest - English DOS 3.5" Floppy (supplied by alonzotg in bug report #3206006)
{"qfg1", "", {
@@ -2758,7 +2818,32 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "7ab2bf8e224b57f75e0cd6e4ba790761", 642203},
{"resource.004", 0, "7ab2bf8e224b57f75e0cd6e4ba790761", 641688},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+
+ // Quest for Glory 1 / Hero's Quest - English DOS 3.5" Floppy v1.102 Int#0.000.629 (suppled by digitoxin1 in bug report #3554611)
+ {"qfg1", "", {
+ {"resource.map", 0, "b162dbd4632250d4d83bed46d0783c10", 6396},
+ {"resource.000", 0, "40332d3ebfc70a4b6a6a0443c2763287", 78800},
+ {"resource.001", 0, "a270012fa74445d74c044d1b65a9ff8c", 459835},
+ {"resource.002", 0, "e64004e020fdf1813be52b639b08be89", 635561},
+ {"resource.003", 0, "f0af87c60ec869946da442833aa5afa8", 640502},
+ {"resource.004", 0, "f0af87c60ec869946da442833aa5afa8", 644575},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+
+ // Quest for Glory 1 / Hero's Quest - English DOS 5.25" Floppy v1.102 Int#0.000.629 (suppled by digitoxin1 in bug report #3554611)
+ {"qfg1", "", {
+ {"resource.map", 0, "5772a2c1bfae46f26582582c9901121e", 6858},
+ {"resource.000", 0, "40332d3ebfc70a4b6a6a0443c2763287", 78800},
+ {"resource.001", 0, "a270012fa74445d74c044d1b65a9ff8c", 75090},
+ {"resource.002", 0, "d22695c53835dfdece056d86f26c251e", 271354},
+ {"resource.003", 0, "3cd085e27078f269b3ece5838812ff41", 258084},
+ {"resource.004", 0, "8927c7a04a78f1e76f342db3ccc9d879", 267835},
+ {"resource.005", 0, "13d16cc9b90b51e2c8643cdf52a62957", 268807},
+ {"resource.006", 0, "48b2b3c964dcbeccb68e984e6d4e97db", 278473},
+ {"resource.007", 0, "f0af87c60ec869946da442833aa5afa8", 269237},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Quest for Glory 1 / Hero's Quest - English DOS 5.25" Floppy (supplied by markcoolio in bug report #2723843)
// Executable scanning reports "0.000.566"
@@ -2773,7 +2858,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "69366c2a2f99917199fe1b60a4fee19d", 267852},
{"resource.007", 0, "7ab2bf8e224b57f75e0cd6e4ba790761", 272747},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Quest for Glory 1 / Hero's Quest - English DOS 5.25" Floppy (supplied by ssburnout in bug report #3049193)
// 1.001 10x5.25" (label: INT.#0.000.566)
@@ -2788,7 +2873,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "69366c2a2f99917199fe1b60a4fee19d", 267852},
{"resource.007", 0, "7ab2bf8e224b57f75e0cd6e4ba790761", 272747},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Quest for Glory 1 / Hero's Quest - English DOS 5.25" Floppy (supplied by ssburnout in bug report #3049193)
// 1.200 10x5.25" (label: INT#9.10.90)
@@ -2803,7 +2888,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "f46690dca714abc8c89357d30e363dd3", 278387},
{"resource.007", 0, "951299a82a8134ed12c5c18118d45c2f", 269173},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Quest for Glory 1 / Hero's Quest - English DOS Demo
// Executable scanning reports "0.000.685"
@@ -2811,7 +2896,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "df34c758cbb9026da175793ff686b0e6", 882},
{"resource.001", 0, "73fbaafdd313b39aeedb80fbf85ecef1", 389884},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Quest for Glory 1 - Japanese PC-98 5.25" Floppy (also includes English language)
// Executable scanning reports "S.old.201"
@@ -2821,7 +2906,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "a21451ef6fa8179bd4b22c4950004c44", 1136968},
{"resource.003", 0, "a21451ef6fa8179bd4b22c4950004c44", 769897},
AD_LISTEND},
- Common::JA_JPN, Common::kPlatformPC98, ADGF_ADDENGLISH, GUIO3(GUIO_NOSPEECH, GUIO_NOASPECT, GUIO_EGAUNDITHER) },
+ Common::JA_JPN, Common::kPlatformPC98, ADGF_ADDENGLISH, GUIO3(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_EGA_UNDITHER) },
// Quest for Glory 1 - Japanese PC-98 5.25" Floppy (also includes English language)
// Executable scanning reports "S.old.201"
@@ -2831,7 +2916,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "a21451ef6fa8179bd4b22c4950004c44", 1147121},
{"resource.003", 0, "a21451ef6fa8179bd4b22c4950004c44", 777575},
AD_LISTEND},
- Common::JA_JPN, Common::kPlatformPC98, ADGF_ADDENGLISH, GUIO3(GUIO_NOSPEECH, GUIO_NOASPECT, GUIO_EGAUNDITHER) },
+ Common::JA_JPN, Common::kPlatformPC98, ADGF_ADDENGLISH, GUIO3(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_EGA_UNDITHER) },
// Quest for Glory 1 - English Amiga
// Executable scanning reports "1.002.020"
@@ -2845,18 +2930,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "16cd4414c37ae3bb6d6da33dce8e25e8", 689124},
{"resource.005", 0, "5f3386ef2f2b1254e4a066f5d9027324", 609529},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
-
- // Quest for Glory 1 (from abevi, bug report #2612718)
- {"qfg1", "", {
- {"resource.map", 0, "b162dbd4632250d4d83bed46d0783c10", 6396},
- {"resource.000", 0, "40332d3ebfc70a4b6a6a0443c2763287", 78800},
- {"resource.001", 0, "a270012fa74445d74c044d1b65a9ff8c", 459835},
- {"resource.002", 0, "e64004e020fdf1813be52b639b08be89", 635561},
- {"resource.003", 0, "f0af87c60ec869946da442833aa5afa8", 640502},
- {"resource.004", 0, "f0af87c60ec869946da442833aa5afa8", 644575},
- AD_LISTEND},
- Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Quest for Glory 1 - English DOS
// SCI interpreter version 0.000.629
@@ -2868,7 +2942,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "951299a82a8134ed12c5c18118d45c2f", 640483},
{"resource.004", 0, "951299a82a8134ed12c5c18118d45c2f", 644443},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Quest for Glory 1 VGA Remake - English DOS
// Executable scanning reports "2.000.411"
@@ -2876,7 +2950,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "a731fb6c9c0b282443f7027bc8694d4c", 8469},
{"resource.000", 0, "ecace1a2771846b1a8aa1afdd44111a0", 6570147},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Quest for Glory 1 VGA Remake - English DOS Non-Interactive Demo (from FRG)
// SCI interpreter version 1.001.029
@@ -2884,7 +2958,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "ac0257051c95a59c0cdc0be24d9b11fa", 729},
{"resource.000", 0, "ec6f5cf369054dd3e5392995e9975b9e", 768218},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Quest for Glory 1 VGA Remake - English Macintosh Floppy
// VERSION file reports "2.0"
@@ -2892,7 +2966,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"Data1", 0, "106527ff8756e4e1a795d63d23e8b833", 1752358},
{"Data2", 0, "5cdd92033231159c6e9c71d43e9f194d", 6574746},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Quest for Glory 2 - English Amiga
// Executable scanning reports "1.003.004"
@@ -2908,7 +2982,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "ccf5dba33e5cab6d5872838c0f8db44c", 500039},
{"resource.007", 0, "4c9fc1587545879295cb9627f56a2cb8", 575056},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Quest for Glory 2 - English (supplied by ssburnout in bug report #3049193)
// 1.000 5x5.25" (label: INT#10.31.90)
@@ -2920,7 +2994,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "0790f67d87642132be515cab05026baa", 972144},
{"resource.004", 0, "2ac1e6fea9aa1f5b91a06693a67b9766", 982830},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Quest for Glory 2 - English (supplied by ssburnout in bug report #3049193)
// 1.000 9x3.5" (label: INT#10.31.90)
@@ -2935,7 +3009,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "5e9deacbdb17198ad844988e04833520", 498593},
{"resource.007", 0, "2ac1e6fea9aa1f5b91a06693a67b9766", 490151},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Quest for Glory 2 - English (from FRG)
// Executable scanning reports "1.000.072"
@@ -2947,7 +3021,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "b192607c42f6960ecdf2ad2e4f90e9bc", 972804},
{"resource.004", 0, "cd2de58e27665d5853530de93fae7cd6", 983617},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Quest for Glory 2 - English DOS
// Executable scanning reports "1.000.072"
@@ -2962,7 +3036,22 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "b1944bd664ddbd2859cdaa0c4a0d6281", 507489},
{"resource.007", 0, "cd2de58e27665d5853530de93fae7cd6", 490794},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+
+ // Quest for Glory 2 - English DOS (supplied by digitoxin1 in bug report #3554614)
+ // v1.102 9x3.5" (label: Int#11.20.90)
+ {"qfg2", "", {
+ {"resource.map", 0, "367023314ea33e3156297402f6c1da49", 8166},
+ {"resource.000", 0, "a17e374c4d33b81208c862bc0ffc1a38", 212119},
+ {"resource.001", 0, "e08d7887e30b12008c40f9570447711a", 331995},
+ {"resource.002", 0, "df137dc7869cab07e1149ba2333c815c", 467461},
+ {"resource.003", 0, "df137dc7869cab07e1149ba2333c815c", 502560},
+ {"resource.004", 0, "df137dc7869cab07e1149ba2333c815c", 488532},
+ {"resource.005", 0, "df137dc7869cab07e1149ba2333c815c", 478574},
+ {"resource.006", 0, "b1944bd664ddbd2859cdaa0c4a0d6281", 507489},
+ {"resource.007", 0, "cd2de58e27665d5853530de93fae7cd6", 490794},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Quest for Glory 2 - English DOS Non-Interactive Demo
// Executable scanning reports "1.000.046"
@@ -2970,7 +3059,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "e75eb86bdd517b3ef709058249986a87", 906},
{"resource.001", 0, "9b098f9e1008abe30e56c93b896494e6", 362123},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Quest for Glory 3 - English DOS Non-Interactive Demo (from FRG)
// Executable scanning reports "1.001.021", VERSION file reports "1.000, 0.001.059, 6.12.92"
@@ -2978,7 +3067,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "fd71de9b588a45f085317caacf050e91", 687},
{"resource.000", 0, "b6c69bf6c18bf177492249fe81fc6a6d", 648702},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Quest for Glory 3 - English DOS
// SCI interpreter version 1.001.050
@@ -2986,7 +3075,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "19e2bf9b693932b5e2bb59b9f9ab86c9", 5958},
{"resource.000", 0, "6178ad2e83e58e4671ca03315f7a6498", 5868000},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Quest for Glory 3 - English DOS (supplied by abevi in bug report #2612718)
// SCI interpreter version 1.001.050
@@ -2994,7 +3083,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "62c185d190363d7df06330fa0cc45b36", 5958},
{"resource.000", 0, "6178ad2e83e58e4671ca03315f7a6498", 5867442},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Quest for Glory 3 - English DOS (supplied by dknute in bug report #3125559)
{"qfg3", "", {
@@ -3002,7 +3091,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "6178ad2e83e58e4671ca03315f7a6498", 5868042},
{"resource.msg", 0, "27e5419c98ce444253f88c95dced14a9", 246888},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Quest for Glory 3 - German DOS (supplied by markcoolio in bug report #2723846)
// Executable scanning reports "L.rry.083"
@@ -3010,7 +3099,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "19e2bf9b693932b5e2bb59b9f9ab86c9", 5958},
{"resource.000", 0, "6178ad2e83e58e4671ca03315f7a6498", 5868042},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::DE_DEU, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Quest for Glory 3 - Spanish DOS CD (from jvprat)
// Executable scanning reports "L.rry.083", VERSION file reports "1.000.000, June 30, 1994"
@@ -3019,7 +3108,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "ba7ac86155e4c531e46cd73c86daa80a", 5884098},
{"resource.msg", 0, "a63974730d294dec0bea10057c36e506", 256014},
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformPC, 0, GUIO0() },
+ Common::ES_ESP, Common::kPlatformPC, 0, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Quest for Glory 3 - Italian DOS
// Supplied by ghoost in bug report #3053457
@@ -3028,7 +3117,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "6178ad2e83e58e4671ca03315f7a6498", 5868000},
{"resource.msg", 0, "5a0a896ff3e4a628db38a75eb6c84114", 259018},
AD_LISTEND},
- Common::IT_ITA, Common::kPlatformPC, 0, GUIO0() },
+ Common::IT_ITA, Common::kPlatformPC, 0, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Quest for Glory 4 - English DOS Non-Interactive Demo (from FRG)
// SCI interpreter version 1.001.069 (just a guess)
@@ -3036,7 +3125,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "1ba7c7ae1efb315326d45cb931569b1b", 922},
{"resource.000", 0, "41ba03f0b188b029132daa3ece0d3e14", 623154},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#ifdef ENABLE_SCI32
// Quest for Glory 4 1.1 Floppy - English DOS (supplied by markcool in bug report #2723852)
@@ -3045,7 +3134,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "685bdb1ed47bbbb0e5e25db392da83ce", 9301},
{"resource.000", 0, "f64fd6aa3977939a86ff30783dd677e1", 11004993},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Quest for Glory 4 1.1 Floppy - English DOS (supplied by abevi in bug report #2612718)
// SCI interpreter version 2.000.000
@@ -3053,7 +3142,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "d10a4cc177d2091d744e2ad8c049b0ae", 9295},
{"resource.000", 0, "f64fd6aa3977939a86ff30783dd677e1", 11003589},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_UNSTABLE, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Quest for Glory 4 1.1 Floppy - German DOS (supplied by markcool in bug report #2723850)
// Executable scanning reports "2.000.000", VERSION file reports "1.1"
@@ -3061,7 +3150,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "9e0abba8746f40565bc7eb5720522ecd", 9301},
{"resource.000", 0, "57f22cdc54eeb35fce1f26b31b5c3ee1", 11076197},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformPC, ADGF_UNSTABLE, GUIO1(GUIO_NOSPEECH) },
+ Common::DE_DEU, Common::kPlatformPC, ADGF_UNSTABLE, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Quest for Glory 4 CD - English DOS/Windows (from jvprat)
// Executable scanning reports "2.100.002", VERSION file reports "1.0"
@@ -3069,7 +3158,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "aba367f2102e81782d961b14fbe3d630", 10246},
{"resource.000", 0, "263dce4aa34c49d3ad29bec889007b1c", 11571394},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_CD | ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_CD | ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// RAMA - English DOS/Windows Demo
// Executable scanning reports "2.100.002", VERSION file reports "000.000.008"
@@ -3077,7 +3166,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.001", 0, "775304e9b2a545156be4d94209550094", 1393},
{"ressci.001", 0, "259437fd75fdf51e8207fda8c01fa4fd", 2334384},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO | ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO | ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#ifdef ENABLE_SCI3_GAMES
// RAMA - English Windows (from jvprat)
@@ -3090,7 +3179,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.003", 0, "31ef4c0621711585d031f0ae81707251", 1636},
{"ressci.003", 0, "2a68edd064e5e4937b5e9c74b38f2082", 6860492},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// RAMA - English Windows (from Quietust, in bug report #2850645)
{"rama", "", {
@@ -3101,7 +3190,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.003", 0, "48841e4b84ef1b98b48d43566fda9e13", 1636},
{"ressci.003", 0, "2a68edd064e5e4937b5e9c74b38f2082", 6870356},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// RAMA - Italian Windows CD (from glorifindel)
// SCI interpreter version 3.000.000 (a guess?)
@@ -3109,7 +3198,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"ressci.001", 0, "2a68edd064e5e4937b5e9c74b38f2082", 70611091},
{"resmap.001", 0, "70ba2ff04a2b7fb2c52420ba7fbd47c2", 8338},
AD_LISTEND},
- Common::IT_ITA, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::IT_ITA, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#endif // ENABLE_SCI3_GAMES
// Shivers - English Windows (from jvprat)
@@ -3118,14 +3207,14 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.000", 0, "f2ead37749ed8f6535a2445a7d05a0cc", 46525},
{"ressci.000", 0, "4294c6d7510935f2e0a52e302073c951", 262654836},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Shivers - German Windows (from Tobis87)
{"shivers", "", {
{"resmap.000", 0, "f483d0a1f78334c18052e92785c3086e", 46537},
{"ressci.000", 0, "6751b144671e2deed919eb9d284b07eb", 262390692},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::DE_DEU, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Shivers - English Windows Demo
// Executable scanning reports "2.100.002"
@@ -3133,7 +3222,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.000", 0, "d9e0bc5eddefcbe47f528760085d8927", 1186},
{"ressci.000", 0, "3a93c6340b54e07e65d0e5583354d186", 10505469},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO | ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO | ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Shivers 2 doesn't contain SCI scripts. The whole game logic has
// been reimplemented from SCI in native code placed in DLL files.
@@ -3151,7 +3240,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resmap.000", 0, "d8659188b84beaef076bd869837cd530", 634},
{"ressci.000", 0, "7fbac0807a044c9543e8ac376d200e59", 4925003},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO | ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO | ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Shivers 2 - English Windows (from abevi)
// VERSION.TXT Version 1.0 (3/25/97)
@@ -3159,7 +3248,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"ressci.001", 0, "a79d03d6eb75be0a79324f14e3d2ace4", 95346793},
{"resmap.001", 0, "a4804d436d90c4ec2e46b537f5e954db", 6268},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#endif
@@ -3172,7 +3261,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.msg", 0, "1aeafe2b495de288d002109650b66614", 1364},
{"resource.000", 0, "8e10d4f05c1fd9f883384fa38a898489", 377394},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Slater & Charlie Go Camping - English DOS/Windows
{"slater", "", {
@@ -3180,7 +3269,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "21f85414124dc23e54544a5536dc35cd", 4044},
{"resource.msg", 0, "c44f51fb955eae266fecf360ebcd5ad2", 1132},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Slater & Charlie Go Camping - English DOS/Windows (Sierra Originals)
@@ -3189,7 +3278,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "21f85414124dc23e54544a5536dc35cd", 4044},
{"resource.msg", 0, "c44f51fb955eae266fecf360ebcd5ad2", 1132},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Slater & Charlie Go Camping - English Macintosh
{"slater", "", {
@@ -3209,7 +3298,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "b25a1539c71701f7715f738c5037e9a6", 775515},
{"resource.005", 0, "640ffe1a9acde392cc33cc1b1a528328", 806324},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 1 VGA Remake - English DOS (from the Space Quest Collection)
// Executable scanning reports "T.A00.081", VERSION file reports "2.000"
@@ -3222,7 +3311,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "c47600e50c6fc591957ae0c5020ee7b8", 1213262},
{"resource.004", 0, "e19ea4ad131472f9238590f2e1d40289", 1203051},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 1 VGA Remake - English Mac (from Fingolfin)
{"sq1sci", "SCI", {
@@ -3233,7 +3322,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "ae46e195e66df5a131917f0aa80b5669", 1242794},
{"resource.004", 0, "91d58a9eb2187c38424990afe4c12bc6", 1250949},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformMacintosh, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformMacintosh, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 1 VGA Remake - English Non-Interactive Demo (from FRG)
// SCI interpreter version 1.000.181
@@ -3241,7 +3330,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "5af709ac5e0e923e0b8174f49978c30e", 636},
{"resource.001", 0, "fd99ea43f57576ded7c86036996346cf", 507642},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 1 VGA Remake - Spanish DOS Floppy (from jvprat)
// Executable scanning reports "T.A00.081", VERSION file reports "2.000"
@@ -3255,7 +3344,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "9b78228ad4f9f335fedf74f1812dcfca", 513325},
{"resource.005", 0, "7d4ebcb745c0bf8fc42e4013f52ecd49", 1101812},
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::ES_ESP, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest I 2.0 EGA DOS (6 x 3.5" disks)
// Provided by ssburnout in bug report #3046805
@@ -3268,7 +3357,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "975c6e81194ae6b65e960a248129ecaa", 684119},
{"resource.005", 0, "13d96f7905637552c0647175ff816145", 695589},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 3 - English Amiga (from www.back2roots.org)
// SCI interpreter version 0.000.453 (just a guess)
@@ -3279,7 +3368,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.003", 0, "ceeda7202b96e5c85ecaa88a40a540fc", 746496},
{"resource.004", 0, "ceeda7202b96e5c85ecaa88a40a540fc", 761984},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 3 - German Amiga (also includes english language)
// Executable scanning reports "1.004.006"
@@ -3292,7 +3381,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "6d8f34090503ce937e7dbef6cb6cdb6a", 545053},
{"resource.005", 0, "6d8f34090503ce937e7dbef6cb6cdb6a", 687507},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformAmiga, ADGF_ADDENGLISH, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::DE_DEU, Common::kPlatformAmiga, ADGF_ADDENGLISH, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 3 - English DOS Non-Interactive Demo
// SCI interpreter version 0.000.453
@@ -3300,7 +3389,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "ec66ac2b1ce58b2575ba00b65058de1a", 612},
{"resource.001", 0, "ceeda7202b96e5c85ecaa88a40a540fc", 180245},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 3 - English DOS (provided by richiefs in bug report #2670691)
// SCI interpreter version 0.000.453
@@ -3310,7 +3399,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "ceeda7202b96e5c85ecaa88a40a540fc", 720244},
{"resource.003", 0, "ceeda7202b96e5c85ecaa88a40a540fc", 688367},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 3 - English DOS (from the Space Quest Collection)
// Executable scanning reports "0.000.685", VERSION file reports "1.018"
@@ -3320,7 +3409,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "8b55c4875298f45ea5696a5ee8f6a7fe", 715777},
{"resource.003", 0, "8b55c4875298f45ea5696a5ee8f6a7fe", 703370},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 3 - English DOS (from abevi, bug report #2612718)
{"sq3", "", {
@@ -3332,7 +3421,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "ceeda7202b96e5c85ecaa88a40a540fc", 328278},
{"resource.006", 0, "ceeda7202b96e5c85ecaa88a40a540fc", 356702},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 3 - English Mac (from Fingolfin)
{"sq3", "", {
@@ -3341,7 +3430,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "0d8dfe42683b46f3131823233a91ce6a", 794072},
{"resource.003", 0, "0d8dfe42683b46f3131823233a91ce6a", 776536},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformMacintosh, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformMacintosh, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 3 - German DOS (from Tobis87, also includes english language)
// SCI interpreter version 0.000.453 (?)
@@ -3355,7 +3444,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.006", 0, "9107c2aa5398e28b5c5406df13491f85", 320643},
{"resource.007", 0, "9107c2aa5398e28b5c5406df13491f85", 344287},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 3 v1.052 - German DOS (supplied by markcoolio in bug report #2723860, also includes english language)
// Executable scanning reports "S.old.114"
@@ -3365,7 +3454,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "9107c2aa5398e28b5c5406df13491f85", 596768},
{"resource.003", 0, "9107c2aa5398e28b5c5406df13491f85", 693573},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO1(GUIO_NOSPEECH) },
+ Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 4 - English Amiga
// Executable scanning reports "1.004.024"
@@ -3380,7 +3469,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "3540d1cc84d674cf4b2c898b88a3b563", 790296},
{"resource.006", 0, "ade814bc4d56244c156d9e9bcfebbc11", 664085},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 4 - German Amiga (from www.back2roots.org, also includes english language)
// SCI interpreter version 1.000.200 (just a guess)
@@ -3394,7 +3483,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "10ee1709e6559c724676d058199b75b5", 818745},
{"resource.006", 0, "67fb188b191d88efe8414af6ea297b93", 672675},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformAmiga, ADGF_ADDENGLISH, GUIO1(GUIO_NOSPEECH) },
+ Common::DE_DEU, Common::kPlatformAmiga, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 4 - English DOS - THIS VERSION IS PIRATED/CRACKED AND REPACKAGED =DO NOT RE-ADD=
// Executable scanning reports "1.000.753"
@@ -3403,7 +3492,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "a18088c8aceb06025dbc945f29e02935", 5124},
{"resource.000", 0, "e1f46832cd2458796028e054a0466031", 5502009},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_PIRATED, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_PIRATED, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 4 - English DOS
// Executable scanning reports "1.000.753"
@@ -3412,7 +3501,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "71ccf4f82ac4efb588731acfb7bf2603", 5646},
{"resource.000", 0, "e1f46832cd2458796028e054a0466031", 933928},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 4 1.052 - English DOS Floppy (supplied by markcoolio in bug report #2723865)
// Executable scanning reports "1.000.753"
@@ -3426,7 +3515,20 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "ff9c87da3bc53473fdee8b9d3edbc93c", 1200631},
{"resource.005", 0, "e33019ac19f755ae33fbf49b4fc9066c", 1053294},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+
+ // Space Quest 4 1.000 - French DOS Floppy (supplied by misterhands in bug report #3515247)
+ {"sq4", "", {
+
+ {"resource.map", 0, "1fd6f356f6a59ad2057686ce6573caeb", 6159},
+ {"resource.000", 0, "8000a55aebc50a68b7cce07a8c33758c", 205287},
+ {"resource.001", 0, "99a6df6d366b3f061271ff3450ac0d32", 1269850},
+ {"resource.002", 0, "a6a8d7a24dbb7a266a26b084e7275e89", 1242668},
+ {"resource.003", 0, "482a99c8103b4bcb5706e5969d1c1193", 1323083},
+ {"resource.004", 0, "b2cca3afcf2e013b8ce86b64155af766", 1254353},
+ {"resource.005", 0, "9e520577e035547c4b5149a6d12ef85b", 1098814},
+ AD_LISTEND},
+ Common::FR_FRA, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 4 1.000 - English DOS Floppy (from abevi, bug report #2612718)
{"sq4", "", {
@@ -3438,7 +3540,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "c06350184a490c10eb4585fff0aa3192", 1254368},
{"resource.005", 0, "b8d6efbd3235329bfe844c794097b2c9", 1098717},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest IV DOS 1.060 EGA (6 x 3.5" disks)
// Supplied by ssburnout in bug report #3046781
@@ -3451,7 +3553,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "9a673e33c3f6dd560b993ffed77eeb49", 534994},
{"resource.005", 0, "3c4841d0a3ebba4404af588c93620c22", 595465},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO2(GUIO_NOSPEECH, GUIO_EGAUNDITHER) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 4 - German DOS (from Tobis87, also includes english language)
// SCI interpreter version 1.000.200 (just a guess)
@@ -3465,7 +3567,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "47ee647b5b12232d27e63cc627c25899", 1156765},
{"resource.006", 0, "dfb023e4e2a1e7a00fa18f9ede72a91b", 924059},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO1(GUIO_NOSPEECH) },
+ Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 4 - Italian DOS Floppy (from glorifindel, also includes english language)
// SCI interpreter version 1.000.200 (just a guess)
@@ -3478,7 +3580,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "4277c61bed40a50dadc4b5a344520af2", 1251000},
{"resource.005", 0, "5f885abd335978e2fd4e5f886d7676c8", 1102880},
AD_LISTEND},
- Common::IT_ITA, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO1(GUIO_NOSPEECH) },
+ Common::IT_ITA, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 4 - Japanese PC-98 5.25" Floppy (also includes english language)
// SCI interpreter version 1.000.1068
@@ -3488,7 +3590,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "454684e3a7a68cbca073945e50778447", 1187088},
{"resource.002", 0, "6dc668326cc22cb9e8bd8ca9e68d2a66", 1181249},
AD_LISTEND},
- Common::JA_JPN, Common::kPlatformPC98, ADGF_ADDENGLISH, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::JA_JPN, Common::kPlatformPC98, ADGF_ADDENGLISH, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 4 - Japanese PC-98 5.25" Floppy (also includes english language)
// SCI interpreter version 1.000.1068
@@ -3498,7 +3600,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.001", 0, "454684e3a7a68cbca073945e50778447", 1187088},
{"resource.002", 0, "6dc668326cc22cb9e8bd8ca9e68d2a66", 1181249},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC98, ADGF_ADDENGLISH, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformPC98, ADGF_ADDENGLISH, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 4 - English DOS CD (from the Space Quest Collection)
// Executable scanning reports "1.001.064", VERSION file reports "1.0"
@@ -3506,7 +3608,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "ed90a8e3ccc53af6633ff6ab58392bae", 7054},
{"resource.000", 0, "63247e3901ab8963d4eece73747832e0", 5157378},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO0() },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_CD, GUIO5(GUIO_MIDIGM, GAMEOPTION_SQ4_SILVER_CURSORS, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 4 - English Windows CD (from the Space Quest Collection)
// Executable scanning reports "1.001.064", VERSION file reports "1.0"
@@ -3516,7 +3618,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "ed90a8e3ccc53af6633ff6ab58392bae", 7054},
{"resource.000", 0, "63247e3901ab8963d4eece73747832e0", 5157378},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_CD, GUIO1(GUIO_MIDIGM) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_CD, GUIO5(GUIO_MIDIGM, GAMEOPTION_SQ4_SILVER_CURSORS, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 4 - Spanish DOS CD (from jvprat, is still text only, not talkie, also includes english language)
// Executable scanning reports "1.SQ4.057", VERSION file reports "1.000"
@@ -3530,7 +3632,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "776fba81c110d1908776232cbe190e20", 1253752},
{"resource.005", 0, "55fae26c2a92f16ef72c1e216e827c0f", 1098328},
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO0() },
+ Common::ES_ESP, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GAMEOPTION_SQ4_SILVER_CURSORS, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 4 - Spanish DOS Floppy (from jvprat, also includes english language)
// Executable scanning reports "1.SQ4.056", VERSION file reports "1.000"
@@ -3542,7 +3644,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.002", 0, "74c62fa2146ff3b3b2ea2b3fb95b9af9", 1140801},
{"resource.003", 0, "42a307941edeb1a3be31daeb2e4be90b", 1088408},
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO1(GUIO_NOSPEECH) },
+ Common::ES_ESP, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 4 1.000 - German DOS Floppy (supplied by markcoolio in bug report #2723862, also includes english language)
// Executable scanning reports "1.SQ4.030"
@@ -3556,7 +3658,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "c06350184a490c10eb4585fff0aa3192", 1254368},
{"resource.005", 0, "b8d6efbd3235329bfe844c794097b2c9", 1098717},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO1(GUIO_NOSPEECH) },
+ Common::DE_DEU, Common::kPlatformPC, ADGF_ADDENGLISH, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 4 - English Macintosh
// Executable scanning reports "x.yyy.zzz"
@@ -3571,7 +3673,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.005", 0, "869d16cab6641c80b06f4dcee18f86bc", 1426228},
{"resource.006", 0, "91d23407bc0447a3722fbeb952d7edee", 1402451},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformMacintosh, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformMacintosh, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 4 - Russian DOS
// Executable scanning reports "1.000.753", VERSION file reports "1.994"
@@ -3584,7 +3686,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.004", 0, "2763fe4f0cb74df716ec8b0c464b0988", 1217428},
{"resource.005", 0, "d608713197c5ba1cd8c6ed46299c3069", 1057924},
AD_LISTEND},
- Common::RU_RUS, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::RU_RUS, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 5 - English DOS (from the Space Quest Collection)
// Executable scanning reports "1.001.068", VERSION file reports "1.04"
@@ -3593,7 +3695,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "4147edc5045e6d62998018b5614c58ec", 5496486},
{"resource.msg", 0, "bb8ad78793c26bdb3f77498b1d6515a9", 125988},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 5 - English DOS - THIS IS THE UNOFFICIAL BETA VERSION, WHICH IS OBVIOUSLY PIRATED AND CONTAINS MANY BUGS
// ffs. http://www.akril15.com/sr/sq5alt/sq5alt.html =DO NOT RE-ADD=
@@ -3602,7 +3704,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "8bde0a9adb9a3e9aaa861826874c9834", 6473},
{"resource.000", 0, "f4a48705764544d7cc64a7bb22a610df", 6025184},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_PIRATED, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_PIRATED, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 5 v1.04 - German DOS (from Tobis87, updated information by markcool from bug reports #2723935 and #2724762)
// SCI interpreter version 1.001.068
@@ -3611,7 +3713,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "4147edc5045e6d62998018b5614c58ec", 5496486},
{"resource.msg", 0, "7c71cfc36153cfe07b450423a51f7e68", 146282},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::DE_DEU, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 5 v1.04 - French DOS (from Hkz, Included in Space Quest Collector's Edition, with chapters I-V)
{"sq5", "", {
@@ -3619,7 +3721,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "4147edc5045e6d62998018b5614c58ec", 5496486},
{"resource.msg", 0, "877c42380320eb1db7dad83ccd261214", 140374},
AD_LISTEND},
- Common::FR_FRA, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::FR_FRA, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 5 - Italian DOS Floppy (from glorifindel)
// SCI interpreter version 1.001.068 (just a guess)
@@ -3627,7 +3729,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "5040026519f37199f3616fb1d4704dff", 6047170},
{"resource.map", 0, "5b09168baa2f6e2e22787429b2d72f54", 6492},
AD_LISTEND},
- Common::IT_ITA, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::IT_ITA, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 5 - Spanish DOS Floppy (from mirir, bug report #3090664)
{"sq5", "", {
@@ -3635,7 +3737,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "5714a899033bdebf2d61ad333c8c6637", 6492},
{"resource.msg", 0, "46deca7ef9cf057f7d442df98c1a2ae2", 134612},
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::ES_ESP, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 5 - Russian DOS
// Executable scanning reports "1.001.068", VERSION file reports "1.994"
@@ -3644,7 +3746,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.000", 0, "6f9ed21e1001526b4137f6703ed476af", 6103778},
{"resource.msg", 0, "0a8931990cd2eac1691602391c68ab85", 147580},
AD_LISTEND},
- Common::RU_RUS, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::RU_RUS, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#ifdef ENABLE_SCI32
// Space Quest 6 - English DOS/Win3.11 CD (from the Space Quest Collection)
@@ -3653,7 +3755,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "6dddfa3a8f3a3a513ec9dfdfae955005", 10528},
{"resource.000", 0, "c4259ab7355aead07773397b1052827d", 41150806},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_CD | ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_CD | ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 6 - English DOS/Win3.11 CD ver 1.11 (from FRG)
// SCI interpreter version 2.100.002 (just a guess)
@@ -3661,7 +3763,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "e0615d6e4e10e37ae42e6a2a95aaf145", 10528},
{"resource.000", 0, "c4259ab7355aead07773397b1052827d", 41150806},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_CD | ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_CD | ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 6 - French DOS/Win3.11 CD (from French magazine Joystick - September 1997)
// Executable scanning reports "2.100.002", VERSION file reports "1.0"
@@ -3669,7 +3771,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "3c831625931d5079b73ae8c275f52c95", 10534},
{"resource.000", 0, "4195ca940f759424f62b90e262cc1737", 40932397},
AD_LISTEND},
- Common::FR_FRA, Common::kPlatformPC, ADGF_CD | ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::FR_FRA, Common::kPlatformPC, ADGF_CD | ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 6 - German DOS (from Tobis87, updated info from markcoolio in bug report #2723884)
// SCI interpreter version 2.100.002 (just a guess)
@@ -3677,7 +3779,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "664d797415484f85c90b1b45aedc7686", 10534},
{"resource.000", 0, "ba87ba91e5bdabb4169dd0df75777722", 40933685},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformPC, ADGF_CD | ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::DE_DEU, Common::kPlatformPC, ADGF_CD | ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// Space Quest 6 - English DOS/Win3.11 Interactive Demo (from FRG)
// SCI interpreter version 2.100.002 (just a guess)
@@ -3685,7 +3787,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "368f07b07433db3f819fa3fa0e5efee5", 2572},
{"resource.000", 0, "ab12724e078dea34b624e0d2a38dcd7c", 2272050},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO | ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO | ADGF_UNSTABLE, GUIO5(GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#endif // ENABLE_SCI32
// The Island of Dr. Brain - English DOS CD (from jvprat)
@@ -3694,7 +3796,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "2388efef8430b041b0f3b00b9050e4a2", 3281},
{"resource.000", 0, "b3acd9b9dd7fe53c4ee133ac9a1acfab", 2103560},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO0() },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// The Island of Dr. Brain - English DOS (from Quietust)
// Executable scanning reports "1.001.053", VERSION file reports "1.1 2.3.93"
@@ -3702,7 +3804,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "3c07da06bdd1689f9d07af78fb94d0ec", 3101},
{"resource.000", 0, "ecc686e0034fb4d41de077ac7167b3cf", 1947866},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, 0, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
// The Island of Dr. Brain - English DOS Non-Interactive Demo
// SCI interpreter version 1.001.053 (just a guess)
@@ -3710,57 +3812,65 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "a8e5ca8ed1996974afa59f4c45e06195", 986},
{"resource.000", 0, "b3acd9b9dd7fe53c4ee133ac9a1acfab", 586560},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO1(GUIO_NOSPEECH) },
+ Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#ifdef ENABLE_SCI32
// Torin's Passage - English Windows Interactive Demo
- // SCI interpreter version 2.100.002 (just a guess)
+ // SCI interpreter version 2.100.002
{"torin", "Demo", {
{"resmap.000", 0, "9a3e172cde9963d0a969f26469318cec", 3403},
{"ressci.000", 0, "db3e290481c35c3224e9602e71e4a1f1", 5073868},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO | ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO | ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
- // Torin's Passage - English Windows
- // SCI interpreter version 2.100.002 (just a guess)
+ // Torin's Passage (Multilingual) - English Windows CD
+ // SCI interpreter version 2.100.002
{"torin", "", {
{"resmap.000", 0, "bb3b0b22ff08df54fbe2d06263409be6", 9799},
{"ressci.000", 0, "693a259d346c9360f4a0c11fdaae430a", 55973887},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
- // Torin's Passage - Spanish Windows (from jvprat)
+ // Torin's Passage (Multilingual) - Spanish Windows CD (from jvprat)
// Executable scanning reports "2.100.002", VERSION file reports "1.0"
{"torin", "", {
{"resmap.000", 0, "bb3b0b22ff08df54fbe2d06263409be6", 9799},
{"ressci.000", 0, "693a259d346c9360f4a0c11fdaae430a", 55973887},
// TODO: depend on one of the patches?
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::ES_ESP, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
- // Torin's Passage - French Windows
- // SCI interpreter version 2.100.002 (just a guess)
+ // Torin's Passage (Multilingual) - French Windows CD
+ // SCI interpreter version 2.100.002
{"torin", "", {
{"resmap.000", 0, "bb3b0b22ff08df54fbe2d06263409be6", 9799},
{"ressci.000", 0, "693a259d346c9360f4a0c11fdaae430a", 55973887},
AD_LISTEND},
- Common::FR_FRA, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::FR_FRA, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
- // Torin's Passage - German Windows
- // SCI interpreter version 2.100.002 (just a guess)
+ // Torin's Passage (Multilingual) - German Windows CD
+ // SCI interpreter version 2.100.002
{"torin", "", {
{"resmap.000", 0, "bb3b0b22ff08df54fbe2d06263409be6", 9799},
{"ressci.000", 0, "693a259d346c9360f4a0c11fdaae430a", 55973887},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO2(GUIO_NOSPEECH, GUIO_NOASPECT) },
+ Common::DE_DEU, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
- // Torin's Passage - Italian Windows CD (from glorifindel)
- // SCI interpreter version 2.100.002 (just a guess)
+ // Torin's Passage (Multilingual) - Italian Windows CD (from glorifindel)
+ // SCI interpreter version 2.100.002
{"torin", "", {
{"resmap.000", 0, "bb3b0b22ff08df54fbe2d06263409be6", 9799},
{"ressci.000", 0, "693a259d346c9360f4a0c11fdaae430a", 55973887},
AD_LISTEND},
- Common::IT_ITA, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO1(GUIO_NOASPECT) },
+ Common::IT_ITA, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
+
+ // Torin's Passage - French Windows (from LePhilousophe)
+ // SCI interpreter version 2.100.002
+ {"torin", "", {
+ {"resmap.000", 0, "66ed46e3e56f487e688d52f05b33d0ba", 9787},
+ {"ressci.000", 0, "118f9bec04bfe17c4f87bbb5ddb43c18", 56126981},
+ AD_LISTEND},
+ Common::FR_FRA, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) },
#endif // ENABLE_SCI32
// SCI Fanmade Games
diff --git a/engines/sci/engine/features.cpp b/engines/sci/engine/features.cpp
index b3cfee873c..22c0a1479d 100644
--- a/engines/sci/engine/features.cpp
+++ b/engines/sci/engine/features.cpp
@@ -26,6 +26,7 @@
#include "sci/engine/selector.h"
#include "sci/engine/vm.h"
+#include "common/config-manager.h"
#include "common/file.h"
namespace Sci {
@@ -42,6 +43,9 @@ GameFeatures::GameFeatures(SegManager *segMan, Kernel *kernel) : _segMan(segMan)
_sci2StringFunctionType = kSci2StringFunctionUninitialized;
#endif
_usesCdTrack = Common::File::exists("cdaudio.map");
+ if (!ConfMan.getBool("use_cdaudio"))
+ _usesCdTrack = false;
+ _forceDOSTracks = false;
}
reg_t GameFeatures::getDetectionAddr(const Common::String &objName, Selector slc, int methodNum) {
@@ -70,11 +74,11 @@ bool GameFeatures::autoDetectSoundType() {
// Look up the script address
reg_t addr = getDetectionAddr("Sound", SELECTOR(play));
- if (!addr.segment)
+ if (!addr.getSegment())
return false;
- uint16 offset = addr.offset;
- Script *script = _segMan->getScript(addr.segment);
+ uint16 offset = addr.getOffset();
+ Script *script = _segMan->getScript(addr.getSegment());
uint16 intParam = 0xFFFF;
bool foundTarget = false;
@@ -217,11 +221,11 @@ bool GameFeatures::autoDetectLofsType(Common::String gameSuperClassName, int met
// Look up the script address
reg_t addr = getDetectionAddr(gameSuperClassName.c_str(), -1, methodNum);
- if (!addr.segment)
+ if (!addr.getSegment())
return false;
- uint16 offset = addr.offset;
- Script *script = _segMan->getScript(addr.segment);
+ uint16 offset = addr.getOffset();
+ Script *script = _segMan->getScript(addr.getSegment());
while (true) {
int16 opparams[4];
@@ -316,11 +320,11 @@ bool GameFeatures::autoDetectGfxFunctionsType(int methodNum) {
// Look up the script address
reg_t addr = getDetectionAddr("Rm", SELECTOR(overlay), methodNum);
- if (!addr.segment)
+ if (!addr.getSegment())
return false;
- uint16 offset = addr.offset;
- Script *script = _segMan->getScript(addr.segment);
+ uint16 offset = addr.getOffset();
+ Script *script = _segMan->getScript(addr.getSegment());
while (true) {
int16 opparams[4];
@@ -470,11 +474,11 @@ bool GameFeatures::autoDetectSci21KernelType() {
// Look up the script address
reg_t addr = getDetectionAddr("Sound", SELECTOR(play));
- if (!addr.segment)
+ if (!addr.getSegment())
return false;
- uint16 offset = addr.offset;
- Script *script = _segMan->getScript(addr.segment);
+ uint16 offset = addr.getOffset();
+ Script *script = _segMan->getScript(addr.getSegment());
while (true) {
int16 opparams[4];
@@ -546,11 +550,11 @@ bool GameFeatures::autoDetectSci21StringFunctionType() {
// Look up the script address
reg_t addr = getDetectionAddr("Str", SELECTOR(size));
- if (!addr.segment)
+ if (!addr.getSegment())
return false;
- uint16 offset = addr.offset;
- Script *script = _segMan->getScript(addr.segment);
+ uint16 offset = addr.getOffset();
+ Script *script = _segMan->getScript(addr.getSegment());
while (true) {
int16 opparams[4];
@@ -583,11 +587,11 @@ bool GameFeatures::autoDetectMoveCountType() {
// Look up the script address
reg_t addr = getDetectionAddr("Motion", SELECTOR(doit));
- if (!addr.segment)
+ if (!addr.getSegment())
return false;
- uint16 offset = addr.offset;
- Script *script = _segMan->getScript(addr.segment);
+ uint16 offset = addr.getOffset();
+ Script *script = _segMan->getScript(addr.getSegment());
bool foundTarget = false;
while (true) {
@@ -639,7 +643,7 @@ MoveCountType GameFeatures::detectMoveCountType() {
}
bool GameFeatures::useAltWinGMSound() {
- if (g_sci && g_sci->getPlatform() == Common::kPlatformWindows && g_sci->isCD()) {
+ if (g_sci && g_sci->getPlatform() == Common::kPlatformWindows && g_sci->isCD() && !_forceDOSTracks) {
SciGameId id = g_sci->getGameId();
return (id == GID_ECOQUEST ||
id == GID_JONES ||
diff --git a/engines/sci/engine/features.h b/engines/sci/engine/features.h
index 4592c5be9c..f6bb0b5759 100644
--- a/engines/sci/engine/features.h
+++ b/engines/sci/engine/features.h
@@ -117,6 +117,12 @@ public:
*/
bool useAltWinGMSound();
+ /**
+ * Forces DOS soundtracks in Windows CD versions when the user hasn't
+ * selected a MIDI output device
+ */
+ void forceDOSTracks() { _forceDOSTracks = true; }
+
private:
reg_t getDetectionAddr(const Common::String &objName, Selector slc, int methodNum = -1);
@@ -137,6 +143,7 @@ private:
MoveCountType _moveCountType;
bool _usesCdTrack;
+ bool _forceDOSTracks;
SegManager *_segMan;
Kernel *_kernel;
diff --git a/engines/sci/engine/file.cpp b/engines/sci/engine/file.cpp
new file mode 100644
index 0000000000..3dc042389e
--- /dev/null
+++ b/engines/sci/engine/file.cpp
@@ -0,0 +1,464 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/savefile.h"
+#include "common/stream.h"
+
+#include "sci/sci.h"
+#include "sci/engine/file.h"
+#include "sci/engine/kernel.h"
+#include "sci/engine/savegame.h"
+#include "sci/engine/selector.h"
+#include "sci/engine/state.h"
+
+namespace Sci {
+
+/*
+ * Note on how file I/O is implemented: In ScummVM, one can not create/write
+ * 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, 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.
+ *
+ * To handle opening a file called "foobar", what we do is this: First, we
+ * create an 'augmented file name', by prepending the game target and a dash,
+ * so if we running game target sq1sci, the name becomes "sq1sci-foobar".
+ * Next, we check if such a file is known to the SaveFileManager. If so, we
+ * we use that for reading/writing, delete it, whatever.
+ *
+ * If no such file is present but we were only asked to *read* the file,
+ * we fallback to looking for a regular file called "foobar", and open that
+ * for reading only.
+ */
+
+reg_t file_open(EngineState *s, const Common::String &filename, int mode, bool unwrapFilename) {
+ Common::String englishName = g_sci->getSciLanguageString(filename, K_LANG_ENGLISH);
+ englishName.toLowercase();
+
+ Common::String wrappedName = unwrapFilename ? g_sci->wrapFilename(englishName) : englishName;
+ Common::SeekableReadStream *inFile = 0;
+ Common::WriteStream *outFile = 0;
+ Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
+
+ bool isCompressed = true;
+ const SciGameId gameId = g_sci->getGameId();
+ if ((gameId == GID_QFG1 || gameId == GID_QFG1VGA || gameId == GID_QFG2 || gameId == GID_QFG3)
+ && englishName.hasSuffix(".sav")) {
+ // QFG Characters are saved via the CharSave object.
+ // We leave them uncompressed so that they can be imported in later QFG
+ // games.
+ // Rooms/Scripts: QFG1: 601, QFG2: 840, QFG3/4: 52
+ isCompressed = false;
+ }
+
+ if (mode == _K_FILE_MODE_OPEN_OR_FAIL) {
+ // Try to open file, abort if not possible
+ inFile = saveFileMan->openForLoading(wrappedName);
+ // If no matching savestate exists: fall back to reading from a regular
+ // file
+ if (!inFile)
+ inFile = SearchMan.createReadStreamForMember(englishName);
+
+ 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) {
+ // Create the file, destroying any content it might have had
+ outFile = saveFileMan->openForSaving(wrappedName, isCompressed);
+ if (!outFile)
+ debugC(kDebugLevelFile, " -> file_open(_K_FILE_MODE_CREATE): failed to create file '%s'", englishName.c_str());
+ } else if (mode == _K_FILE_MODE_OPEN_OR_CREATE) {
+ // Try to open file, create it if it doesn't exist
+ outFile = saveFileMan->openForSaving(wrappedName, isCompressed);
+ if (!outFile)
+ debugC(kDebugLevelFile, " -> file_open(_K_FILE_MODE_CREATE): failed to create file '%s'", englishName.c_str());
+
+ // QfG1 opens the character export file with _K_FILE_MODE_CREATE first,
+ // closes it immediately and opens it again with this here. Perhaps
+ // other games use this for read access as well. I guess changing this
+ // whole code into using virtual files and writing them after close
+ // would be more appropriate.
+ } else {
+ error("file_open: unsupported mode %d (filename '%s')", mode, englishName.c_str());
+ }
+
+ if (!inFile && !outFile) { // Failed
+ debugC(kDebugLevelFile, " -> file_open() failed");
+ return SIGNAL_REG;
+ }
+
+ // Find a free file handle
+ uint handle = 1; // Ignore _fileHandles[0]
+ while ((handle < s->_fileHandles.size()) && s->_fileHandles[handle].isOpen())
+ handle++;
+
+ if (handle == s->_fileHandles.size()) {
+ // Hit size limit => Allocate more space
+ s->_fileHandles.resize(s->_fileHandles.size() + 1);
+ }
+
+ s->_fileHandles[handle]._in = inFile;
+ s->_fileHandles[handle]._out = outFile;
+ s->_fileHandles[handle]._name = englishName;
+
+ debugC(kDebugLevelFile, " -> opened file '%s' with handle %d", englishName.c_str(), handle);
+ return make_reg(0, handle);
+}
+
+FileHandle *getFileFromHandle(EngineState *s, uint handle) {
+ if (handle == 0 || handle == VIRTUALFILE_HANDLE) {
+ error("Attempt to use invalid file handle (%d)", handle);
+ return 0;
+ }
+
+ if ((handle >= s->_fileHandles.size()) || !s->_fileHandles[handle].isOpen()) {
+ warning("Attempt to use invalid/unused file handle %d", handle);
+ return 0;
+ }
+
+ return &s->_fileHandles[handle];
+}
+
+int fgets_wrapper(EngineState *s, char *dest, int maxsize, int handle) {
+ FileHandle *f = getFileFromHandle(s, handle);
+ if (!f)
+ return 0;
+
+ if (!f->_in) {
+ error("fgets_wrapper: Trying to read from file '%s' opened for writing", f->_name.c_str());
+ return 0;
+ }
+ int readBytes = 0;
+ if (maxsize > 1) {
+ memset(dest, 0, maxsize);
+ f->_in->readLine(dest, maxsize);
+ readBytes = strlen(dest); // FIXME: sierra sci returned byte count and didn't react on NUL characters
+ // The returned string must not have an ending LF
+ if (readBytes > 0) {
+ if (dest[readBytes - 1] == 0x0A)
+ dest[readBytes - 1] = 0;
+ }
+ } else {
+ *dest = 0;
+ }
+
+ debugC(kDebugLevelFile, " -> FGets'ed \"%s\"", dest);
+ return readBytes;
+}
+
+static bool _savegame_sort_byDate(const SavegameDesc &l, const SavegameDesc &r) {
+ if (l.date != r.date)
+ return (l.date > r.date);
+ return (l.time > r.time);
+}
+
+// Create a sorted array containing all found savedgames
+void listSavegames(Common::Array<SavegameDesc> &saves) {
+ Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
+
+ // Load all saves
+ Common::StringArray saveNames = saveFileMan->listSavefiles(g_sci->getSavegamePattern());
+
+ for (Common::StringArray::const_iterator iter = saveNames.begin(); iter != saveNames.end(); ++iter) {
+ Common::String filename = *iter;
+ Common::SeekableReadStream *in;
+ if ((in = saveFileMan->openForLoading(filename))) {
+ SavegameMetadata meta;
+ if (!get_savegame_metadata(in, &meta) || meta.name.empty()) {
+ // invalid
+ delete in;
+ continue;
+ }
+ delete in;
+
+ SavegameDesc desc;
+ desc.id = strtol(filename.end() - 3, NULL, 10);
+ desc.date = meta.saveDate;
+ // We need to fix date in here, because we save DDMMYYYY instead of
+ // YYYYMMDD, so sorting wouldn't work
+ desc.date = ((desc.date & 0xFFFF) << 16) | ((desc.date & 0xFF0000) >> 8) | ((desc.date & 0xFF000000) >> 24);
+ desc.time = meta.saveTime;
+ desc.version = meta.version;
+
+ if (meta.name.lastChar() == '\n')
+ meta.name.deleteLastChar();
+
+ Common::strlcpy(desc.name, meta.name.c_str(), SCI_MAX_SAVENAME_LENGTH);
+
+ debug(3, "Savegame in file %s ok, id %d", filename.c_str(), desc.id);
+
+ saves.push_back(desc);
+ }
+ }
+
+ // Sort the list by creation date of the saves
+ Common::sort(saves.begin(), saves.end(), _savegame_sort_byDate);
+}
+
+// Find a savedgame according to virtualId and return the position within our array
+int findSavegame(Common::Array<SavegameDesc> &saves, int16 savegameId) {
+ for (uint saveNr = 0; saveNr < saves.size(); saveNr++) {
+ if (saves[saveNr].id == savegameId)
+ return saveNr;
+ }
+ return -1;
+}
+
+
+FileHandle::FileHandle() : _in(0), _out(0) {
+}
+
+FileHandle::~FileHandle() {
+ close();
+}
+
+void FileHandle::close() {
+ delete _in;
+ delete _out;
+ _in = 0;
+ _out = 0;
+ _name.clear();
+}
+
+bool FileHandle::isOpen() const {
+ return _in || _out;
+}
+
+
+void DirSeeker::addAsVirtualFiles(Common::String title, Common::String fileMask) {
+ Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
+ Common::StringArray foundFiles = saveFileMan->listSavefiles(fileMask);
+ if (!foundFiles.empty()) {
+ _files.push_back(title);
+ _virtualFiles.push_back("");
+ Common::StringArray::iterator it;
+ Common::StringArray::iterator it_end = foundFiles.end();
+
+ for (it = foundFiles.begin(); it != it_end; it++) {
+ Common::String regularFilename = *it;
+ Common::String wrappedFilename = Common::String(regularFilename.c_str() + fileMask.size() - 1);
+
+ Common::SeekableReadStream *testfile = saveFileMan->openForLoading(regularFilename);
+ int32 testfileSize = testfile->size();
+ delete testfile;
+ if (testfileSize > 1024) // check, if larger than 1k. in that case its a saved game.
+ continue; // and we dont want to have those in the list
+ // We need to remove the prefix for display purposes
+ _files.push_back(wrappedFilename);
+ // but remember the actual name as well
+ _virtualFiles.push_back(regularFilename);
+ }
+ }
+}
+
+Common::String DirSeeker::getVirtualFilename(uint fileNumber) {
+ if (fileNumber >= _virtualFiles.size())
+ error("invalid virtual filename access");
+ return _virtualFiles[fileNumber];
+}
+
+reg_t DirSeeker::firstFile(const Common::String &mask, reg_t buffer, SegManager *segMan) {
+ // Verify that we are given a valid buffer
+ if (!buffer.getSegment()) {
+ error("DirSeeker::firstFile('%s') invoked with invalid buffer", mask.c_str());
+ return NULL_REG;
+ }
+ _outbuffer = buffer;
+ _files.clear();
+ _virtualFiles.clear();
+
+ int QfGImport = g_sci->inQfGImportRoom();
+ if (QfGImport) {
+ _files.clear();
+ addAsVirtualFiles("-QfG1-", "qfg1-*");
+ addAsVirtualFiles("-QfG1VGA-", "qfg1vga-*");
+ if (QfGImport > 2)
+ addAsVirtualFiles("-QfG2-", "qfg2-*");
+ if (QfGImport > 3)
+ addAsVirtualFiles("-QfG3-", "qfg3-*");
+
+ if (QfGImport == 3) {
+ // QfG3 sorts the filelisting itself, we can't let that happen otherwise our
+ // virtual list would go out-of-sync
+ reg_t savedHeros = segMan->findObjectByName("savedHeros");
+ if (!savedHeros.isNull())
+ writeSelectorValue(segMan, savedHeros, SELECTOR(sort), 0);
+ }
+
+ } else {
+ // Prefix the mask
+ const Common::String wrappedMask = g_sci->wrapFilename(mask);
+
+ // Obtain a list of all files matching the given mask
+ Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
+ _files = saveFileMan->listSavefiles(wrappedMask);
+ }
+
+ // Reset the list iterator and write the first match to the output buffer,
+ // if any.
+ _iter = _files.begin();
+ return nextFile(segMan);
+}
+
+reg_t DirSeeker::nextFile(SegManager *segMan) {
+ if (_iter == _files.end()) {
+ return NULL_REG;
+ }
+
+ Common::String string;
+
+ if (_virtualFiles.empty()) {
+ // Strip the prefix, if we don't got a virtual filelisting
+ const Common::String wrappedString = *_iter;
+ string = g_sci->unwrapFilename(wrappedString);
+ } else {
+ string = *_iter;
+ }
+ if (string.size() > 12)
+ string = Common::String(string.c_str(), 12);
+ segMan->strcpy(_outbuffer, string.c_str());
+
+ // Return the result and advance the list iterator :)
+ ++_iter;
+ return _outbuffer;
+}
+
+
+#ifdef ENABLE_SCI32
+
+VirtualIndexFile::VirtualIndexFile(Common::String fileName) : _fileName(fileName), _changed(false) {
+ Common::SeekableReadStream *inFile = g_sci->getSaveFileManager()->openForLoading(fileName);
+
+ _bufferSize = inFile->size();
+ _buffer = new char[_bufferSize];
+ inFile->read(_buffer, _bufferSize);
+ _ptr = _buffer;
+ delete inFile;
+}
+
+VirtualIndexFile::VirtualIndexFile(uint32 initialSize) : _changed(false) {
+ _bufferSize = initialSize;
+ _buffer = new char[_bufferSize];
+ _ptr = _buffer;
+}
+
+VirtualIndexFile::~VirtualIndexFile() {
+ close();
+
+ _bufferSize = 0;
+ delete[] _buffer;
+ _buffer = 0;
+}
+
+uint32 VirtualIndexFile::read(char *buffer, uint32 size) {
+ uint32 curPos = _ptr - _buffer;
+ uint32 finalSize = MIN<uint32>(size, _bufferSize - curPos);
+ char *localPtr = buffer;
+
+ for (uint32 i = 0; i < finalSize; i++)
+ *localPtr++ = *_ptr++;
+
+ return finalSize;
+}
+
+uint32 VirtualIndexFile::write(const char *buffer, uint32 size) {
+ _changed = true;
+ uint32 curPos = _ptr - _buffer;
+
+ // Check if the buffer needs to be resized
+ if (curPos + size >= _bufferSize) {
+ _bufferSize = curPos + size + 1;
+ char *tmp = _buffer;
+ _buffer = new char[_bufferSize];
+ _ptr = _buffer + curPos;
+ memcpy(_buffer, tmp, _bufferSize);
+ delete[] tmp;
+ }
+
+ for (uint32 i = 0; i < size; i++)
+ *_ptr++ = *buffer++;
+
+ return size;
+}
+
+uint32 VirtualIndexFile::readLine(char *buffer, uint32 size) {
+ uint32 startPos = _ptr - _buffer;
+ uint32 bytesRead = 0;
+ char *localPtr = buffer;
+
+ // This is not a full-blown implementation of readLine, but it
+ // suffices for Phantasmagoria
+ while (startPos + bytesRead < size) {
+ bytesRead++;
+
+ if (*_ptr == 0 || *_ptr == 0x0A) {
+ _ptr++;
+ *localPtr = 0;
+ return bytesRead;
+ } else {
+ *localPtr++ = *_ptr++;
+ }
+ }
+
+ return bytesRead;
+}
+
+bool VirtualIndexFile::seek(int32 offset, int whence) {
+ uint32 startPos = _ptr - _buffer;
+ assert(offset >= 0);
+
+ switch (whence) {
+ case SEEK_CUR:
+ assert(startPos + offset < _bufferSize);
+ _ptr += offset;
+ break;
+ case SEEK_SET:
+ assert(offset < (int32)_bufferSize);
+ _ptr = _buffer + offset;
+ break;
+ case SEEK_END:
+ assert((int32)_bufferSize - offset >= 0);
+ _ptr = _buffer + (_bufferSize - offset);
+ break;
+ }
+
+ return true;
+}
+
+void VirtualIndexFile::close() {
+ if (_changed && !_fileName.empty()) {
+ Common::WriteStream *outFile = g_sci->getSaveFileManager()->openForSaving(_fileName);
+ outFile->write(_buffer, _bufferSize);
+ delete outFile;
+ }
+
+ // Maintain the buffer, and seek to the beginning of it
+ _ptr = _buffer;
+}
+
+#endif
+
+} // End of namespace Sci
diff --git a/engines/sci/engine/file.h b/engines/sci/engine/file.h
new file mode 100644
index 0000000000..1c8e302d15
--- /dev/null
+++ b/engines/sci/engine/file.h
@@ -0,0 +1,140 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef SCI_ENGINE_FILE_H
+#define SCI_ENGINE_FILE_H
+
+#include "common/str-array.h"
+#include "common/stream.h"
+
+namespace Sci {
+
+enum {
+ _K_FILE_MODE_OPEN_OR_CREATE = 0,
+ _K_FILE_MODE_OPEN_OR_FAIL = 1,
+ _K_FILE_MODE_CREATE = 2
+};
+
+/* Maximum length of a savegame name (including terminator character). */
+#define SCI_MAX_SAVENAME_LENGTH 0x24
+
+enum {
+ MAX_SAVEGAME_NR = 20 /**< Maximum number of savegames */
+};
+
+#define VIRTUALFILE_HANDLE 200
+#define PHANTASMAGORIA_SAVEGAME_INDEX "phantsg.dir"
+
+struct SavegameDesc {
+ int16 id;
+ int virtualId; // straight numbered, according to id but w/o gaps
+ int date;
+ int time;
+ int version;
+ char name[SCI_MAX_SAVENAME_LENGTH];
+};
+
+class FileHandle {
+public:
+ Common::String _name;
+ Common::SeekableReadStream *_in;
+ Common::WriteStream *_out;
+
+public:
+ FileHandle();
+ ~FileHandle();
+
+ void close();
+ bool isOpen() const;
+};
+
+
+class DirSeeker {
+protected:
+ reg_t _outbuffer;
+ Common::StringArray _files;
+ Common::StringArray _virtualFiles;
+ Common::StringArray::const_iterator _iter;
+
+public:
+ DirSeeker() {
+ _outbuffer = NULL_REG;
+ _iter = _files.begin();
+ }
+
+ reg_t firstFile(const Common::String &mask, reg_t buffer, SegManager *segMan);
+ reg_t nextFile(SegManager *segMan);
+
+ Common::String getVirtualFilename(uint fileNumber);
+
+private:
+ void addAsVirtualFiles(Common::String title, Common::String fileMask);
+};
+
+
+#ifdef ENABLE_SCI32
+
+/**
+ * An implementation of a virtual file that supports basic read and write
+ * operations simultaneously.
+ *
+ * This class has been initially implemented for Phantasmagoria, which has its
+ * own custom save/load code. The load code keeps checking for the existence
+ * of the save index file and keeps closing and reopening it for each save
+ * slot. This is notoriously slow and clumsy, and introduces noticeable delays,
+ * especially for non-desktop systems. Also, its game scripts request to open
+ * the index file for reading and writing with the same parameters
+ * (SaveManager::setCurrentSave and SaveManager::getCurrentSave). Moreover,
+ * the game scripts reopen the index file for writing in order to update it
+ * and seek within it. We do not support seeking in writeable streams, and the
+ * fact that our saved games are ZIP files makes this operation even more
+ * expensive. Finally, the savegame index file is supposed to be expanded when
+ * a new save slot is added.
+ * For the aforementioned reasons, this class has been implemented, which offers
+ * the basic functionality needed by the game scripts in Phantasmagoria.
+ */
+class VirtualIndexFile {
+public:
+ VirtualIndexFile(Common::String fileName);
+ VirtualIndexFile(uint32 initialSize);
+ ~VirtualIndexFile();
+
+ uint32 read(char *buffer, uint32 size);
+ uint32 readLine(char *buffer, uint32 size);
+ uint32 write(const char *buffer, uint32 size);
+ bool seek(int32 offset, int whence);
+ void close();
+
+private:
+ char *_buffer;
+ uint32 _bufferSize;
+ char *_ptr;
+
+ Common::String _fileName;
+ bool _changed;
+};
+
+#endif
+
+} // End of namespace Sci
+
+#endif // SCI_ENGINE_FILE_H
diff --git a/engines/sci/engine/gc.cpp b/engines/sci/engine/gc.cpp
index 2d71878bda..9a30ff3e17 100644
--- a/engines/sci/engine/gc.cpp
+++ b/engines/sci/engine/gc.cpp
@@ -47,7 +47,7 @@ const char *segmentTypeNames[] = {
#endif
void WorklistManager::push(reg_t reg) {
- if (!reg.segment) // No numbers
+ if (!reg.getSegment()) // No numbers
return;
debugC(kDebugLevelGC, "[GC] Adding %04x:%04x", PRINT_REG(reg));
@@ -69,7 +69,7 @@ static AddrSet *normalizeAddresses(SegManager *segMan, const AddrSet &nonnormal_
for (AddrSet::const_iterator i = nonnormal_map.begin(); i != nonnormal_map.end(); ++i) {
reg_t reg = i->_key;
- SegmentObj *mobj = segMan->getSegmentObj(reg.segment);
+ SegmentObj *mobj = segMan->getSegmentObj(reg.getSegment());
if (mobj) {
reg = mobj->findCanonicAddress(segMan, reg);
@@ -85,11 +85,11 @@ static void processWorkList(SegManager *segMan, WorklistManager &wm, const Commo
while (!wm._worklist.empty()) {
reg_t reg = wm._worklist.back();
wm._worklist.pop_back();
- if (reg.segment != stackSegment) { // No need to repeat this one
+ if (reg.getSegment() != stackSegment) { // No need to repeat this one
debugC(kDebugLevelGC, "[GC] Checking %04x:%04x", PRINT_REG(reg));
- if (reg.segment < heap.size() && heap[reg.segment]) {
+ if (reg.getSegment() < heap.size() && heap[reg.getSegment()]) {
// Valid heap object? Find its outgoing references!
- wm.pushArray(heap[reg.segment]->listAllOutgoingReferences(reg));
+ wm.pushArray(heap[reg.getSegment()]->listAllOutgoingReferences(reg));
}
}
}
diff --git a/engines/sci/engine/gc.h b/engines/sci/engine/gc.h
index 97aa6513b6..9e02bbd0bd 100644
--- a/engines/sci/engine/gc.h
+++ b/engines/sci/engine/gc.h
@@ -32,7 +32,7 @@ namespace Sci {
struct reg_t_Hash {
uint operator()(const reg_t& x) const {
- return (x.segment << 3) ^ x.offset ^ (x.offset << 16);
+ return (x.getSegment() << 3) ^ x.getOffset() ^ (x.getOffset() << 16);
}
};
diff --git a/engines/sci/engine/kernel.cpp b/engines/sci/engine/kernel.cpp
index c99bc4fe47..46051ef145 100644
--- a/engines/sci/engine/kernel.cpp
+++ b/engines/sci/engine/kernel.cpp
@@ -357,27 +357,27 @@ static uint16 *parseKernelSignature(const char *kernelName, const char *writtenS
uint16 Kernel::findRegType(reg_t reg) {
// No segment? Must be integer
- if (!reg.segment)
- return SIG_TYPE_INTEGER | (reg.offset ? 0 : SIG_TYPE_NULL);
+ if (!reg.getSegment())
+ return SIG_TYPE_INTEGER | (reg.getOffset() ? 0 : SIG_TYPE_NULL);
- if (reg.segment == 0xFFFF)
+ if (reg.getSegment() == 0xFFFF)
return SIG_TYPE_UNINITIALIZED;
// Otherwise it's an object
- SegmentObj *mobj = _segMan->getSegmentObj(reg.segment);
+ SegmentObj *mobj = _segMan->getSegmentObj(reg.getSegment());
if (!mobj)
return SIG_TYPE_ERROR;
uint16 result = 0;
- if (!mobj->isValidOffset(reg.offset))
+ if (!mobj->isValidOffset(reg.getOffset()))
result |= SIG_IS_INVALID;
switch (mobj->getType()) {
case SEG_TYPE_SCRIPT:
- if (reg.offset <= (*(Script *)mobj).getBufSize() &&
- reg.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET &&
- RAW_IS_OBJECT((*(Script *)mobj).getBuf(reg.offset)) ) {
- result |= ((Script *)mobj)->getObject(reg.offset) ? SIG_TYPE_OBJECT : SIG_TYPE_REFERENCE;
+ if (reg.getOffset() <= (*(Script *)mobj).getBufSize() &&
+ reg.getOffset() >= (uint)-SCRIPT_OBJECT_MAGIC_OFFSET &&
+ (*(Script *)mobj).offsetIsObject(reg.getOffset())) {
+ result |= ((Script *)mobj)->getObject(reg.getOffset()) ? SIG_TYPE_OBJECT : SIG_TYPE_REFERENCE;
} else
result |= SIG_TYPE_REFERENCE;
break;
@@ -608,7 +608,7 @@ void Kernel::mapFunctions() {
_kernelFuncs[id].workarounds = kernelMap->workarounds;
if (kernelMap->subFunctions) {
// Get version for subfunction identification
- SciVersion mySubVersion = (SciVersion)kernelMap->function(NULL, 0, NULL).offset;
+ SciVersion mySubVersion = (SciVersion)kernelMap->function(NULL, 0, NULL).getOffset();
// Now check whats the highest subfunction-id for this version
const SciKernelMapSubEntry *kernelSubMap = kernelMap->subFunctions;
uint16 subFunctionCount = 0;
@@ -757,13 +757,26 @@ bool Kernel::debugSetFunction(const char *kernelName, int logging, int breakpoin
return true;
}
-void Kernel::setDefaultKernelNames(GameFeatures *features) {
- _kernelNames = Common::StringArray(s_defaultKernelNames, ARRAYSIZE(s_defaultKernelNames));
+#ifdef ENABLE_SCI32
+enum {
+ kKernelEntriesSci2 = 0x8b,
+ kKernelEntriesGk2Demo = 0xa0,
+ kKernelEntriesSci21 = 0x9d,
+ kKernelEntriesSci3 = 0xa1
+};
+#endif
+
+void Kernel::loadKernelNames(GameFeatures *features) {
+ _kernelNames.clear();
- // Some (later) SCI versions replaced CanBeHere by CantBeHere
- // If vocab.999 exists, the kernel function is still named CanBeHere
- if (_selectorCache.cantBeHere != -1)
- _kernelNames[0x4d] = "CantBeHere";
+ if (getSciVersion() <= SCI_VERSION_1_1) {
+ _kernelNames = Common::StringArray(s_defaultKernelNames, ARRAYSIZE(s_defaultKernelNames));
+
+ // Some (later) SCI versions replaced CanBeHere by CantBeHere
+ // If vocab.999 exists, the kernel function is still named CanBeHere
+ if (_selectorCache.cantBeHere != -1)
+ _kernelNames[0x4d] = "CantBeHere";
+ }
switch (getSciVersion()) {
case SCI_VERSION_0_EARLY:
@@ -817,66 +830,60 @@ void Kernel::setDefaultKernelNames(GameFeatures *features) {
_kernelNames[0x7c] = "Message";
break;
- default:
- // Use default table for the other versions
- break;
- }
-}
-
#ifdef ENABLE_SCI32
+ case SCI_VERSION_2:
+ _kernelNames = Common::StringArray(sci2_default_knames, kKernelEntriesSci2);
+ break;
-enum {
- kKernelEntriesSci2 = 0x8b,
- kKernelEntriesGk2Demo = 0xa0,
- kKernelEntriesSci21 = 0x9d,
- kKernelEntriesSci3 = 0xa1
-};
-
-void Kernel::setKernelNamesSci2() {
- _kernelNames = Common::StringArray(sci2_default_knames, kKernelEntriesSci2);
-}
+ case SCI_VERSION_2_1:
+ if (features->detectSci21KernelType() == SCI_VERSION_2) {
+ // Some early SCI2.1 games use a modified SCI2 kernel table instead of
+ // the SCI2.1 kernel table. We detect which version to use based on
+ // how kDoSound is called from Sound::play().
+ // Known games that use this:
+ // GK2 demo
+ // KQ7 1.4
+ // PQ4 SWAT demo
+ // LSL6
+ // PQ4CD
+ // QFG4CD
+
+ // This is interesting because they all have the same interpreter
+ // version (2.100.002), yet they would not be compatible with other
+ // games of the same interpreter.
+
+ _kernelNames = Common::StringArray(sci2_default_knames, kKernelEntriesGk2Demo);
+ // OnMe is IsOnMe here, but they should be compatible
+ _kernelNames[0x23] = "Robot"; // Graph in SCI2
+ _kernelNames[0x2e] = "Priority"; // DisposeTextBitmap in SCI2
+ } else {
+ // Normal SCI2.1 kernel table
+ _kernelNames = Common::StringArray(sci21_default_knames, kKernelEntriesSci21);
+ }
+ break;
-void Kernel::setKernelNamesSci21(GameFeatures *features) {
- // Some SCI games use a modified SCI2 kernel table instead of the
- // SCI2.1 kernel table. We detect which version to use based on
- // how kDoSound is called from Sound::play().
- // Known games that use this:
- // GK2 demo
- // KQ7 1.4
- // PQ4 SWAT demo
- // LSL6
- // PQ4CD
- // QFG4CD
-
- // This is interesting because they all have the same interpreter
- // version (2.100.002), yet they would not be compatible with other
- // games of the same interpreter.
-
- if (getSciVersion() != SCI_VERSION_3 && features->detectSci21KernelType() == SCI_VERSION_2) {
- _kernelNames = Common::StringArray(sci2_default_knames, kKernelEntriesGk2Demo);
- // OnMe is IsOnMe here, but they should be compatible
- _kernelNames[0x23] = "Robot"; // Graph in SCI2
- _kernelNames[0x2e] = "Priority"; // DisposeTextBitmap in SCI2
- } else if (getSciVersion() != SCI_VERSION_3) {
- _kernelNames = Common::StringArray(sci21_default_knames, kKernelEntriesSci21);
- } else if (getSciVersion() == SCI_VERSION_3) {
+ case SCI_VERSION_3:
_kernelNames = Common::StringArray(sci21_default_knames, kKernelEntriesSci3);
- }
-}
-#endif
-
-void Kernel::loadKernelNames(GameFeatures *features) {
- _kernelNames.clear();
+ // In SCI3, some kernel functions have been removed, and others have been added
+ _kernelNames[0x18] = "Dummy"; // AddMagnify in SCI2.1
+ _kernelNames[0x19] = "Dummy"; // DeleteMagnify in SCI2.1
+ _kernelNames[0x30] = "Dummy"; // SetScroll in SCI2.1
+ _kernelNames[0x39] = "Dummy"; // ShowMovie in SCI2.1
+ _kernelNames[0x4c] = "Dummy"; // ScrollWindow in SCI2.1
+ _kernelNames[0x56] = "Dummy"; // VibrateMouse in SCI2.1 (only used in QFG4 floppy)
+ _kernelNames[0x64] = "Dummy"; // AvoidPath in SCI2.1
+ _kernelNames[0x66] = "Dummy"; // MergePoly in SCI2.1
+ _kernelNames[0x8d] = "MessageBox"; // Dummy in SCI2.1
+ _kernelNames[0x9b] = "Minimize"; // Dummy in SCI2.1
-#ifdef ENABLE_SCI32
- if (getSciVersion() >= SCI_VERSION_2_1)
- setKernelNamesSci21(features);
- else if (getSciVersion() == SCI_VERSION_2)
- setKernelNamesSci2();
- else
+ break;
#endif
- setDefaultKernelNames(features);
+
+ default:
+ // Use default table for the other versions
+ break;
+ }
mapFunctions();
}
@@ -885,15 +892,15 @@ Common::String Kernel::lookupText(reg_t address, int index) {
char *seeker;
Resource *textres;
- if (address.segment)
+ if (address.getSegment())
return _segMan->getString(address);
int textlen;
int _index = index;
- textres = _resMan->findResource(ResourceId(kResourceTypeText, address.offset), 0);
+ textres = _resMan->findResource(ResourceId(kResourceTypeText, address.getOffset()), 0);
if (!textres) {
- error("text.%03d not found", address.offset);
+ error("text.%03d not found", address.getOffset());
return NULL; /* Will probably segfault */
}
@@ -907,7 +914,7 @@ Common::String Kernel::lookupText(reg_t address, int index) {
if (textlen)
return seeker;
- error("Index %d out of bounds in text.%03d", _index, address.offset);
+ error("Index %d out of bounds in text.%03d", _index, address.getOffset());
return NULL;
}
diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h
index e549c1f8ae..f985a69ebc 100644
--- a/engines/sci/engine/kernel.h
+++ b/engines/sci/engine/kernel.h
@@ -224,23 +224,6 @@ public:
private:
/**
- * Sets the default kernel function names, based on the SCI version used.
- */
- void setDefaultKernelNames(GameFeatures *features);
-
-#ifdef ENABLE_SCI32
- /**
- * Sets the default kernel function names to the SCI2 kernel functions.
- */
- void setKernelNamesSci2();
-
- /**
- * Sets the default kernel function names to the SCI2.1 kernel functions.
- */
- void setKernelNamesSci21(GameFeatures *features);
-#endif
-
- /**
* Loads the kernel selector names.
*/
void loadSelectorNames();
@@ -276,9 +259,6 @@ private:
const Common::String _invalid;
};
-/* Maximum length of a savegame name (including terminator character). */
-#define SCI_MAX_SAVENAME_LENGTH 0x24
-
/******************** Kernel functions ********************/
reg_t kStrLen(EngineState *s, int argc, reg_t *argv);
@@ -326,10 +306,6 @@ reg_t kTimesCot(EngineState *s, int argc, reg_t *argv);
reg_t kCosDiv(EngineState *s, int argc, reg_t *argv);
reg_t kSinDiv(EngineState *s, int argc, reg_t *argv);
reg_t kValidPath(EngineState *s, int argc, reg_t *argv);
-reg_t kFOpen(EngineState *s, int argc, reg_t *argv);
-reg_t kFPuts(EngineState *s, int argc, reg_t *argv);
-reg_t kFGets(EngineState *s, int argc, reg_t *argv);
-reg_t kFClose(EngineState *s, int argc, reg_t *argv);
reg_t kMapKeyToDir(EngineState *s, int argc, reg_t *argv);
reg_t kGlobalToLocal(EngineState *s, int argc, reg_t *argv);
reg_t kLocalToGlobal(EngineState *s, int argc, reg_t *argv);
@@ -436,6 +412,7 @@ reg_t kListAt(EngineState *s, int argc, reg_t *argv);
reg_t kString(EngineState *s, int argc, reg_t *argv);
reg_t kMulDiv(EngineState *s, int argc, reg_t *argv);
reg_t kCantBeHere32(EngineState *s, int argc, reg_t *argv);
+reg_t kRemapColors32(EngineState *s, int argc, reg_t *argv);
// "Screen items" in SCI32 are views
reg_t kAddScreenItem(EngineState *s, int argc, reg_t *argv);
reg_t kUpdateScreenItem(EngineState *s, int argc, reg_t *argv);
@@ -458,10 +435,15 @@ 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);
+reg_t kMakeSaveCatName(EngineState *s, int argc, reg_t *argv);
+reg_t kMakeSaveFileName(EngineState *s, int argc, reg_t *argv);
+reg_t kSetScroll(EngineState *s, int argc, reg_t *argv);
+reg_t kPalCycle(EngineState *s, int argc, reg_t *argv);
// SCI2.1 Kernel Functions
reg_t kText(EngineState *s, int argc, reg_t *argv);
reg_t kSave(EngineState *s, int argc, reg_t *argv);
+reg_t kAutoSave(EngineState *s, int argc, reg_t *argv);
reg_t kList(EngineState *s, int argc, reg_t *argv);
reg_t kRobot(EngineState *s, int argc, reg_t *argv);
reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv);
@@ -473,12 +455,16 @@ reg_t kMoveToEnd(EngineState *s, int argc, reg_t *argv);
reg_t kGetWindowsOption(EngineState *s, int argc, reg_t *argv);
reg_t kWinHelp(EngineState *s, int argc, reg_t *argv);
reg_t kGetConfig(EngineState *s, int argc, reg_t *argv);
+reg_t kGetSierraProfileInt(EngineState *s, int argc, reg_t *argv);
reg_t kCelInfo(EngineState *s, int argc, reg_t *argv);
reg_t kSetLanguage(EngineState *s, int argc, reg_t *argv);
reg_t kScrollWindow(EngineState *s, int argc, reg_t *argv);
reg_t kSetFontRes(EngineState *s, int argc, reg_t *argv);
reg_t kFont(EngineState *s, int argc, reg_t *argv);
reg_t kBitmap(EngineState *s, int argc, reg_t *argv);
+reg_t kAddLine(EngineState *s, int argc, reg_t *argv);
+reg_t kUpdateLine(EngineState *s, int argc, reg_t *argv);
+reg_t kDeleteLine(EngineState *s, int argc, reg_t *argv);
// SCI3 Kernel functions
reg_t kPlayDuck(EngineState *s, int argc, reg_t *argv);
@@ -556,6 +542,7 @@ reg_t kFileIOWriteByte(EngineState *s, int argc, reg_t *argv);
reg_t kFileIOReadWord(EngineState *s, int argc, reg_t *argv);
reg_t kFileIOWriteWord(EngineState *s, int argc, reg_t *argv);
reg_t kFileIOCreateSaveSlot(EngineState *s, int argc, reg_t *argv);
+reg_t kFileIOIsValidDirectory(EngineState *s, int argc, reg_t *argv);
#endif
//@}
diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h
index 622511c906..b6b36c47e7 100644
--- a/engines/sci/engine/kernel_tables.h
+++ b/engines/sci/engine/kernel_tables.h
@@ -239,12 +239,27 @@ static const SciKernelMapSubEntry kFileIO_subops[] = {
{ SIG_SCI32, 15, MAP_CALL(FileIOReadWord), "i", NULL },
{ SIG_SCI32, 16, MAP_CALL(FileIOWriteWord), "ii", NULL },
{ SIG_SCI32, 17, MAP_CALL(FileIOCreateSaveSlot), "ir", NULL },
- { SIG_SCI32, 19, MAP_CALL(Stub), "r", NULL }, // for Torin / Torin demo
+ { SIG_SCI32, 18, MAP_EMPTY(FileIOChangeDirectory), "r", NULL }, // for SQ6, when changing the savegame directory in the save/load dialog
+ { SIG_SCI32, 19, MAP_CALL(FileIOIsValidDirectory), "r", NULL }, // for Torin / Torin demo
#endif
SCI_SUBOPENTRY_TERMINATOR
};
#ifdef ENABLE_SCI32
+
+static const SciKernelMapSubEntry kSave_subops[] = {
+ { SIG_SCI32, 0, MAP_CALL(SaveGame), "[r0]i[r0](r0)", NULL },
+ { SIG_SCI32, 1, MAP_CALL(RestoreGame), "[r0]i[r0]", NULL },
+ { SIG_SCI32, 2, MAP_CALL(GetSaveDir), "(r*)", NULL },
+ { SIG_SCI32, 3, MAP_CALL(CheckSaveGame), ".*", NULL },
+ // Subop 4 hasn't been encountered yet
+ { SIG_SCI32, 5, MAP_CALL(GetSaveFiles), "rrr", NULL },
+ { SIG_SCI32, 6, MAP_CALL(MakeSaveCatName), "rr", NULL },
+ { SIG_SCI32, 7, MAP_CALL(MakeSaveFileName), "rri", NULL },
+ { SIG_SCI32, 8, MAP_CALL(AutoSave), "[o0]", NULL },
+ SCI_SUBOPENTRY_TERMINATOR
+};
+
// version, subId, function-mapping, signature, workarounds
static const SciKernelMapSubEntry kList_subops[] = {
{ SIG_SCI21, 0, MAP_CALL(NewList), "", NULL },
@@ -337,10 +352,10 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_CALL(EditControl), SIG_EVERYWHERE, "[o0][o0]", NULL, NULL },
{ MAP_CALL(Empty), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_CALL(EmptyList), SIG_EVERYWHERE, "l", NULL, NULL },
- { MAP_CALL(FClose), SIG_EVERYWHERE, "i", NULL, NULL },
- { MAP_CALL(FGets), SIG_EVERYWHERE, "rii", NULL, NULL },
- { MAP_CALL(FOpen), SIG_EVERYWHERE, "ri", NULL, NULL },
- { MAP_CALL(FPuts), SIG_EVERYWHERE, "ir", NULL, NULL },
+ { "FClose", kFileIOClose, SIG_EVERYWHERE, "i", NULL, NULL },
+ { "FGets", kFileIOReadString, SIG_EVERYWHERE, "rii", NULL, NULL },
+ { "FOpen", kFileIOOpen, SIG_EVERYWHERE, "ri", NULL, NULL },
+ { "FPuts", kFileIOWriteString, SIG_EVERYWHERE, "ir", NULL, NULL },
{ MAP_CALL(FileIO), SIG_EVERYWHERE, "i(.*)", kFileIO_subops, NULL },
{ MAP_CALL(FindKey), SIG_EVERYWHERE, "l.", NULL, kFindKey_workarounds },
{ MAP_CALL(FirstNode), SIG_EVERYWHERE, "[l0]", NULL, NULL },
@@ -404,7 +419,10 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_CALL(PriCoord), SIG_EVERYWHERE, "i", NULL, NULL },
{ MAP_CALL(Random), SIG_EVERYWHERE, "i(i)(i)", NULL, NULL },
{ MAP_CALL(ReadNumber), SIG_EVERYWHERE, "r", NULL, NULL },
- { MAP_CALL(RemapColors), SIG_EVERYWHERE, "i(i)(i)(i)(i)(i)", NULL, NULL },
+ { MAP_CALL(RemapColors), SIG_SCI11, SIGFOR_ALL, "i(i)(i)(i)(i)", NULL, NULL },
+#ifdef ENABLE_SCI32
+ { "RemapColors", kRemapColors32, SIG_SCI32, SIGFOR_ALL, "i(i)(i)(i)(i)(i)", NULL, NULL },
+#endif
{ MAP_CALL(ResCheck), SIG_EVERYWHERE, "ii(iiii)", NULL, NULL },
{ MAP_CALL(RespondsTo), SIG_EVERYWHERE, ".i", NULL, NULL },
{ MAP_CALL(RestartGame), SIG_EVERYWHERE, "", NULL, NULL },
@@ -500,29 +518,23 @@ static SciKernelMapEntry s_kernelMap[] = {
{ 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 - called by script 64909, Styler::doit()
- // PalCycle - called by Game::newRoom. Related to RemapColors.
- // VibrateMouse - used in QFG4
+ { MAP_CALL(MakeSaveCatName), SIG_EVERYWHERE, "rr", NULL, NULL },
+ { MAP_CALL(MakeSaveFileName), SIG_EVERYWHERE, "rri", NULL, NULL },
+ { MAP_CALL(SetScroll), SIG_EVERYWHERE, "oiiiii(i)", NULL, NULL },
+ { MAP_CALL(PalCycle), SIG_EVERYWHERE, "i(.*)", NULL, NULL },
// 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 },
+ // Future TODO: This call is used in the floppy version of QFG4 to add
+ // vibration to exotic mice with force feedback, such as the Logitech
+ // Cyberman and Wingman mice. Since this is only used for very exotic
+ // hardware and we have no direct and cross-platform way of communicating
+ // with them via SDL, plus we would probably need to make changes to common
+ // code, this call is mapped to an empty function for now as it's a rare
+ // feature not worth the effort.
+ { MAP_EMPTY(VibrateMouse), SIG_EVERYWHERE, "(.*)", NULL, NULL },
// Unused / debug SCI2 unused functions, always mapped to kDummy
@@ -549,6 +561,11 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_DUMMY(InputText), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_DUMMY(TextWidth), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_DUMMY(PointSize), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ // SetScroll is called by script 64909, Styler::doit(), but it doesn't seem to
+ // be used at all (plus, it was then changed to a dummy function in SCI3).
+ // Since this is most likely unused, and we got no test case, error out when
+ // it is called in order to find an actual call to it.
+ { MAP_DUMMY(SetScroll), SIG_EVERYWHERE, "(.*)", NULL, NULL },
// SCI2.1 Kernel Functions
{ MAP_CALL(CD), SIG_EVERYWHERE, "(.*)", NULL, NULL },
@@ -557,18 +574,22 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_CALL(MulDiv), SIG_EVERYWHERE, "iii", NULL, NULL },
{ MAP_CALL(PlayVMD), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_CALL(Robot), SIG_EVERYWHERE, "(.*)", NULL, NULL },
- { MAP_CALL(Save), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(Save), SIG_EVERYWHERE, "i(.*)", kSave_subops, NULL },
{ MAP_CALL(Text), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_CALL(AddPicAt), SIG_EVERYWHERE, "oiii", NULL, NULL },
{ MAP_CALL(GetWindowsOption), SIG_EVERYWHERE, "i", NULL, NULL },
{ MAP_CALL(WinHelp), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_CALL(GetConfig), SIG_EVERYWHERE, "ro", NULL, NULL },
+ { MAP_CALL(GetSierraProfileInt), SIG_EVERYWHERE, "rri", NULL, NULL },
{ MAP_CALL(CelInfo), SIG_EVERYWHERE, "iiiiii", NULL, NULL },
{ MAP_CALL(SetLanguage), SIG_EVERYWHERE, "r", NULL, NULL },
- { MAP_CALL(ScrollWindow), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(ScrollWindow), SIG_EVERYWHERE, "io(.*)", NULL, NULL },
{ MAP_CALL(SetFontRes), SIG_EVERYWHERE, "ii", NULL, NULL },
{ MAP_CALL(Font), SIG_EVERYWHERE, "i(.*)", NULL, NULL },
{ MAP_CALL(Bitmap), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ { MAP_CALL(AddLine), SIG_EVERYWHERE, "oiiiiiiiii", NULL, NULL },
+ { MAP_CALL(UpdateLine), SIG_EVERYWHERE, "[r0]oiiiiiiiii", NULL, NULL },
+ { MAP_CALL(DeleteLine), SIG_EVERYWHERE, "[r0]o", NULL, NULL },
// SCI2.1 Empty Functions
@@ -582,11 +603,6 @@ static SciKernelMapEntry s_kernelMap[] = {
// the game window in Phantasmagoria 2. We ignore these settings completely.
{ MAP_EMPTY(SetWindowsOption), SIG_EVERYWHERE, "ii", NULL, NULL },
- // Used by the Windows version of Phantasmagoria 1 to get the video speed setting. This is called after
- // kGetConfig and overrides the setting obtained by it. It is a dummy function in the DOS Version. We can
- // just use GetConfig and mark this one as empty, like the DOS version does.
- { MAP_EMPTY(GetSierraProfileInt), SIG_EVERYWHERE, "(.*)", NULL, NULL },
-
// Debug function called whenever the current room changes
{ MAP_EMPTY(NewRoom), SIG_EVERYWHERE, "(.*)", NULL, NULL },
@@ -618,14 +634,14 @@ static SciKernelMapEntry s_kernelMap[] = {
// SCI2.1 unmapped functions - TODO!
+ // SetHotRectangles - used by Phantasmagoria 1, script 64981 (used in the chase scene)
+ // <lskovlun> The idea, if I understand correctly, is that the engine generates events
+ // of a special HotRect type continuously when the mouse is on that rectangle
+
// MovePlaneItems - used by SQ6 to scroll through the inventory via the up/down buttons
- // AddLine - used by Torin's Passage to highlight the chapter buttons
- // DeleteLine - used by Torin's Passage to delete the highlight from the chapter buttons
- // UpdateLine - used by LSL6
// SetPalStyleRange - 2 integer parameters, start and end. All styles from start-end
// (inclusive) are set to 0
// 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 },
@@ -820,7 +836,7 @@ static const char *const sci2_default_knames[] = {
/*0x20*/ "AddMagnify",
/*0x21*/ "DeleteMagnify",
/*0x22*/ "IsHiRes",
- /*0x23*/ "Graph",
+ /*0x23*/ "Graph", // Robot in early SCI2.1 games with a SCI2 kernel table
/*0x24*/ "InvertRect", // only in SCI2, not used in any SCI2 game
/*0x25*/ "TextSize",
/*0x26*/ "Message",
@@ -831,7 +847,7 @@ static const char *const sci2_default_knames[] = {
/*0x2b*/ "EditText",
/*0x2c*/ "InputText", // unused function
/*0x2d*/ "CreateTextBitmap",
- /*0x2e*/ "DisposeTextBitmap",
+ /*0x2e*/ "DisposeTextBitmap", // Priority in early SCI2.1 games with a SCI2 kernel table
/*0x2f*/ "GetEvent",
/*0x30*/ "GlobalToLocal",
/*0x31*/ "LocalToGlobal",
@@ -1091,7 +1107,7 @@ static const char *const sci21_default_knames[] = {
/*0x8a*/ "LoadChunk",
/*0x8b*/ "SetPalStyleRange",
/*0x8c*/ "AddPicAt",
- /*0x8d*/ "MessageBox", // SCI3, was Dummy in SCI2.1
+ /*0x8d*/ "Dummy", // MessageBox in SCI3
/*0x8e*/ "NewRoom", // debug function
/*0x8f*/ "Dummy",
/*0x90*/ "Priority",
@@ -1105,7 +1121,7 @@ static const char *const sci21_default_knames[] = {
/*0x98*/ "GetWindowsOption", // Windows only
/*0x99*/ "WinDLL", // Windows only
/*0x9a*/ "Dummy",
- /*0x9b*/ "Minimize", // SCI3, was Dummy in SCI2.1
+ /*0x9b*/ "Dummy", // Minimize in SCI3
/*0x9c*/ "DeletePic",
// == SCI3 only ===============
/*0x9d*/ "Dummy",
@@ -1119,57 +1135,73 @@ static const char *const sci21_default_knames[] = {
// 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*/
+ // 00 - 03 / bnot, add, sub, mul
{Script_None}, {Script_None}, {Script_None}, {Script_None},
- /*04*/
+ // 04 - 07 / div, mod, shr, shl
{Script_None}, {Script_None}, {Script_None}, {Script_None},
- /*08*/
+ // 08 - 0B / xor, and, or, neg
{Script_None}, {Script_None}, {Script_None}, {Script_None},
- /*0C*/
+ // 0C - 0F / not, eq, ne, gt
{Script_None}, {Script_None}, {Script_None}, {Script_None},
- /*10*/
+ // 10 - 13 / ge, lt, le, ugt
{Script_None}, {Script_None}, {Script_None}, {Script_None},
- /*14*/
+ // 14 - 17 / uge, ult, ule, bt
{Script_None}, {Script_None}, {Script_None}, {Script_SRelative},
- /*18*/
+ // 18 - 1B / bnt, jmp, ldi, push
{Script_SRelative}, {Script_SRelative}, {Script_SVariable}, {Script_None},
- /*1C*/
+ // 1C - 1F / pushi, toss, dup, link
{Script_SVariable}, {Script_None}, {Script_None}, {Script_Variable},
- /*20*/
+ // 20 - 23 / call, callk, callb, calle
{Script_SRelative, Script_Byte}, {Script_Variable, Script_Byte}, {Script_Variable, Script_Byte}, {Script_Variable, Script_SVariable, Script_Byte},
- /*24 (24=ret)*/
+ // 24 - 27 / ret, send, dummy, dummy
{Script_End}, {Script_Byte}, {Script_Invalid}, {Script_Invalid},
- /*28*/
+ // 28 - 2B / class, dummy, self, super
{Script_Variable}, {Script_Invalid}, {Script_Byte}, {Script_Variable, Script_Byte},
- /*2C*/
+ // 2C - 2F / rest, lea, selfID, dummy
{Script_SVariable}, {Script_SVariable, Script_Variable}, {Script_None}, {Script_Invalid},
- /*30*/
+ // 30 - 33 / pprev, pToa, aTop, pTos
{Script_None}, {Script_Property}, {Script_Property}, {Script_Property},
- /*34*/
+ // 34 - 37 / sTop, ipToa, dpToa, ipTos
{Script_Property}, {Script_Property}, {Script_Property}, {Script_Property},
- /*38*/
+ // 38 - 3B / dpTos, lofsa, lofss, push0
{Script_Property}, {Script_SRelative}, {Script_SRelative}, {Script_None},
- /*3C*/
+ // 3C - 3F / push1, push2, pushSelf, line
{Script_None}, {Script_None}, {Script_None}, {Script_Word},
- /*40-4F*/
+ // ------------------------------------------------------------------------
+ // 40 - 43 / lag, lal, lat, lap
{Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param},
+ // 44 - 47 / lsg, lsl, lst, lsp
{Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param},
+ // 48 - 4B / lagi, lali, lati, lapi
{Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param},
+ // 4C - 4F / lsgi, lsli, lsti, lspi
{Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param},
- /*50-5F*/
+ // ------------------------------------------------------------------------
+ // 50 - 53 / sag, sal, sat, sap
{Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param},
+ // 54 - 57 / ssg, ssl, sst, ssp
{Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param},
+ // 58 - 5B / sagi, sali, sati, sapi
{Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param},
+ // 5C - 5F / ssgi, ssli, ssti, sspi
{Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param},
- /*60-6F*/
+ // ------------------------------------------------------------------------
+ // 60 - 63 / plusag, plusal, plusat, plusap
{Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param},
+ // 64 - 67 / plussg, plussl, plusst, plussp
{Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param},
+ // 68 - 6B / plusagi, plusali, plusati, plusapi
{Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param},
+ // 6C - 6F / plussgi, plussli, plussti, plusspi
{Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param},
- /*70-7F*/
+ // ------------------------------------------------------------------------
+ // 70 - 73 / minusag, minusal, minusat, minusap
{Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param},
+ // 74 - 77 / minussg, minussl, minusst, minussp
{Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param},
+ // 78 - 7B / minusagi, minusali, minusati, minusapi
{Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param},
+ // 7C - 7F / minussgi, minussli, minussti, minusspi
{Script_Global}, {Script_Local}, {Script_Temp}, {Script_Param}
};
#undef END
diff --git a/engines/sci/engine/kevent.cpp b/engines/sci/engine/kevent.cpp
index df3b3efd57..34477cc23b 100644
--- a/engines/sci/engine/kevent.cpp
+++ b/engines/sci/engine/kevent.cpp
@@ -157,7 +157,7 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) {
s->r_acc = NULL_REG;
}
- if ((s->r_acc.offset) && (g_sci->_debugState.stopOnEvent)) {
+ if ((s->r_acc.getOffset()) && (g_sci->_debugState.stopOnEvent)) {
g_sci->_debugState.stopOnEvent = false;
// A SCI event occurred, and we have been asked to stop, so open the debug console
@@ -248,7 +248,7 @@ reg_t kGlobalToLocal(EngineState *s, int argc, reg_t *argv) {
reg_t planeObject = argc > 1 ? argv[1] : NULL_REG; // SCI32
SegManager *segMan = s->_segMan;
- if (obj.segment) {
+ if (obj.getSegment()) {
int16 x = readSelectorValue(segMan, obj, SELECTOR(x));
int16 y = readSelectorValue(segMan, obj, SELECTOR(y));
@@ -267,7 +267,7 @@ reg_t kLocalToGlobal(EngineState *s, int argc, reg_t *argv) {
reg_t planeObject = argc > 1 ? argv[1] : NULL_REG; // SCI32
SegManager *segMan = s->_segMan;
- if (obj.segment) {
+ if (obj.getSegment()) {
int16 x = readSelectorValue(segMan, obj, SELECTOR(x));
int16 y = readSelectorValue(segMan, obj, SELECTOR(y));
diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp
index 312497720a..e977f15c0c 100644
--- a/engines/sci/engine/kfile.cpp
+++ b/engines/sci/engine/kfile.cpp
@@ -33,6 +33,7 @@
#include "gui/saveload.h"
#include "sci/sci.h"
+#include "sci/engine/file.h"
#include "sci/engine/state.h"
#include "sci/engine/kernel.h"
#include "sci/engine/savegame.h"
@@ -40,209 +41,11 @@
namespace Sci {
-struct SavegameDesc {
- int16 id;
- int virtualId; // straight numbered, according to id but w/o gaps
- int date;
- int time;
- int version;
- char name[SCI_MAX_SAVENAME_LENGTH];
-};
-
-/*
- * Note on how file I/O is implemented: In ScummVM, one can not create/write
- * 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, 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.
- *
- * To handle opening a file called "foobar", what we do is this: First, we
- * create an 'augmented file name', by prepending the game target and a dash,
- * so if we running game target sq1sci, the name becomes "sq1sci-foobar".
- * Next, we check if such a file is known to the SaveFileManager. If so, we
- * we use that for reading/writing, delete it, whatever.
- *
- * If no such file is present but we were only asked to *read* the file,
- * we fallback to looking for a regular file called "foobar", and open that
- * for reading only.
- */
-
-
-
-FileHandle::FileHandle() : _in(0), _out(0) {
-}
-
-FileHandle::~FileHandle() {
- close();
-}
-
-void FileHandle::close() {
- delete _in;
- delete _out;
- _in = 0;
- _out = 0;
- _name.clear();
-}
-
-bool FileHandle::isOpen() const {
- return _in || _out;
-}
-
-
-
-enum {
- _K_FILE_MODE_OPEN_OR_CREATE = 0,
- _K_FILE_MODE_OPEN_OR_FAIL = 1,
- _K_FILE_MODE_CREATE = 2
-};
-
-
-
-reg_t file_open(EngineState *s, const Common::String &filename, int mode, bool unwrapFilename) {
- Common::String englishName = g_sci->getSciLanguageString(filename, K_LANG_ENGLISH);
- Common::String wrappedName = unwrapFilename ? g_sci->wrapFilename(englishName) : englishName;
- Common::SeekableReadStream *inFile = 0;
- Common::WriteStream *outFile = 0;
- Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
-
- if (mode == _K_FILE_MODE_OPEN_OR_FAIL) {
- // Try to open file, abort if not possible
- inFile = saveFileMan->openForLoading(wrappedName);
- // If no matching savestate exists: fall back to reading from a regular
- // file
- if (!inFile)
- inFile = SearchMan.createReadStreamForMember(englishName);
-
- 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) {
- // Create the file, destroying any content it might have had
- outFile = saveFileMan->openForSaving(wrappedName);
- if (!outFile)
- debugC(kDebugLevelFile, " -> file_open(_K_FILE_MODE_CREATE): failed to create file '%s'", englishName.c_str());
- } else if (mode == _K_FILE_MODE_OPEN_OR_CREATE) {
- // Try to open file, create it if it doesn't exist
- outFile = saveFileMan->openForSaving(wrappedName);
- if (!outFile)
- debugC(kDebugLevelFile, " -> file_open(_K_FILE_MODE_CREATE): failed to create file '%s'", englishName.c_str());
- // QfG1 opens the character export file with _K_FILE_MODE_CREATE first,
- // closes it immediately and opens it again with this here. Perhaps
- // other games use this for read access as well. I guess changing this
- // whole code into using virtual files and writing them after close
- // would be more appropriate.
- } else {
- error("file_open: unsupported mode %d (filename '%s')", mode, englishName.c_str());
- }
-
- if (!inFile && !outFile) { // Failed
- debugC(kDebugLevelFile, " -> file_open() failed");
- return SIGNAL_REG;
- }
-
- // Find a free file handle
- uint handle = 1; // Ignore _fileHandles[0]
- while ((handle < s->_fileHandles.size()) && s->_fileHandles[handle].isOpen())
- handle++;
-
- if (handle == s->_fileHandles.size()) {
- // Hit size limit => Allocate more space
- s->_fileHandles.resize(s->_fileHandles.size() + 1);
- }
-
- s->_fileHandles[handle]._in = inFile;
- s->_fileHandles[handle]._out = outFile;
- s->_fileHandles[handle]._name = englishName;
-
- debugC(kDebugLevelFile, " -> opened file '%s' with handle %d", englishName.c_str(), handle);
- return make_reg(0, handle);
-}
-
-reg_t kFOpen(EngineState *s, int argc, reg_t *argv) {
- Common::String name = s->_segMan->getString(argv[0]);
- int mode = argv[1].toUint16();
-
- debugC(kDebugLevelFile, "kFOpen(%s,0x%x)", name.c_str(), mode);
- return file_open(s, name, mode, true);
-}
-
-static FileHandle *getFileFromHandle(EngineState *s, uint handle) {
- if (handle == 0) {
- error("Attempt to use file handle 0");
- return 0;
- }
-
- if ((handle >= s->_fileHandles.size()) || !s->_fileHandles[handle].isOpen()) {
- warning("Attempt to use invalid/unused file handle %d", handle);
- return 0;
- }
-
- return &s->_fileHandles[handle];
-}
-
-reg_t kFClose(EngineState *s, int argc, reg_t *argv) {
- debugC(kDebugLevelFile, "kFClose(%d)", argv[0].toUint16());
- if (argv[0] != SIGNAL_REG) {
- FileHandle *f = getFileFromHandle(s, argv[0].toUint16());
- if (f)
- f->close();
- }
- return s->r_acc;
-}
-
-reg_t kFPuts(EngineState *s, int argc, reg_t *argv) {
- int handle = argv[0].toUint16();
- Common::String data = s->_segMan->getString(argv[1]);
-
- FileHandle *f = getFileFromHandle(s, handle);
- if (f)
- f->_out->write(data.c_str(), data.size());
-
- return s->r_acc;
-}
-
-static int fgets_wrapper(EngineState *s, char *dest, int maxsize, int handle) {
- FileHandle *f = getFileFromHandle(s, handle);
- if (!f)
- return 0;
-
- if (!f->_in) {
- error("fgets_wrapper: Trying to read from file '%s' opened for writing", f->_name.c_str());
- return 0;
- }
- int readBytes = 0;
- if (maxsize > 1) {
- memset(dest, 0, maxsize);
- f->_in->readLine(dest, maxsize);
- readBytes = strlen(dest); // FIXME: sierra sci returned byte count and didn't react on NUL characters
- // The returned string must not have an ending LF
- if (readBytes > 0) {
- if (dest[readBytes - 1] == 0x0A)
- dest[readBytes - 1] = 0;
- }
- } else {
- *dest = 0;
- }
-
- debugC(kDebugLevelFile, " -> FGets'ed \"%s\"", dest);
- return readBytes;
-}
-
-reg_t kFGets(EngineState *s, int argc, reg_t *argv) {
- int maxsize = argv[1].toUint16();
- char *buf = new char[maxsize];
- int handle = argv[2].toUint16();
-
- debugC(kDebugLevelFile, "kFGets(%d, %d)", handle, maxsize);
- int readBytes = fgets_wrapper(s, buf, maxsize, handle);
- s->_segMan->memcpy(argv[0], (const byte*)buf, maxsize);
- delete[] buf;
- return readBytes ? argv[0] : NULL_REG;
-}
+extern reg_t file_open(EngineState *s, const Common::String &filename, int mode, bool unwrapFilename);
+extern FileHandle *getFileFromHandle(EngineState *s, uint handle);
+extern int fgets_wrapper(EngineState *s, char *dest, int maxsize, int handle);
+extern void listSavegames(Common::Array<SavegameDesc> &saves);
+extern int findSavegame(Common::Array<SavegameDesc> &saves, int16 savegameId);
/**
* Writes the cwd to the supplied address and returns the address in acc.
@@ -258,9 +61,6 @@ reg_t kGetCWD(EngineState *s, int argc, reg_t *argv) {
return argv[0];
}
-static void listSavegames(Common::Array<SavegameDesc> &saves);
-static int findSavegame(Common::Array<SavegameDesc> &saves, int16 saveId);
-
enum {
K_DEVICE_INFO_GET_DEVICE = 0,
K_DEVICE_INFO_GET_CURRENT_DEVICE = 1,
@@ -331,7 +131,7 @@ reg_t kDeviceInfo(EngineState *s, int argc, reg_t *argv) {
s->_segMan->strcpy(argv[1], "__throwaway");
debug(3, "K_DEVICE_INFO_GET_SAVEFILE_NAME(%s,%d) -> %s", game_prefix.c_str(), virtualId, "__throwaway");
if ((virtualId < SAVEGAMEID_OFFICIALRANGE_START) || (virtualId > SAVEGAMEID_OFFICIALRANGE_END))
- error("kDeviceInfo(deleteSave): invalid savegame-id specified");
+ error("kDeviceInfo(deleteSave): invalid savegame ID specified");
uint savegameId = virtualId - SAVEGAMEID_OFFICIALRANGE_START;
Common::Array<SavegameDesc> saves;
listSavegames(saves);
@@ -352,18 +152,6 @@ reg_t kDeviceInfo(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
-reg_t kGetSaveDir(EngineState *s, int argc, reg_t *argv) {
-#ifdef ENABLE_SCI32
- // 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();
-}
-
reg_t kCheckFreeSpace(EngineState *s, int argc, reg_t *argv) {
if (argc > 1) {
// SCI1.1/SCI32
@@ -394,354 +182,43 @@ reg_t kCheckFreeSpace(EngineState *s, int argc, reg_t *argv) {
return make_reg(0, 1);
}
-static bool _savegame_sort_byDate(const SavegameDesc &l, const SavegameDesc &r) {
- if (l.date != r.date)
- return (l.date > r.date);
- return (l.time > r.time);
-}
-
-// Create a sorted array containing all found savedgames
-static void listSavegames(Common::Array<SavegameDesc> &saves) {
- Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
-
- // Load all saves
- Common::StringArray saveNames = saveFileMan->listSavefiles(g_sci->getSavegamePattern());
-
- for (Common::StringArray::const_iterator iter = saveNames.begin(); iter != saveNames.end(); ++iter) {
- Common::String filename = *iter;
- Common::SeekableReadStream *in;
- if ((in = saveFileMan->openForLoading(filename))) {
- SavegameMetadata meta;
- if (!get_savegame_metadata(in, &meta) || meta.name.empty()) {
- // invalid
- delete in;
- continue;
- }
- delete in;
-
- SavegameDesc desc;
- desc.id = strtol(filename.end() - 3, NULL, 10);
- desc.date = meta.saveDate;
- // We need to fix date in here, because we save DDMMYYYY instead of
- // YYYYMMDD, so sorting wouldn't work
- desc.date = ((desc.date & 0xFFFF) << 16) | ((desc.date & 0xFF0000) >> 8) | ((desc.date & 0xFF000000) >> 24);
- desc.time = meta.saveTime;
- desc.version = meta.version;
-
- if (meta.name.lastChar() == '\n')
- meta.name.deleteLastChar();
-
- Common::strlcpy(desc.name, meta.name.c_str(), SCI_MAX_SAVENAME_LENGTH);
-
- debug(3, "Savegame in file %s ok, id %d", filename.c_str(), desc.id);
-
- saves.push_back(desc);
- }
- }
-
- // Sort the list by creation date of the saves
- Common::sort(saves.begin(), saves.end(), _savegame_sort_byDate);
-}
-
-// Find a savedgame according to virtualId and return the position within our array
-static int findSavegame(Common::Array<SavegameDesc> &saves, int16 savegameId) {
- for (uint saveNr = 0; saveNr < saves.size(); saveNr++) {
- if (saves[saveNr].id == savegameId)
- return saveNr;
- }
- return -1;
-}
-
-// The scripts get IDs ranging from 100->199, because the scripts require us to assign unique ids THAT EVEN STAY BETWEEN
-// SAVES and the scripts also use "saves-count + 1" to create a new savedgame slot.
-// SCI1.1 actually recycles ids, in that case we will currently get "0".
-// This behavior is required especially for LSL6. In this game, it's possible to quick save. The scripts will use
-// the last-used id for that feature. If we don't assign sticky ids, the feature will overwrite different saves all the
-// time. And sadly we can't just use the actual filename ids directly, because of the creation method for new slots.
-
-bool Console::cmdListSaves(int argc, const char **argv) {
- Common::Array<SavegameDesc> saves;
- listSavegames(saves);
-
- for (uint i = 0; i < saves.size(); i++) {
- Common::String filename = g_sci->getSavegameName(saves[i].id);
- DebugPrintf("%s: '%s'\n", filename.c_str(), saves[i].name);
- }
-
- return true;
-}
-
-reg_t kCheckSaveGame(EngineState *s, int argc, reg_t *argv) {
- Common::String game_id = s->_segMan->getString(argv[0]);
- uint16 virtualId = argv[1].toUint16();
-
- debug(3, "kCheckSaveGame(%s, %d)", game_id.c_str(), virtualId);
-
- Common::Array<SavegameDesc> saves;
- listSavegames(saves);
-
- // we allow 0 (happens in QfG2 when trying to restore from an empty saved game list) and return false in that case
- if (virtualId == 0)
- return NULL_REG;
-
- // Find saved-game
- if ((virtualId < SAVEGAMEID_OFFICIALRANGE_START) || (virtualId > SAVEGAMEID_OFFICIALRANGE_END))
- error("kCheckSaveGame: called with invalid savegameId");
- uint savegameId = virtualId - SAVEGAMEID_OFFICIALRANGE_START;
- int savegameNr = findSavegame(saves, savegameId);
- if (savegameNr == -1)
- return NULL_REG;
-
- // Check for compatible savegame version
- int ver = saves[savegameNr].version;
- if (ver < MINIMUM_SAVEGAME_VERSION || ver > CURRENT_SAVEGAME_VERSION)
- return NULL_REG;
-
- // Otherwise we assume the savegame is OK
- return TRUE_REG;
-}
-
-reg_t kGetSaveFiles(EngineState *s, int argc, reg_t *argv) {
- Common::String game_id = s->_segMan->getString(argv[0]);
-
- debug(3, "kGetSaveFiles(%s)", game_id.c_str());
-
- // Scripts ask for current save files, we can assume that if afterwards they ask us to create a new slot they really
- // mean new slot instead of overwriting the old one
- s->_lastSaveVirtualId = SAVEGAMEID_OFFICIALRANGE_START;
-
- Common::Array<SavegameDesc> saves;
- listSavegames(saves);
- uint totalSaves = MIN<uint>(saves.size(), MAX_SAVEGAME_NR);
-
- reg_t *slot = s->_segMan->derefRegPtr(argv[2], totalSaves);
-
- if (!slot) {
- warning("kGetSaveFiles: %04X:%04X invalid or too small to hold slot data", PRINT_REG(argv[2]));
- totalSaves = 0;
- }
-
- const uint bufSize = (totalSaves * SCI_MAX_SAVENAME_LENGTH) + 1;
- char *saveNames = new char[bufSize];
- char *saveNamePtr = saveNames;
-
- for (uint i = 0; i < totalSaves; i++) {
- *slot++ = make_reg(0, saves[i].id + SAVEGAMEID_OFFICIALRANGE_START); // Store the virtual savegame-id ffs. see above
- strcpy(saveNamePtr, saves[i].name);
- saveNamePtr += SCI_MAX_SAVENAME_LENGTH;
- }
-
- *saveNamePtr = 0; // Terminate list
-
- s->_segMan->memcpy(argv[1], (byte *)saveNames, bufSize);
- delete[] saveNames;
-
- return make_reg(0, totalSaves);
-}
-
-reg_t kSaveGame(EngineState *s, int argc, reg_t *argv) {
- Common::String game_id;
- int16 virtualId = argv[1].toSint16();
- int16 savegameId = -1;
- Common::String game_description;
- Common::String version;
-
- if (argc > 3)
- version = s->_segMan->getString(argv[3]);
-
- // We check here, we don't want to delete a users save in case we are within a kernel function
- if (s->executionStackBase) {
- warning("kSaveGame - won't save from within kernel function");
- return NULL_REG;
- }
-
- if (argv[0].isNull()) {
- // Direct call, from a patched Game::save
- if ((argv[1] != SIGNAL_REG) || (!argv[2].isNull()))
- error("kSaveGame: assumed patched call isn't accurate");
-
- // we are supposed to show a dialog for the user and let him choose where to save
- g_sci->_soundCmd->pauseAll(true); // pause music
- const EnginePlugin *plugin = NULL;
- EngineMan.findGame(g_sci->getGameIdStr(), &plugin);
- GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"));
- dialog->setSaveMode(true);
- savegameId = dialog->runModalWithPluginAndTarget(plugin, ConfMan.getActiveDomainName());
- game_description = dialog->getResultString();
- if (game_description.empty()) {
- // create our own description for the saved game, the user didnt enter it
- #if defined(USE_SAVEGAME_TIMESTAMP)
- TimeDate curTime;
- g_system->getTimeAndDate(curTime);
- curTime.tm_year += 1900; // fixup year
- curTime.tm_mon++; // fixup month
- game_description = Common::String::format("%04d.%02d.%02d / %02d:%02d:%02d", curTime.tm_year, curTime.tm_mon, curTime.tm_mday, curTime.tm_hour, curTime.tm_min, curTime.tm_sec);
- #else
- game_description = Common::String::format("Save %d", savegameId + 1);
- #endif
- }
- delete dialog;
- g_sci->_soundCmd->pauseAll(false); // unpause music ( we can't have it paused during save)
- if (savegameId < 0)
- return NULL_REG;
-
- } else {
- // Real call from script
- game_id = s->_segMan->getString(argv[0]);
- if (argv[2].isNull())
- error("kSaveGame: called with description being NULL");
- game_description = s->_segMan->getString(argv[2]);
-
- debug(3, "kSaveGame(%s,%d,%s,%s)", game_id.c_str(), virtualId, game_description.c_str(), version.c_str());
-
- Common::Array<SavegameDesc> saves;
- listSavegames(saves);
-
- if ((virtualId >= SAVEGAMEID_OFFICIALRANGE_START) && (virtualId <= SAVEGAMEID_OFFICIALRANGE_END)) {
- // savegameId is an actual Id, so search for it just to make sure
- savegameId = virtualId - SAVEGAMEID_OFFICIALRANGE_START;
- if (findSavegame(saves, savegameId) == -1)
- return NULL_REG;
- } else if (virtualId < SAVEGAMEID_OFFICIALRANGE_START) {
- // virtualId is low, we assume that scripts expect us to create new slot
- if (virtualId == s->_lastSaveVirtualId) {
- // if last virtual id is the same as this one, we assume that caller wants to overwrite last save
- savegameId = s->_lastSaveNewId;
- } else {
- uint savegameNr;
- // savegameId is in lower range, scripts expect us to create a new slot
- for (savegameId = 0; savegameId < SAVEGAMEID_OFFICIALRANGE_START; savegameId++) {
- for (savegameNr = 0; savegameNr < saves.size(); savegameNr++) {
- if (savegameId == saves[savegameNr].id)
- break;
- }
- if (savegameNr == saves.size())
- break;
- }
- if (savegameId == SAVEGAMEID_OFFICIALRANGE_START)
- error("kSavegame: no more savegame slots available");
- }
- } else {
- error("kSaveGame: invalid savegameId used");
- }
-
- // Save in case caller wants to overwrite last newly created save
- s->_lastSaveVirtualId = virtualId;
- s->_lastSaveNewId = savegameId;
- }
-
- s->r_acc = NULL_REG;
-
- Common::String filename = g_sci->getSavegameName(savegameId);
- Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
- Common::OutSaveFile *out;
-
- out = saveFileMan->openForSaving(filename);
- if (!out) {
- warning("Error opening savegame \"%s\" for writing", filename.c_str());
- } else {
- if (!gamestate_save(s, out, game_description, version)) {
- warning("Saving the game failed");
- } else {
- s->r_acc = TRUE_REG; // save successful
- }
+reg_t kValidPath(EngineState *s, int argc, reg_t *argv) {
+ Common::String path = s->_segMan->getString(argv[0]);
- out->finalize();
- if (out->err()) {
- warning("Writing the savegame failed");
- s->r_acc = NULL_REG; // write failure
- }
- delete out;
- }
+ debug(3, "kValidPath(%s) -> %d", path.c_str(), s->r_acc.getOffset());
- return s->r_acc;
+ // Always return true
+ return make_reg(0, 1);
}
-reg_t kRestoreGame(EngineState *s, int argc, reg_t *argv) {
- Common::String game_id = !argv[0].isNull() ? s->_segMan->getString(argv[0]) : "";
- int16 savegameId = argv[1].toSint16();
- bool pausedMusic = false;
-
- debug(3, "kRestoreGame(%s,%d)", game_id.c_str(), savegameId);
+#ifdef ENABLE_SCI32
- if (argv[0].isNull()) {
- // Direct call, either from launcher or from a patched Game::restore
- if (savegameId == -1) {
- // we are supposed to show a dialog for the user and let him choose a saved game
- g_sci->_soundCmd->pauseAll(true); // pause music
- const EnginePlugin *plugin = NULL;
- EngineMan.findGame(g_sci->getGameIdStr(), &plugin);
- GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"));
- dialog->setSaveMode(false);
- savegameId = dialog->runModalWithPluginAndTarget(plugin, ConfMan.getActiveDomainName());
- delete dialog;
- if (savegameId < 0) {
- g_sci->_soundCmd->pauseAll(false); // unpause music
- return s->r_acc;
- }
- pausedMusic = true;
- }
- // don't adjust ID of the saved game, it's already correct
- } else {
- if (argv[2].isNull())
- error("kRestoreGame: called with parameter 2 being NULL");
- // Real call from script, we need to adjust ID
- if ((savegameId < SAVEGAMEID_OFFICIALRANGE_START) || (savegameId > SAVEGAMEID_OFFICIALRANGE_END)) {
- warning("Savegame ID %d is not allowed", savegameId);
+reg_t kCD(EngineState *s, int argc, reg_t *argv) {
+ // TODO: Stub
+ switch (argv[0].toUint16()) {
+ case 0:
+ if (argc == 1) {
+ // Check if a disc is in the drive
return TRUE_REG;
- }
- savegameId -= SAVEGAMEID_OFFICIALRANGE_START;
- }
-
- s->r_acc = NULL_REG; // signals success
-
- Common::Array<SavegameDesc> saves;
- listSavegames(saves);
- if (findSavegame(saves, savegameId) == -1) {
- s->r_acc = TRUE_REG;
- warning("Savegame ID %d not found", savegameId);
- } else {
- Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
- Common::String filename = g_sci->getSavegameName(savegameId);
- Common::SeekableReadStream *in;
-
- in = saveFileMan->openForLoading(filename);
- if (in) {
- // found a savegame file
-
- gamestate_restore(s, in);
- delete in;
-
- if (g_sci->getGameId() == GID_MOTHERGOOSE256) {
- // WORKAROUND: Mother Goose SCI1/SCI1.1 does some weird things for
- // saving a previously restored game.
- // We set the current savedgame-id directly and remove the script
- // code concerning this via script patch.
- s->variables[VAR_GLOBAL][0xB3].offset = SAVEGAMEID_OFFICIALRANGE_START + savegameId;
- }
} else {
- s->r_acc = TRUE_REG;
- warning("Savegame #%d not found", savegameId);
+ // Check if the specified disc is in the drive
+ // and return the current disc number. We just
+ // return the requested disc number.
+ return argv[1];
}
+ case 1:
+ // Return the current CD number
+ return make_reg(0, 1);
+ default:
+ warning("CD(%d)", argv[0].toUint16());
}
- if (!s->r_acc.isNull()) {
- // no success?
- if (pausedMusic)
- g_sci->_soundCmd->pauseAll(false); // unpause music
- }
-
- return s->r_acc;
+ return NULL_REG;
}
-reg_t kValidPath(EngineState *s, int argc, reg_t *argv) {
- Common::String path = s->_segMan->getString(argv[0]);
-
- debug(3, "kValidPath(%s) -> %d", path.c_str(), s->r_acc.offset);
+#endif
- // Always return true
- return make_reg(0, 1);
-}
+// ---- FileIO operations -----------------------------------------------------
reg_t kFileIO(EngineState *s, int argc, reg_t *argv) {
if (!s)
@@ -779,6 +256,73 @@ reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) {
}
debugC(kDebugLevelFile, "kFileIO(open): %s, 0x%x", name.c_str(), mode);
+#ifdef ENABLE_SCI32
+ if (name == PHANTASMAGORIA_SAVEGAME_INDEX) {
+ if (s->_virtualIndexFile) {
+ return make_reg(0, VIRTUALFILE_HANDLE);
+ } else {
+ Common::String englishName = g_sci->getSciLanguageString(name, K_LANG_ENGLISH);
+ Common::String wrappedName = g_sci->wrapFilename(englishName);
+ if (!g_sci->getSaveFileManager()->listSavefiles(wrappedName).empty()) {
+ s->_virtualIndexFile = new VirtualIndexFile(wrappedName);
+ return make_reg(0, VIRTUALFILE_HANDLE);
+ }
+ }
+ }
+
+ // Shivers is trying to store savegame descriptions and current spots in
+ // separate .SG files, which are hardcoded in the scripts.
+ // Essentially, there is a normal save file, created by the executable
+ // and an extra hardcoded save file, created by the game scripts, probably
+ // because they didn't want to modify the save/load code to add the extra
+ // information.
+ // Each slot in the book then has two strings, the save description and a
+ // description of the current spot that the player is at. Currently, the
+ // spot strings are always empty (probably related to the unimplemented
+ // kString subop 14, which gets called right before this call).
+ // For now, we don't allow the creation of these files, which means that
+ // all the spot descriptions next to each slot description will be empty
+ // (they are empty anyway). Until a viable solution is found to handle these
+ // extra files and until the spot description strings are initialized
+ // correctly, we resort to virtual files in order to make the load screen
+ // useable. Without this code it is unusable, as the extra information is
+ // always saved to 0.SG for some reason, but on restore the correct file is
+ // used. Perhaps the virtual ID is not taken into account when saving.
+ //
+ // Future TODO: maintain spot descriptions and show them too, ideally without
+ // having to return to this logic of extra hardcoded files.
+ if (g_sci->getGameId() == GID_SHIVERS && name.hasSuffix(".SG")) {
+ if (mode == _K_FILE_MODE_OPEN_OR_CREATE || mode == _K_FILE_MODE_CREATE) {
+ // Game scripts are trying to create a file with the save
+ // description, stop them here
+ debugC(kDebugLevelFile, "Not creating unused file %s", name.c_str());
+ return SIGNAL_REG;
+ } else if (mode == _K_FILE_MODE_OPEN_OR_FAIL) {
+ // Create a virtual file containing the save game description
+ // and slot number, as the game scripts expect.
+ int slotNumber;
+ sscanf(name.c_str(), "%d.SG", &slotNumber);
+
+ Common::Array<SavegameDesc> saves;
+ listSavegames(saves);
+ int savegameNr = findSavegame(saves, slotNumber - SAVEGAMEID_OFFICIALRANGE_START);
+
+ if (!s->_virtualIndexFile) {
+ // Make the virtual file buffer big enough to avoid having it grow dynamically.
+ // 50 bytes should be more than enough.
+ s->_virtualIndexFile = new VirtualIndexFile(50);
+ }
+
+ s->_virtualIndexFile->seek(0, SEEK_SET);
+ s->_virtualIndexFile->write(saves[savegameNr].name, strlen(saves[savegameNr].name));
+ s->_virtualIndexFile->write("\0", 1);
+ s->_virtualIndexFile->write("\0", 1); // Spot description (empty)
+ s->_virtualIndexFile->seek(0, SEEK_SET);
+ return make_reg(0, VIRTUALFILE_HANDLE);
+ }
+ }
+#endif
+
// QFG import rooms get a virtual filelisting instead of an actual one
if (g_sci->inQfGImportRoom()) {
// We need to find out what the user actually selected, "savedHeroes" is
@@ -794,44 +338,82 @@ reg_t kFileIOOpen(EngineState *s, int argc, reg_t *argv) {
reg_t kFileIOClose(EngineState *s, int argc, reg_t *argv) {
debugC(kDebugLevelFile, "kFileIO(close): %d", argv[0].toUint16());
- FileHandle *f = getFileFromHandle(s, argv[0].toUint16());
+ if (argv[0] == SIGNAL_REG)
+ return s->r_acc;
+
+ uint16 handle = argv[0].toUint16();
+
+#ifdef ENABLE_SCI32
+ if (handle == VIRTUALFILE_HANDLE) {
+ s->_virtualIndexFile->close();
+ return SIGNAL_REG;
+ }
+#endif
+
+ FileHandle *f = getFileFromHandle(s, handle);
if (f) {
f->close();
+ if (getSciVersion() <= SCI_VERSION_0_LATE)
+ return s->r_acc; // SCI0 semantics: no value returned
return SIGNAL_REG;
}
+
+ if (getSciVersion() <= SCI_VERSION_0_LATE)
+ return s->r_acc; // SCI0 semantics: no value returned
return NULL_REG;
}
reg_t kFileIOReadRaw(EngineState *s, int argc, reg_t *argv) {
- int handle = argv[0].toUint16();
- int size = argv[2].toUint16();
+ uint16 handle = argv[0].toUint16();
+ uint16 size = argv[2].toUint16();
int bytesRead = 0;
char *buf = new char[size];
debugC(kDebugLevelFile, "kFileIO(readRaw): %d, %d", handle, size);
- FileHandle *f = getFileFromHandle(s, handle);
- if (f) {
- bytesRead = f->_in->read(buf, size);
- s->_segMan->memcpy(argv[1], (const byte*)buf, size);
+#ifdef ENABLE_SCI32
+ if (handle == VIRTUALFILE_HANDLE) {
+ bytesRead = s->_virtualIndexFile->read(buf, size);
+ } else {
+#endif
+ FileHandle *f = getFileFromHandle(s, handle);
+ if (f)
+ bytesRead = f->_in->read(buf, size);
+#ifdef ENABLE_SCI32
}
+#endif
+
+ // TODO: What happens if less bytes are read than what has
+ // been requested? (i.e. if bytesRead is non-zero, but still
+ // less than size)
+ if (bytesRead > 0)
+ s->_segMan->memcpy(argv[1], (const byte*)buf, size);
delete[] buf;
return make_reg(0, bytesRead);
}
reg_t kFileIOWriteRaw(EngineState *s, int argc, reg_t *argv) {
- int handle = argv[0].toUint16();
- int size = argv[2].toUint16();
+ uint16 handle = argv[0].toUint16();
+ uint16 size = argv[2].toUint16();
char *buf = new char[size];
bool success = false;
s->_segMan->memcpy((byte *)buf, argv[1], size);
debugC(kDebugLevelFile, "kFileIO(writeRaw): %d, %d", handle, size);
- FileHandle *f = getFileFromHandle(s, handle);
- if (f) {
- f->_out->write(buf, size);
+#ifdef ENABLE_SCI32
+ if (handle == VIRTUALFILE_HANDLE) {
+ s->_virtualIndexFile->write(buf, size);
success = true;
+ } else {
+#endif
+ FileHandle *f = getFileFromHandle(s, handle);
+ if (f) {
+ f->_out->write(buf, size);
+ success = true;
+ }
+#ifdef ENABLE_SCI32
}
+#endif
delete[] buf;
if (success)
@@ -863,6 +445,20 @@ reg_t kFileIOUnlink(EngineState *s, int argc, reg_t *argv) {
int savedir_nr = saves[slotNum].id;
name = g_sci->getSavegameName(savedir_nr);
result = saveFileMan->removeSavefile(name);
+ } else if (getSciVersion() >= SCI_VERSION_2) {
+ // The file name may be already wrapped, so check both cases
+ result = saveFileMan->removeSavefile(name);
+ if (!result) {
+ const Common::String wrappedName = g_sci->wrapFilename(name);
+ result = saveFileMan->removeSavefile(wrappedName);
+ }
+
+#ifdef ENABLE_SCI32
+ if (name == PHANTASMAGORIA_SAVEGAME_INDEX) {
+ delete s->_virtualIndexFile;
+ s->_virtualIndexFile = 0;
+ }
+#endif
} else {
const Common::String wrappedName = g_sci->wrapFilename(name);
result = saveFileMan->removeSavefile(wrappedName);
@@ -875,15 +471,22 @@ reg_t kFileIOUnlink(EngineState *s, int argc, reg_t *argv) {
}
reg_t kFileIOReadString(EngineState *s, int argc, reg_t *argv) {
- int size = argv[1].toUint16();
- char *buf = new char[size];
- int handle = argv[2].toUint16();
- debugC(kDebugLevelFile, "kFileIO(readString): %d, %d", handle, size);
+ uint16 maxsize = argv[1].toUint16();
+ char *buf = new char[maxsize];
+ uint16 handle = argv[2].toUint16();
+ debugC(kDebugLevelFile, "kFileIO(readString): %d, %d", handle, maxsize);
+ uint32 bytesRead;
+
+#ifdef ENABLE_SCI32
+ if (handle == VIRTUALFILE_HANDLE)
+ bytesRead = s->_virtualIndexFile->readLine(buf, maxsize);
+ else
+#endif
+ bytesRead = fgets_wrapper(s, buf, maxsize, handle);
- int readBytes = fgets_wrapper(s, buf, size, handle);
- s->_segMan->memcpy(argv[0], (const byte*)buf, size);
+ s->_segMan->memcpy(argv[0], (const byte*)buf, maxsize);
delete[] buf;
- return readBytes ? argv[0] : NULL_REG;
+ return bytesRead ? argv[0] : NULL_REG;
}
reg_t kFileIOWriteString(EngineState *s, int argc, reg_t *argv) {
@@ -891,126 +494,55 @@ reg_t kFileIOWriteString(EngineState *s, int argc, reg_t *argv) {
Common::String str = s->_segMan->getString(argv[1]);
debugC(kDebugLevelFile, "kFileIO(writeString): %d", handle);
+#ifdef ENABLE_SCI32
+ if (handle == VIRTUALFILE_HANDLE) {
+ s->_virtualIndexFile->write(str.c_str(), str.size());
+ return NULL_REG;
+ }
+#endif
+
FileHandle *f = getFileFromHandle(s, handle);
if (f) {
f->_out->write(str.c_str(), str.size());
+ if (getSciVersion() <= SCI_VERSION_0_LATE)
+ return s->r_acc; // SCI0 semantics: no value returned
return NULL_REG;
}
+ if (getSciVersion() <= SCI_VERSION_0_LATE)
+ return s->r_acc; // SCI0 semantics: no value returned
return make_reg(0, 6); // DOS - invalid handle
}
reg_t kFileIOSeek(EngineState *s, int argc, reg_t *argv) {
- int handle = argv[0].toUint16();
- int offset = argv[1].toUint16();
- int whence = argv[2].toUint16();
+ uint16 handle = argv[0].toUint16();
+ uint16 offset = ABS<int16>(argv[1].toSint16()); // can be negative
+ uint16 whence = argv[2].toUint16();
debugC(kDebugLevelFile, "kFileIO(seek): %d, %d, %d", handle, offset, whence);
- FileHandle *f = getFileFromHandle(s, handle);
-
- if (f)
- s->r_acc = make_reg(0, f->_in->seek(offset, whence));
-
- return SIGNAL_REG;
-}
-
-void DirSeeker::addAsVirtualFiles(Common::String title, Common::String fileMask) {
- Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
- Common::StringArray foundFiles = saveFileMan->listSavefiles(fileMask);
- if (!foundFiles.empty()) {
- _files.push_back(title);
- _virtualFiles.push_back("");
- Common::StringArray::iterator it;
- Common::StringArray::iterator it_end = foundFiles.end();
-
- for (it = foundFiles.begin(); it != it_end; it++) {
- Common::String regularFilename = *it;
- Common::String wrappedFilename = Common::String(regularFilename.c_str() + fileMask.size() - 1);
-
- Common::SeekableReadStream *testfile = saveFileMan->openForLoading(regularFilename);
- int32 testfileSize = testfile->size();
- delete testfile;
- if (testfileSize > 1024) // check, if larger than 1k. in that case its a saved game.
- continue; // and we dont want to have those in the list
- // We need to remove the prefix for display purposes
- _files.push_back(wrappedFilename);
- // but remember the actual name as well
- _virtualFiles.push_back(regularFilename);
- }
- }
-}
+#ifdef ENABLE_SCI32
+ if (handle == VIRTUALFILE_HANDLE)
+ return make_reg(0, s->_virtualIndexFile->seek(offset, whence));
+#endif
-Common::String DirSeeker::getVirtualFilename(uint fileNumber) {
- if (fileNumber >= _virtualFiles.size())
- error("invalid virtual filename access");
- return _virtualFiles[fileNumber];
-}
+ FileHandle *f = getFileFromHandle(s, handle);
-reg_t DirSeeker::firstFile(const Common::String &mask, reg_t buffer, SegManager *segMan) {
- // Verify that we are given a valid buffer
- if (!buffer.segment) {
- error("DirSeeker::firstFile('%s') invoked with invalid buffer", mask.c_str());
- return NULL_REG;
- }
- _outbuffer = buffer;
- _files.clear();
- _virtualFiles.clear();
-
- int QfGImport = g_sci->inQfGImportRoom();
- if (QfGImport) {
- _files.clear();
- addAsVirtualFiles("-QfG1-", "qfg1-*");
- addAsVirtualFiles("-QfG1VGA-", "qfg1vga-*");
- if (QfGImport > 2)
- addAsVirtualFiles("-QfG2-", "qfg2-*");
- if (QfGImport > 3)
- addAsVirtualFiles("-QfG3-", "qfg3-*");
-
- if (QfGImport == 3) {
- // QfG3 sorts the filelisting itself, we can't let that happen otherwise our
- // virtual list would go out-of-sync
- reg_t savedHeros = segMan->findObjectByName("savedHeros");
- if (!savedHeros.isNull())
- writeSelectorValue(segMan, savedHeros, SELECTOR(sort), 0);
+ if (f && f->_in) {
+ // Backward seeking isn't supported in zip file streams, thus adapt the
+ // parameters accordingly if games ask for such a seek mode. A known
+ // case where this is requested is the save file manager in Phantasmagoria
+ if (whence == SEEK_END) {
+ whence = SEEK_SET;
+ offset = f->_in->size() - offset;
}
- } else {
- // Prefix the mask
- const Common::String wrappedMask = g_sci->wrapFilename(mask);
-
- // Obtain a list of all files matching the given mask
- Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
- _files = saveFileMan->listSavefiles(wrappedMask);
+ return make_reg(0, f->_in->seek(offset, whence));
+ } else if (f && f->_out) {
+ error("kFileIOSeek: Unsupported seek operation on a writeable stream (offset: %d, whence: %d)", offset, whence);
}
- // Reset the list iterator and write the first match to the output buffer,
- // if any.
- _iter = _files.begin();
- return nextFile(segMan);
-}
-
-reg_t DirSeeker::nextFile(SegManager *segMan) {
- if (_iter == _files.end()) {
- return NULL_REG;
- }
-
- Common::String string;
-
- if (_virtualFiles.empty()) {
- // Strip the prefix, if we don't got a virtual filelisting
- const Common::String wrappedString = *_iter;
- string = g_sci->unwrapFilename(wrappedString);
- } else {
- string = *_iter;
- }
- if (string.size() > 12)
- string = Common::String(string.c_str(), 12);
- segMan->strcpy(_outbuffer, string.c_str());
-
- // Return the result and advance the list iterator :)
- ++_iter;
- return _outbuffer;
+ return SIGNAL_REG;
}
reg_t kFileIOFindFirst(EngineState *s, int argc, reg_t *argv) {
@@ -1033,6 +565,14 @@ reg_t kFileIOFindNext(EngineState *s, int argc, reg_t *argv) {
reg_t kFileIOExists(EngineState *s, int argc, reg_t *argv) {
Common::String name = s->_segMan->getString(argv[0]);
+#ifdef ENABLE_SCI32
+ // Cache the file existence result for the Phantasmagoria
+ // save index file, as the game scripts keep checking for
+ // its existence.
+ if (name == PHANTASMAGORIA_SAVEGAME_INDEX && s->_virtualIndexFile)
+ return TRUE_REG;
+#endif
+
bool exists = false;
// Check for regular file
@@ -1084,7 +624,7 @@ reg_t kFileIOExists(EngineState *s, int argc, reg_t *argv) {
// 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.
+ // 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);
@@ -1155,51 +695,328 @@ reg_t kFileIOCreateSaveSlot(EngineState *s, int argc, reg_t *argv) {
return TRUE_REG; // slot creation was successful
}
-reg_t kCD(EngineState *s, int argc, reg_t *argv) {
- // TODO: Stub
- switch (argv[0].toUint16()) {
- case 0:
- // Return whether the contents of disc argv[1] is available.
- return TRUE_REG;
- default:
- warning("CD(%d)", argv[0].toUint16());
+reg_t kFileIOIsValidDirectory(EngineState *s, int argc, reg_t *argv) {
+ // Used in Torin's Passage and LSL7 to determine if the directory passed as
+ // a parameter (usually the save directory) is valid. We always return true
+ // here.
+ return TRUE_REG;
+}
+
+#endif
+
+// ---- Save operations -------------------------------------------------------
+
+#ifdef ENABLE_SCI32
+
+reg_t kSave(EngineState *s, int argc, reg_t *argv) {
+ if (!s)
+ return make_reg(0, getSciVersion());
+ error("not supposed to call this");
+}
+
+#endif
+
+reg_t kSaveGame(EngineState *s, int argc, reg_t *argv) {
+ Common::String game_id;
+ int16 virtualId = argv[1].toSint16();
+ int16 savegameId = -1;
+ Common::String game_description;
+ Common::String version;
+
+ if (argc > 3)
+ version = s->_segMan->getString(argv[3]);
+
+ // We check here, we don't want to delete a users save in case we are within a kernel function
+ if (s->executionStackBase) {
+ warning("kSaveGame - won't save from within kernel function");
+ return NULL_REG;
}
- return NULL_REG;
+ if (argv[0].isNull()) {
+ // Direct call, from a patched Game::save
+ if ((argv[1] != SIGNAL_REG) || (!argv[2].isNull()))
+ error("kSaveGame: assumed patched call isn't accurate");
+
+ // we are supposed to show a dialog for the user and let him choose where to save
+ g_sci->_soundCmd->pauseAll(true); // pause music
+ GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
+ savegameId = dialog->runModalWithCurrentTarget();
+ game_description = dialog->getResultString();
+ if (game_description.empty()) {
+ // create our own description for the saved game, the user didnt enter it
+ game_description = dialog->createDefaultSaveDescription(savegameId);
+ }
+ delete dialog;
+ g_sci->_soundCmd->pauseAll(false); // unpause music ( we can't have it paused during save)
+ if (savegameId < 0)
+ return NULL_REG;
+
+ } else {
+ // Real call from script
+ game_id = s->_segMan->getString(argv[0]);
+ if (argv[2].isNull())
+ error("kSaveGame: called with description being NULL");
+ game_description = s->_segMan->getString(argv[2]);
+
+ debug(3, "kSaveGame(%s,%d,%s,%s)", game_id.c_str(), virtualId, game_description.c_str(), version.c_str());
+
+ Common::Array<SavegameDesc> saves;
+ listSavegames(saves);
+
+ if ((virtualId >= SAVEGAMEID_OFFICIALRANGE_START) && (virtualId <= SAVEGAMEID_OFFICIALRANGE_END)) {
+ // savegameId is an actual Id, so search for it just to make sure
+ savegameId = virtualId - SAVEGAMEID_OFFICIALRANGE_START;
+ if (findSavegame(saves, savegameId) == -1)
+ return NULL_REG;
+ } else if (virtualId < SAVEGAMEID_OFFICIALRANGE_START) {
+ // virtualId is low, we assume that scripts expect us to create new slot
+ if (virtualId == s->_lastSaveVirtualId) {
+ // if last virtual id is the same as this one, we assume that caller wants to overwrite last save
+ savegameId = s->_lastSaveNewId;
+ } else {
+ uint savegameNr;
+ // savegameId is in lower range, scripts expect us to create a new slot
+ for (savegameId = 0; savegameId < SAVEGAMEID_OFFICIALRANGE_START; savegameId++) {
+ for (savegameNr = 0; savegameNr < saves.size(); savegameNr++) {
+ if (savegameId == saves[savegameNr].id)
+ break;
+ }
+ if (savegameNr == saves.size())
+ break;
+ }
+ if (savegameId == SAVEGAMEID_OFFICIALRANGE_START)
+ error("kSavegame: no more savegame slots available");
+ }
+ } else {
+ error("kSaveGame: invalid savegameId used");
+ }
+
+ // Save in case caller wants to overwrite last newly created save
+ s->_lastSaveVirtualId = virtualId;
+ s->_lastSaveNewId = savegameId;
+ }
+
+ s->r_acc = NULL_REG;
+
+ Common::String filename = g_sci->getSavegameName(savegameId);
+ Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
+ Common::OutSaveFile *out;
+
+ out = saveFileMan->openForSaving(filename);
+ if (!out) {
+ warning("Error opening savegame \"%s\" for writing", filename.c_str());
+ } else {
+ if (!gamestate_save(s, out, game_description, version)) {
+ warning("Saving the game failed");
+ } else {
+ s->r_acc = TRUE_REG; // save successful
+ }
+
+ out->finalize();
+ if (out->err()) {
+ warning("Writing the savegame failed");
+ s->r_acc = NULL_REG; // write failure
+ }
+ delete out;
+ }
+
+ return s->r_acc;
}
-reg_t kSave(EngineState *s, int argc, reg_t *argv) {
- switch (argv[0].toUint16()) {
- case 0:
- return kSaveGame(s, argc - 1,argv + 1);
- case 1:
- 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
- // (e.g. "timers").
- // It's used for auto-saving (i.e. save every X minutes, by checking
- // the elapsed time from the timer object)
-
- // This function has to return something other than 0 to proceed
- return s->r_acc;
- default:
- kStub(s, argc, argv);
+reg_t kRestoreGame(EngineState *s, int argc, reg_t *argv) {
+ Common::String game_id = !argv[0].isNull() ? s->_segMan->getString(argv[0]) : "";
+ int16 savegameId = argv[1].toSint16();
+ bool pausedMusic = false;
+
+ debug(3, "kRestoreGame(%s,%d)", game_id.c_str(), savegameId);
+
+ if (argv[0].isNull()) {
+ // Direct call, either from launcher or from a patched Game::restore
+ if (savegameId == -1) {
+ // we are supposed to show a dialog for the user and let him choose a saved game
+ g_sci->_soundCmd->pauseAll(true); // pause music
+ GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
+ savegameId = dialog->runModalWithCurrentTarget();
+ delete dialog;
+ if (savegameId < 0) {
+ g_sci->_soundCmd->pauseAll(false); // unpause music
+ return s->r_acc;
+ }
+ pausedMusic = true;
+ }
+ // don't adjust ID of the saved game, it's already correct
+ } else {
+ if (argv[2].isNull())
+ error("kRestoreGame: called with parameter 2 being NULL");
+ // Real call from script, we need to adjust ID
+ if ((savegameId < SAVEGAMEID_OFFICIALRANGE_START) || (savegameId > SAVEGAMEID_OFFICIALRANGE_END)) {
+ warning("Savegame ID %d is not allowed", savegameId);
+ return TRUE_REG;
+ }
+ savegameId -= SAVEGAMEID_OFFICIALRANGE_START;
+ }
+
+ s->r_acc = NULL_REG; // signals success
+
+ Common::Array<SavegameDesc> saves;
+ listSavegames(saves);
+ if (findSavegame(saves, savegameId) == -1) {
+ s->r_acc = TRUE_REG;
+ warning("Savegame ID %d not found", savegameId);
+ } else {
+ Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
+ Common::String filename = g_sci->getSavegameName(savegameId);
+ Common::SeekableReadStream *in;
+
+ in = saveFileMan->openForLoading(filename);
+ if (in) {
+ // found a savegame file
+
+ gamestate_restore(s, in);
+ delete in;
+
+ if (g_sci->getGameId() == GID_MOTHERGOOSE256) {
+ // WORKAROUND: Mother Goose SCI1/SCI1.1 does some weird things for
+ // saving a previously restored game.
+ // We set the current savedgame-id directly and remove the script
+ // code concerning this via script patch.
+ s->variables[VAR_GLOBAL][0xB3].setOffset(SAVEGAMEID_OFFICIALRANGE_START + savegameId);
+ }
+ } else {
+ s->r_acc = TRUE_REG;
+ warning("Savegame #%d not found", savegameId);
+ }
+ }
+
+ if (!s->r_acc.isNull()) {
+ // no success?
+ if (pausedMusic)
+ g_sci->_soundCmd->pauseAll(false); // unpause music
+ }
+
+ return s->r_acc;
+}
+
+reg_t kGetSaveDir(EngineState *s, int argc, reg_t *argv) {
+#ifdef ENABLE_SCI32
+ // 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();
+}
+
+reg_t kCheckSaveGame(EngineState *s, int argc, reg_t *argv) {
+ Common::String game_id = s->_segMan->getString(argv[0]);
+ uint16 virtualId = argv[1].toUint16();
+
+ debug(3, "kCheckSaveGame(%s, %d)", game_id.c_str(), virtualId);
+
+ Common::Array<SavegameDesc> saves;
+ listSavegames(saves);
+
+ // we allow 0 (happens in QfG2 when trying to restore from an empty saved game list) and return false in that case
+ if (virtualId == 0)
return NULL_REG;
+
+ // Find saved-game
+ if ((virtualId < SAVEGAMEID_OFFICIALRANGE_START) || (virtualId > SAVEGAMEID_OFFICIALRANGE_END))
+ error("kCheckSaveGame: called with invalid savegameId");
+ uint savegameId = virtualId - SAVEGAMEID_OFFICIALRANGE_START;
+ int savegameNr = findSavegame(saves, savegameId);
+ if (savegameNr == -1)
+ return NULL_REG;
+
+ // Check for compatible savegame version
+ int ver = saves[savegameNr].version;
+ if (ver < MINIMUM_SAVEGAME_VERSION || ver > CURRENT_SAVEGAME_VERSION)
+ return NULL_REG;
+
+ // Otherwise we assume the savegame is OK
+ return TRUE_REG;
+}
+
+reg_t kGetSaveFiles(EngineState *s, int argc, reg_t *argv) {
+ Common::String game_id = s->_segMan->getString(argv[0]);
+
+ debug(3, "kGetSaveFiles(%s)", game_id.c_str());
+
+ // Scripts ask for current save files, we can assume that if afterwards they ask us to create a new slot they really
+ // mean new slot instead of overwriting the old one
+ s->_lastSaveVirtualId = SAVEGAMEID_OFFICIALRANGE_START;
+
+ Common::Array<SavegameDesc> saves;
+ listSavegames(saves);
+ uint totalSaves = MIN<uint>(saves.size(), MAX_SAVEGAME_NR);
+
+ reg_t *slot = s->_segMan->derefRegPtr(argv[2], totalSaves);
+
+ if (!slot) {
+ warning("kGetSaveFiles: %04X:%04X invalid or too small to hold slot data", PRINT_REG(argv[2]));
+ totalSaves = 0;
}
+
+ const uint bufSize = (totalSaves * SCI_MAX_SAVENAME_LENGTH) + 1;
+ char *saveNames = new char[bufSize];
+ char *saveNamePtr = saveNames;
+
+ for (uint i = 0; i < totalSaves; i++) {
+ *slot++ = make_reg(0, saves[i].id + SAVEGAMEID_OFFICIALRANGE_START); // Store the virtual savegame ID ffs. see above
+ strcpy(saveNamePtr, saves[i].name);
+ saveNamePtr += SCI_MAX_SAVENAME_LENGTH;
+ }
+
+ *saveNamePtr = 0; // Terminate list
+
+ s->_segMan->memcpy(argv[1], (byte *)saveNames, bufSize);
+ delete[] saveNames;
+
+ return make_reg(0, totalSaves);
+}
+
+#ifdef ENABLE_SCI32
+
+reg_t kMakeSaveCatName(EngineState *s, int argc, reg_t *argv) {
+ // Normally, this creates the name of the save catalogue/directory to save into.
+ // First parameter is the string to save the result into. Second is a string
+ // with game parameters. We don't have a use for this at all, as we have our own
+ // savegame directory management, thus we always return an empty string.
+ return argv[0];
+}
+
+reg_t kMakeSaveFileName(EngineState *s, int argc, reg_t *argv) {
+ // Creates a savegame name from a slot number. Used when deleting saved games.
+ // Param 0: the output buffer (same as in kMakeSaveCatName)
+ // Param 1: a string with game parameters, ignored
+ // Param 2: the selected slot
+
+ SciString *resultString = s->_segMan->lookupString(argv[0]);
+ uint16 virtualId = argv[2].toUint16();
+ if ((virtualId < SAVEGAMEID_OFFICIALRANGE_START) || (virtualId > SAVEGAMEID_OFFICIALRANGE_END))
+ error("kMakeSaveFileName: invalid savegame ID specified");
+ uint saveSlot = virtualId - SAVEGAMEID_OFFICIALRANGE_START;
+
+ Common::Array<SavegameDesc> saves;
+ listSavegames(saves);
+
+ Common::String filename = g_sci->getSavegameName(saveSlot);
+ resultString->fromString(filename);
+
+ return argv[0];
+}
+
+reg_t kAutoSave(EngineState *s, int argc, reg_t *argv) {
+ // TODO
+ // This is a timer callback, with 1 parameter: the timer object
+ // (e.g. "timers").
+ // It's used for auto-saving (i.e. save every X minutes, by checking
+ // the elapsed time from the timer object)
+
+ // This function has to return something other than 0 to proceed
+ return TRUE_REG;
}
#endif
diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp
index caae562d67..da377319c0 100644
--- a/engines/sci/engine/kgraphics.cpp
+++ b/engines/sci/engine/kgraphics.cpp
@@ -25,12 +25,10 @@
#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"
#include "sci/sci.h"
-#include "sci/debug.h" // for g_debug_sleeptime_factor
#include "sci/event.h"
#include "sci/resource.h"
#include "sci/engine/features.h"
@@ -50,10 +48,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"
#endif
namespace Sci {
@@ -342,7 +337,7 @@ reg_t kTextSize(EngineState *s, int argc, reg_t *argv) {
Common::String sep_str;
const char *sep = NULL;
- if ((argc > 4) && (argv[4].segment)) {
+ if ((argc > 4) && (argv[4].getSegment())) {
sep_str = s->_segMan->getString(argv[4]);
sep = sep_str.c_str();
}
@@ -650,6 +645,20 @@ reg_t kPaletteAnimate(EngineState *s, int argc, reg_t *argv) {
if (paletteChanged)
g_sci->_gfxPalette->kernelAnimateSet();
+ // WORKAROUND: The game scripts in SQ4 floppy count the number of elapsed
+ // cycles in the intro from the number of successive kAnimate calls during
+ // the palette cycling effect, while showing the SQ4 logo. This worked in
+ // older computers because each animate call took awhile to complete.
+ // Normally, such scripts are handled automatically by our speed throttler,
+ // however in this case there are no calls to kGameIsRestarting (where the
+ // speed throttler gets called) between the different palette animation calls.
+ // Thus, we add a small delay between each animate call to make the whole
+ // palette animation effect slower and visible, and not have the logo screen
+ // get skipped because the scripts don't wait between animation steps. Fixes
+ // bug #3537232.
+ if (g_sci->getGameId() == GID_SQ4 && !g_sci->isCD() && s->currentRoomNumber() == 1)
+ g_sci->sleep(10);
+
return s->r_acc;
}
@@ -794,7 +803,7 @@ void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) {
Common::Rect rect;
TextAlignment alignment;
int16 mode, maxChars, cursorPos, upperPos, listCount, i;
- int16 upperOffset, cursorOffset;
+ uint16 upperOffset, cursorOffset;
GuiResourceId viewId;
int16 loopNo;
int16 celNo;
@@ -876,7 +885,7 @@ void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) {
listCount = 0; listSeeker = textReference;
while (s->_segMan->strlen(listSeeker) > 0) {
listCount++;
- listSeeker.offset += maxChars;
+ listSeeker.incOffset(maxChars);
}
// TODO: This is rather convoluted... It would be a lot cleaner
@@ -890,11 +899,11 @@ void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) {
for (i = 0; i < listCount; i++) {
listStrings[i] = s->_segMan->getString(listSeeker);
listEntries[i] = listStrings[i].c_str();
- if (listSeeker.offset == upperOffset)
+ if (listSeeker.getOffset() == upperOffset)
upperPos = i;
- if (listSeeker.offset == cursorOffset)
+ if (listSeeker.getOffset() == cursorOffset)
cursorPos = i;
- listSeeker.offset += maxChars;
+ listSeeker.incOffset(maxChars);
}
}
@@ -941,8 +950,9 @@ reg_t kDrawControl(EngineState *s, int argc, reg_t *argv) {
}
}
if (objName == "savedHeros") {
- // Import of QfG character files dialog is shown
- // display additional popup information before letting user use it
+ // Import of QfG character files dialog is shown.
+ // Display additional popup information before letting user use it.
+ // For the SCI32 version of this, check kernelAddPlane().
reg_t changeDirButton = s->_segMan->findObjectByName("changeDirItem");
if (!changeDirButton.isNull()) {
// check if checkDirButton is still enabled, in that case we are called the first time during that room
@@ -955,6 +965,8 @@ reg_t kDrawControl(EngineState *s, int argc, reg_t *argv) {
"for Quest for Glory 2. Example: 'qfg2-thief.sav'.");
}
}
+
+ // For the SCI32 version of this, check kListAt().
s->_chosenQfGImportItem = readSelectorValue(s->_segMan, controlObject, SELECTOR(mark));
}
@@ -1109,7 +1121,7 @@ reg_t kNewWindow(EngineState *s, int argc, reg_t *argv) {
rect2 = Common::Rect (argv[5].toSint16(), argv[4].toSint16(), argv[7].toSint16(), argv[6].toSint16());
Common::String title;
- if (argv[4 + argextra].segment) {
+ if (argv[4 + argextra].getSegment()) {
title = s->_segMan->getString(argv[4 + argextra]);
title = g_sci->strSplit(title.c_str(), NULL);
}
@@ -1148,7 +1160,7 @@ reg_t kDisplay(EngineState *s, int argc, reg_t *argv) {
Common::String text;
- if (textp.segment) {
+ if (textp.getSegment()) {
argc--; argv++;
text = s->_segMan->getString(textp);
} else {
@@ -1209,688 +1221,33 @@ reg_t kShow(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
+// Early variant of the SCI32 kRemapColors kernel function, used in the demo of QFG4
reg_t kRemapColors(EngineState *s, int argc, reg_t *argv) {
uint16 operation = argv[0].toUint16();
switch (operation) {
- case 0: { // Set remapping to base. 0 turns remapping off.
- int16 base = (argc >= 2) ? argv[1].toSint16() : 0;
- 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
- // The demo of QFG4 calls this with 1+3 parameters, thus there are differences here
- //int16 unk1 = argv[1].toSint16();
- //int16 unk2 = argv[2].toSint16();
- //int16 unk3 = argv[3].toSint16();
- //uint16 unk4 = argv[4].toUint16();
- //uint16 unk5 = (argc >= 6) ? argv[5].toUint16() : 0;
- kStub(s, argc, argv);
- }
- break;
- case 2: { // remap by percent
- // This adjusts the alpha value of a specific color, and it operates on
- // an RGBA palette. Since we're operating on an RGB palette, we just
- // modify the color intensity instead
- // TODO: From what I understand, palette remapping should be placed
- // separately, so that it can be reset by case 0 above. Thus, we
- // should adjust the functionality of the Palette class accordingly.
- int16 color = argv[1].toSint16();
- if (color >= 10)
- color -= 10;
- uint16 percent = argv[2].toUint16(); // 0 - 100
- if (argc >= 4)
- warning("RemapByPercent called with 4 parameters, unknown parameter is %d", argv[3].toUint16());
- g_sci->_gfxPalette->kernelSetIntensity(color, 255, percent, false);
- }
- break;
- case 3: { // remap to gray
- // NOTE: This adjusts the alpha value of a specific color, and it operates on
- // an RGBA palette
- int16 color = argv[1].toSint16(); // this is subtracted from a maximum color value, and can be offset by 10
- int16 percent = argv[2].toSint16(); // 0 - 100
- uint16 unk3 = (argc >= 4) ? argv[3].toUint16() : 0;
- warning("kRemapColors: RemapToGray color %d by %d percent (unk3 = %d)", color, percent, unk3);
- }
- break;
- case 4: { // unknown
- //int16 unk1 = argv[1].toSint16();
- //uint16 unk2 = argv[2].toUint16();
- //uint16 unk3 = argv[3].toUint16();
- //uint16 unk4 = (argc >= 5) ? argv[4].toUint16() : 0;
- kStub(s, argc, argv);
- }
- break;
- case 5: { // increment color
- //int16 unk1 = argv[1].toSint16();
- //uint16 unk2 = argv[2].toUint16();
- kStub(s, argc, argv);
- }
- break;
- default:
- break;
- }
-
- return s->r_acc;
-}
-
-#ifdef ENABLE_SCI32
-
-reg_t kIsHiRes(EngineState *s, int argc, reg_t *argv) {
- // Returns 0 if the screen width or height is less than 640 or 400,
- // respectively.
- if (g_system->getWidth() < 640 || g_system->getHeight() < 400)
- return make_reg(0, 0);
-
- return make_reg(0, 1);
-}
-
-// SCI32 variant, can't work like sci16 variants
-reg_t kCantBeHere32(EngineState *s, int argc, reg_t *argv) {
- // TODO
-// reg_t curObject = argv[0];
-// reg_t listReference = (argc > 1) ? argv[1] : NULL_REG;
-
- return NULL_REG;
-}
-
-reg_t kAddScreenItem(EngineState *s, int argc, reg_t *argv) {
- 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;
-}
-
-reg_t kUpdateScreenItem(EngineState *s, int argc, reg_t *argv) {
- g_sci->_gfxFrameout->kernelUpdateScreenItem(argv[0]);
- return s->r_acc;
-}
-
-reg_t kDeleteScreenItem(EngineState *s, int argc, reg_t *argv) {
- g_sci->_gfxFrameout->kernelDeleteScreenItem(argv[0]);
- return s->r_acc;
-}
-
-reg_t kAddPlane(EngineState *s, int argc, reg_t *argv) {
- g_sci->_gfxFrameout->kernelAddPlane(argv[0]);
- return s->r_acc;
-}
-
-reg_t kDeletePlane(EngineState *s, int argc, reg_t *argv) {
- g_sci->_gfxFrameout->kernelDeletePlane(argv[0]);
- return s->r_acc;
-}
-
-reg_t kUpdatePlane(EngineState *s, int argc, reg_t *argv) {
- g_sci->_gfxFrameout->kernelUpdatePlane(argv[0]);
- return s->r_acc;
-}
-
-reg_t kAddPicAt(EngineState *s, int argc, reg_t *argv) {
- reg_t planeObj = argv[0];
- GuiResourceId pictureId = argv[1].toUint16();
- int16 pictureX = argv[2].toSint16();
- int16 pictureY = argv[3].toSint16();
-
- g_sci->_gfxFrameout->kernelAddPicAt(planeObj, pictureId, pictureX, pictureY);
- return s->r_acc;
-}
-
-reg_t kGetHighPlanePri(EngineState *s, int argc, reg_t *argv) {
- return make_reg(0, g_sci->_gfxFrameout->kernelGetHighPlanePri());
-}
-
-reg_t kFrameOut(EngineState *s, int argc, reg_t *argv) {
- g_sci->_gfxFrameout->kernelFrameout();
- return NULL_REG;
-}
-
-reg_t kObjectIntersect(EngineState *s, int argc, reg_t *argv) {
- Common::Rect objRect1 = g_sci->_gfxCompare->getNSRect(argv[0]);
- Common::Rect objRect2 = g_sci->_gfxCompare->getNSRect(argv[1]);
- return make_reg(0, objRect1.intersects(objRect2));
-}
-
-// Tests if the coordinate is on the passed object
-reg_t kIsOnMe(EngineState *s, int argc, reg_t *argv) {
- uint16 x = argv[0].toUint16();
- uint16 y = argv[1].toUint16();
- reg_t targetObject = argv[2];
- uint16 illegalBits = argv[3].offset;
- Common::Rect nsRect = g_sci->_gfxCompare->getNSRect(targetObject, true);
-
- // we assume that x, y are local coordinates
-
- 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
- // although illegalBits may get differently set, don't know yet how this really works out
- uint16 viewId = readSelectorValue(s->_segMan, targetObject, SELECTOR(view));
- int16 loopNo = readSelectorValue(s->_segMan, targetObject, SELECTOR(loop));
- int16 celNo = readSelectorValue(s->_segMan, targetObject, SELECTOR(cel));
- if (g_sci->_gfxCompare->kernelIsItSkip(viewId, loopNo, celNo, Common::Point(x - nsRect.left, y - nsRect.top)))
- contained = false;
- }
- return make_reg(0, contained);
-}
-
-reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv) {
- switch (argv[0].toUint16()) {
- case 0: {
- if (argc != 4) {
- warning("kCreateTextBitmap(0): expected 4 arguments, got %i", argc);
- return NULL_REG;
- }
- reg_t object = argv[3];
- Common::String text = s->_segMan->getString(readSelector(s->_segMan, object, SELECTOR(text)));
- debugC(kDebugLevelStrings, "kCreateTextBitmap case 0 (%04x:%04x, %04x:%04x, %04x:%04x)",
- PRINT_REG(argv[1]), PRINT_REG(argv[2]), PRINT_REG(argv[3]));
- debugC(kDebugLevelStrings, "%s", text.c_str());
- uint16 maxWidth = argv[1].toUint16(); // nsRight - nsLeft + 1
- uint16 maxHeight = argv[2].toUint16(); // nsBottom - nsTop + 1
- return g_sci->_gfxText32->createTextBitmap(object, maxWidth, maxHeight);
- }
- case 1: {
- if (argc != 2) {
- warning("kCreateTextBitmap(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());
- return g_sci->_gfxText32->createTextBitmap(object);
- }
- default:
- warning("CreateTextBitmap(%d)", argv[0].toUint16());
- return NULL_REG;
- }
-}
-
-reg_t kDisposeTextBitmap(EngineState *s, int argc, reg_t *argv) {
- g_sci->_gfxText32->disposeTextBitmap(argv[0]);
- return s->r_acc;
-}
-
-reg_t kGetWindowsOption(EngineState *s, int argc, reg_t *argv) {
- uint16 windowsOption = argv[0].toUint16();
- switch (windowsOption) {
- case 0:
- // Title bar on/off in Phantasmagoria, we return 0 (off)
- return NULL_REG;
- default:
- warning("GetWindowsOption: Unknown option %d", windowsOption);
- return NULL_REG;
- }
-}
-
-reg_t kWinHelp(EngineState *s, int argc, reg_t *argv) {
- switch (argv[0].toUint16()) {
- case 1:
- // Load a help file
- // Maybe in the future we can implement this, but for now this message should suffice
- showScummVMDialog("Please use an external viewer to open the game's help file: " + s->_segMan->getString(argv[1]));
- break;
- case 2:
- // Looks like some init function
- break;
- default:
- warning("Unknown kWinHelp subop %d", argv[0].toUint16());
- }
-
- 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.
- */
-reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) {
- // Can be called with 7 or 8 parameters
- // 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]; // the affected plane
- 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;
- if (hasFadeArray) {
- // argv[7]
- divisions = (argc >= 9) ? argv[8].toSint16() : -1; // divisions (transition steps?)
- } else {
- 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
-
- 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;
-}
-
-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);
+ case 0: { // remap by percent
+ uint16 percent = argv[1].toUint16();
+ g_sci->_gfxPalette->resetRemapping();
+ g_sci->_gfxPalette->setRemappingPercent(254, percent);
}
- 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.
-
- // TODO: This is all a stub/skeleton, thus we're invoking kStub() for now
- kStub(s, argc, argv);
-
- switch (argv[0].toUint16()) {
- case 0: // Init
- // 2 parameters
- // argv[1] points to the scroll object (e.g. textScroller in SQ6)
- // argv[2] is an integer (e.g. 0x32)
- break;
- case 1: // Show message
- // 5 or 6 parameters
- // Seems to be called with 5 parameters when the narrator speaks, and
- // with 6 when Roger speaks
- // argv[1] unknown (usually 0)
- // argv[2] the text to show
- // argv[3] a small integer (e.g. 0x32)
- // argv[4] a small integer (e.g. 0x54)
- // argv[5] optional, unknown (usually 0)
- warning("kScrollWindow: '%s'", s->_segMan->getString(argv[2]).c_str());
- break;
- case 2: // Clear
- // 2 parameters
- // TODO
- break;
- case 3: // Page up
- // 2 parameters
- // TODO
- break;
- case 4: // Page down
- // 2 parameters
- // TODO
- break;
- case 5: // Up arrow
- // 2 parameters
- // TODO
- break;
- case 6: // Down arrow
- // 2 parameters
- // TODO
- break;
- case 7: // Home
- // 2 parameters
- // TODO
- break;
- case 8: // End
- // 2 parameters
- // TODO
- break;
- case 9: // Resize
- // 3 parameters
- // TODO
- break;
- case 10: // Where
- // 3 parameters
- // TODO
- break;
- case 11: // Go
- // 4 parameters
- // TODO
- break;
- case 12: // Insert
- // 7 parameters
- // TODO
break;
- case 13: // Delete
- // 3 parameters
- // TODO
- break;
- case 14: // Modify
- // 7 or 8 parameters
- // TODO
- break;
- case 15: // Hide
- // 2 parameters
- // TODO
- break;
- case 16: // Show
- // 2 parameters
- // TODO
- break;
- case 17: // Destroy
- // 2 parameters
- // TODO
- break;
- case 18: // Text
- // 2 parameters
- // TODO
- break;
- case 19: // Reconstruct
- // 3 parameters
- // TODO
- break;
- default:
- error("kScrollWindow: unknown subop %d", argv[0].toUint16());
- break;
- }
-
- return s->r_acc;
-}
-
-reg_t kSetFontRes(EngineState *s, int argc, reg_t *argv) {
- // TODO: This defines the resolution that the fonts are supposed to be displayed
- // in. Currently, this is only used for showing high-res fonts in GK1 Mac, but
- // should be extended to handle other font resolutions such as those
-
- int xResolution = argv[0].toUint16();
- //int yResolution = argv[1].toUint16();
-
- g_sci->_gfxScreen->setFontIsUpscaled(xResolution == 640 &&
- g_sci->_gfxScreen->getUpscaledHires() != GFX_SCREEN_UPSCALED_DISABLED);
-
- return s->r_acc;
-}
-
-reg_t kFont(EngineState *s, int argc, reg_t *argv) {
- // Handle font settings for SCI2.1
-
- switch (argv[0].toUint16()) {
- case 1:
- // Set font resolution
- return kSetFontRes(s, argc - 1, argv + 1);
- default:
- warning("kFont: unknown subop %d", argv[0].toUint16());
- }
-
- return s->r_acc;
-}
-
-// TODO: Eventually, all of the kBitmap operations should be put
-// in a separate class
-
-#define BITMAP_HEADER_SIZE 46
-
-reg_t kBitmap(EngineState *s, int argc, reg_t *argv) {
- // Used for bitmap operations in SCI2.1 and SCI3.
- // This is the SCI2.1 version, the functionality seems to have changed in SCI3.
-
- switch (argv[0].toUint16()) {
- case 0: // init bitmap surface
- {
- // 6 params, called e.g. from TextView::init() in Torin's Passage,
- // script 64890 and TransView::init() in script 64884
- uint16 width = argv[1].toUint16();
- uint16 height = argv[2].toUint16();
- //uint16 skip = argv[3].toUint16();
- uint16 back = argv[4].toUint16(); // usually equals skip
- //uint16 width2 = (argc >= 6) ? argv[5].toUint16() : 0;
- //uint16 height2 = (argc >= 7) ? argv[6].toUint16() : 0;
- //uint16 transparentFlag = (argc >= 8) ? argv[7].toUint16() : 0;
-
- // TODO: skip, width2, height2, transparentFlag
- // (used for transparent bitmaps)
- int entrySize = width * height + BITMAP_HEADER_SIZE;
- reg_t memoryId = s->_segMan->allocateHunkEntry("Bitmap()", entrySize);
- byte *memoryPtr = s->_segMan->getHunkPointer(memoryId);
- memset(memoryPtr, 0, BITMAP_HEADER_SIZE); // zero out the bitmap header
- memset(memoryPtr + BITMAP_HEADER_SIZE, back, width * height);
- // Save totalWidth, totalHeight
- // TODO: Save the whole bitmap header, like SSCI does
- WRITE_LE_UINT16(memoryPtr, width);
- WRITE_LE_UINT16(memoryPtr + 2, height);
- return memoryId;
+ case 1: { // remap by range
+ uint16 from = argv[1].toUint16();
+ uint16 to = argv[2].toUint16();
+ uint16 base = argv[3].toUint16();
+ g_sci->_gfxPalette->resetRemapping();
+ g_sci->_gfxPalette->setRemappingRange(254, from, to, base);
}
break;
- case 1: // dispose text bitmap surface
- return kDisposeTextBitmap(s, argc - 1, argv + 1);
- case 2: // dispose bitmap surface, with extra param
- // 2 params, called e.g. from MenuItem::dispose in Torin's Passage,
- // script 64893
- warning("kBitmap(2), unk1 %d, bitmap ptr %04x:%04x", argv[1].toUint16(), PRINT_REG(argv[2]));
- break;
- case 3: // tiled surface
- {
- // 6 params, called e.g. from TiledBitmap::resize() in Torin's Passage,
- // script 64869
- reg_t hunkId = argv[1]; // obtained from kBitmap(0)
- // The tiled view seems to always have 2 loops.
- // These loops need to have 1 cel in loop 0 and 8 cels in loop 1.
- uint16 viewNum = argv[2].toUint16(); // vTiles selector
- uint16 loop = argv[3].toUint16();
- uint16 cel = argv[4].toUint16();
- uint16 x = argv[5].toUint16();
- uint16 y = argv[6].toUint16();
-
- byte *memoryPtr = s->_segMan->getHunkPointer(hunkId);
- // Get totalWidth, totalHeight
- uint16 totalWidth = READ_LE_UINT16(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: // add text to bitmap
- {
- // 13 params, called e.g. from TextButton::createBitmap() in Torin's Passage,
- // script 64894
- reg_t hunkId = argv[1]; // obtained from kBitmap(0)
- Common::String text = s->_segMan->getString(argv[2]);
- uint16 textX = argv[3].toUint16();
- uint16 textY = argv[4].toUint16();
- //reg_t unk5 = argv[5];
- //reg_t unk6 = argv[6];
- //reg_t unk7 = argv[7]; // skip?
- //reg_t unk8 = argv[8]; // back?
- //reg_t unk9 = argv[9];
- uint16 fontId = argv[10].toUint16();
- //uint16 mode = argv[11].toUint16();
- uint16 dimmed = argv[12].toUint16();
- //warning("kBitmap(4): bitmap ptr %04x:%04x, font %d, mode %d, dimmed %d - text: \"%s\"",
- // PRINT_REG(bitmapPtr), font, mode, dimmed, text.c_str());
- uint16 foreColor = 255; // TODO
-
- byte *memoryPtr = s->_segMan->getHunkPointer(hunkId);
- // Get totalWidth, totalHeight
- uint16 totalWidth = READ_LE_UINT16(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: // fill with color
- {
- // 6 params, called e.g. from TextView::init() and TextView::draw()
- // in Torin's Passage, script 64890
- reg_t hunkId = argv[1]; // obtained from kBitmap(0)
- uint16 x = argv[2].toUint16();
- uint16 y = argv[3].toUint16();
- uint16 fillWidth = argv[4].toUint16(); // width - 1
- uint16 fillHeight = argv[5].toUint16(); // height - 1
- uint16 back = argv[6].toUint16();
-
- byte *memoryPtr = s->_segMan->getHunkPointer(hunkId);
- // Get totalWidth, totalHeight
- uint16 totalWidth = READ_LE_UINT16(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;
- }
- }
-
- }
+ case 2: // turn remapping off (unused)
+ error("Unused subop kRemapColors(2) has been called");
break;
default:
- kStub(s, argc, argv);
break;
}
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/kgraphics32.cpp b/engines/sci/engine/kgraphics32.cpp
new file mode 100644
index 0000000000..8b3afeef99
--- /dev/null
+++ b/engines/sci/engine/kgraphics32.cpp
@@ -0,0 +1,812 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/system.h"
+
+#include "engines/util.h"
+#include "graphics/cursorman.h"
+#include "graphics/surface.h"
+
+#include "gui/message.h"
+
+#include "sci/sci.h"
+#include "sci/event.h"
+#include "sci/resource.h"
+#include "sci/engine/features.h"
+#include "sci/engine/state.h"
+#include "sci/engine/selector.h"
+#include "sci/engine/kernel.h"
+#include "sci/graphics/animate.h"
+#include "sci/graphics/cache.h"
+#include "sci/graphics/compare.h"
+#include "sci/graphics/controls16.h"
+#include "sci/graphics/coordadjuster.h"
+#include "sci/graphics/cursor.h"
+#include "sci/graphics/palette.h"
+#include "sci/graphics/paint16.h"
+#include "sci/graphics/picture.h"
+#include "sci/graphics/ports.h"
+#include "sci/graphics/screen.h"
+#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"
+#endif
+
+namespace Sci {
+#ifdef ENABLE_SCI32
+
+extern void showScummVMDialog(const Common::String &message);
+
+reg_t kIsHiRes(EngineState *s, int argc, reg_t *argv) {
+ // Returns 0 if the screen width or height is less than 640 or 400,
+ // respectively.
+ if (g_system->getWidth() < 640 || g_system->getHeight() < 400)
+ return make_reg(0, 0);
+
+ return make_reg(0, 1);
+}
+
+// SCI32 variant, can't work like sci16 variants
+reg_t kCantBeHere32(EngineState *s, int argc, reg_t *argv) {
+ // TODO
+// reg_t curObject = argv[0];
+// reg_t listReference = (argc > 1) ? argv[1] : NULL_REG;
+
+ return NULL_REG;
+}
+
+reg_t kAddScreenItem(EngineState *s, int argc, reg_t *argv) {
+ 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;
+}
+
+reg_t kUpdateScreenItem(EngineState *s, int argc, reg_t *argv) {
+ g_sci->_gfxFrameout->kernelUpdateScreenItem(argv[0]);
+ return s->r_acc;
+}
+
+reg_t kDeleteScreenItem(EngineState *s, int argc, reg_t *argv) {
+ g_sci->_gfxFrameout->kernelDeleteScreenItem(argv[0]);
+ return s->r_acc;
+}
+
+reg_t kAddPlane(EngineState *s, int argc, reg_t *argv) {
+ g_sci->_gfxFrameout->kernelAddPlane(argv[0]);
+ return s->r_acc;
+}
+
+reg_t kDeletePlane(EngineState *s, int argc, reg_t *argv) {
+ g_sci->_gfxFrameout->kernelDeletePlane(argv[0]);
+ return s->r_acc;
+}
+
+reg_t kUpdatePlane(EngineState *s, int argc, reg_t *argv) {
+ g_sci->_gfxFrameout->kernelUpdatePlane(argv[0]);
+ return s->r_acc;
+}
+
+reg_t kAddPicAt(EngineState *s, int argc, reg_t *argv) {
+ reg_t planeObj = argv[0];
+ GuiResourceId pictureId = argv[1].toUint16();
+ int16 pictureX = argv[2].toSint16();
+ int16 pictureY = argv[3].toSint16();
+
+ g_sci->_gfxFrameout->kernelAddPicAt(planeObj, pictureId, pictureX, pictureY);
+ return s->r_acc;
+}
+
+reg_t kGetHighPlanePri(EngineState *s, int argc, reg_t *argv) {
+ return make_reg(0, g_sci->_gfxFrameout->kernelGetHighPlanePri());
+}
+
+reg_t kFrameOut(EngineState *s, int argc, reg_t *argv) {
+ g_sci->_gfxFrameout->kernelFrameout();
+ return NULL_REG;
+}
+
+reg_t kObjectIntersect(EngineState *s, int argc, reg_t *argv) {
+ Common::Rect objRect1 = g_sci->_gfxCompare->getNSRect(argv[0]);
+ Common::Rect objRect2 = g_sci->_gfxCompare->getNSRect(argv[1]);
+ return make_reg(0, objRect1.intersects(objRect2));
+}
+
+// Tests if the coordinate is on the passed object
+reg_t kIsOnMe(EngineState *s, int argc, reg_t *argv) {
+ uint16 x = argv[0].toUint16();
+ uint16 y = argv[1].toUint16();
+ reg_t targetObject = argv[2];
+ uint16 illegalBits = argv[3].getOffset();
+ Common::Rect nsRect = g_sci->_gfxCompare->getNSRect(targetObject, true);
+
+ // we assume that x, y are local coordinates
+
+ 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
+ // although illegalBits may get differently set, don't know yet how this really works out
+ uint16 viewId = readSelectorValue(s->_segMan, targetObject, SELECTOR(view));
+ int16 loopNo = readSelectorValue(s->_segMan, targetObject, SELECTOR(loop));
+ int16 celNo = readSelectorValue(s->_segMan, targetObject, SELECTOR(cel));
+ if (g_sci->_gfxCompare->kernelIsItSkip(viewId, loopNo, celNo, Common::Point(x - nsRect.left, y - nsRect.top)))
+ contained = false;
+ }
+ return make_reg(0, contained);
+}
+
+reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv) {
+ switch (argv[0].toUint16()) {
+ case 0: {
+ if (argc != 4) {
+ warning("kCreateTextBitmap(0): expected 4 arguments, got %i", argc);
+ return NULL_REG;
+ }
+ reg_t object = argv[3];
+ Common::String text = s->_segMan->getString(readSelector(s->_segMan, object, SELECTOR(text)));
+ 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());
+ int16 maxWidth = argv[1].toUint16();
+ int16 maxHeight = argv[2].toUint16();
+ g_sci->_gfxCoordAdjuster->fromScriptToDisplay(maxHeight, maxWidth);
+ // These values can be larger than the screen in the SQ6 demo, room 100
+ // TODO: Find out why. For now, don't show any text in that room.
+ if (g_sci->getGameId() == GID_SQ6 && g_sci->isDemo() && s->currentRoomNumber() == 100)
+ return NULL_REG;
+ return g_sci->_gfxText32->createTextBitmap(object, maxWidth, maxHeight);
+ }
+ case 1: {
+ if (argc != 2) {
+ 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());
+ return g_sci->_gfxText32->createTextBitmap(object);
+ }
+ default:
+ warning("CreateTextBitmap(%d)", argv[0].toUint16());
+ return NULL_REG;
+ }
+}
+
+reg_t kDisposeTextBitmap(EngineState *s, int argc, reg_t *argv) {
+ g_sci->_gfxText32->disposeTextBitmap(argv[0]);
+ return s->r_acc;
+}
+
+reg_t kWinHelp(EngineState *s, int argc, reg_t *argv) {
+ switch (argv[0].toUint16()) {
+ case 1:
+ // Load a help file
+ // Maybe in the future we can implement this, but for now this message should suffice
+ showScummVMDialog("Please use an external viewer to open the game's help file: " + s->_segMan->getString(argv[1]));
+ break;
+ case 2:
+ // Looks like some init function
+ break;
+ default:
+ warning("Unknown kWinHelp subop %d", argv[0].toUint16());
+ }
+
+ return s->r_acc;
+}
+
+/**
+ * Used for scene transitions, replacing (but reusing parts of) the old
+ * transition code.
+ */
+reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) {
+ // Can be called with 7 or 8 parameters
+ // 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]; // the affected plane
+ Common::String planeObjName = s->_segMan->getObjectName(planeObj);
+ 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;
+ if (hasFadeArray) {
+ // argv[7]
+ divisions = (argc >= 9) ? argv[8].toSint16() : -1; // divisions (transition steps?)
+ } else {
+ 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;
+ }
+
+ // GK1 calls fadeout (13) / fadein (14) with the following parameters:
+ // seconds: 1
+ // backColor: 0 / -1
+ // fade: 200
+ // animate: 0
+ // refFrame: 0
+ // divisions: 0 / 20
+
+ // TODO: Check if the plane is in the list of planes to draw
+
+ Common::String effectName = "unknown";
+
+ switch (showStyle) {
+ case 0: // no transition / show
+ effectName = "show";
+ break;
+ case 13: // fade out
+ effectName = "fade out";
+ // TODO
+ break;
+ case 14: // fade in
+ effectName = "fade in";
+ // TODO
+ break;
+ default:
+ // TODO
+ break;
+ }
+
+ warning("kSetShowStyle: effect %d (%s) - plane: %04x:%04x (%s), sec: %d, "
+ "back: %d, prio: %d, animate: %d, ref frame: %d, divisions: %d",
+ showStyle, effectName.c_str(), PRINT_REG(planeObj), planeObjName.c_str(),
+ seconds, backColor, priority, animate, refFrame, divisions);
+ 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 SQ6 and LSL6 hires for the text area in the bottom of the
+ // screen. The relevant scripts also exist in Phantasmagoria 1, but they're
+ // unused. This is always called by scripts 64906 (ScrollerWindow) and
+ // 64907 (ScrollableWindow).
+
+ reg_t kWindow = argv[1];
+ uint16 op = argv[0].toUint16();
+ switch (op) {
+ case 0: // Init
+ g_sci->_gfxFrameout->initScrollText(argv[2].toUint16()); // maxItems
+ g_sci->_gfxFrameout->clearScrollTexts();
+ return argv[1]; // kWindow
+ case 1: // Show message, called by ScrollableWindow::addString
+ case 14: // Modify message, called by ScrollableWindow::modifyString
+ // 5 or 6 parameters
+ // Seems to be called with 5 parameters when the narrator speaks, and
+ // with 6 when Roger speaks
+ {
+ Common::String text = s->_segMan->getString(argv[2]);
+ uint16 x = 0;//argv[3].toUint16(); // TODO: can't be x (values are all wrong)
+ uint16 y = 0;//argv[4].toUint16(); // TODO: can't be y (values are all wrong)
+ // TODO: argv[5] is an optional unknown parameter (an integer set to 0)
+ g_sci->_gfxFrameout->addScrollTextEntry(text, kWindow, x, y, (op == 14));
+ }
+ break;
+ case 2: // Clear, called by ScrollableWindow::erase
+ g_sci->_gfxFrameout->clearScrollTexts();
+ break;
+ case 3: // Page up, called by ScrollableWindow::scrollTo
+ // TODO
+ kStub(s, argc, argv);
+ break;
+ case 4: // Page down, called by ScrollableWindow::scrollTo
+ // TODO
+ kStub(s, argc, argv);
+ break;
+ case 5: // Up arrow, called by ScrollableWindow::scrollTo
+ g_sci->_gfxFrameout->prevScrollText();
+ break;
+ case 6: // Down arrow, called by ScrollableWindow::scrollTo
+ g_sci->_gfxFrameout->nextScrollText();
+ break;
+ case 7: // Home, called by ScrollableWindow::scrollTo
+ g_sci->_gfxFrameout->firstScrollText();
+ break;
+ case 8: // End, called by ScrollableWindow::scrollTo
+ g_sci->_gfxFrameout->lastScrollText();
+ break;
+ case 9: // Resize, called by ScrollableWindow::resize and ScrollerWindow::resize
+ // TODO
+ kStub(s, argc, argv);
+ break;
+ case 10: // Where, called by ScrollableWindow::where
+ // TODO
+ // argv[2] is an unknown integer
+ // Silenced the warnings because of the high amount of console spam
+ //kStub(s, argc, argv);
+ break;
+ case 11: // Go, called by ScrollableWindow::scrollTo
+ // 2 extra parameters here
+ // TODO
+ kStub(s, argc, argv);
+ break;
+ case 12: // Insert, called by ScrollableWindow::insertString
+ // 3 extra parameters here
+ // TODO
+ kStub(s, argc, argv);
+ break;
+ // case 13 (Delete) is handled below
+ // case 14 (Modify) is handled above
+ case 15: // Hide, called by ScrollableWindow::hide
+ g_sci->_gfxFrameout->toggleScrollText(false);
+ break;
+ case 16: // Show, called by ScrollableWindow::show
+ g_sci->_gfxFrameout->toggleScrollText(true);
+ break;
+ case 17: // Destroy, called by ScrollableWindow::dispose
+ g_sci->_gfxFrameout->clearScrollTexts();
+ break;
+ case 13: // Delete, unused
+ case 18: // Text, unused
+ case 19: // Reconstruct, unused
+ error("kScrollWindow: Unused subop %d invoked", op);
+ break;
+ default:
+ error("kScrollWindow: unknown subop %d", op);
+ break;
+ }
+
+ return s->r_acc;
+}
+
+reg_t kSetFontRes(EngineState *s, int argc, reg_t *argv) {
+ // TODO: This defines the resolution that the fonts are supposed to be displayed
+ // in. Currently, this is only used for showing high-res fonts in GK1 Mac, but
+ // should be extended to handle other font resolutions such as those
+
+ int xResolution = argv[0].toUint16();
+ //int yResolution = argv[1].toUint16();
+
+ g_sci->_gfxScreen->setFontIsUpscaled(xResolution == 640 &&
+ g_sci->_gfxScreen->getUpscaledHires() != GFX_SCREEN_UPSCALED_DISABLED);
+
+ return s->r_acc;
+}
+
+reg_t kFont(EngineState *s, int argc, reg_t *argv) {
+ // Handle font settings for SCI2.1
+
+ switch (argv[0].toUint16()) {
+ case 1:
+ // Set font resolution
+ return kSetFontRes(s, argc - 1, argv + 1);
+ default:
+ warning("kFont: unknown subop %d", argv[0].toUint16());
+ }
+
+ return s->r_acc;
+}
+
+// TODO: Eventually, all of the kBitmap operations should be put
+// in a separate class
+
+#define BITMAP_HEADER_SIZE 46
+
+reg_t kBitmap(EngineState *s, int argc, reg_t *argv) {
+ // Used for bitmap operations in SCI2.1 and SCI3.
+ // This is the SCI2.1 version, the functionality seems to have changed in SCI3.
+
+ switch (argv[0].toUint16()) {
+ case 0: // init bitmap surface
+ {
+ // 6 params, called e.g. from TextView::init() in Torin's Passage,
+ // script 64890 and TransView::init() in script 64884
+ uint16 width = argv[1].toUint16();
+ uint16 height = argv[2].toUint16();
+ //uint16 skip = argv[3].toUint16();
+ uint16 back = argv[4].toUint16(); // usually equals skip
+ //uint16 width2 = (argc >= 6) ? argv[5].toUint16() : 0;
+ //uint16 height2 = (argc >= 7) ? argv[6].toUint16() : 0;
+ //uint16 transparentFlag = (argc >= 8) ? argv[7].toUint16() : 0;
+
+ // TODO: skip, width2, height2, transparentFlag
+ // (used for transparent bitmaps)
+ int entrySize = width * height + BITMAP_HEADER_SIZE;
+ reg_t memoryId = s->_segMan->allocateHunkEntry("Bitmap()", entrySize);
+ byte *memoryPtr = s->_segMan->getHunkPointer(memoryId);
+ memset(memoryPtr, 0, BITMAP_HEADER_SIZE); // zero out the bitmap header
+ memset(memoryPtr + BITMAP_HEADER_SIZE, back, width * height);
+ // Save totalWidth, totalHeight
+ // TODO: Save the whole bitmap header, like SSCI does
+ WRITE_LE_UINT16(memoryPtr, width);
+ WRITE_LE_UINT16(memoryPtr + 2, height);
+ return memoryId;
+ }
+ break;
+ case 1: // dispose text bitmap surface
+ return kDisposeTextBitmap(s, argc - 1, argv + 1);
+ case 2: // dispose bitmap surface, with extra param
+ // 2 params, called e.g. from MenuItem::dispose in Torin's Passage,
+ // script 64893
+ warning("kBitmap(2), unk1 %d, bitmap ptr %04x:%04x", argv[1].toUint16(), PRINT_REG(argv[2]));
+ break;
+ case 3: // tiled surface
+ {
+ // 6 params, called e.g. from TiledBitmap::resize() in Torin's Passage,
+ // script 64869
+ reg_t hunkId = argv[1]; // obtained from kBitmap(0)
+ // The tiled view seems to always have 2 loops.
+ // These loops need to have 1 cel in loop 0 and 8 cels in loop 1.
+ uint16 viewNum = argv[2].toUint16(); // vTiles selector
+ uint16 loop = argv[3].toUint16();
+ uint16 cel = argv[4].toUint16();
+ uint16 x = argv[5].toUint16();
+ uint16 y = argv[6].toUint16();
+
+ byte *memoryPtr = s->_segMan->getHunkPointer(hunkId);
+ // Get totalWidth, totalHeight
+ uint16 totalWidth = READ_LE_UINT16(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: // add text to bitmap
+ {
+ // 13 params, called e.g. from TextButton::createBitmap() in Torin's Passage,
+ // script 64894
+ reg_t hunkId = argv[1]; // obtained from kBitmap(0)
+ Common::String text = s->_segMan->getString(argv[2]);
+ uint16 textX = argv[3].toUint16();
+ uint16 textY = argv[4].toUint16();
+ //reg_t unk5 = argv[5];
+ //reg_t unk6 = argv[6];
+ //reg_t unk7 = argv[7]; // skip?
+ //reg_t unk8 = argv[8]; // back?
+ //reg_t unk9 = argv[9];
+ uint16 fontId = argv[10].toUint16();
+ //uint16 mode = argv[11].toUint16();
+ uint16 dimmed = argv[12].toUint16();
+ //warning("kBitmap(4): bitmap ptr %04x:%04x, font %d, mode %d, dimmed %d - text: \"%s\"",
+ // PRINT_REG(bitmapPtr), font, mode, dimmed, text.c_str());
+ uint16 foreColor = 255; // TODO
+
+ byte *memoryPtr = s->_segMan->getHunkPointer(hunkId);
+ // Get totalWidth, totalHeight
+ uint16 totalWidth = READ_LE_UINT16(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: // fill with color
+ {
+ // 6 params, called e.g. from TextView::init() and TextView::draw()
+ // in Torin's Passage, script 64890
+ reg_t hunkId = argv[1]; // obtained from kBitmap(0)
+ uint16 x = argv[2].toUint16();
+ uint16 y = argv[3].toUint16();
+ uint16 fillWidth = argv[4].toUint16(); // width - 1
+ uint16 fillHeight = argv[5].toUint16(); // height - 1
+ uint16 back = argv[6].toUint16();
+
+ byte *memoryPtr = s->_segMan->getHunkPointer(hunkId);
+ // Get totalWidth, totalHeight
+ uint16 totalWidth = READ_LE_UINT16(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:
+ kStub(s, argc, argv);
+ break;
+ }
+
+ 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;
+}
+
+reg_t kAddLine(EngineState *s, int argc, reg_t *argv) {
+ reg_t plane = argv[0];
+ Common::Point startPoint(argv[1].toUint16(), argv[2].toUint16());
+ Common::Point endPoint(argv[3].toUint16(), argv[4].toUint16());
+ // argv[5] is unknown (a number, usually 200)
+ byte color = (byte)argv[6].toUint16();
+ byte priority = (byte)argv[7].toUint16();
+ byte control = (byte)argv[8].toUint16();
+ // argv[9] is unknown (usually a small number, 1 or 2). Thickness, perhaps?
+ return g_sci->_gfxFrameout->addPlaneLine(plane, startPoint, endPoint, color, priority, control);
+}
+
+reg_t kUpdateLine(EngineState *s, int argc, reg_t *argv) {
+ reg_t hunkId = argv[0];
+ reg_t plane = argv[1];
+ Common::Point startPoint(argv[2].toUint16(), argv[3].toUint16());
+ Common::Point endPoint(argv[4].toUint16(), argv[5].toUint16());
+ // argv[6] is unknown (a number, usually 200)
+ byte color = (byte)argv[7].toUint16();
+ byte priority = (byte)argv[8].toUint16();
+ byte control = (byte)argv[9].toUint16();
+ // argv[10] is unknown (usually a small number, 1 or 2). Thickness, perhaps?
+ g_sci->_gfxFrameout->updatePlaneLine(plane, hunkId, startPoint, endPoint, color, priority, control);
+ return s->r_acc;
+}
+reg_t kDeleteLine(EngineState *s, int argc, reg_t *argv) {
+ reg_t hunkId = argv[0];
+ reg_t plane = argv[1];
+ g_sci->_gfxFrameout->deletePlaneLine(plane, hunkId);
+ return s->r_acc;
+}
+
+reg_t kSetScroll(EngineState *s, int argc, reg_t *argv) {
+ // Called in the intro of LSL6 hires (room 110)
+ // The end effect of this is the same as the old screen scroll transition
+
+ // 7 parameters
+ reg_t planeObject = argv[0];
+ //int16 x = argv[1].toSint16();
+ //int16 y = argv[2].toSint16();
+ uint16 pictureId = argv[3].toUint16();
+ // param 4: int (0 in LSL6, probably scroll direction? The picture in LSL6 scrolls down)
+ // param 5: int (first call is 1, then the subsequent one is 0 in LSL6)
+ // param 6: optional int (0 in LSL6)
+
+ // Set the new picture directly for now
+ //writeSelectorValue(s->_segMan, planeObject, SELECTOR(left), x);
+ //writeSelectorValue(s->_segMan, planeObject, SELECTOR(top), y);
+ writeSelectorValue(s->_segMan, planeObject, SELECTOR(picture), pictureId);
+ // and update our draw list
+ g_sci->_gfxFrameout->kernelUpdatePlane(planeObject);
+
+ // TODO
+ return kStub(s, argc, argv);
+}
+
+reg_t kPalCycle(EngineState *s, int argc, reg_t *argv) {
+ // Examples: GK1 room 480 (Bayou ritual), LSL6 room 100 (title screen)
+
+ switch (argv[0].toUint16()) {
+ case 0: { // Palette animation initialization
+ // 3 or 4 extra params
+ // Case 1 sends fromColor and speed again, so we don't need them here.
+ // Only toColor is stored
+ //uint16 fromColor = argv[1].toUint16();
+ s->_palCycleToColor = argv[2].toUint16();
+ //uint16 speed = argv[3].toUint16();
+
+ // Invalidate the picture, so that the palette steps calls (case 1
+ // below) can update its palette without it being overwritten by the
+ // view/picture palettes.
+ g_sci->_gfxScreen->_picNotValid = 1;
+
+ // TODO: The fourth optional parameter is an unknown integer, and is 0 by default
+ if (argc == 5) {
+ // When this variant is used, picNotValid doesn't seem to be set
+ // (e.g. GK1 room 480). In this case, the animation step calls are
+ // not made, so perhaps this signifies the palette cycling steps
+ // to make.
+ // GK1 sets this to 6 (6 palette steps?)
+ g_sci->_gfxScreen->_picNotValid = 0;
+ }
+ kStub(s, argc, argv);
+ }
+ break;
+ case 1: { // Palette animation step
+ // This is the same as the old kPaletteAnimate call, with 1 set of colors.
+ // The end color is set up during initialization in case 0 above.
+
+ // 1 or 2 extra params
+ uint16 fromColor = argv[1].toUint16();
+ uint16 speed = (argc == 2) ? 1 : argv[2].toUint16();
+ // TODO: For some reason, this doesn't set the color correctly
+ // (e.g. LSL6 intro, room 100, Sierra logo)
+ if (g_sci->_gfxPalette->kernelAnimate(fromColor, s->_palCycleToColor, speed))
+ g_sci->_gfxPalette->kernelAnimateSet();
+ }
+ // No kStub() call here, as this gets called loads of times, like kPaletteAnimate
+ break;
+ // case 2 hasn't been encountered
+ // case 3 hasn't been encountered
+ case 4: // reset any palette cycling and make the picture valid again
+ // Gets called when changing rooms and after palette cycling animations finish
+ // 0 or 1 extra params
+ if (argc == 1) {
+ g_sci->_gfxScreen->_picNotValid = 0;
+ // TODO: This also seems to perform more steps
+ } else {
+ // The variant with the 1 extra param resets remapping to base
+ // TODO
+ }
+ kStub(s, argc, argv);
+ break;
+ default:
+ // TODO
+ kStub(s, argc, argv);
+ break;
+ }
+
+ return s->r_acc;
+}
+
+reg_t kRemapColors32(EngineState *s, int argc, reg_t *argv) {
+ uint16 operation = argv[0].toUint16();
+
+ switch (operation) {
+ case 0: { // turn remapping off
+ // WORKAROUND: Game scripts in QFG4 erroneously turn remapping off in room
+ // 140 (the character point allocation screen) and never turn it back on,
+ // even if it's clearly used in that screen.
+ if (g_sci->getGameId() == GID_QFG4 && s->currentRoomNumber() == 140)
+ return s->r_acc;
+
+ int16 base = (argc >= 2) ? argv[1].toSint16() : 0;
+ if (base > 0)
+ warning("kRemapColors(0) called with base %d", base);
+ g_sci->_gfxPalette->resetRemapping();
+ }
+ break;
+ case 1: { // remap by range
+ uint16 color = argv[1].toUint16();
+ uint16 from = argv[2].toUint16();
+ uint16 to = argv[3].toUint16();
+ uint16 base = argv[4].toUint16();
+ uint16 unk5 = (argc >= 6) ? argv[5].toUint16() : 0;
+ if (unk5 > 0)
+ warning("kRemapColors(1) called with 6 parameters, unknown parameter is %d", unk5);
+ g_sci->_gfxPalette->setRemappingRange(color, from, to, base);
+ }
+ break;
+ case 2: { // remap by percent
+ uint16 color = argv[1].toUint16();
+ uint16 percent = argv[2].toUint16(); // 0 - 100
+ if (argc >= 4)
+ warning("RemapByPercent called with 4 parameters, unknown parameter is %d", argv[3].toUint16());
+ g_sci->_gfxPalette->setRemappingPercent(color, percent);
+ }
+ break;
+ case 3: { // remap to gray
+ // Example call: QFG4 room 490 (Baba Yaga's hut) - params are color 253, 75% and 0.
+ // In this room, it's used for the cloud before Baba Yaga appears.
+ int16 color = argv[1].toSint16();
+ int16 percent = argv[2].toSint16(); // 0 - 100
+ if (argc >= 4)
+ warning("RemapToGray called with 4 parameters, unknown parameter is %d", argv[3].toUint16());
+ g_sci->_gfxPalette->setRemappingPercentGray(color, percent);
+ }
+ break;
+ case 4: { // remap to percent gray
+ // Example call: QFG4 rooms 530/535 (swamp) - params are 253, 100%, 200
+ int16 color = argv[1].toSint16();
+ int16 percent = argv[2].toSint16(); // 0 - 100
+ // argv[3] is unknown (a number, e.g. 200) - start color, perhaps?
+ if (argc >= 5)
+ warning("RemapToGrayPercent called with 5 parameters, unknown parameter is %d", argv[4].toUint16());
+ g_sci->_gfxPalette->setRemappingPercentGray(color, percent);
+ }
+ break;
+ case 5: { // don't map to range
+ //int16 mapping = argv[1].toSint16();
+ uint16 intensity = argv[2].toUint16();
+ // HACK for PQ4
+ if (g_sci->getGameId() == GID_PQ4)
+ g_sci->_gfxPalette->kernelSetIntensity(0, 255, intensity, true);
+
+ kStub(s, argc, argv);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return s->r_acc;
+}
+
+#endif
+
+} // End of namespace Sci
diff --git a/engines/sci/engine/klists.cpp b/engines/sci/engine/klists.cpp
index 2a33df26bc..342fa95eda 100644
--- a/engines/sci/engine/klists.cpp
+++ b/engines/sci/engine/klists.cpp
@@ -409,10 +409,14 @@ 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 < st2->order.segment || (st1->order.segment == st2->order.segment && st1->order.offset < st2->order.offset))
+ if (st1->order.getSegment() < st2->order.getSegment() ||
+ (st1->order.getSegment() == st2->order.getSegment() &&
+ st1->order.getOffset() < st2->order.getOffset()))
return -1;
- if (st1->order.segment > st2->order.segment || (st1->order.segment == st2->order.segment && st1->order.offset > st2->order.offset))
+ if (st1->order.getSegment() > st2->order.getSegment() ||
+ (st1->order.getSegment() == st2->order.getSegment() &&
+ st1->order.getOffset() > st2->order.getOffset()))
return 1;
return 0;
@@ -502,6 +506,11 @@ reg_t kListAt(EngineState *s, int argc, reg_t *argv) {
curIndex++;
}
+ // Update the virtual file selected in the character import screen of QFG4.
+ // For the SCI0-SCI1.1 version of this, check kDrawControl().
+ if (g_sci->inQfGImportRoom() && !strcmp(s->_segMan->getObjectName(curObject), "SelectorDText"))
+ s->_chosenQfGImportItem = listIndex;
+
return curObject;
}
@@ -575,8 +584,11 @@ reg_t kListFirstTrue(EngineState *s, int argc, reg_t *argv) {
// First, check if the target selector is a variable
if (lookupSelector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) {
- // Can this happen with variable selectors?
- error("kListFirstTrue: Attempted to access a variable selector");
+ // If it's a variable selector, check its value.
+ // Example: script 64893 in Torin, MenuHandler::isHilited checks
+ // all children for variable selector 0x03ba (bHilited).
+ if (!readSelector(s->_segMan, curObject, slc).isNull())
+ return curObject;
} else {
invokeSelector(s, curObject, slc, argc, argv, argc - 2, argv + 2);
@@ -609,16 +621,16 @@ reg_t kListAllTrue(EngineState *s, int argc, reg_t *argv) {
// First, check if the target selector is a variable
if (lookupSelector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) {
- // Can this happen with variable selectors?
- error("kListAllTrue: Attempted to access a variable selector");
+ // If it's a variable selector, check its value
+ s->r_acc = readSelector(s->_segMan, curObject, slc);
} else {
invokeSelector(s, curObject, slc, argc, argv, argc - 2, argv + 2);
-
- // Check if the result isn't true
- if (s->r_acc.isNull())
- break;
}
+ // Check if the result isn't true
+ if (s->r_acc.isNull())
+ break;
+
curNode = s->_segMan->lookupNode(nextNode);
}
@@ -662,15 +674,15 @@ reg_t kArray(EngineState *s, int argc, reg_t *argv) {
if (argv[2].toUint16() == 3)
return kString(s, argc, argv);
} else {
- if (s->_segMan->getSegmentType(argv[1].segment) == SEG_TYPE_STRING ||
- s->_segMan->getSegmentType(argv[1].segment) == SEG_TYPE_SCRIPT) {
+ if (s->_segMan->getSegmentType(argv[1].getSegment()) == SEG_TYPE_STRING ||
+ s->_segMan->getSegmentType(argv[1].getSegment()) == SEG_TYPE_SCRIPT) {
return kString(s, argc, argv);
}
#if 0
if (op == 6) {
- if (s->_segMan->getSegmentType(argv[3].segment) == SEG_TYPE_STRING ||
- s->_segMan->getSegmentType(argv[3].segment) == SEG_TYPE_SCRIPT) {
+ if (s->_segMan->getSegmentType(argv[3].getSegment()) == SEG_TYPE_STRING ||
+ s->_segMan->getSegmentType(argv[3].getSegment()) == SEG_TYPE_SCRIPT) {
return kString(s, argc, argv);
}
}
@@ -789,7 +801,7 @@ reg_t kArray(EngineState *s, int argc, reg_t *argv) {
#endif
return NULL_REG;
}
- if (s->_segMan->getSegmentObj(argv[1].segment)->getType() != SEG_TYPE_ARRAY)
+ if (s->_segMan->getSegmentObj(argv[1].getSegment())->getType() != SEG_TYPE_ARRAY)
error("kArray(Dup): Request to duplicate a segment which isn't an array");
reg_t arrayHandle;
diff --git a/engines/sci/engine/kmath.cpp b/engines/sci/engine/kmath.cpp
index 7570856dff..4b8fadbb84 100644
--- a/engines/sci/engine/kmath.cpp
+++ b/engines/sci/engine/kmath.cpp
@@ -77,7 +77,18 @@ reg_t kSqrt(EngineState *s, int argc, reg_t *argv) {
return make_reg(0, (int16) sqrt((float) ABS(argv[0].toSint16())));
}
+/**
+ * Returns the angle (in degrees) between the two points determined by (x1, y1)
+ * and (x2, y2). The angle ranges from 0 to 359 degrees.
+ * What this function does is pretty simple but apparently the original is not
+ * accurate.
+ */
uint16 kGetAngleWorker(int16 x1, int16 y1, int16 x2, int16 y2) {
+ // SCI1 games (QFG2 and newer) use a simple atan implementation. SCI0 games
+ // use a somewhat less accurate calculation (below).
+ if (getSciVersion() >= SCI_VERSION_1_EGA_ONLY)
+ return (int16)(360 - atan2((double)(x1 - x2), (double)(y1 - y2)) * 57.2958) % 360;
+
int16 xRel = x2 - x1;
int16 yRel = y1 - y2; // y-axis is mirrored.
int16 angle;
diff --git a/engines/sci/engine/kmenu.cpp b/engines/sci/engine/kmenu.cpp
index 02aa1d3ece..05ba7005d7 100644
--- a/engines/sci/engine/kmenu.cpp
+++ b/engines/sci/engine/kmenu.cpp
@@ -94,7 +94,7 @@ reg_t kDrawMenuBar(EngineState *s, int argc, reg_t *argv) {
reg_t kMenuSelect(EngineState *s, int argc, reg_t *argv) {
reg_t eventObject = argv[0];
- bool pauseSound = argc > 1 ? (argv[1].isNull() ? false : true) : true;
+ bool pauseSound = argc <= 1 || !argv[1].isNull();
return g_sci->_gfxMenu->kernelSelect(eventObject, pauseSound);
}
diff --git a/engines/sci/engine/kmisc.cpp b/engines/sci/engine/kmisc.cpp
index a32480c168..8b7fc4ffec 100644
--- a/engines/sci/engine/kmisc.cpp
+++ b/engines/sci/engine/kmisc.cpp
@@ -128,7 +128,7 @@ reg_t kMemoryInfo(EngineState *s, int argc, reg_t *argv) {
// fragmented
const uint16 size = 0x7fea;
- switch (argv[0].offset) {
+ switch (argv[0].getOffset()) {
case K_MEMORYINFO_LARGEST_HEAP_BLOCK:
// In order to prevent "Memory fragmented" dialogs from
// popping up in some games, we must return FREE_HEAP - 2 here.
@@ -140,7 +140,7 @@ reg_t kMemoryInfo(EngineState *s, int argc, reg_t *argv) {
return make_reg(0, size);
default:
- error("Unknown MemoryInfo operation: %04x", argv[0].offset);
+ error("Unknown MemoryInfo operation: %04x", argv[0].getOffset());
}
return NULL_REG;
@@ -304,7 +304,7 @@ reg_t kMemory(EngineState *s, int argc, reg_t *argv) {
break;
}
case K_MEMORY_PEEK : {
- if (!argv[1].segment) {
+ if (!argv[1].getSegment()) {
// This occurs in KQ5CD when interacting with certain objects
warning("Attempt to peek invalid memory at %04x:%04x", PRINT_REG(argv[1]));
return s->r_acc;
@@ -334,11 +334,11 @@ reg_t kMemory(EngineState *s, int argc, reg_t *argv) {
}
if (ref.isRaw) {
- if (argv[2].segment) {
+ if (argv[2].getSegment()) {
error("Attempt to poke memory reference %04x:%04x to %04x:%04x", PRINT_REG(argv[2]), PRINT_REG(argv[1]));
return s->r_acc;
}
- WRITE_SCIENDIAN_UINT16(ref.raw, argv[2].offset); // Amiga versions are BE
+ WRITE_SCIENDIAN_UINT16(ref.raw, argv[2].getOffset()); // Amiga versions are BE
} else {
if (ref.skipByte)
error("Attempt to poke memory at odd offset %04X:%04X", PRINT_REG(argv[1]));
@@ -356,10 +356,81 @@ reg_t kGetConfig(EngineState *s, int argc, reg_t *argv) {
Common::String setting = s->_segMan->getString(argv[0]);
reg_t data = readSelector(s->_segMan, argv[1], SELECTOR(data));
- warning("Get config setting %s", setting.c_str());
- s->_segMan->strcpy(data, "");
+ // This function is used to get the benchmarked results stored in the
+ // resource.cfg configuration file in Phantasmagoria 1. Normally,
+ // the configuration file contains values stored by the installer
+ // regarding audio and video settings, which are then used by the
+ // executable. In Phantasmagoria, two extra executable files are used
+ // to perform system benchmarks:
+ // - CPUID for the CPU benchmarks, sets the cpu and cpuspeed settings
+ // - HDDTEC for the graphics and CD-ROM benchmarks, sets the videospeed setting
+ //
+ // These settings are then used by the game scripts directly to modify
+ // the game speed and graphics output. The result of this call is stored
+ // in global 178. The scripts check these values against the value 425.
+ // Anything below that makes Phantasmagoria awfully sluggish, so we're
+ // setting everything to 500, which makes the game playable.
+
+ setting.toLowercase();
+
+ if (setting == "videospeed") {
+ s->_segMan->strcpy(data, "500");
+ } else if (setting == "cpu") {
+ // We always return the fastest CPU setting that CPUID can detect
+ // (i.e. 586).
+ s->_segMan->strcpy(data, "586");
+ } else if (setting == "cpuspeed") {
+ s->_segMan->strcpy(data, "500");
+ } else if (setting == "language") {
+ Common::String languageId = Common::String::format("%d", g_sci->getSciLanguage());
+ s->_segMan->strcpy(data, languageId.c_str());
+ } else if (setting == "torindebug") {
+ // Used to enable the debug mode in Torin's Passage (French).
+ // If true, the debug mode is enabled.
+ s->_segMan->strcpy(data, "");
+ } else if (setting == "leakdump") {
+ // An unknown setting in LSL7. Likely used for debugging.
+ s->_segMan->strcpy(data, "");
+ } else if (setting == "startroom") {
+ // Debug setting in LSL7, specifies the room to start from.
+ s->_segMan->strcpy(data, "");
+ } else {
+ error("GetConfig: Unknown configuration setting %s", setting.c_str());
+ }
+
return argv[1];
}
+
+// Likely modelled after the Windows 3.1 function GetPrivateProfileInt:
+// http://msdn.microsoft.com/en-us/library/windows/desktop/ms724345%28v=vs.85%29.aspx
+reg_t kGetSierraProfileInt(EngineState *s, int argc, reg_t *argv) {
+ Common::String category = s->_segMan->getString(argv[0]); // always "config"
+ category.toLowercase();
+ Common::String setting = s->_segMan->getString(argv[1]);
+ setting.toLowercase();
+ // The third parameter is the default value returned if the configuration key is missing
+
+ if (category == "config" && setting == "videospeed") {
+ // We return the same fake value for videospeed as with kGetConfig
+ return make_reg(0, 500);
+ }
+
+ warning("kGetSierraProfileInt: Returning default value %d for unknown setting %s.%s", argv[2].toSint16(), category.c_str(), setting.c_str());
+ return argv[2];
+}
+
+reg_t kGetWindowsOption(EngineState *s, int argc, reg_t *argv) {
+ uint16 windowsOption = argv[0].toUint16();
+ switch (windowsOption) {
+ case 0:
+ // Title bar on/off in Phantasmagoria, we return 0 (off)
+ return NULL_REG;
+ default:
+ warning("GetWindowsOption: Unknown option %d", windowsOption);
+ return NULL_REG;
+ }
+}
+
#endif
// kIconBar is really a subop of kMacPlatform for SCI1.1 Mac
diff --git a/engines/sci/engine/kparse.cpp b/engines/sci/engine/kparse.cpp
index 59694cb6ee..5e861daaa4 100644
--- a/engines/sci/engine/kparse.cpp
+++ b/engines/sci/engine/kparse.cpp
@@ -48,7 +48,7 @@ reg_t kSaid(EngineState *s, int argc, reg_t *argv) {
const int debug_parser = 0;
#endif
- if (!heap_said_block.segment)
+ if (!heap_said_block.getSegment())
return NULL_REG;
said_block = (byte *)s->_segMan->derefBulkPtr(heap_said_block, 0);
diff --git a/engines/sci/engine/kpathing.cpp b/engines/sci/engine/kpathing.cpp
index 089b325a7f..4061795f82 100644
--- a/engines/sci/engine/kpathing.cpp
+++ b/engines/sci/engine/kpathing.cpp
@@ -31,6 +31,9 @@
#include "common/debug-channels.h"
#include "common/list.h"
#include "common/system.h"
+#include "common/math.h"
+
+//#define DEBUG_MERGEPOLY
namespace Sci {
@@ -71,11 +74,25 @@ enum {
struct FloatPoint {
FloatPoint() : x(0), y(0) {}
FloatPoint(float x_, float y_) : x(x_), y(y_) {}
+ FloatPoint(Common::Point p) : x(p.x), y(p.y) {}
Common::Point toPoint() {
return Common::Point((int16)(x + 0.5), (int16)(y + 0.5));
}
+ float operator*(const FloatPoint &p) const {
+ return x*p.x + y*p.y;
+ }
+ FloatPoint operator*(float l) const {
+ return FloatPoint(l*x, l*y);
+ }
+ FloatPoint operator-(const FloatPoint &p) const {
+ return FloatPoint(x-p.x, y-p.y);
+ }
+ float norm() const {
+ return x*x+y*y;
+ }
+
float x, y;
};
@@ -135,15 +152,20 @@ public:
return _head;
}
- void insertHead(Vertex *elm) {
+ void insertAtEnd(Vertex *elm) {
if (_head == NULL) {
elm->_next = elm->_prev = elm;
+ _head = elm;
} else {
elm->_next = _head;
elm->_prev = _head->_prev;
_head->_prev = elm;
elm->_prev->_next = elm;
}
+ }
+
+ void insertHead(Vertex *elm) {
+ insertAtEnd(elm);
_head = elm;
}
@@ -367,7 +389,7 @@ static void draw_input(EngineState *s, reg_t poly_list, Common::Point start, Com
draw_point(s, start, 1, width, height);
draw_point(s, end, 0, width, height);
- if (!poly_list.segment)
+ if (!poly_list.getSegment())
return;
list = s->_segMan->lookupList(poly_list);
@@ -423,7 +445,7 @@ static void print_input(EngineState *s, reg_t poly_list, Common::Point start, Co
debug("End point: (%i, %i)", end.x, end.y);
debug("Optimization level: %i", opt);
- if (!poly_list.segment)
+ if (!poly_list.getSegment())
return;
list = s->_segMan->lookupList(poly_list);
@@ -788,10 +810,10 @@ int PathfindingState::findNearPoint(const Common::Point &p, Polygon *polygon, Co
* including the vertices themselves)
* Parameters: (const Common::Point &) a, b: The line segment (a, b)
* (Vertex *) vertex: The first vertex of the edge
- * Returns : (int) FP_OK on success, PF_ERROR otherwise
+ * Returns : (int) PF_OK on success, PF_ERROR otherwise
* (FloatPoint) *ret: The intersection point
*/
-static int intersection(const Common::Point &a, const Common::Point &b, Vertex *vertex, FloatPoint *ret) {
+static int intersection(const Common::Point &a, const Common::Point &b, const Vertex *vertex, FloatPoint *ret) {
// Parameters of parametric equations
float s, t;
// Numerator and denominator of equations
@@ -1180,7 +1202,7 @@ static PathfindingState *convert_polygon_set(EngineState *s, reg_t poly_list, Co
PathfindingState *pf_s = new PathfindingState(width, height);
// Convert all polygons
- if (poly_list.segment) {
+ if (poly_list.getSegment()) {
List *list = s->_segMan->lookupList(poly_list);
Node *node = s->_segMan->lookupNode(list->first);
@@ -1503,7 +1525,7 @@ reg_t kAvoidPath(EngineState *s, int argc, reg_t *argv) {
draw_point(s, start, 1, width, height);
draw_point(s, end, 0, width, height);
- if (poly_list.segment) {
+ if (poly_list.getSegment()) {
print_input(s, poly_list, start, end, opt);
draw_input(s, poly_list, start, end, opt, width, height);
}
@@ -1783,39 +1805,619 @@ reg_t kIntersections(EngineState *s, int argc, reg_t *argv) {
}
}
+// ==========================================================================
+// kMergePoly utility functions
+
+// Compute square of the distance of p to the segment a-b.
+static float pointSegDistance(const Common::Point &a, const Common::Point &b,
+ const Common::Point &p) {
+ FloatPoint ba(b-a);
+ FloatPoint pa(p-a);
+ FloatPoint bp(b-p);
+
+ // Check if the projection of p on the line a-b lies between a and b
+ if (ba*pa >= 0.0f && ba*bp >= 0.0f) {
+ // If yes, return the (squared) distance of p to the line a-b:
+ // translate a to origin, project p and subtract
+ float linedist = (ba*((ba*pa)/(ba*ba)) - pa).norm();
+
+ return linedist;
+ } else {
+ // If no, return the (squared) distance to either a or b, whichever
+ // is closest.
+
+ // distance to a:
+ float adist = pa.norm();
+ // distance to b:
+ float bdist = FloatPoint(p-b).norm();
+
+ return MIN(adist, bdist);
+ }
+}
+
+// find intersection between edges of two polygons.
+// endpoints count, except v2->_next
+static bool segSegIntersect(const Vertex *v1, const Vertex *v2, Common::Point &intp) {
+ const Common::Point &a = v1->v;
+ const Common::Point &b = v1->_next->v;
+ const Common::Point &c = v2->v;
+ const Common::Point &d = v2->_next->v;
+
+ // First handle the endpoint cases manually
+
+ if (collinear(a, b, c) && collinear(a, b, d))
+ return false;
+
+ if (collinear(a, b, c)) {
+ // a, b, c collinear
+ // return true/c if c is between a and b
+ intp = c;
+ if (a.x != b.x) {
+ if ((a.x <= c.x && c.x <= b.x) || (b.x <= c.x && c.x <= a.x))
+ return true;
+ } else {
+ if ((a.y <= c.y && c.y <= b.y) || (b.y <= c.y && c.y <= a.y))
+ return true;
+ }
+ }
+
+ if (collinear(a, b, d)) {
+ intp = d;
+ // a, b, d collinear
+ // return false/d if d is between a and b
+ if (a.x != b.x) {
+ if ((a.x <= d.x && d.x <= b.x) || (b.x <= d.x && d.x <= a.x))
+ return false;
+ } else {
+ if ((a.y <= d.y && d.y <= b.y) || (b.y <= d.y && d.y <= a.y))
+ return false;
+ }
+ }
+
+ int len_dc = c.sqrDist(d);
+
+ if (!len_dc) error("zero length edge in polygon");
+
+ if (pointSegDistance(c, d, a) <= 2.0f) {
+ intp = a;
+ return true;
+ }
+
+ if (pointSegDistance(c, d, b) <= 2.0f) {
+ intp = b;
+ return true;
+ }
+
+ // If not an endpoint, call the generic intersection function
+
+ FloatPoint p;
+ if (intersection(a, b, v2, &p) == PF_OK) {
+ intp = p.toPoint();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+// For intersecting polygon segments, determine if
+// * the v2 edge enters polygon 1 at this intersection: positive return value
+// * the v2 edge and the v1 edges are parallel: zero return value
+// * the v2 edge exits polygon 1 at this intersection: negative return value
+static int intersectDir(const Vertex *v1, const Vertex *v2) {
+ Common::Point p1 = v1->_next->v - v1->v;
+ Common::Point p2 = v2->_next->v - v2->v;
+ return (p1.x*p2.y - p2.x*p1.y);
+}
+
+// Direction of edge in degrees from pos. x-axis, between -180 and 180
+static int edgeDir(const Vertex *v) {
+ Common::Point p = v->_next->v - v->v;
+ int deg = (int)Common::rad2deg(atan2((double)p.y, (double)p.x));
+ if (deg < -180) deg += 360;
+ if (deg > 180) deg -= 360;
+ return deg;
+}
+
+// For points p1, p2 on the polygon segment v, determine if
+// * p1 lies before p2: negative return value
+// * p1 and p2 are the same: zero return value
+// * p1 lies after p2: positive return value
+static int liesBefore(const Vertex *v, const Common::Point &p1, const Common::Point &p2) {
+ return v->v.sqrDist(p1) - v->v.sqrDist(p2);
+}
+
+// Structure describing an "extension" to the work polygon following edges
+// of the polygon being merged.
+
+// The patch begins on the point intersection1, being the intersection
+// of the edges starting at indexw1/vertexw1 on the work polygon, and at
+// indexp1/vertexp1 on the polygon being merged.
+// It ends with the point intersection2, being the analogous intersection.
+struct Patch {
+ unsigned int indexw1;
+ unsigned int indexp1;
+ const Vertex *vertexw1;
+ const Vertex *vertexp1;
+ Common::Point intersection1;
+
+ unsigned int indexw2;
+ unsigned int indexp2;
+ const Vertex *vertexw2;
+ const Vertex *vertexp2;
+ Common::Point intersection2;
+
+ bool disabled; // If true, this Patch was made superfluous by another Patch
+};
+
+
+// Check if the given vertex on the work polygon is bypassed by this patch.
+static bool isVertexCovered(const Patch &p, unsigned int wi) {
+
+ // / v (outside)
+ // ---w1--1----p----w2--2----
+ // ^ \ (inside)
+ if (wi > p.indexw1 && wi <= p.indexw2)
+ return true;
+
+ // v / (outside)
+ // ---w2--2----p----w1--1----
+ // \ ^ (inside)
+ if (p.indexw1 > p.indexw2 && (wi <= p.indexw2 || wi > p.indexw1))
+ return true;
+
+ // v / (outside)
+ // ---w1--2--1-------p-----
+ // w2 \ ^ (inside)
+ if (p.indexw1 == p.indexw2 && liesBefore(p.vertexw1, p.intersection1, p.intersection2) > 0)
+ return true; // This patch actually covers _all_ vertices on work
+
+ return false;
+}
+
+// Check if patch p1 makes patch p2 superfluous.
+static bool isPatchCovered(const Patch &p1, const Patch &p2) {
+
+ // Same exit and entry points
+ if (p1.intersection1 == p2.intersection1 && p1.intersection2 == p2.intersection2)
+ return true;
+
+ // / * v (outside)
+ // ---p1w1--1----p2w1-1---p1w2--2----
+ // ^ * \ (inside)
+ if (p1.indexw1 < p2.indexw1 && p2.indexw1 < p1.indexw2)
+ return true;
+ if (p1.indexw1 > p1.indexw2 && (p2.indexw1 > p1.indexw1 || p2.indexw1 < p1.indexw2))
+ return true;
+
+
+ // / * v (outside)
+ // ---p1w1--11----p2w2-2---p1w2--12----
+ // ^ * \ (inside)
+ if (p1.indexw1 < p2.indexw2 && p2.indexw2 < p1.indexw2)
+ return true;
+ if (p1.indexw1 > p1.indexw2 && (p2.indexw2 > p1.indexw1 || p2.indexw2 < p1.indexw2))
+ return true;
+
+ // Opposite of two above situations
+ if (p2.indexw1 < p1.indexw1 && p1.indexw1 < p2.indexw2)
+ return false;
+ if (p2.indexw1 > p2.indexw2 && (p1.indexw1 > p2.indexw1 || p1.indexw1 < p2.indexw2))
+ return false;
+
+ if (p2.indexw1 < p1.indexw2 && p1.indexw2 < p2.indexw2)
+ return false;
+ if (p2.indexw1 > p2.indexw2 && (p1.indexw2 > p2.indexw1 || p1.indexw2 < p2.indexw2))
+ return false;
+
+
+ // The above checks covered the cases where one patch covers the other and
+ // the intersections of the patches are on different edges.
+
+ // So, if we passed the above checks, we have to check the order of
+ // intersections on edges.
+
+
+ if (p1.indexw1 != p1.indexw2) {
+
+ // / * v (outside)
+ // ---p1w1--11---21--------p1w2--2----
+ // p2w1 ^ * \ (inside)
+ if (p1.indexw1 == p2.indexw1)
+ return (liesBefore(p1.vertexw1, p1.intersection1, p2.intersection1) < 0);
+
+ // / * v (outside)
+ // ---p1w1--11---------p1w2--21---12----
+ // ^ p2w1 * \ (inside)
+ if (p1.indexw2 == p2.indexw1)
+ return (liesBefore(p1.vertexw2, p1.intersection2, p2.intersection1) > 0);
+
+ // If neither of the above, then the intervals of the polygon
+ // covered by patch1 and patch2 are disjoint
+ return false;
+ }
+
+ // p1w1 == p1w2
+ // Also, p1w1/p1w2 isn't strictly between p2
+
+
+ // v / * (outside)
+ // ---p1w1--12--11-------p2w1-21----
+ // p1w2 \ ^ * (inside)
+
+ // v / / (outside)
+ // ---p1w1--12--21--11---------
+ // p1w2 \ ^ ^ (inside)
+ // p2w1
+ if (liesBefore(p1.vertexw1, p1.intersection1, p1.intersection2) > 0)
+ return (p1.indexw1 != p2.indexw1);
+
+ // CHECKME: This is meaningless if p2w1 != p2w2 ??
+ if (liesBefore(p2.vertexw1, p2.intersection1, p2.intersection2) > 0)
+ return false;
+
+ // CHECKME: This is meaningless if p1w1 != p2w1 ??
+ if (liesBefore(p2.vertexw1, p2.intersection1, p1.intersection1) <= 0)
+ return false;
+
+ // CHECKME: This is meaningless if p1w2 != p2w1 ??
+ if (liesBefore(p2.vertexw1, p2.intersection1, p1.intersection2) >= 0)
+ return false;
+
+ return true;
+}
+
+// Merge a single polygon into the work polygon.
+// If there is an intersection between work and polygon, this function
+// returns true, and replaces the vertex list of work by an extended version,
+// that covers polygon.
+//
+// NOTE: The strategy used matches qfg1new closely, and is a bit error-prone.
+// A more robust strategy would be inserting all intersection points directly
+// into both vertex lists as a first pass. This would make finding the merged
+// polygon a much more straightforward edge-walk, and avoid cases where SSCI's
+// algorithm mixes up the order of multiple intersections on a single edge.
+bool mergeSinglePolygon(Polygon &work, const Polygon &polygon) {
+#ifdef DEBUG_MERGEPOLY
+ const Vertex *vertex;
+ debugN("work:");
+ CLIST_FOREACH(vertex, &(work.vertices)) {
+ debugN(" (%d,%d) ", vertex->v.x, vertex->v.y);
+ }
+ debugN("\n");
+ debugN("poly:");
+ CLIST_FOREACH(vertex, &(polygon.vertices)) {
+ debugN(" (%d,%d) ", vertex->v.x, vertex->v.y);
+ }
+ debugN("\n");
+#endif
+ uint workSize = work.vertices.size();
+ uint polygonSize = polygon.vertices.size();
+
+ int patchCount = 0;
+ Patch patchList[8];
+
+ const Vertex *workv = work.vertices._head;
+ const Vertex *polyv = polygon.vertices._head;
+ for (uint wi = 0; wi < workSize; ++wi, workv = workv->_next) {
+ for (uint pi = 0; pi < polygonSize; ++pi, polyv = polyv->_next) {
+ Common::Point intersection1;
+ Common::Point intersection2;
+
+ bool intersects = segSegIntersect(workv, polyv, intersection1);
+ if (!intersects)
+ continue;
+
+#ifdef DEBUG_MERGEPOLY
+ debug("mergePoly: intersection at work %d, poly %d", wi, pi);
+#endif
+
+ if (intersectDir(workv, polyv) >= 0)
+ continue;
+
+#ifdef DEBUG_MERGEPOLY
+ debug("mergePoly: intersection in right direction");
+#endif
+
+ int angle = 0;
+ int baseAngle = edgeDir(workv);
+
+ // We now found the point where an edge of 'polygon' left 'work'.
+ // Now find the re-entry point.
+
+ // NOTE: The order in which this searches does not always work
+ // properly if the correct patch would only use a single partial
+ // edge of poly. Because it starts at polyv->_next, it will skip
+ // the correct re-entry and proceed to the next.
+
+ const Vertex *workv2;
+ const Vertex *polyv2 = polyv->_next;
+
+ intersects = false;
+
+ uint pi2, wi2;
+ for (pi2 = 0; pi2 < polygonSize; ++pi2, polyv2 = polyv2->_next) {
+
+ int newAngle = edgeDir(polyv2);
+
+ int relAngle = newAngle - baseAngle;
+ if (relAngle > 180) relAngle -= 360;
+ if (relAngle < -180) relAngle += 360;
+
+ angle += relAngle;
+ baseAngle = newAngle;
+
+ workv2 = workv;
+ for (wi2 = 0; wi2 < workSize; ++wi2, workv2 = workv2->_next) {
+ intersects = segSegIntersect(workv2, polyv2, intersection2);
+ if (!intersects)
+ continue;
+#ifdef DEBUG_MERGEPOLY
+ debug("mergePoly: re-entry intersection at work %d, poly %d", (wi + wi2) % workSize, (pi + 1 + pi2) % polygonSize);
+#endif
+
+ if (intersectDir(workv2, polyv2) > 0) {
+#ifdef DEBUG_MERGEPOLY
+ debug("mergePoly: re-entry intersection in right direction, angle = %d", angle);
+#endif
+ break; // found re-entry point
+ }
+
+ }
+
+ if (intersects)
+ break;
+
+ }
+
+ if (!intersects || angle < 0)
+ continue;
+
+
+ if (patchCount >= 8)
+ error("kMergePoly: Too many patches");
+
+ // convert relative to absolute vertex indices
+ pi2 = (pi + 1 + pi2) % polygonSize;
+ wi2 = (wi + wi2) % workSize;
+
+ Patch &newPatch = patchList[patchCount];
+ newPatch.indexw1 = wi;
+ newPatch.vertexw1 = workv;
+ newPatch.indexp1 = pi;
+ newPatch.vertexp1 = polyv;
+ newPatch.intersection1 = intersection1;
+
+ newPatch.indexw2 = wi2;
+ newPatch.vertexw2 = workv2;
+ newPatch.indexp2 = pi2;
+ newPatch.vertexp2 = polyv2;
+ newPatch.intersection2 = intersection2;
+ newPatch.disabled = false;
+
+#ifdef DEBUG_MERGEPOLY
+ debug("mergePoly: adding patch at work %d, poly %d", wi, pi);
+#endif
+
+ if (patchCount == 0) {
+ patchCount++;
+ continue;
+ }
+
+ bool necessary = true;
+ for (int i = 0; i < patchCount; ++i) {
+ if (isPatchCovered(patchList[i], newPatch)) {
+ necessary = false;
+ break;
+ }
+ }
+
+ if (!necessary)
+ continue;
+
+ patchCount++;
+
+ if (patchCount > 1) {
+ // check if this patch makes other patches superfluous
+ for (int i = 0; i < patchCount-1; ++i)
+ if (isPatchCovered(newPatch, patchList[i]))
+ patchList[i].disabled = true;
+ }
+ }
+ }
+
+
+ if (patchCount == 0)
+ return false; // nothing changed
+
+
+ // Determine merged work by doing a walk over the edges
+ // of work, crossing over to polygon when encountering a patch.
+
+ Polygon output(0);
+
+ workv = work.vertices._head;
+ for (uint wi = 0; wi < workSize; ++wi, workv = workv->_next) {
+
+ bool covered = false;
+ for (int p = 0; p < patchCount; ++p) {
+ if (patchList[p].disabled) continue;
+ if (isVertexCovered(patchList[p], wi)) {
+ covered = true;
+ break;
+ }
+ }
+
+ if (!covered) {
+ // Add vertex to output
+ output.vertices.insertAtEnd(new Vertex(workv->v));
+ }
+
+
+ // CHECKME: Why is this the correct order in which to process
+ // the patches? (What if two of them start on this line segment
+ // in the opposite order?)
+
+ for (int p = 0; p < patchCount; ++p) {
+
+ const Patch &patch = patchList[p];
+ if (patch.disabled) continue;
+ if (patch.indexw1 != wi) continue;
+ if (patch.intersection1 != workv->v) {
+ // Add intersection point to output
+ output.vertices.insertAtEnd(new Vertex(patch.intersection1));
+ }
+
+ // Add vertices from polygon between vertexp1 (excl) and vertexp2 (incl)
+ for (polyv = patch.vertexp1->_next; polyv != patch.vertexp2; polyv = polyv->_next)
+ output.vertices.insertAtEnd(new Vertex(polyv->v));
+
+ output.vertices.insertAtEnd(new Vertex(patch.vertexp2->v));
+
+ if (patch.intersection2 != patch.vertexp2->v) {
+ // Add intersection point to output
+ output.vertices.insertAtEnd(new Vertex(patch.intersection2));
+ }
+
+ // TODO: We could continue after the re-entry point here?
+ }
+ }
+ // Remove last vertex if it's the same as the first vertex
+ if (output.vertices._head->v == output.vertices._head->_prev->v)
+ output.vertices.remove(output.vertices._head->_prev);
+
+
+ // Slight hack: swap vertex lists of output and work polygons.
+ SWAP(output.vertices._head, work.vertices._head);
+
+ return true;
+}
+
+
/**
* This is a quite rare kernel function. An example of when it's called
* is in QFG1VGA, after killing any monster.
+ *
+ * It takes a polygon, and extends it to also cover any polygons from the
+ * input list with which it intersects. Any of those polygons so covered
+ * from the input list are marked by adding 0x10 to their type field.
*/
reg_t kMergePoly(EngineState *s, int argc, reg_t *argv) {
-#if 0
// 3 parameters: raw polygon data, polygon list, list size
reg_t polygonData = argv[0];
List *list = s->_segMan->lookupList(argv[1]);
- Node *node = s->_segMan->lookupNode(list->first);
- // List size is not needed
- Polygon *polygon;
- int count = 0;
+ // The size of the "work" point list SSCI uses. We use a dynamic one instead
+ //reg_t listSize = argv[2];
+
+ SegmentRef pointList = s->_segMan->dereference(polygonData);
+ if (!pointList.isValid() || pointList.skipByte) {
+ warning("kMergePoly: Polygon data pointer is invalid");
+ return make_reg(0, 0);
+ }
+
+ Node *node;
+
+#ifdef DEBUG_MERGEPOLY
+ node = s->_segMan->lookupNode(list->first);
+ while (node) {
+ draw_polygon(s, node->value, 320, 190);
+ node = s->_segMan->lookupNode(node->succ);
+ }
+ Common::Point prev, first;
+ prev = first = readPoint(pointList, 0);
+ for (int i = 1; readPoint(pointList, i).x != 0x7777; i++) {
+ Common::Point point = readPoint(pointList, i);
+ draw_line(s, prev, point, 1, 320, 190);
+ prev = point;
+ }
+ draw_line(s, prev, first, 1, 320, 190);
+ // Update the whole screen
+ g_sci->_gfxScreen->copyToScreen();
+ g_system->updateScreen();
+ g_system->delayMillis(1000);
+#endif
+
+ // The work polygon which we're going to merge with the polygons in list
+ Polygon work(0);
+
+ for (int i = 0; true; ++i) {
+ Common::Point p = readPoint(pointList, i);
+ if (p.x == POLY_LAST_POINT)
+ break;
+ Vertex *vertex = new Vertex(p);
+ work.vertices.insertAtEnd(vertex);
+ }
+
+ // TODO: Check behaviour for single-vertex polygons
+ node = s->_segMan->lookupNode(list->first);
while (node) {
- polygon = convert_polygon(s, node->value);
+ Polygon *polygon = convert_polygon(s, node->value);
if (polygon) {
- count += readSelectorValue(s->_segMan, node->value, SELECTOR(size));
+ // CHECKME: Confirm vertex order that convert_polygon and
+ // fix_vertex_order output. For now, we re-reverse the order since
+ // convert_polygon reads the vertices reversed, and fix up head.
+ polygon->vertices.reverse();
+ polygon->vertices._head = polygon->vertices._head->_next;
+
+ // Merge this polygon into the work polygon if there is an
+ // intersection.
+ bool intersected = mergeSinglePolygon(work, *polygon);
+
+ // If so, flag it
+ if (intersected) {
+ writeSelectorValue(s->_segMan, node->value,
+ SELECTOR(type), polygon->type + 0x10);
+#ifdef DEBUG_MERGEPOLY
+ debugN("Merged polygon: ");
+ // Iterate over edges
+ Vertex *vertex;
+ CLIST_FOREACH(vertex, &(work.vertices)) {
+ debugN(" (%d,%d) ", vertex->v.x, vertex->v.y);
+ }
+ debugN("\n");
+#endif
+ }
}
node = s->_segMan->lookupNode(node->succ);
}
-#endif
- // TODO: actually merge the polygon. We return an empty polygon for now.
- // In QFG1VGA, you can walk over enemy bodies after killing them, since
- // this is a stub.
- reg_t output = allocateOutputArray(s->_segMan, 1);
+
+ // Allocate output array
+ reg_t output = allocateOutputArray(s->_segMan, work.vertices.size()+1);
SegmentRef arrayRef = s->_segMan->dereference(output);
- writePoint(arrayRef, 0, Common::Point(POLY_LAST_POINT, POLY_LAST_POINT));
- warning("Stub: kMergePoly");
+
+ // Copy work.vertices into arrayRef
+ Vertex *vertex;
+ unsigned int n = 0;
+ CLIST_FOREACH(vertex, &work.vertices) {
+ if (vertex == work.vertices._head || vertex->v != vertex->_prev->v)
+ writePoint(arrayRef, n++, vertex->v);
+ }
+
+ writePoint(arrayRef, n, Common::Point(POLY_LAST_POINT, POLY_LAST_POINT));
+
+#ifdef DEBUG_MERGEPOLY
+ prev = first = readPoint(arrayRef, 0);
+ for (int i = 1; readPoint(arrayRef, i).x != 0x7777; i++) {
+ Common::Point point = readPoint(arrayRef, i);
+ draw_line(s, prev, point, 3, 320, 190);
+ prev = point;
+ }
+
+ draw_line(s, prev, first, 3, 320, 190);
+
+ // Update the whole screen
+ g_sci->_gfxScreen->copyToScreen();
+ g_system->updateScreen();
+ if (!g_sci->_gfxPaint16)
+ g_system->delayMillis(1000);
+
+ debug("kMergePoly done");
+#endif
+
return output;
}
diff --git a/engines/sci/engine/kscripts.cpp b/engines/sci/engine/kscripts.cpp
index 9b0e490d7e..2c115be500 100644
--- a/engines/sci/engine/kscripts.cpp
+++ b/engines/sci/engine/kscripts.cpp
@@ -140,7 +140,7 @@ reg_t kClone(EngineState *s, int argc, reg_t *argv) {
debugC(kDebugLevelMemory, "Attempting to clone from %04x:%04x", PRINT_REG(parentAddr));
- uint16 infoSelector = parentObj->getInfoSelector().offset;
+ uint16 infoSelector = parentObj->getInfoSelector().getOffset();
cloneObj = s->_segMan->allocateClone(&cloneAddr);
if (!cloneObj) {
@@ -169,8 +169,8 @@ reg_t kClone(EngineState *s, int argc, reg_t *argv) {
cloneObj->setSpeciesSelector(cloneObj->getPos());
if (parentObj->isClass())
cloneObj->setSuperClassSelector(parentObj->getPos());
- s->_segMan->getScript(parentObj->getPos().segment)->incrementLockers();
- s->_segMan->getScript(cloneObj->getPos().segment)->incrementLockers();
+ s->_segMan->getScript(parentObj->getPos().getSegment())->incrementLockers();
+ s->_segMan->getScript(cloneObj->getPos().getSegment())->incrementLockers();
return cloneAddr;
}
@@ -191,7 +191,7 @@ reg_t kDisposeClone(EngineState *s, int argc, reg_t *argv) {
// At least kq4early relies on this behavior. The scripts clone "Sound", then set bit 1 manually
// and call kDisposeClone later. In that case we may not free it, otherwise we will run into issues
// later, because kIsObject would then return false and Sound object wouldn't get checked.
- uint16 infoSelector = object->getInfoSelector().offset;
+ uint16 infoSelector = object->getInfoSelector().getOffset();
if ((infoSelector & 3) == kInfoFlagClone)
object->markAsFreed();
@@ -203,7 +203,7 @@ reg_t kScriptID(EngineState *s, int argc, reg_t *argv) {
int script = argv[0].toUint16();
uint16 index = (argc > 1) ? argv[1].toUint16() : 0;
- if (argv[0].segment)
+ if (argv[0].getSegment())
return argv[0];
SegmentId scriptSeg = s->_segMan->getScriptSegment(script, SCRIPT_GET_LOAD);
@@ -226,11 +226,6 @@ reg_t kScriptID(EngineState *s, int argc, reg_t *argv) {
return NULL_REG;
}
- if (index > scr->getExportsNr()) {
- error("Dispatch index too big: %d > %d", index, scr->getExportsNr());
- return NULL_REG;
- }
-
uint16 address = scr->validateExportFunc(index, true);
// Point to the heap for SCI1.1 - SCI2.1 games
@@ -251,12 +246,12 @@ reg_t kScriptID(EngineState *s, int argc, reg_t *argv) {
}
reg_t kDisposeScript(EngineState *s, int argc, reg_t *argv) {
- int script = argv[0].offset;
+ int script = argv[0].getOffset();
SegmentId id = s->_segMan->getScriptSegment(script);
Script *scr = s->_segMan->getScriptIfLoaded(id);
if (scr && !scr->isMarkedAsDeleted()) {
- if (s->_executionStack.back().addr.pc.segment != id)
+ if (s->_executionStack.back().addr.pc.getSegment() != id)
scr->setLockers(1);
}
@@ -273,7 +268,7 @@ reg_t kDisposeScript(EngineState *s, int argc, reg_t *argv) {
}
reg_t kIsObject(EngineState *s, int argc, reg_t *argv) {
- if (argv[0].offset == SIGNAL_OFFSET) // Treated specially
+ if (argv[0].getOffset() == SIGNAL_OFFSET) // Treated specially
return NULL_REG;
else
return make_reg(0, s->_segMan->isHeapObject(argv[0]));
diff --git a/engines/sci/engine/ksound.cpp b/engines/sci/engine/ksound.cpp
index c469f775f9..0633267db4 100644
--- a/engines/sci/engine/ksound.cpp
+++ b/engines/sci/engine/ksound.cpp
@@ -140,8 +140,14 @@ reg_t kDoAudio(EngineState *s, int argc, reg_t *argv) {
((argv[3].toUint16() & 0xff) << 16) |
((argv[4].toUint16() & 0xff) << 8) |
(argv[5].toUint16() & 0xff);
- if (argc == 8)
- warning("kDoAudio: Play called with SQ6 extra parameters");
+ // Removed warning because of the high amount of console spam
+ /*if (argc == 8) {
+ // TODO: Handle the extra 2 SCI21 params
+ // argv[6] is always 1
+ // argv[7] is the contents of global 229 (0xE5)
+ warning("kDoAudio: Play called with SCI2.1 extra parameters: %04x:%04x and %04x:%04x",
+ PRINT_REG(argv[6]), PRINT_REG(argv[7]));
+ }*/
} else {
warning("kDoAudio: Play called with an unknown number of parameters (%d)", argc);
return NULL_REG;
@@ -240,6 +246,11 @@ reg_t kDoAudio(EngineState *s, int argc, reg_t *argv) {
// Used in Pharkas whenever a speech sample starts (takes no params)
//warning("kDoAudio: Unhandled case 13, %d extra arguments passed", argc - 1);
break;
+ case 17:
+ // Seems to be some sort of audio sync, used in SQ6. Silenced the
+ // warning due to the high level of spam it produces. (takes no params)
+ //warning("kDoAudio: Unhandled case 17, %d extra arguments passed", argc - 1);
+ break;
default:
warning("kDoAudio: Unhandled case %d, %d extra arguments passed", argv[0].toUint16(), argc - 1);
}
diff --git a/engines/sci/engine/kstring.cpp b/engines/sci/engine/kstring.cpp
index fe8d631497..c4db0b891c 100644
--- a/engines/sci/engine/kstring.cpp
+++ b/engines/sci/engine/kstring.cpp
@@ -33,7 +33,7 @@ namespace Sci {
reg_t kStrEnd(EngineState *s, int argc, reg_t *argv) {
reg_t address = argv[0];
- address.offset += s->_segMan->strlen(address);
+ address.incOffset(s->_segMan->strlen(address));
return address;
}
@@ -96,7 +96,7 @@ reg_t kStrAt(EngineState *s, int argc, reg_t *argv) {
byte value;
byte newvalue = 0;
- unsigned int offset = argv[1].toUint16();
+ uint16 offset = argv[1].toUint16();
if (argc > 2)
newvalue = argv[2].toSint16();
@@ -123,18 +123,22 @@ reg_t kStrAt(EngineState *s, int argc, reg_t *argv) {
oddOffset = !oddOffset;
if (!oddOffset) {
- value = tmp.offset & 0x00ff;
+ value = tmp.getOffset() & 0x00ff;
if (argc > 2) { /* Request to modify this char */
- tmp.offset &= 0xff00;
- tmp.offset |= newvalue;
- tmp.segment = 0;
+ uint16 tmpOffset = tmp.toUint16();
+ tmpOffset &= 0xff00;
+ tmpOffset |= newvalue;
+ tmp.setOffset(tmpOffset);
+ tmp.setSegment(0);
}
} else {
- value = tmp.offset >> 8;
+ value = tmp.getOffset() >> 8;
if (argc > 2) { /* Request to modify this char */
- tmp.offset &= 0x00ff;
- tmp.offset |= newvalue << 8;
- tmp.segment = 0;
+ uint16 tmpOffset = tmp.toUint16();
+ tmpOffset &= 0x00ff;
+ tmpOffset |= newvalue << 8;
+ tmp.setOffset(tmpOffset);
+ tmp.setSegment(0);
}
}
}
@@ -161,6 +165,7 @@ reg_t kReadNumber(EngineState *s, int argc, reg_t *argv) {
// do clipping. In SQ4 we get the door code in here and that's even
// larger than uint32!
if (*source == '-') {
+ // FIXME: Setting result to -1 does _not_ negate the output.
result = -1;
source++;
}
@@ -204,7 +209,7 @@ reg_t kFormat(EngineState *s, int argc, reg_t *argv) {
int strLength = 0; /* Used for stuff like "%13s" */
bool unsignedVar = false;
- if (position.segment)
+ if (position.getSegment())
startarg = 2;
else {
// WORKAROUND: QFG1 VGA Mac calls this without the first parameter (dest). It then
@@ -291,7 +296,7 @@ reg_t kFormat(EngineState *s, int argc, reg_t *argv) {
if (extralen < 0)
extralen = 0;
- if (reg.segment) /* Heap address? */
+ if (reg.getSegment()) /* Heap address? */
paramindex++;
else
paramindex += 2; /* No, text resource address */
@@ -653,10 +658,16 @@ reg_t kString(EngineState *s, int argc, reg_t *argv) {
case 1: // Size
return make_reg(0, s->_segMan->getString(argv[1]).size());
case 2: { // At (return value at an index)
- if (argv[1].segment == s->_segMan->getStringSegmentId())
- return make_reg(0, s->_segMan->lookupString(argv[1])->getRawData()[argv[2].toUint16()]);
-
- return make_reg(0, s->_segMan->getString(argv[1])[argv[2].toUint16()]);
+ // Note that values are put in bytes to avoid sign extension
+ if (argv[1].getSegment() == s->_segMan->getStringSegmentId()) {
+ SciString *string = s->_segMan->lookupString(argv[1]);
+ byte val = string->getRawData()[argv[2].toUint16()];
+ return make_reg(0, val);
+ } else {
+ Common::String string = s->_segMan->getString(argv[1]);
+ byte val = string[argv[2].toUint16()];
+ return make_reg(0, val);
+ }
}
case 3: { // Atput (put value at an index)
SciString *string = s->_segMan->lookupString(argv[1]);
@@ -699,7 +710,7 @@ reg_t kString(EngineState *s, int argc, reg_t *argv) {
uint32 string2Size = 0;
Common::String string;
- if (argv[3].segment == s->_segMan->getStringSegmentId()) {
+ if (argv[3].getSegment() == s->_segMan->getStringSegmentId()) {
SciString *sstr;
sstr = s->_segMan->lookupString(argv[3]);
string2 = sstr->getRawData();
@@ -749,7 +760,7 @@ reg_t kString(EngineState *s, int argc, reg_t *argv) {
SciString *dupString = s->_segMan->allocateString(&stringHandle);
- if (argv[1].segment == s->_segMan->getStringSegmentId()) {
+ if (argv[1].getSegment() == s->_segMan->getStringSegmentId()) {
*dupString = *s->_segMan->lookupString(argv[1]);
} else {
dupString->fromString(s->_segMan->getString(argv[1]));
diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp
index c9cf652013..9b0cb38f51 100644
--- a/engines/sci/engine/kvideo.cpp
+++ b/engines/sci/engine/kvideo.cpp
@@ -32,6 +32,7 @@
#include "common/str.h"
#include "common/system.h"
#include "common/textconsole.h"
+#include "graphics/palette.h"
#include "graphics/pixelformat.h"
#include "graphics/surface.h"
#include "video/video_decoder.h"
@@ -49,6 +50,8 @@ void playVideo(Video::VideoDecoder *videoDecoder, VideoState videoState) {
if (!videoDecoder)
return;
+ videoDecoder->start();
+
byte *scaleBuffer = 0;
byte bytesPerPixel = videoDecoder->getPixelFormat().bytesPerPixel;
uint16 width = videoDecoder->getWidth();
@@ -86,9 +89,12 @@ void playVideo(Video::VideoDecoder *videoDecoder, VideoState videoState) {
}
bool skipVideo = false;
+ EngineState *s = g_sci->getEngineState();
- if (videoDecoder->hasDirtyPalette())
- videoDecoder->setSystemPalette();
+ if (videoDecoder->hasDirtyPalette()) {
+ const byte *palette = videoDecoder->getPalette() + s->_vmdPalStart * 3;
+ g_system->getPaletteManager()->setPalette(palette, s->_vmdPalStart, s->_vmdPalEnd - s->_vmdPalStart);
+ }
while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) {
if (videoDecoder->needsUpdate()) {
@@ -100,11 +106,13 @@ void playVideo(Video::VideoDecoder *videoDecoder, VideoState videoState) {
g_sci->_gfxScreen->scale2x((byte *)frame->pixels, scaleBuffer, videoDecoder->getWidth(), videoDecoder->getHeight(), bytesPerPixel);
g_system->copyRectToScreen(scaleBuffer, pitch, x, y, width, height);
} else {
- g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, width, height);
+ g_system->copyRectToScreen(frame->pixels, frame->pitch, x, y, width, height);
}
- if (videoDecoder->hasDirtyPalette())
- videoDecoder->setSystemPalette();
+ if (videoDecoder->hasDirtyPalette()) {
+ const byte *palette = videoDecoder->getPalette() + s->_vmdPalStart * 3;
+ g_system->getPaletteManager()->setPalette(palette, s->_vmdPalStart, s->_vmdPalEnd - s->_vmdPalStart);
+ }
g_system->updateScreen();
}
@@ -135,7 +143,7 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {
Video::VideoDecoder *videoDecoder = 0;
- if (argv[0].segment != 0) {
+ if (argv[0].getSegment() != 0) {
Common::String filename = s->_segMan->getString(argv[0]);
if (g_sci->getPlatform() == Common::kPlatformMacintosh) {
@@ -156,9 +164,8 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {
} else {
// DOS SEQ
// SEQ's are called with no subops, just the string and delay
- SeqDecoder *seqDecoder = new SeqDecoder();
- seqDecoder->setFrameDelay(argv[1].toUint16()); // Time between frames in ticks
- videoDecoder = seqDecoder;
+ // Time is specified as ticks
+ videoDecoder = new SEQDecoder(argv[1].toUint16());
if (!videoDecoder->loadFile(filename)) {
warning("Failed to open movie file %s", filename.c_str());
@@ -184,7 +191,7 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {
switch (argv[0].toUint16()) {
case 0: {
Common::String filename = s->_segMan->getString(argv[1]);
- videoDecoder = new Video::AviDecoder(g_system->getMixer());
+ videoDecoder = new Video::AVIDecoder();
if (filename.equalsIgnoreCase("gk2a.avi")) {
// HACK: Switch to 16bpp graphics for Indeo3.
@@ -203,6 +210,8 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {
warning("Failed to open movie file %s", filename.c_str());
delete videoDecoder;
videoDecoder = 0;
+ } else {
+ s->_videoState.fileName = filename;
}
break;
}
@@ -244,6 +253,7 @@ reg_t kRobot(EngineState *s, int argc, reg_t *argv) {
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->start();
g_sci->_robotDecoder->setPos(x, y);
}
break;
@@ -259,12 +269,13 @@ reg_t kRobot(EngineState *s, int argc, reg_t *argv) {
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 {
+ //if (true) { // debug: automatically skip all robot videos
+ if (g_sci->_robotDecoder->endOfVideo()) {
g_sci->_robotDecoder->close();
// Signal the engine scripts that the video is done
writeSelector(s->_segMan, argv[1], SELECTOR(signal), SIGNAL_REG);
+ } else {
+ writeSelector(s->_segMan, argv[1], SELECTOR(signal), NULL_REG);
}
break;
default:
@@ -302,7 +313,7 @@ reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv) {
// with subfx 21. The subtleness has to do with creation of temporary
// planes and positioning relative to such planes.
- uint16 flags = argv[3].offset;
+ uint16 flags = argv[3].getOffset();
Common::String flagspec;
if (argc > 3) {
@@ -330,16 +341,22 @@ reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv) {
s->_videoState.flags = flags;
}
- warning("x, y: %d, %d", argv[1].offset, argv[2].offset);
- s->_videoState.x = argv[1].offset;
- s->_videoState.y = argv[2].offset;
+ warning("x, y: %d, %d", argv[1].getOffset(), argv[2].getOffset());
+ s->_videoState.x = argv[1].getOffset();
+ s->_videoState.y = argv[2].getOffset();
if (argc > 4 && flags & 16)
- warning("gammaBoost: %d%% between palette entries %d and %d", argv[4].offset, argv[5].offset, argv[6].offset);
+ warning("gammaBoost: %d%% between palette entries %d and %d", argv[4].getOffset(), argv[5].getOffset(), argv[6].getOffset());
break;
}
case 6: // Play
- videoDecoder = new Video::VMDDecoder(g_system->getMixer());
+ videoDecoder = new Video::AdvancedVMDDecoder();
+
+ if (s->_videoState.fileName.empty()) {
+ // Happens in Lighthouse
+ warning("kPlayVMD: Empty filename passed");
+ return s->r_acc;
+ }
if (!videoDecoder->loadFile(s->_videoState.fileName)) {
warning("Could not open VMD %s", s->_videoState.fileName.c_str());
@@ -354,6 +371,10 @@ reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv) {
if (reshowCursor)
g_sci->_gfxCursor->kernelShow();
break;
+ case 23: // set video palette range
+ s->_vmdPalStart = argv[1].toUint16();
+ s->_vmdPalEnd = argv[2].toUint16();
+ break;
case 14:
// Takes an additional integer parameter (e.g. 3)
case 16:
@@ -387,7 +408,7 @@ reg_t kPlayDuck(EngineState *s, int argc, reg_t *argv) {
s->_videoState.reset();
s->_videoState.fileName = Common::String::format("%d.duk", argv[1].toUint16());
- videoDecoder = new Video::AviDecoder(g_system->getMixer());
+ videoDecoder = new Video::AVIDecoder();
if (!videoDecoder->loadFile(s->_videoState.fileName)) {
warning("Could not open Duck %s", s->_videoState.fileName.c_str());
diff --git a/engines/sci/engine/message.cpp b/engines/sci/engine/message.cpp
index cddd01e10c..a92d572d35 100644
--- a/engines/sci/engine/message.cpp
+++ b/engines/sci/engine/message.cpp
@@ -400,11 +400,21 @@ Common::String MessageState::processString(const char *s) {
void MessageState::outputString(reg_t buf, const Common::String &str) {
#ifdef ENABLE_SCI32
if (getSciVersion() >= SCI_VERSION_2) {
- SciString *sciString = _segMan->lookupString(buf);
- sciString->setSize(str.size() + 1);
- for (uint32 i = 0; i < str.size(); i++)
- sciString->setValue(i, str.c_str()[i]);
- sciString->setValue(str.size(), 0);
+ if (_segMan->getSegmentType(buf.getSegment()) == SEG_TYPE_STRING) {
+ SciString *sciString = _segMan->lookupString(buf);
+ sciString->setSize(str.size() + 1);
+ for (uint32 i = 0; i < str.size(); i++)
+ sciString->setValue(i, str.c_str()[i]);
+ sciString->setValue(str.size(), 0);
+ } else if (_segMan->getSegmentType(buf.getSegment()) == SEG_TYPE_ARRAY) {
+ // Happens in the intro of LSL6, we are asked to write the string
+ // into an array
+ SciArray<reg_t> *sciString = _segMan->lookupArray(buf);
+ sciString->setSize(str.size() + 1);
+ for (uint32 i = 0; i < str.size(); i++)
+ sciString->setValue(i, make_reg(0, str.c_str()[i]));
+ sciString->setValue(str.size(), NULL_REG);
+ }
} else {
#endif
SegmentRef buffer_r = _segMan->dereference(buf);
diff --git a/engines/sci/engine/object.cpp b/engines/sci/engine/object.cpp
index 78e216cdb5..b28026b71f 100644
--- a/engines/sci/engine/object.cpp
+++ b/engines/sci/engine/object.cpp
@@ -44,15 +44,15 @@ static bool relocateBlock(Common::Array<reg_t> &block, int block_location, Segme
error("Attempt to relocate odd variable #%d.5e (relative to %04x)\n", idx, block_location);
return false;
}
- block[idx].segment = segment; // Perform relocation
+ block[idx].setSegment(segment); // Perform relocation
if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1)
- block[idx].offset += scriptSize;
+ block[idx].incOffset(scriptSize);
return true;
}
void Object::init(byte *buf, reg_t obj_pos, bool initVariables) {
- byte *data = buf + obj_pos.offset;
+ byte *data = buf + obj_pos.getOffset();
_baseObj = data;
_pos = obj_pos;
@@ -109,16 +109,16 @@ int Object::locateVarSelector(SegManager *segMan, Selector slc) const {
}
bool Object::relocateSci0Sci21(SegmentId segment, int location, size_t scriptSize) {
- return relocateBlock(_variables, getPos().offset, segment, location, scriptSize);
+ return relocateBlock(_variables, getPos().getOffset(), segment, location, scriptSize);
}
-bool Object::relocateSci3(SegmentId segment, int location, int offset, size_t scriptSize) {
+bool Object::relocateSci3(SegmentId segment, uint32 location, int offset, size_t scriptSize) {
assert(_propertyOffsetsSci3);
for (uint i = 0; i < _variables.size(); ++i) {
if (location == _propertyOffsetsSci3[i]) {
- _variables[i].segment = segment;
- _variables[i].offset += offset;
+ _variables[i].setSegment(segment);
+ _variables[i].incOffset(offset);
return true;
}
}
@@ -148,21 +148,21 @@ int Object::propertyOffsetToId(SegManager *segMan, int propertyOffset) const {
}
void Object::initSpecies(SegManager *segMan, reg_t addr) {
- uint16 speciesOffset = getSpeciesSelector().offset;
+ uint16 speciesOffset = getSpeciesSelector().getOffset();
if (speciesOffset == 0xffff) // -1
setSpeciesSelector(NULL_REG); // no species
else
- setSpeciesSelector(segMan->getClassAddress(speciesOffset, SCRIPT_GET_LOCK, addr));
+ setSpeciesSelector(segMan->getClassAddress(speciesOffset, SCRIPT_GET_LOCK, addr.getSegment()));
}
void Object::initSuperClass(SegManager *segMan, reg_t addr) {
- uint16 superClassOffset = getSuperClassSelector().offset;
+ uint16 superClassOffset = getSuperClassSelector().getOffset();
if (superClassOffset == 0xffff) // -1
setSuperClassSelector(NULL_REG); // no superclass
else
- setSuperClassSelector(segMan->getClassAddress(superClassOffset, SCRIPT_GET_LOCK, addr));
+ setSuperClassSelector(segMan->getClassAddress(superClassOffset, SCRIPT_GET_LOCK, addr.getSegment()));
}
bool Object::initBaseObject(SegManager *segMan, reg_t addr, bool doInitSuperClass) {
@@ -187,7 +187,7 @@ bool Object::initBaseObject(SegManager *segMan, reg_t addr, bool doInitSuperClas
// The effect is that a number of its method selectors may be
// treated as variable selectors, causing unpredictable effects.
- int objScript = segMan->getScript(_pos.segment)->getScriptNumber();
+ int objScript = segMan->getScript(_pos.getSegment())->getScriptNumber();
// We have to do a little bit of work to get the name of the object
// before any relocations are done.
@@ -196,7 +196,7 @@ bool Object::initBaseObject(SegManager *segMan, reg_t addr, bool doInitSuperClas
if (nameReg.isNull()) {
name = "<no name>";
} else {
- nameReg.segment = _pos.segment;
+ nameReg.setSegment(_pos.getSegment());
name = segMan->derefString(nameReg);
if (!name)
name = "<invalid name>";
@@ -286,7 +286,7 @@ void Object::initSelectorsSci3(const byte *buf) {
_variables.resize(properties);
uint16 *propertyIds = (uint16 *)malloc(sizeof(uint16) * properties);
// uint16 *methodOffsets = (uint16 *)malloc(sizeof(uint16) * 2 * methods);
- uint16 *propertyOffsets = (uint16 *)malloc(sizeof(uint16) * properties);
+ uint32 *propertyOffsets = (uint32 *)malloc(sizeof(uint32) * properties);
int propertyCounter = 0;
int methodCounter = 0;
@@ -314,7 +314,8 @@ void Object::initSelectorsSci3(const byte *buf) {
WRITE_SCI11ENDIAN_UINT16(&propertyIds[propertyCounter],
groupBaseId + bit);
_variables[propertyCounter] = make_reg(0, value);
- propertyOffsets[propertyCounter] = (seeker + bit * 2) - buf;
+ uint32 propertyOffset = (seeker + bit * 2) - buf;
+ propertyOffsets[propertyCounter] = propertyOffset;
++propertyCounter;
} else if (value != 0xffff) { // Method
_baseMethod.push_back(groupBaseId + bit);
diff --git a/engines/sci/engine/object.h b/engines/sci/engine/object.h
index 0ca16b48a2..1ea9ae449a 100644
--- a/engines/sci/engine/object.h
+++ b/engines/sci/engine/object.h
@@ -168,7 +168,7 @@ public:
uint16 offset = (getSciVersion() < SCI_VERSION_1_1) ? _methodCount + 1 + i : i * 2 + 2;
if (getSciVersion() == SCI_VERSION_3)
offset--;
- return make_reg(_pos.segment, _baseMethod[offset]);
+ return make_reg(_pos.getSegment(), _baseMethod[offset]);
}
Selector getFuncSelector(uint16 i) const {
@@ -198,7 +198,7 @@ public:
*/
int locateVarSelector(SegManager *segMan, Selector slc) const;
- bool isClass() const { return (getInfoSelector().offset & kInfoFlagClass); }
+ bool isClass() const { return (getInfoSelector().getOffset() & kInfoFlagClass); }
const Object *getClass(SegManager *segMan) const;
void markAsFreed() { _flags |= OBJECT_FLAG_FREED; }
@@ -223,7 +223,7 @@ public:
}
bool relocateSci0Sci21(SegmentId segment, int location, size_t scriptSize);
- bool relocateSci3(SegmentId segment, int location, int offset, size_t scriptSize);
+ bool relocateSci3(SegmentId segment, uint32 location, int offset, size_t scriptSize);
int propertyOffsetToId(SegManager *segMan, int propertyOffset) const;
@@ -238,7 +238,7 @@ private:
const byte *_baseObj; /**< base + object offset within base */
const uint16 *_baseVars; /**< Pointer to the varselector area for this object */
Common::Array<uint16> _baseMethod; /**< Pointer to the method selector area for this object */
- uint16 *_propertyOffsetsSci3; /**< This is used to enable relocation of property valuesa in SCI3 */
+ uint32 *_propertyOffsetsSci3; /**< This is used to enable relocation of property valuesa in SCI3 */
Common::Array<reg_t> _variables;
uint16 _methodCount;
diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp
index 404bea799d..ff3f19b53d 100644
--- a/engines/sci/engine/savegame.cpp
+++ b/engines/sci/engine/savegame.cpp
@@ -115,8 +115,9 @@ void syncArray(Common::Serializer &s, Common::Array<T> &arr) {
template<>
void syncWithSerializer(Common::Serializer &s, reg_t &obj) {
- s.syncAsUint16LE(obj.segment);
- s.syncAsUint16LE(obj.offset);
+ // Segment and offset are accessed directly here
+ s.syncAsUint16LE(obj._segment);
+ s.syncAsUint16LE(obj._offset);
}
template<>
@@ -189,7 +190,7 @@ void SegManager::saveLoadWithSerializer(Common::Serializer &s) {
assert(mobj);
- // Let the object sync custom data
+ // Let the object sync custom data. Scripts are loaded at this point.
mobj->saveLoadWithSerializer(s);
if (type == SEG_TYPE_SCRIPT) {
@@ -200,12 +201,9 @@ void SegManager::saveLoadWithSerializer(Common::Serializer &s) {
// Hook the script up in the script->segment map
_scriptSegMap[scr->getScriptNumber()] = i;
- // Now, load the script itself
- scr->load(g_sci->getResMan());
-
ObjMap objects = scr->getObjectMap();
for (ObjMap::iterator it = objects.begin(); it != objects.end(); ++it)
- it->_value.syncBaseObject(scr->getBuf(it->_value.getPos().offset));
+ it->_value.syncBaseObject(scr->getBuf(it->_value.getPos().getOffset()));
}
@@ -467,7 +465,7 @@ void Script::syncStringHeap(Common::Serializer &s) {
break;
} while (1);
- } else {
+ } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1){
// Strings in SCI1.1 come after the object instances
byte *buf = _heapStart + 4 + READ_SCI11ENDIAN_UINT16(_heapStart + 2) * 2;
@@ -477,6 +475,8 @@ void Script::syncStringHeap(Common::Serializer &s) {
// Now, sync everything till the end of the buffer
s.syncBytes(buf, _heapSize - (buf - _heapStart));
+ } else if (getSciVersion() == SCI_VERSION_3) {
+ warning("TODO: syncStringHeap(): Implement SCI3 variant");
}
}
@@ -484,7 +484,7 @@ void Script::saveLoadWithSerializer(Common::Serializer &s) {
s.syncAsSint32LE(_nr);
if (s.isLoading())
- init(_nr, g_sci->getResMan());
+ load(_nr, g_sci->getResMan());
s.skip(4, VER(14), VER(22)); // OBSOLETE: Used to be _bufSize
s.skip(4, VER(14), VER(22)); // OBSOLETE: Used to be _scriptSize
s.skip(4, VER(14), VER(22)); // OBSOLETE: Used to be _heapSize
@@ -508,7 +508,7 @@ void Script::saveLoadWithSerializer(Common::Serializer &s) {
Object tmp;
for (uint i = 0; i < numObjs; ++i) {
syncWithSerializer(s, tmp);
- _objects[tmp.getPos().offset] = tmp;
+ _objects[tmp.getPos().getOffset()] = tmp;
}
} else {
ObjMap::iterator it;
@@ -817,7 +817,7 @@ bool gamestate_save(EngineState *s, Common::WriteStream *fh, const Common::Strin
Resource *script0 = g_sci->getResMan()->findResource(ResourceId(kResourceTypeScript, 0), false);
meta.script0Size = script0->size;
- meta.gameObjectOffset = g_sci->getGameObject().offset;
+ meta.gameObjectOffset = g_sci->getGameObject().getOffset();
// Checking here again
if (s->executionStackBase) {
@@ -868,7 +868,7 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) {
if (meta.gameObjectOffset > 0 && meta.script0Size > 0) {
Resource *script0 = g_sci->getResMan()->findResource(ResourceId(kResourceTypeScript, 0), false);
- if (script0->size != meta.script0Size || g_sci->getGameObject().offset != meta.gameObjectOffset) {
+ if (script0->size != meta.script0Size || g_sci->getGameObject().getOffset() != meta.gameObjectOffset) {
//warning("This saved game was created with a different version of the game, unable to load it");
showScummVMDialog("This saved game was created with a different version of the game, unable to load it");
diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp
index 8b26969f4a..36d2841b07 100644
--- a/engines/sci/engine/script.cpp
+++ b/engines/sci/engine/script.cpp
@@ -32,58 +32,48 @@
namespace Sci {
-Script::Script() : SegmentObj(SEG_TYPE_SCRIPT) {
+Script::Script() : SegmentObj(SEG_TYPE_SCRIPT), _buf(NULL) {
+ freeScript();
+}
+
+Script::~Script() {
+ freeScript();
+}
+
+void Script::freeScript() {
_nr = 0;
+
+ free(_buf);
_buf = NULL;
_bufSize = 0;
_scriptSize = 0;
+ _heapStart = NULL;
_heapSize = 0;
- _synonyms = NULL;
- _heapStart = NULL;
_exportTable = NULL;
+ _numExports = 0;
+ _synonyms = NULL;
+ _numSynonyms = 0;
_localsOffset = 0;
_localsSegment = 0;
_localsBlock = NULL;
_localsCount = 0;
+ _lockers = 1;
_markedAsDeleted = false;
+ _objects.clear();
}
-Script::~Script() {
+void Script::load(int script_nr, ResourceManager *resMan) {
freeScript();
-}
-
-void Script::freeScript() {
- free(_buf);
- _buf = NULL;
- _bufSize = 0;
-
- _objects.clear();
-}
-void Script::init(int script_nr, ResourceManager *resMan) {
Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, script_nr), 0);
-
if (!script)
error("Script %d not found", script_nr);
- _localsOffset = 0;
- _localsBlock = NULL;
- _localsCount = 0;
-
- _markedAsDeleted = false;
-
_nr = script_nr;
- _buf = 0;
- _heapStart = 0;
-
- _scriptSize = script->size;
- _bufSize = script->size;
- _heapSize = 0;
-
- _lockers = 1;
+ _bufSize = _scriptSize = script->size;
if (getSciVersion() == SCI_VERSION_0_EARLY) {
_bufSize += READ_LE_UINT16(script->data) * 2;
@@ -115,16 +105,18 @@ void Script::init(int script_nr, ResourceManager *resMan) {
// scheme. We need an overlaying mechanism, or a mechanism to split script parts
// in different segments to handle these. For now, simply stop when such a script
// is found.
+ //
+ // Known large SCI 3 scripts are:
+ // Lighthouse: 9, 220, 270, 351, 360, 490, 760, 765, 800
+ // LSL7: 240, 511, 550
+ // Phantasmagoria 2: none (hooray!)
+ // RAMA: 70
+ //
// TODO: Remove this once such a mechanism is in place
if (script->size > 65535)
error("TODO: SCI script %d is over 64KB - it's %d bytes long. This can't "
"be handled at the moment, thus stopping", script_nr, script->size);
}
-}
-
-void Script::load(ResourceManager *resMan) {
- Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, _nr), 0);
- assert(script != 0);
uint extraLocalsWorkaround = 0;
if (g_sci->getGameId() == GID_FANMADE && _nr == 1 && script->size == 11140) {
@@ -152,15 +144,10 @@ void Script::load(ResourceManager *resMan) {
_heapStart = _buf + _scriptSize;
- assert(_bufSize - _scriptSize <= heap->size);
+ assert(_bufSize - _scriptSize >= heap->size);
memcpy(_heapStart, heap->data, heap->size);
}
- _exportTable = 0;
- _numExports = 0;
- _synonyms = 0;
- _numSynonyms = 0;
-
if (getSciVersion() <= SCI_VERSION_1_LATE) {
_exportTable = (const uint16 *)findBlockSCI0(SCI_OBJ_EXPORTS);
if (_exportTable) {
@@ -212,7 +199,7 @@ void Script::load(ResourceManager *resMan) {
_localsOffset = 0;
if (_localsOffset + _localsCount * 2 + 1 >= (int)_bufSize) {
- error("Locals extend beyond end of script: offset %04x, count %d vs size %d", _localsOffset, _localsCount, _bufSize);
+ error("Locals extend beyond end of script: offset %04x, count %d vs size %d", _localsOffset, _localsCount, (int)_bufSize);
//_localsCount = (_bufSize - _localsOffset) >> 1;
}
}
@@ -252,14 +239,14 @@ const Object *Script::getObject(uint16 offset) const {
Object *Script::scriptObjInit(reg_t obj_pos, bool fullObjectInit) {
if (getSciVersion() < SCI_VERSION_1_1 && fullObjectInit)
- obj_pos.offset += 8; // magic offset (SCRIPT_OBJECT_MAGIC_OFFSET)
+ obj_pos.incOffset(8); // magic offset (SCRIPT_OBJECT_MAGIC_OFFSET)
- if (obj_pos.offset >= _bufSize)
+ if (obj_pos.getOffset() >= _bufSize)
error("Attempt to initialize object beyond end of script");
// Get the object at the specified position and init it. This will
// automatically "allocate" space for it in the _objects map if necessary.
- Object *obj = &_objects[obj_pos.offset];
+ Object *obj = &_objects[obj_pos.getOffset()];
obj->init(_buf, obj_pos, fullObjectInit);
return obj;
@@ -282,9 +269,9 @@ static bool relocateBlock(Common::Array<reg_t> &block, int block_location, Segme
error("Attempt to relocate odd variable #%d.5e (relative to %04x)\n", idx, block_location);
return false;
}
- block[idx].segment = segment; // Perform relocation
+ block[idx].setSegment(segment); // Perform relocation
if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1)
- block[idx].offset += scriptSize;
+ block[idx].incOffset(scriptSize);
return true;
}
@@ -323,23 +310,23 @@ void Script::relocateSci0Sci21(reg_t block) {
heapOffset = _scriptSize;
}
- if (block.offset >= (uint16)heapSize ||
- READ_SCI11ENDIAN_UINT16(heap + block.offset) * 2 + block.offset >= (uint16)heapSize)
+ if (block.getOffset() >= (uint16)heapSize ||
+ READ_SCI11ENDIAN_UINT16(heap + block.getOffset()) * 2 + block.getOffset() >= (uint16)heapSize)
error("Relocation block outside of script");
- int count = READ_SCI11ENDIAN_UINT16(heap + block.offset);
+ int count = READ_SCI11ENDIAN_UINT16(heap + block.getOffset());
int exportIndex = 0;
int pos = 0;
for (int i = 0; i < count; i++) {
- pos = READ_SCI11ENDIAN_UINT16(heap + block.offset + 2 + (exportIndex * 2)) + heapOffset;
+ pos = READ_SCI11ENDIAN_UINT16(heap + block.getOffset() + 2 + (exportIndex * 2)) + heapOffset;
// This occurs in SCI01/SCI1 games where usually one export value is
// zero. It seems that in this situation, we should skip the export and
// move to the next one, though the total count of valid exports remains
// the same
if (!pos) {
exportIndex++;
- pos = READ_SCI11ENDIAN_UINT16(heap + block.offset + 2 + (exportIndex * 2)) + heapOffset;
+ pos = READ_SCI11ENDIAN_UINT16(heap + block.getOffset() + 2 + (exportIndex * 2)) + heapOffset;
if (!pos)
error("Script::relocate(): Consecutive zero exports found");
}
@@ -348,12 +335,12 @@ void Script::relocateSci0Sci21(reg_t block) {
// We only relocate locals and objects here, and ignore relocation of
// code blocks. In SCI1.1 and newer versions, only locals and objects
// are relocated.
- if (!relocateLocal(block.segment, pos)) {
+ if (!relocateLocal(block.getSegment(), pos)) {
// Not a local? It's probably an object or code block. If it's an
// object, relocate it.
const ObjMap::iterator end = _objects.end();
for (ObjMap::iterator it = _objects.begin(); it != end; ++it)
- if (it->_value.relocateSci0Sci21(block.segment, pos, _scriptSize))
+ if (it->_value.relocateSci0Sci21(block.getSegment(), pos, _scriptSize))
break;
}
@@ -370,7 +357,7 @@ void Script::relocateSci3(reg_t block) {
const byte *seeker = relocStart;
while (seeker < _buf + _bufSize) {
// TODO: Find out what UINT16 at (seeker + 8) means
- it->_value.relocateSci3(block.segment,
+ it->_value.relocateSci3(block.getSegment(),
READ_SCI11ENDIAN_UINT32(seeker),
READ_SCI11ENDIAN_UINT32(seeker + 4),
_scriptSize);
@@ -398,7 +385,7 @@ void Script::setLockers(int lockers) {
_lockers = lockers;
}
-uint16 Script::validateExportFunc(int pubfunct, bool relocate) {
+uint32 Script::validateExportFunc(int pubfunct, bool relocSci3) {
bool exportsAreWide = (g_sci->_features->detectLofsType() == SCI_VERSION_1_MIDDLE);
if (_numExports <= pubfunct) {
@@ -409,17 +396,17 @@ uint16 Script::validateExportFunc(int pubfunct, bool relocate) {
if (exportsAreWide)
pubfunct *= 2;
- uint16 offset;
+ uint32 offset;
- if (getSciVersion() != SCI_VERSION_3 || !relocate) {
+ if (getSciVersion() != SCI_VERSION_3) {
offset = READ_SCI11ENDIAN_UINT16(_exportTable + pubfunct);
} else {
- offset = relocateOffsetSci3(pubfunct * 2 + 22);
+ if (!relocSci3)
+ offset = READ_SCI11ENDIAN_UINT16(_exportTable + pubfunct) + getCodeBlockOffsetSci3();
+ else
+ offset = relocateOffsetSci3(pubfunct * 2 + 22);
}
- if (offset >= _bufSize)
- error("Invalid export function pointer");
-
// Check if the offset found points to a second export table (e.g. script 912
// in Camelot and script 306 in KQ4). Such offsets are usually small (i.e. < 10),
// thus easily distinguished from actual code offsets.
@@ -432,11 +419,16 @@ uint16 Script::validateExportFunc(int pubfunct, bool relocate) {
if (secondExportTable) {
secondExportTable += 3; // skip header plus 2 bytes (secondExportTable is a uint16 pointer)
offset = READ_SCI11ENDIAN_UINT16(secondExportTable + pubfunct);
- if (offset >= _bufSize)
- error("Invalid export function pointer");
}
}
+ // Note that it's perfectly normal to return a zero offset, especially in
+ // SCI1.1 and newer games. Examples include script 64036 in Torin's Passage,
+ // script 64908 in the demo of RAMA and script 1013 in KQ6 floppy.
+
+ if (offset >= _bufSize)
+ error("Invalid export function pointer");
+
return offset;
}
@@ -479,7 +471,7 @@ bool Script::isValidOffset(uint16 offset) const {
}
SegmentRef Script::dereference(reg_t pointer) {
- if (pointer.offset > _bufSize) {
+ if (pointer.getOffset() > _bufSize) {
error("Script::dereference(): Attempt to dereference invalid pointer %04x:%04x into script segment (script size=%d)",
PRINT_REG(pointer), (uint)_bufSize);
return SegmentRef();
@@ -487,8 +479,8 @@ SegmentRef Script::dereference(reg_t pointer) {
SegmentRef ret;
ret.isRaw = true;
- ret.maxSize = _bufSize - pointer.offset;
- ret.raw = _buf + pointer.offset;
+ ret.maxSize = _bufSize - pointer.getOffset();
+ ret.raw = _buf + pointer.getOffset();
return ret;
}
@@ -553,7 +545,7 @@ void Script::initializeClasses(SegManager *segMan) {
uint16 marker;
bool isClass = false;
- uint16 classpos;
+ uint32 classpos;
int16 species = 0;
while (true) {
@@ -564,7 +556,7 @@ void Script::initializeClasses(SegManager *segMan) {
if (getSciVersion() <= SCI_VERSION_1_LATE && !marker)
break;
- if (getSciVersion() >= SCI_VERSION_1_1 && marker != 0x1234)
+ if (getSciVersion() >= SCI_VERSION_1_1 && marker != SCRIPT_OBJECT_MAGIC_NUMBER)
break;
if (getSciVersion() <= SCI_VERSION_1_LATE) {
@@ -666,7 +658,7 @@ void Script::initializeObjectsSci11(SegManager *segMan, SegmentId segmentId) {
// Copy base from species class, as we need its selector IDs
obj->setSuperClassSelector(
- segMan->getClassAddress(obj->getSuperClassSelector().offset, SCRIPT_GET_LOCK, NULL_REG));
+ segMan->getClassAddress(obj->getSuperClassSelector().getOffset(), SCRIPT_GET_LOCK, 0));
// If object is instance, get -propDict- from class and set it for this
// object. This is needed for ::isMemberOf() to work.
@@ -699,7 +691,7 @@ void Script::initializeObjectsSci3(SegManager *segMan, SegmentId segmentId) {
reg_t reg = make_reg(segmentId, seeker - _buf);
Object *obj = scriptObjInit(reg);
- obj->setSuperClassSelector(segMan->getClassAddress(obj->getSuperClassSelector().offset, SCRIPT_GET_LOCK, NULL_REG));
+ obj->setSuperClassSelector(segMan->getClassAddress(obj->getSuperClassSelector().getOffset(), SCRIPT_GET_LOCK, 0));
seeker += READ_SCI11ENDIAN_UINT16(seeker + 2);
}
@@ -716,7 +708,7 @@ void Script::initializeObjects(SegManager *segMan, SegmentId segmentId) {
}
reg_t Script::findCanonicAddress(SegManager *segMan, reg_t addr) const {
- addr.offset = 0;
+ addr.setOffset(0);
return addr;
}
@@ -738,8 +730,8 @@ Common::Array<reg_t> Script::listAllDeallocatable(SegmentId segId) const {
Common::Array<reg_t> Script::listAllOutgoingReferences(reg_t addr) const {
Common::Array<reg_t> tmp;
- if (addr.offset <= _bufSize && addr.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET && RAW_IS_OBJECT(_buf + addr.offset)) {
- const Object *obj = getObject(addr.offset);
+ if (addr.getOffset() <= _bufSize && addr.getOffset() >= (uint)-SCRIPT_OBJECT_MAGIC_OFFSET && offsetIsObject(addr.getOffset())) {
+ const Object *obj = getObject(addr.getOffset());
if (obj) {
// Note all local variables, if we have a local variable environment
if (_localsSegment)
@@ -774,4 +766,8 @@ Common::Array<reg_t> Script::listObjectReferences() const {
return tmp;
}
+bool Script::offsetIsObject(uint16 offset) const {
+ return (READ_SCI11ENDIAN_UINT16((const byte *)_buf + offset + SCRIPT_OBJECT_MAGIC_OFFSET) == SCRIPT_OBJECT_MAGIC_NUMBER);
+}
+
} // End of namespace Sci
diff --git a/engines/sci/engine/script.h b/engines/sci/engine/script.h
index 1ebae3b7a8..0b499203c2 100644
--- a/engines/sci/engine/script.h
+++ b/engines/sci/engine/script.h
@@ -57,7 +57,7 @@ private:
int _lockers; /**< Number of classes and objects that require this script */
size_t _scriptSize;
size_t _heapSize;
- uint16 _bufSize;
+ size_t _bufSize;
const uint16 *_exportTable; /**< Abs. offset of the export table or 0 if not present */
uint16 _numExports; /**< Number of entries in the exports table */
@@ -89,14 +89,14 @@ public:
void syncLocalsBlock(SegManager *segMan);
ObjMap &getObjectMap() { return _objects; }
const ObjMap &getObjectMap() const { return _objects; }
+ bool offsetIsObject(uint16 offset) const;
public:
Script();
~Script();
void freeScript();
- void init(int script_nr, ResourceManager *resMan);
- void load(ResourceManager *resMan);
+ void load(int script_nr, ResourceManager *resMan);
void matchSignatureAndPatch(uint16 scriptNr, byte *scriptData, const uint32 scriptSize);
int32 findSignature(const SciScriptSignature *signature, const byte *scriptData, const uint32 scriptSize);
@@ -197,11 +197,11 @@ public:
* Validate whether the specified public function is exported by
* the script in the specified segment.
* @param pubfunct Index of the function to validate
- * @param relocate Decide whether to relocate this public function or not
+ * @param relocSci3 Decide whether to relocate this SCI3 public function or not
* @return NULL if the public function is invalid, its
* offset into the script's segment otherwise
*/
- uint16 validateExportFunc(int pubfunct, bool relocate);
+ uint32 validateExportFunc(int pubfunct, bool relocSci3);
/**
* Marks the script as deleted.
@@ -249,7 +249,7 @@ public:
/**
* Gets an offset to the beginning of the code block in a SCI3 script
*/
- int getCodeBlockOffset() { return READ_SCI11ENDIAN_UINT32(_buf); }
+ int getCodeBlockOffsetSci3() { return READ_SCI11ENDIAN_UINT32(_buf); }
private:
/**
diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp
index 187f1ce021..8454be514a 100644
--- a/engines/sci/engine/script_patches.cpp
+++ b/engines/sci/engine/script_patches.cpp
@@ -775,7 +775,7 @@ const uint16 mothergoose256PatchReplay[] = {
PATCH_END
};
-// when saving, it also checks if the savegame-id is below 13.
+// when saving, it also checks if the savegame ID is below 13.
// we change this to check if below 113 instead
const byte mothergoose256SignatureSaveLimit[] = {
5,
@@ -915,9 +915,53 @@ const uint16 qfg3PatchImportDialog[] = {
PATCH_END
};
+
+
+// ===========================================================================
+// Patch for the Woo dialog option in Uhura's conversation. Bug #3040722
+// Problem: The Woo dialog option (0xffb5) is negative, and therefore
+// treated as an option opening a submenu. This leads to uhuraTell::doChild
+// being called, which calls hero::solvePuzzle and then proceeds with
+// Teller::doChild to open the submenu. However, there is no actual submenu
+// defined for option -75 since -75 does not show up in uhuraTell::keys.
+// This will cause Teller::doChild to run out of bounds while scanning through
+// uhuraTell::keys.
+// Strategy: there is another conversation option in uhuraTell::doChild calling
+// hero::solvePuzzle (0xfffc) which does a ret afterwards without going to
+// Teller::doChild. We jump to this call of hero::solvePuzzle to get that same
+// behaviour.
+
+const byte qfg3SignatureWooDialog[] = {
+ 30,
+ 0x67, 0x12, // pTos 12 (query)
+ 0x35, 0xb6, // ldi b6
+ 0x1a, // eq?
+ 0x2f, 0x05, // bt 05
+ 0x67, 0x12, // pTos 12 (query)
+ 0x35, 0x9b, // ldi 9b
+ 0x1a, // eq?
+ 0x31, 0x0c, // bnt 0c
+ 0x38, 0x97, 0x02, // pushi 0297
+ 0x7a, // push2
+ 0x38, 0x0c, 0x01, // pushi 010c
+ 0x7a, // push2
+ 0x81, 0x00, // lag 00
+ 0x4a, 0x08, // send 08
+ 0x67, 0x12, // pTos 12 (query)
+ 0x35, 0xb5, // ldi b5
+ 0
+};
+
+const uint16 qfg3PatchWooDialog[] = {
+ PATCH_ADDTOOFFSET | +0x29,
+ 0x33, 0x11, // jmp to 0x6a2, the call to hero::solvePuzzle for 0xFFFC
+ PATCH_END
+};
+
// script, description, magic DWORD, adjust
const SciScriptSignature qfg3Signatures[] = {
{ 944, "import dialog continuous calls", 1, PATCH_MAGICDWORD(0x2a, 0x31, 0x0b, 0x7a), -1, qfg3SignatureImportDialog, qfg3PatchImportDialog },
+ { 440, "dialog crash when asking about Woo", 1, PATCH_MAGICDWORD(0x67, 0x12, 0x35, 0xb5), -26, qfg3SignatureWooDialog, qfg3PatchWooDialog },
SCI_SIGNATUREENTRY_TERMINATOR
};
@@ -978,7 +1022,27 @@ const uint16 sq4CdPatchTextOptionsButton[] = {
PATCH_END
};
-// Patch 2: Add the ability to toggle among the three available options,
+// Patch 2: Adjust a check in babbleIcon::init, which handles the babble icon
+// (e.g. the two guys from Andromeda) shown when dying/quitting.
+// Fixes bug #3538418.
+const byte sq4CdSignatureBabbleIcon[] = {
+ 7,
+ 0x89, 0x5a, // lsg 5a
+ 0x35, 0x02, // ldi 02
+ 0x1a, // eq?
+ 0x31, 0x26, // bnt 26 [02a7]
+ 0
+};
+
+const uint16 sq4CdPatchBabbleIcon[] = {
+ 0x89, 0x5a, // lsg 5a
+ 0x35, 0x01, // ldi 01
+ 0x1a, // eq?
+ 0x2f, 0x26, // bt 26 [02a7]
+ PATCH_END
+};
+
+// Patch 3: Add the ability to toggle among the three available options,
// when the text options button is clicked: "Speech", "Text" and "Both".
// Refer to the patch above for additional details.
// iconTextSwitch::doit (called when the text options button is clicked)
@@ -1030,6 +1094,7 @@ const SciScriptSignature sq4Signatures[] = {
{ 298, "Floppy: endless flight", 1, PATCH_MAGICDWORD(0x67, 0x08, 0x63, 0x44), -3, sq4FloppySignatureEndlessFlight, sq4FloppyPatchEndlessFlight },
{ 298, "Floppy (German): endless flight", 1, PATCH_MAGICDWORD(0x67, 0x08, 0x63, 0x4c), -3, sq4FloppySignatureEndlessFlightGerman, sq4FloppyPatchEndlessFlight },
{ 818, "CD: Speech and subtitles option", 1, PATCH_MAGICDWORD(0x89, 0x5a, 0x3c, 0x35), 0, sq4CdSignatureTextOptions, sq4CdPatchTextOptions },
+ { 0, "CD: Babble icon speech and subtitles fix", 1, PATCH_MAGICDWORD(0x89, 0x5a, 0x35, 0x02), 0, sq4CdSignatureBabbleIcon, sq4CdPatchBabbleIcon },
{ 818, "CD: Speech and subtitles option button", 1, PATCH_MAGICDWORD(0x35, 0x01, 0xa1, 0x53), 0, sq4CdSignatureTextOptionsButton, sq4CdPatchTextOptionsButton },
SCI_SIGNATUREENTRY_TERMINATOR
};
diff --git a/engines/sci/engine/scriptdebug.cpp b/engines/sci/engine/scriptdebug.cpp
index ef61b2e28a..b2f22aa985 100644
--- a/engines/sci/engine/scriptdebug.cpp
+++ b/engines/sci/engine/scriptdebug.cpp
@@ -50,7 +50,7 @@ const char *opcodeNames[] = {
"lea", "selfID", "dummy", "pprev", "pToa",
"aTop", "pTos", "sTop", "ipToa", "dpToa",
"ipTos", "dpTos", "lofsa", "lofss", "push0",
- "push1", "push2", "pushSelf", "dummy", "lag",
+ "push1", "push2", "pushSelf", "line", "lag",
"lal", "lat", "lap", "lsg", "lsl",
"lst", "lsp", "lagi", "lali", "lati",
"lapi", "lsgi", "lsli", "lsti", "lspi",
@@ -68,18 +68,18 @@ const char *opcodeNames[] = {
#endif // REDUCE_MEMORY_USAGE
// Disassembles one command from the heap, returns address of next command or 0 if a ret was encountered.
-reg_t disassemble(EngineState *s, reg_t pos, bool printBWTag, bool printBytecode) {
- SegmentObj *mobj = s->_segMan->getSegment(pos.segment, SEG_TYPE_SCRIPT);
+reg_t disassemble(EngineState *s, reg32_t pos, bool printBWTag, bool printBytecode) {
+ SegmentObj *mobj = s->_segMan->getSegment(pos.getSegment(), SEG_TYPE_SCRIPT);
Script *script_entity = NULL;
const byte *scr;
- int scr_size;
- reg_t retval = make_reg(pos.segment, pos.offset + 1);
+ uint32 scr_size;
+ reg_t retval = make_reg(pos.getSegment(), pos.getOffset() + 1);
uint16 param_value = 0xffff; // Suppress GCC warning by setting default value, chose value as invalid to getKernelName etc.
- int i = 0;
+ uint i = 0;
Kernel *kernel = g_sci->getKernel();
if (!mobj) {
- warning("Disassembly failed: Segment %04x non-existant or not a script", pos.segment);
+ warning("Disassembly failed: Segment %04x non-existant or not a script", pos.getSegment());
return retval;
} else
script_entity = (Script *)mobj;
@@ -87,28 +87,37 @@ reg_t disassemble(EngineState *s, reg_t pos, bool printBWTag, bool printBytecode
scr = script_entity->getBuf();
scr_size = script_entity->getBufSize();
- if (pos.offset >= scr_size) {
+ if (pos.getOffset() >= scr_size) {
warning("Trying to disassemble beyond end of script");
return NULL_REG;
}
int16 opparams[4];
byte opsize;
- int bytecount = readPMachineInstruction(scr + pos.offset, opsize, opparams);
+ uint bytecount = readPMachineInstruction(scr + pos.getOffset(), opsize, opparams);
const byte opcode = opsize >> 1;
- opsize &= 1; // byte if true, word if false
-
debugN("%04x:%04x: ", PRINT_REG(pos));
+ if (opcode == op_pushSelf) { // 0x3e (62)
+ if ((opsize & 1) && g_sci->getGameId() != GID_FANMADE) {
+ // Debug opcode op_file
+ debugN("file \"%s\"\n", scr + pos.getOffset() + 1); // +1: op_pushSelf size
+ retval.incOffset(bytecount - 1);
+ return retval;
+ }
+ }
+
+ opsize &= 1; // byte if true, word if false
+
if (printBytecode) {
- if (pos.offset + bytecount > scr_size) {
+ if (pos.getOffset() + bytecount > scr_size) {
warning("Operation arguments extend beyond end of script");
return retval;
}
for (i = 0; i < bytecount; i++)
- debugN("%02x ", scr[pos.offset + i]);
+ debugN("%02x ", scr[pos.getOffset() + i]);
for (i = bytecount; i < 5; i++)
debugN(" ");
@@ -130,16 +139,16 @@ reg_t disassemble(EngineState *s, reg_t pos, bool printBWTag, bool printBytecode
case Script_SByte:
case Script_Byte:
- param_value = scr[retval.offset];
- debugN(" %02x", scr[retval.offset]);
- retval.offset++;
+ param_value = scr[retval.getOffset()];
+ debugN(" %02x", scr[retval.getOffset()]);
+ retval.incOffset(1);
break;
case Script_Word:
case Script_SWord:
- param_value = READ_SCI11ENDIAN_UINT16(&scr[retval.offset]);
- debugN(" %04x", READ_SCI11ENDIAN_UINT16(&scr[retval.offset]));
- retval.offset += 2;
+ param_value = READ_SCI11ENDIAN_UINT16(&scr[retval.getOffset()]);
+ debugN(" %04x", READ_SCI11ENDIAN_UINT16(&scr[retval.getOffset()]));
+ retval.incOffset(2);
break;
case Script_SVariable:
@@ -149,11 +158,12 @@ reg_t disassemble(EngineState *s, reg_t pos, bool printBWTag, bool printBytecode
case Script_Local:
case Script_Temp:
case Script_Param:
- if (opsize)
- param_value = scr[retval.offset++];
- else {
- param_value = READ_SCI11ENDIAN_UINT16(&scr[retval.offset]);
- retval.offset += 2;
+ if (opsize) {
+ param_value = scr[retval.getOffset()];
+ retval.incOffset(1);
+ } else {
+ param_value = READ_SCI11ENDIAN_UINT16(&scr[retval.getOffset()]);
+ retval.incOffset(2);
}
if (opcode == op_callk)
@@ -166,24 +176,26 @@ reg_t disassemble(EngineState *s, reg_t pos, bool printBWTag, bool printBytecode
break;
case Script_Offset:
- if (opsize)
- param_value = scr[retval.offset++];
- else {
- param_value = READ_SCI11ENDIAN_UINT16(&scr[retval.offset]);
- retval.offset += 2;
+ if (opsize) {
+ param_value = scr[retval.getOffset()];
+ retval.incOffset(1);
+ } else {
+ param_value = READ_SCI11ENDIAN_UINT16(&scr[retval.getOffset()]);
+ retval.incOffset(2);
}
debugN(opsize ? " %02x" : " %04x", param_value);
break;
case Script_SRelative:
if (opsize) {
- int8 offset = (int8)scr[retval.offset++];
- debugN(" %02x [%04x]", 0xff & offset, 0xffff & (retval.offset + offset));
+ int8 offset = (int8)scr[retval.getOffset()];
+ retval.incOffset(1);
+ debugN(" %02x [%04x]", 0xff & offset, 0xffff & (retval.getOffset() + offset));
}
else {
- int16 offset = (int16)READ_SCI11ENDIAN_UINT16(&scr[retval.offset]);
- retval.offset += 2;
- debugN(" %04x [%04x]", 0xffff & offset, 0xffff & (retval.offset + offset));
+ int16 offset = (int16)READ_SCI11ENDIAN_UINT16(&scr[retval.getOffset()]);
+ retval.incOffset(2);
+ debugN(" %04x [%04x]", 0xffff & offset, 0xffff & (retval.getOffset() + offset));
}
break;
@@ -216,8 +228,8 @@ reg_t disassemble(EngineState *s, reg_t pos, bool printBWTag, bool printBytecode
if (pos == s->xs->addr.pc) { // Extra information if debugging the current opcode
if (opcode == op_callk) {
- int stackframe = (scr[pos.offset + 2] >> 1) + (s->r_rest);
- int argc = ((s->xs->sp)[- stackframe - 1]).offset;
+ int stackframe = (scr[pos.getOffset() + 2] >> 1) + (s->r_rest);
+ int argc = ((s->xs->sp)[- stackframe - 1]).getOffset();
bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
if (!oldScriptHeader)
@@ -233,13 +245,13 @@ reg_t disassemble(EngineState *s, reg_t pos, bool printBWTag, bool printBytecode
debugN(")\n");
} else if ((opcode == op_send) || (opcode == op_self)) {
int restmod = s->r_rest;
- int stackframe = (scr[pos.offset + 1] >> 1) + restmod;
+ int stackframe = (scr[pos.getOffset() + 1] >> 1) + restmod;
reg_t *sb = s->xs->sp;
uint16 selector;
reg_t fun_ref;
while (stackframe > 0) {
- int argc = sb[- stackframe + 1].offset;
+ int argc = sb[- stackframe + 1].getOffset();
const char *name = NULL;
reg_t called_obj_addr = s->xs->objp;
@@ -248,7 +260,7 @@ reg_t disassemble(EngineState *s, reg_t pos, bool printBWTag, bool printBytecode
else if (opcode == op_self)
called_obj_addr = s->xs->objp;
- selector = sb[- stackframe].offset;
+ selector = sb[- stackframe].getOffset();
name = s->_segMan->getObjectName(called_obj_addr);
@@ -290,20 +302,20 @@ reg_t disassemble(EngineState *s, reg_t pos, bool printBWTag, bool printBytecode
}
bool isJumpOpcode(EngineState *s, reg_t pos, reg_t& jumpTarget) {
- SegmentObj *mobj = s->_segMan->getSegment(pos.segment, SEG_TYPE_SCRIPT);
+ SegmentObj *mobj = s->_segMan->getSegment(pos.getSegment(), SEG_TYPE_SCRIPT);
if (!mobj)
return false;
Script *script_entity = (Script *)mobj;
const byte *scr = script_entity->getBuf();
- int scr_size = script_entity->getScriptSize();
+ uint scr_size = script_entity->getScriptSize();
- if (pos.offset >= scr_size)
+ if (pos.getOffset() >= scr_size)
return false;
int16 opparams[4];
byte opsize;
- int bytecount = readPMachineInstruction(scr + pos.offset, opsize, opparams);
+ int bytecount = readPMachineInstruction(scr + pos.getOffset(), opsize, opparams);
const byte opcode = opsize >> 1;
switch (opcode) {
@@ -313,7 +325,7 @@ bool isJumpOpcode(EngineState *s, reg_t pos, reg_t& jumpTarget) {
{
reg_t jmpTarget = pos + bytecount + opparams[0];
// QFG2 has invalid jumps outside the script buffer in script 260
- if (jmpTarget.offset >= scr_size)
+ if (jmpTarget.getOffset() >= scr_size)
return false;
jumpTarget = jmpTarget;
}
@@ -335,17 +347,17 @@ void SciEngine::scriptDebug() {
}
if (_debugState.seeking != kDebugSeekNothing) {
- const reg_t pc = s->xs->addr.pc;
- SegmentObj *mobj = s->_segMan->getSegment(pc.segment, SEG_TYPE_SCRIPT);
+ const reg32_t pc = s->xs->addr.pc;
+ SegmentObj *mobj = s->_segMan->getSegment(pc.getSegment(), SEG_TYPE_SCRIPT);
if (mobj) {
Script *scr = (Script *)mobj;
const byte *code_buf = scr->getBuf();
- int code_buf_size = scr->getBufSize();
- int opcode = pc.offset >= code_buf_size ? 0 : code_buf[pc.offset];
+ uint16 code_buf_size = scr->getBufSize(); // TODO: change to a 32-bit integer for large SCI3 scripts
+ int opcode = pc.getOffset() >= code_buf_size ? 0 : code_buf[pc.getOffset()];
int op = opcode >> 1;
- int paramb1 = pc.offset + 1 >= code_buf_size ? 0 : code_buf[pc.offset + 1];
- int paramf1 = (opcode & 1) ? paramb1 : (pc.offset + 2 >= code_buf_size ? 0 : (int16)READ_SCI11ENDIAN_UINT16(code_buf + pc.offset + 1));
+ uint16 paramb1 = pc.getOffset() + 1 >= code_buf_size ? 0 : code_buf[pc.getOffset() + 1];
+ uint16 paramf1 = (opcode & 1) ? paramb1 : (pc.getOffset() + 2 >= code_buf_size ? 0 : (int16)READ_SCI11ENDIAN_UINT16(code_buf + pc.getOffset() + 1));
switch (_debugState.seeking) {
case kDebugSeekSpecialCallk:
@@ -502,7 +514,7 @@ void Kernel::dissectScript(int scriptNumber, Vocabulary *vocab) {
if (!objType) {
debugN("End of script object (#0) encountered.\n");
- debugN("Classes: %i, Objects: %i, Export: %i,\n Var: %i (all base 10)",
+ debugN("Classes: %i, Objects: %i, Export: %i,\n Var: %i (all base 10)\n",
objectctr[6], objectctr[1], objectctr[7], objectctr[10]);
return;
}
@@ -515,7 +527,8 @@ void Kernel::dissectScript(int scriptNumber, Vocabulary *vocab) {
_seeker += objsize;
- objectctr[objType]++;
+ if (objType >= 0 && objType < ARRAYSIZE(objectctr))
+ objectctr[objType]++;
switch (objType) {
case SCI_OBJ_OBJECT:
@@ -714,7 +727,7 @@ void logKernelCall(const KernelFunction *kernelCall, const KernelSubFunction *ke
else if (regType & SIG_IS_INVALID)
debugN("INVALID");
else if (regType & SIG_TYPE_INTEGER)
- debugN("%d", argv[parmNr].offset);
+ debugN("%d", argv[parmNr].getOffset());
else {
debugN("%04x:%04x", PRINT_REG(argv[parmNr]));
switch (regType) {
@@ -723,12 +736,12 @@ void logKernelCall(const KernelFunction *kernelCall, const KernelSubFunction *ke
break;
case SIG_TYPE_REFERENCE:
{
- SegmentObj *mobj = s->_segMan->getSegmentObj(argv[parmNr].segment);
+ SegmentObj *mobj = s->_segMan->getSegmentObj(argv[parmNr].getSegment());
switch (mobj->getType()) {
case SEG_TYPE_HUNK:
{
HunkTable *ht = (HunkTable *)mobj;
- int index = argv[parmNr].offset;
+ int index = argv[parmNr].getOffset();
if (ht->isValidEntry(index)) {
// NOTE: This ", deleted" isn't as useful as it could
// be because it prints the status _after_ the kernel
@@ -765,7 +778,7 @@ void logKernelCall(const KernelFunction *kernelCall, const KernelSubFunction *ke
if (result.isPointer())
debugN(" = %04x:%04x\n", PRINT_REG(result));
else
- debugN(" = %d\n", result.offset);
+ debugN(" = %d\n", result.getOffset());
}
} // End of namespace Sci
diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp
index cc127c8dbc..04c1dab158 100644
--- a/engines/sci/engine/seg_manager.cpp
+++ b/engines/sci/engine/seg_manager.cpp
@@ -81,7 +81,7 @@ void SegManager::initSysStrings() {
if (getSciVersion() <= SCI_VERSION_1_1) {
// We need to allocate system strings in one segment, for compatibility reasons
allocDynmem(512, "system strings", &_saveDirPtr);
- _parserPtr = make_reg(_saveDirPtr.segment, _saveDirPtr.offset + 256);
+ _parserPtr = make_reg(_saveDirPtr.getSegment(), _saveDirPtr.getOffset() + 256);
#ifdef ENABLE_SCI32
} else {
SciString *saveDirString = allocateString(&_saveDirPtr);
@@ -143,16 +143,27 @@ Script *SegManager::allocateScript(int script_nr, SegmentId *segid) {
}
void SegManager::deallocate(SegmentId seg) {
- if (!check(seg))
- error("SegManager::deallocate(): invalid segment ID");
+ if (seg < 1 || (uint)seg >= _heap.size())
+ error("Attempt to deallocate an invalid segment ID");
SegmentObj *mobj = _heap[seg];
+ if (!mobj)
+ error("Attempt to deallocate an already freed segment");
if (mobj->getType() == SEG_TYPE_SCRIPT) {
Script *scr = (Script *)mobj;
_scriptSegMap.erase(scr->getScriptNumber());
- if (scr->getLocalsSegment())
- deallocate(scr->getLocalsSegment());
+ if (scr->getLocalsSegment()) {
+ // Check if the locals segment has already been deallocated.
+ // If the locals block has been stored in a segment with an ID
+ // smaller than the segment ID of the script itself, it will be
+ // already freed at this point. This can happen when scripts are
+ // uninstantiated and instantiated again: they retain their own
+ // segment ID, but are allocated a new locals segment, which can
+ // have an ID smaller than the segment of the script itself.
+ if (_heap[scr->getLocalsSegment()])
+ deallocate(scr->getLocalsSegment());
+ }
}
delete mobj;
@@ -163,7 +174,7 @@ bool SegManager::isHeapObject(reg_t pos) const {
const Object *obj = getObject(pos);
if (obj == NULL || (obj && obj->isFreed()))
return false;
- Script *scr = getScriptIfLoaded(pos.segment);
+ Script *scr = getScriptIfLoaded(pos.getSegment());
return !(scr && scr->isMarkedAsDeleted());
}
@@ -214,21 +225,21 @@ SegmentObj *SegManager::getSegment(SegmentId seg, SegmentType type) const {
}
Object *SegManager::getObject(reg_t pos) const {
- SegmentObj *mobj = getSegmentObj(pos.segment);
+ SegmentObj *mobj = getSegmentObj(pos.getSegment());
Object *obj = NULL;
if (mobj != NULL) {
if (mobj->getType() == SEG_TYPE_CLONES) {
CloneTable *ct = (CloneTable *)mobj;
- if (ct->isValidEntry(pos.offset))
- obj = &(ct->_table[pos.offset]);
+ if (ct->isValidEntry(pos.getOffset()))
+ obj = &(ct->_table[pos.getOffset()]);
else
warning("getObject(): Trying to get an invalid object");
} else if (mobj->getType() == SEG_TYPE_SCRIPT) {
Script *scr = (Script *)mobj;
- if (pos.offset <= scr->getBufSize() && pos.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET
- && RAW_IS_OBJECT(scr->getBuf(pos.offset))) {
- obj = scr->getObject(pos.offset);
+ if (pos.getOffset() <= scr->getBufSize() && pos.getOffset() >= (uint)-SCRIPT_OBJECT_MAGIC_OFFSET
+ && scr->offsetIsObject(pos.getOffset())) {
+ obj = scr->getObject(pos.getOffset());
}
}
}
@@ -246,7 +257,7 @@ const char *SegManager::getObjectName(reg_t pos) {
return "<no name>";
const char *name = 0;
- if (nameReg.segment)
+ if (nameReg.getSegment())
name = derefString(nameReg);
if (!name)
return "<invalid name>";
@@ -272,7 +283,7 @@ reg_t SegManager::findObjectByName(const Common::String &name, int index) {
const Script *scr = (const Script *)mobj;
const ObjMap &objects = scr->getObjectMap();
for (ObjMap::const_iterator it = objects.begin(); it != objects.end(); ++it) {
- objpos.offset = it->_value.getPos().offset;
+ objpos.setOffset(it->_value.getPos().getOffset());
if (name == getObjectName(objpos))
result.push_back(objpos);
}
@@ -283,7 +294,7 @@ reg_t SegManager::findObjectByName(const Common::String &name, int index) {
if (!ct->isValidEntry(idx))
continue;
- objpos.offset = idx;
+ objpos.setOffset(idx);
if (name == getObjectName(objpos))
result.push_back(objpos);
}
@@ -307,21 +318,6 @@ reg_t SegManager::findObjectByName(const Common::String &name, int index) {
return result[index];
}
-// validate the seg
-// return:
-// false - invalid seg
-// true - valid seg
-bool SegManager::check(SegmentId seg) {
- if (seg < 1 || (uint)seg >= _heap.size()) {
- return false;
- }
- if (!_heap[seg]) {
- warning("SegManager: seg %x is removed from memory, but not removed from hash_map", seg);
- return false;
- }
- return true;
-}
-
// return the seg if script_id is valid and in the map, else 0
SegmentId SegManager::getScriptSegment(int script_id) const {
return _scriptSegMap.getVal(script_id, 0);
@@ -364,14 +360,14 @@ void SegManager::freeHunkEntry(reg_t addr) {
return;
}
- HunkTable *ht = (HunkTable *)getSegment(addr.segment, SEG_TYPE_HUNK);
+ HunkTable *ht = (HunkTable *)getSegment(addr.getSegment(), SEG_TYPE_HUNK);
if (!ht) {
warning("Attempt to free Hunk from address %04x:%04x: Invalid segment type", PRINT_REG(addr));
return;
}
- ht->freeEntryContents(addr.offset);
+ ht->freeEntryContents(addr.getOffset());
}
reg_t SegManager::allocateHunkEntry(const char *hunk_type, int size) {
@@ -398,14 +394,14 @@ reg_t SegManager::allocateHunkEntry(const char *hunk_type, int size) {
}
byte *SegManager::getHunkPointer(reg_t addr) {
- HunkTable *ht = (HunkTable *)getSegment(addr.segment, SEG_TYPE_HUNK);
+ HunkTable *ht = (HunkTable *)getSegment(addr.getSegment(), SEG_TYPE_HUNK);
- if (!ht || !ht->isValidEntry(addr.offset)) {
+ if (!ht || !ht->isValidEntry(addr.getOffset())) {
// Valid SCI behavior, e.g. when loading/quitting
return NULL;
}
- return (byte *)ht->_table[addr.offset].mem;
+ return (byte *)ht->_table[addr.getOffset()].mem;
}
Clone *SegManager::allocateClone(reg_t *addr) {
@@ -462,35 +458,35 @@ reg_t SegManager::newNode(reg_t value, reg_t key) {
}
List *SegManager::lookupList(reg_t addr) {
- if (getSegmentType(addr.segment) != SEG_TYPE_LISTS) {
+ if (getSegmentType(addr.getSegment()) != SEG_TYPE_LISTS) {
error("Attempt to use non-list %04x:%04x as list", PRINT_REG(addr));
return NULL;
}
- ListTable *lt = (ListTable *)_heap[addr.segment];
+ ListTable *lt = (ListTable *)_heap[addr.getSegment()];
- if (!lt->isValidEntry(addr.offset)) {
+ if (!lt->isValidEntry(addr.getOffset())) {
error("Attempt to use non-list %04x:%04x as list", PRINT_REG(addr));
return NULL;
}
- return &(lt->_table[addr.offset]);
+ return &(lt->_table[addr.getOffset()]);
}
Node *SegManager::lookupNode(reg_t addr, bool stopOnDiscarded) {
if (addr.isNull())
return NULL; // Non-error null
- SegmentType type = getSegmentType(addr.segment);
+ SegmentType type = getSegmentType(addr.getSegment());
if (type != SEG_TYPE_NODES) {
error("Attempt to use non-node %04x:%04x (type %d) as list node", PRINT_REG(addr), type);
return NULL;
}
- NodeTable *nt = (NodeTable *)_heap[addr.segment];
+ NodeTable *nt = (NodeTable *)_heap[addr.getSegment()];
- if (!nt->isValidEntry(addr.offset)) {
+ if (!nt->isValidEntry(addr.getOffset())) {
if (!stopOnDiscarded)
return NULL;
@@ -498,19 +494,19 @@ Node *SegManager::lookupNode(reg_t addr, bool stopOnDiscarded) {
return NULL;
}
- return &(nt->_table[addr.offset]);
+ return &(nt->_table[addr.getOffset()]);
}
SegmentRef SegManager::dereference(reg_t pointer) {
SegmentRef ret;
- if (!pointer.segment || (pointer.segment >= _heap.size()) || !_heap[pointer.segment]) {
+ if (!pointer.getSegment() || (pointer.getSegment() >= _heap.size()) || !_heap[pointer.getSegment()]) {
// This occurs in KQ5CD when interacting with certain objects
warning("SegManager::dereference(): Attempt to dereference invalid pointer %04x:%04x", PRINT_REG(pointer));
return ret; /* Invalid */
}
- SegmentObj *mobj = _heap[pointer.segment];
+ SegmentObj *mobj = _heap[pointer.getSegment()];
return mobj->dereference(pointer);
}
@@ -522,7 +518,7 @@ static void *derefPtr(SegManager *segMan, reg_t pointer, int entries, bool wantR
if (ret.isRaw != wantRaw) {
warning("Dereferencing pointer %04x:%04x (type %d) which is %s, but expected %s", PRINT_REG(pointer),
- segMan->getSegmentType(pointer.segment),
+ segMan->getSegmentType(pointer.getSegment()),
ret.isRaw ? "raw" : "not raw",
wantRaw ? "raw" : "not raw");
}
@@ -565,15 +561,15 @@ static inline char getChar(const SegmentRef &ref, uint offset) {
// segment 0xFFFF means that the scripts are using uninitialized temp-variable space
// we can safely ignore this, if it isn't one of the first 2 chars.
// foreign lsl3 uses kFileIO(readraw) and then immediately uses kReadNumber right at the start
- if (val.segment != 0)
- if (!((val.segment == 0xFFFF) && (offset > 1)))
+ if (val.getSegment() != 0)
+ if (!((val.getSegment() == 0xFFFF) && (offset > 1)))
warning("Attempt to read character from non-raw data");
bool oddOffset = offset & 1;
if (g_sci->isBE())
oddOffset = !oddOffset;
- return (oddOffset ? val.offset >> 8 : val.offset & 0xff);
+ return (oddOffset ? val.getOffset() >> 8 : val.getOffset() & 0xff);
}
static inline void setChar(const SegmentRef &ref, uint offset, byte value) {
@@ -582,16 +578,16 @@ static inline void setChar(const SegmentRef &ref, uint offset, byte value) {
reg_t *val = ref.reg + offset / 2;
- val->segment = 0;
+ val->setSegment(0);
bool oddOffset = offset & 1;
if (g_sci->isBE())
oddOffset = !oddOffset;
if (oddOffset)
- val->offset = (val->offset & 0x00ff) | (value << 8);
+ val->setOffset((val->getOffset() & 0x00ff) | (value << 8));
else
- val->offset = (val->offset & 0xff00) | value;
+ val->setOffset((val->getOffset() & 0xff00) | value);
}
// TODO: memcpy, strcpy and strncpy could maybe be folded into a single function
@@ -829,10 +825,11 @@ byte *SegManager::allocDynmem(int size, const char *descr, reg_t *addr) {
}
bool SegManager::freeDynmem(reg_t addr) {
- if (addr.segment < 1 || addr.segment >= _heap.size() || !_heap[addr.segment] || _heap[addr.segment]->getType() != SEG_TYPE_DYNMEM)
+ if (addr.getSegment() < 1 || addr.getSegment() >= _heap.size() ||
+ !_heap[addr.getSegment()] || _heap[addr.getSegment()]->getType() != SEG_TYPE_DYNMEM)
return false; // error
- deallocate(addr.segment);
+ deallocate(addr.getSegment());
return true; // OK
}
@@ -854,28 +851,28 @@ SciArray<reg_t> *SegManager::allocateArray(reg_t *addr) {
}
SciArray<reg_t> *SegManager::lookupArray(reg_t addr) {
- if (_heap[addr.segment]->getType() != SEG_TYPE_ARRAY)
+ if (_heap[addr.getSegment()]->getType() != SEG_TYPE_ARRAY)
error("Attempt to use non-array %04x:%04x as array", PRINT_REG(addr));
- ArrayTable *arrayTable = (ArrayTable *)_heap[addr.segment];
+ ArrayTable *arrayTable = (ArrayTable *)_heap[addr.getSegment()];
- if (!arrayTable->isValidEntry(addr.offset))
+ if (!arrayTable->isValidEntry(addr.getOffset()))
error("Attempt to use non-array %04x:%04x as array", PRINT_REG(addr));
- return &(arrayTable->_table[addr.offset]);
+ return &(arrayTable->_table[addr.getOffset()]);
}
void SegManager::freeArray(reg_t addr) {
- if (_heap[addr.segment]->getType() != SEG_TYPE_ARRAY)
+ if (_heap[addr.getSegment()]->getType() != SEG_TYPE_ARRAY)
error("Attempt to use non-array %04x:%04x as array", PRINT_REG(addr));
- ArrayTable *arrayTable = (ArrayTable *)_heap[addr.segment];
+ ArrayTable *arrayTable = (ArrayTable *)_heap[addr.getSegment()];
- if (!arrayTable->isValidEntry(addr.offset))
+ if (!arrayTable->isValidEntry(addr.getOffset()))
error("Attempt to use non-array %04x:%04x as array", PRINT_REG(addr));
- arrayTable->_table[addr.offset].destroy();
- arrayTable->freeEntry(addr.offset);
+ arrayTable->_table[addr.getOffset()].destroy();
+ arrayTable->freeEntry(addr.getOffset());
}
SciString *SegManager::allocateString(reg_t *addr) {
@@ -894,28 +891,28 @@ SciString *SegManager::allocateString(reg_t *addr) {
}
SciString *SegManager::lookupString(reg_t addr) {
- if (_heap[addr.segment]->getType() != SEG_TYPE_STRING)
+ if (_heap[addr.getSegment()]->getType() != SEG_TYPE_STRING)
error("lookupString: Attempt to use non-string %04x:%04x as string", PRINT_REG(addr));
- StringTable *stringTable = (StringTable *)_heap[addr.segment];
+ StringTable *stringTable = (StringTable *)_heap[addr.getSegment()];
- if (!stringTable->isValidEntry(addr.offset))
+ if (!stringTable->isValidEntry(addr.getOffset()))
error("lookupString: Attempt to use non-string %04x:%04x as string", PRINT_REG(addr));
- return &(stringTable->_table[addr.offset]);
+ return &(stringTable->_table[addr.getOffset()]);
}
void SegManager::freeString(reg_t addr) {
- if (_heap[addr.segment]->getType() != SEG_TYPE_STRING)
+ if (_heap[addr.getSegment()]->getType() != SEG_TYPE_STRING)
error("freeString: Attempt to use non-string %04x:%04x as string", PRINT_REG(addr));
- StringTable *stringTable = (StringTable *)_heap[addr.segment];
+ StringTable *stringTable = (StringTable *)_heap[addr.getSegment()];
- if (!stringTable->isValidEntry(addr.offset))
+ if (!stringTable->isValidEntry(addr.getOffset()))
error("freeString: Attempt to use non-string %04x:%04x as string", PRINT_REG(addr));
- stringTable->_table[addr.offset].destroy();
- stringTable->freeEntry(addr.offset);
+ stringTable->_table[addr.getOffset()].destroy();
+ stringTable->freeEntry(addr.getOffset());
}
#endif
@@ -939,7 +936,7 @@ void SegManager::createClassTable() {
_resMan->unlockResource(vocab996);
}
-reg_t SegManager::getClassAddress(int classnr, ScriptLoadType lock, reg_t caller) {
+reg_t SegManager::getClassAddress(int classnr, ScriptLoadType lock, uint16 callerSegment) {
if (classnr == 0xffff)
return NULL_REG;
@@ -948,16 +945,16 @@ reg_t SegManager::getClassAddress(int classnr, ScriptLoadType lock, reg_t caller
return NULL_REG;
} else {
Class *the_class = &_classTable[classnr];
- if (!the_class->reg.segment) {
+ if (!the_class->reg.getSegment()) {
getScriptSegment(the_class->script, lock);
- if (!the_class->reg.segment) {
+ if (!the_class->reg.getSegment()) {
error("[VM] Trying to instantiate class %x by instantiating script 0x%x (%03d) failed;", classnr, the_class->script, the_class->script);
return NULL_REG;
}
} else
- if (caller.segment != the_class->reg.segment)
- getScript(the_class->reg.segment)->incrementLockers();
+ if (callerSegment != the_class->reg.getSegment())
+ getScript(the_class->reg.getSegment())->incrementLockers();
return the_class->reg;
}
@@ -977,8 +974,7 @@ int SegManager::instantiateScript(int scriptNum) {
scr = allocateScript(scriptNum, &segmentId);
}
- scr->init(scriptNum, _resMan);
- scr->load(_resMan);
+ scr->load(scriptNum, _resMan);
scr->initializeLocals(this);
scr->initializeClasses(this);
scr->initializeObjects(this, segmentId);
@@ -1003,7 +999,7 @@ void SegManager::uninstantiateScript(int script_nr) {
// Free all classtable references to this script
for (uint i = 0; i < classTableSize(); i++)
- if (getClass(i).reg.segment == segmentId)
+ if (getClass(i).reg.getSegment() == segmentId)
setClassOffset(i, NULL_REG);
if (getSciVersion() < SCI_VERSION_1_1)
@@ -1027,18 +1023,18 @@ void SegManager::uninstantiateScriptSci0(int script_nr) {
// Make a pass over the object in order to uninstantiate all superclasses
do {
- reg.offset += objLength; // Step over the last checked object
+ reg.incOffset(objLength); // Step over the last checked object
- objType = READ_SCI11ENDIAN_UINT16(scr->getBuf(reg.offset));
+ objType = READ_SCI11ENDIAN_UINT16(scr->getBuf(reg.getOffset()));
if (!objType)
break;
- objLength = READ_SCI11ENDIAN_UINT16(scr->getBuf(reg.offset + 2));
+ objLength = READ_SCI11ENDIAN_UINT16(scr->getBuf(reg.getOffset() + 2));
- reg.offset += 4; // Step over header
+ reg.incOffset(4); // Step over header
if ((objType == SCI_OBJ_OBJECT) || (objType == SCI_OBJ_CLASS)) { // object or class?
- reg.offset += 8; // magic offset (SCRIPT_OBJECT_MAGIC_OFFSET)
- int16 superclass = READ_SCI11ENDIAN_UINT16(scr->getBuf(reg.offset + 2));
+ reg.incOffset(8); // magic offset (SCRIPT_OBJECT_MAGIC_OFFSET)
+ int16 superclass = READ_SCI11ENDIAN_UINT16(scr->getBuf(reg.getOffset() + 2));
if (superclass >= 0) {
int superclass_script = getClass(superclass).script;
@@ -1052,10 +1048,10 @@ void SegManager::uninstantiateScriptSci0(int script_nr) {
// Recurse to assure that the superclass lockers number gets decreased
}
- reg.offset += SCRIPT_OBJECT_MAGIC_OFFSET;
+ reg.incOffset(SCRIPT_OBJECT_MAGIC_OFFSET);
} // if object or class
- reg.offset -= 4; // Step back on header
+ reg.incOffset(-4); // Step back on header
} while (objType != 0);
}
diff --git a/engines/sci/engine/seg_manager.h b/engines/sci/engine/seg_manager.h
index 62e711e686..074d3f6b0a 100644
--- a/engines/sci/engine/seg_manager.h
+++ b/engines/sci/engine/seg_manager.h
@@ -125,7 +125,7 @@ private:
public:
// TODO: document this
- reg_t getClassAddress(int classnr, ScriptLoadType lock, reg_t caller);
+ reg_t getClassAddress(int classnr, ScriptLoadType lock, uint16 callerSegment);
/**
* Return a pointer to the specified script.
@@ -471,14 +471,6 @@ private:
void createClassTable();
SegmentId findFreeSegment() const;
-
- /**
- * Check segment validity
- * @param[in] seg The segment to validate
- * @return false if 'seg' is an invalid segment, true if
- * 'seg' is a valid segment
- */
- bool check(SegmentId seg);
};
} // End of namespace Sci
diff --git a/engines/sci/engine/segment.cpp b/engines/sci/engine/segment.cpp
index 36b7d92c07..a7f147a15a 100644
--- a/engines/sci/engine/segment.cpp
+++ b/engines/sci/engine/segment.cpp
@@ -93,11 +93,11 @@ Common::Array<reg_t> CloneTable::listAllOutgoingReferences(reg_t addr) const {
Common::Array<reg_t> tmp;
// assert(addr.segment == _segId);
- if (!isValidEntry(addr.offset)) {
+ if (!isValidEntry(addr.getOffset())) {
error("Unexpected request for outgoing references from clone at %04x:%04x", PRINT_REG(addr));
}
- const Clone *clone = &(_table[addr.offset]);
+ const Clone *clone = &(_table[addr.getOffset()]);
// Emit all member variables (including references to the 'super' delegate)
for (uint i = 0; i < clone->getVarCount(); i++)
@@ -112,7 +112,7 @@ Common::Array<reg_t> CloneTable::listAllOutgoingReferences(reg_t addr) const {
void CloneTable::freeAtAddress(SegManager *segMan, reg_t addr) {
#ifdef GC_DEBUG
- Object *victim_obj = &(_table[addr.offset]);
+ Object *victim_obj = &(_table[addr.getOffset()]);
if (!(victim_obj->_flags & OBJECT_FLAG_FREED))
warning("[GC] Clone %04x:%04x not reachable and not freed (freeing now)", PRINT_REG(addr));
@@ -124,7 +124,7 @@ void CloneTable::freeAtAddress(SegManager *segMan, reg_t addr) {
#endif
#endif
- freeEntry(addr.offset);
+ freeEntry(addr.getOffset());
}
@@ -133,15 +133,15 @@ void CloneTable::freeAtAddress(SegManager *segMan, reg_t addr) {
SegmentRef LocalVariables::dereference(reg_t pointer) {
SegmentRef ret;
ret.isRaw = false; // reg_t based data!
- ret.maxSize = (_locals.size() - pointer.offset / 2) * 2;
+ ret.maxSize = (_locals.size() - pointer.getOffset() / 2) * 2;
- if (pointer.offset & 1) {
+ if (pointer.getOffset() & 1) {
ret.maxSize -= 1;
ret.skipByte = true;
}
if (ret.maxSize > 0) {
- ret.reg = &_locals[pointer.offset / 2];
+ ret.reg = &_locals[pointer.getOffset() / 2];
} else {
if ((g_sci->getEngineState()->currentRoomNumber() == 160 ||
g_sci->getEngineState()->currentRoomNumber() == 220)
@@ -181,14 +181,14 @@ Common::Array<reg_t> LocalVariables::listAllOutgoingReferences(reg_t addr) const
SegmentRef DataStack::dereference(reg_t pointer) {
SegmentRef ret;
ret.isRaw = false; // reg_t based data!
- ret.maxSize = (_capacity - pointer.offset / 2) * 2;
+ ret.maxSize = (_capacity - pointer.getOffset() / 2) * 2;
- if (pointer.offset & 1) {
+ if (pointer.getOffset() & 1) {
ret.maxSize -= 1;
ret.skipByte = true;
}
- ret.reg = &_entries[pointer.offset / 2];
+ ret.reg = &_entries[pointer.getOffset() / 2];
return ret;
}
@@ -204,11 +204,11 @@ Common::Array<reg_t> DataStack::listAllOutgoingReferences(reg_t object) const {
Common::Array<reg_t> ListTable::listAllOutgoingReferences(reg_t addr) const {
Common::Array<reg_t> tmp;
- if (!isValidEntry(addr.offset)) {
+ if (!isValidEntry(addr.getOffset())) {
error("Invalid list referenced for outgoing references: %04x:%04x", PRINT_REG(addr));
}
- const List *list = &(_table[addr.offset]);
+ const List *list = &(_table[addr.getOffset()]);
tmp.push_back(list->first);
tmp.push_back(list->last);
@@ -222,10 +222,10 @@ Common::Array<reg_t> ListTable::listAllOutgoingReferences(reg_t addr) const {
Common::Array<reg_t> NodeTable::listAllOutgoingReferences(reg_t addr) const {
Common::Array<reg_t> tmp;
- if (!isValidEntry(addr.offset)) {
+ if (!isValidEntry(addr.getOffset())) {
error("Invalid node referenced for outgoing references: %04x:%04x", PRINT_REG(addr));
}
- const Node *node = &(_table[addr.offset]);
+ const Node *node = &(_table[addr.getOffset()]);
// We need all four here. Can't just stick with 'pred' OR 'succ' because node operations allow us
// to walk around from any given node
@@ -242,8 +242,8 @@ Common::Array<reg_t> NodeTable::listAllOutgoingReferences(reg_t addr) const {
SegmentRef DynMem::dereference(reg_t pointer) {
SegmentRef ret;
ret.isRaw = true;
- ret.maxSize = _size - pointer.offset;
- ret.raw = _buf + pointer.offset;
+ ret.maxSize = _size - pointer.getOffset();
+ ret.raw = _buf + pointer.getOffset();
return ret;
}
@@ -252,27 +252,27 @@ SegmentRef DynMem::dereference(reg_t pointer) {
SegmentRef ArrayTable::dereference(reg_t pointer) {
SegmentRef ret;
ret.isRaw = false;
- ret.maxSize = _table[pointer.offset].getSize() * 2;
- ret.reg = _table[pointer.offset].getRawData();
+ ret.maxSize = _table[pointer.getOffset()].getSize() * 2;
+ ret.reg = _table[pointer.getOffset()].getRawData();
return ret;
}
void ArrayTable::freeAtAddress(SegManager *segMan, reg_t sub_addr) {
- _table[sub_addr.offset].destroy();
- freeEntry(sub_addr.offset);
+ _table[sub_addr.getOffset()].destroy();
+ freeEntry(sub_addr.getOffset());
}
Common::Array<reg_t> ArrayTable::listAllOutgoingReferences(reg_t addr) const {
Common::Array<reg_t> tmp;
- if (!isValidEntry(addr.offset)) {
+ if (!isValidEntry(addr.getOffset())) {
error("Invalid array referenced for outgoing references: %04x:%04x", PRINT_REG(addr));
}
- const SciArray<reg_t> *array = &(_table[addr.offset]);
+ const SciArray<reg_t> *array = &(_table[addr.getOffset()]);
for (uint32 i = 0; i < array->getSize(); i++) {
reg_t value = array->getValue(i);
- if (value.segment != 0)
+ if (value.getSegment() != 0)
tmp.push_back(value);
}
@@ -305,8 +305,8 @@ void SciString::fromString(const Common::String &string) {
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.maxSize = _table[pointer.getOffset()].getSize();
+ ret.raw = (byte *)_table[pointer.getOffset()].getRawData();
return ret;
}
diff --git a/engines/sci/engine/segment.h b/engines/sci/engine/segment.h
index 54cf7b98af..0d54b651e1 100644
--- a/engines/sci/engine/segment.h
+++ b/engines/sci/engine/segment.h
@@ -175,7 +175,7 @@ public:
}
virtual SegmentRef dereference(reg_t pointer);
virtual reg_t findCanonicAddress(SegManager *segMan, reg_t addr) const {
- return make_reg(addr.segment, 0);
+ return make_reg(addr.getSegment(), 0);
}
virtual Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const;
@@ -291,7 +291,7 @@ struct NodeTable : public SegmentObjTable<Node> {
NodeTable() : SegmentObjTable<Node>(SEG_TYPE_NODES) {}
virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr) {
- freeEntry(sub_addr.offset);
+ freeEntry(sub_addr.getOffset());
}
virtual Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const;
@@ -304,7 +304,7 @@ struct ListTable : public SegmentObjTable<List> {
ListTable() : SegmentObjTable<List>(SEG_TYPE_LISTS) {}
virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr) {
- freeEntry(sub_addr.offset);
+ freeEntry(sub_addr.getOffset());
}
virtual Common::Array<reg_t> listAllOutgoingReferences(reg_t object) const;
@@ -333,7 +333,7 @@ struct HunkTable : public SegmentObjTable<Hunk> {
}
virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr) {
- freeEntry(sub_addr.offset);
+ freeEntry(sub_addr.getOffset());
}
virtual void saveLoadWithSerializer(Common::Serializer &ser);
@@ -358,7 +358,7 @@ public:
}
virtual SegmentRef dereference(reg_t pointer);
virtual reg_t findCanonicAddress(SegManager *segMan, reg_t addr) const {
- return make_reg(addr.segment, 0);
+ return make_reg(addr.getSegment(), 0);
}
virtual Common::Array<reg_t> listAllDeallocatable(SegmentId segId) const {
const reg_t r = make_reg(segId, 0);
@@ -502,8 +502,8 @@ struct StringTable : public SegmentObjTable<SciString> {
StringTable() : SegmentObjTable<SciString>(SEG_TYPE_STRING) {}
virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr) {
- _table[sub_addr.offset].destroy();
- freeEntry(sub_addr.offset);
+ _table[sub_addr.getOffset()].destroy();
+ freeEntry(sub_addr.getOffset());
}
void saveLoadWithSerializer(Common::Serializer &ser);
diff --git a/engines/sci/engine/selector.cpp b/engines/sci/engine/selector.cpp
index a8b1cf7ec2..2f6b4d58dd 100644
--- a/engines/sci/engine/selector.cpp
+++ b/engines/sci/engine/selector.cpp
@@ -181,6 +181,7 @@ void Kernel::mapSelectors() {
FIND_SELECTOR(skip);
FIND_SELECTOR(fixPriority);
FIND_SELECTOR(mirrored);
+ FIND_SELECTOR(visible);
FIND_SELECTOR(useInsetRect);
FIND_SELECTOR(inTop);
FIND_SELECTOR(inLeft);
diff --git a/engines/sci/engine/selector.h b/engines/sci/engine/selector.h
index 4b913a866a..5d3d0752ac 100644
--- a/engines/sci/engine/selector.h
+++ b/engines/sci/engine/selector.h
@@ -149,6 +149,7 @@ struct SelectorCache {
Selector fixPriority;
Selector mirrored;
+ Selector visible;
Selector useInsetRect;
Selector inTop, inLeft, inBottom, inRight;
@@ -170,7 +171,7 @@ struct SelectorCache {
* SelectorCache and mapped in script.cpp.
*/
reg_t readSelector(SegManager *segMan, reg_t object, Selector selectorId);
-#define readSelectorValue(segMan, _obj_, _slc_) (readSelector(segMan, _obj_, _slc_).offset)
+#define readSelectorValue(segMan, _obj_, _slc_) (readSelector(segMan, _obj_, _slc_).getOffset())
/**
* Writes a selector value to an object.
diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp
index 28818cddef..0f0c8dcd66 100644
--- a/engines/sci/engine/state.cpp
+++ b/engines/sci/engine/state.cpp
@@ -26,6 +26,7 @@
#include "sci/debug.h" // for g_debug_sleeptime_factor
#include "sci/event.h"
+#include "sci/engine/file.h"
#include "sci/engine/kernel.h"
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
@@ -68,21 +69,26 @@ static const uint16 s_halfWidthSJISMap[256] = {
};
EngineState::EngineState(SegManager *segMan)
-: _segMan(segMan), _dirseeker() {
+: _segMan(segMan),
+#ifdef ENABLE_SCI32
+ _virtualIndexFile(0),
+#endif
+ _dirseeker() {
reset(false);
}
EngineState::~EngineState() {
delete _msgState;
+#ifdef ENABLE_SCI32
+ delete _virtualIndexFile;
+#endif
}
void EngineState::reset(bool isRestoring) {
if (!isRestoring) {
_memorySegmentSize = 0;
-
_fileHandles.resize(5);
-
abortScriptProcessing = kAbortNone;
}
@@ -116,6 +122,11 @@ void EngineState::reset(bool isRestoring) {
_videoState.reset();
_syncedAudioOptions = false;
+
+ _vmdPalStart = 0;
+ _vmdPalEnd = 256;
+
+ _palCycleToColor = 255;
}
void EngineState::speedThrottler(uint32 neededSleep) {
diff --git a/engines/sci/engine/state.h b/engines/sci/engine/state.h
index dcffe6dbb3..81090876c7 100644
--- a/engines/sci/engine/state.h
+++ b/engines/sci/engine/state.h
@@ -34,6 +34,7 @@ class WriteStream;
}
#include "sci/sci.h"
+#include "sci/engine/file.h"
#include "sci/engine/seg_manager.h"
#include "sci/parser/vocabulary.h"
@@ -42,9 +43,12 @@ class WriteStream;
namespace Sci {
+class FileHandle;
+class DirSeeker;
class EventManager;
class MessageState;
class SoundCommandParser;
+class VirtualIndexFile;
enum AbortGameState {
kAbortNone = 0,
@@ -53,32 +57,6 @@ enum AbortGameState {
kAbortQuitGame = 3
};
-class DirSeeker {
-protected:
- reg_t _outbuffer;
- Common::StringArray _files;
- Common::StringArray _virtualFiles;
- Common::StringArray::const_iterator _iter;
-
-public:
- DirSeeker() {
- _outbuffer = NULL_REG;
- _iter = _files.begin();
- }
-
- reg_t firstFile(const Common::String &mask, reg_t buffer, SegManager *segMan);
- reg_t nextFile(SegManager *segMan);
-
- Common::String getVirtualFilename(uint fileNumber);
-
-private:
- void addAsVirtualFiles(Common::String title, Common::String fileMask);
-};
-
-enum {
- MAX_SAVEGAME_NR = 20 /**< Maximum number of savegames */
-};
-
// We assume that scripts give us savegameId 0->99 for creating a new save slot
// and savegameId 100->199 for existing save slots ffs. kfile.cpp
enum {
@@ -92,20 +70,6 @@ enum {
GAMEISRESTARTING_RESTORE = 2
};
-class FileHandle {
-public:
- Common::String _name;
- Common::SeekableReadStream *_in;
- Common::WriteStream *_out;
-
-public:
- FileHandle();
- ~FileHandle();
-
- void close();
- bool isOpen() const;
-};
-
enum VideoFlags {
kNone = 0,
kDoubled = 1 << 0,
@@ -163,6 +127,10 @@ public:
int16 _lastSaveVirtualId; // last virtual id fed to kSaveGame, if no kGetSaveFiles was called inbetween
int16 _lastSaveNewId; // last newly created filename-id by kSaveGame
+#ifdef ENABLE_SCI32
+ VirtualIndexFile *_virtualIndexFile;
+#endif
+
uint _chosenQfGImportItem; // Remembers the item selected in QfG import rooms
bool _cursorWorkaroundActive; // ffs. GfxCursor::setPosition()
@@ -228,8 +196,11 @@ public:
byte _memorySegment[kMemorySegmentMax];
VideoState _videoState;
+ uint16 _vmdPalStart, _vmdPalEnd;
bool _syncedAudioOptions;
+ uint16 _palCycleToColor;
+
/**
* Resets the engine state.
*/
diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp
index 162dce9fcc..ef8f165084 100644
--- a/engines/sci/engine/vm.cpp
+++ b/engines/sci/engine/vm.cpp
@@ -119,7 +119,7 @@ extern const char *opcodeNames[]; // from scriptdebug.cpp
static reg_t read_var(EngineState *s, int type, int index) {
if (validate_variable(s->variables[type], s->stack_base, type, s->variablesMax[type], index)) {
- if (s->variables[type][index].segment == 0xffff) {
+ if (s->variables[type][index].getSegment() == 0xffff) {
switch (type) {
case VAR_TEMP: {
// Uninitialized read on a temp
@@ -195,8 +195,8 @@ static void write_var(EngineState *s, int type, int index, reg_t value) {
// this happens at least in sq1/room 44 (slot-machine), because a send is missing parameters, then
// those parameters are taken from uninitialized stack and afterwards they are copied back into temps
// if we don't remove the segment, we would get false-positive uninitialized reads later
- if (type == VAR_TEMP && value.segment == 0xffff)
- value.segment = 0;
+ if (type == VAR_TEMP && value.getSegment() == 0xffff)
+ value.setSegment(0);
s->variables[type][index] = value;
@@ -225,30 +225,15 @@ ExecStack *execute_method(EngineState *s, uint16 script, uint16 pubfunct, StackP
scr = s->_segMan->getScript(seg);
}
- int temp = scr->validateExportFunc(pubfunct, false);
-
- if (getSciVersion() == SCI_VERSION_3)
- temp += scr->getCodeBlockOffset();
-
- if (!temp) {
-#ifdef ENABLE_SCI32
- 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);
+ uint32 exportAddr = scr->validateExportFunc(pubfunct, false);
+ if (!exportAddr)
return NULL;
- }
// Check if a breakpoint is set on this method
g_sci->checkExportBreakpoint(script, pubfunct);
ExecStack xstack(calling_obj, calling_obj, sp, argc, argp,
- seg, make_reg(seg, temp), -1, pubfunct, -1,
+ seg, make_reg32(seg, exportAddr), -1, pubfunct, -1,
s->_executionStack.size() - 1, EXEC_STACK_TYPE_CALL);
s->_executionStack.push_back(xstack);
return &(s->_executionStack.back());
@@ -304,11 +289,12 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt
ExecStackType stackType = EXEC_STACK_TYPE_VARSELECTOR;
StackPtr curSP = NULL;
- reg_t curFP = NULL_REG;
+ reg32_t curFP = make_reg32(0, 0);
if (selectorType == kSelectorMethod) {
stackType = EXEC_STACK_TYPE_CALL;
curSP = sp;
- curFP = funcp;
+ // TODO: Will this offset suffice for large SCI3 scripts?
+ curFP = make_reg32(funcp.getSegment(), funcp.getOffset());
sp = CALL_SP_CARRY; // Destroy sp, as it will be carried over
}
@@ -342,7 +328,7 @@ static void addKernelCallToExecStack(EngineState *s, int kernelCallNr, int argc,
// Add stack frame to indicate we're executing a callk.
// This is useful in debugger backtraces if this
// kernel function calls a script itself.
- ExecStack xstack(NULL_REG, NULL_REG, NULL, argc, argv - 1, 0xFFFF, NULL_REG,
+ ExecStack xstack(NULL_REG, NULL_REG, NULL, argc, argv - 1, 0xFFFF, make_reg32(0, 0),
kernelCallNr, -1, -1, s->_executionStack.size() - 1, EXEC_STACK_TYPE_KERNEL);
s->_executionStack.push_back(xstack);
}
@@ -578,16 +564,16 @@ void run_vm(EngineState *s) {
int var_type; // See description below
int var_number;
- g_sci->_debugState.old_pc_offset = s->xs->addr.pc.offset;
+ g_sci->_debugState.old_pc_offset = s->xs->addr.pc.getOffset();
g_sci->_debugState.old_sp = s->xs->sp;
if (s->abortScriptProcessing != kAbortNone)
return; // Stop processing
if (s->_executionStackPosChanged) {
- scr = s->_segMan->getScriptIfLoaded(s->xs->addr.pc.segment);
+ scr = s->_segMan->getScriptIfLoaded(s->xs->addr.pc.getSegment());
if (!scr)
- error("No script in segment %d", s->xs->addr.pc.segment);
+ error("No script in segment %d", s->xs->addr.pc.getSegment());
s->xs = &(s->_executionStack.back());
s->_executionStackPosChanged = false;
@@ -624,13 +610,13 @@ void run_vm(EngineState *s) {
s->variablesMax[VAR_TEMP] = s->xs->sp - s->xs->fp;
- if (s->xs->addr.pc.offset >= scr->getBufSize())
+ if (s->xs->addr.pc.getOffset() >= scr->getBufSize())
error("run_vm(): program counter gone astray, addr: %d, code buffer size: %d",
- s->xs->addr.pc.offset, scr->getBufSize());
+ s->xs->addr.pc.getOffset(), scr->getBufSize());
// Get opcode
byte extOpcode;
- s->xs->addr.pc.offset += readPMachineInstruction(scr->getBuf() + s->xs->addr.pc.offset, extOpcode, opparams);
+ s->xs->addr.pc.incOffset(readPMachineInstruction(scr->getBuf(s->xs->addr.pc.getOffset()), extOpcode, opparams));
const byte opcode = extOpcode >> 1;
//debug("%s: %d, %d, %d, %d, acc = %04x:%04x, script %d, local script %d", opcodeNames[opcode], opparams[0], opparams[1], opparams[2], opparams[3], PRINT_REG(s->r_acc), scr->getScriptNumber(), local_script->getScriptNumber());
@@ -705,7 +691,7 @@ void run_vm(EngineState *s) {
break;
case op_not: // 0x0c (12)
- s->r_acc = make_reg(0, !(s->r_acc.offset || s->r_acc.segment));
+ s->r_acc = make_reg(0, !(s->r_acc.getOffset() || s->r_acc.getSegment()));
// Must allow pointers to be negated, as this is used for checking whether objects exist
break;
@@ -765,30 +751,30 @@ void run_vm(EngineState *s) {
case op_bt: // 0x17 (23)
// Branch relative if true
- if (s->r_acc.offset || s->r_acc.segment)
- s->xs->addr.pc.offset += opparams[0];
+ if (s->r_acc.getOffset() || s->r_acc.getSegment())
+ s->xs->addr.pc.incOffset(opparams[0]);
- if (s->xs->addr.pc.offset >= local_script->getScriptSize())
+ if (s->xs->addr.pc.getOffset() >= 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());
+ local_script->getScriptNumber(), s->xs->addr.pc.getOffset(), 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->r_acc.getOffset() || s->r_acc.getSegment()))
+ s->xs->addr.pc.incOffset(opparams[0]);
- if (s->xs->addr.pc.offset >= local_script->getScriptSize())
+ if (s->xs->addr.pc.getOffset() >= 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());
+ local_script->getScriptNumber(), s->xs->addr.pc.getOffset(), local_script->getScriptSize());
break;
case op_jmp: // 0x19 (25)
- s->xs->addr.pc.offset += opparams[0];
+ s->xs->addr.pc.incOffset(opparams[0]);
- if (s->xs->addr.pc.offset >= local_script->getScriptSize())
+ if (s->xs->addr.pc.getOffset() >= 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());
+ local_script->getScriptNumber(), s->xs->addr.pc.getOffset(), local_script->getScriptSize());
break;
case op_ldi: // 0x1a (26)
@@ -831,13 +817,13 @@ void run_vm(EngineState *s) {
int argc = (opparams[1] >> 1) // Given as offset, but we need count
+ 1 + s->r_rest;
StackPtr call_base = s->xs->sp - argc;
- s->xs->sp[1].offset += s->r_rest;
+ s->xs->sp[1].incOffset(s->r_rest);
- uint16 localCallOffset = s->xs->addr.pc.offset + opparams[0];
+ uint32 localCallOffset = s->xs->addr.pc.getOffset() + opparams[0];
ExecStack xstack(s->xs->objp, s->xs->objp, s->xs->sp,
(call_base->requireUint16()) + s->r_rest, call_base,
- s->xs->local_segment, make_reg(s->xs->addr.pc.segment, localCallOffset),
+ s->xs->local_segment, make_reg32(s->xs->addr.pc.getSegment(), localCallOffset),
NULL_SELECTOR, -1, localCallOffset, s->_executionStack.size() - 1,
EXEC_STACK_TYPE_CALL);
@@ -894,9 +880,9 @@ void run_vm(EngineState *s) {
s_temp = s->xs->sp;
s->xs->sp -= temp;
- s->xs->sp[0].offset += s->r_rest;
+ s->xs->sp[0].incOffset(s->r_rest);
xs_new = execute_method(s, 0, opparams[0], s_temp, s->xs->objp,
- s->xs->sp[0].offset, s->xs->sp);
+ s->xs->sp[0].getOffset(), s->xs->sp);
s->r_rest = 0; // Used up the &rest adjustment
if (xs_new) // in case of error, keep old stack
s->_executionStackPosChanged = true;
@@ -908,11 +894,10 @@ void run_vm(EngineState *s) {
s_temp = s->xs->sp;
s->xs->sp -= temp;
- s->xs->sp[0].offset += s->r_rest;
+ s->xs->sp[0].incOffset(s->r_rest);
xs_new = execute_method(s, opparams[0], opparams[1], s_temp, s->xs->objp,
- s->xs->sp[0].offset, s->xs->sp);
+ s->xs->sp[0].getOffset(), s->xs->sp);
s->r_rest = 0; // Used up the &rest adjustment
-
if (xs_new) // in case of error, keep old stack
s->_executionStackPosChanged = true;
break;
@@ -965,7 +950,7 @@ void run_vm(EngineState *s) {
s_temp = s->xs->sp;
s->xs->sp -= ((opparams[0] >> 1) + s->r_rest); // Adjust stack
- s->xs->sp[1].offset += s->r_rest;
+ s->xs->sp[1].incOffset(s->r_rest);
xs_new = send_selector(s, s->r_acc, s->r_acc, s_temp,
(int)(opparams[0] >> 1) + (uint16)s->r_rest, s->xs->sp);
@@ -996,7 +981,7 @@ void run_vm(EngineState *s) {
case op_class: // 0x28 (40)
// Get class address
s->r_acc = s->_segMan->getClassAddress((unsigned)opparams[0], SCRIPT_GET_LOCK,
- s->xs->addr.pc);
+ s->xs->addr.pc.getSegment());
break;
case 0x29: // (41)
@@ -1008,7 +993,7 @@ void run_vm(EngineState *s) {
s_temp = s->xs->sp;
s->xs->sp -= ((opparams[0] >> 1) + s->r_rest); // Adjust stack
- s->xs->sp[1].offset += s->r_rest;
+ s->xs->sp[1].incOffset(s->r_rest);
xs_new = send_selector(s, s->xs->objp, s->xs->objp,
s_temp, (int)(opparams[0] >> 1) + (uint16)s->r_rest,
s->xs->sp);
@@ -1021,7 +1006,7 @@ void run_vm(EngineState *s) {
case op_super: // 0x2b (43)
// Send to any class
- r_temp = s->_segMan->getClassAddress(opparams[0], SCRIPT_GET_LOAD, s->xs->addr.pc);
+ r_temp = s->_segMan->getClassAddress(opparams[0], SCRIPT_GET_LOAD, s->xs->addr.pc.getSegment());
if (!r_temp.isPointer())
error("[VM]: Invalid superclass in object");
@@ -1029,7 +1014,7 @@ void run_vm(EngineState *s) {
s_temp = s->xs->sp;
s->xs->sp -= ((opparams[1] >> 1) + s->r_rest); // Adjust stack
- s->xs->sp[1].offset += s->r_rest;
+ s->xs->sp[1].incOffset(s->r_rest);
xs_new = send_selector(s, r_temp, s->xs->objp, s_temp,
(int)(opparams[1] >> 1) + (uint16)s->r_rest,
s->xs->sp);
@@ -1058,14 +1043,14 @@ void run_vm(EngineState *s) {
var_number = temp & 0x03; // Get variable type
// Get variable block offset
- r_temp.segment = s->variablesSegment[var_number];
- r_temp.offset = s->variables[var_number] - s->variablesBase[var_number];
+ r_temp.setSegment(s->variablesSegment[var_number]);
+ r_temp.setOffset(s->variables[var_number] - s->variablesBase[var_number]);
if (temp & 0x08) // Add accumulator offset if requested
- r_temp.offset += s->r_acc.requireSint16();
+ r_temp.incOffset(s->r_acc.requireSint16());
- r_temp.offset += opparams[1]; // Add index
- r_temp.offset *= 2; // variables are 16 bit
+ r_temp.incOffset(opparams[1]); // Add index
+ r_temp.setOffset(r_temp.getOffset() * 2); // variables are 16 bit
// That's the immediate address now
s->r_acc = r_temp;
break;
@@ -1129,28 +1114,28 @@ void run_vm(EngineState *s) {
case op_lofsa: // 0x39 (57)
case op_lofss: // 0x3a (58)
// Load offset to accumulator or push to stack
- r_temp.segment = s->xs->addr.pc.segment;
+ r_temp.setSegment(s->xs->addr.pc.getSegment());
switch (g_sci->_features->detectLofsType()) {
case SCI_VERSION_0_EARLY:
- r_temp.offset = s->xs->addr.pc.offset + opparams[0];
+ r_temp.setOffset((uint16)s->xs->addr.pc.getOffset() + opparams[0]);
break;
case SCI_VERSION_1_MIDDLE:
- r_temp.offset = opparams[0];
+ r_temp.setOffset(opparams[0]);
break;
case SCI_VERSION_1_1:
- r_temp.offset = opparams[0] + local_script->getScriptSize();
+ r_temp.setOffset(opparams[0] + local_script->getScriptSize());
break;
case SCI_VERSION_3:
// In theory this can break if the variant with a one-byte argument is
// used. For now, assume it doesn't happen.
- r_temp.offset = local_script->relocateOffsetSci3(s->xs->addr.pc.offset-2);
+ r_temp.setOffset(local_script->relocateOffsetSci3(s->xs->addr.pc.getOffset() - 2));
break;
default:
error("Unknown lofs type");
}
- if (r_temp.offset >= scr->getBufSize())
+ if (r_temp.getOffset() >= scr->getBufSize())
error("VM: lofsa/lofss operation overflowed: %04x:%04x beyond end"
" of script (at %04x)", PRINT_REG(r_temp), scr->getBufSize());
@@ -1188,6 +1173,7 @@ void run_vm(EngineState *s) {
case op_line: // 0x3f (63)
// Debug opcode (line number)
+ //debug("Script %d, line %d", scr->getScriptNumber(), opparams[0]);
break;
case op_lag: // 0x40 (64)
diff --git a/engines/sci/engine/vm.h b/engines/sci/engine/vm.h
index 334d224baf..8b38faa013 100644
--- a/engines/sci/engine/vm.h
+++ b/engines/sci/engine/vm.h
@@ -61,8 +61,6 @@ struct Class {
reg_t reg; ///< offset; script-relative offset, segment: 0 if not instantiated
};
-#define RAW_IS_OBJECT(datablock) (READ_SCI11ENDIAN_UINT16(((const byte *) datablock) + SCRIPT_OBJECT_MAGIC_OFFSET) == SCRIPT_OBJECT_MAGIC_NUMBER)
-
// A reference to an object's variable.
// The object is stored as a reg_t, the variable as an index into _variables
struct ObjVarRef {
@@ -84,7 +82,7 @@ struct ExecStack {
union {
ObjVarRef varp; // Variable pointer for r/w access
- reg_t pc; // Pointer to the initial program counter. Not accurate for the TOS element
+ reg32_t pc; // Pointer to the initial program counter. Not accurate for the TOS element
} addr;
StackPtr fp; // Frame pointer
@@ -104,7 +102,7 @@ struct ExecStack {
reg_t* getVarPointer(SegManager *segMan) const;
ExecStack(reg_t objp_, reg_t sendp_, StackPtr sp_, int argc_, StackPtr argp_,
- SegmentId localsSegment_, reg_t pc_, Selector debugSelector_,
+ SegmentId localsSegment_, reg32_t pc_, Selector debugSelector_,
int debugExportId_, int debugLocalCallOffset_, int debugOrigin_,
ExecStackType type_) {
objp = objp_;
@@ -118,7 +116,7 @@ struct ExecStack {
if (localsSegment_ != 0xFFFF)
local_segment = localsSegment_;
else
- local_segment = pc_.segment;
+ local_segment = pc_.getSegment();
debugSelector = debugSelector_;
debugExportId = debugExportId_;
debugLocalCallOffset = debugLocalCallOffset_;
@@ -139,7 +137,7 @@ enum {
GC_INTERVAL = 0x8000
};
-enum sci_opcodes {
+enum SciOpcodes {
op_bnot = 0x00, // 000
op_add = 0x01, // 001
op_sub = 0x02, // 002
@@ -204,6 +202,7 @@ enum sci_opcodes {
op_push2 = 0x3d, // 061
op_pushSelf = 0x3e, // 062
op_line = 0x3f, // 063
+ //
op_lag = 0x40, // 064
op_lal = 0x41, // 065
op_lat = 0x42, // 066
@@ -220,6 +219,7 @@ enum sci_opcodes {
op_lsli = 0x4d, // 077
op_lsti = 0x4e, // 078
op_lspi = 0x4f, // 079
+ //
op_sag = 0x50, // 080
op_sal = 0x51, // 081
op_sat = 0x52, // 082
@@ -236,6 +236,7 @@ enum sci_opcodes {
op_ssli = 0x5d, // 093
op_ssti = 0x5e, // 094
op_sspi = 0x5f, // 095
+ //
op_plusag = 0x60, // 096
op_plusal = 0x61, // 097
op_plusat = 0x62, // 098
@@ -252,6 +253,7 @@ enum sci_opcodes {
op_plussli = 0x6d, // 109
op_plussti = 0x6e, // 110
op_plusspi = 0x6f, // 111
+ //
op_minusag = 0x70, // 112
op_minusal = 0x71, // 113
op_minusat = 0x72, // 114
diff --git a/engines/sci/engine/vm_types.cpp b/engines/sci/engine/vm_types.cpp
index b95fd58129..27015d9be4 100644
--- a/engines/sci/engine/vm_types.cpp
+++ b/engines/sci/engine/vm_types.cpp
@@ -43,17 +43,17 @@ reg_t reg_t::lookForWorkaround(const reg_t right) const {
reg_t reg_t::operator+(const reg_t right) const {
if (isPointer() && right.isNumber()) {
// Pointer arithmetics. Only some pointer types make sense here
- SegmentObj *mobj = g_sci->getEngineState()->_segMan->getSegmentObj(segment);
+ SegmentObj *mobj = g_sci->getEngineState()->_segMan->getSegmentObj(getSegment());
if (!mobj)
- error("[VM]: Attempt to add %d to invalid pointer %04x:%04x", right.offset, PRINT_REG(*this));
+ error("[VM]: Attempt to add %d to invalid pointer %04x:%04x", right.getOffset(), PRINT_REG(*this));
switch (mobj->getType()) {
case SEG_TYPE_LOCALS:
case SEG_TYPE_SCRIPT:
case SEG_TYPE_STACK:
case SEG_TYPE_DYNMEM:
- return make_reg(segment, offset + right.toSint16());
+ return make_reg(getSegment(), getOffset() + right.toSint16());
default:
return lookForWorkaround(right);
}
@@ -69,12 +69,12 @@ reg_t reg_t::operator+(const reg_t right) const {
}
reg_t reg_t::operator-(const reg_t right) const {
- if (segment == right.segment) {
+ if (getSegment() == right.getSegment()) {
// We can subtract numbers, or pointers with the same segment,
// an operation which will yield a number like in C
return make_reg(0, toSint16() - right.toSint16());
} else {
- return *this + make_reg(right.segment, -right.offset);
+ return *this + make_reg(right.getSegment(), -right.toSint16());
}
}
@@ -174,7 +174,7 @@ reg_t reg_t::operator^(const reg_t right) const {
}
int reg_t::cmp(const reg_t right, bool treatAsUnsigned) const {
- if (segment == right.segment) { // can compare things in the same segment
+ if (getSegment() == right.getSegment()) { // can compare things in the same segment
if (treatAsUnsigned || !isNumber())
return toUint16() - right.toUint16();
else
@@ -218,7 +218,7 @@ bool reg_t::pointerComparisonWithInteger(const reg_t right) const {
// SQ1, room 28, when throwing water at the Orat
// SQ1, room 58, when giving the ID card to the robot
// SQ4 CD, at the first game screen, when the narrator is about to speak
- return (isPointer() && right.isNumber() && right.offset <= 2000 && getSciVersion() <= SCI_VERSION_1_1);
+ return (isPointer() && right.isNumber() && right.getOffset() <= 2000 && getSciVersion() <= SCI_VERSION_1_1);
}
} // End of namespace Sci
diff --git a/engines/sci/engine/vm_types.h b/engines/sci/engine/vm_types.h
index 7b155a4532..9a7589e9a7 100644
--- a/engines/sci/engine/vm_types.h
+++ b/engines/sci/engine/vm_types.h
@@ -31,43 +31,64 @@ namespace Sci {
typedef uint16 SegmentId;
struct reg_t {
- SegmentId segment;
- uint16 offset;
+ // Segment and offset. These should never be accessed directly
+ SegmentId _segment;
+ uint16 _offset;
+
+ inline SegmentId getSegment() const {
+ return _segment;
+ }
+
+ inline void setSegment(SegmentId segment) {
+ _segment = segment;
+ }
+
+ inline uint16 getOffset() const {
+ return _offset;
+ }
+
+ inline void setOffset(uint16 offset) {
+ _offset = offset;
+ }
+
+ inline void incOffset(int16 offset) {
+ setOffset(getOffset() + offset);
+ }
inline bool isNull() const {
- return (offset | segment) == 0;
+ return (_offset | getSegment()) == 0;
}
inline uint16 toUint16() const {
- return offset;
+ return _offset;
}
inline int16 toSint16() const {
- return (int16)offset;
+ return (int16)_offset;
}
bool isNumber() const {
- return segment == 0;
+ return getSegment() == 0;
}
bool isPointer() const {
- return segment != 0 && segment != 0xFFFF;
+ return getSegment() != 0 && getSegment() != 0xFFFF;
}
uint16 requireUint16() const;
int16 requireSint16() const;
inline bool isInitialized() const {
- return segment != 0xFFFF;
+ return getSegment() != 0xFFFF;
}
// Comparison operators
bool operator==(const reg_t &x) const {
- return (offset == x.offset) && (segment == x.segment);
+ return (getOffset() == x.getOffset()) && (getSegment() == x.getSegment());
}
bool operator!=(const reg_t &x) const {
- return (offset != x.offset) || (segment != x.segment);
+ return (getOffset() != x.getOffset()) || (getSegment() != x.getSegment());
}
bool operator>(const reg_t right) const {
@@ -141,12 +162,55 @@ private:
static inline reg_t make_reg(SegmentId segment, uint16 offset) {
reg_t r;
- r.offset = offset;
- r.segment = segment;
+ r.setSegment(segment);
+ r.setOffset(offset);
return r;
}
-#define PRINT_REG(r) (0xffff) & (unsigned) (r).segment, (unsigned) (r).offset
+#define PRINT_REG(r) (0xffff) & (unsigned) (r).getSegment(), (unsigned) (r).getOffset()
+
+// A true 32-bit reg_t
+struct reg32_t {
+ // Segment and offset. These should never be accessed directly
+ SegmentId _segment;
+ uint32 _offset;
+
+ inline SegmentId getSegment() const {
+ return _segment;
+ }
+
+ inline void setSegment(SegmentId segment) {
+ _segment = segment;
+ }
+
+ inline uint32 getOffset() const {
+ return _offset;
+ }
+
+ inline void setOffset(uint32 offset) {
+ _offset = offset;
+ }
+
+ inline void incOffset(int32 offset) {
+ setOffset(getOffset() + offset);
+ }
+
+ // Comparison operators
+ bool operator==(const reg32_t &x) const {
+ return (getOffset() == x.getOffset()) && (getSegment() == x.getSegment());
+ }
+
+ bool operator!=(const reg32_t &x) const {
+ return (getOffset() != x.getOffset()) || (getSegment() != x.getSegment());
+ }
+};
+
+static inline reg32_t make_reg32(SegmentId segment, uint32 offset) {
+ reg32_t r;
+ r.setSegment(segment);
+ r.setOffset(offset);
+ return r;
+}
// Stack pointer type
typedef reg_t *StackPtr;
diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp
index a556134e32..9fa0368784 100644
--- a/engines/sci/engine/workarounds.cpp
+++ b/engines/sci/engine/workarounds.cpp
@@ -36,13 +36,15 @@ const SciWorkaroundEntry arithmeticWorkarounds[] = {
{ GID_ECOQUEST2, 100, 0, 0, "Rain", "points", 0xcc6, 0, { WORKAROUND_FAKE, 0 } }, // op_or: when giving the papers to the customs officer, gets called against a pointer instead of a number - bug #3034464
{ GID_ECOQUEST2, 100, 0, 0, "Rain", "points", 0xce0, 0, { WORKAROUND_FAKE, 0 } }, // Same as above, for the Spanish version - bug #3313962
{ GID_FANMADE, 516, 983, 0, "Wander", "setTarget", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_mul: The Legend of the Lost Jewel Demo (fan made): called with object as second parameter when attacked by insects - bug #3038913
+ { GID_GK1, 800,64992, 0, "Fwd", "doit", -1, 0, { WORKAROUND_FAKE, 1 } }, // op_gt: when Mosely finds Gabriel and Grace near the end of the game, compares the Grooper object with 7
{ GID_ICEMAN, 199, 977, 0, "Grooper", "doit", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_add: While dancing with the girl
{ GID_MOTHERGOOSE256, -1, 999, 0, "Event", "new", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_and: constantly during the game (SCI1 version)
{ GID_MOTHERGOOSE256, -1, 4, 0, "rm004", "doit", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_or: when going north and reaching the castle (rooms 4 and 37) - bug #3038228
{ GID_MOTHERGOOSEHIRES,90, 90, 0, "newGameButton", "select", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_ge: MUMG Deluxe, when selecting "New Game" in the main menu. It tries to compare an integer with a list. Needs to return false for the game to continue.
+ { GID_PHANTASMAGORIA, 902, 0, 0, "", "export 7", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_shr: when starting a chapter in Phantasmagoria
{ GID_QFG1VGA, 301, 928, 0, "Blink", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_div: when entering the inn, gets called with 1 parameter, but 2nd parameter is used for div which happens to be an object
{ GID_QFG2, 200, 200, 0, "astro", "messages", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_lsi: when getting asked for your name by the astrologer bug #3039879
- { GID_GK1, 800,64992, 0, "Fwd", "doit", -1, 0, { WORKAROUND_FAKE, 1 } }, // op_gt: when Mosely finds Gabriel and Grace near the end of the game, compares the Grooper object with 7
+ { GID_QFG4, 710,64941, 0, "RandCycle", "doit", -1, 0, { WORKAROUND_FAKE, 1 } }, // op_gt: when the tentacle appears in the third room of the caves
SCI_WORKAROUNDENTRY_TERMINATOR
};
@@ -162,11 +164,12 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = {
{ 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)
{ GID_SQ4, -1, 398, 0, "showBox", "changeState", -1, 0, { WORKAROUND_FAKE, 0 } }, // CD: called when rummaging in Software Excess bargain bin
- { GID_SQ4, -1, 928, 0, "Narrator", "startText", -1, 1000, { WORKAROUND_FAKE, 1 } }, // CD: method returns this to the caller
+ { GID_SQ4, -1, 928, -1, "Narrator", "startText", -1, 1000, { WORKAROUND_FAKE, 1 } }, // CD: happens in the options dialog and in-game when speech and subtitles are used simultaneously
{ GID_SQ5, 201, 201, 0, "buttonPanel", "doVerb", -1, 0, { WORKAROUND_FAKE, 1 } }, // when looking at the orange or red button - bug #3038563
{ GID_SQ6, -1, 0, 0, "SQ6", "init", -1, 2, { WORKAROUND_FAKE, 0 } }, // Demo and full version: called when the game starts (demo: room 0, full: room 100)
- { GID_SQ6, 100, 64950, 0, "View", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // called when pressing "Start game" in the main menu
+ { GID_SQ6, -1, 64950, -1, "Feature", "handleEvent", -1, 0, { WORKAROUND_FAKE, 0 } }, // called when pressing "Start game" in the main menu, when entering the Orion's Belt bar (room 300), and perhaps other places
{ GID_SQ6, -1, 64964, 0, "DPath", "init", -1, 1, { WORKAROUND_FAKE, 0 } }, // during the game
+ { GID_TORIN, -1, 64017, 0, "oFlags", "clear", -1, 0, { WORKAROUND_FAKE, 0 } }, // entering Torin's home in the French version
SCI_WORKAROUNDENTRY_TERMINATOR
};
@@ -176,6 +179,7 @@ const SciWorkaroundEntry kAbs_workarounds[] = {
{ GID_HOYLE1, 2, 2, 0, "room2", "doit", -1, 0, { WORKAROUND_FAKE, 0x3e9 } }, // old maid - called with objects instead of integers
{ GID_HOYLE1, 3, 3, 0, "room3", "doit", -1, 0, { WORKAROUND_FAKE, 0x3e9 } }, // hearts - called with objects instead of integers
{ GID_QFG1VGA, -1, -1, 0, NULL, "doit", -1, 0, { WORKAROUND_FAKE, 0x3e9 } }, // when the game is patched with the NRS patch
+ { GID_QFG3 , -1, -1, 0, NULL, "doit", -1, 0, { WORKAROUND_FAKE, 0x3e9 } }, // when the game is patched with the NRS patch (bugs #3528416, #3528542)
SCI_WORKAROUNDENTRY_TERMINATOR
};
@@ -250,8 +254,9 @@ const SciWorkaroundEntry kDoSoundFade_workarounds[] = {
// gameID, room,script,lvl, object-name, method-name, call,index, workaround
const SciWorkaroundEntry kGetAngle_workarounds[] = {
- { GID_FANMADE, 516, 992, 0, "Motion", "init", -1, 0, { WORKAROUND_IGNORE, 0 } }, // The Legend of the Lost Jewel Demo (fan made): called with third/fourth parameters as objects
+ { GID_FANMADE, 516, 992, 0, "Motion", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // The Legend of the Lost Jewel Demo (fan made): called with third/fourth parameters as objects
{ GID_KQ6, -1, 752, 0, "throwDazzle", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // room 740/790 after the Genie is exposed in the Palace (short and long ending), it starts shooting lightning bolts around. An extra 5th parameter is passed - bug #3034610 & #3041734
+ { GID_SQ1, -1, 927, 0, "PAvoider", "doit", -1, 0, { WORKAROUND_FAKE, 0 } }, // all rooms in Ulence Flats after getting the Pilot Droid: called with a single parameter when the droid is in Roger's path - bug #3513207
SCI_WORKAROUNDENTRY_TERMINATOR
};
@@ -320,8 +325,9 @@ const SciWorkaroundEntry kGraphRedrawBox_workarounds[] = {
// gameID, room,script,lvl, object-name, method-name, call,index, workaround
const SciWorkaroundEntry kGraphUpdateBox_workarounds[] = {
{ GID_ECOQUEST2, 100, 333, 0, "showEcorder", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // necessary workaround for our ecorder script patch, because there isn't enough space to patch the function
- { GID_PQ3, 202, 202, 0, "MapEdit", "movePt", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters - bug #3038077
{ GID_PQ3, 202, 202, 0, "MapEdit", "addPt", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters - bug #3038077
+ { GID_PQ3, 202, 202, 0, "MapEdit", "movePt", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters - bug #3038077
+ { GID_PQ3, 202, 202, 0, "MapEdit", "dispose", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // when plotting crimes, gets called with 2 extra parameters
SCI_WORKAROUNDENTRY_TERMINATOR
};
@@ -336,6 +342,7 @@ const SciWorkaroundEntry kIsObject_workarounds[] = {
// gameID, room,script,lvl, object-name, method-name, call,index, workaround
const SciWorkaroundEntry kMemory_workarounds[] = {
{ GID_LAURABOW2, -1, 999, 0, "", "export 6", -1, 0, { WORKAROUND_FAKE, 0 } }, // during the intro, when exiting the train (room 160), talking to Mr. Augustini, etc. - bug #3034490
+ { GID_SQ1, -1, 999, 0, "", "export 6", -1, 0, { WORKAROUND_FAKE, 0 } }, // during walking Roger around Ulence Flats - bug #3513765
SCI_WORKAROUNDENTRY_TERMINATOR
};
@@ -392,6 +399,7 @@ const SciWorkaroundEntry kUnLoad_workarounds[] = {
{ 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
{ GID_SQ1, 43, 303, 0, "slotGuy", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when leaving ulence flats bar, parameter 1 is not passed - script error
+ { GID_QFG4, -1, 110, 0, "dreamer", "dispose", -1, 0, { WORKAROUND_IGNORE, 0 } }, // during the dream sequence, a 3rd parameter is passed by accident
SCI_WORKAROUNDENTRY_TERMINATOR
};
diff --git a/engines/sci/event.cpp b/engines/sci/event.cpp
index 378e88b7df..14443db1e2 100644
--- a/engines/sci/event.cpp
+++ b/engines/sci/event.cpp
@@ -102,8 +102,8 @@ const MouseEventConversion mouseEventMappings[] = {
{ Common::EVENT_RBUTTONDOWN, SCI_EVENT_MOUSE_PRESS, 2 },
{ Common::EVENT_MBUTTONDOWN, SCI_EVENT_MOUSE_PRESS, 3 },
{ Common::EVENT_LBUTTONUP, SCI_EVENT_MOUSE_RELEASE, 1 },
- { Common::EVENT_LBUTTONUP, SCI_EVENT_MOUSE_RELEASE, 2 },
- { Common::EVENT_LBUTTONUP, SCI_EVENT_MOUSE_RELEASE, 3 }
+ { Common::EVENT_RBUTTONUP, SCI_EVENT_MOUSE_RELEASE, 2 },
+ { Common::EVENT_MBUTTONUP, SCI_EVENT_MOUSE_RELEASE, 3 }
};
EventManager::EventManager(bool fontIsExtended) : _fontIsExtended(fontIsExtended) {
diff --git a/engines/sci/graphics/animate.cpp b/engines/sci/graphics/animate.cpp
index 983e697481..ee28c5ca31 100644
--- a/engines/sci/graphics/animate.cpp
+++ b/engines/sci/graphics/animate.cpp
@@ -723,7 +723,7 @@ void GfxAnimate::printAnimateList(Console *con) {
const AnimateList::iterator end = _list.end();
for (it = _list.begin(); it != end; ++it) {
- Script *scr = _s->_segMan->getScriptIfLoaded(it->object.segment);
+ Script *scr = _s->_segMan->getScriptIfLoaded(it->object.getSegment());
int16 scriptNo = scr ? scr->getScriptNumber() : -1;
con->DebugPrintf("%04x:%04x (%s), script %d, view %d (%d, %d), pal %d, "
diff --git a/engines/sci/graphics/controls16.cpp b/engines/sci/graphics/controls16.cpp
index ab54e468d1..7c09969717 100644
--- a/engines/sci/graphics/controls16.cpp
+++ b/engines/sci/graphics/controls16.cpp
@@ -297,7 +297,7 @@ void GfxControls16::kernelDrawButton(Common::Rect rect, reg_t obj, const char *t
_paint16->eraseRect(rect);
_paint16->frameRect(rect);
rect.grow(-2);
- _ports->textGreyedOutput(style & 1 ? false : true);
+ _ports->textGreyedOutput(!(style & SCI_CONTROLS_STYLE_ENABLED));
_text16->Box(text, false, rect, SCI_TEXT16_ALIGNMENT_CENTER, fontId);
_ports->textGreyedOutput(false);
rect.grow(1);
diff --git a/engines/sci/graphics/controls32.cpp b/engines/sci/graphics/controls32.cpp
index ad1d9e8623..5535a7408a 100644
--- a/engines/sci/graphics/controls32.cpp
+++ b/engines/sci/graphics/controls32.cpp
@@ -68,7 +68,7 @@ void GfxControls32::kernelTexteditChange(reg_t controlObject) {
while (captureEvents) {
curEvent = g_sci->getEventManager()->getSciEvent(SCI_EVENT_KEYBOARD | SCI_EVENT_PEEK);
-
+
if (curEvent.type == SCI_EVENT_NONE) {
eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event
} else {
@@ -170,11 +170,11 @@ void GfxControls32::kernelTexteditChange(reg_t controlObject) {
// Note: the following checkAltInput call might make the text
// too wide to fit, but SSCI fails to check that too.
}
-
+
reg_t hunkId = readSelector(_segMan, controlObject, SELECTOR(bitmap));
Common::Rect nsRect = g_sci->_gfxCompare->getNSRect(controlObject);
//texteditCursorErase(); // TODO: Cursor
-
+
// Write back string
_segMan->strcpy(textReference, text.c_str());
// Modify the buffer and show it
diff --git a/engines/sci/graphics/cursor.cpp b/engines/sci/graphics/cursor.cpp
index 52a5961070..ce77cf6ed3 100644
--- a/engines/sci/graphics/cursor.cpp
+++ b/engines/sci/graphics/cursor.cpp
@@ -132,7 +132,7 @@ void GfxCursor::kernelSetShape(GuiResourceId resourceId) {
resourceData = resource->data;
- if (getSciVersion() <= SCI_VERSION_0_LATE) {
+ if (getSciVersion() <= SCI_VERSION_01) {
// SCI0 cursors contain hotspot flags, not actual hotspot coordinates.
// If bit 0 of resourceData[3] is set, the hotspot should be centered,
// otherwise it's in the top left of the mouse cursor.
@@ -148,11 +148,13 @@ void GfxCursor::kernelSetShape(GuiResourceId resourceId) {
colorMapping[1] = _screen->getColorWhite(); // White is also hardcoded
colorMapping[2] = SCI_CURSOR_SCI0_TRANSPARENCYCOLOR;
colorMapping[3] = _palette->matchColor(170, 170, 170); // Grey
- // Special case for the magnifier cursor in LB1 (bug #3487092).
- // No other SCI0 game has a cursor resource of 1, so this is handled
- // specifically for LB1.
+ // TODO: Figure out if the grey color is hardcoded
+ // HACK for the magnifier cursor in LB1, fixes its color (bug #3487092)
if (g_sci->getGameId() == GID_LAURABOW && resourceId == 1)
colorMapping[3] = _screen->getColorWhite();
+ // HACK for Longbow cursors, fixes the shade of grey they're using (bug #3489101)
+ if (g_sci->getGameId() == GID_LONGBOW)
+ colorMapping[3] = _palette->matchColor(223, 223, 223); // Light Grey
// Seek to actual data
resourceData += 4;
@@ -409,7 +411,7 @@ void GfxCursor::refreshPosition() {
}
}
- CursorMan.replaceCursor((const byte *)_cursorSurface, cursorCelInfo->width, cursorCelInfo->height, cursorHotspot.x, cursorHotspot.y, cursorCelInfo->clearKey);
+ CursorMan.replaceCursor(_cursorSurface, cursorCelInfo->width, cursorCelInfo->height, cursorHotspot.x, cursorHotspot.y, cursorCelInfo->clearKey);
}
}
diff --git a/engines/sci/graphics/font.cpp b/engines/sci/graphics/font.cpp
index fcdd057509..30184cc091 100644
--- a/engines/sci/graphics/font.cpp
+++ b/engines/sci/graphics/font.cpp
@@ -54,7 +54,7 @@ GfxFontFromResource::GfxFontFromResource(ResourceManager *resMan, GfxScreen *scr
}
GfxFontFromResource::~GfxFontFromResource() {
- delete []_chars;
+ delete[] _chars;
_resMan->unlockResource(_resource);
}
diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp
index b12413ab69..8b7fa2c384 100644
--- a/engines/sci/graphics/frameout.cpp
+++ b/engines/sci/graphics/frameout.cpp
@@ -28,9 +28,11 @@
#include "common/system.h"
#include "common/textconsole.h"
#include "engines/engine.h"
+#include "graphics/palette.h"
#include "graphics/surface.h"
#include "sci/sci.h"
+#include "sci/console.h"
#include "sci/engine/kernel.h"
#include "sci/engine/state.h"
#include "sci/engine/selector.h"
@@ -52,12 +54,18 @@ namespace Sci {
// TODO/FIXME: This is all guesswork
+enum SciSpeciaPlanelPictureCodes {
+ kPlaneTranslucent = 0xfffe, // -2
+ kPlanePlainColored = 0xffff // -1
+};
+
GfxFrameout::GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, GfxCache *cache, GfxScreen *screen, GfxPalette *palette, GfxPaint32 *paint32)
: _segMan(segMan), _resMan(resMan), _cache(cache), _screen(screen), _palette(palette), _paint32(paint32) {
_coordAdjuster = (GfxCoordAdjuster32 *)coordAdjuster;
- _scriptsRunningWidth = 320;
- _scriptsRunningHeight = 200;
+ _curScrollText = -1;
+ _showScrollText = false;
+ _maxScrollTexts = 0;
}
GfxFrameout::~GfxFrameout() {
@@ -68,32 +76,88 @@ void GfxFrameout::clear() {
deletePlaneItems(NULL_REG);
_planes.clear();
deletePlanePictures(NULL_REG);
+ clearScrollTexts();
+}
+
+void GfxFrameout::clearScrollTexts() {
+ _scrollTexts.clear();
+ _curScrollText = -1;
+}
+
+void GfxFrameout::addScrollTextEntry(Common::String &text, reg_t kWindow, uint16 x, uint16 y, bool replace) {
+ //reg_t bitmapHandle = g_sci->_gfxText32->createScrollTextBitmap(text, kWindow);
+ // HACK: We set the container dimensions manually
+ reg_t bitmapHandle = g_sci->_gfxText32->createScrollTextBitmap(text, kWindow, 480, 70);
+ ScrollTextEntry textEntry;
+ textEntry.bitmapHandle = bitmapHandle;
+ textEntry.kWindow = kWindow;
+ textEntry.x = x;
+ textEntry.y = y;
+ if (!replace || _scrollTexts.size() == 0) {
+ if (_scrollTexts.size() > _maxScrollTexts) {
+ _scrollTexts.remove_at(0);
+ _curScrollText--;
+ }
+ _scrollTexts.push_back(textEntry);
+ _curScrollText++;
+ } else {
+ _scrollTexts.pop_back();
+ _scrollTexts.push_back(textEntry);
+ }
+}
+
+void GfxFrameout::showCurrentScrollText() {
+ if (!_showScrollText || _curScrollText < 0)
+ return;
+
+ uint16 size = (uint16)_scrollTexts.size();
+ if (size > 0) {
+ assert(_curScrollText < size);
+ ScrollTextEntry textEntry = _scrollTexts[_curScrollText];
+ g_sci->_gfxText32->drawScrollTextBitmap(textEntry.kWindow, textEntry.bitmapHandle, textEntry.x, textEntry.y);
+ }
}
+extern void showScummVMDialog(const Common::String &message);
+
void GfxFrameout::kernelAddPlane(reg_t object) {
PlaneEntry newPlane;
if (_planes.empty()) {
// There has to be another way for sierra sci to do this or maybe script resolution is compiled into
// interpreter (TODO)
- uint16 tmpRunningWidth = readSelectorValue(_segMan, object, SELECTOR(resX));
- uint16 tmpRunningHeight = readSelectorValue(_segMan, object, SELECTOR(resY));
+ uint16 scriptWidth = readSelectorValue(_segMan, object, SELECTOR(resX));
+ uint16 scriptHeight = readSelectorValue(_segMan, object, SELECTOR(resY));
- // The above can be 0 in SCI3 (e.g. Phantasmagoria 2)
- if (tmpRunningWidth > 0 && tmpRunningHeight > 0) {
- _scriptsRunningWidth = tmpRunningWidth;
- _scriptsRunningHeight = tmpRunningHeight;
+ // Phantasmagoria 2 doesn't specify a script width/height
+ if (g_sci->getGameId() == GID_PHANTASMAGORIA2) {
+ scriptWidth = 640;
+ scriptHeight = 480;
}
- _coordAdjuster->setScriptsResolution(_scriptsRunningWidth, _scriptsRunningHeight);
+ assert(scriptWidth > 0 && scriptHeight > 0);
+ _coordAdjuster->setScriptsResolution(scriptWidth, scriptHeight);
+ }
+
+ // Import of QfG character files dialog is shown in QFG4.
+ // Display additional popup information before letting user use it.
+ // For the SCI0-SCI1.1 version of this, check kDrawControl().
+ if (g_sci->inQfGImportRoom() && !strcmp(_segMan->getObjectName(object), "DSPlane")) {
+ showScummVMDialog("Characters saved inside ScummVM are shown "
+ "automatically. Character files saved in the original "
+ "interpreter need to be put inside ScummVM's saved games "
+ "directory and a prefix needs to be added depending on which "
+ "game it was saved in: 'qfg1-' for Quest for Glory 1, 'qfg2-' "
+ "for Quest for Glory 2, 'qfg3-' for Quest for Glory 3. "
+ "Example: 'qfg2-thief.sav'.");
}
newPlane.object = object;
newPlane.priority = readSelectorValue(_segMan, object, SELECTOR(priority));
- newPlane.lastPriority = 0xFFFF; // hidden
+ newPlane.lastPriority = -1; // hidden
newPlane.planeOffsetX = 0;
newPlane.planeOffsetY = 0;
- newPlane.pictureId = 0xFFFF;
+ newPlane.pictureId = kPlanePlainColored;
newPlane.planePictureMirrored = false;
newPlane.planeBack = 0;
_planes.push_back(newPlane);
@@ -111,7 +175,8 @@ void GfxFrameout::kernelUpdatePlane(reg_t object) {
if (lastPictureId != it->pictureId) {
// picture got changed, load new picture
deletePlanePictures(object);
- if ((it->pictureId != 0xFFFF) && (it->pictureId != 0xFFFE)) {
+ // Draw the plane's picture if it's not a translucent/plane colored frame
+ if ((it->pictureId != kPlanePlainColored) && (it->pictureId != kPlaneTranslucent)) {
// SQ6 gives us a bad picture number for the control menu
if (_resMan->testResource(ResourceId(kResourceTypePic, it->pictureId)))
addPlanePicture(object, it->pictureId, 0);
@@ -122,11 +187,8 @@ void GfxFrameout::kernelUpdatePlane(reg_t object) {
it->planeRect.bottom = readSelectorValue(_segMan, object, SELECTOR(bottom));
it->planeRect.right = readSelectorValue(_segMan, object, SELECTOR(right));
- Common::Rect screenRect(_screen->getWidth(), _screen->getHeight());
- it->planeRect.top = (it->planeRect.top * screenRect.height()) / _scriptsRunningHeight;
- it->planeRect.left = (it->planeRect.left * screenRect.width()) / _scriptsRunningWidth;
- it->planeRect.bottom = (it->planeRect.bottom * screenRect.height()) / _scriptsRunningHeight;
- it->planeRect.right = (it->planeRect.right * screenRect.width()) / _scriptsRunningWidth;
+ _coordAdjuster->fromScriptToDisplay(it->planeRect.top, it->planeRect.left);
+ _coordAdjuster->fromScriptToDisplay(it->planeRect.bottom, it->planeRect.right);
// We get negative left in kq7 in scrolling rooms
if (it->planeRect.left < 0) {
@@ -135,7 +197,7 @@ void GfxFrameout::kernelUpdatePlane(reg_t object) {
} else {
it->planeOffsetX = 0;
}
-
+
if (it->planeRect.top < 0) {
it->planeOffsetY = -it->planeRect.top;
it->planeRect.top = 0;
@@ -191,11 +253,9 @@ void GfxFrameout::kernelDeletePlane(reg_t object) {
planeRect.bottom = readSelectorValue(_segMan, object, SELECTOR(bottom));
planeRect.right = readSelectorValue(_segMan, object, SELECTOR(right));
- Common::Rect screenRect(_screen->getWidth(), _screen->getHeight());
- planeRect.top = (planeRect.top * screenRect.height()) / _scriptsRunningHeight;
- planeRect.left = (planeRect.left * screenRect.width()) / _scriptsRunningWidth;
- planeRect.bottom = (planeRect.bottom * screenRect.height()) / _scriptsRunningHeight;
- planeRect.right = (planeRect.right * screenRect.width()) / _scriptsRunningWidth;
+ _coordAdjuster->fromScriptToDisplay(planeRect.top, planeRect.left);
+ _coordAdjuster->fromScriptToDisplay(planeRect.bottom, planeRect.right);
+
// Blackout removed plane rect
_paint32->fillRect(planeRect, 0);
return;
@@ -204,6 +264,9 @@ void GfxFrameout::kernelDeletePlane(reg_t object) {
}
void GfxFrameout::addPlanePicture(reg_t object, GuiResourceId pictureId, uint16 startX, uint16 startY) {
+ if (pictureId == kPlanePlainColored || pictureId == kPlaneTranslucent) // sanity check
+ return;
+
PlanePictureEntry newPicture;
newPicture.object = object;
newPicture.pictureId = pictureId;
@@ -228,15 +291,76 @@ void GfxFrameout::deletePlanePictures(reg_t object) {
}
}
+// Provides the same functionality as kGraph(DrawLine)
+reg_t GfxFrameout::addPlaneLine(reg_t object, Common::Point startPoint, Common::Point endPoint, byte color, byte priority, byte control) {
+ for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); ++it) {
+ if (it->object == object) {
+ PlaneLineEntry line;
+ line.hunkId = _segMan->allocateHunkEntry("PlaneLine()", 1); // we basically use this for a unique ID
+ line.startPoint = startPoint;
+ line.endPoint = endPoint;
+ line.color = color;
+ line.priority = priority;
+ line.control = control;
+ it->lines.push_back(line);
+ return line.hunkId;
+ }
+ }
+
+ return NULL_REG;
+}
+
+void GfxFrameout::updatePlaneLine(reg_t object, reg_t hunkId, Common::Point startPoint, Common::Point endPoint, byte color, byte priority, byte control) {
+ // Check if we're asked to update a line that was never added
+ if (hunkId.isNull())
+ return;
+
+ for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); ++it) {
+ if (it->object == object) {
+ for (PlaneLineList::iterator it2 = it->lines.begin(); it2 != it->lines.end(); ++it2) {
+ if (it2->hunkId == hunkId) {
+ it2->startPoint = startPoint;
+ it2->endPoint = endPoint;
+ it2->color = color;
+ it2->priority = priority;
+ it2->control = control;
+ return;
+ }
+ }
+ }
+ }
+}
+
+void GfxFrameout::deletePlaneLine(reg_t object, reg_t hunkId) {
+ // Check if we're asked to delete a line that was never added (happens during the intro of LSL6)
+ if (hunkId.isNull())
+ return;
+
+ for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); ++it) {
+ if (it->object == object) {
+ for (PlaneLineList::iterator it2 = it->lines.begin(); it2 != it->lines.end(); ++it2) {
+ if (it2->hunkId == hunkId) {
+ _segMan->freeHunkEntry(hunkId);
+ it2 = it->lines.erase(it2);
+ return;
+ }
+ }
+ }
+ }
+}
+
void GfxFrameout::kernelAddScreenItem(reg_t object) {
// Ignore invalid items
- if (!_segMan->isObject(object))
+ if (!_segMan->isObject(object)) {
+ warning("kernelAddScreenItem: Attempt to add an invalid object (%04x:%04x)", PRINT_REG(object));
return;
+ }
FrameoutEntry *itemEntry = new FrameoutEntry();
memset(itemEntry, 0, sizeof(FrameoutEntry));
itemEntry->object = object;
itemEntry->givenOrderNr = _screenItems.size();
+ itemEntry->visible = true;
_screenItems.push_back(itemEntry);
kernelUpdateScreenItem(object);
@@ -244,8 +368,10 @@ void GfxFrameout::kernelAddScreenItem(reg_t object) {
void GfxFrameout::kernelUpdateScreenItem(reg_t object) {
// Ignore invalid items
- if (!_segMan->isObject(object))
+ if (!_segMan->isObject(object)) {
+ warning("kernelUpdateScreenItem: Attempt to update an invalid object (%04x:%04x)", PRINT_REG(object));
return;
+ }
FrameoutEntry *itemEntry = findScreenItem(object);
if (!itemEntry) {
@@ -266,14 +392,18 @@ void GfxFrameout::kernelUpdateScreenItem(reg_t object) {
itemEntry->signal = readSelectorValue(_segMan, object, SELECTOR(signal));
itemEntry->scaleX = readSelectorValue(_segMan, object, SELECTOR(scaleX));
itemEntry->scaleY = readSelectorValue(_segMan, object, SELECTOR(scaleY));
+ itemEntry->visible = true;
+
+ // Check if the entry can be hidden
+ if (lookupSelector(_segMan, object, SELECTOR(visible), NULL, NULL) != kSelectorNone)
+ itemEntry->visible = readSelectorValue(_segMan, object, SELECTOR(visible));
}
void GfxFrameout::kernelDeleteScreenItem(reg_t object) {
FrameoutEntry *itemEntry = findScreenItem(object);
- if (!itemEntry) {
- warning("kernelDeleteScreenItem: invalid object %04x:%04x", PRINT_REG(object));
+ // If the item could not be found, it may already have been deleted
+ if (!itemEntry)
return;
- }
_screenItems.remove(itemEntry);
delete itemEntry;
@@ -290,7 +420,7 @@ void GfxFrameout::deletePlaneItems(reg_t planeObject) {
} else {
objectMatches = true;
}
-
+
if (objectMatches) {
FrameoutEntry *itemEntry = *listIterator;
listIterator = _screenItems.erase(listIterator);
@@ -330,15 +460,10 @@ bool sortHelper(const FrameoutEntry* entry1, const FrameoutEntry* entry2) {
}
bool planeSortHelper(const PlaneEntry &entry1, const PlaneEntry &entry2) {
-// SegManager *segMan = g_sci->getEngineState()->_segMan;
-
-// uint16 plane1Priority = readSelectorValue(segMan, entry1, SELECTOR(priority));
-// uint16 plane2Priority = readSelectorValue(segMan, entry2, SELECTOR(priority));
-
- if (entry1.priority == 0xffff)
+ if (entry1.priority < 0)
return true;
- if (entry2.priority == 0xffff)
+ if (entry2.priority < 0)
return false;
return entry1.priority < entry2.priority;
@@ -357,23 +482,6 @@ void GfxFrameout::sortPlanes() {
Common::sort(_planes.begin(), _planes.end(), planeSortHelper);
}
-int16 GfxFrameout::upscaleHorizontalCoordinate(int16 coordinate) {
- return ((coordinate * _screen->getWidth()) / _scriptsRunningWidth);
-}
-
-int16 GfxFrameout::upscaleVerticalCoordinate(int16 coordinate) {
- return ((coordinate * _screen->getHeight()) / _scriptsRunningHeight);
-}
-
-Common::Rect GfxFrameout::upscaleRect(Common::Rect &rect) {
- rect.top = (rect.top * _scriptsRunningHeight) / _screen->getHeight();
- rect.left = (rect.left * _scriptsRunningWidth) / _screen->getWidth();
- rect.bottom = (rect.bottom * _scriptsRunningHeight) / _screen->getHeight();
- rect.right = (rect.right * _scriptsRunningWidth) / _screen->getWidth();
-
- return rect;
-}
-
void GfxFrameout::showVideo() {
bool skipVideo = false;
RobotDecoder *videoDecoder = g_sci->_robotDecoder;
@@ -381,16 +489,16 @@ void GfxFrameout::showVideo() {
uint16 y = videoDecoder->getPos().y;
if (videoDecoder->hasDirtyPalette())
- videoDecoder->setSystemPalette();
+ g_system->getPaletteManager()->setPalette(videoDecoder->getPalette(), 0, 256);
while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) {
if (videoDecoder->needsUpdate()) {
const Graphics::Surface *frame = videoDecoder->decodeNextFrame();
if (frame) {
- g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h);
+ g_system->copyRectToScreen(frame->pixels, frame->pitch, x, y, frame->w, frame->h);
if (videoDecoder->hasDirtyPalette())
- videoDecoder->setSystemPalette();
+ g_system->getPaletteManager()->setPalette(videoDecoder->getPalette(), 0, 256);
g_system->updateScreen();
}
@@ -433,6 +541,7 @@ void GfxFrameout::createPlaneItemList(reg_t planeObject, FrameoutList &itemList)
picEntry->x = planePicture->getSci32celX(pictureCelNr);
picEntry->picStartX = pictureIt->startX;
picEntry->picStartY = pictureIt->startY;
+ picEntry->visible = true;
picEntry->priority = planePicture->getSci32celPriority(pictureCelNr);
@@ -507,13 +616,26 @@ void GfxFrameout::kernelFrameout() {
for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); it++) {
reg_t planeObject = it->object;
- uint16 planeLastPriority = it->lastPriority;
+
+ // Draw any plane lines, if they exist
+ // These are drawn on invisible planes as well. (e.g. "invisiblePlane" in LSL6 hires)
+ // FIXME: Lines aren't always drawn (e.g. when the narrator speaks in LSL6 hires).
+ // Perhaps something is painted over them?
+ for (PlaneLineList::iterator it2 = it->lines.begin(); it2 != it->lines.end(); ++it2) {
+ Common::Point startPoint = it2->startPoint;
+ Common::Point endPoint = it2->endPoint;
+ _coordAdjuster->kernelLocalToGlobal(startPoint.x, startPoint.y, it->object);
+ _coordAdjuster->kernelLocalToGlobal(endPoint.x, endPoint.y, it->object);
+ _screen->drawLine(startPoint, endPoint, it2->color, it2->priority, it2->control);
+ }
+
+ int16 planeLastPriority = it->lastPriority;
// Update priority here, sq6 sets it w/o UpdatePlane
- uint16 planePriority = it->priority = readSelectorValue(_segMan, planeObject, SELECTOR(priority));
+ int16 planePriority = it->priority = readSelectorValue(_segMan, planeObject, SELECTOR(priority));
it->lastPriority = planePriority;
- if (planePriority == 0xffff) { // Plane currently not meant to be shown
+ if (planePriority < 0) { // Plane currently not meant to be shown
// If plane was shown before, delete plane rect
if (planePriority != planeLastPriority)
_paint32->fillRect(it->planeRect, 0);
@@ -523,44 +645,40 @@ void GfxFrameout::kernelFrameout() {
// There is a race condition lurking in SQ6, which causes the game to hang in the intro, when teleporting to Polysorbate LX.
// Since I first wrote the patch, the race has stopped occurring for me though.
// I'll leave this for investigation later, when someone can reproduce.
- //if (it->pictureId == 0xffff) // FIXME: This is what SSCI does, and fixes the intro of LSL7, but breaks the dialogs in GK1 (adds black boxes)
- if (it->planeBack)
+ //if (it->pictureId == kPlanePlainColored) // FIXME: This is what SSCI does, and fixes the intro of LSL7, but breaks the dialogs in GK1 (adds black boxes)
+ if (it->pictureId == kPlanePlainColored && (it->planeBack || g_sci->getGameId() != GID_GK1))
_paint32->fillRect(it->planeRect, it->planeBack);
- GuiResourceId planeMainPictureId = it->pictureId;
-
_coordAdjuster->pictureSetDisplayArea(it->planeRect);
- _palette->drewPicture(planeMainPictureId);
+ _palette->drewPicture(it->pictureId);
FrameoutList itemList;
createPlaneItemList(planeObject, itemList);
-// warning("Plane %s", _segMan->getObjectName(planeObject));
-
for (FrameoutList::iterator listIterator = itemList.begin(); listIterator != itemList.end(); listIterator++) {
FrameoutEntry *itemEntry = *listIterator;
+ if (!itemEntry->visible)
+ continue;
+
if (itemEntry->object.isNull()) {
// Picture cel data
- itemEntry->x = upscaleHorizontalCoordinate(itemEntry->x);
- itemEntry->y = upscaleVerticalCoordinate(itemEntry->y);
- itemEntry->picStartX = upscaleHorizontalCoordinate(itemEntry->picStartX);
- itemEntry->picStartY = upscaleVerticalCoordinate(itemEntry->picStartY);
+ _coordAdjuster->fromScriptToDisplay(itemEntry->y, itemEntry->x);
+ _coordAdjuster->fromScriptToDisplay(itemEntry->picStartY, itemEntry->picStartX);
if (!isPictureOutOfView(itemEntry, it->planeRect, it->planeOffsetX, it->planeOffsetY))
drawPicture(itemEntry, it->planeOffsetX, it->planeOffsetY, it->planePictureMirrored);
} else {
GfxView *view = (itemEntry->viewId != 0xFFFF) ? _cache->getView(itemEntry->viewId) : NULL;
-
+ int16 dummyX = 0;
+
if (view && view->isSci2Hires()) {
- int16 dummyX = 0;
view->adjustToUpscaledCoordinates(itemEntry->y, itemEntry->x);
view->adjustToUpscaledCoordinates(itemEntry->z, dummyX);
- } else if (getSciVersion() == SCI_VERSION_2_1) {
- itemEntry->x = upscaleHorizontalCoordinate(itemEntry->x);
- itemEntry->y = upscaleVerticalCoordinate(itemEntry->y);
- itemEntry->z = upscaleVerticalCoordinate(itemEntry->z);
+ } else if (getSciVersion() >= SCI_VERSION_2_1) {
+ _coordAdjuster->fromScriptToDisplay(itemEntry->y, itemEntry->x);
+ _coordAdjuster->fromScriptToDisplay(itemEntry->z, dummyX);
}
// Adjust according to current scroll position
@@ -581,13 +699,13 @@ void GfxFrameout::kernelFrameout() {
// TODO: maybe we should clip the cels rect with this, i'm not sure
// the only currently known usage is game menu of gk1
} else if (view) {
- if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128))
- view->getCelRect(itemEntry->loopNo, itemEntry->celNo,
- itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->celRect);
- else
- view->getCelScaledRect(itemEntry->loopNo, itemEntry->celNo,
- itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->scaleX,
- itemEntry->scaleY, itemEntry->celRect);
+ if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128))
+ view->getCelRect(itemEntry->loopNo, itemEntry->celNo,
+ itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->celRect);
+ else
+ view->getCelScaledRect(itemEntry->loopNo, itemEntry->celNo,
+ itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->scaleX,
+ itemEntry->scaleY, itemEntry->celRect);
Common::Rect nsRect = itemEntry->celRect;
// Translate back to actual coordinate within scrollable plane
@@ -596,8 +714,9 @@ void GfxFrameout::kernelFrameout() {
if (view && view->isSci2Hires()) {
view->adjustBackUpscaledCoordinates(nsRect.top, nsRect.left);
view->adjustBackUpscaledCoordinates(nsRect.bottom, nsRect.right);
- } else if (getSciVersion() == SCI_VERSION_2_1) {
- nsRect = upscaleRect(nsRect);
+ } else if (getSciVersion() >= SCI_VERSION_2_1) {
+ _coordAdjuster->fromDisplayToScript(nsRect.top, nsRect.left);
+ _coordAdjuster->fromDisplayToScript(nsRect.bottom, nsRect.right);
}
if (g_sci->getGameId() == GID_PHANTASMAGORIA2) {
@@ -610,17 +729,11 @@ void GfxFrameout::kernelFrameout() {
g_sci->_gfxCompare->setNSRect(itemEntry->object, nsRect);
}
- int16 screenHeight = _screen->getHeight();
- int16 screenWidth = _screen->getWidth();
- if (view && view->isSci2Hires()) {
- screenHeight = _screen->getDisplayHeight();
- screenWidth = _screen->getDisplayWidth();
- }
-
- if (itemEntry->celRect.bottom < 0 || itemEntry->celRect.top >= screenHeight)
- continue;
-
- if (itemEntry->celRect.right < 0 || itemEntry->celRect.left >= screenWidth)
+ // Don't attempt to draw sprites that are outside the visible
+ // screen area. An example is the random people walking in
+ // Jackson Square in GK1.
+ if (itemEntry->celRect.bottom < 0 || itemEntry->celRect.top >= _screen->getDisplayHeight() ||
+ itemEntry->celRect.right < 0 || itemEntry->celRect.left >= _screen->getDisplayWidth())
continue;
Common::Rect clipRect, translatedClipRect;
@@ -631,6 +744,9 @@ void GfxFrameout::kernelFrameout() {
translatedClipRect = clipRect;
translatedClipRect.translate(it->upscaledPlaneRect.left, it->upscaledPlaneRect.top);
} else {
+ // QFG4 passes invalid rectangles when a battle is starting
+ if (!clipRect.isValidRect())
+ continue;
clipRect.clip(it->planeClipRect);
translatedClipRect = clipRect;
translatedClipRect.translate(it->planeRect.left, it->planeRect.top);
@@ -639,10 +755,10 @@ void GfxFrameout::kernelFrameout() {
if (view) {
if (!clipRect.isEmpty()) {
if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128))
- view->draw(itemEntry->celRect, clipRect, translatedClipRect,
+ view->draw(itemEntry->celRect, clipRect, translatedClipRect,
itemEntry->loopNo, itemEntry->celNo, 255, 0, view->isSci2Hires());
else
- view->drawScaled(itemEntry->celRect, clipRect, translatedClipRect,
+ view->drawScaled(itemEntry->celRect, clipRect, translatedClipRect,
itemEntry->loopNo, itemEntry->celNo, 255, itemEntry->scaleX, itemEntry->scaleY);
}
}
@@ -662,9 +778,61 @@ void GfxFrameout::kernelFrameout() {
}
}
+ showCurrentScrollText();
+
_screen->copyToScreen();
g_sci->getEngineState()->_throttleTrigger = true;
}
+void GfxFrameout::printPlaneList(Console *con) {
+ for (PlaneList::const_iterator it = _planes.begin(); it != _planes.end(); ++it) {
+ PlaneEntry p = *it;
+ Common::String curPlaneName = _segMan->getObjectName(p.object);
+ Common::Rect r = p.upscaledPlaneRect;
+ Common::Rect cr = p.upscaledPlaneClipRect;
+
+ con->DebugPrintf("%04x:%04x (%s): prio %d, lastprio %d, offsetX %d, offsetY %d, pic %d, mirror %d, back %d\n",
+ PRINT_REG(p.object), curPlaneName.c_str(),
+ (int16)p.priority, (int16)p.lastPriority,
+ p.planeOffsetX, p.planeOffsetY, p.pictureId,
+ p.planePictureMirrored, p.planeBack);
+ con->DebugPrintf(" rect: (%d, %d, %d, %d), clip rect: (%d, %d, %d, %d)\n",
+ r.left, r.top, r.right, r.bottom,
+ cr.left, cr.top, cr.right, cr.bottom);
+
+ if (p.pictureId != 0xffff && p.pictureId != 0xfffe) {
+ con->DebugPrintf("Pictures:\n");
+
+ for (PlanePictureList::iterator pictureIt = _planePictures.begin(); pictureIt != _planePictures.end(); pictureIt++) {
+ if (pictureIt->object == p.object) {
+ con->DebugPrintf(" Picture %d: x %d, y %d\n", pictureIt->pictureId, pictureIt->startX, pictureIt->startY);
+ }
+ }
+ }
+ }
+}
+
+void GfxFrameout::printPlaneItemList(Console *con, reg_t planeObject) {
+ for (FrameoutList::iterator listIterator = _screenItems.begin(); listIterator != _screenItems.end(); listIterator++) {
+ FrameoutEntry *e = *listIterator;
+ reg_t itemPlane = readSelector(_segMan, e->object, SELECTOR(plane));
+
+ if (planeObject == itemPlane) {
+ Common::String curItemName = _segMan->getObjectName(e->object);
+ Common::Rect icr = e->celRect;
+ GuiResourceId picId = e->picture ? e->picture->getResourceId() : 0;
+
+ con->DebugPrintf("%d: %04x:%04x (%s), view %d, loop %d, cel %d, x %d, y %d, z %d, "
+ "signal %d, scale signal %d, scaleX %d, scaleY %d, rect (%d, %d, %d, %d), "
+ "pic %d, picX %d, picY %d, visible %d\n",
+ e->givenOrderNr, PRINT_REG(e->object), curItemName.c_str(),
+ e->viewId, e->loopNo, e->celNo, e->x, e->y, e->z,
+ e->signal, e->scaleSignal, e->scaleX, e->scaleY,
+ icr.left, icr.top, icr.right, icr.bottom,
+ picId, e->picStartX, e->picStartY, e->visible);
+ }
+ }
+}
+
} // End of namespace Sci
diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h
index 8c3cc261d5..5fd2824224 100644
--- a/engines/sci/graphics/frameout.h
+++ b/engines/sci/graphics/frameout.h
@@ -27,10 +27,21 @@ namespace Sci {
class GfxPicture;
+struct PlaneLineEntry {
+ reg_t hunkId;
+ Common::Point startPoint;
+ Common::Point endPoint;
+ byte color;
+ byte priority;
+ byte control;
+};
+
+typedef Common::List<PlaneLineEntry> PlaneLineList;
+
struct PlaneEntry {
reg_t object;
- uint16 priority;
- uint16 lastPriority;
+ int16 priority;
+ int16 lastPriority;
int16 planeOffsetX;
int16 planeOffsetY;
GuiResourceId pictureId;
@@ -40,6 +51,7 @@ struct PlaneEntry {
Common::Rect upscaledPlaneClipRect;
bool planePictureMirrored;
byte planeBack;
+ PlaneLineList lines;
};
typedef Common::List<PlaneEntry> PlaneList;
@@ -60,6 +72,7 @@ struct FrameoutEntry {
GfxPicture *picture;
int16 picStartX;
int16 picStartY;
+ bool visible;
};
typedef Common::List<FrameoutEntry *> FrameoutList;
@@ -75,6 +88,15 @@ struct PlanePictureEntry {
typedef Common::List<PlanePictureEntry> PlanePictureList;
+struct ScrollTextEntry {
+ reg_t bitmapHandle;
+ reg_t kWindow;
+ uint16 x;
+ uint16 y;
+};
+
+typedef Common::Array<ScrollTextEntry> ScrollTextList;
+
class GfxCache;
class GfxCoordAdjuster32;
class GfxPaint32;
@@ -102,16 +124,30 @@ public:
void addPlanePicture(reg_t object, GuiResourceId pictureId, uint16 startX, uint16 startY = 0);
void deletePlanePictures(reg_t object);
+ reg_t addPlaneLine(reg_t object, Common::Point startPoint, Common::Point endPoint, byte color, byte priority, byte control);
+ void updatePlaneLine(reg_t object, reg_t hunkId, Common::Point startPoint, Common::Point endPoint, byte color, byte priority, byte control);
+ void deletePlaneLine(reg_t object, reg_t hunkId);
void clear();
+ // Scroll text functions
+ void addScrollTextEntry(Common::String &text, reg_t kWindow, uint16 x, uint16 y, bool replace);
+ void showCurrentScrollText();
+ void initScrollText(uint16 maxItems) { _maxScrollTexts = maxItems; }
+ void clearScrollTexts();
+ void firstScrollText() { if (_scrollTexts.size() > 0) _curScrollText = 0; }
+ void lastScrollText() { if (_scrollTexts.size() > 0) _curScrollText = _scrollTexts.size() - 1; }
+ void prevScrollText() { if (_curScrollText > 0) _curScrollText--; }
+ void nextScrollText() { if (_curScrollText + 1 < (uint16)_scrollTexts.size()) _curScrollText++; }
+ void toggleScrollText(bool show) { _showScrollText = show; }
+
+ void printPlaneList(Console *con);
+ void printPlaneItemList(Console *con, reg_t planeObject);
+
private:
void showVideo();
void createPlaneItemList(reg_t planeObject, FrameoutList &itemList);
bool isPictureOutOfView(FrameoutEntry *itemEntry, Common::Rect planeRect, int16 planeOffsetX, int16 planeOffsetY);
void drawPicture(FrameoutEntry *itemEntry, int16 planeOffsetX, int16 planeOffsetY, bool planePictureMirrored);
- int16 upscaleHorizontalCoordinate(int16 coordinate);
- int16 upscaleVerticalCoordinate(int16 coordinate);
- Common::Rect upscaleRect(Common::Rect &rect);
SegManager *_segMan;
ResourceManager *_resMan;
@@ -124,11 +160,12 @@ private:
FrameoutList _screenItems;
PlaneList _planes;
PlanePictureList _planePictures;
+ ScrollTextList _scrollTexts;
+ int16 _curScrollText;
+ bool _showScrollText;
+ uint16 _maxScrollTexts;
void sortPlanes();
-
- uint16 _scriptsRunningWidth;
- uint16 _scriptsRunningHeight;
};
} // End of namespace Sci
diff --git a/engines/sci/graphics/maciconbar.cpp b/engines/sci/graphics/maciconbar.cpp
index bff145ad53..dfb50b0edb 100644
--- a/engines/sci/graphics/maciconbar.cpp
+++ b/engines/sci/graphics/maciconbar.cpp
@@ -31,8 +31,8 @@
#include "common/memstream.h"
#include "common/system.h"
-#include "graphics/pict.h"
#include "graphics/surface.h"
+#include "graphics/decoders/pict.h"
namespace Sci {
@@ -129,7 +129,7 @@ void GfxMacIconBar::drawIcon(uint16 iconIndex, bool selected) {
void GfxMacIconBar::drawEnabledImage(Graphics::Surface *surface, const Common::Rect &rect) {
if (surface)
- g_system->copyRectToScreen((byte *)surface->pixels, surface->pitch, rect.left, rect.top, rect.width(), rect.height());
+ g_system->copyRectToScreen(surface->pixels, surface->pitch, rect.left, rect.top, rect.width(), rect.height());
}
void GfxMacIconBar::drawDisabledImage(Graphics::Surface *surface, const Common::Rect &rect) {
@@ -153,7 +153,7 @@ void GfxMacIconBar::drawDisabledImage(Graphics::Surface *surface, const Common::
*((byte *)newSurf.getBasePtr(j, i)) = 0;
}
- g_system->copyRectToScreen((byte *)newSurf.pixels, newSurf.pitch, rect.left, rect.top, rect.width(), rect.height());
+ g_system->copyRectToScreen(newSurf.pixels, newSurf.pitch, rect.left, rect.top, rect.width(), rect.height());
newSurf.free();
}
@@ -201,18 +201,20 @@ void GfxMacIconBar::setInventoryIcon(int16 icon) {
}
Graphics::Surface *GfxMacIconBar::loadPict(ResourceId id) {
- Graphics::PictDecoder pictDecoder(Graphics::PixelFormat::createFormatCLUT8());
+ Graphics::PICTDecoder pictDecoder;
Resource *res = g_sci->getResMan()->findResource(id, false);
if (!res || res->size == 0)
return 0;
- byte palette[256 * 3];
- Common::SeekableReadStream *stream = new Common::MemoryReadStream(res->data, res->size);
- Graphics::Surface *surface = pictDecoder.decodeImage(stream, palette);
- remapColors(surface, palette);
+ Common::MemoryReadStream stream(res->data, res->size);
+ if (!pictDecoder.loadStream(stream))
+ return 0;
+
+ Graphics::Surface *surface = new Graphics::Surface();
+ surface->copyFrom(*pictDecoder.getSurface());
+ remapColors(surface, pictDecoder.getPalette());
- delete stream;
return surface;
}
@@ -221,7 +223,7 @@ Graphics::Surface *GfxMacIconBar::createImage(uint32 iconIndex, bool isSelected)
return loadPict(ResourceId(type, iconIndex + 1));
}
-void GfxMacIconBar::remapColors(Graphics::Surface *surf, byte *palette) {
+void GfxMacIconBar::remapColors(Graphics::Surface *surf, const byte *palette) {
byte *pixels = (byte *)surf->pixels;
// Remap to the screen palette
diff --git a/engines/sci/graphics/maciconbar.h b/engines/sci/graphics/maciconbar.h
index 43de37a904..eca10b804c 100644
--- a/engines/sci/graphics/maciconbar.h
+++ b/engines/sci/graphics/maciconbar.h
@@ -61,7 +61,7 @@ private:
Graphics::Surface *loadPict(ResourceId id);
Graphics::Surface *createImage(uint32 iconIndex, bool isSelected);
- void remapColors(Graphics::Surface *surf, byte *palette);
+ void remapColors(Graphics::Surface *surf, const byte *palette);
void drawIcon(uint16 index, bool selected);
void drawSelectedImage(uint16 index);
diff --git a/engines/sci/graphics/menu.cpp b/engines/sci/graphics/menu.cpp
index 673729784f..bfecc296a2 100644
--- a/engines/sci/graphics/menu.cpp
+++ b/engines/sci/graphics/menu.cpp
@@ -219,7 +219,7 @@ void GfxMenu::kernelAddEntry(Common::String title, Common::String content, reg_t
}
}
itemEntry->textVmPtr = contentVmPtr;
- itemEntry->textVmPtr.offset += beginPos;
+ itemEntry->textVmPtr.incOffset(beginPos);
if (rightAlignedPos) {
rightAlignedPos++;
@@ -286,7 +286,7 @@ void GfxMenu::kernelSetAttribute(uint16 menuId, uint16 itemId, uint16 attributeI
switch (attributeId) {
case SCI_MENU_ATTRIBUTE_ENABLED:
- itemEntry->enabled = value.isNull() ? false : true;
+ itemEntry->enabled = !value.isNull();
break;
case SCI_MENU_ATTRIBUTE_SAID:
itemEntry->saidVmPtr = value;
@@ -297,13 +297,13 @@ void GfxMenu::kernelSetAttribute(uint16 menuId, uint16 itemId, uint16 attributeI
// We assume here that no script ever creates a separatorLine dynamically
break;
case SCI_MENU_ATTRIBUTE_KEYPRESS:
- itemEntry->keyPress = tolower(value.offset);
+ itemEntry->keyPress = tolower(value.getOffset());
itemEntry->keyModifier = 0;
// TODO: Find out how modifier is handled
- debug("setAttr keypress %X %X", value.segment, value.offset);
+ debug("setAttr keypress %X %X", value.getSegment(), value.getOffset());
break;
case SCI_MENU_ATTRIBUTE_TAG:
- itemEntry->tag = value.offset;
+ itemEntry->tag = value.getOffset();
break;
default:
// Happens when loading a game in LSL3 - attribute 1A
@@ -606,7 +606,7 @@ void GfxMenu::drawMenu(uint16 oldMenuId, uint16 newMenuId) {
listItemEntry = *listItemIterator;
if (listItemEntry->menuId == newMenuId) {
if (!listItemEntry->separatorLine) {
- _ports->textGreyedOutput(listItemEntry->enabled ? false : true);
+ _ports->textGreyedOutput(!listItemEntry->enabled);
_ports->moveTo(_menuRect.left, topPos);
_text16->DrawString(listItemEntry->textSplit.c_str());
_ports->moveTo(_menuRect.right - listItemEntry->textRightAlignedWidth - 5, topPos);
diff --git a/engines/sci/graphics/paint16.cpp b/engines/sci/graphics/paint16.cpp
index 23177dfc7b..d20aa80c77 100644
--- a/engines/sci/graphics/paint16.cpp
+++ b/engines/sci/graphics/paint16.cpp
@@ -491,10 +491,10 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) {
// processing codes in argv
while (argc > 0) {
displayArg = argv[0];
- if (displayArg.segment)
- displayArg.offset = 0xFFFF;
+ if (displayArg.getSegment())
+ displayArg.setOffset(0xFFFF);
argc--; argv++;
- switch (displayArg.offset) {
+ switch (displayArg.getOffset()) {
case SCI_DISPLAY_MOVEPEN:
_ports->moveTo(argv[0].toUint16(), argv[1].toUint16());
argc -= 2; argv += 2;
@@ -513,7 +513,7 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) {
argc--; argv++;
break;
case SCI_DISPLAY_SETGREYEDOUTPUT:
- _ports->textGreyedOutput(argv[0].isNull() ? false : true);
+ _ports->textGreyedOutput(!argv[0].isNull());
argc--; argv++;
break;
case SCI_DISPLAY_SETFONT:
@@ -547,9 +547,9 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) {
if (!(g_sci->getGameId() == GID_LONGBOW && g_sci->isDemo()) &&
!(g_sci->getGameId() == GID_QFG1 && g_sci->isDemo()) &&
!(g_sci->getGameId() == GID_PQ2))
- error("Unknown kDisplay argument %d", displayArg.offset);
+ error("Unknown kDisplay argument %d", displayArg.getOffset());
- if (displayArg.offset == SCI_DISPLAY_DUMMY2) {
+ if (displayArg.getOffset() == SCI_DISPLAY_DUMMY2) {
if (!argc)
error("No parameter left for kDisplay(115)");
argc--; argv++;
@@ -559,8 +559,8 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) {
SciTrackOriginReply originReply;
SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, kDisplay_workarounds, &originReply);
if (solution.type == WORKAROUND_NONE)
- error("Unknown kDisplay argument (%04x:%04x) from method %s::%s (script %d, localCall %x)",
- PRINT_REG(displayArg), originReply.objectName.c_str(), originReply.methodName.c_str(),
+ error("Unknown kDisplay argument (%04x:%04x) from method %s::%s (script %d, localCall %x)",
+ PRINT_REG(displayArg), originReply.objectName.c_str(), originReply.methodName.c_str(),
originReply.scriptNr, originReply.localCallOffset);
assert(solution.type == WORKAROUND_IGNORE);
break;
diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp
index 47d1647c6c..53d69cdcca 100644
--- a/engines/sci/graphics/palette.cpp
+++ b/engines/sci/graphics/palette.cpp
@@ -100,6 +100,9 @@ GfxPalette::GfxPalette(ResourceManager *resMan, GfxScreen *screen)
default:
error("GfxPalette: Unknown view type");
}
+
+ _remapOn = false;
+ resetRemapping();
}
GfxPalette::~GfxPalette() {
@@ -140,8 +143,9 @@ void GfxPalette::createFromData(byte *data, int bytesLeft, Palette *paletteOut)
memset(paletteOut, 0, sizeof(Palette));
// Setup 1:1 mapping
- for (colorNo = 0; colorNo < 256; colorNo++)
+ for (colorNo = 0; colorNo < 256; colorNo++) {
paletteOut->mapping[colorNo] = colorNo;
+ }
if (bytesLeft < 37) {
// This happens when loading palette of picture 0 in sq5 - the resource is broken and doesn't contain a full
@@ -329,6 +333,79 @@ void GfxPalette::set(Palette *newPalette, bool force, bool forceRealMerge) {
}
}
+byte GfxPalette::remapColor(byte remappedColor, byte screenColor) {
+ assert(_remapOn);
+ if (_remappingType[remappedColor] == kRemappingByRange)
+ return _remappingByRange[screenColor];
+ else if (_remappingType[remappedColor] == kRemappingByPercent)
+ return _remappingByPercent[screenColor];
+ else
+ error("remapColor(): Color %d isn't remapped", remappedColor);
+
+ return 0; // should never reach here
+}
+
+void GfxPalette::resetRemapping() {
+ _remapOn = false;
+ _remappingPercentToSet = 0;
+
+ for (int i = 0; i < 256; i++) {
+ _remappingType[i] = kRemappingNone;
+ _remappingByPercent[i] = i;
+ _remappingByRange[i] = i;
+ }
+}
+
+void GfxPalette::setRemappingPercent(byte color, byte percent) {
+ _remapOn = true;
+
+ // We need to defer the setup of the remapping table every time the screen
+ // palette is changed, so that kernelFindColor() can find the correct
+ // colors. Set it once here, in case the palette stays the same and update
+ // it on each palette change by copySysPaletteToScreen().
+ _remappingPercentToSet = percent;
+
+ for (int i = 0; i < 256; i++) {
+ byte r = _sysPalette.colors[i].r * _remappingPercentToSet / 100;
+ byte g = _sysPalette.colors[i].g * _remappingPercentToSet / 100;
+ byte b = _sysPalette.colors[i].b * _remappingPercentToSet / 100;
+ _remappingByPercent[i] = kernelFindColor(r, g, b);
+ }
+
+ _remappingType[color] = kRemappingByPercent;
+}
+
+void GfxPalette::setRemappingPercentGray(byte color, byte percent) {
+ _remapOn = true;
+
+ // We need to defer the setup of the remapping table every time the screen
+ // palette is changed, so that kernelFindColor() can find the correct
+ // colors. Set it once here, in case the palette stays the same and update
+ // it on each palette change by copySysPaletteToScreen().
+ _remappingPercentToSet = percent;
+
+ // Note: This is not what the original does, but the results are the same visually
+ for (int i = 0; i < 256; i++) {
+ byte rComponent = _sysPalette.colors[i].r * _remappingPercentToSet * 0.30 / 100;
+ byte gComponent = _sysPalette.colors[i].g * _remappingPercentToSet * 0.59 / 100;
+ byte bComponent = _sysPalette.colors[i].b * _remappingPercentToSet * 0.11 / 100;
+ byte luminosity = rComponent + gComponent + bComponent;
+ _remappingByPercent[i] = kernelFindColor(luminosity, luminosity, luminosity);
+ }
+
+ _remappingType[color] = kRemappingByPercent;
+}
+
+void GfxPalette::setRemappingRange(byte color, byte from, byte to, byte base) {
+ _remapOn = true;
+
+ for (int i = from; i <= to; i++) {
+ _remappingByRange[i] = i + base;
+ }
+
+ _remappingType[color] = kRemappingByRange;
+}
+
bool GfxPalette::insert(Palette *newPalette, Palette *destPalette) {
bool paletteChanged = false;
@@ -491,6 +568,16 @@ void GfxPalette::copySysPaletteToScreen() {
}
}
+ // Check if we need to reset remapping by percent with the new colors.
+ if (_remappingPercentToSet) {
+ for (int i = 0; i < 256; i++) {
+ byte r = _sysPalette.colors[i].r * _remappingPercentToSet / 100;
+ byte g = _sysPalette.colors[i].g * _remappingPercentToSet / 100;
+ byte b = _sysPalette.colors[i].b * _remappingPercentToSet / 100;
+ _remappingByPercent[i] = kernelFindColor(r, g, b);
+ }
+ }
+
g_system->getPaletteManager()->setPalette(bpal, 0, 256);
}
@@ -698,7 +785,7 @@ void GfxPalette::palVaryInit() {
}
bool GfxPalette::palVaryLoadTargetPalette(GuiResourceId resourceId) {
- _palVaryResourceId = resourceId;
+ _palVaryResourceId = (resourceId != 65535) ? resourceId : -1;
Resource *palResource = _resMan->findResource(ResourceId(kResourceTypePalette, resourceId), false);
if (palResource) {
// Load and initialize destination palette
@@ -999,8 +1086,9 @@ bool GfxPalette::loadClut(uint16 clutId) {
memset(&pal, 0, sizeof(Palette));
// Setup 1:1 mapping
- for (int i = 0; i < 256; i++)
+ for (int i = 0; i < 256; i++) {
pal.mapping[i] = i;
+ }
// Now load in the palette
for (int i = 1; i <= 236; i++) {
diff --git a/engines/sci/graphics/palette.h b/engines/sci/graphics/palette.h
index a9ea1c32de..e974781d49 100644
--- a/engines/sci/graphics/palette.h
+++ b/engines/sci/graphics/palette.h
@@ -31,6 +31,12 @@ namespace Sci {
class ResourceManager;
class GfxScreen;
+enum ColorRemappingType {
+ kRemappingNone = 0,
+ kRemappingByRange = 1,
+ kRemappingByPercent = 2
+};
+
/**
* Palette class, handles palette operations like changing intensity, setting up the palette, merging different palettes
*/
@@ -53,6 +59,15 @@ public:
void getSys(Palette *pal);
uint16 getTotalColorCount() const { return _totalScreenColors; }
+ void resetRemapping();
+ void setRemappingPercent(byte color, byte percent);
+ void setRemappingPercentGray(byte color, byte percent);
+ void setRemappingRange(byte color, byte from, byte to, byte base);
+ bool isRemapped(byte color) const {
+ return _remapOn && (_remappingType[color] != kRemappingNone);
+ }
+ byte remapColor(byte remappedColor, byte screenColor);
+
void setOnScreen();
void copySysPaletteToScreen();
@@ -123,6 +138,12 @@ private:
int _palVarySignal;
uint16 _totalScreenColors;
+ bool _remapOn;
+ ColorRemappingType _remappingType[256];
+ byte _remappingByPercent[256];
+ byte _remappingByRange[256];
+ uint16 _remappingPercentToSet;
+
void loadMacIconBarPalette();
byte *_macClut;
diff --git a/engines/sci/graphics/ports.cpp b/engines/sci/graphics/ports.cpp
index 6b4c8180bf..8acdeed763 100644
--- a/engines/sci/graphics/ports.cpp
+++ b/engines/sci/graphics/ports.cpp
@@ -380,17 +380,50 @@ Window *GfxPorts::addWindow(const Common::Rect &dims, const Common::Rect *restor
int16 oldtop = pwnd->dims.top;
int16 oldleft = pwnd->dims.left;
- if (wmprect.top > pwnd->dims.top)
+ // WORKAROUND: We also adjust the restore rect when adjusting the window
+ // rect.
+ // SSCI does not do this. It wasn't necessary in the original interpreter,
+ // but it is needed for Freddy Pharkas CD. This version does not normally
+ // have text, but we allow this by modifying the text/speech setting
+ // according to what is set in the ScummVM GUI (refer to syncIngameAudioOptions()
+ // in sci.cpp). Since the text used in Freddy Pharkas CD is quite large in
+ // some cases, it ends up being offset in order to fit inside the screen,
+ // but the associated restore rect isn't adjusted accordingly, leading to
+ // artifacts being left on screen when some text boxes are removed. The
+ // fact that the restore rect wasn't ever adjusted doesn't make sense, and
+ // adjusting it shouldn't have any negative side-effects (it *should* be
+ // adjusted, normally, but SCI doesn't do it). The big text boxes are still
+ // odd-looking, because the text rect is drawn outside the text window rect,
+ // but at least there aren't any leftover textbox artifacts left when the
+ // boxes are removed. Adjusting the text window rect would require more
+ // invasive changes than this one, thus it's not really worth the effort
+ // for a feature that was not present in the original game, and its
+ // implementation is buggy in the first place.
+ // Adjusting the restore rect properly fixes bug #3575276.
+
+ if (wmprect.top > pwnd->dims.top) {
pwnd->dims.moveTo(pwnd->dims.left, wmprect.top);
+ if (restoreRect)
+ pwnd->restoreRect.moveTo(pwnd->restoreRect.left, wmprect.top);
+ }
- if (wmprect.bottom < pwnd->dims.bottom)
+ if (wmprect.bottom < pwnd->dims.bottom) {
pwnd->dims.moveTo(pwnd->dims.left, wmprect.bottom - pwnd->dims.bottom + pwnd->dims.top);
+ if (restoreRect)
+ pwnd->restoreRect.moveTo(pwnd->restoreRect.left, wmprect.bottom - pwnd->restoreRect.bottom + pwnd->restoreRect.top);
+ }
- if (wmprect.right < pwnd->dims.right)
+ if (wmprect.right < pwnd->dims.right) {
pwnd->dims.moveTo(wmprect.right + pwnd->dims.left - pwnd->dims.right, pwnd->dims.top);
+ if (restoreRect)
+ pwnd->restoreRect.moveTo(wmprect.right + pwnd->restoreRect.left - pwnd->restoreRect.right, pwnd->restoreRect.top);
+ }
- if (wmprect.left > pwnd->dims.left)
+ if (wmprect.left > pwnd->dims.left) {
pwnd->dims.moveTo(wmprect.left, pwnd->dims.top);
+ if (restoreRect)
+ pwnd->restoreRect.moveTo(wmprect.left, pwnd->restoreRect.top);
+ }
pwnd->rect.moveTo(pwnd->rect.left + pwnd->dims.left - oldleft, pwnd->rect.top + pwnd->dims.top - oldtop);
diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp
index 4020518b72..246b6bfff9 100644
--- a/engines/sci/graphics/screen.cpp
+++ b/engines/sci/graphics/screen.cpp
@@ -53,23 +53,35 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) {
#ifdef ENABLE_SCI32
// GK1 Mac uses a 640x480 resolution too
- if (g_sci->getGameId() == GID_GK1 && g_sci->getPlatform() == Common::kPlatformMacintosh)
- _upscaledHires = GFX_SCREEN_UPSCALED_640x480;
+ if (g_sci->getPlatform() == Common::kPlatformMacintosh) {
+ if (g_sci->getGameId() == GID_GK1)
+ _upscaledHires = GFX_SCREEN_UPSCALED_640x480;
+ }
#endif
if (_resMan->detectHires()) {
_width = 640;
+ _pitch = 640;
_height = 480;
} else {
_width = 320;
+ _pitch = 320;
_height = getLowResScreenHeight();
}
+#ifdef ENABLE_SCI32
+ // Phantasmagoria 1 sets a window area of 630x450
+ if (g_sci->getGameId() == GID_PHANTASMAGORIA) {
+ _width = 630;
+ _height = 450;
+ }
+#endif
+
// Japanese versions of games use hi-res font on upscaled version of the game.
if ((g_sci->getLanguage() == Common::JA_JPN) && (getSciVersion() <= SCI_VERSION_1_1))
_upscaledHires = GFX_SCREEN_UPSCALED_640x400;
- _pixels = _width * _height;
+ _pixels = _pitch * _height;
switch (_upscaledHires) {
case GFX_SCREEN_UPSCALED_640x400:
@@ -91,7 +103,7 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) {
_upscaledMapping[i] = (i * 12) / 5;
break;
default:
- _displayWidth = _width;
+ _displayWidth = _pitch;
_displayHeight = _height;
memset(&_upscaledMapping, 0, sizeof(_upscaledMapping) );
break;
@@ -207,7 +219,7 @@ byte GfxScreen::getDrawingMask(byte color, byte prio, byte control) {
}
void GfxScreen::putPixel(int x, int y, byte drawMask, byte color, byte priority, byte control) {
- int offset = y * _width + x;
+ int offset = y * _pitch + x;
if (drawMask & GFX_SCREEN_MASK_VISUAL) {
_visualScreen[offset] = color;
@@ -240,7 +252,7 @@ void GfxScreen::putFontPixel(int startingY, int x, int y, byte color) {
// Do not scale ourselves, but put it on the display directly
putPixelOnDisplay(x, y + startingY, color);
} else {
- int offset = (startingY + y) * _width + x;
+ int offset = (startingY + y) * _pitch + x;
_visualScreen[offset] = color;
if (!_upscaledHires) {
@@ -342,19 +354,19 @@ void GfxScreen::putKanjiChar(Graphics::FontSJIS *commonFont, int16 x, int16 y, u
}
byte GfxScreen::getVisual(int x, int y) {
- return _visualScreen[y * _width + x];
+ return _visualScreen[y * _pitch + x];
}
byte GfxScreen::getPriority(int x, int y) {
- return _priorityScreen[y * _width + x];
+ return _priorityScreen[y * _pitch + x];
}
byte GfxScreen::getControl(int x, int y) {
- return _controlScreen[y * _width + x];
+ return _controlScreen[y * _pitch + x];
}
byte GfxScreen::isFillMatch(int16 x, int16 y, byte screenMask, byte t_color, byte t_pri, byte t_con, bool isEGA) {
- int offset = y * _width + x;
+ int offset = y * _pitch + x;
byte match = 0;
if (screenMask & GFX_SCREEN_MASK_VISUAL) {
@@ -415,14 +427,14 @@ void GfxScreen::bitsSave(Common::Rect rect, byte mask, byte *memoryPtr) {
memcpy(memoryPtr, (void *)&mask, sizeof(mask)); memoryPtr += sizeof(mask);
if (mask & GFX_SCREEN_MASK_VISUAL) {
- bitsSaveScreen(rect, _visualScreen, _width, memoryPtr);
+ bitsSaveScreen(rect, _visualScreen, _pitch, memoryPtr);
bitsSaveDisplayScreen(rect, memoryPtr);
}
if (mask & GFX_SCREEN_MASK_PRIORITY) {
- bitsSaveScreen(rect, _priorityScreen, _width, memoryPtr);
+ bitsSaveScreen(rect, _priorityScreen, _pitch, memoryPtr);
}
if (mask & GFX_SCREEN_MASK_CONTROL) {
- bitsSaveScreen(rect, _controlScreen, _width, memoryPtr);
+ bitsSaveScreen(rect, _controlScreen, _pitch, memoryPtr);
}
if (mask & GFX_SCREEN_MASK_DISPLAY) {
if (!_upscaledHires)
@@ -475,14 +487,14 @@ void GfxScreen::bitsRestore(byte *memoryPtr) {
memcpy((void *)&mask, memoryPtr, sizeof(mask)); memoryPtr += sizeof(mask);
if (mask & GFX_SCREEN_MASK_VISUAL) {
- bitsRestoreScreen(rect, memoryPtr, _visualScreen, _width);
+ bitsRestoreScreen(rect, memoryPtr, _visualScreen, _pitch);
bitsRestoreDisplayScreen(rect, memoryPtr);
}
if (mask & GFX_SCREEN_MASK_PRIORITY) {
- bitsRestoreScreen(rect, memoryPtr, _priorityScreen, _width);
+ bitsRestoreScreen(rect, memoryPtr, _priorityScreen, _pitch);
}
if (mask & GFX_SCREEN_MASK_CONTROL) {
- bitsRestoreScreen(rect, memoryPtr, _controlScreen, _width);
+ bitsRestoreScreen(rect, memoryPtr, _controlScreen, _pitch);
}
if (mask & GFX_SCREEN_MASK_DISPLAY) {
if (!_upscaledHires)
@@ -560,7 +572,7 @@ void GfxScreen::dither(bool addToFlag) {
if (!_unditheringEnabled) {
// Do dithering on visual and display-screen
for (y = 0; y < _height; y++) {
- for (x = 0; x < _width; x++) {
+ for (x = 0; x < _pitch; x++) {
color = *visualPtr;
if (color & 0xF0) {
color ^= color << 4;
@@ -585,7 +597,7 @@ void GfxScreen::dither(bool addToFlag) {
memset(&_ditheredPicColors, 0, sizeof(_ditheredPicColors));
// Do dithering on visual screen and put decoded but undithered byte onto display-screen
for (y = 0; y < _height; y++) {
- for (x = 0; x < _width; x++) {
+ for (x = 0; x < _pitch; x++) {
color = *visualPtr;
if (color & 0xF0) {
color ^= color << 4;
diff --git a/engines/sci/graphics/screen.h b/engines/sci/graphics/screen.h
index 73ea596ba1..01fb899edb 100644
--- a/engines/sci/graphics/screen.h
+++ b/engines/sci/graphics/screen.h
@@ -132,6 +132,7 @@ public:
private:
uint16 _width;
+ uint16 _pitch;
uint16 _height;
uint _pixels;
uint16 _displayWidth;
diff --git a/engines/sci/graphics/text32.cpp b/engines/sci/graphics/text32.cpp
index 7894c7109c..f14ae2ef0b 100644
--- a/engines/sci/graphics/text32.cpp
+++ b/engines/sci/graphics/text32.cpp
@@ -49,9 +49,12 @@ GfxText32::GfxText32(SegManager *segMan, GfxCache *fonts, GfxScreen *screen)
GfxText32::~GfxText32() {
}
+reg_t GfxText32::createScrollTextBitmap(Common::String text, reg_t textObject, uint16 maxWidth, uint16 maxHeight, reg_t prevHunk) {
+ return createTextBitmapInternal(text, textObject, maxWidth, maxHeight, prevHunk);
+
+}
reg_t GfxText32::createTextBitmap(reg_t textObject, uint16 maxWidth, uint16 maxHeight, reg_t prevHunk) {
reg_t stringObject = readSelector(_segMan, textObject, SELECTOR(text));
-
// The object in the text selector of the item can be either a raw string
// or a Str object. In the latter case, we need to access the object's data
// selector to get the raw string.
@@ -59,6 +62,11 @@ reg_t GfxText32::createTextBitmap(reg_t textObject, uint16 maxWidth, uint16 maxH
stringObject = readSelector(_segMan, stringObject, SELECTOR(data));
Common::String text = _segMan->getString(stringObject);
+
+ return createTextBitmapInternal(text, textObject, maxWidth, maxHeight, prevHunk);
+}
+
+reg_t GfxText32::createTextBitmapInternal(Common::String &text, reg_t textObject, uint16 maxWidth, uint16 maxHeight, reg_t prevHunk) {
// HACK: The character offsets of the up and down arrow buttons are off by one
// in GK1, for some unknown reason. Fix them here.
if (text.size() == 1 && (text[0] == 29 || text[0] == 30)) {
@@ -91,7 +99,11 @@ reg_t GfxText32::createTextBitmap(reg_t textObject, uint16 maxWidth, uint16 maxH
reg_t memoryId = NULL_REG;
if (prevHunk.isNull()) {
memoryId = _segMan->allocateHunkEntry("TextBitmap()", entrySize);
- writeSelector(_segMan, textObject, SELECTOR(bitmap), memoryId);
+
+ // Scroll text objects have no bitmap selector!
+ ObjVarRef varp;
+ if (lookupSelector(_segMan, textObject, SELECTOR(bitmap), &varp, NULL) == kSelectorVariable)
+ writeSelector(_segMan, textObject, SELECTOR(bitmap), memoryId);
} else {
memoryId = prevHunk;
}
@@ -153,10 +165,24 @@ reg_t GfxText32::createTextBitmap(reg_t textObject, uint16 maxWidth, uint16 maxH
warning("Invalid alignment %d used in TextBox()", alignment);
}
+ byte curChar;
+
for (int i = 0; i < charCount; i++) {
- unsigned char curChar = txt[i];
- font->drawToBuffer(curChar, curY + offsetY, curX + offsetX, foreColor, dimmed, bitmap, width, height);
- curX += font->getCharWidth(curChar);
+ curChar = txt[i];
+
+ switch (curChar) {
+ case 0x0A:
+ case 0x0D:
+ case 0:
+ break;
+ case 0x7C:
+ warning("Code processing isn't implemented in SCI32");
+ break;
+ default:
+ font->drawToBuffer(curChar, curY + offsetY, curX + offsetX, foreColor, dimmed, bitmap, width, height);
+ curX += font->getCharWidth(curChar);
+ break;
+ }
}
curX = 0;
@@ -175,7 +201,25 @@ void GfxText32::disposeTextBitmap(reg_t hunkId) {
void GfxText32::drawTextBitmap(int16 x, int16 y, Common::Rect planeRect, reg_t textObject) {
reg_t hunkId = readSelector(_segMan, textObject, SELECTOR(bitmap));
- uint16 backColor = readSelectorValue(_segMan, textObject, SELECTOR(back));
+ drawTextBitmapInternal(x, y, planeRect, textObject, hunkId);
+}
+
+void GfxText32::drawScrollTextBitmap(reg_t textObject, reg_t hunkId, uint16 x, uint16 y) {
+ /*reg_t plane = readSelector(_segMan, textObject, SELECTOR(plane));
+ Common::Rect planeRect;
+ planeRect.top = readSelectorValue(_segMan, plane, SELECTOR(top));
+ planeRect.left = readSelectorValue(_segMan, plane, SELECTOR(left));
+ planeRect.bottom = readSelectorValue(_segMan, plane, SELECTOR(bottom));
+ planeRect.right = readSelectorValue(_segMan, plane, SELECTOR(right));
+
+ drawTextBitmapInternal(x, y, planeRect, textObject, hunkId);*/
+
+ // HACK: we pretty much ignore the plane rect and x, y...
+ drawTextBitmapInternal(0, 0, Common::Rect(20, 390, 600, 460), textObject, hunkId);
+}
+
+void GfxText32::drawTextBitmapInternal(int16 x, int16 y, Common::Rect planeRect, reg_t textObject, reg_t hunkId) {
+ int16 backColor = (int16)readSelectorValue(_segMan, textObject, SELECTOR(back));
// Sanity check: Check if the hunk is set. If not, either the game scripts
// didn't set it, or an old saved game has been loaded, where it wasn't set.
if (hunkId.isNull())
@@ -187,13 +231,17 @@ void GfxText32::drawTextBitmap(int16 x, int16 y, Common::Rect planeRect, reg_t t
byte *memoryPtr = _segMan->getHunkPointer(hunkId);
- if (!memoryPtr)
- error("Attempt to draw an invalid text bitmap");
+ if (!memoryPtr) {
+ // Happens when restoring in some SCI32 games (e.g. SQ6).
+ // Commented out to reduce console spam
+ //warning("Attempt to draw an invalid text bitmap");
+ return;
+ }
byte *surface = memoryPtr + BITMAP_HEADER_SIZE;
int curByte = 0;
- uint16 skipColor = readSelectorValue(_segMan, textObject, SELECTOR(skip));
+ int16 skipColor = (int16)readSelectorValue(_segMan, textObject, SELECTOR(skip));
uint16 textX = planeRect.left + x;
uint16 textY = planeRect.top + y;
// Get totalWidth, totalHeight
@@ -206,10 +254,13 @@ void GfxText32::drawTextBitmap(int16 x, int16 y, Common::Rect planeRect, reg_t t
textY = textY * _screen->getDisplayHeight() / _screen->getHeight();
}
+ bool translucent = (skipColor == -1 && backColor == -1);
+
for (int curY = 0; curY < height; curY++) {
for (int curX = 0; curX < width; curX++) {
byte pixel = surface[curByte++];
- if (pixel != skipColor && pixel != backColor)
+ if ((!translucent && pixel != skipColor && pixel != backColor) ||
+ (translucent && pixel != 0xFF))
_screen->putFontPixel(textY, curX + textX, curY, pixel);
}
}
@@ -265,7 +316,7 @@ void GfxText32::StringWidth(const char *str, GuiResourceId fontId, int16 &textWi
}
void GfxText32::Width(const char *text, int16 from, int16 len, GuiResourceId fontId, int16 &textWidth, int16 &textHeight, bool restoreFont) {
- uint16 curChar;
+ byte curChar;
textWidth = 0; textHeight = 0;
GfxFont *font = _cache->getFont(fontId);
@@ -277,7 +328,6 @@ void GfxText32::Width(const char *text, int16 from, int16 len, GuiResourceId fon
switch (curChar) {
case 0x0A:
case 0x0D:
- case 0x9781: // this one is used by SQ4/japanese as line break as well
textHeight = MAX<int16> (textHeight, font->getHeight());
break;
case 0x7C:
diff --git a/engines/sci/graphics/text32.h b/engines/sci/graphics/text32.h
index 3505de85eb..ce78003fdf 100644
--- a/engines/sci/graphics/text32.h
+++ b/engines/sci/graphics/text32.h
@@ -33,13 +33,17 @@ public:
GfxText32(SegManager *segMan, GfxCache *fonts, GfxScreen *screen);
~GfxText32();
reg_t createTextBitmap(reg_t textObject, uint16 maxWidth = 0, uint16 maxHeight = 0, reg_t prevHunk = NULL_REG);
- void disposeTextBitmap(reg_t hunkId);
+ reg_t createScrollTextBitmap(Common::String text, reg_t textObject, uint16 maxWidth = 0, uint16 maxHeight = 0, reg_t prevHunk = NULL_REG);
void drawTextBitmap(int16 x, int16 y, Common::Rect planeRect, reg_t textObject);
+ void drawScrollTextBitmap(reg_t textObject, reg_t hunkId, uint16 x, uint16 y);
+ void disposeTextBitmap(reg_t hunkId);
int16 GetLongest(const char *text, int16 maxWidth, GfxFont *font);
void kernelTextSize(const char *text, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight);
private:
+ reg_t createTextBitmapInternal(Common::String &text, reg_t textObject, uint16 maxWidth, uint16 maxHeight, reg_t hunkId);
+ void drawTextBitmapInternal(int16 x, int16 y, Common::Rect planeRect, reg_t textObject, reg_t hunkId);
int16 Size(Common::Rect &rect, const char *text, GuiResourceId fontId, int16 maxWidth);
void Width(const char *text, int16 from, int16 len, GuiResourceId orgFontId, int16 &textWidth, int16 &textHeight, bool restoreFont);
void StringWidth(const char *str, GuiResourceId orgFontId, int16 &textWidth, int16 &textHeight);
diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp
index a77bcccc52..36aaae9232 100644
--- a/engines/sci/graphics/view.cpp
+++ b/engines/sci/graphics/view.cpp
@@ -720,6 +720,19 @@ void GfxView::draw(const Common::Rect &rect, const Common::Rect &clipRect, const
bitmap += (clipRect.top - rect.top) * celWidth + (clipRect.left - rect.left);
+ // WORKAROUND: EcoQuest French and German draw the fish and anemone sprites
+ // with priority 15 in scene 440. Afterwards, a dialog is shown on top of
+ // these sprites with priority 15 as well. This is undefined behavior
+ // actually, as the sprites and dialog share the same priority, so in our
+ // implementation the sprites get drawn incorrectly on top of the dialog.
+ // Perhaps this worked by mistake in SSCI because of subtle differences in
+ // how sprites are drawn. We compensate for this by resetting the priority
+ // of all sprites that have a priority of 15 in scene 440 to priority 14,
+ // so that the speech bubble can be drawn correctly on top of them. Fixes
+ // bug #3040625.
+ if (g_sci->getGameId() == GID_ECOQUEST && g_sci->getEngineState()->currentRoomNumber() == 440 && priority == 15)
+ priority = 14;
+
if (!_EGAmapping) {
for (y = 0; y < height; y++, bitmap += celWidth) {
for (x = 0; x < width; x++) {
@@ -728,8 +741,14 @@ void GfxView::draw(const Common::Rect &rect, const Common::Rect &clipRect, const
const int x2 = clipRectTranslated.left + x;
const int y2 = clipRectTranslated.top + y;
if (!upscaledHires) {
- if (priority >= _screen->getPriority(x2, y2))
- _screen->putPixel(x2, y2, drawMask, palette->mapping[color], priority, 0);
+ if (priority >= _screen->getPriority(x2, y2)) {
+ if (!_palette->isRemapped(palette->mapping[color])) {
+ _screen->putPixel(x2, y2, drawMask, palette->mapping[color], priority, 0);
+ } else {
+ byte remappedColor = _palette->remapColor(palette->mapping[color], _screen->getVisual(x2, y2));
+ _screen->putPixel(x2, y2, drawMask, remappedColor, priority, 0);
+ }
+ }
} else {
// UpscaledHires means view is hires and is supposed to
// get drawn onto lowres screen.
@@ -838,7 +857,12 @@ void GfxView::drawScaled(const Common::Rect &rect, const Common::Rect &clipRect,
const int x2 = clipRectTranslated.left + x;
const int y2 = clipRectTranslated.top + y;
if (color != clearKey && priority >= _screen->getPriority(x2, y2)) {
- _screen->putPixel(x2, y2, drawMask, palette->mapping[color], priority, 0);
+ if (!_palette->isRemapped(palette->mapping[color])) {
+ _screen->putPixel(x2, y2, drawMask, palette->mapping[color], priority, 0);
+ } else {
+ byte remappedColor = _palette->remapColor(palette->mapping[color], _screen->getVisual(x2, y2));
+ _screen->putPixel(x2, y2, drawMask, remappedColor, priority, 0);
+ }
}
}
}
diff --git a/engines/sci/module.mk b/engines/sci/module.mk
index 90a0f33f06..6b6058c819 100644
--- a/engines/sci/module.mk
+++ b/engines/sci/module.mk
@@ -10,6 +10,7 @@ MODULE_OBJS := \
sci.o \
util.o \
engine/features.o \
+ engine/file.o \
engine/gc.o \
engine/kernel.o \
engine/kevent.o \
@@ -79,6 +80,7 @@ MODULE_OBJS := \
ifdef ENABLE_SCI32
MODULE_OBJS += \
+ engine/kgraphics32.o \
graphics/controls32.o \
graphics/frameout.o \
graphics/paint32.o \
diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp
index 77a6a40a92..c9c0d25bc6 100644
--- a/engines/sci/resource.cpp
+++ b/engines/sci/resource.cpp
@@ -93,7 +93,7 @@ const char *getSciVersionDesc(SciVersion version) {
//#define SCI_VERBOSE_RESMAN 1
-static const char *const sci_error_types[] = {
+static const char *const s_errorDescriptions[] = {
"No error",
"I/O error",
"Resource is empty (size 0)",
@@ -101,10 +101,8 @@ static const char *const sci_error_types[] = {
"resource.map file not found",
"No resource files found",
"Unknown compression method",
- "Decompression failed: Decompression buffer overflow",
"Decompression failed: Sanity check failed",
- "Decompression failed: Resource too big",
- "SCI version is unsupported"
+ "Decompression failed: Resource too big"
};
static const char *const s_resourceTypeNames[] = {
@@ -576,7 +574,7 @@ void ResourceSource::loadResource(ResourceManager *resMan, Resource *res) {
if (error) {
warning("Error %d occurred while reading %s from resource file %s: %s",
error, res->_id.toString().c_str(), res->getResourceLocation().c_str(),
- sci_error_types[error]);
+ s_errorDescriptions[error]);
res->unalloc();
}
@@ -1876,6 +1874,9 @@ int Resource::readResourceInfo(ResVersion volVersion, Common::SeekableReadStream
uint32 wCompression, szUnpacked;
ResourceType type;
+ if (file->size() == 0)
+ return SCI_ERROR_EMPTY_RESOURCE;
+
switch (volVersion) {
case kResVersionSci0Sci1Early:
case kResVersionSci1Middle:
@@ -1920,7 +1921,7 @@ int Resource::readResourceInfo(ResVersion volVersion, Common::SeekableReadStream
break;
#endif
default:
- return SCI_ERROR_INVALID_RESMAP_ENTRY;
+ return SCI_ERROR_RESMAP_INVALID_ENTRY;
}
// check if there were errors while reading
@@ -1961,7 +1962,7 @@ int Resource::readResourceInfo(ResVersion volVersion, Common::SeekableReadStream
compression = kCompUnknown;
}
- return compression == kCompUnknown ? SCI_ERROR_UNKNOWN_COMPRESSION : 0;
+ return (compression == kCompUnknown) ? SCI_ERROR_UNKNOWN_COMPRESSION : SCI_ERROR_NONE;
}
int Resource::decompress(ResVersion volVersion, Common::SeekableReadStream *file) {
@@ -2589,7 +2590,7 @@ Common::String ResourceManager::findSierraGameId() {
if (!heap)
return "";
- int16 gameObjectOffset = findGameObject(false).offset;
+ int16 gameObjectOffset = findGameObject(false).getOffset();
if (!gameObjectOffset)
return "";
diff --git a/engines/sci/resource.h b/engines/sci/resource.h
index 294a4672e2..4baf39c67f 100644
--- a/engines/sci/resource.h
+++ b/engines/sci/resource.h
@@ -54,17 +54,17 @@ enum ResourceStatus {
kResStatusLocked /**< Allocated and in use */
};
-/** Initialization result types */
-enum {
+/** Resource error codes. Should be in sync with s_errorDescriptions */
+enum ResourceErrorCodes {
+ SCI_ERROR_NONE = 0,
SCI_ERROR_IO_ERROR = 1,
- SCI_ERROR_INVALID_RESMAP_ENTRY = 2, /**< Invalid resource.map entry */
- SCI_ERROR_RESMAP_NOT_FOUND = 3,
- SCI_ERROR_NO_RESOURCE_FILES_FOUND = 4, /**< No resource at all was found */
- SCI_ERROR_UNKNOWN_COMPRESSION = 5,
- SCI_ERROR_DECOMPRESSION_ERROR = 6, /**< sanity checks failed during decompression */
+ SCI_ERROR_EMPTY_RESOURCE = 2,
+ SCI_ERROR_RESMAP_INVALID_ENTRY = 3, /**< Invalid resource.map entry */
+ SCI_ERROR_RESMAP_NOT_FOUND = 4,
+ SCI_ERROR_NO_RESOURCE_FILES_FOUND = 5, /**< No resource at all was found */
+ SCI_ERROR_UNKNOWN_COMPRESSION = 6,
+ SCI_ERROR_DECOMPRESSION_ERROR = 7, /**< sanity checks failed during decompression */
SCI_ERROR_RESOURCE_TOO_BIG = 8 /**< Resource size exceeds SCI_MAX_RESOURCE_SIZE */
-
- /* the first critical error number */
};
enum {
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index 4ae55cbcba..15b18ce8e6 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -191,7 +191,7 @@ extern void showScummVMDialog(const Common::String &message);
Common::Error SciEngine::run() {
// Assign default values to the config manager, in case settings are missing
- ConfMan.registerDefault("sci_originalsaveload", "false");
+ ConfMan.registerDefault("originalsaveload", "false");
ConfMan.registerDefault("native_fb01", "false");
ConfMan.registerDefault("windows_cursors", "false"); // Windows cursors for KQ6 Windows
ConfMan.registerDefault("silver_cursors", "false"); // Silver cursors for SQ4 CD
@@ -385,7 +385,7 @@ bool SciEngine::gameHasFanMadePatch() {
{ GID_PQ3, 994, 4686, 1291, 0x78 }, // English
{ GID_PQ3, 994, 4734, 1283, 0x78 }, // German
{ GID_QFG1VGA, 994, 4388, 0, 0x00 },
- { GID_QFG3, 994, 4714, 0, 0x00 },
+ { GID_QFG3, 33, 260, 0, 0x00 },
// TODO: Disabled, as it fixes a whole lot of bugs which can't be tested till SCI2.1 support is finished
//{ GID_QFG4, 710, 11477, 0, 0x00 },
{ GID_SQ1, 994, 4740, 0, 0x00 },
@@ -453,8 +453,8 @@ static byte patchGameRestoreSaveSci21[] = {
};
static void patchGameSaveRestoreCode(SegManager *segMan, reg_t methodAddress, byte id) {
- Script *script = segMan->getScript(methodAddress.segment);
- byte *patchPtr = const_cast<byte *>(script->getBuf(methodAddress.offset));
+ Script *script = segMan->getScript(methodAddress.getSegment());
+ byte *patchPtr = const_cast<byte *>(script->getBuf(methodAddress.getOffset()));
if (getSciVersion() <= SCI_VERSION_1_1)
memcpy(patchPtr, patchGameRestoreSave, sizeof(patchGameRestoreSave));
else // SCI2+
@@ -463,8 +463,8 @@ static void patchGameSaveRestoreCode(SegManager *segMan, reg_t methodAddress, by
}
static void patchGameSaveRestoreCodeSci21(SegManager *segMan, reg_t methodAddress, byte id, bool doRestore) {
- Script *script = segMan->getScript(methodAddress.segment);
- byte *patchPtr = const_cast<byte *>(script->getBuf(methodAddress.offset));
+ Script *script = segMan->getScript(methodAddress.getSegment());
+ byte *patchPtr = const_cast<byte *>(script->getBuf(methodAddress.getOffset()));
memcpy(patchPtr, patchGameRestoreSaveSci21, sizeof(patchGameRestoreSaveSci21));
if (doRestore)
patchPtr[2] = 0x78; // push1
@@ -492,7 +492,7 @@ void SciEngine::patchGameSaveRestore() {
break;
}
- if (ConfMan.getBool("sci_originalsaveload"))
+ if (ConfMan.getBool("originalsaveload"))
return;
uint16 kernelNamesSize = _kernel->getKernelNamesSize();
@@ -632,7 +632,7 @@ void SciEngine::initGraphics() {
_gfxPaint = _gfxPaint32;
_gfxText32 = new GfxText32(_gamestate->_segMan, _gfxCache, _gfxScreen);
_gfxControls32 = new GfxControls32(_gamestate->_segMan, _gfxCache, _gfxScreen, _gfxText32);
- _robotDecoder = new RobotDecoder(g_system->getMixer(), getPlatform() == Common::kPlatformMacintosh);
+ _robotDecoder = new RobotDecoder(getPlatform() == Common::kPlatformMacintosh);
_gfxFrameout = new GfxFrameout(_gamestate->_segMan, _resMan, _gfxCoordAdjuster, _gfxCache, _gfxScreen, _gfxPalette, _gfxPaint32);
} else {
#endif
@@ -740,7 +740,7 @@ GUI::Debugger *SciEngine::getDebugger() {
if (_gamestate) {
ExecStack *xs = &(_gamestate->_executionStack.back());
if (xs) {
- xs->addr.pc.offset = _debugState.old_pc_offset;
+ xs->addr.pc.setOffset(_debugState.old_pc_offset);
xs->sp = _debugState.old_sp;
}
}
@@ -876,6 +876,7 @@ void SciEngine::syncIngameAudioOptions() {
if (getGameId() == GID_SQ4
|| getGameId() == GID_FREDDYPHARKAS
|| getGameId() == GID_ECOQUEST
+ || getGameId() == GID_LSL6
// TODO: The following need script patches for simultaneous speech and subtitles
//|| getGameId() == GID_KQ6
//|| getGameId() == GID_LAURABOW2
diff --git a/engines/sci/sci.h b/engines/sci/sci.h
index 9f18219cb7..3441e26c01 100644
--- a/engines/sci/sci.h
+++ b/engines/sci/sci.h
@@ -228,6 +228,26 @@ public:
bool canLoadGameStateCurrently();
bool canSaveGameStateCurrently();
void syncSoundSettings();
+
+ /**
+ * Syncs the audio options of the ScummVM launcher (speech, subtitles or
+ * both) with the in-game audio options of certain CD game versions. For
+ * some games, this allows simultaneous playing of speech and subtitles,
+ * even if the original games didn't support this feature.
+ *
+ * SCI1.1 games which support simultaneous speech and subtitles:
+ * - EcoQuest 1 CD
+ * - Leisure Suit Larry 6 CD
+ * SCI1.1 games which don't support simultaneous speech and subtitles,
+ * and we add this functionality in ScummVM:
+ * - Space Quest 4 CD
+ * - Freddy Pharkas CD
+ * SCI1.1 games which don't support simultaneous speech and subtitles,
+ * and we haven't added any extra functionality in ScummVM because extra
+ * script patches are needed:
+ * - Laura Bow 2 CD
+ * - King's Quest 6 CD
+ */
void syncIngameAudioOptions();
const SciGameId &getGameId() const { return _gameId; }
diff --git a/engines/sci/sound/audio.cpp b/engines/sci/sound/audio.cpp
index 123dd21894..528bb51393 100644
--- a/engines/sci/sound/audio.cpp
+++ b/engines/sci/sound/audio.cpp
@@ -67,7 +67,8 @@ int AudioPlayer::startAudio(uint16 module, uint32 number) {
if (audioStream) {
_wPlayFlag = false;
- _mixer->playStream(Audio::Mixer::kSpeechSoundType, &_audioHandle, audioStream);
+ Audio::Mixer::SoundType soundType = (module == 65535) ? Audio::Mixer::kSFXSoundType : Audio::Mixer::kSpeechSoundType;
+ _mixer->playStream(soundType, &_audioHandle, audioStream);
return sampleLen;
} else {
// Don't throw a warning in this case. getAudioStream() already has. Some games
diff --git a/engines/sci/sound/drivers/adlib.cpp b/engines/sci/sound/drivers/adlib.cpp
index db9317e071..191e13db0a 100644
--- a/engines/sci/sound/drivers/adlib.cpp
+++ b/engines/sci/sound/drivers/adlib.cpp
@@ -807,6 +807,9 @@ int MidiPlayer_AdLib::open(ResourceManager *resMan) {
int size = f.size();
const uint patchSize = 1344;
+ // Note: Funseeker's Guide also has another version of adl.drv, 8803 bytes.
+ // This isn't supported, but it's not really used anywhere, as that demo
+ // doesn't have sound anyway.
if ((size == 5684) || (size == 5720) || (size == 5727)) {
byte *buf = new byte[patchSize];
diff --git a/engines/sci/sound/drivers/fmtowns.cpp b/engines/sci/sound/drivers/fmtowns.cpp
index 6d8bb2e525..21cb2f1e43 100644
--- a/engines/sci/sound/drivers/fmtowns.cpp
+++ b/engines/sci/sound/drivers/fmtowns.cpp
@@ -52,8 +52,8 @@ public:
uint16 _duration;
private:
- uint8 _id;
- uint8 _velo;
+ uint8 _id;
+ uint8 _velo;
uint8 _program;
MidiDriver_FMTowns *_drv;
@@ -76,7 +76,7 @@ public:
void addChannels(int num);
void dropChannels(int num);
-
+
uint8 currentProgram() const;
private:
@@ -132,7 +132,7 @@ private:
TownsMidiPart **_parts;
TownsChannel **_out;
-
+
uint8 _masterVolume;
bool _soundOn;
@@ -590,7 +590,7 @@ void MidiDriver_FMTowns::addMissingChannels() {
avlChan -= _parts[i]->_chanMissing;
uint8 m = _parts[i]->_chanMissing;
_parts[i]->_chanMissing = 0;
- _parts[i]->addChannels(m);
+ _parts[i]->addChannels(m);
} else {
_parts[i]->_chanMissing -= avlChan;
_parts[i]->addChannels(avlChan);
@@ -601,7 +601,7 @@ void MidiDriver_FMTowns::addMissingChannels() {
void MidiDriver_FMTowns::updateParser() {
if (_timerProc)
- _timerProc(_timerProcPara);
+ _timerProc(_timerProcPara);
}
void MidiDriver_FMTowns::updateChannels() {
diff --git a/engines/sci/sound/drivers/midi.cpp b/engines/sci/sound/drivers/midi.cpp
index 93a2308410..37eb37b292 100644
--- a/engines/sci/sound/drivers/midi.cpp
+++ b/engines/sci/sound/drivers/midi.cpp
@@ -563,7 +563,7 @@ void MidiPlayer_Midi::readMt32GmPatch(const byte *data, int size) {
switch (command & 0xf0) {
case 0xf0: {
- byte *sysExEnd = (byte *)memchr(midi + i, 0xf7, midiSize - i);
+ const byte *sysExEnd = (const byte *)memchr(midi + i, 0xf7, midiSize - i);
if (!sysExEnd)
error("Failed to find end of sysEx");
diff --git a/engines/sci/sound/midiparser_sci.cpp b/engines/sci/sound/midiparser_sci.cpp
index 422948f975..4e54797960 100644
--- a/engines/sci/sound/midiparser_sci.cpp
+++ b/engines/sci/sound/midiparser_sci.cpp
@@ -98,7 +98,7 @@ bool MidiParser_SCI::loadMusic(SoundResource::Track *track, MusicEntry *psnd, in
midiMixChannels();
}
- _num_tracks = 1;
+ _numTracks = 1;
_tracks[0] = _mixedData;
if (_pSnd)
setTrack(0);
@@ -144,7 +144,7 @@ byte *MidiParser_SCI::midiMixChannels() {
_mixedData = outData;
long ticker = 0;
byte channelNr, curDelta;
- byte midiCommand = 0, midiParam, global_prev = 0;
+ byte midiCommand = 0, midiParam, globalPrev = 0;
long newDelta;
SoundResource::Channel *channel;
@@ -190,13 +190,13 @@ byte *MidiParser_SCI::midiMixChannels() {
byte midiChannel = midiCommand & 0xF;
_channelUsed[midiChannel] = true;
- if (midiCommand != global_prev)
+ if (midiCommand != globalPrev)
*outData++ = midiCommand;
*outData++ = midiParam;
if (nMidiParams[(midiCommand >> 4) - 8] == 2)
*outData++ = channel->data[channel->curPos++];
channel->prev = midiCommand;
- global_prev = midiCommand;
+ globalPrev = midiCommand;
}
}
@@ -372,8 +372,8 @@ void MidiParser_SCI::unloadMusic() {
resetTracking();
allNotesOff();
}
- _num_tracks = 0;
- _active_track = 255;
+ _numTracks = 0;
+ _activeTrack = 255;
_resetOnPause = false;
if (_mixedData) {
@@ -454,26 +454,26 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) {
debugC(4, kDebugLevelSound, "signal %04x", _signalToSet);
}
- info.start = _position._play_pos;
+ info.start = _position._playPos;
info.delta = 0;
- while (*_position._play_pos == 0xF8) {
+ while (*_position._playPos == 0xF8) {
info.delta += 240;
- _position._play_pos++;
+ _position._playPos++;
}
- info.delta += *(_position._play_pos++);
+ info.delta += *(_position._playPos++);
// Process the next info.
- if ((_position._play_pos[0] & 0xF0) >= 0x80)
- info.event = *(_position._play_pos++);
+ if ((_position._playPos[0] & 0xF0) >= 0x80)
+ info.event = *(_position._playPos++);
else
- info.event = _position._running_status;
+ info.event = _position._runningStatus;
if (info.event < 0x80)
return;
- _position._running_status = info.event;
+ _position._runningStatus = info.event;
switch (info.command()) {
case 0xC:
- info.basic.param1 = *(_position._play_pos++);
+ info.basic.param1 = *(_position._playPos++);
info.basic.param2 = 0;
if (info.channel() == 0xF) {// SCI special case
if (info.basic.param1 != kSetSignalLoop) {
@@ -488,23 +488,23 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) {
// in glitches (e.g. the intro of LB1 Amiga gets stuck - bug
// #3297883). Refer to MusicEntry::setSignal() in sound/music.cpp.
if (_soundVersion <= SCI_VERSION_0_LATE ||
- _position._play_tick || info.delta) {
+ _position._playTick || info.delta) {
_signalSet = true;
_signalToSet = info.basic.param1;
}
} else {
- _loopTick = _position._play_tick + info.delta;
+ _loopTick = _position._playTick + info.delta;
}
}
break;
case 0xD:
- info.basic.param1 = *(_position._play_pos++);
+ info.basic.param1 = *(_position._playPos++);
info.basic.param2 = 0;
break;
case 0xB:
- info.basic.param1 = *(_position._play_pos++);
- info.basic.param2 = *(_position._play_pos++);
+ info.basic.param1 = *(_position._playPos++);
+ info.basic.param2 = *(_position._playPos++);
// Reference for some events:
// http://wiki.scummvm.org/index.php/SCI/Specifications/Sound/SCI0_Resource_Format#Status_Reference
@@ -588,8 +588,8 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) {
case 0x9:
case 0xA:
case 0xE:
- info.basic.param1 = *(_position._play_pos++);
- info.basic.param2 = *(_position._play_pos++);
+ info.basic.param1 = *(_position._playPos++);
+ info.basic.param2 = *(_position._playPos++);
if (info.command() == 0x9 && info.basic.param2 == 0)
info.event = info.channel() | 0x80;
info.length = 0;
@@ -598,12 +598,12 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) {
case 0xF: // System Common, Meta or SysEx event
switch (info.event & 0x0F) {
case 0x2: // Song Position Pointer
- info.basic.param1 = *(_position._play_pos++);
- info.basic.param2 = *(_position._play_pos++);
+ info.basic.param1 = *(_position._playPos++);
+ info.basic.param2 = *(_position._playPos++);
break;
case 0x3: // Song Select
- info.basic.param1 = *(_position._play_pos++);
+ info.basic.param1 = *(_position._playPos++);
info.basic.param2 = 0;
break;
@@ -617,16 +617,16 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) {
break;
case 0x0: // SysEx
- info.length = readVLQ(_position._play_pos);
- info.ext.data = _position._play_pos;
- _position._play_pos += info.length;
+ info.length = readVLQ(_position._playPos);
+ info.ext.data = _position._playPos;
+ _position._playPos += info.length;
break;
case 0xF: // META event
- info.ext.type = *(_position._play_pos++);
- info.length = readVLQ(_position._play_pos);
- info.ext.data = _position._play_pos;
- _position._play_pos += info.length;
+ info.ext.type = *(_position._playPos++);
+ info.length = readVLQ(_position._playPos);
+ info.ext.data = _position._playPos;
+ _position._playPos += info.length;
if (info.ext.type == 0x2F) {// end of track reached
if (_pSnd->loop)
_pSnd->loop--;
@@ -677,21 +677,21 @@ void MidiParser_SCI::allNotesOff() {
// Turn off all active notes
for (i = 0; i < 128; ++i) {
for (j = 0; j < 16; ++j) {
- if ((_active_notes[i] & (1 << j)) && (_channelRemap[j] != -1)){
+ if ((_activeNotes[i] & (1 << j)) && (_channelRemap[j] != -1)){
sendToDriver(0x80 | j, i, 0);
}
}
}
// Turn off all hanging notes
- for (i = 0; i < ARRAYSIZE(_hanging_notes); i++) {
- byte midiChannel = _hanging_notes[i].channel;
- if ((_hanging_notes[i].time_left) && (_channelRemap[midiChannel] != -1)) {
- sendToDriver(0x80 | midiChannel, _hanging_notes[i].note, 0);
- _hanging_notes[i].time_left = 0;
+ for (i = 0; i < ARRAYSIZE(_hangingNotes); i++) {
+ byte midiChannel = _hangingNotes[i].channel;
+ if ((_hangingNotes[i].timeLeft) && (_channelRemap[midiChannel] != -1)) {
+ sendToDriver(0x80 | midiChannel, _hangingNotes[i].note, 0);
+ _hangingNotes[i].timeLeft = 0;
}
}
- _hanging_notes_count = 0;
+ _hangingNotesCount = 0;
// To be sure, send an "All Note Off" event (but not all MIDI devices
// support this...).
@@ -703,7 +703,7 @@ void MidiParser_SCI::allNotesOff() {
}
}
- memset(_active_notes, 0, sizeof(_active_notes));
+ memset(_activeNotes, 0, sizeof(_activeNotes));
}
void MidiParser_SCI::setMasterVolume(byte masterVolume) {
diff --git a/engines/sci/sound/midiparser_sci.h b/engines/sci/sound/midiparser_sci.h
index 82f34070a4..d3fd337644 100644
--- a/engines/sci/sound/midiparser_sci.h
+++ b/engines/sci/sound/midiparser_sci.h
@@ -65,7 +65,7 @@ public:
void setMasterVolume(byte masterVolume);
void setVolume(byte volume);
void stop() {
- _abort_parse = true;
+ _abortParse = true;
allNotesOff();
}
void pause() {
diff --git a/engines/sci/sound/music.cpp b/engines/sci/sound/music.cpp
index 09cab75c39..a8a65d2aa4 100644
--- a/engines/sci/sound/music.cpp
+++ b/engines/sci/sound/music.cpp
@@ -88,6 +88,12 @@ void SciMusic::init() {
uint32 dev = MidiDriver::detectDevice(deviceFlags);
_musicType = MidiDriver::getMusicType(dev);
+ if (g_sci->_features->useAltWinGMSound() && _musicType != MT_GM) {
+ warning("A Windows CD version with an alternate MIDI soundtrack has been chosen, "
+ "but no MIDI music device has been selected. Reverting to the DOS soundtrack");
+ g_sci->_features->forceDOSTracks();
+ }
+
switch (_musicType) {
case MT_ADLIB:
// FIXME: There's no Amiga sound option, so we hook it up to AdLib
@@ -119,7 +125,13 @@ void SciMusic::init() {
_pMidiDrv->setTimerCallback(this, &miditimerCallback);
_dwTempo = _pMidiDrv->getBaseTempo();
} else {
- error("Failed to initialize sound driver");
+ if (g_sci->getGameId() == GID_FUNSEEKER) {
+ // HACK: The Fun Seeker's Guide demo doesn't have patch 3 and the version
+ // of the Adlib driver (adl.drv) that it includes is unsupported. That demo
+ // doesn't have any sound anyway, so this shouldn't be fatal.
+ } else {
+ error("Failed to initialize sound driver");
+ }
}
// Find out what the first possible channel is (used, when doing channel
diff --git a/engines/sci/sound/soundcmd.cpp b/engines/sci/sound/soundcmd.cpp
index b8be898abc..5d32f40f18 100644
--- a/engines/sci/sound/soundcmd.cpp
+++ b/engines/sci/sound/soundcmd.cpp
@@ -32,14 +32,11 @@
namespace Sci {
-//#define ENABLE_SFX_TYPE_SELECTION
-
SoundCommandParser::SoundCommandParser(ResourceManager *resMan, SegManager *segMan, Kernel *kernel, AudioPlayer *audio, SciVersion soundVersion) :
_resMan(resMan), _segMan(segMan), _kernel(kernel), _audio(audio), _soundVersion(soundVersion) {
-#ifdef ENABLE_SFX_TYPE_SELECTION
// Check if the user wants synthesized or digital sound effects in SCI1.1
- // games based on the multi_midi config setting
+ // games based on the prefer_digitalsfx config setting
// In SCI2 and later games, this check should always be true - there was
// always only one version of each sound effect or digital music track
@@ -47,12 +44,7 @@ SoundCommandParser::SoundCommandParser(ResourceManager *resMan, SegManager *segM
// resource number, but it's totally unrelated to the menu music).
// The GK1 demo (very late SCI1.1) does the same thing
// TODO: Check the QFG4 demo
-
- _useDigitalSFX = (getSciVersion() >= SCI_VERSION_2 || g_sci->getGameId() == GID_GK1 || ConfMan.getBool("multi_midi"));
-#else
- // Always prefer digital sound effects
- _useDigitalSFX = true;
-#endif
+ _useDigitalSFX = (getSciVersion() >= SCI_VERSION_2 || g_sci->getGameId() == GID_GK1 || ConfMan.getBool("prefer_digitalsfx"));
_music = new SciMusic(_soundVersion, _useDigitalSFX);
_music->init();
@@ -69,7 +61,7 @@ reg_t SoundCommandParser::kDoSoundInit(int argc, reg_t *argv, reg_t acc) {
}
int SoundCommandParser::getSoundResourceId(reg_t obj) {
- int resourceId = obj.segment ? readSelectorValue(_segMan, obj, SELECTOR(number)) : -1;
+ int resourceId = obj.getSegment() ? readSelectorValue(_segMan, obj, SELECTOR(number)) : -1;
// Modify the resourceId for the Windows versions that have an alternate MIDI soundtrack, like SSCI did.
if (g_sci && g_sci->_features->useAltWinGMSound()) {
// Check if the alternate MIDI song actually exists...
@@ -104,7 +96,7 @@ void SoundCommandParser::initSoundResource(MusicEntry *newSound) {
if (_useDigitalSFX || !newSound->soundRes) {
int sampleLen;
newSound->pStreamAud = _audio->getAudioStream(newSound->resourceId, 65535, &sampleLen);
- newSound->soundType = Audio::Mixer::kSpeechSoundType;
+ newSound->soundType = Audio::Mixer::kSFXSoundType;
}
}
@@ -299,7 +291,7 @@ reg_t SoundCommandParser::kDoSoundPause(int argc, reg_t *argv, reg_t acc) {
reg_t obj = argv[0];
uint16 value = argc > 1 ? argv[1].toUint16() : 0;
- if (!obj.segment) { // pause the whole playlist
+ if (!obj.getSegment()) { // pause the whole playlist
_music->pauseAll(value);
} else { // pause a playlist slot
MusicEntry *musicSlot = _music->getSlot(obj);
@@ -375,24 +367,36 @@ reg_t SoundCommandParser::kDoSoundFade(int argc, reg_t *argv, reg_t acc) {
case 4: // SCI01+
case 5: // SCI1+ (SCI1 late sound scheme), with fade and continue
- musicSlot->fadeTo = CLIP<uint16>(argv[1].toUint16(), 0, MUSIC_VOLUME_MAX);
- // sometimes we get objects in that position, fix it up (ffs. workarounds)
- if (!argv[1].segment)
- musicSlot->fadeStep = volume > musicSlot->fadeTo ? -argv[3].toUint16() : argv[3].toUint16();
- else
- musicSlot->fadeStep = volume > musicSlot->fadeTo ? -5 : 5;
- musicSlot->fadeTickerStep = argv[2].toUint16() * 16667 / _music->soundGetTempo();
- musicSlot->fadeTicker = 0;
-
if (argc == 5) {
// TODO: We currently treat this argument as a boolean, but may
// have to handle different non-zero values differently. (e.g.,
- // some KQ6 scripts pass 3 here)
- musicSlot->stopAfterFading = (argv[4].toUint16() != 0);
+ // some KQ6 scripts pass 3 here).
+ // There is a script bug in KQ6, room 460 (the room with the flying
+ // books). An object is passed here, which should not be treated as
+ // a true flag. Fixes bugs #3555404 and #3291115.
+ musicSlot->stopAfterFading = (argv[4].isNumber() && argv[4].toUint16() != 0);
} else {
musicSlot->stopAfterFading = false;
}
+ musicSlot->fadeTo = CLIP<uint16>(argv[1].toUint16(), 0, MUSIC_VOLUME_MAX);
+ // Check if the song is already at the requested volume. If it is, don't
+ // perform any fading. Happens for example during the intro of Longbow.
+ if (musicSlot->fadeTo != musicSlot->volume) {
+ // sometimes we get objects in that position, fix it up (ffs. workarounds)
+ if (!argv[1].getSegment())
+ musicSlot->fadeStep = volume > musicSlot->fadeTo ? -argv[3].toUint16() : argv[3].toUint16();
+ else
+ musicSlot->fadeStep = volume > musicSlot->fadeTo ? -5 : 5;
+ musicSlot->fadeTickerStep = argv[2].toUint16() * 16667 / _music->soundGetTempo();
+ } else {
+ // Stop the music, if requested. Fixes bug #3555404.
+ if (musicSlot->stopAfterFading)
+ processStopSound(obj, false);
+ }
+
+ musicSlot->fadeTicker = 0;
+
// WORKAROUND/HACK: In the labyrinth in KQ6, when falling in the pit and
// lighting the lantern, the game scripts perform a fade in of the game
// music, but set it to stop after fading. Remove that flag here. This is
@@ -505,12 +509,7 @@ void SoundCommandParser::processUpdateCues(reg_t obj) {
// fireworks).
// It is also needed in other games, e.g. LSL6 when talking to the
// receptionist (bug #3192166).
- if (g_sci->getGameId() == GID_LONGBOW && g_sci->getEngineState()->currentRoomNumber() == 95) {
- // HACK: Don't set a signal here in the intro of Longbow, as that makes some dialog
- // boxes disappear too soon (bug #3044844).
- } else {
- writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
- }
+ writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET);
if (_soundVersion <= SCI_VERSION_0_LATE) {
processStopSound(obj, false);
} else {
diff --git a/engines/sci/video/robot_decoder.cpp b/engines/sci/video/robot_decoder.cpp
index 77f45e0788..0337a8d306 100644
--- a/engines/sci/video/robot_decoder.cpp
+++ b/engines/sci/video/robot_decoder.cpp
@@ -22,11 +22,13 @@
#include "common/archive.h"
#include "common/stream.h"
+#include "common/substream.h"
#include "common/system.h"
#include "common/textconsole.h"
#include "common/util.h"
#include "graphics/surface.h"
+#include "audio/audiostream.h"
#include "audio/decoders/raw.h"
#include "sci/resource.h"
@@ -63,46 +65,26 @@ namespace Sci {
// our graphics engine, it looks just like a part of the room. A RBT can move
// around the screen and go behind other objects. (...)
-#ifdef ENABLE_SCI32
-
-enum robotPalTypes {
+enum RobotPalTypes {
kRobotPalVariable = 0,
kRobotPalConstant = 1
};
-RobotDecoder::RobotDecoder(Audio::Mixer *mixer, bool isBigEndian) {
- _surface = 0;
- _width = 0;
- _height = 0;
+RobotDecoder::RobotDecoder(bool isBigEndian) {
_fileStream = 0;
- _audioStream = 0;
- _dirtyPalette = false;
_pos = Common::Point(0, 0);
- _mixer = mixer;
_isBigEndian = isBigEndian;
+ _frameTotalSize = 0;
}
RobotDecoder::~RobotDecoder() {
close();
}
-bool RobotDecoder::load(GuiResourceId id) {
- Common::String fileName = Common::String::format("%d.rbt", id);
- Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(fileName);
-
- if (!stream) {
- warning("Unable to open robot file %s", fileName.c_str());
- return false;
- }
-
- return loadStream(stream);
-}
-
bool RobotDecoder::loadStream(Common::SeekableReadStream *stream) {
close();
_fileStream = new Common::SeekableSubReadStreamEndian(stream, 0, stream->size(), _isBigEndian, DisposeAfterUse::YES);
- _surface = new Graphics::Surface();
readHeaderChunk();
@@ -114,131 +96,60 @@ bool RobotDecoder::loadStream(Common::SeekableReadStream *stream) {
if (_header.version < 4 || _header.version > 6)
error("Unknown robot version: %d", _header.version);
- if (_header.hasSound) {
- _audioStream = Audio::makeQueuingAudioStream(11025, false);
- _mixer->playStream(Audio::Mixer::kMusicSoundType, &_audioHandle, _audioStream);
- }
+ RobotVideoTrack *videoTrack = new RobotVideoTrack(_header.frameCount);
+ addTrack(videoTrack);
- readPaletteChunk(_header.paletteDataSize);
- readFrameSizesChunk();
- calculateVideoDimensions();
- _surface->create(_width, _height, Graphics::PixelFormat::createFormatCLUT8());
+ if (_header.hasSound)
+ addTrack(new RobotAudioTrack());
+ videoTrack->readPaletteChunk(_fileStream, _header.paletteDataSize);
+ readFrameSizesChunk();
+ videoTrack->calculateVideoDimensions(_fileStream, _frameTotalSize);
return true;
}
-void RobotDecoder::readHeaderChunk() {
- // Header (60 bytes)
- _fileStream->skip(6);
- _header.version = _fileStream->readUint16();
- _header.audioChunkSize = _fileStream->readUint16();
- _header.audioSilenceSize = _fileStream->readUint16();
- _fileStream->skip(2);
- _header.frameCount = _fileStream->readUint16();
- _header.paletteDataSize = _fileStream->readUint16();
- _header.unkChunkDataSize = _fileStream->readUint16();
- _fileStream->skip(5);
- _header.hasSound = _fileStream->readByte();
- _fileStream->skip(34);
-
- // Some videos (e.g. robot 1305 in Phantasmagoria and
- // robot 184 in Lighthouse) have an unknown chunk before
- // the palette chunk (probably used for sound preloading).
- // Skip it here.
- if (_header.unkChunkDataSize)
- _fileStream->skip(_header.unkChunkDataSize);
-}
-
-void RobotDecoder::readPaletteChunk(uint16 chunkSize) {
- byte *paletteData = new byte[chunkSize];
- _fileStream->read(paletteData, chunkSize);
+bool RobotDecoder::load(GuiResourceId id) {
+ // TODO: RAMA's robot 1003 cannot be played (shown at the menu screen) -
+ // its drawn at odd coordinates. SV can't play it either (along with some
+ // others), so it must be some new functionality added in RAMA's robot
+ // videos. Skip it for now.
+ if (g_sci->getGameId() == GID_RAMA && id == 1003)
+ return false;
- // SCI1.1 palette
- byte palFormat = paletteData[32];
- uint16 palColorStart = paletteData[25];
- uint16 palColorCount = READ_SCI11ENDIAN_UINT16(paletteData + 29);
+ // TODO: The robot video in the Lighthouse demo gets stuck
+ if (g_sci->getGameId() == GID_LIGHTHOUSE && id == 16)
+ return false;
- int palOffset = 37;
- memset(_palette, 0, 256 * 3);
+ Common::String fileName = Common::String::format("%d.rbt", id);
+ Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(fileName);
- for (uint16 colorNo = palColorStart; colorNo < palColorStart + palColorCount; colorNo++) {
- if (palFormat == kRobotPalVariable)
- palOffset++;
- _palette[colorNo * 3 + 0] = paletteData[palOffset++];
- _palette[colorNo * 3 + 1] = paletteData[palOffset++];
- _palette[colorNo * 3 + 2] = paletteData[palOffset++];
+ if (!stream) {
+ warning("Unable to open robot file %s", fileName.c_str());
+ return false;
}
- _dirtyPalette = true;
- delete[] paletteData;
+ return loadStream(stream);
}
+void RobotDecoder::close() {
+ VideoDecoder::close();
-void RobotDecoder::readFrameSizesChunk() {
- // The robot video file contains 2 tables, with one entry for each frame:
- // - A table containing the size of the image in each video frame
- // - A table containing the total size of each video frame.
- // In v5 robots, the tables contain 16-bit integers, whereas in v6 robots,
- // they contain 32-bit integers.
-
- _frameTotalSize = new uint32[_header.frameCount];
-
- // TODO: The table reading code can probably be removed once the
- // audio chunk size is figured out (check the TODO inside processNextFrame())
-#if 0
- // We don't need any of the two tables to play the video, so we ignore
- // both of them.
- uint16 wordSize = _header.version == 6 ? 4 : 2;
- _fileStream->skip(_header.frameCount * wordSize * 2);
-#else
- switch (_header.version) {
- case 4:
- case 5: // sizes are 16-bit integers
- // Skip table with frame image sizes, as we don't need it
- _fileStream->skip(_header.frameCount * 2);
- for (int i = 0; i < _header.frameCount; ++i)
- _frameTotalSize[i] = _fileStream->readUint16();
- break;
- case 6: // sizes are 32-bit integers
- // Skip table with frame image sizes, as we don't need it
- _fileStream->skip(_header.frameCount * 4);
- for (int i = 0; i < _header.frameCount; ++i)
- _frameTotalSize[i] = _fileStream->readUint32();
- break;
- default:
- error("Can't yet handle index table for robot version %d", _header.version);
- }
-#endif
-
- // 2 more unknown tables
- _fileStream->skip(1024 + 512);
+ delete _fileStream;
+ _fileStream = 0;
- // Pad to nearest 2 kilobytes
- uint32 curPos = _fileStream->pos();
- if (curPos & 0x7ff)
- _fileStream->seek((curPos & ~0x7ff) + 2048);
+ delete[] _frameTotalSize;
+ _frameTotalSize = 0;
}
-void RobotDecoder::calculateVideoDimensions() {
- // This is an O(n) operation, as each frame has a different size.
- // We need to know the actual frame size to have a constant video size.
- uint32 pos = _fileStream->pos();
-
- for (uint32 curFrame = 0; curFrame < _header.frameCount; curFrame++) {
- _fileStream->skip(4);
- uint16 frameWidth = _fileStream->readUint16();
- uint16 frameHeight = _fileStream->readUint16();
- if (frameWidth > _width)
- _width = frameWidth;
- if (frameHeight > _height)
- _height = frameHeight;
- _fileStream->skip(_frameTotalSize[curFrame] - 8);
- }
+void RobotDecoder::readNextPacket() {
+ // Get our track
+ RobotVideoTrack *videoTrack = (RobotVideoTrack *)getTrack(0);
+ videoTrack->increaseCurFrame();
+ Graphics::Surface *surface = videoTrack->getSurface();
- _fileStream->seek(pos);
-}
+ if (videoTrack->endOfTrack())
+ return;
-const Graphics::Surface *RobotDecoder::decodeNextFrame() {
// Read frame image header (24 bytes)
_fileStream->skip(3);
byte frameScale = _fileStream->readByte();
@@ -247,23 +158,28 @@ const Graphics::Surface *RobotDecoder::decodeNextFrame() {
_fileStream->skip(4); // unknown, almost always 0
uint16 frameX = _fileStream->readUint16();
uint16 frameY = _fileStream->readUint16();
+
// TODO: In v4 robot files, frameX and frameY have a different meaning.
// Set them both to 0 for v4 for now, so that robots in PQ:SWAT show up
// correctly.
if (_header.version == 4)
frameX = frameY = 0;
+
uint16 compressedSize = _fileStream->readUint16();
uint16 frameFragments = _fileStream->readUint16();
_fileStream->skip(4); // unknown
uint32 decompressedSize = frameWidth * frameHeight * frameScale / 100;
+
// FIXME: A frame's height + position can go off limits... why? With the
// following, we cut the contents to fit the frame
- uint16 scaledHeight = CLIP<uint16>(decompressedSize / frameWidth, 0, _height - frameY);
+ uint16 scaledHeight = CLIP<uint16>(decompressedSize / frameWidth, 0, surface->h - frameY);
+
// FIXME: Same goes for the frame's width + position. In this case, we
// modify the position to fit the contents on screen.
- if (frameWidth + frameX > _width)
- frameX = _width - frameWidth;
- assert (frameWidth + frameX <= _width && scaledHeight + frameY <= _height);
+ if (frameWidth + frameX > surface->w)
+ frameX = surface->w - frameWidth;
+
+ assert(frameWidth + frameX <= surface->w && scaledHeight + frameY <= surface->h);
DecompressorLZS lzs;
byte *decompressedFrame = new byte[decompressedSize];
@@ -294,24 +210,23 @@ const Graphics::Surface *RobotDecoder::decodeNextFrame() {
// Copy over the decompressed frame
byte *inFrame = decompressedFrame;
- byte *outFrame = (byte *)_surface->pixels;
+ byte *outFrame = (byte *)surface->pixels;
// Black out the surface
- memset(outFrame, 0, _width * _height);
+ memset(outFrame, 0, surface->w * surface->h);
// Move to the correct y coordinate
- outFrame += _width * frameY;
+ outFrame += surface->w * frameY;
for (uint16 y = 0; y < scaledHeight; y++) {
memcpy(outFrame + frameX, inFrame, frameWidth);
inFrame += frameWidth;
- outFrame += _width;
+ outFrame += surface->w;
}
delete[] decompressedFrame;
- // +1 because we start with frame number -1
- uint32 audioChunkSize = _frameTotalSize[_curFrame + 1] - (24 + compressedSize);
+ uint32 audioChunkSize = _frameTotalSize[videoTrack->getCurFrame()] - (24 + compressedSize);
// TODO: The audio chunk size below is usually correct, but there are some
// exceptions (e.g. robot 4902 in Phantasmagoria, towards its end)
@@ -326,41 +241,166 @@ const Graphics::Surface *RobotDecoder::decodeNextFrame() {
// Queue the next audio frame
// FIXME: For some reason, there are audio hiccups/gaps
if (_header.hasSound) {
- _fileStream->skip(8); // header
- _audioStream->queueBuffer(g_sci->_audio->getDecodedRobotAudioFrame(_fileStream, audioChunkSize - 8),
- (audioChunkSize - 8) * 2, DisposeAfterUse::NO,
- Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN);
+ RobotAudioTrack *audioTrack = (RobotAudioTrack *)getTrack(1);
+ _fileStream->skip(8); // header
+ audioChunkSize -= 8;
+ audioTrack->queueBuffer(g_sci->_audio->getDecodedRobotAudioFrame(_fileStream, audioChunkSize), audioChunkSize * 2);
} else {
_fileStream->skip(audioChunkSize);
}
+}
- if (_curFrame == -1)
- _startTime = g_system->getMillis();
-
- _curFrame++;
+void RobotDecoder::readHeaderChunk() {
+ // Header (60 bytes)
+ _fileStream->skip(6);
+ _header.version = _fileStream->readUint16();
+ _header.audioChunkSize = _fileStream->readUint16();
+ _header.audioSilenceSize = _fileStream->readUint16();
+ _fileStream->skip(2);
+ _header.frameCount = _fileStream->readUint16();
+ _header.paletteDataSize = _fileStream->readUint16();
+ _header.unkChunkDataSize = _fileStream->readUint16();
+ _fileStream->skip(5);
+ _header.hasSound = _fileStream->readByte();
+ _fileStream->skip(34);
- return _surface;
+ // Some videos (e.g. robot 1305 in Phantasmagoria and
+ // robot 184 in Lighthouse) have an unknown chunk before
+ // the palette chunk (probably used for sound preloading).
+ // Skip it here.
+ if (_header.unkChunkDataSize)
+ _fileStream->skip(_header.unkChunkDataSize);
}
-void RobotDecoder::close() {
- if (!_fileStream)
- return;
+void RobotDecoder::readFrameSizesChunk() {
+ // The robot video file contains 2 tables, with one entry for each frame:
+ // - A table containing the size of the image in each video frame
+ // - A table containing the total size of each video frame.
+ // In v5 robots, the tables contain 16-bit integers, whereas in v6 robots,
+ // they contain 32-bit integers.
- delete _fileStream;
- _fileStream = 0;
+ _frameTotalSize = new uint32[_header.frameCount];
+
+ // TODO: The table reading code can probably be removed once the
+ // audio chunk size is figured out (check the TODO inside processNextFrame())
+#if 0
+ // We don't need any of the two tables to play the video, so we ignore
+ // both of them.
+ uint16 wordSize = _header.version == 6 ? 4 : 2;
+ _fileStream->skip(_header.frameCount * wordSize * 2);
+#else
+ switch (_header.version) {
+ case 4:
+ case 5: // sizes are 16-bit integers
+ // Skip table with frame image sizes, as we don't need it
+ _fileStream->skip(_header.frameCount * 2);
+ for (int i = 0; i < _header.frameCount; ++i)
+ _frameTotalSize[i] = _fileStream->readUint16();
+ break;
+ case 6: // sizes are 32-bit integers
+ // Skip table with frame image sizes, as we don't need it
+ _fileStream->skip(_header.frameCount * 4);
+ for (int i = 0; i < _header.frameCount; ++i)
+ _frameTotalSize[i] = _fileStream->readUint32();
+ break;
+ default:
+ error("Can't yet handle index table for robot version %d", _header.version);
+ }
+#endif
+ // 2 more unknown tables
+ _fileStream->skip(1024 + 512);
+
+ // Pad to nearest 2 kilobytes
+ uint32 curPos = _fileStream->pos();
+ if (curPos & 0x7ff)
+ _fileStream->seek((curPos & ~0x7ff) + 2048);
+}
+
+RobotDecoder::RobotVideoTrack::RobotVideoTrack(int frameCount) : _frameCount(frameCount) {
+ _surface = new Graphics::Surface();
+ _curFrame = -1;
+ _dirtyPalette = false;
+}
+
+RobotDecoder::RobotVideoTrack::~RobotVideoTrack() {
_surface->free();
delete _surface;
- _surface = 0;
+}
- if (_header.hasSound) {
- _mixer->stopHandle(_audioHandle);
- //delete _audioStream; _audioStream = 0;
+uint16 RobotDecoder::RobotVideoTrack::getWidth() const {
+ return _surface->w;
+}
+
+uint16 RobotDecoder::RobotVideoTrack::getHeight() const {
+ return _surface->h;
+}
+
+Graphics::PixelFormat RobotDecoder::RobotVideoTrack::getPixelFormat() const {
+ return _surface->format;
+}
+
+void RobotDecoder::RobotVideoTrack::readPaletteChunk(Common::SeekableSubReadStreamEndian *stream, uint16 chunkSize) {
+ byte *paletteData = new byte[chunkSize];
+ stream->read(paletteData, chunkSize);
+
+ // SCI1.1 palette
+ byte palFormat = paletteData[32];
+ uint16 palColorStart = paletteData[25];
+ uint16 palColorCount = READ_SCI11ENDIAN_UINT16(paletteData + 29);
+
+ int palOffset = 37;
+ memset(_palette, 0, 256 * 3);
+
+ for (uint16 colorNo = palColorStart; colorNo < palColorStart + palColorCount; colorNo++) {
+ if (palFormat == kRobotPalVariable)
+ palOffset++;
+ _palette[colorNo * 3 + 0] = paletteData[palOffset++];
+ _palette[colorNo * 3 + 1] = paletteData[palOffset++];
+ _palette[colorNo * 3 + 2] = paletteData[palOffset++];
}
- reset();
+ _dirtyPalette = true;
+ delete[] paletteData;
}
-#endif
+void RobotDecoder::RobotVideoTrack::calculateVideoDimensions(Common::SeekableSubReadStreamEndian *stream, uint32 *frameSizes) {
+ // This is an O(n) operation, as each frame has a different size.
+ // We need to know the actual frame size to have a constant video size.
+ uint32 pos = stream->pos();
+
+ uint16 width = 0, height = 0;
+
+ for (int curFrame = 0; curFrame < _frameCount; curFrame++) {
+ stream->skip(4);
+ uint16 frameWidth = stream->readUint16();
+ uint16 frameHeight = stream->readUint16();
+ if (frameWidth > width)
+ width = frameWidth;
+ if (frameHeight > height)
+ height = frameHeight;
+ stream->skip(frameSizes[curFrame] - 8);
+ }
+
+ stream->seek(pos);
+
+ _surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
+}
+
+RobotDecoder::RobotAudioTrack::RobotAudioTrack() {
+ _audioStream = Audio::makeQueuingAudioStream(11025, false);
+}
+
+RobotDecoder::RobotAudioTrack::~RobotAudioTrack() {
+ delete _audioStream;
+}
+
+void RobotDecoder::RobotAudioTrack::queueBuffer(byte *buffer, int size) {
+ _audioStream->queueBuffer(buffer, size, DisposeAfterUse::YES, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN);
+}
+
+Audio::AudioStream *RobotDecoder::RobotAudioTrack::getAudioStream() const {
+ return _audioStream;
+}
} // End of namespace Sci
diff --git a/engines/sci/video/robot_decoder.h b/engines/sci/video/robot_decoder.h
index 3f93582418..437954f7fb 100644
--- a/engines/sci/video/robot_decoder.h
+++ b/engines/sci/video/robot_decoder.h
@@ -25,79 +25,103 @@
#include "common/rational.h"
#include "common/rect.h"
-#include "common/stream.h"
-#include "common/substream.h"
-#include "audio/audiostream.h"
-#include "audio/mixer.h"
-#include "graphics/pixelformat.h"
#include "video/video_decoder.h"
-namespace Sci {
+namespace Audio {
+class QueuingAudioStream;
+}
-#ifdef ENABLE_SCI32
-
-struct RobotHeader {
- // 6 bytes, identifier bytes
- uint16 version;
- uint16 audioChunkSize;
- uint16 audioSilenceSize;
- // 2 bytes, unknown
- uint16 frameCount;
- uint16 paletteDataSize;
- uint16 unkChunkDataSize;
- // 5 bytes, unknown
- byte hasSound;
- // 34 bytes, unknown
-};
+namespace Common {
+class SeekableSubReadStreamEndian;
+}
-class RobotDecoder : public Video::FixedRateVideoDecoder {
+namespace Sci {
+
+class RobotDecoder : public Video::VideoDecoder {
public:
- RobotDecoder(Audio::Mixer *mixer, bool isBigEndian);
+ RobotDecoder(bool isBigEndian);
virtual ~RobotDecoder();
bool loadStream(Common::SeekableReadStream *stream);
bool load(GuiResourceId id);
void close();
- bool isVideoLoaded() const { return _fileStream != 0; }
- uint16 getWidth() const { return _width; }
- uint16 getHeight() const { return _height; }
- uint32 getFrameCount() const { return _header.frameCount; }
- const Graphics::Surface *decodeNextFrame();
- Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); }
- const byte *getPalette() { _dirtyPalette = false; return _palette; }
- bool hasDirtyPalette() const { return _dirtyPalette; }
void setPos(uint16 x, uint16 y) { _pos = Common::Point(x, y); }
Common::Point getPos() const { return _pos; }
protected:
- Common::Rational getFrameRate() const { return Common::Rational(60, 10); }
+ void readNextPacket();
private:
+ class RobotVideoTrack : public FixedRateVideoTrack {
+ public:
+ RobotVideoTrack(int frameCount);
+ ~RobotVideoTrack();
+
+ uint16 getWidth() const;
+ uint16 getHeight() const;
+ Graphics::PixelFormat getPixelFormat() const;
+ int getCurFrame() const { return _curFrame; }
+ int getFrameCount() const { return _frameCount; }
+ const Graphics::Surface *decodeNextFrame() { return _surface; }
+ const byte *getPalette() const { _dirtyPalette = false; return _palette; }
+ bool hasDirtyPalette() const { return _dirtyPalette; }
+
+ void readPaletteChunk(Common::SeekableSubReadStreamEndian *stream, uint16 chunkSize);
+ void calculateVideoDimensions(Common::SeekableSubReadStreamEndian *stream, uint32 *frameSizes);
+ Graphics::Surface *getSurface() { return _surface; }
+ void increaseCurFrame() { _curFrame++; }
+
+ protected:
+ Common::Rational getFrameRate() const { return Common::Rational(60, 10); }
+
+ private:
+ int _frameCount;
+ int _curFrame;
+ byte _palette[256 * 3];
+ mutable bool _dirtyPalette;
+ Graphics::Surface *_surface;
+ };
+
+ class RobotAudioTrack : public AudioTrack {
+ public:
+ RobotAudioTrack();
+ ~RobotAudioTrack();
+
+ Audio::Mixer::SoundType getSoundType() const { return Audio::Mixer::kMusicSoundType; }
+
+ void queueBuffer(byte *buffer, int size);
+
+ protected:
+ Audio::AudioStream *getAudioStream() const;
+
+ private:
+ Audio::QueuingAudioStream *_audioStream;
+ };
+
+ struct RobotHeader {
+ // 6 bytes, identifier bytes
+ uint16 version;
+ uint16 audioChunkSize;
+ uint16 audioSilenceSize;
+ // 2 bytes, unknown
+ uint16 frameCount;
+ uint16 paletteDataSize;
+ uint16 unkChunkDataSize;
+ // 5 bytes, unknown
+ byte hasSound;
+ // 34 bytes, unknown
+ } _header;
+
void readHeaderChunk();
- void readPaletteChunk(uint16 chunkSize);
void readFrameSizesChunk();
- void calculateVideoDimensions();
- void freeData();
-
- RobotHeader _header;
Common::Point _pos;
bool _isBigEndian;
+ uint32 *_frameTotalSize;
Common::SeekableSubReadStreamEndian *_fileStream;
-
- uint16 _width;
- uint16 _height;
- uint32 *_frameTotalSize;
- byte _palette[256 * 3];
- bool _dirtyPalette;
- Graphics::Surface *_surface;
- Audio::QueuingAudioStream *_audioStream;
- Audio::SoundHandle _audioHandle;
- Audio::Mixer *_mixer;
};
-#endif
} // End of namespace Sci
diff --git a/engines/sci/video/seq_decoder.cpp b/engines/sci/video/seq_decoder.cpp
index abd64911a7..a7b6346eca 100644
--- a/engines/sci/video/seq_decoder.cpp
+++ b/engines/sci/video/seq_decoder.cpp
@@ -41,33 +41,44 @@ enum seqFrameTypes {
kSeqFrameDiff = 1
};
-SeqDecoder::SeqDecoder() {
- _fileStream = 0;
- _surface = 0;
- _dirtyPalette = false;
+SEQDecoder::SEQDecoder(uint frameDelay) : _frameDelay(frameDelay) {
}
-SeqDecoder::~SeqDecoder() {
+SEQDecoder::~SEQDecoder() {
close();
}
-bool SeqDecoder::loadStream(Common::SeekableReadStream *stream) {
+bool SEQDecoder::loadStream(Common::SeekableReadStream *stream) {
close();
+ addTrack(new SEQVideoTrack(stream, _frameDelay));
+
+ return true;
+}
+
+SEQDecoder::SEQVideoTrack::SEQVideoTrack(Common::SeekableReadStream *stream, uint frameDelay) {
+ assert(stream);
+ assert(frameDelay != 0);
_fileStream = stream;
+ _frameDelay = frameDelay;
+ _curFrame = -1;
+
_surface = new Graphics::Surface();
_surface->create(SEQ_SCREEN_WIDTH, SEQ_SCREEN_HEIGHT, Graphics::PixelFormat::createFormatCLUT8());
_frameCount = _fileStream->readUint16LE();
- // Set palette
- int paletteChunkSize = _fileStream->readUint32LE();
- readPaletteChunk(paletteChunkSize);
+ // Set initial palette
+ readPaletteChunk(_fileStream->readUint32LE());
+}
- return true;
+SEQDecoder::SEQVideoTrack::~SEQVideoTrack() {
+ delete _fileStream;
+ _surface->free();
+ delete _surface;
}
-void SeqDecoder::readPaletteChunk(uint16 chunkSize) {
+void SEQDecoder::SEQVideoTrack::readPaletteChunk(uint16 chunkSize) {
byte *paletteData = new byte[chunkSize];
_fileStream->read(paletteData, chunkSize);
@@ -91,23 +102,7 @@ void SeqDecoder::readPaletteChunk(uint16 chunkSize) {
delete[] paletteData;
}
-void SeqDecoder::close() {
- if (!_fileStream)
- return;
-
- _frameDelay = 0;
-
- delete _fileStream;
- _fileStream = 0;
-
- _surface->free();
- delete _surface;
- _surface = 0;
-
- reset();
-}
-
-const Graphics::Surface *SeqDecoder::decodeNextFrame() {
+const Graphics::Surface *SEQDecoder::SEQVideoTrack::decodeNextFrame() {
int16 frameWidth = _fileStream->readUint16LE();
int16 frameHeight = _fileStream->readUint16LE();
int16 frameLeft = _fileStream->readUint16LE();
@@ -142,9 +137,6 @@ const Graphics::Surface *SeqDecoder::decodeNextFrame() {
delete[] buf;
}
- if (_curFrame == -1)
- _startTime = g_system->getMillis();
-
_curFrame++;
return _surface;
}
@@ -159,7 +151,7 @@ const Graphics::Surface *SeqDecoder::decodeNextFrame() {
} \
memcpy(dest + writeRow * SEQ_SCREEN_WIDTH + writeCol, litData + litPos, n);
-bool SeqDecoder::decodeFrame(byte *rleData, int rleSize, byte *litData, int litSize, byte *dest, int left, int width, int height, int colorKey) {
+bool SEQDecoder::SEQVideoTrack::decodeFrame(byte *rleData, int rleSize, byte *litData, int litSize, byte *dest, int left, int width, int height, int colorKey) {
int writeRow = 0;
int writeCol = left;
int litPos = 0;
@@ -237,4 +229,9 @@ bool SeqDecoder::decodeFrame(byte *rleData, int rleSize, byte *litData, int litS
return true;
}
+const byte *SEQDecoder::SEQVideoTrack::getPalette() const {
+ _dirtyPalette = false;
+ return _palette;
+}
+
} // End of namespace Sci
diff --git a/engines/sci/video/seq_decoder.h b/engines/sci/video/seq_decoder.h
index 800a3c9024..890f349feb 100644
--- a/engines/sci/video/seq_decoder.h
+++ b/engines/sci/video/seq_decoder.h
@@ -40,44 +40,49 @@ namespace Sci {
/**
* Implementation of the Sierra SEQ decoder, used in KQ6 DOS floppy/CD and GK1 DOS
*/
-class SeqDecoder : public Video::FixedRateVideoDecoder {
+class SEQDecoder : public Video::VideoDecoder {
public:
- SeqDecoder();
- virtual ~SeqDecoder();
+ SEQDecoder(uint frameDelay);
+ virtual ~SEQDecoder();
bool loadStream(Common::SeekableReadStream *stream);
- void close();
-
- void setFrameDelay(int frameDelay) { _frameDelay = frameDelay; }
-
- bool isVideoLoaded() const { return _fileStream != 0; }
- uint16 getWidth() const { return SEQ_SCREEN_WIDTH; }
- uint16 getHeight() const { return SEQ_SCREEN_HEIGHT; }
- uint32 getFrameCount() const { return _frameCount; }
- const Graphics::Surface *decodeNextFrame();
- Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); }
- const byte *getPalette() { _dirtyPalette = false; return _palette; }
- bool hasDirtyPalette() const { return _dirtyPalette; }
-
-protected:
- Common::Rational getFrameRate() const { assert(_frameDelay); return Common::Rational(60, _frameDelay); }
private:
- enum {
- SEQ_SCREEN_WIDTH = 320,
- SEQ_SCREEN_HEIGHT = 200
+ class SEQVideoTrack : public FixedRateVideoTrack {
+ public:
+ SEQVideoTrack(Common::SeekableReadStream *stream, uint frameDelay);
+ ~SEQVideoTrack();
+
+ uint16 getWidth() const { return SEQ_SCREEN_WIDTH; }
+ uint16 getHeight() const { return SEQ_SCREEN_HEIGHT; }
+ Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); }
+ int getCurFrame() const { return _curFrame; }
+ int getFrameCount() const { return _frameCount; }
+ const Graphics::Surface *decodeNextFrame();
+ const byte *getPalette() const;
+ bool hasDirtyPalette() const { return _dirtyPalette; }
+
+ protected:
+ Common::Rational getFrameRate() const { return Common::Rational(60, _frameDelay); }
+
+ private:
+ enum {
+ SEQ_SCREEN_WIDTH = 320,
+ SEQ_SCREEN_HEIGHT = 200
+ };
+
+ void readPaletteChunk(uint16 chunkSize);
+ bool decodeFrame(byte *rleData, int rleSize, byte *litData, int litSize, byte *dest, int left, int width, int height, int colorKey);
+
+ Common::SeekableReadStream *_fileStream;
+ int _curFrame, _frameCount;
+ byte _palette[256 * 3];
+ mutable bool _dirtyPalette;
+ Graphics::Surface *_surface;
+ uint _frameDelay;
};
- void readPaletteChunk(uint16 chunkSize);
- bool decodeFrame(byte *rleData, int rleSize, byte *litData, int litSize, byte *dest, int left, int width, int height, int colorKey);
-
- uint16 _width, _height;
- uint16 _frameDelay;
- Common::SeekableReadStream *_fileStream;
- byte _palette[256 * 3];
- bool _dirtyPalette;
- uint32 _frameCount;
- Graphics::Surface *_surface;
+ uint _frameDelay;
};
} // End of namespace Sci