Exemple #1
0
        private static void InstallPendingSelfUpdates()
        {
            var path = Path.Combine(BeatSaber.InstallPath, "IPA.exe");

            if (!File.Exists(path))
            {
                return;
            }

            var ipaVersion  = new Version(FileVersionInfo.GetVersionInfo(path).FileVersion);
            var selfVersion = Assembly.GetExecutingAssembly().GetName().Version;

            if (ipaVersion > selfVersion)
            {
                Process.Start(new ProcessStartInfo
                {
                    FileName        = path,
                    Arguments       = $"\"-nw={Process.GetCurrentProcess().Id},s={string.Join(" ", Environment.GetCommandLineArgs().Skip(1).StrJP()).Replace("\\", "\\\\").Replace(",", "\\,")}\"",
                    UseShellExecute = false
                });

                updater.Info("Updating BSIPA...");
                Environment.Exit(0);
            }
        }
Exemple #2
0
        public static bool IsInvalid(string path)
        {
            var dataPlugins = Path.Combine(GameVersionEarly.ResolveDataPath(path), "Plugins");

            try
            {
                var userDir = GetPath(new Guid("374DE290-123F-4565-9164-39C4925E467B"),
                                      KnownFolderFlags.AliasOnly | KnownFolderFlags.DontVerify);
                var userDir2 = GetPath(new Guid("7d83ee9b-2244-4e70-b1f5-5393042af1e4"),
                                       KnownFolderFlags.AliasOnly | KnownFolderFlags.DontVerify);

                var curdir = Environment.CurrentDirectory;

                if (curdir.IsSubPathOf(userDir) ||
                    curdir.IsSubPathOf(userDir2))
                {
                    return(false);
                }
            }
            catch { }

            // To the guys that maintain a fork that removes this code: I would greatly appreciate if we could talk
            //   about this for a little bit. Please message me on Discord at DaNike#6223
            return
                (File.Exists(Path.Combine(path, "IGG-GAMES.COM.url")) ||
                 File.Exists(Path.Combine(path, "SmartSteamEmu.ini")) ||
                 File.Exists(Path.Combine(path, "GAMESTORRENT.CO.url")) ||
                 File.Exists(Path.Combine(dataPlugins, "BSteam crack.dll")) ||
                 File.Exists(Path.Combine(dataPlugins, "HUHUVR_steam_api64.dll")) ||
                 Directory.GetFiles(dataPlugins, "*.ini", SearchOption.TopDirectoryOnly).Length > 0);
        }
        public static bool IsInvalid(string path)
        {
            var dataPlugins = Path.Combine(GameVersionEarly.ResolveDataPath(path), "Plugins");

            try
            {
                var userDir = GetPath(new Guid("374DE290-123F-4565-9164-39C4925E467B"),
                                      KnownFolderFlags.AliasOnly | KnownFolderFlags.DontVerify);
                var userDir2 = GetPath(new Guid("7d83ee9b-2244-4e70-b1f5-5393042af1e4"),
                                       KnownFolderFlags.AliasOnly | KnownFolderFlags.DontVerify);

                var curdir = Environment.CurrentDirectory;

                if (curdir.IsSubPathOf(userDir) ||
                    curdir.IsSubPathOf(userDir2))
                {
                    return(false);
                }
            }
            catch { }

            // To the guys that maintain a fork that removes this code: I would greatly appreciate if we could talk
            //   about this for a little bit. Please message me on Discord at DaNike#6223
            return(false);
        }
Exemple #4
0
        internal static void SetupAssemblyFilenames(bool force = false)
        {
            if (FilenameLocations == null || force)
            {
                FilenameLocations = new Dictionary <string, string>();

                foreach (var fn in TraverseTree(LibraryPath, s => s != NativeLibraryPath))
                {
                    if (FilenameLocations.ContainsKey(fn.Name))
                    {
                        Log(Logger.Level.Critical, $"Multiple instances of {fn.Name} exist in Libs! Ignoring {fn.FullName}");
                    }
                    else
                    {
                        FilenameLocations.Add(fn.Name, fn.FullName);
                    }
                }


                if (!SetDefaultDllDirectories(LoadLibraryFlags.LOAD_LIBRARY_SEARCH_USER_DIRS | LoadLibraryFlags.LOAD_LIBRARY_SEARCH_SYSTEM32
                                              | LoadLibraryFlags.LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LoadLibraryFlags.LOAD_LIBRARY_SEARCH_APPLICATION_DIR))
                {
                    var err = new Win32Exception();
                    Log(Logger.Level.Critical, $"Error configuring DLL search path");
                    Log(Logger.Level.Critical, err);
                    return;
                }

                void AddDir(string path)
                {
                    var retPtr = AddDllDirectory(path);

                    if (retPtr == IntPtr.Zero)
                    {
                        var err = new Win32Exception();
                        Log(Logger.Level.Warning, $"Could not add DLL directory");
                        Log(Logger.Level.Warning, err);
                    }
                }

                if (Directory.Exists(NativeLibraryPath))
                {
                    AddDir(NativeLibraryPath);
                    TraverseTree(NativeLibraryPath, dir =>
                    {                  // this is a terrible hack for iterating directories
                        AddDir(dir); return(true);
                    }).All(f => true); // force it to iterate all
                }

                var unityData = Directory.EnumerateDirectories(Environment.CurrentDirectory, "*_Data").First();
                AddDir(Path.Combine(unityData, "Plugins"));

                foreach (var dir in Environment.GetEnvironmentVariable("path").Split(Path.PathSeparator))
                {
                    AddDir(dir);
                }
            }
        }
        private static Tuple <IEnumerable <PluginInfo>, IEnumerable <IPlugin> > LoadPluginsFromFile(string file)
        {
            List <IPlugin> ipaPlugins = new List <IPlugin>();

            if (!File.Exists(file) || !file.EndsWith(".dll", true, null))
            {
                return(new Tuple <IEnumerable <PluginInfo>, IEnumerable <IPlugin> >(null, ipaPlugins));
            }

            T OptionalGetPlugin <T>(Type t) where T : class
            {
                // use typeof() to allow for easier renaming (in an ideal world this compiles to a string, but ¯\_(ツ)_/¯)
                if (t.GetInterface(typeof(T).Name) != null)
                {
                    try
                    {
                        T pluginInstance = Activator.CreateInstance(t) as T;
                        return(pluginInstance);
                    }
                    catch (Exception e)
                    {
                        Logger.loader.Error($"Could not load plugin {t.FullName} in {Path.GetFileName(file)}! {e}");
                    }
                }

                return(null);
            }

            try
            {
                Assembly assembly = Assembly.LoadFrom(file);

                foreach (Type t in assembly.GetTypes())
                {
                    IPlugin ipaPlugin = OptionalGetPlugin <IPlugin>(t);
                    if (ipaPlugin != null)
                    {
                        ipaPlugins.Add(ipaPlugin);
                    }
                }
            }
            catch (ReflectionTypeLoadException e)
            {
                Logger.loader.Error($"Could not load the following types from {Path.GetFileName(file)}:");
                Logger.loader.Error($"  {string.Join("\n  ", e.LoaderExceptions?.Select(e1 => e1?.Message).StrJP() ?? new string[0])}");
            }
            catch (Exception e)
            {
                Logger.loader.Error($"Could not load {Path.GetFileName(file)}!");
                Logger.loader.Error(e);
            }

            return(new Tuple <IEnumerable <PluginInfo>, IEnumerable <IPlugin> >(null, ipaPlugins));
        }
Exemple #6
0
        /// <summary>
        /// Gets a <see cref="Config"/> object using the specified list of preferred config types.
        /// </summary>
        /// <param name="configName">the name of the mod for this config</param>
        /// <param name="extensions">the preferred config types to try to get</param>
        /// <returns>a <see cref="Config"/> using the requested format, or of type JSON.</returns>
        public static Config GetConfigFor(string configName, params string[] extensions)
        {
            var chosenExt = extensions.FirstOrDefault(s => registeredProviders.ContainsKey(s)) ?? "json";
            var provider  = registeredProviders[chosenExt];

            var filename = Path.Combine(UnityGame.UserDataPath, configName + "." + provider.Extension);
            var config   = new Config(configName, provider, new FileInfo(filename));

            ConfigRuntime.RegisterConfig(config);

            return(config);
        }
Exemple #7
0
        public static bool IsInvalid(string path)
        {
            var dataPlugins = Path.Combine(GameVersionEarly.ResolveDataPath(path), "Plugins");

            return
                (File.Exists(Path.Combine(path, "IGG-GAMES.COM.url")) ||
                 File.Exists(Path.Combine(path, "SmartSteamEmu.ini")) ||
                 File.Exists(Path.Combine(path, "GAMESTORRENT.CO.url")) ||
                 File.Exists(Path.Combine(dataPlugins, "BSteam crack.dll")) ||
                 File.Exists(Path.Combine(dataPlugins, "HUHUVR_steam_api64.dll")) ||
                 Directory.GetFiles(dataPlugins, "*.ini", SearchOption.TopDirectoryOnly).Length > 0);
        }
Exemple #8
0
        private static void EnsureDirectories()
        {
            string path;

            if (!Directory.Exists(path = Path.Combine(Environment.CurrentDirectory, "UserData")))
            {
                Directory.CreateDirectory(path);
            }
            if (!Directory.Exists(path = Path.Combine(Environment.CurrentDirectory, "Plugins")))
            {
                Directory.CreateDirectory(path);
            }
        }
        /// <summary>
        /// Gets an <see cref="IConfigProvider"/> using the specified list of preferred config types.
        /// </summary>
        /// <param name="configName">the name of the mod for this config</param>
        /// <param name="extensions">the preferred config types to try to get</param>
        /// <returns>an <see cref="IConfigProvider"/> of the requested type, or of type JSON.</returns>
        public static IConfigProvider GetProviderFor(string configName, params string[] extensions)
        {
            var chosenExt = extensions.FirstOrDefault(s => registeredProviders.ContainsKey(s)) ?? "json";
            var type      = registeredProviders[chosenExt];
            var provider  = Activator.CreateInstance(type) as IConfigProvider;

            if (provider != null)
            {
                provider.Filename = Path.Combine(BeatSaber.UserDataPath, configName);
                configProviders.Add(Tuple.Create(Ref.Create(provider.LastModified), provider));
            }

            return(provider);
        }
        private static void InstallPendingSelfUpdates()
        {
            var path = Path.Combine(UnityGame.InstallPath, "IPA.exe");

            if (!File.Exists(path))
            {
                return;
            }

            var ipaVersion  = new Version(FileVersionInfo.GetVersionInfo(path).FileVersion);
            var selfVersion = Assembly.GetExecutingAssembly().GetName().Version;

            if (ipaVersion > selfVersion)
            {
                var scanResult = AntiMalwareEngine.Engine.ScanFile(new FileInfo(path));
                if (scanResult == ScanResult.Detected)
                {
                    Updater.Error("Scan of BSIPA installer found malware; not updating");
                    return;
                }
                if (!SelfConfig.AntiMalware_.RunPartialThreatCode_ && scanResult is not ScanResult.KnownSafe and not ScanResult.NotDetected)
                {
                    Updater.Error("Scan of BSIPA installer returned partial threat; not updating. To allow this, enable AntiMalware.RunPartialThreatCode in the config.");
                    return;
                }

                _ = Process.Start(new ProcessStartInfo
                {
                    FileName  = path,
                    Arguments = $"\"-nw={Process.GetCurrentProcess().Id}," +
                                $"s={string.Join(" ", Environment.GetCommandLineArgs().Skip(1).StrJP()).Replace("\\", "\\\\").Replace(",", "\\,")}\"",
                    UseShellExecute = false
                });

                Updater.Info("Updating BSIPA...");
                Environment.Exit(0);
            }
        }
        internal static void YeetIfNeeded()
        {
            string pluginDir = UnityGame.PluginsPath;

            if (SelfConfig.YeetMods_ && UnityGame.IsGameVersionBoundary)
            {
                var oldPluginsName = Path.Combine(UnityGame.InstallPath, $"Old {UnityGame.OldVersion} Plugins");
                var newPluginsName = Path.Combine(UnityGame.InstallPath, $"Old {UnityGame.GameVersion} Plugins");

                if (Directory.Exists(oldPluginsName))
                {
                    Directory.Delete(oldPluginsName, true);
                }
                Directory.Move(pluginDir, oldPluginsName);
                if (Directory.Exists(newPluginsName))
                {
                    Directory.Move(newPluginsName, pluginDir);
                }
                else
                {
                    Directory.CreateDirectory(pluginDir);
                }
            }
        }
Exemple #12
0
        private static void InstallPendingModUpdates()
        {
            var pendingDir = Path.Combine(BeatSaber.InstallPath, "IPA", "Pending");

            if (!Directory.Exists(pendingDir))
            {
                return;
            }

            // there are pending updates, install
            updater.Info("Installing pending updates");

            var toDelete = new string[0];
            var delFn    = Path.Combine(pendingDir, DeleteFileName);

            if (File.Exists(delFn))
            {
                toDelete = File.ReadAllLines(delFn);
                File.Delete(delFn);
            }

            foreach (var file in toDelete)
            {
                try
                {
                    File.Delete(Path.Combine(BeatSaber.InstallPath, file));
                }
                catch (Exception e)
                {
                    updater.Error("While trying to install pending updates: Error deleting file marked for deletion");
                    updater.Error(e);
                }
            }

            #region Self Protection

            string path;
            if (Directory.Exists(path = Path.Combine(pendingDir, "IPA")))
            {
                var dirs = new Stack <string>(20);

                dirs.Push(path);

                while (dirs.Count > 0)
                {
                    var      currentDir = dirs.Pop();
                    string[] subDirs;
                    string[] files;
                    try
                    {
                        subDirs = Directory.GetDirectories(currentDir);
                        files   = Directory.GetFiles(currentDir);
                    }
                    catch (UnauthorizedAccessException e)
                    {
                        updater.Error(e);
                        continue;
                    }
                    catch (DirectoryNotFoundException e)
                    {
                        updater.Error(e);
                        continue;
                    }

                    foreach (var file in files)
                    {
                        try
                        {
                            if (!Utils.GetRelativePath(file, path).Split(Path.PathSeparator).Contains("Pending"))
                            {
                                File.Delete(file);
                            }
                        }
                        catch (FileNotFoundException e)
                        {
                            updater.Error(e);
                        }
                    }

                    foreach (var str in subDirs)
                    {
                        dirs.Push(str);
                    }
                }
            }
            if (File.Exists(path = Path.Combine(pendingDir, "IPA.exe")))
            {
                File.Delete(path);
                if (File.Exists(path = Path.Combine(pendingDir, "Mono.Cecil.dll")))
                {
                    File.Delete(path);
                }
            }

            #endregion

            try
            {
                Utils.CopyAll(new DirectoryInfo(pendingDir), new DirectoryInfo(BeatSaber.InstallPath), onCopyException: (e, f) =>
                {
                    updater.Error($"Error copying file {Utils.GetRelativePath(f.FullName, pendingDir)} from Pending:");
                    updater.Error(e);
                    return(true);
                });
            }
            catch (Exception e)
            {
                updater.Error("While trying to install pending updates: Error copying files in");
                updater.Error(e);
            }

            try
            {
                Directory.Delete(pendingDir, true);
            }
            catch (Exception e)
            {
                updater.Error("Something went wrong performing an operation that should never fail!");
                updater.Error(e);
            }
        }
        internal static void Load()
        {
            string pluginDirectory = BeatSaber.PluginsPath;

            // Process.GetCurrentProcess().MainModule crashes the game and Assembly.GetEntryAssembly() is NULL,
            // so we need to resort to P/Invoke
            string exeName = Path.GetFileNameWithoutExtension(AppInfo.StartupPath);

            _bsPlugins  = new List <PluginInfo>();
            _ipaPlugins = new List <IPlugin>();

            if (!Directory.Exists(pluginDirectory))
            {
                return;
            }

            string cacheDir = Path.Combine(pluginDirectory, ".cache");

            if (!Directory.Exists(cacheDir))
            {
                Directory.CreateDirectory(cacheDir);
            }
            else
            {
                foreach (string plugin in Directory.GetFiles(cacheDir, "*"))
                {
                    File.Delete(plugin);
                }
            }

            // initialize BSIPA plugins first
            _bsPlugins.AddRange(PluginLoader.LoadPlugins());

            //Copy plugins to .cache
            string[] originalPlugins = Directory.GetFiles(pluginDirectory, "*.dll");
            foreach (string s in originalPlugins)
            {
                if (PluginsMetadata.Select(m => m.File.FullName).Contains(s))
                {
                    continue;
                }
                string pluginCopy = Path.Combine(cacheDir, Path.GetFileName(s));

                #region Fix assemblies for refactor

                var module = ModuleDefinition.ReadModule(Path.Combine(pluginDirectory, s));
                foreach (var @ref in module.AssemblyReferences)
                { // fix assembly references
                    if (@ref.Name == "IllusionPlugin" || @ref.Name == "IllusionInjector")
                    {
                        @ref.Name = "IPA.Loader";
                    }
                }

                foreach (var @ref in module.GetTypeReferences())
                { // fix type references
                    if (@ref.FullName == "IllusionPlugin.IPlugin")
                    {
                        @ref.Namespace = "IPA.Old";                                            //@ref.Name = "";
                    }
                    if (@ref.FullName == "IllusionPlugin.IEnhancedPlugin")
                    {
                        @ref.Namespace = "IPA.Old";                                                    //@ref.Name = "";
                    }
                    if (@ref.FullName == "IllusionPlugin.IBeatSaberPlugin")
                    {
                        @ref.Namespace = "IPA";                                                     //@ref.Name = "";
                    }
                    if (@ref.FullName == "IllusionPlugin.IEnhancedBeatSaberPlugin")
                    {
                        @ref.Namespace = "IPA";                                                             //@ref.Name = "";
                    }
                    if (@ref.FullName == "IllusionPlugin.BeatSaber.ModsaberModInfo")
                    {
                        @ref.Namespace = "IPA";                                                              //@ref.Name = "";
                    }
                    if (@ref.FullName == "IllusionPlugin.IniFile")
                    {
                        @ref.Namespace = "IPA.Config";                                            //@ref.Name = "";
                    }
                    if (@ref.FullName == "IllusionPlugin.IModPrefs")
                    {
                        @ref.Namespace = "IPA.Config";                                              //@ref.Name = "";
                    }
                    if (@ref.FullName == "IllusionPlugin.ModPrefs")
                    {
                        @ref.Namespace = "IPA.Config";                                             //@ref.Name = "";
                    }
                    if (@ref.FullName == "IllusionPlugin.Utils.ReflectionUtil")
                    {
                        @ref.Namespace = "IPA.Utilities";                                                         //@ref.Name = "";
                    }
                    if (@ref.FullName == "IllusionPlugin.Logging.Logger")
                    {
                        @ref.Namespace = "IPA.Logging";                                                   //@ref.Name = "";
                    }
                    if (@ref.FullName == "IllusionPlugin.Logging.LogPrinter")
                    {
                        @ref.Namespace = "IPA.Logging";                                                       //@ref.Name = "";
                    }
                    if (@ref.FullName == "IllusionInjector.PluginManager")
                    {
                        @ref.Namespace = "IPA.Loader";                                                    //@ref.Name = "";
                    }
                    if (@ref.FullName == "IllusionInjector.PluginComponent")
                    {
                        @ref.Namespace = "IPA.Loader";                                                      //@ref.Name = "";
                    }
                    if (@ref.FullName == "IllusionInjector.CompositeBSPlugin")
                    {
                        @ref.Namespace = "IPA.Loader.Composite";                                                        //@ref.Name = "";
                    }
                    if (@ref.FullName == "IllusionInjector.CompositeIPAPlugin")
                    {
                        @ref.Namespace = "IPA.Loader.Composite";                                                         //@ref.Name = "";
                    }
                    if (@ref.FullName == "IllusionInjector.Logging.UnityLogInterceptor")
                    {
                        @ref.Namespace = "IPA.Logging";                                                                  //@ref.Name = "";
                    }
                    if (@ref.FullName == "IllusionInjector.Logging.StandardLogger")
                    {
                        @ref.Namespace = "IPA.Logging";                                                             //@ref.Name = "";
                    }
                    if (@ref.FullName == "IllusionInjector.Updating.SelfPlugin")
                    {
                        @ref.Namespace = "IPA.Updating";                                                          //@ref.Name = "";
                    }
                    if (@ref.FullName == "IllusionInjector.Updating.Backup.BackupUnit")
                    {
                        @ref.Namespace = "IPA.Updating.Backup";                                                                 //@ref.Name = "";
                    }
                    if (@ref.Namespace == "IllusionInjector.Utilities")
                    {
                        @ref.Namespace = "IPA.Utilities";                                                 //@ref.Name = "";
                    }
                    if (@ref.Namespace == "IllusionInjector.Logging.Printers")
                    {
                        @ref.Namespace = "IPA.Logging.Printers";                                                        //@ref.Name = "";
                    }
                    if (@ref.Namespace == "IllusionInjector.Updating.ModsaberML")
                    {
                        @ref.Namespace = "IPA.Updating.ModSaber";                                                           //@ref.Name = "";
                    }
                }
                module.Write(pluginCopy);

                #endregion
            }

            //Load copied plugins
            string[] copiedPlugins = Directory.GetFiles(cacheDir, "*.dll");
            foreach (string s in copiedPlugins)
            {
                var result = LoadPluginsFromFile(s);
                _ipaPlugins.AddRange(result.Item2);
            }

            Logger.log.Info(exeName);
            Logger.log.Info($"Running on Unity {Application.unityVersion}");
            Logger.log.Info($"Game version {BeatSaber.GameVersion}");
            Logger.log.Info("-----------------------------");
            Logger.log.Info($"Loading plugins from {Utils.GetRelativePath(pluginDirectory, Environment.CurrentDirectory)} and found {_bsPlugins.Count + _ipaPlugins.Count}");
            Logger.log.Info("-----------------------------");
            foreach (var plugin in _bsPlugins)
            {
                Logger.log.Info($"{plugin.Metadata.Name} ({plugin.Metadata.Id}): {plugin.Metadata.Version}");
            }
            Logger.log.Info("-----------------------------");
            foreach (var plugin in _ipaPlugins)
            {
                Logger.log.Info($"{plugin.Name}: {plugin.Version}");
            }
            Logger.log.Info("-----------------------------");
        }
Exemple #14
0
        private static void InstallBootstrapPatch()
        {
            var sw = Stopwatch.StartNew();

            var cAsmName    = Assembly.GetExecutingAssembly().GetName();
            var managedPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

            var dataDir  = new DirectoryInfo(managedPath).Parent.Name;
            var gameName = dataDir.Substring(0, dataDir.Length - 5);

            injector.Debug("Finding backup");
            var backupPath = Path.Combine(Environment.CurrentDirectory, "IPA", "Backups", gameName);
            var bkp        = BackupManager.FindLatestBackup(backupPath);

            if (bkp == null)
            {
                injector.Warn("No backup found! Was BSIPA installed using the installer?");
            }

            injector.Debug("Ensuring patch on UnityEngine.CoreModule exists");

            #region Insert patch into UnityEngine.CoreModule.dll

            {
                var unityPath = Path.Combine(managedPath,
                                             "UnityEngine.CoreModule.dll");

                // this is a critical section because if you exit in here, CoreModule can die
                using var critSec = CriticalSection.ExecuteSection();

                using var unityAsmDef = AssemblyDefinition.ReadAssembly(unityPath, new ReaderParameters
                {
                    ReadWrite   = false,
                    InMemory    = true,
                    ReadingMode = ReadingMode.Immediate
                });
                var unityModDef = unityAsmDef.MainModule;

                bool modified = false;
                foreach (var asmref in unityModDef.AssemblyReferences)
                {
                    if (asmref.Name == cAsmName.Name)
                    {
                        if (asmref.Version != cAsmName.Version)
                        {
                            asmref.Version = cAsmName.Version;
                            modified       = true;
                        }
                    }
                }

                var application = unityModDef.GetType("UnityEngine", "Camera");

                if (application == null)
                {
                    injector.Critical("UnityEngine.CoreModule doesn't have a definition for UnityEngine.Camera!"
                                      + "Nothing to patch to get ourselves into the Unity run cycle!");
                    goto endPatchCoreModule;
                }

                MethodDefinition?cctor = null;
                foreach (var m in application.Methods)
                {
                    if (m.IsRuntimeSpecialName && m.Name == ".cctor")
                    {
                        cctor = m;
                    }
                }

                var cbs = unityModDef.ImportReference(((Action)CreateBootstrapper).Method);

                if (cctor == null)
                {
                    cctor = new MethodDefinition(".cctor",
                                                 MethodAttributes.RTSpecialName | MethodAttributes.Static | MethodAttributes.SpecialName,
                                                 unityModDef.TypeSystem.Void);
                    application.Methods.Add(cctor);
                    modified = true;

                    var ilp = cctor.Body.GetILProcessor();
                    ilp.Emit(OpCodes.Call, cbs);
                    ilp.Emit(OpCodes.Ret);
                }
                else
                {
                    var ilp = cctor.Body.GetILProcessor();
                    for (var i = 0; i < Math.Min(2, cctor.Body.Instructions.Count); i++)
                    {
                        var ins = cctor.Body.Instructions[i];
                        switch (i)
                        {
                        case 0 when ins.OpCode != OpCodes.Call:
                            ilp.Replace(ins, ilp.Create(OpCodes.Call, cbs));
                            modified = true;
                            break;

                        case 0:
                        {
                            var methodRef = ins.Operand as MethodReference;
                            if (methodRef?.FullName != cbs.FullName)
                            {
                                ilp.Replace(ins, ilp.Create(OpCodes.Call, cbs));
                                modified = true;
                            }

                            break;
                        }

                        case 1 when ins.OpCode != OpCodes.Ret:
                            ilp.Replace(ins, ilp.Create(OpCodes.Ret));
                            modified = true;
                            break;
                        }
                    }
                }

                if (modified)
                {
                    bkp?.Add(unityPath);
                    unityAsmDef.Write(unityPath);
                }
            }
endPatchCoreModule:
            #endregion Insert patch into UnityEngine.CoreModule.dll

            injector.Debug("Ensuring game assemblies are virtualized");

            #region Virtualize game assemblies
            bool isFirst = true;
            foreach (var name in SelfConfig.GameAssemblies_)
            {
                var ascPath = Path.Combine(managedPath, name);

                using var execSec = CriticalSection.ExecuteSection();

                try
                {
                    injector.Debug($"Virtualizing {name}");
                    using var ascModule = VirtualizedModule.Load(ascPath);
                    ascModule.Virtualize(cAsmName, () => bkp?.Add(ascPath));
                }
                catch (Exception e)
                {
                    injector.Error($"Could not virtualize {ascPath}");
                    if (SelfConfig.Debug_.ShowHandledErrorStackTraces_)
                    {
                        injector.Error(e);
                    }
                }

#if BeatSaber
                if (isFirst)
                {
                    try
                    {
                        injector.Debug("Applying anti-yeet patch");

                        using var ascAsmDef = AssemblyDefinition.ReadAssembly(ascPath, new ReaderParameters
                        {
                            ReadWrite   = false,
                            InMemory    = true,
                            ReadingMode = ReadingMode.Immediate
                        });
                        var ascModDef = ascAsmDef.MainModule;

                        var deleter = ascModDef.GetType("IPAPluginsDirDeleter");
                        deleter.Methods.Clear(); // delete all methods

                        ascAsmDef.Write(ascPath);

                        isFirst = false;
                    }
                    catch (Exception e)
                    {
                        injector.Warn($"Could not apply anti-yeet patch to {ascPath}");
                        if (SelfConfig.Debug_.ShowHandledErrorStackTraces_)
                        {
                            injector.Warn(e);
                        }
                    }
                }
#endif
            }
            #endregion

            sw.Stop();
            injector.Info($"Installing bootstrapper took {sw.Elapsed}");
        }
Exemple #15
0
        private static void InstallBootstrapPatch()
        {
            var cAsmName    = Assembly.GetExecutingAssembly().GetName();
            var managedPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

            var dataDir  = new DirectoryInfo(managedPath).Parent.Name;
            var gameName = dataDir.Substring(0, dataDir.Length - 5);

            loader.Debug("Finding backup");
            var backupPath = Path.Combine(Environment.CurrentDirectory, "IPA", "Backups", gameName);
            var bkp        = BackupManager.FindLatestBackup(backupPath);

            if (bkp == null)
            {
                loader.Warn("No backup found! Was BSIPA installed using the installer?");
            }

            loader.Debug("Ensuring patch on UnityEngine.CoreModule exists");

            #region Insert patch into UnityEngine.CoreModule.dll

            {
                var unityPath = Path.Combine(managedPath,
                                             "UnityEngine.CoreModule.dll");

                // this is a critical section because if you exit in here, CoreModule can die
                CriticalSection.EnterExecuteSection();

                var unityAsmDef = AssemblyDefinition.ReadAssembly(unityPath, new ReaderParameters
                {
                    ReadWrite   = false,
                    InMemory    = true,
                    ReadingMode = ReadingMode.Immediate
                });
                var unityModDef = unityAsmDef.MainModule;

                bool modified = false;
                foreach (var asmref in unityModDef.AssemblyReferences)
                {
                    if (asmref.Name == cAsmName.Name)
                    {
                        if (asmref.Version != cAsmName.Version)
                        {
                            asmref.Version = cAsmName.Version;
                            modified       = true;
                        }
                    }
                }

                var application = unityModDef.GetType("UnityEngine", "Application");

                MethodDefinition cctor = null;
                foreach (var m in application.Methods)
                {
                    if (m.IsRuntimeSpecialName && m.Name == ".cctor")
                    {
                        cctor = m;
                    }
                }

                var cbs = unityModDef.ImportReference(((Action)CreateBootstrapper).Method);

                if (cctor == null)
                {
                    cctor = new MethodDefinition(".cctor",
                                                 MethodAttributes.RTSpecialName | MethodAttributes.Static | MethodAttributes.SpecialName,
                                                 unityModDef.TypeSystem.Void);
                    application.Methods.Add(cctor);
                    modified = true;

                    var ilp = cctor.Body.GetILProcessor();
                    ilp.Emit(OpCodes.Call, cbs);
                    ilp.Emit(OpCodes.Ret);
                }
                else
                {
                    var ilp = cctor.Body.GetILProcessor();
                    for (var i = 0; i < Math.Min(2, cctor.Body.Instructions.Count); i++)
                    {
                        var ins = cctor.Body.Instructions[i];
                        switch (i)
                        {
                        case 0 when ins.OpCode != OpCodes.Call:
                            ilp.Replace(ins, ilp.Create(OpCodes.Call, cbs));
                            modified = true;
                            break;

                        case 0:
                        {
                            var methodRef = ins.Operand as MethodReference;
                            if (methodRef?.FullName != cbs.FullName)
                            {
                                ilp.Replace(ins, ilp.Create(OpCodes.Call, cbs));
                                modified = true;
                            }

                            break;
                        }

                        case 1 when ins.OpCode != OpCodes.Ret:
                            ilp.Replace(ins, ilp.Create(OpCodes.Ret));
                            modified = true;
                            break;
                        }
                    }
                }

                if (modified)
                {
                    bkp?.Add(unityPath);
                    unityAsmDef.Write(unityPath);
                }

                CriticalSection.ExitExecuteSection();
            }

            #endregion Insert patch into UnityEngine.CoreModule.dll

            loader.Debug("Ensuring Assembly-CSharp is virtualized");

            {
                var ascPath = Path.Combine(managedPath,
                                           "MainAssembly.dll"); // TODO: change to config option for other games

                #region Virtualize Assembly-CSharp.dll

                {
                    CriticalSection.EnterExecuteSection();

                    try
                    {
                        var ascModule = VirtualizedModule.Load(ascPath);
                        ascModule.Virtualize(cAsmName, () => bkp?.Add(ascPath));
                    }
                    catch (Exception e)
                    {
                        loader.Error($"Could not virtualize {ascPath}");
                        loader.Error(e);
                    }

                    CriticalSection.ExitExecuteSection();
                }

                #endregion Virtualize Assembly-CSharp.dll

                #region Anti-Yeet

                CriticalSection.EnterExecuteSection();

                try
                {
                    loader.Debug("Applying anti-yeet patch");

                    var ascAsmDef = AssemblyDefinition.ReadAssembly(ascPath, new ReaderParameters
                    {
                        ReadWrite   = false,
                        InMemory    = true,
                        ReadingMode = ReadingMode.Immediate
                    });
                    var ascModDef = ascAsmDef.MainModule;

                    var deleter = ascModDef.GetType("IPAPluginsDirDeleter");
                    deleter.Methods.Clear(); // delete all methods

                    ascAsmDef.Write(ascPath);
                }
                catch (Exception)
                {
                    // ignore
                }

                CriticalSection.ExitExecuteSection();

                #endregion
            }
        }
Exemple #16
0
 internal static string GlobalGameManagers(string installDir) =>
 Path.Combine(ResolveDataPath(installDir), "globalgamemanagers");
        public static bool IsInvalid(string path)
        {
            var dataPlugins = Path.Combine(GameVersionEarly.ResolveDataPath(path), "Plugins");

            return(false);
        }
        internal static void LoadMetadata()
        {
            string[] plugins = Directory.GetFiles(UnityGame.PluginsPath, "*.dll");

            try
            {
                var selfMeta = new PluginMetadata
                {
                    Assembly   = Assembly.GetExecutingAssembly(),
                    File       = new FileInfo(Path.Combine(UnityGame.InstallPath, "IPA.exe")),
                    PluginType = null,
                    IsSelf     = true
                };

                string manifest;
                using (var manifestReader =
                           new StreamReader(
                               selfMeta.Assembly.GetManifestResourceStream(typeof(PluginLoader), "manifest.json") ??
                               throw new InvalidOperationException()))
                    manifest = manifestReader.ReadToEnd();

                selfMeta.Manifest = JsonConvert.DeserializeObject <PluginManifest>(manifest);

                PluginsMetadata.Add(selfMeta);
                SelfMeta = selfMeta;
            }
            catch (Exception e)
            {
                Logger.loader.Critical("Error loading own manifest");
                Logger.loader.Critical(e);
            }

            var resolver = new CecilLibLoader();

            resolver.AddSearchDirectory(UnityGame.LibraryPath);
            resolver.AddSearchDirectory(UnityGame.PluginsPath);
            foreach (var plugin in plugins)
            {
                var metadata = new PluginMetadata
                {
                    File   = new FileInfo(Path.Combine(UnityGame.PluginsPath, plugin)),
                    IsSelf = false
                };

                try
                {
                    var pluginModule = AssemblyDefinition.ReadAssembly(plugin, new ReaderParameters
                    {
                        ReadingMode      = ReadingMode.Immediate,
                        ReadWrite        = false,
                        AssemblyResolver = resolver
                    }).MainModule;

                    string pluginNs = "";

                    foreach (var resource in pluginModule.Resources)
                    {
                        const string manifestSuffix = ".manifest.json";
                        if (!(resource is EmbeddedResource embedded) ||
                            !embedded.Name.EndsWith(manifestSuffix))
                        {
                            continue;
                        }

                        pluginNs = embedded.Name.Substring(0, embedded.Name.Length - manifestSuffix.Length);

                        string manifest;
                        using (var manifestReader = new StreamReader(embedded.GetResourceStream()))
                            manifest = manifestReader.ReadToEnd();

                        metadata.Manifest = JsonConvert.DeserializeObject <PluginManifest>(manifest);
                        break;
                    }

                    if (metadata.Manifest == null)
                    {
#if DIRE_LOADER_WARNINGS
                        Logger.loader.Error($"Could not find manifest.json for {Path.GetFileName(plugin)}");
#else
                        Logger.loader.Notice($"No manifest.json in {Path.GetFileName(plugin)}");
#endif
                        continue;
                    }

                    void TryGetNamespacedPluginType(string ns, PluginMetadata meta)
                    {
                        foreach (var type in pluginModule.Types)
                        {
                            if (type.Namespace != ns)
                            {
                                continue;
                            }

                            if (type.HasCustomAttributes)
                            {
                                var attr = type.CustomAttributes.FirstOrDefault(a => a.Constructor.DeclaringType.FullName == typeof(PluginAttribute).FullName);
                                if (attr != null)
                                {
                                    if (!attr.HasConstructorArguments)
                                    {
                                        Logger.loader.Warn($"Attribute plugin found in {type.FullName}, but attribute has no arguments");
                                        return;
                                    }

                                    var args = attr.ConstructorArguments;
                                    if (args.Count != 1)
                                    {
                                        Logger.loader.Warn($"Attribute plugin found in {type.FullName}, but attribute has unexpected number of arguments");
                                        return;
                                    }
                                    var rtOptionsArg = args[0];
                                    if (rtOptionsArg.Type.FullName != typeof(RuntimeOptions).FullName)
                                    {
                                        Logger.loader.Warn($"Attribute plugin found in {type.FullName}, but first argument is of unexpected type {rtOptionsArg.Type.FullName}");
                                        return;
                                    }

                                    var rtOptionsValInt = (int)rtOptionsArg.Value; // `int` is the underlying type of RuntimeOptions

                                    meta.RuntimeOptions = (RuntimeOptions)rtOptionsValInt;
                                    meta.PluginType     = type;
                                    return;
                                }
                            }
                        }
                    }

                    var hint = metadata.Manifest.Misc?.PluginMainHint;

                    if (hint != null)
                    {
                        var type = pluginModule.GetType(hint);
                        if (type != null)
                        {
                            TryGetNamespacedPluginType(hint, metadata);
                        }
                    }

                    if (metadata.PluginType == null)
                    {
                        TryGetNamespacedPluginType(pluginNs, metadata);
                    }

                    if (metadata.PluginType == null)
                    {
                        Logger.loader.Error($"No plugin found in the manifest {(hint != null ? $"hint path ({hint}) or " : "")}namespace ({pluginNs}) in {Path.GetFileName(plugin)}");
                        continue;
                    }

                    Logger.loader.Debug($"Adding info for {Path.GetFileName(plugin)}");
                    PluginsMetadata.Add(metadata);
                }
                catch (Exception e)
                {
                    Logger.loader.Error($"Could not load data for plugin {Path.GetFileName(plugin)}");
                    Logger.loader.Error(e);
                    ignoredPlugins.Add(metadata, new IgnoreReason(Reason.Error)
                    {
                        ReasonText = "An error ocurred loading the data",
                        Error      = e
                    });
                }
            }

            IEnumerable <string> bareManifests = Directory.GetFiles(UnityGame.PluginsPath, "*.json");
            bareManifests = bareManifests.Concat(Directory.GetFiles(UnityGame.PluginsPath, "*.manifest"));
            foreach (var manifest in bareManifests)
            {
                try
                {
                    var metadata = new PluginMetadata
                    {
                        File   = new FileInfo(Path.Combine(UnityGame.PluginsPath, manifest)),
                        IsSelf = false,
                        IsBare = true,
                    };

                    metadata.Manifest = JsonConvert.DeserializeObject <PluginManifest>(File.ReadAllText(manifest));

                    if (metadata.Manifest.Files.Length < 1)
                    {
                        Logger.loader.Warn($"Bare manifest {Path.GetFileName(manifest)} does not declare any files. " +
                                           $"Dependency resolution and verification cannot be completed.");
                    }

                    Logger.loader.Debug($"Adding info for bare manifest {Path.GetFileName(manifest)}");
                    PluginsMetadata.Add(metadata);
                }
                catch (Exception e)
                {
                    Logger.loader.Error($"Could not load data for bare manifest {Path.GetFileName(manifest)}");
                    Logger.loader.Error(e);
                }
            }

            foreach (var meta in PluginsMetadata)
            { // process description include
                var lines = meta.Manifest.Description.Split('\n');
                var m     = embeddedTextDescriptionPattern.Match(lines[0]);
                if (m.Success)
                {
                    if (meta.IsBare)
                    {
                        Logger.loader.Warn($"Bare manifest cannot specify description file");
                        meta.Manifest.Description = string.Join("\n", lines.Skip(1).StrJP()); // ignore first line
                        continue;
                    }

                    var    name = m.Groups[1].Value;
                    string description;
                    if (!meta.IsSelf)
                    {
                        var resc = meta.PluginType.Module.Resources.Select(r => r as EmbeddedResource)
                                   .NonNull()
                                   .FirstOrDefault(r => r.Name == name);
                        if (resc == null)
                        {
                            Logger.loader.Warn($"Could not find description file for plugin {meta.Name} ({name}); ignoring include");
                            meta.Manifest.Description = string.Join("\n", lines.Skip(1).StrJP()); // ignore first line
                            continue;
                        }

                        using var reader = new StreamReader(resc.GetResourceStream());
                        description      = reader.ReadToEnd();
                    }
                    else
                    {
                        using var descriptionReader = new StreamReader(meta.Assembly.GetManifestResourceStream(name));
                        description = descriptionReader.ReadToEnd();
                    }

                    meta.Manifest.Description = description;
                }
            }
        }