using System.Collections.Generic; using SabreTools.Core.Tools; using SabreTools.DatItems; using SabreTools.DatItems.Formats; using SabreTools.Hashing; namespace SabreTools.DatFiles { /// /// Statistics wrapper for outputting /// public class DatStatistics { #region Private instance variables /// /// Lock for statistics calculation /// private readonly object statsLock = new(); #endregion #region Fields /// /// Overall item count /// public long TotalCount { get; private set; } = 0; /// /// Number of items for each item type /// public Dictionary ItemCounts { get; private set; } = []; /// /// Number of machines /// /// Special count only used by statistics output public long GameCount { get; set; } = 0; /// /// Total uncompressed size /// public long TotalSize { get; private set; } = 0; /// /// Number of items for each hash type /// public Dictionary HashCounts { get; private set; } = []; /// /// Number of items for each item status /// public Dictionary StatusCounts { get; private set; } = []; /// /// Number of items with the remove flag /// public long RemovedCount { get; private set; } = 0; /// /// Name to display on output /// public string? DisplayName { get; set; } /// /// Total machine count to use on output /// public long MachineCount { get; set; } = 0; /// /// Determines if statistics are for a directory or not /// public bool IsDirectory { get; set; } = false; #endregion #region Accessors /// /// Add to the statistics given a DatItem /// /// Item to add info from public void AddItemStatistics(DatItem item) { lock (statsLock) { // No matter what the item is, we increment the count TotalCount++; // Increment removal count if (item.GetBoolFieldValue(DatItem.RemoveKey) == true) RemovedCount++; // Increment the item count for the type AddItemCount(item.GetStringFieldValue(Models.Metadata.DatItem.TypeKey).AsEnumValue()); // Some item types require special processing switch (item) { case Disk disk: if (disk.GetStringFieldValue(Models.Metadata.Disk.StatusKey).AsEnumValue() != ItemStatus.Nodump) { AddHashCount(HashType.MD5, string.IsNullOrEmpty(disk.GetStringFieldValue(Models.Metadata.Disk.MD5Key)) ? 0 : 1); AddHashCount(HashType.SHA1, string.IsNullOrEmpty(disk.GetStringFieldValue(Models.Metadata.Disk.SHA1Key)) ? 0 : 1); } AddStatusCount(ItemStatus.BadDump, disk.GetStringFieldValue(Models.Metadata.Disk.StatusKey).AsEnumValue() == ItemStatus.BadDump ? 1 : 0); AddStatusCount(ItemStatus.Good, disk.GetStringFieldValue(Models.Metadata.Disk.StatusKey).AsEnumValue() == ItemStatus.Good ? 1 : 0); AddStatusCount(ItemStatus.Nodump, disk.GetStringFieldValue(Models.Metadata.Disk.StatusKey).AsEnumValue() == ItemStatus.Nodump ? 1 : 0); AddStatusCount(ItemStatus.Verified, disk.GetStringFieldValue(Models.Metadata.Disk.StatusKey).AsEnumValue() == ItemStatus.Verified ? 1 : 0); break; case Media media: AddHashCount(HashType.MD5, string.IsNullOrEmpty(media.GetStringFieldValue(Models.Metadata.Media.MD5Key)) ? 0 : 1); AddHashCount(HashType.SHA1, string.IsNullOrEmpty(media.GetStringFieldValue(Models.Metadata.Media.SHA1Key)) ? 0 : 1); AddHashCount(HashType.SHA256, string.IsNullOrEmpty(media.GetStringFieldValue(Models.Metadata.Media.SHA256Key)) ? 0 : 1); AddHashCount(HashType.SpamSum, string.IsNullOrEmpty(media.GetStringFieldValue(Models.Metadata.Media.SpamSumKey)) ? 0 : 1); break; case Rom rom: if (rom.GetStringFieldValue(Models.Metadata.Rom.StatusKey).AsEnumValue() != ItemStatus.Nodump) { TotalSize += rom.GetInt64FieldValue(Models.Metadata.Rom.SizeKey) ?? 0; AddHashCount(HashType.CRC32, string.IsNullOrEmpty(rom.GetStringFieldValue(Models.Metadata.Rom.CRCKey)) ? 0 : 1); AddHashCount(HashType.MD5, string.IsNullOrEmpty(rom.GetStringFieldValue(Models.Metadata.Rom.MD5Key)) ? 0 : 1); AddHashCount(HashType.SHA1, string.IsNullOrEmpty(rom.GetStringFieldValue(Models.Metadata.Rom.SHA1Key)) ? 0 : 1); AddHashCount(HashType.SHA256, string.IsNullOrEmpty(rom.GetStringFieldValue(Models.Metadata.Rom.SHA256Key)) ? 0 : 1); AddHashCount(HashType.SHA384, string.IsNullOrEmpty(rom.GetStringFieldValue(Models.Metadata.Rom.SHA384Key)) ? 0 : 1); AddHashCount(HashType.SHA512, string.IsNullOrEmpty(rom.GetStringFieldValue(Models.Metadata.Rom.SHA512Key)) ? 0 : 1); AddHashCount(HashType.SpamSum, string.IsNullOrEmpty(rom.GetStringFieldValue(Models.Metadata.Rom.SpamSumKey)) ? 0 : 1); } AddStatusCount(ItemStatus.BadDump, rom.GetStringFieldValue(Models.Metadata.Rom.StatusKey).AsEnumValue() == ItemStatus.BadDump ? 1 : 0); AddStatusCount(ItemStatus.Good, rom.GetStringFieldValue(Models.Metadata.Rom.StatusKey).AsEnumValue() == ItemStatus.Good ? 1 : 0); AddStatusCount(ItemStatus.Nodump, rom.GetStringFieldValue(Models.Metadata.Rom.StatusKey).AsEnumValue() == ItemStatus.Nodump ? 1 : 0); AddStatusCount(ItemStatus.Verified, rom.GetStringFieldValue(Models.Metadata.Rom.StatusKey).AsEnumValue() == ItemStatus.Verified ? 1 : 0); break; } } } /// /// Add statistics from another DatStatistics object /// /// DatStatistics object to add from public void AddStatistics(DatStatistics stats) { TotalCount += stats.TotalCount; // Loop through and add stats for all items foreach (var itemCountKvp in stats.ItemCounts) { AddItemCount(itemCountKvp.Key, itemCountKvp.Value); } GameCount += stats.GameCount; TotalSize += stats.TotalSize; // Individual hash counts foreach (var hashCountKvp in stats.HashCounts) { AddHashCount(hashCountKvp.Key, hashCountKvp.Value); } // Individual status counts foreach (var statusCountKvp in stats.StatusCounts) { AddStatusCount(statusCountKvp.Key, statusCountKvp.Value); } RemovedCount += stats.RemovedCount; } /// /// Get the item count for a given hash type, defaulting to 0 if it does not exist /// /// Hash type to retrieve /// The number of items with that hash, if it exists public long GetHashCount(HashType hashType) { lock (HashCounts) { if (!HashCounts.ContainsKey(hashType)) return 0; return HashCounts[hashType]; } } /// /// Get the item count for a given item type, defaulting to 0 if it does not exist /// /// Item type to retrieve /// The number of items of that type, if it exists public long GetItemCount(ItemType itemType) { lock (ItemCounts) { if (!ItemCounts.ContainsKey(itemType)) return 0; return ItemCounts[itemType]; } } /// /// Get the item count for a given item status, defaulting to 0 if it does not exist /// /// Item status to retrieve /// The number of items of that type, if it exists public long GetStatusCount(ItemStatus itemStatus) { lock (StatusCounts) { if (!StatusCounts.ContainsKey(itemStatus)) return 0; return StatusCounts[itemStatus]; } } /// /// Increment the hash count for a given hash type /// /// Hash type to increment /// Amount to increment by, defaults to 1 private void AddHashCount(HashType hashType, long interval = 1) { lock (HashCounts) { if (!HashCounts.ContainsKey(hashType)) HashCounts[hashType] = 0; HashCounts[hashType] += interval; if (HashCounts[hashType] < 0) HashCounts[hashType] = 0; } } /// /// Increment the item count for a given item type /// /// Item type to increment /// Amount to increment by, defaults to 1 private void AddItemCount(ItemType itemType, long interval = 1) { lock (ItemCounts) { if (!ItemCounts.ContainsKey(itemType)) ItemCounts[itemType] = 0; ItemCounts[itemType] += interval; if (ItemCounts[itemType] < 0) ItemCounts[itemType] = 0; } } /// /// Increment the item count for a given item status /// /// Item type to increment /// Amount to increment by, defaults to 1 private void AddStatusCount(ItemStatus itemStatus, long interval = 1) { lock (StatusCounts) { if (!StatusCounts.ContainsKey(itemStatus)) StatusCounts[itemStatus] = 0; StatusCounts[itemStatus] += interval; if (StatusCounts[itemStatus] < 0) StatusCounts[itemStatus] = 0; } } /// /// Remove from the statistics given a DatItem /// /// Item to remove info for public void RemoveItemStatistics(DatItem item) { // If we have a null item, we can't do anything if (item == null) return; lock (statsLock) { // No matter what the item is, we decrease the count TotalCount--; // Decrement removal count if (item.GetBoolFieldValue(DatItem.RemoveKey) == true) RemovedCount--; // Decrement the item count for the type RemoveItemCount(item.GetStringFieldValue(Models.Metadata.DatItem.TypeKey).AsEnumValue()); // Some item types require special processing switch (item) { case Disk disk: if (disk.GetStringFieldValue(Models.Metadata.Disk.StatusKey).AsEnumValue() != ItemStatus.Nodump) { RemoveHashCount(HashType.MD5, string.IsNullOrEmpty(disk.GetStringFieldValue(Models.Metadata.Disk.MD5Key)) ? 0 : 1); RemoveHashCount(HashType.SHA1, string.IsNullOrEmpty(disk.GetStringFieldValue(Models.Metadata.Disk.SHA1Key)) ? 0 : 1); } RemoveStatusCount(ItemStatus.BadDump, disk.GetStringFieldValue(Models.Metadata.Disk.StatusKey).AsEnumValue() == ItemStatus.BadDump ? 1 : 0); RemoveStatusCount(ItemStatus.Good, disk.GetStringFieldValue(Models.Metadata.Disk.StatusKey).AsEnumValue() == ItemStatus.Good ? 1 : 0); RemoveStatusCount(ItemStatus.Nodump, disk.GetStringFieldValue(Models.Metadata.Disk.StatusKey).AsEnumValue() == ItemStatus.Nodump ? 1 : 0); RemoveStatusCount(ItemStatus.Verified, disk.GetStringFieldValue(Models.Metadata.Disk.StatusKey).AsEnumValue() == ItemStatus.Verified ? 1 : 0); break; case Media media: RemoveHashCount(HashType.MD5, string.IsNullOrEmpty(media.GetStringFieldValue(Models.Metadata.Media.MD5Key)) ? 0 : 1); RemoveHashCount(HashType.SHA1, string.IsNullOrEmpty(media.GetStringFieldValue(Models.Metadata.Media.SHA1Key)) ? 0 : 1); RemoveHashCount(HashType.SHA256, string.IsNullOrEmpty(media.GetStringFieldValue(Models.Metadata.Media.SHA256Key)) ? 0 : 1); RemoveHashCount(HashType.SpamSum, string.IsNullOrEmpty(media.GetStringFieldValue(Models.Metadata.Media.SpamSumKey)) ? 0 : 1); break; case Rom rom: if (rom.GetStringFieldValue(Models.Metadata.Rom.StatusKey).AsEnumValue() != ItemStatus.Nodump) { TotalSize -= rom.GetInt64FieldValue(Models.Metadata.Rom.SizeKey) ?? 0; RemoveHashCount(HashType.CRC32, string.IsNullOrEmpty(rom.GetStringFieldValue(Models.Metadata.Rom.CRCKey)) ? 0 : 1); RemoveHashCount(HashType.MD5, string.IsNullOrEmpty(rom.GetStringFieldValue(Models.Metadata.Rom.MD5Key)) ? 0 : 1); RemoveHashCount(HashType.SHA1, string.IsNullOrEmpty(rom.GetStringFieldValue(Models.Metadata.Rom.SHA1Key)) ? 0 : 1); RemoveHashCount(HashType.SHA256, string.IsNullOrEmpty(rom.GetStringFieldValue(Models.Metadata.Rom.SHA256Key)) ? 0 : 1); RemoveHashCount(HashType.SHA384, string.IsNullOrEmpty(rom.GetStringFieldValue(Models.Metadata.Rom.SHA384Key)) ? 0 : 1); RemoveHashCount(HashType.SHA512, string.IsNullOrEmpty(rom.GetStringFieldValue(Models.Metadata.Rom.SHA512Key)) ? 0 : 1); RemoveHashCount(HashType.SpamSum, string.IsNullOrEmpty(rom.GetStringFieldValue(Models.Metadata.Rom.SpamSumKey)) ? 0 : 1); } RemoveStatusCount(ItemStatus.BadDump, rom.GetStringFieldValue(Models.Metadata.Rom.StatusKey).AsEnumValue() == ItemStatus.BadDump ? 1 : 0); RemoveStatusCount(ItemStatus.Good, rom.GetStringFieldValue(Models.Metadata.Rom.StatusKey).AsEnumValue() == ItemStatus.Good ? 1 : 0); RemoveStatusCount(ItemStatus.Nodump, rom.GetStringFieldValue(Models.Metadata.Rom.StatusKey).AsEnumValue() == ItemStatus.Nodump ? 1 : 0); RemoveStatusCount(ItemStatus.Verified, rom.GetStringFieldValue(Models.Metadata.Rom.StatusKey).AsEnumValue() == ItemStatus.Verified ? 1 : 0); break; } } } /// /// Reset all statistics /// public void ResetStatistics() { TotalCount = 0; ItemCounts = []; GameCount = 0; TotalSize = 0; HashCounts = []; StatusCounts = []; RemovedCount = 0; } /// /// Decrement the hash count for a given hash type /// /// Hash type to increment /// Amount to increment by, defaults to 1 private void RemoveHashCount(HashType hashType, long interval = 1) { lock (HashCounts) { if (!HashCounts.ContainsKey(hashType)) return; HashCounts[hashType] -= interval; if (HashCounts[hashType] < 0) HashCounts[hashType] = 0; } } /// /// Decrement the item count for a given item type /// /// Item type to decrement /// Amount to increment by, defaults to 1 private void RemoveItemCount(ItemType itemType, long interval = 1) { lock (ItemCounts) { if (!ItemCounts.ContainsKey(itemType)) return; ItemCounts[itemType] -= interval; if (ItemCounts[itemType] < 0) ItemCounts[itemType] = 0; } } /// /// Decrement the item count for a given item status /// /// Item type to decrement /// Amount to increment by, defaults to 1 private void RemoveStatusCount(ItemStatus itemStatus, long interval = 1) { lock (StatusCounts) { if (!StatusCounts.ContainsKey(itemStatus)) return; StatusCounts[itemStatus] -= interval; if (StatusCounts[itemStatus] < 0) StatusCounts[itemStatus] = 0; } } #endregion } }