[Scummvm-git-logs] scummvm master -> eea428cbc350501c70ded31081721cb722440585

sev- noreply at scummvm.org
Mon Aug 7 08:22:16 UTC 2023


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

Summary:
5e6c5ca07b MTROPOLIS: Fix project platform returning the wrong value
3777a3bf9a GUI: Force Unix newlines on Windows for default.inc
3c1efe4f89 COMMON: Add file format info API
7bacf7b430 GUI: Add image album dialog for viewing images exported by an engine, mainly for supporting printing features
6f005a75a4 TESTBED: Add tests for image album
46a0aca354 MTROPOLIS: Hook up MPZ-1000 to image album
58271a834c COMMON: Remove #pragma once
9598e343a3 GUI: Add imagealbum-dialog.cpp to POTFILES
eea428cbc3 GUI: Update theme binaries


Commit: 5e6c5ca07b245d428c2d9dcdde7359ab2692b534
    https://github.com/scummvm/scummvm/commit/5e6c5ca07b245d428c2d9dcdde7359ab2692b534
Author: elasota (ejlasota at gmail.com)
Date: 2023-08-07T10:22:09+02:00

Commit Message:
MTROPOLIS: Fix project platform returning the wrong value

Changed paths:
    engines/mtropolis/modifiers.cpp
    engines/mtropolis/runtime.cpp
    engines/mtropolis/runtime.h


diff --git a/engines/mtropolis/modifiers.cpp b/engines/mtropolis/modifiers.cpp
index c9304542daf..b8f95e27e08 100644
--- a/engines/mtropolis/modifiers.cpp
+++ b/engines/mtropolis/modifiers.cpp
@@ -2339,11 +2339,11 @@ bool KeyboardMessengerModifier::checkKeyEventTrigger(Runtime *runtime, Common::E
 		return false;
 
 	if (_keyModCommand) {
-		if (runtime->getPlatform() == kProjectPlatformWindows) {
+		if (runtime->getProject()->getPlatform() == kProjectPlatformWindows) {
 			// Windows projects check "alt"
 			if ((keyEvt.flags & Common::KBD_ALT) == 0)
 				return false;
-		} else if (runtime->getPlatform() == kProjectPlatformMacintosh) {
+		} else if (runtime->getProject()->getPlatform() == kProjectPlatformMacintosh) {
 			if ((keyEvt.flags & Common::KBD_META) == 0)
 				return false;
 		}
@@ -2377,7 +2377,7 @@ bool KeyboardMessengerModifier::checkKeyEventTrigger(Runtime *runtime, Common::E
 		break;
 	case Common::KEYCODE_F1:
 		// Windows projects map F1 to "help"
-		if (runtime->getPlatform() == kProjectPlatformWindows)
+		if (runtime->getProject()->getPlatform() == kProjectPlatformWindows)
 			resolvedType = kHelp;
 		break;
 	case Common::KEYCODE_BACKSPACE:
diff --git a/engines/mtropolis/runtime.cpp b/engines/mtropolis/runtime.cpp
index aaf7aa3cd29..6c6dfb4a53a 100644
--- a/engines/mtropolis/runtime.cpp
+++ b/engines/mtropolis/runtime.cpp
@@ -4255,7 +4255,7 @@ Runtime::Runtime(OSystem *system, Audio::Mixer *mixer, ISaveUIProvider *saveProv
 	: _system(system), _mixer(mixer), _saveProvider(saveProvider), _loadProvider(loadProvider),
 	  _nextRuntimeGUID(1), _realDisplayMode(kColorDepthModeInvalid), _fakeDisplayMode(kColorDepthModeInvalid),
 	  _displayWidth(1024), _displayHeight(768), _realTime(0), _realTimeBase(0), _playTime(0), _playTimeBase(0), _sceneTransitionState(kSceneTransitionStateNotTransitioning),
-	  _lastFrameCursor(nullptr), _lastFrameMouseVisible(false), _defaultCursor(new DefaultCursorGraphic()), _platform(kProjectPlatformUnknown),
+	  _lastFrameCursor(nullptr), _lastFrameMouseVisible(false), _defaultCursor(new DefaultCursorGraphic()),
 	  _cachedMousePosition(Common::Point(0, 0)), _realMousePosition(Common::Point(0, 0)), _trackedMouseOutside(false),
 	  _forceCursorRefreshOnce(true), _autoResetCursor(false), _haveModifierOverrideCursor(false), _haveCursorElement(false), _sceneGraphChanged(false), _isQuitting(false),
 	  _collisionCheckTime(0), /*_elementCursorUpdateTime(0), */_defaultVolumeState(true), _activeSceneTransitionEffect(nullptr), _sceneTransitionStartTime(0), _sceneTransitionEndTime(0),
@@ -5938,10 +5938,6 @@ void Runtime::setVolume(double volume) {
 	mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, static_cast<int>(volume * Audio::Mixer::kMaxMixerVolume));
 }
 
-ProjectPlatform Runtime::getPlatform() const {
-	return _platform;
-}
-
 void Runtime::onMouseDown(int32 x, int32 y, Actions::MouseButton mButton) {
 	_realMousePosition.x = x;
 	_realMousePosition.y = y;
@@ -6927,7 +6923,8 @@ Project::AssetDesc::AssetDesc() : typeCode(0), id(0), streamID(0), filePosition(
 Project::Project(Runtime *runtime)
 	: Structural(runtime), _projectFormat(Data::kProjectFormatUnknown), _isBigEndian(false),
 	  _haveGlobalObjectInfo(false), _haveProjectStructuralDef(false), _playMediaSignaller(new PlayMediaSignaller()),
-	  _keyboardEventSignaller(new KeyboardEventSignaller()), _guessedVersion(MTropolisVersions::kMTropolisVersion1_0) {
+	  _keyboardEventSignaller(new KeyboardEventSignaller()), _guessedVersion(MTropolisVersions::kMTropolisVersion1_0),
+	  _platform(kProjectPlatformUnknown) {
 }
 
 Project::~Project() {
@@ -6957,6 +6954,7 @@ void Project::loadFromDescription(const ProjectDescription &desc, const Hacks &h
 	_resources = desc.getResources();
 	_cursorGraphics = desc.getCursorGraphics();
 	_subtitles = desc.getSubtitles();
+	_platform = desc.getPlatform();
 
 	debug(1, "Loading new project...");
 
@@ -7451,6 +7449,10 @@ MTropolisVersions::MTropolisVersion Project::guessVersion() const {
 	return _guessedVersion;
 }
 
+ProjectPlatform Project::getPlatform() const {
+	return _platform;
+}
+
 void Project::loadPresentationSettings(const Data::PresentationSettings &presentationSettings) {
 	_presentationSettings.bitsPerPixel = presentationSettings.bitsPerPixel;
 	if (_presentationSettings.bitsPerPixel != 8 && _presentationSettings.bitsPerPixel != 16) {
diff --git a/engines/mtropolis/runtime.h b/engines/mtropolis/runtime.h
index 166b30e1b03..9c5c7e1b650 100644
--- a/engines/mtropolis/runtime.h
+++ b/engines/mtropolis/runtime.h
@@ -1651,8 +1651,6 @@ public:
 
 	void setVolume(double volume);
 
-	ProjectPlatform getPlatform() const;
-
 	void onMouseDown(int32 x, int32 y, Actions::MouseButton mButton);
 	void onMouseMove(int32 x, int32 y);
 	void onMouseUp(int32 x, int32 y, Actions::MouseButton mButton);
@@ -1930,8 +1928,6 @@ private:
 
 	Common::WeakPtr<Window> _keyFocusWindow;
 
-	ProjectPlatform _platform;
-
 	Common::SharedPtr<SystemInterface> _systemInterface;
 	Common::SharedPtr<WorldManagerInterface> _worldManagerInterface;
 	Common::SharedPtr<AssetManagerInterface> _assetManagerInterface;
@@ -2438,6 +2434,7 @@ public:
 	const SubtitleTables &getSubtitles() const;
 
 	MTropolisVersions::MTropolisVersion guessVersion() const;
+	ProjectPlatform getPlatform() const;
 
 #ifdef MTROPOLIS_DEBUG_ENABLE
 	const char *debugGetTypeName() const override { return "Project"; }
@@ -2553,6 +2550,7 @@ private:
 	SubtitleTables _subtitles;
 
 	MTropolisVersions::MTropolisVersion _guessedVersion;
+	ProjectPlatform _platform;
 };
 
 class Section : public Structural {


Commit: 3777a3bf9a1066a32301267771557baae04412ef
    https://github.com/scummvm/scummvm/commit/3777a3bf9a1066a32301267771557baae04412ef
Author: elasota (ejlasota at gmail.com)
Date: 2023-08-07T10:22:09+02:00

Commit Message:
GUI: Force Unix newlines on Windows for default.inc

Changed paths:
    gui/themes/scummtheme.py


diff --git a/gui/themes/scummtheme.py b/gui/themes/scummtheme.py
index a6fe93f49b1..2f8322b2141 100755
--- a/gui/themes/scummtheme.py
+++ b/gui/themes/scummtheme.py
@@ -85,7 +85,7 @@ def parseSTX(theme_file, def_file, subcount):
 	return subcount
 
 def buildDefTheme(themeName):
-	def_file = open("default.inc", "w")
+	def_file = open("default.inc", mode="w", newline="\n")
 
 	if not os.path.isdir(themeName):
 		print ("Cannot open default theme dir.")


Commit: 3c1efe4f895437cb07754b48251fbe8990280597
    https://github.com/scummvm/scummvm/commit/3c1efe4f895437cb07754b48251fbe8990280597
Author: elasota (ejlasota at gmail.com)
Date: 2023-08-07T10:22:09+02:00

Commit Message:
COMMON: Add file format info API

Changed paths:
  A common/formats/formatinfo.cpp
  A common/formats/formatinfo.h
    common/formats/module.mk
    po/POTFILES


diff --git a/common/formats/formatinfo.cpp b/common/formats/formatinfo.cpp
new file mode 100644
index 00000000000..ff8e6dcc6a4
--- /dev/null
+++ b/common/formats/formatinfo.cpp
@@ -0,0 +1,152 @@
+/* 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/formats/formatinfo.h"
+
+#include "common/translation.h"
+#include "common/util.h"
+
+#include "image/bmp.h"
+#include "image/png.h"
+
+namespace Common {
+
+namespace FormatInfo {
+
+struct FormatTypeInfo {
+	const char *_shortExtension;	///< Extension limited to 3 characters
+	const char *_longExtension;     ///< Extension with no length limit
+};
+
+const FormatTypeInfo g_formatTypeInfo[kCount] = {
+	{nullptr},
+
+	// Image formats
+	{"bmp"},
+	{"png"},
+	{"jpg"},
+	{"pct", "pict"},
+
+	// Binary formats
+	{"application/octet-stream", nullptr},
+};
+
+ImageFormatCharacteristics::ImageFormatCharacteristics()
+	: _lossiness(kLossinessUnknown), _supportAlpha(false), _supportPalette(false), _maxBitDepth(0) {
+}
+
+ImageSaveProperties::ImageSaveProperties() : _qualityLevel(75) {
+}
+
+bool getImageFormatCharacteristics(FormatID format, ImageFormatCharacteristics &outCharacteristics) {
+	outCharacteristics = ImageFormatCharacteristics();
+
+	switch (format) {
+	case kPICT:
+		outCharacteristics._supportPalette = true;
+		outCharacteristics._maxBitDepth = 24;
+		outCharacteristics._lossiness = kLossinessEither;
+		break;
+	case kBMP:
+		outCharacteristics._supportPalette = true;
+		outCharacteristics._maxBitDepth = 24;
+		outCharacteristics._lossiness = kLossinessLossless;
+		break;
+	case kPNG:
+		outCharacteristics._supportPalette = true;
+		outCharacteristics._supportAlpha = true;
+		outCharacteristics._maxBitDepth = 32;
+		outCharacteristics._lossiness = kLossinessLossless;
+		break;
+	case kJPEG:
+		outCharacteristics._maxBitDepth = 24;
+		outCharacteristics._lossiness = kLossinessLossy;
+		break;
+	default:
+		return false;
+	}
+
+	return true;
+}
+
+const char *getFormatExtension(Common::FormatInfo::FormatID format, bool limitTo3Characters) {
+	const FormatTypeInfo &typeInfo = g_formatTypeInfo[format];
+
+	if (!limitTo3Characters && typeInfo._longExtension != nullptr)
+		return typeInfo._longExtension;
+
+	return typeInfo._shortExtension;
+}
+
+Common::U32String getFormatSaveDescription(Common::FormatInfo::FormatID format) {
+	switch (format) {
+	case kBMP:
+		return _("Bitmap Image File");
+	case kPNG:
+		return _("PNG Image File");
+	case kJPEG:
+		return _("JPEG Image File");
+	case kPICT:
+		return _("QuickDraw PICT Image File");
+	case kUntypedBinary:
+	default:
+		return _("Data File");
+	}
+}
+
+#ifdef USE_PNG
+static bool savePNG(Common::WriteStream &stream, const Graphics::Surface &surf, const byte *palette, const ImageSaveProperties &props) {
+	return Image::writePNG(stream, surf, palette);
+}
+#endif
+
+static bool saveBMP(Common::WriteStream &stream, const Graphics::Surface &surf, const byte *palette, const ImageSaveProperties &props) {
+	return Image::writeBMP(stream, surf, palette);
+}
+
+ImageSaveCallback_t getImageSaveFunction(FormatID format) {
+	switch (format) {
+#ifdef USE_PNG
+	case kPNG:
+		return savePNG;
+#endif
+	case kBMP:
+		return saveBMP;
+	default:
+		return nullptr;
+	}
+}
+
+FormatSupportLevel getFormatSupportLevel(Common::FormatInfo::FormatID format) {
+	switch (format) {
+	case kPICT:
+		return kFormatSupportLevelAvoid;
+	case kPNG:
+	case kJPEG:
+		return kFormatSupportLevelPreferred;
+	default:
+		return kFormatSupportLevelDefault;
+	}
+}
+
+} // End of namespace FormatInfo
+
+} // End of namespace Common
diff --git a/common/formats/formatinfo.h b/common/formats/formatinfo.h
new file mode 100644
index 00000000000..05d6769155f
--- /dev/null
+++ b/common/formats/formatinfo.h
@@ -0,0 +1,120 @@
+#pragma once
+/* 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 COMMON_FORMATS_FORMATINFO_H
+#define COMMON_FORMATS_FORMATINFO_H
+
+#include "common/str.h"
+#include "image/bmp.h"
+
+namespace Graphics {
+
+struct Surface;
+
+} // End of namespace Graphics
+
+namespace Common {
+
+namespace FormatInfo {
+
+enum Lossiness {
+	kLossinessUnknown,
+
+	kLossinessLossless,
+	kLossinessLossy,
+	kLossinessEither,
+};
+
+
+/**
+ * Values representing support for saving or sharing file types on the system.
+ */
+enum FormatSupportLevel {
+	kFormatSupportLevelNone,      ///< Not supported at all
+	kFormatSupportLevelAvoid,     ///< Can be saved/loaded but most likely unsupported even by third-party tools
+	kFormatSupportLevelDefault,   ///< Not natively supported by the operating system, support by third-party tools is either available or unknown
+	kFormatSupportLevelSupported, ///< Well-supported by the operating system
+	kFormatSupportLevelPreferred, ///< Supported by operating system, preferred over other formats if lossless conversion is possible
+};
+
+enum FormatID {
+	kFormatUnknown,
+
+	// Image formats
+	kBMP,	kFirstImageFormat = kBMP,
+	kPNG,
+	kJPEG,
+	kPICT,	// Macintosh PICT
+
+	// Binary formats
+	kUntypedBinary,	kFirstBinaryFormat = kUntypedBinary,
+
+	// End of list
+	kCount,
+
+	kLastImageFormat = kFirstBinaryFormat - 1,
+	kLastBinaryFormat = kCount - 1,
+};
+
+struct ImageFormatCharacteristics {
+	ImageFormatCharacteristics();
+
+	Lossiness _lossiness;
+	bool _supportAlpha;
+	bool _supportPalette;
+	uint _maxBitDepth;
+};
+
+struct ImageSaveProperties {
+	ImageSaveProperties();
+
+	uint8 _qualityLevel;
+};
+
+typedef bool (*ImageSaveCallback_t)(Common::WriteStream &stream, const Graphics::Surface &surf, const byte *palette, const ImageSaveProperties &saveProperties);
+
+/**
+ * Returns the default file extension for a format
+ *
+ * @param format              The file format
+ * @param limitTo3Characters  If true, limit the extension to 3 characters
+ * @return 
+*/
+const char *getFormatExtension(Common::FormatInfo::FormatID format, bool limitTo3Characters);
+
+Common::U32String getFormatSaveDescription(Common::FormatInfo::FormatID format);
+
+ImageSaveCallback_t getImageSaveFunction(FormatID format);
+
+
+bool getImageFormatCharacteristics(FormatID format, ImageFormatCharacteristics &outCharacteristics);
+
+/**
+ * Returns the OS's level of support of a file format.
+ */
+FormatSupportLevel getFormatSupportLevel(Common::FormatInfo::FormatID format);
+
+} // End of namespace FormatInfo
+
+} // End of namespace Common
+
+#endif
diff --git a/common/formats/module.mk b/common/formats/module.mk
index 566fffbebcc..9537f8def77 100644
--- a/common/formats/module.mk
+++ b/common/formats/module.mk
@@ -1,6 +1,7 @@
 MODULE := common/formats
 
 MODULE_OBJS := \
+	formatinfo.o \
 	iff_container.o \
 	ini-file.o \
 	json.o \
diff --git a/po/POTFILES b/po/POTFILES
index 0ede7f511b4..f1cafc86a24 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -37,6 +37,7 @@ common/rendermode.cpp
 common/translation.cpp
 common/updates.cpp
 common/util.cpp
+common/formats/formatinfo.cpp
 
 dists/android.strings.xml.cpp
 dists/org.scummvm.scummvm.metainfo.xml.cpp


Commit: 7bacf7b4306ecd41a806474c72d16ef82befaf42
    https://github.com/scummvm/scummvm/commit/7bacf7b4306ecd41a806474c72d16ef82befaf42
Author: elasota (ejlasota at gmail.com)
Date: 2023-08-07T10:22:09+02:00

Commit Message:
GUI: Add image album dialog for viewing images exported by an engine, mainly for supporting printing features

Changed paths:
  A gui/imagealbum-dialog.cpp
  A gui/imagealbum-dialog.h
    gui/module.mk
    gui/themes/common/highres_layout.stx
    gui/themes/common/lowres_layout.stx
    gui/themes/default.inc
    gui/themes/scummclassic/classic_layout.stx
    gui/themes/scummclassic/classic_layout_lowres.stx


diff --git a/gui/imagealbum-dialog.cpp b/gui/imagealbum-dialog.cpp
new file mode 100644
index 00000000000..c7d392cbd4b
--- /dev/null
+++ b/gui/imagealbum-dialog.cpp
@@ -0,0 +1,472 @@
+/* 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 "gui/imagealbum-dialog.h"
+
+#include "gui/dialog.h"
+#include "gui/filebrowser-dialog.h"
+#include "gui/gui-manager.h"
+#include "gui/ThemeEval.h"
+#include "gui/widget.h"
+
+#include "gui/widgets/scrollcontainer.h"
+
+#include "image/bmp.h"
+#include "image/png.h"
+
+#include "common/dialogs.h"
+#include "common/savefile.h"
+#include "common/stream.h"
+#include "common/translation.h"
+
+namespace GUI {
+
+ImageAlbumImageSupplier::~ImageAlbumImageSupplier() {
+}
+
+class ImageAlbumDialog : public Dialog {
+public:
+	ImageAlbumDialog(const Common::U32String &title, ImageAlbumImageSupplier *imageSupplier, uint initialSlot);
+	~ImageAlbumDialog();
+
+	void open() override;
+	void close() override;
+
+	void handleCommand(CommandSender *sender, uint32 cmd, uint32 data) override;
+
+private:
+	ImageAlbumDialog() = delete;
+	ImageAlbumDialog(const ImageAlbumDialog &) = delete;
+
+	void changeToSlot(uint slot);
+	void saveImageInSlot(uint slot);
+
+	ImageAlbumImageSupplier *_imageSupplier;
+	uint _currentSlot;
+	uint _numSlots;
+
+	ButtonWidget *_prevButton;
+	ButtonWidget *_nextButton;
+	ButtonWidget *_saveButton;
+
+	StaticTextWidget *_imageNumberLabel;
+
+	ContainerWidget *_imageContainer;
+	GraphicsWidget *_imageGraphic;
+
+	bool _canAlwaysSaveImage;
+
+	enum {
+		kPrevCmd = 'PREV',
+		kNextCmd = 'NEXT',
+		kSaveCmd = 'SAVE',
+	};
+};
+
+
+ImageAlbumDialog::ImageAlbumDialog(const Common::U32String &title, ImageAlbumImageSupplier *imageSupplier, uint initialSlot)
+	: Dialog("ImageAlbum"), _imageSupplier(imageSupplier), _currentSlot(initialSlot), _numSlots(0), _imageGraphic(nullptr), _canAlwaysSaveImage(false) {
+
+	_backgroundType = ThemeEngine::kDialogBackgroundSpecial;
+
+	_numSlots = imageSupplier->getNumSlots();
+
+	assert(_numSlots > 0);
+
+	new StaticTextWidget(this, "ImageAlbum.Title", title);
+
+	if (initialSlot >= _numSlots)
+		initialSlot = _numSlots - 1;
+
+	// Buttons
+	if (_numSlots > 1) {
+		_prevButton = new ButtonWidget(this, "ImageAlbum.Prev", _("Prev"), Common::U32String(), kPrevCmd);
+		_nextButton = new ButtonWidget(this, "ImageAlbum.Next", _("Next"), Common::U32String(), kNextCmd);
+		_imageNumberLabel = new StaticTextWidget(this, "ImageAlbum.ImageNumber", Common::U32String());
+	} else {
+		_prevButton = nullptr;
+		_nextButton = nullptr;
+		_imageNumberLabel = nullptr;
+	}
+
+	_saveButton = nullptr;
+
+
+	bool canSaveAnyFormat = false;
+	for (uint fmtID = Common::FormatInfo::kFirstImageFormat; fmtID <= Common::FormatInfo::kLastImageFormat; fmtID++) {
+		Common::FormatInfo::FormatID format = static_cast<Common::FormatInfo::FormatID>(fmtID);
+
+		if (Common::FormatInfo::getFormatSupportLevel(format) > Common::FormatInfo::kFormatSupportLevelNone) {
+			canSaveAnyFormat = true;
+			if (Common::FormatInfo::getImageSaveFunction(format) != nullptr) {
+				_canAlwaysSaveImage = true;
+				break;
+			}
+		}
+	}
+
+	if (canSaveAnyFormat) {
+		_saveButton = new ButtonWidget(this, "ImageAlbum.Save", _("Save Image..."), Common::U32String(), kSaveCmd);
+		_saveButton->setEnabled(!_canAlwaysSaveImage);
+	}
+
+	new ButtonWidget(this, "ImageAlbum.Close", _("Close"), Common::U32String(), kCloseCmd);
+
+	_imageContainer = new ContainerWidget(this, "ImageAlbum.ImageContainer");
+
+	_imageGraphic = nullptr;
+}
+
+ImageAlbumDialog::~ImageAlbumDialog() {
+}
+
+void ImageAlbumDialog::open() {
+	Dialog::open();
+
+	changeToSlot(_currentSlot);
+}
+
+void ImageAlbumDialog::changeToSlot(uint slot) {
+	ContainerWidget *container = _imageContainer;
+	container = _imageContainer;
+
+	bool canSaveImage = _canAlwaysSaveImage;
+
+	if (_imageGraphic) {
+		_imageContainer->removeWidget(_imageGraphic);
+		delete _imageGraphic;
+		_imageGraphic = nullptr;
+	}
+
+	Common::Rect graphicRect = Common::Rect(0, 0, _imageContainer->getWidth(), _imageContainer->getHeight());
+
+	int inset = g_gui.xmlEval()->getVar("Globals.ImageAlbum.ImageInset", 0);
+	graphicRect.grow(-inset);
+
+	if (graphicRect.isValidRect()) {
+		uint32 graphicRectWidth = graphicRect.width();
+		uint32 graphicRectHeight = graphicRect.height();
+
+		const Graphics::Surface *surf = nullptr;
+		bool hasPalette = false;
+		byte palette[256 * 3];
+		ImageAlbumImageMetadata metadata;
+
+		for (byte &paletteByte : palette)
+			paletteByte = 0;
+
+		if (_imageSupplier->loadImageSlot(slot, surf, hasPalette, palette, metadata)) {
+			if (!canSaveImage) {
+				// If we can't always save the image (meaning we don't have an image write-out function) then see if we can
+				// at least save this file in its native format.
+				Common::FormatInfo::FormatID format = Common::FormatInfo::kFormatUnknown;
+				if (_imageSupplier->getFileFormatForImageSlot(slot, format)) {
+					if (Common::FormatInfo::getFormatSupportLevel(format) > Common::FormatInfo::kFormatSupportLevelNone)
+						canSaveImage = true;
+				}
+			}
+
+			uint32 imageWidth = surf->w;
+			uint32 imageHeight = surf->h;
+
+			uint32 scaledWidth = graphicRectWidth;
+			uint32 scaledHeight = graphicRectHeight;
+
+			bool needs90Rotate = (metadata._viewTransformation == kImageAlbumViewTransformationRotate90CCW || metadata._viewTransformation == kImageAlbumViewTransformationRotate90CW);
+
+			uint32 imageRotatedWidth = imageWidth;
+			uint32 imageRotatedHeight = imageHeight;
+			if (needs90Rotate) {
+				imageRotatedWidth = imageHeight;
+				imageRotatedHeight = imageWidth;
+			}
+
+			// if (imageRotatedWidth / imageRotatedHeight > graphicRectWidth / graphicRectHeight)
+			if (imageRotatedWidth * graphicRectHeight >= graphicRectWidth * imageRotatedHeight) {
+				// Image aspect ratio is wider than the graphic space, or same
+				scaledWidth = graphicRectWidth;
+				scaledHeight = imageRotatedHeight * graphicRectWidth / imageRotatedWidth;
+			} else {
+				// Image aspect ratio is taller than the graphic space
+				scaledWidth = imageRotatedWidth * graphicRectHeight / imageRotatedHeight;
+				scaledHeight = graphicRectHeight;
+			}
+
+			if (scaledWidth < 1)
+				scaledWidth = 1;
+			if (scaledHeight < 1)
+				scaledHeight = 1;
+
+			Graphics::ManagedSurface rescaledGraphic;
+			rescaledGraphic.create(scaledWidth, scaledHeight, surf->format);
+			if (hasPalette)
+				rescaledGraphic.setPalette(palette, 0, 256);
+
+			if (needs90Rotate) {
+				bool isClockwise = metadata._viewTransformation == kImageAlbumViewTransformationRotate90CW;
+
+				for (uint32 destX = 0; destX < scaledWidth; destX++) {
+					uint32 srcY = destX * imageHeight / scaledWidth;
+					if (isClockwise)
+						srcY = imageHeight - 1 - srcY;
+
+					for (uint32 destY = 0; destY < scaledHeight; destY++) {
+						uint32 srcX = destY * imageWidth / scaledHeight;
+
+						if (!isClockwise)
+							srcX = imageWidth - 1 - srcX;
+
+						rescaledGraphic.setPixel(destX, destY, surf->getPixel(srcX, srcY));
+					}
+				}
+			} else if (metadata._viewTransformation == kImageAlbumViewTransformationRotate180) {
+				for (uint32 destX = 0; destX < scaledWidth; destX++) {
+					uint32 srcX = (imageWidth - 1 - (destX * imageWidth / scaledWidth));
+
+					for (uint32 destY = 0; destY < scaledHeight; destY++) {
+						uint32 srcY = (imageHeight - 1 - (destY * imageHeight / scaledHeight));
+
+						rescaledGraphic.setPixel(destX, destY, surf->getPixel(srcX, srcY));
+					}
+				}
+			} else {
+				rescaledGraphic.blitFrom(*surf, Common::Rect(0, 0, imageWidth, imageHeight), Common::Rect(0, 0, scaledWidth, scaledHeight));
+			}
+
+			_imageSupplier->releaseImageSlot(slot);
+
+			if (rescaledGraphic.format.bytesPerPixel == 1)
+				rescaledGraphic.convertToInPlace(Graphics::createPixelFormat<888>(), palette);
+
+			int32 xCoord = (static_cast<int32>(_imageContainer->getWidth()) - static_cast<int32>(scaledWidth)) / 2u;
+			int32 yCoord = (static_cast<int32>(_imageContainer->getHeight()) - static_cast<int32>(scaledHeight)) / 2u;
+
+			_imageGraphic = new GraphicsWidget(_imageContainer, xCoord, yCoord, xCoord + static_cast<int32>(scaledWidth), yCoord + static_cast<int32>(scaledHeight));
+
+			_imageGraphic->setGfx(&rescaledGraphic, false);
+
+			if (_numSlots > 1) {
+				_imageNumberLabel->setLabel(Common::U32String::format(_("%u of %u"), static_cast<uint>(slot + 1u), _numSlots));
+				_prevButton->setEnabled(slot > 0);
+				_nextButton->setEnabled(slot < _numSlots - 1u);
+			}
+
+			_currentSlot = slot;
+		} else {
+			warning("Image album failed to retrieve slot %u", slot);
+		}
+	}
+
+	if (_saveButton)
+		_saveButton->setEnabled(canSaveImage);
+}
+
+void ImageAlbumDialog::saveImageInSlot(uint slot) {
+	Common::U32String defaultFileName = _imageSupplier->getDefaultFileNameForSlot(slot);
+
+	Common::FormatInfo::FormatID nativeFormat = Common::FormatInfo::kFormatUnknown;
+
+	Common::U32String fileExt;
+	Common::U32String fileDesc;
+
+	bool hasExtension = 0;
+	uint extensionPos = 0;
+	for (uint i = 0; i < defaultFileName.size(); i++) {
+		if (defaultFileName[i] == '.') {
+			hasExtension = true;
+			extensionPos = i;
+		}
+	}
+	Common::SaveFileManager *saveFileManager = g_system->getSavefileManager();
+
+	Common::FormatInfo::FormatSupportLevel bestFormatSupportLevel = Common::FormatInfo::kFormatSupportLevelNone;
+	Common::FormatInfo::FormatID bestFormat = Common::FormatInfo::kFormatUnknown;
+	bool bestFormatIsLossy = true;
+
+	// Find the best format we can write the image as
+	for (uint fmtID = Common::FormatInfo::kFirstImageFormat; fmtID <= Common::FormatInfo::kLastImageFormat; fmtID++) {
+		Common::FormatInfo::FormatID candidateFormat = static_cast<Common::FormatInfo::FormatID>(fmtID);
+
+		if (!Common::FormatInfo::getImageSaveFunction(candidateFormat))
+			continue;
+
+		Common::FormatInfo::FormatSupportLevel supportLevel = Common::FormatInfo::getFormatSupportLevel(candidateFormat);
+
+		bool formatIsLossy = false;
+		Common::FormatInfo::ImageFormatCharacteristics characteristics;
+		if (Common::FormatInfo::getImageFormatCharacteristics(candidateFormat, characteristics))
+			formatIsLossy = (characteristics._lossiness == Common::FormatInfo::kLossinessLossy);
+
+		bool isBetter = false;
+
+		// If the best format we have chosen is lossy, and this is a lossless format that is at least supported, it is better
+		// If this format is the same lossiness, but is better-supported, it is better
+		if (bestFormatIsLossy && !formatIsLossy && supportLevel >= Common::FormatInfo::kFormatSupportLevelSupported)
+			isBetter = true;
+		else if (bestFormatIsLossy == formatIsLossy && supportLevel > bestFormatSupportLevel)
+			isBetter = true;
+		else if (bestFormat == Common::FormatInfo::kFormatUnknown && supportLevel >= Common::FormatInfo::kFormatSupportLevelNone)
+			isBetter = true;
+
+		if (isBetter) {
+			bestFormatSupportLevel = supportLevel;
+			bestFormat = candidateFormat;
+			bestFormatIsLossy = formatIsLossy;
+		}
+	}
+
+	assert(bestFormat != Common::FormatInfo::kFormatUnknown);
+
+	if (_imageSupplier->getFileFormatForImageSlot(slot, nativeFormat)) {
+		if (nativeFormat != bestFormat) {
+			Common::FormatInfo::FormatSupportLevel nativeSupportLevel = Common::FormatInfo::getFormatSupportLevel(nativeFormat);
+
+			if (nativeSupportLevel > Common::FormatInfo::kFormatSupportLevelNone) {
+				bool nativeFormatIsLossy = false;
+
+				Common::FormatInfo::ImageFormatCharacteristics characteristics;
+				if (Common::FormatInfo::getImageFormatCharacteristics(nativeFormat, characteristics))
+					nativeFormatIsLossy = (characteristics._lossiness == Common::FormatInfo::kLossinessLossy);
+
+				// If the native format is lossy and is at least supported, prefer using it directly, otherwise only use it if it has a higher support level
+				if ((nativeFormatIsLossy && nativeSupportLevel >= Common::FormatInfo::kFormatSupportLevelSupported) || nativeSupportLevel >= bestFormatSupportLevel) {
+					bestFormat = nativeFormat;
+					bestFormatSupportLevel = nativeSupportLevel;
+				}
+			}
+		}
+	}
+
+	// This shouldn't be possible, the Save button should not be visible unless there is either a saveable format,
+	// or the file's native format is saveable, and in either of those circumstances, a format should have been selected by this point.
+	assert(bestFormatSupportLevel > Common::FormatInfo::kFormatSupportLevelNone);
+
+	bool needsConversion = false;
+
+	if (nativeFormat == bestFormat) {
+		// Save in the preferred format
+		if (hasExtension)
+			fileExt = defaultFileName.substr(extensionPos + 1);
+	} else {
+		// Save in the preferred format
+		needsConversion = true;
+
+		fileExt = Common::U32String(Common::FormatInfo::getFormatExtension(bestFormat, true));
+
+		if (hasExtension)
+			defaultFileName = defaultFileName.substr(0, extensionPos) + Common::U32String(".") + fileExt;
+	}
+
+	fileDesc = Common::FormatInfo::getFormatSaveDescription(bestFormat);
+
+	Common::U32String title = _("Save Image");
+
+	if (needsConversion) {
+		const Graphics::Surface *surf = nullptr;
+		bool hasPalette = false;
+		byte palette[256 * 3];
+		ImageAlbumImageMetadata metadata;
+
+		for (byte &paletteByte : palette)
+			paletteByte = 0;
+
+		if (_imageSupplier->loadImageSlot(slot, surf, hasPalette, palette, metadata)) {
+			Common::ScopedPtr<Common::SeekableWriteStream> writeStream;
+
+			GUI::FileBrowserDialog browser(title.encode(Common::kUtf8).c_str(), fileExt.encode(Common::kUtf8).c_str(), GUI::kFBModeSave, nullptr, defaultFileName.encode(Common::kUtf8).c_str());
+
+			if (browser.runModal() > 0) {
+				Common::String path = browser.getResult();
+				writeStream.reset(saveFileManager->openForSaving(path, false));
+				if (writeStream) {
+					assert(writeStream);
+
+					Common::FormatInfo::ImageSaveCallback_t saveCallback = Common::FormatInfo::getImageSaveFunction(bestFormat);
+					assert(saveCallback);
+
+					Common::FormatInfo::ImageSaveProperties saveProps;
+					saveCallback(*writeStream, *surf, hasPalette ? palette : nullptr, saveProps);
+				} else {
+					warning("Failed to open image output stream");
+				}
+			}
+
+			_imageSupplier->releaseImageSlot(slot);
+		}
+	} else {
+		Common::ScopedPtr<Common::SeekableReadStream> readStream;
+		readStream.reset(_imageSupplier->createReadStreamForSlot(slot));
+		if (!readStream) {
+			warning("Failed to open input stream for slot %u", slot);
+			return;
+		}
+
+		Common::ScopedPtr<Common::SeekableWriteStream> writeStream;
+
+		GUI::FileBrowserDialog browser(title.encode(Common::kUtf8).c_str(), fileExt.encode(Common::kUtf8).c_str(), GUI::kFBModeSave, nullptr, defaultFileName.encode(Common::kUtf8).c_str());
+
+		if (browser.runModal() > 0) {
+			Common::String path = browser.getResult();
+			writeStream.reset(saveFileManager->openForSaving(path, false));
+
+			if (writeStream) {
+				assert(writeStream);
+
+				byte copyBuffer[2048];
+				uint32 bytesRead = readStream->read(copyBuffer, sizeof(copyBuffer));
+				while (bytesRead) {
+					writeStream->write(copyBuffer, bytesRead);
+					bytesRead = readStream->read(copyBuffer, sizeof(copyBuffer));
+				}
+			} else {
+				warning("Failed to open image output stream");
+			}
+		}
+	}
+}
+
+void ImageAlbumDialog::close() {
+	Dialog::close();
+}
+
+void ImageAlbumDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
+	switch (cmd) {
+	case kPrevCmd:
+		if (_currentSlot > 0)
+			changeToSlot(_currentSlot - 1);
+		break;
+	case kNextCmd:
+		if (_currentSlot < _numSlots - 1)
+			changeToSlot(_currentSlot + 1);
+		break;
+	case kSaveCmd:
+		saveImageInSlot(_currentSlot);
+		break;
+	default:
+		Dialog::handleCommand(sender, cmd, data);
+	}
+}
+
+GUI::Dialog *createImageAlbumDialog(const Common::U32String &title, ImageAlbumImageSupplier *imageSupplier, uint initialSlot) {
+	return new ImageAlbumDialog(title, imageSupplier, initialSlot);
+}
+
+} // End of namespace GUI
diff --git a/gui/imagealbum-dialog.h b/gui/imagealbum-dialog.h
new file mode 100644
index 00000000000..dffd884db56
--- /dev/null
+++ b/gui/imagealbum-dialog.h
@@ -0,0 +1,125 @@
+/* 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 IMAGEALBUM_DIALOG_H
+#define IMAGEALBUM_DIALOG_H
+
+#include "common/formats/formatinfo.h"
+#include "common/str.h"
+#include "common/rational.h"
+
+namespace Common {
+
+class SeekableReadStream;
+
+} // End of namespace Common
+
+namespace Graphics {
+
+struct Surface;
+
+} // End of namespace Graphics
+
+namespace GUI {
+
+class Dialog;
+
+enum ImageAlbumImageOrientation {
+	kImageAlbumImageOrientationUnspecified,
+
+	kImageAlbumImageOrientationLandscape,
+	kImageAlbumImageOrientationPortrait,
+};
+
+enum ImageAlbumViewTransformation {
+	kImageAlbumViewTransformationNone,
+	kImageAlbumViewTransformationRotate90CCW,
+	kImageAlbumViewTransformationRotate90CW,
+	kImageAlbumViewTransformationRotate180,
+};
+
+struct ImageAlbumImageMetadata {
+	ImageAlbumImageMetadata() : _orientation(kImageAlbumImageOrientationUnspecified), _viewTransformation(kImageAlbumViewTransformationNone), _hdpi(72, 1), _vdpi(72, 1) {}
+
+	ImageAlbumViewTransformation _viewTransformation;	///< Transformation required to present the image at its normal intended viewing orientation
+	ImageAlbumImageOrientation _orientation;			///< Orientation of the image after view transformation
+	Common::Rational _hdpi;								///< Horizontal DPI of the image
+	Common::Rational _vdpi;								///< Vertical DPI of the image
+};
+
+/**
+ * @brief Interface that supplies images to the image album dialog.
+ */
+class ImageAlbumImageSupplier {
+public:
+	virtual ~ImageAlbumImageSupplier();
+
+	/**
+	 * @brief Loads and returns the image for a specified slot
+	 * @param slot                    The image slot to load
+	 * @param outSurface              An outputted pointer to a surface containing the image data
+	 * @param outHasPalette           An outputted boolean containing true if the image has a palette and false if not
+	 * @param outPalette              Outputted palette colors if the image has a palette
+	 * @param outMetadata             Outputted metadata for the image
+	 * @return True if the image loaded successfully, false if it failed
+	 */
+	virtual bool loadImageSlot(uint slot, const Graphics::Surface *&outSurface, bool &outHasPalette, byte (&outPalette)[256 * 3], ImageAlbumImageMetadata &outMetadata) = 0;
+
+	/**
+	 * @brief Releases any resources for an image loaded with loadImageSlot
+	 * @param slot The image slot to release
+	 */
+	virtual void releaseImageSlot(uint slot) = 0;
+
+	/**
+	 * Returns the file format of the image in the specified image slot, if it's capable of being loaded as raw file data.
+	 * 
+	 * @param slot       The image slot to load
+	 * @param outFormat  A reference to a file format ID to set to the file format
+	 * @return true if the slot is loadable as raw data and has a MIME type available, false if not
+	 */
+	virtual bool getFileFormatForImageSlot(uint slot, Common::FormatInfo::FormatID &outFormat) const = 0;
+
+	/**
+	 * @brief Opens a slot as a read stream containing raw file data.
+	 * @param slot The image slot to load
+	 * @return The created read stream, if it could be created, or nullptr if it failed
+	 */
+	virtual Common::SeekableReadStream *createReadStreamForSlot(uint slot) = 0;
+
+	/**
+	 * @brief Returns the number of image slots
+	 * @return The number of slots
+	 */
+	virtual uint getNumSlots() const = 0;
+
+	/**
+	 * @brief Returns the default filename, including extension, for the specified slot
+	 * @return The filename of the slot without an extension
+	 */
+	virtual Common::U32String getDefaultFileNameForSlot(uint slot) const = 0;
+};
+
+Dialog *createImageAlbumDialog(const Common::U32String &title, ImageAlbumImageSupplier *imageSupplier, uint initialSlot);
+
+} // End of namespace GUI
+
+#endif
diff --git a/gui/module.mk b/gui/module.mk
index 0c16927c1d7..562a11829d7 100644
--- a/gui/module.mk
+++ b/gui/module.mk
@@ -13,6 +13,7 @@ MODULE_OBJS := \
 	EventRecorder.o \
 	filebrowser-dialog.o \
 	gui-manager.o \
+	imagealbum-dialog.o \
 	launcher.o \
 	massadd.o \
 	message.o \
diff --git a/gui/themes/common/highres_layout.stx b/gui/themes/common/highres_layout.stx
index da2dc0dc272..56a527e0cec 100644
--- a/gui/themes/common/highres_layout.stx
+++ b/gui/themes/common/highres_layout.stx
@@ -47,6 +47,7 @@
 
 		<def var = 'SaveLoadChooser.ExtInfo.Visible' value = '1'/>
 		<def var = 'RecorderDialog.ExtInfo.Visible' value = '1'/>
+		<def var = 'ImageAlbum.ImageInset' value = '16' scalable = 'yes' />
 
 		<def var = 'OnScreenDialog.ShowPics' value = '1'/>
 
@@ -2450,4 +2451,32 @@
 		</layout>
 	</dialog>
 
+	<dialog name = 'ImageAlbum' overlays = 'screen' inset = '8' shading = 'dim'>
+		<layout type = 'vertical' padding = '8, 8, 8, 8' align = 'center'> 
+			<widget name = 'Title' height = 'Globals.Line.Height'/>
+			<widget name = 'ImageContainer' />
+			<layout type = 'horizontal' padding = '0, 0, 16, 0'>
+				<widget name = 'Prev'
+						type = 'Button'
+				/>
+				<widget name = 'ImageNumber'
+					width = '50'
+					height = 'Globals.Line.Height'
+					textalign = 'center'
+					/>
+				<widget name = 'Next'
+						type = 'Button'
+				/>
+				<space/>
+				<widget name = 'Save'
+						type = 'Button'
+				/>
+				<space size = '16'/>
+				<widget name = 'Close'
+						type = 'Button'
+				/>
+			</layout>
+		</layout>
+	</dialog>
+
 </layout_info>
diff --git a/gui/themes/common/lowres_layout.stx b/gui/themes/common/lowres_layout.stx
index e0b7030ef09..9a3637d3da0 100644
--- a/gui/themes/common/lowres_layout.stx
+++ b/gui/themes/common/lowres_layout.stx
@@ -39,6 +39,7 @@
 
 		<def var = 'SaveLoadChooser.ExtInfo.Visible' value = '0'/>
 		<def var = 'RecorderDialog.ExtInfo.Visible' value = '0'/>
+		<def var = 'ImageAlbum.ImageInset' value = '16' scalable = 'yes' />
 
 		<def var = 'OnScreenDialog.ShowPics' value = '1'/>
 
@@ -2278,4 +2279,32 @@
 		</layout>
 	</dialog>
 
+	<dialog name = 'ImageAlbum' overlays = 'screen' inset = '8' shading = 'dim'>
+		<layout type = 'vertical' padding = '8, 8, 8, 8' align = 'center'> 
+			<widget name = 'Title' height = 'Globals.Line.Height'/>
+			<widget name = 'ImageContainer' />
+			<layout type = 'horizontal' padding = '0, 0, 16, 0'>
+				<widget name = 'Prev'
+						type = 'Button'
+				/>
+				<widget name = 'ImageNumber'
+					width = '50'
+					height = 'Globals.Line.Height'
+					textalign = 'center'
+					/>
+				<widget name = 'Next'
+						type = 'Button'
+				/>
+				<space/>
+				<widget name = 'Save'
+						type = 'Button'
+				/>
+				<space size = '16'/>
+				<widget name = 'Close'
+						type = 'Button'
+				/>
+			</layout>
+		</layout>
+	</dialog>
+
 </layout_info>
diff --git a/gui/themes/default.inc b/gui/themes/default.inc
index 115b888d201..32e2c4b80ab 100644
--- a/gui/themes/default.inc
+++ b/gui/themes/default.inc
@@ -1454,6 +1454,7 @@ const char *defaultXML1 = "<?xml version = '1.0'?>"
 "<def var='ShowChooserPageDisplay' value='1'/>"
 "<def var='SaveLoadChooser.ExtInfo.Visible' value='1'/>"
 "<def var='RecorderDialog.ExtInfo.Visible' value='1'/>"
+"<def var='ImageAlbum.ImageInset' value='16' scalable='yes' />"
 "<def var='OnScreenDialog.ShowPics' value='0'/>"
 "<def var='KeyMapper.Spacing' value='10' scalable='yes'/>"
 "<def var='KeyMapper.ButtonWidth' value='140' scalable='yes'/>"
@@ -3512,6 +3513,33 @@ const char *defaultXML1 = "<?xml version = '1.0'?>"
 "</layout>"
 "</layout>"
 "</dialog>"
+"<dialog name='ImageAlbum' overlays='screen' inset='8' shading='dim'>"
+"<layout type='vertical' padding='8,8,8,8' align='center'>"
+"<widget name='Title' height='Globals.Line.Height'/>"
+"<widget name='ImageContainer' />"
+"<layout type='horizontal' padding='0,0,16,0'>"
+"<widget name='Prev' "
+"type='Button' "
+"/>"
+"<widget name='ImageNumber' "
+"width='50' "
+"height='Globals.Line.Height' "
+"textalign='center' "
+"/>"
+"<widget name='Next' "
+"type='Button' "
+"/>"
+"<space/>"
+"<widget name='Save' "
+"type='Button' "
+"/>"
+"<space size='16'/>"
+"<widget name='Close' "
+"type='Button' "
+"/>"
+"</layout>"
+"</layout>"
+"</dialog>"
 "</layout_info>"
 ;
  const char *defaultXML4 = "<layout_info resolution='y<H'>"
@@ -3528,6 +3556,7 @@ const char *defaultXML1 = "<?xml version = '1.0'?>"
 "<def var='ShowChooserPageDisplay' value='0'/>"
 "<def var='SaveLoadChooser.ExtInfo.Visible' value='0'/>"
 "<def var='RecorderDialog.ExtInfo.Visible' value='0'/>"
+"<def var='ImageAlbum.ImageInset' value='16' scalable='yes' />"
 "<def var='OnScreenDialog.ShowPics' value='0'/>"
 "<def var='KeyMapper.Spacing' value='5' scalable='yes'/>"
 "<def var='KeyMapper.ButtonWidth' value='100' scalable='yes'/>"
@@ -5563,6 +5592,33 @@ const char *defaultXML1 = "<?xml version = '1.0'?>"
 "</layout>"
 "</layout>"
 "</dialog>"
+"<dialog name='ImageAlbum' overlays='screen' inset='8' shading='dim'>"
+"<layout type='vertical' padding='8,8,8,8' align='center'>"
+"<widget name='Title' height='Globals.Line.Height'/>"
+"<widget name='ImageContainer' />"
+"<layout type='horizontal' padding='0,0,16,0'>"
+"<widget name='Prev' "
+"type='Button' "
+"/>"
+"<widget name='ImageNumber' "
+"width='50' "
+"height='Globals.Line.Height' "
+"textalign='center' "
+"/>"
+"<widget name='Next' "
+"type='Button' "
+"/>"
+"<space/>"
+"<widget name='Save' "
+"type='Button' "
+"/>"
+"<space size='16'/>"
+"<widget name='Close' "
+"type='Button' "
+"/>"
+"</layout>"
+"</layout>"
+"</dialog>"
 "</layout_info>"
 ;
 const char *defaultXML[] = { defaultXML1, defaultXML2, defaultXML3, defaultXML4 };
diff --git a/gui/themes/scummclassic/classic_layout.stx b/gui/themes/scummclassic/classic_layout.stx
index 32b87f495a9..41fb110098f 100644
--- a/gui/themes/scummclassic/classic_layout.stx
+++ b/gui/themes/scummclassic/classic_layout.stx
@@ -39,6 +39,7 @@
 
 		<def var = 'SaveLoadChooser.ExtInfo.Visible' value = '1'/>
 		<def var = 'RecorderDialog.ExtInfo.Visible' value = '1'/>
+		<def var = 'ImageAlbum.ImageInset' value = '16' scalable = 'yes' />
 
 		<def var = 'OnScreenDialog.ShowPics' value = '0'/>
 
@@ -2195,4 +2196,32 @@
 		</layout>
 	</dialog>
 
+	<dialog name = 'ImageAlbum' overlays = 'screen' inset = '8' shading = 'dim'>
+		<layout type = 'vertical' padding = '8, 8, 8, 8' align = 'center'> 
+			<widget name = 'Title' height = 'Globals.Line.Height'/>
+			<widget name = 'ImageContainer' />
+			<layout type = 'horizontal' padding = '0, 0, 16, 0'>
+				<widget name = 'Prev'
+						type = 'Button'
+				/>
+				<widget name = 'ImageNumber'
+					width = '50'
+					height = 'Globals.Line.Height'
+					textalign = 'center'
+					/>
+				<widget name = 'Next'
+						type = 'Button'
+				/>
+				<space/>
+				<widget name = 'Save'
+						type = 'Button'
+				/>
+				<space size = '16'/>
+				<widget name = 'Close'
+						type = 'Button'
+				/>
+			</layout>
+		</layout>
+	</dialog>
+
 </layout_info>
diff --git a/gui/themes/scummclassic/classic_layout_lowres.stx b/gui/themes/scummclassic/classic_layout_lowres.stx
index 284075fffed..018c076ff3e 100644
--- a/gui/themes/scummclassic/classic_layout_lowres.stx
+++ b/gui/themes/scummclassic/classic_layout_lowres.stx
@@ -39,6 +39,7 @@
 
 		<def var = 'SaveLoadChooser.ExtInfo.Visible' value = '0'/>
 		<def var = 'RecorderDialog.ExtInfo.Visible' value = '0'/>
+		<def var = 'ImageAlbum.ImageInset' value = '16' scalable = 'yes' />
 
 		<def var = 'OnScreenDialog.ShowPics' value = '0'/>
 
@@ -2172,4 +2173,32 @@
 		</layout>
 	</dialog>
 
+	<dialog name = 'ImageAlbum' overlays = 'screen' inset = '8' shading = 'dim'>
+		<layout type = 'vertical' padding = '8, 8, 8, 8' align = 'center'> 
+			<widget name = 'Title' height = 'Globals.Line.Height'/>
+			<widget name = 'ImageContainer' />
+			<layout type = 'horizontal' padding = '0, 0, 16, 0'>
+				<widget name = 'Prev'
+						type = 'Button'
+				/>
+				<widget name = 'ImageNumber'
+					width = '50'
+					height = 'Globals.Line.Height'
+					textalign = 'center'
+					/>
+				<widget name = 'Next'
+						type = 'Button'
+				/>
+				<space/>
+				<widget name = 'Save'
+						type = 'Button'
+				/>
+				<space size = '16'/>
+				<widget name = 'Close'
+						type = 'Button'
+				/>
+			</layout>
+		</layout>
+	</dialog>
+
 </layout_info>


Commit: 6f005a75a4b065139a8601d3701c2681e4cd89d0
    https://github.com/scummvm/scummvm/commit/6f005a75a4b065139a8601d3701c2681e4cd89d0
Author: elasota (ejlasota at gmail.com)
Date: 2023-08-07T10:22:09+02:00

Commit Message:
TESTBED: Add tests for image album

Changed paths:
  A dists/engine-data/testbed-audiocd-files/imagealbum/image1.bmp
  A dists/engine-data/testbed-audiocd-files/imagealbum/image2.bmp
  A dists/engine-data/testbed-audiocd-files/imagealbum/image3.jpg
    engines/testbed/misc.cpp
    engines/testbed/misc.h


diff --git a/dists/engine-data/testbed-audiocd-files/imagealbum/image1.bmp b/dists/engine-data/testbed-audiocd-files/imagealbum/image1.bmp
new file mode 100644
index 00000000000..d1e2550be2f
Binary files /dev/null and b/dists/engine-data/testbed-audiocd-files/imagealbum/image1.bmp differ
diff --git a/dists/engine-data/testbed-audiocd-files/imagealbum/image2.bmp b/dists/engine-data/testbed-audiocd-files/imagealbum/image2.bmp
new file mode 100644
index 00000000000..d67ca155453
Binary files /dev/null and b/dists/engine-data/testbed-audiocd-files/imagealbum/image2.bmp differ
diff --git a/dists/engine-data/testbed-audiocd-files/imagealbum/image3.jpg b/dists/engine-data/testbed-audiocd-files/imagealbum/image3.jpg
new file mode 100644
index 00000000000..bd0ca525360
Binary files /dev/null and b/dists/engine-data/testbed-audiocd-files/imagealbum/image3.jpg differ
diff --git a/engines/testbed/misc.cpp b/engines/testbed/misc.cpp
index a0aaee2af4e..c687e1c57d6 100644
--- a/engines/testbed/misc.cpp
+++ b/engines/testbed/misc.cpp
@@ -21,6 +21,12 @@
 
 #include "testbed/misc.h"
 #include "common/timer.h"
+#include "common/file.h"
+
+#include "gui/dialog.h"
+#include "gui/imagealbum-dialog.h"
+
+#include "image/jpeg.h"
 
 namespace Testbed {
 
@@ -182,11 +188,139 @@ TestExitStatus MiscTests::testOpenUrl() {
 	return kTestPassed;
 }
 
+class ImageAlbumImageSupplier : public GUI::ImageAlbumImageSupplier {
+public:
+	void addFile(const Common::Path &path, Common::FormatInfo::FormatID format, bool dontReportFormat);
+
+	bool loadImageSlot(uint slot, const Graphics::Surface *&outSurface, bool &outHasPalette, byte (&outPalette)[256 * 3], GUI::ImageAlbumImageMetadata &outMetadata) override;
+	void releaseImageSlot(uint slot) override;
+	bool getFileFormatForImageSlot(uint slot, Common::FormatInfo::FormatID &outFormat) const override;
+	Common::SeekableReadStream *createReadStreamForSlot(uint slot) override;
+	uint getNumSlots() const override;
+	Common::U32String getDefaultFileNameForSlot(uint slot) const override;
+
+private:
+	struct FileInfo {
+		FileInfo(const Common::Path &path, Common::FormatInfo::FormatID format, bool dontReportFormat);
+
+		Common::Path _path;
+		Common::FormatInfo::FormatID _format;
+		bool _dontReportFormat;
+
+		Common::SharedPtr<Image::ImageDecoder> _decoder;
+	};
+
+	Common::Array<FileInfo> _slots;
+};
+
+void ImageAlbumImageSupplier::addFile(const Common::Path &path, Common::FormatInfo::FormatID format, bool dontReportFormat) {
+	_slots.push_back(FileInfo(path, format, dontReportFormat));
+}
+
+bool ImageAlbumImageSupplier::loadImageSlot(uint slot, const Graphics::Surface *&outSurface, bool &outHasPalette, byte (&outPalette)[256 * 3], GUI::ImageAlbumImageMetadata &outMetadata) {
+
+	FileInfo &fi = _slots[slot];
+
+	switch (fi._format) {
+	case Common::FormatInfo::kBMP:
+		fi._decoder.reset(new Image::BitmapDecoder());
+		break;
+#ifdef USE_JPEG
+	case Common::FormatInfo::kJPEG:
+		fi._decoder.reset(new Image::JPEGDecoder());
+		break;
+#endif
+	default:
+		return false;
+	}
+
+	Common::ScopedPtr<Common::SeekableReadStream> readStream(createReadStreamForSlot(slot));
+	if (!readStream)
+		return false;
+
+	if (!fi._decoder->loadStream(*readStream))
+		return false;
+
+	outSurface = fi._decoder->getSurface();
+	outHasPalette = fi._decoder->hasPalette();
+	if (fi._decoder->hasPalette())
+		memcpy(outPalette, fi._decoder->getPalette() + fi._decoder->getPaletteStartIndex() * 3, fi._decoder->getPaletteColorCount() * 3);
+	outMetadata = GUI::ImageAlbumImageMetadata();
+
+	return true;
+}
+
+void ImageAlbumImageSupplier::releaseImageSlot(uint slot) {
+	_slots[slot]._decoder.reset();
+}
+
+bool ImageAlbumImageSupplier::getFileFormatForImageSlot(uint slot, Common::FormatInfo::FormatID &outFormat) const {
+	if (_slots[slot]._dontReportFormat)
+		return false;
+
+	outFormat = _slots[slot]._format;
+	return true;
+}
+
+Common::SeekableReadStream *ImageAlbumImageSupplier::createReadStreamForSlot(uint slot) {
+	Common::ScopedPtr<Common::File> f(new Common::File());
+	if (!f->open(_slots[slot]._path))
+		return nullptr;
+
+	return f.release();
+}
+
+uint ImageAlbumImageSupplier::getNumSlots() const {
+	return _slots.size();
+}
+
+Common::U32String ImageAlbumImageSupplier::getDefaultFileNameForSlot(uint slot) const {
+	return Common::U32String(_slots[slot]._path.getLastComponent().toString());
+}
+
+ImageAlbumImageSupplier::FileInfo::FileInfo(const Common::Path &path, Common::FormatInfo::FormatID format, bool dontReportFormat) {
+	_path = path;
+	_format = format;
+	_dontReportFormat = dontReportFormat;
+}
+
+TestExitStatus MiscTests::testImageAlbum() {
+	Common::String info = "Testing ImageAlbum method.\n"
+						  "In this test we'll try to display some images,\n"
+						  "and you should be able to save them if the backend supports it.\n"
+						  "The second image will not report a file format to the backend, the third (if it exists) will be JPEG.\n";
+
+	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
+		Testsuite::logPrintf("Info! Skipping test : ImageAlbum()\n");
+		return kTestSkipped;
+	}
+
+	ImageAlbumImageSupplier imageSupplier;
+	imageSupplier.addFile("imagealbum/image1.bmp", Common::FormatInfo::kBMP, false);
+	imageSupplier.addFile("imagealbum/image2.bmp", Common::FormatInfo::kBMP, true);
+#ifdef USE_JPEG
+	imageSupplier.addFile("imagealbum/image3.jpg", Common::FormatInfo::kJPEG, false);
+#endif
+
+	GUI::Dialog *dialog = GUI::createImageAlbumDialog(Common::U32String("Image Album"), &imageSupplier, 0);
+	dialog->runModal();
+	delete dialog;
+
+	if (Testsuite::handleInteractiveInput("Did the image album work as expected?", "Yes", "No", kOptionRight)) {
+		Testsuite::logDetailedPrintf("Error! ImageAlbum is not working!\n");
+		return kTestFailed;
+	}
+
+	Testsuite::logDetailedPrintf("ImageAlbum is OK\n");
+	return kTestPassed;
+}
+
 MiscTestSuite::MiscTestSuite() {
 	addTest("Datetime", &MiscTests::testDateTime, false);
 	addTest("Timers", &MiscTests::testTimers, false);
 	addTest("Mutexes", &MiscTests::testMutexes, false);
 	addTest("openUrl", &MiscTests::testOpenUrl, true);
+	addTest("ImageAlbum", &MiscTests::testImageAlbum, true);
 }
 
 } // End of namespace Testbed
diff --git a/engines/testbed/misc.h b/engines/testbed/misc.h
index 36d339e4621..fd3c4b14359 100644
--- a/engines/testbed/misc.h
+++ b/engines/testbed/misc.h
@@ -51,6 +51,7 @@ TestExitStatus testDateTime();
 TestExitStatus testTimers();
 TestExitStatus testMutexes();
 TestExitStatus testOpenUrl();
+TestExitStatus testImageAlbum();
 // add more here
 
 } // End of namespace MiscTests
@@ -71,7 +72,7 @@ public:
 		return "Misc";
 	}
 	const char *getDescription() const override {
-		return "Miscellaneous: Timers/Mutexes/Datetime/openUrl";
+		return "Miscellaneous: Timers/Mutexes/Datetime/openUrl/ImageAlbum";
 	}
 };
 


Commit: 46a0aca35459183eec42646276ae73a93c044e14
    https://github.com/scummvm/scummvm/commit/46a0aca35459183eec42646276ae73a93c044e14
Author: elasota (ejlasota at gmail.com)
Date: 2023-08-07T10:22:09+02:00

Commit Message:
MTROPOLIS: Hook up MPZ-1000 to image album

Changed paths:
    engines/mtropolis/POTFILES
    engines/mtropolis/plugin/mti.cpp
    engines/mtropolis/plugin/mti.h


diff --git a/engines/mtropolis/POTFILES b/engines/mtropolis/POTFILES
index 3c7669e2149..4b638b4ab15 100644
--- a/engines/mtropolis/POTFILES
+++ b/engines/mtropolis/POTFILES
@@ -1,3 +1,4 @@
 engines/mtropolis/metaengine.cpp
 engines/mtropolis/saveload.cpp
 engines/mtropolis/mtropolis.cpp
+engines/mtropolis/plugin/mti.cpp
diff --git a/engines/mtropolis/plugin/mti.cpp b/engines/mtropolis/plugin/mti.cpp
index f186e14ed8a..7a913eee285 100644
--- a/engines/mtropolis/plugin/mti.cpp
+++ b/engines/mtropolis/plugin/mti.cpp
@@ -19,6 +19,17 @@
  *
  */
 
+#include "common/file.h"
+#include "common/random.h"
+#include "common/translation.h"
+#include "common/macresman.h"
+
+#include "gui/dialog.h"
+#include "gui/imagealbum-dialog.h"
+
+#include "image/bmp.h"
+#include "image/pict.h"
+
 #include "mtropolis/plugin/mti.h"
 #include "mtropolis/plugins.h"
 
@@ -29,8 +40,6 @@
 #include "graphics/managed_surface.h"
 
 #include "common/file.h"
-#include "common/random.h"
-
 namespace MTropolis {
 
 namespace MTI {
@@ -403,6 +412,126 @@ const char *ShanghaiModifier::getDefaultName() const {
 	return "Shanghai Modifier";	// ???
 }
 
+class PrintModifierImageSupplier : public GUI::ImageAlbumImageSupplier {
+public:
+	PrintModifierImageSupplier(const Common::String &inputPath, bool isMacVersion);
+
+	bool loadImageSlot(uint slot, const Graphics::Surface *&outSurface, bool &outHasPalette, byte (&outPalette)[256 * 3], GUI::ImageAlbumImageMetadata &outMetadata) override;
+	void releaseImageSlot(uint slot) override;
+	uint getNumSlots() const override;
+	Common::U32String getDefaultFileNameForSlot(uint slot) const override;
+	bool getFileFormatForImageSlot(uint slot, Common::FormatInfo::FormatID &outFormat) const override;
+	Common::SeekableReadStream *createReadStreamForSlot(uint slot) override;
+
+private:
+	Common::String _path;
+
+	Common::SharedPtr<Image::ImageDecoder> _decoder;
+	bool _isMacVersion;
+};
+
+PrintModifierImageSupplier::PrintModifierImageSupplier(const Common::String &inputPath, bool isMacVersion) : _path(inputPath), _isMacVersion(isMacVersion) {
+	if (isMacVersion)
+		_decoder.reset(new Image::PICTDecoder());
+	else
+		_decoder.reset(new Image::BitmapDecoder());
+}
+
+bool PrintModifierImageSupplier::loadImageSlot(uint slot, const Graphics::Surface *&outSurface, bool &outHasPalette, byte (&outPalette)[256 * 3], GUI::ImageAlbumImageMetadata &outMetadata) {
+	Common::ScopedPtr<Common::SeekableReadStream> dataStream(createReadStreamForSlot(slot));
+
+	if (!dataStream)
+		return false;
+
+	if (!_decoder->loadStream(*dataStream)) {
+		warning("Failed to decode print file");
+		return false;
+	}
+
+	dataStream.reset();
+
+	outSurface = _decoder->getSurface();
+	outHasPalette = _decoder->hasPalette();
+
+	if (_decoder->hasPalette())
+		memcpy(outPalette + _decoder->getPaletteStartIndex() * 3, _decoder->getPalette(), _decoder->getPaletteColorCount() * 3);
+
+	outMetadata = GUI::ImageAlbumImageMetadata();
+	outMetadata._orientation = GUI::kImageAlbumImageOrientationLandscape;
+	outMetadata._viewTransformation = GUI::kImageAlbumViewTransformationRotate90CW;
+
+	return true;
+}
+
+void PrintModifierImageSupplier::releaseImageSlot(uint slot) {
+	_decoder->destroy();
+}
+
+uint PrintModifierImageSupplier::getNumSlots() const {
+	return 1;
+}
+
+Common::U32String PrintModifierImageSupplier::getDefaultFileNameForSlot(uint slot) const {
+	Common::String filename = _path;
+
+	size_t lastColonPos = filename.findLastOf(':');
+
+	if (lastColonPos != Common::String::npos)
+		filename = filename.substr(lastColonPos + 1);
+
+	size_t lastDotPos = filename.findLastOf('.');
+	if (lastDotPos != Common::String::npos)
+		filename = filename.substr(0, lastDotPos);
+
+	if (_isMacVersion)
+		filename += Common::U32String(".pict");
+	else
+		filename += Common::U32String(".bmp");
+
+	return filename.decode(Common::kASCII);
+}
+
+bool PrintModifierImageSupplier::getFileFormatForImageSlot(uint slot, Common::FormatInfo::FormatID &outFormat) const {
+	if (slot != 0)
+		return false;
+
+	if (_isMacVersion)
+		outFormat = Common::FormatInfo::kPICT;
+	else
+		outFormat = Common::FormatInfo::kBMP;
+
+	return true;
+}
+
+Common::SeekableReadStream *PrintModifierImageSupplier::createReadStreamForSlot(uint slot) {
+	if (slot != 0)
+		return nullptr;
+
+	size_t lastColonPos = _path.findLastOf(':');
+	Common::String filename;
+
+	if (lastColonPos == Common::String::npos)
+		filename = _path;
+	else
+		filename = _path.substr(lastColonPos + 1);
+
+	Common::Path path(Common::String("MPZ_MTI/") + filename);
+
+	if (_isMacVersion) {
+		// Color images have res fork data so we must load from the data fork
+		return Common::MacResManager::openFileOrDataFork(path);
+	} else {
+		// Win versions are just files
+		Common::File *f = new Common::File();
+
+		if (!f->open(path)) {
+			delete f;
+			return nullptr;
+		}
+		return f;
+	}
+}
+
 PrintModifier::PrintModifier() {
 }
 
@@ -414,13 +543,32 @@ bool PrintModifier::respondsToEvent(const Event &evt) const {
 }
 
 VThreadState PrintModifier::consumeMessage(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msg) {
-	warning("Print modifier is not implemented");
+	if (_executeWhen.respondsTo(msg->getEvent())) {
+		PrintModifierImageSupplier imageSupplier(_filePath, runtime->getProject()->getPlatform() == kProjectPlatformMacintosh);
+
+		Common::ScopedPtr<GUI::Dialog> dialog(GUI::createImageAlbumDialog(_("Image Viewer"), &imageSupplier, 0));
+		dialog->runModal();
+	}
+
 	return kVThreadReturn;
 }
 
 void PrintModifier::disable(Runtime *runtime) {
 }
 
+MiniscriptInstructionOutcome PrintModifier::writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib) {
+	if (attrib == "showdialog") {
+		// This is only ever set to "false"
+		DynamicValueWriteDiscardHelper::create(writeProxy);
+		return kMiniscriptInstructionOutcomeContinue;
+	} else if (attrib == "filepath") {
+		DynamicValueWriteStringHelper::create(&_filePath, writeProxy);
+		return kMiniscriptInstructionOutcomeContinue;
+	}
+
+	return Modifier::writeRefAttribute(thread, writeProxy, attrib);
+}
+
 bool PrintModifier::load(const PlugInModifierLoaderContext &context, const Data::MTI::PrintModifier &data) {
 	if (data.executeWhen.type != Data::PlugInTypeTaggedValue::kEvent)
 		return false;
diff --git a/engines/mtropolis/plugin/mti.h b/engines/mtropolis/plugin/mti.h
index 637c5484f32..ca013a95601 100644
--- a/engines/mtropolis/plugin/mti.h
+++ b/engines/mtropolis/plugin/mti.h
@@ -103,6 +103,8 @@ public:
 	VThreadState consumeMessage(Runtime *runtime, const Common::SharedPtr<MessageProperties> &msg) override;
 	void disable(Runtime *runtime) override;
 
+	MiniscriptInstructionOutcome writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &writeProxy, const Common::String &attrib) override;
+
 	bool load(const PlugInModifierLoaderContext &context, const Data::MTI::PrintModifier &data);
 
 #ifdef MTROPOLIS_DEBUG_ENABLE


Commit: 58271a834cf669bfa0ab2abe89ba437981f1c3c6
    https://github.com/scummvm/scummvm/commit/58271a834cf669bfa0ab2abe89ba437981f1c3c6
Author: elasota (ejlasota at gmail.com)
Date: 2023-08-07T10:22:09+02:00

Commit Message:
COMMON: Remove #pragma once

Changed paths:
    common/formats/formatinfo.h


diff --git a/common/formats/formatinfo.h b/common/formats/formatinfo.h
index 05d6769155f..206125d9d43 100644
--- a/common/formats/formatinfo.h
+++ b/common/formats/formatinfo.h
@@ -1,4 +1,3 @@
-#pragma once
 /* ScummVM - Graphic Adventure Engine
  *
  * ScummVM is the legal property of its developers, whose names


Commit: 9598e343a35eb3d3d452d09f645a4c23adf2f96b
    https://github.com/scummvm/scummvm/commit/9598e343a35eb3d3d452d09f645a4c23adf2f96b
Author: elasota (ejlasota at gmail.com)
Date: 2023-08-07T10:22:09+02:00

Commit Message:
GUI: Add imagealbum-dialog.cpp to POTFILES

Changed paths:
    po/POTFILES


diff --git a/po/POTFILES b/po/POTFILES
index f1cafc86a24..ba86acdb24b 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -10,6 +10,7 @@ gui/error.cpp
 gui/filebrowser-dialog.cpp
 gui/fluidsynth-dialog.cpp
 gui/gui-manager.cpp
+gui/imagealbum-dialog.cpp
 gui/launcher.cpp
 gui/massadd.cpp
 gui/message.cpp


Commit: eea428cbc350501c70ded31081721cb722440585
    https://github.com/scummvm/scummvm/commit/eea428cbc350501c70ded31081721cb722440585
Author: elasota (ejlasota at gmail.com)
Date: 2023-08-07T10:22:09+02:00

Commit Message:
GUI: Update theme binaries

Changed paths:
    gui/themes/residualvm.zip
    gui/themes/scummclassic.zip
    gui/themes/scummmodern.zip
    gui/themes/scummremastered.zip


diff --git a/gui/themes/residualvm.zip b/gui/themes/residualvm.zip
index 02aa00024e3..b7935ba4d4b 100644
Binary files a/gui/themes/residualvm.zip and b/gui/themes/residualvm.zip differ
diff --git a/gui/themes/scummclassic.zip b/gui/themes/scummclassic.zip
index e9d2bd8b43b..efb7846a940 100644
Binary files a/gui/themes/scummclassic.zip and b/gui/themes/scummclassic.zip differ
diff --git a/gui/themes/scummmodern.zip b/gui/themes/scummmodern.zip
index 70137e0a9e5..1152b6736b2 100644
Binary files a/gui/themes/scummmodern.zip and b/gui/themes/scummmodern.zip differ
diff --git a/gui/themes/scummremastered.zip b/gui/themes/scummremastered.zip
index dacee6f204f..fc4fd3fa974 100644
Binary files a/gui/themes/scummremastered.zip and b/gui/themes/scummremastered.zip differ




More information about the Scummvm-git-logs mailing list