Files
SabreTools/SabreHelper/Converters.cs
Matt Nadareski d8200433e0 Enable cascaded diffing of input files
Requested by @tractivo; allows for a series of DATs to be progressively pruned so that no dupes remain but the original info is intact.
2016-05-19 12:43:30 -07:00

319 lines
8.9 KiB
C#

using System;
using System.Linq;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Linq;
namespace SabreTools.Helper
{
/// <summary>
/// Provide DAT to XML conversion functionality
///
/// The following features have been requested:
/// - Implement converting from DOSCenter format
/// </summary>
public class Converters
{
// Regex matching patterns
private static string _headerPatternCMP = @"(^.*?) \($";
private static string _itemPatternCMP = @"^\s*(\S*?) (.*)";
private static string _endPatternCMP = @"^\s*\)\s*$";
/// <summary>
/// Convert a ClrMamePro style DAT to an Logiqx XML derived DAT
/// </summary>
/// <param name="filecontents">Array of strings representing the input file</param>
/// <returns>XElement representing the output XML DAT file</returns>
public static XElement ClrMameProToXML(string[] filecontents)
{
XElement elem = new XElement("datafile");
bool block = false;
for (int k = 0; k < filecontents.Length; k++)
{
string line = filecontents[k];
// Comments in CMP DATs start with a #
if (line.Trim().StartsWith("#"))
{
continue;
}
// If the line is the header or a game
if (Regex.IsMatch(line, _headerPatternCMP))
{
GroupCollection gc = Regex.Match(line, _headerPatternCMP).Groups;
if (gc[1].Value == "clrmamepro" || gc[1].Value == "romvault")
{
elem.Add(new XElement("header"));
elem = elem.Elements("header").Last();
}
else
{
elem.Add(new XElement(gc[1].Value));
elem = elem.Elements(gc[1].Value).Last();
}
block = true;
}
// If the line is a rom or disk and we're in a block
else if ((line.Trim().StartsWith("rom (") || line.Trim().StartsWith("disk (")) && block)
{
string[] gc = line.Trim().Split(' ');
XElement temp = new XElement(gc[0]);
// Loop over all attributes and add them if possible
bool quote = false;
string attrib = "", val = "";
for (int i = 2; i < gc.Length; i++)
{
//If the item is empty, we automatically skip it because it's a fluke
if (gc[i].Trim() == String.Empty)
{
continue;
}
// Special case for nodump...
else if (gc[i] == "nodump" && attrib != "status" && attrib != "flags")
{
temp.SetAttributeValue("status", "nodump");
}
// Even number of quotes, not in a quote, not in attribute
else if (Regex.Matches(gc[i], "\"").Count % 2 == 0 && !quote && attrib == "")
{
attrib = gc[i].Replace("\"", "");
}
// Even number of quotes, not in a quote, in attribute
else if (Regex.Matches(gc[i], "\"").Count % 2 == 0 && !quote && attrib != "")
{
temp.SetAttributeValue(attrib, gc[i].Replace("\"", ""));
attrib = "";
}
// Even number of quotes, in a quote, not in attribute
else if (Regex.Matches(gc[i], "\"").Count % 2 == 0 && quote && attrib == "")
{
// Attributes can't have quoted names
}
// Even number of quotes, in a quote, in attribute
else if (Regex.Matches(gc[i], "\"").Count % 2 == 0 && quote && attrib != "")
{
val += " " + gc[i];
}
// Odd number of quotes, not in a quote, not in attribute
else if (Regex.Matches(gc[i], "\"").Count % 2 == 1 && !quote && attrib == "")
{
// Attributes can't have quoted names
}
// Odd number of quotes, not in a quote, in attribute
else if (Regex.Matches(gc[i], "\"").Count % 2 == 1 && !quote && attrib != "")
{
val = gc[i].Replace("\"", "");
quote = true;
}
// Odd number of quotes, in a quote, not in attribute
else if (Regex.Matches(gc[i], "\"").Count % 2 == 1 && quote && attrib == "")
{
quote = false;
}
// Odd number of quotes, in a quote, in attribute
else if (Regex.Matches(gc[i], "\"").Count % 2 == 1 && quote && attrib != "")
{
val += " " + gc[i].Replace("\"", "");
temp.SetAttributeValue(attrib, val);
quote = false;
attrib = "";
val = "";
}
}
elem.Add(new XElement(temp));
}
// If the line is anything but a rom or disk and we're in a block
else if (Regex.IsMatch(line, _itemPatternCMP) && block)
{
GroupCollection gc = Regex.Match(line, _itemPatternCMP).Groups;
if (gc[1].Value == "name" && elem.Name != "header")
{
elem.SetAttributeValue(gc[1].Value, gc[2].Value.Replace("\"", ""));
elem.Add(new XElement("description", gc[2].Value.Replace("\"", "")));
}
else
{
elem.Add(new XElement(gc[1].Value, gc[2].Value.Replace("\"", "")));
}
}
// If we find an end bracket that's not associated with anything else, the block is done
else if (Regex.IsMatch(line, _endPatternCMP) && block)
{
block = false;
elem = elem.Parent;
}
}
return elem;
}
/// <summary>
/// Convert a RomCenter style DAT to an Logiqx XML derived DAT
/// </summary>
/// <param name="filecontents">Array of strings representing the input file</param>
/// <returns>XElement representing the output XML DAT file</returns>
public static XElement RomCenterToXML(string[] filecontents)
{
XElement elem = new XElement("datafile");
string blocktype = "";
string lastgame = null;
for (int k = 0; k < filecontents.Length; k++)
{
string line = filecontents[k];
// If the line is the start of the credits section
if (line.ToLowerInvariant().Contains("[credits]"))
{
blocktype = "credits";
if (elem.Name != "header")
{
elem.Add(new XElement("header"));
elem = elem.Elements("header").Last();
}
}
// If the line is the start of the dat section
else if (line.ToLowerInvariant().Contains("[dat]"))
{
blocktype = "dat";
if (elem.Name != "header")
{
elem.Add(new XElement("header"));
elem = elem.Elements("header").Last();
}
}
// If the line is the start of the emulator section
else if (line.ToLowerInvariant().Contains("[emulator]"))
{
blocktype = "emulator";
if (elem.Name != "header")
{
elem.Add(new XElement("header"));
elem = elem.Elements("header").Last();
}
}
// If the line is the start of the game section
else if (line.ToLowerInvariant().Contains("[games]"))
{
blocktype = "games";
}
// Otherwise, it's not a section and it's data, so get out all data
else
{
// If we have an author
if (line.StartsWith("author="))
{
elem.Add(new XElement("author", line.Split('=')[1]));
}
// If we have one of the three version tags
else if (line.StartsWith("version="))
{
switch (blocktype)
{
case "credits":
elem.Add(new XElement("version", line.Split('=')[1]));
break;
case "emulator":
elem.Add(new XElement("description", line.Split('=')[1]));
break;
}
}
// If we have a comment
else if (line.StartsWith("comment="))
{
elem.Add(new XElement("comment", line.Split('=')[1]));
}
// If we have the split flag
else if (line.StartsWith("split="))
{
int split = 0;
if (Int32.TryParse(line.Split('=')[1], out split))
{
if (split == 1)
{
XElement cmp = new XElement("clrmamepro");
cmp.Add(new XAttribute("forcemerging", "split"));
elem.Add(cmp);
}
}
}
// If we have the merge tag
else if (line.StartsWith("merge="))
{
int merge = 0;
if (Int32.TryParse(line.Split('=')[1], out merge))
{
if (merge == 1)
{
XElement cmp = new XElement("clrmamepro");
cmp.Add(new XAttribute("forcemerging", "full"));
elem.Add(cmp);
}
}
}
// If we have the refname tag
else if (line.StartsWith("refname="))
{
elem.Add(new XElement("name", line.Split('=')[1]));
}
// If we have a rom
else if (line.StartsWith("¬"))
{
/*
The rominfo order is as follows:
1 - parent name
2 - parent description
3 - game name
4 - game description
5 - rom name
6 - rom crc
7 - rom size
8 - romof name
9 - merge name
*/
string[] rominfo = line.Split('¬');
RomData rom = new RomData
{
Game = rominfo[3],
Name = rominfo[5],
CRC = rominfo[6],
Size = Int64.Parse(rominfo[7]),
};
if (lastgame != rom.Game)
{
elem = elem.Parent;
XElement current = new XElement("machine");
current.Add(new XAttribute("name", rom.Game));
current.Add(new XElement("description", rom.Game));
elem.Add(current);
}
elem = elem.Elements("machine").Last();
XElement romelem = new XElement("rom");
romelem.Add(new XAttribute("name", rom.Name));
romelem.Add(new XAttribute("size", rom.Size));
romelem.Add(new XAttribute("crc", rom.CRC));
elem.Add(romelem);
}
}
}
return elem.Parent;
}
}
}