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