diff --git a/OpenRA.Game/FileSystem/FileSystem.cs b/OpenRA.Game/FileSystem/FileSystem.cs
index f34f1534fa..11b3baae10 100644
--- a/OpenRA.Game/FileSystem/FileSystem.cs
+++ b/OpenRA.Game/FileSystem/FileSystem.cs
@@ -297,5 +297,40 @@ namespace OpenRA.FileSystem
return fileIndex.ContainsKey(filename);
}
+
+ ///
+ /// Resolves a filesystem for an assembly, accounting for explicit and mod mounts.
+ /// Assemblies must exist in the native OS file system (not inside an OpenRA-defined package).
+ ///
+ public static string ResolveAssemblyPath(string path, Manifest manifest, InstalledMods installedMods)
+ {
+ var explicitSplit = path.IndexOf('|');
+ if (explicitSplit > 0)
+ {
+ var parent = path.Substring(0, explicitSplit);
+ var filename = path.Substring(explicitSplit + 1);
+
+ var parentPath = manifest.Packages.FirstOrDefault(kv => kv.Value == parent).Key;
+ if (parentPath == null)
+ return null;
+
+ if (parentPath.StartsWith("$", StringComparison.Ordinal))
+ {
+ Manifest mod;
+ if (!installedMods.TryGetValue(parentPath.Substring(1), out mod))
+ return null;
+
+ if (!(mod.Package is Folder))
+ return null;
+
+ path = Path.Combine(mod.Package.Name, filename);
+ }
+ else
+ path = Path.Combine(parentPath, filename);
+ }
+
+ var resolvedPath = Platform.ResolvePath(path);
+ return File.Exists(resolvedPath) ? resolvedPath : null;
+ }
}
}
diff --git a/OpenRA.Game/ModData.cs b/OpenRA.Game/ModData.cs
index 4b5e3c6a6d..d73ccda644 100644
--- a/OpenRA.Game/ModData.cs
+++ b/OpenRA.Game/ModData.cs
@@ -49,13 +49,13 @@ namespace OpenRA
{
Languages = new string[0];
- ModFiles = new FS(mods);
// Take a local copy of the manifest
Manifest = new Manifest(mod.Id, mod.Package);
- ModFiles.LoadFromManifest(Manifest);
+ ObjectCreator = new ObjectCreator(Manifest, mods);
- ObjectCreator = new ObjectCreator(Manifest, ModFiles);
+ ModFiles = new FS(mods);
+ ModFiles.LoadFromManifest(Manifest);
Manifest.LoadCustomData(ObjectCreator);
if (useLoadScreen)
diff --git a/OpenRA.Game/ObjectCreator.cs b/OpenRA.Game/ObjectCreator.cs
index e4cc1072af..cd45fda8ff 100644
--- a/OpenRA.Game/ObjectCreator.cs
+++ b/OpenRA.Game/ObjectCreator.cs
@@ -27,40 +27,31 @@ namespace OpenRA
readonly Cache typeCache;
readonly Cache ctorCache;
readonly Pair[] assemblies;
- readonly bool isMonoRuntime = Type.GetType("Mono.Runtime") != null;
- public ObjectCreator(Manifest manifest, FileSystem.FileSystem modFiles)
+ public ObjectCreator(Manifest manifest, InstalledMods mods)
{
typeCache = new Cache(FindType);
ctorCache = new Cache(GetCtor);
// Allow mods to load types from the core Game assembly, and any additional assemblies they specify.
+ // Assemblies can only be loaded from directories to avoid circular dependencies on package loaders.
var assemblyList = new List() { typeof(Game).Assembly };
foreach (var path in manifest.Assemblies)
{
- var data = modFiles.Open(path).ReadAllBytes();
+ var resolvedPath = FileSystem.FileSystem.ResolveAssemblyPath(path, manifest, mods);
+ if (resolvedPath == null)
+ throw new FileNotFoundException("Assembly `{0}` not found.".F(path));
// .NET doesn't provide any way of querying the metadata of an assembly without either:
// (a) loading duplicate data into the application domain, breaking the world.
// (b) crashing if the assembly has already been loaded.
// We can't check the internal name of the assembly, so we'll work off the data instead
- var hash = CryptoUtil.SHA1Hash(data);
+ var hash = CryptoUtil.SHA1Hash(File.ReadAllBytes(resolvedPath));
Assembly assembly;
if (!ResolvedAssemblies.TryGetValue(hash, out assembly))
{
- Stream symbolStream = null;
- var hasSymbols = false;
-
- // Mono has its own symbol format.
- if (isMonoRuntime)
- hasSymbols = modFiles.TryOpen(path + ".mdb", out symbolStream);
-
- // .NET uses .pdb files.
- else
- hasSymbols = modFiles.TryOpen(path.Substring(0, path.Length - 4) + ".pdb", out symbolStream);
-
- assembly = hasSymbols ? Assembly.Load(data, symbolStream.ReadAllBytes()) : Assembly.Load(data);
+ assembly = Assembly.LoadFile(resolvedPath);
ResolvedAssemblies.Add(hash, assembly);
}