Files
LuaCsForBarotraumaEP/Libraries/MonoGame.Framework/Src/MonoGame.Framework.Content.Pipeline/ContentStatsCollection.cs
2019-06-25 16:00:44 +03:00

230 lines
8.8 KiB
C#

// MonoGame - Copyright (C) The MonoGame Team
// This file is subject to the terms and conditions defined in
// file 'LICENSE.txt', which is part of this source code package.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace Microsoft.Xna.Framework.Content.Pipeline
{
/// <summary>
/// A collection of content building statistics for use in diagnosing content issues.
/// </summary>
public class ContentStatsCollection
{
private static readonly string _header = "Source File,Dest File,Processor Type,Content Type,Source File Size,Dest File Size,Build Seconds";
private static readonly Regex _split = new Regex(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)");
private readonly object _locker = new object();
private readonly Dictionary<string, ContentStats> _statsBySource = new Dictionary<string, ContentStats>(1024);
public static readonly string Extension = ".mgstats";
/// <summary>
/// Optionally used for copying stats that were stored in another collection.
/// </summary>
public ContentStatsCollection PreviousStats { get; set; }
/// <summary>
/// The internal content statistics dictionary.
/// </summary>
public IReadOnlyDictionary<string, ContentStats> Stats
{
get { return _statsBySource; }
}
/// <summary>
/// Get the content statistics for a source file and returns true if found.
/// </summary>
public bool TryGetStats(string sourceFile, out ContentStats stats)
{
lock (_locker)
{
if (!_statsBySource.TryGetValue(sourceFile, out stats))
return false;
return true;
}
}
/// <summary>
/// Clears all the content statistics.
/// </summary>
public void Reset()
{
lock (_locker)
_statsBySource.Clear();
}
/// <summary>
/// Store content building stats for a source file.
/// </summary>
/// <param name="sourceFile">The absolute path to the source asset file.</param>
/// <param name="destFile">The absolute path to the destination content file.</param>
/// <param name="processorType">The type name of the content processor.</param>
/// <param name="contentType">The content type object.</param>
/// <param name="buildSeconds">The build time in seconds.</param>
public void RecordStats(string sourceFile, string destFile, string processorType, Type contentType, float buildSeconds)
{
var sourceSize = new FileInfo(sourceFile).Length;
var destSize = new FileInfo(destFile).Length;
lock (_locker)
{
ContentStats stats;
_statsBySource.TryGetValue(sourceFile, out stats);
stats.SourceFile = sourceFile;
stats.DestFile = destFile;
stats.SourceFileSize = sourceSize;
stats.DestFileSize = destSize;
stats.ContentType = GetFriendlyTypeName(contentType);
stats.ProcessorType = processorType;
stats.BuildSeconds = buildSeconds;
_statsBySource[stats.SourceFile] = stats;
}
}
/// <summary>
/// Copy content building stats to the current collection from the PreviousStats.
/// </summary>
/// <param name="sourceFile">The absolute path to the source asset file.</param>
public void CopyPreviousStats(string sourceFile)
{
if (PreviousStats == null)
return;
lock (_locker)
{
if (_statsBySource.ContainsKey(sourceFile))
return;
ContentStats stats;
if (PreviousStats.TryGetStats(sourceFile, out stats))
_statsBySource[stats.SourceFile] = stats;
}
}
private static string GetFriendlyTypeName(Type type)
{
if (type == null)
return "";
if (type == typeof(int))
return "int";
else if (type == typeof(short))
return "short";
else if (type == typeof(byte))
return "byte";
else if (type == typeof(bool))
return "bool";
else if (type == typeof(long))
return "long";
else if (type == typeof(float))
return "float";
else if (type == typeof(double))
return "double";
else if (type == typeof(decimal))
return "decimal";
else if (type == typeof(string))
return "string";
else if (type.IsArray)
return GetFriendlyTypeName(type.GetElementType()) + "[" + new string(',', type.GetArrayRank() - 1) + "]";
else if (type.IsGenericType)
return type.Name.Split('`')[0] + "<" + string.Join(", ", type.GetGenericArguments().Select(x => GetFriendlyTypeName(x)).ToArray()) + ">";
else
return type.Name;
}
/// <summary>
/// Load the content statistics from a folder.
/// </summary>
/// <param name="outputPath">The folder where the .mgstats file can be found.</param>
/// <returns>Returns the content statistics or an empty collection.</returns>
public static ContentStatsCollection Read(string outputPath)
{
var collection = new ContentStatsCollection();
var filePath = Path.Combine(outputPath, Extension);
try
{
var lines = File.ReadAllLines(filePath);
// The first line is the CSV header... if it doesn't match then
// assume the data is invalid or changed formats.
if (lines[0] != _header)
return collection;
for (var i = 1; i < lines.Length; i++)
{
var columns = _split.Split(lines[i]);
if (columns.Length != 7)
continue;
ContentStats stats;
stats.SourceFile = columns[0].Trim('"');
stats.DestFile = columns[1].Trim('"');
stats.ProcessorType = columns[2].Trim('"');
stats.ContentType = columns[3].Trim('"');
stats.SourceFileSize = long.Parse(columns[4]);
stats.DestFileSize = long.Parse(columns[5]);
stats.BuildSeconds = float.Parse(columns[6]);
if (!collection._statsBySource.ContainsKey(stats.SourceFile))
collection._statsBySource.Add(stats.SourceFile, stats);
}
}
catch (Exception ex)
{
// Assume the file didn't exist or was incorrectly
// formatted... either way we start from fresh data.
collection.Reset();
}
return collection;
}
/// <summary>
/// Write the content statistics to a folder with the .mgstats file name.
/// </summary>
/// <param name="outputPath">The folder to write the .mgstats file.</param>
public void Write(string outputPath)
{
// ensure the output folder exists
Directory.CreateDirectory(outputPath);
var filePath = Path.Combine(outputPath, Extension);
using (var textWriter = new StreamWriter(filePath, false, new UTF8Encoding(false)))
{
// Sort the items alphabetically to ensure a consistent output
// and better mergability of the resulting file.
var contentStats = _statsBySource.Values.OrderBy(c => c.SourceFile, StringComparer.InvariantCulture).ToList();
textWriter.WriteLine(_header);
foreach (var stats in contentStats)
textWriter.WriteLine("\"{0}\",\"{1}\",\"{2}\",\"{3}\",{4},{5},{6}", stats.SourceFile, stats.DestFile, stats.ProcessorType, stats.ContentType, stats.SourceFileSize, stats.DestFileSize, stats.BuildSeconds);
}
}
/// <summary>
/// Merge in statistics from PreviousStats that do not exist in this collection.
/// </summary>
public void MergePreviousStats()
{
if (PreviousStats == null)
return;
foreach (var stats in PreviousStats._statsBySource.Values)
{
if (!_statsBySource.ContainsKey(stats.SourceFile))
_statsBySource.Add(stats.SourceFile, stats);
}
}
}
}