// https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/file-system/how-to-iterate-through-a-directory-tree private static IEnumerable <FileInfo> TraverseTree(string root, Func <string, bool> dirValidator = null) { if (dirValidator == null) { dirValidator = s => true; } Stack <string> dirs = new Stack <string>(32); if (!Directory.Exists(root)) { throw new ArgumentException(); } dirs.Push(root); while (dirs.Count > 0) { string currentDir = dirs.Pop(); string[] subDirs; try { subDirs = Directory.GetDirectories(currentDir); } catch (UnauthorizedAccessException) { continue; } catch (DirectoryNotFoundException) { continue; } string[] files; try { files = Directory.GetFiles(currentDir); } catch (UnauthorizedAccessException) { continue; } catch (DirectoryNotFoundException) { continue; } foreach (string str in subDirs) { if (dirValidator(str)) { dirs.Push(str); } } foreach (string file in files) { FileInfo nextValue; try { nextValue = new FileInfo(file); } catch (FileNotFoundException) { continue; } yield return(nextValue); } } }
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 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); } }
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("-----------------------------"); }