Example #1
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
            }
        }
Example #2
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}");
        }