Example #1
0
        public static PluginInfo ToPluginInfo(TypeDefinition type)
        {
            if (type.IsInterface || type.IsAbstract)
            {
                return(null);
            }

            try
            {
                if (!type.IsSubtypeOf(typeof(BaseUnityPlugin)))
                {
                    return(null);
                }
            }
            catch (AssemblyResolutionException)
            {
                // Can happen if this type inherits a type from an assembly that can't be found. Safe to assume it's not a plugin.
                return(null);
            }

            var metadata = BepInPlugin.FromCecilType(type);

            // Perform checks that will prevent the plugin from being loaded in ALL cases
            if (metadata == null)
            {
                Logger.LogWarning($"Skipping over type [{type.FullName}] as no metadata attribute is specified");
                return(null);
            }

            if (string.IsNullOrEmpty(metadata.GUID) || !allowedGuidRegex.IsMatch(metadata.GUID))
            {
                Logger.LogWarning($"Skipping type [{type.FullName}] because its GUID [{metadata.GUID}] is of an illegal format.");
                return(null);
            }

            if (metadata.Version == null)
            {
                Logger.LogWarning($"Skipping type [{type.FullName}] because its version is invalid.");
                return(null);
            }

            if (metadata.Name == null)
            {
                Logger.LogWarning($"Skipping type [{type.FullName}] because its name is null.");
                return(null);
            }

            var filters           = BepInProcess.FromCecilType(type);
            var dependencies      = BepInDependency.FromCecilType(type);
            var incompatibilities = BepInIncompatibility.FromCecilType(type);

            return(new PluginInfo
            {
                Metadata = metadata,
                Processes = filters,
                Dependencies = dependencies,
                Incompatibilities = incompatibilities,
                TypeName = type.FullName
            });
        }
Example #2
0
 private void OnInstallUnityTlsInterface(IntPtr unityTlsInterfaceStruct)
 {
     Logger.LogDebug($"Captured UnityTls interface at {unityTlsInterfaceStruct.ToInt64():x8}");
     Il2CppTlsAdapter.Options.UnityTlsInterface = unityTlsInterfaceStruct;
     originalInstallUnityTlsInterface(unityTlsInterfaceStruct);
     InstallUnityTlsInterfaceDetour.Dispose();
     InstallUnityTlsInterfaceDetour = null;
 }
 void Update()
 {
     if (Refresh.Value.IsDown())
     {
         Logger.LogInfo("Refreshing!");
         LoadSongs();
     }
 }
Example #4
0
        /// <summary>
        /// Initializes BepInEx to be able to start the chainloader.
        /// </summary>
        public static void Initialize(string gameExePath, bool startConsole = true)
        {
            if (_initialized)
            {
                return;
            }

            ThreadingHelper.Initialize();

            // Set vitals
            if (gameExePath != null)
            {
                // Checking for null allows a more advanced initialization workflow, where the Paths class has been initialized before calling Chainloader.Initialize
                // This is used by Preloader to use environment variables, for example
                Paths.SetExecutablePath(gameExePath);
            }

            // Start logging
            if (ConsoleWindow.ConfigConsoleEnabled.Value && startConsole)
            {
                ConsoleWindow.Attach();
                Logger.Listeners.Add(new ConsoleLogListener());
            }

            // Fix for standard output getting overwritten by UnityLogger
            if (ConsoleWindow.StandardOut != null)
            {
                Console.SetOut(ConsoleWindow.StandardOut);

                var encoding = ConsoleWindow.ConfigConsoleShiftJis.Value ? 932 : (uint)Encoding.UTF8.CodePage;
                ConsoleEncoding.ConsoleCodePage = encoding;
                Console.OutputEncoding          = ConsoleEncoding.GetEncoding(encoding);
            }

            Logger.Listeners.Add(new UnityLogListener());

            if (ConfigDiskLogging.Value)
            {
                Logger.Listeners.Add(new DiskLogListener("LogOutput.log", ConfigDiskConsoleDisplayedLevel.Value, ConfigDiskAppend.Value, ConfigDiskWriteUnityLog.Value));
            }

            if (!TraceLogSource.IsListening)
            {
                Logger.Sources.Add(TraceLogSource.CreateSource());
            }

            if (ConfigUnityLogging.Value)
            {
                Logger.Sources.Add(new UnityLogSource());
            }


            Logger.LogMessage("Chainloader ready");

            _initialized = true;
        }
Example #5
0
        /// <summary>
        /// Initializes BepInEx to be able to start the chainloader.
        /// </summary>
        public static void Initialize(string gameExePath, bool startConsole = true, ICollection <LogEventArgs> preloaderLogEvents = null)
        {
            if (_initialized)
            {
                return;
            }

            ThreadingHelper.Initialize();

            // Set vitals
            if (gameExePath != null)
            {
                // Checking for null allows a more advanced initialization workflow, where the Paths class has been initialized before calling Chainloader.Initialize
                // This is used by Preloader to use environment variables, for example
                Paths.SetExecutablePath(gameExePath);
            }

            // Start logging
            if (ConsoleManager.ConfigConsoleEnabled.Value && startConsole)
            {
                ConsoleManager.CreateConsole();
                Logger.Listeners.Add(new ConsoleLogListener());
            }

            Logger.InitializeInternalLoggers();

            if (ConfigDiskLogging.Value)
            {
                Logger.Listeners.Add(new DiskLogListener("LogOutput.log", ConfigDiskConsoleDisplayedLevel.Value, ConfigDiskAppend.Value, ConfigDiskWriteUnityLog.Value));
            }

            if (!TraceLogSource.IsListening)
            {
                Logger.Sources.Add(TraceLogSource.CreateSource());
            }

            ReplayPreloaderLogs(preloaderLogEvents);

            // Add Unity log source only after replaying to prevent duplication in console
            if (ConfigUnityLogging.Value)
            {
                Logger.Sources.Add(new UnityLogSource());
            }
            Logger.Listeners.Add(new UnityLogListener());

            if (Utility.CurrentOs == Platform.Linux)
            {
                Logger.LogInfo($"Detected Unity version: v{Application.unityVersion}");
            }

            Logger.LogMessage("Chainloader ready");

            _initialized = true;
        }
        void Awake()
        {
            CustomBeatsPlugin.INSTANCE = this;
            CustomBeatsPlugin.LOGGER   = this.Logger;

            Logger.LogInfo("Helloooooooo, world!");
            Logger.LogInfo(Application.streamingAssetsPath);

            harmony = Harmony.CreateAndPatchAll(typeof(Patches));

            LoadSongs();
        }
Example #7
0
        /// <summary>
        /// Attempt to deserialize key combination from the string.
        /// </summary>
        public static KeyboardShortcut Deserialize(string str)
        {
            try
            {
                var parts = str.Split(new[] { ' ', '+', ',', ';', '|' }, StringSplitOptions.RemoveEmptyEntries)
                            .Select(x => (KeyCode)Enum.Parse(typeof(KeyCode), x)).ToArray();

                return(new KeyboardShortcut(parts));
            }
            catch (SystemException ex)
            {
                Logger.Log(LogLevel.Error, "Failed to read keybind from settings: " + ex.Message);
                return(Empty);
            }
        }
Example #8
0
        /// <summary>
        /// Initializes BepInEx to be able to start the chainloader.
        /// </summary>
        public static void Initialize(string containerExePath, bool startConsole = true)
        {
            if (_initialized)
            {
                return;
            }

            //Set vitals
            Paths.SetExecutablePath(containerExePath);

            Paths.SetPluginPath(ConfigPluginsDirectory.Value);

            //Start logging

            if (startConsole)
            {
                ConsoleWindow.Attach();

                ConsoleEncoding.ConsoleCodePage = (uint)Encoding.UTF8.CodePage;
                Console.OutputEncoding          = Encoding.UTF8;
                Logger.Listeners.Add(new ConsoleLogListener());
            }

            //Fix for standard output getting overwritten by UnityLogger
            if (ConsoleWindow.StandardOut != null)
            {
                Console.SetOut(ConsoleWindow.StandardOut);
            }


            Logger.Listeners.Add(new UnityLogListener());
            Logger.Listeners.Add(new DiskLogListener());

            if (!TraceLogSource.IsListening)
            {
                Logger.Sources.Add(TraceLogSource.CreateSource());
            }

            if (ConfigUnityLogging.Value)
            {
                Logger.Sources.Add(new UnityLogSource());
            }


            Logger.LogMessage("Chainloader ready");

            _initialized = true;
        }
Example #9
0
        public override unsafe void Initialize(string gameExePath = null)
        {
            UnhollowerBaseLib.GeneratedDatabasesUtil.DatabasesLocationOverride = Preloader.IL2CPPUnhollowedPath;
            PatchManager.ResolvePatcher += IL2CPPDetourMethodPatcher.TryResolve;

            base.Initialize(gameExePath);
            Instance = this;

            var version =             //Version.Parse(Application.unityVersion);
                          Version.Parse(Process.GetCurrentProcess().MainModule.FileVersionInfo.FileVersion);

            UnityVersionHandler.Initialize(version.Major, version.Minor, version.Revision);

            // One or the other here for Unhollower to work correctly

            //ClassInjector.Detour = new DetourHandler();

            ClassInjector.DoHook = (ptr, patchedFunctionPtr) =>
            {
                IntPtr originalFunc = new IntPtr(*(void **)ptr);

                var detour = new FastNativeDetour(originalFunc, patchedFunctionPtr);

                detour.Apply();

                *(void **)ptr = (void *)detour.TrampolinePtr;
            };

            var gameAssemblyModule = Process.GetCurrentProcess().Modules.Cast <ProcessModule>().First(x => x.ModuleName.Contains("GameAssembly"));

            // TODO: Check that DynDll.GetFunction works fine now
            var runtimeInvokePtr = GetProcAddress(gameAssemblyModule.BaseAddress, "il2cpp_runtime_invoke");             //DynDll.GetFunction(gameAssemblyModule.BaseAddress, "il2cpp_runtime_invoke");

            PreloaderLogger.Log.LogDebug($"Runtime invoke pointer: 0x{runtimeInvokePtr.ToInt64():X}");
            RuntimeInvokeDetour = FastNativeDetour.CreateAndApply(runtimeInvokePtr, OnInvokeMethod, out originalInvoke, CallingConvention.Cdecl);

            var installTlsPtr = GetProcAddress(gameAssemblyModule.BaseAddress, "il2cpp_unity_install_unitytls_interface");

            if (installTlsPtr != IntPtr.Zero)
            {
                InstallUnityTlsInterfaceDetour = FastNativeDetour.CreateAndApply(installTlsPtr, OnInstallUnityTlsInterface, out originalInstallUnityTlsInterface, CallingConvention.Cdecl);
            }

            Logger.LogDebug("Initializing TLS adapters");
            Il2CppTlsAdapter.Initialize();

            PreloaderLogger.Log.LogDebug("Runtime invoke patched");
        }
Example #10
0
        public static PluginInfo ToPluginInfo(TypeDefinition type)
        {
            if (type.IsInterface || type.IsAbstract || !type.IsSubtypeOf(typeof(BaseUnityPlugin)))
            {
                return(null);
            }

            var metadata = BepInPlugin.FromCecilType(type);

            // Perform checks that will prevent the plugin from being loaded in ALL cases
            if (metadata == null)
            {
                Logger.LogWarning($"Skipping over type [{type.FullName}] as no metadata attribute is specified");
                return(null);
            }

            if (string.IsNullOrEmpty(metadata.GUID) || !allowedGuidRegex.IsMatch(metadata.GUID))
            {
                Logger.LogWarning($"Skipping type [{type.FullName}] because its GUID [{metadata.GUID}] is of an illegal format.");
                return(null);
            }

            if (metadata.Version == null)
            {
                Logger.LogWarning($"Skipping type [{type.FullName}] because its version is invalid.");
                return(null);
            }

            if (metadata.Name == null)
            {
                Logger.LogWarning($"Skipping type [{type.FullName}] because its name is null.");
                return(null);
            }

            var filters      = BepInProcess.FromCecilType(type);
            var dependencies = BepInDependency.FromCecilType(type);

            return(new PluginInfo
            {
                Metadata = metadata,
                Processes = filters,
                Dependencies = dependencies,
                TypeName = type.FullName
            });
        }
Example #11
0
        /// <summary>
        ///     Initialize Registry with needed data
        /// </summary>
        /// <param name="bundleName">Name of bundle to load</param>
        /// <param name="keyword">UNIQUE keyword of your mod</param>
        /// <param name="requireBundle">Do you need to load asset bundles</param>
        /// <param name="requireVerta">Do you need to load verta files</param>
        public static void Init(string bundleName, string keyword, bool requireBundle, bool requireVerta)
        {
            LogSource = Logger.CreateLogSource("Registry-" + keyword);
            keyWord   = keyword;
            //get location of the plugin
            string pluginfolder = Path.GetDirectoryName(Assembly.GetAssembly(typeof(Registry)).Location);

            int MainTex     = Shader.PropertyToID("_MainTex");
            int NormalTex   = Shader.PropertyToID("_NormalTex");
            int MSTex       = Shader.PropertyToID("_MS_Tex");
            int EmissionTex = Shader.PropertyToID("_EmissionTex");

            textureNames = new[] { MainTex, NormalTex, MSTex, EmissionTex };


            FileInfo folder  = new FileInfo($"{pluginfolder}/Verta/");
            FileInfo folder1 = new FileInfo($"{pluginfolder}/plugins/");

            if (Directory.Exists(folder.Directory?.FullName))
            {
                vertaFolder = pluginfolder;
            }
            else if (Directory.Exists(folder1.Directory?.FullName))
            {
                vertaFolder = $"{pluginfolder}/plugins";
            }
            else if (requireVerta)
            {
                vertaFolder = "";
                LogSource.LogError("Cannot find folder with verta files. Mod WILL not work!");
                return;
            }

            if (requireBundle)
            {
                //load assetbundle then load the prefab
                bundle = AssetBundle.LoadFromFile($"{pluginfolder}/{bundleName}");
            }

            LDBTool.PostAddDataAction += onPostAdd;
            LDBTool.EditDataAction    += EditProto;

            Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly());
        }
Example #12
0
    public override void Initialize(string gameExePath = null)
    {
        GeneratedDatabasesUtil.DatabasesLocationOverride = Preloader.IL2CPPUnhollowedPath;
        PatchManager.ResolvePatcher += IL2CPPDetourMethodPatcher.TryResolve;

        base.Initialize(gameExePath);
        Instance = this;

        ClassInjector.Detour = new UnhollowerDetourHandler();

        var gameAssemblyModule = Process.GetCurrentProcess().Modules.Cast <ProcessModule>()
                                 .FirstOrDefault(x => x.ModuleName.Contains("GameAssembly") ||
                                                 x.ModuleName.Contains("UserAssembly"));

        if (gameAssemblyModule == null)
        {
            Logger.Log(LogLevel.Fatal,
                       "Could not locate Il2Cpp game assembly (GameAssembly.dll) or (UserAssembly.dll). The game might be obfuscated or use a yet unsupported build of Unity.");
            return;
        }

        gameAssemblyModule.BaseAddress.TryGetFunction("il2cpp_runtime_invoke", out var runtimeInvokePtr);
        PreloaderLogger.Log.Log(LogLevel.Debug, $"Runtime invoke pointer: 0x{runtimeInvokePtr.ToInt64():X}");
        RuntimeInvokeDetour =
            FastNativeDetour.CreateAndApply(runtimeInvokePtr, OnInvokeMethod, out originalInvoke,
                                            CallingConvention.Cdecl);

        if (gameAssemblyModule.BaseAddress.TryGetFunction("il2cpp_unity_install_unitytls_interface",
                                                          out var installTlsPtr))
        {
            InstallUnityTlsInterfaceDetour =
                FastNativeDetour.CreateAndApply(installTlsPtr, OnInstallUnityTlsInterface,
                                                out originalInstallUnityTlsInterface, CallingConvention.Cdecl);
        }

        Logger.Log(LogLevel.Debug, "Initializing TLS adapters");
        Il2CppTlsAdapter.Initialize();

        PreloaderLogger.Log.Log(LogLevel.Debug, "Runtime invoke patched");
    }
        private static IntPtr OnInvokeMethod(IntPtr method, IntPtr obj, IntPtr parameters, IntPtr exc)
        {
            var methodName = Marshal.PtrToStringAnsi(UnhollowerBaseLib.IL2CPP.il2cpp_method_get_name(method));

            var unhook = false;

            if (methodName == "Internal_ActiveSceneChanged")
            {
                try
                {
                    if (ConfigUnityLogging.Value)
                    {
                        Logger.Sources.Add(new IL2CPPUnityLogSource());

                        Application.CallLogCallback("Test call after applying unity logging hook", "", LogType.Assert,
                                                    true);
                    }

                    unhook = true;

                    Instance.Execute();
                }
                catch (Exception ex)
                {
                    Logger.LogFatal("Unable to execute IL2CPP chainloader");
                    Logger.LogError(ex);
                }
            }

            var result = originalInvoke(method, obj, parameters, exc);

            if (unhook)
            {
                RuntimeInvokeDetour.Dispose();

                PreloaderLogger.Log.LogDebug("Runtime invoke unpatched");
            }

            return(result);
        }
Example #14
0
        public RichPresence(IntPtr intPtr) : base(intPtr)
        {
            log              = Logger.CreateLogSource("Discord Rich Presence");
            _discord         = new Discord.Discord(764433332330037249L, 0UL);
            _activityManager = _discord.GetActivityManager();
            // TODO: ?
            _activityManager.RegisterCommand("gtfo://run");
            _activityManager.RegisterSteam(493520);

            _currentTime = _currentTime.AddSeconds(10);

            _activityManager.OnActivityJoin += Events.OnActivityJoin;
            log.LogMessage("RichPresence created.");

            Activity activity = new Activity()
            {
                State   = "Playing GTFO",
                Details = "Selecting an expedition."
            };

            SetActivity(activity);
        }
Example #15
0
        static UnityLogListener()
        {
            foreach (var methodInfo in typeof(UnityLogWriter).GetMethods(BindingFlags.Static | BindingFlags.Public))
            {
                try
                {
                    methodInfo.Invoke(null, new object[] { "" });
                }
                catch
                {
                    continue;
                }

                WriteStringToUnityLog = (Action <string>)Delegate.CreateDelegate(typeof(Action <string>), methodInfo);
                break;
            }

            if (WriteStringToUnityLog == null)
            {
                Logger.LogError("Unable to start Unity log writer");
            }
        }
        public override void Initialize(string gameExePath = null)
        {
            GeneratedDatabasesUtil.DatabasesLocationOverride = Preloader.IL2CPPUnhollowedPath;
            PatchManager.ResolvePatcher += IL2CPPDetourMethodPatcher.TryResolve;

            base.Initialize(gameExePath);
            Instance = this;

            ClassInjector.Detour = new UnhollowerDetourHandler();

            var gameAssemblyModule = Process.GetCurrentProcess().Modules.Cast <ProcessModule>()
                                     .First(x => x.ModuleName.Contains("GameAssembly"));

            // TODO: Check that DynDll.GetFunction works fine now
            var runtimeInvokePtr =
                GetProcAddress(gameAssemblyModule.BaseAddress,
                               "il2cpp_runtime_invoke"); //DynDll.GetFunction(gameAssemblyModule.BaseAddress, "il2cpp_runtime_invoke");

            PreloaderLogger.Log.LogDebug($"Runtime invoke pointer: 0x{runtimeInvokePtr.ToInt64():X}");
            RuntimeInvokeDetour =
                FastNativeDetour.CreateAndApply(runtimeInvokePtr, OnInvokeMethod, out originalInvoke,
                                                CallingConvention.Cdecl);

            var installTlsPtr =
                GetProcAddress(gameAssemblyModule.BaseAddress, "il2cpp_unity_install_unitytls_interface");

            if (installTlsPtr != IntPtr.Zero)
            {
                InstallUnityTlsInterfaceDetour =
                    FastNativeDetour.CreateAndApply(installTlsPtr, OnInstallUnityTlsInterface,
                                                    out originalInstallUnityTlsInterface, CallingConvention.Cdecl);
            }

            Logger.LogDebug("Initializing TLS adapters");
            Il2CppTlsAdapter.Initialize();

            PreloaderLogger.Log.LogDebug("Runtime invoke patched");
        }
Example #17
0
        private static void ReplayPreloaderLogs(ICollection <LogEventArgs> preloaderLogEvents)
        {
            if (preloaderLogEvents == null)
            {
                return;
            }

            var unityLogger = new UnityLogListener();

            Logger.Listeners.Add(unityLogger);

            // Temporarily disable the console log listener (if there is one from preloader) as we replay the preloader logs
            var logListener = Logger.Listeners.FirstOrDefault(logger => logger is ConsoleLogListener);

            if (logListener != null)
            {
                Logger.Listeners.Remove(logListener);
            }

            // Write preloader log events if there are any, including the original log source name
            var preloaderLogSource = Logger.CreateLogSource("Preloader");

            foreach (var preloaderLogEvent in preloaderLogEvents)
            {
                Logger.InternalLogEvent(preloaderLogSource, preloaderLogEvent);
            }

            Logger.Sources.Remove(preloaderLogSource);

            Logger.Listeners.Remove(unityLogger);

            if (logListener != null)
            {
                Logger.Listeners.Add(logListener);
            }
        }
Example #18
0
        /// <summary>
        /// The entrypoint for the BepInEx plugin system.
        /// </summary>
        public static void Start()
        {
            if (_loaded)
            {
                return;
            }

            if (!_initialized)
            {
                throw new InvalidOperationException("BepInEx has not been initialized. Please call Chainloader.Initialize prior to starting the chainloader instance.");
            }

            if (!Directory.Exists(Paths.PluginPath))
            {
                Directory.CreateDirectory(Paths.PluginPath);
            }

            if (!Directory.Exists(Paths.PatcherPluginPath))
            {
                Directory.CreateDirectory(Paths.PatcherPluginPath);
            }

            try
            {
                var productNameProp = typeof(Application).GetProperty("productName", BindingFlags.Public | BindingFlags.Static);

                if (ConsoleManager.ConsoleActive)
                {
                    ConsoleManager.SetConsoleTitle($"{CurrentAssemblyName} {CurrentAssemblyVersion} - {productNameProp?.GetValue(null, null) ?? Paths.ProcessName}");
                }

                Logger.LogMessage("Chainloader started");

                ManagerObject = new GameObject("BepInEx_Manager");

                UnityEngine.Object.DontDestroyOnLoad(ManagerObject);

                var pluginsToLoad = TypeLoader.FindPluginTypes(Paths.PluginPath, ToPluginInfo, HasBepinPlugins, "chainloader");
                foreach (var keyValuePair in pluginsToLoad)
                {
                    foreach (var pluginInfo in keyValuePair.Value)
                    {
                        pluginInfo.Location = keyValuePair.Key;
                    }
                }
                var pluginInfos      = pluginsToLoad.SelectMany(p => p.Value).ToList();
                var loadedAssemblies = new Dictionary <string, Assembly>();

                Logger.LogInfo($"{pluginInfos.Count} plugins to load");

                // We use a sorted dictionary to ensure consistent load order
                var dependencyDict = new SortedDictionary <string, IEnumerable <string> >(StringComparer.InvariantCultureIgnoreCase);
                var pluginsByGUID  = new Dictionary <string, PluginInfo>();

                foreach (var pluginInfoGroup in pluginInfos.GroupBy(info => info.Metadata.GUID))
                {
                    PluginInfo loadedVersion = null;
                    foreach (var pluginInfo in pluginInfoGroup.OrderByDescending(x => x.Metadata.Version))
                    {
                        if (loadedVersion != null)
                        {
                            Logger.LogWarning($"Skipping [{pluginInfo}] because a newer version exists ({loadedVersion})");
                            continue;
                        }

                        loadedVersion = pluginInfo;

                        // Perform checks that will prevent loading plugins in this run
                        var  filters            = pluginInfo.Processes.ToList();
                        bool invalidProcessName = filters.Count != 0 && filters.All(x => !string.Equals(x.ProcessName.Replace(".exe", ""), Paths.ProcessName, StringComparison.InvariantCultureIgnoreCase));

                        if (invalidProcessName)
                        {
                            Logger.LogWarning($"Skipping [{pluginInfo}] because of process filters ({string.Join(", ", pluginInfo.Processes.Select(p => p.ProcessName).ToArray())})");
                            continue;
                        }

                        dependencyDict[pluginInfo.Metadata.GUID] = pluginInfo.Dependencies.Select(d => d.DependencyGUID);
                        pluginsByGUID[pluginInfo.Metadata.GUID]  = pluginInfo;
                    }
                }

                foreach (var pluginInfo in pluginsByGUID.Values.ToList())
                {
                    if (pluginInfo.Incompatibilities.Any(incompatibility => pluginsByGUID.ContainsKey(incompatibility.IncompatibilityGUID)))
                    {
                        pluginsByGUID.Remove(pluginInfo.Metadata.GUID);
                        dependencyDict.Remove(pluginInfo.Metadata.GUID);

                        var    incompatiblePlugins = pluginInfo.Incompatibilities.Select(x => x.IncompatibilityGUID).Where(x => pluginsByGUID.ContainsKey(x)).ToArray();
                        string message             = $@"Could not load [{pluginInfo}] because it is incompatible with: {string.Join(", ", incompatiblePlugins)}";
                        DependencyErrors.Add(message);
                        Logger.LogError(message);
                    }
                    else if (PluginTargetsWrongBepin(pluginInfo))
                    {
                        string message = $@"Plugin [{pluginInfo}] targets a wrong version of BepInEx ({pluginInfo.TargettedBepInExVersion}) and might not work until you update";
                        DependencyErrors.Add(message);
                        Logger.LogWarning(message);
                    }
                }

                var emptyDependencies = new string[0];

                // Sort plugins by their dependencies.
                // Give missing dependencies no dependencies of its own, which will cause missing plugins to be first in the resulting list.
                var sortedPlugins = Utility.TopologicalSort(dependencyDict.Keys, x => dependencyDict.TryGetValue(x, out var deps) ? deps : emptyDependencies).ToList();

                var invalidPlugins   = new HashSet <string>();
                var processedPlugins = new Dictionary <string, Version>();

                foreach (var pluginGUID in sortedPlugins)
                {
                    // If the plugin is missing, don't process it
                    if (!pluginsByGUID.TryGetValue(pluginGUID, out var pluginInfo))
                    {
                        continue;
                    }

                    var dependsOnInvalidPlugin = false;
                    var missingDependencies    = new List <BepInDependency>();
                    foreach (var dependency in pluginInfo.Dependencies)
                    {
                        bool IsHardDependency(BepInDependency dep) => (dep.Flags & BepInDependency.DependencyFlags.HardDependency) != 0;

                        // If the dependency wasn't already processed, it's missing altogether
                        bool dependencyExists = processedPlugins.TryGetValue(dependency.DependencyGUID, out var pluginVersion);
                        if (!dependencyExists || pluginVersion < dependency.MinimumVersion)
                        {
                            // If the dependency is hard, collect it into a list to show
                            if (IsHardDependency(dependency))
                            {
                                missingDependencies.Add(dependency);
                            }
                            continue;
                        }

                        // If the dependency is invalid (e.g. has missing dependencies) and hard, report that to the user
                        if (invalidPlugins.Contains(dependency.DependencyGUID) && IsHardDependency(dependency))
                        {
                            dependsOnInvalidPlugin = true;
                            break;
                        }
                    }

                    processedPlugins.Add(pluginGUID, pluginInfo.Metadata.Version);

                    if (dependsOnInvalidPlugin)
                    {
                        string message = $"Skipping [{pluginInfo}] because it has a dependency that was not loaded. See previous errors for details.";
                        DependencyErrors.Add(message);
                        Logger.LogWarning(message);
                        continue;
                    }

                    if (missingDependencies.Count != 0)
                    {
                        bool IsEmptyVersion(Version v) => v.Major == 0 && v.Minor == 0 && v.Build <= 0 && v.Revision <= 0;

                        string message = $@"Could not load [{pluginInfo}] because it has missing dependencies: {
							string.Join(", ", missingDependencies.Select(s => IsEmptyVersion(s.MinimumVersion) ? s.DependencyGUID : $"{s.DependencyGUID} (v{s.MinimumVersion} or newer)").ToArray())
							}"                            ;
                        DependencyErrors.Add(message);
                        Logger.LogError(message);

                        invalidPlugins.Add(pluginGUID);
                        continue;
                    }

                    try
                    {
                        Logger.LogInfo($"Loading [{pluginInfo}]");

                        if (!loadedAssemblies.TryGetValue(pluginInfo.Location, out var ass))
                        {
                            loadedAssemblies[pluginInfo.Location] = ass = Assembly.LoadFile(pluginInfo.Location);
                        }

                        PluginInfos[pluginGUID] = pluginInfo;
                        pluginInfo.Instance     = (BaseUnityPlugin)ManagerObject.AddComponent(ass.GetType(pluginInfo.TypeName));

                        _plugins.Add(pluginInfo.Instance);
                    }
                    catch (Exception ex)
                    {
                        invalidPlugins.Add(pluginGUID);
                        PluginInfos.Remove(pluginGUID);

                        Logger.LogError($"Error loading [{pluginInfo}] : {ex.Message}");
                        if (ex is ReflectionTypeLoadException re)
                        {
                            Logger.LogDebug(TypeLoader.TypeLoadExceptionToString(re));
                        }
                        else
                        {
                            Logger.LogDebug(ex);
                        }
                    }
                }
            }
Example #19
0
        /// <summary>
        /// The entrypoint for the BepInEx plugin system.
        /// </summary>
        public static void Start()
        {
            if (_loaded)
            {
                return;
            }

            if (!_initialized)
            {
                throw new InvalidOperationException("BepInEx has not been initialized. Please call Chainloader.Initialize prior to starting the chainloader instance.");
            }

            if (!Directory.Exists(Paths.PluginPath))
            {
                Directory.CreateDirectory(Paths.PluginPath);
            }

            if (!Directory.Exists(Paths.PatcherPluginPath))
            {
                Directory.CreateDirectory(Paths.PatcherPluginPath);
            }

            try
            {
                var productNameProp = typeof(Application).GetProperty("productName", BindingFlags.Public | BindingFlags.Static);
                if (productNameProp != null)
                {
                    ConsoleWindow.Title =
                        $"BepInEx {Assembly.GetExecutingAssembly().GetName().Version} - {productNameProp.GetValue(null, null)}";
                }

                Logger.LogMessage("Chainloader started");

                ManagerObject = new GameObject("BepInEx_Manager");

                UnityEngine.Object.DontDestroyOnLoad(ManagerObject);


                string currentProcess = Process.GetCurrentProcess().ProcessName.ToLower();

                var globalPluginTypes = TypeLoader.LoadTypes <BaseUnityPlugin>(Paths.PluginPath).ToList();

                var selectedPluginTypes = globalPluginTypes
                                          .Where(plugin =>
                {
                    //Ensure metadata exists
                    var metadata = MetadataHelper.GetMetadata(plugin);

                    if (metadata == null)
                    {
                        Logger.LogWarning($"Skipping over type [{plugin.Name}] as no metadata attribute is specified");
                        return(false);
                    }

                    //Perform a filter for currently running process
                    var filters = MetadataHelper.GetAttributes <BepInProcess>(plugin);

                    if (filters.Length == 0)                                                                       //no filters means it loads everywhere
                    {
                        return(true);
                    }

                    var result = filters.Any(x => x.ProcessName.ToLower().Replace(".exe", "") == currentProcess);

                    if (!result)
                    {
                        Logger.LogInfo($"Skipping over plugin [{metadata.GUID}] due to process filter");
                    }

                    return(result);
                })
                                          .ToList();

                Logger.LogInfo($"{selectedPluginTypes.Count} / {globalPluginTypes.Count} plugins to load");

                Dictionary <Type, IEnumerable <Type> > dependencyDict = new Dictionary <Type, IEnumerable <Type> >();


                foreach (Type t in selectedPluginTypes)
                {
                    try
                    {
                        IEnumerable <Type> dependencies = MetadataHelper.GetDependencies(t, selectedPluginTypes);

                        dependencyDict[t] = dependencies;
                    }
                    catch (MissingDependencyException)
                    {
                        var metadata = MetadataHelper.GetMetadata(t);

                        Logger.LogWarning($"Cannot load [{metadata.Name}] due to missing dependencies.");
                    }
                }

                var sortedTypes = Utility.TopologicalSort(dependencyDict.Keys, x => dependencyDict[x]).ToList();

                foreach (Type t in sortedTypes)
                {
                    try
                    {
                        var metadata = MetadataHelper.GetMetadata(t);
                        Logger.LogInfo($"Loading [{metadata.Name} {metadata.Version}]");

                        var plugin = (BaseUnityPlugin)ManagerObject.AddComponent(t);

                        Plugins.Add(plugin);
                    }
                    catch (Exception ex)
                    {
                        Logger.LogError($"Error loading [{t.Name}] : {ex.Message}");
                        Logger.LogDebug(ex);
                    }
                }
            }
            catch (Exception ex)
            {
                ConsoleWindow.Attach();

                Console.WriteLine("Error occurred starting the game");
                Console.WriteLine(ex.ToString());
            }

            Logger.LogMessage("Chainloader startup complete");

            _loaded = true;
        }
Example #20
0
        /// <summary>
        /// The entrypoint for the BepInEx plugin system.
        /// </summary>
        public static void Start()
        {
            if (_loaded)
            {
                return;
            }

            if (!_initialized)
            {
                throw new InvalidOperationException("BepInEx has not been initialized. Please call Chainloader.Initialize prior to starting the chainloader instance.");
            }

            if (!Directory.Exists(Paths.PluginPath))
            {
                Directory.CreateDirectory(Paths.PluginPath);
            }

            if (!Directory.Exists(Paths.PatcherPluginPath))
            {
                Directory.CreateDirectory(Paths.PatcherPluginPath);
            }

            try
            {
                var productNameProp = typeof(Application).GetProperty("productName", BindingFlags.Public | BindingFlags.Static);
                if (productNameProp != null)
                {
                    ConsoleWindow.Title =
                        $"BepInEx {Assembly.GetExecutingAssembly().GetName().Version} - {productNameProp.GetValue(null, null)}";
                }

                Logger.LogMessage("Chainloader started");

                ManagerObject = new GameObject("BepInEx_Manager");

                UnityEngine.Object.DontDestroyOnLoad(ManagerObject);


                string currentProcess = Process.GetCurrentProcess().ProcessName.ToLower();

                var globalPluginTypes = TypeLoader.LoadTypes <BaseUnityPlugin>(Paths.PluginPath).ToList();

                var selectedPluginTypes = globalPluginTypes
                                          .Where(plugin =>
                {
                    //Ensure metadata exists
                    var metadata = MetadataHelper.GetMetadata(plugin);

                    if (metadata == null)
                    {
                        Logger.LogWarning($"Skipping over type [{plugin.Name}] as no metadata attribute is specified");
                        return(false);
                    }

                    //Perform a filter for currently running process
                    var filters = MetadataHelper.GetAttributes <BepInProcess>(plugin);

                    if (filters.Length == 0)                                                                       //no filters means it loads everywhere
                    {
                        return(true);
                    }

                    var result = filters.Any(x => x.ProcessName.ToLower().Replace(".exe", "") == currentProcess);

                    if (!result)
                    {
                        Logger.LogInfo($"Skipping over plugin [{metadata.GUID}] due to process filter");
                    }

                    return(result);
                })
                                          .ToList();

                Logger.LogInfo($"{selectedPluginTypes.Count} / {globalPluginTypes.Count} plugins to load");

                var dependencyDict = new Dictionary <string, IEnumerable <string> >();
                var pluginsByGUID  = new Dictionary <string, Type>();

                foreach (Type t in selectedPluginTypes)
                {
                    var dependencies = MetadataHelper.GetDependencies(t, selectedPluginTypes);
                    var metadata     = MetadataHelper.GetMetadata(t);

                    if (metadata.GUID == null)
                    {
                        Logger.LogWarning($"Skipping [{metadata.Name}] because it does not have a valid GUID.");
                        continue;
                    }

                    if (dependencyDict.ContainsKey(metadata.GUID))
                    {
                        Logger.LogWarning($"Skipping [{metadata.Name}] because its GUID ({metadata.GUID}) is already used by another plugin.");
                        continue;
                    }

                    dependencyDict[metadata.GUID] = dependencies.Select(d => d.DependencyGUID);
                    pluginsByGUID[metadata.GUID]  = t;
                }

                var emptyDependencies = new string[0];

                // Sort plugins by their dependencies.
                // Give missing dependencies no dependencies of its own, which will cause missing plugins to be first in the resulting list.
                var sortedPlugins = Utility.TopologicalSort(dependencyDict.Keys, x => dependencyDict.TryGetValue(x, out var deps) ? deps : emptyDependencies).ToList();

                var invalidPlugins   = new HashSet <string>();
                var processedPlugins = new HashSet <string>();

                foreach (var pluginGUID in sortedPlugins)
                {
                    // If the plugin is missing, don't process it
                    if (!pluginsByGUID.TryGetValue(pluginGUID, out var pluginType))
                    {
                        continue;
                    }

                    var metadata               = MetadataHelper.GetMetadata(pluginType);
                    var dependencies           = MetadataHelper.GetDependencies(pluginType, selectedPluginTypes);
                    var dependsOnInvalidPlugin = false;
                    var missingDependencies    = new List <string>();
                    foreach (var dependency in dependencies)
                    {
                        // If the depenency wasn't already processed, it's missing altogether
                        if (!processedPlugins.Contains(dependency.DependencyGUID))
                        {
                            // If the dependency is hard, collect it into a list to show
                            if ((dependency.Flags & BepInDependency.DependencyFlags.HardDependency) != 0)
                            {
                                missingDependencies.Add(dependency.DependencyGUID);
                            }
                            continue;
                        }

                        // If the dependency is invalid (e.g. has missing depedencies), report that to the user
                        if (invalidPlugins.Contains(dependency.DependencyGUID))
                        {
                            dependsOnInvalidPlugin = true;
                            break;
                        }
                    }

                    processedPlugins.Add(pluginGUID);

                    if (dependsOnInvalidPlugin)
                    {
                        Logger.LogWarning($"Skipping [{metadata.Name}] because it has a dependency that was not loaded. See above errors for details.");
                        continue;
                    }

                    if (missingDependencies.Count != 0)
                    {
                        Logger.LogError($@"Missing the following dependencies for [{metadata.Name}]: {"\r\n"}{
							string.Join("\r\n", missingDependencies.Select(s => $"- {s}").ToArray())
							}{"\r\n"}Loading will be skipped; expect further errors and unstabilities."                            );

                        invalidPlugins.Add(pluginGUID);
                        continue;
                    }

                    try
                    {
                        Logger.LogInfo($"Loading [{metadata.Name} {metadata.Version}]");
                        Plugins.Add((BaseUnityPlugin)ManagerObject.AddComponent(pluginType));
                    }
                    catch (Exception ex)
                    {
                        invalidPlugins.Add(pluginGUID);
                        Logger.LogError($"Error loading [{metadata.Name}] : {ex.Message}");
                        Logger.LogDebug(ex);
                    }
                }
            }
            catch (Exception ex)
            {
                ConsoleWindow.Attach();

                Console.WriteLine("Error occurred starting the game");
                Console.WriteLine(ex.ToString());
            }

            Logger.LogMessage("Chainloader startup complete");

            _loaded = true;
        }
Example #21
0
        /// <summary>
        /// The entrypoint for the BepInEx plugin system.
        /// </summary>
        public static void Start()
        {
            if (_loaded)
            {
                return;
            }

            if (!_initialized)
            {
                throw new InvalidOperationException("BepInEx has not been initialized. Please call Chainloader.Initialize prior to starting the chainloader instance.");
            }

            if (!Directory.Exists(Paths.PluginPath))
            {
                Directory.CreateDirectory(Paths.PluginPath);
            }

            if (!Directory.Exists(Paths.PatcherPluginPath))
            {
                Directory.CreateDirectory(Paths.PatcherPluginPath);
            }

            try
            {
                var productNameProp = typeof(Application).GetProperty("productName", BindingFlags.Public | BindingFlags.Static);
                if (productNameProp != null)
                {
                    ConsoleWindow.Title = $"BepInEx {Assembly.GetExecutingAssembly().GetName().Version} - {productNameProp.GetValue(null, null)}";
                }

                Logger.LogMessage("Chainloader started");

                ManagerObject = new GameObject("BepInEx_Manager");

                UnityEngine.Object.DontDestroyOnLoad(ManagerObject);

                var pluginsToLoad = TypeLoader.FindPluginTypes(Paths.PluginPath, ToPluginInfo, HasBepinPlugins, "chainloader");
                foreach (var keyValuePair in pluginsToLoad)
                {
                    foreach (var pluginInfo in keyValuePair.Value)
                    {
                        pluginInfo.Location = keyValuePair.Key;
                    }
                }
                var pluginInfos      = pluginsToLoad.SelectMany(p => p.Value).ToList();
                var loadedAssemblies = new Dictionary <string, Assembly>();

                Logger.LogInfo($"{pluginInfos.Count} plugins to load");

                var dependencyDict = new Dictionary <string, IEnumerable <string> >();
                var pluginsByGUID  = new Dictionary <string, PluginInfo>();

                foreach (var pluginInfo in pluginInfos)
                {
                    // Perform checks that will prevent loading plugins in this run
                    var  filters            = pluginInfo.Processes.ToList();
                    bool invalidProcessName = filters.Count != 0 && filters.All(x => !string.Equals(x.ProcessName.Replace(".exe", ""), Paths.ProcessName, StringComparison.InvariantCultureIgnoreCase));

                    if (invalidProcessName)
                    {
                        Logger.LogWarning($"Skipping over plugin [{pluginInfo.Metadata.GUID}] due to process filter");
                        continue;
                    }

                    if (dependencyDict.ContainsKey(pluginInfo.Metadata.GUID))
                    {
                        Logger.LogWarning($"Skipping [{pluginInfo.Metadata.Name}] because its GUID ({pluginInfo.Metadata.GUID}) is already used by another plugin.");
                        continue;
                    }

                    dependencyDict[pluginInfo.Metadata.GUID] = pluginInfo.Dependencies.Select(d => d.DependencyGUID).Concat(pluginInfo.Incompatibilities.Select(i => i.IncompatibilityGUID));
                    pluginsByGUID[pluginInfo.Metadata.GUID]  = pluginInfo;
                }

                var emptyDependencies = new string[0];

                // Sort plugins by their dependencies.
                // Give missing dependencies no dependencies of its own, which will cause missing plugins to be first in the resulting list.
                var sortedPlugins = Utility.TopologicalSort(dependencyDict.Keys, x => dependencyDict.TryGetValue(x, out var deps) ? deps : emptyDependencies).ToList();

                var invalidPlugins   = new HashSet <string>();
                var processedPlugins = new Dictionary <string, Version>();

                foreach (var pluginGUID in sortedPlugins)
                {
                    // If the plugin is missing, don't process it
                    if (!pluginsByGUID.TryGetValue(pluginGUID, out var pluginInfo))
                    {
                        continue;
                    }

                    var dependsOnInvalidPlugin = false;
                    var missingDependencies    = new List <BepInDependency>();
                    foreach (var dependency in pluginInfo.Dependencies)
                    {
                        // If the depenency wasn't already processed, it's missing altogether
                        bool depenencyExists = processedPlugins.TryGetValue(dependency.DependencyGUID, out var pluginVersion);
                        if (!depenencyExists || pluginVersion < dependency.MinimumVersion)
                        {
                            // If the dependency is hard, collect it into a list to show
                            if ((dependency.Flags & BepInDependency.DependencyFlags.HardDependency) != 0)
                            {
                                missingDependencies.Add(dependency);
                            }
                            continue;
                        }

                        // If the dependency is invalid (e.g. has missing depedencies), report that to the user
                        if (invalidPlugins.Contains(dependency.DependencyGUID))
                        {
                            dependsOnInvalidPlugin = true;
                            break;
                        }
                    }

                    var incompatibilities = new List <BepInIncompatibility>();
                    foreach (var incompatibility in pluginInfo.Incompatibilities)
                    {
                        if (processedPlugins.ContainsKey(incompatibility.IncompatibilityGUID))
                        {
                            incompatibilities.Add(incompatibility);
                        }
                    }

                    processedPlugins.Add(pluginGUID, pluginInfo.Metadata.Version);

                    if (dependsOnInvalidPlugin)
                    {
                        string message = $"Skipping [{pluginInfo.Metadata.Name}] because it has a dependency that was not loaded. See above errors for details.";
                        DependencyErrors.Add(message);
                        Logger.LogWarning(message);
                        continue;
                    }

                    if (missingDependencies.Count != 0)
                    {
                        string ToMissingString(BepInDependency s)
                        {
                            bool emptyVersion = s.MinimumVersion.Major == 0 && s.MinimumVersion.Minor == 0 && s.MinimumVersion.Build == 0 && s.MinimumVersion.Revision == 0;

                            if (emptyVersion)
                            {
                                return("- " + s.DependencyGUID);
                            }
                            return($"- {s.DependencyGUID} (at least v{s.MinimumVersion})");
                        }

                        string message = $@"Could not load [{pluginInfo.Metadata.Name}] because it has missing dependencies: {string.Join(", ", missingDependencies.Select(ToMissingString).ToArray())}";
                        DependencyErrors.Add(message);
                        Logger.LogError(message);

                        invalidPlugins.Add(pluginGUID);
                        continue;
                    }

                    if (incompatibilities.Count != 0)
                    {
                        string message = $@"Could not load [{pluginInfo.Metadata.Name}] because it is incompatible with: {string.Join(", ", incompatibilities.Select(i => i.IncompatibilityGUID).ToArray())}";
                        DependencyErrors.Add(message);
                        Logger.LogError(message);

                        invalidPlugins.Add(pluginGUID);
                        continue;
                    }

                    try
                    {
                        Logger.LogInfo($"Loading [{pluginInfo.Metadata.Name} {pluginInfo.Metadata.Version}]");

                        if (!loadedAssemblies.TryGetValue(pluginInfo.Location, out var ass))
                        {
                            loadedAssemblies[pluginInfo.Location] = ass = Assembly.LoadFile(pluginInfo.Location);
                        }

                        PluginInfos[pluginGUID] = pluginInfo;
                        pluginInfo.Instance     = (BaseUnityPlugin)ManagerObject.AddComponent(ass.GetType(pluginInfo.TypeName));

                        Plugins.Add(pluginInfo.Instance);
                    }
                    catch (Exception ex)
                    {
                        invalidPlugins.Add(pluginGUID);
                        PluginInfos.Remove(pluginGUID);

                        Logger.LogError($"Error loading [{pluginInfo.Metadata.Name}] : {ex.Message}");
                        if (ex is ReflectionTypeLoadException re)
                        {
                            Logger.LogDebug(TypeLoader.TypeLoadExceptionToString(re));
                        }
                        else
                        {
                            Logger.LogDebug(ex);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                ConsoleWindow.Attach();

                Console.WriteLine("Error occurred starting the game");
                Console.WriteLine(ex.ToString());
            }

            Logger.LogMessage("Chainloader startup complete");

            _loaded = true;
        }
Example #22
0
        /// <summary>
        /// Initializes BepInEx to be able to start the chainloader.
        /// </summary>
        public static void Initialize(string gameExePath, bool startConsole = true, ICollection <LogEventArgs> preloaderLogEvents = null)
        {
            if (_initialized)
            {
                return;
            }

            ThreadingHelper.Initialize();

            // Set vitals
            if (gameExePath != null)
            {
                // Checking for null allows a more advanced initialization workflow, where the Paths class has been initialized before calling Chainloader.Initialize
                // This is used by Preloader to use environment variables, for example
                Paths.SetExecutablePath(gameExePath);
            }

            // Start logging
            if (ConsoleManager.ConfigConsoleEnabled.Value && startConsole)
            {
                ConsoleManager.CreateConsole();
                Logger.Listeners.Add(new ConsoleLogListener());
            }

            // Fix for standard output getting overwritten by UnityLogger
            if (ConsoleManager.ConsoleActive)
            {
                ConsoleManager.SetConsoleStreams();
                ConsoleManager.SetConsoleEncoding();
            }

            Logger.InitializeInternalLoggers();
            Logger.Listeners.Add(new UnityLogListener());

            if (ConfigDiskLogging.Value)
            {
                Logger.Listeners.Add(new DiskLogListener("LogOutput.log", ConfigDiskConsoleDisplayedLevel.Value, ConfigDiskAppend.Value, ConfigDiskWriteUnityLog.Value));
            }

            if (!TraceLogSource.IsListening)
            {
                Logger.Sources.Add(TraceLogSource.CreateSource());
            }

            if (ConfigUnityLogging.Value)
            {
                Logger.Sources.Add(new UnityLogSource());
            }


            // Temporarily disable the console log listener as we replay the preloader logs
            var logListener = Logger.Listeners.FirstOrDefault(logger => logger is ConsoleLogListener);

            if (logListener != null)
            {
                Logger.Listeners.Remove(logListener);
            }

            // Write preloader log events if there are any, including the original log source name
            if (preloaderLogEvents != null)
            {
                var preloaderLogSource = Logger.CreateLogSource("Preloader");

                foreach (var preloaderLogEvent in preloaderLogEvents)
                {
                    Logger.InternalLogEvent(preloaderLogSource, preloaderLogEvent);
                }

                Logger.Sources.Remove(preloaderLogSource);
            }

            if (logListener != null)
            {
                Logger.Listeners.Add(logListener);
            }

            if (Utility.CurrentOs == Platform.Linux)
            {
                Logger.LogInfo($"Detected Unity version: v{Application.unityVersion}");
            }

            Logger.LogMessage("Chainloader ready");

            _initialized = true;
        }
Example #23
0
        /// <summary>
        /// Initializes BepInEx to be able to start the chainloader.
        /// </summary>
        public static void Initialize(string gameExePath, bool startConsole = true, ICollection <LogEventArgs> preloaderLogEvents = null)
        {
            if (_initialized)
            {
                return;
            }

            ThreadingHelper.Initialize();

            // Set vitals
            if (gameExePath != null)
            {
                // Checking for null allows a more advanced initialization workflow, where the Paths class has been initialized before calling Chainloader.Initialize
                // This is used by Preloader to use environment variables, for example
                Paths.SetExecutablePath(gameExePath);
            }

            // Start logging
            if (ConsoleWindow.ConfigConsoleEnabled.Value && startConsole)
            {
                ConsoleWindow.Attach();
                Logger.Listeners.Add(new ConsoleLogListener());
            }

            // Fix for standard output getting overwritten by UnityLogger
            if (ConsoleWindow.StandardOut != null)
            {
                Console.SetOut(ConsoleWindow.StandardOut);

                var encoding = ConsoleWindow.ConfigConsoleShiftJis.Value ? 932 : (uint)Encoding.UTF8.CodePage;
                ConsoleEncoding.ConsoleCodePage = encoding;
                Console.OutputEncoding          = ConsoleEncoding.GetEncoding(encoding);
            }

            Logger.Listeners.Add(new UnityLogListener());

            if (ConfigDiskLogging.Value)
            {
                Logger.Listeners.Add(new DiskLogListener("LogOutput.log", ConfigDiskConsoleDisplayedLevel.Value, ConfigDiskAppend.Value, ConfigDiskWriteUnityLog.Value));
            }

            if (!TraceLogSource.IsListening)
            {
                Logger.Sources.Add(TraceLogSource.CreateSource());
            }

            if (ConfigUnityLogging.Value)
            {
                Logger.Sources.Add(new UnityLogSource());
            }


            // Temporarily disable the console log listener as we replay the preloader logs
            var logListener = Logger.Listeners.FirstOrDefault(logger => logger is ConsoleLogListener);

            if (logListener != null)
            {
                Logger.Listeners.Remove(logListener);
            }

            // Write preloader log events if there are any, including the original log source name
            if (preloaderLogEvents != null)
            {
                var preloaderLogSource = Logger.CreateLogSource("Preloader");

                foreach (var preloaderLogEvent in preloaderLogEvents)
                {
                    preloaderLogSource.Log(preloaderLogEvent.Level, $"[{preloaderLogEvent.Source.SourceName,10}] {preloaderLogEvent.Data}");
                }

                Logger.Sources.Remove(preloaderLogSource);
            }

            if (logListener != null)
            {
                Logger.Listeners.Add(logListener);
            }

            Logger.LogMessage("Chainloader ready");

            _initialized = true;
        }
Example #24
0
        void LoadSongs()
        {
            songs    = new List <string>();
            beatmaps = new List <BeatmapInfo>();
            string[] defaultDifficulties = new[]
            {
                "Beginner",
                "Easy",
                "Normal",
                "Hard",
                "UNBEATABLE",
                "Trailer",
                "Tutorial"
            };
            difficulties = new List <string>();

            string customSongDir = $"{Application.dataPath.Substring(0, Application.dataPath.LastIndexOf('/'))}/CustomBeats/Songs/";

            if (!Directory.Exists(customSongDir))
            {
                Directory.CreateDirectory(customSongDir);
            }
            Logger.LogInfo($"CustomBeats Song Directory: {customSongDir}");

            string[] songDirs = Directory.GetDirectories(customSongDir);
            foreach (var dir in songDirs)
            {
                string songName = Path.GetFileNameWithoutExtension(dir);
                Logger.LogInfo("Loading song " + songName);
                bool hasMaps = false;

                string[] osuFiles = Directory.GetFiles(dir, "*.osu");
                foreach (var osuFile in osuFiles)
                {
                    string    beatmapName = Path.GetFileNameWithoutExtension(osuFile);
                    string    content     = File.ReadAllText(osuFile);
                    TextAsset asset       = new TextAsset(content);
                    asset.name = beatmapName;
                    var    difficultyMatch = Regex.Match(content, "Version: *(.+?)\r?\n");
                    string difficulty      = difficultyMatch.Groups[1].Value;
                    if (!defaultDifficulties.Contains(difficulty) && !difficulties.Contains(difficulty))
                    {
                        difficulties.Add(difficulty);
                    }
                    var    titleMatch = Regex.Match(content, "Title: *(.+?)\r?\n");
                    string title      = titleMatch.Groups[1].Value;
                    var    clipMatch  = Regex.Match(content, "AudioFilename: *(.+?)\r?\n");
                    var    clip       = clipMatch.Groups[1].Value;

                    Logger.LogInfo(" - Loading beatmap " + beatmapName + " (" + difficulty + "): " + clip);

                    CustomBeatmapInfo beatmap = new CustomBeatmapInfo(asset, songName, difficulty, clip);

                    beatmaps.Add(beatmap);
                    hasMaps = true;
                }

                if (hasMaps)
                {
                    songs.Add(songName);
                }
            }

            var allDifficulties = defaultDifficulties.Concat(difficulties).ToList();

            beatmaps.Sort(delegate(BeatmapInfo x, BeatmapInfo y)
            {
                return(allDifficulties.IndexOf(x.difficulty) - allDifficulties.IndexOf(y.difficulty));
            });

            dirty = true;
        }
Example #25
0
 void OnDestroy()
 {
     Logger.LogInfo("Goodbye, world!");
     harmony.UnpatchSelf();
 }