From 0930b700845b5719d80fb94ab9f26ecce641eb11 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Sun, 30 Jul 2023 21:27:02 -0400 Subject: [PATCH] Use Archive.org serializer, fix stream position --- .../Formats/ArchiveDotOrg.Reader.cs | 189 ++++++++ .../Formats/ArchiveDotOrg.Writer.cs | 195 +++++++++ SabreTools.DatFiles/Formats/ArchiveDotOrg.cs | 404 +----------------- SabreTools.Models/ArchiveDotOrg/File.cs | 38 +- .../AttractMode.Serializer.cs | 2 +- .../ClrMamePro.Serializer.cs | 2 +- .../DosCenter.Serializer.cs | 2 +- .../EverdriveSMDB.Serializer.cs | 2 +- .../Hashfile.Serializer.cs | 2 +- .../Listrom.Serializer.cs | 2 +- .../RomCenter.Serializer.cs | 2 +- .../SeparatedValue.Serializer.cs | 2 +- .../XmlSerializer.Serializer.cs | 5 + 13 files changed, 419 insertions(+), 428 deletions(-) create mode 100644 SabreTools.DatFiles/Formats/ArchiveDotOrg.Reader.cs create mode 100644 SabreTools.DatFiles/Formats/ArchiveDotOrg.Writer.cs diff --git a/SabreTools.DatFiles/Formats/ArchiveDotOrg.Reader.cs b/SabreTools.DatFiles/Formats/ArchiveDotOrg.Reader.cs new file mode 100644 index 00000000..91d262cb --- /dev/null +++ b/SabreTools.DatFiles/Formats/ArchiveDotOrg.Reader.cs @@ -0,0 +1,189 @@ +using System; +using System.IO; +using System.Linq; +using SabreTools.Core; +using SabreTools.Core.Tools; +using SabreTools.DatItems; +using SabreTools.DatItems.Formats; + +namespace SabreTools.DatFiles.Formats +{ + /// + /// Represents parsing a Archive.org file list + /// + internal partial class ArchiveDotOrg : DatFile + { + /// + public override void ParseFile(string filename, int indexId, bool keep, bool statsOnly = false, bool throwOnError = false) + { + try + { + // Deserialize the input file + var files = Serialization.ArchiveDotOrg.Deserialize(filename); + + // Convert the files data to the internal format + ConvertFiles(files?.File, filename, indexId, statsOnly); + } + catch (Exception ex) when (!throwOnError) + { + string message = $"'{filename}' - An error occurred during parsing"; + logger.Error(ex, message); + } + } + + #region Converters + + /// + /// Create a machine from the filename + /// + /// Filename to derive from + /// Filled machine and new filename on success, null on error + private static (Machine?, string?) DeriveMachine(string filename) + { + // If the filename is missing, we can't do anything + if (string.IsNullOrWhiteSpace(filename)) + return (null, null); + + string machineName = Path.GetFileNameWithoutExtension(filename); + if (filename.Contains('/')) + { + string[] split = filename.Split('/'); + machineName = split[0]; + filename = filename[(machineName.Length + 1)..]; + } + else if (filename.Contains('\\')) + { + string[] split = filename.Split('\\'); + machineName = split[0]; + filename = filename[(machineName.Length + 1)..]; + } + + var machine = new Machine { Name = machineName }; + return (machine, filename); + } + + /// + /// Convert files 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 ConvertFiles(Models.ArchiveDotOrg.File[]? files, string filename, int indexId, bool statsOnly) + { + // If the files array is missing, we can't do anything + if (files == null || !files.Any()) + return; + + // Loop through the rows and add + foreach (var file in files) + { + ConvertFile(file, filename, indexId, statsOnly); + } + } + + /// + /// Convert file 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 ConvertFile(Models.ArchiveDotOrg.File? file, string filename, int indexId, bool statsOnly) + { + // If the file is missing, we can't do anything + if (file == null) + return; + + (var machine, string name) = DeriveMachine(file.Name); + if (machine == null) + machine = new Machine { Name = Path.GetFileNameWithoutExtension(file.Name) }; + + machine.Publisher = file.Publisher; + machine.Comment = file.Comment; + + var rom = new Rom() + { + Name = name, + ArchiveDotOrgSource = file.Source, + //BitTorrentMagnetHash = file.BitTorrentMagnetHash, // TODO: Add to internal model + Date = file.LastModifiedTime?.ToString(), + Size = Utilities.CleanLong(file.Size), + MD5 = file.MD5, + CRC = file.CRC32, + SHA1 = file.SHA1, + //FileCount = file.FileCount, // TODO: Add to internal model + ArchiveDotOrgFormat = file.Format, + //Original = file.Original, // TODO: Add to internal model + Summation = file.Summation, + //MatrixNumber = file.MatrixNumber, // TODO: Add to internal model + //CollectionCatalogNumber = file.CollectionCatalogNumber, // TODO: Add to internal model + + // ASR-Related + //ASRDetectedLang = file.ASRDetectedLang, // TODO: Add to internal model + //ASRDetectedLangConf = file.ASRDetectedLangConf, // TODO: Add to internal model + //ASRTranscribedLang = file.ASRTranscribedLang, // TODO: Add to internal model + //WhisperASRModuleVersion = file.WhisperASRModuleVersion, // TODO: Add to internal model + //WhisperModelHash = file.WhisperModelHash, // TODO: Add to internal model + //WhisperModelName = file.WhisperModelName, // TODO: Add to internal model + //WhisperVersion = file.WhisperVersion, // TODO: Add to internal model + + // OCR-Related + //ClothCoverDetectionModuleVersion = file.ClothCoverDetectionModuleVersions, // TODO: Add to internal model + //hOCRCharToWordhOCRVersion = file.hOCRCharToWordhOCRVersion, // TODO: Add to internal model + //hOCRCharToWordModuleVersion = file.hOCRCharToWordModuleVersion, // TODO: Add to internal model + //hOCRFtsTexthOCRVersion = file.hOCRFtsTexthOCRVersion, // TODO: Add to internal model + //hOCRFtsTextModuleVersion = file.hOCRFtsTextModuleVersion, // TODO: Add to internal model + //hOCRPageIndexhOCRVersion = file.hOCRPageIndexhOCRVersion, // TODO: Add to internal model + //hOCRPageIndexModuleVersion = file.hOCRPageIndexModuleVersion, // TODO: Add to internal model + //TesseractOCR = file.TesseractOCR, // TODO: Add to internal model + //TesseractOCRConverted = file.TesseractOCRConverted, // TODO: Add to internal model + //TesseractOCRDetectedLang = file.TesseractOCRDetectedLang, // TODO: Add to internal model + //TesseractOCRDetectedLangConf = file.TesseractOCRDetectedLangConf, // TODO: Add to internal model + //TesseractOCRDetectedScript = file.TesseractOCRDetectedScript, // TODO: Add to internal model + //TesseractOCRDetectedScriptConf = file.TesseractOCRDetectedScriptConf, // TODO: Add to internal model + //TesseractOCRParameters = file.TesseractOCRParameters, // TODO: Add to internal model + //TesseractOCRModuleVersion = file.TesseractOCRModuleVersion, // TODO: Add to internal model + //PDFModuleVersion = file.PDFModuleVersion, // TODO: Add to internal model + //WordConfidenceInterval0To10 = file.WordConfidenceInterval0To10, // TODO: Add to internal model + //WordConfidenceInterval11To20 = file.WordConfidenceInterval11To20, // TODO: Add to internal model + //WordConfidenceInterval21To30 = file.WordConfidenceInterval21To30, // TODO: Add to internal model + //WordConfidenceInterval31To40 = file.WordConfidenceInterval31To40, // TODO: Add to internal model + //WordConfidenceInterval41To50 = file.WordConfidenceInterval41To50, // TODO: Add to internal model + //WordConfidenceInterval51To60 = file.WordConfidenceInterval51To60, // TODO: Add to internal model + //WordConfidenceInterval61To70 = file.WordConfidenceInterval61To70, // TODO: Add to internal model + //WordConfidenceInterval71To80 = file.WordConfidenceInterval71To80, // TODO: Add to internal model + //WordConfidenceInterval81To90 = file.WordConfidenceInterval81To90, // TODO: Add to internal model + //WordConfidenceInterval91To100 = file.WordConfidenceInterval91To100, // TODO: Add to internal model + + // Media-Related + //Album = file.Album, // TODO: Add to internal model + //Artist = file.Artist, // TODO: Add to internal model + //Bitrate = file.Bitrate, // TODO: Add to internal model + //Creator = file.Creator, // TODO: Add to internal model + //Height = file.Height, // TODO: Add to internal model + //Length = file.Length, // TODO: Add to internal model + //PreviewImage = file.PreviewImage, // TODO: Add to internal model + //Rotation = file.Rotation, // TODO: Add to internal model + //Title = file.Title, // TODO: Add to internal model + //Track = file.Track, // TODO: Add to internal model + //Width = file.Width, // TODO: Add to internal model + + ItemStatus = ItemStatus.None, + + Machine = machine, + + Source = new Source + { + Index = indexId, + Name = filename, + }, + }; + + // Now process and add the rom + ParseAddHelper(rom, statsOnly); + } + + #endregion + } +} diff --git a/SabreTools.DatFiles/Formats/ArchiveDotOrg.Writer.cs b/SabreTools.DatFiles/Formats/ArchiveDotOrg.Writer.cs new file mode 100644 index 00000000..a64cd539 --- /dev/null +++ b/SabreTools.DatFiles/Formats/ArchiveDotOrg.Writer.cs @@ -0,0 +1,195 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using SabreTools.Core; +using SabreTools.DatItems; +using SabreTools.DatItems.Formats; + +namespace SabreTools.DatFiles.Formats +{ + /// + /// Represents parsing and writing of a Archive.org file list + /// + internal partial class ArchiveDotOrg : DatFile + { + /// + protected override ItemType[] GetSupportedTypes() + { + return new ItemType[] + { + 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 Rom rom: + if (rom.Size == null || rom.Size < 0) + missingFields.Add(DatItemField.Size); + if (string.IsNullOrWhiteSpace(rom.CRC) + && string.IsNullOrWhiteSpace(rom.MD5) + && string.IsNullOrWhiteSpace(rom.SHA1)) + { + 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 files = CreateFiles(ignoreblanks); + if (!Serialization.ArchiveDotOrg.SerializeToFile(files, outfile)) + { + 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 Files from the current internal information + /// + /// True if blank roms should be skipped on output, false otherwise + private Models.ArchiveDotOrg.Files CreateFiles(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 files + var files = 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 Rom rom: + files.Add(CreateFile(rom)); + break; + } + } + } + + return new Models.ArchiveDotOrg.Files { File = files.ToArray() }; + } + + /// + /// Create a File from the current Rom DatItem + /// + private static Models.ArchiveDotOrg.File CreateFile(Rom item) + { + var file = new Models.ArchiveDotOrg.File + { + Name = item.Name, + Source = item.ArchiveDotOrgSource, + //BitTorrentMagnetHash = item.BitTorrentMagnetHash, // TODO: Add to internal model + Size = item.Size?.ToString(), + MD5 = item.MD5, + CRC32 = item.CRC, + SHA1 = item.SHA1, + //FileCount = item.FileCount, // TODO: Add to internal model + Format = item.ArchiveDotOrgFormat, + //Original = item.Original, // TODO: Add to internal model + Summation = item.Summation, + //MatrixNumber = item.MatrixNumber, // TODO: Add to internal model + //CollectionCatalogNumber = item.CollectionCatalogNumber, // TODO: Add to internal model + + // ASR-Related + //ASRDetectedLang = item.ASRDetectedLang, // TODO: Add to internal model + //ASRDetectedLangConf = item.ASRDetectedLangConf, // TODO: Add to internal model + //ASRTranscribedLang = item.ASRTranscribedLang, // TODO: Add to internal model + //WhisperASRModuleVersion = item.WhisperASRModuleVersion, // TODO: Add to internal model + //WhisperModelHash = item.WhisperModelHash, // TODO: Add to internal model + //WhisperModelName = item.WhisperModelName, // TODO: Add to internal model + //WhisperVersion = item.WhisperVersion, // TODO: Add to internal model + + // OCR-Related + //ClothCoverDetectionModuleVersion = item.ClothCoverDetectionModuleVersions, // TODO: Add to internal model + //hOCRCharToWordhOCRVersion = item.hOCRCharToWordhOCRVersion, // TODO: Add to internal model + //hOCRCharToWordModuleVersion = item.hOCRCharToWordModuleVersion, // TODO: Add to internal model + //hOCRFtsTexthOCRVersion = item.hOCRFtsTexthOCRVersion, // TODO: Add to internal model + //hOCRFtsTextModuleVersion = item.hOCRFtsTextModuleVersion, // TODO: Add to internal model + //hOCRPageIndexhOCRVersion = item.hOCRPageIndexhOCRVersion, // TODO: Add to internal model + //hOCRPageIndexModuleVersion = item.hOCRPageIndexModuleVersion, // TODO: Add to internal model + //TesseractOCR = item.TesseractOCR, // TODO: Add to internal model + //TesseractOCRConverted = item.TesseractOCRConverted, // TODO: Add to internal model + //TesseractOCRDetectedLang = item.TesseractOCRDetectedLang, // TODO: Add to internal model + //TesseractOCRDetectedLangConf = item.TesseractOCRDetectedLangConf, // TODO: Add to internal model + //TesseractOCRDetectedScript = item.TesseractOCRDetectedScript, // TODO: Add to internal model + //TesseractOCRDetectedScriptConf = item.TesseractOCRDetectedScriptConf, // TODO: Add to internal model + //TesseractOCRParameters = item.TesseractOCRParameters, // TODO: Add to internal model + //TesseractOCRModuleVersion = item.TesseractOCRModuleVersion, // TODO: Add to internal model + //PDFModuleVersion = item.PDFModuleVersion, // TODO: Add to internal model + //WordConfidenceInterval0To10 = item.WordConfidenceInterval0To10, // TODO: Add to internal model + //WordConfidenceInterval11To20 = item.WordConfidenceInterval11To20, // TODO: Add to internal model + //WordConfidenceInterval21To30 = item.WordConfidenceInterval21To30, // TODO: Add to internal model + //WordConfidenceInterval31To40 = item.WordConfidenceInterval31To40, // TODO: Add to internal model + //WordConfidenceInterval41To50 = item.WordConfidenceInterval41To50, // TODO: Add to internal model + //WordConfidenceInterval51To60 = item.WordConfidenceInterval51To60, // TODO: Add to internal model + //WordConfidenceInterval61To70 = item.WordConfidenceInterval61To70, // TODO: Add to internal model + //WordConfidenceInterval71To80 = item.WordConfidenceInterval71To80, // TODO: Add to internal model + //WordConfidenceInterval81To90 = item.WordConfidenceInterval81To90, // TODO: Add to internal model + //WordConfidenceInterval91To100 = item.WordConfidenceInterval91To100, // TODO: Add to internal model + + // Media-Related + //Album = item.Album, // TODO: Add to internal model + //Artist = item.Artist, // TODO: Add to internal model + //Bitrate = item.Bitrate, // TODO: Add to internal model + //Creator = item.Creator, // TODO: Add to internal model + //Height = item.Height, // TODO: Add to internal model + //Length = item.Length, // TODO: Add to internal model + //PreviewImage = item.PreviewImage, // TODO: Add to internal model + //Rotation = item.Rotation, // TODO: Add to internal model + //Title = item.Title, // TODO: Add to internal model + //Track = item.Track, // TODO: Add to internal model + //Width = item.Width, // TODO: Add to internal model + + }; + + if (long.TryParse(item.Date ?? string.Empty, out long lastModifiedTime)) + file.LastModifiedTime = lastModifiedTime.ToString(); + + return file; + } + + #endregion + } +} diff --git a/SabreTools.DatFiles/Formats/ArchiveDotOrg.cs b/SabreTools.DatFiles/Formats/ArchiveDotOrg.cs index 90ae704d..62079103 100644 --- a/SabreTools.DatFiles/Formats/ArchiveDotOrg.cs +++ b/SabreTools.DatFiles/Formats/ArchiveDotOrg.cs @@ -1,21 +1,9 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using System.Xml; -using System.Xml.Schema; -using SabreTools.Core; -using SabreTools.Core.Tools; -using SabreTools.DatItems; -using SabreTools.DatItems.Formats; -using SabreTools.IO; - -namespace SabreTools.DatFiles.Formats +namespace SabreTools.DatFiles.Formats { /// - /// Represents parsing and writing of a Archive.org file list + /// Represents a Archive.org file list /// - internal class ArchiveDotOrg : DatFile + internal partial class ArchiveDotOrg : DatFile { /// /// Constructor designed for casting a base DatFile @@ -25,391 +13,5 @@ namespace SabreTools.DatFiles.Formats : base(datFile) { } - - /// - public override void ParseFile(string filename, int indexId, bool keep, bool statsOnly = false, bool throwOnError = false) - { - // Prepare all internal variables - XmlReader xtr = XmlReader.Create(filename, new XmlReaderSettings - { - CheckCharacters = false, - DtdProcessing = DtdProcessing.Ignore, - IgnoreComments = true, - IgnoreWhitespace = true, - ValidationFlags = XmlSchemaValidationFlags.None, - ValidationType = ValidationType.None, - }); - - // If we got a null reader, just return - if (xtr == null) - return; - - // Otherwise, read the file to the end - try - { - xtr.MoveToContent(); - while (!xtr.EOF) - { - // We only want elements - if (xtr.NodeType != XmlNodeType.Element) - { - xtr.Read(); - continue; - } - - switch (xtr.Name) - { - case "files": - ReadFiles(xtr.ReadSubtree(), statsOnly, filename, indexId, keep); - - // Skip the machine now that we've processed it - xtr.Skip(); - break; - - default: - xtr.Read(); - break; - } - } - } - catch (Exception ex) when (!throwOnError) - { - logger.Warning(ex, $"Exception found while parsing '{filename}'"); - - // For XML errors, just skip the affected node - xtr?.Read(); - } - - xtr.Dispose(); - } - - /// - /// Read files information - /// - /// XmlReader to use to parse the machine - /// True to only add item statistics while parsing, false otherwise - /// Name of the file to be parsed - /// Index ID for the DAT - /// True if full pathnames are to be kept, false otherwise (default) - private void ReadFiles( - XmlReader reader, - bool statsOnly, - - // Standard Dat parsing - string filename, - int indexId, - - // Miscellaneous - bool keep) - { - // If we have an empty machine, skip it - if (reader == null) - return; - - // Otherwise, add what is possible - reader.MoveToContent(); - - while (!reader.EOF) - { - // We only want elements - if (reader.NodeType != XmlNodeType.Element) - { - reader.Read(); - continue; - } - - // Get the files from the list - switch (reader.Name) - { - case "file": - ReadFile(reader.ReadSubtree(), statsOnly, filename, indexId, keep); - - // Skip the file node now that we've processed it - reader.Skip(); - break; - - default: - reader.Read(); - break; - } - } - } - - /// - /// Read file information - /// - /// XmlReader to use to parse the machine - /// True to only add item statistics while parsing, false otherwise - /// Name of the file to be parsed - /// Index ID for the DAT - /// True if full pathnames are to be kept, false otherwise (default) - private void ReadFile( - XmlReader reader, - bool statsOnly, - - // Standard Dat parsing - string filename, - int indexId, - - // Miscellaneous - bool keep) - { - // If we have an empty machine, skip it - if (reader == null) - return; - - // Otherwise, add what is possible - reader.MoveToContent(); - - // Create the Rom to store the info - Rom rom = new() - { - Name = reader.GetAttribute("name"), - ArchiveDotOrgSource = reader.GetAttribute("source"), - - Machine = new Machine - { - Name = "Default", - Description = "Default", - }, - - Source = new Source - { - Index = indexId, - Name = filename, - } - }; - - // If we have a path, update the machine name and description - if (rom.Name.Contains('/')) - { - string[] splitpath = rom.Name.Split('/'); - rom.Machine.Name = splitpath[0]; - rom.Machine.Description = splitpath[0]; - - rom.Name = rom.Name[(splitpath[0].Length + 1)..]; - } - - // TODO: Handle SuperDAT - //if (Header.Type == "SuperDAT" && !keep) - //{ - // string tempout = Regex.Match(machine.Name, @".*?\\(.*)").Groups[1].Value; - // if (!string.IsNullOrWhiteSpace(tempout)) - // machine.Name = tempout; - //} - - while (!reader.EOF) - { - // We only want elements - if (reader.NodeType != XmlNodeType.Element) - { - reader.Read(); - continue; - } - - // Get the roms from the machine - switch (reader.Name) - { - case "crc32": - rom.CRC = reader.ReadElementContentAsString(); - break; - - case "md5": - rom.MD5 = reader.ReadElementContentAsString(); - break; - - case "mtime": - rom.Date = reader.ReadElementContentAsString(); - break; - - case "sha1": - rom.SHA1 = reader.ReadElementContentAsString(); - break; - - case "size": - rom.Size = Utilities.CleanLong(reader.ReadElementContentAsString()); - break; - - case "format": - rom.ArchiveDotOrgFormat = reader.ReadElementContentAsString(); - break; - - case "original": - rom.OriginalFilename = reader.ReadElementContentAsString(); - break; - - case "rotation": - rom.Rotation = reader.ReadElementContentAsString(); - break; - - case "summation": - rom.Summation = reader.ReadElementContentAsString(); - break; - - default: - reader.Read(); - break; - } - } - - // Now process and add the rom - ParseAddHelper(rom, statsOnly); - } - - /// - protected override ItemType[] GetSupportedTypes() - { - return new ItemType[] - { - 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; - } - - XmlTextWriter xtw = new(fs, new UTF8Encoding(false)) - { - Formatting = Formatting.Indented, - IndentChar = ' ', - Indentation = 2 - }; - - // Write out the header - WriteHeader(xtw); - - // Write out each of the machines and roms - string lastgame = null; - - // 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(xtw, datItem); - - // Set the new data to compare against - lastgame = datItem.Machine.Name; - } - } - - // Write the file footer out - WriteFooter(xtw); - - logger.User($"'{outfile}' written!{Environment.NewLine}"); - xtw.Dispose(); - fs.Dispose(); - } - catch (Exception ex) when (!throwOnError) - { - logger.Error(ex); - return false; - } - - return true; - } - - /// - /// Write out DAT header using the supplied StreamWriter - /// - /// XmlTextWriter to output to - private void WriteHeader(XmlTextWriter xtw) - { - xtw.WriteStartDocument(); - - xtw.WriteStartElement("files"); - - xtw.Flush(); - } - - /// - /// Write out DatItem using the supplied StreamWriter - /// - /// XmlTextWriter to output to - /// DatItem object to be output - private void WriteDatItem(XmlTextWriter xtw, DatItem datItem) - { - // Pre-process the item name - ProcessItemName(datItem, true); - - // Build the state - switch (datItem.ItemType) - { - case ItemType.Rom: - var rom = datItem as Rom; - xtw.WriteStartElement("file"); - - // Filename has to be derived from both machine and item, if possible - string filename = rom.Name; - if (!string.IsNullOrWhiteSpace(rom.Machine?.Name) && !rom.Machine.Name.Equals("Default", StringComparison.OrdinalIgnoreCase)) - filename = $"{rom.Machine.Name}/{filename}"; - - xtw.WriteRequiredAttributeString("name", filename); - xtw.WriteOptionalAttributeString("source", rom.ArchiveDotOrgSource); - - xtw.WriteOptionalElementString("mtime", rom.Date); - xtw.WriteOptionalElementString("size", rom.Size?.ToString()); - xtw.WriteOptionalElementString("md5", rom.MD5?.ToLowerInvariant()); - xtw.WriteOptionalElementString("crc32", rom.CRC?.ToLowerInvariant()); - xtw.WriteOptionalElementString("sha1", rom.SHA1?.ToLowerInvariant()); - xtw.WriteOptionalElementString("format", rom.ArchiveDotOrgFormat); - xtw.WriteOptionalElementString("original", rom.OriginalFilename); - xtw.WriteOptionalElementString("rotation", rom.Rotation?.ToString()); - xtw.WriteOptionalElementString("summation", rom.Summation); - - // End file - xtw.WriteEndElement(); - break; - } - - xtw.Flush(); - } - - /// - /// Write out DAT footer using the supplied StreamWriter - /// - /// XmlTextWriter to output to - private void WriteFooter(XmlTextWriter xtw) - { - // End files - xtw.WriteEndElement(); - - xtw.Flush(); - } } } diff --git a/SabreTools.Models/ArchiveDotOrg/File.cs b/SabreTools.Models/ArchiveDotOrg/File.cs index 00681403..eb9ee31c 100644 --- a/SabreTools.Models/ArchiveDotOrg/File.cs +++ b/SabreTools.Models/ArchiveDotOrg/File.cs @@ -17,10 +17,10 @@ namespace SabreTools.Models.ArchiveDotOrg public string? BitTorrentMagnetHash { get; set; } [XmlElement("mtime")] - public long? LastModifiedTime { get; set; } + public string? LastModifiedTime { get; set; } [XmlElement("size")] - public long? Size { get; set; } + public string? Size { get; set; } [XmlElement("md5")] public string? MD5 { get; set; } @@ -32,7 +32,7 @@ namespace SabreTools.Models.ArchiveDotOrg public string? SHA1 { get; set; } [XmlElement("filecount")] - public long? FileCount { get; set; } + public string? FileCount { get; set; } /// Is this a set of defined values? [XmlElement("format")] @@ -133,34 +133,34 @@ namespace SabreTools.Models.ArchiveDotOrg public string? PDFModuleVersion { get; set; } [XmlElement("word_conf_0_10")] - public long? WordConfidenceInterval0To10 { get; set; } + public string? WordConfidenceInterval0To10 { get; set; } [XmlElement("word_conf_11_20")] - public long? WordConfidenceInterval11To20 { get; set; } + public string? WordConfidenceInterval11To20 { get; set; } [XmlElement("word_conf_21_30")] - public long? WordConfidenceInterval21To30 { get; set; } + public string? WordConfidenceInterval21To30 { get; set; } [XmlElement("word_conf_31_40")] - public long? WordConfidenceInterval31To40 { get; set; } + public string? WordConfidenceInterval31To40 { get; set; } [XmlElement("word_conf_41_50")] - public long? WordConfidenceInterval41To50 { get; set; } + public string? WordConfidenceInterval41To50 { get; set; } [XmlElement("word_conf_51_60")] - public long? WordConfidenceInterval51To60 { get; set; } + public string? WordConfidenceInterval51To60 { get; set; } [XmlElement("word_conf_61_70")] - public long? WordConfidenceInterval61To70 { get; set; } + public string? WordConfidenceInterval61To70 { get; set; } [XmlElement("word_conf_71_80")] - public long? WordConfidenceInterval71To80 { get; set; } + public string? WordConfidenceInterval71To80 { get; set; } [XmlElement("word_conf_81_90")] - public long? WordConfidenceInterval81To90 { get; set; } + public string? WordConfidenceInterval81To90 { get; set; } [XmlElement("word_conf_91_100")] - public long? WordConfidenceInterval91To100 { get; set; } + public string? WordConfidenceInterval91To100 { get; set; } #endregion @@ -173,32 +173,32 @@ namespace SabreTools.Models.ArchiveDotOrg public string? Artist { get; set; } [XmlElement("bitrate")] - public long? Bitrate { get; set; } + public string? Bitrate { get; set; } [XmlElement("creator")] public string? Creator { get; set; } [XmlElement("height")] - public long? Height { get; set; } + public string? Height { get; set; } [XmlElement("length")] - public double? Length { get; set; } + public string? Length { get; set; } [XmlElement("preview-image")] public string? PreviewImage { get; set; } /// Is this a set of defined values? [XmlElement("rotation")] - public long? Rotation { get; set; } + public string? Rotation { get; set; } [XmlElement("title")] public string? Title { get; set; } [XmlElement("track")] - public long? Track { get; set; } + public string? Track { get; set; } [XmlElement("width")] - public long? Width { get; set; } + public string? Width { get; set; } #endregion diff --git a/SabreTools.Serialization/AttractMode.Serializer.cs b/SabreTools.Serialization/AttractMode.Serializer.cs index 24a91777..28ab85fd 100644 --- a/SabreTools.Serialization/AttractMode.Serializer.cs +++ b/SabreTools.Serialization/AttractMode.Serializer.cs @@ -24,7 +24,6 @@ namespace SabreTools.Serialization return false; using var fs = File.OpenWrite(path); - stream.Seek(0, SeekOrigin.Begin); stream.CopyTo(fs); return true; } @@ -57,6 +56,7 @@ namespace SabreTools.Serialization WriteRows(metadataFile.Row, writer); // Return the stream + stream.Seek(0, SeekOrigin.Begin); return stream; } diff --git a/SabreTools.Serialization/ClrMamePro.Serializer.cs b/SabreTools.Serialization/ClrMamePro.Serializer.cs index 0724201e..527810ec 100644 --- a/SabreTools.Serialization/ClrMamePro.Serializer.cs +++ b/SabreTools.Serialization/ClrMamePro.Serializer.cs @@ -25,7 +25,6 @@ namespace SabreTools.Serialization return false; using var fs = File.OpenWrite(path); - stream.Seek(0, SeekOrigin.Begin); stream.CopyTo(fs); return true; } @@ -53,6 +52,7 @@ namespace SabreTools.Serialization WriteGames(metadataFile.Game, writer); // Return the stream + stream.Seek(0, SeekOrigin.Begin); return stream; } diff --git a/SabreTools.Serialization/DosCenter.Serializer.cs b/SabreTools.Serialization/DosCenter.Serializer.cs index 7004da15..fe774bc2 100644 --- a/SabreTools.Serialization/DosCenter.Serializer.cs +++ b/SabreTools.Serialization/DosCenter.Serializer.cs @@ -23,7 +23,6 @@ namespace SabreTools.Serialization return false; using var fs = System.IO.File.OpenWrite(path); - stream.Seek(0, SeekOrigin.Begin); stream.CopyTo(fs); return true; } @@ -53,6 +52,7 @@ namespace SabreTools.Serialization WriteGames(metadataFile.Game, writer); // Return the stream + stream.Seek(0, SeekOrigin.Begin); return stream; } diff --git a/SabreTools.Serialization/EverdriveSMDB.Serializer.cs b/SabreTools.Serialization/EverdriveSMDB.Serializer.cs index 6cb44db6..f6ed64ba 100644 --- a/SabreTools.Serialization/EverdriveSMDB.Serializer.cs +++ b/SabreTools.Serialization/EverdriveSMDB.Serializer.cs @@ -25,7 +25,6 @@ namespace SabreTools.Serialization return false; using var fs = File.OpenWrite(path); - stream.Seek(0, SeekOrigin.Begin); stream.CopyTo(fs); return true; } @@ -49,6 +48,7 @@ namespace SabreTools.Serialization WriteRows(metadataFile.Row, writer); // Return the stream + stream.Seek(0, SeekOrigin.Begin); return stream; } diff --git a/SabreTools.Serialization/Hashfile.Serializer.cs b/SabreTools.Serialization/Hashfile.Serializer.cs index 83343910..fb189b86 100644 --- a/SabreTools.Serialization/Hashfile.Serializer.cs +++ b/SabreTools.Serialization/Hashfile.Serializer.cs @@ -27,7 +27,6 @@ namespace SabreTools.Serialization return false; using var fs = File.OpenWrite(path); - stream.Seek(0, SeekOrigin.Begin); stream.CopyTo(fs); return true; } @@ -82,6 +81,7 @@ namespace SabreTools.Serialization } // Return the stream + stream.Seek(0, SeekOrigin.Begin); return stream; } diff --git a/SabreTools.Serialization/Listrom.Serializer.cs b/SabreTools.Serialization/Listrom.Serializer.cs index 55b222ea..f0ded159 100644 --- a/SabreTools.Serialization/Listrom.Serializer.cs +++ b/SabreTools.Serialization/Listrom.Serializer.cs @@ -23,7 +23,6 @@ namespace SabreTools.Serialization return false; using var fs = File.OpenWrite(path); - stream.Seek(0, SeekOrigin.Begin); stream.CopyTo(fs); return true; } @@ -47,6 +46,7 @@ namespace SabreTools.Serialization WriteSets(metadataFile.Set, writer); // Return the stream + stream.Seek(0, SeekOrigin.Begin); return stream; } diff --git a/SabreTools.Serialization/RomCenter.Serializer.cs b/SabreTools.Serialization/RomCenter.Serializer.cs index b0ebc6a3..4142f705 100644 --- a/SabreTools.Serialization/RomCenter.Serializer.cs +++ b/SabreTools.Serialization/RomCenter.Serializer.cs @@ -24,7 +24,6 @@ namespace SabreTools.Serialization return false; using var fs = File.OpenWrite(path); - stream.Seek(0, SeekOrigin.Begin); stream.CopyTo(fs); return true; } @@ -57,6 +56,7 @@ namespace SabreTools.Serialization WriteGames(metadataFile.Games, writer); // Return the stream + stream.Seek(0, SeekOrigin.Begin); return stream; } diff --git a/SabreTools.Serialization/SeparatedValue.Serializer.cs b/SabreTools.Serialization/SeparatedValue.Serializer.cs index 008284b6..9421d5f0 100644 --- a/SabreTools.Serialization/SeparatedValue.Serializer.cs +++ b/SabreTools.Serialization/SeparatedValue.Serializer.cs @@ -25,7 +25,6 @@ namespace SabreTools.Serialization return false; using var fs = File.OpenWrite(path); - stream.Seek(0, SeekOrigin.Begin); stream.CopyTo(fs); return true; } @@ -54,6 +53,7 @@ namespace SabreTools.Serialization WriteRows(metadataFile.Row, writer); // Return the stream + stream.Seek(0, SeekOrigin.Begin); return stream; } diff --git a/SabreTools.Serialization/XmlSerializer.Serializer.cs b/SabreTools.Serialization/XmlSerializer.Serializer.cs index 763c07ff..1b726856 100644 --- a/SabreTools.Serialization/XmlSerializer.Serializer.cs +++ b/SabreTools.Serialization/XmlSerializer.Serializer.cs @@ -1,4 +1,5 @@ using System.IO; +using System.Text; using System.Xml; using System.Xml.Serialization; @@ -42,6 +43,9 @@ namespace SabreTools.Serialization var settings = new XmlWriterSettings { CheckCharacters = false, + Encoding = Encoding.UTF8, + Indent = true, + NewLineChars = "\n", }; var stream = new MemoryStream(); var streamWriter = new StreamWriter(stream); @@ -49,6 +53,7 @@ namespace SabreTools.Serialization // Perform the deserialization and return serializer.Serialize(xmlWriter, obj); + stream.Seek(0, SeekOrigin.Begin); return stream; } }