diff --git a/SabreTools.DatFiles/Formats/DosCenter.Reader.cs b/SabreTools.DatFiles/Formats/DosCenter.Reader.cs index 2ef61262..f6abf864 100644 --- a/SabreTools.DatFiles/Formats/DosCenter.Reader.cs +++ b/SabreTools.DatFiles/Formats/DosCenter.Reader.cs @@ -54,7 +54,7 @@ namespace SabreTools.DatFiles.Formats Header.Comment ??= doscenter.Comment; // Handle implied SuperDAT - if (doscenter.Name.Contains(" - SuperDAT") && keep) + if (doscenter.Name?.Contains(" - SuperDAT") == true && keep) Header.Type ??= "SuperDAT"; } diff --git a/SabreTools.DatFiles/Formats/OpenMSX.Reader.cs b/SabreTools.DatFiles/Formats/OpenMSX.Reader.cs index c781d187..df9b7dcc 100644 --- a/SabreTools.DatFiles/Formats/OpenMSX.Reader.cs +++ b/SabreTools.DatFiles/Formats/OpenMSX.Reader.cs @@ -1,9 +1,6 @@ using System; -using System.Collections.Generic; -using System.Xml; -using System.Xml.Schema; +using System.Linq; using SabreTools.Core; -using SabreTools.Core.Tools; using SabreTools.DatItems; using SabreTools.DatItems.Formats; @@ -17,144 +14,93 @@ namespace SabreTools.DatFiles.Formats /// 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; - } + // Deserialize the input file + var softwareDb = Serialization.OpenMSX.Deserialize(filename); - switch (xtr.Name) - { - case "softwaredb": - Header.Name ??= "openMSX Software List"; - Header.Description ??= Header.Name; - Header.Date ??= xtr.GetAttribute("timestamp"); - xtr.Read(); - break; + // Convert the header to the internal format + ConvertHeader(softwareDb); - // We want to process the entire subtree of the software - case "software": - ReadSoftware(xtr.ReadSubtree(), statsOnly, filename, indexId); - - // Skip the software now that we've processed it - xtr.Skip(); - break; - - default: - xtr.Read(); - break; - } - } + // Convert the software data to the internal format + ConvertSoftwares(softwareDb?.Software, filename, indexId, statsOnly); } catch (Exception ex) when (!throwOnError) { - logger.Warning(ex, $"Exception found while parsing '{filename}'"); - - // For XML errors, just skip the affected node - xtr?.Read(); + string message = $"'{filename}' - An error occurred during parsing"; + logger.Error(ex, message); } + } - xtr.Dispose(); + #region Converters + + /// + /// Convert header information + /// + /// Deserialized model to convert + private void ConvertHeader(Models.OpenMSX.SoftwareDb? datafile) + { + // If the datafile is missing, we can't do anything + if (datafile == null) + return; + + Header.Name ??= "openMSX Software List"; + Header.Description ??= Header.Name; + Header.Date ??= datafile.Timestamp; } /// - /// Read software information + /// Convert softwares information /// - /// XmlReader representing a machine block - /// True to only add item statistics while parsing, false otherwise + /// Array of deserialized models to convert /// Name of the file to be parsed /// Index ID for the DAT - private void ReadSoftware(XmlReader reader, bool statsOnly, string filename, int indexId) + /// True to only add item statistics while parsing, false otherwise + private void ConvertSoftwares(Models.OpenMSX.Software[]? softwares, string filename, int indexId, bool statsOnly) { - // If we have an empty machine, skip it - if (reader == null) + // If the software array is missing, we can't do anything + if (softwares == null || !softwares.Any()) return; - // Otherwise, add what is possible - reader.MoveToContent(); - - int diskno = 0; - bool containsItems = false; - - // Create a new machine - Machine machine = new(); - - while (!reader.EOF) + // Loop through the software and add + foreach (var software in softwares) { - // We only want elements - if (reader.NodeType != XmlNodeType.Element) - { - reader.Read(); - continue; - } - - // Get the roms from the machine - switch (reader.Name) - { - case "title": - machine.Name = reader.ReadElementContentAsString(); - break; - - case "genmsxid": - machine.GenMSXID = reader.ReadElementContentAsString(); - break; - - case "system": - machine.System = reader.ReadElementContentAsString(); - break; - - case "company": - machine.Manufacturer = reader.ReadElementContentAsString(); - break; - - case "year": - machine.Year = reader.ReadElementContentAsString(); - break; - - case "country": - machine.Country = reader.ReadElementContentAsString(); - break; - - case "dump": - containsItems = ReadDump(reader.ReadSubtree(), machine, diskno, statsOnly, filename, indexId); - diskno++; - - // Skip the dump now that we've processed it - reader.Skip(); - break; - - default: - reader.Read(); - break; - } + ConvertSoftware(software, filename, indexId, statsOnly); } + } - // If no items were found for this machine, add a Blank placeholder + /// + /// Convert software information + /// + /// Deserialized model to convert + /// Name of the file to be parsed + /// Index ID for the DAT + /// True to only add item statistics while parsing, false otherwise + private void ConvertSoftware(Models.OpenMSX.Software software, string filename, int indexId, bool statsOnly, string dirname = null) + { + // If the software is missing, we can't do anything + if (software == null) + return; + + // Create the machine for copying information + var machine = new Machine + { + Name = software.Title, + GenMSXID = software.GenMSXID, + System = software.System, + Manufacturer = software.Company, + Year = software.Year, + Country = software.Country, + }; + + // Check if there are any items + bool containsItems = false; + ConvertDumps(software.Dump, machine, filename, indexId, statsOnly, ref containsItems); + + // If we had no items, create a Blank placeholder if (!containsItems) { - Blank blank = new() + var blank = new Blank { Source = new Source { @@ -164,342 +110,78 @@ namespace SabreTools.DatFiles.Formats }; blank.CopyMachineInformation(machine); - - // Now process and add the rom ParseAddHelper(blank, statsOnly); } } /// - /// Read dump information + /// Convert Dump information /// - /// XmlReader representing a part block - /// Machine information to pass to contained items - /// Disk number to use when outputting to other DAT formats - /// True to only add item statistics while parsing, false otherwise + /// Array of deserialized models to convert + /// Prefilled machine to use /// Name of the file to be parsed /// Index ID for the DAT - private bool ReadDump( - XmlReader reader, - Machine machine, - int diskno, - bool statsOnly, - - // Standard Dat parsing - string filename, - int indexId) + /// True to only add item statistics while parsing, false otherwise + /// True if there were any items in the array, false otherwise + private void ConvertDumps(Models.OpenMSX.Dump[]? dumps, Machine machine, string filename, int indexId, bool statsOnly, ref bool containsItems) { - List items = new(); - Original original = null; + // If the dumps array is missing, we can't do anything + if (dumps == null || !dumps.Any()) + return; - while (!reader.EOF) + containsItems = true; + int index = 0; + foreach (var dump in dumps) { - // We only want elements - if (reader.NodeType != XmlNodeType.Element) - { - reader.Read(); + // If we don't have rom data, we can't do anything + if (dump?.Rom == null) continue; + + var rom = dump.Rom; + + string name = $"{machine.Name}_{index++}{(!string.IsNullOrWhiteSpace(rom.Remark) ? $" {rom.Remark}" : string.Empty)}"; + var item = new Rom + { + Name = name, + Offset = dump.Rom?.Start, + OpenMSXType = rom.Type, + SHA1 = rom.Hash, + Remark = rom.Remark, + + Source = new Source + { + Index = indexId, + Name = filename, + }, + }; + + if (dump.Original != null) + { + item.Original = new Original + { + Value = dump.Original.Value, + Content = dump.Original.Content, + }; } - // Get the elements from the dump - switch (reader.Name) + switch (dump.Rom) { - case "rom": - DatItem rom = ReadRom(reader.ReadSubtree(), machine, diskno, filename, indexId); - if (rom != null) - items.Add(rom); - - // Skip the rom now that we've processed it - reader.Skip(); + case Models.OpenMSX.Rom: + item.OpenMSXSubType = OpenMSXSubType.Rom; break; - - case "megarom": - DatItem megarom = ReadMegaRom(reader.ReadSubtree(), machine, diskno, filename, indexId); - if (megarom != null) - items.Add(megarom); - - // Skip the megarom now that we've processed it - reader.Skip(); + case Models.OpenMSX.MegaRom: + item.OpenMSXSubType = OpenMSXSubType.MegaRom; break; - - case "sccpluscart": - DatItem sccpluscart = ReadSccPlusCart(reader.ReadSubtree(), machine, diskno, filename, indexId); - if (sccpluscart != null) - items.Add(sccpluscart); - - // Skip the sccpluscart now that we've processed it - reader.Skip(); - break; - - case "original": - original = new Original - { - Value = reader.GetAttribute("value").AsYesNo(), - Content = reader.ReadElementContentAsString() - }; - break; - - default: - reader.Read(); - break; - } - } - - // If we have any items, loop through and add them - foreach (DatItem item in items) - { - switch (item.ItemType) - { - case ItemType.Rom: - (item as Rom).Original = original; + case Models.OpenMSX.SCCPlusCart: + item.OpenMSXSubType = OpenMSXSubType.SCCPlusCart; break; } item.CopyMachineInformation(machine); ParseAddHelper(item, statsOnly); } - - return items.Count > 0; } - /// - /// Read rom information - /// - /// XmlReader representing a rom block - /// Machine information to pass to contained items - /// Disk number to use when outputting to other DAT formats - /// Name of the file to be parsed - /// Index ID for the DAT - private DatItem ReadRom( - XmlReader reader, - Machine machine, - int diskno, - - // Standard Dat parsing - string filename, - int indexId) - { - string hash = string.Empty, - offset = string.Empty, - type = string.Empty, - remark = string.Empty; - - while (!reader.EOF) - { - // We only want elements - if (reader.NodeType != XmlNodeType.Element) - { - reader.Read(); - continue; - } - - // Get the elements from the rom - switch (reader.Name) - { - case "hash": - hash = reader.ReadElementContentAsString(); - break; - - case "start": - offset = reader.ReadElementContentAsString(); - break; - - case "type": - type = reader.ReadElementContentAsString(); - break; - - case "remark": - remark = reader.ReadElementContentAsString(); - break; - - default: - reader.Read(); - break; - } - } - - // If we got a hash, then create and return the item - if (!string.IsNullOrWhiteSpace(hash)) - { - return new Rom - { - Name = machine.Name + "_" + diskno + (!string.IsNullOrWhiteSpace(remark) ? " " + remark : string.Empty), - Offset = offset, - Size = null, - SHA1 = hash, - - Source = new Source - { - Index = indexId, - Name = filename, - }, - - OpenMSXSubType = OpenMSXSubType.Rom, - OpenMSXType = type, - Remark = remark, - }; - } - - // No valid item means returning null - return null; - } - - /// - /// Read megarom information - /// - /// XmlReader representing a megarom block - /// Machine information to pass to contained items - /// Disk number to use when outputting to other DAT formats - /// Name of the file to be parsed - /// Index ID for the DAT - private DatItem ReadMegaRom( - XmlReader reader, - Machine machine, - int diskno, - - // Standard Dat parsing - string filename, - int indexId) - { - string hash = string.Empty, - offset = string.Empty, - type = string.Empty, - remark = string.Empty; - - while (!reader.EOF) - { - // We only want elements - if (reader.NodeType != XmlNodeType.Element) - { - reader.Read(); - continue; - } - - // Get the elements from the dump - switch (reader.Name) - { - case "hash": - hash = reader.ReadElementContentAsString(); - break; - - case "start": - offset = reader.ReadElementContentAsString(); - break; - - case "type": - reader.ReadElementContentAsString(); - break; - - case "remark": - remark = reader.ReadElementContentAsString(); - break; - - default: - reader.Read(); - break; - } - } - - // If we got a hash, then create and return the item - if (!string.IsNullOrWhiteSpace(hash)) - { - return new Rom - { - Name = machine.Name + "_" + diskno + (!string.IsNullOrWhiteSpace(remark) ? " " + remark : string.Empty), - Offset = offset, - Size = null, - SHA1 = hash, - - Source = new Source - { - Index = indexId, - Name = filename, - }, - - OpenMSXSubType = OpenMSXSubType.MegaRom, - OpenMSXType = type, - Remark = remark, - }; - } - - // No valid item means returning null - return null; - } - - /// - /// Read sccpluscart information - /// - /// XmlReader representing a sccpluscart block - /// Machine information to pass to contained items - /// Disk number to use when outputting to other DAT formats - /// Name of the file to be parsed - /// Index ID for the DAT - private DatItem ReadSccPlusCart( - XmlReader reader, - Machine machine, - int diskno, - - // Standard Dat parsing - string filename, - int indexId) - { - string boot = string.Empty, - hash = string.Empty, - remark = string.Empty; - - while (!reader.EOF) - { - // We only want elements - if (reader.NodeType != XmlNodeType.Element) - { - reader.Read(); - continue; - } - - // Get the elements from the dump - switch (reader.Name) - { - case "boot": - boot = reader.ReadElementContentAsString(); - break; - - case "hash": - hash = reader.ReadElementContentAsString(); - break; - - case "remark": - remark = reader.ReadElementContentAsString(); - break; - - default: - reader.Read(); - break; - } - } - - // If we got a hash, then create and return the item - if (!string.IsNullOrWhiteSpace(hash)) - { - return new Rom - { - Name = machine.Name + "_" + diskno + (!string.IsNullOrWhiteSpace(remark) ? " " + remark : string.Empty), - Size = null, - SHA1 = hash, - - Source = new Source - { - Index = indexId, - Name = filename, - }, - - OpenMSXSubType = OpenMSXSubType.SCCPlusCart, - Boot = boot, - Remark = remark, - }; - } - - // No valid item means returning null - return null; - } + #endregion } } diff --git a/SabreTools.Test/Serialization/SerializationTests.cs b/SabreTools.Test/Serialization/SerializationTests.cs index d589ed16..e31e410d 100644 --- a/SabreTools.Test/Serialization/SerializationTests.cs +++ b/SabreTools.Test/Serialization/SerializationTests.cs @@ -18,7 +18,7 @@ namespace SabreTools.Test.Parser Assert.NotNull(stream); byte[] hash = System.Security.Cryptography.SHA1.Create().ComputeHash(stream.GetBuffer()); string hashstr = BitConverter.ToString(hash).Replace("-", string.Empty); - Assert.Equal("195D11C8A93D73F9FBF1ECD8166D80D7BB1B0974", hashstr); + Assert.Equal("268940391C107ABE67E804BC5479E40B5FF68B34", hashstr); } #region Payload Generators