diff --git a/SabreTools.Helper/Dats/Partials/DatFile.Parsers.cs b/SabreTools.Helper/Dats/Partials/DatFile.Parsers.cs index 9006e916..ba7237ae 100644 --- a/SabreTools.Helper/Dats/Partials/DatFile.Parsers.cs +++ b/SabreTools.Helper/Dats/Partials/DatFile.Parsers.cs @@ -117,7 +117,7 @@ namespace SabreTools.Helper.Dats ParseCMP(filename, sysid, srcid, filter, trim, single, root, logger, keep, clean, descAsName); break; case DatFormat.CSV: - // Nothing yet + ParseCSVTSV(filename, sysid, srcid, ',', filter, trim, single, root, logger, keep, clean, descAsName); break; case DatFormat.Logiqx: case DatFormat.OfflineList: @@ -141,7 +141,7 @@ namespace SabreTools.Helper.Dats ParseRC(filename, sysid, srcid, filter, trim, single, root, logger, clean, descAsName); break; case DatFormat.TSV: - // Nothing yet + ParseCSVTSV(filename, sysid, srcid, '\t', filter, trim, single, root, logger, keep, clean, descAsName); break; default: return; @@ -871,6 +871,343 @@ namespace SabreTools.Helper.Dats sr.Dispose(); } + /// + /// Parse a CSV or a TSV 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 + /// Delimiter for parsing individual lines + /// Filter object for passing to the DatItem level + /// True if we are supposed to trim names to NTFS length, false otherwise + /// True if all games should be replaced by '!', false otherwise + /// String representing root directory to compare against for length calculation + /// Logger object for console and/or file output + /// True if full pathnames are to be kept, false otherwise (default) + /// True if game names are sanitized, false otherwise (default) + /// True if SL XML names should be kept, false otherwise (default) + private void ParseCSVTSV( + // Standard Dat parsing + string filename, + int sysid, + int srcid, + char delim, + + // Rom filtering + Filter filter, + + // Rom renaming + bool trim, + bool single, + string root, + + // Miscellaneous + Logger logger, + bool keep, + bool clean, + bool descAsName) + ) + { + // Open a file reader + Encoding enc = Style.GetEncoding(filename); + StreamReader sr = new StreamReader(File.OpenRead(filename), enc); + + // Create an empty list of columns to parse though + List columns = new List(); + + long linenum = -1; + while (!sr.EndOfStream) + { + string line = sr.ReadLine(); + linenum++; + + // Parse the first line, getting types from the column names + if (linenum == 0) + { + string[] parsedColumns = line.Split(delim); + foreach (string parsed in parsedColumns) + { + switch (parsed.ToLowerInvariant()) + { + case "file": + case "filename": + case "file name": + parsed.Add("DatFile.FileName"); + break; + case "internal name": + parsed.Add("DatFile.Name"); + break; + case "description": + case "dat description": + parsed.Add("DatFile.Description"); + break; + case "game name": + case "game": + case "machine": + parsed.Add("Machine.Name"); + break; + case "game description": + parsed.Add("Machine.Description"); + break; + case "type": + parsed.Add("DatItem.Type"); + break; + case "rom": + case "romname": + case "rom name": + case "name": + parsed.Add("Rom.Name"); + break; + case "disk": + case "diskname": + case "disk name": + parsed.Add("Disk.Name"); + break; + case "size": + parsed.Add("DatItem.Size"); + break; + case "crc": + case "crc hash" + parsed.Add("DatItem.CRC"); + break; + case "md5": + case "md5 hash": + parsed.Add("DatItem.MD5"); + break; + case "sha1": + case "sha-1": + case "sha1 hash": + case "sha-1 hash": + parsed.Add("DatItem.SHA1"); + break; + case "sha256": + case "sha-256": + case "sha256 hash": + case "sha-256 hash": + parsed.Add("DatItem.SHA256"); + break; + case "nodump": + case "no dump": + parsed.Add("DatItem.Nodump"); + break; + } + } + + continue; + } + + // Otherwise, we want to split the line and parse + string[] parsedLine = line.Split(delim); + + // If the line doesn't have the correct number of columns, we log and skip + if (parsedLine.Length != columns.Count) + { + logger.Warning("Malformed line found in '" + filename + " at line " + linenum); + continue; + } + + // Set the output item information + string machineName = null, machineDesc = null, name = null, crc = null, md5 = null, sha1 = null, sha256 = null; + long size = -1; + ItemType itemType = ItemType.Rom; + ItemStatus status = ItemStatus.None; + + // Now we loop through and get values for everything + for (int i = 0; i < columns.Count; i++) + { + string value = parsedLine[i]; + switch (columns[i]) + { + case "DatFile.FileName": + Filename = (String.IsNullOrEmpty(FileName) ? value : FileName); + break; + case "DatFile.Name"): + Name = (String.IsNullOrEmpty(Name) ? value : Name); + break; + case "DatFile.Description": + Description = (String.IsNullOrEmpty(Description) ? value : Description); + break; + case "Machine.Name": + machineName = value; + break; + case "Machine.Description": + machineDesc = value; + machineName = (descAsName ? value : machineName); + break; + case "DatItem.Type": + switch (value.ToLowerInvariant()) + { + case "archive": + itemType = ItemType.Archive; + break; + case "biosset": + itemType = ItemType.BiosSet; + break; + case "disk": + itemType = ItemType.Disk; + break; + case "release": + itemType = ItemType.Release; + break; + case "rom": + itemType = ItemType.Rom; + break; + case "sample": + itemType = ItemType.Sample; + break; + } + break; + case "Rom.Name": + case "Disk.Name": + name = value; + break; + case "DatItem.Size": + if (!Int64.TryParse(value, out size)) + { + size = -1; + } + break; + case "DatItem.CRC": + crc = value; + break; + case "DatItem.MD5": + md5 = value; + break; + case "DatItem.SHA1": + sha1 = value; + break; + case "DatItem.SHA256": + sha256 = value; + break; + case "DatItem.Nodump": + switch (value.ToLowerInvariant()) + { + case "baddump": + status = ItemStatus.BadDump; + break; + case "good": + status = ItemStatus.Good; + break; + case "no": + case "none": + status = ItemStatus.None; + break; + case "nodump": + case "yes": + nodump = true; + break; + case "verified": + status = ItemStatus.Verified; + break; + } + break; + } + } + + // And now we populate and add the new item + string key = ""; + switch (itemType) + { + case ItemType.Archive: + Archive archive = new Archive() + { + Name = name, + + Machine = new Machine() + { + Name = machineName, + Description = machineDesc, + }, + }; + + ParseAddHelper(archive, filter, trim, single, root, clean, logger, out key); + break; + case ItemType.BiosSet: + BiosSet biosset = new BiosSet() + { + Name = name, + + Machine = new Machine() + { + Name = machineName, + Description = machineDesc, + }, + }; + + ParseAddHelper(biosset, filter, trim, single, root, clean, logger, out key); + break; + case ItemType.Disk: + Disk disk = new Disk() + { + Name = name, + MD5 = md5, + SHA1 = sha1, + SHA256 = sha256, + + Machine = new Machine() + { + Name = machineName, + Description = machineDesc, + }, + + ItemStatus = status, + }; + + ParseAddHelper(disk, filter, trim, single, root, clean, logger, out key); + break; + case ItemType.Release: + Release release = new Release() + { + Name = name, + + Machine = new Machine() + { + Name = machineName, + Description = machineDesc, + }, + }; + + ParseAddHelper(release, filter, trim, single, root, clean, logger, out key); + break; + case ItemType.Rom: + Rom disk = new Rom() + { + Name = name, + Size = size, + CRC = crc, + MD5 = md5, + SHA1 = sha1, + SHA256 = sha256, + + Machine = new Machine() + { + Name = machineName, + Description = machineDesc, + }, + + ItemStatus = status, + }; + + ParseAddHelper(rom, filter, trim, single, root, clean, logger, out key); + break; + case ItemType.Sample: + Sample sample = new Sample() + { + Name = name, + + Machine = new Machine() + { + Name = machineName, + Description = machineDesc, + }, + }; + + ParseAddHelper(sample, filter, trim, single, root, clean, logger, out key); + break; + } + } + } + /// /// Parse an XML DAT (Logiqx, OfflineList, SabreDAT, and Software List) and return all found games and roms within ///