using System; using System.Collections.Generic; using System.Linq; using SabreTools.Core; using SabreTools.Core.Tools; using SabreTools.DatFiles; using SabreTools.DatItems; using SabreTools.Logging; namespace SabreTools.Filtering { public class ExtraIni { #region Fields /// /// List of extras to apply /// public List Items { get; set; } = new List(); #endregion #region Logging /// /// Logging object /// private readonly Logger logger; #endregion #region Constructors /// /// Constructor /// public ExtraIni() { logger = new Logger(this); } #endregion #region Population /// /// Populate item using field:file inputs /// /// Field and file combinations public void PopulateFromList(List inputs) { // If there are no inputs, just skip if (inputs == null || !inputs.Any()) return; InternalStopwatch watch = new("Populating extras from list"); foreach (string input in inputs) { ExtraIniItem item = new(); // If we don't even have a possible field and file combination if (!input.Contains(':')) { logger.Warning($"'{input}` is not a valid INI extras string. Valid INI extras strings are of the form 'key:value'. Please refer to README.1ST or the help feature for more details."); return; } string inputTrimmed = input.Trim('"', ' ', '\t'); string fieldString = inputTrimmed.Split(':')[0].ToLowerInvariant().Trim('"', ' ', '\t'); string fileString = inputTrimmed.Substring(fieldString.Length + 1).Trim('"', ' ', '\t'); item.DatItemField = fieldString.AsDatItemField(); item.MachineField = fieldString.AsMachineField(); if (item.PopulateFromFile(fileString)) Items.Add(item); } watch.Stop(); } #endregion #region Running /// /// Apply a set of Extra INIs on the DatFile /// /// Current DatFile object to run operations on /// True if the error that is thrown should be thrown back to the caller, false otherwise /// True if the extras were applied, false on error public bool ApplyExtras(DatFile datFile, bool throwOnError = false) { // If we have no extras, don't attempt to apply and just return true if (Items == null || !Items.Any()) return true; InternalStopwatch watch = new("Applying extra mappings to DAT"); try { // Bucket by game first datFile.Items.BucketBy(ItemKey.Machine, DedupeType.None); // Create mappings based on the extra items var combinedMachineMaps = CombineMachineExtras(); var combinedDatItemMaps = CombineDatItemExtras(); // Now get the combined set of keys var machines = combinedMachineMaps.Keys.Concat(combinedDatItemMaps.Keys).Distinct(); // Apply the mappings foreach (string machine in machines) { // If the key doesn't exist, continue if (!datFile.Items.ContainsKey(machine)) continue; // Get the list of DatItems for the machine var datItems = datFile.Items[machine]; if (datItems == null) continue; // Try to get the map values, if possible combinedMachineMaps.TryGetValue(machine, out var machineMappings); combinedDatItemMaps.TryGetValue(machine, out var datItemMappings); // Create a setter with the new mappings Setter setter = new() { MachineMappings = machineMappings, DatItemMappings = datItemMappings, }; // Loop through and set the fields accordingly foreach (var datItem in datItems) { setter.SetFields(datItem.Machine); setter.SetFields(datItem); } } } catch (Exception ex) when (!throwOnError) { logger.Error(ex); return false; } finally { watch.Stop(); } return true; } /// /// Combine MachineField-based ExtraIni fields /// /// Mapping dictionary from machine name to field mapping private Dictionary> CombineMachineExtras() { var machineMap = new Dictionary>(); // Loop through each of the extras foreach (ExtraIniItem item in Items.Where(i => i.MachineField != MachineField.NULL)) { foreach (var mapping in item.Mappings) { string machineName = mapping.Key; string value = mapping.Value; machineMap[machineName] = new Dictionary { [item.MachineField] = value, }; } } return machineMap; } /// /// Combine DatItemField-based ExtraIni fields /// /// Mapping dictionary from machine name to field mapping private Dictionary> CombineDatItemExtras() { var datItemMap = new Dictionary>(); // Loop through each of the extras foreach (ExtraIniItem item in Items.Where(i => i.DatItemField != DatItemField.NULL)) { foreach (var mapping in item.Mappings) { string machineName = mapping.Key; string value = mapping.Value; datItemMap[machineName] = new Dictionary() { [item.DatItemField] = value, }; } } return datItemMap; } #endregion } }