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