UltimateZoneBuilder/Source/Core/Plugins/Plugin.cs
MaxED a1c290d49c Fixed a crash when trying to parse an empty TEXTURES lump (probably covers some other empty text lumps as well).
Added strict revision checking to BuilderModes.dll (this plugin must always stay in sync with the core).
2016-01-20 17:54:56 +00:00

240 lines
7.4 KiB
C#

#region ================== Copyright (c) 2007 Pascal vd Heiden
/*
* Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com
* This program is released under GNU General Public License
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#endregion
#region ================== Namespaces
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
#endregion
namespace CodeImp.DoomBuilder.Plugins
{
internal class Plugin : IDisposable
{
#region ================== Constants
#endregion
#region ================== Variables
// The plugin assembly
private Assembly asm;
// The plug
private Plug plug;
// Unique name used to refer to this assembly
private readonly string name;
// Disposing
private bool isdisposed;
#endregion
#region ================== Properties
public Assembly Assembly { get { return asm; } }
public Plug Plug { get { return plug; } }
public string Name { get { return name; } }
public bool IsDisposed { get { return isdisposed; } }
#endregion
#region ================== Constructor / Disposer
// Constructor
public Plugin(string filename)
{
// Initialize
string shortfilename = Path.GetFileName(filename);
name = Path.GetFileNameWithoutExtension(filename);
General.WriteLogLine("Loading plugin '" + name + "' from '" + shortfilename + "'...");
try
{
// Load assembly
asm = Assembly.LoadFrom(filename);
}
catch(Exception)
{
General.ErrorLogger.Add(ErrorType.Error, "Could not load plugin \"" + shortfilename + "\", the DLL file could not be read. This file is not supposed to be in the Plugins subdirectory.");
throw new InvalidProgramException();
}
// Find the class that inherits from Plugin
Type t = FindSingleClass(typeof(Plug));
if(t != null)
{
// Are the multiple plug classes?
if(FindClasses(typeof(Plug)).Length > 1)
{
// Show a warning
General.ErrorLogger.Add(ErrorType.Warning, "Plugin \"" + shortfilename + "\" has more than one Plug class. The following class is used to create in instance: " + t.FullName);
}
// Make plug instance
plug = CreateObject<Plug>(t);
plug.Plugin = this;
// Verify revision numbers
int thisrevision = General.ThisAssembly.GetName().Version.Revision;
//mxd. Revision numbers should match?
if(plug.StrictRevisionMatching && plug.MinimumRevision != thisrevision)
{
string message = shortfilename + " plugin's assembly version (" + plug.MinimumRevision + ") doesn't match main module version (" + thisrevision + ").";
if(General.ShowWarningMessage(message + Environment.NewLine +
"It's strongly recomended to update the editor." + Environment.NewLine +
"Program stability is not guaranteed." + Environment.NewLine + Environment.NewLine +
"Continue anyway?", MessageBoxButtons.YesNo, MessageBoxDefaultButton.Button2, false) == DialogResult.No)
{
General.WriteLogLine("Quiting on " + shortfilename + " module version mismatch");
General.Exit(General.Map != null);
return;
}
else
{
General.ErrorLogger.Add(ErrorType.Warning, message);
throw new InvalidProgramException();
}
}
// Verify minimum revision number
if((thisrevision != 0) && (plug.MinimumRevision > thisrevision))
{
// Can't load this plugin because it is meant for a newer version
General.ErrorLogger.Add(ErrorType.Error, "Could not load plugin \"" + shortfilename + "\", the Plugin is made for GZDoom Builder R" + plug.MinimumRevision + " or newer and you are running R" + thisrevision + ".");
throw new InvalidProgramException();
}
}
else
{
// How can we plug something in without a plug?
General.ErrorLogger.Add(ErrorType.Error, "Could not load plugin \"" + shortfilename + "\", plugin is missing the Plug class. This file is not supposed to be in the Plugins subdirectory.");
throw new InvalidProgramException();
}
// We have no destructor
GC.SuppressFinalize(this);
}
// Disposer
public void Dispose()
{
// Not already disposed?
if(!isdisposed)
{
// Clean up
plug.Dispose(); //mxd
plug = null; //mxd
asm = null;
// Done
isdisposed = true;
}
}
#endregion
#region ================== Methods
// This creates a stream to read a resource or returns null when not found
public Stream GetResourceStream(string resourcename)
{
// Find a resource
resourcename = "." + resourcename; //mxd. Otherwise, we can get Properties.Resources.SuperCoolMode.png while searching for CoolMode.png
string[] resnames = asm.GetManifestResourceNames();
foreach(string rn in resnames)
{
// Found it?
if(rn.EndsWith(resourcename, StringComparison.InvariantCultureIgnoreCase))
{
// Get a stream from the resource
return asm.GetManifestResourceStream(rn);
}
}
// Nothing found
return null;
}
// This finds all class types that inherits from the given type
public Type[] FindClasses(Type t)
{
List<Type> found = new List<Type>();
// Get all exported types
Type[] types = asm.GetExportedTypes();
foreach(Type it in types)
{
// Compare types
if(t.IsAssignableFrom(it)) found.Add(it);
}
// Return list
return found.ToArray();
}
// This finds a single class type that inherits from the given type
// Returns null when no valid type was found
public Type FindSingleClass(Type t)
{
Type[] types = FindClasses(t);
return (types.Length > 0 ? types[0] : null);
}
// This creates an instance of a class
public T CreateObject<T>(Type t, params object[] args)
{
return CreateObjectA<T>(t, args);
}
// This creates an instance of a class
public T CreateObjectA<T>(Type t, object[] args)
{
try
{
// Create instance
return (T)asm.CreateInstance(t.FullName, false, BindingFlags.Default, null, args, CultureInfo.CurrentCulture, new object[0]);
}
catch(TargetInvocationException e)
{
// Error!
string error = "Failed to create class instance '" + t.Name + "' from plugin '" + name + "'.";
General.ShowErrorMessage(error + Environment.NewLine + Environment.NewLine + "See the error log for more details", MessageBoxButtons.OK, false);
General.WriteLogLine(error + " " + e.InnerException.GetType().Name + " at target: "
+ e.InnerException.Message + Environment.NewLine + "Stacktrace: " + e.InnerException.StackTrace.Trim());
return default(T);
}
catch(Exception e)
{
// Error!
string error = "Failed to create class instance '" + t.Name + "' from plugin '" + name + "'.";
General.ShowErrorMessage(error + Environment.NewLine + Environment.NewLine + "See the error log for more details", MessageBoxButtons.OK, false);
General.WriteLogLine(error + " " + e.GetType().Name + ": " + e.Message + Environment.NewLine
+ "Stacktrace: " + e.StackTrace.Trim());
return default(T);
}
}
#endregion
}
}