Resolve assembly paths before loading the FileSystem.

This commit is contained in:
Paul Chote
2017-05-01 15:27:42 +00:00
parent f0d7a6caca
commit 248c12827e
3 changed files with 45 additions and 19 deletions

View File

@@ -297,5 +297,40 @@ namespace OpenRA.FileSystem
return fileIndex.ContainsKey(filename); return fileIndex.ContainsKey(filename);
} }
/// <summary>
/// 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).
/// </summary>
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;
}
} }
} }

View File

@@ -49,13 +49,13 @@ namespace OpenRA
{ {
Languages = new string[0]; Languages = new string[0];
ModFiles = new FS(mods);
// Take a local copy of the manifest // Take a local copy of the manifest
Manifest = new Manifest(mod.Id, mod.Package); 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); Manifest.LoadCustomData(ObjectCreator);
if (useLoadScreen) if (useLoadScreen)

View File

@@ -27,40 +27,31 @@ namespace OpenRA
readonly Cache<string, Type> typeCache; readonly Cache<string, Type> typeCache;
readonly Cache<Type, ConstructorInfo> ctorCache; readonly Cache<Type, ConstructorInfo> ctorCache;
readonly Pair<Assembly, string>[] assemblies; readonly Pair<Assembly, string>[] 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<string, Type>(FindType); typeCache = new Cache<string, Type>(FindType);
ctorCache = new Cache<Type, ConstructorInfo>(GetCtor); ctorCache = new Cache<Type, ConstructorInfo>(GetCtor);
// Allow mods to load types from the core Game assembly, and any additional assemblies they specify. // 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<Assembly>() { typeof(Game).Assembly }; var assemblyList = new List<Assembly>() { typeof(Game).Assembly };
foreach (var path in manifest.Assemblies) 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: // .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. // (a) loading duplicate data into the application domain, breaking the world.
// (b) crashing if the assembly has already been loaded. // (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 // 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; Assembly assembly;
if (!ResolvedAssemblies.TryGetValue(hash, out assembly)) if (!ResolvedAssemblies.TryGetValue(hash, out assembly))
{ {
Stream symbolStream = null; assembly = Assembly.LoadFile(resolvedPath);
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);
ResolvedAssemblies.Add(hash, assembly); ResolvedAssemblies.Add(hash, assembly);
} }