From 1b3f45ca8c3a3ad87d826cf6ada865b3f0766e28 Mon Sep 17 00:00:00 2001 From: Anairkoen Schno Date: Fri, 31 Aug 2018 20:07:01 -0500 Subject: [PATCH 01/12] Began refactor --- BSIPA.sln | 12 + IPA.Injector/Bootstrapper.cs | 31 ++ IPA.Injector/ConsoleWindow.cs | 72 ++++ IPA.Injector/IPA.Injector.csproj | 91 +++++ IPA.Injector/Injector.cs | 63 ++++ IPA.Injector/PostBuild.msbuild | 35 ++ IPA.Injector/Properties/AssemblyInfo.cs | 37 ++ IPA.Injector/packages.config | 5 + IPA.Loader/BeatSaber/CompositeBSPlugin.cs | 97 +++++ IPA.Loader/IPA.Loader.csproj | 94 +++++ IPA.Loader/IPA/CompositeIPAPlugin.cs | 75 ++++ .../BeatSaber/IBeatSaberPlugin.cs | 71 ++++ .../BeatSaber/IEnhancedBeatSaberPlugin.cs | 13 + .../BeatSaber/ModsaberModInfo.cs | 24 ++ .../IllusionPlugin/IGenericEnhancedPlugin.cs | 25 ++ .../IllusionPlugin/IPA/IEnhancedPlugin.cs | 14 + IPA.Loader/IllusionPlugin/IPA/IPlugin.cs | 58 +++ IPA.Loader/IllusionPlugin/IniFile.cs | 100 +++++ .../IllusionPlugin/Logging/LogPrinter.cs | 37 ++ IPA.Loader/IllusionPlugin/Logging/Logger.cs | 182 +++++++++ IPA.Loader/IllusionPlugin/ModPrefs.cs | 285 +++++++++++++++ .../IllusionPlugin/Utils/ReflectionUtil.cs | 203 ++++++++++ .../Logging/Printers/ColoredConsolePrinter.cs | 29 ++ IPA.Loader/Logging/Printers/GZFilePrinter.cs | 92 +++++ .../Logging/Printers/GlobalLogFilePrinter.cs | 30 ++ .../Logging/Printers/PluginLogFilePrinter.cs | 37 ++ IPA.Loader/Logging/StandardLogger.cs | 168 +++++++++ IPA.Loader/Logging/UnityLogInterceptor.cs | 34 ++ IPA.Loader/PluginComponent.cs | 109 ++++++ IPA.Loader/PluginManager.cs | 264 +++++++++++++ IPA.Loader/Properties/AssemblyInfo.cs | 37 ++ IPA.Loader/Updating/ModsaberML/ApiEndpoint.cs | 123 +++++++ IPA.Loader/Updating/ModsaberML/Updater.cs | 346 ++++++++++++++++++ IPA.Loader/Updating/SelfPlugin.cs | 55 +++ IPA.Loader/Utilities/Extensions.cs | 20 + IPA.Loader/Utilities/LoneFunctions.cs | 63 ++++ IPA.Loader/Utilities/SteamCheck.cs | 27 ++ IPA.Loader/packages.config | 5 + IPA/IPA.csproj | 2 +- IPA/Patcher/Patcher.cs | 15 +- IPA/Program.cs | 4 +- IPA/Properties/AssemblyInfo.cs | 4 +- IllusionInjector/Updating/SelfPlugin.cs | 2 +- 43 files changed, 3077 insertions(+), 13 deletions(-) create mode 100644 IPA.Injector/Bootstrapper.cs create mode 100644 IPA.Injector/ConsoleWindow.cs create mode 100644 IPA.Injector/IPA.Injector.csproj create mode 100644 IPA.Injector/Injector.cs create mode 100644 IPA.Injector/PostBuild.msbuild create mode 100644 IPA.Injector/Properties/AssemblyInfo.cs create mode 100644 IPA.Injector/packages.config create mode 100644 IPA.Loader/BeatSaber/CompositeBSPlugin.cs create mode 100644 IPA.Loader/IPA.Loader.csproj create mode 100644 IPA.Loader/IPA/CompositeIPAPlugin.cs create mode 100644 IPA.Loader/IllusionPlugin/BeatSaber/IBeatSaberPlugin.cs create mode 100644 IPA.Loader/IllusionPlugin/BeatSaber/IEnhancedBeatSaberPlugin.cs create mode 100644 IPA.Loader/IllusionPlugin/BeatSaber/ModsaberModInfo.cs create mode 100644 IPA.Loader/IllusionPlugin/IGenericEnhancedPlugin.cs create mode 100644 IPA.Loader/IllusionPlugin/IPA/IEnhancedPlugin.cs create mode 100644 IPA.Loader/IllusionPlugin/IPA/IPlugin.cs create mode 100644 IPA.Loader/IllusionPlugin/IniFile.cs create mode 100644 IPA.Loader/IllusionPlugin/Logging/LogPrinter.cs create mode 100644 IPA.Loader/IllusionPlugin/Logging/Logger.cs create mode 100644 IPA.Loader/IllusionPlugin/ModPrefs.cs create mode 100644 IPA.Loader/IllusionPlugin/Utils/ReflectionUtil.cs create mode 100644 IPA.Loader/Logging/Printers/ColoredConsolePrinter.cs create mode 100644 IPA.Loader/Logging/Printers/GZFilePrinter.cs create mode 100644 IPA.Loader/Logging/Printers/GlobalLogFilePrinter.cs create mode 100644 IPA.Loader/Logging/Printers/PluginLogFilePrinter.cs create mode 100644 IPA.Loader/Logging/StandardLogger.cs create mode 100644 IPA.Loader/Logging/UnityLogInterceptor.cs create mode 100644 IPA.Loader/PluginComponent.cs create mode 100644 IPA.Loader/PluginManager.cs create mode 100644 IPA.Loader/Properties/AssemblyInfo.cs create mode 100644 IPA.Loader/Updating/ModsaberML/ApiEndpoint.cs create mode 100644 IPA.Loader/Updating/ModsaberML/Updater.cs create mode 100644 IPA.Loader/Updating/SelfPlugin.cs create mode 100644 IPA.Loader/Utilities/Extensions.cs create mode 100644 IPA.Loader/Utilities/LoneFunctions.cs create mode 100644 IPA.Loader/Utilities/SteamCheck.cs create mode 100644 IPA.Loader/packages.config diff --git a/BSIPA.sln b/BSIPA.sln index 548da4f3..f0444c68 100644 --- a/BSIPA.sln +++ b/BSIPA.sln @@ -17,6 +17,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IPA.Tests", "IPA.Tests\IPA. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MSBuildTasks", "MSBuildTasks\MSBuildTasks.csproj", "{F08C3C7A-3221-432E-BAB8-32BCE58408C8}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IPA.Loader", "IPA.Loader\IPA.Loader.csproj", "{5AD344F0-01A0-4CA8-92E5-9D095737744D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IPA.Injector", "IPA.Injector\IPA.Injector.csproj", "{2A1AF16B-27F1-46E0-9A95-181516BC1CB7}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -43,6 +47,14 @@ Global {F08C3C7A-3221-432E-BAB8-32BCE58408C8}.Debug|Any CPU.Build.0 = Debug|Any CPU {F08C3C7A-3221-432E-BAB8-32BCE58408C8}.Release|Any CPU.ActiveCfg = Release|Any CPU {F08C3C7A-3221-432E-BAB8-32BCE58408C8}.Release|Any CPU.Build.0 = Release|Any CPU + {5AD344F0-01A0-4CA8-92E5-9D095737744D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5AD344F0-01A0-4CA8-92E5-9D095737744D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5AD344F0-01A0-4CA8-92E5-9D095737744D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5AD344F0-01A0-4CA8-92E5-9D095737744D}.Release|Any CPU.Build.0 = Release|Any CPU + {2A1AF16B-27F1-46E0-9A95-181516BC1CB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2A1AF16B-27F1-46E0-9A95-181516BC1CB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2A1AF16B-27F1-46E0-9A95-181516BC1CB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2A1AF16B-27F1-46E0-9A95-181516BC1CB7}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/IPA.Injector/Bootstrapper.cs b/IPA.Injector/Bootstrapper.cs new file mode 100644 index 00000000..1f8e9a78 --- /dev/null +++ b/IPA.Injector/Bootstrapper.cs @@ -0,0 +1,31 @@ +using IllusionInjector.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; + +namespace IPA.Injector +{ + class Bootstrapper : MonoBehaviour + { + public event Action Destroyed = delegate {}; + + void Awake() + { + //if (Environment.CommandLine.Contains("--verbose")) + //{ + Windows.GuiConsole.CreateConsole(); + //} + } + + void Start() + { + Destroy(gameObject); + } + void OnDestroy() + { + Destroyed(); + } + } +} diff --git a/IPA.Injector/ConsoleWindow.cs b/IPA.Injector/ConsoleWindow.cs new file mode 100644 index 00000000..acee7433 --- /dev/null +++ b/IPA.Injector/ConsoleWindow.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections; +using System.Runtime.InteropServices; +using System.IO; +using System.Text; +using Microsoft.Win32.SafeHandles; + +namespace Windows +{ + + class GuiConsole + { + public static void CreateConsole() + { + if (hasConsole) + return; + if (oldOut == IntPtr.Zero) + oldOut = GetStdHandle( -11 ); + if (! AllocConsole()) + throw new Exception("AllocConsole() failed"); + conOut = CreateFile( "CONOUT$", 0x40000000, 2, IntPtr.Zero, 3, 0, IntPtr.Zero ); + if (! SetStdHandle(-11, conOut)) + throw new Exception("SetStdHandle() failed"); + StreamToConsole(); + hasConsole = true; + } + public static void ReleaseConsole() + { + if (! hasConsole) + return; + if (! CloseHandle(conOut)) + throw new Exception("CloseHandle() failed"); + conOut = IntPtr.Zero; + if (! FreeConsole()) + throw new Exception("FreeConsole() failed"); + if (! SetStdHandle(-11, oldOut)) + throw new Exception("SetStdHandle() failed"); + StreamToConsole(); + hasConsole = false; + } + private static void StreamToConsole() + { + Stream cstm = Console.OpenStandardOutput(); + StreamWriter cstw = new StreamWriter( cstm, Encoding.Default ); + cstw.AutoFlush = true; + Console.SetOut( cstw ); + Console.SetError( cstw ); + } + private static bool hasConsole = false; + private static IntPtr conOut; + private static IntPtr oldOut; + [DllImport("kernel32.dll", SetLastError=true)] + private static extern bool AllocConsole(); + [DllImport("kernel32.dll", SetLastError=false)] + private static extern bool FreeConsole(); + [DllImport("kernel32.dll", SetLastError=true)] + private static extern IntPtr GetStdHandle( int nStdHandle ); + [DllImport("kernel32.dll", SetLastError=true)] + private static extern bool SetStdHandle(int nStdHandle, IntPtr hConsoleOutput); + [DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)] + private static extern IntPtr CreateFile( + string fileName, + int desiredAccess, + int shareMode, + IntPtr securityAttributes, + int creationDisposition, + int flagsAndAttributes, + IntPtr templateFile ); + [DllImport("kernel32.dll", ExactSpelling=true, SetLastError=true)] + private static extern bool CloseHandle(IntPtr handle); + } +} \ No newline at end of file diff --git a/IPA.Injector/IPA.Injector.csproj b/IPA.Injector/IPA.Injector.csproj new file mode 100644 index 00000000..f07fc0af --- /dev/null +++ b/IPA.Injector/IPA.Injector.csproj @@ -0,0 +1,91 @@ + + + + + Debug + AnyCPU + {2A1AF16B-27F1-46E0-9A95-181516BC1CB7} + Library + Properties + IPA.Injector + IPA.Injector + v4.6 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Ionic.Zip.1.9.1.8\lib\Ionic.Zip.dll + + + ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll + + + + + + + + + + + False + ..\..\..\..\..\..\Game Library\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\UnityEngine.CoreModule.dll + False + + + + + + + + + + + {5ad344f0-01a0-4ca8-92e5-9d095737744d} + IPA.Loader + + + + + + + + Libraries\Included\0Harmony.dll + Always + + + Libraries\Mono\I18N.dll + Always + + + Libraries\Mono\I18N.West.dll + Always + + + Libraries\Mono\System.Runtime.Serialization.dll + Always + + + + + + + \ No newline at end of file diff --git a/IPA.Injector/Injector.cs b/IPA.Injector/Injector.cs new file mode 100644 index 00000000..8638e9de --- /dev/null +++ b/IPA.Injector/Injector.cs @@ -0,0 +1,63 @@ +using IllusionInjector; +using IllusionInjector.Logging; +using System; +using System.IO; +using System.Reflection; +using UnityEngine; +using static IllusionPlugin.Logging.Logger; +using Logger = IllusionInjector.Logging.Logger; + +namespace IPA.Injector +{ + public static class Injector + { + private static bool injected = false; + public static void Inject() + { + if (!injected) + { + injected = true; + AppDomain.CurrentDomain.AssemblyResolve += AssemblyLibLoader; + var bootstrapper = new GameObject("Bootstrapper").AddComponent(); + bootstrapper.Destroyed += Bootstrapper_Destroyed; + } + } + + private static string libsDir; + private static Assembly AssemblyLibLoader(object source, ResolveEventArgs e) + { + if (libsDir == null) + libsDir = Path.Combine(Environment.CurrentDirectory, "Libs"); + + var asmName = new AssemblyName(e.Name); + Log(Level.Debug, $"Resolving library {asmName}"); + + var testFilen = Path.Combine(libsDir, $"{asmName.Name}.{asmName.Version}.dll"); + Log(Level.Debug, $"Looking for file {testFilen}"); + + if (File.Exists(testFilen)) + return Assembly.LoadFile(testFilen); + + Log(Level.Critical, $"Could not load library {asmName}"); + + return null; + } + private static void Log(Level lvl, string message) + { // multiple proxy methods to delay loading of assemblies until it's done + if (Logger.LogCreated) + AssemblyLibLoaderCallLogger(lvl, message); + else + if (((byte)lvl & (byte)StandardLogger.PrintFilter) != 0) + Console.WriteLine($"[{lvl}] {message}"); + } + private static void AssemblyLibLoaderCallLogger(Level lvl, string message) + { + Logger.log.Log(lvl, message); + } + + private static void Bootstrapper_Destroyed() + { + PluginComponent.Create(); + } + } +} diff --git a/IPA.Injector/PostBuild.msbuild b/IPA.Injector/PostBuild.msbuild new file mode 100644 index 00000000..20f1fccb --- /dev/null +++ b/IPA.Injector/PostBuild.msbuild @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/IPA.Injector/Properties/AssemblyInfo.cs b/IPA.Injector/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..bae971d5 --- /dev/null +++ b/IPA.Injector/Properties/AssemblyInfo.cs @@ -0,0 +1,37 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("IPA.Injector")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("IPA.Injector")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("2a1af16b-27f1-46e0-9a95-181516bc1cb7")] +[assembly: InternalsVisibleTo("IPA.Loader")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("3.9.0")] +[assembly: AssemblyFileVersion("3.9.0")] diff --git a/IPA.Injector/packages.config b/IPA.Injector/packages.config new file mode 100644 index 00000000..71c37d6d --- /dev/null +++ b/IPA.Injector/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/IPA.Loader/BeatSaber/CompositeBSPlugin.cs b/IPA.Loader/BeatSaber/CompositeBSPlugin.cs new file mode 100644 index 00000000..96d6ddf0 --- /dev/null +++ b/IPA.Loader/BeatSaber/CompositeBSPlugin.cs @@ -0,0 +1,97 @@ +using IllusionPlugin; +using IllusionPlugin.BeatSaber; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; +using UnityEngine.SceneManagement; +using Logger = IllusionInjector.Logging.Logger; + +namespace IllusionInjector { + public class CompositeBSPlugin : IBeatSaberPlugin + { + IEnumerable plugins; + + private delegate void CompositeCall(IBeatSaberPlugin plugin); + + public CompositeBSPlugin(IEnumerable plugins) { + this.plugins = plugins; + } + + public void OnApplicationStart() { + Invoke(plugin => plugin.OnApplicationStart()); + } + + public void OnApplicationQuit() { + Invoke(plugin => plugin.OnApplicationQuit()); + } + + public void OnSceneLoaded(Scene scene, LoadSceneMode sceneMode) { + foreach (var plugin in plugins) { + try { + plugin.OnSceneLoaded(scene, sceneMode); + } + catch (Exception ex) { + Logger.log.Error($"{plugin.Name}: {ex}"); + } + } + } + + public void OnSceneUnloaded(Scene scene) { + foreach (var plugin in plugins) { + try { + plugin.OnSceneUnloaded(scene); + } + catch (Exception ex) { + Logger.log.Error($"{plugin.Name}: {ex}"); + } + } + } + + public void OnActiveSceneChanged(Scene prevScene, Scene nextScene) { + foreach (var plugin in plugins) { + try { + plugin.OnActiveSceneChanged(prevScene, nextScene); + } + catch (Exception ex) { + Logger.log.Error($"{plugin.Name}: {ex}"); + } + } + } + + private void Invoke(CompositeCall callback) { + foreach (var plugin in plugins) { + try { + callback(plugin); + } + catch (Exception ex) { + Logger.log.Error($"{plugin.Name}: {ex}"); + } + } + } + + public void OnUpdate() { + Invoke(plugin => plugin.OnUpdate()); + } + + public void OnFixedUpdate() { + Invoke(plugin => plugin.OnFixedUpdate()); + } + + public string Name => throw new NotImplementedException(); + + public string Version => throw new NotImplementedException(); + + public Uri UpdateUri => throw new NotImplementedException(); + + public ModsaberModInfo ModInfo => throw new NotImplementedException(); + + public void OnLateUpdate() { + Invoke(plugin => { + if (plugin is IEnhancedBeatSaberPlugin) + ((IEnhancedBeatSaberPlugin) plugin).OnLateUpdate(); + }); + } + } +} \ No newline at end of file diff --git a/IPA.Loader/IPA.Loader.csproj b/IPA.Loader/IPA.Loader.csproj new file mode 100644 index 00000000..fe8d387f --- /dev/null +++ b/IPA.Loader/IPA.Loader.csproj @@ -0,0 +1,94 @@ + + + + + Debug + AnyCPU + {5AD344F0-01A0-4CA8-92E5-9D095737744D} + Library + Properties + IPA.Loader + IPA.Loader + v4.6 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + true + + + + ..\packages\Ionic.Zip.1.9.1.8\lib\Ionic.Zip.dll + + + ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll + + + + + + + + + + + ..\..\..\..\..\..\Game Library\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\UnityEngine.CoreModule.dll + False + + + ..\..\..\..\..\..\Game Library\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\UnityEngine.UnityWebRequestModule.dll + False + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/IPA.Loader/IPA/CompositeIPAPlugin.cs b/IPA.Loader/IPA/CompositeIPAPlugin.cs new file mode 100644 index 00000000..001677a6 --- /dev/null +++ b/IPA.Loader/IPA/CompositeIPAPlugin.cs @@ -0,0 +1,75 @@ +using IllusionPlugin; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; +using UnityEngine.SceneManagement; +using Logger = IllusionInjector.Logging.Logger; + +namespace IllusionInjector { +#pragma warning disable CS0618 // Type or member is obsolete + public class CompositeIPAPlugin : IPlugin + { + IEnumerable plugins; + + private delegate void CompositeCall(IPlugin plugin); + + public CompositeIPAPlugin(IEnumerable plugins) { + this.plugins = plugins; + } + + public void OnApplicationStart() { + Invoke(plugin => plugin.OnApplicationStart()); + } + + public void OnApplicationQuit() { + Invoke(plugin => plugin.OnApplicationQuit()); + } + + private void Invoke(CompositeCall callback) { + foreach (var plugin in plugins) { + try { + callback(plugin); + } + catch (Exception ex) { + Logger.log.Error($"{plugin.Name}: {ex}"); + } + } + } + + public void OnUpdate() { + Invoke(plugin => plugin.OnUpdate()); + } + + public void OnFixedUpdate() { + Invoke(plugin => plugin.OnFixedUpdate()); + } + + public string Name { + get { throw new NotImplementedException(); } + } + + public string Version { + get { throw new NotImplementedException(); } + } + + public void OnLateUpdate() { + Invoke(plugin => { + if (plugin is IEnhancedBeatSaberPlugin) + ((IEnhancedBeatSaberPlugin) plugin).OnLateUpdate(); + }); + } + + public void OnLevelWasLoaded(int level) + { + Invoke(plugin => plugin.OnLevelWasLoaded(level)); + } + + public void OnLevelWasInitialized(int level) + { + Invoke(plugin => plugin.OnLevelWasInitialized(level)); + } + } +#pragma warning restore CS0618 // Type or member is obsolete +} \ No newline at end of file diff --git a/IPA.Loader/IllusionPlugin/BeatSaber/IBeatSaberPlugin.cs b/IPA.Loader/IllusionPlugin/BeatSaber/IBeatSaberPlugin.cs new file mode 100644 index 00000000..a9e2753c --- /dev/null +++ b/IPA.Loader/IllusionPlugin/BeatSaber/IBeatSaberPlugin.cs @@ -0,0 +1,71 @@ +using IllusionPlugin.BeatSaber; +using System; +using System.Collections.Generic; +using System.Text; +using UnityEngine.SceneManagement; + +namespace IllusionPlugin +{ + /// + /// Interface for Beat Saber plugins. Every class that implements this will be loaded if the DLL is placed at + /// data/Managed/Plugins. + /// + public interface IBeatSaberPlugin + { + + /// + /// Gets the name of the plugin. + /// + string Name { get; } + + /// + /// Gets the version of the plugin. + /// + string Version { get; } + + /// + /// Gets the info for the Modsaber release of this plugin. Return null if there is no Modsaber release. + /// + ModsaberModInfo ModInfo { get; } + + /// + /// Gets invoked when the application is started. + /// + void OnApplicationStart(); + + /// + /// Gets invoked when the application is closed. + /// + void OnApplicationQuit(); + + /// + /// Gets invoked on every graphic update. + /// + void OnUpdate(); + + /// + /// Gets invoked on ever physics update. + /// + void OnFixedUpdate(); + + /// + /// Gets invoked whenever a scene is loaded. + /// + /// The scene currently loaded + /// The type of loading + void OnSceneLoaded(Scene scene, LoadSceneMode sceneMode); + + /// + /// Gets invoked whenever a scene is unloaded + /// + /// The unloaded scene + void OnSceneUnloaded(Scene scene); + + /// + /// Gets invoked whenever a scene is changed + /// + /// The Scene that was previously loaded + /// The Scene being loaded + void OnActiveSceneChanged(Scene prevScene, Scene nextScene); + } +} diff --git a/IPA.Loader/IllusionPlugin/BeatSaber/IEnhancedBeatSaberPlugin.cs b/IPA.Loader/IllusionPlugin/BeatSaber/IEnhancedBeatSaberPlugin.cs new file mode 100644 index 00000000..dfbd4f9f --- /dev/null +++ b/IPA.Loader/IllusionPlugin/BeatSaber/IEnhancedBeatSaberPlugin.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace IllusionPlugin +{ + /// + /// An enhanced version of a standard BeatSaber plugin. + /// + public interface IEnhancedBeatSaberPlugin : IBeatSaberPlugin, IGenericEnhancedPlugin + { + } +} diff --git a/IPA.Loader/IllusionPlugin/BeatSaber/ModsaberModInfo.cs b/IPA.Loader/IllusionPlugin/BeatSaber/ModsaberModInfo.cs new file mode 100644 index 00000000..6defe31c --- /dev/null +++ b/IPA.Loader/IllusionPlugin/BeatSaber/ModsaberModInfo.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IllusionPlugin.BeatSaber +{ + /// + /// A class to provide information about a mod on ModSaber.ML + /// + public class ModsaberModInfo + { + /// + /// The name the mod uses on ModSaber as an identifier. + /// + public string InternalName { get; set; } + + /// + /// The version of the currently installed mod. Used to compare to the version on ModSaber. + /// + public Version CurrentVersion { get; set; } + } +} diff --git a/IPA.Loader/IllusionPlugin/IGenericEnhancedPlugin.cs b/IPA.Loader/IllusionPlugin/IGenericEnhancedPlugin.cs new file mode 100644 index 00000000..80327f5c --- /dev/null +++ b/IPA.Loader/IllusionPlugin/IGenericEnhancedPlugin.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IllusionPlugin +{ + /// + /// A generic interface for the modification for enhanced plugins. + /// + public interface IGenericEnhancedPlugin + { + /// + /// Gets a list of executables this plugin should be excuted on (without the file ending) + /// + /// { "PlayClub", "PlayClubStudio" } + string[] Filter { get; } + + /// + /// Called after Update. + /// + void OnLateUpdate(); + } +} diff --git a/IPA.Loader/IllusionPlugin/IPA/IEnhancedPlugin.cs b/IPA.Loader/IllusionPlugin/IPA/IEnhancedPlugin.cs new file mode 100644 index 00000000..ac925b2e --- /dev/null +++ b/IPA.Loader/IllusionPlugin/IPA/IEnhancedPlugin.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace IllusionPlugin +{ + /// + /// An enhanced version of the standard IPA plugin. + /// + [Obsolete("When building plugins for Beat Saber, use IEnhancedBeatSaberPlugin")] + public interface IEnhancedPlugin : IPlugin, IGenericEnhancedPlugin + { + } +} \ No newline at end of file diff --git a/IPA.Loader/IllusionPlugin/IPA/IPlugin.cs b/IPA.Loader/IllusionPlugin/IPA/IPlugin.cs new file mode 100644 index 00000000..0610da73 --- /dev/null +++ b/IPA.Loader/IllusionPlugin/IPA/IPlugin.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace IllusionPlugin +{ + /// + /// Interface for generic Illusion unity plugins. Every class that implements this will be loaded if the DLL is placed at + /// data/Managed/Plugins. + /// + [Obsolete("When building plugins for Beat Saber, use IBeatSaberPlugin")] + public interface IPlugin + { + + /// + /// Gets the name of the plugin. + /// + string Name { get; } + + /// + /// Gets the version of the plugin. + /// + string Version { get; } + + /// + /// Gets invoked when the application is started. + /// + void OnApplicationStart(); + + /// + /// Gets invoked when the application is closed. + /// + void OnApplicationQuit(); + + /// + /// Gets invoked whenever a level is loaded. + /// + /// + void OnLevelWasLoaded(int level); + + /// + /// Gets invoked after the first update cycle after a level was loaded. + /// + /// + void OnLevelWasInitialized(int level); + + /// + /// Gets invoked on every graphic update. + /// + void OnUpdate(); + + + /// + /// Gets invoked on ever physics update. + /// + void OnFixedUpdate(); + } +} \ No newline at end of file diff --git a/IPA.Loader/IllusionPlugin/IniFile.cs b/IPA.Loader/IllusionPlugin/IniFile.cs new file mode 100644 index 00000000..b8f47bce --- /dev/null +++ b/IPA.Loader/IllusionPlugin/IniFile.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; + +namespace IllusionPlugin +{ + /// + /// Create a New INI file to store or load data + /// + internal class IniFile + { + [DllImport("KERNEL32.DLL", EntryPoint = "GetPrivateProfileStringW", + SetLastError = true, + CharSet = CharSet.Unicode, ExactSpelling = true, + CallingConvention = CallingConvention.StdCall)] + private static extern int GetPrivateProfileString( + string lpSection, + string lpKey, + string lpDefault, + StringBuilder lpReturnString, + int nSize, + string lpFileName); + + [DllImport("KERNEL32.DLL", EntryPoint = "WritePrivateProfileStringW", + SetLastError = true, + CharSet = CharSet.Unicode, ExactSpelling = true, + CallingConvention = CallingConvention.StdCall)] + private static extern int WritePrivateProfileString( + string lpSection, + string lpKey, + string lpValue, + string lpFileName); + + /*private string _path = ""; + public string Path + { + get + { + return _path; + } + set + { + if (!File.Exists(value)) + File.WriteAllText(value, "", Encoding.Unicode); + _path = value; + } + }*/ + + private FileInfo _iniFileInfo; + public FileInfo IniFileInfo { + get => _iniFileInfo; + set { + _iniFileInfo = value; + if (_iniFileInfo.Exists) return; + _iniFileInfo.Directory?.Create(); + _iniFileInfo.Create(); + } + } + + /// + /// INIFile Constructor. + /// + /// + public IniFile(string iniPath) + { + IniFileInfo = new FileInfo(iniPath); + //this.Path = INIPath; + } + + /// + /// Write Data to the INI File + /// + /// + /// Section name + /// + /// Key Name + /// + /// Value Name + public void IniWriteValue(string Section, string Key, string Value) + { + WritePrivateProfileString(Section, Key, Value, IniFileInfo.FullName); + } + + /// + /// Read Data Value From the Ini File + /// + /// + /// + /// + public string IniReadValue(string Section, string Key) + { + const int MAX_CHARS = 1023; + StringBuilder result = new StringBuilder(MAX_CHARS); + GetPrivateProfileString(Section, Key, "", result, MAX_CHARS, IniFileInfo.FullName); + return result.ToString(); + } + } +} diff --git a/IPA.Loader/IllusionPlugin/Logging/LogPrinter.cs b/IPA.Loader/IllusionPlugin/Logging/LogPrinter.cs new file mode 100644 index 00000000..441029e6 --- /dev/null +++ b/IPA.Loader/IllusionPlugin/Logging/LogPrinter.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IllusionPlugin.Logging +{ + /// + /// The log printer's base class. + /// + public abstract class LogPrinter + { + /// + /// Provides a filter for which log levels to allow through. + /// + public abstract Logger.LogLevel Filter { get; set; } + /// + /// Prints a provided message from a given log at the specified time. + /// + /// the log level + /// the time the message was composed + /// the name of the log that created this message + /// the message + public abstract void Print(Logger.Level level, DateTime time, string logName, string message); + /// + /// Called before the first print in a group. May be called multiple times. + /// Use this to create file handles and the like. + /// + public virtual void StartPrint() { } + /// + /// Called after the last print in a group. May be called multiple times. + /// Use this to dispose file handles and the like. + /// + public virtual void EndPrint() { } + } +} diff --git a/IPA.Loader/IllusionPlugin/Logging/Logger.cs b/IPA.Loader/IllusionPlugin/Logging/Logger.cs new file mode 100644 index 00000000..befa4ccd --- /dev/null +++ b/IPA.Loader/IllusionPlugin/Logging/Logger.cs @@ -0,0 +1,182 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IllusionPlugin.Logging +{ + /// + /// The logger base class. Provides the format for console logs. + /// + public abstract class Logger + { + /// + /// The standard format for log messages. + /// + public static string LogFormat { get; protected internal set; } = "[{3} @ {2:HH:mm:ss} | {1}] {0}"; + + /// + /// An enum specifying the level of the message. Resembles Syslog. + /// + public enum Level : byte + { + /// + /// No associated level. These never get shown. + /// + None = 0, + /// + /// A debug message. + /// + Debug = 1, + /// + /// An informational message. + /// + Info = 2, + /// + /// A warning message. + /// + Warning = 4, + /// + /// An error message. + /// + Error = 8, + /// + /// A critical error message. + /// + Critical = 16 + } + + /// + /// An enum providing log level filters. + /// + [Flags] + public enum LogLevel : byte + { + /// + /// Allow no messages through. + /// + None = Level.None, + /// + /// Only shows Debug messages. + /// + DebugOnly = Level.Debug, + /// + /// Only shows info messages. + /// + InfoOnly = Level.Info, + /// + /// Only shows Warning messages. + /// + WarningOnly = Level.Warning, + /// + /// Only shows Error messages. + /// + ErrorOnly = Level.Error, + /// + /// Only shows Critical messages. + /// + CriticalOnly = Level.Critical, + + /// + /// Shows all messages error and up. + /// + ErrorUp = ErrorOnly | CriticalOnly, + /// + /// Shows all messages warning and up. + /// + WarningUp = WarningOnly | ErrorUp, + /// + /// Shows all messages info and up. + /// + InfoUp = InfoOnly | WarningUp, + /// + /// Shows all messages. + /// + All = DebugOnly | InfoUp, + } + + /// + /// A basic log function. + /// + /// the level of the message + /// the message to log + public abstract void Log(Level level, string message); + /// + /// A basic log function taking an exception to log. + /// + /// the level of the message + /// the exception to log + public virtual void Log(Level level, Exception exeption) => Log(level, exeption.ToString()); + /// + /// Sends a debug message. + /// Equivalent to Log(Level.Debug, message); + /// + /// + /// the message to log + public virtual void Debug(string message) => Log(Level.Debug, message); + /// + /// Sends an exception as a debug message. + /// Equivalent to Log(Level.Debug, e); + /// + /// + /// the exception to log + public virtual void Debug(Exception e) => Log(Level.Debug, e); + /// + /// Sends an info message. + /// Equivalent to Log(Level.Info, message). + /// + /// + /// the message to log + public virtual void Info(string message) => Log(Level.Info, message); + /// + /// Sends an exception as an info message. + /// Equivalent to Log(Level.Info, e); + /// + /// + /// the exception to log + public virtual void Info(Exception e) => Log(Level.Info, e); + /// + /// Sends a warning message. + /// Equivalent to Log(Level.Warning, message). + /// + /// + /// the message to log + public virtual void Warn(string message) => Log(Level.Warning, message); + /// + /// Sends an exception as a warning message. + /// Equivalent to Log(Level.Warning, e); + /// + /// + /// the exception to log + public virtual void Warn(Exception e) => Log(Level.Warning, e); + /// + /// Sends an error message. + /// Equivalent to Log(Level.Error, message). + /// + /// + /// the message to log + public virtual void Error(string message) => Log(Level.Error, message); + /// + /// Sends an exception as an error message. + /// Equivalent to Log(Level.Error, e); + /// + /// + /// the exception to log + public virtual void Error(Exception e) => Log(Level.Error, e); + /// + /// Sends a critical message. + /// Equivalent to Log(Level.Critical, message). + /// + /// + /// the message to log + public virtual void Critical(string message) => Log(Level.Critical, message); + /// + /// Sends an exception as a critical message. + /// Equivalent to Log(Level.Critical, e); + /// + /// + /// the exception to log + public virtual void Critical(Exception e) => Log(Level.Critical, e); + } +} diff --git a/IPA.Loader/IllusionPlugin/ModPrefs.cs b/IPA.Loader/IllusionPlugin/ModPrefs.cs new file mode 100644 index 00000000..198cc6aa --- /dev/null +++ b/IPA.Loader/IllusionPlugin/ModPrefs.cs @@ -0,0 +1,285 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace IllusionPlugin +{ + /// + /// Allows to get and set preferences for your mod. + /// + public interface IModPrefs + { + /// + /// Gets a string from the ini. + /// + /// Section of the key. + /// Name of the key. + /// Value that should be used when no value is found. + /// Whether or not the default value should be written if no value is found. + /// + string GetString(string section, string name, string defaultValue = "", bool autoSave = false); + /// + /// Gets an int from the ini. + /// + /// Section of the key. + /// Name of the key. + /// Value that should be used when no value is found. + /// Whether or not the default value should be written if no value is found. + /// + int GetInt(string section, string name, int defaultValue = 0, bool autoSave = false); + /// + /// Gets a float from the ini. + /// + /// Section of the key. + /// Name of the key. + /// Value that should be used when no value is found. + /// Whether or not the default value should be written if no value is found. + /// + float GetFloat(string section, string name, float defaultValue = 0f, bool autoSave = false); + /// + /// Gets a bool from the ini. + /// + /// Section of the key. + /// Name of the key. + /// Value that should be used when no value is found. + /// Whether or not the default value should be written if no value is found. + /// + bool GetBool(string section, string name, bool defaultValue = false, bool autoSave = false); + /// + /// Checks whether or not a key exists in the ini. + /// + /// Section of the key. + /// Name of the key. + /// + bool HasKey(string section, string name); + /// + /// Sets a float in the ini. + /// + /// Section of the key. + /// Name of the key. + /// Value that should be written. + void SetFloat(string section, string name, float value); + /// + /// Sets an int in the ini. + /// + /// Section of the key. + /// Name of the key. + /// Value that should be written. + void SetInt(string section, string name, int value); + /// + /// Sets a string in the ini. + /// + /// Section of the key. + /// Name of the key. + /// Value that should be written. + void SetString(string section, string name, string value); + /// + /// Sets a bool in the ini. + /// + /// Section of the key. + /// Name of the key. + /// Value that should be written. + void SetBool(string section, string name, bool value); + } + + /// + /// Allows to get and set preferences for your mod. + /// + public class ModPrefs : IModPrefs + { + private static ModPrefs _staticInstance = null; + private static IModPrefs StaticInstace + { + get + { + if (_staticInstance == null) + _staticInstance = new ModPrefs(); + return _staticInstance; + } + } + + internal static Dictionary ModPrefses { get; set; } = new Dictionary(); + + private IniFile Instance; + + /// + /// Constructs a ModPrefs object for the provide plugin. + /// + /// the plugin to get the preferences file for + public ModPrefs(IBeatSaberPlugin plugin) { + Instance = new IniFile(Path.Combine(Environment.CurrentDirectory, "UserData", "ModPrefs", $"{plugin.Name}.ini")); + ModPrefses.Add(plugin, this); + } + + private ModPrefs() + { + Instance = new IniFile(Path.Combine(Environment.CurrentDirectory, "UserData", "modprefs.ini")); + } + + string IModPrefs.GetString(string section, string name, string defaultValue, bool autoSave) + { + var value = Instance.IniReadValue(section, name); + if (value != "") + return value; + else if (autoSave) + (this as IModPrefs).SetString(section, name, defaultValue); + + return defaultValue; + } + /// + /// Gets a string from the ini. + /// + /// Section of the key. + /// Name of the key. + /// Value that should be used when no value is found. + /// Whether or not the default value should be written if no value is found. + /// + public static string GetString(string section, string name, string defaultValue = "", bool autoSave = false) + => StaticInstace.GetString(section, name, defaultValue, autoSave); + + int IModPrefs.GetInt(string section, string name, int defaultValue, bool autoSave) + { + if (int.TryParse(Instance.IniReadValue(section, name), out var value)) + return value; + else if (autoSave) + (this as IModPrefs).SetInt(section, name, defaultValue); + + return defaultValue; + } + /// + /// Gets an int from the ini. + /// + /// Section of the key. + /// Name of the key. + /// Value that should be used when no value is found. + /// Whether or not the default value should be written if no value is found. + /// + public static int GetInt(string section, string name, int defaultValue = 0, bool autoSave = false) + => StaticInstace.GetInt(section, name, defaultValue, autoSave); + + float IModPrefs.GetFloat(string section, string name, float defaultValue, bool autoSave) + { + if (float.TryParse(Instance.IniReadValue(section, name), out var value)) + return value; + else if (autoSave) + (this as IModPrefs).SetFloat(section, name, defaultValue); + + return defaultValue; + } + /// + /// Gets a float from the ini. + /// + /// Section of the key. + /// Name of the key. + /// Value that should be used when no value is found. + /// Whether or not the default value should be written if no value is found. + /// + public static float GetFloat(string section, string name, float defaultValue = 0f, bool autoSave = false) + => StaticInstace.GetFloat(section, name, defaultValue, autoSave); + + bool IModPrefs.GetBool(string section, string name, bool defaultValue, bool autoSave) + { + string sVal = GetString(section, name, null); + if (sVal == "1" || sVal == "0") + { + return sVal == "1"; + } else if (autoSave) + { + (this as IModPrefs).SetBool(section, name, defaultValue); + } + + return defaultValue; + } + /// + /// Gets a bool from the ini. + /// + /// Section of the key. + /// Name of the key. + /// Value that should be used when no value is found. + /// Whether or not the default value should be written if no value is found. + /// + public static bool GetBool(string section, string name, bool defaultValue = false, bool autoSave = false) + => StaticInstace.GetBool(section, name, defaultValue, autoSave); + + bool IModPrefs.HasKey(string section, string name) + { + return Instance.IniReadValue(section, name) != null; + } + /// + /// Checks whether or not a key exists in the ini. + /// + /// Section of the key. + /// Name of the key. + /// + public static bool HasKey(string section, string name) => StaticInstace.HasKey(section, name); + + void IModPrefs.SetFloat(string section, string name, float value) + { + Instance.IniWriteValue(section, name, value.ToString()); + } + /// + /// Sets a float in the ini. + /// + /// Section of the key. + /// Name of the key. + /// Value that should be written. + public static void SetFloat(string section, string name, float value) + => StaticInstace.SetFloat(section, name, value); + + void IModPrefs.SetInt(string section, string name, int value) + { + Instance.IniWriteValue(section, name, value.ToString()); + } + /// + /// Sets an int in the ini. + /// + /// Section of the key. + /// Name of the key. + /// Value that should be written. + public static void SetInt(string section, string name, int value) + => StaticInstace.SetInt(section, name, value); + + void IModPrefs.SetString(string section, string name, string value) + { + Instance.IniWriteValue(section, name, value); + } + /// + /// Sets a string in the ini. + /// + /// Section of the key. + /// Name of the key. + /// Value that should be written. + public static void SetString(string section, string name, string value) + => StaticInstace.SetString(section, name, value); + + void IModPrefs.SetBool(string section, string name, bool value) + { + Instance.IniWriteValue(section, name, value ? "1" : "0"); + } + /// + /// Sets a bool in the ini. + /// + /// Section of the key. + /// Name of the key. + /// Value that should be written. + public static void SetBool(string section, string name, bool value) + => StaticInstace.SetBool(section, name, value); + } + + /// + /// An extension class for IBeatSaberPlugins. + /// + public static class ModPrefsExtensions { + /// + /// Gets the ModPrefs object for the provided plugin. + /// + /// the plugin wanting the prefrences + /// the ModPrefs object + public static IModPrefs GetModPrefs(this IBeatSaberPlugin plugin) { + return ModPrefs.ModPrefses.First(o => o.Key == plugin).Value; + } + } +} diff --git a/IPA.Loader/IllusionPlugin/Utils/ReflectionUtil.cs b/IPA.Loader/IllusionPlugin/Utils/ReflectionUtil.cs new file mode 100644 index 00000000..cf8fdde1 --- /dev/null +++ b/IPA.Loader/IllusionPlugin/Utils/ReflectionUtil.cs @@ -0,0 +1,203 @@ +using System; +using System.Reflection; +using UnityEngine; + +namespace IllusionPlugin.Utils +{ + /// + /// A utility class providing reflection helper methods. + /// + public static class ReflectionUtil + { + /// + /// Sets a (potentially) private field on the target object. + /// + /// the object instance + /// the field to set + /// the value to set it to + public static void SetPrivateField(this object obj, string fieldName, object value) + { + var prop = obj.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); + prop.SetValue(obj, value); + } + + /// + /// Gets the value of a (potentially) private field. + /// + /// the type of te field (result casted) + /// the object instance to pull from + /// the name of the field to read + /// the value of the field + public static T GetPrivateField(this object obj, string fieldName) + { + var prop = obj.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance); + var value = prop.GetValue(obj); + return (T) value; + } + + /// + /// Sets a (potentially) private propert on the target object. + /// + /// the target object instance + /// the name of the property + /// the value to set it to + public static void SetPrivateProperty(this object obj, string propertyName, object value) + { + var prop = obj.GetType() + .GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); + prop.SetValue(obj, value, null); + } + + /// + /// Invokes a (potentially) private method. + /// + /// the object to call from + /// the method name + /// the method parameters + /// the return value + public static object InvokePrivateMethod(this object obj, string methodName, params object[] methodParams) + { + MethodInfo dynMethod = obj.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); + return dynMethod.Invoke(obj, methodParams); + } + + /// + /// Invokes a (potentially) private method. + /// + /// the return type + /// the object to call from + /// the method name to call + /// the method's parameters + /// the return value + public static T InvokePrivateMethod(this object obj, string methodName, params object[] methodParams) + { + return (T)InvokePrivateMethod(obj, methodName, methodParams); + } + + /// + /// Copies a component of type originalType to a component of overridingType on the destination GameObject. + /// + /// the original component + /// the new component's type + /// the destination GameObject + /// overrides the source component type (for example, to a superclass) + /// the copied component + public static Component CopyComponent(this Component original, Type overridingType, GameObject destination, Type originalTypeOverride = null) + { + var copy = destination.AddComponent(overridingType); + var originalType = originalTypeOverride ?? original.GetType(); + + Type type = originalType; + while (type != typeof(MonoBehaviour)) + { + CopyForType(type, original, copy); + type = type.BaseType; + } + + return copy; + } + + /// + /// A generic version of CopyComponent. + /// + /// + /// the overriding type + /// the original component + /// the destination game object + /// overrides the source component type (for example, to a superclass) + /// the copied component + public static T CopyComponent(this Component original, GameObject destination, Type originalTypeOverride = null) + where T : Component + { + var copy = destination.AddComponent(); + var originalType = originalTypeOverride ?? original.GetType(); + + Type type = originalType; + while (type != typeof(MonoBehaviour)) + { + CopyForType(type, original, copy); + type = type.BaseType; + } + + return copy; + } + + private static void CopyForType(Type type, Component source, Component destination) + { + FieldInfo[] myObjectFields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetField); + + foreach (FieldInfo fi in myObjectFields) + { + fi.SetValue(destination, fi.GetValue(source)); + } + } + + /// + /// Calls an instance method on a type specified by functionClass and dependency. + /// + /// + /// the type name + /// the assembly the type is in + /// the name of the method to call + /// the type signature of the method + /// the method parameters + /// the result of the call + public static object CallNonStaticMethod(string functionClass, string dependency, string function, Type[] methodSig, params object[] parameters) + { + return CallNonStaticMethod(Type.GetType(string.Format("{0},{1}", functionClass, dependency)), function, methodSig, parameters); + } + + /// + /// Calls an instance method on a new object. + /// + /// the object type + /// the name of the method to call + /// the type signature + /// the parameters + /// the result of the call + public static object CallNonStaticMethod(this Type type, /*string functionClass, string dependency,*/ string function, Type[] methodSig, params object[] parameters) + { + //Type FunctionClass = Type.GetType(string.Format("{0},{1}", functionClass, dependency)); + if (type != null) + { + object instance = Activator.CreateInstance(type); + if (instance != null) + { + Type instType = instance.GetType(); + MethodInfo methodInfo = instType.GetMethod(function, methodSig); + if (methodInfo != null) + { + return methodInfo.Invoke(instance, parameters); + } + else + { + throw new Exception("Method not found"); + } + } + else + { + throw new Exception("Unable to instantiate object of type"); + } + } + else + { + throw new ArgumentNullException("type"); + } + } + + /// + /// Calls an instance method on a new object. + /// + /// + /// the return type + /// the object type + /// the name of the method to call + /// the type signature + /// the parameters + /// the result of the call + public static T CallNonStaticMethod(this Type type, string function, Type[] methodSig, params object[] parameters) + { + return (T)CallNonStaticMethod(type, function, methodSig, parameters); + } + } +} diff --git a/IPA.Loader/Logging/Printers/ColoredConsolePrinter.cs b/IPA.Loader/Logging/Printers/ColoredConsolePrinter.cs new file mode 100644 index 00000000..a4759b28 --- /dev/null +++ b/IPA.Loader/Logging/Printers/ColoredConsolePrinter.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using IllusionPlugin.Logging; +using LoggerBase = IllusionPlugin.Logging.Logger; + +namespace IllusionInjector.Logging.Printers +{ + public class ColoredConsolePrinter : LogPrinter + { + LoggerBase.LogLevel filter = LoggerBase.LogLevel.All; + public override LoggerBase.LogLevel Filter { get => filter; set => filter = value; } + + ConsoleColor color = Console.ForegroundColor; + public ConsoleColor Color { get => color; set => color = value; } + + public override void Print(LoggerBase.Level level, DateTime time, string logName, string message) + { + if (((byte)level & (byte)StandardLogger.PrintFilter) == 0) return; + Console.ForegroundColor = color; + foreach (var line in message.Split(new string[] { "\n", Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)) + Console.WriteLine(string.Format(LoggerBase.LogFormat, line, logName, time, level.ToString().ToUpper())); + Console.ResetColor(); + } + } +} diff --git a/IPA.Loader/Logging/Printers/GZFilePrinter.cs b/IPA.Loader/Logging/Printers/GZFilePrinter.cs new file mode 100644 index 00000000..7a3fac36 --- /dev/null +++ b/IPA.Loader/Logging/Printers/GZFilePrinter.cs @@ -0,0 +1,92 @@ +using IllusionPlugin.Logging; +using Ionic.Zlib; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace IllusionInjector.Logging.Printers +{ + public abstract class GZFilePrinter : LogPrinter + { + [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + static extern bool CreateHardLink( + string lpFileName, + string lpExistingFileName, + IntPtr lpSecurityAttributes + ); + + [DllImport("Kernel32.dll")] + static extern Int32 GetLastError(); + + private FileInfo fileInfo; + protected StreamWriter fileWriter; + private GZipStream zstream; + private FileStream fstream; + + protected abstract FileInfo GetFileInfo(); + + private void InitLog() + { + try + { + if (fileInfo == null) + { // first init + fileInfo = GetFileInfo(); + var ext = fileInfo.Extension; + fileInfo = new FileInfo(fileInfo.FullName + ".gz"); + fileInfo.Create().Close(); + + var symlink = new FileInfo(Path.Combine(fileInfo.DirectoryName, $"latest{ext}.gz")); + if (symlink.Exists) symlink.Delete(); + + try + { + if (!CreateHardLink(symlink.FullName, fileInfo.FullName, IntPtr.Zero)) + { + Logger.log.Error($"Hardlink creation failed {GetLastError()}"); + } + } + catch (Exception e) + { + Logger.log.Error("Error creating latest hardlink!"); + Logger.log.Error(e); + } + } + } + catch (Exception e) + { + Logger.log.Error("Error initializing log!"); + Logger.log.Error(e); + } + } + + public override sealed void StartPrint() + { + InitLog(); + + fstream = fileInfo.Open(FileMode.Append, FileAccess.Write); + zstream = new GZipStream(fstream, CompressionMode.Compress) + { + FlushMode = FlushType.Full + }; + fileWriter = new StreamWriter(zstream, new UTF8Encoding(false)); + } + + public override sealed void EndPrint() + { + fileWriter.Flush(); + zstream.Flush(); + fstream.Flush(); + fileWriter.Close(); + zstream.Close(); + fstream.Close(); + fileWriter.Dispose(); + zstream.Dispose(); + fstream.Dispose(); + } + } +} diff --git a/IPA.Loader/Logging/Printers/GlobalLogFilePrinter.cs b/IPA.Loader/Logging/Printers/GlobalLogFilePrinter.cs new file mode 100644 index 00000000..9f617b72 --- /dev/null +++ b/IPA.Loader/Logging/Printers/GlobalLogFilePrinter.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using IllusionPlugin.Logging; +using LoggerBase = IllusionPlugin.Logging.Logger; + +namespace IllusionInjector.Logging.Printers +{ + class GlobalLogFilePrinter : GZFilePrinter + { + public override LoggerBase.LogLevel Filter { get; set; } = LoggerBase.LogLevel.All; + + public override void Print(IllusionPlugin.Logging.Logger.Level level, DateTime time, string logName, string message) + { + foreach (var line in message.Split(new string[] { "\n", Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)) + fileWriter.WriteLine(string.Format(LoggerBase.LogFormat, line, logName, time, level.ToString().ToUpper())); + } + + protected override FileInfo GetFileInfo() + { + var logsDir = new DirectoryInfo("Logs"); + logsDir.Create(); + var finfo = new FileInfo(Path.Combine(logsDir.FullName, $"{DateTime.Now:yyyy.MM.dd.HH.mm}.log")); + return finfo; + } + } +} diff --git a/IPA.Loader/Logging/Printers/PluginLogFilePrinter.cs b/IPA.Loader/Logging/Printers/PluginLogFilePrinter.cs new file mode 100644 index 00000000..cd0c41e4 --- /dev/null +++ b/IPA.Loader/Logging/Printers/PluginLogFilePrinter.cs @@ -0,0 +1,37 @@ +using IllusionPlugin.Logging; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using LoggerBase = IllusionPlugin.Logging.Logger; + +namespace IllusionInjector.Logging.Printers +{ + class PluginLogFilePrinter : GZFilePrinter + { + public override LoggerBase.LogLevel Filter { get; set; } = LoggerBase.LogLevel.All; + + private string name; + + protected override FileInfo GetFileInfo() + { + var logsDir = new DirectoryInfo(Path.Combine("Logs",name)); + logsDir.Create(); + var finfo = new FileInfo(Path.Combine(logsDir.FullName, $"{DateTime.Now:yyyy.MM.dd.HH.mm}.log")); + return finfo; + } + + public PluginLogFilePrinter(string name) + { + this.name = name; + } + + public override void Print(IllusionPlugin.Logging.Logger.Level level, DateTime time, string logName, string message) + { + foreach (var line in message.Split(new string[] { "\n", Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)) + fileWriter.WriteLine(string.Format("[{3} @ {2:HH:mm:ss}] {0}", line, logName, time, level.ToString().ToUpper())); + } + } +} diff --git a/IPA.Loader/Logging/StandardLogger.cs b/IPA.Loader/Logging/StandardLogger.cs new file mode 100644 index 00000000..c2c9a484 --- /dev/null +++ b/IPA.Loader/Logging/StandardLogger.cs @@ -0,0 +1,168 @@ +using IllusionInjector.Logging.Printers; +using IllusionPlugin; +using IllusionPlugin.Logging; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using LoggerBase = IllusionPlugin.Logging.Logger; + +namespace IllusionInjector.Logging +{ + internal static class Logger + { + private static LoggerBase _log; + internal static LoggerBase log + { + get + { + if (_log == null) + _log = new StandardLogger("IPA"); + return _log; + } + } + internal static bool LogCreated => _log != null; + } + + public class StandardLogger : LoggerBase + { + private static readonly IReadOnlyList defaultPrinters = new List() + { + new ColoredConsolePrinter() + { + Filter = LogLevel.DebugOnly, + Color = ConsoleColor.Green, + }, + new ColoredConsolePrinter() + { + Filter = LogLevel.InfoOnly, + Color = ConsoleColor.White, + }, + new ColoredConsolePrinter() + { + Filter = LogLevel.WarningOnly, + Color = ConsoleColor.Yellow, + }, + new ColoredConsolePrinter() + { + Filter = LogLevel.ErrorOnly, + Color = ConsoleColor.Red, + }, + new ColoredConsolePrinter() + { + Filter = LogLevel.CriticalOnly, + Color = ConsoleColor.Magenta, + }, + new GlobalLogFilePrinter() + }; + + private string logName; + private static bool showSourceClass = true; + public static LogLevel PrintFilter { get; set; } = LogLevel.InfoUp; + private List printers = new List(defaultPrinters); + + static StandardLogger() + { + if (ModPrefs.GetBool("IPA", "PrintDebug", false, true)) + PrintFilter = LogLevel.All; + showSourceClass = ModPrefs.GetBool("IPA", "DebugShowCallSource", false, true); + } + + internal StandardLogger(string name) + { + logName = name; + + printers.Add(new PluginLogFilePrinter(name)); + + if (_logThread == null || !_logThread.IsAlive) + { + _logThread = new Thread(LogThread); + _logThread.Start(); + } + } + + public override void Log(Level level, string message) + { + _logQueue.Add(new LogMessage + { + level = level, + message = message, + logger = this, + time = DateTime.Now + }); + } + + public override void Debug(string message) + { // add source to message + var stfm = new StackTrace().GetFrame(1).GetMethod(); + if (showSourceClass) + base.Debug($"{{{stfm.DeclaringType.FullName}::{stfm.Name}}} {message}"); + else + base.Debug(message); + } + + internal struct LogMessage + { + public Level level; + public StandardLogger logger; + public string message; + public DateTime time; + } + + private static BlockingCollection _logQueue = new BlockingCollection(); + private static Thread _logThread; + + private static void LogThread() + { + HashSet started = new HashSet(); + while (_logQueue.TryTake(out LogMessage msg, Timeout.Infinite)) { + foreach (var printer in msg.logger.printers) + { + try + { + if (((byte)msg.level & (byte)printer.Filter) != 0) + { + if (!started.Contains(printer)) + { + printer.StartPrint(); + started.Add(printer); + } + + printer.Print(msg.level, msg.time, msg.logger.logName, msg.message); + } + } + catch (Exception e) + { + Console.WriteLine($"printer errored {e}"); + } + } + + if (_logQueue.Count == 0) + { + foreach (var printer in started) + { + try + { + printer.EndPrint(); + } + catch (Exception e) + { + Console.WriteLine($"printer errored {e}"); + } + } + started.Clear(); + } + } + } + + public static void StopLogThread() + { + _logQueue.CompleteAdding(); + _logThread.Join(); + } + } +} diff --git a/IPA.Loader/Logging/UnityLogInterceptor.cs b/IPA.Loader/Logging/UnityLogInterceptor.cs new file mode 100644 index 00000000..64e42926 --- /dev/null +++ b/IPA.Loader/Logging/UnityLogInterceptor.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using LoggerBase = IllusionPlugin.Logging.Logger; + +namespace IllusionInjector.Logging +{ + public class UnityLogInterceptor + { + public static LoggerBase Unitylogger = new StandardLogger("UnityEngine"); + + public static LoggerBase.Level LogTypeToLevel(LogType type) + { + switch (type) + { + case LogType.Assert: + return LoggerBase.Level.Debug; + case LogType.Error: + return LoggerBase.Level.Error; + case LogType.Exception: + return LoggerBase.Level.Critical; + case LogType.Log: + return LoggerBase.Level.Info; + case LogType.Warning: + return LoggerBase.Level.Warning; + default: + return LoggerBase.Level.Info; + } + } + } +} diff --git a/IPA.Loader/PluginComponent.cs b/IPA.Loader/PluginComponent.cs new file mode 100644 index 00000000..1f3fc9d3 --- /dev/null +++ b/IPA.Loader/PluginComponent.cs @@ -0,0 +1,109 @@ +using IllusionInjector.Logging; +using System; +using System.Collections.Generic; +using System.Text; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace IllusionInjector +{ + public class PluginComponent : MonoBehaviour + { + private CompositeBSPlugin bsPlugins; + private CompositeIPAPlugin ipaPlugins; + private bool quitting = false; + + public static PluginComponent Create() + { + Application.logMessageReceived += delegate (string condition, string stackTrace, LogType type) + { + var level = UnityLogInterceptor.LogTypeToLevel(type); + UnityLogInterceptor.Unitylogger.Log(level, $"{condition.Trim()}"); + UnityLogInterceptor.Unitylogger.Log(level, $"{stackTrace.Trim()}"); + }; + + return new GameObject("IPA_PluginManager").AddComponent(); + } + + void Awake() + { + DontDestroyOnLoad(gameObject); + + bsPlugins = new CompositeBSPlugin(PluginManager.BSPlugins); + ipaPlugins = new CompositeIPAPlugin(PluginManager.Plugins); + + // this has no relevance since there is a new mod updater system + //gameObject.AddComponent(); // AFTER plugins are loaded, but before most things + gameObject.AddComponent(); + + bsPlugins.OnApplicationStart(); + ipaPlugins.OnApplicationStart(); + + SceneManager.activeSceneChanged += OnActiveSceneChanged; + SceneManager.sceneLoaded += OnSceneLoaded; + SceneManager.sceneUnloaded += OnSceneUnloaded; + } + + void Update() + { + bsPlugins.OnUpdate(); + ipaPlugins.OnUpdate(); + } + + void LateUpdate() + { + bsPlugins.OnLateUpdate(); + ipaPlugins.OnLateUpdate(); + } + + void FixedUpdate() + { + bsPlugins.OnFixedUpdate(); + ipaPlugins.OnFixedUpdate(); + } + + void OnDestroy() + { + if (!quitting) + { + Create(); + } + } + + void OnApplicationQuit() + { + SceneManager.activeSceneChanged -= OnActiveSceneChanged; + SceneManager.sceneLoaded -= OnSceneLoaded; + SceneManager.sceneUnloaded -= OnSceneUnloaded; + + bsPlugins.OnApplicationQuit(); + ipaPlugins.OnApplicationQuit(); + + quitting = true; + } + + void OnLevelWasLoaded(int level) + { + ipaPlugins.OnLevelWasLoaded(level); + } + + public void OnLevelWasInitialized(int level) + { + ipaPlugins.OnLevelWasInitialized(level); + } + + void OnSceneLoaded(Scene scene, LoadSceneMode sceneMode) + { + bsPlugins.OnSceneLoaded(scene, sceneMode); + } + + private void OnSceneUnloaded(Scene scene) { + bsPlugins.OnSceneUnloaded(scene); + } + + private void OnActiveSceneChanged(Scene prevScene, Scene nextScene) { + bsPlugins.OnActiveSceneChanged(prevScene, nextScene); + } + + } +} diff --git a/IPA.Loader/PluginManager.cs b/IPA.Loader/PluginManager.cs new file mode 100644 index 00000000..14c67222 --- /dev/null +++ b/IPA.Loader/PluginManager.cs @@ -0,0 +1,264 @@ +using IllusionInjector.Logging; +using IllusionInjector.Updating; +using IllusionInjector.Utilities; +using IllusionPlugin; +using IllusionPlugin.BeatSaber; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using LoggerBase = IllusionPlugin.Logging.Logger; + +namespace IllusionInjector +{ + public static class PluginManager + { +#pragma warning disable CS0618 // Type or member is obsolete (IPlugin) + + public class BSPluginMeta + { + public IBeatSaberPlugin Plugin { get; internal set; } + public string Filename { get; internal set; } + public ModsaberModInfo ModsaberInfo { get; internal set; } + } + + public static IEnumerable BSPlugins + { + get + { + if(_bsPlugins == null) + { + LoadPlugins(); + } + return _bsPlugins.Select(p => p.Plugin); + } + } + private static List _bsPlugins = null; + internal static IEnumerable BSMetas + { + get + { + if (_bsPlugins == null) + { + LoadPlugins(); + } + return _bsPlugins; + } + } + + public static IEnumerable Plugins + { + get + { + if (_ipaPlugins == null) + { + LoadPlugins(); + } + return _ipaPlugins; + } + } + private static List _ipaPlugins = null; + + + + private static void LoadPlugins() + { + string pluginDirectory = Path.Combine(Environment.CurrentDirectory, "Plugins"); + + // 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); + Logger.log.Info(exeName); + _bsPlugins = new List(); + _ipaPlugins = new List(); + + 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); + } + } + + //Copy plugins to .cache + string[] originalPlugins = Directory.GetFiles(pluginDirectory, "*.dll"); + foreach (string s in originalPlugins) + { + string pluginCopy = Path.Combine(cacheDir, Path.GetFileName(s)); + File.Copy(Path.Combine(pluginDirectory, s), pluginCopy); + } + + var selfPlugin = new BSPluginMeta + { + Filename = Path.Combine(Environment.CurrentDirectory, "IPA.exe"), + Plugin = new SelfPlugin() + }; + selfPlugin.ModsaberInfo = selfPlugin.Plugin.ModInfo; + + _bsPlugins.Add(selfPlugin); + + //Load copied plugins + string[] copiedPlugins = Directory.GetFiles(cacheDir, "*.dll"); + foreach (string s in copiedPlugins) + { + var result = LoadPluginsFromFile(s, exeName); + _bsPlugins.AddRange(result.Item1); + _ipaPlugins.AddRange(result.Item2); + } + + + // DEBUG + Logger.log.Info($"Running on Unity {UnityEngine.Application.unityVersion}"); + Logger.log.Info($"Game version {UnityEngine.Application.version}"); + Logger.log.Info("-----------------------------"); + Logger.log.Info($"Loading plugins from {LoneFunctions.GetRelativePath(pluginDirectory, Environment.CurrentDirectory)} and found {_bsPlugins.Count + _ipaPlugins.Count}"); + Logger.log.Info("-----------------------------"); + foreach (var plugin in _bsPlugins) + { + Logger.log.Info($"{plugin.Plugin.Name}: {plugin.Plugin.Version}"); + } + Logger.log.Info("-----------------------------"); + foreach (var plugin in _ipaPlugins) + { + Logger.log.Info($"{plugin.Name}: {plugin.Version}"); + } + Logger.log.Info("-----------------------------"); + } + + private static Tuple, IEnumerable> LoadPluginsFromFile(string file, string exeName) + { + List bsPlugins = new List(); + List ipaPlugins = new List(); + + if (!File.Exists(file) || !file.EndsWith(".dll", true, null)) + return new Tuple, IEnumerable>(bsPlugins, ipaPlugins); + + T OptionalGetPlugin(Type t) where T : class + { + // use typeof() to allow for easier renaming (in an ideal world this compiles to a string, but ¯\_(ツ)_/¯) + if (t.GetInterface(typeof(T).Name) != null) + { + try + { + T pluginInstance = Activator.CreateInstance(t) as T; + string[] filter = null; + + if (pluginInstance is IGenericEnhancedPlugin) + { + filter = ((IGenericEnhancedPlugin)pluginInstance).Filter; + } + + if (filter == null || filter.Contains(exeName, StringComparer.OrdinalIgnoreCase)) + return pluginInstance; + } + catch (Exception e) + { + Logger.log.Error($"Could not load plugin {t.FullName} in {Path.GetFileName(file)}! {e}"); + } + } + + return null; + } + + try + { + Assembly assembly = Assembly.LoadFrom(file); + + foreach (Type t in assembly.GetTypes()) + { + IBeatSaberPlugin bsPlugin = OptionalGetPlugin(t); + if (bsPlugin != null) + { + try + { + var init = t.GetMethod("Init", BindingFlags.Instance | BindingFlags.Public); + if (init != null) + { + var initArgs = new List(); + var initParams = init.GetParameters(); + + LoggerBase modLogger = null; + IModPrefs modPrefs = null; + + foreach (var param in initParams) + { + var ptype = param.ParameterType; + if (ptype.IsAssignableFrom(typeof(LoggerBase))) { + if (modLogger == null) modLogger = new StandardLogger(bsPlugin.Name); + initArgs.Add(modLogger); + } + else if (ptype.IsAssignableFrom(typeof(IModPrefs))) + { + if (modPrefs == null) modPrefs = new ModPrefs(bsPlugin); + initArgs.Add(modPrefs); + } + else + initArgs.Add(ptype.GetDefault()); + } + + init.Invoke(bsPlugin, initArgs.ToArray()); + } + + bsPlugins.Add(new BSPluginMeta + { + Plugin = bsPlugin, + Filename = file.Replace("\\.cache", ""), // quick and dirty fix + ModsaberInfo = bsPlugin.ModInfo + }); + } + catch (AmbiguousMatchException) + { + Logger.log.Error($"Only one Init allowed per plugin"); + } + } + else + { + IPlugin ipaPlugin = OptionalGetPlugin(t); + if (ipaPlugin != null) + { + ipaPlugins.Add(ipaPlugin); + } + } + } + + } + catch (Exception e) + { + Logger.log.Error($"Could not load {Path.GetFileName(file)}! {e}"); + } + + return new Tuple, IEnumerable>(bsPlugins, ipaPlugins); + } + + public class AppInfo + { + [DllImport("kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = false)] + private static extern int GetModuleFileName(HandleRef hModule, StringBuilder buffer, int length); + private static HandleRef NullHandleRef = new HandleRef(null, IntPtr.Zero); + public static string StartupPath + { + get + { + StringBuilder stringBuilder = new StringBuilder(260); + GetModuleFileName(NullHandleRef, stringBuilder, stringBuilder.Capacity); + return stringBuilder.ToString(); + } + } + } +#pragma warning restore CS0618 // Type or member is obsolete (IPlugin) + } +} diff --git a/IPA.Loader/Properties/AssemblyInfo.cs b/IPA.Loader/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..a4c96091 --- /dev/null +++ b/IPA.Loader/Properties/AssemblyInfo.cs @@ -0,0 +1,37 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("IPA.Loader")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("IPA.Loader")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("5ad344f0-01a0-4ca8-92e5-9d095737744d")] +[assembly: InternalsVisibleTo("IPA.Injector")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/IPA.Loader/Updating/ModsaberML/ApiEndpoint.cs b/IPA.Loader/Updating/ModsaberML/ApiEndpoint.cs new file mode 100644 index 00000000..6465f381 --- /dev/null +++ b/IPA.Loader/Updating/ModsaberML/ApiEndpoint.cs @@ -0,0 +1,123 @@ +using IllusionInjector.Logging; +using IllusionInjector.Utilities; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IllusionInjector.Updating.ModsaberML +{ + class ApiEndpoint + { +#if DEBUG && UPDATETEST + public const string ApiBase = "file://Z:/Users/aaron/Source/Repos/IPA-Reloaded-BeatSaber/IPA.Tests/"; + public const string GetApprovedEndpoint = "updater_test.json"; +#else + public const string ApiBase = "https://www.modsaber.ml/"; + public const string GetApprovedEndpoint = "registry/{0}"; +#endif + + class HexArrayConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return objectType == typeof(byte[]); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) + { + return null; + } + if (reader.TokenType == JsonToken.String) + { + try + { + return LoneFunctions.StringToByteArray((string)reader.Value); + } + catch (Exception ex) + { + throw new Exception(string.Format("Error parsing version string: {0}", reader.Value), ex); + } + } + throw new Exception(string.Format("Unexpected token or value when parsing hex string. Token: {0}, Value: {1}", reader.TokenType, reader.Value)); + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (value == null) + { + writer.WriteNull(); + } + else + { + if (!(value is byte[])) + { + throw new JsonSerializationException("Expected byte[] object value"); + } + writer.WriteValue(LoneFunctions.ByteArrayToString(value as byte[])); + } + } + } + + [Serializable] + public class Mod + { +#pragma warning disable CS0649 + [JsonProperty("name")] + public string Name; + [JsonProperty("version"), + JsonConverter(typeof(VersionConverter))] + public Version Version; + [JsonProperty("approved")] + public bool Approved; + [JsonProperty("title")] + public string Title; + [JsonProperty("gameVersion"), + JsonConverter(typeof(VersionConverter))] + public Version GameVersion; + [JsonProperty("author")] + public string Author; +#pragma warning restore CS0649 + [Serializable] + public class PlatformFile + { + [JsonProperty("hash"), + JsonConverter(typeof(HexArrayConverter))] + public byte[] Hash = new byte[20]; + [JsonProperty("files", ItemConverterType = typeof(HexArrayConverter))] + public Dictionary FileHashes = new Dictionary(); + [JsonProperty("url")] + public string DownloadPath = null; + + public override string ToString() + { + return $"{LoneFunctions.ByteArrayToString(Hash)}@{DownloadPath}({string.Join(",",FileHashes.Select(o=>$"\"{o.Key}\":\"{LoneFunctions.ByteArrayToString(o.Value)}\""))})"; + } + } + + [Serializable] + public class FilesObject + { + [JsonProperty("steam")] + public PlatformFile Steam = null; + [JsonProperty("oculus")] + public PlatformFile Oculus = null; + } + + [JsonProperty("files")] + public FilesObject Files = null; + + public override string ToString() + { + return $"{{\"{Title} ({Name})\"v{Version} for {GameVersion} by {Author} with \"{Files.Steam}\" and \"{Files.Oculus}\"}}"; + } + } + + } +} diff --git a/IPA.Loader/Updating/ModsaberML/Updater.cs b/IPA.Loader/Updating/ModsaberML/Updater.cs new file mode 100644 index 00000000..26a5c653 --- /dev/null +++ b/IPA.Loader/Updating/ModsaberML/Updater.cs @@ -0,0 +1,346 @@ +using IllusionInjector.Updating.Backup; +using IllusionInjector.Utilities; +using Ionic.Zip; +using Newtonsoft.Json; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using UnityEngine; +using UnityEngine.Networking; +using Logger = IllusionInjector.Logging.Logger; + +namespace IllusionInjector.Updating.ModsaberML +{ + class Updater : MonoBehaviour + { + public static Updater instance; + + public void Awake() + { + try + { + if (instance != null) + Destroy(this); + else + { + instance = this; + CheckForUpdates(); + } + } + catch (Exception e) + { + Logger.log.Error(e); + } + } + + public void CheckForUpdates() + { + StartCoroutine(CheckForUpdatesCoroutine()); + } + + private struct UpdateStruct + { + public PluginManager.BSPluginMeta plugin; + public ApiEndpoint.Mod externInfo; + } + + IEnumerator CheckForUpdatesCoroutine() + { + Logger.log.Info("Checking for mod updates..."); + + var toUpdate = new List(); + var GameVersion = new Version(Application.version); + + foreach (var plugin in PluginManager.BSMetas) + { + var info = plugin.ModsaberInfo; + if (info == null) continue; + + using (var request = UnityWebRequest.Get(ApiEndpoint.ApiBase + string.Format(ApiEndpoint.GetApprovedEndpoint, info.InternalName))) + { + yield return request.SendWebRequest(); + + if (request.isNetworkError) + { + Logger.log.Error("Network error while trying to update mods"); + Logger.log.Error(request.error); + continue; + } + if (request.isHttpError) + { + if (request.responseCode == 404) + { + Logger.log.Error($"Mod {plugin.Plugin.Name} not found under name {info.InternalName}"); + continue; + } + + Logger.log.Error($"Server returned an error code while trying to update mod {plugin.Plugin.Name}"); + Logger.log.Error(request.error); + continue; + } + + var json = request.downloadHandler.text; + + ApiEndpoint.Mod modRegistry; + try + { + modRegistry = JsonConvert.DeserializeObject(json); + Logger.log.Debug(modRegistry.ToString()); + } + catch (Exception e) + { + Logger.log.Error($"Parse error while trying to update mods"); + Logger.log.Error(e); + continue; + } + + Logger.log.Debug($"Found Modsaber.ML registration for {plugin.Plugin.Name} ({info.InternalName})"); + Logger.log.Debug($"Installed version: {info.CurrentVersion}; Latest version: {modRegistry.Version}"); + if (modRegistry.Version > info.CurrentVersion) + { + Logger.log.Debug($"{plugin.Plugin.Name} needs an update!"); + if (modRegistry.GameVersion == GameVersion) + { + Logger.log.Debug($"Queueing update..."); + toUpdate.Add(new UpdateStruct + { + plugin = plugin, + externInfo = modRegistry + }); + } + else + { + Logger.log.Warn($"Update avaliable for {plugin.Plugin.Name}, but for a different Beat Saber version!"); + } + } + } + } + + Logger.log.Info($"{toUpdate.Count} mods need updating"); + + if (toUpdate.Count == 0) yield break; + + string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + Path.GetRandomFileName()); + Directory.CreateDirectory(tempDirectory); + foreach (var item in toUpdate) + { + StartCoroutine(UpdateModCoroutine(item, tempDirectory)); + } + } + + class StreamDownloadHandler : DownloadHandlerScript + { + public MemoryStream Stream { get; set; } + + public StreamDownloadHandler(MemoryStream stream) : base() + { + Stream = stream; + } + + protected override void ReceiveContentLength(int contentLength) + { + Stream.Capacity = contentLength; + Logger.log.Debug($"Got content length: {contentLength}"); + } + + protected override void CompleteContent() + { + Logger.log.Debug("Download complete"); + } + + protected override bool ReceiveData(byte[] data, int dataLength) + { + if (data == null || data.Length < 1) + { + Logger.log.Debug("CustomWebRequest :: ReceiveData - received a null/empty buffer"); + return false; + } + + Stream.Write(data, 0, dataLength); + return true; + } + + protected override byte[] GetData() { return null; } + + protected override float GetProgress() + { + return 0f; + } + + public override string ToString() + { + return $"{base.ToString()} ({Stream?.ToString()})"; + } + } + + private void ExtractPluginAsync(MemoryStream stream, UpdateStruct item, ApiEndpoint.Mod.PlatformFile fileInfo, string tempDirectory) + { + Logger.log.Debug($"Extracting ZIP file for {item.plugin.Plugin.Name}"); + + var data = stream.GetBuffer(); + SHA1 sha = new SHA1CryptoServiceProvider(); + var hash = sha.ComputeHash(data); + if (!LoneFunctions.UnsafeCompare(hash, fileInfo.Hash)) + throw new Exception("The hash for the file doesn't match what is defined"); + + var newFiles = new List(); + var backup = new BackupUnit(tempDirectory, $"backup-{item.plugin.ModsaberInfo.InternalName}"); + + try + { + bool shouldDeleteOldFile = true; + + using (var zipFile = ZipFile.Read(stream)) + { + Logger.log.Debug("Streams opened"); + foreach (var entry in zipFile) + { + if (entry.IsDirectory) + { + Logger.log.Debug($"Creating directory {entry.FileName}"); + Directory.CreateDirectory(Path.Combine(Environment.CurrentDirectory, entry.FileName)); + } + else + { + using (var ostream = new MemoryStream((int)entry.UncompressedSize)) + { + entry.Extract(ostream); + ostream.Seek(0, SeekOrigin.Begin); + + sha = new SHA1CryptoServiceProvider(); + var fileHash = sha.ComputeHash(ostream); + if (!LoneFunctions.UnsafeCompare(fileHash, fileInfo.FileHashes[entry.FileName])) + throw new Exception("The hash for the file doesn't match what is defined"); + + ostream.Seek(0, SeekOrigin.Begin); + FileInfo targetFile = new FileInfo(Path.Combine(Environment.CurrentDirectory, entry.FileName)); + Directory.CreateDirectory(targetFile.DirectoryName); + + if (targetFile.FullName == item.plugin.Filename) + shouldDeleteOldFile = false; // overwriting old file, no need to delete + + if (targetFile.Exists) + backup.Add(targetFile); + else + newFiles.Add(targetFile); + + Logger.log.Debug($"Extracting file {targetFile.FullName}"); + + var fstream = targetFile.Create(); + ostream.CopyTo(fstream); + } + } + } + } + + if (item.plugin.Plugin is SelfPlugin) + { // currently updating self + Process.Start(new ProcessStartInfo + { + FileName = item.plugin.Filename, + Arguments = $"--waitfor={Process.GetCurrentProcess().Id} --nowait", + UseShellExecute = false + }); + } + else if (shouldDeleteOldFile) + File.Delete(item.plugin.Filename); + } + catch (Exception) + { // something failed; restore + foreach (var file in newFiles) + file.Delete(); + backup.Restore(); + backup.Delete(); + + throw; + } + + backup.Delete(); + + Logger.log.Debug("Downloader exited"); + } + + IEnumerator UpdateModCoroutine(UpdateStruct item, string tempDirectory) + { + Logger.log.Debug($"Steam avaliable: {SteamCheck.IsAvailable}"); + + ApiEndpoint.Mod.PlatformFile platformFile; + if (SteamCheck.IsAvailable || item.externInfo.Files.Oculus == null) + platformFile = item.externInfo.Files.Steam; + else + platformFile = item.externInfo.Files.Oculus; + + string url = platformFile.DownloadPath; + + Logger.log.Debug($"URL = {url}"); + + const int MaxTries = 3; + int maxTries = MaxTries; + while (maxTries > 0) + { + if (maxTries-- != MaxTries) + Logger.log.Info($"Re-trying download..."); + + using (var stream = new MemoryStream()) + using (var request = UnityWebRequest.Get(url)) + using (var taskTokenSource = new CancellationTokenSource()) + { + var dlh = new StreamDownloadHandler(stream); + request.downloadHandler = dlh; + + Logger.log.Debug("Sending request"); + //Logger.log.Debug(request?.downloadHandler?.ToString() ?? "DLH==NULL"); + yield return request.SendWebRequest(); + Logger.log.Debug("Download finished"); + + if (request.isNetworkError) + { + Logger.log.Error("Network error while trying to update mod"); + Logger.log.Error(request.error); + taskTokenSource.Cancel(); + continue; + } + if (request.isHttpError) + { + Logger.log.Error($"Server returned an error code while trying to update mod"); + Logger.log.Error(request.error); + taskTokenSource.Cancel(); + continue; + } + + stream.Seek(0, SeekOrigin.Begin); // reset to beginning + + var downloadTask = Task.Run(() => + { // use slightly more multithreaded approach than coroutines + ExtractPluginAsync(stream, item, platformFile, tempDirectory); + }, taskTokenSource.Token); + + while (!(downloadTask.IsCompleted || downloadTask.IsCanceled || downloadTask.IsFaulted)) + yield return null; // pause coroutine until task is done + + if (downloadTask.IsFaulted) + { + Logger.log.Error($"Error downloading mod {item.plugin.Plugin.Name}"); + Logger.log.Error(downloadTask.Exception); + continue; + } + + break; + } + } + + if (maxTries == 0) + Logger.log.Warn($"Plugin download failed {MaxTries} times, not re-trying"); + else + Logger.log.Debug("Download complete"); + } + } +} diff --git a/IPA.Loader/Updating/SelfPlugin.cs b/IPA.Loader/Updating/SelfPlugin.cs new file mode 100644 index 00000000..b24f5642 --- /dev/null +++ b/IPA.Loader/Updating/SelfPlugin.cs @@ -0,0 +1,55 @@ +using IllusionPlugin; +using IllusionPlugin.BeatSaber; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine.SceneManagement; + +namespace IllusionInjector.Updating +{ + internal class SelfPlugin : IBeatSaberPlugin + { + internal const string IPA_Name = "Beat Saber IPA"; + internal const string IPA_Version = "3.9.0"; + + public string Name => IPA_Name; + + public string Version => IPA_Version; + + public ModsaberModInfo ModInfo => new ModsaberModInfo + { + CurrentVersion = new Version(IPA_Version), + InternalName = "beatsaber-ipa-reloaded" + }; + + public void OnActiveSceneChanged(Scene prevScene, Scene nextScene) + { + } + + public void OnApplicationQuit() + { + } + + public void OnApplicationStart() + { + } + + public void OnFixedUpdate() + { + } + + public void OnSceneLoaded(Scene scene, LoadSceneMode sceneMode) + { + } + + public void OnSceneUnloaded(Scene scene) + { + } + + public void OnUpdate() + { + } + } +} diff --git a/IPA.Loader/Utilities/Extensions.cs b/IPA.Loader/Utilities/Extensions.cs new file mode 100644 index 00000000..326c675a --- /dev/null +++ b/IPA.Loader/Utilities/Extensions.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IllusionInjector.Utilities +{ + public static class Extensions + { + public static object GetDefault(this Type type) + { + if (type.IsValueType) + { + return Activator.CreateInstance(type); + } + return null; + } + } +} diff --git a/IPA.Loader/Utilities/LoneFunctions.cs b/IPA.Loader/Utilities/LoneFunctions.cs new file mode 100644 index 00000000..c270f6e8 --- /dev/null +++ b/IPA.Loader/Utilities/LoneFunctions.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IllusionInjector.Utilities +{ + public static class LoneFunctions + { + public static byte[] StringToByteArray(string hex) + { + int NumberChars = hex.Length; + byte[] bytes = new byte[NumberChars / 2]; + for (int i = 0; i < NumberChars; i += 2) + bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); + return bytes; + } + + public static string ByteArrayToString(byte[] ba) + { + StringBuilder hex = new StringBuilder(ba.Length * 2); + foreach (byte b in ba) + hex.AppendFormat("{0:x2}", b); + return hex.ToString(); + } + + // Copyright (c) 2008-2013 Hafthor Stefansson + // Distributed under the MIT/X11 software license + // Ref: http://www.opensource.org/licenses/mit-license.php. + // From: https://stackoverflow.com/a/8808245/3117125 + public static unsafe bool UnsafeCompare(byte[] a1, byte[] a2) + { + if (a1 == a2) return true; + if (a1 == null || a2 == null || a1.Length != a2.Length) + return false; + fixed (byte* p1 = a1, p2 = a2) + { + byte* x1 = p1, x2 = p2; + int l = a1.Length; + for (int i = 0; i < l / 8; i++, x1 += 8, x2 += 8) + if (*((long*)x1) != *((long*)x2)) return false; + if ((l & 4) != 0) { if (*((int*)x1) != *((int*)x2)) return false; x1 += 4; x2 += 4; } + if ((l & 2) != 0) { if (*((short*)x1) != *((short*)x2)) return false; x1 += 2; x2 += 2; } + if ((l & 1) != 0) if (*((byte*)x1) != *((byte*)x2)) return false; + return true; + } + } + + public static string GetRelativePath(string filespec, string folder) + { + Uri pathUri = new Uri(filespec); + // Folders must end in a slash + if (!folder.EndsWith(Path.DirectorySeparatorChar.ToString())) + { + folder += Path.DirectorySeparatorChar; + } + Uri folderUri = new Uri(folder); + return Uri.UnescapeDataString(folderUri.MakeRelativeUri(pathUri).ToString().Replace('/', Path.DirectorySeparatorChar)); + } + } +} diff --git a/IPA.Loader/Utilities/SteamCheck.cs b/IPA.Loader/Utilities/SteamCheck.cs new file mode 100644 index 00000000..a4f9624b --- /dev/null +++ b/IPA.Loader/Utilities/SteamCheck.cs @@ -0,0 +1,27 @@ +using IllusionInjector.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IllusionInjector.Utilities +{ + public static class SteamCheck + { + public static Type SteamVRCamera; + public static Type SteamVRExternalCamera; + public static Type SteamVRFade; + public static bool IsAvailable => FindSteamVRAsset(); + + private static bool FindSteamVRAsset() + { + // these require assembly qualified names.... + SteamVRCamera = Type.GetType("SteamVR_Camera, Assembly-CSharp-firstpass, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", false); + SteamVRExternalCamera = Type.GetType("SteamVR_ExternalCamera, Assembly-CSharp-firstpass, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", false); + SteamVRFade = Type.GetType("SteamVR_Fade, Assembly-CSharp-firstpass, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", false); + + return SteamVRCamera != null && SteamVRExternalCamera != null && SteamVRFade != null; + } + } +} diff --git a/IPA.Loader/packages.config b/IPA.Loader/packages.config new file mode 100644 index 00000000..71c37d6d --- /dev/null +++ b/IPA.Loader/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/IPA/IPA.csproj b/IPA/IPA.csproj index f55672d3..1bb17d89 100644 --- a/IPA/IPA.csproj +++ b/IPA/IPA.csproj @@ -96,7 +96,7 @@ - + diff --git a/IPA/Patcher/Patcher.cs b/IPA/Patcher/Patcher.cs index 0a4527ce..7f8b25f5 100644 --- a/IPA/Patcher/Patcher.cs +++ b/IPA/Patcher/Patcher.cs @@ -51,8 +51,9 @@ namespace IPA.Patcher { var IIdata = new PatchData { IsPatched = false, Version = null }; foreach (var @ref in _Module.AssemblyReferences) { - if (@ref.Name == "IllusionInjector") IIdata = new PatchData { IsPatched = true, Version = new Version(0,0,0,0) }; - if (@ref.Name == "IllusionPlugin") return new PatchData { IsPatched = true, Version = @ref.Version }; + if (@ref.Name == "IllusionInjector") IIdata = new PatchData { IsPatched = true, Version = new Version(0, 0, 0, 0) }; + if (@ref.Name == "IllusionPlugin") IIdata = new PatchData { IsPatched = true, Version = new Version(0, 0, 0, 0) }; + if (@ref.Name == "IPA.Injector") return new PatchData { IsPatched = true, Version = @ref.Version }; } return IIdata; } @@ -61,9 +62,8 @@ namespace IPA.Patcher public void Patch(Version v) { // First, let's add the reference - var nameReference = new AssemblyNameReference("IllusionInjector", new Version(1,0,0,0)); - var versionNameReference = new AssemblyNameReference("IllusionPlugin", v); - var injectorPath = Path.Combine(_File.DirectoryName, "IllusionInjector.dll"); + var nameReference = new AssemblyNameReference("IPA.Injector", Program.Version); + var injectorPath = Path.Combine(_File.DirectoryName, "IPA.Injector.dll"); var injector = ModuleDefinition.ReadModule(injectorPath); for (int i = 0; i < _Module.AssemblyReferences.Count; i++) @@ -72,10 +72,11 @@ namespace IPA.Patcher _Module.AssemblyReferences.RemoveAt(i--); if (_Module.AssemblyReferences[i].Name == "IllusionPlugin") _Module.AssemblyReferences.RemoveAt(i--); + if (_Module.AssemblyReferences[i].Name == "IPA.Injector") + _Module.AssemblyReferences.RemoveAt(i--); } _Module.AssemblyReferences.Add(nameReference); - _Module.AssemblyReferences.Add(versionNameReference); int patched = 0; foreach(var type in FindEntryTypes()) @@ -100,7 +101,7 @@ namespace IPA.Patcher var targetMethod = targetType.Methods.FirstOrDefault(m => m.IsConstructor && m.IsStatic); if (targetMethod != null) { - var methodReference = _Module.Import(injector.GetType("IllusionInjector.Injector").Methods.First(m => m.Name == "Inject")); + var methodReference = _Module.Import(injector.GetType("IPA.Injector.Injector").Methods.First(m => m.Name == "Inject")); targetMethod.Body.Instructions.Insert(0, Instruction.Create(OpCodes.Call, methodReference)); return true; } diff --git a/IPA/Program.cs b/IPA/Program.cs index 257c3452..e697345d 100644 --- a/IPA/Program.cs +++ b/IPA/Program.cs @@ -19,7 +19,7 @@ namespace IPA { Unknown } - private static Version Version => Assembly.GetEntryAssembly().GetName().Version; + public static Version Version => Assembly.GetEntryAssembly().GetName().Version; public static ArgumentFlag ArgHelp = new ArgumentFlag("--help", "-h") { DocString = "prints this message" }; public static ArgumentFlag ArgWaitFor = new ArgumentFlag("--waitfor", "-w") { DocString = "waits for the specified PID to exit", ValueString = "PID" }; @@ -110,7 +110,7 @@ namespace IPA { #region Patch Version Check var patchedModule = PatchedModule.Load(context.EngineFile); - var isCurrentNewer = Version.CompareTo(patchedModule.Data.Version) > 0; + var isCurrentNewer = Version.CompareTo(patchedModule.Data.Version) >= 0; Console.WriteLine($"Current: {Version} Patched: {patchedModule.Data.Version}"); if (isCurrentNewer) { Console.ForegroundColor = ConsoleColor.White; diff --git a/IPA/Properties/AssemblyInfo.cs b/IPA/Properties/AssemblyInfo.cs index abe9ca50..4c0dbfcc 100644 --- a/IPA/Properties/AssemblyInfo.cs +++ b/IPA/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("3.8.9.*")] -[assembly: AssemblyFileVersion("3.8.9")] +[assembly: AssemblyVersion("3.9.0")] +[assembly: AssemblyFileVersion("3.9.0")] diff --git a/IllusionInjector/Updating/SelfPlugin.cs b/IllusionInjector/Updating/SelfPlugin.cs index 39863d70..b24f5642 100644 --- a/IllusionInjector/Updating/SelfPlugin.cs +++ b/IllusionInjector/Updating/SelfPlugin.cs @@ -12,7 +12,7 @@ namespace IllusionInjector.Updating internal class SelfPlugin : IBeatSaberPlugin { internal const string IPA_Name = "Beat Saber IPA"; - internal const string IPA_Version = "3.8.9"; + internal const string IPA_Version = "3.9.0"; public string Name => IPA_Name; From 1e0913eec48b56c14d30a1bee3edac5270e1467e Mon Sep 17 00:00:00 2001 From: Anairkoen Schno Date: Fri, 31 Aug 2018 20:15:04 -0500 Subject: [PATCH 02/12] Removed old version ** Need to fix more stuff, add backwards-compatability tweaker --- BSIPA.sln | 16 +- .../BeatSaber/CompositeBSPlugin.cs | 97 ----- IllusionInjector/Bootstrapper.cs | 38 -- IllusionInjector/ConsoleWindow.cs | 72 ---- IllusionInjector/IPA/CompositeIPAPlugin.cs | 75 ---- IllusionInjector/IllusionInjector.csproj | 132 ------- IllusionInjector/Injector.cs | 49 --- .../Logging/Printers/ColoredConsolePrinter.cs | 29 -- .../Logging/Printers/GZFilePrinter.cs | 92 ----- .../Logging/Printers/GlobalLogFilePrinter.cs | 30 -- .../Logging/Printers/PluginLogFilePrinter.cs | 37 -- IllusionInjector/Logging/StandardLogger.cs | 168 --------- .../Logging/UnityLogInterceptor.cs | 34 -- IllusionInjector/PluginComponent.cs | 101 ----- IllusionInjector/PluginManager.cs | 264 ------------- IllusionInjector/PostBuild.msbuild | 35 -- IllusionInjector/Properties/AssemblyInfo.cs | 40 -- .../Updating/Backup/BackupUnit.cs | 120 ------ .../Updating/ModsaberML/ApiEndpoint.cs | 123 ------- .../Updating/ModsaberML/Updater.cs | 346 ------------------ IllusionInjector/Updating/SelfPlugin.cs | 55 --- IllusionInjector/Utilities/Extensions.cs | 20 - IllusionInjector/Utilities/LoneFunctions.cs | 63 ---- IllusionInjector/Utilities/SteamCheck.cs | 27 -- ...ionInjector.csproj.CoreCompileInputs.cache | 1 - IllusionInjector/packages.config | 5 - IllusionPlugin/BeatSaber/IBeatSaberPlugin.cs | 71 ---- .../BeatSaber/IEnhancedBeatSaberPlugin.cs | 13 - IllusionPlugin/BeatSaber/ModsaberModInfo.cs | 24 -- IllusionPlugin/IGenericEnhancedPlugin.cs | 25 -- IllusionPlugin/IPA/IEnhancedPlugin.cs | 14 - IllusionPlugin/IPA/IPlugin.cs | 58 --- IllusionPlugin/IllusionPlugin.XML | 178 --------- IllusionPlugin/IllusionPlugin.csproj | 69 ---- IllusionPlugin/IniFile.cs | 100 ----- IllusionPlugin/Logging/LogPrinter.cs | 37 -- IllusionPlugin/Logging/Logger.cs | 182 --------- IllusionPlugin/ModPrefs.cs | 285 --------------- IllusionPlugin/Properties/AssemblyInfo.cs | 37 -- IllusionPlugin/Utils/ReflectionUtil.cs | 203 ---------- ...usionPlugin.csproj.CoreCompileInputs.cache | 1 - 41 files changed, 2 insertions(+), 3364 deletions(-) delete mode 100644 IllusionInjector/BeatSaber/CompositeBSPlugin.cs delete mode 100644 IllusionInjector/Bootstrapper.cs delete mode 100644 IllusionInjector/ConsoleWindow.cs delete mode 100644 IllusionInjector/IPA/CompositeIPAPlugin.cs delete mode 100644 IllusionInjector/IllusionInjector.csproj delete mode 100644 IllusionInjector/Injector.cs delete mode 100644 IllusionInjector/Logging/Printers/ColoredConsolePrinter.cs delete mode 100644 IllusionInjector/Logging/Printers/GZFilePrinter.cs delete mode 100644 IllusionInjector/Logging/Printers/GlobalLogFilePrinter.cs delete mode 100644 IllusionInjector/Logging/Printers/PluginLogFilePrinter.cs delete mode 100644 IllusionInjector/Logging/StandardLogger.cs delete mode 100644 IllusionInjector/Logging/UnityLogInterceptor.cs delete mode 100644 IllusionInjector/PluginComponent.cs delete mode 100644 IllusionInjector/PluginManager.cs delete mode 100644 IllusionInjector/PostBuild.msbuild delete mode 100644 IllusionInjector/Properties/AssemblyInfo.cs delete mode 100644 IllusionInjector/Updating/Backup/BackupUnit.cs delete mode 100644 IllusionInjector/Updating/ModsaberML/ApiEndpoint.cs delete mode 100644 IllusionInjector/Updating/ModsaberML/Updater.cs delete mode 100644 IllusionInjector/Updating/SelfPlugin.cs delete mode 100644 IllusionInjector/Utilities/Extensions.cs delete mode 100644 IllusionInjector/Utilities/LoneFunctions.cs delete mode 100644 IllusionInjector/Utilities/SteamCheck.cs delete mode 100644 IllusionInjector/obj/Debug/IllusionInjector.csproj.CoreCompileInputs.cache delete mode 100644 IllusionInjector/packages.config delete mode 100644 IllusionPlugin/BeatSaber/IBeatSaberPlugin.cs delete mode 100644 IllusionPlugin/BeatSaber/IEnhancedBeatSaberPlugin.cs delete mode 100644 IllusionPlugin/BeatSaber/ModsaberModInfo.cs delete mode 100644 IllusionPlugin/IGenericEnhancedPlugin.cs delete mode 100644 IllusionPlugin/IPA/IEnhancedPlugin.cs delete mode 100644 IllusionPlugin/IPA/IPlugin.cs delete mode 100644 IllusionPlugin/IllusionPlugin.XML delete mode 100644 IllusionPlugin/IllusionPlugin.csproj delete mode 100644 IllusionPlugin/IniFile.cs delete mode 100644 IllusionPlugin/Logging/LogPrinter.cs delete mode 100644 IllusionPlugin/Logging/Logger.cs delete mode 100644 IllusionPlugin/ModPrefs.cs delete mode 100644 IllusionPlugin/Properties/AssemblyInfo.cs delete mode 100644 IllusionPlugin/Utils/ReflectionUtil.cs delete mode 100644 IllusionPlugin/obj/Debug/IllusionPlugin.csproj.CoreCompileInputs.cache diff --git a/BSIPA.sln b/BSIPA.sln index f0444c68..34c5c3a7 100644 --- a/BSIPA.sln +++ b/BSIPA.sln @@ -5,14 +5,10 @@ VisualStudioVersion = 15.0.27428.2043 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IPA", "IPA\IPA.csproj", "{14092533-98BB-40A4-9AFC-27BB75672A70}" ProjectSection(ProjectDependencies) = postProject - {D1C61AF5-0D2D-4752-8203-1C6929025F7C} = {D1C61AF5-0D2D-4752-8203-1C6929025F7C} - {E2848BFB-5432-42F4-8AE0-D2EC0CDF2F71} = {E2848BFB-5432-42F4-8AE0-D2EC0CDF2F71} + {5AD344F0-01A0-4CA8-92E5-9D095737744D} = {5AD344F0-01A0-4CA8-92E5-9D095737744D} + {2A1AF16B-27F1-46E0-9A95-181516BC1CB7} = {2A1AF16B-27F1-46E0-9A95-181516BC1CB7} EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IllusionPlugin", "IllusionPlugin\IllusionPlugin.csproj", "{E2848BFB-5432-42F4-8AE0-D2EC0CDF2F71}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IllusionInjector", "IllusionInjector\IllusionInjector.csproj", "{D1C61AF5-0D2D-4752-8203-1C6929025F7C}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IPA.Tests", "IPA.Tests\IPA.Tests.csproj", "{C66092B0-5C1E-44E9-B524-E0E8E1425379}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MSBuildTasks", "MSBuildTasks\MSBuildTasks.csproj", "{F08C3C7A-3221-432E-BAB8-32BCE58408C8}" @@ -31,14 +27,6 @@ Global {14092533-98BB-40A4-9AFC-27BB75672A70}.Debug|Any CPU.Build.0 = Debug|Any CPU {14092533-98BB-40A4-9AFC-27BB75672A70}.Release|Any CPU.ActiveCfg = Release|Any CPU {14092533-98BB-40A4-9AFC-27BB75672A70}.Release|Any CPU.Build.0 = Release|Any CPU - {E2848BFB-5432-42F4-8AE0-D2EC0CDF2F71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E2848BFB-5432-42F4-8AE0-D2EC0CDF2F71}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E2848BFB-5432-42F4-8AE0-D2EC0CDF2F71}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E2848BFB-5432-42F4-8AE0-D2EC0CDF2F71}.Release|Any CPU.Build.0 = Release|Any CPU - {D1C61AF5-0D2D-4752-8203-1C6929025F7C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D1C61AF5-0D2D-4752-8203-1C6929025F7C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D1C61AF5-0D2D-4752-8203-1C6929025F7C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D1C61AF5-0D2D-4752-8203-1C6929025F7C}.Release|Any CPU.Build.0 = Release|Any CPU {C66092B0-5C1E-44E9-B524-E0E8E1425379}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C66092B0-5C1E-44E9-B524-E0E8E1425379}.Debug|Any CPU.Build.0 = Debug|Any CPU {C66092B0-5C1E-44E9-B524-E0E8E1425379}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/IllusionInjector/BeatSaber/CompositeBSPlugin.cs b/IllusionInjector/BeatSaber/CompositeBSPlugin.cs deleted file mode 100644 index 96d6ddf0..00000000 --- a/IllusionInjector/BeatSaber/CompositeBSPlugin.cs +++ /dev/null @@ -1,97 +0,0 @@ -using IllusionPlugin; -using IllusionPlugin.BeatSaber; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using UnityEngine; -using UnityEngine.SceneManagement; -using Logger = IllusionInjector.Logging.Logger; - -namespace IllusionInjector { - public class CompositeBSPlugin : IBeatSaberPlugin - { - IEnumerable plugins; - - private delegate void CompositeCall(IBeatSaberPlugin plugin); - - public CompositeBSPlugin(IEnumerable plugins) { - this.plugins = plugins; - } - - public void OnApplicationStart() { - Invoke(plugin => plugin.OnApplicationStart()); - } - - public void OnApplicationQuit() { - Invoke(plugin => plugin.OnApplicationQuit()); - } - - public void OnSceneLoaded(Scene scene, LoadSceneMode sceneMode) { - foreach (var plugin in plugins) { - try { - plugin.OnSceneLoaded(scene, sceneMode); - } - catch (Exception ex) { - Logger.log.Error($"{plugin.Name}: {ex}"); - } - } - } - - public void OnSceneUnloaded(Scene scene) { - foreach (var plugin in plugins) { - try { - plugin.OnSceneUnloaded(scene); - } - catch (Exception ex) { - Logger.log.Error($"{plugin.Name}: {ex}"); - } - } - } - - public void OnActiveSceneChanged(Scene prevScene, Scene nextScene) { - foreach (var plugin in plugins) { - try { - plugin.OnActiveSceneChanged(prevScene, nextScene); - } - catch (Exception ex) { - Logger.log.Error($"{plugin.Name}: {ex}"); - } - } - } - - private void Invoke(CompositeCall callback) { - foreach (var plugin in plugins) { - try { - callback(plugin); - } - catch (Exception ex) { - Logger.log.Error($"{plugin.Name}: {ex}"); - } - } - } - - public void OnUpdate() { - Invoke(plugin => plugin.OnUpdate()); - } - - public void OnFixedUpdate() { - Invoke(plugin => plugin.OnFixedUpdate()); - } - - public string Name => throw new NotImplementedException(); - - public string Version => throw new NotImplementedException(); - - public Uri UpdateUri => throw new NotImplementedException(); - - public ModsaberModInfo ModInfo => throw new NotImplementedException(); - - public void OnLateUpdate() { - Invoke(plugin => { - if (plugin is IEnhancedBeatSaberPlugin) - ((IEnhancedBeatSaberPlugin) plugin).OnLateUpdate(); - }); - } - } -} \ No newline at end of file diff --git a/IllusionInjector/Bootstrapper.cs b/IllusionInjector/Bootstrapper.cs deleted file mode 100644 index f7f8db54..00000000 --- a/IllusionInjector/Bootstrapper.cs +++ /dev/null @@ -1,38 +0,0 @@ -using IllusionInjector.Logging; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using UnityEngine; - -namespace IllusionInjector -{ - class Bootstrapper : MonoBehaviour - { - public event Action Destroyed = delegate {}; - - void Awake() - { - //if (Environment.CommandLine.Contains("--verbose")) - //{ - Windows.GuiConsole.CreateConsole(); - //} - - Application.logMessageReceived += delegate (string condition, string stackTrace, LogType type) - { - var level = UnityLogInterceptor.LogTypeToLevel(type); - UnityLogInterceptor.Unitylogger.Log(level, $"{condition.Trim()}"); - UnityLogInterceptor.Unitylogger.Log(level, $"{stackTrace.Trim()}"); - }; - } - - void Start() - { - Destroy(gameObject); - } - void OnDestroy() - { - Destroyed(); - } - } -} diff --git a/IllusionInjector/ConsoleWindow.cs b/IllusionInjector/ConsoleWindow.cs deleted file mode 100644 index 6b2a1b45..00000000 --- a/IllusionInjector/ConsoleWindow.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using System.Collections; -using System.Runtime.InteropServices; -using System.IO; -using System.Text; -using Microsoft.Win32.SafeHandles; - -namespace Windows -{ - - class GuiConsole - { - public static void CreateConsole() - { - if (hasConsole) - return; - if (oldOut == IntPtr.Zero) - oldOut = GetStdHandle( -11 ); - if (! AllocConsole()) - throw new Exception("AllocConsole() failed"); - conOut = CreateFile( "CONOUT$", 0x40000000, 2, IntPtr.Zero, 3, 0, IntPtr.Zero ); - if (! SetStdHandle(-11, conOut)) - throw new Exception("SetStdHandle() failed"); - StreamToConsole(); - hasConsole = true; - } - public static void ReleaseConsole() - { - if (! hasConsole) - return; - if (! CloseHandle(conOut)) - throw new Exception("CloseHandle() failed"); - conOut = IntPtr.Zero; - if (! FreeConsole()) - throw new Exception("FreeConsole() failed"); - if (! SetStdHandle(-11, oldOut)) - throw new Exception("SetStdHandle() failed"); - StreamToConsole(); - hasConsole = false; - } - private static void StreamToConsole() - { - Stream cstm = Console.OpenStandardOutput(); - StreamWriter cstw = new StreamWriter( cstm, Encoding.Default ); - cstw.AutoFlush = true; - Console.SetOut( cstw ); - Console.SetError( cstw ); - } - private static bool hasConsole = false; - private static IntPtr conOut; - private static IntPtr oldOut; - [DllImport("kernel32.dll", SetLastError=true)] - private static extern bool AllocConsole(); - [DllImport("kernel32.dll", SetLastError=false)] - private static extern bool FreeConsole(); - [DllImport("kernel32.dll", SetLastError=true)] - private static extern IntPtr GetStdHandle( int nStdHandle ); - [DllImport("kernel32.dll", SetLastError=true)] - private static extern bool SetStdHandle(int nStdHandle, IntPtr hConsoleOutput); - [DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)] - private static extern IntPtr CreateFile( - string fileName, - int desiredAccess, - int shareMode, - IntPtr securityAttributes, - int creationDisposition, - int flagsAndAttributes, - IntPtr templateFile ); - [DllImport("kernel32.dll", ExactSpelling=true, SetLastError=true)] - private static extern bool CloseHandle(IntPtr handle); - } -} \ No newline at end of file diff --git a/IllusionInjector/IPA/CompositeIPAPlugin.cs b/IllusionInjector/IPA/CompositeIPAPlugin.cs deleted file mode 100644 index 001677a6..00000000 --- a/IllusionInjector/IPA/CompositeIPAPlugin.cs +++ /dev/null @@ -1,75 +0,0 @@ -using IllusionPlugin; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using UnityEngine; -using UnityEngine.SceneManagement; -using Logger = IllusionInjector.Logging.Logger; - -namespace IllusionInjector { -#pragma warning disable CS0618 // Type or member is obsolete - public class CompositeIPAPlugin : IPlugin - { - IEnumerable plugins; - - private delegate void CompositeCall(IPlugin plugin); - - public CompositeIPAPlugin(IEnumerable plugins) { - this.plugins = plugins; - } - - public void OnApplicationStart() { - Invoke(plugin => plugin.OnApplicationStart()); - } - - public void OnApplicationQuit() { - Invoke(plugin => plugin.OnApplicationQuit()); - } - - private void Invoke(CompositeCall callback) { - foreach (var plugin in plugins) { - try { - callback(plugin); - } - catch (Exception ex) { - Logger.log.Error($"{plugin.Name}: {ex}"); - } - } - } - - public void OnUpdate() { - Invoke(plugin => plugin.OnUpdate()); - } - - public void OnFixedUpdate() { - Invoke(plugin => plugin.OnFixedUpdate()); - } - - public string Name { - get { throw new NotImplementedException(); } - } - - public string Version { - get { throw new NotImplementedException(); } - } - - public void OnLateUpdate() { - Invoke(plugin => { - if (plugin is IEnhancedBeatSaberPlugin) - ((IEnhancedBeatSaberPlugin) plugin).OnLateUpdate(); - }); - } - - public void OnLevelWasLoaded(int level) - { - Invoke(plugin => plugin.OnLevelWasLoaded(level)); - } - - public void OnLevelWasInitialized(int level) - { - Invoke(plugin => plugin.OnLevelWasInitialized(level)); - } - } -#pragma warning restore CS0618 // Type or member is obsolete -} \ No newline at end of file diff --git a/IllusionInjector/IllusionInjector.csproj b/IllusionInjector/IllusionInjector.csproj deleted file mode 100644 index 1b4be7e6..00000000 --- a/IllusionInjector/IllusionInjector.csproj +++ /dev/null @@ -1,132 +0,0 @@ - - - - - Debug - AnyCPU - {D1C61AF5-0D2D-4752-8203-1C6929025F7C} - Library - Properties - IllusionInjector - IllusionInjector - v4.6 - 512 - - - - - - - true - full - false - bin\Debug\ - TRACE;DEBUG - prompt - 4 - false - true - - - none - true - bin\Release\ - TRACE - prompt - 4 - false - true - true - - - - ..\Libs\I18N.dll - - - ..\Libs\I18N.West.dll - - - ..\packages\Ionic.Zip.1.9.1.8\lib\Ionic.Zip.dll - - - ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll - - - - - - ..\Libs\UnityEngine.dll - False - - - ..\Libs\UnityEngine.CoreModule.dll - False - - - ..\..\..\..\..\..\Game Library\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\UnityEngine.UnityWebRequestModule.dll - False - - - - - - - - - - - - - - - - - - - - - - - - - - - - {e2848bfb-5432-42f4-8ae0-d2ec0cdf2f71} - IllusionPlugin - - - {f08c3c7a-3221-432e-bab8-32bce58408c8} - MSBuildTasks - False - - - - - - - - IncludedLibs\0Harmony.dll - Always - - - RequiredMonoLibs\System.Runtime.Serialization.dll - Always - - - - - - - - - - - - \ No newline at end of file diff --git a/IllusionInjector/Injector.cs b/IllusionInjector/Injector.cs deleted file mode 100644 index d70a6ec6..00000000 --- a/IllusionInjector/Injector.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.IO; -using System.Reflection; -using UnityEngine; - -namespace IllusionInjector -{ - public static class Injector - { - private static bool injected = false; - public static void Inject() - { - if (!injected) - { - injected = true; - AppDomain.CurrentDomain.AssemblyResolve += AssemblyLibLoader; - var bootstrapper = new GameObject("Bootstrapper").AddComponent(); - bootstrapper.Destroyed += Bootstrapper_Destroyed; - } - } - - private static string libsDir; - private static Assembly AssemblyLibLoader(object source, ResolveEventArgs e) - { - if (libsDir == null) - libsDir = Path.Combine(Environment.CurrentDirectory, "Libs"); - - var asmName = new AssemblyName(e.Name); - //Logger.log.Debug($"Resolving library {asmName}"); - - var testFilen = Path.Combine(libsDir, $"{asmName.Name}.{asmName.Version}.dll"); - //Logger.log.Debug($"Looking for file {testFilen}"); - - if (File.Exists(testFilen)) - { - return Assembly.LoadFile(testFilen); - } - - //Logger.log.Error($"Could not load library {asmName}"); - - return null; - } - - private static void Bootstrapper_Destroyed() - { - PluginComponent.Create(); - } - } -} diff --git a/IllusionInjector/Logging/Printers/ColoredConsolePrinter.cs b/IllusionInjector/Logging/Printers/ColoredConsolePrinter.cs deleted file mode 100644 index a4759b28..00000000 --- a/IllusionInjector/Logging/Printers/ColoredConsolePrinter.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using IllusionPlugin.Logging; -using LoggerBase = IllusionPlugin.Logging.Logger; - -namespace IllusionInjector.Logging.Printers -{ - public class ColoredConsolePrinter : LogPrinter - { - LoggerBase.LogLevel filter = LoggerBase.LogLevel.All; - public override LoggerBase.LogLevel Filter { get => filter; set => filter = value; } - - ConsoleColor color = Console.ForegroundColor; - public ConsoleColor Color { get => color; set => color = value; } - - public override void Print(LoggerBase.Level level, DateTime time, string logName, string message) - { - if (((byte)level & (byte)StandardLogger.PrintFilter) == 0) return; - Console.ForegroundColor = color; - foreach (var line in message.Split(new string[] { "\n", Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)) - Console.WriteLine(string.Format(LoggerBase.LogFormat, line, logName, time, level.ToString().ToUpper())); - Console.ResetColor(); - } - } -} diff --git a/IllusionInjector/Logging/Printers/GZFilePrinter.cs b/IllusionInjector/Logging/Printers/GZFilePrinter.cs deleted file mode 100644 index 7a3fac36..00000000 --- a/IllusionInjector/Logging/Printers/GZFilePrinter.cs +++ /dev/null @@ -1,92 +0,0 @@ -using IllusionPlugin.Logging; -using Ionic.Zlib; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; - -namespace IllusionInjector.Logging.Printers -{ - public abstract class GZFilePrinter : LogPrinter - { - [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] - static extern bool CreateHardLink( - string lpFileName, - string lpExistingFileName, - IntPtr lpSecurityAttributes - ); - - [DllImport("Kernel32.dll")] - static extern Int32 GetLastError(); - - private FileInfo fileInfo; - protected StreamWriter fileWriter; - private GZipStream zstream; - private FileStream fstream; - - protected abstract FileInfo GetFileInfo(); - - private void InitLog() - { - try - { - if (fileInfo == null) - { // first init - fileInfo = GetFileInfo(); - var ext = fileInfo.Extension; - fileInfo = new FileInfo(fileInfo.FullName + ".gz"); - fileInfo.Create().Close(); - - var symlink = new FileInfo(Path.Combine(fileInfo.DirectoryName, $"latest{ext}.gz")); - if (symlink.Exists) symlink.Delete(); - - try - { - if (!CreateHardLink(symlink.FullName, fileInfo.FullName, IntPtr.Zero)) - { - Logger.log.Error($"Hardlink creation failed {GetLastError()}"); - } - } - catch (Exception e) - { - Logger.log.Error("Error creating latest hardlink!"); - Logger.log.Error(e); - } - } - } - catch (Exception e) - { - Logger.log.Error("Error initializing log!"); - Logger.log.Error(e); - } - } - - public override sealed void StartPrint() - { - InitLog(); - - fstream = fileInfo.Open(FileMode.Append, FileAccess.Write); - zstream = new GZipStream(fstream, CompressionMode.Compress) - { - FlushMode = FlushType.Full - }; - fileWriter = new StreamWriter(zstream, new UTF8Encoding(false)); - } - - public override sealed void EndPrint() - { - fileWriter.Flush(); - zstream.Flush(); - fstream.Flush(); - fileWriter.Close(); - zstream.Close(); - fstream.Close(); - fileWriter.Dispose(); - zstream.Dispose(); - fstream.Dispose(); - } - } -} diff --git a/IllusionInjector/Logging/Printers/GlobalLogFilePrinter.cs b/IllusionInjector/Logging/Printers/GlobalLogFilePrinter.cs deleted file mode 100644 index 9f617b72..00000000 --- a/IllusionInjector/Logging/Printers/GlobalLogFilePrinter.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using IllusionPlugin.Logging; -using LoggerBase = IllusionPlugin.Logging.Logger; - -namespace IllusionInjector.Logging.Printers -{ - class GlobalLogFilePrinter : GZFilePrinter - { - public override LoggerBase.LogLevel Filter { get; set; } = LoggerBase.LogLevel.All; - - public override void Print(IllusionPlugin.Logging.Logger.Level level, DateTime time, string logName, string message) - { - foreach (var line in message.Split(new string[] { "\n", Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)) - fileWriter.WriteLine(string.Format(LoggerBase.LogFormat, line, logName, time, level.ToString().ToUpper())); - } - - protected override FileInfo GetFileInfo() - { - var logsDir = new DirectoryInfo("Logs"); - logsDir.Create(); - var finfo = new FileInfo(Path.Combine(logsDir.FullName, $"{DateTime.Now:yyyy.MM.dd.HH.mm}.log")); - return finfo; - } - } -} diff --git a/IllusionInjector/Logging/Printers/PluginLogFilePrinter.cs b/IllusionInjector/Logging/Printers/PluginLogFilePrinter.cs deleted file mode 100644 index cd0c41e4..00000000 --- a/IllusionInjector/Logging/Printers/PluginLogFilePrinter.cs +++ /dev/null @@ -1,37 +0,0 @@ -using IllusionPlugin.Logging; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using LoggerBase = IllusionPlugin.Logging.Logger; - -namespace IllusionInjector.Logging.Printers -{ - class PluginLogFilePrinter : GZFilePrinter - { - public override LoggerBase.LogLevel Filter { get; set; } = LoggerBase.LogLevel.All; - - private string name; - - protected override FileInfo GetFileInfo() - { - var logsDir = new DirectoryInfo(Path.Combine("Logs",name)); - logsDir.Create(); - var finfo = new FileInfo(Path.Combine(logsDir.FullName, $"{DateTime.Now:yyyy.MM.dd.HH.mm}.log")); - return finfo; - } - - public PluginLogFilePrinter(string name) - { - this.name = name; - } - - public override void Print(IllusionPlugin.Logging.Logger.Level level, DateTime time, string logName, string message) - { - foreach (var line in message.Split(new string[] { "\n", Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)) - fileWriter.WriteLine(string.Format("[{3} @ {2:HH:mm:ss}] {0}", line, logName, time, level.ToString().ToUpper())); - } - } -} diff --git a/IllusionInjector/Logging/StandardLogger.cs b/IllusionInjector/Logging/StandardLogger.cs deleted file mode 100644 index 4b4a329a..00000000 --- a/IllusionInjector/Logging/StandardLogger.cs +++ /dev/null @@ -1,168 +0,0 @@ -using IllusionInjector.Logging.Printers; -using IllusionPlugin; -using IllusionPlugin.Logging; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using LoggerBase = IllusionPlugin.Logging.Logger; - -namespace IllusionInjector.Logging -{ - internal static class Logger - { - private static LoggerBase _log; - internal static LoggerBase log - { - get - { - if (_log == null) - _log = new StandardLogger("IPA"); - return _log; - } - } - } - - public class StandardLogger : LoggerBase - { - private static readonly IReadOnlyList defaultPrinters = new List() - { - new ColoredConsolePrinter() - { - Filter = LogLevel.DebugOnly, - Color = ConsoleColor.Green, - }, - new ColoredConsolePrinter() - { - Filter = LogLevel.InfoOnly, - Color = ConsoleColor.White, - }, - new ColoredConsolePrinter() - { - Filter = LogLevel.WarningOnly, - Color = ConsoleColor.Yellow, - }, - new ColoredConsolePrinter() - { - Filter = LogLevel.ErrorOnly, - Color = ConsoleColor.Red, - }, - new ColoredConsolePrinter() - { - Filter = LogLevel.CriticalOnly, - Color = ConsoleColor.Magenta, - }, - new GlobalLogFilePrinter() - }; - - private string logName; - private static LogLevel showFilter = LogLevel.InfoUp; - private static bool showSourceClass = true; - public static LogLevel PrintFilter { get => showFilter; set => showFilter = value; } - private List printers = new List(defaultPrinters); - - static StandardLogger() - { - if (ModPrefs.GetBool("IPA", "PrintDebug", false, true)) - showFilter = LogLevel.All; - showSourceClass = ModPrefs.GetBool("IPA", "DebugShowCallSource", false, true); - } - - internal StandardLogger(string name) - { - logName = name; - - printers.Add(new PluginLogFilePrinter(name)); - - if (_logThread == null || !_logThread.IsAlive) - { - _logThread = new Thread(LogThread); - _logThread.Start(); - } - } - - public override void Log(Level level, string message) - { - _logQueue.Add(new LogMessage - { - level = level, - message = message, - logger = this, - time = DateTime.Now - }); - } - - public override void Debug(string message) - { // add source to message - var stfm = new StackTrace().GetFrame(1).GetMethod(); - if (showSourceClass) - base.Debug($"{{{stfm.DeclaringType.FullName}::{stfm.Name}}} {message}"); - else - base.Debug(message); - } - - internal struct LogMessage - { - public Level level; - public StandardLogger logger; - public string message; - public DateTime time; - } - - private static BlockingCollection _logQueue = new BlockingCollection(); - private static Thread _logThread; - - private static void LogThread() - { - HashSet started = new HashSet(); - while (_logQueue.TryTake(out LogMessage msg, Timeout.Infinite)) { - foreach (var printer in msg.logger.printers) - { - try - { - if (((byte)msg.level & (byte)printer.Filter) != 0) - { - if (!started.Contains(printer)) - { - printer.StartPrint(); - started.Add(printer); - } - - printer.Print(msg.level, msg.time, msg.logger.logName, msg.message); - } - } - catch (Exception e) - { - Console.WriteLine($"printer errored {e}"); - } - } - - if (_logQueue.Count == 0) - { - foreach (var printer in started) - { - try - { - printer.EndPrint(); - } - catch (Exception e) - { - Console.WriteLine($"printer errored {e}"); - } - } - started.Clear(); - } - } - } - - public static void StopLogThread() - { - _logQueue.CompleteAdding(); - _logThread.Join(); - } - } -} diff --git a/IllusionInjector/Logging/UnityLogInterceptor.cs b/IllusionInjector/Logging/UnityLogInterceptor.cs deleted file mode 100644 index 64e42926..00000000 --- a/IllusionInjector/Logging/UnityLogInterceptor.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using UnityEngine; -using LoggerBase = IllusionPlugin.Logging.Logger; - -namespace IllusionInjector.Logging -{ - public class UnityLogInterceptor - { - public static LoggerBase Unitylogger = new StandardLogger("UnityEngine"); - - public static LoggerBase.Level LogTypeToLevel(LogType type) - { - switch (type) - { - case LogType.Assert: - return LoggerBase.Level.Debug; - case LogType.Error: - return LoggerBase.Level.Error; - case LogType.Exception: - return LoggerBase.Level.Critical; - case LogType.Log: - return LoggerBase.Level.Info; - case LogType.Warning: - return LoggerBase.Level.Warning; - default: - return LoggerBase.Level.Info; - } - } - } -} diff --git a/IllusionInjector/PluginComponent.cs b/IllusionInjector/PluginComponent.cs deleted file mode 100644 index 2c8140fa..00000000 --- a/IllusionInjector/PluginComponent.cs +++ /dev/null @@ -1,101 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using UnityEngine; -using UnityEngine.SceneManagement; - -namespace IllusionInjector -{ - public class PluginComponent : MonoBehaviour - { - private CompositeBSPlugin bsPlugins; - private CompositeIPAPlugin ipaPlugins; - private bool quitting = false; - - public static PluginComponent Create() - { - return new GameObject("IPA_PluginManager").AddComponent(); - } - - void Awake() - { - DontDestroyOnLoad(gameObject); - - bsPlugins = new CompositeBSPlugin(PluginManager.BSPlugins); - ipaPlugins = new CompositeIPAPlugin(PluginManager.Plugins); - - // this has no relevance since there is a new mod updater system - //gameObject.AddComponent(); // AFTER plugins are loaded, but before most things - gameObject.AddComponent(); - - bsPlugins.OnApplicationStart(); - ipaPlugins.OnApplicationStart(); - - SceneManager.activeSceneChanged += OnActiveSceneChanged; - SceneManager.sceneLoaded += OnSceneLoaded; - SceneManager.sceneUnloaded += OnSceneUnloaded; - } - - void Update() - { - bsPlugins.OnUpdate(); - ipaPlugins.OnUpdate(); - } - - void LateUpdate() - { - bsPlugins.OnLateUpdate(); - ipaPlugins.OnLateUpdate(); - } - - void FixedUpdate() - { - bsPlugins.OnFixedUpdate(); - ipaPlugins.OnFixedUpdate(); - } - - void OnDestroy() - { - if (!quitting) - { - Create(); - } - } - - void OnApplicationQuit() - { - SceneManager.activeSceneChanged -= OnActiveSceneChanged; - SceneManager.sceneLoaded -= OnSceneLoaded; - SceneManager.sceneUnloaded -= OnSceneUnloaded; - - bsPlugins.OnApplicationQuit(); - ipaPlugins.OnApplicationQuit(); - - quitting = true; - } - - void OnLevelWasLoaded(int level) - { - ipaPlugins.OnLevelWasLoaded(level); - } - - public void OnLevelWasInitialized(int level) - { - ipaPlugins.OnLevelWasInitialized(level); - } - - void OnSceneLoaded(Scene scene, LoadSceneMode sceneMode) - { - bsPlugins.OnSceneLoaded(scene, sceneMode); - } - - private void OnSceneUnloaded(Scene scene) { - bsPlugins.OnSceneUnloaded(scene); - } - - private void OnActiveSceneChanged(Scene prevScene, Scene nextScene) { - bsPlugins.OnActiveSceneChanged(prevScene, nextScene); - } - - } -} diff --git a/IllusionInjector/PluginManager.cs b/IllusionInjector/PluginManager.cs deleted file mode 100644 index 14c67222..00000000 --- a/IllusionInjector/PluginManager.cs +++ /dev/null @@ -1,264 +0,0 @@ -using IllusionInjector.Logging; -using IllusionInjector.Updating; -using IllusionInjector.Utilities; -using IllusionPlugin; -using IllusionPlugin.BeatSaber; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; -using LoggerBase = IllusionPlugin.Logging.Logger; - -namespace IllusionInjector -{ - public static class PluginManager - { -#pragma warning disable CS0618 // Type or member is obsolete (IPlugin) - - public class BSPluginMeta - { - public IBeatSaberPlugin Plugin { get; internal set; } - public string Filename { get; internal set; } - public ModsaberModInfo ModsaberInfo { get; internal set; } - } - - public static IEnumerable BSPlugins - { - get - { - if(_bsPlugins == null) - { - LoadPlugins(); - } - return _bsPlugins.Select(p => p.Plugin); - } - } - private static List _bsPlugins = null; - internal static IEnumerable BSMetas - { - get - { - if (_bsPlugins == null) - { - LoadPlugins(); - } - return _bsPlugins; - } - } - - public static IEnumerable Plugins - { - get - { - if (_ipaPlugins == null) - { - LoadPlugins(); - } - return _ipaPlugins; - } - } - private static List _ipaPlugins = null; - - - - private static void LoadPlugins() - { - string pluginDirectory = Path.Combine(Environment.CurrentDirectory, "Plugins"); - - // 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); - Logger.log.Info(exeName); - _bsPlugins = new List(); - _ipaPlugins = new List(); - - 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); - } - } - - //Copy plugins to .cache - string[] originalPlugins = Directory.GetFiles(pluginDirectory, "*.dll"); - foreach (string s in originalPlugins) - { - string pluginCopy = Path.Combine(cacheDir, Path.GetFileName(s)); - File.Copy(Path.Combine(pluginDirectory, s), pluginCopy); - } - - var selfPlugin = new BSPluginMeta - { - Filename = Path.Combine(Environment.CurrentDirectory, "IPA.exe"), - Plugin = new SelfPlugin() - }; - selfPlugin.ModsaberInfo = selfPlugin.Plugin.ModInfo; - - _bsPlugins.Add(selfPlugin); - - //Load copied plugins - string[] copiedPlugins = Directory.GetFiles(cacheDir, "*.dll"); - foreach (string s in copiedPlugins) - { - var result = LoadPluginsFromFile(s, exeName); - _bsPlugins.AddRange(result.Item1); - _ipaPlugins.AddRange(result.Item2); - } - - - // DEBUG - Logger.log.Info($"Running on Unity {UnityEngine.Application.unityVersion}"); - Logger.log.Info($"Game version {UnityEngine.Application.version}"); - Logger.log.Info("-----------------------------"); - Logger.log.Info($"Loading plugins from {LoneFunctions.GetRelativePath(pluginDirectory, Environment.CurrentDirectory)} and found {_bsPlugins.Count + _ipaPlugins.Count}"); - Logger.log.Info("-----------------------------"); - foreach (var plugin in _bsPlugins) - { - Logger.log.Info($"{plugin.Plugin.Name}: {plugin.Plugin.Version}"); - } - Logger.log.Info("-----------------------------"); - foreach (var plugin in _ipaPlugins) - { - Logger.log.Info($"{plugin.Name}: {plugin.Version}"); - } - Logger.log.Info("-----------------------------"); - } - - private static Tuple, IEnumerable> LoadPluginsFromFile(string file, string exeName) - { - List bsPlugins = new List(); - List ipaPlugins = new List(); - - if (!File.Exists(file) || !file.EndsWith(".dll", true, null)) - return new Tuple, IEnumerable>(bsPlugins, ipaPlugins); - - T OptionalGetPlugin(Type t) where T : class - { - // use typeof() to allow for easier renaming (in an ideal world this compiles to a string, but ¯\_(ツ)_/¯) - if (t.GetInterface(typeof(T).Name) != null) - { - try - { - T pluginInstance = Activator.CreateInstance(t) as T; - string[] filter = null; - - if (pluginInstance is IGenericEnhancedPlugin) - { - filter = ((IGenericEnhancedPlugin)pluginInstance).Filter; - } - - if (filter == null || filter.Contains(exeName, StringComparer.OrdinalIgnoreCase)) - return pluginInstance; - } - catch (Exception e) - { - Logger.log.Error($"Could not load plugin {t.FullName} in {Path.GetFileName(file)}! {e}"); - } - } - - return null; - } - - try - { - Assembly assembly = Assembly.LoadFrom(file); - - foreach (Type t in assembly.GetTypes()) - { - IBeatSaberPlugin bsPlugin = OptionalGetPlugin(t); - if (bsPlugin != null) - { - try - { - var init = t.GetMethod("Init", BindingFlags.Instance | BindingFlags.Public); - if (init != null) - { - var initArgs = new List(); - var initParams = init.GetParameters(); - - LoggerBase modLogger = null; - IModPrefs modPrefs = null; - - foreach (var param in initParams) - { - var ptype = param.ParameterType; - if (ptype.IsAssignableFrom(typeof(LoggerBase))) { - if (modLogger == null) modLogger = new StandardLogger(bsPlugin.Name); - initArgs.Add(modLogger); - } - else if (ptype.IsAssignableFrom(typeof(IModPrefs))) - { - if (modPrefs == null) modPrefs = new ModPrefs(bsPlugin); - initArgs.Add(modPrefs); - } - else - initArgs.Add(ptype.GetDefault()); - } - - init.Invoke(bsPlugin, initArgs.ToArray()); - } - - bsPlugins.Add(new BSPluginMeta - { - Plugin = bsPlugin, - Filename = file.Replace("\\.cache", ""), // quick and dirty fix - ModsaberInfo = bsPlugin.ModInfo - }); - } - catch (AmbiguousMatchException) - { - Logger.log.Error($"Only one Init allowed per plugin"); - } - } - else - { - IPlugin ipaPlugin = OptionalGetPlugin(t); - if (ipaPlugin != null) - { - ipaPlugins.Add(ipaPlugin); - } - } - } - - } - catch (Exception e) - { - Logger.log.Error($"Could not load {Path.GetFileName(file)}! {e}"); - } - - return new Tuple, IEnumerable>(bsPlugins, ipaPlugins); - } - - public class AppInfo - { - [DllImport("kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = false)] - private static extern int GetModuleFileName(HandleRef hModule, StringBuilder buffer, int length); - private static HandleRef NullHandleRef = new HandleRef(null, IntPtr.Zero); - public static string StartupPath - { - get - { - StringBuilder stringBuilder = new StringBuilder(260); - GetModuleFileName(NullHandleRef, stringBuilder, stringBuilder.Capacity); - return stringBuilder.ToString(); - } - } - } -#pragma warning restore CS0618 // Type or member is obsolete (IPlugin) - } -} diff --git a/IllusionInjector/PostBuild.msbuild b/IllusionInjector/PostBuild.msbuild deleted file mode 100644 index 72841a21..00000000 --- a/IllusionInjector/PostBuild.msbuild +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/IllusionInjector/Properties/AssemblyInfo.cs b/IllusionInjector/Properties/AssemblyInfo.cs deleted file mode 100644 index e967a81a..00000000 --- a/IllusionInjector/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System.Resources; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("IllusionInjector")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("IllusionInjector")] -[assembly: AssemblyCopyright("Copyright © 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("400a540a-d21f-4609-966b-206059b6e73b")] -[assembly: InternalsVisibleTo("IllusionPlugin")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] -[assembly: NeutralResourcesLanguage("en")] - diff --git a/IllusionInjector/Updating/Backup/BackupUnit.cs b/IllusionInjector/Updating/Backup/BackupUnit.cs deleted file mode 100644 index 3aad5ab0..00000000 --- a/IllusionInjector/Updating/Backup/BackupUnit.cs +++ /dev/null @@ -1,120 +0,0 @@ -using IllusionInjector.Utilities; -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.IO; -using System.Linq; -using System.Text; - -namespace IllusionInjector.Updating.Backup -{ - /// - /// A unit for backup. WIP. - /// - internal class BackupUnit - { - public string Name { get; private set; } - - private DirectoryInfo _BackupPath; - private List _Files = new List(); - - public BackupUnit(string backupPath) : this(backupPath, DateTime.Now.ToString("yyyy-MM-dd_h-mm-ss")) - { - } - - public BackupUnit(string backupPath, string name) - { - Name = name; - _BackupPath = new DirectoryInfo(Path.Combine(backupPath, Name)); - _BackupPath.Create(); - } - - public static BackupUnit FromDirectory(DirectoryInfo directory, string backupPath) - { - var unit = new BackupUnit(backupPath, directory.Name); - - // Parse directory - foreach(var file in directory.GetFiles("*", SearchOption.AllDirectories)) { - var relativePath = file.FullName.Substring(directory.FullName.Length + 1); - unit._Files.Add(relativePath); - } - - return unit; - } - - public void Add(string file) - { - Add(new FileInfo(file)); - } - - internal void Delete() - { - _BackupPath.Delete(true); - } - - /// - /// Adds a file to the list of changed files and backups it. - /// - /// - public void Add(FileInfo file) - { - var relativePath = LoneFunctions.GetRelativePath(Environment.CurrentDirectory, file.FullName); - var backupPath = new FileInfo(Path.Combine(_BackupPath.FullName, relativePath)); - - if(_Files.Contains(relativePath)) - { - Console.WriteLine("Skipping backup of {0}", relativePath); - return; - } - - - // Copy over - backupPath.Directory.Create(); - if (file.Exists) - { - file.CopyTo(backupPath.FullName, true); - } else - { - // Make empty file - backupPath.Create().Close(); - } - - // Add to list - _Files.Add(relativePath); - } - - /// - /// Reverts the changes made in this unit. - /// - public void Restore() - { - foreach(var relativePath in _Files) - { - //Console.WriteLine("Restoring {0}", relativePath); - // Original version - var backupFile = new FileInfo(Path.Combine(_BackupPath.FullName, relativePath)); - var target = new FileInfo(Path.Combine(Environment.CurrentDirectory, relativePath)); - - if (backupFile.Exists) - { - if (backupFile.Length > 0) - { - //Console.WriteLine(" {0} => {1}", backupFile.FullName, target.FullName); - target.Directory.Create(); - backupFile.CopyTo(target.FullName, true); - } else - { - //Console.WriteLine(" x {0}", target.FullName); - if(target.Exists) - { - target.Delete(); - } - } - } else { - //Console.Error.WriteLine("Backup not found!"); - } - } - } - - } -} diff --git a/IllusionInjector/Updating/ModsaberML/ApiEndpoint.cs b/IllusionInjector/Updating/ModsaberML/ApiEndpoint.cs deleted file mode 100644 index 6465f381..00000000 --- a/IllusionInjector/Updating/ModsaberML/ApiEndpoint.cs +++ /dev/null @@ -1,123 +0,0 @@ -using IllusionInjector.Logging; -using IllusionInjector.Utilities; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace IllusionInjector.Updating.ModsaberML -{ - class ApiEndpoint - { -#if DEBUG && UPDATETEST - public const string ApiBase = "file://Z:/Users/aaron/Source/Repos/IPA-Reloaded-BeatSaber/IPA.Tests/"; - public const string GetApprovedEndpoint = "updater_test.json"; -#else - public const string ApiBase = "https://www.modsaber.ml/"; - public const string GetApprovedEndpoint = "registry/{0}"; -#endif - - class HexArrayConverter : JsonConverter - { - public override bool CanConvert(Type objectType) - { - return objectType == typeof(byte[]); - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - if (reader.TokenType == JsonToken.Null) - { - return null; - } - if (reader.TokenType == JsonToken.String) - { - try - { - return LoneFunctions.StringToByteArray((string)reader.Value); - } - catch (Exception ex) - { - throw new Exception(string.Format("Error parsing version string: {0}", reader.Value), ex); - } - } - throw new Exception(string.Format("Unexpected token or value when parsing hex string. Token: {0}, Value: {1}", reader.TokenType, reader.Value)); - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - if (value == null) - { - writer.WriteNull(); - } - else - { - if (!(value is byte[])) - { - throw new JsonSerializationException("Expected byte[] object value"); - } - writer.WriteValue(LoneFunctions.ByteArrayToString(value as byte[])); - } - } - } - - [Serializable] - public class Mod - { -#pragma warning disable CS0649 - [JsonProperty("name")] - public string Name; - [JsonProperty("version"), - JsonConverter(typeof(VersionConverter))] - public Version Version; - [JsonProperty("approved")] - public bool Approved; - [JsonProperty("title")] - public string Title; - [JsonProperty("gameVersion"), - JsonConverter(typeof(VersionConverter))] - public Version GameVersion; - [JsonProperty("author")] - public string Author; -#pragma warning restore CS0649 - [Serializable] - public class PlatformFile - { - [JsonProperty("hash"), - JsonConverter(typeof(HexArrayConverter))] - public byte[] Hash = new byte[20]; - [JsonProperty("files", ItemConverterType = typeof(HexArrayConverter))] - public Dictionary FileHashes = new Dictionary(); - [JsonProperty("url")] - public string DownloadPath = null; - - public override string ToString() - { - return $"{LoneFunctions.ByteArrayToString(Hash)}@{DownloadPath}({string.Join(",",FileHashes.Select(o=>$"\"{o.Key}\":\"{LoneFunctions.ByteArrayToString(o.Value)}\""))})"; - } - } - - [Serializable] - public class FilesObject - { - [JsonProperty("steam")] - public PlatformFile Steam = null; - [JsonProperty("oculus")] - public PlatformFile Oculus = null; - } - - [JsonProperty("files")] - public FilesObject Files = null; - - public override string ToString() - { - return $"{{\"{Title} ({Name})\"v{Version} for {GameVersion} by {Author} with \"{Files.Steam}\" and \"{Files.Oculus}\"}}"; - } - } - - } -} diff --git a/IllusionInjector/Updating/ModsaberML/Updater.cs b/IllusionInjector/Updating/ModsaberML/Updater.cs deleted file mode 100644 index 26a5c653..00000000 --- a/IllusionInjector/Updating/ModsaberML/Updater.cs +++ /dev/null @@ -1,346 +0,0 @@ -using IllusionInjector.Updating.Backup; -using IllusionInjector.Utilities; -using Ionic.Zip; -using Newtonsoft.Json; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Security.Cryptography; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading; -using System.Threading.Tasks; -using UnityEngine; -using UnityEngine.Networking; -using Logger = IllusionInjector.Logging.Logger; - -namespace IllusionInjector.Updating.ModsaberML -{ - class Updater : MonoBehaviour - { - public static Updater instance; - - public void Awake() - { - try - { - if (instance != null) - Destroy(this); - else - { - instance = this; - CheckForUpdates(); - } - } - catch (Exception e) - { - Logger.log.Error(e); - } - } - - public void CheckForUpdates() - { - StartCoroutine(CheckForUpdatesCoroutine()); - } - - private struct UpdateStruct - { - public PluginManager.BSPluginMeta plugin; - public ApiEndpoint.Mod externInfo; - } - - IEnumerator CheckForUpdatesCoroutine() - { - Logger.log.Info("Checking for mod updates..."); - - var toUpdate = new List(); - var GameVersion = new Version(Application.version); - - foreach (var plugin in PluginManager.BSMetas) - { - var info = plugin.ModsaberInfo; - if (info == null) continue; - - using (var request = UnityWebRequest.Get(ApiEndpoint.ApiBase + string.Format(ApiEndpoint.GetApprovedEndpoint, info.InternalName))) - { - yield return request.SendWebRequest(); - - if (request.isNetworkError) - { - Logger.log.Error("Network error while trying to update mods"); - Logger.log.Error(request.error); - continue; - } - if (request.isHttpError) - { - if (request.responseCode == 404) - { - Logger.log.Error($"Mod {plugin.Plugin.Name} not found under name {info.InternalName}"); - continue; - } - - Logger.log.Error($"Server returned an error code while trying to update mod {plugin.Plugin.Name}"); - Logger.log.Error(request.error); - continue; - } - - var json = request.downloadHandler.text; - - ApiEndpoint.Mod modRegistry; - try - { - modRegistry = JsonConvert.DeserializeObject(json); - Logger.log.Debug(modRegistry.ToString()); - } - catch (Exception e) - { - Logger.log.Error($"Parse error while trying to update mods"); - Logger.log.Error(e); - continue; - } - - Logger.log.Debug($"Found Modsaber.ML registration for {plugin.Plugin.Name} ({info.InternalName})"); - Logger.log.Debug($"Installed version: {info.CurrentVersion}; Latest version: {modRegistry.Version}"); - if (modRegistry.Version > info.CurrentVersion) - { - Logger.log.Debug($"{plugin.Plugin.Name} needs an update!"); - if (modRegistry.GameVersion == GameVersion) - { - Logger.log.Debug($"Queueing update..."); - toUpdate.Add(new UpdateStruct - { - plugin = plugin, - externInfo = modRegistry - }); - } - else - { - Logger.log.Warn($"Update avaliable for {plugin.Plugin.Name}, but for a different Beat Saber version!"); - } - } - } - } - - Logger.log.Info($"{toUpdate.Count} mods need updating"); - - if (toUpdate.Count == 0) yield break; - - string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + Path.GetRandomFileName()); - Directory.CreateDirectory(tempDirectory); - foreach (var item in toUpdate) - { - StartCoroutine(UpdateModCoroutine(item, tempDirectory)); - } - } - - class StreamDownloadHandler : DownloadHandlerScript - { - public MemoryStream Stream { get; set; } - - public StreamDownloadHandler(MemoryStream stream) : base() - { - Stream = stream; - } - - protected override void ReceiveContentLength(int contentLength) - { - Stream.Capacity = contentLength; - Logger.log.Debug($"Got content length: {contentLength}"); - } - - protected override void CompleteContent() - { - Logger.log.Debug("Download complete"); - } - - protected override bool ReceiveData(byte[] data, int dataLength) - { - if (data == null || data.Length < 1) - { - Logger.log.Debug("CustomWebRequest :: ReceiveData - received a null/empty buffer"); - return false; - } - - Stream.Write(data, 0, dataLength); - return true; - } - - protected override byte[] GetData() { return null; } - - protected override float GetProgress() - { - return 0f; - } - - public override string ToString() - { - return $"{base.ToString()} ({Stream?.ToString()})"; - } - } - - private void ExtractPluginAsync(MemoryStream stream, UpdateStruct item, ApiEndpoint.Mod.PlatformFile fileInfo, string tempDirectory) - { - Logger.log.Debug($"Extracting ZIP file for {item.plugin.Plugin.Name}"); - - var data = stream.GetBuffer(); - SHA1 sha = new SHA1CryptoServiceProvider(); - var hash = sha.ComputeHash(data); - if (!LoneFunctions.UnsafeCompare(hash, fileInfo.Hash)) - throw new Exception("The hash for the file doesn't match what is defined"); - - var newFiles = new List(); - var backup = new BackupUnit(tempDirectory, $"backup-{item.plugin.ModsaberInfo.InternalName}"); - - try - { - bool shouldDeleteOldFile = true; - - using (var zipFile = ZipFile.Read(stream)) - { - Logger.log.Debug("Streams opened"); - foreach (var entry in zipFile) - { - if (entry.IsDirectory) - { - Logger.log.Debug($"Creating directory {entry.FileName}"); - Directory.CreateDirectory(Path.Combine(Environment.CurrentDirectory, entry.FileName)); - } - else - { - using (var ostream = new MemoryStream((int)entry.UncompressedSize)) - { - entry.Extract(ostream); - ostream.Seek(0, SeekOrigin.Begin); - - sha = new SHA1CryptoServiceProvider(); - var fileHash = sha.ComputeHash(ostream); - if (!LoneFunctions.UnsafeCompare(fileHash, fileInfo.FileHashes[entry.FileName])) - throw new Exception("The hash for the file doesn't match what is defined"); - - ostream.Seek(0, SeekOrigin.Begin); - FileInfo targetFile = new FileInfo(Path.Combine(Environment.CurrentDirectory, entry.FileName)); - Directory.CreateDirectory(targetFile.DirectoryName); - - if (targetFile.FullName == item.plugin.Filename) - shouldDeleteOldFile = false; // overwriting old file, no need to delete - - if (targetFile.Exists) - backup.Add(targetFile); - else - newFiles.Add(targetFile); - - Logger.log.Debug($"Extracting file {targetFile.FullName}"); - - var fstream = targetFile.Create(); - ostream.CopyTo(fstream); - } - } - } - } - - if (item.plugin.Plugin is SelfPlugin) - { // currently updating self - Process.Start(new ProcessStartInfo - { - FileName = item.plugin.Filename, - Arguments = $"--waitfor={Process.GetCurrentProcess().Id} --nowait", - UseShellExecute = false - }); - } - else if (shouldDeleteOldFile) - File.Delete(item.plugin.Filename); - } - catch (Exception) - { // something failed; restore - foreach (var file in newFiles) - file.Delete(); - backup.Restore(); - backup.Delete(); - - throw; - } - - backup.Delete(); - - Logger.log.Debug("Downloader exited"); - } - - IEnumerator UpdateModCoroutine(UpdateStruct item, string tempDirectory) - { - Logger.log.Debug($"Steam avaliable: {SteamCheck.IsAvailable}"); - - ApiEndpoint.Mod.PlatformFile platformFile; - if (SteamCheck.IsAvailable || item.externInfo.Files.Oculus == null) - platformFile = item.externInfo.Files.Steam; - else - platformFile = item.externInfo.Files.Oculus; - - string url = platformFile.DownloadPath; - - Logger.log.Debug($"URL = {url}"); - - const int MaxTries = 3; - int maxTries = MaxTries; - while (maxTries > 0) - { - if (maxTries-- != MaxTries) - Logger.log.Info($"Re-trying download..."); - - using (var stream = new MemoryStream()) - using (var request = UnityWebRequest.Get(url)) - using (var taskTokenSource = new CancellationTokenSource()) - { - var dlh = new StreamDownloadHandler(stream); - request.downloadHandler = dlh; - - Logger.log.Debug("Sending request"); - //Logger.log.Debug(request?.downloadHandler?.ToString() ?? "DLH==NULL"); - yield return request.SendWebRequest(); - Logger.log.Debug("Download finished"); - - if (request.isNetworkError) - { - Logger.log.Error("Network error while trying to update mod"); - Logger.log.Error(request.error); - taskTokenSource.Cancel(); - continue; - } - if (request.isHttpError) - { - Logger.log.Error($"Server returned an error code while trying to update mod"); - Logger.log.Error(request.error); - taskTokenSource.Cancel(); - continue; - } - - stream.Seek(0, SeekOrigin.Begin); // reset to beginning - - var downloadTask = Task.Run(() => - { // use slightly more multithreaded approach than coroutines - ExtractPluginAsync(stream, item, platformFile, tempDirectory); - }, taskTokenSource.Token); - - while (!(downloadTask.IsCompleted || downloadTask.IsCanceled || downloadTask.IsFaulted)) - yield return null; // pause coroutine until task is done - - if (downloadTask.IsFaulted) - { - Logger.log.Error($"Error downloading mod {item.plugin.Plugin.Name}"); - Logger.log.Error(downloadTask.Exception); - continue; - } - - break; - } - } - - if (maxTries == 0) - Logger.log.Warn($"Plugin download failed {MaxTries} times, not re-trying"); - else - Logger.log.Debug("Download complete"); - } - } -} diff --git a/IllusionInjector/Updating/SelfPlugin.cs b/IllusionInjector/Updating/SelfPlugin.cs deleted file mode 100644 index b24f5642..00000000 --- a/IllusionInjector/Updating/SelfPlugin.cs +++ /dev/null @@ -1,55 +0,0 @@ -using IllusionPlugin; -using IllusionPlugin.BeatSaber; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using UnityEngine.SceneManagement; - -namespace IllusionInjector.Updating -{ - internal class SelfPlugin : IBeatSaberPlugin - { - internal const string IPA_Name = "Beat Saber IPA"; - internal const string IPA_Version = "3.9.0"; - - public string Name => IPA_Name; - - public string Version => IPA_Version; - - public ModsaberModInfo ModInfo => new ModsaberModInfo - { - CurrentVersion = new Version(IPA_Version), - InternalName = "beatsaber-ipa-reloaded" - }; - - public void OnActiveSceneChanged(Scene prevScene, Scene nextScene) - { - } - - public void OnApplicationQuit() - { - } - - public void OnApplicationStart() - { - } - - public void OnFixedUpdate() - { - } - - public void OnSceneLoaded(Scene scene, LoadSceneMode sceneMode) - { - } - - public void OnSceneUnloaded(Scene scene) - { - } - - public void OnUpdate() - { - } - } -} diff --git a/IllusionInjector/Utilities/Extensions.cs b/IllusionInjector/Utilities/Extensions.cs deleted file mode 100644 index 326c675a..00000000 --- a/IllusionInjector/Utilities/Extensions.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace IllusionInjector.Utilities -{ - public static class Extensions - { - public static object GetDefault(this Type type) - { - if (type.IsValueType) - { - return Activator.CreateInstance(type); - } - return null; - } - } -} diff --git a/IllusionInjector/Utilities/LoneFunctions.cs b/IllusionInjector/Utilities/LoneFunctions.cs deleted file mode 100644 index c270f6e8..00000000 --- a/IllusionInjector/Utilities/LoneFunctions.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace IllusionInjector.Utilities -{ - public static class LoneFunctions - { - public static byte[] StringToByteArray(string hex) - { - int NumberChars = hex.Length; - byte[] bytes = new byte[NumberChars / 2]; - for (int i = 0; i < NumberChars; i += 2) - bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); - return bytes; - } - - public static string ByteArrayToString(byte[] ba) - { - StringBuilder hex = new StringBuilder(ba.Length * 2); - foreach (byte b in ba) - hex.AppendFormat("{0:x2}", b); - return hex.ToString(); - } - - // Copyright (c) 2008-2013 Hafthor Stefansson - // Distributed under the MIT/X11 software license - // Ref: http://www.opensource.org/licenses/mit-license.php. - // From: https://stackoverflow.com/a/8808245/3117125 - public static unsafe bool UnsafeCompare(byte[] a1, byte[] a2) - { - if (a1 == a2) return true; - if (a1 == null || a2 == null || a1.Length != a2.Length) - return false; - fixed (byte* p1 = a1, p2 = a2) - { - byte* x1 = p1, x2 = p2; - int l = a1.Length; - for (int i = 0; i < l / 8; i++, x1 += 8, x2 += 8) - if (*((long*)x1) != *((long*)x2)) return false; - if ((l & 4) != 0) { if (*((int*)x1) != *((int*)x2)) return false; x1 += 4; x2 += 4; } - if ((l & 2) != 0) { if (*((short*)x1) != *((short*)x2)) return false; x1 += 2; x2 += 2; } - if ((l & 1) != 0) if (*((byte*)x1) != *((byte*)x2)) return false; - return true; - } - } - - public static string GetRelativePath(string filespec, string folder) - { - Uri pathUri = new Uri(filespec); - // Folders must end in a slash - if (!folder.EndsWith(Path.DirectorySeparatorChar.ToString())) - { - folder += Path.DirectorySeparatorChar; - } - Uri folderUri = new Uri(folder); - return Uri.UnescapeDataString(folderUri.MakeRelativeUri(pathUri).ToString().Replace('/', Path.DirectorySeparatorChar)); - } - } -} diff --git a/IllusionInjector/Utilities/SteamCheck.cs b/IllusionInjector/Utilities/SteamCheck.cs deleted file mode 100644 index a4f9624b..00000000 --- a/IllusionInjector/Utilities/SteamCheck.cs +++ /dev/null @@ -1,27 +0,0 @@ -using IllusionInjector.Logging; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace IllusionInjector.Utilities -{ - public static class SteamCheck - { - public static Type SteamVRCamera; - public static Type SteamVRExternalCamera; - public static Type SteamVRFade; - public static bool IsAvailable => FindSteamVRAsset(); - - private static bool FindSteamVRAsset() - { - // these require assembly qualified names.... - SteamVRCamera = Type.GetType("SteamVR_Camera, Assembly-CSharp-firstpass, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", false); - SteamVRExternalCamera = Type.GetType("SteamVR_ExternalCamera, Assembly-CSharp-firstpass, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", false); - SteamVRFade = Type.GetType("SteamVR_Fade, Assembly-CSharp-firstpass, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", false); - - return SteamVRCamera != null && SteamVRExternalCamera != null && SteamVRFade != null; - } - } -} diff --git a/IllusionInjector/obj/Debug/IllusionInjector.csproj.CoreCompileInputs.cache b/IllusionInjector/obj/Debug/IllusionInjector.csproj.CoreCompileInputs.cache deleted file mode 100644 index 484972a6..00000000 --- a/IllusionInjector/obj/Debug/IllusionInjector.csproj.CoreCompileInputs.cache +++ /dev/null @@ -1 +0,0 @@ -dae758b90b096a39aa928b136ec952d8ed591e5d diff --git a/IllusionInjector/packages.config b/IllusionInjector/packages.config deleted file mode 100644 index 71c37d6d..00000000 --- a/IllusionInjector/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/IllusionPlugin/BeatSaber/IBeatSaberPlugin.cs b/IllusionPlugin/BeatSaber/IBeatSaberPlugin.cs deleted file mode 100644 index a9e2753c..00000000 --- a/IllusionPlugin/BeatSaber/IBeatSaberPlugin.cs +++ /dev/null @@ -1,71 +0,0 @@ -using IllusionPlugin.BeatSaber; -using System; -using System.Collections.Generic; -using System.Text; -using UnityEngine.SceneManagement; - -namespace IllusionPlugin -{ - /// - /// Interface for Beat Saber plugins. Every class that implements this will be loaded if the DLL is placed at - /// data/Managed/Plugins. - /// - public interface IBeatSaberPlugin - { - - /// - /// Gets the name of the plugin. - /// - string Name { get; } - - /// - /// Gets the version of the plugin. - /// - string Version { get; } - - /// - /// Gets the info for the Modsaber release of this plugin. Return null if there is no Modsaber release. - /// - ModsaberModInfo ModInfo { get; } - - /// - /// Gets invoked when the application is started. - /// - void OnApplicationStart(); - - /// - /// Gets invoked when the application is closed. - /// - void OnApplicationQuit(); - - /// - /// Gets invoked on every graphic update. - /// - void OnUpdate(); - - /// - /// Gets invoked on ever physics update. - /// - void OnFixedUpdate(); - - /// - /// Gets invoked whenever a scene is loaded. - /// - /// The scene currently loaded - /// The type of loading - void OnSceneLoaded(Scene scene, LoadSceneMode sceneMode); - - /// - /// Gets invoked whenever a scene is unloaded - /// - /// The unloaded scene - void OnSceneUnloaded(Scene scene); - - /// - /// Gets invoked whenever a scene is changed - /// - /// The Scene that was previously loaded - /// The Scene being loaded - void OnActiveSceneChanged(Scene prevScene, Scene nextScene); - } -} diff --git a/IllusionPlugin/BeatSaber/IEnhancedBeatSaberPlugin.cs b/IllusionPlugin/BeatSaber/IEnhancedBeatSaberPlugin.cs deleted file mode 100644 index dfbd4f9f..00000000 --- a/IllusionPlugin/BeatSaber/IEnhancedBeatSaberPlugin.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace IllusionPlugin -{ - /// - /// An enhanced version of a standard BeatSaber plugin. - /// - public interface IEnhancedBeatSaberPlugin : IBeatSaberPlugin, IGenericEnhancedPlugin - { - } -} diff --git a/IllusionPlugin/BeatSaber/ModsaberModInfo.cs b/IllusionPlugin/BeatSaber/ModsaberModInfo.cs deleted file mode 100644 index 6defe31c..00000000 --- a/IllusionPlugin/BeatSaber/ModsaberModInfo.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace IllusionPlugin.BeatSaber -{ - /// - /// A class to provide information about a mod on ModSaber.ML - /// - public class ModsaberModInfo - { - /// - /// The name the mod uses on ModSaber as an identifier. - /// - public string InternalName { get; set; } - - /// - /// The version of the currently installed mod. Used to compare to the version on ModSaber. - /// - public Version CurrentVersion { get; set; } - } -} diff --git a/IllusionPlugin/IGenericEnhancedPlugin.cs b/IllusionPlugin/IGenericEnhancedPlugin.cs deleted file mode 100644 index 80327f5c..00000000 --- a/IllusionPlugin/IGenericEnhancedPlugin.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace IllusionPlugin -{ - /// - /// A generic interface for the modification for enhanced plugins. - /// - public interface IGenericEnhancedPlugin - { - /// - /// Gets a list of executables this plugin should be excuted on (without the file ending) - /// - /// { "PlayClub", "PlayClubStudio" } - string[] Filter { get; } - - /// - /// Called after Update. - /// - void OnLateUpdate(); - } -} diff --git a/IllusionPlugin/IPA/IEnhancedPlugin.cs b/IllusionPlugin/IPA/IEnhancedPlugin.cs deleted file mode 100644 index ac925b2e..00000000 --- a/IllusionPlugin/IPA/IEnhancedPlugin.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace IllusionPlugin -{ - /// - /// An enhanced version of the standard IPA plugin. - /// - [Obsolete("When building plugins for Beat Saber, use IEnhancedBeatSaberPlugin")] - public interface IEnhancedPlugin : IPlugin, IGenericEnhancedPlugin - { - } -} \ No newline at end of file diff --git a/IllusionPlugin/IPA/IPlugin.cs b/IllusionPlugin/IPA/IPlugin.cs deleted file mode 100644 index 0610da73..00000000 --- a/IllusionPlugin/IPA/IPlugin.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace IllusionPlugin -{ - /// - /// Interface for generic Illusion unity plugins. Every class that implements this will be loaded if the DLL is placed at - /// data/Managed/Plugins. - /// - [Obsolete("When building plugins for Beat Saber, use IBeatSaberPlugin")] - public interface IPlugin - { - - /// - /// Gets the name of the plugin. - /// - string Name { get; } - - /// - /// Gets the version of the plugin. - /// - string Version { get; } - - /// - /// Gets invoked when the application is started. - /// - void OnApplicationStart(); - - /// - /// Gets invoked when the application is closed. - /// - void OnApplicationQuit(); - - /// - /// Gets invoked whenever a level is loaded. - /// - /// - void OnLevelWasLoaded(int level); - - /// - /// Gets invoked after the first update cycle after a level was loaded. - /// - /// - void OnLevelWasInitialized(int level); - - /// - /// Gets invoked on every graphic update. - /// - void OnUpdate(); - - - /// - /// Gets invoked on ever physics update. - /// - void OnFixedUpdate(); - } -} \ No newline at end of file diff --git a/IllusionPlugin/IllusionPlugin.XML b/IllusionPlugin/IllusionPlugin.XML deleted file mode 100644 index 493113b0..00000000 --- a/IllusionPlugin/IllusionPlugin.XML +++ /dev/null @@ -1,178 +0,0 @@ - - - - IllusionPlugin - - - - - Gets a list of executables this plugin should be excuted on (without the file ending) - - { "PlayClub", "PlayClubStudio" } - - - - Create a New INI file to store or load data - - - - - INIFile Constructor. - - - - - - Write Data to the INI File - - - Section name - - Key Name - - Value Name - - - - Read Data Value From the Ini File - - - - - - - - - Interface for generic Illusion unity plugins. Every class that implements this will be loaded if the DLL is placed at - data/Managed/Plugins. - - - - - Gets the name of the plugin. - - - - - Gets the version of the plugin. - - - - - Gets invoked when the application is started. - - - - - Gets invoked when the application is closed. - - - - - Gets invoked whenever a level is loaded. - - - - - - Gets invoked after the first update cycle after a level was loaded. - - - - - - Gets invoked on every graphic update. - - - - - Gets invoked on ever physics update. - - - - - Allows to get and set preferences for your mod. - - - - - Gets a string from the ini. - - Section of the key. - Name of the key. - Value that should be used when no value is found. - Whether or not the default value should be written if no value is found. - - - - - Gets an int from the ini. - - Section of the key. - Name of the key. - Value that should be used when no value is found. - Whether or not the default value should be written if no value is found. - - - - - Gets a float from the ini. - - Section of the key. - Name of the key. - Value that should be used when no value is found. - Whether or not the default value should be written if no value is found. - - - - - Gets a bool from the ini. - - Section of the key. - Name of the key. - Value that should be used when no value is found. - Whether or not the default value should be written if no value is found. - - - - - Checks whether or not a key exists in the ini. - - Section of the key. - Name of the key. - - - - - Sets a float in the ini. - - Section of the key. - Name of the key. - Value that should be written. - - - - Sets an int in the ini. - - Section of the key. - Name of the key. - Value that should be written. - - - - Sets a string in the ini. - - Section of the key. - Name of the key. - Value that should be written. - - - - Sets a bool in the ini. - - Section of the key. - Name of the key. - Value that should be written. - - - diff --git a/IllusionPlugin/IllusionPlugin.csproj b/IllusionPlugin/IllusionPlugin.csproj deleted file mode 100644 index f5f34363..00000000 --- a/IllusionPlugin/IllusionPlugin.csproj +++ /dev/null @@ -1,69 +0,0 @@ - - - - - Debug - AnyCPU - {E2848BFB-5432-42F4-8AE0-D2EC0CDF2F71} - Library - Properties - IllusionPlugin - IllusionPlugin - v4.6 - 512 - - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - - - none - true - bin\Release\ - TRACE - prompt - 4 - bin\Release\IllusionPlugin.XML - false - true - - - - - - - ..\Libs\UnityEngine.CoreModule.dll - False - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/IllusionPlugin/IniFile.cs b/IllusionPlugin/IniFile.cs deleted file mode 100644 index b8f47bce..00000000 --- a/IllusionPlugin/IniFile.cs +++ /dev/null @@ -1,100 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Runtime.InteropServices; -using System.Text; - -namespace IllusionPlugin -{ - /// - /// Create a New INI file to store or load data - /// - internal class IniFile - { - [DllImport("KERNEL32.DLL", EntryPoint = "GetPrivateProfileStringW", - SetLastError = true, - CharSet = CharSet.Unicode, ExactSpelling = true, - CallingConvention = CallingConvention.StdCall)] - private static extern int GetPrivateProfileString( - string lpSection, - string lpKey, - string lpDefault, - StringBuilder lpReturnString, - int nSize, - string lpFileName); - - [DllImport("KERNEL32.DLL", EntryPoint = "WritePrivateProfileStringW", - SetLastError = true, - CharSet = CharSet.Unicode, ExactSpelling = true, - CallingConvention = CallingConvention.StdCall)] - private static extern int WritePrivateProfileString( - string lpSection, - string lpKey, - string lpValue, - string lpFileName); - - /*private string _path = ""; - public string Path - { - get - { - return _path; - } - set - { - if (!File.Exists(value)) - File.WriteAllText(value, "", Encoding.Unicode); - _path = value; - } - }*/ - - private FileInfo _iniFileInfo; - public FileInfo IniFileInfo { - get => _iniFileInfo; - set { - _iniFileInfo = value; - if (_iniFileInfo.Exists) return; - _iniFileInfo.Directory?.Create(); - _iniFileInfo.Create(); - } - } - - /// - /// INIFile Constructor. - /// - /// - public IniFile(string iniPath) - { - IniFileInfo = new FileInfo(iniPath); - //this.Path = INIPath; - } - - /// - /// Write Data to the INI File - /// - /// - /// Section name - /// - /// Key Name - /// - /// Value Name - public void IniWriteValue(string Section, string Key, string Value) - { - WritePrivateProfileString(Section, Key, Value, IniFileInfo.FullName); - } - - /// - /// Read Data Value From the Ini File - /// - /// - /// - /// - public string IniReadValue(string Section, string Key) - { - const int MAX_CHARS = 1023; - StringBuilder result = new StringBuilder(MAX_CHARS); - GetPrivateProfileString(Section, Key, "", result, MAX_CHARS, IniFileInfo.FullName); - return result.ToString(); - } - } -} diff --git a/IllusionPlugin/Logging/LogPrinter.cs b/IllusionPlugin/Logging/LogPrinter.cs deleted file mode 100644 index 441029e6..00000000 --- a/IllusionPlugin/Logging/LogPrinter.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace IllusionPlugin.Logging -{ - /// - /// The log printer's base class. - /// - public abstract class LogPrinter - { - /// - /// Provides a filter for which log levels to allow through. - /// - public abstract Logger.LogLevel Filter { get; set; } - /// - /// Prints a provided message from a given log at the specified time. - /// - /// the log level - /// the time the message was composed - /// the name of the log that created this message - /// the message - public abstract void Print(Logger.Level level, DateTime time, string logName, string message); - /// - /// Called before the first print in a group. May be called multiple times. - /// Use this to create file handles and the like. - /// - public virtual void StartPrint() { } - /// - /// Called after the last print in a group. May be called multiple times. - /// Use this to dispose file handles and the like. - /// - public virtual void EndPrint() { } - } -} diff --git a/IllusionPlugin/Logging/Logger.cs b/IllusionPlugin/Logging/Logger.cs deleted file mode 100644 index befa4ccd..00000000 --- a/IllusionPlugin/Logging/Logger.cs +++ /dev/null @@ -1,182 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace IllusionPlugin.Logging -{ - /// - /// The logger base class. Provides the format for console logs. - /// - public abstract class Logger - { - /// - /// The standard format for log messages. - /// - public static string LogFormat { get; protected internal set; } = "[{3} @ {2:HH:mm:ss} | {1}] {0}"; - - /// - /// An enum specifying the level of the message. Resembles Syslog. - /// - public enum Level : byte - { - /// - /// No associated level. These never get shown. - /// - None = 0, - /// - /// A debug message. - /// - Debug = 1, - /// - /// An informational message. - /// - Info = 2, - /// - /// A warning message. - /// - Warning = 4, - /// - /// An error message. - /// - Error = 8, - /// - /// A critical error message. - /// - Critical = 16 - } - - /// - /// An enum providing log level filters. - /// - [Flags] - public enum LogLevel : byte - { - /// - /// Allow no messages through. - /// - None = Level.None, - /// - /// Only shows Debug messages. - /// - DebugOnly = Level.Debug, - /// - /// Only shows info messages. - /// - InfoOnly = Level.Info, - /// - /// Only shows Warning messages. - /// - WarningOnly = Level.Warning, - /// - /// Only shows Error messages. - /// - ErrorOnly = Level.Error, - /// - /// Only shows Critical messages. - /// - CriticalOnly = Level.Critical, - - /// - /// Shows all messages error and up. - /// - ErrorUp = ErrorOnly | CriticalOnly, - /// - /// Shows all messages warning and up. - /// - WarningUp = WarningOnly | ErrorUp, - /// - /// Shows all messages info and up. - /// - InfoUp = InfoOnly | WarningUp, - /// - /// Shows all messages. - /// - All = DebugOnly | InfoUp, - } - - /// - /// A basic log function. - /// - /// the level of the message - /// the message to log - public abstract void Log(Level level, string message); - /// - /// A basic log function taking an exception to log. - /// - /// the level of the message - /// the exception to log - public virtual void Log(Level level, Exception exeption) => Log(level, exeption.ToString()); - /// - /// Sends a debug message. - /// Equivalent to Log(Level.Debug, message); - /// - /// - /// the message to log - public virtual void Debug(string message) => Log(Level.Debug, message); - /// - /// Sends an exception as a debug message. - /// Equivalent to Log(Level.Debug, e); - /// - /// - /// the exception to log - public virtual void Debug(Exception e) => Log(Level.Debug, e); - /// - /// Sends an info message. - /// Equivalent to Log(Level.Info, message). - /// - /// - /// the message to log - public virtual void Info(string message) => Log(Level.Info, message); - /// - /// Sends an exception as an info message. - /// Equivalent to Log(Level.Info, e); - /// - /// - /// the exception to log - public virtual void Info(Exception e) => Log(Level.Info, e); - /// - /// Sends a warning message. - /// Equivalent to Log(Level.Warning, message). - /// - /// - /// the message to log - public virtual void Warn(string message) => Log(Level.Warning, message); - /// - /// Sends an exception as a warning message. - /// Equivalent to Log(Level.Warning, e); - /// - /// - /// the exception to log - public virtual void Warn(Exception e) => Log(Level.Warning, e); - /// - /// Sends an error message. - /// Equivalent to Log(Level.Error, message). - /// - /// - /// the message to log - public virtual void Error(string message) => Log(Level.Error, message); - /// - /// Sends an exception as an error message. - /// Equivalent to Log(Level.Error, e); - /// - /// - /// the exception to log - public virtual void Error(Exception e) => Log(Level.Error, e); - /// - /// Sends a critical message. - /// Equivalent to Log(Level.Critical, message). - /// - /// - /// the message to log - public virtual void Critical(string message) => Log(Level.Critical, message); - /// - /// Sends an exception as a critical message. - /// Equivalent to Log(Level.Critical, e); - /// - /// - /// the exception to log - public virtual void Critical(Exception e) => Log(Level.Critical, e); - } -} diff --git a/IllusionPlugin/ModPrefs.cs b/IllusionPlugin/ModPrefs.cs deleted file mode 100644 index 198cc6aa..00000000 --- a/IllusionPlugin/ModPrefs.cs +++ /dev/null @@ -1,285 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; - -namespace IllusionPlugin -{ - /// - /// Allows to get and set preferences for your mod. - /// - public interface IModPrefs - { - /// - /// Gets a string from the ini. - /// - /// Section of the key. - /// Name of the key. - /// Value that should be used when no value is found. - /// Whether or not the default value should be written if no value is found. - /// - string GetString(string section, string name, string defaultValue = "", bool autoSave = false); - /// - /// Gets an int from the ini. - /// - /// Section of the key. - /// Name of the key. - /// Value that should be used when no value is found. - /// Whether or not the default value should be written if no value is found. - /// - int GetInt(string section, string name, int defaultValue = 0, bool autoSave = false); - /// - /// Gets a float from the ini. - /// - /// Section of the key. - /// Name of the key. - /// Value that should be used when no value is found. - /// Whether or not the default value should be written if no value is found. - /// - float GetFloat(string section, string name, float defaultValue = 0f, bool autoSave = false); - /// - /// Gets a bool from the ini. - /// - /// Section of the key. - /// Name of the key. - /// Value that should be used when no value is found. - /// Whether or not the default value should be written if no value is found. - /// - bool GetBool(string section, string name, bool defaultValue = false, bool autoSave = false); - /// - /// Checks whether or not a key exists in the ini. - /// - /// Section of the key. - /// Name of the key. - /// - bool HasKey(string section, string name); - /// - /// Sets a float in the ini. - /// - /// Section of the key. - /// Name of the key. - /// Value that should be written. - void SetFloat(string section, string name, float value); - /// - /// Sets an int in the ini. - /// - /// Section of the key. - /// Name of the key. - /// Value that should be written. - void SetInt(string section, string name, int value); - /// - /// Sets a string in the ini. - /// - /// Section of the key. - /// Name of the key. - /// Value that should be written. - void SetString(string section, string name, string value); - /// - /// Sets a bool in the ini. - /// - /// Section of the key. - /// Name of the key. - /// Value that should be written. - void SetBool(string section, string name, bool value); - } - - /// - /// Allows to get and set preferences for your mod. - /// - public class ModPrefs : IModPrefs - { - private static ModPrefs _staticInstance = null; - private static IModPrefs StaticInstace - { - get - { - if (_staticInstance == null) - _staticInstance = new ModPrefs(); - return _staticInstance; - } - } - - internal static Dictionary ModPrefses { get; set; } = new Dictionary(); - - private IniFile Instance; - - /// - /// Constructs a ModPrefs object for the provide plugin. - /// - /// the plugin to get the preferences file for - public ModPrefs(IBeatSaberPlugin plugin) { - Instance = new IniFile(Path.Combine(Environment.CurrentDirectory, "UserData", "ModPrefs", $"{plugin.Name}.ini")); - ModPrefses.Add(plugin, this); - } - - private ModPrefs() - { - Instance = new IniFile(Path.Combine(Environment.CurrentDirectory, "UserData", "modprefs.ini")); - } - - string IModPrefs.GetString(string section, string name, string defaultValue, bool autoSave) - { - var value = Instance.IniReadValue(section, name); - if (value != "") - return value; - else if (autoSave) - (this as IModPrefs).SetString(section, name, defaultValue); - - return defaultValue; - } - /// - /// Gets a string from the ini. - /// - /// Section of the key. - /// Name of the key. - /// Value that should be used when no value is found. - /// Whether or not the default value should be written if no value is found. - /// - public static string GetString(string section, string name, string defaultValue = "", bool autoSave = false) - => StaticInstace.GetString(section, name, defaultValue, autoSave); - - int IModPrefs.GetInt(string section, string name, int defaultValue, bool autoSave) - { - if (int.TryParse(Instance.IniReadValue(section, name), out var value)) - return value; - else if (autoSave) - (this as IModPrefs).SetInt(section, name, defaultValue); - - return defaultValue; - } - /// - /// Gets an int from the ini. - /// - /// Section of the key. - /// Name of the key. - /// Value that should be used when no value is found. - /// Whether or not the default value should be written if no value is found. - /// - public static int GetInt(string section, string name, int defaultValue = 0, bool autoSave = false) - => StaticInstace.GetInt(section, name, defaultValue, autoSave); - - float IModPrefs.GetFloat(string section, string name, float defaultValue, bool autoSave) - { - if (float.TryParse(Instance.IniReadValue(section, name), out var value)) - return value; - else if (autoSave) - (this as IModPrefs).SetFloat(section, name, defaultValue); - - return defaultValue; - } - /// - /// Gets a float from the ini. - /// - /// Section of the key. - /// Name of the key. - /// Value that should be used when no value is found. - /// Whether or not the default value should be written if no value is found. - /// - public static float GetFloat(string section, string name, float defaultValue = 0f, bool autoSave = false) - => StaticInstace.GetFloat(section, name, defaultValue, autoSave); - - bool IModPrefs.GetBool(string section, string name, bool defaultValue, bool autoSave) - { - string sVal = GetString(section, name, null); - if (sVal == "1" || sVal == "0") - { - return sVal == "1"; - } else if (autoSave) - { - (this as IModPrefs).SetBool(section, name, defaultValue); - } - - return defaultValue; - } - /// - /// Gets a bool from the ini. - /// - /// Section of the key. - /// Name of the key. - /// Value that should be used when no value is found. - /// Whether or not the default value should be written if no value is found. - /// - public static bool GetBool(string section, string name, bool defaultValue = false, bool autoSave = false) - => StaticInstace.GetBool(section, name, defaultValue, autoSave); - - bool IModPrefs.HasKey(string section, string name) - { - return Instance.IniReadValue(section, name) != null; - } - /// - /// Checks whether or not a key exists in the ini. - /// - /// Section of the key. - /// Name of the key. - /// - public static bool HasKey(string section, string name) => StaticInstace.HasKey(section, name); - - void IModPrefs.SetFloat(string section, string name, float value) - { - Instance.IniWriteValue(section, name, value.ToString()); - } - /// - /// Sets a float in the ini. - /// - /// Section of the key. - /// Name of the key. - /// Value that should be written. - public static void SetFloat(string section, string name, float value) - => StaticInstace.SetFloat(section, name, value); - - void IModPrefs.SetInt(string section, string name, int value) - { - Instance.IniWriteValue(section, name, value.ToString()); - } - /// - /// Sets an int in the ini. - /// - /// Section of the key. - /// Name of the key. - /// Value that should be written. - public static void SetInt(string section, string name, int value) - => StaticInstace.SetInt(section, name, value); - - void IModPrefs.SetString(string section, string name, string value) - { - Instance.IniWriteValue(section, name, value); - } - /// - /// Sets a string in the ini. - /// - /// Section of the key. - /// Name of the key. - /// Value that should be written. - public static void SetString(string section, string name, string value) - => StaticInstace.SetString(section, name, value); - - void IModPrefs.SetBool(string section, string name, bool value) - { - Instance.IniWriteValue(section, name, value ? "1" : "0"); - } - /// - /// Sets a bool in the ini. - /// - /// Section of the key. - /// Name of the key. - /// Value that should be written. - public static void SetBool(string section, string name, bool value) - => StaticInstace.SetBool(section, name, value); - } - - /// - /// An extension class for IBeatSaberPlugins. - /// - public static class ModPrefsExtensions { - /// - /// Gets the ModPrefs object for the provided plugin. - /// - /// the plugin wanting the prefrences - /// the ModPrefs object - public static IModPrefs GetModPrefs(this IBeatSaberPlugin plugin) { - return ModPrefs.ModPrefses.First(o => o.Key == plugin).Value; - } - } -} diff --git a/IllusionPlugin/Properties/AssemblyInfo.cs b/IllusionPlugin/Properties/AssemblyInfo.cs deleted file mode 100644 index 73d2f6b4..00000000 --- a/IllusionPlugin/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("IllusionPlugin")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("IllusionPlugin")] -[assembly: AssemblyCopyright("Copyright © 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("e8cea89d-6c2f-4729-94b3-f355f7db19e5")] -[assembly: InternalsVisibleTo("IllusionInjector")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/IllusionPlugin/Utils/ReflectionUtil.cs b/IllusionPlugin/Utils/ReflectionUtil.cs deleted file mode 100644 index cf8fdde1..00000000 --- a/IllusionPlugin/Utils/ReflectionUtil.cs +++ /dev/null @@ -1,203 +0,0 @@ -using System; -using System.Reflection; -using UnityEngine; - -namespace IllusionPlugin.Utils -{ - /// - /// A utility class providing reflection helper methods. - /// - public static class ReflectionUtil - { - /// - /// Sets a (potentially) private field on the target object. - /// - /// the object instance - /// the field to set - /// the value to set it to - public static void SetPrivateField(this object obj, string fieldName, object value) - { - var prop = obj.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - prop.SetValue(obj, value); - } - - /// - /// Gets the value of a (potentially) private field. - /// - /// the type of te field (result casted) - /// the object instance to pull from - /// the name of the field to read - /// the value of the field - public static T GetPrivateField(this object obj, string fieldName) - { - var prop = obj.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance); - var value = prop.GetValue(obj); - return (T) value; - } - - /// - /// Sets a (potentially) private propert on the target object. - /// - /// the target object instance - /// the name of the property - /// the value to set it to - public static void SetPrivateProperty(this object obj, string propertyName, object value) - { - var prop = obj.GetType() - .GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - prop.SetValue(obj, value, null); - } - - /// - /// Invokes a (potentially) private method. - /// - /// the object to call from - /// the method name - /// the method parameters - /// the return value - public static object InvokePrivateMethod(this object obj, string methodName, params object[] methodParams) - { - MethodInfo dynMethod = obj.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); - return dynMethod.Invoke(obj, methodParams); - } - - /// - /// Invokes a (potentially) private method. - /// - /// the return type - /// the object to call from - /// the method name to call - /// the method's parameters - /// the return value - public static T InvokePrivateMethod(this object obj, string methodName, params object[] methodParams) - { - return (T)InvokePrivateMethod(obj, methodName, methodParams); - } - - /// - /// Copies a component of type originalType to a component of overridingType on the destination GameObject. - /// - /// the original component - /// the new component's type - /// the destination GameObject - /// overrides the source component type (for example, to a superclass) - /// the copied component - public static Component CopyComponent(this Component original, Type overridingType, GameObject destination, Type originalTypeOverride = null) - { - var copy = destination.AddComponent(overridingType); - var originalType = originalTypeOverride ?? original.GetType(); - - Type type = originalType; - while (type != typeof(MonoBehaviour)) - { - CopyForType(type, original, copy); - type = type.BaseType; - } - - return copy; - } - - /// - /// A generic version of CopyComponent. - /// - /// - /// the overriding type - /// the original component - /// the destination game object - /// overrides the source component type (for example, to a superclass) - /// the copied component - public static T CopyComponent(this Component original, GameObject destination, Type originalTypeOverride = null) - where T : Component - { - var copy = destination.AddComponent(); - var originalType = originalTypeOverride ?? original.GetType(); - - Type type = originalType; - while (type != typeof(MonoBehaviour)) - { - CopyForType(type, original, copy); - type = type.BaseType; - } - - return copy; - } - - private static void CopyForType(Type type, Component source, Component destination) - { - FieldInfo[] myObjectFields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetField); - - foreach (FieldInfo fi in myObjectFields) - { - fi.SetValue(destination, fi.GetValue(source)); - } - } - - /// - /// Calls an instance method on a type specified by functionClass and dependency. - /// - /// - /// the type name - /// the assembly the type is in - /// the name of the method to call - /// the type signature of the method - /// the method parameters - /// the result of the call - public static object CallNonStaticMethod(string functionClass, string dependency, string function, Type[] methodSig, params object[] parameters) - { - return CallNonStaticMethod(Type.GetType(string.Format("{0},{1}", functionClass, dependency)), function, methodSig, parameters); - } - - /// - /// Calls an instance method on a new object. - /// - /// the object type - /// the name of the method to call - /// the type signature - /// the parameters - /// the result of the call - public static object CallNonStaticMethod(this Type type, /*string functionClass, string dependency,*/ string function, Type[] methodSig, params object[] parameters) - { - //Type FunctionClass = Type.GetType(string.Format("{0},{1}", functionClass, dependency)); - if (type != null) - { - object instance = Activator.CreateInstance(type); - if (instance != null) - { - Type instType = instance.GetType(); - MethodInfo methodInfo = instType.GetMethod(function, methodSig); - if (methodInfo != null) - { - return methodInfo.Invoke(instance, parameters); - } - else - { - throw new Exception("Method not found"); - } - } - else - { - throw new Exception("Unable to instantiate object of type"); - } - } - else - { - throw new ArgumentNullException("type"); - } - } - - /// - /// Calls an instance method on a new object. - /// - /// - /// the return type - /// the object type - /// the name of the method to call - /// the type signature - /// the parameters - /// the result of the call - public static T CallNonStaticMethod(this Type type, string function, Type[] methodSig, params object[] parameters) - { - return (T)CallNonStaticMethod(type, function, methodSig, parameters); - } - } -} diff --git a/IllusionPlugin/obj/Debug/IllusionPlugin.csproj.CoreCompileInputs.cache b/IllusionPlugin/obj/Debug/IllusionPlugin.csproj.CoreCompileInputs.cache deleted file mode 100644 index 2a6cf0f9..00000000 --- a/IllusionPlugin/obj/Debug/IllusionPlugin.csproj.CoreCompileInputs.cache +++ /dev/null @@ -1 +0,0 @@ -77d5fd2376e4df56c59ee712f4f804807c24fc6f From 6c9ea9b338e6a92b7bb5f764b415470cc7f95ceb Mon Sep 17 00:00:00 2001 From: Anairkoen Schno Date: Sat, 1 Sep 2018 15:43:49 -0500 Subject: [PATCH 03/12] Improved the backup system added $manifest$.txt to backups --- IPA.Injector/IPA.Injector.csproj | 9 ---- IPA.Injector/packages.config | 5 -- IPA.Loader/IPA.Loader.csproj | 16 +++--- IPA.Loader/packages.config | 5 -- IPA/IPA.csproj | 52 +++++++++++++------ IPA/PatchContext.cs | 2 +- IPA/Patcher/BackupManager.cs | 30 +++++++++++ IPA/Patcher/BackupUnit.cs | 37 +++++++++---- IPA/Program.cs | 4 +- .../Debug/IPA.csproj.CoreCompileInputs.cache | 2 +- IPA/packages.config | 4 -- 11 files changed, 106 insertions(+), 60 deletions(-) delete mode 100644 IPA.Injector/packages.config delete mode 100644 IPA.Loader/packages.config delete mode 100644 IPA/packages.config diff --git a/IPA.Injector/IPA.Injector.csproj b/IPA.Injector/IPA.Injector.csproj index f07fc0af..e8e044a1 100644 --- a/IPA.Injector/IPA.Injector.csproj +++ b/IPA.Injector/IPA.Injector.csproj @@ -31,12 +31,6 @@ 4 - - ..\packages\Ionic.Zip.1.9.1.8\lib\Ionic.Zip.dll - - - ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll - @@ -63,9 +57,6 @@ IPA.Loader - - - Libraries\Included\0Harmony.dll diff --git a/IPA.Injector/packages.config b/IPA.Injector/packages.config deleted file mode 100644 index 71c37d6d..00000000 --- a/IPA.Injector/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/IPA.Loader/IPA.Loader.csproj b/IPA.Loader/IPA.Loader.csproj index fe8d387f..af6be3af 100644 --- a/IPA.Loader/IPA.Loader.csproj +++ b/IPA.Loader/IPA.Loader.csproj @@ -33,12 +33,6 @@ true - - ..\packages\Ionic.Zip.1.9.1.8\lib\Ionic.Zip.dll - - - ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll - @@ -88,7 +82,15 @@ - + + 1.9.1.8 + + + 0.9.6.4 + + + 11.0.2 + \ No newline at end of file diff --git a/IPA.Loader/packages.config b/IPA.Loader/packages.config deleted file mode 100644 index 71c37d6d..00000000 --- a/IPA.Loader/packages.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/IPA/IPA.csproj b/IPA/IPA.csproj index 1bb17d89..705e0703 100644 --- a/IPA/IPA.csproj +++ b/IPA/IPA.csproj @@ -13,6 +13,21 @@ 512 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true AnyCPU @@ -39,23 +54,10 @@ favicon.ico + + IPA.Program + - - ..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.dll - True - - - ..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Mdb.dll - False - - - ..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Pdb.dll - False - - - ..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Rocks.dll - False - @@ -77,7 +79,6 @@ - @@ -85,6 +86,23 @@ + + + 0.9.6.4 + + + + + False + Microsoft .NET Framework 4.6.2 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 + false + +