-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathCodeMod.cs
More file actions
92 lines (78 loc) · 3.2 KB
/
CodeMod.cs
File metadata and controls
92 lines (78 loc) · 3.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
using System.Reflection;
using Common;
using FezEngine.Tools;
using HatModLoader.Source.AssemblyResolving;
using HatModLoader.Source.FileProxies;
using Microsoft.Xna.Framework;
namespace HatModLoader.Source.ModDefinition
{
public class CodeMod
{
public byte[] RawAssembly { get; }
public Assembly Assembly { get; private set; }
public List<GameComponent> Components { get; private set; }
private CodeMod(byte[] rawAssembly)
{
RawAssembly = rawAssembly;
}
public void Initialize(Game game, string entrypoint)
{
if (RawAssembly == null || RawAssembly.Length < 1)
{
throw new ArgumentNullException(nameof(RawAssembly), "There's no raw assembly data.");
}
if (Assembly != null)
{
throw new InvalidOperationException("Assembly is already loaded.");
}
Assembly = Assembly.Load(RawAssembly);
Components = [];
Type[] types;
if (!string.IsNullOrEmpty(entrypoint))
{
if (!Assembly.GetTypes().Any(t => t.FullName?.Equals(entrypoint) ?? false))
{
throw new ArgumentException($"The entrypoint name is not a fully qualified name: {entrypoint}");
}
// Entrypoint class may load other components (services) via Game.Components (Game.Services)
Logger.Log("HAT", LogSeverity.Information, $"Starting at entrypoint {entrypoint}.");
types = [Assembly.GetType(entrypoint)];
}
else
{
// Use backward compatible method
Logger.Log("HAT", LogSeverity.Warning, "No entrypoint was specified. Loading all public components...");
types = Assembly.GetExportedTypes();
}
foreach (var type in types)
{
if (typeof(GameComponent).IsAssignableFrom(type) && type.IsPublic && !type.IsAbstract)
{
// NOTE: The constructor accepting the type (Game) is defined in GameComponent
var gameComponent = (GameComponent)Activator.CreateInstance(type, [game]);
Components.Add(gameComponent);
}
}
}
public static bool TryLoad(IFileProxy proxy, Metadata metadata, out CodeMod codeMod)
{
if (string.IsNullOrEmpty(metadata.LibraryName) ||
!metadata.LibraryName.EndsWith(".dll", StringComparison.InvariantCultureIgnoreCase) ||
!proxy.FileExists(metadata.LibraryName))
{
codeMod = null;
return false;
}
using var assemblyStream = proxy.OpenFile(metadata.LibraryName);
var rawAssembly = new byte[assemblyStream.Length];
var count = assemblyStream.Read(rawAssembly, 0, rawAssembly.Length);
if (rawAssembly.Length != count)
{
codeMod = null;
return false;
}
codeMod = new CodeMod(rawAssembly);
return true;
}
}
}