Merge Filtering into DatTools

This commit is contained in:
Matt Nadareski
2024-10-30 11:26:56 -04:00
parent 9e637021b1
commit 0e5572ff18
19 changed files with 19 additions and 82 deletions

View File

@@ -0,0 +1,341 @@
using System;
using System.Collections.Generic;
using System.IO;
using SabreTools.Core.Tools;
using SabreTools.DatFiles;
using SabreTools.DatItems;
using SabreTools.IO.Logging;
namespace SabreTools.DatTools
{
/// <summary>
/// Represents the cleaning operations that need to be performed on a set of items, usually a DAT
/// </summary>
public class Cleaner
{
#region Fields
/// <summary>
/// Clean all names to WoD standards
/// </summary>
public bool Clean { get; set; }
/// <summary>
/// Deduplicate items using the given method
/// </summary>
public DedupeType DedupeRoms { get; set; }
/// <summary>
/// Set Machine Description from Machine Name
/// </summary>
public bool DescriptionAsName { get; set; }
/// <summary>
/// Keep machines that don't contain any items
/// </summary>
public bool KeepEmptyGames { get; set; }
/// <summary>
/// Enable "One Rom, One Region (1G1R)" mode
/// </summary>
public bool OneGamePerRegion { get; set; }
/// <summary>
/// Ordered list of regions for "One Rom, One Region (1G1R)" mode
/// </summary>
public List<string>? RegionList { get; set; }
/// <summary>
/// Ensure each rom is in their own game
/// </summary>
public bool OneRomPerGame { get; set; }
/// <summary>
/// Remove all unicode characters
/// </summary>
public bool RemoveUnicode { get; set; }
/// <summary>
/// Include root directory when determing trim sizes
/// </summary>
public string? Root { get; set; }
/// <summary>
/// Remove scene dates from the beginning of machine names
/// </summary>
public bool SceneDateStrip { get; set; }
/// <summary>
/// Change all machine names to "!"
/// </summary>
public bool Single { get; set; }
/// <summary>
/// Trim total machine and item name to not exceed NTFS limits
/// </summary>
public bool Trim { get; set; }
#endregion
#region Logging
/// <summary>
/// Logging object
/// </summary>
private readonly Logger logger = new();
#endregion
#region Running
/// <summary>
/// Apply cleaning methods to the DatFile
/// </summary>
/// <param name="datFile">Current DatFile object to run operations on</param>
/// <param name="throwOnError">True if the error that is thrown should be thrown back to the caller, false otherwise</param>
/// <returns>True if cleaning was successful, false on error</returns>
public bool ApplyCleaning(DatFile datFile, bool throwOnError = false)
{
InternalStopwatch watch = new("Applying cleaning steps to DAT");
try
{
// Perform item-level cleaning
CleanDatItems(datFile);
CleanDatItemsDB(datFile);
// Bucket and dedupe according to the flag
if (DedupeRoms == DedupeType.Full)
{
datFile.Items.BucketBy(ItemKey.CRC, DedupeRoms);
datFile.ItemsDB.BucketBy(ItemKey.CRC, DedupeRoms);
}
else if (DedupeRoms == DedupeType.Game)
{
datFile.Items.BucketBy(ItemKey.Machine, DedupeRoms);
datFile.ItemsDB.BucketBy(ItemKey.Machine, DedupeRoms);
}
// Process description to machine name
if (DescriptionAsName == true)
{
datFile.Items.MachineDescriptionToName(throwOnError);
datFile.ItemsDB.MachineDescriptionToName(throwOnError);
}
// If we are removing scene dates, do that now
if (SceneDateStrip == true)
{
datFile.Items.StripSceneDatesFromItems();
datFile.ItemsDB.StripSceneDatesFromItems();
}
// Run the one rom per game logic, if required
if (OneGamePerRegion == true && RegionList != null)
{
datFile.Items.SetOneGamePerRegion(RegionList);
datFile.ItemsDB.SetOneGamePerRegion(RegionList);
}
// Run the one rom per game logic, if required
if (OneRomPerGame == true)
{
datFile.Items.SetOneRomPerGame();
datFile.ItemsDB.SetOneRomPerGame();
}
// Remove all marked items
datFile.Items.ClearMarked();
datFile.ItemsDB.ClearMarked();
// We remove any blanks, if we aren't supposed to have any
if (KeepEmptyGames == false)
{
datFile.Items.ClearEmpty();
datFile.ItemsDB.ClearEmpty();
}
}
catch (Exception ex) when (!throwOnError)
{
logger.Error(ex);
return false;
}
finally
{
watch.Stop();
}
return true;
}
/// <summary>
/// Clean individual items based on the current filter
/// </summary>
/// <param name="datFile">Current DatFile object to run operations on</param>
internal void CleanDatItems(DatFile datFile)
{
List<string> keys = [.. datFile.Items.Keys];
foreach (string key in keys)
{
// For every item in the current key
var items = datFile.Items[key];
if (items == null)
continue;
foreach (DatItem item in items)
{
// If we have a null item, we can't clean it it
if (item == null)
continue;
// Run cleaning per item
CleanDatItem(item);
}
// Assign back for caution
datFile.Items[key] = items;
}
}
/// <summary>
/// Clean individual items based on the current filter
/// </summary>
/// <param name="datFile">Current DatFile object to run operations on</param>
internal void CleanDatItemsDB(DatFile datFile)
{
List<string> keys = [.. datFile.ItemsDB.SortedKeys];
foreach (string key in keys)
{
// For every item in the current key
var items = datFile.ItemsDB.GetItemsForBucket(key);
if (items == null)
continue;
foreach ((long, DatItem) item in items)
{
// If we have a null item, we can't clean it it
if (item.Item2 == null)
continue;
// Run cleaning per item
CleanDatItemDB(datFile.ItemsDB, item);
}
}
}
/// <summary>
/// Clean a DatItem according to the cleaner
/// </summary>
/// <param name="datItem">DatItem to clean</param>
internal void CleanDatItem(DatItem datItem)
{
// Get the machine associated with the item, if possible
var machine = datItem.GetFieldValue<Machine>(DatItem.MachineKey);
if (machine == null)
return;
// Get the fields for processing
string? machineName = machine.GetStringFieldValue(Models.Metadata.Machine.NameKey);
string? machineDesc = machine.GetStringFieldValue(Models.Metadata.Machine.DescriptionKey);
string? datItemName = datItem.GetName();
// If we're stripping unicode characters, strip machine name and description
if (RemoveUnicode)
{
machineName = TextHelper.RemoveUnicodeCharacters(machineName);
machineDesc = TextHelper.RemoveUnicodeCharacters(machineDesc);
datItemName = TextHelper.RemoveUnicodeCharacters(datItemName);
}
// If we're in cleaning mode, sanitize machine name and description
if (Clean)
{
machineName = TextHelper.NormalizeCharacters(machineName);
machineDesc = TextHelper.NormalizeCharacters(machineDesc);
}
// If we are in single game mode, rename the machine
if (Single)
{
machineName = "!";
machineDesc = "!";
}
// If we are in NTFS trim mode, trim the item name
if (Trim && datItemName != null)
{
// Windows max name length is 260
int usableLength = 260 - (machineName?.Length ?? 0) - (Root?.Length ?? 0);
if (datItemName.Length > usableLength)
{
string ext = Path.GetExtension(datItemName);
datItemName = datItemName.Substring(0, usableLength - ext.Length) + ext;
}
}
// Set the fields back, if necessary
machine.SetFieldValue<string?>(Models.Metadata.Machine.NameKey, machineName);
machine.SetFieldValue<string?>(Models.Metadata.Machine.DescriptionKey, machineDesc);
datItem.SetName(datItemName);
}
/// <summary>
/// Clean a DatItem according to the cleaner
/// </summary>
/// <param name="db">ItemDictionaryDB to get machine information from</param>
/// <param name="datItem">DatItem to clean</param>
internal void CleanDatItemDB(ItemDictionaryDB db, (long, DatItem) datItem)
{
// Get the machine associated with the item, if possible
var machine = db.GetMachineForItem(datItem.Item1);
if (machine.Item2 == null)
return;
// Get the fields for processing
string? machineName = machine.Item2.GetStringFieldValue(Models.Metadata.Machine.NameKey);
string? machineDesc = machine.Item2.GetStringFieldValue(Models.Metadata.Machine.DescriptionKey);
string? datItemName = datItem.Item2.GetName();
// If we're stripping unicode characters, strip machine name and description
if (RemoveUnicode)
{
machineName = TextHelper.RemoveUnicodeCharacters(machineName);
machineDesc = TextHelper.RemoveUnicodeCharacters(machineDesc);
datItemName = TextHelper.RemoveUnicodeCharacters(datItemName);
}
// If we're in cleaning mode, sanitize machine name and description
if (Clean)
{
machineName = TextHelper.NormalizeCharacters(machineName);
machineDesc = TextHelper.NormalizeCharacters(machineDesc);
}
// If we are in single game mode, rename the machine
if (Single)
{
machineName = "!";
machineDesc = "!";
}
// If we are in NTFS trim mode, trim the item name
if (Trim && datItemName != null)
{
// Windows max name length is 260
int usableLength = 260 - (machineName?.Length ?? 0) - (Root?.Length ?? 0);
if (datItemName.Length > usableLength)
{
string ext = Path.GetExtension(datItemName);
datItemName = datItemName.Substring(0, usableLength - ext.Length) + ext;
}
}
// Set the fields back, if necessary
machine.Item2.SetFieldValue<string?>(Models.Metadata.Machine.NameKey, machineName);
machine.Item2.SetFieldValue<string?>(Models.Metadata.Machine.DescriptionKey, machineDesc);
datItem.Item2.SetName(datItemName);
}
#endregion
}
}

View File

@@ -6,7 +6,6 @@ using System.Threading.Tasks;
#endif
using SabreTools.DatFiles;
using SabreTools.DatItems;
using SabreTools.Filtering;
using SabreTools.IO;
using SabreTools.IO.Logging;

View File

@@ -0,0 +1,234 @@
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
/// <summary>
/// List of extras to apply
/// </summary>
public readonly List<ExtraIniItem> Items = [];
#endregion
#region Logging
/// <summary>
/// Logging object
/// </summary>
private readonly Logger logger;
#endregion
#region Constructors
/// <summary>
/// Constructor
/// </summary>
public ExtraIni()
{
logger = new Logger(this);
}
#endregion
#region Population
/// <summary>
/// Populate item using field:file inputs
/// </summary>
/// <param name="inputs">Field and file combinations</param>
public void PopulateFromList(List<string> 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
/// <summary>
/// Apply a set of Extra INIs on the DatFile
/// </summary>
/// <param name="datFile">Current DatFile object to run operations on</param>
/// <param name="throwOnError">True if the error that is thrown should be thrown back to the caller, false otherwise</param>
/// <returns>True if the extras were applied, false on error</returns>
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.Count == 0)
return true;
var watch = new InternalStopwatch("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 combinedMaps = CombineExtras();
var machines = combinedMaps.Keys;
// 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
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<Machine>(DatItem.MachineKey));
setter.SetFields(datItem);
}
}
}
catch (Exception ex) when (!throwOnError)
{
logger.Error(ex);
return false;
}
finally
{
watch.Stop();
}
return true;
}
/// <summary>
/// Apply a set of Extra INIs on the DatFile
/// </summary>
/// <param name="datFile">Current DatFile object to run operations on</param>
/// <param name="throwOnError">True if the error that is thrown should be thrown back to the caller, false otherwise</param>
/// <returns>True if the extras were applied, false on error</returns>
public bool ApplyExtrasDB(DatFile datFile, bool throwOnError = false)
{
// If we have no extras, don't attempt to apply and just return true
if (Items == null || Items.Count == 0)
return true;
var watch = new InternalStopwatch("Applying extra mappings to DAT");
try
{
// Bucket by game first
datFile.ItemsDB.BucketBy(ItemKey.Machine, DedupeType.None);
// 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.ItemsDB.GetItemsForBucket(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.ItemsDB.GetMachineForItem(datItem.Item1);
if (machine.Item2 != null)
setter.SetFields(machine.Item2);
setter.SetFields(datItem.Item2);
}
}
}
catch (Exception ex) when (!throwOnError)
{
logger.Error(ex);
return false;
}
finally
{
watch.Stop();
}
return true;
}
/// <summary>
/// Combine ExtraIni fields
/// </summary>
/// <returns>Mapping dictionary from machine name to field mapping</returns>
private Dictionary<string, Dictionary<FilterKey, string>> CombineExtras()
{
var machineMap = new Dictionary<string, Dictionary<FilterKey, string>>();
// 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
}
}

View File

@@ -0,0 +1,258 @@
using System;
using SabreTools.Core.Tools;
using SabreTools.DatFiles;
using SabreTools.DatItems;
using SabreTools.IO.Logging;
namespace SabreTools.DatTools
{
public class MergeSplit
{
#region Fields
/// <summary>
/// Splitting mode to apply
/// </summary>
public MergingFlag SplitType { get; set; }
#endregion
#region Logging
/// <summary>
/// Logging object
/// </summary>
private static readonly Logger logger = new();
#endregion
// TODO: Should any of these create a new DatFile in the process?
// The reason this comes up is that doing any of the splits or merges
// is an inherently destructive process. Making it output a new DatFile
// might make it easier to deal with multiple internal steps. On the other
// hand, this will increase memory usage significantly and would force the
// existing paths to behave entirely differently
#region Running
/// <summary>
/// Apply splitting on the DatFile
/// </summary>
/// <param name="datFile">Current DatFile object to run operations on</param>
/// <param name="useTags">True if DatFile tags override splitting, false otherwise</param>
/// <param name="throwOnError">True if the error that is thrown should be thrown back to the caller, false otherwise</param>
/// <returns>True if the DatFile was split, false on error</returns>
public bool ApplySplitting(DatFile datFile, bool useTags, bool throwOnError = false)
{
InternalStopwatch watch = new("Applying splitting to DAT");
try
{
// If we are using tags from the DAT, set the proper input for split type unless overridden
if (useTags && SplitType == MergingFlag.None)
SplitType = datFile.Header.GetStringFieldValue(Models.Metadata.Header.ForceMergingKey).AsEnumValue<MergingFlag>();
// Run internal splitting
switch (SplitType)
{
// Standard
case MergingFlag.None:
// No-op
break;
case MergingFlag.Split:
CreateSplitSets(datFile);
break;
case MergingFlag.Merged:
CreateMergedSets(datFile);
break;
case MergingFlag.NonMerged:
CreateNonMergedSets(datFile);
break;
// Nonstandard
case MergingFlag.FullMerged:
CreateFullyMergedSets(datFile);
break;
case MergingFlag.DeviceNonMerged:
CreateDeviceNonMergedSets(datFile);
break;
case MergingFlag.FullNonMerged:
CreateFullyNonMergedSets(datFile);
break;
}
}
catch (Exception ex) when (!throwOnError)
{
logger.Error(ex);
return false;
}
finally
{
watch.Stop();
}
return true;
}
/// <summary>
/// Use cdevice_ref tags to get full non-merged sets and remove parenting tags
/// </summary>
/// <param name="datFile">Current DatFile object to run operations on</param>
internal static void CreateDeviceNonMergedSets(DatFile datFile)
{
logger.User("Creating device non-merged sets from the DAT");
// For sake of ease, the first thing we want to do is bucket by game
datFile.Items.BucketBy(ItemKey.Machine, DedupeType.None, norename: true);
datFile.ItemsDB.BucketBy(ItemKey.Machine, DedupeType.None, norename: true);
// Now we want to loop through all of the games and set the correct information
while (datFile.Items.AddRomsFromDevices(false, false)) ;
while (datFile.ItemsDB.AddRomsFromDevices(false, false)) ;
while (datFile.Items.AddRomsFromDevices(true, false)) ;
while (datFile.ItemsDB.AddRomsFromDevices(true, false)) ;
// Then, remove the romof and cloneof tags so it's not picked up by the manager
datFile.Items.RemoveTagsFromChild();
datFile.ItemsDB.RemoveTagsFromChild();
}
/// <summary>
/// Use cloneof tags to create merged sets and remove the tags plus deduplicating if tags don't catch everything
/// </summary>
/// <param name="datFile">Current DatFile object to run operations on</param>
internal static void CreateFullyMergedSets(DatFile datFile)
{
logger.User("Creating fully merged sets from the DAT");
// For sake of ease, the first thing we want to do is bucket by game
datFile.Items.BucketBy(ItemKey.Machine, DedupeType.None, norename: true);
datFile.ItemsDB.BucketBy(ItemKey.Machine, DedupeType.None, norename: true);
// Now we want to loop through all of the games and set the correct information
datFile.Items.AddRomsFromChildren(true, false);
datFile.ItemsDB.AddRomsFromChildren(true, false);
// Now that we have looped through the cloneof tags, we loop through the romof tags
datFile.Items.RemoveBiosRomsFromChild(false);
datFile.ItemsDB.RemoveBiosRomsFromChild(false);
datFile.Items.RemoveBiosRomsFromChild(true);
datFile.ItemsDB.RemoveBiosRomsFromChild(true);
// Finally, remove the romof and cloneof tags so it's not picked up by the manager
datFile.Items.RemoveTagsFromChild();
datFile.ItemsDB.RemoveTagsFromChild();
}
/// <summary>
/// Use cloneof tags to create non-merged sets and remove the tags plus using the device_ref tags to get full sets
/// </summary>
/// <param name="datFile">Current DatFile object to run operations on</param>
internal static void CreateFullyNonMergedSets(DatFile datFile)
{
logger.User("Creating fully non-merged sets from the DAT");
// For sake of ease, the first thing we want to do is bucket by game
datFile.Items.BucketBy(ItemKey.Machine, DedupeType.None, norename: true);
datFile.ItemsDB.BucketBy(ItemKey.Machine, DedupeType.None, norename: true);
// Now we want to loop through all of the games and set the correct information
while (datFile.Items.AddRomsFromDevices(true, true)) ;
while (datFile.ItemsDB.AddRomsFromDevices(true, true)) ;
datFile.Items.AddRomsFromDevices(false, true);
datFile.ItemsDB.AddRomsFromDevices(false, true);
datFile.Items.AddRomsFromParent();
datFile.ItemsDB.AddRomsFromParent();
// Now that we have looped through the cloneof tags, we loop through the romof tags
datFile.Items.AddRomsFromBios();
datFile.ItemsDB.AddRomsFromBios();
// Then, remove the romof and cloneof tags so it's not picked up by the manager
datFile.Items.RemoveTagsFromChild();
datFile.ItemsDB.RemoveTagsFromChild();
}
/// <summary>
/// Use cloneof tags to create merged sets and remove the tags
/// </summary>
/// <param name="datFile">Current DatFile object to run operations on</param>
internal static void CreateMergedSets(DatFile datFile)
{
logger.User("Creating merged sets from the DAT");
// For sake of ease, the first thing we want to do is bucket by game
datFile.Items.BucketBy(ItemKey.Machine, DedupeType.None, norename: true);
datFile.ItemsDB.BucketBy(ItemKey.Machine, DedupeType.None, norename: true);
// Now we want to loop through all of the games and set the correct information
datFile.Items.AddRomsFromChildren(true, true);
datFile.ItemsDB.AddRomsFromChildren(true, true);
// Now that we have looped through the cloneof tags, we loop through the romof tags
datFile.Items.RemoveBiosRomsFromChild(false);
datFile.ItemsDB.RemoveBiosRomsFromChild(false);
datFile.Items.RemoveBiosRomsFromChild(true);
datFile.ItemsDB.RemoveBiosRomsFromChild(true);
// Finally, remove the romof and cloneof tags so it's not picked up by the manager
datFile.Items.RemoveTagsFromChild();
datFile.ItemsDB.RemoveTagsFromChild();
}
/// <summary>
/// Use cloneof tags to create non-merged sets and remove the tags
/// </summary>
/// <param name="datFile">Current DatFile object to run operations on</param>
internal static void CreateNonMergedSets(DatFile datFile)
{
logger.User("Creating non-merged sets from the DAT");
// For sake of ease, the first thing we want to do is bucket by game
datFile.Items.BucketBy(ItemKey.Machine, DedupeType.None, norename: true);
datFile.ItemsDB.BucketBy(ItemKey.Machine, DedupeType.None, norename: true);
// Now we want to loop through all of the games and set the correct information
datFile.Items.AddRomsFromParent();
datFile.ItemsDB.AddRomsFromParent();
// Now that we have looped through the cloneof tags, we loop through the romof tags
datFile.Items.RemoveBiosRomsFromChild(false);
datFile.ItemsDB.RemoveBiosRomsFromChild(false);
datFile.Items.RemoveBiosRomsFromChild(true);
datFile.ItemsDB.RemoveBiosRomsFromChild(true);
// Finally, remove the romof and cloneof tags so it's not picked up by the manager
datFile.Items.RemoveTagsFromChild();
datFile.ItemsDB.RemoveTagsFromChild();
}
/// <summary>
/// Use cloneof and romof tags to create split sets and remove the tags
/// </summary>
/// <param name="datFile">Current DatFile object to run operations on</param>
internal static void CreateSplitSets(DatFile datFile)
{
logger.User("Creating split sets from the DAT");
// For sake of ease, the first thing we want to do is bucket by game
datFile.Items.BucketBy(ItemKey.Machine, DedupeType.None, norename: true);
datFile.ItemsDB.BucketBy(ItemKey.Machine, DedupeType.None, norename: true);
// Now we want to loop through all of the games and set the correct information
datFile.Items.RemoveRomsFromChild();
datFile.ItemsDB.RemoveRomsFromChild();
// Now that we have looped through the cloneof tags, we loop through the romof tags
datFile.Items.RemoveBiosRomsFromChild(false);
datFile.ItemsDB.RemoveBiosRomsFromChild(false);
datFile.Items.RemoveBiosRomsFromChild(true);
datFile.ItemsDB.RemoveBiosRomsFromChild(true);
// Finally, remove the romof and cloneof tags so it's not picked up by the manager
datFile.Items.RemoveTagsFromChild();
datFile.ItemsDB.RemoveTagsFromChild();
}
#endregion
}
}

View File

@@ -0,0 +1,139 @@
using System.Collections.Generic;
using SabreTools.Core.Filter;
using SabreTools.DatFiles;
using SabreTools.IO.Logging;
namespace SabreTools.DatTools
{
/// <summary>
/// Represents the removal operations that need to be performed on a set of items, usually a DAT
/// </summary>
public class Remover
{
#region Fields
/// <summary>
/// List of header fields to exclude from writing
/// </summary>
public readonly List<string> HeaderFieldNames = [];
/// <summary>
/// List of machine fields to exclude from writing
/// </summary>
public readonly List<string> MachineFieldNames = [];
/// <summary>
/// List of fields to exclude from writing
/// </summary>
public readonly Dictionary<string, List<string>> ItemFieldNames = [];
#endregion
#region Logging
/// <summary>
/// Logging object
/// </summary>
protected Logger logger;
#endregion
#region Constructors
/// <summary>
/// Constructor
/// </summary>
public Remover()
{
logger = new Logger(this);
}
#endregion
#region Population
/// <summary>
/// Populate the exclusion objects using a field name
/// </summary>
/// <param name="field">Field name</param>
public void PopulateExclusions(string field)
=> PopulateExclusionsFromList([field]);
/// <summary>
/// Populate the exclusion objects using a set of field names
/// </summary>
/// <param name="fields">List of field names</param>
public void PopulateExclusionsFromList(List<string>? fields)
{
// If the list is null or empty, just return
if (fields == null || fields.Count == 0)
return;
var watch = new InternalStopwatch("Populating removals from list");
foreach (string field in fields)
{
bool removerSet = SetRemover(field);
if (!removerSet)
logger.Warning($"The value {field} did not match any known field names. Please check the wiki for more details on supported field names.");
}
watch.Stop();
}
/// <summary>
/// Set remover from a value
/// </summary>
/// <param name="field">Key for the remover to be set</param>
private bool SetRemover(string field)
{
// If the key is null or empty, return false
if (string.IsNullOrEmpty(field))
return false;
// Get the parser pair out of it, if possible
try
{
var key = new FilterKey(field);
switch (key.ItemName)
{
case Models.Metadata.MetadataFile.HeaderKey:
HeaderFieldNames.Add(key.FieldName);
return true;
case Models.Metadata.MetadataFile.MachineKey:
MachineFieldNames.Add(key.FieldName);
return true;
default:
if (!ItemFieldNames.ContainsKey(key.ItemName))
ItemFieldNames[key.ItemName] = [];
ItemFieldNames[key.ItemName].Add(key.FieldName);
return true;
}
}
catch
{
return false;
}
}
#endregion
#region Running
/// <summary>
/// Remove fields from a DatFile
/// </summary>
/// <param name="datFile">Current DatFile object to run operations on</param>
public void ApplyRemovals(DatFile datFile)
{
InternalStopwatch watch = new("Applying removals to DAT");
datFile.ApplyRemovals(HeaderFieldNames, MachineFieldNames, ItemFieldNames);
watch.Stop();
}
#endregion
}
}

View File

@@ -0,0 +1,204 @@
using System.Collections.Generic;
using System.Linq;
using SabreTools.Core.Tools;
using SabreTools.DatItems;
using SabreTools.DatItems.Formats;
namespace SabreTools.DatTools
{
/// <summary>
/// Replace fields in DatItems
/// </summary>
public static class Replacer
{
/// <summary>
/// Replace fields with given values
/// </summary>
/// <param name="machine">Machine to replace fields in</param>
/// <param name="repMachine">Machine to pull new information from</param>
/// <param name="machineFieldNames">List of fields representing what should be updated</param>
/// <param name="onlySame">True if descriptions should only be replaced if the game name is the same, false otherwise</param>
public static void ReplaceFields(Machine machine, Machine repMachine, List<string> machineFieldNames, bool onlySame)
{
// If we have an invalid input, return
if (machine == null || repMachine == null || machineFieldNames == null)
return;
// Loop through and replace fields
foreach (string fieldName in machineFieldNames)
{
// Special case for description
if (machineFieldNames.Contains(Models.Metadata.Machine.DescriptionKey))
{
if (!onlySame || (onlySame && machine.GetStringFieldValue(Models.Metadata.Machine.NameKey) == machine.GetStringFieldValue(Models.Metadata.Machine.DescriptionKey)))
machine.SetFieldValue<string?>(Models.Metadata.Machine.DescriptionKey, repMachine.GetStringFieldValue(Models.Metadata.Machine.DescriptionKey));
continue;
}
machine.ReplaceField(repMachine, fieldName);
}
}
/// <summary>
/// Replace fields with given values
/// </summary>
/// <param name="datItem">DatItem to replace fields in</param>
/// <param name="repDatItem">DatItem to pull new information from</param>
/// <param name="itemFieldNames">List of fields representing what should be updated</param>
public static void ReplaceFields(DatItem datItem, DatItem repDatItem, Dictionary<string, List<string>> itemFieldNames)
{
// If we have an invalid input, return
if (datItem == null || repDatItem == null || itemFieldNames == null)
return;
#region Common
if (datItem.GetStringFieldValue(Models.Metadata.DatItem.TypeKey) != repDatItem.GetStringFieldValue(Models.Metadata.DatItem.TypeKey))
return;
// If there are no field names for this type or generic, return
string? itemType = datItem.GetStringFieldValue(Models.Metadata.DatItem.TypeKey).AsEnumValue<ItemType>().AsStringValue();
if (itemType == null || (!itemFieldNames.ContainsKey(itemType) && !itemFieldNames.ContainsKey("item")))
return;
// Get the combined list of fields to remove
var fieldNames = new List<string>();
if (itemFieldNames.ContainsKey(itemType))
fieldNames.AddRange(itemFieldNames[itemType]);
if (itemFieldNames.ContainsKey("item"))
fieldNames.AddRange(itemFieldNames["item"]);
fieldNames = fieldNames.Distinct().ToList();
// If the field specifically contains Name, set it separately
if (fieldNames.Contains(Models.Metadata.Rom.NameKey))
datItem.SetName(repDatItem.GetName());
#endregion
#region Item-Specific
// Handle normal sets first
foreach (var fieldName in fieldNames)
{
datItem.ReplaceField(repDatItem, fieldName);
}
// TODO: Filter out hashes before here so these checks actually work
// Handle special cases
switch (datItem, repDatItem)
{
case (Disk disk, Disk repDisk): ReplaceFields(disk, repDisk, fieldNames); break;
case (Media media, Media repMedia): ReplaceFields(media, repMedia, fieldNames); break;
case (Rom rom, Rom repRom): ReplaceFields(rom, repRom, fieldNames); break;
}
#endregion
}
/// <summary>
/// Replace fields with given values
/// </summary>
/// <param name="disk">Disk to remove replace fields in</param>
/// <param name="newItem">Disk to pull new information from</param>
/// <param name="datItemFields">List of fields representing what should be updated</param>
private static void ReplaceFields(Disk disk, Disk newItem, List<string> datItemFields)
{
if (datItemFields.Contains(Models.Metadata.Disk.MD5Key))
{
if (string.IsNullOrEmpty(disk.GetStringFieldValue(Models.Metadata.Disk.MD5Key)) && !string.IsNullOrEmpty(newItem.GetStringFieldValue(Models.Metadata.Disk.MD5Key)))
disk.SetFieldValue<string?>(Models.Metadata.Disk.MD5Key, newItem.GetStringFieldValue(Models.Metadata.Disk.MD5Key));
}
if (datItemFields.Contains(Models.Metadata.Disk.SHA1Key))
{
if (string.IsNullOrEmpty(disk.GetStringFieldValue(Models.Metadata.Disk.SHA1Key)) && !string.IsNullOrEmpty(newItem.GetStringFieldValue(Models.Metadata.Disk.SHA1Key)))
disk.SetFieldValue<string?>(Models.Metadata.Disk.SHA1Key, newItem.GetStringFieldValue(Models.Metadata.Disk.SHA1Key));
}
}
/// <summary>
/// Replace fields with given values
/// </summary>
/// <param name="media">Media to remove replace fields in</param>
/// <param name="newItem">Media to pull new information from</param>
/// <param name="datItemFields">List of fields representing what should be updated</param>
private static void ReplaceFields(Media media, Media newItem, List<string> datItemFields)
{
if (datItemFields.Contains(Models.Metadata.Media.MD5Key))
{
if (string.IsNullOrEmpty(media.GetStringFieldValue(Models.Metadata.Media.MD5Key)) && !string.IsNullOrEmpty(newItem.GetStringFieldValue(Models.Metadata.Media.MD5Key)))
media.SetFieldValue<string?>(Models.Metadata.Media.MD5Key, newItem.GetStringFieldValue(Models.Metadata.Media.MD5Key));
}
if (datItemFields.Contains(Models.Metadata.Media.SHA1Key))
{
if (string.IsNullOrEmpty(media.GetStringFieldValue(Models.Metadata.Media.SHA1Key)) && !string.IsNullOrEmpty(newItem.GetStringFieldValue(Models.Metadata.Media.SHA1Key)))
media.SetFieldValue<string?>(Models.Metadata.Media.SHA1Key, newItem.GetStringFieldValue(Models.Metadata.Media.SHA1Key));
}
if (datItemFields.Contains(Models.Metadata.Media.SHA256Key))
{
if (string.IsNullOrEmpty(media.GetStringFieldValue(Models.Metadata.Media.SHA256Key)) && !string.IsNullOrEmpty(newItem.GetStringFieldValue(Models.Metadata.Media.SHA256Key)))
media.SetFieldValue<string?>(Models.Metadata.Media.SHA256Key, newItem.GetStringFieldValue(Models.Metadata.Media.SHA256Key));
}
if (datItemFields.Contains(Models.Metadata.Media.SpamSumKey))
{
if (string.IsNullOrEmpty(media.GetStringFieldValue(Models.Metadata.Media.SpamSumKey)) && !string.IsNullOrEmpty(newItem.GetStringFieldValue(Models.Metadata.Media.SpamSumKey)))
media.SetFieldValue<string?>(Models.Metadata.Media.SpamSumKey, newItem.GetStringFieldValue(Models.Metadata.Media.SpamSumKey));
}
}
/// <summary>
/// Replace fields with given values
/// </summary>
/// <param name="rom">Rom to remove replace fields in</param>
/// <param name="newItem">Rom to pull new information from</param>
/// <param name="datItemFields">List of fields representing what should be updated</param>
private static void ReplaceFields(Rom rom, Rom newItem, List<string> datItemFields)
{
if (datItemFields.Contains(Models.Metadata.Rom.CRCKey))
{
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Models.Metadata.Rom.CRCKey)) && !string.IsNullOrEmpty(newItem.GetStringFieldValue(Models.Metadata.Rom.CRCKey)))
rom.SetFieldValue<string?>(Models.Metadata.Rom.CRCKey, newItem.GetStringFieldValue(Models.Metadata.Rom.CRCKey));
}
if (datItemFields.Contains(Models.Metadata.Rom.MD5Key))
{
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Models.Metadata.Rom.MD5Key)) && !string.IsNullOrEmpty(newItem.GetStringFieldValue(Models.Metadata.Rom.MD5Key)))
rom.SetFieldValue<string?>(Models.Metadata.Rom.MD5Key, newItem.GetStringFieldValue(Models.Metadata.Rom.MD5Key));
}
if (datItemFields.Contains(Models.Metadata.Rom.SHA1Key))
{
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Models.Metadata.Rom.SHA1Key)) && !string.IsNullOrEmpty(newItem.GetStringFieldValue(Models.Metadata.Rom.SHA1Key)))
rom.SetFieldValue<string?>(Models.Metadata.Rom.SHA1Key, newItem.GetStringFieldValue(Models.Metadata.Rom.SHA1Key));
}
if (datItemFields.Contains(Models.Metadata.Rom.SHA256Key))
{
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Models.Metadata.Rom.SHA256Key)) && !string.IsNullOrEmpty(newItem.GetStringFieldValue(Models.Metadata.Rom.SHA256Key)))
rom.SetFieldValue<string?>(Models.Metadata.Rom.SHA256Key, newItem.GetStringFieldValue(Models.Metadata.Rom.SHA256Key));
}
if (datItemFields.Contains(Models.Metadata.Rom.SHA384Key))
{
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Models.Metadata.Rom.SHA384Key)) && !string.IsNullOrEmpty(newItem.GetStringFieldValue(Models.Metadata.Rom.SHA384Key)))
rom.SetFieldValue<string?>(Models.Metadata.Rom.SHA384Key, newItem.GetStringFieldValue(Models.Metadata.Rom.SHA384Key));
}
if (datItemFields.Contains(Models.Metadata.Rom.SHA512Key))
{
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Models.Metadata.Rom.SHA512Key)) && !string.IsNullOrEmpty(newItem.GetStringFieldValue(Models.Metadata.Rom.SHA512Key)))
rom.SetFieldValue<string?>(Models.Metadata.Rom.SHA512Key, newItem.GetStringFieldValue(Models.Metadata.Rom.SHA512Key));
}
if (datItemFields.Contains(Models.Metadata.Rom.SpamSumKey))
{
if (string.IsNullOrEmpty(rom.GetStringFieldValue(Models.Metadata.Rom.SpamSumKey)) && !string.IsNullOrEmpty(newItem.GetStringFieldValue(Models.Metadata.Rom.SpamSumKey)))
rom.SetFieldValue<string?>(Models.Metadata.Rom.SpamSumKey, newItem.GetStringFieldValue(Models.Metadata.Rom.SpamSumKey));
}
}
}
}

View File

@@ -33,12 +33,15 @@
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<InternalsVisibleTo Include="SabreTools.Test" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SabreTools.Core\SabreTools.Core.csproj" />
<ProjectReference Include="..\SabreTools.DatFiles\SabreTools.DatFiles.csproj" />
<ProjectReference Include="..\SabreTools.DatItems\SabreTools.DatItems.csproj" />
<ProjectReference Include="..\SabreTools.FileTypes\SabreTools.FileTypes.csproj" />
<ProjectReference Include="..\SabreTools.Filtering\SabreTools.Filtering.csproj" />
<ProjectReference Include="..\SabreTools.Reports\SabreTools.Reports.csproj" />
</ItemGroup>

View File

@@ -0,0 +1,465 @@
using System.Collections.Generic;
using System.Linq;
using SabreTools.Core.Filter;
using SabreTools.Core.Tools;
using SabreTools.DatFiles;
using SabreTools.DatItems;
using SabreTools.DatItems.Formats;
using SabreTools.IO.Logging;
namespace SabreTools.DatTools
{
/// <summary>
/// Set fields on DatItems
/// </summary>
public class Setter
{
#region Fields
/// <summary>
/// Mappings to set DatHeader fields
/// </summary>
public Dictionary<string, string> HeaderFieldMappings { get; } = [];
/// <summary>
/// Mappings to set Machine fields
/// </summary>
public Dictionary<string, string> MachineFieldMappings { get; } = [];
/// <summary>
/// Mappings to set DatItem fields
/// </summary>
public Dictionary<FilterKey, string> ItemFieldMappings { get; } = [];
#endregion
#region Logging
/// <summary>
/// Logging object
/// </summary>
private readonly Logger logger = new();
#endregion
#region Population
/// <summary>
/// Populate the setters using a field name and a value
/// </summary>
/// <param name="field">Field name</param>
/// <param name="value">Field value</param>
public void PopulateSetters(FilterKey field, string value)
=> PopulateSettersFromList([field], [value]);
/// <summary>
/// Populate the setters using a set of field names
/// </summary>
/// <param name="fields">List of field names</param>
/// <param name="values">List of field values</param>
public void PopulateSettersFromList(List<FilterKey> fields, List<string> values)
{
// If the list is null or empty, just return
if (values == null || values.Count == 0)
return;
var watch = new InternalStopwatch("Populating setters from list");
// Now we loop through and get values for everything
for (int i = 0; i < fields.Count; i++)
{
FilterKey field = fields[i];
string value = values[i];
if (!SetSetter(field, value))
logger.Warning($"The value {value} did not match any known field names. Please check the wiki for more details on supported field names.");
}
watch.Stop();
}
/// <summary>
/// Populate the setters using a set of field names
/// </summary>
/// <param name="mappings">Dictionary of mappings</param>
public void PopulateSettersFromDictionary(Dictionary<FilterKey, string>? mappings)
{
// If the dictionary is null or empty, just return
if (mappings == null || mappings.Count == 0)
return;
var watch = new InternalStopwatch("Populating setters from dictionary");
// Now we loop through and get values for everything
foreach (var mapping in mappings)
{
FilterKey field = mapping.Key;
string value = mapping.Value;
if (!SetSetter(field, value))
logger.Warning($"The value {value} did not match any known field names. Please check the wiki for more details on supported field names.");
}
watch.Stop();
}
/// <summary>
/// Set remover from a value
/// </summary>
/// <param name="key">Key for the remover to be set</param>
private bool SetSetter(FilterKey key, string value)
{
switch (key.ItemName)
{
case Models.Metadata.MetadataFile.HeaderKey:
HeaderFieldMappings[key.FieldName] = value;
return true;
case Models.Metadata.MetadataFile.MachineKey:
MachineFieldMappings[key.FieldName] = value;
return true;
default:
ItemFieldMappings[key] = value;
return true;
}
}
#endregion
/// <summary>
/// Set fields with given values
/// </summary>
/// <param name="datHeader">DatHeader to set fields on</param>
public void SetFields(DatHeader datHeader)
{
// If we have an invalid input, return
if (datHeader == null || HeaderFieldMappings.Count == 0)
return;
foreach (var kvp in HeaderFieldMappings)
{
datHeader.SetField(kvp.Key, kvp.Value);
}
}
/// <summary>
/// Set fields with given values
/// </summary>
/// <param name="machine">Machine to set fields on</param>
public void SetFields(Machine? machine)
{
// If we have an invalid input, return
if (machine == null || MachineFieldMappings.Count == 0)
return;
foreach (var kvp in MachineFieldMappings)
{
machine.SetField(kvp.Key, kvp.Value);
}
}
/// <summary>
/// Set fields with given values
/// </summary>
/// <param name="datItem">DatItem to set fields on</param>
public void SetFields(DatItem datItem)
{
// If we have an invalid input, return
if (datItem == null)
return;
#region Common
// Handle Machine fields
if (MachineFieldMappings.Count > 0 && datItem.GetFieldValue<Machine>(DatItem.MachineKey) != null)
SetFields(datItem.GetFieldValue<Machine>(DatItem.MachineKey)!);
// If there are no field names, return
if (ItemFieldMappings == null || ItemFieldMappings.Count == 0)
return;
// If there are no field names for this type or generic, return
string? itemType = datItem.GetStringFieldValue(Models.Metadata.DatItem.TypeKey).AsEnumValue<ItemType>().AsStringValue();
if (itemType == null || (!ItemFieldMappings.Keys.Any(kvp => kvp.ItemName == itemType) && !ItemFieldMappings.Keys.Any(kvp => kvp.ItemName == "item")))
return;
// Get the combined list of fields to remove
var fieldMappings = new Dictionary<string, string>();
foreach (var mapping in ItemFieldMappings.Where(kvp => kvp.Key.ItemName == "item").ToDictionary(kvp => kvp.Key.FieldName, kvp => kvp.Value))
{
fieldMappings[mapping.Key] = mapping.Value;
}
foreach (var mapping in ItemFieldMappings.Where(kvp => kvp.Key.ItemName == itemType).ToDictionary(kvp => kvp.Key.FieldName, kvp => kvp.Value))
{
fieldMappings[mapping.Key] = mapping.Value;
}
// If the field specifically contains Name, set it separately
if (fieldMappings.Keys.Contains(Models.Metadata.Rom.NameKey))
{
datItem.SetName(fieldMappings[Models.Metadata.Rom.NameKey]);
fieldMappings.Remove(Models.Metadata.Rom.NameKey);
}
#endregion
#region Item-Specific
// Handle unnested sets first
foreach (var kvp in fieldMappings)
{
datItem.SetField(kvp.Key, kvp.Value);
}
// Handle nested sets
switch (datItem)
{
case Adjuster adjuster: SetFields(adjuster); break;
case Configuration condition: SetFields(condition); break;
case ConfSetting confSetting: SetFields(confSetting); break;
case Device device: SetFields(device); break;
case DipSwitch dipSwitch: SetFields(dipSwitch); break;
case DipValue dipValue: SetFields(dipValue); break;
case Disk disk: SetFields(disk); break;
case Input input: SetFields(input); break;
case Part part: SetFields(part); break;
case Port port: SetFields(port); break;
case Rom rom: SetFields(rom); break;
case Slot slot: SetFields(slot); break;
}
#endregion
}
/// <summary>
/// Set fields with given values
/// </summary>
/// <param name="adjuster">Adjuster to remove replace fields in</param>
private void SetFields(Adjuster adjuster)
{
// Field.DatItem_Conditions does not apply here
if (adjuster.ConditionsSpecified)
{
foreach (Condition subCondition in adjuster.GetFieldValue<Condition[]?>(Models.Metadata.Adjuster.ConditionKey)!)
{
SetFields(subCondition);
}
}
}
/// <summary>
/// Set fields with given values
/// </summary>
/// <param name="configuration">Configuration to remove replace fields in</param>
private void SetFields(Configuration configuration)
{
if (configuration.ConditionsSpecified)
{
foreach (Condition subCondition in configuration.GetFieldValue<Condition[]?>(Models.Metadata.Configuration.ConditionKey)!)
{
SetFields(subCondition);
}
}
if (configuration.LocationsSpecified)
{
foreach (ConfLocation subLocation in configuration.GetFieldValue<ConfLocation[]?>(Models.Metadata.Configuration.ConfLocationKey)!)
{
SetFields(subLocation);
}
}
if (configuration.SettingsSpecified)
{
foreach (ConfSetting subSetting in configuration.GetFieldValue<ConfSetting[]?>(Models.Metadata.Configuration.ConfSettingKey)!)
{
SetFields(subSetting as DatItem);
}
}
}
/// <summary>
/// Set fields with given values
/// </summary>
/// <param name="confSetting">ConfSetting to remove replace fields in</param>
private void SetFields(ConfSetting confSetting)
{
if (confSetting.ConditionsSpecified)
{
foreach (Condition subCondition in confSetting.GetFieldValue<Condition[]?>(Models.Metadata.ConfSetting.ConditionKey)!)
{
SetFields(subCondition);
}
}
}
/// <summary>
/// Set fields with given values
/// </summary>
/// <param name="device">Device to remove replace fields in</param>
private void SetFields(Device device)
{
if (device.ExtensionsSpecified)
{
foreach (Extension subExtension in device.GetFieldValue<Extension[]?>(Models.Metadata.Device.ExtensionKey)!)
{
SetFields(subExtension);
}
}
if (device.InstancesSpecified)
{
foreach (Instance subInstance in device.GetFieldValue<Instance[]?>(Models.Metadata.Device.InstanceKey)!)
{
SetFields(subInstance);
}
}
}
/// <summary>
/// Set fields with given values
/// </summary>
/// <param name="dipSwitch">DipSwitch to remove replace fields in</param>
private void SetFields(DipSwitch dipSwitch)
{
if (dipSwitch.ConditionsSpecified)
{
foreach (Condition subCondition in dipSwitch.GetFieldValue<Condition[]?>(Models.Metadata.DipSwitch.ConditionKey)!)
{
SetFields(subCondition);
}
}
if (dipSwitch.LocationsSpecified)
{
foreach (DipLocation subLocation in dipSwitch.GetFieldValue<DipLocation[]?>(Models.Metadata.DipSwitch.DipLocationKey)!)
{
SetFields(subLocation);
}
}
if (dipSwitch.ValuesSpecified)
{
foreach (DipValue subValue in dipSwitch.GetFieldValue<DipValue[]?>(Models.Metadata.DipSwitch.DipValueKey)!)
{
SetFields(subValue as DatItem);
}
}
if (!dipSwitch.PartSpecified)
dipSwitch.SetFieldValue<Part?>(DipSwitch.PartKey, new Part());
SetFields((dipSwitch.GetFieldValue<Part?>(DipSwitch.PartKey) as DatItem)!);
}
/// <summary>
/// Set fields with given values
/// </summary>
/// <param name="dipValue">DipValue to remove replace fields in</param>
private void SetFields(DipValue dipValue)
{
if (dipValue.ConditionsSpecified)
{
foreach (Condition subCondition in dipValue.GetFieldValue<Condition[]?>(Models.Metadata.DipValue.ConditionKey)!)
{
SetFields(subCondition);
}
}
}
/// <summary>
/// Set fields with given values
/// </summary>
/// <param name="disk">Disk to remove replace fields in</param>
private void SetFields(Disk disk)
{
if (!disk.DiskAreaSpecified)
disk.SetFieldValue<DiskArea?>(Disk.DiskAreaKey, new DiskArea());
SetFields(disk.GetFieldValue<DiskArea?>(Disk.DiskAreaKey)! as DatItem);
if (!disk.PartSpecified)
disk.SetFieldValue<Part?>(Disk.PartKey, new Part());
SetFields(disk.GetFieldValue<Part?>(Disk.PartKey)! as DatItem);
}
/// <summary>
/// Set fields with given values
/// </summary>
/// <param name="input">Input to remove replace fields in</param>
private void SetFields(Input input)
{
if (input.ControlsSpecified)
{
foreach (Control subControl in input.GetFieldValue<Control[]?>(Models.Metadata.Input.ControlKey)!)
{
SetFields(subControl);
}
}
}
/// <summary>s
/// Set fields with given values
/// </summary>
/// <param name="part">Part to remove replace fields in</param>
private void SetFields(Part part)
{
if (part.FeaturesSpecified)
{
foreach (PartFeature subPartFeature in part.GetFieldValue<PartFeature[]?>(Models.Metadata.Part.FeatureKey)!)
{
SetFields(subPartFeature);
}
}
}
/// <summary>
/// Set fields with given values
/// </summary>
/// <param name="port">Port to remove replace fields in</param>
private void SetFields(Port port)
{
if (port.AnalogsSpecified)
{
foreach (Analog subAnalog in port.GetFieldValue<Analog[]?>(Models.Metadata.Port.AnalogKey)!)
{
SetFields(subAnalog);
}
}
}
/// <summary>
/// Set fields with given values
/// </summary>
/// <param name="rom">Rom to remove replace fields in</param>
private void SetFields(Rom rom)
{
if (!rom.DataAreaSpecified)
rom.SetFieldValue<DataArea?>(Rom.DataAreaKey, new DataArea());
SetFields(rom.GetFieldValue<DataArea?>(Rom.DataAreaKey)! as DatItem);
if (!rom.PartSpecified)
rom.SetFieldValue<Part?>(Rom.PartKey, new Part());
SetFields(rom.GetFieldValue<Part?>(Rom.PartKey)! as DatItem);
}
/// <summary>
/// Set fields with given values
/// </summary>
/// <param name="slot">Slot to remove replace fields in</param>
private void SetFields(Slot slot)
{
if (slot.SlotOptionsSpecified)
{
foreach (SlotOption subSlotOption in slot.GetFieldValue<SlotOption[]?>(Models.Metadata.Slot.SlotOptionKey)!)
{
SetFields(subSlotOption);
}
}
}
}
}