using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Xml; using SabreTools.Library.Data; using SabreTools.Library.DatItems; using SabreTools.Library.IO; using SabreTools.Library.Tools; namespace SabreTools.Library.DatFiles { /// /// Represents parsing and writing of a MAME XML DAT /// internal class Listxml : DatFile { /// /// Constructor designed for casting a base DatFile /// /// Parent DatFile to copy from public Listxml(DatFile datFile) : base(datFile) { } /// /// Parse a MAME XML DAT and return all found games and roms within /// /// Name of the file to be parsed /// Index ID for the DAT /// True if full pathnames are to be kept, false otherwise (default) /// /// protected override void ParseFile( // Standard Dat parsing string filename, int indexId, // Miscellaneous bool keep) { // Prepare all internal variables XmlReader xtr = filename.GetXmlTextReader(); // If we got a null reader, just return if (xtr == null) return; // Otherwise, read the file to the end try { xtr.MoveToContent(); while (!xtr.EOF) { // We only want elements if (xtr.NodeType != XmlNodeType.Element) { xtr.Read(); continue; } switch (xtr.Name) { case "mame": Header.Name = (Header.Name == null ? xtr.GetAttribute("build") : Header.Name); Header.Description = (Header.Description == null ? Header.Name : Header.Description); Header.Debug = (Header.Debug == null ? xtr.GetAttribute("debug").AsYesNo() : Header.Debug); Header.MameConfig = (Header.MameConfig == null ? xtr.GetAttribute("mameconfig") : Header.MameConfig); xtr.Read(); break; // Handle M1 DATs since they're 99% the same as a SL DAT case "m1": Header.Name = (Header.Name == null ? "M1" : Header.Name); Header.Description = (Header.Description == null ? "M1" : Header.Description); Header.Version = (Header.Version == null ? xtr.GetAttribute("version") ?? string.Empty : Header.Version); xtr.Read(); break; // We want to process the entire subtree of the machine case "game": // Some older DATs still use "game" case "machine": ReadMachine(xtr.ReadSubtree(), filename, indexId); // Skip the machine now that we've processed it xtr.Skip(); break; default: xtr.Read(); break; } } } catch (Exception ex) { Globals.Logger.Warning($"Exception found while parsing '{filename}': {ex}"); // For XML errors, just skip the affected node xtr?.Read(); } xtr.Dispose(); } /// /// Read machine information /// /// XmlReader representing a machine block /// Name of the file to be parsed /// Index ID for the DAT private void ReadMachine( XmlReader reader, // Standard Dat parsing string filename, int indexId) { // If we have an empty machine, skip it if (reader == null) return; // Otherwise, add what is possible reader.MoveToContent(); // Create a new machine MachineType machineType = MachineType.NULL; if (reader.GetAttribute("isbios").AsYesNo() == true) machineType |= MachineType.Bios; if (reader.GetAttribute("isdevice").AsYesNo() == true) machineType |= MachineType.Device; if (reader.GetAttribute("ismechanical").AsYesNo() == true) machineType |= MachineType.Mechanical; Machine machine = new Machine { Name = reader.GetAttribute("name"), Comment = string.Empty, Description = reader.GetAttribute("name"), CloneOf = reader.GetAttribute("cloneof") ?? string.Empty, RomOf = reader.GetAttribute("romof") ?? string.Empty, SampleOf = reader.GetAttribute("sampleof") ?? string.Empty, MachineType = (machineType == MachineType.NULL ? MachineType.None : machineType), SourceFile = reader.GetAttribute("sourcefile"), Runnable = reader.GetAttribute("runnable").AsRunnable(), DeviceReferences = new List(), Chips = new List(), Displays = new List(), Sounds = new List(), Conditions = new List(), Inputs = new List(), DipSwitches = new List(), Configurations = new List(), Slots = new List(), }; // Get list for new DatItems List datItems = new List(); while (!reader.EOF) { // We only want elements if (reader.NodeType != XmlNodeType.Element) { reader.Read(); continue; } // Get the roms from the machine switch (reader.Name) { case "description": machine.Description = reader.ReadElementContentAsString(); break; case "year": machine.Year = reader.ReadElementContentAsString(); break; case "manufacturer": machine.Manufacturer = reader.ReadElementContentAsString(); break; case "biosset": datItems.Add(new BiosSet { Name = reader.GetAttribute("name"), Description = reader.GetAttribute("description"), Default = reader.GetAttribute("default").AsYesNo(), Source = new Source { Index = indexId, Name = filename, }, }); reader.Read(); break; case "rom": datItems.Add(new Rom { Name = reader.GetAttribute("name"), Bios = reader.GetAttribute("bios"), Size = Sanitizer.CleanSize(reader.GetAttribute("size")), CRC = reader.GetAttribute("crc"), MD5 = reader.GetAttribute("md5"), #if NET_FRAMEWORK RIPEMD160 = reader.GetAttribute("ripemd160"), #endif SHA1 = reader.GetAttribute("sha1"), SHA256 = reader.GetAttribute("sha256"), SHA384 = reader.GetAttribute("sha384"), SHA512 = reader.GetAttribute("sha512"), MergeTag = reader.GetAttribute("merge"), Region = reader.GetAttribute("region"), Offset = reader.GetAttribute("offset"), ItemStatus = reader.GetAttribute("status").AsItemStatus(), Optional = reader.GetAttribute("optional").AsYesNo(), Source = new Source { Index = indexId, Name = filename, }, }); reader.Read(); break; case "disk": datItems.Add(new Disk { Name = reader.GetAttribute("name"), MD5 = reader.GetAttribute("md5"), #if NET_FRAMEWORK RIPEMD160 = reader.GetAttribute("ripemd160"), #endif SHA1 = reader.GetAttribute("sha1"), SHA256 = reader.GetAttribute("sha256"), SHA384 = reader.GetAttribute("sha384"), SHA512 = reader.GetAttribute("sha512"), MergeTag = reader.GetAttribute("merge"), Region = reader.GetAttribute("region"), Index = reader.GetAttribute("index"), Writable = reader.GetAttribute("writable").AsYesNo(), ItemStatus = reader.GetAttribute("status").AsItemStatus(), Optional = reader.GetAttribute("optional").AsYesNo(), Source = new Source { Index = indexId, Name = filename, }, }); reader.Read(); break; case "device_ref": var deviceReference = new ListXmlDeviceReference(); deviceReference.Name = reader.GetAttribute("name"); machine.DeviceReferences.Add(deviceReference); reader.Read(); break; case "sample": datItems.Add(new Sample { Name = reader.GetAttribute("name"), Source = new Source { Index = indexId, Name = filename, }, }); reader.Read(); break; case "chip": var chip = new ListXmlChip(); chip.Name = reader.GetAttribute("name"); chip.Tag = reader.GetAttribute("tag"); chip.Type = reader.GetAttribute("type"); chip.Clock = reader.GetAttribute("clock"); machine.Chips.Add(chip); reader.Read(); break; case "display": var display = new ListXmlDisplay(); display.Tag = reader.GetAttribute("tag"); display.Type = reader.GetAttribute("type"); display.Rotate = reader.GetAttribute("rotate"); display.FlipX = reader.GetAttribute("flipx").AsYesNo(); display.Width = reader.GetAttribute("width"); display.Height = reader.GetAttribute("height"); display.Refresh = reader.GetAttribute("refresh"); display.PixClock = reader.GetAttribute("pixclock"); display.HTotal = reader.GetAttribute("htotal"); display.HBend = reader.GetAttribute("hbend"); display.HStart = reader.GetAttribute("hstart"); display.VTotal = reader.GetAttribute("vtotal"); display.VBend = reader.GetAttribute("vbend"); display.VStart = reader.GetAttribute("vstart"); machine.Displays.Add(display); reader.Read(); break; case "sound": var sound = new ListXmlSound(); sound.Channels = reader.GetAttribute("channels"); machine.Sounds.Add(sound); reader.Read(); break; case "condition": var condition = new ListXmlCondition(); condition.Tag = reader.GetAttribute("tag"); condition.Mask = reader.GetAttribute("mask"); condition.Relation = reader.GetAttribute("relation"); condition.Value = reader.GetAttribute("value"); machine.Conditions.Add(condition); reader.Read(); break; case "input": var input = new ListXmlInput(); input.Service = reader.GetAttribute("service").AsYesNo(); input.Tilt = reader.GetAttribute("tilt").AsYesNo(); input.Players = reader.GetAttribute("players"); input.Coins = reader.GetAttribute("coins"); // Now read the internal tags ReadInput(reader.ReadSubtree(), input); machine.Inputs.Add(input); // Skip the input now that we've processed it reader.Skip(); break; case "dipswitch": var dipSwitch = new ListXmlDipSwitch(); dipSwitch.Name = reader.GetAttribute("name"); dipSwitch.Tag = reader.GetAttribute("tag"); dipSwitch.Mask = reader.GetAttribute("mask"); // Now read the internal tags ReadDipSwitch(reader.ReadSubtree(), dipSwitch); machine.DipSwitches.Add(dipSwitch); // Skip the dipswitch now that we've processed it reader.Skip(); break; case "configuration": var configuration = new ListXmlConfiguration(); configuration.Name = reader.GetAttribute("name"); configuration.Tag = reader.GetAttribute("tag"); configuration.Mask = reader.GetAttribute("mask"); // Now read the internal tags ReadConfiguration(reader.ReadSubtree(), configuration); machine.Configurations.Add(configuration); // Skip the configuration now that we've processed it reader.Skip(); break; case "port": // TODO: Use these ports var port = new ListXmlPort(); port.Tag = reader.GetAttribute("tag"); // Now read the internal tags ReadPort(reader.ReadSubtree(), port); // Skip the port now that we've processed it reader.Skip(); break; case "adjuster": // TODO: Use these adjusters var adjuster = new ListXmlAdjuster(); adjuster.Name = reader.GetAttribute("name"); adjuster.Default = reader.GetAttribute("default").AsYesNo(); // Now read the internal tags ReadAdjuster(reader.ReadSubtree(), adjuster); // Skip the adjuster now that we've processed it reader.Skip(); break; case "driver": // TODO: Use these drivers var driver = new ListXmlDriver(); driver.Status = reader.GetAttribute("status"); driver.Emulation = reader.GetAttribute("emulation"); driver.Cocktail = reader.GetAttribute("cocktail"); driver.SaveState = reader.GetAttribute("savestate"); reader.Read(); break; case "feature": // TODO: Use these features var feature = new ListXmlFeature(); feature.Type = reader.GetAttribute("type"); feature.Status = reader.GetAttribute("status"); feature.Overall = reader.GetAttribute("overall"); reader.Read(); break; case "device": // TODO: Use these devices var device = new ListXmlDevice(); device.Type = reader.GetAttribute("type"); device.Tag = reader.GetAttribute("tag"); device.FixedImage = reader.GetAttribute("fixed_image"); device.Mandatory = reader.GetAttribute("mandatory"); device.Interface = reader.GetAttribute("interface"); // Now read the internal tags ReadDevice(reader.ReadSubtree(), device); // Skip the device now that we've processed it reader.Skip(); break; case "slot": var slot = new ListXmlSlot(); slot.Name = reader.GetAttribute("name"); // Now read the internal tags ReadSlot(reader.ReadSubtree(), slot, machine); machine.Slots.Add(slot); // Skip the slot now that we've processed it reader.Skip(); break; case "softwarelist": // TODO: Use these softwarelists var softwareList = new ListXmlSoftwareList(); softwareList.Name = reader.GetAttribute("name"); softwareList.Status = reader.GetAttribute("status"); softwareList.Filter = reader.GetAttribute("filter"); reader.Read(); break; case "ramoption": // TODO: Use these ramoptions var ramOption = new ListXmlRamOption(); ramOption.Default = reader.GetAttribute("default").AsYesNo(); reader.Read(); break; default: reader.Read(); break; } } // If we found items, copy the machine info and add them if (datItems.Any()) { foreach (DatItem datItem in datItems) { datItem.CopyMachineInformation(machine); ParseAddHelper(datItem); } } // If no items were found for this machine, add a Blank placeholder else { Blank blank = new Blank() { Source = new Source { Index = indexId, Name = filename, }, }; blank.CopyMachineInformation(machine); // Now process and add the rom ParseAddHelper(blank); } } /// /// Read slot information /// /// XmlReader representing a machine block /// ListXmlSlot to populate /// Machine information to pass to contained items private void ReadSlot(XmlReader reader, ListXmlSlot slot, Machine machine) { // If we have an empty machine, skip it if (reader == null) return; // Get list ready slot.SlotOptions = new List(); // Otherwise, add what is possible reader.MoveToContent(); while (!reader.EOF) { // We only want elements if (reader.NodeType != XmlNodeType.Element) { reader.Read(); continue; } // Get the roms from the machine switch (reader.Name) { case "slotoption": var slotOption = new ListXmlSlotOption(); slotOption.Name = reader.GetAttribute("name"); slotOption.DeviceName = reader.GetAttribute("devname"); slotOption.Default = reader.GetAttribute("default").AsYesNo(); slot.SlotOptions.Add(slotOption); reader.Read(); break; default: reader.Read(); break; } } } /// /// Read Input information /// /// XmlReader representing a diskarea block /// ListXmlInput to populate private void ReadInput(XmlReader reader, ListXmlInput input) { // If we have an empty input, skip it if (reader == null) return; // Get list ready input.Controls = new List(); // Otherwise, add what is possible reader.MoveToContent(); while (!reader.EOF) { // We only want elements if (reader.NodeType != XmlNodeType.Element) { reader.Read(); continue; } // Get the information from the dipswitch switch (reader.Name) { case "control": var control = new ListXmlControl(); control.Type = reader.GetAttribute("type"); control.Player = reader.GetAttribute("player"); control.Buttons = reader.GetAttribute("buttons"); control.RegButtons = reader.GetAttribute("regbuttons"); control.Minimum = reader.GetAttribute("minimum"); control.Maximum = reader.GetAttribute("maximum"); control.Sensitivity = reader.GetAttribute("sensitivity"); control.KeyDelta = reader.GetAttribute("keydelta"); control.Reverse = reader.GetAttribute("reverse").AsYesNo(); control.Ways = reader.GetAttribute("ways"); control.Ways2 = reader.GetAttribute("ways2"); control.Ways3 = reader.GetAttribute("ways3"); input.Controls.Add(control); reader.Read(); break; default: reader.Read(); break; } } } /// /// Read DipSwitch information /// /// XmlReader representing a diskarea block /// ListXmlDipSwitch to populate private void ReadDipSwitch(XmlReader reader, ListXmlDipSwitch dipSwitch) { // If we have an empty dipswitch, skip it if (reader == null) return; // Get lists ready dipSwitch.Locations = new List(); dipSwitch.Values = new List(); // Otherwise, add what is possible reader.MoveToContent(); while (!reader.EOF) { // We only want elements if (reader.NodeType != XmlNodeType.Element) { reader.Read(); continue; } // Get the information from the dipswitch switch (reader.Name) { case "diplocation": var dipLocation = new ListXmlDipLocation(); dipLocation.Name = reader.GetAttribute("name"); dipLocation.Number = reader.GetAttribute("number"); dipLocation.Inverted = reader.GetAttribute("inverted").AsYesNo(); dipSwitch.Locations.Add(dipLocation); reader.Read(); break; case "dipvalue": var dipValue = new ListXmlDipValue(); dipValue.Name = reader.GetAttribute("name"); dipValue.Value = reader.GetAttribute("value"); dipValue.Default = reader.GetAttribute("default").AsYesNo(); dipSwitch.Values.Add(dipValue); reader.Read(); break; default: reader.Read(); break; } } } /// /// Read Configuration information /// /// XmlReader representing a diskarea block /// ListXmlConfiguration to populate private void ReadConfiguration(XmlReader reader, ListXmlConfiguration configuration) { // If we have an empty configuration, skip it if (reader == null) return; // Get lists ready configuration.Locations = new List(); configuration.Settings = new List(); // Otherwise, add what is possible reader.MoveToContent(); while (!reader.EOF) { // We only want elements if (reader.NodeType != XmlNodeType.Element) { reader.Read(); continue; } // Get the information from the dipswitch switch (reader.Name) { case "conflocation": var confLocation = new ListXmlConfLocation(); confLocation.Name = reader.GetAttribute("name"); confLocation.Number = reader.GetAttribute("number"); confLocation.Inverted = reader.GetAttribute("inverted").AsYesNo(); configuration.Locations.Add(confLocation); reader.Read(); break; case "confsetting": var confSetting = new ListXmlConfSetting(); confSetting.Name = reader.GetAttribute("name"); confSetting.Value = reader.GetAttribute("value"); confSetting.Default = reader.GetAttribute("default").AsYesNo(); configuration.Settings.Add(confSetting); reader.Read(); break; default: reader.Read(); break; } } } /// /// Read Port information /// /// XmlReader representing a diskarea block /// ListXmlPort to populate private void ReadPort(XmlReader reader, ListXmlPort port) { // If we have an empty port, skip it if (reader == null) return; // Get list ready port.Analogs = new List(); // Otherwise, add what is possible reader.MoveToContent(); while (!reader.EOF) { // We only want elements if (reader.NodeType != XmlNodeType.Element) { reader.Read(); continue; } // Get the information from the port switch (reader.Name) { case "analog": var analog = new ListXmlAnalog(); analog.Mask = reader.GetAttribute("mask"); port.Analogs.Add(analog); reader.Read(); break; default: reader.Read(); break; } } } /// /// Read Adjuster information /// /// XmlReader representing a diskarea block /// ListXmlAdjuster to populate private void ReadAdjuster(XmlReader reader, ListXmlAdjuster adjuster) { // If we have an empty port, skip it if (reader == null) return; // Get list ready adjuster.Conditions = new List(); // Otherwise, add what is possible reader.MoveToContent(); while (!reader.EOF) { // We only want elements if (reader.NodeType != XmlNodeType.Element) { reader.Read(); continue; } // Get the information from the adjuster switch (reader.Name) { case "condition": var condition = new ListXmlCondition(); condition.Tag = reader.GetAttribute("tag"); condition.Mask = reader.GetAttribute("mask"); condition.Relation = reader.GetAttribute("relation"); condition.Value = reader.GetAttribute("value"); adjuster.Conditions.Add(condition); reader.Read(); break; default: reader.Read(); break; } } } /// /// Read Device information /// /// XmlReader representing a diskarea block /// ListXmlDevice to populate private void ReadDevice(XmlReader reader, ListXmlDevice device) { // If we have an empty port, skip it if (reader == null) return; // Get lists ready device.Instances = new List(); device.Extensions = new List(); // Otherwise, add what is possible reader.MoveToContent(); while (!reader.EOF) { // We only want elements if (reader.NodeType != XmlNodeType.Element) { reader.Read(); continue; } // Get the information from the adjuster switch (reader.Name) { case "instance": var instance = new ListXmlInstance(); instance.Name = reader.GetAttribute("name"); instance.BriefName = reader.GetAttribute("briefname"); device.Instances.Add(instance); reader.Read(); break; case "extension": var extension = new ListXmlExtension(); extension.Name = reader.GetAttribute("name"); device.Extensions.Add(extension); reader.Read(); break; default: reader.Read(); break; } } } /// /// Create and open an output file for writing direct from a dictionary /// /// Name of the file to write to /// True if blank roms should be skipped on output, false otherwise (default) /// True if the DAT was written correctly, false otherwise public override bool WriteToFile(string outfile, bool ignoreblanks = false) { try { Globals.Logger.User($"Opening file for writing: {outfile}"); FileStream fs = FileExtensions.TryCreate(outfile); // If we get back null for some reason, just log and return if (fs == null) { Globals.Logger.Warning($"File '{outfile}' could not be created for writing! Please check to see if the file is writable"); return false; } XmlTextWriter xtw = new XmlTextWriter(fs, new UTF8Encoding(false)) { Formatting = Formatting.Indented, IndentChar = '\t', Indentation = 1 }; // Write out the header WriteHeader(xtw); // Write out each of the machines and roms string lastgame = null; // Use a sorted list of games to output foreach (string key in Items.SortedKeys) { List roms = Items[key]; // Resolve the names in the block roms = DatItem.ResolveNames(roms); for (int index = 0; index < roms.Count; index++) { DatItem rom = roms[index]; // There are apparently times when a null rom can skip by, skip them if (rom.Name == null || rom.Machine.Name == null) { Globals.Logger.Warning("Null rom found!"); continue; } // If we have a different game and we're not at the start of the list, output the end of last item if (lastgame != null && lastgame.ToLowerInvariant() != rom.Machine.Name.ToLowerInvariant()) WriteEndGame(xtw); // If we have a new game, output the beginning of the new item if (lastgame == null || lastgame.ToLowerInvariant() != rom.Machine.Name.ToLowerInvariant()) WriteStartGame(xtw, rom); // If we have a "null" game (created by DATFromDir or something similar), log it to file if (rom.ItemType == ItemType.Rom && ((Rom)rom).Size == -1 && ((Rom)rom).CRC == "null") { Globals.Logger.Verbose($"Empty folder found: {rom.Machine.Name}"); lastgame = rom.Machine.Name; continue; } // Now, output the rom data WriteDatItem(xtw, rom, ignoreblanks); // Set the new data to compare against lastgame = rom.Machine.Name; } } // Write the file footer out WriteFooter(xtw); Globals.Logger.Verbose("File written!" + Environment.NewLine); xtw.Dispose(); fs.Dispose(); } catch (Exception ex) { Globals.Logger.Error(ex.ToString()); return false; } return true; } /// /// Write out DAT header using the supplied StreamWriter /// /// XmlTextWriter to output to /// True if the data was written, false on error private bool WriteHeader(XmlTextWriter xtw) { try { xtw.WriteStartDocument(); xtw.WriteStartElement("mame"); xtw.WriteAttributeString("build", Header.Name); if (Header.Debug != null) { switch (Header.Debug) { case true: xtw.WriteAttributeString("debug", "yes"); break; case false: xtw.WriteAttributeString("debug", "no"); break; } } if (!string.IsNullOrEmpty(Header.MameConfig)) xtw.WriteAttributeString("mameconfig", Header.MameConfig); xtw.Flush(); } catch (Exception ex) { Globals.Logger.Error(ex.ToString()); return false; } return true; } /// /// Write out Game start using the supplied StreamWriter /// /// XmlTextWriter to output to /// DatItem object to be output /// True if the data was written, false on error private bool WriteStartGame(XmlTextWriter xtw, DatItem datItem) { try { // No game should start with a path separator datItem.Machine.Name = datItem.Machine.Name.TrimStart(Path.DirectorySeparatorChar); // Build the state xtw.WriteStartElement("machine"); xtw.WriteAttributeString("name", datItem.Machine.Name); if (!string.IsNullOrWhiteSpace(datItem.Machine.SourceFile)) xtw.WriteAttributeString("sourcefile", datItem.Machine.SourceFile); if (datItem.Machine.MachineType != MachineType.NULL) { if (datItem.Machine.MachineType.HasFlag(MachineType.Bios)) xtw.WriteAttributeString("isbios", "yes"); if (datItem.Machine.MachineType.HasFlag(MachineType.Device)) xtw.WriteAttributeString("isdevice", "yes"); if (datItem.Machine.MachineType.HasFlag(MachineType.Mechanical)) xtw.WriteAttributeString("ismechanical", "yes"); } if (datItem.Machine.Runnable != Runnable.NULL) { switch (datItem.Machine.Runnable) { case Runnable.No: xtw.WriteAttributeString("runnable", "no"); break; case Runnable.Partial: xtw.WriteAttributeString("runnable", "partial"); break; case Runnable.Yes: xtw.WriteAttributeString("runnable", "yes"); break; } } if (!string.IsNullOrWhiteSpace(datItem.Machine.CloneOf) && !string.Equals(datItem.Machine.Name, datItem.Machine.CloneOf, StringComparison.OrdinalIgnoreCase)) xtw.WriteAttributeString("cloneof", datItem.Machine.CloneOf); if (!string.IsNullOrWhiteSpace(datItem.Machine.RomOf) && !string.Equals(datItem.Machine.Name, datItem.Machine.RomOf, StringComparison.OrdinalIgnoreCase)) xtw.WriteAttributeString("romof", datItem.Machine.RomOf); if (!string.IsNullOrWhiteSpace(datItem.Machine.SampleOf) && !string.Equals(datItem.Machine.Name, datItem.Machine.SampleOf, StringComparison.OrdinalIgnoreCase)) xtw.WriteAttributeString("sampleof", datItem.Machine.SampleOf); if (!string.IsNullOrWhiteSpace(datItem.Machine.Description)) xtw.WriteElementString("description", datItem.Machine.Description); if (!string.IsNullOrWhiteSpace(datItem.Machine.Year)) xtw.WriteElementString("year", datItem.Machine.Year); if (!string.IsNullOrWhiteSpace(datItem.Machine.Publisher)) xtw.WriteElementString("publisher", datItem.Machine.Publisher); if (!string.IsNullOrWhiteSpace(datItem.Machine.Category)) xtw.WriteElementString("category", datItem.Machine.Category); if (datItem.Machine.Infos != null && datItem.Machine.Infos.Count > 0) { foreach (ListXmlInfo kvp in datItem.Machine.Infos) { xtw.WriteStartElement("info"); xtw.WriteAttributeString("name", kvp.Name); xtw.WriteAttributeString("value", kvp.Value); xtw.WriteEndElement(); } } xtw.Flush(); } catch (Exception ex) { Globals.Logger.Error(ex.ToString()); return false; } return true; } /// /// Write out Game start using the supplied StreamWriter /// /// XmlTextWriter to output to /// True if the data was written, false on error private bool WriteEndGame(XmlTextWriter xtw) { try { // End machine xtw.WriteEndElement(); xtw.Flush(); } catch (Exception ex) { Globals.Logger.Error(ex.ToString()); return false; } return true; } /// /// Write out DatItem using the supplied StreamWriter /// /// XmlTextWriter to output to /// DatItem object to be output /// True if blank roms should be skipped on output, false otherwise (default) /// True if the data was written, false on error private bool WriteDatItem(XmlTextWriter xtw, DatItem datItem, bool ignoreblanks = false) { // If we are in ignore blanks mode AND we have a blank (0-size) rom, skip if (ignoreblanks && (datItem.ItemType == ItemType.Rom && ((datItem as Rom).Size == 0 || (datItem as Rom).Size == -1))) return true; try { // Pre-process the item name ProcessItemName(datItem, true); // Build the state switch (datItem.ItemType) { case ItemType.BiosSet: var biosSet = datItem as BiosSet; xtw.WriteStartElement("biosset"); xtw.WriteAttributeString("name", biosSet.Name); if (!string.IsNullOrWhiteSpace(biosSet.Description)) xtw.WriteAttributeString("description", biosSet.Description); if (biosSet.Default != null) xtw.WriteAttributeString("default", biosSet.Default.ToString().ToLowerInvariant()); xtw.WriteEndElement(); break; case ItemType.Disk: var disk = datItem as Disk; xtw.WriteStartElement("disk"); xtw.WriteAttributeString("name", disk.Name); if (!string.IsNullOrWhiteSpace(disk.MD5)) xtw.WriteAttributeString("md5", disk.MD5.ToLowerInvariant()); #if NET_FRAMEWORK if (!string.IsNullOrWhiteSpace(disk.RIPEMD160)) xtw.WriteAttributeString("ripemd160", disk.RIPEMD160.ToLowerInvariant()); #endif if (!string.IsNullOrWhiteSpace(disk.SHA1)) xtw.WriteAttributeString("sha1", disk.SHA1.ToLowerInvariant()); if (!string.IsNullOrWhiteSpace(disk.SHA256)) xtw.WriteAttributeString("sha256", disk.SHA256.ToLowerInvariant()); if (!string.IsNullOrWhiteSpace(disk.SHA384)) xtw.WriteAttributeString("sha384", disk.SHA384.ToLowerInvariant()); if (!string.IsNullOrWhiteSpace(disk.SHA512)) xtw.WriteAttributeString("sha512", disk.SHA512.ToLowerInvariant()); if (!string.IsNullOrWhiteSpace(disk.MergeTag)) xtw.WriteAttributeString("merge", disk.MergeTag); if (!string.IsNullOrWhiteSpace(disk.Region)) xtw.WriteAttributeString("region", disk.Region); if (!string.IsNullOrWhiteSpace(disk.Index)) xtw.WriteAttributeString("index", disk.Index); if (disk.Writable != null) xtw.WriteAttributeString("writable", disk.Writable == true ? "yes" : "no"); if (disk.ItemStatus != ItemStatus.None) xtw.WriteAttributeString("status", disk.ItemStatus.ToString()); if (disk.Optional != null) xtw.WriteAttributeString("optional", disk.Optional == true ? "yes" : "no"); xtw.WriteEndElement(); break; case ItemType.Rom: var rom = datItem as Rom; xtw.WriteStartElement("rom"); xtw.WriteAttributeString("name", rom.Name); if (rom.Size != -1) xtw.WriteAttributeString("size", rom.Size.ToString()); if (!string.IsNullOrWhiteSpace(rom.CRC)) xtw.WriteAttributeString("crc", rom.CRC.ToLowerInvariant()); if (!string.IsNullOrWhiteSpace(rom.MD5)) xtw.WriteAttributeString("md5", rom.MD5.ToLowerInvariant()); #if NET_FRAMEWORK if (!string.IsNullOrWhiteSpace(rom.RIPEMD160)) xtw.WriteAttributeString("ripemd160", rom.RIPEMD160.ToLowerInvariant()); #endif if (!string.IsNullOrWhiteSpace(rom.SHA1)) xtw.WriteAttributeString("sha1", rom.SHA1.ToLowerInvariant()); if (!string.IsNullOrWhiteSpace(rom.SHA256)) xtw.WriteAttributeString("sha256", rom.SHA256.ToLowerInvariant()); if (!string.IsNullOrWhiteSpace(rom.SHA384)) xtw.WriteAttributeString("sha384", rom.SHA384.ToLowerInvariant()); if (!string.IsNullOrWhiteSpace(rom.SHA512)) xtw.WriteAttributeString("sha512", rom.SHA512.ToLowerInvariant()); if (!string.IsNullOrWhiteSpace(rom.Bios)) xtw.WriteAttributeString("bios", rom.Bios); if (!string.IsNullOrWhiteSpace(rom.MergeTag)) xtw.WriteAttributeString("merge", rom.MergeTag); if (!string.IsNullOrWhiteSpace(rom.Region)) xtw.WriteAttributeString("region", rom.Region); if (!string.IsNullOrWhiteSpace(rom.Offset)) xtw.WriteAttributeString("offset", rom.Offset); if (rom.ItemStatus != ItemStatus.None) xtw.WriteAttributeString("status", rom.ItemStatus.ToString().ToLowerInvariant()); if (rom.Optional != null) xtw.WriteAttributeString("optional", rom.Optional == true ? "yes" : "no"); xtw.WriteEndElement(); break; case ItemType.Sample: xtw.WriteStartElement("sample"); xtw.WriteAttributeString("name", datItem.Name); xtw.WriteEndElement(); break; } xtw.Flush(); } catch (Exception ex) { Globals.Logger.Error(ex.ToString()); return false; } return true; } /// /// Write out DAT footer using the supplied StreamWriter /// /// XmlTextWriter to output to /// True if the data was written, false on error private bool WriteFooter(XmlTextWriter xtw) { try { // End machine xtw.WriteEndElement(); // End mame xtw.WriteEndElement(); xtw.Flush(); } catch (Exception ex) { Globals.Logger.Error(ex.ToString()); return false; } return true; } } }