Add listrom deserialization test, fix issues

This commit is contained in:
Matt Nadareski
2023-07-13 17:52:25 -04:00
parent 025bea69a1
commit d55f35d3fc
7 changed files with 257 additions and 8 deletions

View File

@@ -34,9 +34,9 @@ namespace SabreTools.DatFiles.Formats
/// abcd.bin 1024 CRC(00000000) SHA1(da39a3ee5e6b4b0d3255bfef95601890afd80709) /// abcd.bin 1024 CRC(00000000) SHA1(da39a3ee5e6b4b0d3255bfef95601890afd80709)
/// efgh.bin 1024 BAD CRC(00000000) SHA1(da39a3ee5e6b4b0d3255bfef95601890afd80709) BAD_DUMP /// efgh.bin 1024 BAD CRC(00000000) SHA1(da39a3ee5e6b4b0d3255bfef95601890afd80709) BAD_DUMP
/// ijkl.bin 1024 NO GOOD DUMP KNOWN /// ijkl.bin 1024 NO GOOD DUMP KNOWN
/// abcd.chd SHA1(da39a3ee5e6b4b0d3255bfef95601890afd80709) /// abcd SHA1(da39a3ee5e6b4b0d3255bfef95601890afd80709)
/// efgh.chd BAD (da39a3ee5e6b4b0d3255bfef95601890afd80709) BAD_DUMP /// efgh BAD (da39a3ee5e6b4b0d3255bfef95601890afd80709) BAD_DUMP
/// ijkl.chd NO GOOD DUMP KNOWN /// ijkl NO GOOD DUMP KNOWN
/// </remarks> /// </remarks>
public override void ParseFile(string filename, int indexId, bool keep, bool statsOnly = false, bool throwOnError = false) public override void ParseFile(string filename, int indexId, bool keep, bool statsOnly = false, bool throwOnError = false)
{ {

View File

@@ -0,0 +1,14 @@
namespace SabreTools.Models.Listrom
{
public class Dat
{
public Set[]? Set { get; set; }
#region DO NOT USE IN PRODUCTION
/// <remarks>Should be empty</remarks>
public string[]? ADDITIONAL_ELEMENTS { get; set; }
#endregion
}
}

View File

@@ -6,15 +6,15 @@ namespace SabreTools.Models.Listrom
/// abcd.bin 1024 CRC(00000000) SHA1(da39a3ee5e6b4b0d3255bfef95601890afd80709) /// abcd.bin 1024 CRC(00000000) SHA1(da39a3ee5e6b4b0d3255bfef95601890afd80709)
/// efgh.bin 1024 BAD CRC(00000000) SHA1(da39a3ee5e6b4b0d3255bfef95601890afd80709) BAD_DUMP /// efgh.bin 1024 BAD CRC(00000000) SHA1(da39a3ee5e6b4b0d3255bfef95601890afd80709) BAD_DUMP
/// ijkl.bin 1024 NO GOOD DUMP KNOWN /// ijkl.bin 1024 NO GOOD DUMP KNOWN
/// abcd.chd SHA1(da39a3ee5e6b4b0d3255bfef95601890afd80709) /// abcd SHA1(da39a3ee5e6b4b0d3255bfef95601890afd80709)
/// efgh.chd BAD (da39a3ee5e6b4b0d3255bfef95601890afd80709) BAD_DUMP /// efgh BAD SHA1(da39a3ee5e6b4b0d3255bfef95601890afd80709) BAD_DUMP
/// ijkl.chd NO GOOD DUMP KNOWN /// ijkl NO GOOD DUMP KNOWN
/// </summary> /// </summary>
public class Row public class Row
{ {
public string Name { get; set; } public string Name { get; set; }
public long? Size { get; set; } public string? Size { get; set; }
public bool Bad { get; set; } public bool Bad { get; set; }

View File

@@ -0,0 +1,21 @@
namespace SabreTools.Models.Listrom
{
/// <summary>
/// ROMs required for driver "testdriver".
/// Name Size Checksum
/// abcd.bin 1024 CRC(00000000) SHA1(da39a3ee5e6b4b0d3255bfef95601890afd80709)
/// efgh.bin 1024 BAD CRC(00000000) SHA1(da39a3ee5e6b4b0d3255bfef95601890afd80709) BAD_DUMP
/// ijkl.bin 1024 NO GOOD DUMP KNOWN
/// abcd SHA1(da39a3ee5e6b4b0d3255bfef95601890afd80709)
/// efgh BAD SHA1(da39a3ee5e6b4b0d3255bfef95601890afd80709) BAD_DUMP
/// ijkl NO GOOD DUMP KNOWN
/// </summary>
public class Set
{
public string? Driver { get; set; }
public string? Device { get; set; }
public Row[]? Row { get; set; }
}
}

View File

@@ -0,0 +1,197 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using SabreTools.Models.Listrom;
namespace SabreTools.Serialization
{
/// <summary>
/// Serializer for MAME listrom files
/// </summary>
public class Listrom
{
/// <summary>
/// Deserializes a MAME listrom file to the defined type
/// </summary>
/// <param name="path">Path to the file to deserialize</param>
/// <returns>Deserialized data on success, null on failure</returns>
public static Dat? Deserialize(string path)
{
try
{
using var stream = PathProcessor.OpenStream(path);
return Deserialize(stream);
}
catch
{
// TODO: Handle logging the exception
return default;
}
}
/// <summary>
/// Deserializes a MAME listrom file in a stream to the defined type
/// </summary>
/// <param name="stream">Stream to deserialize</param>
/// <returns>Deserialized data on success, null on failure</returns>
public static Dat? Deserialize(Stream? stream)
{
try
{
// If the stream is null
if (stream == null)
return default;
// Setup the reader and output
var reader = new StreamReader(stream, Encoding.UTF8);
var dat = new Dat();
Set? set = null;
var sets = new List<Set?>();
var rows = new List<Row?>();
var additional = new List<string>();
while (!reader.EndOfStream)
{
// Read the line and don't split yet
string? line = reader.ReadLine();
if (string.IsNullOrWhiteSpace(line))
{
// If we have a set to process
if (set != null)
{
set.Row = rows.ToArray();
sets.Add(set);
set = null;
rows.Clear();
}
continue;
}
// Set lines are unique
if (line.StartsWith("ROMs required for driver"))
{
string driver = line["ROMs required for driver".Length..].Trim('"', ' ', '.');
set = new Set { Driver = driver };
continue;
}
else if (line.StartsWith("No ROMs required for driver"))
{
string driver = line["No ROMs required for driver".Length..].Trim('"', ' ', '.');
set = new Set { Driver = driver };
continue;
}
else if (line.StartsWith("ROMs required for device"))
{
string device = line["ROMs required for device".Length..].Trim('"', ' ', '.');
set = new Set { Device = device };
continue;
}
else if (line.StartsWith("No ROMs required for device"))
{
string device = line["No ROMs required for device".Length..].Trim('"', ' ', '.');
set = new Set { Device = device };
continue;
}
else if (line.Equals("Name Size Checksum", StringComparison.OrdinalIgnoreCase))
{
// No-op
continue;
}
// Split the line for the name iteratively
string[]? lineParts = line?.Split(" ", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (lineParts?.Length == 1)
lineParts = line?.Split(" ", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (lineParts?.Length == 1)
lineParts = line?.Split(" ", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
if (lineParts?.Length == 1)
lineParts = line?.Split(" ", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
// Read the name and set the rest of the line for processing
string name = lineParts[0];
string trimmedLine = line[name.Length..];
lineParts = trimmedLine?.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
// The number of items in the row explains what type of row it is
var row = new Row();
switch (lineParts.Length)
{
// Normal CHD (Name, SHA1)
case 1:
row.Name = name;
row.SHA1 = lineParts[0]["SHA1".Length..].Trim('(', ')');
break;
// Normal ROM (Name, Size, CRC, SHA1)
case 3 when line.Contains("CRC"):
row.Name = name;
row.Size = lineParts[0];
row.CRC = lineParts[1]["CRC".Length..].Trim('(', ')');
row.SHA1 = lineParts[2]["SHA1".Length..].Trim('(', ')');
break;
// Bad CHD (Name, BAD, SHA1, BAD_DUMP)
case 3 when line.Contains("BAD_DUMP"):
row.Name = name;
row.Bad = true;
row.SHA1 = lineParts[1]["SHA1".Length..].Trim('(', ')');
break;
// Nodump CHD (Name, NO GOOD DUMP KNOWN)
case 4 when line.Contains("NO GOOD DUMP KNOWN"):
row.Name = name;
row.NoGoodDumpKnown = true;
break;
// Bad ROM (Name, Size, BAD, CRC, SHA1, BAD_DUMP)
case 5 when line.Contains("BAD_DUMP"):
row.Name = name;
row.Size = lineParts[0];
row.Bad = true;
row.CRC = lineParts[2]["CRC".Length..].Trim('(', ')');
row.SHA1 = lineParts[3]["SHA1".Length..].Trim('(', ')');
break;
// Nodump ROM (Name, Size, NO GOOD DUMP KNOWN)
case 5 when line.Contains("NO GOOD DUMP KNOWN"):
row.Name = name;
row.Size = lineParts[0];
row.NoGoodDumpKnown = true;
break;
default:
row = null;
additional.Add(line);
break;
}
if (row != null)
rows.Add(row);
}
// If we have a set to process
if (set != null)
{
set.Row = rows.ToArray();
sets.Add(set);
set = null;
rows.Clear();
}
// Add extra pieces and return
dat.Set = sets.ToArray();
dat.ADDITIONAL_ELEMENTS = additional.ToArray();
return dat;
}
catch
{
// TODO: Handle logging the exception
return default;
}
}
}
}

View File

@@ -98,7 +98,7 @@ namespace SabreTools.Test.Parser
[Theory] [Theory]
[InlineData("test-sfv.sfv", Hash.CRC)] [InlineData("test-sfv.sfv", Hash.CRC)]
[InlineData("test-md5.md5", Hash.MD5)] [InlineData("test-md5.md5", Hash.MD5)]
[InlineData("test-sha1.sha1", Hash.SHA1)] [InlineData("test-sha1.sha1", Hash.SHA1)]
[InlineData("test-sha256.sha256", Hash.SHA256)] [InlineData("test-sha256.sha256", Hash.SHA256)]
[InlineData("test-sha384.sha384", Hash.SHA384)] [InlineData("test-sha384.sha384", Hash.SHA384)]
[InlineData("test-sha512.sha512", Hash.SHA512)] [InlineData("test-sha512.sha512", Hash.SHA512)]
@@ -142,6 +142,23 @@ namespace SabreTools.Test.Parser
} }
} }
[Fact]
public void ListromDeserializeTest()
{
// Open the file for reading
string filename = System.IO.Path.Combine(Environment.CurrentDirectory, "TestData", "test-listrom-files.txt.gz");
// Deserialize the file
var dat = Serialization.Listrom.Deserialize(filename);
// Validate the values
Assert.NotNull(dat?.Set);
Assert.Equal(45861, dat.Set.Length);
// Validate we're not missing any attributes or elements
Assert.Empty(dat.ADDITIONAL_ELEMENTS);
}
[Fact] [Fact]
public void ListxmlDeserializeTest() public void ListxmlDeserializeTest()
{ {

Binary file not shown.