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);
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,19 +431,21 @@ namespace SabreTools.DatFiles.Formats
/// <summary>
/// Convert Video information
/// </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="filename">Name of the file to be parsed</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="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 (video == null)
// If the video array is missing, we can't do anything
if (videos == null || !videos.Any())
return;
containsItems = true;
foreach (var video in videos)
{
var item = new Display
{
DisplayType = video.Screen?.AsDisplayType() ?? DisplayType.NULL,
@@ -473,6 +475,7 @@ namespace SabreTools.DatFiles.Formats
item.CopyMachineInformation(machine);
ParseAddHelper(item, statsOnly);
}
}
/// <summary>
/// Convert Sound information

View File

@@ -251,6 +251,7 @@ namespace SabreTools.DatFiles.Formats
var samples = new List<Models.ClrMamePro.Sample>();
var archives = new List<Models.ClrMamePro.Archive>();
var chips = new List<Models.ClrMamePro.Chip>();
var videos = new List<Models.ClrMamePro.Video>();
var dipswitches = new List<Models.ClrMamePro.DipSwitch>();
// 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);

View File

@@ -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
/// </summary>
/// <param name="path">Path to get extension from</param>
/// <returns>Extension, if possible</returns>
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))
@@ -102,7 +107,7 @@ namespace SabreTools.IO
/// </summary>
/// <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>
public static List<string> ListEmpty(this string root)
public static List<string>? ListEmpty(this string? root)
{
// Check null or empty first
if (string.IsNullOrEmpty(root))

View File

@@ -11,14 +11,14 @@ namespace SabreTools.IO
/// <summary>
/// Current full path represented
/// </summary>
public string CurrentPath { get; private set; }
public string CurrentPath { get; init; }
/// <summary>
/// Possible parent path represented (may be null or empty)
/// </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;
ParentPath = parentPath;
@@ -29,7 +29,7 @@ namespace SabreTools.IO
/// </summary>
/// <param name="sanitize">True if path separators should be converted to '-', false otherwise</param>
/// <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 (string.IsNullOrWhiteSpace(CurrentPath))
@@ -55,7 +55,7 @@ namespace SabreTools.IO
/// <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>
/// <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 (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)));
}
}

View File

@@ -23,12 +23,12 @@ namespace SabreTools.IO.Readers
/// <summary>
/// Internal stream reader for inputting
/// </summary>
private readonly StreamReader sr;
private readonly StreamReader? sr;
/// <summary>
/// Contents of the current line, unprocessed
/// </summary>
public string CurrentLine { get; private set; } = string.Empty;
public string? CurrentLine { get; private set; } = string.Empty;
/// <summary>
/// Get the current line number
@@ -49,12 +49,12 @@ namespace SabreTools.IO.Readers
/// <summary>
/// Contents of the currently read line as an internal item
/// </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>
/// Current internal item name
/// </summary>
public string InternalName { get; private set; } = null;
public string? InternalName { get; private set; }
/// <summary>
/// Get if we should be making DosCenter exceptions
@@ -85,7 +85,7 @@ namespace SabreTools.IO.Readers
/// <summary>
/// Current top-level being read
/// </summary>
public string TopLevel { get; private set; } = string.Empty;
public string? TopLevel { get; private set; } = string.Empty;
/// <summary>
/// Constructor for opening a write from a file
@@ -108,10 +108,13 @@ namespace SabreTools.IO.Readers
/// </summary>
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
/// </summary>
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
/// </summary>
public void Dispose()
{
sr.Dispose();
sr?.Dispose();
}
}
}

View File

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

View File

@@ -12,7 +12,7 @@ namespace SabreTools.IO.Readers
/// <summary>
/// Internal stream reader for inputting
/// </summary>
private readonly StreamReader sr;
private readonly StreamReader? sr;
/// <summary>
/// Internal value to say how many fields should be written
@@ -33,7 +33,7 @@ namespace SabreTools.IO.Readers
/// <summary>
/// Contents of the current line, unprocessed
/// </summary>
public string CurrentLine { get; private set; } = string.Empty;
public string? CurrentLine { get; private set; } = string.Empty;
/// <summary>
/// Get the current line number
@@ -48,12 +48,12 @@ namespace SabreTools.IO.Readers
/// <summary>
/// Header row values
/// </summary>
public List<string> HeaderValues { get; set; } = null;
public List<string>? HeaderValues { get; set; } = null;
/// <summary>
/// Get the current line values
/// </summary>
public List<string> Line { get; private set; } = null;
public List<string>? Line { get; private set; } = null;
/// <summary>
/// Assume that values are wrapped in quotes
@@ -105,13 +105,19 @@ namespace SabreTools.IO.Readers
/// </summary>
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
/// <summary>
/// Get the value for the current line for the current key
/// </summary>
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
/// </summary>
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
/// </summary>
public void Dispose()
{
sr.Dispose();
sr?.Dispose();
}
}
}

View File

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

View File

@@ -44,11 +44,8 @@ namespace SabreTools.IO.Writers
/// <summary>
/// Tag information for the stack
/// </summary>
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
/// <summary>
/// Write a complete element with content
/// </summary>
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
/// <param name="name">Name of 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>
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
/// </summary>
/// <param name="name">Name of 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))
WriteElementString(name, value);
@@ -253,7 +250,7 @@ namespace SabreTools.IO.Writers
/// <param name="name">Name of 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>
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
/// <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="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
if (value == null && throwOnError)
@@ -282,7 +279,7 @@ namespace SabreTools.IO.Writers
/// <param name="name">Name of 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>
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
/// <param name="name">Name of 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>
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
/// <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="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
if (value == null && throwOnError)
@@ -353,7 +350,7 @@ namespace SabreTools.IO.Writers
/// <param name="name">Name of 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>
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
/// <summary>
/// Write a string content value
/// </summary>
public void WriteString(string value)
public void WriteString(string? value)
{
try
{

View File

@@ -9,7 +9,7 @@ namespace SabreTools.IO.Writers
/// <summary>
/// Internal stream writer for outputting
/// </summary>
private readonly StreamWriter sw;
private readonly StreamWriter? sw;
/// <summary>
/// Constructor for writing to a file
@@ -30,8 +30,11 @@ namespace SabreTools.IO.Writers
/// <summary>
/// Write a section tag
/// </summary>
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
/// <summary>
/// Write a key value pair
/// </summary>
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
/// <summary>
/// Write a comment
/// </summary>
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
/// <summary>
/// Write a generic string
/// </summary>
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
/// </summary>
public void WriteLine()
{
if (sw?.BaseStream == null)
return;
sw.WriteLine();
}
@@ -81,7 +96,7 @@ namespace SabreTools.IO.Writers
/// </summary>
public void Flush()
{
sw.Flush();
sw?.Flush();
}
/// <summary>
@@ -89,7 +104,7 @@ namespace SabreTools.IO.Writers
/// </summary>
public void Dispose()
{
sw.Dispose();
sw?.Dispose();
}
}
}

View File

@@ -60,7 +60,7 @@ namespace SabreTools.IO.Writers
/// <summary>
/// Write a header row
/// </summary>
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
/// <summary>
/// Write a value row
/// </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 (sw == null || !sw.BaseStream.CanWrite)

View File

@@ -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

View File

@@ -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<GameBase>();
@@ -75,14 +75,18 @@ 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();
if (game != null)
{
game.Release = releases.ToArray();
game.BiosSet = biosSets.ToArray();
game.Rom = roms.ToArray();
game.Disk = disks.ToArray();
@@ -96,6 +100,7 @@ namespace SabreTools.Serialization
games.Add(game);
game = null;
}
releases.Clear();
biosSets.Clear();
@@ -138,6 +143,7 @@ namespace SabreTools.Serialization
game = new Set();
break;
default:
if (reader.CurrentLine != null)
additional.Add(reader.CurrentLine);
break;
}
@@ -198,6 +204,7 @@ namespace SabreTools.Serialization
dat.ClrMamePro.ForcePacking = reader.Standalone?.Value;
break;
default:
if (reader.CurrentLine != null)
headerAdditional.Add(reader.CurrentLine);
break;
}
@@ -255,6 +262,7 @@ namespace SabreTools.Serialization
samples.Add(sample);
break;
default:
if (reader.CurrentLine != null)
gameAdditional.Add(reader.CurrentLine);
break;
}
@@ -272,45 +280,72 @@ 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:
if (reader.CurrentLine != null)
gameAdditional.Add(reader.CurrentLine);
continue;
}
@@ -318,6 +353,7 @@ namespace SabreTools.Serialization
else
{
if (reader.CurrentLine != null)
additional.Add(reader.CurrentLine);
}
}
@@ -333,8 +369,11 @@ namespace SabreTools.Serialization
/// </summary>
/// <param name="reader">ClrMameProReader representing the metadata file</param>
/// <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 release = new Release();
foreach (var kvp in reader.Internal)
@@ -371,8 +410,11 @@ namespace SabreTools.Serialization
/// </summary>
/// <param name="reader">ClrMameProReader representing the metadata file</param>
/// <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 biosset = new BiosSet();
foreach (var kvp in reader.Internal)
@@ -403,8 +445,11 @@ namespace SabreTools.Serialization
/// </summary>
/// <param name="reader">ClrMameProReader representing the metadata file</param>
/// <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 rom = new Rom();
foreach (var kvp in reader.Internal)
@@ -489,8 +534,11 @@ namespace SabreTools.Serialization
/// </summary>
/// <param name="reader">ClrMameProReader representing the metadata file</param>
/// <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 disk = new Disk();
foreach (var kvp in reader.Internal)
@@ -530,8 +578,11 @@ namespace SabreTools.Serialization
/// </summary>
/// <param name="reader">ClrMameProReader representing the metadata file</param>
/// <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 media = new Media();
foreach (var kvp in reader.Internal)
@@ -568,8 +619,11 @@ namespace SabreTools.Serialization
/// </summary>
/// <param name="reader">ClrMameProReader representing the metadata file</param>
/// <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 sample = new Sample();
foreach (var kvp in reader.Internal)
@@ -594,8 +648,11 @@ namespace SabreTools.Serialization
/// </summary>
/// <param name="reader">ClrMameProReader representing the metadata file</param>
/// <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 archive = new Archive();
foreach (var kvp in reader.Internal)
@@ -620,8 +677,11 @@ namespace SabreTools.Serialization
/// </summary>
/// <param name="reader">ClrMameProReader representing the metadata file</param>
/// <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 chip = new Chip();
foreach (var kvp in reader.Internal)
@@ -655,8 +715,11 @@ namespace SabreTools.Serialization
/// </summary>
/// <param name="reader">ClrMameProReader representing the metadata file</param>
/// <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 video = new Video();
foreach (var kvp in reader.Internal)
@@ -699,8 +762,11 @@ namespace SabreTools.Serialization
/// </summary>
/// <param name="reader">ClrMameProReader representing the metadata file</param>
/// <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 sound = new Sound();
foreach (var kvp in reader.Internal)
@@ -725,8 +791,11 @@ namespace SabreTools.Serialization
/// </summary>
/// <param name="reader">ClrMameProReader representing the metadata file</param>
/// <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 input = new Input();
foreach (var kvp in reader.Internal)
@@ -766,8 +835,11 @@ namespace SabreTools.Serialization
/// </summary>
/// <param name="reader">ClrMameProReader representing the metadata file</param>
/// <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 dipswitch = new DipSwitch();
var entries = new List<string>();
@@ -800,8 +872,11 @@ namespace SabreTools.Serialization
/// </summary>
/// <param name="reader">ClrMameProReader representing the metadata file</param>
/// <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 driver = new Driver();
foreach (var kvp in reader.Internal)

View File

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

View File

@@ -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
}

View File

@@ -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

View File

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

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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,6 +77,7 @@ namespace SabreTools.Serialization
dat.Games ??= new Games();
break;
default:
if (reader.CurrentLine != null)
additional.Add(reader.CurrentLine);
break;
}
@@ -84,7 +85,7 @@ namespace SabreTools.Serialization
}
// 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:
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:
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:
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))
{
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;
}

View File

@@ -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

View File

@@ -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<Models.ClrMamePro.Video>())
{
Assert.Empty(game.Video.ADDITIONAL_ELEMENTS);
Assert.Empty(video.ADDITIONAL_ELEMENTS);
}
if (game.Sound != null)