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> /// <summary>
/// Represents parsing and writing of a ClrMamePro DAT /// Represents parsing and writing of a ClrMamePro DAT
/// </summary> /// </summary>
/// TODO: Can there be a writer like XmlTextWriter for this? Or too inconsistent?
internal class ClrMamePro : DatFile internal class ClrMamePro : DatFile
{ {
/// <summary> /// <summary>
@@ -66,8 +65,7 @@ namespace SabreTools.Library.DatFiles
string normalizedValue = gc[1].Value.ToLowerInvariant(); string normalizedValue = gc[1].Value.ToLowerInvariant();
// If we have a known header // If we have a known header
if (normalizedValue == "clrmamepro" if (normalizedValue == "clrmamepro" || normalizedValue == "romvault")
|| normalizedValue == "romvault")
{ {
ReadHeader(sr, keep); ReadHeader(sr, keep);
} }
@@ -240,11 +238,11 @@ namespace SabreTools.Library.DatFiles
{ {
containsItems = true; containsItems = true;
ItemType temptype = ItemType.Rom; ItemType temptype = ItemType.Rom;
if (line.Trim().StartsWith("rom (")) if (trimmedline.StartsWith("rom ("))
temptype = ItemType.Rom; temptype = ItemType.Rom;
else if (line.Trim().StartsWith("disk (")) else if (trimmedline.StartsWith("disk ("))
temptype = ItemType.Disk; temptype = ItemType.Disk;
else if (line.Trim().StartsWith("sample")) else if (trimmedline.StartsWith("sample"))
temptype = ItemType.Sample; temptype = ItemType.Sample;
// Create the proper DatItem based on the type // 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 // Some dats don't have the space between "Name:" and the dat name
if (line.Trim().StartsWith("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(); line = reader.ReadLine();
continue; continue;
} }
@@ -198,7 +198,7 @@ namespace SabreTools.Library.DatFiles
&& linegc[i] != "date" && linegc[i] != "date"
&& linegc[i] != "crc") && linegc[i] != "crc")
{ {
item.Name += "{linegc[i]}"; item.Name += $"{linegc[i]}";
} }
// Perform correction // Perform correction

View File

@@ -566,6 +566,9 @@ namespace SabreTools.Library.Data
public const string XmlPattern = @"<(.*?)>(.*?)</(.*?)>"; public const string XmlPattern = @"<(.*?)>(.*?)</(.*?)>";
public const string HeaderPatternCMP = @"(^.*?) \($"; 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 ItemPatternCMP = @"^\s*(\S*?) (.*)";
public const string EndPatternCMP = @"^\s*\)\s*$"; public const string EndPatternCMP = @"^\s*\)\s*$";

View File

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

View File

@@ -1,12 +1,224 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Text.RegularExpressions;
using SabreTools.Library.Data;
using SabreTools.Library.Tools;
namespace SabreTools.Library.Readers 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 // Now we get each string, divided up as cleanly as possible
string[] matches = Regex string[] matches = Regex
//.Matches(s, @"([^\s]*""[^""]+""[^\s]*)|[^""]?\w+[^""]?") .Matches(s, Constants.InternalPatternAttributesCMP)
.Matches(s, @"[^\s""]+|""[^""]*""")
.Cast<Match>() .Cast<Match>()
.Select(m => m.Groups[0].Value) .Select(m => m.Groups[0].Value)
.ToArray(); .ToArray();

View File

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