diff --git a/SabreTools.DatFiles/Formats/Listxml.Reader.cs b/SabreTools.DatFiles/Formats/Listxml.Reader.cs
new file mode 100644
index 00000000..3552c656
--- /dev/null
+++ b/SabreTools.DatFiles/Formats/Listxml.Reader.cs
@@ -0,0 +1,1145 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Xml;
+using System.Xml.Schema;
+using SabreTools.Core;
+using SabreTools.Core.Tools;
+using SabreTools.DatItems;
+using SabreTools.DatItems.Formats;
+
+namespace SabreTools.DatFiles.Formats
+{
+ ///
+ /// Represents parsing a MAME XML DAT
+ ///
+ internal partial class Listxml : DatFile
+ {
+ ///
+ public override void ParseFile(string filename, int indexId, bool keep, bool statsOnly = false, bool throwOnError = false)
+ {
+ // Prepare all internal variables
+ XmlReader xtr = XmlReader.Create(filename, new XmlReaderSettings
+ {
+ CheckCharacters = false,
+ DtdProcessing = DtdProcessing.Ignore,
+ IgnoreComments = true,
+ IgnoreWhitespace = true,
+ ValidationFlags = XmlSchemaValidationFlags.None,
+ ValidationType = ValidationType.None,
+ });
+
+ // 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 ??= xtr.GetAttribute("build");
+ Header.Description ??= Header.Name;
+ Header.Debug ??= xtr.GetAttribute("debug").AsYesNo();
+ Header.MameConfig ??= xtr.GetAttribute("mameconfig");
+ xtr.Read();
+ break;
+
+ // Handle M1 DATs since they're 99% the same as a SL DAT
+ case "m1":
+ Header.Name ??= "M1";
+ Header.Description ??= "M1";
+ Header.Version ??= xtr.GetAttribute("version") ?? string.Empty;
+ 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(), statsOnly, filename, indexId);
+
+ // Skip the machine now that we've processed it
+ xtr.Skip();
+ break;
+
+ default:
+ xtr.Read();
+ break;
+ }
+ }
+ }
+ catch (Exception ex) when (!throwOnError)
+ {
+ logger.Warning(ex, $"Exception found while parsing '{filename}'");
+
+ // For XML errors, just skip the affected node
+ xtr?.Read();
+ }
+
+ xtr.Dispose();
+ }
+
+ ///
+ /// Read machine information
+ ///
+ /// XmlReader representing a machine block
+ /// True to only add item statistics while parsing, false otherwise
+ /// Name of the file to be parsed
+ /// Index ID for the DAT
+ private void ReadMachine(XmlReader reader, bool statsOnly, 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 = 0x0;
+ 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()
+ {
+ Name = reader.GetAttribute("name"),
+ Description = reader.GetAttribute("name"),
+ CloneOf = reader.GetAttribute("cloneof"),
+ RomOf = reader.GetAttribute("romof"),
+ SampleOf = reader.GetAttribute("sampleof"),
+ MachineType = (machineType == 0x0 ? MachineType.None : machineType),
+
+ SourceFile = reader.GetAttribute("sourcefile"),
+ Runnable = reader.GetAttribute("runnable").AsRunnable(),
+ };
+
+ // Get list for new DatItems
+ List datItems = new();
+
+ 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 "history":
+ machine.History = reader.ReadElementContentAsString();
+ break;
+
+ case "adjuster":
+ var adjuster = new Adjuster
+ {
+ Name = reader.GetAttribute("name"),
+ Default = reader.GetAttribute("default").AsYesNo(),
+ Conditions = new List(),
+
+ Source = new Source
+ {
+ Index = indexId,
+ Name = filename,
+ },
+ };
+
+ // Now read the internal tags
+ ReadAdjuster(reader.ReadSubtree(), adjuster);
+
+ datItems.Add(adjuster);
+
+ // Skip the adjuster now that we've processed it
+ reader.Skip();
+ 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 "chip":
+ var chip = new Chip
+ {
+ Name = reader.GetAttribute("name"),
+ Tag = reader.GetAttribute("tag"),
+ ChipType = reader.GetAttribute("type").AsChipType(),
+ Clock = Utilities.CleanLong(reader.GetAttribute("clock")),
+
+ Source = new Source
+ {
+ Index = indexId,
+ Name = filename,
+ },
+ };
+
+ datItems.Add(chip);
+
+ reader.Read();
+ break;
+
+ case "condition":
+ datItems.Add(new Condition
+ {
+ Tag = reader.GetAttribute("tag"),
+ Mask = reader.GetAttribute("mask"),
+ Relation = reader.GetAttribute("relation").AsRelation(),
+ Value = reader.GetAttribute("value"),
+
+ Source = new Source
+ {
+ Index = indexId,
+ Name = filename,
+ },
+ });
+
+ reader.Read();
+ break;
+
+ case "configuration":
+ var configuration = new Configuration
+ {
+ Name = reader.GetAttribute("name"),
+ Tag = reader.GetAttribute("tag"),
+ Mask = reader.GetAttribute("mask"),
+ Conditions = new List(),
+ Locations = new List(),
+ Settings = new List(),
+
+ Source = new Source
+ {
+ Index = indexId,
+ Name = filename,
+ },
+ };
+
+ // Now read the internal tags
+ ReadConfiguration(reader.ReadSubtree(), configuration);
+
+ datItems.Add(configuration);
+
+ // Skip the configuration now that we've processed it
+ reader.Skip();
+ break;
+
+ case "device":
+ var device = new Device
+ {
+ DeviceType = reader.GetAttribute("type").AsDeviceType(),
+ Tag = reader.GetAttribute("tag"),
+ FixedImage = reader.GetAttribute("fixed_image"),
+ Mandatory = Utilities.CleanLong(reader.GetAttribute("mandatory")),
+ Interface = reader.GetAttribute("interface"),
+
+ Source = new Source
+ {
+ Index = indexId,
+ Name = filename,
+ },
+ };
+
+ // Now read the internal tags
+ ReadDevice(reader.ReadSubtree(), device);
+
+ datItems.Add(device);
+
+ // Skip the device now that we've processed it
+ reader.Skip();
+ break;
+
+ case "device_ref":
+ datItems.Add(new DeviceReference
+ {
+ Name = reader.GetAttribute("name"),
+
+ Source = new Source
+ {
+ Index = indexId,
+ Name = filename,
+ },
+ });
+
+ reader.Read();
+ break;
+
+ case "dipswitch":
+ var dipSwitch = new DipSwitch
+ {
+ Name = reader.GetAttribute("name"),
+ Tag = reader.GetAttribute("tag"),
+ Mask = reader.GetAttribute("mask"),
+ Conditions = new List(),
+ Locations = new List(),
+ Values = new List(),
+
+ Source = new Source
+ {
+ Index = indexId,
+ Name = filename,
+ },
+ };
+
+ // Now read the internal tags
+ ReadDipSwitch(reader.ReadSubtree(), dipSwitch);
+
+ datItems.Add(dipSwitch);
+
+ // Skip the dipswitch now that we've processed it
+ reader.Skip();
+ break;
+
+ case "disk":
+ datItems.Add(new Disk
+ {
+ Name = reader.GetAttribute("name"),
+ SHA1 = reader.GetAttribute("sha1"),
+ 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 "display":
+ var display = new Display
+ {
+ Tag = reader.GetAttribute("tag"),
+ DisplayType = reader.GetAttribute("type").AsDisplayType(),
+ Rotate = Utilities.CleanLong(reader.GetAttribute("rotate")),
+ FlipX = reader.GetAttribute("flipx").AsYesNo(),
+ Width = Utilities.CleanLong(reader.GetAttribute("width")),
+ Height = Utilities.CleanLong(reader.GetAttribute("height")),
+ Refresh = Utilities.CleanDouble(reader.GetAttribute("refresh")),
+ PixClock = Utilities.CleanLong(reader.GetAttribute("pixclock")),
+ HTotal = Utilities.CleanLong(reader.GetAttribute("htotal")),
+ HBEnd = Utilities.CleanLong(reader.GetAttribute("hbend")),
+ HBStart = Utilities.CleanLong(reader.GetAttribute("hbstart")),
+ VTotal = Utilities.CleanLong(reader.GetAttribute("vtotal")),
+ VBEnd = Utilities.CleanLong(reader.GetAttribute("vbend")),
+ VBStart = Utilities.CleanLong(reader.GetAttribute("vbstart")),
+
+ Source = new Source
+ {
+ Index = indexId,
+ Name = filename,
+ },
+ };
+
+ datItems.Add(display);
+
+ reader.Read();
+ break;
+
+ case "driver":
+ datItems.Add(new Driver
+ {
+ Status = reader.GetAttribute("status").AsSupportStatus(),
+ Emulation = reader.GetAttribute("emulation").AsSupportStatus(),
+ Cocktail = reader.GetAttribute("cocktail").AsSupportStatus(),
+ SaveState = reader.GetAttribute("savestate").AsSupported(),
+ RequiresArtwork = reader.GetAttribute("requiresartwork").AsYesNo(),
+ Unofficial = reader.GetAttribute("unofficial").AsYesNo(),
+ NoSoundHardware = reader.GetAttribute("nosoundhardware").AsYesNo(),
+ Incomplete = reader.GetAttribute("incomplete").AsYesNo(),
+
+ Source = new Source
+ {
+ Index = indexId,
+ Name = filename,
+ },
+ });
+
+ reader.Read();
+ break;
+
+ case "feature":
+ datItems.Add(new Feature
+ {
+ Type = reader.GetAttribute("type").AsFeatureType(),
+ Status = reader.GetAttribute("status").AsFeatureStatus(),
+ Overall = reader.GetAttribute("overall").AsFeatureStatus(),
+
+ Source = new Source
+ {
+ Index = indexId,
+ Name = filename,
+ },
+ });
+
+ reader.Read();
+ break;
+
+ case "input":
+ var input = new Input
+ {
+ Service = reader.GetAttribute("service").AsYesNo(),
+ Tilt = reader.GetAttribute("tilt").AsYesNo(),
+ Players = Utilities.CleanLong(reader.GetAttribute("players")),
+ Coins = Utilities.CleanLong(reader.GetAttribute("coins")),
+
+ Source = new Source
+ {
+ Index = indexId,
+ Name = filename,
+ },
+ };
+
+ // Now read the internal tags
+ ReadInput(reader.ReadSubtree(), input);
+
+ datItems.Add(input);
+
+ // Skip the input now that we've processed it
+ reader.Skip();
+ break;
+
+ case "port":
+ var port = new Port
+ {
+ Tag = reader.GetAttribute("tag"),
+
+ Source = new Source
+ {
+ Index = indexId,
+ Name = filename,
+ },
+ };
+
+ // Now read the internal tags
+ ReadPort(reader.ReadSubtree(), port);
+
+ datItems.Add(port);
+
+ // Skip the port now that we've processed it
+ reader.Skip();
+ break;
+
+ case "ramoption":
+ datItems.Add(new RamOption
+ {
+ Name = reader.GetAttribute("name"),
+ Default = reader.GetAttribute("default").AsYesNo(),
+ Content = reader.ReadElementContentAsString(),
+
+ Source = new Source
+ {
+ Index = indexId,
+ Name = filename,
+ },
+ });
+
+ break;
+
+ case "rom":
+ datItems.Add(new Rom
+ {
+ Name = reader.GetAttribute("name"),
+ Bios = reader.GetAttribute("bios"),
+ Size = Utilities.CleanLong(reader.GetAttribute("size")),
+ CRC = reader.GetAttribute("crc"),
+ SHA1 = reader.GetAttribute("sha1"),
+ 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 "sample":
+ datItems.Add(new Sample
+ {
+ Name = reader.GetAttribute("name"),
+
+ Source = new Source
+ {
+ Index = indexId,
+ Name = filename,
+ },
+ });
+
+ reader.Read();
+ break;
+
+ case "slot":
+ var slot = new Slot
+ {
+ Name = reader.GetAttribute("name"),
+ SlotOptions = new List(),
+
+ Source = new Source
+ {
+ Index = indexId,
+ Name = filename,
+ },
+ };
+
+ // Now read the internal tags
+ ReadSlot(reader.ReadSubtree(), slot);
+
+ datItems.Add(slot);
+
+ // Skip the slot now that we've processed it
+ reader.Skip();
+ break;
+
+ case "softwarelist":
+ datItems.Add(new DatItems.Formats.SoftwareList
+ {
+ Tag = reader.GetAttribute("tag"),
+ Name = reader.GetAttribute("name"),
+ Status = reader.GetAttribute("status").AsSoftwareListStatus(),
+ Filter = reader.GetAttribute("filter"),
+
+ Source = new Source
+ {
+ Index = indexId,
+ Name = filename,
+ },
+ });
+
+ reader.Read();
+ break;
+
+ case "sound":
+ var sound = new Sound
+ {
+ Channels = Utilities.CleanLong(reader.GetAttribute("channels")),
+
+ Source = new Source
+ {
+ Index = indexId,
+ Name = filename,
+ },
+ };
+
+ datItems.Add(sound);
+
+ 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, statsOnly);
+ }
+ }
+
+ // If no items were found for this machine, add a Blank placeholder
+ else
+ {
+ Blank blank = new()
+ {
+ Source = new Source
+ {
+ Index = indexId,
+ Name = filename,
+ },
+ };
+
+ blank.CopyMachineInformation(machine);
+
+ // Now process and add the rom
+ ParseAddHelper(blank, statsOnly);
+ }
+ }
+
+ ///
+ /// Read slot information
+ ///
+ /// XmlReader representing a machine block
+ /// ListXmlSlot to populate
+ private void ReadSlot(XmlReader reader, Slot slot)
+ {
+ // 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 SlotOption
+ {
+ Name = reader.GetAttribute("name"),
+ DeviceName = reader.GetAttribute("devname"),
+ 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, Input 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 Control
+ {
+ ControlType = reader.GetAttribute("type").AsControlType(),
+ Player = Utilities.CleanLong(reader.GetAttribute("player")),
+ Buttons = Utilities.CleanLong(reader.GetAttribute("buttons")),
+ RequiredButtons = Utilities.CleanLong(reader.GetAttribute("reqbuttons")),
+ Minimum = Utilities.CleanLong(reader.GetAttribute("minimum")),
+ Maximum = Utilities.CleanLong(reader.GetAttribute("maximum")),
+ Sensitivity = Utilities.CleanLong(reader.GetAttribute("sensitivity")),
+ KeyDelta = Utilities.CleanLong(reader.GetAttribute("keydelta")),
+ Reverse = reader.GetAttribute("reverse").AsYesNo(),
+ Ways = reader.GetAttribute("ways"),
+ Ways2 = reader.GetAttribute("ways2"),
+ Ways3 = reader.GetAttribute("ways3"),
+ };
+
+ input.Controls.Add(control);
+
+ reader.Read();
+ break;
+
+ default:
+ reader.Read();
+ break;
+ }
+ }
+ }
+
+ ///
+ /// Read DipSwitch information
+ ///
+ /// XmlReader representing a diskarea block
+ /// DipSwitch to populate
+ private void ReadDipSwitch(XmlReader reader, DipSwitch dipSwitch)
+ {
+ // If we have an empty dipswitch, skip it
+ if (reader == null)
+ return;
+
+ // Get lists ready
+ dipSwitch.Conditions = new List();
+ 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 "condition":
+ var condition = new Condition
+ {
+ Tag = reader.GetAttribute("tag"),
+ Mask = reader.GetAttribute("mask"),
+ Relation = reader.GetAttribute("relation").AsRelation(),
+ Value = reader.GetAttribute("value")
+ };
+
+ dipSwitch.Conditions.Add(condition);
+
+ reader.Read();
+ break;
+
+ case "diplocation":
+ var dipLocation = new Location
+ {
+ Name = reader.GetAttribute("name"),
+ Number = Utilities.CleanLong(reader.GetAttribute("number")),
+ Inverted = reader.GetAttribute("inverted").AsYesNo()
+ };
+
+ dipSwitch.Locations.Add(dipLocation);
+
+ reader.Read();
+ break;
+
+ case "dipvalue":
+ var dipValue = new Setting
+ {
+ Name = reader.GetAttribute("name"),
+ Value = reader.GetAttribute("value"),
+ Default = reader.GetAttribute("default").AsYesNo()
+ };
+
+ // Now read the internal tags
+ ReadDipValue(reader, dipValue);
+
+ dipSwitch.Values.Add(dipValue);
+
+ // Skip the dipvalue now that we've processed it
+ reader.Read();
+ break;
+
+ default:
+ reader.Read();
+ break;
+ }
+ }
+ }
+
+ ///
+ /// Read DipValue information
+ ///
+ /// XmlReader representing a diskarea block
+ /// Setting to populate
+ private void ReadDipValue(XmlReader reader, Setting dipValue)
+ {
+ // If we have an empty dipvalue, skip it
+ if (reader == null)
+ return;
+
+ // Get list ready
+ dipValue.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 dipvalue
+ switch (reader.Name)
+ {
+ case "condition":
+ var condition = new Condition
+ {
+ Tag = reader.GetAttribute("tag"),
+ Mask = reader.GetAttribute("mask"),
+ Relation = reader.GetAttribute("relation").AsRelation(),
+ Value = reader.GetAttribute("value")
+ };
+
+ dipValue.Conditions.Add(condition);
+
+ reader.Read();
+ break;
+
+ default:
+ reader.Read();
+ break;
+ }
+ }
+ }
+
+ ///
+ /// Read Configuration information
+ ///
+ /// XmlReader representing a diskarea block
+ /// Configuration to populate
+ private void ReadConfiguration(XmlReader reader, Configuration configuration)
+ {
+ // If we have an empty configuration, skip it
+ if (reader == null)
+ return;
+
+ // Get lists ready
+ configuration.Conditions = new List();
+ 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 "condition":
+ var condition = new Condition
+ {
+ Tag = reader.GetAttribute("tag"),
+ Mask = reader.GetAttribute("mask"),
+ Relation = reader.GetAttribute("relation").AsRelation(),
+ Value = reader.GetAttribute("value")
+ };
+
+ configuration.Conditions.Add(condition);
+
+ reader.Read();
+ break;
+
+ case "conflocation":
+ var confLocation = new Location
+ {
+ Name = reader.GetAttribute("name"),
+ Number = Utilities.CleanLong(reader.GetAttribute("number")),
+ Inverted = reader.GetAttribute("inverted").AsYesNo()
+ };
+
+ configuration.Locations.Add(confLocation);
+
+ reader.Read();
+ break;
+
+ case "confsetting":
+ var confSetting = new Setting
+ {
+ Name = reader.GetAttribute("name"),
+ Value = reader.GetAttribute("value"),
+ Default = reader.GetAttribute("default").AsYesNo()
+ };
+
+ // Now read the internal tags
+ ReadConfSetting(reader, confSetting);
+
+ configuration.Settings.Add(confSetting);
+
+ // Skip the dipvalue now that we've processed it
+ reader.Read();
+ break;
+
+ default:
+ reader.Read();
+ break;
+ }
+ }
+ }
+
+ ///
+ /// Read ConfSetting information
+ ///
+ /// XmlReader representing a diskarea block
+ /// Setting to populate
+ private void ReadConfSetting(XmlReader reader, Setting confSetting)
+ {
+ // If we have an empty confsetting, skip it
+ if (reader == null)
+ return;
+
+ // Get list ready
+ confSetting.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 confsetting
+ switch (reader.Name)
+ {
+ case "condition":
+ var condition = new Condition
+ {
+ Tag = reader.GetAttribute("tag"),
+ Mask = reader.GetAttribute("mask"),
+ Relation = reader.GetAttribute("relation").AsRelation(),
+ Value = reader.GetAttribute("value")
+ };
+
+ confSetting.Conditions.Add(condition);
+
+ reader.Read();
+ break;
+
+ default:
+ reader.Read();
+ break;
+ }
+ }
+ }
+
+ ///
+ /// Read Port information
+ ///
+ /// XmlReader representing a diskarea block
+ /// ListXmlPort to populate
+ private void ReadPort(XmlReader reader, Port 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 Analog
+ {
+ Mask = reader.GetAttribute("mask")
+ };
+
+ port.Analogs.Add(analog);
+
+ reader.Read();
+ break;
+
+ default:
+ reader.Read();
+ break;
+ }
+ }
+ }
+
+ ///
+ /// Read Adjuster information
+ ///
+ /// XmlReader representing a diskarea block
+ /// Adjuster to populate
+ private void ReadAdjuster(XmlReader reader, Adjuster 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 Condition
+ {
+ Tag = reader.GetAttribute("tag"),
+ Mask = reader.GetAttribute("mask"),
+ Relation = reader.GetAttribute("relation").AsRelation(),
+ 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, Device 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 Instance
+ {
+ Name = reader.GetAttribute("name"),
+ BriefName = reader.GetAttribute("briefname"),
+ };
+
+ device.Instances.Add(instance);
+
+ reader.Read();
+ break;
+
+ case "extension":
+ var extension = new Extension
+ {
+ Name = reader.GetAttribute("name"),
+ };
+
+ device.Extensions.Add(extension);
+
+ reader.Read();
+ break;
+
+ default:
+ reader.Read();
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/SabreTools.DatFiles/Formats/Listxml.Writer.cs b/SabreTools.DatFiles/Formats/Listxml.Writer.cs
new file mode 100644
index 00000000..fd987665
--- /dev/null
+++ b/SabreTools.DatFiles/Formats/Listxml.Writer.cs
@@ -0,0 +1,604 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Xml;
+using SabreTools.Core;
+using SabreTools.Core.Tools;
+using SabreTools.DatItems;
+using SabreTools.DatItems.Formats;
+using SabreTools.IO;
+
+namespace SabreTools.DatFiles.Formats
+{
+ ///
+ /// Represents writing a MAME XML DAT
+ ///
+ internal partial class Listxml : DatFile
+ {
+ ///
+ protected override ItemType[] GetSupportedTypes()
+ {
+ return new ItemType[]
+ {
+ ItemType.Adjuster,
+ ItemType.BiosSet,
+ ItemType.Chip,
+ ItemType.Condition,
+ ItemType.Configuration,
+ ItemType.Device,
+ ItemType.DeviceReference,
+ ItemType.DipSwitch,
+ ItemType.Disk,
+ ItemType.Display,
+ ItemType.Driver,
+ ItemType.Feature,
+ ItemType.Input,
+ ItemType.Port,
+ ItemType.RamOption,
+ ItemType.Rom,
+ ItemType.Sample,
+ ItemType.Slot,
+ ItemType.SoftwareList,
+ ItemType.Sound,
+ };
+ }
+
+ ///
+ protected override List GetMissingRequiredFields(DatItem datItem)
+ {
+ // TODO: Check required fields
+ return null;
+ }
+
+ ///
+ public override bool WriteToFile(string outfile, bool ignoreblanks = false, bool throwOnError = false)
+ {
+ try
+ {
+ logger.User($"Writing to '{outfile}'...");
+ FileStream fs = System.IO.File.Create(outfile);
+
+ // If we get back null for some reason, just log and return
+ if (fs == null)
+ {
+ logger.Warning($"File '{outfile}' could not be created for writing! Please check to see if the file is writable");
+ return false;
+ }
+
+ XmlTextWriter xtw = new(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)
+ {
+ ConcurrentList datItems = Items.FilteredItems(key);
+
+ // If this machine doesn't contain any writable items, skip
+ if (!ContainsWritable(datItems))
+ continue;
+
+ // Resolve the names in the block
+ datItems = DatItem.ResolveNames(datItems);
+
+ for (int index = 0; index < datItems.Count; index++)
+ {
+ DatItem datItem = datItems[index];
+
+ // 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() != datItem.Machine.Name.ToLowerInvariant())
+ WriteEndGame(xtw);
+
+ // If we have a new game, output the beginning of the new item
+ if (lastgame == null || lastgame.ToLowerInvariant() != datItem.Machine.Name.ToLowerInvariant())
+ WriteStartGame(xtw, datItem);
+
+ // Check for a "null" item
+ datItem = ProcessNullifiedItem(datItem);
+
+ // Write out the item if we're not ignoring
+ if (!ShouldIgnore(datItem, ignoreblanks))
+ WriteDatItem(xtw, datItem);
+
+ // Set the new data to compare against
+ lastgame = datItem.Machine.Name;
+ }
+ }
+
+ // Write the file footer out
+ WriteFooter(xtw);
+
+ logger.User($"'{outfile}' written!{Environment.NewLine}");
+ xtw.Dispose();
+ fs.Dispose();
+ }
+ catch (Exception ex) when (!throwOnError)
+ {
+ logger.Error(ex);
+ return false;
+ }
+
+ return true;
+ }
+
+ ///
+ /// Write out DAT header using the supplied StreamWriter
+ ///
+ /// XmlTextWriter to output to
+ private void WriteHeader(XmlTextWriter xtw)
+ {
+ xtw.WriteStartDocument();
+
+ xtw.WriteStartElement("mame");
+ xtw.WriteRequiredAttributeString("build", Header.Name);
+ xtw.WriteOptionalAttributeString("debug", Header.Debug.FromYesNo());
+ xtw.WriteOptionalAttributeString("mameconfig", Header.MameConfig);
+
+ xtw.Flush();
+ }
+
+ ///
+ /// 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 void WriteStartGame(XmlTextWriter xtw, DatItem datItem)
+ {
+ // No game should start with a path separator
+ datItem.Machine.Name = datItem.Machine.Name.TrimStart(Path.DirectorySeparatorChar);
+
+ // Build the state
+ xtw.WriteStartElement("machine");
+ xtw.WriteRequiredAttributeString("name", datItem.Machine.Name);
+ xtw.WriteOptionalAttributeString("sourcefile", datItem.Machine.SourceFile);
+
+ 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");
+
+ xtw.WriteOptionalAttributeString("runnable", datItem.Machine.Runnable.FromRunnable());
+
+ if (!string.Equals(datItem.Machine.Name, datItem.Machine.CloneOf, StringComparison.OrdinalIgnoreCase))
+ xtw.WriteOptionalAttributeString("cloneof", datItem.Machine.CloneOf);
+ if (!string.Equals(datItem.Machine.Name, datItem.Machine.RomOf, StringComparison.OrdinalIgnoreCase))
+ xtw.WriteOptionalAttributeString("romof", datItem.Machine.RomOf);
+ if (!string.Equals(datItem.Machine.Name, datItem.Machine.SampleOf, StringComparison.OrdinalIgnoreCase))
+ xtw.WriteOptionalAttributeString("sampleof", datItem.Machine.SampleOf);
+
+ xtw.WriteOptionalElementString("description", datItem.Machine.Description);
+ xtw.WriteOptionalElementString("year", datItem.Machine.Year);
+ xtw.WriteOptionalElementString("manufacturer", datItem.Machine.Manufacturer);
+ xtw.WriteOptionalElementString("history", datItem.Machine.History);
+
+ xtw.Flush();
+ }
+
+ ///
+ /// Write out Game start using the supplied StreamWriter
+ ///
+ /// XmlTextWriter to output to
+ private void WriteEndGame(XmlTextWriter xtw)
+ {
+ // End machine
+ xtw.WriteEndElement();
+
+ xtw.Flush();
+ }
+
+ ///
+ /// Write out DatItem using the supplied StreamWriter
+ ///
+ /// XmlTextWriter to output to
+ /// DatItem object to be output
+ private void WriteDatItem(XmlTextWriter xtw, DatItem datItem)
+ {
+ // Pre-process the item name
+ ProcessItemName(datItem, true);
+
+ // Build the state
+ switch (datItem.ItemType)
+ {
+ case ItemType.Adjuster:
+ var adjuster = datItem as Adjuster;
+ xtw.WriteStartElement("adjuster");
+ xtw.WriteRequiredAttributeString("name", adjuster.Name);
+ xtw.WriteRequiredAttributeString("default", adjuster.Default.FromYesNo());
+ if (adjuster.ConditionsSpecified)
+ {
+ foreach (var adjusterCondition in adjuster.Conditions)
+ {
+ xtw.WriteStartElement("condition");
+ xtw.WriteRequiredAttributeString("tag", adjusterCondition.Tag);
+ xtw.WriteRequiredAttributeString("mask", adjusterCondition.Mask);
+ xtw.WriteRequiredAttributeString("relation", adjusterCondition.Relation.FromRelation());
+ xtw.WriteRequiredAttributeString("value", adjusterCondition.Value);
+ xtw.WriteEndElement();
+ }
+ }
+ xtw.WriteEndElement();
+ break;
+
+ case ItemType.BiosSet:
+ var biosSet = datItem as BiosSet;
+ xtw.WriteStartElement("biosset");
+ xtw.WriteRequiredAttributeString("name", biosSet.Name);
+ xtw.WriteRequiredAttributeString("description", biosSet.Description);
+ xtw.WriteOptionalAttributeString("default", biosSet.Default?.ToString().ToLowerInvariant());
+ xtw.WriteEndElement();
+ break;
+
+ case ItemType.Chip:
+ var chip = datItem as Chip;
+ xtw.WriteStartElement("chip");
+ xtw.WriteRequiredAttributeString("name", chip.Name);
+ xtw.WriteOptionalAttributeString("tag", chip.Tag);
+ xtw.WriteRequiredAttributeString("type", chip.ChipType.FromChipType());
+ xtw.WriteOptionalAttributeString("clock", chip.Clock?.ToString());
+ xtw.WriteEndElement();
+ break;
+
+ case ItemType.Condition:
+ var condition = datItem as Condition;
+ xtw.WriteStartElement("condition");
+ xtw.WriteRequiredAttributeString("tag", condition.Tag);
+ xtw.WriteRequiredAttributeString("mask", condition.Mask);
+ xtw.WriteRequiredAttributeString("relation", condition.Relation.FromRelation());
+ xtw.WriteRequiredAttributeString("value", condition.Value);
+ xtw.WriteEndElement();
+ break;
+
+ case ItemType.Configuration:
+ var configuration = datItem as Configuration;
+ xtw.WriteStartElement("configuration");
+ xtw.WriteRequiredAttributeString("name", configuration.Name);
+ xtw.WriteRequiredAttributeString("tag", configuration.Tag);
+ xtw.WriteRequiredAttributeString("mask", configuration.Mask);
+
+ if (configuration.ConditionsSpecified)
+ {
+ foreach (var configurationCondition in configuration.Conditions)
+ {
+ xtw.WriteStartElement("condition");
+ xtw.WriteRequiredAttributeString("tag", configurationCondition.Tag);
+ xtw.WriteRequiredAttributeString("mask", configurationCondition.Mask);
+ xtw.WriteRequiredAttributeString("relation", configurationCondition.Relation.FromRelation());
+ xtw.WriteRequiredAttributeString("value", configurationCondition.Value);
+ xtw.WriteEndElement();
+ }
+ }
+ if (configuration.LocationsSpecified)
+ {
+ foreach (var location in configuration.Locations)
+ {
+ xtw.WriteStartElement("conflocation");
+ xtw.WriteRequiredAttributeString("name", location.Name);
+ xtw.WriteRequiredAttributeString("number", location.Number?.ToString());
+ xtw.WriteOptionalAttributeString("inverted", location.Inverted.FromYesNo());
+ xtw.WriteEndElement();
+ }
+ }
+ if (configuration.SettingsSpecified)
+ {
+ foreach (var setting in configuration.Settings)
+ {
+ xtw.WriteStartElement("confsetting");
+ xtw.WriteRequiredAttributeString("name", setting.Name);
+ xtw.WriteRequiredAttributeString("value", setting.Value);
+ xtw.WriteOptionalAttributeString("default", setting.Default.FromYesNo());
+ if (setting.ConditionsSpecified)
+ {
+ foreach (var confsettingCondition in setting.Conditions)
+ {
+ xtw.WriteStartElement("condition");
+ xtw.WriteRequiredAttributeString("tag", confsettingCondition.Tag);
+ xtw.WriteRequiredAttributeString("mask", confsettingCondition.Mask);
+ xtw.WriteRequiredAttributeString("relation", confsettingCondition.Relation.FromRelation());
+ xtw.WriteRequiredAttributeString("value", confsettingCondition.Value);
+ xtw.WriteEndElement();
+ }
+ }
+ xtw.WriteEndElement();
+ }
+ }
+ xtw.WriteEndElement();
+ break;
+
+ case ItemType.Device:
+ var device = datItem as Device;
+ xtw.WriteStartElement("device");
+ xtw.WriteRequiredAttributeString("type", device.DeviceType.FromDeviceType());
+ xtw.WriteOptionalAttributeString("tag", device.Tag);
+ xtw.WriteOptionalAttributeString("fixed_image", device.FixedImage);
+ xtw.WriteOptionalAttributeString("mandatory", device.Mandatory?.ToString());
+ xtw.WriteOptionalAttributeString("interface", device.Interface);
+ if (device.InstancesSpecified)
+ {
+ foreach (var instance in device.Instances)
+ {
+ xtw.WriteStartElement("instance");
+ xtw.WriteRequiredAttributeString("name", instance.Name);
+ xtw.WriteRequiredAttributeString("briefname", instance.BriefName);
+ xtw.WriteEndElement();
+ }
+ }
+ if (device.ExtensionsSpecified)
+ {
+ foreach (var extension in device.Extensions)
+ {
+ xtw.WriteStartElement("extension");
+ xtw.WriteRequiredAttributeString("name", extension.Name);
+ xtw.WriteEndElement();
+ }
+ }
+ xtw.WriteEndElement();
+ break;
+
+ case ItemType.DeviceReference:
+ var deviceRef = datItem as DeviceReference;
+ xtw.WriteStartElement("device_ref");
+ xtw.WriteRequiredAttributeString("name", deviceRef.Name);
+ xtw.WriteEndElement();
+ break;
+
+ case ItemType.DipSwitch:
+ var dipSwitch = datItem as DipSwitch;
+ xtw.WriteStartElement("dipswitch");
+ xtw.WriteRequiredAttributeString("name", dipSwitch.Name);
+ xtw.WriteRequiredAttributeString("tag", dipSwitch.Tag);
+ xtw.WriteRequiredAttributeString("mask", dipSwitch.Mask);
+ if (dipSwitch.ConditionsSpecified)
+ {
+ foreach (var dipSwitchCondition in dipSwitch.Conditions)
+ {
+ xtw.WriteStartElement("condition");
+ xtw.WriteRequiredAttributeString("tag", dipSwitchCondition.Tag);
+ xtw.WriteRequiredAttributeString("mask", dipSwitchCondition.Mask);
+ xtw.WriteRequiredAttributeString("relation", dipSwitchCondition.Relation.FromRelation());
+ xtw.WriteRequiredAttributeString("value", dipSwitchCondition.Value);
+ xtw.WriteEndElement();
+ }
+ }
+ if (dipSwitch.LocationsSpecified)
+ {
+ foreach (var location in dipSwitch.Locations)
+ {
+ xtw.WriteStartElement("diplocation");
+ xtw.WriteRequiredAttributeString("name", location.Name);
+ xtw.WriteRequiredAttributeString("number", location.Number?.ToString());
+ xtw.WriteOptionalAttributeString("inverted", location.Inverted.FromYesNo());
+ xtw.WriteEndElement();
+ }
+ }
+ if (dipSwitch.ValuesSpecified)
+ {
+ foreach (var value in dipSwitch.Values)
+ {
+ xtw.WriteStartElement("dipvalue");
+ xtw.WriteRequiredAttributeString("name", value.Name);
+ xtw.WriteRequiredAttributeString("value", value.Value);
+ xtw.WriteOptionalAttributeString("default", value.Default.FromYesNo());
+ if (value.ConditionsSpecified)
+ {
+ foreach (var dipValueCondition in value.Conditions)
+ {
+ xtw.WriteStartElement("condition");
+ xtw.WriteRequiredAttributeString("tag", dipValueCondition.Tag);
+ xtw.WriteRequiredAttributeString("mask", dipValueCondition.Mask);
+ xtw.WriteRequiredAttributeString("relation", dipValueCondition.Relation.FromRelation());
+ xtw.WriteRequiredAttributeString("value", dipValueCondition.Value);
+ xtw.WriteEndElement();
+ }
+ }
+ xtw.WriteEndElement();
+ }
+ }
+ xtw.WriteEndElement();
+ break;
+
+ case ItemType.Disk:
+ var disk = datItem as Disk;
+ xtw.WriteStartElement("disk");
+ xtw.WriteRequiredAttributeString("name", disk.Name);
+ xtw.WriteOptionalAttributeString("sha1", disk.SHA1?.ToLowerInvariant());
+ xtw.WriteOptionalAttributeString("merge", disk.MergeTag);
+ xtw.WriteOptionalAttributeString("region", disk.Region);
+ xtw.WriteOptionalAttributeString("index", disk.Index);
+ xtw.WriteOptionalAttributeString("writable", disk.Writable.FromYesNo());
+ if (disk.ItemStatus != ItemStatus.None)
+ xtw.WriteOptionalAttributeString("status", disk.ItemStatus.FromItemStatus(false));
+ xtw.WriteOptionalAttributeString("optional", disk.Optional.FromYesNo());
+ xtw.WriteEndElement();
+ break;
+
+ case ItemType.Display:
+ var display = datItem as Display;
+ xtw.WriteStartElement("display");
+ xtw.WriteOptionalAttributeString("tag", display.Tag);
+ xtw.WriteRequiredAttributeString("type", display.DisplayType.FromDisplayType());
+ xtw.WriteOptionalAttributeString("rotate", display.Rotate?.ToString());
+ xtw.WriteOptionalAttributeString("flipx", display.FlipX.FromYesNo());
+ xtw.WriteOptionalAttributeString("width", display.Width?.ToString());
+ xtw.WriteOptionalAttributeString("height", display.Height?.ToString());
+ xtw.WriteRequiredAttributeString("refresh", display.Refresh?.ToString("N6"));
+ xtw.WriteOptionalAttributeString("pixclock", display.PixClock?.ToString());
+ xtw.WriteOptionalAttributeString("htotal", display.HTotal?.ToString());
+ xtw.WriteOptionalAttributeString("hbend", display.HBEnd?.ToString());
+ xtw.WriteOptionalAttributeString("hstart", display.HBStart?.ToString());
+ xtw.WriteOptionalAttributeString("vtotal", display.VTotal?.ToString());
+ xtw.WriteOptionalAttributeString("vbend", display.VBEnd?.ToString());
+ xtw.WriteOptionalAttributeString("vbstart", display.VBStart?.ToString());
+ xtw.WriteEndElement();
+ break;
+
+ case ItemType.Driver:
+ var driver = datItem as Driver;
+ xtw.WriteStartElement("driver");
+ xtw.WriteRequiredAttributeString("status", driver.Status.FromSupportStatus());
+ xtw.WriteRequiredAttributeString("emulation", driver.Emulation.FromSupportStatus());
+ xtw.WriteOptionalAttributeString("cocktail", driver.Cocktail.FromSupportStatus());
+ xtw.WriteRequiredAttributeString("savestate", driver.SaveState.FromSupported(true));
+ xtw.WriteOptionalAttributeString("requiresartwork", driver.RequiresArtwork.FromYesNo());
+ xtw.WriteOptionalAttributeString("unofficial", driver.Unofficial.FromYesNo());
+ xtw.WriteOptionalAttributeString("nosoundhardware", driver.NoSoundHardware.FromYesNo());
+ xtw.WriteOptionalAttributeString("incomplete", driver.Incomplete.FromYesNo());
+ xtw.WriteEndElement();
+ break;
+
+ case ItemType.Feature:
+ var feature = datItem as Feature;
+ xtw.WriteStartElement("feature");
+ xtw.WriteRequiredAttributeString("type", feature.Type.FromFeatureType());
+ xtw.WriteOptionalAttributeString("status", feature.Status.FromFeatureStatus());
+ xtw.WriteOptionalAttributeString("overall", feature.Overall.FromFeatureStatus());
+ xtw.WriteEndElement();
+ break;
+
+ case ItemType.Input:
+ var input = datItem as Input;
+ xtw.WriteStartElement("input");
+ xtw.WriteOptionalAttributeString("service", input.Service.FromYesNo());
+ xtw.WriteOptionalAttributeString("tilt", input.Tilt.FromYesNo());
+ xtw.WriteRequiredAttributeString("players", input.Players?.ToString());
+ xtw.WriteOptionalAttributeString("coins", input.Coins?.ToString());
+ if (input.ControlsSpecified)
+ {
+ foreach (var control in input.Controls)
+ {
+ xtw.WriteStartElement("control");
+ xtw.WriteRequiredAttributeString("type", control.ControlType.FromControlType());
+ xtw.WriteOptionalAttributeString("player", control.Player?.ToString());
+ xtw.WriteOptionalAttributeString("buttons", control.Buttons?.ToString());
+ xtw.WriteOptionalAttributeString("reqbuttons", control.RequiredButtons?.ToString());
+ xtw.WriteOptionalAttributeString("minimum", control.Minimum?.ToString());
+ xtw.WriteOptionalAttributeString("maximum", control.Maximum?.ToString());
+ xtw.WriteOptionalAttributeString("sensitivity", control.Sensitivity?.ToString());
+ xtw.WriteOptionalAttributeString("keydelta", control.KeyDelta?.ToString());
+ xtw.WriteOptionalAttributeString("reverse", control.Reverse.FromYesNo());
+ xtw.WriteOptionalAttributeString("ways", control.Ways);
+ xtw.WriteOptionalAttributeString("ways2", control.Ways2);
+ xtw.WriteOptionalAttributeString("ways3", control.Ways3);
+ xtw.WriteEndElement();
+ }
+ }
+ xtw.WriteEndElement();
+ break;
+
+ case ItemType.Port:
+ var port = datItem as Port;
+ xtw.WriteStartElement("port");
+ xtw.WriteRequiredAttributeString("tag", port.Tag);
+ if (port.AnalogsSpecified)
+ {
+ foreach (var analog in port.Analogs)
+ {
+ xtw.WriteStartElement("analog");
+ xtw.WriteRequiredAttributeString("mask", analog.Mask);
+ xtw.WriteEndElement();
+ }
+ }
+ xtw.WriteEndElement();
+ break;
+
+ case ItemType.RamOption:
+ var ramOption = datItem as RamOption;
+ xtw.WriteStartElement("ramoption");
+ xtw.WriteRequiredAttributeString("name", ramOption.Name);
+ xtw.WriteOptionalAttributeString("default", ramOption.Default.FromYesNo());
+ xtw.WriteRaw(ramOption.Content ?? string.Empty);
+ xtw.WriteFullEndElement();
+ break;
+
+ case ItemType.Rom:
+ var rom = datItem as Rom;
+ xtw.WriteStartElement("rom");
+ xtw.WriteRequiredAttributeString("name", rom.Name);
+ xtw.WriteOptionalAttributeString("bios", rom.Bios);
+ xtw.WriteRequiredAttributeString("size", rom.Size?.ToString());
+ xtw.WriteOptionalAttributeString("crc", rom.CRC?.ToLowerInvariant());
+ xtw.WriteOptionalAttributeString("sha1", rom.SHA1?.ToLowerInvariant());
+ xtw.WriteOptionalAttributeString("merge", rom.MergeTag);
+ xtw.WriteOptionalAttributeString("region", rom.Region);
+ xtw.WriteOptionalAttributeString("offset", rom.Offset);
+ if (rom.ItemStatus != ItemStatus.None)
+ xtw.WriteOptionalAttributeString("status", rom.ItemStatus.FromItemStatus(false));
+ xtw.WriteOptionalAttributeString("optional", rom.Optional.FromYesNo());
+ xtw.WriteEndElement();
+ break;
+
+ case ItemType.Sample:
+ var sample = datItem as Sample;
+ xtw.WriteStartElement("sample");
+ xtw.WriteRequiredAttributeString("name", sample.Name);
+ xtw.WriteEndElement();
+ break;
+
+ case ItemType.Slot:
+ var slot = datItem as Slot;
+ xtw.WriteStartElement("slot");
+ xtw.WriteRequiredAttributeString("name", slot.Name);
+ if (slot.SlotOptionsSpecified)
+ {
+ foreach (var slotOption in slot.SlotOptions)
+ {
+ xtw.WriteStartElement("slotoption");
+ xtw.WriteRequiredAttributeString("name", slotOption.Name);
+ xtw.WriteRequiredAttributeString("devname", slotOption.DeviceName);
+ xtw.WriteOptionalAttributeString("default", slotOption.Default.FromYesNo());
+ xtw.WriteEndElement();
+ }
+ }
+ xtw.WriteEndElement();
+ break;
+
+ case ItemType.SoftwareList:
+ var softwareList = datItem as DatItems.Formats.SoftwareList;
+ xtw.WriteStartElement("softwarelist");
+ xtw.WriteRequiredAttributeString("tag", softwareList.Tag);
+ xtw.WriteRequiredAttributeString("name", softwareList.Name);
+ if (softwareList.Status != SoftwareListStatus.None)
+ xtw.WriteRequiredAttributeString("status", softwareList.Status.FromSoftwareListStatus());
+ xtw.WriteOptionalAttributeString("filter", softwareList.Filter);
+ xtw.WriteEndElement();
+ break;
+
+ case ItemType.Sound:
+ var sound = datItem as Sound;
+ xtw.WriteStartElement("sound");
+ xtw.WriteRequiredAttributeString("channels", sound.Channels?.ToString());
+ xtw.WriteEndElement();
+ break;
+ }
+
+ xtw.Flush();
+ }
+
+ ///
+ /// Write out DAT footer using the supplied StreamWriter
+ ///
+ /// XmlTextWriter to output to
+ private void WriteFooter(XmlTextWriter xtw)
+ {
+ // End machine
+ xtw.WriteEndElement();
+
+ // End mame
+ xtw.WriteEndElement();
+
+ xtw.Flush();
+ }
+ }
+}
diff --git a/SabreTools.DatFiles/Formats/Listxml.cs b/SabreTools.DatFiles/Formats/Listxml.cs
index 8211d928..75f4094d 100644
--- a/SabreTools.DatFiles/Formats/Listxml.cs
+++ b/SabreTools.DatFiles/Formats/Listxml.cs
@@ -1,22 +1,9 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Xml;
-using System.Xml.Schema;
-using SabreTools.Core;
-using SabreTools.Core.Tools;
-using SabreTools.DatItems;
-using SabreTools.DatItems.Formats;
-using SabreTools.IO;
-
-namespace SabreTools.DatFiles.Formats
+namespace SabreTools.DatFiles.Formats
{
///
- /// Represents parsing and writing of a MAME XML DAT
+ /// Represents a MAME XML DAT
///
- internal class Listxml : DatFile
+ internal partial class Listxml : DatFile
{
///
/// DTD for original MAME XML DATs
@@ -192,1717 +179,5 @@ namespace SabreTools.DatFiles.Formats
: base(datFile)
{
}
-
- ///
- public override void ParseFile(string filename, int indexId, bool keep, bool statsOnly = false, bool throwOnError = false)
- {
- // Prepare all internal variables
- XmlReader xtr = XmlReader.Create(filename, new XmlReaderSettings
- {
- CheckCharacters = false,
- DtdProcessing = DtdProcessing.Ignore,
- IgnoreComments = true,
- IgnoreWhitespace = true,
- ValidationFlags = XmlSchemaValidationFlags.None,
- ValidationType = ValidationType.None,
- });
-
- // 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 ??= xtr.GetAttribute("build");
- Header.Description ??= Header.Name;
- Header.Debug ??= xtr.GetAttribute("debug").AsYesNo();
- Header.MameConfig ??= xtr.GetAttribute("mameconfig");
- xtr.Read();
- break;
-
- // Handle M1 DATs since they're 99% the same as a SL DAT
- case "m1":
- Header.Name ??= "M1";
- Header.Description ??= "M1";
- Header.Version ??= xtr.GetAttribute("version") ?? string.Empty;
- 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(), statsOnly, filename, indexId);
-
- // Skip the machine now that we've processed it
- xtr.Skip();
- break;
-
- default:
- xtr.Read();
- break;
- }
- }
- }
- catch (Exception ex) when (!throwOnError)
- {
- logger.Warning(ex, $"Exception found while parsing '{filename}'");
-
- // For XML errors, just skip the affected node
- xtr?.Read();
- }
-
- xtr.Dispose();
- }
-
- ///
- /// Read machine information
- ///
- /// XmlReader representing a machine block
- /// True to only add item statistics while parsing, false otherwise
- /// Name of the file to be parsed
- /// Index ID for the DAT
- private void ReadMachine(XmlReader reader, bool statsOnly, 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 = 0x0;
- 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()
- {
- Name = reader.GetAttribute("name"),
- Description = reader.GetAttribute("name"),
- CloneOf = reader.GetAttribute("cloneof"),
- RomOf = reader.GetAttribute("romof"),
- SampleOf = reader.GetAttribute("sampleof"),
- MachineType = (machineType == 0x0 ? MachineType.None : machineType),
-
- SourceFile = reader.GetAttribute("sourcefile"),
- Runnable = reader.GetAttribute("runnable").AsRunnable(),
- };
-
- // Get list for new DatItems
- List datItems = new();
-
- 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 "history":
- machine.History = reader.ReadElementContentAsString();
- break;
-
- case "adjuster":
- var adjuster = new Adjuster
- {
- Name = reader.GetAttribute("name"),
- Default = reader.GetAttribute("default").AsYesNo(),
- Conditions = new List(),
-
- Source = new Source
- {
- Index = indexId,
- Name = filename,
- },
- };
-
- // Now read the internal tags
- ReadAdjuster(reader.ReadSubtree(), adjuster);
-
- datItems.Add(adjuster);
-
- // Skip the adjuster now that we've processed it
- reader.Skip();
- 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 "chip":
- var chip = new Chip
- {
- Name = reader.GetAttribute("name"),
- Tag = reader.GetAttribute("tag"),
- ChipType = reader.GetAttribute("type").AsChipType(),
- Clock = Utilities.CleanLong(reader.GetAttribute("clock")),
-
- Source = new Source
- {
- Index = indexId,
- Name = filename,
- },
- };
-
- datItems.Add(chip);
-
- reader.Read();
- break;
-
- case "condition":
- datItems.Add(new Condition
- {
- Tag = reader.GetAttribute("tag"),
- Mask = reader.GetAttribute("mask"),
- Relation = reader.GetAttribute("relation").AsRelation(),
- Value = reader.GetAttribute("value"),
-
- Source = new Source
- {
- Index = indexId,
- Name = filename,
- },
- });
-
- reader.Read();
- break;
-
- case "configuration":
- var configuration = new Configuration
- {
- Name = reader.GetAttribute("name"),
- Tag = reader.GetAttribute("tag"),
- Mask = reader.GetAttribute("mask"),
- Conditions = new List(),
- Locations = new List(),
- Settings = new List(),
-
- Source = new Source
- {
- Index = indexId,
- Name = filename,
- },
- };
-
- // Now read the internal tags
- ReadConfiguration(reader.ReadSubtree(), configuration);
-
- datItems.Add(configuration);
-
- // Skip the configuration now that we've processed it
- reader.Skip();
- break;
-
- case "device":
- var device = new Device
- {
- DeviceType = reader.GetAttribute("type").AsDeviceType(),
- Tag = reader.GetAttribute("tag"),
- FixedImage = reader.GetAttribute("fixed_image"),
- Mandatory = Utilities.CleanLong(reader.GetAttribute("mandatory")),
- Interface = reader.GetAttribute("interface"),
-
- Source = new Source
- {
- Index = indexId,
- Name = filename,
- },
- };
-
- // Now read the internal tags
- ReadDevice(reader.ReadSubtree(), device);
-
- datItems.Add(device);
-
- // Skip the device now that we've processed it
- reader.Skip();
- break;
-
- case "device_ref":
- datItems.Add(new DeviceReference
- {
- Name = reader.GetAttribute("name"),
-
- Source = new Source
- {
- Index = indexId,
- Name = filename,
- },
- });
-
- reader.Read();
- break;
-
- case "dipswitch":
- var dipSwitch = new DipSwitch
- {
- Name = reader.GetAttribute("name"),
- Tag = reader.GetAttribute("tag"),
- Mask = reader.GetAttribute("mask"),
- Conditions = new List(),
- Locations = new List(),
- Values = new List(),
-
- Source = new Source
- {
- Index = indexId,
- Name = filename,
- },
- };
-
- // Now read the internal tags
- ReadDipSwitch(reader.ReadSubtree(), dipSwitch);
-
- datItems.Add(dipSwitch);
-
- // Skip the dipswitch now that we've processed it
- reader.Skip();
- break;
-
- case "disk":
- datItems.Add(new Disk
- {
- Name = reader.GetAttribute("name"),
- SHA1 = reader.GetAttribute("sha1"),
- 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 "display":
- var display = new Display
- {
- Tag = reader.GetAttribute("tag"),
- DisplayType = reader.GetAttribute("type").AsDisplayType(),
- Rotate = Utilities.CleanLong(reader.GetAttribute("rotate")),
- FlipX = reader.GetAttribute("flipx").AsYesNo(),
- Width = Utilities.CleanLong(reader.GetAttribute("width")),
- Height = Utilities.CleanLong(reader.GetAttribute("height")),
- Refresh = Utilities.CleanDouble(reader.GetAttribute("refresh")),
- PixClock = Utilities.CleanLong(reader.GetAttribute("pixclock")),
- HTotal = Utilities.CleanLong(reader.GetAttribute("htotal")),
- HBEnd = Utilities.CleanLong(reader.GetAttribute("hbend")),
- HBStart = Utilities.CleanLong(reader.GetAttribute("hbstart")),
- VTotal = Utilities.CleanLong(reader.GetAttribute("vtotal")),
- VBEnd = Utilities.CleanLong(reader.GetAttribute("vbend")),
- VBStart = Utilities.CleanLong(reader.GetAttribute("vbstart")),
-
- Source = new Source
- {
- Index = indexId,
- Name = filename,
- },
- };
-
- datItems.Add(display);
-
- reader.Read();
- break;
-
- case "driver":
- datItems.Add(new Driver
- {
- Status = reader.GetAttribute("status").AsSupportStatus(),
- Emulation = reader.GetAttribute("emulation").AsSupportStatus(),
- Cocktail = reader.GetAttribute("cocktail").AsSupportStatus(),
- SaveState = reader.GetAttribute("savestate").AsSupported(),
- RequiresArtwork = reader.GetAttribute("requiresartwork").AsYesNo(),
- Unofficial = reader.GetAttribute("unofficial").AsYesNo(),
- NoSoundHardware = reader.GetAttribute("nosoundhardware").AsYesNo(),
- Incomplete = reader.GetAttribute("incomplete").AsYesNo(),
-
- Source = new Source
- {
- Index = indexId,
- Name = filename,
- },
- });
-
- reader.Read();
- break;
-
- case "feature":
- datItems.Add(new Feature
- {
- Type = reader.GetAttribute("type").AsFeatureType(),
- Status = reader.GetAttribute("status").AsFeatureStatus(),
- Overall = reader.GetAttribute("overall").AsFeatureStatus(),
-
- Source = new Source
- {
- Index = indexId,
- Name = filename,
- },
- });
-
- reader.Read();
- break;
-
- case "input":
- var input = new Input
- {
- Service = reader.GetAttribute("service").AsYesNo(),
- Tilt = reader.GetAttribute("tilt").AsYesNo(),
- Players = Utilities.CleanLong(reader.GetAttribute("players")),
- Coins = Utilities.CleanLong(reader.GetAttribute("coins")),
-
- Source = new Source
- {
- Index = indexId,
- Name = filename,
- },
- };
-
- // Now read the internal tags
- ReadInput(reader.ReadSubtree(), input);
-
- datItems.Add(input);
-
- // Skip the input now that we've processed it
- reader.Skip();
- break;
-
- case "port":
- var port = new Port
- {
- Tag = reader.GetAttribute("tag"),
-
- Source = new Source
- {
- Index = indexId,
- Name = filename,
- },
- };
-
- // Now read the internal tags
- ReadPort(reader.ReadSubtree(), port);
-
- datItems.Add(port);
-
- // Skip the port now that we've processed it
- reader.Skip();
- break;
-
- case "ramoption":
- datItems.Add(new RamOption
- {
- Name = reader.GetAttribute("name"),
- Default = reader.GetAttribute("default").AsYesNo(),
- Content = reader.ReadElementContentAsString(),
-
- Source = new Source
- {
- Index = indexId,
- Name = filename,
- },
- });
-
- break;
-
- case "rom":
- datItems.Add(new Rom
- {
- Name = reader.GetAttribute("name"),
- Bios = reader.GetAttribute("bios"),
- Size = Utilities.CleanLong(reader.GetAttribute("size")),
- CRC = reader.GetAttribute("crc"),
- SHA1 = reader.GetAttribute("sha1"),
- 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 "sample":
- datItems.Add(new Sample
- {
- Name = reader.GetAttribute("name"),
-
- Source = new Source
- {
- Index = indexId,
- Name = filename,
- },
- });
-
- reader.Read();
- break;
-
- case "slot":
- var slot = new Slot
- {
- Name = reader.GetAttribute("name"),
- SlotOptions = new List(),
-
- Source = new Source
- {
- Index = indexId,
- Name = filename,
- },
- };
-
- // Now read the internal tags
- ReadSlot(reader.ReadSubtree(), slot);
-
- datItems.Add(slot);
-
- // Skip the slot now that we've processed it
- reader.Skip();
- break;
-
- case "softwarelist":
- datItems.Add(new DatItems.Formats.SoftwareList
- {
- Tag = reader.GetAttribute("tag"),
- Name = reader.GetAttribute("name"),
- Status = reader.GetAttribute("status").AsSoftwareListStatus(),
- Filter = reader.GetAttribute("filter"),
-
- Source = new Source
- {
- Index = indexId,
- Name = filename,
- },
- });
-
- reader.Read();
- break;
-
- case "sound":
- var sound = new Sound
- {
- Channels = Utilities.CleanLong(reader.GetAttribute("channels")),
-
- Source = new Source
- {
- Index = indexId,
- Name = filename,
- },
- };
-
- datItems.Add(sound);
-
- 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, statsOnly);
- }
- }
-
- // If no items were found for this machine, add a Blank placeholder
- else
- {
- Blank blank = new()
- {
- Source = new Source
- {
- Index = indexId,
- Name = filename,
- },
- };
-
- blank.CopyMachineInformation(machine);
-
- // Now process and add the rom
- ParseAddHelper(blank, statsOnly);
- }
- }
-
- ///
- /// Read slot information
- ///
- /// XmlReader representing a machine block
- /// ListXmlSlot to populate
- private void ReadSlot(XmlReader reader, Slot slot)
- {
- // 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 SlotOption
- {
- Name = reader.GetAttribute("name"),
- DeviceName = reader.GetAttribute("devname"),
- 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, Input 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 Control
- {
- ControlType = reader.GetAttribute("type").AsControlType(),
- Player = Utilities.CleanLong(reader.GetAttribute("player")),
- Buttons = Utilities.CleanLong(reader.GetAttribute("buttons")),
- RequiredButtons = Utilities.CleanLong(reader.GetAttribute("reqbuttons")),
- Minimum = Utilities.CleanLong(reader.GetAttribute("minimum")),
- Maximum = Utilities.CleanLong(reader.GetAttribute("maximum")),
- Sensitivity = Utilities.CleanLong(reader.GetAttribute("sensitivity")),
- KeyDelta = Utilities.CleanLong(reader.GetAttribute("keydelta")),
- Reverse = reader.GetAttribute("reverse").AsYesNo(),
- Ways = reader.GetAttribute("ways"),
- Ways2 = reader.GetAttribute("ways2"),
- Ways3 = reader.GetAttribute("ways3"),
- };
-
- input.Controls.Add(control);
-
- reader.Read();
- break;
-
- default:
- reader.Read();
- break;
- }
- }
- }
-
- ///
- /// Read DipSwitch information
- ///
- /// XmlReader representing a diskarea block
- /// DipSwitch to populate
- private void ReadDipSwitch(XmlReader reader, DipSwitch dipSwitch)
- {
- // If we have an empty dipswitch, skip it
- if (reader == null)
- return;
-
- // Get lists ready
- dipSwitch.Conditions = new List();
- 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 "condition":
- var condition = new Condition
- {
- Tag = reader.GetAttribute("tag"),
- Mask = reader.GetAttribute("mask"),
- Relation = reader.GetAttribute("relation").AsRelation(),
- Value = reader.GetAttribute("value")
- };
-
- dipSwitch.Conditions.Add(condition);
-
- reader.Read();
- break;
-
- case "diplocation":
- var dipLocation = new Location
- {
- Name = reader.GetAttribute("name"),
- Number = Utilities.CleanLong(reader.GetAttribute("number")),
- Inverted = reader.GetAttribute("inverted").AsYesNo()
- };
-
- dipSwitch.Locations.Add(dipLocation);
-
- reader.Read();
- break;
-
- case "dipvalue":
- var dipValue = new Setting
- {
- Name = reader.GetAttribute("name"),
- Value = reader.GetAttribute("value"),
- Default = reader.GetAttribute("default").AsYesNo()
- };
-
- // Now read the internal tags
- ReadDipValue(reader, dipValue);
-
- dipSwitch.Values.Add(dipValue);
-
- // Skip the dipvalue now that we've processed it
- reader.Read();
- break;
-
- default:
- reader.Read();
- break;
- }
- }
- }
-
- ///
- /// Read DipValue information
- ///
- /// XmlReader representing a diskarea block
- /// Setting to populate
- private void ReadDipValue(XmlReader reader, Setting dipValue)
- {
- // If we have an empty dipvalue, skip it
- if (reader == null)
- return;
-
- // Get list ready
- dipValue.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 dipvalue
- switch (reader.Name)
- {
- case "condition":
- var condition = new Condition
- {
- Tag = reader.GetAttribute("tag"),
- Mask = reader.GetAttribute("mask"),
- Relation = reader.GetAttribute("relation").AsRelation(),
- Value = reader.GetAttribute("value")
- };
-
- dipValue.Conditions.Add(condition);
-
- reader.Read();
- break;
-
- default:
- reader.Read();
- break;
- }
- }
- }
-
- ///
- /// Read Configuration information
- ///
- /// XmlReader representing a diskarea block
- /// Configuration to populate
- private void ReadConfiguration(XmlReader reader, Configuration configuration)
- {
- // If we have an empty configuration, skip it
- if (reader == null)
- return;
-
- // Get lists ready
- configuration.Conditions = new List();
- 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 "condition":
- var condition = new Condition
- {
- Tag = reader.GetAttribute("tag"),
- Mask = reader.GetAttribute("mask"),
- Relation = reader.GetAttribute("relation").AsRelation(),
- Value = reader.GetAttribute("value")
- };
-
- configuration.Conditions.Add(condition);
-
- reader.Read();
- break;
-
- case "conflocation":
- var confLocation = new Location
- {
- Name = reader.GetAttribute("name"),
- Number = Utilities.CleanLong(reader.GetAttribute("number")),
- Inverted = reader.GetAttribute("inverted").AsYesNo()
- };
-
- configuration.Locations.Add(confLocation);
-
- reader.Read();
- break;
-
- case "confsetting":
- var confSetting = new Setting
- {
- Name = reader.GetAttribute("name"),
- Value = reader.GetAttribute("value"),
- Default = reader.GetAttribute("default").AsYesNo()
- };
-
- // Now read the internal tags
- ReadConfSetting(reader, confSetting);
-
- configuration.Settings.Add(confSetting);
-
- // Skip the dipvalue now that we've processed it
- reader.Read();
- break;
-
- default:
- reader.Read();
- break;
- }
- }
- }
-
- ///
- /// Read ConfSetting information
- ///
- /// XmlReader representing a diskarea block
- /// Setting to populate
- private void ReadConfSetting(XmlReader reader, Setting confSetting)
- {
- // If we have an empty confsetting, skip it
- if (reader == null)
- return;
-
- // Get list ready
- confSetting.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 confsetting
- switch (reader.Name)
- {
- case "condition":
- var condition = new Condition
- {
- Tag = reader.GetAttribute("tag"),
- Mask = reader.GetAttribute("mask"),
- Relation = reader.GetAttribute("relation").AsRelation(),
- Value = reader.GetAttribute("value")
- };
-
- confSetting.Conditions.Add(condition);
-
- reader.Read();
- break;
-
- default:
- reader.Read();
- break;
- }
- }
- }
-
- ///
- /// Read Port information
- ///
- /// XmlReader representing a diskarea block
- /// ListXmlPort to populate
- private void ReadPort(XmlReader reader, Port 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 Analog
- {
- Mask = reader.GetAttribute("mask")
- };
-
- port.Analogs.Add(analog);
-
- reader.Read();
- break;
-
- default:
- reader.Read();
- break;
- }
- }
- }
-
- ///
- /// Read Adjuster information
- ///
- /// XmlReader representing a diskarea block
- /// Adjuster to populate
- private void ReadAdjuster(XmlReader reader, Adjuster 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 Condition
- {
- Tag = reader.GetAttribute("tag"),
- Mask = reader.GetAttribute("mask"),
- Relation = reader.GetAttribute("relation").AsRelation(),
- 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, Device 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 Instance
- {
- Name = reader.GetAttribute("name"),
- BriefName = reader.GetAttribute("briefname"),
- };
-
- device.Instances.Add(instance);
-
- reader.Read();
- break;
-
- case "extension":
- var extension = new Extension
- {
- Name = reader.GetAttribute("name"),
- };
-
- device.Extensions.Add(extension);
-
- reader.Read();
- break;
-
- default:
- reader.Read();
- break;
- }
- }
- }
-
- ///
- protected override ItemType[] GetSupportedTypes()
- {
- return new ItemType[]
- {
- ItemType.Adjuster,
- ItemType.BiosSet,
- ItemType.Chip,
- ItemType.Condition,
- ItemType.Configuration,
- ItemType.Device,
- ItemType.DeviceReference,
- ItemType.DipSwitch,
- ItemType.Disk,
- ItemType.Display,
- ItemType.Driver,
- ItemType.Feature,
- ItemType.Input,
- ItemType.Port,
- ItemType.RamOption,
- ItemType.Rom,
- ItemType.Sample,
- ItemType.Slot,
- ItemType.SoftwareList,
- ItemType.Sound,
- };
- }
-
- ///
- protected override List GetMissingRequiredFields(DatItem datItem)
- {
- // TODO: Check required fields
- return null;
- }
-
- ///
- public override bool WriteToFile(string outfile, bool ignoreblanks = false, bool throwOnError = false)
- {
- try
- {
- logger.User($"Writing to '{outfile}'...");
- FileStream fs = System.IO.File.Create(outfile);
-
- // If we get back null for some reason, just log and return
- if (fs == null)
- {
- logger.Warning($"File '{outfile}' could not be created for writing! Please check to see if the file is writable");
- return false;
- }
-
- XmlTextWriter xtw = new(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)
- {
- ConcurrentList datItems = Items.FilteredItems(key);
-
- // If this machine doesn't contain any writable items, skip
- if (!ContainsWritable(datItems))
- continue;
-
- // Resolve the names in the block
- datItems = DatItem.ResolveNames(datItems);
-
- for (int index = 0; index < datItems.Count; index++)
- {
- DatItem datItem = datItems[index];
-
- // 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() != datItem.Machine.Name.ToLowerInvariant())
- WriteEndGame(xtw);
-
- // If we have a new game, output the beginning of the new item
- if (lastgame == null || lastgame.ToLowerInvariant() != datItem.Machine.Name.ToLowerInvariant())
- WriteStartGame(xtw, datItem);
-
- // Check for a "null" item
- datItem = ProcessNullifiedItem(datItem);
-
- // Write out the item if we're not ignoring
- if (!ShouldIgnore(datItem, ignoreblanks))
- WriteDatItem(xtw, datItem);
-
- // Set the new data to compare against
- lastgame = datItem.Machine.Name;
- }
- }
-
- // Write the file footer out
- WriteFooter(xtw);
-
- logger.User($"'{outfile}' written!{Environment.NewLine}");
- xtw.Dispose();
- fs.Dispose();
- }
- catch (Exception ex) when (!throwOnError)
- {
- logger.Error(ex);
- return false;
- }
-
- return true;
- }
-
- ///
- /// Write out DAT header using the supplied StreamWriter
- ///
- /// XmlTextWriter to output to
- private void WriteHeader(XmlTextWriter xtw)
- {
- xtw.WriteStartDocument();
-
- xtw.WriteStartElement("mame");
- xtw.WriteRequiredAttributeString("build", Header.Name);
- xtw.WriteOptionalAttributeString("debug", Header.Debug.FromYesNo());
- xtw.WriteOptionalAttributeString("mameconfig", Header.MameConfig);
-
- xtw.Flush();
- }
-
- ///
- /// 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 void WriteStartGame(XmlTextWriter xtw, DatItem datItem)
- {
- // No game should start with a path separator
- datItem.Machine.Name = datItem.Machine.Name.TrimStart(Path.DirectorySeparatorChar);
-
- // Build the state
- xtw.WriteStartElement("machine");
- xtw.WriteRequiredAttributeString("name", datItem.Machine.Name);
- xtw.WriteOptionalAttributeString("sourcefile", datItem.Machine.SourceFile);
-
- 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");
-
- xtw.WriteOptionalAttributeString("runnable", datItem.Machine.Runnable.FromRunnable());
-
- if (!string.Equals(datItem.Machine.Name, datItem.Machine.CloneOf, StringComparison.OrdinalIgnoreCase))
- xtw.WriteOptionalAttributeString("cloneof", datItem.Machine.CloneOf);
- if (!string.Equals(datItem.Machine.Name, datItem.Machine.RomOf, StringComparison.OrdinalIgnoreCase))
- xtw.WriteOptionalAttributeString("romof", datItem.Machine.RomOf);
- if (!string.Equals(datItem.Machine.Name, datItem.Machine.SampleOf, StringComparison.OrdinalIgnoreCase))
- xtw.WriteOptionalAttributeString("sampleof", datItem.Machine.SampleOf);
-
- xtw.WriteOptionalElementString("description", datItem.Machine.Description);
- xtw.WriteOptionalElementString("year", datItem.Machine.Year);
- xtw.WriteOptionalElementString("manufacturer", datItem.Machine.Manufacturer);
- xtw.WriteOptionalElementString("history", datItem.Machine.History);
-
- xtw.Flush();
- }
-
- ///
- /// Write out Game start using the supplied StreamWriter
- ///
- /// XmlTextWriter to output to
- private void WriteEndGame(XmlTextWriter xtw)
- {
- // End machine
- xtw.WriteEndElement();
-
- xtw.Flush();
- }
-
- ///
- /// Write out DatItem using the supplied StreamWriter
- ///
- /// XmlTextWriter to output to
- /// DatItem object to be output
- private void WriteDatItem(XmlTextWriter xtw, DatItem datItem)
- {
- // Pre-process the item name
- ProcessItemName(datItem, true);
-
- // Build the state
- switch (datItem.ItemType)
- {
- case ItemType.Adjuster:
- var adjuster = datItem as Adjuster;
- xtw.WriteStartElement("adjuster");
- xtw.WriteRequiredAttributeString("name", adjuster.Name);
- xtw.WriteRequiredAttributeString("default", adjuster.Default.FromYesNo());
- if (adjuster.ConditionsSpecified)
- {
- foreach (var adjusterCondition in adjuster.Conditions)
- {
- xtw.WriteStartElement("condition");
- xtw.WriteRequiredAttributeString("tag", adjusterCondition.Tag);
- xtw.WriteRequiredAttributeString("mask", adjusterCondition.Mask);
- xtw.WriteRequiredAttributeString("relation", adjusterCondition.Relation.FromRelation());
- xtw.WriteRequiredAttributeString("value", adjusterCondition.Value);
- xtw.WriteEndElement();
- }
- }
- xtw.WriteEndElement();
- break;
-
- case ItemType.BiosSet:
- var biosSet = datItem as BiosSet;
- xtw.WriteStartElement("biosset");
- xtw.WriteRequiredAttributeString("name", biosSet.Name);
- xtw.WriteRequiredAttributeString("description", biosSet.Description);
- xtw.WriteOptionalAttributeString("default", biosSet.Default?.ToString().ToLowerInvariant());
- xtw.WriteEndElement();
- break;
-
- case ItemType.Chip:
- var chip = datItem as Chip;
- xtw.WriteStartElement("chip");
- xtw.WriteRequiredAttributeString("name", chip.Name);
- xtw.WriteOptionalAttributeString("tag", chip.Tag);
- xtw.WriteRequiredAttributeString("type", chip.ChipType.FromChipType());
- xtw.WriteOptionalAttributeString("clock", chip.Clock?.ToString());
- xtw.WriteEndElement();
- break;
-
- case ItemType.Condition:
- var condition = datItem as Condition;
- xtw.WriteStartElement("condition");
- xtw.WriteRequiredAttributeString("tag", condition.Tag);
- xtw.WriteRequiredAttributeString("mask", condition.Mask);
- xtw.WriteRequiredAttributeString("relation", condition.Relation.FromRelation());
- xtw.WriteRequiredAttributeString("value", condition.Value);
- xtw.WriteEndElement();
- break;
-
- case ItemType.Configuration:
- var configuration = datItem as Configuration;
- xtw.WriteStartElement("configuration");
- xtw.WriteRequiredAttributeString("name", configuration.Name);
- xtw.WriteRequiredAttributeString("tag", configuration.Tag);
- xtw.WriteRequiredAttributeString("mask", configuration.Mask);
-
- if (configuration.ConditionsSpecified)
- {
- foreach (var configurationCondition in configuration.Conditions)
- {
- xtw.WriteStartElement("condition");
- xtw.WriteRequiredAttributeString("tag", configurationCondition.Tag);
- xtw.WriteRequiredAttributeString("mask", configurationCondition.Mask);
- xtw.WriteRequiredAttributeString("relation", configurationCondition.Relation.FromRelation());
- xtw.WriteRequiredAttributeString("value", configurationCondition.Value);
- xtw.WriteEndElement();
- }
- }
- if (configuration.LocationsSpecified)
- {
- foreach (var location in configuration.Locations)
- {
- xtw.WriteStartElement("conflocation");
- xtw.WriteRequiredAttributeString("name", location.Name);
- xtw.WriteRequiredAttributeString("number", location.Number?.ToString());
- xtw.WriteOptionalAttributeString("inverted", location.Inverted.FromYesNo());
- xtw.WriteEndElement();
- }
- }
- if (configuration.SettingsSpecified)
- {
- foreach (var setting in configuration.Settings)
- {
- xtw.WriteStartElement("confsetting");
- xtw.WriteRequiredAttributeString("name", setting.Name);
- xtw.WriteRequiredAttributeString("value", setting.Value);
- xtw.WriteOptionalAttributeString("default", setting.Default.FromYesNo());
- if (setting.ConditionsSpecified)
- {
- foreach (var confsettingCondition in setting.Conditions)
- {
- xtw.WriteStartElement("condition");
- xtw.WriteRequiredAttributeString("tag", confsettingCondition.Tag);
- xtw.WriteRequiredAttributeString("mask", confsettingCondition.Mask);
- xtw.WriteRequiredAttributeString("relation", confsettingCondition.Relation.FromRelation());
- xtw.WriteRequiredAttributeString("value", confsettingCondition.Value);
- xtw.WriteEndElement();
- }
- }
- xtw.WriteEndElement();
- }
- }
- xtw.WriteEndElement();
- break;
-
- case ItemType.Device:
- var device = datItem as Device;
- xtw.WriteStartElement("device");
- xtw.WriteRequiredAttributeString("type", device.DeviceType.FromDeviceType());
- xtw.WriteOptionalAttributeString("tag", device.Tag);
- xtw.WriteOptionalAttributeString("fixed_image", device.FixedImage);
- xtw.WriteOptionalAttributeString("mandatory", device.Mandatory?.ToString());
- xtw.WriteOptionalAttributeString("interface", device.Interface);
- if (device.InstancesSpecified)
- {
- foreach (var instance in device.Instances)
- {
- xtw.WriteStartElement("instance");
- xtw.WriteRequiredAttributeString("name", instance.Name);
- xtw.WriteRequiredAttributeString("briefname", instance.BriefName);
- xtw.WriteEndElement();
- }
- }
- if (device.ExtensionsSpecified)
- {
- foreach (var extension in device.Extensions)
- {
- xtw.WriteStartElement("extension");
- xtw.WriteRequiredAttributeString("name", extension.Name);
- xtw.WriteEndElement();
- }
- }
- xtw.WriteEndElement();
- break;
-
- case ItemType.DeviceReference:
- var deviceRef = datItem as DeviceReference;
- xtw.WriteStartElement("device_ref");
- xtw.WriteRequiredAttributeString("name", deviceRef.Name);
- xtw.WriteEndElement();
- break;
-
- case ItemType.DipSwitch:
- var dipSwitch = datItem as DipSwitch;
- xtw.WriteStartElement("dipswitch");
- xtw.WriteRequiredAttributeString("name", dipSwitch.Name);
- xtw.WriteRequiredAttributeString("tag", dipSwitch.Tag);
- xtw.WriteRequiredAttributeString("mask", dipSwitch.Mask);
- if (dipSwitch.ConditionsSpecified)
- {
- foreach (var dipSwitchCondition in dipSwitch.Conditions)
- {
- xtw.WriteStartElement("condition");
- xtw.WriteRequiredAttributeString("tag", dipSwitchCondition.Tag);
- xtw.WriteRequiredAttributeString("mask", dipSwitchCondition.Mask);
- xtw.WriteRequiredAttributeString("relation", dipSwitchCondition.Relation.FromRelation());
- xtw.WriteRequiredAttributeString("value", dipSwitchCondition.Value);
- xtw.WriteEndElement();
- }
- }
- if (dipSwitch.LocationsSpecified)
- {
- foreach (var location in dipSwitch.Locations)
- {
- xtw.WriteStartElement("diplocation");
- xtw.WriteRequiredAttributeString("name", location.Name);
- xtw.WriteRequiredAttributeString("number", location.Number?.ToString());
- xtw.WriteOptionalAttributeString("inverted", location.Inverted.FromYesNo());
- xtw.WriteEndElement();
- }
- }
- if (dipSwitch.ValuesSpecified)
- {
- foreach (var value in dipSwitch.Values)
- {
- xtw.WriteStartElement("dipvalue");
- xtw.WriteRequiredAttributeString("name", value.Name);
- xtw.WriteRequiredAttributeString("value", value.Value);
- xtw.WriteOptionalAttributeString("default", value.Default.FromYesNo());
- if (value.ConditionsSpecified)
- {
- foreach (var dipValueCondition in value.Conditions)
- {
- xtw.WriteStartElement("condition");
- xtw.WriteRequiredAttributeString("tag", dipValueCondition.Tag);
- xtw.WriteRequiredAttributeString("mask", dipValueCondition.Mask);
- xtw.WriteRequiredAttributeString("relation", dipValueCondition.Relation.FromRelation());
- xtw.WriteRequiredAttributeString("value", dipValueCondition.Value);
- xtw.WriteEndElement();
- }
- }
- xtw.WriteEndElement();
- }
- }
- xtw.WriteEndElement();
- break;
-
- case ItemType.Disk:
- var disk = datItem as Disk;
- xtw.WriteStartElement("disk");
- xtw.WriteRequiredAttributeString("name", disk.Name);
- xtw.WriteOptionalAttributeString("sha1", disk.SHA1?.ToLowerInvariant());
- xtw.WriteOptionalAttributeString("merge", disk.MergeTag);
- xtw.WriteOptionalAttributeString("region", disk.Region);
- xtw.WriteOptionalAttributeString("index", disk.Index);
- xtw.WriteOptionalAttributeString("writable", disk.Writable.FromYesNo());
- if (disk.ItemStatus != ItemStatus.None)
- xtw.WriteOptionalAttributeString("status", disk.ItemStatus.FromItemStatus(false));
- xtw.WriteOptionalAttributeString("optional", disk.Optional.FromYesNo());
- xtw.WriteEndElement();
- break;
-
- case ItemType.Display:
- var display = datItem as Display;
- xtw.WriteStartElement("display");
- xtw.WriteOptionalAttributeString("tag", display.Tag);
- xtw.WriteRequiredAttributeString("type", display.DisplayType.FromDisplayType());
- xtw.WriteOptionalAttributeString("rotate", display.Rotate?.ToString());
- xtw.WriteOptionalAttributeString("flipx", display.FlipX.FromYesNo());
- xtw.WriteOptionalAttributeString("width", display.Width?.ToString());
- xtw.WriteOptionalAttributeString("height", display.Height?.ToString());
- xtw.WriteRequiredAttributeString("refresh", display.Refresh?.ToString("N6"));
- xtw.WriteOptionalAttributeString("pixclock", display.PixClock?.ToString());
- xtw.WriteOptionalAttributeString("htotal", display.HTotal?.ToString());
- xtw.WriteOptionalAttributeString("hbend", display.HBEnd?.ToString());
- xtw.WriteOptionalAttributeString("hstart", display.HBStart?.ToString());
- xtw.WriteOptionalAttributeString("vtotal", display.VTotal?.ToString());
- xtw.WriteOptionalAttributeString("vbend", display.VBEnd?.ToString());
- xtw.WriteOptionalAttributeString("vbstart", display.VBStart?.ToString());
- xtw.WriteEndElement();
- break;
-
- case ItemType.Driver:
- var driver = datItem as Driver;
- xtw.WriteStartElement("driver");
- xtw.WriteRequiredAttributeString("status", driver.Status.FromSupportStatus());
- xtw.WriteRequiredAttributeString("emulation", driver.Emulation.FromSupportStatus());
- xtw.WriteOptionalAttributeString("cocktail", driver.Cocktail.FromSupportStatus());
- xtw.WriteRequiredAttributeString("savestate", driver.SaveState.FromSupported(true));
- xtw.WriteOptionalAttributeString("requiresartwork", driver.RequiresArtwork.FromYesNo());
- xtw.WriteOptionalAttributeString("unofficial", driver.Unofficial.FromYesNo());
- xtw.WriteOptionalAttributeString("nosoundhardware", driver.NoSoundHardware.FromYesNo());
- xtw.WriteOptionalAttributeString("incomplete", driver.Incomplete.FromYesNo());
- xtw.WriteEndElement();
- break;
-
- case ItemType.Feature:
- var feature = datItem as Feature;
- xtw.WriteStartElement("feature");
- xtw.WriteRequiredAttributeString("type", feature.Type.FromFeatureType());
- xtw.WriteOptionalAttributeString("status", feature.Status.FromFeatureStatus());
- xtw.WriteOptionalAttributeString("overall", feature.Overall.FromFeatureStatus());
- xtw.WriteEndElement();
- break;
-
- case ItemType.Input:
- var input = datItem as Input;
- xtw.WriteStartElement("input");
- xtw.WriteOptionalAttributeString("service", input.Service.FromYesNo());
- xtw.WriteOptionalAttributeString("tilt", input.Tilt.FromYesNo());
- xtw.WriteRequiredAttributeString("players", input.Players?.ToString());
- xtw.WriteOptionalAttributeString("coins", input.Coins?.ToString());
- if (input.ControlsSpecified)
- {
- foreach (var control in input.Controls)
- {
- xtw.WriteStartElement("control");
- xtw.WriteRequiredAttributeString("type", control.ControlType.FromControlType());
- xtw.WriteOptionalAttributeString("player", control.Player?.ToString());
- xtw.WriteOptionalAttributeString("buttons", control.Buttons?.ToString());
- xtw.WriteOptionalAttributeString("reqbuttons", control.RequiredButtons?.ToString());
- xtw.WriteOptionalAttributeString("minimum", control.Minimum?.ToString());
- xtw.WriteOptionalAttributeString("maximum", control.Maximum?.ToString());
- xtw.WriteOptionalAttributeString("sensitivity", control.Sensitivity?.ToString());
- xtw.WriteOptionalAttributeString("keydelta", control.KeyDelta?.ToString());
- xtw.WriteOptionalAttributeString("reverse", control.Reverse.FromYesNo());
- xtw.WriteOptionalAttributeString("ways", control.Ways);
- xtw.WriteOptionalAttributeString("ways2", control.Ways2);
- xtw.WriteOptionalAttributeString("ways3", control.Ways3);
- xtw.WriteEndElement();
- }
- }
- xtw.WriteEndElement();
- break;
-
- case ItemType.Port:
- var port = datItem as Port;
- xtw.WriteStartElement("port");
- xtw.WriteRequiredAttributeString("tag", port.Tag);
- if (port.AnalogsSpecified)
- {
- foreach (var analog in port.Analogs)
- {
- xtw.WriteStartElement("analog");
- xtw.WriteRequiredAttributeString("mask", analog.Mask);
- xtw.WriteEndElement();
- }
- }
- xtw.WriteEndElement();
- break;
-
- case ItemType.RamOption:
- var ramOption = datItem as RamOption;
- xtw.WriteStartElement("ramoption");
- xtw.WriteRequiredAttributeString("name", ramOption.Name);
- xtw.WriteOptionalAttributeString("default", ramOption.Default.FromYesNo());
- xtw.WriteRaw(ramOption.Content ?? string.Empty);
- xtw.WriteFullEndElement();
- break;
-
- case ItemType.Rom:
- var rom = datItem as Rom;
- xtw.WriteStartElement("rom");
- xtw.WriteRequiredAttributeString("name", rom.Name);
- xtw.WriteOptionalAttributeString("bios", rom.Bios);
- xtw.WriteRequiredAttributeString("size", rom.Size?.ToString());
- xtw.WriteOptionalAttributeString("crc", rom.CRC?.ToLowerInvariant());
- xtw.WriteOptionalAttributeString("sha1", rom.SHA1?.ToLowerInvariant());
- xtw.WriteOptionalAttributeString("merge", rom.MergeTag);
- xtw.WriteOptionalAttributeString("region", rom.Region);
- xtw.WriteOptionalAttributeString("offset", rom.Offset);
- if (rom.ItemStatus != ItemStatus.None)
- xtw.WriteOptionalAttributeString("status", rom.ItemStatus.FromItemStatus(false));
- xtw.WriteOptionalAttributeString("optional", rom.Optional.FromYesNo());
- xtw.WriteEndElement();
- break;
-
- case ItemType.Sample:
- var sample = datItem as Sample;
- xtw.WriteStartElement("sample");
- xtw.WriteRequiredAttributeString("name", sample.Name);
- xtw.WriteEndElement();
- break;
-
- case ItemType.Slot:
- var slot = datItem as Slot;
- xtw.WriteStartElement("slot");
- xtw.WriteRequiredAttributeString("name", slot.Name);
- if (slot.SlotOptionsSpecified)
- {
- foreach (var slotOption in slot.SlotOptions)
- {
- xtw.WriteStartElement("slotoption");
- xtw.WriteRequiredAttributeString("name", slotOption.Name);
- xtw.WriteRequiredAttributeString("devname", slotOption.DeviceName);
- xtw.WriteOptionalAttributeString("default", slotOption.Default.FromYesNo());
- xtw.WriteEndElement();
- }
- }
- xtw.WriteEndElement();
- break;
-
- case ItemType.SoftwareList:
- var softwareList = datItem as DatItems.Formats.SoftwareList;
- xtw.WriteStartElement("softwarelist");
- xtw.WriteRequiredAttributeString("tag", softwareList.Tag);
- xtw.WriteRequiredAttributeString("name", softwareList.Name);
- if (softwareList.Status != SoftwareListStatus.None)
- xtw.WriteRequiredAttributeString("status", softwareList.Status.FromSoftwareListStatus());
- xtw.WriteOptionalAttributeString("filter", softwareList.Filter);
- xtw.WriteEndElement();
- break;
-
- case ItemType.Sound:
- var sound = datItem as Sound;
- xtw.WriteStartElement("sound");
- xtw.WriteRequiredAttributeString("channels", sound.Channels?.ToString());
- xtw.WriteEndElement();
- break;
- }
-
- xtw.Flush();
- }
-
- ///
- /// Write out DAT footer using the supplied StreamWriter
- ///
- /// XmlTextWriter to output to
- private void WriteFooter(XmlTextWriter xtw)
- {
- // End machine
- xtw.WriteEndElement();
-
- // End mame
- xtw.WriteEndElement();
-
- xtw.Flush();
- }
}
}