Files
scummvm-cursorfix/engines/scumm/macgui/macgui_v6.cpp
2026-02-02 04:50:13 +01:00

1313 lines
35 KiB
C++

/* 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 "common/system.h"
#include "common/config-manager.h"
#include "common/macresman.h"
#include "audio/mixer.h"
#include "engines/engine.h"
#include "engines/metaengine.h"
#include "engines/savestate.h"
#include "graphics/color_quantizer.h"
#include "graphics/macgamma.h"
#include "graphics/palette.h"
#include "graphics/paletteman.h"
#include "graphics/macgui/macwindowmanager.h"
#include "graphics/surface.h"
#include "scumm/scumm.h"
#include "scumm/detection.h"
#include "scumm/file.h"
#include "scumm/imuse_digi/dimuse_engine.h"
#include "scumm/macgui/macgui_impl.h"
#include "scumm/macgui/macgui_v6.h"
namespace Scumm {
// ===========================================================================
// The Maniac Mansion + Mac SCUMM v6 (and later) GUI
//
// Note that the original Maniac Mansion GUI would not open the Open and Save
// dialogs directly. Instead it would trigger the built-in save/load screen,
// and trigger the dialogs from there. At least for now, that's more work than
// it is worth.
// ===========================================================================
MacV6Gui::MacV6Gui(ScummEngine *vm, const Common::Path &resourceFile) : MacGuiImpl(vm, resourceFile) {
_backupScreen = nullptr;
_backupPalette = nullptr;
if (_vm->_game.id == GID_TENTACLE)
_gameName = "Day of the Tentacle";
else if (_vm->_game.id == GID_SAMNMAX)
_gameName = "Sam & Max";
#ifdef ENABLE_SCUMM_7_8
else if (_vm->_game.id == GID_DIG)
_gameName = "The Dig";
else if (_vm->_game.id == GID_FT)
_gameName = "Full Throttle";
#endif
else if (_vm->_game.id == GID_MANIAC)
_gameName = "Maniac Mansion";
else if (_vm->_game.id == GID_INDY4)
_gameName = "Fate of Atlantis PowerPC";
else
_gameName = "Some Game I Do Not Know";
if (_vm->_game.features & GF_DEMO)
_gameName += " Demo";
}
MacV6Gui::~MacV6Gui() {
if (_backupScreen) {
_backupScreen->free();
delete _backupScreen;
}
delete[] _backupPalette;
}
bool MacV6Gui::initialize() {
if (!MacGuiImpl::initialize())
return false;
// We can't use the name of the menus here, because there are
// non-English versions. Let's hope the menu positions are always the
// same, at least!
Graphics::MacMenu *menu = _windowManager->getMenu();
Graphics::MacMenuItem *editMenu = menu->getMenuItem(2);
Graphics::MacMenuItem *videoMenu = menu->getMenuItem(3);
editMenu->enabled = false;
menu->getSubMenuItem(videoMenu, 0)->enabled = false; // Small
menu->getSubMenuItem(videoMenu, 1)->enabled = false; // Interlaced
if (_vm->_game.id == GID_MANIAC) {
Graphics::MacMenuItem *soundMenu = menu->getMenuItem(4);
soundMenu->enabled = false;
}
return true;
}
bool MacV6Gui::readStrings() {
_strsStrings.clear();
_strsStrings.reserve(128);
for (int i = 0; i < 128; i++)
_strsStrings.emplace_back("");
return true;
}
const Graphics::Font *MacV6Gui::getFontByScummId(int32 id) {
// V6 and V7 games (and Maniac Mansion) do not use CharsetRendererMac
return nullptr;
}
bool MacV6Gui::getFontParams(FontId fontId, int &id, int &size, int &slant) const {
return false;
}
void MacV6Gui::setupCursor(int &width, int &height, int &hotspotX, int &hotspotY, int &animate) {
if (_vm->_game.id == GID_INDY4) {
byte cross[15 * 15];
memset(cross, 3, sizeof(cross));
for (int i = 0; i < 15; i++) {
if (i < 6 || i > 8) {
cross[i * 15 + 7] = 1;
cross[7 * 15 + i] = 1;
}
}
width = height = 15;
hotspotX = hotspotY = 7;
animate = false;
_windowManager->replaceCustomCursor(cross, width, height, hotspotX, hotspotY, 3);
} else if (_vm->_game.id == GID_MANIAC) {
byte invertedMacArrow[11 * 16] = {
0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3,
0, 1, 0, 3, 3, 3, 3, 3, 3, 3, 3,
0, 1, 1, 0, 3, 3, 3, 3, 3, 3, 3,
0, 1, 1, 1, 0, 3, 3, 3, 3, 3, 3,
0, 1, 1, 1, 1, 0, 3, 3, 3, 3, 3,
0, 1, 1, 1, 1, 1, 0, 3, 3, 3, 3,
0, 1, 1, 1, 1, 1, 1, 0, 3, 3, 3,
0, 1, 1, 1, 1, 1, 1, 1, 0, 3, 3,
0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 3,
0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
0, 1, 1, 0, 1, 1, 0, 3, 3, 3, 3,
0, 1, 0, 3, 0, 1, 1, 0, 3, 3, 3,
0, 0, 3, 3, 0, 1, 1, 0, 3, 3, 3,
0, 3, 3, 3, 3, 0, 1, 1, 0, 3, 3,
3, 3, 3, 3, 3, 0, 1, 1, 0, 3, 3,
3, 3, 3, 3, 3, 3, 0, 0, 0, 3, 3
};
width = 11;
height = 16;
hotspotX = 1;
hotspotY = 1;
animate = false;
_windowManager->replaceCustomCursor(invertedMacArrow, width, height, hotspotX, hotspotY, 3);
}
}
void MacV6Gui::updateMenus() {
MacGuiImpl::updateMenus();
#if ENABLE_SCUMM_7_8
// Remember the old music and sfx volume so that we can toggle back and
// forth between that and 0. If we don't have an old volume, use the
// default value. We could save it to the config file, but... Nah.
if (_vm->_imuseDigital && _oldMusicVolume == -1 && _oldSfxVolume == -1) {
_oldMusicVolume = _vm->_imuseDigital->diMUSEGetMusicGroupVol();
if (_oldMusicVolume == 0)
_oldMusicVolume = 81;
_oldSfxVolume = _vm->_imuseDigital->diMUSEGetSFXGroupVol();
if (_oldSfxVolume == 0)
_oldSfxVolume = 81;
}
#endif
Graphics::MacMenu *menu = _windowManager->getMenu();
Graphics::MacMenuItem *videoMenu = menu->getMenuItem(3);
menu->getSubMenuItem(videoMenu, 2)->checked = true;
if (_vm->_game.id != GID_MANIAC)
menu->getSubMenuItem(videoMenu, 3)->checked = _vm->_useMacGraphicsSmoothing;
Graphics::MacMenuItem *soundMenu = menu->getMenuItem(4);
int voiceMenuIndex = 5;
#if ENABLE_SCUMM_7_8
if (_vm->_game.version >= 7) {
if (_vm->_imuseDigital) {
menu->getSubMenuItem(soundMenu, 0)->checked = (_vm->_imuseDigital->diMUSEGetMusicGroupVol() > 0);
menu->getSubMenuItem(soundMenu, 1)->checked = (_vm->_imuseDigital->diMUSEGetSFXGroupVol() > 0);
}
} else
#endif
if (_vm->_game.id == GID_INDY4) {
menu->getSubMenuItem(soundMenu, 0)->checked = (_vm->_soundEnabled & 2); // Music
voiceMenuIndex = 4;
} else {
menu->getSubMenuItem(soundMenu, 0)->checked = (_vm->_soundEnabled & 2); // Music
menu->getSubMenuItem(soundMenu, 1)->checked = (_vm->_soundEnabled & 1); // Effects
}
for (int i = 0; i < 3; i++)
menu->getSubMenuItem(soundMenu, i + voiceMenuIndex)->checked = false;
switch (_vm->_voiceMode) {
case 0: // Voice Only
menu->getSubMenuItem(soundMenu, voiceMenuIndex + 1)->checked = true;
break;
case 1: // Voice and Text
menu->getSubMenuItem(soundMenu, voiceMenuIndex + 2)->checked = true;
break;
case 2: // Text Only
menu->getSubMenuItem(soundMenu, voiceMenuIndex)->checked = true;
break;
default:
warning("MacV6Gui::updateMenus(): Invalid voice mode %d", _vm->_voiceMode);
break;
}
}
bool MacV6Gui::handleMenu(int id, Common::String &name) {
// Don't call the original method. The menus are too different.
int saveSlotToHandle = -1;
bool syncSoundSettings = false;
Common::String savegameName;
// The Dig and Full Throttle don't have a Restart menu entry
if (_vm->_game.version > 6 && id >= 204 && id < 300)
id++;
// Fate of Atlantis doesn't have an Effects menu entry
if (_vm->_game.id == GID_INDY4 && id >= 501 && id < 600)
id++;
switch (id) {
case 100: // About
runAboutDialog();
return true;
case 200: // Open
if (runOpenDialog(saveSlotToHandle)) {
if (saveSlotToHandle > -1) {
_vm->loadGameState(saveSlotToHandle);
}
}
return true;
case 201: // Save
_vm->beginTextInput();
if (runSaveDialog(saveSlotToHandle, savegameName)) {
if (saveSlotToHandle > -1) {
_vm->saveGameState(saveSlotToHandle, savegameName);
}
}
_vm->endTextInput();
return true;
case 202:
_skipScene = true;
return true;
case 203:
// Resume is supposed to simply close the Mac menu, and that
// happens automatically when selecting any menu item.
return true;
case 204: // Restart
if (runRestartDialog())
_vm->restart();
return true;
case 205:
runOptionsDialog();
return true;
case 206:
if (runQuitDialog())
_vm->quitGame();
return true;
// In the original, the Edit menu is active during save dialogs, though
// only Cut, Copy and Paste.
case 300: // Undo
case 301: // Cut
case 302: // Copy
case 303: // Paste
case 304: // Clear
return true;
case 403: // Graphics Smoothing
if (_vm->_game.id != GID_MANIAC)
_vm->mac_toggleSmoothing();
return true;
case 500: // Music
#if ENABLE_SCUMM_7_8
if (_vm->_game.version >= 7) {
int musicVolume = _vm->_imuseDigital->diMUSEGetMusicGroupVol();
if (musicVolume == 0) {
musicVolume = _oldMusicVolume;
} else {
_oldMusicVolume = musicVolume;
musicVolume = 0;
}
setVolume(0, musicVolume / 8);
} else
#endif
{
_vm->_soundEnabled = (_vm->_soundEnabled & ~8) ^ 2;
ConfMan.setBool("music_mute", !(_vm->_soundEnabled & 2));
ConfMan.setBool("mute", (_vm->_soundEnabled == 0 && _vm->_voiceMode == 2));
}
syncSoundSettings = true;
break;
case 501: // Effects
#if ENABLE_SCUMM_7_8
if (_vm->_game.version >= 7) {
int sfxVolume = _vm->_imuseDigital->diMUSEGetSFXGroupVol();
if (sfxVolume == 0) {
sfxVolume = _oldSfxVolume;
} else {
_oldSfxVolume = sfxVolume;
sfxVolume = 0;
}
setVolume(1, sfxVolume / 8);
} else
#endif
{
_vm->_soundEnabled = (_vm->_soundEnabled & ~8) ^ 1;
ConfMan.setBool("sfx_mute", !(_vm->_soundEnabled & 1));
ConfMan.setBool("mute", (_vm->_soundEnabled == 0 && _vm->_voiceMode == 2));
}
syncSoundSettings = true;
break;
case 502: // Toggle Text & Voice
switch (_vm->_voiceMode) {
case 0: // Voice Only -> Text & Voice
ConfMan.setBool("subtitles", true);
ConfMan.setBool("speech_mute", false);
break;
case 1: // Text & Voice -> Text Only
ConfMan.setBool("subtitles", true);
ConfMan.setBool("speech_mute", true);
break;
case 2: // Text Only -> Voice Only
ConfMan.setBool("subtitles", false);
ConfMan.setBool("speech_mute", false);
break;
default:
warning("Invalid voice mode %d", _vm->_voiceMode);
return true;
}
syncSoundSettings = true;
break;
case 503: // Text Only
ConfMan.setBool("subtitles", true);
ConfMan.setBool("speech_mute", true);
syncSoundSettings = true;
break;
case 504: // Voice Only
ConfMan.setBool("subtitles", false);
ConfMan.setBool("speech_mute", false);
syncSoundSettings = true;
break;
case 505: // Text & Voice
ConfMan.setBool("subtitles", true);
ConfMan.setBool("speech_mute", false);
syncSoundSettings = true;
break;
default:
warning("Unknown menu command: %d", id);
break;
}
if (syncSoundSettings) {
ConfMan.flushToDisk();
_vm->syncSoundSettings();
return true;
}
return false;
}
void MacV6Gui::saveScreen() {
if (_screenSaveLevel++ == 0) {
_suspendPaletteUpdates = true;
Graphics::Surface *screen = _vm->_macScreen;
_backupScreen = new Graphics::Surface();
_backupScreen->copyFrom(*screen);
// We have to grab the actual palette, becaues the engine
// palette may not be what's on screen, e.g. during SMUSH
// movies.
_backupPalette = new byte[256 * 3];
_system->getPaletteManager()->grabPalette(_backupPalette, 0, 256);
Graphics::Palette palette(256);
setMacGuiColors(palette);
_windowManager->passPalette(palette.data(), 256);
if (_vm->_useGammaCorrection) {
for (int i = 0; i < 256; i++) {
byte r, g, b;
palette.get(i, r, g, b);
r = Graphics::macGammaCorrectionLookUp[r];
g = Graphics::macGammaCorrectionLookUp[g];
b = Graphics::macGammaCorrectionLookUp[b];
palette.set(i, r, g, b);
}
}
screen->fillRect(Common::Rect(screen->w, screen->h), getBlack());
_system->copyRectToScreen(screen->getBasePtr(0, 0), screen->pitch, 0, 0, screen->w, screen->h);
_system->getPaletteManager()->setPalette(palette);
if (_windowManager->_screenCopy)
_windowManager->_screenCopy->copyFrom(*_vm->_macScreen);
_system->updateScreen();
}
}
void MacV6Gui::restoreScreen() {
if (--_screenSaveLevel == 0) {
_suspendPaletteUpdates = false;
Graphics::Surface *screen = _vm->_macScreen;
screen->copyFrom(*_backupScreen);
_system->copyRectToScreen(screen->getBasePtr(0, 0), screen->pitch, 0, 0, screen->w, screen->h);
_system->getPaletteManager()->setPalette(_backupPalette, 0, 256);
_backupScreen->free();
delete _backupScreen;
_backupScreen = nullptr;
delete[] _backupPalette;
_backupPalette = nullptr;
}
}
void MacV6Gui::onMenuOpen() {
MacGuiImpl::onMenuOpen();
saveScreen();
}
void MacV6Gui::onMenuClose() {
MacGuiImpl::onMenuClose();
restoreScreen();
}
void MacV6Gui::drawDottedFrame(MacDialogWindow *window, Common::Rect bounds, int x1, int x2) {
Graphics::Surface *s = window->innerSurface();
uint32 black = getBlack();
for (int x = bounds.left; x < bounds.right; x++) {
if (((x + bounds.bottom - 1) & 1) == 0)
s->setPixel(x, bounds.bottom - 1, black);
if ((x <= x1 || x >= x2) && ((x + bounds.top) & 1) == 0)
s->setPixel(x, bounds.top, black);
}
for (int y = bounds.top; y < bounds.bottom; y++) {
if (((bounds.left + y) & 1) == 0)
s->setPixel(bounds.left, y, black);
if (((bounds.right - 1 + y) & 1) == 0)
s->setPixel(bounds.right - 1, y, black);
}
}
MacGuiImpl::MacImageSlider *MacV6Gui::addSlider(MacDialogWindow *window, int x, int y, int width, int minValue, int maxValue, int primaryMarkings) {
int numMarkings = (maxValue - minValue) + 1;
Graphics::Surface *s = window->innerSurface();
uint32 gray = _windowManager->findBestColor(0xCD, 0xCD, 0xCD);
uint32 black = getBlack();
Common::Rect r(width, 12);
r.moveTo(x, y);
s->fillRect(r, gray);
s->frameRect(r, black);
int yt = y + 14;
int *positions = new int[numMarkings];
for (int i = 0; i < numMarkings; i++) {
int ht = ((i % primaryMarkings) == 0) ? 4 : 2;
int xt = x + (i * (width - 1)) / (numMarkings - 1);
s->vLine(xt, yt, yt + ht, black);
positions[i] = xt - x;
}
MacImage *handle = window->addIcon(x - 6, y - 4, 300, true);
MacImageSlider *slider = window->addImageSlider(Common::Rect(x - 6, y - 4, x + width + 7, y + 16), handle, true, 0, width - 1, minValue, maxValue);
for (int i = 0; i < numMarkings; i++)
slider->addStop(positions[i], minValue + i);
slider->setSnapWhileDragging(true);
slider->setValue(minValue);
delete[] positions;
return slider;
}
void MacV6Gui::runAboutDialog() {
// While there is a menu item for a Maniac Mansion credits screen, it
// doesn't do anything.
//
// "I was probably exhausted from hand-placing all the letters on the
// DOTT credits screen to make one for Maniac." -- Aaron Giles,
// December 30, 2024.
if (_vm->_game.id == GID_MANIAC)
return;
if (_vm->_game.features & GF_DEMO) {
// HACK: Use the largest bounds as default for unknown demos
// It would be nice if we could figure these out automatically
Common::Rect bounds(117, 5, 523, 384);
if (_vm->_game.id == GID_TENTACLE || _vm->_game.id == GID_SAMNMAX) {
bounds.left = 117;
bounds.top = 5;
bounds.right = 523;
bounds.bottom = 384;
#ifdef ENABLE_SCUMM_7_8
} else if (_vm->_game.id == GID_DIG || _vm->_game.id == GID_FT) {
bounds.left = 121;
bounds.top = 15;
bounds.right = 519;
bounds.bottom = 364;
#endif
}
MacDialogWindow *window = createDialog(136, bounds);
MacButton *buttonOk = (MacButton *)window->getWidget(kWidgetButton, 0);
window->setDefaultWidget(buttonOk);
while (!_vm->shouldQuit()) {
MacDialogEvent event;
while (window->runDialog(event)) {
switch (event.type) {
case kDialogClick:
if (event.widget == buttonOk) {
delete window;
return;
}
break;
default:
break;
}
}
window->delayAndUpdate();
}
delete window;
return;
}
ScummFile aboutFile(_vm);
if (!_vm->openFile(aboutFile, "ABOUT"))
return;
PauseToken token = _vm->pauseEngine();
const int aboutW = 480;
const int aboutH = 299;
Graphics::Surface *screen = _vm->_macScreen;
Common::Rect aboutArea(aboutW, aboutH);
const int aboutX = (screen->w - aboutW) / 2;
const int aboutY = (screen->h - aboutH) / 2;
aboutArea.moveTo(aboutX, aboutY);
Graphics::Surface aboutImage = screen->getSubArea(aboutArea);
uint black = 0;
Graphics::Palette palette(256);
for (uint i = 0; i < 256; i++) {
byte r = aboutFile.readByte();
byte g = aboutFile.readByte();
byte b = aboutFile.readByte();
if (r == 0 && g == 0 && b == 0)
black = i;
if (_vm->_useGammaCorrection) {
r = Graphics::macGammaCorrectionLookUp[r];
g = Graphics::macGammaCorrectionLookUp[g];
b = Graphics::macGammaCorrectionLookUp[b];
}
palette.set(i, r, g, b);
}
// The screen is already black, but what's black in the palette may
// have changed. Also, we want to clear the menu area.
screen->fillRect(Common::Rect(screen->w, screen->h), black);
for (int y = 0; y < aboutH; y++) {
for (int x = 0; x < aboutW; x++) {
byte *dst = (byte *)aboutImage.getBasePtr(x, y);
*dst = aboutFile.readByte();
}
}
aboutFile.close();
_system->copyRectToScreen(screen->getBasePtr(0, 0), screen->pitch, 0, 0, screen->w, screen->h);
_system->getPaletteManager()->setPalette(palette);
bool done = false;
while (!_vm->shouldQuit() && !done) {
Common::Event event;
while (_system->getEventManager()->pollEvent(event)) {
switch (event.type) {
case Common::EVENT_LBUTTONDOWN:
done = true;
break;
case Common::EVENT_KEYDOWN:
if (event.kbd.keycode == Common::KEYCODE_ESCAPE)
done = true;
break;
default:
break;
}
}
_system->delayMillis(10);
_system->updateScreen();
}
token.clear();
}
void MacV6Gui::updateThumbnail(MacDialogWindow *window, Common::Rect drawArea, int saveSlot) {
if (_vm->_game.id == GID_MANIAC)
return;
if (saveSlot < 0)
return;
SaveStateDescriptor desc = _vm->getMetaEngine()->querySaveMetaInfos(_vm->_targetName.c_str(), saveSlot);
const Graphics::Surface *thumbnail = desc.getThumbnail();
Common::HashMap<uint32, uint16> paletteMap;
int diff = thumbnail->h - drawArea.height();
int yMin = diff / 2;
int yMax = thumbnail->h - (diff / 2);
assert(drawArea.width() == thumbnail->w);
assert(drawArea.height() == yMax - yMin);
Common::Rect thumbnailRect(0, yMin, drawArea.width(), yMax);
// We don't know in advance how many colors the thumbnail is going to
// use. Reduce the image to a smaller palette. We reserve 10 colors for
// the Mac GUI itself.
Graphics::ColorQuantizer quantizer(245);
for (int y = yMin; y < yMax; y++) {
for (int x = 0; x < thumbnail->w; x++) {
uint32 color = thumbnail->getPixel(x, y);
byte r, g, b;
thumbnail->format.colorToRGB(color, r, g, b);
quantizer.addColor(r, g, b);
}
}
Graphics::Palette *palette = quantizer.getPalette();
_system->getPaletteManager()->setPalette(*palette);
delete palette;
Graphics::Palette fullPalette = _system->getPaletteManager()->grabPalette(0, 256);
Graphics::Surface *palettedThumbnail = thumbnail->convertTo(window->innerSurface()->format, nullptr, 0, fullPalette.data(), fullPalette.size());
window->innerSurface()->copyRectToSurface(*palettedThumbnail, drawArea.left, drawArea.top, thumbnailRect);
window->markRectAsDirty(drawArea);
palettedThumbnail->free();
delete palettedThumbnail;
}
bool MacV6Gui::runOpenDialog(int &saveSlotToHandle) {
// Widgets:
//
// 0 - Open button
// 1 - Cancel button
// 2 - Unknown item type 129
// 3 - User item
// 4 - Eject button
// 5 - Desktop button
// 6 - User item
// 7 - User item
// 8 - Picture (thumbnail?)
//
// Not in Maniac Mansion:
//
// 9 - User item
// 10 - "Where you were:" text
int dialogId = (_vm->_game.id == GID_MANIAC) ? 384 : 256;
MacDialogWindow *window = createDialog(dialogId);
MacButton *buttonSave = (MacButton *)window->getWidget(kWidgetButton, 0);
MacButton *buttonCancel = (MacButton *)window->getWidget(kWidgetButton, 1);
MacButton *buttonEject = (MacButton *)window->getWidget(kWidgetButton, 2);
MacButton *buttonDesktop = (MacButton *)window->getWidget(kWidgetButton, 3);
window->setDefaultWidget(buttonSave);
buttonEject->setEnabled(false);
buttonDesktop->setEnabled(false);
bool availSlots[100];
int slotIds[100];
Common::StringArray savegameNames;
prepareSaveLoad(savegameNames, availSlots, slotIds, ARRAYSIZE(availSlots));
Common::Rect thumbnailRect(12, 32, 172, 132);
MacListBox *listBox;
if (_vm->_game.id == GID_MANIAC) {
listBox = window->addListBox(Common::Rect(10, 31, 228, 161), savegameNames, true);
drawFakePathList(window, Common::Rect(10, 8, 228, 27), _gameName.c_str());
drawFakeDriveLabel(window, Common::Rect(238, 10, 335, 26), "ScummVM");
} else {
listBox = window->addListBox(Common::Rect(184, 31, 402, 161), savegameNames, true);
drawFakePathList(window, Common::Rect(184, 8, 402, 27), _gameName.c_str());
drawFakeDriveLabel(window, Common::Rect(412, 10, 509, 26), "ScummVM");
window->innerSurface()->frameRect(Common::Rect(thumbnailRect.left - 1, thumbnailRect.top - 1, thumbnailRect.right + 1, thumbnailRect.bottom + 1), getBlack());
}
int saveSlot = listBox->getValue() < ARRAYSIZE(slotIds) ? slotIds[listBox->getValue()] : -1;
updateThumbnail(window, thumbnailRect, saveSlot);
while (!_vm->shouldQuit()) {
MacDialogEvent event;
while (window->runDialog(event)) {
switch (event.type) {
case kDialogClick:
if (event.widget == buttonSave || event.widget == listBox) {
saveSlotToHandle =
listBox->getValue() < ARRAYSIZE(slotIds) ? slotIds[listBox->getValue()] : -1;
delete window;
return true;
} else if (event.widget == buttonCancel) {
delete window;
return false;
}
break;
case kDialogValueChange:
if (event.widget == listBox) {
saveSlot = listBox->getValue() < ARRAYSIZE(slotIds) ? slotIds[listBox->getValue()] : -1;
updateThumbnail(window, thumbnailRect, saveSlot);
}
break;
default:
break;
}
}
window->delayAndUpdate();
}
// When quitting, do not load the saved game
delete window;
return false;
}
bool MacV6Gui::runSaveDialog(int &saveSlotToHandle, Common::String &saveName) {
// Widgets:
//
// 0 - Save button
// 1 - Cancel button
// 2 - Unknown item type 129
// 3 - User item
// 4 - Eject button
// 5 - Desktop button
// 6 - User item
// 7 - User item
// 8 - Picture
// 9 - "Save as:" text
// 10 - User item
int dialogId = (_vm->_game.id == GID_MANIAC) ? 386 : 258;
MacDialogWindow *window = createDialog(dialogId);
MacButton *buttonSave = (MacButton *)window->getWidget(kWidgetButton, 0);
MacButton *buttonCancel = (MacButton *)window->getWidget(kWidgetButton, 1);
MacButton *buttonEject = (MacButton *)window->getWidget(kWidgetButton, 2);
MacButton *buttonDrive = (MacButton *)window->getWidget(kWidgetButton, 3);
MacEditText *editText = (MacEditText *)window->getWidget(kWidgetEditText);
window->setDefaultWidget(buttonSave);
buttonEject->setEnabled(false);
buttonDrive->setEnabled(false);
bool busySlots[100];
int slotIds[100];
Common::StringArray savegameNames;
prepareSaveLoad(savegameNames, busySlots, slotIds, ARRAYSIZE(busySlots));
drawFakePathList(window, Common::Rect(14, 8, 232, 27), _gameName.c_str());
drawFakeDriveLabel(window, Common::Rect(242, 10, 339, 26), "ScummVM");
int firstAvailableSlot = -1;
for (int i = 0; i < ARRAYSIZE(busySlots); i++) {
if (!busySlots[i]) {
firstAvailableSlot = i;
break;
}
}
window->addListBox(Common::Rect(14, 31, 232, 129), savegameNames, true, true);
while (!_vm->shouldQuit()) {
MacDialogEvent event;
while (window->runDialog(event)) {
switch (event.type) {
case kDialogClick:
if (event.widget == buttonSave) {
saveName = editText->getText();
saveSlotToHandle = firstAvailableSlot;
delete window;
return true;
} else if (event.widget == buttonCancel) {
delete window;
return false;
}
break;
case kDialogValueChange:
if (event.widget == editText) {
buttonSave->setEnabled(!editText->getText().empty());
}
break;
default:
break;
}
}
window->delayAndUpdate();
}
// When quitting, do not save the game
delete window;
return false;
}
void MacV6Gui::setVolume(int type, int volume) {
const char *keys[] = {
"music_volume",
"sfx_volume",
"speech_volume"
};
const Audio::Mixer::SoundType soundTypes[] = {
Audio::Mixer::kMusicSoundType,
Audio::Mixer::kSFXSoundType,
Audio::Mixer::kSpeechSoundType
};
// Input volume comes from the options dialog slider. It's 0-16.
// The regular mixer uses 0-256 while Digital iMUSE uses 0-127.
int mixerVolume = CLIP(16 * volume, 0, 256);
#ifdef ENABLE_SCUMM_7_8
if (_vm->_game.version >= 7) {
int dimuseVolume = CLIP(8 * volume, 0, 127);
switch (type) {
case 0:
_vm->_imuseDigital->diMUSESetMusicGroupVol(dimuseVolume);
break;
case 1:
_vm->_imuseDigital->diMUSESetSFXGroupVol(dimuseVolume);
break;
case 2:
_vm->_imuseDigital->diMUSESetVoiceGroupVol(dimuseVolume);
break;
}
} else
#endif
{
_vm->_mixer->setVolumeForSoundType(soundTypes[type], mixerVolume);
}
ConfMan.setInt(keys[type], mixerVolume);
}
bool MacV6Gui::runOptionsDialog() {
// There are too many different variations to list all widgets here.
// The important thing that they share are that the first three buttons
// are OK, Cancel, and Defaults, and that with the exception of Maniac
// Mansion they expect the first text to contain the name of the game
// as "^3".
int dialogId = (_vm->_game.id == GID_MANIAC) ? 385 : 257;
MacDialogWindow *window = createDialog(dialogId);
MacButton *buttonOk = (MacButton *)window->getWidget(kWidgetButton, 0);
MacButton *buttonCancel = (MacButton *)window->getWidget(kWidgetButton, 1);
MacButton *buttonDefaults = (MacButton *)window->getWidget(kWidgetButton, 2);
MacImageSlider *sliderMusicVolume = nullptr;
MacImageSlider *sliderEffectVolume = nullptr;
MacImageSlider *sliderVoiceVolume = nullptr;
MacImageSlider *sliderTextSpeed = nullptr;
#ifdef ENABLE_SCUMM_7_8
MacCheckbox *checkboxSpoolMusic = nullptr;
#endif
MacPopUpMenu *popUpInteraction = nullptr;
MacPopUpMenu *popUpVideoQuality = nullptr;
if (_vm->_game.id != GID_MANIAC) {
window->addSubstitution("");
window->addSubstitution("");
window->addSubstitution("");
window->addSubstitution(_gameName);
popUpInteraction = (MacPopUpMenu *)window->getWidget(kWidgetPopUpMenu, 0);
popUpVideoQuality = (MacPopUpMenu *)window->getWidget(kWidgetPopUpMenu, 1);
} else {
popUpVideoQuality = (MacPopUpMenu *)window->getWidget(kWidgetPopUpMenu, 0);
}
// Note: The video quality menu contains an additional "Graphics
// Smoothing" entry. I don't know why it doesn't show up in the
// original, but as long as we disabled the pop-up that's not a
// problem. My future self can thank me later.
popUpVideoQuality->setValue(1);
popUpVideoQuality->setEnabled(false);
window->setDefaultWidget(buttonOk);
if (_vm->_game.id == GID_TENTACLE || _vm->_game.id == GID_INDY4) {
// Yes, the frames really are supposed to be slightly
// misaligned to match the original appearance.
drawDottedFrame(window, Common::Rect(12, 41, 337, 113), 21, 137);
drawDottedFrame(window, Common::Rect(11, 130, 336, 203), 20, 168);
sliderMusicVolume = addSlider(window, 152, 63, 147, 0, 16);
sliderVoiceVolume = addSlider(window, 152, 87, 147, 0, 16);
sliderTextSpeed = addSlider(window, 151, 177, 147, 1, 9);
} else if (_vm->_game.id == GID_MANIAC) {
sliderMusicVolume = addSlider(window, 152, 41, 147, 0, 16);
sliderTextSpeed = addSlider(window, 152, 72, 147, 0, 9, 5);
} else if (_vm->_game.id == GID_SAMNMAX || _vm->_game.id == GID_DIG) {
drawDottedFrame(window, Common::Rect(12, 41, 337, 136), 21, 137);
drawDottedFrame(window, Common::Rect(12, 156, 337, 229), 20, 168);
sliderMusicVolume = addSlider(window, 152, 63, 147, 0, 16);
sliderEffectVolume = addSlider(window, 152, 87, 147, 0, 16);
sliderVoiceVolume = addSlider(window, 152, 111, 147, 0, 16);
sliderTextSpeed = addSlider(window, 152, 203, 147, 1, 9);
#ifdef ENABLE_SCUMM_7_8
} else if (_vm->_game.id == GID_FT) {
drawDottedFrame(window, Common::Rect(12, 41, 337, 164), 21, 137);
drawDottedFrame(window, Common::Rect(12, 184, 337, 257), 20, 168);
sliderMusicVolume = addSlider(window, 152, 63, 147, 0, 16);
sliderEffectVolume = addSlider(window, 152, 87, 147, 0, 16);
sliderVoiceVolume = addSlider(window, 152, 111, 147, 0, 16);
sliderTextSpeed = addSlider(window, 152, 231, 147, 1, 9);
checkboxSpoolMusic = (MacCheckbox *)window->getWidget(kWidgetCheckbox, 0);
#endif
} else
error("MacV6Gui::runOptionsDialog: Unknown game");
if (popUpInteraction) {
switch (_vm->_voiceMode) {
case 0: // Voice Only
popUpInteraction->setValue(1);
break;
case 1: // Voice And Text
popUpInteraction->setValue(2);
break;
case 2: // Text Only
popUpInteraction->setValue(0);
break;
default:
warning("MacGuiImpl::MacV6Gui::runOptionsDialog(): Invalid voice mode %d", _vm->_voiceMode);
break;
}
}
int musicVolume = 0;
int effectVolume = 0;
int voiceVolume = 0;
// There is always a music volume slider. The rest are not guaranteed.
// If there is a voice volume slider but no effect volume slider, the
// voice volume slider cover both.
musicVolume = ConfMan.getInt("music_volume") / 16;
sliderMusicVolume->setValue(musicVolume);
if (sliderEffectVolume) {
effectVolume = ConfMan.getInt("sfx_volume") / 16;
sliderEffectVolume->setValue(effectVolume);
}
if (sliderVoiceVolume) {
voiceVolume = ConfMan.getInt("speech_volume") / 16;
sliderVoiceVolume->setValue(voiceVolume);
}
#ifdef ENABLE_SCUMM_7_8
if (checkboxSpoolMusic)
checkboxSpoolMusic->setValue(_vm->_spooledMusicIsToBeEnabled);
#endif
if (sliderTextSpeed)
sliderTextSpeed->setValue(_vm->_defaultTextSpeed);
while (!_vm->shouldQuit()) {
MacDialogEvent event;
while (window->runDialog(event)) {
switch (event.type) {
case kDialogClick:
if (event.widget == buttonOk) {
musicVolume = sliderMusicVolume->getValue();
if (_vm->_game.id == GID_MANIAC) {
effectVolume = musicVolume;
} else if (_vm->_game.id == GID_TENTACLE || _vm->_game.id == GID_INDY4) {
musicVolume = sliderMusicVolume->getValue();
voiceVolume = sliderVoiceVolume->getValue();
effectVolume = voiceVolume;
} else {
musicVolume = sliderMusicVolume->getValue();
effectVolume = sliderEffectVolume->getValue();
voiceVolume = sliderVoiceVolume->getValue();
}
setVolume(0, musicVolume);
setVolume(1, effectVolume);
if (sliderVoiceVolume) {
voiceVolume = sliderVoiceVolume->getValue();
setVolume(2, voiceVolume);
}
// The DOS version uses 0-9. Apparently the Mac version
// uses 1-9 instead, except for Maniac Mansion.
_vm->_defaultTextSpeed = sliderTextSpeed->getValue();
ConfMan.setInt("original_gui_text_speed", _vm->_defaultTextSpeed);
_vm->setTalkSpeed(_vm->_defaultTextSpeed);
#ifdef ENABLE_SCUMM_7_8
if (checkboxSpoolMusic) {
bool spoolMusic = (checkboxSpoolMusic->getValue() != 0);
_vm->_spooledMusicIsToBeEnabled = spoolMusic;
if (_vm->_imuseDigital) {
if (spoolMusic) {
_vm->_imuseDigital->diMUSEEnableSpooledMusic();
} else {
_vm->_imuseDigital->diMUSEDisableSpooledMusic();
}
}
}
#endif
if (popUpInteraction) {
switch (popUpInteraction->getValue()) {
case 0: // Text Only
ConfMan.setBool("subtitles", true);
ConfMan.setBool("speech_mute", true);
break;
case 1: // Voice Only
ConfMan.setBool("subtitles", false);
ConfMan.setBool("speech_mute", false);
break;
case 2: // Text & Voice
ConfMan.setBool("subtitles", true);
ConfMan.setBool("speech_mute", false);
break;
}
}
ConfMan.flushToDisk();
_vm->syncSoundSettings();
delete window;
return true;
} else if (event.widget == buttonCancel) {
delete window;
return false;
} else if (event.widget == buttonDefaults) {
if (sliderMusicVolume)
sliderMusicVolume->setValue(16);
if (sliderEffectVolume)
sliderEffectVolume->setValue(12);
if (sliderVoiceVolume)
sliderVoiceVolume->setValue(16);
if (popUpInteraction)
popUpInteraction->setValue(1);
if (sliderTextSpeed)
sliderTextSpeed->setValue((_vm->_game.id == GID_MANIAC) ? 4 : 5);
#ifdef ENABLE_SCUMM_7_8
if (checkboxSpoolMusic)
checkboxSpoolMusic->setValue(1);
#endif
}
break;
default:
break;
}
}
window->delayAndUpdate();
}
delete window;
return false;
}
bool MacV6Gui::runQuitDialog() {
int dialogId = (_vm->_game.id == GID_MANIAC) ? 192 : 128;
MacDialogWindow *window = createDialog(dialogId);
MacButton *buttonOk = (MacButton *)window->getWidget(kWidgetButton, 0);
MacButton *buttonCancel = (MacButton *)window->getWidget(kWidgetButton, 1);
window->setDefaultWidget(buttonOk);
while (!_vm->shouldQuit()) {
MacDialogEvent event;
while (window->runDialog(event)) {
switch (event.type) {
case kDialogClick:
if (event.widget == buttonOk) {
delete window;
return true;
} else if (event.widget == buttonCancel) {
delete window;
return false;
}
break;
default:
break;
}
}
window->delayAndUpdate();
}
// When quitting, quit
delete window;
return true;
}
bool MacV6Gui::runRestartDialog() {
int dialogId = (_vm->_game.id == GID_MANIAC) ? 193 : 137;
MacDialogWindow *window = createDialog(dialogId);
// If there is no dialog, just restart without asking.
if (!window)
return true;
MacButton *buttonOk = (MacButton *)window->getWidget(kWidgetButton, 0);
MacButton *buttonCancel = (MacButton *)window->getWidget(kWidgetButton, 1);
window->setDefaultWidget(buttonOk);
window->addSubstitution("");
window->addSubstitution("");
window->addSubstitution("");
window->addSubstitution(_gameName);
while (!_vm->shouldQuit()) {
MacDialogEvent event;
while (window->runDialog(event)) {
switch (event.type) {
case kDialogClick:
if (event.widget == buttonOk) {
delete window;
return true;
} else if (event.widget == buttonCancel) {
delete window;
return false;
}
break;
default:
break;
}
}
window->delayAndUpdate();
}
// When quitting, do not restart
delete window;
return false;
}
void MacV6Gui::resetAfterLoad() {
reset();
}
void MacV6Gui::updateWindowManager() {
MacGuiImpl::updateWindowManager();
if (_skipScene && _screenSaveLevel == 0) {
_skipScene = false;
_vm->processKeyboard(Common::KEYCODE_ESCAPE);
}
}
} // End of namespace Scumm