[Scummvm-git-logs] scummvm master -> c0ecf7cccd42340e82dd777cbf6eeef1204f83b7
neuromancer
noreply at scummvm.org
Tue Jun 23 19:52:13 UTC 2026
This automated email contains information about 4 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
7c2094af1f EEM: correct TRAVIS rendering for EEM1 Mac
904afc0b5e EEM: clue resolution in EEM1 Mac
267abe0a5e FREESCAPE: make sound timing more accurate in DOS sounds
c0ecf7cccd FREESCAPE: fix infinite loop in DOS/ZX sound handling
Commit: 7c2094af1f34aeba07deddd0a7259d9a8af62bfa
https://github.com/scummvm/scummvm/commit/7c2094af1f34aeba07deddd0a7259d9a8af62bfa
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-23T21:52:01+02:00
Commit Message:
EEM: correct TRAVIS rendering for EEM1 Mac
Changed paths:
engines/eem/eem.h
engines/eem/graphics.cpp
engines/eem/ui.cpp
diff --git a/engines/eem/eem.h b/engines/eem/eem.h
index 7c0ae46d08f..c90c52279f0 100644
--- a/engines/eem/eem.h
+++ b/engines/eem/eem.h
@@ -232,6 +232,8 @@ public:
/// Returns true if the player committed (SOLVE clicked), false on ESC.
bool doAccuseNotes();
+ void drawKDBalloonOverCurrentScreen(Common::String text);
+
/// `_KDHelp @ 1560:010a` + `_DisplayHint @ 1560:0009`.
void doHelp();
diff --git a/engines/eem/graphics.cpp b/engines/eem/graphics.cpp
index 3f616e3c70a..1832c6fcd95 100644
--- a/engines/eem/graphics.cpp
+++ b/engines/eem/graphics.cpp
@@ -638,48 +638,7 @@ void EEMEngine::doHelp() {
const Common::String raw = _mystery.textAt(chosenText);
Common::String text = parseString(raw, _playerName, _partner);
- Graphics::ManagedSurface ms(kScreenWidth, kScreenHeight,
- Graphics::PixelFormat::createFormatCLUT8());
- ms.clear();
- {
- Graphics::Surface *cur = g_system->lockScreen();
- if (cur) {
- ms.simpleBlitFrom(*cur);
- g_system->unlockScreen();
- }
- }
-
- const byte firstChar =
- text.empty() ? (byte)0 : (byte)text[0];
- uint16 bubNum = getKDTextBalloon(firstChar);
- if (firstChar >= '0' && firstChar <= '9')
- text.deleteChar(0);
- bubNum = fitBalloonToText(bubNum, text);
- Picture balloon;
- const bool haveBalloon =
- _balloonArchive.size() > (bubNum & 0x7F) &&
- _balloonArchive.loadEntry(bubNum & 0x7F, balloon);
-
- const int balloonX = 0x21;
- int balloonY = 1;
- if (haveBalloon && balloon.surface.h < 0x4e)
- balloonY = (0x50 - balloon.surface.h) / 2;
-
- if (haveBalloon) {
- const byte transp = (byte)(balloon.flags >> 8);
- ms.transBlitFrom(balloon.surface,
- Common::Point(balloonX, balloonY),
- (uint32)transp);
- }
-
- uint16 tx = 5, ty = 4, tw = 155;
- getBalloonInsets(bubNum, tx, ty, tw);
- _font.drawWordWrapped(&ms, balloonX + tx, balloonY + ty, tw, text,
- haveBalloon ? 0 : 0xF);
-
- g_system->copyRectToScreen(ms.getPixels(), ms.pitch,
- 0, 0, kScreenWidth, kScreenHeight);
- g_system->updateScreen();
+ drawKDBalloonOverCurrentScreen(text);
if (_audio && _mystery.kdTextIndex()) {
if (hintVoiceSlot >= 0)
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 2dd491ef025..300adf74dc3 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -49,6 +49,14 @@ const GallerySlot kGallerySlots[5] = {
{ 191, 90 } // 4
};
+const GallerySlot kMacGallerySlots[5] = {
+ { 133, 27 }, // 0
+ { 248, 27 }, // 1
+ { 363, 27 }, // 2
+ { 190, 173 }, // 3
+ { 306, 173 } // 4
+};
+
struct LondonApproachData {
uint16 videoId = 0;
uint16 videoX = 0;
@@ -346,23 +354,49 @@ const Picture *partnerFrameFor(DBDArchive &aniArchive, uint8 partner,
return &outAni[frameIdx];
}
+int scalePdaAnchor(int value, int target, int source) {
+ const bool negative = value < 0;
+ const int magnitude = negative ? -value : value;
+ const int scaled = (magnitude * target + source / 2) / source;
+ return negative ? -scaled : scaled;
+}
+
void blitPdaPartner(Graphics::ManagedSurface &dst, DBDArchive &aniArchive,
uint8 partner, const PdaPartnerSpec &spec,
- uint32 tickMs) {
+ uint32 tickMs, bool mac = false) {
Animation ani;
if (const Picture *fr = partnerFrameFor(aniArchive, partner, spec,
- tickMs, ani))
- blitAnimFrameAnchored(dst.surfacePtr(), *fr, spec.anchorX,
- spec.anchorY);
+ tickMs, ani)) {
+ if (mac) {
+ const int anchorX =
+ scalePdaAnchor(spec.anchorX, kMacScreenWidth, kScreenWidth);
+ const int anchorY =
+ scalePdaAnchor(spec.anchorY, kMacScreenHeight, kScreenHeight);
+ blitMacAnimFrameAnchored(dst.surfacePtr(), *fr, anchorX,
+ anchorY);
+ } else {
+ blitAnimFrameAnchored(dst.surfacePtr(), *fr, spec.anchorX,
+ spec.anchorY);
+ }
+ }
}
void blitPdaPartner(Graphics::Surface *screen, DBDArchive &aniArchive,
uint8 partner, const PdaPartnerSpec &spec,
- uint32 tickMs) {
+ uint32 tickMs, bool mac = false) {
Animation ani;
if (const Picture *fr = partnerFrameFor(aniArchive, partner, spec,
- tickMs, ani))
- blitAnimFrameAnchored(screen, *fr, spec.anchorX, spec.anchorY);
+ tickMs, ani)) {
+ if (mac) {
+ const int anchorX =
+ scalePdaAnchor(spec.anchorX, kMacScreenWidth, kScreenWidth);
+ const int anchorY =
+ scalePdaAnchor(spec.anchorY, kMacScreenHeight, kScreenHeight);
+ blitMacAnimFrameAnchored(screen, *fr, anchorX, anchorY);
+ } else {
+ blitAnimFrameAnchored(screen, *fr, spec.anchorX, spec.anchorY);
+ }
+ }
}
constexpr Common::Rect kEndingPrevPageRect(Common::Point(0, 0), 28, kScreenHeight);
@@ -380,6 +414,21 @@ constexpr Common::Rect kPdaPagePrevRect(Common::Point(226, 174), 21, 16);
constexpr Common::Rect kPdaHelp2Rect(Common::Point(267, 174), 21, 16);
constexpr Common::Rect kPdaLondonCloseRect(Common::Point(0, 0), 66, 79);
+// Mac _NotebookRect and _NoteButtons table at 0x177e. The coordinates are
+// native QuickDraw rects; keep them exact instead of deriving them from the
+// rounded DOS scaler.
+constexpr Common::Rect kMacNotebookTextRect(Common::Point(125, 23), 336, 269);
+constexpr Common::Rect kMacPdaNotebookRect(Common::Point(214, 334), 34, 30);
+constexpr Common::Rect kMacPdaHelpRect(Common::Point(149, 334), 35, 30);
+constexpr Common::Rect kMacPdaGalleryRect(Common::Point(251, 334), 34, 30);
+constexpr Common::Rect kMacPdaPartnerHeadHintRect(Common::Point(13, 154), 57, 57);
+constexpr Common::Rect kMacPdaAccuseRect(Common::Point(288, 334), 33, 30);
+constexpr Common::Rect kMacPdaPageNextRect(Common::Point(325, 334), 33, 30);
+constexpr Common::Rect kMacPdaPagePrevRect(Common::Point(362, 334), 33, 30);
+constexpr Common::Rect kMacPdaPartnerFootMapRect(Common::Point(11, 340), 80, 44);
+constexpr Common::Rect kMacPdaSiteRect(Common::Point(56, 213), 34, 48);
+constexpr Common::Rect kMacPdaHelp2Rect(Common::Point(427, 334), 33, 30);
+
constexpr uint16 kProfilePickerRevealPic = 0x105;
constexpr int kProfilePickerRevealX = 0x3e;
constexpr int kProfilePickerRevealY = 0xb3;
@@ -421,16 +470,46 @@ constexpr uint16 kScrapbookExtraMaxRecords = 16;
constexpr uint kRestoredContentFirstMystery = 1;
constexpr uint kRestoredContentLastMystery = 0x18;
+Common::Rect pdaControlRect(const EEMEngine *vm, const Common::Rect &rect) {
+ if (!vm || !vm->isMacintosh())
+ return rect;
+ if (rect == kPdaNotebookRect)
+ return kMacPdaNotebookRect;
+ if (rect == kPdaHelpRect)
+ return kMacPdaHelpRect;
+ if (rect == kPdaGalleryRect)
+ return kMacPdaGalleryRect;
+ if (rect == kPdaPartnerHeadHintRect)
+ return kMacPdaPartnerHeadHintRect;
+ if (rect == kPdaAccuseRect)
+ return kMacPdaAccuseRect;
+ if (rect == kPdaPageNextRect)
+ return kMacPdaPageNextRect;
+ if (rect == kPdaPagePrevRect)
+ return kMacPdaPagePrevRect;
+ if (rect == kPdaPartnerFootMapRect)
+ return kMacPdaPartnerFootMapRect;
+ if (rect == kPdaSiteRect)
+ return kMacPdaSiteRect;
+ if (rect == kPdaHelp2Rect)
+ return kMacPdaHelp2Rect;
+ return vm->scaleRect(rect);
+}
+
+bool notebookButtonAt(const EEMEngine *vm, int x, int y) {
+ return pdaControlRect(vm, kPdaHelpRect).contains(x, y) ||
+ pdaControlRect(vm, kPdaGalleryRect).contains(x, y) ||
+ pdaControlRect(vm, kPdaPartnerHeadHintRect).contains(x, y) ||
+ pdaControlRect(vm, kPdaAccuseRect).contains(x, y) ||
+ pdaControlRect(vm, kPdaPageNextRect).contains(x, y) ||
+ pdaControlRect(vm, kPdaPagePrevRect).contains(x, y) ||
+ pdaControlRect(vm, kPdaHelp2Rect).contains(x, y) ||
+ pdaControlRect(vm, kPdaPartnerFootMapRect).contains(x, y) ||
+ pdaControlRect(vm, kPdaSiteRect).contains(x, y);
+}
+
bool notebookButtonAt(int x, int y) {
- return kPdaHelpRect.contains(x, y) ||
- kPdaGalleryRect.contains(x, y) ||
- kPdaPartnerHeadHintRect.contains(x, y) ||
- kPdaAccuseRect.contains(x, y) ||
- kPdaPageNextRect.contains(x, y) ||
- kPdaPagePrevRect.contains(x, y) ||
- kPdaHelp2Rect.contains(x, y) ||
- kPdaPartnerFootMapRect.contains(x, y) ||
- kPdaSiteRect.contains(x, y);
+ return notebookButtonAt(nullptr, x, y);
}
bool galleryButtonAt(int x, int y) {
@@ -442,6 +521,15 @@ bool galleryButtonAt(int x, int y) {
kPdaPartnerHeadHintRect.contains(x, y);
}
+bool galleryButtonAt(const EEMEngine *vm, int x, int y) {
+ return pdaControlRect(vm, kPdaSiteRect).contains(x, y) ||
+ pdaControlRect(vm, kPdaPartnerFootMapRect).contains(x, y) ||
+ pdaControlRect(vm, kPdaAccuseRect).contains(x, y) ||
+ pdaControlRect(vm, kPdaNotebookRect).contains(x, y) ||
+ pdaControlRect(vm, kPdaHelpRect).contains(x, y) ||
+ pdaControlRect(vm, kPdaPartnerHeadHintRect).contains(x, y);
+}
+
bool rectListContains(const Common::Array<Common::Rect> &rects, int x, int y) {
for (uint i = 0; i < rects.size(); i++) {
if (rects[i].contains(x, y))
@@ -2596,9 +2684,11 @@ void EEMEngine::doNotebook() {
if (_music && _voiceOn && notebookFromSite)
_music->playMus(30, /* loop= */ false);
+ if (isMacintosh())
+ setSitePalette(0);
drawNotebookFrame(page);
Common::Point mouse = g_system->getEventManager()->getMousePos();
- setInteractiveMouseCursor(notebookButtonAt(mouse.x, mouse.y));
+ setInteractiveMouseCursor(notebookButtonAt(this, mouse.x, mouse.y));
if (isLondon() && _music && _voiceOn) {
while (notebookFromSite && _music->isPlaying() && !shouldQuit()) {
@@ -2625,7 +2715,7 @@ void EEMEngine::doNotebook() {
break;
}
if (ev.type == Common::EVENT_MOUSEMOVE) {
- setInteractiveMouseCursor(notebookButtonAt(ev.mouse.x,
+ setInteractiveMouseCursor(notebookButtonAt(this, ev.mouse.x,
ev.mouse.y));
}
if (ev.type == Common::EVENT_KEYDOWN) {
@@ -2646,35 +2736,43 @@ void EEMEngine::doNotebook() {
}
}
if (ev.type == Common::EVENT_LBUTTONDOWN) {
- if (kPdaSiteRect.contains(ev.mouse.x, ev.mouse.y) ||
+ if (pdaControlRect(this, kPdaSiteRect).contains(ev.mouse.x,
+ ev.mouse.y) ||
(isLondon() &&
kPdaLondonCloseRect.contains(ev.mouse.x, ev.mouse.y))) {
_nextScreen = kScreenSite;
exitFlag = true;
break; // back to site
}
- if (kPdaPartnerFootMapRect.contains(ev.mouse.x, ev.mouse.y)) {
+ if (pdaControlRect(this, kPdaPartnerFootMapRect)
+ .contains(ev.mouse.x, ev.mouse.y)) {
_nextScreen = kScreenMapAlt;
exitFlag = true;
break;
}
- if (kPdaPartnerHeadHintRect.contains(ev.mouse.x, ev.mouse.y)) {
+ if (pdaControlRect(this, kPdaPartnerHeadHintRect)
+ .contains(ev.mouse.x, ev.mouse.y)) {
setInteractiveMouseCursor(false);
doHelp();
+ if (isMacintosh())
+ setSitePalette(0);
dirty = true;
continue;
}
- if (kPdaAccuseRect.contains(ev.mouse.x, ev.mouse.y)) {
+ if (pdaControlRect(this, kPdaAccuseRect).contains(ev.mouse.x,
+ ev.mouse.y)) {
_nextScreen = kScreenAccuse;
exitFlag = true;
break;
}
- if (kPdaGalleryRect.contains(ev.mouse.x, ev.mouse.y)) {
+ if (pdaControlRect(this, kPdaGalleryRect).contains(ev.mouse.x,
+ ev.mouse.y)) {
_nextScreen = kScreenGallery;
exitFlag = true;
break;
}
- if (kPdaHelpRect.contains(ev.mouse.x, ev.mouse.y)) {
+ if (pdaControlRect(this, kPdaHelpRect).contains(ev.mouse.x,
+ ev.mouse.y)) {
if (isLondon()) {
_nextScreen = kScreenMapAlt;
exitFlag = true;
@@ -2682,23 +2780,30 @@ void EEMEngine::doNotebook() {
}
setInteractiveMouseCursor(false);
doInterfaceHelp(0);
+ if (isMacintosh())
+ setSitePalette(0);
dirty = true;
continue;
}
- if (kPdaPagePrevRect.contains(ev.mouse.x, ev.mouse.y)) {
+ if (pdaControlRect(this, kPdaPagePrevRect)
+ .contains(ev.mouse.x, ev.mouse.y)) {
if (page > 0)
page--;
dirty = true;
continue;
}
- if (kPdaPageNextRect.contains(ev.mouse.x, ev.mouse.y)) {
+ if (pdaControlRect(this, kPdaPageNextRect)
+ .contains(ev.mouse.x, ev.mouse.y)) {
page++;
dirty = true;
continue;
}
- if (kPdaHelp2Rect.contains(ev.mouse.x, ev.mouse.y)) {
+ if (pdaControlRect(this, kPdaHelp2Rect).contains(ev.mouse.x,
+ ev.mouse.y)) {
setInteractiveMouseCursor(false);
doInterfaceHelp(0);
+ if (isMacintosh())
+ setSitePalette(0);
dirty = true;
continue;
}
@@ -2712,7 +2817,8 @@ void EEMEngine::doNotebook() {
drawNotebookFrame(page);
lastDraw = now;
mouse = g_system->getEventManager()->getMousePos();
- setInteractiveMouseCursor(notebookButtonAt(mouse.x, mouse.y));
+ setInteractiveMouseCursor(notebookButtonAt(this, mouse.x,
+ mouse.y));
}
if (now - gizmoLastTick >= kChooserCycleMillis) {
gizmoLastTick = now;
@@ -2760,8 +2866,12 @@ Common::String EEMEngine::notebookNoteText(uint clueId, const byte *ni,
void EEMEngine::drawNotebookFrame(int &page) {
const Common::Rect kNotebookRect(78, 12, 288, 152);
+ const Common::Rect notebookRect =
+ isMacintosh() ? kMacNotebookTextRect : kNotebookRect;
+ const int sw = screenWidth();
+ const int sh = screenHeight();
- Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
+ Graphics::ManagedSurface scratch(sw, sh,
Graphics::PixelFormat::createFormatCLUT8());
scratch.clear();
@@ -2770,7 +2880,7 @@ void EEMEngine::drawNotebookFrame(int &page) {
scratch.simpleBlitFrom(frame.surface);
blitPdaPartner(scratch, _aniArchive, _partner, kPdaNotebookPartner,
- g_system->getMillis());
+ g_system->getMillis(), isMacintosh());
// `_DrawNotes` walks `_NoteIndex` for current page; word-wraps each
// found clue in `_NotebookRect`. Selected = color 0x3c.
@@ -2792,10 +2902,10 @@ void EEMEngine::drawNotebookFrame(int &page) {
const uint16 niCount = isLondon()
? londonCount : _mystery.noteIndexCount();
- const int kRectX = kNotebookRect.left;
- const int kRectY = kNotebookRect.top;
- const int kRectW = kNotebookRect.width();
- const int kRectH = kNotebookRect.height();
+ const int kRectX = notebookRect.left;
+ const int kRectY = notebookRect.top;
+ const int kRectW = notebookRect.width();
+ const int kRectH = notebookRect.height();
int clueCursor = 0;
Common::Array<int> pageStarts;
@@ -2863,7 +2973,7 @@ void EEMEngine::drawNotebookFrame(int &page) {
}
g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
- 0, 0, kScreenWidth, kScreenHeight);
+ 0, 0, sw, sh);
g_system->updateScreen();
}
@@ -2878,6 +2988,8 @@ void EEMEngine::doGallery() {
}
CursorMan.showMouse(true);
+ if (isMacintosh())
+ setSitePalette(0);
Picture galBg;
const bool haveBg = _picsArchive.getPicture(0x3f, galBg);
@@ -2897,7 +3009,7 @@ void EEMEngine::doGallery() {
drawGalleryFrame(gd, num, slotRects, slotSuspect);
Common::Point mouse = g_system->getEventManager()->getMousePos();
- setInteractiveMouseCursor(galleryButtonAt(mouse.x, mouse.y) ||
+ setInteractiveMouseCursor(galleryButtonAt(this, mouse.x, mouse.y) ||
gallerySlotAt(slotRects, slotSuspect,
mouse.x, mouse.y));
uint32 lastDraw = g_system->getMillis();
@@ -2914,7 +3026,7 @@ void EEMEngine::doGallery() {
return;
}
if (ev.type == Common::EVENT_MOUSEMOVE) {
- setInteractiveMouseCursor(galleryButtonAt(ev.mouse.x,
+ setInteractiveMouseCursor(galleryButtonAt(this, ev.mouse.x,
ev.mouse.y) ||
gallerySlotAt(slotRects,
slotSuspect,
@@ -2939,27 +3051,32 @@ void EEMEngine::doGallery() {
// [6] (226,247) â generic exit (0x638)
// [7] ( 7,177) â MAP = NextScreen=2 (0x5f7)
// [8] ( 35,111) â SITE = NextScreen=3 (0x5e4)
- if (kPdaSiteRect.contains(ev.mouse.x, ev.mouse.y)) {
+ if (pdaControlRect(this, kPdaSiteRect).contains(ev.mouse.x,
+ ev.mouse.y)) {
_nextScreen = kScreenSite;
exitFlag = true;
break;
}
- if (kPdaPartnerFootMapRect.contains(ev.mouse.x, ev.mouse.y)) {
+ if (pdaControlRect(this, kPdaPartnerFootMapRect)
+ .contains(ev.mouse.x, ev.mouse.y)) {
_nextScreen = kScreenMapAlt;
exitFlag = true;
break;
}
- if (kPdaAccuseRect.contains(ev.mouse.x, ev.mouse.y)) {
+ if (pdaControlRect(this, kPdaAccuseRect).contains(ev.mouse.x,
+ ev.mouse.y)) {
_nextScreen = kScreenAccuse;
exitFlag = true;
break;
}
- if (kPdaNotebookRect.contains(ev.mouse.x, ev.mouse.y)) {
+ if (pdaControlRect(this, kPdaNotebookRect).contains(ev.mouse.x,
+ ev.mouse.y)) {
_nextScreen = kScreenNotebook;
exitFlag = true;
break;
}
- if (kPdaHelpRect.contains(ev.mouse.x, ev.mouse.y)) {
+ if (pdaControlRect(this, kPdaHelpRect).contains(ev.mouse.x,
+ ev.mouse.y)) {
if (isLondon()) {
_nextScreen = kScreenMapAlt;
exitFlag = true;
@@ -2967,12 +3084,17 @@ void EEMEngine::doGallery() {
}
setInteractiveMouseCursor(false);
doInterfaceHelp(0);
+ if (isMacintosh())
+ setSitePalette(0);
lastDraw = 0;
continue;
}
- if (kPdaPartnerHeadHintRect.contains(ev.mouse.x, ev.mouse.y)) {
+ if (pdaControlRect(this, kPdaPartnerHeadHintRect)
+ .contains(ev.mouse.x, ev.mouse.y)) {
setInteractiveMouseCursor(false);
doHelp();
+ if (isMacintosh())
+ setSitePalette(0);
lastDraw = 0;
continue;
}
@@ -2987,7 +3109,7 @@ void EEMEngine::doGallery() {
drawGalleryFrame(gd, num, slotRects, slotSuspect);
lastDraw = g_system->getMillis();
mouse = g_system->getEventManager()->getMousePos();
- setInteractiveMouseCursor(galleryButtonAt(mouse.x,
+ setInteractiveMouseCursor(galleryButtonAt(this, mouse.x,
mouse.y) ||
gallerySlotAt(slotRects,
slotSuspect,
@@ -3008,7 +3130,7 @@ void EEMEngine::doGallery() {
drawGalleryFrame(gd, num, slotRects, slotSuspect);
lastDraw = now;
mouse = g_system->getEventManager()->getMousePos();
- setInteractiveMouseCursor(galleryButtonAt(mouse.x, mouse.y) ||
+ setInteractiveMouseCursor(galleryButtonAt(this, mouse.x, mouse.y) ||
gallerySlotAt(slotRects, slotSuspect,
mouse.x, mouse.y));
}
@@ -3025,7 +3147,8 @@ void EEMEngine::doGallery() {
bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
const Picture &galBg, bool haveBg) {
const bool floppyMI = isFloppy();
- const bool compactMI = floppyMI || isMacintosh();
+ const bool mac = isMacintosh();
+ const bool compactMI = floppyMI || mac;
const byte *suspect = compactMI
? _mystery.floppySuspectEntry(suspectIdx)
: gd + suspectIdx * 0x46;
@@ -3038,8 +3161,14 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
setInteractiveMouseCursor(false);
- const int rx = 78, ry = 93;
- const int rw = 288 - 78, rh = 152 - 93;
+ const Common::Rect noteRectBase(78, 93, 288, 152);
+ const Common::Rect noteRect = mac ? scaleRect(noteRectBase) : noteRectBase;
+ const int rx = noteRect.left;
+ const int ry = noteRect.top;
+ const int rw = noteRect.width();
+ const int rh = noteRect.height();
+ const int sw = screenWidth();
+ const int sh = screenHeight();
const int lineH = _font.getFontHeight();
const uint clueMax = compactMI ? clueCount : 30u;
const byte *ni = _mystery.noteIndex();
@@ -3054,19 +3183,24 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
bool isFirstShow = true;
while (!back && !shouldQuit()) {
- Graphics::ManagedSurface ms(kScreenWidth, kScreenHeight,
+ Graphics::ManagedSurface ms(sw, sh,
Graphics::PixelFormat::createFormatCLUT8());
ms.clear();
if (haveBg)
ms.simpleBlitFrom(galBg.surface);
blitPdaPartner(ms, _aniArchive, _partner, kPdaGalleryPartner,
- g_system->getMillis());
+ g_system->getMillis(), mac);
Picture detail;
if (_picsArchive.getPicture(detailPic, detail)) {
- const byte transp = (byte)(detail.flags >> 8);
- ms.transBlitFrom(detail.surface,
- Common::Point(0x94, 0x0f), transp);
+ const Common::Point detailPos =
+ mac ? scalePoint(0x94, 0x0f) : Common::Point(0x94, 0x0f);
+ if (mac)
+ blitMacMaskedSurface(ms.surfacePtr(), detail,
+ detailPos.x, detailPos.y);
+ else
+ ms.transBlitFrom(detail.surface, detailPos,
+ (uint32)(byte)(detail.flags >> 8));
}
// Walk clues from pageStart; defer overflow to next page unless
@@ -3135,7 +3269,7 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
rx, ry + rh + 2, MAX<int>(8, rw), 0x3C);
}
g_system->copyRectToScreen(ms.getPixels(), ms.pitch,
- 0, 0, kScreenWidth, kScreenHeight);
+ 0, 0, sw, sh);
g_system->updateScreen();
// Drain the LBUTTONDOWN that opened MoreInfo (first page only).
@@ -3176,56 +3310,61 @@ bool EEMEngine::moreInfo(const byte *gd, uint suspectIdx,
debugC(2, kDebugGfx,
"MoreInfo click (%d,%d) hasMore=%d hasPrev=%d",
mx, my, (int)hasMore, (int)hasPrev);
- if (kPdaNotebookRect.contains(mx, my)) {
+ if (pdaControlRect(this, kPdaNotebookRect).contains(mx, my)) {
_nextScreen = kScreenNotebook;
exitGallery = true;
back = true;
break;
}
- if (kPdaAccuseRect.contains(mx, my)) {
+ if (pdaControlRect(this, kPdaAccuseRect).contains(mx, my)) {
_nextScreen = kScreenAccuse;
exitGallery = true;
back = true;
break;
}
- if (kPdaPartnerFootMapRect.contains(mx, my)) {
+ if (pdaControlRect(this, kPdaPartnerFootMapRect).contains(mx, my)) {
_nextScreen = kScreenMapAlt;
exitGallery = true;
back = true;
break;
}
- if (isLondon() && kPdaHelpRect.contains(mx, my)) {
+ if (isLondon() &&
+ pdaControlRect(this, kPdaHelpRect).contains(mx, my)) {
_nextScreen = kScreenMapAlt;
exitGallery = true;
back = true;
break;
}
- if (kPdaHelpRect.contains(mx, my) ||
- kPdaHelp2Rect.contains(mx, my)) {
+ if (pdaControlRect(this, kPdaHelpRect).contains(mx, my) ||
+ pdaControlRect(this, kPdaHelp2Rect).contains(mx, my)) {
setInteractiveMouseCursor(false);
doInterfaceHelp(0);
+ if (mac)
+ setSitePalette(0);
redraw = true;
break;
}
- if (kPdaGalleryRect.contains(mx, my)) {
+ if (pdaControlRect(this, kPdaGalleryRect).contains(mx, my)) {
// Case 2: close MoreInfo.
back = true;
break;
}
- if (kPdaPageNextRect.contains(mx, my)) {
+ if (pdaControlRect(this, kPdaPageNextRect).contains(mx, my)) {
if (hasMore)
advance = true;
break;
}
- if (kPdaPagePrevRect.contains(mx, my)) {
+ if (pdaControlRect(this, kPdaPagePrevRect).contains(mx, my)) {
if (hasPrev)
prev = true;
break;
}
- if (kPdaPartnerHeadHintRect.contains(mx, my)) {
+ if (pdaControlRect(this, kPdaPartnerHeadHintRect).contains(mx, my)) {
// Case 3: _KDHelp.
setInteractiveMouseCursor(false);
doHelp();
+ if (mac)
+ setSitePalette(0);
redraw = true;
break;
}
@@ -3276,8 +3415,11 @@ void EEMEngine::drawGalleryFrame(const byte *gd, uint8 numSuspects,
Common::Array<int> &slotSuspect) {
Picture galBg;
const bool haveBg = _picsArchive.getPicture(0x3f, galBg);
+ const bool mac = isMacintosh();
+ const int sw = screenWidth();
+ const int sh = screenHeight();
- Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
+ Graphics::ManagedSurface scratch(sw, sh,
Graphics::PixelFormat::createFormatCLUT8());
scratch.clear();
@@ -3285,12 +3427,12 @@ void EEMEngine::drawGalleryFrame(const byte *gd, uint8 numSuspects,
scratch.simpleBlitFrom(galBg.surface);
blitPdaPartner(scratch, _aniArchive, _partner, kPdaGalleryPartner,
- g_system->getMillis());
+ g_system->getMillis(), mac);
const bool floppy = isFloppy();
const bool compactGallery = floppy || isMacintosh();
const GallerySlot * const slots =
- floppy ? kFloppyGallerySlots : kGallerySlots;
+ mac ? kMacGallerySlots : (floppy ? kFloppyGallerySlots : kGallerySlots);
for (uint i = 0; i < numSuspects && i < Mystery::kGalleryCap; i++) {
slotRects[i] = Common::Rect();
slotSuspect[i] = -1;
@@ -3314,24 +3456,28 @@ void EEMEngine::drawGalleryFrame(const byte *gd, uint8 numSuspects,
continue;
const int placeX = s.x;
- const int placeY = s.y + (0x48 - portrait.surface.h);
- const int w = MIN<int>(portrait.surface.w, kScreenWidth - placeX);
- const int h = MIN<int>(portrait.surface.h, kScreenHeight - placeY);
+ const int placeY = mac ? s.y : s.y + (0x48 - portrait.surface.h);
+ const int w = MIN<int>(portrait.surface.w, sw - placeX);
+ const int h = MIN<int>(portrait.surface.h, sh - placeY);
if (w <= 0 || h <= 0)
continue;
- scratch.transBlitFrom(portrait.surface,
- Common::Point(placeX, placeY),
- (uint32)(byte)(portrait.flags >> 8));
+ if (mac)
+ blitMacMaskedSurface(scratch.surfacePtr(), portrait,
+ placeX, placeY);
+ else
+ scratch.transBlitFrom(portrait.surface,
+ Common::Point(placeX, placeY),
+ (uint32)(byte)(portrait.flags >> 8));
slotRects[i] = Common::Rect(placeX, placeY,
placeX + w, placeY + h);
slotSuspect[i] = (int)i;
} else {
// Undiscovered placeholder â small framed "?" box.
- const int phW = 0x40;
- const int phH = 0x48;
+ const int phW = mac ? 0x72 : 0x40;
+ const int phH = mac ? 0x90 : 0x48;
const int phX = s.x;
const int phY = s.y;
- if (phX + phW <= kScreenWidth && phY + phH <= kScreenHeight) {
+ if (phX + phW <= sw && phY + phH <= sh) {
scratch.fillRect(Common::Rect(phX, phY,
phX + phW, phY + phH), 0x20);
scratch.frameRect(Common::Rect(phX, phY,
@@ -3346,7 +3492,7 @@ void EEMEngine::drawGalleryFrame(const byte *gd, uint8 numSuspects,
}
g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
- 0, 0, kScreenWidth, kScreenHeight);
+ 0, 0, sw, sh);
g_system->updateScreen();
}
@@ -4140,14 +4286,18 @@ void EEMEngine::accuseRebuildPagination(const AccuseNotesCtx &ctx) {
}
void EEMEngine::accuseDrawScreen(const AccuseNotesCtx &ctx) {
- Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
+ const bool mac = isMacintosh();
+ const int sw = screenWidth();
+ const int sh = screenHeight();
+
+ Graphics::ManagedSurface scratch(sw, sh,
Graphics::PixelFormat::createFormatCLUT8());
scratch.clear();
if (ctx.haveBg)
scratch.simpleBlitFrom(ctx.accuseBg->surface);
blitPdaPartner(scratch, _aniArchive, _partner, kPdaGalleryPartner,
- g_system->getMillis());
+ g_system->getMillis(), mac);
Common::Array<Common::Rect> &slotRects = *ctx.slotRects;
Common::Array<uint> &slotClues = *ctx.slotClues;
@@ -4196,16 +4346,17 @@ void EEMEngine::accuseDrawScreen(const AccuseNotesCtx &ctx) {
: (remaining == 1 ? "clue" : "clues");
const Common::String counter =
Common::String::format("%u %s", remaining, clueWord);
- _font.drawString(&scratch, counter, 209, 11, 100, 0x0F);
+ _font.drawString(&scratch, counter, scaleX(209), scaleY(11),
+ scaleX(100), 0x0F);
if (*ctx.numPages > 1) {
_font.drawString(&scratch,
Common::String::format("p%d/%d", *ctx.page + 1, *ctx.numPages),
- ctx.rectX, 11, 60, 0x0F);
+ ctx.rectX, scaleY(11), scaleX(60), 0x0F);
}
g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
- 0, 0, kScreenWidth, kScreenHeight);
+ 0, 0, sw, sh);
g_system->updateScreen();
}
@@ -4242,15 +4393,22 @@ bool EEMEngine::doAccuseNotes() {
found.push_back(i);
}
- const int rectX = 79;
- const int rectY = 27;
- const int rectW = 304 - 79;
- const int rectH = 159 - 27;
+ const Common::Rect noteRectBase(79, 27, 304, 159);
+ const Common::Rect noteRect =
+ isMacintosh() ? scaleRect(noteRectBase) : noteRectBase;
+ const int rectX = noteRect.left;
+ const int rectY = noteRect.top;
+ const int rectW = noteRect.width();
+ const int rectH = noteRect.height();
- const Common::Rect kBtnSolve (180, 174, 201, 190); // [4]
- const Common::Rect kBtnPageNext(204, 174, 224, 190); // [5]
- const Common::Rect kBtnPagePrev(226, 174, 247, 190); // [6]
- const Common::Rect kBtnPartner ( 5, 80, 44, 110); // [3]
+ const Common::Rect btnSolve =
+ pdaControlRect(this, kPdaAccuseRect); // [4]
+ const Common::Rect btnPageNext =
+ pdaControlRect(this, kPdaPageNextRect); // [5]
+ const Common::Rect btnPagePrev =
+ pdaControlRect(this, kPdaPagePrevRect); // [6]
+ const Common::Rect btnPartner =
+ pdaControlRect(this, kPdaPartnerHeadHintRect); // [3]
// Per-page slot rects + their clue IDs (for click hit-testing).
Common::Array<Common::Rect> slotRects;
@@ -4287,8 +4445,9 @@ bool EEMEngine::doAccuseNotes() {
accuseRebuildPagination(ctx);
accuseDrawScreen(ctx);
Common::Point mouse = g_system->getEventManager()->getMousePos();
- setInteractiveMouseCursor(notebookButtonAt(mouse.x, mouse.y) ||
- kPdaNotebookRect.contains(mouse.x, mouse.y) ||
+ setInteractiveMouseCursor(notebookButtonAt(this, mouse.x, mouse.y) ||
+ pdaControlRect(this, kPdaNotebookRect)
+ .contains(mouse.x, mouse.y) ||
rectListContains(slotRects, mouse.x, mouse.y));
while (!shouldQuit()) {
@@ -4302,8 +4461,9 @@ bool EEMEngine::doAccuseNotes() {
}
if (ev.type == Common::EVENT_MOUSEMOVE) {
setInteractiveMouseCursor(
- notebookButtonAt(ev.mouse.x, ev.mouse.y) ||
- kPdaNotebookRect.contains(ev.mouse.x, ev.mouse.y) ||
+ notebookButtonAt(this, ev.mouse.x, ev.mouse.y) ||
+ pdaControlRect(this, kPdaNotebookRect)
+ .contains(ev.mouse.x, ev.mouse.y) ||
rectListContains(slotRects, ev.mouse.x, ev.mouse.y));
}
if (ev.type == Common::EVENT_KEYDOWN) {
@@ -4325,53 +4485,59 @@ bool EEMEngine::doAccuseNotes() {
if (ev.type == Common::EVENT_LBUTTONDOWN) {
const int mx = ev.mouse.x;
const int my = ev.mouse.y;
- if (kPdaSiteRect.contains(mx, my)) {
+ if (pdaControlRect(this, kPdaSiteRect).contains(mx, my)) {
_nextScreen = kScreenSite;
return false;
}
- if (kPdaPartnerFootMapRect.contains(mx, my)) {
+ if (pdaControlRect(this, kPdaPartnerFootMapRect)
+ .contains(mx, my)) {
_nextScreen = kScreenMapAlt;
return false;
}
- if (kPdaNotebookRect.contains(mx, my)) {
+ if (pdaControlRect(this, kPdaNotebookRect).contains(mx, my)) {
_nextScreen = kScreenNotebook;
return false;
}
- if (kPdaGalleryRect.contains(mx, my)) {
+ if (pdaControlRect(this, kPdaGalleryRect).contains(mx, my)) {
_nextScreen = kScreenGallery;
return false;
}
- if (isLondon() && kPdaHelpRect.contains(mx, my)) {
+ if (isLondon() &&
+ pdaControlRect(this, kPdaHelpRect).contains(mx, my)) {
_nextScreen = kScreenMapAlt;
return false;
}
- if (kPdaHelpRect.contains(mx, my) ||
- kPdaHelp2Rect.contains(mx, my)) {
+ if (pdaControlRect(this, kPdaHelpRect).contains(mx, my) ||
+ pdaControlRect(this, kPdaHelp2Rect).contains(mx, my)) {
setInteractiveMouseCursor(false);
doInterfaceHelp(0);
+ if (isMacintosh())
+ setSitePalette(0);
dirty = true;
continue;
}
- if (kBtnPageNext.contains(mx, my)) {
+ if (btnPageNext.contains(mx, my)) {
if (page + 1 < numPages) {
page++;
dirty = true;
}
continue;
}
- if (kBtnPagePrev.contains(mx, my)) {
+ if (btnPagePrev.contains(mx, my)) {
if (page > 0) {
page--;
dirty = true;
}
continue;
}
- if (kBtnPartner.contains(mx, my)) {
+ if (btnPartner.contains(mx, my)) {
doHelp();
+ if (isMacintosh())
+ setSitePalette(0);
dirty = true;
continue;
}
- if (kBtnSolve.contains(mx, my)) {
+ if (btnSolve.contains(mx, my)) {
uint selected = 0;
for (uint i = 0; i < found.size(); i++) {
if (_mystery._noteSelected[found[i]])
@@ -4417,6 +4583,67 @@ bool EEMEngine::doAccuseNotes() {
return false;
}
+void EEMEngine::drawKDBalloonOverCurrentScreen(Common::String text) {
+ const bool mac = isMacintosh();
+ const int sw = screenWidth();
+ const int sh = screenHeight();
+
+ Graphics::ManagedSurface ms(sw, sh,
+ Graphics::PixelFormat::createFormatCLUT8());
+ ms.clear();
+ Graphics::Surface *cur = g_system->lockScreen();
+ if (cur) {
+ ms.simpleBlitFrom(*cur);
+ g_system->unlockScreen();
+ }
+
+ const byte firstChar = text.empty() ? (byte)0 : (byte)text[0];
+ uint16 bubNum = getKDTextBalloon(firstChar);
+ if (firstChar >= '0' && firstChar <= '9')
+ text.deleteChar(0);
+ bubNum = fitBalloonToText(bubNum, text);
+
+ Picture balloon;
+ const bool haveBalloon =
+ _balloonArchive.size() > (bubNum & 0x7F) &&
+ _balloonArchive.loadEntry(bubNum & 0x7F, balloon);
+
+ const int balloonX = mac ? scaleX(0x21) : 0x21;
+ const int topBandH = mac ? scaleY(0x50) : 0x50;
+ const int centeredMaxH = mac ? scaleY(0x4e) : 0x4e;
+ int balloonY = mac ? scaleY(1) : 1;
+ if (haveBalloon && balloon.surface.h < centeredMaxH)
+ balloonY = (topBandH - balloon.surface.h) / 2;
+
+ MacSpritePaletteMap macPaletteMap = {0x00, 0xFF};
+ if (mac)
+ macPaletteMap = getMacSpritePaletteMap();
+
+ if (haveBalloon) {
+ if (mac) {
+ blitMacMaskedSurface(ms.surfacePtr(), balloon, balloonX,
+ balloonY, false, macPaletteMap);
+ } else {
+ ms.transBlitFrom(balloon.surface,
+ Common::Point(balloonX, balloonY),
+ (uint32)(byte)(balloon.flags >> 8));
+ }
+ }
+
+ uint16 tx = 5, ty = 4, tw = 155;
+ getBalloonInsets(bubNum, tx, ty, tw);
+ const EEMFont &dialogFont =
+ (mac && _dialogFont.isLoaded()) ? _dialogFont : _font;
+ if (dialogFont.isLoaded()) {
+ const byte textColor =
+ mac ? macPaletteMap.black : (haveBalloon ? 0 : 0xF);
+ dialogFont.drawWordWrapped(&ms, balloonX + tx, balloonY + ty,
+ tw, text, textColor);
+ }
+
+ copyToScreen(ms);
+}
+
void EEMEngine::doAccuse() {
if (!_mystery.isLoaded() || !_font.isLoaded())
return;
@@ -4487,42 +4714,7 @@ void EEMEngine::doAccuse() {
_playerName, _partner);
}
if (!entryText.empty()) {
- Graphics::ManagedSurface ms(kScreenWidth, kScreenHeight,
- Graphics::PixelFormat::createFormatCLUT8());
- ms.clear();
- Graphics::Surface *cur = g_system->lockScreen();
- if (cur) {
- ms.simpleBlitFrom(*cur);
- g_system->unlockScreen();
- }
- const byte firstChar = (byte)entryText[0];
- uint16 bubNum = getKDTextBalloon(firstChar);
- if (firstChar >= '0' && firstChar <= '9')
- entryText.deleteChar(0);
- bubNum = fitBalloonToText(bubNum, entryText);
- Picture balloon;
- const bool haveBalloon =
- _balloonArchive.size() > (bubNum & 0x7F) &&
- _balloonArchive.loadEntry(bubNum & 0x7F, balloon);
- const int balloonX = 0x21;
- int balloonY = 1;
- if (haveBalloon && balloon.surface.h < 0x4e)
- balloonY = (0x50 - balloon.surface.h) / 2;
- if (haveBalloon) {
- const byte transp = (byte)(balloon.flags >> 8);
- ms.transBlitFrom(balloon.surface,
- Common::Point(balloonX, balloonY),
- (uint32)transp);
- }
- uint16 tx = 5, ty = 4, tw = 155;
- getBalloonInsets(bubNum, tx, ty, tw);
- if (_font.isLoaded()) {
- _font.drawWordWrapped(&ms, balloonX + tx, balloonY + ty,
- tw, entryText, haveBalloon ? 0 : 0xF);
- }
- g_system->copyRectToScreen(ms.getPixels(), ms.pitch,
- 0, 0, kScreenWidth, kScreenHeight);
- g_system->updateScreen();
+ drawKDBalloonOverCurrentScreen(entryText);
if (_audio) {
if (entryVoiceOverride != 0xFFFF)
_audio->spoolSound(entryVoiceOverride);
@@ -4597,46 +4789,7 @@ void EEMEngine::doAccuse() {
}
}
- Graphics::ManagedSurface ms(kScreenWidth, kScreenHeight,
- Graphics::PixelFormat::createFormatCLUT8());
- ms.clear();
- Graphics::Surface *cur = g_system->lockScreen();
- if (cur) {
- ms.simpleBlitFrom(*cur);
- g_system->unlockScreen();
- }
- const byte firstChar =
- hint.empty() ? (byte)0 : (byte)hint[0];
- uint16 bubNum = getKDTextBalloon(firstChar);
-
- if (firstChar >= '0' && firstChar <= '9')
- hint.deleteChar(0);
- bubNum = fitBalloonToText(bubNum, hint);
- Picture balloon;
- const bool haveBalloon =
- _balloonArchive.size() > (bubNum & 0x7F) &&
- _balloonArchive.loadEntry(bubNum & 0x7F, balloon);
- const int balloonX = 0x21;
- int balloonY = 1;
- if (haveBalloon && balloon.surface.h < 0x4e)
- balloonY = (0x50 - balloon.surface.h) / 2;
- if (haveBalloon) {
- const byte transp = (byte)(balloon.flags >> 8);
- ms.transBlitFrom(balloon.surface,
- Common::Point(balloonX, balloonY),
- (uint32)transp);
- }
- uint16 tx = 5, ty = 4, tw = 155;
- getBalloonInsets(bubNum, tx, ty, tw);
- if (_font.isLoaded()) {
- _font.drawWordWrapped(&ms, balloonX + tx,
- balloonY + ty, tw, hint,
- haveBalloon ? 0 : 0xF);
- }
- g_system->copyRectToScreen(ms.getPixels(), ms.pitch,
- 0, 0, kScreenWidth, kScreenHeight);
- g_system->updateScreen();
-
+ drawKDBalloonOverCurrentScreen(hint);
if (_audio && kdIdx)
_audio->sayKDDigital(kdIdx, 3, _partner);
@@ -4647,9 +4800,6 @@ void EEMEngine::doAccuse() {
return;
}
- Picture accuseBg;
- const bool haveAccuseBg = _picsArchive.getPicture(0x3f, accuseBg);
-
Common::Array<Common::Rect> slotRects;
Common::Array<int> slotSuspect;
slotRects.resize(num);
@@ -4667,58 +4817,10 @@ void EEMEngine::doAccuse() {
Common::String hint =
parseString(raw ? raw : "", _playerName, _partner);
if (!hint.empty()) {
- const byte firstChar =
- hint.empty() ? (byte)0 : (byte)hint[0];
- uint16 bubNum = getKDTextBalloon(firstChar);
- // Strip digit prefix (`_DisplayAlibi @ 1df2:0163`).
- if (firstChar >= '0' && firstChar <= '9')
- hint.deleteChar(0);
- bubNum = fitBalloonToText(bubNum, hint);
- Picture balloon;
- const bool haveBalloon =
- _balloonArchive.size() > (bubNum & 0x7F) &&
- _balloonArchive.loadEntry(bubNum & 0x7F, balloon);
-
- const int balloonX = 0x21;
- int balloonY = 1;
- if (haveBalloon && balloon.surface.h < 0x4e)
- balloonY = (0x50 - balloon.surface.h) / 2;
-
// Render gallery first so the snapshot includes partner.
drawAccuseGallery(num, gd, /* highlighted= */ -1,
slotRects, slotSuspect);
-
- Graphics::ManagedSurface ms(kScreenWidth, kScreenHeight,
- Graphics::PixelFormat::createFormatCLUT8());
- ms.clear();
- {
- Graphics::Surface *cur = g_system->lockScreen();
- if (cur) {
- ms.simpleBlitFrom(*cur);
- g_system->unlockScreen();
- } else if (haveAccuseBg) {
- // Fallback if lockScreen failed.
- ms.simpleBlitFrom(accuseBg.surface);
- }
- }
- if (haveBalloon) {
- const byte transp = (byte)(balloon.flags >> 8);
- ms.transBlitFrom(balloon.surface,
- Common::Point(balloonX, balloonY),
- transp);
- }
- uint16 tx = 5;
- uint16 ty = 4;
- uint16 tw = 155;
- getBalloonInsets(bubNum, tx, ty, tw);
- if (_font.isLoaded()) {
- _font.drawWordWrapped(&ms, balloonX + tx,
- balloonY + ty, tw, hint,
- haveBalloon ? 0 : 0xF);
- }
- g_system->copyRectToScreen(ms.getPixels(), ms.pitch,
- 0, 0, kScreenWidth, kScreenHeight);
- g_system->updateScreen();
+ drawKDBalloonOverCurrentScreen(hint);
if (_audio)
_audio->sayKDDigital(kdIdx, 4, _partner);
waitForInput(8000);
@@ -5678,8 +5780,11 @@ void EEMEngine::drawAccuseGallery(uint8 numSuspects, const byte *gd,
Common::Array<int> &slotSuspect) {
Picture accuseBg;
const bool haveAccuseBg = _picsArchive.getPicture(0x3f, accuseBg);
+ const bool mac = isMacintosh();
+ const int sw = screenWidth();
+ const int sh = screenHeight();
- Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
+ Graphics::ManagedSurface scratch(sw, sh,
Graphics::PixelFormat::createFormatCLUT8());
scratch.clear();
if (haveAccuseBg)
@@ -5687,7 +5792,8 @@ void EEMEngine::drawAccuseGallery(uint8 numSuspects, const byte *gd,
// Partner drawn first; defensive (no slot overlap).
blitPdaPartner(scratch, _aniArchive, _partner, kPdaGalleryPartner,
- g_system->getMillis());
+ g_system->getMillis(), mac);
+ const GallerySlot * const slots = mac ? kMacGallerySlots : kGallerySlots;
for (uint i = 0; i < numSuspects && i < Mystery::kGalleryCap; i++) {
slotRects[i] = Common::Rect();
@@ -5699,7 +5805,7 @@ void EEMEngine::drawAccuseGallery(uint8 numSuspects, const byte *gd,
continue;
if (_mystery._inGallery[phys] == 0)
continue;
- const GallerySlot &s = kGallerySlots[phys];
+ const GallerySlot &s = slots[phys];
const byte *entry = isMacintosh()
? _mystery.floppySuspectEntry(i)
@@ -5714,14 +5820,18 @@ void EEMEngine::drawAccuseGallery(uint8 numSuspects, const byte *gd,
continue;
const int placeX = s.x;
- const int placeY = s.y + (0x48 - portrait.surface.h);
- const int w = MIN<int>(portrait.surface.w, kScreenWidth - placeX);
- const int h = MIN<int>(portrait.surface.h, kScreenHeight - placeY);
+ const int placeY = mac ? s.y : s.y + (0x48 - portrait.surface.h);
+ const int w = MIN<int>(portrait.surface.w, sw - placeX);
+ const int h = MIN<int>(portrait.surface.h, sh - placeY);
if (w <= 0 || h <= 0)
continue;
- scratch.transBlitFrom(portrait.surface,
- Common::Point(placeX, placeY),
- (uint32)(byte)(portrait.flags >> 8));
+ if (mac)
+ blitMacMaskedSurface(scratch.surfacePtr(), portrait,
+ placeX, placeY);
+ else
+ scratch.transBlitFrom(portrait.surface,
+ Common::Point(placeX, placeY),
+ (uint32)(byte)(portrait.flags >> 8));
slotRects[i] = Common::Rect(placeX, placeY,
placeX + w, placeY + h);
slotSuspect[i] = (int)i;
@@ -5736,7 +5846,7 @@ void EEMEngine::drawAccuseGallery(uint8 numSuspects, const byte *gd,
}
g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
- 0, 0, kScreenWidth, kScreenHeight);
+ 0, 0, sw, sh);
g_system->updateScreen();
}
Commit: 904afc0b5e28dfe4b65a8d2dda652aaedbf5738b
https://github.com/scummvm/scummvm/commit/904afc0b5e28dfe4b65a8d2dda652aaedbf5738b
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-23T21:52:01+02:00
Commit Message:
EEM: clue resolution in EEM1 Mac
Changed paths:
engines/eem/mystery.cpp
engines/eem/ui.cpp
diff --git a/engines/eem/mystery.cpp b/engines/eem/mystery.cpp
index d8dbd96a2eb..b6e5994613c 100644
--- a/engines/eem/mystery.cpp
+++ b/engines/eem/mystery.cpp
@@ -189,7 +189,10 @@ static void normalizeMacMystery(Common::Array<byte> &data) {
swapU16Range(data, section[4], section[7]); // notebook index
normalizeMacGalleryData(data, section[5], section[6]);
swapU16Range(data, section[8], MIN<uint>(section[8] + 12, section[9]));
- swapU16Range(data, section[9], data.size()); // solved clue block
+
+ // Solved outro block: u8 count followed by Mac-native dialog records.
+ // Keep it byte-exact; displayFloppyDialogRecords() parses widened fields
+ // as big-endian for Mac.
}
uint16 Mystery::readU16(uint offset) const {
diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 300adf74dc3..27a88aa3691 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -399,11 +399,73 @@ void blitPdaPartner(Graphics::Surface *screen, DBDArchive &aniArchive,
}
}
+bool playMacScrapbookPartnerAnimation(EEMEngine *vm,
+ Graphics::ManagedSurface &base) {
+ if (!vm || !vm->isMacintosh())
+ return false;
+
+ g_system->copyRectToScreen(base.getPixels(), base.pitch, 0, 0,
+ base.w, base.h);
+ g_system->updateScreen();
+
+ const uint animId =
+ (vm->getPartnerIndex() == kPartnerJake) ? 0x17 : 0x3b;
+ const int anchorX =
+ (vm->getPartnerIndex() == kPartnerJake) ? 0x144 : 0x146;
+ const int anchorY =
+ (vm->getPartnerIndex() == kPartnerJake) ? 0x0d8 : 0x0d6;
+ Animation anim;
+ if (!vm->getAni().loadAnimation(animId, anim) || anim.empty())
+ return false;
+
+ const uint kFrameDelayMs = 140;
+ bool skip = false;
+ for (uint frame = 0; frame < anim.size() && !vm->shouldQuit() && !skip;
+ frame++) {
+ Graphics::ManagedSurface scratch(base.w, base.h,
+ Graphics::PixelFormat::createFormatCLUT8());
+ scratch.simpleBlitFrom(*base.surfacePtr());
+
+ // The Mac executable drives these endgame partner cells with the
+ // same script used by the case-intro desk animation.
+ const uint cell = partnerFrameAtTick(0x17, (uint)anim.size(),
+ frame * kFrameDelayMs);
+ blitMacAnimFrameAnchored(scratch.surfacePtr(), anim[cell],
+ anchorX, anchorY);
+ g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+ 0, 0, scratch.w, scratch.h);
+ g_system->updateScreen();
+
+ const uint32 wakeup = g_system->getMillis() + kFrameDelayMs;
+ while (g_system->getMillis() < wakeup && !vm->shouldQuit() &&
+ !skip) {
+ Common::Event ev;
+ while (g_system->getEventManager()->pollEvent(ev)) {
+ if (ev.type == Common::EVENT_QUIT ||
+ ev.type == Common::EVENT_RETURN_TO_LAUNCHER)
+ return true;
+ if (ev.type == Common::EVENT_KEYDOWN ||
+ ev.type == Common::EVENT_LBUTTONDOWN) {
+ skip = true;
+ break;
+ }
+ }
+ g_system->updateScreen();
+ g_system->delayMillis(10);
+ }
+ }
+
+ return true;
+}
+
constexpr Common::Rect kEndingPrevPageRect(Common::Point(0, 0), 28, kScreenHeight);
constexpr Common::Rect kEndingNextPageRect(Common::Point(292, 0), 28, kScreenHeight);
constexpr uint16 kFloppyEndingBackgroundPic = 0x8b;
constexpr uint16 kFirstTryBadgePic = 0x205;
constexpr Common::Point kFirstTryBadgePos(0x1e, 9);
+constexpr uint kMacMysteryDataTableOffset = 0x08cd;
+constexpr uint kMacMysteryDataMysteryCount = 55;
+constexpr uint kMacMysteryDataEndingCount = 55;
constexpr Common::Rect kPdaHelpRect(Common::Point(93, 174), 22, 16);
constexpr Common::Rect kPdaNotebookRect(Common::Point(134, 174), 21, 16);
@@ -568,11 +630,15 @@ bool gallerySlotAt(const Common::Array<Common::Rect> &rects,
}
const byte *advanceFloppyDialogRecords(const byte *rec, uint count,
- const byte *end) {
+ const byte *end, bool mac = false) {
+ if (!rec || (!end && count != 0))
+ return nullptr;
+ const uint headerLen = mac ? 14 : 11;
+ const uint textCountOffset = mac ? 13 : 10;
for (uint i = 0; i < count; i++) {
- if (!rec || rec + 11 > end)
+ if (rec + headerLen > end)
return nullptr;
- const uint len = 11u + rec[10];
+ const uint len = headerLen + rec[textCountOffset];
if (rec + len > end)
return nullptr;
rec += len;
@@ -617,6 +683,45 @@ void copyToScreen(Graphics::ManagedSurface &scratch) {
g_system->updateScreen();
}
+bool loadMacEndingBlob(uint num, Common::Array<byte> &out) {
+ if (num >= kMacMysteryDataEndingCount)
+ return false;
+
+ Common::File f;
+ if (!f.open(Common::Path("MysteryData"))) {
+ warning("doShowEnding: cannot open MysteryData");
+ return false;
+ }
+
+ const uint entry = kMacMysteryDataMysteryCount + num;
+ const uint32 tableOff = kMacMysteryDataTableOffset + entry * 8;
+ if (tableOff + 8 > (uint32)f.size()) {
+ warning("doShowEnding: MysteryData ending index %u out of range",
+ num);
+ return false;
+ }
+
+ f.seek(tableOff);
+ const uint32 offset = f.readUint32BE();
+ const uint32 size = f.readUint32BE();
+ if (size < 16 || offset > (uint32)f.size() ||
+ size > (uint32)f.size() - offset) {
+ warning("doShowEnding: MysteryData ending %u is invalid "
+ "(off=0x%08x size=0x%08x)", num, offset, size);
+ return false;
+ }
+
+ out.resize(size);
+ f.seek(offset);
+ if (f.read(out.data(), size) != size) {
+ warning("doShowEnding: short read on MysteryData ending %u", num);
+ out.clear();
+ return false;
+ }
+
+ return true;
+}
+
void cycleChooserPalette() {
cyclePaletteRange(kChooserCycleStart, kChooserCycleEnd);
}
@@ -1407,44 +1512,60 @@ void EEMEngine::doNewPlayer() {
}
// `_DisplayEnding @ 1df2:0548` + `_DisplayEndingPage @ 1df2:044c`
int EEMEngine::doShowEnding(uint num, bool firstPage) {
- const Common::String fname = Common::String::format("E%u.BIN", num);
- Common::File f;
- if (!f.open(Common::Path(fname))) {
- warning("doShowEnding: %s missing", fname.c_str());
- return 0;
- }
- const uint32 size = f.size();
- if (size < 2) {
- warning("doShowEnding: %s too small (%u bytes)",
- fname.c_str(), size);
- return 0;
- }
- Common::Array<byte> buf(size);
- if (f.read(buf.data(), size) != size) {
- warning("doShowEnding: %s short read", fname.c_str());
- return 0;
+ Common::Array<byte> buf;
+ uint32 size = 0;
+ const bool macEnding = isMacintosh();
+
+ if (macEnding) {
+ if (!loadMacEndingBlob(num, buf))
+ return 0;
+ size = (uint32)buf.size();
+ } else {
+ const Common::String fname = Common::String::format("E%u.BIN", num);
+ Common::File f;
+ if (!f.open(Common::Path(fname))) {
+ warning("doShowEnding: %s missing", fname.c_str());
+ return 0;
+ }
+ size = f.size();
+ if (size < 2) {
+ warning("doShowEnding: %s too small (%u bytes)",
+ fname.c_str(), size);
+ return 0;
+ }
+ buf.resize(size);
+ if (f.read(buf.data(), size) != size) {
+ warning("doShowEnding: %s short read", fname.c_str());
+ return 0;
+ }
}
EEMFont tinyFont;
- const bool haveTinyFont = tinyFont.load(Common::Path("TINY.FNT"));
- if (!haveTinyFont)
+ const bool haveTinyFont =
+ !macEnding && tinyFont.load(Common::Path("TINY.FNT"));
+ if (!macEnding && !haveTinyFont)
warning("doShowEnding: TINY.FNT failed to load â falling back");
setSitePalette(0);
CursorMan.showMouse(true);
const bool floppyEnding = isFloppy();
+ const int sw = screenWidth();
+ const int sh = screenHeight();
+ const Common::Rect endingPrevRect =
+ macEnding ? scaleRect(kEndingPrevPageRect) : kEndingPrevPageRect;
+ const Common::Rect endingNextRect =
+ macEnding ? scaleRect(kEndingNextPageRect) : kEndingNextPageRect;
uint pageOffsets[8];
const uint pageOffsetCap =
(uint)(sizeof(pageOffsets) / sizeof(pageOffsets[0]));
uint validPages = 0;
- if (floppyEnding) {
- // Floppy `E<num>.BIN` starts with:
+ if (floppyEnding || macEnding) {
+ // Floppy `E<num>.BIN` and Mac `MysteryData[55 + num]` start with:
// u8 type, 3 bytes of title metadata, char title[], u8 pageCount
- // followed by pages:
- // u8 overlayCount, N * { u16 picNum, u16 x, u8 y },
- // u16 x1, y1, x2, y2, char text[].
+ // followed by pages. Mac stores overlay pic/x words big-endian and
+ // native-coordinate text rects in little-endian order.
uint titleEnd = 4;
while (titleEnd < size && buf[titleEnd] != 0)
titleEnd++;
@@ -1502,10 +1623,10 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
bool exitLoop = false;
bool dirty = true;
const Common::Point mousePos = g_system->getEventManager()->getMousePos();
- setInteractiveMouseCursor(kEndingPrevPageRect.contains(mousePos.x,
- mousePos.y) ||
- kEndingNextPageRect.contains(mousePos.x,
- mousePos.y));
+ setInteractiveMouseCursor(endingPrevRect.contains(mousePos.x,
+ mousePos.y) ||
+ endingNextRect.contains(mousePos.x,
+ mousePos.y));
while (!shouldQuit() && !exitLoop) {
if (dirty) {
const uint off = pageOffsets[pageIdx];
@@ -1513,13 +1634,14 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
uint16 y1 = 0;
uint16 x2 = 0;
const char *raw = nullptr;
- Graphics::ManagedSurface scratch(kScreenWidth, kScreenHeight,
+ Graphics::ManagedSurface scratch(sw, sh,
Graphics::PixelFormat::createFormatCLUT8());
scratch.clear();
- if (floppyEnding) {
+ if (floppyEnding || macEnding) {
Picture bg;
- if (_picsArchive.getPicture(kFloppyEndingBackgroundPic, bg))
+ if (floppyEnding &&
+ _picsArchive.getPicture(kFloppyEndingBackgroundPic, bg))
scratch.simpleBlitFrom(bg.surface);
uint cursor = off;
@@ -1529,14 +1651,22 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
for (uint i = 0; i < overlayCount; i++) {
if (cursor + 5 > size)
break;
- const uint16 picNum = READ_LE_UINT16(buf.data() + cursor);
- const uint16 px = READ_LE_UINT16(buf.data() + cursor + 2);
+ const uint16 picNum = macEnding
+ ? READ_BE_UINT16(buf.data() + cursor)
+ : READ_LE_UINT16(buf.data() + cursor);
+ const uint16 px = macEnding
+ ? READ_BE_UINT16(buf.data() + cursor + 2)
+ : READ_LE_UINT16(buf.data() + cursor + 2);
const byte py = buf[cursor + 4];
Picture overlay;
if (_picsArchive.getPicture(picNum, overlay)) {
- const byte transp = (byte)(overlay.flags >> 8);
- scratch.transBlitFrom(overlay.surface,
- Common::Point(px, py), transp);
+ if (macEnding)
+ blitMacMaskedSurface(scratch.surfacePtr(),
+ overlay, px, py);
+ else
+ scratch.transBlitFrom(overlay.surface,
+ Common::Point(px, py),
+ (uint32)(byte)(overlay.flags >> 8));
}
cursor += 5;
}
@@ -1547,6 +1677,8 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
x2 = READ_LE_UINT16(buf.data() + cursor + 4);
(void)READ_LE_UINT16(buf.data() + cursor + 6);
raw = (const char *)buf.data() + cursor + 8;
+ if (macEnding && (byte)*raw == 0xd9)
+ raw++;
} else {
if (off + 10 >= size)
break;
@@ -1572,13 +1704,18 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
const EEMFont &renderFont = haveTinyFont ? tinyFont : _font;
if (renderFont.isLoaded() && x2 > x1) {
- const int textW = MIN<int>((int)x2 - (int)x1, kScreenWidth - (int)x1);
+ const int textW = MIN<int>((int)x2 - (int)x1,
+ sw - (int)x1);
+ MacSpritePaletteMap macPaletteMap = {0x00, 0xFF};
+ if (macEnding)
+ macPaletteMap = getMacSpritePaletteMap();
renderFont.drawWordWrapped(&scratch, (int)x1, (int)y1,
- textW, text, 0);
+ textW, text,
+ macEnding ? macPaletteMap.black : 0);
}
g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
- 0, 0, kScreenWidth, kScreenHeight);
+ 0, 0, sw, sh);
g_system->updateScreen();
dirty = false;
}
@@ -1593,8 +1730,8 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
}
if (ev.type == Common::EVENT_MOUSEMOVE)
setInteractiveMouseCursor(
- kEndingPrevPageRect.contains(ev.mouse.x, ev.mouse.y) ||
- kEndingNextPageRect.contains(ev.mouse.x, ev.mouse.y));
+ endingPrevRect.contains(ev.mouse.x, ev.mouse.y) ||
+ endingNextRect.contains(ev.mouse.x, ev.mouse.y));
if (ev.type == Common::EVENT_KEYDOWN) {
switch (ev.kbd.keycode) {
case Common::KEYCODE_ESCAPE:
@@ -1633,9 +1770,9 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
}
if (ev.type == Common::EVENT_LBUTTONDOWN) {
setInteractiveMouseCursor(
- kEndingPrevPageRect.contains(ev.mouse.x, ev.mouse.y) ||
- kEndingNextPageRect.contains(ev.mouse.x, ev.mouse.y));
- if (kEndingPrevPageRect.contains(ev.mouse.x, ev.mouse.y)) {
+ endingPrevRect.contains(ev.mouse.x, ev.mouse.y) ||
+ endingNextRect.contains(ev.mouse.x, ev.mouse.y));
+ if (endingPrevRect.contains(ev.mouse.x, ev.mouse.y)) {
if (pageIdx > 0) {
pageIdx--;
dirty = true;
@@ -1643,7 +1780,7 @@ int EEMEngine::doShowEnding(uint num, bool firstPage) {
direction = -1;
exitLoop = true;
}
- } else if (kEndingNextPageRect.contains(ev.mouse.x, ev.mouse.y)) {
+ } else if (endingNextRect.contains(ev.mouse.x, ev.mouse.y)) {
if (pageIdx + 1 < validPages) {
pageIdx++;
dirty = true;
@@ -5116,53 +5253,76 @@ void EEMEngine::doAccuse() {
advanceChainStageAfterSolve(mn);
- Graphics::Surface *blk = g_system->lockScreen();
- if (blk) {
- memset(blk->getPixels(), 0, kScreenWidth * kScreenHeight);
- g_system->unlockScreen();
- }
// `_DisplayCorrect` win background = `_BuildBackground(scene, 0x42, 0x14)`
// (frame PIC 0x3d + scene at 0x42,0x14, palette scene+1). EEM1 CD uses
// scene 5; EEM2/London uses scene 0x1b.
const uint winScene = isLondon() ? 0x1b : 5;
setSitePalette(winScene + 1);
+ const int screenW = screenWidth();
+ const int screenH = screenHeight();
+ Graphics::ManagedSurface scratch(screenW, screenH,
+ Graphics::PixelFormat::createFormatCLUT8());
+ scratch.clear();
Picture frame, scene;
- if (_picsArchive.loadEntry(0x3d, frame)) {
- g_system->copyRectToScreen(frame.surface.getPixels(),
- frame.surface.pitch, 0, 0,
- frame.surface.w, frame.surface.h);
- }
+ if (_picsArchive.loadEntry(0x3d, frame))
+ scratch.simpleBlitFrom(frame.surface);
if (winScene < _sitesArchive.size() &&
_sitesArchive.loadEntry(winScene, scene)) {
- const int sx = 0x42, sy = 0x14;
- const int sw = MIN<int>(scene.surface.w, kScreenWidth - sx);
- const int sh = MIN<int>(scene.surface.h, kScreenHeight - sy);
+ const int sx = scaleX(0x42);
+ const int sy = scaleY(0x14);
+ const int sw = MIN<int>(scene.surface.w, screenW - sx);
+ const int sh = MIN<int>(scene.surface.h, screenH - sy);
if (sw > 0 && sh > 0)
- g_system->copyRectToScreen(scene.surface.getPixels(),
- scene.surface.pitch, sx, sy,
- sw, sh);
- }
-
- if (Graphics::Surface *screen = g_system->lockScreen()) {
- blitPdaPartner(screen, _aniArchive, _partner,
- kPdaGalleryPartner, g_system->getMillis());
- g_system->unlockScreen();
+ scratch.copyRectToSurface(scene.surface.getPixels(),
+ scene.surface.pitch, sx, sy,
+ sw, sh);
}
+ blitPdaPartner(scratch, _aniArchive, _partner,
+ kPdaGalleryPartner, g_system->getMillis(),
+ isMacintosh());
+ g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
+ 0, 0, screenW, screenH);
g_system->updateScreen();
if (_music && _voiceOn)
_music->playMus(5, /* loop= */ false);
const byte *solved = _mystery.solvedClueBlock();
- if (solved)
+ if (isMacintosh() && solved) {
+ const byte *bufBase = _mystery.blobAt(0);
+ const byte *end = bufBase ? bufBase + _mystery.dataSize()
+ : nullptr;
+ const uint count = solved[0];
+ const byte *records = solved + 1;
+ const bool validChain =
+ advanceFloppyDialogRecords(records, count, end,
+ /* mac= */ true) != nullptr;
+ if (!validChain) {
+ warning("doAccuse: malformed Mac solved chain");
+ } else if (count > 3) {
+ const uint beforeScrapbook = count - 3;
+ const byte *tail =
+ advanceFloppyDialogRecords(records, beforeScrapbook,
+ end, /* mac= */ true);
+ if (tail) {
+ displayFloppyDialogRecords(records, beforeScrapbook, 1);
+ playMacScrapbookPartnerAnimation(this, scratch);
+ displayFloppyDialogRecords(tail, 3, 1);
+ }
+ } else {
+ displayFloppyDialogRecords(records, count, 1);
+ }
+ } else if (solved) {
displayClue(solved);
+ }
if (_music && _voiceOn)
_music->stop();
- playAnm(Common::Path(isLondon() ? "SCRAP.ANM" : "SCRAPBK.ANI"), 120,
- /* holdLastFrame= */ false);
-
- displayScrapbookExtra(mn);
+ if (!isMacintosh()) {
+ playAnm(Common::Path(isLondon() ? "SCRAP.ANM" : "SCRAPBK.ANI"),
+ 120, /* holdLastFrame= */ false);
+ displayScrapbookExtra(mn);
+ }
doShowEnding(mn);
Commit: 267abe0a5e90b7cbcce6799b2f2f0a754fcfa35c
https://github.com/scummvm/scummvm/commit/267abe0a5e90b7cbcce6799b2f2f0a754fcfa35c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-23T21:52:01+02:00
Commit Message:
FREESCAPE: make sound timing more accurate in DOS sounds
Changed paths:
engines/freescape/sound/dos.cpp
diff --git a/engines/freescape/sound/dos.cpp b/engines/freescape/sound/dos.cpp
index ddd0fba5bca..ab3a84e4ddc 100644
--- a/engines/freescape/sound/dos.cpp
+++ b/engines/freescape/sound/dos.cpp
@@ -148,8 +148,13 @@ uint16 SoundDOS::playSoundDOSSpeaker(uint16 frequencyStart, soundSpeakerFx *spea
uint8 frequencyDuration = speakerFxInfo->frequencyDuration;
int16 freq = frequencyStart;
- int waveDurationMultipler = 1800;
- int waveDuration = waveDurationMultipler * (frequencyDuration + 1);
+ // The DOS build advances the speaker sequencer from its timer IRQ, which
+ // the executable reprograms to ~300 Hz (PIT channel 0 reload = 3977, i.e.
+ // 1193182/3977). Each step holds its frequency for frequencyDuration timer
+ // ticks; the original 8-bit counter makes a duration of 0 wrap to 256 ticks.
+ const int kTimerHz = 300;
+ int durationTicks = frequencyDuration ? frequencyDuration : 256;
+ int waveDuration = (1000000 / kTimerHz) * durationTicks; // microseconds
while (true) {
if (freq > 0) {
Commit: c0ecf7cccd42340e82dd777cbf6eeef1204f83b7
https://github.com/scummvm/scummvm/commit/c0ecf7cccd42340e82dd777cbf6eeef1204f83b7
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-23T21:52:01+02:00
Commit Message:
FREESCAPE: fix infinite loop in DOS/ZX sound handling
Changed paths:
engines/freescape/sound/dos.cpp
engines/freescape/sound/zx.cpp
diff --git a/engines/freescape/sound/dos.cpp b/engines/freescape/sound/dos.cpp
index ab3a84e4ddc..cb9dbdf83e0 100644
--- a/engines/freescape/sound/dos.cpp
+++ b/engines/freescape/sound/dos.cpp
@@ -56,6 +56,7 @@ public:
}
void stopSound(Type type) override {
+ _speaker->stop(); // flush the queue too, or isPlayingSound() hangs waitForSounds()
_mixer->stopHandle(_soundFxHandle);
}
diff --git a/engines/freescape/sound/zx.cpp b/engines/freescape/sound/zx.cpp
index bf57552f5f2..a6ccc887461 100644
--- a/engines/freescape/sound/zx.cpp
+++ b/engines/freescape/sound/zx.cpp
@@ -50,6 +50,7 @@ public:
}
void stopSound(Type type) override {
+ _speaker->stop(); // flush the queue too, or isPlayingSound() hangs waitForSounds()
_mixer->stopHandle(_soundFxHandle);
}
More information about the Scummvm-git-logs
mailing list