예제 #1
0
파일: ModLoader.cs 프로젝트: YLMAPI/YLMAPI
        public static void LoadMod(GameModMetadata meta, Assembly asm)
        {
            ModContent.Crawl(null, asm);

            Type[] types;
            try {
                types = asm.GetTypes();
            } catch (Exception e) {
                e.LogDetailed();
                ModLogger.Log("loader", $"Failed reading assembly: {e}");
                return;
            }
            for (int i = 0; i < types.Length; i++)
            {
                Type type = types[i];
                if (!typeof(GameMod).IsAssignableFrom(type) || type.IsAbstract)
                {
                    continue;
                }

                GameMod mod = (GameMod)type.GetConstructor(_EmptyTypeArray).Invoke(_EmptyObjectArray);

                mod.Metadata = meta;

                Mods.Add(mod);
                _ModuleTypes.Add(type);
                _ModuleMethods.Add(new FastDictionary <string, MethodInfo>());
            }

            ModLogger.Log("loader", $"Mod {meta} initialized.");
        }
예제 #2
0
파일: ModLoader.cs 프로젝트: YLMAPI/YLMAPI
        /// <summary>
        /// Checks if an dependency is loaded.
        /// Can be used by mods manually to f.e. activate / disable functionality if an API is (not) existing.
        /// </summary>
        /// <param name="dependency">Dependency to check for. Name and Version will be checked.</param>
        /// <returns></returns>
        public static bool DependencyLoaded(GameModMetadata dep)
        {
            string  depName    = dep.Name;
            Version depVersion = dep.Version;

            if (depName == "YLMAPI")
            {
                if (ModAPI.Version.Major != depVersion.Major)
                {
                    return(false);
                }
                if (ModAPI.Version.Minor < depVersion.Minor)
                {
                    return(false);
                }
                return(true);
            }

            foreach (GameMod mod in Mods)
            {
                GameModMetadata meta = mod.Metadata;
                if (meta.Name != depName)
                {
                    continue;
                }
                Version version = meta.Version;
                if (version.Major != depVersion.Major)
                {
                    return(false);
                }
                if (version.Minor < depVersion.Minor)
                {
                    return(false);
                }
                return(true);
            }

            return(false);
        }
예제 #3
0
 private static MissingDependencyResolver _GenerateModDependencyResolver(this GameModMetadata meta)
 {
     if (!string.IsNullOrEmpty(meta.Archive))
     {
         return(delegate(MonoModder mod, ModuleDefinition main, string name, string fullName) {
             string asmName = name + ".dll";
             using (ZipFile zip = ZipFile.Read(meta.Archive)) {
                 foreach (ZipEntry entry in zip.Entries)
                 {
                     if (entry.FileName != asmName)
                     {
                         continue;
                     }
                     using (MemoryStream ms = new MemoryStream()) {
                         entry.Extract(ms);
                         ms.Seek(0, SeekOrigin.Begin);
                         return ModuleDefinition.ReadModule(ms, mod.GenReaderParameters(false));
                     }
                 }
             }
             return null;
         });
     }
     if (!string.IsNullOrEmpty(meta.Directory))
     {
         return(delegate(MonoModder mod, ModuleDefinition main, string name, string fullName) {
             string asmPath = Path.Combine(meta.Directory, name + ".dll");
             if (!File.Exists(asmPath))
             {
                 return null;
             }
             return ModuleDefinition.ReadModule(asmPath, mod.GenReaderParameters(false, asmPath));
         });
     }
     return(null);
 }
예제 #4
0
파일: ModLoader.cs 프로젝트: YLMAPI/YLMAPI
 private static ResolveEventHandler _GenerateModAssemblyResolver(this GameModMetadata meta)
 {
     if (!string.IsNullOrEmpty(meta.Archive))
     {
         return(delegate(object sender, ResolveEventArgs args) {
             string asmName = new AssemblyName(args.Name).Name + ".dll";
             using (ZipFile zip = ZipFile.Read(meta.Archive)) {
                 foreach (ZipEntry entry in zip.Entries)
                 {
                     if (entry.FileName != asmName)
                     {
                         continue;
                     }
                     using (MemoryStream ms = new MemoryStream()) {
                         entry.Extract(ms);
                         ms.Seek(0, SeekOrigin.Begin);
                         return Assembly.Load(ms.GetBuffer());
                     }
                 }
             }
             return null;
         });
     }
     if (!string.IsNullOrEmpty(meta.Directory))
     {
         return(delegate(object sender, ResolveEventArgs args) {
             string asmPath = Path.Combine(meta.Directory, new AssemblyName(args.Name).Name + ".dll");
             if (!File.Exists(asmPath))
             {
                 return null;
             }
             return Assembly.LoadFrom(asmPath);
         });
     }
     return(null);
 }
예제 #5
0
        public static Assembly GetRelinkedAssembly(this GameModMetadata meta, Stream stream, MissingDependencyResolver depResolver = null)
        {
            string name               = Path.GetFileName(meta.DLL);
            string cachedName         = meta.Name + "." + name.Substring(0, name.Length - 3) + "dll";
            string cachedPath         = Path.Combine(ModLoader.ModsCacheDirectory, cachedName);
            string cachedChecksumPath = Path.Combine(ModLoader.ModsCacheDirectory, cachedName + ".sum");

            string[] checksums = new string[2];
            using (MD5 md5 = MD5.Create()) {
                if (GameChecksum == null)
                {
                    using (FileStream fs = File.OpenRead(Assembly.GetAssembly(typeof(ModRelinker)).Location))
                        GameChecksum = md5.ComputeHash(fs).ToHexadecimalString();
                }
                checksums[0] = GameChecksum;

                string modPath = meta.Archive;
                if (modPath.Length == 0)
                {
                    modPath = meta.DLL;
                }
                using (FileStream fs = File.OpenRead(modPath))
                    checksums[1] = md5.ComputeHash(fs).ToHexadecimalString();
            }

            if (File.Exists(cachedPath) && File.Exists(cachedChecksumPath) &&
                checksums.ChecksumsEqual(File.ReadAllLines(cachedChecksumPath)))
            {
                return(Assembly.LoadFrom(cachedPath));
            }

            if (depResolver == null)
            {
                depResolver = _GenerateModDependencyResolver(meta);
            }

            using (MonoModder modder = new MonoModder()
            {
                Input = stream,
                OutputPath = cachedPath,
                CleanupEnabled = false,
                RelinkModuleMap = AssemblyRelinkMap,
                DependencyDirs =
                {
                    ManagedDirectory
                },
                MissingDependencyResolver = depResolver,
                RelinkMap = ModRuntimePatcher.Detourer.RelinkMap
            })
                try {
                    modder.ReaderParameters.ReadSymbols          = false;
                    modder.WriterParameters.WriteSymbols         = false;
                    modder.WriterParameters.SymbolWriterProvider = null;

                    modder.Read();
                    modder.MapDependencies();
                    modder.AutoPatch();
                    modder.Write();
                } catch (Exception e) {
                    ModLogger.Log("relinker", $"Failed relinking {meta}: {e}");
                    return(null);
                }

            return(Assembly.LoadFrom(cachedPath));
        }
예제 #6
0
파일: ModLoader.cs 프로젝트: YLMAPI/YLMAPI
        public static void LoadModZIP(string archive)
        {
            if (!File.Exists(archive))
            {
                // Probably a mod in the mod directory
                archive = Path.Combine(ModsDirectory, archive);
            }
            if (!File.Exists(archive))
            {
                // It just doesn't exist.
                return;
            }

            ModLogger.Log("loader", $"Loading mod .zip: {archive}");

            GameModMetadata meta = null;
            Assembly        asm  = null;

            using (ZipFile zip = ZipFile.Read(archive)) {
                Texture2D icon = null;
                // First read the metadata, ...
                foreach (ZipEntry entry in zip.Entries)
                {
                    if (entry.FileName == "metadata.yaml")
                    {
                        using (MemoryStream ms = new MemoryStream()) {
                            entry.Extract(ms);
                            ms.Seek(0, SeekOrigin.Begin);
                            using (StreamReader reader = new StreamReader(ms))
                                meta = GameModMetadata.Parse(archive, "", reader);
                        }
                        continue;
                    }
                    if (entry.FileName == "icon.png")
                    {
                        icon      = new Texture2D(2, 2);
                        icon.name = "icon";
                        using (MemoryStream ms = new MemoryStream()) {
                            entry.Extract(ms);
                            ms.Seek(0, SeekOrigin.Begin);
                            icon.LoadImage(ms.GetBuffer());
                        }
                        icon.filterMode = FilterMode.Point;
                        continue;
                    }
                }

                if (meta != null)
                {
                    // In case the icon appears before the metadata in the .zip
                    if (icon != null)
                    {
                        meta.Icon = icon;
                    }

                    // ... then check if the mod runs on this profile ...
                    if (meta.ProfileID > ModAPI.Profile.Id)
                    {
                        ModLogger.Log("loader", "Mod meant for an in-dev YLMAPI version!");
                        return;
                    }

                    // ... then check if the dependencies are loaded ...
                    foreach (GameModMetadata dep in meta.Dependencies)
                    {
                        if (!DependencyLoaded(dep))
                        {
                            ModLogger.Log("loader", $"Dependency {dep} of mod {meta} not loaded!");
                            return;
                        }
                    }

                    // ... then add an AssemblyResolve handler for all the .zip-ped libraries
                    AppDomain.CurrentDomain.AssemblyResolve += meta._GenerateModAssemblyResolver();
                }

                // ... then the patch (if any) ...
                if (!string.IsNullOrEmpty(meta?.PatchDLL))
                {
                    foreach (ZipEntry entry in zip.Entries)
                    {
                        string entryName = entry.FileName.Replace("\\", "/");
                        if (entryName != meta.PatchDLL)
                        {
                            continue;
                        }

                        using (MemoryStream ms = new MemoryStream()) {
                            entry.Extract(ms);
                            ms.Seek(0, SeekOrigin.Begin);
                            ModRuntimePatcher.LoadPatch(ms);
                        }

                        break;
                    }
                }

                // ... then everything else
                foreach (ZipEntry entry in zip.Entries)
                {
                    string entryName = entry.FileName.Replace("\\", "/");
                    if (meta != null && entryName == meta.DLL)
                    {
                        using (MemoryStream ms = new MemoryStream()) {
                            entry.Extract(ms);
                            ms.Seek(0, SeekOrigin.Begin);
                            if (meta.Prelinked)
                            {
                                asm = Assembly.Load(ms.GetBuffer());
                            }
                            else
                            {
                                asm = meta.GetRelinkedAssembly(ms);
                            }
                        }
                    }

                    ModContent.AddMapping(entryName, new AssetMetadata(archive, entryName)
                    {
                        AssetType = entry.IsDirectory ? typeof(AssetTypeDirectory) : null
                    });
                }
            }

            if (meta != null && asm != null)
            {
                LoadMod(meta, asm);
            }
        }
예제 #7
0
파일: ModLoader.cs 프로젝트: YLMAPI/YLMAPI
        public static void LoadModDir(string dir)
        {
            if (!Directory.Exists(dir))
            {
                // Probably a mod in the mod directory
                dir = Path.Combine(ModsDirectory, dir);
            }
            if (!Directory.Exists(dir))
            {
                // It just doesn't exist.
                return;
            }

            ModLogger.Log("loader", $"Loading mod directory: {dir}");

            GameModMetadata meta = null;
            Assembly        asm  = null;

            // First read the metadata, ...
            string metaPath = Path.Combine(dir, "metadata.yaml");

            if (File.Exists(metaPath))
            {
                using (StreamReader reader = new StreamReader(metaPath))
                    meta = GameModMetadata.Parse("", dir, reader);
            }

            if (meta != null)
            {
                // ... then check if the mod runs on this profile ...
                if (meta.ProfileID > ModAPI.Profile.Id)
                {
                    ModLogger.Log("loader", "Mod meant for an in-dev YLMAPI version!");
                    return;
                }

                // ... then check if the dependencies are loaded ...
                foreach (GameModMetadata dep in meta.Dependencies)
                {
                    if (!DependencyLoaded(dep))
                    {
                        ModLogger.Log("loader", $"Dependency {dep} of mod {meta} not loaded!");
                        return;
                    }
                }

                // ... then add an AssemblyResolve handler for all the .zip-ped libraries
                AppDomain.CurrentDomain.AssemblyResolve += meta._GenerateModAssemblyResolver();
            }

            // ... then everything else
            ModContent.Crawl(null, dir);
            if (meta == null || !File.Exists(meta.DLL))
            {
                return;
            }
            if (!string.IsNullOrEmpty(meta.PatchDLL) && File.Exists(meta.PatchDLL))
            {
                using (Stream stream = File.OpenRead(meta.PatchDLL))
                    ModRuntimePatcher.LoadPatch(stream);
            }
            if (meta.Prelinked)
            {
                asm = Assembly.LoadFrom(meta.DLL);
            }
            else
            {
                using (FileStream fs = File.OpenRead(meta.DLL))
                    asm = meta.GetRelinkedAssembly(fs);
            }

            if (asm != null)
            {
                LoadMod(meta, asm);
            }
        }