Files
crtr/Assets/Cryville/Crtr/Browsing/ExtensionManager.cs
2023-03-26 23:25:20 +08:00

139 lines
4.5 KiB
C#

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<string, List<ResourceConverter>> _converters
= new Dictionary<string, List<ResourceConverter>>();
public static IEnumerable<string> GetSupportedFormats() {
return _converters.Keys;
}
public static bool TryGetConverters(string extension, out IEnumerable<ResourceConverter> converters) {
List<ResourceConverter> outResult;
bool result = _converters.TryGetValue(extension, out outResult);
converters = outResult;
return result;
}
static readonly Dictionary<string, string> _localRes
= new Dictionary<string, string>();
public static IReadOnlyDictionary<string, string> 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<ModuleItem>();
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<string>();
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<ResourceConverter> { 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);
}
}
}
}