(ded4a3e0a) v0.9.0.7

This commit is contained in:
Joonas Rikkonen
2019-06-25 16:00:44 +03:00
parent e5ae622c77
commit 4a51db77b5
1777 changed files with 421528 additions and 917 deletions
@@ -0,0 +1,84 @@
// 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.Collections.Generic;
using System.Linq;
namespace MonoGame.Tools.Pipeline
{
/// <summary>
/// Represents a stack of undo/redo-able actions.
/// </summary>
public class ActionStack
{
private readonly PipelineController _controller;
private readonly List<IProjectAction> _undoStack;
private readonly List<IProjectAction> _redoStack;
public bool CanUndo { get; private set; }
public bool CanRedo { get; private set; }
public ActionStack(PipelineController controller)
{
_controller = controller;
_undoStack = new List<IProjectAction>();
_redoStack = new List<IProjectAction>();
}
public void Add(IProjectAction action)
{
_undoStack.Add(action);
if (_redoStack.Count > 0)
_redoStack.Clear();
Update();
}
public void Undo()
{
if (!_undoStack.Any())
return;
var action = _undoStack.Last();
if (action.Undo())
{
_undoStack.Remove(action);
_redoStack.Add(action);
}
Update();
}
public void Redo()
{
if (!_redoStack.Any())
return;
var action = _redoStack.Last();
if (action.Do())
{
_redoStack.Remove(action);
_undoStack.Add(action);
}
Update();
}
public void Clear()
{
_undoStack.Clear();
_redoStack.Clear();
Update();
}
private void Update()
{
CanUndo = _undoStack.Any();
CanRedo = _redoStack.Any();
_controller.UpdateMenu();
}
}
}
@@ -0,0 +1,107 @@
// 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.Globalization;
using System.IO;
using System.Linq;
namespace MonoGame.Tools.Pipeline
{
/// <summary>
/// Custom converter for the Processor property of a ContentItem.
/// </summary>
public class ImporterConverter : TypeConverter
{
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
if (context.Instance is Array)
{
var array = context.Instance as Array;
foreach (var obj in array)
{
var item = obj as ContentItem;
if (item.BuildAction == BuildAction.Copy)
return false;
}
}
else
{
var contentItem = (context.Instance as ContentItem);
if (contentItem.BuildAction == BuildAction.Copy)
return false;
}
return true;
}
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
{
return true;
}
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
return PipelineTypes.ImportersStandardValuesCollection;
}
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (sourceType == typeof (string))
return true;
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string)
{
var str = value as string;
foreach (var i in PipelineTypes.Importers)
{
if (i.DisplayName.Equals(str))
{
return i;
}
}
if (string.IsNullOrEmpty(str))
return PipelineTypes.NullImporter;
else
return PipelineTypes.MissingImporter;
}
return base.ConvertFrom(context, culture, value);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
var importer = (ImporterTypeDescription)value;// contentItem.Importer;
//System.Diagnostics.Debug.Assert(importer == value);
if (destinationType == typeof (string))
{
if (importer == PipelineTypes.MissingImporter)
{
var contentItem = (ContentItem)context.Instance;
return string.Format("[missing] {0}", contentItem.ImporterName ?? "[null]");
}
return ((ImporterTypeDescription)value).DisplayName;
}
return base.ConvertTo(context, culture, value, destinationType);
}
public override bool GetPropertiesSupported(ITypeDescriptorContext context)
{
return false;
}
}
}
@@ -0,0 +1,204 @@
// 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.Globalization;
using System.Linq;
using Microsoft.Xna.Framework.Content.Pipeline;
namespace MonoGame.Tools.Pipeline
{
/// <summary>
/// Custom converter for the Processor property of a ContentItem.
/// </summary>
public class ProcessorConverter : TypeConverter
{
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
if (context.Instance is Array)
{
var array = context.Instance as Array;
foreach (var obj in array)
{
var item = obj as ContentItem;
if (item.BuildAction == BuildAction.Copy)
return false;
}
}
else
{
var contentItem = (context.Instance as ContentItem);
if (contentItem.BuildAction == BuildAction.Copy)
return false;
}
return true;
}
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
{
return true;
}
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
return PipelineTypes.ProcessorsStandardValuesCollection;
}
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (sourceType == typeof (string))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string)
{
foreach (var i in PipelineTypes.Processors)
{
if (i.DisplayName.Equals(value))
{
return i;
}
}
}
return base.ConvertFrom(context, culture, value);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof (string))
{
var processor = (ProcessorTypeDescription)value;
if (processor == PipelineTypes.MissingProcessor)
{
var contentItem = context.Instance as ContentItem;
return string.Format("[missing] {0}", contentItem.ProcessorName);
}
return ((ProcessorTypeDescription)value).DisplayName;
}
return base.ConvertTo(context, culture, value, destinationType);
}
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
var props = new PropertyDescriptorCollection(null);
var processor = value as ProcessorTypeDescription;
if (context.Instance is Array)
{
var array = context.Instance as object[];
foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(value, attributes, true))
props.Add(new MultiTargetPropertyDescriptor(prop.Name, prop.PropertyType, prop.ComponentType, prop, array));
var paramArray = array.Select(e => ((ContentItem)e).ProcessorParams).ToArray();
foreach (var p in processor.Properties)
{
var prop = new OpaqueDataDictionaryElementPropertyDescriptor(p.Name,
p.Type,
null);
var prop2 = new MultiTargetPropertyDescriptor(prop.Name,
prop.PropertyType,
prop.ComponentType,
prop,
paramArray);
props.Add(prop2);
}
}
else
{
var contentItem = context.Instance as ContentItem;
if (value == PipelineTypes.MissingProcessor)
{
props.Add(new ReadonlyPropertyDescriptor("Name", typeof (string), typeof (ProcessorTypeDescription), contentItem.ProcessorName));
foreach (var p in contentItem.ProcessorParams)
{
var desc = new OpaqueDataDictionaryElementPropertyDescriptor(p.Key,
p.Value.GetType(),
contentItem.ProcessorParams);
props.Add(desc);
}
}
else
{
// Emit regular properties.
foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(value, attributes, true))
props.Add(prop);
// Emit processor parameters.
foreach (var p in processor.Properties)
{
var desc = new OpaqueDataDictionaryElementPropertyDescriptor(p.Name,
p.Type,
contentItem.ProcessorParams);
props.Add(desc);
}
}
}
return props;
}
public override bool GetPropertiesSupported(ITypeDescriptorContext context)
{
if (!GetStandardValuesSupported(context))
return false;
if (context.Instance is Array)
{
var array = (context.Instance as Array);
var first = array.GetValue(0) as ContentItem;
if (!first.Processor.Properties.Any())
return false;
if (first.Processor == PipelineTypes.MissingProcessor)
return false;
for (var i = 1; i < array.Length; i++)
{
var item = array.GetValue(i) as ContentItem;
if (item.Processor != first.Processor)
return false;
if (!item.Processor.Properties.Any())
return false;
}
}
else
{
var item = context.Instance as ContentItem;
if (item.BuildAction == BuildAction.Copy)
return false;
if (!item.Processor.Properties.Any())
return false;
}
return true;
}
}
}
@@ -0,0 +1,199 @@
// 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.ComponentModel;
using System.Globalization;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Content.Pipeline.Builder.Convertors;
namespace MonoGame.Tools.Pipeline
{
public enum BuildAction
{
Build,
Copy,
}
public class ContentItem : IProjectItem
{
public IContentItemObserver Observer;
public string ImporterName;
public string ProcessorName;
public OpaqueDataDictionary ProcessorParams;
private ImporterTypeDescription _importer;
private ProcessorTypeDescription _processor;
private BuildAction _buildAction;
#region IProjectItem
[Browsable(false)]
public string OriginalPath { get; set; }
[Browsable(false)]
public string DestinationPath { get; set; }
[Category("Common")]
[Description("The file name of this item.")]
public string Name
{
get
{
return System.IO.Path.GetFileName(OriginalPath);
}
}
[Category("Common")]
[Description("The folder where this item is located.")]
public string Location
{
get
{
return System.IO.Path.GetDirectoryName(OriginalPath);
}
}
#endregion
[Category("Settings")]
[DisplayName("Build Action")]
[Description("The way to process this content item.")]
public BuildAction BuildAction
{
get { return _buildAction; }
set
{
if (_buildAction == value)
return;
_buildAction = value;
if (Observer != null)
Observer.OnItemModified(this);
}
}
[Category("Settings")]
[Description("The importer used to load the content file.")]
[TypeConverter(typeof(ImporterConverter))]
public ImporterTypeDescription Importer
{
get { return _importer; }
set
{
if (_importer == value)
return;
_importer = value;
ImporterName = _importer.TypeName;
// Validate that our processor can accept input content of the type output by the new importer.
if ((_processor == null || _processor.InputType != _importer.OutputType) && _processor != PipelineTypes.MissingProcessor)
{
// If it cannot, set the default processor.
Processor = PipelineTypes.FindProcessor(_importer.DefaultProcessor, _importer);
}
if (Observer != null)
Observer.OnItemModified(this);
}
}
[Category("Settings")]
[Description("The processor used to transform the content for runtime use.")]
[TypeConverter(typeof(ProcessorConverter))]
public ProcessorTypeDescription Processor
{
get { return _processor; }
set
{
if (_processor == value)
return;
_processor = value;
ProcessorName = _processor.TypeName;
// When the processor changes reset our parameters
// to the default for the processor type.
ProcessorParams.Clear();
foreach (var p in _processor.Properties)
{
ProcessorParams.Add(p.Name, p.DefaultValue);
}
if (Observer != null)
Observer.OnItemModified(this);
// Note:
// There is no need to validate that the new processor can accept input
// of the type output by our importer, because that should be handled by
// only showing valid processors in the drop-down (eg, within ProcessConverter).
}
}
public void ResolveTypes()
{
if (BuildAction == BuildAction.Copy)
{
// Copy items do not have importers or processors.
_importer = PipelineTypes.NullImporter;
_processor = PipelineTypes.NullProcessor;
}
else
{
_importer = PipelineTypes.FindImporter(ImporterName, System.IO.Path.GetExtension(OriginalPath));
if (_importer != null && (string.IsNullOrEmpty(ImporterName) || ImporterName != _importer.TypeName))
ImporterName = _importer.TypeName;
if (_importer == null)
_importer = PipelineTypes.MissingImporter;
_processor = PipelineTypes.FindProcessor(ProcessorName, _importer);
if (_processor != null && (string.IsNullOrEmpty(ProcessorName) || ProcessorName != _processor.TypeName))
ProcessorName = _processor.TypeName;
if (_processor == null)
_processor = PipelineTypes.MissingProcessor;
// ProcessorParams get deserialized as strings
// this code converts them to object(s) of their actual type
// so that the correct editor appears within the property grid.
foreach (var p in _processor.Properties)
{
if (!ProcessorParams.ContainsKey(p.Name))
{
ProcessorParams[p.Name] = p.DefaultValue;
}
else
{
var src = ProcessorParams[p.Name];
if (src != null)
{
var srcType = src.GetType();
var converter = PipelineTypes.FindConverter(p.Type);
// Should we throw an exception here?
// This property will actually not be editable in the property grid
// since we do not have a type converter for it.
if (converter.CanConvertFrom(srcType))
{
var dst = converter.ConvertFrom(null, CultureInfo.InvariantCulture, src);
ProcessorParams[p.Name] = dst;
}
}
}
}
}
}
public override string ToString()
{
return System.IO.Path.GetFileName(OriginalPath);
}
}
}
@@ -0,0 +1,20 @@
// 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.
namespace MonoGame.Tools.Pipeline
{
public class ContentItemTemplate
{
public string Label;
public string Icon;
public string ImporterName;
public string ProcessorName;
public string TemplateFile;
public override string ToString()
{
return Label;
}
}
}
@@ -0,0 +1,154 @@
// 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.ComponentModel;
using Microsoft.Xna.Framework.Content.Pipeline;
namespace MonoGame.Tools.Pipeline
{
/// <summary>
/// PropertyDescriptor for delegating get/set calls to more than one component (object).
/// </summary>
public class MultiTargetPropertyDescriptor : PropertyDescriptor
{
private readonly Type _propertyType;
private readonly Type _componentType;
private readonly object[] _targets;
private readonly PropertyDescriptor _property;
public MultiTargetPropertyDescriptor(string propertyName, Type propertyType, Type componentType, PropertyDescriptor property, object[] targets)
: base(propertyName, new Attribute[] { })
{
_propertyType = propertyType;
_componentType = componentType;
_targets = targets;
_property = property;
}
public override object GetValue(object component)
{
var val = _property.GetValue(_targets[0]);
for (var i = 1; i < _targets.Length; i++)
{
var v = _property.GetValue(_targets[i]);
if (!v.Equals(val))
return null;
}
return val;
}
public override void SetValue(object component, object value)
{
for (var i = 0; i < _targets.Length; i++)
_property.SetValue(_targets[i], value);
}
public override bool CanResetValue(object component) { return true; }
public override Type ComponentType { get { return _componentType; } }
public override bool IsReadOnly { get { return false; } }
public override Type PropertyType { get { return _propertyType; } }
public override void ResetValue(object component) { SetValue(component, null); }
public override bool ShouldSerializeValue(object component) { return true; }
}
/// <summary>
/// PropertyDescriptor for a named item within an OpaqueDataDictionary.
/// </summary>
public class OpaqueDataDictionaryElementPropertyDescriptor : PropertyDescriptor
{
private readonly Type _propertyType;
private readonly Type _componentType;
private readonly string _propertyName;
private readonly OpaqueDataDictionary _target;
public OpaqueDataDictionaryElementPropertyDescriptor(string propertyName, Type propertyType, OpaqueDataDictionary target)
: base(propertyName, new Attribute[] { })
{
_propertyType = propertyType;
_propertyName = propertyName;
_componentType = typeof(OpaqueDataDictionary);
_target = target;
}
public override object GetValue(object component)
{
var data = _target ?? (component as OpaqueDataDictionary);
if (!data.ContainsKey(_propertyName))
return string.Empty;
return data[_propertyName];
}
public override void SetValue(object component, object value)
{
var data = _target ?? (component as OpaqueDataDictionary);
data[_propertyName] = value;
}
public override bool CanResetValue(object component) { return true; }
public override Type ComponentType { get { return _componentType; } }
public override bool IsReadOnly { get { return false; } }
public override Type PropertyType { get { return _propertyType; } }
public override void ResetValue(object component) { SetValue(component, null); }
public override bool ShouldSerializeValue(object component) { return true; }
}
/// <summary>
/// PropertyDescriptor which has a fixed value.
/// </summary>
public class ReadonlyPropertyDescriptor : PropertyDescriptor
{
private readonly Type _propertyType;
private readonly Type _componentType;
private readonly object _value;
public ReadonlyPropertyDescriptor(string propertyName, Type propertyType, Type componentType, object value)
: base(propertyName, new Attribute[] {})
{
_propertyType = propertyType;
_componentType = componentType;
_value = value;
}
public override object GetValue(object component)
{
return _value;
}
public override bool CanResetValue(object component)
{
return false;
}
public override Type ComponentType
{
get { return _componentType; }
}
public override bool IsReadOnly
{
get { return true; }
}
public override Type PropertyType
{
get { return _propertyType; }
}
public override void ResetValue(object component)
{
}
public override void SetValue(object component, object value)
{
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
}
}
@@ -0,0 +1,67 @@
// 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.ComponentModel;
using System.IO;
namespace MonoGame.Tools.Pipeline
{
public class DirectoryItem : IProjectItem
{
public DirectoryItem(string name, string location) : this(location + Path.DirectorySeparatorChar + name)
{
}
public DirectoryItem(string path)
{
OriginalPath = path.Trim(Path.DirectorySeparatorChar).Replace(Path.DirectorySeparatorChar, '/');
Exists = true;
}
#region IProjectItem
[Browsable(false)]
public string OriginalPath { get; set; }
[Category("Common")]
[Description("The file name of this item.")]
public string Name
{
get
{
return Path.GetFileName(OriginalPath);
}
}
[Category("Common")]
[Description("The folder where this item is located.")]
public string Location
{
get
{
return Path.GetDirectoryName(OriginalPath);
}
}
[Browsable(false)]
public bool Exists { get; set; }
[Browsable(false)]
public bool ExpandToThis { get; set; }
[Browsable(false)]
public bool SelectThis { get; set; }
[Browsable(false)]
public string DestinationPath
{
get { return OriginalPath; }
set { OriginalPath = value; }
}
#endregion
}
}
@@ -0,0 +1,19 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MonoGame.Tools.Pipeline
{
public static class Extensions
{
public static bool NullOrEmpty(this IList list)
{
if (list == null || list.Count == 0)
return true;
return false;
}
}
}
@@ -0,0 +1,14 @@
// 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.
namespace MonoGame.Tools.Pipeline
{
public enum FileType
{
Base,
File,
Folder
}
}
@@ -0,0 +1,134 @@
// 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;
namespace MonoGame.Tools.Pipeline
{
public interface IContentItemObserver
{
void OnItemModified(ContentItem item);
}
public interface IController : IContentItemObserver
{
/// <summary>
/// Types of content which can be created and added to a project.
/// </summary>
IEnumerable<ContentItemTemplate> Templates { get; }
List<IProjectItem> SelectedItems { get; }
IProjectItem SelectedItem { get; }
PipelineProject ProjectItem { get; }
/// <summary>
/// True if there is a project.
/// </summary>
bool ProjectOpen { get; }
/// <summary>
/// True if the project has unsaved changes.
/// </summary>
bool ProjectDirty { get; }
/// <summary>
/// True if the project is actively building.
/// </summary>
bool ProjectBuilding { get; }
/// <summary>
/// The view this controller is attached to.
/// </summary>
IView View { get; }
/// <summary>
/// Triggered when the project starts loading.
/// </summary>
event Action OnProjectLoading;
/// <summary>
/// Triggered when the project finishes loading.
/// </summary>
event Action OnProjectLoaded;
/// <summary>
/// Notify controller that a property of Project or its contents has been modified.
/// </summary>
void OnProjectModified();
/// <summary>
/// Notify controller that Project.References has been modified.
/// </summary>
void OnReferencesModified();
void NewProject();
void ImportProject();
void OpenProject();
void OpenProject(string projectFilePath);
void ClearRecentList();
void CloseProject();
bool SaveProject(bool saveAs);
void Build(bool rebuild);
void RebuildItems();
void Clean();
void CancelBuild();
bool Exit();
#region ContentItem
void DragDrop(string initialDirectory, string[] folders, string[] files);
void Include();
void IncludeFolder();
void Exclude(bool delete);
void NewItem();
void NewFolder();
void Rename();
void AddAction(IProjectAction action);
void SelectionChanged(List<IProjectItem> items);
IProjectItem GetItem(string originalPath);
void CopyAssetPath();
#endregion
#region Undo, Redo
bool CanRedo { get; }
bool CanUndo { get; }
void Undo();
void Redo();
#endregion
string GetFullPath(string filePath);
string GetRelativePath(string filePath);
}
}
@@ -0,0 +1,12 @@
// 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.
namespace MonoGame.Tools.Pipeline
{
public interface IProjectAction
{
bool Do();
bool Undo();
}
}
@@ -0,0 +1,14 @@
// 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.
namespace MonoGame.Tools.Pipeline
{
public interface IProjectItem
{
string OriginalPath { get; set; }
string Name { get; }
string Location { get; }
string DestinationPath { get; set; }
}
}
@@ -0,0 +1,10 @@
// 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.
namespace MonoGame.Tools.Pipeline
{
public interface IProjectObserver
{
}
}
@@ -0,0 +1,76 @@
// 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.Diagnostics;
namespace MonoGame.Tools.Pipeline
{
public enum AskResult
{
Yes,
No,
Cancel
}
public interface IView
{
void Attach(IController controller);
void Invoke(Action action);
AskResult AskSaveOrCancel();
bool AskSaveName(ref string filePath, string title);
bool AskOpenProject(out string projectFilePath);
bool AskImportProject(out string projectFilePath);
void ShowError(string title, string message);
void ShowMessage(string message);
bool ShowDeleteDialog(List<IProjectItem> items);
bool ShowEditDialog(string title, string text, string oldname, bool file, out string newname);
void BeginTreeUpdate();
void SetTreeRoot(IProjectItem item);
void AddTreeItem(IProjectItem item);
void RemoveTreeItem(IProjectItem item);
void UpdateTreeItem(IProjectItem item);
void EndTreeUpdate();
void UpdateProperties();
void OutputAppend(string text);
void OutputClear();
bool ChooseContentFile(string initialDirectory, out List<string> files);
bool ChooseContentFolder(string initialDirectory, out string folder);
bool ChooseItemTemplate(string folder, out ContentItemTemplate template, out string name);
bool CopyOrLinkFile(string file, bool exists, out IncludeType action, out bool applyforall);
bool CopyOrLinkFolder(string folder, bool exists, out IncludeType action, out bool applyforall);
Process CreateProcess(string exe, string commands);
void UpdateCommands(MenuInfo info);
void UpdateRecentList(List<string> recentList);
void SetClipboard(string text);
}
}
@@ -0,0 +1,44 @@
// 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.
namespace MonoGame.Tools.Pipeline
{
public enum IncludeType
{
Skip,
Copy,
Link,
Create
}
/// <summary>
/// This class is used for IncludeAction file handling.
/// </summary>
public class IncludeItem
{
/// <summary>
/// Gets or sets the source path.
/// </summary>
/// <value>The source path, should be an absolute path.</value>
public string SourcePath { get; set; }
public IncludeType IncludeType { get; set; }
/// <summary>
/// Gets or sets the relative destination path.
/// </summary>
/// <value>The relative destination path.</value>
public string RelativeDestPath { get; set; }
public bool IsDirectory { get; set; }
/// <summary>
/// Gets or sets the item template.
///
/// Only usefull if the action is create and the item is not a directory.
/// </summary>
/// <value>The item template.</value>
public ContentItemTemplate ItemTemplate { get; set; }
}
}
@@ -0,0 +1,59 @@
// 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.
namespace MonoGame.Tools.Pipeline
{
public class MenuInfo
{
public bool New { get; set; }
public bool Open { get; set; }
public bool Close { get; set; }
public bool Import { get; set; }
public bool Save { get; set; }
public bool SaveAs { get; set; }
public bool Exit { get; set; }
public bool Undo { get; set; }
public bool Redo { get; set; }
public bool Add { get; set; }
public bool Exclude { get; set; }
public bool Rename { get; set; }
public bool Delete { get; set; }
public bool BuildMenu { get; set; }
public bool Build { get; set; }
public bool Rebuild { get; set; }
public bool Clean { get; set; }
public bool Cancel { get; set; }
public bool Debug { get; set; }
public bool OpenItem { get; set; }
public bool OpenItemWith { get; set; }
public bool OpenItemLocation { get; set; }
public bool OpenOutputItemLocation { get; set; }
public bool CopyAssetPath { get; set; }
public bool RebuildItem { get; set; }
}
}
@@ -0,0 +1,194 @@
// 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.Text.RegularExpressions;
namespace MonoGame.Tools.Pipeline
{
public enum OutputState
{
Initialized,
BuildBegin,
Cleaning,
Skipping,
BuildAsset,
BuildError,
BuildWarning,
BuildErrorContinue,
BuildEnd,
BuildTime,
BuildTerminated,
Unknown
}
public class OutputParser
{
internal OutputState State { get; private set; }
internal String Filename { get; private set; }
internal String ErrorMessage { get; private set; }
internal String BuildBeginTime { get; private set; }
internal String BuildInfo { get; private set; }
internal String BuildElapsedTime { get; private set; }
Regex _reBuildBegin = new Regex(@"^(Build started)\W+(?<buildBeginTime>.*?)\r?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
Regex _reCleaning = new Regex(@"^(Cleaning)\W(?<filename>.*?)\r?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
Regex _reSkipping = new Regex(@"^(Skipping)\W(?<filename>.*?)\r?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
Regex _reBuildAsset = new Regex(@"^(?<filename>([a-zA-Z]:)?/.+?)\r?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
Regex _reBuildError = new Regex(@"^(?<filename>([a-zA-Z]:)?/.+?)\W*?:\W*?error\W*?:\W*(?<errorMessage>.*?)\r?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
Regex _reFileErrorWithLineNum = new Regex(@"^(?<filename>.+?)(\((?<line>[0-9]+),(?<column>[0-9]+)(-(?<columnEnd>[0-9]+))?\))?:\W*?(error)\W*(?<errorCode>[A-Z][0-9]+):\W*(?<errorMessage>.*?)\r?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
Regex _reFileWarningWithLineNum = new Regex(@"^(?<filename>.+?)(\((?<line>[0-9]+),(?<column>[0-9]+)(-(?<columnEnd>[0-9]+))?\))?:\W*?(warning)\W*(?<warningCode>[A-Z][0-9]+):\W*(?<warningMessage>.*?)\r?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
Regex _reFileError = new Regex(@"^(?<filename>([a-zA-Z]:)?/.+?)\W*?: (?<errorMessage>.*?)\r?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
Regex _reBuildEnd = new Regex(@"^(Build)\W+(?<buildInfo>.*?)\r?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
Regex _reBuildTime = new Regex(@"^(Time elapsed)\W+(?<buildElapsedTime>.*?)\.\r?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
public OutputParser()
{
Reset();
}
internal void Reset()
{
State = OutputState.Initialized;
Filename = null;
BuildBeginTime = null;
BuildInfo = null;
BuildElapsedTime = null;
ErrorMessage = null;
}
internal void Parse(string text)
{
ParseLine(text);
}
private void ParseLine(string line)
{
/*
* Line <-- BuildBegin
* Line <-- BuildEnd
* Line <-- BuildTime
* Line <-- Cleaning
* Line <-- Skipping
* Line <-- BuildError (BuildErrorContinue)+
* Line <-- BuildAsset
* BuildBegin <-- "Build" "started" buildBeginTime
* Cleaning <-- "Cleaning" filenane
* Skipping <-- "Skipping" filenane
* BuildAsset <-- filename
* BuildError <-- filename ':' "Error" ':' errorMessage
* BuildErrorContinue <-- errorMessage
* BuildEnd <-- "Build" buildInfo
* BuildTime <-- "Time" "elapsed" buildElapsedTime
*/
var prevState = State;
var prevFilename = Filename;
State = OutputState.Unknown;
Filename = null;
BuildBeginTime = null;
BuildInfo = null;
BuildElapsedTime = null;
ErrorMessage = null;
if (line == "Build terminated!")
{
State = OutputState.BuildTerminated;
}
else if (_reBuildBegin.IsMatch(line))
{
State = OutputState.BuildBegin;
var m = _reBuildBegin.Match(line);
BuildBeginTime = m.Groups["buildBeginTime"].Value;
}
else if (_reBuildEnd.IsMatch(line))
{
State = OutputState.BuildEnd;
var m = _reBuildEnd.Match(line);
BuildInfo = m.Groups["buildInfo"].Value;
}
else if (_reBuildTime.IsMatch(line))
{
State = OutputState.BuildTime;
var m = _reBuildTime.Match(line);
BuildElapsedTime = m.Groups["buildElapsedTime"].Value;
}
else if (_reCleaning.IsMatch(line))
{
State = OutputState.Cleaning;
var m = _reCleaning.Match(line);
Filename = m.Groups["filename"].Value;
}
else if (_reSkipping.IsMatch(line))
{
State = OutputState.Skipping;
var m = _reSkipping.Match(line);
Filename = m.Groups["filename"].Value;
}
else if (_reBuildError.IsMatch(line))
{
State = OutputState.BuildError;
var m = _reBuildError.Match(line);
Filename = m.Groups["filename"].Value;
ErrorMessage = m.Groups["errorMessage"].Value;
}
else if (_reFileErrorWithLineNum.IsMatch(line))
{
State = OutputState.BuildError;
var m = _reFileErrorWithLineNum.Match(line);
var lineNum = m.Groups["line"];
var columnBegin = m.Groups["column"];
var columnEnd = m.Groups["columnEnd"];
var column = columnBegin.Value;
if(columnEnd.Success)
column += "-" + columnEnd.Value;
var errorCode = m.Groups["errorCode"];
Filename = m.Groups["filename"].Value.Replace("\\\\","/").Replace("\\", "/");
ErrorMessage = string.Format("{0} ({1},{2}): {3}", errorCode, lineNum, column, m.Groups["errorMessage"].Value);
}
else if (_reFileWarningWithLineNum.IsMatch(line))
{
State = OutputState.BuildWarning;
var m = _reFileWarningWithLineNum.Match(line);
var lineNum = m.Groups["line"];
var columnBegin = m.Groups["column"];
var columnEnd = m.Groups["columnEnd"];
var column = columnBegin.Value;
if(columnEnd.Success)
column += "-" + columnEnd.Value;
var errorCode = m.Groups["warningCode"];
Filename = m.Groups["filename"].Value.Replace("\\\\", "/").Replace("\\", "/");
ErrorMessage = string.Format("{0} ({1},{2}): {3}", errorCode, lineNum, column, m.Groups["warningMessage"].Value);
}
else if (_reFileError.IsMatch(line))
{
State = OutputState.BuildError;
var m = _reFileError.Match(line);
Filename = m.Groups["filename"].Value;
ErrorMessage = m.Groups["errorMessage"].Value;
}
else if (_reBuildAsset.IsMatch(line))
{
State = OutputState.BuildAsset;
var m = _reBuildAsset.Match(line);
Filename = m.Groups["filename"].Value;
}
else if (prevState == OutputState.BuildError || prevState == OutputState.BuildErrorContinue)
{
State = OutputState.BuildErrorContinue;
Filename = prevFilename;
ErrorMessage = line.TrimEnd();
}
else
{
State = OutputState.Unknown;
}
}
}
}
@@ -0,0 +1,112 @@
// 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;
namespace MonoGame.Tools.Pipeline
{
public partial class PipelineController
{
private class ExcludeAction : IProjectAction
{
private readonly PipelineController _con;
private readonly List<IProjectItem> _items;
private readonly List<ContentItem> _subitems;
private readonly bool _delete;
public ExcludeAction(PipelineController controller, List<IProjectItem> items, bool delete)
{
_items = new List<IProjectItem>();
_subitems = new List<ContentItem>();
_con = controller;
_items.AddRange(items);
_delete = delete;
foreach (var item in items)
if (item is DirectoryItem)
foreach (var citem in _con._project.ContentItems)
if (citem.OriginalPath.StartsWith(item.OriginalPath))
_subitems.Add(citem);
}
public bool Do()
{
_con.View.BeginTreeUpdate();
foreach (var item in _items)
{
if (item is ContentItem)
_con._project.ContentItems.Remove(item as ContentItem);
_con.View.RemoveTreeItem(item);
if (_delete)
{
// Only delete if the item is in the project folder, otherwise we may (and have done!) delete files/folders outside of the project
if (!item.OriginalPath.Contains(".."))
{
var fullItemPath = _con.GetFullPath(item.OriginalPath);
try
{
if (item is DirectoryItem)
Directory.Delete(_con.GetFullPath(item.OriginalPath), true);
else
File.Delete(_con.GetFullPath(item.OriginalPath));
}
catch (FileNotFoundException)
{
// No error needed in case file is not found
}
catch (Exception ex)
{
_con.View.ShowError("Error while trying to delete the file", ex.Message);
}
}
}
}
foreach (var sitem in _subitems)
_con._project.ContentItems.Remove(sitem);
//Since these items are removed from the project, manually clear the selection
_con.SelectedItems.Clear();
_con.SelectionChanged(_con.SelectedItems);
_con.View.EndTreeUpdate();
_con.ProjectDirty = true;
return true;
}
public bool Undo()
{
if (_delete)
return false;
_con.View.BeginTreeUpdate();
foreach (var item in _items)
{
if(item is ContentItem)
_con._project.ContentItems.Add(item as ContentItem);
_con.View.AddTreeItem(item);
}
foreach (var item in _subitems)
{
_con._project.ContentItems.Add(item);
_con.View.AddTreeItem(item);
}
_con.View.EndTreeUpdate();
_con.ProjectDirty = true;
return true;
}
}
}
}
@@ -0,0 +1,153 @@
// 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.Collections.Generic;
using System.IO;
using System;
namespace MonoGame.Tools.Pipeline
{
public partial class PipelineController
{
private class IncludeAction : IProjectAction
{
private readonly PipelineController _con;
private readonly List<IncludeItem> _includes;
private readonly List<IProjectItem> _items;
private bool _firsttime;
public IncludeAction(List<IncludeItem> includes)
{
_items = new List<IProjectItem>();
_con = Instance;
_includes = includes;
_firsttime = true;
}
public IncludeAction(IncludeItem item) : this(new List<IncludeItem> { item })
{
}
/// <summary>
/// Prepares the item list and creates all the neccecary files.
/// </summary>
public void FirstDo()
{
var parser = new PipelineProjectParser(_con, _con._project);
for (int i = 0; i < _includes.Count; i++)
{
var item = _includes[i].IsDirectory ?
(IProjectItem)new DirectoryItem("") : new ContentItem();
if (_includes[i].IncludeType == IncludeType.Create ||
_includes[i].IncludeType == IncludeType.Link)
{
item.OriginalPath = Util.GetRelativePath(_includes[i].SourcePath, _con.ProjectLocation);
item.DestinationPath = _includes[i].RelativeDestPath;
if (_includes[i].IncludeType == IncludeType.Create)
{
if (_includes[i].IsDirectory)
Directory.CreateDirectory(_includes[i].SourcePath);
else
{
var conitem = item as ContentItem;
var template = _includes[i].ItemTemplate;
conitem.ImporterName = template.ImporterName;
conitem.ProcessorName = template.ProcessorName;
Directory.CreateDirectory(Path.GetDirectoryName(_includes[i].SourcePath));
File.Copy(template.TemplateFile, _includes[i].SourcePath);
}
}
}
else if (!_includes[i].IsDirectory) // This is only a valid action for files
{
var sourcePath = _includes[i].SourcePath;
var destPath = Path.Combine(_con.ProjectLocation, _includes[i].RelativeDestPath);
Directory.CreateDirectory(Path.GetDirectoryName(destPath));
File.Copy(sourcePath, destPath);
item.OriginalPath = item.DestinationPath = _includes[i].RelativeDestPath;
}
var citem = item as ContentItem;
if (citem != null)
{
citem.ProcessorParams = new Microsoft.Xna.Framework.Content.Pipeline.OpaqueDataDictionary();
citem.Observer = _con;
citem.ResolveTypes();
}
// Always keep Unix slashes in the .mgcb files for cross platform compatibility
item.OriginalPath = item.OriginalPath.Replace('\\', '/');
item.DestinationPath = item.DestinationPath.Replace('\\', '/');
_items.Add(item);
}
}
public bool Do()
{
if (_firsttime)
{
// Generate file list and populate item list
_firsttime = false;
try
{
FirstDo();
}
catch (Exception ex)
{
_con.View.ShowError(
"Include Action Error",
"The include action has failed for the following reason: " +
Environment.NewLine + ex
);
return false;
}
}
_con.View.BeginTreeUpdate();
foreach (var item in _items)
{
if (item is ContentItem)
_con._project.ContentItems.Add(item as ContentItem);
_con.View.AddTreeItem(item);
}
_con.View.EndTreeUpdate();
_con.ProjectDirty = true;
return true;
}
public bool Undo()
{
_con.View.BeginTreeUpdate();
foreach (var item in _items)
{
if (item is ContentItem)
_con._project.ContentItems.Remove(item as ContentItem);
_con.View.RemoveTreeItem(item);
}
_con.View.EndTreeUpdate();
_con.ProjectDirty = true;
return true;
}
}
}
}
@@ -0,0 +1,48 @@
// 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.IO;
namespace MonoGame.Tools.Pipeline
{
public partial class PipelineController
{
private class MoveAction : IProjectAction
{
private IProjectItem _item;
private readonly PipelineController _con;
private string _oldname, _newname;
public MoveAction(IProjectItem item, string newname)
{
_con = PipelineController.Instance;
_oldname = Path.GetFileName(item.OriginalPath);
_newname = newname;
_item = item;
}
public bool Do()
{
_con.View.RemoveTreeItem(_item);
var folder = Path.GetDirectoryName(_item.DestinationPath);
_item.DestinationPath = Path.Combine(folder, _newname).Replace('\\', '/');
_con.View.AddTreeItem(_item);
_con.ProjectDirty = true;
return true;
}
public bool Undo()
{
_con.View.RemoveTreeItem(_item);
var folder = Path.GetDirectoryName(_item.DestinationPath);
_item.DestinationPath = Path.Combine(folder, _oldname).Replace('\\', '/');
_con.View.AddTreeItem(_item);
_con.ProjectDirty = true;
return true;
}
}
}
}
@@ -0,0 +1,56 @@
// 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.Collections.Generic;
namespace MonoGame.Tools.Pipeline
{
public class UpdateProcessorAction : IProjectAction
{
private readonly IView _view;
private readonly List<ContentItem> _objects;
private readonly string _property;
private List<object> _values;
public UpdateProcessorAction(IView view, List<ContentItem> objects, string property, object value)
{
_view = view;
_objects = objects;
_property = property;
_values = new List<object>();
for (int i = 0; i < _objects.Count; i++)
_values.Add(value);
}
public bool Do()
{
Toggle();
return true;
}
public bool Undo()
{
Toggle();
return true;
}
private void Toggle()
{
var oldValues = new List<object>();
for (int i = 0; i < _objects.Count; i++)
{
var obj = _objects[i];
oldValues.Add(obj.ProcessorParams[_property]);
obj.ProcessorParams[_property] = _values[i];
}
_view.UpdateProperties();
_values = oldValues;
}
}
}
@@ -0,0 +1,57 @@
// 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.Collections.Generic;
using System.Reflection;
namespace MonoGame.Tools.Pipeline
{
public class UpdatePropertyAction : IProjectAction
{
private readonly IView _view;
private readonly List<object> _objects;
private readonly PropertyInfo _property;
private List<object> _values;
public UpdatePropertyAction(IView view, List<object> objects, PropertyInfo property, object value)
{
_view = view;
_objects = objects;
_property = property;
_values = new List<object>();
for (int i = 0; i < _objects.Count; i++)
_values.Add(value);
}
public bool Do()
{
Toggle();
return true;
}
public bool Undo()
{
Toggle();
return true;
}
private void Toggle()
{
var oldValues = new List<object>();
for (int i = 0; i < _objects.Count; i++)
{
var obj = _objects[i];
oldValues.Add(_property.GetValue(obj, null));
_property.SetValue(obj, _values[i], null);
}
_view.UpdateProperties();
_values = oldValues;
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,89 @@
// 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.IO;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Graphics;
namespace MonoGame.Tools.Pipeline
{
public class PipelineProject : IProjectItem
{
[Browsable(false)]
public string OriginalPath { get; set; }
[Browsable(false)]
public string DestinationPath { get; set; }
[Browsable(false)]
public List<ContentItem> ContentItems { get; private set; }
[Browsable(false)]
public bool LaunchDebugger { get; set; }
public string OutputDir { get; set; }
public string IntermediateDir { get; set; }
public List<string> References { get; set; }
public TargetPlatform Platform { get; set; }
public GraphicsProfile Profile { get; set; }
public string Config { get; set; }
public bool Compress { get; set; }
#region IPipelineItem
[Category("Common")]
public string Name
{
get
{
if (string.IsNullOrEmpty(OriginalPath))
return "";
return System.IO.Path.GetFileNameWithoutExtension(OriginalPath);
}
}
[Category("Common")]
public string Location
{
get
{
if (string.IsNullOrEmpty(OriginalPath))
return "";
var idx = OriginalPath.LastIndexOfAny(new char[] {Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar}, OriginalPath.Length - 1);
return OriginalPath.Remove(idx);
}
}
[Browsable(false)]
public bool Exists { get; set; }
[Browsable(false)]
public bool ExpandToThis { get; set; }
[Browsable(false)]
public bool SelectThis { get; set; }
#endregion
public PipelineProject()
{
ContentItems = new List<ContentItem>();
References = new List<string>();
OutputDir = "bin";
IntermediateDir = "obj";
Exists = true;
}
}
}
@@ -0,0 +1,586 @@
// 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.Globalization;
using System.IO;
using System.Linq;
using System.Xml;
using MGCB;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Graphics;
using PathHelper = MonoGame.Framework.Content.Pipeline.Builder.PathHelper;
namespace MonoGame.Tools.Pipeline
{
public class PipelineProjectParser
{
#region Other Data
private readonly PipelineProject _project;
private readonly IContentItemObserver _observer;
private readonly OpaqueDataDictionary _processorParams = new OpaqueDataDictionary();
private string _processor;
#endregion
#region CommandLineParameters
[CommandLineParameter(
Name = "outputDir",
ValueName = "directoryPath",
Description = "The directory where all content is written.")]
public string OutputDir { set { _project.OutputDir = value; } }
[CommandLineParameter(
Name = "intermediateDir",
ValueName = "directoryPath",
Description = "The directory where all intermediate files are written.")]
public string IntermediateDir { set { _project.IntermediateDir = value; } }
[CommandLineParameter(
Name = "reference",
ValueName = "assemblyNameOrFile",
Description = "Adds an assembly reference for resolving content importers, processors, and writers.")]
public List<string> References
{
set { _project.References = value; }
get { return _project.References; }
}
[CommandLineParameter(
Name = "platform",
ValueName = "targetPlatform",
Description = "Set the target platform for this build. Defaults to Windows.")]
public TargetPlatform Platform { set { _project.Platform = value; } }
[CommandLineParameter(
Name = "profile",
ValueName = "graphicsProfile",
Description = "Set the target graphics profile for this build. Defaults to HiDef.")]
public GraphicsProfile Profile { set { _project.Profile = value; } }
[CommandLineParameter(
Name = "config",
ValueName = "string",
Description = "The optional build config string from the build system.")]
public string Config { set { _project.Config = value; } }
#pragma warning disable 414
// Allow a MGCB file containing the /rebuild parameter to be imported without error
[CommandLineParameter(
Name = "rebuild",
ValueName = "bool",
Description = "Forces a rebuild of the project.")]
public bool Rebuild { set { _rebuild = value; } }
private bool _rebuild;
// Allow a MGCB file containing the /clean parameter to be imported without error
[CommandLineParameter(
Name = "clean",
ValueName = "bool",
Description = "Removes intermediate and output files.")]
public bool Clean { set { _clean = value; } }
private bool _clean;
#pragma warning restore 414
[CommandLineParameter(
Name = "compress",
ValueName = "bool",
Description = "Content files can be compressed for smaller file sizes.")]
public bool Compress { set { _project.Compress = value; } }
[CommandLineParameter(
Name = "importer",
ValueName = "className",
Description = "Defines the class name of the content importer for reading source content.")]
public string Importer;
[CommandLineParameter(
Name = "processor",
ValueName = "className",
Description = "Defines the class name of the content processor for processing imported content.")]
public string Processor
{
get { return _processor; }
set
{
_processor = value;
_processorParams.Clear();
}
}
[CommandLineParameter(
Name = "processorParam",
ValueName = "name=value",
Description = "Defines a parameter name and value to set on a content processor.")]
public void AddProcessorParam(string nameAndValue)
{
var keyAndValue = nameAndValue.Split('=');
if (keyAndValue.Length != 2)
{
// Do we error out or something?
return;
}
_processorParams.Remove(keyAndValue[0]);
_processorParams.Add(keyAndValue[0], keyAndValue[1]);
}
[CommandLineParameter(
Name = "build",
ValueName = "sourceFile",
Description = "Build the content source file using the previously set switches and options.")]
public void OnBuild(string sourceFile)
{
AddContent(sourceFile, false);
}
[CommandLineParameter(
Name = "launchDebugger",
ValueName = "sourceFile")]
public void OnDebug()
{
_project.LaunchDebugger = true;
}
public bool AddContent(string sourceFile, bool skipDuplicates)
{
string link = null;
if (sourceFile.Contains(";"))
{
var split = sourceFile.Split(';');
sourceFile = split[0];
if (split.Length > 0)
link = split[1];
}
// Make sure the source file is relative to the project.
var projectDir = ProjectDirectory + Path.DirectorySeparatorChar;
sourceFile = PathHelper.GetRelativePath(projectDir, sourceFile);
// Do we have a duplicate?
var previous = _project.ContentItems.FindIndex(e => string.Equals(e.OriginalPath, sourceFile, StringComparison.InvariantCultureIgnoreCase));
if (previous != -1)
{
if (skipDuplicates)
return false;
// Replace the duplicate.
_project.ContentItems.RemoveAt(previous);
}
// Create the item for processing later.
var item = new ContentItem
{
Observer = _observer,
BuildAction = BuildAction.Build,
OriginalPath = sourceFile,
DestinationPath = string.IsNullOrEmpty(link) ? sourceFile : link,
ImporterName = Importer,
ProcessorName = Processor,
ProcessorParams = new OpaqueDataDictionary()
};
_project.ContentItems.Add(item);
// Copy the current processor parameters blind as we
// will validate and remove invalid parameters during
// the build process later.
foreach (var pair in _processorParams)
item.ProcessorParams.Add(pair.Key, pair.Value);
return true;
}
[CommandLineParameter(
Name = "copy",
ValueName = "sourceFile",
Description = "Copy the content source file verbatim to the output directory.")]
public void OnCopy(string sourceFile)
{
string link = null;
if (sourceFile.Contains(";"))
{
var split = sourceFile.Split(';');
sourceFile = split[0];
if (split.Length > 0)
link = split[1];
}
// Make sure the source file is relative to the project.
var projectDir = ProjectDirectory + Path.DirectorySeparatorChar;
sourceFile = PathHelper.GetRelativePath(projectDir, sourceFile);
// Remove duplicates... keep this new one.
var previous = _project.ContentItems.FirstOrDefault(e => e.OriginalPath.Equals(sourceFile));
if (previous != null)
_project.ContentItems.Remove(previous);
// Create the item for processing later.
var item = new ContentItem
{
BuildAction = BuildAction.Copy,
OriginalPath = sourceFile,
DestinationPath = string.IsNullOrEmpty(link) ? sourceFile : link,
ProcessorParams = new OpaqueDataDictionary()
};
_project.ContentItems.Add(item);
// Copy the current processor parameters blind as we
// will validate and remove invalid parameters during
// the build process later.
foreach (var pair in _processorParams)
item.ProcessorParams.Add(pair.Key, pair.Value);
}
#endregion
public PipelineProjectParser(IContentItemObserver observer, PipelineProject project)
{
_observer = observer;
_project = project;
}
public void OpenProject(string projectFilePath, MGBuildParser.ErrorCallback errorCallback)
{
_project.ContentItems.Clear();
// Store the file name for saving later.
_project.OriginalPath = projectFilePath;
var parser = new MGBuildParser(this);
parser.Title = "Pipeline";
if (errorCallback != null)
parser.OnError += errorCallback;
var commands = new string[]
{
string.Format("/@:{0}", projectFilePath),
};
parser.Parse(commands);
}
public void SaveProject()
{
using (var io = File.CreateText(_project.OriginalPath))
SaveProject(io, null);
}
public void SaveProject(TextWriter io, Func<ContentItem, bool> filterItem)
{
const string lineFormat = "/{0}:{1}";
const string processorParamFormat = "{0}={1}";
string line;
line = FormatDivider("Global Properties");
io.WriteLine(line);
line = string.Format(lineFormat, "outputDir", _project.OutputDir);
io.WriteLine(line);
line = string.Format(lineFormat, "intermediateDir", _project.IntermediateDir);
io.WriteLine(line);
line = string.Format(lineFormat, "platform", _project.Platform);
io.WriteLine(line);
line = string.Format(lineFormat, "config", _project.Config);
io.WriteLine(line);
line = string.Format(lineFormat, "profile", _project.Profile);
io.WriteLine(line);
line = string.Format(lineFormat, "compress", _project.Compress);
io.WriteLine(line);
if (_project.LaunchDebugger)
io.WriteLine("/launchdebugger");
line = FormatDivider("References");
io.WriteLine(line);
foreach (var i in _project.References)
{
line = string.Format(lineFormat, "reference", i);
io.WriteLine(line);
}
line = FormatDivider("Content");
io.WriteLine(line);
// Sort the items alphabetically to ensure a consistent output
// and better mergability of the resulting MGCB file.
var sortedItems = _project.ContentItems.OrderBy(c => c.OriginalPath, StringComparer.InvariantCulture);
foreach (var i in sortedItems)
{
// Reject any items that don't pass the filter.
if (filterItem != null && filterItem(i))
continue;
// Wrap content item lines with a begin comment line
// to make them more cohesive (for version control).
line = string.Format("#begin {0}", i.OriginalPath);
io.WriteLine(line);
if (i.BuildAction == BuildAction.Copy)
{
string path = i.OriginalPath;
if (i.OriginalPath != i.DestinationPath)
path += ";" + i.DestinationPath;
line = string.Format(lineFormat, "copy", path);
io.WriteLine(line);
io.WriteLine();
}
else
{
// Write importer.
{
line = string.Format(lineFormat, "importer", i.ImporterName);
io.WriteLine(line);
}
// Write processor.
{
line = string.Format(lineFormat, "processor", i.ProcessorName);
io.WriteLine(line);
}
// Write processor parameters.
{
if (i.Processor == PipelineTypes.MissingProcessor)
{
// Could still be missing the real processor.
// If so, write the string parameters from import.
foreach (var j in i.ProcessorParams)
{
line = string.Format(lineFormat, "processorParam", string.Format(processorParamFormat, j.Key, j.Value));
io.WriteLine(line);
}
}
else
{
// Otherwise, write only values which are defined by the real processor.
foreach (var j in i.Processor.Properties)
{
object value = null;
if (i.ProcessorParams.ContainsKey(j.Name))
value = i.ProcessorParams[j.Name];
// JCF: I 'think' writting an empty string for null would be appropriate but to be on the safe side
// im just not writting the value at all.
if (value != null)
{
var converter = PipelineTypes.FindConverter(value.GetType());
var valueStr = converter.ConvertTo(null, CultureInfo.InvariantCulture, value, typeof(string));
line = string.Format(lineFormat, "processorParam", string.Format(processorParamFormat, j.Name, valueStr));
io.WriteLine(line);
}
}
}
}
string buildValue = i.OriginalPath;
if (i.OriginalPath != i.DestinationPath)
buildValue += ";" + i.DestinationPath;
line = string.Format(lineFormat, "build", buildValue);
io.WriteLine(line);
io.WriteLine();
}
}
}
public void ImportProject(string projectFilePath)
{
_project.OriginalPath = projectFilePath.Remove(projectFilePath.LastIndexOf('.')) + ".mgcb";
using (var io = XmlReader.Create(File.OpenText(projectFilePath)))
{
while (io.Read())
{
if (io.NodeType == XmlNodeType.Element)
{
var buildAction = io.LocalName;
if (buildAction.Equals("Reference"))
{
string include, hintPath;
ReadIncludeReference(io, out include, out hintPath);
if (!string.IsNullOrEmpty(hintPath) &&
hintPath.IndexOf("microsoft", StringComparison.CurrentCultureIgnoreCase) == -1 &&
hintPath.IndexOf("monogamecontentprocessors", StringComparison.CurrentCultureIgnoreCase) == -1)
{
_project.References.Add(hintPath);
}
}
else if (buildAction.Equals("Content") || buildAction.Equals("None"))
{
string include, copyToOutputDirectory;
ReadIncludeContent(io, out include, out copyToOutputDirectory);
if (!string.IsNullOrEmpty(copyToOutputDirectory) && !copyToOutputDirectory.Equals("Never"))
{
var sourceFilePath = Path.GetDirectoryName(projectFilePath);
sourceFilePath += "\\" + include;
OnCopy(sourceFilePath);
}
}
else if (buildAction.Equals("Compile"))
{
string include, name, importer, processor;
string[] processorParams;
ReadIncludeCompile(io, out include, out name, out importer, out processor, out processorParams);
Importer = importer;
Processor = processor;
if (processorParams != null)
{
foreach (var i in processorParams)
AddProcessorParam(i);
}
var sourceFilePath = Path.GetDirectoryName(projectFilePath);
sourceFilePath += "\\" + include;
OnBuild(sourceFilePath);
}
}
}
}
}
private string ProjectDirectory
{
get
{
return _project.Location;
}
}
private void ReadIncludeReference(XmlReader io, out string include, out string hintPath)
{
include = io.GetAttribute("Include").Unescape();
hintPath = null;
if (!io.IsEmptyElement)
{
var depth = io.Depth;
for (io.Read(); io.Depth != depth; io.Read())
{
// process sub nodes
if (io.IsStartElement("HintPath"))
{
io.Read();
hintPath = io.Value.Unescape();
}
}
}
}
private void ReadIncludeContent(XmlReader io, out string include, out string copyToOutputDirectory)
{
copyToOutputDirectory = null;
include = io.GetAttribute("Include").Unescape();
if (!io.IsEmptyElement)
{
var depth = io.Depth;
for (io.Read(); io.Depth != depth; io.Read())
{
// process sub nodes here.
if (io.IsStartElement())
{
switch (io.LocalName)
{
case "CopyToOutputDirectory":
io.Read();
copyToOutputDirectory = io.Value.Unescape();
break;
}
}
}
}
}
private void ReadIncludeCompile(XmlReader io,
out string include,
out string name,
out string importer,
out string processor,
out string[] processorParams)
{
name = null;
importer = null;
processor = null;
include = io.GetAttribute("Include").Unescape();
var parameters = new List<string>();
if (!io.IsEmptyElement)
{
var depth = io.Depth;
for (io.Read(); io.Depth != depth; io.Read())
{
// process sub nodes here.
if (io.IsStartElement())
{
switch (io.LocalName)
{
case "Name":
io.Read();
name = io.Value.Unescape();
break;
case "Importer":
io.Read();
importer = io.Value.Unescape();
break;
case "Processor":
io.Read();
processor = io.Value.Unescape();
break;
default:
if (io.LocalName.Contains("ProcessorParameters_"))
{
var line = io.LocalName.Replace("ProcessorParameters_", "");
line += "=";
io.Read();
line += io.Value;
parameters.Add(line.Unescape());
}
break;
}
}
}
}
processorParams = parameters.ToArray();
}
private string FormatDivider(string label)
{
var commentFormat = Environment.NewLine + "#----------------------------------------------------------------------------#" + Environment.NewLine;
label = " " + label + " ";
var src = commentFormat.Length / 2 - label.Length / 2;
var dst = src + label.Length;
return commentFormat.Substring(0, src) + label + commentFormat.Substring(dst);
}
}
}
@@ -0,0 +1,127 @@
// 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.Collections.Generic;
using System.IO;
using System.IO.IsolatedStorage;
using System.Xml.Serialization;
namespace MonoGame.Tools.Pipeline
{
public class PipelineSettings
{
private const string SettingsPath = "Settings.xml";
private IsolatedStorageFile _isoStore;
private bool _isoStoreInit;
public static PipelineSettings Default { get; private set; }
public List<string> ProjectHistory;
public string StartupProject;
public Microsoft.Xna.Framework.Point Size;
public int HSeparator, VSeparator;
public bool Maximized, DebugMode, PropertyGroupSort;
public bool FilterOutput, FilterShowSkipped, FilterShowSuccessful, FilterShowCleaned, AutoScrollBuildOutput;
public string ErrorMessage;
static PipelineSettings()
{
Default = new PipelineSettings();
}
public PipelineSettings()
{
ProjectHistory = new List<string>();
PropertyGroupSort = true;
FilterOutput = true;
FilterShowSkipped = true;
FilterShowSuccessful = true;
FilterShowCleaned = true;
AutoScrollBuildOutput = true;
try
{
_isoStore = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Assembly, null, null);
_isoStoreInit = true;
}
catch { }
}
/// <summary>
/// If the project already exists in history, it will be moved to the end.
/// </summary>
public void AddProjectHistory(string file)
{
var cleanFile = file.Trim();
ProjectHistory.Remove(cleanFile);
ProjectHistory.Add(cleanFile);
}
public void RemoveProjectHistory(string file)
{
var cleanFile = file.Trim();
ProjectHistory.Remove(cleanFile);
}
public void Clear()
{
ProjectHistory.Clear();
StartupProject = null;
Save();
}
public void Save()
{
if (!_isoStoreInit)
return;
try
{
var mode = FileMode.CreateNew;
if (_isoStore.FileExists(SettingsPath))
mode = FileMode.Truncate;
using (var isoStream = new IsolatedStorageFileStream(SettingsPath, mode, _isoStore))
{
using (var writer = new StreamWriter(isoStream))
{
var serializer = new XmlSerializer(typeof(PipelineSettings));
serializer.Serialize(writer, this);
}
}
} catch { }
}
public void Load()
{
if (!_isoStoreInit)
return;
try
{
if (_isoStore.FileExists(SettingsPath))
{
using (var isoStream = new IsolatedStorageFileStream(SettingsPath, FileMode.Open, _isoStore))
{
using (var reader = new StreamReader(isoStream))
{
var serializer = new XmlSerializer(typeof(PipelineSettings));
Default = (PipelineSettings)serializer.Deserialize(reader);
var history = Default.ProjectHistory.ToArray();
foreach (var h in history)
if (!File.Exists(h))
Default.ProjectHistory.Remove(h);
}
}
}
}
catch
{
Save();
}
}
}
}
@@ -0,0 +1,548 @@
// 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;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Content.Pipeline.Builder.Convertors;
using Microsoft.Xna.Framework.Graphics;
using System.Diagnostics;
using MonoGame.Framework.Content.Pipeline.Builder;
namespace MonoGame.Tools.Pipeline
{
public class ImporterTypeDescription
{
public string TypeName;
public string DisplayName;
public string DefaultProcessor;
public IEnumerable<string> FileExtensions;
public Type OutputType;
public ImporterTypeDescription()
{
TypeName = "Invalid / Missing Importer";
}
public override string ToString()
{
return TypeName;
}
public override int GetHashCode()
{
return TypeName == null ? 0 : TypeName.GetHashCode();
}
public override bool Equals(object obj)
{
var other = obj as ImporterTypeDescription;
if (other == null)
return false;
if (string.IsNullOrEmpty(other.TypeName) != string.IsNullOrEmpty(TypeName))
return false;
return TypeName.Equals(other.TypeName);
}
};
public class ProcessorTypeDescription
{
#region Supporting Types
public struct Property
{
public string Name;
public string DisplayName;
public Type Type;
public object DefaultValue;
public bool Browsable;
public override string ToString()
{
return Name;
}
}
public class ProcessorPropertyCollection : IEnumerable<Property>
{
private readonly Property[] _properties;
public ProcessorPropertyCollection(IEnumerable<Property> properties)
{
_properties = properties.ToArray();
}
public Property this[int index]
{
get
{
return _properties[index];
}
set
{
_properties[index] = value;
}
}
public Property this[string name]
{
get
{
foreach (var p in _properties)
{
if (p.Name.Equals(name))
return p;
}
throw new IndexOutOfRangeException();
}
set
{
for (var i = 0; i < _properties.Length; i++)
{
var p = _properties[i];
if (p.Name.Equals(name))
{
_properties[i] = value;
return;
}
}
throw new IndexOutOfRangeException();
}
}
public bool Contains(string name)
{
return _properties.Any(e => e.Name == name);
}
public IEnumerator<Property> GetEnumerator()
{
return _properties.AsEnumerable().GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _properties.GetEnumerator();
}
}
#endregion
public string TypeName;
public string DisplayName;
public ProcessorPropertyCollection Properties;
public Type InputType;
public override string ToString()
{
return TypeName;
}
};
internal class PipelineTypes
{
[DebuggerDisplay("ImporterInfo: {Type.Name}")]
private struct ImporterInfo
{
public ContentImporterAttribute Attribute;
public Type Type;
}
[DebuggerDisplay("ProcessorInfo: {Type.Name}")]
private struct ProcessorInfo
{
public ContentProcessorAttribute Attribute;
public Type Type;
}
private static List<ImporterInfo> _importers;
private static List<ProcessorInfo> _processors;
private static List<FileSystemWatcher> _watchers;
private static string _currentAssemblyDirectory;
public static ImporterTypeDescription[] Importers { get; private set; }
public static ProcessorTypeDescription[] Processors { get; private set; }
public static ImporterTypeDescription NullImporter { get; private set; }
public static ProcessorTypeDescription NullProcessor { get; private set; }
public static ImporterTypeDescription MissingImporter { get; private set; }
public static ProcessorTypeDescription MissingProcessor { get; private set; }
public static TypeConverter.StandardValuesCollection ImportersStandardValuesCollection { get; private set; }
public static TypeConverter.StandardValuesCollection ProcessorsStandardValuesCollection { get; private set; }
private static readonly Dictionary<string, string> _oldNameRemap = new Dictionary<string, string>()
{
{ "MGMaterialProcessor", "MaterialProcessor" },
{ "MGSongProcessor", "SongProcessor" },
{ "MGSoundEffectProcessor", "SoundEffectProcessor" },
{ "MGSpriteFontDescriptionProcessor", "FontDescriptionProcessor" },
{ "MGSpriteFontTextureProcessor", "FontTextureProcessor" },
{ "MGTextureProcessor", "TextureProcessor" },
{ "MGEffectProcessor", "EffectProcessor" },
};
private static string RemapOldNames(string name)
{
if (_oldNameRemap.ContainsKey(name))
return _oldNameRemap[name];
return name;
}
static PipelineTypes()
{
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
MissingImporter = new ImporterTypeDescription()
{
DisplayName = "Invalid / Missing Importer",
};
MissingProcessor = new ProcessorTypeDescription()
{
DisplayName = "Invalid / Missing Processor",
Properties = new ProcessorTypeDescription.ProcessorPropertyCollection(new ProcessorTypeDescription.Property[0]),
};
NullImporter = new ImporterTypeDescription()
{
DisplayName = "",
};
NullProcessor = new ProcessorTypeDescription()
{
DisplayName = "",
Properties = new ProcessorTypeDescription.ProcessorPropertyCollection(new ProcessorTypeDescription.Property[0]),
};
_watchers = new List<FileSystemWatcher>();
}
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
if (string.IsNullOrEmpty(_currentAssemblyDirectory))
return null;
var path = Path.Combine(_currentAssemblyDirectory, (new AssemblyName(args.Name).Name) + ".dll");
if (!File.Exists(path))
return null;
return Assembly.Load(File.ReadAllBytes(path));
}
public static void Load(PipelineProject project)
{
Unload();
var assemblyPaths = new List<string>();
var projectRoot = project.Location;
foreach (var i in project.References)
{
var path = Path.Combine(projectRoot, i);
if (string.IsNullOrEmpty(path))
throw new ArgumentException("assemblyFilePath cannot be null!");
if (!Path.IsPathRooted(path))
throw new ArgumentException("assemblyFilePath must be absolute!");
// Make sure we're not adding the same assembly twice.
path = PathHelper.Normalize(path);
if (!assemblyPaths.Contains(path))
assemblyPaths.Add(path);
}
ResolveAssemblies(assemblyPaths);
var importerDescriptions = new ImporterTypeDescription[_importers.Count];
var cur = 0;
foreach (var item in _importers)
{
// Find the abstract base class ContentImporter<T>.
var baseType = item.Type.BaseType;
while (!baseType.IsAbstract)
baseType = baseType.BaseType;
var outputType = baseType.GetGenericArguments()[0];
var name = item.Attribute.DisplayName;
if (string.IsNullOrEmpty(name))
name = item.GetType().Name;
var desc = new ImporterTypeDescription()
{
TypeName = item.Type.Name,
DisplayName = name,
DefaultProcessor = item.Attribute.DefaultProcessor,
FileExtensions = item.Attribute.FileExtensions,
OutputType = outputType,
};
importerDescriptions[cur] = desc;
cur++;
}
Importers = importerDescriptions;
ImportersStandardValuesCollection = new TypeConverter.StandardValuesCollection(Importers);
var processorDescriptions = new ProcessorTypeDescription[_processors.Count];
const BindingFlags bindings = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
cur = 0;
foreach (var item in _processors)
{
var obj = Activator.CreateInstance(item.Type);
var typeProperties = item.Type.GetProperties(bindings);
var properties = new List<ProcessorTypeDescription.Property>();
foreach (var i in typeProperties)
{
var attrs = i.GetCustomAttributes(true);
var name = i.Name;
var browsable = true;
var defvalue = i.GetValue(obj, null);
foreach (var a in attrs)
{
if (a is BrowsableAttribute)
browsable = (a as BrowsableAttribute).Browsable;
else if (a is DisplayNameAttribute)
name = (a as DisplayNameAttribute).DisplayName;
}
var p = new ProcessorTypeDescription.Property()
{
Name = i.Name,
DisplayName = name,
Type = i.PropertyType,
DefaultValue = defvalue,
Browsable = browsable
};
properties.Add(p);
}
var inputType = (obj as IContentProcessor).InputType;
var desc = new ProcessorTypeDescription()
{
TypeName = item.Type.Name,
DisplayName = item.Attribute.DisplayName,
Properties = new ProcessorTypeDescription.ProcessorPropertyCollection(properties),
InputType = inputType,
};
if (string.IsNullOrEmpty(desc.DisplayName))
desc.DisplayName = desc.TypeName;
processorDescriptions[cur] = desc;
cur++;
}
Processors = processorDescriptions;
ProcessorsStandardValuesCollection = new TypeConverter.StandardValuesCollection(Processors);
}
public static void Unload()
{
foreach (var watch in _watchers)
watch.Dispose();
_watchers.Clear();
_importers = null;
Importers = null;
_processors = null;
Processors = null;
ImportersStandardValuesCollection = null;
ProcessorsStandardValuesCollection = null;
}
public static TypeConverter FindConverter(Type type)
{
if (type == typeof(Color))
return new StringToColorConverter();
return TypeDescriptor.GetConverter(type);
}
public static ImporterTypeDescription FindImporter(string name, string fileExtension)
{
if (!string.IsNullOrEmpty(name))
{
name = RemapOldNames(name);
foreach (var i in Importers)
{
if (i.TypeName.Equals(name))
return i;
}
foreach (var i in Importers)
{
if (i.DisplayName.Equals(name))
return i;
}
//Debug.Fail(string.Format("Importer not found! name={0}, ext={1}", name, fileExtension));
return null;
}
var lowerFileExt = fileExtension.ToLowerInvariant();
foreach (var i in Importers)
{
if (i.FileExtensions.Any(e => e.ToLowerInvariant() == lowerFileExt))
return i;
}
//Debug.Fail(string.Format("Importer not found! name={0}, ext={1}", name, fileExtension));
return null;
}
public static ProcessorTypeDescription FindProcessor(string name, ImporterTypeDescription importer)
{
if (!string.IsNullOrEmpty(name))
{
name = RemapOldNames(name);
foreach (var i in Processors)
{
if (i.TypeName.Equals(name))
return i;
}
//Debug.Fail(string.Format("Processor not found! name={0}, importer={1}", name, importer));
return null;
}
if (importer != null)
{
foreach (var i in Processors)
{
if (i.TypeName.Equals(importer.DefaultProcessor))
return i;
}
}
//Debug.Fail(string.Format("Processor not found! name={0}, importer={1}", name, importer));
return null;
}
private static void ResolveAssemblies(IEnumerable<string> assemblyPaths)
{
_importers = new List<ImporterInfo>();
_processors = new List<ProcessorInfo>();
var assemblies = new List<Assembly>(AppDomain.CurrentDomain.GetAssemblies());
foreach (var asm in assemblies)
{
#if SHIPPING
try
#endif
{
if (!asm.ToString().Contains("MonoGame"))
continue;
var types = asm.GetTypes();
ProcessTypes(types);
}
#if SHIPPING
catch (Exception e)
{
// ??
}
#endif
}
foreach (var watch in _watchers)
watch.Dispose();
_watchers.Clear();
foreach (var path in assemblyPaths)
{
try
{
_currentAssemblyDirectory = Path.GetDirectoryName(path);
var a = Assembly.Load(File.ReadAllBytes(path));
var types = a.GetTypes();
ProcessTypes(types);
var watch = new FileSystemWatcher();
watch.Path = Path.GetDirectoryName(path);
watch.EnableRaisingEvents = true;
watch.Filter = Path.GetFileName(path);
watch.Changed += (sender, e) =>
{
if (Path.GetFileName(path) == e.Name)
PipelineController.Instance.OnReferencesModified();
};
watch.Created += (sender, e) =>
{
if (Path.GetFileName(path) == e.Name)
PipelineController.Instance.OnReferencesModified();
};
_watchers.Add(watch);
}
catch
{
//Logger.LogWarning(null, null, "Failed to load assembly '{0}': {1}", assemblyPath, e.Message);
// The assembly failed to load... nothing
// we can do but ignore it.
continue;
}
}
_currentAssemblyDirectory = null;
}
private static void ProcessTypes(IEnumerable<Type> types)
{
foreach (var t in types)
{
if (t.IsAbstract)
continue;
if (t.GetInterface(@"IContentImporter") == typeof(IContentImporter))
{
var attributes = t.GetCustomAttributes(typeof(ContentImporterAttribute), false);
if (attributes.Length != 0)
{
var importerAttribute = attributes[0] as ContentImporterAttribute;
_importers.Add(new ImporterInfo { Attribute = importerAttribute, Type = t });
}
else
{
// If no attribute specify default one
var importerAttribute = new ContentImporterAttribute(".*");
importerAttribute.DefaultProcessor = "";
importerAttribute.DisplayName = t.Name;
_importers.Add(new ImporterInfo { Attribute = importerAttribute, Type = t });
}
}
else if (t.GetInterface(@"IContentProcessor") == typeof(IContentProcessor))
{
var attributes = t.GetCustomAttributes(typeof(ContentProcessorAttribute), false);
if (attributes.Length != 0)
{
var processorAttribute = attributes[0] as ContentProcessorAttribute;
_processors.Add(new ProcessorInfo { Attribute = processorAttribute, Type = t });
}
}
}
}
}
}
@@ -0,0 +1,56 @@
// 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.Collections.Generic;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Graphics;
namespace MonoGame.Tools.Pipeline
{
/// <summary>
/// Snapshot of a PipelineProject's state, used for undo/redo.
/// </summary>
internal class ProjectState
{
public string OutputDir;
public string IntermediateDir;
public List<string> References;
public TargetPlatform Platform;
public GraphicsProfile Profile;
public string Config;
public string OriginalPath;
/// <summary>
/// Create a ProjectState storing member values of the passed PipelineProject.
/// </summary>
public static ProjectState Get(PipelineProject proj)
{
var state = new ProjectState()
{
OriginalPath = proj.OriginalPath,
OutputDir = proj.OutputDir,
IntermediateDir = proj.IntermediateDir,
References = new List<string>(proj.References),
Platform = proj.Platform,
Profile = proj.Profile,
Config = proj.Config,
};
return state;
}
/// <summary>
/// Set a PipelineProject's member values from this state object.
/// </summary>
public void Apply(PipelineProject proj)
{
proj.OutputDir = OutputDir;
proj.IntermediateDir = IntermediateDir;
proj.References = new List<string>(References);
proj.Platform = Platform;
proj.Profile = Profile;
proj.Config = Config;
}
}
}
@@ -0,0 +1,26 @@
using System;
using System.Security;
namespace MonoGame.Tools.Pipeline
{
public static class StringExtensions
{
public static string Unescape(this string text)
{
if (string.IsNullOrEmpty(text))
return text;
var result = Uri.UnescapeDataString(text);
// JCF: XmlReader already does this.
/*
result = result.Replace("&apos;", "'");
result = result.Replace("&quot;", "\"");
result = result.Replace("&gt;", ">");
result = result.Replace("&amp;", "&");
*/
return result;
}
}
}
@@ -0,0 +1,50 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace MonoGame.Tools.Pipeline
{
public static class Util
{
[DllImport("libc")]
private static extern string realpath(string path, IntPtr resolved_path);
public static string GetRealPath(string path)
{
// resolve symlinks on Unix systems
if (Environment.OSVersion.Platform == PlatformID.Unix)
return realpath(path, IntPtr.Zero);
return path;
}
/// <summary>
/// Returns the path 'filspec' made relative path 'folder'.
///
/// If 'folder' is not an absolute path, throws ArgumentException.
/// If 'filespec' is not an absolute path, returns 'filespec' unmodified.
/// </summary>
public static string GetRelativePath(string filespec, string folder)
{
if (!Path.IsPathRooted(filespec))
return filespec;
if (!Path.IsPathRooted(folder))
throw new ArgumentException("Must be an absolute path.", "folder");
filespec = Path.GetFullPath(filespec).TrimEnd(new[] { '/', '\\' });
folder = Path.GetFullPath(folder).TrimEnd(new[] { '/', '\\' });
if (filespec == folder)
return string.Empty;
var pathUri = new Uri(filespec);
var folderUri = new Uri(folder + Path.DirectorySeparatorChar);
var result = folderUri.MakeRelativeUri(pathUri).ToString();
result = result.Replace('/', Path.DirectorySeparatorChar);
result = Uri.UnescapeDataString(result);
return result;
}
}
}