Use centralized DAT parsing and manipulate data after

Though the change doesn't reflect this, RomManipulation is a renamed Sorting with an addtional method, Parse.
This commit is contained in:
Matt Nadareski
2016-04-19 01:11:23 -07:00
parent 06517988b5
commit 14d229bc1d
7 changed files with 368 additions and 510 deletions

View File

@@ -255,8 +255,8 @@ JOIN checksums
// If we're in a merged mode, merge and then resort by the correct parameters
if (merged)
{
roms = Sorting.RomMerge(roms, true);
Sorting.RomSort(roms, _norename);
roms = RomManipulation.Merge(roms, true);
RomManipulation.Sort(roms, _norename);
}
// Now check rename within games

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Data.SQLite;
using System.IO;
using System.Text.RegularExpressions;
@@ -377,156 +378,24 @@ namespace SabreTools
}
}
// Attempt to load the current file as XML
bool superdat = false;
XmlDocument doc = new XmlDocument();
try
{
doc.LoadXml(File.ReadAllText(_filepath));
}
catch (XmlException)
{
doc.LoadXml(Converters.RomVaultToXML(File.ReadAllLines(_filepath)).ToString());
}
// Get all roms that are found in the DAT to see what needs to be added
List<RomData> roms = RomManipulation.Parse(_filepath, sysid, srcid, _logger);
// Experimental looping using only XML parsing
XmlNode node = doc.FirstChild;
if (node != null && node.Name == "xml")
// Sort and loop over all roms, checking for adds
RomManipulation.Sort(roms, true);
string lastgame = "";
long gameid = -1;
foreach (RomData rom in roms)
{
// Skip over everything that's not an element
while (node.NodeType != XmlNodeType.Element)
// If we have a new game, check for a new ID
if (rom.Game != lastgame)
{
node = node.NextSibling;
}
}
// Once we find the main body, enter it
if (node != null && (node.Name == "datafile" || node.Name == "softwarelist"))
{
node = node.FirstChild;
}
// Skip the header if it exists
if (node != null && node.Name == "header")
{
// Check for SuperDAT mode
if (node.SelectSingleNode("name").InnerText.Contains(" - SuperDAT"))
{
superdat = true;
gameid = AddGame(sysid, rom.Game, srcid);
lastgame = rom.Game;
}
// Skip over anything that's not an element
while (node.NodeType != XmlNodeType.Element)
{
node = node.NextSibling;
}
}
while (node != null)
{
if (node.NodeType == XmlNodeType.Element && (node.Name == "machine" || node.Name == "game" || node.Name == "software"))
{
long gameid = -1;
string tempname = "";
if (node.Name == "software")
{
tempname = node.SelectSingleNode("description").InnerText;
}
else
{
// There are rare cases where a malformed XML will not have the required attributes. We can only skip them.
if (node.Attributes.Count == 0)
{
_logger.Error(@"A node with malformed XML has been found!
For RV DATs, please make sure that all names and descriptions are quoted.
For XML DATs, make sure that the DAT has all required information.");
node = node.NextSibling;
continue;
}
tempname = node.Attributes["name"].Value;
}
if (superdat)
{
tempname = Regex.Match(tempname, @".*?\\(.*)").Groups[1].Value;
}
gameid = AddGame(sysid, tempname, srcid);
// Get the roms from the machine
if (node.HasChildNodes)
{
// If this node has children, traverse the children
foreach (XmlNode child in node.ChildNodes)
{
// If we find a rom or disk, add it
if (child.NodeType == XmlNodeType.Element && (child.Name == "rom" || child.Name == "disk"))
{
// Take care of hex-sized files
long size = -1;
if (child.Attributes["size"] != null && child.Attributes["size"].Value.Contains("0x"))
{
size = Convert.ToInt64(child.Attributes["size"].Value, 16);
}
else if (child.Attributes["size"] != null)
{
size = Int64.Parse(child.Attributes["size"].Value);
}
AddRom(
child.Name,
gameid,
child.Attributes["name"].Value,
date,
size,
(child.Attributes["crc"] != null ? child.Attributes["crc"].Value.ToLowerInvariant().Trim() : ""),
(child.Attributes["md5"] != null ? child.Attributes["md5"].Value.ToLowerInvariant().Trim() : ""),
(child.Attributes["sha1"] != null ? child.Attributes["sha1"].Value.ToLowerInvariant().Trim() : "")
);
}
// If we find the signs of a software list, traverse the children
else if (child.NodeType == XmlNodeType.Element && child.Name == "part" && child.HasChildNodes)
{
foreach (XmlNode part in child.ChildNodes)
{
// If we find a dataarea, traverse the children
if (part.NodeType == XmlNodeType.Element && part.Name == "dataarea")
{
foreach (XmlNode data in part.ChildNodes)
{
// If we find a rom or disk, add it
if (data.NodeType == XmlNodeType.Element && (data.Name == "rom" || data.Name == "disk") && data.Attributes["name"] != null)
{
// Take care of hex-sized files
long size = -1;
if (data.Attributes["size"] != null && data.Attributes["size"].Value.Contains("0x"))
{
size = Convert.ToInt64(data.Attributes["size"].Value, 16);
}
else if (data.Attributes["size"] != null)
{
size = Int64.Parse(data.Attributes["size"].Value);
}
AddRom(
data.Name,
gameid,
data.Attributes["name"].Value,
date,
size,
(data.Attributes["crc"] != null ? data.Attributes["crc"].Value.ToLowerInvariant().Trim() : ""),
(data.Attributes["md5"] != null ? data.Attributes["md5"].Value.ToLowerInvariant().Trim() : ""),
(data.Attributes["sha1"] != null ? data.Attributes["sha1"].Value.ToLowerInvariant().Trim() : "")
);
}
}
}
}
}
}
}
}
node = node.NextSibling;
// Try to add the rom with the game information
AddRom(rom, gameid, date);
}
return true;
@@ -598,28 +467,23 @@ namespace SabreTools
/// <summary>
/// Add a file to the database if it doesn't already exist
/// </summary>
/// <param name="romtype">File type (either "rom" or "disk")</param>
/// <param name="rom">RomData object representing the rom</param>
/// <param name="gameid">ID of the parent game to be mapped to</param>
/// <param name="name">File name</param>
/// <param name="date">Last updated date</param>
/// <param name="size">File size in bytes</param>
/// <param name="crc">CRC32 hash of the file</param>
/// <param name="md5">MD5 hash of the file</param>
/// <param name="sha1">SHA-1 hash of the file</param>
/// <returns>True if the file exists or could be added, false on error</returns>
private bool AddRom(string romtype, long gameid, string name, string date, long size, string crc, string md5, string sha1)
private bool AddRom(RomData rom, long gameid, string date)
{
// WOD origninally stripped out any subdirs from the imported files, we do the same
name = Path.GetFileName(name);
rom.Name = Path.GetFileName(rom.Name);
// Run the name through the filters to make sure that it's correct
name = Style.NormalizeChars(name);
name = Style.RussianToLatin(name);
name = Regex.Replace(name, @"(.*) \.(.*)", "$1.$2");
rom.Name = Style.NormalizeChars(rom.Name);
rom.Name = Style.RussianToLatin(rom.Name);
rom.Name = Regex.Replace(rom.Name, @"(.*) \.(.*)", "$1.$2");
if (romtype != "rom" && romtype != "disk")
if (rom.Type != "rom" && rom.Type != "disk")
{
romtype = "rom";
rom.Type = "rom";
}
// Check to see if this exact file is in the database already
@@ -627,13 +491,13 @@ namespace SabreTools
SELECT files.id FROM files
JOIN checksums
ON files.id=checksums.file
WHERE files.name='" + name.Replace("'", "''") + @"'
AND files.type='" + romtype + @"'
WHERE files.name='" + rom.Name.Replace("'", "''") + @"'
AND files.type='" + rom.Type + @"'
AND files.setid=" + gameid + " " +
" AND checksums.size=" + size +
" AND checksums.crc='" + crc + "'" +
" AND checksums.md5='" + md5 + "'" +
" AND checksums.sha1='" + sha1 + "'";
" AND checksums.size=" + rom.Size +
" AND checksums.crc='" + rom.CRC + "'" +
" AND checksums.md5='" + rom.MD5 + "'" +
" AND checksums.sha1='" + rom.SHA1 + "'";
using (SQLiteConnection dbc = new SQLiteConnection(_connectionString))
{
dbc.Open();
@@ -646,7 +510,7 @@ SELECT files.id FROM files
{
query = @"
INSERT INTO files (setid, name, type, lastupdated)
VALUES (" + gameid + ", '" + name.Replace("'", "''") + "', '" + romtype + "', '" + date + "')";
VALUES (" + gameid + ", '" + rom.Name.Replace("'", "''") + "', '" + rom.Type + "', '" + date + "')";
using (SQLiteCommand slc2 = new SQLiteCommand(query, dbc))
{
int affected = slc2.ExecuteNonQuery();
@@ -662,7 +526,7 @@ INSERT INTO files (setid, name, type, lastupdated)
}
query = @"INSERT INTO checksums (file, size, crc, md5, sha1) VALUES (" +
romid + ", " + size + ", '" + crc + "'" + ", '" + md5 + "'" + ", '" + sha1 + "')";
romid + ", " + rom.Size + ", '" + rom.CRC + "'" + ", '" + rom.MD5 + "'" + ", '" + rom.SHA1 + "')";
using (SQLiteCommand slc3 = new SQLiteCommand(query, dbc))
{
affected = slc3.ExecuteNonQuery();
@@ -671,14 +535,14 @@ INSERT INTO files (setid, name, type, lastupdated)
// If the insert of the checksums failed, that's bad
if (affected < 1)
{
_logger.Error("There was an error adding checksums for " + name + " to the database!");
_logger.Error("There was an error adding checksums for " + rom.Name + " to the database!");
return false;
}
}
// Otherwise, something happened which is bad
else
{
_logger.Error("There was an error adding " + name + " to the database!");
_logger.Error("There was an error adding " + rom.Name + " to the database!");
return false;
}
}

View File

@@ -32,142 +32,47 @@ namespace DatSplit
return;
}
Logger logger = new Logger(false, "datsplit.log");
logger.Start();
// Output the title
Build.Start("DatSplit");
// Set needed strings
// Set needed variables
_filename = args[0];
_extA = (args[1].StartsWith(".") ? args[1] : "." + args[1]).ToUpperInvariant();
_extB = (args[2].StartsWith(".") ? args[2] : "." + args[2]).ToUpperInvariant();
List<RomData> romsA = new List<RomData>();
List<RomData> romsB = new List<RomData>();
// Take the filename, and load it as an XML document
XmlDocument doc = new XmlDocument();
try
{
doc.LoadXml(File.ReadAllText(_filename));
}
catch (XmlException)
{
doc.LoadXml(Converters.RomVaultToXML(File.ReadAllLines(_filename)).ToString());
}
// Load the current DAT to be processed
List<RomData> roms = RomManipulation.Parse(_filename, 0, 0, logger);
// We all start the same
XmlNode node = doc.FirstChild;
if (node != null && node.Name == "xml")
// Now separate the roms accordingly
foreach (RomData rom in roms)
{
// Skip over everything that's not an element
while (node.NodeType != XmlNodeType.Element)
if (rom.Name.ToUpperInvariant().EndsWith(_extA))
{
node = node.NextSibling;
romsA.Add(rom);
}
}
XmlDocument outDocA = new XmlDocument();
outDocA.AppendChild(outDocA.CreateXmlDeclaration("1.0", Encoding.UTF8.WebName, null));
outDocA.AppendChild(outDocA.CreateDocumentType("datafile", "-//Logiqx//DTD ROM Management Datafile//EN", "http://www.logiqx.com/Dats/datafile.dtd", null));
XmlNode outA = outDocA.CreateNode(XmlNodeType.Element, node.Name, "");
XmlDocument outDocB = new XmlDocument();
outDocB.AppendChild(outDocB.CreateXmlDeclaration("1.0", Encoding.UTF8.WebName, null));
outDocB.AppendChild(outDocB.CreateDocumentType("datafile", "-//Logiqx//DTD ROM Management Datafile//EN", "http://www.logiqx.com/Dats/datafile.dtd", null));
XmlNode outB = outDocB.CreateNode(XmlNodeType.Element, node.Name, "");
// Once we find the main body, enter it
if (node != null && node.Name == "datafile")
{
node = node.FirstChild;
}
// Now here's where it differs from import
while (node != null)
{
// If we're at a game node, add the parent node but not all the internals
if (node.NodeType == XmlNodeType.Element && (node.Name == "machine" || node.Name == "game"))
else if (rom.Name.ToUpperInvariant().EndsWith(_extB))
{
bool inA = false;
bool inB = false;
// Get the roms from the machine
if (node.HasChildNodes)
{
// If this node has children, traverse the children
foreach (XmlNode child in node.ChildNodes)
{
// If we find a rom or disk, add it
if (child.NodeType == XmlNodeType.Element && (child.Name == "rom" || child.Name == "disk"))
{
// Take care of hex-sized files
long size = -1;
if (child.Attributes["size"] != null && child.Attributes["size"].Value.Contains("0x"))
{
size = Convert.ToInt64(child.Attributes["size"].Value, 16);
}
else if (child.Attributes["size"] != null)
{
size = Int64.Parse(child.Attributes["size"].Value);
}
if (child.Attributes["name"].Value.ToUpperInvariant().EndsWith(_extA))
{
if (!inA)
{
//XmlNode temp = tempDoc.CreateNode(XmlNodeType.Element, node.Name, "");
XmlNode temp = outDocA.ImportNode(node, false);
outA.AppendChild(temp);
outA = outA.LastChild;
inA = true;
}
outA.AppendChild(outDocA.ImportNode(child, true));
}
else if (child.Attributes["name"].Value.ToUpperInvariant().EndsWith(_extB))
{
if (!inB)
{
//XmlNode temp = tempDoc.CreateNode(XmlNodeType.Element, node.Name, "");
XmlNode temp = outDocB.ImportNode(node, false);
outB.AppendChild(temp);
outB = outB.LastChild;
inB = true;
}
outB.AppendChild(outDocB.ImportNode(child, true));
}
else
{
outA.AppendChild(outDocA.ImportNode(child, true));
outB.AppendChild(outDocB.ImportNode(child, true));
}
}
}
// Set the output node to the right one for both
if (inA)
{
outA = outA.ParentNode;
}
if (inB)
{
outB = outB.ParentNode;
}
}
romsB.Add(rom);
}
else
{
XmlNode tempNode = outDocA.ImportNode(node, true);
outA.AppendChild(tempNode);
tempNode = outDocB.ImportNode(node, true);
outB.AppendChild(tempNode);
romsA.Add(rom);
romsB.Add(rom);
}
node = node.NextSibling;
}
// Append the built nodes to the documents
outDocA.AppendChild(outDocA.ImportNode(outA, true));
string outPathA = Path.GetFileNameWithoutExtension(_filename) + _extA + Path.GetExtension(_filename);
File.WriteAllText(outPathA, Style.Beautify(outDocA), Encoding.UTF8);
// Then write out both files
Output.WriteToDat(Path.GetFileNameWithoutExtension(_filename) + "." + _extA, Path.GetFileNameWithoutExtension(_filename) + "." + _extA,
"", "", "", "", false, Path.GetExtension(_filename) == ".dat", "", romsA, logger);
Output.WriteToDat(Path.GetFileNameWithoutExtension(_filename) + "." + _extB, Path.GetFileNameWithoutExtension(_filename) + "." + _extB,
"", "", "", "", false, Path.GetExtension(_filename) == ".dat", "", romsB, logger);
outDocB.AppendChild(outDocB.ImportNode(outB, true));
string outPathB = Path.GetFileNameWithoutExtension(_filename) + _extB + Path.GetExtension(_filename);
File.WriteAllText(outPathB, Style.Beautify(outDocB), Encoding.UTF8);
logger.Close();
}
}
}

View File

@@ -0,0 +1,288 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Xml;
namespace SabreTools.Helper
{
public class RomManipulation
{
/// <summary>
/// Parse a DAT and return all found games and roms within
/// </summary>
/// <param name="filename">Name of the file to be parsed</param>
/// <param name="sysid">System ID for the DAT</param>
/// <param name="srcid">Source ID for the DAT</param>
/// <param name="logger">Logger object for console and/or file output</param>
/// <returns>List of RomData objects representing the found data</returns>
public static List<RomData> Parse(string filename, int sysid, int srcid, Logger logger)
{
List<RomData> roms = new List<RomData>();
bool superdat = false;
XmlDocument doc = new XmlDocument();
try
{
doc.LoadXml(File.ReadAllText(filename));
}
catch (XmlException)
{
doc.LoadXml(Converters.RomVaultToXML(File.ReadAllLines(filename)).ToString());
}
// Experimental looping using only XML parsing
XmlNode node = doc.FirstChild;
if (node != null && node.Name == "xml")
{
// Skip over everything that's not an element
while (node.NodeType != XmlNodeType.Element)
{
node = node.NextSibling;
}
}
// Once we find the main body, enter it
if (node != null && (node.Name == "datafile" || node.Name == "softwarelist"))
{
node = node.FirstChild;
}
// Skip the header if it exists
if (node != null && node.Name == "header")
{
// Check for SuperDAT mode
if (node.SelectSingleNode("name").InnerText.Contains(" - SuperDAT"))
{
superdat = true;
}
// Skip over anything that's not an element
while (node.NodeType != XmlNodeType.Element)
{
node = node.NextSibling;
}
}
// Loop over the document until the end
while (node != null)
{
if (node.NodeType == XmlNodeType.Element && (node.Name == "machine" || node.Name == "game" || node.Name == "software"))
{
string tempname = "";
if (node.Name == "software")
{
tempname = node.SelectSingleNode("description").InnerText;
}
else
{
// There are rare cases where a malformed XML will not have the required attributes. We can only skip them.
if (node.Attributes.Count == 0)
{
logger.Error(@"A node with malformed XML has been found!
For RV DATs, please make sure that all names and descriptions are quoted.
For XML DATs, make sure that the DAT has all required information.");
node = node.NextSibling;
continue;
}
tempname = node.Attributes["name"].Value;
}
if (superdat)
{
tempname = Regex.Match(tempname, @".*?\\(.*)").Groups[1].Value;
}
// Get the roms from the machine
if (node.HasChildNodes)
{
// If this node has children, traverse the children
foreach (XmlNode child in node.ChildNodes)
{
// If we find a rom or disk, add it
if (child.NodeType == XmlNodeType.Element && (child.Name == "rom" || child.Name == "disk"))
{
// Take care of hex-sized files
long size = -1;
if (child.Attributes["size"] != null && child.Attributes["size"].Value.Contains("0x"))
{
size = Convert.ToInt64(child.Attributes["size"].Value, 16);
}
else if (child.Attributes["size"] != null)
{
size = Int64.Parse(child.Attributes["size"].Value);
}
roms.Add(new RomData
{
Game = tempname,
Name = child.Attributes["name"].Value,
Type = child.Name,
SystemID = sysid,
SourceID = srcid,
Size = size,
CRC = (child.Attributes["crc"] != null ? child.Attributes["crc"].Value.ToLowerInvariant().Trim() : ""),
MD5 = (child.Attributes["md5"] != null ? child.Attributes["md5"].Value.ToLowerInvariant().Trim() : ""),
SHA1 = (child.Attributes["sha1"] != null ? child.Attributes["sha1"].Value.ToLowerInvariant().Trim() : ""),
});
}
// If we find the signs of a software list, traverse the children
else if (child.NodeType == XmlNodeType.Element && child.Name == "part" && child.HasChildNodes)
{
foreach (XmlNode part in child.ChildNodes)
{
// If we find a dataarea, traverse the children
if (part.NodeType == XmlNodeType.Element && part.Name == "dataarea")
{
foreach (XmlNode data in part.ChildNodes)
{
// If we find a rom or disk, add it
if (data.NodeType == XmlNodeType.Element && (data.Name == "rom" || data.Name == "disk") && data.Attributes["name"] != null)
{
// Take care of hex-sized files
long size = -1;
if (data.Attributes["size"] != null && data.Attributes["size"].Value.Contains("0x"))
{
size = Convert.ToInt64(data.Attributes["size"].Value, 16);
}
else if (data.Attributes["size"] != null)
{
size = Int64.Parse(data.Attributes["size"].Value);
}
roms.Add(new RomData
{
Game = tempname,
Name = data.Attributes["name"].Value,
Type = data.Name,
SystemID = sysid,
SourceID = srcid,
Size = size,
CRC = (data.Attributes["crc"] != null ? data.Attributes["crc"].Value.ToLowerInvariant().Trim() : ""),
MD5 = (data.Attributes["md5"] != null ? data.Attributes["md5"].Value.ToLowerInvariant().Trim() : ""),
SHA1 = (data.Attributes["sha1"] != null ? data.Attributes["sha1"].Value.ToLowerInvariant().Trim() : ""),
});
}
}
}
}
}
}
}
}
node = node.NextSibling;
}
return roms;
}
/// <summary>
/// Merge an arbitrary set of ROMs based on the supplied information
/// </summary>
/// <param name="inroms">List of RomData objects representing the roms to be merged</param>
/// <param name="presorted">True if the list should be considered pre-sorted (default false)</param>
/// <returns>A List of RomData objects representing the merged roms</returns>
public static List<RomData> Merge(List<RomData> inroms, bool presorted = false)
{
List<RomData> outroms = new List<RomData>();
// First sort the roms by size, crc, sysid, srcid, md5, and sha1 (in order), if not sorted already
if (!presorted)
{
inroms.Sort(delegate (RomData x, RomData y)
{
if (x.Size == y.Size)
{
if (x.CRC == y.CRC)
{
if (x.SystemID == y.SystemID)
{
if (x.SourceID == y.SourceID)
{
if (x.MD5 == y.MD5)
{
return String.Compare(x.SHA1, y.SHA1);
}
return String.Compare(x.MD5, y.MD5);
}
return x.SourceID - y.SourceID;
}
return x.SystemID - y.SystemID;
}
return String.Compare(x.CRC, y.CRC);
}
return (int)(x.Size - y.Size);
});
}
// Then, deduplicate them by checking to see if data matches
foreach (RomData rom in inroms)
{
// If it's the first rom in the list, don't touch it
if (outroms.Count != 0)
{
// Check if the rom is a duplicate
RomData last = outroms[outroms.Count - 1];
bool shouldcont = false;
if (rom.Type == "rom" && last.Type == "rom")
{
shouldcont = ((rom.Size != -1 && rom.Size == last.Size) && (
(rom.CRC != "" && last.CRC != "" && rom.CRC == last.CRC) ||
(rom.MD5 != "" && last.MD5 != "" && rom.MD5 == last.MD5) ||
(rom.SHA1 != "" && last.SHA1 != "" && rom.SHA1 == last.SHA1)
)
);
}
else if (rom.Type == "disk" && last.Type == "disk")
{
shouldcont = ((rom.MD5 != "" && last.MD5 != "" && rom.MD5 == last.MD5) ||
(rom.SHA1 != "" && last.SHA1 != "" && rom.SHA1 == last.SHA1)
);
}
// If it's a duplicate, skip adding it to the output but add any missing information
if (shouldcont)
{
last.CRC = (last.CRC == "" && rom.CRC != "" ? rom.CRC : last.CRC);
last.MD5 = (last.MD5 == "" && rom.MD5 != "" ? rom.MD5 : last.MD5);
last.SHA1 = (last.SHA1 == "" && rom.SHA1 != "" ? rom.SHA1 : last.SHA1);
outroms.RemoveAt(inroms.Count - 1);
outroms.Insert(inroms.Count, last);
continue;
}
}
}
// Then return the result
return outroms;
}
/// <summary>
/// Sort a list of RomData objects by SystemID, SourceID, Game, and Name (in order)
/// </summary>
/// <param name="roms">List of RomData objects representing the roms to be sorted</param>
/// <param name="norename">True if files are not renamed, false otherwise</param>
public static void Sort(List<RomData> roms, bool norename)
{
roms.Sort(delegate (RomData x, RomData y)
{
if (x.SystemID == y.SystemID)
{
if (x.SourceID == y.SourceID)
{
if (x.Game == y.Game)
{
return String.Compare(x.Name, y.Name);
}
return String.Compare(x.Game, y.Game);
}
return (norename ? String.Compare(x.Game, y.Game) : x.SourceID - y.SourceID);
}
return (norename ? String.Compare(x.Game, y.Game) : x.SystemID - y.SystemID);
});
}
}
}

View File

@@ -76,7 +76,7 @@
<Compile Include="Output.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Remapping.cs" />
<Compile Include="Sorting.cs" />
<Compile Include="RomManipulation.cs" />
<Compile Include="Structs.cs" />
<Compile Include="Style.cs" />
<Compile Include="Build.cs" />

View File

@@ -1,117 +0,0 @@
using System;
using System.Collections.Generic;
namespace SabreTools.Helper
{
public class Sorting
{
/// <summary>
/// Merge an arbitrary set of ROMs based on the supplied information
/// </summary>
/// <param name="inroms">List of RomData objects representing the roms to be merged</param>
/// <param name="presorted">True if the list should be considered pre-sorted (default false)</param>
/// <returns>A List of RomData objects representing the merged roms</returns>
public static List<RomData> RomMerge(List<RomData> inroms, bool presorted = false)
{
List<RomData> outroms = new List<RomData>();
// First sort the roms by size, crc, sysid, srcid, md5, and sha1 (in order), if not sorted already
if (!presorted)
{
inroms.Sort(delegate (RomData x, RomData y)
{
if (x.Size == y.Size)
{
if (x.CRC == y.CRC)
{
if (x.SystemID == y.SystemID)
{
if (x.SourceID == y.SourceID)
{
if (x.MD5 == y.MD5)
{
return String.Compare(x.SHA1, y.SHA1);
}
return String.Compare(x.MD5, y.MD5);
}
return x.SourceID - y.SourceID;
}
return x.SystemID - y.SystemID;
}
return String.Compare(x.CRC, y.CRC);
}
return (int)(x.Size - y.Size);
});
}
// Then, deduplicate them by checking to see if data matches
foreach (RomData rom in inroms)
{
// If it's the first rom in the list, don't touch it
if (outroms.Count != 0)
{
// Check if the rom is a duplicate
RomData last = outroms[outroms.Count - 1];
bool shouldcont = false;
if (rom.Type == "rom" && last.Type == "rom")
{
shouldcont = ((rom.Size != -1 && rom.Size == last.Size) && (
(rom.CRC != "" && last.CRC != "" && rom.CRC == last.CRC) ||
(rom.MD5 != "" && last.MD5 != "" && rom.MD5 == last.MD5) ||
(rom.SHA1 != "" && last.SHA1 != "" && rom.SHA1 == last.SHA1)
)
);
}
else if (rom.Type == "disk" && last.Type == "disk")
{
shouldcont = ((rom.MD5 != "" && last.MD5 != "" && rom.MD5 == last.MD5) ||
(rom.SHA1 != "" && last.SHA1 != "" && rom.SHA1 == last.SHA1)
);
}
// If it's a duplicate, skip adding it to the output but add any missing information
if (shouldcont)
{
last.CRC = (last.CRC == "" && rom.CRC != "" ? rom.CRC : last.CRC);
last.MD5 = (last.MD5 == "" && rom.MD5 != "" ? rom.MD5 : last.MD5);
last.SHA1 = (last.SHA1 == "" && rom.SHA1 != "" ? rom.SHA1 : last.SHA1);
outroms.RemoveAt(inroms.Count - 1);
outroms.Insert(inroms.Count, last);
continue;
}
}
}
// Then return the result
return outroms;
}
/// <summary>
/// Sort a list of RomData objects by SystemID, SourceID, Game, and Name (in order)
/// </summary>
/// <param name="roms">List of RomData objects representing the roms to be sorted</param>
/// <param name="norename">True if files are not renamed, false otherwise</param>
public static void RomSort(List<RomData> roms, bool norename)
{
roms.Sort(delegate (RomData x, RomData y)
{
if (x.SystemID == y.SystemID)
{
if (x.SourceID == y.SourceID)
{
if (x.Game == y.Game)
{
return String.Compare(x.Name, y.Name);
}
return String.Compare(x.Game, y.Game);
}
return (norename ? String.Compare(x.Game, y.Game) : x.SourceID - y.SourceID);
}
return (norename ? String.Compare(x.Game, y.Game) : x.SystemID - y.SystemID);
});
}
}
}

View File

@@ -30,6 +30,9 @@ namespace SabreTools
return;
}
Logger logger = new Logger(false, "singlegame.log");
logger.Start();
// Output the title
Build.Start("SingleGame");
@@ -46,122 +49,37 @@ namespace SabreTools
_path = (_path == "" ? Environment.CurrentDirectory : _path);
// Take the filename, and load it as an XML document
XmlDocument doc = new XmlDocument();
try
{
doc.LoadXml(File.ReadAllText(_filename));
}
catch (XmlException)
{
doc.LoadXml(Converters.RomVaultToXML(File.ReadAllLines(_filename)).ToString());
}
// Import the existing DAT
List<RomData> roms = RomManipulation.Parse(_filename, 0, 0, logger);
// We all start the same
XmlNode node = doc.FirstChild;
if (node != null && node.Name == "xml")
// If we are in single game mode, rename all games
if (_rename)
{
// Skip over everything that's not an element
while (node.NodeType != XmlNodeType.Element)
roms.ForEach(delegate (RomData x)
{
node = node.NextSibling;
}
x.Game = "!";
});
}
XmlDocument tempDoc = new XmlDocument();
XmlNode outNode = tempDoc.CreateNode(XmlNodeType.Element, node.Name, "");
// Once we find the main body, enter it
if (node != null && node.Name == "datafile")
// Trim all file names according to the path that's set
roms.ForEach(delegate (RomData x)
{
node = node.FirstChild;
}
// Windows max name length is 260
int usableLength = 259 - _path.Length;
// Now here's where it differs from import
bool inGame = false;
while (node != null)
{
// If we're at a game node, add the parent node but not all the internals
if (_rename && node.NodeType == XmlNodeType.Element && (node.Name == "machine" || node.Name == "game"))
if (x.Name.Length > usableLength)
{
if (!inGame)
{
XmlElement tempNode = tempDoc.CreateElement(node.Name);
tempNode.SetAttribute("name", "!");
outNode.AppendChild(tempNode);
outNode = outNode.LastChild;
inGame = true;
}
// Get the roms from the machine
if (node.HasChildNodes)
{
// If this node has children, traverse the children
foreach (XmlNode child in node.ChildNodes)
{
// If we find a rom or disk, add it
if (child.NodeType == XmlNodeType.Element && (child.Name == "rom" || child.Name == "disk"))
{
// Take care of hex-sized files
long size = -1;
if (child.Attributes["size"] != null && child.Attributes["size"].Value.Contains("0x"))
{
size = Convert.ToInt64(child.Attributes["size"].Value, 16);
}
else if (child.Attributes["size"] != null)
{
size = Int64.Parse(child.Attributes["size"].Value);
}
XmlElement tempNode = (XmlElement)tempDoc.ImportNode(child, true);
// Windows max name length is 260
string tempname = child.Attributes["name"].Value;
int usableLength = 259 - _path.Length;
if (tempname.Length > usableLength)
{
string ext = Path.GetExtension(tempname);
tempname = tempname.Substring(0, usableLength - ext.Length);
tempname += ext;
}
tempNode.SetAttribute("name", tempname);
outNode.AppendChild(tempNode);
}
}
}
string ext = Path.GetExtension(x.Name);
x.Name = x.Name.Substring(0, usableLength - ext.Length);
x.Name += ext;
}
else
{
XmlNode tempNode = tempDoc.ImportNode(node, true);
});
if (tempNode.Name == "header")
{
if (tempNode.SelectSingleNode("clrmamepro") == null)
{
XmlElement tempChild = tempDoc.CreateElement("clrmamepro");
tempChild.SetAttribute("forcepacking", "unzip");
tempNode.AppendChild(tempChild);
}
else
{
(tempNode.SelectSingleNode("clrmamepro") as XmlElement).SetAttribute("forcepacking", "unzip");
}
}
// Now write the file out accordingly
Output.WriteToDat(Path.GetFileNameWithoutExtension(_filename),
Path.GetFileNameWithoutExtension(_filename), "", "", "", "", false, Path.GetExtension(_filename) == ".dat", "", roms, logger);
outNode.AppendChild(tempNode);
}
node = node.NextSibling;
}
if (inGame)
{
outNode = outNode.ParentNode;
}
tempDoc.AppendChild(tempDoc.CreateDocumentType("datafile", "-//Logiqx//DTD ROM Management Datafile//EN", "http://www.logiqx.com/Dats/datafile.dtd", null));
tempDoc.AppendChild(outNode);
string outPath = Path.GetFileNameWithoutExtension(_filename) + ".new" + Path.GetExtension(_filename);
File.WriteAllText(outPath, Style.Beautify(tempDoc));
logger.Close();
}
}
}