Port Reports namespace from ST

This commit is contained in:
Matt Nadareski
2026-04-20 14:03:49 -04:00
parent c179cf410b
commit 2bf686675e
15 changed files with 847 additions and 0 deletions

View File

@@ -85,6 +85,7 @@ Below are a list of the included namespaces with links to their README files:
- [SabreTools.Metadata.DatFiles](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Metadata.DatFiles)
- [SabreTools.Metadata.DatItems](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Metadata.Datitems)
- [SabreTools.Metadata.Filter](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Metadata.Filter)
- [SabreTools.Metadata.Reports](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Metadata.Reports)
- [SabreTools.ObjectIdentifier](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.ObjectIdentifier)
- [SabreTools.Serialization.CrossModel](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Serialization.CrossModel)
- [SabreTools.Serialization.Readers](https://github.com/SabreTools/SabreTools.Serialization/tree/main/SabreTools.Serialization.Readers)

View File

@@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.IO;
using SabreTools.Logging;
using SabreTools.Metadata.DatFiles;
#pragma warning disable IDE0290 // Use primary constructor
namespace SabreTools.Metadata.Reports
{
/// <summary>
/// Base class for a report output format
/// </summary>
public abstract class BaseReport
{
#region Logging
/// <summary>
/// Logging object
/// </summary>
protected readonly Logger _logger = new();
#endregion
/// <summary>
/// Set of DatStatistics objects to use for formatting
/// </summary>
protected List<DatStatistics> _statistics;
/// <summary>
/// Create a new report from the filename
/// </summary>
/// <param name="statsList">List of statistics objects to set</param>
public BaseReport(List<DatStatistics> statsList)
{
_statistics = statsList;
}
/// <summary>
/// Create and open an output file for writing direct from a set of statistics
/// </summary>
/// <param name="outfile">Name of the file to write to</param>
/// <param name="baddumpCol">True if baddumps should be included in output, false otherwise</param>
/// <param name="nodumpCol">True if nodumps should be included in output, false otherwise</param>
/// <param name="throwOnError">True if the error that is thrown should be thrown back to the caller, false otherwise</param>
/// <returns>True if the report was written correctly, false otherwise</returns>
public bool WriteToFile(string? outfile, bool baddumpCol, bool nodumpCol, bool throwOnError = false)
{
InternalStopwatch watch = new($"Writing statistics to '{outfile}");
try
{
// Try to create the output file
FileStream stream = File.Create(outfile ?? string.Empty);
if (stream is null)
{
_logger.Warning($"File '{outfile}' could not be created for writing! Please check to see if the file is writable");
return false;
}
// Write to the stream
bool result = WriteToStream(stream, baddumpCol, nodumpCol, throwOnError);
// Dispose of the stream
stream.Dispose();
}
catch (Exception ex) when (!throwOnError)
{
_logger.Error(ex);
return false;
}
finally
{
watch.Stop();
}
return true;
}
/// <summary>
/// Write a set of statistics to an input stream
/// </summary>
/// <param name="stream">Stream to write to</param>
/// <param name="baddumpCol">True if baddumps should be included in output, false otherwise</param>
/// <param name="nodumpCol">True if nodumps should be included in output, false otherwise</param>
/// <param name="throwOnError">True if the error that is thrown should be thrown back to the caller, false otherwise</param>
/// <returns>True if the report was written correctly, false otherwise</returns>
public abstract bool WriteToStream(Stream stream, bool baddumpCol, bool nodumpCol, bool throwOnError = false);
}
}

View File

@@ -0,0 +1,38 @@
namespace SabreTools.Metadata.Reports
{
/// <summary>
/// Determine which format to output Stats to
/// </summary>
public enum StatReportFormat
{
/// <summary>
/// Only output to the console
/// </summary>
None,
/// <summary>
/// Console-formatted
/// </summary>
Textfile,
/// <summary>
/// ClrMamePro HTML
/// </summary>
HTML,
/// <summary>
/// Comma-Separated Values (Standardized)
/// </summary>
CSV,
/// <summary>
/// Semicolon-Separated Values (Standardized)
/// </summary>
SSV,
/// <summary>
/// Tab-Separated Values (Standardized)
/// </summary>
TSV,
}
}

View File

@@ -0,0 +1,20 @@
using System.Collections.Generic;
using SabreTools.Metadata.DatFiles;
namespace SabreTools.Metadata.Reports.Formats
{
/// <summary>
/// Represents a comma-separated value file
/// </summary>
public sealed class CommaSeparatedValue : SeparatedValue
{
/// <summary>
/// Create a new report from the filename
/// </summary>
/// <param name="statsList">List of statistics objects to set</param>
public CommaSeparatedValue(List<DatStatistics> statsList) : base(statsList)
{
_delim = ',';
}
}
}

View File

@@ -0,0 +1,329 @@
using System;
using System.Collections.Generic;
using System.IO;
#if NET40_OR_GREATER || NETCOREAPP || NETSTANDARD
using System.Net;
#endif
using System.Text;
using System.Xml;
using SabreTools.Hashing;
using SabreTools.Metadata.DatFiles;
using SabreTools.Text.Extensions;
using ItemStatus = SabreTools.Data.Models.Metadata.ItemStatus;
using ItemType = SabreTools.Data.Models.Metadata.ItemType;
#pragma warning disable IDE0290 // Use primary constructor
namespace SabreTools.Metadata.Reports.Formats
{
/// <summary>
/// HTML report format
/// </summary>
/// TODO: Make output standard width, without making the entire thing a table
public class Html : BaseReport
{
/// <summary>
/// Create a new report from the filename
/// </summary>
/// <param name="statsList">List of statistics objects to set</param>
public Html(List<DatStatistics> statsList)
: base(statsList)
{
}
/// <inheritdoc/>
public override bool WriteToStream(Stream stream, bool baddumpCol, bool nodumpCol, bool throwOnError = false)
{
try
{
XmlTextWriter xtw = new(stream, Encoding.UTF8)
{
Formatting = Formatting.Indented,
IndentChar = '\t',
Indentation = 1
};
// Write out the header
WriteHeader(xtw, baddumpCol, nodumpCol);
// Now process each of the statistics
for (int i = 0; i < _statistics.Count; i++)
{
// Get the current statistic
DatStatistics stat = _statistics[i];
// If we have a directory statistic
if (stat.IsDirectory)
{
WriteMidSeparator(xtw, baddumpCol, nodumpCol);
WriteIndividual(xtw, stat, baddumpCol, nodumpCol);
// If we have anything but the last value, write the separator
if (i < _statistics.Count - 1)
{
WriteFooterSeparator(xtw, baddumpCol, nodumpCol);
WriteMidHeader(xtw, baddumpCol, nodumpCol);
}
}
// If we have a normal statistic
else
{
WriteIndividual(xtw, stat, baddumpCol, nodumpCol);
}
}
WriteFooter(xtw);
#if NET452_OR_GREATER
xtw.Dispose();
#endif
}
catch (Exception ex) when (!throwOnError)
{
_logger.Error(ex);
return false;
}
return true;
}
/// <summary>
/// Write out the header to the stream, if any exists
/// </summary>
/// <param name="xtw">XmlTextWriter to write to</param>
/// <param name="baddumpCol">True if baddumps should be included in output, false otherwise</param>
/// <param name="nodumpCol">True if nodumps should be included in output, false otherwise</param>
private static void WriteHeader(XmlTextWriter xtw, bool baddumpCol, bool nodumpCol)
{
xtw.WriteDocType("html", null, null, null);
xtw.WriteStartElement("html");
xtw.WriteStartElement("header");
xtw.WriteElementString("title", "DAT Statistics Report");
xtw.WriteElementString("style", @"
body {
background-color: lightgray;
}
.dir {
color: #0088FF;
}");
xtw.WriteEndElement(); // header
xtw.WriteStartElement("body");
xtw.WriteElementString("h2", $"DAT Statistics Report ({DateTime.Now:d})");
xtw.WriteStartElement("table");
xtw.WriteAttributeString("border", "1");
xtw.WriteAttributeString("cellpadding", "5");
xtw.WriteAttributeString("cellspacing", "0");
xtw.Flush();
// Now write the mid header for those who need it
WriteMidHeader(xtw, baddumpCol, nodumpCol);
}
/// <summary>
/// Write out the mid-header to the stream, if any exists
/// </summary>
/// <param name="xtw">XmlTextWriter to write to</param>
/// <param name="baddumpCol">True if baddumps should be included in output, false otherwise</param>
/// <param name="nodumpCol">True if nodumps should be included in output, false otherwise</param>
private static void WriteMidHeader(XmlTextWriter xtw, bool baddumpCol, bool nodumpCol)
{
xtw.WriteStartElement("tr");
xtw.WriteAttributeString("bgcolor", "gray");
xtw.WriteElementString("th", "File Name");
xtw.WriteStartElement("th");
xtw.WriteAttributeString("align", "right");
xtw.WriteString("Total Size");
xtw.WriteEndElement(); // th
xtw.WriteStartElement("th");
xtw.WriteAttributeString("align", "right");
xtw.WriteString("Games");
xtw.WriteEndElement(); // th
xtw.WriteStartElement("th");
xtw.WriteAttributeString("align", "right");
xtw.WriteString("Roms");
xtw.WriteEndElement(); // th
xtw.WriteStartElement("th");
xtw.WriteAttributeString("align", "right");
xtw.WriteString("Disks");
xtw.WriteEndElement(); // th
xtw.WriteStartElement("th");
xtw.WriteAttributeString("align", "right");
xtw.WriteString("# with CRC");
xtw.WriteEndElement(); // th
xtw.WriteStartElement("th");
xtw.WriteAttributeString("align", "right");
xtw.WriteString("# with MD5");
xtw.WriteEndElement(); // th
xtw.WriteStartElement("th");
xtw.WriteAttributeString("align", "right");
xtw.WriteString("# with SHA-1");
xtw.WriteEndElement(); // th
xtw.WriteStartElement("th");
xtw.WriteAttributeString("align", "right");
xtw.WriteString("# with SHA-256");
xtw.WriteEndElement(); // th
if (baddumpCol)
{
xtw.WriteStartElement("th");
xtw.WriteAttributeString("align", "right");
xtw.WriteString("Baddumps");
xtw.WriteEndElement(); // th
}
if (nodumpCol)
{
xtw.WriteStartElement("th");
xtw.WriteAttributeString("align", "right");
xtw.WriteString("Nodumps");
xtw.WriteEndElement(); // th
}
xtw.WriteEndElement(); // tr
xtw.Flush();
}
/// <summary>
/// Write a single set of statistics
/// </summary>
/// <param name="xtw">XmlTextWriter to write to</param>
/// <param name="stat">DatStatistics object to write out</param>
/// <param name="baddumpCol">True if baddumps should be included in output, false ot
/// herwise</param>
/// <param name="nodumpCol">True if nodumps should be included in output, false otherwise</param>
private static void WriteIndividual(XmlTextWriter xtw, DatStatistics stat, bool baddumpCol, bool nodumpCol)
{
bool isDirectory = stat.DisplayName!.StartsWith("DIR: ");
xtw.WriteStartElement("tr");
if (isDirectory)
xtw.WriteAttributeString("class", "dir");
#if NET20 || NET35
xtw.WriteElementString("td", isDirectory ? stat.DisplayName.Remove(0, 5) : stat.DisplayName);
#else
xtw.WriteElementString("td", isDirectory ? WebUtility.HtmlEncode(stat.DisplayName.Remove(0, 5)) : WebUtility.HtmlEncode(stat.DisplayName));
#endif
xtw.WriteStartElement("td");
xtw.WriteAttributeString("align", "right");
xtw.WriteString(NumberHelper.GetBytesReadable(stat.TotalSize));
xtw.WriteEndElement(); // td
xtw.WriteStartElement("td");
xtw.WriteAttributeString("align", "right");
xtw.WriteString(stat.MachineCount.ToString());
xtw.WriteEndElement(); // td
xtw.WriteStartElement("td");
xtw.WriteAttributeString("align", "right");
xtw.WriteString(stat.GetItemCount(ItemType.Rom).ToString());
xtw.WriteEndElement(); // td
xtw.WriteStartElement("td");
xtw.WriteAttributeString("align", "right");
xtw.WriteString(stat.GetItemCount(ItemType.Disk).ToString());
xtw.WriteEndElement(); // td
xtw.WriteStartElement("td");
xtw.WriteAttributeString("align", "right");
xtw.WriteString(stat.GetHashCount(HashType.CRC32).ToString());
xtw.WriteEndElement(); // td
xtw.WriteStartElement("td");
xtw.WriteAttributeString("align", "right");
xtw.WriteString(stat.GetHashCount(HashType.MD5).ToString());
xtw.WriteEndElement(); // td
xtw.WriteStartElement("td");
xtw.WriteAttributeString("align", "right");
xtw.WriteString(stat.GetHashCount(HashType.SHA1).ToString());
xtw.WriteEndElement(); // td
xtw.WriteStartElement("td");
xtw.WriteAttributeString("align", "right");
xtw.WriteString(stat.GetHashCount(HashType.SHA256).ToString());
xtw.WriteEndElement(); // td
if (baddumpCol)
{
xtw.WriteStartElement("td");
xtw.WriteAttributeString("align", "right");
xtw.WriteString(stat.GetStatusCount(ItemStatus.BadDump).ToString());
xtw.WriteEndElement(); // td
}
if (nodumpCol)
{
xtw.WriteStartElement("td");
xtw.WriteAttributeString("align", "right");
xtw.WriteString(stat.GetStatusCount(ItemStatus.Nodump).ToString());
xtw.WriteEndElement(); // td
}
xtw.WriteEndElement(); // tr
xtw.Flush();
}
/// <summary>
/// Write out the separator to the stream, if any exists
/// </summary>
/// <param name="xtw">XmlTextWriter to write to</param>
/// <param name="baddumpCol">True if baddumps should be included in output, false otherwise</param>
/// <param name="nodumpCol">True if nodumps should be included in output, false otherwise</param>
private static void WriteMidSeparator(XmlTextWriter xtw, bool baddumpCol, bool nodumpCol)
{
xtw.WriteStartElement("tr");
xtw.WriteStartElement("td");
xtw.WriteAttributeString("colspan", baddumpCol && nodumpCol ? "12" : (baddumpCol ^ nodumpCol ? "11" : "10"));
xtw.WriteEndElement(); // td
xtw.WriteEndElement(); // tr
xtw.Flush();
}
/// <summary>
/// Write out the footer-separator to the stream, if any exists
/// </summary>
/// <param name="xtw">XmlTextWriter to write to</param>
/// <param name="baddumpCol">True if baddumps should be included in output, false otherwise</param>
/// <param name="nodumpCol">True if nodumps should be included in output, false otherwise</param>
private static void WriteFooterSeparator(XmlTextWriter xtw, bool baddumpCol, bool nodumpCol)
{
xtw.WriteStartElement("tr");
xtw.WriteAttributeString("border", "0");
xtw.WriteStartElement("td");
xtw.WriteAttributeString("colspan", baddumpCol && nodumpCol ? "12" : (baddumpCol ^ nodumpCol ? "11" : "10"));
xtw.WriteEndElement(); // td
xtw.WriteEndElement(); // tr
xtw.Flush();
}
/// <summary>
/// Write out the footer to the stream, if any exists
/// </summary>
/// <param name="xtw">XmlTextWriter to write to</param>
private static void WriteFooter(XmlTextWriter xtw)
{
xtw.WriteEndElement(); // table
xtw.WriteEndElement(); // body
xtw.WriteEndElement(); // html
xtw.Flush();
}
}
}

View File

@@ -0,0 +1,20 @@
using System.Collections.Generic;
using SabreTools.Metadata.DatFiles;
namespace SabreTools.Metadata.Reports.Formats
{
/// <summary>
/// Represents a semicolon-separated value file
/// </summary>
public sealed class SemicolonSeparatedValue : SeparatedValue
{
/// <summary>
/// Create a new report from the filename
/// </summary>
/// <param name="statsList">List of statistics objects to set</param>
public SemicolonSeparatedValue(List<DatStatistics> statsList) : base(statsList)
{
_delim = ';';
}
}
}

View File

@@ -0,0 +1,148 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using SabreTools.Hashing;
using SabreTools.Metadata.DatFiles;
using SabreTools.Text.SeparatedValue;
using ItemStatus = SabreTools.Data.Models.Metadata.ItemStatus;
using ItemType = SabreTools.Data.Models.Metadata.ItemType;
#pragma warning disable IDE0290 // Use primary constructor
namespace SabreTools.Metadata.Reports.Formats
{
/// <summary>
/// Separated-Value report format
/// </summary>
public abstract class SeparatedValue : BaseReport
{
/// <summary>
/// Represents the delimiter between fields
/// </summary>
protected char _delim;
/// <summary>
/// Create a new report from the filename
/// </summary>
/// <param name="statsList">List of statistics objects to set</param>
public SeparatedValue(List<DatStatistics> statsList)
: base(statsList)
{
}
/// <inheritdoc/>
public override bool WriteToStream(Stream stream, bool baddumpCol, bool nodumpCol, bool throwOnError = false)
{
try
{
Writer svw = new(stream, Encoding.UTF8)
{
Separator = _delim,
Quotes = true,
};
// Write out the header
WriteHeader(svw, baddumpCol, nodumpCol);
// Now process each of the statistics
for (int i = 0; i < _statistics.Count; i++)
{
// Get the current statistic
DatStatistics stat = _statistics[i];
// If we have a directory statistic
if (stat.IsDirectory)
{
WriteIndividual(svw, stat, baddumpCol, nodumpCol);
// If we have anything but the last value, write the separator
if (i < _statistics.Count - 1)
WriteFooterSeparator(svw);
}
// If we have a normal statistic
else
{
WriteIndividual(svw, stat, baddumpCol, nodumpCol);
}
}
svw.Dispose();
}
catch (Exception ex) when (!throwOnError)
{
_logger.Error(ex);
return false;
}
return true;
}
/// <summary>
/// Write out the header to the stream, if any exists
/// </summary>
/// <param name="svw">Writer to write to</param>
/// <param name="baddumpCol">True if baddumps should be included in output, false otherwise</param>
/// <param name="nodumpCol">True if nodumps should be included in output, false otherwise</param>
private static void WriteHeader(Writer svw, bool baddumpCol, bool nodumpCol)
{
string[] headers =
[
"File Name",
"Total Size",
"Games",
"Roms",
"Disks",
"# with CRC",
"# with MD5",
"# with SHA-1",
"# with SHA-256",
"# with SHA-384",
"# with SHA-512",
baddumpCol ? "BadDumps" : string.Empty,
nodumpCol ? "Nodumps" : string.Empty,
];
svw.WriteHeader(headers);
svw.Flush();
}
/// <summary>
/// Write a single set of statistics
/// </summary>
/// <param name="svw">Writer to write to</param>
/// <param name="stat">DatStatistics object to write out</param>
/// <param name="baddumpCol">True if baddumps should be included in output, false otherwise</param>
/// <param name="nodumpCol">True if nodumps should be included in output, false otherwise</param>
private static void WriteIndividual(Writer svw, DatStatistics stat, bool baddumpCol, bool nodumpCol)
{
string[] values =
[
stat.DisplayName!,
stat.TotalSize.ToString(),
stat.MachineCount.ToString(),
stat.GetItemCount(ItemType.Rom).ToString(),
stat.GetItemCount(ItemType.Disk).ToString(),
stat.GetHashCount(HashType.CRC32).ToString(),
stat.GetHashCount(HashType.MD5).ToString(),
stat.GetHashCount(HashType.SHA1).ToString(),
stat.GetHashCount(HashType.SHA256).ToString(),
stat.GetHashCount(HashType.SHA384).ToString(),
stat.GetHashCount(HashType.SHA512).ToString(),
baddumpCol ? stat.GetStatusCount(ItemStatus.BadDump).ToString() : string.Empty,
nodumpCol ? stat.GetStatusCount(ItemStatus.Nodump).ToString() : string.Empty,
];
svw.WriteValues(values);
svw.Flush();
}
/// <summary>
/// Write out the footer-separator to the stream, if any exists
/// </summary>
/// <param name="svw">Writer to write to</param>
private static void WriteFooterSeparator(Writer svw)
{
svw.WriteString("\n");
svw.Flush();
}
}
}

View File

@@ -0,0 +1,20 @@
using System.Collections.Generic;
using SabreTools.Metadata.DatFiles;
namespace SabreTools.Metadata.Reports.Formats
{
/// <summary>
/// Represents a tab-separated value file
/// </summary>
public sealed class TabSeparatedValue : SeparatedValue
{
/// <summary>
/// Create a new report from the filename
/// </summary>
/// <param name="statsList">List of statistics objects to set</param>
public TabSeparatedValue(List<DatStatistics> statsList) : base(statsList)
{
_delim = '\t';
}
}
}

View File

@@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using SabreTools.Hashing;
using SabreTools.Metadata.DatFiles;
using SabreTools.Text.Extensions;
using ItemStatus = SabreTools.Data.Models.Metadata.ItemStatus;
using ItemType = SabreTools.Data.Models.Metadata.ItemType;
#pragma warning disable IDE0290 // Use primary constructor
namespace SabreTools.Metadata.Reports.Formats
{
/// <summary>
/// Textfile report format
/// </summary>
public class Textfile : BaseReport
{
/// <summary>
/// Create a new report from the filename
/// </summary>
/// <param name="statsList">List of statistics objects to set</param>
public Textfile(List<DatStatistics> statsList)
: base(statsList)
{
}
/// <inheritdoc/>
public override bool WriteToStream(Stream stream, bool baddumpCol, bool nodumpCol, bool throwOnError = false)
{
try
{
StreamWriter sw = new(stream);
// Now process each of the statistics
for (int i = 0; i < _statistics.Count; i++)
{
// Get the current statistic
DatStatistics stat = _statistics[i];
// If we have a directory statistic
if (stat.IsDirectory)
{
WriteIndividual(sw, stat, baddumpCol, nodumpCol);
// If we have anything but the last value, write the separator
if (i < _statistics.Count - 1)
WriteFooterSeparator(sw);
}
// If we have a normal statistic
else
{
WriteIndividual(sw, stat, baddumpCol, nodumpCol);
}
}
sw.Dispose();
}
catch (Exception ex) when (!throwOnError)
{
_logger.Error(ex);
return false;
}
return true;
}
/// <summary>
/// Write a single set of statistics
/// </summary>
/// <param name="sw">StreamWriter to write to</param>
/// <param name="stat">DatStatistics object to write out</param>
/// <param name="baddumpCol">True if baddumps should be included in output, false otherwise</param>
/// <param name="nodumpCol">True if nodumps should be included in output, false otherwise</param>
private static void WriteIndividual(StreamWriter sw, DatStatistics stat, bool baddumpCol, bool nodumpCol)
{
var line = new StringBuilder();
line.AppendLine($"'{stat.DisplayName}':");
line.AppendLine($"--------------------------------------------------");
line.AppendLine($" Uncompressed size: {NumberHelper.GetBytesReadable(stat!.TotalSize)}");
line.AppendLine($" Games found: {stat.MachineCount}");
line.AppendLine($" Roms found: {stat.GetItemCount(ItemType.Rom)}");
line.AppendLine($" Disks found: {stat.GetItemCount(ItemType.Disk)}");
line.AppendLine($" Roms with CRC-32: {stat.GetHashCount(HashType.CRC32)}");
line.AppendLine($" Roms with MD5: {stat.GetHashCount(HashType.MD5)}");
line.AppendLine($" Roms with SHA-1: {stat.GetHashCount(HashType.SHA1)}");
line.AppendLine($" Roms with SHA-256: {stat.GetHashCount(HashType.SHA256)}");
line.AppendLine($" Roms with SHA-384: {stat.GetHashCount(HashType.SHA384)}");
line.AppendLine($" Roms with SHA-512: {stat.GetHashCount(HashType.SHA512)}");
if (baddumpCol)
line.AppendLine($" Roms with BadDump status: {stat.GetStatusCount(ItemStatus.BadDump)}");
if (nodumpCol)
line.AppendLine($" Roms with Nodump status: {stat.GetStatusCount(ItemStatus.Nodump)}");
// For spacing between DATs
line.AppendLine();
line.AppendLine();
sw.Write(line.ToString());
sw.Flush();
}
/// <summary>
/// Write out the footer-separator to the stream, if any exists
/// </summary>
/// <param name="sw">StreamWriter to write to</param>
private static void WriteFooterSeparator(StreamWriter sw)
{
sw.Write("\n");
sw.Flush();
}
}
}

View File

@@ -0,0 +1,3 @@
# SabreTools.Metadata.Reports
This library contains report generating functionality used by metadata format processing.

View File

@@ -0,0 +1,44 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- Assembly Properties -->
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0;net10.0;netstandard2.0;netstandard2.1</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<IncludeSymbols>true</IncludeSymbols>
<LangVersion>latest</LangVersion>
<NoWarn>NU1902;NU1903</NoWarn>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>2.3.0</Version>
<!-- Package Properties -->
<Authors>Matt Nadareski</Authors>
<Description>Report functionality for metadata file processing</Description>
<Copyright>Copyright (c) Matt Nadareski 2016-2026</Copyright>
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/SabreTools/SabreTools.Serialization</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>metadata dat datfile report</PackageTags>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup>
<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SabreTools.Data.Models\SabreTools.Data.Models.csproj" />
<ProjectReference Include="..\SabreTools.Metadata.DatFiles\SabreTools.Metadata.DatFiles.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SabreTools.Hashing" Version="[2.0.0]" />
<PackageReference Include="SabreTools.IO" Version="[2.0.0]" />
</ItemGroup>
</Project>

View File

@@ -47,6 +47,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SabreTools.Metadata.DatFile
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SabreTools.Metadata.DatFiles.Test", "SabreTools.Metadata.DatFiles.Test\SabreTools.Metadata.DatFiles.Test.csproj", "{EFF85ED6-27D6-4076-A935-9792D2975078}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SabreTools.Metadata.Reports", "SabreTools.Metadata.Reports\SabreTools.Metadata.Reports.csproj", "{6CDE2071-784B-4072-9EAE-33F0AE00566C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -321,6 +323,18 @@ Global
{EFF85ED6-27D6-4076-A935-9792D2975078}.Release|x64.Build.0 = Release|Any CPU
{EFF85ED6-27D6-4076-A935-9792D2975078}.Release|x86.ActiveCfg = Release|Any CPU
{EFF85ED6-27D6-4076-A935-9792D2975078}.Release|x86.Build.0 = Release|Any CPU
{6CDE2071-784B-4072-9EAE-33F0AE00566C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6CDE2071-784B-4072-9EAE-33F0AE00566C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6CDE2071-784B-4072-9EAE-33F0AE00566C}.Debug|x64.ActiveCfg = Debug|Any CPU
{6CDE2071-784B-4072-9EAE-33F0AE00566C}.Debug|x64.Build.0 = Debug|Any CPU
{6CDE2071-784B-4072-9EAE-33F0AE00566C}.Debug|x86.ActiveCfg = Debug|Any CPU
{6CDE2071-784B-4072-9EAE-33F0AE00566C}.Debug|x86.Build.0 = Debug|Any CPU
{6CDE2071-784B-4072-9EAE-33F0AE00566C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6CDE2071-784B-4072-9EAE-33F0AE00566C}.Release|Any CPU.Build.0 = Release|Any CPU
{6CDE2071-784B-4072-9EAE-33F0AE00566C}.Release|x64.ActiveCfg = Release|Any CPU
{6CDE2071-784B-4072-9EAE-33F0AE00566C}.Release|x64.Build.0 = Release|Any CPU
{6CDE2071-784B-4072-9EAE-33F0AE00566C}.Release|x86.ActiveCfg = Release|Any CPU
{6CDE2071-784B-4072-9EAE-33F0AE00566C}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -46,6 +46,8 @@
<Compile Include="..\SabreTools.Metadata.DatItems\*.cs" />
<Compile Include="..\SabreTools.Metadata.DatItems\Formats\*.cs" />
<Compile Include="..\SabreTools.Metadata.Filter\*.cs" />
<Compile Include="..\SabreTools.Metadata.Reports\*.cs" />
<Compile Include="..\SabreTools.Metadata.Reports\Formats\*.cs" />
<Compile Include="..\SabreTools.ObjectIdentifier\*.cs" />
<Compile Include="..\SabreTools.Serialization.CrossModel\*.cs" Exclude="..\SabreTools.Serialization.CrossModel\ExtensionAttribute.cs" />
<Compile Include="..\SabreTools.Serialization.Readers\*.cs" />

View File

@@ -84,6 +84,7 @@ if [ $NO_BUILD = false ]; then
dotnet pack SabreTools.Metadata.DatFiles/SabreTools.Metadata.DatFiles.csproj --output $BUILD_FOLDER
dotnet pack SabreTools.Metadata.DatItems/SabreTools.Metadata.DatItems.csproj --output $BUILD_FOLDER
dotnet pack SabreTools.Metadata.Filter/SabreTools.Metadata.Filter.csproj --output $BUILD_FOLDER
dotnet pack SabreTools.Metadata.Reports/SabreTools.Metadata.Reports.csproj --output $BUILD_FOLDER
dotnet pack SabreTools.ObjectIdentifier/SabreTools.ObjectIdentifier.csproj --output $BUILD_FOLDER
dotnet pack SabreTools.Serialization.CrossModel/SabreTools.Serialization.CrossModel.csproj --output $BUILD_FOLDER
dotnet pack SabreTools.Serialization.Readers/SabreTools.Serialization.Readers.csproj --output $BUILD_FOLDER

View File

@@ -75,6 +75,7 @@ if (!$NO_BUILD.IsPresent) {
dotnet pack SabreTools.Metadata.DatFiles\SabreTools.Metadata.DatFiles.csproj --output $BUILD_FOLDER
dotnet pack SabreTools.Metadata.DatItems\SabreTools.Metadata.DatItems.csproj --output $BUILD_FOLDER
dotnet pack SabreTools.Metadata.Filter\SabreTools.Metadata.Filter.csproj --output $BUILD_FOLDER
dotnet pack SabreTools.Metadata.Reports\SabreTools.Metadata.Reports.csproj --output $BUILD_FOLDER
dotnet pack SabreTools.ObjectIdentifier\SabreTools.ObjectIdentifier.csproj --output $BUILD_FOLDER
dotnet pack SabreTools.Serialization.CrossModel\SabreTools.Serialization.CrossModel.csproj --output $BUILD_FOLDER
dotnet pack SabreTools.Serialization.Readers\SabreTools.Serialization.Readers.csproj --output $BUILD_FOLDER