diff --git a/CrossModel/RomCenter.Deserializer.cs b/CrossModel/RomCenter.Deserializer.cs new file mode 100644 index 00000000..46c90112 --- /dev/null +++ b/CrossModel/RomCenter.Deserializer.cs @@ -0,0 +1,125 @@ +using System; +using System.Linq; +using SabreTools.Models.RomCenter; + +namespace SabreTools.Serialization.CrossModel +{ + public partial class RomCenter : 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 header = obj.Read(Models.Metadata.MetadataFile.HeaderKey); + var metadataFile = header != null ? ConvertHeaderFromInternalModel(header) : new MetadataFile(); + + var machines = obj.Read(Models.Metadata.MetadataFile.MachineKey); + if (machines != null && machines.Any()) + { + metadataFile.Games = new Games + { + Rom = machines + .Where(m => m != null) + .SelectMany(ConvertMachineFromInternalModel) + .ToArray() + }; + } + + return metadataFile; + } + + /// + /// Convert from to + /// + private static MetadataFile ConvertHeaderFromInternalModel(Models.Metadata.Header item) + { + var metadataFile = new MetadataFile(); + + if (item.ContainsKey(Models.Metadata.Header.AuthorKey) + || item.ContainsKey(Models.Metadata.Header.VersionKey) + || item.ContainsKey(Models.Metadata.Header.EmailKey) + || item.ContainsKey(Models.Metadata.Header.HomepageKey) + || item.ContainsKey(Models.Metadata.Header.UrlKey) + || item.ContainsKey(Models.Metadata.Header.DateKey) + || item.ContainsKey(Models.Metadata.Header.CommentKey)) + { + metadataFile.Credits = new Credits + { + Author = item.ReadString(Models.Metadata.Header.AuthorKey), + Version = item.ReadString(Models.Metadata.Header.VersionKey), + Email = item.ReadString(Models.Metadata.Header.EmailKey), + Homepage = item.ReadString(Models.Metadata.Header.HomepageKey), + Url = item.ReadString(Models.Metadata.Header.UrlKey), + Date = item.ReadString(Models.Metadata.Header.DateKey), + Comment = item.ReadString(Models.Metadata.Header.CommentKey), + }; + } + + if (item.ContainsKey(Models.Metadata.Header.DatVersionKey) + || item.ContainsKey(Models.Metadata.Header.PluginKey) + || item.ContainsKey(Models.Metadata.Header.ForceMergingKey)) + { + metadataFile.Dat = new Dat + { + Version = item.ReadString(Models.Metadata.Header.DatVersionKey), + Plugin = item.ReadString(Models.Metadata.Header.PluginKey), + Split = item.ReadString(Models.Metadata.Header.ForceMergingKey) == "split" ? "yes" : "no", + Merge = item.ReadString(Models.Metadata.Header.ForceMergingKey) == "merge" ? "yes" : "no", + }; + } + + if (item.ContainsKey(Models.Metadata.Header.RefNameKey) + || item.ContainsKey(Models.Metadata.Header.EmulatorVersionKey)) + { + metadataFile.Emulator = new Emulator + { + RefName = item.ReadString(Models.Metadata.Header.RefNameKey), + Version = item.ReadString(Models.Metadata.Header.EmulatorVersionKey), + }; + } + + return metadataFile; + } + + /// + /// Convert from to an array of + /// + private static Rom[] ConvertMachineFromInternalModel(Models.Metadata.Machine item) + { + var roms = item.Read(Models.Metadata.Machine.RomKey); + if (roms == null) + return Array.Empty(); + + return roms + .Where(r => r != null) + .Select(rom => ConvertFromInternalModel(rom, item)) + .ToArray(); + } + + /// + /// Convert from to + /// + private static Rom ConvertFromInternalModel(Models.Metadata.Rom item, Models.Metadata.Machine parent) + { + var row = new Rom + { + RomName = item.ReadString(Models.Metadata.Rom.NameKey), + RomCRC = item.ReadString(Models.Metadata.Rom.CRCKey), + RomSize = item.ReadString(Models.Metadata.Rom.SizeKey), + MergeName = item.ReadString(Models.Metadata.Rom.MergeKey), + + ParentName = parent.ReadString(Models.Metadata.Machine.RomOfKey), + //ParentDescription = parent.ReadString(Models.Metadata.Machine.ParentDescriptionKey), // This is unmappable + GameName = parent.ReadString(Models.Metadata.Machine.NameKey), + GameDescription = parent.ReadString(Models.Metadata.Machine.DescriptionKey), + }; + return row; + } + } +} \ No newline at end of file diff --git a/CrossModel/RomCenter.Serializer.cs b/CrossModel/RomCenter.Serializer.cs new file mode 100644 index 00000000..126d1d44 --- /dev/null +++ b/CrossModel/RomCenter.Serializer.cs @@ -0,0 +1,103 @@ +using System.Linq; +using SabreTools.Models.RomCenter; + +namespace SabreTools.Serialization.CrossModel +{ + public partial class RomCenter : 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 + { + [Models.Metadata.MetadataFile.HeaderKey] = ConvertHeaderToInternalModel(obj), + }; + + if (obj?.Games?.Rom != null && obj.Games.Rom.Any()) + { + metadataFile[Models.Metadata.MetadataFile.MachineKey] = obj.Games.Rom + .Where(r => r != null) + .Select(ConvertMachineToInternalModel).ToArray(); + } + + return metadataFile; + } + + /// + /// Convert from to + /// + private static Models.Metadata.Header ConvertHeaderToInternalModel(MetadataFile item) + { + var header = new Models.Metadata.Header(); + + if (item.Credits != null) + { + header[Models.Metadata.Header.AuthorKey] = item.Credits.Author; + header[Models.Metadata.Header.VersionKey] = item.Credits.Version; + header[Models.Metadata.Header.EmailKey] = item.Credits.Email; + header[Models.Metadata.Header.HomepageKey] = item.Credits.Homepage; + header[Models.Metadata.Header.UrlKey] = item.Credits.Url; + header[Models.Metadata.Header.DateKey] = item.Credits.Date; + header[Models.Metadata.Header.CommentKey] = item.Credits.Comment; + } + + if (item.Dat != null) + { + header[Models.Metadata.Header.DatVersionKey] = item.Dat.Version; + header[Models.Metadata.Header.PluginKey] = item.Dat.Plugin; + + if (item.Dat.Split == "yes" || item.Dat.Split == "1") + header[Models.Metadata.Header.ForceMergingKey] = "split"; + else if (item.Dat.Merge == "yes" || item.Dat.Merge == "1") + header[Models.Metadata.Header.ForceMergingKey] = "merge"; + } + + if (item.Emulator != null) + { + header[Models.Metadata.Header.RefNameKey] = item.Emulator.RefName; + header[Models.Metadata.Header.EmulatorVersionKey] = item.Emulator.Version; + } + + return header; + } + + /// + /// Convert from to + /// + private static Models.Metadata.Machine ConvertMachineToInternalModel(Rom item) + { + var machine = new Models.Metadata.Machine + { + [Models.Metadata.Machine.RomOfKey] = item.ParentName, + //[Models.Metadata.Machine.ParentDescriptionKey] = item.ParentDescription, // This is unmappable + [Models.Metadata.Machine.NameKey] = item.GameName, + [Models.Metadata.Machine.DescriptionKey] = item.GameDescription, + [Models.Metadata.Machine.RomKey] = new Models.Metadata.Rom[] { ConvertToInternalModel(item) }, + }; + + return machine; + } + + /// + /// Convert from to + /// + private static Models.Metadata.Rom ConvertToInternalModel(Rom item) + { + var rom = new Models.Metadata.Rom + { + [Models.Metadata.Rom.NameKey] = item.RomName, + [Models.Metadata.Rom.CRCKey] = item.RomCRC, + [Models.Metadata.Rom.SizeKey] = item.RomSize, + [Models.Metadata.Rom.MergeKey] = item.MergeName, + }; + return rom; + } + } +} \ No newline at end of file diff --git a/Files/RomCenter.Deserializer.cs b/Files/RomCenter.Deserializer.cs new file mode 100644 index 00000000..ca8ada2e --- /dev/null +++ b/Files/RomCenter.Deserializer.cs @@ -0,0 +1,20 @@ +using SabreTools.Models.RomCenter; + +namespace SabreTools.Serialization.Files +{ + public partial class RomCenter : IFileSerializer + { + /// +#if NET48 + public MetadataFile Deserialize(string path) +#else + public MetadataFile? Deserialize(string? path) +#endif + { + using (var stream = PathProcessor.OpenStream(path)) + { + return new Streams.RomCenter().Deserialize(stream); + } + } + } +} \ No newline at end of file diff --git a/Files/RomCenter.Serializer.cs b/Files/RomCenter.Serializer.cs new file mode 100644 index 00000000..1f71d15a --- /dev/null +++ b/Files/RomCenter.Serializer.cs @@ -0,0 +1,27 @@ +using SabreTools.Models.RomCenter; + +namespace SabreTools.Serialization.Files +{ + public partial class RomCenter : IFileSerializer + { + /// +#if NET48 + public bool Serialize(MetadataFile obj, string path) +#else + public bool Serialize(MetadataFile? obj, string? path) +#endif + { + using (var stream = new Streams.RomCenter().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/Logiqx.Serializer.cs b/Streams/Logiqx.Serializer.cs index bf7047c6..61452d63 100644 --- a/Streams/Logiqx.Serializer.cs +++ b/Streams/Logiqx.Serializer.cs @@ -3,7 +3,7 @@ using SabreTools.Models.Logiqx; namespace SabreTools.Serialization.Streams { - public partial class Logiqx : XmlFile + public partial class Logiqx : XmlFile { /// #if NET48 diff --git a/Streams/RomCenter.Deserializer.cs b/Streams/RomCenter.Deserializer.cs new file mode 100644 index 00000000..02f73b6a --- /dev/null +++ b/Streams/RomCenter.Deserializer.cs @@ -0,0 +1,252 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using SabreTools.IO.Readers; +using SabreTools.Models.RomCenter; + +namespace SabreTools.Serialization.Streams +{ + public partial class RomCenter : 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 IniReader(data, Encoding.UTF8) + { + ValidateRows = false, + }; + var dat = new MetadataFile(); + + // Loop through and parse out the values + var roms = new List(); + var additional = new List(); + var creditsAdditional = new List(); + var datAdditional = new List(); + var emulatorAdditional = new List(); + var gamesAdditional = new List(); + while (!reader.EndOfStream) + { + // If we have no next line + if (!reader.ReadNextLine()) + break; + + // Ignore certain row types + switch (reader.RowType) + { + case IniRowType.None: + case IniRowType.Comment: + continue; + case IniRowType.SectionHeader: + switch (reader.Section?.ToLowerInvariant()) + { +#if NET48 + case "credits": + if (dat.Credits == null) + dat.Credits = new Credits(); + break; + case "dat": + if (dat.Dat == null) + dat.Dat = new Dat(); + break; + case "emulator": + if (dat.Emulator == null) + dat.Emulator = new Emulator(); + break; + case "games": + if (dat.Games == null) + dat.Games = new Games(); + break; +#else + case "credits": + dat.Credits ??= new Credits(); + break; + case "dat": + dat.Dat ??= new Dat(); + break; + case "emulator": + dat.Emulator ??= new Emulator(); + break; + case "games": + dat.Games ??= new Games(); + break; +#endif + default: + if (reader.CurrentLine != null) + additional.Add(reader.CurrentLine); + break; + } + continue; + } + + // If we're in credits + if (reader.Section?.ToLowerInvariant() == "credits") + { + // Create the section if we haven't already +#if NET48 + dat.Credits = dat.Credits != null ? dat.Credits : new Credits(); +#else + dat.Credits ??= new Credits(); +#endif + + switch (reader.KeyValuePair?.Key?.ToLowerInvariant()) + { + case "author": + dat.Credits.Author = reader.KeyValuePair?.Value; + break; + case "version": + dat.Credits.Version = reader.KeyValuePair?.Value; + break; + case "email": + dat.Credits.Email = reader.KeyValuePair?.Value; + break; + case "homepage": + dat.Credits.Homepage = reader.KeyValuePair?.Value; + break; + case "url": + dat.Credits.Url = reader.KeyValuePair?.Value; + break; + case "date": + dat.Credits.Date = reader.KeyValuePair?.Value; + break; + case "comment": + dat.Credits.Comment = reader.KeyValuePair?.Value; + break; + default: + if (reader.CurrentLine != null) + creditsAdditional.Add(reader.CurrentLine); + break; + } + } + + // If we're in dat + else if (reader.Section?.ToLowerInvariant() == "dat") + { + // Create the section if we haven't already +#if NET48 + dat.Dat = dat.Dat != null ? dat.Dat : new Dat(); +#else + dat.Dat ??= new Dat(); +#endif + + switch (reader.KeyValuePair?.Key?.ToLowerInvariant()) + { + case "version": + dat.Dat.Version = reader.KeyValuePair?.Value; + break; + case "plugin": + dat.Dat.Plugin = reader.KeyValuePair?.Value; + break; + case "split": + dat.Dat.Split = reader.KeyValuePair?.Value; + break; + case "merge": + dat.Dat.Merge = reader.KeyValuePair?.Value; + break; + default: + if (reader.CurrentLine != null) + datAdditional.Add(reader.CurrentLine); + break; + } + } + + // If we're in emulator + else if (reader.Section?.ToLowerInvariant() == "emulator") + { + // Create the section if we haven't already +#if NET48 + dat.Emulator = dat.Emulator != null ? dat.Emulator : new Emulator(); +#else + dat.Emulator ??= new Emulator(); +#endif + + switch (reader.KeyValuePair?.Key?.ToLowerInvariant()) + { + case "refname": + dat.Emulator.RefName = reader.KeyValuePair?.Value; + break; + case "version": + dat.Emulator.Version = reader.KeyValuePair?.Value; + break; + default: + if (reader.CurrentLine != null) + emulatorAdditional.Add(reader.CurrentLine); + break; + } + } + + // If we're in games + else if (reader.Section?.ToLowerInvariant() == "games") + { + // Create the section if we haven't already +#if NET48 + dat.Games = dat.Games != null ? dat.Games : new Games(); +#else + dat.Games ??= new Games(); +#endif + + // If the line doesn't contain the delimiter + if (!(reader.CurrentLine?.Contains('¬') ?? false)) + { + if (reader.CurrentLine != null) + gamesAdditional.Add(reader.CurrentLine); + + continue; + } + + // Otherwise, separate out the line + string[] splitLine = reader.CurrentLine.Split('¬'); + var rom = new Rom + { + // EMPTY = splitLine[0] + ParentName = splitLine[1], + ParentDescription = splitLine[2], + GameName = splitLine[3], + GameDescription = splitLine[4], + RomName = splitLine[5], + RomCRC = splitLine[6], + RomSize = splitLine[7], + RomOf = splitLine[8], + MergeName = splitLine[9], + // EMPTY = splitLine[10] + }; + + if (splitLine.Length > 11) + rom.ADDITIONAL_ELEMENTS = splitLine.Skip(11).ToArray(); + + roms.Add(rom); + } + + else + { + if (reader.CurrentLine != null) + additional.Add(reader.CurrentLine); + } + } + + // Add extra pieces and return + dat.ADDITIONAL_ELEMENTS = additional.Where(s => s != null).ToArray(); + if (dat.Credits != null) + dat.Credits.ADDITIONAL_ELEMENTS = creditsAdditional.Where(s => s != null).ToArray(); + if (dat.Dat != null) + dat.Dat.ADDITIONAL_ELEMENTS = datAdditional.Where(s => s != null).ToArray(); + if (dat.Emulator != null) + dat.Emulator.ADDITIONAL_ELEMENTS = emulatorAdditional.Where(s => s != null).ToArray(); + if (dat.Games != null) + { + dat.Games.Rom = roms.ToArray(); + dat.Games.ADDITIONAL_ELEMENTS = gamesAdditional.Where(s => s != null).Select(s => s).ToArray(); + } + return dat; + } + } +} \ No newline at end of file diff --git a/Streams/RomCenter.Serializer.cs b/Streams/RomCenter.Serializer.cs new file mode 100644 index 00000000..5716f20e --- /dev/null +++ b/Streams/RomCenter.Serializer.cs @@ -0,0 +1,172 @@ +using System.IO; +using System.Linq; +using System.Text; +using SabreTools.IO.Writers; +using SabreTools.Models.RomCenter; + +namespace SabreTools.Serialization.Streams +{ + public partial class RomCenter : 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 IniWriter(stream, Encoding.UTF8); + + // Write out the credits section + WriteCredits(obj.Credits, writer); + + // Write out the dat section + WriteDat(obj.Dat, writer); + + // Write out the emulator section + WriteEmulator(obj.Emulator, writer); + + // Write out the games + WriteGames(obj.Games, writer); + + // Return the stream + stream.Seek(0, SeekOrigin.Begin); + return stream; + } + + /// + /// Write credits information to the current writer + /// + /// Credits object representing the credits information + /// IniWriter representing the output +#if NET48 + private static void WriteCredits(Credits credits, IniWriter writer) +#else + private static void WriteCredits(Credits? credits, IniWriter writer) +#endif + { + // If the credits information is missing, we can't do anything + if (credits == null) + return; + + writer.WriteSection("credits"); + + writer.WriteKeyValuePair("author", credits.Author); + writer.WriteKeyValuePair("version", credits.Version); + writer.WriteKeyValuePair("email", credits.Email); + writer.WriteKeyValuePair("homepage", credits.Homepage); + writer.WriteKeyValuePair("url", credits.Url); + writer.WriteKeyValuePair("date", credits.Date); + writer.WriteKeyValuePair("comment", credits.Comment); + writer.WriteLine(); + + writer.Flush(); + } + + /// + /// Write dat information to the current writer + /// + /// Dat object representing the dat information + /// IniWriter representing the output +#if NET48 + private static void WriteDat(Dat dat, IniWriter writer) +#else + private static void WriteDat(Dat? dat, IniWriter writer) +#endif + { + // If the dat information is missing, we can't do anything + if (dat == null) + return; + + writer.WriteSection("dat"); + + writer.WriteKeyValuePair("version", dat.Version); + writer.WriteKeyValuePair("plugin", dat.Plugin); + writer.WriteKeyValuePair("split", dat.Split); + writer.WriteKeyValuePair("merge", dat.Merge); + writer.WriteLine(); + + writer.Flush(); + } + + /// + /// Write emulator information to the current writer + /// + /// Emulator object representing the emulator information + /// IniWriter representing the output +#if NET48 + private static void WriteEmulator(Emulator emulator, IniWriter writer) +#else + private static void WriteEmulator(Emulator? emulator, IniWriter writer) +#endif + { + // If the emulator information is missing, we can't do anything + if (emulator == null) + return; + + writer.WriteSection("emulator"); + + writer.WriteKeyValuePair("refname", emulator.RefName); + writer.WriteKeyValuePair("version", emulator.Version); + writer.WriteLine(); + + writer.Flush(); + } + + /// + /// Write games information to the current writer + /// + /// Games object representing the games information + /// IniWriter representing the output +#if NET48 + private static void WriteGames(Games games, IniWriter writer) +#else + private static void WriteGames(Games? games, IniWriter writer) +#endif + { + // If the games information is missing, we can't do anything + if (games?.Rom == null || !games.Rom.Any()) + return; + + writer.WriteSection("games"); + + foreach (var rom in games.Rom) + { + var romBuilder = new StringBuilder(); + + romBuilder.Append('¬'); + romBuilder.Append(rom.ParentName); + romBuilder.Append('¬'); + romBuilder.Append(rom.ParentDescription); + romBuilder.Append('¬'); + romBuilder.Append(rom.GameName); + romBuilder.Append('¬'); + romBuilder.Append(rom.GameDescription); + romBuilder.Append('¬'); + romBuilder.Append(rom.RomName); + romBuilder.Append('¬'); + romBuilder.Append(rom.RomCRC); + romBuilder.Append('¬'); + romBuilder.Append(rom.RomSize); + romBuilder.Append('¬'); + romBuilder.Append(rom.RomOf); + romBuilder.Append('¬'); + romBuilder.Append(rom.MergeName); + romBuilder.Append('¬'); + romBuilder.Append('\n'); + + writer.WriteString(romBuilder.ToString()); + writer.Flush(); + } + + writer.WriteLine(); + writer.Flush(); + } + } +} \ No newline at end of file