diff options
author | Matthew Hoops | 2011-10-07 14:34:22 -0400 |
---|---|---|
committer | Matthew Hoops | 2011-10-07 14:34:22 -0400 |
commit | e1dc4db7aa53d1bbc4cdb03d1163c97d049702f5 (patch) | |
tree | 9d57b5fdd6737bc3449851a87e573abe7be984a6 /engines/sci | |
parent | deab5b28753155863062746ef1239535f562fd0b (diff) | |
parent | 842b471e45ae8b7c1b4516b9bd5bf39d61112077 (diff) | |
download | scummvm-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')
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); |