mirror of
https://github.com/claunia/SabreTools.git
synced 2025-12-16 19:14:27 +00:00
Implement ClrMameProReader
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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*$";
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user