using System.Xml.Serialization; using Newtonsoft.Json; using SabreTools.Core; using SabreTools.Core.Tools; using SabreTools.FileTypes; namespace SabreTools.DatItems.Formats { /// /// Represents Compressed Hunks of Data (CHD) formatted disks which use internal hashes /// [JsonObject("disk"), XmlRoot("disk")] public 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 [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.GetFieldValue(Models.Metadata.Part.InterfaceKey))); } } #endregion #region Accessors /// public override string? GetName() => GetFieldValue(Models.Metadata.Disk.NameKey); /// public override void SetName(string? name) => SetFieldValue(Models.Metadata.Disk.NameKey, name); #endregion #region Constructors /// /// Create a default, empty Disk object /// public Disk() { _internal = new Models.Metadata.Disk(); SetName(string.Empty); SetFieldValue(Models.Metadata.DatItem.TypeKey, ItemType.Disk); SetFieldValue(DatItem.DupeTypeKey, 0x00); SetFieldValue(DatItem.MachineKey, new Machine()); SetFieldValue(Models.Metadata.Disk.StatusKey, ItemStatus.None); } /// /// Create a Disk object from a BaseFile /// public Disk(BaseFile baseFile) { _internal = new Models.Metadata.Disk(); SetName(baseFile.Filename); SetFieldValue(Models.Metadata.Disk.MD5Key, TextHelper.ByteArrayToString(baseFile.MD5)); SetFieldValue(Models.Metadata.Disk.SHA1Key, TextHelper.ByteArrayToString(baseFile.SHA1)); SetFieldValue(Models.Metadata.DatItem.TypeKey, ItemType.Disk); SetFieldValue(DatItem.DupeTypeKey, 0x00); SetFieldValue(DatItem.MachineKey, new Machine()); SetFieldValue(Models.Metadata.Disk.StatusKey, ItemStatus.None); } /// /// Create a Disk object from the internal model /// public Disk(Models.Metadata.Disk item) { _internal = item; SetFieldValue(Models.Metadata.DatItem.TypeKey, ItemType.Disk); SetFieldValue(DatItem.MachineKey, new Machine()); } #endregion #region Cloning Methods /// public override object Clone() { return new Disk() { _internal = this._internal?.Clone() as Models.Metadata.Disk ?? [], }; } /// /// Convert Disk object to a BaseFile /// public BaseFile ConvertToBaseFile() { return new BaseFile() { Filename = this.GetName(), Parent = GetFieldValue(DatItem.MachineKey)!.GetFieldValue(Models.Metadata.Machine.NameKey), MD5 = TextHelper.StringToByteArray(GetFieldValue(Models.Metadata.Disk.MD5Key)), SHA1 = TextHelper.StringToByteArray(GetFieldValue(Models.Metadata.Disk.SHA1Key)), }; } /// /// Convert a disk to the closest Rom approximation /// /// public Rom ConvertToRom() { var rom = new Rom(_internal.ConvertToRom()!); rom.GetFieldValue(Rom.DataAreaKey)?.SetName(this.GetFieldValue(Disk.DiskAreaKey)?.GetName()); rom.SetFieldValue(DatItem.DupeTypeKey, GetFieldValue(DatItem.DupeTypeKey)); rom.SetFieldValue(DatItem.MachineKey, GetFieldValue(DatItem.MachineKey)!.Clone() as Machine ?? new Machine()); rom.SetFieldValue(DatItem.RemoveKey, GetFieldValue(DatItem.RemoveKey)); rom.SetFieldValue(DatItem.SourceKey, GetFieldValue(DatItem.SourceKey)); 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); /// /// Get unique duplicate suffix on name collision /// /// String representing the suffix public string GetDuplicateSuffix() => _internal.GetDuplicateSuffix(); #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 = GetFieldValue(Models.Metadata.Disk.MD5Key); break; case ItemKey.SHA1: key = GetFieldValue(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; return key; } #endregion } }