using System; using System.Collections.Generic; using System.IO; using System.Linq; using SabreTools.Core; using SabreTools.DatItems; using SabreTools.DatItems.Formats; namespace SabreTools.DatFiles.Formats { /// /// Represents writing a hashfile such as an SFV, MD5, or SHA-1 file /// internal partial class Hashfile : DatFile { /// protected override ItemType[] GetSupportedTypes() { return new ItemType[] { ItemType.Disk, ItemType.Media, ItemType.Rom }; } /// protected override List GetMissingRequiredFields(DatItem datItem) { List missingFields = new(); // Check item name if (string.IsNullOrWhiteSpace(datItem.GetName())) missingFields.Add(DatItemField.Name); // Check hash linked to specific Hashfile type switch (_hash) { case Hash.CRC: switch (datItem.ItemType) { case ItemType.Rom: if (string.IsNullOrEmpty((datItem as Rom)?.CRC)) missingFields.Add(DatItemField.CRC); break; default: missingFields.Add(DatItemField.CRC); break; } break; case Hash.MD5: switch (datItem.ItemType) { case ItemType.Disk: if (string.IsNullOrEmpty((datItem as Disk)?.MD5)) missingFields.Add(DatItemField.MD5); break; case ItemType.Media: if (string.IsNullOrEmpty((datItem as Media)?.MD5)) missingFields.Add(DatItemField.MD5); break; case ItemType.Rom: if (string.IsNullOrEmpty((datItem as Rom)?.MD5)) missingFields.Add(DatItemField.MD5); break; default: missingFields.Add(DatItemField.MD5); break; } break; case Hash.SHA1: switch (datItem.ItemType) { case ItemType.Disk: if (string.IsNullOrEmpty((datItem as Disk)?.SHA1)) missingFields.Add(DatItemField.SHA1); break; case ItemType.Media: if (string.IsNullOrEmpty((datItem as Media)?.SHA1)) missingFields.Add(DatItemField.SHA1); break; case ItemType.Rom: if (string.IsNullOrEmpty((datItem as Rom)?.SHA1)) missingFields.Add(DatItemField.SHA1); break; default: missingFields.Add(DatItemField.SHA1); break; } break; case Hash.SHA256: switch (datItem.ItemType) { case ItemType.Media: if (string.IsNullOrEmpty((datItem as Media)?.SHA256)) missingFields.Add(DatItemField.SHA256); break; case ItemType.Rom: if (string.IsNullOrEmpty((datItem as Rom)?.SHA256)) missingFields.Add(DatItemField.SHA256); break; default: missingFields.Add(DatItemField.SHA256); break; } break; case Hash.SHA384: switch (datItem.ItemType) { case ItemType.Rom: if (string.IsNullOrEmpty((datItem as Rom)?.SHA384)) missingFields.Add(DatItemField.SHA384); break; default: missingFields.Add(DatItemField.SHA384); break; } break; case Hash.SHA512: switch (datItem.ItemType) { case ItemType.Rom: if (string.IsNullOrEmpty((datItem as Rom)?.SHA512)) missingFields.Add(DatItemField.SHA512); break; default: missingFields.Add(DatItemField.SHA512); break; } break; case Hash.SpamSum: switch (datItem.ItemType) { case ItemType.Media: if (string.IsNullOrEmpty((datItem as Media)?.SpamSum)) missingFields.Add(DatItemField.SpamSum); break; case ItemType.Rom: if (string.IsNullOrEmpty((datItem as Rom)?.SpamSum)) missingFields.Add(DatItemField.SpamSum); break; default: missingFields.Add(DatItemField.SpamSum); break; } break; } return missingFields; } /// public override bool WriteToFile(string outfile, bool ignoreblanks = false, bool throwOnError = false) { try { logger.User($"Writing to '{outfile}'..."); var hashfile = CreateHashFile(ignoreblanks); if (!Serialization.Hashfile.SerializeToFile(hashfile, outfile, _hash)) { logger.Warning($"File '{outfile}' could not be written! See the log for more details."); return false; } } catch (Exception ex) when (!throwOnError) { logger.Error(ex); return false; } return true; } #region Converters /// /// Create a Hashfile from the current internal information /// /// True if blank roms should be skipped on output, false otherwise private Models.Hashfile.Hashfile CreateHashFile(bool ignoreblanks) { var hashfile = new Models.Hashfile.Hashfile(); switch (_hash) { case Hash.CRC: hashfile.SFV = CreateSFV(ignoreblanks); break; case Hash.MD5: hashfile.MD5 = CreateMD5(ignoreblanks); break; case Hash.SHA1: hashfile.SHA1 = CreateSHA1(ignoreblanks); break; case Hash.SHA256: hashfile.SHA256 = CreateSHA256(ignoreblanks); break; case Hash.SHA384: hashfile.SHA384 = CreateSHA384(ignoreblanks); break; case Hash.SHA512: hashfile.SHA512 = CreateSHA512(ignoreblanks); break; case Hash.SpamSum: hashfile.SpamSum = CreateSpamSum(ignoreblanks); break; } return hashfile; } /// /// Create an array of SFV /// /// True if blank roms should be skipped on output, false otherwise private Models.Hashfile.SFV[]? CreateSFV(bool ignoreblanks) { // Create a list of hold the SFVs var sfvs = new List(); // Loop through the sorted items and create items for them foreach (string key in Items.SortedKeys) { var items = Items.FilteredItems(key); if (items == null || !items.Any()) continue; // Loop through and convert the items to respective lists for (int index = 0; index < items.Count; index++) { // Get the item var item = items[index]; // Check for a "null" item item = ProcessNullifiedItem(item); // Skip if we're ignoring the item if (ShouldIgnore(item, ignoreblanks)) continue; string name = string.Empty; if (Header.GameName) name = $"{item.Machine.Name}{Path.DirectorySeparatorChar}"; switch (item) { case Rom rom: sfvs.Add(new Models.Hashfile.SFV { File = name + rom.Name, Hash = rom.CRC, }); break; } } } return sfvs.ToArray(); } /// /// Create an array of MD5 /// /// True if blank roms should be skipped on output, false otherwise private Models.Hashfile.MD5[]? CreateMD5(bool ignoreblanks) { // Create a list of hold the MD5s var md5s = new List(); // Loop through the sorted items and create items for them foreach (string key in Items.SortedKeys) { var items = Items.FilteredItems(key); if (items == null || !items.Any()) continue; // Loop through and convert the items to respective lists for (int index = 0; index < items.Count; index++) { // Get the item var item = items[index]; // Check for a "null" item item = ProcessNullifiedItem(item); // Skip if we're ignoring the item if (ShouldIgnore(item, ignoreblanks)) continue; string name = string.Empty; if (Header.GameName) name = $"{item.Machine.Name}{Path.DirectorySeparatorChar}"; switch (item) { case Disk disk: md5s.Add(new Models.Hashfile.MD5 { Hash = disk.MD5, File = name + disk.Name, }); break; case Media media: md5s.Add(new Models.Hashfile.MD5 { Hash = media.MD5, File = name + media.Name, }); break; case Rom rom: md5s.Add(new Models.Hashfile.MD5 { Hash = rom.MD5, File = name + rom.Name, }); break; } } } return md5s.ToArray(); } /// /// Create an array of SHA1 /// /// True if blank roms should be skipped on output, false otherwise private Models.Hashfile.SHA1[]? CreateSHA1(bool ignoreblanks) { // Create a list of hold the SHA1s var sha1s = new List(); // Loop through the sorted items and create items for them foreach (string key in Items.SortedKeys) { var items = Items.FilteredItems(key); if (items == null || !items.Any()) continue; // Loop through and convert the items to respective lists for (int index = 0; index < items.Count; index++) { // Get the item var item = items[index]; // Check for a "null" item item = ProcessNullifiedItem(item); // Skip if we're ignoring the item if (ShouldIgnore(item, ignoreblanks)) continue; string name = string.Empty; if (Header.GameName) name = $"{item.Machine.Name}{Path.DirectorySeparatorChar}"; switch (item) { case Disk disk: sha1s.Add(new Models.Hashfile.SHA1 { Hash = disk.SHA1, File = name + disk.Name, }); break; case Media media: sha1s.Add(new Models.Hashfile.SHA1 { Hash = media.SHA1, File = name + media.Name, }); break; case Rom rom: sha1s.Add(new Models.Hashfile.SHA1 { Hash = rom.SHA1, File = name + rom.Name, }); break; } } } return sha1s.ToArray(); } /// /// Create an array of SHA256 /// /// True if blank roms should be skipped on output, false otherwise private Models.Hashfile.SHA256[]? CreateSHA256(bool ignoreblanks) { // Create a list of hold the SHA256s var sha256s = new List(); // Loop through the sorted items and create items for them foreach (string key in Items.SortedKeys) { var items = Items.FilteredItems(key); if (items == null || !items.Any()) continue; // Loop through and convert the items to respective lists for (int index = 0; index < items.Count; index++) { // Get the item var item = items[index]; // Check for a "null" item item = ProcessNullifiedItem(item); // Skip if we're ignoring the item if (ShouldIgnore(item, ignoreblanks)) continue; string name = string.Empty; if (Header.GameName) name = $"{item.Machine.Name}{Path.DirectorySeparatorChar}"; switch (item) { case Media media: sha256s.Add(new Models.Hashfile.SHA256 { Hash = media.SHA256, File = name + media.Name, }); break; case Rom rom: sha256s.Add(new Models.Hashfile.SHA256 { Hash = rom.SHA256, File = name + rom.Name, }); break; } } } return sha256s.ToArray(); } /// /// Create an array of SHA384 /// /// True if blank roms should be skipped on output, false otherwise private Models.Hashfile.SHA384[]? CreateSHA384(bool ignoreblanks) { // Create a list of hold the SHA384s var sha384s = new List(); // Loop through the sorted items and create items for them foreach (string key in Items.SortedKeys) { var items = Items.FilteredItems(key); if (items == null || !items.Any()) continue; // Loop through and convert the items to respective lists for (int index = 0; index < items.Count; index++) { // Get the item var item = items[index]; // Check for a "null" item item = ProcessNullifiedItem(item); // Skip if we're ignoring the item if (ShouldIgnore(item, ignoreblanks)) continue; string name = string.Empty; if (Header.GameName) name = $"{item.Machine.Name}{Path.DirectorySeparatorChar}"; switch (item) { case Rom rom: sha384s.Add(new Models.Hashfile.SHA384 { Hash = rom.SHA384, File = name + rom.Name, }); break; } } } return sha384s.ToArray(); } /// /// Create an array of SHA512 /// /// True if blank roms should be skipped on output, false otherwise private Models.Hashfile.SHA512[]? CreateSHA512(bool ignoreblanks) { // Create a list of hold the SHA512s var sha512s = new List(); // Loop through the sorted items and create items for them foreach (string key in Items.SortedKeys) { var items = Items.FilteredItems(key); if (items == null || !items.Any()) continue; // Loop through and convert the items to respective lists for (int index = 0; index < items.Count; index++) { // Get the item var item = items[index]; // Check for a "null" item item = ProcessNullifiedItem(item); // Skip if we're ignoring the item if (ShouldIgnore(item, ignoreblanks)) continue; string name = string.Empty; if (Header.GameName) name = $"{item.Machine.Name}{Path.DirectorySeparatorChar}"; switch (item) { case Rom rom: sha512s.Add(new Models.Hashfile.SHA512 { Hash = rom.SHA512, File = name + rom.Name, }); break; } } } return sha512s.ToArray(); } /// /// Create an array of SpamSum /// /// True if blank roms should be skipped on output, false otherwise private Models.Hashfile.SpamSum[]? CreateSpamSum(bool ignoreblanks) { // Create a list of hold the SpamSums var spamsums = new List(); // Loop through the sorted items and create items for them foreach (string key in Items.SortedKeys) { var items = Items.FilteredItems(key); if (items == null || !items.Any()) continue; // Loop through and convert the items to respective lists for (int index = 0; index < items.Count; index++) { // Get the item var item = items[index]; // Check for a "null" item item = ProcessNullifiedItem(item); // Skip if we're ignoring the item if (ShouldIgnore(item, ignoreblanks)) continue; string name = string.Empty; if (Header.GameName) name = $"{item.Machine.Name}{Path.DirectorySeparatorChar}"; switch (item) { case Media media: spamsums.Add(new Models.Hashfile.SpamSum { Hash = media.SpamSum, File = name + media.Name, }); break; case Rom rom: spamsums.Add(new Models.Hashfile.SpamSum { Hash = rom.SpamSum, File = name + rom.Name, }); break; } } } return spamsums.ToArray(); } #endregion } }