[Scummvm-git-logs] scummvm master -> 30717e261a1f0a55bcd9067d0ea2191d3a165d3d

neuromancer noreply at scummvm.org
Thu Jun 25 12:55:05 UTC 2026


This automated email contains information about 3 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .

Summary:
5a77120d68 EEM: better palette cycling in map of EEM2 Mac
f25af05a60 EEM: correctly place hotspots in EEM2 Mac
30717e261a EEM: voiceover support for EEM2 Mac


Commit: 5a77120d68d04d2d366831a49e7adf95511d826a
    https://github.com/scummvm/scummvm/commit/5a77120d68d04d2d366831a49e7adf95511d826a
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-25T14:54:44+02:00

Commit Message:
EEM: better palette cycling in map of EEM2 Mac

Changed paths:
    engines/eem/ui.cpp


diff --git a/engines/eem/ui.cpp b/engines/eem/ui.cpp
index 89f88c1431e..fff43073d73 100644
--- a/engines/eem/ui.cpp
+++ b/engines/eem/ui.cpp
@@ -199,6 +199,8 @@ bool readBigMapEntryInfo(const byte *entry, bool floppy, bool macintosh,
 	return true;
 }
 
+const uint32 kMacMapColorCycleDelayMs = 25 * 1000 / 60;
+
 bool loadMacBigMapPixels(Common::Array<byte> &mapPixels,
 						 uint16 &mapW, uint16 &mapH) {
 	DBDArchive bigMapArchive;
@@ -3761,12 +3763,6 @@ void EEMEngine::doBigMap() {
 	if (!_mystery.isLoaded())
 		return;
 
-	if (isMacintosh()) {
-		for (uint i = 0; i < _mystery.numSites() &&
-						 i < Mystery::kVisitedSiteCap; i++)
-			_mystery._onSites[i] = 1;
-	}
-
 	if (isLondon()) {
 		_mystery._pendingSiteJump = 0;
 		_mystery._siteReturnDepth = 0;
@@ -3782,6 +3778,7 @@ void EEMEngine::doBigMap() {
 		const uint32 mapStartTick = g_system->getMillis();
 		drawBigMapOverview(0);
 		uint32 mapLastTick = mapStartTick;
+		uint32 mapLastCycleTick = mapStartTick;
 
 		const bool mac = isMacintosh();
 		const Common::Rect bigMapWindowBase(0, 0, 247, 192);
@@ -3835,12 +3832,24 @@ void EEMEngine::doBigMap() {
 				break;
 
 			const uint32 now = g_system->getMillis();
-			if (now - mapLastTick >= 100) {
+			if (mac && isLondon() &&
+				now - mapLastCycleTick >= kMacMapColorCycleDelayMs) {
+				mapLastCycleTick = now;
+				// Mac `_UpdateBigMap` rotates ColorTable entries 0xef..0xf2
+				// and 0xfc..0xff. Redraw immediately so Mac endpoint art maps
+				// its black pixels to the current black slot after the cycle.
+				cyclePaletteRangeReverse(0xef, 0xf2);
+				cyclePaletteRangeReverse(0xfc, 0xff);
+				drawBigMapOverview(now - mapStartTick);
+				mapLastTick = now;
+			} else if (now - mapLastTick >= 100) {
 				mapLastTick = now;
 				drawBigMapOverview(now - mapStartTick);
 				if (isLondon()) {
-					cyclePaletteRangeReverse(0xf4, 0xf9);
-					cyclePaletteRangeReverse(0xfa, 0xff);
+					if (!mac) {
+						cyclePaletteRangeReverse(0xf4, 0xf9);
+						cyclePaletteRangeReverse(0xfa, 0xff);
+					}
 				} else {
 					cyclePaletteRangeReverse(0xf7, 0xfa);
 					cyclePaletteRangeReverse(0xfb, 0xfe);
@@ -3902,6 +3911,7 @@ void EEMEngine::doBigMap() {
 		const uint32 detailStartTick = g_system->getMillis();
 		drawBigMapDetail(scrollX, scrollY, mapPixels, mapW, mapH, 0);
 		uint32 detailLastTick = detailStartTick;
+		uint32 detailLastCycleTick = detailStartTick;
 		bool returnToOverview = false;
 
 		const Common::Rect returnBase(252, 43, kScreenWidth, kScreenHeight);
@@ -3949,7 +3959,7 @@ void EEMEngine::doBigMap() {
 		while (!shouldQuit() && !returnToOverview) {
 			Common::Event ev;
 			bool dirty = false;
-			bool frameTick = false;
+			bool cycleTick = false;
 			while (g_system->getEventManager()->pollEvent(ev)) {
 				if (ev.type == Common::EVENT_QUIT ||
 					ev.type == Common::EVENT_RETURN_TO_LAUNCHER) {
@@ -4085,15 +4095,28 @@ void EEMEngine::doBigMap() {
 			if (now - detailLastTick >= 100) {
 				detailLastTick = now;
 				dirty = true;
-				frameTick = true;
+			}
+			if (isLondon()) {
+				const uint32 cycleDelay = mac ? kMacMapColorCycleDelayMs : 100;
+				if (now - detailLastCycleTick >= cycleDelay) {
+					detailLastCycleTick = now;
+					cycleTick = true;
+					dirty = true;
+				}
+			}
+			if (cycleTick && isLondon()) {
+				if (mac) {
+					cyclePaletteRangeReverse(0xe9, 0xeb);
+					cyclePaletteRangeReverse(0xec, 0xef);
+					cyclePaletteRangeReverse(0xf0, 0xf2);
+				} else {
+					cyclePaletteRangeReverse(0xee, 0xf2);
+					cyclePaletteRangeReverse(0xea, 0xed);
+				}
 			}
 			if (dirty)
 				drawBigMapDetail(scrollX, scrollY, mapPixels, mapW, mapH,
 					now - detailStartTick);
-			if (frameTick && isLondon()) {
-				cyclePaletteRangeReverse(0xee, 0xf2);
-				cyclePaletteRangeReverse(0xea, 0xed);
-			}
 			g_system->updateScreen();
 			g_system->delayMillis(10);
 		}
@@ -4399,8 +4422,11 @@ void EEMEngine::drawBigMapOverview(uint32 elapsedMs) {
 	scratch.clear();
 
 	Picture frame;
-	if (_picsArchive.getPicture(0x42, frame))
+	if (_picsArchive.getPicture(0x42, frame)) {
+		if (mac)
+			remapMacSurfaceEndpoints(frame.surface, getMacSpritePaletteMap());
 		scratch.simpleBlitFrom(frame.surface);
+	}
 
 	// Marker PICs from `_main`:
 	//   EEM1 CD @ 1a35:0f59: Done=0x20d, Site=0xc5, Crime=0xc6.
@@ -4463,9 +4489,14 @@ void EEMEngine::drawBigMapOverview(uint32 elapsedMs) {
 		const uint frameIdx = bigMapPartnerFrameAtTick((uint)mapAnim.size(),
 													   elapsedMs, isLondon());
 
-		blitAnimFrameAnchored(scratch.surfacePtr(), mapAnim[frameIdx],
-							  mac ? scaleX(0xfd) : 0xfd,
-							  mac ? scaleY(0x50) : 0x50);
+		const int anchorX = mac ? scaleX(0xfd) : 0xfd;
+		const int anchorY = mac ? scaleY(0x50) : 0x50;
+		if (mac)
+			blitMacAnimFrameAnchored(scratch.surfacePtr(), mapAnim[frameIdx],
+									 anchorX, anchorY);
+		else
+			blitAnimFrameAnchored(scratch.surfacePtr(), mapAnim[frameIdx],
+								  anchorX, anchorY);
 	}
 
 	g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,


Commit: f25af05a60d12e9fabc8490b4b5ad15f4c1aefc9
    https://github.com/scummvm/scummvm/commit/f25af05a60d12e9fabc8490b4b5ad15f4c1aefc9
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-25T14:54:44+02:00

Commit Message:
EEM: correctly place hotspots in EEM2 Mac

Changed paths:
    engines/eem/clues.cpp
    engines/eem/site.cpp


diff --git a/engines/eem/clues.cpp b/engines/eem/clues.cpp
index 6eaa7bc151f..398e1064b15 100644
--- a/engines/eem/clues.cpp
+++ b/engines/eem/clues.cpp
@@ -377,10 +377,9 @@ void EEMEngine::doChoosePartner() {
 void EEMEngine::playLondonInitCluesAnim(uint16 caseType, const Picture &bg,
 										bool haveBriefingBg) {
 	const bool mac = isMacintosh();
-	const uint introAni = mac ? 0x17
+	const uint introAni = mac ? (_partner == kPartnerJake ? 0x18 : 0x1c)
 							  : (_partner == kPartnerJake ? 0x18 : 0x71);
-	const uint introScript = mac ? (_partner == kPartnerJake ? 0x18 : 0x1c)
-								 : 0x18;
+	const uint introScript = mac ? 0x17 : 0x18;
 	const int kAnchorX = mac ? 0x14c : 0xd2;
 	const int kAnchorY = mac ? (_partner == kPartnerJake ? 0x78 : 0x76)
 							 : 0x3f;
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 57afddc5199..22df42a435a 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -1903,7 +1903,7 @@ int SiteScreen::hotspotAtPoint(uint siteNum, int x, int y) const {
 	const bool compact = _vm &&
 		(_vm->isFloppy() || (_mystery && _mystery->usesCompactMacData()));
 	const uint stride = compact ? 8 : 14;
-	const bool mac = _vm && _mystery && _mystery->usesCompactMacData();
+	const bool mac = _vm && _vm->isMacintosh();
 	for (uint i = 0; i < count; i++) {
 		const byte *r = spots + i * stride;
 		Common::Rect rect;


Commit: 30717e261a1f0a55bcd9067d0ea2191d3a165d3d
    https://github.com/scummvm/scummvm/commit/30717e261a1f0a55bcd9067d0ea2191d3a165d3d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-25T14:54:44+02:00

Commit Message:
EEM: voiceover support for EEM2 Mac

Changed paths:
    engines/eem/audio.cpp
    engines/eem/audio.h
    engines/eem/site.cpp


diff --git a/engines/eem/audio.cpp b/engines/eem/audio.cpp
index b84844d9712..5db6134304e 100644
--- a/engines/eem/audio.cpp
+++ b/engines/eem/audio.cpp
@@ -88,6 +88,93 @@ uint16 macSndResourceIdForPath(const Common::Path &path) {
 	return 0;
 }
 
+Audio::SeekableAudioStream *makeMacCsndStream(Common::SeekableReadStream *stream,
+											  DisposeAfterUse::Flag disposeAfterUse) {
+	if (!stream)
+		return nullptr;
+
+	if (stream->size() < 4) {
+		if (disposeAfterUse == DisposeAfterUse::YES)
+			delete stream;
+		warning("makeMacCsndStream: resource too short");
+		return nullptr;
+	}
+
+	const uint32 decodedSize = stream->readUint32BE();
+	const uint32 packedSize = (uint32)(stream->size() - stream->pos());
+	Common::Array<byte> packed;
+	packed.resize(packedSize);
+	if (stream->read(packed.data(), packedSize) != packedSize) {
+		if (disposeAfterUse == DisposeAfterUse::YES)
+			delete stream;
+		warning("makeMacCsndStream: short packed read (%u bytes)", packedSize);
+		return nullptr;
+	}
+	if (disposeAfterUse == DisposeAfterUse::YES)
+		delete stream;
+
+	byte *decoded = (byte *)malloc(decodedSize);
+	if (!decoded) {
+		warning("makeMacCsndStream: oom (%u bytes)", decodedSize);
+		return nullptr;
+	}
+
+	uint32 src = 0;
+	uint32 dst = 0;
+	bool ok = true;
+	while (ok && dst < decodedSize && src < packedSize) {
+		byte flags = packed[src++];
+		for (uint bit = 0; bit < 8 && dst < decodedSize; bit++, flags >>= 1) {
+			if (flags & 1) {
+				if (src >= packedSize) {
+					ok = false;
+					break;
+				}
+				decoded[dst++] = packed[src++];
+			} else {
+				if (src + 1 >= packedSize) {
+					ok = false;
+					break;
+				}
+				const uint16 token = ((uint16)packed[src] << 8) | packed[src + 1];
+				src += 2;
+				int32 copyPos = (int32)dst + (int32)(token & 0x0fff) - 0x1000;
+				uint count = ((token >> 12) & 0x0f) + 3;
+				while (count-- && dst < decodedSize) {
+					if (copyPos < 0 || (uint32)copyPos >= dst) {
+						ok = false;
+						break;
+					}
+					decoded[dst++] = decoded[copyPos++];
+				}
+			}
+		}
+	}
+
+	if (!ok || dst != decodedSize) {
+		warning("makeMacCsndStream: decoded %u of %u bytes", dst, decodedSize);
+		free(decoded);
+		return nullptr;
+	}
+
+	if (decodedSize != 0) {
+		byte acc = decoded[0];
+		for (uint32 i = 1; i < decodedSize; i++) {
+			acc = (byte)(acc + decoded[i]);
+			decoded[i] = acc;
+		}
+	}
+
+	Common::MemoryReadStream *sndStream =
+		new Common::MemoryReadStream(decoded, decodedSize,
+									 DisposeAfterUse::YES);
+	Audio::SeekableAudioStream *audioStream =
+		Audio::makeMacSndStream(sndStream, DisposeAfterUse::YES);
+	if (!audioStream)
+		delete sndStream;
+	return audioStream;
+}
+
 AudioPlayer::~AudioPlayer() {
 	stopAll();
 }
@@ -202,12 +289,8 @@ bool AudioPlayer::initMysterySounds(uint mysteryNum) {
 
 	cleanMysterySounds();
 
-	if (_isMacintosh) {
-		debugC(2, kDebugSound,
-			   "AudioPlayer: Mac release has no SDB/SDX bundle for mystery %u",
-			   mysteryNum);
-		return true;
-	}
+	if (_isMacintosh)
+		return initMacMysterySounds(mysteryNum);
 
 	if (!readSdxIndex(sdxPath)) {
 		_sdxIndex.clear();
@@ -224,9 +307,41 @@ void AudioPlayer::cleanMysterySounds() {
 	stopSpool();
 	_sdxIndex.clear();
 	_sdbPath = Common::Path();
+	_macMysterySoundPath = Common::Path();
 	_currentMystery = -1;
 }
 
+bool AudioPlayer::initMacMysterySounds(uint mysteryNum) {
+	const uint16 firstResourceId = 1001;
+	const Common::Path candidates[2] = {
+		Common::Path(Common::String::format("M%02u.DBD", mysteryNum)),
+		Common::Path(Common::String::format("M%02u.CPD", mysteryNum))
+	};
+
+	for (uint i = 0; i < ARRAYSIZE(candidates); i++) {
+		Common::SeekableReadStream *stream =
+			openMacResource(candidates[i], MKTAG('c', 's', 'n', 'd'),
+							firstResourceId);
+		if (!stream) {
+			stream = openMacResource(candidates[i], MKTAG('s', 'n', 'd', ' '),
+									 firstResourceId);
+		}
+		if (!stream)
+			continue;
+
+		delete stream;
+		_macMysterySoundPath = candidates[i];
+		_currentMystery = (int)mysteryNum;
+		debugC(1, kDebugSound, "AudioPlayer: Mac mystery %u sounds from %s",
+			   mysteryNum, _macMysterySoundPath.toString().c_str());
+		return true;
+	}
+
+	warning("AudioPlayer: Mac mystery sound file for mystery %u missing",
+			mysteryNum);
+	return false;
+}
+
 void AudioPlayer::playMacSnd(uint16 resourceId, Audio::SoundHandle &handle,
 							 Audio::Mixer::SoundType type) {
 	Common::SeekableReadStream *stream =
@@ -313,6 +428,10 @@ void AudioPlayer::spoolSound(uint num) {
 			   "AudioPlayer: spoolSound(%u) skipped (no mystery sounds)", num);
 		return;
 	}
+	if (_isMacintosh) {
+		playMacMysterySound(num);
+		return;
+	}
 	if (num >= _sdxIndex.size()) {
 		warning("AudioPlayer: spoolSound(%u) — index out of range (%u)",
 				num, (uint)_sdxIndex.size());
@@ -405,6 +524,47 @@ void AudioPlayer::spoolSound(uint num) {
 				  Audio::Mixer::kSFXSoundType);
 }
 
+bool AudioPlayer::playMacMysterySound(uint num) {
+	if (_macMysterySoundPath.empty())
+		return false;
+
+	const uint16 resourceId = (uint16)(1001 + num);
+	bool compressed = true;
+	Common::SeekableReadStream *stream =
+		openMacResource(_macMysterySoundPath, MKTAG('c', 's', 'n', 'd'),
+						resourceId);
+	if (!stream) {
+		compressed = false;
+		stream = openMacResource(_macMysterySoundPath,
+								 MKTAG('s', 'n', 'd', ' '), resourceId);
+	}
+	if (!stream) {
+		warning("AudioPlayer: Mac mystery sound resource %u missing in %s",
+				resourceId, _macMysterySoundPath.toString().c_str());
+		return false;
+	}
+
+	stopSpool();
+
+	Audio::SeekableAudioStream *audioStream = compressed
+		? makeMacCsndStream(stream, DisposeAfterUse::YES)
+		: Audio::makeMacSndStream(stream, DisposeAfterUse::YES);
+	if (!audioStream) {
+		if (!compressed)
+			delete stream;
+		warning("AudioPlayer: Mac mystery sound resource %u is not playable",
+				resourceId);
+		return false;
+	}
+
+	_mixer->playStream(Audio::Mixer::kSFXSoundType, &_spoolHandle,
+					   audioStream, -1, Audio::Mixer::kMaxChannelVolume,
+					   0, DisposeAfterUse::YES);
+	debugC(1, kDebugSound, "AudioPlayer: playMacMysterySound(%u, %s)",
+		   resourceId, compressed ? "csnd" : "snd");
+	return true;
+}
+
 bool AudioPlayer::isSpoolPlaying() const {
 	return _mixer->isSoundHandleActive(_spoolHandle);
 }
diff --git a/engines/eem/audio.h b/engines/eem/audio.h
index dc6298190c8..e2f8778c97e 100644
--- a/engines/eem/audio.h
+++ b/engines/eem/audio.h
@@ -39,11 +39,14 @@ class EEMEngine;
  *
  * - VOC files (THUNDER.VOC, PHONE.VOC, JEN.VOC/JAKE.VOC): _LoadSoundName +
  *   _PlayVoice @ 1ff1:0299 / 1ff1:023e via _AIL_play_VOC_file.
- * - Mystery spool stream: _InitMysterySounds @ 202f:05cb opens M%d.SDX/.SDB.
+ * - DOS mystery spool stream: _InitMysterySounds @ 202f:05cb opens M%d.SDX/.SDB.
  *   Each SDX entry is (offset, compSize, uncompSize). Equal sizes => raw PCM
  *   (_UncompressedSound @ 202f:03e6); otherwise PKWARE DCL Implode
  *   (_DeCompressSound @ 202f:02ad). Each blob starts with SB Time Constant +
  *   AIL block count, then 8-bit unsigned PCM.
+ * - Mac mystery spool streams live in M%02d.DBD/M%02d.CPD resource forks.
+ *   The executable tries compressed 'csnd' by resource id, then falls back to
+ *   plain 'snd '; ids are 1001 + the zero-based mystery voice slot.
  *
  * M60.SDB/SDX holds the 19 voiceovers between ANIM01..ANIM20 in
  * _DoOpeningAnims (loaded via _InitMysterySounds(0x3c)).
@@ -82,8 +85,8 @@ public:
 	/// Common slots: 12 = PHONESL.VOC, 20 = partner intro, 25 = THUNDER.VOC.
 	void playFloppyVoiceSlot(uint slot, uint partner);
 
-	/// Loads M%u.SDX into memory and remembers the matching M%u.SDB path.
-	/// Mirrors _InitMysterySounds @ 202f:05cb.
+	/// Loads the current mystery voice bundle. DOS uses M%u.SDX/M%u.SDB;
+	/// Mac uses M%02u.DBD/M%02u.CPD 'csnd'/'snd ' resources.
 	bool initMysterySounds(uint mysteryNum);
 
 	/// _CleanMysterySounds @ 202f:05a5.
@@ -128,6 +131,8 @@ private:
 	};
 
 	bool readSdxIndex(const Common::Path &sdxPath);
+	bool initMacMysterySounds(uint mysteryNum);
+	bool playMacMysterySound(uint num);
 	void playMacSnd(uint16 resourceId, Audio::SoundHandle &handle,
 					Audio::Mixer::SoundType type);
 	void playPcmBuffer(byte *pcm, uint32 size, uint sampleRate,
@@ -141,6 +146,7 @@ private:
 
 	Common::Array<SoundEntry> _sdxIndex;
 	Common::Path _sdbPath;
+	Common::Path _macMysterySoundPath;
 	int _currentMystery = -1;
 	bool _voiceEnabled = true;
 	bool _isMacintosh = false;
diff --git a/engines/eem/site.cpp b/engines/eem/site.cpp
index 22df42a435a..d9f888732ce 100644
--- a/engines/eem/site.cpp
+++ b/engines/eem/site.cpp
@@ -345,6 +345,19 @@ const uint16 kWaitAnimsLondon[7][6] = {
 	{ 0x06, 0x2d, 2, 2, 78, 78 }, // 6
 };
 
+// EEM2 Mac site loop uses its own `_WaitAnims` A5 table
+// (`lea (-0x2040,A5)` in FUN_0000d042; data @ 0x153e). These are native
+// Mac 512x384 anchors, not a scale transform of the DOS table.
+const uint16 kMacWaitAnimsLondon[7][6] = {
+	{ 0x00, 0x0a,  0, 3, 150, 150 }, // 0
+	{ 0x03, 0x0c,  1, 3, 120, 128 }, // 1
+	{ 0x01, 0x0b, 13, 5, 151, 151 }, // 2
+	{ 0x04, 0x0d,  2, 5, 148, 152 }, // 3
+	{ 0x02, 0x10, 20, 7, 146, 152 }, // 4
+	{ 0x05, 0x1b, (uint16)-3, 4, 151, 154 }, // 5
+	{ 0x06, 0x1a,  5, 5, 149, 152 }, // 6
+};
+
 // Animation script table (`_AnimationSequences @ 29be:22d4`)
 // Script byte format:
 //   0x80         = restart (loop back to index 0; terminator for one-shots)
@@ -1289,18 +1302,51 @@ bool SiteScreen::enterSiteAnim() {
 
 	if (_vm->isLondon()) {
 		const uint animId = (partner == 0) ? 7 : 0xf;
-		const int anchorY = (partner == 0) ? 0x50 : 0x4e;
+		int anchorX = mac ? (int)(int16)kMacWaitAnimsLondon[0][2 + partner] : 0;
+		int anchorY = mac ? (int)(int16)kMacWaitAnimsLondon[0][4 + partner]
+						  : ((partner == 0) ? 0x50 : 0x4e);
+		bool alignToIdleBottom = false;
+		int idleBottom = 0;
 		Animation anim;
 		if (!_vm->getAni().loadAnimation(animId, anim) || anim.empty())
 			return false;
+		if (mac) {
+			uint16 idleAnimId = 0;
+			int idleX = 0;
+			int idleY = 0;
+			Animation idleAnim;
+			if (partnerIdleAnimParams(_mystery->_siteNumber, idleAnimId,
+									  idleX, idleY) &&
+				_vm->getAni().loadAnimation(idleAnimId, idleAnim) &&
+				!idleAnim.empty()) {
+				const uint idleFrame =
+					partnerFrameAtTick(idleAnimId, (uint)idleAnim.size(), 0);
+				if (idleFrame < idleAnim.size()) {
+					const Picture &idle = idleAnim[idleFrame];
+					anchorX = idleX;
+					anchorY = idleY;
+					idleBottom = idleY - (int)(int16)idle.rowoff +
+								 idle.surface.h;
+					alignToIdleBottom = true;
+				}
+			}
+		}
 		for (uint frameIdx = 0;
 			 frameIdx < anim.size() && !_vm->shouldQuit();
 			 frameIdx++) {
+			const Picture &frame = anim[frameIdx];
+			const int frameY = alignToIdleBottom
+				? idleBottom - frame.surface.h + (int)(int16)frame.rowoff
+				: anchorY;
 			Graphics::ManagedSurface scratch(sw, sh,
 				Graphics::PixelFormat::createFormatCLUT8());
 			scratch.simpleBlitFrom(bg);
-			blitAnimFrameAnchored(scratch.surfacePtr(), anim[frameIdx],
-								  0, anchorY);
+			if (mac)
+				blitMacAnimFrameAnchored(scratch.surfacePtr(), frame,
+										 anchorX, frameY, macPaletteMap);
+			else
+				blitAnimFrameAnchored(scratch.surfacePtr(), frame,
+									  anchorX, frameY);
 			g_system->copyRectToScreen(scratch.getPixels(), scratch.pitch,
 									   0, 0, sw, sh);
 			g_system->updateScreen();
@@ -1654,8 +1700,10 @@ bool SiteScreen::partnerIdleAnimParams(uint siteNum, uint16 &animId,
 		}
 	} else {
 		const uint16 speaker = READ_LE_UINT16(site + 8);
-		const uint16 (*waitTable)[6] = _vm->isLondon()
-			? kWaitAnimsLondon : kWaitAnims;
+		const uint16 (*waitTable)[6] = kWaitAnims;
+		if (_vm->isLondon())
+			waitTable = _vm->isMacintosh()
+				? kMacWaitAnimsLondon : kWaitAnimsLondon;
 		if (speaker >= ARRAYSIZE(kWaitAnims))
 			return false;
 		animId = waitTable[speaker][0 + partner];
@@ -2061,7 +2109,14 @@ bool EEMEngine::loadKdAnim(uint16 num, Animation &anim, int &px, int &py,
 	if (num >= ARRAYSIZE(kKdAnimTable))
 		return false;
 
-	const uint16 (*kdTable)[6] = isLondon() ? kKdAnimTableLondon : kKdAnimTable;
+	const uint16 (*kdTable)[6] = kKdAnimTable;
+	if (isLondon()) {
+		// EEM2 Mac FUN_0000ce6e indexes `_WaitAnims + 1`
+		// (`lea (-0x2034,A5)`), so KD gestures share the Mac idle anchors
+		// for speaker rows 1..6.
+		kdTable = isMacintosh()
+			? kMacWaitAnimsLondon + 1 : kKdAnimTableLondon;
+	}
 	const uint partner = (_partner == kPartnerJake) ? 0 : 1;
 	animId = kdTable[num][partner];
 	px     = (int)kdTable[num][2 + partner];




More information about the Scummvm-git-logs mailing list