[Scummvm-git-logs] scummvm master -> dd980af9ff65d1f5452b43baea7531a532466b6f
sev-
noreply at scummvm.org
Tue Mar 10 17:57:51 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:
dd980af9ff WAGE: added ability to display Startup Image and play Startup Sound for supported games
Commit: dd980af9ff65d1f5452b43baea7531a532466b6f
https://github.com/scummvm/scummvm/commit/dd980af9ff65d1f5452b43baea7531a532466b6f
Author: roby405 (robep2341 at gmail.com)
Date: 2026-03-10T18:57:45+01:00
Commit Message:
WAGE: added ability to display Startup Image and play Startup Sound for supported games
Changed paths:
engines/wage/gui.cpp
engines/wage/gui.h
engines/wage/sound.cpp
engines/wage/wage.h
diff --git a/engines/wage/gui.cpp b/engines/wage/gui.cpp
index 2c46d01df44..05d5bae8ebc 100644
--- a/engines/wage/gui.cpp
+++ b/engines/wage/gui.cpp
@@ -47,6 +47,7 @@
#include "common/timer.h"
#include "common/system.h"
#include "common/config-manager.h"
+#include "common/macresman.h"
#include "audio/softsynth/pcspk.h"
@@ -62,6 +63,7 @@
#include "wage/entities.h"
#include "wage/gui.h"
#include "wage/world.h"
+#include "wage/sound.h"
namespace Wage {
@@ -108,6 +110,10 @@ Gui::Gui(WageEngine *engine) {
_menu->addStaticMenus(menuSubItems);
_menu->addSubMenu(nullptr, kMenuAbout);
_menu->addMenuItem(_menu->getSubmenu(nullptr, kMenuAbout), _engine->_world->getAboutMenuItemName(), kMenuActionAbout);
+ if (Common::File::exists("StartUpScreen"))
+ _menu->addMenuItem(_menu->getSubmenu(nullptr, kMenuAbout), "Startup Screen", kMenuActionStartupScreen);
+ if (Common::File::exists("StartupSound"))
+ _menu->addMenuItem(_menu->getSubmenu(nullptr, kMenuAbout), "Startup Sound", kMenuActionStartupSound);
if (!_engine->_world->_fileMenu.empty()) {
_menu->setName(_menu->getMenuItem(kMenuFile), _engine->_world->_fileMenuName);
@@ -356,6 +362,24 @@ void menuCommandsCallback(int action, Common::String &text, void *data) {
g->executeMenuCommand(action, text);
}
+bool Gui::decodeStartupScreen() {
+ Common::SeekableReadStream *stream = Common::MacResManager::openFileOrDataFork("StartupScreen");
+ if (!stream)
+ return false;
+
+ for (int y = 0; y < kScreenHeight; y++) {
+ for (int x = 0; x < kScreenWidth / 8; x++) {
+ byte b = stream->readByte();
+
+ for (int z = 0; z < 8; z++) {
+ _screen.setPixel(8 * x + z, y, (b & (0x80 >> z)) ? kColorBlack : kColorWhite);
+ }
+ }
+ }
+
+ return true;
+}
+
void Gui::executeMenuCommand(int action, Common::String &text) {
switch(action) {
case kMenuActionAbout:
@@ -428,6 +452,35 @@ void Gui::executeMenuCommand(int action, Common::String &text) {
_engine->processTurn(&text, NULL);
break;
}
+ case kMenuActionStartupScreen: {
+ if (!decodeStartupScreen()) {
+ warning("StartUpScreen file not found");
+ break;
+ }
+ g_system->copyRectToScreen(_screen.getPixels(), _screen.pitch,
+ 0, 0, _screen.w, _screen.h);
+
+ uint32 now = g_system->getMillis();
+ bool earlyExit = false;
+
+ while (g_system->getMillis() < now + 3000 && !_engine->shouldQuit() && !earlyExit) {
+ Common::Event event;
+
+ while (_engine->getEventManager()->pollEvent(event)) {
+ if (event.type == Common::EVENT_KEYDOWN || event.type == Common::EVENT_LBUTTONUP) {
+ earlyExit = true;
+ break;
+ }
+ }
+
+ g_system->updateScreen();
+ g_system->delayMillis(10);
+ }
+ break;
+ }
+ case kMenuActionStartupSound:
+ actionStartupSound();
+ break;
default:
warning("Unknown action: %d", action);
@@ -509,6 +562,121 @@ void Gui::actionCut() {
_menu->enableCommand(kMenuEdit, kMenuActionPaste, true);
}
+// HCOM file structure and algorithm adapted from https://stuff.mit.edu/afs/net/dev/contrib/audio/src/sox/hcom.c
+void Gui::actionStartupSound() {
+ Common::SeekableReadStream *file = Common::MacResManager::openFileOrDataFork("StartupSound");
+ if (!file) {
+ return;
+ }
+
+ // Huffman tree node structure
+ struct DictEnt {
+ int16 leftson;
+ int16 rightson;
+ };
+
+ uint32 magic = file->readUint32BE();
+ if (magic != MKTAG('H','C','O','M')) {
+ delete file;
+ return;
+ }
+
+ uint32 huffCount = file->readUint32BE(); ///< Sample count
+ file->readUint32BE(); ///< Unused checksum
+ uint32 compressType = file->readUint32BE(); ///< 0 = value compression, 1 = delta compression
+ uint32 divisor = file->readUint32BE(); ///< Sample rate divisor, only between 1 and 4
+ uint16 dictSize = file->readUint16BE(); ///< Number of dictionary entries
+
+ if (compressType > 1) {
+ delete file;
+ return;
+ }
+
+ if (divisor == 0 || divisor > 4) {
+ delete file;
+ return;
+ }
+
+ // Read Huffman tree
+ Common::Array<DictEnt> dictionary(dictSize);
+ for (uint16 i = 0; i < dictSize; i++) {
+ dictionary[i].leftson = file->readSint16BE();
+ dictionary[i].rightson = file->readSint16BE();
+ }
+
+ file->readByte(); // Skip pad byte
+ // First byte is uncompressed
+ uint8 currentHuff = file->readByte();
+ uint32 huffsRemaining = huffCount;
+
+ byte *byteStream = (byte *)malloc(huffCount);
+ if (!byteStream) {
+ delete file;
+ return;
+ }
+
+ byte *out = byteStream;
+ if (huffsRemaining > 0) {
+ *out++ = currentHuff;
+ huffsRemaining--;
+ }
+
+ uint32 currentBits = 0;
+ int bitsLeft = 0;
+ int dictEntry = 0;
+
+ // Bit-by-bit decompression
+ while (huffsRemaining > 0 && !file->eos()) {
+ // Work 32 bits at a time
+ if (bitsLeft == 0) {
+ currentBits = file->readUint32BE();
+ bitsLeft = 32;
+ }
+
+ // Read most significant bit
+ bool bit = (currentBits & 0x80000000) != 0;
+ currentBits <<= 1;
+ bitsLeft--;
+
+ // Traverse Huffman tree
+ if (bit) {
+ dictEntry = dictionary[dictEntry].rightson;
+ } else {
+ dictEntry = dictionary[dictEntry].leftson;
+ }
+
+ // Validate data
+ if (dictEntry < 0 || dictEntry >= dictSize) {
+ break;
+ }
+
+ // Negative leftson means we've reached leaf node
+ if (dictionary[dictEntry].leftson < 0) {
+ int16 datum = dictionary[dictEntry].rightson; // Value is stored in rightson
+
+ // Absolute values
+ if (compressType == 0) {
+ currentHuff = 0;
+ }
+
+ currentHuff = (currentHuff + datum) & 0xFF; // Sample
+
+ *out++ = currentHuff;
+ huffsRemaining--;
+ dictEntry = 0;
+ }
+ }
+
+ delete file;
+
+ if (huffCount - huffsRemaining == 0) {
+ free(byteStream);
+ return;
+ }
+
+ _engine->playStartupSound(byteStream, huffCount - huffsRemaining, divisor);
+}
+
void Gui::disableUndo() {
_menu->enableCommand(kMenuEdit, kMenuActionUndo, false);
}
diff --git a/engines/wage/gui.h b/engines/wage/gui.h
index b666f786439..3204eb7d5a4 100644
--- a/engines/wage/gui.h
+++ b/engines/wage/gui.h
@@ -110,7 +110,15 @@ enum {
kMenuActionPaste,
kMenuActionClear,
- kMenuActionCommand
+ kMenuActionCommand,
+
+ kMenuActionStartupScreen,
+ kMenuActionStartupSound
+};
+
+enum {
+ kScreenWidth = 512,
+ kScreenHeight = 342
};
class Gui {
@@ -131,11 +139,13 @@ public:
void actionUndo();
void actionClear();
void actionCut();
+ void actionStartupSound();
void disableUndo();
void disableAllMenus();
void enableNewGameMenus();
void enableSave();
void enableRevert();
+ bool decodeStartupScreen();
bool processSceneEvents(WindowClick click, Common::Event &event);
bool processConsoleEvents(WindowClick click, Common::Event &event);
diff --git a/engines/wage/sound.cpp b/engines/wage/sound.cpp
index 6115e2381f6..29b2db25935 100644
--- a/engines/wage/sound.cpp
+++ b/engines/wage/sound.cpp
@@ -130,6 +130,19 @@ void WageEngine::playSound(Common::String soundName, bool blocking) {
}
}
+void WageEngine::playStartupSound(byte *stream, uint32 size, int divisor) {
+ Audio::AudioStream *audioStream = Audio::makeRawStream(
+ stream,
+ size,
+ 22254 / divisor, // 22254 is default Macintosh frequency
+ Audio::FLAG_UNSIGNED,
+ DisposeAfterUse::YES
+ );
+
+ _mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle, audioStream,
+ -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO);
+}
+
static void soundTimer(void *refCon) {
Scene *scene = (Scene *)refCon;
WageEngine *engine = (WageEngine *)g_engine;
diff --git a/engines/wage/wage.h b/engines/wage/wage.h
index a30e55cb62e..31c970f7506 100644
--- a/engines/wage/wage.h
+++ b/engines/wage/wage.h
@@ -220,6 +220,7 @@ public:
Audio::PCSpeaker *_speaker;
void playSound(Common::String soundName, bool blocking = true);
+ void playStartupSound(byte *stream, uint32 size, int divisor);
void updateSoundTimerForScene(Scene *scene, bool firstTime);
void setMenu(Common::String soundName);
void appendText(const char *str);
More information about the Scummvm-git-logs
mailing list