using Cryville.Common; using Cryville.Crtr.Extension; using Mono.Cecil; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; namespace Cryville.Crtr.Browsing { internal static class ExtensionManager { static readonly Dictionary> _converters = new Dictionary>(); public static IEnumerable GetSupportedFormats() { return _converters.Keys; } public static bool TryGetConverters(string extension, out IEnumerable converters) { List outResult; bool result = _converters.TryGetValue(extension, out outResult); converters = outResult; return result; } static readonly Dictionary _localRes = new Dictionary(); public static IReadOnlyDictionary GetLocalResourcePaths() { return _localRes; } public static void Init(string rootPath) { LoadExtension(typeof(Extensions.Umg.Extension)); var asms = AppDomain.CurrentDomain.GetAssemblies().Select(a => a.GetName().Name).ToHashSet(); var modules = new Queue(); var extensionDir = new DirectoryInfo(Path.Combine(rootPath, "extensions")); if (extensionDir.Exists) { foreach (var extension in extensionDir.EnumerateFiles("*.dll")) { try { modules.Enqueue(new ModuleItem(extension.OpenRead())); } catch (Exception ex) { Logger.Log("main", 4, "Extension", "Failed to load DLL {0}: {1}", extension, ex); } } } int refCounter = 0; while (modules.Count > 0 && refCounter < modules.Count) { var module = modules.Dequeue(); bool flag = false; foreach (var reference in module.Definition.AssemblyReferences) { if (!asms.Contains(reference.Name)) { flag = true; break; } } if (flag) { modules.Enqueue(module); refCounter++; } else { try { var stream = module.Stream; stream.Seek(0, SeekOrigin.Begin); var buf = new byte[stream.Length]; stream.Read(buf, 0, buf.Length); var asm = Assembly.Load(buf); if (asm == null) throw new TypeLoadException("Failed to load the module"); asms.Add(asm.GetName().Name); foreach (var type in asm.GetTypes()) { if (typeof(ExtensionInterface).IsAssignableFrom(type)) { LoadExtension(type); } } Logger.Log("main", 1, "Extension", "Loaded module {0}", module.Definition.Name); } catch (Exception ex) { Logger.Log("main", 4, "Extension", "An error occured while trying to load module {0}: {1}", module.Definition.Name, ex); } finally { module.Definition.Dispose(); module.Stream.Dispose(); refCounter = 0; } } } var missingList = new List(); while (modules.Count > 0) { missingList.Clear(); var module = modules.Dequeue(); foreach (var reference in module.Definition.AssemblyReferences) { if (!asms.Contains(reference.Name)) { missingList.Add(reference.Name); } } Logger.Log("main", 4, "Extension", "Could not load the module {0} because the following dependencies were missing: {1}", module.Definition.Name, missingList.Aggregate((current, next) => current + ", " + next)); module.Definition.Dispose(); module.Stream.Dispose(); } } struct ModuleItem { public ModuleDefinition Definition { get; set; } public FileStream Stream { get; set; } public ModuleItem(FileStream stream) { Stream = stream; Definition = ModuleDefinition.ReadModule(stream); } } static void LoadExtension(Type type) { try { var extension = (ExtensionInterface)Activator.CreateInstance(type); var l1 = extension.GetResourceConverters(); if (l1 != null) { foreach (var c in l1) { var fs = c.GetSupportedFormats(); if (fs == null) continue; foreach (var f in fs) { if (f == null) continue; if (!_converters.ContainsKey(f)) _converters.Add(f, new List { c }); else _converters[f].Add(c); } } } var l2 = extension.GetResourceFinders(); if (l2 != null) { foreach (var f in l2) { var name = f.Name; var path = f.GetRootPath(); if (name != null && path != null) _localRes.Add(name, path); } } Logger.Log("main", 1, "Extension", "Loaded extension {0}", type); } catch (Exception ex) { Logger.Log("main", 4, "Extension", "Failed to load extension {0}: {1}", type, ex); } } } }