[Scummvm-git-logs] scummvm master -> 2e00256424edea691a3831c07ed822c296649428

mduggan noreply at scummvm.org
Thu Jun 18 09:25:57 UTC 2026


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

Summary:
2e00256424 ACCESS: Support multiple languages in Noct rerelease


Commit: 2e00256424edea691a3831c07ed822c296649428
    https://github.com/scummvm/scummvm/commit/2e00256424edea691a3831c07ed822c296649428
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2026-06-18T18:57:23+10:00

Commit Message:
ACCESS: Support multiple languages in Noct rerelease

Changed paths:
    engines/access/access.cpp
    engines/access/access.h
    engines/access/detection.cpp
    engines/access/detection_tables.h
    engines/access/metaengine.cpp
    engines/access/noctropolis/noctropolis_comicviewer.cpp
    engines/access/noctropolis/noctropolis_game.cpp
    engines/access/noctropolis/noctropolis_game.h
    engines/access/noctropolis/noctropolis_inventory.cpp
    engines/access/noctropolis/noctropolis_resources.cpp
    engines/access/noctropolis/noctropolis_resources.h
    engines/access/scripts.cpp


diff --git a/engines/access/access.cpp b/engines/access/access.cpp
index 9fb4964b70a..04c92b2ddcd 100644
--- a/engines/access/access.cpp
+++ b/engines/access/access.cpp
@@ -122,6 +122,8 @@ AccessEngine::AccessEngine(OSystem *syst, const AccessGameDescription *gameDesc)
 	_stilScaleOff = 0;
 
 	ARRAYCLEAR(_countTbl);
+
+	_lang = Common::UNK_LANG;
 }
 
 AccessEngine::~AccessEngine() {
@@ -212,8 +214,7 @@ void AccessEngine::initialize() {
 
 const SpriteResource *AccessEngine::getIcons() {
 	if (!_icons) {
-		const char *fname = (getGameID() == kGameNoctropolis) ? "ICONS.AP" : "ICONS.LZ";
-		Resource *iconData = _files->loadRawFile(fname);
+		Resource *iconData = _files->loadRawFile(getIconPath());
 		_icons = new SpriteResource(this, iconData);
 		delete iconData;
 	}
@@ -221,6 +222,11 @@ const SpriteResource *AccessEngine::getIcons() {
 }
 
 Common::Error AccessEngine::run() {
+	if (ConfMan.hasKey("language"))
+		_lang = Common::parseLanguage(ConfMan.get("language"));
+	else
+		_lang = getLanguage();
+
 	_res = Resources::init(this);
 	Common::U32String errorMessage;
 	if (!_res->load(errorMessage)) {
diff --git a/engines/access/access.h b/engines/access/access.h
index 0e2698252fc..6044f115658 100644
--- a/engines/access/access.h
+++ b/engines/access/access.h
@@ -147,6 +147,11 @@ private:
 	 */
 	SpriteResource *_icons;
 
+	/**
+	 * Lang to use for resources.
+	 */
+	Common::Language _lang;
+
 	/**
 	 * Handles basic initialization
 	 */
@@ -201,6 +206,11 @@ protected:
 	 */
 	virtual void setupGame() = 0;
 
+	/**
+	 * Get the path to the icons file
+	 */
+	virtual Common::Path getIconPath() const { return Common::Path("ICONS.LZ"); }
+
 public:
 	AnimationManager *_animation;
 	BubbleBox *_bubbleBox;
diff --git a/engines/access/detection.cpp b/engines/access/detection.cpp
index 9de6d795476..b73c1e2c5d8 100644
--- a/engines/access/detection.cpp
+++ b/engines/access/detection.cpp
@@ -65,6 +65,30 @@ public:
 	const DebugChannelDef *getDebugChannels() const override {
 		return debugFlagList;
 	}
+
+	DetectedGame toDetectedGame(const ADDetectedGame &adGame, ADDetectedGameExtraInfo *extraInfo) const override {
+		static const Common::Language NOCTROPOLIS_RERELEASE_LANGS[] = {
+			Common::EN_ANY,
+			Common::FR_FRA,
+			Common::DE_DEU,
+			Common::ES_ESP,
+		};
+		DetectedGame game = AdvancedMetaEngineDetection::toDetectedGame(adGame);
+
+		const Access::AccessGameDescription *desc = reinterpret_cast<const Access::AccessGameDescription *>(adGame.desc);
+		assert(desc);
+
+		if (desc && (desc->features & Access::FEATURE_NOCT_MULTI_LANG)) {
+			// The AdvancedDetector model only allows specifying a single supported
+			// game language. The Noctropolis re-release supports multiple languages.
+			for (const auto lang: NOCTROPOLIS_RERELEASE_LANGS) {
+				game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(lang));
+			}
+		}
+
+		return game;
+	}
+
 };
 
 
diff --git a/engines/access/detection_tables.h b/engines/access/detection_tables.h
index cf9398ab7a1..b9513ac7d0f 100644
--- a/engines/access/detection_tables.h
+++ b/engines/access/detection_tables.h
@@ -21,6 +21,8 @@
 
 namespace Access {
 
+static const uint FEATURE_NOCT_MULTI_LANG = 1;
+
 static const AccessGameDescription gameDescriptions[] = {
 	{
 		// Amazon Guardians of Eden - Floppy English
@@ -163,7 +165,7 @@ static const AccessGameDescription gameDescriptions[] = {
 	},
 
 	{
-		// Noctropolis - sum of this file is the same in all known verisons
+		// Noctropolis - original release
 		{
 			"noctropolis",
 			nullptr,
@@ -179,13 +181,13 @@ static const AccessGameDescription gameDescriptions[] = {
 	},
 
 	{
-		// Noctropolis - GOG/Steam version
+		// Noctropolis - original release, German
 		{
 			"noctropolis",
-			"Rerelease",
+			nullptr,
 			AD_ENTRY2s("dark/scene01.ap", "3a154bf58e10cd7ace14cab1bf5adf4a", 147954,
-					   "dark/scene13.ap", "0d400713ed30e692d63af4d28ba42c5e", 327980),
-			Common::EN_ANY,
+					   "dark/scene13.ap", "261c65ea702c6088ab3ed6e39e217d64", 328430),
+			Common::DE_DEU,
 			Common::kPlatformWindows,
 			ADGF_TESTING,
 			GUIO1(GUIO_NONE)
@@ -194,6 +196,22 @@ static const AccessGameDescription gameDescriptions[] = {
 		0
 	},
 
+	{
+		// Noctropolis - GOG/Steam version (multi-language support)
+		{
+			"noctropolis",
+			"Rerelease",
+			AD_ENTRY2s("dark/scene01.ap", "3a154bf58e10cd7ace14cab1bf5adf4a", 147954,
+					   "dark/scene13.ap", "0d400713ed30e692d63af4d28ba42c5e", 327980),
+			Common::UNK_LANG,
+			Common::kPlatformWindows,
+			ADGF_TESTING,
+			GUIO1(GUIO_GAMEOPTIONS1)
+		},
+		kGameNoctropolis,
+		FEATURE_NOCT_MULTI_LANG
+	},
+
 	{ AD_TABLE_END_MARKER, 0, 0 }
 };
 
diff --git a/engines/access/metaengine.cpp b/engines/access/metaengine.cpp
index 4da9f8dedb9..309663aa331 100644
--- a/engines/access/metaengine.cpp
+++ b/engines/access/metaengine.cpp
@@ -41,6 +41,7 @@
 #include "common/translation.h"
 
 #define MAX_SAVES 99
+#define GAMEOPTION_OGG_MUSIC GUIO_GAMEOPTIONS1
 
 namespace Access {
 
@@ -65,7 +66,7 @@ bool AccessEngine::isDemo() const {
 }
 
 Common::Language AccessEngine::getLanguage() const {
-	return _gameDescription->desc.language;
+	return _lang;
 }
 
 Common::Platform AccessEngine::getPlatform() const {
@@ -74,6 +75,22 @@ Common::Platform AccessEngine::getPlatform() const {
 
 } // End of namespace Access
 
+static const ADExtraGuiOptionsMap optionsList[] = {
+	{
+		GAMEOPTION_OGG_MUSIC,
+		{
+			_s("Use 'high definition' OGG music"),
+			_s("Use the OGG audio from the re-release instead of original MIDI tracks"),
+			"ogg_music",
+			true,
+			0,
+			0
+		}
+	},
+	AD_EXTRA_GUI_OPTIONS_TERMINATOR
+};
+
+
 class AccessMetaEngine : public AdvancedMetaEngine<Access::AccessGameDescription> {
 public:
 	const char *getName() const override {
@@ -88,6 +105,11 @@ public:
 	SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override;
 
 	Common::KeymapArray initKeymaps(const char *target) const override;
+
+	const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override {
+		return optionsList;
+	}
+
 };
 
 bool AccessMetaEngine::hasFeature(MetaEngineFeature f) const {
@@ -201,7 +223,6 @@ SaveStateDescriptor AccessMetaEngine::querySaveMetaInfos(const char *target, int
 	return SaveStateDescriptor();
 }
 
-
 Common::KeymapArray AccessMetaEngine::initKeymaps(const char *target) const {
 	using namespace Common;
 	using namespace Access;
diff --git a/engines/access/noctropolis/noctropolis_comicviewer.cpp b/engines/access/noctropolis/noctropolis_comicviewer.cpp
index 30bc311df63..757c5631760 100644
--- a/engines/access/noctropolis/noctropolis_comicviewer.cpp
+++ b/engines/access/noctropolis/noctropolis_comicviewer.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "access/noctropolis/noctropolis_comicviewer.h"
+#include "access/noctropolis/noctropolis_resources.h"
 
 namespace Access {
 
@@ -72,7 +73,7 @@ void ComicViewer::run(const ComicResource *comic) {
 PageResult ComicViewer::runPage(const ComicPage *page) {
 	PageResult result = kPageResultNone;
 
-	Common::Path pagePath(page->filename);
+	Common::Path pagePath = ((NoctropolisResources *)_vm->_res)->translatePath(page->filename);
 
 	if (!_vm->_files->existFile(pagePath)) {
 		// Happens in Demo 2 - no comic files
@@ -82,7 +83,8 @@ PageResult ComicViewer::runPage(const ComicPage *page) {
 
 	_vm->_files->loadScreen(pagePath);
 
-	Resource *bubbleData = _vm->_files->loadRawFile("COMDATA/comic.ap");
+	Common::Path bubbleDataPath = ((NoctropolisResources *)_vm->_res)->translatePath("DARK/COMDATA/comic.ap");
+	Resource *bubbleData = _vm->_files->loadRawFile(bubbleDataPath);
 	_bubbleSprites = new SpriteResource(_vm, bubbleData);
 	delete bubbleData;
 
diff --git a/engines/access/noctropolis/noctropolis_game.cpp b/engines/access/noctropolis/noctropolis_game.cpp
index 8e040bad117..eecb61287b8 100644
--- a/engines/access/noctropolis/noctropolis_game.cpp
+++ b/engines/access/noctropolis/noctropolis_game.cpp
@@ -72,6 +72,11 @@ void NoctropolisEngine::initObjects() {
 	}
 }
 
+Common::Path NoctropolisEngine::getIconPath() const {
+	return ((NoctropolisResources *)_res)->translatePath("DARK/ICONS.AP");
+}
+
+
 void NoctropolisEngine::setupGame() {
 	_timers.clear();
 	for (int i = 0; i < 32; ++i) {
diff --git a/engines/access/noctropolis/noctropolis_game.h b/engines/access/noctropolis/noctropolis_game.h
index badb490bc83..8ddf2729e70 100644
--- a/engines/access/noctropolis/noctropolis_game.h
+++ b/engines/access/noctropolis/noctropolis_game.h
@@ -106,6 +106,11 @@ protected:
 	*/
 	Common::Error synchronize(Common::Serializer &s) override;
 
+	/**
+	 * Get the path to the icons file
+	 */
+	Common::Path getIconPath() const override;
+
 private:
 	void doIntro();
 	void doFlashLogo();
diff --git a/engines/access/noctropolis/noctropolis_inventory.cpp b/engines/access/noctropolis/noctropolis_inventory.cpp
index 98b780cafc6..39a7d155614 100644
--- a/engines/access/noctropolis/noctropolis_inventory.cpp
+++ b/engines/access/noctropolis/noctropolis_inventory.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "access/noctropolis/noctropolis_inventory.h"
+#include "access/noctropolis/noctropolis_resources.h"
 #include "access/access.h"
 #include "access/resources.h"
 #include "access/asurface.h"
@@ -60,8 +61,8 @@ int NoctropolisInventory::displayInv() {
 			g_system->warpMouse(warpMouseX, warpMouseY);
 	}
 
-	// TODO: Maybe move/load these globally?
-	Resource *iconData = _vm->_files->loadRawFile("INV.AP");
+	Common::Path path = ((NoctropolisResources *)_vm->_res)->translatePath("DARK/INV.AP");
+	Resource *iconData = _vm->_files->loadRawFile(path);
 	SpriteResource *inventorySprites = new SpriteResource(_vm, iconData);
 	delete iconData;
 
diff --git a/engines/access/noctropolis/noctropolis_resources.cpp b/engines/access/noctropolis/noctropolis_resources.cpp
index 43739415fd3..c3a1c708af0 100644
--- a/engines/access/noctropolis/noctropolis_resources.cpp
+++ b/engines/access/noctropolis/noctropolis_resources.cpp
@@ -1866,19 +1866,15 @@ static int _langOffset(Common::Language lang) {
 
 void NoctropolisResources::load(Common::SeekableReadStream &s) {
 	// Note: *don't* call the base class here. Noctropolis doesn't have data in access.dat.
-
-	// TODO: For non-EN variants we want to use something other than DARK/ as the path.
 	for (int i = 0; i < ARRAYSIZE(NOCT_FILES_1); i++) {
-		Common::Path filename = Common::Path(NOCT_FILES_1[i]).getLastComponent();
-		FILENAMES.push_back(filename);
+		FILENAMES.push_back(translatePath(NOCT_FILES_1[i]));
 	}
 	// TODO: These last few files are maybe only ever used from hard-coded points,
 	// so maybe we can just hard-code the names in those places and avoid this hack?
 	while (FILENAMES.size() < 256 - ARRAYSIZE(NOCT_FILES_2))
 		FILENAMES.push_back(Common::Path());
 	for (int i = 0; i < ARRAYSIZE(NOCT_FILES_2); i++) {
-		Common::Path filename = Common::Path(NOCT_FILES_2[i]).getLastComponent();
-		FILENAMES.push_back(filename);
+		FILENAMES.push_back(translatePath(NOCT_FILES_2[i]));
 	}
 
 	_fontChaleteu = new NoctropolisFont(0x66, 11, 1, 0xe2, CHALETEU_OFFSETS, CHALETEU_DATA);
@@ -2022,6 +2018,26 @@ void NoctropolisResources::load(Common::SeekableReadStream &s) {
 	}
 }
 
+Common::Path NoctropolisResources::translatePath(const char *rawPath) const {
+	Common::String path_prefix;
+	switch (_vm->getLanguage()) {
+	case Common::FR_FRA: path_prefix = "FR/"; break;
+	case Common::DE_DEU: path_prefix = "DE/"; break;
+	case Common::ES_ESP: path_prefix = "ES/"; break;
+	default: break;
+	}
+
+	Common::String path = path_prefix + rawPath;
+	if (_vm->isDemo())
+		path.replace(0, 4, "DEMO");  // replace DARK with DEMO.
+
+	if (!path_prefix.empty() && !SearchMan.hasFile(Common::Path(path)))
+		path = path.substr(path_prefix.size());
+
+	return Common::Path(path);
+}
+
+
 int NoctropolisResources::menuAt(int16 x, int16 y) const {
 	for (int i = 0; i < (int)_menus.size(); i++) {
 		if (Polygon::pointInside(_menus[i], x, y))
diff --git a/engines/access/noctropolis/noctropolis_resources.h b/engines/access/noctropolis/noctropolis_resources.h
index d496ab309a9..d3c2e3c6e72 100644
--- a/engines/access/noctropolis/noctropolis_resources.h
+++ b/engines/access/noctropolis/noctropolis_resources.h
@@ -74,6 +74,8 @@ public:
 	const ComicResource *getLastComicResource() const;
 	const ComicResource *getSpecialComicResource() const;
 
+	Common::Path translatePath(const char *rawPath) const;
+
 	int menuAt(int16 x, int16 y) const;
 
 private:
diff --git a/engines/access/scripts.cpp b/engines/access/scripts.cpp
index 1d9ed34f795..ceb4f757ef3 100644
--- a/engines/access/scripts.cpp
+++ b/engines/access/scripts.cpp
@@ -1532,7 +1532,8 @@ void Scripts::cmdDispAbout_v3() {
 	int selectedItem = -2;
 	bool needRedraw = true;
 
-	Resource *spriteData = _vm->_files->loadRawFile("ASK.AP");
+	Common::Path path = ((Noctropolis::NoctropolisResources *)_vm->_res)->translatePath("DARK/ASK.AP");
+	Resource *spriteData = _vm->_files->loadRawFile(path);
 	SpriteResource *askSprites = new SpriteResource(_vm, spriteData);
 	delete spriteData;
 




More information about the Scummvm-git-logs mailing list