Add nullable context to SabreTools.IO

This commit is contained in:
Matt Nadareski
2023-08-10 15:02:40 -04:00
parent fb81fd0243
commit 7bb0ba245d
22 changed files with 356 additions and 174 deletions

View File

@@ -129,7 +129,7 @@ namespace SabreTools.DatFiles.Formats
ConvertMedia(game.Media, machine, filename, indexId, statsOnly, ref containsItems); ConvertMedia(game.Media, machine, filename, indexId, statsOnly, ref containsItems);
ConvertArchives(game.Archive, machine, filename, indexId, statsOnly, ref containsItems); ConvertArchives(game.Archive, machine, filename, indexId, statsOnly, ref containsItems);
ConvertChips(game.Chip, 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); ConvertSound(game.Sound, machine, filename, indexId, statsOnly, ref containsItems);
ConvertInput(game.Input, machine, filename, indexId, statsOnly, ref containsItems); ConvertInput(game.Input, machine, filename, indexId, statsOnly, ref containsItems);
ConvertDipSwitches(game.DipSwitch, machine, filename, indexId, statsOnly, ref containsItems); ConvertDipSwitches(game.DipSwitch, machine, filename, indexId, statsOnly, ref containsItems);
@@ -431,47 +431,50 @@ namespace SabreTools.DatFiles.Formats
/// <summary> /// <summary>
/// Convert Video information /// Convert Video information
/// </summary> /// </summary>
/// <param name="video">Deserialized model to convert</param> /// <param name="video">Array of deserialized models to convert</param>
/// <param name="machine">Prefilled machine to use</param> /// <param name="machine">Prefilled machine to use</param>
/// <param name="filename">Name of the file to be parsed</param> /// <param name="filename">Name of the file to be parsed</param>
/// <param name="indexId">Index ID for the DAT</param> /// <param name="indexId">Index ID for the DAT</param>
/// <param name="statsOnly">True to only add item statistics while parsing, false otherwise</param> /// <param name="statsOnly">True to only add item statistics while parsing, false otherwise</param>
/// <param name="containsItems">True if there were any items in the array, false otherwise</param> /// <param name="containsItems">True if there were any items in the array, false otherwise</param>
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 the video array is missing, we can't do anything
if (video == null) if (videos == null || !videos.Any())
return; return;
containsItems = true; containsItems = true;
var item = new Display foreach (var video in videos)
{ {
DisplayType = video.Screen?.AsDisplayType() ?? DisplayType.NULL, var item = new Display
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
{ {
Index = indexId, DisplayType = video.Screen?.AsDisplayType() ?? DisplayType.NULL,
Name = filename, 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) Source = new Source
{ {
case "horizontal": Index = indexId,
item.Rotate = 0; Name = filename,
break; },
case "vertical": };
item.Rotate = 90;
break; 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);
} }
/// <summary> /// <summary>

View File

@@ -251,6 +251,7 @@ namespace SabreTools.DatFiles.Formats
var samples = new List<Models.ClrMamePro.Sample>(); var samples = new List<Models.ClrMamePro.Sample>();
var archives = new List<Models.ClrMamePro.Archive>(); var archives = new List<Models.ClrMamePro.Archive>();
var chips = new List<Models.ClrMamePro.Chip>(); var chips = new List<Models.ClrMamePro.Chip>();
var videos = new List<Models.ClrMamePro.Video>();
var dipswitches = new List<Models.ClrMamePro.DipSwitch>(); var dipswitches = new List<Models.ClrMamePro.DipSwitch>();
// Loop through and convert the items to respective lists // Loop through and convert the items to respective lists
@@ -293,7 +294,7 @@ namespace SabreTools.DatFiles.Formats
chips.Add(CreateChip(chip)); chips.Add(CreateChip(chip));
break; break;
case Display display: case Display display:
game.Video = CreateVideo(display); videos.Add(CreateVideo(display));
break; break;
case Sound sound: case Sound sound:
game.Sound = CreateSound(sound); game.Sound = CreateSound(sound);
@@ -318,6 +319,9 @@ namespace SabreTools.DatFiles.Formats
game.Media = medias.ToArray(); game.Media = medias.ToArray();
game.Sample = samples.ToArray(); game.Sample = samples.ToArray();
game.Archive = archives.ToArray(); game.Archive = archives.ToArray();
game.Chip = chips.ToArray();
game.Video = videos.ToArray();
game.DipSwitch = dipswitches.ToArray();
// Add the game to the list // Add the game to the list
games.Add(game); games.Add(game);

View File

@@ -59,6 +59,9 @@ namespace SabreTools.IO
file.Read(bom, 0, 4); file.Read(bom, 0, 4);
file.Dispose(); file.Dispose();
// Disable warning about UTF7 usage
#pragma warning disable SYSLIB0001
// Analyze the BOM // Analyze the BOM
if (bom[0] == 0x2b && bom[1] == 0x2f && bom[2] == 0x76) return Encoding.UTF7; 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; 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] == 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; if (bom[0] == 0 && bom[1] == 0 && bom[2] == 0xfe && bom[3] == 0xff) return Encoding.UTF32;
return Encoding.Default; return Encoding.Default;
#pragma warning restore SYSLIB0001
} }
catch catch
{ {
@@ -78,14 +83,14 @@ namespace SabreTools.IO
/// </summary> /// </summary>
/// <param name="path">Path to get extension from</param> /// <param name="path">Path to get extension from</param>
/// <returns>Extension, if possible</returns> /// <returns>Extension, if possible</returns>
public static string GetNormalizedExtension(this string path) public static string? GetNormalizedExtension(this string? path)
{ {
// Check null or empty first // Check null or empty first
if (string.IsNullOrWhiteSpace(path)) if (string.IsNullOrWhiteSpace(path))
return null; return null;
// Get the extension from the path, if possible // 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 // Check if the extension is null or empty
if (string.IsNullOrWhiteSpace(ext)) if (string.IsNullOrWhiteSpace(ext))
@@ -96,13 +101,13 @@ namespace SabreTools.IO
return ext; return ext;
} }
/// <summary> /// <summary>
/// Get all empty folders within a root folder /// Get all empty folders within a root folder
/// </summary> /// </summary>
/// <param name="root">Root directory to parse</param> /// <param name="root">Root directory to parse</param>
/// <returns>IEumerable containing all directories that are empty, an empty enumerable if the root is empty, null otherwise</returns> /// <returns>IEumerable containing all directories that are empty, an empty enumerable if the root is empty, null otherwise</returns>
public static List<string> ListEmpty(this string root) public static List<string>? ListEmpty(this string? root)
{ {
// Check null or empty first // Check null or empty first
if (string.IsNullOrEmpty(root)) if (string.IsNullOrEmpty(root))

View File

@@ -11,14 +11,14 @@ namespace SabreTools.IO
/// <summary> /// <summary>
/// Current full path represented /// Current full path represented
/// </summary> /// </summary>
public string CurrentPath { get; private set; } public string CurrentPath { get; init; }
/// <summary> /// <summary>
/// Possible parent path represented (may be null or empty) /// Possible parent path represented (may be null or empty)
/// </summary> /// </summary>
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; CurrentPath = currentPath;
ParentPath = parentPath; ParentPath = parentPath;
@@ -29,7 +29,7 @@ namespace SabreTools.IO
/// </summary> /// </summary>
/// <param name="sanitize">True if path separators should be converted to '-', false otherwise</param> /// <param name="sanitize">True if path separators should be converted to '-', false otherwise</param>
/// <returns>Subpath for the file</returns> /// <returns>Subpath for the file</returns>
public string GetNormalizedFileName(bool sanitize) public string? GetNormalizedFileName(bool sanitize)
{ {
// If the current path is empty, we can't do anything // If the current path is empty, we can't do anything
if (string.IsNullOrWhiteSpace(CurrentPath)) if (string.IsNullOrWhiteSpace(CurrentPath))
@@ -55,7 +55,7 @@ namespace SabreTools.IO
/// <param name="outDir">Output directory to use</param> /// <param name="outDir">Output directory to use</param>
/// <param name="inplace">True if the output file should go to the same input folder, false otherwise</param> /// <param name="inplace">True if the output file should go to the same input folder, false otherwise</param>
/// <returns>Complete output path</returns> /// <returns>Complete output path</returns>
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 the current path is empty, we can't do anything
if (string.IsNullOrWhiteSpace(CurrentPath)) if (string.IsNullOrWhiteSpace(CurrentPath))
@@ -73,21 +73,22 @@ namespace SabreTools.IO
return Path.GetDirectoryName(CurrentPath); return Path.GetDirectoryName(CurrentPath);
// If the current and parent paths are the same, just use the output directory // 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; return outDir;
// By default, the working parent directory is the parent path // 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? // 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 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) 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 // Determine the correct subfolder based on the working parent directory
int extraLength = workingParent.EndsWith(':') int extraLength = workingParent.EndsWith(':')
|| workingParent.EndsWith(Path.DirectorySeparatorChar) || workingParent.EndsWith(Path.DirectorySeparatorChar)
|| workingParent.EndsWith(Path.AltDirectorySeparatorChar) ? 0 : 1; || workingParent.EndsWith(Path.AltDirectorySeparatorChar) ? 0 : 1;
return Path.GetDirectoryName(Path.Combine(outDir, CurrentPath.Remove(0, workingParent.Length + extraLength))); return Path.GetDirectoryName(Path.Combine(outDir, CurrentPath.Remove(0, workingParent.Length + extraLength)));
} }
} }

View File

@@ -23,12 +23,12 @@ namespace SabreTools.IO.Readers
/// <summary> /// <summary>
/// Internal stream reader for inputting /// Internal stream reader for inputting
/// </summary> /// </summary>
private readonly StreamReader sr; private readonly StreamReader? sr;
/// <summary> /// <summary>
/// Contents of the current line, unprocessed /// Contents of the current line, unprocessed
/// </summary> /// </summary>
public string CurrentLine { get; private set; } = string.Empty; public string? CurrentLine { get; private set; } = string.Empty;
/// <summary> /// <summary>
/// Get the current line number /// Get the current line number
@@ -49,12 +49,12 @@ namespace SabreTools.IO.Readers
/// <summary> /// <summary>
/// Contents of the currently read line as an internal item /// Contents of the currently read line as an internal item
/// </summary> /// </summary>
public Dictionary<string, string> Internal { get; private set; } = new Dictionary<string, string>(); public Dictionary<string, string>? Internal { get; private set; } = new Dictionary<string, string>();
/// <summary> /// <summary>
/// Current internal item name /// Current internal item name
/// </summary> /// </summary>
public string InternalName { get; private set; } = null; public string? InternalName { get; private set; }
/// <summary> /// <summary>
/// Get if we should be making DosCenter exceptions /// Get if we should be making DosCenter exceptions
@@ -85,7 +85,7 @@ namespace SabreTools.IO.Readers
/// <summary> /// <summary>
/// Current top-level being read /// Current top-level being read
/// </summary> /// </summary>
public string TopLevel { get; private set; } = string.Empty; public string? TopLevel { get; private set; } = string.Empty;
/// <summary> /// <summary>
/// Constructor for opening a write from a file /// Constructor for opening a write from a file
@@ -108,10 +108,13 @@ namespace SabreTools.IO.Readers
/// </summary> /// </summary>
public bool ReadNextLine() public bool ReadNextLine()
{ {
if (!(sr.BaseStream?.CanRead ?? false) || sr.EndOfStream) if (sr?.BaseStream == null)
return false; return false;
CurrentLine = sr.ReadLine().Trim(); if (!sr.BaseStream.CanRead || sr.EndOfStream)
return false;
CurrentLine = sr.ReadLine()?.Trim();
LineNumber++; LineNumber++;
ProcessLine(); ProcessLine();
return true; return true;
@@ -122,6 +125,9 @@ namespace SabreTools.IO.Readers
/// </summary> /// </summary>
private void ProcessLine() private void ProcessLine()
{ {
if (CurrentLine == null)
return;
// Standalone (special case for DC dats) // Standalone (special case for DC dats)
if (CurrentLine.StartsWith("Name:")) if (CurrentLine.StartsWith("Name:"))
{ {
@@ -303,7 +309,7 @@ namespace SabreTools.IO.Readers
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
sr.Dispose(); sr?.Dispose();
} }
} }
} }

View File

@@ -11,7 +11,7 @@ namespace SabreTools.IO.Readers
/// <summary> /// <summary>
/// Internal stream reader for inputting /// Internal stream reader for inputting
/// </summary> /// </summary>
private readonly StreamReader sr; private readonly StreamReader? sr;
/// <summary> /// <summary>
/// Get if at end of stream /// Get if at end of stream
@@ -32,7 +32,7 @@ namespace SabreTools.IO.Readers
/// <summary> /// <summary>
/// Contents of the current line, unprocessed /// Contents of the current line, unprocessed
/// </summary> /// </summary>
public string CurrentLine { get; private set; } = string.Empty; public string? CurrentLine { get; private set; } = string.Empty;
/// <summary> /// <summary>
/// Get the current line number /// Get the current line number
@@ -47,7 +47,7 @@ namespace SabreTools.IO.Readers
/// <summary> /// <summary>
/// Current section being read /// Current section being read
/// </summary> /// </summary>
public string Section { get; private set; } = string.Empty; public string? Section { get; private set; } = string.Empty;
/// <summary> /// <summary>
/// Validate that rows are in key=value format /// Validate that rows are in key=value format
@@ -75,10 +75,13 @@ namespace SabreTools.IO.Readers
/// </summary> /// </summary>
public bool ReadNextLine() public bool ReadNextLine()
{ {
if (!(sr.BaseStream?.CanRead ?? false) || sr.EndOfStream) if (sr?.BaseStream == null)
return false; return false;
CurrentLine = sr.ReadLine().Trim(); if (!sr.BaseStream.CanRead || sr.EndOfStream)
return false;
CurrentLine = sr.ReadLine()?.Trim();
LineNumber++; LineNumber++;
ProcessLine(); ProcessLine();
return true; return true;
@@ -89,6 +92,9 @@ namespace SabreTools.IO.Readers
/// </summary> /// </summary>
private void ProcessLine() private void ProcessLine()
{ {
if (CurrentLine == null)
return;
// Comment // Comment
if (CurrentLine.StartsWith(";")) if (CurrentLine.StartsWith(";"))
{ {
@@ -142,7 +148,7 @@ namespace SabreTools.IO.Readers
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
sr.Dispose(); sr?.Dispose();
} }
} }
} }

View File

@@ -12,7 +12,7 @@ namespace SabreTools.IO.Readers
/// <summary> /// <summary>
/// Internal stream reader for inputting /// Internal stream reader for inputting
/// </summary> /// </summary>
private readonly StreamReader sr; private readonly StreamReader? sr;
/// <summary> /// <summary>
/// Internal value to say how many fields should be written /// Internal value to say how many fields should be written
@@ -33,7 +33,7 @@ namespace SabreTools.IO.Readers
/// <summary> /// <summary>
/// Contents of the current line, unprocessed /// Contents of the current line, unprocessed
/// </summary> /// </summary>
public string CurrentLine { get; private set; } = string.Empty; public string? CurrentLine { get; private set; } = string.Empty;
/// <summary> /// <summary>
/// Get the current line number /// Get the current line number
@@ -48,12 +48,12 @@ namespace SabreTools.IO.Readers
/// <summary> /// <summary>
/// Header row values /// Header row values
/// </summary> /// </summary>
public List<string> HeaderValues { get; set; } = null; public List<string>? HeaderValues { get; set; } = null;
/// <summary> /// <summary>
/// Get the current line values /// Get the current line values
/// </summary> /// </summary>
public List<string> Line { get; private set; } = null; public List<string>? Line { get; private set; } = null;
/// <summary> /// <summary>
/// Assume that values are wrapped in quotes /// Assume that values are wrapped in quotes
@@ -105,13 +105,19 @@ namespace SabreTools.IO.Readers
/// </summary> /// </summary>
public bool ReadNextLine() public bool ReadNextLine()
{ {
if (!(sr.BaseStream?.CanRead ?? false) || sr.EndOfStream) if (sr?.BaseStream == null)
return false; return false;
string fullLine = sr.ReadLine(); if (!sr.BaseStream.CanRead || sr.EndOfStream)
return false;
string? fullLine = sr.ReadLine();
CurrentLine = fullLine; CurrentLine = fullLine;
LineNumber++; LineNumber++;
if (fullLine == null)
return false;
// If we have quotes, we need to split specially // If we have quotes, we need to split specially
if (Quotes) if (Quotes)
{ {
@@ -155,17 +161,21 @@ namespace SabreTools.IO.Readers
/// <summary> /// <summary>
/// Get the value for the current line for the current key /// Get the value for the current line for the current key
/// </summary> /// </summary>
public string GetValue(string key) public string? GetValue(string key)
{ {
// No header means no key-based indexing // No header means no key-based indexing
if (!Header) if (!Header)
throw new ArgumentException("No header expected so no keys can be used"); 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)) if (!HeaderValues.Contains(key))
return null; return null;
int index = HeaderValues.IndexOf(key); int index = HeaderValues.IndexOf(key);
if (Line == null)
throw new ArgumentException($"Current line doesn't have index {index}");
if (Line.Count < index) if (Line.Count < index)
throw new ArgumentException($"Current line doesn't have index {index}"); throw new ArgumentException($"Current line doesn't have index {index}");
@@ -177,6 +187,8 @@ namespace SabreTools.IO.Readers
/// </summary> /// </summary>
public string GetValue(int index) public string GetValue(int index)
{ {
if (Line == null)
throw new ArgumentException($"Current line doesn't have index {index}");
if (Line.Count < index) if (Line.Count < index)
throw new ArgumentException($"Current line doesn't have index {index}"); throw new ArgumentException($"Current line doesn't have index {index}");
@@ -188,7 +200,7 @@ namespace SabreTools.IO.Readers
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
sr.Dispose(); sr?.Dispose();
} }
} }
} }

View File

@@ -2,6 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks> <TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@@ -44,11 +44,8 @@ namespace SabreTools.IO.Writers
/// <summary> /// <summary>
/// Tag information for the stack /// Tag information for the stack
/// </summary> /// </summary>
private struct TagInfo private record struct TagInfo(string? Name, bool Mixed)
{ {
public string Name;
public bool Mixed;
public void Init() public void Init()
{ {
Name = null; Name = null;
@@ -173,7 +170,7 @@ namespace SabreTools.IO.Writers
/// <summary> /// <summary>
/// Write a complete element with content /// Write a complete element with content
/// </summary> /// </summary>
public void WriteElementString(string name, string value) public void WriteElementString(string name, string? value)
{ {
WriteStartElement(name); WriteStartElement(name);
WriteString(value); WriteString(value);
@@ -186,7 +183,7 @@ namespace SabreTools.IO.Writers
/// <param name="name">Name of the element</param> /// <param name="name">Name of the element</param>
/// <param name="value">Value to write in the element</param> /// <param name="value">Value to write in the element</param>
/// <param name="throwOnError">Indicates if an error should be thrown on a missing required value</param> /// <param name="throwOnError">Indicates if an error should be thrown on a missing required value</param>
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 // Throw an exception if we are configured to
if (value == null && throwOnError) if (value == null && throwOnError)
@@ -200,7 +197,7 @@ namespace SabreTools.IO.Writers
/// </summary> /// </summary>
/// <param name="name">Name of the element</param> /// <param name="name">Name of the element</param>
/// <param name="value">Value to write in the element</param> /// <param name="value">Value to write in the element</param>
public void WriteOptionalElementString(string name, string value) public void WriteOptionalElementString(string name, string? value)
{ {
if (!string.IsNullOrEmpty(value)) if (!string.IsNullOrEmpty(value))
WriteElementString(name, value); WriteElementString(name, value);
@@ -253,7 +250,7 @@ namespace SabreTools.IO.Writers
/// <param name="name">Name of the attribute</param> /// <param name="name">Name of the attribute</param>
/// <param name="value">Value to write in the attribute</param> /// <param name="value">Value to write in the attribute</param>
/// <param name="quoteOverride">Non-null to overwrite the writer setting, null otherwise</param> /// <param name="quoteOverride">Non-null to overwrite the writer setting, null otherwise</param>
public void WriteAttributeString(string name, string value, bool? quoteOverride = null) public void WriteAttributeString(string name, string? value, bool? quoteOverride = null)
{ {
WriteStartAttribute(name, quoteOverride); WriteStartAttribute(name, quoteOverride);
WriteString(value); WriteString(value);
@@ -267,7 +264,7 @@ namespace SabreTools.IO.Writers
/// <param name="value">Value to write in the attribute</param> /// <param name="value">Value to write in the attribute</param>
/// <param name="quoteOverride">Non-null to overwrite the writer setting, null otherwise</param> /// <param name="quoteOverride">Non-null to overwrite the writer setting, null otherwise</param>
/// <param name="throwOnError">Indicates if an error should be thrown on a missing required value</param> /// <param name="throwOnError">Indicates if an error should be thrown on a missing required value</param>
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 // Throw an exception if we are configured to
if (value == null && throwOnError) if (value == null && throwOnError)
@@ -282,7 +279,7 @@ namespace SabreTools.IO.Writers
/// <param name="name">Name of the attribute</param> /// <param name="name">Name of the attribute</param>
/// <param name="value">Value to write in the attribute</param> /// <param name="value">Value to write in the attribute</param>
/// <param name="quoteOverride">Non-null to overwrite the writer setting, null otherwise</param> /// <param name="quoteOverride">Non-null to overwrite the writer setting, null otherwise</param>
public void WriteOptionalAttributeString(string name, string value, bool? quoteOverride = null) public void WriteOptionalAttributeString(string name, string? value, bool? quoteOverride = null)
{ {
if (!string.IsNullOrEmpty(value)) if (!string.IsNullOrEmpty(value))
WriteAttributeString(name, value, quoteOverride); WriteAttributeString(name, value, quoteOverride);
@@ -294,7 +291,7 @@ namespace SabreTools.IO.Writers
/// <param name="name">Name of the attribute</param> /// <param name="name">Name of the attribute</param>
/// <param name="value">Value to write in the attribute</param> /// <param name="value">Value to write in the attribute</param>
/// <param name="quoteOverride">Non-null to overwrite the writer setting, null otherwise</param> /// <param name="quoteOverride">Non-null to overwrite the writer setting, null otherwise</param>
public void WriteStandalone(string name, string value, bool? quoteOverride = null) public void WriteStandalone(string name, string? value, bool? quoteOverride = null)
{ {
try try
{ {
@@ -306,7 +303,7 @@ namespace SabreTools.IO.Writers
|| (quoteOverride == true)) || (quoteOverride == true))
{ {
name = name.Replace("\"", "''"); name = name.Replace("\"", "''");
value = value.Replace("\"", "''"); value = value?.Replace("\"", "''");
} }
AutoComplete(Token.Standalone); AutoComplete(Token.Standalone);
@@ -338,7 +335,7 @@ namespace SabreTools.IO.Writers
/// <param name="value">Value to write in the attribute</param> /// <param name="value">Value to write in the attribute</param>
/// <param name="quoteOverride">Non-null to overwrite the writer setting, null otherwise</param> /// <param name="quoteOverride">Non-null to overwrite the writer setting, null otherwise</param>
/// <param name="throwOnError">Indicates if an error should be thrown on a missing required value</param> /// <param name="throwOnError">Indicates if an error should be thrown on a missing required value</param>
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 // Throw an exception if we are configured to
if (value == null && throwOnError) if (value == null && throwOnError)
@@ -353,7 +350,7 @@ namespace SabreTools.IO.Writers
/// <param name="name">Name of the attribute</param> /// <param name="name">Name of the attribute</param>
/// <param name="value">Value to write in the attribute</param> /// <param name="value">Value to write in the attribute</param>
/// <param name="quoteOverride">Non-null to overwrite the writer setting, null otherwise</param> /// <param name="quoteOverride">Non-null to overwrite the writer setting, null otherwise</param>
public void WriteOptionalStandalone(string name, string value, bool? quoteOverride = null) public void WriteOptionalStandalone(string name, string? value, bool? quoteOverride = null)
{ {
if (!string.IsNullOrEmpty(value)) if (!string.IsNullOrEmpty(value))
WriteStandalone(name, value, quoteOverride); WriteStandalone(name, value, quoteOverride);
@@ -362,7 +359,7 @@ namespace SabreTools.IO.Writers
/// <summary> /// <summary>
/// Write a string content value /// Write a string content value
/// </summary> /// </summary>
public void WriteString(string value) public void WriteString(string? value)
{ {
try try
{ {

View File

@@ -9,7 +9,7 @@ namespace SabreTools.IO.Writers
/// <summary> /// <summary>
/// Internal stream writer for outputting /// Internal stream writer for outputting
/// </summary> /// </summary>
private readonly StreamWriter sw; private readonly StreamWriter? sw;
/// <summary> /// <summary>
/// Constructor for writing to a file /// Constructor for writing to a file
@@ -30,8 +30,11 @@ namespace SabreTools.IO.Writers
/// <summary> /// <summary>
/// Write a section tag /// Write a section tag
/// </summary> /// </summary>
public void WriteSection(string value) public void WriteSection(string? value)
{ {
if (sw?.BaseStream == null)
return;
if (string.IsNullOrWhiteSpace(value)) if (string.IsNullOrWhiteSpace(value))
throw new ArgumentException("Section tag cannot be null or empty", nameof(value)); throw new ArgumentException("Section tag cannot be null or empty", nameof(value));
@@ -41,8 +44,11 @@ namespace SabreTools.IO.Writers
/// <summary> /// <summary>
/// Write a key value pair /// Write a key value pair
/// </summary> /// </summary>
public void WriteKeyValuePair(string key, string value) public void WriteKeyValuePair(string key, string? value)
{ {
if (sw?.BaseStream == null)
return;
if (string.IsNullOrWhiteSpace(key)) if (string.IsNullOrWhiteSpace(key))
throw new ArgumentException("Key cannot be null or empty", nameof(key)); throw new ArgumentException("Key cannot be null or empty", nameof(key));
@@ -53,8 +59,11 @@ namespace SabreTools.IO.Writers
/// <summary> /// <summary>
/// Write a comment /// Write a comment
/// </summary> /// </summary>
public void WriteComment(string value) public void WriteComment(string? value)
{ {
if (sw?.BaseStream == null)
return;
value ??= string.Empty; value ??= string.Empty;
sw.WriteLine($";{value}"); sw.WriteLine($";{value}");
} }
@@ -62,8 +71,11 @@ namespace SabreTools.IO.Writers
/// <summary> /// <summary>
/// Write a generic string /// Write a generic string
/// </summary> /// </summary>
public void WriteString(string value) public void WriteString(string? value)
{ {
if (sw?.BaseStream == null)
return;
value ??= string.Empty; value ??= string.Empty;
sw.Write(value); sw.Write(value);
} }
@@ -73,6 +85,9 @@ namespace SabreTools.IO.Writers
/// </summary> /// </summary>
public void WriteLine() public void WriteLine()
{ {
if (sw?.BaseStream == null)
return;
sw.WriteLine(); sw.WriteLine();
} }
@@ -81,7 +96,7 @@ namespace SabreTools.IO.Writers
/// </summary> /// </summary>
public void Flush() public void Flush()
{ {
sw.Flush(); sw?.Flush();
} }
/// <summary> /// <summary>
@@ -89,7 +104,7 @@ namespace SabreTools.IO.Writers
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
sw.Dispose(); sw?.Dispose();
} }
} }
} }

View File

@@ -60,7 +60,7 @@ namespace SabreTools.IO.Writers
/// <summary> /// <summary>
/// Write a header row /// Write a header row
/// </summary> /// </summary>
public void WriteHeader(string[] headers) public void WriteHeader(string?[] headers)
{ {
// If we haven't written anything out, we can write headers // If we haven't written anything out, we can write headers
if (!header && !firstRow) if (!header && !firstRow)
@@ -72,7 +72,7 @@ namespace SabreTools.IO.Writers
/// <summary> /// <summary>
/// Write a value row /// Write a value row
/// </summary> /// </summary>
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 the writer can't be used, we error
if (sw == null || !sw.BaseStream.CanWrite) if (sw == null || !sw.BaseStream.CanWrite)

View File

@@ -44,7 +44,7 @@ namespace SabreTools.Serialization
var dat = new MetadataFile(); var dat = new MetadataFile();
// Read the header values first // Read the header values first
if (!reader.ReadHeader()) if (!reader.ReadHeader() || reader.HeaderValues == null)
return null; return null;
dat.Header = reader.HeaderValues.ToArray(); dat.Header = reader.HeaderValues.ToArray();
@@ -54,11 +54,11 @@ namespace SabreTools.Serialization
while (!reader.EndOfStream) while (!reader.EndOfStream)
{ {
// If we have no next line // If we have no next line
if (!reader.ReadNextLine()) if (!reader.ReadNextLine() || reader.Line == null)
break; break;
// Parse the line into a row // Parse the line into a row
Row? row = null; Row row;
if (reader.Line.Count < HeaderWithRomnameCount) if (reader.Line.Count < HeaderWithRomnameCount)
{ {
row = new Row row = new Row

View File

@@ -41,7 +41,7 @@ namespace SabreTools.Serialization
var dat = new MetadataFile(); var dat = new MetadataFile();
// Loop through and parse out the values // Loop through and parse out the values
string lastTopLevel = reader.TopLevel; string? lastTopLevel = reader.TopLevel;
GameBase? game = null; GameBase? game = null;
var games = new List<GameBase>(); var games = new List<GameBase>();
@@ -75,27 +75,32 @@ namespace SabreTools.Serialization
switch (lastTopLevel) switch (lastTopLevel)
{ {
case "doscenter": case "doscenter":
dat.ClrMamePro!.ADDITIONAL_ELEMENTS = headerAdditional.ToArray(); if (dat.ClrMamePro != null)
dat.ClrMamePro.ADDITIONAL_ELEMENTS = headerAdditional.ToArray();
headerAdditional.Clear(); headerAdditional.Clear();
break; break;
case "game": case "game":
case "machine": case "machine":
case "resource": case "resource":
case "set": case "set":
game!.Release = releases.ToArray(); if (game != null)
game.BiosSet = biosSets.ToArray(); {
game.Rom = roms.ToArray(); game.Release = releases.ToArray();
game.Disk = disks.ToArray(); game.BiosSet = biosSets.ToArray();
game.Media = medias.ToArray(); game.Rom = roms.ToArray();
game.Sample = samples.ToArray(); game.Disk = disks.ToArray();
game.Archive = archives.ToArray(); game.Media = medias.ToArray();
game.Chip = chips.ToArray(); game.Sample = samples.ToArray();
game.Video = videos.ToArray(); game.Archive = archives.ToArray();
game.DipSwitch = dipSwitches.ToArray(); game.Chip = chips.ToArray();
game.ADDITIONAL_ELEMENTS = gameAdditional.ToArray(); game.Video = videos.ToArray();
game.DipSwitch = dipSwitches.ToArray();
game.ADDITIONAL_ELEMENTS = gameAdditional.ToArray();
games.Add(game); games.Add(game);
game = null; game = null;
}
releases.Clear(); releases.Clear();
biosSets.Clear(); biosSets.Clear();
@@ -138,7 +143,8 @@ namespace SabreTools.Serialization
game = new Set(); game = new Set();
break; break;
default: default:
additional.Add(reader.CurrentLine); if (reader.CurrentLine != null)
additional.Add(reader.CurrentLine);
break; break;
} }
} }
@@ -198,7 +204,8 @@ namespace SabreTools.Serialization
dat.ClrMamePro.ForcePacking = reader.Standalone?.Value; dat.ClrMamePro.ForcePacking = reader.Standalone?.Value;
break; break;
default: default:
headerAdditional.Add(reader.CurrentLine); if (reader.CurrentLine != null)
headerAdditional.Add(reader.CurrentLine);
break; break;
} }
} }
@@ -255,7 +262,8 @@ namespace SabreTools.Serialization
samples.Add(sample); samples.Add(sample);
break; break;
default: default:
gameAdditional.Add(reader.CurrentLine); if (reader.CurrentLine != null)
gameAdditional.Add(reader.CurrentLine);
break; break;
} }
} }
@@ -272,53 +280,81 @@ namespace SabreTools.Serialization
switch (reader.InternalName) switch (reader.InternalName)
{ {
case "release": case "release":
releases.Add(CreateRelease(reader)); var release = CreateRelease(reader);
if (release != null)
releases.Add(release);
break; break;
case "biosset": case "biosset":
biosSets.Add(CreateBiosSet(reader)); var biosSet = CreateBiosSet(reader);
if (biosSet != null)
biosSets.Add(biosSet);
break; break;
case "rom": case "rom":
roms.Add(CreateRom(reader)); var rom = CreateRom(reader);
if (rom != null)
roms.Add(rom);
break; break;
case "disk": case "disk":
disks.Add(CreateDisk(reader)); var disk = CreateDisk(reader);
if (disk != null)
disks.Add(disk);
break; break;
case "media": case "media":
medias.Add(CreateMedia(reader)); var media = CreateMedia(reader);
if (media != null)
medias.Add(media);
break; break;
case "sample": case "sample":
samples.Add(CreateSample(reader)); var sample = CreateSample(reader);
if (sample != null)
samples.Add(sample);
break; break;
case "archive": case "archive":
archives.Add(CreateArchive(reader)); var archive = CreateArchive(reader);
if (archive != null)
archives.Add(archive);
break; break;
case "chip": case "chip":
chips.Add(CreateChip(reader)); var chip = CreateChip(reader);
if (chip != null)
chips.Add(chip);
break; break;
case "video": case "video":
videos.Add(CreateVideo(reader)); var video = CreateVideo(reader);
if (video != null)
videos.Add(video);
break; break;
case "sound": case "sound":
game.Sound = CreateSound(reader); var sound = CreateSound(reader);
if (sound != null)
game.Sound = sound;
break; break;
case "input": case "input":
game.Input = CreateInput(reader); var input = CreateInput(reader);
if (input != null)
game.Input = input;
break; break;
case "dipswitch": case "dipswitch":
dipSwitches.Add(CreateDipSwitch(reader)); var dipSwitch = CreateDipSwitch(reader);
if (dipSwitch != null)
dipSwitches.Add(dipSwitch);
break; break;
case "driver": case "driver":
game.Driver = CreateDriver(reader); var driver = CreateDriver(reader);
if (driver != null)
game.Driver = driver;
break; break;
default: default:
gameAdditional.Add(reader.CurrentLine); if (reader.CurrentLine != null)
gameAdditional.Add(reader.CurrentLine);
continue; continue;
} }
} }
else else
{ {
additional.Add(reader.CurrentLine); if (reader.CurrentLine != null)
additional.Add(reader.CurrentLine);
} }
} }
@@ -333,8 +369,11 @@ namespace SabreTools.Serialization
/// </summary> /// </summary>
/// <param name="reader">ClrMameProReader representing the metadata file</param> /// <param name="reader">ClrMameProReader representing the metadata file</param>
/// <returns>Release object created from the reader context</returns> /// <returns>Release object created from the reader context</returns>
private static Release CreateRelease(ClrMameProReader reader) private static Release? CreateRelease(ClrMameProReader reader)
{ {
if (reader.Internal == null)
return null;
var itemAdditional = new List<string>(); var itemAdditional = new List<string>();
var release = new Release(); var release = new Release();
foreach (var kvp in reader.Internal) foreach (var kvp in reader.Internal)
@@ -371,8 +410,11 @@ namespace SabreTools.Serialization
/// </summary> /// </summary>
/// <param name="reader">ClrMameProReader representing the metadata file</param> /// <param name="reader">ClrMameProReader representing the metadata file</param>
/// <returns>BiosSet object created from the reader context</returns> /// <returns>BiosSet object created from the reader context</returns>
private static BiosSet CreateBiosSet(ClrMameProReader reader) private static BiosSet? CreateBiosSet(ClrMameProReader reader)
{ {
if (reader.Internal == null)
return null;
var itemAdditional = new List<string>(); var itemAdditional = new List<string>();
var biosset = new BiosSet(); var biosset = new BiosSet();
foreach (var kvp in reader.Internal) foreach (var kvp in reader.Internal)
@@ -403,8 +445,11 @@ namespace SabreTools.Serialization
/// </summary> /// </summary>
/// <param name="reader">ClrMameProReader representing the metadata file</param> /// <param name="reader">ClrMameProReader representing the metadata file</param>
/// <returns>Rom object created from the reader context</returns> /// <returns>Rom object created from the reader context</returns>
private static Rom CreateRom(ClrMameProReader reader) private static Rom? CreateRom(ClrMameProReader reader)
{ {
if (reader.Internal == null)
return null;
var itemAdditional = new List<string>(); var itemAdditional = new List<string>();
var rom = new Rom(); var rom = new Rom();
foreach (var kvp in reader.Internal) foreach (var kvp in reader.Internal)
@@ -489,8 +534,11 @@ namespace SabreTools.Serialization
/// </summary> /// </summary>
/// <param name="reader">ClrMameProReader representing the metadata file</param> /// <param name="reader">ClrMameProReader representing the metadata file</param>
/// <returns>Disk object created from the reader context</returns> /// <returns>Disk object created from the reader context</returns>
private static Disk CreateDisk(ClrMameProReader reader) private static Disk? CreateDisk(ClrMameProReader reader)
{ {
if (reader.Internal == null)
return null;
var itemAdditional = new List<string>(); var itemAdditional = new List<string>();
var disk = new Disk(); var disk = new Disk();
foreach (var kvp in reader.Internal) foreach (var kvp in reader.Internal)
@@ -530,8 +578,11 @@ namespace SabreTools.Serialization
/// </summary> /// </summary>
/// <param name="reader">ClrMameProReader representing the metadata file</param> /// <param name="reader">ClrMameProReader representing the metadata file</param>
/// <returns>Media object created from the reader context</returns> /// <returns>Media object created from the reader context</returns>
private static Media CreateMedia(ClrMameProReader reader) private static Media? CreateMedia(ClrMameProReader reader)
{ {
if (reader.Internal == null)
return null;
var itemAdditional = new List<string>(); var itemAdditional = new List<string>();
var media = new Media(); var media = new Media();
foreach (var kvp in reader.Internal) foreach (var kvp in reader.Internal)
@@ -568,8 +619,11 @@ namespace SabreTools.Serialization
/// </summary> /// </summary>
/// <param name="reader">ClrMameProReader representing the metadata file</param> /// <param name="reader">ClrMameProReader representing the metadata file</param>
/// <returns>Sample object created from the reader context</returns> /// <returns>Sample object created from the reader context</returns>
private static Sample CreateSample(ClrMameProReader reader) private static Sample? CreateSample(ClrMameProReader reader)
{ {
if (reader.Internal == null)
return null;
var itemAdditional = new List<string>(); var itemAdditional = new List<string>();
var sample = new Sample(); var sample = new Sample();
foreach (var kvp in reader.Internal) foreach (var kvp in reader.Internal)
@@ -594,8 +648,11 @@ namespace SabreTools.Serialization
/// </summary> /// </summary>
/// <param name="reader">ClrMameProReader representing the metadata file</param> /// <param name="reader">ClrMameProReader representing the metadata file</param>
/// <returns>Archive object created from the reader context</returns> /// <returns>Archive object created from the reader context</returns>
private static Archive CreateArchive(ClrMameProReader reader) private static Archive? CreateArchive(ClrMameProReader reader)
{ {
if (reader.Internal == null)
return null;
var itemAdditional = new List<string>(); var itemAdditional = new List<string>();
var archive = new Archive(); var archive = new Archive();
foreach (var kvp in reader.Internal) foreach (var kvp in reader.Internal)
@@ -620,8 +677,11 @@ namespace SabreTools.Serialization
/// </summary> /// </summary>
/// <param name="reader">ClrMameProReader representing the metadata file</param> /// <param name="reader">ClrMameProReader representing the metadata file</param>
/// <returns>Chip object created from the reader context</returns> /// <returns>Chip object created from the reader context</returns>
private static Chip CreateChip(ClrMameProReader reader) private static Chip? CreateChip(ClrMameProReader reader)
{ {
if (reader.Internal == null)
return null;
var itemAdditional = new List<string>(); var itemAdditional = new List<string>();
var chip = new Chip(); var chip = new Chip();
foreach (var kvp in reader.Internal) foreach (var kvp in reader.Internal)
@@ -655,8 +715,11 @@ namespace SabreTools.Serialization
/// </summary> /// </summary>
/// <param name="reader">ClrMameProReader representing the metadata file</param> /// <param name="reader">ClrMameProReader representing the metadata file</param>
/// <returns>Video object created from the reader context</returns> /// <returns>Video object created from the reader context</returns>
private static Video CreateVideo(ClrMameProReader reader) private static Video? CreateVideo(ClrMameProReader reader)
{ {
if (reader.Internal == null)
return null;
var itemAdditional = new List<string>(); var itemAdditional = new List<string>();
var video = new Video(); var video = new Video();
foreach (var kvp in reader.Internal) foreach (var kvp in reader.Internal)
@@ -699,8 +762,11 @@ namespace SabreTools.Serialization
/// </summary> /// </summary>
/// <param name="reader">ClrMameProReader representing the metadata file</param> /// <param name="reader">ClrMameProReader representing the metadata file</param>
/// <returns>Sound object created from the reader context</returns> /// <returns>Sound object created from the reader context</returns>
private static Sound CreateSound(ClrMameProReader reader) private static Sound? CreateSound(ClrMameProReader reader)
{ {
if (reader.Internal == null)
return null;
var itemAdditional = new List<string>(); var itemAdditional = new List<string>();
var sound = new Sound(); var sound = new Sound();
foreach (var kvp in reader.Internal) foreach (var kvp in reader.Internal)
@@ -725,8 +791,11 @@ namespace SabreTools.Serialization
/// </summary> /// </summary>
/// <param name="reader">ClrMameProReader representing the metadata file</param> /// <param name="reader">ClrMameProReader representing the metadata file</param>
/// <returns>Input object created from the reader context</returns> /// <returns>Input object created from the reader context</returns>
private static Input CreateInput(ClrMameProReader reader) private static Input? CreateInput(ClrMameProReader reader)
{ {
if (reader.Internal == null)
return null;
var itemAdditional = new List<string>(); var itemAdditional = new List<string>();
var input = new Input(); var input = new Input();
foreach (var kvp in reader.Internal) foreach (var kvp in reader.Internal)
@@ -766,8 +835,11 @@ namespace SabreTools.Serialization
/// </summary> /// </summary>
/// <param name="reader">ClrMameProReader representing the metadata file</param> /// <param name="reader">ClrMameProReader representing the metadata file</param>
/// <returns>DipSwitch object created from the reader context</returns> /// <returns>DipSwitch object created from the reader context</returns>
private static DipSwitch CreateDipSwitch(ClrMameProReader reader) private static DipSwitch? CreateDipSwitch(ClrMameProReader reader)
{ {
if (reader.Internal == null)
return null;
var itemAdditional = new List<string>(); var itemAdditional = new List<string>();
var dipswitch = new DipSwitch(); var dipswitch = new DipSwitch();
var entries = new List<string>(); var entries = new List<string>();
@@ -800,8 +872,11 @@ namespace SabreTools.Serialization
/// </summary> /// </summary>
/// <param name="reader">ClrMameProReader representing the metadata file</param> /// <param name="reader">ClrMameProReader representing the metadata file</param>
/// <returns>Driver object created from the reader context</returns> /// <returns>Driver object created from the reader context</returns>
private static Driver CreateDriver(ClrMameProReader reader) private static Driver? CreateDriver(ClrMameProReader reader)
{ {
if (reader.Internal == null)
return null;
var itemAdditional = new List<string>(); var itemAdditional = new List<string>();
var driver = new Driver(); var driver = new Driver();
foreach (var kvp in reader.Internal) foreach (var kvp in reader.Internal)
@@ -832,7 +907,7 @@ namespace SabreTools.Serialization
driver.ADDITIONAL_ELEMENTS = itemAdditional.ToArray(); driver.ADDITIONAL_ELEMENTS = itemAdditional.ToArray();
return driver; return driver;
} }
#region Internal #region Internal
/// <summary> /// <summary>
@@ -842,7 +917,7 @@ namespace SabreTools.Serialization
{ {
if (item == null) if (item == null)
return null; return null;
var metadataFile = new MetadataFile(); var metadataFile = new MetadataFile();
var header = item.Read<Models.Internal.Header>(Models.Internal.MetadataFile.HeaderKey); var header = item.Read<Models.Internal.Header>(Models.Internal.MetadataFile.HeaderKey);

View File

@@ -39,7 +39,7 @@ namespace SabreTools.Serialization
var dat = new MetadataFile(); var dat = new MetadataFile();
// Loop through and parse out the values // Loop through and parse out the values
string lastTopLevel = reader.TopLevel; string? lastTopLevel = reader.TopLevel;
Game? game = null; Game? game = null;
var games = new List<Game>(); var games = new List<Game>();
@@ -48,7 +48,6 @@ namespace SabreTools.Serialization
var additional = new List<string>(); var additional = new List<string>();
var headerAdditional = new List<string>(); var headerAdditional = new List<string>();
var gameAdditional = new List<string>(); var gameAdditional = new List<string>();
var fileAdditional = new List<string>();
while (!reader.EndOfStream) while (!reader.EndOfStream)
{ {
// If we have no next line // If we have no next line
@@ -102,7 +101,8 @@ namespace SabreTools.Serialization
game = new Game(); game = new Game();
break; break;
default: default:
additional.Add(reader.CurrentLine); if (reader.CurrentLine != null)
additional.Add(reader.CurrentLine);
break; break;
} }
} }
@@ -137,7 +137,8 @@ namespace SabreTools.Serialization
dat.DosCenter.Comment = reader.Standalone?.Value; dat.DosCenter.Comment = reader.Standalone?.Value;
break; break;
default: default:
headerAdditional.Add(item: reader.CurrentLine); if (reader.CurrentLine != null)
headerAdditional.Add(item: reader.CurrentLine);
break; break;
} }
} }
@@ -154,7 +155,8 @@ namespace SabreTools.Serialization
game.Name = reader.Standalone?.Value; game.Name = reader.Standalone?.Value;
break; break;
default: default:
gameAdditional.Add(item: reader.CurrentLine); if (reader.CurrentLine != null)
gameAdditional.Add(item: reader.CurrentLine);
break; break;
} }
} }
@@ -165,18 +167,21 @@ namespace SabreTools.Serialization
// If we have an unknown type, log it // If we have an unknown type, log it
if (reader.InternalName != "file") if (reader.InternalName != "file")
{ {
gameAdditional.Add(reader.CurrentLine); if (reader.CurrentLine != null)
gameAdditional.Add(reader.CurrentLine);
continue; continue;
} }
// Create the file and add to the list // Create the file and add to the list
var file = CreateFile(reader); var file = CreateFile(reader);
files.Add(file); if (file != null)
files.Add(file);
} }
else else
{ {
additional.Add(item: reader.CurrentLine); if (reader.CurrentLine != null)
additional.Add(item: reader.CurrentLine);
} }
} }
@@ -191,8 +196,11 @@ namespace SabreTools.Serialization
/// </summary> /// </summary>
/// <param name="reader">ClrMameProReader representing the metadata file</param> /// <param name="reader">ClrMameProReader representing the metadata file</param>
/// <returns>File object created from the reader context</returns> /// <returns>File object created from the reader context</returns>
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<string>(); var itemAdditional = new List<string>();
var file = new Models.DosCenter.File(); var file = new Models.DosCenter.File();
foreach (var kvp in reader.Internal) foreach (var kvp in reader.Internal)
@@ -212,7 +220,8 @@ namespace SabreTools.Serialization
file.Date = kvp.Value; file.Date = kvp.Value;
break; break;
default: default:
itemAdditional.Add(item: reader.CurrentLine); if (reader.CurrentLine != null)
itemAdditional.Add(item: reader.CurrentLine);
break; break;
} }
} }

View File

@@ -140,7 +140,7 @@ namespace SabreTools.Serialization
writer.WriteRequiredAttributeString("name", file.Name, throwOnError: true); writer.WriteRequiredAttributeString("name", file.Name, throwOnError: true);
writer.WriteRequiredAttributeString("size", file.Size, throwOnError: true); writer.WriteRequiredAttributeString("size", file.Size, throwOnError: true);
writer.WriteOptionalAttributeString("date", file.Date); writer.WriteOptionalAttributeString("date", file.Date);
writer.WriteRequiredAttributeString("crc", file.CRC.ToUpperInvariant(), throwOnError: true); writer.WriteRequiredAttributeString("crc", file.CRC?.ToUpperInvariant(), throwOnError: true);
writer.WriteEndElement(); // file writer.WriteEndElement(); // file
} }

View File

@@ -49,7 +49,7 @@ namespace SabreTools.Serialization
while (!reader.EndOfStream) while (!reader.EndOfStream)
{ {
// If we have no next line // If we have no next line
if (!reader.ReadNextLine()) if (!reader.ReadNextLine() || reader.Line == null)
break; break;
// Parse the line into a row // Parse the line into a row

View File

@@ -66,7 +66,10 @@ namespace SabreTools.Serialization
// Loop through and write out the rows // Loop through and write out the rows
foreach (var row in rows) foreach (var row in rows)
{ {
var rowArray = new List<string> if (row == null)
continue;
var rowArray = new List<string?>
{ {
row.SHA256, row.SHA256,
row.Name, row.Name,

View File

@@ -99,6 +99,11 @@ namespace SabreTools.Serialization
// Loop through and write out the items // Loop through and write out the items
foreach (var sfv in sfvs) 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.WriteValues(new string[] { sfv.File, sfv.Hash });
writer.Flush(); writer.Flush();
} }
@@ -118,6 +123,11 @@ namespace SabreTools.Serialization
// Loop through and write out the items // Loop through and write out the items
foreach (var md5 in md5s) 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.WriteValues(new string[] { md5.Hash, md5.File });
writer.Flush(); writer.Flush();
} }
@@ -137,6 +147,11 @@ namespace SabreTools.Serialization
// Loop through and write out the items // Loop through and write out the items
foreach (var sha1 in sha1s) 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.WriteValues(new string[] { sha1.Hash, sha1.File });
writer.Flush(); writer.Flush();
} }
@@ -156,6 +171,11 @@ namespace SabreTools.Serialization
// Loop through and write out the items // Loop through and write out the items
foreach (var sha256 in sha256s) 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.WriteValues(new string[] { sha256.Hash, sha256.File });
writer.Flush(); writer.Flush();
} }
@@ -175,6 +195,11 @@ namespace SabreTools.Serialization
// Loop through and write out the items // Loop through and write out the items
foreach (var sha384 in sha384s) 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.WriteValues(new string[] { sha384.Hash, sha384.File });
writer.Flush(); writer.Flush();
} }
@@ -194,6 +219,11 @@ namespace SabreTools.Serialization
// Loop through and write out the items // Loop through and write out the items
foreach (var sha512 in sha512s) 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.WriteValues(new string[] { sha512.Hash, sha512.File });
writer.Flush(); writer.Flush();
} }
@@ -213,6 +243,11 @@ namespace SabreTools.Serialization
// Loop through and write out the items // Loop through and write out the items
foreach (var spamsum in spamsums) 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.WriteValues(new string[] { spamsum.Hash, spamsum.File });
writer.Flush(); writer.Flush();
} }

View File

@@ -136,6 +136,9 @@ namespace SabreTools.Serialization
foreach (var row in rows) foreach (var row in rows)
{ {
if (string.IsNullOrWhiteSpace(row.Name))
continue;
var rowBuilder = new StringBuilder(); var rowBuilder = new StringBuilder();
int padding = 40 - (row.Size?.Length ?? 0); int padding = 40 - (row.Size?.Length ?? 0);

View File

@@ -62,7 +62,7 @@ namespace SabreTools.Serialization
case IniRowType.Comment: case IniRowType.Comment:
continue; continue;
case IniRowType.SectionHeader: case IniRowType.SectionHeader:
switch (reader.Section.ToLowerInvariant()) switch (reader.Section?.ToLowerInvariant())
{ {
case "credits": case "credits":
dat.Credits ??= new Credits(); dat.Credits ??= new Credits();
@@ -77,14 +77,15 @@ namespace SabreTools.Serialization
dat.Games ??= new Games(); dat.Games ??= new Games();
break; break;
default: default:
additional.Add(reader.CurrentLine); if (reader.CurrentLine != null)
additional.Add(reader.CurrentLine);
break; break;
} }
continue; continue;
} }
// If we're in credits // If we're in credits
if (reader.Section.ToLowerInvariant() == "credits") if (reader.Section?.ToLowerInvariant() == "credits")
{ {
// Create the section if we haven't already // Create the section if we haven't already
dat.Credits ??= new Credits(); dat.Credits ??= new Credits();
@@ -113,13 +114,14 @@ namespace SabreTools.Serialization
dat.Credits.Comment = reader.KeyValuePair?.Value; dat.Credits.Comment = reader.KeyValuePair?.Value;
break; break;
default: default:
creditsAdditional.Add(reader.CurrentLine); if (reader.CurrentLine != null)
creditsAdditional.Add(reader.CurrentLine);
break; break;
} }
} }
// If we're in dat // 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 // Create the section if we haven't already
dat.Dat ??= new Dat(); dat.Dat ??= new Dat();
@@ -139,13 +141,14 @@ namespace SabreTools.Serialization
dat.Dat.Merge = reader.KeyValuePair?.Value; dat.Dat.Merge = reader.KeyValuePair?.Value;
break; break;
default: default:
datAdditional.Add(reader.CurrentLine); if (reader.CurrentLine != null)
datAdditional.Add(reader.CurrentLine);
break; break;
} }
} }
// If we're in emulator // 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 // Create the section if we haven't already
dat.Emulator ??= new Emulator(); dat.Emulator ??= new Emulator();
@@ -159,21 +162,24 @@ namespace SabreTools.Serialization
dat.Emulator.Version = reader.KeyValuePair?.Value; dat.Emulator.Version = reader.KeyValuePair?.Value;
break; break;
default: default:
emulatorAdditional.Add(reader.CurrentLine); if (reader.CurrentLine != null)
emulatorAdditional.Add(reader.CurrentLine);
break; break;
} }
} }
// If we're in games // 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 // Create the section if we haven't already
dat.Games ??= new Games(); dat.Games ??= new Games();
// If the line doesn't contain the delimiter // 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; continue;
} }
@@ -202,22 +208,23 @@ namespace SabreTools.Serialization
else else
{ {
additional.Add(item: reader.CurrentLine); if (reader.CurrentLine != null)
additional.Add(reader.CurrentLine);
} }
} }
// Add extra pieces and return // Add extra pieces and return
dat.ADDITIONAL_ELEMENTS = additional.ToArray(); dat.ADDITIONAL_ELEMENTS = additional.Where(s => s != null).ToArray();
if (dat.Credits != null) if (dat.Credits != null)
dat.Credits.ADDITIONAL_ELEMENTS = creditsAdditional.ToArray(); dat.Credits.ADDITIONAL_ELEMENTS = creditsAdditional.Where(s => s != null).ToArray();
if (dat.Dat != null) if (dat.Dat != null)
dat.Dat.ADDITIONAL_ELEMENTS = datAdditional.ToArray(); dat.Dat.ADDITIONAL_ELEMENTS = datAdditional.Where(s => s != null).ToArray();
if (dat.Emulator != null) if (dat.Emulator != null)
dat.Emulator.ADDITIONAL_ELEMENTS = emulatorAdditional.ToArray(); dat.Emulator.ADDITIONAL_ELEMENTS = emulatorAdditional.Where(s => s != null).ToArray();
if (dat.Games != null) if (dat.Games != null)
{ {
dat.Games.Rom = roms.ToArray(); 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; return dat;
} }

View File

@@ -46,7 +46,7 @@ namespace SabreTools.Serialization
var dat = new MetadataFile(); var dat = new MetadataFile();
// Read the header values first // Read the header values first
if (!reader.ReadHeader()) if (!reader.ReadHeader() || reader.HeaderValues == null)
return null; return null;
dat.Header = reader.HeaderValues.ToArray(); dat.Header = reader.HeaderValues.ToArray();
@@ -56,7 +56,7 @@ namespace SabreTools.Serialization
while (!reader.EndOfStream) while (!reader.EndOfStream)
{ {
// If we have no next line // If we have no next line
if (!reader.ReadNextLine()) if (!reader.ReadNextLine() || reader.Line == null)
break; break;
// Parse the line into a row // Parse the line into a row

View File

@@ -127,9 +127,9 @@ namespace SabreTools.Test.Parser
Assert.Empty(chip.ADDITIONAL_ELEMENTS); Assert.Empty(chip.ADDITIONAL_ELEMENTS);
} }
if (game.Video != null) foreach (var video in game.Video ?? Array.Empty<Models.ClrMamePro.Video>())
{ {
Assert.Empty(game.Video.ADDITIONAL_ELEMENTS); Assert.Empty(video.ADDITIONAL_ELEMENTS);
} }
if (game.Sound != null) if (game.Sound != null)