using System; using System.Xml.Serialization; using Newtonsoft.Json; using SabreTools.Data.Extensions; using SabreTools.Metadata.Filter; using MergingFlag = SabreTools.Data.Models.Metadata.MergingFlag; using NodumpFlag = SabreTools.Data.Models.Metadata.NodumpFlag; using PackingFlag = SabreTools.Data.Models.Metadata.PackingFlag; namespace SabreTools.Metadata.DatFiles { /// /// Represents all possible DAT header information /// [JsonObject("header"), XmlRoot("header")] public sealed class DatHeader : ModelBackedItem, ICloneable { #region Constants /// /// Read or write format /// public const string DatFormatKey = "DATFORMAT"; /// /// External name of the DAT /// public const string FileNameKey = "FILENAME"; #endregion #region Fields [JsonIgnore] public bool CanOpenSpecified { get { var canOpen = ReadStringArray(Data.Models.Metadata.Header.CanOpenKey); return canOpen is not null && canOpen.Length > 0; } } [JsonIgnore] public bool ImagesSpecified { get { return Read(Data.Models.Metadata.Header.ImagesKey) is not null; } } [JsonIgnore] public bool InfosSpecified { get { return Read(Data.Models.Metadata.Header.InfosKey) is not null; } } [JsonIgnore] public bool NewDatSpecified { get { return Read(Data.Models.Metadata.Header.NewDatKey) is not null; } } [JsonIgnore] public bool SearchSpecified { get { return Read(Data.Models.Metadata.Header.SearchKey) is not null; } } #endregion #region Constructors public DatHeader() { } public DatHeader(Data.Models.Metadata.Header header) { // Create a new internal model _internal = []; // Get all fields to automatically copy without processing var nonItemFields = TypeHelper.GetConstants(typeof(Data.Models.Metadata.Header)); if (nonItemFields is not null) { // Populate the internal machine from non-filter fields foreach (string fieldName in nonItemFields) { if (header.TryGetValue(fieldName, out var fieldValue)) _internal[fieldName] = fieldValue; } } // Get all fields specific to the DatFiles implementation var nonStandardFields = TypeHelper.GetConstants(typeof(DatHeader)); if (nonStandardFields is not null) { // Populate the internal machine from filter fields foreach (string fieldName in nonStandardFields) { if (header.TryGetValue(fieldName, out var fieldValue)) _internal[fieldName] = fieldValue; } } // Get all no-filter fields if (header.TryGetValue(Data.Models.Metadata.Header.CanOpenKey, out var canOpenValue)) _internal[Data.Models.Metadata.Header.CanOpenKey] = canOpenValue; if (header.TryGetValue(Data.Models.Metadata.Header.ImagesKey, out var imageValue)) _internal[Data.Models.Metadata.Header.ImagesKey] = imageValue; if (header.TryGetValue(Data.Models.Metadata.Header.InfosKey, out var infosValue)) _internal[Data.Models.Metadata.Header.InfosKey] = infosValue; if (header.TryGetValue(Data.Models.Metadata.Header.NewDatKey, out var newDatValue)) _internal[Data.Models.Metadata.Header.NewDatKey] = newDatValue; if (header.TryGetValue(Data.Models.Metadata.Header.SearchKey, out var searchValue)) _internal[Data.Models.Metadata.Header.SearchKey] = searchValue; } #endregion #region Cloning Methods /// /// Clone the current header /// public object Clone() => new DatHeader(GetInternalClone()); /// /// Clone just the format from the current header /// public DatHeader CloneFormat() { var header = new DatHeader(); header.Write(DatFormatKey, Read(DatFormatKey)); return header; } /// /// Get a clone of the current internal model /// public Data.Models.Metadata.Header GetInternalClone() { var header = (_internal.Clone() as Data.Models.Metadata.Header)!; // Remove fields with default values if (header.ReadString(Data.Models.Metadata.Header.ForceMergingKey).AsMergingFlag() == MergingFlag.None) header.Remove(Data.Models.Metadata.Header.ForceMergingKey); if (header.ReadString(Data.Models.Metadata.Header.ForceNodumpKey).AsNodumpFlag() == NodumpFlag.None) header.Remove(Data.Models.Metadata.Header.ForceNodumpKey); if (header.ReadString(Data.Models.Metadata.Header.ForcePackingKey).AsPackingFlag() == PackingFlag.None) header.Remove(Data.Models.Metadata.Header.ForcePackingKey); if (header.ReadString(Data.Models.Metadata.Header.BiosModeKey).AsMergingFlag() == MergingFlag.None) header.Remove(Data.Models.Metadata.Header.BiosModeKey); if (header.ReadString(Data.Models.Metadata.Header.RomModeKey).AsMergingFlag() == MergingFlag.None) header.Remove(Data.Models.Metadata.Header.RomModeKey); if (header.ReadString(Data.Models.Metadata.Header.SampleModeKey).AsMergingFlag() == MergingFlag.None) header.Remove(Data.Models.Metadata.Header.SampleModeKey); // Convert subheader values if (CanOpenSpecified) header[Data.Models.Metadata.Header.CanOpenKey] = new Data.Models.OfflineList.CanOpen { Extension = ReadStringArray(Data.Models.Metadata.Header.CanOpenKey) }; if (ImagesSpecified) header[Data.Models.Metadata.Header.ImagesKey] = Read(Data.Models.Metadata.Header.ImagesKey); if (InfosSpecified) header[Data.Models.Metadata.Header.InfosKey] = Read(Data.Models.Metadata.Header.InfosKey); if (NewDatSpecified) header[Data.Models.Metadata.Header.NewDatKey] = Read(Data.Models.Metadata.Header.NewDatKey); if (SearchSpecified) header[Data.Models.Metadata.Header.SearchKey] = Read(Data.Models.Metadata.Header.SearchKey); return header; } #endregion #region Comparision Methods /// public override bool Equals(ModelBackedItem? other) { // If other is null if (other is null) return false; // If the type is mismatched if (other is not DatHeader otherItem) return false; // Compare internal models return _internal.EqualTo(otherItem._internal); } /// public override bool Equals(ModelBackedItem? other) { // If other is null if (other is null) return false; // If the type is mismatched if (other is not DatHeader otherItem) return false; // Compare internal models return _internal.EqualTo(otherItem._internal); } #endregion #region Manipulation /// /// Runs a filter and determines if it passes or not /// /// Filter runner to use for checking /// True if the Machine passes the filter, false otherwise public bool PassesFilter(FilterRunner filterRunner) => filterRunner.Run(_internal); #endregion } }