diff --git a/SabreTools.DatFiles/Formats/OpenMSX.Reader.cs b/SabreTools.DatFiles/Formats/OpenMSX.Reader.cs index df9b7dcc..86d217e4 100644 --- a/SabreTools.DatFiles/Formats/OpenMSX.Reader.cs +++ b/SabreTools.DatFiles/Formats/OpenMSX.Reader.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using SabreTools.Core; +using SabreTools.Core.Tools; using SabreTools.DatItems; using SabreTools.DatItems.Formats; @@ -159,7 +160,7 @@ namespace SabreTools.DatFiles.Formats { item.Original = new Original { - Value = dump.Original.Value, + Value = dump.Original.Value.AsYesNo(), Content = dump.Original.Content, }; } diff --git a/SabreTools.DatFiles/Formats/OpenMSX.Writer.cs b/SabreTools.DatFiles/Formats/OpenMSX.Writer.cs index f5d5a6d9..c36217c8 100644 --- a/SabreTools.DatFiles/Formats/OpenMSX.Writer.cs +++ b/SabreTools.DatFiles/Formats/OpenMSX.Writer.cs @@ -1,13 +1,9 @@ using System; using System.Collections.Generic; -using System.IO; -using System.Text; -using System.Xml; +using System.Linq; using SabreTools.Core; -using SabreTools.Core.Tools; using SabreTools.DatItems; using SabreTools.DatItems.Formats; -using SabreTools.IO; namespace SabreTools.DatFiles.Formats { @@ -28,8 +24,20 @@ namespace SabreTools.DatFiles.Formats /// protected override List GetMissingRequiredFields(DatItem datItem) { - // TODO: Check required fields - return null; + var missingFields = new List(); + + if (string.IsNullOrWhiteSpace(datItem.GetName())) + missingFields.Add(DatItemField.Name); + + switch (datItem) + { + case Rom rom: + if (string.IsNullOrWhiteSpace(rom.SHA1)) + missingFields.Add(DatItemField.SHA1); + break; + } + + return missingFields; } /// @@ -38,70 +46,14 @@ namespace SabreTools.DatFiles.Formats 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) + // TODO: Write out comment prefix somehow + var softwaredb = CreateSoftwareDb(ignoreblanks); + if (!Serialization.OpenMSX.SerializeToFileWithDocType(softwaredb, outfile)) { - logger.Warning($"File '{outfile}' could not be created for writing! Please check to see if the file is writable"); + logger.Warning($"File '{outfile}' could not be written! See the log for more details."); 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) { @@ -109,149 +61,124 @@ namespace SabreTools.DatFiles.Formats return false; } + logger.User($"'{outfile}' written!{Environment.NewLine}"); return true; } - /// - /// Write out DAT header using the supplied StreamWriter - /// - /// XmlTextWriter to output to - private void WriteHeader(XmlTextWriter xtw) - { - xtw.WriteStartDocument(); - xtw.WriteDocType("softwaredb", null, "softwaredb1.dtd", null); - - xtw.WriteStartElement("softwaredb"); - xtw.WriteRequiredAttributeString("timestamp", Header.Date); - - //TODO: Figure out how to fix the issue with removed formatting after this point -// xtw.WriteComment("Credits"); -// xtw.WriteCData(@"The softwaredb.xml file contains information about rom mapper types - -//-Copyright 2003 Nicolas Beyaert(Initial Database) -//-Copyright 2004 - 2013 BlueMSX Team -//-Copyright 2005 - 2020 openMSX Team -//-Generation MSXIDs by www.generation - msx.nl - -//- Thanks go out to: -//-Generation MSX / Sylvester for the incredible source of information -//- p_gimeno and diedel for their help adding and valdiating ROM additions -//- GDX for additional ROM info and validations and corrections"); - - xtw.Flush(); - } + #region Converters /// - /// Write out Game start using the supplied StreamWriter - /// - /// XmlTextWriter to output to - /// DatItem object to be output - 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("software"); - xtw.WriteRequiredElementString("title", datItem.Machine.Name); - xtw.WriteRequiredElementString("genmsxid", datItem.Machine.GenMSXID); - xtw.WriteRequiredElementString("system", datItem.Machine.System); - xtw.WriteRequiredElementString("company", datItem.Machine.Manufacturer); - xtw.WriteRequiredElementString("year", datItem.Machine.Year); - xtw.WriteRequiredElementString("country", datItem.Machine.Country); - - xtw.Flush(); - } - + /// Create a SoftwareDb from the current internal information /// - /// Write out Game start using the supplied StreamWriter - /// - /// XmlTextWriter to output to - private void WriteEndGame(XmlTextWriter xtw) + /// True if blank roms should be skipped on output, false otherwise + private Models.OpenMSX.SoftwareDb CreateSoftwareDb(bool ignoreblanks) { - // End software - 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) + var softwaredb = new Models.OpenMSX.SoftwareDb { - case ItemType.Rom: - var rom = datItem as Rom; - xtw.WriteStartElement("dump"); + Timestamp = Header.Date, + Software = CreateSoftwares(ignoreblanks) + }; + return softwaredb; + } - if (rom.Original != null) + /// + /// Create an array of Software from the current internal information + /// + /// True if blank roms should be skipped on output, false otherwise + private Models.OpenMSX.Software[]? CreateSoftwares(bool ignoreblanks) + { + // If we don't have items, we can't do anything + if (this.Items == null || !this.Items.Any()) + return null; + + // Create a list of hold the games + var softwares = new List(); + + // Loop through the sorted items and create games for them + foreach (string key in Items.SortedKeys) + { + var items = Items.FilteredItems(key); + if (items == null || !items.Any()) + continue; + + // Get the first item for game information + var machine = items[0].Machine; + var software = new Models.OpenMSX.Software + { + Title = machine.Name, + GenMSXID = machine.GenMSXID, + System = machine.System, + Company = machine.Manufacturer, + Year = machine.Year, + Country = machine.Country, + }; + + // Create holder for dumps + var dumps = new List(); + + // Loop through and convert the items to respective lists + for (int index = 0; index < items.Count; index++) + { + // Get the item + var item = items[index]; + + // Check for a "null" item + item = ProcessNullifiedItem(item); + + // Skip if we're ignoring the item + if (ShouldIgnore(item, ignoreblanks)) + continue; + + switch (item) { - xtw.WriteStartElement("original"); - xtw.WriteAttributeString("value", rom.Original.Value == true ? "true" : "false"); - xtw.WriteString(rom.Original.Content); - xtw.WriteEndElement(); - } - - switch (rom.OpenMSXSubType) - { - // Default to Rom for converting from other formats - case OpenMSXSubType.Rom: - case OpenMSXSubType.NULL: - xtw.WriteStartElement(rom.OpenMSXSubType.FromOpenMSXSubType()); - xtw.WriteRequiredElementString("hash", rom.SHA1?.ToLowerInvariant()); - xtw.WriteOptionalElementString("start", rom.Offset); - xtw.WriteOptionalElementString("type", rom.OpenMSXType); - xtw.WriteOptionalElementString("remark", rom.Remark); - xtw.WriteEndElement(); - break; - - case OpenMSXSubType.MegaRom: - xtw.WriteStartElement(rom.OpenMSXSubType.FromOpenMSXSubType()); - xtw.WriteRequiredElementString("hash", rom.SHA1?.ToLowerInvariant()); - xtw.WriteOptionalElementString("start", rom.Offset); - xtw.WriteOptionalElementString("type", rom.OpenMSXType); - xtw.WriteOptionalElementString("remark", rom.Remark); - xtw.WriteEndElement(); - break; - - case OpenMSXSubType.SCCPlusCart: - xtw.WriteStartElement(rom.OpenMSXSubType.FromOpenMSXSubType()); - xtw.WriteOptionalElementString("boot", rom.Boot); - xtw.WriteRequiredElementString("hash", rom.SHA1?.ToLowerInvariant()); - xtw.WriteOptionalElementString("remark", rom.Remark); - xtw.WriteEndElement(); + case Rom rom: + dumps.Add(CreateDump(rom)); break; } + } - // End dump - xtw.WriteEndElement(); - break; + software.Dump = dumps.ToArray(); + softwares.Add(software); } - xtw.Flush(); + return softwares.ToArray(); } /// - /// Write out DAT footer using the supplied StreamWriter - /// - /// XmlTextWriter to output to - private void WriteFooter(XmlTextWriter xtw) + /// Create a Dump from the current Rom DatItem + /// + private static Models.OpenMSX.Dump CreateDump(Rom item) { - // End software - xtw.WriteEndElement(); - // End softwaredb - xtw.WriteEndElement(); + Models.OpenMSX.Original original = null; + if (item.OriginalSpecified) + { + original = new Models.OpenMSX.Original { Content = item.Original.Content }; + if (item.Original.Value != null) + original.Value = item.Original.Value.ToString(); + } - xtw.Flush(); + Models.OpenMSX.RomBase rom = item.OpenMSXSubType switch + { + OpenMSXSubType.MegaRom => new Models.OpenMSX.MegaRom(), + OpenMSXSubType.SCCPlusCart => new Models.OpenMSX.SCCPlusCart(), + _ => new Models.OpenMSX.Rom(), + }; + + rom.Start = item.Offset; + rom.Type = item.OpenMSXType; + rom.Hash = item.SHA1; + rom.Remark = item.Remark; + + var dump = new Models.OpenMSX.Dump + { + Original = original, + Rom = rom, + }; + + return dump; } + + #endregion } } diff --git a/SabreTools.DatFiles/Formats/OpenMSX.cs b/SabreTools.DatFiles/Formats/OpenMSX.cs index 96983125..07c87a33 100644 --- a/SabreTools.DatFiles/Formats/OpenMSX.cs +++ b/SabreTools.DatFiles/Formats/OpenMSX.cs @@ -19,6 +19,23 @@ "; + private const string OpenMSXCredits = @" +"; + /// /// Constructor designed for casting a base DatFile /// diff --git a/SabreTools.Models/OpenMSX/Original.cs b/SabreTools.Models/OpenMSX/Original.cs index 35933355..7659b1d4 100644 --- a/SabreTools.Models/OpenMSX/Original.cs +++ b/SabreTools.Models/OpenMSX/Original.cs @@ -6,8 +6,9 @@ namespace SabreTools.Models.OpenMSX [XmlRoot("original")] public class Original { + /// Boolean? [XmlAttribute("value")] - public bool Value { get; set; } + public string? Value { get; set; } [XmlText] public string? Content { get; set; } diff --git a/SabreTools.Test/Serialization/SerializationTests.cs b/SabreTools.Test/Serialization/SerializationTests.cs index e31e410d..1a36f2c0 100644 --- a/SabreTools.Test/Serialization/SerializationTests.cs +++ b/SabreTools.Test/Serialization/SerializationTests.cs @@ -30,7 +30,7 @@ namespace SabreTools.Test.Parser { var original = new Models.OpenMSX.Original { - Value = false, + Value = "false", Content = "Original Name", };