From 248c12827eadb7b7b408fdc9e0d7d8a7cdb166f2 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Mon, 1 May 2017 15:27:42 +0000 Subject: [PATCH] Resolve assembly paths before loading the FileSystem. --- OpenRA.Game/FileSystem/FileSystem.cs | 35 ++++++++++++++++++++++++++++ OpenRA.Game/ModData.cs | 6 ++--- OpenRA.Game/ObjectCreator.cs | 23 ++++++------------ 3 files changed, 45 insertions(+), 19 deletions(-) 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); }