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 } }
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}"); }