diff --git a/SabreTools.Library/DatFiles/ClrMamePro.cs b/SabreTools.Library/DatFiles/ClrMamePro.cs index fc5466ab..408c83ce 100644 --- a/SabreTools.Library/DatFiles/ClrMamePro.cs +++ b/SabreTools.Library/DatFiles/ClrMamePro.cs @@ -66,8 +66,7 @@ namespace SabreTools.Library.DatFiles // If we have a known header if (normalizedValue == "clrmamepro" - || normalizedValue == "romvault" - || normalizedValue == "doscenter") + || normalizedValue == "romvault") { ReadHeader(sr, keep); } @@ -116,22 +115,9 @@ namespace SabreTools.Library.DatFiles GroupCollection gc = Regex.Match(line, Constants.ItemPatternCMP).Groups; string itemval = gc[2].Value.Replace("\"", string.Empty); - 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"); @@ -140,51 +126,39 @@ namespace SabreTools.Library.DatFiles 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; @@ -259,7 +233,6 @@ namespace SabreTools.Library.DatFiles 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"))) @@ -270,8 +243,6 @@ namespace SabreTools.Library.DatFiles 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; @@ -300,99 +271,6 @@ namespace SabreTools.Library.DatFiles // 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] != "ripemd160" - && 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("\"", string.Empty)} {linegc[++i].Replace("\"", string.Empty)}"; - } - - // Get the CRC from the next part - else if (linegc[i] == "crc") - { - ((Rom)item).CRC = linegc[++i].Replace("\"", string.Empty).ToLowerInvariant(); - } - - // Get the MD5 from the next part - else if (linegc[i] == "md5") - { - ((Rom)item).MD5 = linegc[++i].Replace("\"", string.Empty).ToLowerInvariant(); - } - - // Get the RIPEMD160 from the next part - else if (linegc[i] == "ripemd160") - { - ((Rom)item).RIPEMD160 = linegc[++i].Replace("\"", string.Empty).ToLowerInvariant(); - } - - // Get the SHA1 from the next part - else if (linegc[i] == "sha1") - { - ((Rom)item).SHA1 = linegc[++i].Replace("\"", string.Empty).ToLowerInvariant(); - } - - // Get the SHA256 from the next part - else if (linegc[i] == "sha256") - { - ((Rom)item).SHA256 = linegc[++i].Replace("\"", string.Empty).ToLowerInvariant(); - } - - // Get the SHA384 from the next part - else if (linegc[i] == "sha384") - { - ((Rom)item).SHA384 = linegc[++i].Replace("\"", string.Empty).ToLowerInvariant(); - } - - // Get the SHA512 from the next part - else if (linegc[i] == "sha512") - { - ((Rom)item).SHA512 = linegc[++i].Replace("\"", string.Empty).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++) { diff --git a/SabreTools.Library/DatFiles/DosCenter.cs b/SabreTools.Library/DatFiles/DosCenter.cs index 9ba22b55..1189e116 100644 --- a/SabreTools.Library/DatFiles/DosCenter.cs +++ b/SabreTools.Library/DatFiles/DosCenter.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; +using System.Text.RegularExpressions; using SabreTools.Library.Data; using SabreTools.Library.DatItems; @@ -26,7 +27,7 @@ namespace SabreTools.Library.DatFiles } /// - /// Parse a DosCenter DAT and return all found games and roms within + /// Parse a DOSCenter DAT and return all found games and roms within /// /// Name of the file to be parsed /// System ID for the DAT @@ -34,7 +35,6 @@ namespace SabreTools.Library.DatFiles /// 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: Pull parsing into this file instead of relying on CMP public override void ParseFile( // Standard Dat parsing string filename, @@ -46,8 +46,224 @@ namespace SabreTools.Library.DatFiles bool clean, bool remUnicode) { - // ClrMamePro and DosCenter parsing are identical so it just calls one implementation - new ClrMamePro(this).ParseFile(filename, sysid, srcid, keep, clean, 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(); + + // If the line is the header or a game + if (Regex.IsMatch(line, Constants.HeaderPatternCMP)) + { + GroupCollection gc = Regex.Match(line, Constants.HeaderPatternCMP).Groups; + string normalizedValue = gc[1].Value.ToLowerInvariant(); + + // If we have a known header + if (normalizedValue == "doscenter") + ReadHeader(sr, keep); + + // If we have a known set type + else if (normalizedValue == "game" ) + ReadGame(sr, filename, sysid, srcid, 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) + 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)) + { + // 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("\"", string.Empty); + + // 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) : 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:": + Name = (string.IsNullOrWhiteSpace(Name) ? itemval : Name); + superdat = superdat || itemval.Contains(" - SuperDAT"); + + if (keep && superdat) + Type = (string.IsNullOrWhiteSpace(Type) ? "SuperDAT" : Type); + + break; + case "Description:": + Description = (string.IsNullOrWhiteSpace(Description) ? itemval : Description); + break; + case "Version:": + Version = (string.IsNullOrWhiteSpace(Version) ? itemval : Version); + break; + case "Date:": + Date = (string.IsNullOrWhiteSpace(Date) ? itemval : Date); + break; + case "Author:": + Author = (string.IsNullOrWhiteSpace(Author) ? itemval : Author); + break; + case "Homepage:": + Homepage = (string.IsNullOrWhiteSpace(Homepage) ? itemval : Homepage); + break; + case "Comment:": + Comment = (string.IsNullOrWhiteSpace(Comment) ? itemval : Comment); + 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 game names are sanitized, false otherwise (default) + /// True if we should remove non-ASCII characters from output, false otherwise (default) + private void ReadGame( + StreamReader reader, + + // Standard Dat parsing + string filename, + int sysid, + int srcid, + + // Miscellaneous + bool clean, + bool remUnicode) + { + // Prepare all internal variables + bool containsItems = false; + Machine machine = new Machine() + { + MachineType = MachineType.None, + }; + + // 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)) + { + // Item-specific lines have a known pattern + string trimmedline = line.Trim(); + if (trimmedline.StartsWith("file (")) + { + containsItems = true; + ItemType temptype = ItemType.Rom; + + // Create the proper DatItem based on the type + DatItem item = Utilities.GetDatItem(temptype); + + // Then populate it with information + item.CopyMachineInformation(machine); + + item.SystemID = sysid; + item.System = filename; + item.SourceID = srcid; + + // Get the line split by spaces and quotes + string[] linegc = Utilities.SplitLineAsCMP(line); + + // 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") + { + item.Name += $" {linegc[i]}"; + } + + // Perform correction + item.Name = item.Name.TrimStart(); + i--; + } + + // Get the size from the next part + else if (linegc[i] == "size") + { + if (!Int64.TryParse(linegc[++i], out long 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("\"", string.Empty)} {linegc[++i].Replace("\"", string.Empty)}"; + } + + // Get the CRC from the next part + else if (linegc[i] == "crc") + { + ((Rom)item).CRC = linegc[++i].Replace("\"", string.Empty).ToLowerInvariant(); + } + } + + // Now process and add the rom + ParseAddHelper(item, clean, remUnicode); + + line = reader.ReadLine(); + continue; + } + + line = reader.ReadLine(); + } + + // If no items were found for this machine, add a Blank placeholder + if (!containsItems) + { + Blank blank = new Blank() + { + SystemID = sysid, + System = filename, + SourceID = srcid, + }; + + blank.CopyMachineInformation(machine); + + // Now process and add the rom + ParseAddHelper(blank, clean, remUnicode); + } } @@ -198,7 +414,7 @@ namespace SabreTools.Library.DatFiles // Build the state based on excluded fields sw.Write("game (\n"); - sw.Write($"\tname \"{datItem.GetField(Field.MachineName, ExcludeFields)}.zip\n"); + sw.Write($"\tname \"{datItem.GetField(Field.MachineName, ExcludeFields)}.zip\"\n"); sw.Flush(); } @@ -265,7 +481,7 @@ namespace SabreTools.Library.DatFiles case ItemType.Rom: var rom = datItem as Rom; sw.Write("\tfile ("); - sw.Write($" name \"{datItem.GetField(Field.Name, ExcludeFields)}\""); + sw.Write($" name {datItem.GetField(Field.Name, ExcludeFields)}"); if (!ExcludeFields[(int)Field.Size] && rom.Size != -1) sw.Write($" size \"{rom.Size}\""); if (!string.IsNullOrWhiteSpace(datItem.GetField(Field.Date, ExcludeFields))) @@ -300,7 +516,6 @@ namespace SabreTools.Library.DatFiles // End game sw.Write(")\n"); - // Write the footer out sw.Flush(); } catch (Exception ex)