/// <summary> /// Use cloneof tags to add roms to the children, setting the new romof tag in the process /// </summary> /// <param name="datFile">Current DatFile object to run operations on</param> internal static void AddRomsFromParent(DatFile datFile) { List <string> games = datFile.Items.Keys.OrderBy(g => g).ToList(); foreach (string game in games) { // If the game has no items in it, we want to continue if (datFile.Items[game].Count == 0) { continue; } // Determine if the game has a parent or not string parent = null; if (!string.IsNullOrWhiteSpace(datFile.Items[game][0].Machine.CloneOf)) { parent = datFile.Items[game][0].Machine.CloneOf; } // If the parent doesnt exist, we want to continue if (string.IsNullOrWhiteSpace(parent)) { continue; } // If the parent doesn't have any items, we want to continue if (datFile.Items[parent].Count == 0) { continue; } // If the parent exists and has items, we copy the items from the parent to the current game DatItem copyFrom = datFile.Items[game][0]; ConcurrentList <DatItem> parentItems = datFile.Items[parent]; foreach (DatItem item in parentItems) { DatItem datItem = (DatItem)item.Clone(); datItem.CopyMachineInformation(copyFrom); if (datFile.Items[game].Where(i => i.GetName()?.ToLowerInvariant() == datItem.GetName()?.ToLowerInvariant()).Count() == 0 && !datFile.Items[game].Contains(datItem)) { datFile.Items.Add(game, datItem); } } // Now we want to get the parent romof tag and put it in each of the items ConcurrentList <DatItem> items = datFile.Items[game]; string romof = datFile.Items[parent][0].Machine.RomOf; foreach (DatItem item in items) { item.Machine.RomOf = romof; } } }
/// <summary> /// Read Files information /// </summary> /// <param name="xtr">XmlReader to use to parse the header</param> /// <param name="machine">Machine to copy information from</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 ReadFiles(XmlReader xtr, Machine machine, bool statsOnly, string filename, int indexId) { // If the reader is invalid, skip if (xtr == null) { return; } // Otherwise, read the items xtr.MoveToContent(); while (!xtr.EOF) { // We only want elements if (xtr.NodeType != XmlNodeType.Element) { xtr.Read(); continue; } switch (xtr.Name) { case "datitem": XmlSerializer xs = new XmlSerializer(typeof(DatItem)); DatItem item = xs.Deserialize(xtr.ReadSubtree()) as DatItem; item.CopyMachineInformation(machine); item.Source = new Source { Name = filename, Index = indexId }; ParseAddHelper(item, statsOnly); xtr.Skip(); break; default: xtr.Read(); break; } } }
/// <summary> /// Read set information /// </summary> /// <param name="reader">StreamReader to use to parse the header</param> /// <param name="resource">True if the item is a resource (bios), false otherwise</param> /// <param name="filename">Name of the file to be parsed</param> /// <param name="sysid">System ID for the DAT</param> /// <param name="srcid">Source ID for the DAT</param> /// <param name="keep">True if full pathnames are to be kept, false otherwise (default)</param> /// <param name="clean">True if game names are sanitized, false otherwise (default)</param> /// <param name="remUnicode">True if we should remove non-ASCII characters from output, false otherwise (default)</param> private void ReadSet( StreamReader reader, bool resource, // 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() { MachineType = (resource ? MachineType.Bios : 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)) { // 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.SystemID = sysid; item.System = filename; 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.ItemType == ItemType.Rom) { ((Rom)item).ItemStatus = tempStandaloneStatus; } else if (item.ItemType == ItemType.Disk) { ((Disk)item).ItemStatus = tempStandaloneStatus; } break; // Regular attributes case "name": quoteless = linegc[++i].Replace("\"", ""); item.Name = quoteless; break; case "size": if (item.ItemType == 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.ItemType == ItemType.Rom) { quoteless = linegc[++i].Replace("\"", ""); ((Rom)item).CRC = Utilities.CleanHashData(quoteless, Constants.CRCLength); } break; case "md5": if (item.ItemType == ItemType.Rom) { quoteless = linegc[++i].Replace("\"", ""); ((Rom)item).MD5 = Utilities.CleanHashData(quoteless, Constants.MD5Length); } else if (item.ItemType == ItemType.Disk) { i++; quoteless = linegc[i].Replace("\"", ""); ((Disk)item).MD5 = Utilities.CleanHashData(quoteless, Constants.MD5Length); } break; case "sha1": if (item.ItemType == ItemType.Rom) { quoteless = linegc[++i].Replace("\"", ""); ((Rom)item).SHA1 = Utilities.CleanHashData(quoteless, Constants.SHA1Length); } else if (item.ItemType == ItemType.Disk) { quoteless = linegc[++i].Replace("\"", ""); ((Disk)item).SHA1 = Utilities.CleanHashData(quoteless, Constants.SHA1Length); } break; case "sha256": if (item.ItemType == ItemType.Rom) { quoteless = linegc[++i].Replace("\"", ""); ((Rom)item).SHA256 = Utilities.CleanHashData(quoteless, Constants.SHA256Length); } else if (item.ItemType == ItemType.Disk) { quoteless = linegc[++i].Replace("\"", ""); ((Disk)item).SHA256 = Utilities.CleanHashData(quoteless, Constants.SHA256Length); } break; case "sha384": if (item.ItemType == ItemType.Rom) { quoteless = linegc[++i].Replace("\"", ""); ((Rom)item).SHA384 = Utilities.CleanHashData(quoteless, Constants.SHA384Length); } else if (item.ItemType == ItemType.Disk) { quoteless = linegc[++i].Replace("\"", ""); ((Disk)item).SHA384 = Utilities.CleanHashData(quoteless, Constants.SHA384Length); } break; case "sha512": if (item.ItemType == ItemType.Rom) { quoteless = linegc[++i].Replace("\"", ""); ((Rom)item).SHA512 = Utilities.CleanHashData(quoteless, Constants.SHA512Length); } else if (item.ItemType == ItemType.Disk) { quoteless = linegc[++i].Replace("\"", ""); ((Disk)item).SHA512 = Utilities.CleanHashData(quoteless, Constants.SHA512Length); } break; case "status": case "flags": quoteless = linegc[++i].Replace("\"", ""); ItemStatus tempFlagStatus = Utilities.GetItemStatus(quoteless); if (item.ItemType == ItemType.Rom) { ((Rom)item).ItemStatus = tempFlagStatus; } else if (item.ItemType == ItemType.Disk) { ((Disk)item).ItemStatus = tempFlagStatus; } break; case "date": if (item.ItemType == 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.ItemType == 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.ItemType == ItemType.BiosSet) { quoteless = linegc[++i].Replace("\"", ""); ((BiosSet)item).Default = Utilities.GetYesNo(quoteless.ToLowerInvariant()); } else if (item.ItemType == ItemType.Release) { quoteless = linegc[++i].Replace("\"", ""); ((Release)item).Default = Utilities.GetYesNo(quoteless.ToLowerInvariant()); } break; case "description": if (item.ItemType == ItemType.BiosSet) { quoteless = linegc[++i].Replace("\"", ""); ((BiosSet)item).Description = quoteless.ToLowerInvariant(); } break; case "region": if (item.ItemType == ItemType.Release) { quoteless = linegc[++i].Replace("\"", ""); ((Release)item).Region = quoteless.ToLowerInvariant(); } break; case "language": if (item.ItemType == 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() { SystemID = sysid, System = filename, SourceID = srcid, }; blank.CopyMachineInformation(machine); // Now process and add the rom ParseAddHelper(blank, clean, remUnicode); } }
/// <summary> /// Read item information /// </summary> /// <param name="machineObj">JObject representing a single datitem</param> /// <param name="filename">Name of the file to be parsed</param> /// <param name="indexId">Index ID for the DAT</param> /// <param name="machine">Machine information to add to the parsed items</param> private void ReadItem( JObject itemObj, // Standard Dat parsing string filename, int indexId, // Miscellaneous Machine machine) { // If we have an empty item, skip it if (itemObj == null) { return; } // Prepare internal variables DatItem datItem = null; // Read the datitem info, if possible if (itemObj.ContainsKey("datitem")) { JToken datItemObj = itemObj["datitem"]; switch (datItemObj.Value <string>("type").AsItemType()) { case ItemType.Adjuster: datItem = datItemObj.ToObject <Adjuster>(); break; case ItemType.Analog: datItem = datItemObj.ToObject <Analog>(); break; case ItemType.Archive: datItem = datItemObj.ToObject <Archive>(); break; case ItemType.BiosSet: datItem = datItemObj.ToObject <BiosSet>(); break; case ItemType.Blank: datItem = datItemObj.ToObject <Blank>(); break; case ItemType.Chip: datItem = datItemObj.ToObject <Chip>(); break; case ItemType.Condition: datItem = datItemObj.ToObject <Condition>(); break; case ItemType.Configuration: datItem = datItemObj.ToObject <Configuration>(); break; case ItemType.Control: datItem = datItemObj.ToObject <Control>(); break; case ItemType.DataArea: datItem = datItemObj.ToObject <DataArea>(); break; case ItemType.Device: datItem = datItemObj.ToObject <Device>(); break; case ItemType.DeviceReference: datItem = datItemObj.ToObject <DeviceReference>(); break; case ItemType.DipSwitch: datItem = datItemObj.ToObject <DipSwitch>(); break; case ItemType.Disk: datItem = datItemObj.ToObject <Disk>(); break; case ItemType.DiskArea: datItem = datItemObj.ToObject <DiskArea>(); break; case ItemType.Display: datItem = datItemObj.ToObject <Display>(); break; case ItemType.Driver: datItem = datItemObj.ToObject <Driver>(); break; case ItemType.Extension: datItem = datItemObj.ToObject <Extension>(); break; case ItemType.Feature: datItem = datItemObj.ToObject <Feature>(); break; case ItemType.Info: datItem = datItemObj.ToObject <Info>(); break; case ItemType.Input: datItem = datItemObj.ToObject <Input>(); break; case ItemType.Instance: datItem = datItemObj.ToObject <Instance>(); break; case ItemType.Location: datItem = datItemObj.ToObject <Location>(); break; case ItemType.Media: datItem = datItemObj.ToObject <Media>(); break; case ItemType.Part: datItem = datItemObj.ToObject <Part>(); break; case ItemType.PartFeature: datItem = datItemObj.ToObject <PartFeature>(); break; case ItemType.Port: datItem = datItemObj.ToObject <Port>(); break; case ItemType.RamOption: datItem = datItemObj.ToObject <RamOption>(); break; case ItemType.Release: datItem = datItemObj.ToObject <Release>(); break; case ItemType.Rom: datItem = datItemObj.ToObject <Rom>(); break; case ItemType.Sample: datItem = datItemObj.ToObject <Sample>(); break; case ItemType.Setting: datItem = datItemObj.ToObject <Setting>(); break; case ItemType.SharedFeature: datItem = datItemObj.ToObject <SharedFeature>(); break; case ItemType.Slot: datItem = datItemObj.ToObject <Slot>(); break; case ItemType.SlotOption: datItem = datItemObj.ToObject <SlotOption>(); break; case ItemType.SoftwareList: datItem = datItemObj.ToObject <DatItems.SoftwareList>(); break; case ItemType.Sound: datItem = datItemObj.ToObject <Sound>(); break; } } // If we got a valid datitem, copy machine info and add if (datItem != null) { datItem.CopyMachineInformation(machine); datItem.Source = new Source { Index = indexId, Name = filename }; ParseAddHelper(datItem); } }
/// <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> /// Use device_ref and optionally slotoption tags to add roms to the children /// </summary> /// <param name="datFile">Current DatFile object to run operations on</param> /// <param name="dev">True if only child device sets are touched, false for non-device sets (default)</param> /// <param name="useSlotOptions">True if slotoptions tags are used as well, false otherwise</param> internal static bool AddRomsFromDevices(DatFile datFile, bool dev = false, bool useSlotOptions = false) { bool foundnew = false; List <string> machines = datFile.Items.Keys.OrderBy(g => g).ToList(); foreach (string machine in machines) { // If the machine doesn't have items, we continue if (datFile.Items[machine] == null || datFile.Items[machine].Count == 0) { continue; } // If the machine (is/is not) a device, we want to continue if (dev ^ (datFile.Items[machine][0].Machine.MachineType.HasFlag(MachineType.Device))) { continue; } // Get all device reference names from the current machine List <string> deviceReferences = datFile.Items[machine] .Where(i => i.ItemType == ItemType.DeviceReference) .Select(i => i as DeviceReference) .Select(dr => dr.Name) .Distinct() .ToList(); // Get all slot option names from the current machine List <string> slotOptions = datFile.Items[machine] .Where(i => i.ItemType == ItemType.Slot) .Select(i => i as Slot) .Where(s => s.SlotOptionsSpecified) .SelectMany(s => s.SlotOptions) .Select(so => so.DeviceName) .Distinct() .ToList(); // If we're checking device references if (deviceReferences.Any()) { // Loop through all names and check the corresponding machines List <string> newDeviceReferences = new List <string>(); foreach (string deviceReference in deviceReferences) { // If the machine doesn't exist then we continue if (datFile.Items[deviceReference] == null || datFile.Items[deviceReference].Count == 0) { continue; } // Add to the list of new device reference names ConcurrentList <DatItem> devItems = datFile.Items[deviceReference]; newDeviceReferences.AddRange(devItems .Where(i => i.ItemType == ItemType.DeviceReference) .Select(i => (i as DeviceReference).Name)); // Set new machine information and add to the current machine DatItem copyFrom = datFile.Items[machine][0]; foreach (DatItem item in devItems) { // If the parent machine doesn't already contain this item, add it if (!datFile.Items[machine].Any(i => i.ItemType == item.ItemType && i.GetName() == item.GetName())) { // Set that we found new items foundnew = true; // Clone the item and then add it DatItem datItem = (DatItem)item.Clone(); datItem.CopyMachineInformation(copyFrom); datFile.Items.Add(machine, datItem); } } } // Now that every device reference is accounted for, add the new list of device references, if they don't already exist foreach (string deviceReference in newDeviceReferences.Distinct()) { if (!deviceReferences.Contains(deviceReference)) { datFile.Items[machine].Add(new DeviceReference() { Name = deviceReference }); } } } // If we're checking slotoptions if (useSlotOptions && slotOptions.Any()) { // Loop through all names and check the corresponding machines List <string> newSlotOptions = new List <string>(); foreach (string slotOption in slotOptions) { // If the machine doesn't exist then we continue if (datFile.Items[slotOption] == null || datFile.Items[slotOption].Count == 0) { continue; } // Add to the list of new slot option names ConcurrentList <DatItem> slotItems = datFile.Items[slotOption]; newSlotOptions.AddRange(slotItems .Where(i => i.ItemType == ItemType.Slot) .Where(s => (s as Slot).SlotOptionsSpecified) .SelectMany(s => (s as Slot).SlotOptions) .Select(o => o.DeviceName)); // Set new machine information and add to the current machine DatItem copyFrom = datFile.Items[machine][0]; foreach (DatItem item in slotItems) { // If the parent machine doesn't already contain this item, add it if (!datFile.Items[machine].Any(i => i.ItemType == item.ItemType && i.GetName() == item.GetName())) { // Set that we found new items foundnew = true; // Clone the item and then add it DatItem datItem = (DatItem)item.Clone(); datItem.CopyMachineInformation(copyFrom); datFile.Items.Add(machine, datItem); } } } // Now that every device is accounted for, add the new list of slot options, if they don't already exist foreach (string slotOption in newSlotOptions.Distinct()) { if (!slotOptions.Contains(slotOption)) { datFile.Items[machine].Add(new Slot() { SlotOptions = new List <SlotOption> { new SlotOption { DeviceName = slotOption } } }); } } } } return(foundnew); }
/// <summary> /// Find duplicates and rebuild individual files to output /// </summary> /// <param name="datFile">Current DatFile object to rebuild from</param> /// <param name="datItem">Information for the current file to rebuild from</param> /// <param name="file">Name of the file to process</param> /// <param name="outDir">Output directory to use to build to</param> /// <param name="date">True if the date from the DAT should be used if available, false otherwise</param> /// <param name="inverse">True if the DAT should be used as a filter instead of a template, false otherwise</param> /// <param name="outputFormat">Output format that files should be written to</param> /// <param name="isZip">True if the input file is an archive, false if the file is TGZ/TXZ, null otherwise</param> /// <returns>True if the file was able to be rebuilt, false otherwise</returns> private static bool RebuildIndividualFile( DatFile datFile, DatItem datItem, string file, string outDir, bool date, bool inverse, OutputFormat outputFormat, bool?isZip = null) { // Set the initial output value bool rebuilt = false; // If the DatItem is a Disk or Media, force rebuilding to a folder except if TGZ or TXZ if ((datItem.ItemType == ItemType.Disk || datItem.ItemType == ItemType.Media) && !(outputFormat == OutputFormat.TorrentGzip || outputFormat == OutputFormat.TorrentGzipRomba) && !(outputFormat == OutputFormat.TorrentXZ || outputFormat == OutputFormat.TorrentXZRomba)) { outputFormat = OutputFormat.Folder; } // If we have a Disk or Media, change it into a Rom for later use if (datItem.ItemType == ItemType.Disk) { datItem = (datItem as Disk).ConvertToRom(); } else if (datItem.ItemType == ItemType.Media) { datItem = (datItem as Media).ConvertToRom(); } // Prepopluate a key string string crc = (datItem as Rom).CRC ?? string.Empty; // Try to get the stream for the file if (!GetFileStream(datItem, file, isZip, out Stream fileStream)) { return(false); } // If either we have duplicates or we're filtering if (ShouldRebuild(datFile, datItem, fileStream, inverse, out ConcurrentList <DatItem> dupes)) { // If we have a very specific TGZ->TGZ case, just copy it accordingly if (RebuildTorrentGzip(datFile, datItem, file, outDir, outputFormat, isZip)) { return(true); } // If we have a very specific TXZ->TXZ case, just copy it accordingly if (RebuildTorrentXz(datFile, datItem, file, outDir, outputFormat, isZip)) { return(true); } logger.User($"{(inverse ? "No matches" : "Matches")} found for '{Path.GetFileName(datItem.GetName() ?? datItem.ItemType.ToString())}', rebuilding accordingly..."); rebuilt = true; // Special case for partial packing mode bool shouldCheck = false; if (outputFormat == OutputFormat.Folder && datFile.Header.ForcePacking == PackingFlag.Partial) { shouldCheck = true; datFile.Items.BucketBy(ItemKey.Machine, DedupeType.None, lower: false); } // Now loop through the list and rebuild accordingly foreach (DatItem item in dupes) { // If we should check for the items in the machine if (shouldCheck && datFile.Items[item.Machine.Name].Count > 1) { outputFormat = OutputFormat.Folder; } else if (shouldCheck && datFile.Items[item.Machine.Name].Count == 1) { outputFormat = OutputFormat.ParentFolder; } // Get the output archive, if possible Folder outputArchive = GetPreconfiguredFolder(datFile, date, outputFormat); // Now rebuild to the output file outputArchive.Write(fileStream, outDir, (item as Rom).ConvertToBaseFile()); } // Close the input stream fileStream?.Dispose(); } // Now we want to take care of headers, if applicable if (datFile.Header.HeaderSkipper != null) { // Check to see if we have a matching header first SkipperMatch.Init(); SkipperRule rule = SkipperMatch.GetMatchingRule(fileStream, Path.GetFileNameWithoutExtension(datFile.Header.HeaderSkipper)); // If there's a match, create the new file to write if (rule.Tests != null && rule.Tests.Count != 0) { // If the file could be transformed correctly MemoryStream transformStream = new MemoryStream(); if (rule.TransformStream(fileStream, transformStream, keepReadOpen: true, keepWriteOpen: true)) { // Get the file informations that we will be using Rom headerless = new Rom(BaseFile.GetInfo(transformStream, keepReadOpen: true)); // If we have duplicates and we're not filtering if (ShouldRebuild(datFile, headerless, transformStream, false, out dupes)) { logger.User($"Headerless matches found for '{Path.GetFileName(datItem.GetName() ?? datItem.ItemType.ToString())}', rebuilding accordingly..."); rebuilt = true; // Now loop through the list and rebuild accordingly foreach (DatItem item in dupes) { // Create a headered item to use as well datItem.CopyMachineInformation(item); datItem.SetName($"{datItem.GetName()}_{crc}"); // Get the output archive, if possible Folder outputArchive = GetPreconfiguredFolder(datFile, date, outputFormat); // Now rebuild to the output file bool eitherSuccess = false; eitherSuccess |= outputArchive.Write(transformStream, outDir, (item as Rom).ConvertToBaseFile()); eitherSuccess |= outputArchive.Write(fileStream, outDir, (datItem as Rom).ConvertToBaseFile()); // Now add the success of either rebuild rebuilt &= eitherSuccess; } } } // Dispose of the stream transformStream?.Dispose(); } // Dispose of the stream fileStream?.Dispose(); } return(rebuilt); }