using System; using System.Collections.Generic; using System.IO; using System.Xml; using SabreTools.Data.Extensions; using SabreTools.Data.Models.SoftwareList; using SabreTools.Text.Extensions; namespace SabreTools.Serialization.Readers { public class SoftwareList : BaseBinaryReader { /// public override Data.Models.SoftwareList.SoftwareList? Deserialize(Stream? data) { // If the data is invalid if (data is null || !data.CanRead) return null; try { // Cache the current offset long initialOffset = data.Position; // Create the XmlTextReader var reader = new XmlTextReader(data); reader.WhitespaceHandling = WhitespaceHandling.None; // Parse the XML, if possible Data.Models.SoftwareList.SoftwareList? softwareList = null; while (reader.Read()) { // Comments have to be skipped if (reader.NodeType == XmlNodeType.Comment) continue; // An ending element means exit if (reader.NodeType == XmlNodeType.EndElement) break; // Only process starting elements if (!reader.IsStartElement()) continue; switch (reader.Name) { case "softwarelist": if (softwareList is not null && Debug) Console.WriteLine($"'{reader.Name}' element already found, overwriting"); softwareList = ParseSoftwareList(reader); break; default: if (Debug) Console.Error.WriteLine($"Element '{reader.Name}' is not recognized"); break; } } return softwareList; } catch { // Ignore the actual error return null; } } /// /// Parse from an XmlTextReader into a SoftwareList /// /// XmlTextReader to read from /// Filled SoftwareList on success, null on error public Data.Models.SoftwareList.SoftwareList ParseSoftwareList(XmlTextReader reader) { var obj = new Data.Models.SoftwareList.SoftwareList(); obj.Name = reader.GetAttribute("name"); obj.Description = reader.GetAttribute("description"); // Handle empty elements if (reader.IsEmptyElement) return obj; List softwares = []; reader.Read(); while (!reader.EOF) { // Comments have to be skipped if (reader.NodeType == XmlNodeType.Comment) { reader.Skip(); continue; } // An ending element means exit if (reader.NodeType == XmlNodeType.EndElement) break; // Only process starting elements if (!reader.IsStartElement()) { reader.Skip(); continue; } switch (reader.Name) { case "notes": if (obj.Notes is not null && Debug) Console.WriteLine($"'{reader.Name}' element already found, overwriting"); obj.Notes = reader.ReadElementContentAsString(); break; case "software": var software = ParseSoftware(reader); if (software is not null) softwares.Add(software); reader.Skip(); break; default: if (Debug) Console.Error.WriteLine($"Element '{reader.Name}' is not recognized"); reader.Skip(); break; } } if (softwares.Count > 0) obj.Software = [.. softwares]; return obj; } /// /// Parse from an XmlTextReader into a DataArea /// /// XmlTextReader to read from /// Filled DataArea on success, null on error public DataArea ParseDataArea(XmlTextReader reader) { var obj = new DataArea(); obj.Name = reader.GetAttribute("name"); obj.Size = NumberHelper.ConvertToInt64(reader.GetAttribute("size")); obj.Width = reader.GetAttribute("width").AsWidth(); obj.Endianness = reader.GetAttribute("endianness").AsEndianness(); // Handle empty elements if (reader.IsEmptyElement) return obj; List roms = []; reader.Read(); while (!reader.EOF) { // Comments have to be skipped if (reader.NodeType == XmlNodeType.Comment) { reader.Skip(); continue; } // An ending element means exit if (reader.NodeType == XmlNodeType.EndElement) break; // Only process starting elements if (!reader.IsStartElement()) { reader.Skip(); continue; } switch (reader.Name) { case "rom": var rom = ParseRom(reader); if (rom is not null) roms.Add(rom); reader.Skip(); break; default: if (Debug) Console.Error.WriteLine($"Element '{reader.Name}' is not recognized"); reader.Skip(); break; } } if (roms.Count > 0) obj.Rom = [.. roms]; return obj; } /// /// Parse from an XmlTextReader into a DipSwitch /// /// XmlTextReader to read from /// Filled DipSwitch on success, null on error public DipSwitch ParseDipSwitch(XmlTextReader reader) { var obj = new DipSwitch(); obj.Name = reader.GetAttribute("name"); obj.Tag = reader.GetAttribute("tag"); obj.Mask = reader.GetAttribute("mask"); // Handle empty elements if (reader.IsEmptyElement) return obj; List dipValues = []; reader.Read(); while (!reader.EOF) { // Comments have to be skipped if (reader.NodeType == XmlNodeType.Comment) { reader.Skip(); continue; } // An ending element means exit if (reader.NodeType == XmlNodeType.EndElement) break; // Only process starting elements if (!reader.IsStartElement()) { reader.Skip(); continue; } switch (reader.Name) { case "dipvalue": var dipValue = ParseDipValue(reader); if (dipValue is not null) dipValues.Add(dipValue); reader.Skip(); break; default: if (Debug) Console.Error.WriteLine($"Element '{reader.Name}' is not recognized"); reader.Skip(); break; } } if (dipValues.Count > 0) obj.DipValue = [.. dipValues]; return obj; } /// /// Parse from an XmlTextReader into a DipValue /// /// XmlTextReader to read from /// Filled DipValue on success, null on error public DipValue ParseDipValue(XmlTextReader reader) { var obj = new DipValue(); obj.Name = reader.GetAttribute("name"); obj.Value = reader.GetAttribute("value"); obj.Default = reader.GetAttribute("default").AsYesNo(); return obj; } /// /// Parse from an XmlTextReader into a Disk /// /// XmlTextReader to read from /// Filled Disk on success, null on error public Disk ParseDisk(XmlTextReader reader) { var obj = new Disk(); obj.Name = reader.GetAttribute("name"); obj.MD5 = reader.GetAttribute("md5"); obj.SHA1 = reader.GetAttribute("sha1"); obj.Status = reader.GetAttribute("status").AsItemStatus(); obj.Writeable = reader.GetAttribute("writable").AsYesNo(); return obj; } /// /// Parse from an XmlTextReader into a DiskArea /// /// XmlTextReader to read from /// Filled DiskArea on success, null on error public DiskArea ParseDiskArea(XmlTextReader reader) { var obj = new DiskArea(); obj.Name = reader.GetAttribute("name"); // Handle empty elements if (reader.IsEmptyElement) return obj; List disks = []; reader.Read(); while (!reader.EOF) { // Comments have to be skipped if (reader.NodeType == XmlNodeType.Comment) { reader.Skip(); continue; } // An ending element means exit if (reader.NodeType == XmlNodeType.EndElement) break; // Only process starting elements if (!reader.IsStartElement()) { reader.Skip(); continue; } switch (reader.Name) { case "disk": var disk = ParseDisk(reader); if (disk is not null) disks.Add(disk); reader.Skip(); break; default: if (Debug) Console.Error.WriteLine($"Element '{reader.Name}' is not recognized"); reader.Skip(); break; } } if (disks.Count > 0) obj.Disk = [.. disks]; return obj; } /// /// Parse from an XmlTextReader into a Feature /// /// XmlTextReader to read from /// Filled Feature on success, null on error public Feature ParseFeature(XmlTextReader reader) { var obj = new Feature(); obj.Name = reader.GetAttribute("name"); obj.Value = reader.GetAttribute("value"); return obj; } /// /// Parse from an XmlTextReader into a Info /// /// XmlTextReader to read from /// Filled Info on success, null on error public Info ParseInfo(XmlTextReader reader) { var obj = new Info(); obj.Name = reader.GetAttribute("name"); obj.Value = reader.GetAttribute("value"); return obj; } /// /// Parse from an XmlTextReader into a Part /// /// XmlTextReader to read from /// Filled Part on success, null on error public Part ParsePart(XmlTextReader reader) { var obj = new Part(); obj.Name = reader.GetAttribute("name"); obj.Interface = reader.GetAttribute("interface"); // Handle empty elements if (reader.IsEmptyElement) return obj; List features = []; List dataAreas = []; List diskAreas = []; List dipSwitches = []; reader.Read(); while (!reader.EOF) { // Comments have to be skipped if (reader.NodeType == XmlNodeType.Comment) { reader.Skip(); continue; } // An ending element means exit if (reader.NodeType == XmlNodeType.EndElement) break; // Only process starting elements if (!reader.IsStartElement()) { reader.Skip(); continue; } switch (reader.Name) { case "feature": var feature = ParseFeature(reader); if (feature is not null) features.Add(feature); reader.Skip(); break; case "dataarea": var dataArea = ParseDataArea(reader); if (dataArea is not null) dataAreas.Add(dataArea); reader.Skip(); break; case "diskarea": var diskArea = ParseDiskArea(reader); if (diskArea is not null) diskAreas.Add(diskArea); reader.Skip(); break; case "dipswitch": var dipSwitch = ParseDipSwitch(reader); if (dipSwitch is not null) dipSwitches.Add(dipSwitch); reader.Skip(); break; default: if (Debug) Console.Error.WriteLine($"Element '{reader.Name}' is not recognized"); reader.Skip(); break; } } if (features.Count > 0) obj.Feature = [.. features]; if (dataAreas.Count > 0) obj.DataArea = [.. dataAreas]; if (diskAreas.Count > 0) obj.DiskArea = [.. diskAreas]; if (dipSwitches.Count > 0) obj.DipSwitch = [.. dipSwitches]; return obj; } /// /// Parse from an XmlTextReader into a Rom /// /// XmlTextReader to read from /// Filled Rom on success, null on error public Rom ParseRom(XmlTextReader reader) { var obj = new Rom(); obj.Name = reader.GetAttribute("name"); obj.Size = NumberHelper.ConvertToInt64(reader.GetAttribute("size")); obj.Length = reader.GetAttribute("length"); obj.CRC = reader.GetAttribute("crc"); obj.SHA1 = reader.GetAttribute("sha1"); obj.Offset = reader.GetAttribute("offset"); obj.Value = reader.GetAttribute("value"); obj.Status = reader.GetAttribute("status").AsItemStatus(); obj.LoadFlag = reader.GetAttribute("loadflag").AsLoadFlag(); return obj; } /// /// Parse from an XmlTextReader into a SharedFeat /// /// XmlTextReader to read from /// Filled SharedFeat on success, null on error public SharedFeat ParseSharedFeat(XmlTextReader reader) { var obj = new SharedFeat(); obj.Name = reader.GetAttribute("name"); obj.Value = reader.GetAttribute("value"); return obj; } /// /// Parse from an XmlTextReader into a Software /// /// XmlTextReader to read from /// Filled Software on success, null on error public Software ParseSoftware(XmlTextReader reader) { var obj = new Software(); obj.Name = reader.GetAttribute("name"); obj.CloneOf = reader.GetAttribute("cloneof"); obj.Supported = reader.GetAttribute("supported")?.AsSupported(); // Handle empty elements if (reader.IsEmptyElement) return obj; List infos = []; List sharedFeats = []; List parts = []; reader.Read(); while (!reader.EOF) { // Comments have to be skipped if (reader.NodeType == XmlNodeType.Comment) { reader.Skip(); continue; } // An ending element means exit if (reader.NodeType == XmlNodeType.EndElement) break; // Only process starting elements if (!reader.IsStartElement()) { reader.Skip(); continue; } switch (reader.Name) { case "description": if (obj.Description is not null && Debug) Console.WriteLine($"'{reader.Name}' element already found, overwriting"); obj.Description = reader.ReadElementContentAsString(); break; case "year": if (obj.Year is not null && Debug) Console.WriteLine($"'{reader.Name}' element already found, overwriting"); obj.Year = reader.ReadElementContentAsString(); break; case "publisher": if (obj.Publisher is not null && Debug) Console.WriteLine($"'{reader.Name}' element already found, overwriting"); obj.Publisher = reader.ReadElementContentAsString(); break; case "notes": if (obj.Notes is not null && Debug) Console.WriteLine($"'{reader.Name}' element already found, overwriting"); obj.Notes = reader.ReadElementContentAsString(); break; case "info": var info = ParseInfo(reader); if (info is not null) infos.Add(info); reader.Skip(); break; case "sharedfeat": var sharedFeat = ParseSharedFeat(reader); if (sharedFeat is not null) sharedFeats.Add(sharedFeat); reader.Skip(); break; case "part": var part = ParsePart(reader); if (part is not null) parts.Add(part); reader.Skip(); break; default: if (Debug) Console.Error.WriteLine($"Element '{reader.Name}' is not recognized"); reader.Skip(); break; } } if (infos.Count > 0) obj.Info = [.. infos]; if (sharedFeats.Count > 0) obj.SharedFeat = [.. sharedFeats]; if (parts.Count > 0) obj.Part = [.. parts]; return obj; } } }