[Scummvm-git-logs] scummvm master -> bc4692563606f8ea798a4c1032872eae71833ae5
sluicebox
noreply at scummvm.org
Fri Jul 12 05:30:06 UTC 2024
This automated email contains information about 5 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
03b41d6045 SCI: Apply Bridge workaround to HOYLE4, HOYLE5-Mac
28d4df575a SCI: Use mirror flag in SCI1.1 scaling calculations
157c8935f3 SCI: Improve workaround for incomplete class table
b29ffb2bfb SCI: Update class table while initializing objects
bc46925636 SCI: Add support for KQ5 FM-Towns save/restore UI
Commit: 03b41d60453f7c31e94fc3f345488a91d9d70c9a
https://github.com/scummvm/scummvm/commit/03b41d60453f7c31e94fc3f345488a91d9d70c9a
Author: sluicebox (22204938+sluicebox at users.noreply.github.com)
Date: 2024-07-11T22:28:16-07:00
Commit Message:
SCI: Apply Bridge workaround to HOYLE4, HOYLE5-Mac
Changed paths:
engines/sci/engine/workarounds.cpp
diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp
index 209cd8cd08b..e1e44dd00d0 100644
--- a/engines/sci/engine/workarounds.cpp
+++ b/engines/sci/engine/workarounds.cpp
@@ -33,13 +33,28 @@
namespace Sci {
// Attention:
-// To identify local-call-subroutines code signatures are used.
+// To identify locally called procedures, code signatures are used.
// Offsets change a lot between different versions of games, especially between different language releases.
-// That's why it isn't good to hardcode the offsets of those subroutines.
+// That's why it isn't good to hardcode the offsets of those procedures.
//
// Those signatures are just like the script patcher signatures (for further study: engine\script_patches.cpp)
// However you may NOT use command SIG_SELECTOR8 nor SIG_SELECTOR16 atm. Proper support for those may be added later.
+
+// Empty signature for matching against any procedure.
+//
+// Use this when maintaining signatures is unnecessary or difficult.
+//
+// For example, a local procedure in the Hoyle4 bridge game has an uninit bug.
+// This procedure is also in Hoyle5, where is is compiled as SCI32 bytecode
+// with debug instructions, and compiled again without debug instructions in
+// the Mac version of Hoyle5. This would require three different signatures,
+// but there are only two procedures in the script so it doesn't matter; it's
+// enough to identify that the bug occurs in a local procedure in the script.
+static const uint16 sig_any_procedure[] = {
+ SIG_END
+};
+
// Game: Conquests of Camelot
// Calling method: endingCartoon2::changeState
// Subroutine offset: English 0x020d (script 92)
@@ -96,17 +111,6 @@ static const uint16 sig_uninitread_hoyle1_1[] = {
SIG_END
};
-// Game: Hoyle 5
-// Calling method: LeadSeat_NoTrump::think
-// Subroutine offset: 0x22e (script 753)
-// Applies to at least: English PC
-static const uint16 sig_uninitread_hoyle5_1[] = {
- 0x7e, SIG_ADDTOOFFSET(2), // line N
- 0x7d, 0x73, 0x74, 0x67, 0x62, 0x64, 0x6c, 0x6e, 0x74,
- 0x2e, 0x73, 0x63, 0x00, // file "stgbdlnt.sc"
- SIG_END
-};
-
// Game: Jones in the fast lane
// Calling method: weekendText::draw
// Subroutine offset: 0x03d3 (script 232)
@@ -323,6 +327,7 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = {
{ GID_HOYLE4, 700, 921, 0, "Print", "addEdit", nullptr, -1, -1, { WORKAROUND_FAKE, 118 } }, // when saving the game (may also occur in other situations) - bug #6601, bug #6614
{ GID_HOYLE4, 400, 400, 1, "GinHand", "calcRuns", nullptr, 4, 4, { WORKAROUND_FAKE, 0 } }, // sometimes while playing Gin Rummy (e.g. when knocking and placing a card) - bug #5665
{ GID_HOYLE4, 500, 17, 1, "Character", "say", nullptr, 504, 504, { WORKAROUND_FAKE, 0 } }, // sometimes while playing Cribbage (e.g. when the opponent says "Last Card") - bug #5662
+ { GID_HOYLE4, 700, 753, 0, "LeadSeat_NoTrump", "think", sig_any_procedure, 4, 6, { WORKAROUND_FAKE, 0 } }, // when playing Bridge
{ GID_HOYLE4, 800, 870, 0, "EuchreStrategy", "thinkLead", nullptr, 0, 0, { WORKAROUND_FAKE, 0 } }, // while playing Euchre, happens at least on 2nd or 3rd turn - bug #6602
{ GID_HOYLE4, -1, 937, 0, "IconBar", "dispatchEvent", nullptr, 408, 408, { WORKAROUND_FAKE, 0 } }, // pressing ENTER on scoreboard while mouse is not on OK button, may not happen all the time - bug #6603
{ GID_HOYLE5, -1, 14, -1, nullptr, "select", nullptr, 1, 1, { WORKAROUND_FAKE, 0 } }, // dragging the sliders in game settings
@@ -335,7 +340,7 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = {
{ GID_HOYLE5, 700, -1, 1, "BridgeDefense", "makeContractMinusAce", nullptr, -1, -1, { WORKAROUND_FAKE, 0 } }, // when playing Bridge
{ GID_HOYLE5, 700, -1, 1, "BridgeDefense", "think", nullptr, -1, -1, { WORKAROUND_FAKE, 0 } }, // when an opponent is playing in Bridge, objects LeadSeat_NoTrump and others
{ GID_HOYLE5, 700, -1, 1, "Code", "doit", nullptr, -1, -1, { WORKAROUND_FAKE, 0 } }, // when placing a bid in Bridge, objects c2_tree, other1_tree, compwe_tree - bugs #11168, #11169, #11170, #11183
- { GID_HOYLE5, 700, 753, 0, "LeadSeat_NoTrump", "think", sig_uninitread_hoyle5_1, 4, 6, { WORKAROUND_FAKE, 0 } }, // when playing Bridge
+ { GID_HOYLE5, 700, 753, 0, "LeadSeat_NoTrump", "think", sig_any_procedure, 4, 6, { WORKAROUND_FAKE, 0 } }, // when playing Bridge
{ GID_HOYLE5, 700, 1115, 0, nullptr, "select", nullptr, 1, 1, { WORKAROUND_FAKE, 0 } }, // when adjusting the attitude slider in Bridge - bug #11166
{ GID_HOYLE5, 1100, 18, 0, "Tray", "init", nullptr, 0, 0, { WORKAROUND_FAKE, 0 } }, // when playing Poker
{ GID_HOYLE5, 1100, 1100, 0, "anteButton", "handleEvent", nullptr, 1, 1, { WORKAROUND_FAKE, 0 } }, // when exiting Poker
Commit: 28d4df575a0bf733c3ec85ceacb6878798815d05
https://github.com/scummvm/scummvm/commit/28d4df575a0bf733c3ec85ceacb6878798815d05
Author: sluicebox (22204938+sluicebox at users.noreply.github.com)
Date: 2024-07-11T22:28:16-07:00
Commit Message:
SCI: Use mirror flag in SCI1.1 scaling calculations
Sierra's scaling algorithm used the mirror flag to calculate the
scaling table. When set, it produces slightly different results
than reversing the results when the mirror flag is not set.
Changed paths:
engines/sci/graphics/view.cpp
engines/sci/graphics/view.h
diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp
index c27f90a201e..d5281c25384 100644
--- a/engines/sci/graphics/view.cpp
+++ b/engines/sci/graphics/view.cpp
@@ -872,8 +872,16 @@ void GfxView::drawScaled(const Common::Rect &rect, const Common::Rect &clipRect,
_palette->set(&_viewPalette, false);
Common::Array<uint16> scalingX, scalingY;
- createScalingTable(scalingX, celWidth, _screen->getWidth(), scaleX);
- createScalingTable(scalingY, celHeight, _screen->getHeight(), scaleY);
+ const bool mirrorFlag = _loop[CLIP<int16>(loopNo, 0, _loop.size() - 1)].mirrorFlag;
+ createScalingTable(scalingX, celWidth, _screen->getWidth(), scaleX, mirrorFlag);
+ if (mirrorFlag) {
+ // reverse the table when mirroring; we already reversed the bitmap
+ uint scaleTableSize = scalingX.size();
+ for (uint i = 0; i < scaleTableSize / 2; i++) {
+ SWAP(scalingX[i], scalingX[scaleTableSize - i - 1]);
+ }
+ }
+ createScalingTable(scalingY, celHeight, _screen->getHeight(), scaleY, false);
int16 scaledWidth = MIN(clipRect.width(), (int16)scalingX.size());
int16 scaledHeight = MIN(clipRect.height(), (int16)scalingY.size());
@@ -894,7 +902,7 @@ void GfxView::drawScaled(const Common::Rect &rect, const Common::Rect &clipRect,
}
}
-void GfxView::createScalingTable(Common::Array<uint16> &table, int16 celSize, uint16 maxSize, int16 scale) {
+void GfxView::createScalingTable(Common::Array<uint16> &table, int16 celSize, uint16 maxSize, int16 scale, bool mirrorFlag) {
const int16 scaledSize = (celSize * scale) >> 7;
const int16 clippedScaledSize = CLIP<int16>(scaledSize, 0, maxSize);
const int16 stepCount = scaledSize - 1;
@@ -904,10 +912,10 @@ void GfxView::createScalingTable(Common::Array<uint16> &table, int16 celSize, ui
return;
}
- // SSCI used the mirror flag when creating the scaling table by swapping start and end.
- // We don't do this because we reverse the cel's bitmap in memory before drawing.
- const int16 start = 0;
- const int16 end = (celSize - 1);
+ // Note that the table produced by this algorithm when mirroring
+ // is slightly different than simply reversing the normal table.
+ const int16 start = mirrorFlag ? (celSize - 1) : 0;
+ const int16 end = mirrorFlag ? 0 : (celSize - 1);
int32 acc;
int32 inc;
diff --git a/engines/sci/graphics/view.h b/engines/sci/graphics/view.h
index bd8f6dc1b3b..64f20a1810a 100644
--- a/engines/sci/graphics/view.h
+++ b/engines/sci/graphics/view.h
@@ -86,7 +86,7 @@ private:
void unditherBitmap(SciSpan<byte> &bitmap, int16 width, int16 height, byte clearKey);
byte getMappedColor(byte color, uint16 scaleSignal, const Palette *palette, int x2, int y2);
- static void createScalingTable(Common::Array<uint16> &table, int16 celSize, uint16 maxSize, int16 scale);
+ static void createScalingTable(Common::Array<uint16> &table, int16 celSize, uint16 maxSize, int16 scale, bool mirrorFlag);
ResourceManager *_resMan;
GfxCoordAdjuster16 *_coordAdjuster;
Commit: 157c8935f30d763f11d9ee20a3d16a5aa477cdd5
https://github.com/scummvm/scummvm/commit/157c8935f30d763f11d9ee20a3d16a5aa477cdd5
Author: sluicebox (22204938+sluicebox at users.noreply.github.com)
Date: 2024-07-11T22:28:16-07:00
Commit Message:
SCI: Improve workaround for incomplete class table
Fixes Macintosh version of Castle of Dr. Brain, room 220
Changed paths:
engines/sci/engine/script.cpp
diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp
index 230c1593f9b..4f76a85d489 100644
--- a/engines/sci/engine/script.cpp
+++ b/engines/sci/engine/script.cpp
@@ -1042,7 +1042,7 @@ void Script::initializeClasses(SegManager *segMan) {
return;
bool isClass = false;
- int16 species = 0;
+ uint16 species = 0;
for (;;) {
// In SCI0-SCI1, this is the segment type. In SCI11, it's a marker (0x1234)
@@ -1069,24 +1069,22 @@ void Script::initializeClasses(SegManager *segMan) {
}
if (isClass) {
- // WORKAROUNDs for off-by-one script errors
- if (species == (int)segMan->classTableSize()) {
- if (g_sci->getGameId() == GID_LSL2 && g_sci->isDemo())
- segMan->resizeClassTable(species + 1);
- else if (g_sci->getGameId() == GID_LSL3 && !g_sci->isDemo() && _nr == 500)
- segMan->resizeClassTable(species + 1);
- else if (g_sci->getGameId() == GID_SQ3 && !g_sci->isDemo() && _nr == 93)
- segMan->resizeClassTable(species + 1);
- else if (g_sci->getGameId() == GID_SQ3 && !g_sci->isDemo() && _nr == 99)
- segMan->resizeClassTable(species + 1);
- else if (g_sci->getGameId() == GID_KQ5 && g_sci->getPlatform() == Common::kPlatformAmiga && _nr == 220)
+ // WORKAROUND: The class table (vocab 996) is incomplete in some games.
+ // Because of this, scripts can contain species (class indexes) beyond
+ // the table's limit. In SCI2 this was a fatal error, but prior to this
+ // the interpreter would write to memory beyond the class table boundary
+ // and then read it back. We accommodate this by resizing the class table
+ // for plausible species values in earlier games.
+ // Ex: sq3 1.018 scripts 93 & 99, lsl3 script 500, kq5 amiga script 220
+ if (species >= segMan->classTableSize()) {
+ if (species <= 255 && getSciVersion() < SCI_VERSION_2) {
+ warning("Resizing class table for species %d in script %d", species, _nr);
segMan->resizeClassTable(species + 1);
+ } else {
+ error("Invalid species %d in script %d", species, _nr);
+ }
}
- if (species < 0 || species >= (int)segMan->classTableSize())
- error("Invalid species %d(0x%x) unknown max %d(0x%x) while instantiating script %d",
- species, species, segMan->classTableSize(), segMan->classTableSize(), _nr);
-
segMan->setClassOffset(species, make_reg32(segMan->getScriptSegment(_nr), classpos));
}
Commit: b29ffb2bfb1430d8c571fd34d22b8245040fce99
https://github.com/scummvm/scummvm/commit/b29ffb2bfb1430d8c571fd34d22b8245040fce99
Author: sluicebox (22204938+sluicebox at users.noreply.github.com)
Date: 2024-07-11T22:28:17-07:00
Commit Message:
SCI: Update class table while initializing objects
Sierra's interpreter populated the class table and initialized objects
in one pass, until SCI3. We have always been doing two passes: one to
initialize the class table and then another to initialize objects.
This should not make a difference, except that some scripts contain two
classes with the same supposedly unique class number. When this occurs,
the first class is always overwritten before it can be used, causing any
objects in between to be associated with the wrong class.
In 2010, this condition caused the ICEMAN demo to crash. It was worked
around by adding a third pass. This prevented that particular script
from crashing, but the real problem remained.
Now we update the class table and initialize objects in one pass.
For SCI3, we continue to use two passes like the original.
- Fixes KQ5 FM-Towns save and restore dialog buttons
- Fixes ICEMAN demo properly, removes workaround (bug #4963)
See: 3485d433c5c158ceb1ea74d985fa9c1274185e9c
Changed paths:
engines/sci/engine/script.cpp
engines/sci/engine/script.h
engines/sci/engine/seg_manager.cpp
diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp
index 4f76a85d489..43f0efe0b74 100644
--- a/engines/sci/engine/script.cpp
+++ b/engines/sci/engine/script.cpp
@@ -129,8 +129,7 @@ void Script::load(int script_nr, ResourceManager *resMan, ScriptPatcher *scriptP
// WORKAROUND: Script 1 in Ocean Battle doesn't have enough locals to
// fit the string showing how many shots are left (a nasty script bug,
// corrupting heap memory). We add 10 more locals so that it has enough
- // space to use as the target for its kFormat operation. Fixes bug
- // #5335.
+ // space to use as the target for its kFormat operation. Fixes bug #5335.
extraLocalsWorkaround = 10;
}
bufSize += extraLocalsWorkaround * 2;
@@ -138,8 +137,7 @@ void Script::load(int script_nr, ResourceManager *resMan, ScriptPatcher *scriptP
SciSpan<byte> outBuffer = _buf->allocate(bufSize, script->name() + " buffer");
script->copyDataTo(outBuffer);
// The word-aligned script size is used here because other parts of the code
- // currently rely on finding the start of the heap by reading the script
- // size
+ // currently rely on finding the start of the heap by reading the script size
_script = _buf->subspan(0, scriptSize, script->name());
if (heap != nullptr) {
@@ -1016,130 +1014,60 @@ void Script::syncLocalsBlock(SegManager *segMan) {
_localsBlock = (_localsSegment == 0) ? nullptr : (LocalVariables *)(segMan->getSegment(_localsSegment, SEG_TYPE_LOCALS));
}
-void Script::initializeClasses(SegManager *segMan) {
- SciSpan<const byte> seeker;
- uint16 mult = 0;
-
- if (getSciVersion() <= SCI_VERSION_1_LATE) {
- seeker = _script;
- mult = 1;
-
- // SCI0 early has an extra two bytes of header
- if (getSciVersion() == SCI_VERSION_0_EARLY) {
- seeker += 2;
+void Script::initializeClass(SegManager *segMan, uint16 species, uint32 position) {
+ // WORKAROUND: The class table (vocab 996) is incomplete in some games.
+ // Because of this, scripts can contain species (class indexes) beyond
+ // the table's limit. In SCI2 this was a fatal error, but prior to this
+ // the interpreter would write to memory beyond the class table boundary
+ // and then read it back. We accommodate this by resizing the class table
+ // for plausible species values in earlier games.
+ // Ex: sq3 1.018 scripts 93 & 99, lsl3 script 500, kq5 amiga script 220
+ if (species >= segMan->classTableSize()) {
+ if (species <= 255 && getSciVersion() < SCI_VERSION_2) {
+ warning("Resizing class table for species %d in script %d", species, _nr);
+ segMan->resizeClassTable(species + 1);
+ } else {
+ error("Invalid species %d in script %d", species, _nr);
}
- } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1_LATE) {
- seeker = _heap.subspan(4 + _heap.getUint16SEAt(2) * 2);
- mult = 2;
-#ifdef ENABLE_SCI32
- } else if (getSciVersion() == SCI_VERSION_3) {
- seeker = getSci3ObjectsPointer();
- mult = 1;
-#endif
}
- if (!seeker)
- return;
-
- bool isClass = false;
- uint16 species = 0;
-
- for (;;) {
- // In SCI0-SCI1, this is the segment type. In SCI11, it's a marker (0x1234)
- uint16 marker = seeker.getUint16SEAt(0);
- uint32 classpos = seeker - *_buf;
-
- if (getSciVersion() <= SCI_VERSION_1_LATE && !marker)
- break;
-
- if (getSciVersion() >= SCI_VERSION_1_1 && marker != SCRIPT_OBJECT_MAGIC_NUMBER)
- break;
-
- if (getSciVersion() <= SCI_VERSION_1_LATE) {
- isClass = (marker == SCI_OBJ_CLASS);
- if (isClass)
- species = seeker.getUint16SEAt(12);
- classpos += 12;
- } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1_LATE) {
- isClass = (seeker.getUint16SEAt(14) & kInfoFlagClass); // -info- selector
- species = seeker.getUint16SEAt(10);
- } else if (getSciVersion() == SCI_VERSION_3) {
- isClass = (seeker.getUint16SEAt(10) & kInfoFlagClass);
- species = seeker.getUint16SEAt(4);
- }
-
- if (isClass) {
- // WORKAROUND: The class table (vocab 996) is incomplete in some games.
- // Because of this, scripts can contain species (class indexes) beyond
- // the table's limit. In SCI2 this was a fatal error, but prior to this
- // the interpreter would write to memory beyond the class table boundary
- // and then read it back. We accommodate this by resizing the class table
- // for plausible species values in earlier games.
- // Ex: sq3 1.018 scripts 93 & 99, lsl3 script 500, kq5 amiga script 220
- if (species >= segMan->classTableSize()) {
- if (species <= 255 && getSciVersion() < SCI_VERSION_2) {
- warning("Resizing class table for species %d in script %d", species, _nr);
- segMan->resizeClassTable(species + 1);
- } else {
- error("Invalid species %d in script %d", species, _nr);
- }
- }
-
- segMan->setClassOffset(species, make_reg32(segMan->getScriptSegment(_nr), classpos));
- }
-
- seeker += seeker.getUint16SEAt(2) * mult;
- }
+ segMan->setClassOffset(species, make_reg32(segMan->getScriptSegment(_nr), position));
}
void Script::initializeObjectsSci0(SegManager *segMan, SegmentId segmentId, bool applyScriptPatches) {
bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY);
+ SciSpan<const byte> seeker = _buf->subspan(oldScriptHeader ? 2 : 0);
- // We need to make two passes, as the objects in the script might be in the
- // wrong order (e.g. in the demo of Iceman) - refer to bug #4963
- for (int pass = 1; pass <= 2; pass++) {
- SciSpan<const byte> seeker = _buf->subspan(oldScriptHeader ? 2 : 0);
+ do {
+ uint16 objType = seeker.getUint16SEAt(0);
+ if (objType == 0)
+ break;
- do {
- uint16 objType = seeker.getUint16SEAt(0);
- if (!objType)
- break;
+ if (objType == SCI_OBJ_OBJECT || objType == SCI_OBJ_CLASS) {
+ uint16 objectPosition = seeker - *_buf + 12;
+ if (objType == SCI_OBJ_CLASS) {
+ uint16 species = seeker.getUint16SEAt(12);
+ initializeClass(segMan, species, objectPosition);
+ }
- switch (objType) {
- case SCI_OBJ_OBJECT:
- case SCI_OBJ_CLASS:
- {
- reg_t addr = make_reg(segmentId, seeker - *_buf + 4 - SCRIPT_OBJECT_MAGIC_OFFSET);
- Object *obj;
- if (pass == 1) {
- obj = scriptObjInit(addr);
- obj->initSpecies(segMan, addr, applyScriptPatches);
- } else {
- obj = getObject(addr.getOffset());
- if (!obj->initBaseObject(segMan, addr, true, applyScriptPatches)) {
- if ((_nr == 202 || _nr == 764) && g_sci->getGameId() == GID_KQ5) {
- // WORKAROUND: Script 202 of KQ5 French and German
- // (perhaps Spanish too?) has an invalid object.
- // This is non-fatal. Refer to bugs #4996 and
- // #5568.
- // Same happens with script 764, it seems to
- // contain junk towards its end.
- _objects.erase(addr.toUint16() - SCRIPT_OBJECT_MAGIC_OFFSET);
- } else {
- error("Failed to locate base object for object at %04x:%04x in script %d", PRINT_REG(addr), _nr);
- }
- }
- }
+ reg_t addr = make_reg(segmentId, objectPosition);
+ Object *obj = scriptObjInit(addr);
+ obj->initSpecies(segMan, addr, applyScriptPatches);
+ if (!obj->initBaseObject(segMan, addr, true, applyScriptPatches)) {
+ if ((_nr == 202 || _nr == 764) && g_sci->getGameId() == GID_KQ5) {
+ // WORKAROUND: Script 202 of KQ5 French and German
+ // (perhaps Spanish too?) has an invalid object.
+ // This is non-fatal. Refer to bugs #4996 and #5568.
+ // Same happens with script 764, it seems to
+ // contain junk towards its end.
+ _objects.erase(addr.toUint16() - SCRIPT_OBJECT_MAGIC_OFFSET);
+ } else {
+ error("Failed to locate base object for object at %04x:%04x in script %d", PRINT_REG(addr), _nr);
}
- break;
-
- default:
- break;
}
-
- seeker += seeker.getUint16SEAt(2);
- } while ((uint32)(seeker - *_buf) < getScriptSize() - 2);
- }
+ }
+ seeker += seeker.getUint16SEAt(2);
+ } while ((uint32)(seeker - *_buf) < getScriptSize() - 2);
relocateSci0Sci21(segmentId);
}
@@ -1149,7 +1077,14 @@ void Script::initializeObjectsSci11(SegManager *segMan, SegmentId segmentId, boo
Common::Array<reg_t> mismatchedVarCountObjects;
while (seeker.getUint16SEAt(0) == SCRIPT_OBJECT_MAGIC_NUMBER) {
- reg_t reg = make_reg(segmentId, seeker - *_buf);
+ uint16 objectPosition = seeker - *_buf;
+ bool isClass = (seeker.getUint16SEAt(14) & kInfoFlagClass); // -info- selector
+ if (isClass) {
+ uint16 species = seeker.getUint16SEAt(10);
+ initializeClass(segMan, species, objectPosition);
+ }
+
+ reg_t reg = make_reg(segmentId, objectPosition);
Object *obj = scriptObjInit(reg);
// Copy base from species class, as we need its selector IDs
@@ -1213,10 +1148,22 @@ void Script::initializeObjectsSci11(SegManager *segMan, SegmentId segmentId, boo
#ifdef ENABLE_SCI32
void Script::initializeObjectsSci3(SegManager *segMan, SegmentId segmentId, bool applyScriptPatches) {
+ // SCI3 added all classes to the class table before initializing objects
SciSpan<const byte> seeker = getSci3ObjectsPointer();
+ while (seeker.getUint16SEAt(0) == SCRIPT_OBJECT_MAGIC_NUMBER) {
+ uint32 objectPosition = seeker - *_buf;
+ bool isClass = (seeker.getUint16SEAt(10) & kInfoFlagClass);
+ if (isClass) {
+ uint16 species = seeker.getUint16SEAt(4);
+ initializeClass(segMan, species, objectPosition);
+ }
+ seeker += seeker.getUint16SEAt(2);
+ }
+ seeker = getSci3ObjectsPointer();
while (seeker.getUint16SEAt(0) == SCRIPT_OBJECT_MAGIC_NUMBER) {
- Object *obj = scriptObjInit(make_reg32(segmentId, seeker - *_buf));
+ uint32 objectPosition = seeker - *_buf;
+ Object *obj = scriptObjInit(make_reg32(segmentId, objectPosition));
obj->setSuperClassSelector(segMan->getClassAddress(obj->getSuperClassSelector().getOffset(), SCRIPT_GET_LOCK, 0, applyScriptPatches));
seeker += seeker.getUint16SEAt(2);
}
diff --git a/engines/sci/engine/script.h b/engines/sci/engine/script.h
index ba9f5bc188e..e7a298798f3 100644
--- a/engines/sci/engine/script.h
+++ b/engines/sci/engine/script.h
@@ -176,13 +176,15 @@ public:
void initializeLocals(SegManager *segMan);
/**
- * Adds the script's classes to the segment manager's class table
+ * Adds a script's class to the segment manager's class table
* @param segMan A reference to the segment manager
+ * @param species The class number (index)
+ * @param position The position of the class (object) in the script
*/
- void initializeClasses(SegManager *segMan);
+ void initializeClass(SegManager *segMan, uint16 species, uint32 position);
/**
- * Initializes the script's objects (SCI0)
+ * Initializes the script's objects
* @param segMan A reference to the segment manager
* @param segmentId The script's segment id
* @param applyScriptPatches Apply patches for the script, if available
diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp
index d3d138e220f..3d2b37e4b10 100644
--- a/engines/sci/engine/seg_manager.cpp
+++ b/engines/sci/engine/seg_manager.cpp
@@ -1101,7 +1101,6 @@ int SegManager::instantiateScript(int scriptNum, bool applyScriptPatches) {
scr->load(scriptNum, _resMan, _scriptPatcher, applyScriptPatches);
scr->initializeLocals(this);
- scr->initializeClasses(this);
scr->initializeObjects(this, segmentId, applyScriptPatches);
#ifdef ENABLE_SCI32
g_sci->_guestAdditions->instantiateScriptHook(*scr);
Commit: bc4692563606f8ea798a4c1032872eae71833ae5
https://github.com/scummvm/scummvm/commit/bc4692563606f8ea798a4c1032872eae71833ae5
Author: sluicebox (22204938+sluicebox at users.noreply.github.com)
Date: 2024-07-11T22:28:17-07:00
Commit Message:
SCI: Add support for KQ5 FM-Towns save/restore UI
Changed paths:
engines/sci/engine/kernel_tables.h
engines/sci/engine/kfile.cpp
engines/sci/engine/workarounds.cpp
engines/sci/engine/workarounds.h
diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h
index b5dc5943192..63286383315 100644
--- a/engines/sci/engine/kernel_tables.h
+++ b/engines/sci/engine/kernel_tables.h
@@ -727,7 +727,7 @@ static SciKernelMapEntry s_kernelMap[] = {
#ifdef ENABLE_SCI32
{ "GetSaveFiles", kGetSaveFiles32, SIG_THRU_SCI21EARLY, SIGFOR_ALL, "rrr", NULL, NULL },
#endif
- { MAP_CALL(GetSaveFiles), SIG_EVERYWHERE, "rrr", NULL, NULL },
+ { MAP_CALL(GetSaveFiles), SIG_EVERYWHERE, "rrr", NULL, kGetSaveFiles_workarounds },
{ MAP_CALL(GetTime), SIG_EVERYWHERE, "(i)", NULL, NULL },
{ MAP_CALL(GlobalToLocal), SIG_SCI16, SIGFOR_ALL, "o", NULL, NULL },
#ifdef ENABLE_SCI32
diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp
index 1a84766f4e4..7e8db3a0be7 100644
--- a/engines/sci/engine/kfile.cpp
+++ b/engines/sci/engine/kfile.cpp
@@ -663,7 +663,7 @@ reg_t kFileIOWriteRaw(EngineState *s, int argc, reg_t *argv) {
reg_t kFileIOUnlink(EngineState *s, int argc, reg_t *argv) {
Common::String name = s->_segMan->getString(argv[0]);
Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
- bool result;
+ bool result = false;
// SQ4 floppy prepends /\ to the filenames
if (name.hasPrefix("/\\")) {
@@ -671,7 +671,7 @@ reg_t kFileIOUnlink(EngineState *s, int argc, reg_t *argv) {
name.deleteChar(0);
}
- if (name.hasPrefix("sq4sg.")) {
+ if (g_sci->getGameId() == GID_SQ4 && name.hasPrefix("sq4sg.")) {
// Special case for SQ4 floppy: This game has hardcoded save game names.
// They are named "sq4sg.xxx", where xxx is the virtual ID. We construct
// the appropriate save game name and delete it.
@@ -698,6 +698,17 @@ reg_t kFileIOUnlink(EngineState *s, int argc, reg_t *argv) {
result = saveFileMan->removeSavefile(wrappedName);
}
#endif
+ } else if (g_sci->getGameId() == GID_KQ5 &&
+ g_sci->getPlatform() == Common::kPlatformFMTowns &&
+ name.hasPrefix("a:\\KQ5sg.")) {
+ // KQ5 FM-Towns uses a custom save/restore UI in script 764.
+ // It directly deletes save files using a hard-coded path.
+ int saveNo = 0;
+ sscanf(name.c_str(), "a:\\KQ5sg.%d", &saveNo);
+ if (1 <= saveNo && saveNo <= 10) { // UI has ten buttons
+ name = g_sci->getSavegameName(saveNo);
+ result = saveFileMan->removeSavefile(name);
+ }
} else {
const Common::String wrappedName = g_sci->wrapFilename(name);
result = saveFileMan->removeSavefile(wrappedName);
@@ -807,8 +818,6 @@ reg_t kFileIOFindNext(EngineState *s, int argc, reg_t *argv) {
reg_t kFileIOExists(EngineState *s, int argc, reg_t *argv) {
Common::String name = s->_segMan->getString(argv[0]);
- bool exists = false;
-
if (g_sci->getGameId() == GID_PEPPER) {
// HACK: Special case for Pepper's Adventure in Time
// The game checks like crazy for the file CDAUDIO when entering the game menu.
@@ -820,36 +829,48 @@ reg_t kFileIOExists(EngineState *s, int argc, reg_t *argv) {
}
#ifdef ENABLE_SCI32
- if (isSaveCatalogue(name)) {
- return saveCatalogueExists(name) ? TRUE_REG : NULL_REG;
- }
-
- int findSaveNo = -1;
+ if (getSciVersion() >= SCI_VERSION_2) {
+ if (isSaveCatalogue(name)) {
+ return saveCatalogueExists(name) ? TRUE_REG : NULL_REG;
+ }
- if (g_sci->getGameId() == GID_LSL7 && name == "autosvsg.000") {
- // LSL7 checks to see if the autosave save exists when deciding whether
- // to go to the main menu or not on startup
- findSaveNo = kAutoSaveId;
- } else if (g_sci->getGameId() == GID_RAMA) {
- // RAMA checks to see if save game files exist before showing them in
- // the native save/load dialogue
- if (name == "autorama.sg") {
+ int findSaveNo = -1;
+ if (g_sci->getGameId() == GID_LSL7 && name == "autosvsg.000") {
+ // LSL7 checks to see if the autosave save exists when deciding whether
+ // to go to the main menu or not on startup
findSaveNo = kAutoSaveId;
- } else if (sscanf(name.c_str(), "ramasg.%d", &findSaveNo) == 1) {
- findSaveNo += kSaveIdShift;
+ } else if (g_sci->getGameId() == GID_RAMA) {
+ // RAMA checks to see if save game files exist before showing them in
+ // the native save/load dialogue
+ if (name == "autorama.sg") {
+ findSaveNo = kAutoSaveId;
+ } else if (sscanf(name.c_str(), "ramasg.%d", &findSaveNo) == 1) {
+ findSaveNo += kSaveIdShift;
+ }
}
- }
- if (findSaveNo != -1) {
- return g_sci->getSaveFileManager()->listSavefiles(g_sci->getSavegameName(findSaveNo)).empty() ? NULL_REG : TRUE_REG;
+ if (findSaveNo != -1) {
+ return g_sci->getSaveFileManager()->listSavefiles(g_sci->getSavegameName(findSaveNo)).empty() ? NULL_REG : TRUE_REG;
+ }
+ // TODO: It may apparently be worth caching the existence of
+ // phantsg.dir, and possibly even keeping it open persistently
}
#endif
- // TODO: It may apparently be worth caching the existence of
- // phantsg.dir, and possibly even keeping it open persistently
+ if (g_sci->getGameId() == GID_KQ5 && g_sci->getPlatform() == Common::kPlatformFMTowns) {
+ // KQ5 FM-Towns uses a custom save/restore UI in script 764.
+ // It directly tests for save files using a hard-coded path.
+ int saveNo = 0;
+ sscanf(name.c_str(), "a:\\KQ5sg.%d", &saveNo);
+ if (1 <= saveNo && saveNo <= 10) { // UI has ten buttons
+ Common::Array<SavegameDesc> saves;
+ listSavegames(saves);
+ return (findSavegame(saves, saveNo) != -1) ? TRUE_REG : NULL_REG;
+ }
+ }
// Check for regular file
- exists = Common::File::exists(Common::Path(name));
+ bool exists = Common::File::exists(Common::Path(name));
// Check for a savegame with the name
Common::SaveFileManager *saveFileMan = g_sci->getSaveFileManager();
@@ -1122,6 +1143,15 @@ reg_t kSaveGame(EngineState *s, int argc, reg_t *argv) {
// Jones has one save slot only
savegameId = 0;
break;
+ case GID_KQ5:
+ if (g_sci->getPlatform() == Common::kPlatformFMTowns) {
+ // KQ5 FM-Towns uses custom save/restore code.
+ // Use the provided id.
+ savegameId = virtualId;
+ // Use a default description, game passes path since it wasn't displayed.
+ game_description = Common::String::format("Save %d", savegameId);
+ }
+ break;
case GID_QFG3: {
// Auto-save system used by QFG3
reg_t autoSaveNameId;
@@ -1227,6 +1257,9 @@ reg_t kRestoreGame(EngineState *s, int argc, reg_t *argv) {
if (g_sci->getGameId() == GID_JONES) {
// Jones has one save slot only
savegameId = 0;
+ } else if (g_sci->getGameId() == GID_KQ5 && g_sci->getPlatform() == Common::kPlatformFMTowns) {
+ // KQ5 FM-Towns uses custom save/restore code.
+ // Use the provided id.
} else {
// Real call from script, we need to adjust ID
if ((savegameId < SAVEGAMEID_OFFICIALRANGE_START) || (savegameId > SAVEGAMEID_OFFICIALRANGE_END)) {
@@ -1286,6 +1319,10 @@ reg_t kCheckSaveGame(EngineState *s, int argc, reg_t *argv) {
uint savegameId = 0;
if (g_sci->getGameId() == GID_JONES) {
// Jones has one save slot only
+ } else if (g_sci->getGameId() == GID_KQ5 && g_sci->getPlatform() == Common::kPlatformFMTowns) {
+ // KQ5 FM-Towns uses custom save/restore code.
+ // Use the provided id.
+ savegameId = virtualId;
} else {
// Find saved game
if ((virtualId < SAVEGAMEID_OFFICIALRANGE_START) || (virtualId > SAVEGAMEID_OFFICIALRANGE_END))
diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp
index e1e44dd00d0..f95e658045a 100644
--- a/engines/sci/engine/workarounds.cpp
+++ b/engines/sci/engine/workarounds.cpp
@@ -359,6 +359,7 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = {
{ GID_KQ5, 25, 25, 0, "rm025", "doit", nullptr, 0, 0, { WORKAROUND_FAKE, 0 } }, // inside witch forest, when going to the room where the walking rock is
{ GID_KQ5, 55, 55, 0, "helpScript", "doit", nullptr, 0, 0, { WORKAROUND_FAKE, 0 } }, // when giving the tambourine to the monster in the labyrinth (only happens at one of the locations) - bug #5198
{ GID_KQ5, -1, 755, 0, "gcWin", "open", nullptr, -1, -1, { WORKAROUND_FAKE, 0 } }, // when entering control menu in the FM-Towns version
+ { GID_KQ5, -1, 764, 0, "trash", "select", nullptr, -1, -1, { WORKAROUND_FAKE, 0 } }, // when clicking delete button on save/restore dialog in the FM-Towns version
{ GID_KQ6, -1, 30, 0, "rats", "changeState", nullptr, 0, 5, { WORKAROUND_FAKE, 0 } }, // rats in the catacombs (temps 0-5, all temps!) - bugs #4958, #4998, #5017
{ GID_KQ6, 210, 210, 0, "rm210", "scriptCheck", nullptr, 0, 0, { WORKAROUND_FAKE, 1 } }, // using inventory in that room - bug #4953
{ GID_KQ6, 500, 500, 0, "rm500", "init", nullptr, 0, 0, { WORKAROUND_FAKE, 0 } }, // going to island of the beast
@@ -705,6 +706,12 @@ const SciWorkaroundEntry kGetCWD_workarounds[] = {
SCI_WORKAROUNDENTRY_TERMINATOR
};
+// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround
+const SciWorkaroundEntry kGetSaveFiles_workarounds[] = {
+ { GID_KQ5, -1, 764, 0, "trash", "select", nullptr, 0, 0, { WORKAROUND_FAKE, 0 } }, // FM-Towns version when clicking delete save button, save-catalog code passes buffers by value instead of address
+ SCI_WORKAROUNDENTRY_TERMINATOR
+};
+
// gameID, room,script,lvl, object-name, method-name, local-call-signature, index-range, workaround
const SciWorkaroundEntry kFileIOOpen_workarounds[] = {
{ GID_HOYLE5, -1, 64990, 0, "Restore", "doit", nullptr, 0, 0, { WORKAROUND_STILLCALL, 0 } }, // Missing second argument when checking for bridgesg.cat or poker.cat when showing restore dialog
diff --git a/engines/sci/engine/workarounds.h b/engines/sci/engine/workarounds.h
index 13772593b76..842be448854 100644
--- a/engines/sci/engine/workarounds.h
+++ b/engines/sci/engine/workarounds.h
@@ -81,6 +81,7 @@ extern const SciWorkaroundEntry kFrameOut_workarounds[];
extern const SciWorkaroundEntry kDeleteKey_workarounds[];
extern const SciWorkaroundEntry kGetAngle_workarounds[];
extern const SciWorkaroundEntry kGetCWD_workarounds[];
+extern const SciWorkaroundEntry kGetSaveFiles_workarounds[];
extern const SciWorkaroundEntry kGraphDrawLine_workarounds[];
extern const SciWorkaroundEntry kGraphSaveBox_workarounds[];
extern const SciWorkaroundEntry kGraphRestoreBox_workarounds[];
More information about the Scummvm-git-logs
mailing list