diff --git a/SabreTools.Library/DatFiles/ClrMamePro.cs b/SabreTools.Library/DatFiles/ClrMamePro.cs
index 90d3ec2a..e0fd9a35 100644
--- a/SabreTools.Library/DatFiles/ClrMamePro.cs
+++ b/SabreTools.Library/DatFiles/ClrMamePro.cs
@@ -590,6 +590,628 @@ namespace SabreTools.Library.DatFiles
sr.Dispose();
}
+ ///
+ /// Parse a ClrMamePro DAT and return all found games and roms within
+ ///
+ /// Name of the file to be parsed
+ /// System ID for the DAT
+ /// Source ID for the DAT
+ /// True if full pathnames are to be kept, false otherwise (default)
+ /// True if game names are sanitized, false otherwise (default)
+ /// True if we should remove non-ASCII characters from output, false otherwise (default)
+ public void ParseFileStripped(
+ // Standard Dat parsing
+ string filename,
+ int sysid,
+ int srcid,
+
+ // Miscellaneous
+ bool keep,
+ bool clean,
+ bool remUnicode)
+ {
+ // Open a file reader
+ Encoding enc = Utilities.GetEncoding(filename);
+ StreamReader sr = new StreamReader(Utilities.TryOpenRead(filename), enc);
+
+ while (!sr.EndOfStream)
+ {
+ string line = sr.ReadLine();
+
+ // 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, Constants.HeaderPatternCMP))
+ {
+ GroupCollection gc = Regex.Match(line, Constants.HeaderPatternCMP).Groups;
+
+ // If we have a known header
+ if (gc[1].Value == "clrmamepro"
+ || gc[1].Value == "romvault"
+ || gc[1].Value.ToLowerInvariant() == "doscenter")
+ {
+ ReadHeader(sr, keep);
+ }
+ // If we have a known set type
+ else if (gc[1].Value == "set"
+ || gc[1].Value == "game"
+ || gc[1].Value == "machine")
+ {
+ ReadSet(sr, filename, sysid, srcid, keep, clean, remUnicode);
+ }
+ }
+ }
+
+ sr.Dispose();
+ }
+
+ ///
+ /// Read header information
+ ///
+ /// StreamReader to use to parse the header
+ /// True if full pathnames are to be kept, false otherwise (default)
+ /// TODO: Make sure this only is called if the block is "clrmamepro", "doscenter", "romcenter"
+ private void ReadHeader(StreamReader reader, bool keep)
+ {
+ bool superdat = false;
+
+ // If there's no subtree to the header, skip it
+ if (reader == null || reader.EndOfStream)
+ {
+ return;
+ }
+
+ // Otherwise, add what is possible
+ string line = reader.ReadLine();
+ while (!Regex.IsMatch(line, Constants.EndPatternCMP))
+ {
+ // We only want elements
+ if (line.Trim().StartsWith("#"))
+ {
+ line = reader.ReadLine();
+ continue;
+ }
+
+ // Get all header items (ONLY OVERWRITE IF THERE'S NO DATA)
+ GroupCollection gc = Regex.Match(line, Constants.ItemPatternCMP).Groups;
+ string itemval = gc[2].Value.Replace("\"", "");
+
+ if (line.Trim().StartsWith("Name:"))
+ {
+ Name = (String.IsNullOrWhiteSpace(Name) ? line.Substring(6) : Name);
+ superdat = superdat || itemval.Contains(" - SuperDAT");
+ if (keep && superdat)
+ {
+ Type = (String.IsNullOrWhiteSpace(Type) ? "SuperDAT" : Type);
+ }
+
+ line = reader.ReadLine();
+ continue;
+ }
+
+ switch (gc[1].Value)
+ {
+ case "name":
+ case "Name:":
+ Name = (String.IsNullOrWhiteSpace(Name) ? itemval : Name);
+ superdat = superdat || itemval.Contains(" - SuperDAT");
+ if (keep && superdat)
+ {
+ Type = (String.IsNullOrWhiteSpace(Type) ? "SuperDAT" : Type);
+ }
+ break;
+ case "description":
+ case "Description:":
+ Description = (String.IsNullOrWhiteSpace(Description) ? itemval : Description);
+ break;
+ case "rootdir":
+ case "Rootdir:":
+ RootDir = (String.IsNullOrWhiteSpace(RootDir) ? itemval : RootDir);
+ break;
+ case "category":
+ case "Category:":
+ Category = (String.IsNullOrWhiteSpace(Category) ? itemval : Category);
+ break;
+ case "version":
+ case "Version:":
+ Version = (String.IsNullOrWhiteSpace(Version) ? itemval : Version);
+ break;
+ case "date":
+ case "Date:":
+ Date = (String.IsNullOrWhiteSpace(Date) ? itemval : Date);
+ break;
+ case "author":
+ case "Author:":
+ Author = (String.IsNullOrWhiteSpace(Author) ? itemval : Author);
+ break;
+ case "email":
+ case "Email:":
+ Email = (String.IsNullOrWhiteSpace(Email) ? itemval : Email);
+ break;
+ case "homepage":
+ case "Homepage:":
+ Homepage = (String.IsNullOrWhiteSpace(Homepage) ? itemval : Homepage);
+ break;
+ case "url":
+ case "Url:":
+ Url = (String.IsNullOrWhiteSpace(Url) ? itemval : Url);
+ break;
+ case "comment":
+ case "Comment:":
+ Comment = (String.IsNullOrWhiteSpace(Comment) ? itemval : Comment);
+ break;
+ case "header":
+ case "Header:":
+ Header = (String.IsNullOrWhiteSpace(Header) ? itemval : Header);
+ break;
+ case "type":
+ case "Type:":
+ Type = (String.IsNullOrWhiteSpace(Type) ? itemval : Type);
+ superdat = superdat || itemval.Contains("SuperDAT");
+ break;
+ case "forcemerging":
+ if (ForceMerging == ForceMerging.None)
+ {
+ ForceMerging = Utilities.GetForceMerging(itemval);
+ }
+ break;
+ case "forcezipping":
+ if (ForcePacking == ForcePacking.None)
+ {
+ ForcePacking = Utilities.GetForcePacking(itemval);
+ }
+ break;
+ case "forcepacking":
+ if (ForcePacking == ForcePacking.None)
+ {
+ ForcePacking = Utilities.GetForcePacking(itemval);
+ }
+ break;
+ }
+
+ line = reader.ReadLine();
+ }
+ }
+
+ ///
+ /// Read set information
+ ///
+ /// StreamReader to use to parse the header
+ /// Name of the file to be parsed
+ /// System ID for the DAT
+ /// Source ID for the DAT
+ /// True if full pathnames are to be kept, false otherwise (default)
+ /// True if game names are sanitized, false otherwise (default)
+ /// True if we should remove non-ASCII characters from output, false otherwise (default)
+ /// TODO: Make sure this is only called if the block is "set", "game", "machine"
+ private void ReadSet(
+ StreamReader reader,
+
+ // Standard Dat parsing
+ string filename,
+ int sysid,
+ int srcid,
+
+ // Miscellaneous
+ bool keep,
+ bool clean,
+ bool remUnicode)
+ {
+ // Prepare all internal variables
+ bool containsItems = false;
+ Machine machine = new Machine();
+
+ // If there's no subtree to the header, skip it
+ if (reader == null || reader.EndOfStream)
+ {
+ return;
+ }
+
+ // Otherwise, add what is possible
+ string line = reader.ReadLine();
+ while (!Regex.IsMatch(line, Constants.EndPatternCMP))
+ {
+ // We only want elements
+ if (line.Trim().StartsWith("#"))
+ {
+ line = reader.ReadLine();
+ continue;
+ }
+
+ // Item-specific lines have a known pattern
+ string trimmedline = line.Trim();
+ if (trimmedline.StartsWith("archive (")
+ || trimmedline.StartsWith("biosset (")
+ || trimmedline.StartsWith("disk (")
+ || trimmedline.StartsWith("file (") // This is a DOSCenter file, not a SabreDAT file
+ || trimmedline.StartsWith("release (")
+ || trimmedline.StartsWith("rom (")
+ || (trimmedline.StartsWith("sample") && !trimmedline.StartsWith("sampleof")))
+ {
+ containsItems = true;
+ ItemType temptype = ItemType.Rom;
+ if (line.Trim().StartsWith("rom ("))
+ {
+ temptype = ItemType.Rom;
+ }
+ else if (line.Trim().StartsWith("disk ("))
+ {
+ temptype = ItemType.Disk;
+ }
+ else if (line.Trim().StartsWith("file ("))
+ {
+ temptype = ItemType.Rom;
+ }
+ else if (line.Trim().StartsWith("sample"))
+ {
+ temptype = ItemType.Sample;
+ }
+
+ // Create the proper DatItem based on the type
+ DatItem item = Utilities.GetDatItem(temptype);
+
+ // Then populate it with information
+ item.CopyMachineInformation(machine);
+
+ item.SourceFile = filename;
+ item.SystemID = sysid;
+ item.SourceID = srcid;
+
+ // If we have a sample, treat it special
+ if (temptype == ItemType.Sample)
+ {
+ line = line.Trim().Remove(0, 6).Trim().Replace("\"", ""); // Remove "sample" from the input string
+ item.Name = line;
+
+ // Now process and add the sample
+ ParseAddHelper(item, clean, remUnicode);
+ line = reader.ReadLine();
+ continue;
+ }
+
+ // Get the line split by spaces and quotes
+ string[] linegc = Utilities.SplitLineAsCMP(line);
+
+ // Special cases for DOSCenter DATs only because of how the lines are arranged
+ if (line.Trim().StartsWith("file ("))
+ {
+ // Loop over the specifics
+ for (int i = 0; i < linegc.Length; i++)
+ {
+ // Names are not quoted, for some stupid reason
+ if (linegc[i] == "name")
+ {
+ // Get the name in order until we find the next flag
+ while (++i < linegc.Length && linegc[i] != "size"
+ && linegc[i] != "date"
+ && linegc[i] != "crc"
+ && linegc[i] != "md5"
+ && linegc[i] != "sha1"
+ && linegc[i] != "sha256"
+ && linegc[i] != "sha384"
+ && linegc[i] != "sha512")
+ {
+ item.Name += " " + linegc[i];
+ }
+
+ // Perform correction
+ item.Name = item.Name.TrimStart();
+ i--;
+ }
+
+ // Get the size from the next part
+ else if (linegc[i] == "size")
+ {
+ long tempsize = -1;
+ if (!Int64.TryParse(linegc[++i], out tempsize))
+ {
+ tempsize = 0;
+ }
+ ((Rom)item).Size = tempsize;
+ }
+
+ // Get the date from the next part
+ else if (linegc[i] == "date")
+ {
+ ((Rom)item).Date = linegc[++i].Replace("\"", "") + " " + linegc[++i].Replace("\"", "");
+ }
+
+ // Get the CRC from the next part
+ else if (linegc[i] == "crc")
+ {
+ ((Rom)item).CRC = linegc[++i].Replace("\"", "").ToLowerInvariant();
+ }
+
+ // Get the MD5 from the next part
+ else if (linegc[i] == "md5")
+ {
+ ((Rom)item).MD5 = linegc[++i].Replace("\"", "").ToLowerInvariant();
+ }
+
+ // Get the SHA1 from the next part
+ else if (linegc[i] == "sha1")
+ {
+ ((Rom)item).SHA1 = linegc[++i].Replace("\"", "").ToLowerInvariant();
+ }
+
+ // Get the SHA256 from the next part
+ else if (linegc[i] == "sha256")
+ {
+ ((Rom)item).SHA256 = linegc[++i].Replace("\"", "").ToLowerInvariant();
+ }
+
+ // Get the SHA384 from the next part
+ else if (linegc[i] == "sha384")
+ {
+ ((Rom)item).SHA384 = linegc[++i].Replace("\"", "").ToLowerInvariant();
+ }
+
+ // Get the SHA512 from the next part
+ else if (linegc[i] == "sha512")
+ {
+ ((Rom)item).SHA512 = linegc[++i].Replace("\"", "").ToLowerInvariant();
+ }
+ }
+
+ // Now process and add the rom
+ ParseAddHelper(item, clean, remUnicode);
+ line = reader.ReadLine();
+ continue;
+ }
+
+ // Loop over all attributes normally and add them if possible
+ for (int i = 0; i < linegc.Length; i++)
+ {
+ // Look at the current item and use it if possible
+ string quoteless = linegc[i].Replace("\"", "");
+ switch (quoteless)
+ {
+ //If the item is empty, we automatically skip it because it's a fluke
+ case "":
+ continue;
+
+ // Special cases for standalone item statuses
+ case "baddump":
+ case "good":
+ case "nodump":
+ case "verified":
+ ItemStatus tempStandaloneStatus = Utilities.GetItemStatus(quoteless);
+ if (item.Type == ItemType.Rom)
+ {
+ ((Rom)item).ItemStatus = tempStandaloneStatus;
+ }
+ else if (item.Type == ItemType.Disk)
+ {
+ ((Disk)item).ItemStatus = tempStandaloneStatus;
+ }
+ break;
+
+ // Regular attributes
+ case "name":
+ quoteless = linegc[++i].Replace("\"", "");
+ item.Name = quoteless;
+ break;
+ case "size":
+ if (item.Type == ItemType.Rom)
+ {
+ quoteless = linegc[++i].Replace("\"", "");
+ if (Int64.TryParse(quoteless, out long size))
+ {
+ ((Rom)item).Size = size;
+ }
+ else
+ {
+ ((Rom)item).Size = -1;
+ }
+ }
+ break;
+ case "crc":
+ if (item.Type == ItemType.Rom)
+ {
+ quoteless = linegc[++i].Replace("\"", "");
+ ((Rom)item).CRC = quoteless.ToLowerInvariant();
+ }
+ break;
+ case "md5":
+ if (item.Type == ItemType.Rom)
+ {
+ quoteless = linegc[++i].Replace("\"", "");
+ ((Rom)item).MD5 = quoteless.ToLowerInvariant();
+ }
+ else if (item.Type == ItemType.Disk)
+ {
+ i++;
+ quoteless = linegc[i].Replace("\"", "");
+ ((Disk)item).MD5 = quoteless.ToLowerInvariant();
+ }
+ break;
+ case "sha1":
+ if (item.Type == ItemType.Rom)
+ {
+ quoteless = linegc[++i].Replace("\"", "");
+ ((Rom)item).SHA1 = quoteless.ToLowerInvariant();
+ }
+ else if (item.Type == ItemType.Disk)
+ {
+ quoteless = linegc[++i].Replace("\"", "");
+ ((Disk)item).SHA1 = quoteless.ToLowerInvariant();
+ }
+ break;
+ case "sha256":
+ if (item.Type == ItemType.Rom)
+ {
+ quoteless = linegc[++i].Replace("\"", "");
+ ((Rom)item).SHA256 = quoteless.ToLowerInvariant();
+ }
+ else if (item.Type == ItemType.Disk)
+ {
+ quoteless = linegc[++i].Replace("\"", "");
+ ((Disk)item).SHA256 = quoteless.ToLowerInvariant();
+ }
+ break;
+ case "sha384":
+ if (item.Type == ItemType.Rom)
+ {
+ quoteless = linegc[++i].Replace("\"", "");
+ ((Rom)item).SHA384 = quoteless.ToLowerInvariant();
+ }
+ else if (item.Type == ItemType.Disk)
+ {
+ quoteless = linegc[++i].Replace("\"", "");
+ ((Disk)item).SHA384 = quoteless.ToLowerInvariant();
+ }
+ break;
+ case "sha512":
+ if (item.Type == ItemType.Rom)
+ {
+ quoteless = linegc[++i].Replace("\"", "");
+ ((Rom)item).SHA512 = quoteless.ToLowerInvariant();
+ }
+ else if (item.Type == ItemType.Disk)
+ {
+ quoteless = linegc[++i].Replace("\"", "");
+ ((Disk)item).SHA512 = quoteless.ToLowerInvariant();
+ }
+ break;
+ case "status":
+ case "flags":
+ quoteless = linegc[++i].Replace("\"", "");
+ ItemStatus tempFlagStatus = Utilities.GetItemStatus(quoteless);
+ if (item.Type == ItemType.Rom)
+ {
+ ((Rom)item).ItemStatus = tempFlagStatus;
+ }
+ else if (item.Type == ItemType.Disk)
+ {
+ ((Disk)item).ItemStatus = tempFlagStatus;
+ }
+ break;
+ case "date":
+ if (item.Type == ItemType.Rom)
+ {
+ // If we have quotes in the next item, assume only one item
+ if (linegc[i + 1].Contains("\""))
+ {
+ quoteless = linegc[++i].Replace("\"", "");
+ }
+ // Otherwise, we assume we need to read the next two items
+ else
+ {
+ quoteless = linegc[++i].Replace("\"", "") + " " + linegc[++i].Replace("\"", "");
+ }
+ ((Rom)item).Date = quoteless;
+ }
+ else if (item.Type == ItemType.Release)
+ {
+ // If we have quotes in the next item, assume only one item
+ if (linegc[i + 1].Contains("\""))
+ {
+ quoteless = linegc[++i].Replace("\"", "");
+ }
+ // Otherwise, we assume we need to read the next two items
+ else
+ {
+ quoteless = linegc[++i].Replace("\"", "") + " " + linegc[++i].Replace("\"", "");
+ }
+ ((Release)item).Date = quoteless;
+ }
+ break;
+ case "default":
+ if (item.Type == ItemType.BiosSet)
+ {
+ quoteless = linegc[++i].Replace("\"", "");
+ ((BiosSet)item).Default = Utilities.GetYesNo(quoteless.ToLowerInvariant());
+ }
+ else if (item.Type == ItemType.Release)
+ {
+ quoteless = linegc[++i].Replace("\"", "");
+ ((Release)item).Default = Utilities.GetYesNo(quoteless.ToLowerInvariant());
+ }
+ break;
+ case "description":
+ if (item.Type == ItemType.BiosSet)
+ {
+ quoteless = linegc[++i].Replace("\"", "");
+ ((BiosSet)item).Description = quoteless.ToLowerInvariant();
+ }
+ break;
+ case "region":
+ if (item.Type == ItemType.Release)
+ {
+ quoteless = linegc[++i].Replace("\"", "");
+ ((Release)item).Region = quoteless.ToLowerInvariant();
+ }
+ break;
+ case "language":
+ if (item.Type == ItemType.Release)
+ {
+ quoteless = linegc[++i].Replace("\"", "");
+ ((Release)item).Language = quoteless.ToLowerInvariant();
+ }
+ break;
+ }
+ }
+
+ // Now process and add the rom
+ ParseAddHelper(item, clean, remUnicode);
+
+ line = reader.ReadLine();
+ continue;
+ }
+
+ // Set-specific lines have a known pattern
+ GroupCollection setgc = Regex.Match(line, Constants.ItemPatternCMP).Groups;
+ string itemval = setgc[2].Value.Replace("\"", "");
+
+ switch (setgc[1].Value)
+ {
+ case "name":
+ machine.Name = (itemval.ToLowerInvariant().EndsWith(".zip") ? itemval.Remove(itemval.Length - 4) : itemval);
+ machine.Description = (itemval.ToLowerInvariant().EndsWith(".zip") ? itemval.Remove(itemval.Length - 4) : itemval);
+ break;
+ case "description":
+ machine.Description = itemval;
+ break;
+ case "year":
+ machine.Year = itemval;
+ break;
+ case "manufacturer":
+ machine.Manufacturer = itemval;
+ break;
+ case "cloneof":
+ machine.CloneOf = itemval;
+ break;
+ case "romof":
+ machine.RomOf = itemval;
+ break;
+ case "sampleof":
+ machine.SampleOf = itemval;
+ break;
+ }
+
+ line = reader.ReadLine();
+ }
+
+ // If no items were found for this machine, add a Blank placeholder
+ if (!containsItems)
+ {
+ Blank blank = new Blank()
+ {
+ SourceFile = filename,
+ SystemID = sysid,
+ SourceID = srcid,
+ };
+ blank.CopyMachineInformation(machine);
+
+ // Now process and add the rom
+ ParseAddHelper(blank, clean, remUnicode);
+ }
+ }
+
///
/// Create and open an output file for writing direct from a dictionary
///