diff --git a/SabreTools.DatFiles/Formats/ClrMamePro.Reader.cs b/SabreTools.DatFiles/Formats/ClrMamePro.Reader.cs index 83b83189..5b01f166 100644 --- a/SabreTools.DatFiles/Formats/ClrMamePro.Reader.cs +++ b/SabreTools.DatFiles/Formats/ClrMamePro.Reader.cs @@ -129,7 +129,7 @@ namespace SabreTools.DatFiles.Formats ConvertMedia(game.Media, machine, filename, indexId, statsOnly, ref containsItems); ConvertArchives(game.Archive, machine, filename, indexId, statsOnly, ref containsItems); ConvertChips(game.Chip, machine, filename, indexId, statsOnly, ref containsItems); - ConvertVideo(game.Video, machine, filename, indexId, statsOnly, ref containsItems); + ConvertVideos(game.Video, machine, filename, indexId, statsOnly, ref containsItems); ConvertSound(game.Sound, machine, filename, indexId, statsOnly, ref containsItems); ConvertInput(game.Input, machine, filename, indexId, statsOnly, ref containsItems); ConvertDipSwitches(game.DipSwitch, machine, filename, indexId, statsOnly, ref containsItems); @@ -431,47 +431,50 @@ namespace SabreTools.DatFiles.Formats /// /// Convert Video information /// - /// Deserialized model to convert + /// Array of deserialized models to convert /// Prefilled machine to use /// Name of the file to be parsed /// Index ID for the DAT /// True to only add item statistics while parsing, false otherwise /// True if there were any items in the array, false otherwise - private void ConvertVideo(Models.ClrMamePro.Video? video, Machine machine, string filename, int indexId, bool statsOnly, ref bool containsItems) + private void ConvertVideos(Models.ClrMamePro.Video[]? videos, Machine machine, string filename, int indexId, bool statsOnly, ref bool containsItems) { - // If the video is missing, we can't do anything - if (video == null) + // If the video array is missing, we can't do anything + if (videos == null || !videos.Any()) return; containsItems = true; - var item = new Display + foreach (var video in videos) { - DisplayType = video.Screen?.AsDisplayType() ?? DisplayType.NULL, - Width = Utilities.CleanLong(video.X), - Height = Utilities.CleanLong(video.Y), - //AspectX = video.AspectX, // TODO: Add to internal model or find mapping - //AspectY = video.AspectY, // TODO: Add to internal model or find mapping - Refresh = Utilities.CleanDouble(video.Freq), - - Source = new Source + var item = new Display { - Index = indexId, - Name = filename, - }, - }; + DisplayType = video.Screen?.AsDisplayType() ?? DisplayType.NULL, + Width = Utilities.CleanLong(video.X), + Height = Utilities.CleanLong(video.Y), + //AspectX = video.AspectX, // TODO: Add to internal model or find mapping + //AspectY = video.AspectY, // TODO: Add to internal model or find mapping + Refresh = Utilities.CleanDouble(video.Freq), - switch (video.Orientation) - { - case "horizontal": - item.Rotate = 0; - break; - case "vertical": - item.Rotate = 90; - break; + Source = new Source + { + Index = indexId, + Name = filename, + }, + }; + + switch (video.Orientation) + { + case "horizontal": + item.Rotate = 0; + break; + case "vertical": + item.Rotate = 90; + break; + } + + item.CopyMachineInformation(machine); + ParseAddHelper(item, statsOnly); } - - item.CopyMachineInformation(machine); - ParseAddHelper(item, statsOnly); } /// diff --git a/SabreTools.DatFiles/Formats/ClrMamePro.Writer.cs b/SabreTools.DatFiles/Formats/ClrMamePro.Writer.cs index 897e4378..1d8ef220 100644 --- a/SabreTools.DatFiles/Formats/ClrMamePro.Writer.cs +++ b/SabreTools.DatFiles/Formats/ClrMamePro.Writer.cs @@ -251,6 +251,7 @@ namespace SabreTools.DatFiles.Formats var samples = new List(); var archives = new List(); var chips = new List(); + var videos = new List(); var dipswitches = new List(); // Loop through and convert the items to respective lists @@ -293,7 +294,7 @@ namespace SabreTools.DatFiles.Formats chips.Add(CreateChip(chip)); break; case Display display: - game.Video = CreateVideo(display); + videos.Add(CreateVideo(display)); break; case Sound sound: game.Sound = CreateSound(sound); @@ -318,6 +319,9 @@ namespace SabreTools.DatFiles.Formats game.Media = medias.ToArray(); game.Sample = samples.ToArray(); game.Archive = archives.ToArray(); + game.Chip = chips.ToArray(); + game.Video = videos.ToArray(); + game.DipSwitch = dipswitches.ToArray(); // Add the game to the list games.Add(game); diff --git a/SabreTools.IO/IOExtensions.cs b/SabreTools.IO/IOExtensions.cs index 4ebd837d..6124c2d5 100644 --- a/SabreTools.IO/IOExtensions.cs +++ b/SabreTools.IO/IOExtensions.cs @@ -59,6 +59,9 @@ namespace SabreTools.IO file.Read(bom, 0, 4); file.Dispose(); + // Disable warning about UTF7 usage + #pragma warning disable SYSLIB0001 + // Analyze the BOM if (bom[0] == 0x2b && bom[1] == 0x2f && bom[2] == 0x76) return Encoding.UTF7; if (bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf) return Encoding.UTF8; @@ -66,6 +69,8 @@ namespace SabreTools.IO if (bom[0] == 0xfe && bom[1] == 0xff) return Encoding.BigEndianUnicode; //UTF-16BE if (bom[0] == 0 && bom[1] == 0 && bom[2] == 0xfe && bom[3] == 0xff) return Encoding.UTF32; return Encoding.Default; + + #pragma warning restore SYSLIB0001 } catch { @@ -78,14 +83,14 @@ namespace SabreTools.IO /// /// Path to get extension from /// Extension, if possible - public static string GetNormalizedExtension(this string path) + public static string? GetNormalizedExtension(this string? path) { // Check null or empty first if (string.IsNullOrWhiteSpace(path)) return null; // Get the extension from the path, if possible - string ext = Path.GetExtension(path)?.ToLowerInvariant(); + string? ext = Path.GetExtension(path)?.ToLowerInvariant(); // Check if the extension is null or empty if (string.IsNullOrWhiteSpace(ext)) @@ -96,13 +101,13 @@ namespace SabreTools.IO return ext; } - + /// /// Get all empty folders within a root folder /// /// Root directory to parse /// IEumerable containing all directories that are empty, an empty enumerable if the root is empty, null otherwise - public static List ListEmpty(this string root) + public static List? ListEmpty(this string? root) { // Check null or empty first if (string.IsNullOrEmpty(root)) diff --git a/SabreTools.IO/ParentablePath.cs b/SabreTools.IO/ParentablePath.cs index 4bb7d328..505e09aa 100644 --- a/SabreTools.IO/ParentablePath.cs +++ b/SabreTools.IO/ParentablePath.cs @@ -11,14 +11,14 @@ namespace SabreTools.IO /// /// Current full path represented /// - public string CurrentPath { get; private set; } + public string CurrentPath { get; init; } /// /// Possible parent path represented (may be null or empty) /// - public string ParentPath { get; private set; } + public string? ParentPath { get; init; } - public ParentablePath(string currentPath, string parentPath = null) + public ParentablePath(string currentPath, string? parentPath = null) { CurrentPath = currentPath; ParentPath = parentPath; @@ -29,7 +29,7 @@ namespace SabreTools.IO /// /// True if path separators should be converted to '-', false otherwise /// Subpath for the file - public string GetNormalizedFileName(bool sanitize) + public string? GetNormalizedFileName(bool sanitize) { // If the current path is empty, we can't do anything if (string.IsNullOrWhiteSpace(CurrentPath)) @@ -55,7 +55,7 @@ namespace SabreTools.IO /// Output directory to use /// True if the output file should go to the same input folder, false otherwise /// Complete output path - public string GetOutputPath(string outDir, bool inplace) + public string? GetOutputPath(string outDir, bool inplace) { // If the current path is empty, we can't do anything if (string.IsNullOrWhiteSpace(CurrentPath)) @@ -73,21 +73,22 @@ namespace SabreTools.IO return Path.GetDirectoryName(CurrentPath); // If the current and parent paths are the same, just use the output directory - if (!splitpath || CurrentPath.Length == ParentPath.Length) + if (!splitpath || CurrentPath.Length == (ParentPath?.Length ?? 0)) return outDir; // By default, the working parent directory is the parent path - string workingParent = ParentPath; + string workingParent = ParentPath ?? string.Empty; // TODO: Should this be the default? Always create a subfolder if a folder is found? // If we are processing a path that is coming from a directory and we are outputting to the current directory, we want to get the subfolder to write to if (outDir == Environment.CurrentDirectory) - workingParent = Path.GetDirectoryName(ParentPath); + workingParent = Path.GetDirectoryName(ParentPath ?? string.Empty) ?? string.Empty; // Determine the correct subfolder based on the working parent directory int extraLength = workingParent.EndsWith(':') || workingParent.EndsWith(Path.DirectorySeparatorChar) || workingParent.EndsWith(Path.AltDirectorySeparatorChar) ? 0 : 1; + return Path.GetDirectoryName(Path.Combine(outDir, CurrentPath.Remove(0, workingParent.Length + extraLength))); } } diff --git a/SabreTools.IO/Readers/ClrMameProReader.cs b/SabreTools.IO/Readers/ClrMameProReader.cs index 5b6f864d..3e22067c 100644 --- a/SabreTools.IO/Readers/ClrMameProReader.cs +++ b/SabreTools.IO/Readers/ClrMameProReader.cs @@ -23,12 +23,12 @@ namespace SabreTools.IO.Readers /// /// Internal stream reader for inputting /// - private readonly StreamReader sr; + private readonly StreamReader? sr; /// /// Contents of the current line, unprocessed /// - public string CurrentLine { get; private set; } = string.Empty; + public string? CurrentLine { get; private set; } = string.Empty; /// /// Get the current line number @@ -49,12 +49,12 @@ namespace SabreTools.IO.Readers /// /// Contents of the currently read line as an internal item /// - public Dictionary Internal { get; private set; } = new Dictionary(); + public Dictionary? Internal { get; private set; } = new Dictionary(); /// /// Current internal item name /// - public string InternalName { get; private set; } = null; + public string? InternalName { get; private set; } /// /// Get if we should be making DosCenter exceptions @@ -85,7 +85,7 @@ namespace SabreTools.IO.Readers /// /// Current top-level being read /// - public string TopLevel { get; private set; } = string.Empty; + public string? TopLevel { get; private set; } = string.Empty; /// /// Constructor for opening a write from a file @@ -108,10 +108,13 @@ namespace SabreTools.IO.Readers /// public bool ReadNextLine() { - if (!(sr.BaseStream?.CanRead ?? false) || sr.EndOfStream) + if (sr?.BaseStream == null) return false; - CurrentLine = sr.ReadLine().Trim(); + if (!sr.BaseStream.CanRead || sr.EndOfStream) + return false; + + CurrentLine = sr.ReadLine()?.Trim(); LineNumber++; ProcessLine(); return true; @@ -122,6 +125,9 @@ namespace SabreTools.IO.Readers /// private void ProcessLine() { + if (CurrentLine == null) + return; + // Standalone (special case for DC dats) if (CurrentLine.StartsWith("Name:")) { @@ -303,7 +309,7 @@ namespace SabreTools.IO.Readers /// public void Dispose() { - sr.Dispose(); + sr?.Dispose(); } } } diff --git a/SabreTools.IO/Readers/IniReader.cs b/SabreTools.IO/Readers/IniReader.cs index 4296fb19..0d1e84ac 100644 --- a/SabreTools.IO/Readers/IniReader.cs +++ b/SabreTools.IO/Readers/IniReader.cs @@ -11,7 +11,7 @@ namespace SabreTools.IO.Readers /// /// Internal stream reader for inputting /// - private readonly StreamReader sr; + private readonly StreamReader? sr; /// /// Get if at end of stream @@ -32,7 +32,7 @@ namespace SabreTools.IO.Readers /// /// Contents of the current line, unprocessed /// - public string CurrentLine { get; private set; } = string.Empty; + public string? CurrentLine { get; private set; } = string.Empty; /// /// Get the current line number @@ -47,7 +47,7 @@ namespace SabreTools.IO.Readers /// /// Current section being read /// - public string Section { get; private set; } = string.Empty; + public string? Section { get; private set; } = string.Empty; /// /// Validate that rows are in key=value format @@ -75,10 +75,13 @@ namespace SabreTools.IO.Readers /// public bool ReadNextLine() { - if (!(sr.BaseStream?.CanRead ?? false) || sr.EndOfStream) + if (sr?.BaseStream == null) return false; - CurrentLine = sr.ReadLine().Trim(); + if (!sr.BaseStream.CanRead || sr.EndOfStream) + return false; + + CurrentLine = sr.ReadLine()?.Trim(); LineNumber++; ProcessLine(); return true; @@ -89,6 +92,9 @@ namespace SabreTools.IO.Readers /// private void ProcessLine() { + if (CurrentLine == null) + return; + // Comment if (CurrentLine.StartsWith(";")) { @@ -142,7 +148,7 @@ namespace SabreTools.IO.Readers /// public void Dispose() { - sr.Dispose(); + sr?.Dispose(); } } } diff --git a/SabreTools.IO/Readers/SeparatedValueReader.cs b/SabreTools.IO/Readers/SeparatedValueReader.cs index 6941b372..86a2d0fa 100644 --- a/SabreTools.IO/Readers/SeparatedValueReader.cs +++ b/SabreTools.IO/Readers/SeparatedValueReader.cs @@ -12,7 +12,7 @@ namespace SabreTools.IO.Readers /// /// Internal stream reader for inputting /// - private readonly StreamReader sr; + private readonly StreamReader? sr; /// /// Internal value to say how many fields should be written @@ -33,7 +33,7 @@ namespace SabreTools.IO.Readers /// /// Contents of the current line, unprocessed /// - public string CurrentLine { get; private set; } = string.Empty; + public string? CurrentLine { get; private set; } = string.Empty; /// /// Get the current line number @@ -48,12 +48,12 @@ namespace SabreTools.IO.Readers /// /// Header row values /// - public List HeaderValues { get; set; } = null; + public List? HeaderValues { get; set; } = null; /// /// Get the current line values /// - public List Line { get; private set; } = null; + public List? Line { get; private set; } = null; /// /// Assume that values are wrapped in quotes @@ -105,13 +105,19 @@ namespace SabreTools.IO.Readers /// public bool ReadNextLine() { - if (!(sr.BaseStream?.CanRead ?? false) || sr.EndOfStream) + if (sr?.BaseStream == null) return false; - string fullLine = sr.ReadLine(); + if (!sr.BaseStream.CanRead || sr.EndOfStream) + return false; + + string? fullLine = sr.ReadLine(); CurrentLine = fullLine; LineNumber++; + if (fullLine == null) + return false; + // If we have quotes, we need to split specially if (Quotes) { @@ -155,17 +161,21 @@ namespace SabreTools.IO.Readers /// /// Get the value for the current line for the current key /// - public string GetValue(string key) + public string? GetValue(string key) { // No header means no key-based indexing if (!Header) throw new ArgumentException("No header expected so no keys can be used"); - // If we don't have the key, return null; + // If we don't have the key, return null + if (HeaderValues == null) + throw new ArgumentException($"Current line doesn't have key {key}"); if (!HeaderValues.Contains(key)) return null; int index = HeaderValues.IndexOf(key); + if (Line == null) + throw new ArgumentException($"Current line doesn't have index {index}"); if (Line.Count < index) throw new ArgumentException($"Current line doesn't have index {index}"); @@ -177,6 +187,8 @@ namespace SabreTools.IO.Readers /// public string GetValue(int index) { + if (Line == null) + throw new ArgumentException($"Current line doesn't have index {index}"); if (Line.Count < index) throw new ArgumentException($"Current line doesn't have index {index}"); @@ -188,7 +200,7 @@ namespace SabreTools.IO.Readers /// public void Dispose() { - sr.Dispose(); + sr?.Dispose(); } } } diff --git a/SabreTools.IO/SabreTools.IO.csproj b/SabreTools.IO/SabreTools.IO.csproj index 741d5731..5a4dc941 100644 --- a/SabreTools.IO/SabreTools.IO.csproj +++ b/SabreTools.IO/SabreTools.IO.csproj @@ -2,6 +2,7 @@ net6.0;net7.0 + enable diff --git a/SabreTools.IO/Writers/ClrMameProWriter.cs b/SabreTools.IO/Writers/ClrMameProWriter.cs index d9324f1a..b0473c20 100644 --- a/SabreTools.IO/Writers/ClrMameProWriter.cs +++ b/SabreTools.IO/Writers/ClrMameProWriter.cs @@ -44,11 +44,8 @@ namespace SabreTools.IO.Writers /// /// Tag information for the stack /// - private struct TagInfo + private record struct TagInfo(string? Name, bool Mixed) { - public string Name; - public bool Mixed; - public void Init() { Name = null; @@ -173,7 +170,7 @@ namespace SabreTools.IO.Writers /// /// Write a complete element with content /// - public void WriteElementString(string name, string value) + public void WriteElementString(string name, string? value) { WriteStartElement(name); WriteString(value); @@ -186,7 +183,7 @@ namespace SabreTools.IO.Writers /// Name of the element /// Value to write in the element /// Indicates if an error should be thrown on a missing required value - public void WriteRequiredElementString(string name, string value, bool throwOnError = false) + public void WriteRequiredElementString(string name, string? value, bool throwOnError = false) { // Throw an exception if we are configured to if (value == null && throwOnError) @@ -200,7 +197,7 @@ namespace SabreTools.IO.Writers /// /// Name of the element /// Value to write in the element - public void WriteOptionalElementString(string name, string value) + public void WriteOptionalElementString(string name, string? value) { if (!string.IsNullOrEmpty(value)) WriteElementString(name, value); @@ -253,7 +250,7 @@ namespace SabreTools.IO.Writers /// Name of the attribute /// Value to write in the attribute /// Non-null to overwrite the writer setting, null otherwise - public void WriteAttributeString(string name, string value, bool? quoteOverride = null) + public void WriteAttributeString(string name, string? value, bool? quoteOverride = null) { WriteStartAttribute(name, quoteOverride); WriteString(value); @@ -267,7 +264,7 @@ namespace SabreTools.IO.Writers /// Value to write in the attribute /// Non-null to overwrite the writer setting, null otherwise /// Indicates if an error should be thrown on a missing required value - public void WriteRequiredAttributeString(string name, string value, bool? quoteOverride = null, bool throwOnError = false) + public void WriteRequiredAttributeString(string name, string? value, bool? quoteOverride = null, bool throwOnError = false) { // Throw an exception if we are configured to if (value == null && throwOnError) @@ -282,7 +279,7 @@ namespace SabreTools.IO.Writers /// Name of the attribute /// Value to write in the attribute /// Non-null to overwrite the writer setting, null otherwise - public void WriteOptionalAttributeString(string name, string value, bool? quoteOverride = null) + public void WriteOptionalAttributeString(string name, string? value, bool? quoteOverride = null) { if (!string.IsNullOrEmpty(value)) WriteAttributeString(name, value, quoteOverride); @@ -294,7 +291,7 @@ namespace SabreTools.IO.Writers /// Name of the attribute /// Value to write in the attribute /// Non-null to overwrite the writer setting, null otherwise - public void WriteStandalone(string name, string value, bool? quoteOverride = null) + public void WriteStandalone(string name, string? value, bool? quoteOverride = null) { try { @@ -306,7 +303,7 @@ namespace SabreTools.IO.Writers || (quoteOverride == true)) { name = name.Replace("\"", "''"); - value = value.Replace("\"", "''"); + value = value?.Replace("\"", "''"); } AutoComplete(Token.Standalone); @@ -338,7 +335,7 @@ namespace SabreTools.IO.Writers /// Value to write in the attribute /// Non-null to overwrite the writer setting, null otherwise /// Indicates if an error should be thrown on a missing required value - public void WriteRequiredStandalone(string name, string value, bool? quoteOverride = null, bool throwOnError = false) + public void WriteRequiredStandalone(string name, string? value, bool? quoteOverride = null, bool throwOnError = false) { // Throw an exception if we are configured to if (value == null && throwOnError) @@ -353,7 +350,7 @@ namespace SabreTools.IO.Writers /// Name of the attribute /// Value to write in the attribute /// Non-null to overwrite the writer setting, null otherwise - public void WriteOptionalStandalone(string name, string value, bool? quoteOverride = null) + public void WriteOptionalStandalone(string name, string? value, bool? quoteOverride = null) { if (!string.IsNullOrEmpty(value)) WriteStandalone(name, value, quoteOverride); @@ -362,7 +359,7 @@ namespace SabreTools.IO.Writers /// /// Write a string content value /// - public void WriteString(string value) + public void WriteString(string? value) { try { diff --git a/SabreTools.IO/Writers/IniWriter.cs b/SabreTools.IO/Writers/IniWriter.cs index 26ad51e8..c36efe63 100644 --- a/SabreTools.IO/Writers/IniWriter.cs +++ b/SabreTools.IO/Writers/IniWriter.cs @@ -9,7 +9,7 @@ namespace SabreTools.IO.Writers /// /// Internal stream writer for outputting /// - private readonly StreamWriter sw; + private readonly StreamWriter? sw; /// /// Constructor for writing to a file @@ -30,8 +30,11 @@ namespace SabreTools.IO.Writers /// /// Write a section tag /// - public void WriteSection(string value) + public void WriteSection(string? value) { + if (sw?.BaseStream == null) + return; + if (string.IsNullOrWhiteSpace(value)) throw new ArgumentException("Section tag cannot be null or empty", nameof(value)); @@ -41,8 +44,11 @@ namespace SabreTools.IO.Writers /// /// Write a key value pair /// - public void WriteKeyValuePair(string key, string value) + public void WriteKeyValuePair(string key, string? value) { + if (sw?.BaseStream == null) + return; + if (string.IsNullOrWhiteSpace(key)) throw new ArgumentException("Key cannot be null or empty", nameof(key)); @@ -53,8 +59,11 @@ namespace SabreTools.IO.Writers /// /// Write a comment /// - public void WriteComment(string value) + public void WriteComment(string? value) { + if (sw?.BaseStream == null) + return; + value ??= string.Empty; sw.WriteLine($";{value}"); } @@ -62,8 +71,11 @@ namespace SabreTools.IO.Writers /// /// Write a generic string /// - public void WriteString(string value) + public void WriteString(string? value) { + if (sw?.BaseStream == null) + return; + value ??= string.Empty; sw.Write(value); } @@ -73,6 +85,9 @@ namespace SabreTools.IO.Writers /// public void WriteLine() { + if (sw?.BaseStream == null) + return; + sw.WriteLine(); } @@ -81,7 +96,7 @@ namespace SabreTools.IO.Writers /// public void Flush() { - sw.Flush(); + sw?.Flush(); } /// @@ -89,7 +104,7 @@ namespace SabreTools.IO.Writers /// public void Dispose() { - sw.Dispose(); + sw?.Dispose(); } } } diff --git a/SabreTools.IO/Writers/SeparatedValueWriter.cs b/SabreTools.IO/Writers/SeparatedValueWriter.cs index 9d3865c7..15e17237 100644 --- a/SabreTools.IO/Writers/SeparatedValueWriter.cs +++ b/SabreTools.IO/Writers/SeparatedValueWriter.cs @@ -60,7 +60,7 @@ namespace SabreTools.IO.Writers /// /// Write a header row /// - public void WriteHeader(string[] headers) + public void WriteHeader(string?[] headers) { // If we haven't written anything out, we can write headers if (!header && !firstRow) @@ -72,7 +72,7 @@ namespace SabreTools.IO.Writers /// /// Write a value row /// - public void WriteValues(object[] values, bool newline = true) + public void WriteValues(object?[] values, bool newline = true) { // If the writer can't be used, we error if (sw == null || !sw.BaseStream.CanWrite) diff --git a/SabreTools.Serialization/AttractMode.Deserializer.cs b/SabreTools.Serialization/AttractMode.Deserializer.cs index ff314467..aa9f4ed6 100644 --- a/SabreTools.Serialization/AttractMode.Deserializer.cs +++ b/SabreTools.Serialization/AttractMode.Deserializer.cs @@ -44,7 +44,7 @@ namespace SabreTools.Serialization var dat = new MetadataFile(); // Read the header values first - if (!reader.ReadHeader()) + if (!reader.ReadHeader() || reader.HeaderValues == null) return null; dat.Header = reader.HeaderValues.ToArray(); @@ -54,11 +54,11 @@ namespace SabreTools.Serialization while (!reader.EndOfStream) { // If we have no next line - if (!reader.ReadNextLine()) + if (!reader.ReadNextLine() || reader.Line == null) break; // Parse the line into a row - Row? row = null; + Row row; if (reader.Line.Count < HeaderWithRomnameCount) { row = new Row diff --git a/SabreTools.Serialization/ClrMamePro.Deserializer.cs b/SabreTools.Serialization/ClrMamePro.Deserializer.cs index 01bcb9a5..9ec0a6e0 100644 --- a/SabreTools.Serialization/ClrMamePro.Deserializer.cs +++ b/SabreTools.Serialization/ClrMamePro.Deserializer.cs @@ -41,7 +41,7 @@ namespace SabreTools.Serialization var dat = new MetadataFile(); // Loop through and parse out the values - string lastTopLevel = reader.TopLevel; + string? lastTopLevel = reader.TopLevel; GameBase? game = null; var games = new List(); @@ -75,27 +75,32 @@ namespace SabreTools.Serialization switch (lastTopLevel) { case "doscenter": - dat.ClrMamePro!.ADDITIONAL_ELEMENTS = headerAdditional.ToArray(); + if (dat.ClrMamePro != null) + dat.ClrMamePro.ADDITIONAL_ELEMENTS = headerAdditional.ToArray(); + headerAdditional.Clear(); break; case "game": case "machine": case "resource": case "set": - game!.Release = releases.ToArray(); - game.BiosSet = biosSets.ToArray(); - game.Rom = roms.ToArray(); - game.Disk = disks.ToArray(); - game.Media = medias.ToArray(); - game.Sample = samples.ToArray(); - game.Archive = archives.ToArray(); - game.Chip = chips.ToArray(); - game.Video = videos.ToArray(); - game.DipSwitch = dipSwitches.ToArray(); - game.ADDITIONAL_ELEMENTS = gameAdditional.ToArray(); + if (game != null) + { + game.Release = releases.ToArray(); + game.BiosSet = biosSets.ToArray(); + game.Rom = roms.ToArray(); + game.Disk = disks.ToArray(); + game.Media = medias.ToArray(); + game.Sample = samples.ToArray(); + game.Archive = archives.ToArray(); + game.Chip = chips.ToArray(); + game.Video = videos.ToArray(); + game.DipSwitch = dipSwitches.ToArray(); + game.ADDITIONAL_ELEMENTS = gameAdditional.ToArray(); - games.Add(game); - game = null; + games.Add(game); + game = null; + } releases.Clear(); biosSets.Clear(); @@ -138,7 +143,8 @@ namespace SabreTools.Serialization game = new Set(); break; default: - additional.Add(reader.CurrentLine); + if (reader.CurrentLine != null) + additional.Add(reader.CurrentLine); break; } } @@ -198,7 +204,8 @@ namespace SabreTools.Serialization dat.ClrMamePro.ForcePacking = reader.Standalone?.Value; break; default: - headerAdditional.Add(reader.CurrentLine); + if (reader.CurrentLine != null) + headerAdditional.Add(reader.CurrentLine); break; } } @@ -255,7 +262,8 @@ namespace SabreTools.Serialization samples.Add(sample); break; default: - gameAdditional.Add(reader.CurrentLine); + if (reader.CurrentLine != null) + gameAdditional.Add(reader.CurrentLine); break; } } @@ -272,53 +280,81 @@ namespace SabreTools.Serialization switch (reader.InternalName) { case "release": - releases.Add(CreateRelease(reader)); + var release = CreateRelease(reader); + if (release != null) + releases.Add(release); break; case "biosset": - biosSets.Add(CreateBiosSet(reader)); + var biosSet = CreateBiosSet(reader); + if (biosSet != null) + biosSets.Add(biosSet); break; case "rom": - roms.Add(CreateRom(reader)); + var rom = CreateRom(reader); + if (rom != null) + roms.Add(rom); break; case "disk": - disks.Add(CreateDisk(reader)); + var disk = CreateDisk(reader); + if (disk != null) + disks.Add(disk); break; case "media": - medias.Add(CreateMedia(reader)); + var media = CreateMedia(reader); + if (media != null) + medias.Add(media); break; case "sample": - samples.Add(CreateSample(reader)); + var sample = CreateSample(reader); + if (sample != null) + samples.Add(sample); break; case "archive": - archives.Add(CreateArchive(reader)); + var archive = CreateArchive(reader); + if (archive != null) + archives.Add(archive); break; case "chip": - chips.Add(CreateChip(reader)); + var chip = CreateChip(reader); + if (chip != null) + chips.Add(chip); break; case "video": - videos.Add(CreateVideo(reader)); + var video = CreateVideo(reader); + if (video != null) + videos.Add(video); break; case "sound": - game.Sound = CreateSound(reader); + var sound = CreateSound(reader); + if (sound != null) + game.Sound = sound; break; case "input": - game.Input = CreateInput(reader); + var input = CreateInput(reader); + if (input != null) + game.Input = input; break; case "dipswitch": - dipSwitches.Add(CreateDipSwitch(reader)); + var dipSwitch = CreateDipSwitch(reader); + if (dipSwitch != null) + dipSwitches.Add(dipSwitch); break; case "driver": - game.Driver = CreateDriver(reader); + var driver = CreateDriver(reader); + if (driver != null) + game.Driver = driver; break; default: - gameAdditional.Add(reader.CurrentLine); + if (reader.CurrentLine != null) + gameAdditional.Add(reader.CurrentLine); continue; } } else { - additional.Add(reader.CurrentLine); + if (reader.CurrentLine != null) + additional.Add(reader.CurrentLine); } } @@ -333,8 +369,11 @@ namespace SabreTools.Serialization /// /// ClrMameProReader representing the metadata file /// Release object created from the reader context - private static Release CreateRelease(ClrMameProReader reader) + private static Release? CreateRelease(ClrMameProReader reader) { + if (reader.Internal == null) + return null; + var itemAdditional = new List(); var release = new Release(); foreach (var kvp in reader.Internal) @@ -371,8 +410,11 @@ namespace SabreTools.Serialization /// /// ClrMameProReader representing the metadata file /// BiosSet object created from the reader context - private static BiosSet CreateBiosSet(ClrMameProReader reader) + private static BiosSet? CreateBiosSet(ClrMameProReader reader) { + if (reader.Internal == null) + return null; + var itemAdditional = new List(); var biosset = new BiosSet(); foreach (var kvp in reader.Internal) @@ -403,8 +445,11 @@ namespace SabreTools.Serialization /// /// ClrMameProReader representing the metadata file /// Rom object created from the reader context - private static Rom CreateRom(ClrMameProReader reader) + private static Rom? CreateRom(ClrMameProReader reader) { + if (reader.Internal == null) + return null; + var itemAdditional = new List(); var rom = new Rom(); foreach (var kvp in reader.Internal) @@ -489,8 +534,11 @@ namespace SabreTools.Serialization /// /// ClrMameProReader representing the metadata file /// Disk object created from the reader context - private static Disk CreateDisk(ClrMameProReader reader) + private static Disk? CreateDisk(ClrMameProReader reader) { + if (reader.Internal == null) + return null; + var itemAdditional = new List(); var disk = new Disk(); foreach (var kvp in reader.Internal) @@ -530,8 +578,11 @@ namespace SabreTools.Serialization /// /// ClrMameProReader representing the metadata file /// Media object created from the reader context - private static Media CreateMedia(ClrMameProReader reader) + private static Media? CreateMedia(ClrMameProReader reader) { + if (reader.Internal == null) + return null; + var itemAdditional = new List(); var media = new Media(); foreach (var kvp in reader.Internal) @@ -568,8 +619,11 @@ namespace SabreTools.Serialization /// /// ClrMameProReader representing the metadata file /// Sample object created from the reader context - private static Sample CreateSample(ClrMameProReader reader) + private static Sample? CreateSample(ClrMameProReader reader) { + if (reader.Internal == null) + return null; + var itemAdditional = new List(); var sample = new Sample(); foreach (var kvp in reader.Internal) @@ -594,8 +648,11 @@ namespace SabreTools.Serialization /// /// ClrMameProReader representing the metadata file /// Archive object created from the reader context - private static Archive CreateArchive(ClrMameProReader reader) + private static Archive? CreateArchive(ClrMameProReader reader) { + if (reader.Internal == null) + return null; + var itemAdditional = new List(); var archive = new Archive(); foreach (var kvp in reader.Internal) @@ -620,8 +677,11 @@ namespace SabreTools.Serialization /// /// ClrMameProReader representing the metadata file /// Chip object created from the reader context - private static Chip CreateChip(ClrMameProReader reader) + private static Chip? CreateChip(ClrMameProReader reader) { + if (reader.Internal == null) + return null; + var itemAdditional = new List(); var chip = new Chip(); foreach (var kvp in reader.Internal) @@ -655,8 +715,11 @@ namespace SabreTools.Serialization /// /// ClrMameProReader representing the metadata file /// Video object created from the reader context - private static Video CreateVideo(ClrMameProReader reader) + private static Video? CreateVideo(ClrMameProReader reader) { + if (reader.Internal == null) + return null; + var itemAdditional = new List(); var video = new Video(); foreach (var kvp in reader.Internal) @@ -699,8 +762,11 @@ namespace SabreTools.Serialization /// /// ClrMameProReader representing the metadata file /// Sound object created from the reader context - private static Sound CreateSound(ClrMameProReader reader) + private static Sound? CreateSound(ClrMameProReader reader) { + if (reader.Internal == null) + return null; + var itemAdditional = new List(); var sound = new Sound(); foreach (var kvp in reader.Internal) @@ -725,8 +791,11 @@ namespace SabreTools.Serialization /// /// ClrMameProReader representing the metadata file /// Input object created from the reader context - private static Input CreateInput(ClrMameProReader reader) + private static Input? CreateInput(ClrMameProReader reader) { + if (reader.Internal == null) + return null; + var itemAdditional = new List(); var input = new Input(); foreach (var kvp in reader.Internal) @@ -766,8 +835,11 @@ namespace SabreTools.Serialization /// /// ClrMameProReader representing the metadata file /// DipSwitch object created from the reader context - private static DipSwitch CreateDipSwitch(ClrMameProReader reader) + private static DipSwitch? CreateDipSwitch(ClrMameProReader reader) { + if (reader.Internal == null) + return null; + var itemAdditional = new List(); var dipswitch = new DipSwitch(); var entries = new List(); @@ -800,8 +872,11 @@ namespace SabreTools.Serialization /// /// ClrMameProReader representing the metadata file /// Driver object created from the reader context - private static Driver CreateDriver(ClrMameProReader reader) + private static Driver? CreateDriver(ClrMameProReader reader) { + if (reader.Internal == null) + return null; + var itemAdditional = new List(); var driver = new Driver(); foreach (var kvp in reader.Internal) @@ -832,7 +907,7 @@ namespace SabreTools.Serialization driver.ADDITIONAL_ELEMENTS = itemAdditional.ToArray(); return driver; } - + #region Internal /// @@ -842,7 +917,7 @@ namespace SabreTools.Serialization { if (item == null) return null; - + var metadataFile = new MetadataFile(); var header = item.Read(Models.Internal.MetadataFile.HeaderKey); diff --git a/SabreTools.Serialization/DosCenter.Deserializer.cs b/SabreTools.Serialization/DosCenter.Deserializer.cs index 0ec52199..a271cf0a 100644 --- a/SabreTools.Serialization/DosCenter.Deserializer.cs +++ b/SabreTools.Serialization/DosCenter.Deserializer.cs @@ -39,7 +39,7 @@ namespace SabreTools.Serialization var dat = new MetadataFile(); // Loop through and parse out the values - string lastTopLevel = reader.TopLevel; + string? lastTopLevel = reader.TopLevel; Game? game = null; var games = new List(); @@ -48,7 +48,6 @@ namespace SabreTools.Serialization var additional = new List(); var headerAdditional = new List(); var gameAdditional = new List(); - var fileAdditional = new List(); while (!reader.EndOfStream) { // If we have no next line @@ -102,7 +101,8 @@ namespace SabreTools.Serialization game = new Game(); break; default: - additional.Add(reader.CurrentLine); + if (reader.CurrentLine != null) + additional.Add(reader.CurrentLine); break; } } @@ -137,7 +137,8 @@ namespace SabreTools.Serialization dat.DosCenter.Comment = reader.Standalone?.Value; break; default: - headerAdditional.Add(item: reader.CurrentLine); + if (reader.CurrentLine != null) + headerAdditional.Add(item: reader.CurrentLine); break; } } @@ -154,7 +155,8 @@ namespace SabreTools.Serialization game.Name = reader.Standalone?.Value; break; default: - gameAdditional.Add(item: reader.CurrentLine); + if (reader.CurrentLine != null) + gameAdditional.Add(item: reader.CurrentLine); break; } } @@ -165,18 +167,21 @@ namespace SabreTools.Serialization // If we have an unknown type, log it if (reader.InternalName != "file") { - gameAdditional.Add(reader.CurrentLine); + if (reader.CurrentLine != null) + gameAdditional.Add(reader.CurrentLine); continue; } // Create the file and add to the list var file = CreateFile(reader); - files.Add(file); + if (file != null) + files.Add(file); } else { - additional.Add(item: reader.CurrentLine); + if (reader.CurrentLine != null) + additional.Add(item: reader.CurrentLine); } } @@ -191,8 +196,11 @@ namespace SabreTools.Serialization /// /// ClrMameProReader representing the metadata file /// File object created from the reader context - private static Models.DosCenter.File CreateFile(ClrMameProReader reader) + private static Models.DosCenter.File? CreateFile(ClrMameProReader reader) { + if (reader.Internal == null) + return null; + var itemAdditional = new List(); var file = new Models.DosCenter.File(); foreach (var kvp in reader.Internal) @@ -212,7 +220,8 @@ namespace SabreTools.Serialization file.Date = kvp.Value; break; default: - itemAdditional.Add(item: reader.CurrentLine); + if (reader.CurrentLine != null) + itemAdditional.Add(item: reader.CurrentLine); break; } } diff --git a/SabreTools.Serialization/DosCenter.Serializer.cs b/SabreTools.Serialization/DosCenter.Serializer.cs index 1c350448..2c4b34d9 100644 --- a/SabreTools.Serialization/DosCenter.Serializer.cs +++ b/SabreTools.Serialization/DosCenter.Serializer.cs @@ -140,7 +140,7 @@ namespace SabreTools.Serialization writer.WriteRequiredAttributeString("name", file.Name, throwOnError: true); writer.WriteRequiredAttributeString("size", file.Size, throwOnError: true); writer.WriteOptionalAttributeString("date", file.Date); - writer.WriteRequiredAttributeString("crc", file.CRC.ToUpperInvariant(), throwOnError: true); + writer.WriteRequiredAttributeString("crc", file.CRC?.ToUpperInvariant(), throwOnError: true); writer.WriteEndElement(); // file } diff --git a/SabreTools.Serialization/EverdriveSMDB.Deserializer.cs b/SabreTools.Serialization/EverdriveSMDB.Deserializer.cs index ded27de8..d52a695c 100644 --- a/SabreTools.Serialization/EverdriveSMDB.Deserializer.cs +++ b/SabreTools.Serialization/EverdriveSMDB.Deserializer.cs @@ -49,7 +49,7 @@ namespace SabreTools.Serialization while (!reader.EndOfStream) { // If we have no next line - if (!reader.ReadNextLine()) + if (!reader.ReadNextLine() || reader.Line == null) break; // Parse the line into a row diff --git a/SabreTools.Serialization/EverdriveSMDB.Serializer.cs b/SabreTools.Serialization/EverdriveSMDB.Serializer.cs index c45f6ec8..3c8e3973 100644 --- a/SabreTools.Serialization/EverdriveSMDB.Serializer.cs +++ b/SabreTools.Serialization/EverdriveSMDB.Serializer.cs @@ -66,7 +66,10 @@ namespace SabreTools.Serialization // Loop through and write out the rows foreach (var row in rows) { - var rowArray = new List + if (row == null) + continue; + + var rowArray = new List { row.SHA256, row.Name, diff --git a/SabreTools.Serialization/Hashfile.Serializer.cs b/SabreTools.Serialization/Hashfile.Serializer.cs index d314d7c1..24c06636 100644 --- a/SabreTools.Serialization/Hashfile.Serializer.cs +++ b/SabreTools.Serialization/Hashfile.Serializer.cs @@ -99,6 +99,11 @@ namespace SabreTools.Serialization // Loop through and write out the items foreach (var sfv in sfvs) { + if (sfv == null) + continue; + if (string.IsNullOrWhiteSpace(sfv.File) || string.IsNullOrWhiteSpace(sfv.Hash)) + continue; + writer.WriteValues(new string[] { sfv.File, sfv.Hash }); writer.Flush(); } @@ -118,6 +123,11 @@ namespace SabreTools.Serialization // Loop through and write out the items foreach (var md5 in md5s) { + if (md5 == null) + continue; + if (string.IsNullOrWhiteSpace(md5.Hash) || string.IsNullOrWhiteSpace(md5.File)) + continue; + writer.WriteValues(new string[] { md5.Hash, md5.File }); writer.Flush(); } @@ -137,6 +147,11 @@ namespace SabreTools.Serialization // Loop through and write out the items foreach (var sha1 in sha1s) { + if (sha1 == null) + continue; + if (string.IsNullOrWhiteSpace(sha1.Hash) || string.IsNullOrWhiteSpace(sha1.File)) + continue; + writer.WriteValues(new string[] { sha1.Hash, sha1.File }); writer.Flush(); } @@ -156,6 +171,11 @@ namespace SabreTools.Serialization // Loop through and write out the items foreach (var sha256 in sha256s) { + if (sha256 == null) + continue; + if (string.IsNullOrWhiteSpace(sha256.Hash) || string.IsNullOrWhiteSpace(sha256.File)) + continue; + writer.WriteValues(new string[] { sha256.Hash, sha256.File }); writer.Flush(); } @@ -175,6 +195,11 @@ namespace SabreTools.Serialization // Loop through and write out the items foreach (var sha384 in sha384s) { + if (sha384 == null) + continue; + if (string.IsNullOrWhiteSpace(sha384.Hash) || string.IsNullOrWhiteSpace(sha384.File)) + continue; + writer.WriteValues(new string[] { sha384.Hash, sha384.File }); writer.Flush(); } @@ -194,6 +219,11 @@ namespace SabreTools.Serialization // Loop through and write out the items foreach (var sha512 in sha512s) { + if (sha512 == null) + continue; + if (string.IsNullOrWhiteSpace(sha512.Hash) || string.IsNullOrWhiteSpace(sha512.File)) + continue; + writer.WriteValues(new string[] { sha512.Hash, sha512.File }); writer.Flush(); } @@ -213,6 +243,11 @@ namespace SabreTools.Serialization // Loop through and write out the items foreach (var spamsum in spamsums) { + if (spamsum == null) + continue; + if (string.IsNullOrWhiteSpace(spamsum.Hash) || string.IsNullOrWhiteSpace(spamsum.File)) + continue; + writer.WriteValues(new string[] { spamsum.Hash, spamsum.File }); writer.Flush(); } diff --git a/SabreTools.Serialization/Listrom.Serializer.cs b/SabreTools.Serialization/Listrom.Serializer.cs index 5a34b62a..4d45a861 100644 --- a/SabreTools.Serialization/Listrom.Serializer.cs +++ b/SabreTools.Serialization/Listrom.Serializer.cs @@ -136,6 +136,9 @@ namespace SabreTools.Serialization foreach (var row in rows) { + if (string.IsNullOrWhiteSpace(row.Name)) + continue; + var rowBuilder = new StringBuilder(); int padding = 40 - (row.Size?.Length ?? 0); diff --git a/SabreTools.Serialization/RomCenter.Deserializer.cs b/SabreTools.Serialization/RomCenter.Deserializer.cs index 6342ca64..bdc9ce62 100644 --- a/SabreTools.Serialization/RomCenter.Deserializer.cs +++ b/SabreTools.Serialization/RomCenter.Deserializer.cs @@ -62,7 +62,7 @@ namespace SabreTools.Serialization case IniRowType.Comment: continue; case IniRowType.SectionHeader: - switch (reader.Section.ToLowerInvariant()) + switch (reader.Section?.ToLowerInvariant()) { case "credits": dat.Credits ??= new Credits(); @@ -77,14 +77,15 @@ namespace SabreTools.Serialization dat.Games ??= new Games(); break; default: - additional.Add(reader.CurrentLine); + if (reader.CurrentLine != null) + additional.Add(reader.CurrentLine); break; } continue; } // If we're in credits - if (reader.Section.ToLowerInvariant() == "credits") + if (reader.Section?.ToLowerInvariant() == "credits") { // Create the section if we haven't already dat.Credits ??= new Credits(); @@ -113,13 +114,14 @@ namespace SabreTools.Serialization dat.Credits.Comment = reader.KeyValuePair?.Value; break; default: - creditsAdditional.Add(reader.CurrentLine); + if (reader.CurrentLine != null) + creditsAdditional.Add(reader.CurrentLine); break; } } // If we're in dat - else if (reader.Section.ToLowerInvariant() == "dat") + else if (reader.Section?.ToLowerInvariant() == "dat") { // Create the section if we haven't already dat.Dat ??= new Dat(); @@ -139,13 +141,14 @@ namespace SabreTools.Serialization dat.Dat.Merge = reader.KeyValuePair?.Value; break; default: - datAdditional.Add(reader.CurrentLine); + if (reader.CurrentLine != null) + datAdditional.Add(reader.CurrentLine); break; } } // If we're in emulator - else if (reader.Section.ToLowerInvariant() == "emulator") + else if (reader.Section?.ToLowerInvariant() == "emulator") { // Create the section if we haven't already dat.Emulator ??= new Emulator(); @@ -159,21 +162,24 @@ namespace SabreTools.Serialization dat.Emulator.Version = reader.KeyValuePair?.Value; break; default: - emulatorAdditional.Add(reader.CurrentLine); + if (reader.CurrentLine != null) + emulatorAdditional.Add(reader.CurrentLine); break; } } // If we're in games - else if (reader.Section.ToLowerInvariant() == "games") + else if (reader.Section?.ToLowerInvariant() == "games") { // Create the section if we haven't already dat.Games ??= new Games(); // If the line doesn't contain the delimiter - if (!reader.CurrentLine.Contains('¬')) + if (!(reader.CurrentLine?.Contains('¬') ?? false)) { - gamesAdditional.Add(reader.CurrentLine); + if (reader.CurrentLine != null) + gamesAdditional.Add(reader.CurrentLine); + continue; } @@ -202,22 +208,23 @@ namespace SabreTools.Serialization else { - additional.Add(item: reader.CurrentLine); + if (reader.CurrentLine != null) + additional.Add(reader.CurrentLine); } } // Add extra pieces and return - dat.ADDITIONAL_ELEMENTS = additional.ToArray(); + dat.ADDITIONAL_ELEMENTS = additional.Where(s => s != null).ToArray(); if (dat.Credits != null) - dat.Credits.ADDITIONAL_ELEMENTS = creditsAdditional.ToArray(); + dat.Credits.ADDITIONAL_ELEMENTS = creditsAdditional.Where(s => s != null).ToArray(); if (dat.Dat != null) - dat.Dat.ADDITIONAL_ELEMENTS = datAdditional.ToArray(); + dat.Dat.ADDITIONAL_ELEMENTS = datAdditional.Where(s => s != null).ToArray(); if (dat.Emulator != null) - dat.Emulator.ADDITIONAL_ELEMENTS = emulatorAdditional.ToArray(); + dat.Emulator.ADDITIONAL_ELEMENTS = emulatorAdditional.Where(s => s != null).ToArray(); if (dat.Games != null) { dat.Games.Rom = roms.ToArray(); - dat.Games.ADDITIONAL_ELEMENTS = gamesAdditional.ToArray(); + dat.Games.ADDITIONAL_ELEMENTS = gamesAdditional.Where(s => s != null).Select(s => s).ToArray(); } return dat; } diff --git a/SabreTools.Serialization/SeparatedValue.Deserializer.cs b/SabreTools.Serialization/SeparatedValue.Deserializer.cs index 94fe1b88..70e8638f 100644 --- a/SabreTools.Serialization/SeparatedValue.Deserializer.cs +++ b/SabreTools.Serialization/SeparatedValue.Deserializer.cs @@ -46,7 +46,7 @@ namespace SabreTools.Serialization var dat = new MetadataFile(); // Read the header values first - if (!reader.ReadHeader()) + if (!reader.ReadHeader() || reader.HeaderValues == null) return null; dat.Header = reader.HeaderValues.ToArray(); @@ -56,7 +56,7 @@ namespace SabreTools.Serialization while (!reader.EndOfStream) { // If we have no next line - if (!reader.ReadNextLine()) + if (!reader.ReadNextLine() || reader.Line == null) break; // Parse the line into a row diff --git a/SabreTools.Test/Serialization/DeserializationTests.cs b/SabreTools.Test/Serialization/DeserializationTests.cs index d0823f47..491184a5 100644 --- a/SabreTools.Test/Serialization/DeserializationTests.cs +++ b/SabreTools.Test/Serialization/DeserializationTests.cs @@ -127,9 +127,9 @@ namespace SabreTools.Test.Parser Assert.Empty(chip.ADDITIONAL_ELEMENTS); } - if (game.Video != null) + foreach (var video in game.Video ?? Array.Empty()) { - Assert.Empty(game.Video.ADDITIONAL_ELEMENTS); + Assert.Empty(video.ADDITIONAL_ELEMENTS); } if (game.Sound != null)