예제 #1
0
        // 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);
                }
            }
        }
예제 #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);
        }
예제 #3
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);
                }
            }
        }
예제 #4
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);
        }
예제 #5
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);
            }
        }
        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);
                }
            }
        }
예제 #7
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 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;
                }
            }
        }
예제 #9
0
 internal static string ResolveDataPath(string installDir) =>
 Directory.EnumerateDirectories(installDir, "*_Data").First();
        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("-----------------------------");
        }