diff --git a/SabreTools.Core/ArrayExtensions.cs b/SabreTools.Core/ArrayExtensions.cs new file mode 100644 index 00000000..e5d632af --- /dev/null +++ b/SabreTools.Core/ArrayExtensions.cs @@ -0,0 +1,15 @@ +using System; + +namespace SabreTools.Core +{ + public static class ArrayExtensions + { + /// + /// Indicates whether the specified array is null or has a length of zero + /// + public static bool IsNullOrEmpty(this Array? array) + { + return array == null || array.Length == 0; + } + } +} \ No newline at end of file diff --git a/SabreTools.Core/DictionaryBaseExtensions.cs b/SabreTools.Core/DictionaryBaseExtensions.cs new file mode 100644 index 00000000..4aad835c --- /dev/null +++ b/SabreTools.Core/DictionaryBaseExtensions.cs @@ -0,0 +1,479 @@ +using System; +using System.Linq; +using SabreTools.Models.Internal; + +namespace SabreTools.Core +{ + public static class DictionaryBaseExtensions + { + #region Cloning + + /// + /// Deep clone a DictionaryBase object + /// + public static DictionaryBase? Clone(this DictionaryBase dictionaryBase) + { + // Create a new object of the same type + var clone = dictionaryBase + .GetType() + .GetConstructor(System.Reflection.BindingFlags.Public, Array.Empty())? + .Invoke(null) as DictionaryBase; + + // If construction failed, we can't do anything + if (clone == null) + return null; + + // Loop through and clone per type + foreach (string key in dictionaryBase.Keys) + { + object? value = dictionaryBase[key]; + switch (value) + { + // Primative types + case bool: + case long: + case double: + case string: + clone[key] = value; + break; + + // DictionaryBase types + case DictionaryBase db: + clone[key] = db.Clone(); + break; + + // Enumerable types + case byte[] bytArr: + clone[key] = bytArr.Clone(); + break; + case string[] strArr: + clone[key] = strArr.Clone(); + break; + case DictionaryBase[] enDb: + clone[key] = enDb.Select(Clone).ToArray(); + break; + + // Everything else just copies + default: + clone[key] = value; + break; + } + } + + return clone; + } + + #endregion + + #region Equality Checking + + /// + /// Check equality of two DictionaryBase objects + /// + public static bool EqualTo(this DictionaryBase self, DictionaryBase other) + { + // Check types first + if (self.GetType() != other.GetType()) + return false; + + // Check based on the item type + return (self, other) switch + { + (Disk diskSelf, Disk diskOther) => EqualsImpl(diskSelf, diskOther), + (Media mediaSelf, Media mediaOther) => EqualsImpl(mediaSelf, mediaOther), + (Rom romSelf, Rom romOther) => EqualsImpl(romSelf, romOther), + _ => EqualsImpl(self, other), + }; + } + + /// + /// Check equality of two DictionaryBase objects + /// + private static bool EqualsImpl(this DictionaryBase self, DictionaryBase other) + { + // If the number of key-value pairs doesn't match, they can't match + if (self.Count != other.Count) + return false; + + // If any keys are missing on either side, they can't match + if (self.Keys.Except(other.Keys).Any()) + return false; + if (other.Keys.Except(self.Keys).Any()) + return false; + + // Check all pairs to see if they're equal + foreach (var kvp in self) + { + switch (kvp.Value, other[kvp.Key]) + { + case (string selfString, string otherString): + if (!string.Equals(selfString, otherString, StringComparison.OrdinalIgnoreCase)) + return false; + break; + + case (DictionaryBase selfDb, DictionaryBase otherDb): + if (!selfDb.Equals(otherDb)) + return false; + break; + + // TODO: Make this case-insensitive + case (string[] selfStrArr, string[] otherStrArr): + if (selfStrArr.Length != otherStrArr.Length) + return false; + if (selfStrArr.Except(otherStrArr).Any()) + return false; + if (otherStrArr.Except(selfStrArr).Any()) + return false; + break; + + // TODO: Fix the logic here for real equality + case (DictionaryBase[] selfDbArr, DictionaryBase[] otherDbArr): + if (selfDbArr.Length != otherDbArr.Length) + return false; + if (selfDbArr.Except(otherDbArr).Any()) + return false; + if (otherDbArr.Except(selfDbArr).Any()) + return false; + break; + + default: + if (kvp.Value != other[kvp.Key]) + return false; + break; + } + } + + return true; + } + + /// + /// Check equality of two Disk objects + /// + private static bool EqualsImpl(this Disk self, Disk other) + { + string? selfStatus = self.ReadString(Disk.StatusKey); + string? otherStatus = other.ReadString(Disk.StatusKey); + + string? selfName = self.ReadString(Disk.NameKey); + string? otherName = other.ReadString(Disk.NameKey); + + // If all hashes are empty but they're both nodump and the names match, then they're dupes + if (string.Equals(selfStatus, "nodump", StringComparison.OrdinalIgnoreCase) + && string.Equals(otherStatus, "nodump", StringComparison.OrdinalIgnoreCase) + && string.Equals(selfName, otherName, StringComparison.OrdinalIgnoreCase) + && !self.HasHashes() + && !other.HasHashes()) + { + return true; + } + + // If we get a partial match + if (self.HashMatch(other)) + return true; + + // All other cases fail + return false; + } + + /// + /// Check equality of two Media objects + /// + private static bool EqualsImpl(this Media self, Media other) + { + // If we get a partial match + if (self.HashMatch(other)) + return true; + + // All other cases fail + return false; + } + + /// + /// Check equality of two Rom objects + /// + private static bool EqualsImpl(this Rom self, Rom other) + { + string? selfStatus = self.ReadString(Rom.StatusKey); + string? otherStatus = other.ReadString(Rom.StatusKey); + + string? selfName = self.ReadString(Rom.NameKey); + string? otherName = other.ReadString(Rom.NameKey); + + long? selfSize = self.ReadLong(Rom.SizeKey); + long? otherSize = other.ReadLong(Rom.SizeKey); + + // If all hashes are empty but they're both nodump and the names match, then they're dupes + if (string.Equals(selfStatus, "nodump", StringComparison.OrdinalIgnoreCase) + && string.Equals(otherStatus, "nodump", StringComparison.OrdinalIgnoreCase) + && string.Equals(selfName, otherName, StringComparison.OrdinalIgnoreCase) + && !self.HasHashes() + && !other.HasHashes()) + { + return true; + } + + // If we have a file that has no known size, rely on the hashes only + if (selfSize == null && self.HashMatch(other)) + + // If we get a partial match + if (selfSize == otherSize && self.HashMatch(other)) + return true; + + // All other cases fail + return false; + } + + #endregion + + #region Hash Checking + + /// + /// Returns if any hashes are common + /// + public static bool HashMatch(this Disk self, Disk other) + { + // If either have no hashes, we return false, otherwise this would be a false positive + if (!self.HasHashes() || !other.HasHashes()) + return false; + + // If neither have hashes in common, we return false, otherwise this would be a false positive + if (!self.HasCommonHash(other)) + return false; + + // Return if all hashes match according to merge rules + return ConditionalHashEquals(self.ReadString(Disk.MD5Key), other.ReadString(Disk.MD5Key)) + && ConditionalHashEquals(self.ReadString(Disk.SHA1Key), other.ReadString(Disk.SHA1Key)); + } + + /// + /// Returns if any hashes are common + /// + public static bool HashMatch(this Media self, Media other) + { + // If either have no hashes, we return false, otherwise this would be a false positive + if (!self.HasHashes() || !other.HasHashes()) + return false; + + // If neither have hashes in common, we return false, otherwise this would be a false positive + if (!self.HasCommonHash(other)) + return false; + + // Return if all hashes match according to merge rules + return ConditionalHashEquals(self.ReadString(Media.MD5Key), other.ReadString(Media.MD5Key)) + && ConditionalHashEquals(self.ReadString(Media.SHA1Key), other.ReadString(Media.SHA1Key)) + && ConditionalHashEquals(self.ReadString(Media.SHA256Key), other.ReadString(Media.SHA256Key)) + && ConditionalHashEquals(self.ReadString(Media.SpamSumKey), other.ReadString(Media.SpamSumKey)); + } + + /// + /// Returns if any hashes are common + /// + public static bool HashMatch(this Rom self, Rom other) + { + // If either have no hashes, we return false, otherwise this would be a false positive + if (!self.HasHashes() || !other.HasHashes()) + return false; + + // If neither have hashes in common, we return false, otherwise this would be a false positive + if (!self.HasCommonHash(other)) + return false; + + // Return if all hashes match according to merge rules + return ConditionalHashEquals(self.ReadString(Rom.CRCKey), other.ReadString(Rom.CRCKey)) + && ConditionalHashEquals(self.ReadString(Rom.MD5Key), other.ReadString(Rom.MD5Key)) + && ConditionalHashEquals(self.ReadString(Rom.SHA1Key), other.ReadString(Rom.SHA1Key)) + && ConditionalHashEquals(self.ReadString(Rom.SHA256Key), other.ReadString(Rom.SHA256Key)) + && ConditionalHashEquals(self.ReadString(Rom.SHA384Key), other.ReadString(Rom.SHA384Key)) + && ConditionalHashEquals(self.ReadString(Rom.SHA512Key), other.ReadString(Rom.SHA512Key)) + && ConditionalHashEquals(self.ReadString(Rom.SpamSumKey), other.ReadString(Rom.SpamSumKey)); + } + + /// + /// Returns if any hashes exist + /// + public static bool HasHashes(this Disk disk) + { + bool md5Null = string.IsNullOrWhiteSpace(disk.ReadString(Disk.MD5Key)); + bool sha1Null = string.IsNullOrWhiteSpace(disk.ReadString(Disk.SHA1Key)); + + return !md5Null || !sha1Null; + } + + /// + /// Returns if any hashes exist + /// + public static bool HasHashes(this Media media) + { + bool md5Null = string.IsNullOrWhiteSpace(media.ReadString(Media.MD5Key)); + bool sha1Null = string.IsNullOrWhiteSpace(media.ReadString(Media.SHA1Key)); + bool sha256Null = string.IsNullOrWhiteSpace(media.ReadString(Media.SHA256Key)); + bool spamsumNull = string.IsNullOrWhiteSpace(media.ReadString(Media.SpamSumKey)); + + return !md5Null || !sha1Null || !sha256Null || !spamsumNull; + } + + /// + /// Returns if any hashes exist + /// + public static bool HasHashes(this Rom rom) + { + bool crcNull = string.IsNullOrWhiteSpace(rom.ReadString(Rom.CRCKey)); + bool md5Null = string.IsNullOrWhiteSpace(rom.ReadString(Rom.MD5Key)); + bool sha1Null = string.IsNullOrWhiteSpace(rom.ReadString(Rom.SHA1Key)); + bool sha256Null = string.IsNullOrWhiteSpace(rom.ReadString(Rom.SHA256Key)); + bool sha384Null = string.IsNullOrWhiteSpace(rom.ReadString(Rom.SHA384Key)); + bool sha512Null = string.IsNullOrWhiteSpace(rom.ReadString(Rom.SHA512Key)); + bool spamsumNull = string.IsNullOrWhiteSpace(rom.ReadString(Rom.SpamSumKey)); + + return !crcNull || !md5Null || !sha1Null || !sha256Null || !sha384Null || !sha512Null || !spamsumNull; + } + + /// + /// Returns if all of the hashes are set to their 0-byte values or null + /// + public static bool HasZeroHash(this Disk disk) + { + string? md5 = disk.ReadString(Disk.MD5Key); + bool md5Null = string.IsNullOrWhiteSpace(md5) || string.Equals(md5, Constants.MD5Zero, StringComparison.OrdinalIgnoreCase); + + string? sha1 = disk.ReadString(Disk.SHA1Key); + bool sha1Null = string.IsNullOrWhiteSpace(sha1) || string.Equals(sha1, Constants.SHA1Zero, StringComparison.OrdinalIgnoreCase); + + return md5Null && sha1Null; + } + + /// + /// Returns if all of the hashes are set to their 0-byte values or null + /// + public static bool HasZeroHash(this Media media) + { + string? md5 = media.ReadString(Media.MD5Key); + bool md5Null = string.IsNullOrWhiteSpace(md5) || string.Equals(md5, Constants.MD5Zero, StringComparison.OrdinalIgnoreCase); + + string? sha1 = media.ReadString(Media.SHA1Key); + bool sha1Null = string.IsNullOrWhiteSpace(sha1) || string.Equals(sha1, Constants.SHA1Zero, StringComparison.OrdinalIgnoreCase); + + string? sha256 = media.ReadString(Media.SHA256Key); + bool sha256Null = string.IsNullOrWhiteSpace(sha256) || string.Equals(sha256, Constants.SHA256Zero, StringComparison.OrdinalIgnoreCase); + + string? spamsum = media.ReadString(Media.SpamSumKey); + bool spamsumNull = string.IsNullOrWhiteSpace(spamsum) || string.Equals(spamsum, Constants.SpamSumZero, StringComparison.OrdinalIgnoreCase); + + return md5Null && sha1Null && sha256Null && spamsumNull; + } + + /// + /// Returns if all of the hashes are set to their 0-byte values or null + /// + public static bool HasZeroHash(this Rom rom) + { + string? crc = rom.ReadString(Rom.CRCKey); + bool crcNull = string.IsNullOrWhiteSpace(crc) || string.Equals(crc, Constants.CRCZero, StringComparison.OrdinalIgnoreCase); + + string? md5 = rom.ReadString(Rom.MD5Key); + bool md5Null = string.IsNullOrWhiteSpace(md5) || string.Equals(md5, Constants.MD5Zero, StringComparison.OrdinalIgnoreCase); + + string? sha1 = rom.ReadString(Rom.SHA1Key); + bool sha1Null = string.IsNullOrWhiteSpace(sha1) || string.Equals(sha1, Constants.SHA1Zero, StringComparison.OrdinalIgnoreCase); + + string? sha256 = rom.ReadString(Rom.SHA256Key); + bool sha256Null = string.IsNullOrWhiteSpace(sha256) || string.Equals(sha256, Constants.SHA256Zero, StringComparison.OrdinalIgnoreCase); + + string? sha384 = rom.ReadString(Rom.SHA384Key); + bool sha384Null = string.IsNullOrWhiteSpace(sha384) || string.Equals(sha384, Constants.SHA384Zero, StringComparison.OrdinalIgnoreCase); + + string? sha512 = rom.ReadString(Rom.SHA512Key); + bool sha512Null = string.IsNullOrWhiteSpace(sha512) || string.Equals(sha512, Constants.SHA512Zero, StringComparison.OrdinalIgnoreCase); + + string? spamsum = rom.ReadString(Rom.SpamSumKey); + bool spamsumNull = string.IsNullOrWhiteSpace(spamsum) || string.Equals(spamsum, Constants.SpamSumZero, StringComparison.OrdinalIgnoreCase); + + return crcNull && md5Null && sha1Null && sha256Null && sha384Null && sha512Null && spamsumNull; + } + + /// + /// Determine if two hashes are equal for the purposes of merging + /// + private static bool ConditionalHashEquals(string? firstHash, string? secondHash) + { + // If either hash is empty, we say they're equal for merging + if (string.IsNullOrWhiteSpace(firstHash) || string.IsNullOrWhiteSpace(secondHash)) + return true; + + // If they're different sizes, they can't match + if (firstHash!.Length != secondHash!.Length) + return false; + + // Otherwise, they need to match exactly + return string.Equals(firstHash, secondHash, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Returns if there are no, non-empty hashes in common + /// + private static bool HasCommonHash(this Disk self, Disk other) + { + bool md5Null = string.IsNullOrWhiteSpace(self.ReadString(Disk.MD5Key)); + md5Null ^= string.IsNullOrWhiteSpace(other.ReadString(Disk.MD5Key)); + + bool sha1Null = string.IsNullOrWhiteSpace(self.ReadString(Disk.SHA1Key)); + sha1Null ^= string.IsNullOrWhiteSpace(other.ReadString(Disk.SHA1Key)); + + return !md5Null || !sha1Null; + } + + /// + /// Returns if there are no, non-empty hashes in common + /// + private static bool HasCommonHash(this Media self, Media other) + { + bool md5Null = string.IsNullOrWhiteSpace(self.ReadString(Media.MD5Key)); + md5Null ^= string.IsNullOrWhiteSpace(other.ReadString(Media.MD5Key)); + + bool sha1Null = string.IsNullOrWhiteSpace(self.ReadString(Media.SHA1Key)); + sha1Null ^= string.IsNullOrWhiteSpace(other.ReadString(Media.SHA1Key)); + + bool sha256Null = string.IsNullOrWhiteSpace(self.ReadString(Media.SHA256Key)); + sha256Null ^= string.IsNullOrWhiteSpace(other.ReadString(Media.SHA256Key)); + + bool spamsumNull = string.IsNullOrWhiteSpace(self.ReadString(Media.SpamSumKey)); + spamsumNull ^= string.IsNullOrWhiteSpace(other.ReadString(Media.SpamSumKey)); + + return !md5Null || !sha1Null || !sha256Null || !spamsumNull; + } + + /// + /// Returns if there are no, non-empty hashes in common + /// + private static bool HasCommonHash(this Rom self, Rom other) + { + bool crcNull = string.IsNullOrWhiteSpace(self.ReadString(Rom.CRCKey)); + crcNull ^= string.IsNullOrWhiteSpace(other.ReadString(Rom.CRCKey)); + + bool md5Null = string.IsNullOrWhiteSpace(self.ReadString(Rom.MD5Key)); + md5Null ^= string.IsNullOrWhiteSpace(other.ReadString(Rom.MD5Key)); + + bool sha1Null = string.IsNullOrWhiteSpace(self.ReadString(Rom.SHA1Key)); + sha1Null ^= string.IsNullOrWhiteSpace(other.ReadString(Rom.SHA1Key)); + + bool sha256Null = string.IsNullOrWhiteSpace(self.ReadString(Rom.SHA256Key)); + sha256Null ^= string.IsNullOrWhiteSpace(other.ReadString(Rom.SHA256Key)); + + bool sha384Null = string.IsNullOrWhiteSpace(self.ReadString(Rom.SHA384Key)); + sha384Null ^= string.IsNullOrWhiteSpace(other.ReadString(Rom.SHA384Key)); + + bool sha512Null = string.IsNullOrWhiteSpace(self.ReadString(Rom.SHA512Key)); + sha512Null ^= string.IsNullOrWhiteSpace(other.ReadString(Rom.SHA512Key)); + + bool spamsumNull = string.IsNullOrWhiteSpace(self.ReadString(Rom.SpamSumKey)); + spamsumNull ^= string.IsNullOrWhiteSpace(other.ReadString(Rom.SpamSumKey)); + + return !crcNull || !md5Null || !sha1Null || !sha256Null || !sha384Null || !sha512Null || !spamsumNull; + } + + #endregion + + // TODO: Add DatItem conversion extensions here + // TODO: Once done with the above, replace innards on each current model + } +} \ No newline at end of file diff --git a/SabreTools.Core/Enums.cs b/SabreTools.Core/Enums.cs index f3ee2937..eaccc295 100644 --- a/SabreTools.Core/Enums.cs +++ b/SabreTools.Core/Enums.cs @@ -452,6 +452,12 @@ namespace SabreTools.Core [Mapping("configuration")] Configuration, + [Mapping("conflocation")] + ConfLocation, + + [Mapping("confsetting")] + ConfSetting, + [Mapping("control")] Control, @@ -464,9 +470,15 @@ namespace SabreTools.Core [Mapping("device_ref", "deviceref")] DeviceReference, + [Mapping("diplocation")] + DipLocation, + [Mapping("dipswitch")] DipSwitch, + [Mapping("dipvalue")] + DipValue, + [Mapping("diskarea")] DiskArea, @@ -491,9 +503,6 @@ namespace SabreTools.Core [Mapping("instance")] Instance, - [Mapping("location")] - Location, - [Mapping("original")] Original, @@ -521,9 +530,6 @@ namespace SabreTools.Core [Mapping("serials")] Serials, - [Mapping("setting")] - Setting, - [Mapping("sharedfeat", "shared_feat", "sharedfeature", "shared_feature")] SharedFeature, diff --git a/SabreTools.Core/SabreTools.Core.csproj b/SabreTools.Core/SabreTools.Core.csproj index 9e23202e..2223d477 100644 --- a/SabreTools.Core/SabreTools.Core.csproj +++ b/SabreTools.Core/SabreTools.Core.csproj @@ -9,6 +9,10 @@ + + + + diff --git a/SabreTools.Core/Tools/TextHelper.cs b/SabreTools.Core/Tools/TextHelper.cs index d085122d..8a5c0ac4 100644 --- a/SabreTools.Core/Tools/TextHelper.cs +++ b/SabreTools.Core/Tools/TextHelper.cs @@ -25,7 +25,43 @@ namespace SabreTools.Core.Tools return input; } - // + /// + /// Normalize a CRC32 string and pad to the correct size + /// + public static string NormalizeCRC32(string? hash) + => NormalizeHashData(hash, Constants.CRCLength); + + /// + /// Normalize a MD5 string and pad to the correct size + /// + public static string NormalizeMD5(string? hash) + => NormalizeHashData(hash, Constants.MD5Length); + + /// + /// Normalize a SHA1 string and pad to the correct size + /// + public static string NormalizeSHA1(string? hash) + => NormalizeHashData(hash, Constants.SHA1Length); + + /// + /// Normalize a SHA256 string and pad to the correct size + /// + public static string NormalizeSHA256(string? hash) + => NormalizeHashData(hash, Constants.SHA256Length); + + /// + /// Normalize a SHA384 string and pad to the correct size + /// + public static string NormalizeSHA384(string? hash) + => NormalizeHashData(hash, Constants.SHA384Length); + + /// + /// Normalize a SHA512 string and pad to the correct size + /// + public static string NormalizeSHA512(string? hash) + => NormalizeHashData(hash, Constants.SHA512Length); + + /// /// Remove all chars that are considered path unsafe /// public static string? RemovePathUnsafeCharacters(string? input) @@ -33,6 +69,8 @@ namespace SabreTools.Core.Tools if (string.IsNullOrWhiteSpace(input)) return input; + input = input.ToLowerInvariant(); + List invalidPath = Path.GetInvalidPathChars().ToList(); return new string(input.Where(c => !invalidPath.Contains(c)).ToArray()); } @@ -90,6 +128,40 @@ namespace SabreTools.Core.Tools return input; } + /// + /// Normalize a hash string and pad to the correct size + /// + private static string NormalizeHashData(string? hash, int expectedLength) + { + // If we have a known blank hash, return blank + if (string.IsNullOrWhiteSpace(hash) || hash == "-" || hash == "_") + return string.Empty; + + // Check to see if it's a "hex" hash + hash = hash.Trim().Replace("0x", string.Empty); + + // If we have a blank hash now, return blank + if (string.IsNullOrWhiteSpace(hash)) + return string.Empty; + + // If the hash shorter than the required length, pad it + if (hash.Length < expectedLength) + hash = hash.PadLeft(expectedLength, '0'); + + // If the hash is longer than the required length, it's invalid + else if (hash.Length > expectedLength) + return string.Empty; + + // Now normalize the hash + hash = hash.ToLowerInvariant(); + + // Otherwise, make sure that every character is a proper match + if (hash.Any(c => (c < '0' || c > '9') && (c < 'a' || c > 'f'))) + hash = string.Empty; + + return hash; + } + /// /// Convert Cyrillic lettering to Latin lettering /// diff --git a/SabreTools.Core/Tools/Utilities.cs b/SabreTools.Core/Tools/Utilities.cs index f4a1acf3..add94037 100644 --- a/SabreTools.Core/Tools/Utilities.cs +++ b/SabreTools.Core/Tools/Utilities.cs @@ -163,16 +163,6 @@ namespace SabreTools.Core.Tools }; } - /// Indicates whether the specified array is null or has a length of zero - /// - /// The array to test - /// true if the array parameter is null or has a length of zero; otherwise, false. - /// https://stackoverflow.com/questions/8560106/isnullorempty-equivalent-for-array-c-sharp - public static bool IsNullOrEmpty(this Array array) - { - return array == null || array.Length == 0; - } - //// /// Returns if the first byte array starts with the second array /// diff --git a/SabreTools.DatFiles/DatFile.cs b/SabreTools.DatFiles/DatFile.cs index 7fc9e370..c7b9939b 100644 --- a/SabreTools.DatFiles/DatFile.cs +++ b/SabreTools.DatFiles/DatFile.cs @@ -316,7 +316,7 @@ namespace SabreTools.DatFiles { // Initialize strings string fix, - game = item.Machine.Name, + game = item.Machine?.Name ?? string.Empty, name = item.GetName() ?? item.ItemType.ToString(), crc = string.Empty, md5 = string.Empty, @@ -365,9 +365,9 @@ namespace SabreTools.DatFiles .Replace("%game%", game) .Replace("%machine%", game) .Replace("%name%", name) - .Replace("%manufacturer%", item.Machine.Manufacturer ?? string.Empty) - .Replace("%publisher%", item.Machine.Publisher ?? string.Empty) - .Replace("%category%", item.Machine.Category ?? string.Empty) + .Replace("%manufacturer%", item.Machine?.Manufacturer ?? string.Empty) + .Replace("%publisher%", item.Machine?.Publisher ?? string.Empty) + .Replace("%category%", item.Machine?.Category ?? string.Empty) .Replace("%crc%", crc) .Replace("%md5%", md5) .Replace("%sha1%", sha1) @@ -397,7 +397,7 @@ namespace SabreTools.DatFiles Header.UseRomName = true; // Get the name to update - string? name = (Header.UseRomName ? item.GetName() : item.Machine.Name) ?? string.Empty; + string? name = (Header.UseRomName ? item.GetName() : item.Machine?.Name) ?? string.Empty; // Create the proper Prefix and Postfix string pre = CreatePrefixPostfix(item, true); @@ -454,13 +454,13 @@ namespace SabreTools.DatFiles name += Header.AddExtension; if (Header.UseRomName && Header.GameName) - name = Path.Combine(item.Machine.Name, name); + name = Path.Combine(item.Machine?.Name ?? string.Empty, name); // Now assign back the formatted name name = $"{pre}{name}{post}"; if (Header.UseRomName) item.SetName(name); - else + else if (item.Machine != null) item.Machine.Name = name; // Restore all relevant values @@ -489,7 +489,7 @@ namespace SabreTools.DatFiles // If the Rom has "null" characteristics, ensure all fields if (rom.Size == null && rom.CRC == "null") { - logger.Verbose($"Empty folder found: {datItem.Machine.Name}"); + logger.Verbose($"Empty folder found: {datItem.Machine?.Name}"); rom.Name = (rom.Name == "null" ? "-" : rom.Name); rom.Size = Constants.SizeZero; diff --git a/SabreTools.DatFiles/Formats/ArchiveDotOrg.Reader.cs b/SabreTools.DatFiles/Formats/ArchiveDotOrg.Reader.cs index 68e3038e..556929fb 100644 --- a/SabreTools.DatFiles/Formats/ArchiveDotOrg.Reader.cs +++ b/SabreTools.DatFiles/Formats/ArchiveDotOrg.Reader.cs @@ -113,7 +113,7 @@ namespace SabreTools.DatFiles.Formats SHA1 = file.SHA1, //FileCount = file.FileCount, // TODO: Add to internal model ArchiveDotOrgFormat = file.Format, - //Original = file.Original, // TODO: Add to internal model + OriginalFilename = file.Original, Summation = file.Summation, //MatrixNumber = file.MatrixNumber, // TODO: Add to internal model //CollectionCatalogNumber = file.CollectionCatalogNumber, // TODO: Add to internal model diff --git a/SabreTools.DatFiles/Formats/AttractMode.Writer.cs b/SabreTools.DatFiles/Formats/AttractMode.Writer.cs index 3c3bacf6..662f1107 100644 --- a/SabreTools.DatFiles/Formats/AttractMode.Writer.cs +++ b/SabreTools.DatFiles/Formats/AttractMode.Writer.cs @@ -121,23 +121,23 @@ namespace SabreTools.DatFiles.Formats { var row = new Models.AttractMode.Row { - Name = rom.Machine.Name, - Title = rom.Machine.Description, + Name = rom.Machine?.Name, + Title = rom.Machine?.Description, Emulator = Header.FileName, - CloneOf = rom.Machine.CloneOf, - Year = rom.Machine.Year, - Manufacturer = rom.Machine.Manufacturer, - Category = rom.Machine.Category, - Players = rom.Machine.Players, - Rotation = rom.Machine.Rotation, - Control = rom.Machine.Control, - Status = rom.Machine.Status, - DisplayCount = rom.Machine.DisplayCount, - DisplayType = rom.Machine.DisplayType, + CloneOf = rom.Machine?.CloneOf, + Year = rom.Machine?.Year, + Manufacturer = rom.Machine?.Manufacturer, + Category = rom.Machine?.Category, + Players = rom.Machine?.Players, + Rotation = rom.Machine?.Rotation, + Control = rom.Machine?.Control, + Status = rom.Machine?.Status, + DisplayCount = rom.Machine?.DisplayCount, + DisplayType = rom.Machine?.DisplayType, AltRomname = rom.AltName, AltTitle = rom.AltTitle, - Extra = rom.Machine.Comment, - Buttons = rom.Machine.Buttons, + Extra = rom.Machine?.Comment, + Buttons = rom.Machine?.Buttons, // TODO: Add extended fields }; return row; diff --git a/SabreTools.DatFiles/Formats/ClrMamePro.Reader.cs b/SabreTools.DatFiles/Formats/ClrMamePro.Reader.cs index a8d9e449..d67099a5 100644 --- a/SabreTools.DatFiles/Formats/ClrMamePro.Reader.cs +++ b/SabreTools.DatFiles/Formats/ClrMamePro.Reader.cs @@ -571,7 +571,7 @@ namespace SabreTools.DatFiles.Formats var item = new DipSwitch { Name = dipswitch.Name, - Values = new List(), + Values = new List(), Source = new Source { @@ -582,13 +582,13 @@ namespace SabreTools.DatFiles.Formats foreach (string entry in dipswitch.Entry ?? Array.Empty()) { - var setting = new Setting + var dipValue = new DipValue { Name = dipswitch.Name, Value = entry, Default = entry == dipswitch.Default, }; - item.Values.Add(setting); + item.Values.Add(dipValue); } item.CopyMachineInformation(machine); diff --git a/SabreTools.DatFiles/Formats/ClrMamePro.Writer.cs b/SabreTools.DatFiles/Formats/ClrMamePro.Writer.cs index e68fb5d1..85be11af 100644 --- a/SabreTools.DatFiles/Formats/ClrMamePro.Writer.cs +++ b/SabreTools.DatFiles/Formats/ClrMamePro.Writer.cs @@ -232,14 +232,14 @@ namespace SabreTools.DatFiles.Formats // We normalize to all "game" var game = new Models.ClrMamePro.Game { - Name = machine.Name, - Description = machine.Description, - Year = machine.Year, - Manufacturer = machine.Manufacturer, - Category = machine.Category, - CloneOf = machine.CloneOf, - RomOf = machine.RomOf, - SampleOf = machine.SampleOf, + Name = machine?.Name, + Description = machine?.Description, + Year = machine?.Year, + Manufacturer = machine?.Manufacturer, + Category = machine?.Category, + CloneOf = machine?.CloneOf, + RomOf = machine?.RomOf, + SampleOf = machine?.SampleOf, }; // Create holders for all item types @@ -535,7 +535,7 @@ namespace SabreTools.DatFiles.Formats }; if (item.ControlsSpecified) - input.Buttons = item.Controls[0].Buttons?.ToString(); + input.Buttons = item.Controls![0].Buttons?.ToString(); return input; } @@ -553,9 +553,9 @@ namespace SabreTools.DatFiles.Formats if (item.ValuesSpecified) { var entries = new List(); - foreach (var setting in item.Values) + foreach (var setting in item.Values!) { - entries.Add(setting.Value); + entries.Add(setting.Value!); if (setting.Default == true) dipswitch.Default = setting.Value; } diff --git a/SabreTools.DatFiles/Formats/DosCenter.Writer.cs b/SabreTools.DatFiles/Formats/DosCenter.Writer.cs index dec0996c..a1d163b1 100644 --- a/SabreTools.DatFiles/Formats/DosCenter.Writer.cs +++ b/SabreTools.DatFiles/Formats/DosCenter.Writer.cs @@ -134,7 +134,7 @@ namespace SabreTools.DatFiles.Formats // We re-add the missing parts of the game name var game = new Models.DosCenter.Game { - Name = $"\"{machine.Name}.zip\"" + Name = $"\"{machine?.Name ?? string.Empty}.zip\"" }; // Create holders for all item types diff --git a/SabreTools.DatFiles/Formats/EverdriveSMDB.Writer.cs b/SabreTools.DatFiles/Formats/EverdriveSMDB.Writer.cs index 3bd6133f..3bdcf9f0 100644 --- a/SabreTools.DatFiles/Formats/EverdriveSMDB.Writer.cs +++ b/SabreTools.DatFiles/Formats/EverdriveSMDB.Writer.cs @@ -139,7 +139,7 @@ namespace SabreTools.DatFiles.Formats var row = new Models.EverdriveSMDB.Row { SHA256 = rom.SHA256, - Name = $"{rom.Machine.Name}/{rom.Name}", + Name = $"{rom.Machine?.Name ?? string.Empty}/{rom.Name}", SHA1 = rom.SHA1, MD5 = rom.MD5, CRC32 = rom.CRC, diff --git a/SabreTools.DatFiles/Formats/Listrom.Writer.cs b/SabreTools.DatFiles/Formats/Listrom.Writer.cs index c4c70ca1..02b8c698 100644 --- a/SabreTools.DatFiles/Formats/Listrom.Writer.cs +++ b/SabreTools.DatFiles/Formats/Listrom.Writer.cs @@ -116,8 +116,8 @@ namespace SabreTools.DatFiles.Formats var set = new Models.Listrom.Set { - Driver = !items[0].Machine.MachineType.HasFlag(MachineType.Device) ? items[0].Machine.Name : null, - Device = items[0].Machine.MachineType.HasFlag(MachineType.Device) ? items[0].Machine.Name : null, + Driver = items[0]!.Machine!.MachineType.HasFlag(MachineType.Device) ? items[0]!.Machine!.Name : null, + Device = items[0]!.Machine!.MachineType.HasFlag(MachineType.Device) ? items[0]!.Machine!.Name : null, }; // Loop through and convert the items to respective lists diff --git a/SabreTools.DatFiles/Formats/Listxml.Reader.cs b/SabreTools.DatFiles/Formats/Listxml.Reader.cs index d86eac52..a2d5dab4 100644 --- a/SabreTools.DatFiles/Formats/Listxml.Reader.cs +++ b/SabreTools.DatFiles/Formats/Listxml.Reader.cs @@ -615,10 +615,10 @@ namespace SabreTools.DatFiles.Formats item.Conditions = new List { condition }; } - var locations = new List(); + var locations = new List(); foreach (var diplocation in dipswitch.DipLocation ?? Array.Empty()) { - var locationItem = new Location + var locationItem = new DipLocation { Name = diplocation.Name, Number = NumberHelper.ConvertToInt64(diplocation.Number), @@ -630,10 +630,10 @@ namespace SabreTools.DatFiles.Formats if (locations.Any()) item.Locations = locations; - var settings = new List(); + var settings = new List(); foreach (var dipvalue in dipswitch.DipValue ?? Array.Empty()) { - var settingItem = new Setting + var dipValueItem = new DipValue { Name = dipvalue.Name, Value = dipvalue.Value, @@ -649,10 +649,10 @@ namespace SabreTools.DatFiles.Formats Relation = dipvalue.Condition.Relation.AsRelation(), Value = dipvalue.Condition.Value, }; - settingItem.Conditions = new List { condition }; + dipValueItem.Conditions = new List { condition }; } - settings.Add(settingItem); + settings.Add(dipValueItem); } if (settings.Any()) @@ -696,20 +696,20 @@ namespace SabreTools.DatFiles.Formats if (configuration.Condition != null) { - var condition = new Condition + var condition = new DatItems.Formats.Condition { Tag = configuration.Condition.Tag, Mask = configuration.Condition.Mask, Relation = configuration.Condition.Relation.AsRelation(), Value = configuration.Condition.Value, }; - item.Conditions = new List { condition }; + item.Conditions = new List { condition }; } - var locations = new List(); + var locations = new List(); foreach (var confLocation in configuration.ConfLocation ?? Array.Empty()) { - var locationItem = new Location + var locationItem = new ConfLocation { Name = confLocation.Name, Number = NumberHelper.ConvertToInt64(confLocation.Number), @@ -721,10 +721,10 @@ namespace SabreTools.DatFiles.Formats if (locations.Any()) item.Locations = locations; - var settings = new List(); + var settings = new List(); foreach (var dipvalue in configuration.ConfSetting ?? Array.Empty()) { - var settingItem = new Setting + var settingItem = new ConfSetting { Name = dipvalue.Name, Value = dipvalue.Value, @@ -1094,6 +1094,7 @@ namespace SabreTools.DatFiles.Formats { Name = ramoption.Name, Default = ramoption.Default.AsYesNo(), + Content = ramoption.Content, Source = new Source { diff --git a/SabreTools.DatFiles/Formats/Listxml.Writer.cs b/SabreTools.DatFiles/Formats/Listxml.Writer.cs index 7c9a5da2..92fb166a 100644 --- a/SabreTools.DatFiles/Formats/Listxml.Writer.cs +++ b/SabreTools.DatFiles/Formats/Listxml.Writer.cs @@ -244,7 +244,7 @@ namespace SabreTools.DatFiles.Formats // Get the first item for game information var machine = items[0].Machine; - var game = CreateGame(machine); + var game = CreateGame(machine!); // Create holders for all item types var biosSets = new List(); @@ -604,19 +604,19 @@ namespace SabreTools.DatFiles.Formats if (item.ConditionsSpecified) { - var conditionItem = item.Conditions[0]; + var conditionItem = item.Conditions?.FirstOrDefault(); var condition = new Models.Listxml.Condition { - Tag = conditionItem.Tag, - Mask = conditionItem.Mask, - Relation = conditionItem.Relation.FromRelation(), - Value = conditionItem.Value, + Tag = conditionItem?.Tag, + Mask = conditionItem?.Mask, + Relation = conditionItem?.Relation.FromRelation(), + Value = conditionItem?.Value, }; dipswitch.Condition = condition; } var diplocations = new List(); - foreach (var locationItem in item.Locations ?? new List()) + foreach (var locationItem in item.Locations ?? new List()) { var control = CreateDipLocation(locationItem); diplocations.Add(control); @@ -626,9 +626,9 @@ namespace SabreTools.DatFiles.Formats dipswitch.DipLocation = diplocations.ToArray(); var dipvalues = new List(); - foreach (var settingItem in item.Values ?? new List()) + foreach (var dipValueItem in item.Values ?? new List()) { - var dipvalue = CreateDipValue(settingItem); + var dipvalue = CreateDipValue(dipValueItem); dipvalues.Add(dipvalue); } @@ -639,9 +639,9 @@ namespace SabreTools.DatFiles.Formats } /// - /// Create a DipLocation from the current Location DatItem + /// Create a DipLocation from the current DipLocation DatItem /// - private static Models.Listxml.DipLocation CreateDipLocation(Location item) + private static Models.Listxml.DipLocation CreateDipLocation(DipLocation item) { var diplocation = new Models.Listxml.DipLocation { @@ -654,9 +654,9 @@ namespace SabreTools.DatFiles.Formats } /// - /// Create a DipValue from the current Setting DatItem + /// Create a DipValue from the current DipValue DatItem /// - private static Models.Listxml.DipValue CreateDipValue(Setting item) + private static Models.Listxml.DipValue CreateDipValue(DipValue item) { var dipvalue = new Models.Listxml.DipValue { @@ -667,13 +667,13 @@ namespace SabreTools.DatFiles.Formats if (item.ConditionsSpecified) { - var conditionItem = item.Conditions[0]; + var conditionItem = item.Conditions?.FirstOrDefault(); var condition = new Models.Listxml.Condition { - Tag = conditionItem.Tag, - Mask = conditionItem.Mask, - Relation = conditionItem.Relation.FromRelation(), - Value = conditionItem.Value, + Tag = conditionItem?.Tag, + Mask = conditionItem?.Mask, + Relation = conditionItem?.Relation.FromRelation(), + Value = conditionItem?.Value, }; dipvalue.Condition = condition; } @@ -695,19 +695,19 @@ namespace SabreTools.DatFiles.Formats if (item.ConditionsSpecified) { - var conditionItem = item.Conditions[0]; + var conditionItem = item.Conditions?.FirstOrDefault(); var condition = new Models.Listxml.Condition { - Tag = conditionItem.Tag, - Mask = conditionItem.Mask, - Relation = conditionItem.Relation.FromRelation(), - Value = conditionItem.Value, + Tag = conditionItem?.Tag, + Mask = conditionItem?.Mask, + Relation = conditionItem?.Relation.FromRelation(), + Value = conditionItem?.Value, }; configuration.Condition = condition; } var confLocations = new List(); - foreach (var location in item.Locations ?? new List()) + foreach (var location in item.Locations ?? new List()) { var control = CreateConfLocation(location); confLocations.Add(control); @@ -717,9 +717,9 @@ namespace SabreTools.DatFiles.Formats configuration.ConfLocation = confLocations.ToArray(); var confsettings = new List(); - foreach (var settingItem in item.Settings ?? new List()) + foreach (var confSettingItem in item.Settings ?? new List()) { - var dipvalue = CreateConfSetting(settingItem); + var dipvalue = CreateConfSetting(confSettingItem); confsettings.Add(dipvalue); } @@ -730,9 +730,9 @@ namespace SabreTools.DatFiles.Formats } /// - /// Create a ConfLocation from the current Location DatItem + /// Create a ConfLocation from the current ConfLocation DatItem /// - private static Models.Listxml.ConfLocation CreateConfLocation(Location item) + private static Models.Listxml.ConfLocation CreateConfLocation(ConfLocation item) { var conflocation = new Models.Listxml.ConfLocation { @@ -745,9 +745,9 @@ namespace SabreTools.DatFiles.Formats } /// - /// Create a ConfSetting from the current Setting DatItem + /// Create a ConfSetting from the current ConfSetting DatItem /// - private static Models.Listxml.ConfSetting CreateConfSetting(Setting item) + private static Models.Listxml.ConfSetting CreateConfSetting(ConfSetting item) { var confsetting = new Models.Listxml.ConfSetting { @@ -758,13 +758,13 @@ namespace SabreTools.DatFiles.Formats if (item.ConditionsSpecified) { - var conditionItem = item.Conditions[0]; + var conditionItem = item.Conditions?.FirstOrDefault(); var condition = new Models.Listxml.Condition { - Tag = conditionItem.Tag, - Mask = conditionItem.Mask, - Relation = conditionItem.Relation.FromRelation(), - Value = conditionItem.Value, + Tag = conditionItem?.Tag, + Mask = conditionItem?.Mask, + Relation = conditionItem?.Relation.FromRelation(), + Value = conditionItem?.Value, }; confsetting.Condition = condition; } @@ -798,13 +798,13 @@ namespace SabreTools.DatFiles.Formats if (item.ConditionsSpecified) { - var conditionItem = item.Conditions[0]; + var conditionItem = item.Conditions?.FirstOrDefault(); var condition = new Models.Listxml.Condition { - Tag = conditionItem.Tag, - Mask = conditionItem.Mask, - Relation = conditionItem.Relation.FromRelation(), - Value = conditionItem.Value, + Tag = conditionItem?.Tag, + Mask = conditionItem?.Mask, + Relation = conditionItem?.Relation.FromRelation(), + Value = conditionItem?.Value, }; adjuster.Condition = condition; } @@ -866,11 +866,11 @@ namespace SabreTools.DatFiles.Formats if (item.InstancesSpecified) { - var instanceItem = item.Instances[0]; + var instanceItem = item.Instances?.FirstOrDefault(); var instance = new Models.Listxml.Instance { - Name = instanceItem.Name, - BriefName = instanceItem.BriefName, + Name = instanceItem?.Name, + BriefName = instanceItem?.BriefName, }; device.Instance = instance; } @@ -944,6 +944,7 @@ namespace SabreTools.DatFiles.Formats { Name = item.Name, Default = item.Default.FromYesNo(), + Content = item.Content, }; return softwarelist; diff --git a/SabreTools.DatFiles/Formats/Logiqx.Writer.cs b/SabreTools.DatFiles/Formats/Logiqx.Writer.cs index 20193809..166735f4 100644 --- a/SabreTools.DatFiles/Formats/Logiqx.Writer.cs +++ b/SabreTools.DatFiles/Formats/Logiqx.Writer.cs @@ -303,7 +303,7 @@ namespace SabreTools.DatFiles.Formats // Get the first item for game information var machine = items[0].Machine; - var game = CreateGame(machine); + var game = CreateGame(machine!); // Create holders for all item types var releases = new List(); diff --git a/SabreTools.DatFiles/Formats/Missfile.Writer.cs b/SabreTools.DatFiles/Formats/Missfile.Writer.cs index 393d2aa7..24d44448 100644 --- a/SabreTools.DatFiles/Formats/Missfile.Writer.cs +++ b/SabreTools.DatFiles/Formats/Missfile.Writer.cs @@ -63,7 +63,7 @@ namespace SabreTools.DatFiles.Formats WriteDatItem(sw, datItem, lastgame); // Set the new data to compare against - lastgame = datItem.Machine.Name; + lastgame = datItem.Machine?.Name; } } @@ -94,8 +94,8 @@ namespace SabreTools.DatFiles.Formats // Romba mode automatically uses item name if (Header.OutputDepot?.IsActive == true || Header.UseRomName) sw.Write($"{datItem.GetName() ?? string.Empty}\n"); - else if (!Header.UseRomName && datItem.Machine.Name != lastgame) - sw.Write($"{datItem.Machine.Name}\n"); + else if (!Header.UseRomName && datItem.Machine?.Name != lastgame) + sw.Write($"{datItem.Machine?.Name ?? string.Empty}\n"); sw.Flush(); } diff --git a/SabreTools.DatFiles/Formats/OfflineList.Writer.cs b/SabreTools.DatFiles/Formats/OfflineList.Writer.cs index da966a8a..b5e0b3b8 100644 --- a/SabreTools.DatFiles/Formats/OfflineList.Writer.cs +++ b/SabreTools.DatFiles/Formats/OfflineList.Writer.cs @@ -321,7 +321,7 @@ namespace SabreTools.DatFiles.Formats // Get the first item for game information var machine = items[0].Machine; - var game = CreateGame(machine); + var game = CreateGame(machine!); // Create holders for all item types var romCRCs = new List(); diff --git a/SabreTools.DatFiles/Formats/OpenMSX.Writer.cs b/SabreTools.DatFiles/Formats/OpenMSX.Writer.cs index 2ae7d05e..10f847da 100644 --- a/SabreTools.DatFiles/Formats/OpenMSX.Writer.cs +++ b/SabreTools.DatFiles/Formats/OpenMSX.Writer.cs @@ -105,12 +105,12 @@ namespace SabreTools.DatFiles.Formats var machine = items[0].Machine; var software = new Models.OpenMSX.Software { - Title = machine.Name, - GenMSXID = machine.GenMSXID, - System = machine.System, - Company = machine.Manufacturer, - Year = machine.Year, - Country = machine.Country, + Title = machine?.Name, + GenMSXID = machine?.GenMSXID, + System = machine?.System, + Company = machine?.Manufacturer, + Year = machine?.Year, + Country = machine?.Country, }; // Create holder for dumps diff --git a/SabreTools.DatFiles/Formats/RomCenter.Writer.cs b/SabreTools.DatFiles/Formats/RomCenter.Writer.cs index aeabd613..e98f6bc8 100644 --- a/SabreTools.DatFiles/Formats/RomCenter.Writer.cs +++ b/SabreTools.DatFiles/Formats/RomCenter.Writer.cs @@ -183,14 +183,14 @@ namespace SabreTools.DatFiles.Formats { var rom = new Models.RomCenter.Rom { - ParentName = item.Machine.CloneOf, - //ParentDescription = item.Machine.CloneOfDescription, // TODO: Add to internal model or find mapping - GameName = item.Machine.Name, - GameDescription = item.Machine.Description, + ParentName = item.Machine?.CloneOf, + //ParentDescription = item.Machine?.CloneOfDescription, // TODO: Add to internal model or find mapping + GameName = item.Machine?.Name, + GameDescription = item.Machine?.Description, RomName = item.Name, RomCRC = item.CRC, RomSize = item.Size?.ToString(), - RomOf = item.Machine.RomOf, + RomOf = item.Machine?.RomOf, MergeName = item.MergeTag, }; return rom; diff --git a/SabreTools.DatFiles/Formats/SabreJSON.cs b/SabreTools.DatFiles/Formats/SabreJSON.cs index cef8f4f0..7fb68288 100644 --- a/SabreTools.DatFiles/Formats/SabreJSON.cs +++ b/SabreTools.DatFiles/Formats/SabreJSON.cs @@ -235,6 +235,12 @@ namespace SabreTools.DatFiles.Formats case ItemType.Configuration: datItem = datItemObj.ToObject(); break; + case ItemType.ConfLocation: + datItem = datItemObj.ToObject(); + break; + case ItemType.ConfSetting: + datItem = datItemObj.ToObject(); + break; case ItemType.Control: datItem = datItemObj.ToObject(); break; @@ -247,6 +253,12 @@ namespace SabreTools.DatFiles.Formats case ItemType.DeviceReference: datItem = datItemObj.ToObject(); break; + case ItemType.DipLocation: + datItem = datItemObj.ToObject(); + break; + case ItemType.DipValue: + datItem = datItemObj.ToObject(); + break; case ItemType.DipSwitch: datItem = datItemObj.ToObject(); break; @@ -277,9 +289,6 @@ namespace SabreTools.DatFiles.Formats case ItemType.Instance: datItem = datItemObj.ToObject(); break; - case ItemType.Location: - datItem = datItemObj.ToObject(); - break; case ItemType.Media: datItem = datItemObj.ToObject(); break; @@ -310,9 +319,6 @@ namespace SabreTools.DatFiles.Formats case ItemType.Serials: datItem = datItemObj.ToObject(); break; - case ItemType.Setting: - datItem = datItemObj.ToObject(); - break; case ItemType.SharedFeature: datItem = datItemObj.ToObject(); break; @@ -389,11 +395,11 @@ namespace SabreTools.DatFiles.Formats DatItem datItem = datItems[index]; // If we have a different game and we're not at the start of the list, output the end of last item - if (lastgame != null && lastgame.ToLowerInvariant() != datItem.Machine.Name.ToLowerInvariant()) + if (lastgame != null && lastgame.ToLowerInvariant() != datItem.Machine?.Name?.ToLowerInvariant()) WriteEndGame(jtw); // If we have a new game, output the beginning of the new item - if (lastgame == null || lastgame.ToLowerInvariant() != datItem.Machine.Name.ToLowerInvariant()) + if (lastgame == null || lastgame.ToLowerInvariant() != datItem.Machine?.Name?.ToLowerInvariant()) WriteStartGame(jtw, datItem); // Check for a "null" item @@ -404,7 +410,7 @@ namespace SabreTools.DatFiles.Formats WriteDatItem(jtw, datItem); // Set the new data to compare against - lastgame = datItem.Machine.Name; + lastgame = datItem.Machine?.Name; } } @@ -451,7 +457,8 @@ namespace SabreTools.DatFiles.Formats private void WriteStartGame(JsonTextWriter jtw, DatItem datItem) { // No game should start with a path separator - datItem.Machine.Name = datItem.Machine.Name.TrimStart(Path.DirectorySeparatorChar) ?? string.Empty; + if (!string.IsNullOrWhiteSpace(datItem.Machine?.Name)) + datItem.Machine.Name = datItem.Machine.Name.TrimStart(Path.DirectorySeparatorChar) ?? string.Empty; // Build the state jtw.WriteStartObject(); diff --git a/SabreTools.DatFiles/Formats/SabreXML.cs b/SabreTools.DatFiles/Formats/SabreXML.cs index 92414ffc..67c49c3f 100644 --- a/SabreTools.DatFiles/Formats/SabreXML.cs +++ b/SabreTools.DatFiles/Formats/SabreXML.cs @@ -223,11 +223,11 @@ namespace SabreTools.DatFiles.Formats DatItem datItem = datItems[index]; // If we have a different game and we're not at the start of the list, output the end of last item - if (lastgame != null && lastgame.ToLowerInvariant() != datItem.Machine.Name.ToLowerInvariant()) + if (lastgame != null && lastgame.ToLowerInvariant() != datItem.Machine?.Name?.ToLowerInvariant()) WriteEndGame(xtw); // If we have a new game, output the beginning of the new item - if (lastgame == null || lastgame.ToLowerInvariant() != datItem.Machine.Name.ToLowerInvariant()) + if (lastgame == null || lastgame.ToLowerInvariant() != datItem.Machine?.Name?.ToLowerInvariant()) WriteStartGame(xtw, datItem); // Check for a "null" item @@ -238,7 +238,7 @@ namespace SabreTools.DatFiles.Formats WriteDatItem(xtw, datItem); // Set the new data to compare against - lastgame = datItem.Machine.Name; + lastgame = datItem.Machine?.Name; } } @@ -286,7 +286,7 @@ namespace SabreTools.DatFiles.Formats private void WriteStartGame(XmlTextWriter xtw, DatItem datItem) { // No game should start with a path separator - datItem.Machine.Name = datItem.Machine.Name?.TrimStart(Path.DirectorySeparatorChar) ?? string.Empty; + datItem.Machine!.Name = datItem.Machine.Name?.TrimStart(Path.DirectorySeparatorChar) ?? string.Empty; // Write the machine xtw.WriteStartElement("directory"); diff --git a/SabreTools.DatFiles/Formats/SeparatedValue.Writer.cs b/SabreTools.DatFiles/Formats/SeparatedValue.Writer.cs index 8540a9b6..d59d46b8 100644 --- a/SabreTools.DatFiles/Formats/SeparatedValue.Writer.cs +++ b/SabreTools.DatFiles/Formats/SeparatedValue.Writer.cs @@ -162,8 +162,8 @@ namespace SabreTools.DatFiles.Formats FileName = Header.FileName, InternalName = Header.Name, Description = Header.Description, - GameName = disk.Machine.Name, - GameDescription = disk.Machine.Description, + GameName = disk.Machine?.Name, + GameDescription = disk.Machine?.Description, Type = disk.ItemType.FromItemType(), RomName = string.Empty, DiskName = disk.Name, @@ -190,8 +190,8 @@ namespace SabreTools.DatFiles.Formats FileName = Header.FileName, InternalName = Header.Name, Description = Header.Description, - GameName = media.Machine.Name, - GameDescription = media.Machine.Description, + GameName = media.Machine?.Name, + GameDescription = media.Machine?.Description, Type = media.ItemType.FromItemType(), RomName = string.Empty, DiskName = media.Name, @@ -218,8 +218,8 @@ namespace SabreTools.DatFiles.Formats FileName = Header.FileName, InternalName = Header.Name, Description = Header.Description, - GameName = rom.Machine.Name, - GameDescription = rom.Machine.Description, + GameName = rom.Machine?.Name, + GameDescription = rom.Machine?.Description, Type = rom.ItemType.FromItemType(), RomName = rom.Name, DiskName = string.Empty, diff --git a/SabreTools.DatFiles/Formats/SoftwareList.Reader.cs b/SabreTools.DatFiles/Formats/SoftwareList.Reader.cs index 047184c9..dffe9962 100644 --- a/SabreTools.DatFiles/Formats/SoftwareList.Reader.cs +++ b/SabreTools.DatFiles/Formats/SoftwareList.Reader.cs @@ -418,7 +418,7 @@ namespace SabreTools.DatFiles.Formats Name = dipswitch.Name, Tag = dipswitch.Tag, Mask = dipswitch.Mask, - Values = CreateSettings(dipswitch.DipValue, machine, filename, indexId), + Values = CreateDipValues(dipswitch.DipValue, machine, filename, indexId), Part = part, @@ -441,16 +441,16 @@ namespace SabreTools.DatFiles.Formats /// Prefilled machine to use /// Name of the file to be parsed /// Index ID for the DAT - private static List? CreateSettings(Models.SoftwareList.DipValue[]? dipvalues, Machine machine, string filename, int indexId) + private static List? CreateDipValues(Models.SoftwareList.DipValue[]? dipvalues, Machine machine, string filename, int indexId) { // If the feature array is missing, we can't do anything if (dipvalues == null || !dipvalues.Any()) return null; - var settings = new List(); + var settings = new List(); foreach (var dipvalue in dipvalues) { - var item = new Setting + var item = new DipValue { Name = dipvalue.Name, Value = dipvalue.Value, diff --git a/SabreTools.DatFiles/Formats/SoftwareList.Writer.cs b/SabreTools.DatFiles/Formats/SoftwareList.Writer.cs index dcea6fcd..2063fbfd 100644 --- a/SabreTools.DatFiles/Formats/SoftwareList.Writer.cs +++ b/SabreTools.DatFiles/Formats/SoftwareList.Writer.cs @@ -41,7 +41,7 @@ namespace SabreTools.DatFiles.Formats } else { - if (string.IsNullOrWhiteSpace(dipSwitch.Part.Name)) + if (string.IsNullOrWhiteSpace(dipSwitch.Part!.Name)) missingFields.Add(DatItemField.Part_Name); if (string.IsNullOrWhiteSpace(dipSwitch.Part.Interface)) missingFields.Add(DatItemField.Part_Interface); @@ -54,9 +54,9 @@ namespace SabreTools.DatFiles.Formats missingFields.Add(DatItemField.Mask); if (dipSwitch.ValuesSpecified) { - if (dipSwitch.Values.Any(dv => string.IsNullOrWhiteSpace(dv.Name))) + if (dipSwitch.Values!.Any(dv => string.IsNullOrWhiteSpace(dv.Name))) missingFields.Add(DatItemField.Part_Feature_Name); - if (dipSwitch.Values.Any(dv => string.IsNullOrWhiteSpace(dv.Value))) + if (dipSwitch.Values!.Any(dv => string.IsNullOrWhiteSpace(dv.Value))) missingFields.Add(DatItemField.Part_Feature_Value); } @@ -70,7 +70,7 @@ namespace SabreTools.DatFiles.Formats } else { - if (string.IsNullOrWhiteSpace(disk.Part.Name)) + if (string.IsNullOrWhiteSpace(disk.Part!.Name)) missingFields.Add(DatItemField.Part_Name); if (string.IsNullOrWhiteSpace(disk.Part.Interface)) missingFields.Add(DatItemField.Part_Interface); @@ -81,7 +81,7 @@ namespace SabreTools.DatFiles.Formats } else { - if (string.IsNullOrWhiteSpace(disk.DiskArea.Name)) + if (string.IsNullOrWhiteSpace(disk.DiskArea!.Name)) missingFields.Add(DatItemField.AreaName); } if (string.IsNullOrWhiteSpace(disk.Name)) @@ -101,7 +101,7 @@ namespace SabreTools.DatFiles.Formats } else { - if (string.IsNullOrWhiteSpace(rom.Part.Name)) + if (string.IsNullOrWhiteSpace(rom.Part!.Name)) missingFields.Add(DatItemField.Part_Name); if (string.IsNullOrWhiteSpace(rom.Part.Interface)) missingFields.Add(DatItemField.Part_Interface); @@ -113,7 +113,7 @@ namespace SabreTools.DatFiles.Formats } else { - if (string.IsNullOrWhiteSpace(rom.DataArea.Name)) + if (string.IsNullOrWhiteSpace(rom.DataArea!.Name)) missingFields.Add(DatItemField.AreaName); if (!rom.DataArea.SizeSpecified) missingFields.Add(DatItemField.AreaSize); @@ -197,7 +197,7 @@ namespace SabreTools.DatFiles.Formats // Get the first item for game information var machine = items[0].Machine; - var sw = CreateSoftware(machine); + var sw = CreateSoftware(machine!); // Create holders for all item types var infos = new List(); @@ -304,9 +304,9 @@ namespace SabreTools.DatFiles.Formats { var part = new Models.SoftwareList.Part { - Name = item.Part.Name, - Interface = item.Part.Interface, - Feature = CreateFeatures(item.Part.Features), + Name = item.Part?.Name, + Interface = item.Part?.Interface, + Feature = CreateFeatures(item.Part?.Features), DataArea = CreateDataAreas(item), DiskArea = null, DipSwitch = null, @@ -321,9 +321,9 @@ namespace SabreTools.DatFiles.Formats { var part = new Models.SoftwareList.Part { - Name = item.Part.Name, - Interface = item.Part.Interface, - Feature = CreateFeatures(item.Part.Features), + Name = item.Part?.Name, + Interface = item.Part?.Interface, + Feature = CreateFeatures(item.Part?.Features), DataArea = null, DiskArea = CreateDiskAreas(item), DipSwitch = null, @@ -338,9 +338,9 @@ namespace SabreTools.DatFiles.Formats { var part = new Models.SoftwareList.Part { - Name = item.Part.Name, - Interface = item.Part.Interface, - Feature = CreateFeatures(item.Part.Features), + Name = item.Part?.Name, + Interface = item.Part?.Interface, + Feature = CreateFeatures(item.Part?.Features), DataArea = null, DiskArea = null, DipSwitch = CreateDipSwitches(item), @@ -351,7 +351,7 @@ namespace SabreTools.DatFiles.Formats /// /// Create a Feature array from the current list of PartFeature DatItems /// - private static Models.SoftwareList.Feature[]? CreateFeatures(List items) + private static Models.SoftwareList.Feature[]? CreateFeatures(List? items) { // If we don't have features, we can't do anything if (items == null || !items.Any()) @@ -378,10 +378,10 @@ namespace SabreTools.DatFiles.Formats { var dataArea = new Models.SoftwareList.DataArea { - Name = item.DataArea.Name, - Size = item.DataArea.Size?.ToString(), - Width = item.DataArea.Width?.ToString(), - Endianness = item.DataArea.Endianness.FromEndianness(), + Name = item.DataArea?.Name, + Size = item.DataArea?.Size?.ToString(), + Width = item.DataArea?.Width?.ToString(), + Endianness = item.DataArea?.Endianness.FromEndianness(), Rom = CreateRom(item), }; return new Models.SoftwareList.DataArea[] { dataArea }; @@ -441,7 +441,7 @@ namespace SabreTools.DatFiles.Formats private static Models.SoftwareList.DipSwitch[]? CreateDipSwitches(DipSwitch item) { var dipValues = new List(); - foreach (var setting in item.Values ?? new List()) + foreach (var setting in item.Values ?? new List()) { var dipValue = new Models.SoftwareList.DipValue { diff --git a/SabreTools.DatFiles/Setter.cs b/SabreTools.DatFiles/Setter.cs index 8d95ce90..28d4d9f0 100644 --- a/SabreTools.DatFiles/Setter.cs +++ b/SabreTools.DatFiles/Setter.cs @@ -231,10 +231,14 @@ namespace SabreTools.DatFiles case Chip chip: SetFields(chip); break; case Condition condition: SetFields(condition); break; case Configuration condition: SetFields(condition); break; + case ConfLocation confLocation: SetFields(confLocation); break; + case ConfSetting confSetting: SetFields(confSetting); break; case Control control: SetFields(control); break; case DataArea dataArea: SetFields(dataArea); break; case Device device: SetFields(device); break; + case DipLocation dipLocation: SetFields(dipLocation); break; case DipSwitch dipSwitch: SetFields(dipSwitch); break; + case DipValue dipValue: SetFields(dipValue); break; case Disk disk: SetFields(disk); break; case DiskArea diskArea: SetFields(diskArea); break; case Display display: SetFields(display); break; @@ -243,7 +247,6 @@ namespace SabreTools.DatFiles case Feature feature: SetFields(feature); break; case Input input: SetFields(input); break; case Instance instance: SetFields(instance); break; - case Location location: SetFields(location); break; case Media media: SetFields(media); break; case Part part: SetFields(part); break; case PartFeature partFeature: SetFields(partFeature); break; @@ -251,7 +254,6 @@ namespace SabreTools.DatFiles case RamOption ramOption: SetFields(ramOption); break; case Release release: SetFields(release); break; case Rom rom: SetFields(rom); break; - case Setting setting: SetFields(setting); break; case SharedFeature sharedFeat: SetFields(sharedFeat); break; case Slot slot: SetFields(slot); break; case SlotOption slotOption: SetFields(slotOption); break; @@ -398,7 +400,7 @@ namespace SabreTools.DatFiles // Field.DatItem_Conditions does not apply here if (adjuster.ConditionsSpecified) { - foreach (Condition subCondition in adjuster.Conditions) + foreach (Condition subCondition in adjuster.Conditions!) { SetFields(subCondition, true); } @@ -529,7 +531,7 @@ namespace SabreTools.DatFiles if (configuration.ConditionsSpecified) { - foreach (Condition subCondition in configuration.Conditions) + foreach (Condition subCondition in configuration.Conditions!) { SetFields(subCondition, true); } @@ -537,7 +539,7 @@ namespace SabreTools.DatFiles if (configuration.LocationsSpecified) { - foreach (Location subLocation in configuration.Locations) + foreach (ConfLocation subLocation in configuration.Locations!) { SetFields(subLocation); } @@ -545,13 +547,53 @@ namespace SabreTools.DatFiles if (configuration.SettingsSpecified) { - foreach (Setting subSetting in configuration.Settings) + foreach (ConfSetting subSetting in configuration.Settings!) { SetFields(subSetting); } } } + /// + /// Set fields with given values + /// + /// ConfLocation to remove replace fields in + private void SetFields(ConfLocation confLocation) + { + if (DatItemMappings!.ContainsKey(DatItemField.Location_Inverted)) + confLocation.Inverted = DatItemMappings[DatItemField.Location_Inverted].AsYesNo(); + + if (DatItemMappings!.ContainsKey(DatItemField.Location_Name)) + confLocation.Name = DatItemMappings[DatItemField.Location_Name]; + + if (DatItemMappings!.ContainsKey(DatItemField.Location_Number)) + confLocation.Number = NumberHelper.ConvertToInt64(DatItemMappings[DatItemField.Location_Number]); + } + + /// + /// Set fields with given values + /// + /// ConfSetting to remove replace fields in + private void SetFields(ConfSetting confSetting) + { + if (DatItemMappings!.ContainsKey(DatItemField.Setting_Default)) + confSetting.Default = DatItemMappings[DatItemField.Setting_Default].AsYesNo(); + + if (DatItemMappings!.ContainsKey(DatItemField.Setting_Name)) + confSetting.Name = DatItemMappings[DatItemField.Setting_Name]; + + if (DatItemMappings!.ContainsKey(DatItemField.Setting_Value)) + confSetting.Value = DatItemMappings[DatItemField.Setting_Value]; + + if (confSetting.ConditionsSpecified) + { + foreach (Condition subCondition in confSetting.Conditions!) + { + SetFields(subCondition, true); + } + } + } + /// /// Set fields with given values /// @@ -637,7 +679,7 @@ namespace SabreTools.DatFiles if (device.ExtensionsSpecified) { - foreach (Extension subExtension in device.Extensions) + foreach (Extension subExtension in device.Extensions!) { SetFields(subExtension); } @@ -645,13 +687,29 @@ namespace SabreTools.DatFiles if (device.InstancesSpecified) { - foreach (Instance subInstance in device.Instances) + foreach (Instance subInstance in device.Instances!) { SetFields(subInstance); } } } + /// + /// Set fields with given values + /// + /// DipLocation to remove replace fields in + private void SetFields(DipLocation dipLocation) + { + if (DatItemMappings!.ContainsKey(DatItemField.Location_Inverted)) + dipLocation.Inverted = DatItemMappings[DatItemField.Location_Inverted].AsYesNo(); + + if (DatItemMappings!.ContainsKey(DatItemField.Location_Name)) + dipLocation.Name = DatItemMappings[DatItemField.Location_Name]; + + if (DatItemMappings!.ContainsKey(DatItemField.Location_Number)) + dipLocation.Number = NumberHelper.ConvertToInt64(DatItemMappings[DatItemField.Location_Number]); + } + /// /// Set fields with given values /// @@ -666,7 +724,7 @@ namespace SabreTools.DatFiles if (dipSwitch.ConditionsSpecified) { - foreach (Condition subCondition in dipSwitch.Conditions) + foreach (Condition subCondition in dipSwitch.Conditions!) { SetFields(subCondition, true); } @@ -674,7 +732,7 @@ namespace SabreTools.DatFiles if (dipSwitch.LocationsSpecified) { - foreach (Location subLocation in dipSwitch.Locations) + foreach (DipLocation subLocation in dipSwitch.Locations!) { SetFields(subLocation); } @@ -682,7 +740,7 @@ namespace SabreTools.DatFiles if (dipSwitch.ValuesSpecified) { - foreach (Setting subValue in dipSwitch.Values) + foreach (DipValue subValue in dipSwitch.Values!) { SetFields(subValue); } @@ -692,6 +750,30 @@ namespace SabreTools.DatFiles SetFields(dipSwitch.Part); } + /// + /// Set fields with given values + /// + /// DipValue to remove replace fields in + private void SetFields(DipValue dipValue) + { + if (DatItemMappings!.ContainsKey(DatItemField.Setting_Default)) + dipValue.Default = DatItemMappings[DatItemField.Setting_Default].AsYesNo(); + + if (DatItemMappings!.ContainsKey(DatItemField.Setting_Name)) + dipValue.Name = DatItemMappings[DatItemField.Setting_Name]; + + if (DatItemMappings!.ContainsKey(DatItemField.Setting_Value)) + dipValue.Value = DatItemMappings[DatItemField.Setting_Value]; + + if (dipValue.ConditionsSpecified) + { + foreach (Condition subCondition in dipValue.Conditions!) + { + SetFields(subCondition, true); + } + } + } + /// /// Set fields with given values /// @@ -875,7 +957,7 @@ namespace SabreTools.DatFiles if (input.ControlsSpecified) { - foreach (Control subControl in input.Controls) + foreach (Control subControl in input.Controls!) { SetFields(subControl); } @@ -895,22 +977,6 @@ namespace SabreTools.DatFiles instance.BriefName = DatItemMappings[DatItemField.Instance_Name]; } - /// - /// Set fields with given values - /// - /// Location to remove replace fields in - private void SetFields(Location location) - { - if (DatItemMappings!.ContainsKey(DatItemField.Location_Inverted)) - location.Inverted = DatItemMappings[DatItemField.Location_Inverted].AsYesNo(); - - if (DatItemMappings!.ContainsKey(DatItemField.Location_Name)) - location.Name = DatItemMappings[DatItemField.Location_Name]; - - if (DatItemMappings!.ContainsKey(DatItemField.Location_Number)) - location.Number = NumberHelper.ConvertToInt64(DatItemMappings[DatItemField.Location_Number]); - } - /// /// Set fields with given values /// @@ -944,7 +1010,7 @@ namespace SabreTools.DatFiles if (part.FeaturesSpecified) { - foreach (PartFeature subPartFeature in part.Features) + foreach (PartFeature subPartFeature in part.Features!) { SetFields(subPartFeature); } @@ -975,7 +1041,7 @@ namespace SabreTools.DatFiles if (port.AnalogsSpecified) { - foreach (Analog subAnalog in port.Analogs) + foreach (Analog subAnalog in port.Analogs!) { SetFields(subAnalog); } @@ -1120,30 +1186,6 @@ namespace SabreTools.DatFiles SetFields(rom.Part); } - /// - /// Set fields with given values - /// - /// Setting to remove replace fields in - private void SetFields(Setting setting) - { - if (DatItemMappings!.ContainsKey(DatItemField.Setting_Default)) - setting.Default = DatItemMappings[DatItemField.Setting_Default].AsYesNo(); - - if (DatItemMappings!.ContainsKey(DatItemField.Setting_Name)) - setting.Name = DatItemMappings[DatItemField.Setting_Name]; - - if (DatItemMappings!.ContainsKey(DatItemField.Setting_Value)) - setting.Value = DatItemMappings[DatItemField.Setting_Value]; - - if (setting.ConditionsSpecified) - { - foreach (Condition subCondition in setting.Conditions) - { - SetFields(subCondition, true); - } - } - } - /// /// Set fields with given values /// @@ -1162,7 +1204,7 @@ namespace SabreTools.DatFiles { if (slot.SlotOptionsSpecified) { - foreach (SlotOption subSlotOption in slot.SlotOptions) + foreach (SlotOption subSlotOption in slot.SlotOptions!) { SetFields(subSlotOption); } diff --git a/SabreTools.DatItems/DatItem.cs b/SabreTools.DatItems/DatItem.cs index 199d7fbb..97db2745 100644 --- a/SabreTools.DatItems/DatItem.cs +++ b/SabreTools.DatItems/DatItem.cs @@ -2,15 +2,14 @@ using System.IO; using System.Linq; using System.Xml.Serialization; - +using NaturalSort; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; using SabreTools.Core; using SabreTools.Core.Tools; using SabreTools.DatItems.Formats; using SabreTools.FileTypes; using SabreTools.Logging; -using NaturalSort; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; namespace SabreTools.DatItems { @@ -29,11 +28,15 @@ namespace SabreTools.DatItems [XmlInclude(typeof(Chip))] [XmlInclude(typeof(Condition))] [XmlInclude(typeof(Configuration))] + [XmlInclude(typeof(ConfLocation))] + [XmlInclude(typeof(ConfSetting))] [XmlInclude(typeof(Control))] [XmlInclude(typeof(DataArea))] [XmlInclude(typeof(Device))] [XmlInclude(typeof(DeviceReference))] + [XmlInclude(typeof(DipLocation))] [XmlInclude(typeof(DipSwitch))] + [XmlInclude(typeof(DipValue))] [XmlInclude(typeof(Disk))] [XmlInclude(typeof(DiskArea))] [XmlInclude(typeof(Display))] @@ -43,7 +46,6 @@ namespace SabreTools.DatItems [XmlInclude(typeof(Info))] [XmlInclude(typeof(Input))] [XmlInclude(typeof(Instance))] - [XmlInclude(typeof(Location))] [XmlInclude(typeof(Media))] [XmlInclude(typeof(Part))] [XmlInclude(typeof(PartFeature))] @@ -52,7 +54,6 @@ namespace SabreTools.DatItems [XmlInclude(typeof(Release))] [XmlInclude(typeof(Rom))] [XmlInclude(typeof(Sample))] - [XmlInclude(typeof(Setting))] [XmlInclude(typeof(SharedFeature))] [XmlInclude(typeof(Slot))] [XmlInclude(typeof(SlotOption))] @@ -86,7 +87,7 @@ namespace SabreTools.DatItems /// Machine values /// [JsonIgnore, XmlIgnore] - public Machine Machine { get; set; } = new Machine(); + public Machine? Machine { get; set; } = new Machine(); #endregion @@ -96,7 +97,7 @@ namespace SabreTools.DatItems /// Source information /// [JsonIgnore, XmlIgnore] - public Source Source { get; set; } = new Source(); + public Source? Source { get; set; } = new Source(); /// /// Flag if item should be removed @@ -132,13 +133,13 @@ namespace SabreTools.DatItems /// Gets the name to use for a DatItem /// /// Name if available, null otherwise - public virtual string GetName() => null; + public virtual string? GetName() => null; /// /// Sets the name to use for a DatItem /// /// Name to set for the item - public virtual void SetName(string name) { } + public virtual void SetName(string? name) { } #endregion @@ -169,8 +170,10 @@ namespace SabreTools.DatItems ItemType.Chip => new Chip(), ItemType.Condition => new Condition(), ItemType.Configuration => new Configuration(), + ItemType.ConfLocation => new ConfLocation(), ItemType.Device => new Device(), ItemType.DeviceReference => new DeviceReference(), + ItemType.DipLocation => new DipLocation(), ItemType.DipSwitch => new DipSwitch(), ItemType.Disk => new Disk(), ItemType.Display => new Display(), @@ -180,7 +183,6 @@ namespace SabreTools.DatItems ItemType.File => new Formats.File(), ItemType.Info => new Info(), ItemType.Instance => new Instance(), - ItemType.Location => new Location(), ItemType.Media => new Media(), ItemType.PartFeature => new PartFeature(), ItemType.Port => new Port(), @@ -205,7 +207,7 @@ namespace SabreTools.DatItems /// /// BaseFile containing information to be created /// DatItem of the specific internal type that corresponds to the inputs - public static DatItem Create(BaseFile baseFile) + public static DatItem? Create(BaseFile baseFile) { return baseFile.Type switch { @@ -265,14 +267,14 @@ namespace SabreTools.DatItems #region Comparision Methods /// - public int CompareTo(DatItem other) + public int CompareTo(DatItem? other) { try { - if (GetName() == other.GetName()) + if (GetName() == other?.GetName()) return Equals(other) ? 0 : 1; - return string.Compare(GetName(), other.GetName()); + return string.Compare(GetName(), other?.GetName()); } catch { @@ -285,14 +287,14 @@ namespace SabreTools.DatItems /// /// DatItem to use as a baseline /// True if the items are duplicates, false otherwise - public abstract bool Equals(DatItem other); + public abstract bool Equals(DatItem? other); /// /// Return the duplicate status of two items /// /// DatItem to check against /// The DupeType corresponding to the relationship between the two - public DupeType GetDuplicateStatus(DatItem lastItem) + public DupeType GetDuplicateStatus(DatItem? lastItem) { DupeType output = 0x00; @@ -301,9 +303,9 @@ namespace SabreTools.DatItems return output; // If the duplicate is external already or should be, set it - if (lastItem.DupeType.HasFlag(DupeType.External) || lastItem.Source.Index != Source.Index) + if (lastItem.DupeType.HasFlag(DupeType.External) || lastItem?.Source?.Index != Source?.Index) { - if (lastItem.Machine.Name == Machine.Name && lastItem.GetName() == GetName()) + if (lastItem?.Machine?.Name == Machine?.Name && lastItem?.GetName() == GetName()) output = DupeType.External | DupeType.All; else output = DupeType.External | DupeType.Hash; @@ -312,7 +314,7 @@ namespace SabreTools.DatItems // Otherwise, it's considered an internal dupe else { - if (lastItem.Machine.Name == Machine.Name && lastItem.GetName() == GetName()) + if (lastItem?.Machine?.Name == Machine?.Name && lastItem?.GetName() == GetName()) output = DupeType.Internal | DupeType.All; else output = DupeType.Internal | DupeType.Hash; @@ -323,113 +325,6 @@ namespace SabreTools.DatItems #endregion - #region Filtering - - /// - /// Clean a CRC32 string and pad to the correct size - /// - /// Hash string to sanitize - /// Cleaned string - protected static string CleanCRC32(string hash) - { - return CleanHashData(hash, Constants.CRCLength); - } - - /// - /// Clean a MD5 string and pad to the correct size - /// - /// Hash string to sanitize - /// Cleaned string - protected static string CleanMD5(string hash) - { - return CleanHashData(hash, Constants.MD5Length); - } - - /// - /// Clean a SHA1 string and pad to the correct size - /// - /// Hash string to sanitize - /// Cleaned string - protected static string CleanSHA1(string hash) - { - return CleanHashData(hash, Constants.SHA1Length); - } - - /// - /// Clean a SHA256 string and pad to the correct size - /// - /// Hash string to sanitize - /// Cleaned string - protected static string CleanSHA256(string hash) - { - return CleanHashData(hash, Constants.SHA256Length); - } - - /// - /// Clean a SHA384 string and pad to the correct size - /// - /// Hash string to sanitize - /// Cleaned string - protected static string CleanSHA384(string hash) - { - return CleanHashData(hash, Constants.SHA384Length); - } - - /// - /// Clean a SHA512 string and pad to the correct size - /// - /// Hash string to sanitize - /// Cleaned string - protected static string CleanSHA512(string hash) - { - return CleanHashData(hash, Constants.SHA512Length); - } - - /// - /// Clean a hash string and pad to the correct size - /// - /// Hash string to sanitize - /// Amount of characters to pad to - /// Cleaned string - private static string CleanHashData(string hash, int padding) - { - // If we have a known blank hash, return blank - if (string.IsNullOrWhiteSpace(hash) || hash == "-" || hash == "_") - return string.Empty; - - // Check to see if it's a "hex" hash - hash = hash.Trim().Replace("0x", string.Empty); - - // If we have a blank hash now, return blank - if (string.IsNullOrWhiteSpace(hash)) - return string.Empty; - - // If the hash shorter than the required length, pad it - if (hash.Length < padding) - hash = hash.PadLeft(padding, '0'); - - // If the hash is longer than the required length, it's invalid - else if (hash.Length > padding) - return string.Empty; - - // Now normalize the hash - hash = hash.ToLowerInvariant(); - - // Otherwise, make sure that every character is a proper match - for (int i = 0; i < hash.Length; i++) - { - if ((hash[i] < '0' || hash[i] > '9') && (hash[i] < 'a' || hash[i] > 'f')) - { - hash = string.Empty; - break; - } - } - - return hash; - } - - #endregion - #region Sorting and Merging /// @@ -453,9 +348,9 @@ namespace SabreTools.DatItems case ItemKey.Machine: key = (norename ? string.Empty - : Source.Index.ToString().PadLeft(10, '0') + : Source?.Index.ToString().PadLeft(10, '0') + "-") - + (string.IsNullOrWhiteSpace(Machine.Name) + + (string.IsNullOrWhiteSpace(Machine?.Name) ? "Default" : Machine.Name); if (lower) @@ -510,14 +405,14 @@ namespace SabreTools.DatItems /// First hash to compare /// Second hash to compare /// True if either is empty OR the hashes exactly match, false otherwise - public static bool ConditionalHashEquals(byte[] firstHash, byte[] secondHash) + public static bool ConditionalHashEquals(byte[]? firstHash, byte[]? secondHash) { // If either hash is empty, we say they're equal for merging if (firstHash.IsNullOrEmpty() || secondHash.IsNullOrEmpty()) return true; // If they're different sizes, they can't match - if (firstHash.Length != secondHash.Length) + if (firstHash!.Length != secondHash!.Length) return false; // Otherwise, they need to match exactly @@ -556,13 +451,13 @@ namespace SabreTools.DatItems } // If it's a nodump, add and skip - if (file.ItemType == ItemType.Rom && (file as Rom).ItemStatus == ItemStatus.Nodump) + if (file is Rom rom && rom.ItemStatus == ItemStatus.Nodump) { outfiles.Add(file); nodumpCount++; continue; } - else if (file.ItemType == ItemType.Disk && (file as Disk).ItemStatus == ItemStatus.Nodump) + else if (file is Disk disk && disk.ItemStatus == ItemStatus.Nodump) { outfiles.Add(file); nodumpCount++; @@ -593,19 +488,19 @@ namespace SabreTools.DatItems pos = i; // Disks, Media, and Roms have more information to fill - if (file.ItemType == ItemType.Disk) - (saveditem as Disk).FillMissingInformation(file as Disk); - else if (file.ItemType == ItemType.File) - (saveditem as Formats.File).FillMissingInformation(file as Formats.File); - else if (file.ItemType == ItemType.Media) - (saveditem as Media).FillMissingInformation(file as Media); - else if (file.ItemType == ItemType.Rom) - (saveditem as Rom).FillMissingInformation(file as Rom); + if (file is Disk disk && saveditem is Disk savedDisk) + savedDisk.FillMissingInformation(disk); + else if (file is Formats.File fileItem && saveditem is Formats.File savedFile) + savedFile.FillMissingInformation(fileItem); + else if (file is Media media && saveditem is Media savedMedia) + savedMedia.FillMissingInformation(media); + else if (file is Rom romItem && saveditem is Rom savedRom) + savedRom.FillMissingInformation(romItem); saveditem.DupeType = dupetype; // If the current system has a lower ID than the previous, set the system accordingly - if (file.Source.Index < saveditem.Source.Index) + if (file.Source?.Index < saveditem.Source?.Index) { saveditem.Source = file.Source.Clone() as Source; saveditem.CopyMachineInformation(file); @@ -613,7 +508,7 @@ namespace SabreTools.DatItems } // If the current machine is a child of the new machine, use the new machine instead - if (saveditem.Machine.CloneOf == file.Machine.Name || saveditem.Machine.RomOf == file.Machine.Name) + if (saveditem.Machine?.CloneOf == file.Machine?.Name || saveditem.Machine?.RomOf == file.Machine?.Name) { saveditem.CopyMachineInformation(file); saveditem.SetName(file.GetName()); @@ -654,8 +549,8 @@ namespace SabreTools.DatItems Sort(ref infiles, true); // Now we want to loop through and check names - DatItem lastItem = null; - string lastrenamed = null; + DatItem? lastItem = null; + string? lastrenamed = null; int lastid = 0; for (int i = 0; i < infiles.Count; i++) { @@ -735,16 +630,14 @@ namespace SabreTools.DatItems /// private static string GetDuplicateSuffix(DatItem datItem) { - if (datItem.ItemType == ItemType.Disk) - return (datItem as Disk).GetDuplicateSuffix(); - else if (datItem.ItemType == ItemType.File) - return (datItem as Formats.File).GetDuplicateSuffix(); - else if (datItem.ItemType == ItemType.Media) - return (datItem as Media).GetDuplicateSuffix(); - else if (datItem.ItemType == ItemType.Rom) - return (datItem as Rom).GetDuplicateSuffix(); - - return "_1"; + return datItem switch + { + Disk disk => disk.GetDuplicateSuffix(), + Formats.File file => file.GetDuplicateSuffix(), + Media media => media.GetDuplicateSuffix(), + Rom rom => rom.GetDuplicateSuffix(), + _ => "_1", + }; } /// @@ -762,23 +655,23 @@ namespace SabreTools.DatItems NaturalComparer nc = new(); // If machine names match, more refinement is needed - if (x.Machine.Name == y.Machine.Name) + if (x.Machine?.Name == y.Machine?.Name) { // If item types match, more refinement is needed if (x.ItemType == y.ItemType) { - string xDirectoryName = Path.GetDirectoryName(TextHelper.RemovePathUnsafeCharacters(x.GetName() ?? string.Empty)); - string yDirectoryName = Path.GetDirectoryName(TextHelper.RemovePathUnsafeCharacters(y.GetName() ?? string.Empty)); + string? xDirectoryName = Path.GetDirectoryName(TextHelper.RemovePathUnsafeCharacters(x.GetName() ?? string.Empty)); + string? yDirectoryName = Path.GetDirectoryName(TextHelper.RemovePathUnsafeCharacters(y.GetName() ?? string.Empty)); // If item directory names match, more refinement is needed if (xDirectoryName == yDirectoryName) { - string xName = Path.GetFileName(TextHelper.RemovePathUnsafeCharacters(x.GetName() ?? string.Empty)); - string yName = Path.GetFileName(TextHelper.RemovePathUnsafeCharacters(y.GetName() ?? string.Empty)); + string? xName = Path.GetFileName(TextHelper.RemovePathUnsafeCharacters(x.GetName() ?? string.Empty)); + string? yName = Path.GetFileName(TextHelper.RemovePathUnsafeCharacters(y.GetName() ?? string.Empty)); // If item names match, then compare on machine or source, depending on the flag if (xName == yName) - return (norename ? nc.Compare(x.Machine.Name, y.Machine.Name) : x.Source.Index - y.Source.Index); + return (norename ? nc.Compare(x.Machine?.Name, y.Machine?.Name) : (x.Source?.Index - y.Source?.Index) ?? 0); // Otherwise, just sort based on item names return nc.Compare(xName, yName); @@ -793,7 +686,7 @@ namespace SabreTools.DatItems } // Otherwise, just sort based on machine name - return nc.Compare(x.Machine.Name, y.Machine.Name); + return nc.Compare(x.Machine?.Name, y.Machine?.Name); } catch { diff --git a/SabreTools.DatItems/Formats/Adjuster.cs b/SabreTools.DatItems/Formats/Adjuster.cs index 59346c6b..d3a47d6f 100644 --- a/SabreTools.DatItems/Formats/Adjuster.cs +++ b/SabreTools.DatItems/Formats/Adjuster.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using System.Xml.Serialization; using Newtonsoft.Json; using SabreTools.Core; @@ -17,13 +18,21 @@ namespace SabreTools.DatItems.Formats /// Name of the item /// [JsonProperty("name"), XmlElement("name")] - public string Name { get; set; } + public string? Name + { + get => _adjuster.ReadString(Models.Internal.Adjuster.NameKey); + set => _adjuster[Models.Internal.Adjuster.NameKey] = value; + } /// /// Determine whether the value is default /// [JsonProperty("default", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("default")] - public bool? Default { get; set; } + public bool? Default + { + get => _adjuster.ReadBool(Models.Internal.Adjuster.DefaultKey); + set => _adjuster[Models.Internal.Adjuster.DefaultKey] = value; + } [JsonIgnore] public bool DefaultSpecified { get { return Default != null; } } @@ -32,20 +41,30 @@ namespace SabreTools.DatItems.Formats /// Conditions associated with the adjustment /// [JsonProperty("conditions", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("conditions")] - public List Conditions { get; set; } + public List? Conditions + { + get => _adjuster.Read(Models.Internal.Adjuster.ConditionKey)?.ToList(); + set => _adjuster[Models.Internal.Adjuster.ConditionKey] = value?.ToArray(); + } [JsonIgnore] public bool ConditionsSpecified { get { return Conditions != null && Conditions.Count > 0; } } + /// + /// Internal Adjuster model + /// + [JsonIgnore] + private Models.Internal.Adjuster _adjuster = new(); + #endregion #region Accessors /// - public override string GetName() => Name; + public override string? GetName() => Name; /// - public override void SetName(string name) => Name = name; + public override void SetName(string? name) => Name = name; #endregion @@ -72,13 +91,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - Name = this.Name, - Default = this.Default, - Conditions = this.Conditions, + _adjuster = this._adjuster?.Clone() as Models.Internal.Adjuster ?? new Models.Internal.Adjuster(), }; } @@ -87,31 +104,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { // If we don't have a Adjuster, return false - if (ItemType != other.ItemType) + if (ItemType != other?.ItemType || other is not Adjuster otherInternal) return false; - // Otherwise, treat it as a Adjuster - Adjuster newOther = other as Adjuster; - - // If the Adjuster information matches - bool match = (Name == newOther.Name - && Default == newOther.Default); - if (!match) - return match; - - // If the conditions match - if (ConditionsSpecified) - { - foreach (Condition condition in Conditions) - { - match &= newOther.Conditions.Contains(condition); - } - } - - return match; + // Compare the internal models + return _adjuster.EqualTo(otherInternal._adjuster); } #endregion diff --git a/SabreTools.DatItems/Formats/Analog.cs b/SabreTools.DatItems/Formats/Analog.cs index 7daccaf8..b1c1cb93 100644 --- a/SabreTools.DatItems/Formats/Analog.cs +++ b/SabreTools.DatItems/Formats/Analog.cs @@ -16,7 +16,17 @@ namespace SabreTools.DatItems.Formats /// Analog mask value /// [JsonProperty("mask", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("mask")] - public string Mask { get; set; } + public string? Mask + { + get => _analog.ReadString(Models.Internal.Analog.MaskKey); + set => _analog[Models.Internal.Analog.MaskKey] = value; + } + + /// + /// Internal Analog model + /// + [JsonIgnore] + private Models.Internal.Analog _analog = new(); #endregion @@ -42,11 +52,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - Mask = this.Mask, + _analog = this._analog?.Clone() as Models.Internal.Analog ?? new Models.Internal.Analog(), }; } @@ -55,17 +65,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { // If we don't have a Analog, return false - if (ItemType != other.ItemType) + if (ItemType != other?.ItemType || other is not Analog otherInternal) return false; - // Otherwise, treat it as a Analog - Analog newOther = other as Analog; - - // If the Feature information matches - return (Mask == newOther.Mask); + // Compare the internal models + return _analog.EqualTo(otherInternal._analog); } #endregion diff --git a/SabreTools.DatItems/Formats/Archive.cs b/SabreTools.DatItems/Formats/Archive.cs index 8bdd0225..0c946f5e 100644 --- a/SabreTools.DatItems/Formats/Archive.cs +++ b/SabreTools.DatItems/Formats/Archive.cs @@ -16,82 +16,92 @@ namespace SabreTools.DatItems.Formats /// Name of the item /// [JsonProperty("name"), XmlElement("name")] - public string Name { get; set; } + public string? Name + { + get => _archive.ReadString(Models.Internal.Archive.NameKey); + set => _archive[Models.Internal.Archive.NameKey] = value; + } /// /// Archive ID number /// - /// No-Intro database export only + /// TODO: No-Intro database export only [JsonProperty("number"), XmlElement("number")] - public string Number { get; set; } + public string? Number { get; set; } /// /// Clone value /// - /// No-Intro database export only + /// TODO: No-Intro database export only [JsonProperty("clone"), XmlElement("clone")] - public string CloneValue { get; set; } + public string? CloneValue { get; set; } /// /// Regional parent value /// - /// No-Intro database export only + /// TODO: No-Intro database export only [JsonProperty("regparent"), XmlElement("regparent")] - public string RegParent { get; set; } + public string? RegParent { get; set; } /// /// Region value /// - /// No-Intro database export only + /// TODO: No-Intro database export only [JsonProperty("region"), XmlElement("region")] - public string Region { get; set; } + public string? Region { get; set; } /// /// Languages value /// - /// No-Intro database export only + /// TODO: No-Intro database export only [JsonProperty("languages"), XmlElement("languages")] - public string Languages { get; set; } + public string? Languages { get; set; } /// /// Development status value /// - /// No-Intro database export only + /// TODO: No-Intro database export only [JsonProperty("devstatus"), XmlElement("devstatus")] - public string DevStatus { get; set; } + public string? DevStatus { get; set; } /// /// Physical value /// - /// No-Intro database export only + /// TODO: No-Intro database export only /// TODO: Is this numeric or a flag? [JsonProperty("physical"), XmlElement("physical")] - public string Physical { get; set; } + public string? Physical { get; set; } /// /// Complete value /// - /// No-Intro database export only + /// TODO: No-Intro database export only /// TODO: Is this numeric or a flag? [JsonProperty("complete"), XmlElement("complete")] - public string Complete { get; set; } + public string? Complete { get; set; } /// /// Categories value /// - /// No-Intro database export only + /// TODO: No-Intro database export only [JsonProperty("categories"), XmlElement("categories")] - public string Categories { get; set; } + public string? Categories { get; set; } + + /// + /// Internal Archive model + /// + [JsonIgnore] + private Models.Internal.Archive _archive = new(); #endregion #region Accessors /// - public override string GetName() => Name; + public override string? GetName() => Name; /// - public override void SetName(string name) => Name = name; + public override void SetName(string? name) => Name = name; #endregion @@ -118,20 +128,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - Name = this.Name, - Number = this.Number, - CloneValue = this.CloneValue, - RegParent = this.RegParent, - Region = this.Region, - Languages = this.Languages, - DevStatus = this.DevStatus, - Physical = this.Physical, - Complete = this.Complete, - Categories = this.Categories, + _archive = this._archive?.Clone() as Models.Internal.Archive ?? new Models.Internal.Archive(), }; } @@ -140,26 +141,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { - // If we don't have an archive, return false - if (ItemType != other.ItemType) + // If we don't have a Archive, return false + if (ItemType != other?.ItemType || other is not Archive otherInternal) return false; - // Otherwise, treat it as an archive - Archive newOther = other as Archive; - - // If the archive information matches - return (Name == newOther.Name - && Number == newOther.Number - && CloneValue == newOther.CloneValue - && RegParent == newOther.RegParent - && Region == newOther.Region - && Languages == newOther.Languages - && DevStatus == newOther.DevStatus - && Physical == newOther.Physical - && Complete == newOther.Complete - && Categories == newOther.Categories); + // Compare the internal models + return _archive.EqualTo(otherInternal._archive); } #endregion diff --git a/SabreTools.DatItems/Formats/Auxiliary.cs b/SabreTools.DatItems/Formats/Auxiliary.cs index 7a793b29..fed1f72a 100644 --- a/SabreTools.DatItems/Formats/Auxiliary.cs +++ b/SabreTools.DatItems/Formats/Auxiliary.cs @@ -17,10 +17,24 @@ namespace SabreTools.DatItems.Formats public class Original { [JsonProperty("value"), XmlElement("value")] - public bool? Value { get; set; } + public bool? Value + { + get => _original.ReadBool(Models.Internal.Original.ValueKey); + set => _original[Models.Internal.Original.ValueKey] = value; + } [JsonProperty("content"), XmlElement("content")] - public string Content { get; set; } + public string? Content + { + get => _original.ReadString(Models.Internal.Original.ContentKey); + set => _original[Models.Internal.Original.ContentKey] = value; + } + + /// + /// Internal Original model + /// + [JsonIgnore] + private readonly Models.Internal.Original _original = new(); } #endregion diff --git a/SabreTools.DatItems/Formats/BiosSet.cs b/SabreTools.DatItems/Formats/BiosSet.cs index e56bffd0..8bf80800 100644 --- a/SabreTools.DatItems/Formats/BiosSet.cs +++ b/SabreTools.DatItems/Formats/BiosSet.cs @@ -16,32 +16,50 @@ namespace SabreTools.DatItems.Formats /// Name of the item /// [JsonProperty("name"), XmlElement("name")] - public string Name { get; set; } + public string? Name + { + get => _biosSet.ReadString(Models.Internal.BiosSet.NameKey); + set => _biosSet[Models.Internal.BiosSet.NameKey] = value; + } /// /// Description of the BIOS /// [JsonProperty("description", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("description")] - public string Description { get; set; } + public string? Description + { + get => _biosSet.ReadString(Models.Internal.BiosSet.DescriptionKey); + set => _biosSet[Models.Internal.BiosSet.DescriptionKey] = value; + } /// /// Determine whether the BIOS is default /// [JsonProperty("default", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("default")] - public bool? Default { get; set; } + public bool? Default + { + get => _biosSet.ReadBool(Models.Internal.BiosSet.DefaultKey); + set => _biosSet[Models.Internal.BiosSet.DefaultKey] = value; + } [JsonIgnore] public bool DefaultSpecified { get { return Default != null; } } + /// + /// Internal BiosSet model + /// + [JsonIgnore] + private Models.Internal.BiosSet _biosSet = new(); + #endregion #region Accessors /// - public override string GetName() => Name; + public override string? GetName() => Name; /// - public override void SetName(string name) => Name = name; + public override void SetName(string? name) => Name = name; #endregion @@ -68,13 +86,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - Name = this.Name, - Description = this.Description, - Default = this.Default, + _biosSet = this._biosSet?.Clone() as Models.Internal.BiosSet ?? new Models.Internal.BiosSet(), }; } @@ -83,19 +99,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { // If we don't have a BiosSet, return false - if (ItemType != other.ItemType) + if (ItemType != other?.ItemType || other is not BiosSet otherInternal) return false; - // Otherwise, treat it as a BiosSet - BiosSet newOther = other as BiosSet; - - // If the BiosSet information matches - return (Name == newOther.Name - && Description == newOther.Description - && Default == newOther.Default); + // Compare the internal models + return _biosSet.EqualTo(otherInternal._biosSet); } #endregion diff --git a/SabreTools.DatItems/Formats/Blank.cs b/SabreTools.DatItems/Formats/Blank.cs index 93a3b154..1264952f 100644 --- a/SabreTools.DatItems/Formats/Blank.cs +++ b/SabreTools.DatItems/Formats/Blank.cs @@ -32,8 +32,8 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, }; } @@ -43,17 +43,17 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { // If we don't have a blank, return false - if (ItemType != other.ItemType) + if (ItemType != other?.ItemType) return false; // Otherwise, treat it as a Blank - Blank newOther = other as Blank; + Blank? newOther = other as Blank; // If the archive information matches - return (Machine == newOther.Machine); + return (Machine == newOther!.Machine); } #endregion diff --git a/SabreTools.DatItems/Formats/Chip.cs b/SabreTools.DatItems/Formats/Chip.cs index 8a59bae8..85237c70 100644 --- a/SabreTools.DatItems/Formats/Chip.cs +++ b/SabreTools.DatItems/Formats/Chip.cs @@ -2,6 +2,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; using SabreTools.Core; +using SabreTools.Core.Tools; namespace SabreTools.DatItems.Formats { @@ -17,20 +18,32 @@ namespace SabreTools.DatItems.Formats /// Name of the item /// [JsonProperty("name"), XmlElement("name")] - public string Name { get; set; } + public string? Name + { + get => _chip.ReadString(Models.Internal.Chip.NameKey); + set => _chip[Models.Internal.Chip.NameKey] = value; + } /// /// Internal tag /// [JsonProperty("tag", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("tag")] - public string Tag { get; set; } + public string? Tag + { + get => _chip.ReadString(Models.Internal.Chip.TagKey); + set => _chip[Models.Internal.Chip.TagKey] = value; + } /// /// Type of the chip /// [JsonProperty("type", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("type")] [JsonConverter(typeof(StringEnumConverter))] - public ChipType ChipType { get; set; } + public ChipType ChipType + { + get => _chip.ReadString(Models.Internal.Chip.ChipTypeKey).AsChipType(); + set => _chip[Models.Internal.Chip.ChipTypeKey] = value.FromChipType(); + } [JsonIgnore] public bool ChipTypeSpecified { get { return ChipType != ChipType.NULL; } } @@ -39,20 +52,30 @@ namespace SabreTools.DatItems.Formats /// Clock speed /// [JsonProperty("clock", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("clock")] - public long? Clock { get; set; } + public long? Clock + { + get => _chip.ReadLong(Models.Internal.Chip.ClockKey); + set => _chip[Models.Internal.Chip.ClockKey] = value; + } [JsonIgnore] public bool ClockSpecified { get { return Clock != null; } } + /// + /// Internal Chip model + /// + [JsonIgnore] + private Models.Internal.Chip _chip = new(); + #endregion #region Accessors /// - public override string GetName() => Name; + public override string? GetName() => Name; /// - public override void SetName(string name) => Name = name; + public override void SetName(string? name) => Name = name; #endregion @@ -79,14 +102,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - Name = this.Name, - Tag = this.Tag, - ChipType = this.ChipType, - Clock = this.Clock, + _chip = this._chip?.Clone() as Models.Internal.Chip ?? new Models.Internal.Chip(), }; } @@ -95,20 +115,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { - // If we don't have a chip, return false - if (ItemType != other.ItemType) + // If we don't have a Chip, return false + if (ItemType != other?.ItemType || other is not Chip otherInternal) return false; - // Otherwise, treat it as a chip - Chip newOther = other as Chip; - - // If the chip information matches - return (Name == newOther.Name - && Tag == newOther.Tag - && ChipType == newOther.ChipType - && Clock == newOther.Clock); + // Compare the internal models + return _chip.EqualTo(otherInternal._chip); } #endregion diff --git a/SabreTools.DatItems/Formats/Condition.cs b/SabreTools.DatItems/Formats/Condition.cs index 72aeab5d..7a452542 100644 --- a/SabreTools.DatItems/Formats/Condition.cs +++ b/SabreTools.DatItems/Formats/Condition.cs @@ -2,6 +2,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; using SabreTools.Core; +using SabreTools.Core.Tools; namespace SabreTools.DatItems.Formats { @@ -17,29 +18,51 @@ namespace SabreTools.DatItems.Formats /// Condition tag value /// [JsonProperty("tag", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("tag")] - public string Tag { get; set; } + public string? Tag + { + get => _condition.ReadString(Models.Internal.Condition.TagKey); + set => _condition[Models.Internal.Condition.TagKey] = value; + } /// /// Condition mask /// [JsonProperty("mask", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("mask")] - public string Mask { get; set; } + public string? Mask + { + get => _condition.ReadString(Models.Internal.Condition.MaskKey); + set => _condition[Models.Internal.Condition.MaskKey] = value; + } /// /// Condition relationship /// [JsonProperty("relation", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("relation")] [JsonConverter(typeof(StringEnumConverter))] - public Relation Relation { get; set; } + public Relation Relation + { + get => _condition.ReadString(Models.Internal.Condition.RelationKey).AsRelation(); + set => _condition[Models.Internal.Condition.RelationKey] = value.FromRelation(); + } [JsonIgnore] - public bool RelationSpecified { get { return Relation != Relation.NULL; } } + public bool RelationSpecified { get { return Relation != Core.Relation.NULL; } } /// /// Condition value /// [JsonProperty("value", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("value")] - public string Value { get; set; } + public string? Value + { + get => _condition.ReadString(Models.Internal.Condition.ValueKey); + set => _condition[Models.Internal.Condition.ValueKey] = value; + } + + /// + /// Internal Condition model + /// + [JsonIgnore] + private Models.Internal.Condition _condition = new(); #endregion @@ -65,14 +88,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - Tag = this.Tag, - Mask = this.Mask, - Relation = this.Relation, - Value = this.Value, + _condition = this._condition?.Clone() as Models.Internal.Condition ?? new Models.Internal.Condition(), }; } @@ -81,20 +101,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { // If we don't have a Condition, return false - if (ItemType != other.ItemType) + if (ItemType != other?.ItemType || other is not Condition otherInternal) return false; - // Otherwise, treat it as a Condition - Condition newOther = other as Condition; - - // If the Feature information matches - return (Tag == newOther.Tag - && Mask == newOther.Mask - && Relation == newOther.Relation - && Value == newOther.Value); + // Compare the internal models + return _condition.EqualTo(otherInternal._condition); } #endregion diff --git a/SabreTools.DatItems/Formats/ConfLocation.cs b/SabreTools.DatItems/Formats/ConfLocation.cs new file mode 100644 index 00000000..2400f91e --- /dev/null +++ b/SabreTools.DatItems/Formats/ConfLocation.cs @@ -0,0 +1,117 @@ +using System.Xml.Serialization; +using Newtonsoft.Json; +using SabreTools.Core; + +namespace SabreTools.DatItems.Formats +{ + /// + /// Represents one conflocation + /// + [JsonObject("conflocation"), XmlRoot("conflocation")] + public class ConfLocation : DatItem + { + #region Fields + + /// + /// Location name + /// + [JsonProperty("name"), XmlElement("name")] + public string? Name + { + get => _confLocation.ReadString(Models.Internal.ConfLocation.NameKey); + set => _confLocation[Models.Internal.ConfLocation.NameKey] = value; + } + + /// + /// Location ID + /// + [JsonProperty("number", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("number")] + public long? Number + { + get => _confLocation.ReadLong(Models.Internal.ConfLocation.NumberKey); + set => _confLocation[Models.Internal.ConfLocation.NumberKey] = value; + } + + [JsonIgnore] + public bool NumberSpecified { get { return Number != null; } } + + /// + /// Determines if location is inverted or not + /// + [JsonProperty("inverted", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("inverted")] + public bool? Inverted + { + get => _confLocation.ReadBool(Models.Internal.ConfLocation.InvertedKey); + set => _confLocation[Models.Internal.ConfLocation.InvertedKey] = value; + } + + [JsonIgnore] + public bool InvertedSpecified { get { return Inverted != null; } } + + /// + /// Internal ConfLocation model + /// + [JsonIgnore] + private Models.Internal.ConfLocation _confLocation = new(); + + #endregion + + #region Accessors + + /// + public override string? GetName() => Name; + + /// + public override void SetName(string? name) => Name = name; + + #endregion + + #region Constructors + + /// + /// Create a default, empty ConfLocation object + /// + public ConfLocation() + { + Name = string.Empty; + ItemType = ItemType.ConfLocation; + } + + #endregion + + #region Cloning Methods + + /// + public override object Clone() + { + return new ConfLocation() + { + ItemType = this.ItemType, + DupeType = this.DupeType, + + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, + Remove = this.Remove, + + _confLocation = this._confLocation?.Clone() as Models.Internal.ConfLocation ?? new Models.Internal.ConfLocation(), + }; + } + + #endregion + + #region Comparision Methods + + /// + public override bool Equals(DatItem? other) + { + // If we don't have a v, return false + if (ItemType != other?.ItemType || other is not ConfLocation otherInternal) + return false; + + // Compare the internal models + return _confLocation.EqualTo(otherInternal._confLocation); + } + + #endregion + } +} diff --git a/SabreTools.DatItems/Formats/ConfSetting.cs b/SabreTools.DatItems/Formats/ConfSetting.cs new file mode 100644 index 00000000..d5b40546 --- /dev/null +++ b/SabreTools.DatItems/Formats/ConfSetting.cs @@ -0,0 +1,129 @@ +using System.Collections.Generic; +using System.Linq; +using System.Xml.Serialization; +using Newtonsoft.Json; +using SabreTools.Core; + +namespace SabreTools.DatItems.Formats +{ + /// + /// Represents one ListXML confsetting + /// + [JsonObject("confsetting"), XmlRoot("confsetting")] + public class ConfSetting : DatItem + { + #region Fields + + /// + /// Setting name + /// + [JsonProperty("name"), XmlElement("name")] + public string? Name + { + get => _confSetting.ReadString(Models.Internal.ConfSetting.NameKey); + set => _confSetting[Models.Internal.ConfSetting.NameKey] = value; + } + + /// + /// Setting value + /// + [JsonProperty("value", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("value")] + public string? Value + { + get => _confSetting.ReadString(Models.Internal.ConfSetting.ValueKey); + set => _confSetting[Models.Internal.ConfSetting.ValueKey] = value; + } + + /// + /// Determines if the setting is default or not + /// + [JsonProperty("default", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("default")] + public bool? Default + { + get => _confSetting.ReadBool(Models.Internal.ConfSetting.DefaultKey); + set => _confSetting[Models.Internal.ConfSetting.DefaultKey] = value; + } + + [JsonIgnore] + public bool DefaultSpecified { get { return Default != null; } } + + /// + /// List of conditions on the setting + /// + [JsonProperty("conditions", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("conditions")] + public List? Conditions + { + get => _confSetting.Read(Models.Internal.ConfSetting.ConditionKey)?.ToList(); + set => _confSetting[Models.Internal.ConfSetting.ConditionKey] = value?.ToArray(); + } + + [JsonIgnore] + public bool ConditionsSpecified { get { return Conditions != null && Conditions.Count > 0; } } + + /// + /// Internal ConfSetting model + /// + [JsonIgnore] + private Models.Internal.ConfSetting _confSetting = new(); + + #endregion + + #region Accessors + + /// + public override string? GetName() => Name; + + /// + public override void SetName(string? name) => Name = name; + + #endregion + + #region Constructors + + /// + /// Create a default, empty ConfSetting object + /// + public ConfSetting() + { + Name = string.Empty; + ItemType = ItemType.ConfSetting; + } + + #endregion + + #region Cloning Methods + + /// + public override object Clone() + { + return new ConfSetting() + { + ItemType = this.ItemType, + DupeType = this.DupeType, + + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, + Remove = this.Remove, + + _confSetting = this._confSetting?.Clone() as Models.Internal.ConfSetting ?? new Models.Internal.ConfSetting(), + }; + } + + #endregion + + #region Comparision Methods + + /// + public override bool Equals(DatItem? other) + { + // If we don't have a ConfSetting, return false + if (ItemType != other?.ItemType || other is not ConfSetting otherInternal) + return false; + + // Compare the internal models + return _confSetting.EqualTo(otherInternal._confSetting); + } + + #endregion + } +} diff --git a/SabreTools.DatItems/Formats/Configuration.cs b/SabreTools.DatItems/Formats/Configuration.cs index e4829b7f..dc0e2f41 100644 --- a/SabreTools.DatItems/Formats/Configuration.cs +++ b/SabreTools.DatItems/Formats/Configuration.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using System.Xml.Serialization; using Newtonsoft.Json; using SabreTools.Core; @@ -17,25 +18,41 @@ namespace SabreTools.DatItems.Formats /// Name of the item /// [JsonProperty("name"), XmlElement("name")] - public string Name { get; set; } + public string? Name + { + get => _configuration.ReadString(Models.Internal.Configuration.NameKey); + set => _configuration[Models.Internal.Configuration.NameKey] = value; + } /// /// Tag associated with the configuration /// [JsonProperty("tag", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("tag")] - public string Tag { get; set; } + public string? Tag + { + get => _configuration.ReadString(Models.Internal.Configuration.TagKey); + set => _configuration[Models.Internal.Configuration.TagKey] = value; + } /// /// Mask associated with the configuration /// [JsonProperty("mask", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("mask")] - public string Mask { get; set; } + public string? Mask + { + get => _configuration.ReadString(Models.Internal.Configuration.MaskKey); + set => _configuration[Models.Internal.Configuration.MaskKey] = value; + } /// /// Conditions associated with the configuration /// [JsonProperty("conditions", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("conditions")] - public List Conditions { get; set; } + public List? Conditions + { + get => _configuration.Read(Models.Internal.Configuration.ConditionKey)?.ToList(); + set => _configuration[Models.Internal.Configuration.ConditionKey] = value?.ToArray(); + } [JsonIgnore] public bool ConditionsSpecified { get { return Conditions != null && Conditions.Count > 0; } } @@ -44,7 +61,11 @@ namespace SabreTools.DatItems.Formats /// Locations associated with the configuration /// [JsonProperty("locations", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("locations")] - public List Locations { get; set; } + public List? Locations + { + get => _configuration.Read(Models.Internal.Configuration.ConfLocationKey)?.ToList(); + set => _configuration[Models.Internal.Configuration.ConfLocationKey] = value?.ToArray(); + } [JsonIgnore] public bool LocationsSpecified { get { return Locations != null && Locations.Count > 0; } } @@ -53,20 +74,30 @@ namespace SabreTools.DatItems.Formats /// Settings associated with the configuration /// [JsonProperty("settings", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("settings")] - public List Settings { get; set; } + public List? Settings + { + get => _configuration.Read>(Models.Internal.Configuration.ConfSettingKey); + set => _configuration[Models.Internal.Configuration.ConfSettingKey] = value; + } [JsonIgnore] public bool SettingsSpecified { get { return Settings != null && Settings.Count > 0; } } + /// + /// Internal Configuration model + /// + [JsonIgnore] + private Models.Internal.Configuration _configuration = new(); + #endregion #region Accessors /// - public override string GetName() => Name; + public override string? GetName() => Name; /// - public override void SetName(string name) => Name = name; + public override void SetName(string? name) => Name = name; #endregion @@ -93,16 +124,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - Name = this.Name, - Tag = this.Tag, - Mask = this.Mask, - Conditions = this.Conditions, - Locations = this.Locations, - Settings = this.Settings, + _configuration = this._configuration?.Clone() as Models.Internal.Configuration ?? new Models.Internal.Configuration(), }; } @@ -111,50 +137,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { // If we don't have a Configuration, return false - if (ItemType != other.ItemType) + if (ItemType != other?.ItemType || other is not Configuration otherInternal) return false; - // Otherwise, treat it as a Configuration - Configuration newOther = other as Configuration; - - // If the Configuration information matches - bool match = (Name == newOther.Name - && Tag == newOther.Tag - && Mask == newOther.Mask); - if (!match) - return match; - - // If the conditions match - if (ConditionsSpecified) - { - foreach (Condition condition in Conditions) - { - match &= newOther.Conditions.Contains(condition); - } - } - - // If the locations match - if (LocationsSpecified) - { - foreach (Location location in Locations) - { - match &= newOther.Locations.Contains(location); - } - } - - // If the settings match - if (SettingsSpecified) - { - foreach (Setting setting in Settings) - { - match &= newOther.Settings.Contains(setting); - } - } - - return match; + // Compare the internal models + return _configuration.EqualTo(otherInternal._configuration); } #endregion diff --git a/SabreTools.DatItems/Formats/Control.cs b/SabreTools.DatItems/Formats/Control.cs index e30d0dbd..dd0afb71 100644 --- a/SabreTools.DatItems/Formats/Control.cs +++ b/SabreTools.DatItems/Formats/Control.cs @@ -2,6 +2,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; using SabreTools.Core; +using SabreTools.Core.Tools; namespace SabreTools.DatItems.Formats { @@ -18,7 +19,11 @@ namespace SabreTools.DatItems.Formats /// [JsonProperty("type", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("type")] [JsonConverter(typeof(StringEnumConverter))] - public ControlType ControlType { get; set; } + public ControlType ControlType + { + get => _control.ReadString(Models.Internal.Control.ControlTypeKey).AsControlType(); + set => _control[Models.Internal.Control.ControlTypeKey] = value.FromControlType(); + } [JsonIgnore] public bool ControlTypeSpecified { get { return ControlType != ControlType.NULL; } } @@ -27,7 +32,11 @@ namespace SabreTools.DatItems.Formats /// Player which the input belongs to /// [JsonProperty("player", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("player")] - public long? Player { get; set; } + public long? Player + { + get => _control.ReadLong(Models.Internal.Control.PlayerKey); + set => _control[Models.Internal.Control.PlayerKey] = value; + } [JsonIgnore] public bool PlayerSpecified { get { return Player != null; } } @@ -36,7 +45,11 @@ namespace SabreTools.DatItems.Formats /// Total number of buttons /// [JsonProperty("buttons", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("buttons")] - public long? Buttons { get; set; } + public long? Buttons + { + get => _control.ReadLong(Models.Internal.Control.ButtonsKey); + set => _control[Models.Internal.Control.ButtonsKey] = value; + } [JsonIgnore] public bool ButtonsSpecified { get { return Buttons != null; } } @@ -45,7 +58,11 @@ namespace SabreTools.DatItems.Formats /// Total number of non-optional buttons /// [JsonProperty("reqbuttons", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("reqbuttons")] - public long? RequiredButtons { get; set; } + public long? RequiredButtons + { + get => _control.ReadLong(Models.Internal.Control.ReqButtonsKey); + set => _control[Models.Internal.Control.ReqButtonsKey] = value; + } [JsonIgnore] public bool RequiredButtonsSpecified { get { return RequiredButtons != null; } } @@ -54,7 +71,11 @@ namespace SabreTools.DatItems.Formats /// Analog minimum value /// [JsonProperty("minimum", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("minimum")] - public long? Minimum { get; set; } + public long? Minimum + { + get => _control.ReadLong(Models.Internal.Control.MinimumKey); + set => _control[Models.Internal.Control.MinimumKey] = value; + } [JsonIgnore] public bool MinimumSpecified { get { return Minimum != null; } } @@ -63,7 +84,11 @@ namespace SabreTools.DatItems.Formats /// Analog maximum value /// [JsonProperty("maximum", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("maximum")] - public long? Maximum { get; set; } + public long? Maximum + { + get => _control.ReadLong(Models.Internal.Control.MaximumKey); + set => _control[Models.Internal.Control.MaximumKey] = value; + } [JsonIgnore] public bool MaximumSpecified { get { return Maximum != null; } } @@ -72,7 +97,11 @@ namespace SabreTools.DatItems.Formats /// Default analog sensitivity /// [JsonProperty("sensitivity", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("sensitivity")] - public long? Sensitivity { get; set; } + public long? Sensitivity + { + get => _control.ReadLong(Models.Internal.Control.SensitivityKey); + set => _control[Models.Internal.Control.SensitivityKey] = value; + } [JsonIgnore] public bool SensitivitySpecified { get { return Sensitivity != null; } } @@ -81,7 +110,11 @@ namespace SabreTools.DatItems.Formats /// Default analog keydelta /// [JsonProperty("keydelta", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("keydelta")] - public long? KeyDelta { get; set; } + public long? KeyDelta + { + get => _control.ReadLong(Models.Internal.Control.KeyDeltaKey); + set => _control[Models.Internal.Control.KeyDeltaKey] = value; + } [JsonIgnore] public bool KeyDeltaSpecified { get { return KeyDelta != null; } } @@ -90,7 +123,11 @@ namespace SabreTools.DatItems.Formats /// Default analog reverse setting /// [JsonProperty("reverse", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("reverse")] - public bool? Reverse { get; set; } + public bool? Reverse + { + get => _control.ReadBool(Models.Internal.Control.ReverseKey); + set => _control[Models.Internal.Control.ReverseKey] = value; + } [JsonIgnore] public bool ReverseSpecified { get { return Reverse != null; } } @@ -99,19 +136,37 @@ namespace SabreTools.DatItems.Formats /// First set of ways /// [JsonProperty("ways", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("ways")] - public string Ways { get; set; } + public string? Ways + { + get => _control.ReadString(Models.Internal.Control.WaysKey); + set => _control[Models.Internal.Control.WaysKey] = value; + } /// /// Second set of ways /// [JsonProperty("ways2", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("ways2")] - public string Ways2 { get; set; } + public string? Ways2 + { + get => _control.ReadString(Models.Internal.Control.Ways2Key); + set => _control[Models.Internal.Control.Ways2Key] = value; + } /// /// Third set of ways /// [JsonProperty("ways3", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("ways3")] - public string Ways3 { get; set; } + public string? Ways3 + { + get => _control.ReadString(Models.Internal.Control.Ways3Key); + set => _control[Models.Internal.Control.Ways3Key] = value; + } + + /// + /// Internal Control model + /// + [JsonIgnore] + private Models.Internal.Control _control = new(); #endregion @@ -137,22 +192,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - ControlType = this.ControlType, - Player = this.Player, - Buttons = this.Buttons, - RequiredButtons = this.RequiredButtons, - Minimum = this.Minimum, - Maximum = this.Maximum, - Sensitivity = this.Sensitivity, - KeyDelta = this.KeyDelta, - Reverse = this.Reverse, - Ways = this.Ways, - Ways2 = this.Ways2, - Ways3 = this.Ways3, + _control = this._control?.Clone() as Models.Internal.Control ?? new Models.Internal.Control(), }; } @@ -161,28 +205,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { // If we don't have a Control, return false - if (ItemType != other.ItemType) + if (ItemType != other?.ItemType || other is not Control otherInternal) return false; - // Otherwise, treat it as a Control - Control newOther = other as Control; - - // If the Control information matches - return (ControlType == newOther.ControlType - && Player == newOther.Player - && Buttons == newOther.Buttons - && RequiredButtons == newOther.RequiredButtons - && Minimum == newOther.Minimum - && Maximum == newOther.Maximum - && Sensitivity == newOther.Sensitivity - && KeyDelta == newOther.KeyDelta - && Reverse == newOther.Reverse - && Ways == newOther.Ways - && Ways2 == newOther.Ways2 - && Ways3 == newOther.Ways3); + // Compare the internal models + return _control.EqualTo(otherInternal._control); } #endregion diff --git a/SabreTools.DatItems/Formats/DataArea.cs b/SabreTools.DatItems/Formats/DataArea.cs index d629e77c..cd2a414b 100644 --- a/SabreTools.DatItems/Formats/DataArea.cs +++ b/SabreTools.DatItems/Formats/DataArea.cs @@ -1,6 +1,7 @@ using System.Xml.Serialization; using Newtonsoft.Json; using SabreTools.Core; +using SabreTools.Core.Tools; namespace SabreTools.DatItems.Formats { @@ -17,13 +18,21 @@ namespace SabreTools.DatItems.Formats /// Name of the item /// [JsonProperty("name", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("name")] - public string Name { get; set; } + public string? Name + { + get => _dataArea.ReadString(Models.Internal.DataArea.NameKey); + set => _dataArea[Models.Internal.DataArea.NameKey] = value; + } /// /// Total size of the area /// [JsonProperty("size", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("size")] - public long? Size { get; set; } + public long? Size + { + get => _dataArea.ReadLong(Models.Internal.DataArea.SizeKey); + set => _dataArea[Models.Internal.DataArea.SizeKey] = value; + } [JsonIgnore] public bool SizeSpecified { get { return Size != null; } } @@ -32,7 +41,11 @@ namespace SabreTools.DatItems.Formats /// Word width for the area /// [JsonProperty("width", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("width")] - public long? Width { get; set; } + public long? Width + { + get => _dataArea.ReadLong(Models.Internal.DataArea.WidthKey); + set => _dataArea[Models.Internal.DataArea.WidthKey] = value; + } [JsonIgnore] public bool WidthSpecified { get { return Width != null; } } @@ -41,20 +54,30 @@ namespace SabreTools.DatItems.Formats /// Byte endianness of the area /// [JsonProperty("endianness", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("endianness")] - public Endianness Endianness { get; set; } + public Endianness Endianness + { + get => _dataArea.ReadString(Models.Internal.DataArea.WidthKey).AsEndianness(); + set => _dataArea[Models.Internal.DataArea.WidthKey] = value.FromEndianness(); + } [JsonIgnore] public bool EndiannessSpecified { get { return Endianness != Endianness.NULL; } } + /// + /// Internal DataArea model + /// + [JsonIgnore] + private Models.Internal.DataArea _dataArea = new(); + #endregion #region Accessors /// - public override string GetName() => Name; + public override string? GetName() => Name; /// - public override void SetName(string name) => Name = name; + public override void SetName(string? name) => Name = name; #endregion @@ -81,14 +104,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - Name = this.Name, - Size = this.Size, - Width = this.Width, - Endianness = this.Endianness, + _dataArea = this._dataArea?.Clone() as Models.Internal.DataArea ?? new Models.Internal.DataArea(), }; } @@ -97,20 +117,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { // If we don't have a DataArea, return false - if (ItemType != other.ItemType) + if (ItemType != other?.ItemType || other is not DataArea otherInternal) return false; - // Otherwise, treat it as a DataArea - DataArea newOther = other as DataArea; - - // If the DataArea information matches - return (Name == newOther.Name - && Size == newOther.Size - && Width == newOther.Width - && Endianness == newOther.Endianness); + // Compare the internal models + return _dataArea.EqualTo(otherInternal._dataArea); } #endregion diff --git a/SabreTools.DatItems/Formats/Device.cs b/SabreTools.DatItems/Formats/Device.cs index 01d4ed81..750548dd 100644 --- a/SabreTools.DatItems/Formats/Device.cs +++ b/SabreTools.DatItems/Formats/Device.cs @@ -1,8 +1,10 @@ using System.Collections.Generic; +using System.Linq; using System.Xml.Serialization; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using SabreTools.Core; +using SabreTools.Core.Tools; namespace SabreTools.DatItems.Formats { @@ -19,7 +21,11 @@ namespace SabreTools.DatItems.Formats /// [JsonProperty("type", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("type")] [JsonConverter(typeof(StringEnumConverter))] - public DeviceType DeviceType { get; set; } + public DeviceType DeviceType + { + get => _device.ReadString(Models.Internal.Device.DeviceTypeKey).AsDeviceType(); + set => _device[Models.Internal.Device.DeviceTypeKey] = value.FromDeviceType(); + } [JsonIgnore] public bool DeviceTypeSpecified { get { return DeviceType != DeviceType.NULL; } } @@ -28,20 +34,32 @@ namespace SabreTools.DatItems.Formats /// Device tag /// [JsonProperty("tag", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("tag")] - public string Tag { get; set; } + public string? Tag + { + get => _device.ReadString(Models.Internal.Device.TagKey); + set => _device[Models.Internal.Device.TagKey] = value; + } /// /// Fixed image format /// [JsonProperty("fixed_image", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("fixed_image")] - public string FixedImage { get; set; } + public string? FixedImage + { + get => _device.ReadString(Models.Internal.Device.FixedImageKey); + set => _device[Models.Internal.Device.FixedImageKey] = value; + } /// /// Determines if the devices is mandatory /// /// Only value used seems to be 1. Used like bool, but actually int [JsonProperty("mandatory", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("mandatory")] - public long? Mandatory { get; set; } + public long? Mandatory + { + get => _device.ReadLong(Models.Internal.Device.MandatoryKey); + set => _device[Models.Internal.Device.MandatoryKey] = value; + } [JsonIgnore] public bool MandatorySpecified { get { return Mandatory != null; } } @@ -50,13 +68,21 @@ namespace SabreTools.DatItems.Formats /// Device interface /// [JsonProperty("interface", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("interface")] - public string Interface { get; set; } + public string? Interface + { + get => _device.ReadString(Models.Internal.Device.InterfaceKey); + set => _device[Models.Internal.Device.InterfaceKey] = value; + } /// /// Device instances /// [JsonProperty("instances", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("instances")] - public List Instances { get; set; } + public List? Instances + { + get => _device.Read(Models.Internal.Device.InstanceKey)?.ToList(); + set => _device[Models.Internal.Device.InstanceKey] = value?.ToArray(); + } [JsonIgnore] public bool InstancesSpecified { get { return Instances != null && Instances.Count > 0; } } @@ -65,11 +91,21 @@ namespace SabreTools.DatItems.Formats /// Device extensions /// [JsonProperty("extensions", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("extensions")] - public List Extensions { get; set; } + public List? Extensions + { + get => _device.Read(Models.Internal.Device.ExtensionKey)?.ToList(); + set => _device[Models.Internal.Device.ExtensionKey] = value?.ToArray(); + } [JsonIgnore] public bool ExtensionsSpecified { get { return Extensions != null && Extensions.Count > 0; } } + /// + /// Internal Device model + /// + [JsonIgnore] + private Models.Internal.Device _device = new(); + #endregion #region Constructors @@ -94,17 +130,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - DeviceType = this.DeviceType, - Tag = this.Tag, - FixedImage = this.FixedImage, - Mandatory = this.Mandatory, - Interface = this.Interface, - Instances = this.Instances, - Extensions = this.Extensions, + _device = this._device?.Clone() as Models.Internal.Device ?? new Models.Internal.Device(), }; } @@ -113,43 +143,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { - // If we don't have a Device, return false - if (ItemType != other.ItemType) + // If we don't have a Adjuster, return false + if (ItemType != other?.ItemType || other is not Device otherInternal) return false; - // Otherwise, treat it as a Device - Device newOther = other as Device; - - // If the Device information matches - bool match = (DeviceType == newOther.DeviceType - && Tag == newOther.Tag - && FixedImage == newOther.FixedImage - && Mandatory == newOther.Mandatory - && Interface == newOther.Interface); - if (!match) - return match; - - // If the instances match - if (InstancesSpecified) - { - foreach (Instance instance in Instances) - { - match &= newOther.Instances.Contains(instance); - } - } - - // If the extensions match - if (ExtensionsSpecified) - { - foreach (Extension extension in Extensions) - { - match &= newOther.Extensions.Contains(extension); - } - } - - return match; + // Compare the internal models + return _device.EqualTo(otherInternal._device); } #endregion diff --git a/SabreTools.DatItems/Formats/DeviceReference.cs b/SabreTools.DatItems/Formats/DeviceReference.cs index 48d13e03..57ad0c32 100644 --- a/SabreTools.DatItems/Formats/DeviceReference.cs +++ b/SabreTools.DatItems/Formats/DeviceReference.cs @@ -16,17 +16,27 @@ namespace SabreTools.DatItems.Formats /// Name of the item /// [JsonProperty("name"), XmlElement("name")] - public string Name { get; set; } + public string? Name + { + get => _deviceRef.ReadString(Models.Internal.DeviceRef.NameKey); + set => _deviceRef[Models.Internal.DeviceRef.NameKey] = value; + } + + /// + /// Internal DeviceRef model + /// + [JsonIgnore] + private Models.Internal.DeviceRef _deviceRef = new(); #endregion #region Accessors /// - public override string GetName() => Name; + public override string? GetName() => Name; /// - public override void SetName(string name) => Name = name; + public override void SetName(string? name) => Name = name; #endregion @@ -53,11 +63,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - Name = this.Name, + _deviceRef = this._deviceRef?.Clone() as Models.Internal.DeviceRef ?? new Models.Internal.DeviceRef(), }; } @@ -66,17 +76,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { - // If we don't have a device reference, return false - if (ItemType != other.ItemType) + // If we don't have a Adjuster, return false + if (ItemType != other?.ItemType || other is not DeviceReference otherInternal) return false; - // Otherwise, treat it as a device reference - DeviceReference newOther = other as DeviceReference; - - // If the device reference information matches - return (Name == newOther.Name); + // Compare the internal models + return _deviceRef.EqualTo(otherInternal._deviceRef); } #endregion diff --git a/SabreTools.DatItems/Formats/DipLocation.cs b/SabreTools.DatItems/Formats/DipLocation.cs new file mode 100644 index 00000000..1d9f0bbf --- /dev/null +++ b/SabreTools.DatItems/Formats/DipLocation.cs @@ -0,0 +1,117 @@ +using System.Xml.Serialization; +using Newtonsoft.Json; +using SabreTools.Core; + +namespace SabreTools.DatItems.Formats +{ + /// + /// Represents one diplocation + /// + [JsonObject("diplocation"), XmlRoot("diplocation")] + public class DipLocation : DatItem + { + #region Fields + + /// + /// Location name + /// + [JsonProperty("name"), XmlElement("name")] + public string? Name + { + get => _dipLocation.ReadString(Models.Internal.DipLocation.NameKey); + set => _dipLocation[Models.Internal.DipLocation.NameKey] = value; + } + + /// + /// Location ID + /// + [JsonProperty("number", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("number")] + public long? Number + { + get => _dipLocation.ReadLong(Models.Internal.DipLocation.NameKey); + set => _dipLocation[Models.Internal.DipLocation.NameKey] = value; + } + + [JsonIgnore] + public bool NumberSpecified { get { return Number != null; } } + + /// + /// Determines if location is inverted or not + /// + [JsonProperty("inverted", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("inverted")] + public bool? Inverted + { + get => _dipLocation.ReadBool(Models.Internal.DipLocation.InvertedKey); + set => _dipLocation[Models.Internal.DipLocation.InvertedKey] = value; + } + + [JsonIgnore] + public bool InvertedSpecified { get { return Inverted != null; } } + + /// + /// Internal DipLocation model + /// + [JsonIgnore] + private Models.Internal.DipLocation _dipLocation = new(); + + #endregion + + #region Accessors + + /// + public override string? GetName() => Name; + + /// + public override void SetName(string? name) => Name = name; + + #endregion + + #region Constructors + + /// + /// Create a default, empty DipLocation object + /// + public DipLocation() + { + Name = string.Empty; + ItemType = ItemType.DipLocation; + } + + #endregion + + #region Cloning Methods + + /// + public override object Clone() + { + return new DipLocation() + { + ItemType = this.ItemType, + DupeType = this.DupeType, + + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, + Remove = this.Remove, + + _dipLocation = this._dipLocation?.Clone() as Models.Internal.DipLocation ?? new Models.Internal.DipLocation(), + }; + } + + #endregion + + #region Comparision Methods + + /// + public override bool Equals(DatItem? other) + { + // If we don't have a DipLocation, return false + if (ItemType != other?.ItemType || other is not DipLocation otherInternal) + return false; + + // Compare the internal models + return _dipLocation.EqualTo(otherInternal._dipLocation); + } + + #endregion + } +} diff --git a/SabreTools.DatItems/Formats/DipSwitch.cs b/SabreTools.DatItems/Formats/DipSwitch.cs index e8255064..bb4460cf 100644 --- a/SabreTools.DatItems/Formats/DipSwitch.cs +++ b/SabreTools.DatItems/Formats/DipSwitch.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using System.Xml.Serialization; using Newtonsoft.Json; using SabreTools.Core; @@ -19,25 +20,41 @@ namespace SabreTools.DatItems.Formats /// Name of the item /// [JsonProperty("name"), XmlElement("name")] - public string Name { get; set; } + public string? Name + { + get => _dipSwitch.ReadString(Models.Internal.DipSwitch.NameKey); + set => _dipSwitch[Models.Internal.DipSwitch.NameKey] = value; + } /// /// Tag associated with the dipswitch /// [JsonProperty("tag", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("tag")] - public string Tag { get; set; } + public string? Tag + { + get => _dipSwitch.ReadString(Models.Internal.DipSwitch.TagKey); + set => _dipSwitch[Models.Internal.DipSwitch.TagKey] = value; + } /// /// Mask associated with the dipswitch /// [JsonProperty("mask", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("mask")] - public string Mask { get; set; } + public string? Mask + { + get => _dipSwitch.ReadString(Models.Internal.DipSwitch.MaskKey); + set => _dipSwitch[Models.Internal.DipSwitch.MaskKey] = value; + } /// /// Conditions associated with the dipswitch /// [JsonProperty("conditions", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("conditions")] - public List Conditions { get; set; } + public List? Conditions + { + get => _dipSwitch.Read(Models.Internal.DipSwitch.ConditionKey)?.ToList(); + set => _dipSwitch[Models.Internal.DipSwitch.ConditionKey] = value?.ToArray(); + } [JsonIgnore] public bool ConditionsSpecified { get { return Conditions != null && Conditions.Count > 0; } } @@ -46,7 +63,11 @@ namespace SabreTools.DatItems.Formats /// Locations associated with the dipswitch /// [JsonProperty("locations", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("locations")] - public List Locations { get; set; } + public List? Locations + { + get => _dipSwitch.Read(Models.Internal.DipSwitch.DipLocationKey)?.ToList(); + set => _dipSwitch[Models.Internal.DipSwitch.DipLocationKey] = value?.ToArray(); + } [JsonIgnore] public bool LocationsSpecified { get { return Locations != null && Locations.Count > 0; } } @@ -55,7 +76,11 @@ namespace SabreTools.DatItems.Formats /// Settings associated with the dipswitch /// [JsonProperty("values", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("values")] - public List Values { get; set; } + public List? Values + { + get => _dipSwitch.Read(Models.Internal.DipSwitch.DipValueKey)?.ToList(); + set => _dipSwitch[Models.Internal.DipSwitch.DipValueKey] = value?.ToArray(); + } [JsonIgnore] public bool ValuesSpecified { get { return Values != null && Values.Count > 0; } } @@ -67,8 +92,9 @@ namespace SabreTools.DatItems.Formats /// /// Original hardware part associated with the item /// + /// This is inverted from the internal model [JsonProperty("part", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("part")] - public Part Part { get; set; } = null; + public Part? Part { get; set; } [JsonIgnore] public bool PartSpecified @@ -83,15 +109,21 @@ namespace SabreTools.DatItems.Formats #endregion + /// + /// Internal DipSwitch model + /// + [JsonIgnore] + private Models.Internal.DipSwitch _dipSwitch = new(); + #endregion // Fields #region Accessors /// - public override string GetName() => Name; + public override string? GetName() => Name; /// - public override void SetName(string name) => Name = name; + public override void SetName(string? name) => Name = name; #endregion @@ -118,16 +150,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - Name = this.Name, - Tag = this.Tag, - Mask = this.Mask, - Conditions = this.Conditions, - Locations = this.Locations, - Values = this.Values, + _dipSwitch = this._dipSwitch?.Clone() as Models.Internal.DipSwitch ?? new Models.Internal.DipSwitch(), Part = this.Part, }; @@ -138,54 +165,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { // If we don't have a DipSwitch, return false - if (ItemType != other.ItemType) + if (ItemType != other?.ItemType || other is not DipSwitch otherInternal) return false; - // Otherwise, treat it as a DipSwitch - DipSwitch newOther = other as DipSwitch; - - // If the DipSwitch information matches - bool match = (Name == newOther.Name - && Tag == newOther.Tag - && Mask == newOther.Mask); - if (!match) - return match; - - // If the part matches - if (PartSpecified) - match &= (Part == newOther.Part); - - // If the conditions match - if (ConditionsSpecified) - { - foreach (Condition condition in Conditions) - { - match &= newOther.Conditions.Contains(condition); - } - } - - // If the locations match - if (LocationsSpecified) - { - foreach (Location location in Locations) - { - match &= newOther.Locations.Contains(location); - } - } - - // If the values match - if (ValuesSpecified) - { - foreach (Setting value in Values) - { - match &= newOther.Values.Contains(value); - } - } - - return match; + // Compare the internal models + return _dipSwitch.EqualTo(otherInternal._dipSwitch); } #endregion diff --git a/SabreTools.DatItems/Formats/DipValue.cs b/SabreTools.DatItems/Formats/DipValue.cs new file mode 100644 index 00000000..0e406706 --- /dev/null +++ b/SabreTools.DatItems/Formats/DipValue.cs @@ -0,0 +1,129 @@ +using System.Collections.Generic; +using System.Linq; +using System.Xml.Serialization; +using Newtonsoft.Json; +using SabreTools.Core; + +namespace SabreTools.DatItems.Formats +{ + /// + /// Represents one ListXML dipvalue + /// + [JsonObject("dipvalue"), XmlRoot("dipvalue")] + public class DipValue : DatItem + { + #region Fields + + /// + /// Setting name + /// + [JsonProperty("name"), XmlElement("name")] + public string? Name + { + get => _dipValue.ReadString(Models.Internal.DipValue.NameKey); + set => _dipValue[Models.Internal.DipValue.NameKey] = value; + } + + /// + /// Setting value + /// + [JsonProperty("value", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("value")] + public string? Value + { + get => _dipValue.ReadString(Models.Internal.DipValue.ValueKey); + set => _dipValue[Models.Internal.DipValue.ValueKey] = value; + } + + /// + /// Determines if the setting is default or not + /// + [JsonProperty("default", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("default")] + public bool? Default + { + get => _dipValue.ReadBool(Models.Internal.DipValue.DefaultKey); + set => _dipValue[Models.Internal.DipValue.DefaultKey] = value; + } + + [JsonIgnore] + public bool DefaultSpecified { get { return Default != null; } } + + /// + /// List of conditions on the setting + /// + [JsonProperty("conditions", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("conditions")] + public List? Conditions + { + get => _dipValue.Read(Models.Internal.DipValue.ConditionKey)?.ToList(); + set => _dipValue[Models.Internal.DipValue.ConditionKey] = value?.ToArray(); + } + + [JsonIgnore] + public bool ConditionsSpecified { get { return Conditions != null && Conditions.Count > 0; } } + + /// + /// Internal DipValue model + /// + [JsonIgnore] + private Models.Internal.DipValue _dipValue = new(); + + #endregion + + #region Accessors + + /// + public override string? GetName() => Name; + + /// + public override void SetName(string? name) => Name = name; + + #endregion + + #region Constructors + + /// + /// Create a default, empty DipValue object + /// + public DipValue() + { + Name = string.Empty; + ItemType = ItemType.DipValue; + } + + #endregion + + #region Cloning Methods + + /// + public override object Clone() + { + return new DipValue() + { + ItemType = this.ItemType, + DupeType = this.DupeType, + + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, + Remove = this.Remove, + + _dipValue = this._dipValue?.Clone() as Models.Internal.DipValue ?? new Models.Internal.DipValue(), + }; + } + + #endregion + + #region Comparision Methods + + /// + public override bool Equals(DatItem? other) + { + // If we don't have a DipValue, return false + if (ItemType != other?.ItemType || other is not DipValue otherInternal) + return false; + + // Compare the internal models + return _dipValue.EqualTo(otherInternal._dipValue); + } + + #endregion + } +} diff --git a/SabreTools.DatItems/Formats/Disk.cs b/SabreTools.DatItems/Formats/Disk.cs index a1be653e..4422f565 100644 --- a/SabreTools.DatItems/Formats/Disk.cs +++ b/SabreTools.DatItems/Formats/Disk.cs @@ -13,13 +13,6 @@ namespace SabreTools.DatItems.Formats [JsonObject("disk"), XmlRoot("disk")] public class Disk : DatItem { - #region Private instance variables - - private byte[] _md5; // 16 bytes - private byte[] _sha1; // 20 bytes - - #endregion - #region Fields #region Common @@ -28,51 +21,71 @@ namespace SabreTools.DatItems.Formats /// Name of the item /// [JsonProperty("name"), XmlElement("name")] - public string Name { get; set; } + public string? Name + { + get => _disk.ReadString(Models.Internal.Disk.NameKey); + set => _disk[Models.Internal.Disk.NameKey] = value; + } /// /// Data MD5 hash /// [JsonProperty("md5", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("md5")] - public string MD5 + public string? MD5 { - get { return _md5.IsNullOrEmpty() ? null : Utilities.ByteArrayToString(_md5); } - set { _md5 = Utilities.StringToByteArray(CleanMD5(value)); } + get => _disk.ReadString(Models.Internal.Disk.MD5Key); + set => _disk[Models.Internal.Disk.MD5Key] = TextHelper.NormalizeMD5(value); } /// /// Data SHA-1 hash /// [JsonProperty("sha1", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("sha1")] - public string SHA1 + public string? SHA1 { - get { return _sha1.IsNullOrEmpty() ? null : Utilities.ByteArrayToString(_sha1); } - set { _sha1 = Utilities.StringToByteArray(CleanSHA1(value)); } + get => _disk.ReadString(Models.Internal.Disk.SHA1Key); + set => _disk[Models.Internal.Disk.SHA1Key] = TextHelper.NormalizeSHA1(value); } /// /// Disk name to merge from parent /// [JsonProperty("merge", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("merge")] - public string MergeTag { get; set; } + public string? MergeTag + { + get => _disk.ReadString(Models.Internal.Disk.MergeKey); + set => _disk[Models.Internal.Disk.MergeKey] = value; + } /// /// Disk region /// [JsonProperty("region", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("region")] - public string Region { get; set; } + public string? Region + { + get => _disk.ReadString(Models.Internal.Disk.RegionKey); + set => _disk[Models.Internal.Disk.RegionKey] = value; + } /// /// Disk index /// [JsonProperty("index", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("index")] - public string Index { get; set; } + public string? Index + { + get => _disk.ReadString(Models.Internal.Disk.IndexKey); + set => _disk[Models.Internal.Disk.IndexKey] = value; + } /// /// Disk writable flag /// [JsonProperty("writable", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("writable")] - public bool? Writable { get; set; } = null; + public bool? Writable + { + get => _disk.ReadBool(Models.Internal.Disk.WritableKey); + set => _disk[Models.Internal.Disk.WritableKey] = value; + } [JsonIgnore] public bool WritableSpecified { get { return Writable != null; } } @@ -82,7 +95,11 @@ namespace SabreTools.DatItems.Formats /// [JsonProperty("status", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("status")] [JsonConverter(typeof(StringEnumConverter))] - public ItemStatus ItemStatus { get; set; } + public ItemStatus ItemStatus + { + get => _disk.ReadString(Models.Internal.Disk.StatusKey).AsItemStatus(); + set => _disk[Models.Internal.Disk.StatusKey] = value.FromItemStatus(yesno: false); + } [JsonIgnore] public bool ItemStatusSpecified { get { return ItemStatus != ItemStatus.NULL; } } @@ -91,7 +108,11 @@ namespace SabreTools.DatItems.Formats /// Determine if the disk is optional in the set /// [JsonProperty("optional", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("optional")] - public bool? Optional { get; set; } = null; + public bool? Optional + { + get => _disk.ReadBool(Models.Internal.Disk.OptionalKey); + set => _disk[Models.Internal.Disk.OptionalKey] = value; + } [JsonIgnore] public bool OptionalSpecified { get { return Optional != null; } } @@ -103,8 +124,9 @@ namespace SabreTools.DatItems.Formats /// /// Disk area information /// + /// This is inverted from the internal model [JsonProperty("diskarea", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("diskarea")] - public DiskArea DiskArea { get; set; } = null; + public DiskArea? DiskArea { get; set; } [JsonIgnore] public bool DiskAreaSpecified @@ -119,8 +141,9 @@ namespace SabreTools.DatItems.Formats /// /// Original hardware part associated with the item /// + /// This is inverted from the internal model [JsonProperty("part", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("part")] - public Part Part { get; set; } = null; + public Part? Part { get; set; } [JsonIgnore] public bool PartSpecified @@ -135,15 +158,21 @@ namespace SabreTools.DatItems.Formats #endregion + /// + /// Internal Disk model + /// + [JsonIgnore] + private Models.Internal.Disk _disk = new(); + #endregion // Fields #region Accessors /// - public override string GetName() => Name; + public override string? GetName() => Name; /// - public override void SetName(string name) => Name = name; + public override void SetName(string? name) => Name = name; #endregion @@ -167,8 +196,8 @@ namespace SabreTools.DatItems.Formats public Disk(BaseFile baseFile) { Name = baseFile.Filename; - _md5 = baseFile.MD5; - _sha1 = baseFile.SHA1; + MD5 = Utilities.ByteArrayToString(baseFile.MD5); + SHA1 = Utilities.ByteArrayToString(baseFile.SHA1); ItemType = ItemType.Disk; DupeType = 0x00; @@ -188,18 +217,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - _md5 = this._md5, - _sha1 = this._sha1, - MergeTag = this.MergeTag, - Region = this.Region, - Index = this.Index, - Writable = this.Writable, - ItemStatus = this.ItemStatus, - Optional = this.Optional, + _disk = this._disk?.Clone() as Models.Internal.Disk ?? new Models.Internal.Disk(), DiskArea = this.DiskArea, Part = this.Part, @@ -215,8 +237,8 @@ namespace SabreTools.DatItems.Formats { Filename = this.Name, Parent = this.Machine?.Name, - MD5 = this._md5, - SHA1 = this._sha1, + MD5 = Utilities.StringToByteArray(this.MD5), + SHA1 = Utilities.StringToByteArray(this.SHA1), }; } @@ -232,8 +254,8 @@ namespace SabreTools.DatItems.Formats ItemType = ItemType.Rom, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, MergeTag = this.MergeTag, @@ -244,7 +266,7 @@ namespace SabreTools.DatItems.Formats MD5 = this.MD5, SHA1 = this.SHA1, - DataArea = new DataArea { Name = this.DiskArea.Name }, + DataArea = new DataArea { Name = this.DiskArea?.Name }, Part = this.Part, }; @@ -256,32 +278,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { - bool dupefound = false; + // If we don't have a Disk, return false + if (ItemType != other?.ItemType || other is not Disk otherInternal) + return false; - // If we don't have a rom, return false - if (ItemType != other.ItemType) - return dupefound; - - // Otherwise, treat it as a Disk - Disk newOther = other as Disk; - - // If all hashes are empty but they're both nodump and the names match, then they're dupes - if ((ItemStatus == ItemStatus.Nodump && newOther.ItemStatus == ItemStatus.Nodump) - && Name == newOther.Name - && !HasHashes() && !newOther.HasHashes()) - { - dupefound = true; - } - - // Otherwise if we get a partial match - else if (HashMatch(newOther)) - { - dupefound = true; - } - - return dupefound; + // Compare the internal models + return _disk.EqualTo(otherInternal._disk); } /// @@ -290,11 +294,11 @@ namespace SabreTools.DatItems.Formats /// Disk to fill information from public void FillMissingInformation(Disk other) { - if (_md5.IsNullOrEmpty() && !other._md5.IsNullOrEmpty()) - _md5 = other._md5; + if (string.IsNullOrWhiteSpace(MD5) && !string.IsNullOrWhiteSpace(other.MD5)) + MD5 = other.MD5; - if (_sha1.IsNullOrEmpty() && !other._sha1.IsNullOrEmpty()) - _sha1 = other._sha1; + if (string.IsNullOrWhiteSpace(SHA1) && !string.IsNullOrWhiteSpace(other.SHA1)) + SHA1 = other.SHA1; } /// @@ -303,55 +307,14 @@ namespace SabreTools.DatItems.Formats /// String representing the suffix public string GetDuplicateSuffix() { - if (!_md5.IsNullOrEmpty()) + if (!string.IsNullOrWhiteSpace(MD5)) return $"_{MD5}"; - else if (!_sha1.IsNullOrEmpty()) + else if (!string.IsNullOrWhiteSpace(SHA1)) return $"_{SHA1}"; else return "_1"; } - /// - /// Returns if there are no, non-empty hashes in common with another Disk - /// - /// Disk to compare against - /// True if at least one hash is not mutually exclusive, false otherwise - private bool HasCommonHash(Disk other) - { - return !(_md5.IsNullOrEmpty() ^ other._md5.IsNullOrEmpty()) - || !(_sha1.IsNullOrEmpty() ^ other._sha1.IsNullOrEmpty()); - } - - /// - /// Returns if the Disk contains any hashes - /// - /// True if any hash exists, false otherwise - private bool HasHashes() - { - return !_md5.IsNullOrEmpty() - || !_sha1.IsNullOrEmpty(); - } - - /// - /// Returns if any hashes are common with another Disk - /// - /// Disk to compare against - /// True if any hashes are in common, false otherwise - private bool HashMatch(Disk other) - { - // If either have no hashes, we return false, otherwise this would be a false positive - if (!HasHashes() || !other.HasHashes()) - return false; - - // If neither have hashes in common, we return false, otherwise this would be a false positive - if (!HasCommonHash(other)) - return false; - - // Return if all hashes match according to merge rules - return ConditionalHashEquals(_md5, other._md5) - && ConditionalHashEquals(_sha1, other._sha1); - } - #endregion #region Sorting and Merging @@ -360,7 +323,7 @@ namespace SabreTools.DatItems.Formats public override string GetKey(ItemKey bucketedBy, bool lower = true, bool norename = true) { // Set the output key as the default blank string - string key; + string? key; // Now determine what the key should be based on the bucketedBy value switch (bucketedBy) diff --git a/SabreTools.DatItems/Formats/DiskArea.cs b/SabreTools.DatItems/Formats/DiskArea.cs index 287e16e8..934750c2 100644 --- a/SabreTools.DatItems/Formats/DiskArea.cs +++ b/SabreTools.DatItems/Formats/DiskArea.cs @@ -17,17 +17,27 @@ namespace SabreTools.DatItems.Formats /// Name of the item /// [JsonProperty("name", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("name")] - public string Name { get; set; } + public string? Name + { + get => _diskArea.ReadString(Models.Internal.DiskArea.NameKey); + set => _diskArea[Models.Internal.DiskArea.NameKey] = value; + } + + /// + /// Internal DiskArea model + /// + [JsonIgnore] + private Models.Internal.DiskArea _diskArea = new(); #endregion #region Accessors /// - public override string GetName() => Name; + public override string? GetName() => Name; /// - public override void SetName(string name) => Name = name; + public override void SetName(string? name) => Name = name; #endregion @@ -54,11 +64,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - Name = this.Name, + _diskArea = this._diskArea?.Clone() as Models.Internal.DiskArea ?? new Models.Internal.DiskArea(), }; } @@ -67,17 +77,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { // If we don't have a DiskArea, return false - if (ItemType != other.ItemType) + if (ItemType != other?.ItemType || other is not DiskArea otherInternal) return false; - // Otherwise, treat it as a DiskArea - DiskArea newOther = other as DiskArea; - - // If the DiskArea information matches - return (Name == newOther.Name); + // Compare the internal models + return _diskArea.EqualTo(otherInternal._diskArea); } #endregion diff --git a/SabreTools.DatItems/Formats/Display.cs b/SabreTools.DatItems/Formats/Display.cs index 300a68b4..edb46632 100644 --- a/SabreTools.DatItems/Formats/Display.cs +++ b/SabreTools.DatItems/Formats/Display.cs @@ -2,6 +2,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; using SabreTools.Core; +using SabreTools.Core.Tools; namespace SabreTools.DatItems.Formats { @@ -17,14 +18,22 @@ namespace SabreTools.DatItems.Formats /// Display tag /// [JsonProperty("tag", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("tag")] - public string Tag { get; set; } + public string? Tag + { + get => _display.ReadString(Models.Internal.Display.TagKey); + set => _display[Models.Internal.Display.TagKey] = value; + } /// /// Display type /// [JsonProperty("type", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("type")] [JsonConverter(typeof(StringEnumConverter))] - public DisplayType DisplayType { get; set; } + public DisplayType DisplayType + { + get => _display.ReadString(Models.Internal.Display.DisplayTypeKey).AsDisplayType(); + set => _display[Models.Internal.Display.DisplayTypeKey] = value.FromDisplayType(); + } [JsonIgnore] public bool DisplayTypeSpecified { get { return DisplayType != DisplayType.NULL; } } @@ -33,7 +42,11 @@ namespace SabreTools.DatItems.Formats /// Display rotation /// [JsonProperty("rotate", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("rotate")] - public long? Rotate { get; set; } + public long? Rotate + { + get => _display.ReadLong(Models.Internal.Display.RotateKey); + set => _display[Models.Internal.Display.RotateKey] = value; + } [JsonIgnore] public bool RotateSpecified { get { return Rotate != null; } } @@ -42,7 +55,11 @@ namespace SabreTools.DatItems.Formats /// Determines if display is flipped in the X-coordinates /// [JsonProperty("flipx", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("flipx")] - public bool? FlipX { get; set; } + public bool? FlipX + { + get => _display.ReadBool(Models.Internal.Display.FlipXKey); + set => _display[Models.Internal.Display.FlipXKey] = value; + } [JsonIgnore] public bool FlipXSpecified { get { return FlipX != null; } } @@ -51,7 +68,11 @@ namespace SabreTools.DatItems.Formats /// Display width /// [JsonProperty("width", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("width")] - public long? Width { get; set; } + public long? Width + { + get => _display.ReadLong(Models.Internal.Display.WidthKey); + set => _display[Models.Internal.Display.WidthKey] = value; + } [JsonIgnore] public bool WidthSpecified { get { return Width != null; } } @@ -60,7 +81,11 @@ namespace SabreTools.DatItems.Formats /// Display height /// [JsonProperty("height", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("height")] - public long? Height { get; set; } + public long? Height + { + get => _display.ReadLong(Models.Internal.Display.HeightKey); + set => _display[Models.Internal.Display.HeightKey] = value; + } [JsonIgnore] public bool HeightSpecified { get { return Height != null; } } @@ -69,7 +94,11 @@ namespace SabreTools.DatItems.Formats /// Refresh rate /// [JsonProperty("refresh", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("refresh")] - public double? Refresh { get; set; } + public double? Refresh + { + get => _display.ReadDouble(Models.Internal.Display.RefreshKey); + set => _display[Models.Internal.Display.RefreshKey] = value; + } [JsonIgnore] public bool RefreshSpecified { get { return Refresh != null; } } @@ -78,7 +107,11 @@ namespace SabreTools.DatItems.Formats /// Pixel clock timer /// [JsonProperty("pixclock", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("pixclock")] - public long? PixClock { get; set; } + public long? PixClock + { + get => _display.ReadLong(Models.Internal.Display.PixClockKey); + set => _display[Models.Internal.Display.PixClockKey] = value; + } [JsonIgnore] public bool PixClockSpecified { get { return PixClock != null; } } @@ -87,7 +120,11 @@ namespace SabreTools.DatItems.Formats /// Total horizontal lines /// [JsonProperty("htotal", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("htotal")] - public long? HTotal { get; set; } + public long? HTotal + { + get => _display.ReadLong(Models.Internal.Display.HTotalKey); + set => _display[Models.Internal.Display.HTotalKey] = value; + } [JsonIgnore] public bool HTotalSpecified { get { return HTotal != null; } } @@ -96,7 +133,11 @@ namespace SabreTools.DatItems.Formats /// Horizontal blank end /// [JsonProperty("hbend", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("hbend")] - public long? HBEnd { get; set; } + public long? HBEnd + { + get => _display.ReadLong(Models.Internal.Display.HBEndKey); + set => _display[Models.Internal.Display.HBEndKey] = value; + } [JsonIgnore] public bool HBEndSpecified { get { return HBEnd != null; } } @@ -105,7 +146,11 @@ namespace SabreTools.DatItems.Formats /// Horizontal blank start /// [JsonProperty("hbstart", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("hbstart")] - public long? HBStart { get; set; } + public long? HBStart + { + get => _display.ReadLong(Models.Internal.Display.HBStartKey); + set => _display[Models.Internal.Display.HBStartKey] = value; + } [JsonIgnore] public bool HBStartSpecified { get { return HBStart != null; } } @@ -114,7 +159,11 @@ namespace SabreTools.DatItems.Formats /// Total vertical lines /// [JsonProperty("vtotal", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("vtotal")] - public long? VTotal { get; set; } + public long? VTotal + { + get => _display.ReadLong(Models.Internal.Display.VTotalKey); + set => _display[Models.Internal.Display.VTotalKey] = value; + } [JsonIgnore] public bool VTotalSpecified { get { return VTotal != null; } } @@ -123,7 +172,11 @@ namespace SabreTools.DatItems.Formats /// Vertical blank end /// [JsonProperty("vbend", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("vbend")] - public long? VBEnd { get; set; } + public long? VBEnd + { + get => _display.ReadLong(Models.Internal.Display.VBEndKey); + set => _display[Models.Internal.Display.VBEndKey] = value; + } [JsonIgnore] public bool VBEndSpecified { get { return VBEnd != null; } } @@ -132,11 +185,21 @@ namespace SabreTools.DatItems.Formats /// Vertical blank start /// [JsonProperty("vbstart", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("vbstart")] - public long? VBStart { get; set; } + public long? VBStart + { + get => _display.ReadLong(Models.Internal.Display.VBStartKey); + set => _display[Models.Internal.Display.VBStartKey] = value; + } [JsonIgnore] public bool VBStartSpecified { get { return VBStart != null; } } + /// + /// Internal Display model + /// + [JsonIgnore] + private Models.Internal.Display _display = new(); + #endregion #region Constructors @@ -161,24 +224,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - Tag = this.Tag, - DisplayType = this.DisplayType, - Rotate = this.Rotate, - FlipX = this.FlipX, - Width = this.Width, - Height = this.Height, - Refresh = this.Refresh, - PixClock = this.PixClock, - HTotal = this.HTotal, - HBEnd = this.HBEnd, - HBStart = this.HBStart, - VTotal = this.VTotal, - VBEnd = this.VBEnd, - VBStart = this.VBStart, + _display = this._display?.Clone() as Models.Internal.Display ?? new Models.Internal.Display(), }; } @@ -187,30 +237,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { // If we don't have a Display, return false - if (ItemType != other.ItemType) + if (ItemType != other?.ItemType || other is not Display otherInternal) return false; - // Otherwise, treat it as a Display - Display newOther = other as Display; - - // If the Display information matches - return (Tag == newOther.Tag - && DisplayType == newOther.DisplayType - && Rotate == newOther.Rotate - && FlipX == newOther.FlipX - && Width == newOther.Width - && Height == newOther.Height - && Refresh == newOther.Refresh - && PixClock == newOther.PixClock - && HTotal == newOther.HTotal - && HBEnd == newOther.HBEnd - && HBStart == newOther.HBStart - && VTotal == newOther.VTotal - && VBEnd == newOther.VBEnd - && VBStart == newOther.VBStart); + // Compare the internal models + return _display.EqualTo(otherInternal._display); } #endregion diff --git a/SabreTools.DatItems/Formats/Driver.cs b/SabreTools.DatItems/Formats/Driver.cs index 341e794a..a1b433fe 100644 --- a/SabreTools.DatItems/Formats/Driver.cs +++ b/SabreTools.DatItems/Formats/Driver.cs @@ -2,6 +2,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; using SabreTools.Core; +using SabreTools.Core.Tools; namespace SabreTools.DatItems.Formats { @@ -21,7 +22,11 @@ namespace SabreTools.DatItems.Formats /// [JsonProperty("status", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("status")] [JsonConverter(typeof(StringEnumConverter))] - public SupportStatus Status { get; set; } + public SupportStatus Status + { + get => _driver.ReadString(Models.Internal.Driver.StatusKey).AsSupportStatus(); + set => _driver[Models.Internal.Driver.StatusKey] = value.FromSupportStatus(); + } [JsonIgnore] public bool StatusSpecified { get { return Status != SupportStatus.NULL; } } @@ -31,7 +36,11 @@ namespace SabreTools.DatItems.Formats /// [JsonProperty("emulation", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("emulation")] [JsonConverter(typeof(StringEnumConverter))] - public SupportStatus Emulation { get; set; } + public SupportStatus Emulation + { + get => _driver.ReadString(Models.Internal.Driver.EmulationKey).AsSupportStatus(); + set => _driver[Models.Internal.Driver.EmulationKey] = value.FromSupportStatus(); + } [JsonIgnore] public bool EmulationSpecified { get { return Emulation != SupportStatus.NULL; } } @@ -41,7 +50,11 @@ namespace SabreTools.DatItems.Formats /// [JsonProperty("cocktail", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("cocktail")] [JsonConverter(typeof(StringEnumConverter))] - public SupportStatus Cocktail { get; set; } + public SupportStatus Cocktail + { + get => _driver.ReadString(Models.Internal.Driver.CocktailKey).AsSupportStatus(); + set => _driver[Models.Internal.Driver.CocktailKey] = value.FromSupportStatus(); + } [JsonIgnore] public bool CocktailSpecified { get { return Cocktail != SupportStatus.NULL; } } @@ -51,7 +64,11 @@ namespace SabreTools.DatItems.Formats /// [JsonProperty("savestate", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("savestate")] [JsonConverter(typeof(StringEnumConverter))] - public Supported SaveState { get; set; } + public Supported SaveState + { + get => _driver.ReadString(Models.Internal.Driver.SaveStateKey).AsSupported(); + set => _driver[Models.Internal.Driver.SaveStateKey] = value.FromSupported(verbose: true); + } [JsonIgnore] public bool SaveStateSpecified { get { return SaveState != Supported.NULL; } } @@ -60,7 +77,11 @@ namespace SabreTools.DatItems.Formats /// Requires artwork /// [JsonProperty("requiresartwork", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("requiresartwork")] - public bool? RequiresArtwork { get; set; } + public bool? RequiresArtwork + { + get => _driver.ReadBool(Models.Internal.Driver.RequiresArtworkKey); + set => _driver[Models.Internal.Driver.RequiresArtworkKey] = value; + } [JsonIgnore] public bool RequiresArtworkSpecified { get { return RequiresArtwork != null; } } @@ -69,7 +90,11 @@ namespace SabreTools.DatItems.Formats /// Unofficial /// [JsonProperty("unofficial", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("unofficial")] - public bool? Unofficial { get; set; } + public bool? Unofficial + { + get => _driver.ReadBool(Models.Internal.Driver.UnofficialKey); + set => _driver[Models.Internal.Driver.UnofficialKey] = value; + } [JsonIgnore] public bool UnofficialSpecified { get { return Unofficial != null; } } @@ -78,7 +103,11 @@ namespace SabreTools.DatItems.Formats /// No sound hardware /// [JsonProperty("nosoundhardware", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("nosoundhardware")] - public bool? NoSoundHardware { get; set; } + public bool? NoSoundHardware + { + get => _driver.ReadBool(Models.Internal.Driver.NoSoundHardwareKey); + set => _driver[Models.Internal.Driver.NoSoundHardwareKey] = value; + } [JsonIgnore] public bool NoSoundHardwareSpecified { get { return NoSoundHardware != null; } } @@ -87,11 +116,21 @@ namespace SabreTools.DatItems.Formats /// Incomplete /// [JsonProperty("incomplete", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("incomplete")] - public bool? Incomplete { get; set; } + public bool? Incomplete + { + get => _driver.ReadBool(Models.Internal.Driver.IncompleteKey); + set => _driver[Models.Internal.Driver.IncompleteKey] = value; + } [JsonIgnore] public bool IncompleteSpecified { get { return Incomplete != null; } } + /// + /// Internal Driver model + /// + [JsonIgnore] + private Models.Internal.Driver _driver = new(); + #endregion #region Constructors @@ -116,18 +155,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - Status = this.Status, - Emulation = this.Emulation, - Cocktail = this.Cocktail, - SaveState = this.SaveState, - RequiresArtwork = this.RequiresArtwork, - Unofficial = this.Unofficial, - NoSoundHardware = this.NoSoundHardware, - Incomplete = this.Incomplete, + _driver = this._driver?.Clone() as Models.Internal.Driver ?? new Models.Internal.Driver(), }; } @@ -136,24 +168,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { // If we don't have a Driver, return false - if (ItemType != other.ItemType) + if (ItemType != other?.ItemType || other is not Driver otherInternal) return false; - // Otherwise, treat it as a Driver - Driver newOther = other as Driver; - - // If the Feature information matches - return (Status == newOther.Status - && Emulation == newOther.Emulation - && Cocktail == newOther.Cocktail - && SaveState == newOther.SaveState - && RequiresArtwork == newOther.RequiresArtwork - && Unofficial == newOther.Unofficial - && NoSoundHardware == newOther.NoSoundHardware - && Incomplete == newOther.Incomplete); + // Compare the internal models + return _driver.EqualTo(otherInternal._driver); } #endregion diff --git a/SabreTools.DatItems/Formats/Extension.cs b/SabreTools.DatItems/Formats/Extension.cs index d2c86db6..48d00a1a 100644 --- a/SabreTools.DatItems/Formats/Extension.cs +++ b/SabreTools.DatItems/Formats/Extension.cs @@ -16,17 +16,27 @@ namespace SabreTools.DatItems.Formats /// Name of the item /// [JsonProperty("name"), XmlElement("name")] - public string Name { get; set; } + public string? Name + { + get => _extension.ReadString(Models.Internal.Extension.NameKey); + set => _extension[Models.Internal.Extension.NameKey] = value; + } + + /// + /// Internal Extension model + /// + [JsonIgnore] + private Models.Internal.Extension _extension = new(); #endregion #region Accessors /// - public override string GetName() => Name; + public override string? GetName() => Name; /// - public override void SetName(string name) => Name = name; + public override void SetName(string? name) => Name = name; #endregion @@ -53,11 +63,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - Name = this.Name, + _extension = this._extension?.Clone() as Models.Internal.Extension ?? new Models.Internal.Extension(), }; } @@ -66,17 +76,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { - // If we don't have a Extension, return false - if (ItemType != other.ItemType) + // If we don't have a Adjuster, return false + if (ItemType != other?.ItemType || other is not Extension otherInternal) return false; - // Otherwise, treat it as a Extension - Extension newOther = other as Extension; - - // If the Extension information matches - return (Name == newOther.Name); + // Compare the internal models + return _extension.EqualTo(otherInternal._extension); } #endregion diff --git a/SabreTools.DatItems/Formats/Feature.cs b/SabreTools.DatItems/Formats/Feature.cs index 5cfdb7ea..d70472dd 100644 --- a/SabreTools.DatItems/Formats/Feature.cs +++ b/SabreTools.DatItems/Formats/Feature.cs @@ -2,6 +2,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; using SabreTools.Core; +using SabreTools.Core.Tools; namespace SabreTools.DatItems.Formats { @@ -18,7 +19,11 @@ namespace SabreTools.DatItems.Formats /// [JsonProperty("type", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("type")] [JsonConverter(typeof(StringEnumConverter))] - public FeatureType Type { get; set; } + public FeatureType Type + { + get => _feature.ReadString(Models.Internal.Feature.FeatureTypeKey).AsFeatureType(); + set => _feature[Models.Internal.Feature.FeatureTypeKey] = value.FromFeatureType(); + } [JsonIgnore] public bool TypeSpecified { get { return Type != FeatureType.NULL; } } @@ -28,7 +33,11 @@ namespace SabreTools.DatItems.Formats /// [JsonProperty("status", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("status")] [JsonConverter(typeof(StringEnumConverter))] - public FeatureStatus Status { get; set; } + public FeatureStatus Status + { + get => _feature.ReadString(Models.Internal.Feature.StatusKey).AsFeatureStatus(); + set => _feature[Models.Internal.Feature.StatusKey] = value.FromFeatureStatus(); + } [JsonIgnore] public bool StatusSpecified { get { return Status != FeatureStatus.NULL; } } @@ -38,11 +47,21 @@ namespace SabreTools.DatItems.Formats /// [JsonProperty("overall", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("overall")] [JsonConverter(typeof(StringEnumConverter))] - public FeatureStatus Overall { get; set; } + public FeatureStatus Overall + { + get => _feature.ReadString(Models.Internal.Feature.OverallKey).AsFeatureStatus(); + set => _feature[Models.Internal.Feature.OverallKey] = value.FromFeatureStatus(); + } [JsonIgnore] public bool OverallSpecified { get { return Overall != FeatureStatus.NULL; } } + /// + /// Internal Feature model + /// + [JsonIgnore] + private Models.Internal.Feature _feature = new(); + #endregion #region Constructors @@ -67,13 +86,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - Type = this.Type, - Status = this.Status, - Overall = this.Overall, + _feature = this._feature?.Clone() as Models.Internal.Feature ?? new Models.Internal.Feature(), }; } @@ -82,19 +99,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { // If we don't have a Feature, return false - if (ItemType != other.ItemType) + if (ItemType != other?.ItemType || other is not Feature otherInternal) return false; - // Otherwise, treat it as a Feature - Feature newOther = other as Feature; - - // If the Feature information matches - return (Type == newOther.Type - && Status == newOther.Status - && Overall == newOther.Overall); + // Compare the internal models + return _feature.EqualTo(otherInternal._feature); } #endregion diff --git a/SabreTools.DatItems/Formats/File.cs b/SabreTools.DatItems/Formats/File.cs index 41ff085b..ebd021a7 100644 --- a/SabreTools.DatItems/Formats/File.cs +++ b/SabreTools.DatItems/Formats/File.cs @@ -17,10 +17,10 @@ namespace SabreTools.DatItems.Formats { #region Private instance variables - private byte[] _crc; // 8 bytes - private byte[] _md5; // 16 bytes - private byte[] _sha1; // 20 bytes - private byte[] _sha256; // 32 bytes + private byte[]? _crc; // 8 bytes + private byte[]? _md5; // 16 bytes + private byte[]? _sha1; // 20 bytes + private byte[]? _sha256; // 32 bytes #endregion @@ -30,13 +30,13 @@ namespace SabreTools.DatItems.Formats /// ID value /// [JsonProperty("id", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("id")] - public string Id { get; set; } + public string? Id { get; set; } /// /// Extension value /// [JsonProperty("extension", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("extension")] - public string Extension { get; set; } + public string? Extension { get; set; } /// /// Byte size of the rom @@ -51,47 +51,47 @@ namespace SabreTools.DatItems.Formats /// File CRC32 hash /// [JsonProperty("crc", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("crc")] - public string CRC + public string? CRC { get { return _crc.IsNullOrEmpty() ? null : Utilities.ByteArrayToString(_crc); } - set { _crc = (value == "null" ? Constants.CRCZeroBytes : Utilities.StringToByteArray(CleanCRC32(value))); } + set { _crc = (value == "null" ? Constants.CRCZeroBytes : Utilities.StringToByteArray(TextHelper.NormalizeCRC32(value))); } } /// /// File MD5 hash /// [JsonProperty("md5", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("md5")] - public string MD5 + public string? MD5 { get { return _md5.IsNullOrEmpty() ? null : Utilities.ByteArrayToString(_md5); } - set { _md5 = Utilities.StringToByteArray(CleanMD5(value)); } + set { _md5 = Utilities.StringToByteArray(TextHelper.NormalizeMD5(value)); } } /// /// File SHA-1 hash /// [JsonProperty("sha1", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("sha1")] - public string SHA1 + public string? SHA1 { get { return _sha1.IsNullOrEmpty() ? null : Utilities.ByteArrayToString(_sha1); } - set { _sha1 = Utilities.StringToByteArray(CleanSHA1(value)); } + set { _sha1 = Utilities.StringToByteArray(TextHelper.NormalizeSHA1(value)); } } /// /// File SHA-256 hash /// [JsonProperty("sha256", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("sha256")] - public string SHA256 + public string? SHA256 { get { return _sha256.IsNullOrEmpty() ? null : Utilities.ByteArrayToString(_sha256); } - set { _sha256 = Utilities.StringToByteArray(CleanSHA256(value)); } + set { _sha256 = Utilities.StringToByteArray(TextHelper.NormalizeSHA256(value)); } } /// /// Format value /// [JsonProperty("format", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("format")] - public string Format { get; set; } + public string? Format { get; set; } #endregion // Fields @@ -132,8 +132,8 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, Id = this.Id, @@ -174,8 +174,8 @@ namespace SabreTools.DatItems.Formats ItemType = ItemType.Rom, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, CRC = this.CRC, @@ -192,31 +192,31 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { bool dupefound = false; // If we don't have a file, return false - if (ItemType != other.ItemType) + if (ItemType != other?.ItemType) return dupefound; // Otherwise, treat it as a File - File newOther = other as File; + File? newOther = other as File; // If all hashes are empty, then they're dupes - if (!HasHashes() && !newOther.HasHashes()) + if (!HasHashes() && !newOther!.HasHashes()) { dupefound = true; } // If we have a file that has no known size, rely on the hashes only - else if (Size == null && HashMatch(newOther)) + else if (Size == null && HashMatch(newOther!)) { dupefound = true; } // Otherwise if we get a partial match - else if (Size == newOther.Size && HashMatch(newOther)) + else if (Size == newOther!.Size && HashMatch(newOther)) { dupefound = true; } @@ -331,7 +331,7 @@ namespace SabreTools.DatItems.Formats public override string GetKey(ItemKey bucketedBy, bool lower = true, bool norename = true) { // Set the output key as the default blank string - string key; + string? key; // Now determine what the key should be based on the bucketedBy value switch (bucketedBy) diff --git a/SabreTools.DatItems/Formats/Info.cs b/SabreTools.DatItems/Formats/Info.cs index bff88885..611e0b6d 100644 --- a/SabreTools.DatItems/Formats/Info.cs +++ b/SabreTools.DatItems/Formats/Info.cs @@ -16,23 +16,37 @@ namespace SabreTools.DatItems.Formats /// Name of the item /// [JsonProperty("name"), XmlElement("name")] - public string Name { get; set; } + public string? Name + { + get => _info.ReadString(Models.Internal.Info.NameKey); + set => _info[Models.Internal.Info.NameKey] = value; + } /// /// Information value /// [JsonProperty("value"), XmlElement("value")] - public string Value { get; set; } + public string? Value + { + get => _info.ReadString(Models.Internal.Info.ValueKey); + set => _info[Models.Internal.Info.ValueKey] = value; + } + + /// + /// Internal Info model + /// + [JsonIgnore] + private Models.Internal.Info _info = new(); #endregion #region Accessors /// - public override string GetName() => Name; + public override string? GetName() => Name; /// - public override void SetName(string name) => Name = name; + public override void SetName(string? name) => Name = name; #endregion @@ -59,12 +73,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - Name = this.Name, - Value = this.Value, + _info = this._info?.Clone() as Models.Internal.Info ?? new Models.Internal.Info(), }; } @@ -73,18 +86,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { - // If we don't have a sample, return false - if (ItemType != other.ItemType) + // If we don't have a Info, return false + if (ItemType != other?.ItemType || other is not Info otherInternal) return false; - // Otherwise, treat it as a Info - Info newOther = other as Info; - - // If the archive information matches - return (Name == newOther.Name - && Value == newOther.Value); + // Compare the internal models + return _info.EqualTo(otherInternal._info); } #endregion diff --git a/SabreTools.DatItems/Formats/Input.cs b/SabreTools.DatItems/Formats/Input.cs index ab1129df..f38fee88 100644 --- a/SabreTools.DatItems/Formats/Input.cs +++ b/SabreTools.DatItems/Formats/Input.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using System.Xml.Serialization; using Newtonsoft.Json; using SabreTools.Core; @@ -17,7 +18,11 @@ namespace SabreTools.DatItems.Formats /// Input service ID /// [JsonProperty("service", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("service")] - public bool? Service { get; set; } + public bool? Service + { + get => _input.ReadBool(Models.Internal.Input.ServiceKey); + set => _input[Models.Internal.Input.ServiceKey] = value; + } [JsonIgnore] public bool ServiceSpecified { get { return Service != null; } } @@ -26,7 +31,11 @@ namespace SabreTools.DatItems.Formats /// Determins if this has a tilt sensor /// [JsonProperty("tilt", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("tilt")] - public bool? Tilt { get; set; } + public bool? Tilt + { + get => _input.ReadBool(Models.Internal.Input.TiltKey); + set => _input[Models.Internal.Input.TiltKey] = value; + } [JsonIgnore] public bool TiltSpecified { get { return Tilt != null; } } @@ -35,7 +44,11 @@ namespace SabreTools.DatItems.Formats /// Number of players on the input /// [JsonProperty("players", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("players")] - public long? Players { get; set; } + public long? Players + { + get => _input.ReadLong(Models.Internal.Input.PlayersKey); + set => _input[Models.Internal.Input.PlayersKey] = value; + } [JsonIgnore] public bool PlayersSpecified { get { return Players != null; } } @@ -44,7 +57,11 @@ namespace SabreTools.DatItems.Formats /// Number of coins required /// [JsonProperty("coins", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("coins")] - public long? Coins { get; set; } + public long? Coins + { + get => _input.ReadLong(Models.Internal.Input.CoinsKey); + set => _input[Models.Internal.Input.CoinsKey] = value; + } [JsonIgnore] public bool CoinsSpecified { get { return Coins != null; } } @@ -53,11 +70,21 @@ namespace SabreTools.DatItems.Formats /// Set of controls for the input /// [JsonProperty("controls", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("controls")] - public List Controls { get; set; } + public List? Controls + { + get => _input.Read(Models.Internal.Input.ControlKey)?.ToList(); + set => _input[Models.Internal.Input.ControlKey] = value?.ToArray(); + } [JsonIgnore] public bool ControlsSpecified { get { return Controls != null && Controls.Count > 0; } } + /// + /// Internal Input model + /// + [JsonIgnore] + private Models.Internal.Input _input = new(); + #endregion #region Constructors @@ -82,15 +109,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - Service = this.Service, - Tilt = this.Tilt, - Players = this.Players, - Coins = this.Coins, - Controls = this.Controls, + _input = this._input?.Clone() as Models.Internal.Input ?? new Models.Internal.Input(), }; } @@ -99,33 +122,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { // If we don't have a Input, return false - if (ItemType != other.ItemType) + if (ItemType != other?.ItemType || other is not Input otherInternal) return false; - // Otherwise, treat it as a Input - Input newOther = other as Input; - - // If the Input information matches - bool match = (Service == newOther.Service - && Tilt == newOther.Tilt - && Players == newOther.Players - && Coins == newOther.Coins); - if (!match) - return match; - - // If the controls match - if (ControlsSpecified) - { - foreach (Control control in Controls) - { - match &= newOther.Controls.Contains(control); - } - } - - return match; + // Compare the internal models + return _input.EqualTo(otherInternal._input); } #endregion diff --git a/SabreTools.DatItems/Formats/Instance.cs b/SabreTools.DatItems/Formats/Instance.cs index 3aa1fef7..b77242fe 100644 --- a/SabreTools.DatItems/Formats/Instance.cs +++ b/SabreTools.DatItems/Formats/Instance.cs @@ -16,23 +16,37 @@ namespace SabreTools.DatItems.Formats /// Name of the item /// [JsonProperty("name"), XmlElement("name")] - public string Name { get; set; } + public string? Name + { + get => _instance.ReadString(Models.Internal.Instance.NameKey); + set => _instance[Models.Internal.Instance.NameKey] = value; + } /// /// Short name for the instance /// [JsonProperty("briefname", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("briefname")] - public string BriefName { get; set; } + public string? BriefName + { + get => _instance.ReadString(Models.Internal.Instance.BriefNameKey); + set => _instance[Models.Internal.Instance.BriefNameKey] = value; + } + + /// + /// Internal Instance model + /// + [JsonIgnore] + private Models.Internal.Instance _instance = new(); #endregion #region Accessors /// - public override string GetName() => Name; + public override string? GetName() => Name; /// - public override void SetName(string name) => Name = name; + public override void SetName(string? name) => Name = name; #endregion @@ -59,12 +73,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - Name = this.Name, - BriefName = this.BriefName, + _instance = this._instance?.Clone() as Models.Internal.Instance ?? new Models.Internal.Instance(), }; } @@ -73,18 +86,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { // If we don't have a Instance, return false - if (ItemType != other.ItemType) + if (ItemType != other?.ItemType || other is not Instance otherInternal) return false; - // Otherwise, treat it as a Instance - Instance newOther = other as Instance; - - // If the Instance information matches - return (Name == newOther.Name - && BriefName == newOther.BriefName); + // Compare the internal models + return _instance.EqualTo(otherInternal._instance); } #endregion diff --git a/SabreTools.DatItems/Formats/Location.cs b/SabreTools.DatItems/Formats/Location.cs deleted file mode 100644 index 1484c13f..00000000 --- a/SabreTools.DatItems/Formats/Location.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System.Xml.Serialization; -using Newtonsoft.Json; -using SabreTools.Core; - -namespace SabreTools.DatItems.Formats -{ - /// - /// Represents one conflocation or diplocation - /// - [JsonObject("location"), XmlRoot("location")] - public class Location : DatItem - { - #region Fields - - /// - /// Location name - /// - [JsonProperty("name"), XmlElement("name")] - public string Name { get; set; } - - /// - /// Location ID - /// - [JsonProperty("number", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("number")] - public long? Number { get; set; } - - [JsonIgnore] - public bool NumberSpecified { get { return Number != null; } } - - /// - /// Determines if location is inverted or not - /// - [JsonProperty("inverted", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("inverted")] - public bool? Inverted { get; set; } - - [JsonIgnore] - public bool InvertedSpecified { get { return Inverted != null; } } - - #endregion - - #region Accessors - - /// - public override string GetName() => Name; - - /// - public override void SetName(string name) => Name = name; - - #endregion - - #region Constructors - - /// - /// Create a default, empty Location object - /// - public Location() - { - Name = string.Empty; - ItemType = ItemType.Location; - } - - #endregion - - #region Cloning Methods - - /// - public override object Clone() - { - return new Location() - { - ItemType = this.ItemType, - DupeType = this.DupeType, - - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, - Remove = this.Remove, - - Name = this.Name, - Number = this.Number, - Inverted = this.Inverted, - }; - } - - #endregion - - #region Comparision Methods - - /// - public override bool Equals(DatItem other) - { - // If we don't have a Location, return false - if (ItemType != other.ItemType) - return false; - - // Otherwise, treat it as a Location - Location newOther = other as Location; - - // If the Location information matches - return (Name == newOther.Name - && Number == newOther.Number - && Inverted == newOther.Inverted); - } - - #endregion - } -} diff --git a/SabreTools.DatItems/Formats/Media.cs b/SabreTools.DatItems/Formats/Media.cs index 8bd56da3..a2cb91d9 100644 --- a/SabreTools.DatItems/Formats/Media.cs +++ b/SabreTools.DatItems/Formats/Media.cs @@ -1,5 +1,4 @@ -using System.Text; -using System.Xml.Serialization; +using System.Xml.Serialization; using Newtonsoft.Json; using SabreTools.Core; using SabreTools.Core.Tools; @@ -13,72 +12,73 @@ namespace SabreTools.DatItems.Formats [JsonObject("media"), XmlRoot("media")] public class Media : DatItem { - #region Private instance variables - - private byte[] _md5; // 16 bytes - private byte[] _sha1; // 20 bytes - private byte[] _sha256; // 32 bytes - private byte[] _spamsum; // variable bytes - - #endregion - #region Fields /// /// Name of the item /// [JsonProperty("name"), XmlElement("name")] - public string Name { get; set; } + public string? Name + { + get => _media.ReadString(Models.Internal.Media.NameKey); + set => _media[Models.Internal.Media.NameKey] = value; + } /// /// Data MD5 hash /// [JsonProperty("md5", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("md5")] - public string MD5 + public string? MD5 { - get { return _md5.IsNullOrEmpty() ? null : Utilities.ByteArrayToString(_md5); } - set { _md5 = Utilities.StringToByteArray(CleanMD5(value)); } + get => _media.ReadString(Models.Internal.Media.MD5Key); + set => _media[Models.Internal.Media.MD5Key] = TextHelper.NormalizeMD5(value); } /// /// Data SHA-1 hash /// [JsonProperty("sha1", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("sha1")] - public string SHA1 + public string? SHA1 { - get { return _sha1.IsNullOrEmpty() ? null : Utilities.ByteArrayToString(_sha1); } - set { _sha1 = Utilities.StringToByteArray(CleanSHA1(value)); } + get => _media.ReadString(Models.Internal.Media.SHA1Key); + set => _media[Models.Internal.Media.SHA1Key] = TextHelper.NormalizeSHA1(value); } /// /// Data SHA-256 hash /// [JsonProperty("sha256", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("sha256")] - public string SHA256 + public string? SHA256 { - get { return _sha256.IsNullOrEmpty() ? null : Utilities.ByteArrayToString(_sha256); } - set { _sha256 = Utilities.StringToByteArray(CleanSHA256(value)); } + get => _media.ReadString(Models.Internal.Media.SHA256Key); + set => _media[Models.Internal.Media.SHA256Key] = TextHelper.NormalizeSHA256(value); } /// /// File SpamSum fuzzy hash /// [JsonProperty("spamsum", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("spamsum")] - public string SpamSum + public string? SpamSum { - get { return _spamsum.IsNullOrEmpty() ? null : Encoding.UTF8.GetString(_spamsum); } - set { _spamsum = Encoding.UTF8.GetBytes(value ?? string.Empty); } + get => _media.ReadString(Models.Internal.Media.SpamSumKey); + set => _media[Models.Internal.Media.SpamSumKey] = value; } + /// + /// Internal Media model + /// + [JsonIgnore] + private Models.Internal.Media _media = new(); + #endregion #region Accessors /// - public override string GetName() => Name; + public override string? GetName() => Name; /// - public override void SetName(string name) => Name = name; + public override void SetName(string? name) => Name = name; #endregion @@ -101,10 +101,10 @@ namespace SabreTools.DatItems.Formats public Media(BaseFile baseFile) { Name = baseFile.Filename; - _md5 = baseFile.MD5; - _sha1 = baseFile.SHA1; - _sha256 = baseFile.SHA256; - _spamsum = baseFile.SpamSum; + MD5 = Utilities.ByteArrayToString(baseFile.MD5); + SHA1 = Utilities.ByteArrayToString(baseFile.SHA1); + SHA256 = Utilities.ByteArrayToString(baseFile.SHA256); + SpamSum = Utilities.ByteArrayToString(baseFile.SpamSum); ItemType = ItemType.Media; DupeType = 0x00; @@ -122,15 +122,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - Name = this.Name, - _md5 = this._md5, - _sha1 = this._sha1, - _sha256 = this._sha256, - _spamsum = this._spamsum, + _media = this._media?.Clone() as Models.Internal.Media ?? new Models.Internal.Media(), }; } @@ -143,10 +139,10 @@ namespace SabreTools.DatItems.Formats { Filename = this.Name, Parent = this.Machine?.Name, - MD5 = this._md5, - SHA1 = this._sha1, - SHA256 = this._sha256, - SpamSum = this._spamsum, + MD5 = Utilities.StringToByteArray(this.MD5), + SHA1 = Utilities.StringToByteArray(this.SHA1), + SHA256 = Utilities.StringToByteArray(this.SHA256), + SpamSum = Utilities.StringToByteArray(this.SpamSum), }; } @@ -161,8 +157,8 @@ namespace SabreTools.DatItems.Formats ItemType = ItemType.Rom, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, Name = this.Name + ".aif", @@ -180,22 +176,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { - bool dupefound = false; - // If we don't have a Media, return false - if (ItemType != other.ItemType) - return dupefound; + if (ItemType != other?.ItemType || other is not Media otherInternal) + return false; - // Otherwise, treat it as a Media - Media newOther = other as Media; - - // If we get a partial match - if (HashMatch(newOther)) - dupefound = true; - - return dupefound; + // Compare the internal models + return _media.EqualTo(otherInternal._media); } /// @@ -204,17 +192,17 @@ namespace SabreTools.DatItems.Formats /// Media to fill information from public void FillMissingInformation(Media other) { - if (_md5.IsNullOrEmpty() && !other._md5.IsNullOrEmpty()) - _md5 = other._md5; + if (string.IsNullOrWhiteSpace(MD5) && !string.IsNullOrWhiteSpace(other.MD5)) + MD5 = other.MD5; - if (_sha1.IsNullOrEmpty() && !other._sha1.IsNullOrEmpty()) - _sha1 = other._sha1; + if (string.IsNullOrWhiteSpace(SHA1) && !string.IsNullOrWhiteSpace(other.SHA1)) + SHA1 = other.SHA1; - if (_sha256.IsNullOrEmpty() && !other._sha256.IsNullOrEmpty()) - _sha256 = other._sha256; + if (string.IsNullOrWhiteSpace(SHA256) && !string.IsNullOrWhiteSpace(other.SHA256)) + SHA256 = other.SHA256; - if (_spamsum.IsNullOrEmpty() && !other._spamsum.IsNullOrEmpty()) - _spamsum = other._spamsum; + if (string.IsNullOrWhiteSpace(SpamSum) && !string.IsNullOrWhiteSpace(other.SpamSum)) + SpamSum = other.SpamSum; } /// @@ -223,64 +211,18 @@ namespace SabreTools.DatItems.Formats /// String representing the suffix public string GetDuplicateSuffix() { - if (!_md5.IsNullOrEmpty()) + if (!string.IsNullOrWhiteSpace(MD5)) return $"_{MD5}"; - else if (!_sha1.IsNullOrEmpty()) + else if (!string.IsNullOrWhiteSpace(SHA1)) return $"_{SHA1}"; - else if (!_sha256.IsNullOrEmpty()) + else if (!string.IsNullOrWhiteSpace(SHA256)) return $"_{SHA256}"; - else if (!_spamsum.IsNullOrEmpty()) + else if (!string.IsNullOrWhiteSpace(SpamSum)) return $"_{SpamSum}"; else return "_1"; } - /// - /// Returns if there are no, non-empty hashes in common with another Media - /// - /// Media to compare against - /// True if at least one hash is not mutually exclusive, false otherwise - private bool HasCommonHash(Media other) - { - return !(_md5.IsNullOrEmpty() ^ other._md5.IsNullOrEmpty()) - || !(_sha1.IsNullOrEmpty() ^ other._sha1.IsNullOrEmpty()) - || !(_sha256.IsNullOrEmpty() ^ other._sha256.IsNullOrEmpty()) - || !(_spamsum.IsNullOrEmpty() ^ other._spamsum.IsNullOrEmpty()); - } - - /// - /// Returns if the Media contains any hashes - /// - /// True if any hash exists, false otherwise - private bool HasHashes() - { - return !_md5.IsNullOrEmpty() - || !_sha1.IsNullOrEmpty() - || !_sha256.IsNullOrEmpty() - || !_spamsum.IsNullOrEmpty(); - } - - /// - /// Returns if any hashes are common with another Media - /// - /// Media to compare against - /// True if any hashes are in common, false otherwise - private bool HashMatch(Media other) - { - // If either have no hashes, we return false, otherwise this would be a false positive - if (!HasHashes() || !other.HasHashes()) - return false; - - // If neither have hashes in common, we return false, otherwise this would be a false positive - if (!HasCommonHash(other)) - return false; - - // Return if all hashes match according to merge rules - return ConditionalHashEquals(_md5, other._md5) - && ConditionalHashEquals(_sha1, other._sha1) - && ConditionalHashEquals(_spamsum, other._spamsum); - } - #endregion #region Sorting and Merging @@ -289,7 +231,7 @@ namespace SabreTools.DatItems.Formats public override string GetKey(ItemKey bucketedBy, bool lower = true, bool norename = true) { // Set the output key as the default blank string - string key; + string? key; // Now determine what the key should be based on the bucketedBy value switch (bucketedBy) diff --git a/SabreTools.DatItems/Formats/Part.cs b/SabreTools.DatItems/Formats/Part.cs index fde80631..0b1c49cc 100644 --- a/SabreTools.DatItems/Formats/Part.cs +++ b/SabreTools.DatItems/Formats/Part.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using System.Xml.Serialization; using Newtonsoft.Json; using SabreTools.Core; @@ -15,26 +16,44 @@ namespace SabreTools.DatItems.Formats #region Fields [JsonProperty("name"), XmlElement("name")] - public string Name { get; set; } + public string? Name + { + get => _part.ReadString(Models.Internal.Part.NameKey); + set => _part[Models.Internal.Part.NameKey] = value; + } [JsonProperty("interface"), XmlElement("interface")] - public string Interface { get; set; } + public string? Interface + { + get => _part.ReadString(Models.Internal.Part.InterfaceKey); + set => _part[Models.Internal.Part.InterfaceKey] = value; + } [JsonProperty("features", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("features")] - public List Features { get; set; } + public List? Features + { + get => _part.Read(Models.Internal.Part.FeatureKey)?.ToList(); + set => _part[Models.Internal.Part.FeatureKey] = value?.ToArray(); + } [JsonIgnore] public bool FeaturesSpecified { get { return Features != null && Features.Count > 0; } } + /// + /// Internal Part model + /// + [JsonIgnore] + private Models.Internal.Part _part = new(); + #endregion #region Accessors /// - public override string GetName() => Name; + public override string? GetName() => Name; /// - public override void SetName(string name) => Name = name; + public override void SetName(string? name) => Name = name; #endregion @@ -61,13 +80,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - Name = this.Name, - Interface = this.Interface, - Features = this.Features, + _part = this._part?.Clone() as Models.Internal.Part ?? new Models.Internal.Part(), }; } @@ -76,31 +93,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { // If we don't have a Part, return false - if (ItemType != other.ItemType) + if (ItemType != other?.ItemType || other is not Part otherInternal) return false; - // Otherwise, treat it as a Part - Part newOther = other as Part; - - // If the Part information matches - bool match = (Name == newOther.Name - && Interface == newOther.Interface); - if (!match) - return match; - - // If the features match - if (FeaturesSpecified) - { - foreach (PartFeature partFeature in Features) - { - match &= newOther.Features.Contains(partFeature); - } - } - - return match; + // Compare the internal models + return _part.EqualTo(otherInternal._part); } #endregion diff --git a/SabreTools.DatItems/Formats/PartFeature.cs b/SabreTools.DatItems/Formats/PartFeature.cs index 75cb0407..2182d416 100644 --- a/SabreTools.DatItems/Formats/PartFeature.cs +++ b/SabreTools.DatItems/Formats/PartFeature.cs @@ -16,23 +16,37 @@ namespace SabreTools.DatItems.Formats /// Name of the item /// [JsonProperty("name"), XmlElement("name")] - public string Name { get; set; } + public string? Name + { + get => _feature.ReadString(Models.Internal.Feature.NameKey); + set => _feature[Models.Internal.Feature.NameKey] = value; + } /// /// PartFeature value /// [JsonProperty("value"), XmlElement("value")] - public string Value { get; set; } + public string? Value + { + get => _feature.ReadString(Models.Internal.Feature.ValueKey); + set => _feature[Models.Internal.Feature.ValueKey] = value; + } + + /// + /// Internal Feature model + /// + [JsonIgnore] + private Models.Internal.Feature _feature = new(); #endregion #region Accessors /// - public override string GetName() => Name; + public override string? GetName() => Name; /// - public override void SetName(string name) => Name = name; + public override void SetName(string? name) => Name = name; #endregion @@ -59,12 +73,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - Name = this.Name, - Value = this.Value, + _feature = this._feature?.Clone() as Models.Internal.Feature ?? new Models.Internal.Feature(), }; } @@ -73,18 +86,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { - // If we don't have a sample, return false - if (ItemType != other.ItemType) + // If we don't have a PartFeature, return false + if (ItemType != other?.ItemType || other is not PartFeature otherInternal) return false; - // Otherwise, treat it as a PartFeature - PartFeature newOther = other as PartFeature; - - // If the archive information matches - return (Name == newOther.Name - && Value == newOther.Value); + // Compare the internal models + return _feature.EqualTo(otherInternal._feature); } #endregion diff --git a/SabreTools.DatItems/Formats/Port.cs b/SabreTools.DatItems/Formats/Port.cs index 8f26709d..2134e621 100644 --- a/SabreTools.DatItems/Formats/Port.cs +++ b/SabreTools.DatItems/Formats/Port.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using System.Xml.Serialization; using Newtonsoft.Json; using SabreTools.Core; @@ -17,17 +18,31 @@ namespace SabreTools.DatItems.Formats /// Tag for the port /// [JsonProperty("tag", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("tag")] - public string Tag { get; set; } + public string? Tag + { + get => _port.ReadString(Models.Internal.Port.TagKey); + set => _port[Models.Internal.Port.TagKey] = value; + } /// /// List of analogs on the port /// [JsonProperty("analogs", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("analogs")] - public List Analogs { get; set; } + public List? Analogs + { + get => _port.Read(Models.Internal.Port.AnalogKey)?.ToList(); + set => _port[Models.Internal.Port.AnalogKey] = value?.ToArray(); + } [JsonIgnore] public bool AnalogsSpecified { get { return Analogs != null && Analogs.Count > 0; } } + /// + /// Internal Port model + /// + [JsonIgnore] + private Models.Internal.Port _port = new(); + #endregion #region Constructors @@ -52,12 +67,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - Tag = this.Tag, - Analogs = this.Analogs, + _port = this._port?.Clone() as Models.Internal.Port ?? new Models.Internal.Port(), }; } @@ -66,30 +80,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { // If we don't have a Port, return false - if (ItemType != other.ItemType) + if (ItemType != other?.ItemType || other is not Port otherInternal) return false; - // Otherwise, treat it as a Port - Port newOther = other as Port; - - // If the Port information matches - bool match = (Tag == newOther.Tag); - if (!match) - return match; - - // If the analogs match - if (AnalogsSpecified) - { - foreach (Analog analog in Analogs) - { - match &= newOther.Analogs.Contains(analog); - } - } - - return match; + // Compare the internal models + return _port.EqualTo(otherInternal._port); } #endregion diff --git a/SabreTools.DatItems/Formats/RamOption.cs b/SabreTools.DatItems/Formats/RamOption.cs index 7c3695c9..23ff09fb 100644 --- a/SabreTools.DatItems/Formats/RamOption.cs +++ b/SabreTools.DatItems/Formats/RamOption.cs @@ -16,13 +16,21 @@ namespace SabreTools.DatItems.Formats /// Name of the item /// [JsonProperty("name"), XmlElement("name")] - public string Name { get; set; } + public string? Name + { + get => _ramOption.ReadString(Models.Internal.RamOption.NameKey); + set => _ramOption[Models.Internal.RamOption.NameKey] = value; + } /// /// Determine whether the RamOption is default /// [JsonProperty("default", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("default")] - public bool? Default { get; set; } + public bool? Default + { + get => _ramOption.ReadBool(Models.Internal.RamOption.DefaultKey); + set => _ramOption[Models.Internal.RamOption.DefaultKey] = value; + } [JsonIgnore] public bool DefaultSpecified { get { return Default != null; } } @@ -31,17 +39,27 @@ namespace SabreTools.DatItems.Formats /// Determines the content of the RamOption /// [JsonProperty("content", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("content")] - public string Content { get; set; } + public string? Content + { + get => _ramOption.ReadString(Models.Internal.RamOption.ContentKey); + set => _ramOption[Models.Internal.RamOption.ContentKey] = value; + } + + /// + /// Internal RamOption model + /// + [JsonIgnore] + private Models.Internal.RamOption _ramOption = new(); #endregion #region Accessors /// - public override string GetName() => Name; + public override string? GetName() => Name; /// - public override void SetName(string name) => Name = name; + public override void SetName(string? name) => Name = name; #endregion @@ -68,13 +86,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - Name = this.Name, - Default = this.Default, - Content = this.Content, + _ramOption = this._ramOption?.Clone() as Models.Internal.RamOption ?? new Models.Internal.RamOption(), }; } @@ -83,19 +99,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { - // If we don't have a RamOption, return false - if (ItemType != other.ItemType) + // If we don't have a RamOption, return false + if (ItemType != other?.ItemType || other is not RamOption otherInternal) return false; - // Otherwise, treat it as a RamOption - RamOption newOther = other as RamOption; - - // If the BiosSet information matches - return (Name == newOther.Name - && Default == newOther.Default - && Content == newOther.Content); + // Compare the internal models + return _ramOption.EqualTo(otherInternal._ramOption); } #endregion diff --git a/SabreTools.DatItems/Formats/Release.cs b/SabreTools.DatItems/Formats/Release.cs index 80ccf893..fe47fee3 100644 --- a/SabreTools.DatItems/Formats/Release.cs +++ b/SabreTools.DatItems/Formats/Release.cs @@ -16,44 +16,70 @@ namespace SabreTools.DatItems.Formats /// Name of the item /// [JsonProperty("name"), XmlElement("name")] - public string Name { get; set; } + public string? Name + { + get => _release.ReadString(Models.Internal.Release.NameKey); + set => _release[Models.Internal.Release.NameKey] = value; + } /// /// Release region(s) /// [JsonProperty("region", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("region")] - public string Region { get; set; } + public string? Region + { + get => _release.ReadString(Models.Internal.Release.RegionKey); + set => _release[Models.Internal.Release.RegionKey] = value; + } /// /// Release language(s) /// [JsonProperty("language", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("language")] - public string Language { get; set; } + public string? Language + { + get => _release.ReadString(Models.Internal.Release.LanguageKey); + set => _release[Models.Internal.Release.LanguageKey] = value; + } /// /// Date of release /// [JsonProperty("date", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("date")] - public string Date { get; set; } + public string? Date + { + get => _release.ReadString(Models.Internal.Release.DateKey); + set => _release[Models.Internal.Release.DateKey] = value; + } /// /// Default release, if applicable /// [JsonProperty("default", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("default")] - public bool? Default { get; set; } + public bool? Default + { + get => _release.ReadBool(Models.Internal.Release.DefaultKey); + set => _release[Models.Internal.Release.DefaultKey] = value; + } [JsonIgnore] public bool DefaultSpecified { get { return Default != null; } } + /// + /// Internal Release model + /// + [JsonIgnore] + private Models.Internal.Release _release = new(); + #endregion #region Accessors /// - public override string GetName() => Name; + public override string? GetName() => Name; /// - public override void SetName(string name) => Name = name; + public override void SetName(string? name) => Name = name; #endregion @@ -84,15 +110,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - Name = this.Name, - Region = this.Region, - Language = this.Language, - Date = this.Date, - Default = this.Default, + _release = this._release?.Clone() as Models.Internal.Release ?? new Models.Internal.Release(), }; } @@ -101,21 +123,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { - // If we don't have a release return false - if (ItemType != other.ItemType) + // If we don't have a Release, return false + if (ItemType != other?.ItemType || other is not Release otherInternal) return false; - // Otherwise, treat it as a Release - Release newOther = other as Release; - - // If the archive information matches - return (Name == newOther.Name - && Region == newOther.Region - && Language == newOther.Language - && Date == newOther.Date - && Default == newOther.Default); + // Compare the internal models + return _release.EqualTo(otherInternal._release); } #endregion diff --git a/SabreTools.DatItems/Formats/ReleaseDetails.cs b/SabreTools.DatItems/Formats/ReleaseDetails.cs index 063e7b1b..94696135 100644 --- a/SabreTools.DatItems/Formats/ReleaseDetails.cs +++ b/SabreTools.DatItems/Formats/ReleaseDetails.cs @@ -18,31 +18,31 @@ namespace SabreTools.DatItems.Formats /// /// TODO: Is this required? [JsonProperty("id", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("id")] - public string Id { get; set; } + public string? Id { get; set; } /// /// Directory name value /// [JsonProperty("dirname", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("dirname")] - public string DirName { get; set; } + public string? DirName { get; set; } /// /// Rom info value /// [JsonProperty("rominfo", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("rominfo")] - public string RomInfo { get; set; } + public string? RomInfo { get; set; } /// /// Category value /// [JsonProperty("category", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("category")] - public string Category { get; set; } + public string? Category { get; set; } /// /// NFO name value /// [JsonProperty("nfoname", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("nfoname")] - public string NfoName { get; set; } + public string? NfoName { get; set; } /// /// NFO size value @@ -57,55 +57,55 @@ namespace SabreTools.DatItems.Formats /// NFO CRC value /// [JsonProperty("nfocrc", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("nfocrc")] - public string NfoCrc { get; set; } + public string? NfoCrc { get; set; } /// /// Archive name value /// [JsonProperty("archivename", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("archivename")] - public string ArchiveName { get; set; } + public string? ArchiveName { get; set; } /// /// Original format value /// [JsonProperty("originalformat", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("originalformat")] - public string OriginalFormat { get; set; } + public string? OriginalFormat { get; set; } /// /// Date value /// [JsonProperty("date", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("date")] - public string Date { get; set; } + public string? Date { get; set; } /// /// Grpup value /// [JsonProperty("group", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("group")] - public string Group { get; set; } + public string? Group { get; set; } /// /// Comment value /// [JsonProperty("comment", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("comment")] - public string Comment { get; set; } + public string? Comment { get; set; } /// /// Tool value /// [JsonProperty("tool", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("tool")] - public string Tool { get; set; } + public string? Tool { get; set; } /// /// Region value /// [JsonProperty("region", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("region")] - public string Region { get; set; } + public string? Region { get; set; } /// /// Origin value /// [JsonProperty("origin", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("origin")] - public string Origin { get; set; } + public string? Origin { get; set; } #endregion @@ -131,8 +131,8 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, Id = this.Id, @@ -158,17 +158,17 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { // If we don't have a Details, return false - if (ItemType != other.ItemType) + if (ItemType != other?.ItemType) return false; // Otherwise, treat it as a Details - ReleaseDetails newOther = other as ReleaseDetails; + ReleaseDetails? newOther = other as ReleaseDetails; // If the Details information matches - return (Id == newOther.Id + return (Id == newOther!.Id && DirName == newOther.DirName && RomInfo == newOther.RomInfo && Category == newOther.Category diff --git a/SabreTools.DatItems/Formats/Rom.cs b/SabreTools.DatItems/Formats/Rom.cs index 6c83ccd6..b34ecdbe 100644 --- a/SabreTools.DatItems/Formats/Rom.cs +++ b/SabreTools.DatItems/Formats/Rom.cs @@ -1,7 +1,4 @@ -using System; -using System.Linq; -using System.Text; -using System.Xml.Serialization; +using System.Xml.Serialization; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using SabreTools.Core; @@ -16,18 +13,6 @@ namespace SabreTools.DatItems.Formats [JsonObject("rom"), XmlRoot("rom")] public class Rom : DatItem { - #region Private instance variables - - private byte[] _crc; // 8 bytes - private byte[] _md5; // 16 bytes - private byte[] _sha1; // 20 bytes - private byte[] _sha256; // 32 bytes - private byte[] _sha384; // 48 bytes - private byte[] _sha512; // 64 bytes - private byte[] _spamsum; // variable bytes - - #endregion - #region Fields #region Common @@ -36,19 +21,31 @@ namespace SabreTools.DatItems.Formats /// Name of the item /// [JsonProperty("name"), XmlElement("name")] - public string Name { get; set; } + public string? Name + { + get => _rom.ReadString(Models.Internal.Rom.NameKey); + set => _rom[Models.Internal.Rom.NameKey] = value; + } /// /// What BIOS is required for this rom /// [JsonProperty("bios", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("bios")] - public string Bios { get; set; } + public string? Bios + { + get => _rom.ReadString(Models.Internal.Rom.BiosKey); + set => _rom[Models.Internal.Rom.BiosKey] = value; + } /// /// Byte size of the rom /// [JsonProperty("size", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("size")] - public long? Size { get; set; } = null; + public long? Size + { + get => _rom.ReadLong(Models.Internal.Rom.SizeKey); + set => _rom[Models.Internal.Rom.SizeKey] = value; + } [JsonIgnore] public bool SizeSpecified { get { return Size != null; } } @@ -57,102 +54,122 @@ namespace SabreTools.DatItems.Formats /// File CRC32 hash /// [JsonProperty("crc", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("crc")] - public string CRC + public string? CRC { - get { return _crc.IsNullOrEmpty() ? null : Utilities.ByteArrayToString(_crc); } - set { _crc = (value == "null" ? Constants.CRCZeroBytes : Utilities.StringToByteArray(CleanCRC32(value))); } + get => _rom.ReadString(Models.Internal.Rom.CRCKey); + set => _rom[Models.Internal.Rom.CRCKey] = TextHelper.NormalizeCRC32(value); } /// /// File MD5 hash /// [JsonProperty("md5", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("md5")] - public string MD5 + public string? MD5 { - get { return _md5.IsNullOrEmpty() ? null : Utilities.ByteArrayToString(_md5); } - set { _md5 = Utilities.StringToByteArray(CleanMD5(value)); } + get => _rom.ReadString(Models.Internal.Rom.MD5Key); + set => _rom[Models.Internal.Rom.MD5Key] = TextHelper.NormalizeMD5(value); } /// /// File SHA-1 hash /// [JsonProperty("sha1", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("sha1")] - public string SHA1 + public string? SHA1 { - get { return _sha1.IsNullOrEmpty() ? null : Utilities.ByteArrayToString(_sha1); } - set { _sha1 = Utilities.StringToByteArray(CleanSHA1(value)); } + get => _rom.ReadString(Models.Internal.Rom.SHA1Key); + set => _rom[Models.Internal.Rom.SHA1Key] = TextHelper.NormalizeSHA1(value); } /// /// File SHA-256 hash /// [JsonProperty("sha256", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("sha256")] - public string SHA256 + public string? SHA256 { - get { return _sha256.IsNullOrEmpty() ? null : Utilities.ByteArrayToString(_sha256); } - set { _sha256 = Utilities.StringToByteArray(CleanSHA256(value)); } + get => _rom.ReadString(Models.Internal.Rom.SHA256Key); + set => _rom[Models.Internal.Rom.SHA256Key] = TextHelper.NormalizeSHA256(value); } /// /// File SHA-384 hash /// [JsonProperty("sha384", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("sha384")] - public string SHA384 + public string? SHA384 { - get { return _sha384.IsNullOrEmpty() ? null : Utilities.ByteArrayToString(_sha384); } - set { _sha384 = Utilities.StringToByteArray(CleanSHA384(value)); } + get => _rom.ReadString(Models.Internal.Rom.SHA384Key); + set => _rom[Models.Internal.Rom.SHA384Key] = TextHelper.NormalizeSHA384(value); } /// /// File SHA-512 hash /// [JsonProperty("sha512", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("sha512")] - public string SHA512 + public string? SHA512 { - get { return _sha512.IsNullOrEmpty() ? null : Utilities.ByteArrayToString(_sha512); } - set { _sha512 = Utilities.StringToByteArray(CleanSHA512(value)); } + get => _rom.ReadString(Models.Internal.Rom.SHA512Key); + set => _rom[Models.Internal.Rom.SHA512Key] = TextHelper.NormalizeSHA512(value); } /// /// File SpamSum fuzzy hash /// [JsonProperty("spamsum", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("spamsum")] - public string SpamSum + public string? SpamSum { - get { return _spamsum.IsNullOrEmpty() ? null : Encoding.UTF8.GetString(_spamsum); } - set { _spamsum = Encoding.UTF8.GetBytes(value ?? string.Empty); } + get => _rom.ReadString(Models.Internal.Rom.SpamSumKey); + set => _rom[Models.Internal.Rom.SpamSumKey] = value; } /// /// Rom name to merge from parent /// [JsonProperty("merge", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("merge")] - public string MergeTag { get; set; } + public string? MergeTag + { + get => _rom.ReadString(Models.Internal.Rom.MergeKey); + set => _rom[Models.Internal.Rom.MergeKey] = value; + } /// /// Rom region /// [JsonProperty("region", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("biregionos")] - public string Region { get; set; } + public string? Region + { + get => _rom.ReadString(Models.Internal.Rom.RegionKey); + set => _rom[Models.Internal.Rom.RegionKey] = value; + } /// /// Data offset within rom /// [JsonProperty("offset", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("offset")] - public string Offset { get; set; } + public string? Offset + { + get => _rom.ReadString(Models.Internal.Rom.OffsetKey); + set => _rom[Models.Internal.Rom.OffsetKey] = value; + } /// /// File created date /// [JsonProperty("date", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("date")] - public string Date { get; set; } + public string? Date + { + get => _rom.ReadString(Models.Internal.Rom.DateKey); + set => _rom[Models.Internal.Rom.DateKey] = value; + } /// /// Rom dump status /// [JsonProperty("status", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("status")] [JsonConverter(typeof(StringEnumConverter))] - public ItemStatus ItemStatus { get; set; } + public ItemStatus ItemStatus + { + get => _rom.ReadString(Models.Internal.Rom.StatusKey).AsItemStatus(); + set => _rom[Models.Internal.Rom.StatusKey] = value.FromItemStatus(yesno: false); + } [JsonIgnore] public bool ItemStatusSpecified { get { return ItemStatus != ItemStatus.NULL && ItemStatus != ItemStatus.None; } } @@ -161,7 +178,11 @@ namespace SabreTools.DatItems.Formats /// Determine if the rom is optional in the set /// [JsonProperty("optional", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("optional")] - public bool? Optional { get; set; } = null; + public bool? Optional + { + get => _rom.ReadBool(Models.Internal.Rom.OptionalKey); + set => _rom[Models.Internal.Rom.OptionalKey] = value; + } [JsonIgnore] public bool OptionalSpecified { get { return Optional != null; } } @@ -170,7 +191,11 @@ namespace SabreTools.DatItems.Formats /// Determine if the CRC32 hash is inverted /// [JsonProperty("inverted", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("inverted")] - public bool? Inverted { get; set; } = null; + public bool? Inverted + { + get => _rom.ReadBool(Models.Internal.Rom.InvertedKey); + set => _rom[Models.Internal.Rom.InvertedKey] = value; + } [JsonIgnore] public bool InvertedSpecified { get { return Inverted != null; } } @@ -183,19 +208,31 @@ namespace SabreTools.DatItems.Formats /// Source of file /// [JsonProperty("ado_source", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("ado_source")] - public string ArchiveDotOrgSource { get; set; } + public string? ArchiveDotOrgSource + { + get => _rom.ReadString(Models.Internal.Rom.SourceKey); + set => _rom[Models.Internal.Rom.SourceKey] = value; + } /// /// Archive.org recognized file format /// [JsonProperty("ado_format", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("ado_format")] - public string ArchiveDotOrgFormat { get; set; } + public string? ArchiveDotOrgFormat + { + get => _rom.ReadString(Models.Internal.Rom.FormatKey); + set => _rom[Models.Internal.Rom.FormatKey] = value; + } /// /// Original filename /// [JsonProperty("original_filename", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("original_filename")] - public string OriginalFilename { get; set; } + public string? OriginalFilename + { + get => _rom.ReadString(Models.Internal.Rom.OriginalKey); + set => _rom[Models.Internal.Rom.OriginalKey] = value; + } /// /// Image rotation @@ -204,13 +241,21 @@ namespace SabreTools.DatItems.Formats /// TODO: This might be Int32? /// [JsonProperty("rotation", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("rotation")] - public string Rotation { get; set; } + public string? Rotation + { + get => _rom.ReadString(Models.Internal.Rom.RotationKey); + set => _rom[Models.Internal.Rom.RotationKey] = value; + } /// /// Summation value? /// [JsonProperty("summation", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("summation")] - public string Summation { get; set; } + public string? Summation + { + get => _rom.ReadString(Models.Internal.Rom.SummationKey); + set => _rom[Models.Internal.Rom.SummationKey] = value; + } #endregion @@ -220,13 +265,21 @@ namespace SabreTools.DatItems.Formats /// Alternate name for the item /// [JsonProperty("alt_romname", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("alt_romname")] - public string AltName { get; set; } + public string? AltName + { + get => _rom.ReadString(Models.Internal.Rom.AltRomnameKey); + set => _rom[Models.Internal.Rom.AltRomnameKey] = value; + } /// /// Alternate title for the item /// [JsonProperty("alt_title", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("alt_title")] - public string AltTitle { get; set; } + public string? AltTitle + { + get => _rom.ReadString(Models.Internal.Rom.AltTitleKey); + set => _rom[Models.Internal.Rom.AltTitleKey] = value; + } #endregion @@ -236,7 +289,11 @@ namespace SabreTools.DatItems.Formats /// Alternate title for the item /// [JsonProperty("mia", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("mia")] - public bool? MIA { get; set; } = null; + public bool? MIA + { + get => _rom.ReadBool(Models.Internal.Rom.MIAKey); + set => _rom[Models.Internal.Rom.MIAKey] = value; + } [JsonIgnore] public bool MIASpecified { get { return MIA != null; } } @@ -248,8 +305,9 @@ namespace SabreTools.DatItems.Formats /// /// OpenMSX sub item type /// + /// This is inverted from the internal model [JsonProperty("original", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("original")] - public Original Original { get; set; } = null; + public Original? Original { get; set; } [JsonIgnore] public bool OriginalSpecified { get { return Original != null && Original != default; } } @@ -259,7 +317,11 @@ namespace SabreTools.DatItems.Formats /// [JsonProperty("openmsx_subtype", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("openmsx_subtype")] [JsonConverter(typeof(StringEnumConverter))] - public OpenMSXSubType OpenMSXSubType { get; set; } + public OpenMSXSubType OpenMSXSubType + { + get => _rom.ReadString(Models.Internal.Rom.OpenMSXMediaType).AsOpenMSXSubType(); + set => _rom[Models.Internal.Rom.OpenMSXMediaType] = value.FromOpenMSXSubType(); + } [JsonIgnore] public bool OpenMSXSubTypeSpecified { get { return OpenMSXSubType != OpenMSXSubType.NULL; } } @@ -269,19 +331,28 @@ namespace SabreTools.DatItems.Formats /// /// Not related to the subtype above [JsonProperty("openmsx_type", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("openmsx_type")] - public string OpenMSXType { get; set; } + public string? OpenMSXType + { + get => _rom.ReadString(Models.Internal.Rom.OpenMSXType); + set => _rom[Models.Internal.Rom.OpenMSXType] = value; + } /// /// Item remark (like a comment) /// [JsonProperty("remark", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("remark")] - public string Remark { get; set; } + public string? Remark + { + get => _rom.ReadString(Models.Internal.Rom.RemarkKey); + set => _rom[Models.Internal.Rom.RemarkKey] = value; + } /// /// Boot state /// + /// TODO: Investigate where this value came from? [JsonProperty("boot", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("boot")] - public string Boot { get; set; } + public string? Boot { get; set; } #endregion @@ -290,8 +361,9 @@ namespace SabreTools.DatItems.Formats /// /// Data area information /// + /// This is inverted from the internal model [JsonProperty("dataarea", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("dataarea")] - public DataArea DataArea { get; set; } = null; + public DataArea? DataArea { get; set; } = null; [JsonIgnore] public bool DataAreaSpecified @@ -311,7 +383,11 @@ namespace SabreTools.DatItems.Formats /// [JsonProperty("loadflag", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("loadflag")] [JsonConverter(typeof(StringEnumConverter))] - public LoadFlag LoadFlag { get; set; } + public LoadFlag LoadFlag + { + get => _rom.ReadString(Models.Internal.Rom.LoadFlagKey).AsLoadFlag(); + set => _rom[Models.Internal.Rom.LoadFlagKey] = value.FromLoadFlag(); + } [JsonIgnore] public bool LoadFlagSpecified { get { return LoadFlag != LoadFlag.NULL; } } @@ -319,8 +395,9 @@ namespace SabreTools.DatItems.Formats /// /// Original hardware part associated with the item /// + /// This is inverted from the internal model [JsonProperty("part", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("part")] - public Part Part { get; set; } = null; + public Part? Part { get; set; } = null; [JsonIgnore] public bool PartSpecified @@ -337,19 +414,29 @@ namespace SabreTools.DatItems.Formats /// SoftwareList value associated with the item /// [JsonProperty("value", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("value")] - public string Value { get; set; } + public string? Value + { + get => _rom.ReadString(Models.Internal.Rom.ValueKey); + set => _rom[Models.Internal.Rom.ValueKey] = value; + } #endregion + /// + /// Internal Rom model + /// + [JsonIgnore] + private Models.Internal.Rom _rom = new(); + #endregion // Fields #region Accessors /// - public override string GetName() => Name; + public override string? GetName() => Name; /// - public override void SetName(string name) => Name = name; + public override void SetName(string? name) => Name = name; #endregion @@ -394,13 +481,13 @@ namespace SabreTools.DatItems.Formats { Name = baseFile.Filename; Size = baseFile.Size; - _crc = baseFile.CRC; - _md5 = baseFile.MD5; - _sha1 = baseFile.SHA1; - _sha256 = baseFile.SHA256; - _sha384 = baseFile.SHA384; - _sha512 = baseFile.SHA512; - _spamsum = baseFile.SpamSum; + CRC = Utilities.ByteArrayToString(baseFile.CRC); + MD5 = Utilities.ByteArrayToString(baseFile.MD5); + SHA1 = Utilities.ByteArrayToString(baseFile.SHA1); + SHA256 = Utilities.ByteArrayToString(baseFile.SHA256); + SHA384 = Utilities.ByteArrayToString(baseFile.SHA384); + SHA512 = Utilities.ByteArrayToString(baseFile.SHA512); + SpamSum = Utilities.ByteArrayToString(baseFile.SpamSum); ItemType = ItemType.Rom; DupeType = 0x00; @@ -421,42 +508,14 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - Bios = this.Bios, - Size = this.Size, - _crc = this._crc, - _md5 = this._md5, - _sha1 = this._sha1, - _sha256 = this._sha256, - _sha384 = this._sha384, - _sha512 = this._sha512, - _spamsum = this._spamsum, - MergeTag = this.MergeTag, - Region = this.Region, - Offset = this.Offset, - Date = this.Date, - ItemStatus = this.ItemStatus, - Optional = this.Optional, - Inverted = this.Inverted, - - AltName = this.AltName, - AltTitle = this.AltTitle, - - MIA = this.MIA, - - Original = this.Original, - OpenMSXSubType = this.OpenMSXSubType, - OpenMSXType = this.OpenMSXType, - Remark = this.Remark, - Boot = this.Boot, - + _rom = this._rom?.Clone() as Models.Internal.Rom ?? new Models.Internal.Rom(), + DataArea = this.DataArea, - LoadFlag = this.LoadFlag, Part = this.Part, - Value = this.Value, }; } @@ -471,13 +530,13 @@ namespace SabreTools.DatItems.Formats Parent = this.Machine?.Name, Date = this.Date, Size = this.Size, - CRC = this._crc, - MD5 = this._md5, - SHA1 = this._sha1, - SHA256 = this._sha256, - SHA384 = this._sha384, - SHA512 = this._sha512, - SpamSum = this._spamsum, + CRC = Utilities.StringToByteArray(this.CRC), + MD5 = Utilities.StringToByteArray(this.MD5), + SHA1 = Utilities.StringToByteArray(this.SHA1), + SHA256 = Utilities.StringToByteArray(this.SHA256), + SHA384 = Utilities.StringToByteArray(this.SHA384), + SHA512 = Utilities.StringToByteArray(this.SHA512), + SpamSum = Utilities.StringToByteArray(this.SpamSum), }; } @@ -486,38 +545,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { - bool dupefound = false; + // If we don't have a Rom, return false + if (ItemType != other?.ItemType || other is not Rom otherInternal) + return false; - // If we don't have a rom, return false - if (ItemType != other.ItemType) - return dupefound; - - // Otherwise, treat it as a Rom - Rom newOther = other as Rom; - - // If all hashes are empty but they're both nodump and the names match, then they're dupes - if ((ItemStatus == ItemStatus.Nodump && newOther.ItemStatus == ItemStatus.Nodump) - && Name == newOther.Name - && !HasHashes() && !newOther.HasHashes()) - { - dupefound = true; - } - - // If we have a file that has no known size, rely on the hashes only - else if (Size == null && HashMatch(newOther)) - { - dupefound = true; - } - - // Otherwise if we get a partial match - else if (Size == newOther.Size && HashMatch(newOther)) - { - dupefound = true; - } - - return dupefound; + // Compare the internal models + return _rom.EqualTo(otherInternal._rom); } /// @@ -529,26 +564,26 @@ namespace SabreTools.DatItems.Formats if (Size == null && other.Size != null) Size = other.Size; - if (_crc.IsNullOrEmpty() && !other._crc.IsNullOrEmpty()) - _crc = other._crc; + if (string.IsNullOrWhiteSpace(CRC) && !string.IsNullOrWhiteSpace(other.CRC)) + CRC = other.CRC; - if (_md5.IsNullOrEmpty() && !other._md5.IsNullOrEmpty()) - _md5 = other._md5; + if (string.IsNullOrWhiteSpace(MD5) && !string.IsNullOrWhiteSpace(other.MD5)) + MD5 = other.MD5; - if (_sha1.IsNullOrEmpty() && !other._sha1.IsNullOrEmpty()) - _sha1 = other._sha1; + if (string.IsNullOrWhiteSpace(SHA1) && !string.IsNullOrWhiteSpace(other.SHA1)) + SHA1 = other.SHA1; - if (_sha256.IsNullOrEmpty() && !other._sha256.IsNullOrEmpty()) - _sha256 = other._sha256; + if (string.IsNullOrWhiteSpace(SHA256) && !string.IsNullOrWhiteSpace(other.SHA256)) + SHA256 = other.SHA256; - if (_sha384.IsNullOrEmpty() && !other._sha384.IsNullOrEmpty()) - _sha384 = other._sha384; + if (string.IsNullOrWhiteSpace(SHA384) && !string.IsNullOrWhiteSpace(other.SHA384)) + SHA384 = other.SHA384; - if (_sha512.IsNullOrEmpty() && !other._sha512.IsNullOrEmpty()) - _sha512 = other._sha512; + if (string.IsNullOrWhiteSpace(SHA512) && !string.IsNullOrWhiteSpace(other.SHA512)) + SHA512 = other.SHA512; - if (_spamsum.IsNullOrEmpty() && !other._spamsum.IsNullOrEmpty()) - _spamsum = other._spamsum; + if (string.IsNullOrWhiteSpace(SpamSum) && !string.IsNullOrWhiteSpace(other.SpamSum)) + SpamSum = other.SpamSum; } /// @@ -557,19 +592,19 @@ namespace SabreTools.DatItems.Formats /// String representing the suffix public string GetDuplicateSuffix() { - if (!_crc.IsNullOrEmpty()) + if (!string.IsNullOrWhiteSpace(CRC)) return $"_{CRC}"; - else if (!_md5.IsNullOrEmpty()) + else if (!string.IsNullOrWhiteSpace(MD5)) return $"_{MD5}"; - else if (!_sha1.IsNullOrEmpty()) + else if (!string.IsNullOrWhiteSpace(SHA1)) return $"_{SHA1}"; - else if (!_sha256.IsNullOrEmpty()) + else if (!string.IsNullOrWhiteSpace(SHA256)) return $"_{SHA256}"; - else if (!_sha384.IsNullOrEmpty()) + else if (!string.IsNullOrWhiteSpace(SHA384)) return $"_{SHA384}"; - else if (!_sha512.IsNullOrEmpty()) + else if (!string.IsNullOrWhiteSpace(SHA512)) return $"_{SHA512}"; - else if (!_spamsum.IsNullOrEmpty()) + else if (!string.IsNullOrWhiteSpace(SpamSum)) return $"_{SpamSum}"; else return "_1"; @@ -579,72 +614,13 @@ namespace SabreTools.DatItems.Formats /// Returns if the Rom contains any hashes /// /// True if any hash exists, false otherwise - public bool HasHashes() - { - return !_crc.IsNullOrEmpty() - || !_md5.IsNullOrEmpty() - || !_sha1.IsNullOrEmpty() - || !_sha256.IsNullOrEmpty() - || !_sha384.IsNullOrEmpty() - || !_sha512.IsNullOrEmpty() - || !_spamsum.IsNullOrEmpty(); - } + public bool HasHashes() => _rom.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() - { - return (_crc != null && _crc.SequenceEqual(Constants.CRCZeroBytes)) - || (_md5 != null && _md5.SequenceEqual(Constants.MD5ZeroBytes)) - || (_sha1 != null && _sha1.SequenceEqual(Constants.SHA1ZeroBytes)) - || (_sha256 != null && _sha256.SequenceEqual(Constants.SHA256ZeroBytes)) - || (_sha384 != null && _sha384.SequenceEqual(Constants.SHA384ZeroBytes)) - || (_sha512 != null && _sha512.SequenceEqual(Constants.SHA512ZeroBytes)) - || (_spamsum != null && _spamsum.SequenceEqual(Constants.SpamSumZeroBytes)); - } - - /// - /// Returns if there are no, non-empty hashes in common with another Rom - /// - /// Rom to compare against - /// True if at least one hash is not mutually exclusive, false otherwise - private bool HasCommonHash(Rom other) - { - return !(_crc.IsNullOrEmpty() ^ other._crc.IsNullOrEmpty()) - || !(_md5.IsNullOrEmpty() ^ other._md5.IsNullOrEmpty()) - || !(_sha1.IsNullOrEmpty() ^ other._sha1.IsNullOrEmpty()) - || !(_sha256.IsNullOrEmpty() ^ other._sha256.IsNullOrEmpty()) - || !(_sha384.IsNullOrEmpty() ^ other._sha384.IsNullOrEmpty()) - || !(_sha512.IsNullOrEmpty() ^ other._sha512.IsNullOrEmpty()) - || !(_spamsum.IsNullOrEmpty() ^ other._spamsum.IsNullOrEmpty()); - } - - /// - /// Returns if any hashes are common with another Rom - /// - /// Rom to compare against - /// True if any hashes are in common, false otherwise - private bool HashMatch(Rom other) - { - // If either have no hashes, we return false, otherwise this would be a false positive - if (!HasHashes() || !other.HasHashes()) - return false; - - // If neither have hashes in common, we return false, otherwise this would be a false positive - if (!HasCommonHash(other)) - return false; - - // Return if all hashes match according to merge rules - return ConditionalHashEquals(_crc, other._crc) - && ConditionalHashEquals(_md5, other._md5) - && ConditionalHashEquals(_sha1, other._sha1) - && ConditionalHashEquals(_sha256, other._sha256) - && ConditionalHashEquals(_sha384, other._sha384) - && ConditionalHashEquals(_sha512, other._sha512) - && ConditionalHashEquals(_spamsum, other._spamsum); - } + public bool HasZeroHash() => _rom.HasZeroHash(); #endregion @@ -654,7 +630,7 @@ namespace SabreTools.DatItems.Formats public override string GetKey(ItemKey bucketedBy, bool lower = true, bool norename = true) { // Set the output key as the default blank string - string key; + string? key; // Now determine what the key should be based on the bucketedBy value switch (bucketedBy) diff --git a/SabreTools.DatItems/Formats/Sample.cs b/SabreTools.DatItems/Formats/Sample.cs index c127edde..cc0b8dbc 100644 --- a/SabreTools.DatItems/Formats/Sample.cs +++ b/SabreTools.DatItems/Formats/Sample.cs @@ -16,17 +16,27 @@ namespace SabreTools.DatItems.Formats /// Name of the item /// [JsonProperty("name"), XmlElement("name")] - public string Name { get; set; } + public string? Name + { + get => _sample.ReadString(Models.Internal.Sample.NameKey); + set => _sample[Models.Internal.Sample.NameKey] = value; + } + + /// + /// Internal Sample model + /// + [JsonIgnore] + private Models.Internal.Sample _sample = new(); #endregion #region Accessors /// - public override string GetName() => Name; + public override string? GetName() => Name; /// - public override void SetName(string name) => Name = name; + public override void SetName(string? name) => Name = name; #endregion @@ -53,11 +63,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - Name = this.Name, + _sample = this._sample?.Clone() as Models.Internal.Sample ?? new Models.Internal.Sample(), }; } @@ -66,17 +76,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { - // If we don't have a sample, return false - if (ItemType != other.ItemType) + // If we don't have a Sample, return false + if (ItemType != other?.ItemType || other is not Sample otherInternal) return false; - // Otherwise, treat it as a Sample - Sample newOther = other as Sample; - - // If the archive information matches - return (Name == newOther.Name); + // Compare the internal models + return _sample.EqualTo(otherInternal._sample); } #endregion diff --git a/SabreTools.DatItems/Formats/Serials.cs b/SabreTools.DatItems/Formats/Serials.cs index 34c76606..697231a8 100644 --- a/SabreTools.DatItems/Formats/Serials.cs +++ b/SabreTools.DatItems/Formats/Serials.cs @@ -17,85 +17,85 @@ namespace SabreTools.DatItems.Formats /// Digital serial 1 value /// [JsonProperty("digital_serial1", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("digital_serial1")] - public string DigitalSerial1 { get; set; } + public string? DigitalSerial1 { get; set; } /// /// Digital serial 2 value /// [JsonProperty("digital_serial2", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("digital_serial2")] - public string DigitalSerial2 { get; set; } + public string? DigitalSerial2 { get; set; } /// /// Media serial 1 value /// [JsonProperty("media_serial1", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("media_serial1")] - public string MediaSerial1 { get; set; } + public string? MediaSerial1 { get; set; } /// /// Media serial 2 value /// [JsonProperty("media_serial2", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("media_serial2")] - public string MediaSerial2 { get; set; } + public string? MediaSerial2 { get; set; } /// /// Media serial 3 value /// [JsonProperty("media_serial3", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("media_serial3")] - public string MediaSerial3 { get; set; } + public string? MediaSerial3 { get; set; } /// /// PCB serial value /// [JsonProperty("pcb_serial", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("pcb_serial")] - public string PcbSerial { get; set; } + public string? PcbSerial { get; set; } /// /// Rom chip serial 1 value /// [JsonProperty("romchip_serial1", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("romchip_serial1")] - public string RomChipSerial1 { get; set; } + public string? RomChipSerial1 { get; set; } /// /// Rom chip serial 2 value /// [JsonProperty("romchip_serial2", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("romchip_serial2")] - public string RomChipSerial2 { get; set; } + public string? RomChipSerial2 { get; set; } /// /// Lockout serial value /// [JsonProperty("lockout_serial", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("lockout_serial")] - public string LockoutSerial { get; set; } + public string? LockoutSerial { get; set; } /// /// Save chip serial value /// [JsonProperty("savechip_serial", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("savechip_serial")] - public string SaveChipSerial { get; set; } + public string? SaveChipSerial { get; set; } /// /// Chip serial value /// [JsonProperty("chip_serial", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("chip_serial")] - public string ChipSerial { get; set; } + public string? ChipSerial { get; set; } /// /// Box serial value /// [JsonProperty("box_serial", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("box_serial")] - public string BoxSerial { get; set; } + public string? BoxSerial { get; set; } /// /// Media stamp value /// [JsonProperty("mediastamp", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("mediastamp")] - public string MediaStamp { get; set; } + public string? MediaStamp { get; set; } /// /// Box barcode value /// [JsonProperty("box_barcode", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("box_barcode")] - public string BoxBarcode { get; set; } + public string? BoxBarcode { get; set; } #endregion @@ -121,8 +121,8 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, DigitalSerial1 = this.DigitalSerial1, @@ -147,17 +147,17 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { // If we don't have a Serials, return false - if (ItemType != other.ItemType) + if (ItemType != other?.ItemType) return false; // Otherwise, treat it as a Serials - Serials newOther = other as Serials; + Serials? newOther = other as Serials; // If the Serials information matches - return (DigitalSerial1 == newOther.DigitalSerial1 + return (DigitalSerial1 == newOther!.DigitalSerial1 && DigitalSerial2 == newOther.DigitalSerial2 && MediaSerial1 == newOther.MediaSerial1 && MediaSerial2 == newOther.MediaSerial2 diff --git a/SabreTools.DatItems/Formats/Setting.cs b/SabreTools.DatItems/Formats/Setting.cs deleted file mode 100644 index b577a74f..00000000 --- a/SabreTools.DatItems/Formats/Setting.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System.Collections.Generic; -using System.Xml.Serialization; -using Newtonsoft.Json; -using SabreTools.Core; - -namespace SabreTools.DatItems.Formats -{ - /// - /// Represents one ListXML confsetting or dipvalue - /// - [JsonObject("setting"), XmlRoot("setting")] - public class Setting : DatItem - { - #region Fields - - /// - /// Setting name - /// - [JsonProperty("name"), XmlElement("name")] - public string Name { get; set; } - - /// - /// Setting value - /// - [JsonProperty("value", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("value")] - public string Value { get; set; } - - /// - /// Determines if the setting is default or not - /// - [JsonProperty("default", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("default")] - public bool? Default { get; set; } - - [JsonIgnore] - public bool DefaultSpecified { get { return Default != null; } } - - /// - /// List of conditions on the setting - /// - [JsonProperty("conditions", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("conditions")] - public List Conditions { get; set; } - - [JsonIgnore] - public bool ConditionsSpecified { get { return Conditions != null && Conditions.Count > 0; } } - - #endregion - - #region Accessors - - /// - public override string GetName() => Name; - - /// - public override void SetName(string name) => Name = name; - - #endregion - - #region Constructors - - /// - /// Create a default, empty Setting object - /// - public Setting() - { - Name = string.Empty; - ItemType = ItemType.Setting; - } - - #endregion - - #region Cloning Methods - - /// - public override object Clone() - { - return new Setting() - { - ItemType = this.ItemType, - DupeType = this.DupeType, - - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, - Remove = this.Remove, - - Name = this.Name, - Value = this.Value, - Default = this.Default, - Conditions = this.Conditions, - }; - } - - #endregion - - #region Comparision Methods - - /// - public override bool Equals(DatItem other) - { - // If we don't have a Setting, return false - if (ItemType != other.ItemType) - return false; - - // Otherwise, treat it as a Setting - Setting newOther = other as Setting; - - // If the Setting information matches - bool match = (Name == newOther.Name - && Value == newOther.Value - && Default == newOther.Default); - if (!match) - return match; - - // If the conditions match - if (ConditionsSpecified) - { - foreach (Condition condition in Conditions) - { - match &= newOther.Conditions.Contains(condition); - } - } - - return match; - } - - #endregion - } -} diff --git a/SabreTools.DatItems/Formats/SharedFeature.cs b/SabreTools.DatItems/Formats/SharedFeature.cs index fea3f89a..f3576626 100644 --- a/SabreTools.DatItems/Formats/SharedFeature.cs +++ b/SabreTools.DatItems/Formats/SharedFeature.cs @@ -16,23 +16,37 @@ namespace SabreTools.DatItems.Formats /// Name of the item /// [JsonProperty("name"), XmlElement("name")] - public string Name { get; set; } + public string? Name + { + get => _sharedFeat.ReadString(Models.Internal.SharedFeat.NameKey); + set => _sharedFeat[Models.Internal.SharedFeat.NameKey] = value; + } /// /// SharedFeature value /// [JsonProperty("value"), XmlElement("value")] - public string Value { get; set; } + public string? Value + { + get => _sharedFeat.ReadString(Models.Internal.SharedFeat.ValueKey); + set => _sharedFeat[Models.Internal.SharedFeat.ValueKey] = value; + } + + /// + /// Internal SharedFeat model + /// + [JsonIgnore] + private Models.Internal.SharedFeat _sharedFeat = new(); #endregion #region Accessors /// - public override string GetName() => Name; + public override string? GetName() => Name; /// - public override void SetName(string name) => Name = name; + public override void SetName(string? name) => Name = name; #endregion @@ -59,12 +73,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - Name = this.Name, - Value = this.Value, + _sharedFeat = this._sharedFeat?.Clone() as Models.Internal.SharedFeat ?? new Models.Internal.SharedFeat(), }; } @@ -73,18 +86,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { - // If we don't have a sample, return false - if (ItemType != other.ItemType) + // If we don't have a SharedFeature, return false + if (ItemType != other?.ItemType || other is not SharedFeature otherInternal) return false; - // Otherwise, treat it as a SharedFeature - SharedFeature newOther = other as SharedFeature; - - // If the archive information matches - return (Name == newOther.Name - && Value == newOther.Value); + // Compare the internal models + return _sharedFeat.EqualTo(otherInternal._sharedFeat); } #endregion diff --git a/SabreTools.DatItems/Formats/Slot.cs b/SabreTools.DatItems/Formats/Slot.cs index a1d89df6..b63c43ce 100644 --- a/SabreTools.DatItems/Formats/Slot.cs +++ b/SabreTools.DatItems/Formats/Slot.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using System.Xml.Serialization; using Newtonsoft.Json; using SabreTools.Core; @@ -17,26 +18,40 @@ namespace SabreTools.DatItems.Formats /// Name of the item /// [JsonProperty("name"), XmlElement("name")] - public string Name { get; set; } + public string? Name + { + get => _slot.ReadString(Models.Internal.Slot.NameKey); + set => _slot[Models.Internal.Slot.NameKey] = value; + } /// /// Slot options associated with the slot /// [JsonProperty("slotoptions", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("slotoptions")] - public List SlotOptions { get; set; } + public List? SlotOptions + { + get => _slot.Read(Models.Internal.Slot.SlotOptionKey)?.ToList(); + set => _slot[Models.Internal.Slot.SlotOptionKey] = value?.ToArray(); + } [JsonIgnore] public bool SlotOptionsSpecified { get { return SlotOptions != null && SlotOptions.Count > 0; } } + /// + /// Internal Slot model + /// + [JsonIgnore] + private Models.Internal.Slot _slot = new(); + #endregion #region Accessors /// - public override string GetName() => Name; + public override string? GetName() => Name; /// - public override void SetName(string name) => Name = name; + public override void SetName(string? name) => Name = name; #endregion @@ -63,12 +78,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - Name = this.Name, - SlotOptions = this.SlotOptions, + _slot = this._slot?.Clone() as Models.Internal.Slot ?? new Models.Internal.Slot(), }; } @@ -77,30 +91,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { // If we don't have a Slot, return false - if (ItemType != other.ItemType) + if (ItemType != other?.ItemType || other is not Slot otherInternal) return false; - // Otherwise, treat it as a Slot - Slot newOther = other as Slot; - - // If the Slot information matches - bool match = (Name == newOther.Name); - if (!match) - return match; - - // If the slot options match - if (SlotOptionsSpecified) - { - foreach (SlotOption slotOption in SlotOptions) - { - match &= newOther.SlotOptions.Contains(slotOption); - } - } - - return match; + // Compare the internal models + return _slot.EqualTo(otherInternal._slot); } #endregion diff --git a/SabreTools.DatItems/Formats/SlotOption.cs b/SabreTools.DatItems/Formats/SlotOption.cs index e6796719..53c73dab 100644 --- a/SabreTools.DatItems/Formats/SlotOption.cs +++ b/SabreTools.DatItems/Formats/SlotOption.cs @@ -16,32 +16,50 @@ namespace SabreTools.DatItems.Formats /// Slot option name /// [JsonProperty("name"), XmlElement("name")] - public string Name { get; set; } + public string? Name + { + get => _slotOption.ReadString(Models.Internal.SlotOption.NameKey); + set => _slotOption[Models.Internal.SlotOption.NameKey] = value; + } /// /// Referenced device name /// [JsonProperty("devname"), XmlElement("devname")] - public string DeviceName { get; set; } + public string? DeviceName + { + get => _slotOption.ReadString(Models.Internal.SlotOption.DevNameKey); + set => _slotOption[Models.Internal.SlotOption.DevNameKey] = value; + } /// /// Determines if this slot option is default or not /// [JsonProperty("default", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("default")] - public bool? Default { get; set; } + public bool? Default + { + get => _slotOption.ReadBool(Models.Internal.SlotOption.DefaultKey); + set => _slotOption[Models.Internal.SlotOption.DefaultKey] = value; + } [JsonIgnore] public bool DefaultSpecified { get { return Default != null; } } + /// + /// Internal SlotOption model + /// + [JsonIgnore] + private Models.Internal.SlotOption _slotOption = new(); + #endregion #region Accessors /// - public override string GetName() => Name; + public override string? GetName() => Name; /// - public override void SetName(string name) => Name = name; + public override void SetName(string? name) => Name = name; #endregion @@ -68,13 +86,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - Name = this.Name, - DeviceName = this.DeviceName, - Default = this.Default, + _slotOption = this._slotOption?.Clone() as Models.Internal.SlotOption ?? new Models.Internal.SlotOption(), }; } @@ -83,19 +99,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { - // If we don't have a SlotOption, return false - if (ItemType != other.ItemType) + // If we don't have a Adjuster, return false + if (ItemType != other?.ItemType || other is not SlotOption otherInternal) return false; - // Otherwise, treat it as a SlotOption - SlotOption newOther = other as SlotOption; - - // If the SlotOption information matches - return (Name == newOther.Name - && DeviceName == newOther.DeviceName - && Default == newOther.Default); + // Compare the internal models + return _slotOption.EqualTo(otherInternal._slotOption); } #endregion diff --git a/SabreTools.DatItems/Formats/SoftwareList.cs b/SabreTools.DatItems/Formats/SoftwareList.cs index e3bbeb44..e2d18401 100644 --- a/SabreTools.DatItems/Formats/SoftwareList.cs +++ b/SabreTools.DatItems/Formats/SoftwareList.cs @@ -1,8 +1,8 @@ using System.Xml.Serialization; - -using SabreTools.Core; using Newtonsoft.Json; using Newtonsoft.Json.Converters; +using SabreTools.Core; +using SabreTools.Core.Tools; namespace SabreTools.DatItems.Formats { @@ -22,14 +22,22 @@ namespace SabreTools.DatItems.Formats /// [JsonProperty("tag", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("tag")] - public string Tag { get; set; } + public string? Tag + { + get => _softwareList.ReadString(Models.Internal.SoftwareList.TagKey); + set => _softwareList[Models.Internal.SoftwareList.TagKey] = value; + } /// /// Name of the item /// [JsonProperty("name")] [XmlElement("name")] - public string Name { get; set; } + public string? Name + { + get => _softwareList.ReadString(Models.Internal.SoftwareList.NameKey); + set => _softwareList[Models.Internal.SoftwareList.NameKey] = value; + } /// /// Status of the softare list according to the machine @@ -37,7 +45,11 @@ namespace SabreTools.DatItems.Formats [JsonProperty("status", DefaultValueHandling = DefaultValueHandling.Ignore)] [JsonConverter(typeof(StringEnumConverter))] [XmlElement("status")] - public SoftwareListStatus Status { get; set; } + public SoftwareListStatus Status + { + get => _softwareList.ReadString(Models.Internal.SoftwareList.StatusKey).AsSoftwareListStatus(); + set => _softwareList[Models.Internal.SoftwareList.StatusKey] = value.FromSoftwareListStatus(); + } [JsonIgnore] public bool StatusSpecified { get { return Status != SoftwareListStatus.None; } } @@ -47,17 +59,27 @@ namespace SabreTools.DatItems.Formats /// [JsonProperty("filter", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("filter")] - public string Filter { get; set; } + public string? Filter + { + get => _softwareList.ReadString(Models.Internal.SoftwareList.FilterKey); + set => _softwareList[Models.Internal.SoftwareList.FilterKey] = value; + } + + /// + /// Internal SoftwareList model + /// + [JsonIgnore] + private Models.Internal.SoftwareList _softwareList = new(); #endregion #region Accessors /// - public override string GetName() => Name; + public override string? GetName() => Name; /// - public override void SetName(string name) => Name = name; + public override void SetName(string? name) => Name = name; #endregion @@ -83,14 +105,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - Tag = this.Tag, - Name = this.Name, - Status = this.Status, - Filter = this.Filter, + _softwareList = this._softwareList?.Clone() as Models.Internal.SoftwareList ?? new Models.Internal.SoftwareList(), }; } @@ -98,20 +117,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { - // If we don't have a sample, return false - if (ItemType != other.ItemType) + // If we don't have a Adjuster, return false + if (ItemType != other?.ItemType || other is not SoftwareList otherInternal) return false; - // Otherwise, treat it as a SoftwareList - SoftwareList newOther = other as SoftwareList; - - // If the SoftwareList information matches - return (Tag == newOther.Tag - && Name == newOther.Name - && Status == newOther.Status - && Filter == newOther.Filter); + // Compare the internal models + return _softwareList.EqualTo(otherInternal._softwareList); } #endregion diff --git a/SabreTools.DatItems/Formats/Sound.cs b/SabreTools.DatItems/Formats/Sound.cs index f14a3370..2e23401b 100644 --- a/SabreTools.DatItems/Formats/Sound.cs +++ b/SabreTools.DatItems/Formats/Sound.cs @@ -16,11 +16,21 @@ namespace SabreTools.DatItems.Formats /// Number of speakers or channels /// [JsonProperty("channels", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("channels")] - public long? Channels { get; set; } + public long? Channels + { + get => _sound.ReadLong(Models.Internal.Sound.ChannelsKey); + set => _sound[Models.Internal.Sound.ChannelsKey] = value; + } [JsonIgnore] public bool ChannelsSpecified { get { return Channels != null; } } + /// + /// Internal Sound model + /// + [JsonIgnore] + private Models.Internal.Sound _sound = new(); + #endregion #region Constructors @@ -45,11 +55,11 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, - Channels = this.Channels, + _sound = this._sound?.Clone() as Models.Internal.Sound ?? new Models.Internal.Sound(), }; } @@ -58,17 +68,14 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { // If we don't have a Sound, return false - if (ItemType != other.ItemType) + if (ItemType != other?.ItemType || other is not Sound otherInternal) return false; - // Otherwise, treat it as a Sound - Sound newOther = other as Sound; - - // If the Sound information matches - return (Channels == newOther.Channels); + // Compare the internal models + return _sound.EqualTo(otherInternal._sound); } #endregion diff --git a/SabreTools.DatItems/Formats/SourceDetails.cs b/SabreTools.DatItems/Formats/SourceDetails.cs index c54b3099..ad8fdf52 100644 --- a/SabreTools.DatItems/Formats/SourceDetails.cs +++ b/SabreTools.DatItems/Formats/SourceDetails.cs @@ -18,121 +18,121 @@ namespace SabreTools.DatItems.Formats /// /// TODO: Is this required? [JsonProperty("id", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("id")] - public string Id { get; set; } + public string? Id { get; set; } /// /// Section value /// [JsonProperty("section", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("section")] - public string Section { get; set; } + public string? Section { get; set; } /// /// Rom info value /// [JsonProperty("rominfo", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("rominfo")] - public string RomInfo { get; set; } + public string? RomInfo { get; set; } /// /// Dumping date value /// [JsonProperty("d_date", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("d_date")] - public string DDate { get; set; } + public string? DDate { get; set; } /// /// Dumping date info value /// [JsonProperty("d_date_info", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("d_date_info")] - public string DDateInfo { get; set; } + public string? DDateInfo { get; set; } /// /// Release date value /// [JsonProperty("r_date", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("r_date")] - public string RDate { get; set; } + public string? RDate { get; set; } /// /// Release date info value /// [JsonProperty("r_date_info", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("r_date_info")] - public string RDateInfo { get; set; } + public string? RDateInfo { get; set; } /// /// Origin value /// [JsonProperty("origin", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("origin")] - public string Origin { get; set; } + public string? Origin { get; set; } /// /// Region value /// [JsonProperty("region", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("region")] - public string Region { get; set; } + public string? Region { get; set; } /// /// Media title value /// [JsonProperty("media_title", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("media_title")] - public string MediaTitle { get; set; } + public string? MediaTitle { get; set; } /// /// Dumper value /// [JsonProperty("dumper", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("dumper")] - public string Dumper { get; set; } + public string? Dumper { get; set; } /// /// Project value /// [JsonProperty("project", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("project")] - public string Project { get; set; } + public string? Project { get; set; } /// /// Original format value /// [JsonProperty("originalformat", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("originalformat")] - public string OriginalFormat { get; set; } + public string? OriginalFormat { get; set; } /// /// Nodump value /// [JsonProperty("nodump", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("nodump")] - public string Nodump { get; set; } + public string? Nodump { get; set; } /// /// Tool value /// [JsonProperty("tool", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("tool")] - public string Tool { get; set; } + public string? Tool { get; set; } /// /// Comment 1 value /// [JsonProperty("comment1", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("comment1")] - public string Comment1 { get; set; } + public string? Comment1 { get; set; } /// /// Link 2 value /// [JsonProperty("comment2", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("comment2")] - public string Comment2 { get; set; } + public string? Comment2 { get; set; } /// /// Link 1 value /// [JsonProperty("link1", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("link1")] - public string Link1 { get; set; } + public string? Link1 { get; set; } /// /// Link 2 value /// [JsonProperty("link2", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("link2")] - public string Link2 { get; set; } + public string? Link2 { get; set; } /// /// Link 3 value /// [JsonProperty("link3", DefaultValueHandling = DefaultValueHandling.Ignore), XmlElement("link3")] - public string Link3 { get; set; } + public string? Link3 { get; set; } #endregion @@ -158,8 +158,8 @@ namespace SabreTools.DatItems.Formats ItemType = this.ItemType, DupeType = this.DupeType, - Machine = this.Machine.Clone() as Machine, - Source = this.Source.Clone() as Source, + Machine = this.Machine?.Clone() as Machine, + Source = this.Source?.Clone() as Source, Remove = this.Remove, Id = this.Id, @@ -190,17 +190,17 @@ namespace SabreTools.DatItems.Formats #region Comparision Methods /// - public override bool Equals(DatItem other) + public override bool Equals(DatItem? other) { // If we don't have a SourceDetails, return false - if (ItemType != other.ItemType) + if (ItemType != other?.ItemType) return false; // Otherwise, treat it as a SourceDetails - SourceDetails newOther = other as SourceDetails; + SourceDetails? newOther = other as SourceDetails; // If the Details information matches - return (Id == newOther.Id + return (Id == newOther!.Id && Section == newOther.Section && RomInfo == newOther.RomInfo && DDate == newOther.DDate diff --git a/SabreTools.DatItems/Machine.cs b/SabreTools.DatItems/Machine.cs index 456a7ece..8f7affb9 100644 --- a/SabreTools.DatItems/Machine.cs +++ b/SabreTools.DatItems/Machine.cs @@ -22,7 +22,7 @@ namespace SabreTools.DatItems /// [JsonProperty("name", DefaultValueHandling = DefaultValueHandling.Include)] [XmlElement("name")] - public string Name { get; set; } = null; + public string? Name { get; set; } = null; /// /// Additional notes @@ -30,63 +30,63 @@ namespace SabreTools.DatItems /// Known as "Extra" in AttractMode [JsonProperty("comment", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("comment")] - public string Comment { get; set; } = null; + public string? Comment { get; set; } = null; /// /// Extended description /// [JsonProperty("description", DefaultValueHandling = DefaultValueHandling.Include)] [XmlElement("description")] - public string Description { get; set; } = null; + public string? Description { get; set; } = null; /// /// Year(s) of release/manufacture /// [JsonProperty("year", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("year")] - public string Year { get; set; } = null; + public string? Year { get; set; } = null; /// /// Manufacturer, if available /// [JsonProperty("manufacturer", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("manufacturer")] - public string Manufacturer { get; set; } = null; + public string? Manufacturer { get; set; } = null; /// /// Publisher, if available /// [JsonProperty("publisher", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("publisher")] - public string Publisher { get; set; } = null; + public string? Publisher { get; set; } = null; /// /// Category, if available /// [JsonProperty("category", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("category")] - public string Category { get; set; } = null; + public string? Category { get; set; } = null; /// /// fomof parent /// [JsonProperty("romof", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("romof")] - public string RomOf { get; set; } = null; + public string? RomOf { get; set; } = null; /// /// cloneof parent /// [JsonProperty("cloneof", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("cloneof")] - public string CloneOf { get; set; } = null; + public string? CloneOf { get; set; } = null; /// /// sampleof parent /// [JsonProperty("sampleof", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("sampleof")] - public string SampleOf { get; set; } = null; + public string? SampleOf { get; set; } = null; /// /// Type of the machine @@ -109,49 +109,49 @@ namespace SabreTools.DatItems /// Also in Logiqx EmuArc [JsonProperty("players", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("players")] - public string Players { get; set; } = null; + public string? Players { get; set; } = null; /// /// Screen rotation /// [JsonProperty("rotation", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("rotation")] - public string Rotation { get; set; } = null; + public string? Rotation { get; set; } = null; /// /// Control method /// [JsonProperty("control", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("control")] - public string Control { get; set; } = null; + public string? Control { get; set; } = null; /// /// Support status /// [JsonProperty("status", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("status")] - public string Status { get; set; } = null; + public string? Status { get; set; } = null; /// /// Display count /// [JsonProperty("displaycount", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("displaycount")] - public string DisplayCount { get; set; } = null; + public string? DisplayCount { get; set; } = null; /// /// Display type /// [JsonProperty("displaytype", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("displaytype")] - public string DisplayType { get; set; } = null; + public string? DisplayType { get; set; } = null; /// /// Number of input buttons /// [JsonProperty("buttons", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("buttons")] - public string Buttons { get; set; } = null; + public string? Buttons { get; set; } = null; #endregion @@ -162,7 +162,7 @@ namespace SabreTools.DatItems /// [JsonProperty("history", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("history")] - public string History { get; set; } = null; + public string? History { get; set; } = null; /// /// Emulator source file related to the machine @@ -170,7 +170,7 @@ namespace SabreTools.DatItems /// Also in Logiqx [JsonProperty("sourcefile", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("sourcefile")] - public string SourceFile { get; set; } = null; + public string? SourceFile { get; set; } = null; /// /// Machine runnable status @@ -192,28 +192,28 @@ namespace SabreTools.DatItems /// [JsonProperty("board", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("board")] - public string Board { get; set; } = null; + public string? Board { get; set; } = null; /// /// Rebuild location if different than machine name /// [JsonProperty("rebuildto", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("rebuildto")] - public string RebuildTo { get; set; } = null; + public string? RebuildTo { get; set; } = null; /// /// No-Intro ID for the game /// [JsonProperty("nointroid", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("nointroid")] - public string NoIntroId { get; set; } = null; + public string? NoIntroId { get; set; } = null; /// /// No-Intro ID for the game /// [JsonProperty("nointrocloneofid", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("nointrocloneofid")] - public string NoIntroCloneOfId { get; set; } = null; + public string? NoIntroCloneOfId { get; set; } = null; #endregion @@ -225,49 +225,49 @@ namespace SabreTools.DatItems /// [JsonProperty("titleid", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("titleid")] - public string TitleID { get; set; } = null; + public string? TitleID { get; set; } = null; /// /// Machine developer /// [JsonProperty("developer", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("developer")] - public string Developer { get; set; } = null; + public string? Developer { get; set; } = null; /// /// Game genre /// [JsonProperty("genre", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("genre")] - public string Genre { get; set; } = null; + public string? Genre { get; set; } = null; /// /// Game subgenre /// [JsonProperty("subgenre", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("subgenre")] - public string Subgenre { get; set; } = null; + public string? Subgenre { get; set; } = null; /// /// Game ratings /// [JsonProperty("ratings", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("ratings")] - public string Ratings { get; set; } = null; + public string? Ratings { get; set; } = null; /// /// Game score /// [JsonProperty("score", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("score")] - public string Score { get; set; } = null; + public string? Score { get; set; } = null; /// /// Is the machine enabled /// [JsonProperty("enabled", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("enabled")] - public string Enabled { get; set; } = null; // bool? + public string? Enabled { get; set; } = null; // bool? /// /// Does the game have a CRC check @@ -284,7 +284,7 @@ namespace SabreTools.DatItems /// [JsonProperty("relatedto", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("relatedto")] - public string RelatedTo { get; set; } = null; + public string? RelatedTo { get; set; } = null; #endregion @@ -295,21 +295,21 @@ namespace SabreTools.DatItems /// [JsonProperty("genmsxid", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("genmsxid")] - public string GenMSXID { get; set; } = null; + public string? GenMSXID { get; set; } = null; /// /// MSX System /// [JsonProperty("system", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("system")] - public string System { get; set; } = null; + public string? System { get; set; } = null; /// /// Machine country of origin /// [JsonProperty("country", DefaultValueHandling = DefaultValueHandling.Ignore)] [XmlElement("country")] - public string Country { get; set; } = null; + public string? Country { get; set; } = null; #endregion diff --git a/SabreTools.DatItems/SabreTools.DatItems.csproj b/SabreTools.DatItems/SabreTools.DatItems.csproj index 0d71852a..1884a695 100644 --- a/SabreTools.DatItems/SabreTools.DatItems.csproj +++ b/SabreTools.DatItems/SabreTools.DatItems.csproj @@ -2,12 +2,14 @@ net6.0;net7.0 + enable + diff --git a/SabreTools.DatItems/Source.cs b/SabreTools.DatItems/Source.cs index a63718eb..b8a107c0 100644 --- a/SabreTools.DatItems/Source.cs +++ b/SabreTools.DatItems/Source.cs @@ -15,14 +15,14 @@ namespace SabreTools.DatItems /// /// Source name /// - public string Name { get; set; } + public string? Name { get; set; } /// /// Constructor /// /// Source ID, default 0 /// Source name, default null - public Source(int id = 0, string source = null) + public Source(int id = 0, string? source = null) { Index = id; Name = source; diff --git a/SabreTools.Filter/FieldManipulator.cs b/SabreTools.Filter/FieldManipulator.cs index f6fdc40a..4e9b00a0 100644 --- a/SabreTools.Filter/FieldManipulator.cs +++ b/SabreTools.Filter/FieldManipulator.cs @@ -70,7 +70,7 @@ namespace SabreTools.Filter // Retrieve the list of valid fields for the item and validate var constants = TypeHelper.GetConstants(dictionaryBase.GetType()); - if (constants == null || !constants.Any(c => string.Equals(c, fieldName, StringComparison.InvariantCultureIgnoreCase))) + if (constants == null || !constants.Any(c => string.Equals(c, fieldName, StringComparison.OrdinalIgnoreCase))) return false; // Set the field with the new value diff --git a/SabreTools.Filtering/DatItemFilter.cs b/SabreTools.Filtering/DatItemFilter.cs index c5bf4a63..57c0c6dd 100644 --- a/SabreTools.Filtering/DatItemFilter.cs +++ b/SabreTools.Filtering/DatItemFilter.cs @@ -785,10 +785,14 @@ namespace SabreTools.Filtering Chip chip => PassesFilters(chip), Condition condition => PassesFilters(condition), Configuration configuration => PassesFilters(configuration), + ConfLocation confLocation => PassesFilters(confLocation), + ConfSetting confSetting => PassesFilters(confSetting), Control control => PassesFilters(control), DataArea dataArea => PassesFilters(dataArea), Device device => PassesFilters(device), + DipLocation dipLocation => PassesFilters(dipLocation), DipSwitch dipSwitch => PassesFilters(dipSwitch), + DipValue dipValue => PassesFilters(dipValue), Disk disk => PassesFilters(disk), DiskArea diskArea => PassesFilters(diskArea), Display display => PassesFilters(display), @@ -798,7 +802,6 @@ namespace SabreTools.Filtering Info info => PassesFilters(info), Input input => PassesFilters(input), Instance instance => PassesFilters(instance), - Location location => PassesFilters(location), Media media => PassesFilters(media), Part part => PassesFilters(part), PartFeature partFeature => PassesFilters(partFeature), @@ -806,7 +809,6 @@ namespace SabreTools.Filtering RamOption ramOption => PassesFilters(ramOption), Release release => PassesFilters(release), Rom rom => PassesFilters(rom), - Setting setting => PassesFilters(setting), SharedFeature sharedFeature => PassesFilters(sharedFeature), Slot slot => PassesFilters(slot), SlotOption slotOption => PassesFilters(slotOption), @@ -1024,7 +1026,7 @@ namespace SabreTools.Filtering // Filter on individual locations if (configuration.LocationsSpecified) { - foreach (Location subLocation in configuration.Locations) + foreach (ConfLocation subLocation in configuration.Locations) { if (!PassesFilters(subLocation)) return false; @@ -1034,7 +1036,7 @@ namespace SabreTools.Filtering // Filter on individual settings if (configuration.SettingsSpecified) { - foreach (Setting subSetting in configuration.Settings) + foreach (ConfSetting subSetting in configuration.Settings) { if (!PassesFilters(subSetting)) return false; @@ -1044,6 +1046,60 @@ namespace SabreTools.Filtering return true; } + /// + /// Check to see if a ConfLocation passes the filters + /// + /// ConfLocation to check + /// True if the item passed the filter, false otherwise + private bool PassesFilters(ConfLocation confLocation) + { + // DatItem_Location_Inverted + if (!PassBoolFilter(Location_Inverted, confLocation.Inverted)) + return false; + + // DatItem_Location_Name + if (!PassStringFilter(Location_Name, confLocation.Name)) + return false; + + // DatItem_Location_Number + if (!PassLongFilter(Location_Number, confLocation.Number)) + return false; + + return true; + } + + /// + /// Check to see if a ConfSetting passes the filters + /// + /// ConfSetting to check + /// True if the item passed the filter, false otherwise + private bool PassesFilters(ConfSetting confSetting) + { + // DatItem_Setting_Default + if (!PassBoolFilter(Setting_Default, confSetting.Default)) + return false; + + // DatItem_Setting_Name + if (!PassStringFilter(Setting_Name, confSetting.Name)) + return false; + + // DatItem_Setting_Value + if (!PassStringFilter(Setting_Value, confSetting.Value)) + return false; + + // Filter on individual conditions + if (confSetting.ConditionsSpecified) + { + foreach (Condition subCondition in confSetting.Conditions) + { + if (!PassesFilters(subCondition, true)) + return false; + } + } + + return true; + } + /// /// Check to see if a Control passes the filters /// @@ -1184,6 +1240,28 @@ namespace SabreTools.Filtering return true; } + /// + /// Check to see if a DipLocation passes the filters + /// + /// DipLocation to check + /// True if the item passed the filter, false otherwise + private bool PassesFilters(DipLocation dipLocation) + { + // DatItem_Location_Inverted + if (!PassBoolFilter(Location_Inverted, dipLocation.Inverted)) + return false; + + // DatItem_Location_Name + if (!PassStringFilter(Location_Name, dipLocation.Name)) + return false; + + // DatItem_Location_Number + if (!PassLongFilter(Location_Number, dipLocation.Number)) + return false; + + return true; + } + /// /// Check to see if a DipSwitch passes the filters /// @@ -1212,7 +1290,7 @@ namespace SabreTools.Filtering // Filter on individual locations if (dipSwitch.LocationsSpecified) { - foreach (Location subLocation in dipSwitch.Locations) + foreach (DipLocation subLocation in dipSwitch.Locations) { if (!PassesFilters(subLocation)) return false; @@ -1222,7 +1300,7 @@ namespace SabreTools.Filtering // Filter on individual values if (dipSwitch.ValuesSpecified) { - foreach (Setting subValue in dipSwitch.Values) + foreach (DipValue subValue in dipSwitch.Values) { if (!PassesFilters(subValue)) return false; @@ -1239,6 +1317,38 @@ namespace SabreTools.Filtering return true; } + /// + /// Check to see if a DipValue passes the filters + /// + /// DipValue to check + /// True if the item passed the filter, false otherwise + private bool PassesFilters(DipValue dipValue) + { + // DatItem_Setting_Default + if (!PassBoolFilter(Setting_Default, dipValue.Default)) + return false; + + // DatItem_Setting_Name + if (!PassStringFilter(Setting_Name, dipValue.Name)) + return false; + + // DatItem_Setting_Value + if (!PassStringFilter(Setting_Value, dipValue.Value)) + return false; + + // Filter on individual conditions + if (dipValue.ConditionsSpecified) + { + foreach (Condition subCondition in dipValue.Conditions) + { + if (!PassesFilters(subCondition, true)) + return false; + } + } + + return true; + } + /// /// Check to see if a Disk passes the filters /// @@ -1539,28 +1649,6 @@ namespace SabreTools.Filtering return true; } - /// - /// Check to see if a Location passes the filters - /// - /// Location to check - /// True if the item passed the filter, false otherwise - private bool PassesFilters(Location location) - { - // DatItem_Location_Inverted - if (!PassBoolFilter(Location_Inverted, location.Inverted)) - return false; - - // DatItem_Location_Name - if (!PassStringFilter(Location_Name, location.Name)) - return false; - - // DatItem_Location_Number - if (!PassLongFilter(Location_Number, location.Number)) - return false; - - return true; - } - /// /// Check to see if a Media passes the filters /// @@ -1855,38 +1943,6 @@ namespace SabreTools.Filtering return true; } - /// - /// Check to see if a Setting passes the filters - /// - /// Setting to check - /// True if the item passed the filter, false otherwise - private bool PassesFilters(Setting setting) - { - // DatItem_Setting_Default - if (!PassBoolFilter(Setting_Default, setting.Default)) - return false; - - // DatItem_Setting_Name - if (!PassStringFilter(Setting_Name, setting.Name)) - return false; - - // DatItem_Setting_Value - if (!PassStringFilter(Setting_Value, setting.Value)) - return false; - - // Filter on individual conditions - if (setting.ConditionsSpecified) - { - foreach (Condition subCondition in setting.Conditions) - { - if (!PassesFilters(subCondition, true)) - return false; - } - } - - return true; - } - /// /// Check to see if a SharedFeature passes the filters /// diff --git a/SabreTools.Filtering/DatItemRemover.cs b/SabreTools.Filtering/DatItemRemover.cs index 8fc58e1b..98c128b2 100644 --- a/SabreTools.Filtering/DatItemRemover.cs +++ b/SabreTools.Filtering/DatItemRemover.cs @@ -92,10 +92,14 @@ namespace SabreTools.Filtering else if (datItem is Chip) RemoveFields(datItem as Chip); else if (datItem is Condition) RemoveFields(datItem as Condition); else if (datItem is Configuration) RemoveFields(datItem as Configuration); + else if (datItem is ConfLocation) RemoveFields(datItem as ConfLocation); + else if (datItem is ConfSetting) RemoveFields(datItem as ConfSetting); else if (datItem is Control) RemoveFields(datItem as Control); else if (datItem is DataArea) RemoveFields(datItem as DataArea); else if (datItem is Device) RemoveFields(datItem as Device); + else if (datItem is DipLocation) RemoveFields(datItem as DipLocation); else if (datItem is DipSwitch) RemoveFields(datItem as DipSwitch); + else if (datItem is DipValue) RemoveFields(datItem as DipValue); else if (datItem is Disk) RemoveFields(datItem as Disk); else if (datItem is DiskArea) RemoveFields(datItem as DiskArea); else if (datItem is Display) RemoveFields(datItem as Display); @@ -105,7 +109,6 @@ namespace SabreTools.Filtering else if (datItem is Info) RemoveFields(datItem as Info); else if (datItem is Input) RemoveFields(datItem as Input); else if (datItem is Instance) RemoveFields(datItem as Instance); - else if (datItem is Location) RemoveFields(datItem as Location); else if (datItem is Media) RemoveFields(datItem as Media); else if (datItem is Part) RemoveFields(datItem as Part); else if (datItem is PartFeature) RemoveFields(datItem as PartFeature); @@ -113,7 +116,6 @@ namespace SabreTools.Filtering else if (datItem is RamOption) RemoveFields(datItem as RamOption); else if (datItem is Release) RemoveFields(datItem as Release); else if (datItem is Rom) RemoveFields(datItem as Rom); - else if (datItem is Setting) RemoveFields(datItem as Setting); else if (datItem is SharedFeature) RemoveFields(datItem as SharedFeature); else if (datItem is Slot) RemoveFields(datItem as Slot); else if (datItem is SlotOption) RemoveFields(datItem as SlotOption); @@ -394,7 +396,7 @@ namespace SabreTools.Filtering if (configuration.LocationsSpecified) { - foreach (Location subLocation in configuration.Locations) + foreach (ConfLocation subLocation in configuration.Locations) { RemoveFields(subLocation); } @@ -402,13 +404,53 @@ namespace SabreTools.Filtering if (configuration.SettingsSpecified) { - foreach (Setting subSetting in configuration.Settings) + foreach (ConfSetting subSetting in configuration.Settings) { RemoveFields(subSetting); } } } + /// + /// Remove fields with given values + /// + /// ConfLocation to remove fields from + private void RemoveFields(ConfLocation confLocation) + { + if (DatItemFields.Contains(DatItemField.Location_Inverted)) + confLocation.Inverted = null; + + if (DatItemFields.Contains(DatItemField.Location_Name)) + confLocation.Name = null; + + if (DatItemFields.Contains(DatItemField.Location_Number)) + confLocation.Number = null; + } + + /// + /// Remove fields with given values + /// + /// ConfSetting to remove fields from + private void RemoveFields(ConfSetting confsetting) + { + if (DatItemFields.Contains(DatItemField.Setting_Default)) + confsetting.Default = null; + + if (DatItemFields.Contains(DatItemField.Setting_Name)) + confsetting.Name = null; + + if (DatItemFields.Contains(DatItemField.Setting_Value)) + confsetting.Value = null; + + if (confsetting.ConditionsSpecified) + { + foreach (Condition subCondition in confsetting.Conditions) + { + RemoveFields(subCondition, true); + } + } + } + /// /// Remove fields with given values /// @@ -509,6 +551,22 @@ namespace SabreTools.Filtering } } + /// + /// Remove fields with given values + /// + /// DipLocation to remove fields from + private void RemoveFields(DipLocation dipLocation) + { + if (DatItemFields.Contains(DatItemField.Location_Inverted)) + dipLocation.Inverted = null; + + if (DatItemFields.Contains(DatItemField.Location_Name)) + dipLocation.Name = null; + + if (DatItemFields.Contains(DatItemField.Location_Number)) + dipLocation.Number = null; + } + /// /// Remove fields with given values /// @@ -531,7 +589,7 @@ namespace SabreTools.Filtering if (dipSwitch.LocationsSpecified) { - foreach (Location subLocation in dipSwitch.Locations) + foreach (DipLocation subLocation in dipSwitch.Locations) { RemoveFields(subLocation); } @@ -539,7 +597,7 @@ namespace SabreTools.Filtering if (dipSwitch.ValuesSpecified) { - foreach (Setting subValue in dipSwitch.Values) + foreach (DipValue subValue in dipSwitch.Values) { RemoveFields(subValue); } @@ -549,6 +607,30 @@ namespace SabreTools.Filtering RemoveFields(dipSwitch.Part); } + /// + /// Remove fields with given values + /// + /// DipValue to remove fields from + private void RemoveFields(DipValue dipValue) + { + if (DatItemFields.Contains(DatItemField.Setting_Default)) + dipValue.Default = null; + + if (DatItemFields.Contains(DatItemField.Setting_Name)) + dipValue.Name = null; + + if (DatItemFields.Contains(DatItemField.Setting_Value)) + dipValue.Value = null; + + if (dipValue.ConditionsSpecified) + { + foreach (Condition subCondition in dipValue.Conditions) + { + RemoveFields(subCondition, true); + } + } + } + /// /// Remove fields with given values /// @@ -752,22 +834,6 @@ namespace SabreTools.Filtering instance.Name = null; } - /// - /// Remove fields with given values - /// - /// Location to remove fields from - private void RemoveFields(Location location) - { - if (DatItemFields.Contains(DatItemField.Location_Inverted)) - location.Inverted = null; - - if (DatItemFields.Contains(DatItemField.Location_Name)) - location.Name = null; - - if (DatItemFields.Contains(DatItemField.Location_Number)) - location.Number = null; - } - /// /// Remove fields with given values /// @@ -977,30 +1043,6 @@ namespace SabreTools.Filtering RemoveFields(rom.Part); } - /// - /// Remove fields with given values - /// - /// Setting to remove fields from - private void RemoveFields(Setting setting) - { - if (DatItemFields.Contains(DatItemField.Setting_Default)) - setting.Default = null; - - if (DatItemFields.Contains(DatItemField.Setting_Name)) - setting.Name = null; - - if (DatItemFields.Contains(DatItemField.Setting_Value)) - setting.Value = null; - - if (setting.ConditionsSpecified) - { - foreach (Condition subCondition in setting.Conditions) - { - RemoveFields(subCondition, true); - } - } - } - /// /// Remove fields with given values /// diff --git a/SabreTools.Filtering/Replacer.cs b/SabreTools.Filtering/Replacer.cs index 6e6ea810..a5a0fa60 100644 --- a/SabreTools.Filtering/Replacer.cs +++ b/SabreTools.Filtering/Replacer.cs @@ -40,10 +40,14 @@ namespace SabreTools.Filtering else if (datItem is Chip) ReplaceFields(datItem as Chip, repDatItem as Chip, datItemFields); else if (datItem is Condition) ReplaceFields(datItem as Condition, repDatItem as Condition, datItemFields); else if (datItem is Configuration) ReplaceFields(datItem as Configuration, repDatItem as Configuration, datItemFields); + else if (datItem is ConfLocation) ReplaceFields(datItem as ConfLocation, repDatItem as ConfLocation, datItemFields); + else if (datItem is ConfSetting) ReplaceFields(datItem as ConfSetting, repDatItem as ConfSetting, datItemFields); else if (datItem is Control) ReplaceFields(datItem as Control, repDatItem as Control, datItemFields); else if (datItem is DataArea) ReplaceFields(datItem as DataArea, repDatItem as DataArea, datItemFields); else if (datItem is Device) ReplaceFields(datItem as Device, repDatItem as Device, datItemFields); + else if (datItem is DipLocation) ReplaceFields(datItem as DipLocation, repDatItem as DipLocation, datItemFields); else if (datItem is DipSwitch) ReplaceFields(datItem as DipSwitch, repDatItem as DipSwitch, datItemFields); + else if (datItem is DipValue) ReplaceFields(datItem as DipValue, repDatItem as DipValue, datItemFields); else if (datItem is Disk) ReplaceFields(datItem as Disk, repDatItem as Disk, datItemFields); else if (datItem is DiskArea) ReplaceFields(datItem as DiskArea, repDatItem as DiskArea, datItemFields); else if (datItem is Display) ReplaceFields(datItem as Display, repDatItem as Display, datItemFields); @@ -53,7 +57,6 @@ namespace SabreTools.Filtering else if (datItem is Info) ReplaceFields(datItem as Info, repDatItem as Info, datItemFields); else if (datItem is Input) ReplaceFields(datItem as Input, repDatItem as Input, datItemFields); else if (datItem is Instance) ReplaceFields(datItem as Instance, repDatItem as Instance, datItemFields); - else if (datItem is Location) ReplaceFields(datItem as Location, repDatItem as Location, datItemFields); else if (datItem is Media) ReplaceFields(datItem as Media, repDatItem as Media, datItemFields); else if (datItem is Part) ReplaceFields(datItem as Part, repDatItem as Part, datItemFields); else if (datItem is PartFeature) ReplaceFields(datItem as PartFeature, repDatItem as PartFeature, datItemFields); @@ -61,7 +64,6 @@ namespace SabreTools.Filtering else if (datItem is RamOption) ReplaceFields(datItem as RamOption, repDatItem as RamOption, datItemFields); else if (datItem is Release) ReplaceFields(datItem as Release, repDatItem as Release, datItemFields); else if (datItem is Rom) ReplaceFields(datItem as Rom, repDatItem as Rom, datItemFields); - else if (datItem is Setting) ReplaceFields(datItem as Setting, repDatItem as Setting, datItemFields); else if (datItem is SharedFeature) ReplaceFields(datItem as SharedFeature, repDatItem as SharedFeature, datItemFields); else if (datItem is Slot) ReplaceFields(datItem as Slot, repDatItem as Slot, datItemFields); else if (datItem is SlotOption) ReplaceFields(datItem as SlotOption, repDatItem as SlotOption, datItemFields); @@ -351,6 +353,46 @@ namespace SabreTools.Filtering // can replace every setting under this item } + /// + /// Replace fields with given values + /// + /// ConfLocation to remove replace fields in + /// ConfLocation to pull new information from + /// List of fields representing what should be updated + private static void ReplaceFields(ConfLocation location, ConfLocation newItem, List datItemFields) + { + if (datItemFields.Contains(DatItemField.Location_Inverted)) + location.Inverted = newItem.Inverted; + + if (datItemFields.Contains(DatItemField.Location_Name)) + location.Name = newItem.Name; + + if (datItemFields.Contains(DatItemField.Location_Number)) + location.Number = newItem.Number; + } + + /// + /// Replace fields with given values + /// + /// ConfSetting to remove replace fields in + /// ConfSetting to pull new information from + /// List of fields representing what should be updated + private static void ReplaceFields(ConfSetting confSetting, ConfSetting newItem, List datItemFields) + { + if (datItemFields.Contains(DatItemField.Setting_Default)) + confSetting.Default = newItem.Default; + + if (datItemFields.Contains(DatItemField.Setting_Name)) + confSetting.Name = newItem.Name; + + if (datItemFields.Contains(DatItemField.Setting_Value)) + confSetting.Value = newItem.Value; + + // Condition_* doesn't make sense here + // since not every condition under the other item + // can replace every condition under this item + } + /// /// Replace fields with given values /// @@ -449,6 +491,24 @@ namespace SabreTools.Filtering // can replace every extension under this item } + /// + /// Replace fields with given values + /// + /// DipLocation to remove replace fields in + /// DipLocation to pull new information from + /// List of fields representing what should be updated + private static void ReplaceFields(DipLocation location, DipLocation newItem, List datItemFields) + { + if (datItemFields.Contains(DatItemField.Location_Inverted)) + location.Inverted = newItem.Inverted; + + if (datItemFields.Contains(DatItemField.Location_Name)) + location.Name = newItem.Name; + + if (datItemFields.Contains(DatItemField.Location_Number)) + location.Number = newItem.Number; + } + /// /// Replace fields with given values /// @@ -479,6 +539,28 @@ namespace SabreTools.Filtering ReplaceFields(dipSwitch.Part, newItem.Part, datItemFields); } + /// + /// Replace fields with given values + /// + /// DipValue to remove replace fields in + /// DipValue to pull new information from + /// List of fields representing what should be updated + private static void ReplaceFields(DipValue dipValue, DipValue newItem, List datItemFields) + { + if (datItemFields.Contains(DatItemField.Setting_Default)) + dipValue.Default = newItem.Default; + + if (datItemFields.Contains(DatItemField.Setting_Name)) + dipValue.Name = newItem.Name; + + if (datItemFields.Contains(DatItemField.Setting_Value)) + dipValue.Value = newItem.Value; + + // Condition_* doesn't make sense here + // since not every condition under the other item + // can replace every condition under this item + } + /// /// Replace fields with given values /// @@ -702,24 +784,6 @@ namespace SabreTools.Filtering instance.Name = newItem.Name; } - /// - /// Replace fields with given values - /// - /// Location to remove replace fields in - /// Location to pull new information from - /// List of fields representing what should be updated - private static void ReplaceFields(Location location, Location newItem, List datItemFields) - { - if (datItemFields.Contains(DatItemField.Location_Inverted)) - location.Inverted = newItem.Inverted; - - if (datItemFields.Contains(DatItemField.Location_Name)) - location.Name = newItem.Name; - - if (datItemFields.Contains(DatItemField.Location_Number)) - location.Number = newItem.Number; - } - /// /// Replace fields with given values /// @@ -968,28 +1032,6 @@ namespace SabreTools.Filtering ReplaceFields(rom.Part, newItem.Part, datItemFields); } - /// - /// Replace fields with given values - /// - /// Setting to remove replace fields in - /// Setting to pull new information from - /// List of fields representing what should be updated - private static void ReplaceFields(Setting setting, Setting newItem, List datItemFields) - { - if (datItemFields.Contains(DatItemField.Setting_Default)) - setting.Default = newItem.Default; - - if (datItemFields.Contains(DatItemField.Setting_Name)) - setting.Name = newItem.Name; - - if (datItemFields.Contains(DatItemField.Setting_Value)) - setting.Value = newItem.Value; - - // Condition_* doesn't make sense here - // since not every condition under the other item - // can replace every condition under this item - } - /// /// Replace fields with given values /// diff --git a/SabreTools.Models/Internal/DictionaryBase.cs b/SabreTools.Models/Internal/DictionaryBase.cs index 9424042e..1a0077fd 100644 --- a/SabreTools.Models/Internal/DictionaryBase.cs +++ b/SabreTools.Models/Internal/DictionaryBase.cs @@ -93,6 +93,9 @@ namespace SabreTools.Models.Internal if (asArray != null) return string.Join(',', asArray); + // TODO: Add byte array conversion here + // TODO: Add byte array read helper + return this[key]!.ToString(); } diff --git a/SabreTools.Models/Internal/RamOption.cs b/SabreTools.Models/Internal/RamOption.cs index 79c91b3f..de59581c 100644 --- a/SabreTools.Models/Internal/RamOption.cs +++ b/SabreTools.Models/Internal/RamOption.cs @@ -8,6 +8,9 @@ namespace SabreTools.Models.Internal { #region Keys + /// string + public const string ContentKey = "content"; + /// (yes|no) "no" public const string DefaultKey = "default"; diff --git a/SabreTools.Models/Listxml/RamOption.cs b/SabreTools.Models/Listxml/RamOption.cs index f3e42f7c..a6a7603f 100644 --- a/SabreTools.Models/Listxml/RamOption.cs +++ b/SabreTools.Models/Listxml/RamOption.cs @@ -13,6 +13,9 @@ namespace SabreTools.Models.Listxml [XmlAttribute("default")] public string? Default { get; set; } + [XmlText] + public string? Content { get; set; } + #region DO NOT USE IN PRODUCTION /// Should be empty diff --git a/SabreTools.Serialization/Listxml.Deserializer.cs b/SabreTools.Serialization/Listxml.Deserializer.cs index 3adf41f4..ecb6d513 100644 --- a/SabreTools.Serialization/Listxml.Deserializer.cs +++ b/SabreTools.Serialization/Listxml.Deserializer.cs @@ -717,6 +717,7 @@ namespace SabreTools.Serialization { Name = item.ReadString(Models.Internal.RamOption.NameKey), Default = item.ReadString(Models.Internal.RamOption.DefaultKey), + Content = item.ReadString(Models.Internal.RamOption.ContentKey), }; return ramOption; } diff --git a/SabreTools.Serialization/Listxml.Serializer.cs b/SabreTools.Serialization/Listxml.Serializer.cs index 18c14b09..51cbc1b6 100644 --- a/SabreTools.Serialization/Listxml.Serializer.cs +++ b/SabreTools.Serialization/Listxml.Serializer.cs @@ -685,6 +685,7 @@ namespace SabreTools.Serialization { [Models.Internal.RamOption.NameKey] = item.Name, [Models.Internal.RamOption.DefaultKey] = item.Default, + [Models.Internal.RamOption.ContentKey] = item.Content, }; return ramOption; } diff --git a/SabreTools.Test/Core/ConvertersTests.cs b/SabreTools.Test/Core/ConvertersTests.cs index e1786bf8..9c23f74c 100644 --- a/SabreTools.Test/Core/ConvertersTests.cs +++ b/SabreTools.Test/Core/ConvertersTests.cs @@ -194,12 +194,16 @@ namespace SabreTools.Test.Core [InlineData("chip", ItemType.Chip)] [InlineData("condition", ItemType.Condition)] [InlineData("configuration", ItemType.Configuration)] + [InlineData("conflocation", ItemType.ConfLocation)] + [InlineData("confsetting", ItemType.ConfSetting)] [InlineData("control", ItemType.Control)] [InlineData("dataarea", ItemType.DataArea)] [InlineData("device", ItemType.Device)] [InlineData("deviceref", ItemType.DeviceReference)] [InlineData("device_ref", ItemType.DeviceReference)] + [InlineData("diplocation", ItemType.DipLocation)] [InlineData("dipswitch", ItemType.DipSwitch)] + [InlineData("dipvalue", ItemType.DipValue)] [InlineData("disk", ItemType.Disk)] [InlineData("diskarea", ItemType.DiskArea)] [InlineData("display", ItemType.Display)] @@ -210,7 +214,6 @@ namespace SabreTools.Test.Core [InlineData("info", ItemType.Info)] [InlineData("input", ItemType.Input)] [InlineData("instance", ItemType.Instance)] - [InlineData("location", ItemType.Location)] [InlineData("media", ItemType.Media)] [InlineData("part", ItemType.Part)] [InlineData("partfeature", ItemType.PartFeature)] @@ -224,7 +227,6 @@ namespace SabreTools.Test.Core [InlineData("rom", ItemType.Rom)] [InlineData("sample", ItemType.Sample)] [InlineData("serials", ItemType.Serials)] - [InlineData("setting", ItemType.Setting)] [InlineData("sharedfeat", ItemType.SharedFeature)] [InlineData("shared_feat", ItemType.SharedFeature)] [InlineData("sharedfeature", ItemType.SharedFeature)] @@ -657,11 +659,15 @@ namespace SabreTools.Test.Core [InlineData(ItemType.Chip, "chip")] [InlineData(ItemType.Condition, "condition")] [InlineData(ItemType.Configuration, "configuration")] + [InlineData(ItemType.ConfLocation, "conflocation")] + [InlineData(ItemType.ConfSetting, "confsetting")] [InlineData(ItemType.Control, "control")] [InlineData(ItemType.DataArea, "dataarea")] [InlineData(ItemType.Device, "device")] [InlineData(ItemType.DeviceReference, "device_ref")] + [InlineData(ItemType.DipLocation, "diplocation")] [InlineData(ItemType.DipSwitch, "dipswitch")] + [InlineData(ItemType.DipValue, "dipvalue")] [InlineData(ItemType.Disk, "disk")] [InlineData(ItemType.DiskArea, "diskarea")] [InlineData(ItemType.Display, "display")] @@ -672,7 +678,6 @@ namespace SabreTools.Test.Core [InlineData(ItemType.Info, "info")] [InlineData(ItemType.Input, "input")] [InlineData(ItemType.Instance, "instance")] - [InlineData(ItemType.Location, "location")] [InlineData(ItemType.Media, "media")] [InlineData(ItemType.Part, "part")] [InlineData(ItemType.PartFeature, "part_feature")] @@ -683,7 +688,6 @@ namespace SabreTools.Test.Core [InlineData(ItemType.Rom, "rom")] [InlineData(ItemType.Sample, "sample")] [InlineData(ItemType.Serials, "serials")] - [InlineData(ItemType.Setting, "setting")] [InlineData(ItemType.SharedFeature, "sharedfeat")] [InlineData(ItemType.Slot, "slot")] [InlineData(ItemType.SlotOption, "slotoption")]