/// <summary> /// Parse a ClrMamePro DAT and return all found games and roms within /// </summary> /// <param name="filename">Name of the file to be parsed</param> /// <param name="indexId">Index ID for the DAT</param> /// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param> /// <param name="throwOnError">True if the error that is thrown should be thrown back to the caller, false otherwise</param> protected override void ParseFile(string filename, int indexId, bool keep, bool throwOnError = false) { // Open a file reader Encoding enc = FileExtensions.GetEncoding(filename); ClrMameProReader cmpr = new ClrMameProReader(FileExtensions.TryOpenRead(filename), enc) { DosCenter = false, Quotes = Quotes, }; while (!cmpr.EndOfStream) { try { cmpr.ReadNextLine(); // Ignore everything not top-level if (cmpr.RowType != CmpRowType.TopLevel) { continue; } // Switch on the top-level name switch (cmpr.TopLevel.ToLowerInvariant()) { // Header values case "clrmamepro": case "romvault": ReadHeader(cmpr, keep); break; // Sets case "set": // Used by the most ancient DATs case "game": // Used by most CMP DATs case "machine": // Possibly used by MAME CMP DATs ReadSet(cmpr, false, filename, indexId); break; case "resource": // Used by some other DATs to denote a BIOS set ReadSet(cmpr, true, filename, indexId); break; default: break; } } catch (Exception ex) { string message = $"'{filename}' - There was an error parsing line {cmpr.LineNumber} '{cmpr.CurrentLine}'"; logger.Error(ex, message); if (throwOnError) { cmpr.Dispose(); throw new Exception(message, ex); } } } cmpr.Dispose(); }
/// <summary> /// Parse a DOSCenter DAT and return all found games and roms within /// </summary> /// <param name="filename">Name of the file to be parsed</param> /// <param name="indexId">Index ID for the DAT</param> /// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param> /// <param name="throwOnError">True if the error that is thrown should be thrown back to the caller, false otherwise</param> protected override void ParseFile(string filename, int indexId, bool keep, bool throwOnError = false) { // Open a file reader Encoding enc = FileExtensions.GetEncoding(filename); ClrMameProReader cmpr = new ClrMameProReader(FileExtensions.TryOpenRead(filename), enc) { DosCenter = true }; while (!cmpr.EndOfStream) { try { cmpr.ReadNextLine(); // Ignore everything not top-level if (cmpr.RowType != CmpRowType.TopLevel) { continue; } // Switch on the top-level name switch (cmpr.TopLevel.ToLowerInvariant()) { // Header values case "doscenter": ReadHeader(cmpr); break; // Sets case "game": ReadGame(cmpr, filename, indexId); break; default: break; } } catch (Exception ex) { string message = $"'{filename}' - There was an error parsing line {cmpr.LineNumber} '{cmpr.CurrentLine}'"; logger.Error(ex, message); if (throwOnError) { cmpr.Dispose(); throw new Exception(message, ex); } } } cmpr.Dispose(); }
/// <summary> /// Read set information /// </summary> /// <param name="cmpr">ClrMameProReader to use to parse the header</param> /// <param name="resource">True if the item is a resource (bios), false otherwise</param> /// <param name="statsOnly">True to only add item statistics while parsing, false otherwise</param> /// <param name="filename">Name of the file to be parsed</param> /// <param name="indexId">Index ID for the DAT</param> private void ReadSet( ClrMameProReader cmpr, bool resource, bool statsOnly, // Standard Dat parsing string filename, int indexId) { // Prepare all internal variables bool containsItems = false; Machine machine = new Machine() { MachineType = (resource ? MachineType.Bios : MachineType.NULL), }; // If there's no subtree to the header, skip it if (cmpr == null || cmpr.EndOfStream) { return; } // While we don't hit an end element or end of stream while (!cmpr.EndOfStream) { cmpr.ReadNextLine(); // Ignore comments and nothingness if (cmpr.RowType == CmpRowType.None || cmpr.RowType == CmpRowType.Comment) { continue; } // If we reached the end of a section, break if (cmpr.RowType == CmpRowType.EndTopLevel) { break; } // Handle any standalone items if (cmpr.RowType == CmpRowType.Standalone && cmpr.Standalone != null) { string itemKey = cmpr.Standalone?.Key.ToLowerInvariant(); string itemVal = cmpr.Standalone?.Value; switch (itemKey) { case "name": machine.Name = itemVal; break; case "description": machine.Description = itemVal; break; case "year": machine.Year = itemVal; break; case "manufacturer": machine.Manufacturer = itemVal; break; case "category": machine.Category = itemVal; break; case "cloneof": machine.CloneOf = itemVal; break; case "romof": machine.RomOf = itemVal; break; case "sampleof": machine.SampleOf = itemVal; break; } } // Handle any internal items else if (cmpr.RowType == CmpRowType.Internal && !string.IsNullOrWhiteSpace(cmpr.InternalName) && cmpr.Internal != null) { containsItems = true; string itemKey = cmpr.InternalName; // Create the proper DatItem based on the type ItemType itemType = itemKey.AsItemType() ?? ItemType.Rom; DatItem item = DatItem.Create(itemType); // Then populate it with information item.CopyMachineInformation(machine); item.Source.Index = indexId; item.Source.Name = filename; // Loop through all of the attributes foreach (var kvp in cmpr.Internal) { string attrKey = kvp.Key; string attrVal = kvp.Value; switch (attrKey) { //If the item is empty, we automatically skip it because it's a fluke case "": continue; // Regular attributes case "name": item.SetName(attrVal); break; case "size": if (item.ItemType == ItemType.Rom) { (item as Rom).Size = Utilities.CleanLong(attrVal); } break; case "crc": if (item.ItemType == ItemType.Rom) { (item as Rom).CRC = attrVal; } break; case "md5": if (item.ItemType == ItemType.Disk) { (item as Disk).MD5 = attrVal; } else if (item.ItemType == ItemType.Media) { (item as Media).MD5 = attrVal; } else if (item.ItemType == ItemType.Rom) { (item as Rom).MD5 = attrVal; } break; case "sha1": if (item.ItemType == ItemType.Disk) { (item as Disk).SHA1 = attrVal; } else if (item.ItemType == ItemType.Media) { (item as Media).SHA1 = attrVal; } else if (item.ItemType == ItemType.Rom) { (item as Rom).SHA1 = attrVal; } break; case "sha256": if (item.ItemType == ItemType.Media) { (item as Media).SHA256 = attrVal; } else if (item.ItemType == ItemType.Rom) { (item as Rom).SHA256 = attrVal; } break; case "sha384": if (item.ItemType == ItemType.Rom) { (item as Rom).SHA384 = attrVal; } break; case "sha512": if (item.ItemType == ItemType.Rom) { (item as Rom).SHA512 = attrVal; } break; case "spamsum": if (item.ItemType == ItemType.Media) { (item as Media).SpamSum = attrVal; } else if (item.ItemType == ItemType.Rom) { (item as Rom).SpamSum = attrVal; } break; case "status": ItemStatus tempFlagStatus = attrVal.AsItemStatus(); if (item.ItemType == ItemType.Disk) { (item as Disk).ItemStatus = tempFlagStatus; } else if (item.ItemType == ItemType.Rom) { (item as Rom).ItemStatus = tempFlagStatus; } break; case "date": if (item.ItemType == ItemType.Release) { (item as Release).Date = attrVal; } else if (item.ItemType == ItemType.Rom) { (item as Rom).Date = attrVal; } break; case "default": if (item.ItemType == ItemType.BiosSet) { (item as BiosSet).Default = attrVal.AsYesNo(); } else if (item.ItemType == ItemType.Release) { (item as Release).Default = attrVal.AsYesNo(); } break; case "description": if (item.ItemType == ItemType.BiosSet) { (item as BiosSet).Description = attrVal; } break; case "region": if (item.ItemType == ItemType.Release) { (item as Release).Region = attrVal; } break; case "language": if (item.ItemType == ItemType.Release) { (item as Release).Language = attrVal; } break; } } // Now process and add the rom ParseAddHelper(item, statsOnly); } } // If no items were found for this machine, add a Blank placeholder if (!containsItems) { Blank blank = new Blank() { Source = new Source { Index = indexId, Name = filename, }, }; blank.CopyMachineInformation(machine); // Now process and add the rom ParseAddHelper(blank, statsOnly); } }
/// <summary> /// Read header information /// </summary> /// <param name="cmpr">ClrMameProReader to use to parse the header</param> /// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param> private void ReadHeader(ClrMameProReader cmpr, bool keep) { bool superdat = false; // If there's no subtree to the header, skip it if (cmpr == null || cmpr.EndOfStream) { return; } // While we don't hit an end element or end of stream while (!cmpr.EndOfStream) { cmpr.ReadNextLine(); // Ignore comments, internal items, and nothingness if (cmpr.RowType == CmpRowType.None || cmpr.RowType == CmpRowType.Comment || cmpr.RowType == CmpRowType.Internal) { continue; } // If we reached the end of a section, break if (cmpr.RowType == CmpRowType.EndTopLevel) { break; } // If the standalone value is null, we skip if (cmpr.Standalone == null) { continue; } string itemKey = cmpr.Standalone?.Key.ToLowerInvariant(); string itemVal = cmpr.Standalone?.Value; // For all other cases switch (itemKey) { case "name": Header.Name ??= itemVal; superdat |= itemVal.Contains(" - SuperDAT"); if (keep && superdat) { Header.Type ??= "SuperDAT"; } break; case "description": Header.Description ??= itemVal; break; case "rootdir": Header.RootDir ??= itemVal; break; case "category": Header.Category ??= itemVal; break; case "version": Header.Version ??= itemVal; break; case "date": Header.Date ??= itemVal; break; case "author": Header.Author ??= itemVal; break; case "email": Header.Email ??= itemVal; break; case "homepage": Header.Homepage ??= itemVal; break; case "url": Header.Url ??= itemVal; break; case "comment": Header.Comment ??= itemVal; break; case "header": Header.HeaderSkipper ??= itemVal; break; case "type": Header.Type ??= itemVal; superdat |= itemVal.Contains("SuperDAT"); break; case "forcemerging": if (Header.ForceMerging == MergingFlag.None) { Header.ForceMerging = itemVal.AsMergingFlag(); } break; case "forcezipping": if (Header.ForcePacking == PackingFlag.None) { Header.ForcePacking = itemVal.AsPackingFlag(); } break; case "forcepacking": if (Header.ForcePacking == PackingFlag.None) { Header.ForcePacking = itemVal.AsPackingFlag(); } break; } } }
/// <summary> /// Read header information /// </summary> /// <param name="cmpr">ClrMameProReader to use to parse the header</param> private void ReadHeader(ClrMameProReader cmpr) { // If there's no subtree to the header, skip it if (cmpr == null || cmpr.EndOfStream) { return; } // While we don't hit an end element or end of stream while (!cmpr.EndOfStream) { cmpr.ReadNextLine(); // Ignore comments, internal items, and nothingness if (cmpr.RowType == CmpRowType.None || cmpr.RowType == CmpRowType.Comment || cmpr.RowType == CmpRowType.Internal) { continue; } // If we reached the end of a section, break if (cmpr.RowType == CmpRowType.EndTopLevel) { break; } // If the standalone value is null, we skip if (cmpr.Standalone == null) { continue; } string itemKey = cmpr.Standalone?.Key.ToLowerInvariant().TrimEnd(':'); string itemVal = cmpr.Standalone?.Value; // For all other cases switch (itemKey) { case "name": Header.Name = Header.Name ?? itemVal; break; case "description": Header.Description = Header.Description ?? itemVal; break; case "dersion": Header.Version = Header.Version ?? itemVal; break; case "date": Header.Date = Header.Date ?? itemVal; break; case "author": Header.Author = Header.Author ?? itemVal; break; case "homepage": Header.Homepage = Header.Homepage ?? itemVal; break; case "comment": Header.Comment = Header.Comment ?? itemVal; break; } } }
/// <summary> /// Read set information /// </summary> /// <param name="cmpr">ClrMameProReader to use to parse the header</param> /// <param name="filename">Name of the file to be parsed</param> /// <param name="indexId">Index ID for the DAT</param> private void ReadGame(ClrMameProReader cmpr, string filename, int indexId) { // Prepare all internal variables bool containsItems = false; Machine machine = new Machine() { MachineType = MachineType.NULL, }; // If there's no subtree to the header, skip it if (cmpr == null || cmpr.EndOfStream) { return; } // While we don't hit an end element or end of stream while (!cmpr.EndOfStream) { cmpr.ReadNextLine(); // Ignore comments and nothingness if (cmpr.RowType == CmpRowType.None || cmpr.RowType == CmpRowType.Comment) { continue; } // If we reached the end of a section, break if (cmpr.RowType == CmpRowType.EndTopLevel) { break; } // Handle any standalone items if (cmpr.RowType == CmpRowType.Standalone && cmpr.Standalone != null) { string itemKey = cmpr.Standalone?.Key.ToLowerInvariant(); string itemVal = cmpr.Standalone?.Value; switch (itemKey) { 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; } } // Handle any internal items else if (cmpr.RowType == CmpRowType.Internal && string.Equals(cmpr.InternalName, "file", StringComparison.OrdinalIgnoreCase) && cmpr.Internal != null) { containsItems = true; // Create the proper DatItem based on the type Rom item = DatItem.Create(ItemType.Rom) as Rom; // Then populate it with information item.CopyMachineInformation(machine); item.Source = new Source { Index = indexId, Name = filename, }; // Loop through all of the attributes foreach (var kvp in cmpr.Internal) { string attrKey = kvp.Key; string attrVal = kvp.Value; switch (attrKey) { //If the item is empty, we automatically skip it because it's a fluke case "": continue; // Regular attributes case "name": item.Name = attrVal; break; case "size": item.Size = Sanitizer.CleanLong(attrVal); break; case "crc": item.CRC = attrVal; break; case "date": item.Date = attrVal; break; } } // Now process and add the rom ParseAddHelper(item); } } // If no items were found for this machine, add a Blank placeholder if (!containsItems) { Blank blank = new Blank() { Source = new Source { Index = indexId, Name = filename, }, }; blank.CopyMachineInformation(machine); // Now process and add the rom ParseAddHelper(blank); } }