summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <github@jplamondonw.com>2016-11-22 00:36:48 -0500
committerJesse Plamondon-Willard <github@jplamondonw.com>2016-11-26 13:14:26 -0500
commite9fee3f6fe18927192b1dd676cd420507af2e389 (patch)
tree943dd5fc7e8c27044350d0e00749bffdd87c112a /src
parent08d5ee293ffc001dff3e866bd45954ef306e81ac (diff)
downloadSMAPI-e9fee3f6fe18927192b1dd676cd420507af2e389.tar.gz
SMAPI-e9fee3f6fe18927192b1dd676cd420507af2e389.tar.bz2
SMAPI-e9fee3f6fe18927192b1dd676cd420507af2e389.zip
preprocess all mod assemblies for compatibility with multi-assembly mods (#166)
Diffstat (limited to 'src')
-rw-r--r--src/StardewModdingAPI/Framework/ModAssemblyLoader.cs82
-rw-r--r--src/StardewModdingAPI/Program.cs34
2 files changed, 94 insertions, 22 deletions
diff --git a/src/StardewModdingAPI/Framework/ModAssemblyLoader.cs b/src/StardewModdingAPI/Framework/ModAssemblyLoader.cs
index 2615e1f7..6c0f0cdf 100644
--- a/src/StardewModdingAPI/Framework/ModAssemblyLoader.cs
+++ b/src/StardewModdingAPI/Framework/ModAssemblyLoader.cs
@@ -1,4 +1,5 @@
-using System.IO;
+using System;
+using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
@@ -6,7 +7,7 @@ using Mono.Cecil;
namespace StardewModdingAPI.Framework
{
- /// <summary>Loads mod assemblies.</summary>
+ /// <summary>Preprocesses and loads mod assemblies.</summary>
internal class ModAssemblyLoader
{
/*********
@@ -26,19 +27,17 @@ namespace StardewModdingAPI.Framework
this.CacheDirPath = cacheDirPath;
}
- /// <summary>Read an assembly from the given path.</summary>
+ /// <summary>Preprocess an assembly and cache the modified version.</summary>
/// <param name="assemblyPath">The assembly file path.</param>
- public Assembly ProcessAssembly(string assemblyPath)
+ public void ProcessAssembly(string assemblyPath)
{
// read assembly data
byte[] assemblyBytes = File.ReadAllBytes(assemblyPath);
byte[] hash = MD5.Create().ComputeHash(assemblyBytes);
- // get cache data
- string key = Path.GetFileNameWithoutExtension(assemblyPath);
- string cachePath = Path.Combine(this.CacheDirPath, $"{key}.dll");
- string cacheHashPath = Path.Combine(this.CacheDirPath, $"{key}-hash.txt");
- bool canUseCache = File.Exists(cachePath) && File.Exists(cacheHashPath) && hash.SequenceEqual(File.ReadAllBytes(cacheHashPath));
+ // check cache
+ CachePaths cachePaths = this.GetCacheInfo(assemblyPath);
+ bool canUseCache = File.Exists(cachePaths.Assembly) && File.Exists(cachePaths.Hash) && hash.SequenceEqual(File.ReadAllBytes(cachePaths.Hash));
// process assembly if not cached
if (!canUseCache)
@@ -53,13 +52,70 @@ namespace StardewModdingAPI.Framework
{
definition.Write(outStream);
byte[] outBytes = outStream.ToArray();
- File.WriteAllBytes(cachePath, outBytes);
- File.WriteAllBytes(cacheHashPath, hash);
+ Directory.CreateDirectory(cachePaths.Directory);
+ File.WriteAllBytes(cachePaths.Assembly, outBytes);
+ File.WriteAllBytes(cachePaths.Hash, hash);
}
}
+ }
+
+ /// <summary>Load a preprocessed assembly.</summary>
+ /// <param name="assemblyPath">The assembly file path.</param>
+ public Assembly LoadCachedAssembly(string assemblyPath)
+ {
+ CachePaths cachePaths = this.GetCacheInfo(assemblyPath);
+ if (!File.Exists(cachePaths.Assembly))
+ throw new InvalidOperationException($"The assembly {assemblyPath} doesn't exist in the preprocessed cache.");
+ return Assembly.UnsafeLoadFrom(cachePaths.Assembly);
+ }
- // load assembly
- return Assembly.UnsafeLoadFrom(cachePath);
+
+ /*********
+ ** Private methods
+ *********/
+ /// <summary>Get the cache details for an assembly.</summary>
+ /// <param name="assemblyPath">The assembly file path.</param>
+ private CachePaths GetCacheInfo(string assemblyPath)
+ {
+ string key = Path.GetFileNameWithoutExtension(assemblyPath);
+ string dirPath = Path.Combine(this.CacheDirPath, new DirectoryInfo(Path.GetDirectoryName(assemblyPath)).Name);
+ string cacheAssemblyPath = Path.Combine(dirPath, $"{key}.dll");
+ string cacheHashPath = Path.Combine(dirPath, $"{key}.hash");
+ return new CachePaths(dirPath, cacheAssemblyPath, cacheHashPath);
+ }
+
+ /*********
+ ** Private objects
+ *********/
+ /// <summary>Contains the paths for an assembly's cached data.</summary>
+ private struct CachePaths
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>The directory path which contains the assembly.</summary>
+ public string Directory { get; }
+
+ /// <summary>The file path of the assembly file.</summary>
+ public string Assembly { get; }
+
+ /// <summary>The file path containing the MD5 hash for the assembly.</summary>
+ public string Hash { get; }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="directory">The directory path which contains the assembly.</param>
+ /// <param name="assembly">The file path of the assembly file.</param>
+ /// <param name="hash">The file path containing the MD5 hash for the assembly.</param>
+ public CachePaths(string directory, string assembly, string hash)
+ {
+ this.Directory = directory;
+ this.Assembly = assembly;
+ this.Hash = hash;
+ }
}
}
}
diff --git a/src/StardewModdingAPI/Program.cs b/src/StardewModdingAPI/Program.cs
index 255ad365..f8920896 100644
--- a/src/StardewModdingAPI/Program.cs
+++ b/src/StardewModdingAPI/Program.cs
@@ -301,6 +301,10 @@ namespace StardewModdingAPI
ModAssemblyLoader modAssemblyLoader = new ModAssemblyLoader(Program.CachePath);
foreach (string directory in Directory.GetDirectories(Program.ModPath))
{
+ // ignore internal directory
+ if (new DirectoryInfo(directory).Name == ".cache")
+ continue;
+
// check for cancellation
if (Program.CancellationTokenSource.IsCancellationRequested)
{
@@ -388,20 +392,32 @@ namespace StardewModdingAPI
}
}
+ // preprocess mod assemblies
+ {
+ bool succeeded = true;
+ foreach (string assemblyPath in Directory.GetFiles(directory, "*.dll"))
+ {
+ try
+ {
+ modAssemblyLoader.ProcessAssembly(assemblyPath);
+ }
+ catch (Exception ex)
+ {
+ Program.Monitor.Log($"{errorPrefix}: an error occurred while preprocessing '{assemblyPath}'.\n{ex}", LogLevel.Error);
+ succeeded = false;
+ break;
+ }
+ }
+ if (!succeeded)
+ continue;
+ }
+
// load assembly
Assembly modAssembly;
try
{
- // get assembly path
string assemblyPath = Path.Combine(directory, manifest.EntryDll);
- if (!File.Exists(assemblyPath))
- {
- Program.Monitor.Log($"{errorPrefix}: target DLL '{assemblyPath}' does not exist.", LogLevel.Error);
- continue;
- }
-
- // read assembly
- modAssembly = modAssemblyLoader.ProcessAssembly(assemblyPath);
+ modAssembly = modAssemblyLoader.LoadCachedAssembly(assemblyPath);
if (modAssembly.DefinedTypes.Count(x => x.BaseType == typeof(Mod)) == 0)
{
Program.Monitor.Log($"{errorPrefix}: the mod DLL does not contain an implementation of the 'Mod' class.", LogLevel.Error);