aboutsummaryrefslogtreecommitdiff
path: root/engines/sci
diff options
context:
space:
mode:
authorMatthew Hoops2011-10-07 14:34:22 -0400
committerMatthew Hoops2011-10-07 14:34:22 -0400
commite1dc4db7aa53d1bbc4cdb03d1163c97d049702f5 (patch)
tree9d57b5fdd6737bc3449851a87e573abe7be984a6 /engines/sci
parentdeab5b28753155863062746ef1239535f562fd0b (diff)
parent842b471e45ae8b7c1b4516b9bd5bf39d61112077 (diff)
downloadscummvm-rg350-e1dc4db7aa53d1bbc4cdb03d1163c97d049702f5.tar.gz
scummvm-rg350-e1dc4db7aa53d1bbc4cdb03d1163c97d049702f5.tar.bz2
scummvm-rg350-e1dc4db7aa53d1bbc4cdb03d1163c97d049702f5.zip
Merge remote branch 'upstream/master' into pegasus
Conflicts: video/qt_decoder.cpp
Diffstat (limited to 'engines/sci')
-rw-r--r--engines/sci/detection.cpp11
-rw-r--r--engines/sci/detection_tables.h12
-rw-r--r--engines/sci/engine/kernel.cpp11
-rw-r--r--engines/sci/engine/kernel.h1
-rw-r--r--engines/sci/engine/kernel_tables.h13
-rw-r--r--engines/sci/engine/kfile.cpp6
-rw-r--r--engines/sci/engine/kgraphics.cpp118
-rw-r--r--engines/sci/engine/kmath.cpp8
-rw-r--r--engines/sci/engine/kmisc.cpp38
-rw-r--r--engines/sci/engine/kstring.cpp34
-rw-r--r--engines/sci/engine/savegame.cpp8
-rw-r--r--engines/sci/engine/scriptdebug.cpp5
-rw-r--r--engines/sci/engine/static_selectors.cpp32
-rw-r--r--engines/sci/engine/vm.cpp1
-rw-r--r--engines/sci/engine/workarounds.cpp9
-rw-r--r--engines/sci/graphics/cursor.cpp2
-rw-r--r--engines/sci/graphics/frameout.cpp102
-rw-r--r--engines/sci/graphics/frameout.h6
-rw-r--r--engines/sci/graphics/palette.cpp17
-rw-r--r--engines/sci/graphics/picture.cpp2
-rw-r--r--engines/sci/graphics/text32.cpp238
-rw-r--r--engines/sci/graphics/text32.h71
-rw-r--r--engines/sci/graphics/view.cpp3
-rw-r--r--engines/sci/module.mk1
-rw-r--r--engines/sci/resource.cpp14
-rw-r--r--engines/sci/sci.cpp4
-rw-r--r--engines/sci/sci.h2
-rw-r--r--engines/sci/sound/drivers/gm_names.h4
-rw-r--r--engines/sci/sound/midiparser_sci.cpp17
-rw-r--r--engines/sci/sound/music.cpp7
-rw-r--r--engines/sci/sound/soundcmd.cpp68
-rw-r--r--engines/sci/sound/soundcmd.h3
32 files changed, 658 insertions, 210 deletions
diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp
index 33ca3a6c9c..56da696592 100644
--- a/engines/sci/detection.cpp
+++ b/engines/sci/detection.cpp
@@ -296,8 +296,10 @@ Common::String convertSierraGameId(Common::String sierraId, uint32 *gameFlags, R
sierraId == "mg" || sierraId == "pq" ||
sierraId == "jones" ||
sierraId == "cardgames" || sierraId == "solitare" ||
- sierraId == "hoyle3" || sierraId == "hoyle4")
+ sierraId == "hoyle4")
demoThreshold = 40;
+ if (sierraId == "hoyle3")
+ demoThreshold = 45; // cnick-kq has 42 scripts. The actual hoyle 3 demo has 27.
if (sierraId == "fp" || sierraId == "gk" || sierraId == "pq4")
demoThreshold = 150;
@@ -310,8 +312,11 @@ Common::String convertSierraGameId(Common::String sierraId, uint32 *gameFlags, R
return "cnick-lsl";
if (sierraId == "sq4" && resources->size() == 34)
return "cnick-sq";
-
- // TODO: cnick-kq, cnick-laurabow and cnick-longbow (their resources can't be read)
+ if (sierraId == "hoyle3" && resources->size() == 42)
+ return "cnick-kq";
+ if (sierraId == "rh budget" && resources->size() == 39)
+ return "cnick-longbow";
+ // TODO: cnick-laurabow (the name of the game object contains junk)
// Handle Astrochicken 1 (SQ3) and 2 (SQ4)
if (sierraId == "sq3" && resources->size() == 20)
diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h
index a3dd305222..455223a086 100644
--- a/engines/sci/detection_tables.h
+++ b/engines/sci/detection_tables.h
@@ -91,7 +91,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, ADGF_DEMO, GUIO_NOSPEECH },
- // Castle of Dr. Brain - English DOS Floppy EGA (from omer_mor, bug report #3035349)
+ // Castle of Dr. Brain - English DOS 5.25" Floppy EGA (from omer_mor, bug report #3035349)
{"castlebrain", "EGA", {
{"resource.map", 0, "88d106f945f7fd9d1aeda961cfec38a9", 2646},
{"resource.000", 0, "6e125f4ce3f4f5c35f2617c7b66c6e21", 25325},
@@ -104,6 +104,16 @@ static const struct ADGameDescription SciGameDescriptions[] = {
AD_LISTEND},
Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+ // Castle of Dr. Brain - English DOS 3.5" Floppy EGA (from nozomi77, bug report #3405307)
+ {"castlebrain", "EGA", {
+ {"resource.map", 0, "dfcf23e36cb81223bdf11166aaf90754", 2730},
+ {"resource.000", 0, "27ec5fa09cd12a7fd16e86d96a2ed245", 300857},
+ {"resource.001", 0, "6e0020a9f9bef9a9d65943dc013f14b5", 222108},
+ {"resource.002", 0, "de2f182529efaad2c4b510b452ab77ac", 633662},
+ {"resource.003", 0, "38b4b37febc6b4f5061c461a283df148", 430388},
+ AD_LISTEND},
+ Common::EN_ANY, Common::kPlatformPC, 0, GUIO_NOSPEECH },
+
// 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"
// SCI interpreter version 1.000.510
diff --git a/engines/sci/engine/kernel.cpp b/engines/sci/engine/kernel.cpp
index 8fb6322f55..a83a026762 100644
--- a/engines/sci/engine/kernel.cpp
+++ b/engines/sci/engine/kernel.cpp
@@ -488,8 +488,15 @@ bool Kernel::signatureMatch(const uint16 *sig, int argc, const reg_t *argv) {
if ((type & SIG_IS_INVALID) && (!(curSig & SIG_IS_INVALID)))
return false; // pointer is invalid and signature doesn't allow that?
- if (!((type & ~SIG_IS_INVALID) & curSig))
- return false; // type mismatch
+ if (!((type & ~SIG_IS_INVALID) & curSig)) {
+ if ((type & ~SIG_IS_INVALID) == SIG_TYPE_ERROR && (curSig & SIG_IS_INVALID)) {
+ // Type is unknown (error - usually because of a deallocated object or
+ // stale pointer) and the signature allows invalid pointers. In this case,
+ // ignore the invalid pointer.
+ } else {
+ return false; // type mismatch
+ }
+ }
if (!(curSig & SIG_MORE_MAY_FOLLOW)) {
sig++;
diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h
index ff3c67c84b..b605908dc1 100644
--- a/engines/sci/engine/kernel.h
+++ b/engines/sci/engine/kernel.h
@@ -476,6 +476,7 @@ 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);
#endif
reg_t kDoSoundInit(EngineState *s, int argc, reg_t *argv);
diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h
index 0c5d4e680d..d3adcaccbf 100644
--- a/engines/sci/engine/kernel_tables.h
+++ b/engines/sci/engine/kernel_tables.h
@@ -546,6 +546,7 @@ static SciKernelMapEntry s_kernelMap[] = {
{ MAP_CALL(ScrollWindow), SIG_EVERYWHERE, "(.*)", NULL, NULL },
{ MAP_CALL(SetFontRes), SIG_EVERYWHERE, "ii", NULL, NULL },
{ MAP_CALL(Font), SIG_EVERYWHERE, "i(.*)", NULL, NULL },
+ { MAP_CALL(Bitmap), SIG_EVERYWHERE, "(.*)", NULL, NULL },
// SCI2.1 Empty Functions
@@ -564,6 +565,9 @@ static SciKernelMapEntry s_kernelMap[] = {
// just use GetConfig and mark this one as empty, like the DOS version does.
{ MAP_EMPTY(GetSierraProfileInt), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+ // Debug function called whenever the current room changes
+ { MAP_EMPTY(NewRoom), SIG_EVERYWHERE, "(.*)", NULL, NULL },
+
// Unused / debug SCI2.1 unused functions, always mapped to kDummy
// The debug functions are called from the inbuilt debugger or polygon
@@ -593,7 +597,6 @@ static SciKernelMapEntry s_kernelMap[] = {
// UpdateLine - used by LSL6
// SetPalStyleRange - 2 integer parameters, start and end. All styles from start-end
// (inclusive) are set to 0
- // NewRoom - 1 integer parameter, the current room number
// MorphOn - used by SQ6, script 900, the datacorder reprogramming puzzle (from room 270)
// SetHotRectangles - used by Phantasmagoria 1
#endif
@@ -602,7 +605,7 @@ static SciKernelMapEntry s_kernelMap[] = {
};
/** Default kernel name table. */
-static const char *s_defaultKernelNames[] = {
+static const char *const s_defaultKernelNames[] = {
/*0x00*/ "Load",
/*0x01*/ "UnLoad",
/*0x02*/ "ScriptID",
@@ -751,7 +754,7 @@ static const char *s_defaultKernelNames[] = {
// NOTE: 0x72-0x79, 0x85-0x86, 0x88 are from the GK2 demo (which has debug support) and are
// just Dummy in other SCI2 games.
-static const char *sci2_default_knames[] = {
+static const char *const sci2_default_knames[] = {
/*0x00*/ "Load",
/*0x01*/ "UnLoad",
/*0x02*/ "ScriptID",
@@ -916,7 +919,7 @@ static const char *sci2_default_knames[] = {
/*0x9f*/ "MessageBox"
};
-static const char *sci21_default_knames[] = {
+static const char *const sci21_default_knames[] = {
/*0x00*/ "Load",
/*0x01*/ "UnLoad",
/*0x02*/ "ScriptID",
@@ -1059,7 +1062,7 @@ static const char *sci21_default_knames[] = {
/*0x8b*/ "SetPalStyleRange",
/*0x8c*/ "AddPicAt",
/*0x8d*/ "MessageBox", // SCI3, was Dummy in SCI2.1
- /*0x8e*/ "NewRoom",
+ /*0x8e*/ "NewRoom", // debug function
/*0x8f*/ "Dummy",
/*0x90*/ "Priority",
/*0x91*/ "MorphOn",
diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp
index 1bd6754ca5..0c73125bdb 100644
--- a/engines/sci/engine/kfile.cpp
+++ b/engines/sci/engine/kfile.cpp
@@ -578,11 +578,15 @@ reg_t kSaveGame(EngineState *s, int argc, reg_t *argv) {
game_description = dialog->getResultString();
if (game_description.empty()) {
// create our own description for the saved game, the user didnt enter it
+ #if defined(USE_SAVEGAME_TIMESTAMP)
TimeDate curTime;
g_system->getTimeAndDate(curTime);
curTime.tm_year += 1900; // fixup year
curTime.tm_mon++; // fixup month
- game_description = Common::String::format("%02d.%02d.%04d / %02d:%02d:%02d", curTime.tm_mday, curTime.tm_mon, curTime.tm_year, curTime.tm_hour, curTime.tm_min, curTime.tm_sec);
+ game_description = Common::String::format("%04d.%02d.%02d / %02d:%02d:%02d", curTime.tm_year, curTime.tm_mon, curTime.tm_mday, curTime.tm_hour, curTime.tm_min, curTime.tm_sec);
+ #else
+ game_description = Common::String::format("Save %d", savegameId + 1);
+ #endif
}
delete dialog;
g_sci->_soundCmd->pauseAll(false); // unpause music ( we can't have it paused during save)
diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp
index 36de767464..9f309aeab7 100644
--- a/engines/sci/engine/kgraphics.cpp
+++ b/engines/sci/engine/kgraphics.cpp
@@ -49,6 +49,7 @@
#include "sci/graphics/text16.h"
#include "sci/graphics/view.h"
#ifdef ENABLE_SCI32
+#include "sci/graphics/text32.h"
#include "sci/graphics/frameout.h"
#include "sci/video/robot_decoder.h"
#endif
@@ -1321,10 +1322,10 @@ reg_t kRepaintPlane(EngineState *s, int argc, reg_t *argv) {
reg_t kAddPicAt(EngineState *s, int argc, reg_t *argv) {
reg_t planeObj = argv[0];
GuiResourceId pictureId = argv[1].toUint16();
- int16 forWidth = argv[2].toSint16();
- // argv[3] seems to be 0 most of the time
+ int16 pictureX = argv[2].toSint16();
+ int16 pictureY = argv[3].toSint16();
- g_sci->_gfxFrameout->kernelAddPicAt(planeObj, forWidth, pictureId);
+ g_sci->_gfxFrameout->kernelAddPicAt(planeObj, pictureId, pictureX, pictureY);
return s->r_acc;
}
@@ -1333,12 +1334,7 @@ reg_t kGetHighPlanePri(EngineState *s, int argc, reg_t *argv) {
}
reg_t kFrameOut(EngineState *s, int argc, reg_t *argv) {
- // This kernel call likely seems to be doing the screen updates,
- // as its called right after a view is updated
-
- // TODO
g_sci->_gfxFrameout->kernelFrameout();
-
return NULL_REG;
}
@@ -1380,15 +1376,19 @@ reg_t kIsOnMe(EngineState *s, int argc, reg_t *argv) {
}
reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv) {
- // TODO: argument 0 is usually 0, and arguments 1 and 2 are usually 1
switch (argv[0].toUint16()) {
case 0: {
if (argc != 4) {
warning("kCreateTextBitmap(0): expected 4 arguments, got %i", argc);
return NULL_REG;
}
- //reg_t object = argv[3];
- //Common::String text = s->_segMan->getString(readSelector(s->_segMan, object, SELECTOR(text)));
+ 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());
+ // TODO: arguments 1 and 2
+ g_sci->_gfxText32->createTextBitmap(object);
break;
}
case 1: {
@@ -1396,8 +1396,11 @@ reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv) {
warning("kCreateTextBitmap(0): 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)));
+ reg_t object = argv[1];
+ Common::String text = s->_segMan->getString(readSelector(s->_segMan, object, SELECTOR(text)));
+ debugC(kDebugLevelStrings, "kCreateTextBitmap case 1 (%04x:%04x)", PRINT_REG(argv[1]));
+ debugC(kDebugLevelStrings, "%s", text.c_str());
+ g_sci->_gfxText32->createTextBitmap(object);
break;
}
default:
@@ -1667,6 +1670,95 @@ reg_t kFont(EngineState *s, int argc, reg_t *argv) {
return s->r_acc;
}
+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();
+ uint16 width2 = (argc >= 6) ? argv[5].toUint16() : 0;
+ uint16 height2 = (argc >= 7) ? argv[6].toUint16() : 0;
+ uint16 transparent = (argc >= 8) ? argv[7].toUint16() : 0;
+ warning("kBitmap(0): width %d, height %d, skip %d, back %d, width2 %d, height2 %d, transparent %d",
+ width, height, skip, back, width2, height2, transparent);
+ // returns a pointer to a bitmap
+ }
+ break;
+ case 1: // dispose bitmap surface
+ // 1 param, bitmap pointer, called e.g. from MenuItem::dispose
+ // in Torin's Passage, script 64893
+ warning("kBitmap(1), bitmap ptr %04x:%04x", PRINT_REG(argv[1]));
+ break;
+ case 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 bitmapPtr = argv[1]; // obtained from kBitmap(0)
+ // The tiled view seems to always have 2 loops.
+ // These loops need to have 1 cel in loop 0 and 8 cels in loop 1.
+ uint16 view = argv[2].toUint16(); // vTiles selector
+ uint16 loop = argv[3].toUint16();
+ uint16 cel = argv[4].toUint16();
+ uint16 x = argv[5].toUint16();
+ uint16 y = argv[6].toUint16();
+ warning("kBitmap(3): bitmap ptr %04x:%04x, view %d, loop %d, cel %d, x %d, y %d",
+ PRINT_REG(bitmapPtr), view, loop, cel, x, y);
+ }
+ break;
+ case 4: // process text
+ {
+ // 13 params, called e.g. from TextButton::createBitmap() in Torin's Passage,
+ // script 64894
+ reg_t bitmapPtr = argv[1]; // obtained from kBitmap(0)
+ Common::String text = s->_segMan->getString(argv[2]);
+ // unk3
+ // unk4
+ // unk5
+ // unk6
+ // skip?
+ // back?
+ uint16 font = argv[9].toUint16();
+ uint16 mode = argv[10].toUint16();
+ // unk
+ uint16 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());
+ }
+ break;
+ case 5:
+ {
+ // 6 params, called e.g. from TextView::init() and TextView::draw()
+ // in Torin's Passage, script 64890
+ reg_t bitmapPtr = argv[1]; // obtained from kBitmap(0)
+ uint16 unk1 = argv[2].toUint16(); // unknown, usually 0, judging from scripts?
+ uint16 unk2 = argv[3].toUint16(); // unknown, usually 0, judging from scripts?
+ uint16 width = argv[4].toUint16(); // width - 1
+ uint16 height = argv[5].toUint16(); // height - 1
+ uint16 back = argv[6].toUint16();
+ warning("kBitmap(5): bitmap ptr %04x:%04x, unk1 %d, unk2 %d, width %d, height %d, back %d",
+ PRINT_REG(bitmapPtr), unk1, unk2, width, height, back);
+ }
+ break;
+ default:
+ kStub(s, argc, argv);
+ break;
+ }
+
+ return s->r_acc;
+}
+
#endif
} // End of namespace Sci
diff --git a/engines/sci/engine/kmath.cpp b/engines/sci/engine/kmath.cpp
index ef795d7e2f..7570856dff 100644
--- a/engines/sci/engine/kmath.cpp
+++ b/engines/sci/engine/kmath.cpp
@@ -37,6 +37,14 @@ reg_t kRandom(EngineState *s, int argc, reg_t *argv) {
// some codes in sq4 are also random and 5 digit (if i remember correctly)
const uint16 fromNumber = argv[0].toUint16();
const uint16 toNumber = argv[1].toUint16();
+ // Some scripts may request a range in the reverse order (from largest
+ // to smallest). An example can be found in Longbow, room 710, where a
+ // random number is requested from 119 to 83. In this case, we're
+ // supposed to return toNumber (determined by the KQ5CD disasm).
+ // Fixes bug #3413020.
+ if (fromNumber > toNumber)
+ return make_reg(0, toNumber);
+
uint16 range = toNumber - fromNumber + 1;
// calculating range is exactly how sierra sci did it and is required for hoyle 4
// where we get called with kRandom(0, -1) and we are supposed to give back values from 0 to 0
diff --git a/engines/sci/engine/kmisc.cpp b/engines/sci/engine/kmisc.cpp
index e6837242e4..a32480c168 100644
--- a/engines/sci/engine/kmisc.cpp
+++ b/engines/sci/engine/kmisc.cpp
@@ -54,8 +54,33 @@ reg_t kGameIsRestarting(EngineState *s, int argc, reg_t *argv) {
uint32 neededSleep = 30;
- // WORKAROUNDS:
+ // WORKAROUNDS for scripts that are polling too quickly in scenes that
+ // are not animating much
switch (g_sci->getGameId()) {
+ case GID_CASTLEBRAIN:
+ // In Castle of Dr. Brain, memory color matching puzzle in the first
+ // room (room 100), the game scripts constantly poll the state of each
+ // stone when the user clicks on one. Since the scene is not animating
+ // much, this results in activating and deactivating each stone very
+ // quickly (together with its associated tone sound), depending on how
+ // low it is in the animate list. This worked somewhat in older PCs, but
+ // not in modern computers. We throttle the scene in order to allow the
+ // stones to display, otherwise the game scripts reset them too soon.
+ // Fixes bug #3127824.
+ if (s->currentRoomNumber() == 100) {
+ s->_throttleTrigger = true;
+ neededSleep = 60;
+ }
+ break;
+ case GID_ICEMAN:
+ // In ICEMAN the submarine control room is not animating much, so it
+ // runs way too fast. We calm it down even more, otherwise fighting
+ // against other submarines is almost impossible.
+ if (s->currentRoomNumber() == 27) {
+ s->_throttleTrigger = true;
+ neededSleep = 60;
+ }
+ break;
case GID_LSL3:
// LSL3 calculates a machinespeed variable during game startup
// (right after the filthy questions). This one would go through w/o
@@ -65,21 +90,12 @@ reg_t kGameIsRestarting(EngineState *s, int argc, reg_t *argv) {
if (s->currentRoomNumber() == 290)
s->_throttleTrigger = true;
break;
- case GID_ICEMAN:
- // In ICEMAN the submarine control room is not animating much, so it runs way too fast
- // we calm it down even more otherwise especially fighting against other submarines
- // is almost impossible
- if (s->currentRoomNumber() == 27) {
- s->_throttleTrigger = true;
- neededSleep = 60;
- }
- break;
case GID_SQ4:
// In SQ4 (floppy and CD) the sequel police appear way too quickly in
// the Skate-o-rama rooms, resulting in all sorts of timer issues, like
// #3109139 (which occurs because a police officer instantly teleports
// just before Roger exits and shoots him). We throttle these scenes a
- // bit more, in order to prevent timer bugs related to the sequel police
+ // bit more, in order to prevent timer bugs related to the sequel police.
if (s->currentRoomNumber() == 405 || s->currentRoomNumber() == 406 ||
s->currentRoomNumber() == 410 || s->currentRoomNumber() == 411) {
s->_throttleTrigger = true;
diff --git a/engines/sci/engine/kstring.cpp b/engines/sci/engine/kstring.cpp
index 783845bb76..1a9359bb26 100644
--- a/engines/sci/engine/kstring.cpp
+++ b/engines/sci/engine/kstring.cpp
@@ -201,8 +201,8 @@ reg_t kFormat(EngineState *s, int argc, reg_t *argv) {
char xfer;
int i;
int startarg;
- int str_leng = 0; /* Used for stuff like "%13s" */
- int unsigned_var = 0;
+ int strLength = 0; /* Used for stuff like "%13s" */
+ bool unsignedVar = false;
if (position.segment)
startarg = 2;
@@ -236,7 +236,7 @@ reg_t kFormat(EngineState *s, int argc, reg_t *argv) {
mode = 0;
} else {
mode = 1;
- str_leng = 0;
+ strLength = 0;
}
} else if (mode == 1) { /* xfer != '%' */
char fillchar = ' ';
@@ -256,22 +256,22 @@ reg_t kFormat(EngineState *s, int argc, reg_t *argv) {
else if (isdigit(static_cast<unsigned char>(xfer)) || (xfer == '-'))
source--; // Go to start of length argument
- str_leng = strtol(source, &destp, 10);
+ strLength = strtol(source, &destp, 10);
if (destp > source)
source = destp;
- if (str_leng < 0) {
+ if (strLength < 0) {
align = ALIGN_LEFT;
- str_leng = -str_leng;
+ strLength = -strLength;
} else if (align != ALIGN_CENTER)
align = ALIGN_RIGHT;
xfer = *source++;
} else
- str_leng = 0;
+ strLength = 0;
- assert((target - targetbuf) + str_leng + 1 <= maxsize);
+ assert((target - targetbuf) + strLength + 1 <= maxsize);
switch (xfer) {
case 's': { /* Copy string */
@@ -286,7 +286,7 @@ reg_t kFormat(EngineState *s, int argc, reg_t *argv) {
Common::String tempsource = g_sci->getKernel()->lookupText(reg,
arguments[paramindex + 1]);
int slen = strlen(tempsource.c_str());
- int extralen = str_leng - slen;
+ int extralen = strLength - slen;
assert((target - targetbuf) + extralen <= maxsize);
if (extralen < 0)
extralen = 0;
@@ -342,7 +342,7 @@ reg_t kFormat(EngineState *s, int argc, reg_t *argv) {
case 'c': { /* insert character */
assert((target - targetbuf) + 2 <= maxsize);
if (align >= 0)
- while (str_leng-- > 1)
+ while (strLength-- > 1)
*target++ = ' '; /* Format into the text */
char argchar = arguments[paramindex++];
if (argchar)
@@ -353,8 +353,14 @@ reg_t kFormat(EngineState *s, int argc, reg_t *argv) {
case 'x':
case 'u':
- unsigned_var = 1;
+ unsignedVar = true;
case 'd': { /* Copy decimal */
+ // In the new SCI2 kString function, %d is used for unsigned
+ // integers. An example is script 962 in Shivers - it uses %d
+ // to create file names.
+ if (getSciVersion() >= SCI_VERSION_2)
+ unsignedVar = true;
+
/* int templen; -- unused atm */
const char *format_string = "%d";
@@ -362,14 +368,14 @@ reg_t kFormat(EngineState *s, int argc, reg_t *argv) {
format_string = "%x";
int val = arguments[paramindex];
- if (!unsigned_var)
+ if (!unsignedVar)
val = (int16)arguments[paramindex];
target += sprintf(target, format_string, val);
paramindex++;
assert((target - targetbuf) <= maxsize);
- unsigned_var = 0;
+ unsignedVar = false;
mode = 0;
}
@@ -384,7 +390,7 @@ reg_t kFormat(EngineState *s, int argc, reg_t *argv) {
if (align) {
int written = target - writestart;
- int padding = str_leng - written;
+ int padding = strLength - written;
if (padding > 0) {
if (align > 0) {
diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp
index e43c7097ed..c30518ab42 100644
--- a/engines/sci/engine/savegame.cpp
+++ b/engines/sci/engine/savegame.cpp
@@ -626,12 +626,8 @@ void SoundCommandParser::reconstructPlayList() {
const MusicList::iterator end = _music->getPlayListEnd();
for (MusicList::iterator i = _music->getPlayListStart(); i != end; ++i) {
- if ((*i)->resourceId && _resMan->testResource(ResourceId(kResourceTypeSound, (*i)->resourceId))) {
- (*i)->soundRes = new SoundResource((*i)->resourceId, _resMan, _soundVersion);
- _music->soundInitSnd(*i);
- } else {
- (*i)->soundRes = 0;
- }
+ initSoundResource(*i);
+
if ((*i)->status == kSoundPlaying) {
// Sync the sound object's selectors related to playing with the stored
// ones in the playlist, as they may have been invalidated when loading.
diff --git a/engines/sci/engine/scriptdebug.cpp b/engines/sci/engine/scriptdebug.cpp
index 3a18fbc68f..ad3f4fb788 100644
--- a/engines/sci/engine/scriptdebug.cpp
+++ b/engines/sci/engine/scriptdebug.cpp
@@ -31,6 +31,8 @@
namespace Sci {
+//#define VM_DEBUG_SEND
+
// This table is only used for debugging. Don't include it for devices
// with not enough available memory (e.g. phones), where REDUCE_MEMORY_USAGE
// is defined
@@ -618,12 +620,13 @@ void debugSelectorCall(reg_t send_obj, Selector selector, int argc, StackPtr arg
#ifdef VM_DEBUG_SEND
debugN("Send to %04x:%04x (%s), selector %04x (%s):", PRINT_REG(send_obj),
- s->_segMan->getObjectName(send_obj), selector,
+ segMan->getObjectName(send_obj), selector,
g_sci->getKernel()->getSelectorName(selector).c_str());
#endif // VM_DEBUG_SEND
switch (selectorType) {
case kSelectorNone:
+ debugN("\n");
break;
case kSelectorVariable:
#ifdef VM_DEBUG_SEND
diff --git a/engines/sci/engine/static_selectors.cpp b/engines/sci/engine/static_selectors.cpp
index cca4c47be8..8f3337743d 100644
--- a/engines/sci/engine/static_selectors.cpp
+++ b/engines/sci/engine/static_selectors.cpp
@@ -102,18 +102,28 @@ static const char * const sci2Selectors[] = {
#endif
static const SelectorRemap sciSelectorRemap[] = {
- { SCI_VERSION_0_EARLY, SCI_VERSION_0_LATE, "moveDone", 170 },
- { SCI_VERSION_0_EARLY, SCI_VERSION_0_LATE, "points", 316 },
- { SCI_VERSION_0_EARLY, SCI_VERSION_0_LATE, "flags", 368 },
- { SCI_VERSION_1_EARLY, SCI_VERSION_1_LATE, "nodePtr", 44 },
- { SCI_VERSION_1_LATE, SCI_VERSION_1_LATE, "cantBeHere", 57 },
- { SCI_VERSION_1_EARLY, SCI_VERSION_1_LATE, "topString", 101 },
- { SCI_VERSION_1_EARLY, SCI_VERSION_1_LATE, "flags", 102 },
+ { SCI_VERSION_0_EARLY, SCI_VERSION_0_LATE, "moveDone", 170 },
+ { SCI_VERSION_0_EARLY, SCI_VERSION_0_LATE, "points", 316 },
+ { SCI_VERSION_0_EARLY, SCI_VERSION_0_LATE, "flags", 368 },
+ { SCI_VERSION_1_EARLY, SCI_VERSION_1_LATE, "nodePtr", 44 },
+ { SCI_VERSION_1_LATE, SCI_VERSION_1_LATE, "cantBeHere", 57 },
+ { SCI_VERSION_1_EARLY, SCI_VERSION_1_LATE, "topString", 101 },
+ { SCI_VERSION_1_EARLY, SCI_VERSION_1_LATE, "flags", 102 },
// SCI1.1
- { SCI_VERSION_1_1, SCI_VERSION_1_1, "nodePtr", 41 },
- { SCI_VERSION_1_1, SCI_VERSION_1_1, "cantBeHere", 54 },
- { SCI_VERSION_1_1, SCI_VERSION_2_1, "-info-",4103 },
- { SCI_VERSION_NONE, SCI_VERSION_NONE, 0, 0 }
+ { SCI_VERSION_1_1, SCI_VERSION_1_1, "nodePtr", 41 },
+ { SCI_VERSION_1_1, SCI_VERSION_1_1, "cantBeHere", 54 },
+ // The following are not really needed. They've only been defined to
+ // ease game debugging.
+ { SCI_VERSION_1_1, SCI_VERSION_2_1, "-objID-", 4096 },
+ { SCI_VERSION_1_1, SCI_VERSION_2_1, "-size-", 4097 },
+ { SCI_VERSION_1_1, SCI_VERSION_2_1, "-propDict-", 4098 },
+ { SCI_VERSION_1_1, SCI_VERSION_2_1, "-methDict-", 4099 },
+ { SCI_VERSION_1_1, SCI_VERSION_2_1, "-classScript-", 4100 },
+ { SCI_VERSION_1_1, SCI_VERSION_2_1, "-script-", 4101 },
+ { SCI_VERSION_1_1, SCI_VERSION_2_1, "-super-", 4102 },
+ //
+ { SCI_VERSION_1_1, SCI_VERSION_2_1, "-info-", 4103 },
+ { SCI_VERSION_NONE, SCI_VERSION_NONE, 0, 0 }
};
struct ClassReference {
diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp
index 274b0bbbc9..7c22b48ece 100644
--- a/engines/sci/engine/vm.cpp
+++ b/engines/sci/engine/vm.cpp
@@ -41,7 +41,6 @@ namespace Sci {
const reg_t NULL_REG = {0, 0};
const reg_t SIGNAL_REG = {0, SIGNAL_OFFSET};
const reg_t TRUE_REG = {0, 1};
-//#define VM_DEBUG_SEND
// Enable the define below to have the VM abort on cases where a conditional
// statement is followed by an unconditional jump (which will most likely lead
// to an infinite loop). Aids in detecting script bugs such as #3040722.
diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp
index 968afcb11c..ac8d5fa262 100644
--- a/engines/sci/engine/workarounds.cpp
+++ b/engines/sci/engine/workarounds.cpp
@@ -209,15 +209,9 @@ const SciWorkaroundEntry kDeviceInfo_workarounds[] = {
// gameID, room,script,lvl, object-name, method-name, call,index, workaround
const SciWorkaroundEntry kDisplay_workarounds[] = {
{ GID_ISLANDBRAIN, 300, 300, 0, "geneDude", "show", -1, 0, { WORKAROUND_IGNORE, 0 } }, // when looking at the gene explanation chart - a parameter is an object
- { GID_PQ1, 500, 500, 0, "endInter", "changeState", 0x3e8, 0, { WORKAROUND_IGNORE, 0 } }, // restoring a game at the map scene (bug #3389579)
- { GID_PQ1, 500, 500, 0, "endInter", "changeState", 0x46b, 0, { WORKAROUND_IGNORE, 0 } }, // restoring a game at the map scene (bug #3389579)
{ GID_PQ2, 23, 23, 0, "rm23Script", "elements", 0x4ae, 0, { WORKAROUND_IGNORE, 0 } }, // when looking at the 2nd page of pate's file - 0x75 as id
{ GID_PQ2, 23, 23, 0, "rm23Script", "elements", 0x4c1, 0, { WORKAROUND_IGNORE, 0 } }, // when looking at the 2nd page of pate's file - 0x75 as id (another pq2 version, bug #3043904)
{ GID_QFG1, 11, 11, 0, "battle", "<noname90>", -1, 0, { WORKAROUND_IGNORE, 0 } }, // DEMO: When entering battle, 0x75 as id
- { GID_QFG3, -1, 47, 0, "barterWin", "open", 0x1426, 0, { WORKAROUND_IGNORE, 0 } }, // sometimes when talking with a vendor that can be bartered with, the wrong local variable is checked and the variable contents are wrong - bug #3292251
- { GID_QFG3, -1, 47, 0, "barterIcon", "show", 0x135c, 0, { WORKAROUND_IGNORE, 0 } }, // sometimes when talking with a vendor that can be bartered with, the wrong local variable is checked and the variable contents are wrong - bug #3292251
- { GID_SQ1, -1, 700, 0, "arcadaRegion", "doit", -1, 0, { WORKAROUND_IGNORE, 0 } }, // restoring in some rooms of the arcada (right at the start)
- { GID_SQ1, 44, 44, 0, "spinDone", "changeState",0x13b0, 0, { WORKAROUND_IGNORE, 0 } }, // restoring a game at the slot machine in Ulence Flats (bug #3308087)
{ GID_SQ4, 397, 0, 0, "", "export 12", -1, 0, { WORKAROUND_IGNORE, 0 } }, // FLOPPY: when going into the computer store (bug #3044044)
{ GID_SQ4, 391, 391, 0, "doCatalog", "mode", 0x84, 0, { WORKAROUND_IGNORE, 0 } }, // CD: clicking on catalog in roboter sale - a parameter is an object
{ GID_SQ4, 391, 391, 0, "choosePlug", "changeState", -1, 0, { WORKAROUND_IGNORE, 0 } }, // CD: ordering connector in roboter sale - a parameter is an object
@@ -285,10 +279,7 @@ const SciWorkaroundEntry kGraphSaveBox_workarounds[] = {
// gameID, room,script,lvl, object-name, method-name, call,index, workaround
const SciWorkaroundEntry kGraphRestoreBox_workarounds[] = {
- { GID_LSL6, -1, 86, 0, "LL6Inv", "show", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens when restoring, is called with hunk segment, but hunk is not allocated at that time
- // ^^ TODO: check, if this is really a script error or an issue with our restore code
{ GID_LSL6, -1, 86, 0, "LL6Inv", "hide", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens during the game, gets called with 1 extra parameter
- { GID_SQ5, 850, 850, 0, NULL, "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens while playing Battle Cruiser (invalid segment) - bug #3056811
SCI_WORKAROUNDENTRY_TERMINATOR
};
diff --git a/engines/sci/graphics/cursor.cpp b/engines/sci/graphics/cursor.cpp
index ec49a38814..57c65aa6e0 100644
--- a/engines/sci/graphics/cursor.cpp
+++ b/engines/sci/graphics/cursor.cpp
@@ -227,8 +227,10 @@ void GfxCursor::kernelSetView(GuiResourceId viewNum, int loopNum, int celNum, Co
// Game, newPosition, validRect
static const SciCursorSetPositionWorkarounds setPositionWorkarounds[] = {
{ GID_ISLANDBRAIN, 84, 109, 46, 76, 174, 243 }, // island of dr. brain / game menu
+ { GID_ISLANDBRAIN,143, 135, 57, 102, 163, 218 },// island of dr. brain / pause menu within copy protection
{ GID_LSL5, 23, 171, 0, 0, 26, 320 }, // larry 5 / skip forward helper
{ GID_QFG1VGA, 64, 174, 40, 37, 74, 284 }, // Quest For Glory 1 VGA / run/walk/sleep sub-menu
+ { GID_QFG3, 70, 170, 40, 61, 81, 258 }, // Quest For Glory 3 / run/walk/sleep sub-menu
{ (SciGameId)0, -1, -1, -1, -1, -1, -1 }
};
diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp
index 11948d5d38..6bd310f1a0 100644
--- a/engines/sci/graphics/frameout.cpp
+++ b/engines/sci/graphics/frameout.cpp
@@ -43,6 +43,7 @@
#include "sci/graphics/paint32.h"
#include "sci/graphics/palette.h"
#include "sci/graphics/picture.h"
+#include "sci/graphics/text32.h"
#include "sci/graphics/frameout.h"
#include "sci/video/robot_decoder.h"
@@ -197,12 +198,13 @@ void GfxFrameout::kernelDeletePlane(reg_t object) {
}
}
-void GfxFrameout::addPlanePicture(reg_t object, GuiResourceId pictureId, uint16 startX) {
+void GfxFrameout::addPlanePicture(reg_t object, GuiResourceId pictureId, uint16 startX, uint16 startY) {
PlanePictureEntry newPicture;
newPicture.object = object;
newPicture.pictureId = pictureId;
newPicture.picture = new GfxPicture(_resMan, _coordAdjuster, 0, _screen, _palette, pictureId, false);
newPicture.startX = startX;
+ newPicture.startY = startY;
newPicture.pictureCels = 0;
_planePictures.push_back(newPicture);
}
@@ -274,9 +276,8 @@ int16 GfxFrameout::kernelGetHighPlanePri() {
return readSelectorValue(g_sci->getEngineState()->_segMan, _planes.back().object, SELECTOR(priority));
}
-// TODO: No idea yet how to implement this
-void GfxFrameout::kernelAddPicAt(reg_t planeObj, int16 forWidth, GuiResourceId pictureId) {
- addPlanePicture(planeObj, pictureId, forWidth);
+void GfxFrameout::kernelAddPicAt(reg_t planeObj, GuiResourceId pictureId, int16 pictureX, int16 pictureY) {
+ addPlanePicture(planeObj, pictureId, pictureX, pictureY);
}
bool sortHelper(const FrameoutEntry* entry1, const FrameoutEntry* entry2) {
@@ -316,44 +317,6 @@ void GfxFrameout::sortPlanes() {
Common::sort(_planes.begin(), _planes.end(), planeSortHelper);
}
-static int16 GetLongest(const char *text, int16 maxWidth, GfxFont *font) {
- uint16 curChar = 0;
- int16 maxChars = 0, curCharCount = 0;
- uint16 width = 0;
-
- while (width <= maxWidth) {
- curChar = (*(const byte *)text++);
-
- switch (curChar) {
- // We need to add 0xD, 0xA and 0xD 0xA to curCharCount and then exit
- // which means, we split text like
- // 'Mature, experienced software analyst available.' 0xD 0xA
- // 'Bug installation a proven speciality. "No version too clean."' (normal game text, this is from lsl2)
- // and 0xA '-------' 0xA (which is the official sierra subtitle separator)
- // Sierra did it the same way.
- case 0xD:
- // Check, if 0xA is following, if so include it as well
- if ((*(const unsigned char *)text) == 0xA)
- curCharCount++;
- // it's meant to pass through here
- case 0xA:
- curCharCount++;
- // and it's also meant to pass through here
- case 0:
- return curCharCount;
- case ' ':
- maxChars = curCharCount; // return count up to (but not including) breaking space
- break;
- }
- if (width + font->getCharWidth(curChar) > maxWidth)
- break;
- width += font->getCharWidth(curChar);
- curCharCount++;
- }
-
- return maxChars;
-}
-
void GfxFrameout::kernelFrameout() {
if (g_sci->_robotDecoder->isVideoLoaded()) {
bool skipVideo = false;
@@ -440,6 +403,7 @@ void GfxFrameout::kernelFrameout() {
picEntry->y = planePicture->getSci32celY(pictureCelNr);
picEntry->x = planePicture->getSci32celX(pictureCelNr);
picEntry->picStartX = pictureIt->startX;
+ picEntry->picStartY = pictureIt->startY;
picEntry->priority = planePicture->getSci32celPriority(pictureCelNr);
@@ -462,8 +426,9 @@ void GfxFrameout::kernelFrameout() {
itemEntry->y = ((itemEntry->y * _screen->getHeight()) / scriptsRunningHeight);
itemEntry->x = ((itemEntry->x * _screen->getWidth()) / scriptsRunningWidth);
itemEntry->picStartX = ((itemEntry->picStartX * _screen->getWidth()) / scriptsRunningWidth);
+ itemEntry->picStartY = ((itemEntry->picStartY * _screen->getHeight()) / scriptsRunningHeight);
- // Out of view
+ // Out of view horizontally (sanity checks)
int16 pictureCelStartX = itemEntry->picStartX + itemEntry->x;
int16 pictureCelEndX = pictureCelStartX + itemEntry->picture->getSci32celWidth(itemEntry->celNo);
int16 planeStartX = it->planeOffsetX;
@@ -473,6 +438,9 @@ void GfxFrameout::kernelFrameout() {
if (pictureCelStartX > planeEndX)
continue;
+ // Out of view vertically (sanity checks)
+ // TODO
+
int16 pictureOffsetX = it->planeOffsetX;
int16 pictureX = itemEntry->x;
if ((it->planeOffsetX) || (itemEntry->picStartX)) {
@@ -484,6 +452,7 @@ void GfxFrameout::kernelFrameout() {
}
}
+ // TODO: pictureOffsetY
itemEntry->picture->drawSci32Vga(itemEntry->celNo, pictureX, itemEntry->y, pictureOffsetX, it->planePictureMirrored);
// warning("picture cel %d %d", itemEntry->celNo, itemEntry->priority);
@@ -578,62 +547,25 @@ void GfxFrameout::kernelFrameout() {
}
} else {
// Most likely a text entry
- // This draws text the "SCI0-SCI11" way. In SCI2, text is prerendered in kCreateTextBitmap
- // TODO: rewrite this the "SCI2" way (i.e. implement the text buffer to draw inside kCreateTextBitmap)
if (lookupSelector(_segMan, itemEntry->object, SELECTOR(text), NULL, NULL) == kSelectorVariable) {
- reg_t stringObject = readSelector(_segMan, itemEntry->object, 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.
- if (_segMan->isHeapObject(stringObject))
- stringObject = readSelector(_segMan, stringObject, SELECTOR(data));
-
- Common::String text = _segMan->getString(stringObject);
- GfxFont *font = _cache->getFont(readSelectorValue(_segMan, itemEntry->object, SELECTOR(font)));
- bool dimmed = readSelectorValue(_segMan, itemEntry->object, SELECTOR(dimmed));
- uint16 foreColor = readSelectorValue(_segMan, itemEntry->object, SELECTOR(fore));
-
- itemEntry->y = ((itemEntry->y * _screen->getHeight()) / scriptsRunningHeight);
- itemEntry->x = ((itemEntry->x * _screen->getWidth()) / scriptsRunningWidth);
-
- uint16 startX = itemEntry->x + it->planeRect.left;
- uint16 curY = itemEntry->y + it->planeRect.top;
- const char *txt = text.c_str();
+ TextEntry *textEntry = g_sci->_gfxText32->getTextEntry(itemEntry->object);
+ uint16 startX = ((textEntry->x * _screen->getWidth()) / scriptsRunningWidth) + it->planeRect.left;
+ uint16 startY = ((textEntry->y * _screen->getHeight()) / scriptsRunningHeight) + it->planeRect.top;
// HACK. The plane sometimes doesn't contain the correct width. This
// hack breaks the dialog options when speaking with Grace, but it's
// the best we got up to now. This happens because of the unimplemented
// kTextWidth function in SCI32.
// TODO: Remove this once kTextWidth has been implemented.
uint16 w = it->planeRect.width() >= 20 ? it->planeRect.width() : _screen->getWidth() - 10;
- int16 charCount;
// Upscale the coordinates/width if the fonts are already upscaled
if (_screen->fontIsUpscaled()) {
startX = startX * _screen->getDisplayWidth() / _screen->getWidth();
- curY = curY * _screen->getDisplayHeight() / _screen->getHeight();
+ startY = startY * _screen->getDisplayHeight() / _screen->getHeight();
w = w * _screen->getDisplayWidth() / _screen->getWidth();
}
- while (*txt) {
- charCount = GetLongest(txt, w, font);
- if (charCount == 0)
- break;
-
- uint16 curX = startX;
-
- for (int i = 0; i < charCount; i++) {
- unsigned char curChar = txt[i];
- font->draw(curChar, curY, curX, foreColor, dimmed);
- curX += font->getCharWidth(curChar);
- }
-
- curY += font->getHeight();
- txt += charCount;
- while (*txt == ' ')
- txt++; // skip over breaking spaces
- }
-
+ g_sci->_gfxText32->drawTextBitmap(itemEntry->object, startX, startY, w);
}
}
}
diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h
index 7e5b1a5006..d2c8086f5f 100644
--- a/engines/sci/graphics/frameout.h
+++ b/engines/sci/graphics/frameout.h
@@ -58,6 +58,7 @@ struct FrameoutEntry {
Common::Rect celRect;
GfxPicture *picture;
int16 picStartX;
+ int16 picStartY;
};
typedef Common::List<FrameoutEntry *> FrameoutList;
@@ -65,6 +66,7 @@ typedef Common::List<FrameoutEntry *> FrameoutList;
struct PlanePictureEntry {
reg_t object;
int16 startX;
+ int16 startY;
GuiResourceId pictureId;
GfxPicture *picture;
FrameoutEntry *pictureCels; // temporary
@@ -93,10 +95,10 @@ public:
void kernelUpdateScreenItem(reg_t object);
void kernelDeleteScreenItem(reg_t object);
int16 kernelGetHighPlanePri();
- void kernelAddPicAt(reg_t planeObj, int16 forWidth, GuiResourceId pictureId);
+ void kernelAddPicAt(reg_t planeObj, GuiResourceId pictureId, int16 pictureX, int16 pictureY);
void kernelFrameout();
- void addPlanePicture(reg_t object, GuiResourceId pictureId, uint16 startX);
+ void addPlanePicture(reg_t object, GuiResourceId pictureId, uint16 startX, uint16 startY = 0);
void deletePlanePictures(reg_t object);
void clear();
diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp
index 38919593b4..5f703b90e3 100644
--- a/engines/sci/graphics/palette.cpp
+++ b/engines/sci/graphics/palette.cpp
@@ -114,7 +114,7 @@ bool GfxPalette::isMerging() {
void GfxPalette::setDefault() {
if (_resMan->getViewType() == kViewEga)
setEGA();
- else if (_resMan->getViewType() == kViewAmiga)
+ else if (_resMan->getViewType() == kViewAmiga || _resMan->getViewType() == kViewAmiga64)
setAmiga();
else
kernelSetFromResource(999, true);
@@ -206,6 +206,14 @@ bool GfxPalette::setAmiga() {
_sysPalette.colors[curColor].r = (byte1 & 0x0F) * 0x11;
_sysPalette.colors[curColor].g = ((byte2 & 0xF0) >> 4) * 0x11;
_sysPalette.colors[curColor].b = (byte2 & 0x0F) * 0x11;
+
+ if (_totalScreenColors == 64) {
+ // Set the associated color from the Amiga halfbrite colors
+ _sysPalette.colors[curColor + 32].used = 1;
+ _sysPalette.colors[curColor + 32].r = _sysPalette.colors[curColor].r >> 1;
+ _sysPalette.colors[curColor + 32].g = _sysPalette.colors[curColor].g >> 1;
+ _sysPalette.colors[curColor + 32].b = _sysPalette.colors[curColor].b >> 1;
+ }
}
// Directly set the palette, because setOnScreen() wont do a thing for amiga
@@ -226,6 +234,13 @@ void GfxPalette::modifyAmigaPalette(byte *data) {
_sysPalette.colors[curColor].r = (byte1 & 0x0F) * 0x11;
_sysPalette.colors[curColor].g = ((byte2 & 0xF0) >> 4) * 0x11;
_sysPalette.colors[curColor].b = (byte2 & 0x0F) * 0x11;
+
+ if (_totalScreenColors == 64) {
+ // Set the associated color from the Amiga halfbrite colors
+ _sysPalette.colors[curColor + 32].r = _sysPalette.colors[curColor].r >> 1;
+ _sysPalette.colors[curColor + 32].g = _sysPalette.colors[curColor].g >> 1;
+ _sysPalette.colors[curColor + 32].b = _sysPalette.colors[curColor].b >> 1;
+ }
}
copySysPaletteToScreen();
diff --git a/engines/sci/graphics/picture.cpp b/engines/sci/graphics/picture.cpp
index ecb54e89e8..dad2b77036 100644
--- a/engines/sci/graphics/picture.cpp
+++ b/engines/sci/graphics/picture.cpp
@@ -740,7 +740,7 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
// Left-Over VGA palette, we simply ignore it
curPos += 256 + 4 + 1024;
} else {
- // Setting half of the amiga palette
+ // Setting half of the Amiga palette
_palette->modifyAmigaPalette(&data[curPos]);
curPos += 32;
}
diff --git a/engines/sci/graphics/text32.cpp b/engines/sci/graphics/text32.cpp
new file mode 100644
index 0000000000..21372f1502
--- /dev/null
+++ b/engines/sci/graphics/text32.cpp
@@ -0,0 +1,238 @@
+/* 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.
+ *
+ * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/engines/sci/graphics/text16.cpp $
+ * $Id: text16.cpp 55178 2011-01-08 23:16:44Z thebluegr $
+ *
+ */
+
+#include "common/util.h"
+#include "common/stack.h"
+#include "graphics/primitives.h"
+
+#include "sci/sci.h"
+#include "sci/engine/kernel.h"
+#include "sci/engine/selector.h"
+#include "sci/engine/state.h"
+#include "sci/graphics/cache.h"
+#include "sci/graphics/font.h"
+#include "sci/graphics/screen.h"
+#include "sci/graphics/text32.h"
+
+namespace Sci {
+
+GfxText32::GfxText32(SegManager *segMan, GfxCache *fonts, GfxScreen *screen)
+ : _segMan(segMan), _cache(fonts), _screen(screen) {
+}
+
+GfxText32::~GfxText32() {
+ purgeCache();
+}
+
+void GfxText32::purgeCache() {
+ for (TextCache::iterator cacheIterator = _textCache.begin(); cacheIterator != _textCache.end(); cacheIterator++) {
+ delete[] cacheIterator->_value->surface;
+ delete cacheIterator->_value;
+ cacheIterator->_value = 0;
+ }
+
+ _textCache.clear();
+}
+
+void GfxText32::createTextBitmap(reg_t textObject) {
+ if (_textCache.size() >= MAX_CACHED_TEXTS)
+ purgeCache();
+
+ uint32 textId = (textObject.segment << 16) | textObject.offset;
+
+ if (_textCache.contains(textId)) {
+ // Delete the old entry
+ TextEntry *oldEntry = _textCache[textId];
+ delete[] oldEntry->surface;
+ delete oldEntry;
+ _textCache.erase(textId);
+ }
+
+ _textCache[textId] = createTextEntry(textObject);
+}
+
+// TODO: Finish this!
+void GfxText32::drawTextBitmap(reg_t textObject, uint16 textX, uint16 textY, uint16 w) {
+ uint32 textId = (textObject.segment << 16) | textObject.offset;
+
+ if (!_textCache.contains(textId))
+ createTextBitmap(textObject);
+
+ TextEntry *entry = _textCache[textId];
+
+ // This draws text the "SCI0-SCI11" way. In SCI2, text is prerendered in kCreateTextBitmap
+ // TODO: rewrite this the "SCI2" way (i.e. implement the text buffer to draw inside kCreateTextBitmap)
+ GfxFont *font = _cache->getFont(readSelectorValue(_segMan, textObject, SELECTOR(font)));
+ bool dimmed = readSelectorValue(_segMan,textObject, SELECTOR(dimmed));
+ uint16 foreColor = readSelectorValue(_segMan, textObject, SELECTOR(fore));
+
+ const char *txt = entry->text.c_str();
+ int16 charCount;
+
+ while (*txt) {
+ charCount = GetLongest(txt, w, font);
+ if (charCount == 0)
+ break;
+
+ uint16 curX = textX;
+
+ for (int i = 0; i < charCount; i++) {
+ unsigned char curChar = txt[i];
+ font->draw(curChar, textY, curX, foreColor, dimmed);
+ curX += font->getCharWidth(curChar);
+ }
+
+ textY += font->getHeight();
+ txt += charCount;
+ while (*txt == ' ')
+ txt++; // skip over breaking spaces
+ }
+
+ // TODO: The "SCI2" way of font drawing. Currently buggy
+ /*
+ for (int x = textX; x < entry->width; x++) {
+ for (int y = textY; y < entry->height; y++) {
+ byte pixel = entry->surface[y * entry->width + x];
+ if (pixel)
+ _screen->putPixel(x, y, 1, pixel, 0, 0);
+ }
+ }
+ */
+}
+
+TextEntry *GfxText32::getTextEntry(reg_t textObject) {
+ uint32 textId = (textObject.segment << 16) | textObject.offset;
+
+ if (!_textCache.contains(textId))
+ createTextBitmap(textObject);
+
+ return _textCache[textId];
+}
+
+// TODO: Finish this! Currently buggy.
+TextEntry *GfxText32::createTextEntry(reg_t textObject) {
+ 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.
+ if (_segMan->isHeapObject(stringObject))
+ stringObject = readSelector(_segMan, stringObject, SELECTOR(data));
+
+ const char *text = _segMan->getString(stringObject).c_str();
+ GfxFont *font = _cache->getFont(readSelectorValue(_segMan, textObject, SELECTOR(font)));
+ bool dimmed = readSelectorValue(_segMan, textObject, SELECTOR(dimmed));
+ uint16 foreColor = readSelectorValue(_segMan, textObject, SELECTOR(fore));
+ uint16 x = readSelectorValue(_segMan, textObject, SELECTOR(x));
+ uint16 y = readSelectorValue(_segMan, textObject, SELECTOR(y));
+
+ // Now get the bounding box from the associated plane
+ reg_t planeObject = readSelector(_segMan, textObject, SELECTOR(plane));
+ Common::Rect planeRect;
+ if (!planeObject.isNull()) {
+ planeRect.top = readSelectorValue(_segMan, planeObject, SELECTOR(top));
+ planeRect.left = readSelectorValue(_segMan, planeObject, SELECTOR(left));
+ planeRect.bottom = readSelectorValue(_segMan, planeObject, SELECTOR(bottom)) + 1;
+ planeRect.right = readSelectorValue(_segMan, planeObject, SELECTOR(right)) + 1;
+ } else {
+ planeRect.top = 0;
+ planeRect.left = 0;
+ planeRect.bottom = _screen->getHeight();
+ planeRect.right = _screen->getWidth();
+ }
+
+ TextEntry *newEntry = new TextEntry();
+ newEntry->object = stringObject;
+ newEntry->x = x;
+ newEntry->y = y;
+ newEntry->width = planeRect.width();
+ newEntry->height = planeRect.height();
+ newEntry->surface = new byte[newEntry->width * newEntry->height];
+ memset(newEntry->surface, 0, newEntry->width * newEntry->height);
+ newEntry->text = _segMan->getString(stringObject);
+
+ int16 maxTextWidth, charCount;
+ uint16 curX = 0, curY = 0;
+
+ maxTextWidth = 0;
+ while (*text) {
+ charCount = GetLongest(text, planeRect.width(), font);
+ if (charCount == 0)
+ break;
+
+ for (int i = 0; i < charCount; i++) {
+ unsigned char curChar = text[i];
+ font->drawToBuffer(curChar, curY, curX, foreColor, dimmed, newEntry->surface, newEntry->width, newEntry->height);
+ curX += font->getCharWidth(curChar);
+ }
+
+ curY += font->getHeight();
+ text += charCount;
+ while (*text == ' ')
+ text++; // skip over breaking spaces
+ }
+
+ return newEntry;
+}
+
+int16 GfxText32::GetLongest(const char *text, int16 maxWidth, GfxFont *font) {
+ uint16 curChar = 0;
+ int16 maxChars = 0, curCharCount = 0;
+ uint16 width = 0;
+
+ while (width <= maxWidth) {
+ curChar = (*(const byte *)text++);
+
+ switch (curChar) {
+ // We need to add 0xD, 0xA and 0xD 0xA to curCharCount and then exit
+ // which means, we split text like
+ // 'Mature, experienced software analyst available.' 0xD 0xA
+ // 'Bug installation a proven speciality. "No version too clean."' (normal game text, this is from lsl2)
+ // and 0xA '-------' 0xA (which is the official sierra subtitle separator)
+ // Sierra did it the same way.
+ case 0xD:
+ // Check, if 0xA is following, if so include it as well
+ if ((*(const unsigned char *)text) == 0xA)
+ curCharCount++;
+ // it's meant to pass through here
+ case 0xA:
+ curCharCount++;
+ // and it's also meant to pass through here
+ case 0:
+ return curCharCount;
+ case ' ':
+ maxChars = curCharCount; // return count up to (but not including) breaking space
+ break;
+ }
+ if (width + font->getCharWidth(curChar) > maxWidth)
+ break;
+ width += font->getCharWidth(curChar);
+ curCharCount++;
+ }
+
+ return maxChars;
+}
+
+} // End of namespace Sci
diff --git a/engines/sci/graphics/text32.h b/engines/sci/graphics/text32.h
new file mode 100644
index 0000000000..39fe710a86
--- /dev/null
+++ b/engines/sci/graphics/text32.h
@@ -0,0 +1,71 @@
+/* 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.
+ *
+ * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/engines/sci/graphics/text16.h $
+ * $Id: text16.h 55178 2011-01-08 23:16:44Z thebluegr $
+ *
+ */
+
+#ifndef SCI_GRAPHICS_TEXT32_H
+#define SCI_GRAPHICS_TEXT32_H
+
+#include "common/hashmap.h"
+
+namespace Sci {
+
+struct TextEntry {
+ reg_t object;
+ uint16 x;
+ uint16 y;
+ uint16 width;
+ uint16 height;
+ byte *surface;
+ Common::String text;
+};
+
+// TODO: Move to Cache, perhaps?
+#define MAX_CACHED_TEXTS 20
+typedef Common::HashMap<uint32, TextEntry *> TextCache;
+
+/**
+ * Text32 class, handles text calculation and displaying of text for SCI2, SCI21 and SCI3 games
+ */
+class GfxText32 {
+public:
+ GfxText32(SegManager *segMan, GfxCache *fonts, GfxScreen *screen);
+ ~GfxText32();
+ void createTextBitmap(reg_t textObject);
+ void drawTextBitmap(reg_t textObject, uint16 textX, uint16 textY, uint16 w);
+ int16 GetLongest(const char *text, int16 maxWidth, GfxFont *font);
+ TextEntry *getTextEntry(reg_t textObject);
+
+private:
+ TextEntry *createTextEntry(reg_t textObject);
+ void purgeCache();
+
+ SegManager *_segMan;
+ GfxCache *_cache;
+ TextCache _textCache;
+ GfxScreen *_screen;
+};
+
+} // End of namespace Sci
+
+#endif
diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp
index 6ca4903e17..a0d5b51a2b 100644
--- a/engines/sci/graphics/view.cpp
+++ b/engines/sci/graphics/view.cpp
@@ -471,7 +471,8 @@ void unpackCelData(byte *inBuffer, byte *celBitmap, byte clearColor, int pixelCo
curByte = *rlePtr++;
if (curByte & 0xC0) { // fill with color
runLength = curByte >> 6;
- memset(outPtr + pixelNr, curByte & 0x3F, MIN<uint16>(runLength, pixelCount - pixelNr));
+ curByte = curByte & 0x3F;
+ memset(outPtr + pixelNr, curByte, MIN<uint16>(runLength, pixelCount - pixelNr));
} else { // skip the next pixels (transparency)
runLength = curByte & 0x3F;
}
diff --git a/engines/sci/module.mk b/engines/sci/module.mk
index 2202002e2e..c129ae5439 100644
--- a/engines/sci/module.mk
+++ b/engines/sci/module.mk
@@ -80,6 +80,7 @@ ifdef ENABLE_SCI32
MODULE_OBJS += \
graphics/frameout.o \
graphics/paint32.o \
+ graphics/text32.o \
video/robot_decoder.o
endif
diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp
index 1b8b7b2f0e..bdd7d6692b 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 *sci_error_types[] = {
+static const char *const sci_error_types[] = {
"No error",
"I/O error",
"Resource is empty (size 0)",
@@ -107,7 +107,7 @@ static const char *sci_error_types[] = {
"SCI version is unsupported"
};
-static const char *s_resourceTypeNames[] = {
+static const char *const s_resourceTypeNames[] = {
"view", "pic", "script", "text", "sound",
"memory", "vocab", "font", "cursor",
"patch", "bitmap", "palette", "cdaudio",
@@ -120,7 +120,7 @@ static const char *s_resourceTypeNames[] = {
// Resource type suffixes. Note that the
// suffic of SCI3 scripts has been changed from
// scr to csc
-static const char *s_resourceTypeSuffixes[] = {
+static const char *const s_resourceTypeSuffixes[] = {
"v56", "p56", "scr", "tex", "snd",
"", "voc", "fon", "cur", "pat",
"bit", "pal", "cda", "aud", "syn",
@@ -1158,8 +1158,10 @@ ResVersion ResourceManager::detectMapVersion() {
}
}
- if (!fileStream)
- error("Failed to open resource map file");
+ if (!fileStream) {
+ warning("Failed to open resource map file");
+ return kResVersionUnknown;
+ }
// detection
// SCI0 and SCI01 maps have last 6 bytes set to FF
@@ -1259,7 +1261,7 @@ ResVersion ResourceManager::detectVolVersion() {
}
if (!fileStream) {
- error("Failed to open volume file - if you got resource.p01/resource.p02/etc. files, merge them together into resource.000");
+ warning("Failed to open volume file - if you got resource.p01/resource.p02/etc. files, merge them together into resource.000");
// resource.p01/resource.p02/etc. may be there when directly copying the files from the original floppies
// the sierra installer would merge those together (perhaps we could do this as well?)
// possible TODO
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index 792b2b2055..6c1b6e4dd6 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -61,6 +61,7 @@
#include "sci/graphics/transitions.h"
#ifdef ENABLE_SCI32
+#include "sci/graphics/text32.h"
#include "sci/graphics/frameout.h"
#include "sci/video/robot_decoder.h"
#endif
@@ -146,6 +147,7 @@ SciEngine::~SciEngine() {
DebugMan.clearAllDebugChannels();
#ifdef ENABLE_SCI32
+ delete _gfxText32;
delete _robotDecoder;
delete _gfxFrameout;
#endif
@@ -598,6 +600,7 @@ void SciEngine::initGraphics() {
_gfxText16 = 0;
_gfxTransitions = 0;
#ifdef ENABLE_SCI32
+ _gfxText32 = 0;
_robotDecoder = 0;
_gfxFrameout = 0;
_gfxPaint32 = 0;
@@ -627,6 +630,7 @@ void SciEngine::initGraphics() {
_gfxCompare = new GfxCompare(_gamestate->_segMan, _kernel, _gfxCache, _gfxScreen, _gfxCoordAdjuster);
_gfxPaint32 = new GfxPaint32(_resMan, _gamestate->_segMan, _kernel, _gfxCoordAdjuster, _gfxCache, _gfxScreen, _gfxPalette);
_gfxPaint = _gfxPaint32;
+ _gfxText32 = new GfxText32(_gamestate->_segMan, _gfxCache, _gfxScreen);
_robotDecoder = new RobotDecoder(g_system->getMixer(), getPlatform() == Common::kPlatformMacintosh);
_gfxFrameout = new GfxFrameout(_gamestate->_segMan, _resMan, _gfxCoordAdjuster, _gfxCache, _gfxScreen, _gfxPalette, _gfxPaint32);
} else {
diff --git a/engines/sci/sci.h b/engines/sci/sci.h
index b419d862a4..81bbdc51de 100644
--- a/engines/sci/sci.h
+++ b/engines/sci/sci.h
@@ -71,6 +71,7 @@ class GfxPalette;
class GfxPorts;
class GfxScreen;
class GfxText16;
+class GfxText32;
class GfxTransitions;
#ifdef ENABLE_SCI32
@@ -313,6 +314,7 @@ public:
GfxPorts *_gfxPorts; // Port managment for 16-bit gfx
GfxScreen *_gfxScreen;
GfxText16 *_gfxText16;
+ GfxText32 *_gfxText32;
GfxTransitions *_gfxTransitions; // transitions between screens for 16-bit gfx
GfxMacIconBar *_gfxMacIconBar; // Mac Icon Bar manager
diff --git a/engines/sci/sound/drivers/gm_names.h b/engines/sci/sound/drivers/gm_names.h
index bfe5ff88c7..fbfa413a4a 100644
--- a/engines/sci/sound/drivers/gm_names.h
+++ b/engines/sci/sound/drivers/gm_names.h
@@ -30,7 +30,7 @@ namespace Sci {
// is defined
#ifndef REDUCE_MEMORY_USAGE
-static const char *GmInstrumentNames[] = {
+static const char *const GmInstrumentNames[] = {
/*000*/ "Acoustic Grand Piano",
/*001*/ "Bright Acoustic Piano",
/*002*/ "Electric Grand Piano",
@@ -162,7 +162,7 @@ static const char *GmInstrumentNames[] = {
};
// The GM Percussion map is downwards compatible to the MT32 map, which is used in SCI
-static const char *GmPercussionNames[] = {
+static const char *const GmPercussionNames[] = {
/*00*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/*10*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/*20*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
diff --git a/engines/sci/sound/midiparser_sci.cpp b/engines/sci/sound/midiparser_sci.cpp
index f48a68dc66..ad7ba7ca36 100644
--- a/engines/sci/sound/midiparser_sci.cpp
+++ b/engines/sci/sound/midiparser_sci.cpp
@@ -480,11 +480,18 @@ void MidiParser_SCI::parseNextEvent(EventInfo &info) {
info.basic.param2 = 0;
if (info.channel() == 0xF) {// SCI special case
if (info.basic.param1 != kSetSignalLoop) {
- // at least in kq5/french&mac the first scene in the intro has a song that sets signal to 4 immediately
- // on tick 0. Signal isn't set at that point by sierra sci and it would cause the castle daventry text to
- // get immediately removed, so we currently filter it.
- // Sierra SCI ignores them as well at that time
- if ((_position._play_tick) || (info.delta)) {
+ // At least in kq5/french&mac the first scene in the intro has
+ // a song that sets signal to 4 immediately on tick 0. Signal
+ // isn't set at that point by sierra sci and it would cause the
+ // castle daventry text to get immediately removed, so we
+ // currently filter it. Sierra SCI ignores them as well at that
+ // time. However, this filtering should only be performed for
+ // SCI1 and newer games. Signalling is done differently in SCI0
+ // though, so ignoring these signals in SCI0 games will result
+ // 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) {
_signalSet = true;
_signalToSet = info.basic.param1;
}
diff --git a/engines/sci/sound/music.cpp b/engines/sci/sound/music.cpp
index 2afab3858d..9610b6f847 100644
--- a/engines/sci/sound/music.cpp
+++ b/engines/sci/sound/music.cpp
@@ -63,12 +63,13 @@ void SciMusic::init() {
// SCI sound init
_dwTempo = 0;
- // Default to MIDI in SCI2.1+ games, as many don't have AdLib support.
Common::Platform platform = g_sci->getPlatform();
-
uint32 deviceFlags = MDT_PCSPK | MDT_PCJR | MDT_ADLIB | MDT_MIDI;
- if (getSciVersion() >= SCI_VERSION_2_1)
+ // Default to MIDI in SCI2.1+ games, as many don't have AdLib support.
+ // Also, default to MIDI for Windows versions of SCI1.1 games, as their
+ // soundtrack is written for GM.
+ if (getSciVersion() >= SCI_VERSION_2_1 || g_sci->_features->useAltWinGMSound())
deviceFlags |= MDT_PREFER_GM;
// Currently our CMS implementation only supports SCI1(.1)
diff --git a/engines/sci/sound/soundcmd.cpp b/engines/sci/sound/soundcmd.cpp
index d4cff7614c..a91b103214 100644
--- a/engines/sci/sound/soundcmd.cpp
+++ b/engines/sci/sound/soundcmd.cpp
@@ -37,6 +37,7 @@ SoundCommandParser::SoundCommandParser(ResourceManager *resMan, SegManager *segM
_music = new SciMusic(_soundVersion);
_music->init();
+ _bMultiMidi = ConfMan.getBool("multi_midi");
}
SoundCommandParser::~SoundCommandParser() {
@@ -63,6 +64,35 @@ int SoundCommandParser::getSoundResourceId(reg_t obj) {
return resourceId;
}
+void SoundCommandParser::initSoundResource(MusicEntry *newSound) {
+ if (newSound->resourceId && _resMan->testResource(ResourceId(kResourceTypeSound, newSound->resourceId)))
+ newSound->soundRes = new SoundResource(newSound->resourceId, _resMan, _soundVersion);
+ else
+ newSound->soundRes = 0;
+
+ // In SCI1.1 games, sound effects are started from here. If we can find
+ // a relevant audio resource, play it, otherwise switch to synthesized
+ // effects. If the resource exists, play it using map 65535 (sound
+ // effects map)
+ bool checkAudioResource = getSciVersion() >= SCI_VERSION_1_1;
+ if (g_sci->getGameId() == GID_HOYLE4)
+ checkAudioResource = false; // hoyle 4 has garbled audio resources in place of the sound resources
+ // if we play those, we will only make the user deaf and break speakers. Sierra SCI doesn't play anything
+ // on soundblaster. FIXME: check, why this is
+
+ if (checkAudioResource && _resMan->testResource(ResourceId(kResourceTypeAudio, newSound->resourceId))) {
+ // Found a relevant audio resource, create an audio stream
+ if (_bMultiMidi || !newSound->soundRes) {
+ int sampleLen;
+ newSound->pStreamAud = _audio->getAudioStream(newSound->resourceId, 65535, &sampleLen);
+ newSound->soundType = Audio::Mixer::kSpeechSoundType;
+ }
+ }
+
+ if (!newSound->pStreamAud && newSound->soundRes)
+ _music->soundInitSnd(newSound);
+}
+
void SoundCommandParser::processInitSound(reg_t obj) {
int resourceId = getSoundResourceId(obj);
@@ -73,11 +103,6 @@ void SoundCommandParser::processInitSound(reg_t obj) {
MusicEntry *newSound = new MusicEntry();
newSound->resourceId = resourceId;
- if (resourceId && _resMan->testResource(ResourceId(kResourceTypeSound, resourceId)))
- newSound->soundRes = new SoundResource(resourceId, _resMan, _soundVersion);
- else
- newSound->soundRes = 0;
-
newSound->soundObj = obj;
newSound->loop = readSelectorValue(_segMan, obj, SELECTOR(loop));
newSound->priority = readSelectorValue(_segMan, obj, SELECTOR(pri)) & 0xFF;
@@ -88,25 +113,7 @@ void SoundCommandParser::processInitSound(reg_t obj) {
debugC(kDebugLevelSound, "kDoSound(init): %04x:%04x number %d, loop %d, prio %d, vol %d", PRINT_REG(obj),
resourceId, newSound->loop, newSound->priority, newSound->volume);
- // In SCI1.1 games, sound effects are started from here. If we can find
- // a relevant audio resource, play it, otherwise switch to synthesized
- // effects. If the resource exists, play it using map 65535 (sound
- // effects map)
- bool checkAudioResource = getSciVersion() >= SCI_VERSION_1_1;
- if (g_sci->getGameId() == GID_HOYLE4)
- checkAudioResource = false; // hoyle 4 has garbled audio resources in place of the sound resources
- // if we play those, we will only make the user deaf and break speakers. Sierra SCI doesn't play anything
- // on soundblaster. FIXME: check, why this is
-
- if (checkAudioResource && _resMan->testResource(ResourceId(kResourceTypeAudio, resourceId))) {
- // Found a relevant audio resource, play it
- int sampleLen;
- newSound->pStreamAud = _audio->getAudioStream(resourceId, 65535, &sampleLen);
- newSound->soundType = Audio::Mixer::kSpeechSoundType;
- } else {
- if (newSound->soundRes)
- _music->soundInitSnd(newSound);
- }
+ initSoundResource(newSound);
_music->pushBackSlot(newSound);
@@ -130,8 +137,14 @@ reg_t SoundCommandParser::kDoSoundPlay(int argc, reg_t *argv, reg_t acc) {
void SoundCommandParser::processPlaySound(reg_t obj) {
MusicEntry *musicSlot = _music->getSlot(obj);
if (!musicSlot) {
- warning("kDoSound(play): Slot not found (%04x:%04x)", PRINT_REG(obj));
- return;
+ warning("kDoSound(play): Slot not found (%04x:%04x), initializing it manually", PRINT_REG(obj));
+ // The sound hasn't been initialized for some reason, so initialize it here.
+ // Happens in KQ6, room 460, when giving the creature to the bookwork (the
+ // bookworm's child). Fixes bug #3413301.
+ processInitSound(obj);
+ musicSlot = _music->getSlot(obj);
+ if (!musicSlot)
+ error("Failed to initialize uninitialized sound slot");
}
int resourceId = getSoundResourceId(obj);
@@ -157,6 +170,9 @@ void SoundCommandParser::processPlaySound(reg_t obj) {
musicSlot->loop = readSelectorValue(_segMan, obj, SELECTOR(loop));
musicSlot->priority = readSelectorValue(_segMan, obj, SELECTOR(priority));
+ // Reset hold when starting a new song. kDoSoundSetHold is always called after
+ // kDoSoundPlay to set it properly, if needed. Fixes bug #3413589.
+ musicSlot->hold = -1;
if (_soundVersion >= SCI_VERSION_1_EARLY)
musicSlot->volume = readSelectorValue(_segMan, obj, SELECTOR(vol));
diff --git a/engines/sci/sound/soundcmd.h b/engines/sci/sound/soundcmd.h
index 7f6e2a0fe8..c1dce014d2 100644
--- a/engines/sci/sound/soundcmd.h
+++ b/engines/sci/sound/soundcmd.h
@@ -32,6 +32,7 @@ namespace Sci {
class Console;
class SciMusic;
class SoundCommandParser;
+class MusicEntry;
//typedef void (SoundCommandParser::*SoundCommand)(reg_t obj, int16 value);
//struct MusicEntryCommand {
@@ -64,6 +65,7 @@ public:
void processPlaySound(reg_t obj);
void processStopSound(reg_t obj, bool sampleFinishedPlaying);
+ void initSoundResource(MusicEntry *newSound);
MusicType getMusicType() const;
@@ -109,6 +111,7 @@ private:
SciMusic *_music;
AudioPlayer *_audio;
SciVersion _soundVersion;
+ bool _bMultiMidi;
void processInitSound(reg_t obj);
void processDisposeSound(reg_t obj);