From dbf042240f843449a30c5350632cc93ba1bbdacf Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 27 Jul 2026 02:09:49 +0100 Subject: [PATCH 1/3] Add install flat option --- Games/RomMImport.cs | 5 +++-- Games/RomMInstallController.cs | 8 ++++++-- Settings/EmulatorMapping.cs | 18 +++++++++++++++--- Settings/SettingsView.xaml | 1 + 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/Games/RomMImport.cs b/Games/RomMImport.cs index bc58364..5912a18 100644 --- a/Games/RomMImport.cs +++ b/Games/RomMImport.cs @@ -210,8 +210,8 @@ private Game ImportGame(RomMRom ROM, Guid StatusID) return null; } - var gameInstallDir = RomMInstallPaths.InstallDir(rootInstallDir, folderName, fileName); - var pathToGame = RomMInstallPaths.GamePath(rootInstallDir, folderName, fileName); + var gameInstallDir = _mapping.InstallFlat ? rootInstallDir : RomMInstallPaths.InstallDir(rootInstallDir, folderName, fileName); + var pathToGame = _mapping.InstallFlat ? $"{rootInstallDir}\\{fileName}" : RomMInstallPaths.GamePath(rootInstallDir, folderName, fileName); var status = _plugin.Playnite.Database.CompletionStatuses.Get(StatusID); var completionStatusProperty = status != null ? new MetadataNameProperty(status.Name) : null; @@ -246,6 +246,7 @@ private Game ImportGame(RomMRom ROM, Guid StatusID) } }; + // Import new game Game game = _plugin.Playnite.Database.ImportGame(metadata, _plugin); diff --git a/Games/RomMInstallController.cs b/Games/RomMInstallController.cs index 5ee2b07..e5012ca 100644 --- a/Games/RomMInstallController.cs +++ b/Games/RomMInstallController.cs @@ -1,14 +1,15 @@ -using Playnite.SDK; +using Newtonsoft.Json; +using Playnite.SDK; using Playnite.SDK.Models; using Playnite.SDK.Plugins; using RomM.Downloads; using RomM.Models.RomM.Rom; +using RomM.Settings; using SharpCompress.Archives; using System; using System.Collections.Generic; using System.IO; using System.Linq; -using Newtonsoft.Json; namespace RomM.Games { @@ -47,6 +48,9 @@ public override void Install(InstallActionArgs args) // IsInstalled detection lines up. var installDir = RomMInstallPaths.InstallDir(dstPath, _gameData.FolderName, _gameData.FileName); + if (_gameData.Mapping.InstallFlat) + installDir = dstPath; + // If RomM indicates multiple files, we download as an archive name (zip) into the install folder. // Otherwise we download the single ROM file. var downloadFilePath = _gameData.HasMultipleFiles diff --git a/Settings/EmulatorMapping.cs b/Settings/EmulatorMapping.cs index a3114a4..4c18773 100644 --- a/Settings/EmulatorMapping.cs +++ b/Settings/EmulatorMapping.cs @@ -41,6 +41,8 @@ public class EmulatorMapping : ObservableObject public int _romMPlatformId = -1; [JsonIgnore] private string _destinationPath = ""; + [JsonIgnore] + private bool _installFlat = false; public EmulatorMapping(List romMPlatforms) { @@ -232,10 +234,20 @@ public string DestinationPath { _destinationPath = value; OnPropertyChanged(); - } -} + } + } + + public bool InstallFlat + { + get => _installFlat; + set + { + _installFlat = value; + OnPropertyChanged(); + } + } -[JsonIgnore] + [JsonIgnore] public static IEnumerable AvailableEmulators => SettingsViewModel.Instance.PlayniteAPI.Database.Emulators?.OrderBy(x => x.Name) ?? Enumerable.Empty(); [JsonIgnore] public IEnumerable AvailableProfiles diff --git a/Settings/SettingsView.xaml b/Settings/SettingsView.xaml index db2c4fd..481a9ff 100644 --- a/Settings/SettingsView.xaml +++ b/Settings/SettingsView.xaml @@ -229,6 +229,7 @@ + From 19c03354437f5894657a5bdea19b119c342d8049 Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 27 Jul 2026 15:34:41 +0100 Subject: [PATCH 2/3] Skip extracting archives when install flat is enabled --- Downloads/DownloadQueueController.cs | 6 ++++-- Downloads/DownloadRequest.cs | 2 +- Games/RomMInstallController.cs | 1 + 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Downloads/DownloadQueueController.cs b/Downloads/DownloadQueueController.cs index f186f45..1465d4e 100644 --- a/Downloads/DownloadQueueController.cs +++ b/Downloads/DownloadQueueController.cs @@ -177,7 +177,8 @@ private async Task DownloadAndInstall(DownloadQueueItem item, DownloadRequest re } // Extract if needed (we treat extract as 0..100 in its own bar) - if (req.HasMultipleFiles || (req.AutoExtract && IsFileCompressed(req.GamePath))) + // Don't extract archives when install flat is enabled + if (req.HasMultipleFiles || (req.AutoExtract && IsFileCompressed(req.GamePath) && !req.InstallFlat)) { item.SetStatus(DownloadStatus.Extracting, "Extracting..."); Logger.Info($"Extracting {req.GamePath}..."); @@ -323,7 +324,8 @@ private void TryCleanupPartialInstall(DownloadRequest req) SafeDeleteFileWithRetry(req.GamePath); // delete folder (recursively) if it exists - SafeDeleteDirectoryWithRetry(req.InstallDir); + if(!req.InstallFlat) + SafeDeleteDirectoryWithRetry(req.InstallDir); } catch (Exception ex) { diff --git a/Downloads/DownloadRequest.cs b/Downloads/DownloadRequest.cs index a66982a..7f5e4d2 100644 --- a/Downloads/DownloadRequest.cs +++ b/Downloads/DownloadRequest.cs @@ -16,7 +16,7 @@ public class DownloadRequest public bool AutoExtract { get; set; } = true; public bool Use7z { get; set; } = false; public string PathTo7Z { get; set; } = ""; - + public bool InstallFlat { get; set; } = false; /// Optional function used after extraction to build rom list for Playnite public Func> BuildRoms { get; set; } diff --git a/Games/RomMInstallController.cs b/Games/RomMInstallController.cs index e5012ca..b9e6299 100644 --- a/Games/RomMInstallController.cs +++ b/Games/RomMInstallController.cs @@ -70,6 +70,7 @@ public override void Install(InstallActionArgs args) HasMultipleFiles = _gameData.HasMultipleFiles, AutoExtract = _gameData.Mapping != null && _gameData.Mapping.AutoExtract, + InstallFlat = _gameData.Mapping.InstallFlat, // Called by queue AFTER download/extract is done BuildRoms = () => From 0829008769b154e20ea1053aaeac9a3a81221ead Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 27 Jul 2026 15:37:43 +0100 Subject: [PATCH 3/3] Uninstall ROMs when install flat is enabled --- Games/RomMGameInfo.Plugin.cs | 2 +- Games/RomMUninstallController.cs | 25 ++++++++++++++++++++----- RomM.cs | 23 ++++++++++++++++++++++- 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/Games/RomMGameInfo.Plugin.cs b/Games/RomMGameInfo.Plugin.cs index 31a18b2..ef135ab 100644 --- a/Games/RomMGameInfo.Plugin.cs +++ b/Games/RomMGameInfo.Plugin.cs @@ -26,7 +26,7 @@ public EmulatorMapping Mapping public InstallController GetInstallController(Game game, RomM romm, GameInstallInfo GameData) => new RomMInstallController(game, romm, GameData); - public UninstallController GetUninstallController(Game game, RomM romm) => new RomMUninstallController(game, romm); + public UninstallController GetUninstallController(Game game, RomM romm, EmulatorMapping mapping) => new RomMUninstallController(game, romm, mapping); protected IEnumerable GetDescriptionLines() { diff --git a/Games/RomMUninstallController.cs b/Games/RomMUninstallController.cs index bf9b33d..39a1d66 100644 --- a/Games/RomMUninstallController.cs +++ b/Games/RomMUninstallController.cs @@ -1,5 +1,6 @@ using Playnite.SDK.Models; using Playnite.SDK.Plugins; +using RomM.Settings; using System.IO; using System.Windows; @@ -8,24 +9,38 @@ namespace RomM.Games internal class RomMUninstallController : UninstallController { private readonly IRomM _romM; + private EmulatorMapping _mapping; - internal RomMUninstallController(Game game, IRomM romM) : base(game) + internal RomMUninstallController(Game game, IRomM romM, EmulatorMapping mapping) : base(game) { Name = "Uninstall"; _romM = romM; + _mapping = mapping; } public override void Uninstall(UninstallActionArgs args) { - if (new DirectoryInfo(Game.InstallDirectory).Exists) + if(_mapping.InstallFlat) { - Directory.Delete(Game.InstallDirectory, true); + foreach (var RomFile in Game.Roms) + { + if(File.Exists(RomFile.Path)) + File.Delete(RomFile.Path); + } } else { - _romM.Playnite.Dialogs.ShowMessage($"\"{Game.Name}\" folder could not be found. Marking as uninstalled.", "Game not found", MessageBoxButton.OK); + if (new DirectoryInfo(Game.InstallDirectory).Exists) + { + Directory.Delete(Game.InstallDirectory, true); + } + else + { + _romM.Playnite.Dialogs.ShowMessage($"\"{Game.Name}\" folder could not be found. Marking as uninstalled.", "Game not found", MessageBoxButton.OK); + } } - Game.Roms.Clear(); + + Game.Roms.Clear(); InvokeOnUninstalled(new GameUninstalledEventArgs()); } } diff --git a/RomM.cs b/RomM.cs index dacd444..429555d 100644 --- a/RomM.cs +++ b/RomM.cs @@ -490,7 +490,28 @@ public override IEnumerable GetUninstallActions(GetUninstal { if (args.Game.PluginId == Id) { - yield return new RomMUninstallController(args.Game, this); + EmulatorMapping mapping = null; + + try + { + var splitID = args.Game.GameId.Split(':'); + string sidecarPath = $"{ROMDataPath}{splitID[1]}.json"; + if (File.Exists(sidecarPath)) + { + var existingJson = File.ReadAllText(sidecarPath); + var localROM = JsonConvert.DeserializeObject(existingJson); + mapping = Settings.Mappings.FirstOrDefault(x => x.MappingId == localROM.MappingID); + } + } + catch (Exception) + { + Logger.Error($"{args.Game.Name} GameID is malformed or json file is corrupted!"); + } + + if (mapping == null) + yield return null; + + yield return new RomMUninstallController(args.Game, this, mapping); } } public override void OnGameInstalled(OnGameInstalledEventArgs args)