using System.Xml.Serialization; using Newtonsoft.Json; using SabreTools.Core; using SabreTools.Core.Tools; using SabreTools.FileTypes; namespace SabreTools.DatItems.Formats { /// /// Represents a generic file within a set /// [JsonObject("rom"), XmlRoot("rom")] public class Rom : DatItem { #region Constants /// /// Non-standard key for inverted logic /// public const string DataAreaKey = "DATAAREA"; /// /// Non-standard key for inverted logic /// public const string PartKey = "PART"; #endregion #region Fields [JsonIgnore] public bool ItemStatusSpecified { get { var status = GetFieldValue(Models.Metadata.Rom.StatusKey); return status != ItemStatus.NULL && status != ItemStatus.None; } } [JsonIgnore] public bool OriginalSpecified { get { var original = GetFieldValue("ORIGINAL"); return original != null && original != default; } } [JsonIgnore] public bool DataAreaSpecified { get { var dataArea = GetFieldValue(Rom.DataAreaKey); return dataArea != null && (!string.IsNullOrEmpty(dataArea.GetName()) || dataArea.GetFieldValue(Models.Metadata.DataArea.SizeKey) != null || dataArea.GetFieldValue(Models.Metadata.DataArea.WidthKey) != null || dataArea.GetFieldValue(Models.Metadata.DataArea.EndiannessKey) != Endianness.NULL); } } [JsonIgnore] public bool PartSpecified { get { var part = GetFieldValue(Rom.PartKey); return part != null && (!string.IsNullOrEmpty(part.GetName()) || !string.IsNullOrEmpty(part.GetFieldValue(Models.Metadata.Part.InterfaceKey))); } } #endregion #region Accessors /// public override string? GetName() => GetFieldValue(Models.Metadata.Rom.NameKey); /// public override void SetName(string? name) => SetFieldValue(Models.Metadata.Rom.NameKey, name); #endregion #region Constructors /// /// Create a default, empty Rom object /// public Rom() { _internal = new Models.Metadata.Rom(); SetName(null); SetFieldValue(Models.Metadata.DatItem.TypeKey, ItemType.Rom); SetFieldValue(DatItem.DupeTypeKey, 0x00); SetFieldValue(DatItem.MachineKey, new Machine()); SetFieldValue(Models.Metadata.Rom.StatusKey, ItemStatus.None); } /// /// Create a "blank" Rom object /// /// /// /// public Rom(string name, string machineName) { _internal = new Models.Metadata.Rom(); SetName(name); SetFieldValue(Models.Metadata.DatItem.TypeKey, ItemType.Rom); SetFieldValue(Models.Metadata.Rom.SizeKey, null); SetFieldValue(Models.Metadata.Rom.StatusKey, ItemStatus.None); SetFieldValue(DatItem.MachineKey, new Machine()); GetFieldValue(DatItem.MachineKey)!.SetFieldValue(Models.Metadata.Machine.DescriptionKey, machineName); GetFieldValue(DatItem.MachineKey)!.SetFieldValue(Models.Metadata.Machine.NameKey, machineName); } /// /// Create a Rom object from a BaseFile /// /// public Rom(BaseFile baseFile) { _internal = new Models.Metadata.Rom(); SetName(baseFile.Filename); SetFieldValue(Models.Metadata.Rom.SizeKey, baseFile.Size); SetFieldValue(Models.Metadata.Rom.CRCKey, TextHelper.ByteArrayToString(baseFile.CRC)); SetFieldValue(Models.Metadata.Rom.MD5Key, TextHelper.ByteArrayToString(baseFile.MD5)); SetFieldValue(Models.Metadata.Rom.SHA1Key, TextHelper.ByteArrayToString(baseFile.SHA1)); SetFieldValue(Models.Metadata.Rom.SHA256Key, TextHelper.ByteArrayToString(baseFile.SHA256)); SetFieldValue(Models.Metadata.Rom.SHA384Key, TextHelper.ByteArrayToString(baseFile.SHA384)); SetFieldValue(Models.Metadata.Rom.SHA512Key, TextHelper.ByteArrayToString(baseFile.SHA512)); SetFieldValue(Models.Metadata.Rom.SpamSumKey, System.Text.Encoding.UTF8.GetString(baseFile.SpamSum ?? [])); SetFieldValue(Models.Metadata.DatItem.TypeKey, ItemType.Rom); SetFieldValue(DatItem.DupeTypeKey, 0x00); SetFieldValue(DatItem.MachineKey, new Machine()); SetFieldValue(Models.Metadata.Rom.StatusKey, ItemStatus.None); SetFieldValue(Models.Metadata.Rom.DateKey, baseFile.Date); } /// /// Create a Rom object from the internal model /// public Rom(Models.Metadata.Rom item) { _internal = item; SetFieldValue(Models.Metadata.DatItem.TypeKey, ItemType.Rom); SetFieldValue(DatItem.MachineKey, new Machine()); } #endregion #region Cloning Methods /// public override object Clone() { return new Rom() { _internal = this._internal?.Clone() as Models.Metadata.Rom ?? [], }; } /// /// Convert Rom object to a BaseFile /// public BaseFile ConvertToBaseFile() { return new BaseFile() { Filename = GetName(), Parent = GetFieldValue(DatItem.MachineKey)!.GetFieldValue(Models.Metadata.Machine.NameKey), Date = GetFieldValue(Models.Metadata.Rom.DateKey), Size = GetFieldValue(Models.Metadata.Rom.SizeKey), CRC = TextHelper.StringToByteArray(GetFieldValue(Models.Metadata.Rom.CRCKey)), MD5 = TextHelper.StringToByteArray(GetFieldValue(Models.Metadata.Rom.MD5Key)), SHA1 = TextHelper.StringToByteArray(GetFieldValue(Models.Metadata.Rom.SHA1Key)), SHA256 = TextHelper.StringToByteArray(GetFieldValue(Models.Metadata.Rom.SHA256Key)), SHA384 = TextHelper.StringToByteArray(GetFieldValue(Models.Metadata.Rom.SHA384Key)), SHA512 = TextHelper.StringToByteArray(GetFieldValue(Models.Metadata.Rom.SHA512Key)), SpamSum = System.Text.Encoding.UTF8.GetBytes(GetFieldValue(Models.Metadata.Rom.SpamSumKey) ?? string.Empty), }; } #endregion #region Comparision Methods /// /// Fill any missing size and hash information from another Rom /// /// Rom to fill information from public void FillMissingInformation(Rom other) => _internal.FillMissingHashes(other?._internal); /// /// Get unique duplicate suffix on name collision /// /// String representing the suffix public string GetDuplicateSuffix() => _internal.GetDuplicateSuffix(); /// /// Returns if the Rom contains any hashes /// /// True if any hash exists, false otherwise public bool HasHashes() => _internal.HasHashes(); /// /// Returns if all of the hashes are set to their 0-byte values /// /// True if any hash matches the 0-byte value, false otherwise public bool HasZeroHash() => _internal.HasZeroHash(); #endregion #region Sorting and Merging /// public override string GetKey(ItemKey bucketedBy, bool lower = true, bool norename = true) { // Set the output key as the default blank string string? key; // Now determine what the key should be based on the bucketedBy value switch (bucketedBy) { case ItemKey.CRC: key = GetFieldValue(Models.Metadata.Rom.CRCKey); break; case ItemKey.MD5: key = GetFieldValue(Models.Metadata.Rom.MD5Key); break; case ItemKey.SHA1: key = GetFieldValue(Models.Metadata.Rom.SHA1Key); break; case ItemKey.SHA256: key = GetFieldValue(Models.Metadata.Rom.SHA256Key); break; case ItemKey.SHA384: key = GetFieldValue(Models.Metadata.Rom.SHA384Key); break; case ItemKey.SHA512: key = GetFieldValue(Models.Metadata.Rom.SHA512Key); break; case ItemKey.SpamSum: key = GetFieldValue(Models.Metadata.Rom.SpamSumKey); break; // Let the base handle generic stuff default: return base.GetKey(bucketedBy, lower, norename); } // Double and triple check the key for corner cases key ??= string.Empty; return key; } #endregion } }