diff --git a/SabreTools.Models/AttractMode/Dat.cs b/SabreTools.Models/AttractMode/Dat.cs
new file mode 100644
index 00000000..3ebeaeed
--- /dev/null
+++ b/SabreTools.Models/AttractMode/Dat.cs
@@ -0,0 +1,12 @@
+namespace SabreTools.Models.AttractMode
+{
+ ///
+ /// #Name;Title;Emulator;CloneOf;Year;Manufacturer;Category;Players;Rotation;Control;Status;DisplayCount;DisplayType;AltRomname;AltTitle;Extra;Buttons ///
+ ///
+ public class Dat
+ {
+ public string[] Header { get; set; }
+
+ public Row[]? Row { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SabreTools.Models/AttractMode/Row.cs b/SabreTools.Models/AttractMode/Row.cs
index 88f91db3..8d702fc3 100644
--- a/SabreTools.Models/AttractMode/Row.cs
+++ b/SabreTools.Models/AttractMode/Row.cs
@@ -1,10 +1,8 @@
namespace SabreTools.Models.AttractMode
{
- ///
- /// #Name;Title;Emulator;CloneOf;Year;Manufacturer;Category;Players;Rotation;Control;Status;DisplayCount;DisplayType;AltRomname;AltTitle;Extra;Buttons
- ///
public class Row
{
+ /// Also called Romname
public string Name { get; set; }
public string Title { get; set; }
@@ -38,5 +36,22 @@ namespace SabreTools.Models.AttractMode
public string Extra { get; set; }
public string Buttons { get; set; }
+
+ public string Favorite { get; set; }
+
+ public string Tags { get; set; }
+
+ public string PlayedCount { get; set; }
+
+ public string PlayedTime { get; set; }
+
+ public string FileIsAvailable { 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/AttractMode.cs b/SabreTools.Serialization/AttractMode.cs
new file mode 100644
index 00000000..e718c728
--- /dev/null
+++ b/SabreTools.Serialization/AttractMode.cs
@@ -0,0 +1,146 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using SabreTools.IO.Readers;
+using SabreTools.Models.AttractMode;
+
+namespace SabreTools.Serialization
+{
+ ///
+ /// Separated value serializer for AttractMode romlists
+ ///
+ public class AttractMode
+ {
+ private const string HeaderWithoutRomname = "#Name;Title;Emulator;CloneOf;Year;Manufacturer;Category;Players;Rotation;Control;Status;DisplayCount;DisplayType;AltRomname;AltTitle;Extra;Buttons";
+ private const int HeaderWithoutRomnameCount = 17;
+
+ private const string HeaderWithRomname = "#Romname;Title;Emulator;Cloneof;Year;Manufacturer;Category;Players;Rotation;Control;Status;DisplayCount;DisplayType;AltRomname;AltTitle;Extra;Buttons;Favourite;Tags;PlayedCount;PlayedTime;FileIsAvailable";
+ private const int HeaderWithRomnameCount = 22;
+
+ ///
+ /// Deserializes an AttractMode romlist to the defined type
+ ///
+ /// Path to the file to deserialize
+ /// Deserialized data on success, null on failure
+ public static Dat? Deserialize(string path)
+ {
+ try
+ {
+ using var stream = PathProcessor.OpenStream(path);
+ return Deserialize(stream);
+ }
+ catch
+ {
+ // TODO: Handle logging the exception
+ return default;
+ }
+ }
+
+ ///
+ /// Deserializes an AttractMode romlist in a stream to the defined type
+ ///
+ /// Stream to deserialize
+ /// Deserialized data on success, null on failure
+ public static Dat? Deserialize(Stream? stream)
+ {
+ try
+ {
+ // If the stream is null
+ if (stream == null)
+ return default;
+
+ // Setup the reader and output
+ var reader = new SeparatedValueReader(stream, Encoding.UTF8)
+ {
+ Separator = ';',
+ VerifyFieldCount = false,
+ };
+ var dat = new Dat();
+
+ // Read the header values first
+ if (!reader.ReadHeader())
+ return null;
+
+ dat.Header = reader.HeaderValues.ToArray();
+
+ // Loop through the rows and parse out values
+ var rows = new List();
+ while (!reader.EndOfStream)
+ {
+ // If we have no next line
+ if (!reader.ReadNextLine())
+ break;
+
+ // Parse the line into a row
+ Row row = null;
+ if (reader.Line.Count < HeaderWithRomnameCount)
+ {
+ row = new Row
+ {
+ Name = reader.Line[0],
+ Title = reader.Line[1],
+ Emulator = reader.Line[2],
+ CloneOf = reader.Line[3],
+ Year = reader.Line[4],
+ Manufacturer = reader.Line[5],
+ Category = reader.Line[6],
+ Players = reader.Line[7],
+ Rotation = reader.Line[8],
+ Control = reader.Line[9],
+ Status = reader.Line[10],
+ DisplayCount = reader.Line[11],
+ DisplayType = reader.Line[12],
+ AltRomname = reader.Line[13],
+ AltTitle = reader.Line[14],
+ Extra = reader.Line[15],
+ Buttons = reader.Line[16],
+ };
+
+ // If we have additional fields
+ if (reader.Line.Count > HeaderWithoutRomnameCount)
+ row.ADDITIONAL_ELEMENTS = reader.Line.Skip(HeaderWithoutRomnameCount).ToArray();
+ }
+ else
+ {
+ row = new Row
+ {
+ Name = reader.Line[0],
+ Title = reader.Line[1],
+ Emulator = reader.Line[2],
+ CloneOf = reader.Line[3],
+ Year = reader.Line[4],
+ Manufacturer = reader.Line[5],
+ Category = reader.Line[6],
+ Players = reader.Line[7],
+ Rotation = reader.Line[8],
+ Control = reader.Line[9],
+ Status = reader.Line[10],
+ DisplayCount = reader.Line[11],
+ DisplayType = reader.Line[12],
+ AltRomname = reader.Line[13],
+ AltTitle = reader.Line[14],
+ Extra = reader.Line[15],
+ Buttons = reader.Line[16],
+ };
+
+ // If we have additional fields
+ if (reader.Line.Count > HeaderWithRomnameCount)
+ row.ADDITIONAL_ELEMENTS = reader.Line.Skip(HeaderWithRomnameCount).ToArray();
+ }
+
+ rows.Add(row);
+ }
+
+ // Assign the rows to the Dat and return
+ dat.Row = rows.ToArray();
+ return dat;
+ }
+ catch
+ {
+ // TODO: Handle logging the exception
+ return default;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/SabreTools.Serialization/SabreTools.Serialization.csproj b/SabreTools.Serialization/SabreTools.Serialization.csproj
index abcdc453..c1595d16 100644
--- a/SabreTools.Serialization/SabreTools.Serialization.csproj
+++ b/SabreTools.Serialization/SabreTools.Serialization.csproj
@@ -6,6 +6,7 @@
+
diff --git a/SabreTools.Test/Parser/SerializationTests.cs b/SabreTools.Test/Parser/SerializationTests.cs
index 45a64f30..30214788 100644
--- a/SabreTools.Test/Parser/SerializationTests.cs
+++ b/SabreTools.Test/Parser/SerializationTests.cs
@@ -1,5 +1,4 @@
using System;
-using System.Xml.Serialization;
using Xunit;
namespace SabreTools.Test.Parser
@@ -29,6 +28,26 @@ namespace SabreTools.Test.Parser
}
}
+ [Fact]
+ public void AttractModeDeserializeTest()
+ {
+ // Open the file for reading
+ string filename = System.IO.Path.Combine(Environment.CurrentDirectory, "TestData", "test-attractmode-files.txt");
+
+ // Deserialize the file
+ var dat = Serialization.AttractMode.Deserialize(filename);
+
+ // Validate the values
+ Assert.NotNull(dat?.Row);
+ Assert.Equal(11, dat.Row.Length);
+
+ // Validate we're not missing any attributes or elements
+ foreach (var file in dat.Row)
+ {
+ Assert.Null(file.ADDITIONAL_ELEMENTS);
+ }
+ }
+
[Fact]
public void ListxmlDeserializeTest()
{
diff --git a/SabreTools.Test/TestData/test-attractmode-files.txt b/SabreTools.Test/TestData/test-attractmode-files.txt
new file mode 100644
index 00000000..8a8565d7
--- /dev/null
+++ b/SabreTools.Test/TestData/test-attractmode-files.txt
@@ -0,0 +1,12 @@
+#Romname;Title;Emulator;Cloneof;Year;Manufacturer;Category;Players;Rotation;Control;Status;DisplayCount;DisplayType;AltRomname;AltTitle;Extra;Buttons;Favourite;Tags;PlayedCount;PlayedTime;FileIsAvailable
+005;005;advmame;;1981;Sega;Maze / Shooter Small;2P alt;270;joystick (4-way),joystick (4-way);imperfect;1;raster;;;;1;;;0;;1
+alpine;Alpine Ski (set 1);advmame;;1982;Taito Corporation;Sports / Skiing;2P alt;270;joystick (2-way),joystick (2-way);good;1;raster;;;;1;;;0;;1
+amidar;Amidar;advmame;;1982;Konami;Maze / Outline;2P alt;90;joystick (4-way),joystick (4-way);good;1;raster;;;;1;;;0;;1
+anteater;Anteater;advmame;;1982;Tago Electronics;Maze / Collect;2P alt;90;joystick (4-way);good;1;raster;;;;1;;;0;;1
+armorcar;Armored Car (set 1);advmame;;1981;Stern Electronics;Maze / Collect;2P alt;90;joystick (4-way),joystick (4-way);good;1;raster;;;;2;;;0;;1
+assault;Assault (Rev B);advmame;;1988;Namco;Shooter / Driving;1P;90;double joystick (4-way),double joystick (4-way);good;1;raster;;;;1;;;0;;1
+astrob;Astro Blaster (version 3);advmame;;1981;Sega;Shooter / Gallery;2P alt;270;joystick (2-way),joystick (2-way);imperfect;1;raster;;;;2;;;0;;1
+astrof;Astro Fighter (set 1);advmame;;1979;Data East;Shooter / Gallery;2P alt;90;joystick (2-way),joystick (2-way);imperfect;1;raster;;;;1;;;0;;1
+attckufo;Attack UFO;advmame;;1980;Ryoto Electric Co.;Shooter / Gallery;2P alt;270;joystick (2-way),joystick (2-way);good;1;raster;;;;1;;;0;;1
+bagman;Bagman;advmame;;1982;Valadon Automation;Platform / Run Jump;2P alt;270;joystick (4-way),joystick (4-way);good;1;raster;;;;1;;;0;;1
+ballbomb;Balloon Bomber;advmame;;1980;Taito;Shooter / Gallery;2P alt;270;joystick (2-way),joystick (2-way);imperfect;1;raster;;;;1;;;0;;1
\ No newline at end of file