using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using SabreTools.Core; using SabreTools.DatFiles; using SabreTools.DatFiles.Reports; using SabreTools.IO; using SabreTools.Logging; namespace SabreTools.DatTools { public class Statistics { #region Logging /// /// Logging object /// private static readonly Logger logger = new Logger(); #endregion /// /// Output the stats for a list of input dats as files in a human-readable format /// /// List of input files and folders /// Name of the output file /// True if single DAT stats are output, false otherwise /// True if baddumps should be included in output, false otherwise /// 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, StatReportFormat statDatFormat) { // If there's no output format, set the default if (statDatFormat == StatReportFormat.None) statDatFormat = StatReportFormat.Textfile; // Get the proper output file name if (string.IsNullOrWhiteSpace(reportName)) reportName = "report"; // Get the proper output directory name outDir = outDir.Ensure(); // Get the dictionary of desired output report names Dictionary outputs = CreateOutStatsNames(outDir, statDatFormat, reportName); // Make sure we have all files and then order them List files = PathTool.GetFilesOnly(inputs); files = files .OrderBy(i => Path.GetDirectoryName(i.CurrentPath)) .ThenBy(i => Path.GetFileName(i.CurrentPath)) .ToList(); // Get all of the writers that we need List reports = outputs.Select(kvp => BaseReport.Create(kvp.Key, kvp.Value, baddumpCol, nodumpCol)).ToList(); // Write the header, if any reports.ForEach(report => report.WriteHeader()); // Init all total variables ItemDictionary totalStats = new ItemDictionary(); // Init directory-level variables string lastdir = null; string basepath = null; ItemDictionary dirStats = new ItemDictionary(); // Now process each of the input files foreach (ParentablePath file in files) { // Get the directory for the current file string thisdir = Path.GetDirectoryName(file.CurrentPath); basepath = Path.GetDirectoryName(Path.GetDirectoryName(file.CurrentPath)); // If we don't have the first file and the directory has changed, show the previous directory stats and reset if (lastdir != null && thisdir != lastdir) { // Output separator if needed reports.ForEach(report => report.WriteMidSeparator()); DatFile lastdirdat = DatFile.Create(); reports.ForEach(report => report.ReplaceStatistics($"DIR: {WebUtility.HtmlEncode(lastdir)}", dirStats.GameCount, dirStats)); reports.ForEach(report => report.Write()); // Write the mid-footer, if any reports.ForEach(report => report.WriteFooterSeparator()); // Write the header, if any reports.ForEach(report => report.WriteMidHeader()); // Reset the directory stats dirStats.ResetStatistics(); } logger.Verbose($"Beginning stat collection for '{file.CurrentPath}'"); List games = new List(); DatFile datdata = Parser.CreateAndParse(file.CurrentPath); datdata.Items.BucketBy(Field.Machine_Name, DedupeType.None, norename: true); // Output single DAT stats (if asked) logger.User($"Adding stats for file '{file.CurrentPath}'\n"); if (single) { reports.ForEach(report => report.ReplaceStatistics(datdata.Header.FileName, datdata.Items.Keys.Count, datdata.Items)); reports.ForEach(report => report.Write()); } // Add single DAT stats to dir dirStats.AddStatistics(datdata.Items); dirStats.GameCount += datdata.Items.Keys.Count(); // Add single DAT stats to totals totalStats.AddStatistics(datdata.Items); totalStats.GameCount += datdata.Items.Keys.Count(); // Make sure to assign the new directory lastdir = thisdir; } // Output the directory stats one last time reports.ForEach(report => report.WriteMidSeparator()); if (single) { reports.ForEach(report => report.ReplaceStatistics($"DIR: {WebUtility.HtmlEncode(lastdir)}", dirStats.GameCount, dirStats)); reports.ForEach(report => report.Write()); } // Write the mid-footer, if any reports.ForEach(report => report.WriteFooterSeparator()); // Write the header, if any reports.ForEach(report => report.WriteMidHeader()); // Reset the directory stats dirStats.ResetStatistics(); // Output total DAT stats reports.ForEach(report => report.ReplaceStatistics("DIR: All DATs", totalStats.GameCount, totalStats)); reports.ForEach(report => report.Write()); // Output footer if needed reports.ForEach(report => report.WriteFooter()); logger.User($"{Environment.NewLine}Please check the log folder if the stats scrolled offscreen"); } /// /// Get the proper extension for the stat output format /// /// Output path to use /// StatDatFormat to get the extension for /// Name of the input file to use /// Dictionary of output formats mapped to file names private static Dictionary CreateOutStatsNames(string outDir, StatReportFormat statDatFormat, string reportName, bool overwrite = true) { Dictionary output = new Dictionary(); // First try to create the output directory if we need to if (!Directory.Exists(outDir)) Directory.CreateDirectory(outDir); // Double check the outDir for the end delim if (!outDir.EndsWith(Path.DirectorySeparatorChar.ToString())) outDir += Path.DirectorySeparatorChar; // For each output format, get the appropriate stream writer output.Add(StatReportFormat.None, CreateOutStatsNamesHelper(outDir, ".null", reportName, overwrite)); if (statDatFormat.HasFlag(StatReportFormat.Textfile)) output.Add(StatReportFormat.Textfile, CreateOutStatsNamesHelper(outDir, ".txt", reportName, overwrite)); if (statDatFormat.HasFlag(StatReportFormat.CSV)) output.Add(StatReportFormat.CSV, CreateOutStatsNamesHelper(outDir, ".csv", reportName, overwrite)); if (statDatFormat.HasFlag(StatReportFormat.HTML)) output.Add(StatReportFormat.HTML, CreateOutStatsNamesHelper(outDir, ".html", reportName, overwrite)); if (statDatFormat.HasFlag(StatReportFormat.SSV)) output.Add(StatReportFormat.SSV, CreateOutStatsNamesHelper(outDir, ".ssv", reportName, overwrite)); if (statDatFormat.HasFlag(StatReportFormat.TSV)) output.Add(StatReportFormat.TSV, CreateOutStatsNamesHelper(outDir, ".tsv", reportName, overwrite)); return output; } /// /// Help generating the outstats name /// /// Output directory /// Extension to use for the file /// Name of the input file to use /// True if we ignore existing files, false otherwise /// String containing the new filename private static string CreateOutStatsNamesHelper(string outDir, string extension, string reportName, bool overwrite) { string outfile = outDir + reportName + extension; outfile = outfile.Replace($"{Path.DirectorySeparatorChar}{Path.DirectorySeparatorChar}", Path.DirectorySeparatorChar.ToString()); if (!overwrite) { int i = 1; while (File.Exists(outfile)) { outfile = $"{outDir}{reportName}_{i}{extension}"; outfile = outfile.Replace($"{Path.DirectorySeparatorChar}{Path.DirectorySeparatorChar}", Path.DirectorySeparatorChar.ToString()); i++; } } return outfile; } } }