diff --git a/SabreTools.Helper/Objects/Dat/DatFile.cs b/SabreTools.Helper/Objects/Dat/DatFile.cs index 33093cf2..3b1881b6 100644 --- a/SabreTools.Helper/Objects/Dat/DatFile.cs +++ b/SabreTools.Helper/Objects/Dat/DatFile.cs @@ -8,6 +8,8 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Web; using System.Xml; +using System.Xml.Linq; +using System.Xml.Schema; namespace SabreTools.Helper { @@ -386,7 +388,7 @@ namespace SabreTools.Helper _md5Count = 0; _sha1Count = 0; _nodumpCount = 0; - } + } #endregion @@ -3083,6 +3085,851 @@ namespace SabreTools.Helper xtr.Dispose(); } + /// + /// Parse an XML DAT (Logiqx, OfflineList, SabreDAT, and Software List) 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 + /// Name of the game to match (can use asterisk-partials) + /// Name of the rom to match (can use asterisk-partials) + /// Type of the rom to match + /// Find roms greater than or equal to this size + /// Find roms less than or equal to this size + /// Find roms equal to this size + /// CRC of the rom to match (can use asterisk-partials) + /// MD5 of the rom to match (can use asterisk-partials) + /// SHA-1 of the rom to match (can use asterisk-partials) + /// Select roms with the given status + /// 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 ParseXMLLinq( + // Standard Dat parsing + string filename, + int sysid, + int srcid, + + // Rom filtering + string gamename, + string romname, + string romtype, + long sgt, + long slt, + long seq, + string crc, + string md5, + string sha1, + ItemStatus itemStatus, + + // Rom renaming + bool trim, + bool single, + string root, + + // Miscellaneous + Logger logger, + bool keep, + bool clean, + bool softlist) + + { + // Prepare all internal variables + bool superdat = false, empty = true; + string key = "", date = ""; + long size = -1; + ItemStatus its = ItemStatus.None; + List parent = new List(); + + Encoding enc = Style.GetEncoding(filename); + XElement xtr = XElement.Load(FileTools.GetXmlTextReader(filename, logger), LoadOptions.None); + + // If we got a null reader, just return + if (xtr == null) + { + return; + } + + // Otherwise, read the file to the end + foreach (XElement node in xtr.Nodes()) + { + // If we're ending a folder or game, take care of possibly empty games and removing from the parent + if (node.NodeType == XmlNodeType.EndElement && (node.Name == "directory" || node.Name == "dir")) + { + // If we didn't find any items in the folder, make sure to add the blank rom + if (empty) + { + string tempgame = String.Join("\\", parent); + Rom rom = new Rom("null", tempgame); + + // Now process and add the rom + ParseAddHelper(rom, gamename, romname, romtype, sgt, slt, seq, crc, md5, sha1, itemStatus, trim, single, root, clean, logger, out key); + } + + // Regardless, end the current folder + int parentcount = parent.Count; + if (parentcount == 0) + { + logger.Verbose("Empty parent: " + String.Join("\\", parent)); + empty = true; + } + + // If we have an end folder element, remove one item from the parent, if possible + if (parentcount > 0) + { + parent.RemoveAt(parent.Count - 1); + if (keep && parentcount > 1) + { + Type = (String.IsNullOrEmpty(Type) ? "SuperDAT" : Type); + superdat = true; + } + } + } + + // We only want elements + if (node.NodeType != XmlNodeType.Element) + { + continue; + } + + switch (node.Name.ToString()) + { + // Handle MAME listxml since they're halfway between a SL and a Logiqx XML + case "mame": + if (node.Attribute("build") != null) + { + Name = (String.IsNullOrEmpty(Name) ? node.Attribute("build").ToString() : Name); + Description = (String.IsNullOrEmpty(Description) ? Name : Name); + } + break; + // New software lists have this behavior + case "softwarelist": + if (node.Attribute("name") != null) + { + Name = (String.IsNullOrEmpty(Name) ? node.Attribute("name").ToString() : Name); + } + if (node.Attribute("description") != null) + { + Description = (String.IsNullOrEmpty(Description) ? node.Attribute("description").ToString() : Description); + } + if (node.Attribute("forcemerging") != null) + { + switch (node.Attribute("forcemerging").ToString()) + { + case "split": + ForceMerging = ForceMerging.Split; + break; + case "none": + ForceMerging = ForceMerging.None; + break; + case "full": + ForceMerging = ForceMerging.Full; + break; + } + } + if (node.Attribute("forceitemStatus") != null) + { + switch (node.Attribute("forceitemStatus").ToString()) + { + case "obsolete": + ForceNodump = ForceNodump.Obsolete; + break; + case "required": + ForceNodump = ForceNodump.Required; + break; + case "ignore": + ForceNodump = ForceNodump.Ignore; + break; + } + } + if (node.Attribute("forcepacking") != null) + { + switch (node.Attribute("forcepacking").ToString()) + { + case "zip": + ForcePacking = ForcePacking.Zip; + break; + case "unzip": + ForcePacking = ForcePacking.Unzip; + break; + } + } + break; + // Handle M1 DATs since they're 99% the same as a SL DAT + case "m1": + Name = (String.IsNullOrEmpty(Name) ? "M1" : Name); + Description = (String.IsNullOrEmpty(Description) ? "M1" : Description); + if (node.Attribute("version") != null) + { + Version = (String.IsNullOrEmpty(Version) ? node.Attribute("version").ToString() : Version); + } + break; + // OfflineList has a different header format + case "configuration": + // If there's no subtree to the header, skip it + if (!node.HasElements) + { + continue; + } + + // Otherwise, read what we can from the header + foreach (XElement headnode in node.Elements()) + { + // We only want elements + if (headnode.NodeType != XmlNodeType.Element || headnode.Name == "configuration") + { + continue; + } + + // Get all header items (ONLY OVERWRITE IF THERE'S NO DATA) + string content = ""; + switch (headnode.Name.ToString().ToLowerInvariant()) + { + case "datname": + content = headnode.Value; + Name = (String.IsNullOrEmpty(Name) ? content : Name); + superdat = superdat || content.Contains(" - SuperDAT"); + if (keep && superdat) + { + Type = (String.IsNullOrEmpty(Type) ? "SuperDAT" : Type); + } + break; + case "datversionurl": + content = headnode.Value; + Url = (String.IsNullOrEmpty(Name) ? content : Url); + break; + default: + break; + } + } + + break; + // We want to process the entire subtree of the header + case "header": + // If there's no subtree to the header, skip it + if (!node.HasElements) + { + continue; + } + + // Otherwise, read what we can from the header + foreach (XElement headnode in node.Elements()) + { + // We only want elements + if (headnode.NodeType != XmlNodeType.Element || headnode.Name == "header") + { + continue; + } + + // Get all header items (ONLY OVERWRITE IF THERE'S NO DATA) + string content = ""; + switch (headnode.Name.ToString()) + { + case "name": + content = headnode.Value; + Name = (String.IsNullOrEmpty(Name) ? content : Name); + superdat = superdat || content.Contains(" - SuperDAT"); + if (keep && superdat) + { + Type = (String.IsNullOrEmpty(Type) ? "SuperDAT" : Type); + } + break; + case "description": + content = headnode.Value; + Description = (String.IsNullOrEmpty(Description) ? content : Description); + break; + case "rootdir": + content = headnode.Value; + RootDir = (String.IsNullOrEmpty(RootDir) ? content : RootDir); + break; + case "category": + content = headnode.Value; + Category = (String.IsNullOrEmpty(Category) ? content : Category); + break; + case "version": + content = headnode.Value; + Version = (String.IsNullOrEmpty(Version) ? content : Version); + break; + case "date": + content = headnode.Value; + Date = (String.IsNullOrEmpty(Date) ? content.Replace(".", "/") : Date); + break; + case "author": + content = headnode.Value; + Author = (String.IsNullOrEmpty(Author) ? content : Author); + + // Special cases for SabreDAT + Email = (String.IsNullOrEmpty(Email) && headnode.Attribute("email") != null ? + headnode.Attribute("email").ToString() : Email); + Homepage = (String.IsNullOrEmpty(Homepage) && headnode.Attribute("homepage") != null ? + headnode.Attribute("homepage").ToString() : Email); + Url = (String.IsNullOrEmpty(Url) && headnode.Attribute("url") != null ? + headnode.Attribute("url").ToString() : Email); + break; + case "email": + content = headnode.Value; + Email = (String.IsNullOrEmpty(Email) ? content : Email); + break; + case "homepage": + content = headnode.Value; + Homepage = (String.IsNullOrEmpty(Homepage) ? content : Homepage); + break; + case "url": + content = headnode.Value; + Url = (String.IsNullOrEmpty(Url) ? content : Url); + break; + case "comment": + content = headnode.Value; + Comment = (String.IsNullOrEmpty(Comment) ? content : Comment); + break; + case "type": + content = headnode.Value; + Type = (String.IsNullOrEmpty(Type) ? content : Type); + superdat = superdat || content.Contains("SuperDAT"); + break; + case "clrmamepro": + case "romcenter": + if (headnode.Attribute("header") != null) + { + Header = (String.IsNullOrEmpty(Header) ? headnode.Attribute("header").ToString() : Header); + } + if (headnode.Attribute("plugin") != null) + { + Header = (String.IsNullOrEmpty(Header) ? headnode.Attribute("plugin").ToString() : Header); + } + if (headnode.Attribute("forcemerging") != null) + { + switch (headnode.Attribute("forcemerging").ToString()) + { + case "split": + ForceMerging = ForceMerging.Split; + break; + case "none": + ForceMerging = ForceMerging.None; + break; + case "full": + ForceMerging = ForceMerging.Full; + break; + } + } + if (headnode.Attribute("forceitemStatus") != null) + { + switch (headnode.Attribute("forceitemStatus").ToString()) + { + case "obsolete": + ForceNodump = ForceNodump.Obsolete; + break; + case "required": + ForceNodump = ForceNodump.Required; + break; + case "ignore": + ForceNodump = ForceNodump.Ignore; + break; + } + } + if (headnode.Attribute("forcepacking") != null) + { + switch (headnode.Attribute("forcepacking").ToString()) + { + case "zip": + ForcePacking = ForcePacking.Zip; + break; + case "unzip": + ForcePacking = ForcePacking.Unzip; + break; + } + } + break; + case "flags": + // If there's no subtree to the flags, skip it + if (!headnode.HasElements) + { + continue; + } + + // Otherwise, read what we can from the flags + foreach (XElement flagnode in node.Elements()) + { + // We only want elements + if (flagnode.NodeType != XmlNodeType.Element || flagnode.Name == "flags") + { + continue; + } + + switch (flagnode.Name.ToString()) + { + case "flag": + if (flagnode.Attribute("name") != null && flagnode.Attribute("value") != null) + { + content = flagnode.Attribute("value").ToString(); + switch (flagnode.Attribute("name").ToString()) + { + case "type": + Type = (String.IsNullOrEmpty(Type) ? content : Type); + superdat = superdat || content.Contains("SuperDAT"); + break; + case "forcemerging": + switch (content) + { + case "split": + ForceMerging = ForceMerging.Split; + break; + case "none": + ForceMerging = ForceMerging.None; + break; + case "full": + ForceMerging = ForceMerging.Full; + break; + } + break; + case "forceitemStatus": + switch (content) + { + case "obsolete": + ForceNodump = ForceNodump.Obsolete; + break; + case "required": + ForceNodump = ForceNodump.Required; + break; + case "ignore": + ForceNodump = ForceNodump.Ignore; + break; + } + break; + case "forcepacking": + switch (content) + { + case "zip": + ForcePacking = ForcePacking.Zip; + break; + case "unzip": + ForcePacking = ForcePacking.Unzip; + break; + } + break; + } + } + break; + default: + break; + } + } + break; + default: + break; + } + } + break; + case "machine": + case "game": + case "software": + string temptype = node.Name.ToString(); + string tempname = "", gamedesc = "", romof = "", + cloneof = "", sampleof = "", year = "", manufacturer = ""; + + // If there's no subtree to the header, skip it + if (!node.HasElements) + { + continue; + } + + // Otherwise, add what is possible + if (!softlist && temptype == "software" && node.Element("description") != null) + { + tempname = node.Element("description").Value; + gamedesc = tempname; + tempname = tempname.Replace('/', '_').Replace("\"", "''"); + } + else + { + tempname = node.Attribute("name").ToString(); + romof = (node.Attribute("romof") != null ? node.Attribute("romof").ToString() : ""); + cloneof = (node.Attribute("cloneof") != null ? node.Attribute("cloneof").ToString() : ""); + sampleof = (node.Attribute("sampleof") != null ? node.Attribute("sampleof").ToString() : ""); + } + + if (superdat && !keep) + { + string tempout = Regex.Match(tempname, @".*?\\(.*)").Groups[1].Value; + if (tempout != "") + { + tempname = tempout; + } + } + // Get the name of the game from the parent + else if (superdat && keep && parent.Count > 0) + { + tempname = String.Join("\\", parent) + "\\" + tempname; + } + + // Special offline list parts + string ext = ""; + string releaseNumber = ""; + + // Now loop through the rest of the child nodes + foreach (XElement gamenode in node.Elements()) + { + // We only want elements + if (gamenode.NodeType != XmlNodeType.Element) + { + continue; + } + + // Get the roms from the machine + switch (gamenode.Name.ToString()) + { + // For offline lists only + case "title": + tempname = gamenode.Value; + break; + case "releaseNumber": + releaseNumber = gamenode.Value; + break; + case "romSize": + if (!Int64.TryParse(gamenode.Value, out size)) + { + size = -1; + } + break; + case "romCRC": + empty = false; + + ext = (gamenode.Attribute("extension") != null ? gamenode.Attribute("extension").ToString() : ""); + + DatItem olrom = new Rom(releaseNumber + " - " + tempname + ext, size, gamenode.Value, null, null, ItemStatus.None, + null, tempname, null, tempname, null, null, null, null, null, null, false, null, null, sysid, null, srcid, ""); + + // Now process and add the rom + ParseAddHelper(olrom, gamename, romname, romtype, sgt, slt, seq, crc, md5, sha1, itemStatus, trim, single, root, clean, logger, out key); + break; + + // For Logiqx, SabreDAT, and Software List + case "description": + gamedesc = gamenode.Value; + break; + case "year": + year = gamenode.Value; + break; + case "manufacturer": + manufacturer = gamenode.Value; + break; + case "release": + empty = false; + + bool? defaultrel = null; + if (gamenode.Attribute("default") != null) + { + if (gamenode.Attribute("default").ToString() == "yes") + { + defaultrel = true; + } + else if (gamenode.Attribute("default").ToString() == "no") + { + defaultrel = false; + } + } + + DatItem relrom = new Release(gamenode.Attribute("name").ToString(), + gamenode.Attribute("region").ToString(), gamenode.Attribute("language").ToString(), date, defaultrel); + + // Now process and add the rom + ParseAddHelper(relrom, gamename, romname, romtype, sgt, slt, seq, crc, md5, sha1, itemStatus, trim, single, root, clean, logger, out key); + break; + case "biosset": + empty = false; + + bool? defaultbios = null; + if (gamenode.Attribute("default") != null) + { + if (gamenode.Attribute("default").ToString() == "yes") + { + defaultbios = true; + } + else if (gamenode.Attribute("default").ToString() == "no") + { + defaultbios = false; + } + } + + DatItem biosrom = new BiosSet(gamenode.Attribute("name").ToString(), gamenode.Attribute("description").ToString(), defaultbios, + tempname, null, gamedesc, null, null, romof, cloneof, sampleof, null, false, null, null, sysid, filename, srcid, null); + + // Now process and add the rom + ParseAddHelper(biosrom, gamename, romname, romtype, sgt, slt, seq, crc, md5, sha1, itemStatus, trim, single, root, clean, logger, out key); + break; + case "archive": + empty = false; + + DatItem archiverom = new Archive(gamenode.Attribute("name").ToString(), tempname, null, gamedesc, null, null, + romof, cloneof, sampleof, null, false, null, null, sysid, filename, srcid, null); + + // Now process and add the rom + ParseAddHelper(archiverom, gamename, romname, romtype, sgt, slt, seq, crc, md5, sha1, itemStatus, trim, single, root, clean, logger, out key); + break; + case "sample": + empty = false; + + DatItem samplerom = new Sample(gamenode.Attribute("name").ToString(), tempname, null, gamedesc, null, null, + romof, cloneof, sampleof, null, false, null, null, sysid, filename, srcid, null); + + // Now process and add the rom + ParseAddHelper(samplerom, gamename, romname, romtype, sgt, slt, seq, crc, md5, sha1, itemStatus, trim, single, root, clean, logger, out key); + break; + case "rom": + case "disk": + empty = false; + + // If the rom has a status, flag it + its = ItemStatus.None; + if (gamenode.Attribute("flags").ToString() == "good" || gamenode.Attribute("status").ToString() == "good") + { + its = ItemStatus.Good; + } + if (gamenode.Attribute("flags").ToString() == "baddump" || gamenode.Attribute("status").ToString() == "baddump") + { + logger.Verbose("Bad dump detected: " + + (gamenode.Attribute("name") != null ? "\"" + node.Attribute("name") + "\"" : "ROM NAME NOT FOUND")); + its = ItemStatus.BadDump; + } + if (gamenode.Attribute("flags").ToString() == "itemStatus" || gamenode.Attribute("status").ToString() == "itemStatus") + { + logger.Verbose("Nodump detected: " + + (gamenode.Attribute("name") != null ? "\"" + node.Attribute("name") + "\"" : "ROM NAME NOT FOUND")); + its = ItemStatus.Nodump; + } + if (gamenode.Attribute("flags").ToString() == "verified" || gamenode.Attribute("status").ToString() == "verified") + { + its = ItemStatus.Verified; + } + + // If the rom has a Date attached, read it in and then sanitize it + date = ""; + if (gamenode.Attribute("date") != null) + { + DateTime dateTime = DateTime.Now; + if (DateTime.TryParse(gamenode.Attribute("date").ToString(), out dateTime)) + { + date = dateTime.ToString(); + } + else + { + date = gamenode.Attribute("date").ToString(); + } + } + + // Take care of hex-sized files + size = -1; + if (gamenode.Attribute("size") != null && gamenode.Attribute("size").ToString().Contains("0x")) + { + size = Convert.ToInt64(gamenode.Attribute("size").ToString(), 16); + } + else if (gamenode.Attribute("size") != null) + { + Int64.TryParse(gamenode.Attribute("size").ToString(), out size); + } + + // If the rom is continue or ignore, add the size to the previous rom + if (gamenode.Attribute("loadflag").ToString() == "continue" || gamenode.Attribute("loadflag").ToString() == "ignore") + { + int index = Files[key].Count() - 1; + DatItem lastrom = Files[key][index]; + if (lastrom.Type == ItemType.Rom) + { + ((Rom)lastrom).Size += size; + } + Files[key].RemoveAt(index); + Files[key].Add(lastrom); + continue; + } + + // If we're in clean mode, sanitize the game name + if (clean) + { + tempname = Style.CleanGameName(tempname.Split(Path.DirectorySeparatorChar)); + } + + DatItem inrom; + switch (gamenode.Name.ToString().ToLowerInvariant()) + { + case "disk": + inrom = new Disk(gamenode.Attribute("name").ToString(), gamenode.Attribute("md5").ToString(), gamenode.Attribute("sha1").ToString(), + its, tempname, null, gamedesc, null, null, romof, cloneof, sampleof, null, false, null, null, sysid, + filename, srcid, null); + break; + case "rom": + default: + inrom = new Rom(gamenode.Attribute("name").ToString(), size, gamenode.Attribute("crc").ToString(), gamenode.Attribute("md5").ToString(), + gamenode.Attribute("sha1").ToString(), its, date, tempname, null, gamedesc, null, null, romof, cloneof, sampleof, + null, false, null, null, sysid, filename, srcid, null); + break; + } + + // Now process and add the rom + ParseAddHelper(inrom, gamename, romname, romtype, sgt, slt, seq, crc, md5, sha1, itemStatus, trim, single, root, clean, logger, out key); + break; + default: + break; + } + } + + // If we didn't find any items in the folder, make sure to add the blank rom + if (empty) + { + tempname = (parent.Count > 0 ? String.Join("\\", parent) + Path.DirectorySeparatorChar : "") + tempname; + + Rom inrom = new Rom("null", tempname); + + // Now process and add the rom + ParseAddHelper(inrom, gamename, romname, romtype, sgt, slt, seq, crc, md5, sha1, itemStatus, trim, single, root, clean, logger, out key); + + // Regardless, end the current folder + if (parent.Count == 0) + { + empty = true; + } + } + break; + case "dir": + case "directory": + // Set SuperDAT flag for all SabreDAT inputs, regardless of depth + superdat = true; + if (keep) + { + Type = (Type == "" ? "SuperDAT" : Type); + } + + string foldername = (node.Attribute("name") == null ? "" : node.Attribute("name").ToString()); + if (foldername != "") + { + parent.Add(foldername); + } + break; + case "file": + empty = false; + + // If the rom is itemStatus, flag it + its = ItemStatus.None; + // If there's no subtree to the flags, skip it + if (!node.HasElements) + { + continue; + } + + // Otherwise, read what we can from the flags + foreach (XElement flagnode in node.Elements()) + { + // We only want elements + if (flagnode.NodeType != XmlNodeType.Element || flagnode.Name == "flags") + { + continue; + } + + switch (flagnode.Name.ToString()) + { + case "flag": + case "status": + if (flagnode.Attribute("name") != null && flagnode.Attribute("value") != null) + { + string content = flagnode.Attribute("value").ToString(); + switch (flagnode.Attribute("name").ToString()) + { + case "good": + its = ItemStatus.Good; + break; + case "baddump": + logger.Verbose("Bad dump detected: " + (node.Attribute("name") != null ? + "\"" + node.Attribute("name") + "\"" : "ROM NAME NOT FOUND")); + its = ItemStatus.BadDump; + break; + case "itemStatus": + logger.Verbose("Nodump detected: " + (node.Attribute("name") != null ? + "\"" + node.Attribute("name") + "\"" : "ROM NAME NOT FOUND")); + its = ItemStatus.Nodump; + break; + case "verified": + its = ItemStatus.Verified; + break; + } + } + break; + } + } + + // If the rom has a Date attached, read it in and then sanitize it + date = ""; + if (node.Attribute("date") != null) + { + date = DateTime.Parse(node.Attribute("date").ToString()).ToString(); + } + + // Take care of hex-sized files + size = -1; + if (node.Attribute("size") != null && node.Attribute("size").ToString().Contains("0x")) + { + size = Convert.ToInt64(node.Attribute("size").ToString(), 16); + } + else if (node.Attribute("size") != null) + { + Int64.TryParse(node.Attribute("size").ToString(), out size); + } + + // If the rom is continue or ignore, add the size to the previous rom + if (node.Attribute("loadflag").ToString() == "continue" || node.Attribute("loadflag").ToString() == "ignore") + { + int index = Files[key].Count() - 1; + DatItem lastrom = Files[key][index]; + if (lastrom.Type == ItemType.Rom) + { + ((Rom)lastrom).Size += size; + } + Files[key].RemoveAt(index); + Files[key].Add(lastrom); + continue; + } + + // Get the name of the game from the parent + tempname = String.Join("\\", parent); + + // If we aren't keeping names, trim out the path + if (!keep || !superdat) + { + string tempout = Regex.Match(tempname, @".*?\\(.*)").Groups[1].Value; + if (tempout != "") + { + tempname = tempout; + } + } + + DatItem rom; + switch (node.Attribute("type").ToString().ToLowerInvariant()) + { + case "disk": + rom = new Disk(node.Attribute("name").ToString(), node.Attribute("md5")?.ToString().ToLowerInvariant(), + node.Attribute("sha1")?.ToString().ToLowerInvariant(), its, tempname, null, tempname, null, null, + null, null, null, null, false, null, null, sysid, filename, srcid, null); + break; + case "rom": + default: + rom = new Rom(node.Attribute("name").ToString(), size, node.Attribute("crc")?.ToString().ToLowerInvariant(), + node.Attribute("md5")?.ToString().ToLowerInvariant(), node.Attribute("sha1")?.ToString().ToLowerInvariant(), its, + date, tempname, null, tempname, null, null, null, null, null, null, false, null, null, sysid, filename, + srcid, null); + break; + } + + // Now process and add the rom + ParseAddHelper(rom, gamename, romname, romtype, sgt, slt, seq, crc, md5, sha1, itemStatus, trim, single, root, clean, logger, out key); + break; + default: + break; + } + } + } + /// /// Add a rom to the Dat after checking ///