using System.Xml.Serialization; using Newtonsoft.Json; using SabreTools.Core; using SabreTools.Core.Tools; namespace SabreTools.DatItems.Formats { /// /// Represents Compressed Hunks of Data (CHD) formatted disks which use internal hashes /// [JsonObject("disk"), XmlRoot("disk")] public sealed class Disk : DatItem { #region Constants /// /// Non-standard key for inverted logic /// public const string DiskAreaKey = "DISKAREA"; /// /// Non-standard key for inverted logic /// public const string PartKey = "PART"; #endregion #region Fields /// /> protected override ItemType ItemType => ItemType.Disk; /// /> protected override string? NameKey => Models.Metadata.Disk.NameKey; [JsonIgnore] public bool DiskAreaSpecified { get { var diskArea = GetFieldValue(Disk.DiskAreaKey); return diskArea != null && !string.IsNullOrEmpty(diskArea.GetName()); } } [JsonIgnore] public bool PartSpecified { get { var part = GetFieldValue(Disk.PartKey); return part != null && (!string.IsNullOrEmpty(part.GetName()) || !string.IsNullOrEmpty(part.GetStringFieldValue(Models.Metadata.Part.InterfaceKey))); } } #endregion #region Constructors public Disk() : base() { SetFieldValue(DatItem.DupeTypeKey, 0x00); SetFieldValue(Models.Metadata.Disk.StatusKey, ItemStatus.None.AsStringValue()); } public Disk(Models.Metadata.Disk item) : base(item) { SetFieldValue(DatItem.DupeTypeKey, 0x00); // Process flag values if (GetBoolFieldValue(Models.Metadata.Disk.OptionalKey) != null) SetFieldValue(Models.Metadata.Disk.OptionalKey, GetBoolFieldValue(Models.Metadata.Disk.OptionalKey).FromYesNo()); if (GetStringFieldValue(Models.Metadata.Disk.StatusKey) != null) SetFieldValue(Models.Metadata.Disk.StatusKey, GetStringFieldValue(Models.Metadata.Disk.StatusKey).AsEnumValue().AsStringValue()); if (GetBoolFieldValue(Models.Metadata.Disk.WritableKey) != null) SetFieldValue(Models.Metadata.Disk.WritableKey, GetBoolFieldValue(Models.Metadata.Disk.WritableKey).FromYesNo()); // Process hash values if (GetStringFieldValue(Models.Metadata.Disk.MD5Key) != null) SetFieldValue(Models.Metadata.Disk.MD5Key, TextHelper.NormalizeMD5(GetStringFieldValue(Models.Metadata.Disk.MD5Key))); if (GetStringFieldValue(Models.Metadata.Disk.SHA1Key) != null) SetFieldValue(Models.Metadata.Disk.SHA1Key, TextHelper.NormalizeSHA1(GetStringFieldValue(Models.Metadata.Disk.SHA1Key))); } #endregion #region Cloning Methods /// /// Convert a disk to the closest Rom approximation /// /// public Rom ConvertToRom() { var rom = new Rom(_internal.ConvertToRom()!); // Create a DataArea if there was an existing DiskArea var diskArea = GetFieldValue(Disk.DiskAreaKey); if (diskArea != null) { var dataArea = new DataArea(); string? diskAreaName = diskArea.GetStringFieldValue(Models.Metadata.DiskArea.NameKey); dataArea.SetFieldValue(Models.Metadata.DataArea.NameKey, diskAreaName); rom.SetFieldValue(Rom.DataAreaKey, dataArea); } rom.SetFieldValue(DatItem.DupeTypeKey, GetFieldValue(DatItem.DupeTypeKey)); rom.SetFieldValue(DatItem.MachineKey, GetFieldValue(DatItem.MachineKey)?.Clone() as Machine); rom.SetFieldValue(Rom.PartKey, GetFieldValue(Disk.PartKey)?.Clone() as Part); rom.SetFieldValue(DatItem.RemoveKey, GetBoolFieldValue(DatItem.RemoveKey)); rom.SetFieldValue(DatItem.SourceKey, GetFieldValue(DatItem.SourceKey)?.Clone() as Source); return rom; } #endregion #region Comparision Methods /// /// Fill any missing size and hash information from another Disk /// /// Disk to fill information from public void FillMissingInformation(Disk other) => _internal.FillMissingHashes(other._internal); /// /// 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.MD5: key = GetStringFieldValue(Models.Metadata.Disk.MD5Key); break; case ItemKey.SHA1: key = GetStringFieldValue(Models.Metadata.Disk.SHA1Key); 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; if (lower) key = key.ToLowerInvariant(); return key; } /// public override string GetKeyDB(ItemKey bucketedBy, Machine? machine, Source? source, 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.MD5: key = GetStringFieldValue(Models.Metadata.Disk.MD5Key); break; case ItemKey.SHA1: key = GetStringFieldValue(Models.Metadata.Disk.SHA1Key); break; // Let the base handle generic stuff default: return base.GetKeyDB(bucketedBy, machine, source, lower, norename); } // Double and triple check the key for corner cases key ??= string.Empty; if (lower) key = key.ToLowerInvariant(); return key; } #endregion } }