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