Implement ClrMameProReader

This commit is contained in:
Matt Nadareski
2020-06-15 10:56:47 -07:00
parent 78340b6813
commit 0cc10e73df
7 changed files with 269 additions and 50 deletions

View File

@@ -15,7 +15,6 @@ namespace SabreTools.Library.DatFiles
/// <summary>
/// Represents parsing and writing of a ClrMamePro DAT
/// </summary>
/// TODO: Can there be a writer like XmlTextWriter for this? Or too inconsistent?
internal class ClrMamePro : DatFile
{
/// <summary>
@@ -66,8 +65,7 @@ namespace SabreTools.Library.DatFiles
string normalizedValue = gc[1].Value.ToLowerInvariant();
// If we have a known header
if (normalizedValue == "clrmamepro"
|| normalizedValue == "romvault")
if (normalizedValue == "clrmamepro" || normalizedValue == "romvault")
{
ReadHeader(sr, keep);
}
@@ -240,11 +238,11 @@ namespace SabreTools.Library.DatFiles
{
containsItems = true;
ItemType temptype = ItemType.Rom;
if (line.Trim().StartsWith("rom ("))
if (trimmedline.StartsWith("rom ("))
temptype = ItemType.Rom;
else if (line.Trim().StartsWith("disk ("))
else if (trimmedline.StartsWith("disk ("))
temptype = ItemType.Disk;
else if (line.Trim().StartsWith("sample"))
else if (trimmedline.StartsWith("sample"))
temptype = ItemType.Sample;
// Create the proper DatItem based on the type

View File

@@ -96,7 +96,7 @@ namespace SabreTools.Library.DatFiles
// Some dats don't have the space between "Name:" and the dat name
if (line.Trim().StartsWith("Name:"))
{
Name = (string.IsNullOrWhiteSpace(Name) ? line.Substring(6).Trim() : Name);
Name = (string.IsNullOrWhiteSpace(Name) ? line.Substring("Name:".Length).Trim() : Name);
line = reader.ReadLine();
continue;
}
@@ -198,7 +198,7 @@ namespace SabreTools.Library.DatFiles
&& linegc[i] != "date"
&& linegc[i] != "crc")
{
item.Name += "{linegc[i]}";
item.Name += $"{linegc[i]}";
}
// Perform correction

View File

@@ -566,6 +566,9 @@ namespace SabreTools.Library.Data
public const string XmlPattern = @"<(.*?)>(.*?)</(.*?)>";
public const string HeaderPatternCMP = @"(^.*?) \($";
public const string InternalPatternCMP = @"(^.*?) (\(.+\))$";
public const string InternalPatternAttributesCMP = @"[^\s""]+|""[^""]*""";
//public const string InternalPatternAttributesCMP = @"([^\s]*""[^""]+""[^\s]*)|[^""]?\w+[^""]?";
public const string ItemPatternCMP = @"^\s*(\S*?) (.*)";
public const string EndPatternCMP = @"^\s*\)\s*$";

View File

@@ -440,6 +440,18 @@
#region Reader related
/// <summary>
/// Different types of CMP rows being parsed
/// </summary>
public enum CmpRowType
{
None,
TopLevel,
Standalone,
Internal,
Comment,
}
/// <summary>
/// Different types of INI rows being parsed
/// </summary>

View File

@@ -1,12 +1,224 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
using SabreTools.Library.Data;
using SabreTools.Library.Tools;
namespace SabreTools.Library.Readers
{
public class ClrMameProReader
public class ClrMameProReader : IDisposable
{
/// <summary>
/// Internal stream reader for inputting
/// </summary>
private StreamReader sr;
/// <summary>
/// Get if at end of stream
/// </summary>
public bool EndOfStream
{
get
{
return sr?.EndOfStream ?? true;
}
}
/// <summary>
/// Contents of the currently read line as an internal item
/// </summary>
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;
/// <summary>
/// Get if we should be making DosCenter exceptions
/// </summary>
public bool DosCenter { get; set; } = false;
/// <summary>
/// Current row type
/// </summary>
public CmpRowType RowType { get; private set; } = CmpRowType.None;
/// <summary>
/// Contents of the currently read line as a standalone item
/// </summary>
public KeyValuePair<string, string>? Standalone { get; private set; } = null;
/// <summary>
/// Current top-level being read
/// </summary>
public string TopLevel { get; private set; } = string.Empty;
/// <summary>
/// Constructor for opening a write from a file
/// </summary>
public ClrMameProReader(string filename)
{
sr = new StreamReader(filename);
DosCenter = true;
}
/// <summary>
/// Constructor for opening a write from a stream and encoding
/// </summary>
public ClrMameProReader(Stream stream, Encoding encoding)
{
sr = new StreamReader(stream, encoding);
DosCenter = true;
}
/// <summary>
/// Read the next line in the file
/// </summary>
public bool ReadNextLine()
{
if (!(sr.BaseStream?.CanRead ?? false) || sr.EndOfStream)
return false;
string line = sr.ReadLine().Trim();
ProcessLine(line);
return true;
}
/// <summary>
/// Process the current line and extract out values
/// </summary>
private void ProcessLine(string line)
{
// Standalone (special case for DC dats)
if (line.StartsWith("Name:"))
{
string temp = line.Substring("Name:".Length).Trim();
line = $"Name: {temp}";
}
// Comment
if (line.StartsWith("#"))
{
Internal = null;
InternalName = null;
RowType = CmpRowType.Comment;
Standalone = null;
}
// Top-level
else if (Regex.IsMatch(line, Constants.HeaderPatternCMP))
{
GroupCollection gc = Regex.Match(line, Constants.HeaderPatternCMP).Groups;
string normalizedValue = gc[1].Value.ToLowerInvariant();
Internal = null;
InternalName = null;
RowType = CmpRowType.TopLevel;
Standalone = null;
TopLevel = normalizedValue;
}
// Internal
else if (Regex.IsMatch(line, Constants.InternalPatternCMP))
{
GroupCollection gc = Regex.Match(line, Constants.InternalPatternCMP).Groups;
string normalizedValue = gc[1].Value.ToLowerInvariant();
string[] linegc = Utilities.SplitLineAsCMP(gc[2].Value);
Internal = new Dictionary<string, string>();
for (int i = 0; i < linegc.Length; i++)
{
string key = linegc[i].Replace("\"", string.Empty);
if (string.IsNullOrWhiteSpace(key))
continue;
string value = string.Empty;
// Special case for DC-style dats, only a few known fields
if (DosCenter)
{
// If we have a name
if (key == "name")
{
while (++i < linegc.Length && linegc[i] != "size" && linegc[i] != "date" && linegc[i] != "crc")
{
value += $"{linegc[i]}";
}
value = value.Trim();
i--;
}
// If we have a date (split into 2 parts)
else if (key == "date")
{
value = $"{linegc[++i].Replace("\"", string.Empty)} {linegc[++i].Replace("\"", string.Empty)}";
}
}
else
{
// Special cases for standalone statuses
if (key == "baddump" || key == "good" || key == "nodump" || key == "verified")
{
value = key;
key = "status";
}
else
{
value = linegc[++i].Replace("\"", string.Empty);
}
}
Internal[key] = value;
RowType = CmpRowType.Internal;
Standalone = null;
}
InternalName = normalizedValue;
}
// Standalone
else if (Regex.IsMatch(line, Constants.ItemPatternCMP))
{
GroupCollection gc = Regex.Match(line, Constants.ItemPatternCMP).Groups;
string itemval = gc[2].Value.Replace("\"", string.Empty);
Internal = null;
InternalName = null;
RowType = CmpRowType.Standalone;
Standalone = new KeyValuePair<string, string>(gc[1].Value, itemval);
}
// End section
else if (Regex.IsMatch(line, Constants.EndPatternCMP))
{
Internal = null;
InternalName = null;
RowType = CmpRowType.None;
Standalone = null;
TopLevel = null;
}
// Invalid (usually whitespace)
else
{
Internal = null;
InternalName = null;
RowType = CmpRowType.None;
Standalone = null;
}
}
/// <summary>
/// Dispose of the underlying reader
/// </summary>
public void Dispose()
{
sr.Dispose();
}
}
}

View File

@@ -2865,8 +2865,7 @@ namespace SabreTools.Library.Tools
// Now we get each string, divided up as cleanly as possible
string[] matches = Regex
//.Matches(s, @"([^\s]*""[^""]+""[^\s]*)|[^""]?\w+[^""]?")
.Matches(s, @"[^\s""]+|""[^""]*""")
.Matches(s, Constants.InternalPatternAttributesCMP)
.Cast<Match>()
.Select(m => m.Groups[0].Value)
.ToArray();

View File

@@ -59,7 +59,7 @@ namespace SabreTools.Library.Writers
/// <summary>
/// Internal stream writer
/// </summary>
private StreamWriter textWriter;
private StreamWriter sw;
/// <summary>
/// Stack for tracking current node
@@ -107,20 +107,9 @@ namespace SabreTools.Library.Writers
/// </summary>
public ClrMameProWriter(string filename)
{
textWriter = new StreamWriter(filename);
sw = new StreamWriter(filename);
Quotes = true;
stack = new TagInfo[10];
top = 0;
}
/// <summary>
/// Constructor for opening a write from a stream and encoding
/// </summary>
public ClrMameProWriter(Stream stream, Encoding encoding)
{
textWriter = new StreamWriter(stream, encoding);
Quotes = true;
// Element stack
stack = new TagInfo[10];
top = 0;
@@ -128,11 +117,17 @@ namespace SabreTools.Library.Writers
}
/// <summary>
/// Base stream for easy access
/// Constructor for opening a write from a stream and encoding
/// </summary>
public Stream BaseStream
public ClrMameProWriter(Stream stream, Encoding encoding)
{
get { return textWriter?.BaseStream ?? null; }
sw = new StreamWriter(stream, encoding);
Quotes = true;
// Element stack
stack = new TagInfo[10];
top = 0;
stack[top].Init();
}
/// <summary>
@@ -145,8 +140,8 @@ namespace SabreTools.Library.Writers
AutoComplete(Token.StartElement);
PushStack();
stack[top].Name = name;
textWriter.Write(name);
textWriter.Write(" (");
sw.Write(name);
sw.Write(" (");
}
catch
{
@@ -189,10 +184,10 @@ namespace SabreTools.Library.Writers
try
{
AutoComplete(Token.StartAttribute);
textWriter.Write(name);
textWriter.Write(" ");
sw.Write(name);
sw.Write(" ");
if (Quotes)
textWriter.Write("\"");
sw.Write("\"");
}
catch
{
@@ -238,18 +233,18 @@ namespace SabreTools.Library.Writers
throw new ArgumentException();
AutoComplete(Token.Standalone);
textWriter.Write(name);
textWriter.Write(" ");
sw.Write(name);
sw.Write(" ");
if ((quoteOverride == null && Quotes)
|| (quoteOverride == true))
{
textWriter.Write("\"");
sw.Write("\"");
}
textWriter.Write(value);
sw.Write(value);
if ((quoteOverride == null && Quotes)
|| (quoteOverride == true))
{
textWriter.Write("\"");
sw.Write("\"");
}
}
catch
@@ -269,7 +264,7 @@ namespace SabreTools.Library.Writers
if (!string.IsNullOrEmpty(value))
{
AutoComplete(Token.Content);
textWriter.Write(value);
sw.Write(value);
}
}
catch
@@ -295,7 +290,7 @@ namespace SabreTools.Library.Writers
finally
{
currentState = State.Closed;
textWriter.Close();
sw.Close();
}
}
@@ -305,7 +300,7 @@ namespace SabreTools.Library.Writers
public void Dispose()
{
Close();
textWriter.Dispose();
sw.Dispose();
}
/// <summary>
@@ -313,7 +308,7 @@ namespace SabreTools.Library.Writers
/// </summary>
public void Flush()
{
textWriter.Flush();
sw.Flush();
}
/// <summary>
@@ -367,11 +362,11 @@ namespace SabreTools.Library.Writers
if (currentState == State.Attribute)
{
WriteEndAttributeQuote();
textWriter.Write(' ');
sw.Write(' ');
}
else if (currentState == State.Element)
{
textWriter.Write(' ');
sw.Write(' ');
}
break;
@@ -422,7 +417,7 @@ namespace SabreTools.Library.Writers
if (this.lastToken == Token.LongEndElement)
{
Indent(true);
textWriter.Write(')');
sw.Write(')');
}
top--;
@@ -440,7 +435,7 @@ namespace SabreTools.Library.Writers
private void WriteEndStartTag(bool empty)
{
if (empty)
textWriter.Write(" )");
sw.Write(" )");
}
/// <summary>
@@ -449,7 +444,7 @@ namespace SabreTools.Library.Writers
private void WriteEndAttributeQuote()
{
if (Quotes)
textWriter.Write("\"");
sw.Write("\"");
}
/// <summary>
@@ -459,15 +454,15 @@ namespace SabreTools.Library.Writers
{
if (top == 0)
{
textWriter.WriteLine();
sw.WriteLine();
}
else if (!stack[top].Mixed)
{
textWriter.WriteLine();
sw.WriteLine();
int i = beforeEndElement ? top - 1 : top;
for (; i > 0; i--)
{
textWriter.Write('\t');
sw.Write('\t');
}
}
}