[Scummvm-git-logs] scummvm master -> 5fafc5143ec2a6726546b5293cc6c2ef0efe5036

neuromancer noreply at scummvm.org
Wed Apr 1 17:29:56 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:
5fafc5143e FREESCAPE: refactored music coe and extended optional music for eclipse dos


Commit: 5fafc5143ec2a6726546b5293cc6c2ef0efe5036
    https://github.com/scummvm/scummvm/commit/5fafc5143ec2a6726546b5293cc6c2ef0efe5036
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-04-01T19:29:36+02:00

Commit Message:
FREESCAPE: refactored music coe and extended optional music for eclipse dos

Changed paths:
  A engines/freescape/games/eclipse/eclipse.musicdata.h
  A engines/freescape/games/eclipse/opl.music.cpp
  A engines/freescape/games/eclipse/opl.music.h
    engines/freescape/detection.cpp
    engines/freescape/detection.h
    engines/freescape/games/eclipse/ay.music.cpp
    engines/freescape/games/eclipse/dos.cpp
    engines/freescape/games/eclipse/eclipse.cpp
    engines/freescape/games/eclipse/eclipse.h
    engines/freescape/metaengine.cpp
    engines/freescape/module.mk


diff --git a/engines/freescape/detection.cpp b/engines/freescape/detection.cpp
index c76c21d76b7..e7ba5d6056b 100644
--- a/engines/freescape/detection.cpp
+++ b/engines/freescape/detection.cpp
@@ -701,7 +701,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::EN_ANY,
 		Common::kPlatformDOS,
 		ADGF_NO_FLAGS,
-		GUIO5(GUIO_NOMIDI, GUIO_RENDEREGA, GUIO_RENDERCGA, GAMEOPTION_MODERN_MOVEMENT, GAMEOPTION_WASD_CONTROLS)
+		GUIO6(GUIO_NOMIDI, GUIO_RENDEREGA, GUIO_RENDERCGA, GAMEOPTION_MODERN_MOVEMENT, GAMEOPTION_WASD_CONTROLS, GAMEOPTION_OPL_MUSIC)
 	},
 	{
 		// Erbe Software release
@@ -718,7 +718,7 @@ static const ADGameDescription gameDescriptions[] = {
 		Common::EN_ANY,
 		Common::kPlatformDOS,
 		ADGF_NO_FLAGS,
-		GUIO5(GUIO_NOMIDI, GUIO_RENDEREGA, GUIO_RENDERCGA, GAMEOPTION_MODERN_MOVEMENT, GAMEOPTION_WASD_CONTROLS)
+		GUIO6(GUIO_NOMIDI, GUIO_RENDEREGA, GUIO_RENDERCGA, GAMEOPTION_MODERN_MOVEMENT, GAMEOPTION_WASD_CONTROLS, GAMEOPTION_OPL_MUSIC)
 	},
 	{
 		"totaleclipse", // Tape relese
diff --git a/engines/freescape/detection.h b/engines/freescape/detection.h
index 6aa9a32d617..97d35ddf3be 100644
--- a/engines/freescape/detection.h
+++ b/engines/freescape/detection.h
@@ -40,6 +40,7 @@
 #define GAMEOPTION_TRAVEL_ROCK   GUIO_GAMEOPTIONS9
 #define GAMEOPTION_WASD_CONTROLS GUIO_GAMEOPTIONS11
 #define GAMEOPTION_AY_MUSIC      GUIO_GAMEOPTIONS12
+#define GAMEOPTION_OPL_MUSIC     GUIO_GAMEOPTIONS13
 
 
 #endif
diff --git a/engines/freescape/games/eclipse/ay.music.cpp b/engines/freescape/games/eclipse/ay.music.cpp
index 1a058b3b221..56f7c7e9946 100644
--- a/engines/freescape/games/eclipse/ay.music.cpp
+++ b/engines/freescape/games/eclipse/ay.music.cpp
@@ -23,6 +23,9 @@
 
 #include "common/textconsole.h"
 #include "freescape/wb.h"
+#include "freescape/games/eclipse/eclipse.musicdata.h"
+
+using namespace Freescape::EclipseMusicData;
 
 namespace Freescape {
 
@@ -47,106 +50,13 @@ const uint16 kAYPeriods[] = {
 	   24,    23,    21,    20,    19,    18,    17
 };
 
-// 12 instruments, 6 bytes each (SID-only fields PW and PWM stripped)
-// Format: ctrl, AD, SR, vibrato, autoArp, flags
-static const byte kInstruments[] = {
-	0x40, 0x00, 0x00, 0x00, 0x00, 0x00,  // 0
-	0x41, 0x42, 0x24, 0x23, 0x00, 0x04,  // 1
-	0x11, 0x8A, 0xAC, 0x64, 0x00, 0x00,  // 2
-	0x11, 0x6C, 0x4F, 0x63, 0x00, 0x04,  // 3
-	0x41, 0x3A, 0xAC, 0x00, 0x00, 0x04,  // 4
-	0x81, 0x42, 0x00, 0x00, 0x00, 0x00,  // 5
-	0x11, 0x3D, 0x1C, 0x35, 0x00, 0x00,  // 6
-	0x41, 0x4C, 0x2C, 0x45, 0x00, 0x00,  // 7
-	0x11, 0x5D, 0xBC, 0x65, 0x00, 0x04,  // 8
-	0x41, 0x4C, 0xAF, 0x00, 0x00, 0x04,  // 9
-	0x21, 0x4A, 0x2A, 0x64, 0x00, 0x04,  // 10
-	0x41, 0x6A, 0x6B, 0x00, 0x80, 0x04,  // 11
-};
-static const byte kInstrumentSize = 6;
-static const byte kInstrumentCount = 12;
-
-const byte kOrderList0[] = {
-	0xE0, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x0B, 0x0B, 0x0B, 0x0B, 0x01, 0x01,
-	0x0B, 0x10, 0x0B, 0x0B, 0x01, 0x10, 0x01, 0x01, 0x13, 0x13, 0x13, 0x13, 0x0B, 0x0B, 0x13, 0x13,
-	0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x0B, 0x0B, 0x0B, 0x0B, 0x01, 0x01, 0x01, 0x01, 0x0B, 0x10,
-	0x0B, 0x0B, 0x01, 0x13, 0x01, 0x01, 0x13, 0x13, 0x13, 0x13, 0xFF
-};
-
-const byte kOrderList1[] = {
-	0xE0, 0x00, 0x00, 0x00, 0x00, 0x06, 0x07, 0x07, 0x07, 0x07, 0x09, 0x09, 0x07, 0x07, 0xE0, 0x08,
-	0x08, 0x08, 0x08, 0x18, 0x0A, 0x0A, 0x18, 0x0C, 0x0D, 0x0C, 0x0D, 0x14, 0x14, 0x14, 0x14, 0x16,
-	0x17, 0x0C, 0x0D, 0xD4, 0x0C, 0x0D, 0xE0, 0x05, 0x05, 0x05, 0x05, 0x17, 0x0C, 0x0D, 0xD4, 0x0C,
-	0x0D, 0xEC, 0x08, 0x08, 0x08, 0x08, 0xFF
-};
+// Instruments, order lists, pattern data, and arpeggio intervals
+// are in eclipse.musicdata.h (shared with the OPL player).
 
-const byte kOrderList2[] = {
-	0xE0, 0x05, 0x05, 0x05, 0x05, 0xEC, 0x08, 0xE0, 0x02, 0x02, 0x03, 0x02, 0x04, 0xEC, 0x04, 0xE0,
-	0x0E, 0x0F, 0x11, 0x12, 0xEC, 0x02, 0x02, 0x03, 0x02, 0xE0, 0x15, 0x1B, 0x19, 0x1B, 0x1A, 0x1C,
-	0x19, 0x1C, 0x1A, 0x1D, 0x1E, 0x07, 0x00, 0x07, 0x00, 0x04, 0xEC, 0x04, 0xE0, 0x0E, 0x0F, 0x11,
-	0x12, 0xEC, 0x02, 0x02, 0x03, 0x02, 0xFF
-};
+// ============================================================================
 
-// Pattern offset table (31 patterns into kPatternData)
-const uint16 kPatternOffsets[] = {
-	    0,     4,    40,    65,    94,   122,   138,   148,
-	  173,   178,   192,   200,   235,   249,   263,   295,
-	  334,   369,   406,   450,   485,   490,   526,   562,
-	  564,   566,   618,   670,   672,   674,   718
-};
 
-const byte kPatternData[] = {
-	0xC0, 0xBF, 0x00, 0xFF, 0xF3, 0xC1, 0x83, 0x1A, 0x21, 0x21, 0x1A, 0x1A, 0x21, 0x1A, 0x21, 0x1A,
-	0x21, 0x21, 0x1A, 0x1A, 0x21, 0x1A, 0x21, 0x1A, 0x21, 0x21, 0x1A, 0x1A, 0x21, 0x1A, 0x21, 0x1A,
-	0x21, 0x21, 0x1A, 0x1A, 0x21, 0x1A, 0x21, 0xFF, 0xC2, 0x97, 0x30, 0x87, 0x2D, 0x97, 0x30, 0x87,
-	0x2D, 0x30, 0x83, 0x2E, 0x30, 0x80, 0x2E, 0x30, 0x2E, 0x30, 0x2E, 0x30, 0x2E, 0x30, 0xA7, 0x2D,
-	0xFF, 0xC2, 0x97, 0x30, 0x87, 0x32, 0x33, 0x85, 0x32, 0x30, 0x83, 0x2D, 0x80, 0x2D, 0x2E, 0x2D,
-	0x2E, 0x2D, 0x2E, 0x2D, 0x2E, 0x97, 0x2B, 0x87, 0x2D, 0x97, 0x2B, 0x87, 0x2D, 0xFF, 0xC3, 0x97,
-	0x3E, 0x81, 0x3D, 0x3E, 0x3D, 0x39, 0x97, 0x3C, 0x81, 0x3A, 0x3C, 0x3A, 0x35, 0x80, 0x39, 0x37,
-	0x9D, 0x39, 0x80, 0x39, 0x3A, 0x39, 0x38, 0x9B, 0x39, 0xFF, 0xF3, 0xC4, 0xBF, 0x7D, 0x89, 0x3E,
-	0x7D, 0x8A, 0x3C, 0x7D, 0x94, 0x39, 0x7D, 0x8A, 0x3C, 0xFF, 0xC3, 0xBF, 0x26, 0x28, 0x29, 0xB7,
-	0x28, 0x87, 0x24, 0xFF, 0xC0, 0x87, 0x00, 0xC5, 0x81, 0x3C, 0x3C, 0x3C, 0x3C, 0x87, 0x3C, 0xC0,
-	0x00, 0x00, 0xC5, 0x81, 0x3C, 0x3C, 0x3C, 0x3C, 0x87, 0x3C, 0xC0, 0x00, 0xFF, 0xC3, 0xBF, 0x1A,
-	0x26, 0xFF, 0xC3, 0xB7, 0x32, 0x81, 0x31, 0x32, 0x31, 0x2D, 0xB7, 0x30, 0x83, 0x2D, 0x30, 0xFF,
-	0xBF, 0x7D, 0x89, 0x3E, 0x7D, 0x89, 0x3E, 0xFF, 0xC1, 0x83, 0x1A, 0x21, 0x26, 0x1A, 0x21, 0x26,
-	0x1A, 0x21, 0x1A, 0x21, 0x26, 0x1A, 0x21, 0x26, 0x1A, 0x21, 0x1A, 0x21, 0x26, 0x1A, 0x21, 0x26,
-	0x1A, 0x21, 0x1A, 0x21, 0x26, 0x1A, 0x21, 0x26, 0x1A, 0x21, 0xFF, 0xBF, 0x7D, 0x89, 0x3E, 0x7D,
-	0x8A, 0x3C, 0x7D, 0x89, 0x39, 0x7D, 0x8A, 0x37, 0xFF, 0xBF, 0x7D, 0x89, 0x3E, 0x7D, 0x8A, 0x3F,
-	0x7D, 0x89, 0x3C, 0x7D, 0x89, 0x3E, 0xFF, 0xC6, 0xB7, 0x3E, 0x81, 0x3C, 0x3E, 0x3C, 0x39, 0x8B,
-	0x3C, 0xAB, 0x39, 0x83, 0x3C, 0x3E, 0x81, 0x39, 0x37, 0xA3, 0x39, 0x87, 0x3C, 0x39, 0x83, 0x37,
-	0x39, 0x81, 0x37, 0x39, 0xBB, 0x37, 0xFF, 0xA7, 0x3E, 0x87, 0x3E, 0x85, 0x3F, 0x3E, 0x83, 0x3C,
-	0x81, 0x3F, 0x3E, 0xA3, 0x3C, 0x83, 0x3F, 0x3E, 0x87, 0x3C, 0x83, 0x3E, 0x3C, 0x9B, 0x39, 0x83,
-	0x39, 0x8F, 0x3A, 0x37, 0x97, 0x39, 0x83, 0x3C, 0x81, 0x3E, 0x3F, 0x9F, 0x3E, 0xFF, 0xC1, 0x83,
-	0x1A, 0x21, 0x26, 0x1A, 0x21, 0x26, 0x1A, 0x21, 0x1A, 0x21, 0x26, 0x1A, 0x21, 0x26, 0x1A, 0x21,
-	0x1A, 0x1F, 0x26, 0x1A, 0x1F, 0x26, 0x1A, 0x1F, 0x1A, 0x1F, 0x26, 0x1A, 0x1F, 0x26, 0x1A, 0x1F,
-	0xFF, 0xC7, 0xB1, 0x3E, 0x81, 0x3F, 0x3E, 0x3C, 0x3E, 0x3F, 0x3E, 0x3F, 0x8F, 0x3C, 0xA7, 0x39,
-	0x83, 0x3C, 0x3E, 0x81, 0x37, 0x39, 0x3A, 0xB1, 0x39, 0x81, 0x37, 0x39, 0x37, 0x39, 0x9F, 0x37,
-	0x8F, 0x3E, 0x87, 0x3F, 0x3C, 0xFF, 0xC7, 0x9F, 0x3E, 0x8B, 0x45, 0x83, 0x45, 0x87, 0x46, 0x83,
-	0x45, 0x81, 0x43, 0x45, 0x97, 0x46, 0x81, 0x46, 0x48, 0x46, 0x45, 0x9B, 0x46, 0x81, 0x3F, 0x3E,
-	0x97, 0x3C, 0x87, 0x3E, 0x8F, 0x3F, 0x3C, 0xAF, 0x39, 0xC5, 0x81, 0x3C, 0x3C, 0x3C, 0x3C, 0x87,
-	0x3C, 0xFF, 0xC1, 0x83, 0x1A, 0x1A, 0x26, 0x1A, 0x1A, 0x26, 0x1A, 0x26, 0x1A, 0x1A, 0x26, 0x1A,
-	0x1A, 0x26, 0x1A, 0x26, 0x1A, 0x1A, 0x26, 0x1A, 0x1A, 0x26, 0x1A, 0x26, 0x1A, 0x1A, 0x26, 0x1A,
-	0x1A, 0x26, 0x1A, 0x26, 0xFF, 0xC6, 0xBF, 0x26, 0x32, 0xFF, 0xC8, 0x97, 0x35, 0x87, 0x37, 0x39,
-	0x97, 0x39, 0x81, 0x38, 0x39, 0x38, 0x39, 0xA7, 0x35, 0x8F, 0x32, 0x81, 0x33, 0x35, 0x33, 0x35,
-	0x33, 0x35, 0x33, 0xB1, 0x32, 0x97, 0x30, 0x87, 0x32, 0x97, 0x30, 0x87, 0x32, 0xFF, 0xC8, 0x97,
-	0x32, 0x87, 0x34, 0x35, 0x97, 0x35, 0x81, 0x34, 0x35, 0x34, 0x35, 0xA7, 0x32, 0x8F, 0x2D, 0x81,
-	0x2E, 0x2F, 0x2E, 0x2F, 0x2E, 0x2F, 0x2E, 0xB1, 0x2D, 0x97, 0x2D, 0x87, 0x2D, 0x97, 0x2D, 0x87,
-	0x2D, 0xFF, 0xC9, 0xFF, 0xC4, 0xFF, 0x97, 0x32, 0x87, 0x30, 0x97, 0x32, 0x81, 0x32, 0x34, 0x32,
-	0x34, 0x87, 0x30, 0x9F, 0x2D, 0x83, 0x30, 0x32, 0x34, 0x35, 0x34, 0x35, 0x8B, 0x34, 0x81, 0x32,
-	0x30, 0x97, 0x2D, 0x83, 0x2F, 0x30, 0x32, 0x30, 0x2F, 0x30, 0x97, 0x32, 0x83, 0x2F, 0x30, 0x8F,
-	0x2F, 0xC5, 0x81, 0x3C, 0x3C, 0x3C, 0x3C, 0x87, 0x3C, 0xFF, 0x81, 0x3E, 0x3C, 0x3E, 0x3F, 0xA3,
-	0x3E, 0x83, 0x3E, 0x87, 0x3F, 0x83, 0x3E, 0x3C, 0x81, 0x3F, 0x3E, 0x3F, 0x41, 0x97, 0x3F, 0x83,
-	0x3C, 0x87, 0x3E, 0x8B, 0x3F, 0x87, 0x3E, 0x81, 0x3C, 0x3E, 0xA3, 0x3C, 0x83, 0x3C, 0x3E, 0x3C,
-	0x3A, 0x39, 0x3A, 0xAF, 0x39, 0xC5, 0x81, 0x3C, 0x3C, 0x3C, 0x3C, 0x87, 0x3C, 0xFF, 0xC7, 0xFF,
-	0xCA, 0xFF, 0xCB, 0x8B, 0x3E, 0x83, 0x3C, 0x9B, 0x3E, 0x83, 0x3C, 0x87, 0x3E, 0x3F, 0x8F, 0x3C,
-	0x97, 0x39, 0x83, 0x3C, 0x3E, 0x87, 0x3C, 0x83, 0x3A, 0x39, 0x8F, 0x39, 0x9F, 0x3E, 0x87, 0x3E,
-	0x40, 0x8F, 0x3C, 0x9F, 0x39, 0xC5, 0x81, 0x3C, 0x3C, 0x3C, 0x3C, 0x87, 0x3C, 0xFF, 0xCB, 0x97,
-	0x39, 0x83, 0x3E, 0x97, 0x40, 0x83, 0x39, 0x3E, 0x41, 0x8B, 0x40, 0x81, 0x3E, 0x3C, 0xA7, 0x39,
-	0x81, 0x3C, 0x3E, 0x3C, 0x3E, 0x89, 0x40, 0x81, 0x41, 0x40, 0x41, 0xAF, 0x3E, 0x8F, 0x3E, 0x3C,
-	0x9F, 0x39, 0xFF
-};
 
-const byte kEmbeddedArpeggioIntervals[] = { 0x03, 0x04, 0x05, 0x07, 0x08, 0x09, 0x0A, 0x0C };
 
 // ============================================================================
 // Software ADSR rate tables (8.8 fixed point, per-frame at 50Hz)
@@ -221,7 +131,7 @@ EclipseAYMusicPlayer::EclipseAYMusicPlayer(Audio::Mixer *mixer)
 	  _speedCounter(0),
 	  _mixerRegister(0x38),
 	  _tickSampleCount(0) {
-	memcpy(_arpeggioIntervals, kEmbeddedArpeggioIntervals, 8);
+	memcpy(_arpeggioIntervals, kArpeggioIntervals, 8);
 }
 
 EclipseAYMusicPlayer::~EclipseAYMusicPlayer() {
diff --git a/engines/freescape/games/eclipse/dos.cpp b/engines/freescape/games/eclipse/dos.cpp
index 76ec01a6b5e..2019f48a3d9 100644
--- a/engines/freescape/games/eclipse/dos.cpp
+++ b/engines/freescape/games/eclipse/dos.cpp
@@ -21,9 +21,11 @@
 
 #include "audio/audiostream.h"
 #include "audio/decoders/raw.h"
+#include "common/config-manager.h"
 
 #include "freescape/freescape.h"
 #include "freescape/games/eclipse/eclipse.h"
+#include "freescape/games/eclipse/opl.music.h"
 #include "freescape/language/8bitDetokeniser.h"
 
 namespace Freescape {
@@ -158,6 +160,9 @@ void EclipseEngine::loadAssetsDOSFullGame() {
 		swapPalette(_startArea);
 	} else
 		error("Invalid or unsupported render mode %s for Total Eclipse", Common::getRenderModeDescription(_renderMode));
+
+	if (ConfMan.getBool("opl_music"))
+		_playerOPLMusic = new EclipseOPLMusicPlayer();
 }
 
 void EclipseEngine::drawDOSUI(Graphics::Surface *surface) {
diff --git a/engines/freescape/games/eclipse/eclipse.cpp b/engines/freescape/games/eclipse/eclipse.cpp
index 4fc28d015b1..77ab804c326 100644
--- a/engines/freescape/games/eclipse/eclipse.cpp
+++ b/engines/freescape/games/eclipse/eclipse.cpp
@@ -33,6 +33,7 @@
 #include "freescape/games/eclipse/c64.music.h"
 #include "freescape/games/eclipse/c64.sfx.h"
 #include "freescape/games/eclipse/ay.music.h"
+#include "freescape/games/eclipse/opl.music.h"
 #include "freescape/games/eclipse/eclipse.h"
 #include "freescape/language/8bitDetokeniser.h"
 
@@ -46,6 +47,7 @@ EclipseEngine::EclipseEngine(OSystem *syst, const ADGameDescription *gd) : Frees
 	_playerC64Music = nullptr;
 	_playerC64Sfx = nullptr;
 	_playerAYMusic = nullptr;
+	_playerOPLMusic = nullptr;
 	_c64UseSFX = false;
 
 	// These sounds can be overriden by the class of each platform
@@ -108,6 +110,7 @@ EclipseEngine::EclipseEngine(OSystem *syst, const ADGameDescription *gd) : Frees
 }
 
 EclipseEngine::~EclipseEngine() {
+	delete _playerOPLMusic;
 	delete _playerAYMusic;
 	delete _playerC64Music;
 	delete _playerC64Sfx;
@@ -134,6 +137,8 @@ void EclipseEngine::initGameState() {
 		_playerC64Music->startMusic();
 	else if ((isCPC() || isSpectrum()) && _playerAYMusic)
 		_playerAYMusic->startMusic();
+	else if (isDOS() && _playerOPLMusic)
+		_playerOPLMusic->startMusic();
 	else
 		playMusic("Total Eclipse Theme");
 }
diff --git a/engines/freescape/games/eclipse/eclipse.h b/engines/freescape/games/eclipse/eclipse.h
index 6de63bda278..b78a709517c 100644
--- a/engines/freescape/games/eclipse/eclipse.h
+++ b/engines/freescape/games/eclipse/eclipse.h
@@ -28,6 +28,7 @@ namespace Freescape {
 class EclipseAYMusicPlayer;
 class EclipseC64MusicPlayer;
 class EclipseC64SFXPlayer;
+class EclipseOPLMusicPlayer;
 
 enum EclipseReleaseFlags {
 	GF_ZX_DEMO_CRASH = (1 << 0),
@@ -123,6 +124,7 @@ public:
 	void toggleC64Sound();
 
 	EclipseAYMusicPlayer *_playerAYMusic;
+	EclipseOPLMusicPlayer *_playerOPLMusic;
 
 	// Atari ST UI sprites (extracted from binary, pre-converted to target format)
 	Font _fontScore; // Font B (10 score digit glyphs, 4-plane at $249BE)
diff --git a/engines/freescape/games/eclipse/eclipse.musicdata.h b/engines/freescape/games/eclipse/eclipse.musicdata.h
new file mode 100644
index 00000000000..466b222f56e
--- /dev/null
+++ b/engines/freescape/games/eclipse/eclipse.musicdata.h
@@ -0,0 +1,140 @@
+/* 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 FREESCAPE_ECLIPSE_MUSICDATA_H
+#define FREESCAPE_ECLIPSE_MUSICDATA_H
+
+/**
+ * Shared music data for Total Eclipse backported music players.
+ *
+ * Song data extracted from the C64 version's Wally Beben music engine.
+ * SID-only fields (pulse width, PWM) have been stripped from instruments.
+ * This header is included by both the AY and OPL player implementations.
+ */
+
+namespace Freescape {
+namespace EclipseMusicData {
+
+// 12 instruments, 6 bytes each (SID-only fields PW and PWM stripped)
+// Format: ctrl, AD, SR, vibrato, autoArp, flags
+static const byte kInstruments[] = {
+	0x40, 0x00, 0x00, 0x00, 0x00, 0x00,  // 0
+	0x41, 0x42, 0x24, 0x23, 0x00, 0x04,  // 1
+	0x11, 0x8A, 0xAC, 0x64, 0x00, 0x00,  // 2
+	0x11, 0x6C, 0x4F, 0x63, 0x00, 0x04,  // 3
+	0x41, 0x3A, 0xAC, 0x00, 0x00, 0x04,  // 4
+	0x81, 0x42, 0x00, 0x00, 0x00, 0x00,  // 5
+	0x11, 0x3D, 0x1C, 0x35, 0x00, 0x00,  // 6
+	0x41, 0x4C, 0x2C, 0x45, 0x00, 0x00,  // 7
+	0x11, 0x5D, 0xBC, 0x65, 0x00, 0x04,  // 8
+	0x41, 0x4C, 0xAF, 0x00, 0x00, 0x04,  // 9
+	0x21, 0x4A, 0x2A, 0x64, 0x00, 0x04,  // 10
+	0x41, 0x6A, 0x6B, 0x00, 0x80, 0x04,  // 11
+};
+static const byte kInstrumentSize = 6;
+static const byte kInstrumentCount = 12;
+
+static const byte kOrderList0[] = {
+	0xE0, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x0B, 0x0B, 0x0B, 0x0B, 0x01, 0x01,
+	0x0B, 0x10, 0x0B, 0x0B, 0x01, 0x10, 0x01, 0x01, 0x13, 0x13, 0x13, 0x13, 0x0B, 0x0B, 0x13, 0x13,
+	0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x0B, 0x0B, 0x0B, 0x0B, 0x01, 0x01, 0x01, 0x01, 0x0B, 0x10,
+	0x0B, 0x0B, 0x01, 0x13, 0x01, 0x01, 0x13, 0x13, 0x13, 0x13, 0xFF
+};
+
+static const byte kOrderList1[] = {
+	0xE0, 0x00, 0x00, 0x00, 0x00, 0x06, 0x07, 0x07, 0x07, 0x07, 0x09, 0x09, 0x07, 0x07, 0xE0, 0x08,
+	0x08, 0x08, 0x08, 0x18, 0x0A, 0x0A, 0x18, 0x0C, 0x0D, 0x0C, 0x0D, 0x14, 0x14, 0x14, 0x14, 0x16,
+	0x17, 0x0C, 0x0D, 0xD4, 0x0C, 0x0D, 0xE0, 0x05, 0x05, 0x05, 0x05, 0x17, 0x0C, 0x0D, 0xD4, 0x0C,
+	0x0D, 0xEC, 0x08, 0x08, 0x08, 0x08, 0xFF
+};
+
+static const byte kOrderList2[] = {
+	0xE0, 0x05, 0x05, 0x05, 0x05, 0xEC, 0x08, 0xE0, 0x02, 0x02, 0x03, 0x02, 0x04, 0xEC, 0x04, 0xE0,
+	0x0E, 0x0F, 0x11, 0x12, 0xEC, 0x02, 0x02, 0x03, 0x02, 0xE0, 0x15, 0x1B, 0x19, 0x1B, 0x1A, 0x1C,
+	0x19, 0x1C, 0x1A, 0x1D, 0x1E, 0x07, 0x00, 0x07, 0x00, 0x04, 0xEC, 0x04, 0xE0, 0x0E, 0x0F, 0x11,
+	0x12, 0xEC, 0x02, 0x02, 0x03, 0x02, 0xFF
+};
+
+// Pattern offset table (31 patterns into kPatternData)
+static const uint16 kPatternOffsets[] = {
+	    0,     4,    40,    65,    94,   122,   138,   148,
+	  173,   178,   192,   200,   235,   249,   263,   295,
+	  334,   369,   406,   450,   485,   490,   526,   562,
+	  564,   566,   618,   670,   672,   674,   718
+};
+
+static const byte kPatternData[] = {
+	0xC0, 0xBF, 0x00, 0xFF, 0xF3, 0xC1, 0x83, 0x1A, 0x21, 0x21, 0x1A, 0x1A, 0x21, 0x1A, 0x21, 0x1A,
+	0x21, 0x21, 0x1A, 0x1A, 0x21, 0x1A, 0x21, 0x1A, 0x21, 0x21, 0x1A, 0x1A, 0x21, 0x1A, 0x21, 0x1A,
+	0x21, 0x21, 0x1A, 0x1A, 0x21, 0x1A, 0x21, 0xFF, 0xC2, 0x97, 0x30, 0x87, 0x2D, 0x97, 0x30, 0x87,
+	0x2D, 0x30, 0x83, 0x2E, 0x30, 0x80, 0x2E, 0x30, 0x2E, 0x30, 0x2E, 0x30, 0x2E, 0x30, 0xA7, 0x2D,
+	0xFF, 0xC2, 0x97, 0x30, 0x87, 0x32, 0x33, 0x85, 0x32, 0x30, 0x83, 0x2D, 0x80, 0x2D, 0x2E, 0x2D,
+	0x2E, 0x2D, 0x2E, 0x2D, 0x2E, 0x97, 0x2B, 0x87, 0x2D, 0x97, 0x2B, 0x87, 0x2D, 0xFF, 0xC3, 0x97,
+	0x3E, 0x81, 0x3D, 0x3E, 0x3D, 0x39, 0x97, 0x3C, 0x81, 0x3A, 0x3C, 0x3A, 0x35, 0x80, 0x39, 0x37,
+	0x9D, 0x39, 0x80, 0x39, 0x3A, 0x39, 0x38, 0x9B, 0x39, 0xFF, 0xF3, 0xC4, 0xBF, 0x7D, 0x89, 0x3E,
+	0x7D, 0x8A, 0x3C, 0x7D, 0x94, 0x39, 0x7D, 0x8A, 0x3C, 0xFF, 0xC3, 0xBF, 0x26, 0x28, 0x29, 0xB7,
+	0x28, 0x87, 0x24, 0xFF, 0xC0, 0x87, 0x00, 0xC5, 0x81, 0x3C, 0x3C, 0x3C, 0x3C, 0x87, 0x3C, 0xC0,
+	0x00, 0x00, 0xC5, 0x81, 0x3C, 0x3C, 0x3C, 0x3C, 0x87, 0x3C, 0xC0, 0x00, 0xFF, 0xC3, 0xBF, 0x1A,
+	0x26, 0xFF, 0xC3, 0xB7, 0x32, 0x81, 0x31, 0x32, 0x31, 0x2D, 0xB7, 0x30, 0x83, 0x2D, 0x30, 0xFF,
+	0xBF, 0x7D, 0x89, 0x3E, 0x7D, 0x89, 0x3E, 0xFF, 0xC1, 0x83, 0x1A, 0x21, 0x26, 0x1A, 0x21, 0x26,
+	0x1A, 0x21, 0x1A, 0x21, 0x26, 0x1A, 0x21, 0x26, 0x1A, 0x21, 0x1A, 0x21, 0x26, 0x1A, 0x21, 0x26,
+	0x1A, 0x21, 0x1A, 0x21, 0x26, 0x1A, 0x21, 0x26, 0x1A, 0x21, 0xFF, 0xBF, 0x7D, 0x89, 0x3E, 0x7D,
+	0x8A, 0x3C, 0x7D, 0x89, 0x39, 0x7D, 0x8A, 0x37, 0xFF, 0xBF, 0x7D, 0x89, 0x3E, 0x7D, 0x8A, 0x3F,
+	0x7D, 0x89, 0x3C, 0x7D, 0x89, 0x3E, 0xFF, 0xC6, 0xB7, 0x3E, 0x81, 0x3C, 0x3E, 0x3C, 0x39, 0x8B,
+	0x3C, 0xAB, 0x39, 0x83, 0x3C, 0x3E, 0x81, 0x39, 0x37, 0xA3, 0x39, 0x87, 0x3C, 0x39, 0x83, 0x37,
+	0x39, 0x81, 0x37, 0x39, 0xBB, 0x37, 0xFF, 0xA7, 0x3E, 0x87, 0x3E, 0x85, 0x3F, 0x3E, 0x83, 0x3C,
+	0x81, 0x3F, 0x3E, 0xA3, 0x3C, 0x83, 0x3F, 0x3E, 0x87, 0x3C, 0x83, 0x3E, 0x3C, 0x9B, 0x39, 0x83,
+	0x39, 0x8F, 0x3A, 0x37, 0x97, 0x39, 0x83, 0x3C, 0x81, 0x3E, 0x3F, 0x9F, 0x3E, 0xFF, 0xC1, 0x83,
+	0x1A, 0x21, 0x26, 0x1A, 0x21, 0x26, 0x1A, 0x21, 0x1A, 0x21, 0x26, 0x1A, 0x21, 0x26, 0x1A, 0x21,
+	0x1A, 0x1F, 0x26, 0x1A, 0x1F, 0x26, 0x1A, 0x1F, 0x1A, 0x1F, 0x26, 0x1A, 0x1F, 0x26, 0x1A, 0x1F,
+	0xFF, 0xC7, 0xB1, 0x3E, 0x81, 0x3F, 0x3E, 0x3C, 0x3E, 0x3F, 0x3E, 0x3F, 0x8F, 0x3C, 0xA7, 0x39,
+	0x83, 0x3C, 0x3E, 0x81, 0x37, 0x39, 0x3A, 0xB1, 0x39, 0x81, 0x37, 0x39, 0x37, 0x39, 0x9F, 0x37,
+	0x8F, 0x3E, 0x87, 0x3F, 0x3C, 0xFF, 0xC7, 0x9F, 0x3E, 0x8B, 0x45, 0x83, 0x45, 0x87, 0x46, 0x83,
+	0x45, 0x81, 0x43, 0x45, 0x97, 0x46, 0x81, 0x46, 0x48, 0x46, 0x45, 0x9B, 0x46, 0x81, 0x3F, 0x3E,
+	0x97, 0x3C, 0x87, 0x3E, 0x8F, 0x3F, 0x3C, 0xAF, 0x39, 0xC5, 0x81, 0x3C, 0x3C, 0x3C, 0x3C, 0x87,
+	0x3C, 0xFF, 0xC1, 0x83, 0x1A, 0x1A, 0x26, 0x1A, 0x1A, 0x26, 0x1A, 0x26, 0x1A, 0x1A, 0x26, 0x1A,
+	0x1A, 0x26, 0x1A, 0x26, 0x1A, 0x1A, 0x26, 0x1A, 0x1A, 0x26, 0x1A, 0x26, 0x1A, 0x1A, 0x26, 0x1A,
+	0x1A, 0x26, 0x1A, 0x26, 0xFF, 0xC6, 0xBF, 0x26, 0x32, 0xFF, 0xC8, 0x97, 0x35, 0x87, 0x37, 0x39,
+	0x97, 0x39, 0x81, 0x38, 0x39, 0x38, 0x39, 0xA7, 0x35, 0x8F, 0x32, 0x81, 0x33, 0x35, 0x33, 0x35,
+	0x33, 0x35, 0x33, 0xB1, 0x32, 0x97, 0x30, 0x87, 0x32, 0x97, 0x30, 0x87, 0x32, 0xFF, 0xC8, 0x97,
+	0x32, 0x87, 0x34, 0x35, 0x97, 0x35, 0x81, 0x34, 0x35, 0x34, 0x35, 0xA7, 0x32, 0x8F, 0x2D, 0x81,
+	0x2E, 0x2F, 0x2E, 0x2F, 0x2E, 0x2F, 0x2E, 0xB1, 0x2D, 0x97, 0x2D, 0x87, 0x2D, 0x97, 0x2D, 0x87,
+	0x2D, 0xFF, 0xC9, 0xFF, 0xC4, 0xFF, 0x97, 0x32, 0x87, 0x30, 0x97, 0x32, 0x81, 0x32, 0x34, 0x32,
+	0x34, 0x87, 0x30, 0x9F, 0x2D, 0x83, 0x30, 0x32, 0x34, 0x35, 0x34, 0x35, 0x8B, 0x34, 0x81, 0x32,
+	0x30, 0x97, 0x2D, 0x83, 0x2F, 0x30, 0x32, 0x30, 0x2F, 0x30, 0x97, 0x32, 0x83, 0x2F, 0x30, 0x8F,
+	0x2F, 0xC5, 0x81, 0x3C, 0x3C, 0x3C, 0x3C, 0x87, 0x3C, 0xFF, 0x81, 0x3E, 0x3C, 0x3E, 0x3F, 0xA3,
+	0x3E, 0x83, 0x3E, 0x87, 0x3F, 0x83, 0x3E, 0x3C, 0x81, 0x3F, 0x3E, 0x3F, 0x41, 0x97, 0x3F, 0x83,
+	0x3C, 0x87, 0x3E, 0x8B, 0x3F, 0x87, 0x3E, 0x81, 0x3C, 0x3E, 0xA3, 0x3C, 0x83, 0x3C, 0x3E, 0x3C,
+	0x3A, 0x39, 0x3A, 0xAF, 0x39, 0xC5, 0x81, 0x3C, 0x3C, 0x3C, 0x3C, 0x87, 0x3C, 0xFF, 0xC7, 0xFF,
+	0xCA, 0xFF, 0xCB, 0x8B, 0x3E, 0x83, 0x3C, 0x9B, 0x3E, 0x83, 0x3C, 0x87, 0x3E, 0x3F, 0x8F, 0x3C,
+	0x97, 0x39, 0x83, 0x3C, 0x3E, 0x87, 0x3C, 0x83, 0x3A, 0x39, 0x8F, 0x39, 0x9F, 0x3E, 0x87, 0x3E,
+	0x40, 0x8F, 0x3C, 0x9F, 0x39, 0xC5, 0x81, 0x3C, 0x3C, 0x3C, 0x3C, 0x87, 0x3C, 0xFF, 0xCB, 0x97,
+	0x39, 0x83, 0x3E, 0x97, 0x40, 0x83, 0x39, 0x3E, 0x41, 0x8B, 0x40, 0x81, 0x3E, 0x3C, 0xA7, 0x39,
+	0x81, 0x3C, 0x3E, 0x3C, 0x3E, 0x89, 0x40, 0x81, 0x41, 0x40, 0x41, 0xAF, 0x3E, 0x8F, 0x3E, 0x3C,
+	0x9F, 0x39, 0xFF
+};
+
+static const byte kArpeggioIntervals[] = { 0x03, 0x04, 0x05, 0x07, 0x08, 0x09, 0x0A, 0x0C };
+
+} // namespace EclipseMusicData
+} // namespace Freescape
+
+#endif
diff --git a/engines/freescape/games/eclipse/opl.music.cpp b/engines/freescape/games/eclipse/opl.music.cpp
new file mode 100644
index 00000000000..bb77368206c
--- /dev/null
+++ b/engines/freescape/games/eclipse/opl.music.cpp
@@ -0,0 +1,723 @@
+/* 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/>.
+ *
+ */
+
+#include "engines/freescape/games/eclipse/opl.music.h"
+
+#include "common/textconsole.h"
+#include "common/util.h"
+#include "freescape/wb.h"
+#include "freescape/games/eclipse/eclipse.musicdata.h"
+
+using namespace Freescape::EclipseMusicData;
+
+namespace Freescape {
+
+// ============================================================================
+// Embedded music data (shared with AY player, extracted from C64)
+// ============================================================================
+
+// OPL2 F-number/block table (95 entries)
+// Format: fnum (bits 0-9) | block (bits 10-12)
+// Derived from SID frequency table
+static const uint16 kOPLFreqs[] = {
+	0x0000, 0x0168, 0x017D, 0x0194, 0x01AD, 0x01C5,
+	0x01E1, 0x01FD, 0x021B, 0x023B, 0x025E, 0x0282,
+	0x02A8, 0x02D0, 0x02FB, 0x0328, 0x0358, 0x038B,
+	0x03C1, 0x03FA, 0x061B, 0x063C, 0x065E, 0x0682,
+	0x06A7, 0x06D0, 0x06FB, 0x0728, 0x0758, 0x078B,
+	0x07C1, 0x07FA, 0x0A1B, 0x0A3B, 0x0A5D, 0x0A81,
+	0x0AA8, 0x0AD0, 0x0AFB, 0x0B2B, 0x0B58, 0x0B8B,
+	0x0BC1, 0x0BFA, 0x0E1B, 0x0E3B, 0x0E5D, 0x0E81,
+	0x0EA8, 0x0ED0, 0x0EFB, 0x0F28, 0x0F58, 0x0F8B,
+	0x0FC1, 0x0FFA, 0x121B, 0x123B, 0x125D, 0x1281,
+	0x12A8, 0x12D0, 0x12FB, 0x1328, 0x1358, 0x138B,
+	0x13C1, 0x13FA, 0x161B, 0x163B, 0x1661, 0x1681,
+	0x16A8, 0x16D0, 0x16FB, 0x1729, 0x1758, 0x178B,
+	0x17C1, 0x17FA, 0x1A1B, 0x1A3B, 0x1A5D, 0x1A81,
+	0x1AA8, 0x1AD0, 0x1AFB, 0x1B28, 0x1B58, 0x1B8B,
+	0x1BC1, 0x1BFA, 0x1E1B, 0x1E3B, 0x1E5D
+};
+
+// Instruments, order lists, pattern data, and arpeggio intervals
+// are in eclipse.musicdata.h (shared with the AY player).
+
+// ============================================================================
+// (pattern data removed - now in eclipse.musicdata.h)
+//
+// ============================================================================
+// OPL2 FM instrument patches (our own creation, not from C64)
+// ============================================================================
+
+// OPL operator register offsets for channels 0-2
+// Each OPL channel has 2 operators (modulator + carrier)
+// Modulator offsets: 0x00, 0x01, 0x02 for channels 0-2
+// Carrier offsets:   0x03, 0x04, 0x05 for channels 0-2
+static const byte kOPLModOffset[] = { 0x00, 0x01, 0x02 };
+static const byte kOPLCarOffset[] = { 0x03, 0x04, 0x05 };
+
+// FM patch definitions, 11 bytes each:
+//  [0] mod: AM/VIB/EG/KSR/MULT  (reg 0x20+mod)
+//  [1] car: AM/VIB/EG/KSR/MULT  (reg 0x20+car)
+//  [2] mod: KSL/output level     (reg 0x40+mod)
+//  [3] car: KSL/output level     (reg 0x40+car)
+//  [4] mod: attack/decay          (reg 0x60+mod)
+//  [5] car: attack/decay          (reg 0x60+car)
+//  [6] mod: sustain/release       (reg 0x80+mod)
+//  [7] car: sustain/release       (reg 0x80+car)
+//  [8] mod: wave select           (reg 0xE0+mod)
+//  [9] car: wave select           (reg 0xE0+car)
+// [10] feedback/connection        (reg 0xC0+ch)
+// Carrier ADSR derived from SID AD/SR bytes using timing-matched conversion.
+// Modulator settings tuned per SID waveform type:
+//   triangle → additive sine (soft, warm)
+//   pulse    → FM with slight harmonic content (brighter)
+//   sawtooth → FM with feedback (rich harmonics)
+//   noise    → inharmonic FM (metallic percussion)
+static const byte kOPLPatches[][11] = {
+	// 0: rest — silent
+	{ 0x00, 0x00, 0x3F, 0x3F, 0xFF, 0xFF, 0x0F, 0x0F, 0x00, 0x00, 0x00 },
+	// 1: bass pulse (AD=0x42 SR=0x24) — punchy FM bass
+	{ 0x02, 0x01, 0x1E, 0x00, 0xF1, 0x8A, 0x07, 0xD9, 0x00, 0x01, 0x06 },
+	// 2: triangle lead (AD=0x8A SR=0xAC) — warm sine lead
+	{ 0x01, 0x01, 0x2A, 0x00, 0xF1, 0x75, 0x07, 0x54, 0x00, 0x00, 0x01 },
+	// 3: triangle pad (AD=0x6C SR=0x4F) — soft sustained pad
+	{ 0x01, 0x01, 0x2A, 0x00, 0xF1, 0x84, 0x07, 0xB1, 0x00, 0x00, 0x01 },
+	// 4: pulse arpeggio (AD=0x3A SR=0xAC) — bright arpeggio
+	{ 0x02, 0x01, 0x1E, 0x00, 0xF1, 0x95, 0x07, 0x54, 0x00, 0x01, 0x06 },
+	// 5: noise percussion (AD=0x42 SR=0x00) — metallic hit
+	{ 0x01, 0x0C, 0x00, 0x00, 0xFA, 0x8A, 0x07, 0xFD, 0x00, 0x00, 0x01 },
+	// 6: triangle melody (AD=0x3D SR=0x1C) — flute-like
+	{ 0x01, 0x01, 0x2A, 0x00, 0xF1, 0x92, 0x07, 0xE4, 0x00, 0x00, 0x01 },
+	// 7: pulse melody (AD=0x4C SR=0x2C) — vibrato lead
+	{ 0x02, 0x01, 0x1E, 0x00, 0xF1, 0x84, 0x07, 0xD4, 0x00, 0x01, 0x06 },
+	// 8: triangle sustain (AD=0x5D SR=0xBC) — organ-like
+	{ 0x01, 0x01, 0x2A, 0x00, 0xF1, 0x82, 0x07, 0x44, 0x00, 0x00, 0x01 },
+	// 9: pulse sustain (AD=0x4C SR=0xAF) — electric piano
+	{ 0x02, 0x01, 0x1E, 0x00, 0xF1, 0x84, 0x07, 0x51, 0x00, 0x01, 0x06 },
+	// 10: sawtooth lead (AD=0x4A SR=0x2A) — brass-like
+	{ 0x21, 0x21, 0x1A, 0x00, 0xF1, 0x85, 0x07, 0xD5, 0x02, 0x00, 0x0E },
+	// 11: pulse w/ arpeggio (AD=0x6A SR=0x6B) — harpsichord-like
+	{ 0x02, 0x01, 0x1E, 0x00, 0xF1, 0x85, 0x07, 0x94, 0x00, 0x01, 0x06 },
+};
+
+// ============================================================================
+// ChannelState
+// ============================================================================
+
+void EclipseOPLMusicPlayer::ChannelState::reset() {
+	orderList = nullptr;
+	orderPos = 0;
+	patternDataOffset = 0;
+	patternOffset = 0;
+	instrumentOffset = 0;
+	currentNote = 0;
+	transpose = 0;
+	durationReload = 0;
+	durationCounter = 0;
+	effectMode = 0;
+	effectParam = 0;
+	arpeggioTarget = 0;
+	arpeggioParam = 0;
+	arpeggioSequencePos = 0;
+	memset(arpeggioSequence, 0, sizeof(arpeggioSequence));
+	arpeggioSequenceLen = 0;
+	noteStepCommand = 0;
+	stepDownCounter = 0;
+	vibratoPhase = 0;
+	vibratoCounter = 0;
+	delayValue = 0;
+	delayCounter = 0;
+	waveform = 0;
+	instrumentFlags = 0;
+	gateOffDisabled = false;
+	keyOn = false;
+}
+
+// ============================================================================
+// Constructor / Destructor
+// ============================================================================
+
+EclipseOPLMusicPlayer::EclipseOPLMusicPlayer()
+	: _opl(nullptr),
+	  _musicActive(false),
+	  _speedDivider(1),
+	  _speedCounter(0) {
+	memcpy(_arpeggioIntervals, kArpeggioIntervals, 8);
+
+	_opl = OPL::Config::create();
+	if (!_opl || !_opl->init()) {
+		warning("EclipseOPLMusicPlayer: Failed to create OPL emulator");
+		delete _opl;
+		_opl = nullptr;
+	}
+}
+
+EclipseOPLMusicPlayer::~EclipseOPLMusicPlayer() {
+	stopMusic();
+	delete _opl;
+}
+
+// ============================================================================
+// Public interface
+// ============================================================================
+
+void EclipseOPLMusicPlayer::startMusic() {
+	if (!_opl)
+		return;
+	_opl->start(new Common::Functor0Mem<void, EclipseOPLMusicPlayer>(
+		this, &EclipseOPLMusicPlayer::onTimer), 50);
+	setupSong();
+}
+
+void EclipseOPLMusicPlayer::stopMusic() {
+	_musicActive = false;
+	if (_opl) {
+		silenceAll();
+		_opl->stop();
+	}
+}
+
+bool EclipseOPLMusicPlayer::isPlaying() const {
+	return _musicActive;
+}
+
+// ============================================================================
+// OPL register helpers
+// ============================================================================
+
+void EclipseOPLMusicPlayer::noteToFnumBlock(byte note, uint16 &fnum, byte &block) const {
+	if (note > kMaxNote)
+		note = kMaxNote;
+	uint16 combined = kOPLFreqs[note];
+	fnum = combined & 0x03FF;
+	block = (combined >> 10) & 0x07;
+}
+
+void EclipseOPLMusicPlayer::setFrequency(int channel, uint16 fnum, byte block) {
+	if (!_opl)
+		return;
+	_opl->writeReg(0xA0 + channel, fnum & 0xFF);
+	// Preserve key-on bit in 0xB0
+	byte b0 = ((fnum >> 8) & 0x03) | (block << 2);
+	if (_channels[channel].keyOn)
+		b0 |= 0x20;
+	_opl->writeReg(0xB0 + channel, b0);
+}
+
+void EclipseOPLMusicPlayer::setOPLInstrument(int channel, byte instrumentOffset) {
+	if (!_opl)
+		return;
+	byte patchIdx = instrumentOffset / kInstrumentSize;
+	if (patchIdx >= ARRAYSIZE(kOPLPatches))
+		patchIdx = 0;
+
+	const byte *patch = kOPLPatches[patchIdx];
+	byte mod = kOPLModOffset[channel];
+	byte car = kOPLCarOffset[channel];
+
+	_opl->writeReg(0x20 + mod, patch[0]);
+	_opl->writeReg(0x20 + car, patch[1]);
+	_opl->writeReg(0x40 + mod, patch[2]);
+	_opl->writeReg(0x40 + car, patch[3]);
+	_opl->writeReg(0x60 + mod, patch[4]);
+	_opl->writeReg(0x60 + car, patch[5]);
+	_opl->writeReg(0x80 + mod, patch[6]);
+	_opl->writeReg(0x80 + car, patch[7]);
+	_opl->writeReg(0xE0 + mod, patch[8]);
+	_opl->writeReg(0xE0 + car, patch[9]);
+	_opl->writeReg(0xC0 + channel, patch[10]);
+}
+
+void EclipseOPLMusicPlayer::noteOn(int channel, byte note) {
+	if (!_opl)
+		return;
+	uint16 fnum;
+	byte block;
+	noteToFnumBlock(note, fnum, block);
+
+	_channels[channel].keyOn = true;
+	_opl->writeReg(0xA0 + channel, fnum & 0xFF);
+	_opl->writeReg(0xB0 + channel, 0x20 | (block << 2) | ((fnum >> 8) & 0x03));
+}
+
+void EclipseOPLMusicPlayer::noteOff(int channel) {
+	if (!_opl)
+		return;
+	_channels[channel].keyOn = false;
+	// Clear key-on bit, preserve frequency
+	byte b0 = _opl ? 0x00 : 0x00; // just clear everything
+	_opl->writeReg(0xB0 + channel, b0);
+}
+
+// ============================================================================
+// Timer / sequencer core
+// ============================================================================
+
+void EclipseOPLMusicPlayer::onTimer() {
+	if (!_musicActive)
+		return;
+
+	bool newBeat = (_speedCounter == 0);
+
+	for (int channel = kChannelCount - 1; channel >= 0; channel--)
+		processChannel(channel, newBeat);
+
+	if (!_musicActive)
+		return;
+
+	if (newBeat)
+		_speedCounter = _speedDivider;
+	else
+		_speedCounter--;
+}
+
+void EclipseOPLMusicPlayer::processChannel(int channel, bool newBeat) {
+	if (newBeat) {
+		_channels[channel].durationCounter--;
+		if (_channels[channel].durationCounter == 0xFF) {
+			parseCommands(channel);
+			if (!_musicActive)
+				return;
+			finalizeChannel(channel);
+			return;
+		}
+
+		if (_channels[channel].noteStepCommand != 0) {
+			if (_channels[channel].noteStepCommand == 0xDE) {
+				if (_channels[channel].currentNote > 0)
+					_channels[channel].currentNote--;
+			} else if (_channels[channel].currentNote < kMaxNote) {
+				_channels[channel].currentNote++;
+			}
+			loadCurrentFrequency(channel);
+			finalizeChannel(channel);
+			return;
+		}
+	} else if (_channels[channel].stepDownCounter != 0) {
+		_channels[channel].stepDownCounter--;
+		if (_channels[channel].currentNote > 0)
+			_channels[channel].currentNote--;
+		loadCurrentFrequency(channel);
+		finalizeChannel(channel);
+		return;
+	}
+
+	applyFrameEffects(channel);
+	finalizeChannel(channel);
+}
+
+void EclipseOPLMusicPlayer::finalizeChannel(int channel) {
+	// Gate off at half duration: trigger release via key-off
+	if (_channels[channel].durationReload != 0 &&
+	    !_channels[channel].gateOffDisabled &&
+	    ((_channels[channel].durationReload >> 1) == _channels[channel].durationCounter)) {
+		noteOff(channel);
+	}
+}
+
+// ============================================================================
+// Song setup
+// ============================================================================
+
+void EclipseOPLMusicPlayer::setupSong() {
+	silenceAll();
+
+	// Enable wave select (required for non-sine waveforms)
+	if (_opl)
+		_opl->writeReg(0x01, 0x20);
+
+	_speedDivider = 1;
+	_speedCounter = 0;
+
+	const byte *orderLists[3] = { kOrderList0, kOrderList1, kOrderList2 };
+
+	for (int i = 0; i < kChannelCount; i++) {
+		_channels[i].reset();
+		_channels[i].orderList = orderLists[i];
+		loadNextPattern(i);
+	}
+
+	_musicActive = true;
+}
+
+void EclipseOPLMusicPlayer::silenceAll() {
+	if (!_opl)
+		return;
+	for (int ch = 0; ch < 9; ch++) {
+		_opl->writeReg(0xB0 + ch, 0x00); // key off
+		_opl->writeReg(0x40 + kOPLModOffset[ch < 3 ? ch : 0], 0x3F); // silence mod
+		_opl->writeReg(0x40 + kOPLCarOffset[ch < 3 ? ch : 0], 0x3F); // silence car
+	}
+}
+
+// ============================================================================
+// Order list / pattern navigation
+// ============================================================================
+
+void EclipseOPLMusicPlayer::loadNextPattern(int channel) {
+	int safety = 200;
+	while (safety-- > 0) {
+		byte value = _channels[channel].orderList[_channels[channel].orderPos];
+		_channels[channel].orderPos++;
+
+		if (value == 0xFF) {
+			_channels[channel].orderPos = 0;
+			continue;
+		}
+
+		if (value >= 0xC0) {
+			_channels[channel].transpose = (byte)WBCommon::decodeOrderTranspose(value);
+			continue;
+		}
+
+		if (value < ARRAYSIZE(kPatternOffsets)) {
+			_channels[channel].patternDataOffset = kPatternOffsets[value];
+			_channels[channel].patternOffset = 0;
+		}
+		break;
+	}
+}
+
+byte EclipseOPLMusicPlayer::readPatternByte(int channel) {
+	byte value = kPatternData[_channels[channel].patternDataOffset + _channels[channel].patternOffset];
+	_channels[channel].patternOffset++;
+	return value;
+}
+
+byte EclipseOPLMusicPlayer::clampNote(byte note) const {
+	return note > kMaxNote ? kMaxNote : note;
+}
+
+// ============================================================================
+// Pattern command parser
+// ============================================================================
+
+void EclipseOPLMusicPlayer::parseCommands(int channel) {
+	if (_channels[channel].effectMode != 2) {
+		_channels[channel].effectParam = 0;
+		_channels[channel].effectMode = 0;
+		_channels[channel].arpeggioSequenceLen = 0;
+		_channels[channel].arpeggioSequencePos = 0;
+	}
+
+	_channels[channel].arpeggioTarget = 0;
+	_channels[channel].noteStepCommand = 0;
+
+	int safety = 200;
+	while (safety-- > 0) {
+		byte cmd = readPatternByte(channel);
+
+		if (cmd == 0xFF) {
+			loadNextPattern(channel);
+			continue;
+		}
+
+		if (cmd == 0xFE) {
+			stopMusic();
+			return;
+		}
+
+		if (cmd == 0xFD) {
+			readPatternByte(channel);
+			cmd = readPatternByte(channel);
+			if (cmd == 0xFF) {
+				loadNextPattern(channel);
+				continue;
+			}
+		}
+
+		if (cmd >= 0xF0) {
+			_speedDivider = cmd & 0x0F;
+			continue;
+		}
+
+		if (cmd >= 0xC0) {
+			byte instrument = cmd & 0x1F;
+			if (instrument < kInstrumentCount)
+				_channels[channel].instrumentOffset = instrument * kInstrumentSize;
+			continue;
+		}
+
+		if (cmd >= 0x80) {
+			_channels[channel].durationReload = cmd & 0x3F;
+			continue;
+		}
+
+		if (cmd == 0x7F) {
+			_channels[channel].noteStepCommand = 0xDE;
+			_channels[channel].effectMode = 0xDE;
+			continue;
+		}
+
+		if (cmd == 0x7E) {
+			_channels[channel].effectMode = 0xFE;
+			continue;
+		}
+
+		if (cmd == 0x7D) {
+			_channels[channel].effectMode = 1;
+			_channels[channel].effectParam = readPatternByte(channel);
+			buildEffectArpeggio(channel);
+			continue;
+		}
+
+		if (cmd == 0x7C) {
+			_channels[channel].effectMode = 2;
+			_channels[channel].effectParam = readPatternByte(channel);
+			buildEffectArpeggio(channel);
+			continue;
+		}
+
+		if (cmd == 0x7B) {
+			_channels[channel].effectParam = 0;
+			_channels[channel].effectMode = 1;
+			_channels[channel].arpeggioTarget = readPatternByte(channel) + _channels[channel].transpose;
+			_channels[channel].arpeggioParam = readPatternByte(channel);
+			continue;
+		}
+
+		if (cmd == 0x7A) {
+			_channels[channel].delayValue = readPatternByte(channel);
+			cmd = readPatternByte(channel);
+		}
+
+		applyNote(channel, cmd);
+		return;
+	}
+}
+
+// ============================================================================
+// Note application
+// ============================================================================
+
+void EclipseOPLMusicPlayer::applyNote(int channel, byte note) {
+	byte instrumentOffset = _channels[channel].instrumentOffset;
+	byte ctrl = kInstruments[instrumentOffset + 0];
+	byte autoEffect = kInstruments[instrumentOffset + 4];
+	byte flags = kInstruments[instrumentOffset + 5];
+	byte sustainRelease = kInstruments[instrumentOffset + 2];
+	byte actualNote = note;
+
+	if (actualNote != 0)
+		actualNote = clampNote(actualNote + _channels[channel].transpose);
+
+	_channels[channel].currentNote = actualNote;
+	_channels[channel].waveform = ctrl;
+	_channels[channel].instrumentFlags = flags;
+	_channels[channel].stepDownCounter = 0;
+
+	if (actualNote != 0 && _channels[channel].effectParam == 0 && autoEffect != 0) {
+		_channels[channel].effectParam = autoEffect;
+		buildEffectArpeggio(channel);
+	}
+
+	if (actualNote != 0 && (flags & 0x02) != 0) {
+		_channels[channel].stepDownCounter = 2;
+		_channels[channel].currentNote = clampNote(_channels[channel].currentNote + 2);
+	}
+
+	// Set the OPL FM patch for this instrument
+	setOPLInstrument(channel, instrumentOffset);
+
+	_channels[channel].gateOffDisabled = (sustainRelease & 0x0F) == 0x0F;
+
+	if (actualNote == 0) {
+		noteOff(channel);
+	} else {
+		// Key off then on to retrigger envelope
+		noteOff(channel);
+		noteOn(channel, actualNote);
+	}
+
+	_channels[channel].durationCounter = _channels[channel].durationReload;
+	_channels[channel].delayCounter = _channels[channel].delayValue;
+	_channels[channel].arpeggioSequencePos = 0;
+}
+
+// ============================================================================
+// Frequency helpers
+// ============================================================================
+
+void EclipseOPLMusicPlayer::loadCurrentFrequency(int channel) {
+	byte note = clampNote(_channels[channel].currentNote);
+	uint16 fnum;
+	byte block;
+	noteToFnumBlock(note, fnum, block);
+	setFrequency(channel, fnum, block);
+}
+
+// ============================================================================
+// Effects
+// ============================================================================
+
+void EclipseOPLMusicPlayer::buildEffectArpeggio(int channel) {
+	_channels[channel].arpeggioSequenceLen = WBCommon::buildArpeggioTable(
+		_arpeggioIntervals,
+		_channels[channel].effectParam,
+		_channels[channel].arpeggioSequence,
+		sizeof(_channels[channel].arpeggioSequence),
+		true);
+	_channels[channel].arpeggioSequencePos = 0;
+}
+
+void EclipseOPLMusicPlayer::applyFrameEffects(int channel) {
+	if (_channels[channel].currentNote == 0)
+		return;
+
+	if (applyInstrumentVibrato(channel))
+		return;
+
+	applyEffectArpeggio(channel);
+	applyTimedSlide(channel);
+}
+
+bool EclipseOPLMusicPlayer::applyInstrumentVibrato(int channel) {
+	byte vibrato = kInstruments[_channels[channel].instrumentOffset + 3];
+	if (vibrato == 0 || _channels[channel].currentNote >= kMaxNote)
+		return false;
+
+	byte shift = vibrato & 0x0F;
+	byte span = vibrato >> 4;
+	if (span == 0)
+		return false;
+
+	uint16 noteFnum, nextFnum;
+	byte noteBlock, nextBlock;
+	noteToFnumBlock(_channels[channel].currentNote, noteFnum, noteBlock);
+	noteToFnumBlock(_channels[channel].currentNote + 1, nextFnum, nextBlock);
+
+	// Normalize to same block for delta computation
+	int32 noteFreq = noteFnum << noteBlock;
+	int32 nextFreq = nextFnum << nextBlock;
+	int32 delta = nextFreq - noteFreq;
+	if (delta <= 0)
+		return false;
+
+	while (shift-- != 0)
+		delta >>= 1;
+
+	if ((_channels[channel].vibratoPhase & 0x80) != 0) {
+		if (_channels[channel].vibratoCounter != 0)
+			_channels[channel].vibratoCounter--;
+		if (_channels[channel].vibratoCounter == 0)
+			_channels[channel].vibratoPhase = 0;
+	} else {
+		_channels[channel].vibratoCounter++;
+		if (_channels[channel].vibratoCounter >= span)
+			_channels[channel].vibratoPhase = 0xFF;
+	}
+
+	if (_channels[channel].delayCounter != 0) {
+		_channels[channel].delayCounter--;
+		return false;
+	}
+
+	int32 freq = noteFreq;
+	for (byte i = 0; i < (span >> 1); i++)
+		freq -= delta;
+	for (byte i = 0; i < _channels[channel].vibratoCounter; i++)
+		freq += delta;
+
+	if (freq < 1) freq = 1;
+
+	// Convert back to fnum/block
+	byte block = 0;
+	while (freq > 1023 && block < 7) {
+		freq >>= 1;
+		block++;
+	}
+	setFrequency(channel, freq & 0x3FF, block);
+	return true;
+}
+
+void EclipseOPLMusicPlayer::applyEffectArpeggio(int channel) {
+	if (_channels[channel].effectParam == 0 || _channels[channel].arpeggioSequenceLen == 0)
+		return;
+
+	if (_channels[channel].arpeggioSequencePos >= _channels[channel].arpeggioSequenceLen)
+		_channels[channel].arpeggioSequencePos = 0;
+
+	byte note = clampNote(_channels[channel].currentNote +
+	                      _channels[channel].arpeggioSequence[_channels[channel].arpeggioSequencePos]);
+	_channels[channel].arpeggioSequencePos++;
+
+	uint16 fnum;
+	byte block;
+	noteToFnumBlock(note, fnum, block);
+	setFrequency(channel, fnum, block);
+}
+
+void EclipseOPLMusicPlayer::applyTimedSlide(int channel) {
+	if (_channels[channel].arpeggioTarget == 0)
+		return;
+
+	byte total = _channels[channel].durationReload;
+	byte remaining = _channels[channel].durationCounter;
+	byte start = _channels[channel].arpeggioParam >> 4;
+	byte span = _channels[channel].arpeggioParam & 0x0F;
+	byte elapsed = total - remaining;
+
+	if (elapsed <= start || elapsed > start + span || span == 0)
+		return;
+
+	byte currentNote = clampNote(_channels[channel].currentNote);
+	byte targetNote = clampNote(_channels[channel].arpeggioTarget);
+	if (currentNote == targetNote)
+		return;
+
+	uint16 srcFnum, tgtFnum;
+	byte srcBlock, tgtBlock;
+	noteToFnumBlock(currentNote, srcFnum, srcBlock);
+	noteToFnumBlock(targetNote, tgtFnum, tgtBlock);
+
+	int32 srcFreq = srcFnum << srcBlock;
+	int32 tgtFreq = tgtFnum << tgtBlock;
+	int32 difference = srcFreq > tgtFreq ? srcFreq - tgtFreq : tgtFreq - srcFreq;
+	uint16 divisor = span * (_speedDivider + 1);
+	if (divisor == 0)
+		return;
+
+	int32 delta = difference / divisor;
+	if (delta == 0)
+		return;
+
+	// Read current frequency from stored fnum/block
+	uint16 curFnum;
+	byte curBlock;
+	noteToFnumBlock(currentNote, curFnum, curBlock);
+	int32 curFreq = curFnum << curBlock;
+
+	if (tgtFreq > srcFreq)
+		curFreq += delta;
+	else
+		curFreq -= delta;
+
+	if (curFreq < 1) curFreq = 1;
+
+	byte block = 0;
+	while (curFreq > 1023 && block < 7) {
+		curFreq >>= 1;
+		block++;
+	}
+	setFrequency(channel, curFreq & 0x3FF, block);
+}
+
+} // namespace Freescape
diff --git a/engines/freescape/games/eclipse/opl.music.h b/engines/freescape/games/eclipse/opl.music.h
new file mode 100644
index 00000000000..0f49e9c6ffd
--- /dev/null
+++ b/engines/freescape/games/eclipse/opl.music.h
@@ -0,0 +1,124 @@
+/* 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 FREESCAPE_ECLIPSE_OPL_MUSIC_H
+#define FREESCAPE_ECLIPSE_OPL_MUSIC_H
+
+#include "audio/fmopl.h"
+
+namespace Freescape {
+
+/**
+ * OPL2/AdLib music player for Total Eclipse DOS.
+ *
+ * Ports the Wally Beben C64 SID music to the OPL2 FM chip by:
+ * - Reusing the same sequencer (order lists, patterns, instruments)
+ * - Converting SID note numbers to OPL F-number/block pairs
+ * - Mapping SID waveforms to OPL FM instrument patches
+ * - Using OPL's built-in ADSR envelopes
+ */
+class EclipseOPLMusicPlayer {
+public:
+	EclipseOPLMusicPlayer();
+	~EclipseOPLMusicPlayer();
+
+	void startMusic();
+	void stopMusic();
+	bool isPlaying() const;
+
+private:
+	static const byte kChannelCount = 3;
+	static const byte kMaxNote = 94;
+
+	struct ChannelState {
+		const byte *orderList;
+		byte orderPos;
+
+		uint16 patternDataOffset;
+		uint16 patternOffset;
+
+		byte instrumentOffset;
+		byte currentNote;
+		byte transpose;
+
+		byte durationReload;
+		byte durationCounter;
+
+		byte effectMode;
+		byte effectParam;
+		byte arpeggioTarget;
+		byte arpeggioParam;
+		byte arpeggioSequencePos;
+		byte arpeggioSequence[9];
+		byte arpeggioSequenceLen;
+
+		byte noteStepCommand;
+		byte stepDownCounter;
+
+		byte vibratoPhase;
+		byte vibratoCounter;
+
+		byte delayValue;
+		byte delayCounter;
+
+		byte waveform;
+		byte instrumentFlags;
+		bool gateOffDisabled;
+		bool keyOn;
+
+		void reset();
+	};
+
+	OPL::OPL *_opl;
+	bool _musicActive;
+	byte _speedDivider;
+	byte _speedCounter;
+	ChannelState _channels[kChannelCount];
+	byte _arpeggioIntervals[8];
+
+	void onTimer();
+	void setupSong();
+	void silenceAll();
+	void loadNextPattern(int channel);
+	void buildEffectArpeggio(int channel);
+	void loadCurrentFrequency(int channel);
+	void finalizeChannel(int channel);
+	void processChannel(int channel, bool newBeat);
+	void parseCommands(int channel);
+	void applyNote(int channel, byte note);
+	void applyFrameEffects(int channel);
+	bool applyInstrumentVibrato(int channel);
+	void applyEffectArpeggio(int channel);
+	void applyTimedSlide(int channel);
+
+	void setOPLInstrument(int channel, byte instrumentOffset);
+	void noteOn(int channel, byte note);
+	void noteOff(int channel);
+	void setFrequency(int channel, uint16 fnum, byte block);
+	void noteToFnumBlock(byte note, uint16 &fnum, byte &block) const;
+
+	byte readPatternByte(int channel);
+	byte clampNote(byte note) const;
+};
+
+} // namespace Freescape
+
+#endif
diff --git a/engines/freescape/metaengine.cpp b/engines/freescape/metaengine.cpp
index aba59923e52..77b1feb7baa 100644
--- a/engines/freescape/metaengine.cpp
+++ b/engines/freescape/metaengine.cpp
@@ -158,6 +158,18 @@ static const ADExtraGuiOptionsMap optionsList[] = {
 			0
 		}
 	},
+	{
+		GAMEOPTION_OPL_MUSIC,
+		{
+			// I18N: Enable background music using AdLib/OPL2 FM synthesis
+			_s("Backported music from C64 releases (AdLib)"),
+			_s("Enable background music ported from the C64 version using AdLib FM synthesis"),
+			"opl_music",
+			false,
+			0,
+			0
+		}
+	},
 	{
 		GAMEOPTION_AY_MUSIC,
 		{
diff --git a/engines/freescape/module.mk b/engines/freescape/module.mk
index 17c97329b97..de18151c54d 100644
--- a/engines/freescape/module.mk
+++ b/engines/freescape/module.mk
@@ -42,6 +42,7 @@ MODULE_OBJS := \
 	games/eclipse/c64.sfx.o \
 	games/eclipse/dos.o \
 	games/eclipse/eclipse.o \
+	games/eclipse/opl.music.o \
 	games/eclipse/cpc.o \
 	games/eclipse/zx.o \
 	games/palettes.o \




More information about the Scummvm-git-logs mailing list