diff --git a/SabreTools.Library/DatFiles/DatFile.cs b/SabreTools.Library/DatFiles/DatFile.cs index 60d805a8..cf6b0187 100644 --- a/SabreTools.Library/DatFiles/DatFile.cs +++ b/SabreTools.Library/DatFiles/DatFile.cs @@ -9,6 +9,7 @@ using System.Web; using SabreTools.Library.Data; using SabreTools.Library.FileTypes; using SabreTools.Library.DatItems; +using SabreTools.Library.Reports; using SabreTools.Library.Skippers; using SabreTools.Library.Tools; @@ -5399,14 +5400,11 @@ namespace SabreTools.Library.DatFiles /// /// Output the stats for the Dat in a human-readable format /// - /// Dictionary representing the outputs - /// Set the statistics output format to use /// True if numbers should be recalculated for the DAT, false otherwise (default) /// Number of games to use, -1 means recalculate games (default) /// True if baddumps should be included in output, false otherwise (default) /// True if nodumps should be included in output, false otherwise (default) - public void OutputStats(Dictionary outputs, StatDatFormat statDatFormat, - bool recalculate = false, long game = -1, bool baddumpCol = false, bool nodumpCol = false) + public void WriteStatsToScreen(bool recalculate = false, long game = -1, bool baddumpCol = false, bool nodumpCol = false) { // If we're supposed to recalculate the statistics, do so if (recalculate) @@ -5447,115 +5445,6 @@ namespace SabreTools.Library.DatFiles results += "\n\n"; Globals.Logger.User(results); - - // Now write it out to file as well - string line = ""; - if (outputs.ContainsKey(StatDatFormat.None)) - { - line = @"'" + FileName + @"': --------------------------------------------------- - Uncompressed size: " + Style.GetBytesReadable(TotalSize) + @" - Games found: " + (game == -1 ? Keys.Count() : game) + @" - Roms found: " + RomCount + @" - Disks found: " + DiskCount + @" - Roms with CRC: " + CRCCount + @" - Roms with SHA-1: " + SHA1Count + @" - Roms with SHA-256: " + SHA256Count + @" - Roms with SHA-384: " + SHA384Count + @" - Roms with SHA-512: " + SHA512Count + "\n"; - - if (baddumpCol) - { - line += " Roms with BadDump status: " + BaddumpCount + "\n"; - } - if (nodumpCol) - { - line += " Roms with Nodump status: " + NodumpCount + "\n"; - } - - // For spacing between DATs - line += "\n\n"; - - outputs[StatDatFormat.None].Write(line); - } - if (outputs.ContainsKey(StatDatFormat.CSV)) - { - line = "\"" + FileName + "\"," - + "\"" + TotalSize + "\"," - + "\"" + (game == -1 ? Keys.Count() : game) + "\"," - + "\"" + RomCount + "\"," - + "\"" + DiskCount + "\"," - + "\"" + CRCCount + "\"," - + "\"" + MD5Count + "\"," - + "\"" + SHA1Count + "\"," - + "\"" + SHA256Count + "\"," - + "\"" + SHA384Count + "\"," - + "\"" + SHA512Count + "\""; - - if (baddumpCol) - { - line += ",\"" + BaddumpCount + "\""; - } - if (nodumpCol) - { - line += ",\"" + NodumpCount + "\""; - } - - line += "\n"; - outputs[StatDatFormat.CSV].Write(line); - } - if (outputs.ContainsKey(StatDatFormat.HTML)) - { - line = "\t\t\t" + HttpUtility.HtmlEncode(FileName.Remove(0, 5)) - : ">" + HttpUtility.HtmlEncode(FileName)) + "" - + "" + Style.GetBytesReadable(TotalSize) + "" - + "" + (game == -1 ? Keys.Count() : game) + "" - + "" + RomCount + "" - + "" + DiskCount + "" - + "" + CRCCount + "" - + "" + MD5Count + "" - + "" + SHA1Count + "" - + "" + SHA256Count + ""; - - if (baddumpCol) - { - line += "" + BaddumpCount + ""; - } - if (nodumpCol) - { - line += "" + NodumpCount + ""; - } - - line += "\n"; - outputs[StatDatFormat.HTML].Write(line); - } - if (outputs.ContainsKey(StatDatFormat.TSV)) - { - line = "\"" + FileName + "\"\t" - + "\"" + TotalSize + "\"\t" - + "\"" + (game == -1 ? Keys.Count() : game) + "\"\t" - + "\"" + RomCount + "\"\t" - + "\"" + DiskCount + "\"\t" - + "\"" + CRCCount + "\"\t" - + "\"" + MD5Count + "\"\t" - + "\"" + SHA1Count + "\"\t" - + "\"" + SHA256Count + "\"\t" - + "\"" + SHA384Count + "\"\t" - + "\"" + SHA512Count + "\""; - - if (baddumpCol) - { - line += "\t\"" + BaddumpCount + "\""; - } - if (nodumpCol) - { - line += "\t\"" + NodumpCount + "\""; - } - - line += "\n"; - outputs[StatDatFormat.TSV].Write(line); - } } /// @@ -5664,8 +5553,7 @@ namespace SabreTools.Library.DatFiles // Output initial statistics, for kicks if (stats) { - OutputStats(new Dictionary(), StatDatFormat.None, - recalculate: (RomCount + DiskCount == 0), baddumpCol: true, nodumpCol: true); + WriteStatsToScreen(recalculate: (RomCount + DiskCount == 0), baddumpCol: true, nodumpCol: true); } // Bucket and dedupe according to the flag @@ -5802,12 +5690,12 @@ namespace SabreTools.Library.DatFiles /// True if nodumps should be included in output, false otherwise /// Set the statistics output format to use public static void OutputStats(List inputs, string reportName, string outDir, bool single, - bool baddumpCol, bool nodumpCol, StatDatFormat statDatFormat) + bool baddumpCol, bool nodumpCol, StatReportFormat statDatFormat) { // If there's no output format, set the default if (statDatFormat == 0x0) { - statDatFormat = StatDatFormat.None; + statDatFormat = StatReportFormat.None; } // Get the proper output file name @@ -5818,7 +5706,7 @@ namespace SabreTools.Library.DatFiles outDir = Path.GetFullPath(outDir); // Get the dictionary of desired output report names - Dictionary outputs = Style.CreateOutStatsNames(outDir, statDatFormat, reportName); + Dictionary outputs = Style.CreateOutStatsNames(outDir, statDatFormat, reportName); // Make sure we have all files and then order them List files = FileTools.GetOnlyFilesFromInputs(inputs); @@ -5827,19 +5715,35 @@ namespace SabreTools.Library.DatFiles .ThenBy(i => Path.GetFileName(i)) .ToList(); - // Create output writers based on filenames - Dictionary writers = new Dictionary(); - foreach (KeyValuePair kvp in outputs) + // Get all of the writers that we need + List reports = new List(); + + // Loop through and output based on the inputs + foreach (KeyValuePair kvp in outputs) { - FileStream fs = FileTools.TryCreate(kvp.Value); - if (fs != null) + // Create the proper report for this format + BaseReport report = null; + switch (kvp.Key) { - writers.Add(kvp.Key, new StreamWriter(fs)); + case StatReportFormat.None: + report = new Textfile(null, kvp.Value, baddumpCol, nodumpCol); + break; + case StatReportFormat.CSV: + report = new Reports.SeparatedValue(null, kvp.Value, ',', baddumpCol, nodumpCol); + break; + case StatReportFormat.HTML: + report = new Html(null, kvp.Value, baddumpCol, nodumpCol); + break; + case StatReportFormat.TSV: + report = new Reports.SeparatedValue(null, kvp.Value, '\t', baddumpCol, nodumpCol); + break; } + + reports.Add(report); } // Write the header, if any - WriteStatsHeader(writers, statDatFormat, baddumpCol, nodumpCol); + reports.ForEach(report => report.WriteStatsHeader()); // Init all total variables DatStats totalStats = new DatStats(); @@ -5860,7 +5764,7 @@ namespace SabreTools.Library.DatFiles if (lastdir != null && thisdir != lastdir) { // Output separator if needed - WriteStatsMidSeparator(writers, statDatFormat, baddumpCol, nodumpCol); + reports.ForEach(report => report.WriteStatsMidSeparator()); DatFile lastdirdat = new DatFile { @@ -5868,13 +5772,15 @@ namespace SabreTools.Library.DatFiles _datStats = dirStats, }; - lastdirdat.OutputStats(writers, statDatFormat, game: dirStats.GameCount, baddumpCol: baddumpCol, nodumpCol: nodumpCol); + lastdirdat.WriteStatsToScreen(recalculate: false, game: dirStats.GameCount, baddumpCol: baddumpCol, nodumpCol: nodumpCol); + reports.ForEach(report => report.ReplaceDatFile(lastdirdat)); + reports.ForEach(report => report.Write(game: dirStats.GameCount)); // Write the mid-footer, if any - WriteStatsFooterSeparator(writers, statDatFormat, baddumpCol, nodumpCol); + reports.ForEach(report => report.WriteStatsFooterSeparator()); // Write the header, if any - WriteStatsMidHeader(writers, statDatFormat, baddumpCol, nodumpCol); + reports.ForEach(report => report.WriteStatsMidHeader()); // Reset the directory stats dirStats.Reset(); @@ -5890,8 +5796,9 @@ namespace SabreTools.Library.DatFiles Globals.Logger.User("Adding stats for file '{0}'\n", false, file); if (single) { - datdata.OutputStats(writers, statDatFormat, - baddumpCol: baddumpCol, nodumpCol: nodumpCol); + datdata.WriteStatsToScreen(recalculate: false, baddumpCol: baddumpCol, nodumpCol: nodumpCol); + reports.ForEach(report => report.ReplaceDatFile(datdata)); + reports.ForEach(report => report.Write()); } // Add single DAT stats to dir @@ -5907,7 +5814,7 @@ namespace SabreTools.Library.DatFiles } // Output the directory stats one last time - WriteStatsMidSeparator(writers, statDatFormat, baddumpCol, nodumpCol); + reports.ForEach(report => report.WriteStatsMidSeparator()); if (single) { @@ -5917,14 +5824,16 @@ namespace SabreTools.Library.DatFiles _datStats = dirStats, }; - dirdat.OutputStats(writers, statDatFormat, game: dirStats.GameCount, baddumpCol: baddumpCol, nodumpCol: nodumpCol); + dirdat.WriteStatsToScreen(recalculate: false, game: dirStats.GameCount, baddumpCol: baddumpCol, nodumpCol: nodumpCol); + reports.ForEach(report => report.ReplaceDatFile(dirdat)); + reports.ForEach(report => report.Write(dirStats.GameCount)); } // Write the mid-footer, if any - WriteStatsFooterSeparator(writers, statDatFormat, baddumpCol, nodumpCol); + reports.ForEach(report => report.WriteStatsFooterSeparator()); // Write the header, if any - WriteStatsMidHeader(writers, statDatFormat, baddumpCol, nodumpCol); + reports.ForEach(report => report.WriteStatsMidHeader()); // Reset the directory stats dirStats.Reset(); @@ -5936,198 +5845,17 @@ namespace SabreTools.Library.DatFiles _datStats = totalStats, }; - totaldata.OutputStats(writers, statDatFormat, game: totalStats.GameCount, baddumpCol: baddumpCol, nodumpCol: nodumpCol); + totaldata.WriteStatsToScreen(recalculate: false, game: totalStats.GameCount, baddumpCol: baddumpCol, nodumpCol: nodumpCol); + reports.ForEach(report => report.ReplaceDatFile(totaldata)); + reports.ForEach(report => report.Write(totalStats.GameCount)); // Output footer if needed - WriteStatsFooter(writers, statDatFormat); - - // Flush and dispose of the stream writers - foreach (StatDatFormat format in outputs.Keys) - { - writers[format].Flush(); - writers[format].Dispose(); - } + reports.ForEach(report => report.WriteStatsFooter()); Globals.Logger.User(@" Please check the log folder if the stats scrolled offscreen", false); } - /// - /// Write out the header to the stream, if any exists - /// - /// Dictionary representing the outputs - /// StatDatFormat representing output format - /// True if baddumps should be included in output, false otherwise - /// True if nodumps should be included in output, false otherwise - private static void WriteStatsHeader(Dictionary outputs, StatDatFormat statDatFormat, bool baddumpCol, bool nodumpCol) - { - if (outputs.ContainsKey(StatDatFormat.None)) - { - // Nothing - } - if (outputs.ContainsKey(StatDatFormat.CSV)) - { - outputs[StatDatFormat.CSV].Write("\"File Name\",\"Total Size\",\"Games\",\"Roms\",\"Disks\",\"# with CRC\",\"# with MD5\",\"# with SHA-1\",\"# with SHA-256\"" - + (baddumpCol ? ",\"BadDumps\"" : "") + (nodumpCol ? ",\"Nodumps\"" : "") + "\n"); - } - if (outputs.ContainsKey(StatDatFormat.HTML)) - { - outputs[StatDatFormat.HTML].Write(@" - -
- DAT Statistics Report - -
- -

DAT Statistics Report (" + DateTime.Now.ToShortDateString() + @")

- -"); - } - if (outputs.ContainsKey(StatDatFormat.TSV)) - { - outputs[StatDatFormat.TSV].Write("\"File Name\"\t\"Total Size\"\t\"Games\"\t\"Roms\"\t\"Disks\"\t\"# with CRC\"\t\"# with MD5\"\t\"# with SHA-1\"\t\"# with SHA-256\"" - + (baddumpCol ? "\t\"BadDumps\"" : "") + (nodumpCol ? "\t\"Nodumps\"" : "") + "\n"); - } - - // Now write the mid header for those who need it - WriteStatsMidHeader(outputs, statDatFormat, baddumpCol, nodumpCol); - } - - /// - /// Write out the mid-header to the stream, if any exists - /// - /// Dictionary representing the outputs - /// StatDatFormat representing output format - /// True if baddumps should be included in output, false otherwise - /// True if nodumps should be included in output, false otherwise - private static void WriteStatsMidHeader(Dictionary outputs, StatDatFormat statDatFormat, bool baddumpCol, bool nodumpCol) - { - if (outputs.ContainsKey(StatDatFormat.None)) - { - // Nothing - } - if (outputs.ContainsKey(StatDatFormat.CSV)) - { - // Nothing - } - if (outputs.ContainsKey(StatDatFormat.HTML)) - { - outputs[StatDatFormat.HTML].Write(@" " -+ @"" -+ (baddumpCol ? "" : "") + (nodumpCol ? "" : "") + "\n"); - } - if (outputs.ContainsKey(StatDatFormat.TSV)) - { - // Nothing - } - } - - /// - /// Write out the separator to the stream, if any exists - /// - /// Dictionary representing the outputs - /// StatDatFormat representing output format - /// True if baddumps should be included in output, false otherwise - /// True if nodumps should be included in output, false otherwise - private static void WriteStatsMidSeparator(Dictionary outputs, StatDatFormat statDatFormat, bool baddumpCol, bool nodumpCol) - { - if (outputs.ContainsKey(StatDatFormat.None)) - { - // Nothing - } - if (outputs.ContainsKey(StatDatFormat.CSV)) - { - // Nothing - } - if (outputs.ContainsKey(StatDatFormat.HTML)) - { - outputs[StatDatFormat.HTML].Write("\n"); - } - if (outputs.ContainsKey(StatDatFormat.TSV)) - { - // Nothing - } - } - - /// - /// Write out the footer-separator to the stream, if any exists - /// - /// Dictionary representing the outputs - /// StatDatFormat representing output format - /// True if baddumps should be included in output, false otherwise - /// True if nodumps should be included in output, false otherwise - private static void WriteStatsFooterSeparator(Dictionary outputs, StatDatFormat statDatFormat, bool baddumpCol, bool nodumpCol) - { - if (outputs.ContainsKey(StatDatFormat.None)) - { - outputs[StatDatFormat.None].Write("\n"); - } - if (outputs.ContainsKey(StatDatFormat.CSV)) - { - outputs[StatDatFormat.CSV].Write("\n"); - } - if (outputs.ContainsKey(StatDatFormat.HTML)) - { - outputs[StatDatFormat.HTML].Write("\n"); - } - if (outputs.ContainsKey(StatDatFormat.TSV)) - { - outputs[StatDatFormat.TSV].Write("\n"); - } - } - - /// - /// Write out the footer to the stream, if any exists - /// - /// StreamWriter representing the output - /// StatDatFormat representing output format - private static void WriteStatsFooter(Dictionary outputs, StatDatFormat statDatFormat) - { - if (outputs.ContainsKey(StatDatFormat.None)) - { - // Nothing - } - if (outputs.ContainsKey(StatDatFormat.CSV)) - { - // Nothing - } - if (outputs.ContainsKey(StatDatFormat.HTML)) - { - outputs[StatDatFormat.HTML].Write(@"
File NameTotal SizeGamesRomsDisks# with CRC# with MD5# with SHA-1# with SHA-256BaddumpsNodumps
- - -"); - } - if (outputs.ContainsKey(StatDatFormat.TSV)) - { - // Nothing - } - } - #endregion #endregion // Static Methods diff --git a/SabreTools.Library/Data/Flags.cs b/SabreTools.Library/Data/Flags.cs index ee455447..fba694bc 100644 --- a/SabreTools.Library/Data/Flags.cs +++ b/SabreTools.Library/Data/Flags.cs @@ -246,12 +246,14 @@ namespace SabreTools.Library.Data /// Determine which format to output Stats to ///
/// [Flags] - public enum StatDatFormat + public enum StatReportFormat { None = 0x01, HTML = None << 1, CSV = HTML << 1, TSV = CSV << 1, + + All = None | HTML | CSV | TSV, } /// diff --git a/SabreTools.Library/README.1ST b/SabreTools.Library/README.1ST index e23e55a3..422e9fb8 100644 --- a/SabreTools.Library/README.1ST +++ b/SabreTools.Library/README.1ST @@ -810,6 +810,9 @@ Options: - Roms that include a SHA-1 - Roms with Nodump status + -as, --all-stats Write all statistics to all available formats + Output all rom information to all available formats + -bc, --baddump-col Add statistics for baddumps to output Add a new column or field for counting the number of baddumps in the DAT @@ -839,6 +842,10 @@ Options: -tsv, --tsv Output in Tab-Separated Value format Output all rom information in standardized TSV format + + -txt, --text Output in generic text format + Output all rom information in generic text format. If no other format + flags are enabled, this is the default output. -ts, --type-split Split DAT(s) or folder by file types (rom/disk) For a DAT, or set of DATs, allow for splitting based on the types of the diff --git a/SabreTools.Library/Reports/BaseReport.cs b/SabreTools.Library/Reports/BaseReport.cs new file mode 100644 index 00000000..62d6562f --- /dev/null +++ b/SabreTools.Library/Reports/BaseReport.cs @@ -0,0 +1,108 @@ +using System; + +using SabreTools.Library.DatFiles; +using SabreTools.Library.Tools; + +#if MONO +using System.IO; +#else +using Alphaleonis.Win32.Filesystem; + +using FileStream = System.IO.FileStream; +using IOException = System.IO.IOException; +using MemoryStream = System.IO.MemoryStream; +using SearchOption = System.IO.SearchOption; +using SeekOrigin = System.IO.SeekOrigin; +using Stream = System.IO.Stream; +using StreamWriter = System.IO.StreamWriter; +#endif + +namespace SabreTools.Library.Reports +{ + /// + /// Base class for a report output format + /// + public abstract class BaseReport + { + protected DatFile _datFile; + protected StreamWriter _writer; + protected bool _baddumpCol; + protected bool _nodumpCol; + + /// + /// Create a new report from the input DatFile and the filename + /// + /// DatFile to write out statistics for + /// Name of the file to write out to + /// True if baddumps should be included in output, false otherwise + /// True if nodumps should be included in output, false otherwise + public BaseReport(DatFile datfile, string filename, bool baddumpCol = false, bool nodumpCol = false) + { + _datFile = datfile; + _writer = new StreamWriter(FileTools.TryCreate(filename)); + _baddumpCol = baddumpCol; + _nodumpCol = nodumpCol; + } + + /// + /// Create a new report from the input DatFile and the stream + /// + /// DatFile to write out statistics for + /// Output stream to write to + /// True if baddumps should be included in output, false otherwise + /// True if nodumps should be included in output, false otherwise + public BaseReport(DatFile datfile, Stream stream, bool baddumpCol = false, bool nodumpCol = false) + { + _datFile = datfile; + + if (!stream.CanWrite) + { + throw new ArgumentException(nameof(stream)); + } + + _writer = new StreamWriter(stream); + _baddumpCol = baddumpCol; + _nodumpCol = nodumpCol; + } + + /// + /// Replace the DatFile that is being output + /// + /// + public void ReplaceDatFile(DatFile datfile) + { + _datFile = datfile; + } + + /// + /// Write the report to the output stream + /// + /// Number of games to use, -1 means use the number of keys + public abstract void Write(long game = -1); + + /// + /// Write out the header to the stream, if any exists + /// + public abstract void WriteStatsHeader(); + + /// + /// Write out the mid-header to the stream, if any exists + /// + public abstract void WriteStatsMidHeader(); + + /// + /// Write out the separator to the stream, if any exists + /// + public abstract void WriteStatsMidSeparator(); + + /// + /// Write out the footer-separator to the stream, if any exists + /// + public abstract void WriteStatsFooterSeparator(); + + /// + /// Write out the footer to the stream, if any exists + /// + public abstract void WriteStatsFooter(); + } +} diff --git a/SabreTools.Library/Reports/Html.cs b/SabreTools.Library/Reports/Html.cs new file mode 100644 index 00000000..affbd1d3 --- /dev/null +++ b/SabreTools.Library/Reports/Html.cs @@ -0,0 +1,163 @@ +using System; +using System.Linq; +using System.Web; + +using SabreTools.Library.DatFiles; +using SabreTools.Library.Tools; + +#if MONO +using System.IO; +#else +using Alphaleonis.Win32.Filesystem; + +using FileStream = System.IO.FileStream; +using IOException = System.IO.IOException; +using MemoryStream = System.IO.MemoryStream; +using SearchOption = System.IO.SearchOption; +using SeekOrigin = System.IO.SeekOrigin; +using Stream = System.IO.Stream; +using StreamWriter = System.IO.StreamWriter; +#endif + +namespace SabreTools.Library.Reports +{ + /// + /// HTML report format + /// + public class Html : BaseReport + { + /// + /// Create a new report from the input DatFile and the filename + /// + /// DatFile to write out statistics for + /// Name of the file to write out to + /// True if baddumps should be included in output, false otherwise + /// True if nodumps should be included in output, false otherwise + public Html(DatFile datfile, string filename, bool baddumpCol = false, bool nodumpCol = false) + : base(datfile, filename, baddumpCol, nodumpCol) + { + } + + /// + /// Create a new report from the input DatFile and the stream + /// + /// DatFile to write out statistics for + /// Output stream to write to + /// True if baddumps should be included in output, false otherwise + /// True if nodumps should be included in output, false otherwise + public Html(DatFile datfile, Stream stream, bool baddumpCol = false, bool nodumpCol = false) + : base(datfile, stream, baddumpCol, nodumpCol) + { + } + + /// + /// Write the report to file + /// + /// Number of games to use, -1 means use the number of keys + public override void Write(long game = -1) + { + string line = "\t\t\t" + HttpUtility.HtmlEncode(_datFile.FileName.Remove(0, 5)) + : ">" + HttpUtility.HtmlEncode(_datFile.FileName)) + "" + + "" + Style.GetBytesReadable(_datFile.TotalSize) + "" + + "" + (game == -1 ? _datFile.Keys.Count() : game) + "" + + "" + _datFile.RomCount + "" + + "" + _datFile.DiskCount + "" + + "" + _datFile.CRCCount + "" + + "" + _datFile.MD5Count + "" + + "" + _datFile.SHA1Count + "" + + "" + _datFile.SHA256Count + "" + + (_baddumpCol ? "" + _datFile.BaddumpCount + "" : "") + + (_nodumpCol ? "" + _datFile.NodumpCount + "" : "") + + "\n"; + _writer.Write(line); + _writer.Flush(); + } + + /// + /// Write out the header to the stream, if any exists + /// + public override void WriteStatsHeader() + { + _writer.Write(@" + +
+ DAT Statistics Report + +
+ +

DAT Statistics Report (" + DateTime.Now.ToShortDateString() + @")

+ +"); + _writer.Flush(); + + // Now write the mid header for those who need it + WriteStatsMidHeader(); + } + + /// + /// Write out the mid-header to the stream, if any exists + /// + public override void WriteStatsMidHeader() + { + _writer.Write(@" " ++ @"" ++ (_baddumpCol ? "" : "") + (_nodumpCol ? "" : "") + "\n"); + _writer.Flush(); + } + + /// + /// Write out the separator to the stream, if any exists + /// + public override void WriteStatsMidSeparator() + { + _writer.Write("\n"); + _writer.Flush(); + } + + /// + /// Write out the footer-separator to the stream, if any exists + /// + public override void WriteStatsFooterSeparator() + { + _writer.Write("\n"); + _writer.Flush(); + } + + /// + /// Write out the footer to the stream, if any exists + /// + public override void WriteStatsFooter() + { + _writer.Write(@"
File NameTotal SizeGamesRomsDisks# with CRC# with MD5# with SHA-1# with SHA-256BaddumpsNodumps
+ + +"); + _writer.Flush(); + } + } +} diff --git a/SabreTools.Library/Reports/SeparatedValue.cs b/SabreTools.Library/Reports/SeparatedValue.cs new file mode 100644 index 00000000..d138ebd3 --- /dev/null +++ b/SabreTools.Library/Reports/SeparatedValue.cs @@ -0,0 +1,124 @@ +using System.Linq; + +using SabreTools.Library.DatFiles; + +#if MONO +using System.IO; +#else +using Alphaleonis.Win32.Filesystem; + +using FileStream = System.IO.FileStream; +using IOException = System.IO.IOException; +using MemoryStream = System.IO.MemoryStream; +using SearchOption = System.IO.SearchOption; +using SeekOrigin = System.IO.SeekOrigin; +using Stream = System.IO.Stream; +using StreamWriter = System.IO.StreamWriter; +#endif + +namespace SabreTools.Library.Reports +{ + /// + /// Separated-Value report format + /// + public class SeparatedValue : BaseReport + { + private char _separator; + + /// + /// Create a new report from the input DatFile and the filename + /// + /// DatFile to write out statistics for + /// Name of the file to write out to + /// Separator character to use in output + /// True if baddumps should be included in output, false otherwise + /// True if nodumps should be included in output, false otherwise + public SeparatedValue(DatFile datfile, string filename, char separator, bool baddumpCol = false, bool nodumpCol = false) + : base(datfile, filename, baddumpCol, nodumpCol) + { + _separator = separator; + } + + /// + /// Create a new report from the input DatFile and the stream + /// + /// DatFile to write out statistics for + /// Output stream to write to + /// Separator character to use in output + /// True if baddumps should be included in output, false otherwise + /// True if nodumps should be included in output, false otherwise + public SeparatedValue(DatFile datfile, Stream stream, char separator, bool baddumpCol = false, bool nodumpCol = false) + : base(datfile, stream, baddumpCol, nodumpCol) + { + _separator = separator; + } + + /// + /// Write the report to file + /// + /// Number of games to use, -1 means use the number of keys + public override void Write(long game = -1) + { + string line = string.Format("\"" + _datFile.FileName + "\"{0}" + + "\"" + _datFile.TotalSize + "\"{0}" + + "\"" + (game == -1 ? _datFile.Keys.Count() : game) + "\"{0}" + + "\"" + _datFile.RomCount + "\"{0}" + + "\"" + _datFile.DiskCount + "\"{0}" + + "\"" + _datFile.CRCCount + "\"{0}" + + "\"" + _datFile.MD5Count + "\"{0}" + + "\"" + _datFile.SHA1Count + "\"{0}" + + "\"" + _datFile.SHA256Count + "\"{0}" + + "\"" + _datFile.SHA384Count + "\"{0}" + + "\"" + _datFile.SHA512Count + "\"" + + (_baddumpCol ? "{0}\"" + _datFile.BaddumpCount + "\"" : "") + + (_nodumpCol ? "{0}\"" + _datFile.BaddumpCount + "\"" : "") + + "\n", _separator); + + _writer.Write(line); + _writer.Flush(); + } + + /// + /// Write out the header to the stream, if any exists + /// + public override void WriteStatsHeader() + { + _writer.Write(string.Format("\"File Name\"{0}\"Total Size\"{0}\"Games\"{0}\"Roms\"{0}\"Disks\"{0}\"# with CRC\"{0}\"# with MD5\"{0}\"# with SHA-1\"{0}\"# with SHA-256\"" + + (_baddumpCol ? "{0}\"BadDumps\"" : "") + (_nodumpCol ? "{0}\"Nodumps\"" : "") + "\n", _separator)); + _writer.Flush(); + } + + /// + /// Write out the mid-header to the stream, if any exists + /// + public override void WriteStatsMidHeader() + { + // This call is a no-op for separated value formats + } + + /// + /// Write out the separator to the stream, if any exists + /// + public override void WriteStatsMidSeparator() + { + // This call is a no-op for separated value formats + } + + /// + /// Write out the footer-separator to the stream, if any exists + /// + public override void WriteStatsFooterSeparator() + { + _writer.Write("\n"); + _writer.Flush(); + } + + /// + /// Write out the footer to the stream, if any exists + /// + public override void WriteStatsFooter() + { + // This call is a no-op for separated value formats + } + } +} diff --git a/SabreTools.Library/Reports/Textfile.cs b/SabreTools.Library/Reports/Textfile.cs new file mode 100644 index 00000000..fd1fbf82 --- /dev/null +++ b/SabreTools.Library/Reports/Textfile.cs @@ -0,0 +1,126 @@ +using System.Linq; + +using SabreTools.Library.DatFiles; +using SabreTools.Library.Tools; + +#if MONO +using System.IO; +#else +using Alphaleonis.Win32.Filesystem; + +using FileStream = System.IO.FileStream; +using IOException = System.IO.IOException; +using MemoryStream = System.IO.MemoryStream; +using SearchOption = System.IO.SearchOption; +using SeekOrigin = System.IO.SeekOrigin; +using Stream = System.IO.Stream; +using StreamWriter = System.IO.StreamWriter; +#endif + +namespace SabreTools.Library.Reports +{ + /// + /// Textfile report format + /// + public class Textfile : BaseReport + { + /// + /// Create a new report from the input DatFile and the filename + /// + /// DatFile to write out statistics for + /// Name of the file to write out to + /// True if baddumps should be included in output, false otherwise + /// True if nodumps should be included in output, false otherwise + public Textfile(DatFile datfile, string filename, bool baddumpCol = false, bool nodumpCol = false) + : base(datfile, filename, baddumpCol, nodumpCol) + { + } + + /// + /// Create a new report from the input DatFile and the stream + /// + /// DatFile to write out statistics for + /// Output stream to write to + /// True if baddumps should be included in output, false otherwise + /// True if nodumps should be included in output, false otherwise + public Textfile(DatFile datfile, Stream stream, bool baddumpCol = false, bool nodumpCol = false) + : base(datfile, stream, baddumpCol, nodumpCol) + { + } + + /// + /// Write the report to file + /// + /// Number of games to use, -1 means use the number of keys + public override void Write(long game = -1) + { + string line = @"'" + _datFile.FileName + @"': +-------------------------------------------------- + Uncompressed size: " + Style.GetBytesReadable(_datFile.TotalSize) + @" + Games found: " + (game == -1 ? _datFile.Keys.Count() : game) + @" + Roms found: " + _datFile.RomCount + @" + Disks found: " + _datFile.DiskCount + @" + Roms with CRC: " + _datFile.CRCCount + @" + Roms with SHA-1: " + _datFile.SHA1Count + @" + Roms with SHA-256: " + _datFile.SHA256Count + @" + Roms with SHA-384: " + _datFile.SHA384Count + @" + Roms with SHA-512: " + _datFile.SHA512Count + "\n"; + + if (_baddumpCol) + { + line += " Roms with BadDump status: " + _datFile.BaddumpCount + "\n"; + } + if (_nodumpCol) + { + line += " Roms with Nodump status: " + _datFile.NodumpCount + "\n"; + } + + // For spacing between DATs + line += "\n\n"; + + _writer.Write(line); + _writer.Flush(); + } + + /// + /// Write out the header to the stream, if any exists + /// + public override void WriteStatsHeader() + { + // This call is a no-op for textfile output + } + + /// + /// Write out the mid-header to the stream, if any exists + /// + public override void WriteStatsMidHeader() + { + // This call is a no-op for textfile output + } + + /// + /// Write out the separator to the stream, if any exists + /// + public override void WriteStatsMidSeparator() + { + // This call is a no-op for textfile output + } + + /// + /// Write out the footer-separator to the stream, if any exists + /// + public override void WriteStatsFooterSeparator() + { + _writer.Write("\n"); + _writer.Flush(); + } + + /// + /// Write out the footer to the stream, if any exists + /// + public override void WriteStatsFooter() + { + // This call is a no-op for textfile output + } + } +} diff --git a/SabreTools.Library/SabreTools.Library.csproj b/SabreTools.Library/SabreTools.Library.csproj index fd4819a9..ca65b2cb 100644 --- a/SabreTools.Library/SabreTools.Library.csproj +++ b/SabreTools.Library/SabreTools.Library.csproj @@ -175,6 +175,10 @@ + + + + diff --git a/SabreTools.Library/Tools/Style.cs b/SabreTools.Library/Tools/Style.cs index 221c7949..71b979f0 100644 --- a/SabreTools.Library/Tools/Style.cs +++ b/SabreTools.Library/Tools/Style.cs @@ -331,9 +331,9 @@ namespace SabreTools.Library.Tools /// StatDatFormat to get the extension for /// Name of the input file to use /// Dictionary of output formats mapped to file names - public static Dictionary CreateOutStatsNames(string outDir, StatDatFormat statDatFormat, string reportName, bool overwrite = true) + public static Dictionary CreateOutStatsNames(string outDir, StatReportFormat statDatFormat, string reportName, bool overwrite = true) { - Dictionary output = new Dictionary(); + Dictionary output = new Dictionary(); // First try to create the output directory if we need to if (!Directory.Exists(outDir)) @@ -348,21 +348,21 @@ namespace SabreTools.Library.Tools } // For each output format, get the appropriate stream writer - if ((statDatFormat & StatDatFormat.None) != 0) + if ((statDatFormat & StatReportFormat.None) != 0) { - output.Add(StatDatFormat.None, CreateOutStatsNamesHelper(outDir, ".txt", reportName, overwrite)); + output.Add(StatReportFormat.None, CreateOutStatsNamesHelper(outDir, ".txt", reportName, overwrite)); } - if ((statDatFormat & StatDatFormat.CSV) != 0) + if ((statDatFormat & StatReportFormat.CSV) != 0) { - output.Add(StatDatFormat.CSV, CreateOutStatsNamesHelper(outDir, ".csv", reportName, overwrite)); + output.Add(StatReportFormat.CSV, CreateOutStatsNamesHelper(outDir, ".csv", reportName, overwrite)); } - if ((statDatFormat & StatDatFormat.HTML) != 0) + if ((statDatFormat & StatReportFormat.HTML) != 0) { - output.Add(StatDatFormat.HTML, CreateOutStatsNamesHelper(outDir, ".html", reportName, overwrite)); + output.Add(StatReportFormat.HTML, CreateOutStatsNamesHelper(outDir, ".html", reportName, overwrite)); } - if ((statDatFormat & StatDatFormat.TSV) != 0) + if ((statDatFormat & StatReportFormat.TSV) != 0) { - output.Add(StatDatFormat.TSV, CreateOutStatsNamesHelper(outDir, ".tsv", reportName, overwrite)); + output.Add(StatReportFormat.TSV, CreateOutStatsNamesHelper(outDir, ".tsv", reportName, overwrite)); } return output; diff --git a/SabreTools/SabreTools.Help.cs b/SabreTools/SabreTools.Help.cs index 3a870ef6..1caf42bc 100644 --- a/SabreTools/SabreTools.Help.cs +++ b/SabreTools/SabreTools.Help.cs @@ -738,10 +738,15 @@ namespace SabreTools FeatureType.Flag, null)); stats.AddFeature("tsv", new Feature( - new List() { "-tsv", "--csv" }, + new List() { "-tsv", "--tsv" }, "Output in Tab-Separated Value format", FeatureType.Flag, null)); + stats.AddFeature("text", new Feature( + new List() { "-txt", "--text" }, + "Output in generic text format", + FeatureType.Flag, + null)); // Create the Type Split feature Feature typeSplit = new Feature( diff --git a/SabreTools/SabreTools.Inits.cs b/SabreTools/SabreTools.Inits.cs index 7468bee6..7e0cf358 100644 --- a/SabreTools/SabreTools.Inits.cs +++ b/SabreTools/SabreTools.Inits.cs @@ -404,7 +404,7 @@ namespace SabreTools /// True if nodumps should be included in output, false otherwise /// Set the statistics output format to use private static void InitStats(List inputs, string filename, string outDir, bool single, bool baddumpCol, bool nodumpCol, - StatDatFormat statDatFormat) + StatReportFormat statDatFormat) { DatFile.OutputStats(inputs, filename, outDir, single, baddumpCol, nodumpCol, statDatFormat); } diff --git a/SabreTools/SabreTools.cs b/SabreTools/SabreTools.cs index da737365..c63dbab0 100644 --- a/SabreTools/SabreTools.cs +++ b/SabreTools/SabreTools.cs @@ -128,7 +128,7 @@ namespace SabreTools OutputFormat outputFormat = OutputFormat.Folder; SkipFileType skipFileType = SkipFileType.None; SplitType splitType = SplitType.None; - StatDatFormat statDatFormat = 0x0; + StatReportFormat statDatFormat = 0x0; UpdateMode updateMode = UpdateMode.None; // User inputs @@ -293,6 +293,10 @@ namespace SabreTools case "--against": updateMode |= UpdateMode.DiffAgainst; break; + case "-as": + case "--all-stats": + statDatFormat = StatReportFormat.All; + break; case "-b": case "--bare": removeDateFromAutomaticName = true; @@ -323,7 +327,7 @@ namespace SabreTools break; case "-csv": case "--csv": - statDatFormat |= StatDatFormat.CSV; + statDatFormat |= StatReportFormat.CSV; break; case "-dan": case "--desc-name": @@ -391,7 +395,7 @@ namespace SabreTools break; case "-html": case "--html": - statDatFormat |= StatDatFormat.HTML; + statDatFormat |= StatReportFormat.HTML; break; case "-ic": case "--ignore-chd": @@ -631,12 +635,16 @@ namespace SabreTools break; case "-tsv": case "--tsv": - statDatFormat |= StatDatFormat.TSV; + statDatFormat |= StatReportFormat.TSV; break; case "-txz": case "--txz": outputFormat = OutputFormat.TorrentXZ; break; + case "-txt": + case "--text": + statDatFormat |= StatReportFormat.None; + break; case "-tzip": case "--tzip": outputFormat = OutputFormat.TorrentZip;