diff --git a/QuickLook.Plugin/QuickLook.Plugin.AppViewer/InfoPanels/DmgInfoPanel.xaml b/QuickLook.Plugin/QuickLook.Plugin.AppViewer/InfoPanels/DmgInfoPanel.xaml
new file mode 100644
index 0000000..77da66d
--- /dev/null
+++ b/QuickLook.Plugin/QuickLook.Plugin.AppViewer/InfoPanels/DmgInfoPanel.xaml
@@ -0,0 +1,274 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/QuickLook.Plugin/QuickLook.Plugin.AppViewer/InfoPanels/DmgInfoPanel.xaml.cs b/QuickLook.Plugin/QuickLook.Plugin.AppViewer/InfoPanels/DmgInfoPanel.xaml.cs
new file mode 100644
index 0000000..4b92fc5
--- /dev/null
+++ b/QuickLook.Plugin/QuickLook.Plugin.AppViewer/InfoPanels/DmgInfoPanel.xaml.cs
@@ -0,0 +1,96 @@
+// Copyright © 2017-2025 QL-Win Contributors
+//
+// This file is part of QuickLook program.
+//
+// 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 .
+
+using QuickLook.Common.ExtensionMethods;
+using QuickLook.Common.Helpers;
+using QuickLook.Common.Plugin;
+using QuickLook.Plugin.AppViewer.PackageParsers.Dmg;
+using System;
+using System.Globalization;
+using System.IO;
+using System.Reflection;
+using System.Threading.Tasks;
+using System.Windows.Controls;
+using System.Windows.Media.Imaging;
+
+namespace QuickLook.Plugin.AppViewer.InfoPanels;
+
+public partial class DmgInfoPanel : UserControl, IAppInfoPanel
+{
+ private readonly ContextObject _context;
+
+ public DmgInfoPanel(ContextObject context)
+ {
+ _context = context;
+
+ DataContext = this;
+ InitializeComponent();
+
+ string translationFile = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Translations.config");
+ applicationNameTitle.Text = TranslationHelper.Get("APP_NAME", translationFile);
+ versionNameTitle.Text = TranslationHelper.Get("APP_VERSION_NAME", translationFile);
+ versionCodeTitle.Text = TranslationHelper.Get("APP_VERSION_CODE", translationFile);
+ packageNameTitle.Text = TranslationHelper.Get("PACKAGE_NAME", translationFile);
+ minimumOSVersionTitle.Text = TranslationHelper.Get("APP_MIN_OS_VERSION", translationFile);
+ deviceFamilyTitle.Text = TranslationHelper.Get("DEVICE_FAMILY", translationFile);
+ platformVersionTitle.Text = TranslationHelper.Get("APP_TARGET_OS_VERSION", translationFile);
+ totalSizeTitle.Text = TranslationHelper.Get("TOTAL_SIZE", translationFile);
+ modDateTitle.Text = TranslationHelper.Get("LAST_MODIFIED", translationFile);
+ permissionsGroupBox.Header = TranslationHelper.Get("PERMISSIONS", translationFile);
+ }
+
+ public void DisplayInfo(string path)
+ {
+ var name = Path.GetFileName(path);
+ filename.Text = string.IsNullOrEmpty(name) ? path : name;
+
+ _ = Task.Run(() =>
+ {
+ if (File.Exists(path))
+ {
+ var size = new FileInfo(path).Length;
+ DmgInfo dmgInfo = DmgParser.Parse(path);
+ var last = File.GetLastWriteTime(path);
+
+ Dispatcher.Invoke(() =>
+ {
+ applicationName.Text = dmgInfo.DisplayName;
+ versionName.Text = dmgInfo.VersionName;
+ versionCode.Text = dmgInfo.VersionCode;
+ packageName.Text = dmgInfo.Identifier;
+ minimumOSVersion.Text = dmgInfo.MinimumOSVersion;
+ platformVersion.Text = dmgInfo.PlatformVersion;
+ deviceFamily.Text = dmgInfo.SupportedPlatforms;
+ totalSize.Text = size.ToPrettySize(2);
+ modDate.Text = last.ToString(CultureInfo.CurrentCulture);
+ permissions.ItemsSource = dmgInfo.Permissions;
+
+ if (dmgInfo.HasIcon)
+ {
+ image.Source = dmgInfo.Logo.ToBitmapSource();
+ }
+ else
+ {
+ image.Source = new BitmapImage(new Uri("pack://application:,,,/QuickLook.Plugin.AppViewer;component/Resources/ios.png"));
+ }
+
+ _context.IsBusy = false;
+ });
+ }
+ });
+ }
+}
diff --git a/QuickLook.Plugin/QuickLook.Plugin.AppViewer/PackageParsers/Dmg/DmgArchive.cs b/QuickLook.Plugin/QuickLook.Plugin.AppViewer/PackageParsers/Dmg/DmgArchive.cs
new file mode 100644
index 0000000..5a54fda
--- /dev/null
+++ b/QuickLook.Plugin/QuickLook.Plugin.AppViewer/PackageParsers/Dmg/DmgArchive.cs
@@ -0,0 +1,51 @@
+// Copyright © 2017-2025 QL-Win Contributors
+//
+// This file is part of QuickLook program.
+//
+// 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 .
+
+using DiscUtils.HfsPlus;
+using System;
+using System.IO;
+
+namespace QuickLook.Plugin.AppViewer.PackageParsers.Dmg;
+
+public class DmgArchive : IDisposable
+{
+ public string Entry { get; set; }
+
+ public HfsPlusFileSystem FileSystem { get; set; }
+
+ public void Dispose()
+ {
+ FileSystem?.Dispose();
+ FileSystem = null;
+ }
+
+ public byte[] GetBytes()
+ {
+ if (Entry is null)
+ return null;
+
+ if (FileSystem is null)
+ return null;
+
+ using var stream = FileSystem.OpenFile(Entry, FileMode.Open, FileAccess.Read);
+ using var ms = new MemoryStream();
+ stream.CopyTo(ms);
+ byte[] fileBytes = ms.ToArray();
+
+ return fileBytes;
+ }
+}
diff --git a/QuickLook.Plugin/QuickLook.Plugin.AppViewer/PackageParsers/Dmg/DmgInfo.cs b/QuickLook.Plugin/QuickLook.Plugin.AppViewer/PackageParsers/Dmg/DmgInfo.cs
new file mode 100644
index 0000000..f35b143
--- /dev/null
+++ b/QuickLook.Plugin/QuickLook.Plugin.AppViewer/PackageParsers/Dmg/DmgInfo.cs
@@ -0,0 +1,43 @@
+// Copyright © 2017-2025 QL-Win Contributors
+//
+// This file is part of QuickLook program.
+//
+// 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 .
+
+using System.Drawing;
+
+namespace QuickLook.Plugin.AppViewer.PackageParsers.Dmg;
+
+public class DmgInfo
+{
+ public string DisplayName { get; set; }
+
+ public string VersionName { get; set; }
+
+ public string VersionCode { get; set; }
+
+ public string Identifier { get; set; }
+
+ public string MinimumOSVersion { get; set; }
+
+ public string PlatformVersion { get; set; }
+
+ public string SupportedPlatforms { get; set; }
+
+ public string[] Permissions { get; set; } = [];
+
+ public Bitmap Logo { get; set; }
+
+ public bool HasIcon => Logo is not null;
+}
diff --git a/QuickLook.Plugin/QuickLook.Plugin.AppViewer/PackageParsers/Dmg/DmgParser.cs b/QuickLook.Plugin/QuickLook.Plugin.AppViewer/PackageParsers/Dmg/DmgParser.cs
new file mode 100644
index 0000000..8f3f58c
--- /dev/null
+++ b/QuickLook.Plugin/QuickLook.Plugin.AppViewer/PackageParsers/Dmg/DmgParser.cs
@@ -0,0 +1,41 @@
+// Copyright © 2017-2025 QL-Win Contributors
+//
+// This file is part of QuickLook program.
+//
+// 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 .
+
+using System.Linq;
+
+namespace QuickLook.Plugin.AppViewer.PackageParsers.Dmg;
+
+public static class DmgParser
+{
+ public static DmgInfo Parse(string path)
+ {
+ using DmgReader reader = new(path);
+
+ return new DmgInfo()
+ {
+ DisplayName = reader.DisplayName,
+ VersionName = reader.ShortVersionString,
+ VersionCode = reader.Version,
+ Identifier = reader.Identifier,
+ MinimumOSVersion = reader.MinimumOSVersion,
+ SupportedPlatforms = reader.SupportedPlatforms,
+ PlatformVersion = reader.PlatformVersion,
+ Permissions = [.. reader.InfoPlistDict.Keys.Where(key => key.StartsWith("NS") && key.EndsWith("UsageDescription"))],
+ Logo = reader.Logo,
+ };
+ }
+}
diff --git a/QuickLook.Plugin/QuickLook.Plugin.AppViewer/PackageParsers/Dmg/DmgReader.cs b/QuickLook.Plugin/QuickLook.Plugin.AppViewer/PackageParsers/Dmg/DmgReader.cs
new file mode 100644
index 0000000..8f064eb
--- /dev/null
+++ b/QuickLook.Plugin/QuickLook.Plugin.AppViewer/PackageParsers/Dmg/DmgReader.cs
@@ -0,0 +1,216 @@
+// Copyright © 2017-2025 QL-Win Contributors
+//
+// This file is part of QuickLook program.
+//
+// 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 .
+
+using DiscUtils;
+using DiscUtils.HfsPlus;
+using QuickLook.Plugin.AppViewer.PackageParsers.Ipa;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Drawing;
+using System.IO;
+using System.Text.RegularExpressions;
+
+namespace QuickLook.Plugin.AppViewer.PackageParsers.Dmg;
+
+public class DmgReader : IDisposable
+{
+ public string VolumeLabel { get; set; }
+
+ public string ContentsEntry { get; set; }
+
+ public Dictionary Archives { get; } = [];
+
+ public Dictionary InfoPlistDict { get; set; }
+
+ public string DisplayName { get; set; }
+
+ public string ShortVersionString { get; set; }
+
+ public string Version { get; set; }
+
+ public string Identifier { get; set; }
+
+ public byte[] Icon { get; set; }
+
+ public string IconName { get; set; }
+
+ public string IconEntry { get; set; }
+
+ public Bitmap Logo { get; set; }
+
+ public string MinimumOSVersion { get; set; }
+
+ public string PlatformVersion { get; set; }
+
+ public string SupportedPlatforms { get; set; }
+
+ static DmgReader()
+ {
+ DiscUtils.Complete.SetupHelper.SetupComplete();
+ }
+
+ public DmgReader(string path)
+ {
+ Open(path);
+ }
+
+ public void Dispose()
+ {
+ if (Archives is not null)
+ {
+ foreach (var archive in Archives.Values)
+ {
+ archive.Dispose();
+ }
+ Archives.Clear();
+ }
+ }
+
+ private void Open(string path)
+ {
+ using var disk = VirtualDisk.OpenDisk(path, FileAccess.Read, useAsync: false);
+ if (disk is null)
+ {
+ Debug.WriteLine($"Failed to open '{path}' as virtual disk.");
+ return;
+ }
+
+ // Find the first (and supposedly, only, HFS partition)
+ foreach (var volume in VolumeManager.GetPhysicalVolumes(disk))
+ {
+ foreach (var fileSystem in FileSystemManager.DetectFileSystems(volume))
+ {
+ // Apple HFS+
+ if (fileSystem.Name == "HFS+")
+ {
+ using var hfs = (HfsPlusFileSystem)fileSystem.Open(volume);
+
+ VolumeLabel = hfs.VolumeLabel;
+ ListFiles(hfs, string.Empty);
+ }
+ }
+ }
+
+ byte[] infoPlistData = null;
+
+ foreach (var archive in Archives.Values)
+ {
+ Match m = Regex.Match(archive.Entry, @".*\.app\\Contents\\Info\.plist$");
+
+ if (m.Success)
+ {
+ ContentsEntry = Path.GetDirectoryName(archive.Entry);
+ infoPlistData = archive.GetBytes();
+ if (Plist.ReadPlist(infoPlistData) is Dictionary dict)
+ {
+ InfoPlistDict = dict;
+ }
+ break;
+ }
+ }
+
+ {
+ if (InfoPlistDict.TryGetValue("CFBundleDisplayName", out object value) && value is string stringValue)
+ {
+ DisplayName = stringValue;
+ }
+ }
+ {
+ if (InfoPlistDict.TryGetValue("CFBundleShortVersionString", out object value) && value is string stringValue)
+ {
+ ShortVersionString = stringValue;
+ }
+ }
+ {
+ if (InfoPlistDict.TryGetValue("CFBundleVersion", out object value) && value is string stringValue)
+ {
+ Version = stringValue;
+ }
+ }
+ {
+ if (InfoPlistDict.TryGetValue("CFBundleIdentifier", out object value) && value is string stringValue)
+ {
+ Identifier = stringValue;
+ }
+ }
+ {
+ if (InfoPlistDict.TryGetValue("LSMinimumSystemVersion", out object value) && value is string stringValue)
+ {
+ MinimumOSVersion = $"macOS {stringValue}";
+ }
+ }
+ {
+ if (InfoPlistDict.TryGetValue("DTPlatformVersion", out object value) && value is string stringValue)
+ {
+ PlatformVersion = $"macOS {stringValue}";
+ }
+ }
+ {
+ if (InfoPlistDict.TryGetValue("CFBundleSupportedPlatforms", out object familyNode) && familyNode is IEnumerable