diff --git a/SabreTools.DatFiles/DatFile.cs b/SabreTools.DatFiles/DatFile.cs index 9aac47fc..5a576bb2 100644 --- a/SabreTools.DatFiles/DatFile.cs +++ b/SabreTools.DatFiles/DatFile.cs @@ -36,6 +36,12 @@ namespace SabreTools.DatFiles [JsonProperty("items"), XmlElement("items")] public ItemDictionary Items { get; set; } = []; + /// + /// DatItems and related statistics + /// + [JsonProperty("items"), XmlElement("items")] + public ItemDictionaryDB ItemsDB { get; set; } = new ItemDictionaryDB(); + #endregion #region Logging @@ -172,7 +178,7 @@ namespace SabreTools.DatFiles public abstract void ParseFile(string filename, int indexId, bool keep, bool statsOnly = false, bool throwOnError = false); /// - /// Add a rom to the Dat after checking + /// Add a DatItem to the dictionary after checking /// /// Item data to check against /// True to only add item statistics while parsing, false otherwise @@ -180,6 +186,16 @@ namespace SabreTools.DatFiles protected string ParseAddHelper(DatItem item, bool statsOnly) => Items.AddItem(item, statsOnly); + /// + /// Add a DatItem to the dictionary after checking + /// + /// Item data to check against + /// Index of the machine to map the DatItem to + /// True to only add item statistics while parsing, false otherwise + /// The key for the item + protected long ParseAddHelper(DatItem item, long machineIndex, bool statsOnly) + => ItemsDB.AddItem(item, machineIndex, statsOnly); + #endregion #region Writing @@ -391,6 +407,36 @@ namespace SabreTools.DatFiles return rom; } + /// + /// Process any DatItems that are "null", usually created from directory population + /// + /// DatItem to check for "null" status + /// Cleaned DatItem + protected (long, DatItem) ProcessNullifiedItem((long, DatItem) datItem) + { + // If we don't have a Rom, we can ignore it + if (datItem.Item2 is not Rom rom) + return datItem; + + // If the Rom has "null" characteristics, ensure all fields + if (rom.GetInt64FieldValue(Models.Metadata.Rom.SizeKey) == null && rom.GetStringFieldValue(Models.Metadata.Rom.CRCKey) == "null") + { + logger.Verbose($"Empty folder found: {datItem.Item2.GetFieldValue(DatItem.MachineKey)!.GetStringFieldValue(Models.Metadata.Machine.NameKey)}"); + + rom.SetName(rom.GetName() == "null" ? "-" : rom.GetName()); + rom.SetFieldValue(Models.Metadata.Rom.SizeKey, Constants.SizeZero.ToString()); + rom.SetFieldValue(Models.Metadata.Rom.CRCKey, rom.GetStringFieldValue(Models.Metadata.Rom.CRCKey) == "null" ? Constants.CRCZero : null); + rom.SetFieldValue(Models.Metadata.Rom.MD5Key, rom.GetStringFieldValue(Models.Metadata.Rom.MD5Key) == "null" ? Constants.MD5Zero : null); + rom.SetFieldValue(Models.Metadata.Rom.SHA1Key, rom.GetStringFieldValue(Models.Metadata.Rom.SHA1Key) == "null" ? Constants.SHA1Zero : null); + rom.SetFieldValue(Models.Metadata.Rom.SHA256Key, rom.GetStringFieldValue(Models.Metadata.Rom.SHA256Key) == "null" ? Constants.SHA256Zero : null); + rom.SetFieldValue(Models.Metadata.Rom.SHA384Key, rom.GetStringFieldValue(Models.Metadata.Rom.SHA384Key) == "null" ? Constants.SHA384Zero : null); + rom.SetFieldValue(Models.Metadata.Rom.SHA512Key, rom.GetStringFieldValue(Models.Metadata.Rom.SHA512Key) == "null" ? Constants.SHA512Zero : null); + rom.SetFieldValue(Models.Metadata.Rom.SpamSumKey, rom.GetStringFieldValue(Models.Metadata.Rom.SpamSumKey) == "null" ? Constants.SpamSumZero : null); + } + + return (datItem.Item1, rom); + } + /// /// Get supported types for write /// @@ -427,6 +473,27 @@ namespace SabreTools.DatFiles return false; } + /// + /// Get if a machine contains any writable items + /// + /// DatItems to check + /// True if the machine contains at least one writable item, false otherwise + /// Empty machines are kept with this + protected bool ContainsWritable((long, DatItem)[]? datItems) + { + // Empty machines are considered writable + if (datItems == null || datItems.Length == 0) + return true; + + foreach ((long, DatItem) datItem in datItems) + { + if (GetSupportedTypes().Contains(datItem.Item2.GetStringFieldValue(Models.Metadata.DatItem.TypeKey).AsEnumValue())) + return true; + } + + return false; + } + /// /// Get if an item should be ignored on write /// @@ -495,6 +562,74 @@ namespace SabreTools.DatFiles return false; } + /// + /// Get if an item should be ignored on write + /// + /// DatItem to check + /// True if blank roms should be skipped on output, false otherwise + /// True if the item should be skipped on write, false otherwise + protected bool ShouldIgnore((long, DatItem?) datItem, bool ignoreBlanks) + { + // If this is invoked with a null DatItem, we ignore + if (datItem.Item1 < 0 || datItem.Item2 == null) + { + logger?.Verbose($"Item was skipped because it was null"); + return true; + } + + // If the item is supposed to be removed, we ignore + if (datItem.Item2.GetBoolFieldValue(DatItem.RemoveKey) == true) + { + string itemString = JsonConvert.SerializeObject(datItem, Formatting.None); + logger?.Verbose($"Item '{itemString}' was skipped because it was marked for removal"); + return true; + } + + // If we have the Blank dat item, we ignore + if (datItem.Item2 is Blank) + { + string itemString = JsonConvert.SerializeObject(datItem, Formatting.None); + logger?.Verbose($"Item '{itemString}' was skipped because it was of type 'Blank'"); + return true; + } + + // If we're ignoring blanks and we have a Rom + if (ignoreBlanks && datItem.Item2 is Rom rom) + { + // If we have a 0-size or blank rom, then we ignore + long? size = rom.GetInt64FieldValue(Models.Metadata.Rom.SizeKey); + if (size == 0 || size == null) + { + string itemString = JsonConvert.SerializeObject(datItem, Formatting.None); + logger?.Verbose($"Item '{itemString}' was skipped because it had an invalid size"); + return true; + } + } + + // If we have an item type not in the list of supported values + if (!GetSupportedTypes().Contains(datItem.Item2.GetStringFieldValue(Models.Metadata.DatItem.TypeKey).AsEnumValue())) + { + string itemString = JsonConvert.SerializeObject(datItem, Formatting.None); + logger?.Verbose($"Item '{itemString}' was skipped because it was not supported in {Header?.GetFieldValue(DatHeader.DatFormatKey)}"); + return true; + } + + // If we have an item with missing required fields + List? missingFields = GetMissingRequiredFields(datItem.Item2); + if (missingFields != null && missingFields.Count != 0) + { + string itemString = JsonConvert.SerializeObject(datItem, Formatting.None); +#if NET20 || NET35 + logger?.Verbose($"Item '{itemString}' was skipped because it was missing required fields for {Header?.GetFieldValue(DatHeader.DatFormatKey)}: {string.Join(", ", [.. missingFields])}"); +#else + logger?.Verbose($"Item '{itemString}' was skipped because it was missing required fields for {Header?.GetFieldValue(DatHeader.DatFormatKey)}: {string.Join(", ", missingFields)}"); +#endif + return true; + } + + return false; + } + #endregion }