diff options
Diffstat (limited to 'engines/sci')
40 files changed, 1499 insertions, 684 deletions
diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp index 6cd072ee8b..a092e0676d 100644 --- a/engines/sci/console.cpp +++ b/engines/sci/console.cpp @@ -141,6 +141,8 @@ Console::Console(SciEngine *engine) : GUI::Debugger(), registerCmd("vpl", WRAP_METHOD(Console, cmdVisiblePlaneList)); // alias registerCmd("plane_items", WRAP_METHOD(Console, cmdPlaneItemList)); registerCmd("pi", WRAP_METHOD(Console, cmdPlaneItemList)); // alias + registerCmd("visible_plane_items", WRAP_METHOD(Console, cmdVisiblePlaneItemList)); + registerCmd("vpi", WRAP_METHOD(Console, cmdVisiblePlaneItemList)); // alias registerCmd("saved_bits", WRAP_METHOD(Console, cmdSavedBits)); registerCmd("show_saved_bits", WRAP_METHOD(Console, cmdShowSavedBits)); // Segments @@ -386,6 +388,7 @@ bool Console::cmdHelp(int argc, const char **argv) { debugPrintf(" plane_list / pl - Shows a list of all the planes in the draw list (SCI2+)\n"); debugPrintf(" visible_plane_list / vpl - Shows a list of all the planes in the visible draw list (SCI2+)\n"); debugPrintf(" plane_items / pi - Shows a list of all items for a plane (SCI2+)\n"); + debugPrintf(" visible_plane_items / vpi - Shows a list of all items for a plane in the visible draw list (SCI2+)\n"); debugPrintf(" saved_bits - List saved bits on the hunk\n"); debugPrintf(" show_saved_bits - Display saved bits\n"); debugPrintf("\n"); @@ -1817,6 +1820,34 @@ bool Console::cmdPlaneItemList(int argc, const char **argv) { return true; } +bool Console::cmdVisiblePlaneItemList(int argc, const char **argv) { + if (argc != 2) { + debugPrintf("Shows the list of items for a plane\n"); + debugPrintf("Usage: %s <plane address>\n", argv[0]); + return true; + } + + reg_t planeObject = NULL_REG; + + if (parse_reg_t(_engine->_gamestate, argv[1], &planeObject, false)) { + debugPrintf("Invalid address passed.\n"); + debugPrintf("Check the \"addresses\" command on how to use addresses\n"); + return true; + } + +#ifdef ENABLE_SCI32 + if (_engine->_gfxFrameout) { + debugPrintf("Visible plane item list:\n"); + _engine->_gfxFrameout->printVisiblePlaneItemList(this, planeObject); + } else { + debugPrintf("This SCI version does not have a list of plane items\n"); + } +#else + debugPrintf("SCI32 isn't included in this compiled executable\n"); +#endif + return true; +} + bool Console::cmdSavedBits(int argc, const char **argv) { SegManager *segman = _engine->_gamestate->_segMan; SegmentId id = segman->findSegmentByType(SEG_TYPE_HUNK); diff --git a/engines/sci/console.h b/engines/sci/console.h index b20f1f7251..cf85def950 100644 --- a/engines/sci/console.h +++ b/engines/sci/console.h @@ -98,6 +98,7 @@ private: bool cmdPlaneList(int argc, const char **argv); bool cmdVisiblePlaneList(int argc, const char **argv); bool cmdPlaneItemList(int argc, const char **argv); + bool cmdVisiblePlaneItemList(int argc, const char **argv); bool cmdSavedBits(int argc, const char **argv); bool cmdShowSavedBits(int argc, const char **argv); // Segments diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp index 985e12a7f6..c920ef10e2 100644 --- a/engines/sci/detection.cpp +++ b/engines/sci/detection.cpp @@ -98,8 +98,11 @@ static const PlainGameDescriptor s_sciGameTitles[] = { {"lsl6", "Leisure Suit Larry 6: Shape Up or Slip Out!"}, {"pepper", "Pepper's Adventure in Time"}, {"slater", "Slater & Charlie Go Camping"}, + {"gk1demo", "Gabriel Knight: Sins of the Fathers"}, + {"qfg4demo", "Quest for Glory IV: Shadows of Darkness"}, + {"pq4demo", "Police Quest IV: Open Season"}, // === SCI2 games ========================================================= - {"gk1", "Gabriel Knight: Sins of the Fathers"}, // demo is SCI11, full version SCI32 + {"gk1", "Gabriel Knight: Sins of the Fathers"}, {"pq4", "Police Quest IV: Open Season"}, // floppy is SCI2, CD SCI2.1 {"qfg4", "Quest for Glory IV: Shadows of Darkness"}, // floppy is SCI2, CD SCI2.1 // === SCI2.1 games ======================================================== @@ -146,6 +149,7 @@ static const GameIdStrToEnum s_gameIdStrToEnum[] = { { "fairytales", GID_FAIRYTALES }, { "freddypharkas", GID_FREDDYPHARKAS }, { "funseeker", GID_FUNSEEKER }, + { "gk1demo", GID_GK1DEMO }, { "gk1", GID_GK1 }, { "gk2", GID_GK2 }, { "hoyle1", GID_HOYLE1 }, @@ -183,12 +187,14 @@ static const GameIdStrToEnum s_gameIdStrToEnum[] = { { "pq2", GID_PQ2 }, { "pq3", GID_PQ3 }, { "pq4", GID_PQ4 }, + { "pq4demo", GID_PQ4DEMO }, { "pqswat", GID_PQSWAT }, { "qfg1", GID_QFG1 }, { "qfg1vga", GID_QFG1VGA }, { "qfg2", GID_QFG2 }, { "qfg3", GID_QFG3 }, { "qfg4", GID_QFG4 }, + { "qfg4demo", GID_QFG4DEMO }, { "rama", GID_RAMA }, { "sci-fanmade", GID_FANMADE }, // FIXME: Do we really need/want this? { "shivers", GID_SHIVERS }, @@ -356,7 +362,7 @@ Common::String convertSierraGameId(Common::String sierraId, uint32 *gameFlags, R // qfg4 demo has less than 50 scripts if (resources.size() < 50) - return "qfg4"; + return "qfg4demo"; // Otherwise it's qfg3 return "qfg3"; @@ -473,7 +479,7 @@ static char s_fallbackGameIdBuf[256]; class SciMetaEngine : public AdvancedMetaEngine { public: SciMetaEngine() : AdvancedMetaEngine(Sci::SciGameDescriptions, sizeof(ADGameDescription), s_sciGameTitles, optionsList) { - _singleid = "sci"; + _singleId = "sci"; } virtual const char *getName() const { @@ -526,8 +532,8 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles, s_fallbackDesc.language = Common::EN_ANY; s_fallbackDesc.flags = ADGF_NO_FLAGS; s_fallbackDesc.platform = Common::kPlatformDOS; // default to PC platform - s_fallbackDesc.gameid = "sci"; - s_fallbackDesc.guioptions = GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI); + s_fallbackDesc.gameId = "sci"; + s_fallbackDesc.guiOptions = GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI); if (allFiles.contains("resource.map") || allFiles.contains("Data1") || allFiles.contains("resmap.001") || allFiles.contains("resmap.001")) { @@ -610,7 +616,7 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles, Common::String gameId = convertSierraGameId(sierraGameId, &s_fallbackDesc.flags, resMan); strncpy(s_fallbackGameIdBuf, gameId.c_str(), sizeof(s_fallbackGameIdBuf) - 1); s_fallbackGameIdBuf[sizeof(s_fallbackGameIdBuf) - 1] = 0; // Make sure string is NULL terminated - s_fallbackDesc.gameid = s_fallbackGameIdBuf; + s_fallbackDesc.gameId = s_fallbackGameIdBuf; // Try to determine the game language // Load up text 0 and start looking for "#" characters @@ -653,7 +659,7 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles, const bool isCD = (s_fallbackDesc.flags & ADGF_CD); if (!isCD) - s_fallbackDesc.guioptions = GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI); + s_fallbackDesc.guiOptions = GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI); if (gameId.hasSuffix("sci")) { s_fallbackDesc.extra = "SCI"; @@ -686,7 +692,7 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles, bool SciMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { const GameIdStrToEnum *g = s_gameIdStrToEnum; for (; g->gameidStr; ++g) { - if (0 == strcmp(desc->gameid, g->gameidStr)) { + if (0 == strcmp(desc->gameId, g->gameidStr)) { *engine = new SciEngine(syst, desc, g->gameidEnum); return true; } diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h index 11218142a4..d84500cc60 100644 --- a/engines/sci/detection_tables.h +++ b/engines/sci/detection_tables.h @@ -684,21 +684,23 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformDOS, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, - // Gabriel Knight - English DOS CD Demo + // Gabriel Knight - English DOS Demo // SCI interpreter version 1.001.092 - {"gk1", "CD Demo", { + // Note: we are not using ADGF_DEMO here, to avoid a game ID like gk1demo-demo + {"gk1demo", "Demo", { {"resource.map", 0, "39645952ae0ed8072c7e838f31b75464", 2490}, {"resource.000", 0, "eb3ed7477ca4110813fe1fcf35928561", 1718450}, AD_LISTEND}, - Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + Common::EN_ANY, Common::kPlatformDOS, 0, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, - // Gabriel Knight - English DOS CD Demo (from DrMcCoy) + // Gabriel Knight - English DOS Demo (from DrMcCoy) // SCI interpreter version 1.001.092 - {"gk1", "CD Demo", { + // Note: we are not using ADGF_DEMO here, to avoid a game ID like gk1demo-demo + {"gk1demo", "Demo", { {"resource.map", 0, "8cad2a256f41463030cbb7ea1bfb2857", 2490}, {"resource.000", 0, "eb3ed7477ca4110813fe1fcf35928561", 1718450}, AD_LISTEND}, - Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + Common::EN_ANY, Common::kPlatformDOS, 0, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, #ifdef ENABLE_SCI32 // Gabriel Knight - English DOS Floppy @@ -2976,11 +2978,12 @@ static const struct ADGameDescription SciGameDescriptions[] = { // Police Quest 4 - English DOS Non-Interactive Demo (from FRG) // SCI interpreter version 1.001.096 - {"pq4", "Demo", { + // Note: we are not using ADGF_DEMO here, to avoid a game ID like pq4demo-demo + {"pq4demo", "Demo", { {"resource.map", 0, "be56f87a1c4a13062a30a362df860c2f", 1472}, {"resource.000", 0, "527d5684016e6816157cd15d9071b11b", 1121310}, AD_LISTEND}, - Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + Common::EN_ANY, Common::kPlatformDOS, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, #ifdef ENABLE_SCI32 // Police Quest 4 - English DOS CD (from the Police Quest Collection) @@ -3388,11 +3391,12 @@ static const struct ADGameDescription SciGameDescriptions[] = { // Quest for Glory 4 - English DOS Non-Interactive Demo (from FRG) // SCI interpreter version 1.001.069 (just a guess) - {"qfg4", "Demo", { + // Note: we are not using ADGF_DEMO here, to avoid a game ID like qfg4demo-demo + {"qfg4demo", "Demo", { {"resource.map", 0, "1ba7c7ae1efb315326d45cb931569b1b", 922}, {"resource.000", 0, "41ba03f0b188b029132daa3ece0d3e14", 623154}, AD_LISTEND}, - Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + Common::EN_ANY, Common::kPlatformDOS, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, #ifdef ENABLE_SCI32 // Quest for Glory 4 1.1 Floppy - English DOS (supplied by markcool in bug report #2723852) diff --git a/engines/sci/engine/kernel.cpp b/engines/sci/engine/kernel.cpp index 0df4701334..796dea2382 100644 --- a/engines/sci/engine/kernel.cpp +++ b/engines/sci/engine/kernel.cpp @@ -853,7 +853,7 @@ void Kernel::loadKernelNames(GameFeatures *features) { _kernelNames[0x26] = "Portrait"; else if (g_sci->getPlatform() == Common::kPlatformMacintosh) _kernelNames[0x84] = "ShowMovie"; - } else if (g_sci->getGameId() == GID_QFG4 && g_sci->isDemo()) { + } else if (g_sci->getGameId() == GID_QFG4DEMO) { _kernelNames[0x7b] = "RemapColors"; // QFG4 Demo has this SCI2 function instead of StrSplit } diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp index c0a3be4c11..93d0d91b65 100644 --- a/engines/sci/engine/kgraphics.cpp +++ b/engines/sci/engine/kgraphics.cpp @@ -45,6 +45,7 @@ #include "sci/graphics/paint16.h" #include "sci/graphics/picture.h" #include "sci/graphics/ports.h" +#include "sci/graphics/remap.h" #include "sci/graphics/screen.h" #include "sci/graphics/text16.h" #include "sci/graphics/view.h" @@ -1248,16 +1249,16 @@ reg_t kRemapColors(EngineState *s, int argc, reg_t *argv) { switch (operation) { case 0: { // remap by percent uint16 percent = argv[1].toUint16(); - g_sci->_gfxPalette16->resetRemapping(); - g_sci->_gfxPalette16->setRemappingPercent(254, percent); + g_sci->_gfxRemap16->resetRemapping(); + g_sci->_gfxRemap16->setRemappingPercent(254, percent); } break; case 1: { // remap by range uint16 from = argv[1].toUint16(); uint16 to = argv[2].toUint16(); uint16 base = argv[3].toUint16(); - g_sci->_gfxPalette16->resetRemapping(); - g_sci->_gfxPalette16->setRemappingRange(254, from, to, base); + g_sci->_gfxRemap16->resetRemapping(); + g_sci->_gfxRemap16->setRemappingRange(254, from, to, base); } break; case 2: // turn remapping off (unused) diff --git a/engines/sci/engine/kgraphics32.cpp b/engines/sci/engine/kgraphics32.cpp index 0463b125ae..9c5d214488 100644 --- a/engines/sci/engine/kgraphics32.cpp +++ b/engines/sci/engine/kgraphics32.cpp @@ -152,12 +152,12 @@ reg_t kObjectIntersect(EngineState *s, int argc, reg_t *argv) { // Tests if the coordinate is on the passed object reg_t kIsOnMe(EngineState *s, int argc, reg_t *argv) { - uint16 x = argv[0].toUint16(); - uint16 y = argv[1].toUint16(); - reg_t targetObject = argv[2]; - uint16 checkPixels = argv[3].getOffset(); + int16 x = argv[0].toSint16(); + int16 y = argv[1].toSint16(); + reg_t object = argv[2]; + bool checkPixel = argv[3].toSint16(); - return make_reg(0, g_sci->_gfxFrameout->kernelIsOnMe(x, y, checkPixels, targetObject)); + return g_sci->_gfxFrameout->kernelIsOnMe(object, Common::Point(x, y), checkPixel); } reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv) { @@ -197,16 +197,14 @@ reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv) { if (subop == 0) { TextAlign alignment = (TextAlign)readSelectorValue(segMan, object, SELECTOR(mode)); - reg_t out; - return g_sci->_gfxText32->createFontBitmap(width, height, rect, text, foreColor, backColor, skipColor, fontId, alignment, borderColor, dimmed, true, &out); + return g_sci->_gfxText32->createFontBitmap(width, height, rect, text, foreColor, backColor, skipColor, fontId, alignment, borderColor, dimmed, true); } else { CelInfo32 celInfo; celInfo.type = kCelTypeView; celInfo.resourceId = readSelectorValue(segMan, object, SELECTOR(view)); celInfo.loopNo = readSelectorValue(segMan, object, SELECTOR(loop)); celInfo.celNo = readSelectorValue(segMan, object, SELECTOR(cel)); - reg_t out; - return g_sci->_gfxText32->createFontBitmap(celInfo, rect, text, foreColor, backColor, fontId, skipColor, borderColor, dimmed, &out); + return g_sci->_gfxText32->createFontBitmap(celInfo, rect, text, foreColor, backColor, fontId, skipColor, borderColor, dimmed); } } @@ -506,7 +504,6 @@ reg_t kBitmap(EngineState *s, int argc, reg_t *argv) { } reg_t kBitmapCreate(EngineState *s, int argc, reg_t *argv) { - uint32 bitmapHeaderSize = CelObjMem::getBitmapHeaderSize(); int16 width = argv[0].toSint16(); int16 height = argv[1].toSint16(); int16 skipColor = argv[2].toSint16(); @@ -515,11 +512,9 @@ reg_t kBitmapCreate(EngineState *s, int argc, reg_t *argv) { int16 scaledHeight = argc > 5 ? argv[5].toSint16() : g_sci->_gfxText32->_scaledHeight; bool useRemap = argc > 6 ? argv[6].toSint16() : false; - reg_t bitmapMemId = s->_segMan->allocateHunkEntry("Bitmap()", width * height + bitmapHeaderSize); - byte *bitmap = s->_segMan->getHunkPointer(bitmapMemId); - memset(bitmap + bitmapHeaderSize, backColor, width * height); - CelObjMem::buildBitmapHeader(bitmap, width, height, skipColor, 0, 0, scaledWidth, scaledHeight, 0, useRemap); - return bitmapMemId; + BitmapResource bitmap(s->_segMan, width, height, skipColor, 0, 0, scaledWidth, scaledHeight, 0, useRemap); + memset(bitmap.getPixels(), backColor, width * height); + return bitmap.getObject(); } reg_t kBitmapDestroy(EngineState *s, int argc, reg_t *argv) { @@ -579,7 +574,37 @@ reg_t kBitmapDrawText(EngineState *s, int argc, reg_t *argv) { // called e.g. from TextButton::createBitmap() in Torin's Passage, script 64894 // bitmap, text, textLeft, textTop, textRight, textBottom, foreColor, backColor, skipColor, fontNo, alignment, borderColor, dimmed - return kStubNull(s, argc + 1, argv - 1); + BitmapResource bitmap(argv[0]); + Common::String text = s->_segMan->getString(argv[1]); + Common::Rect textRect( + argv[2].toSint16(), + argv[3].toSint16(), + argv[4].toSint16() + 1, + argv[5].toSint16() + 1 + ); + int16 foreColor = argv[6].toSint16(); + int16 backColor = argv[7].toSint16(); + int16 skipColor = argv[8].toSint16(); + GuiResourceId fontId = (GuiResourceId)argv[9].toUint16(); + TextAlign alignment = (TextAlign)argv[10].toSint16(); + int16 borderColor = argv[11].toSint16(); + bool dimmed = argv[12].toUint16(); + + // NOTE: Technically the engine checks these things: + // textRect.bottom > 0 + // textRect.right > 0 + // textRect.left < bitmap.width + // textRect.top < bitmap.height + // Then clips. But this seems stupid. + textRect.clip(Common::Rect(bitmap.getWidth(), bitmap.getHeight())); + + reg_t textBitmapObject = g_sci->_gfxText32->createFontBitmap(textRect.width(), textRect.height(), Common::Rect(textRect.width(), textRect.height()), text, foreColor, backColor, skipColor, fontId, alignment, borderColor, dimmed, false); + Buffer bitmapBuffer(bitmap.getWidth(), bitmap.getHeight(), bitmap.getPixels()); + CelObjMem textCel(textBitmapObject); + textCel.draw(bitmapBuffer, textRect, Common::Point(textRect.left, textRect.top), false); + s->_segMan->freeHunkEntry(textBitmapObject); + + return NULL_REG; } reg_t kBitmapDrawColor(EngineState *s, int argc, reg_t *argv) { @@ -670,14 +695,9 @@ reg_t kBitmapCreateFromUnknown(EngineState *s, int argc, reg_t *argv) { // but it handles events on its own, using an internal loop, instead of using SCI // scripts for event management like kEditControl does. Called by script 64914, // DEdit::hilite(). -reg_t kEditText(EngineState *s, int argc, reg_t *argv) { - reg_t controlObject = argv[0]; - - if (!controlObject.isNull()) { - g_sci->_gfxControls32->kernelTexteditChange(controlObject); - } - return s->r_acc; +reg_t kEditText(EngineState *s, int argc, reg_t *argv) { + return g_sci->_gfxControls32->kernelEditText(argv[0]); } reg_t kAddLine(EngineState *s, int argc, reg_t *argv) { @@ -894,6 +914,8 @@ reg_t kPalCycle(EngineState *s, int argc, reg_t *argv) { } reg_t kRemapColors32(EngineState *s, int argc, reg_t *argv) { + // TODO +#if 0 uint16 operation = argv[0].toUint16(); switch (operation) { @@ -904,57 +926,57 @@ reg_t kRemapColors32(EngineState *s, int argc, reg_t *argv) { if (g_sci->getGameId() == GID_QFG4 && s->currentRoomNumber() == 140) return s->r_acc; - int16 base = (argc >= 2) ? argv[1].toSint16() : 0; - if (base > 0) - warning("kRemapColors(0) called with base %d", base); - g_sci->_gfxPalette32->resetRemapping(); + int16 color = (argc >= 2) ? argv[1].toSint16() : 0; + if (color > 0) + warning("kRemapColors(0) called with base %d", color); + //g_sci->_gfxPalette32->resetRemapping(); } break; case 1: { // remap by range uint16 color = argv[1].toUint16(); uint16 from = argv[2].toUint16(); uint16 to = argv[3].toUint16(); - uint16 base = argv[4].toUint16(); - uint16 unk5 = (argc >= 6) ? argv[5].toUint16() : 0; - if (unk5 > 0) - warning("kRemapColors(1) called with 6 parameters, unknown parameter is %d", unk5); - g_sci->_gfxPalette32->setRemappingRange(color, from, to, base); + uint16 delta = argv[4].toUint16(); + uint16 depth = (argc >= 6) ? argv[5].toUint16() : 0; + if (depth > 0) + warning("kRemapColors(1) called with 6 parameters, depth is %d", depth); + //g_sci->_gfxPalette32->setRemappingRange(color, from, to, delta); } break; case 2: { // remap by percent uint16 color = argv[1].toUint16(); uint16 percent = argv[2].toUint16(); // 0 - 100 - if (argc >= 4) - warning("RemapByPercent called with 4 parameters, unknown parameter is %d", argv[3].toUint16()); - g_sci->_gfxPalette32->setRemappingPercent(color, percent); + uint16 depth = (argc >= 4) ? argv[3].toUint16() : 0; + if (depth >= 0) + warning("RemapByPercent called with 4 parameters, depth is %d", depth); + //g_sci->_gfxPalette32->setRemappingPercent(color, percent); } break; case 3: { // remap to gray // Example call: QFG4 room 490 (Baba Yaga's hut) - params are color 253, 75% and 0. // In this room, it's used for the cloud before Baba Yaga appears. - int16 color = argv[1].toSint16(); - int16 percent = argv[2].toSint16(); // 0 - 100 - if (argc >= 4) - warning("RemapToGray called with 4 parameters, unknown parameter is %d", argv[3].toUint16()); - g_sci->_gfxPalette32->setRemappingPercentGray(color, percent); + uint16 color = argv[1].toUint16(); + uint16 percent = argv[2].toUint16(); // 0 - 100 + uint16 depth = (argc >= 4) ? argv[3].toUint16() : 0; + if (depth >= 0) + warning("RemapToGray called with 4 parameters, depth is %d", depth); + //g_sci->_gfxPalette32->setRemappingPercentGray(color, percent); } break; case 4: { // remap to percent gray // Example call: QFG4 rooms 530/535 (swamp) - params are 253, 100%, 200 - int16 color = argv[1].toSint16(); - int16 percent = argv[2].toSint16(); // 0 - 100 - // argv[3] is unknown (a number, e.g. 200) - start color, perhaps? + uint16 color = argv[1].toUint16(); + uint16 percent = argv[2].toUint16(); // 0 - 100 + uint16 grayPercent = argv[3].toUint16(); + uint16 depth = (argc >= 5) ? argv[4].toUint16() : 0; if (argc >= 5) - warning("RemapToGrayPercent called with 5 parameters, unknown parameter is %d", argv[4].toUint16()); - g_sci->_gfxPalette32->setRemappingPercentGray(color, percent); + warning("RemapToGrayPercent called with 5 parameters, depth is %d", depth); + //g_sci->_gfxPalette32->setRemappingPercentGray(color, percent); } break; case 5: { // don't map to range - //int16 mapping = argv[1].toSint16(); - uint16 intensity = argv[2].toUint16(); - // HACK for PQ4 - if (g_sci->getGameId() == GID_PQ4) - g_sci->_gfxPalette32->kernelSetIntensity(0, 255, intensity, true); + //uint16 start = argv[1].toSint16(); + //uint16 count = argv[2].toUint16(); kStub(s, argc, argv); } @@ -962,6 +984,7 @@ reg_t kRemapColors32(EngineState *s, int argc, reg_t *argv) { default: break; } +#endif return s->r_acc; } diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp index fc0dca5123..8039c5f282 100644 --- a/engines/sci/engine/script_patches.cpp +++ b/engines/sci/engine/script_patches.cpp @@ -813,6 +813,32 @@ static const uint16 kq5PatchWitchCageInit[] = { PATCH_END }; +// The multilingual releases of KQ5 hang right at the end during the magic battle with Mordack. +// It seems additional code was added to wait for signals, but the signals are never set and thus +// the game hangs. We disable that code, so that the battle works again. +// This also happened in the original interpreter. +// We must not change similar code, that happens before. + +// Applies to at least: French PC floppy, German PC floppy, Spanish PC floppy +// Responsible method: stingScript::changeState, dragonScript::changeState, snakeScript::changeState +static const uint16 kq5SignatureMultilingualEndingGlitch[] = { + SIG_MAGICDWORD, + 0x89, 0x57, // lsg global[57h] + 0x35, 0x00, // ldi 0 + 0x1a, // eq? + 0x18, // not + 0x30, SIG_UINT16(0x0011), // bnt [skip signal check] + SIG_ADDTOOFFSET(+8), // skip globalSound::prevSignal get code + 0x36, // push + 0x35, 0x0a, // ldi 0Ah + SIG_END +}; + +static const uint16 kq5PatchMultilingualEndingGlitch[] = { + PATCH_ADDTOOFFSET(+6), + 0x32, // change BNT into JMP + PATCH_END +}; // In the final battle, the DOS version uses signals in the music to handle // timing, while in the Windows version another method is used and the GM @@ -843,9 +869,10 @@ static const uint16 kq5PatchWinGMSignals[] = { // script, description, signature patch static const SciScriptPatcherEntry kq5Signatures[] = { - { true, 0, "CD: harpy volume change", 1, kq5SignatureCdHarpyVolume, kq5PatchCdHarpyVolume }, - { true, 200, "CD: witch cage init", 1, kq5SignatureWitchCageInit, kq5PatchWitchCageInit }, - { false, 124, "Win: GM Music signal checks", 4, kq5SignatureWinGMSignals, kq5PatchWinGMSignals }, + { true, 0, "CD: harpy volume change", 1, kq5SignatureCdHarpyVolume, kq5PatchCdHarpyVolume }, + { true, 200, "CD: witch cage init", 1, kq5SignatureWitchCageInit, kq5PatchWitchCageInit }, + { true, 124, "Multilingual: Ending glitching out", 3, kq5SignatureMultilingualEndingGlitch, kq5PatchMultilingualEndingGlitch }, + { false, 124, "Win: GM Music signal checks", 4, kq5SignatureWinGMSignals, kq5PatchWinGMSignals }, SCI_SIGNATUREENTRY_TERMINATOR }; diff --git a/engines/sci/engine/selector.cpp b/engines/sci/engine/selector.cpp index 1393e96880..ac621f58ae 100644 --- a/engines/sci/engine/selector.cpp +++ b/engines/sci/engine/selector.cpp @@ -180,6 +180,7 @@ void Kernel::mapSelectors() { FIND_SELECTOR(back); FIND_SELECTOR(skip); FIND_SELECTOR(borderColor); + FIND_SELECTOR(width); FIND_SELECTOR(fixPriority); FIND_SELECTOR(mirrored); FIND_SELECTOR(visible); @@ -192,7 +193,12 @@ void Kernel::mapSelectors() { FIND_SELECTOR(textLeft); FIND_SELECTOR(textBottom); FIND_SELECTOR(textRight); + FIND_SELECTOR(title); + FIND_SELECTOR(titleFont); + FIND_SELECTOR(titleFore); + FIND_SELECTOR(titleBack); FIND_SELECTOR(magnifier); + FIND_SELECTOR(frameOut); FIND_SELECTOR(casts); #endif } diff --git a/engines/sci/engine/selector.h b/engines/sci/engine/selector.h index a8bbbe75e3..f2d06d1cf4 100644 --- a/engines/sci/engine/selector.h +++ b/engines/sci/engine/selector.h @@ -147,6 +147,7 @@ struct SelectorCache { Selector skip; Selector dimmed; Selector borderColor; + Selector width; Selector fixPriority; Selector mirrored; @@ -155,9 +156,10 @@ struct SelectorCache { Selector useInsetRect; Selector inTop, inLeft, inBottom, inRight; Selector textTop, textLeft, textBottom, textRight; + Selector title, titleFont, titleFore, titleBack; Selector magnifier; - + Selector frameOut; Selector casts; // needed for sync'ing screen items/planes with scripts, when our save/restore code is patched in (see GfxFrameout::syncWithScripts) #endif }; diff --git a/engines/sci/engine/vm_types.cpp b/engines/sci/engine/vm_types.cpp index 53a5a5c507..d74e2b194c 100644 --- a/engines/sci/engine/vm_types.cpp +++ b/engines/sci/engine/vm_types.cpp @@ -230,6 +230,10 @@ int reg_t::cmp(const reg_t right, bool treatAsUnsigned) const { return toUint16() - right.toUint16(); else return toSint16() - right.toSint16(); +#ifdef ENABLE_SCI32 + } else if (getSciVersion() >= SCI_VERSION_2) { + return sci32Comparison(right); +#endif } else if (pointerComparisonWithInteger(right)) { return 1; } else if (right.pointerComparisonWithInteger(*this)) { @@ -238,6 +242,26 @@ int reg_t::cmp(const reg_t right, bool treatAsUnsigned) const { return lookForWorkaround(right, "comparison").toSint16(); } +#ifdef ENABLE_SCI32 +int reg_t::sci32Comparison(const reg_t right) const { + // In SCI32, MemIDs are normally indexes into the memory manager's handle + // list, but the engine reserves indexes at and above 20000 for objects + // that were created inside the engine (as opposed to inside the VM). The + // engine compares these as a tiebreaker for graphics objects that are at + // the same priority, and it is necessary to at least minimally handle + // this situation. + // This is obviously a bogus comparision, but then, this entire thing is + // bogus. For the moment, it just needs to be deterministic. + if (isNumber() && !right.isNumber()) { + return 1; + } else if (right.isNumber() && !isNumber()) { + return -1; + } + + return getOffset() - right.getOffset(); +} +#endif + bool reg_t::pointerComparisonWithInteger(const reg_t right) const { // This function handles the case where a script tries to compare a pointer // to a number. Normally, we would not want to allow that. However, SCI0 - diff --git a/engines/sci/engine/vm_types.h b/engines/sci/engine/vm_types.h index a646478a8e..e60f52e85c 100644 --- a/engines/sci/engine/vm_types.h +++ b/engines/sci/engine/vm_types.h @@ -160,6 +160,10 @@ private: int cmp(const reg_t right, bool treatAsUnsigned) const; reg_t lookForWorkaround(const reg_t right, const char *operation) const; bool pointerComparisonWithInteger(const reg_t right) const; + +#ifdef ENABLE_SCI32 + int sci32Comparison(const reg_t right) const; +#endif }; static inline reg_t make_reg(SegmentId segment, uint16 offset) { diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp index 10731e270c..a4e19dc8b9 100644 --- a/engines/sci/engine/workarounds.cpp +++ b/engines/sci/engine/workarounds.cpp @@ -630,7 +630,7 @@ const SciWorkaroundEntry kGraphUpdateBox_workarounds[] = { // gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kIsObject_workarounds[] = { - { GID_GK1, 50, 999, 0, "List", "eachElementDo", NULL, 0, { WORKAROUND_FAKE, 0 } }, // GK1 demo, when asking Grace for messages it gets called with an invalid parameter (type "error") - bug #4950 + { GID_GK1DEMO, 50, 999, 0, "List", "eachElementDo", NULL, 0, { WORKAROUND_FAKE, 0 } }, // GK1 demo, when asking Grace for messages it gets called with an invalid parameter (type "error") - bug #4950 { GID_ISLANDBRAIN, -1, 999, 0, "List", "eachElementDo", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when going to the game options, choosing "Info" and selecting anything from the list, gets called with an invalid parameter (type "error") - bug #4989 { GID_QFG3, -1, 999, 0, "List", "eachElementDo", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when asking for something, gets called with type error parameter SCI_WORKAROUNDENTRY_TERMINATOR @@ -670,7 +670,7 @@ const SciWorkaroundEntry kReadNumber_workarounds[] = { // gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kPaletteUnsetFlag_workarounds[] = { - { GID_QFG4, 100, 100, 0, "doMovie", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // after the Sierra logo, no flags are passed, thus the call is meaningless - bug #4947 + { GID_QFG4DEMO, 100, 100, 0, "doMovie", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // after the Sierra logo, no flags are passed, thus the call is meaningless - bug #4947 SCI_WORKAROUNDENTRY_TERMINATOR }; diff --git a/engines/sci/event.cpp b/engines/sci/event.cpp index 3322a273ae..34f1618514 100644 --- a/engines/sci/event.cpp +++ b/engines/sci/event.cpp @@ -29,6 +29,9 @@ #include "sci/console.h" #include "sci/engine/state.h" #include "sci/engine/kernel.h" +#ifdef ENABLE_SCI32 +#include "sci/graphics/frameout.h" +#endif #include "sci/graphics/screen.h" namespace Sci { @@ -133,8 +136,13 @@ static int altify(int ch) { } SciEvent EventManager::getScummVMEvent() { - SciEvent input = { SCI_EVENT_NONE, 0, 0, Common::Point(0, 0) }; - SciEvent noEvent = { SCI_EVENT_NONE, 0, 0, Common::Point(0, 0) }; +#ifdef ENABLE_SCI32 + SciEvent input = { SCI_EVENT_NONE, 0, 0, Common::Point(), Common::Point() }; + SciEvent noEvent = { SCI_EVENT_NONE, 0, 0, Common::Point(), Common::Point() }; +#else + SciEvent input = { SCI_EVENT_NONE, 0, 0, Common::Point() }; + SciEvent noEvent = { SCI_EVENT_NONE, 0, 0, Common::Point() }; +#endif Common::EventManager *em = g_system->getEventManager(); Common::Event ev; @@ -155,7 +163,20 @@ SciEvent EventManager::getScummVMEvent() { // via pollEvent. // We also adjust the position based on the scaling of the screen. Common::Point mousePos = em->getMousePos(); - g_sci->_gfxScreen->adjustBackUpscaledCoordinates(mousePos.y, mousePos.x); + +#if ENABLE_SCI32 + if (getSciVersion() >= SCI_VERSION_2) { + Buffer &screen = g_sci->_gfxFrameout->getCurrentBuffer(); + + Common::Point mousePosSci = mousePos; + mulru(mousePosSci, Ratio(screen.scriptWidth, screen.screenWidth), Ratio(screen.scriptHeight, screen.screenHeight)); + noEvent.mousePosSci = input.mousePosSci = mousePosSci; + } else { +#endif + g_sci->_gfxScreen->adjustBackUpscaledCoordinates(mousePos.y, mousePos.x); +#if ENABLE_SCI32 + } +#endif noEvent.mousePos = input.mousePos = mousePos; @@ -302,6 +323,11 @@ SciEvent EventManager::getScummVMEvent() { input.character = altify(input.character); if (getSciVersion() <= SCI_VERSION_1_MIDDLE && (scummVMKeyFlags & Common::KBD_CTRL) && input.character > 0 && input.character < 27) input.character += 96; // 0x01 -> 'a' +#ifdef ENABLE_SCI32 + if (getSciVersion() >= SCI_VERSION_2 && (scummVMKeyFlags & Common::KBD_CTRL) && input.character == 'c') { + input.character = SCI_KEY_ETX; + } +#endif // If no actual key was pressed (e.g. if only a modifier key was pressed), // ignore the event @@ -329,7 +355,11 @@ void EventManager::updateScreen() { } SciEvent EventManager::getSciEvent(uint32 mask) { - SciEvent event = { SCI_EVENT_NONE, 0, 0, Common::Point(0, 0) }; +#ifdef ENABLE_SCI32 + SciEvent event = { SCI_EVENT_NONE, 0, 0, Common::Point(), Common::Point() }; +#else + SciEvent event = { SCI_EVENT_NONE, 0, 0, Common::Point() }; +#endif EventManager::updateScreen(); diff --git a/engines/sci/event.h b/engines/sci/event.h index 0d0c550622..15a94b3e73 100644 --- a/engines/sci/event.h +++ b/engines/sci/event.h @@ -39,11 +39,18 @@ struct SciEvent { uint16 character; /** - * The mouse position at the time the event was created. - * - * These are display coordinates! + * The mouse position at the time the event was created, + * in display coordinates. */ Common::Point mousePos; + +#ifdef ENABLE_SCI32 + /** + * The mouse position at the time the event was created, + * in script coordinates. + */ + Common::Point mousePosSci; +#endif }; /*Values for type*/ @@ -59,6 +66,9 @@ struct SciEvent { #define SCI_EVENT_ANY 0x7fff /* Keycodes of special keys: */ +#ifdef ENABLE_SCI32 +#define SCI_KEY_ETX 3 +#endif #define SCI_KEY_ESC 27 #define SCI_KEY_BACKSPACE 8 #define SCI_KEY_ENTER 13 diff --git a/engines/sci/graphics/celobj32.cpp b/engines/sci/graphics/celobj32.cpp index 787d295d5e..389270ec42 100644 --- a/engines/sci/graphics/celobj32.cpp +++ b/engines/sci/graphics/celobj32.cpp @@ -27,6 +27,7 @@ #include "sci/graphics/frameout.h" #include "sci/graphics/palette32.h" #include "sci/graphics/picture.h" +#include "sci/graphics/text32.h" #include "sci/graphics/view.h" namespace Sci { @@ -84,10 +85,9 @@ const CelScalerTable *CelScaler::getScalerTable(const Ratio &scaleX, const Ratio #pragma mark CelObj void CelObj::init() { + CelObj::deinit(); _nextCacheId = 1; - delete _scaler; _scaler = new CelScaler(); - delete _cache; _cache = new CelCache; _cache->resize(100); } @@ -95,6 +95,11 @@ void CelObj::init() { void CelObj::deinit() { delete _scaler; _scaler = nullptr; + if (_cache != nullptr) { + for (CelCache::iterator it = _cache->begin(); it != _cache->end(); ++it) { + delete it->celObj; + } + } delete _cache; _cache = nullptr; } @@ -102,7 +107,7 @@ void CelObj::deinit() { #pragma mark - #pragma mark CelObj - Scalers -template <bool FLIP, typename READER> +template<bool FLIP, typename READER> struct SCALER_NoScale { const byte *_row; READER _reader; @@ -557,7 +562,7 @@ void CelObj::putCopyInCache(const int cacheIndex) const { #pragma mark - #pragma mark CelObj - Drawing -template <typename MAPPER, typename SCALER> +template<typename MAPPER, typename SCALER> struct RENDERER { MAPPER &_mapper; SCALER &_scaler; @@ -589,7 +594,7 @@ struct RENDERER { } }; -template <typename MAPPER, typename SCALER> +template<typename MAPPER, typename SCALER> void CelObj::render(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { MAPPER mapper; @@ -598,7 +603,7 @@ void CelObj::render(Buffer &target, const Common::Rect &targetRect, const Common renderer.draw(target, targetRect, scaledPosition); } -template <typename MAPPER, typename SCALER> +template<typename MAPPER, typename SCALER> void CelObj::render(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, const Ratio &scaleX, const Ratio &scaleY) const { MAPPER mapper; @@ -968,67 +973,23 @@ byte *CelObjPic::getResPointer() const { #pragma mark - #pragma mark CelObjMem -void CelObjMem::buildBitmapHeader(byte *bitmap, const int16 width, const int16 height, const uint8 skipColor, const int16 displaceX, const int16 displaceY, const int16 scaledWidth, const int16 scaledHeight, const uint32 hunkPaletteOffset, const bool useRemap) { - const uint16 bitmapHeaderSize = getBitmapHeaderSize(); - - WRITE_SCI11ENDIAN_UINT16(bitmap + 0, width); - WRITE_SCI11ENDIAN_UINT16(bitmap + 2, height); - WRITE_SCI11ENDIAN_UINT16(bitmap + 4, (uint16)displaceX); - WRITE_SCI11ENDIAN_UINT16(bitmap + 6, (uint16)displaceY); - bitmap[8] = skipColor; - bitmap[9] = 0; - WRITE_SCI11ENDIAN_UINT16(bitmap + 10, 0); - - if (useRemap) { - bitmap[10] |= 2; - } - - WRITE_SCI11ENDIAN_UINT32(bitmap + 12, width * height); - WRITE_SCI11ENDIAN_UINT32(bitmap + 16, 0); - - if (hunkPaletteOffset) { - WRITE_SCI11ENDIAN_UINT32(bitmap + 20, hunkPaletteOffset + bitmapHeaderSize); - } else { - WRITE_SCI11ENDIAN_UINT32(bitmap + 20, 0); - } - - WRITE_SCI11ENDIAN_UINT32(bitmap + 24, bitmapHeaderSize); - WRITE_SCI11ENDIAN_UINT32(bitmap + 28, bitmapHeaderSize); - WRITE_SCI11ENDIAN_UINT32(bitmap + 32, 0); - - if (bitmapHeaderSize >= 40) { - WRITE_SCI11ENDIAN_UINT16(bitmap + 36, scaledWidth); - WRITE_SCI11ENDIAN_UINT16(bitmap + 38, scaledHeight); - } -} - -CelObjMem::CelObjMem(const reg_t bitmap) { +CelObjMem::CelObjMem(const reg_t bitmapObject) { _info.type = kCelTypeMem; - _info.bitmap = bitmap; + _info.bitmap = bitmapObject; _mirrorX = false; _compressionType = kCelCompressionNone; _celHeaderOffset = 0; _transparent = true; - const uint32 bitmapHeaderSize = getBitmapHeaderSize(); - byte *bitmapData = g_sci->getEngineState()->_segMan->getHunkPointer(bitmap); - if (bitmapData == nullptr || READ_SCI11ENDIAN_UINT32(bitmapData + 28) != bitmapHeaderSize) { - error("Invalid Text bitmap %04x:%04x", PRINT_REG(bitmap)); - } - - _width = READ_SCI11ENDIAN_UINT16(bitmapData); - _height = READ_SCI11ENDIAN_UINT16(bitmapData + 2); - _displace.x = READ_SCI11ENDIAN_UINT16(bitmapData + 4); - _displace.y = READ_SCI11ENDIAN_UINT16(bitmapData + 6); - _transparentColor = bitmapData[8]; - if (bitmapHeaderSize >= 40) { - _scaledWidth = READ_SCI11ENDIAN_UINT16(bitmapData + 36); - _scaledHeight = READ_SCI11ENDIAN_UINT16(bitmapData + 38); - } else { - error("TODO: SCI2 bitmaps not implemented yet!"); - } - _hunkPaletteOffset = READ_SCI11ENDIAN_UINT16(bitmapData + 20); - _remap = (READ_SCI11ENDIAN_UINT16(bitmapData + 10) & 2) ? true : false; + BitmapResource bitmap(bitmapObject); + _width = bitmap.getWidth(); + _height = bitmap.getHeight(); + _displace = bitmap.getDisplace(); + _transparentColor = bitmap.getSkipColor(); + _scaledWidth = bitmap.getScaledWidth(); + _scaledHeight = bitmap.getScaledHeight(); + _hunkPaletteOffset = bitmap.getHunkPaletteOffset(); + _remap = bitmap.getRemap(); } CelObjMem *CelObjMem::duplicate() const { @@ -1075,4 +1036,4 @@ CelObjColor *CelObjColor::duplicate() const { byte *CelObjColor::getResPointer() const { error("Unsupported method"); } -} +} // End of namespace Sci diff --git a/engines/sci/graphics/celobj32.h b/engines/sci/graphics/celobj32.h index 5707333818..8d030cfd4a 100644 --- a/engines/sci/graphics/celobj32.h +++ b/engines/sci/graphics/celobj32.h @@ -379,10 +379,10 @@ public: #pragma mark - #pragma mark CelObj - Drawing private: - template <typename MAPPER, typename SCALER> + template<typename MAPPER, typename SCALER> void render(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; - template <typename MAPPER, typename SCALER> + template<typename MAPPER, typename SCALER> void render(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, const Ratio &scaleX, const Ratio &scaleY) const; void drawHzFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; @@ -547,34 +547,6 @@ public: */ class CelObjMem : public CelObj { public: - /** - * Writes a bitmap header to the given data buffer. - */ - static void buildBitmapHeader(byte *bitmap, const int16 width, const int16 height, const uint8 skipColor, const int16 displaceX, const int16 displaceY, const int16 scaledWidth, const int16 scaledHeight, const uint32 hunkPaletteOffset, const bool useRemap); - - /** - * Gets the size of the bitmap header for the current - * engine version. - */ - inline static uint32 getBitmapHeaderSize() { - // TODO: These values are accurate for each engine, but there may be no reason - // to not simply just always use size 40, since SCI2.1mid does not seem to - // actually store any data above byte 40, and SCI2 did not allow bitmaps with - // scaling resolutions other than the default (320x200). Perhaps SCI3 used - // the extra bytes, or there is some reason why they tried to align the header - // size with other headers like pic headers? -// uint32 bitmapHeaderSize; -// if (getSciVersion() >= SCI_VERSION_2_1_MIDDLE) { -// bitmapHeaderSize = 46; -// } else if (getSciVersion() == SCI_VERSION_2_1_EARLY) { -// bitmapHeaderSize = 40; -// } else { -// bitmapHeaderSize = 36; -// } -// return bitmapHeaderSize; - return 46; - } - CelObjMem(reg_t bitmap); virtual ~CelObjMem() override {}; @@ -605,6 +577,6 @@ public: virtual CelObjColor *duplicate() const override; virtual byte *getResPointer() const override; }; -} +} // End of namespace Sci #endif diff --git a/engines/sci/graphics/controls32.cpp b/engines/sci/graphics/controls32.cpp index df518888a9..a877d8c276 100644 --- a/engines/sci/graphics/controls32.cpp +++ b/engines/sci/graphics/controls32.cpp @@ -23,9 +23,11 @@ #include "common/system.h" #include "sci/sci.h" +#include "sci/console.h" #include "sci/event.h" #include "sci/engine/kernel.h" #include "sci/engine/seg_manager.h" +#include "sci/engine/state.h" #include "sci/graphics/cache.h" #include "sci/graphics/compare.h" #include "sci/graphics/controls32.h" @@ -34,174 +36,321 @@ #include "sci/graphics/text32.h" namespace Sci { +GfxControls32::GfxControls32(SegManager *segMan, GfxCache *cache, GfxText32 *text) : + _segMan(segMan), + _gfxCache(cache), + _gfxText32(text), + _overwriteMode(false), + _nextCursorFlashTick(0) {} -GfxControls32::GfxControls32(SegManager *segMan, GfxCache *cache, GfxText32 *text) - : _segMan(segMan), _cache(cache), _text(text) { -} +reg_t GfxControls32::kernelEditText(const reg_t controlObject) { + SegManager *segMan = _segMan; -GfxControls32::~GfxControls32() { -} + TextEditor editor; + reg_t textObject = readSelector(_segMan, controlObject, SELECTOR(text)); + editor.text = _segMan->getString(textObject); + editor.foreColor = readSelectorValue(_segMan, controlObject, SELECTOR(fore)); + editor.backColor = readSelectorValue(_segMan, controlObject, SELECTOR(back)); + editor.skipColor = readSelectorValue(_segMan, controlObject, SELECTOR(skip)); + editor.fontId = readSelectorValue(_segMan, controlObject, SELECTOR(font)); + editor.maxLength = readSelectorValue(_segMan, controlObject, SELECTOR(width)); + editor.bitmap = readSelector(_segMan, controlObject, SELECTOR(bitmap)); + editor.cursorCharPosition = 0; + editor.cursorIsDrawn = false; + editor.borderColor = readSelectorValue(_segMan, controlObject, SELECTOR(borderColor)); -void GfxControls32::kernelTexteditChange(reg_t controlObject) { - SciEvent curEvent; - uint16 maxChars = 40; //readSelectorValue(_segMan, controlObject, SELECTOR(max)); // TODO - reg_t textReference = readSelector(_segMan, controlObject, SELECTOR(text)); - GfxFont *font = _cache->getFont(readSelectorValue(_segMan, controlObject, SELECTOR(font))); - Common::String text; - uint16 textSize; - bool textChanged = false; - bool textAddChar = false; - Common::Rect rect; + reg_t titleObject = readSelector(_segMan, controlObject, SELECTOR(title)); + + int16 titleHeight = 0; + GuiResourceId titleFontId = readSelectorValue(_segMan, controlObject, SELECTOR(titleFont)); + if (!titleObject.isNull()) { + GfxFont *titleFont = _gfxCache->getFont(titleFontId); + titleHeight += _gfxText32->scaleUpHeight(titleFont->getHeight()) + 1; + if (editor.borderColor != -1) { + titleHeight += 2; + } + } - if (textReference.isNull()) - error("kEditControl called on object that doesn't have a text reference"); - text = _segMan->getString(textReference); + int16 width = 0; + int16 height = titleHeight; - // TODO: Finish this - warning("kEditText ('%s')", text.c_str()); - return; + GfxFont *editorFont = _gfxCache->getFont(editor.fontId); + height += _gfxText32->scaleUpHeight(editorFont->getHeight()) + 1; + _gfxText32->setFont(editor.fontId); + int16 emSize = _gfxText32->getCharWidth('M', true); + width += editor.maxLength * emSize + 1; + if (editor.borderColor != -1) { + width += 4; + height += 2; + } - uint16 cursorPos = 0; - //uint16 oldCursorPos = cursorPos; - bool captureEvents = true; - EventManager* eventMan = g_sci->getEventManager(); + Common::Rect editorPlaneRect(width, height); + editorPlaneRect.translate(readSelectorValue(_segMan, controlObject, SELECTOR(x)), readSelectorValue(_segMan, controlObject, SELECTOR(y))); - while (captureEvents) { - curEvent = g_sci->getEventManager()->getSciEvent(SCI_EVENT_KEYBOARD | SCI_EVENT_PEEK); + reg_t planeObj = readSelector(_segMan, controlObject, SELECTOR(plane)); + Plane *sourcePlane = g_sci->_gfxFrameout->getVisiblePlanes().findByObject(planeObj); + if (sourcePlane == nullptr) { + error("Could not find plane %04x:%04x", PRINT_REG(planeObj)); + } + editorPlaneRect.translate(sourcePlane->_gameRect.left, sourcePlane->_gameRect.top); - if (curEvent.type == SCI_EVENT_NONE) { - eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event + editor.textRect = Common::Rect(2, titleHeight + 2, width - 1, height - 1); + editor.width = width; + + if (editor.bitmap.isNull()) { + TextAlign alignment = (TextAlign)readSelectorValue(_segMan, controlObject, SELECTOR(mode)); + + if (titleObject.isNull()) { + bool dimmed = readSelectorValue(_segMan, controlObject, SELECTOR(dimmed)); + editor.bitmap = _gfxText32->createFontBitmap(width, height, editor.textRect, editor.text, editor.foreColor, editor.backColor, editor.skipColor, editor.fontId, alignment, editor.borderColor, dimmed, true); } else { - textSize = text.size(); + Common::String title = _segMan->getString(titleObject); + int16 titleBackColor = readSelectorValue(_segMan, controlObject, SELECTOR(titleBack)); + int16 titleForeColor = readSelectorValue(_segMan, controlObject, SELECTOR(titleFore)); + editor.bitmap = _gfxText32->createTitledBitmap(width, height, editor.textRect, editor.text, editor.foreColor, editor.backColor, editor.skipColor, editor.fontId, alignment, editor.borderColor, title, titleForeColor, titleBackColor, titleFontId, true); + } + } + + drawCursor(editor); - switch (curEvent.type) { - case SCI_EVENT_MOUSE_PRESS: - // TODO: Implement mouse support for cursor change + Plane *plane = new Plane(editorPlaneRect, kPlanePicTransparent); + plane->changePic(); + g_sci->_gfxFrameout->addPlane(*plane); + + CelInfo32 celInfo; + celInfo.type = kCelTypeMem; + celInfo.bitmap = editor.bitmap; + + ScreenItem *screenItem = new ScreenItem(plane->_object, celInfo, Common::Point(), ScaleInfo()); + plane->_screenItemList.add(screenItem); + + // frameOut must be called after the screen item is + // created, and before it is updated at the end of the + // event loop, otherwise it has both created and updated + // flags set which crashes the engine (it runs updates + // before creations) + g_sci->_gfxFrameout->frameOut(true); + + EventManager *eventManager = g_sci->getEventManager(); + bool clearTextOnInput = true; + bool textChanged = false; + for (;;) { + // We peek here because the last event needs to be allowed to + // dispatch a second time to the normal event handling system. + // In the actual engine, the event is always consumed and then + // the last event just gets posted back to the event manager for + // reprocessing, but instead, we only remove the event from the + // queue *after* we have determined it is not a defocusing event + const SciEvent event = eventManager->getSciEvent(SCI_EVENT_ANY | SCI_EVENT_PEEK); + + bool focused = true; + // Original engine did not have a QUIT event but we have to handle it + if (event.type == SCI_EVENT_QUIT) { + focused = false; + break; + } else if (event.type == SCI_EVENT_MOUSE_PRESS && !editorPlaneRect.contains(event.mousePosSci)) { + focused = false; + } else if (event.type == SCI_EVENT_KEYBOARD) { + switch (event.character) { + case SCI_KEY_ESC: + case SCI_KEY_UP: + case SCI_KEY_DOWN: + case SCI_KEY_TAB: + case SCI_KEY_SHIFT_TAB: + case SCI_KEY_ENTER: + focused = false; break; - case SCI_EVENT_KEYBOARD: - switch (curEvent.character) { - case SCI_KEY_BACKSPACE: - if (cursorPos > 0) { - cursorPos--; text.deleteChar(cursorPos); - textChanged = true; - } - eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event - break; - case SCI_KEY_DELETE: - if (cursorPos < textSize) { - text.deleteChar(cursorPos); - textChanged = true; - } - eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event - break; - case SCI_KEY_HOME: // HOME - cursorPos = 0; textChanged = true; - eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event - break; - case SCI_KEY_END: // END - cursorPos = textSize; textChanged = true; - eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event - break; - case SCI_KEY_LEFT: // LEFT - if (cursorPos > 0) { - cursorPos--; textChanged = true; - } - eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event - break; - case SCI_KEY_RIGHT: // RIGHT - if (cursorPos + 1 <= textSize) { - cursorPos++; textChanged = true; - } - eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event - break; - case 3: // returned in SCI1 late and newer when Control - C is pressed - if (curEvent.modifiers & SCI_KEYMOD_CTRL) { - // Control-C erases the whole line - cursorPos = 0; text.clear(); - textChanged = true; + } + } + + if (!focused) { + break; + } + + // Consume the event now that we know it is not one of the + // defocusing events above + eventManager->getSciEvent(SCI_EVENT_ANY); + + // NOTE: In the original engine, the font and bitmap were + // reset here on each iteration through the loop, but it + // doesn't seem like this should be necessary since + // control is not yielded back to the VM until input is + // received, which means there is nothing that could modify + // the GfxText32's state with a different font in the + // meantime + + bool shouldDeleteChar = false; + bool shouldRedrawText = false; + uint16 lastCursorPosition = editor.cursorCharPosition; + if (event.type == SCI_EVENT_KEYBOARD) { + switch (event.character) { + case SCI_KEY_LEFT: + clearTextOnInput = false; + if (editor.cursorCharPosition > 0) { + --editor.cursorCharPosition; + } + break; + + case SCI_KEY_RIGHT: + clearTextOnInput = false; + if (editor.cursorCharPosition < editor.text.size()) { + ++editor.cursorCharPosition; + } + break; + + case SCI_KEY_HOME: + clearTextOnInput = false; + editor.cursorCharPosition = 0; + break; + + case SCI_KEY_END: + clearTextOnInput = false; + editor.cursorCharPosition = editor.text.size(); + break; + + case SCI_KEY_INSERT: + clearTextOnInput = false; + // Redrawing also changes the cursor rect to + // reflect the new insertion mode + shouldRedrawText = true; + _overwriteMode = !_overwriteMode; + break; + + case SCI_KEY_DELETE: + clearTextOnInput = false; + if (editor.cursorCharPosition < editor.text.size()) { + shouldDeleteChar = true; + } + break; + + case SCI_KEY_BACKSPACE: + clearTextOnInput = false; + shouldDeleteChar = true; + if (editor.cursorCharPosition > 0) { + --editor.cursorCharPosition; + } + break; + + case SCI_KEY_ETX: + editor.text.clear(); + editor.cursorCharPosition = 0; + shouldRedrawText = true; + break; + + default: { + if (event.character >= 20 && event.character < 257) { + if (clearTextOnInput) { + clearTextOnInput = false; + editor.text.clear(); } - eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event - break; - case SCI_KEY_UP: - case SCI_KEY_DOWN: - case SCI_KEY_ENTER: - case SCI_KEY_ESC: - case SCI_KEY_TAB: - case SCI_KEY_SHIFT_TAB: - captureEvents = false; - break; - default: - if ((curEvent.modifiers & SCI_KEYMOD_CTRL) && curEvent.character == 'c') { - // Control-C in earlier SCI games (SCI0 - SCI1 middle) - // Control-C erases the whole line - cursorPos = 0; text.clear(); - textChanged = true; - } else if (curEvent.character > 31 && curEvent.character < 256 && textSize < maxChars) { - // insert pressed character - textAddChar = true; - textChanged = true; + + if ( + (_overwriteMode && editor.cursorCharPosition < editor.maxLength) || + (editor.text.size() < editor.maxLength && _gfxText32->getCharWidth(event.character, true) + _gfxText32->getStringWidth(editor.text) < editor.textRect.width()) + ) { + if (_overwriteMode && editor.cursorCharPosition < editor.text.size()) { + editor.text.setChar(event.character, editor.cursorCharPosition); + } else { + editor.text.insertChar(event.character, editor.cursorCharPosition); + } + + ++editor.cursorCharPosition; + shouldRedrawText = true; } - eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event - break; } - break; + } + } + } + + if (shouldDeleteChar) { + shouldRedrawText = true; + if (editor.cursorCharPosition < editor.text.size()) { + editor.text.deleteChar(editor.cursorCharPosition); } } - if (textChanged) { - rect = g_sci->_gfxCompare->getNSRect(controlObject); + if (shouldRedrawText) { + eraseCursor(editor); + _gfxText32->erase(editor.textRect, true); + _gfxText32->drawTextBox(editor.text); + drawCursor(editor); + textChanged = true; + screenItem->_updated = g_sci->_gfxFrameout->getScreenCount(); + } else if (editor.cursorCharPosition != lastCursorPosition) { + eraseCursor(editor); + drawCursor(editor); + screenItem->_updated = g_sci->_gfxFrameout->getScreenCount(); + } else { + flashCursor(editor); + screenItem->_updated = g_sci->_gfxFrameout->getScreenCount(); + } - if (textAddChar) { - const char *textPtr = text.c_str(); + g_sci->_gfxFrameout->frameOut(true); + g_sci->getSciDebugger()->onFrame(); + g_sci->getEngineState()->speedThrottler(16); + g_sci->getEngineState()->_throttleTrigger = true; + } - // We check if we are really able to add the new char - uint16 textWidth = 0; - while (*textPtr) - textWidth += font->getCharWidth((byte)*textPtr++); - textWidth += font->getCharWidth(curEvent.character); + g_sci->_gfxFrameout->deletePlane(*plane); + if (readSelectorValue(segMan, controlObject, SELECTOR(frameOut))) { + g_sci->_gfxFrameout->frameOut(true); + } - // Does it fit? - if (textWidth >= rect.width()) { - return; - } + _segMan->freeHunkEntry(editor.bitmap); - text.insertChar(curEvent.character, cursorPos++); + if (textChanged) { + editor.text.trim(); + SciString *string = _segMan->lookupString(textObject); + string->fromString(editor.text); + } - // Note: the following checkAltInput call might make the text - // too wide to fit, but SSCI fails to check that too. - } + return make_reg(0, textChanged); +} + +void GfxControls32::drawCursor(TextEditor &editor) { + if (!editor.cursorIsDrawn) { + editor.cursorRect.left = editor.textRect.left + _gfxText32->getTextWidth(editor.text, 0, editor.cursorCharPosition); - reg_t hunkId = readSelector(_segMan, controlObject, SELECTOR(bitmap)); - Common::Rect nsRect = g_sci->_gfxCompare->getNSRect(controlObject); - //texteditCursorErase(); // TODO: Cursor - - // Write back string - _segMan->strcpy(textReference, text.c_str()); - // Modify the buffer and show it - warning("kernelTexteditChange"); -#if 0 - _text->createTextBitmap(controlObject, 0, 0, hunkId); - - _text->drawTextBitmap(0, 0, nsRect, controlObject); - //texteditCursorDraw(rect, text.c_str(), cursorPos); // TODO: Cursor - g_system->updateScreen(); -#endif + const int16 scaledFontHeight = _gfxText32->scaleUpHeight(_gfxText32->_font->getHeight()); + + // NOTE: The original code branched on borderColor here but + // the two branches appeared to be identical, differing only + // because the compiler decided to be differently clever + // when optimising multiplication in each branch + if (_overwriteMode) { + editor.cursorRect.top = editor.textRect.top; + editor.cursorRect.setHeight(scaledFontHeight); } else { - // TODO: Cursor - /* - if (g_system->getMillis() >= _texteditBlinkTime) { - _paint16->invertRect(_texteditCursorRect); - _paint16->bitsShow(_texteditCursorRect); - _texteditCursorVisible = !_texteditCursorVisible; - texteditSetBlinkTime(); - } - */ + editor.cursorRect.top = editor.textRect.top + scaledFontHeight - 1; + editor.cursorRect.setHeight(1); } - textAddChar = false; - textChanged = false; - g_sci->sleep(10); - } // while + const char currentChar = editor.cursorCharPosition < editor.text.size() ? editor.text[editor.cursorCharPosition] : ' '; + editor.cursorRect.setWidth(_gfxText32->getCharWidth(currentChar, true)); + + _gfxText32->invertRect(editor.bitmap, editor.width, editor.cursorRect, editor.foreColor, editor.backColor, true); + + editor.cursorIsDrawn = true; + } + + _nextCursorFlashTick = g_sci->getTickCount() + 30; +} + +void GfxControls32::eraseCursor(TextEditor &editor) { + if (editor.cursorIsDrawn) { + _gfxText32->invertRect(editor.bitmap, editor.width, editor.cursorRect, editor.foreColor, editor.backColor, true); + editor.cursorIsDrawn = false; + } + + _nextCursorFlashTick = g_sci->getTickCount() + 30; } +void GfxControls32::flashCursor(TextEditor &editor) { + if (g_sci->getTickCount() > _nextCursorFlashTick) { + _gfxText32->invertRect(editor.bitmap, editor.width, editor.cursorRect, editor.foreColor, editor.backColor, true); + + editor.cursorIsDrawn = !editor.cursorIsDrawn; + _nextCursorFlashTick = g_sci->getTickCount() + 30; + } +} } // End of namespace Sci diff --git a/engines/sci/graphics/controls32.h b/engines/sci/graphics/controls32.h index 5af7c20f16..1bb7679ddd 100644 --- a/engines/sci/graphics/controls32.h +++ b/engines/sci/graphics/controls32.h @@ -29,20 +29,95 @@ class GfxCache; class GfxScreen; class GfxText32; +struct TextEditor { + /** + * The bitmap where the editor is rendered. + */ + reg_t bitmap; + + /** + * The width of the editor, in bitmap pixels. + */ + int16 width; + + /** + * The text in the editor. + */ + Common::String text; + + /** + * The rect where text should be drawn into the editor, + * in bitmap pixels. + */ + Common::Rect textRect; + + /** + * The color of the border. -1 indicates no border. + */ + int16 borderColor; + + /** + * The text color. + */ + uint8 foreColor; + + /** + * The background color. + */ + uint8 backColor; + + /** + * The transparent color. + */ + uint8 skipColor; + + /** + * The font used to render the text in the editor. + */ + GuiResourceId fontId; + + /** + * The current position of the cursor within the editor. + */ + uint16 cursorCharPosition; + + /** + * Whether or not the cursor is currently drawn to the + * screen. + */ + bool cursorIsDrawn; + + /** + * The rectangle for drawing the input cursor, in bitmap + * pixels. + */ + Common::Rect cursorRect; + + /** + * The maximum allowed text length, in characters. + */ + uint16 maxLength; +}; + /** * Controls class, handles drawing of controls in SCI32 (SCI2, SCI2.1, SCI3) games */ class GfxControls32 { public: GfxControls32(SegManager *segMan, GfxCache *cache, GfxText32 *text); - ~GfxControls32(); - void kernelTexteditChange(reg_t controlObject); + reg_t kernelEditText(const reg_t controlObject); private: SegManager *_segMan; - GfxCache *_cache; - GfxText32 *_text; + GfxCache *_gfxCache; + GfxText32 *_gfxText32; + + bool _overwriteMode; + uint32 _nextCursorFlashTick; + void drawCursor(TextEditor &editor); + void eraseCursor(TextEditor &editor); + void flashCursor(TextEditor &editor); }; } // End of namespace Sci diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp index ab2ed7a4a5..655e59de00 100644 --- a/engines/sci/graphics/frameout.cpp +++ b/engines/sci/graphics/frameout.cpp @@ -133,8 +133,9 @@ GfxFrameout::GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAd } GfxFrameout::~GfxFrameout() { - CelObj::deinit(); clear(); + CelObj::deinit(); + free(_currentBuffer.getPixels()); } void GfxFrameout::run() { @@ -286,7 +287,7 @@ void GfxFrameout::kernelUpdateScreenItem(const reg_t object) { screenItem->update(object); } else { - warning("TODO: Magnifier view not implemented yet!"); + error("Magnifier view is not known to be used by any game. Please submit a bug report with details about the game you were playing and what you were doing that triggered this error. Thanks!"); } } @@ -365,6 +366,21 @@ void GfxFrameout::kernelDeletePlane(const reg_t object) { } } +void GfxFrameout::deletePlane(Plane &planeToFind) { + Plane *plane = _planes.findByObject(planeToFind._object); + if (plane == nullptr) { + error("Invalid plane passed to deletePlane"); + } + + if (plane->_created) { + _planes.erase(plane); + } else { + plane->_created = 0; + plane->_moved = 0; + plane->_deleted = getScreenCount(); + } +} + int16 GfxFrameout::kernelGetHighPlanePri() { return _planes.getTopSciPlanePriority(); } @@ -765,7 +781,7 @@ void GfxFrameout::drawScreenItemList(const DrawList &screenItemList) { mergeToShowList(drawItem.rect, _showList, _overdrawThreshold); ScreenItem &screenItem = *drawItem.screenItem; // TODO: Remove -// debug("Drawing item %04x:%04x to %d %d %d %d", PRINT_REG(screenItem._object), drawItem.rect.left, drawItem.rect.top, drawItem.rect.right, drawItem.rect.bottom); +// debug("Drawing item %04x:%04x to %d %d %d %d", PRINT_REG(screenItem._object), PRINT_RECT(drawItem.rect)); CelObj &celObj = *screenItem._celObj; celObj.draw(_currentBuffer, screenItem, drawItem.rect, screenItem._mirrorX ^ celObj._mirrorX); } @@ -1010,8 +1026,6 @@ void GfxFrameout::alterVmap(const Palette &palette1, const Palette &palette2, co // NOTE: This is currBuffer->ptr in SCI engine byte *pixels = (byte *)_currentBuffer.getPixels(); - // TODO: Guessing that display width/height is the correct - // equivalent to screen width/height in SCI engine for (int pixelIndex = 0, numPixels = _currentBuffer.screenWidth * _currentBuffer.screenHeight; pixelIndex < numPixels; ++pixelIndex) { byte currentValue = pixels[pixelIndex]; int8 styleRangeValue = styleRanges[currentValue]; @@ -1963,73 +1977,55 @@ void GfxFrameout::kernelFrameOut(const bool shouldShowBits) { } } -uint16 GfxFrameout::kernelIsOnMe(int16 x, int16 y, uint16 checkPixels, reg_t screenObject) { - reg_t planeObject = readSelector(_segMan, screenObject, SELECTOR(plane)); - Plane *screenItemPlane = _visiblePlanes.findByObject(planeObject); // Search for plane in visible planes - ScreenItem *screenItem = nullptr; +#pragma mark - +#pragma mark Mouse cursor - if (!screenItemPlane) { - // Specified plane not found - return 0; +reg_t GfxFrameout::kernelIsOnMe(const reg_t object, const Common::Point &position, bool checkPixel) const { + reg_t planeObject = readSelector(_segMan, object, SELECTOR(plane)); + Plane *plane = _visiblePlanes.findByObject(planeObject); + if (plane == nullptr) { + return make_reg(0, 0); } - screenItem = screenItemPlane->_screenItemList.findByObject(screenObject); - if (!screenItem) { - // Specified screen object not in item list - return 0; + ScreenItem *screenItem = plane->_screenItemList.findByObject(object); + if (screenItem == nullptr) { + return make_reg(0, 0); } - // Original SCI32 seems to have made a copy (?) of the screenitem? - // there is also a "or [ebp+arg_56], 1 - not sure what that did - return isOnMe(screenItemPlane, screenItem, x, y, checkPixels); + return make_reg(0, isOnMe(*screenItem, *plane, position, checkPixel)); } -uint16 GfxFrameout::isOnMe(Plane *screenItemPlane, ScreenItem *screenItem, int16 x, int16 y, uint16 checkPixels) { - // adjust coordinate according to resolution - int32 adjustedX = x * getCurrentBuffer().screenWidth / getCurrentBuffer().scriptWidth; - int32 adjustedY = y * getCurrentBuffer().screenHeight / getCurrentBuffer().scriptHeight; - - adjustedX += screenItemPlane->_planeRect.left; - adjustedY += screenItemPlane->_planeRect.top; +bool GfxFrameout::isOnMe(const ScreenItem &screenItem, const Plane &plane, const Common::Point &position, const bool checkPixel) const { - //warning("kIsOnMe %s %d (%d, %d -> %d, %d) mouse %d, %d", _segMan->getObjectName(screenObject), checkPixels, screenItem->_screenRect.left, screenItem->_screenRect.top, screenItem->_screenRect.right, screenItem->_screenRect.bottom, adjustedX, adjustedY); + Common::Point scaledPosition(position); + mulru(scaledPosition, Ratio(_currentBuffer.screenWidth, _currentBuffer.scriptWidth), Ratio(_currentBuffer.screenHeight, _currentBuffer.scriptHeight)); + scaledPosition.x += plane._planeRect.left; + scaledPosition.y += plane._planeRect.top; - if (!screenItem->_screenRect.contains(adjustedX, adjustedY)) { - // Specified coordinates are not within screen item - return 0; + if (!screenItem._screenRect.contains(scaledPosition)) { + return false; } - //warning("HIT!"); - if (checkPixels) { - //warning("Check Pixels"); - CelObj &screenItemCelObject = screenItem->getCelObj(); + if (checkPixel) { + CelObj &celObj = screenItem.getCelObj(); - Common::Point celAdjustedPoint(adjustedX, adjustedY); - bool celMirrored = screenItem->_mirrorX ^ screenItemCelObject._mirrorX; + bool mirrorX = screenItem._mirrorX ^ celObj._mirrorX; - celAdjustedPoint.x -= screenItem->_scaledPosition.x; - celAdjustedPoint.y -= screenItem->_scaledPosition.y; + scaledPosition.x -= screenItem._scaledPosition.x; + scaledPosition.y -= screenItem._scaledPosition.y; - Ratio celAdjustXRatio(screenItemCelObject._scaledWidth, getCurrentBuffer().screenWidth); - Ratio celAdjustYRatio(screenItemCelObject._scaledHeight, getCurrentBuffer().screenHeight); - mulru(celAdjustedPoint, celAdjustXRatio, celAdjustYRatio); + mulru(scaledPosition, Ratio(celObj._scaledWidth, _currentBuffer.screenWidth), Ratio(celObj._scaledHeight, _currentBuffer.screenHeight)); - if ((screenItem->_scale.signal) && (screenItem->_scale.x) && (screenItem->_scale.y)) { - // Apply scaling - celAdjustedPoint.x = celAdjustedPoint.x * 128 / screenItem->_scale.x; - celAdjustedPoint.y = celAdjustedPoint.y * 128 / screenItem->_scale.y; + if (screenItem._scale.signal != kScaleSignalNone && screenItem._scale.x && screenItem._scale.y) { + scaledPosition.x = scaledPosition.x * 128 / screenItem._scale.x; + scaledPosition.y = scaledPosition.y * 128 / screenItem._scale.y; } - byte coordinateColor = screenItemCelObject.readPixel(celAdjustedPoint.x, celAdjustedPoint.y, celMirrored); - byte transparentColor = screenItemCelObject._transparentColor; - - if (coordinateColor == transparentColor) { - // Coordinate is transparent - //warning("TRANSPARENT!"); - return 0; - } + uint8 pixel = celObj.readPixel(scaledPosition.x, scaledPosition.y, mirrorX); + return pixel != celObj._transparentColor; } - return 1; + + return true; } #pragma mark - @@ -2050,6 +2046,15 @@ void GfxFrameout::printVisiblePlaneList(Console *con) const { printPlaneListInternal(con, _visiblePlanes); } +void GfxFrameout::printPlaneItemListInternal(Console *con, const ScreenItemList &screenItemList) const { + ScreenItemList::size_type i = 0; + for (ScreenItemList::const_iterator sit = screenItemList.begin(); sit != screenItemList.end(); sit++) { + ScreenItem *screenItem = *sit; + con->debugPrintf("%2d: ", i++); + screenItem->printDebugInfo(con); + } +} + void GfxFrameout::printPlaneItemList(Console *con, const reg_t planeObject) const { Plane *p = _planes.findByObject(planeObject); @@ -2058,12 +2063,18 @@ void GfxFrameout::printPlaneItemList(Console *con, const reg_t planeObject) cons return; } - ScreenItemList::size_type i = 0; - for (ScreenItemList::iterator sit = p->_screenItemList.begin(); sit != p->_screenItemList.end(); sit++) { - ScreenItem *screenItem = *sit; - con->debugPrintf("%2d: ", i++); - screenItem->printDebugInfo(con); + printPlaneItemListInternal(con, p->_screenItemList); +} + +void GfxFrameout::printVisiblePlaneItemList(Console *con, const reg_t planeObject) const { + Plane *p = _visiblePlanes.findByObject(planeObject); + + if (p == nullptr) { + con->debugPrintf("Plane does not exist"); + return; } + + printPlaneItemListInternal(con, p->_screenItemList); } } // End of namespace Sci diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h index f172997704..f864abc5bc 100644 --- a/engines/sci/graphics/frameout.h +++ b/engines/sci/graphics/frameout.h @@ -259,6 +259,13 @@ private: PlaneList _planes; /** + * Updates an existing plane with properties from the + * given VM object. + */ + void updatePlane(Plane &plane); + +public: + /** * Creates and adds a new plane to the plane list, or * cancels deletion and updates an already-existing * plane if a plane matching the given plane VM object @@ -270,15 +277,19 @@ private: void addPlane(Plane &plane); /** - * Updates an existing plane with properties from the - * given VM object. + * Deletes a plane within the current plane list. + * + * @note This method is on Screen in SCI engine, but it + * is only ever called on `GraphicsMgr.screen`. */ - void updatePlane(Plane &plane); + void deletePlane(Plane &plane); -public: const PlaneList &getPlanes() const { return _planes; } + const PlaneList &getVisiblePlanes() const { + return _visiblePlanes; + } void kernelAddPlane(const reg_t object); void kernelUpdatePlane(const reg_t object); void kernelDeletePlane(const reg_t object); @@ -423,13 +434,6 @@ private: void drawScreenItemList(const DrawList &screenItemList); /** - * Updates the internal screen buffer for the next - * frame. If `shouldShowBits` is true, also sends the - * buffer to hardware. - */ - void frameOut(const bool shouldShowBits, const Common::Rect &rect = Common::Rect()); - - /** * Adds a new rectangle to the list of regions to write * out to the hardware. The provided rect may be merged * into an existing rectangle to reduce the number of @@ -469,6 +473,13 @@ public: void kernelFrameOut(const bool showBits); /** + * Updates the internal screen buffer for the next + * frame. If `shouldShowBits` is true, also sends the + * buffer to hardware. + */ + void frameOut(const bool shouldShowBits, const Common::Rect &rect = Common::Rect()); + + /** * Modifies the raw pixel data for the next frame with * new palette indexes based on matched style ranges. */ @@ -491,8 +502,17 @@ public: return 1; }; - uint16 kernelIsOnMe(int16 x, int16 y, uint16 checkPixels, reg_t screenObject); - uint16 isOnMe(Plane *screenItemPlane, ScreenItem *screenItem, int16 x, int16 y, uint16 checkPixels); +#pragma mark - +#pragma mark Mouse cursor +private: + /** + * Determines whether or not the point given by + * `position` is inside of the given screen item. + */ + bool isOnMe(const ScreenItem &screenItem, const Plane &plane, const Common::Point &position, const bool checkPixel) const; + +public: + reg_t kernelIsOnMe(const reg_t object, const Common::Point &position, const bool checkPixel) const; #pragma mark - #pragma mark Debugging @@ -501,6 +521,8 @@ public: void printVisiblePlaneList(Console *con) const; void printPlaneListInternal(Console *con, const PlaneList &planeList) const; void printPlaneItemList(Console *con, const reg_t planeObject) const; + void printVisiblePlaneItemList(Console *con, const reg_t planeObject) const; + void printPlaneItemListInternal(Console *con, const ScreenItemList &screenItemList) const; }; } // End of namespace Sci diff --git a/engines/sci/graphics/lists32.h b/engines/sci/graphics/lists32.h index bb990e17ca..4f74c77325 100644 --- a/engines/sci/graphics/lists32.h +++ b/engines/sci/graphics/lists32.h @@ -36,7 +36,7 @@ namespace Sci { * calling `erase` or when destroying the * StablePointerArray. */ -template <class T, uint N> +template<class T, uint N> class StablePointerArray { uint _size; T *_items[N]; @@ -178,7 +178,7 @@ public: } }; -template <typename T> +template<typename T> class FindByObject { const reg_t &_object; public: @@ -188,5 +188,5 @@ public: } }; -} +} // End of namespace Sci #endif diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp index 6f6e0b672e..1514ad838f 100644 --- a/engines/sci/graphics/palette.cpp +++ b/engines/sci/graphics/palette.cpp @@ -32,6 +32,7 @@ #include "sci/graphics/cache.h" #include "sci/graphics/maciconbar.h" #include "sci/graphics/palette.h" +#include "sci/graphics/remap.h" #include "sci/graphics/screen.h" #include "sci/graphics/view.h" @@ -103,9 +104,6 @@ GfxPalette::GfxPalette(ResourceManager *resMan, GfxScreen *screen) default: error("GfxPalette: Unknown view type"); } - - _remapOn = false; - resetRemapping(); } GfxPalette::~GfxPalette() { @@ -336,79 +334,6 @@ void GfxPalette::set(Palette *newPalette, bool force, bool forceRealMerge) { } } -byte GfxPalette::remapColor(byte remappedColor, byte screenColor) { - assert(_remapOn); - if (_remappingType[remappedColor] == kRemappingByRange) - return _remappingByRange[screenColor]; - else if (_remappingType[remappedColor] == kRemappingByPercent) - return _remappingByPercent[screenColor]; - else - error("remapColor(): Color %d isn't remapped", remappedColor); - - return 0; // should never reach here -} - -void GfxPalette::resetRemapping() { - _remapOn = false; - _remappingPercentToSet = 0; - - for (int i = 0; i < 256; i++) { - _remappingType[i] = kRemappingNone; - _remappingByPercent[i] = i; - _remappingByRange[i] = i; - } -} - -void GfxPalette::setRemappingPercent(byte color, byte percent) { - _remapOn = true; - - // We need to defer the setup of the remapping table every time the screen - // palette is changed, so that kernelFindColor() can find the correct - // colors. Set it once here, in case the palette stays the same and update - // it on each palette change by copySysPaletteToScreen(). - _remappingPercentToSet = percent; - - for (int i = 0; i < 256; i++) { - byte r = _sysPalette.colors[i].r * _remappingPercentToSet / 100; - byte g = _sysPalette.colors[i].g * _remappingPercentToSet / 100; - byte b = _sysPalette.colors[i].b * _remappingPercentToSet / 100; - _remappingByPercent[i] = kernelFindColor(r, g, b); - } - - _remappingType[color] = kRemappingByPercent; -} - -void GfxPalette::setRemappingPercentGray(byte color, byte percent) { - _remapOn = true; - - // We need to defer the setup of the remapping table every time the screen - // palette is changed, so that kernelFindColor() can find the correct - // colors. Set it once here, in case the palette stays the same and update - // it on each palette change by copySysPaletteToScreen(). - _remappingPercentToSet = percent; - - // Note: This is not what the original does, but the results are the same visually - for (int i = 0; i < 256; i++) { - byte rComponent = (byte)(_sysPalette.colors[i].r * _remappingPercentToSet * 0.30 / 100); - byte gComponent = (byte)(_sysPalette.colors[i].g * _remappingPercentToSet * 0.59 / 100); - byte bComponent = (byte)(_sysPalette.colors[i].b * _remappingPercentToSet * 0.11 / 100); - byte luminosity = rComponent + gComponent + bComponent; - _remappingByPercent[i] = kernelFindColor(luminosity, luminosity, luminosity); - } - - _remappingType[color] = kRemappingByPercent; -} - -void GfxPalette::setRemappingRange(byte color, byte from, byte to, byte base) { - _remapOn = true; - - for (int i = from; i <= to; i++) { - _remappingByRange[i] = i + base; - } - - _remappingType[color] = kRemappingByRange; -} - bool GfxPalette::insert(Palette *newPalette, Palette *destPalette) { bool paletteChanged = false; @@ -589,15 +514,8 @@ void GfxPalette::copySysPaletteToScreen() { } } - // Check if we need to reset remapping by percent with the new colors. - if (_remappingPercentToSet) { - for (int i = 0; i < 256; i++) { - byte r = _sysPalette.colors[i].r * _remappingPercentToSet / 100; - byte g = _sysPalette.colors[i].g * _remappingPercentToSet / 100; - byte b = _sysPalette.colors[i].b * _remappingPercentToSet / 100; - _remappingByPercent[i] = kernelFindColor(r, g, b); - } - } + if (g_sci->_gfxRemap16) + g_sci->_gfxRemap16->updateRemapping(); g_system->getPaletteManager()->setPalette(bpal, 0, 256); } diff --git a/engines/sci/graphics/palette.h b/engines/sci/graphics/palette.h index 61a8d36cb9..7335dc59d0 100644 --- a/engines/sci/graphics/palette.h +++ b/engines/sci/graphics/palette.h @@ -35,12 +35,6 @@ class GfxScreen; #define SCI_PALETTE_MATCH_PERFECT 0x8000 #define SCI_PALETTE_MATCH_COLORMASK 0xFF -enum ColorRemappingType { - kRemappingNone = 0, - kRemappingByRange = 1, - kRemappingByPercent = 2 -}; - /** * Palette class, handles palette operations like changing intensity, setting up the palette, merging different palettes */ @@ -64,15 +58,6 @@ public: void getSys(Palette *pal); uint16 getTotalColorCount() const { return _totalScreenColors; } - void resetRemapping(); - void setRemappingPercent(byte color, byte percent); - void setRemappingPercentGray(byte color, byte percent); - void setRemappingRange(byte color, byte from, byte to, byte base); - bool isRemapped(byte color) const { - return _remapOn && (_remappingType[color] != kRemappingNone); - } - byte remapColor(byte remappedColor, byte screenColor); - void setOnScreen(); void copySysPaletteToScreen(); @@ -138,12 +123,6 @@ protected: int _palVarySignal; uint16 _totalScreenColors; - bool _remapOn; - ColorRemappingType _remappingType[256]; - byte _remappingByPercent[256]; - byte _remappingByRange[256]; - uint16 _remappingPercentToSet; - void loadMacIconBarPalette(); byte *_macClut; }; diff --git a/engines/sci/graphics/palette32.cpp b/engines/sci/graphics/palette32.cpp index 9204e4bf96..82e8d779f1 100644 --- a/engines/sci/graphics/palette32.cpp +++ b/engines/sci/graphics/palette32.cpp @@ -55,7 +55,9 @@ GfxPalette32::GfxPalette32(ResourceManager *resMan, GfxScreen *screen) _cyclers(), _cycleMap() { _varyPercent = _varyTargetPercent; - memset(_fadeTable, 100, sizeof(_fadeTable)); + for (int i = 0, len = ARRAYSIZE(_fadeTable); i < len; ++i) { + _fadeTable[i] = 100; + } // NOTE: In SCI engine, the palette manager constructor loads // the default palette, but in ScummVM this initialisation // is performed by SciEngine::run; see r49523 for details @@ -68,7 +70,7 @@ GfxPalette32::~GfxPalette32() { } inline void mergePaletteInternal(Palette *const to, const Palette *const from) { - for (int i = 0; i < ARRAYSIZE(to->colors); ++i) { + for (int i = 0, len = ARRAYSIZE(to->colors); i < len; ++i) { if (from->colors[i].used) { to->colors[i] = from->colors[i]; } @@ -695,14 +697,8 @@ void GfxPalette32::cycleAllOn() { } void GfxPalette32::cycleAllPause() { - // TODO: The SCI SQ6 cycleAllPause function does not seem to perform - // nullptr checking?? This would definitely cause null pointer - // dereference in SCI code. I have not seen anything actually call - // this function yet, so it is possible it is just unused and broken - // in SCI SQ6. This assert exists so that if this function is called, - // it is noticed and can be rechecked in the actual engine. - // Obviously this code *does* do nullptr checks instead of crashing. :) - assert(0); + // NOTE: The original engine did not check for null pointers in the + // palette cyclers pointer array. for (int i = 0, len = ARRAYSIZE(_cyclers); i < len; ++i) { PalCycler *cycler = _cyclers[i]; if (cycler != nullptr) { @@ -795,7 +791,7 @@ void GfxPalette32::applyCycles() { // the last palette entry is intentionally left unmodified, or if this is a bug // in the engine. It certainly seems confused because all other places that accept // color ranges typically receive values in the range of 0–255. -void GfxPalette32::setFade(uint8 percent, uint8 fromColor, uint16 numColorsToFade) { +void GfxPalette32::setFade(uint16 percent, uint8 fromColor, uint16 numColorsToFade) { if (fromColor > numColorsToFade) { return; } @@ -817,9 +813,10 @@ void GfxPalette32::applyFade() { Color &color = _nextPalette.colors[i]; - color.r = (int16)color.r * _fadeTable[i] / 100; - color.g = (int16)color.g * _fadeTable[i] / 100; - color.b = (int16)color.b * _fadeTable[i] / 100; + color.r = MIN(255, (uint16)color.r * _fadeTable[i] / 100); + color.g = MIN(255, (uint16)color.g * _fadeTable[i] / 100); + color.b = MIN(255, (uint16)color.b * _fadeTable[i] / 100); } } -} + +} // End of namespace Sci diff --git a/engines/sci/graphics/palette32.h b/engines/sci/graphics/palette32.h index 9da217bf31..b8388d11ea 100644 --- a/engines/sci/graphics/palette32.h +++ b/engines/sci/graphics/palette32.h @@ -265,13 +265,19 @@ private: * The fade table records the expected intensity level of each pixel * in the palette that will be displayed on the next frame. */ - byte _fadeTable[256]; + uint16 _fadeTable[256]; public: - void setFade(const uint8 percent, const uint8 fromColor, const uint16 toColor); + /** + * Sets the intensity level for a range of palette + * entries. An intensity of zero indicates total + * darkness. Intensity may be set to over 100 percent. + */ + void setFade(const uint16 percent, const uint8 fromColor, const uint16 toColor); void fadeOff(); void applyFade(); }; -} + +} // End of namespace Sci #endif diff --git a/engines/sci/graphics/plane32.cpp b/engines/sci/graphics/plane32.cpp index 52dde4b465..4487070948 100644 --- a/engines/sci/graphics/plane32.cpp +++ b/engines/sci/graphics/plane32.cpp @@ -43,10 +43,10 @@ void DrawList::add(ScreenItem *screenItem, const Common::Rect &rect) { #pragma mark Plane uint16 Plane::_nextObjectId = 20000; -Plane::Plane(const Common::Rect &gameRect) : +Plane::Plane(const Common::Rect &gameRect, PlanePictureCodes pictureId) : _width(g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth), _height(g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight), -_pictureId(kPlanePicColored), +_pictureId(pictureId), _mirrored(false), _back(0), _priorityChanged(0), @@ -55,6 +55,7 @@ _redrawAllCount(g_sci->_gfxFrameout->getScreenCount()), _created(g_sci->_gfxFrameout->getScreenCount()), _updated(0), _deleted(0), +_moved(0), _gameRect(gameRect) { convertGameRectToPlaneRect(); _priority = MAX(10000, g_sci->_gfxFrameout->getPlanes().getTopPlanePriority() + 1); @@ -296,7 +297,7 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList ScreenItemList::size_type planeItemCount = _screenItemList.size(); ScreenItemList::size_type visiblePlaneItemCount = visiblePlane._screenItemList.size(); - for (PlaneList::size_type i = 0; i < planeItemCount; ++i) { + for (ScreenItemList::size_type i = 0; i < planeItemCount; ++i) { ScreenItem *vitem = nullptr; // NOTE: The original engine used an array without bounds checking // so could just get the visible screen item directly; we need to @@ -311,13 +312,15 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList if (i < _screenItemList.size() && item != nullptr) { if (item->_deleted) { // add item's rect to erase list - if (i < visiblePlane._screenItemList.size() && vitem != nullptr) { - if (!vitem->_screenRect.isEmpty()) { - if (/* TODO: g_Remap_numActiveRemaps */ false) { // active remaps? - mergeToRectList(vitem->_screenRect, eraseList); - } else { - eraseList.add(vitem->_screenRect); - } + if ( + i < visiblePlane._screenItemList.size() && + vitem != nullptr && + !vitem->_screenRect.isEmpty() + ) { + if (/* TODO: g_Remap_numActiveRemaps */ false) { // active remaps? + mergeToRectList(vitem->_screenRect, eraseList); + } else { + eraseList.add(vitem->_screenRect); } } } else if (item->_created) { @@ -350,7 +353,11 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList drawList.add(item, item->_screenRect); mergeToRectList(item->_screenRect, eraseList); } - if (i < visiblePlaneItemCount && vitem != nullptr && !vitem->_screenRect.isEmpty()) { + if ( + i < visiblePlaneItemCount && + vitem != nullptr && + !vitem->_screenRect.isEmpty() + ) { mergeToRectList(vitem->_screenRect, eraseList); } } else { @@ -358,10 +365,11 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList // and item to draw list // TODO: This was changed from disasm, verify please! - Common::Rect extendedScreenItem = vitem->_screenRect; - extendedScreenItem.extend(item->_screenRect); + Common::Rect extendedScreenRect = vitem->_screenRect; + extendedScreenRect.extend(item->_screenRect); + drawList.add(item, item->_screenRect); - mergeToRectList(extendedScreenItem, eraseList); + mergeToRectList(extendedScreenRect, eraseList); } } else { // if no active remaps, just add item to draw list and old rect @@ -369,7 +377,11 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList if (!item->_screenRect.isEmpty()) { drawList.add(item, item->_screenRect); } - if (i < visiblePlaneItemCount && vitem != nullptr && !vitem->_screenRect.isEmpty()) { + if ( + i < visiblePlaneItemCount && + vitem != nullptr && + !vitem->_screenRect.isEmpty() + ) { eraseList.add(vitem->_screenRect); } } @@ -381,6 +393,10 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList breakEraseListByPlanes(eraseList, planeList); breakDrawListByPlanes(drawList, planeList); + // We store the current size of the drawlist, as we want to loop + // over the currently inserted entries later. + DrawList::size_type drawListSizePrimary = drawList.size(); + // NOTE: Setting this to true fixes the menu bars in GK1 if (/* TODO: dword_C6288 */ false) { // "high resolution pictures"???? _screenItemList.sort(); @@ -428,31 +444,42 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList for (RectList::size_type i = 0; i < eraseList.size(); ++i) { for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) { ScreenItem *item = _screenItemList[j]; - if (item != nullptr && !item->_updated && !item->_deleted && !item->_created && eraseList[i]->intersects(item->_screenRect)) { + if ( + item != nullptr && + !item->_created && !item->_updated && !item->_deleted && + eraseList[i]->intersects(item->_screenRect) + ) { drawList.add(item, eraseList[i]->findIntersectingRect(item->_screenRect)); } } } } + if (/* TODO: g_Remap_numActiveRemaps == 0 */ true) { // no remaps active? // Add all items that overlap with items in the drawlist and have higher - // priority - for (DrawList::size_type i = 0; i < drawList.size(); ++i) { + // priority. + + // We only loop over "primary" items in the draw list, skipping + // those that were added because of the erase list in the previous loop, + // or those to be added in this loop. + for (DrawList::size_type i = 0; i < drawListSizePrimary; ++i) { DrawItem *dli = drawList[i]; - for (PlaneList::size_type j = 0; j < planeItemCount; ++j) { + for (ScreenItemList::size_type j = 0; j < planeItemCount; ++j) { ScreenItem *sli = _screenItemList[j]; - if (i < drawList.size() && dli) { - if (j < _screenItemList.size() && sli) { - if (!sli->_updated && !sli->_deleted && !sli->_created) { - ScreenItem *item = dli->screenItem; - if (sli->_priority > item->_priority /* TODO: || (sli->_priority == item->_priority && sli->_object > item->_object)*/) { - if (dli->rect.intersects(sli->_screenRect)) { - drawList.add(sli, dli->rect.findIntersectingRect(sli->_screenRect)); - } - } - } + if ( + i < drawList.size() && dli != nullptr && + j < _screenItemList.size() && sli != nullptr && + !sli->_created && !sli->_updated && !sli->_deleted + ) { + ScreenItem *item = dli->screenItem; + + if ( + (sli->_priority > item->_priority || (sli->_priority == item->_priority && sli->_object > item->_object)) && + dli->rect.intersects(sli->_screenRect) + ) { + drawList.add(sli, dli->rect.findIntersectingRect(sli->_screenRect)); } } } @@ -491,8 +518,7 @@ void Plane::decrementScreenItemArrayCounts(Plane *visiblePlane, const bool force if (item->_created) { item->_created--; if (visiblePlane != nullptr) { - ScreenItem *n = new ScreenItem(*item); - visiblePlane->_screenItemList.add(n); + visiblePlane->_screenItemList.add(new ScreenItem(*item)); } } @@ -582,14 +608,13 @@ void Plane::filterUpEraseRects(DrawList &drawList, RectList &eraseList) const { } } -void Plane::mergeToDrawList(const DrawList::size_type index, const Common::Rect &rect, DrawList &drawList) const { +void Plane::mergeToDrawList(const ScreenItemList::size_type index, const Common::Rect &rect, DrawList &drawList) const { RectList rects; - Common::Rect r = _screenItemList[index]->_screenRect; + ScreenItem *item = _screenItemList[index]; + Common::Rect r = item->_screenRect; r.clip(rect); - rects.add(r); - ScreenItem *item = _screenItemList[index]; for (RectList::size_type i = 0; i < rects.size(); ++i) { r = *rects[i]; @@ -778,6 +803,17 @@ void Plane::update(const reg_t object) { #pragma mark - #pragma mark PlaneList +void PlaneList::add(Plane *plane) { + for (iterator it = begin(); it != end(); ++it) { + if ((*it)->_priority > plane->_priority) { + insert(it, plane); + return; + } + } + + push_back(plane); +} + void PlaneList::clear() { for (iterator it = begin(); it != end(); ++it) { delete *it; @@ -795,6 +831,11 @@ void PlaneList::erase(Plane *plane) { } } +PlaneList::iterator PlaneList::erase(iterator it) { + delete *it; + return PlaneListBase::erase(it); +} + int PlaneList::findIndexByObject(const reg_t object) const { for (size_type i = 0; i < size(); ++i) { if ((*this)[i] != nullptr && (*this)[i]->_object == object) { @@ -837,15 +878,8 @@ int16 PlaneList::getTopSciPlanePriority() const { return priority; } -void PlaneList::add(Plane *plane) { - for (iterator it = begin(); it != end(); ++it) { - if ((*it)->_priority < plane->_priority) { - insert(it, plane); - return; - } - } - - push_back(plane); +void PlaneList::remove_at(size_type index) { + delete PlaneListBase::remove_at(index); } -} +} // End of namespace Sci diff --git a/engines/sci/graphics/plane32.h b/engines/sci/graphics/plane32.h index be6f71464a..d844919c9e 100644 --- a/engines/sci/graphics/plane32.h +++ b/engines/sci/graphics/plane32.h @@ -133,7 +133,7 @@ private: * synchronised to another plane (which calls * changePic). */ - bool _pictureChanged; // ? + bool _pictureChanged; // TODO: Are these ever actually used? int _field_34, _field_38; // probably a point or ratio @@ -241,30 +241,28 @@ public: */ static void init(); - Plane(const Common::Rect &gameRect); + // NOTE: This constructor signature originally did not accept a + // picture ID, but some calls to construct planes with this signature + // immediately set the picture ID and then called setType again, so + // it made more sense to just make the picture ID a parameter instead. + Plane(const Common::Rect &gameRect, PlanePictureCodes pictureId = kPlanePicColored); + Plane(const reg_t object); + Plane(const Plane &other); + void operator=(const Plane &other); + inline bool operator<(const Plane &other) const { - // TODO: In SCI engine, _object is actually a uint16 and can either - // contain a MemID (a handle to MemoryMgr, similar to reg_t) or - // a serial (Plane::_nextObjectId). These numbers can be compared - // directly in the real engine and the lowest MemID wins, but in - // ScummVM reg_t pointers are not comparable so we have to use a - // different strategy when two planes generated by scripts conflict. - // For now we just don't check if the priority is below 0, since - // that priority is used to represent hidden planes and is guaranteed - // to generate conflicts with script-generated planes. If there are - // other future conflicts with script-generated planes then we need - // to come up with a solution that works, similar to - // reg_t::pointerComparisonWithInteger used by SCI16. - // - // For now, we check the object offsets, as this will likely work - // like in the original SCI engine, without comparing objects. - // However, this whole comparison is quite ugly, and if it still - // fails, we should try to change it to something equivalent, to avoid - // adding loads of workarounds just for this - return _priority < other._priority || (_priority == other._priority && _priority > -1 && _object.getOffset() < other._object.getOffset()); + if (_priority < other._priority) { + return true; + } + + if (_priority == other._priority) { + return _object < other._object; + } + + return false; } /** @@ -318,12 +316,6 @@ private: inline void addPicInternal(const GuiResourceId pictureId, const Common::Point *position, const bool mirrorX); /** - * If the plane is a picture plane, re-adds all cels - * from its picture resource to the plane. - */ - void changePic(); - - /** * Marks all screen items to be deleted that are within * this plane and match the given picture ID. */ @@ -352,6 +344,13 @@ public: */ void addPic(const GuiResourceId pictureId, const Common::Point &position, const bool mirrorX); + /** + * If the plane is a picture plane, re-adds all cels + * from its picture resource to the plane. Otherwise, + * just clears the _pictureChanged flag. + */ + void changePic(); + #pragma mark - #pragma mark Plane - Rendering private: @@ -459,13 +458,14 @@ public: void add(Plane *plane); void clear(); - using PlaneListBase::erase; + iterator erase(iterator it); void erase(Plane *plane); inline void sort() { Common::sort(begin(), end(), sortHelper); } + void remove_at(size_type index); }; -} +} // End of namespace Sci #endif diff --git a/engines/sci/graphics/remap.cpp b/engines/sci/graphics/remap.cpp new file mode 100644 index 0000000000..1f1ae4da0e --- /dev/null +++ b/engines/sci/graphics/remap.cpp @@ -0,0 +1,134 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "sci/sci.h" +#include "sci/resource.h" +#include "sci/graphics/palette.h" +#include "sci/graphics/remap.h" +#include "sci/graphics/screen.h" + +namespace Sci { + +#pragma mark - +#pragma mark SCI16 remapping (QFG4 demo) + +GfxRemap::GfxRemap(GfxScreen *screen, GfxPalette *palette) + : _screen(screen), _palette(palette) { + _remapOn = false; + resetRemapping(); +} + +GfxRemap::~GfxRemap() { +} + +byte GfxRemap::remapColor(byte remappedColor, byte screenColor) { + assert(_remapOn); + if (_remappingType[remappedColor] == kRemappingByRange) + return _remappingByRange[screenColor]; + else if (_remappingType[remappedColor] == kRemappingByPercent) + return _remappingByPercent[screenColor]; + else + error("remapColor(): Color %d isn't remapped", remappedColor); + + return 0; // should never reach here +} + +void GfxRemap::resetRemapping() { + _remapOn = false; + _remappingPercentToSet = 0; + + for (int i = 0; i < 256; i++) { + _remappingType[i] = kRemappingNone; + _remappingByPercent[i] = i; + _remappingByRange[i] = i; + } +} + +void GfxRemap::setRemappingPercent(byte color, byte percent) { + _remapOn = true; + + // We need to defer the setup of the remapping table every time the screen + // palette is changed, so that kernelFindColor() can find the correct + // colors. Set it once here, in case the palette stays the same and update + // it on each palette change by copySysPaletteToScreen(). + _remappingPercentToSet = percent; + + for (int i = 0; i < 256; i++) { + byte r = _palette->_sysPalette.colors[i].r * _remappingPercentToSet / 100; + byte g = _palette->_sysPalette.colors[i].g * _remappingPercentToSet / 100; + byte b = _palette->_sysPalette.colors[i].b * _remappingPercentToSet / 100; + _remappingByPercent[i] = _palette->kernelFindColor(r, g, b); + } + + _remappingType[color] = kRemappingByPercent; +} + +void GfxRemap::setRemappingRange(byte color, byte from, byte to, byte base) { + _remapOn = true; + + for (int i = from; i <= to; i++) { + _remappingByRange[i] = i + base; + } + + _remappingType[color] = kRemappingByRange; +} + +void GfxRemap::updateRemapping() { + // Check if we need to reset remapping by percent with the new colors. + if (_remappingPercentToSet) { + for (int i = 0; i < 256; i++) { + byte r = _palette->_sysPalette.colors[i].r * _remappingPercentToSet / 100; + byte g = _palette->_sysPalette.colors[i].g * _remappingPercentToSet / 100; + byte b = _palette->_sysPalette.colors[i].b * _remappingPercentToSet / 100; + _remappingByPercent[i] = _palette->kernelFindColor(r, g, b); + } + } +} + +#pragma mark - +#pragma mark SCI32 remapping + +#if 0 +// TODO +void GfxRemap32::setRemappingPercentGray(byte color, byte percent) { + _remapOn = true; + + // We need to defer the setup of the remapping table every time the screen + // palette is changed, so that kernelFindColor() can find the correct + // colors. Set it once here, in case the palette stays the same and update + // it on each palette change by copySysPaletteToScreen(). + _remappingPercentToSet = percent; + + // Note: This is not what the original does, but the results are the same visually + for (int i = 0; i < 256; i++) { + byte rComponent = (byte)(_sysPalette.colors[i].r * _remappingPercentToSet * 0.30 / 100); + byte gComponent = (byte)(_sysPalette.colors[i].g * _remappingPercentToSet * 0.59 / 100); + byte bComponent = (byte)(_sysPalette.colors[i].b * _remappingPercentToSet * 0.11 / 100); + byte luminosity = rComponent + gComponent + bComponent; + _remappingByPercent[i] = kernelFindColor(luminosity, luminosity, luminosity); + } + + _remappingType[color] = kRemappingByPercent; +} +#endif + +} // End of namespace Sci diff --git a/engines/sci/graphics/remap.h b/engines/sci/graphics/remap.h new file mode 100644 index 0000000000..e92eaffad2 --- /dev/null +++ b/engines/sci/graphics/remap.h @@ -0,0 +1,77 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef SCI_GRAPHICS_REMAP_H +#define SCI_GRAPHICS_REMAP_H + +#include "common/array.h" +#include "sci/graphics/helpers.h" + +namespace Sci { + +class GfxScreen; + +enum ColorRemappingType { + kRemappingNone = 0, + kRemappingByRange = 1, + kRemappingByPercent = 2 +}; + +/** + * Remap class, handles color remapping + */ +class GfxRemap { +public: + GfxRemap(GfxScreen *screen, GfxPalette *_palette); + ~GfxRemap(); + void resetRemapping(); + void setRemappingPercent(byte color, byte percent); + void setRemappingRange(byte color, byte from, byte to, byte base); + bool isRemapped(byte color) const { + return _remapOn && (_remappingType[color] != kRemappingNone); + } + byte remapColor(byte remappedColor, byte screenColor); + void updateRemapping(); + +private: + GfxScreen *_screen; + GfxPalette *_palette; + + bool _remapOn; + ColorRemappingType _remappingType[256]; + byte _remappingByPercent[256]; + byte _remappingByRange[256]; + uint16 _remappingPercentToSet; +}; + +#ifdef ENABLE_SCI32 +class GfxRemap32 { +public: + GfxRemap32(GfxScreen *screen, GfxPalette *_palette) {} + ~GfxRemap32() {} + //void setRemappingPercentGray(byte color, byte percent); +}; +#endif + +} // End of namespace Sci + +#endif diff --git a/engines/sci/graphics/screen_item32.cpp b/engines/sci/graphics/screen_item32.cpp index a07dacee83..4a42221875 100644 --- a/engines/sci/graphics/screen_item32.cpp +++ b/engines/sci/graphics/screen_item32.cpp @@ -83,7 +83,7 @@ _mirrorX(false) { } } -ScreenItem::ScreenItem(const reg_t plane, const CelInfo32 &celInfo, const Common::Rect &rect, const ScaleInfo &scaleInfo) : +ScreenItem::ScreenItem(const reg_t plane, const CelInfo32 &celInfo, const Common::Point &position, const ScaleInfo &scaleInfo) : _plane(plane), _scale(scaleInfo), _useInsetRect(false), @@ -91,7 +91,7 @@ _z(0), _celInfo(celInfo), _celObj(nullptr), _fixPriority(false), -_position(rect.left, rect.top), +_position(position), _object(make_reg(0, _nextObjectId++)), _pictureId(-1), _created(g_sci->_gfxFrameout->getScreenCount()), @@ -126,6 +126,10 @@ void ScreenItem::operator=(const ScreenItem &other) { _scaledPosition = other._scaledPosition; } +ScreenItem::~ScreenItem() { + delete _celObj; +} + void ScreenItem::init() { _nextObjectId = 20000; } @@ -395,7 +399,7 @@ void ScreenItem::calcRects(const Plane &plane) { } } -CelObj &ScreenItem::getCelObj() { +CelObj &ScreenItem::getCelObj() const { if (_celObj == nullptr) { switch (_celInfo.type) { case kCelTypeView: @@ -417,7 +421,7 @@ CelObj &ScreenItem::getCelObj() { } void ScreenItem::printDebugInfo(Console *con) const { - con->debugPrintf("%x:%x (%s), prio %d, x %d, y %d, z: %d, scaledX: %d, scaledY: %d flags: %d\n", + con->debugPrintf("%04x:%04x (%s), prio %d, x %d, y %d, z: %d, scaledX: %d, scaledY: %d flags: %d\n", _object.getSegment(), _object.getOffset(), g_sci->getEngineState()->_segMan->getObjectName(_object), _priority, @@ -523,4 +527,4 @@ void ScreenItemList::unsort() { } } -} +} // End of namespace Sci diff --git a/engines/sci/graphics/screen_item32.h b/engines/sci/graphics/screen_item32.h index 0ca840a13a..52782acc56 100644 --- a/engines/sci/graphics/screen_item32.h +++ b/engines/sci/graphics/screen_item32.h @@ -125,7 +125,7 @@ public: * item. This member is populated by calling * `getCelObj`. */ - CelObj *_celObj; + mutable CelObj *_celObj; /** * If set, the priority for this screen item is fixed @@ -213,8 +213,9 @@ public: ScreenItem(const reg_t screenItem); ScreenItem(const reg_t plane, const CelInfo32 &celInfo); ScreenItem(const reg_t plane, const CelInfo32 &celInfo, const Common::Rect &rect); - ScreenItem(const reg_t plane, const CelInfo32 &celInfo, const Common::Rect &rect, const ScaleInfo &scaleInfo); + ScreenItem(const reg_t plane, const CelInfo32 &celInfo, const Common::Point &position, const ScaleInfo &scaleInfo); ScreenItem(const ScreenItem &other); + ~ScreenItem(); void operator=(const ScreenItem &); inline bool operator<(const ScreenItem &other) const { @@ -228,9 +229,7 @@ public: } if (_position.y + _z == other._position.y + other._z) { - return false; - // TODO: Failure in SQ6 room 220 -// return _object < other._object; + return _object < other._object; } } @@ -251,7 +250,7 @@ public: * screen item. If a cel object does not already exist, * one will be created and assigned. */ - CelObj &getCelObj(); + CelObj &getCelObj() const; void printDebugInfo(Console *con) const; @@ -277,6 +276,6 @@ public: void sort(); void unsort(); }; -} +} // End of namespace Sci #endif diff --git a/engines/sci/graphics/text32.cpp b/engines/sci/graphics/text32.cpp index 8e3222f580..fa19047a4c 100644 --- a/engines/sci/graphics/text32.cpp +++ b/engines/sci/graphics/text32.cpp @@ -40,10 +40,9 @@ namespace Sci { int16 GfxText32::_defaultFontId = 0; -GfxText32::GfxText32(SegManager *segMan, GfxCache *fonts, GfxScreen *screen) : +GfxText32::GfxText32(SegManager *segMan, GfxCache *fonts) : _segMan(segMan), _cache(fonts), - _screen(screen), _scaledWidth(g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth), _scaledHeight(g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight), // Not a typo, the original engine did not initialise height, only width @@ -60,7 +59,7 @@ GfxText32::GfxText32(SegManager *segMan, GfxCache *fonts, GfxScreen *screen) : _font = _cache->getFont(_defaultFontId); } -reg_t GfxText32::createFontBitmap(int16 width, int16 height, const Common::Rect &rect, const Common::String &text, const uint8 foreColor, const uint8 backColor, const uint8 skipColor, const GuiResourceId fontId, const TextAlign alignment, const int16 borderColor, const bool dimmed, const bool doScaling, reg_t *outBitmapObject) { +reg_t GfxText32::createFontBitmap(int16 width, int16 height, const Common::Rect &rect, const Common::String &text, const uint8 foreColor, const uint8 backColor, const uint8 skipColor, const GuiResourceId fontId, const TextAlign alignment, const int16 borderColor, const bool dimmed, const bool doScaling) { _field_22 = 0; _borderColor = borderColor; @@ -98,10 +97,8 @@ reg_t GfxText32::createFontBitmap(int16 width, int16 height, const Common::Rect _textRect = Common::Rect(); } - _bitmap = _segMan->allocateHunkEntry("FontBitmap()", _width * _height + CelObjMem::getBitmapHeaderSize()); - - byte *bitmap = _segMan->getHunkPointer(_bitmap); - CelObjMem::buildBitmapHeader(bitmap, _width, _height, _skipColor, 0, 0, _scaledWidth, _scaledHeight, 0, false); + BitmapResource bitmap(_segMan, _width, _height, _skipColor, 0, 0, _scaledWidth, _scaledHeight, 0, false); + _bitmap = bitmap.getObject(); erase(bitmapRect, false); @@ -110,12 +107,10 @@ reg_t GfxText32::createFontBitmap(int16 width, int16 height, const Common::Rect } drawTextBox(); - - *outBitmapObject = _bitmap; return _bitmap; } -reg_t GfxText32::createFontBitmap(const CelInfo32 &celInfo, const Common::Rect &rect, const Common::String &text, const int16 foreColor, const int16 backColor, const GuiResourceId fontId, const int16 skipColor, const int16 borderColor, const bool dimmed, reg_t *outBitmapObject) { +reg_t GfxText32::createFontBitmap(const CelInfo32 &celInfo, const Common::Rect &rect, const Common::String &text, const int16 foreColor, const int16 backColor, const GuiResourceId fontId, const int16 skipColor, const int16 borderColor, const bool dimmed) { _field_22 = 0; _borderColor = borderColor; _text = text; @@ -143,11 +138,9 @@ reg_t GfxText32::createFontBitmap(const CelInfo32 &celInfo, const Common::Rect & _textRect = Common::Rect(); } - _bitmap = _segMan->allocateHunkEntry("FontBitmap()", _width * _height + CelObjMem::getBitmapHeaderSize()); - byte *bitmap = _segMan->getHunkPointer(_bitmap); - CelObjMem::buildBitmapHeader(bitmap, _width, _height, _skipColor, 0, 0, _scaledWidth, _scaledHeight, 0, false); - - Buffer buffer(_width, _height, bitmap + READ_SCI11ENDIAN_UINT32(bitmap + 28)); + BitmapResource bitmap(_segMan, _width, _height, _skipColor, 0, 0, _scaledWidth, _scaledHeight, 0, false); + _bitmap = bitmap.getObject(); + Buffer buffer(_width, _height, bitmap.getPixels()); // NOTE: The engine filled the bitmap pixels with 11 here, which is silly // because then it just erased the bitmap using the skip color. So we don't @@ -175,10 +168,14 @@ reg_t GfxText32::createFontBitmap(const CelInfo32 &celInfo, const Common::Rect & } } - *outBitmapObject = _bitmap; return _bitmap; } +reg_t GfxText32::createTitledBitmap(const int16 width, const int16 height, const Common::Rect &textRect, const Common::String &text, const int16 foreColor, const int16 backColor, const int16 skipColor, const GuiResourceId fontId, const TextAlign alignment, const int16 borderColor, Common::String &title, const int16 titleForeColor, const int16 titleBackColor, const GuiResourceId titleFontId, const bool doScaling) { + warning("TODO: createTitledBitmap incomplete !"); + return createFontBitmap(width, height, textRect, text, foreColor, backColor, skipColor, fontId, alignment, borderColor, false, doScaling); +} + void GfxText32::setFont(const GuiResourceId fontId) { // NOTE: In SCI engine this calls FontMgr::BuildFontTable and then a font // table is built on the FontMgr directly; instead, because we already have @@ -193,16 +190,35 @@ void GfxText32::drawFrame(const Common::Rect &rect, const int16 size, const uint Common::Rect targetRect = doScaling ? scaleRect(rect) : rect; byte *bitmap = _segMan->getHunkPointer(_bitmap); - byte *pixels = bitmap + READ_SCI11ENDIAN_UINT32(bitmap + 28); + byte *pixels = bitmap + READ_SCI11ENDIAN_UINT32(bitmap + 28) + rect.top * _width + rect.left; // NOTE: Not fully disassembled, but this should be right - // TODO: Implement variable frame size - assert(size == 1); - Buffer buffer(_width, _height, pixels); - buffer.frameRect(targetRect, color); + int16 rectWidth = targetRect.width(); + int16 sidesHeight = targetRect.height() - size * 2; + int16 centerWidth = rectWidth - size * 2; + int16 stride = _width - rectWidth; + + for (int16 y = 0; y < size; ++y) { + memset(pixels, color, rectWidth); + pixels += _width; + } + for (int16 y = 0; y < sidesHeight; ++y) { + for (int16 x = 0; x < size; ++x) { + *pixels++ = color; + } + pixels += centerWidth; + for (int16 x = 0; x < size; ++x) { + *pixels++ = color; + } + pixels += stride; + } + for (int16 y = 0; y < size; ++y) { + memset(pixels, color, rectWidth); + pixels += _width; + } } -void GfxText32::drawChar(const uint8 charIndex) { +void GfxText32::drawChar(const char charIndex) { byte *bitmap = _segMan->getHunkPointer(_bitmap); byte *pixels = bitmap + READ_SCI11ENDIAN_UINT32(bitmap + 28); @@ -210,7 +226,7 @@ void GfxText32::drawChar(const uint8 charIndex) { _drawPosition.x += _font->getCharWidth(charIndex); } -uint16 GfxText32::getCharWidth(const uint8 charIndex, const bool doScaling) const { +uint16 GfxText32::getCharWidth(const char charIndex, const bool doScaling) const { uint16 width = _font->getCharWidth(charIndex); if (doScaling) { width = scaleUpWidth(width); @@ -253,6 +269,11 @@ void GfxText32::drawTextBox() { } } +void GfxText32::drawTextBox(const Common::String &text) { + _text = text; + drawTextBox(); +} + void GfxText32::drawText(const uint index, uint length) { assert(index + length <= _text.size()); @@ -309,6 +330,51 @@ void GfxText32::drawText(const uint index, uint length) { } } +void GfxText32::invertRect(const reg_t bitmap, int16 bitmapStride, const Common::Rect &rect, const uint8 foreColor, const uint8 backColor, const bool doScaling) { + Common::Rect targetRect = rect; + if (doScaling) { + bitmapStride = bitmapStride * _scaledWidth / g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; + targetRect = scaleRect(rect); + } + + byte *bitmapData = _segMan->getHunkPointer(bitmap); + + // NOTE: SCI code is super weird here; it seems to be trying to look at the + // entire size of the bitmap including the header, instead of just the pixel + // data size. We just look at the pixel size. This function generally is an + // odd duck since the stride dimension for a bitmap is built in to the bitmap + // header, so perhaps it was once an unheadered bitmap format and this + // function was never updated to match? Or maybe they exploit the + // configurable stride length somewhere else to do stair stepping inverts... + uint32 invertSize = targetRect.height() * bitmapStride + targetRect.width(); + uint32 bitmapSize = READ_SCI11ENDIAN_UINT32(bitmapData + 12); + + if (invertSize >= bitmapSize) { + error("InvertRect too big: %u >= %u", invertSize, bitmapSize); + } + + // NOTE: Actual engine just added the bitmap header size hardcoded here + byte *pixel = bitmapData + READ_SCI11ENDIAN_UINT32(bitmapData + 28) + bitmapStride * targetRect.top + targetRect.left; + + int16 stride = bitmapStride - targetRect.width(); + int16 targetHeight = targetRect.height(); + int16 targetWidth = targetRect.width(); + + for (int16 y = 0; y < targetHeight; ++y) { + for (int16 x = 0; x < targetWidth; ++x) { + if (*pixel == foreColor) { + *pixel = backColor; + } else if (*pixel == backColor) { + *pixel = foreColor; + } + + ++pixel; + } + + pixel += stride; + } +} + uint GfxText32::getLongest(uint *charIndex, const int16 width) { assert(width > 0); @@ -543,7 +609,7 @@ Common::Rect GfxText32::getTextSize(const Common::String &text, int16 maxWidth, } void GfxText32::erase(const Common::Rect &rect, const bool doScaling) { - Common::Rect targetRect = doScaling ? rect : scaleRect(rect); + Common::Rect targetRect = doScaling ? scaleRect(rect) : rect; byte *bitmap = _segMan->getHunkPointer(_bitmap); byte *pixels = bitmap + READ_SCI11ENDIAN_UINT32(bitmap + 28); @@ -556,10 +622,7 @@ void GfxText32::erase(const Common::Rect &rect, const bool doScaling) { } int16 GfxText32::getStringWidth(const Common::String &text) { - // TODO: The fact that this double-scales the text makes it - // seem pretty unlikely that this is ever called in real life - error("Called weirdo getStringWidth (FontMgr::StringWidth)"); - return scaleUpWidth(getTextWidth(text, 0, 10000)); + return getTextWidth(text, 0, 10000); } } // End of namespace Sci diff --git a/engines/sci/graphics/text32.h b/engines/sci/graphics/text32.h index 5de54d318f..472d5e0956 100644 --- a/engines/sci/graphics/text32.h +++ b/engines/sci/graphics/text32.h @@ -34,6 +34,200 @@ enum TextAlign { kTextAlignRight = 2 }; +enum BitmapFlags { + kBitmapRemap = 2 +}; + +#define BITMAP_PROPERTY(size, property, offset)\ +inline uint##size get##property() const {\ + return READ_SCI11ENDIAN_UINT##size(_bitmap + (offset));\ +}\ +inline void set##property(uint##size value) {\ + WRITE_SCI11ENDIAN_UINT##size(_bitmap + (offset), (value));\ +} + +/** + * A convenience class for creating and modifying in-memory + * bitmaps. + */ +class BitmapResource { + byte *_bitmap; + reg_t _object; + + /** + * Gets the size of the bitmap header for the current + * engine version. + */ + static inline uint16 getBitmapHeaderSize() { + // TODO: These values are accurate for each engine, but there may be no reason + // to not simply just always use size 40, since SCI2.1mid does not seem to + // actually store any data above byte 40, and SCI2 did not allow bitmaps with + // scaling resolutions other than the default (320x200). Perhaps SCI3 used + // the extra bytes, or there is some reason why they tried to align the header + // size with other headers like pic headers? +// uint32 bitmapHeaderSize; +// if (getSciVersion() >= SCI_VERSION_2_1_MIDDLE) { +// bitmapHeaderSize = 46; +// } else if (getSciVersion() == SCI_VERSION_2_1_EARLY) { +// bitmapHeaderSize = 40; +// } else { +// bitmapHeaderSize = 36; +// } +// return bitmapHeaderSize; + return 46; + } + + /** + * Gets the byte size of a bitmap with the given width + * and height. + */ + static inline uint32 getBitmapSize(const uint16 width, const uint16 height) { + return width * height + getBitmapHeaderSize(); + } + +public: + /** + * Create a bitmap resource for an existing bitmap. + * Ownership of the bitmap is retained by the caller. + */ + inline BitmapResource(reg_t bitmap) : + _bitmap(g_sci->getEngineState()->_segMan->getHunkPointer(bitmap)), + _object(bitmap) { + if (_bitmap == nullptr || getUncompressedDataOffset() != getBitmapHeaderSize()) { + error("Invalid Text bitmap %04x:%04x", PRINT_REG(bitmap)); + } + } + + /** + * Allocates and initialises a new bitmap in the given + * segment manager. + */ + inline BitmapResource(SegManager *segMan, const int16 width, const int16 height, const uint8 skipColor, const int16 displaceX, const int16 displaceY, const int16 scaledWidth, const int16 scaledHeight, const uint32 hunkPaletteOffset, const bool remap) { + + _object = segMan->allocateHunkEntry("Bitmap()", getBitmapSize(width, height)); + _bitmap = segMan->getHunkPointer(_object); + + const uint16 bitmapHeaderSize = getBitmapHeaderSize(); + + setWidth(width); + setHeight(height); + setDisplace(Common::Point(displaceX, displaceY)); + setSkipColor(skipColor); + _bitmap[9] = 0; + WRITE_SCI11ENDIAN_UINT16(_bitmap + 10, 0); + setRemap(remap); + setDataSize(width * height); + WRITE_SCI11ENDIAN_UINT32(_bitmap + 16, 0); + setHunkPaletteOffset(hunkPaletteOffset); + setDataOffset(bitmapHeaderSize); + setUncompressedDataOffset(bitmapHeaderSize); + setControlOffset(0); + setScaledWidth(scaledWidth); + setScaledHeight(scaledHeight); + } + + reg_t getObject() const { + return _object; + } + + BITMAP_PROPERTY(16, Width, 0); + BITMAP_PROPERTY(16, Height, 2); + + inline Common::Point getDisplace() const { + return Common::Point( + (int16)READ_SCI11ENDIAN_UINT16(_bitmap + 4), + (int16)READ_SCI11ENDIAN_UINT16(_bitmap + 6) + ); + } + + inline void setDisplace(const Common::Point &displace) { + WRITE_SCI11ENDIAN_UINT16(_bitmap + 4, (uint16)displace.x); + WRITE_SCI11ENDIAN_UINT16(_bitmap + 6, (uint16)displace.y); + } + + inline uint8 getSkipColor() const { + return _bitmap[8]; + } + + inline void setSkipColor(const uint8 skipColor) { + _bitmap[8] = skipColor; + } + + inline bool getRemap() const { + return READ_SCI11ENDIAN_UINT16(_bitmap + 10) & kBitmapRemap; + } + + inline void setRemap(const bool remap) { + uint16 flags = READ_SCI11ENDIAN_UINT16(_bitmap + 10); + if (remap) { + flags |= kBitmapRemap; + } else { + flags &= ~kBitmapRemap; + } + WRITE_SCI11ENDIAN_UINT16(_bitmap + 10, flags); + } + + BITMAP_PROPERTY(32, DataSize, 12); + + inline uint32 getHunkPaletteOffset() const { + return READ_SCI11ENDIAN_UINT32(_bitmap + 20); + } + + void setHunkPaletteOffset(uint32 hunkPaletteOffset) { + if (hunkPaletteOffset) { + hunkPaletteOffset += getBitmapHeaderSize(); + } + + WRITE_SCI11ENDIAN_UINT32(_bitmap + 20, hunkPaletteOffset); + } + + BITMAP_PROPERTY(32, DataOffset, 24); + + // NOTE: This property is used as a "magic number" for + // validating that a block of memory is a valid bitmap, + // and so is always set to the size of the header. + BITMAP_PROPERTY(32, UncompressedDataOffset, 28); + + // NOTE: This property always seems to be zero + BITMAP_PROPERTY(32, ControlOffset, 32); + + inline uint16 getScaledWidth() const { + if (getDataOffset() >= 40) { + return READ_SCI11ENDIAN_UINT16(_bitmap + 36); + } + + // SCI2 bitmaps did not have scaling ability + return 320; + } + + inline void setScaledWidth(uint16 scaledWidth) { + if (getDataOffset() >= 40) { + WRITE_SCI11ENDIAN_UINT16(_bitmap + 36, scaledWidth); + } + } + + inline uint16 getScaledHeight() const { + if (getDataOffset() >= 40) { + return READ_SCI11ENDIAN_UINT16(_bitmap + 38); + } + + // SCI2 bitmaps did not have scaling ability + return 200; + } + + inline void setScaledHeight(uint16 scaledHeight) { + if (getDataOffset() >= 40) { + WRITE_SCI11ENDIAN_UINT16(_bitmap + 38, scaledHeight); + } + } + + inline byte *getPixels() { + return _bitmap + getUncompressedDataOffset(); + } +}; + +class GfxFont; + /** * This class handles text calculation and rendering for * SCI32 games. The text calculation system in SCI32 is @@ -45,7 +239,6 @@ class GfxText32 { private: SegManager *_segMan; GfxCache *_cache; - GfxScreen *_screen; /** * The resource ID of the default font used by the game. @@ -111,11 +304,6 @@ private: */ TextAlign _alignment; - /** - * The memory handle of the currently active bitmap. - */ - reg_t _bitmap; - int16 _field_20; /** @@ -133,18 +321,10 @@ private: Common::Point _drawPosition; void drawFrame(const Common::Rect &rect, const int16 size, const uint8 color, const bool doScaling); - void drawTextBox(); - void erase(const Common::Rect &rect, const bool doScaling); - void drawChar(const uint8 charIndex); - uint16 getCharWidth(const uint8 charIndex, const bool doScaling) const; + void drawChar(const char charIndex); void drawText(const uint index, uint length); - inline int scaleUpWidth(int value) const { - const int scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; - return (value * scriptWidth + _scaledWidth - 1) / _scaledWidth; - } - /** * Gets the length of the longest run of text available * within the currently loaded text, starting from the @@ -162,24 +342,23 @@ private: */ int16 getTextWidth(const uint index, uint length) const; - /** - * Gets the pixel width of a substring of the currently - * loaded text, with scaling. - */ - int16 getTextWidth(const Common::String &text, const uint index, const uint length); - inline Common::Rect scaleRect(const Common::Rect &rect) { Common::Rect scaledRect(rect); int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; Ratio scaleX(_scaledWidth, scriptWidth); Ratio scaleY(_scaledHeight, scriptHeight); - mul(scaledRect, scaleX, scaleY); + mulinc(scaledRect, scaleX, scaleY); return scaledRect; } public: - GfxText32(SegManager *segMan, GfxCache *fonts, GfxScreen *screen); + GfxText32(SegManager *segMan, GfxCache *fonts); + + /** + * The memory handle of the currently active bitmap. + */ + reg_t _bitmap; /** * The size of the x-dimension of the coordinate system @@ -206,12 +385,52 @@ public: * Creates a plain font bitmap with a flat color * background. */ - reg_t createFontBitmap(int16 width, int16 height, const Common::Rect &rect, const Common::String &text, const uint8 foreColor, const uint8 backColor, const uint8 skipColor, const GuiResourceId fontId, TextAlign alignment, const int16 borderColor, bool dimmed, const bool doScaling, reg_t *outBitmapObject); + reg_t createFontBitmap(int16 width, int16 height, const Common::Rect &rect, const Common::String &text, const uint8 foreColor, const uint8 backColor, const uint8 skipColor, const GuiResourceId fontId, TextAlign alignment, const int16 borderColor, bool dimmed, const bool doScaling); /** * Creates a font bitmap with a view background. */ - reg_t createFontBitmap(const CelInfo32 &celInfo, const Common::Rect &rect, const Common::String &text, const int16 foreColor, const int16 backColor, const GuiResourceId fontId, const int16 skipColor, const int16 borderColor, const bool dimmed, reg_t *outBitmapObject); + reg_t createFontBitmap(const CelInfo32 &celInfo, const Common::Rect &rect, const Common::String &text, const int16 foreColor, const int16 backColor, const GuiResourceId fontId, const int16 skipColor, const int16 borderColor, const bool dimmed); + + /** + * Creates a font bitmap with a title. + */ + reg_t createTitledBitmap(const int16 width, const int16 height, const Common::Rect &textRect, const Common::String &text, const int16 foreColor, const int16 backColor, const int16 skipColor, const GuiResourceId fontId, const TextAlign alignment, const int16 borderColor, Common::String &title, const int16 titleForeColor, const int16 titleBackColor, const GuiResourceId titleFontId, const bool doScaling); + + inline int scaleUpWidth(int value) const { + const int scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; + return (value * scriptWidth + _scaledWidth - 1) / _scaledWidth; + } + + inline int scaleUpHeight(int value) const { + const int scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + return (value * scriptHeight + _scaledHeight - 1) / _scaledHeight; + } + + /** + * Draws the text to the bitmap. + */ + void drawTextBox(); + + /** + * Draws the given text to the bitmap. + * + * @note The original engine holds a reference to a + * shared string which lets the text be updated from + * outside of the font manager. Instead, we give this + * extra signature to send the text to draw. + * + * TODO: Use shared string instead? + */ + void drawTextBox(const Common::String &text); + + /** + * Erases the given rect by filling with the background + * color. + */ + void erase(const Common::Rect &rect, const bool doScaling); + + void invertRect(const reg_t bitmap, const int16 bitmapStride, const Common::Rect &rect, const uint8 foreColor, const uint8 backColor, const bool doScaling); /** * Sets the font to be used for rendering and @@ -220,11 +439,22 @@ public: void setFont(const GuiResourceId fontId); /** + * Gets the width of a character. + */ + uint16 getCharWidth(const char charIndex, const bool doScaling) const; + + /** * Retrieves the width and height of a block of text. */ Common::Rect getTextSize(const Common::String &text, const int16 maxWidth, bool doScaling); /** + * Gets the pixel width of a substring of the currently + * loaded text, with scaling. + */ + int16 getTextWidth(const Common::String &text, const uint index, const uint length); + + /** * Retrieves the width of a line of text. */ int16 getStringWidth(const Common::String &text); diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp index 4116eda876..ff3fc70619 100644 --- a/engines/sci/graphics/view.cpp +++ b/engines/sci/graphics/view.cpp @@ -25,6 +25,7 @@ #include "sci/engine/state.h" #include "sci/graphics/screen.h" #include "sci/graphics/palette.h" +#include "sci/graphics/remap.h" #include "sci/graphics/coordadjuster.h" #include "sci/graphics/view.h" @@ -842,12 +843,11 @@ void GfxView::draw(const Common::Rect &rect, const Common::Rect &clipRect, const const int y2 = clipRectTranslated.top + y; if (!upscaledHires) { if (priority >= _screen->getPriority(x2, y2)) { - if (!_palette->isRemapped(palette->mapping[color])) { - _screen->putPixel(x2, y2, drawMask, palette->mapping[color], priority, 0); - } else { - byte remappedColor = _palette->remapColor(palette->mapping[color], _screen->getVisual(x2, y2)); - _screen->putPixel(x2, y2, drawMask, remappedColor, priority, 0); - } + byte outputColor = palette->mapping[color]; + // SCI16 remapping (QFG4 demo) + if (g_sci->_gfxRemap16 && g_sci->_gfxRemap16->isRemapped(outputColor)) + outputColor = g_sci->_gfxRemap16->remapColor(outputColor, _screen->getVisual(x2, y2)); + _screen->putPixel(x2, y2, drawMask, outputColor, priority, 0); } } else { // UpscaledHires means view is hires and is supposed to @@ -957,12 +957,11 @@ void GfxView::drawScaled(const Common::Rect &rect, const Common::Rect &clipRect, const int x2 = clipRectTranslated.left + x; const int y2 = clipRectTranslated.top + y; if (color != clearKey && priority >= _screen->getPriority(x2, y2)) { - if (!_palette->isRemapped(palette->mapping[color])) { - _screen->putPixel(x2, y2, drawMask, palette->mapping[color], priority, 0); - } else { - byte remappedColor = _palette->remapColor(palette->mapping[color], _screen->getVisual(x2, y2)); - _screen->putPixel(x2, y2, drawMask, remappedColor, priority, 0); - } + byte outputColor = palette->mapping[color]; + // SCI16 remapping (QFG4 demo) + if (g_sci->_gfxRemap16 && g_sci->_gfxRemap16->isRemapped(outputColor)) + outputColor = g_sci->_gfxRemap16->remapColor(outputColor, _screen->getVisual(x2, y2)); + _screen->putPixel(x2, y2, drawMask, outputColor, priority, 0); } } } diff --git a/engines/sci/module.mk b/engines/sci/module.mk index 963b65742d..a02147e4d0 100644 --- a/engines/sci/module.mk +++ b/engines/sci/module.mk @@ -57,6 +57,7 @@ MODULE_OBJS := \ graphics/picture.o \ graphics/portrait.o \ graphics/ports.o \ + graphics/remap.o \ graphics/screen.o \ graphics/text16.o \ graphics/transitions.o \ diff --git a/engines/sci/resource_audio.cpp b/engines/sci/resource_audio.cpp index 82099413cb..5717a09121 100644 --- a/engines/sci/resource_audio.cpp +++ b/engines/sci/resource_audio.cpp @@ -688,6 +688,12 @@ SoundResource::SoundResource(uint32 resourceNr, ResourceManager *resMan, SciVers channel->data = resource->data + dataOffset; channel->size = READ_LE_UINT16(data + 4); + + if (dataOffset + channel->size > resource->size) { + warning("Invalid size inside sound resource %d: track %d, channel %d", resourceNr, trackNr, channelNr); + channel->size = resource->size - dataOffset; + } + channel->curPos = 0; channel->number = *channel->data; diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp index 52188db0fb..6bc6650245 100644 --- a/engines/sci/sci.cpp +++ b/engines/sci/sci.cpp @@ -58,6 +58,7 @@ #include "sci/graphics/picture.h" #include "sci/graphics/ports.h" #include "sci/graphics/palette.h" +#include "sci/graphics/remap.h" #include "sci/graphics/screen.h" #include "sci/graphics/text16.h" #include "sci/graphics/transitions.h" @@ -163,6 +164,7 @@ SciEngine::~SciEngine() { delete _gfxText32; delete _robotDecoder; delete _gfxFrameout; + delete _gfxRemap32; #endif delete _gfxMenu; delete _gfxControls16; @@ -175,6 +177,7 @@ SciEngine::~SciEngine() { delete _gfxPorts; delete _gfxCache; delete _gfxPalette16; + delete _gfxRemap16; delete _gfxCursor; delete _gfxScreen; @@ -238,13 +241,7 @@ Common::Error SciEngine::run() { // Only DOS+Windows switch (_gameId) { case GID_KQ6: - if (isCD()) - _forceHiresGraphics = ConfMan.getBool("enable_high_resolution_graphics"); - break; case GID_GK1: - if ((isCD()) && (!isDemo())) - _forceHiresGraphics = ConfMan.getBool("enable_high_resolution_graphics"); - break; case GID_PQ4: if (isCD()) _forceHiresGraphics = ConfMan.getBool("enable_high_resolution_graphics"); @@ -666,6 +663,7 @@ void SciEngine::initGraphics() { _gfxPaint = 0; _gfxPaint16 = 0; _gfxPalette16 = 0; + _gfxRemap16 = 0; _gfxPorts = 0; _gfxText16 = 0; _gfxTransitions = 0; @@ -676,6 +674,7 @@ void SciEngine::initGraphics() { _gfxFrameout = 0; _gfxPaint32 = 0; _gfxPalette32 = 0; + _gfxRemap32 = 0; #endif if (hasMacIconBar()) @@ -685,9 +684,12 @@ void SciEngine::initGraphics() { if (getSciVersion() >= SCI_VERSION_2) { _gfxPalette32 = new GfxPalette32(_resMan, _gfxScreen); _gfxPalette16 = _gfxPalette32; + _gfxRemap32 = new GfxRemap32(_gfxScreen, _gfxPalette32); } else { #endif _gfxPalette16 = new GfxPalette(_resMan, _gfxScreen); + if (getGameId() == GID_QFG4DEMO) + _gfxRemap16 = new GfxRemap(_gfxScreen, _gfxPalette16); #ifdef ENABLE_SCI32 } #endif @@ -705,7 +707,7 @@ void SciEngine::initGraphics() { _gfxPaint = _gfxPaint32; _robotDecoder = new RobotDecoder(getPlatform() == Common::kPlatformMacintosh); _gfxFrameout = new GfxFrameout(_gamestate->_segMan, _resMan, _gfxCoordAdjuster, _gfxCache, _gfxScreen, _gfxPalette32, _gfxPaint32); - _gfxText32 = new GfxText32(_gamestate->_segMan, _gfxCache, _gfxScreen); + _gfxText32 = new GfxText32(_gamestate->_segMan, _gfxCache); _gfxControls32 = new GfxControls32(_gamestate->_segMan, _gfxCache, _gfxText32); _gfxFrameout->run(); } else { @@ -834,7 +836,7 @@ Console *SciEngine::getSciDebugger() { } const char *SciEngine::getGameIdStr() const { - return _gameDescription->gameid; + return _gameDescription->gameId; } Common::Language SciEngine::getLanguage() const { diff --git a/engines/sci/sci.h b/engines/sci/sci.h index 3945e68e33..2474db81d9 100644 --- a/engines/sci/sci.h +++ b/engines/sci/sci.h @@ -71,6 +71,8 @@ class GfxPaint16; class GfxPaint32; class GfxPalette; class GfxPalette32; +class GfxRemap; +class GfxRemap32; class GfxPorts; class GfxScreen; class GfxText16; @@ -128,6 +130,7 @@ enum SciGameId { GID_FAIRYTALES, GID_FREDDYPHARKAS, GID_FUNSEEKER, + GID_GK1DEMO, // We have a separate ID for GK1 demo, because it's actually a completely different game (SCI1.1 vs SCI2/SCI2.1) GID_GK1, GID_GK2, GID_HOYLE1, @@ -165,12 +168,14 @@ enum SciGameId { GID_PQ2, GID_PQ3, GID_PQ4, + GID_PQ4DEMO, // We have a separate ID for PQ4 demo, because it's actually a completely different game (SCI1.1 vs SCI2/SCI2.1) GID_PQSWAT, GID_QFG1, GID_QFG1VGA, GID_QFG2, GID_QFG3, GID_QFG4, + GID_QFG4DEMO, // We have a separate ID for QFG4 demo, because it's actually a completely different game (SCI1.1 vs SCI2/SCI2.1) GID_RAMA, GID_SHIVERS, //GID_SHIVERS2, // Not SCI @@ -349,6 +354,8 @@ public: GfxMenu *_gfxMenu; // Menu for 16-bit gfx GfxPalette *_gfxPalette16; GfxPalette32 *_gfxPalette32; // Palette for 32-bit gfx + GfxRemap *_gfxRemap16; // Remapping for the QFG4 demo + GfxRemap32 *_gfxRemap32; // Remapping for 32-bit gfx GfxPaint *_gfxPaint; GfxPaint16 *_gfxPaint16; // Painting in 16-bit gfx GfxPaint32 *_gfxPaint32; // Painting in 32-bit gfx diff --git a/engines/sci/sound/soundcmd.cpp b/engines/sci/sound/soundcmd.cpp index ee5903fda2..e7b25eb1fc 100644 --- a/engines/sci/sound/soundcmd.cpp +++ b/engines/sci/sound/soundcmd.cpp @@ -44,7 +44,7 @@ SoundCommandParser::SoundCommandParser(ResourceManager *resMan, SegManager *segM // resource number, but it's totally unrelated to the menu music). // The GK1 demo (very late SCI1.1) does the same thing // TODO: Check the QFG4 demo - _useDigitalSFX = (getSciVersion() >= SCI_VERSION_2 || g_sci->getGameId() == GID_GK1 || ConfMan.getBool("prefer_digitalsfx")); + _useDigitalSFX = (getSciVersion() >= SCI_VERSION_2 || g_sci->getGameId() == GID_GK1DEMO || ConfMan.getBool("prefer_digitalsfx")); _music = new SciMusic(_soundVersion, _useDigitalSFX); _music->init(); |