diff --git a/SabreTools.DatFiles/Formats/DosCenter.Reader.cs b/SabreTools.DatFiles/Formats/DosCenter.Reader.cs
new file mode 100644
index 00000000..2ef61262
--- /dev/null
+++ b/SabreTools.DatFiles/Formats/DosCenter.Reader.cs
@@ -0,0 +1,163 @@
+using System;
+using System.Linq;
+using SabreTools.Core.Tools;
+using SabreTools.DatItems;
+using SabreTools.DatItems.Formats;
+
+namespace SabreTools.DatFiles.Formats
+{
+ ///
+ /// Represents parsing and writing of a DosCenter DAT
+ ///
+ internal partial class DosCenter : DatFile
+ {
+ ///
+ public override void ParseFile(string filename, int indexId, bool keep, bool statsOnly = false, bool throwOnError = false)
+ {
+ try
+ {
+ // Deserialize the input file
+ var metadataFile = Serialization.DosCenter.Deserialize(filename);
+
+ // Convert the header to the internal format
+ ConvertHeader(metadataFile?.DosCenter, keep);
+
+ // Convert the game data to the internal format
+ ConvertGames(metadataFile?.Game, filename, indexId, statsOnly);
+ }
+ catch (Exception ex) when (!throwOnError)
+ {
+ string message = $"'{filename}' - An error occurred during parsing";
+ logger.Error(ex, message);
+ }
+ }
+
+ #region Converters
+
+ ///
+ /// Convert header information
+ ///
+ /// Deserialized model to convert
+ /// True if full pathnames are to be kept, false otherwise (default)
+ private void ConvertHeader(Models.DosCenter.DosCenter? doscenter, bool keep)
+ {
+ // If the header is missing, we can't do anything
+ if (doscenter == null)
+ return;
+
+ Header.Name ??= doscenter.Name;
+ Header.Description ??= doscenter.Description;
+ Header.Version ??= doscenter.Version;
+ Header.Date ??= doscenter.Date;
+ Header.Author ??= doscenter.Author;
+ Header.Homepage ??= doscenter.Homepage;
+ Header.Comment ??= doscenter.Comment;
+
+ // Handle implied SuperDAT
+ if (doscenter.Name.Contains(" - SuperDAT") && keep)
+ Header.Type ??= "SuperDAT";
+ }
+
+ ///
+ /// Convert games information
+ ///
+ /// Array of deserialized models 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 ConvertGames(Models.DosCenter.Game[]? games, string filename, int indexId, bool statsOnly)
+ {
+ // If the game array is missing, we can't do anything
+ if (games == null || !games.Any())
+ return;
+
+ // Loop through the games and add
+ foreach (var game in games)
+ {
+ ConvertGame(game, filename, indexId, statsOnly);
+ }
+ }
+
+ ///
+ /// Convert game 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 ConvertGame(Models.DosCenter.Game game, string filename, int indexId, bool statsOnly)
+ {
+ // If the game is missing, we can't do anything
+ if (game == null)
+ return;
+
+ // Create the machine for copying information
+ string machineName = game.Name.Trim('"');
+ if (machineName.EndsWith(".zip"))
+ machineName = System.IO.Path.GetFileNameWithoutExtension(machineName);
+
+ var machine = new Machine { Name = machineName };
+
+ // Check if there are any items
+ bool containsItems = false;
+
+ // Loop through each type of item
+ ConvertFiles(game.File, machine, filename, indexId, statsOnly, ref containsItems);
+
+ // If we had no items, create a Blank placeholder
+ if (!containsItems)
+ {
+ var blank = new Blank
+ {
+ Source = new Source
+ {
+ Index = indexId,
+ Name = filename,
+ },
+ };
+
+ blank.CopyMachineInformation(machine);
+ ParseAddHelper(blank, statsOnly);
+ }
+ }
+
+ ///
+ /// Convert Rom information
+ ///
+ /// Array of deserialized models to convert
+ /// Prefilled machine to use
+ /// Name of the file to be parsed
+ /// Index ID for the DAT
+ /// True to only add item statistics while parsing, false otherwise
+ /// True if there were any items in the array, false otherwise
+ private void ConvertFiles(Models.DosCenter.File[]? files, Machine machine, string filename, int indexId, bool statsOnly, ref bool containsItems)
+ {
+ // If the files array is missing, we can't do anything
+ if (files == null || !files.Any())
+ return;
+
+ containsItems = true;
+ foreach (var rom in files)
+ {
+ var item = new Rom
+ {
+ Name = rom.Name,
+ Size = Utilities.CleanLong(rom.Size),
+ CRC = rom.CRC,
+ Date = rom.Date,
+
+ Source = new Source
+ {
+ Index = indexId,
+ Name = filename,
+ },
+ };
+
+ item.CopyMachineInformation(machine);
+ ParseAddHelper(item, statsOnly);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/SabreTools.DatFiles/Formats/DosCenter.Writer.cs b/SabreTools.DatFiles/Formats/DosCenter.Writer.cs
new file mode 100644
index 00000000..b787f2f0
--- /dev/null
+++ b/SabreTools.DatFiles/Formats/DosCenter.Writer.cs
@@ -0,0 +1,184 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using SabreTools.Core;
+using SabreTools.DatItems;
+using SabreTools.DatItems.Formats;
+
+namespace SabreTools.DatFiles.Formats
+{
+ ///
+ /// Represents parsing and writing of a DosCenter DAT
+ ///
+ internal partial class DosCenter : DatFile
+ {
+ ///
+ protected override ItemType[] GetSupportedTypes()
+ {
+ return new ItemType[]
+ {
+ ItemType.Rom
+ };
+ }
+
+ ///
+ protected override List GetMissingRequiredFields(DatItem datItem)
+ {
+ List missingFields = new();
+
+ // Check item name
+ if (string.IsNullOrWhiteSpace(datItem.GetName()))
+ missingFields.Add(DatItemField.Name);
+
+ switch (datItem)
+ {
+ case Rom rom:
+ if (!rom.SizeSpecified)
+ missingFields.Add(DatItemField.Size);
+ // if (string.IsNullOrWhiteSpace(rom.Date))
+ // missingFields.Add(DatItemField.Date);
+ if (string.IsNullOrWhiteSpace(rom.CRC))
+ missingFields.Add(DatItemField.CRC);
+ break;
+ }
+
+ return missingFields;
+ }
+
+ ///
+ public override bool WriteToFile(string outfile, bool ignoreblanks = false, bool throwOnError = false)
+ {
+ try
+ {
+ logger.User($"Writing to '{outfile}'...");
+
+ var metadataFile = CreateMetadataFile(ignoreblanks);
+ if (!Serialization.DosCenter.SerializeToFile(metadataFile, outfile))
+ {
+ logger.Warning($"File '{outfile}' could not be written! See the log for more details.");
+ return false;
+ }
+ }
+ catch (Exception ex) when (!throwOnError)
+ {
+ logger.Error(ex);
+ return false;
+ }
+
+ return true;
+ }
+
+ #region Converters
+
+ ///
+ /// Create a MetadataFile from the current internal information
+ ///
+ /// True if blank roms should be skipped on output, false otherwise
+ private Models.DosCenter.MetadataFile CreateMetadataFile(bool ignoreblanks)
+ {
+ var metadataFile = new Models.DosCenter.MetadataFile
+ {
+ DosCenter = CreateDosCenter(),
+ Game = CreateGames(ignoreblanks)
+ };
+ return metadataFile;
+ }
+
+ ///
+ /// Create a DosCenter from the current internal information
+ ///
+ private Models.DosCenter.DosCenter? CreateDosCenter()
+ {
+ // If we don't have a header, we can't do anything
+ if (this.Header == null)
+ return null;
+
+ var clrMamePro = new Models.DosCenter.DosCenter
+ {
+ Name = Header.Name,
+ Description = Header.Description,
+ Version = Header.Version,
+ Date = Header.Date,
+ Author = Header.Author,
+ Homepage = Header.Homepage,
+ Comment = Header.Comment,
+ };
+
+ return clrMamePro;
+ }
+
+ ///
+ /// Create an array of GameBase from the current internal information
+ ///
+ /// True if blank roms should be skipped on output, false otherwise
+ private Models.DosCenter.Game[]? CreateGames(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 games = 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;
+
+ // We re-add the missing parts of the game name
+ var game = new Models.DosCenter.Game
+ {
+ Name = $"\"{machine.Name}.zip\""
+ };
+
+ // Create holders for all item types
+ var files = new List();
+
+ // Loop through and convert the items to respective lists
+ foreach (var item in items)
+ {
+ // Skip if we're ignoring the item
+ if (ShouldIgnore(item, ignoreblanks))
+ continue;
+
+ switch (item)
+ {
+ case Rom rom:
+ files.Add(CreateFile(rom));
+ break;
+ }
+ }
+
+ // Assign the values to the game
+ game.File = files.ToArray();
+
+ // Add the game to the list
+ games.Add(game);
+ }
+
+ return games.ToArray();
+ }
+
+ ///
+ /// Create a File from the current Rom DatItem
+ ///
+ private static Models.DosCenter.File CreateFile(Rom item)
+ {
+ var rom = new Models.DosCenter.File
+ {
+ Name = item.Name,
+ Size = item.Size?.ToString(),
+ CRC = item.CRC,
+ Date = item.Date,
+ };
+ return rom;
+ }
+
+ #endregion
+ }
+}
diff --git a/SabreTools.DatFiles/Formats/DosCenter.cs b/SabreTools.DatFiles/Formats/DosCenter.cs
index f3033d43..380a619e 100644
--- a/SabreTools.DatFiles/Formats/DosCenter.cs
+++ b/SabreTools.DatFiles/Formats/DosCenter.cs
@@ -1,22 +1,9 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text;
-using SabreTools.Core;
-using SabreTools.Core.Tools;
-using SabreTools.DatItems;
-using SabreTools.DatItems.Formats;
-using SabreTools.IO;
-using SabreTools.IO.Readers;
-using SabreTools.IO.Writers;
-
-namespace SabreTools.DatFiles.Formats
+namespace SabreTools.DatFiles.Formats
{
///
- /// Represents parsing and writing of a DosCenter DAT
+ /// Represents a DosCenter DAT
///
- internal class DosCenter : DatFile
+ internal partial class DosCenter : DatFile
{
///
/// Constructor designed for casting a base DatFile
@@ -26,414 +13,5 @@ namespace SabreTools.DatFiles.Formats
: base(datFile)
{
}
-
- ///
- public override void ParseFile(string filename, int indexId, bool keep, bool statsOnly = false, bool throwOnError = false)
- {
- // Open a file reader
- Encoding enc = filename.GetEncoding();
- ClrMameProReader cmpr = new(System.IO.File.OpenRead(filename), enc)
- {
- DosCenter = true
- };
-
- while (!cmpr.EndOfStream)
- {
- try
- {
- cmpr.ReadNextLine();
-
- // Ignore everything not top-level
- if (cmpr.RowType != CmpRowType.TopLevel)
- continue;
-
- // Switch on the top-level name
- switch (cmpr.TopLevel.ToLowerInvariant())
- {
- // Header values
- case "doscenter":
- ReadHeader(cmpr);
- break;
-
- // Sets
- case "game":
- ReadGame(cmpr, statsOnly, filename, indexId);
- break;
-
- default:
- break;
- }
- }
- catch (Exception ex) when (!throwOnError)
- {
- string message = $"'{filename}' - There was an error parsing line {cmpr.LineNumber} '{cmpr.CurrentLine}'";
- logger.Error(ex, message);
- }
- }
-
- cmpr.Dispose();
- }
-
- ///
- /// Read header information
- ///
- /// ClrMameProReader to use to parse the header
- private void ReadHeader(ClrMameProReader cmpr)
- {
- // If there's no subtree to the header, skip it
- if (cmpr == null || cmpr.EndOfStream)
- return;
-
- // While we don't hit an end element or end of stream
- while (!cmpr.EndOfStream)
- {
- cmpr.ReadNextLine();
-
- // Ignore comments, internal items, and nothingness
- if (cmpr.RowType == CmpRowType.None || cmpr.RowType == CmpRowType.Comment || cmpr.RowType == CmpRowType.Internal)
- continue;
-
- // If we reached the end of a section, break
- if (cmpr.RowType == CmpRowType.EndTopLevel)
- break;
-
- // If the standalone value is null, we skip
- if (cmpr.Standalone == null)
- continue;
-
- string itemKey = cmpr.Standalone?.Key.ToLowerInvariant().TrimEnd(':');
- string itemVal = cmpr.Standalone?.Value;
-
- // For all other cases
- switch (itemKey)
- {
- case "name":
- Header.Name ??= itemVal;
- break;
- case "description":
- Header.Description ??= itemVal;
- break;
- case "version":
- Header.Version ??= itemVal;
- break;
- case "date":
- Header.Date ??= itemVal;
- break;
- case "author":
- Header.Author ??= itemVal;
- break;
- case "homepage":
- Header.Homepage ??= itemVal;
- break;
- case "comment":
- Header.Comment ??= itemVal;
- break;
- }
- }
- }
-
- ///
- /// Read set information
- ///
- /// ClrMameProReader to use to parse the header
- /// True to only add item statistics while parsing, false otherwise
- /// Name of the file to be parsed
- /// Index ID for the DAT
- private void ReadGame(ClrMameProReader cmpr, bool statsOnly, string filename, int indexId)
- {
- // Prepare all internal variables
- bool containsItems = false;
- Machine machine = new()
- {
- MachineType = MachineType.None,
- };
-
- // If there's no subtree to the header, skip it
- if (cmpr == null || cmpr.EndOfStream)
- return;
-
- // While we don't hit an end element or end of stream
- while (!cmpr.EndOfStream)
- {
- cmpr.ReadNextLine();
-
- // Ignore comments and nothingness
- if (cmpr.RowType == CmpRowType.None || cmpr.RowType == CmpRowType.Comment)
- continue;
-
- // If we reached the end of a section, break
- if (cmpr.RowType == CmpRowType.EndTopLevel)
- break;
-
- // Handle any standalone items
- if (cmpr.RowType == CmpRowType.Standalone && cmpr.Standalone != null)
- {
- string itemKey = cmpr.Standalone?.Key.ToLowerInvariant();
- string itemVal = cmpr.Standalone?.Value;
-
- switch (itemKey)
- {
- case "name":
- machine.Name = (itemVal.ToLowerInvariant().EndsWith(".zip") ? itemVal.Remove(itemVal.Length - 4) : itemVal);
- machine.Description = (itemVal.ToLowerInvariant().EndsWith(".zip") ? itemVal.Remove(itemVal.Length - 4) : itemVal);
- break;
- }
- }
-
- // Handle any internal items
- else if (cmpr.RowType == CmpRowType.Internal
- && string.Equals(cmpr.InternalName, "file", StringComparison.OrdinalIgnoreCase)
- && cmpr.Internal != null)
- {
- containsItems = true;
-
- // Create the proper DatItem based on the type
- Rom item = DatItem.Create(ItemType.Rom) as Rom;
-
- // Then populate it with information
- item.CopyMachineInformation(machine);
- item.Source = new Source
- {
- Index = indexId,
- Name = filename,
- };
-
- // Loop through all of the attributes
- foreach (var kvp in cmpr.Internal)
- {
- string attrKey = kvp.Key;
- string attrVal = kvp.Value;
-
- switch (attrKey)
- {
- //If the item is empty, we automatically skip it because it's a fluke
- case "":
- continue;
-
- // Regular attributes
- case "name":
- item.Name = attrVal;
- break;
-
- case "size":
- item.Size = Utilities.CleanLong(attrVal);
- break;
-
- case "crc":
- item.CRC = attrVal;
- break;
- case "date":
- item.Date = attrVal;
- break;
- }
- }
-
- // Now process and add the rom
- ParseAddHelper(item, statsOnly);
- }
- }
-
- // If no items were found for this machine, add a Blank placeholder
- if (!containsItems)
- {
- Blank blank = new()
- {
- Source = new Source
- {
- Index = indexId,
- Name = filename,
- },
- };
-
- blank.CopyMachineInformation(machine);
-
- // Now process and add the rom
- ParseAddHelper(blank, statsOnly);
- }
- }
-
- ///
- protected override ItemType[] GetSupportedTypes()
- {
- return new ItemType[] { ItemType.Rom };
- }
-
- ///
- 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;
- }
-
- ClrMameProWriter cmpw = new(fs, new UTF8Encoding(false))
- {
- Quotes = false
- };
-
- // Write out the header
- WriteHeader(cmpw);
-
- // 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];
-
- List newsplit = datItem.Machine.Name.Split('\\').ToList();
-
- // 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(cmpw);
-
- // If we have a new game, output the beginning of the new item
- if (lastgame == null || lastgame.ToLowerInvariant() != datItem.Machine.Name.ToLowerInvariant())
- WriteStartGame(cmpw, datItem);
-
- // Check for a "null" item
- datItem = ProcessNullifiedItem(datItem);
-
- // Write out the item if we're not ignoring
- if (!ShouldIgnore(datItem, ignoreblanks))
- WriteDatItem(cmpw, datItem);
-
- // Set the new data to compare against
- lastgame = datItem.Machine.Name;
- }
- }
-
- // Write the file footer out
- WriteFooter(cmpw);
-
- logger.User($"'{outfile}' written!{Environment.NewLine}");
- cmpw.Dispose();
- fs.Dispose();
- }
- catch (Exception ex) when (!throwOnError)
- {
- logger.Error(ex);
- return false;
- }
-
- return true;
- }
-
- ///
- /// Write out DAT header using the supplied StreamWriter
- ///
- /// ClrMameProWriter to output to
- private void WriteHeader(ClrMameProWriter cmpw)
- {
- // Build the state
- cmpw.WriteStartElement("DOSCenter");
-
- cmpw.WriteRequiredStandalone("Name:", Header.Name, false);
- cmpw.WriteRequiredStandalone("Description:", Header.Description, false);
- cmpw.WriteRequiredStandalone("Version:", Header.Version, false);
- cmpw.WriteRequiredStandalone("Date:", Header.Date, false);
- cmpw.WriteRequiredStandalone("Author:", Header.Author, false);
- cmpw.WriteRequiredStandalone("Homepage:", Header.Homepage, false);
- cmpw.WriteRequiredStandalone("Comment:", Header.Comment, false);
-
- cmpw.WriteEndElement();
-
- cmpw.Flush();
- }
-
- ///
- /// Write out Game start using the supplied StreamWriter
- ///
- /// ClrMameProWriter to output to
- /// DatItem object to be output
- private void WriteStartGame(ClrMameProWriter cmpw, DatItem datItem)
- {
- // No game should start with a path separator
- datItem.Machine.Name = datItem.Machine.Name.TrimStart(Path.DirectorySeparatorChar);
-
- // Build the state
- cmpw.WriteStartElement("game");
- cmpw.WriteRequiredStandalone("name", $"{datItem.Machine.Name}.zip", true);
-
- cmpw.Flush();
- }
-
- ///
- /// Write out Game end using the supplied StreamWriter
- ///
- /// ClrMameProWriter to output to
- private void WriteEndGame(ClrMameProWriter cmpw)
- {
- // End game
- cmpw.WriteEndElement();
-
- cmpw.Flush();
- }
-
- ///
- /// Write out DatItem using the supplied StreamWriter
- ///
- /// ClrMameProWriter to output to
- /// DatItem object to be output
- private void WriteDatItem(ClrMameProWriter cmpw, DatItem datItem)
- {
- // Pre-process the item name
- ProcessItemName(datItem, true);
-
- // Build the state
- switch (datItem.ItemType)
- {
- case ItemType.Rom:
- var rom = datItem as Rom;
- cmpw.WriteStartElement("file");
- cmpw.WriteRequiredAttributeString("name", rom.Name);
- cmpw.WriteOptionalAttributeString("size", rom.Size?.ToString());
- cmpw.WriteOptionalAttributeString("date", rom.Date);
- cmpw.WriteOptionalAttributeString("crc", rom.CRC?.ToLowerInvariant());
- cmpw.WriteEndElement();
- break;
- }
-
- cmpw.Flush();
- }
-
- ///
- /// Write out DAT footer using the supplied StreamWriter
- ///
- /// ClrMameProWriter to output to
- /// True if the data was written, false on error
- private void WriteFooter(ClrMameProWriter cmpw)
- {
- // End game
- cmpw.WriteEndElement();
-
- cmpw.Flush();
- }
}
}
diff --git a/SabreTools.Models/DosCenter/File.cs b/SabreTools.Models/DosCenter/File.cs
index 5003a7c9..8c6f7375 100644
--- a/SabreTools.Models/DosCenter/File.cs
+++ b/SabreTools.Models/DosCenter/File.cs
@@ -4,16 +4,16 @@ namespace SabreTools.Models.DosCenter
public class File
{
/// name, attribute
- public string? Name { get; set; }
+ public string Name { get; set; }
/// size, attribute, numeric
- public string? Size { get; set; }
+ public string Size { get; set; }
/// crc, attribute
- public string? CRC { get; set; }
+ public string CRC { get; set; }
/// date, attribute
- public string? Date { get; set; }
+ public string Date { get; set; }
#region DO NOT USE IN PRODUCTION
diff --git a/SabreTools.Models/DosCenter/DatFile.cs b/SabreTools.Models/DosCenter/MetadataFile.cs
similarity index 92%
rename from SabreTools.Models/DosCenter/DatFile.cs
rename to SabreTools.Models/DosCenter/MetadataFile.cs
index 571b37ee..48f2a6e9 100644
--- a/SabreTools.Models/DosCenter/DatFile.cs
+++ b/SabreTools.Models/DosCenter/MetadataFile.cs
@@ -1,6 +1,6 @@
namespace SabreTools.Models.DosCenter
{
- public class DatFile
+ public class MetadataFile
{
/// doscenter
public DosCenter? DosCenter { get; set; }
diff --git a/SabreTools.Serialization/DosCenter.cs b/SabreTools.Serialization/DosCenter.Deserializer.cs
similarity index 81%
rename from SabreTools.Serialization/DosCenter.cs
rename to SabreTools.Serialization/DosCenter.Deserializer.cs
index c40e1002..aa668de5 100644
--- a/SabreTools.Serialization/DosCenter.cs
+++ b/SabreTools.Serialization/DosCenter.Deserializer.cs
@@ -7,16 +7,16 @@ using SabreTools.Models.DosCenter;
namespace SabreTools.Serialization
{
///
- /// Serializer for DosCenter metadata files
+ /// Deserializer for DosCenter metadata files
///
- public class DosCenter
+ public partial class DosCenter
{
///
/// Deserializes a DosCenter metadata file to the defined type
///
/// Path to the file to deserialize
/// Deserialized data on success, null on failure
- public static DatFile? Deserialize(string path)
+ public static MetadataFile? Deserialize(string path)
{
try
{
@@ -35,7 +35,7 @@ namespace SabreTools.Serialization
///
/// Stream to deserialize
/// Deserialized data on success, null on failure
- public static DatFile? Deserialize(Stream? stream)
+ public static MetadataFile? Deserialize(Stream? stream)
{
try
{
@@ -45,7 +45,7 @@ namespace SabreTools.Serialization
// Setup the reader and output
var reader = new ClrMameProReader(stream, Encoding.UTF8) { DosCenter = true };
- var dat = new DatFile();
+ var dat = new MetadataFile();
// Loop through and parse out the values
string lastTopLevel = reader.TopLevel;
@@ -166,9 +166,6 @@ namespace SabreTools.Serialization
// If we're in a file block
else if (reader.TopLevel == "game" && reader.RowType == CmpRowType.Internal)
{
- // Create the block
- var file = new Models.DosCenter.File();
-
// If we have an unknown type, log it
if (reader.InternalName != "file")
{
@@ -176,32 +173,9 @@ namespace SabreTools.Serialization
continue;
}
- foreach (var kvp in reader.Internal)
- {
- switch (kvp.Key?.ToLowerInvariant())
- {
- case "name":
- file.Name = kvp.Value;
- break;
- case "size":
- file.Size = kvp.Value;
- break;
- case "crc":
- file.CRC = kvp.Value;
- break;
- case "date":
- file.Date = kvp.Value;
- break;
- default:
- fileAdditional.Add(item: reader.CurrentLine);
- break;
- }
- }
-
- // Add the file to the list
- file.ADDITIONAL_ELEMENTS = fileAdditional.ToArray();
+ // Create the file and add to the list
+ var file = CreateFile(reader);
files.Add(file);
- fileAdditional.Clear();
}
else
@@ -221,5 +195,40 @@ namespace SabreTools.Serialization
return default;
}
}
+
+ ///
+ /// Create a File object from the current reader context
+ ///
+ /// ClrMameProReader representing the metadata file
+ /// File object created from the reader context
+ private static Models.DosCenter.File CreateFile(ClrMameProReader reader)
+ {
+ var itemAdditional = new List();
+ var file = new Models.DosCenter.File();
+ foreach (var kvp in reader.Internal)
+ {
+ switch (kvp.Key?.ToLowerInvariant())
+ {
+ case "name":
+ file.Name = kvp.Value;
+ break;
+ case "size":
+ file.Size = kvp.Value;
+ break;
+ case "crc":
+ file.CRC = kvp.Value;
+ break;
+ case "date":
+ file.Date = kvp.Value;
+ break;
+ default:
+ itemAdditional.Add(item: reader.CurrentLine);
+ break;
+ }
+ }
+
+ file.ADDITIONAL_ELEMENTS = itemAdditional.ToArray();
+ return file;
+ }
}
}
\ No newline at end of file
diff --git a/SabreTools.Serialization/DosCenter.Serializer.cs b/SabreTools.Serialization/DosCenter.Serializer.cs
new file mode 100644
index 00000000..7004da15
--- /dev/null
+++ b/SabreTools.Serialization/DosCenter.Serializer.cs
@@ -0,0 +1,149 @@
+using System.IO;
+using System.Linq;
+using System.Text;
+using SabreTools.IO.Writers;
+using SabreTools.Models.DosCenter;
+namespace SabreTools.Serialization
+{
+ ///
+ /// Deserializer for DosCenter metadata files
+ ///
+ public partial class DosCenter
+ {
+ ///
+ /// Serializes the defined type to a DosCenter metadata file
+ ///
+ /// Data to serialize
+ /// Path to the file to serialize to
+ /// True on successful serialization, false otherwise
+ public static bool SerializeToFile(MetadataFile? metadataFile, string path)
+ {
+ using var stream = SerializeToStream(metadataFile);
+ if (stream == null)
+ return false;
+
+ using var fs = System.IO.File.OpenWrite(path);
+ stream.Seek(0, SeekOrigin.Begin);
+ stream.CopyTo(fs);
+ return true;
+ }
+
+ ///
+ /// Serializes the defined type to a stream
+ ///
+ /// Data to serialize
+ /// Stream containing serialized data on success, null otherwise
+ public static Stream? SerializeToStream(MetadataFile? metadataFile)
+ {
+ // If the metadata file is null
+ if (metadataFile == null)
+ return null;
+
+ // Setup the writer and output
+ var stream = new MemoryStream();
+ var writer = new ClrMameProWriter(stream, Encoding.UTF8)
+ {
+ Quotes = false,
+ };
+
+ // Write the header, if it exists
+ WriteHeader(metadataFile.DosCenter, writer);
+
+ // Write out the games, if they exist
+ WriteGames(metadataFile.Game, writer);
+
+ // Return the stream
+ return stream;
+ }
+
+ ///
+ /// Write header information to the current writer
+ ///
+ /// DosCenter representing the header information
+ /// ClrMameProWriter representing the output
+ private static void WriteHeader(Models.DosCenter.DosCenter? header, ClrMameProWriter writer)
+ {
+ // If the header information is missing, we can't do anything
+ if (header == null)
+ return;
+
+ writer.WriteStartElement("DOSCenter");
+
+ writer.WriteOptionalStandalone("Name:", header.Name);
+ writer.WriteOptionalStandalone("Description:", header.Description);
+ writer.WriteOptionalStandalone("Version:", header.Version);
+ writer.WriteOptionalStandalone("Date:", header.Date);
+ writer.WriteOptionalStandalone("Author:", header.Author);
+ writer.WriteOptionalStandalone("Homepage:", header.Homepage);
+ writer.WriteOptionalStandalone("Comment:", header.Comment);
+
+ writer.WriteEndElement(); // doscenter
+ writer.Flush();
+ }
+
+ ///
+ /// Write games information to the current writer
+ ///
+ /// Array of Game objects representing the games information
+ /// ClrMameProWriter representing the output
+ private static void WriteGames(Game[]? games, ClrMameProWriter writer)
+ {
+ // If the games information is missing, we can't do anything
+ if (games == null || !games.Any())
+ return;
+
+ // Loop through and write out the games
+ foreach (var game in games)
+ {
+ WriteGame(game, writer);
+ writer.Flush();
+ }
+ }
+
+ ///
+ /// Write game information to the current writer
+ ///
+ /// Game object representing the game information
+ /// ClrMameProWriter representing the output
+ private static void WriteGame(Game game, ClrMameProWriter writer)
+ {
+ // If the game information is missing, we can't do anything
+ if (game == null)
+ return;
+
+ writer.WriteStartElement("game");
+
+ // Write the standalone values
+ writer.WriteRequiredStandalone("name", game.Name, throwOnError: true);
+
+ // Write the item values
+ WriteFiles(game.File, writer);
+
+ writer.WriteEndElement(); // game
+ }
+
+ ///
+ /// Write files information to the current writer
+ ///
+ /// Array of File objects to write
+ /// ClrMameProWriter representing the output
+ private static void WriteFiles(Models.DosCenter.File[]? files, ClrMameProWriter writer)
+ {
+ // If the array is missing, we can't do anything
+ if (files == null)
+ return;
+
+ foreach (var file in files)
+ {
+ writer.WriteStartElement("file");
+
+ writer.WriteRequiredAttributeString("name", file.Name, throwOnError: true);
+ writer.WriteRequiredAttributeString("size", file.Size, throwOnError: true);
+ writer.WriteOptionalAttributeString("date", file.Date);
+ writer.WriteRequiredAttributeString("crc", file.CRC.ToUpperInvariant(), throwOnError: true);
+
+ writer.WriteEndElement(); // file
+ }
+ }
+ }
+}
\ No newline at end of file