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

303 lines
11 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.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Xml.Serialization;
using Microsoft.Xna.Framework.Content.Pipeline;
namespace MonoGame.Framework.Content.Pipeline.Builder
{
public class PipelineBuildEvent
{
private static readonly OpaqueDataDictionary EmptyParameters = new OpaqueDataDictionary();
public static readonly string Extension = ".mgcontent";
public PipelineBuildEvent()
{
SourceFile = string.Empty;
DestFile = string.Empty;
Importer = string.Empty;
Processor = string.Empty;
Parameters = new OpaqueDataDictionary();
ParametersXml = new List<Pair>();
Dependencies = new List<string>();
BuildAsset = new List<string>();
BuildOutput = new List<string>();
}
/// <summary>
/// Absolute path to the source file.
/// </summary>
public string SourceFile { get; set; }
/// <summary>
/// The date/time stamp of the source file.
/// </summary>
public DateTime SourceTime { get; set; }
/// <summary>
/// Absolute path to the output file.
/// </summary>
public string DestFile { get; set; }
/// <summary>
/// The date/time stamp of the destination file.
/// </summary>
public DateTime DestTime { get; set; }
public string Importer { get; set; }
/// <summary>
/// The date/time stamp of the DLL containing the importer.
/// </summary>
public DateTime ImporterTime { get; set; }
public string Processor { get; set; }
/// <summary>
/// The date/time stamp of the DLL containing the processor.
/// </summary>
public DateTime ProcessorTime { get; set; }
[XmlIgnore]
public OpaqueDataDictionary Parameters { get; set; }
public class Pair
{
public string Key { get; set; }
public string Value { get; set; }
}
[XmlElement("Parameters")]
public List<Pair> ParametersXml { get; set; }
/// <summary>
/// Gets or sets the dependencies.
/// </summary>
/// <value>The dependencies.</value>
/// <remarks>
/// Dependencies are extra files that are required in addition to the <see cref="SourceFile"/>.
/// Dependencies are added using <see cref="ContentProcessorContext.AddDependency"/>. Changes
/// to the dependent file causes a rebuilt of the content.
/// </remarks>
public List<string> Dependencies { get; set; }
/// <summary>
/// Gets or sets the additional (nested) assets.
/// </summary>
/// <value>The additional (nested) assets.</value>
/// <remarks>
/// <para>
/// Additional assets are built by using an <see cref="ExternalReference{T}"/> and calling
/// <see cref="ContentProcessorContext.BuildAndLoadAsset{TInput,TOutput}(ExternalReference{TInput},string)"/>
/// or <see cref="ContentProcessorContext.BuildAsset{TInput,TOutput}(ExternalReference{TInput},string)"/>.
/// </para>
/// <para>
/// Examples: The mesh processor may build textures and effects in addition to the mesh.
/// </para>
/// </remarks>
public List<string> BuildAsset { get; set; }
/// <summary>
/// Gets or sets the related output files.
/// </summary>
/// <value>The related output files.</value>
/// <remarks>
/// Related output files are non-XNB files that are included in addition to the XNB files.
/// Related output files need to be copied to the output folder by a content processor and
/// registered by calling <see cref="ContentProcessorContext.AddOutputFile"/>.
/// </remarks>
public List<string> BuildOutput { get; set; }
public static PipelineBuildEvent Load(string filePath)
{
var fullFilePath = Path.GetFullPath(filePath);
var deserializer = new XmlSerializer(typeof (PipelineBuildEvent));
PipelineBuildEvent pipelineEvent;
try
{
using (var textReader = new StreamReader(fullFilePath))
pipelineEvent = (PipelineBuildEvent) deserializer.Deserialize(textReader);
}
catch (Exception)
{
return null;
}
// Repopulate the parameters from the serialized state.
foreach (var pair in pipelineEvent.ParametersXml)
pipelineEvent.Parameters.Add(pair.Key, pair.Value);
pipelineEvent.ParametersXml.Clear();
return pipelineEvent;
}
public void Save(string filePath)
{
var fullFilePath = Path.GetFullPath(filePath);
// Make sure the directory exists.
Directory.CreateDirectory(Path.GetDirectoryName(fullFilePath) + Path.DirectorySeparatorChar);
// Convert the parameters into something we can serialize.
ParametersXml.Clear();
foreach (var pair in Parameters)
ParametersXml.Add(new Pair { Key = pair.Key, Value = ConvertToString(pair.Value) });
// Serialize our state.
var serializer = new XmlSerializer(typeof (PipelineBuildEvent));
using (var textWriter = new StreamWriter(fullFilePath, false, new UTF8Encoding(false)))
serializer.Serialize(textWriter, this);
}
public bool NeedsRebuild(PipelineManager manager, PipelineBuildEvent cachedEvent)
{
// If we have no previously cached build event then we cannot
// be sure that the state hasn't changed... force a rebuild.
if (cachedEvent == null)
return true;
// Verify that the last write time of the source file matches
// what we recorded when it was built. If it is different
// that means someone modified it and we need to rebuild.
var sourceWriteTime = File.GetLastWriteTime(SourceFile);
if (cachedEvent.SourceTime != sourceWriteTime)
return true;
// Do the same test for the dest file.
var destWriteTime = File.GetLastWriteTime(DestFile);
if (cachedEvent.DestTime != destWriteTime)
return true;
// If the source file is newer than the dest file
// then it must have been updated and needs a rebuild.
if (sourceWriteTime >= destWriteTime)
return true;
// Are any of the dependancy files newer than the dest file?
foreach (var depFile in cachedEvent.Dependencies)
{
if (File.GetLastWriteTime(depFile) >= destWriteTime)
return true;
}
// This shouldn't happen... but if the source or dest files changed
// then force a rebuild.
if (cachedEvent.SourceFile != SourceFile ||
cachedEvent.DestFile != DestFile)
return true;
// Did the importer assembly change?
if (manager.GetImporterAssemblyTimestamp(cachedEvent.Importer) > cachedEvent.ImporterTime)
return true;
// Did the importer change?
if (cachedEvent.Importer != Importer)
return true;
// Did the processor assembly change?
if (manager.GetProcessorAssemblyTimestamp(cachedEvent.Processor) > cachedEvent.ProcessorTime)
return true;
// Did the processor change?
if (cachedEvent.Processor != Processor)
return true;
// Did the parameters change?
var defaultValues = manager.GetProcessorDefaultValues(Processor);
if (!AreParametersEqual(cachedEvent.Parameters, Parameters, defaultValues))
return true;
return false;
}
internal static bool AreParametersEqual(OpaqueDataDictionary parameters0, OpaqueDataDictionary parameters1, OpaqueDataDictionary defaultValues)
{
Debug.Assert(defaultValues != null, "defaultValues must not be empty.");
Debug.Assert(EmptyParameters != null && EmptyParameters.Count == 0);
// Same reference or both null?
if (parameters0 == parameters1)
return true;
if (parameters0 == null)
parameters0 = EmptyParameters;
if (parameters1 == null)
parameters1 = EmptyParameters;
// Are both dictionaries empty?
if (parameters0.Count == 0 && parameters1.Count == 0)
return true;
// Compare the values with the second dictionary or
// the default values.
if (parameters0.Count < parameters1.Count)
{
var dummy = parameters0;
parameters0 = parameters1;
parameters1 = dummy;
}
// Compare parameters0 with parameters1 or defaultValues.
foreach (var pair in parameters0)
{
object value0 = pair.Value;
object value1;
// Search for matching parameter.
if (!parameters1.TryGetValue(pair.Key, out value1) && !defaultValues.TryGetValue(pair.Key, out value1))
return false;
if (!AreEqual(value0, value1))
return false;
}
// Compare parameters which are only in parameters1 with defaultValues.
foreach (var pair in parameters1)
{
if (parameters0.ContainsKey(pair.Key))
continue;
object defaultValue;
if (!defaultValues.TryGetValue(pair.Key, out defaultValue))
return false;
if (!AreEqual(pair.Value, defaultValue))
return false;
}
return true;
}
private static bool AreEqual(object value0, object value1)
{
// Are values equal or both null?
if (Equals(value0, value1))
return true;
// Is one value null?
if (value0 == null || value1 == null)
return false;
// Values are of different type: Compare string representation.
if (ConvertToString(value0) != ConvertToString(value1))
return false;
return true;
}
private static string ConvertToString(object value)
{
if (value == null)
return null;
var typeConverter = TypeDescriptor.GetConverter(value.GetType());
return typeConverter.ConvertToInvariantString(value);
}
};
}