using System; using System.Collections.Generic; using SabreTools.Core.Filter; using SabreTools.DatFiles; using SabreTools.DatItems; using SabreTools.IO.Logging; namespace SabreTools.DatTools { public class ExtraIni { #region Fields /// /// List of extras to apply /// public readonly List Items = []; #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.Count == 0) return; InternalStopwatch watch = new("Populating extras from list"); foreach (string input in inputs) { // 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'); var item = new ExtraIniItem(fieldString, fileString); if (item.Mappings.Count > 0) 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.Count == 0) return true; var watch = new InternalStopwatch("Applying extra mappings to DAT"); try { // Bucket by game first datFile.BucketBy(ItemKey.Machine); // Create mappings based on the extra items var combinedMaps = CombineExtras(); var machines = combinedMaps.Keys; // Apply the mappings foreach (string machine in machines) { // Get the list of DatItems for the machine List datItems = datFile.GetItemsForBucket(machine); if (datItems.Count == 0) continue; // Try to get the map values, if possible combinedMaps.TryGetValue(machine, out var mappings); // Create a setter with the new mappings var setter = new Setter(); setter.PopulateSettersFromDictionary(mappings); // Loop through and set the fields accordingly foreach (var datItem in datItems) { setter.SetFields(datItem.GetFieldValue(DatItem.MachineKey)); setter.SetFields(datItem); } } } catch (Exception ex) when (!throwOnError) { _logger.Error(ex); return false; } finally { watch.Stop(); } return true; } /// /// 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 ApplyExtrasDB(DatFile datFile, bool throwOnError = false) { // If we have no extras, don't attempt to apply and just return true if (Items.Count == 0) return true; var watch = new InternalStopwatch("Applying extra mappings to DAT"); try { // Bucket by game first datFile.BucketBy(ItemKey.Machine); // Create mappings based on the extra items var combinedMaps = CombineExtras(); var games = combinedMaps.Keys; // Apply the mappings foreach (string game in games) { // Get the list of DatItems for the machine var datItems = datFile.GetItemsForBucketDB(game); if (datItems == null) continue; // Try to get the map values, if possible combinedMaps.TryGetValue(game, out var mappings); // Create a setter with the new mappings var setter = new Setter(); setter.PopulateSettersFromDictionary(mappings); // Loop through and set the fields accordingly foreach (var datItem in datItems) { var machine = datFile.GetMachineForItemDB(datItem.Key); if (machine.Value != null) setter.SetFields(machine.Value); setter.SetFields(datItem.Value); } } } catch (Exception ex) when (!throwOnError) { _logger.Error(ex); return false; } finally { watch.Stop(); } return true; } /// /// Combine ExtraIni fields /// /// Mapping dictionary from machine name to field mapping private Dictionary> CombineExtras() { var machineMap = new Dictionary>(); // Loop through each of the extras foreach (ExtraIniItem item in Items) { foreach (var mapping in item.Mappings) { string machineName = mapping.Key; string value = mapping.Value; if (!machineMap.ContainsKey(machineName)) machineMap[machineName] = []; machineMap[machineName][item.Key] = value; } } return machineMap; } #endregion } }