diff --git a/SabreTools.IO/Readers/ClrMameProReader.cs b/SabreTools.IO/Readers/ClrMameProReader.cs
index 020ed3f0..5b6f864d 100644
--- a/SabreTools.IO/Readers/ClrMameProReader.cs
+++ b/SabreTools.IO/Readers/ClrMameProReader.cs
@@ -175,7 +175,7 @@ namespace SabreTools.IO.Readers
{
while (++i < linegc.Length
&& linegc[i] != "size"
- && linegc[i] != "date"
+ && !(linegc[i] == "date" && char.IsDigit(linegc[i + 1][0]))
&& linegc[i] != "crc")
{
value += $" {linegc[i]}";
@@ -235,7 +235,7 @@ namespace SabreTools.IO.Readers
Internal[key] = value;
RowType = CmpRowType.Internal;
Standalone = null;
- }
+ }
InternalName = normalizedValue;
}
diff --git a/SabreTools.Models/AttractMode/Row.cs b/SabreTools.Models/AttractMode/Row.cs
index 8d702fc3..278eede2 100644
--- a/SabreTools.Models/AttractMode/Row.cs
+++ b/SabreTools.Models/AttractMode/Row.cs
@@ -50,7 +50,7 @@ namespace SabreTools.Models.AttractMode
#region DO NOT USE IN PRODUCTION
/// Should be empty
- public string[] ADDITIONAL_ELEMENTS { get; set; }
+ public string[]? ADDITIONAL_ELEMENTS { get; set; }
#endregion
}
diff --git a/SabreTools.Models/DosCenter/DatFile.cs b/SabreTools.Models/DosCenter/DatFile.cs
index df721086..571b37ee 100644
--- a/SabreTools.Models/DosCenter/DatFile.cs
+++ b/SabreTools.Models/DosCenter/DatFile.cs
@@ -7,5 +7,12 @@ namespace SabreTools.Models.DosCenter
/// game
public Game[]? Game { get; set; }
+
+ #region DO NOT USE IN PRODUCTION
+
+ /// Should be empty
+ public string[]? ADDITIONAL_ELEMENTS { get; set; }
+
+ #endregion
}
}
\ No newline at end of file
diff --git a/SabreTools.Models/DosCenter/DosCenter.cs b/SabreTools.Models/DosCenter/DosCenter.cs
index b391ceb6..9fb3fda2 100644
--- a/SabreTools.Models/DosCenter/DosCenter.cs
+++ b/SabreTools.Models/DosCenter/DosCenter.cs
@@ -23,5 +23,12 @@ namespace SabreTools.Models.DosCenter
/// comment
public string? Comment { get; set; }
+
+ #region DO NOT USE IN PRODUCTION
+
+ /// Should be empty
+ public string[] ADDITIONAL_ELEMENTS { get; set; }
+
+ #endregion
}
}
\ No newline at end of file
diff --git a/SabreTools.Models/DosCenter/File.cs b/SabreTools.Models/DosCenter/File.cs
index edb049dc..5003a7c9 100644
--- a/SabreTools.Models/DosCenter/File.cs
+++ b/SabreTools.Models/DosCenter/File.cs
@@ -6,13 +6,20 @@ namespace SabreTools.Models.DosCenter
/// name, attribute
public string? Name { get; set; }
- /// size, attribute
- public long? Size { get; set; }
+ /// size, attribute, numeric
+ public string? Size { get; set; }
/// crc, attribute
public string? CRC { get; set; }
/// date, attribute
public string? Date { get; set; }
+
+ #region DO NOT USE IN PRODUCTION
+
+ /// Should be empty
+ public string[]? ADDITIONAL_ELEMENTS { get; set; }
+
+ #endregion
}
}
\ No newline at end of file
diff --git a/SabreTools.Models/DosCenter/Game.cs b/SabreTools.Models/DosCenter/Game.cs
index 165b5645..3f5b159a 100644
--- a/SabreTools.Models/DosCenter/Game.cs
+++ b/SabreTools.Models/DosCenter/Game.cs
@@ -8,5 +8,12 @@ namespace SabreTools.Models.DosCenter
/// file
public File[]? File { get; set; }
+
+ #region DO NOT USE IN PRODUCTION
+
+ /// Should be empty
+ public string[]? ADDITIONAL_ELEMENTS { get; set; }
+
+ #endregion
}
}
\ No newline at end of file
diff --git a/SabreTools.Serialization/DosCenter.cs b/SabreTools.Serialization/DosCenter.cs
new file mode 100644
index 00000000..fab060a9
--- /dev/null
+++ b/SabreTools.Serialization/DosCenter.cs
@@ -0,0 +1,218 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using SabreTools.IO.Readers;
+using SabreTools.Models.DosCenter;
+
+namespace SabreTools.Serialization
+{
+ ///
+ /// Serializer for DosCenter metadata files
+ ///
+ public 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)
+ {
+ try
+ {
+ using var stream = PathProcessor.OpenStream(path);
+ return Deserialize(stream);
+ }
+ catch
+ {
+ // TODO: Handle logging the exception
+ return default;
+ }
+ }
+
+ ///
+ /// Deserializes a DosCenter metadata file in a stream to the defined type
+ ///
+ /// Stream to deserialize
+ /// Deserialized data on success, null on failure
+ public static DatFile? Deserialize(Stream? stream)
+ {
+ try
+ {
+ // If the stream is null
+ if (stream == null)
+ return default;
+
+ // Setup the reader and output
+ var reader = new ClrMameProReader(stream, Encoding.UTF8) { DosCenter = true };
+ var dat = new DatFile();
+
+ // Loop through and parse out the values
+ string lastTopLevel = reader.TopLevel;
+
+ Game? game = null;
+ var games = new List();
+ var files = new List();
+
+ var additional = new List();
+ var headerAdditional = new List();
+ var gameAdditional = new List();
+ var fileAdditional = new List();
+ while (!reader.EndOfStream)
+ {
+ // If we have no next line
+ if (!reader.ReadNextLine())
+ break;
+
+ // Ignore certain row types
+ switch (reader.RowType)
+ {
+ case CmpRowType.None:
+ case CmpRowType.Comment:
+ continue;
+ case CmpRowType.EndTopLevel:
+ switch (lastTopLevel)
+ {
+ case "doscenter":
+ dat.DosCenter.ADDITIONAL_ELEMENTS = headerAdditional.ToArray();
+ headerAdditional.Clear();
+ break;
+ case "game":
+ game.File = files.ToArray();
+ game.ADDITIONAL_ELEMENTS = gameAdditional.ToArray();
+ games.Add(game);
+
+ game = null;
+ files.Clear();
+ gameAdditional.Clear();
+ break;
+ default:
+ // No-op
+ break;
+ }
+ continue;
+ }
+
+ // If we're at the root
+ if (reader.RowType == CmpRowType.TopLevel)
+ {
+ lastTopLevel = reader.TopLevel;
+ switch (reader.TopLevel)
+ {
+ case "doscenter":
+ dat.DosCenter = new Models.DosCenter.DosCenter();
+ break;
+ case "game":
+ game = new Game();
+ break;
+ default:
+ additional.Add(reader.CurrentLine);
+ break;
+ }
+ }
+
+ // If we're in the doscenter block
+ else if (reader.TopLevel == "doscenter" && reader.RowType == CmpRowType.Standalone)
+ {
+ // Create the block if we haven't already
+ dat.DosCenter ??= new Models.DosCenter.DosCenter();
+
+ switch (reader.Standalone?.Key?.ToLowerInvariant())
+ {
+ case "name:":
+ dat.DosCenter.Name = reader.Standalone?.Value;
+ break;
+ case "description:":
+ dat.DosCenter.Description = reader.Standalone?.Value;
+ break;
+ case "version:":
+ dat.DosCenter.Version = reader.Standalone?.Value;
+ break;
+ case "date:":
+ dat.DosCenter.Date = reader.Standalone?.Value;
+ break;
+ case "author:":
+ dat.DosCenter.Author = reader.Standalone?.Value;
+ break;
+ case "homepage:":
+ dat.DosCenter.Homepage = reader.Standalone?.Value;
+ break;
+ case "comment:":
+ dat.DosCenter.Comment = reader.Standalone?.Value;
+ break;
+ default:
+ headerAdditional.Add(item: reader.CurrentLine);
+ break;
+ }
+ }
+
+ // If we're in a game block
+ else if (reader.TopLevel == "game" && reader.RowType == CmpRowType.Standalone)
+ {
+ // Create the block if we haven't already
+ game ??= new Game();
+
+ switch (reader.Standalone?.Key?.ToLowerInvariant())
+ {
+ case "name":
+ game.Name = reader.Standalone?.Value;
+ break;
+ default:
+ gameAdditional.Add(item: reader.CurrentLine);
+ break;
+ }
+ }
+
+ // 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();
+
+ 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();
+ files.Add(file);
+ fileAdditional.Clear();
+ }
+
+ else
+ {
+ additional.Add(item: reader.CurrentLine);
+ }
+ }
+
+ // Add extra pieces and return
+ dat.Game = games.ToArray();
+ dat.ADDITIONAL_ELEMENTS = additional.ToArray();
+ return dat;
+ }
+ catch
+ {
+ // TODO: Handle logging the exception
+ return default;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/SabreTools.Test/Parser/SerializationTests.cs b/SabreTools.Test/Parser/SerializationTests.cs
index 30214788..d7f108eb 100644
--- a/SabreTools.Test/Parser/SerializationTests.cs
+++ b/SabreTools.Test/Parser/SerializationTests.cs
@@ -48,6 +48,32 @@ namespace SabreTools.Test.Parser
}
}
+ [Fact]
+ public void DosCenterDeserializeTest()
+ {
+ // Open the file for reading
+ string filename = System.IO.Path.Combine(Environment.CurrentDirectory, "TestData", "test-doscenter-files.dat.gz");
+
+ // Deserialize the file
+ var dat = Serialization.DosCenter.Deserialize(filename);
+
+ // Validate the values
+ Assert.NotNull(dat?.DosCenter);
+ Assert.Equal(34965, dat.Game.Length);
+
+ // Validate we're not missing any attributes or elements
+ Assert.Empty(dat.ADDITIONAL_ELEMENTS);
+ Assert.Empty(dat.DosCenter.ADDITIONAL_ELEMENTS);
+ foreach (var game in dat.Game)
+ {
+ Assert.Empty(game.ADDITIONAL_ELEMENTS);
+ foreach (var file in game.File)
+ {
+ Assert.Empty(file.ADDITIONAL_ELEMENTS);
+ }
+ }
+ }
+
[Fact]
public void ListxmlDeserializeTest()
{
diff --git a/SabreTools.Test/TestData/test-doscenter-files.dat.gz b/SabreTools.Test/TestData/test-doscenter-files.dat.gz
new file mode 100644
index 00000000..d2f2d147
Binary files /dev/null and b/SabreTools.Test/TestData/test-doscenter-files.dat.gz differ