[Scummvm-git-logs] scummvm master -> 223a437ce3afd99495314e403f463513f0cc9991
dreammaster
noreply at scummvm.org
Sat Aug 8 06:27:37 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:
f1f1d208ee MADS: NEBULAR: Fix Macintosh GMM save/load
72a927f1f2 MADS: Hook Macintosh original menus gameoption
dcf9766f39 MADS: NEBULAR: Add original Macintosh menu framework
ef6f275a44 MADS: NEBULAR: Present original Macintosh composition
223a437ce3 MADS: NEBULAR: Implement Macintosh menu actions
Commit: f1f1d208ee5065ba4e490813fdaed0fd810fdefe
https://github.com/scummvm/scummvm/commit/f1f1d208ee5065ba4e490813fdaed0fd810fdefe
Author: fusefib (fibofuse at gmail.com)
Date: 2026-08-08T16:27:31+10:00
Commit Message:
MADS: NEBULAR: Fix Macintosh GMM save/load
Changed paths:
engines/mads/nebular/mac_menus.cpp
diff --git a/engines/mads/nebular/mac_menus.cpp b/engines/mads/nebular/mac_menus.cpp
index 14b6138c90e..a697d8e5351 100644
--- a/engines/mads/nebular/mac_menus.cpp
+++ b/engines/mads/nebular/mac_menus.cpp
@@ -65,13 +65,13 @@ void selectMacintoshDifficulty() {
void macintoshGameMenu() {
g_engine->flushKeys();
+ const int requestedMenu = kernel.activate_menu;
+ kernel.activate_menu = GAME_NO_MENU;
- if (kernel.activate_menu == GAME_DIFFICULTY_MENU)
+ if (requestedMenu == GAME_DIFFICULTY_MENU)
selectMacintoshDifficulty();
- else if (kernel.activate_menu != GAME_NO_MENU)
+ else if (requestedMenu != GAME_NO_MENU)
g_engine->openMainMenuDialog();
-
- kernel.activate_menu = GAME_NO_MENU;
}
} // namespace RexNebular
Commit: 72a927f1f297e63f414e7fece5292ccdd9173898
https://github.com/scummvm/scummvm/commit/72a927f1f297e63f414e7fece5292ccdd9173898
Author: fusefib (fibofuse at gmail.com)
Date: 2026-08-08T16:27:31+10:00
Commit Message:
MADS: Hook Macintosh original menus gameoption
Changed paths:
engines/mads/detection.h
engines/mads/detection_tables.h
engines/mads/metaengine.cpp
diff --git a/engines/mads/detection.h b/engines/mads/detection.h
index fe2b406b3a6..20b72658739 100644
--- a/engines/mads/detection.h
+++ b/engines/mads/detection.h
@@ -60,6 +60,8 @@ struct MADSGameDescription {
#define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS7
+#define GAMEOPTION_ORIGINAL_MAC_MENUS GUIO_GAMEOPTIONS8
+
} // namespace MADS
#endif
diff --git a/engines/mads/detection_tables.h b/engines/mads/detection_tables.h
index f801b6cb1f5..24260a79829 100644
--- a/engines/mads/detection_tables.h
+++ b/engines/mads/detection_tables.h
@@ -160,9 +160,9 @@ static const MADSGameDescription gameDescriptions[] = {
Common::kPlatformMacintosh,
ADGF_UNSTABLE,
#ifdef USE_TTS
- GUIO10(GUIO_NOMIDI, GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_EASY_MOUSE, GAMEOPTION_ANIMATED_INVENTORY, GAMEOPTION_ANIMATED_INTERFACE, GAMEOPTION_NAUGHTY_MODE, GAMEOPTION_TTS_NARRATOR, GAMEOPTION_COPY_PROTECTION, GAMEOPTION_ORIGINAL_SAVELOAD)
+ GUIO10(GUIO_NOMIDI, GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_EASY_MOUSE, GAMEOPTION_ANIMATED_INVENTORY, GAMEOPTION_ANIMATED_INTERFACE, GAMEOPTION_NAUGHTY_MODE, GAMEOPTION_TTS_NARRATOR, GAMEOPTION_COPY_PROTECTION, GAMEOPTION_ORIGINAL_MAC_MENUS)
#else
- GUIO9(GUIO_NOMIDI, GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_EASY_MOUSE, GAMEOPTION_ANIMATED_INVENTORY, GAMEOPTION_ANIMATED_INTERFACE, GAMEOPTION_NAUGHTY_MODE, GAMEOPTION_COPY_PROTECTION, GAMEOPTION_ORIGINAL_SAVELOAD)
+ GUIO9(GUIO_NOMIDI, GUIO_NOSPEECH, GUIO_NOASPECT, GAMEOPTION_EASY_MOUSE, GAMEOPTION_ANIMATED_INVENTORY, GAMEOPTION_ANIMATED_INTERFACE, GAMEOPTION_NAUGHTY_MODE, GAMEOPTION_COPY_PROTECTION, GAMEOPTION_ORIGINAL_MAC_MENUS)
#endif
},
GType_RexNebular,
diff --git a/engines/mads/metaengine.cpp b/engines/mads/metaengine.cpp
index f9427f7bc3b..e35413c5035 100644
--- a/engines/mads/metaengine.cpp
+++ b/engines/mads/metaengine.cpp
@@ -115,6 +115,18 @@ static const ADExtraGuiOptionsMap optionsList[] = {
}
},
+ {
+ GAMEOPTION_ORIGINAL_MAC_MENUS,
+ {
+ _s("Use original Macintosh menus (experimental)"),
+ _s("Use the Macintosh menu bar and original desktop framing"),
+ "original_mac_menus",
+ false,
+ 0,
+ 0
+ }
+ },
+
#ifdef USE_TTS
{
GAMEOPTION_TTS_NARRATOR,
Commit: dcf9766f39d307b061f4c79e870a12d8d923799e
https://github.com/scummvm/scummvm/commit/dcf9766f39d307b061f4c79e870a12d8d923799e
Author: fusefib (fibofuse at gmail.com)
Date: 2026-08-08T16:27:31+10:00
Commit Message:
MADS: NEBULAR: Add original Macintosh menu framework
Assisted-by: Codex:GPT-5.4
Changed paths:
engines/mads/mads.cpp
engines/mads/mads.h
engines/mads/nebular/mac_menus.cpp
engines/mads/nebular/mac_menus.h
engines/mads/nebular/mac_nebular.cpp
engines/mads/nebular/mac_nebular.h
engines/mads/nebular/mac_resources.cpp
engines/mads/nebular/mac_resources.h
engines/mads/nebular/nebular.h
diff --git a/engines/mads/mads.cpp b/engines/mads/mads.cpp
index 559ec692450..63eb7f5c0bc 100644
--- a/engines/mads/mads.cpp
+++ b/engines/mads/mads.cpp
@@ -258,6 +258,9 @@ void MADSEngine::pollEvents() {
// Poll for events
Common::Event e;
while (g_system->getEventManager()->pollEvent(e)) {
+ if (handleMacEvent(e))
+ continue;
+
bool isMouse = false;
switch (e.type) {
case Common::EVENT_LBUTTONDOWN:
diff --git a/engines/mads/mads.h b/engines/mads/mads.h
index 19526e5f4b4..e98aee825c6 100644
--- a/engines/mads/mads.h
+++ b/engines/mads/mads.h
@@ -92,6 +92,8 @@ protected:
virtual Common::Point gameToScreen(const Common::Point &point) const;
virtual void presentScreen(int shakeOffset);
+ virtual bool handleMacEvent(Common::Event &event) { return false; }
+
bool hasFeature(EngineFeature f) const override;
void pollEvents();
diff --git a/engines/mads/nebular/mac_menus.cpp b/engines/mads/nebular/mac_menus.cpp
index a697d8e5351..4f8907866ff 100644
--- a/engines/mads/nebular/mac_menus.cpp
+++ b/engines/mads/nebular/mac_menus.cpp
@@ -20,17 +20,188 @@
*/
#include "common/config-manager.h"
+#include "common/debug.h"
+#include "common/events.h"
+#include "common/stream.h"
+#include "common/system.h"
#include "common/translation.h"
+#include "graphics/macgui/macmenu.h"
+#include "graphics/macgui/macwindowmanager.h"
+#include "graphics/managed_surface.h"
+#include "graphics/paletteman.h"
#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"
+#include "mads/nebular/mac_resources.h"
+#include "mads/nebular/nebular.h"
namespace MADS {
namespace RexNebular {
+namespace {
+
+struct MenuResourceItem {
+ Common::String text;
+ byte key = 0;
+ byte mark = 0;
+ byte style = 0;
+ bool enabled = false;
+};
+
+struct MenuResource {
+ uint16 id = 0;
+ Common::String title;
+ Common::Array<MenuResourceItem> items;
+};
+
+bool readMenuResource(Common::SeekableReadStream &stream, MenuResource &resource) {
+ if (stream.size() < 15)
+ return false;
+
+ resource.id = stream.readUint16BE();
+ stream.skip(8); // Width, height, resource ID, and placeholder.
+ const uint32 enabledItems = stream.readUint32BE();
+ resource.title = stream.readPascalString();
+
+ for (uint itemNumber = 1; stream.pos() < stream.size(); ++itemNumber) {
+ MenuResourceItem item;
+ item.text = stream.readPascalString();
+ if (item.text.empty())
+ return !stream.err();
+ if (stream.pos() + 4 > stream.size())
+ return false;
+
+ /* byte icon = */ stream.readByte();
+ item.key = stream.readByte();
+ item.mark = stream.readByte();
+ item.style = stream.readByte();
+ item.enabled = itemNumber < 32 &&
+ (enabledItems & (1U << itemNumber)) != 0;
+ resource.items.push_back(item);
+ }
+
+ return false;
+}
+
+} // namespace
+
+MacNebularMenu::MacNebularMenu(RexNebularEngine &engine,
+ MacResourceProvider &resources, Graphics::ManagedSurface &screen) :
+ _engine(engine), _resources(resources), _screen(screen) {
+}
+
+MacNebularMenu::~MacNebularMenu() {
+ delete _windowManager;
+}
+
+bool MacNebularMenu::initialize() {
+ // MacWindowManager installs its default palette during construction. Save
+ // and restore the active MADS palette before it can reach the backend.
+ byte palette[256 * 3];
+ g_system->getPaletteManager()->grabPalette(palette, 0, 256);
+
+ const uint32 mode = Graphics::kWMModeNoDesktop |
+ Graphics::kWMModalMenuMode | Graphics::kWMModeNoCursorOverride |
+ Graphics::kWMModeForceMacFonts | Graphics::kWMModeNoSystemRedraw |
+ Graphics::kWMNoScummVMWallpaper;
+ _windowManager = new Graphics::MacWindowManager(mode);
+ g_system->getPaletteManager()->setPalette(palette, 0, 256);
+ _windowManager->setEngine(&_engine);
+ _windowManager->setScreen(&_screen);
+ _windowManager->passPalette(palette, 256);
+ memcpy(_palette, palette, sizeof(_palette));
+ _paletteValid = true;
+ _menu = _windowManager->addMenu();
+ _menu->setCommandsCallback(menuCallback, this);
+
+ if (!loadMenuResource(1000) || !loadMenuResource(1001) ||
+ !loadMenuResource(1002) || !loadMenuResource(2001) ||
+ !loadMenuResource(2101, _menu->getSubmenu(nullptr, 3), 2) ||
+ !loadMenuResource(2102, _menu->getSubmenu(nullptr, 3), 3) ||
+ !loadMenuResource(2000)) {
+ warning("Could not construct the Macintosh Rex menu bar");
+ return false;
+ }
+
+ _menu->calcDimensions();
+ disableActions();
+ return true;
+}
+
+bool MacNebularMenu::loadMenuResource(uint16 resourceID,
+ Graphics::MacMenuSubMenu *parent, int parentItem) {
+ Common::SeekableReadStream *stream = _resources.openResource(
+ MacResourceProvider::kApplicationContainer, MKTAG('M', 'E', 'N', 'U'),
+ resourceID);
+ if (!stream)
+ return false;
+
+ MenuResource resource;
+ const bool valid = readMenuResource(*stream, resource);
+ delete stream;
+ if (!valid)
+ return false;
+
+ Graphics::MacMenuSubMenu *submenu = nullptr;
+ if (parent) {
+ if (parentItem < 0 || (uint)parentItem >= parent->items.size())
+ return false;
+ submenu = _menu->addSubMenu(parent, parentItem);
+ } else {
+ const int menuIndex = _menu->addMenuItem(nullptr, resource.title);
+ submenu = _menu->addSubMenu(nullptr, menuIndex);
+ }
+
+ for (uint index = 0; index < resource.items.size(); ++index) {
+ const MenuResourceItem &item = resource.items[index];
+ const char shortcut = item.key == 0x1b ? 0 : item.key;
+ _menu->addMenuItem(submenu, item.text,
+ (resource.id << 16) | index, item.style, shortcut, item.enabled);
+ }
+
+ return true;
+}
+
+void MacNebularMenu::disableActions() {
+ for (int menuIndex = 0; menuIndex < _menu->numberOfMenus(); ++menuIndex) {
+ Graphics::MacMenuItem *topLevel = _menu->getMenuItem(menuIndex);
+ for (int itemIndex = 0;
+ itemIndex < _menu->numberOfMenuItems(topLevel); ++itemIndex)
+ _menu->setEnabled(_menu->getSubMenuItem(topLevel, itemIndex), false);
+ }
+}
+
+void MacNebularMenu::syncPalette() {
+ byte palette[256 * 3];
+ g_system->getPaletteManager()->grabPalette(palette, 0, 256);
+ if (_paletteValid && memcmp(_palette, palette, sizeof(_palette)) == 0)
+ return;
+
+ memcpy(_palette, palette, sizeof(_palette));
+ _paletteValid = true;
+ _windowManager->passPalette(_palette, 256);
+}
+
+bool MacNebularMenu::processEvent(Common::Event &event) {
+ return _windowManager && _windowManager->processEvent(event);
+}
+
+void MacNebularMenu::draw() {
+ if (!_menu)
+ return;
+
+ syncPalette();
+ _menu->draw(&_screen, true);
+}
+
+void MacNebularMenu::menuCallback(int, Common::String &, void *data) {
+ MacNebularMenu *menus = (MacNebularMenu *)data;
+ menus->_menu->closeMenu();
+}
+
void selectMacintoshDifficulty() {
const int configuredDifficulty = ConfMan.getInt("difficulty");
if (configuredDifficulty >= DIFFICULTY_HARD && configuredDifficulty <= DIFFICULTY_EASY) {
diff --git a/engines/mads/nebular/mac_menus.h b/engines/mads/nebular/mac_menus.h
index c40fc913136..26d4ebe4ad7 100644
--- a/engines/mads/nebular/mac_menus.h
+++ b/engines/mads/nebular/mac_menus.h
@@ -22,9 +22,52 @@
#ifndef MADS_NEBULAR_MAC_MENUS_H
#define MADS_NEBULAR_MAC_MENUS_H
+#include "common/scummsys.h"
+
+namespace Common {
+struct Event;
+class String;
+}
+
+namespace Graphics {
+class MacMenu;
+class MacMenuSubMenu;
+class MacWindowManager;
+class ManagedSurface;
+}
+
namespace MADS {
namespace RexNebular {
+class MacResourceProvider;
+class RexNebularEngine;
+
+class MacNebularMenu {
+private:
+ RexNebularEngine &_engine;
+ MacResourceProvider &_resources;
+ Graphics::ManagedSurface &_screen;
+ Graphics::MacWindowManager *_windowManager = nullptr;
+ Graphics::MacMenu *_menu = nullptr;
+ byte _palette[256 * 3] = {};
+ bool _paletteValid = false;
+
+ static void menuCallback(int command, Common::String &name, void *data);
+ bool loadMenuResource(uint16 resourceID,
+ Graphics::MacMenuSubMenu *parent = nullptr, int parentItem = -1);
+ void disableActions();
+ void syncPalette();
+
+public:
+ MacNebularMenu(RexNebularEngine &engine, MacResourceProvider &resources,
+ Graphics::ManagedSurface &screen);
+ ~MacNebularMenu();
+
+ bool initialize();
+ bool processEvent(Common::Event &event);
+ void draw();
+};
+
void selectMacintoshDifficulty();
void macintoshGameMenu();
diff --git a/engines/mads/nebular/mac_nebular.cpp b/engines/mads/nebular/mac_nebular.cpp
index 20969eed2ef..55f730d5ce2 100644
--- a/engines/mads/nebular/mac_nebular.cpp
+++ b/engines/mads/nebular/mac_nebular.cpp
@@ -20,7 +20,9 @@
*/
#include "engines/util.h"
+#include "common/config-manager.h"
#include "common/debug.h"
+#include "common/events.h"
#include "common/system.h"
#include "graphics/font.h"
#include "graphics/managed_surface.h"
@@ -30,6 +32,7 @@
#include "mads/core/object.h"
#include "mads/core/pal.h"
#include "mads/core/screen.h"
+#include "mads/nebular/mac_menus.h"
#include "mads/nebular/mac_nebular.h"
#include "mads/nebular/mac_resources.h"
#include "mads/nebular/nebular.h"
@@ -422,6 +425,8 @@ MacNebular::MacNebular(RexNebularEngine &engine) : _engine(engine) {
}
MacNebular::~MacNebular() {
+ delete _menus;
+
if (!_resources)
return;
@@ -433,6 +438,8 @@ MacNebular::~MacNebular() {
void MacNebular::initGraphics() {
::initGraphics(kMacScreenWidth, kMacScreenHeight);
+ _output.create(kMacScreenWidth, kMacScreenHeight,
+ Graphics::PixelFormat::createFormatCLUT8());
}
bool MacNebular::initResources() {
@@ -446,6 +453,14 @@ bool MacNebular::initResources() {
env_set_resource_provider(_resources);
_engine._soundManager = new Sound::MacSoundManager(
_engine._mixer, _engine._soundFlag, _resources);
+
+ if (ConfMan.getBool("original_mac_menus")) {
+ _menus = new MacNebularMenu(_engine, *_resources, _output);
+ if (!_menus->initialize()) {
+ delete _menus;
+ _menus = nullptr;
+ }
+ }
return true;
}
@@ -487,14 +502,13 @@ Common::Point MacNebular::gameToScreen(const Common::Point &point) const {
}
void MacNebular::presentScreen(int shakeOffset) {
- _output.resize(kMacScreenWidth * kMacScreenHeight);
- memset(_output.data(), kMacBlackColor, _output.size());
+ _output.fillRect(_output.getBounds(), kMacBlackColor);
// Native large-window mode doubles the 320x156 scene in both axes.
for (int y = 0; y < 156; ++y) {
const byte *source = (const byte *)_engine._screen->getBasePtr(0, y);
- byte *line1 = _output.data() + (y * 2) * kMacScreenWidth;
- byte *line2 = line1 + kMacScreenWidth;
+ byte *line1 = (byte *)_output.getBasePtr(0, y * 2);
+ byte *line2 = (byte *)_output.getBasePtr(0, y * 2 + 1);
for (int x = 0; x < 320; ++x) {
const byte color = source[(x + shakeOffset) % 320];
line1[x * 2] = color;
@@ -560,8 +574,7 @@ void MacNebular::presentScreen(int shakeOffset) {
drawMacInterfaceState(panel, *interfaceFont);
for (int y = 0; y < kMacInterfaceHeight; ++y) {
- memcpy(_output.data() +
- (kMacSceneHeight + y) * kMacScreenWidth + kMacInterfaceX,
+ memcpy(_output.getBasePtr(kMacInterfaceX, kMacSceneHeight + y),
panel.getBasePtr(0, y), kMacInterfaceWidth);
}
} else {
@@ -569,8 +582,8 @@ void MacNebular::presentScreen(int shakeOffset) {
// fallback by scaling the shared 320x44 interface into its Mac bounds.
for (int y = 0; y < kMacInterfaceHeight; ++y) {
const byte *source = (const byte *)_engine._screen->getBasePtr(0, 156 + y / 2);
- byte *target = _output.data() +
- (kMacSceneHeight + y) * kMacScreenWidth + kMacInterfaceX;
+ byte *target = (byte *)_output.getBasePtr(
+ kMacInterfaceX, kMacSceneHeight + y);
for (int x = 0; x < kMacInterfaceWidth; ++x)
target[x] = source[x * 320 / kMacInterfaceWidth];
}
@@ -587,7 +600,7 @@ void MacNebular::presentScreen(int shakeOffset) {
const int width = MIN<int>(_popup.w - sourceX,
kMacScreenWidth - targetX);
if (width > 0)
- memcpy(_output.data() + targetY * kMacScreenWidth + targetX,
+ memcpy(_output.getBasePtr(targetX, targetY),
_popup.getBasePtr(sourceX, y), width);
}
}
@@ -597,12 +610,19 @@ void MacNebular::presentScreen(int shakeOffset) {
_layoutLogged = true;
}
- g_system->copyRectToScreen(_output.data(), kMacScreenWidth,
+ if (_menus)
+ _menus->draw();
+
+ g_system->copyRectToScreen(_output.getPixels(), _output.pitch,
0, 0, kMacScreenWidth, kMacScreenHeight);
g_system->updateScreen();
_engine._screen->clearDirtyRects();
}
+bool MacNebular::handleMacEvent(Common::Event &event) {
+ return _menus && _menus->processEvent(event);
+}
+
void MacNebular::showPopup() {
if (!_resources || !box)
return;
@@ -727,6 +747,10 @@ void RexNebularEngine::presentScreen(int shakeOffset) {
MADSEngine::presentScreen(shakeOffset);
}
+bool RexNebularEngine::handleMacEvent(Common::Event &event) {
+ return _macNebular && _macNebular->handleMacEvent(event);
+}
+
bool RexNebularEngine::drawPopup() {
if (!_macNebular)
return false;
diff --git a/engines/mads/nebular/mac_nebular.h b/engines/mads/nebular/mac_nebular.h
index f4560c50d78..65b883d405d 100644
--- a/engines/mads/nebular/mac_nebular.h
+++ b/engines/mads/nebular/mac_nebular.h
@@ -22,21 +22,26 @@
#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 Common {
+struct Event;
+}
+
namespace MADS {
namespace RexNebular {
class MacResourceProvider;
+class MacNebularMenu;
class RexNebularEngine;
class MacNebular {
private:
RexNebularEngine &_engine;
MacResourceProvider *_resources = nullptr;
- Common::Array<byte> _output;
+ MacNebularMenu *_menus = nullptr;
+ Graphics::ManagedSurface _output;
Graphics::ManagedSurface _popup;
Common::Rect _popupRect;
bool _popupActive = false;
@@ -51,6 +56,7 @@ public:
void applyGameSettings();
Common::Point screenToGame(const Common::Point &point) const;
Common::Point gameToScreen(const Common::Point &point) const;
+ bool handleMacEvent(Common::Event &event);
void presentScreen(int shakeOffset);
void showPopup();
void hidePopup();
diff --git a/engines/mads/nebular/mac_resources.cpp b/engines/mads/nebular/mac_resources.cpp
index fc9ccd1fd6d..3cff373d887 100644
--- a/engines/mads/nebular/mac_resources.cpp
+++ b/engines/mads/nebular/mac_resources.cpp
@@ -188,6 +188,12 @@ Common::MacResManager *MacResourceProvider::getContainer(Container container) co
return _containers[container];
}
+Common::SeekableReadStream *MacResourceProvider::openResource(
+ Container container, uint32 type, uint16 id) const {
+ Common::MacResManager *resourceContainer = getContainer(container);
+ return resourceContainer ? resourceContainer->getResource(type, id) : nullptr;
+}
+
const Graphics::Font *MacResourceProvider::getDialogFont() {
if (!_fontManager)
return nullptr;
diff --git a/engines/mads/nebular/mac_resources.h b/engines/mads/nebular/mac_resources.h
index 6ef0b66521d..15730e034b9 100644
--- a/engines/mads/nebular/mac_resources.h
+++ b/engines/mads/nebular/mac_resources.h
@@ -29,6 +29,7 @@
namespace Common {
class MacResManager;
+class SeekableReadStream;
}
namespace Graphics {
@@ -65,6 +66,8 @@ public:
~MacResourceProvider() override;
bool load();
+ Common::SeekableReadStream *openResource(Container container, uint32 type,
+ uint16 id) const;
Common::SeekableReadStream *open(const char *filename) override;
bool exists(const char *filename) override;
bool allowsFallback(const char *filename) const override;
diff --git a/engines/mads/nebular/nebular.h b/engines/mads/nebular/nebular.h
index 199908f0b32..f3232243075 100644
--- a/engines/mads/nebular/nebular.h
+++ b/engines/mads/nebular/nebular.h
@@ -53,6 +53,7 @@ protected:
Common::Point screenToGame(const Common::Point &point) const override;
Common::Point gameToScreen(const Common::Point &point) const override;
void presentScreen(int shakeOffset) override;
+ bool handleMacEvent(Common::Event &event) override;
public:
RexNebularEngine(OSystem *syst, const MADSGameDescription *gameDesc);
Commit: ef6f275a4479154823ae75c8979b3f68be4af62d
https://github.com/scummvm/scummvm/commit/ef6f275a4479154823ae75c8979b3f68be4af62d
Author: fusefib (fibofuse at gmail.com)
Date: 2026-08-08T16:27:31+10:00
Commit Message:
MADS: NEBULAR: Present original Macintosh composition
Assisted-by: Codex:GPT-5.4
Changed paths:
engines/mads/nebular/mac_nebular.cpp
engines/mads/nebular/mac_nebular.h
diff --git a/engines/mads/nebular/mac_nebular.cpp b/engines/mads/nebular/mac_nebular.cpp
index 55f730d5ce2..55a89c982e8 100644
--- a/engines/mads/nebular/mac_nebular.cpp
+++ b/engines/mads/nebular/mac_nebular.cpp
@@ -49,7 +49,12 @@ enum {
kMacInterfaceWidth = 512,
kMacInterfaceHeight = 88,
kMacInterfaceX = (kMacScreenWidth - kMacInterfaceWidth) / 2,
- kMacScreenHeight = kMacSceneHeight + kMacInterfaceHeight,
+ kMacLegacyScreenHeight = kMacSceneHeight + kMacInterfaceHeight,
+ kMacDesktopHeight = 480,
+ kMacMenuBarHeight = 20,
+ kMacDesktopSceneY = kMacMenuBarHeight + 20,
+ kMacDesktopSeparatorY = kMacDesktopSceneY + kMacSceneHeight,
+ kMacDesktopInterfaceY = kMacDesktopSeparatorY + 1,
kMacBlackColor = 8,
kMacNormalTextColor = 15,
kMacLeftSelectColor = 13,
@@ -421,7 +426,8 @@ static bool isMacInterfaceSemanticPixel(int x, int y) {
return false;
}
-MacNebular::MacNebular(RexNebularEngine &engine) : _engine(engine) {
+MacNebular::MacNebular(RexNebularEngine &engine) :
+ _engine(engine), _useOriginalMenus(ConfMan.getBool("original_mac_menus")) {
}
MacNebular::~MacNebular() {
@@ -437,8 +443,9 @@ MacNebular::~MacNebular() {
}
void MacNebular::initGraphics() {
- ::initGraphics(kMacScreenWidth, kMacScreenHeight);
- _output.create(kMacScreenWidth, kMacScreenHeight,
+ const int height = _useOriginalMenus ? kMacDesktopHeight : kMacLegacyScreenHeight;
+ ::initGraphics(kMacScreenWidth, height);
+ _output.create(kMacScreenWidth, height,
Graphics::PixelFormat::createFormatCLUT8());
}
@@ -454,7 +461,7 @@ bool MacNebular::initResources() {
_engine._soundManager = new Sound::MacSoundManager(
_engine._mixer, _engine._soundFlag, _resources);
- if (ConfMan.getBool("original_mac_menus")) {
+ if (_useOriginalMenus) {
_menus = new MacNebularMenu(_engine, *_resources, _output);
if (!_menus->initialize()) {
delete _menus;
@@ -465,7 +472,7 @@ bool MacNebular::initResources() {
}
void MacNebular::applyGameSettings() {
- // The Macintosh port's 640x400 large-window mode uses square pixels.
+ // The Macintosh port's large-window presentation uses square pixels.
// DOS-style 320x200 aspect correction would stretch its scene and native
// interface vertically.
if (g_system->hasFeature(OSystem::kFeatureAspectRatioCorrection) &&
@@ -477,15 +484,19 @@ void MacNebular::applyGameSettings() {
}
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);
-
- if (point.y >= kMacSceneHeight && point.y < kMacScreenHeight &&
+ const int sceneY = _useOriginalMenus ? kMacDesktopSceneY : 0;
+ const int interfaceY = _useOriginalMenus ?
+ kMacDesktopInterfaceY : kMacSceneHeight;
+ if (point.y >= sceneY && point.y < sceneY + kMacSceneHeight)
+ return Common::Point(CLIP<int>(point.x / 2, 0, 319),
+ (point.y - sceneY) / 2);
+
+ if (point.y >= interfaceY && point.y < interfaceY + kMacInterfaceHeight &&
point.x >= kMacInterfaceX &&
point.x < kMacInterfaceX + kMacInterfaceWidth) {
return Common::Point(
(point.x - kMacInterfaceX) * 320 / kMacInterfaceWidth,
- 156 + (point.y - kMacSceneHeight) / 2);
+ 156 + (point.y - interfaceY) / 2);
}
// The native interface is narrower than the scene. Its side gutters are
@@ -494,21 +505,27 @@ Common::Point MacNebular::screenToGame(const Common::Point &point) const {
}
Common::Point MacNebular::gameToScreen(const Common::Point &point) const {
+ const int sceneY = _useOriginalMenus ? kMacDesktopSceneY : 0;
+ const int interfaceY = _useOriginalMenus ?
+ kMacDesktopInterfaceY : kMacSceneHeight;
if (point.y < 156)
- return Common::Point(point.x * 2, point.y * 2);
+ return Common::Point(point.x * 2, sceneY + point.y * 2);
return Common::Point(kMacInterfaceX + point.x * kMacInterfaceWidth / 320,
- kMacSceneHeight + (point.y - 156) * 2);
+ interfaceY + (point.y - 156) * 2);
}
void MacNebular::presentScreen(int shakeOffset) {
+ const int sceneY = _useOriginalMenus ? kMacDesktopSceneY : 0;
+ const int interfaceY = _useOriginalMenus ?
+ kMacDesktopInterfaceY : kMacSceneHeight;
_output.fillRect(_output.getBounds(), kMacBlackColor);
// Native large-window mode doubles the 320x156 scene in both axes.
for (int y = 0; y < 156; ++y) {
const byte *source = (const byte *)_engine._screen->getBasePtr(0, y);
- byte *line1 = (byte *)_output.getBasePtr(0, y * 2);
- byte *line2 = (byte *)_output.getBasePtr(0, y * 2 + 1);
+ byte *line1 = (byte *)_output.getBasePtr(0, sceneY + y * 2);
+ byte *line2 = (byte *)_output.getBasePtr(0, sceneY + y * 2 + 1);
for (int x = 0; x < 320; ++x) {
const byte color = source[(x + shakeOffset) % 320];
line1[x * 2] = color;
@@ -574,7 +591,7 @@ void MacNebular::presentScreen(int shakeOffset) {
drawMacInterfaceState(panel, *interfaceFont);
for (int y = 0; y < kMacInterfaceHeight; ++y) {
- memcpy(_output.getBasePtr(kMacInterfaceX, kMacSceneHeight + y),
+ memcpy(_output.getBasePtr(kMacInterfaceX, interfaceY + y),
panel.getBasePtr(0, y), kMacInterfaceWidth);
}
} else {
@@ -583,7 +600,7 @@ void MacNebular::presentScreen(int shakeOffset) {
for (int y = 0; y < kMacInterfaceHeight; ++y) {
const byte *source = (const byte *)_engine._screen->getBasePtr(0, 156 + y / 2);
byte *target = (byte *)_output.getBasePtr(
- kMacInterfaceX, kMacSceneHeight + y);
+ kMacInterfaceX, interfaceY + y);
for (int x = 0; x < kMacInterfaceWidth; ++x)
target[x] = source[x * 320 / kMacInterfaceWidth];
}
@@ -592,7 +609,7 @@ void MacNebular::presentScreen(int shakeOffset) {
if (_popupActive && !_popup.empty()) {
for (int y = 0; y < _popup.h; ++y) {
const int targetY = _popupRect.top + y;
- if (targetY < 0 || targetY >= kMacScreenHeight)
+ if (targetY < 0 || targetY >= _output.h)
continue;
const int targetX = MAX<int>(0, _popupRect.left);
@@ -606,7 +623,11 @@ void MacNebular::presentScreen(int shakeOffset) {
}
if (!_layoutLogged) {
- debug(2, "Presenting Macintosh Rex as 640x312 scene plus centered 512x88 interface");
+ if (_useOriginalMenus) {
+ debug(2, "Presenting Macintosh Rex in original 640x480 composition");
+ } else {
+ debug(2, "Presenting Macintosh Rex as 640x312 scene plus centered 512x88 interface");
+ }
_layoutLogged = true;
}
@@ -614,7 +635,7 @@ void MacNebular::presentScreen(int shakeOffset) {
_menus->draw();
g_system->copyRectToScreen(_output.getPixels(), _output.pitch,
- 0, 0, kMacScreenWidth, kMacScreenHeight);
+ 0, 0, kMacScreenWidth, _output.h);
g_system->updateScreen();
_engine._screen->clearDirtyRects();
}
@@ -668,8 +689,11 @@ void MacNebular::showPopup() {
appendWrappedMacPopupText(*font, paragraph, paragraphTab,
width - 20, lines);
+ const int popupAreaY = _useOriginalMenus ? kMacDesktopSceneY : 0;
+ const int popupAreaHeight = _useOriginalMenus ?
+ kMacSceneHeight : kMacLegacyScreenHeight;
const int height = CLIP<int>((int)lines.size() * 12 + 20, 20,
- kMacScreenHeight - 2);
+ popupAreaHeight - 2);
_popup.create(width, height, Graphics::PixelFormat::createFormatCLUT8());
_popup.fillRect(Common::Rect(width, height), kMacPopupColor);
_popup.frameRect(Common::Rect(width, height), kMacBlackColor);
@@ -704,9 +728,9 @@ void MacNebular::showPopup() {
_popupRect = Common::Rect(
(kMacScreenWidth - width) / 2,
- (kMacScreenHeight - height) / 2,
+ popupAreaY + (popupAreaHeight - height) / 2,
(kMacScreenWidth + width) / 2,
- (kMacScreenHeight + height) / 2);
+ popupAreaY + (popupAreaHeight + height) / 2);
_popupActive = true;
presentScreen(0);
}
diff --git a/engines/mads/nebular/mac_nebular.h b/engines/mads/nebular/mac_nebular.h
index 65b883d405d..79efd0ce051 100644
--- a/engines/mads/nebular/mac_nebular.h
+++ b/engines/mads/nebular/mac_nebular.h
@@ -44,6 +44,7 @@ private:
Graphics::ManagedSurface _output;
Graphics::ManagedSurface _popup;
Common::Rect _popupRect;
+ bool _useOriginalMenus;
bool _popupActive = false;
bool _layoutLogged = false;
Commit: 223a437ce3afd99495314e403f463513f0cc9991
https://github.com/scummvm/scummvm/commit/223a437ce3afd99495314e403f463513f0cc9991
Author: fusefib (fibofuse at gmail.com)
Date: 2026-08-08T16:27:31+10:00
Commit Message:
MADS: NEBULAR: Implement Macintosh menu actions
Assisted-by: Codex:GPT-5.4
Changed paths:
engines/mads/nebular/mac_menus.cpp
engines/mads/nebular/mac_menus.h
diff --git a/engines/mads/nebular/mac_menus.cpp b/engines/mads/nebular/mac_menus.cpp
index 4f8907866ff..9a0bbdb6ed5 100644
--- a/engines/mads/nebular/mac_menus.cpp
+++ b/engines/mads/nebular/mac_menus.cpp
@@ -33,6 +33,7 @@
#include "mads/core/config.h"
#include "mads/core/game.h"
#include "mads/core/kernel.h"
+#include "mads/core/sound_manager.h"
#include "mads/mads.h"
#include "mads/nebular/mac_menus.h"
#include "mads/nebular/mac_resources.h"
@@ -43,6 +44,24 @@ namespace RexNebular {
namespace {
+enum {
+ kFileMenu = 1,
+ kOptionsMenu = 3,
+ kWindowMenu = 4,
+
+ kFileOpen = (1001 << 16) | 2,
+ kFileSave = (1001 << 16) | 4,
+ kFileSaveAs = (1001 << 16) | 5,
+ kFileQuit = (1001 << 16) | 9,
+ kOptionNoSound = (2001 << 16),
+ kOptionEasyInterface = (2001 << 16) | 1,
+ kFadeSmooth = (101 << 16),
+ kFadeMedium = (101 << 16) | 1,
+ kFadeFast = (101 << 16) | 2,
+ kStoryNaughty = (102 << 16),
+ kStoryNice = (102 << 16) | 1
+};
+
struct MenuResourceItem {
Common::String text;
byte key = 0;
@@ -127,7 +146,7 @@ bool MacNebularMenu::initialize() {
}
_menu->calcDimensions();
- disableActions();
+ updateState();
return true;
}
@@ -149,6 +168,10 @@ bool MacNebularMenu::loadMenuResource(uint16 resourceID,
if (parent) {
if (parentItem < 0 || (uint)parentItem >= parent->items.size())
return false;
+ // In a classic MENU resource, the parent item's mark byte carries
+ // the hierarchical submenu ID. Refuse to attach a mismatched resource.
+ if (parent->items[parentItem]->checkSymbol != resource.id)
+ return false;
submenu = _menu->addSubMenu(parent, parentItem);
} else {
const int menuIndex = _menu->addMenuItem(nullptr, resource.title);
@@ -158,20 +181,135 @@ bool MacNebularMenu::loadMenuResource(uint16 resourceID,
for (uint index = 0; index < resource.items.size(); ++index) {
const MenuResourceItem &item = resource.items[index];
const char shortcut = item.key == 0x1b ? 0 : item.key;
- _menu->addMenuItem(submenu, item.text,
+ const int itemIndex = _menu->addMenuItem(submenu, item.text,
(resource.id << 16) | index, item.style, shortcut, item.enabled);
+ submenu->items[itemIndex]->checkSymbol = item.mark;
}
return true;
}
-void MacNebularMenu::disableActions() {
+Graphics::MacMenuItem *MacNebularMenu::getMenuItem(int menu, int item) const {
+ Graphics::MacMenuItem *topLevel = _menu->getMenuItem(menu);
+ if (!topLevel || !topLevel->submenu || item < 0 ||
+ (uint)item >= topLevel->submenu->items.size())
+ return nullptr;
+ return topLevel->submenu->items[item];
+}
+
+Graphics::MacMenuItem *MacNebularMenu::getSubMenuItem(int menu,
+ int parentItem, int item) const {
+ Graphics::MacMenuItem *parent = getMenuItem(menu, parentItem);
+ if (!parent || !parent->submenu || item < 0 ||
+ (uint)item >= parent->submenu->items.size())
+ return nullptr;
+ return parent->submenu->items[item];
+}
+
+void MacNebularMenu::setItemState(Graphics::MacMenuItem *item,
+ bool enabled, bool checked) {
+ if (!item)
+ return;
+ if (item->enabled != enabled)
+ _menu->setEnabled(item, enabled);
+ if (item->checked != checked)
+ _menu->setCheckMark(item, checked);
+}
+
+void MacNebularMenu::updateState() {
+ // Start with every direct action disabled. This preserves separators and
+ // keeps native actions without a safe ScummVM equivalent visible.
for (int menuIndex = 0; menuIndex < _menu->numberOfMenus(); ++menuIndex) {
Graphics::MacMenuItem *topLevel = _menu->getMenuItem(menuIndex);
for (int itemIndex = 0;
itemIndex < _menu->numberOfMenuItems(topLevel); ++itemIndex)
- _menu->setEnabled(_menu->getSubMenuItem(topLevel, itemIndex), false);
+ setItemState(_menu->getSubMenuItem(topLevel, itemIndex), false, false);
+ }
+
+ setItemState(getMenuItem(kFileMenu, 2),
+ _engine.canLoadGameStateCurrently(nullptr), false);
+ const bool canSave = _engine.canSaveGameStateCurrently(nullptr);
+ setItemState(getMenuItem(kFileMenu, 4), canSave, false);
+ setItemState(getMenuItem(kFileMenu, 5), canSave, false);
+ setItemState(getMenuItem(kFileMenu, 9), true, false);
+
+ const bool noSound = !_engine._musicFlag && !_engine._soundFlag;
+ setItemState(getMenuItem(kOptionsMenu, 0), true, noSound);
+ setItemState(getMenuItem(kOptionsMenu, 1), true, inter_report_hotspots);
+ setItemState(getMenuItem(kOptionsMenu, 2), true, false);
+ setItemState(getMenuItem(kOptionsMenu, 3), true, false);
+
+ for (int fade = SCREEN_FADE_SMOOTH; fade <= SCREEN_FADE_FAST; ++fade)
+ setItemState(getSubMenuItem(kOptionsMenu, 2, fade), true,
+ kernel_screen_fade == fade);
+
+ setItemState(getSubMenuItem(kOptionsMenu, 3, 0),
+ config_file.naughtiness != NAUGHTY,
+ config_file.naughtiness == NAUGHTY);
+ setItemState(getSubMenuItem(kOptionsMenu, 3, 1),
+ config_file.naughtiness != NICE,
+ config_file.naughtiness == NICE);
+ setItemState(getSubMenuItem(kOptionsMenu, 3, 2), false,
+ config_file.naughtiness != NAUGHTY && config_file.naughtiness != NICE);
+
+ setItemState(getMenuItem(kWindowMenu, 0), false, false);
+ setItemState(getMenuItem(kWindowMenu, 1), false, false);
+ setItemState(getMenuItem(kWindowMenu, 2), true, true);
+}
+
+void MacNebularMenu::dispatchCommand(int command) {
+ switch (command) {
+ case kFileOpen:
+ if (_engine.canLoadGameStateCurrently(nullptr))
+ _engine.loadGameDialog();
+ break;
+ case kFileSave:
+ case kFileSaveAs:
+ if (_engine.canSaveGameStateCurrently(nullptr))
+ _engine.saveGameDialog();
+ break;
+ case kFileQuit:
+ _engine.quitGame();
+ break;
+ case kOptionNoSound: {
+ const bool enableSound = !_engine._musicFlag && !_engine._soundFlag;
+ _engine._musicFlag = enableSound;
+ _engine._soundFlag = enableSound;
+ config_file.music_flag = enableSound;
+ config_file.sound_flag = enableSound;
+ if (!enableSound && _engine._soundManager)
+ _engine._soundManager->stop();
+ ConfMan.setBool("music_mute", !enableSound);
+ ConfMan.setBool("sfx_mute", !enableSound);
+ ConfMan.flushToDisk();
+ break;
+ }
+ case kOptionEasyInterface:
+ inter_report_hotspots = !inter_report_hotspots;
+ config_file.interface_hotspots = inter_report_hotspots ?
+ INTERFACE_BRAINDEAD : INTERFACE_MACINTOSH;
+ ConfMan.setBool("interface_hotspots", inter_report_hotspots);
+ ConfMan.flushToDisk();
+ break;
+ case kFadeSmooth:
+ case kFadeMedium:
+ case kFadeFast:
+ kernel_screen_fade = command - kFadeSmooth;
+ config_file.screen_fade = kernel_screen_fade;
+ ConfMan.setInt("screen_fade", kernel_screen_fade);
+ ConfMan.flushToDisk();
+ break;
+ case kStoryNaughty:
+ case kStoryNice:
+ config_file.naughtiness = command == kStoryNaughty ? NAUGHTY : NICE;
+ ConfMan.setBool("naughtiness", config_file.naughtiness == NAUGHTY);
+ ConfMan.flushToDisk();
+ break;
+ default:
+ break;
}
+
+ updateState();
}
void MacNebularMenu::syncPalette() {
@@ -186,7 +324,17 @@ void MacNebularMenu::syncPalette() {
}
bool MacNebularMenu::processEvent(Common::Event &event) {
- return _windowManager && _windowManager->processEvent(event);
+ if (!_windowManager)
+ return false;
+
+ updateState();
+ const bool handled = _windowManager->processEvent(event);
+ if (_pendingCommand != -1) {
+ const int command = _pendingCommand;
+ _pendingCommand = -1;
+ dispatchCommand(command);
+ }
+ return handled;
}
void MacNebularMenu::draw() {
@@ -194,11 +342,13 @@ void MacNebularMenu::draw() {
return;
syncPalette();
+ updateState();
_menu->draw(&_screen, true);
}
-void MacNebularMenu::menuCallback(int, Common::String &, void *data) {
+void MacNebularMenu::menuCallback(int command, Common::String &, void *data) {
MacNebularMenu *menus = (MacNebularMenu *)data;
+ menus->_pendingCommand = command;
menus->_menu->closeMenu();
}
diff --git a/engines/mads/nebular/mac_menus.h b/engines/mads/nebular/mac_menus.h
index 26d4ebe4ad7..ed52e7b9e38 100644
--- a/engines/mads/nebular/mac_menus.h
+++ b/engines/mads/nebular/mac_menus.h
@@ -31,6 +31,7 @@ class String;
namespace Graphics {
class MacMenu;
+struct MacMenuItem;
class MacMenuSubMenu;
class MacWindowManager;
class ManagedSurface;
@@ -51,11 +52,16 @@ private:
Graphics::MacMenu *_menu = nullptr;
byte _palette[256 * 3] = {};
bool _paletteValid = false;
+ int _pendingCommand = -1;
static void menuCallback(int command, Common::String &name, void *data);
bool loadMenuResource(uint16 resourceID,
Graphics::MacMenuSubMenu *parent = nullptr, int parentItem = -1);
- void disableActions();
+ Graphics::MacMenuItem *getMenuItem(int menu, int item) const;
+ Graphics::MacMenuItem *getSubMenuItem(int menu, int parentItem, int item) const;
+ void setItemState(Graphics::MacMenuItem *item, bool enabled, bool checked);
+ void updateState();
+ void dispatchCommand(int command);
void syncPalette();
public:
More information about the Scummvm-git-logs
mailing list