[Scummvm-git-logs] scummvm master -> 80ddc80b0c0d59061ebc76f22273e81a0b8b0543

dreammaster noreply at scummvm.org
Thu Aug 6 02:27:22 UTC 2026


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

Summary:
f68a21ebbf MADS: NEBULAR: Encapsulate Macintosh runtime state
95eaa4268b MADS: NEBULAR: Give Macintosh menus their own path
9e57b37250 MADS: NEBULAR: Preserve Macintosh interface text colors
ce2fe83c13 MADS: NEBULAR: Follow native Macintosh interface loading
80ddc80b0c MADS: Isolate Macintosh specific interface presentation


Commit: f68a21ebbf05733de0de2c0117dd864515fdf4e6
    https://github.com/scummvm/scummvm/commit/f68a21ebbf05733de0de2c0117dd864515fdf4e6
Author: fusefib (fibofuse at gmail.com)
Date: 2026-08-06T12:27:16+10:00

Commit Message:
MADS: NEBULAR: Encapsulate Macintosh runtime state

Assisted-by: Codex:GPT-5.4

Changed paths:
  A engines/mads/nebular/mac_nebular.h
    engines/mads/nebular/mac_nebular.cpp
    engines/mads/nebular/nebular.cpp
    engines/mads/nebular/nebular.h


diff --git a/engines/mads/nebular/mac_nebular.cpp b/engines/mads/nebular/mac_nebular.cpp
index 7fdcf958abe..83782ebf54e 100644
--- a/engines/mads/nebular/mac_nebular.cpp
+++ b/engines/mads/nebular/mac_nebular.cpp
@@ -30,6 +30,7 @@
 #include "mads/core/object.h"
 #include "mads/core/pal.h"
 #include "mads/core/screen.h"
+#include "mads/nebular/mac_nebular.h"
 #include "mads/nebular/mac_resources.h"
 #include "mads/nebular/nebular.h"
 #include "mads/nebular/popup.h"
@@ -403,39 +404,42 @@ static bool isMacInterfaceSemanticPixel(int x, int y) {
 	return false;
 }
 
-void RexNebularEngine::initMacintoshGraphics() {
-	initGraphics(kMacScreenWidth, kMacScreenHeight);
+MacNebular::MacNebular(RexNebularEngine &engine) : _engine(engine) {
 }
 
-bool RexNebularEngine::initMacintoshResources() {
-	_macResources = new MacResourceProvider();
-	if (!_macResources->load())
-		return false;
-
-	env_set_resource_provider(_macResources);
-	_soundManager = new Sound::MacSoundManager(_mixer, _soundFlag, _macResources);
-	return true;
-}
-
-void RexNebularEngine::shutdownMacintoshResources() {
-	if (!_macResources)
+MacNebular::~MacNebular() {
+	if (!_resources)
 		return;
 
-	delete _soundManager;
-	_soundManager = nullptr;
+	delete _engine._soundManager;
+	_engine._soundManager = nullptr;
 	env_set_resource_provider(nullptr);
-	delete _macResources;
-	_macResources = nullptr;
+	delete _resources;
 }
 
-void RexNebularEngine::applyGameSettings() {
-	Engine::applyGameSettings();
+void MacNebular::initGraphics() {
+	::initGraphics(kMacScreenWidth, kMacScreenHeight);
+}
+
+bool MacNebular::initResources() {
+	_resources = new MacResourceProvider();
+	if (!_resources->load()) {
+		delete _resources;
+		_resources = nullptr;
+		return false;
+	}
 
+	env_set_resource_provider(_resources);
+	_engine._soundManager = new Sound::MacSoundManager(
+		_engine._mixer, _engine._soundFlag, _resources);
+	return true;
+}
+
+void MacNebular::applyGameSettings() {
 	// The Macintosh port's 640x400 large-window mode uses square pixels.
 	// DOS-style 320x200 aspect correction would stretch its scene and native
 	// interface vertically.
-	if (getPlatform() == Common::kPlatformMacintosh &&
-			g_system->hasFeature(OSystem::kFeatureAspectRatioCorrection) &&
+	if (g_system->hasFeature(OSystem::kFeatureAspectRatioCorrection) &&
 			g_system->getFeatureState(OSystem::kFeatureAspectRatioCorrection)) {
 		g_system->beginGFXTransaction();
 		g_system->setFeatureState(OSystem::kFeatureAspectRatioCorrection, false);
@@ -443,10 +447,7 @@ void RexNebularEngine::applyGameSettings() {
 	}
 }
 
-Common::Point RexNebularEngine::screenToGame(const Common::Point &point) const {
-	if (getPlatform() != Common::kPlatformMacintosh)
-		return MADSEngine::screenToGame(point);
-
+Common::Point MacNebular::screenToGame(const Common::Point &point) const {
 	if (point.y >= 0 && point.y < kMacSceneHeight)
 		return Common::Point(CLIP<int>(point.x / 2, 0, 319), point.y / 2);
 
@@ -463,10 +464,7 @@ Common::Point RexNebularEngine::screenToGame(const Common::Point &point) const {
 	return Common::Point(-1, -1);
 }
 
-Common::Point RexNebularEngine::gameToScreen(const Common::Point &point) const {
-	if (getPlatform() != Common::kPlatformMacintosh)
-		return MADSEngine::gameToScreen(point);
-
+Common::Point MacNebular::gameToScreen(const Common::Point &point) const {
 	if (point.y < 156)
 		return Common::Point(point.x * 2, point.y * 2);
 
@@ -474,19 +472,14 @@ Common::Point RexNebularEngine::gameToScreen(const Common::Point &point) const {
 		kMacSceneHeight + (point.y - 156) * 2);
 }
 
-void RexNebularEngine::presentScreen(int shakeOffset) {
-	if (getPlatform() != Common::kPlatformMacintosh) {
-		MADSEngine::presentScreen(shakeOffset);
-		return;
-	}
-
-	_macOutput.resize(kMacScreenWidth * kMacScreenHeight);
-	memset(_macOutput.data(), kMacBlackColor, _macOutput.size());
+void MacNebular::presentScreen(int shakeOffset) {
+	_output.resize(kMacScreenWidth * kMacScreenHeight);
+	memset(_output.data(), kMacBlackColor, _output.size());
 
 	// Native large-window mode doubles the 320x156 scene in both axes.
 	for (int y = 0; y < 156; ++y) {
-		const byte *source = (const byte *)_screen->getBasePtr(0, y);
-		byte *line1 = _macOutput.data() + (y * 2) * kMacScreenWidth;
+		const byte *source = (const byte *)_engine._screen->getBasePtr(0, y);
+		byte *line1 = _output.data() + (y * 2) * kMacScreenWidth;
 		byte *line2 = line1 + kMacScreenWidth;
 		for (int x = 0; x < 320; ++x) {
 			const byte color = source[(x + shakeOffset) % 320];
@@ -497,9 +490,9 @@ void RexNebularEngine::presentScreen(int shakeOffset) {
 	}
 
 	const Graphics::Surface *nativeInterface =
-		_macResources ? _macResources->getNativeInterface() : nullptr;
+		_resources ? _resources->getNativeInterface() : nullptr;
 	const Graphics::Surface *logicalInterface =
-		_macResources ? _macResources->getLogicalInterface() : nullptr;
+		_resources ? _resources->getLogicalInterface() : nullptr;
 	if (nativeInterface && nativeInterface->w == kMacInterfaceWidth &&
 			nativeInterface->h == kMacInterfaceHeight &&
 			nativeInterface->format.bytesPerPixel == 1) {
@@ -511,7 +504,7 @@ void RexNebularEngine::presentScreen(int shakeOffset) {
 				kMacInterfaceWidth);
 
 		const Graphics::Font *interfaceFont =
-			_macResources ? _macResources->getInterfaceFont() : nullptr;
+			_resources ? _resources->getInterfaceFont() : nullptr;
 
 		// Keep live inventory artwork and other non-text changes produced by
 		// the shared interface state machine. Semantic regions are redrawn
@@ -524,7 +517,7 @@ void RexNebularEngine::presentScreen(int shakeOffset) {
 				byte *target = (byte *)panel.getBasePtr(0, y);
 				const int logicalY = y / 2;
 				const byte *current =
-					(const byte *)_screen->getBasePtr(0, 156 + logicalY);
+					(const byte *)_engine._screen->getBasePtr(0, 156 + logicalY);
 				const byte *baseline =
 					(const byte *)logicalInterface->getBasePtr(0, logicalY);
 				for (int x = 0; x < kMacInterfaceWidth; ++x) {
@@ -543,7 +536,7 @@ void RexNebularEngine::presentScreen(int shakeOffset) {
 		// Apply the blue layer after composing artwork, live state, controls,
 		// and text so the whole native panel receives one uniform treatment.
 		byte washLUT[256];
-		buildMacPanelWashLUT(_macResources->getNativeInterfacePalette(), washLUT);
+		buildMacPanelWashLUT(_resources->getNativeInterfacePalette(), washLUT);
 		for (int y = 0; y < kMacInterfaceHeight; ++y) {
 			byte *target = (byte *)panel.getBasePtr(0, y);
 			for (int x = 0; x < kMacInterfaceWidth; ++x)
@@ -551,7 +544,7 @@ void RexNebularEngine::presentScreen(int shakeOffset) {
 		}
 
 		for (int y = 0; y < kMacInterfaceHeight; ++y) {
-			memcpy(_macOutput.data() +
+			memcpy(_output.data() +
 				(kMacSceneHeight + y) * kMacScreenWidth + kMacInterfaceX,
 				panel.getBasePtr(0, y), kMacInterfaceWidth);
 		}
@@ -559,46 +552,46 @@ void RexNebularEngine::presentScreen(int shakeOffset) {
 		// Before the native panel is loaded, retain a structurally equivalent
 		// fallback by scaling the shared 320x44 interface into its Mac bounds.
 		for (int y = 0; y < kMacInterfaceHeight; ++y) {
-			const byte *source = (const byte *)_screen->getBasePtr(0, 156 + y / 2);
-			byte *target = _macOutput.data() +
+			const byte *source = (const byte *)_engine._screen->getBasePtr(0, 156 + y / 2);
+			byte *target = _output.data() +
 				(kMacSceneHeight + y) * kMacScreenWidth + kMacInterfaceX;
 			for (int x = 0; x < kMacInterfaceWidth; ++x)
 				target[x] = source[x * 320 / kMacInterfaceWidth];
 		}
 	}
 
-	if (_macPopupActive && !_macPopup.empty()) {
-		for (int y = 0; y < _macPopup.h; ++y) {
-			const int targetY = _macPopupRect.top + y;
+	if (_popupActive && !_popup.empty()) {
+		for (int y = 0; y < _popup.h; ++y) {
+			const int targetY = _popupRect.top + y;
 			if (targetY < 0 || targetY >= kMacScreenHeight)
 				continue;
 
-			const int targetX = MAX<int>(0, _macPopupRect.left);
-			const int sourceX = targetX - _macPopupRect.left;
-			const int width = MIN<int>(_macPopup.w - sourceX,
+			const int targetX = MAX<int>(0, _popupRect.left);
+			const int sourceX = targetX - _popupRect.left;
+			const int width = MIN<int>(_popup.w - sourceX,
 				kMacScreenWidth - targetX);
 			if (width > 0)
-				memcpy(_macOutput.data() + targetY * kMacScreenWidth + targetX,
-					_macPopup.getBasePtr(sourceX, y), width);
+				memcpy(_output.data() + targetY * kMacScreenWidth + targetX,
+					_popup.getBasePtr(sourceX, y), width);
 		}
 	}
 
-	if (!_macLayoutLogged) {
+	if (!_layoutLogged) {
 		debug(2, "Presenting Macintosh Rex as 640x312 scene plus centered 512x88 interface");
-		_macLayoutLogged = true;
+		_layoutLogged = true;
 	}
 
-	g_system->copyRectToScreen(_macOutput.data(), kMacScreenWidth,
+	g_system->copyRectToScreen(_output.data(), kMacScreenWidth,
 		0, 0, kMacScreenWidth, kMacScreenHeight);
 	g_system->updateScreen();
-	_screen->clearDirtyRects();
+	_engine._screen->clearDirtyRects();
 }
 
-void RexNebularEngine::showMacPopup() {
-	if (getPlatform() != Common::kPlatformMacintosh || !_macResources || !box)
+void MacNebular::showPopup() {
+	if (!_resources || !box)
 		return;
 
-	const Graphics::Font *font = _macResources->getDialogFont();
+	const Graphics::Font *font = _resources->getDialogFont();
 	if (!font)
 		return;
 
@@ -641,9 +634,9 @@ void RexNebularEngine::showMacPopup() {
 
 	const int height = CLIP<int>((int)lines.size() * 12 + 20, 20,
 		kMacScreenHeight - 2);
-	_macPopup.create(width, height, Graphics::PixelFormat::createFormatCLUT8());
-	_macPopup.fillRect(Common::Rect(width, height), kMacPopupColor);
-	_macPopup.frameRect(Common::Rect(width, height), kMacBlackColor);
+	_popup.create(width, height, Graphics::PixelFormat::createFormatCLUT8());
+	_popup.fillRect(Common::Rect(width, height), kMacPopupColor);
+	_popup.frameRect(Common::Rect(width, height), kMacBlackColor);
 
 	const int ascent = font->getFontAscent() >= 0 ?
 		font->getFontAscent() : font->getFontHeight();
@@ -654,7 +647,7 @@ void RexNebularEngine::showMacPopup() {
 			break;
 
 		if (lines[line].tab == POPUP_BAR) {
-			_macPopup.fillRect(Common::Rect(2, baseline - 5,
+			_popup.fillRect(Common::Rect(2, baseline - 5,
 				width - 2, baseline - 4), kMacBlackColor);
 			continue;
 		}
@@ -664,32 +657,65 @@ void RexNebularEngine::showMacPopup() {
 			(width - textWidth) / 2 :
 			10 + (lines[line].tab &
 				~(POPUP_UNDERLINE | POPUP_DOWNPIXEL)) * 3 / 4;
-		font->drawString(&_macPopup, lines[line].text, x, baseline - ascent,
+		font->drawString(&_popup, lines[line].text, x, baseline - ascent,
 			MAX(0, width - x - 2), kMacBlackColor);
 		if (lines[line].tab & POPUP_UNDERLINE) {
-			_macPopup.fillRect(Common::Rect(x, baseline + 1,
+			_popup.fillRect(Common::Rect(x, baseline + 1,
 				MIN(width - 2, x + textWidth), baseline + 2),
 				kMacBlackColor);
 		}
 	}
 
-	_macPopupRect = Common::Rect(
+	_popupRect = Common::Rect(
 		(kMacScreenWidth - width) / 2,
 		(kMacScreenHeight - height) / 2,
 		(kMacScreenWidth + width) / 2,
 		(kMacScreenHeight + height) / 2);
-	_macPopupActive = true;
+	_popupActive = true;
 	presentScreen(0);
 }
 
-void RexNebularEngine::hideMacPopup() {
-	if (!_macPopupActive)
+void MacNebular::hidePopup() {
+	if (!_popupActive)
 		return;
 
-	_macPopupActive = false;
-	_macPopup.free();
+	_popupActive = false;
+	_popup.free();
 	presentScreen(0);
 }
 
+void RexNebularEngine::applyGameSettings() {
+	Engine::applyGameSettings();
+	if (_macNebular)
+		_macNebular->applyGameSettings();
+}
+
+Common::Point RexNebularEngine::screenToGame(const Common::Point &point) const {
+	return _macNebular ? _macNebular->screenToGame(point) :
+		MADSEngine::screenToGame(point);
+}
+
+Common::Point RexNebularEngine::gameToScreen(const Common::Point &point) const {
+	return _macNebular ? _macNebular->gameToScreen(point) :
+		MADSEngine::gameToScreen(point);
+}
+
+void RexNebularEngine::presentScreen(int shakeOffset) {
+	if (_macNebular)
+		_macNebular->presentScreen(shakeOffset);
+	else
+		MADSEngine::presentScreen(shakeOffset);
+}
+
+void RexNebularEngine::showMacPopup() {
+	if (_macNebular)
+		_macNebular->showPopup();
+}
+
+void RexNebularEngine::hideMacPopup() {
+	if (_macNebular)
+		_macNebular->hidePopup();
+}
+
 } // namespace RexNebular
 } // namespace MADS
diff --git a/engines/mads/nebular/mac_nebular.h b/engines/mads/nebular/mac_nebular.h
new file mode 100644
index 00000000000..f4560c50d78
--- /dev/null
+++ b/engines/mads/nebular/mac_nebular.h
@@ -0,0 +1,62 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef MADS_NEBULAR_MAC_NEBULAR_H
+#define MADS_NEBULAR_MAC_NEBULAR_H
+
+#include "common/array.h"
+#include "common/rect.h"
+#include "graphics/managed_surface.h"
+
+namespace MADS {
+namespace RexNebular {
+
+class MacResourceProvider;
+class RexNebularEngine;
+
+class MacNebular {
+private:
+	RexNebularEngine &_engine;
+	MacResourceProvider *_resources = nullptr;
+	Common::Array<byte> _output;
+	Graphics::ManagedSurface _popup;
+	Common::Rect _popupRect;
+	bool _popupActive = false;
+	bool _layoutLogged = false;
+
+public:
+	explicit MacNebular(RexNebularEngine &engine);
+	~MacNebular();
+
+	void initGraphics();
+	bool initResources();
+	void applyGameSettings();
+	Common::Point screenToGame(const Common::Point &point) const;
+	Common::Point gameToScreen(const Common::Point &point) const;
+	void presentScreen(int shakeOffset);
+	void showPopup();
+	void hidePopup();
+};
+
+} // namespace RexNebular
+} // namespace MADS
+
+#endif
diff --git a/engines/mads/nebular/nebular.cpp b/engines/mads/nebular/nebular.cpp
index 93ec941e59b..989595a731b 100644
--- a/engines/mads/nebular/nebular.cpp
+++ b/engines/mads/nebular/nebular.cpp
@@ -40,6 +40,7 @@
 #include "mads/nebular/copy.h"
 #include "mads/nebular/global.h"
 #include "mads/nebular/main.h"
+#include "mads/nebular/mac_nebular.h"
 #include "mads/nebular/popup.h"
 #include "mads/nebular/mads/inventory.h"
 #include "mads/nebular/mads/words.h"
@@ -58,18 +59,20 @@ namespace RexNebular {
 
 RexNebularEngine::RexNebularEngine(OSystem *syst, const MADSGameDescription *gameDesc) :
 		MADSEngine(syst, gameDesc) {
+	if (getPlatform() == Common::kPlatformMacintosh)
+		_macNebular = new MacNebular(*this);
+
 	// Initialize globals
 	RexNebular::popup_init();
 }
 
 RexNebularEngine::~RexNebularEngine() {
-	shutdownMacintoshResources();
+	delete _macNebular;
 }
 
 Common::Error RexNebularEngine::run() {
-	const bool isMacintosh = getPlatform() == Common::kPlatformMacintosh;
-	if (isMacintosh)
-		initMacintoshGraphics();
+	if (_macNebular)
+		_macNebular->initGraphics();
 	else
 		initGraphics(320, 200);
 	applyGameSettings();
@@ -91,8 +94,8 @@ Common::Error RexNebularEngine::run() {
 	}
 
 	// Set up the platform resource and sound providers
-	if (isMacintosh) {
-		if (!initMacintoshResources())
+	if (_macNebular) {
+		if (!_macNebular->initResources())
 			return Common::Error(Common::kNoGameDataFoundError,
 				"Could not open the Macintosh Rex resource files");
 	} else {
diff --git a/engines/mads/nebular/nebular.h b/engines/mads/nebular/nebular.h
index f24553dcb87..f9e69e468e1 100644
--- a/engines/mads/nebular/nebular.h
+++ b/engines/mads/nebular/nebular.h
@@ -22,15 +22,12 @@
 #ifndef MADS_NEBULAR_H
 #define MADS_NEBULAR_H
 
-#include "common/array.h"
-#include "common/rect.h"
-#include "graphics/managed_surface.h"
 #include "mads/mads.h"
 
 namespace MADS {
 namespace RexNebular {
 
-class MacResourceProvider;
+class MacNebular;
 
 struct MADSSavegameHeader {
 	uint8 _version;
@@ -46,16 +43,9 @@ struct MADSSavegameHeader {
 
 class RexNebularEngine : public MADSEngine {
 private:
-	MacResourceProvider *_macResources = nullptr;
-	Common::Array<byte> _macOutput;
-	Graphics::ManagedSurface _macPopup;
-	Common::Rect _macPopupRect;
-	bool _macPopupActive = false;
-	bool _macLayoutLogged = false;
+	friend class MacNebular;
+	MacNebular *_macNebular = nullptr;
 
-	void initMacintoshGraphics();
-	bool initMacintoshResources();
-	void shutdownMacintoshResources();
 	void showRecipe();
 
 protected:


Commit: 95eaa4268bc7cff9f2815c17dd6089e8d0ea2f13
    https://github.com/scummvm/scummvm/commit/95eaa4268bc7cff9f2815c17dd6089e8d0ea2f13
Author: fusefib (fibofuse at gmail.com)
Date: 2026-08-06T12:27:16+10:00

Commit Message:
MADS: NEBULAR: Give Macintosh menus their own path

Assisted-by: Codex:GPT-5.4

Changed paths:
    engines/mads/core/game.cpp
    engines/mads/mads.h
    engines/mads/nebular/mac_menus.cpp
    engines/mads/nebular/mac_menus.h
    engines/mads/nebular/main.cpp
    engines/mads/nebular/nebular.h


diff --git a/engines/mads/core/game.cpp b/engines/mads/core/game.cpp
index c23ca85c27d..8d4d4481a5e 100644
--- a/engines/mads/core/game.cpp
+++ b/engines/mads/core/game.cpp
@@ -947,30 +947,6 @@ int game_parse_keystroke(int mykey) {
 
 	mykey = main_normal_key(mykey);
 
-	// FIXME: The Macintosh release uses the Mac platform's menu instead of 
-	// MADS' DOS menu rooms. Until a native Macintosh menu bar is exposed
-	// through ScummVM, route its menu shortcuts to the Global Menu and avoid
-	// loading game-specific rooms 921-924.
-	if (g_engine->usesScummVMMenu()) {
-		switch (mykey) {
-		case esc_key:
-		case f1_key:
-		case f2_key:
-		case alt_s_key:
-		case f3_key:
-		case alt_r_key:
-		case f4_key:
-		case f5_key:
-		case f6_key:
-		case f7_key:
-			g_engine->openMainMenuDialog();
-			mykey = 0;
-			break;
-		default:
-			break;
-		}
-	}
-
 	switch (mykey) {
 	case space_key:
 		global[player_hyperwalked] = true;
@@ -1957,8 +1933,9 @@ static void game_control_loop() {
 		if (kernel.activate_menu) {
 			if (!kernel.trigger && player.commands_allowed) {
 				if (g_engine->getGameID() == GType_RexNebular) {
-					// Rex Nebular menus are their own virtual room, so the current room
-					// has to be completely unloaded before we open the menu
+					// Rex handles menus after completely unloading the current room.
+					// DOS uses virtual menu rooms; for now, Macintosh dispatches to
+					// its own ScummVM-dialog adapter through the same game callback.
 					kernel.force_restart = true;
 
 				} else {
diff --git a/engines/mads/mads.h b/engines/mads/mads.h
index f17bd9e9dcb..2f3f11639b2 100644
--- a/engines/mads/mads.h
+++ b/engines/mads/mads.h
@@ -115,7 +115,6 @@ public:
 	uint32 getGameFeatures() const;
 	bool isDemo() const;
 	bool isCDROM() const;
-	virtual bool usesScummVMMenu() const { return false; }
 
 	void readConfigFile();
 	int getRandomNumber(int maxNumber);
diff --git a/engines/mads/nebular/mac_menus.cpp b/engines/mads/nebular/mac_menus.cpp
index e6a0e072e93..14b6138c90e 100644
--- a/engines/mads/nebular/mac_menus.cpp
+++ b/engines/mads/nebular/mac_menus.cpp
@@ -24,6 +24,8 @@
 #include "gui/chooser.h"
 #include "mads/core/config.h"
 #include "mads/core/game.h"
+#include "mads/core/kernel.h"
+#include "mads/mads.h"
 #include "mads/nebular/mac_menus.h"
 
 namespace MADS {
@@ -61,5 +63,16 @@ void selectMacintoshDifficulty() {
 	}
 }
 
+void macintoshGameMenu() {
+	g_engine->flushKeys();
+
+	if (kernel.activate_menu == GAME_DIFFICULTY_MENU)
+		selectMacintoshDifficulty();
+	else if (kernel.activate_menu != GAME_NO_MENU)
+		g_engine->openMainMenuDialog();
+
+	kernel.activate_menu = GAME_NO_MENU;
+}
+
 } // namespace RexNebular
 } // namespace MADS
diff --git a/engines/mads/nebular/mac_menus.h b/engines/mads/nebular/mac_menus.h
index 9d4e045f975..c40fc913136 100644
--- a/engines/mads/nebular/mac_menus.h
+++ b/engines/mads/nebular/mac_menus.h
@@ -26,6 +26,7 @@ namespace MADS {
 namespace RexNebular {
 
 void selectMacintoshDifficulty();
+void macintoshGameMenu();
 
 } // namespace RexNebular
 } // namespace MADS
diff --git a/engines/mads/nebular/main.cpp b/engines/mads/nebular/main.cpp
index 3272d013c2b..b7fd431cea8 100644
--- a/engines/mads/nebular/main.cpp
+++ b/engines/mads/nebular/main.cpp
@@ -217,7 +217,8 @@ static void main_cold_data_init() {
 	debugger_reset = game_debugger_reset;
 	debugger_update = game_debugger;
 
-	game_menu_routine = global_game_menu;
+	game_menu_routine = g_engine->getPlatform() == Common::kPlatformMacintosh ?
+		macintoshGameMenu : global_game_menu;
 	game_menu_init = global_menu_system_init;
 	game_menu_exit = global_menu_system_shutdown;
 	game_emergency_save = global_emergency_save;
diff --git a/engines/mads/nebular/nebular.h b/engines/mads/nebular/nebular.h
index f9e69e468e1..02f2a646190 100644
--- a/engines/mads/nebular/nebular.h
+++ b/engines/mads/nebular/nebular.h
@@ -59,9 +59,6 @@ public:
 	~RexNebularEngine() override;
 
 	Common::Error run() override;
-	bool usesScummVMMenu() const override {
-		return getPlatform() == Common::kPlatformMacintosh;
-	}
 	void syncRoom(Common::Serializer &s) override;
 	void showMacPopup();
 	void hideMacPopup();


Commit: 9e57b372501581a700347514af3108564142331d
    https://github.com/scummvm/scummvm/commit/9e57b372501581a700347514af3108564142331d
Author: fusefib (fibofuse at gmail.com)
Date: 2026-08-06T12:27:16+10:00

Commit Message:
MADS: NEBULAR: Preserve Macintosh interface text colors

Assisted-by: Codex:GPT-5.4

Changed paths:
    engines/mads/nebular/mac_nebular.cpp


diff --git a/engines/mads/nebular/mac_nebular.cpp b/engines/mads/nebular/mac_nebular.cpp
index 83782ebf54e..c1ccc733ad7 100644
--- a/engines/mads/nebular/mac_nebular.cpp
+++ b/engines/mads/nebular/mac_nebular.cpp
@@ -104,11 +104,25 @@ static void buildMacPanelWashLUT(const byte *nativePalette, byte washLUT[256]) {
 	displayPalette[kMacBlackColor].b = 0;
 	mcga_setpal_range(&displayPalette, 0, kMacBlackColor + 1);
 
-	// Keep the action caption face independent of room palette changes.
+	// CODE 7 draws normal and selected interface words with palette indexes
+	// 15, 13, and 14 respectively. Keep those roles independent of room
+	// palette changes and outside the panel wash below.
+	displayPalette[kMacLeftSelectColor].r =
+		(nativePalette[1 * 3 + 0] * 63 + 127) / 255;
+	displayPalette[kMacLeftSelectColor].g =
+		(nativePalette[1 * 3 + 1] * 63 + 127) / 255;
+	displayPalette[kMacLeftSelectColor].b =
+		(nativePalette[1 * 3 + 2] * 63 + 127) / 255;
+	displayPalette[kMacRightSelectColor].r =
+		(nativePalette[2 * 3 + 0] * 63 + 127) / 255;
+	displayPalette[kMacRightSelectColor].g =
+		(nativePalette[2 * 3 + 1] * 63 + 127) / 255;
+	displayPalette[kMacRightSelectColor].b =
+		(nativePalette[2 * 3 + 2] * 63 + 127) / 255;
 	displayPalette[kMacNormalTextColor].r = 63;
 	displayPalette[kMacNormalTextColor].g = 63;
 	displayPalette[kMacNormalTextColor].b = 63;
-	mcga_setpal_range(&displayPalette, kMacNormalTextColor, 1);
+	mcga_setpal_range(&displayPalette, kMacLeftSelectColor, 3);
 
 	for (int sourceColor = 0; sourceColor < 256; ++sourceColor) {
 		int sourceR, sourceG, sourceB;
@@ -530,11 +544,10 @@ void MacNebular::presentScreen(int shakeOffset) {
 			}
 		}
 
-		if (interfaceFont)
-			drawMacInterfaceState(panel, *interfaceFont);
-
-		// Apply the blue layer after composing artwork, live state, controls,
-		// and text so the whole native panel receives one uniform treatment.
+		// Apply the native blue layer to the panel artwork and non-semantic live
+		// state. CODE 7 draws the interface words afterward with distinct
+		// normal and selection colors, so they must not be quantized into the
+		// eight washed background colors.
 		byte washLUT[256];
 		buildMacPanelWashLUT(_resources->getNativeInterfacePalette(), washLUT);
 		for (int y = 0; y < kMacInterfaceHeight; ++y) {
@@ -543,6 +556,9 @@ void MacNebular::presentScreen(int shakeOffset) {
 				target[x] = washLUT[target[x]];
 		}
 
+		if (interfaceFont)
+			drawMacInterfaceState(panel, *interfaceFont);
+
 		for (int y = 0; y < kMacInterfaceHeight; ++y) {
 			memcpy(_output.data() +
 				(kMacSceneHeight + y) * kMacScreenWidth + kMacInterfaceX,


Commit: ce2fe83c139fed8f4bd49d25f19e859032ff05d5
    https://github.com/scummvm/scummvm/commit/ce2fe83c139fed8f4bd49d25f19e859032ff05d5
Author: fusefib (fibofuse at gmail.com)
Date: 2026-08-06T12:27:16+10:00

Commit Message:
MADS: NEBULAR: Follow native Macintosh interface loading

The Macintosh loader returns after reading AA_INTERFACE controller data
and does not load or animate the DOS sprite-series names retained in
those headers. Model that capability explicitly so the native InBx panel
loads without requesting absent I1 bubble and fish resources. This also
preserves the Macintosh port's static inventory presentation while
leaving all DOS MADS games unchanged.

Assisted-by: Codex:GPT-5.4

Changed paths:
    engines/mads/core/anim.cpp
    engines/mads/core/inter.cpp
    engines/mads/mads.h
    engines/mads/nebular/mac_nebular.cpp
    engines/mads/nebular/nebular.h


diff --git a/engines/mads/core/anim.cpp b/engines/mads/core/anim.cpp
index cc537e7ae7b..6af4574f2b8 100644
--- a/engines/mads/core/anim.cpp
+++ b/engines/mads/core/anim.cpp
@@ -35,6 +35,7 @@
 #include "mads/core/mads.h"
 #include "mads/core/tile.h"
 #include "mads/core/error.h"
+#include "mads/mads.h"
 
 namespace MADS {
 
@@ -202,10 +203,13 @@ void anim_unload(AnimPtr anim) {
 
 	if (anim != NULL) {
 		if (anim->misc_any_packed) {
-			matte_deallocate_series(anim->series_id[anim->misc_packed_series], true);
+			const int seriesId = anim->series_id[anim->misc_packed_series];
+			if (seriesId >= 0)
+				matte_deallocate_series(seriesId, true);
 		}
 		for (count = anim->num_series - 1; count >= 0; count--) {
-			if (!anim->misc_any_packed || (count != anim->misc_packed_series)) {
+			if ((!anim->misc_any_packed || (count != anim->misc_packed_series)) &&
+					anim->series_id[count] >= 0) {
 				matte_deallocate_series(anim->series_id[count], true);
 			}
 		}
@@ -407,6 +411,15 @@ AnimPtr anim_load(const char *file_name, Buffer *orig, Buffer *depth,
 
 	loader_close(&load_handle);
 
+	// The Macintosh AA_INTERFACE loader returns here. Its native resource
+	// forks retain the controllers and backgrounds, but deliberately omit the
+	// DOS-only sprite series named in the controller headers.
+	if (anim_in.background_type == AA_INTERFACE && !g_engine->hasInterfaceAnimations()) {
+		error_flag = false;
+		anim_error = 0;
+		goto done;
+	}
+
 	if (anim->load_flags & AA_LOAD_FONT) {
 		temp_buf[0] = 0;
 		if (star_search)
diff --git a/engines/mads/core/inter.cpp b/engines/mads/core/inter.cpp
index f8cf4873c4e..dc2f0cf8cbc 100644
--- a/engines/mads/core/inter.cpp
+++ b/engines/mads/core/inter.cpp
@@ -2193,8 +2193,10 @@ void inter_main_loop(int allow_input) {
 				image_inter_list[count].flags = IMAGE_ERASE;
 			}
 		}
-		inter_background_animation();
-		inter_spinning_object();
+		if (g_engine->hasInterfaceAnimations()) {
+			inter_background_animation();
+			inter_spinning_object();
+		}
 		inter_base_time = now_time + 6;
 	}
 }
diff --git a/engines/mads/mads.h b/engines/mads/mads.h
index 2f3f11639b2..15a72a6f1a2 100644
--- a/engines/mads/mads.h
+++ b/engines/mads/mads.h
@@ -168,6 +168,7 @@ public:
 	virtual void global_sound_driver() = 0;
 	virtual void global_game_main_loop() {}
 	virtual void global_verb_filter() {}
+	virtual bool hasInterfaceAnimations() const { return true; }
 	virtual void player_keep_walking();
 
 	void playSpeech(Audio::AudioStream *stream);
diff --git a/engines/mads/nebular/mac_nebular.cpp b/engines/mads/nebular/mac_nebular.cpp
index c1ccc733ad7..5cdde38cbf2 100644
--- a/engines/mads/nebular/mac_nebular.cpp
+++ b/engines/mads/nebular/mac_nebular.cpp
@@ -733,5 +733,12 @@ void RexNebularEngine::hideMacPopup() {
 		_macNebular->hidePopup();
 }
 
+bool RexNebularEngine::hasInterfaceAnimations() const {
+	// Macintosh CODE 7 stops loading an AA_INTERFACE controller before its
+	// DOS sprite-series list. The Mac resource set accordingly contains the
+	// controllers and InBx backgrounds, but not those subordinate series.
+	return _macNebular == nullptr;
+}
+
 } // namespace RexNebular
 } // namespace MADS
diff --git a/engines/mads/nebular/nebular.h b/engines/mads/nebular/nebular.h
index 02f2a646190..41fc9b52ab4 100644
--- a/engines/mads/nebular/nebular.h
+++ b/engines/mads/nebular/nebular.h
@@ -74,6 +74,7 @@ public:
 	void global_error_code() override;
 	void global_room_init() override {}
 	void global_sound_driver() override;
+	bool hasInterfaceAnimations() const override;
 };
 
 } // namespace RexNebular


Commit: 80ddc80b0c0d59061ebc76f22273e81a0b8b0543
    https://github.com/scummvm/scummvm/commit/80ddc80b0c0d59061ebc76f22273e81a0b8b0543
Author: fusefib (fibofuse at gmail.com)
Date: 2026-08-06T12:27:16+10:00

Commit Message:
MADS: Isolate Macintosh specific interface presentation

Replace direct Macintosh Rex checks in shared interface and popup code
with behavior-preserving engine hooks. Group the Rex implementations
with the rest of the Macintosh presentation adapter, while every DOS
engine keeps the existing defaults.

Assisted-by: Codex:GPT-5.4

Changed paths:
    engines/mads/core/inter.cpp
    engines/mads/core/popup.cpp
    engines/mads/mads.h
    engines/mads/nebular/mac_nebular.cpp
    engines/mads/nebular/nebular.h
    engines/mads/nebular/popup.cpp
    engines/mads/nebular/popup.h


diff --git a/engines/mads/core/inter.cpp b/engines/mads/core/inter.cpp
index dc2f0cf8cbc..1a6d9b23a26 100644
--- a/engines/mads/core/inter.cpp
+++ b/engines/mads/core/inter.cpp
@@ -140,11 +140,6 @@ int  inter_sentence_handle = -1;       /* Sentence message handle (for matte) */
 static int inter_sentence_shadow_handle = -1;
 int  inter_sentence_changed = false;    /* Mark if sentence contents changed   */
 
-enum {
-	kMacSentenceColor = 15,
-	kMacSentenceShadowColor = 8
-};
-
 int  inter_look_around;                 /* "Look around" command            */
 
 int  inter_command;                     /* Vocab # of sentence's verb       */
@@ -2166,18 +2161,16 @@ void inter_main_loop(int allow_input) {
 			x = (video_x >> 1) - (width >> 1);
 			y = (viewing_at_y + scr_work.y - 1) - 12;
 
-			const bool isMacRex = g_engine->getGameID() == GType_RexNebular &&
-				g_engine->getPlatform() == Common::kPlatformMacintosh;
-			if (isMacRex) {
-				// Native large-window captions use a light face with a dark
-				// one-pixel offset, rather than the DOS interface cyan.
+			byte sentenceColor = g_engine->getGameID() == GType_RexNebular ?
+				INTER_MESSAGE_COLOR_REX : INTER_MESSAGE_COLOR;
+			byte shadowColor = 0;
+			if (g_engine->getInterfaceSentenceColors(sentenceColor, shadowColor)) {
 				inter_sentence_shadow_handle = matte_add_message(use_font,
-					inter_sentence, x + 1, y + 1, kMacSentenceShadowColor,
+					inter_sentence, x + 1, y + 1, shadowColor,
 					use_spacing);
 			}
 			inter_sentence_handle = matte_add_message(use_font, inter_sentence, x, y,
-				isMacRex ? kMacSentenceColor :
-					(g_engine->getGameID() == GType_RexNebular ? INTER_MESSAGE_COLOR_REX : INTER_MESSAGE_COLOR),
+				sentenceColor,
 				use_spacing);
 		}
 		inter_sentence_changed = false;
diff --git a/engines/mads/core/popup.cpp b/engines/mads/core/popup.cpp
index 4faf3044d99..f60e3694704 100644
--- a/engines/mads/core/popup.cpp
+++ b/engines/mads/core/popup.cpp
@@ -780,9 +780,7 @@ void popup_destroy() {
 	int x, y;
 	int xs, ys;
 
-	if (g_engine->getGameID() == GType_RexNebular &&
-			g_engine->getPlatform() == Common::kPlatformMacintosh)
-		RexNebular::popup_close();
+	g_engine->onPopupDestroyed();
 
 	if (box->active && box->screen_saved) {
 		// Always restore the screen from scr_main — it spans both the game
diff --git a/engines/mads/mads.h b/engines/mads/mads.h
index 15a72a6f1a2..18eb8a05905 100644
--- a/engines/mads/mads.h
+++ b/engines/mads/mads.h
@@ -168,7 +168,16 @@ public:
 	virtual void global_sound_driver() = 0;
 	virtual void global_game_main_loop() {}
 	virtual void global_verb_filter() {}
+
+	// Optional Macintosh presentation hooks. Defaults preserve the shared
+	// MADS rendering path used by DOS releases.
 	virtual bool hasInterfaceAnimations() const { return true; }
+	virtual bool drawPopup() { return false; }
+	virtual void onPopupDestroyed() {}
+	virtual bool getInterfaceSentenceColors(byte &, byte &) const {
+		return false;
+	}
+
 	virtual void player_keep_walking();
 
 	void playSpeech(Audio::AudioStream *stream);
diff --git a/engines/mads/nebular/mac_nebular.cpp b/engines/mads/nebular/mac_nebular.cpp
index 5cdde38cbf2..20969eed2ef 100644
--- a/engines/mads/nebular/mac_nebular.cpp
+++ b/engines/mads/nebular/mac_nebular.cpp
@@ -700,6 +700,10 @@ void MacNebular::hidePopup() {
 	presentScreen(0);
 }
 
+// -------------------------------------------------------------------------
+// RexNebularEngine Macintosh presentation hooks
+// -------------------------------------------------------------------------
+
 void RexNebularEngine::applyGameSettings() {
 	Engine::applyGameSettings();
 	if (_macNebular)
@@ -723,16 +727,28 @@ void RexNebularEngine::presentScreen(int shakeOffset) {
 		MADSEngine::presentScreen(shakeOffset);
 }
 
-void RexNebularEngine::showMacPopup() {
-	if (_macNebular)
-		_macNebular->showPopup();
+bool RexNebularEngine::drawPopup() {
+	if (!_macNebular)
+		return false;
+
+	_macNebular->showPopup();
+	return true;
 }
 
-void RexNebularEngine::hideMacPopup() {
+void RexNebularEngine::onPopupDestroyed() {
 	if (_macNebular)
 		_macNebular->hidePopup();
 }
 
+bool RexNebularEngine::getInterfaceSentenceColors(byte &foreground, byte &shadow) const {
+	if (!_macNebular)
+		return false;
+
+	foreground = kMacNormalTextColor;
+	shadow = kMacBlackColor;
+	return true;
+}
+
 bool RexNebularEngine::hasInterfaceAnimations() const {
 	// Macintosh CODE 7 stops loading an AA_INTERFACE controller before its
 	// DOS sprite-series list. The Mac resource set accordingly contains the
@@ -740,5 +756,9 @@ bool RexNebularEngine::hasInterfaceAnimations() const {
 	return _macNebular == nullptr;
 }
 
+// -------------------------------------------------------------------------
+// End RexNebularEngine Macintosh presentation hooks
+// -------------------------------------------------------------------------
+
 } // namespace RexNebular
 } // namespace MADS
diff --git a/engines/mads/nebular/nebular.h b/engines/mads/nebular/nebular.h
index 41fc9b52ab4..199908f0b32 100644
--- a/engines/mads/nebular/nebular.h
+++ b/engines/mads/nebular/nebular.h
@@ -60,8 +60,6 @@ public:
 
 	Common::Error run() override;
 	void syncRoom(Common::Serializer &s) override;
-	void showMacPopup();
-	void hideMacPopup();
 
 	int main_copy_verify() override;
 	void global_init_code() override;
@@ -75,6 +73,9 @@ public:
 	void global_room_init() override {}
 	void global_sound_driver() override;
 	bool hasInterfaceAnimations() const override;
+	bool drawPopup() override;
+	void onPopupDestroyed() override;
+	bool getInterfaceSentenceColors(byte &foreground, byte &shadow) const override;
 };
 
 } // namespace RexNebular
diff --git a/engines/mads/nebular/popup.cpp b/engines/mads/nebular/popup.cpp
index 5a60947042b..54ad8bcd377 100644
--- a/engines/mads/nebular/popup.cpp
+++ b/engines/mads/nebular/popup.cpp
@@ -94,10 +94,8 @@ static int popup_draw_content(int x, int y, int xs, int ys, int unknown, byte co
 }
 
 void popup_draw() {
-	if (g_engine->getPlatform() == Common::kPlatformMacintosh) {
-		static_cast<RexNebularEngine *>(g_engine)->showMacPopup();
+	if (g_engine->drawPopup())
 		return;
-	}
 
 	int askY;
 
@@ -156,11 +154,6 @@ void popup_draw() {
 	mouse_show();
 }
 
-void popup_close() {
-	if (g_engine->getPlatform() == Common::kPlatformMacintosh)
-		static_cast<RexNebularEngine *>(g_engine)->hideMacPopup();
-}
-
 void popup_setup_cycle() {
 	font_set_colors(-1, DIALOG_BLACK_COLOR, DIALOG_BLACK_COLOR, DIALOG_BLACK_COLOR);
 
diff --git a/engines/mads/nebular/popup.h b/engines/mads/nebular/popup.h
index a1a04bd58a4..5d35612617c 100644
--- a/engines/mads/nebular/popup.h
+++ b/engines/mads/nebular/popup.h
@@ -30,7 +30,6 @@ namespace RexNebular {
 
 extern void popup_init();
 extern void popup_draw();
-extern void popup_close();
 extern void popup_setup_cycle();
 extern void popup_update_ask(const char *string, int maxlen);
 




More information about the Scummvm-git-logs mailing list