Files
Matt Nadareski 7689c6dd07 Libraries
This change looks dramatic, but it's just separating out the already-split namespaces into separate top-level folders. In theory, every single one could be built into their own Nuget package. `SabreTools.Serialization` still builds the normal Nuget package that is used by all other projects and includes all namespaces.
2026-03-21 16:26:56 -04:00

259 lines
11 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using SabreTools.Data.Models.Listrom;
#pragma warning disable IDE0057 // Use range operator
namespace SabreTools.Serialization.Readers
{
public class Listrom : BaseBinaryReader<MetadataFile>
{
/// <inheritdoc/>
public override MetadataFile? Deserialize(Stream? data)
{
// If the data is invalid
if (data is null || !data.CanRead)
return default;
try
{
// Setup the reader and output
var reader = new StreamReader(data, Encoding.UTF8);
var dat = new MetadataFile();
Set? set = null;
var sets = new List<Set>();
var rows = new List<Row>();
while (!reader.EndOfStream)
{
// Read the line and don't split yet
string? line = reader.ReadLine();
if (string.IsNullOrEmpty(line))
{
// If we have a set to process
if (set is not null)
{
set.Row = [.. rows];
sets.Add(set);
set = null;
rows.Clear();
}
continue;
}
// Set lines are unique
if (line.StartsWith("ROMs required for driver"))
{
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
string driver = line["ROMs required for driver".Length..].Trim('"', ' ', '.');
#else
string driver = line.Substring("ROMs required for driver".Length).Trim('"', ' ', '.');
#endif
set = new Set { Driver = driver };
continue;
}
else if (line.StartsWith("No ROMs required for driver"))
{
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
string driver = line["No ROMs required for driver".Length..].Trim('"', ' ', '.');
#else
string driver = line.Substring("No ROMs required for driver".Length).Trim('"', ' ', '.');
#endif
set = new Set { Driver = driver };
continue;
}
else if (line.StartsWith("ROMs required for device"))
{
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
string device = line["ROMs required for device".Length..].Trim('"', ' ', '.');
#else
string device = line.Substring("ROMs required for device".Length).Trim('"', ' ', '.');
#endif
set = new Set { Device = device };
continue;
}
else if (line.StartsWith("No ROMs required for device"))
{
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
string device = line["No ROMs required for device".Length..].Trim('"', ' ', '.');
#else
string device = line.Substring("No ROMs required for device".Length).Trim('"', ' ', '.');
#endif
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
#if NETFRAMEWORK || NETCOREAPP3_1 || NETSTANDARD2_0_OR_GREATER
string[] lineParts = line.Split([" "], StringSplitOptions.RemoveEmptyEntries);
if (lineParts.Length == 1)
lineParts = line.Split([" "], StringSplitOptions.RemoveEmptyEntries);
if (lineParts.Length == 1)
lineParts = line.Split([" "], StringSplitOptions.RemoveEmptyEntries);
if (lineParts.Length == 1)
lineParts = line.Split([" "], StringSplitOptions.RemoveEmptyEntries);
#else
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);
#endif
// Read the name and set the rest of the line for processing
string name = lineParts[0];
string trimmedLine = line.Substring(name.Length);
if (trimmedLine is null)
continue;
#if NETFRAMEWORK || NETCOREAPP3_1 || NETSTANDARD2_0_OR_GREATER
lineParts = trimmedLine.Split([' '], StringSplitOptions.RemoveEmptyEntries);
#else
lineParts = trimmedLine.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
#endif
// The number of items in the row explains what type of row it is
var row = new Row();
switch (lineParts.Length)
{
// Normal CHD (Name, MD5/SHA1)
case 1:
row.Name = name;
if (line.Contains("MD5("))
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
row.MD5 = lineParts[0]["MD5".Length..].Trim('(', ')');
#else
row.MD5 = lineParts[0].Substring("MD5".Length).Trim('(', ')');
#endif
else
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
row.SHA1 = lineParts[0]["SHA1".Length..].Trim('(', ')');
#else
row.SHA1 = lineParts[0].Substring("SHA1".Length).Trim('(', ')');
#endif
break;
// Normal ROM (Name, Size, CRC, MD5/SHA1)
case 3 when line.Contains("CRC"):
row.Name = name;
row.Size = lineParts[0];
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
row.CRC = lineParts[1]["CRC".Length..].Trim('(', ')');
#else
row.CRC = lineParts[1].Substring("CRC".Length).Trim('(', ')');
#endif
if (line.Contains("MD5("))
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
row.MD5 = lineParts[2]["MD5".Length..].Trim('(', ')');
#else
row.MD5 = lineParts[2].Substring("MD5".Length).Trim('(', ')');
#endif
else
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
row.SHA1 = lineParts[2]["SHA1".Length..].Trim('(', ')');
#else
row.SHA1 = lineParts[2].Substring("SHA1".Length).Trim('(', ')');
#endif
break;
// Bad CHD (Name, BAD, SHA1, BAD_DUMP)
case 3 when line.Contains("BAD_DUMP"):
row.Name = name;
row.Bad = true;
if (line.Contains("MD5("))
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
row.MD5 = lineParts[1]["MD5".Length..].Trim('(', ')');
#else
row.MD5 = lineParts[1].Substring("MD5".Length).Trim('(', ')');
#endif
else
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
row.SHA1 = lineParts[1]["SHA1".Length..].Trim('(', ')');
#else
row.SHA1 = lineParts[1].Substring("SHA1".Length).Trim('(', ')');
#endif
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, MD5/SHA1, BAD_DUMP)
case 5 when line.Contains("BAD_DUMP"):
row.Name = name;
row.Size = lineParts[0];
row.Bad = true;
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
row.CRC = lineParts[2]["CRC".Length..].Trim('(', ')');
#else
row.CRC = lineParts[2].Substring("CRC".Length).Trim('(', ')');
#endif
if (line.Contains("MD5("))
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
row.MD5 = lineParts[3]["MD5".Length..].Trim('(', ')');
#else
row.MD5 = lineParts[3].Substring("MD5".Length).Trim('(', ')');
#endif
else
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
row.SHA1 = lineParts[3]["SHA1".Length..].Trim('(', ')');
#else
row.SHA1 = lineParts[3].Substring("SHA1".Length).Trim('(', ')');
#endif
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;
break;
}
if (row is not null)
rows.Add(row);
}
// If we have a set to process
if (set is not null)
{
set.Row = [.. rows];
sets.Add(set);
set = null;
rows.Clear();
}
// Add extra pieces and return
if (sets.Count > 0)
{
dat.Set = [.. sets];
return dat;
}
return null;
}
catch
{
// Ignore the actual error
return null;
}
}
}
}