diff --git a/CrossModel/ClrMamePro.Deserializer.cs b/CrossModel/ClrMamePro.Deserializer.cs index 79662031..d370a27f 100644 --- a/CrossModel/ClrMamePro.Deserializer.cs +++ b/CrossModel/ClrMamePro.Deserializer.cs @@ -1,4 +1,3 @@ -using System; using System.Linq; using SabreTools.Models.ClrMamePro; diff --git a/CrossModel/DosCenter.Deserializer.cs b/CrossModel/DosCenter.Deserializer.cs new file mode 100644 index 00000000..1c975a91 --- /dev/null +++ b/CrossModel/DosCenter.Deserializer.cs @@ -0,0 +1,91 @@ +using System.Linq; +using SabreTools.Models.DosCenter; + +namespace SabreTools.Serialization.CrossModel +{ + public partial class DosCenter : IModelSerializer + { + /// +#if NET48 + public MetadataFile Deserialize(Models.Metadata.MetadataFile obj) +#else + public MetadataFile? Deserialize(Models.Metadata.MetadataFile? obj) +#endif + { + if (obj == null) + return null; + + var metadataFile = new MetadataFile(); + + var header = obj.Read(Models.Metadata.MetadataFile.HeaderKey); + if (header != null) + metadataFile.DosCenter = ConvertHeaderFromInternalModel(header); + + var machines = obj.Read(Models.Metadata.MetadataFile.MachineKey); + if (machines != null && machines.Any()) + { + metadataFile.Game = machines + .Where(m => m != null) + .Select(ConvertMachineFromInternalModel) + .ToArray(); + } + + return metadataFile; + } + + /// + /// Convert from to + /// + private static Models.DosCenter.DosCenter ConvertHeaderFromInternalModel(Models.Metadata.Header item) + { + var dosCenter = new Models.DosCenter.DosCenter + { + Name = item.ReadString(Models.Metadata.Header.NameKey), + Description = item.ReadString(Models.Metadata.Header.DescriptionKey), + Version = item.ReadString(Models.Metadata.Header.VersionKey), + Date = item.ReadString(Models.Metadata.Header.DateKey), + Author = item.ReadString(Models.Metadata.Header.AuthorKey), + Homepage = item.ReadString(Models.Metadata.Header.HomepageKey), + Comment = item.ReadString(Models.Metadata.Header.CommentKey), + }; + return dosCenter; + } + + /// + /// Convert from to + /// + private static Game ConvertMachineFromInternalModel(Models.Metadata.Machine item) + { + var game = new Game + { + Name = item.ReadString(Models.Metadata.Machine.NameKey), + }; + + var roms = item.Read(Models.Metadata.Machine.RomKey); + if (roms != null && roms.Any()) + { + game.File = roms + .Where(r => r != null) + .Select(ConvertFromInternalModel) + .ToArray(); + } + + return game; + } + + /// + /// Convert from to + /// + private static Models.DosCenter.File ConvertFromInternalModel(Models.Metadata.Rom item) + { + var file = new Models.DosCenter.File + { + Name = item.ReadString(Models.Metadata.Rom.NameKey), + Size = item.ReadString(Models.Metadata.Rom.SizeKey), + CRC = item.ReadString(Models.Metadata.Rom.CRCKey), + Date = item.ReadString(Models.Metadata.Rom.DateKey), + }; + return file; + } + } +} \ No newline at end of file diff --git a/CrossModel/DosCenter.Serializer.cs b/CrossModel/DosCenter.Serializer.cs new file mode 100644 index 00000000..bd5ff417 --- /dev/null +++ b/CrossModel/DosCenter.Serializer.cs @@ -0,0 +1,88 @@ +using System.Linq; +using SabreTools.Models.DosCenter; + +namespace SabreTools.Serialization.CrossModel +{ + public partial class DosCenter : IModelSerializer + { + /// +#if NET48 + public Models.Metadata.MetadataFile Serialize(MetadataFile obj) +#else + public Models.Metadata.MetadataFile? Serialize(MetadataFile? obj) +#endif + { + if (obj == null) + return null; + + var metadataFile = new Models.Metadata.MetadataFile(); + + if (obj?.DosCenter != null) + metadataFile[Models.Metadata.MetadataFile.HeaderKey] = ConvertHeaderToInternalModel(obj.DosCenter); + + if (obj?.Game != null && obj.Game.Any()) + { + metadataFile[Models.Metadata.MetadataFile.MachineKey] = obj.Game + .Where(g => g != null) + .Select(ConvertMachineToInternalModel) + .ToArray(); + } + + return metadataFile; + } + + /// + /// Convert from to + /// + private static Models.Metadata.Header ConvertHeaderToInternalModel(Models.DosCenter.DosCenter item) + { + var header = new Models.Metadata.Header + { + [Models.Metadata.Header.NameKey] = item.Name, + [Models.Metadata.Header.DescriptionKey] = item.Description, + [Models.Metadata.Header.VersionKey] = item.Version, + [Models.Metadata.Header.DateKey] = item.Date, + [Models.Metadata.Header.AuthorKey] = item.Author, + [Models.Metadata.Header.HomepageKey] = item.Homepage, + [Models.Metadata.Header.CommentKey] = item.Comment, + }; + return header; + } + + /// + /// Convert from to + /// + private static Models.Metadata.Machine ConvertMachineToInternalModel(Game item) + { + var machine = new Models.Metadata.Machine + { + [Models.Metadata.Machine.NameKey] = item.Name, + }; + + if (item.File != null && item.File.Any()) + { + machine[Models.Metadata.Machine.RomKey] = item.File + .Where(f => f != null) + .Select(ConvertToInternalModel) + .ToArray(); + } + + return machine; + } + + /// + /// Convert from to + /// + private static Models.Metadata.Rom ConvertToInternalModel(Models.DosCenter.File item) + { + var rom = new Models.Metadata.Rom + { + [Models.Metadata.Rom.NameKey] = item.Name, + [Models.Metadata.Rom.SizeKey] = item.Size, + [Models.Metadata.Rom.CRCKey] = item.CRC, + [Models.Metadata.Rom.DateKey] = item.Date, + }; + return rom; + } + } +} \ No newline at end of file diff --git a/Files/DosCenter.Deserializer.cs b/Files/DosCenter.Deserializer.cs new file mode 100644 index 00000000..cce38e5e --- /dev/null +++ b/Files/DosCenter.Deserializer.cs @@ -0,0 +1,20 @@ +using SabreTools.Models.DosCenter; + +namespace SabreTools.Serialization.Files +{ + public partial class DosCenter : IFileSerializer + { + /// +#if NET48 + public MetadataFile Deserialize(string path) +#else + public MetadataFile? Deserialize(string? path) +#endif + { + using (var stream = PathProcessor.OpenStream(path)) + { + return new Streams.DosCenter().Deserialize(stream); + } + } + } +} \ No newline at end of file diff --git a/Files/DosCenter.Serializer.cs b/Files/DosCenter.Serializer.cs new file mode 100644 index 00000000..d0de8429 --- /dev/null +++ b/Files/DosCenter.Serializer.cs @@ -0,0 +1,27 @@ +using SabreTools.Models.DosCenter; + +namespace SabreTools.Serialization.Files +{ + public partial class DosCenter : IFileSerializer + { + /// +#if NET48 + public bool Serialize(MetadataFile obj, string path) +#else + public bool Serialize(MetadataFile? obj, string? path) +#endif + { + using (var stream = new Streams.DosCenter().Serialize(obj)) + { + if (stream == null) + return false; + + using (var fs = System.IO.File.OpenWrite(path)) + { + stream.CopyTo(fs); + return true; + } + } + } + } +} \ No newline at end of file diff --git a/Streams/DosCenter.Deserializer.cs b/Streams/DosCenter.Deserializer.cs new file mode 100644 index 00000000..798911be --- /dev/null +++ b/Streams/DosCenter.Deserializer.cs @@ -0,0 +1,238 @@ +using System.Collections.Generic; +using System.IO; +using System.Text; +using SabreTools.IO.Readers; +using SabreTools.Models.DosCenter; + +namespace SabreTools.Serialization.Streams +{ + public partial class DosCenter : IStreamSerializer + { + /// +#if NET48 + public MetadataFile Deserialize(Stream data) +#else + public MetadataFile? Deserialize(Stream? data) +#endif + { + // If the stream is null + if (data == null) + return default; + + // Setup the reader and output + var reader = new ClrMameProReader(data, Encoding.UTF8) { DosCenter = true }; + var dat = new MetadataFile(); + + // Loop through and parse out the values +#if NET48 + string lastTopLevel = reader.TopLevel; +#else + string? lastTopLevel = reader.TopLevel; +#endif + +#if NET48 + Game game = null; +#else + Game? game = null; +#endif + var games = new List(); + var files = new List(); + + var additional = new List(); + var headerAdditional = new List(); + var gameAdditional = 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": + if (dat.DosCenter != null) + dat.DosCenter.ADDITIONAL_ELEMENTS = headerAdditional.ToArray(); + + headerAdditional.Clear(); + break; + case "game": + if (game != null) + { + 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: + if (reader.CurrentLine != null) + 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 +#if NET48 + dat.DosCenter = dat.DosCenter != null ? dat.DosCenter : new Models.DosCenter.DosCenter(); +#else + dat.DosCenter ??= new Models.DosCenter.DosCenter(); +#endif + + 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: + if (reader.CurrentLine != null) + 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 +#if NET48 + game = game != null ? game : new Game(); +#else + game ??= new Game(); +#endif + + switch (reader.Standalone?.Key?.ToLowerInvariant()) + { + case "name": + game.Name = reader.Standalone?.Value; + break; + default: + if (reader.CurrentLine != null) + gameAdditional.Add(item: reader.CurrentLine); + break; + } + } + + // If we're in a file block + else if (reader.TopLevel == "game" && reader.RowType == CmpRowType.Internal) + { + // If we have an unknown type, log it + if (reader.InternalName != "file") + { + if (reader.CurrentLine != null) + gameAdditional.Add(reader.CurrentLine); + continue; + } + + // Create the file and add to the list + var file = CreateFile(reader); + if (file != null) + files.Add(file); + } + + else + { + if (reader.CurrentLine != null) + additional.Add(item: reader.CurrentLine); + } + } + + // Add extra pieces and return + dat.Game = games.ToArray(); + dat.ADDITIONAL_ELEMENTS = additional.ToArray(); + return dat; + } + + /// + /// Create a File object from the current reader context + /// + /// ClrMameProReader representing the metadata file + /// File object created from the reader context +#if NET48 + private static Models.DosCenter.File CreateFile(ClrMameProReader reader) +#else + private static Models.DosCenter.File? CreateFile(ClrMameProReader reader) +#endif + { + if (reader.Internal == null) + return null; + + 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: + if (reader.CurrentLine != null) + itemAdditional.Add(item: reader.CurrentLine); + break; + } + } + + file.ADDITIONAL_ELEMENTS = itemAdditional.ToArray(); + return file; + } + } +} \ No newline at end of file diff --git a/Streams/DosCenter.Serializer.cs b/Streams/DosCenter.Serializer.cs new file mode 100644 index 00000000..d0c10ec2 --- /dev/null +++ b/Streams/DosCenter.Serializer.cs @@ -0,0 +1,142 @@ +using System.IO; +using System.Linq; +using System.Text; +using SabreTools.IO.Writers; +using SabreTools.Models.DosCenter; + +namespace SabreTools.Serialization.Streams +{ + public partial class DosCenter : IStreamSerializer + { + /// +#if NET48 + public Stream Serialize(MetadataFile obj) +#else + public Stream? Serialize(MetadataFile? obj) +#endif + { + // If the metadata file is null + if (obj == 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(obj.DosCenter, writer); + + // Write out the games, if they exist + WriteGames(obj.Game, writer); + + // Return the stream + stream.Seek(0, SeekOrigin.Begin); + return stream; + } + + /// + /// Write header information to the current writer + /// + /// DosCenter representing the header information + /// ClrMameProWriter representing the output +#if NET48 + private static void WriteHeader(Models.DosCenter.DosCenter header, ClrMameProWriter writer) +#else + private static void WriteHeader(Models.DosCenter.DosCenter? header, ClrMameProWriter writer) +#endif + { + // 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 +#if NET48 + private static void WriteGames(Game[] games, ClrMameProWriter writer) +#else + private static void WriteGames(Game[]? games, ClrMameProWriter writer) +#endif + { + // 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 +#if NET48 + private static void WriteFiles(Models.DosCenter.File[] files, ClrMameProWriter writer) +#else + private static void WriteFiles(Models.DosCenter.File[]? files, ClrMameProWriter writer) +#endif + { + // 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