diff --git a/SabreTools.DatFiles/Formats/SeparatedValue.Reader.cs b/SabreTools.DatFiles/Formats/SeparatedValue.Reader.cs new file mode 100644 index 00000000..445047a8 --- /dev/null +++ b/SabreTools.DatFiles/Formats/SeparatedValue.Reader.cs @@ -0,0 +1,151 @@ +using System; +using System.Linq; +using SabreTools.Core; +using SabreTools.Core.Tools; +using SabreTools.DatItems; +using SabreTools.DatItems.Formats; + +namespace SabreTools.DatFiles.Formats +{ + /// + /// Represents parsing a value-separated DAT + /// + internal partial class SeparatedValue : DatFile + { + /// + public override void ParseFile(string filename, int indexId, bool keep, bool statsOnly = false, bool throwOnError = false) + { + try + { + // Deserialize the input file + var metadataFile = Serialization.SeparatedValue.Deserialize(filename, _delim); + + // Convert the row data to the internal format + ConvertRows(metadataFile?.Row, filename, indexId, statsOnly); + } + catch (Exception ex) when (!throwOnError) + { + string message = $"'{filename}' - An error occurred during parsing"; + logger.Error(ex, message); + } + } + + #region Converters + + /// + /// Convert rows information + /// + /// Array of deserialized models to convert + /// Name of the file to be parsed + /// Index ID for the DAT + /// True to only add item statistics while parsing, false otherwise + private void ConvertRows(Models.SeparatedValue.Row[]? rows, string filename, int indexId, bool statsOnly) + { + // If the rows array is missing, we can't do anything + if (rows == null || !rows.Any()) + return; + + // Loop through the rows and add + foreach (var row in rows) + { + ConvertRow(row, filename, indexId, statsOnly); + } + } + + /// + /// Convert rows information + /// + /// Deserialized model to convert + /// Name of the file to be parsed + /// Index ID for the DAT + /// True to only add item statistics while parsing, false otherwise + private void ConvertRow(Models.SeparatedValue.Row? row, string filename, int indexId, bool statsOnly) + { + // If the row is missing, we can't do anything + if (row == null) + return; + + // Read DAT-level values + //Header.FileName ??= row.FileName; + Header.Name ??= row.InternalName; + Header.Description ??= row.Description; + + // Read Machine values + var machine = new Machine + { + Name = row.GameName, + Description = row.GameDescription, + }; + + // Read item values + DatItem item = null; + switch (row.Type.AsItemType()) + { + case ItemType.Disk: + item = new Disk + { + Name = row.DiskName, + MD5 = row.MD5, + SHA1 = row.SHA1, + ItemStatus = row.Status.AsItemStatus(), + + Machine = machine, + + Source = new Source + { + Index = indexId, + Name = filename, + }, + }; + break; + + case ItemType.Media: + item = new Media + { + Name = row.DiskName, + MD5 = row.MD5, + SHA1 = row.SHA1, + SHA256 = row.SHA256, + SpamSum = row.SpamSum, + + Machine = machine, + + Source = new Source + { + Index = indexId, + Name = filename, + }, + }; + break; + + case ItemType.Rom: + item = new Rom + { + Name = row.RomName, + CRC = row.CRC, + MD5 = row.MD5, + SHA1 = row.SHA1, + SHA256 = row.SHA256, + SHA384 = row.SHA384, + SHA512 = row.SHA512, + SpamSum = row.SpamSum, + ItemStatus = row.Status.AsItemStatus(), + + Machine = machine, + + Source = new Source + { + Index = indexId, + Name = filename, + }, + }; + break; + } + + // Now process and add the item + ParseAddHelper(item, statsOnly); + } + + #endregion + } +} diff --git a/SabreTools.DatFiles/Formats/SeparatedValue.Writer.cs b/SabreTools.DatFiles/Formats/SeparatedValue.Writer.cs new file mode 100644 index 00000000..4970f446 --- /dev/null +++ b/SabreTools.DatFiles/Formats/SeparatedValue.Writer.cs @@ -0,0 +1,235 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using SabreTools.Core; +using SabreTools.Core.Tools; +using SabreTools.DatItems; +using SabreTools.DatItems.Formats; + +namespace SabreTools.DatFiles.Formats +{ + /// + /// Represents writing a value-separated DAT + /// + internal partial class SeparatedValue : DatFile + { + /// + protected override ItemType[] GetSupportedTypes() + { + return new ItemType[] + { + ItemType.Disk, + ItemType.Media, + ItemType.Rom + }; + } + + /// + protected override List GetMissingRequiredFields(DatItem datItem) + { + List missingFields = new(); + + // Check item name + if (string.IsNullOrWhiteSpace(datItem.GetName())) + missingFields.Add(DatItemField.Name); + + switch (datItem) + { + case Disk disk: + if (string.IsNullOrWhiteSpace(disk.Name)) + missingFields.Add(DatItemField.Name); + if (string.IsNullOrWhiteSpace(disk.MD5) + && string.IsNullOrWhiteSpace(disk.SHA1)) + { + missingFields.Add(DatItemField.SHA1); + } + break; + + case Rom rom: + if (string.IsNullOrWhiteSpace(rom.Name)) + missingFields.Add(DatItemField.Name); + if (rom.Size == null || rom.Size < 0) + missingFields.Add(DatItemField.Size); + if (string.IsNullOrWhiteSpace(rom.CRC) + && string.IsNullOrWhiteSpace(rom.MD5) + && string.IsNullOrWhiteSpace(rom.SHA1) + && string.IsNullOrWhiteSpace(rom.SHA256) + && string.IsNullOrWhiteSpace(rom.SHA384) + && string.IsNullOrWhiteSpace(rom.SHA512) + && string.IsNullOrWhiteSpace(rom.SpamSum)) + { + missingFields.Add(DatItemField.SHA1); + } + break; + } + + return missingFields; + } + + /// + public override bool WriteToFile(string outfile, bool ignoreblanks = false, bool throwOnError = false) + { + try + { + logger.User($"Writing to '{outfile}'..."); + + var metadataFile = CreateMetadataFile(ignoreblanks); + if (!Serialization.SeparatedValue.SerializeToFile(metadataFile, outfile, _delim)) + { + logger.Warning($"File '{outfile}' could not be written! See the log for more details."); + return false; + } + } + catch (Exception ex) when (!throwOnError) + { + logger.Error(ex); + return false; + } + + return true; + } + + #region Converters + + /// + /// Create a MetadataFile from the current internal information + /// + /// True if blank roms should be skipped on output, false otherwise + private Models.SeparatedValue.MetadataFile CreateMetadataFile(bool ignoreblanks) + { + var metadataFile = new Models.SeparatedValue.MetadataFile + { + Row = CreateRows(ignoreblanks) + }; + return metadataFile; + } + + /// + /// Create an array of Row from the current internal information + /// + /// True if blank roms should be skipped on output, false otherwise + private Models.SeparatedValue.Row[]? CreateRows(bool ignoreblanks) + { + // If we don't have items, we can't do anything + if (this.Items == null || !this.Items.Any()) + return null; + + // Create a list of hold the rows + var rows = new List(); + + // Loop through the sorted items and create games for them + foreach (string key in Items.SortedKeys) + { + var items = Items.FilteredItems(key); + if (items == null || !items.Any()) + continue; + + // Loop through and convert the items to respective lists + foreach (var item in items) + { + // Skip if we're ignoring the item + if (ShouldIgnore(item, ignoreblanks)) + continue; + + switch (item) + { + case Disk disk: + rows.Add(CreateRow(disk)); + break; + case Rom rom: + rows.Add(CreateRow(rom)); + break; + } + } + } + + return rows.ToArray(); + } + + /// + /// Create a Row from the current Disk DatItem + /// + private Models.SeparatedValue.Row CreateRow(Disk disk) + { + var row = new Models.SeparatedValue.Row + { + FileName = Header.FileName, + InternalName = Header.Name, + Description = Header.Description, + GameName = disk.Machine.Name, + GameDescription = disk.Machine.Description, + Type = disk.ItemType.FromItemType(), + RomName = string.Empty, + DiskName = disk.Name, + Size = string.Empty, + CRC = string.Empty, + MD5 = disk.MD5, + SHA1 = disk.SHA1, + SHA256 = string.Empty, + SHA384 = string.Empty, + SHA512 = string.Empty, + SpamSum = string.Empty, + Status = disk.ItemStatus.FromItemStatus(yesno: false), + }; + return row; + } + + /// + /// Create a Row from the current Media DatItem + /// + private Models.SeparatedValue.Row CreateRow(Media media) + { + var row = new Models.SeparatedValue.Row + { + FileName = Header.FileName, + InternalName = Header.Name, + Description = Header.Description, + GameName = media.Machine.Name, + GameDescription = media.Machine.Description, + Type = media.ItemType.FromItemType(), + RomName = string.Empty, + DiskName = media.Name, + Size = string.Empty, + CRC = string.Empty, + MD5 = media.MD5, + SHA1 = media.SHA1, + SHA256 = media.SHA256, + SHA384 = string.Empty, + SHA512 = string.Empty, + SpamSum = media.SpamSum, + Status = string.Empty, + }; + return row; + } + + /// + /// Create a Row from the current Rom DatItem + /// + private Models.SeparatedValue.Row CreateRow(Rom rom) + { + var row = new Models.SeparatedValue.Row + { + FileName = Header.FileName, + InternalName = Header.Name, + Description = Header.Description, + GameName = rom.Machine.Name, + GameDescription = rom.Machine.Description, + Type = rom.ItemType.FromItemType(), + RomName = rom.Name, + DiskName = string.Empty, + Size = rom.Size?.ToString(), + CRC = rom.CRC, + MD5 = rom.MD5, + SHA1 = rom.SHA1, + SHA256 = rom.SHA256, + SHA384 = rom.SHA384, + SHA512 = rom.SHA512, + SpamSum = rom.SpamSum, + Status = rom.ItemStatus.FromItemStatus(yesno: false), + }; + return row; + } + + #endregion + } +} diff --git a/SabreTools.DatFiles/Formats/SeparatedValue.cs b/SabreTools.DatFiles/Formats/SeparatedValue.cs index 625cf6ca..e2897ade 100644 --- a/SabreTools.DatFiles/Formats/SeparatedValue.cs +++ b/SabreTools.DatFiles/Formats/SeparatedValue.cs @@ -1,21 +1,9 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using SabreTools.Core; -using SabreTools.Core.Tools; -using SabreTools.DatItems; -using SabreTools.DatItems.Formats; -using SabreTools.IO; -using SabreTools.IO.Readers; -using SabreTools.IO.Writers; - -namespace SabreTools.DatFiles.Formats +namespace SabreTools.DatFiles.Formats { /// - /// Represents parsing and writing of a value-separated DAT + /// Represents a value-separated DAT /// - internal class SeparatedValue : DatFile + internal partial class SeparatedValue : DatFile { // Private instance variables specific to Separated Value DATs private readonly char _delim; @@ -30,252 +18,5 @@ namespace SabreTools.DatFiles.Formats { _delim = delim; } - - /// - public override void ParseFile(string filename, int indexId, bool keep, bool statsOnly = false, bool throwOnError = false) - { - // Open a file reader - Encoding enc = filename.GetEncoding(); - SeparatedValueReader svr = new(System.IO.File.OpenRead(filename), enc) - { - Header = true, - Quotes = true, - Separator = _delim, - VerifyFieldCount = true, - }; - - // If we're somehow at the end of the stream already, we can't do anything - if (svr.EndOfStream) - return; - - // Read in the header - svr.ReadHeader(); - - // Loop through all of the data lines - while (!svr.EndOfStream) - { - try - { - // Get the current line, split and parse - svr.ReadNextLine(); - - // Create mapping dictionaries - Setter setter = new(); - setter.PopulateSettersFromList(svr.HeaderValues, svr.Line); - - // Set DatHeader fields - DatHeader datHeader = new(); - setter.SetFields(datHeader); - Header.ConditionalCopy(datHeader); - - // Set Machine and DatItem fields - if (setter.DatItemMappings.ContainsKey(DatItemField.Type)) - { - DatItem datItem = DatItem.Create(setter.DatItemMappings[DatItemField.Type].AsItemType()); - setter.SetFields(datItem); - datItem.Machine = new Machine(); - setter.SetFields(datItem.Machine); - datItem.Source = new Source(indexId, filename); - ParseAddHelper(datItem, statsOnly); - } - } - catch (Exception ex) when (!throwOnError) - { - string message = $"'{filename}' - There was an error parsing line {svr.LineNumber} '{svr.CurrentLine}'"; - logger.Error(ex, message); - } - } - - svr.Dispose(); - } - - /// - protected override ItemType[] GetSupportedTypes() - { - return new ItemType[] { ItemType.Disk, ItemType.Media, ItemType.Rom }; - } - - /// - protected override List GetMissingRequiredFields(DatItem datItem) - { - // TODO: Check required fields - return null; - } - - /// - public override bool WriteToFile(string outfile, bool ignoreblanks = false, bool throwOnError = false) - { - try - { - logger.User($"Writing to '{outfile}'..."); - FileStream fs = System.IO.File.Create(outfile); - - // If we get back null for some reason, just log and return - if (fs == null) - { - logger.Warning($"File '{outfile}' could not be created for writing! Please check to see if the file is writable"); - return false; - } - - SeparatedValueWriter svw = new(fs, new UTF8Encoding(false)) - { - Quotes = true, - Separator = this._delim, - VerifyFieldCount = true - }; - - // Write out the header - WriteHeader(svw); - - // Use a sorted list of games to output - foreach (string key in Items.SortedKeys) - { - ConcurrentList datItems = Items.FilteredItems(key); - - // If this machine doesn't contain any writable items, skip - if (!ContainsWritable(datItems)) - continue; - - // Resolve the names in the block - datItems = DatItem.ResolveNames(datItems); - - for (int index = 0; index < datItems.Count; index++) - { - DatItem datItem = datItems[index]; - - // Check for a "null" item - datItem = ProcessNullifiedItem(datItem); - - // Write out the item if we're not ignoring - if (!ShouldIgnore(datItem, ignoreblanks)) - WriteDatItem(svw, datItem); - } - } - - logger.User($"'{outfile}' written!{Environment.NewLine}"); - svw.Dispose(); - fs.Dispose(); - } - catch (Exception ex) when (!throwOnError) - { - logger.Error(ex); - return false; - } - - return true; - } - - /// - /// Write out DAT header using the supplied StreamWriter - /// - /// SeparatedValueWriter to output to - private void WriteHeader(SeparatedValueWriter svw) - { - string[] headers = new string[] - { - "File Name", - "Internal Name", - "Description", - "Game Name", - "Game Description", - "Type", - "Rom Name", - "Disk Name", - "Size", - "CRC", - "MD5", - "SHA1", - "SHA256", - //"SHA384", - //"SHA512", - //"SpamSum", - "Nodump", - }; - - svw.WriteHeader(headers); - - svw.Flush(); - } - - /// - /// Write out DatItem using the supplied StreamWriter - /// - /// SeparatedValueWriter to output to - /// DatItem object to be output - private void WriteDatItem(SeparatedValueWriter svw, DatItem datItem) - { - // Separated values should only output Rom and Disk - if (datItem.ItemType != ItemType.Disk && datItem.ItemType != ItemType.Rom) - return; - - // Build the state - // TODO: Can we have some way of saying what fields to write out? Support for read extends to all fields now - string[] fields = new string[14]; // 18; - fields[0] = Header.FileName; - fields[1] = Header.Name; - fields[2] = Header.Description; - fields[3] = datItem.Machine.Name; - fields[4] = datItem.Machine.Description; - - switch (datItem.ItemType) - { - case ItemType.Disk: - var disk = datItem as Disk; - fields[5] = "disk"; - fields[6] = string.Empty; - fields[7] = disk.Name; - fields[8] = string.Empty; - fields[9] = string.Empty; - fields[10] = disk.MD5?.ToLowerInvariant(); - //fields[11] = string.Empty; - fields[11] = disk.SHA1?.ToLowerInvariant(); - fields[12] = string.Empty; - //fields[13] = string.Empty; - //fields[14] = string.Empty; - //fields[15] = string.Empty; - fields[13] = disk.ItemStatus.ToString(); - break; - - case ItemType.Media: - var media = datItem as Media; - fields[5] = "media"; - fields[6] = string.Empty; - fields[7] = media.Name; - fields[8] = string.Empty; - fields[9] = string.Empty; - fields[10] = media.MD5?.ToLowerInvariant(); - //fields[11] = string.Empty; - fields[11] = media.SHA1?.ToLowerInvariant(); - fields[12] = media.SHA256?.ToLowerInvariant(); - //fields[13] = string.Empty; - //fields[14] = string.Empty; - //fields[15] = media.SpamSum?.ToLowerInvariant(); - fields[13] = string.Empty; - break; - - case ItemType.Rom: - var rom = datItem as Rom; - fields[5] = "rom"; - fields[6] = rom.Name; - fields[7] = string.Empty; - fields[8] = rom.Size?.ToString(); - fields[9] = rom.CRC?.ToLowerInvariant(); - fields[10] = rom.MD5?.ToLowerInvariant(); - fields[11] = rom.SHA1?.ToLowerInvariant(); - fields[12] = rom.SHA256?.ToLowerInvariant(); - //fields[13] = rom.SHA384?.ToLowerInvariant(); - //fields[14] = rom.SHA512?.ToLowerInvariant(); - //fields[15] = rom.SpamSum?.ToLowerInvariant(); - fields[13] = rom.ItemStatus.ToString(); - break; - } - - svw.WriteString(CreatePrefixPostfix(datItem, true)); - svw.WriteValues(fields, false); - svw.WriteString(CreatePrefixPostfix(datItem, false)); - svw.WriteLine(); - - svw.Flush(); - } } } diff --git a/SabreTools.Models/SeparatedValue/Row.cs b/SabreTools.Models/SeparatedValue/Row.cs index ebce2bb6..1786cea4 100644 --- a/SabreTools.Models/SeparatedValue/Row.cs +++ b/SabreTools.Models/SeparatedValue/Row.cs @@ -53,8 +53,8 @@ namespace SabreTools.Models.SeparatedValue /// SpamSum, Optional public string SpamSum { get; set; } - /// Nodump - public string Nodump { get; set; } + /// Status, Nodump + public string Status { get; set; } #region DO NOT USE IN PRODUCTION diff --git a/SabreTools.Serialization/SeparatedValue.Deserializer.cs b/SabreTools.Serialization/SeparatedValue.Deserializer.cs new file mode 100644 index 00000000..a2bf2d24 --- /dev/null +++ b/SabreTools.Serialization/SeparatedValue.Deserializer.cs @@ -0,0 +1,139 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using SabreTools.IO.Readers; +using SabreTools.Models.SeparatedValue; + +namespace SabreTools.Serialization +{ + /// + /// Deserializer for separated-value variants + /// + public partial class SeparatedValue + { + /// + /// Deserializes a separated-value variant to the defined type + /// + /// Path to the file to deserialize + /// Character delimiter between values + /// Deserialized data on success, null on failure + public static MetadataFile? Deserialize(string path, char delim) + { + try + { + using var stream = PathProcessor.OpenStream(path); + return Deserialize(stream, delim); + } + catch + { + // TODO: Handle logging the exception + return default; + } + } + + /// + /// Deserializes a separated-value variant in a stream to the defined type + /// + /// Stream to deserialize + /// Character delimiter between values + /// Deserialized data on success, null on failure + public static MetadataFile? Deserialize(Stream? stream, char delim) + { + try + { + // If the stream is null + if (stream == null) + return default; + + // Setup the reader and output + var reader = new SeparatedValueReader(stream, Encoding.UTF8) + { + Header = true, + Separator = delim, + VerifyFieldCount = false, + }; + var dat = new MetadataFile(); + + // 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 < HeaderWithExtendedHashesCount) + { + row = new Row + { + FileName = reader.Line[0], + InternalName = reader.Line[1], + Description = reader.Line[2], + GameName = reader.Line[3], + GameDescription = reader.Line[4], + Type = reader.Line[5], + RomName = reader.Line[6], + DiskName = reader.Line[7], + Size = reader.Line[8], + CRC = reader.Line[9], + MD5 = reader.Line[10], + SHA1 = reader.Line[11], + SHA256 = reader.Line[12], + Status = reader.Line[13], + }; + + // If we have additional fields + if (reader.Line.Count > HeaderWithoutExtendedHashesCount) + row.ADDITIONAL_ELEMENTS = reader.Line.Skip(HeaderWithoutExtendedHashesCount).ToArray(); + } + else + { + row = new Row + { + FileName = reader.Line[0], + InternalName = reader.Line[1], + Description = reader.Line[2], + GameName = reader.Line[3], + GameDescription = reader.Line[4], + Type = reader.Line[5], + RomName = reader.Line[6], + DiskName = reader.Line[7], + Size = reader.Line[8], + CRC = reader.Line[9], + MD5 = reader.Line[10], + SHA1 = reader.Line[11], + SHA256 = reader.Line[12], + SHA384 = reader.Line[13], + SHA512 = reader.Line[14], + SpamSum = reader.Line[15], + Status = reader.Line[16], + }; + + // If we have additional fields + if (reader.Line.Count > HeaderWithExtendedHashesCount) + row.ADDITIONAL_ELEMENTS = reader.Line.Skip(HeaderWithExtendedHashesCount).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/SeparatedValue.Serializer.cs b/SabreTools.Serialization/SeparatedValue.Serializer.cs new file mode 100644 index 00000000..d9d91b93 --- /dev/null +++ b/SabreTools.Serialization/SeparatedValue.Serializer.cs @@ -0,0 +1,147 @@ +using System.IO; +using System.Linq; +using System.Text; +using SabreTools.IO.Writers; +using SabreTools.Models.SeparatedValue; + +namespace SabreTools.Serialization +{ + /// + /// Serializer for separated-value variants + /// + public partial class SeparatedValue + { + /// + /// Serializes the defined type to a separated-value variant + /// + /// Data to serialize + /// Path to the file to serialize to + /// Character delimiter between values + /// True on successful serialization, false otherwise + public static bool SerializeToFile(MetadataFile? metadataFile, string path, char delim) + { + try + { + using var stream = SerializeToStream(metadataFile, delim); + if (stream == null) + return false; + + using var fs = File.OpenWrite(path); + stream.Seek(0, SeekOrigin.Begin); + stream.CopyTo(fs); + return true; + } + catch + { + // TODO: Handle logging the exception + return false; + } + } + + /// + /// Serializes the defined type to a stream + /// + /// Data to serialize + /// Character delimiter between values + /// Stream containing serialized data on success, null otherwise + public static Stream? SerializeToStream(MetadataFile? metadataFile, char delim) + { + try + { + // If the metadata file is null + if (metadataFile == null) + return null; + + // Setup the writer and output + var stream = new MemoryStream(); + var writer = new SeparatedValueWriter(stream, Encoding.UTF8) { Separator = delim, Quotes = true }; + + // TODO: Include flag to write out long or short header + // Write the short header + WriteHeader(writer); + + // Write out the rows, if they exist + WriteRows(metadataFile.Row, writer); + + // Return the stream + return stream; + } + catch + { + // TODO: Handle logging the exception + return null; + } + } + + /// + /// Write header information to the current writer + /// + /// SeparatedValueWriter representing the output + private static void WriteHeader(SeparatedValueWriter writer) + { + var headerArray = new string[] + { + "File Name", + "Internal Name", + "Description", + "Game Name", + "Game Description", + "Type", + "Rom Name", + "Disk Name", + "Size", + "CRC", + "MD5", + "SHA1", + "SHA256", + //"SHA384", + //"SHA512", + //"SpamSum", + "Status", + }; + + writer.WriteHeader(headerArray); + writer.Flush(); + } + + /// + /// Write rows information to the current writer + /// + /// Array of Row objects representing the rows information + /// SeparatedValueWriter representing the output + private static void WriteRows(Row[]? rows, SeparatedValueWriter writer) + { + // If the games information is missing, we can't do anything + if (rows == null || !rows.Any()) + return; + + // Loop through and write out the rows + foreach (var row in rows) + { + var rowArray = new string[] + { + row.FileName, + row.InternalName, + row.Description, + row.GameName, + row.GameDescription, + row.Type, + row.RomName, + row.DiskName, + row.Size, + row.CRC, + row.MD5, + row.SHA1, + row.SHA256, + //row.SHA384, + //row.SHA512, + //row.SpamSum, + row.Status, + }; + + writer.WriteValues(rowArray); + writer.Flush(); + } + } + } +} \ No newline at end of file diff --git a/SabreTools.Serialization/SeparatedValue.cs b/SabreTools.Serialization/SeparatedValue.cs index afaedbae..970c7cdb 100644 --- a/SabreTools.Serialization/SeparatedValue.cs +++ b/SabreTools.Serialization/SeparatedValue.cs @@ -1,143 +1,12 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using SabreTools.IO.Readers; -using SabreTools.Models.SeparatedValue; - namespace SabreTools.Serialization { /// - /// Serializer for separated-value variants + /// Represents separated-value variants /// - public class SeparatedValue + public partial class SeparatedValue { private const int HeaderWithoutExtendedHashesCount = 14; private const int HeaderWithExtendedHashesCount = 17; - - /// - /// Deserializes a separated-value variant to the defined type - /// - /// Path to the file to deserialize - /// Character delimiter between values - /// Deserialized data on success, null on failure - public static MetadataFile? Deserialize(string path, char delim) - { - try - { - using var stream = PathProcessor.OpenStream(path); - return Deserialize(stream, delim); - } - catch - { - // TODO: Handle logging the exception - return default; - } - } - - /// - /// Deserializes a separated-value variant in a stream to the defined type - /// - /// Stream to deserialize - /// Character delimiter between values - /// Deserialized data on success, null on failure - public static MetadataFile? Deserialize(Stream? stream, char delim) - { - try - { - // If the stream is null - if (stream == null) - return default; - - // Setup the reader and output - var reader = new SeparatedValueReader(stream, Encoding.UTF8) - { - Header = true, - Separator = delim, - VerifyFieldCount = false, - }; - var dat = new MetadataFile(); - - // 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 < HeaderWithExtendedHashesCount) - { - row = new Row - { - FileName = reader.Line[0], - InternalName = reader.Line[1], - Description = reader.Line[2], - GameName = reader.Line[3], - GameDescription = reader.Line[4], - Type = reader.Line[5], - RomName = reader.Line[6], - DiskName = reader.Line[7], - Size = reader.Line[8], - CRC = reader.Line[9], - MD5 = reader.Line[10], - SHA1 = reader.Line[11], - SHA256 = reader.Line[12], - Nodump = reader.Line[13], - }; - - // If we have additional fields - if (reader.Line.Count > HeaderWithoutExtendedHashesCount) - row.ADDITIONAL_ELEMENTS = reader.Line.Skip(HeaderWithoutExtendedHashesCount).ToArray(); - } - else - { - row = new Row - { - FileName = reader.Line[0], - InternalName = reader.Line[1], - Description = reader.Line[2], - GameName = reader.Line[3], - GameDescription = reader.Line[4], - Type = reader.Line[5], - RomName = reader.Line[6], - DiskName = reader.Line[7], - Size = reader.Line[8], - CRC = reader.Line[9], - MD5 = reader.Line[10], - SHA1 = reader.Line[11], - SHA256 = reader.Line[12], - SHA384 = reader.Line[13], - SHA512 = reader.Line[14], - SpamSum = reader.Line[15], - Nodump = reader.Line[16], - }; - - // If we have additional fields - if (reader.Line.Count > HeaderWithExtendedHashesCount) - row.ADDITIONAL_ELEMENTS = reader.Line.Skip(HeaderWithExtendedHashesCount).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